Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ build = "build.rs"
default = ["feat_common_core"]
uudoc = []

feat_common_core = [
"hostname",
]
feat_common_core = ["hostname", "dnsdomainname"]

[workspace.dependencies]
uucore = "0.1.0"
Expand Down Expand Up @@ -66,6 +64,7 @@ textwrap = { workspace = true }

#
hostname = { optional = true, version = "0.0.1", package = "uu_hostname", path = "src/uu/hostname" }
dnsdomainname = { optional = true, version = "0.0.1", package = "uu_dnsdomainname", path = "src/uu/dnsdomainname" }

[dev-dependencies]
pretty_assertions = "1.4.0"
Expand All @@ -92,6 +91,10 @@ phf_codegen = { workspace = true }
name = "hostname"
path = "src/bin/hostname.rs"

[[bin]]
name = "dnsdomainname"
path = "src/bin/hostname.rs"

[[bin]]
name = "uudoc"
path = "src/bin/uudoc.rs"
Expand Down
15 changes: 15 additions & 0 deletions src/bin/dnsdomainname.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This file is part of the uutils hostname package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use std::env;

fn main() {
let args: Vec<String> = env::args().collect();
let exit_code = uu_dnsdomainname::uumain(args);
std::process::exit(match exit_code {
Ok(()) => 0,
Err(_) => 1,
});
}
34 changes: 34 additions & 0 deletions src/uu/dnsdomainname/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# dnsdomainname (uutils)
# * see the repository LICENSE, README, and CONTRIBUTING files for more information

[package]
name = "uu_dnsdomainname"
version = "0.0.1"
authors = ["uutils developers"]
license = "MIT"
description = "dnsdomainname ~ implemented as universal (cross-platform) utils, written in Rust"

homepage = "https://github.com/uutils/hostname"
repository = "https://github.com/uutils/hostname"
keywords = [
"dnsdomainname",
"hostname",
"uutils",
"cross-platform",
"cli",
"utility",
]
categories = ["command-line-utilities"]
edition = "2024"

[lib]
path = "src/dnsdomainname.rs"

[dependencies]
clap = { workspace = true }
uucore = { workspace = true }
libc = { workspace = true }
errno = { workspace = true }

[target.'cfg(windows)'.dependencies]
windows-sys = { workspace = true }
7 changes: 7 additions & 0 deletions src/uu/dnsdomainname/dnsdomainname.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# dnsdomainname

```
dnsdomainname [-v]
```

Display the DNS domain name.
46 changes: 46 additions & 0 deletions src/uu/dnsdomainname/src/dnsdomainname.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// This file is part of the uutils hostname package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

mod net;
mod print;

use crate::print::PrintHostName;
use clap::{Arg, ArgAction, Command, crate_version};
use std::io;
use uucore::{error::UResult, format_usage};

const ABOUT: &str = "Display the DNS domain name.";
const USAGE: &str = "dnsdomainname [-v]";

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().try_get_matches_from(args)?;

let _net_lib_guard = net::LibraryGuard::load()?;

let _verbose = matches.get_flag("verbose");
// Note: verbose flag is accepted but doesn't change behavior in GNU implementation

let domain_printer = print::DomainHostName;
let mut stdout = io::stdout();

domain_printer.print_host_name(&mut stdout)
}

#[must_use]
pub fn uu_app() -> Command {
Command::new(uucore::util_name())
.version(crate_version!())
.about(ABOUT)
.override_usage(format_usage(USAGE))
.infer_long_args(true)
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.action(ArgAction::SetTrue)
.help("Be verbose and tell what's going on"),
)
}
1 change: 1 addition & 0 deletions src/uu/dnsdomainname/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uucore::bin!(uu_dnsdomainname);
11 changes: 11 additions & 0 deletions src/uu/dnsdomainname/src/net.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#[cfg(not(target_family = "windows"))]
mod unix;
#[cfg(target_family = "windows")]
mod windows;

#[cfg(not(target_family = "windows"))]
pub(crate) use unix::*;
#[cfg(target_family = "windows")]
pub(crate) use windows::*;

pub(crate) struct LibraryGuard;
84 changes: 84 additions & 0 deletions src/uu/dnsdomainname/src/net/unix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// This file is part of the uutils hostname package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use std::ffi::{CStr, CString, c_int};
use std::ptr;
use std::ptr::NonNull;

impl crate::net::LibraryGuard {
pub(crate) fn load() -> std::io::Result<Self> {
Ok(Self)
}
}

fn max_host_name_size() -> usize {
const _POSIX_HOST_NAME_MAX: usize = 255;

usize::try_from(unsafe { libc::sysconf(libc::_SC_HOST_NAME_MAX) })
.unwrap_or(_POSIX_HOST_NAME_MAX)
.max(_POSIX_HOST_NAME_MAX)
.saturating_add(1)
}

