kernel - work on dmsg disk exports
[dragonfly.git] / lib / libstand / rpc.c
1 /*      $NetBSD: rpc.c,v 1.18 1998/01/23 19:27:45 thorpej Exp $ */
2 /* $DragonFly: src/lib/libstand/rpc.c,v 1.3 2005/12/11 02:27:26 swildner Exp $                                                  */
3
4 /*
5  * Copyright (c) 1992 Regents of the University of California.
6  * All rights reserved.
7  *
8  * This software was developed by the Computer Systems Engineering group
9  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
10  * contributed to Berkeley.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *      This product includes software developed by the University of
23  *      California, Lawrence Berkeley Laboratory and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  * @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp  (LBL)
41  */
42
43 /*
44  * RPC functions used by NFS and bootparams.
45  * Note that bootparams requires the ability to find out the
46  * address of the server from which its response has come.
47  * This is supported by keeping the IP/UDP headers in the
48  * buffer space provided by the caller.  (See rpc_fromaddr)
49  */
50
51 #include <sys/param.h>
52 #include <sys/socket.h>
53
54 #include <netinet/in.h>
55 #include <netinet/in_systm.h>
56
57 #include <string.h>
58
59 #include "rpcv2.h"
60
61 #include "stand.h"
62 #include "net.h"
63 #include "netif.h"
64 #include "rpc.h"
65
66 struct auth_info {
67         int32_t         authtype;       /* auth type */
68         u_int32_t       authlen;        /* auth length */
69 };
70
71 struct auth_unix {
72         int32_t   ua_time;
73         int32_t   ua_hostname;  /* null */
74         int32_t   ua_uid;
75         int32_t   ua_gid;
76         int32_t   ua_gidlist;   /* null */
77 };
78
79 struct rpc_call {
80         u_int32_t       rp_xid;         /* request transaction id */
81         int32_t         rp_direction;   /* call direction (0) */
82         u_int32_t       rp_rpcvers;     /* rpc version (2) */
83         u_int32_t       rp_prog;        /* program */
84         u_int32_t       rp_vers;        /* version */
85         u_int32_t       rp_proc;        /* procedure */
86 };
87
88 struct rpc_reply {
89         u_int32_t       rp_xid;         /* request transaction id */
90         int32_t         rp_direction;   /* call direction (1) */
91         int32_t         rp_astatus;     /* accept status (0: accepted) */
92         union {
93                 u_int32_t       rpu_errno;
94                 struct {
95                         struct auth_info rok_auth;
96                         u_int32_t       rok_status;
97                 } rpu_rok;
98         } rp_u;
99 };
100
101 /* Local forwards */
102 static  ssize_t recvrpc(struct iodesc *, void *, size_t, time_t);
103 static  int rpc_getport(struct iodesc *, n_long, n_long);
104
105 int rpc_xid;
106 static int rpc_port = 0x400;
107
108 int
109 rpc_newport(void)
110 {
111         if (rpc_port <= 256)            /* recycle after a while */
112                 rpc_port = 0x400;
113         return (--rpc_port);
114 }
115
116 /*
117  * Make a rpc call; return length of answer
118  * Note: Caller must leave room for headers.
119  */
120 ssize_t
121 rpc_call(struct iodesc *d, n_long prog, n_long vers, n_long proc, void *sdata,
122          size_t slen, void *rdata, size_t rlen)
123 {
124         ssize_t cc;
125         struct auth_info *auth;
126         struct rpc_call *call;
127         struct rpc_reply *reply;
128         char *send_head, *send_tail;
129         char *recv_head, *recv_tail;
130         n_long x;
131         int port;       /* host order */
132
133 #ifdef RPC_DEBUG
134         if (debug)
135                 printf("rpc_call: prog=0x%x vers=%d proc=%d\n",
136                     prog, vers, proc);
137 #endif
138
139         port = rpc_getport(d, prog, vers);
140         if (port == -1)
141                 return (-1);
142
143         d->destport = htons(port);
144
145         /*
146          * Prepend authorization stuff and headers.
147          * Note, must prepend things in reverse order.
148          */
149         send_head = sdata;
150         send_tail = (char *)sdata + slen;
151
152         /* Auth verifier is always auth_null */
153         send_head -= sizeof(*auth);
154         auth = (struct auth_info *)send_head;
155         auth->authtype = htonl(RPCAUTH_NULL);
156         auth->authlen = 0;
157
158 #if 1
159         /* Auth credentials: always auth unix (as root) */
160         send_head -= sizeof(struct auth_unix);
161         bzero(send_head, sizeof(struct auth_unix));
162         send_head -= sizeof(*auth);
163         auth = (struct auth_info *)send_head;
164         auth->authtype = htonl(RPCAUTH_UNIX);
165         auth->authlen = htonl(sizeof(struct auth_unix));
166 #else
167         /* Auth credentials: always auth_null (XXX OK?) */
168         send_head -= sizeof(*auth);
169         auth = send_head;
170         auth->authtype = htonl(RPCAUTH_NULL);
171         auth->authlen = 0;
172 #endif
173
174         /* RPC call structure. */
175         send_head -= sizeof(*call);
176         call = (struct rpc_call *)send_head;
177         rpc_xid++;
178         call->rp_xid       = htonl(rpc_xid);
179         call->rp_direction = htonl(RPC_CALL);
180         call->rp_rpcvers   = htonl(RPC_VER2);
181         call->rp_prog = htonl(prog);
182         call->rp_vers = htonl(vers);
183         call->rp_proc = htonl(proc);
184
185         /* Make room for the rpc_reply header. */
186         recv_head = rdata;
187         recv_tail = (char *)rdata + rlen;
188         recv_head -= sizeof(*reply);
189
190         cc = sendrecv(d,
191             sendudp, send_head, send_tail - send_head,
192             recvrpc, recv_head, recv_tail - recv_head);
193
194 #ifdef RPC_DEBUG
195         if (debug)
196                 printf("callrpc: cc=%ld rlen=%lu\n", (long)cc, (u_long)rlen);
197 #endif
198         if (cc == -1)
199                 return (-1);
200
201         if (cc <= sizeof(*reply)) {
202                 errno = EBADRPC;
203                 return (-1);
204         }
205
206         recv_tail = recv_head + cc;
207
208         /*
209          * Check the RPC reply status.
210          * The xid, dir, astatus were already checked.
211          */
212         reply = (struct rpc_reply *)recv_head;
213         auth = &reply->rp_u.rpu_rok.rok_auth;
214         x = ntohl(auth->authlen);
215         if (x != 0) {
216 #ifdef RPC_DEBUG
217                 if (debug)
218                         printf("callrpc: reply auth != NULL\n");
219 #endif
220                 errno = EBADRPC;
221                 return(-1);
222         }
223         x = ntohl(reply->rp_u.rpu_rok.rok_status);
224         if (x != 0) {
225                 printf("callrpc: error = %ld\n", (long)x);
226                 errno = EBADRPC;
227                 return(-1);
228         }
229         recv_head += sizeof(*reply);
230
231         return (ssize_t)(recv_tail - recv_head);
232 }
233
234 /*
235  * Returns true if packet is the one we're waiting for.
236  * This just checks the XID, direction, acceptance.
237  * Remaining checks are done by callrpc
238  */
239 static ssize_t
240 recvrpc(struct iodesc *d, void *pkt, size_t len, time_t tleft)
241 {
242         struct rpc_reply *reply;
243         ssize_t n;
244         int     x;
245
246         errno = 0;
247 #ifdef RPC_DEBUG
248         if (debug)
249                 printf("recvrpc: called len=%lu\n", (u_long)len);
250 #endif
251
252         n = readudp(d, pkt, len, tleft);
253         if (n <= (4 * 4))
254                 return -1;
255
256         reply = (struct rpc_reply *)pkt;
257
258         x = ntohl(reply->rp_xid);
259         if (x != rpc_xid) {
260 #ifdef RPC_DEBUG
261                 if (debug)
262                         printf("recvrpc: rp_xid %d != xid %d\n", x, rpc_xid);
263 #endif
264                 return -1;
265         }
266
267         x = ntohl(reply->rp_direction);
268         if (x != RPC_REPLY) {
269 #ifdef RPC_DEBUG
270                 if (debug)
271                         printf("recvrpc: rp_direction %d != REPLY\n", x);
272 #endif
273                 return -1;
274         }
275
276         x = ntohl(reply->rp_astatus);
277         if (x != RPC_MSGACCEPTED) {
278                 errno = ntohl(reply->rp_u.rpu_errno);
279                 printf("recvrpc: reject, astat=%d, errno=%d\n", x, errno);
280                 return -1;
281         }
282
283         /* Return data count (thus indicating success) */
284         return (n);
285 }
286
287 /*
288  * Given a pointer to a reply just received,
289  * dig out the IP address/port from the headers.
290  */
291 void
292 rpc_fromaddr(void *pkt, struct in_addr *addr, u_short *port)
293 {
294         struct hackhdr {
295                 /* Tail of IP header: just IP addresses */
296                 n_long ip_src;
297                 n_long ip_dst;
298                 /* UDP header: */
299                 u_int16_t uh_sport;             /* source port */
300                 u_int16_t uh_dport;             /* destination port */
301                 int16_t   uh_ulen;              /* udp length */
302                 u_int16_t uh_sum;               /* udp checksum */
303                 /* RPC reply header: */
304                 struct rpc_reply rpc;
305         } *hhdr;
306
307         hhdr = ((struct hackhdr *)pkt) - 1;
308         addr->s_addr = hhdr->ip_src;
309         *port = hhdr->uh_sport;
310 }
311
312 /*
313  * RPC Portmapper cache
314  */
315 #define PMAP_NUM 8                      /* need at most 5 pmap entries */
316
317 int rpc_pmap_num;
318 struct pmap_list {
319         struct in_addr  addr;   /* server, net order */
320         u_int   prog;           /* host order */
321         u_int   vers;           /* host order */
322         int     port;           /* host order */
323 } rpc_pmap_list[PMAP_NUM];
324
325 /* 
326  * return port number in host order, or -1 
327  *
328  * Parameters:
329  *      addr:   server, net order
330  *      prog:   host order
331  *      vers:   host order
332  */
333 int
334 rpc_pmap_getcache(struct in_addr addr, u_int prog, u_int vers)
335 {
336         struct pmap_list *pl;
337
338         for (pl = rpc_pmap_list; pl < &rpc_pmap_list[rpc_pmap_num]; pl++) {
339                 if (pl->addr.s_addr == addr.s_addr &&
340                         pl->prog == prog && pl->vers == vers )
341                 {
342                         return (pl->port);
343                 }
344         }
345         return (-1);
346 }
347
348 /*
349  * Parameters:
350  *      addr:   server, net order
351  *      prog:   host order
352  *      vers:   host order
353  *      port:   host order
354  */
355 void
356 rpc_pmap_putcache(struct in_addr addr, u_int prog, u_int vers, int port)
357 {
358         struct pmap_list *pl;
359
360         /* Don't overflow cache... */
361         if (rpc_pmap_num >= PMAP_NUM) {
362                 /* ... just re-use the last entry. */
363                 rpc_pmap_num = PMAP_NUM - 1;
364 #ifdef  RPC_DEBUG
365                 printf("rpc_pmap_putcache: cache overflow\n");
366 #endif
367         }
368
369         pl = &rpc_pmap_list[rpc_pmap_num];
370         rpc_pmap_num++;
371
372         /* Cache answer */
373         pl->addr = addr;
374         pl->prog = prog;
375         pl->vers = vers;
376         pl->port = port;
377 }
378
379
380 /*
381  * Request a port number from the port mapper.
382  * Returns the port in host order.
383  *
384  * Parameters:
385  *      prog:   host order
386  *      vers:   host order
387  */
388 int
389 rpc_getport(struct iodesc *d, n_long prog, n_long vers)
390 {
391         struct args {
392                 n_long  prog;           /* call program */
393                 n_long  vers;           /* call version */
394                 n_long  proto;          /* call protocol */
395                 n_long  port;           /* call port (unused) */
396         } *args;
397         struct res {
398                 n_long port;
399         } *res;
400         struct {
401                 n_long  h[RPC_HEADER_WORDS];
402                 struct args d;
403         } sdata;
404         struct {
405                 n_long  h[RPC_HEADER_WORDS];
406                 struct res d;
407                 n_long  pad;
408         } rdata;
409         ssize_t cc;
410         int port;
411
412 #ifdef RPC_DEBUG
413         if (debug)
414                 printf("getport: prog=0x%x vers=%d\n", prog, vers);
415 #endif
416
417         /* This one is fixed forever. */
418         if (prog == PMAPPROG)
419                 return (PMAPPORT);
420
421         /* Try for cached answer first */
422         port = rpc_pmap_getcache(d->destip, prog, vers);
423         if (port != -1)
424                 return (port);
425
426         args = &sdata.d;
427         args->prog = htonl(prog);
428         args->vers = htonl(vers);
429         args->proto = htonl(IPPROTO_UDP);
430         args->port = 0;
431         res = &rdata.d;
432
433         cc = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT,
434                 args, sizeof(*args), res, sizeof(*res));
435         if (cc < sizeof(*res)) {
436                 printf("getport: %s", strerror(errno));
437                 errno = EBADRPC;
438                 return (-1);
439         }
440         port = (int)ntohl(res->port);
441
442         rpc_pmap_putcache(d->destip, prog, vers, port);
443
444         return (port);
445 }