Skip to content

Commit 95b3689

Browse files
committed
Implement FromStr for IpAddr and SocketAddr
Better than that in rt::uv::net, because it: * handles invalid input explicitly, without fail!() * parses socket address, not just IP * handles various ipv4-in-ipv6 addresses, like 2001:db8:122:344::192.0.2.33 (see http://tools.ietf.org/html/rfc6052 for example) * rejects output like `127.0000000.0.1` * does not allocate heap memory * have unit tests
1 parent ca63850 commit 95b3689

File tree

1 file changed

+365
-0
lines changed
  • src/libstd/rt/io/net

1 file changed

+365
-0
lines changed

src/libstd/rt/io/net/ip.rs

+365
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
// except according to those terms.
1010

1111
use num::FromStrRadix;
12+
use vec::MutableCloneableVector;
1213
use to_str::ToStr;
14+
use from_str::FromStr;
15+
use option::{Option, None, Some};
16+
1317

1418
type Port = u16;
1519

@@ -73,3 +77,364 @@ impl ToStr for SocketAddr {
7377
}
7478
}
7579
}
80+
81+
struct Parser<'self> {
82+
// parsing as ASCII, so can use byte array
83+
s: &'self [u8],
84+
pos: uint,
85+
}
86+
87+
impl<'self> Parser<'self> {
88+
fn new(s: &'self str) -> Parser<'self> {
89+
Parser {
90+
s: s.as_bytes(),
91+
pos: 0,
92+
}
93+
}
94+
95+
fn is_eof(&self) -> bool {
96+
self.pos == self.s.len()
97+
}
98+
99+
// Commit only if parser returns Some
100+
fn read_atomically<T>(&mut self, cb: &fn(&mut Parser) -> Option<T>) -> Option<T> {
101+
let pos = self.pos;
102+
let r = cb(self);
103+
if r.is_none() {
104+
self.pos = pos;
105+
}
106+
r
107+
}
108+
109+
// Commit only if parser read till EOF
110+
fn read_till_eof<T>(&mut self, cb: &fn(&mut Parser) -> Option<T>) -> Option<T> {
111+
do self.read_atomically |p| {
112+
cb(p).filtered(|_| p.is_eof())
113+
}
114+
}
115+
116+
// Return result of first successful parser
117+
fn read_or<T>(&mut self, parsers: &[&fn(&mut Parser) -> Option<T>]) -> Option<T> {
118+
for pf in parsers.iter() {
119+
match self.read_atomically(|p: &mut Parser| (*pf)(p)) {
120+
Some(r) => return Some(r),
121+
None => {}
122+
}
123+
}
124+
None
125+
}
126+
127+
// Apply 3 parsers sequentially
128+
fn read_seq_3<A, B, C>(&mut self,
129+
pa: &fn(&mut Parser) -> Option<A>,
130+
pb: &fn(&mut Parser) -> Option<B>,
131+
pc: &fn(&mut Parser) -> Option<C>
132+
) -> Option<(A, B, C)>
133+
{
134+
do self.read_atomically |p| {
135+
let a = pa(p);
136+
let b = if a.is_some() { pb(p) } else { None };
137+
let c = if b.is_some() { pc(p) } else { None };
138+
match (a, b, c) {
139+
(Some(a), Some(b), Some(c)) => Some((a, b, c)),
140+
_ => None
141+
}
142+
}
143+
}
144+
145+
// Read next char
146+
fn read_char(&mut self) -> Option<char> {
147+
if self.is_eof() {
148+
None
149+
} else {
150+
let r = self.s[self.pos] as char;
151+
self.pos += 1;
152+
Some(r)
153+
}
154+
}
155+
156+
// Return char and advance iff next char is equal to requested
157+
fn read_given_char(&mut self, c: char) -> Option<char> {
158+
do self.read_atomically |p| {
159+
p.read_char().filtered(|&next| next == c)
160+
}
161+
}
162+
163+
// Read digit
164+
fn read_digit(&mut self, radix: u8) -> Option<u8> {
165+
fn parse_digit(c: char, radix: u8) -> Option<u8> {
166+
// assuming radix is either 10 or 16
167+
if c >= '0' && c <= '9' {
168+
Some((c - '0') as u8)
169+
} else if radix > 10 && c >= 'a' && c < 'a' + (radix - 10) as char {
170+
Some((c - 'a' + (10 as char)) as u8)
171+
} else if radix > 10 && c >= 'A' && c < 'A' + (radix - 10) as char {
172+
Some((c - 'A' + (10 as char)) as u8)
173+
} else {
174+
None
175+
}
176+
}
177+
178+
do self.read_atomically |p| {
179+
p.read_char().chain(|c| parse_digit(c, radix))
180+
}
181+
}
182+
183+
fn read_number_impl(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
184+
let mut r = 0u32;
185+
let mut digit_count = 0;
186+
loop {
187+
match self.read_digit(radix) {
188+
Some(d) => {
189+
r = r * (radix as u32) + (d as u32);
190+
digit_count += 1;
191+
if digit_count > max_digits || r >= upto {
192+
return None
193+
}
194+
}
195+
None => {
196+
if digit_count == 0 {
197+
return None
198+
} else {
199+
return Some(r)
200+
}
201+
}
202+
};
203+
}
204+
}
205+
206+
// Read number, failing if max_digits of number value exceeded
207+
fn read_number(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
208+
do self.read_atomically |p| {
209+
p.read_number_impl(radix, max_digits, upto)
210+
}
211+
}
212+
213+
fn read_ipv4_addr_impl(&mut self) -> Option<IpAddr> {
214+
let mut bs = [0u8, ..4];
215+
let mut i = 0;
216+
while i < 4 {
217+
if i != 0 && self.read_given_char('.').is_none() {
218+
return None;
219+
}
220+
221+
let octet = self.read_number(10, 3, 0x100).map(|&n| n as u8);
222+
match octet {
223+
Some(d) => bs[i] = d,
224+
None => return None,
225+
};
226+
i += 1;
227+
}
228+
Some(Ipv4Addr(bs[0], bs[1], bs[2], bs[3]))
229+
}
230+
231+
// Read IPv4 address
232+
fn read_ipv4_addr(&mut self) -> Option<IpAddr> {
233+
do self.read_atomically |p| {
234+
p.read_ipv4_addr_impl()
235+
}
236+
}
237+
238+
fn read_ipv6_addr_impl(&mut self) -> Option<IpAddr> {
239+
fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> IpAddr {
240+
assert!(head.len() + tail.len() <= 8);
241+
let mut gs = [0u16, ..8];
242+
gs.copy_from(head);
243+
gs.mut_slice(8 - tail.len(), 8).copy_from(tail);
244+
Ipv6Addr(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7])
245+
}
246+
247+
fn read_groups(p: &mut Parser, groups: &mut [u16, ..8], limit: uint) -> (uint, bool) {
248+
let mut i = 0;
249+
while i < limit {
250+
if i < limit - 1 {
251+
let ipv4 = do p.read_atomically |p| {
252+
if i == 0 || p.read_given_char(':').is_some() {
253+
p.read_ipv4_addr()
254+
} else {
255+
None
256+
}
257+
};
258+
match ipv4 {
259+
Some(Ipv4Addr(a, b, c, d)) => {
260+
groups[i + 0] = (a as u16 << 8) | (b as u16);
261+
groups[i + 1] = (c as u16 << 8) | (d as u16);
262+
return (i + 2, true);
263+
}
264+
_ => {}
265+
}
266+
}
267+
268+
let group = do p.read_atomically |p| {
269+
if i == 0 || p.read_given_char(':').is_some() {
270+
p.read_number(16, 4, 0x10000).map(|&n| n as u16)
271+
} else {
272+
None
273+
}
274+
};
275+
match group {
276+
Some(g) => groups[i] = g,
277+
None => return (i, false)
278+
}
279+
i += 1;
280+
}
281+
(i, false)
282+
}
283+
284+
let mut head = [0u16, ..8];
285+
let (head_size, head_ipv4) = read_groups(self, &mut head, 8);
286+
287+
if head_size == 8 {
288+
return Some(Ipv6Addr(
289+
head[0], head[1], head[2], head[3],
290+
head[4], head[5], head[6], head[7]))
291+
}
292+
293+
// IPv4 part is not allowed before `::`
294+
if head_ipv4 {
295+
return None
296+
}
297+
298+
// read `::` if previous code parsed less than 8 groups
299+
if !self.read_given_char(':').is_some() || !self.read_given_char(':').is_some() {
300+
return None;
301+
}
302+
303+
let mut tail = [0u16, ..8];
304+
let (tail_size, _) = read_groups(self, &mut tail, 8 - head_size);
305+
Some(ipv6_addr_from_head_tail(head.slice(0, head_size), tail.slice(0, tail_size)))
306+
}
307+
308+
fn read_ipv6_addr(&mut self) -> Option<IpAddr> {
309+
do self.read_atomically |p| {
310+
p.read_ipv6_addr_impl()
311+
}
312+
}
313+
314+
fn read_ip_addr(&mut self) -> Option<IpAddr> {
315+
let ipv4_addr = |p: &mut Parser| p.read_ipv4_addr();
316+
let ipv6_addr = |p: &mut Parser| p.read_ipv6_addr();
317+
self.read_or([ipv4_addr, ipv6_addr])
318+
}
319+
320+
fn read_socket_addr(&mut self) -> Option<SocketAddr> {
321+
let ip_addr = |p: &mut Parser| {
322+
let ipv4_p = |p: &mut Parser| p.read_ip_addr();
323+
let ipv6_p = |p: &mut Parser| {
324+
let open_br = |p: &mut Parser| p.read_given_char('[');
325+
let ip_addr = |p: &mut Parser| p.read_ipv6_addr();
326+
let clos_br = |p: &mut Parser| p.read_given_char(']');
327+
p.read_seq_3::<char, IpAddr, char>(open_br, ip_addr, clos_br)
328+
.map(|&t| match t { (_, ip, _) => ip })
329+
};
330+
p.read_or([ipv4_p, ipv6_p])
331+
};
332+
let colon = |p: &mut Parser| p.read_given_char(':');
333+
let port = |p: &mut Parser| p.read_number(10, 5, 0x10000).map(|&n| n as u16);
334+
335+
// host, colon, port
336+
self.read_seq_3::<IpAddr, char, u16>(ip_addr, colon, port)
337+
.map(|&t| match t { (ip, _, port) => SocketAddr { ip: ip, port: port } })
338+
}
339+
}
340+
341+
impl FromStr for IpAddr {
342+
fn from_str(s: &str) -> Option<IpAddr> {
343+
do Parser::new(s).read_till_eof |p| {
344+
p.read_ip_addr()
345+
}
346+
}
347+
}
348+
349+
impl FromStr for SocketAddr {
350+
fn from_str(s: &str) -> Option<SocketAddr> {
351+
do Parser::new(s).read_till_eof |p| {
352+
p.read_socket_addr()
353+
}
354+
}
355+
}
356+
357+
358+
#[cfg(test)]
359+
mod test {
360+
use super::*;
361+
use from_str::FromStr;
362+
use option::{Some, None};
363+
364+
#[test]
365+
fn test_from_str_ipv4() {
366+
assert_eq!(Some(Ipv4Addr(127, 0, 0, 1)), FromStr::from_str("127.0.0.1"));
367+
assert_eq!(Some(Ipv4Addr(255, 255, 255, 255)), FromStr::from_str("255.255.255.255"));
368+
assert_eq!(Some(Ipv4Addr(0, 0, 0, 0)), FromStr::from_str("0.0.0.0"));
369+
370+
// out of range
371+
assert_eq!(None, FromStr::from_str::<IpAddr>("256.0.0.1"));
372+
// too short
373+
assert_eq!(None, FromStr::from_str::<IpAddr>("255.0.0"));
374+
// too long
375+
assert_eq!(None, FromStr::from_str::<IpAddr>("255.0.0.1.2"));
376+
// no number between dots
377+
assert_eq!(None, FromStr::from_str::<IpAddr>("255.0..1"));
378+
}
379+
380+
#[test]
381+
fn test_from_str_ipv6() {
382+
assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("0:0:0:0:0:0:0:0"));
383+
assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("0:0:0:0:0:0:0:1"));
384+
385+
assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("::1"));
386+
assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("::"));
387+
388+
assert_eq!(Some(Ipv6Addr(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)),
389+
FromStr::from_str("2a02:6b8::11:11"));
390+
391+
// too long group
392+
assert_eq!(None, FromStr::from_str::<IpAddr>("::00000"));
393+
// too short
394+
assert_eq!(None, FromStr::from_str::<IpAddr>("1:2:3:4:5:6:7"));
395+
// too long
396+
assert_eq!(None, FromStr::from_str::<IpAddr>("1:2:3:4:5:6:7:8:9"));
397+
// triple colon
398+
assert_eq!(None, FromStr::from_str::<IpAddr>("1:2:::6:7:8"));
399+
// two double colons
400+
assert_eq!(None, FromStr::from_str::<IpAddr>("1:2::6::8"));
401+
}
402+
403+
#[test]
404+
fn test_from_str_ipv4_in_ipv6() {
405+
assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 49152, 545)),
406+
FromStr::from_str("::192.0.2.33"));
407+
assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)),
408+
FromStr::from_str("::FFFF:192.0.2.33"));
409+
assert_eq!(Some(Ipv6Addr(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)),
410+
FromStr::from_str("64:ff9b::192.0.2.33"));
411+
assert_eq!(Some(Ipv6Addr(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)),
412+
FromStr::from_str("2001:db8:122:c000:2:2100:192.0.2.33"));
413+
414+
// colon after v4
415+
assert_eq!(None, FromStr::from_str::<IpAddr>("::127.0.0.1:"));
416+
// not enought groups
417+
assert_eq!(None, FromStr::from_str::<IpAddr>("1.2.3.4.5:127.0.0.1"));
418+
// too many groups
419+
assert_eq!(None, FromStr::from_str::<IpAddr>("1.2.3.4.5:6:7:127.0.0.1"));
420+
}
421+
422+
#[test]
423+
fn test_from_str_socket_addr() {
424+
assert_eq!(Some(SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 80 }),
425+
FromStr::from_str("77.88.21.11:80"));
426+
assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }),
427+
FromStr::from_str("[2a02:6b8:0:1::1]:53"));
428+
assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0x7F00, 1), port: 22 }),
429+
FromStr::from_str("[::127.0.0.1]:22"));
430+
431+
// without port
432+
assert_eq!(None, FromStr::from_str::<SocketAddr>("127.0.0.1"));
433+
// without port
434+
assert_eq!(None, FromStr::from_str::<SocketAddr>("127.0.0.1:"));
435+
// wrong brackets around v4
436+
assert_eq!(None, FromStr::from_str::<SocketAddr>("[127.0.0.1]:22"));
437+
// port out of range
438+
assert_eq!(None, FromStr::from_str::<SocketAddr>("127.0.0.1:123456"));
439+
}
440+
}

0 commit comments

Comments
 (0)