Skip to main content

wacore_noise/
edge_routing.rs

1use thiserror::Error;
2use wacore_binary::consts::WA_CONN_HEADER;
3
4/// Maximum length for edge routing data (3 bytes max = 0xFFFFFF)
5pub const MAX_EDGE_ROUTING_LEN: usize = 0xFF_FFFF;
6
7#[derive(Debug, Error)]
8pub enum EdgeRoutingError {
9    #[error("edge routing info too large (max {MAX_EDGE_ROUTING_LEN} bytes)")]
10    RoutingInfoTooLarge,
11}
12
13/// Builds the edge routing pre-intro header.
14/// Format: `ED\0\1` (4 bytes) + length (3 bytes big-endian) + routing_data
15pub fn build_edge_routing_preintro(routing_info: &[u8]) -> Result<Vec<u8>, EdgeRoutingError> {
16    let len = routing_info.len();
17    if len > MAX_EDGE_ROUTING_LEN {
18        return Err(EdgeRoutingError::RoutingInfoTooLarge);
19    }
20
21    let mut preintro = Vec::with_capacity(7 + len);
22    preintro.extend_from_slice(b"ED\x00\x01");
23    preintro.push((len >> 16) as u8);
24    preintro.push((len >> 8) as u8);
25    preintro.push(len as u8);
26    preintro.extend_from_slice(routing_info);
27    Ok(preintro)
28}
29
30/// Builds the complete handshake connection header.
31///
32/// If edge routing info is provided and valid, prepends the edge routing pre-intro
33/// to the WhatsApp connection header. Otherwise, returns just the connection header.
34///
35/// Returns `(header, used_edge_routing)` where `used_edge_routing` indicates whether
36/// edge routing was successfully applied.
37pub fn build_handshake_header(edge_routing_info: Option<&[u8]>) -> (Vec<u8>, bool) {
38    let Some(routing_info) = edge_routing_info else {
39        return (WA_CONN_HEADER.to_vec(), false);
40    };
41
42    match build_edge_routing_preintro(routing_info) {
43        Ok(mut header) => {
44            header.extend_from_slice(&WA_CONN_HEADER);
45            (header, true)
46        }
47        // Intentional silent fallback: edge routing is optional and failures (e.g., oversized
48        // routing info) should not prevent connection. The returned `false` flag allows callers
49        // to detect and log this condition if needed.
50        Err(_) => (WA_CONN_HEADER.to_vec(), false),
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn test_build_edge_routing_preintro_basic() {
60        let routing_data = vec![0x01, 0x02, 0x03];
61        let preintro = build_edge_routing_preintro(&routing_data)
62            .expect("valid routing data should build preintro");
63
64        assert_eq!(&preintro[0..4], b"ED\x00\x01");
65        assert_eq!(preintro[4], 0x00);
66        assert_eq!(preintro[5], 0x00);
67        assert_eq!(preintro[6], 0x03);
68        assert_eq!(&preintro[7..], &routing_data[..]);
69    }
70
71    #[test]
72    fn test_build_edge_routing_preintro_empty() {
73        let preintro =
74            build_edge_routing_preintro(&[]).expect("valid routing data should build preintro");
75
76        assert_eq!(&preintro[0..4], b"ED\x00\x01");
77        assert_eq!(preintro[4], 0x00);
78        assert_eq!(preintro[5], 0x00);
79        assert_eq!(preintro[6], 0x00);
80        assert_eq!(preintro.len(), 7);
81    }
82
83    #[test]
84    fn test_build_edge_routing_preintro_large_length() {
85        let routing_data = vec![0xAA; 0x010203];
86        let preintro = build_edge_routing_preintro(&routing_data)
87            .expect("valid routing data should build preintro");
88
89        assert_eq!(preintro[4], 0x01);
90        assert_eq!(preintro[5], 0x02);
91        assert_eq!(preintro[6], 0x03);
92    }
93
94    #[test]
95    fn test_build_edge_routing_preintro_too_large() {
96        let routing_data = vec![0x00; MAX_EDGE_ROUTING_LEN + 1];
97        let result = build_edge_routing_preintro(&routing_data);
98
99        assert!(matches!(result, Err(EdgeRoutingError::RoutingInfoTooLarge)));
100    }
101
102    #[test]
103    fn test_build_edge_routing_preintro_max_size() {
104        let len = MAX_EDGE_ROUTING_LEN;
105
106        assert_eq!((len >> 16) as u8, 0xFF);
107        assert_eq!((len >> 8) as u8, 0xFF);
108        assert_eq!(len as u8, 0xFF);
109    }
110
111    #[test]
112    fn test_build_handshake_header_without_edge_routing() {
113        let (header, used) = build_handshake_header(None);
114        assert_eq!(header, WA_CONN_HEADER.to_vec());
115        assert!(!used);
116    }
117
118    #[test]
119    fn test_build_handshake_header_with_edge_routing() {
120        let routing = vec![0x01, 0x02, 0x03];
121        let (header, used) = build_handshake_header(Some(&routing));
122
123        assert!(used);
124        assert!(header.starts_with(b"ED\x00\x01"));
125        assert!(header.ends_with(&WA_CONN_HEADER));
126    }
127
128    #[test]
129    fn test_build_handshake_header_with_oversized_routing() {
130        let routing = vec![0x00; MAX_EDGE_ROUTING_LEN + 1];
131        let (header, used) = build_handshake_header(Some(&routing));
132
133        assert!(!used);
134        assert_eq!(header, WA_CONN_HEADER.to_vec());
135    }
136}