Skip to main content

relibc/header/arpa_inet/
mod.rs

1//! `arpa/inet.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html>.
4
5use core::{
6    ptr, slice,
7    str::{self, FromStr},
8};
9
10use crate::{
11    c_str::CStr,
12    header::{
13        bits_arpainet::ntohl,
14        errno::{EAFNOSUPPORT, ENOSPC},
15        netinet_in::{INADDR_NONE, in_addr, in_addr_t, in6_addr},
16        sys_socket::{
17            constants::{AF_INET, AF_INET6},
18            socklen_t,
19        },
20    },
21    io::Write,
22    platform::{
23        self,
24        types::{c_char, c_int, c_void},
25    },
26    raw_cell::RawCell,
27};
28
29/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_addr.html>.
30///
31/// Converts the string pointed to by `cp`, in the standard IPv4 dotted
32/// decimal notation, to an integer value suitable for use as an Internet
33/// address.
34///
35/// # Deprecated
36/// The `inet_addr()` function was marked obsolescent in the Open Group Base
37/// Specifications Issue 8.
38///
39/// Applications should prefer `inet_pton()` over `inet_addr()` for the
40/// following reasons:
41/// - The return value from `inet_addr()` when converting 255.255.255.255 is
42///   indistinguishable from an error.
43/// - The `inet_pton()` function supports multiple address families.
44/// - The alternative textual representations supported by `inet_addr()` (but
45///   not `inet_pton()`) are often used maliciously to confuse or mislead
46///   users (e.g, for phishing).
47#[deprecated]
48#[unsafe(no_mangle)]
49pub unsafe extern "C" fn inet_addr(cp: *const c_char) -> in_addr_t {
50    let mut val: in_addr = in_addr { s_addr: 0 };
51
52    if unsafe { inet_aton(cp, &raw mut val) } > 0 {
53        val.s_addr
54    } else {
55        INADDR_NONE
56    }
57}
58
59/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/inet_aton.3.html>.
60///
61/// Converts the Internet host address `cp` from the IPv4 numbers-and-dots
62/// notation into binary form (in network byte order) and stores it in the
63/// structure that `inp` points to.
64#[unsafe(no_mangle)]
65pub unsafe extern "C" fn inet_aton(cp: *const c_char, inp: *mut in_addr) -> c_int {
66    let cp_cstr = unsafe { CStr::from_ptr(cp) };
67    let parts = unsafe { str::from_utf8_unchecked(cp_cstr.to_bytes()).split('.') };
68    let count = parts.clone().count();
69    if count > 4 {
70        return 0;
71    }
72    let mut result = 0;
73    let mut parts_iter = parts.peekable();
74    let mut index = 0u32;
75    while index < 4
76        && let Some(part) = parts_iter.next()
77    {
78        if let Ok(parsed_value) = {
79            if let Some(hex_or_oct) = part.strip_prefix('0')
80                && part.len() > 1
81            {
82                match hex_or_oct.bytes().next() {
83                    Some(b'x' | b'X') => u32::from_str_radix(&hex_or_oct[1..], 16),
84                    // While it is true that C2Y accepts 0o and 0O as octal prefixes, C17 doesn't
85                    // The POSIX spec defers to C17
86                    // see https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_addr.html
87                    _ => u32::from_str_radix(hex_or_oct, 8),
88                }
89            } else {
90                part.parse::<u32>()
91            }
92        } {
93            if parts_iter.peek().is_some() {
94                // this is not the last part
95                if parsed_value > 0xff {
96                    return 0;
97                }
98                result += parsed_value << (24 - index * 8);
99            } else {
100                // this is the last part
101                if index > 0 && parsed_value >= 1 << (32 - index * 8) {
102                    return 0;
103                } else {
104                    result += parsed_value;
105                }
106            }
107        } else {
108            return 0;
109        }
110        index += 1;
111    }
112    unsafe { (*inp.cast::<in_addr>()).s_addr = result.to_be() };
113    1
114}
115
116/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_lnaof.html>.
117///
118/// Takes an Internet host address specified by `in` and extracts the local
119/// network address part, in host byte order.
120///
121/// # Deprecation
122/// The `inet_lnaof()` function was specified in Networking Services Issue 5,
123/// but not in the Open Group Base Specifications Issue 6 and later.
124#[deprecated]
125#[unsafe(no_mangle)]
126pub extern "C" fn inet_lnaof(r#in: in_addr) -> in_addr_t {
127    if r#in.s_addr >> 24 < 128 {
128        r#in.s_addr & 0xff_ffff
129    } else if r#in.s_addr >> 24 < 192 {
130        r#in.s_addr & 0xffff
131    } else {
132        r#in.s_addr & 0xff
133    }
134}
135
136/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_makeaddr.html>.
137///
138/// Takes the Internet network number specified by `net` and the local network
139/// address specified by `lna`, both in host byte order, and constructs an
140/// Internet address from them.
141///
142/// # Deprecation
143/// The `inet_makeaddr()` function was specified in Networking Services Issue
144/// 5, but not in the Open Group Base Specifications Issue 6 and later.
145#[deprecated]
146#[unsafe(no_mangle)]
147pub extern "C" fn inet_makeaddr(net: in_addr_t, lna: in_addr_t) -> in_addr {
148    let mut output: in_addr = in_addr { s_addr: 0 };
149
150    if net < 256 {
151        output.s_addr = lna | net << 24;
152    } else if net < 65536 {
153        output.s_addr = lna | net << 16;
154    } else {
155        output.s_addr = lna | net << 8;
156    }
157
158    output
159}
160
161/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_netof.html>.
162///
163/// Takes an Internet host address specified by `in` and extracts the network
164/// number part, in host byte order.
165///
166/// # Deprecation
167/// The `inet_netof()` function was specified in Networking Services Issue 5,
168/// but not in the Open Group Base Specifications Issue 6 and later.
169#[deprecated]
170#[unsafe(no_mangle)]
171pub extern "C" fn inet_netof(r#in: in_addr) -> in_addr_t {
172    if r#in.s_addr >> 24 < 128 {
173        r#in.s_addr & 0xff_ffff
174    } else if r#in.s_addr >> 24 < 192 {
175        r#in.s_addr & 0xffff
176    } else {
177        r#in.s_addr & 0xff
178    }
179}
180
181/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_network.html>.
182///
183/// Converts the string pointed to by `cp`, in the Internet standard dot
184/// notation, to an integer value suitable for use as an Internet network
185/// number.
186///
187/// # Deprecation
188/// The `inet_network()` function was specified in Networking Services Issue 5,
189/// but not in the Open Group Base Specifications Issue 6 and later.
190#[deprecated]
191#[unsafe(no_mangle)]
192pub unsafe extern "C" fn inet_network(cp: *const c_char) -> in_addr_t {
193    ntohl(unsafe {
194        #[allow(deprecated)]
195        inet_addr(cp)
196    })
197}
198
199/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_addr.html>.
200///
201/// Converts the Internet host address specified by `in` to a string in the
202/// Internet standard dot notation.
203///
204/// # Deprecation
205/// The `inet_ntoa()` function was marked obsolescent in the Open Group Base
206/// Specifications Issue 8.
207///
208/// Applications should prefer `inet_ntop()` over `inet_ntoa()` as it supports
209/// multiple address families and is thread-safe.
210#[deprecated]
211#[unsafe(no_mangle)]
212pub unsafe extern "C" fn inet_ntoa(r#in: in_addr) -> *mut c_char {
213    static NTOA_ADDR: RawCell<[c_char; 16]> = RawCell::new([0; 16]);
214
215    unsafe {
216        let ptr = inet_ntop(
217            AF_INET,
218            ptr::from_ref::<in_addr>(&r#in).cast::<c_void>(),
219            NTOA_ADDR.unsafe_mut().as_mut_ptr(),
220            NTOA_ADDR.unsafe_ref().len() as socklen_t,
221        );
222        // Mutable pointer is required, inet_ntop returns destination as const pointer
223        ptr.cast_mut()
224    }
225}
226
227/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_ntop.html>.
228///
229/// Converts a numeric address into a text string suitable for presentation.
230#[unsafe(no_mangle)]
231pub unsafe extern "C" fn inet_ntop(
232    af: c_int,
233    src: *const c_void,
234    dst: *mut c_char,
235    size: socklen_t,
236) -> *const c_char {
237    if af != AF_INET {
238        platform::ERRNO.set(EAFNOSUPPORT);
239        ptr::null()
240    } else if size < 16 {
241        platform::ERRNO.set(ENOSPC);
242        ptr::null()
243    } else {
244        let s_addr = unsafe {
245            slice::from_raw_parts(
246                ptr::from_ref(&(*(src.cast::<in_addr>())).s_addr).cast::<u8>(),
247                4,
248            )
249        };
250        let mut w = platform::StringWriter(dst, size as usize);
251        let _ = write!(
252            w,
253            "{}.{}.{}.{}\0",
254            s_addr[0], s_addr[1], s_addr[2], s_addr[3]
255        );
256        dst
257    }
258}
259
260/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_ntop.html>.
261///
262/// Converts an address in its standard text presentation form into its
263/// numeric binary form.
264#[unsafe(no_mangle)]
265pub unsafe extern "C" fn inet_pton(af: c_int, src: *const c_char, dst: *mut c_void) -> c_int {
266    if af == AF_INET {
267        let s_addr = unsafe {
268            slice::from_raw_parts_mut(
269                ptr::from_mut(&mut (*dst.cast::<in_addr>()).s_addr).cast::<u8>(),
270                4,
271            )
272        };
273        let src_cstr = unsafe { CStr::from_ptr(src) };
274        let mut octets = unsafe { str::from_utf8_unchecked(src_cstr.to_bytes()).split('.') };
275        for part in s_addr.iter_mut().take(4) {
276            if let Some(n) = octets
277                .next()
278                .filter(|x| x.len() <= 3)
279                .and_then(|x| u8::from_str(x).ok())
280            {
281                *part = n;
282            } else {
283                return 0;
284            }
285        }
286        if octets.next().is_none() {
287            1 // Success
288        } else {
289            0
290        }
291    } else if af == AF_INET6 {
292        let src_str = unsafe { str::from_utf8_unchecked(CStr::from_ptr(src).to_bytes()) };
293        let mut chunks = vec![src_str];
294        let colons = src_str.bytes().filter(|&c| c == b':').count();
295        if !(2..=7).contains(&colons) {
296            return 0;
297        }
298        let dots = src_str.bytes().filter(|&c| c == b'.').count();
299        if dots != 0 && dots != 3 {
300            return 0;
301        }
302        let double_colon = src_str.find("::");
303        if colons < 2
304            || double_colon.is_some() && ((dots == 0 && colons > 7) || (dots == 3 && colons > 6))
305            || double_colon.is_none() && ((dots == 0 && colons != 7) || (dots == 3 && colons != 6))
306        {
307            return 0;
308        }
309        if dots == 3
310            && let Some(first_dot) = src_str.find('.')
311            && let Some(last_colon) = src_str.find(':')
312            && last_colon > first_dot
313        {
314            return 0;
315        }
316        if let Some(first) = src_str.find("::")
317            && let Some(last) = src_str.rfind("::")
318        {
319            // :: is allowed only once
320            if first != last {
321                return 0;
322            }
323            chunks = vec![&src_str[..first], &src_str[(first + 2)..]]
324        }
325
326        let s6_addr = unsafe {
327            slice::from_raw_parts_mut(
328                ptr::from_mut(&mut (*dst.cast::<in6_addr>()).s6_addr).cast::<u16>(),
329                8,
330            )
331        };
332        s6_addr.iter_mut().for_each(|w| *w = 0);
333
334        for (count, &chunk) in chunks
335            .iter()
336            .enumerate()
337            .filter(|&(_, &chunk)| !chunk.is_empty())
338        {
339            let mut parts = s6_addr
340                .iter_mut()
341                .skip(count * (8 - chunk.split(':').count() - dots / 3));
342            let mut words = chunk.split(':');
343            while let Some(word) = words.next()
344                && let Some(part) = parts.next()
345            {
346                if word.is_empty() {
347                    break;
348                } else if word.len() <= 4
349                    && let Some(n) = u16::from_str_radix(word, 16).ok()
350                {
351                    *part = n.to_be();
352                } else if word.contains('.') {
353                    let bytes =
354                        unsafe { slice::from_raw_parts_mut(ptr::from_mut(part).cast::<u8>(), 4) };
355                    let mut octets = word.split('.');
356                    for byte in bytes {
357                        if let Some(octet) = octets.next() {
358                            if octet.len() > 1 && octet.starts_with('0') {
359                                return 0;
360                            }
361                            if let Ok(value) = u8::from_str(octet) {
362                                *byte = value
363                            } else {
364                                return 0;
365                            }
366                        }
367                    }
368                } else {
369                    return 0;
370                }
371            }
372        }
373        1 // Success
374    } else {
375        platform::ERRNO.set(EAFNOSUPPORT);
376        -1
377    }
378}