1 /* $OpenBSD: krpc_subr.c,v 1.39 2024/05/01 13:15:59 jsg Exp $ */
2 /* $NetBSD: krpc_subr.c,v 1.12.4.1 1996/06/07 00:52:26 cgd Exp $ */
3
4 /*
5 * Copyright (c) 1995 Gordon Ross, Adam Glass
6 * Copyright (c) 1992 Regents of the University of California.
7 * All rights reserved.
8 *
9 * This software was developed by the Computer Systems Engineering group
10 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
11 * contributed to Berkeley.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by the University of
24 * California, Lawrence Berkeley Laboratory and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 *
41 * partially based on:
42 * libnetboot/rpc.c
43 * @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp (LBL)
44 */
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/proc.h>
49 #include <sys/mbuf.h>
50 #include <sys/socket.h>
51 #include <sys/socketvar.h>
52
53 #include <netinet/in.h>
54
55 #include <nfs/rpcv2.h>
56 #include <nfs/krpc.h>
57 #include <nfs/xdr_subs.h>
58 #include <crypto/idgen.h>
59
60 /*
61 * Kernel support for Sun RPC
62 *
63 * Used currently for bootstrapping in nfs diskless configurations.
64 */
65
66 /*
67 * Generic RPC headers
68 */
69
70 struct auth_info {
71 u_int32_t authtype; /* auth type */
72 u_int32_t authlen; /* auth length */
73 };
74
75 struct auth_unix {
76 int32_t ua_time;
77 int32_t ua_hostname; /* null */
78 int32_t ua_uid;
79 int32_t ua_gid;
80 int32_t ua_gidlist; /* null */
81 };
82
83 struct rpc_call {
84 u_int32_t rp_xid; /* request transaction id */
85 int32_t rp_direction; /* call direction (0) */
86 u_int32_t rp_rpcvers; /* rpc version (2) */
87 u_int32_t rp_prog; /* program */
88 u_int32_t rp_vers; /* version */
89 u_int32_t rp_proc; /* procedure */
90 struct auth_info rpc_auth;
91 struct auth_unix rpc_unix;
92 struct auth_info rpc_verf;
93 };
94
95 struct rpc_reply {
96 u_int32_t rp_xid; /* request transaction id */
97 int32_t rp_direction; /* call direction (1) */
98 int32_t rp_astatus; /* accept status (0: accepted) */
99 union {
100 u_int32_t rpu_errno;
101 struct {
102 struct auth_info rok_auth;
103 u_int32_t rok_status;
104 } rpu_rok;
105 } rp_u;
106 };
107 #define rp_errno rp_u.rpu_errno
108 #define rp_auth rp_u.rpu_rok.rok_auth
109 #define rp_status rp_u.rpu_rok.rok_status
110
111 #define MIN_REPLY_HDR 16 /* xid, dir, astat, errno */
112
113 u_int32_t krpc_get_xid(void);
114
115 /*
116 * Return an unpredictable XID.
117 */
118 u_int32_t
krpc_get_xid(void)119 krpc_get_xid(void)
120 {
121 static struct idgen32_ctx krpc_xid_ctx;
122 static int called = 0;
123
124 if (!called) {
125 called = 1;
126 idgen32_init(&krpc_xid_ctx);
127 }
128 return idgen32(&krpc_xid_ctx);
129 }
130
131 /*
132 * What is the longest we will wait before re-sending a request?
133 * Note this is also the frequency of "RPC timeout" messages.
134 * The re-send loop count sup linearly to this maximum, so the
135 * first complaint will happen after (1+2+3+4+5)=15 seconds.
136 */
137 #define MAX_RESEND_DELAY 5 /* seconds */
138
139 /*
140 * Call portmap to lookup a port number for a particular rpc program
141 * Returns non-zero error on failure.
142 */
143 int
krpc_portmap(struct sockaddr_in * sin,u_int prog,u_int vers,u_int16_t * portp)144 krpc_portmap(struct sockaddr_in *sin, u_int prog, u_int vers, u_int16_t *portp)
145 {
146 struct sdata {
147 u_int32_t prog; /* call program */
148 u_int32_t vers; /* call version */
149 u_int32_t proto; /* call protocol */
150 u_int32_t port; /* call port (unused) */
151 } *sdata;
152 struct rdata {
153 u_int16_t pad;
154 u_int16_t port;
155 } *rdata;
156 struct mbuf *m;
157 int error;
158
159 /* The portmapper port is fixed. */
160 if (prog == PMAPPROG) {
161 *portp = htons(PMAPPORT);
162 return 0;
163 }
164
165 m = m_get(M_WAIT, MT_DATA);
166 sdata = mtod(m, struct sdata *);
167 m->m_len = sizeof(*sdata);
168
169 /* Do the RPC to get it. */
170 sdata->prog = txdr_unsigned(prog);
171 sdata->vers = txdr_unsigned(vers);
172 sdata->proto = txdr_unsigned(IPPROTO_UDP);
173 sdata->port = 0;
174
175 sin->sin_port = htons(PMAPPORT);
176 error = krpc_call(sin, PMAPPROG, PMAPVERS,
177 PMAPPROC_GETPORT, &m, NULL, -1);
178 if (error)
179 return error;
180
181 if (m->m_len < sizeof(*rdata)) {
182 m = m_pullup(m, sizeof(*rdata));
183 if (m == NULL)
184 return ENOBUFS;
185 }
186 rdata = mtod(m, struct rdata *);
187 *portp = rdata->port;
188
189 m_freem(m);
190 return 0;
191 }
192
193 /*
194 * Do a remote procedure call (RPC) and wait for its reply.
195 * If from_p is non-null, then we are doing broadcast, and
196 * the address from whence the response came is saved there.
197 * data: input/output
198 * from_p: output
199 */
200 int
krpc_call(struct sockaddr_in * sa,u_int prog,u_int vers,u_int func,struct mbuf ** data,struct mbuf ** from_p,int retries)201 krpc_call(struct sockaddr_in *sa, u_int prog, u_int vers, u_int func,
202 struct mbuf **data, struct mbuf **from_p, int retries)
203 {
204 struct socket *so;
205 struct sockaddr_in *sin;
206 struct mbuf *m, *nam, *mhead, *from, *mopt;
207 struct rpc_call *call;
208 struct rpc_reply *reply;
209 struct uio auio;
210 int error, rcvflg, timo, secs, len, authlen;
211 static u_int32_t xid = 0;
212 char addr[INET_ADDRSTRLEN];
213 int *ip;
214 struct timeval tv;
215
216 /*
217 * Validate address family.
218 * Sorry, this is INET specific...
219 */
220 if (sa->sin_family != AF_INET)
221 return (EAFNOSUPPORT);
222
223 /* Free at end if not null. */
224 nam = mhead = NULL;
225 from = NULL;
226
227 /*
228 * Create socket and set its receive timeout.
229 */
230 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)))
231 goto out;
232
233 m = m_get(M_WAIT, MT_SOOPTS);
234 tv.tv_sec = 1;
235 tv.tv_usec = 0;
236 memcpy(mtod(m, struct timeval *), &tv, sizeof tv);
237 m->m_len = sizeof(tv);
238 error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m);
239 m_freem(m);
240 if (error)
241 goto out;
242
243 /*
244 * Enable broadcast if necessary.
245 */
246 if (from_p) {
247 int32_t *on;
248 m = m_get(M_WAIT, MT_SOOPTS);
249 on = mtod(m, int32_t *);
250 m->m_len = sizeof(*on);
251 *on = 1;
252 error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m);
253 m_freem(m);
254 if (error)
255 goto out;
256 }
257
258 /*
259 * Bind the local endpoint to a reserved port,
260 * because some NFS servers refuse requests from
261 * non-reserved (non-privileged) ports.
262 */
263 MGET(mopt, M_WAIT, MT_SOOPTS);
264 mopt->m_len = sizeof(int);
265 ip = mtod(mopt, int *);
266 *ip = IP_PORTRANGE_LOW;
267 error = sosetopt(so, IPPROTO_IP, IP_PORTRANGE, mopt);
268 m_freem(mopt);
269 if (error)
270 goto out;
271
272 MGET(m, M_WAIT, MT_SONAME);
273 sin = mtod(m, struct sockaddr_in *);
274 memset(sin, 0, sizeof(*sin));
275 sin->sin_len = m->m_len = sizeof(struct sockaddr_in);
276 sin->sin_family = AF_INET;
277 sin->sin_addr.s_addr = INADDR_ANY;
278 sin->sin_port = htons(0);
279 solock(so);
280 error = sobind(so, m, &proc0);
281 sounlock(so);
282 m_freem(m);
283 if (error) {
284 printf("bind failed\n");
285 goto out;
286 }
287
288 MGET(mopt, M_WAIT, MT_SOOPTS);
289 mopt->m_len = sizeof(int);
290 ip = mtod(mopt, int *);
291 *ip = IP_PORTRANGE_DEFAULT;
292 error = sosetopt(so, IPPROTO_IP, IP_PORTRANGE, mopt);
293 m_freem(mopt);
294 if (error)
295 goto out;
296
297 /*
298 * Setup socket address for the server.
299 */
300 nam = m_get(M_WAIT, MT_SONAME);
301 sin = mtod(nam, struct sockaddr_in *);
302 bcopy(sa, sin, (nam->m_len = sa->sin_len));
303
304 /*
305 * Prepend RPC message header.
306 */
307 mhead = m_gethdr(M_WAIT, MT_DATA);
308 mhead->m_next = *data;
309 call = mtod(mhead, struct rpc_call *);
310 mhead->m_len = sizeof(*call);
311 memset(call, 0, sizeof(*call));
312 /* rpc_call part */
313 xid = krpc_get_xid();
314 call->rp_xid = txdr_unsigned(xid);
315 /* call->rp_direction = 0; */
316 call->rp_rpcvers = txdr_unsigned(2);
317 call->rp_prog = txdr_unsigned(prog);
318 call->rp_vers = txdr_unsigned(vers);
319 call->rp_proc = txdr_unsigned(func);
320 /* rpc_auth part (auth_unix as root) */
321 call->rpc_auth.authtype = txdr_unsigned(RPCAUTH_UNIX);
322 call->rpc_auth.authlen = txdr_unsigned(sizeof(struct auth_unix));
323 /* rpc_verf part (auth_null) */
324 call->rpc_verf.authtype = 0;
325 call->rpc_verf.authlen = 0;
326
327 /*
328 * Setup packet header
329 */
330 m_calchdrlen(mhead);
331 mhead->m_pkthdr.ph_ifidx = 0;
332
333 /*
334 * Send it, repeatedly, until a reply is received,
335 * but delay each re-send by an increasing amount.
336 * If the delay hits the maximum, start complaining.
337 */
338 for (timo = 0; retries; retries--) {
339 /* Send RPC request (or re-send). */
340 m = m_copym(mhead, 0, M_COPYALL, M_WAIT);
341 if (m == NULL) {
342 error = ENOBUFS;
343 goto out;
344 }
345 error = sosend(so, nam, NULL, m, NULL, 0);
346 if (error) {
347 printf("krpc_call: sosend: %d\n", error);
348 goto out;
349 }
350 m = NULL;
351
352 /* Determine new timeout. */
353 if (timo < MAX_RESEND_DELAY)
354 timo++;
355 else
356 printf("RPC timeout for server %s (0x%x) prog %u\n",
357 inet_ntop(AF_INET, &sin->sin_addr,
358 addr, sizeof(addr)),
359 ntohl(sin->sin_addr.s_addr), prog);
360
361 /*
362 * Wait for up to timo seconds for a reply.
363 * The socket receive timeout was set to 1 second.
364 */
365 secs = timo;
366 while (secs > 0) {
367 m_freem(from);
368 from = NULL;
369
370 m_freem(m);
371 m = NULL;
372
373 auio.uio_resid = len = 1<<16;
374 auio.uio_procp = NULL;
375 rcvflg = 0;
376 error = soreceive(so, &from, &auio, &m, NULL, &rcvflg,
377 0);
378 if (error == EWOULDBLOCK) {
379 secs--;
380 continue;
381 }
382 if (error)
383 goto out;
384 len -= auio.uio_resid;
385
386 /* Does the reply contain at least a header? */
387 if (len < MIN_REPLY_HDR)
388 continue;
389 if (m->m_len < MIN_REPLY_HDR)
390 continue;
391 reply = mtod(m, struct rpc_reply *);
392
393 /* Is it the right reply? */
394 if (reply->rp_direction != txdr_unsigned(RPC_REPLY))
395 continue;
396
397 if (reply->rp_xid != txdr_unsigned(xid))
398 continue;
399
400 /* Was RPC accepted? (authorization OK) */
401 if (reply->rp_astatus != 0) {
402 error = fxdr_unsigned(u_int32_t, reply->rp_errno);
403 printf("rpc denied, error=%d\n", error);
404 continue;
405 }
406
407 /* Did the call succeed? */
408 if (reply->rp_status != 0) {
409 error = fxdr_unsigned(u_int32_t, reply->rp_status);
410 printf("rpc denied, status=%d\n", error);
411 continue;
412 }
413
414 goto gotreply; /* break two levels */
415
416 } /* while secs */
417 } /* forever send/receive */
418
419 error = ETIMEDOUT;
420 goto out;
421
422 gotreply:
423
424 /*
425 * Get RPC reply header into first mbuf,
426 * get its length, then strip it off.
427 */
428 len = sizeof(*reply);
429 KASSERT(m->m_flags & M_PKTHDR);
430 if (m->m_pkthdr.len < len) {
431 error = EBADRPC;
432 goto out;
433 }
434 if (m->m_len < len) {
435 m = m_pullup(m, len);
436 if (m == NULL) {
437 error = ENOBUFS;
438 goto out;
439 }
440 }
441 reply = mtod(m, struct rpc_reply *);
442 if (reply->rp_auth.authtype != 0) {
443 authlen = fxdr_unsigned(u_int32_t, reply->rp_auth.authlen);
444 if (authlen < 0 || authlen > RPCAUTH_MAXSIZ) {
445 error = EBADRPC;
446 goto out;
447 }
448 len += (authlen + 3) & ~3; /* XXX? */
449 }
450 if (len < 0 || m->m_pkthdr.len < len) {
451 error = EBADRPC;
452 goto out;
453 }
454 m_adj(m, len);
455
456 /* result */
457 *data = m;
458 if (from_p && error == 0) {
459 *from_p = from;
460 from = NULL;
461 }
462
463 out:
464 m_freem(nam);
465 m_freem(mhead);
466 m_freem(from);
467 soclose(so, 0);
468 return error;
469 }
470
471 /*
472 * eXternal Data Representation routines.
473 * (but with non-standard args...)
474 */
475
476 /*
477 * String representation for RPC.
478 */
479 struct xdr_string {
480 u_int32_t len; /* length without null or padding */
481 char data[4]; /* data (longer, of course) */
482 /* data is padded to a long-word boundary */
483 };
484
485 struct mbuf *
xdr_string_encode(char * str,int len)486 xdr_string_encode(char *str, int len)
487 {
488 struct mbuf *m;
489 struct xdr_string *xs;
490 int dlen; /* padded string length */
491 int mlen; /* message length */
492
493 dlen = (len + 3) & ~3;
494 mlen = dlen + 4;
495
496 if (mlen > MCLBYTES) /* If too big, we just can't do it. */
497 return (NULL);
498
499 m = m_get(M_WAIT, MT_DATA);
500 if (mlen > MLEN) {
501 MCLGET(m, M_WAIT);
502 if ((m->m_flags & M_EXT) == 0) {
503 (void) m_free(m); /* There can be only one. */
504 return (NULL);
505 }
506 }
507 xs = mtod(m, struct xdr_string *);
508 m->m_len = mlen;
509 xs->len = txdr_unsigned(len);
510 bcopy(str, xs->data, len);
511 return (m);
512 }
513
514 struct mbuf *
xdr_string_decode(struct mbuf * m,char * str,int * len_p)515 xdr_string_decode(struct mbuf *m, char *str, int *len_p)
516 {
517 struct xdr_string *xs;
518 int mlen; /* message length */
519 int slen; /* string length */
520
521 mlen = sizeof(u_int32_t);
522 KASSERT(m->m_flags & M_PKTHDR);
523 if (m->m_pkthdr.len < mlen) {
524 m_freem(m);
525 return (NULL);
526 }
527 if (m->m_len < mlen) {
528 m = m_pullup(m, mlen);
529 if (m == NULL)
530 return (NULL);
531 }
532 xs = mtod(m, struct xdr_string *);
533 slen = fxdr_unsigned(u_int32_t, xs->len);
534 if (slen < 0 || slen > INT_MAX - 3 - mlen) {
535 m_freem(m);
536 return (NULL);
537 }
538 mlen += (slen + 3) & ~3;
539
540 if (slen > *len_p)
541 slen = *len_p;
542 if (m->m_pkthdr.len < mlen) {
543 m_freem(m);
544 return (NULL);
545 }
546 m_copydata(m, 4, slen, str);
547 m_adj(m, mlen);
548
549 str[slen] = '\0';
550 *len_p = slen;
551
552 return (m);
553 }
554
555
556 /*
557 * Inet address in RPC messages
558 * (Note, really four ints, NOT chars. Blech.)
559 */
560 struct xdr_inaddr {
561 u_int32_t atype;
562 u_int32_t addr[4];
563 };
564
565 struct mbuf *
xdr_inaddr_encode(struct in_addr * ia)566 xdr_inaddr_encode(struct in_addr *ia)
567 {
568 struct mbuf *m;
569 struct xdr_inaddr *xi;
570 u_int8_t *cp;
571 u_int32_t *ip;
572
573 m = m_get(M_WAIT, MT_DATA);
574 xi = mtod(m, struct xdr_inaddr *);
575 m->m_len = sizeof(*xi);
576 xi->atype = txdr_unsigned(1);
577 ip = xi->addr;
578 cp = (u_int8_t *)&ia->s_addr;
579 *ip++ = txdr_unsigned(*cp++);
580 *ip++ = txdr_unsigned(*cp++);
581 *ip++ = txdr_unsigned(*cp++);
582 *ip++ = txdr_unsigned(*cp++);
583
584 return (m);
585 }
586
587 struct mbuf *
xdr_inaddr_decode(struct mbuf * m,struct in_addr * ia)588 xdr_inaddr_decode(struct mbuf *m, struct in_addr *ia)
589 {
590 struct xdr_inaddr *xi;
591 u_int8_t *cp;
592 u_int32_t *ip;
593
594 if (m->m_len < sizeof(*xi)) {
595 m = m_pullup(m, sizeof(*xi));
596 if (m == NULL)
597 return (NULL);
598 }
599 xi = mtod(m, struct xdr_inaddr *);
600 if (xi->atype != txdr_unsigned(1)) {
601 ia->s_addr = INADDR_ANY;
602 goto out;
603 }
604 ip = xi->addr;
605 cp = (u_int8_t *)&ia->s_addr;
606 *cp++ = fxdr_unsigned(u_int8_t, *ip++);
607 *cp++ = fxdr_unsigned(u_int8_t, *ip++);
608 *cp++ = fxdr_unsigned(u_int8_t, *ip++);
609 *cp++ = fxdr_unsigned(u_int8_t, *ip++);
610
611 out:
612 m_adj(m, sizeof(*xi));
613 return (m);
614 }
615