Skip to main content

c/
netdb_extra.rs

1// Copyright (c) 2026 Meng Zhuo
2// SPDX-License-Identifier: BSD-3-Clause
3
4// Network database backends: /etc/services, /etc/protocols, /etc/networks,
5// /etc/ethers and /etc/hosts, plus getaddrinfo/getnameinfo.
6
7#[repr(C)]
8pub struct ServEnt {
9    pub s_name: *mut c_char,
10    pub s_aliases: *mut *mut c_char,
11    pub s_port: c_int,
12    pub s_proto: *mut c_char,
13}
14
15#[repr(C)]
16pub struct ProtoEnt {
17    pub p_name: *mut c_char,
18    pub p_aliases: *mut *mut c_char,
19    pub p_proto: c_int,
20}
21
22#[repr(C)]
23pub struct NetEnt {
24    pub n_name: *mut c_char,
25    pub n_aliases: *mut *mut c_char,
26    pub n_addrtype: c_int,
27    pub n_net: u32,
28}
29
30const NDB_ENOENT: c_int = 2;
31const NDB_ERANGE: c_int = 34;
32const NDB_ENOTTY: c_int = 25;
33
34const EAI_BADFLAGS_V: c_int = -1;
35const EAI_NONAME_V: c_int = -2;
36const EAI_AGAIN_V: c_int = -3;
37const EAI_FAIL_V: c_int = -4;
38const EAI_FAMILY_V: c_int = -6;
39const EAI_SOCKTYPE_V: c_int = -7;
40const EAI_SERVICE_V: c_int = -8;
41const EAI_MEMORY_V: c_int = -10;
42const EAI_SYSTEM_V: c_int = -11;
43const EAI_OVERFLOW_V: c_int = -12;
44
45const AI_PASSIVE_V: c_int = 0x01;
46const AI_CANONNAME_V: c_int = 0x02;
47const AI_NUMERICHOST_V: c_int = 0x04;
48const AI_V4MAPPED_V: c_int = 0x08;
49const AI_ALL_V: c_int = 0x10;
50const AI_ADDRCONFIG_V: c_int = 0x20;
51const AI_NUMERICSERV_V: c_int = 0x400;
52
53const NI_NUMERICHOST_V: c_int = 0x01;
54const NI_NUMERICSERV_V: c_int = 0x02;
55const NI_NOFQDN_V: c_int = 0x04;
56const NI_NAMEREQD_V: c_int = 0x08;
57const NI_DGRAM_V: c_int = 0x10;
58const NI_NUMERICSCOPE_V: c_int = 0x100;
59
60unsafe fn nd_isspace(c: c_char) -> bool {
61    c == b' ' as c_char || c == b'\t' as c_char || c == b'\r' as c_char || c == b'\n' as c_char
62}
63
64unsafe fn nd_copy_line(src: *const c_char, dst: *mut c_char, cap: usize) -> bool {
65    let mut i = 0usize;
66    while i + 1 < cap {
67        let c = *src.add(i);
68        *dst.add(i) = c;
69        if c == 0 { return true; }
70        i += 1;
71    }
72    *dst.add(cap - 1) = 0;
73    true
74}
75
76/// Split a mutable line into whitespace-separated tokens, honouring '#'.
77unsafe fn nd_split(buf: *mut c_char, toks: *mut *mut c_char, maxtok: usize) -> usize {
78    let mut n = 0usize;
79    let mut p = buf;
80    loop {
81        while *p != 0 && nd_isspace(*p) { p = p.add(1); }
82        if *p == 0 || *p == b'#' as c_char { break; }
83        if n < maxtok {
84            *toks.add(n) = p;
85            n += 1;
86        }
87        while *p != 0 && !nd_isspace(*p) && *p != b'#' as c_char { p = p.add(1); }
88        if *p == 0 { break; }
89        if *p == b'#' as c_char { *p = 0; break; }
90        *p = 0;
91        p = p.add(1);
92    }
93    n
94}
95
96unsafe fn nd_tokens(line: *const c_char, buf: *mut c_char, cap: usize, toks: &mut [*mut c_char; 24]) -> usize {
97    if !nd_copy_line(line, buf, cap) { return 0; }
98    nd_split(buf, toks.as_mut_ptr(), toks.len())
99}
100
101// ------------------------------------------------------------------
102// /etc/services
103// ------------------------------------------------------------------
104
105static mut SERV_FILE: *mut FILE = core::ptr::null_mut();
106static mut SERV_ENT: ServEnt = ServEnt {
107    s_name: core::ptr::null_mut(),
108    s_aliases: core::ptr::null_mut(),
109    s_port: 0,
110    s_proto: core::ptr::null_mut(),
111};
112static mut SERV_BUF: [c_char; 1024] = [0; 1024];
113static mut SERV_ALIASES: [*mut c_char; 16] = [core::ptr::null_mut(); 16];
114
115unsafe fn serv_parse(line: *const c_char) -> bool {
116    let mut toks: [*mut c_char; 24] = [core::ptr::null_mut(); 24];
117    let n = nd_tokens(line, SERV_BUF.as_mut_ptr(), SERV_BUF.len(), &mut toks);
118    if n < 2 { return false; }
119    let mut p = toks[1];
120    let port = crate::strtoul(p, &mut p, 10);
121    if p == toks[1] || port > 65535 { return false; }
122    let mut proto: *mut c_char = b"tcp\0".as_ptr() as *mut c_char;
123    if *p == b'/' as c_char {
124        p = p.add(1);
125        if *p == 0 { return false; }
126        proto = p;
127    }
128    SERV_ENT.s_name = toks[0];
129    SERV_ENT.s_port = htons(port as u16) as c_int;
130    SERV_ENT.s_proto = proto;
131    let mut ai = 0usize;
132    for i in 2..n {
133        if ai + 1 >= SERV_ALIASES.len() { break; }
134        SERV_ALIASES[ai] = toks[i];
135        ai += 1;
136    }
137    SERV_ALIASES[ai] = core::ptr::null_mut();
138    SERV_ENT.s_aliases = SERV_ALIASES.as_mut_ptr();
139    true
140}
141
142unsafe fn serv_next(f: *mut FILE) -> bool {
143    let mut line = [0 as c_char; 1024];
144    while !crate::fgets(line.as_mut_ptr(), line.len() as c_int, f).is_null() {
145        if serv_parse(line.as_ptr()) { return true; }
146    }
147    false
148}
149
150#[no_mangle]
151pub unsafe extern "C" fn setservent(stayopen: c_int) {
152    endservent();
153    SERV_FILE = crate::fopen(b"/etc/services\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
154    let _ = stayopen;
155}
156
157#[no_mangle]
158pub unsafe extern "C" fn endservent() {
159    if !SERV_FILE.is_null() {
160        crate::fclose(SERV_FILE);
161        SERV_FILE = core::ptr::null_mut();
162    }
163}
164
165#[no_mangle]
166pub unsafe extern "C" fn getservent() -> *mut ServEnt {
167    if SERV_FILE.is_null() {
168        SERV_FILE = crate::fopen(b"/etc/services\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
169        if SERV_FILE.is_null() { return core::ptr::null_mut(); }
170    }
171    if serv_next(SERV_FILE) { &raw mut SERV_ENT } else { core::ptr::null_mut() }
172}
173
174unsafe fn serv_name_matches(name: *const c_char) -> bool {
175    if crate::strcmp(SERV_ENT.s_name as *const u8, name as *const u8) == 0 { return true; }
176    let mut i = 0usize;
177    while !(*SERV_ENT.s_aliases.add(i)).is_null() {
178        if crate::strcmp(*SERV_ENT.s_aliases.add(i) as *const u8, name as *const u8) == 0 {
179            return true;
180        }
181        i += 1;
182    }
183    false
184}
185
186unsafe fn serv_proto_matches(proto: *const c_char) -> bool {
187    proto.is_null() || crate::strcmp(SERV_ENT.s_proto as *const u8, proto as *const u8) == 0
188}
189
190unsafe fn serv_search(name: *const c_char, proto: *const c_char, port: c_int) -> bool {
191    let f = crate::fopen(b"/etc/services\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
192    if f.is_null() { return false; }
193    let mut found = false;
194    while serv_next(f) {
195        if !serv_proto_matches(proto) { continue; }
196        let hit = if name.is_null() {
197            SERV_ENT.s_port == port
198        } else {
199            serv_name_matches(name)
200        };
201        if hit { found = true; break; }
202    }
203    crate::fclose(f);
204    found
205}
206
207#[no_mangle]
208pub unsafe extern "C" fn getservbyname(name: *const c_char, proto: *const c_char) -> *mut ServEnt {
209    if name.is_null() { return core::ptr::null_mut(); }
210    if serv_search(name, proto, 0) { &raw mut SERV_ENT } else { core::ptr::null_mut() }
211}
212
213#[no_mangle]
214pub unsafe extern "C" fn getservbyport(port: c_int, proto: *const c_char) -> *mut ServEnt {
215    if serv_search(core::ptr::null(), proto, port) { &raw mut SERV_ENT } else { core::ptr::null_mut() }
216}
217
218unsafe fn serv_copy_out(se: *mut ServEnt, buf: *mut c_char, buflen: usize, res: *mut *mut ServEnt) -> c_int {
219    if res.is_null() || se.is_null() { return EINVAL; }
220    *res = core::ptr::null_mut();
221    let ptrsz = core::mem::size_of::<*mut c_char>();
222    let mut need = crate::strlen((*se).s_name) + 1 + crate::strlen((*se).s_proto) + 1;
223    let mut nalias = 0usize;
224    while !(*(*se).s_aliases.add(nalias)).is_null() {
225        need += crate::strlen(*(*se).s_aliases.add(nalias)) + 1;
226        nalias += 1;
227    }
228    let align = (ptrsz - (buf as usize & (ptrsz - 1))) & (ptrsz - 1);
229    need += align + (nalias + 1) * ptrsz;
230    if need > buflen { return NDB_ERANGE; }
231
232    let mut p = buf.add(align);
233    let aliases = p as *mut *mut c_char;
234    p = p.add((nalias + 1) * ptrsz);
235
236    let n = crate::strlen((*se).s_name);
237    core::ptr::copy_nonoverlapping((*se).s_name, p, n + 1);
238    (*se).s_name = p;
239    p = p.add(n + 1);
240
241    let n = crate::strlen((*se).s_proto);
242    core::ptr::copy_nonoverlapping((*se).s_proto, p, n + 1);
243    (*se).s_proto = p;
244    p = p.add(n + 1);
245
246    for j in 0..nalias {
247        let src = *(*se).s_aliases.add(j);
248        let n = crate::strlen(src);
249        core::ptr::copy_nonoverlapping(src, p, n + 1);
250        *aliases.add(j) = p;
251        p = p.add(n + 1);
252    }
253    *aliases.add(nalias) = core::ptr::null_mut();
254    (*se).s_aliases = aliases;
255    *res = se;
256    0
257}
258
259#[no_mangle]
260pub unsafe extern "C" fn getservbyname_r(
261    name: *const c_char,
262    proto: *const c_char,
263    _se: *mut ServEnt,
264    buf: *mut c_char,
265    buflen: usize,
266    res: *mut *mut ServEnt,
267) -> c_int {
268    if res.is_null() { return EINVAL; }
269    *res = core::ptr::null_mut();
270    let p = getservbyname(name, proto);
271    if p.is_null() { return NDB_ENOENT; }
272    let mut tmp = ServEnt { s_name: (*p).s_name, s_aliases: (*p).s_aliases, s_port: (*p).s_port, s_proto: (*p).s_proto };
273    serv_copy_out(&mut tmp, buf, buflen, res)
274}
275
276#[no_mangle]
277pub unsafe extern "C" fn getservbyport_r(
278    port: c_int,
279    proto: *const c_char,
280    _se: *mut ServEnt,
281    buf: *mut c_char,
282    buflen: usize,
283    res: *mut *mut ServEnt,
284) -> c_int {
285    if res.is_null() { return EINVAL; }
286    *res = core::ptr::null_mut();
287    let p = getservbyport(port, proto);
288    if p.is_null() { return NDB_ENOENT; }
289    let mut tmp = ServEnt { s_name: (*p).s_name, s_aliases: (*p).s_aliases, s_port: (*p).s_port, s_proto: (*p).s_proto };
290    serv_copy_out(&mut tmp, buf, buflen, res)
291}
292
293// ------------------------------------------------------------------
294// /etc/protocols (with a builtin fallback table)
295// ------------------------------------------------------------------
296
297static PROTO_TABLE: [(&[u8], c_int); 36] = [
298    (b"ip\0", 0), (b"icmp\0", 1), (b"igmp\0", 2), (b"ggp\0", 3),
299    (b"ipencap\0", 4), (b"st\0", 5), (b"tcp\0", 6), (b"egp\0", 8),
300    (b"pup\0", 12), (b"udp\0", 17), (b"hmp\0", 20), (b"xns-idp\0", 22),
301    (b"rdp\0", 27), (b"iso-tp4\0", 29), (b"xtp\0", 36), (b"ddp\0", 37),
302    (b"idpr-cmtp\0", 38), (b"ipv6\0", 41), (b"ipv6-route\0", 43),
303    (b"ipv6-frag\0", 44), (b"idrp\0", 45), (b"rsvp\0", 46), (b"gre\0", 47),
304    (b"esp\0", 50), (b"ah\0", 51), (b"skip\0", 57), (b"ipv6-icmp\0", 58),
305    (b"ipv6-nonxt\0", 59), (b"ipv6-opts\0", 60), (b"rspf\0", 73),
306    (b"vmtp\0", 81), (b"ospf\0", 89), (b"ipip\0", 94), (b"encap\0", 98),
307    (b"pim\0", 103), (b"raw\0", 255),
308];
309
310static mut PROTO_FILE: *mut FILE = core::ptr::null_mut();
311static mut PROTO_ENT: ProtoEnt = ProtoEnt {
312    p_name: core::ptr::null_mut(),
313    p_aliases: core::ptr::null_mut(),
314    p_proto: 0,
315};
316static mut PROTO_BUF: [c_char; 1024] = [0; 1024];
317static mut PROTO_ALIASES: [*mut c_char; 16] = [core::ptr::null_mut(); 16];
318static mut PROTO_IDX: usize = 0;
319
320unsafe fn proto_parse(line: *const c_char) -> bool {
321    let mut toks: [*mut c_char; 24] = [core::ptr::null_mut(); 24];
322    let n = nd_tokens(line, PROTO_BUF.as_mut_ptr(), PROTO_BUF.len(), &mut toks);
323    if n < 2 { return false; }
324    let num = crate::strtol(toks[1], core::ptr::null_mut(), 10);
325    if num < 0 || num > 255 { return false; }
326    PROTO_ENT.p_name = toks[0];
327    PROTO_ENT.p_proto = num as c_int;
328    let mut ai = 0usize;
329    for i in 2..n {
330        if ai + 1 >= PROTO_ALIASES.len() { break; }
331        PROTO_ALIASES[ai] = toks[i];
332        ai += 1;
333    }
334    PROTO_ALIASES[ai] = core::ptr::null_mut();
335    PROTO_ENT.p_aliases = PROTO_ALIASES.as_mut_ptr();
336    true
337}
338
339unsafe fn proto_from_table(i: usize) -> bool {
340    if i >= PROTO_TABLE.len() { return false; }
341    PROTO_ENT.p_name = PROTO_TABLE[i].0.as_ptr() as *mut c_char;
342    PROTO_ENT.p_proto = PROTO_TABLE[i].1;
343    PROTO_ALIASES[0] = core::ptr::null_mut();
344    PROTO_ENT.p_aliases = PROTO_ALIASES.as_mut_ptr();
345    true
346}
347
348unsafe fn proto_file() -> *mut FILE {
349    if PROTO_FILE.is_null() {
350        PROTO_FILE = crate::fopen(b"/etc/protocols\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
351    }
352    PROTO_FILE
353}
354
355#[no_mangle]
356pub unsafe extern "C" fn setprotoent(stayopen: c_int) {
357    endprotoent();
358    PROTO_IDX = 0;
359    PROTO_FILE = crate::fopen(b"/etc/protocols\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
360    let _ = stayopen;
361}
362
363#[no_mangle]
364pub unsafe extern "C" fn endprotoent() {
365    if !PROTO_FILE.is_null() {
366        crate::fclose(PROTO_FILE);
367        PROTO_FILE = core::ptr::null_mut();
368    }
369    PROTO_IDX = 0;
370}
371
372#[no_mangle]
373pub unsafe extern "C" fn getprotoent() -> *mut ProtoEnt {
374    if PROTO_FILE.is_null() {
375        let f = crate::fopen(b"/etc/protocols\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
376        if f.is_null() {
377            let ok = proto_from_table(PROTO_IDX);
378            PROTO_IDX += 1;
379            return if ok { &raw mut PROTO_ENT } else { core::ptr::null_mut() };
380        }
381        PROTO_FILE = f;
382    }
383    let mut line = [0 as c_char; 1024];
384    while !crate::fgets(line.as_mut_ptr(), line.len() as c_int, PROTO_FILE).is_null() {
385        if proto_parse(line.as_ptr()) { return &raw mut PROTO_ENT; }
386    }
387    core::ptr::null_mut()
388}
389
390unsafe fn proto_name_matches(name: *const c_char) -> bool {
391    if crate::strcmp(PROTO_ENT.p_name as *const u8, name as *const u8) == 0 { return true; }
392    let mut i = 0usize;
393    while !(*PROTO_ENT.p_aliases.add(i)).is_null() {
394        if crate::strcmp(*PROTO_ENT.p_aliases.add(i) as *const u8, name as *const u8) == 0 { return true; }
395        i += 1;
396    }
397    false
398}
399
400unsafe fn proto_scan(name: *const c_char, num: c_int, by_name: bool) -> bool {
401    let f = crate::fopen(b"/etc/protocols\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
402    if !f.is_null() {
403        let mut line = [0 as c_char; 1024];
404        while !crate::fgets(line.as_mut_ptr(), line.len() as c_int, f).is_null() {
405            if !proto_parse(line.as_ptr()) { continue; }
406            let hit = if by_name { proto_name_matches(name) } else { PROTO_ENT.p_proto == num };
407            if hit { crate::fclose(f); return true; }
408        }
409        crate::fclose(f);
410    }
411    for i in 0..PROTO_TABLE.len() {
412        proto_from_table(i);
413        let hit = if by_name { proto_name_matches(name) } else { PROTO_ENT.p_proto == num };
414        if hit { return true; }
415    }
416    false
417}
418
419#[no_mangle]
420pub unsafe extern "C" fn getprotobyname(name: *const c_char) -> *mut ProtoEnt {
421    if name.is_null() { return core::ptr::null_mut(); }
422    if proto_scan(name, 0, true) { &raw mut PROTO_ENT } else { core::ptr::null_mut() }
423}
424
425#[no_mangle]
426pub unsafe extern "C" fn getprotobynumber(num: c_int) -> *mut ProtoEnt {
427    if proto_scan(core::ptr::null(), num, false) { &raw mut PROTO_ENT } else { core::ptr::null_mut() }
428}
429
430// ------------------------------------------------------------------
431// /etc/networks
432// ------------------------------------------------------------------
433
434static mut NET_FILE: *mut FILE = core::ptr::null_mut();
435static mut NET_ENT: NetEnt = NetEnt {
436    n_name: core::ptr::null_mut(),
437    n_aliases: core::ptr::null_mut(),
438    n_addrtype: AF_INET,
439    n_net: 0,
440};
441static mut NET_BUF: [c_char; 1024] = [0; 1024];
442static mut NET_ALIASES: [*mut c_char; 16] = [core::ptr::null_mut(); 16];
443
444unsafe fn net_parse(line: *const c_char) -> bool {
445    let mut toks: [*mut c_char; 24] = [core::ptr::null_mut(); 24];
446    let n = nd_tokens(line, NET_BUF.as_mut_ptr(), NET_BUF.len(), &mut toks);
447    if n < 2 { return false; }
448    let net = inet_network(toks[1]);
449    if net == u32::MAX { return false; }
450    NET_ENT.n_name = toks[0];
451    NET_ENT.n_addrtype = AF_INET;
452    NET_ENT.n_net = net;
453    let mut ai = 0usize;
454    for i in 2..n {
455        if ai + 1 >= NET_ALIASES.len() { break; }
456        NET_ALIASES[ai] = toks[i];
457        ai += 1;
458    }
459    NET_ALIASES[ai] = core::ptr::null_mut();
460    NET_ENT.n_aliases = NET_ALIASES.as_mut_ptr();
461    true
462}
463
464unsafe fn net_next(f: *mut FILE) -> bool {
465    let mut line = [0 as c_char; 1024];
466    while !crate::fgets(line.as_mut_ptr(), line.len() as c_int, f).is_null() {
467        if net_parse(line.as_ptr()) { return true; }
468    }
469    false
470}
471
472#[no_mangle]
473pub unsafe extern "C" fn setnetent(stayopen: c_int) {
474    endnetent();
475    NET_FILE = crate::fopen(b"/etc/networks\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
476    let _ = stayopen;
477}
478
479#[no_mangle]
480pub unsafe extern "C" fn endnetent() {
481    if !NET_FILE.is_null() {
482        crate::fclose(NET_FILE);
483        NET_FILE = core::ptr::null_mut();
484    }
485}
486
487#[no_mangle]
488pub unsafe extern "C" fn getnetent() -> *mut NetEnt {
489    if NET_FILE.is_null() {
490        NET_FILE = crate::fopen(b"/etc/networks\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
491        if NET_FILE.is_null() { return core::ptr::null_mut(); }
492    }
493    if net_next(NET_FILE) { &raw mut NET_ENT } else { core::ptr::null_mut() }
494}
495
496unsafe fn net_name_matches(name: *const c_char) -> bool {
497    if crate::strcmp(NET_ENT.n_name as *const u8, name as *const u8) == 0 { return true; }
498    let mut i = 0usize;
499    while !(*NET_ENT.n_aliases.add(i)).is_null() {
500        if crate::strcmp(*NET_ENT.n_aliases.add(i) as *const u8, name as *const u8) == 0 { return true; }
501        i += 1;
502    }
503    false
504}
505
506#[no_mangle]
507pub unsafe extern "C" fn getnetbyname(name: *const c_char) -> *mut NetEnt {
508    if name.is_null() { return core::ptr::null_mut(); }
509    let f = crate::fopen(b"/etc/networks\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
510    if !f.is_null() {
511        while net_next(f) {
512            if net_name_matches(name) { crate::fclose(f); return &raw mut NET_ENT; }
513        }
514        crate::fclose(f);
515    }
516    // "loopback" / "default" are always available even without the file.
517    if crate::strcmp(name as *const u8, b"loopback\0".as_ptr()) == 0 {
518        net_parse(b"loopback 127.0.0.0\0".as_ptr() as *const c_char);
519        return &raw mut NET_ENT;
520    }
521    if crate::strcmp(name as *const u8, b"default\0".as_ptr()) == 0 {
522        net_parse(b"default 0.0.0.0\0".as_ptr() as *const c_char);
523        return &raw mut NET_ENT;
524    }
525    core::ptr::null_mut()
526}
527
528#[no_mangle]
529pub unsafe extern "C" fn getnetbyaddr(net: u32, type_: c_int) -> *mut NetEnt {
530    if type_ != AF_INET { return core::ptr::null_mut(); }
531    let f = crate::fopen(b"/etc/networks\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
532    if !f.is_null() {
533        while net_next(f) {
534            let shift = if NET_ENT.n_net < 0x80000000 { 24 }
535                else if NET_ENT.n_net < 0xc0000000 { 16 } else { 8 };
536            if (NET_ENT.n_net >> shift) == (net >> shift) { crate::fclose(f); return &raw mut NET_ENT; }
537        }
538        crate::fclose(f);
539    }
540    core::ptr::null_mut()
541}
542
543// ------------------------------------------------------------------
544// /etc/ethers
545// ------------------------------------------------------------------
546
547#[no_mangle]
548pub unsafe extern "C" fn ether_line(line: *const c_char, ea: *mut u8, hostname: *mut c_char) -> c_int {
549    if line.is_null() || ea.is_null() || hostname.is_null() { return -1; }
550    let mut buf = [0 as c_char; 512];
551    let mut toks: [*mut c_char; 24] = [core::ptr::null_mut(); 24];
552    let n = nd_tokens(line, buf.as_mut_ptr(), buf.len(), &mut toks);
553    if n < 2 { return -1; }
554    if ether_aton_r(toks[0], ea).is_null() { return -1; }
555    let mut i = 0usize;
556    while *toks[1].add(i) != 0 {
557        *hostname.add(i) = *toks[1].add(i);
558        i += 1;
559    }
560    *hostname.add(i) = 0;
561    0
562}
563
564unsafe fn ether_scan(want_name: *const c_char, ea: *mut u8, hostname: *mut c_char) -> c_int {
565    let f = crate::fopen(b"/etc/ethers\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
566    if f.is_null() { return -1; }
567    let mut line = [0 as c_char; 512];
568    let mut found = -1;
569    while !crate::fgets(line.as_mut_ptr(), line.len() as c_int, f).is_null() {
570        let mut mac = [0u8; 6];
571        let mut host = [0 as c_char; 256];
572        if ether_line(line.as_ptr(), mac.as_mut_ptr(), host.as_mut_ptr()) != 0 { continue; }
573        if !want_name.is_null() {
574            if crate::strcmp(host.as_ptr() as *const u8, want_name as *const u8) != 0 { continue; }
575            core::ptr::copy_nonoverlapping(mac.as_ptr(), ea, 6);
576            found = 0;
577            break;
578        } else {
579            let mut same = true;
580            for i in 0..6 {
581                if mac[i] != *ea.add(i) { same = false; break; }
582            }
583            if !same { continue; }
584            let mut i = 0usize;
585            while host[i] != 0 {
586                *hostname.add(i) = host[i];
587                i += 1;
588            }
589            *hostname.add(i) = 0;
590            found = 0;
591            break;
592        }
593    }
594    crate::fclose(f);
595    found
596}
597
598#[no_mangle]
599pub unsafe extern "C" fn ether_hostton(hostname: *const c_char, ea: *mut u8) -> c_int {
600    if hostname.is_null() || ea.is_null() { return -1; }
601    ether_scan(hostname, ea, core::ptr::null_mut())
602}
603
604#[no_mangle]
605pub unsafe extern "C" fn ether_ntohost(hostname: *mut c_char, ea: *const u8) -> c_int {
606    if hostname.is_null() || ea.is_null() { return -1; }
607    ether_scan(core::ptr::null(), ea as *mut u8, hostname)
608}
609
610// ------------------------------------------------------------------
611// /etc/hosts iteration and reverse lookups
612// ------------------------------------------------------------------
613
614static mut HOSTS_FILE: *mut FILE = core::ptr::null_mut();
615static mut HOSTS_BUF: [c_char; 1024] = [0; 1024];
616static mut HOSTS_ADDR: [u8; 4] = [0; 4];
617static mut HOSTS_ADDR_PTRS: [*mut c_char; 2] = [core::ptr::null_mut(); 2];
618static mut HOSTS_ALIASES: [*mut c_char; 16] = [core::ptr::null_mut(); 16];
619
620unsafe fn hosts_parse(line: *const c_char) -> bool {
621    let mut toks: [*mut c_char; 24] = [core::ptr::null_mut(); 24];
622    let n = nd_tokens(line, HOSTS_BUF.as_mut_ptr(), HOSTS_BUF.len(), &mut toks);
623    if n < 2 { return false; }
624    if inet_pton(AF_INET, toks[0], HOSTS_ADDR.as_mut_ptr() as *mut c_void) != 1 { return false; }
625    HOST_ENT.h_name = toks[1];
626    HOST_ENT.h_addrtype = AF_INET;
627    HOST_ENT.h_length = 4;
628    HOST_ENT.h_addr_list = HOSTS_ADDR_PTRS.as_mut_ptr();
629    HOSTS_ADDR_PTRS[0] = HOSTS_ADDR.as_mut_ptr() as *mut c_char;
630    HOSTS_ADDR_PTRS[1] = core::ptr::null_mut();
631    let mut ai = 0usize;
632    for i in 2..n {
633        if ai + 1 >= HOSTS_ALIASES.len() { break; }
634        HOSTS_ALIASES[ai] = toks[i];
635        ai += 1;
636    }
637    HOSTS_ALIASES[ai] = core::ptr::null_mut();
638    HOST_ENT.h_aliases = HOSTS_ALIASES.as_mut_ptr();
639    true
640}
641
642#[no_mangle]
643pub unsafe extern "C" fn sethostent(_stayopen: c_int) {
644    endhostent();
645}
646
647#[no_mangle]
648pub unsafe extern "C" fn endhostent() {
649    if !HOSTS_FILE.is_null() {
650        crate::fclose(HOSTS_FILE);
651        HOSTS_FILE = core::ptr::null_mut();
652    }
653}
654
655#[no_mangle]
656pub unsafe extern "C" fn gethostent() -> *mut HostEnt {
657    if HOSTS_FILE.is_null() {
658        HOSTS_FILE = crate::fopen(b"/etc/hosts\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
659        if HOSTS_FILE.is_null() { return core::ptr::null_mut(); }
660    }
661    let mut line = [0 as c_char; 1024];
662    while !crate::fgets(line.as_mut_ptr(), line.len() as c_int, HOSTS_FILE).is_null() {
663        if hosts_parse(line.as_ptr()) { return &raw mut HOST_ENT; }
664    }
665    core::ptr::null_mut()
666}
667
668unsafe fn hosts_lookup_addr(addr: *const u8, family: c_int) -> bool {
669    let f = crate::fopen(b"/etc/hosts\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
670    if f.is_null() { return false; }
671    let mut line = [0 as c_char; 1024];
672    let mut found = false;
673    while !crate::fgets(line.as_mut_ptr(), line.len() as c_int, f).is_null() {
674        let mut toks: [*mut c_char; 24] = [core::ptr::null_mut(); 24];
675        let n = nd_tokens(line.as_ptr(), HOSTS_BUF.as_mut_ptr(), HOSTS_BUF.len(), &mut toks);
676        if n < 2 { continue; }
677        let mut tmp = [0u8; 16];
678        if inet_pton(AF_INET, toks[0], tmp.as_mut_ptr() as *mut c_void) == 1 {
679            if family != AF_INET { continue; }
680        } else if inet_pton(AF_INET6, toks[0], tmp.as_mut_ptr() as *mut c_void) == 1 {
681            if family != AF_INET6 { continue; }
682        } else {
683            continue;
684        }
685        let len = if family == AF_INET6 { 16 } else { 4 };
686        let mut same = true;
687        for i in 0..len {
688            if tmp[i] != *addr.add(i) { same = false; break; }
689        }
690        if !same { continue; }
691        let nl = crate::strlen(toks[1]);
692        core::ptr::copy_nonoverlapping(toks[1], HOST_NAME.as_mut_ptr(), nl + 1);
693        found = true;
694        break;
695    }
696    crate::fclose(f);
697    found
698}
699
700unsafe fn hosts_lookup_name(name: *const c_char) -> bool {
701    let f = crate::fopen(b"/etc/hosts\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
702    if f.is_null() { return false; }
703    let mut line = [0 as c_char; 1024];
704    let mut found = false;
705    while !crate::fgets(line.as_mut_ptr(), line.len() as c_int, f).is_null() {
706        if !hosts_parse(line.as_ptr()) { continue; }
707        let mut hit = crate::strcmp(HOST_ENT.h_name as *const u8, name as *const u8) == 0;
708        if !hit {
709            let mut i = 0usize;
710            while !(*HOST_ENT.h_aliases.add(i)).is_null() {
711                if crate::strcmp(*HOST_ENT.h_aliases.add(i) as *const u8, name as *const u8) == 0 {
712                    hit = true;
713                    break;
714                }
715                i += 1;
716            }
717        }
718        if hit { found = true; break; }
719    }
720    crate::fclose(f);
721    found
722}
723
724// ------------------------------------------------------------------
725// getnameinfo
726// ------------------------------------------------------------------
727
728unsafe fn nd_itoa(mut v: u32, out: *mut c_char) -> usize {
729    let mut digits = [0u8; 10];
730    let mut n = 0usize;
731    if v == 0 { digits[0] = 0; n = 1; }
732    while v > 0 { digits[n] = (v % 10) as u8; v /= 10; n += 1; }
733    let mut o = 0usize;
734    while n > 0 { n -= 1; *out.add(o) = (b'0' + digits[n]) as c_char; o += 1; }
735    *out.add(o) = 0;
736    o
737}
738
739unsafe fn nd_reverse6(addr: *const u8, out: *mut c_char, cap: usize) -> bool {
740    if cap < 74 { return false; }
741    let hex = b"0123456789abcdef";
742    let mut o = 0usize;
743    let mut i = 16isize;
744    while i > 0 {
745        i -= 1;
746        let b = *addr.add(i as usize);
747        *out.add(o) = hex[(b & 0xf) as usize] as c_char; o += 1;
748        *out.add(o) = b'.' as c_char; o += 1;
749        *out.add(o) = hex[(b >> 4) as usize] as c_char; o += 1;
750        *out.add(o) = b'.' as c_char; o += 1;
751    }
752    for &c in b"ip6.arpa\0" { *out.add(o) = c as c_char; o += 1; }
753    true
754}
755
756#[no_mangle]
757pub unsafe extern "C" fn getnameinfo(
758    sa: *const c_void,
759    salen: u32,
760    host: *mut c_char,
761    hostlen: u32,
762    serv: *mut c_char,
763    servlen: u32,
764    flags: c_int,
765) -> c_int {
766    if sa.is_null() { return EAI_FAIL_V; }
767    let family = *(sa as *const u16) as c_int;
768    let mut addr = [0u8; 16];
769    let mut scope: u32 = 0;
770    let port: u16;
771    match family {
772        AF_INET => {
773            if salen < 16 { return EAI_FAMILY_V; }
774            port = u16::from_be(*((sa as *const u8).add(2) as *const u16));
775            core::ptr::copy_nonoverlapping((sa as *const u8).add(4), addr.as_mut_ptr(), 4);
776        }
777        AF_INET6 => {
778            if salen < 28 { return EAI_FAMILY_V; }
779            port = u16::from_be(*((sa as *const u8).add(2) as *const u16));
780            core::ptr::copy_nonoverlapping((sa as *const u8).add(8), addr.as_mut_ptr(), 16);
781            scope = *((sa as *const u8).add(24) as *const u32);
782        }
783        _ => return EAI_FAMILY_V,
784    }
785
786    if !host.is_null() && hostlen > 0 {
787        let mut buf = [0 as c_char; 300];
788        let mut have = false;
789        if flags & NI_NUMERICHOST_V == 0 {
790            have = hosts_lookup_addr(addr.as_ptr(), family);
791            if have {
792                let l = crate::strlen(HOST_NAME.as_ptr());
793                let l = core::cmp::min(l, buf.len() - 1);
794                core::ptr::copy_nonoverlapping(HOST_NAME.as_ptr(), buf.as_mut_ptr(), l);
795                buf[l] = 0;
796            }
797            if !have {
798                let mut ptr = [0 as c_char; 80];
799                let ok = if family == AF_INET {
800                    let a = addr;
801                    // d.c.b.a.in-addr.arpa
802                    let mut o = 0usize;
803                    let mut i = 4usize;
804                    while i > 0 {
805                        i -= 1;
806                        o += nd_itoa(a[i] as u32, ptr.as_mut_ptr().add(o));
807                        *ptr.as_mut_ptr().add(o) = b'.' as c_char;
808                        o += 1;
809                    }
810                    for &c in b"in-addr.arpa\0" { *ptr.as_mut_ptr().add(o) = c as c_char; o += 1; }
811                    true
812                } else {
813                    nd_reverse6(addr.as_ptr(), ptr.as_mut_ptr(), ptr.len())
814                };
815                if ok {
816                    let mut answer = [0u8; 512];
817                    let r = res_query(ptr.as_ptr() as *const c_char, 1, 12, answer.as_mut_ptr(), answer.len() as c_int);
818                    if r >= 12 {
819                        let end = answer.as_ptr().add(r as usize);
820                        let qd = dns_read_u16(answer.as_ptr().add(4)) as usize;
821                        let an = dns_read_u16(answer.as_ptr().add(6)) as usize;
822                        let mut p = answer.as_ptr().add(12);
823                        for _ in 0..qd { p = dns_skip_name(p, end); p = p.add(4); }
824                        for _ in 0..an {
825                            p = dns_skip_name(p, end);
826                            if p.add(10) > end { break; }
827                            let typ = dns_read_u16(p);
828                            let rdlen = dns_read_u16(p.add(8)) as usize;
829                            let rdata = p.add(10);
830                            if typ == 12 && dns_expand_name(rdata, answer.as_ptr(), buf.as_mut_ptr(), buf.len()) {
831                                let mut l = crate::strlen(buf.as_ptr());
832                                if l > 0 && buf[l - 1] == b'.' as c_char { l -= 1; }
833                                buf[l] = 0;
834                                have = l > 0;
835                                break;
836                            }
837                            p = rdata.add(rdlen);
838                        }
839                    }
840                }
841            }
842        }
843        if !have {
844            if flags & NI_NAMEREQD_V != 0 { return EAI_NONAME_V; }
845            let aptr = if family == AF_INET { addr.as_ptr() as *const c_void } else { addr.as_ptr() as *const c_void };
846            if inet_ntop(family, aptr, buf.as_mut_ptr(), buf.len() as u32).is_null() {
847                return EAI_FAIL_V;
848            }
849            if scope != 0 && family == AF_INET6 {
850                let mut l = crate::strlen(buf.as_ptr());
851                *buf.as_mut_ptr().add(l) = b'%' as c_char;
852                l += 1;
853                let mut name = [0 as c_char; 32];
854                let have_name = flags & NI_NUMERICSCOPE_V == 0
855                    && if_indextoname(scope, name.as_mut_ptr()) != core::ptr::null_mut();
856                if have_name {
857                    let nl = crate::strlen(name.as_ptr());
858                    core::ptr::copy_nonoverlapping(name.as_ptr(), buf.as_mut_ptr().add(l), nl + 1);
859                } else {
860                    nd_itoa(scope, buf.as_mut_ptr().add(l));
861                }
862            }
863        }
864        if flags & NI_NOFQDN_V != 0 {
865            let mut i = 0usize;
866            while buf[i] != 0 {
867                if buf[i] == b'.' as c_char { buf[i] = 0; break; }
868                i += 1;
869            }
870        }
871        if crate::strlen(buf.as_ptr()) >= hostlen as usize { return EAI_OVERFLOW_V; }
872        let l = crate::strlen(buf.as_ptr());
873        core::ptr::copy_nonoverlapping(buf.as_ptr(), host, l + 1);
874    }
875
876    if !serv.is_null() && servlen > 0 {
877        let mut buf = [0 as c_char; 64];
878        let mut have = false;
879        if flags & NI_NUMERICSERV_V == 0 {
880            let proto = if flags & NI_DGRAM_V != 0 { b"udp\0" } else { b"tcp\0" };
881            let port_net = (port as u16).to_be() as c_int;
882            let se = getservbyport(port_net, proto.as_ptr() as *const c_char);
883            if !se.is_null() {
884                let l = crate::strlen((*se).s_name);
885                if l < buf.len() {
886                    core::ptr::copy_nonoverlapping((*se).s_name, buf.as_mut_ptr(), l + 1);
887                    have = true;
888                }
889            }
890        }
891        if !have {
892            nd_itoa(port as u32, buf.as_mut_ptr());
893        }
894        if crate::strlen(buf.as_ptr()) >= servlen as usize { return EAI_OVERFLOW_V; }
895        let l = crate::strlen(buf.as_ptr());
896        core::ptr::copy_nonoverlapping(buf.as_ptr(), serv, l + 1);
897    }
898    0
899}
900
901// ------------------------------------------------------------------
902// getaddrinfo
903// ------------------------------------------------------------------
904
905unsafe fn nd_make_addrinfo(
906    family: c_int,
907    socktype: c_int,
908    protocol: c_int,
909    addr: *const u8,
910    port: u16,
911    flags: c_int,
912    canon: *const c_char,
913) -> *mut AddrInfo {
914    let addrlen: usize = if family == AF_INET6 { 28 } else { 16 };
915    let canonlen = if !canon.is_null() { crate::strlen(canon) + 1 } else { 0 };
916    let total = core::mem::size_of::<AddrInfo>() + addrlen + canonlen;
917    let p = crate::malloc(total) as *mut u8;
918    if p.is_null() { return core::ptr::null_mut(); }
919    core::ptr::write_bytes(p, 0, total);
920    let ai = p as *mut AddrInfo;
921    (*ai).ai_flags = flags;
922    (*ai).ai_family = family;
923    (*ai).ai_socktype = socktype;
924    (*ai).ai_protocol = protocol;
925    (*ai).ai_addrlen = addrlen as u32;
926    let sa = p.add(core::mem::size_of::<AddrInfo>());
927    (*ai).ai_addr = sa as *mut c_void;
928    if family == AF_INET6 {
929        *(sa as *mut u16) = AF_INET6 as u16;
930        *(sa.add(2) as *mut u16) = port.to_be();
931        core::ptr::copy_nonoverlapping(addr, sa.add(8), 16);
932    } else {
933        *(sa as *mut u16) = AF_INET as u16;
934        *(sa.add(2) as *mut u16) = port.to_be();
935        core::ptr::copy_nonoverlapping(addr, sa.add(4), 4);
936    }
937    if canonlen > 0 {
938        let c = sa.add(addrlen);
939        core::ptr::copy_nonoverlapping(canon as *const u8, c, canonlen);
940        (*ai).ai_canonname = c as *mut c_char;
941    }
942    ai
943}
944
945unsafe fn nd_parse_service(service: *const c_char, socktype: c_int, protocol: c_int, port: *mut u16, proto_out: *mut c_int) -> bool {
946    if service.is_null() { *port = 0; return true; }
947    let mut end: *mut c_char = core::ptr::null_mut();
948    let v = crate::strtoul(service, &mut end, 10);
949    let numeric = !end.is_null() && *end == 0;
950    if numeric {
951        if v > 65535 { return false; }
952        *port = v as u16;
953        return true;
954    }
955    let proto_name: *const c_char = if protocol == 6 { b"tcp\0".as_ptr() as *const c_char }
956        else if protocol == 17 { b"udp\0".as_ptr() as *const c_char }
957        else if socktype == SOCK_STREAM { b"tcp\0".as_ptr() as *const c_char }
958        else if socktype == SOCK_DGRAM { b"udp\0".as_ptr() as *const c_char }
959        else { core::ptr::null() };
960    let se = getservbyname(service, proto_name);
961    if se.is_null() { return false; }
962    *port = ntohs((*se).s_port as u16);
963    *proto_out = if crate::strcmp((*se).s_proto as *const u8, b"udp\0".as_ptr()) == 0 { 17 } else { 6 };
964    true
965}
966
967#[no_mangle]
968pub unsafe extern "C" fn getaddrinfo(
969    node: *const c_char,
970    service: *const c_char,
971    hints: *const AddrInfo,
972    res: *mut *mut AddrInfo,
973) -> c_int {
974    if res.is_null() { return EAI_FAIL_V; }
975    *res = core::ptr::null_mut();
976
977    let mut family = 0;
978    let mut socktype = 0;
979    let mut protocol = 0;
980    let mut flags = 0;
981    if !hints.is_null() {
982        family = (*hints).ai_family;
983        socktype = (*hints).ai_socktype;
984        protocol = (*hints).ai_protocol;
985        flags = (*hints).ai_flags;
986    }
987    if family != 0 && family != AF_INET && family != AF_INET6 { return EAI_FAMILY_V; }
988    if socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM { return EAI_SOCKTYPE_V; }
989    if flags & AI_NUMERICSERV_V != 0 && !service.is_null() {
990        let mut end: *mut c_char = core::ptr::null_mut();
991        let _ = crate::strtoul(service, &mut end, 10);
992        if end.is_null() || *end != 0 { return EAI_NONAME_V; }
993    }
994
995    let mut port: u16 = 0;
996    let mut proto_from_service = 0;
997    if !nd_parse_service(service, socktype, protocol, &mut port, &mut proto_from_service) {
998        return EAI_SERVICE_V;
999    }
1000
1001    let mut addrs: [[u8; 16]; 8] = [[0; 16]; 8];
1002    let mut fams: [c_int; 8] = [0; 8];
1003    let mut count = 0usize;
1004    let mut canon: [c_char; 256] = [0; 256];
1005
1006    if node.is_null() {
1007        if family == 0 || family == AF_INET6 {
1008            if family == 0 && flags & AI_V4MAPPED_V != 0 && flags & AI_ALL_V != 0 {
1009                // handled below through IPv4 too
1010            }
1011            addrs[count] = [0; 16];
1012            if flags & AI_PASSIVE_V == 0 {
1013                addrs[count][15] = 1;
1014            }
1015            fams[count] = AF_INET6;
1016            count += 1;
1017        }
1018        if family == 0 || family == AF_INET {
1019            addrs[count] = [0; 16];
1020            if flags & AI_PASSIVE_V == 0 {
1021                addrs[count][0] = 127;
1022                addrs[count][3] = 1;
1023            }
1024            fams[count] = AF_INET;
1025            count += 1;
1026        }
1027    } else {
1028        let mut numeric = false;
1029        let mut tmp = [0u8; 16];
1030        if inet_pton(AF_INET, node, tmp.as_mut_ptr() as *mut c_void) == 1 {
1031            numeric = true;
1032            if family == 0 || family == AF_INET {
1033                addrs[count][..4].copy_from_slice(&tmp[..4]);
1034                fams[count] = AF_INET;
1035                count += 1;
1036            }
1037        }
1038        if !numeric && inet_pton(AF_INET6, node, tmp.as_mut_ptr() as *mut c_void) == 1 {
1039            numeric = true;
1040            if family == 0 || family == AF_INET6 {
1041                addrs[count] = tmp;
1042                fams[count] = AF_INET6;
1043                count += 1;
1044            }
1045        }
1046        if flags & AI_NUMERICHOST_V != 0 && !numeric {
1047            return EAI_NONAME_V;
1048        }
1049        if !numeric {
1050            let mut got = false;
1051            // Prefer /etc/hosts, then DNS.
1052            if hosts_lookup_name(node) {
1053                let l = core::cmp::min(crate::strlen(HOST_ENT.h_name), canon.len() - 1);
1054                core::ptr::copy_nonoverlapping(HOST_ENT.h_name, canon.as_mut_ptr(), l);
1055                canon[l] = 0;
1056                if family == 0 || family == AF_INET {
1057                    core::ptr::copy_nonoverlapping(HOSTS_ADDR.as_ptr(), addrs[count].as_mut_ptr(), 4);
1058                    fams[count] = AF_INET;
1059                    count += 1;
1060                    got = true;
1061                }
1062                // gethostbyname2 may return more than the hosts entry; fall
1063                // through for the requested family only when needed.
1064                if family == AF_INET6 {
1065                    got = false;
1066                }
1067            }
1068            if !got {
1069                let order: [c_int; 2] = if family == AF_INET6 { [AF_INET6, AF_INET] } else { [AF_INET, AF_INET6] };
1070                for &f in &order {
1071                    if family != 0 && family != f { continue; }
1072                    let h = gethostbyname2(node, f);
1073                    if h.is_null() { continue; }
1074                    let n = core::cmp::min((*h).h_length as usize, 16);
1075                    core::ptr::copy_nonoverlapping((*h).h_addr_list.read() as *const u8, addrs[count].as_mut_ptr(), n);
1076                    fams[count] = f;
1077                    count += 1;
1078                    let hl = crate::strlen((*h).h_name);
1079                    let l = core::cmp::min(hl, canon.len() - 1);
1080                    core::ptr::copy_nonoverlapping((*h).h_name, canon.as_mut_ptr(), l);
1081                    canon[l] = 0;
1082                    got = true;
1083                    if family != 0 { break; }
1084                }
1085            }
1086            if !got {
1087                return if h_errno == TRY_AGAIN { EAI_AGAIN_V } else { EAI_NONAME_V };
1088            }
1089        }
1090    }
1091    if count == 0 { return EAI_NONAME_V; }
1092
1093    let canonptr: *const c_char = if flags & AI_CANONNAME_V != 0 && canon[0] != 0 {
1094        canon.as_ptr()
1095    } else if flags & AI_CANONNAME_V != 0 && !node.is_null() {
1096        node
1097    } else {
1098        core::ptr::null()
1099    };
1100
1101    let mut head: *mut AddrInfo = core::ptr::null_mut();
1102    let mut tail: *mut AddrInfo = core::ptr::null_mut();
1103    let mut emitted = 0usize;
1104    for i in 0..count {
1105        let types: [c_int; 2] = if socktype == 0 { [SOCK_STREAM, SOCK_DGRAM] } else { [socktype, socktype] };
1106        let ntypes = if socktype == 0 { 2 } else { 1 };
1107        for t in 0..ntypes {
1108            let st = types[t];
1109            if protocol != 0 {
1110                if (protocol == 6 && st != SOCK_STREAM) || (protocol == 17 && st != SOCK_DGRAM) { continue; }
1111            }
1112            if proto_from_service != 0 && protocol == 0 {
1113                if (proto_from_service == 6 && st != SOCK_STREAM) || (proto_from_service == 17 && st != SOCK_DGRAM) { continue; }
1114            }
1115            let proto = if protocol != 0 { protocol } else if st == SOCK_STREAM { 6 } else { 17 };
1116            let cp = if emitted == 0 { canonptr } else { core::ptr::null() };
1117            let ai = nd_make_addrinfo(fams[i], st, proto, addrs[i].as_ptr(), port, flags, cp);
1118            if ai.is_null() {
1119                freeaddrinfo(head);
1120                return EAI_MEMORY_V;
1121            }
1122            if head.is_null() { head = ai; } else { (*tail).ai_next = ai; }
1123            tail = ai;
1124            emitted += 1;
1125        }
1126    }
1127    if emitted == 0 { return EAI_SERVICE_V; }
1128    *res = head;
1129    0
1130}
1131
1132#[no_mangle]
1133pub unsafe extern "C" fn freeaddrinfo(mut ai: *mut AddrInfo) {
1134    while !ai.is_null() {
1135        let next = (*ai).ai_next;
1136        crate::free(ai as *mut c_void);
1137        ai = next;
1138    }
1139}
1140
1141#[no_mangle]
1142pub unsafe extern "C" fn gai_strerror(code: c_int) -> *const c_char {
1143    let s: &[u8] = match code {
1144        EAI_BADFLAGS_V => b"Bad flags\0",
1145        EAI_NONAME_V => b"Name not known\0",
1146        EAI_AGAIN_V => b"Try again\0",
1147        EAI_FAIL_V => b"Non-recoverable failure\0",
1148        -5 => b"No address data\0",
1149        EAI_FAMILY_V => b"Unsupported family\0",
1150        EAI_SOCKTYPE_V => b"Unsupported socket type\0",
1151        EAI_SERVICE_V => b"Unsupported service\0",
1152        -9 => b"Address family not supported\0",
1153        EAI_MEMORY_V => b"Out of memory\0",
1154        EAI_SYSTEM_V => b"System error\0",
1155        EAI_OVERFLOW_V => b"Overflow\0",
1156        _ => b"Unknown error\0",
1157    };
1158    s.as_ptr() as *const c_char
1159}
1160
1161// ------------------------------------------------------------------
1162// ttyname
1163// ------------------------------------------------------------------
1164
1165#[no_mangle]
1166pub unsafe extern "C" fn ttyname_r(fd: c_int, name: *mut c_char, size: usize) -> c_int {
1167    if name.is_null() || size == 0 { return EINVAL; }
1168    if crate::isatty(fd) != 1 { return NDB_ENOTTY; }
1169    let mut procname = [0 as c_char; 32];
1170    let mut o = 0usize;
1171    for &c in b"/proc/self/fd/" { procname[o] = c as c_char; o += 1; }
1172    o += nd_itoa(fd as u32, procname.as_mut_ptr().add(o));
1173    procname[o] = 0;
1174    let n = crate::readlink(procname.as_ptr(), name, size - 1);
1175    if n < 0 { return ERRNO!(); }
1176    if n as usize == size - 1 { return NDB_ERANGE; }
1177    *name.add(n as usize) = 0;
1178    0
1179}
1180
1181#[no_mangle]
1182pub unsafe extern "C" fn ttyname(fd: c_int) -> *mut c_char {
1183    static mut TTY_BUF: [c_char; 64] = [0; 64];
1184    if ttyname_r(fd, TTY_BUF.as_mut_ptr(), TTY_BUF.len()) != 0 {
1185        core::ptr::null_mut()
1186    } else {
1187        TTY_BUF.as_mut_ptr()
1188    }
1189}
1190
1191// ------------------------------------------------------------------
1192// Reentrant host lookups
1193// ------------------------------------------------------------------
1194
1195unsafe fn host_copy_out(
1196    h: *mut HostEnt,
1197    ret: *mut HostEnt,
1198    buf: *mut c_char,
1199    buflen: usize,
1200    result: *mut *mut HostEnt,
1201) -> c_int {
1202    if result.is_null() { return EINVAL; }
1203    *result = core::ptr::null_mut();
1204    let ptrsz = core::mem::size_of::<*mut c_char>();
1205    let mut need = crate::strlen((*h).h_name) + 1;
1206    let mut nalias = 0usize;
1207    if !(*h).h_aliases.is_null() {
1208        while !(*(*h).h_aliases.add(nalias)).is_null() {
1209            need += crate::strlen(*(*h).h_aliases.add(nalias)) + 1;
1210            nalias += 1;
1211        }
1212    }
1213    let alen = (*h).h_length as usize;
1214    let align = (ptrsz - (buf as usize & (ptrsz - 1))) & (ptrsz - 1);
1215    need += align + (nalias + 1) * ptrsz + alen + 2 * ptrsz;
1216    if need > buflen { return NDB_ERANGE; }
1217
1218    let mut p = buf.add(align);
1219    let aliases = p as *mut *mut c_char;
1220    p = p.add((nalias + 1) * ptrsz);
1221    let addr_list = p as *mut *mut c_char;
1222    p = p.add(2 * ptrsz);
1223
1224    let n = crate::strlen((*h).h_name);
1225    core::ptr::copy_nonoverlapping((*h).h_name, p, n + 1);
1226    (*ret).h_name = p;
1227    p = p.add(n + 1);
1228
1229    for i in 0..nalias {
1230        let src = *(*h).h_aliases.add(i);
1231        let n = crate::strlen(src);
1232        core::ptr::copy_nonoverlapping(src, p, n + 1);
1233        *aliases.add(i) = p;
1234        p = p.add(n + 1);
1235    }
1236    *aliases.add(nalias) = core::ptr::null_mut();
1237
1238    let src = *(*h).h_addr_list;
1239    core::ptr::copy_nonoverlapping(src, p, alen);
1240    *addr_list = p;
1241    *addr_list.add(1) = core::ptr::null_mut();
1242
1243    (*ret).h_aliases = aliases;
1244    (*ret).h_addrtype = (*h).h_addrtype;
1245    (*ret).h_length = (*h).h_length;
1246    (*ret).h_addr_list = addr_list;
1247    *result = ret;
1248    0
1249}
1250
1251#[no_mangle]
1252pub unsafe extern "C" fn gethostbyname_r(
1253    name: *const c_char,
1254    ret: *mut HostEnt,
1255    buf: *mut c_char,
1256    buflen: usize,
1257    result: *mut *mut HostEnt,
1258    errnop: *mut c_int,
1259) -> c_int {
1260    gethostbyname2_r(name, AF_INET, ret, buf, buflen, result, errnop)
1261}
1262
1263#[no_mangle]
1264pub unsafe extern "C" fn gethostbyname2_r(
1265    name: *const c_char,
1266    af: c_int,
1267    ret: *mut HostEnt,
1268    buf: *mut c_char,
1269    buflen: usize,
1270    result: *mut *mut HostEnt,
1271    errnop: *mut c_int,
1272) -> c_int {
1273    if result.is_null() { return EINVAL; }
1274    *result = core::ptr::null_mut();
1275    let h = gethostbyname2(name, af);
1276    if h.is_null() {
1277        if !errnop.is_null() { *errnop = if h_errno != 0 { h_errno } else { HOST_NOT_FOUND }; }
1278        return 0;
1279    }
1280    let r = host_copy_out(h, ret, buf, buflen, result);
1281    if r == NDB_ERANGE {
1282        if !errnop.is_null() { *errnop = NO_RECOVERY; }
1283        *result = core::ptr::null_mut();
1284    }
1285    r
1286}
1287
1288#[no_mangle]
1289pub unsafe extern "C" fn gethostbyaddr_r(
1290    addr: *const c_void,
1291    len: u32,
1292    type_: c_int,
1293    ret: *mut HostEnt,
1294    buf: *mut c_char,
1295    buflen: usize,
1296    result: *mut *mut HostEnt,
1297    errnop: *mut c_int,
1298) -> c_int {
1299    if result.is_null() { return EINVAL; }
1300    *result = core::ptr::null_mut();
1301    let h = gethostbyaddr(addr, len, type_);
1302    if h.is_null() {
1303        if !errnop.is_null() { *errnop = if h_errno != 0 { h_errno } else { HOST_NOT_FOUND }; }
1304        return 0;
1305    }
1306    let r = host_copy_out(h, ret, buf, buflen, result);
1307    if r == NDB_ERANGE {
1308        if !errnop.is_null() { *errnop = NO_RECOVERY; }
1309        *result = core::ptr::null_mut();
1310    }
1311    r
1312}
1313
1314
1315#[no_mangle]
1316pub unsafe extern "C" fn initgroups(user: *const c_char, group: u32) -> c_int {
1317    if user.is_null() { ERRNO!() = EINVAL; return -1; }
1318    let mut buf = [0u32; 256];
1319    let mut n: c_int = buf.len() as c_int;
1320    if getgrouplist(user, group, buf.as_mut_ptr(), &mut n) < 0 {
1321        if n <= 0 { return -1; }
1322        let p = crate::malloc(n as usize * core::mem::size_of::<u32>()) as *mut u32;
1323        if p.is_null() { return -1; }
1324        let mut n2 = n;
1325        let r = getgrouplist(user, group, p, &mut n2);
1326        let rr = if r < 0 { -1 } else { crate::setgroups(n2 as usize, p) };
1327        crate::free(p as *mut c_void);
1328        return rr;
1329    }
1330    crate::setgroups(n as usize, buf.as_ptr())
1331}