Set h_errno for gethostbyname/addr

This commit is contained in:
Josh Megnauth
2024-11-02 12:21:47 +00:00
committed by Jeremy Soller
parent 2dad215394
commit bf1ea4e799
3 changed files with 146 additions and 10 deletions
+9
View File
@@ -1,6 +1,15 @@
#ifndef _BITS_NETDB_H
#define _BITS_NETDB_H
#ifdef __cplusplus
extern "C" {
#endif
#define h_errno (*__h_errno_location())
# define h_addr h_addr_list[0] /* Address, for backward compatibility.*/
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* _BITS_NETDB_H */
+108 -10
View File
@@ -3,6 +3,8 @@
mod dns;
use core::{
cell::Cell,
fmt::Write,
mem, ptr, slice,
str::{self, FromStr},
};
@@ -132,14 +134,18 @@ pub static mut NET_ADDR: Option<u32> = None;
static mut N_POS: usize = 0;
static mut NET_STAYOPEN: c_int = 0;
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static mut h_errno: 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(),
@@ -188,12 +194,44 @@ pub unsafe extern "C" fn endservent() {
SERVDB = 0;
}
/// 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.
#[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 = *(v as *mut in_addr);
// check the hosts file first
@@ -246,24 +284,39 @@ pub unsafe extern "C" fn gethostbyaddr(
// `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) => {
// TODO: Set h_errno instead to match spec
platform::ERRNO.set(EIO);
H_ERRNO.set(HOST_NOT_FOUND);
ptr::null_mut()
}
Err(e) => {
platform::ERRNO.set(e);
// TODO: Better error separation in lookup_addr
H_ERRNO.set(NO_RECOVERY);
ptr::null_mut()
}
}
}
/// 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.
#[no_mangle]
#[deprecated]
pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut hostent {
let name_cstr =
CStr::from_nullable_ptr(name).expect("gethostbyname() called with a NULL pointer");
let Ok(name_str) = str::from_utf8(name_cstr.to_bytes()) else {
// TODO Set h_errno instead of errno (spec)
platform::ERRNO.set(EINVAL);
H_ERRNO.set(NO_RECOVERY);
return ptr::null_mut();
};
@@ -321,14 +374,14 @@ pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut hostent {
let mut host = match lookup_host(name_str) {
Ok(lookuphost) => lookuphost,
Err(e) => {
platform::ERRNO.set(e);
H_ERRNO.set(NO_RECOVERY);
return ptr::null_mut();
}
};
let host_addr = match host.next() {
Some(result) => result,
None => {
platform::ERRNO.set(ENOENT);
H_ERRNO.set(HOST_NOT_FOUND);
return ptr::null_mut();
}
};
@@ -902,9 +955,18 @@ pub extern "C" fn gai_strerror(errcode: c_int) -> *const c_char {
.as_ptr()
}
/// Provide a pointer to relibc's internal [`H_ERRNO`].
#[no_mangle]
#[deprecated]
pub extern "C" fn __h_errno_location() -> *mut c_int {
H_ERRNO.as_ptr()
}
#[no_mangle]
#[deprecated]
pub extern "C" fn hstrerror(errcode: c_int) -> *const c_char {
match errcode {
H_UNSET => c_str!("Resolver error unset"),
HOST_NOT_FOUND => c_str!("Unknown hostname"),
NO_DATA => c_str!("No address for hostname"),
NO_RECOVERY => c_str!("Unknown server error"),
@@ -913,3 +975,39 @@ pub extern "C" fn hstrerror(errcode: c_int) -> *const c_char {
}
.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.
#[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(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(),
}
}
+29
View File
@@ -26,6 +26,7 @@
#include <netdb.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
@@ -37,6 +38,8 @@
#include "test_helpers.h"
#define ADDR_SIZE sizeof(uint8_t) * 4
int error_count;
static void
output_servent (const char *call, struct servent *sptr)
@@ -229,12 +232,38 @@ test_network (void)
while (nptr != NULL);
setnetent (0);
}
static void
test_h_errno (void) {
const uint8_t addr[] = {0, 0, 0, 0};
struct hostent *host = gethostbyaddr(addr, ADDR_SIZE, AF_INET);
if (host) {
++error_count;
}
int err = h_errno;
herror("Prefix test");
if (err != HOST_NOT_FOUND) {
++error_count;
}
host = gethostbyname("");
if (host) {
++error_count;
}
err = h_errno;
herror("");
if (err != HOST_NOT_FOUND) {
++error_count;
}
}
static int
do_test (void)
{
/*
setdb ("db");
*/
test_h_errno ();
test_hosts ();
test_network ();
test_protocols ();