Implement hostent handling
This commit is contained in:
parent
8082509da0
commit
d37ab45d68
|
@ -12,7 +12,7 @@ license = "GPL-3.0-or-later"
|
|||
strip = true
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
panic = "unwind" # We rely on this
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use {
|
||||
crate::helpers::{records_to_gaih_addr, records_to_hostent, set_if_valid},
|
||||
crate::helpers::{records_to_gaih_addr, records_to_hostent, set_if_valid, DnsResult},
|
||||
crate::nss_bindings::{gaih_addrtuple, nss_status, HErrno},
|
||||
crate::policy_checker::{PolicyChecker as _, POLICY_CHECKER},
|
||||
libc::{c_char, c_int, hostent, size_t},
|
||||
|
@ -29,15 +29,10 @@ pub struct Args {
|
|||
}
|
||||
|
||||
pub enum Result {
|
||||
V3(HostEnt),
|
||||
V3(*mut hostent),
|
||||
V4(*mut *mut gaih_addrtuple),
|
||||
}
|
||||
|
||||
pub struct HostEnt {
|
||||
pub host: *mut hostent,
|
||||
pub canonp: *mut *mut char,
|
||||
}
|
||||
|
||||
pub async unsafe fn with(args: &mut Args) -> nss_status {
|
||||
set_if_valid(args.errnop, errno::from_i32(0));
|
||||
set_if_valid(args.h_errnop, HErrno::Success);
|
||||
|
@ -119,14 +114,18 @@ unsafe fn prepare_response(
|
|||
let buf = std::slice::from_raw_parts_mut(args.buffer as *mut u8, args.buflen);
|
||||
let ret = match &mut args.result {
|
||||
Result::V3(hostent) => {
|
||||
if hostent.host.is_null() {
|
||||
if hostent.is_null() {
|
||||
set_if_valid(args.errnop, Errno::EINVAL);
|
||||
set_if_valid(args.h_errnop, HErrno::Internal);
|
||||
return nss_status::NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
|
||||
match records_to_hostent(lookup.into(), hostent, buf) {
|
||||
Ok(_) => nss_status::NSS_STATUS_SUCCESS,
|
||||
match records_to_hostent(lookup.into(), &mut **hostent, buf) {
|
||||
Ok(DnsResult::Found) => nss_status::NSS_STATUS_SUCCESS,
|
||||
Ok(DnsResult::NxDomain) => {
|
||||
set_if_valid(args.h_errnop, HErrno::HostNotFound);
|
||||
nss_status::NSS_STATUS_SUCCESS
|
||||
}
|
||||
Err(err) => {
|
||||
set_if_valid(
|
||||
args.errnop,
|
||||
|
|
162
src/helpers.rs
162
src/helpers.rs
|
@ -2,14 +2,14 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use {
|
||||
crate::gethostbyname::HostEnt,
|
||||
crate::nss_bindings::gaih_addrtuple,
|
||||
anyhow::{bail, Result},
|
||||
libc::{AF_INET, AF_INET6},
|
||||
libc::{c_char, hostent, AF_INET, AF_INET6},
|
||||
nix::errno::Errno,
|
||||
once_cell::sync::Lazy,
|
||||
std::ffi::CString,
|
||||
std::mem::{align_of, size_of},
|
||||
std::mem::{align_of, discriminant, size_of},
|
||||
trust_dns_proto::rr::resource::Record,
|
||||
trust_dns_proto::rr::RData,
|
||||
trust_dns_resolver::lookup::Lookup,
|
||||
};
|
||||
|
@ -34,22 +34,10 @@ pub unsafe fn records_to_gaih_addr(
|
|||
let mut ret = std::ptr::null_mut();
|
||||
let mut prev_link: *mut *mut gaih_addrtuple = std::ptr::null_mut();
|
||||
|
||||
let mut name_dest;
|
||||
for record in lookup.record_iter() {
|
||||
// First add the name to the buffer
|
||||
let name = CString::new(record.name().to_utf8())
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))?;
|
||||
|
||||
let offset = buf.as_ptr().align_offset(align_of::<libc::c_char>());
|
||||
let name_src = name.as_bytes_with_nul();
|
||||
let name_dest = buf.as_mut_ptr().add(offset);
|
||||
|
||||
let l = name_src.len();
|
||||
if buf.len() < offset + l {
|
||||
return Err(Errno::ERANGE.into());
|
||||
}
|
||||
|
||||
std::ptr::copy_nonoverlapping(name_src.as_ptr(), name_dest, l);
|
||||
buf = &mut buf[(offset + l)..];
|
||||
(buf, name_dest) = write_record_name_to_buf(record, buf)?;
|
||||
|
||||
// Then add the tuple with the address
|
||||
let offset = buf.as_ptr().align_offset(align_of::<gaih_addrtuple>());
|
||||
|
@ -60,7 +48,7 @@ pub unsafe fn records_to_gaih_addr(
|
|||
|
||||
let tuple = &mut *(buf.as_mut_ptr().add(offset) as *mut gaih_addrtuple);
|
||||
tuple.next = std::ptr::null_mut();
|
||||
tuple.name = name_dest as *mut i8;
|
||||
tuple.name = name_dest;
|
||||
tuple.scopeid = 0; // how to set tuple.scopeid ????
|
||||
set_if_valid(prev_link, &mut *tuple); // link from previous tuple to this tuple
|
||||
prev_link = &mut (*tuple).next;
|
||||
|
@ -88,22 +76,17 @@ pub unsafe fn records_to_gaih_addr(
|
|||
Ok(ret)
|
||||
}
|
||||
|
||||
pub enum DnsResult {
|
||||
NxDomain,
|
||||
Found,
|
||||
}
|
||||
|
||||
// TODO: refactor to be less ugly
|
||||
pub unsafe fn records_to_hostent(
|
||||
lookup: Lookup,
|
||||
_hostent: &mut HostEnt,
|
||||
mut _buf: &mut [u8],
|
||||
) -> std::io::Result<()> {
|
||||
|
||||
// char *h_name Official name of the host.
|
||||
// char **h_aliases A pointer to an array of pointers to
|
||||
// alternative host names, terminated by a
|
||||
// null pointer.
|
||||
// int h_addrtype Address type.
|
||||
// int h_length The length, in bytes, of the address.
|
||||
// char **h_addr_list A pointer to an array of pointers to network
|
||||
// addresses (in network byte order) for the host,
|
||||
// terminated by a null pointer.
|
||||
|
||||
host: &mut hostent,
|
||||
mut buf: &mut [u8],
|
||||
) -> std::io::Result<DnsResult> {
|
||||
// In C struct hostent:
|
||||
//
|
||||
// - for the type of queries we perform, we can assume h_aliases
|
||||
|
@ -112,11 +95,117 @@ pub unsafe fn records_to_hostent(
|
|||
// in the list. We pick the type of first result, and only
|
||||
// append addresses of the same type.
|
||||
|
||||
for record in lookup.record_iter() {
|
||||
todo!();
|
||||
let first_record = match lookup.record_iter().peekable().peek() {
|
||||
Some(record) => *record,
|
||||
None => return Ok(DnsResult::NxDomain),
|
||||
};
|
||||
|
||||
let first_rdata = match first_record.data() {
|
||||
Some(rdata) => rdata,
|
||||
None => return Err(Errno::ENODATA.into()),
|
||||
};
|
||||
|
||||
// First put the name in the buffer
|
||||
let name_dest;
|
||||
(buf, name_dest) = write_record_name_to_buf(first_record, buf)?;
|
||||
|
||||
// Then the empty aliases array (just a null pointer)
|
||||
let offset = buf.as_ptr().align_offset(align_of::<*mut c_char>());
|
||||
let l = size_of::<*mut c_char>();
|
||||
if buf.len() < offset + l {
|
||||
return Err(Errno::ERANGE.into());
|
||||
}
|
||||
let aliases_ptr = buf[offset..].as_mut_ptr().cast::<*mut c_char>();
|
||||
aliases_ptr.write(std::ptr::null_mut());
|
||||
buf = &mut buf[(offset + l)..];
|
||||
|
||||
// Then the address list
|
||||
let offset = buf.as_ptr().align_offset(align_of::<*mut c_char>());
|
||||
buf = &mut buf[offset..];
|
||||
|
||||
let records = lookup
|
||||
.record_iter()
|
||||
.flat_map(Record::data)
|
||||
.filter(|r| discriminant(*r) == discriminant(first_rdata));
|
||||
|
||||
let mut addresses = vec![];
|
||||
for record in records {
|
||||
let offset = buf.as_ptr().align_offset(align_of::<*mut c_char>());
|
||||
buf = &mut buf[offset..];
|
||||
addresses.push(buf.as_ptr());
|
||||
|
||||
let l = match record {
|
||||
RData::A(addr) => {
|
||||
let octets = addr.octets();
|
||||
let l = octets.len();
|
||||
if buf.len() < l {
|
||||
return Err(Errno::ERANGE.into());
|
||||
}
|
||||
buf[..l].copy_from_slice(&octets);
|
||||
l
|
||||
}
|
||||
RData::AAAA(addr) => {
|
||||
let octets = addr.octets();
|
||||
let l = octets.len();
|
||||
if buf.len() < l {
|
||||
return Err(Errno::ERANGE.into());
|
||||
}
|
||||
buf[..l].copy_from_slice(&octets);
|
||||
l
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
buf = &mut buf[l..];
|
||||
}
|
||||
|
||||
Ok(())
|
||||
addresses.push(std::ptr::null_mut());
|
||||
|
||||
let offset = buf.as_ptr().align_offset(align_of::<*mut *mut c_char>());
|
||||
let l = size_of::<*mut c_char>() * addresses.len();
|
||||
if buf.len() < offset + l {
|
||||
return Err(Errno::ERANGE.into());
|
||||
}
|
||||
let addr_list = buf[offset..].as_mut_ptr().cast::<*mut c_char>();
|
||||
std::ptr::copy_nonoverlapping(addresses.as_ptr() as *const *mut c_char, addr_list, l);
|
||||
|
||||
// Finally, populate the hostent structure
|
||||
host.h_name = name_dest;
|
||||
host.h_aliases = aliases_ptr;
|
||||
host.h_addr_list = addr_list;
|
||||
match first_rdata {
|
||||
RData::A(_) => {
|
||||
host.h_addrtype = AF_INET;
|
||||
host.h_length = 4;
|
||||
}
|
||||
RData::AAAA(_) => {
|
||||
host.h_addrtype = AF_INET6;
|
||||
host.h_length = 16;
|
||||
}
|
||||
_ => return Err(Errno::EBADMSG.into()),
|
||||
}
|
||||
|
||||
Ok(DnsResult::Found)
|
||||
}
|
||||
|
||||
unsafe fn write_record_name_to_buf<'a>(
|
||||
record: &Record,
|
||||
buf: &'a mut [u8],
|
||||
) -> std::io::Result<(&'a mut [u8], *mut c_char)> {
|
||||
use std::io::{Error, ErrorKind};
|
||||
let name = CString::new(record.name().to_utf8())
|
||||
.map_err(|e| Error::new(ErrorKind::InvalidData, e.to_string()))?;
|
||||
|
||||
let offset = buf.as_ptr().align_offset(align_of::<libc::c_char>());
|
||||
let name_src = name.as_bytes_with_nul();
|
||||
let l = name_src.len();
|
||||
if buf.len() < offset + l {
|
||||
return Err(Errno::ERANGE.into());
|
||||
}
|
||||
|
||||
let name_dest = buf.as_mut_ptr().add(offset);
|
||||
std::ptr::copy_nonoverlapping(name_src.as_ptr(), name_dest, l);
|
||||
Ok((&mut buf[(offset + l)..], name_dest as *mut c_char))
|
||||
}
|
||||
|
||||
pub fn set_if_valid<T>(ptr: *mut T, val: T) {
|
||||
|
@ -129,6 +218,9 @@ pub fn block_on<F>(f: F) -> Result<F::Output>
|
|||
where
|
||||
F: std::future::Future,
|
||||
{
|
||||
// TODO: use std::panic::catch_unwind() to be resilient
|
||||
// to OOM problems
|
||||
|
||||
use std::ops::Deref;
|
||||
match RUNTIME.deref() {
|
||||
Ok(rt) => Ok(rt.block_on(async { f.await })),
|
||||
|
|
|
@ -57,12 +57,10 @@ pub unsafe extern "C" fn _nss_malcontent_gethostbyname3_r(
|
|||
ttlp: *mut i32,
|
||||
canonp: *mut *mut char,
|
||||
) -> nss_status {
|
||||
let result = gethostbyname::HostEnt { host, canonp };
|
||||
|
||||
let mut args = gethostbyname::Args {
|
||||
name,
|
||||
family: af,
|
||||
result: gethostbyname::Result::V3(result),
|
||||
result: gethostbyname::Result::V3(host),
|
||||
buffer,
|
||||
buflen,
|
||||
errnop,
|
||||
|
@ -71,7 +69,12 @@ pub unsafe extern "C" fn _nss_malcontent_gethostbyname3_r(
|
|||
};
|
||||
|
||||
match block_on(async { gethostbyname::with(&mut args).await }) {
|
||||
Ok(status) => status,
|
||||
Ok(status) => {
|
||||
if !host.is_null() {
|
||||
set_if_valid(canonp, (*host).h_name as *mut char);
|
||||
}
|
||||
status
|
||||
}
|
||||
Err(runtime_error) => {
|
||||
log::error!("gethostbyname3_r: {}", runtime_error);
|
||||
set_if_valid(h_errnop, HErrno::Internal);
|
||||
|
|
|
@ -66,6 +66,41 @@ fork_test! {
|
|||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn getaddrinfo_resolution() -> Result<()> {
|
||||
common::setup()?;
|
||||
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
||||
let _dbus = common::mock_dbus(HashMap::from([(
|
||||
getuid(),
|
||||
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
|
||||
)]));
|
||||
|
||||
let hostname = std::ffi::CString::new("gnome.org").unwrap();
|
||||
unsafe {
|
||||
let mut addr = std::ptr::null_mut();
|
||||
let getaddrinfo_status = getaddrinfo(
|
||||
hostname.as_ptr(),
|
||||
std::ptr::null(),
|
||||
std::ptr::null(),
|
||||
&mut addr,
|
||||
);
|
||||
|
||||
let error = std::ffi::CStr::from_ptr(gai_strerror(getaddrinfo_status));
|
||||
assert_eq!(
|
||||
getaddrinfo_status,
|
||||
Eai::Success.0,
|
||||
"Should have gotten an hostname, instead got {}",
|
||||
error.to_str().unwrap()
|
||||
);
|
||||
freeaddrinfo(addr);
|
||||
};
|
||||
|
||||
//timeout(Duration::from_secs(1), dbus).await??
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn wikipedia_is_unrestricted() -> Result<()> {
|
||||
common::setup()?;
|
||||
|
|
Loading…
Reference in New Issue