rpc.c revision 84221
1162271Srwatson/*	$NetBSD: rpc.c,v 1.18 1998/01/23 19:27:45 thorpej Exp $	*/
2162271Srwatson
3172106Srwatson/*
4162271Srwatson * Copyright (c) 1992 Regents of the University of California.
5162271Srwatson * All rights reserved.
6162271Srwatson *
7162271Srwatson * This software was developed by the Computer Systems Engineering group
8162271Srwatson * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9162271Srwatson * contributed to Berkeley.
10162271Srwatson *
11162271Srwatson * Redistribution and use in source and binary forms, with or without
12162271Srwatson * modification, are permitted provided that the following conditions
13162271Srwatson * are met:
14162271Srwatson * 1. Redistributions of source code must retain the above copyright
15162271Srwatson *    notice, this list of conditions and the following disclaimer.
16162271Srwatson * 2. Redistributions in binary form must reproduce the above copyright
17162271Srwatson *    notice, this list of conditions and the following disclaimer in the
18162271Srwatson *    documentation and/or other materials provided with the distribution.
19162271Srwatson * 3. All advertising materials mentioning features or use of this software
20162271Srwatson *    must display the following acknowledgement:
21162271Srwatson *	This product includes software developed by the University of
22162271Srwatson *	California, Lawrence Berkeley Laboratory and its contributors.
23162271Srwatson * 4. Neither the name of the University nor the names of its contributors
24162271Srwatson *    may be used to endorse or promote products derived from this software
25162271Srwatson *    without specific prior written permission.
26162271Srwatson *
27162271Srwatson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28162271Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29162271Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30162271Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31162271Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32162271Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33162271Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34172106Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35172106Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36162271Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37162271Srwatson * SUCH DAMAGE.
38162271Srwatson *
39162271Srwatson * @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp  (LBL)
40162271Srwatson */
41162271Srwatson
42162271Srwatson#include <sys/cdefs.h>
43162271Srwatson__FBSDID("$FreeBSD: head/lib/libstand/rpc.c 84221 2001-09-30 22:28:01Z dillon $");
44162271Srwatson
45162271Srwatson/*
46172106Srwatson * RPC functions used by NFS and bootparams.
47172106Srwatson * Note that bootparams requires the ability to find out the
48172106Srwatson * address of the server from which its response has come.
49172106Srwatson * This is supported by keeping the IP/UDP headers in the
50172106Srwatson * buffer space provided by the caller.  (See rpc_fromaddr)
51172106Srwatson */
52172106Srwatson
53172106Srwatson#include <sys/param.h>
54172106Srwatson#include <sys/socket.h>
55162271Srwatson
56162271Srwatson#include <netinet/in.h>
57172106Srwatson#include <netinet/in_systm.h>
58172106Srwatson
59172106Srwatson#include <string.h>
60172106Srwatson
61162271Srwatson#include "rpcv2.h"
62162271Srwatson
63172106Srwatson#include "stand.h"
64172106Srwatson#include "net.h"
65172106Srwatson#include "netif.h"
66172106Srwatson#include "rpc.h"
67172106Srwatson
68172106Srwatsonstruct auth_info {
69172106Srwatson	int32_t 	authtype;	/* auth type */
70172106Srwatson	u_int32_t	authlen;	/* auth length */
71172106Srwatson};
72172106Srwatson
73172106Srwatsonstruct auth_unix {
74172106Srwatson	int32_t   ua_time;
75172106Srwatson	int32_t   ua_hostname;	/* null */
76172106Srwatson	int32_t   ua_uid;
77172106Srwatson	int32_t   ua_gid;
78172106Srwatson	int32_t   ua_gidlist;	/* null */
79172106Srwatson};
80172106Srwatson
81172106Srwatsonstruct rpc_call {
82172106Srwatson	u_int32_t	rp_xid;		/* request transaction id */
83172106Srwatson	int32_t 	rp_direction;	/* call direction (0) */
84172106Srwatson	u_int32_t	rp_rpcvers;	/* rpc version (2) */
85172106Srwatson	u_int32_t	rp_prog;	/* program */
86172106Srwatson	u_int32_t	rp_vers;	/* version */
87172106Srwatson	u_int32_t	rp_proc;	/* procedure */
88172106Srwatson};
89172106Srwatson
90172106Srwatsonstruct rpc_reply {
91172106Srwatson	u_int32_t	rp_xid;		/* request transaction id */
92172106Srwatson	int32_t 	rp_direction;	/* call direction (1) */
93172106Srwatson	int32_t 	rp_astatus;	/* accept status (0: accepted) */
94172106Srwatson	union {
95172106Srwatson		u_int32_t	rpu_errno;
96162271Srwatson		struct {
97172106Srwatson			struct auth_info rok_auth;
98162271Srwatson			u_int32_t	rok_status;
99172106Srwatson		} rpu_rok;
100162271Srwatson	} rp_u;
101172106Srwatson};
102172106Srwatson
103172106Srwatson/* Local forwards */
104172106Srwatsonstatic	ssize_t recvrpc(struct iodesc *, void *, size_t, time_t);
105172106Srwatsonstatic	int rpc_getport(struct iodesc *, n_long, n_long);
106172106Srwatson
107172106Srwatsonint rpc_xid;
108172106Srwatsonint rpc_port = 0x400;	/* predecrement */
109172106Srwatson
110172106Srwatson/*
111172106Srwatson * Make a rpc call; return length of answer
112172106Srwatson * Note: Caller must leave room for headers.
113172106Srwatson */
114172106Srwatsonssize_t
115162271Srwatsonrpc_call(d, prog, vers, proc, sdata, slen, rdata, rlen)
116172106Srwatson	register struct iodesc *d;
117172106Srwatson	register n_long prog, vers, proc;
118172106Srwatson	register void *sdata;
119172106Srwatson	register size_t slen;
120162271Srwatson	register void *rdata;
121172106Srwatson	register size_t rlen;
122172106Srwatson{
123172106Srwatson	register ssize_t cc;
124172106Srwatson	struct auth_info *auth;
125172106Srwatson	struct rpc_call *call;
126172106Srwatson	struct rpc_reply *reply;
127172106Srwatson	char *send_head, *send_tail;
128172106Srwatson	char *recv_head, *recv_tail;
129172106Srwatson	n_long x;
130172106Srwatson	int port;	/* host order */
131172106Srwatson
132172106Srwatson#ifdef RPC_DEBUG
133172106Srwatson	if (debug)
134172106Srwatson		printf("rpc_call: prog=0x%x vers=%d proc=%d\n",
135162271Srwatson		    prog, vers, proc);
136172106Srwatson#endif
137172106Srwatson
138172106Srwatson	port = rpc_getport(d, prog, vers);
139172106Srwatson	if (port == -1)
140162271Srwatson		return (-1);
141172106Srwatson
142172106Srwatson	d->destport = htons(port);
143172106Srwatson
144172106Srwatson	/*
145172106Srwatson	 * Prepend authorization stuff and headers.
146172106Srwatson	 * Note, must prepend things in reverse order.
147172106Srwatson	 */
148172106Srwatson	send_head = sdata;
149172106Srwatson	send_tail = (char *)sdata + slen;
150172106Srwatson
151172106Srwatson	/* Auth verifier is always auth_null */
152172106Srwatson	send_head -= sizeof(*auth);
153172106Srwatson	auth = (struct auth_info *)send_head;
154172106Srwatson	auth->authtype = htonl(RPCAUTH_NULL);
155162271Srwatson	auth->authlen = 0;
156172106Srwatson
157172106Srwatson#if 1
158172106Srwatson	/* Auth credentials: always auth unix (as root) */
159172106Srwatson	send_head -= sizeof(struct auth_unix);
160162271Srwatson	bzero(send_head, sizeof(struct auth_unix));
161172106Srwatson	send_head -= sizeof(*auth);
162172106Srwatson	auth = (struct auth_info *)send_head;
163172106Srwatson	auth->authtype = htonl(RPCAUTH_UNIX);
164172106Srwatson	auth->authlen = htonl(sizeof(struct auth_unix));
165172106Srwatson#else
166172106Srwatson	/* Auth credentials: always auth_null (XXX OK?) */
167172106Srwatson	send_head -= sizeof(*auth);
168172106Srwatson	auth = send_head;
169172106Srwatson	auth->authtype = htonl(RPCAUTH_NULL);
170172106Srwatson	auth->authlen = 0;
171172106Srwatson#endif
172172106Srwatson
173172106Srwatson	/* RPC call structure. */
174172106Srwatson	send_head -= sizeof(*call);
175162271Srwatson	call = (struct rpc_call *)send_head;
176172106Srwatson	rpc_xid++;
177172106Srwatson	call->rp_xid       = htonl(rpc_xid);
178172106Srwatson	call->rp_direction = htonl(RPC_CALL);
179162271Srwatson	call->rp_rpcvers   = htonl(RPC_VER2);
180172106Srwatson	call->rp_prog = htonl(prog);
181172106Srwatson	call->rp_vers = htonl(vers);
182172106Srwatson	call->rp_proc = htonl(proc);
183162271Srwatson
184172106Srwatson	/* Make room for the rpc_reply header. */
185162271Srwatson	recv_head = rdata;
186172106Srwatson	recv_tail = (char *)rdata + rlen;
187172106Srwatson	recv_head -= sizeof(*reply);
188172106Srwatson
189162271Srwatson	cc = sendrecv(d,
190172106Srwatson	    sendudp, send_head, send_tail - send_head,
191172106Srwatson	    recvrpc, recv_head, recv_tail - recv_head);
192172106Srwatson
193162271Srwatson#ifdef RPC_DEBUG
194162271Srwatson	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