atmos/kernel/net/
addr_util.rs1use alloc::string::String;
10
11pub fn is_valid_request_host(host: &str) -> bool {
17 let (name, port) = match host.split_once(':') {
18 Some((n, p)) => (n, Some(p)),
19 None => (host, None),
20 };
21 let name_ok = !name.is_empty()
22 && name.len() <= 253
23 && name.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'.' || b == b'-');
24 let port_ok = match port {
25 None => true,
26 Some(p) => {
27 !p.is_empty()
28 && p.len() <= 5
29 && p.bytes().all(|b| b.is_ascii_digit())
30 && p.parse::<u32>().is_ok_and(|v| (1..=65535).contains(&v))
31 }
32 };
33 name_ok && port_ok
34}
35
36pub fn parse_host_port_with_default(host: &str, default_port: u16) -> (&str, u16) {
39 if let Some((h, p_str)) = host.split_once(':') {
40 if let Ok(p) = p_str.parse::<u16>() {
41 if p > 0 {
42 return (h, p);
43 }
44 }
45 }
46 (host, default_port)
47}
48
49pub fn parse_host_port(host: &str) -> (&str, u16) {
51 parse_host_port_with_default(host, 80)
52}
53
54pub fn parse_ipv4(s: &str) -> Option<[u8; 4]> {
55 let mut out = [0u8; 4];
56 let mut part_idx = 0usize;
57 let mut cur: u16 = 0;
58 let mut has_digit = false;
59
60 for ch in s.chars() {
61 if ch == '.' {
62 if !has_digit || part_idx >= 4 || cur > 255 {
63 return None;
64 }
65 out[part_idx] = cur as u8;
66 part_idx += 1;
67 cur = 0;
68 has_digit = false;
69 continue;
70 }
71
72 if !ch.is_ascii_digit() {
73 return None;
74 }
75
76 cur = cur
77 .saturating_mul(10)
78 .saturating_add((ch as u16).saturating_sub('0' as u16));
79 if cur > 255 {
80 return None;
81 }
82 has_digit = true;
83 }
84
85 if !has_digit || part_idx != 3 {
86 return None;
87 }
88
89 out[3] = cur as u8;
90 Some(out)
91}
92
93pub fn parse_mac(s: &str) -> Option<[u8; 6]> {
94 let mut mac = [0u8; 6];
95 let mut idx = 0usize;
96
97 for part in s.split(':') {
98 if idx >= 6 || part.len() != 2 {
99 return None;
100 }
101
102 let bytes = part.as_bytes();
103 let hi = hex_nibble(bytes[0])?;
104 let lo = hex_nibble(bytes[1])?;
105 mac[idx] = (hi << 4) | lo;
106 idx += 1;
107 }
108
109 if idx != 6 {
110 return None;
111 }
112
113 Some(mac)
114}
115
116pub fn format_ipv4(ip: [u8; 4]) -> String {
117 alloc::format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3])
118}
119
120pub fn format_mac(mac: [u8; 6]) -> String {
121 alloc::format!(
122 "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
123 mac[0],
124 mac[1],
125 mac[2],
126 mac[3],
127 mac[4],
128 mac[5]
129 )
130}
131
132pub(crate) fn ipv4_header_checksum(header: &[u8]) -> u16 {
133 internet_checksum(header)
134}
135
136pub(crate) fn internet_checksum(data: &[u8]) -> u16 {
138 let mut sum: u32 = 0;
139 let mut i = 0usize;
140
141 while i + 1 < data.len() {
142 let word = ((data[i] as u16) << 8) | data[i + 1] as u16;
143 sum = sum.wrapping_add(word as u32);
144 i += 2;
145 }
146
147 if i < data.len() {
148 let word = (data[i] as u16) << 8;
149 sum = sum.wrapping_add(word as u32);
150 }
151
152 while (sum >> 16) != 0 {
153 sum = (sum & 0xFFFF) + (sum >> 16);
154 }
155
156 !(sum as u16)
157}
158
159fn hex_nibble(c: u8) -> Option<u8> {
160 match c {
161 b'0'..=b'9' => Some(c - b'0'),
162 b'a'..=b'f' => Some(c - b'a' + 10),
163 b'A'..=b'F' => Some(c - b'A' + 10),
164 _ => None,
165 }
166}