smoltcp/
parsers.rs

1#![cfg_attr(
2    not(all(feature = "proto-ipv6", feature = "proto-ipv4")),
3    allow(dead_code)
4)]
5
6use core::result;
7use core::str::FromStr;
8
9#[cfg(feature = "medium-ethernet")]
10use crate::wire::EthernetAddress;
11use crate::wire::{IpAddress, IpCidr, IpEndpoint};
12#[cfg(feature = "proto-ipv4")]
13use crate::wire::{Ipv4Address, Ipv4AddressExt, Ipv4Cidr};
14#[cfg(feature = "proto-ipv6")]
15use crate::wire::{Ipv6Address, Ipv6Cidr};
16
17type Result<T> = result::Result<T, ()>;
18
19struct Parser<'a> {
20    data: &'a [u8],
21    pos: usize,
22}
23
24impl<'a> Parser<'a> {
25    fn new(data: &'a str) -> Parser<'a> {
26        Parser {
27            data: data.as_bytes(),
28            pos: 0,
29        }
30    }
31
32    fn lookahead_char(&self, ch: u8) -> bool {
33        if self.pos < self.data.len() {
34            self.data[self.pos] == ch
35        } else {
36            false
37        }
38    }
39
40    fn advance(&mut self) -> Result<u8> {
41        match self.data.get(self.pos) {
42            Some(&chr) => {
43                self.pos += 1;
44                Ok(chr)
45            }
46            None => Err(()),
47        }
48    }
49
50    fn try_do<F, T>(&mut self, f: F) -> Option<T>
51    where
52        F: FnOnce(&mut Parser<'a>) -> Result<T>,
53    {
54        let pos = self.pos;
55        match f(self) {
56            Ok(res) => Some(res),
57            Err(()) => {
58                self.pos = pos;
59                None
60            }
61        }
62    }
63
64    fn accept_eof(&mut self) -> Result<()> {
65        if self.data.len() == self.pos {
66            Ok(())
67        } else {
68            Err(())
69        }
70    }
71
72    fn until_eof<F, T>(&mut self, f: F) -> Result<T>
73    where
74        F: FnOnce(&mut Parser<'a>) -> Result<T>,
75    {
76        let res = f(self)?;
77        self.accept_eof()?;
78        Ok(res)
79    }
80
81    fn accept_char(&mut self, chr: u8) -> Result<()> {
82        if self.advance()? == chr {
83            Ok(())
84        } else {
85            Err(())
86        }
87    }
88
89    fn accept_str(&mut self, string: &[u8]) -> Result<()> {
90        for byte in string.iter() {
91            self.accept_char(*byte)?;
92        }
93        Ok(())
94    }
95
96    fn accept_digit(&mut self, hex: bool) -> Result<u8> {
97        let digit = self.advance()?;
98        if digit.is_ascii_digit() {
99            Ok(digit - b'0')
100        } else if hex && (b'a'..=b'f').contains(&digit) {
101            Ok(digit - b'a' + 10)
102        } else if hex && (b'A'..=b'F').contains(&digit) {
103            Ok(digit - b'A' + 10)
104        } else {
105            Err(())
106        }
107    }
108
109    fn accept_number(&mut self, max_digits: usize, max_value: u32, hex: bool) -> Result<u32> {
110        let mut value = self.accept_digit(hex)? as u32;
111        for _ in 1..max_digits {
112            match self.try_do(|p| p.accept_digit(hex)) {
113                Some(digit) => {
114                    value *= if hex { 16 } else { 10 };
115                    value += digit as u32;
116                }
117                None => break,
118            }
119        }
120        if value < max_value {
121            Ok(value)
122        } else {
123            Err(())
124        }
125    }
126
127    #[cfg(feature = "medium-ethernet")]
128    fn accept_mac_joined_with(&mut self, separator: u8) -> Result<EthernetAddress> {
129        let mut octets = [0u8; 6];
130        for (n, octet) in octets.iter_mut().enumerate() {
131            *octet = self.accept_number(2, 0x100, true)? as u8;
132            if n != 5 {
133                self.accept_char(separator)?;
134            }
135        }
136        Ok(EthernetAddress(octets))
137    }
138
139    #[cfg(feature = "medium-ethernet")]
140    fn accept_mac(&mut self) -> Result<EthernetAddress> {
141        if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b'-')) {
142            return Ok(mac);
143        }
144        if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b':')) {
145            return Ok(mac);
146        }
147        Err(())
148    }
149
150    #[cfg(feature = "proto-ipv6")]
151    fn accept_ipv4_mapped_ipv6_part(&mut self, parts: &mut [u16], idx: &mut usize) -> Result<()> {
152        let octets = self.accept_ipv4_octets()?;
153
154        parts[*idx] = ((octets[0] as u16) << 8) | (octets[1] as u16);
155        *idx += 1;
156        parts[*idx] = ((octets[2] as u16) << 8) | (octets[3] as u16);
157        *idx += 1;
158
159        Ok(())
160    }
161
162    #[cfg(feature = "proto-ipv6")]
163    fn accept_ipv6_part(
164        &mut self,
165        (head, tail): (&mut [u16; 8], &mut [u16; 6]),
166        (head_idx, tail_idx): (&mut usize, &mut usize),
167        mut use_tail: bool,
168    ) -> Result<()> {
169        let double_colon = match self.try_do(|p| p.accept_str(b"::")) {
170            Some(_) if !use_tail && *head_idx < 7 => {
171                // Found a double colon. Start filling out the
172                // tail and set the double colon flag in case
173                // this is the last character we can parse.
174                use_tail = true;
175                true
176            }
177            Some(_) => {
178                // This is a bad address. Only one double colon is
179                // allowed and an address is only 128 bits.
180                return Err(());
181            }
182            None => {
183                if *head_idx != 0 || use_tail && *tail_idx != 0 {
184                    // If this is not the first number or the position following
185                    // a double colon, we expect there to be a single colon.
186                    self.accept_char(b':')?;
187                }
188                false
189            }
190        };
191
192        match self.try_do(|p| p.accept_number(4, 0x10000, true)) {
193            Some(part) if !use_tail && *head_idx < 8 => {
194                // Valid u16 to be added to the address
195                head[*head_idx] = part as u16;
196                *head_idx += 1;
197
198                if *head_idx == 6 && head[0..*head_idx] == [0, 0, 0, 0, 0, 0xffff] {
199                    self.try_do(|p| {
200                        p.accept_char(b':')?;
201                        p.accept_ipv4_mapped_ipv6_part(head, head_idx)
202                    });
203                }
204                Ok(())
205            }
206            Some(part) if *tail_idx < 6 => {
207                // Valid u16 to be added to the address
208                tail[*tail_idx] = part as u16;
209                *tail_idx += 1;
210
211                if *tail_idx == 1 && tail[0] == 0xffff && head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] {
212                    self.try_do(|p| {
213                        p.accept_char(b':')?;
214                        p.accept_ipv4_mapped_ipv6_part(tail, tail_idx)
215                    });
216                }
217                Ok(())
218            }
219            Some(_) => {
220                // Tail or head section is too long
221                Err(())
222            }
223            None if double_colon => {
224                // The address ends with "::". E.g. 1234:: or ::
225                Ok(())
226            }
227            None => {
228                // Invalid address
229                Err(())
230            }
231        }?;
232
233        if *head_idx + *tail_idx > 8 {
234            // The head and tail indexes add up to a bad address length.
235            Err(())
236        } else if !self.lookahead_char(b':') {
237            if *head_idx < 8 && !use_tail {
238                // There was no double colon found, and the head is too short
239                return Err(());
240            }
241            Ok(())
242        } else {
243            // Continue recursing
244            self.accept_ipv6_part((head, tail), (head_idx, tail_idx), use_tail)
245        }
246    }
247
248    #[cfg(feature = "proto-ipv6")]
249    fn accept_ipv6(&mut self) -> Result<Ipv6Address> {
250        // IPv6 addresses may contain a "::" to indicate a series of
251        // 16 bit sections that evaluate to 0. E.g.
252        //
253        // fe80:0000:0000:0000:0000:0000:0000:0001
254        //
255        // May be written as
256        //
257        // fe80::1
258        //
259        // As a result, we need to find the first section of colon
260        // delimited u16's before a possible "::", then the
261        // possible second section after the "::", and finally
262        // combine the second optional section to the end of the
263        // final address.
264        //
265        // See https://tools.ietf.org/html/rfc4291#section-2.2
266        // for details.
267        let (mut addr, mut tail) = ([0u16; 8], [0u16; 6]);
268        let (mut head_idx, mut tail_idx) = (0, 0);
269
270        self.accept_ipv6_part(
271            (&mut addr, &mut tail),
272            (&mut head_idx, &mut tail_idx),
273            false,
274        )?;
275
276        // We need to copy the tail portion (the portion following the "::") to the
277        // end of the address.
278        addr[8 - tail_idx..].copy_from_slice(&tail[..tail_idx]);
279
280        Ok(Ipv6Address::from(addr))
281    }
282
283    fn accept_ipv4_octets(&mut self) -> Result<[u8; 4]> {
284        let mut octets = [0u8; 4];
285        for (n, octet) in octets.iter_mut().enumerate() {
286            *octet = self.accept_number(3, 0x100, false)? as u8;
287            if n != 3 {
288                self.accept_char(b'.')?;
289            }
290        }
291        Ok(octets)
292    }
293
294    #[cfg(feature = "proto-ipv4")]
295    fn accept_ipv4(&mut self) -> Result<Ipv4Address> {
296        let octets = self.accept_ipv4_octets()?;
297        Ok(Ipv4Address::from_bytes(&octets))
298    }
299
300    fn accept_ip(&mut self) -> Result<IpAddress> {
301        #[cfg(feature = "proto-ipv4")]
302        #[allow(clippy::single_match)]
303        match self.try_do(|p| p.accept_ipv4()) {
304            Some(ipv4) => return Ok(IpAddress::Ipv4(ipv4)),
305            None => (),
306        }
307
308        #[cfg(feature = "proto-ipv6")]
309        #[allow(clippy::single_match)]
310        match self.try_do(|p| p.accept_ipv6()) {
311            Some(ipv6) => return Ok(IpAddress::Ipv6(ipv6)),
312            None => (),
313        }
314
315        Err(())
316    }
317
318    #[cfg(feature = "proto-ipv4")]
319    fn accept_ipv4_endpoint(&mut self) -> Result<IpEndpoint> {
320        let ip = self.accept_ipv4()?;
321
322        let port = if self.accept_eof().is_ok() {
323            0
324        } else {
325            self.accept_char(b':')?;
326            self.accept_number(5, 65535, false)?
327        };
328
329        Ok(IpEndpoint {
330            addr: IpAddress::Ipv4(ip),
331            port: port as u16,
332        })
333    }
334
335    #[cfg(feature = "proto-ipv6")]
336    fn accept_ipv6_endpoint(&mut self) -> Result<IpEndpoint> {
337        if self.lookahead_char(b'[') {
338            self.accept_char(b'[')?;
339            let ip = self.accept_ipv6()?;
340            self.accept_char(b']')?;
341            self.accept_char(b':')?;
342            let port = self.accept_number(5, 65535, false)?;
343
344            Ok(IpEndpoint {
345                addr: IpAddress::Ipv6(ip),
346                port: port as u16,
347            })
348        } else {
349            let ip = self.accept_ipv6()?;
350            Ok(IpEndpoint {
351                addr: IpAddress::Ipv6(ip),
352                port: 0,
353            })
354        }
355    }
356
357    fn accept_ip_endpoint(&mut self) -> Result<IpEndpoint> {
358        #[cfg(feature = "proto-ipv4")]
359        #[allow(clippy::single_match)]
360        match self.try_do(|p| p.accept_ipv4_endpoint()) {
361            Some(ipv4) => return Ok(ipv4),
362            None => (),
363        }
364
365        #[cfg(feature = "proto-ipv6")]
366        #[allow(clippy::single_match)]
367        match self.try_do(|p| p.accept_ipv6_endpoint()) {
368            Some(ipv6) => return Ok(ipv6),
369            None => (),
370        }
371
372        Err(())
373    }
374}
375
376#[cfg(feature = "medium-ethernet")]
377impl FromStr for EthernetAddress {
378    type Err = ();
379
380    /// Parse a string representation of an Ethernet address.
381    fn from_str(s: &str) -> Result<EthernetAddress> {
382        Parser::new(s).until_eof(|p| p.accept_mac())
383    }
384}
385
386impl FromStr for IpAddress {
387    type Err = ();
388
389    /// Parse a string representation of an IP address.
390    fn from_str(s: &str) -> Result<IpAddress> {
391        Parser::new(s).until_eof(|p| p.accept_ip())
392    }
393}
394
395#[cfg(feature = "proto-ipv4")]
396impl FromStr for Ipv4Cidr {
397    type Err = ();
398
399    /// Parse a string representation of an IPv4 CIDR.
400    fn from_str(s: &str) -> Result<Ipv4Cidr> {
401        Parser::new(s).until_eof(|p| {
402            let ip = p.accept_ipv4()?;
403            p.accept_char(b'/')?;
404            let prefix_len = p.accept_number(2, 33, false)? as u8;
405            Ok(Ipv4Cidr::new(ip, prefix_len))
406        })
407    }
408}
409
410#[cfg(feature = "proto-ipv6")]
411impl FromStr for Ipv6Cidr {
412    type Err = ();
413
414    /// Parse a string representation of an IPv6 CIDR.
415    fn from_str(s: &str) -> Result<Ipv6Cidr> {
416        // https://tools.ietf.org/html/rfc4291#section-2.3
417        Parser::new(s).until_eof(|p| {
418            let ip = p.accept_ipv6()?;
419            p.accept_char(b'/')?;
420            let prefix_len = p.accept_number(3, 129, false)? as u8;
421            Ok(Ipv6Cidr::new(ip, prefix_len))
422        })
423    }
424}
425
426impl FromStr for IpCidr {
427    type Err = ();
428
429    /// Parse a string representation of an IP CIDR.
430    fn from_str(s: &str) -> Result<IpCidr> {
431        #[cfg(feature = "proto-ipv4")]
432        #[allow(clippy::single_match)]
433        match Ipv4Cidr::from_str(s) {
434            Ok(cidr) => return Ok(IpCidr::Ipv4(cidr)),
435            Err(_) => (),
436        }
437
438        #[cfg(feature = "proto-ipv6")]
439        #[allow(clippy::single_match)]
440        match Ipv6Cidr::from_str(s) {
441            Ok(cidr) => return Ok(IpCidr::Ipv6(cidr)),
442            Err(_) => (),
443        }
444
445        Err(())
446    }
447}
448
449impl FromStr for IpEndpoint {
450    type Err = ();
451
452    fn from_str(s: &str) -> Result<IpEndpoint> {
453        Parser::new(s).until_eof(|p| p.accept_ip_endpoint())
454    }
455}
456
457#[cfg(test)]
458mod test {
459    use super::*;
460
461    macro_rules! check_cidr_test_array {
462        ($tests:expr, $from_str:path, $variant:path) => {
463            for &(s, cidr) in &$tests {
464                assert_eq!($from_str(s), cidr);
465                assert_eq!(IpCidr::from_str(s), cidr.map($variant));
466
467                if let Ok(cidr) = cidr {
468                    assert_eq!($from_str(&format!("{}", cidr)), Ok(cidr));
469                    assert_eq!(IpCidr::from_str(&format!("{}", cidr)), Ok($variant(cidr)));
470                }
471            }
472        };
473    }
474
475    #[test]
476    #[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
477    fn test_mac() {
478        assert_eq!(EthernetAddress::from_str(""), Err(()));
479        assert_eq!(
480            EthernetAddress::from_str("02:00:00:00:00:00"),
481            Ok(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x00]))
482        );
483        assert_eq!(
484            EthernetAddress::from_str("01:23:45:67:89:ab"),
485            Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab]))
486        );
487        assert_eq!(
488            EthernetAddress::from_str("cd:ef:10:00:00:00"),
489            Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00]))
490        );
491        assert_eq!(
492            EthernetAddress::from_str("00:00:00:ab:cd:ef"),
493            Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))
494        );
495        assert_eq!(
496            EthernetAddress::from_str("00-00-00-ab-cd-ef"),
497            Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))
498        );
499        assert_eq!(
500            EthernetAddress::from_str("AB-CD-EF-00-00-00"),
501            Ok(EthernetAddress([0xab, 0xcd, 0xef, 0x00, 0x00, 0x00]))
502        );
503        assert_eq!(EthernetAddress::from_str("100:00:00:00:00:00"), Err(()));
504        assert_eq!(EthernetAddress::from_str("002:00:00:00:00:00"), Err(()));
505        assert_eq!(EthernetAddress::from_str("02:00:00:00:00:000"), Err(()));
506        assert_eq!(EthernetAddress::from_str("02:00:00:00:00:0x"), Err(()));
507    }
508
509    #[test]
510    #[cfg(feature = "proto-ipv4")]
511    fn test_ip_ipv4() {
512        assert_eq!(IpAddress::from_str(""), Err(()));
513        assert_eq!(
514            IpAddress::from_str("1.2.3.4"),
515            Ok(IpAddress::Ipv4(Ipv4Address::new(1, 2, 3, 4)))
516        );
517        assert_eq!(IpAddress::from_str("x"), Err(()));
518    }
519
520    #[test]
521    #[cfg(feature = "proto-ipv6")]
522    fn test_ip_ipv6() {
523        assert_eq!(IpAddress::from_str(""), Err(()));
524        assert_eq!(
525            IpAddress::from_str("fe80::1"),
526            Ok(IpAddress::Ipv6(Ipv6Address::new(
527                0xfe80, 0, 0, 0, 0, 0, 0, 1
528            )))
529        );
530        assert_eq!(IpAddress::from_str("x"), Err(()));
531    }
532
533    #[test]
534    #[cfg(feature = "proto-ipv4")]
535    fn test_cidr_ipv4() {
536        let tests = [
537            (
538                "127.0.0.1/8",
539                Ok(Ipv4Cidr::new(Ipv4Address::new(127, 0, 0, 1), 8u8)),
540            ),
541            (
542                "192.168.1.1/24",
543                Ok(Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 1), 24u8)),
544            ),
545            (
546                "8.8.8.8/32",
547                Ok(Ipv4Cidr::new(Ipv4Address::new(8, 8, 8, 8), 32u8)),
548            ),
549            (
550                "8.8.8.8/0",
551                Ok(Ipv4Cidr::new(Ipv4Address::new(8, 8, 8, 8), 0u8)),
552            ),
553            ("", Err(())),
554            ("1", Err(())),
555            ("127.0.0.1", Err(())),
556            ("127.0.0.1/", Err(())),
557            ("127.0.0.1/33", Err(())),
558            ("127.0.0.1/111", Err(())),
559            ("/32", Err(())),
560        ];
561
562        check_cidr_test_array!(tests, Ipv4Cidr::from_str, IpCidr::Ipv4);
563    }
564
565    #[test]
566    #[cfg(feature = "proto-ipv6")]
567    fn test_cidr_ipv6() {
568        let tests = [
569            (
570                "fe80::1/64",
571                Ok(Ipv6Cidr::new(
572                    Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
573                    64u8,
574                )),
575            ),
576            (
577                "fe80::/64",
578                Ok(Ipv6Cidr::new(
579                    Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
580                    64u8,
581                )),
582            ),
583            ("::1/128", Ok(Ipv6Cidr::new(Ipv6Address::LOCALHOST, 128u8))),
584            ("::/128", Ok(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128u8))),
585            (
586                "fe80:0:0:0:0:0:0:1/64",
587                Ok(Ipv6Cidr::new(
588                    Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
589                    64u8,
590                )),
591            ),
592            ("fe80:0:0:0:0:0:0:1|64", Err(())),
593            ("fe80::|64", Err(())),
594            ("fe80::1::/64", Err(())),
595        ];
596        check_cidr_test_array!(tests, Ipv6Cidr::from_str, IpCidr::Ipv6);
597    }
598
599    #[test]
600    #[cfg(feature = "proto-ipv4")]
601    fn test_endpoint_ipv4() {
602        assert_eq!(IpEndpoint::from_str(""), Err(()));
603        assert_eq!(IpEndpoint::from_str("x"), Err(()));
604        assert_eq!(
605            IpEndpoint::from_str("127.0.0.1"),
606            Ok(IpEndpoint {
607                addr: IpAddress::v4(127, 0, 0, 1),
608                port: 0
609            })
610        );
611        assert_eq!(
612            IpEndpoint::from_str("127.0.0.1:12345"),
613            Ok(IpEndpoint {
614                addr: IpAddress::v4(127, 0, 0, 1),
615                port: 12345
616            })
617        );
618    }
619
620    #[test]
621    #[cfg(feature = "proto-ipv6")]
622    fn test_endpoint_ipv6() {
623        assert_eq!(IpEndpoint::from_str(""), Err(()));
624        assert_eq!(IpEndpoint::from_str("x"), Err(()));
625        assert_eq!(
626            IpEndpoint::from_str("fe80::1"),
627            Ok(IpEndpoint {
628                addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1),
629                port: 0
630            })
631        );
632        assert_eq!(
633            IpEndpoint::from_str("[fe80::1]:12345"),
634            Ok(IpEndpoint {
635                addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1),
636                port: 12345
637            })
638        );
639        assert_eq!(
640            IpEndpoint::from_str("[::]:12345"),
641            Ok(IpEndpoint {
642                addr: IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 0),
643                port: 12345
644            })
645        );
646    }
647}