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