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 use_tail = true;
175 true
176 }
177 Some(_) => {
178 return Err(());
181 }
182 None => {
183 if *head_idx != 0 || use_tail && *tail_idx != 0 {
184 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 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 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 Err(())
222 }
223 None if double_colon => {
224 Ok(())
226 }
227 None => {
228 Err(())
230 }
231 }?;
232
233 if *head_idx + *tail_idx > 8 {
234 Err(())
236 } else if !self.lookahead_char(b':') {
237 if *head_idx < 8 && !use_tail {
238 return Err(());
240 }
241 Ok(())
242 } else {
243 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 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 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 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 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 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 fn from_str(s: &str) -> Result<Ipv6Cidr> {
416 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 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}