1use core::borrow::BorrowMut;
2use core::fmt;
3use core::str::FromStr;
4
5use super::{impl_try_from_any, AddressRange, PrefixLength};
6use crate::{
7 any, concrete,
8 error::{err, Error, Kind},
9 fmt::AddressDisplay,
10 traits::{
11 self,
12 primitive::{Address as _, Octets as _},
13 Afi,
14 },
15 Ipv4, Ipv6,
16};
17
18mod private;
19pub use self::private::Address;
20
21mod convert;
22mod ops;
23
24mod ipv4;
25mod ipv6;
26pub use self::ipv6::MulticastScope as Ipv6MulticastScope;
27
28mod range;
29pub use self::range::Range;
30
31impl<A: Afi> Address<A> {
32 pub const LOCALHOST: Self = Self::new(A::Primitive::LOCALHOST);
34
35 pub const UNSPECIFIED: Self = Self::new(A::Primitive::UNSPECIFIED);
37
38 pub const ZEROS: Self = Self::new(A::Primitive::ZERO);
40
41 pub fn from_octets(octets: A::Octets) -> Self {
60 Self::new(A::Primitive::from_be_bytes(octets))
61 }
62
63 pub fn from_slice(slice: &[u8]) -> Result<Self, Error> {
91 let mut octets = A::Octets::ZEROS;
92 let len = slice.len();
93 if len > A::Octets::LENGTH {
94 return Err(err!(Kind::OctetSliceOverrun));
95 }
96 octets.borrow_mut()[..len].copy_from_slice(slice);
97 Ok(Self::from_octets(octets))
98 }
99
100 pub fn octets(&self) -> A::Octets {
116 self.into_primitive().to_be_bytes()
117 }
118
119 #[allow(clippy::missing_panics_doc)]
120 pub fn common_length(self, other: Self) -> PrefixLength<A> {
124 PrefixLength::<A>::from_primitive((self ^ other).leading_zeros()).unwrap()
126 }
127}
128pub fn common_length<A: Afi>(lhs: Address<A>, rhs: Address<A>) -> PrefixLength<A> {
154 lhs.common_length(rhs)
155}
156
157impl<A: Afi> traits::Address for Address<A> {
158 fn afi(&self) -> concrete::Afi {
159 A::as_afi()
160 }
161
162 #[allow(clippy::option_if_let_else)]
163 fn is_broadcast(&self) -> bool {
164 if let Some(broadcast) = A::Primitive::BROADCAST {
165 self.into_primitive() == broadcast
166 } else {
167 false
168 }
169 }
170
171 fn is_link_local(&self) -> bool {
172 AddressRange::from(&A::Primitive::LINK_LOCAL_RANGE).contains(self)
173 }
174
175 #[allow(clippy::option_if_let_else)]
176 fn is_private(&self) -> bool {
177 if let Some(ranges) = A::Primitive::PRIVATE_RANGES {
178 ranges
179 .iter()
180 .any(|range| AddressRange::from(range).contains(self))
181 } else {
182 false
183 }
184 }
185
186 #[allow(clippy::option_if_let_else)]
187 fn is_reserved(&self) -> bool {
188 if let Some(range) = A::Primitive::RESERVED_RANGE {
189 AddressRange::from(&range).contains(self) && self.into_primitive() != A::Primitive::ONES
192 } else {
193 false
194 }
195 }
196
197 #[allow(clippy::option_if_let_else)]
198 fn is_shared(&self) -> bool {
199 if let Some(range) = A::Primitive::SHARED_RANGE {
200 AddressRange::from(&range).contains(self)
201 } else {
202 false
203 }
204 }
205
206 #[allow(clippy::option_if_let_else)]
207 fn is_thisnet(&self) -> bool {
208 if let Some(range) = A::Primitive::THISNET_RANGE {
209 AddressRange::from(&range).contains(self)
210 } else {
211 false
212 }
213 }
214
215 fn is_benchmarking(&self) -> bool {
216 AddressRange::from(&A::Primitive::BENCHMARK_RANGE).contains(self)
217 }
218
219 fn is_documentation(&self) -> bool {
220 A::Primitive::DOCUMENTATION_RANGES
221 .iter()
222 .any(|range| AddressRange::from(range).contains(self))
223 }
224
225 fn is_global(&self) -> bool {
226 self.into_primitive().is_global()
227 }
228
229 fn is_loopback(&self) -> bool {
230 AddressRange::from(&A::Primitive::LOOPBACK_RANGE).contains(self)
231 }
232
233 fn is_multicast(&self) -> bool {
234 AddressRange::from(&A::Primitive::MULTICAST_RANGE).contains(self)
235 }
236
237 fn is_unspecified(&self) -> bool {
238 self == &Self::UNSPECIFIED
239 }
240
241 #[allow(clippy::option_if_let_else)]
242 fn is_unique_local(&self) -> bool {
243 if let Some(range) = A::Primitive::ULA_RANGE {
244 AddressRange::from(&range).contains(self)
245 } else {
246 false
247 }
248 }
249}
250
251impl<A: Afi> FromStr for Address<A> {
252 type Err = Error;
253
254 fn from_str(s: &str) -> Result<Self, Self::Err> {
255 A::Primitive::parse_addr(s).map(Self::new)
256 }
257}
258
259impl_try_from_any! {
260 any::Address {
261 any::Address::Ipv4 => Address<Ipv4>,
262 any::Address::Ipv6 => Address<Ipv6>,
263 }
264}
265
266impl<A: Afi> fmt::Display for Address<A> {
267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268 self.into_primitive().fmt_addr(f)
269 }
270}
271
272impl<A: Afi> fmt::Debug for Address<A> {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 write!(f, "Address<{:?}>({})", A::as_afi(), self)
275 }
276}
277
278#[cfg(any(test, feature = "arbitrary"))]
279use proptest::{
280 arbitrary::{any_with, Arbitrary, ParamsFor, StrategyFor},
281 strategy::{BoxedStrategy, Strategy},
282};
283
284#[cfg(any(test, feature = "arbitrary"))]
285impl<A> Arbitrary for Address<A>
286where
287 A: Afi + 'static,
288 A::Primitive: Arbitrary + 'static,
289 StrategyFor<A::Primitive>: 'static,
290{
291 type Parameters = ParamsFor<A::Primitive>;
292 type Strategy = BoxedStrategy<Self>;
293
294 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
295 any_with::<A::Primitive>(params).prop_map(Self::new).boxed()
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302 use crate::traits::Address as _;
303
304 #[test]
305 fn ipv4_broadcast_is_broadcast() {
306 assert!("255.255.255.255"
307 .parse::<Address<Ipv4>>()
308 .unwrap()
309 .is_broadcast());
310 }
311
312 #[test]
313 fn ipv4_unicast_is_not_broadcast() {
314 assert!(!"203.0.113.1"
315 .parse::<Address<Ipv4>>()
316 .unwrap()
317 .is_broadcast());
318 }
319
320 #[test]
321 fn ipv6_all_ones_is_not_broadcast() {
322 assert!(!"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
323 .parse::<Address<Ipv6>>()
324 .unwrap()
325 .is_broadcast());
326 }
327
328 #[test]
329 fn ipv4_link_local_is_link_local() {
330 assert!("169.254.254.1"
331 .parse::<Address<Ipv4>>()
332 .unwrap()
333 .is_link_local());
334 }
335
336 #[test]
337 fn ipv6_link_local_is_link_local() {
338 assert!("fe80::1".parse::<Address<Ipv6>>().unwrap().is_link_local());
339 }
340
341 #[test]
342 fn ipv4_unicast_is_not_link_local() {
343 assert!(!"203.0.113.1"
344 .parse::<Address<Ipv4>>()
345 .unwrap()
346 .is_link_local());
347 }
348
349 #[test]
350 fn ipv6_localhost_is_not_link_local() {
351 assert!(!Address::<Ipv6>::LOCALHOST.is_link_local());
352 }
353
354 #[test]
355 fn ipv4_private_is_private() {
356 assert!("172.18.0.1".parse::<Address<Ipv4>>().unwrap().is_private());
357 }
358
359 #[test]
360 fn ipv4_unicast_is_not_private() {
361 assert!(!"203.0.113.1".parse::<Address<Ipv4>>().unwrap().is_private());
362 }
363
364 #[test]
365 fn ipv6_ula_is_not_private() {
366 assert!(!"fc01::1".parse::<Address<Ipv6>>().unwrap().is_private());
367 }
368
369 #[test]
370 fn ipv4_reserved_is_reserved() {
371 assert!("240.0.0.1".parse::<Address<Ipv4>>().unwrap().is_reserved());
372 }
373
374 #[test]
375 fn ipv4_broadcast_is_not_reserved() {
376 assert!(!Address::<Ipv4>::BROADCAST.is_reserved());
377 }
378
379 #[test]
380 fn ipv6_unassigned_is_not_reserved() {
381 assert!(!"4::1".parse::<Address<Ipv6>>().unwrap().is_reserved());
382 }
383
384 #[test]
385 fn ipv4_shared_is_shared() {
386 assert!("100.72.1.1".parse::<Address<Ipv4>>().unwrap().is_shared());
387 }
388
389 #[test]
390 fn ipv4_unicast_is_not_shared() {
391 assert!(!"192.0.2.1".parse::<Address<Ipv4>>().unwrap().is_shared());
392 }
393
394 #[test]
395 fn ipv6_ula_is_not_shared() {
396 assert!(!"fc00::1".parse::<Address<Ipv6>>().unwrap().is_shared());
397 }
398
399 #[test]
400 fn ipv4_thisnet_is_thisnet() {
401 assert!("0.255.255.255"
402 .parse::<Address<Ipv4>>()
403 .unwrap()
404 .is_thisnet());
405 }
406
407 #[test]
408 fn ipv6_unspecified_is_not_thisnet() {
409 assert!(!Address::<Ipv6>::UNSPECIFIED.is_thisnet());
410 }
411
412 #[test]
413 fn ipv4_benchmarking_is_benmarking() {
414 assert!("198.19.0.1"
415 .parse::<Address<Ipv4>>()
416 .unwrap()
417 .is_benchmarking());
418 }
419
420 #[test]
421 fn ipv6_benchmarking_is_benmarking() {
422 assert!("2001:2::1"
423 .parse::<Address<Ipv6>>()
424 .unwrap()
425 .is_benchmarking());
426 }
427
428 #[test]
429 fn ipv4_test_net_2_is_documentation() {
430 assert!("198.51.100.1"
431 .parse::<Address<Ipv4>>()
432 .unwrap()
433 .is_documentation());
434 }
435
436 #[test]
437 fn ipv6_documentation_is_documentation() {
438 assert!("2001:db8::1"
439 .parse::<Address<Ipv6>>()
440 .unwrap()
441 .is_documentation());
442 }
443
444 #[test]
445 fn ipv4_private_1_is_not_global() {
446 assert!(!"10.254.0.0".parse::<Address<Ipv4>>().unwrap().is_global());
447 }
448
449 #[test]
450 fn ipv4_private_2_is_not_global() {
451 assert!(!"192.168.10.65"
452 .parse::<Address<Ipv4>>()
453 .unwrap()
454 .is_global());
455 }
456
457 #[test]
458 fn ipv4_private_3_is_not_global() {
459 assert!(!"172.16.10.65".parse::<Address<Ipv4>>().unwrap().is_global());
460 }
461
462 #[test]
463 fn ipv6_ula_is_not_global() {
464 assert!(!"fc00::1".parse::<Address<Ipv6>>().unwrap().is_global());
465 }
466
467 #[test]
468 fn ipv4_thisnet_is_not_global() {
469 assert!(!"0.1.2.3".parse::<Address<Ipv4>>().unwrap().is_global());
470 }
471
472 #[test]
473 fn ipv4_unspecified_is_not_global() {
474 assert!(!Address::<Ipv4>::UNSPECIFIED.is_global());
475 }
476
477 #[test]
478 fn ipv6_unspecified_is_not_global() {
479 assert!(!Address::<Ipv6>::UNSPECIFIED.is_global());
480 }
481
482 #[test]
483 fn ipv4_localhost_is_not_global() {
484 assert!(!Address::<Ipv4>::LOCALHOST.is_global());
485 }
486
487 #[test]
488 fn ipv6_localhost_is_not_global() {
489 assert!(!Address::<Ipv6>::LOCALHOST.is_global());
490 }
491
492 #[test]
493 fn ipv4_link_local_is_not_global() {
494 assert!(!"169.254.45.1".parse::<Address<Ipv4>>().unwrap().is_global());
495 }
496
497 #[test]
498 fn ipv6_link_local_is_not_global() {
499 assert!(!"fe80::1".parse::<Address<Ipv6>>().unwrap().is_global());
500 }
501
502 #[test]
503 fn ipv4_broadcast_is_not_global() {
504 assert!(!Address::<Ipv4>::BROADCAST.is_global());
505 }
506
507 #[test]
508 fn ipv4_doc_1_is_not_global() {
509 assert!(!"192.0.2.255".parse::<Address<Ipv4>>().unwrap().is_global());
510 }
511
512 #[test]
513 fn ipv4_doc_2_is_not_global() {
514 assert!(!"198.51.100.65"
515 .parse::<Address<Ipv4>>()
516 .unwrap()
517 .is_global());
518 }
519
520 #[test]
521 fn ipv4_doc_3_is_not_global() {
522 assert!(!"203.0.113.6".parse::<Address<Ipv4>>().unwrap().is_global());
523 }
524
525 #[test]
526 fn ipv6_doc_is_not_global() {
527 assert!(!"2001:db8::1".parse::<Address<Ipv6>>().unwrap().is_global());
528 }
529
530 #[test]
531 fn ipv4_shared_is_not_global() {
532 assert!(!"100.100.0.0".parse::<Address<Ipv4>>().unwrap().is_global());
533 }
534
535 #[test]
536 fn ipv4_proto_specific_1_is_not_global() {
537 assert!(!"192.0.0.0".parse::<Address<Ipv4>>().unwrap().is_global());
538 }
539
540 #[test]
541 fn ipv4_proto_specific_2_is_not_global() {
542 assert!(!"192.0.0.255".parse::<Address<Ipv4>>().unwrap().is_global());
543 }
544
545 #[test]
546 fn ipv6_proto_specific_is_not_global() {
547 assert!(!"2001:100::1".parse::<Address<Ipv6>>().unwrap().is_global());
548 }
549
550 #[test]
551 fn ipv4_reserved_is_not_global() {
552 assert!(!"250.10.20.30".parse::<Address<Ipv4>>().unwrap().is_global());
553 }
554
555 #[test]
556 fn ipv4_benchmarking_is_not_global() {
557 assert!(!"198.18.0.0".parse::<Address<Ipv4>>().unwrap().is_global());
558 }
559
560 #[test]
561 fn ipv6_benchmarking_is_not_global() {
562 assert!(!"2001:2::1".parse::<Address<Ipv6>>().unwrap().is_global());
563 }
564
565 #[test]
566 fn ipv4_local_multicast_is_not_global() {
567 assert!(!"224.0.0.1".parse::<Address<Ipv4>>().unwrap().is_global());
568 }
569
570 #[test]
571 fn ipv4_domain_multicast_is_not_global() {
572 assert!(!"239.0.0.1".parse::<Address<Ipv4>>().unwrap().is_global());
573 }
574
575 #[test]
576 fn ipv6_non_global_multicast_is_not_global() {
577 assert!(!"ff08::1".parse::<Address<Ipv6>>().unwrap().is_global());
578 }
579
580 #[test]
581 fn ipv4_pcp_anycast_is_global() {
582 assert!("192.0.0.9".parse::<Address<Ipv4>>().unwrap().is_global());
583 }
584
585 #[test]
586 fn ipv6_orchidv2_is_global() {
587 assert!("2001:20::1".parse::<Address<Ipv6>>().unwrap().is_global());
588 }
589
590 #[test]
591 fn ipv4_global_multicast_is_global() {
592 assert!("224.0.1.1".parse::<Address<Ipv4>>().unwrap().is_global());
593 }
594
595 #[test]
596 fn ipv6_global_multicast_is_global() {
597 assert!("ff0e::1".parse::<Address<Ipv6>>().unwrap().is_global());
598 }
599
600 #[test]
601 fn ipv4_global_unicast_is_global() {
602 assert!("1.1.1.1".parse::<Address<Ipv4>>().unwrap().is_global());
603 }
604
605 #[test]
606 fn ipv6_global_unicast_is_global() {
607 assert!("2606:4700:4700::1111"
608 .parse::<Address<Ipv6>>()
609 .unwrap()
610 .is_global());
611 }
612
613 #[test]
614 fn ipv4_loopback_is_loopback() {
615 assert!("127.0.0.53".parse::<Address<Ipv4>>().unwrap().is_loopback());
616 }
617
618 #[test]
619 fn ipv6_loopback_is_loopback() {
620 assert!("::1".parse::<Address<Ipv6>>().unwrap().is_loopback());
621 }
622
623 #[test]
624 fn ipv4_multicast_is_multicast() {
625 assert!("224.254.0.0"
626 .parse::<Address<Ipv4>>()
627 .unwrap()
628 .is_multicast());
629 }
630
631 #[test]
632 fn ipv4_unicast_is_not_multicast() {
633 assert!(!"172.16.10.65"
634 .parse::<Address<Ipv4>>()
635 .unwrap()
636 .is_multicast());
637 }
638
639 #[test]
640 fn ipv6_multicast_is_multicast() {
641 assert!("ff01::1".parse::<Address<Ipv6>>().unwrap().is_multicast());
642 }
643
644 #[test]
645 fn ipv4_unspecified_is_unspecified() {
646 assert!("0.0.0.0".parse::<Address<Ipv4>>().unwrap().is_unspecified());
647 }
648
649 #[test]
650 fn ipv6_unspecified_is_unspecified() {
651 assert!("::".parse::<Address<Ipv6>>().unwrap().is_unspecified());
652 }
653
654 #[test]
655 fn ipv6_ula_is_unique_local() {
656 assert!("fc01::1"
657 .parse::<Address<Ipv6>>()
658 .unwrap()
659 .is_unique_local());
660 }
661
662 #[test]
663 fn ipv6_doc_is_not_unique_local() {
664 assert!(!"2001:db8::1"
665 .parse::<Address<Ipv6>>()
666 .unwrap()
667 .is_unique_local());
668 }
669
670 #[test]
671 fn ipv4_private_is_not_unique_local() {
672 assert!(!"192.168.1.1"
673 .parse::<Address<Ipv4>>()
674 .unwrap()
675 .is_unique_local());
676 }
677
678 #[test]
679 fn ipv6_unicast_is_unicast() {
680 assert!("2001:db8::1".parse::<Address<Ipv6>>().unwrap().is_unicast());
681 }
682 #[test]
683 fn ipv4_unicast_is_unicast() {
684 assert!("192.168.1.1".parse::<Address<Ipv4>>().unwrap().is_unicast());
685 }
686 #[test]
687 fn ipv6_multicast_is_not_unicast() {
688 assert!(!"ffaa::1".parse::<Address<Ipv6>>().unwrap().is_unicast());
689 }
690 #[test]
691 fn ipv4_multicast_is_not_unicast() {
692 assert!(!"239.0.0.1".parse::<Address<Ipv4>>().unwrap().is_unicast());
693 }
694 #[test]
695 fn ipv4_broadcast_is_not_unicast() {
696 assert!(!"255.255.255.255"
697 .parse::<Address<Ipv4>>()
698 .unwrap()
699 .is_unicast());
700 }
701
702 #[test]
703 fn ipv4_unicast_global_is_unicast_global() {
704 assert!("1.1.1.1"
705 .parse::<Address<Ipv4>>()
706 .unwrap()
707 .is_unicast_global());
708 }
709 #[test]
710 fn ipv6_unicast_global_is_unicast_global() {
711 assert!("2606:4700:4700::1111"
712 .parse::<Address<Ipv6>>()
713 .unwrap()
714 .is_unicast_global());
715 }
716 #[test]
717 fn ipv4_unicast_private_is_not_unicast_global() {
718 assert!(!"192.168.1.1"
719 .parse::<Address<Ipv4>>()
720 .unwrap()
721 .is_unicast_global());
722 }
723 #[test]
724 fn ipv4_multicast_global_is_not_unicast_global() {
725 assert!(!"225.0.0.1"
726 .parse::<Address<Ipv4>>()
727 .unwrap()
728 .is_unicast_global());
729 }
730 #[test]
731 fn ipv6_unicast_documentation_is_not_unicast_global() {
732 assert!(!"2001:db8::1"
733 .parse::<Address<Ipv6>>()
734 .unwrap()
735 .is_unicast_global());
736 }
737 #[test]
738 fn ipv6_multicast_global_is_not_unicast_global() {
739 assert!(!"ff0e::1"
740 .parse::<Address<Ipv6>>()
741 .unwrap()
742 .is_unicast_global());
743 }
744}