use std::ffi::CString;
use std::os::raw::{c_char, c_void};
use std::time::Duration;
use libc::{AF_INET, INADDR_ANY, SOCK_DGRAM};
use libntirpc_sys::*;
const HELLO_PROG: rpcprog_t = 0x3000_0001;
const HELLO_VERS: rpcvers_t = 1;
unsafe extern "C" fn wrap_string(xdrs: *mut XDR, arg: *mut c_void) -> bool {
unsafe { xdr_wrapstring(xdrs, arg as *mut *mut c_char) }
}
unsafe extern "C" fn svc_req_alloc(xprt: *mut SVCXPRT, xdrs: *mut XDR) -> *mut svc_req {
unsafe {
let req = libc::calloc(1, std::mem::size_of::<svc_req>()) as *mut svc_req;
(*req).rq_xprt = xprt;
(*req).rq_xdrs = xdrs;
req
}
}
unsafe extern "C" fn svc_req_free(req: *mut svc_req, _stat: xprt_stat) {
unsafe { libc::free(req as *mut c_void) }
}
unsafe extern "C" fn hello_dispatch(_req: *mut svc_req) {}
unsafe extern "C" fn hello_process(req: *mut svc_req) -> xprt_stat {
unsafe {
let mut no_dispatch = false;
if svc_auth_authenticate(req, &mut no_dispatch) != auth_stat_AUTH_OK {
return xprt_stat_XPRT_IDLE;
}
let mut name: *mut c_char = std::ptr::null_mut();
if !xdr_wrapstring((*req).rq_xdrs, &mut name) {
(*req).rq_msg.ru.RM_rmb.ru.RP_ar.ru.AR_results.proc_ = Some(xdr_void);
(*req).rq_msg.ru.RM_rmb.ru.RP_ar.ru.AR_results.where_ = std::ptr::null_mut();
svc_sendreply(req);
return xprt_stat_XPRT_IDLE;
}
let name_c = std::ffi::CStr::from_ptr(name);
let reply_c = CString::new(format!("Hello, {}!", name_c.to_string_lossy())).unwrap();
libc::free(name as *mut c_void);
let mut reply_ptr: *mut c_char = reply_c.as_ptr() as *mut c_char;
(*req).rq_msg.ru.RM_rmb.ru.RP_ar.ru.AR_results.proc_ = Some(wrap_string);
(*req).rq_msg.ru.RM_rmb.ru.RP_ar.ru.AR_results.where_ =
&mut reply_ptr as *mut *mut c_char as *mut c_void;
svc_sendreply(req);
xprt_stat_XPRT_IDLE
}
}
unsafe extern "C" fn udp_rendezvous(xprt: *mut SVCXPRT) -> xprt_stat {
unsafe {
(*xprt).xp_dispatch.__bindgen_anon_1.process_cb = Some(hello_process);
let recv = (*(*xprt).xp_ops).xp_recv;
match recv {
Some(f) => f(xprt),
None => xprt_stat_XPRT_DIED,
}
}
}
fn main() {
unsafe {
let mut params: svc_init_params = std::mem::zeroed();
params.flags = SVC_INIT_EPOLL as u_long;
params.max_events = 16;
params.alloc_cb = Some(svc_req_alloc);
params.free_cb = Some(svc_req_free);
if !svc_init(&mut params) {
eprintln!("svc_init failed");
std::process::exit(1);
}
let fd = libc::socket(AF_INET, SOCK_DGRAM, 0);
if fd < 0 {
eprintln!("socket() failed: {}", std::io::Error::last_os_error());
std::process::exit(1);
}
let addr = libc::sockaddr_in {
sin_family: AF_INET as libc::sa_family_t,
sin_port: 0,
sin_addr: libc::in_addr {
s_addr: u32::from_ne_bytes(INADDR_ANY.to_ne_bytes()),
},
sin_zero: [0; 8],
};
let rc = libc::bind(
fd,
&addr as *const libc::sockaddr_in as *const libc::sockaddr,
std::mem::size_of::<libc::sockaddr_in>() as libc::socklen_t,
);
if rc != 0 {
eprintln!("bind() failed: {}", std::io::Error::last_os_error());
std::process::exit(1);
}
let xprt = svc_dg_ncreatef(fd, 0, 0, SVC_CREATE_FLAG_CLOSE);
if xprt.is_null() {
eprintln!("svc_dg_ncreatef failed");
std::process::exit(1);
}
(*xprt).xp_dispatch.rendezvous_cb = Some(udp_rendezvous);
let nettype = c"udp";
let nconf = getnetconfigent(nettype.as_ptr());
if nconf.is_null() {
eprintln!("getnetconfigent failed");
std::process::exit(1);
}
rpcb_unset(HELLO_PROG, HELLO_VERS, nconf);
if !svc_reg(xprt, HELLO_PROG, HELLO_VERS, Some(hello_dispatch), nconf) {
eprintln!("svc_reg failed (is rpcbind running?)");
std::process::exit(1);
}
freenetconfigent(nconf);
println!("hello server: prog={HELLO_PROG:#x} vers={HELLO_VERS} on udp");
loop {
std::thread::sleep(Duration::from_secs(1));
}
}
}