Files
RedBear-OS/src/header/netdb/mod.rs
T
2026-02-17 14:35:08 +00:00

1125 lines
37 KiB
Rust

//! `netdb.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netdb.h.html>.
mod dns;
use core::{cell::Cell, fmt::Write, mem, net::Ipv4Addr, ptr, str};
use alloc::{boxed::Box, str::SplitWhitespace, string::ToString, vec::Vec};
use crate::{
c_str::{CStr, CString},
error::ResultExt,
header::{
arpa_inet::inet_aton,
errno::*,
fcntl::O_RDONLY,
netinet_in::{htons, in_addr, ntohl, sockaddr_in, sockaddr_in6},
stdlib::atoi,
strings::strcasecmp,
sys_socket::{constants::AF_INET, sa_family_t, sockaddr, socklen_t},
unistd::SEEK_SET,
},
platform::{
self, Pal, Sys,
rlb::{Line, RawLineBuffer},
types::{c_char, c_int, c_ulong, c_void},
},
raw_cell::RawCell,
};
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
pub mod sys;
#[cfg(target_os = "redox")]
#[path = "redox.rs"]
pub mod sys;
pub use self::host::*;
pub mod host;
pub use self::lookup::*;
pub mod lookup;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netdb.h.html>.
#[repr(C)]
pub struct hostent {
h_name: *mut c_char,
h_aliases: *mut *mut c_char,
h_addrtype: c_int,
h_length: c_int,
h_addr_list: *mut *mut c_char,
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netdb.h.html>.
#[repr(C)]
pub struct netent {
n_name: *mut c_char, /* official name of net */
n_aliases: *mut *mut c_char, /* alias list */
n_addrtype: c_int, /* net address type */
n_net: c_ulong, /* network # */
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netdb.h.html>.
#[repr(C)]
pub struct protoent {
p_name: *mut c_char, /* official protocol name */
p_aliases: *mut *mut c_char, /* alias list */
p_proto: c_int, /* protocol # */
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netdb.h.html>.
#[repr(C)]
pub struct servent {
s_name: *mut c_char, /* official service name */
s_aliases: *mut *mut c_char, /* alias list */
s_port: c_int, /* port # */
s_proto: *mut c_char, /* protocol to use */
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netdb.h.html>.
#[repr(C)]
#[derive(Debug)]
pub struct addrinfo {
ai_flags: c_int, /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
ai_family: c_int, /* PF_xxx */
ai_socktype: c_int, /* SOCK_xxx */
ai_protocol: c_int, /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
ai_addrlen: socklen_t, /* length of ai_addr */
ai_canonname: *mut c_char, /* canonical name for hostname */
ai_addr: *mut sockaddr, /* binary address */
ai_next: *mut addrinfo, /* next structure in linked list */
}
pub const AI_PASSIVE: c_int = 0x0001;
pub const AI_CANONNAME: c_int = 0x0002;
pub const AI_NUMERICHOST: c_int = 0x0004;
pub const AI_V4MAPPED: c_int = 0x0008;
pub const AI_ALL: c_int = 0x0010;
pub const AI_ADDRCONFIG: c_int = 0x0020;
pub const AI_NUMERICSERV: c_int = 0x0400;
pub const EAI_BADFLAGS: c_int = -1;
pub const EAI_NONAME: c_int = -2;
pub const EAI_AGAIN: c_int = -3;
pub const EAI_FAIL: c_int = -4;
pub const EAI_NODATA: c_int = -5;
pub const EAI_FAMILY: c_int = -6;
pub const EAI_SOCKTYPE: c_int = -7;
pub const EAI_SERVICE: c_int = -8;
pub const EAI_ADDRFAMILY: c_int = -9;
pub const EAI_MEMORY: c_int = -10;
pub const EAI_SYSTEM: c_int = -11;
pub const EAI_OVERFLOW: c_int = -12;
pub const NI_MAXHOST: c_int = 1025;
pub const NI_MAXSERV: c_int = 32;
pub const NI_NUMERICHOST: c_int = 0x0001;
pub const NI_NUMERICSERV: c_int = 0x0002;
pub const NI_NOFQDN: c_int = 0x0004;
pub const NI_NAMEREQD: c_int = 0x0008;
pub const NI_DGRAM: c_int = 0x0010;
static mut NETDB: c_int = 0;
pub static mut NET_ENTRY: netent = netent {
n_name: ptr::null_mut(),
n_aliases: ptr::null_mut(),
n_addrtype: 0,
n_net: 0,
};
pub static NET_NAME: RawCell<Option<Vec<u8>>> = RawCell::new(None);
pub static NET_ALIASES: RawCell<Option<Vec<Vec<u8>>>> = RawCell::new(None);
pub static mut NET_ADDR: Option<u32> = None;
static mut N_POS: usize = 0;
static mut NET_STAYOPEN: c_int = 0;
#[thread_local]
pub static H_ERRNO: Cell<c_int> = Cell::new(0);
const H_UNSET: c_int = 0;
pub const HOST_NOT_FOUND: c_int = 1;
pub const NO_DATA: c_int = 2;
pub const NO_RECOVERY: c_int = 3;
pub const TRY_AGAIN: c_int = 4;
// Expected length of addresses
const SOCKLEN_AF_INET4: socklen_t = 4;
const SOCKLEN_AF_INET6: socklen_t = 16;
static mut PROTODB: c_int = 0;
static mut PROTO_ENTRY: protoent = protoent {
p_name: ptr::null_mut(),
p_aliases: ptr::null_mut(),
p_proto: 0 as c_int,
};
static PROTO_NAME: RawCell<Option<Vec<u8>>> = RawCell::new(None);
static PROTO_ALIASES: RawCell<Option<Vec<Vec<u8>>>> = RawCell::new(None);
static mut PROTO_NUM: Option<c_int> = None;
static mut P_POS: usize = 0;
static mut PROTO_STAYOPEN: c_int = 0;
static mut SERVDB: c_int = 0;
static mut SERV_ENTRY: servent = servent {
s_name: ptr::null_mut(),
s_aliases: ptr::null_mut(),
s_port: 0 as c_int,
s_proto: ptr::null_mut(),
};
static SERV_NAME: RawCell<Option<Vec<u8>>> = RawCell::new(None);
static SERV_ALIASES: RawCell<Option<Vec<Vec<u8>>>> = RawCell::new(None);
static mut SERV_PORT: Option<c_int> = None;
static SERV_PROTO: RawCell<Option<Vec<u8>>> = RawCell::new(None);
static mut S_POS: usize = 0;
static mut SERV_STAYOPEN: c_int = 0;
fn bytes_to_box_str(bytes: &[u8]) -> Box<str> {
Box::from(core::str::from_utf8(bytes).unwrap_or(""))
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn endnetent() {
if let Ok(()) = Sys::close(unsafe { NETDB }) {}; // TODO handle error
unsafe { NETDB = 0 };
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endprotoent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn endprotoent() {
if let Ok(()) = Sys::close(unsafe { PROTODB }) {}; // TODO handle error
unsafe { PROTODB = 0 };
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endservent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn endservent() {
if let Ok(()) = Sys::close(unsafe { SERVDB }) {}; // TODO handle error
unsafe { SERVDB = 0 };
}
/// See <https://pubs.opengroup.org/onlinepubs/009696799/functions/gethostbyaddr.html>.
/// Resolve a host name from a given network address.
///
/// # Arguments
/// * `v` - Address to resolve as a non-null [`in_addr`]
/// * `length` -
/// * `format` - AF_INET or AF_INET6
///
/// # Safety
/// * `v` must be a valid pointer.
/// * `length` must correctly match the size of `v` as expected by `format` (usually 4 or 16).
/// * This function is not reentrant and may modify static data.
///
/// # Panics
/// Panics if `v` is a null pointer.
///
/// # Deprecation
/// Deprecated as of POSIX.1-2001 and removed in POSIX.1-2008.
/// New code should use [`getaddrinfo`] instead.
#[unsafe(no_mangle)]
#[deprecated]
pub unsafe extern "C" fn gethostbyaddr(
v: *const c_void,
length: socklen_t,
format: c_int,
) -> *mut hostent {
assert!(
!v.is_null(),
"`gethostbyaddr()` called with null `v` (in_addr)"
);
// Uncomment if optional IPv6 support is added
// if length != SOCKLEN_AF_INET4 || length != SOCKLEN_AF_INET6 {
// H_ERRNO.set(NO_RECOVERY);
// return ptr::null_mut();
// }
if length != SOCKLEN_AF_INET4 {
H_ERRNO.set(NO_RECOVERY);
return ptr::null_mut();
}
let addr: in_addr = unsafe { *(v as *mut in_addr) };
// check the hosts file first
let mut p: *mut hostent;
unsafe { sethostent(HOST_STAYOPEN) };
while {
p = unsafe { gethostent() };
!p.is_null()
} {
let mut cp = unsafe { (*p).h_addr_list };
loop {
if cp.is_null() {
break;
}
if (unsafe { *cp }).is_null() {
break;
}
let mut cp_slice: [c_char; 4] = [0; 4];
unsafe { (*cp).copy_to(cp_slice.as_mut_ptr(), 4) };
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
let cp_s_addr = unsafe { mem::transmute::<[c_char; 4], u32>(cp_slice) };
#[cfg(any(target_arch = "riscv64", target_arch = "aarch64"))]
let cp_s_addr = u32::from_ne_bytes(cp_slice);
if cp_s_addr == addr.s_addr {
unsafe { sethostent(HOST_STAYOPEN) };
return p;
}
cp = unsafe { cp.offset(1) };
}
}
//TODO actually get aliases
let mut _host_aliases: Vec<Vec<u8>> = Vec::new();
_host_aliases.push(vec![b'\0']);
let mut host_aliases: Vec<*mut c_char> = Vec::new();
host_aliases.push(ptr::null_mut());
unsafe { HOST_ALIASES.unsafe_set(Some(_host_aliases)) };
match lookup_addr(addr).map(|host_names| host_names.into_iter().next()) {
Ok(Some(host_name)) => {
unsafe { _HOST_ADDR_LIST = addr.s_addr.to_ne_bytes() };
unsafe { HOST_ADDR_LIST = [&raw mut _HOST_ADDR_LIST as *mut c_char, ptr::null_mut()] };
unsafe { HOST_NAME.unsafe_set(Some(host_name)) };
unsafe {
HOST_ENTRY = hostent {
h_name: HOST_NAME.unsafe_mut().as_mut().unwrap().as_mut_ptr() as *mut c_char,
h_aliases: host_aliases.as_mut_slice().as_mut_ptr(),
h_addrtype: format,
h_length: length as i32,
h_addr_list: &raw mut HOST_ADDR_LIST as *mut _,
}
};
&raw mut HOST_ENTRY
}
// `glibc` sets errno if an address doesn't have a host name
// `musl` uses the address as the host name in said case
Ok(None) => {
H_ERRNO.set(HOST_NOT_FOUND);
ptr::null_mut()
}
Err(e) => {
// TODO: Better error separation in lookup_addr
H_ERRNO.set(NO_RECOVERY);
ptr::null_mut()
}
}
}
/// See <https://pubs.opengroup.org/onlinepubs/009696799/functions/gethostbyaddr.html>.
/// Resolve host information by name or IP address.
///
/// # Arguments
/// * `name` - Host name or IP address.
///
/// # Safety
/// `name` must be a valid string.
/// This function is not reentrant and may modify static data.
///
/// # Panics
/// Panics if `name` is a null pointer.
///
/// # Deprecation
/// Deprecated as of POSIX.1-2001 and removed in POSIX.1-2008.
/// New code should use [`getaddrinfo`] instead.
#[unsafe(no_mangle)]
#[deprecated]
pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut hostent {
let name_cstr = unsafe {
CStr::from_nullable_ptr(name).expect("gethostbyname() called with a NULL pointer")
};
let Ok(name_str) = str::from_utf8(name_cstr.to_bytes()) else {
H_ERRNO.set(NO_RECOVERY);
return ptr::null_mut();
};
// Addresses and hostnames are both valid, so we'll check addresses first
// The standard doesn't define what to do when called with addresses
// Some implementations just skip resolution and copy the address to h_name
if let Some(s_addr) = parse_ipv4_string(name_str) {
let addr = in_addr { s_addr };
return unsafe { gethostbyaddr(&addr as *const _ as *const c_void, 4, AF_INET) };
}
// check the hosts file first
let mut p: *mut hostent;
unsafe { sethostent(HOST_STAYOPEN) };
while {
p = unsafe { gethostent() };
!p.is_null()
} {
if unsafe { strcasecmp((*p).h_name, name) } == 0 {
unsafe { sethostent(HOST_STAYOPEN) };
return p;
}
let mut cp = unsafe { (*p).h_aliases };
loop {
if cp.is_null() {
break;
}
if (unsafe { *cp }).is_null() {
break;
}
if unsafe { strcasecmp(*cp, name) } == 0 {
unsafe { sethostent(HOST_STAYOPEN) };
return p;
}
cp = unsafe { cp.offset(1) };
}
}
let host = match lookup_host(name_str) {
Ok(lookuphost) => lookuphost,
Err(e) => {
H_ERRNO.set(NO_RECOVERY);
return ptr::null_mut();
}
};
let host_addr = match host.into_iter().next() {
Some(result) => result,
None => {
H_ERRNO.set(HOST_NOT_FOUND);
return ptr::null_mut();
}
};
let host_name: Vec<u8> = name_cstr.to_bytes().to_vec();
unsafe { HOST_NAME.unsafe_set(Some(host_name)) };
unsafe { _HOST_ADDR_LIST = host_addr.s_addr.to_ne_bytes() };
unsafe { HOST_ADDR_LIST = [&raw mut _HOST_ADDR_LIST as *mut c_char, ptr::null_mut()] };
unsafe { HOST_ADDR = Some(host_addr) };
//TODO actually get aliases
let mut _host_aliases: Vec<Vec<u8>> = Vec::new();
_host_aliases.push(vec![b'\0']);
let mut host_aliases: Vec<*mut c_char> = Vec::new();
host_aliases.push(ptr::null_mut());
host_aliases.push(ptr::null_mut());
unsafe { HOST_ALIASES.unsafe_set(Some(_host_aliases)) };
unsafe {
HOST_ENTRY = hostent {
h_name: HOST_NAME.unsafe_mut().as_mut().unwrap().as_mut_ptr() as *mut c_char,
h_aliases: host_aliases.as_mut_slice().as_mut_ptr(),
h_addrtype: AF_INET,
h_length: 4,
h_addr_list: &raw mut HOST_ADDR_LIST as *mut _,
}
};
unsafe { sethostent(HOST_STAYOPEN) };
&raw mut HOST_ENTRY as *mut hostent
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
pub unsafe extern "C" fn getnetbyaddr(net: u32, net_type: c_int) -> *mut netent {
unimplemented!();
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getnetbyname(name: *const c_char) -> *mut netent {
let mut n: *mut netent;
unsafe { setnetent(NET_STAYOPEN) };
while {
n = unsafe { getnetent() };
!n.is_null()
} {
if unsafe { strcasecmp((*n).n_name, name) } == 0 {
unsafe { setnetent(NET_STAYOPEN) };
return n;
}
}
unsafe { setnetent(NET_STAYOPEN) };
platform::ERRNO.set(ENOENT);
ptr::null_mut() as *mut netent
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getnetent() -> *mut netent {
// TODO: Rustify implementation
if unsafe { NETDB } == 0 {
unsafe { NETDB = Sys::open(c"/etc/networks".into(), O_RDONLY, 0).or_minus_one_errno() };
}
let mut rlb = RawLineBuffer::new(unsafe { NETDB });
rlb.seek(unsafe { N_POS });
let mut r: Box<str> = Box::default();
while r.is_empty() || r.split_whitespace().next() == None || r.starts_with('#') {
r = match rlb.next() {
Line::Some(s) => bytes_to_box_str(s),
_ => {
if unsafe { NET_STAYOPEN } == 0 {
unsafe { endnetent() };
}
return ptr::null_mut();
}
};
}
rlb.next();
unsafe { N_POS = rlb.line_pos() };
let mut iter: SplitWhitespace = r.split_whitespace();
let net_name = iter.next().unwrap().bytes().chain(Some(b'\0')).collect();
unsafe { NET_NAME.unsafe_set(Some(net_name)) };
let addr_vec: Vec<u8> = iter.next().unwrap().bytes().chain(Some(b'\0')).collect();
let addr_cstr = addr_vec.as_slice().as_ptr() as *const c_char;
let mut addr = mem::MaybeUninit::uninit();
unsafe { inet_aton(addr_cstr, addr.as_mut_ptr()) };
let addr = unsafe { addr.assume_init() };
unsafe { NET_ADDR = Some(ntohl(addr.s_addr)) };
let mut _net_aliases: Vec<Vec<u8>> = iter
.map(|alias| alias.bytes().chain(Some(b'\0')).collect())
.collect();
let mut net_aliases: Vec<*mut c_char> = _net_aliases
.iter_mut()
.map(|x| x.as_mut_ptr() as *mut c_char)
.chain(Some(ptr::null_mut()))
.collect();
unsafe { NET_ALIASES.unsafe_set(Some(_net_aliases)) };
unsafe {
NET_ENTRY = netent {
n_name: NET_NAME.unsafe_mut().as_mut().unwrap().as_mut_ptr() as *mut c_char,
n_aliases: net_aliases.as_mut_slice().as_mut_ptr(),
n_addrtype: AF_INET,
n_net: NET_ADDR.unwrap() as c_ulong,
}
};
&raw mut NET_ENTRY as *mut netent
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endprotoent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getprotobyname(name: *const c_char) -> *mut protoent {
let mut p: *mut protoent;
unsafe { setprotoent(PROTO_STAYOPEN) };
while {
p = unsafe { getprotoent() };
!p.is_null()
} {
if unsafe { strcasecmp((*p).p_name, name) } == 0 {
unsafe { setprotoent(PROTO_STAYOPEN) };
return p;
}
let mut cp = unsafe { (*p).p_aliases };
loop {
if cp.is_null() {
unsafe { setprotoent(PROTO_STAYOPEN) };
break;
}
if (unsafe { *cp }).is_null() {
unsafe { setprotoent(PROTO_STAYOPEN) };
break;
}
if unsafe { strcasecmp(*cp, name) } == 0 {
unsafe { setprotoent(PROTO_STAYOPEN) };
return p;
}
cp = unsafe { cp.offset(1) };
}
}
unsafe { setprotoent(PROTO_STAYOPEN) };
platform::ERRNO.set(ENOENT);
ptr::null_mut() as *mut protoent
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endprotoent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getprotobynumber(number: c_int) -> *mut protoent {
unsafe { setprotoent(PROTO_STAYOPEN) };
let mut p: *mut protoent;
while {
p = unsafe { getprotoent() };
!p.is_null()
} {
if unsafe { (*p).p_proto } == number {
unsafe { setprotoent(PROTO_STAYOPEN) };
return p;
}
}
unsafe { setprotoent(PROTO_STAYOPEN) };
platform::ERRNO.set(ENOENT);
ptr::null_mut() as *mut protoent
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endprotoent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getprotoent() -> *mut protoent {
if unsafe { PROTODB } == 0 {
unsafe { PROTODB = Sys::open(c"/etc/protocols".into(), O_RDONLY, 0).or_minus_one_errno() };
}
let mut rlb = RawLineBuffer::new(unsafe { PROTODB });
rlb.seek(unsafe { P_POS });
let mut r: Box<str> = Box::default();
while r.is_empty() || r.split_whitespace().next() == None || r.starts_with('#') {
r = match rlb.next() {
Line::Some(s) => bytes_to_box_str(s),
_ => {
if unsafe { PROTO_STAYOPEN } == 0 {
unsafe { endprotoent() };
}
return ptr::null_mut();
}
};
}
rlb.next();
unsafe { P_POS = rlb.line_pos() };
let mut iter: SplitWhitespace = r.split_whitespace();
let mut proto_name: Vec<u8> = iter.next().unwrap().as_bytes().to_vec();
proto_name.push(b'\0');
let mut num = iter.next().unwrap().as_bytes().to_vec();
num.push(b'\0');
unsafe { PROTO_NUM = Some(atoi(num.as_mut_slice().as_mut_ptr() as *mut c_char)) };
let mut _proto_aliases: Vec<Vec<u8>> = iter
.map(|alias| alias.bytes().chain(Some(b'\0')).collect())
.collect();
let mut proto_aliases: Vec<*mut i8> = _proto_aliases
.iter_mut()
.map(|x| x.as_mut_ptr() as *mut i8)
.chain(Some(ptr::null_mut()))
.collect();
unsafe { PROTO_ALIASES.unsafe_set(Some(_proto_aliases)) };
unsafe { PROTO_NAME.unsafe_set(Some(proto_name)) };
unsafe {
PROTO_ENTRY = protoent {
p_name: PROTO_NAME
.unsafe_mut()
.as_mut()
.unwrap()
.as_mut_slice()
.as_mut_ptr() as *mut c_char,
p_aliases: proto_aliases.as_mut_slice().as_mut_ptr() as *mut *mut c_char,
p_proto: PROTO_NUM.unwrap(),
}
};
if unsafe { PROTO_STAYOPEN } == 0 {
unsafe { endprotoent() };
}
&raw mut PROTO_ENTRY as *mut protoent
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endservent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getservbyname(name: *const c_char, proto: *const c_char) -> *mut servent {
unsafe { setservent(SERV_STAYOPEN) };
let mut p: *mut servent;
if proto.is_null() {
while {
p = unsafe { getservent() };
!p.is_null()
} {
if unsafe { strcasecmp((*p).s_name, name) } == 0 {
unsafe { setservent(SERV_STAYOPEN) };
return p;
}
}
} else {
while {
p = unsafe { getservent() };
!p.is_null()
} {
if unsafe { strcasecmp((*p).s_name, name) } == 0
&& unsafe { strcasecmp((*p).s_proto, proto) } == 0
{
unsafe { setservent(SERV_STAYOPEN) };
return p;
}
}
}
unsafe { setservent(SERV_STAYOPEN) };
platform::ERRNO.set(ENOENT);
ptr::null_mut() as *mut servent
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endservent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getservbyport(port: c_int, proto: *const c_char) -> *mut servent {
unsafe { setservent(SERV_STAYOPEN) };
let mut p: *mut servent;
if proto.is_null() {
while {
p = unsafe { getservent() };
!p.is_null()
} {
if unsafe { (*p).s_port } == port {
unsafe { setservent(SERV_STAYOPEN) };
return p;
}
}
} else {
while {
p = unsafe { getservent() };
!p.is_null()
} {
if unsafe { (*p).s_port } == port && unsafe { strcasecmp((*p).s_proto, proto) } == 0 {
unsafe { setservent(SERV_STAYOPEN) };
return p;
}
}
}
unsafe { setservent(SERV_STAYOPEN) };
platform::ERRNO.set(ENOENT);
ptr::null_mut()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endservent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getservent() -> *mut servent {
if unsafe { SERVDB } == 0 {
// TODO: Rustify
unsafe { SERVDB = Sys::open(c"/etc/services".into(), O_RDONLY, 0).or_minus_one_errno() };
}
let mut rlb = RawLineBuffer::new(unsafe { SERVDB });
rlb.seek(unsafe { S_POS });
let r: Box<str> = Box::default();
loop {
let r = match rlb.next() {
Line::Some(s) => bytes_to_box_str(s),
_ => {
if unsafe { SERV_STAYOPEN } == 0 {
unsafe { endservent() };
}
return ptr::null_mut();
}
};
let mut iter = r.split_whitespace();
let serv_name = match iter.next() {
Some(serv_name) => serv_name.bytes().chain(Some(b'\0')).collect(),
None => continue,
};
let port_proto = match iter.next() {
Some(port_proto) => port_proto,
None => continue,
};
let mut split = port_proto.split('/');
let mut port: Vec<u8> = match split.next() {
Some(port) => port.bytes().chain(Some(b'\0')).collect(),
None => continue,
};
unsafe {
SERV_PORT = Some(
htons(atoi(port.as_mut_slice().as_mut_ptr() as *mut c_char) as u16) as u32 as i32,
)
};
let proto = match split.next() {
Some(proto) => proto.bytes().chain(Some(b'\0')).collect(),
None => continue,
};
rlb.next();
unsafe { S_POS = rlb.line_pos() };
/*
*let mut _serv_aliases: Vec<Vec<u8>> = Vec::new();
*loop {
* let mut alias = match iter.next() {
* Some(s) => s.as_bytes().to_vec(),
* _ => break
* };
* alias.push(b'\0');
* _serv_aliases.push(alias);
*}
*let mut serv_aliases: Vec<*mut i8> = _serv_aliases.iter_mut().map(|x| x.as_mut_ptr() as *mut i8).collect();
*serv_aliases.push(ptr::null_mut());
*
*/
let mut _serv_aliases: Vec<Vec<u8>> = Vec::new();
_serv_aliases.push(vec![b'\0']);
let mut serv_aliases: Vec<*mut i8> = Vec::new();
serv_aliases.push(ptr::null_mut());
serv_aliases.push(ptr::null_mut());
unsafe { SERV_ALIASES.unsafe_set(Some(_serv_aliases)) };
unsafe { SERV_NAME.unsafe_set(Some(serv_name)) };
unsafe { SERV_PROTO.unsafe_set(Some(proto)) };
unsafe {
SERV_ENTRY = servent {
s_name: SERV_NAME
.unsafe_mut()
.as_mut()
.unwrap()
.as_mut_slice()
.as_mut_ptr() as *mut c_char,
s_aliases: serv_aliases.as_mut_slice().as_mut_ptr() as *mut *mut c_char,
s_port: SERV_PORT.unwrap(),
s_proto: SERV_PROTO
.unsafe_mut()
.as_mut()
.unwrap()
.as_mut_slice()
.as_mut_ptr() as *mut c_char,
}
};
if unsafe { SERV_STAYOPEN } == 0 {
unsafe { endservent() };
}
break &raw mut SERV_ENTRY as *mut servent;
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn setnetent(stayopen: c_int) {
unsafe { NET_STAYOPEN = stayopen };
if unsafe { NETDB } == 0 {
unsafe { NETDB = Sys::open(c"/etc/networks".into(), O_RDONLY, 0).or_minus_one_errno() }
} else {
if let Ok(_) = Sys::lseek(unsafe { NETDB }, 0, SEEK_SET) {}; // TODO handle errror
unsafe { N_POS = 0 };
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endprotoent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn setprotoent(stayopen: c_int) {
unsafe { PROTO_STAYOPEN = stayopen };
if unsafe { PROTODB } == 0 {
unsafe { PROTODB = Sys::open(c"/etc/protocols".into(), O_RDONLY, 0).or_minus_one_errno() }
} else {
if let Ok(_) = Sys::lseek(unsafe { PROTODB }, 0, SEEK_SET) {}; // TODO handle error
unsafe { P_POS = 0 };
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endservent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn setservent(stayopen: c_int) {
unsafe { SERV_STAYOPEN = stayopen };
if unsafe { SERVDB } == 0 {
unsafe { SERVDB = Sys::open(c"/etc/services".into(), O_RDONLY, 0).or_minus_one_errno() }
} else {
if let Ok(_) = Sys::lseek(unsafe { SERVDB }, 0, SEEK_SET) {}; // TODO handle error
unsafe { S_POS = 0 };
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/freeaddrinfo.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getaddrinfo(
node: *const c_char,
service: *const c_char,
hints: *const addrinfo,
res: *mut *mut addrinfo,
) -> c_int {
let node_opt = unsafe { CStr::from_nullable_ptr(node) };
let service_opt = unsafe { CStr::from_nullable_ptr(service) };
let hints_opt = if hints.is_null() {
None
} else {
Some(unsafe { &*hints })
};
log::trace!(
"getaddrinfo({:?}, {:?}, {:?})",
node_opt.map(|c| unsafe { str::from_utf8_unchecked(c.to_bytes()) }),
service_opt.map(|c| unsafe { str::from_utf8_unchecked(c.to_bytes()) }),
hints_opt
);
//TODO: Use hints
let mut ai_flags = hints_opt.map_or(0, |hints| hints.ai_flags);
let mut ai_family; // = hints_opt.map_or(AF_UNSPEC, |hints| hints.ai_family);
let ai_socktype = hints_opt.map_or(0, |hints| hints.ai_socktype);
let mut ai_protocol; // = hints_opt.map_or(0, |hints| hints.ai_protocol);
unsafe { *res = ptr::null_mut() };
let mut port = 0;
if let Some(service) = service_opt {
//TODO: Support other service definitions as well as AI_NUMERICSERV
match unsafe { str::from_utf8_unchecked(service.to_bytes()) }.parse::<u16>() {
Ok(ok) => port = ok,
Err(_err) => (),
}
}
let node = node_opt.unwrap_or_else(|| {
//TODO: Optimize by bypassing string parsing
if ai_flags & AI_PASSIVE > 0 {
c"0.0.0.0".into()
} else {
c"127.0.0.1".into()
}
});
let lookuphost = if ai_flags & AI_NUMERICHOST > 0 {
match parse_ipv4_string(unsafe { str::from_utf8_unchecked(node.to_bytes()) }) {
Some(s_addr) => vec![in_addr { s_addr }],
None => {
return EAI_NONAME;
}
}
} else {
match lookup_host(unsafe { str::from_utf8_unchecked(node.to_bytes()) }) {
Ok(lookuphost) => lookuphost,
Err(e) => {
platform::ERRNO.set(e);
return EAI_SYSTEM;
}
}
};
for in_addr in lookuphost {
ai_family = AF_INET;
ai_protocol = 0;
let ai_addr = Box::into_raw(Box::new(sockaddr_in {
sin_family: ai_family as sa_family_t,
sin_port: htons(port),
sin_addr: in_addr,
sin_zero: [0; 8],
})) as *mut sockaddr;
let ai_addrlen = mem::size_of::<sockaddr_in>() as socklen_t;
let ai_canonname = if ai_flags & AI_CANONNAME > 0 {
if node_opt.is_none() {
return EAI_BADFLAGS;
}
ai_flags &= !AI_CANONNAME;
node.to_owned_cstring().into_raw()
} else {
ptr::null_mut()
};
let addrinfo = Box::new(addrinfo {
ai_flags: 0,
ai_family,
ai_socktype,
ai_protocol,
ai_addrlen,
ai_canonname,
ai_addr,
ai_next: ptr::null_mut(),
});
unsafe {
let mut indirect = res;
while !(*indirect).is_null() {
indirect = &mut (**indirect).ai_next;
}
*indirect = Box::into_raw(addrinfo)
}
}
0
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getnameinfo.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getnameinfo(
addr: *const sockaddr,
addrlen: socklen_t,
host: *mut c_char,
hostlen: socklen_t,
serv: *mut c_char,
servlen: socklen_t,
flags: c_int,
) -> c_int {
if addr.is_null() || addrlen as usize != mem::size_of::<sockaddr_in>() {
return EAI_FAMILY;
}
let sa = unsafe { &*(addr as *const sockaddr_in) };
if !serv.is_null() && servlen > 0 {
if flags & NI_NUMERICSERV != 0 {
let port_str = sa.sin_port.to_be().to_string();
let port_bytes = port_str.as_bytes();
if (servlen as usize) <= port_bytes.len() {
return EAI_MEMORY; // Buffer too small
}
unsafe {
ptr::copy_nonoverlapping(
port_bytes.as_ptr() as *const c_char,
serv,
port_bytes.len(),
)
};
unsafe { *serv.add(port_bytes.len()) = 0 };
} else {
// TODO: Implement service name lookup (e.g., from /etc/services)
unsafe { *serv = 0 };
}
}
if !host.is_null() && hostlen > 0 {
if flags & NI_NUMERICHOST != 0 {
let ip_addr = Ipv4Addr::from(sa.sin_addr.s_addr.to_be());
let ip_str = ip_addr.to_string();
let ip_bytes = ip_str.as_bytes();
if (hostlen as usize) <= ip_bytes.len() {
return EAI_MEMORY; // Buffer too small
}
unsafe {
ptr::copy_nonoverlapping(ip_bytes.as_ptr() as *const c_char, host, ip_bytes.len())
};
unsafe { *host.add(ip_bytes.len()) = 0 };
} else {
match lookup_addr(sa.sin_addr.clone()).map(|host_names| host_names.into_iter().next()) {
Ok(Some(hostname)) => {
if (hostlen as usize) <= hostname.len() {
return EAI_MEMORY; // Buffer too small
}
unsafe {
ptr::copy_nonoverlapping(
hostname.as_ptr() as *const c_char,
host,
hostname.len(),
);
*host.add(hostname.len()) = 0;
}
}
Ok(None) => {
if flags & NI_NAMEREQD != 0 {
return EAI_NONAME;
}
}
Err(_) => {
if flags & NI_NAMEREQD != 0 {
return EAI_NONAME;
}
let ip_addr = Ipv4Addr::from(sa.sin_addr.s_addr.to_be());
let ip_str = ip_addr.to_string();
let ip_bytes = ip_str.as_bytes();
if (hostlen as usize) <= ip_bytes.len() {
return EAI_MEMORY;
}
unsafe {
ptr::copy_nonoverlapping(
ip_bytes.as_ptr() as *const c_char,
host,
ip_bytes.len(),
);
*host.add(ip_bytes.len()) = 0;
}
}
}
}
}
0
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/freeaddrinfo.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn freeaddrinfo(res: *mut addrinfo) {
let mut ai = res;
while !ai.is_null() {
let bai = unsafe { Box::from_raw(ai) };
if !bai.ai_canonname.is_null() {
drop(unsafe { CString::from_raw(bai.ai_canonname) });
}
if !bai.ai_addr.is_null() {
if bai.ai_addrlen == mem::size_of::<sockaddr_in>() as socklen_t {
unsafe { drop(Box::from_raw(bai.ai_addr as *mut sockaddr_in)) };
} else if bai.ai_addrlen == mem::size_of::<sockaddr_in6>() as socklen_t {
unsafe { drop(Box::from_raw(bai.ai_addr as *mut sockaddr_in6)) };
} else {
todo_skip!(0, "freeaddrinfo: unknown ai_addrlen {}", bai.ai_addrlen);
}
}
ai = bai.ai_next;
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/gai_strerror.html>.
#[unsafe(no_mangle)]
pub const extern "C" fn gai_strerror(errcode: c_int) -> *const c_char {
match errcode {
EAI_BADFLAGS => c"Invalid flags",
EAI_NONAME => c"Name does not resolve",
EAI_AGAIN => c"Try again",
EAI_FAIL => c"Non-recoverable error",
EAI_NODATA => c"Unknown error",
EAI_FAMILY => c"Unrecognized address family or invalid length",
EAI_SOCKTYPE => c"Unrecognized socket type",
EAI_SERVICE => c"Unrecognized service",
EAI_ADDRFAMILY => c"Address family for name not supported",
EAI_MEMORY => c"Out of memory",
EAI_SYSTEM => c"System error",
EAI_OVERFLOW => c"Overflow",
_ => c"Unknown error",
}
.as_ptr()
}
/// Provide a pointer to relibc's internal [`H_ERRNO`].
#[unsafe(no_mangle)]
#[deprecated]
pub extern "C" fn __h_errno_location() -> *mut c_int {
H_ERRNO.as_ptr()
}
#[unsafe(no_mangle)]
#[deprecated]
pub const extern "C" fn hstrerror(errcode: c_int) -> *const c_char {
match errcode {
H_UNSET => c"Resolver error unset",
HOST_NOT_FOUND => c"Unknown hostname",
NO_DATA => c"No address for hostname",
NO_RECOVERY => c"Unknown server error",
TRY_AGAIN => c"Hostname lookup failure",
_ => c"Unknown error",
}
.as_ptr()
}
/// Print error message associated with [`H_ERRNO`] to stderr.
///
/// # Arguments
/// * `prefix` - An optional prefix to prepend to the error message. May be null or an empty
/// (`""`) C string.
///
/// # Safety
/// Like [`crate::header::stdio::perror`], `prefix` should be a valid, NUL terminated C string if
/// used. The caller may safely call this function with a null pointer.
///
/// # Deprecation
/// [`H_ERRNO`], [`hstrerror`], [`herror`], and other functions are deprecated as of
/// POSIX.1-2001 and removed as of POSIX.1-2008. These functions are provided for backwards
/// compatibility but should not be used by new code.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[unsafe(no_mangle)]
#[deprecated]
pub extern "C" fn herror(prefix: *const c_char) {
let code = H_ERRNO.get();
// Safety: `hstrerror` handles every error code case and always returns a valid C string
let error = unsafe {
let msg_cstr = CStr::from_ptr(hstrerror(code));
str::from_utf8_unchecked(msg_cstr.to_bytes())
};
let mut writer = platform::FileWriter::new(2);
// Prefix is optional
match unsafe { CStr::from_nullable_ptr(prefix) }
.and_then(|prefix| str::from_utf8(prefix.to_bytes()).ok())
{
Some(prefix) if !prefix.is_empty() => writer
.write_fmt(format_args!("{prefix}: {error}\n"))
.unwrap(),
_ => writer.write_fmt(format_args!("{error}\n")).unwrap(),
}
}