pub(crate) fn host_name() -> std::io::Result<CString> {
let mut buffer: Vec<u8> = vec![0_u8; max_host_name_size()];
loop {
errno::set_errno(errno::Errno(0));

if unsafe { libc::gethostname(buffer.as_mut_ptr().cast(), buffer.len()) } == -1 {
let err = std::io::Error::last_os_error();
if err.raw_os_error() != Some(libc::ENAMETOOLONG) {
break Err(err);
}
// else an error happened because a bigger buffer is needed.
} else if let Some(index) = buffer.iter().position(|&b| b == 0_u8) {
buffer.truncate(index + 1);
break Ok(unsafe { CString::from_vec_with_nul_unchecked(buffer) });
}
// else truncation happened because a bigger buffer is needed.

buffer.resize_with(buffer.len() + 4096, Default::default);
}
}

#[repr(transparent)]
pub(crate) struct AddressInfo(NonNull<libc::addrinfo>);

impl AddressInfo {
pub(crate) fn new(
host_name: &CStr,
hint_family: c_int,
hint_socktype: c_int,
hint_protocol: c_int,
hint_flags: c_int,
) -> std::io::Result<Self> {
let mut c_hints: libc::addrinfo = unsafe { std::mem::zeroed() };
c_hints.ai_family = hint_family;
c_hints.ai_socktype = hint_socktype;
c_hints.ai_protocol = hint_protocol;
c_hints.ai_flags = hint_flags;

let mut ptr: *mut libc::addrinfo = ptr::null_mut();

let r = unsafe { libc::getaddrinfo(host_name.as_ptr(), ptr::null(), &c_hints, &mut ptr) };
if r == 0 {
NonNull::new(ptr)
.map(Self)
.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::InvalidData))
} else {
Err(std::io::Error::from_raw_os_error(r))
}
}

pub(crate) fn first(&self) -> &libc::addrinfo {
unsafe { self.0.as_ref() }
}
}

impl Drop for AddressInfo {
fn drop(&mut self) {
unsafe { libc::freeaddrinfo(self.0.as_ptr()) }
}
}
42 changes: 42 additions & 0 deletions src/uu/dnsdomainname/src/net/windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// This file is part of the uutils hostname package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use std::ffi::CString;

impl crate::net::LibraryGuard {
pub(crate) fn load() -> std::io::Result<Self> {
Ok(Self)
}
}

pub(crate) fn host_name() -> std::io::Result<CString> {
// Windows implementation would go here
// For now, return an error since we're focusing on Unix
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"Windows not yet supported for dnsdomainname",
))
}

pub(crate) struct AddressInfo;

impl AddressInfo {
pub(crate) fn new(
_host_name: &std::ffi::CStr,
_hint_family: std::ffi::c_int,
_hint_socktype: std::ffi::c_int,
_hint_protocol: std::ffi::c_int,
_hint_flags: std::ffi::c_int,
) -> std::io::Result<Self> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"Windows not yet supported for dnsdomainname",
))
}

pub(crate) fn first(&self) -> &libc::addrinfo {
panic!("Windows not yet supported for dnsdomainname")
}
}
12 changes: 12 additions & 0 deletions src/uu/dnsdomainname/src/print.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#[cfg(not(target_family = "windows"))]
mod unix;
#[cfg(target_family = "windows")]
mod windows;

use uucore::error::UResult;

pub(crate) trait PrintHostName {
fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()>;
}

pub(crate) struct DomainHostName;
33 changes: 33 additions & 0 deletions src/uu/dnsdomainname/src/print/unix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// This file is part of the uutils hostname package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use std::ffi::CStr;
use libc::{AF_UNSPEC, AI_CANONNAME, SOCK_DGRAM};
use uucore::error::UResult;

use crate::net::{AddressInfo, host_name};
use crate::print::{DomainHostName, PrintHostName};

impl PrintHostName for DomainHostName {
fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> {
let address_info = AddressInfo::new(&host_name()?, AF_UNSPEC, SOCK_DGRAM, 0, AI_CANONNAME)?;

let canonical_name = address_info.first().ai_canonname;
if canonical_name.is_null() {
return Ok(()); // No canonical name set.
};

let Some(domain_name) = unsafe { CStr::from_ptr(canonical_name) }
.to_bytes()
.splitn(2, |&byte| byte == b'.')
.nth(1)
else {
return Ok(()); // Canonical name contains zero dots.
};

out.write_all(domain_name)?;
out.write_all(b"\n").map_err(From::from)
}
}
17 changes: 17 additions & 0 deletions src/uu/dnsdomainname/src/print/windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// This file is part of the uutils hostname package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use uucore::error::UResult;

use crate::print::{DomainHostName, PrintHostName};

impl PrintHostName for DomainHostName {
fn print_host_name(&self, _out: &mut dyn std::io::Write) -> UResult<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"Windows not yet supported for dnsdomainname",
).into())
}
}
Loading
Loading