use crate::ported::modules::tcp_h::{tcp_session, tcp_sockaddr, ZTCP_LISTEN, ZTCP_INBOUND, ZTCP_ZFTP};
use crate::ported::utils::{addmodulefd, errflag, movefd, redup, zerrnam, zwarn, zwarnnam};
use crate::ported::zsh_h::{OPT_ARG, OPT_ISSET, module, FDT_MODULE, options, features};
use std::net::ToSocketAddrs;
use std::os::unix::io::RawFd;
use std::sync::{Mutex, OnceLock};
use crate::ported::params::setiparam;
impl Default for tcp_sockaddr {
fn default() -> Self {
Self {
a: unsafe { std::mem::zeroed() },
}
}
}
impl Default for tcp_session {
fn default() -> Self {
Self {
fd: -1,
sock: tcp_sockaddr::default(),
peer: tcp_sockaddr::default(),
flags: 0,
}
}
}
thread_local! {
static ZTCP_SESSIONS: std::cell::RefCell<Vec<tcp_session>> = const {
std::cell::RefCell::new(Vec::new())
};
}
pub fn zsh_inet_ntop(af: i32, addr_bytes: &[u8]) -> Option<String> {
if af == libc::AF_INET && addr_bytes.len() >= 4 {
let v4 =
std::net::Ipv4Addr::new(addr_bytes[0], addr_bytes[1], addr_bytes[2], addr_bytes[3]);
Some(v4.to_string())
} else if af == libc::AF_INET6 && addr_bytes.len() >= 16 {
let mut octets = [0u8; 16];
octets.copy_from_slice(&addr_bytes[..16]);
Some(std::net::Ipv6Addr::from(octets).to_string())
} else {
None }
}
pub fn zsh_inet_pton(af: i32, src: &str, dst: &mut [u8]) -> i32 {
if af == libc::AF_INET {
match src.parse::<std::net::Ipv4Addr>() {
Ok(v4) if dst.len() >= 4 => {
dst[..4].copy_from_slice(&v4.octets());
1
}
_ => 0,
}
} else if af == libc::AF_INET6 {
match src.parse::<std::net::Ipv6Addr>() {
Ok(v6) if dst.len() >= 16 => {
dst[..16].copy_from_slice(&v6.octets());
1
}
_ => 0,
}
} else {
-1
}
}
pub fn zsh_gethostbyname2(name: &str, af: i32) -> Vec<[u8; 4]> {
let mut out = Vec::new();
if af == libc::AF_INET {
if let Ok(iter) = format!("{}:0", name).to_socket_addrs() {
for sa in iter {
if let std::net::SocketAddr::V4(v4) = sa {
out.push(v4.ip().octets());
}
}
}
}
out
}
pub fn zsh_getipnodebyname(name: &str, af: i32) -> Vec<[u8; 4]> {
zsh_gethostbyname2(name, af) }
pub fn freehostent() { }
pub fn zts_alloc(ztflags: i32) -> usize {
ZTCP_SESSIONS.with(|s| {
let mut sessions = s.borrow_mut();
let idx = sessions.len();
sessions.push(tcp_session {
fd: -1, sock: tcp_sockaddr::default(),
peer: tcp_sockaddr::default(),
flags: ztflags, });
idx })
}
pub fn tcp_socket(domain: i32, ty: i32, protocol: i32, ztflags: i32) -> TcpSessionHandle {
let idx = zts_alloc(ztflags); let fd = unsafe { libc::socket(domain, ty, protocol) }; sess_with(idx, |s| {
s.fd = fd;
}); if fd >= 0 {
addmodulefd(fd, FDT_MODULE);
}
Some(idx) }
pub fn ztcp_free_session(sess: usize) -> i32 {
0 }
pub fn zts_delete(fd: i32) -> i32 {
ZTCP_SESSIONS.with(|s| {
let mut sessions = s.borrow_mut();
let pos = sessions.iter().position(|sess| sess.fd == fd); match pos {
Some(i) => {
sessions.remove(i);
0 }
None => 1, }
})
}
pub fn zts_byfd(fd: RawFd) -> TcpSessionHandle {
ZTCP_SESSIONS.with(|s| {
s.borrow().iter().position(|sess| sess.fd == fd) })
}
pub fn tcp_cleanup() {
ZTCP_SESSIONS.with(|s| {
let mut sessions = s.borrow_mut();
for sess in sessions.drain(..) {
if sess.fd >= 0 {
unsafe {
libc::close(sess.fd);
}
}
}
});
}
pub fn tcp_close(sess: TcpSessionHandle) -> i32 {
if let Some(idx) = sess {
let fd = sess_get(idx, |s| s.fd);
let mut err = -1;
if fd != -1 {
err = unsafe { libc::close(fd) }; if err != 0 {
zwarn(&format!(
"connection close failed: {}",
std::io::Error::last_os_error()
));
}
}
let _ = zts_delete(fd);
return err; }
0 }
pub fn tcp_connect(sess: TcpSessionHandle, addr: &[u8; 4], d_port: u16) -> i32 {
let idx = match sess {
Some(i) => i,
None => return -1,
};
let fd = sess_get(idx, |s| s.fd);
let mut peer: libc::sockaddr_in = unsafe { std::mem::zeroed() };
peer.sin_family = libc::AF_INET as _; peer.sin_port = d_port; peer.sin_addr.s_addr = u32::from_be_bytes(*addr).to_be(); sess_with(idx, |s| {
s.peer.in_ = peer;
});
unsafe {
libc::connect(
fd, &peer as *const _ as *const libc::sockaddr,
std::mem::size_of::<libc::sockaddr_in>() as libc::socklen_t,
)
}
}
#[allow(non_snake_case)]
pub fn bin_ztcp(
nam: &str,
args: &[String], ops: &options,
_func: i32,
) -> i32 {
let mut err: i32 = 1; let destport: u16; let mut force = 0i32; let mut verbose = 0i32; let mut test = 0i32; let mut targetfd: i32 = 0; let mut len: libc::socklen_t; let desthost: String; let mut sess: TcpSessionHandle = None;
if OPT_ISSET(ops, b'f') {
force = 1;
} if OPT_ISSET(ops, b'v') {
verbose = 1;
} if OPT_ISSET(ops, b't') {
test = 1;
}
if OPT_ISSET(ops, b'd') {
let darg = OPT_ARG(ops, b'd').unwrap_or("");
targetfd = darg.parse::<i32>().unwrap_or(0); if targetfd == 0 {
zwarnnam(nam, &format!("{} is an invalid argument to -d", darg));
return 1; }
}
if OPT_ISSET(ops, b'c') {
if args.is_empty() {
tcp_cleanup(); } else {
targetfd = args[0].parse::<i32>().unwrap_or(0); sess = zts_byfd(targetfd); if targetfd == 0 {
zwarnnam(nam, &format!("{} is an invalid argument to -c", args[0]));
return 1; }
if let Some(sidx) = sess {
let flags = sess_get(sidx, |s| s.flags);
if (flags & ZTCP_ZFTP) != 0 && force == 0 {
zwarnnam(nam, "use -f to force closure of a zftp control connection");
return 1; }
tcp_close(sess); return 0; } else {
zwarnnam(nam, &format!("fd {} not found in tcp table", args[0]));
return 1; }
}
} else if OPT_ISSET(ops, b'l') {
let lport: u16; if args.is_empty() {
zwarnnam(nam, "-l requires an argument");
return 1; }
let srv = {
let cname = std::ffi::CString::new(args[0].as_str()).ok();
let cproto = std::ffi::CString::new("tcp").unwrap();
cname.and_then(|c| {
let p = unsafe { libc::getservbyname(c.as_ptr(), cproto.as_ptr()) };
if p.is_null() {
None
} else {
Some(unsafe { (*p).s_port } as u16)
}
})
};
lport = match srv {
Some(p) => p, None => (args[0].parse::<u16>().unwrap_or(0)).to_be(), };
if lport == 0 {
zwarnnam(nam, "bad service name or port number");
return 1; }
sess = tcp_socket(libc::PF_INET, libc::SOCK_STREAM, 0, ZTCP_LISTEN); if sess.is_none() {
zwarnnam(nam, "unable to allocate a TCP session slot");
return 1; }
let sidx = sess.unwrap();
let one: i32 = 1;
let fd = sess_get(sidx, |s| s.fd);
unsafe {
libc::setsockopt(
fd,
libc::SOL_SOCKET,
libc::SO_OOBINLINE,
&one as *const _ as *const libc::c_void,
std::mem::size_of::<i32>() as libc::socklen_t,
);
}
let mut sin: libc::sockaddr_in = unsafe { std::mem::zeroed() };
sin.sin_family = libc::AF_INET as _; sin.sin_port = lport; sin.sin_addr.s_addr = 0u32.to_be(); sess_with(sidx, |s| {
s.sock.in_ = sin;
});
let r = unsafe {
libc::bind(
fd, &sin as *const _ as *const libc::sockaddr,
std::mem::size_of::<libc::sockaddr_in>() as libc::socklen_t,
)
};
if r != 0 {
zwarnnam(
nam,
&format!(
"could not bind to port {}: {}", u16::from_be(lport),
std::io::Error::last_os_error()
),
);
tcp_close(sess); return 1; }
if unsafe { libc::listen(fd, 1) } != 0 {
zwarnnam(
nam,
&format!(
"could not listen on socket: {}", std::io::Error::last_os_error()
),
);
tcp_close(sess); return 1; }
if targetfd != 0 {
let nfd = redup(fd, targetfd); sess_with(sidx, |s| {
s.fd = nfd;
});
} else {
let nfd = movefd(fd); sess_with(sidx, |s| {
s.fd = nfd;
});
}
let nfd = sess_get(sidx, |s| s.fd);
if nfd == -1 {
zwarnnam(
nam,
&format!(
"cannot duplicate fd {}: {}",
nfd, std::io::Error::last_os_error()
),
);
tcp_close(sess); return 1; }
setiparam("REPLY", nfd as i64); if verbose != 0 {
println!(
"{} listener is on fd {}", u16::from_be(lport),
nfd
);
}
return 0; } else if OPT_ISSET(ops, b'a') {
let lfd: i32;
let rfd: i32;
if args.is_empty() {
zwarnnam(nam, "-a requires an argument");
return 1; }
lfd = args[0].parse::<i32>().unwrap_or(0); if lfd == 0 {
zwarnnam(nam, "invalid numerical argument");
return 1; }
sess = zts_byfd(lfd); if sess.is_none() {
zwarnnam(
nam,
&format!("fd {} is not registered as a tcp connection", args[0]),
);
return 1; }
let flags = sess_get(sess.unwrap(), |s| s.flags);
if (flags & ZTCP_LISTEN) == 0 {
zwarnnam(nam, "tcp connection not a listener");
return 1; }
if test != 0 {
let mut pfd = libc::pollfd {
fd: lfd,
events: libc::POLLIN,
revents: 0,
};
let ret = unsafe { libc::poll(&mut pfd, 1, 0) }; if ret == 0 {
return 1;
}
else if ret == -1 {
zwarnnam(
nam,
&format!(
"poll error: {}", std::io::Error::last_os_error()
),
);
return 1; }
}
sess = Some(zts_alloc(ZTCP_INBOUND)); let sidx = sess.unwrap();
let mut peer: libc::sockaddr_in = unsafe { std::mem::zeroed() };
len = std::mem::size_of::<libc::sockaddr_in>() as libc::socklen_t; loop {
let r = unsafe {
libc::accept(
lfd,
&mut peer as *mut _ as *mut libc::sockaddr,
&mut len as *mut _,
)
};
if r >= 0
|| std::io::Error::last_os_error().raw_os_error() != Some(libc::EINTR)
|| errflag.load(std::sync::atomic::Ordering::Relaxed) != 0
{
rfd = r;
break;
} else {}
}
sess_with(sidx, |s| {
s.peer.in_ = peer;
});
if rfd == -1 {
zwarnnam(
nam,
&format!(
"could not accept connection: {}", std::io::Error::last_os_error()
),
);
tcp_close(sess); return 1; }
addmodulefd(rfd, FDT_MODULE); if targetfd != 0 {
let nfd = redup(rfd, targetfd); sess_with(sidx, |s| {
s.fd = nfd;
});
if nfd < 0 {
zerrnam(
nam,
&format!(
"could not duplicate socket fd to {}: {}",
targetfd,
std::io::Error::last_os_error()
),
);
return 1; }
} else {
sess_with(sidx, |s| {
s.fd = rfd;
}); }
let nfd = sess_get(sidx, |s| s.fd);
setiparam("REPLY", nfd as i64); if verbose != 0 {
println!("{} is on fd {}", u16::from_be(peer.sin_port), nfd); }
} else {
if args.is_empty() {
ZTCP_SESSIONS.with(|s| {
for sess in s.borrow().iter() {
if sess.fd != -1 {
let lname = {
let b = u32::from_be(unsafe { sess.sock.in_.sin_addr.s_addr })
.to_be_bytes();
format!("{}.{}.{}.{}", b[0], b[1], b[2], b[3])
};
let pname = {
let b = u32::from_be(unsafe { sess.peer.in_.sin_addr.s_addr })
.to_be_bytes();
format!("{}.{}.{}.{}", b[0], b[1], b[2], b[3])
};
let lport = u16::from_be(unsafe { sess.sock.in_.sin_port });
let pport = u16::from_be(unsafe { sess.peer.in_.sin_port });
if OPT_ISSET(ops, b'L') {
let schar = if (sess.flags & ZTCP_ZFTP) != 0 {
'Z'
}
else if (sess.flags & ZTCP_LISTEN) != 0 {
'L'
}
else if (sess.flags & ZTCP_INBOUND) != 0 {
'I'
}
else {
'O'
}; println!(
"{} {} {} {} {} {}", sess.fd, schar, lname, lport, pname, pport
);
} else {
let arrow = if (sess.flags & ZTCP_LISTEN) != 0 {
"-<"
} else if (sess.flags & ZTCP_INBOUND) != 0 {
"<-"
} else {
"->"
};
let zftp = if (sess.flags & ZTCP_ZFTP) != 0 {
" ZFTP"
} else {
""
};
println!(
"{}:{} {} {}:{} is on fd {}{}", lname, lport, arrow, pname, pport, sess.fd, zftp
);
}
}
}
});
return 0; } else if args.len() == 1 {
destport = (23u16).to_be(); } else {
let srv = {
let cname = std::ffi::CString::new(args[1].as_str()).ok();
let cproto = std::ffi::CString::new("tcp").unwrap();
cname.and_then(|c| {
let p = unsafe { libc::getservbyname(c.as_ptr(), cproto.as_ptr()) };
if p.is_null() {
None
} else {
Some(unsafe { (*p).s_port } as u16)
}
})
};
destport = match srv {
Some(p) => p, None => (args[1].parse::<u16>().unwrap_or(0)).to_be(), };
}
desthost = args[0].clone(); let zthost = zsh_getipnodebyname(&desthost, libc::AF_INET); if zthost.is_empty() {
zwarnnam(nam, &format!("host resolution failure: {}", desthost));
return 1; }
sess = tcp_socket(libc::PF_INET, libc::SOCK_STREAM, 0, 0); if sess.is_none() {
zwarnnam(nam, "unable to allocate a TCP session slot");
return 1; }
let sidx = sess.unwrap();
let one: i32 = 1;
let fd = sess_get(sidx, |s| s.fd);
unsafe {
libc::setsockopt(
fd,
libc::SOL_SOCKET,
libc::SO_OOBINLINE,
&one as *const _ as *const libc::c_void,
std::mem::size_of::<i32>() as libc::socklen_t,
);
}
if fd < 0 {
zwarnnam(
nam,
&format!(
"socket creation failed: {}", std::io::Error::last_os_error()
),
);
zts_delete(fd); return 1; }
for addr in &zthost {
loop {
err = tcp_connect(sess, addr, destport); if err == 0
|| std::io::Error::last_os_error().raw_os_error() != Some(libc::EINTR)
|| errflag.load(std::sync::atomic::Ordering::Relaxed) != 0
{
break;
}
}
if err == 0 {
break;
}
}
if err != 0 {
zwarnnam(
nam,
&format!(
"connection failed: {}", std::io::Error::last_os_error()
),
);
tcp_close(sess); return 1; } else {
if targetfd != 0 {
let nfd = redup(fd, targetfd); sess_with(sidx, |s| {
s.fd = nfd;
});
if nfd < 0 {
zerrnam(
nam,
&format!(
"could not duplicate socket fd to {}: {}",
targetfd,
std::io::Error::last_os_error()
),
); tcp_close(sess); return 1; }
}
let nfd = sess_get(sidx, |s| s.fd);
setiparam("REPLY", nfd as i64); if verbose != 0 {
println!(
"{}:{} is now on fd {}", desthost,
u16::from_be(destport),
nfd
);
}
}
}
let _ = len; 0 }
#[allow(unused_variables)]
pub fn setup_(m: *const module) -> i32 {
0
}
pub fn features_(m: *const module, features: &mut Vec<String>) -> i32 {
*features = featuresarray(m, module_features());
0 }
pub fn enables_(m: *const module, enables: &mut Option<Vec<i32>>) -> i32 {
handlefeatures(m, module_features(), enables) }
#[allow(unused_variables)]
pub fn boot_(m: *const module) -> i32 {
ZTCP_SESSIONS.with(|s| s.borrow_mut().clear()); 0
}
pub fn cleanup_(m: *const module) -> i32 {
tcp_cleanup(); setfeatureenables(m, module_features(), None) }
#[allow(unused_variables)]
pub fn finish_(m: *const module) -> i32 {
0
}
type TcpSessionHandle = Option<usize>;
pub fn zsh_inet_aton(src: &str) -> Option<u32> {
src.parse::<std::net::Ipv4Addr>()
.ok()
.map(|a| u32::from(a).to_be())
}
fn sess_get<R, F: FnOnce(&tcp_session) -> R>(idx: usize, f: F) -> R {
ZTCP_SESSIONS.with(|s| {
let g = s.borrow();
f(&g[idx])
})
}
fn sess_with<F: FnOnce(&mut tcp_session)>(idx: usize, f: F) {
ZTCP_SESSIONS.with(|s| {
let mut g = s.borrow_mut();
f(&mut g[idx])
});
}
static MODULE_FEATURES: OnceLock<Mutex<features>> = OnceLock::new();
fn featuresarray(_m: *const module, _f: &Mutex<features>) -> Vec<String> {
vec!["b:ztcp".to_string()]
}
fn handlefeatures(
_m: *const module,
_f: &Mutex<features>,
enables: &mut Option<Vec<i32>>,
) -> i32 {
if enables.is_none() {
*enables = Some(vec![1; 1]);
}
0
}
fn setfeatureenables(_m: *const module, _f: &Mutex<features>, _e: Option<&[i32]>) -> i32 {
0
}
fn module_features() -> &'static Mutex<features> {
MODULE_FEATURES.get_or_init(|| {
Mutex::new(features {
bn_list: None,
bn_size: 1,
cd_list: None,
cd_size: 0,
mf_list: None,
mf_size: 0,
pd_list: None,
pd_size: 0,
n_abstract: 0,
})
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zts_alloc_creates_session_with_default_fd() {
let _g = crate::test_util::global_state_lock();
let _ = zts_alloc(ZTCP_LISTEN);
ZTCP_SESSIONS.with(|s| {
let sessions = s.borrow();
assert!(!sessions.is_empty());
let last = sessions.last().unwrap();
assert_eq!(last.fd, -1);
assert_eq!(last.flags, ZTCP_LISTEN);
});
}
#[test]
fn inet_ntop_v4_works() {
let _g = crate::test_util::global_state_lock();
let bytes = [127u8, 0, 0, 1];
assert_eq!(
zsh_inet_ntop(libc::AF_INET, &bytes).as_deref(),
Some("127.0.0.1")
);
}
#[test]
fn inet_pton_v4_works() {
let _g = crate::test_util::global_state_lock();
let mut buf = [0u8; 4];
assert_eq!(zsh_inet_pton(libc::AF_INET, "127.0.0.1", &mut buf), 1);
assert_eq!(buf, [127, 0, 0, 1]);
}
#[test]
fn inet_pton_invalid_returns_zero() {
let _g = crate::test_util::global_state_lock();
let mut buf = [0u8; 4];
assert_eq!(zsh_inet_pton(libc::AF_INET, "bad-ip", &mut buf), 0);
}
#[test]
fn inet_ntop_wildcard_address_is_zero_dotted() {
let _g = crate::test_util::global_state_lock();
let bytes = [0u8, 0, 0, 0];
assert_eq!(
zsh_inet_ntop(libc::AF_INET, &bytes).as_deref(),
Some("0.0.0.0")
);
}
#[test]
fn inet_ntop_broadcast_address_is_all_255() {
let _g = crate::test_util::global_state_lock();
let bytes = [255u8, 255, 255, 255];
assert_eq!(
zsh_inet_ntop(libc::AF_INET, &bytes).as_deref(),
Some("255.255.255.255")
);
}
#[test]
fn inet_pton_ntop_round_trips_for_typical_addresses() {
let _g = crate::test_util::global_state_lock();
for ip in &["192.168.1.1", "10.0.0.1", "172.16.254.1", "8.8.8.8"] {
let mut buf = [0u8; 4];
assert_eq!(
zsh_inet_pton(libc::AF_INET, ip, &mut buf),
1,
"pton failed on {}",
ip
);
assert_eq!(
zsh_inet_ntop(libc::AF_INET, &buf).as_deref(),
Some(*ip),
"round-trip mismatch for {}",
ip
);
}
}
#[test]
fn inet_pton_rejects_octet_over_255() {
let _g = crate::test_util::global_state_lock();
let mut buf = [0u8; 4];
assert_eq!(zsh_inet_pton(libc::AF_INET, "256.0.0.0", &mut buf), 0);
}
#[test]
fn inet_pton_rejects_empty_string() {
let _g = crate::test_util::global_state_lock();
let mut buf = [0u8; 4];
assert_eq!(zsh_inet_pton(libc::AF_INET, "", &mut buf), 0);
}
#[test]
fn zts_alloc_flags_passthrough() {
let _g = crate::test_util::global_state_lock();
let _ = zts_alloc(ZTCP_LISTEN);
ZTCP_SESSIONS.with(|s| {
let sessions = s.borrow();
let last = sessions.last().unwrap();
assert_eq!(
last.flags, ZTCP_LISTEN,
"flags must be passed through verbatim"
);
});
}
#[test]
fn zts_delete_unknown_fd_is_safe() {
let _g = crate::test_util::global_state_lock();
let before = ZTCP_SESSIONS.with(|s| s.borrow().len());
let _ = zts_delete(99999);
let after = ZTCP_SESSIONS.with(|s| s.borrow().len());
assert_eq!(
before, after,
"delete of unknown fd must not change session count"
);
}
#[test]
fn module_lifecycle_shims_all_return_zero() {
let _g = crate::test_util::global_state_lock();
let m: *const module = std::ptr::null();
assert_eq!(setup_(m), 0);
assert_eq!(boot_(m), 0);
assert_eq!(cleanup_(m), 0);
assert_eq!(finish_(m), 0);
}
#[test]
fn tcp_corpus_inet_ntop_loopback_v4() {
let _g = crate::test_util::global_state_lock();
let r = zsh_inet_ntop(libc::AF_INET, &[127, 0, 0, 1]);
assert_eq!(r.as_deref(), Some("127.0.0.1"));
}
#[test]
fn tcp_corpus_inet_ntop_any_v4() {
let _g = crate::test_util::global_state_lock();
let r = zsh_inet_ntop(libc::AF_INET, &[0, 0, 0, 0]);
assert_eq!(r.as_deref(), Some("0.0.0.0"));
}
#[test]
fn tcp_corpus_inet_ntop_short_buffer_returns_none() {
let _g = crate::test_util::global_state_lock();
let r = zsh_inet_ntop(libc::AF_INET, &[1, 2]);
assert!(r.is_none());
}
#[test]
fn tcp_corpus_inet_ntop_unknown_af_returns_none() {
let _g = crate::test_util::global_state_lock();
let r = zsh_inet_ntop(99, &[1, 2, 3, 4]);
assert!(r.is_none());
}
#[test]
fn tcp_corpus_inet_pton_v4_round_trip() {
let _g = crate::test_util::global_state_lock();
let mut dst = [0u8; 4];
let r = zsh_inet_pton(libc::AF_INET, "127.0.0.1", &mut dst);
assert_eq!(r, 1);
assert_eq!(dst, [127, 0, 0, 1]);
}
#[test]
fn tcp_corpus_inet_pton_invalid_returns_zero() {
let _g = crate::test_util::global_state_lock();
let mut dst = [0u8; 4];
assert_eq!(zsh_inet_pton(libc::AF_INET, "not.an.ip", &mut dst), 0);
assert_eq!(zsh_inet_pton(libc::AF_INET, "999.999.999.999", &mut dst), 0);
}
#[test]
fn tcp_corpus_inet_pton_unknown_af_returns_minus_one() {
let _g = crate::test_util::global_state_lock();
let mut dst = [0u8; 4];
assert_eq!(zsh_inet_pton(99, "127.0.0.1", &mut dst), -1);
}
#[test]
fn tcp_corpus_inet_pton_short_dst_returns_zero() {
let _g = crate::test_util::global_state_lock();
let mut dst = [0u8; 2];
assert_eq!(zsh_inet_pton(libc::AF_INET, "127.0.0.1", &mut dst), 0);
}
#[test]
fn tcp_corpus_v4_ntop_pton_round_trip() {
let _g = crate::test_util::global_state_lock();
let original = [192, 168, 1, 100];
let s = zsh_inet_ntop(libc::AF_INET, &original).unwrap();
let mut back = [0u8; 4];
let rc = zsh_inet_pton(libc::AF_INET, &s, &mut back);
assert_eq!(rc, 1);
assert_eq!(back, original);
}
}