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