Skip to main content

relibc/header/netdb/
mod.rs

1//! `netdb.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netdb.h.html>.
4
5mod dns;
6
7use core::{cell::Cell, fmt::Write, mem, net::Ipv4Addr, ptr, str};
8
9use alloc::{boxed::Box, str::SplitWhitespace, string::ToString, vec::Vec};
10
11use crate::{
12    c_str::{CStr, CString},
13    error::ResultExt,
14    header::{
15        arpa_inet::inet_aton,
16        bits_arpainet::{htons, ntohl},
17        bits_safamily_t::sa_family_t,
18        errno::*,
19        fcntl::O_RDONLY,
20        netinet_in::{in_addr, sockaddr_in},
21        stdlib::atoi,
22        strings::strcasecmp,
23        sys_socket::{constants::AF_INET, sockaddr, socklen_t},
24        unistd::SEEK_SET,
25    },
26    platform::{
27        self, Pal, Sys,
28        rlb::{Line, RawLineBuffer},
29        types::{c_char, c_int, c_void, uint32_t},
30    },
31    raw_cell::RawCell,
32};
33
34use crate::header::netinet_in::sockaddr_in6;
35
36#[cfg(target_os = "linux")]
37#[path = "linux.rs"]
38pub mod sys;
39
40#[cfg(target_os = "redox")]
41#[path = "redox.rs"]
42pub mod sys;
43
44pub use self::host::*;
45pub mod host;
46
47pub use self::lookup::*;
48pub mod lookup;
49
50/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netdb.h.html>.
51#[repr(C)]
52pub struct hostent {
53    h_name: *mut c_char,
54    h_aliases: *mut *mut c_char,
55    h_addrtype: c_int,
56    h_length: c_int,
57    h_addr_list: *mut *mut c_char,
58}
59
60/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netdb.h.html>.
61#[repr(C)]
62pub struct netent {
63    n_name: *mut c_char,         /* official name of net */
64    n_aliases: *mut *mut c_char, /* alias list */
65    n_addrtype: c_int,           /* net address type */
66    n_net: uint32_t,             /* network # */
67}
68
69/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netdb.h.html>.
70#[repr(C)]
71pub struct protoent {
72    p_name: *mut c_char,         /* official protocol name */
73    p_aliases: *mut *mut c_char, /* alias list */
74    p_proto: c_int,              /* protocol # */
75}
76
77/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netdb.h.html>.
78#[repr(C)]
79pub struct servent {
80    s_name: *mut c_char,         /* official service name */
81    s_aliases: *mut *mut c_char, /* alias list */
82    s_port: c_int,               /* port # */
83    s_proto: *mut c_char,        /* protocol to use */
84}
85
86/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netdb.h.html>.
87#[repr(C)]
88#[derive(Debug)]
89pub struct addrinfo {
90    ai_flags: c_int,           /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
91    ai_family: c_int,          /* PF_xxx */
92    ai_socktype: c_int,        /* SOCK_xxx */
93    ai_protocol: c_int,        /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
94    ai_addrlen: socklen_t,     /* length of ai_addr */
95    ai_canonname: *mut c_char, /* canonical name for hostname */
96    ai_addr: *mut sockaddr,    /* binary address */
97    ai_next: *mut addrinfo,    /* next structure in linked list */
98}
99
100pub const AI_PASSIVE: c_int = 0x0001;
101pub const AI_CANONNAME: c_int = 0x0002;
102pub const AI_NUMERICHOST: c_int = 0x0004;
103pub const AI_V4MAPPED: c_int = 0x0008;
104pub const AI_ALL: c_int = 0x0010;
105pub const AI_ADDRCONFIG: c_int = 0x0020;
106pub const AI_NUMERICSERV: c_int = 0x0400;
107
108pub const EAI_BADFLAGS: c_int = -1;
109pub const EAI_NONAME: c_int = -2;
110pub const EAI_AGAIN: c_int = -3;
111pub const EAI_FAIL: c_int = -4;
112pub const EAI_NODATA: c_int = -5;
113pub const EAI_FAMILY: c_int = -6;
114pub const EAI_SOCKTYPE: c_int = -7;
115pub const EAI_SERVICE: c_int = -8;
116pub const EAI_ADDRFAMILY: c_int = -9;
117pub const EAI_MEMORY: c_int = -10;
118pub const EAI_SYSTEM: c_int = -11;
119pub const EAI_OVERFLOW: c_int = -12;
120
121pub const NI_MAXHOST: c_int = 1025;
122pub const NI_MAXSERV: c_int = 32;
123
124pub const NI_NUMERICHOST: c_int = 0x0001;
125pub const NI_NUMERICSERV: c_int = 0x0002;
126pub const NI_NOFQDN: c_int = 0x0004;
127pub const NI_NAMEREQD: c_int = 0x0008;
128pub const NI_DGRAM: c_int = 0x0010;
129
130static mut NETDB: c_int = 0;
131pub static mut NET_ENTRY: netent = netent {
132    n_name: ptr::null_mut(),
133    n_aliases: ptr::null_mut(),
134    n_addrtype: 0,
135    n_net: 0,
136};
137pub static NET_NAME: RawCell<Option<Vec<u8>>> = RawCell::new(None);
138pub static NET_ALIASES: RawCell<Option<Vec<Vec<u8>>>> = RawCell::new(None);
139pub static mut NET_ADDR: Option<u32> = None;
140static mut N_POS: usize = 0;
141static mut NET_STAYOPEN: c_int = 0;
142
143#[thread_local]
144pub static H_ERRNO: Cell<c_int> = Cell::new(0);
145const H_UNSET: c_int = 0;
146pub const HOST_NOT_FOUND: c_int = 1;
147pub const NO_DATA: c_int = 2;
148pub const NO_RECOVERY: c_int = 3;
149pub const TRY_AGAIN: c_int = 4;
150
151// Expected length of addresses
152const SOCKLEN_AF_INET4: socklen_t = 4;
153const SOCKLEN_AF_INET6: socklen_t = 16;
154
155static mut PROTODB: c_int = 0;
156static mut PROTO_ENTRY: protoent = protoent {
157    p_name: ptr::null_mut(),
158    p_aliases: ptr::null_mut(),
159    p_proto: 0 as c_int,
160};
161static PROTO_NAME: RawCell<Option<Vec<u8>>> = RawCell::new(None);
162static PROTO_ALIASES: RawCell<Option<Vec<Vec<u8>>>> = RawCell::new(None);
163static mut PROTO_NUM: Option<c_int> = None;
164static mut P_POS: usize = 0;
165static mut PROTO_STAYOPEN: c_int = 0;
166
167static mut SERVDB: c_int = 0;
168static mut SERV_ENTRY: servent = servent {
169    s_name: ptr::null_mut(),
170    s_aliases: ptr::null_mut(),
171    s_port: 0 as c_int,
172    s_proto: ptr::null_mut(),
173};
174static SERV_NAME: RawCell<Option<Vec<u8>>> = RawCell::new(None);
175static SERV_ALIASES: RawCell<Option<Vec<Vec<u8>>>> = RawCell::new(None);
176static mut SERV_PORT: Option<c_int> = None;
177static SERV_PROTO: RawCell<Option<Vec<u8>>> = RawCell::new(None);
178static mut S_POS: usize = 0;
179static mut SERV_STAYOPEN: c_int = 0;
180
181fn bytes_to_box_str(bytes: &[u8]) -> Box<str> {
182    Box::from(core::str::from_utf8(bytes).unwrap_or(""))
183}
184
185/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
186#[unsafe(no_mangle)]
187pub unsafe extern "C" fn endnetent() {
188    if let Ok(()) = Sys::close(unsafe { NETDB }) {}; // TODO handle error
189    unsafe { NETDB = 0 };
190}
191
192/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endprotoent.html>.
193#[unsafe(no_mangle)]
194pub unsafe extern "C" fn endprotoent() {
195    if let Ok(()) = Sys::close(unsafe { PROTODB }) {}; // TODO handle error
196    unsafe { PROTODB = 0 };
197}
198
199/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endservent.html>.
200#[unsafe(no_mangle)]
201pub unsafe extern "C" fn endservent() {
202    if let Ok(()) = Sys::close(unsafe { SERVDB }) {}; // TODO handle error
203    unsafe { SERVDB = 0 };
204}
205
206/// See <https://pubs.opengroup.org/onlinepubs/009696799/functions/gethostbyaddr.html>.
207/// Resolve a host name from a given network address.
208///
209/// # Arguments
210/// * `v` - Address to resolve as a non-null [`in_addr`]
211/// * `length` -
212/// * `format` - AF_INET or AF_INET6
213///
214/// # Safety
215/// * `v` must be a valid pointer.
216/// * `length` must correctly match the size of `v` as expected by `format` (usually 4 or 16).
217/// * This function is not reentrant and may modify static data.
218///
219/// # Panics
220/// Panics if `v` is a null pointer.
221///
222/// # Deprecation
223/// Deprecated as of POSIX.1-2001 and removed in POSIX.1-2008.
224/// New code should use [`getaddrinfo`] instead.
225#[unsafe(no_mangle)]
226#[deprecated]
227pub unsafe extern "C" fn gethostbyaddr(
228    v: *const c_void,
229    length: socklen_t,
230    format: c_int,
231) -> *mut hostent {
232    assert!(
233        !v.is_null(),
234        "`gethostbyaddr()` called with null `v` (in_addr)"
235    );
236    // Uncomment if optional IPv6 support is added
237    // if length != SOCKLEN_AF_INET4 || length != SOCKLEN_AF_INET6 {
238    //     H_ERRNO.set(NO_RECOVERY);
239    //     return ptr::null_mut();
240    // }
241    if length != SOCKLEN_AF_INET4 {
242        H_ERRNO.set(NO_RECOVERY);
243        return ptr::null_mut();
244    }
245    let addr: in_addr = unsafe { (*(v as *mut in_addr)).clone() };
246
247    // check the hosts file first
248    let mut p: *mut hostent;
249    unsafe { sethostent(HOST_STAYOPEN) };
250    while {
251        p = unsafe { gethostent() };
252        !p.is_null()
253    } {
254        let mut cp = unsafe { (*p).h_addr_list };
255        loop {
256            if cp.is_null() {
257                break;
258            }
259            if (unsafe { *cp }).is_null() {
260                break;
261            }
262            let mut cp_slice: [c_char; 4] = [0; 4];
263            unsafe { (*cp).copy_to(cp_slice.as_mut_ptr(), 4) };
264            #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
265            let cp_s_addr = unsafe { mem::transmute::<[c_char; 4], u32>(cp_slice) };
266            #[cfg(any(target_arch = "riscv64", target_arch = "aarch64"))]
267            let cp_s_addr = u32::from_ne_bytes(cp_slice);
268            if cp_s_addr == addr.s_addr {
269                unsafe { sethostent(HOST_STAYOPEN) };
270                return p;
271            }
272            cp = unsafe { cp.offset(1) };
273        }
274    }
275
276    //TODO actually get aliases
277    let mut _host_aliases: Vec<Vec<u8>> = vec![vec![b'\0']];
278    let mut host_aliases: Vec<*mut c_char> = vec![ptr::null_mut()];
279    unsafe { HOST_ALIASES.unsafe_set(Some(_host_aliases)) };
280
281    match lookup_addr(addr.clone()).map(|host_names| host_names.into_iter().next()) {
282        Ok(Some(host_name)) => {
283            unsafe { _HOST_ADDR_LIST = addr.s_addr.to_ne_bytes() };
284            unsafe {
285                HOST_ADDR_LIST = [(&raw mut _HOST_ADDR_LIST).cast::<c_char>(), ptr::null_mut()]
286            };
287            unsafe { HOST_NAME.unsafe_set(Some(host_name)) };
288            unsafe {
289                HOST_ENTRY = hostent {
290                    h_name: HOST_NAME
291                        .unsafe_mut()
292                        .as_mut()
293                        .unwrap()
294                        .as_mut_ptr()
295                        .cast::<c_char>(),
296                    h_aliases: host_aliases.as_mut_slice().as_mut_ptr(),
297                    h_addrtype: format,
298                    h_length: length as i32,
299                    h_addr_list: (&raw mut HOST_ADDR_LIST).cast(),
300                }
301            };
302            &raw mut HOST_ENTRY
303        }
304        // `glibc` sets errno if an address doesn't have a host name
305        // `musl` uses the address as the host name in said case
306        Ok(None) => {
307            H_ERRNO.set(HOST_NOT_FOUND);
308            ptr::null_mut()
309        }
310        Err(e) => {
311            // TODO: Better error separation in lookup_addr
312            H_ERRNO.set(NO_RECOVERY);
313            ptr::null_mut()
314        }
315    }
316}
317
318/// See <https://pubs.opengroup.org/onlinepubs/009696799/functions/gethostbyaddr.html>.
319/// Resolve host information by name or IP address.
320///
321/// # Arguments
322/// * `name` - Host name or IP address.
323///
324/// # Safety
325/// `name` must be a valid string.
326/// This function is not reentrant and may modify static data.
327///
328/// # Panics
329/// Panics if `name` is a null pointer.
330///
331/// # Deprecation
332/// Deprecated as of POSIX.1-2001 and removed in POSIX.1-2008.
333/// New code should use [`getaddrinfo`] instead.
334#[unsafe(no_mangle)]
335#[deprecated]
336pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut hostent {
337    let name_cstr = unsafe {
338        CStr::from_nullable_ptr(name).expect("gethostbyname() called with a NULL pointer")
339    };
340    let Ok(name_str) = str::from_utf8(name_cstr.to_bytes()) else {
341        H_ERRNO.set(NO_RECOVERY);
342        return ptr::null_mut();
343    };
344
345    // Addresses and hostnames are both valid, so we'll check addresses first
346    // The standard doesn't define what to do when called with addresses
347    // Some implementations just skip resolution and copy the address to h_name
348    if let Some(s_addr) = parse_ipv4_string(name_str) {
349        let addr = in_addr { s_addr };
350        return unsafe {
351            #[allow(deprecated)]
352            gethostbyaddr(ptr::from_ref(&addr).cast::<c_void>(), 4, AF_INET)
353        };
354    }
355
356    // check the hosts file first
357    let mut p: *mut hostent;
358    unsafe { sethostent(HOST_STAYOPEN) };
359    while {
360        p = unsafe { gethostent() };
361        !p.is_null()
362    } {
363        if unsafe { strcasecmp((*p).h_name, name) } == 0 {
364            unsafe { sethostent(HOST_STAYOPEN) };
365            return p;
366        }
367        let mut cp = unsafe { (*p).h_aliases };
368        loop {
369            if cp.is_null() {
370                break;
371            }
372            if (unsafe { *cp }).is_null() {
373                break;
374            }
375            if unsafe { strcasecmp(*cp, name) } == 0 {
376                unsafe { sethostent(HOST_STAYOPEN) };
377                return p;
378            }
379            cp = unsafe { cp.offset(1) };
380        }
381    }
382
383    let host = match lookup_host(name_str) {
384        Ok(lookuphost) => lookuphost,
385        Err(e) => {
386            H_ERRNO.set(NO_RECOVERY);
387            return ptr::null_mut();
388        }
389    };
390    let host_addr = match host.into_iter().next() {
391        Some(result) => result,
392        None => {
393            H_ERRNO.set(HOST_NOT_FOUND);
394            return ptr::null_mut();
395        }
396    };
397
398    let host_name: Vec<u8> = name_cstr.to_bytes().to_vec();
399    unsafe { HOST_NAME.unsafe_set(Some(host_name)) };
400    unsafe { _HOST_ADDR_LIST = host_addr.s_addr.to_ne_bytes() };
401    unsafe { HOST_ADDR_LIST = [(&raw mut _HOST_ADDR_LIST).cast::<c_char>(), ptr::null_mut()] };
402    unsafe { HOST_ADDR = Some(host_addr) };
403
404    //TODO actually get aliases
405    let mut _host_aliases: Vec<Vec<u8>> = vec![vec![b'\0']];
406    let mut host_aliases: Vec<*mut c_char> = vec![ptr::null_mut(); 2];
407    unsafe { HOST_ALIASES.unsafe_set(Some(_host_aliases)) };
408
409    unsafe {
410        HOST_ENTRY = hostent {
411            h_name: HOST_NAME
412                .unsafe_mut()
413                .as_mut()
414                .unwrap()
415                .as_mut_ptr()
416                .cast::<c_char>(),
417            h_aliases: host_aliases.as_mut_slice().as_mut_ptr(),
418            h_addrtype: AF_INET,
419            h_length: 4,
420            h_addr_list: (&raw mut HOST_ADDR_LIST).cast(),
421        }
422    };
423    unsafe { sethostent(HOST_STAYOPEN) };
424    (&raw mut HOST_ENTRY).cast::<hostent>()
425}
426
427/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
428pub unsafe extern "C" fn getnetbyaddr(net: u32, net_type: c_int) -> *mut netent {
429    unimplemented!();
430}
431
432/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
433#[unsafe(no_mangle)]
434pub unsafe extern "C" fn getnetbyname(name: *const c_char) -> *mut netent {
435    let mut n: *mut netent;
436    unsafe { setnetent(NET_STAYOPEN) };
437    while {
438        n = unsafe { getnetent() };
439        !n.is_null()
440    } {
441        if unsafe { strcasecmp((*n).n_name, name) } == 0 {
442            unsafe { setnetent(NET_STAYOPEN) };
443            return n;
444        }
445    }
446    unsafe { setnetent(NET_STAYOPEN) };
447
448    platform::ERRNO.set(ENOENT);
449    ptr::null_mut::<netent>()
450}
451
452/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
453#[unsafe(no_mangle)]
454pub unsafe extern "C" fn getnetent() -> *mut netent {
455    // TODO: Rustify implementation
456
457    if unsafe { NETDB } == 0 {
458        unsafe { NETDB = Sys::open(c"/etc/networks".into(), O_RDONLY, 0).or_minus_one_errno() };
459    }
460
461    let mut rlb = RawLineBuffer::new(unsafe { NETDB });
462    rlb.seek(unsafe { N_POS });
463
464    let mut r: Box<str> = Box::default();
465    while r.is_empty() || r.split_whitespace().next().is_none() || r.starts_with('#') {
466        r = match rlb.next() {
467            Line::Some(s) => bytes_to_box_str(s),
468            _ => {
469                if unsafe { NET_STAYOPEN } == 0 {
470                    unsafe { endnetent() };
471                }
472                return ptr::null_mut();
473            }
474        };
475    }
476    rlb.next();
477    unsafe { N_POS = rlb.line_pos() };
478
479    let mut iter: SplitWhitespace = r.split_whitespace();
480
481    let net_name = iter.next().unwrap().bytes().chain(Some(b'\0')).collect();
482    unsafe { NET_NAME.unsafe_set(Some(net_name)) };
483
484    let addr_vec: Vec<u8> = iter.next().unwrap().bytes().chain(Some(b'\0')).collect();
485    let addr_cstr = addr_vec.as_slice().as_ptr().cast::<c_char>();
486    let mut addr = mem::MaybeUninit::uninit();
487    unsafe { inet_aton(addr_cstr, addr.as_mut_ptr()) };
488    let addr = unsafe { addr.assume_init() };
489    unsafe { NET_ADDR = Some(ntohl(addr.s_addr)) };
490
491    let mut _net_aliases: Vec<Vec<u8>> = iter
492        .map(|alias| alias.bytes().chain(Some(b'\0')).collect())
493        .collect();
494    let mut net_aliases: Vec<*mut c_char> = _net_aliases
495        .iter_mut()
496        .map(|x| x.as_mut_ptr().cast::<c_char>())
497        .chain(Some(ptr::null_mut()))
498        .collect();
499    unsafe { NET_ALIASES.unsafe_set(Some(_net_aliases)) };
500
501    unsafe {
502        NET_ENTRY = netent {
503            n_name: NET_NAME
504                .unsafe_mut()
505                .as_mut()
506                .unwrap()
507                .as_mut_ptr()
508                .cast::<c_char>(),
509            n_aliases: net_aliases.as_mut_slice().as_mut_ptr(),
510            n_addrtype: AF_INET,
511            n_net: uint32_t::from(NET_ADDR.unwrap()),
512        }
513    };
514    (&raw mut NET_ENTRY).cast::<netent>()
515}
516
517/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endprotoent.html>.
518#[unsafe(no_mangle)]
519pub unsafe extern "C" fn getprotobyname(name: *const c_char) -> *mut protoent {
520    let mut p: *mut protoent;
521    unsafe { setprotoent(PROTO_STAYOPEN) };
522    while {
523        p = unsafe { getprotoent() };
524        !p.is_null()
525    } {
526        if unsafe { strcasecmp((*p).p_name, name) } == 0 {
527            unsafe { setprotoent(PROTO_STAYOPEN) };
528            return p;
529        }
530
531        let mut cp = unsafe { (*p).p_aliases };
532        loop {
533            if cp.is_null() {
534                unsafe { setprotoent(PROTO_STAYOPEN) };
535                break;
536            }
537            if (unsafe { *cp }).is_null() {
538                unsafe { setprotoent(PROTO_STAYOPEN) };
539                break;
540            }
541            if unsafe { strcasecmp(*cp, name) } == 0 {
542                unsafe { setprotoent(PROTO_STAYOPEN) };
543                return p;
544            }
545            cp = unsafe { cp.offset(1) };
546        }
547    }
548    unsafe { setprotoent(PROTO_STAYOPEN) };
549
550    platform::ERRNO.set(ENOENT);
551    ptr::null_mut::<protoent>()
552}
553
554/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endprotoent.html>.
555#[unsafe(no_mangle)]
556pub unsafe extern "C" fn getprotobynumber(number: c_int) -> *mut protoent {
557    unsafe { setprotoent(PROTO_STAYOPEN) };
558    let mut p: *mut protoent;
559    while {
560        p = unsafe { getprotoent() };
561        !p.is_null()
562    } {
563        if unsafe { (*p).p_proto } == number {
564            unsafe { setprotoent(PROTO_STAYOPEN) };
565            return p;
566        }
567    }
568    unsafe { setprotoent(PROTO_STAYOPEN) };
569    platform::ERRNO.set(ENOENT);
570    ptr::null_mut::<protoent>()
571}
572
573/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endprotoent.html>.
574#[unsafe(no_mangle)]
575pub unsafe extern "C" fn getprotoent() -> *mut protoent {
576    if unsafe { PROTODB } == 0 {
577        unsafe { PROTODB = Sys::open(c"/etc/protocols".into(), O_RDONLY, 0).or_minus_one_errno() };
578    }
579
580    let mut rlb = RawLineBuffer::new(unsafe { PROTODB });
581    rlb.seek(unsafe { P_POS });
582
583    let mut r: Box<str> = Box::default();
584    while r.is_empty() || r.split_whitespace().next().is_none() || r.starts_with('#') {
585        r = match rlb.next() {
586            Line::Some(s) => bytes_to_box_str(s),
587            _ => {
588                if unsafe { PROTO_STAYOPEN } == 0 {
589                    unsafe { endprotoent() };
590                }
591                return ptr::null_mut();
592            }
593        };
594    }
595    rlb.next();
596    unsafe { P_POS = rlb.line_pos() };
597
598    let mut iter: SplitWhitespace = r.split_whitespace();
599
600    let mut proto_name: Vec<u8> = iter.next().unwrap().as_bytes().to_vec();
601    proto_name.push(b'\0');
602
603    let mut num = iter.next().unwrap().as_bytes().to_vec();
604    num.push(b'\0');
605    unsafe { PROTO_NUM = Some(atoi(num.as_mut_slice().as_mut_ptr().cast::<c_char>())) };
606
607    let mut _proto_aliases: Vec<Vec<u8>> = iter
608        .map(|alias| alias.bytes().chain(Some(b'\0')).collect())
609        .collect();
610    let mut proto_aliases: Vec<*mut i8> = _proto_aliases
611        .iter_mut()
612        .map(|x| x.as_mut_ptr().cast::<i8>())
613        .chain(Some(ptr::null_mut()))
614        .collect();
615
616    unsafe { PROTO_ALIASES.unsafe_set(Some(_proto_aliases)) };
617    unsafe { PROTO_NAME.unsafe_set(Some(proto_name)) };
618
619    unsafe {
620        PROTO_ENTRY = protoent {
621            p_name: PROTO_NAME
622                .unsafe_mut()
623                .as_mut()
624                .unwrap()
625                .as_mut_slice()
626                .as_mut_ptr()
627                .cast::<c_char>(),
628            p_aliases: proto_aliases
629                .as_mut_slice()
630                .as_mut_ptr()
631                .cast::<*mut c_char>(),
632            p_proto: PROTO_NUM.unwrap(),
633        }
634    };
635    if unsafe { PROTO_STAYOPEN } == 0 {
636        unsafe { endprotoent() };
637    }
638    (&raw mut PROTO_ENTRY).cast::<protoent>()
639}
640
641/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endservent.html>.
642#[unsafe(no_mangle)]
643pub unsafe extern "C" fn getservbyname(name: *const c_char, proto: *const c_char) -> *mut servent {
644    unsafe { setservent(SERV_STAYOPEN) };
645    let mut p: *mut servent;
646    if proto.is_null() {
647        while {
648            p = unsafe { getservent() };
649            !p.is_null()
650        } {
651            if unsafe { strcasecmp((*p).s_name, name) } == 0 {
652                unsafe { setservent(SERV_STAYOPEN) };
653                return p;
654            }
655        }
656    } else {
657        while {
658            p = unsafe { getservent() };
659            !p.is_null()
660        } {
661            if unsafe { strcasecmp((*p).s_name, name) } == 0
662                && unsafe { strcasecmp((*p).s_proto, proto) } == 0
663            {
664                unsafe { setservent(SERV_STAYOPEN) };
665                return p;
666            }
667        }
668    }
669    unsafe { setservent(SERV_STAYOPEN) };
670    platform::ERRNO.set(ENOENT);
671    ptr::null_mut::<servent>()
672}
673
674/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endservent.html>.
675#[unsafe(no_mangle)]
676pub unsafe extern "C" fn getservbyport(port: c_int, proto: *const c_char) -> *mut servent {
677    unsafe { setservent(SERV_STAYOPEN) };
678    let mut p: *mut servent;
679    if proto.is_null() {
680        while {
681            p = unsafe { getservent() };
682            !p.is_null()
683        } {
684            if unsafe { (*p).s_port } == port {
685                unsafe { setservent(SERV_STAYOPEN) };
686                return p;
687            }
688        }
689    } else {
690        while {
691            p = unsafe { getservent() };
692            !p.is_null()
693        } {
694            if unsafe { (*p).s_port } == port && unsafe { strcasecmp((*p).s_proto, proto) } == 0 {
695                unsafe { setservent(SERV_STAYOPEN) };
696                return p;
697            }
698        }
699    }
700    unsafe { setservent(SERV_STAYOPEN) };
701    platform::ERRNO.set(ENOENT);
702    ptr::null_mut()
703}
704
705/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endservent.html>.
706#[unsafe(no_mangle)]
707pub unsafe extern "C" fn getservent() -> *mut servent {
708    if unsafe { SERVDB } == 0 {
709        // TODO: Rustify
710        unsafe { SERVDB = Sys::open(c"/etc/services".into(), O_RDONLY, 0).or_minus_one_errno() };
711    }
712    let mut rlb = RawLineBuffer::new(unsafe { SERVDB });
713    rlb.seek(unsafe { S_POS });
714
715    let r: Box<str> = Box::default();
716
717    loop {
718        let r = match rlb.next() {
719            Line::Some(s) => bytes_to_box_str(s),
720            _ => {
721                if unsafe { SERV_STAYOPEN } == 0 {
722                    unsafe { endservent() };
723                }
724                return ptr::null_mut();
725            }
726        };
727
728        let mut iter = r.split_whitespace();
729        let serv_name = match iter.next() {
730            Some(serv_name) => serv_name.bytes().chain(Some(b'\0')).collect(),
731            None => continue,
732        };
733        let port_proto = match iter.next() {
734            Some(port_proto) => port_proto,
735            None => continue,
736        };
737        let mut split = port_proto.split('/');
738        let mut port: Vec<u8> = match split.next() {
739            Some(port) => port.bytes().chain(Some(b'\0')).collect(),
740            None => continue,
741        };
742        unsafe {
743            SERV_PORT = Some(u32::from(htons(
744                atoi(port.as_mut_slice().as_mut_ptr().cast::<c_char>()) as u16,
745            )) as i32)
746        };
747        let proto = match split.next() {
748            Some(proto) => proto.bytes().chain(Some(b'\0')).collect(),
749            None => continue,
750        };
751
752        rlb.next();
753        unsafe { S_POS = rlb.line_pos() };
754
755        /*
756         *let mut _serv_aliases: Vec<Vec<u8>> = Vec::new();
757         *loop {
758         *    let mut alias = match iter.next() {
759         *        Some(s) => s.as_bytes().to_vec(),
760         *        _ => break
761         *    };
762         *    alias.push(b'\0');
763         *    _serv_aliases.push(alias);
764         *}
765         *let mut serv_aliases: Vec<*mut i8> = _serv_aliases.iter_mut().map(|x| x.as_mut_ptr() as *mut i8).collect();
766         *serv_aliases.push(ptr::null_mut());
767         *
768         */
769        let mut _serv_aliases: Vec<Vec<u8>> = vec![vec![b'\0']];
770        let mut serv_aliases: Vec<*mut i8> = vec![ptr::null_mut(); 2];
771
772        unsafe { SERV_ALIASES.unsafe_set(Some(_serv_aliases)) };
773        unsafe { SERV_NAME.unsafe_set(Some(serv_name)) };
774        unsafe { SERV_PROTO.unsafe_set(Some(proto)) };
775
776        unsafe {
777            SERV_ENTRY = servent {
778                s_name: SERV_NAME
779                    .unsafe_mut()
780                    .as_mut()
781                    .unwrap()
782                    .as_mut_slice()
783                    .as_mut_ptr()
784                    .cast::<c_char>(),
785                s_aliases: serv_aliases
786                    .as_mut_slice()
787                    .as_mut_ptr()
788                    .cast::<*mut c_char>(),
789                s_port: SERV_PORT.unwrap(),
790                s_proto: SERV_PROTO
791                    .unsafe_mut()
792                    .as_mut()
793                    .unwrap()
794                    .as_mut_slice()
795                    .as_mut_ptr()
796                    .cast::<c_char>(),
797            }
798        };
799
800        if unsafe { SERV_STAYOPEN } == 0 {
801            unsafe { endservent() };
802        }
803        break (&raw mut SERV_ENTRY).cast::<servent>();
804    }
805}
806
807/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
808#[unsafe(no_mangle)]
809pub unsafe extern "C" fn setnetent(stayopen: c_int) {
810    unsafe { NET_STAYOPEN = stayopen };
811    if unsafe { NETDB } == 0 {
812        unsafe { NETDB = Sys::open(c"/etc/networks".into(), O_RDONLY, 0).or_minus_one_errno() }
813    } else {
814        if Sys::lseek(unsafe { NETDB }, 0, SEEK_SET).is_ok() {}; // TODO handle errror
815        unsafe { N_POS = 0 };
816    }
817}
818
819/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endprotoent.html>.
820#[unsafe(no_mangle)]
821pub unsafe extern "C" fn setprotoent(stayopen: c_int) {
822    unsafe { PROTO_STAYOPEN = stayopen };
823    if unsafe { PROTODB } == 0 {
824        unsafe { PROTODB = Sys::open(c"/etc/protocols".into(), O_RDONLY, 0).or_minus_one_errno() }
825    } else {
826        if Sys::lseek(unsafe { PROTODB }, 0, SEEK_SET).is_ok() {}; // TODO handle error
827        unsafe { P_POS = 0 };
828    }
829}
830
831/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endservent.html>.
832#[unsafe(no_mangle)]
833pub unsafe extern "C" fn setservent(stayopen: c_int) {
834    unsafe { SERV_STAYOPEN = stayopen };
835    if unsafe { SERVDB } == 0 {
836        unsafe { SERVDB = Sys::open(c"/etc/services".into(), O_RDONLY, 0).or_minus_one_errno() }
837    } else {
838        if Sys::lseek(unsafe { SERVDB }, 0, SEEK_SET).is_ok() {}; // TODO handle error
839        unsafe { S_POS = 0 };
840    }
841}
842
843/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/freeaddrinfo.html>.
844#[unsafe(no_mangle)]
845pub unsafe extern "C" fn getaddrinfo(
846    node: *const c_char,
847    service: *const c_char,
848    hints: *const addrinfo,
849    res: *mut *mut addrinfo,
850) -> c_int {
851    let node_opt = unsafe { CStr::from_nullable_ptr(node) };
852    let service_opt = unsafe { CStr::from_nullable_ptr(service) };
853
854    let hints_opt = if hints.is_null() {
855        None
856    } else {
857        Some(unsafe { &*hints })
858    };
859
860    log::trace!(
861        "getaddrinfo({:?}, {:?}, {:?})",
862        node_opt.map(|c| unsafe { str::from_utf8_unchecked(c.to_bytes()) }),
863        service_opt.map(|c| unsafe { str::from_utf8_unchecked(c.to_bytes()) }),
864        hints_opt
865    );
866
867    //TODO: Use hints
868    let mut ai_flags = hints_opt.map_or(0, |hints| hints.ai_flags);
869    let mut ai_family; // = hints_opt.map_or(AF_UNSPEC, |hints| hints.ai_family);
870    let ai_socktype = hints_opt.map_or(0, |hints| hints.ai_socktype);
871    let mut ai_protocol; // = hints_opt.map_or(0, |hints| hints.ai_protocol);
872
873    unsafe { *res = ptr::null_mut() };
874
875    let mut port = 0;
876    if let Some(service) = service_opt {
877        //TODO: Support other service definitions as well as AI_NUMERICSERV
878        match unsafe { str::from_utf8_unchecked(service.to_bytes()) }.parse::<u16>() {
879            Ok(ok) => port = ok,
880            Err(_err) => (),
881        }
882    }
883    let node = node_opt.unwrap_or_else(|| {
884        //TODO: Optimize by bypassing string parsing
885        if ai_flags & AI_PASSIVE > 0 {
886            c"0.0.0.0".into()
887        } else {
888            c"127.0.0.1".into()
889        }
890    });
891
892    let lookuphost = if ai_flags & AI_NUMERICHOST > 0 {
893        match parse_ipv4_string(unsafe { str::from_utf8_unchecked(node.to_bytes()) }) {
894            Some(s_addr) => vec![in_addr { s_addr }],
895            None => {
896                return EAI_NONAME;
897            }
898        }
899    } else {
900        match lookup_host(unsafe { str::from_utf8_unchecked(node.to_bytes()) }) {
901            Ok(lookuphost) => lookuphost,
902            Err(e) => {
903                platform::ERRNO.set(e);
904                return EAI_SYSTEM;
905            }
906        }
907    };
908
909    for in_addr in lookuphost {
910        ai_family = AF_INET;
911        ai_protocol = 0;
912
913        let ai_addr = Box::into_raw(Box::new(sockaddr_in {
914            sin_family: ai_family as sa_family_t,
915            sin_port: htons(port),
916            sin_addr: in_addr,
917            sin_zero: [0; 8],
918        }))
919        .cast::<sockaddr>();
920
921        let ai_addrlen = mem::size_of::<sockaddr_in>() as socklen_t;
922
923        let ai_canonname = if ai_flags & AI_CANONNAME > 0 {
924            if node_opt.is_none() {
925                return EAI_BADFLAGS;
926            }
927            ai_flags &= !AI_CANONNAME;
928            node.to_owned_cstring().into_raw()
929        } else {
930            ptr::null_mut()
931        };
932
933        let addrinfo = Box::new(addrinfo {
934            ai_flags: 0,
935            ai_family,
936            ai_socktype,
937            ai_protocol,
938            ai_addrlen,
939            ai_canonname,
940            ai_addr,
941            ai_next: ptr::null_mut(),
942        });
943        unsafe {
944            let mut indirect = res;
945            while !(*indirect).is_null() {
946                indirect = &raw mut (**indirect).ai_next;
947            }
948            *indirect = Box::into_raw(addrinfo)
949        }
950    }
951
952    0
953}
954
955/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getnameinfo.html>.
956#[unsafe(no_mangle)]
957pub unsafe extern "C" fn getnameinfo(
958    addr: *const sockaddr,
959    addrlen: socklen_t,
960    host: *mut c_char,
961    hostlen: socklen_t,
962    serv: *mut c_char,
963    servlen: socklen_t,
964    flags: c_int,
965) -> c_int {
966    if addr.is_null() || addrlen as usize != mem::size_of::<sockaddr_in>() {
967        return EAI_FAMILY;
968    }
969
970    let sa = unsafe { &*(addr.cast::<sockaddr_in>()) };
971
972    if !serv.is_null() && servlen > 0 {
973        if flags & NI_NUMERICSERV != 0 {
974            let port_str = sa.sin_port.to_be().to_string();
975            let port_bytes = port_str.as_bytes();
976            if (servlen as usize) <= port_bytes.len() {
977                return EAI_MEMORY; // Buffer too small
978            }
979            unsafe {
980                ptr::copy_nonoverlapping(
981                    port_bytes.as_ptr().cast::<c_char>(),
982                    serv,
983                    port_bytes.len(),
984                )
985            };
986            unsafe { *serv.add(port_bytes.len()) = 0 };
987        } else {
988            // TODO: Implement service name lookup (e.g., from /etc/services)
989            unsafe { *serv = 0 };
990        }
991    }
992
993    if !host.is_null() && hostlen > 0 {
994        if flags & NI_NUMERICHOST != 0 {
995            let ip_addr = Ipv4Addr::from(sa.sin_addr.s_addr.to_be());
996            let ip_str = ip_addr.to_string();
997            let ip_bytes = ip_str.as_bytes();
998            if (hostlen as usize) <= ip_bytes.len() {
999                return EAI_MEMORY; // Buffer too small
1000            }
1001            unsafe {
1002                ptr::copy_nonoverlapping(ip_bytes.as_ptr().cast::<c_char>(), host, ip_bytes.len())
1003            };
1004            unsafe { *host.add(ip_bytes.len()) = 0 };
1005        } else {
1006            match lookup_addr(sa.sin_addr.clone()).map(|host_names| host_names.into_iter().next()) {
1007                Ok(Some(hostname)) => {
1008                    if (hostlen as usize) <= hostname.len() {
1009                        return EAI_MEMORY; // Buffer too small
1010                    }
1011                    unsafe {
1012                        ptr::copy_nonoverlapping(
1013                            hostname.as_ptr().cast::<c_char>(),
1014                            host,
1015                            hostname.len(),
1016                        );
1017                        *host.add(hostname.len()) = 0;
1018                    }
1019                }
1020                Ok(None) => {
1021                    if flags & NI_NAMEREQD != 0 {
1022                        return EAI_NONAME;
1023                    }
1024                }
1025                Err(_) => {
1026                    if flags & NI_NAMEREQD != 0 {
1027                        return EAI_NONAME;
1028                    }
1029                    let ip_addr = Ipv4Addr::from(sa.sin_addr.s_addr.to_be());
1030                    let ip_str = ip_addr.to_string();
1031                    let ip_bytes = ip_str.as_bytes();
1032                    if (hostlen as usize) <= ip_bytes.len() {
1033                        return EAI_MEMORY;
1034                    }
1035                    unsafe {
1036                        ptr::copy_nonoverlapping(
1037                            ip_bytes.as_ptr().cast::<c_char>(),
1038                            host,
1039                            ip_bytes.len(),
1040                        );
1041                        *host.add(ip_bytes.len()) = 0;
1042                    }
1043                }
1044            }
1045        }
1046    }
1047
1048    0
1049}
1050
1051/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/freeaddrinfo.html>.
1052#[unsafe(no_mangle)]
1053pub unsafe extern "C" fn freeaddrinfo(res: *mut addrinfo) {
1054    let mut ai = res;
1055    while !ai.is_null() {
1056        let bai = unsafe { Box::from_raw(ai) };
1057        if !bai.ai_canonname.is_null() {
1058            drop(unsafe { CString::from_raw(bai.ai_canonname) });
1059        }
1060        if !bai.ai_addr.is_null() {
1061            if bai.ai_addrlen == mem::size_of::<sockaddr_in>() as socklen_t {
1062                unsafe { drop(Box::from_raw(bai.ai_addr.cast::<sockaddr_in>())) };
1063            } else if bai.ai_addrlen == mem::size_of::<sockaddr_in6>() as socklen_t {
1064                unsafe { drop(Box::from_raw(bai.ai_addr.cast::<sockaddr_in6>())) };
1065            } else {
1066                todo_skip!(0, "freeaddrinfo: unknown ai_addrlen {}", bai.ai_addrlen);
1067            }
1068        }
1069        ai = bai.ai_next;
1070    }
1071}
1072
1073/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/gai_strerror.html>.
1074#[unsafe(no_mangle)]
1075pub const extern "C" fn gai_strerror(errcode: c_int) -> *const c_char {
1076    match errcode {
1077        EAI_BADFLAGS => c"Invalid flags",
1078        EAI_NONAME => c"Name does not resolve",
1079        EAI_AGAIN => c"Try again",
1080        EAI_FAIL => c"Non-recoverable error",
1081        EAI_NODATA => c"Unknown error",
1082        EAI_FAMILY => c"Unrecognized address family or invalid length",
1083        EAI_SOCKTYPE => c"Unrecognized socket type",
1084        EAI_SERVICE => c"Unrecognized service",
1085        EAI_ADDRFAMILY => c"Address family for name not supported",
1086        EAI_MEMORY => c"Out of memory",
1087        EAI_SYSTEM => c"System error",
1088        EAI_OVERFLOW => c"Overflow",
1089        _ => c"Unknown error",
1090    }
1091    .as_ptr()
1092}
1093
1094/// Provide a pointer to relibc's internal [`H_ERRNO`].
1095#[unsafe(no_mangle)]
1096#[deprecated]
1097pub extern "C" fn __h_errno_location() -> *mut c_int {
1098    H_ERRNO.as_ptr()
1099}
1100
1101#[unsafe(no_mangle)]
1102#[deprecated]
1103pub const extern "C" fn hstrerror(errcode: c_int) -> *const c_char {
1104    match errcode {
1105        H_UNSET => c"Resolver error unset",
1106        HOST_NOT_FOUND => c"Unknown hostname",
1107        NO_DATA => c"No address for hostname",
1108        NO_RECOVERY => c"Unknown server error",
1109        TRY_AGAIN => c"Hostname lookup failure",
1110        _ => c"Unknown error",
1111    }
1112    .as_ptr()
1113}
1114
1115/// Print error message associated with [`H_ERRNO`] to stderr.
1116///
1117/// # Arguments
1118/// * `prefix` - An optional prefix to prepend to the error message. May be null or an empty
1119///   (`""`) C string.
1120///
1121/// # Safety
1122/// Like [`crate::header::stdio::perror`], `prefix` should be a valid, NUL terminated C string if
1123/// used. The caller may safely call this function with a null pointer.
1124///
1125/// # Deprecation
1126/// [`H_ERRNO`], [`hstrerror`], [`herror`], and other functions are deprecated as of
1127/// POSIX.1-2001 and removed as of POSIX.1-2008. These functions are provided for backwards
1128/// compatibility but should not be used by new code.
1129#[allow(clippy::not_unsafe_ptr_arg_deref)]
1130#[unsafe(no_mangle)]
1131#[deprecated]
1132pub extern "C" fn herror(prefix: *const c_char) {
1133    let code = H_ERRNO.get();
1134    // Safety: `hstrerror` handles every error code case and always returns a valid C string
1135    let error = unsafe {
1136        #[allow(deprecated)]
1137        let msg_cstr = CStr::from_ptr(hstrerror(code));
1138        str::from_utf8_unchecked(msg_cstr.to_bytes())
1139    };
1140
1141    let mut writer = platform::FileWriter::new(2);
1142    // Prefix is optional
1143    match unsafe { CStr::from_nullable_ptr(prefix) }
1144        .and_then(|prefix| str::from_utf8(prefix.to_bytes()).ok())
1145    {
1146        Some(prefix) if !prefix.is_empty() => writer
1147            .write_fmt(format_args!("{prefix}: {error}\n"))
1148            .unwrap(),
1149        _ => writer.write_fmt(format_args!("{error}\n")).unwrap(),
1150    }
1151}