Skip to main content

c/
netdb_extra.rs

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