Skip to content

Commit 7f7fdc6

Browse files
committed
rpcbind: add client and server implementations of SET
1 parent edf7f5c commit 7f7fdc6

4 files changed

Lines changed: 126 additions & 26 deletions

File tree

rpc_protocol/src/client.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use crate::*;
1111
///
1212
/// This blocks the calling thread until the procedure returns a result. It returns either that
1313
/// result as a byte vector (which the caller can decode), or an error.
14-
pub fn do_rpc_call(
15-
stream: &mut TcpStream,
14+
pub fn do_rpc_call<S: Read + Write>(
15+
stream: &mut S,
1616
prog: u32,
1717
vers: u32,
1818
proc: u32,
@@ -45,7 +45,10 @@ pub fn do_rpc_call(
4545
read_reply_from_stream(xid, stream)
4646
}
4747

48-
fn read_reply_from_stream(xid: u32, stream: &mut TcpStream) -> Result<Vec<u8>, crate::Error> {
48+
fn read_reply_from_stream<S: Read + Write>(
49+
xid: u32,
50+
stream: &mut S,
51+
) -> Result<Vec<u8>, crate::Error> {
4952
let message_length = decode_record_mark(stream)?;
5053

5154
let mut buf = vec![0; message_length as usize];

rpc_protocol/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ fn update_record_mark(buf: &mut Vec<u8>) {
9191
/// implementation does not yet support record fragments.
9292
///
9393
/// Otherwise, returns the length of the message.
94-
fn decode_record_mark(stream: &mut std::net::TcpStream) -> Result<u32, crate::Error> {
94+
fn decode_record_mark<S: Read + Write>(stream: &mut S) -> Result<u32, crate::Error> {
9595
let mut record_mark_bytes: [u8; 4] = [0; 4];
9696

9797
stream.read_exact(&mut record_mark_bytes).inspect_err(|e| {

rpc_protocol/src/rpcbind/client.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,59 @@
11
// SPDX-License-Identifier: BSD-3-Clause
22
// Copyright 2025. Triad National Security, LLC.
3+
4+
use log::*;
5+
6+
use std::io::{Read, Write};
7+
use std::net::TcpStream;
8+
use std::os::unix::net::UnixStream;
9+
10+
use crate::client::*;
11+
12+
use super::rpcbind;
13+
use super::rpcbind::procedures::*;
14+
15+
/// An RPCBIND Server tends to listen both on a Unix socket and a TCP socket.
16+
pub enum RpcbindServerAddress {
17+
Unix(String),
18+
Tcp(String),
19+
}
20+
21+
/// Try to call the SET RPC for the RPCBIND server listening at `address`, to add `new_service` to
22+
/// its service list.
23+
pub fn set(
24+
new_service: rpcbind::RpcService,
25+
server_address: RpcbindServerAddress,
26+
) -> Result<bool, crate::Error> {
27+
debug!("performing RPCBIND Set call");
28+
29+
match server_address {
30+
RpcbindServerAddress::Unix(addr) => {
31+
let stream = UnixStream::connect(addr)?;
32+
set_using_stream(new_service, stream)
33+
}
34+
RpcbindServerAddress::Tcp(addr) => {
35+
let stream = TcpStream::connect(addr)?;
36+
set_using_stream(new_service, stream)
37+
}
38+
}
39+
}
40+
41+
fn set_using_stream<S: Read + Write>(
42+
new_service: rpcbind::RpcService,
43+
mut stream: S,
44+
) -> Result<bool, crate::Error> {
45+
let arg = new_service.serialize_alloc();
46+
47+
let res = do_rpc_call(
48+
&mut stream,
49+
RPCBPROG,
50+
RPCBVERS::VERSION,
51+
RPCBVERS::RPCBPROC_SET,
52+
arg.as_slice(),
53+
)?;
54+
55+
match res.as_slice() {
56+
&[0, 0, 0, 0] => Ok(false),
57+
_ => Ok(true),
58+
}
59+
}

rpc_protocol/src/rpcbind/server.rs

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#![allow(non_camel_case_types)]
55

6+
use log::*;
7+
68
use std::ffi::OsString;
79
use std::net::TcpListener;
810

@@ -15,13 +17,8 @@ use super::rpcbind::procedures::*;
1517
pub fn main() {
1618
let service_list = default_service_list();
1719

18-
let procedures: Vec<Option<RpcProcedure<rpcbind::RpcbindList>>> = vec![
19-
None,
20-
None, // set()
21-
None, // unset()
22-
Some(getaddr),
23-
Some(dump),
24-
];
20+
let procedures: Vec<Option<RpcProcedure<rpcbind::RpcbindList>>> =
21+
vec![None, Some(set), Some(unset), Some(getaddr), Some(dump)];
2522
let mut server = RpcService::new(RPCBPROG, RPCBVERS::VERSION, procedures, service_list);
2623

2724
let listener = TcpListener::bind("0.0.0.0:111").unwrap();
@@ -34,26 +31,14 @@ pub fn main() {
3431
fn getaddr(_call: &CallBody, mut arg: &[u8], service_list: &mut rpcbind::RpcbindList) -> RpcResult {
3532
let mut requested = rpcbind::RpcService::default();
3633
rpcbind::RpcService::deserialize(&mut requested, &mut arg).unwrap();
37-
eprintln!("{:?}", requested);
38-
39-
for service in service_list.items.iter() {
40-
let service = &service.rpcb_map;
41-
42-
if requested.prog != service.prog {
43-
continue;
44-
}
45-
46-
if requested.vers != service.vers {
47-
continue;
48-
}
34+
debug!("GETADDR Call: {requested:?}");
4935

36+
if let Some(service) = get_service(requested.prog, requested.vers, service_list) {
5037
let address = rpcbind::RpcbString {
5138
contents: service.addr.clone(),
5239
};
5340

54-
let bytes = rpcbind::RpcbString::serialize_alloc(&address);
55-
56-
return RpcResult::Success(bytes);
41+
return RpcResult::Success(rpcbind::RpcbString::serialize_alloc(&address));
5742
}
5843

5944
let empty = rpcbind::RpcbString {
@@ -63,6 +48,37 @@ fn getaddr(_call: &CallBody, mut arg: &[u8], service_list: &mut rpcbind::Rpcbind
6348
RpcResult::Success(empty.serialize_alloc())
6449
}
6550

51+
/// Implementation of the set RPC. This adds a service to the list.
52+
fn set(_call: &CallBody, arg: &[u8], service_list: &mut rpcbind::RpcbindList) -> RpcResult {
53+
let mut new_service = rpcbind::RpcService::default();
54+
let mut arg = arg;
55+
if let Err(_) = new_service.deserialize(&mut arg) {
56+
return RpcResult::GarbageArgs;
57+
}
58+
59+
// Make sure that this service is not already registered:
60+
if get_service(new_service.prog, new_service.vers, service_list).is_some() {
61+
// If it is, return False to the caller:
62+
return RpcResult::Success(vec![0, 0, 0, 0]);
63+
}
64+
65+
if new_service.netid.is_empty() || new_service.addr.is_empty() {
66+
// According to the RFC, empty netid and address are not allowed.
67+
return RpcResult::Success(vec![0, 0, 0, 0]);
68+
}
69+
70+
service_list.items.push(rpcbind::RpcbindItem {
71+
rpcb_map: new_service,
72+
});
73+
74+
RpcResult::Success(vec![0, 0, 0, 1])
75+
}
76+
77+
/// Implementation of the unset RPC. This removes a service from the list.
78+
fn unset(_call: &CallBody, _arg: &[u8], service_list: &mut rpcbind::RpcbindList) -> RpcResult {
79+
todo!()
80+
}
81+
6682
/// Implementation of the dump RPC. This returns the entire known `service_list`.
6783
fn dump(_call: &CallBody, _arg: &[u8], service_list: &mut rpcbind::RpcbindList) -> RpcResult {
6884
let data = service_list.serialize_alloc();
@@ -83,3 +99,27 @@ fn default_service_list() -> rpcbind::RpcbindList {
8399

84100
rpcbind::RpcbindList { items: vec![item] }
85101
}
102+
103+
/// Returns the service specified by `program` and `version` from the `service_list`, or none if
104+
/// there is no match.
105+
fn get_service(
106+
program: u32,
107+
version: u32,
108+
service_list: &rpcbind::RpcbindList,
109+
) -> Option<&rpcbind::RpcService> {
110+
for service in &service_list.items {
111+
let service = &service.rpcb_map;
112+
113+
if program != service.prog {
114+
continue;
115+
}
116+
117+
if version != service.vers {
118+
continue;
119+
}
120+
121+
return Some(service);
122+
}
123+
124+
return None;
125+
}

0 commit comments

Comments
 (0)