Remove register keyword usage.
[games.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.2 2004/10/25 19:38:45 drhodus 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 int rpc_port = 0x400;   /* predecrement */
107
108 /*
109  * Make a rpc call; return length of answer
110  * Note: Caller must leave room for headers.
111  */
112 ssize_t
113 rpc_call(d, prog, vers, proc, sdata, slen, rdata, rlen)
114         struct iodesc *d;
115         n_long prog, vers, proc;
116         void *sdata;
117         size_t slen;
118         void *rdata;
119         size_t rlen;
120 {
121         ssize_t cc;
122         struct auth_info *auth;
123         struct rpc_call *call;
124         struct rpc_reply *reply;
125         char *send_head, *send_tail;
126         char *recv_head, *recv_tail;
127         n_long x;
128         int port;       /* host order */
129
130 #ifdef RPC_DEBUG
131         if (debug)
132                 printf("rpc_call: prog=0x%x vers=%d proc=%d\n",
133                     prog, vers, proc);
134 #endif
135
136         port = rpc_getport(d, prog, vers);
137         if (port == -1)
138                 return (-1);
139
140         d->destport = htons(port);
141
142         /*
143          * Prepend authorization stuff and headers.
144          * Note, must prepend things in reverse order.
145          */
146         send_head = sdata;
147         send_tail = (char *)sdata + slen;
148
149         /* Auth verifier is always auth_null */
150         send_head -= sizeof(*auth);
151         auth = (struct auth_info *)send_head;
152         auth->authtype = htonl(RPCAUTH_NULL);
153         auth->authlen = 0;
154
155 #if 1
156         /* Auth credentials: always auth unix (as root) */
157         send_head -= sizeof(struct auth_unix);
158         bzero(send_head, sizeof(struct auth_unix));
159         send_head -= sizeof(*auth);
160         auth = (struct auth_info *)send_head;
161         auth->authtype = htonl(RPCAUTH_UNIX);
162         auth->authlen = htonl(sizeof(struct auth_unix));
163 #else
164         /* Auth credentials: always auth_null (XXX OK?) */
165         send_head -= sizeof(*auth);
166         auth = send_head;
167         auth->authtype = htonl(RPCAUTH_NULL);
168         auth->authlen = 0;
169 #endif
170
171         /* RPC call structure. */
172         send_head -= sizeof(*call);
173         call = (struct rpc_call *)send_head;
174         rpc_xid++;
175         call->rp_xid       = htonl(rpc_xid);
176         call->rp_direction = htonl(RPC_CALL);
177         call->rp_rpcvers   = htonl(RPC_VER2);
178         call->rp_prog = htonl(prog);
179         call->rp_vers = htonl(vers);
180         call->rp_proc = htonl(proc);
181
182         /* Make room for the rpc_reply header. */
183         recv_head = rdata;
184         recv_tail = (char *)rdata + rlen;
185         recv_head -= sizeof(*reply);
186
187         cc = sendrecv(d,
188             sendudp, send_head, send_tail - send_head,
189             recvrpc, recv_head, recv_tail - recv_head);
190
191 #ifdef RPC_DEBUG
192         if (debug)
193                 printf("callrpc: cc=%ld rlen=%lu\n", (long)cc, (u_long)rlen);
194 #endif
195         if (cc == -1)
196                 return (-1);
197
198         if (cc <= sizeof(*reply)) {
199                 errno = EBADRPC;
200                 return (-1);
201         }
202
203         recv_tail = recv_head + cc;
204
205         /*
206          * Check the RPC reply status.
207          * The xid, dir, astatus were already checked.
208          */
209         reply = (struct rpc_reply *)recv_head;
210         auth = &reply->rp_u.rpu_rok.rok_auth;
211         x = ntohl(auth->authlen);
212         if (x != 0) {
213 #ifdef RPC_DEBUG
214                 if (debug)
215                         printf("callrpc: reply auth != NULL\n");
216 #endif
217                 errno = EBADRPC;
218                 return(-1);
219         }
220         x = ntohl(reply->rp_u.rpu_rok.rok_status);
221         if (x != 0) {
222                 printf("callrpc: error = %ld\n", (long)x);
223                 errno = EBADRPC;
224                 return(-1);
225         }
226         recv_head += sizeof(*reply);
227
228         return (ssize_t)(recv_tail - recv_head);
229 }
230
231 /*
232  * Returns true if packet is the one we're waiting for.
233  * This just checks the XID, direction, acceptance.
234  * Remaining checks are done by callrpc
235  */
236 static ssize_t
237 recvrpc(d, pkt, len, tleft)
238         struct iodesc *d;
239         void *pkt;
240         size_t len;
241         time_t tleft;
242 {
243         struct rpc_reply *reply;
244         ssize_t n;
245         int     x;
246
247         errno = 0;
248 #ifdef RPC_DEBUG
249         if (debug)
250                 printf("recvrpc: called len=%lu\n", (u_long)len);
251 #endif
252
253         n = readudp(d, pkt, len, tleft);
254         if (n <= (4 * 4))
255                 return -1;
256
257         reply = (struct rpc_reply *)pkt;
258
259         x = ntohl(reply->rp_xid);
260         if (x != rpc_xid) {
261 #ifdef RPC_DEBUG
262                 if (debug)
263                         printf("recvrpc: rp_xid %d != xid %d\n", x, rpc_xid);
264 #endif
265                 return -1;
266         }
267
268         x = ntohl(reply->rp_direction);
269         if (x != RPC_REPLY) {
270 #ifdef RPC_DEBUG
271                 if (debug)
272                         printf("recvrpc: rp_direction %d != REPLY\n", x);
273 #endif
274                 return -1;
275         }
276
277         x = ntohl(reply->rp_astatus);
278         if (x != RPC_MSGACCEPTED) {
279                 errno = ntohl(reply->rp_u.rpu_errno);
280                 printf("recvrpc: reject, astat=%d, errno=%d\n", x, errno);
281                 return -1;
282         }
283
284         /* Return data count (thus indicating success) */
285         return (n);
286 }
287
288 /*
289  * Given a pointer to a reply just received,
290  * dig out the IP address/port from the headers.
291  */
292 void
293 rpc_fromaddr(pkt, addr, port)
294         void            *pkt;
295         struct in_addr  *addr;
296         u_short         *port;
297 {
298         struct hackhdr {
299                 /* Tail of IP header: just IP addresses */
300                 n_long ip_src;
301                 n_long ip_dst;
302                 /* UDP header: */
303                 u_int16_t uh_sport;             /* source port */
304                 u_int16_t uh_dport;             /* destination port */
305                 int16_t   uh_ulen;              /* udp length */
306                 u_int16_t uh_sum;               /* udp checksum */
307                 /* RPC reply header: */
308                 struct rpc_reply rpc;
309         } *hhdr;
310
311         hhdr = ((struct hackhdr *)pkt) - 1;
312         addr->s_addr = hhdr->ip_src;
313         *port = hhdr->uh_sport;
314 }
315
316 /*
317  * RPC Portmapper cache
318  */
319 #define PMAP_NUM 8                      /* need at most 5 pmap entries */
320
321 int rpc_pmap_num;
322 struct pmap_list {
323         struct in_addr  addr;   /* server, net order */
324         u_int   prog;           /* host order */
325         u_int   vers;           /* host order */
326         int     port;           /* host order */
327 } rpc_pmap_list[PMAP_NUM];
328
329 /* return port number in host order, or -1 */
330 int
331 rpc_pmap_getcache(addr, prog, vers)
332         struct in_addr  addr;   /* server, net order */
333         u_int           prog;   /* host order */
334         u_int           vers;   /* host order */
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 void
349 rpc_pmap_putcache(addr, prog, vers, port)
350         struct in_addr  addr;   /* server, net order */
351         u_int           prog;   /* host order */
352         u_int           vers;   /* host order */
353         int             port;   /* host order */
354 {
355         struct pmap_list *pl;
356
357         /* Don't overflow cache... */
358         if (rpc_pmap_num >= PMAP_NUM) {
359                 /* ... just re-use the last entry. */
360                 rpc_pmap_num = PMAP_NUM - 1;
361 #ifdef  RPC_DEBUG
362                 printf("rpc_pmap_putcache: cache overflow\n");
363 #endif
364         }
365
366         pl = &rpc_pmap_list[rpc_pmap_num];
367         rpc_pmap_num++;
368
369         /* Cache answer */
370         pl->addr = addr;
371         pl->prog = prog;
372         pl->vers = vers;
373         pl->port = port;
374 }
375
376
377 /*
378  * Request a port number from the port mapper.
379  * Returns the port in host order.
380  */
381 int
382 rpc_getport(d, prog, vers)
383         struct iodesc *d;
384         n_long prog;    /* host order */
385         n_long vers;    /* host order */
386 {
387         struct args {
388                 n_long  prog;           /* call program */
389                 n_long  vers;           /* call version */
390                 n_long  proto;          /* call protocol */
391                 n_long  port;           /* call port (unused) */
392         } *args;
393         struct res {
394                 n_long port;
395         } *res;
396         struct {
397                 n_long  h[RPC_HEADER_WORDS];
398                 struct args d;
399         } sdata;
400         struct {
401                 n_long  h[RPC_HEADER_WORDS];
402                 struct res d;
403                 n_long  pad;
404         } rdata;
405         ssize_t cc;
406         int port;
407
408 #ifdef RPC_DEBUG
409         if (debug)
410                 printf("getport: prog=0x%x vers=%d\n", prog, vers);
411 #endif
412
413         /* This one is fixed forever. */
414         if (prog == PMAPPROG)
415                 return (PMAPPORT);
416
417         /* Try for cached answer first */
418         port = rpc_pmap_getcache(d->destip, prog, vers);
419         if (port != -1)
420                 return (port);
421
422         args = &sdata.d;
423         args->prog = htonl(prog);
424         args->vers = htonl(vers);
425         args->proto = htonl(IPPROTO_UDP);
426         args->port = 0;
427         res = &rdata.d;
428
429         cc = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT,
430                 args, sizeof(*args), res, sizeof(*res));
431         if (cc < sizeof(*res)) {
432                 printf("getport: %s", strerror(errno));
433                 errno = EBADRPC;
434                 return (-1);
435         }
436         port = (int)ntohl(res->port);
437
438         rpc_pmap_putcache(d->destip, prog, vers, port);
439
440         return (port);
441 }