1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use std::fmt;
pub mod ipv4 {
use crate::*;
type Result<T> = std::result::Result<T, InvalidAddrErr>;
#[derive(Debug, Clone)]
pub struct InvalidAddrErr;
impl fmt::Display for InvalidAddrErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "invalid ipv4 address string")
}
}
// valid_ipv4 will parse a string and return a Result indicating if
// the string is a valid RFC 791 IPv4 address. If the address is valid
// the bool will be true. If it is not valid, an Err will be returned.
pub fn valid_ipv4(ipstr: &str) -> Result<bool> {
// A valid IPv4 address can be at most 15 characters in it's
// string representation. e.g., 100.100.100.101. It must be
// at least 7 characters in it's string representation, i.e.
// 1.1.1.1
if ipstr.len() > 15 || ipstr.len() < 7 {
return Err(InvalidAddrErr);
}
// This algorithm runs in O(N) time where N is the number of digits represented by characters
// in ipstr. We are looking for up to 4 "blocks", where a block is a set of 3 numbers delineated on
// at least one end by a separator character, the "dot" (.). We will iterate through the characters
// in the string and check each one as it comes, ensuring that this character does not invalidate the
// address string.
let mut block_count = 1;
let mut block: [char; 3] = ['\0'; 3];
let mut pos = 0;
// iterate character by character through the address string. If any invalidations are found,
// return immediately.
for c in ipstr.chars() {
// if the character is not a digit or a dot, the address is invalid.
if !c.is_ascii_digit() && c != '.' {
return Err(InvalidAddrErr);
}
// dots ('.') represent a seperator character in the address string,
// and most of the validation logic happens at a separation point.
if c == '.' {
// if we have a dot and we already have seen 4 blocks, the address is invalid.
if block_count == 4 {
return Err(InvalidAddrErr);
}
// if we have a dot and the previous character is a dot -- which we will know because
// the block will have a null character in it's first position, the address is invalid.
if block[0] == '\0' {
return Err(InvalidAddrErr);
}
// check if the block has three characters. if the last character is a null character,
// we only have two characters, and so any two digits [0-9] make up a valid block.
if block[2] != '\0' {
// if we have three characters in the block, we need to make sure
// that the first character is not greater than 2. We have already
// checked previously that the first character:
// a) is not '0'
// b) that it is a valid digit.
if block[0] > '2' {
return Err(InvalidAddrErr);
}
// if the first character is a 2, we need to make sure that the
// subsequent digits are not exceeding 255.
if block[0] == '2' && (block[1] > '5' || (block[1] == '5' && block[2] > '5')) {
return Err(InvalidAddrErr);
}
}
// if all the separator validation logic steps are successful,
// we can start parsing a new block. increment the block counter,
// reset the block, and set our reader position (pos) to 0.
block_count += 1;
block = ['\0'; 3];
pos = 0;
continue;
}
// if the reader position is at character 4, the address is invalid.
if pos == 3 {
return Err(InvalidAddrErr);
}
// if we get here, this is a valid character in the address! track it
// in the block and update our position to the next character.
block[pos] = c;
pos += 1;
}
Ok(true)
}
#[cfg(test)]
mod net_tests {
use super::valid_ipv4;
#[test]
fn test_valid_ip() {
let valids = Vec::from([
"127.0.0.1",
"192.168.0.9",
"10.0.0.1",
"255.255.255.255",
"2.255.99.254",
]);
let invalids = Vec::from([
"295.34.1.5.",
"215.0",
"215",
".10.256.0.9",
"365",
"365.1.0.9",
"10.256.0.1",
"10.358.0.1",
]);
for addr in valids {
let r = valid_ipv4(addr);
if r.is_err() {
panic!(
"correctness error: {} failed but should have succeeded",
addr
);
}
}
for addr in invalids {
let r = valid_ipv4(addr);
if r.is_ok() {
panic!(
"correctness error: {} succeeded but should have failed",
addr
);
}
}
}
}
}