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, 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_parts(&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(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
386#[cfg(feature = "proto-ipv4")]
387impl FromStr for Ipv4Address {
388 type Err = ();
389
390 fn from_str(s: &str) -> Result<Ipv4Address> {
392 Parser::new(s).until_eof(|p| p.accept_ipv4())
393 }
394}
395
396#[cfg(feature = "proto-ipv6")]
397impl FromStr for Ipv6Address {
398 type Err = ();
399
400 fn from_str(s: &str) -> Result<Ipv6Address> {
402 Parser::new(s).until_eof(|p| p.accept_ipv6())
403 }
404}
405
406impl FromStr for IpAddress {
407 type Err = ();
408
409 fn from_str(s: &str) -> Result<IpAddress> {
411 Parser::new(s).until_eof(|p| p.accept_ip())
412 }
413}
414
415#[cfg(feature = "proto-ipv4")]
416impl FromStr for Ipv4Cidr {
417 type Err = ();
418
419 fn from_str(s: &str) -> Result<Ipv4Cidr> {
421 Parser::new(s).until_eof(|p| {
422 let ip = p.accept_ipv4()?;
423 p.accept_char(b'/')?;
424 let prefix_len = p.accept_number(2, 33, false)? as u8;
425 Ok(Ipv4Cidr::new(ip, prefix_len))
426 })
427 }
428}
429
430#[cfg(feature = "proto-ipv6")]
431impl FromStr for Ipv6Cidr {
432 type Err = ();
433
434 fn from_str(s: &str) -> Result<Ipv6Cidr> {
436 Parser::new(s).until_eof(|p| {
438 let ip = p.accept_ipv6()?;
439 p.accept_char(b'/')?;
440 let prefix_len = p.accept_number(3, 129, false)? as u8;
441 Ok(Ipv6Cidr::new(ip, prefix_len))
442 })
443 }
444}
445
446impl FromStr for IpCidr {
447 type Err = ();
448
449 fn from_str(s: &str) -> Result<IpCidr> {
451 #[cfg(feature = "proto-ipv4")]
452 #[allow(clippy::single_match)]
453 match Ipv4Cidr::from_str(s) {
454 Ok(cidr) => return Ok(IpCidr::Ipv4(cidr)),
455 Err(_) => (),
456 }
457
458 #[cfg(feature = "proto-ipv6")]
459 #[allow(clippy::single_match)]
460 match Ipv6Cidr::from_str(s) {
461 Ok(cidr) => return Ok(IpCidr::Ipv6(cidr)),
462 Err(_) => (),
463 }
464
465 Err(())
466 }
467}
468
469impl FromStr for IpEndpoint {
470 type Err = ();
471
472 fn from_str(s: &str) -> Result<IpEndpoint> {
473 Parser::new(s).until_eof(|p| p.accept_ip_endpoint())
474 }
475}
476
477#[cfg(test)]
478mod test {
479 use super::*;
480
481 macro_rules! check_cidr_test_array {
482 ($tests:expr, $from_str:path, $variant:path) => {
483 for &(s, cidr) in &$tests {
484 assert_eq!($from_str(s), cidr);
485 assert_eq!(IpCidr::from_str(s), cidr.map($variant));
486
487 if let Ok(cidr) = cidr {
488 assert_eq!($from_str(&format!("{}", cidr)), Ok(cidr));
489 assert_eq!(IpCidr::from_str(&format!("{}", cidr)), Ok($variant(cidr)));
490 }
491 }
492 };
493 }
494
495 #[test]
496 #[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
497 fn test_mac() {
498 assert_eq!(EthernetAddress::from_str(""), Err(()));
499 assert_eq!(
500 EthernetAddress::from_str("02:00:00:00:00:00"),
501 Ok(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x00]))
502 );
503 assert_eq!(
504 EthernetAddress::from_str("01:23:45:67:89:ab"),
505 Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab]))
506 );
507 assert_eq!(
508 EthernetAddress::from_str("cd:ef:10:00:00:00"),
509 Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00]))
510 );
511 assert_eq!(
512 EthernetAddress::from_str("00:00:00:ab:cd:ef"),
513 Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))
514 );
515 assert_eq!(
516 EthernetAddress::from_str("00-00-00-ab-cd-ef"),
517 Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))
518 );
519 assert_eq!(
520 EthernetAddress::from_str("AB-CD-EF-00-00-00"),
521 Ok(EthernetAddress([0xab, 0xcd, 0xef, 0x00, 0x00, 0x00]))
522 );
523 assert_eq!(EthernetAddress::from_str("100:00:00:00:00:00"), Err(()));
524 assert_eq!(EthernetAddress::from_str("002:00:00:00:00:00"), Err(()));
525 assert_eq!(EthernetAddress::from_str("02:00:00:00:00:000"), Err(()));
526 assert_eq!(EthernetAddress::from_str("02:00:00:00:00:0x"), Err(()));
527 }
528
529 #[test]
530 #[cfg(feature = "proto-ipv4")]
531 fn test_ipv4() {
532 assert_eq!(Ipv4Address::from_str(""), Err(()));
533 assert_eq!(
534 Ipv4Address::from_str("1.2.3.4"),
535 Ok(Ipv4Address([1, 2, 3, 4]))
536 );
537 assert_eq!(
538 Ipv4Address::from_str("001.2.3.4"),
539 Ok(Ipv4Address([1, 2, 3, 4]))
540 );
541 assert_eq!(Ipv4Address::from_str("0001.2.3.4"), Err(()));
542 assert_eq!(Ipv4Address::from_str("999.2.3.4"), Err(()));
543 assert_eq!(Ipv4Address::from_str("1.2.3.4.5"), Err(()));
544 assert_eq!(Ipv4Address::from_str("1.2.3"), Err(()));
545 assert_eq!(Ipv4Address::from_str("1.2.3."), Err(()));
546 assert_eq!(Ipv4Address::from_str("1.2.3.4."), Err(()));
547 }
548
549 #[test]
550 #[cfg(feature = "proto-ipv6")]
551 fn test_ipv6() {
552 assert_eq!(Ipv6Address::from_str(""), Err(()));
554 assert_eq!(
555 Ipv6Address::from_str("fe80:0:0:0:0:0:0:1"),
556 Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))
557 );
558 assert_eq!(Ipv6Address::from_str("::1"), Ok(Ipv6Address::LOOPBACK));
559 assert_eq!(Ipv6Address::from_str("::"), Ok(Ipv6Address::UNSPECIFIED));
560 assert_eq!(
561 Ipv6Address::from_str("fe80::1"),
562 Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))
563 );
564 assert_eq!(
565 Ipv6Address::from_str("1234:5678::"),
566 Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0, 0))
567 );
568 assert_eq!(
569 Ipv6Address::from_str("1234:5678::8765:4321"),
570 Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0x8765, 0x4321))
571 );
572 assert_eq!(Ipv6Address::from_str("1234:5678::1::1"), Err(()));
574 assert_eq!(
575 Ipv6Address::from_str("4444:333:22:1::4"),
576 Ok(Ipv6Address::new(0x4444, 0x0333, 0x0022, 0x0001, 0, 0, 0, 4))
577 );
578 assert_eq!(
579 Ipv6Address::from_str("1:1:1:1:1:1::"),
580 Ok(Ipv6Address::new(1, 1, 1, 1, 1, 1, 0, 0))
581 );
582 assert_eq!(
583 Ipv6Address::from_str("::1:1:1:1:1:1"),
584 Ok(Ipv6Address::new(0, 0, 1, 1, 1, 1, 1, 1))
585 );
586 assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), Err(()));
587 assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1::"), Err(()));
589 assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), Err(()));
591 assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1:1:1"), Err(()));
593 assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1"), Err(()));
595 assert_eq!(Ipv6Address::from_str("::000001"), Err(()));
597 assert_eq!(
599 Ipv6Address::from_str("::ffff:192.168.1.1"),
600 Ok(Ipv6Address([
601 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1
602 ]))
603 );
604 assert_eq!(
605 Ipv6Address::from_str("0:0:0:0:0:ffff:192.168.1.1"),
606 Ok(Ipv6Address([
607 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1
608 ]))
609 );
610 assert_eq!(
611 Ipv6Address::from_str("0::ffff:192.168.1.1"),
612 Ok(Ipv6Address([
613 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1
614 ]))
615 );
616 assert_eq!(Ipv6Address::from_str("0:0:0:0:0:eeee:192.168.1.1"), Err(()));
618 assert_eq!(Ipv6Address::from_str("0:0:0:0:1:ffff:192.168.1.1"), Err(()));
620 assert_eq!(Ipv6Address::from_str("1::ffff:192.168.1.1"), Err(()));
621 assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:256.168.1.1"), Err(()));
623 assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:c0.168.1.1"), Err(()));
625 }
626
627 #[test]
628 #[cfg(feature = "proto-ipv4")]
629 fn test_ip_ipv4() {
630 assert_eq!(IpAddress::from_str(""), Err(()));
631 assert_eq!(
632 IpAddress::from_str("1.2.3.4"),
633 Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4])))
634 );
635 assert_eq!(IpAddress::from_str("x"), Err(()));
636 }
637
638 #[test]
639 #[cfg(feature = "proto-ipv6")]
640 fn test_ip_ipv6() {
641 assert_eq!(IpAddress::from_str(""), Err(()));
642 assert_eq!(
643 IpAddress::from_str("fe80::1"),
644 Ok(IpAddress::Ipv6(Ipv6Address::new(
645 0xfe80, 0, 0, 0, 0, 0, 0, 1
646 )))
647 );
648 assert_eq!(IpAddress::from_str("x"), Err(()));
649 }
650
651 #[test]
652 #[cfg(feature = "proto-ipv4")]
653 fn test_cidr_ipv4() {
654 let tests = [
655 (
656 "127.0.0.1/8",
657 Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8)),
658 ),
659 (
660 "192.168.1.1/24",
661 Ok(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 1]), 24u8)),
662 ),
663 (
664 "8.8.8.8/32",
665 Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 32u8)),
666 ),
667 (
668 "8.8.8.8/0",
669 Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 0u8)),
670 ),
671 ("", Err(())),
672 ("1", Err(())),
673 ("127.0.0.1", Err(())),
674 ("127.0.0.1/", Err(())),
675 ("127.0.0.1/33", Err(())),
676 ("127.0.0.1/111", Err(())),
677 ("/32", Err(())),
678 ];
679
680 check_cidr_test_array!(tests, Ipv4Cidr::from_str, IpCidr::Ipv4);
681 }
682
683 #[test]
684 #[cfg(feature = "proto-ipv6")]
685 fn test_cidr_ipv6() {
686 let tests = [
687 (
688 "fe80::1/64",
689 Ok(Ipv6Cidr::new(
690 Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
691 64u8,
692 )),
693 ),
694 (
695 "fe80::/64",
696 Ok(Ipv6Cidr::new(
697 Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
698 64u8,
699 )),
700 ),
701 ("::1/128", Ok(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128u8))),
702 ("::/128", Ok(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128u8))),
703 (
704 "fe80:0:0:0:0:0:0:1/64",
705 Ok(Ipv6Cidr::new(
706 Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
707 64u8,
708 )),
709 ),
710 ("fe80:0:0:0:0:0:0:1|64", Err(())),
711 ("fe80::|64", Err(())),
712 ("fe80::1::/64", Err(())),
713 ];
714 check_cidr_test_array!(tests, Ipv6Cidr::from_str, IpCidr::Ipv6);
715 }
716
717 #[test]
718 #[cfg(feature = "proto-ipv4")]
719 fn test_endpoint_ipv4() {
720 assert_eq!(IpEndpoint::from_str(""), Err(()));
721 assert_eq!(IpEndpoint::from_str("x"), Err(()));
722 assert_eq!(
723 IpEndpoint::from_str("127.0.0.1"),
724 Ok(IpEndpoint {
725 addr: IpAddress::v4(127, 0, 0, 1),
726 port: 0
727 })
728 );
729 assert_eq!(
730 IpEndpoint::from_str("127.0.0.1:12345"),
731 Ok(IpEndpoint {
732 addr: IpAddress::v4(127, 0, 0, 1),
733 port: 12345
734 })
735 );
736 }
737
738 #[test]
739 #[cfg(feature = "proto-ipv6")]
740 fn test_endpoint_ipv6() {
741 assert_eq!(IpEndpoint::from_str(""), Err(()));
742 assert_eq!(IpEndpoint::from_str("x"), Err(()));
743 assert_eq!(
744 IpEndpoint::from_str("fe80::1"),
745 Ok(IpEndpoint {
746 addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1),
747 port: 0
748 })
749 );
750 assert_eq!(
751 IpEndpoint::from_str("[fe80::1]:12345"),
752 Ok(IpEndpoint {
753 addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1),
754 port: 12345
755 })
756 );
757 assert_eq!(
758 IpEndpoint::from_str("[::]:12345"),
759 Ok(IpEndpoint {
760 addr: IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 0),
761 port: 12345
762 })
763 );
764 }
765}