cidr/parsers/
inetaddr.rs

1use std::net::{
2	AddrParseError,
3	IpAddr,
4	Ipv4Addr,
5};
6
7/// Parse "loose" IPv4 address similar to POSIX `inet_addr`
8///
9/// This also accepts inputs that are not 4 decimal represented octects separated by dots.
10///
11/// * Allows less than 4 numbers separated by dots; the last number is
12///   interpreted as an 32-bit (no dot), 24-bit (1 dot), 16-bit (2 dots) number.
13///   Other numbers still must be octets (i.e. 8 bit)
14/// * Allows hexadecimal ("0x" or "0X" prefix) and octal ("0" prefix) numbers
15///   in all places.
16///
17/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_addr.html>
18pub fn inet_addr(s: &str) -> Option<Ipv4Addr> {
19	let mut slots = [0u32; 4];
20	let mut last_slot = 0;
21	for (ndx, part) in s.split('.').enumerate() {
22		if ndx >= 4 {
23			// too many '.'
24			return None;
25		}
26		let slot = if part.starts_with("0x") || part.starts_with("0X") {
27			let part = &part[2..];
28			if part.starts_with('+') {
29				// we don't want to support "0x+..."
30				return None;
31			}
32			u32::from_str_radix(part, 16)
33		} else if part.starts_with('0') {
34			u32::from_str_radix(part, 8)
35		} else {
36			part.parse::<u32>()
37		};
38		slots[ndx] = slot.ok()?;
39		last_slot = ndx;
40	}
41	debug_assert!(last_slot <= 3, "can't have more than 4");
42
43	let num = if last_slot == 0 {
44		slots[0]
45	} else {
46		let mut base: u32 = 0;
47		for (ndx, &slot) in slots.iter().enumerate().take(last_slot) {
48			if slot >= 256 {
49				// leading parts must be octects
50				return None;
51			}
52			// shift by 24, 16 or 8
53			base |= slots[ndx] << ((3 - ndx) * 8);
54		}
55		// last 3 => have 4 => 1 byte, last 2 => have 3 => 2 bytes, last 1 => have 2 => 3 bytes
56		let last_slot_bit_limit = (4 - last_slot) * 8;
57		if slots[last_slot] >= 1 << last_slot_bit_limit {
58			// last part too big
59			return None;
60		}
61		// never shift last slot
62		base | slots[last_slot]
63	};
64
65	Some(Ipv4Addr::from(num))
66}
67
68/// Parse IPv4 address; fall back to `inet_addr` if normal parser fails
69pub fn parse_loose_ipv4(s: &str) -> Result<Ipv4Addr, AddrParseError> {
70	match s.parse() {
71		Ok(a) => Ok(a),
72		Err(e) => {
73			if let Some(a) = inet_addr(s) {
74				Ok(a)
75			} else {
76				Err(e)
77			}
78		},
79	}
80}
81
82/// Parse IP address; fall back to `inet_addr` if normal parser fails
83pub fn parse_loose_ip(s: &str) -> Result<IpAddr, AddrParseError> {
84	match s.parse() {
85		Ok(a) => Ok(a),
86		Err(e) => {
87			if let Some(a) = inet_addr(s) {
88				Ok(IpAddr::V4(a))
89			} else {
90				Err(e)
91			}
92		},
93	}
94}
95
96#[cfg(test)]
97mod tests {
98	use std::net::{
99		AddrParseError,
100		IpAddr,
101		Ipv4Addr,
102	};
103
104	fn run(s: &str, expect: Ipv4Addr) {
105		assert_eq!(super::inet_addr(s), Some(expect));
106		assert_eq!(super::parse_loose_ipv4(s), Ok::<_, AddrParseError>(expect));
107		assert_eq!(
108			super::parse_loose_ip(s),
109			Ok::<_, AddrParseError>(IpAddr::V4(expect))
110		);
111	}
112
113	#[test]
114	fn test_loose_ipv4() {
115		run("10.0.1.2", Ipv4Addr::from([10, 0, 1, 2]));
116		run("10.0.258", Ipv4Addr::from([10, 0, 1, 2]));
117		run("0xA000102", Ipv4Addr::from([10, 0, 1, 2]));
118		run("0XA000102", Ipv4Addr::from([10, 0, 1, 2]));
119		run("012.0x102", Ipv4Addr::from([10, 0, 1, 2]));
120
121		run("127.0.0.1", Ipv4Addr::from([127, 0, 0, 1]));
122		run("127.0.1", Ipv4Addr::from([127, 0, 0, 1]));
123		run("127.1", Ipv4Addr::from([127, 0, 0, 1]));
124
125		run("0", Ipv4Addr::from([0, 0, 0, 0]));
126		run("1", Ipv4Addr::from([0, 0, 0, 1]));
127	}
128}