1use anyhow::{bail, ensure, Context, Result};
5use serde_json::{json, Value};
6use std::net::{IpAddr, SocketAddr};
7
8pub const PORT: u16 = 111;
9pub const PROGRAM: u32 = 100_000;
10pub const LOW_VERSION: u32 = 2;
12pub const HIGH_VERSION: u32 = 4;
13pub const MAX_RECORD: usize = 256 * 1024;
16pub const MAX_FRAGMENTS: usize = 64;
18pub const MAX_STRING: usize = 1024;
20pub const MAX_AUTH: usize = 400;
22pub const MAX_MAPPINGS: usize = 1000;
24
25pub const IPPROTO_TCP: u32 = 6;
26pub const IPPROTO_UDP: u32 = 17;
27
28pub fn program_name(p: u32) -> Option<&'static str> {
30 Some(match p {
31 100_000 => "portmapper",
32 100_003 => "nfs",
33 100_005 => "mountd",
34 100_011 => "rquotad",
35 100_021 => "nlockmgr",
36 100_024 => "status",
37 100_227 => "nfs_acl",
38 100_004 => "ypserv",
39 100_007 => "ypbind",
40 150_001 => "pcnfsd",
41 _ => return None,
42 })
43}
44
45pub struct Reader<'a> {
46 b: &'a [u8],
47 pos: usize,
48}
49
50impl<'a> Reader<'a> {
51 pub fn new(b: &'a [u8]) -> Self {
52 Self { b, pos: 0 }
53 }
54 pub fn u32(&mut self) -> Result<u32> {
55 let v = self
56 .b
57 .get(self.pos..self.pos + 4)
58 .context("XDR value runs past the message")?;
59 self.pos += 4;
60 Ok(u32::from_be_bytes([v[0], v[1], v[2], v[3]]))
61 }
62 pub fn opaque(&mut self, max: usize) -> Result<&'a [u8]> {
63 let n = self.u32()? as usize;
64 ensure!(n <= max, "XDR opaque of {n} bytes; the limit is {max}");
65 let padded = n + (4 - n % 4) % 4;
66 let v = self
67 .b
68 .get(self.pos..self.pos + n)
69 .context("XDR opaque runs past the message")?;
70 ensure!(self.pos + padded <= self.b.len(), "XDR padding missing");
71 self.pos += padded;
72 Ok(v)
73 }
74 pub fn string(&mut self) -> Result<String> {
75 let b = self.opaque(MAX_STRING)?;
76 String::from_utf8(b.to_vec()).context("XDR string is not UTF-8")
77 }
78 pub fn bool(&mut self) -> Result<bool> {
79 match self.u32()? {
80 0 => Ok(false),
81 1 => Ok(true),
82 v => bail!("XDR bool must be 0 or 1, not {v}"),
83 }
84 }
85 pub fn rest(&self) -> &'a [u8] {
86 &self.b[self.pos..]
87 }
88 pub fn done(&self) -> bool {
89 self.pos == self.b.len()
90 }
91}
92
93#[derive(Default)]
94pub struct Writer(pub Vec<u8>);
95
96impl Writer {
97 pub fn u32(&mut self, v: u32) -> &mut Self {
98 self.0.extend_from_slice(&v.to_be_bytes());
99 self
100 }
101 pub fn opaque(&mut self, b: &[u8]) -> &mut Self {
102 self.u32(b.len() as u32);
103 self.0.extend_from_slice(b);
104 self.0
105 .extend(std::iter::repeat_n(0u8, (4 - b.len() % 4) % 4));
106 self
107 }
108 pub fn string(&mut self, s: &str) -> &mut Self {
109 self.opaque(s.as_bytes())
110 }
111}
112
113pub fn credential_json(flavor: u32, body: &[u8]) -> Value {
115 match flavor {
116 0 => json!({"flavor": "none"}),
117 1 => {
118 let mut r = Reader::new(body);
120 let parsed = (|| -> Result<Value> {
121 let stamp = r.u32()?;
122 let machine = String::from_utf8_lossy(r.opaque(255)?).into_owned();
123 let uid = r.u32()?;
124 let gid = r.u32()?;
125 let n = r.u32()? as usize;
126 ensure!(n <= 16, "too many gids");
127 let gids = (0..n).map(|_| r.u32()).collect::<Result<Vec<_>>>()?;
128 Ok(
129 json!({"flavor": "sys", "stamp": stamp, "machine": machine, "uid": uid, "gid": gid, "gids": gids}),
130 )
131 })();
132 parsed.unwrap_or_else(|_| json!({"flavor": "sys", "malformed": true}))
133 }
134 f => json!({"flavor": f}),
135 }
136}
137
138pub struct Call<'a> {
139 pub xid: u32,
140 pub rpc_version: u32,
141 pub program: u32,
142 pub version: u32,
143 pub procedure: u32,
144 pub cred_flavor: u32,
145 pub cred: &'a [u8],
146 pub args: &'a [u8],
147}
148
149pub fn parse_call(b: &[u8]) -> Result<Option<Call<'_>>> {
151 let mut r = Reader::new(b);
152 let xid = r.u32()?;
153 if r.u32()? != 0 {
154 return Ok(None);
155 }
156 let rpc_version = r.u32()?;
157 let program = r.u32()?;
158 let version = r.u32()?;
159 let procedure = r.u32()?;
160 let cred_flavor = r.u32()?;
161 let cred = r.opaque(MAX_AUTH)?;
162 let _verf_flavor = r.u32()?;
163 let _verf = r.opaque(MAX_AUTH)?;
164 Ok(Some(Call {
165 xid,
166 rpc_version,
167 program,
168 version,
169 procedure,
170 cred_flavor,
171 cred,
172 args: r.rest(),
173 }))
174}
175
176pub fn xid_of(b: &[u8]) -> Option<u32> {
178 b.get(..4)
179 .map(|x| u32::from_be_bytes([x[0], x[1], x[2], x[3]]))
180}
181
182pub enum Accepted {
184 Success(Vec<u8>),
185 ProgUnavail,
186 ProgMismatch(u32, u32),
187 ProcUnavail,
188 GarbageArgs,
189 SystemErr,
190}
191
192pub fn accepted_reply(xid: u32, a: &Accepted) -> Vec<u8> {
193 let mut w = Writer::default();
194 w.u32(xid).u32(1).u32(0); w.u32(0).u32(0); match a {
197 Accepted::Success(body) => {
198 w.u32(0);
199 w.0.extend_from_slice(body);
200 }
201 Accepted::ProgUnavail => {
202 w.u32(1);
203 }
204 Accepted::ProgMismatch(lo, hi) => {
205 w.u32(2).u32(*lo).u32(*hi);
206 }
207 Accepted::ProcUnavail => {
208 w.u32(3);
209 }
210 Accepted::GarbageArgs => {
211 w.u32(4);
212 }
213 Accepted::SystemErr => {
214 w.u32(5);
215 }
216 }
217 w.0
218}
219
220pub fn rpc_mismatch_reply(xid: u32) -> Vec<u8> {
222 let mut w = Writer::default();
223 w.u32(xid).u32(1).u32(1).u32(0).u32(2).u32(2);
224 w.0
225}
226
227pub fn call_message(xid: u32, version: u32, procedure: u32, args: &[u8]) -> Vec<u8> {
228 let mut w = Writer::default();
229 w.u32(xid)
230 .u32(0)
231 .u32(2)
232 .u32(PROGRAM)
233 .u32(version)
234 .u32(procedure);
235 w.u32(0).u32(0).u32(0).u32(0); w.0.extend_from_slice(args);
237 w.0
238}
239
240pub fn parse_reply(b: &[u8]) -> Result<(u32, std::result::Result<Vec<u8>, String>)> {
242 let mut r = Reader::new(b);
243 let xid = r.u32()?;
244 ensure!(r.u32()? == 1, "not a reply");
245 match r.u32()? {
246 0 => {
247 r.u32()?;
248 r.opaque(MAX_AUTH)?;
249 let stat = r.u32()?;
250 Ok((
251 xid,
252 match stat {
253 0 => Ok(r.rest().to_vec()),
254 1 => Err("PROG_UNAVAIL".into()),
255 2 => Err(format!(
256 "PROG_MISMATCH (versions {}-{})",
257 r.u32()?,
258 r.u32()?
259 )),
260 3 => Err("PROC_UNAVAIL".into()),
261 4 => Err("GARBAGE_ARGS".into()),
262 5 => Err("SYSTEM_ERR".into()),
263 s => Err(format!("accept_stat {s}")),
264 },
265 ))
266 }
267 1 => {
268 let why = match r.u32()? {
269 0 => format!("RPC_MISMATCH (versions {}-{})", r.u32()?, r.u32()?),
270 _ => format!("AUTH_ERROR {}", r.u32()?),
271 };
272 Ok((xid, Err(why)))
273 }
274 s => bail!("reply_stat {s}"),
275 }
276}
277
278pub fn record(msg: &[u8]) -> Vec<u8> {
280 let mut out = (0x8000_0000u32 | msg.len() as u32).to_be_bytes().to_vec();
281 out.extend_from_slice(msg);
282 out
283}
284
285pub async fn read_record<R: tokio::io::AsyncRead + Unpin>(r: &mut R) -> Result<Option<Vec<u8>>> {
287 use tokio::io::AsyncReadExt;
288 let mut out = Vec::new();
289 for i in 0..MAX_FRAGMENTS {
290 let mut h = [0u8; 4];
291 match r.read_exact(&mut h).await {
292 Ok(_) => {}
293 Err(e) if i == 0 && e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
294 Err(e) => return Err(e.into()),
295 }
296 let h = u32::from_be_bytes(h);
297 let len = (h & 0x7fff_ffff) as usize;
298 ensure!(
299 out.len() + len <= MAX_RECORD,
300 "record of more than {MAX_RECORD} bytes announced"
301 );
302 let start = out.len();
303 out.resize(start + len, 0);
304 r.read_exact(&mut out[start..]).await?;
305 if h & 0x8000_0000 != 0 {
306 return Ok(Some(out));
307 }
308 }
309 bail!("record split into more than {MAX_FRAGMENTS} fragments")
310}
311
312#[derive(Clone, Debug, PartialEq)]
316pub struct Mapping {
317 pub program: u32,
318 pub version: u32,
319 pub netid: String,
321 pub port: u16,
322 pub owner: String,
323}
324
325pub fn netid_of(protocol: u32) -> Option<&'static str> {
326 match protocol {
327 IPPROTO_TCP => Some("tcp"),
328 IPPROTO_UDP => Some("udp"),
329 _ => None,
330 }
331}
332
333pub fn protocol_of(netid: &str) -> Option<u32> {
334 match netid {
335 "tcp" | "tcp6" => Some(IPPROTO_TCP),
336 "udp" | "udp6" => Some(IPPROTO_UDP),
337 _ => None,
338 }
339}
340
341pub fn uaddr(ip: IpAddr, port: u16) -> String {
343 format!("{ip}.{}.{}", port >> 8, port & 0xff)
344}
345
346pub fn parse_uaddr(s: &str) -> Result<SocketAddr> {
347 let mut parts = s.rsplitn(3, '.');
348 let lo: u16 = parts.next().context("empty universal address")?.parse()?;
349 let hi: u16 = parts
350 .next()
351 .context("universal address has no port")?
352 .parse()?;
353 let ip: IpAddr = parts
354 .next()
355 .context("universal address has no host")?
356 .parse()?;
357 ensure!(
358 hi < 256 && lo < 256,
359 "universal address port bytes out of range"
360 );
361 Ok(SocketAddr::new(ip, hi << 8 | lo))
362}
363
364pub fn mapping_from_json(v: &Value) -> Result<Mapping> {
367 let num = |k: &str| -> Result<u64> {
368 v[k].as_u64()
369 .with_context(|| format!("{k} must be a number"))
370 };
371 let program = num("program")?;
372 let version = num("version")?;
373 let port = num("port")?;
374 ensure!(
375 program <= u64::from(u32::MAX) && version <= u64::from(u32::MAX),
376 "program and version are 32-bit"
377 );
378 ensure!(port <= 65535, "port must be 0-65535");
379 let netid = v["protocol"].as_str().unwrap_or("tcp").to_string();
380 ensure!(
381 protocol_of(&netid).is_some(),
382 "protocol must be tcp, udp, tcp6 or udp6"
383 );
384 Ok(Mapping {
385 program: program as u32,
386 version: version as u32,
387 netid,
388 port: port as u16,
389 owner: v["owner"]
390 .as_str()
391 .unwrap_or("netget")
392 .chars()
393 .take(64)
394 .collect(),
395 })
396}
397
398pub fn mapping_json(m: &Mapping) -> Value {
399 json!({"program": m.program, "program_name": program_name(m.program), "version": m.version,
400 "protocol": m.netid, "port": m.port, "owner": m.owner})
401}
402
403pub fn read_pmap(r: &mut Reader) -> Result<(u32, u32, u32, u32)> {
405 Ok((r.u32()?, r.u32()?, r.u32()?, r.u32()?))
406}
407
408pub fn write_pmap(w: &mut Writer, m: &Mapping) {
409 let prot = protocol_of(&m.netid).unwrap_or(IPPROTO_TCP);
410 w.u32(m.program)
411 .u32(m.version)
412 .u32(prot)
413 .u32(u32::from(m.port));
414}
415
416pub struct Rpcb {
418 pub program: u32,
419 pub version: u32,
420 pub netid: String,
421 pub addr: String,
422 pub owner: String,
423}
424
425pub fn read_rpcb(r: &mut Reader) -> Result<Rpcb> {
426 Ok(Rpcb {
427 program: r.u32()?,
428 version: r.u32()?,
429 netid: r.string()?,
430 addr: r.string()?,
431 owner: r.string()?,
432 })
433}
434
435pub fn write_rpcb(w: &mut Writer, b: &Rpcb) {
436 w.u32(b.program).u32(b.version);
437 w.string(&b.netid).string(&b.addr).string(&b.owner);
438}
439
440pub fn host_for(netid: &str, local: IpAddr) -> IpAddr {
442 match (netid.ends_with('6'), local) {
443 (true, IpAddr::V4(_)) => IpAddr::from(std::net::Ipv6Addr::LOCALHOST),
444 (false, IpAddr::V6(v6)) => v6
445 .to_ipv4_mapped()
446 .map(IpAddr::V4)
447 .unwrap_or(IpAddr::from([127, 0, 0, 1])),
448 _ => local,
449 }
450}
451
452pub fn pmaplist(ms: &[Mapping]) -> Vec<u8> {
454 let mut w = Writer::default();
455 for m in ms.iter().filter(|m| m.netid == "tcp" || m.netid == "udp") {
456 w.u32(1);
457 write_pmap(&mut w, m);
458 }
459 w.u32(0);
460 w.0
461}
462
463pub fn rpcblist(ms: &[Mapping], local: IpAddr) -> Vec<u8> {
465 let mut w = Writer::default();
466 for m in ms {
467 w.u32(1);
468 write_rpcb(
469 &mut w,
470 &Rpcb {
471 program: m.program,
472 version: m.version,
473 netid: m.netid.clone(),
474 addr: uaddr(host_for(&m.netid, local), m.port),
475 owner: m.owner.clone(),
476 },
477 );
478 }
479 w.u32(0);
480 w.0
481}
482
483pub fn entry_list(ms: &[Mapping], local: IpAddr) -> Vec<u8> {
485 let mut w = Writer::default();
486 for m in ms {
487 let tcp = m.netid.starts_with("tcp");
488 w.u32(1);
489 w.string(&uaddr(host_for(&m.netid, local), m.port));
490 w.string(&m.netid);
491 w.u32(if tcp { 3 } else { 1 }); w.string(if m.netid.ends_with('6') {
493 "inet6"
494 } else {
495 "inet"
496 });
497 w.string(if tcp { "tcp" } else { "udp" });
498 }
499 w.u32(0);
500 w.0
501}
502
503pub fn read_pmaplist(b: &[u8]) -> Result<Vec<Mapping>> {
505 let mut r = Reader::new(b);
506 let mut out = Vec::new();
507 while r.bool()? {
508 ensure!(
509 out.len() < MAX_MAPPINGS,
510 "more than {MAX_MAPPINGS} mappings"
511 );
512 let (program, version, prot, port) = read_pmap(&mut r)?;
513 out.push(Mapping {
514 program,
515 version,
516 netid: netid_of(prot).unwrap_or("unknown").to_string(),
517 port: port as u16,
518 owner: String::new(),
519 });
520 }
521 Ok(out)
522}
523
524pub fn read_rpcblist(b: &[u8]) -> Result<Vec<Value>> {
526 let mut r = Reader::new(b);
527 let mut out = Vec::new();
528 while r.bool()? {
529 ensure!(
530 out.len() < MAX_MAPPINGS,
531 "more than {MAX_MAPPINGS} mappings"
532 );
533 let e = read_rpcb(&mut r)?;
534 let port = parse_uaddr(&e.addr).map(|a| a.port()).ok();
535 out.push(json!({"program": e.program, "program_name": program_name(e.program), "version": e.version,
536 "netid": e.netid, "address": e.addr, "port": port, "owner": e.owner}));
537 }
538 Ok(out)
539}