1139749Simp/*	$OpenBSD: krpc_subr.c,v 1.39 2024/05/01 13:15:59 jsg Exp $	*/
2123474Swpaul/*	$NetBSD: krpc_subr.c,v 1.12.4.1 1996/06/07 00:52:26 cgd Exp $	*/
3123474Swpaul
4123474Swpaul/*
5123474Swpaul * Copyright (c) 1995 Gordon Ross, Adam Glass
6123474Swpaul * Copyright (c) 1992 Regents of the University of California.
7123474Swpaul * All rights reserved.
8123474Swpaul *
9123474Swpaul * This software was developed by the Computer Systems Engineering group
10123474Swpaul * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
11123474Swpaul * contributed to Berkeley.
12123474Swpaul *
13123474Swpaul * Redistribution and use in source and binary forms, with or without
14123474Swpaul * modification, are permitted provided that the following conditions
15123474Swpaul * are met:
16123474Swpaul * 1. Redistributions of source code must retain the above copyright
17123474Swpaul *    notice, this list of conditions and the following disclaimer.
18123474Swpaul * 2. Redistributions in binary form must reproduce the above copyright
19123474Swpaul *    notice, this list of conditions and the following disclaimer in the
20123474Swpaul *    documentation and/or other materials provided with the distribution.
21123474Swpaul * 3. All advertising materials mentioning features or use of this software
22123474Swpaul *    must display the following acknowledgement:
23123474Swpaul *	This product includes software developed by the University of
24123474Swpaul *	California, Lawrence Berkeley Laboratory and its contributors.
25123474Swpaul * 4. Neither the name of the University nor the names of its contributors
26123474Swpaul *    may be used to endorse or promote products derived from this software
27123474Swpaul *    without specific prior written permission.
28123474Swpaul *
29123474Swpaul * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30123474Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31145283Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32151207Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33151207Swpaul * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34123474Swpaul * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35123474Swpaul * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36123474Swpaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37123474Swpaul * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38123474Swpaul * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39123474Swpaul * SUCH DAMAGE.
40123474Swpaul *
41123474Swpaul * partially based on:
42123474Swpaul *      libnetboot/rpc.c
43123474Swpaul *               @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp  (LBL)
44171390Sthompsa */
45164033Srwatson
46123474Swpaul#include <sys/param.h>
47123474Swpaul#include <sys/systm.h>
48123474Swpaul#include <sys/proc.h>
49141524Swpaul#include <sys/mbuf.h>
50143204Swpaul#include <sys/socket.h>
51123474Swpaul#include <sys/socketvar.h>
52171390Sthompsa
53123474Swpaul#include <netinet/in.h>
54123474Swpaul
55123474Swpaul#include <nfs/rpcv2.h>
56123474Swpaul#include <nfs/krpc.h>
57123474Swpaul#include <nfs/xdr_subs.h>
58123474Swpaul#include <crypto/idgen.h>
59147256Sbrooks
60129002Sandre/*
61123474Swpaul * Kernel support for Sun RPC
62123474Swpaul *
63123474Swpaul * Used currently for bootstrapping in nfs diskless configurations.
64123474Swpaul */
65123474Swpaul
66123474Swpaul/*
67123474Swpaul * Generic RPC headers
68123474Swpaul */
69123695Swpaul
70123695Swpaulstruct auth_info {
71171390Sthompsa	u_int32_t 	authtype;	/* auth type */
72123695Swpaul	u_int32_t	authlen;	/* auth length */
73123474Swpaul};
74123474Swpaul
75189488Sweongyostruct auth_unix {
76194677Sthompsa	int32_t   ua_time;
77123474Swpaul	int32_t   ua_hostname;	/* null */
78123474Swpaul	int32_t   ua_uid;
79145485Swpaul	int32_t   ua_gid;
80123474Swpaul	int32_t   ua_gidlist;	/* null */
81132953Swpaul};
82128229Swpaul
83123474Swpaulstruct rpc_call {
84186507Sweongyo	u_int32_t	rp_xid;		/* request transaction id */
85123474Swpaul	int32_t 	rp_direction;	/* call direction (0) */
86123474Swpaul	u_int32_t	rp_rpcvers;	/* rpc version (2) */
87171602Sthompsa	u_int32_t	rp_prog;	/* program */
88171390Sthompsa	u_int32_t	rp_vers;	/* version */
89171602Sthompsa	u_int32_t	rp_proc;	/* procedure */
90171602Sthompsa	struct	auth_info rpc_auth;
91171602Sthompsa	struct	auth_unix rpc_unix;
92171602Sthompsa	struct	auth_info rpc_verf;
93171390Sthompsa};
94171390Sthompsa
95171390Sthompsastruct rpc_reply {
96171390Sthompsa	u_int32_t rp_xid;		/* request transaction id */
97186507Sweongyo	int32_t   rp_direction;		/* call direction (1) */
98186507Sweongyo	int32_t   rp_astatus;		/* accept status (0: accepted) */
99186507Sweongyo	union {
100186507Sweongyo		u_int32_t rpu_errno;
101186507Sweongyo		struct {
102187104Sthompsa			struct auth_info rok_auth;
103189550Ssam			u_int32_t	rok_status;
104187104Sthompsa		} rpu_rok;
105187104Sthompsa	} rp_u;
106187104Sthompsa};
107187104Sthompsa#define rp_errno  rp_u.rpu_errno
108187104Sthompsa#define rp_auth   rp_u.rpu_rok.rok_auth
109187104Sthompsa#define rp_status rp_u.rpu_rok.rok_status
110187104Sthompsa
111187104Sthompsa#define MIN_REPLY_HDR 16	/* xid, dir, astat, errno */
112187104Sthompsa
113145485Swpaulu_int32_t krpc_get_xid(void);
114145485Swpaul
115145485Swpaul/*
116126706Swpaul * Return an unpredictable XID.
117145485Swpaul */
118123474Swpaulu_int32_t
119126706Swpaulkrpc_get_xid(void)
120126706Swpaul{
121126706Swpaul	static struct idgen32_ctx krpc_xid_ctx;
122126706Swpaul	static int called = 0;
123126706Swpaul
124123474Swpaul	if (!called) {
125141524Swpaul		called = 1;
126141524Swpaul		idgen32_init(&krpc_xid_ctx);
127144888Swpaul	}
128144888Swpaul	return idgen32(&krpc_xid_ctx);
129146230Swpaul}
130146230Swpaul
131146230Swpaul/*
132146230Swpaul * What is the longest we will wait before re-sending a request?
133146230Swpaul * Note this is also the frequency of "RPC timeout" messages.
134146230Swpaul * The re-send loop count sup linearly to this maximum, so the
135144888Swpaul * first complaint will happen after (1+2+3+4+5)=15 seconds.
136144888Swpaul */
137123695Swpaul#define	MAX_RESEND_DELAY 5	/* seconds */
138141963Swpaul
139141963Swpaul/*
140141963Swpaul * Call portmap to lookup a port number for a particular rpc program
141146230Swpaul * Returns non-zero error on failure.
142146230Swpaul */
143146230Swpaulint
144146230Swpaulkrpc_portmap(struct sockaddr_in *sin, u_int prog, u_int vers, u_int16_t *portp)
145141963Swpaul{
146141963Swpaul	struct sdata {
147145895Swpaul		u_int32_t prog;		/* call program */
148145895Swpaul		u_int32_t vers;		/* call version */
149145895Swpaul		u_int32_t proto;	/* call protocol */
150151451Swpaul		u_int32_t port;		/* call port (unused) */
151141963Swpaul	} *sdata;
152178354Ssam	struct rdata {
153228621Sbschmidt		u_int16_t pad;
154228621Sbschmidt		u_int16_t port;
155228621Sbschmidt	} *rdata;
156178354Ssam	struct mbuf *m;
157123474Swpaul	int error;
158151207Swpaul
159178354Ssam	/* The portmapper port is fixed. */
160178354Ssam	if (prog == PMAPPROG) {
161185485Ssam		*portp = htons(PMAPPORT);
162185485Ssam		return 0;
163123474Swpaul	}
164151207Swpaul
165151207Swpaul	m = m_get(M_WAIT, MT_DATA);
166151451Swpaul	sdata = mtod(m, struct sdata *);
167123474Swpaul	m->m_len = sizeof(*sdata);
168178704Sthompsa
169178354Ssam	/* Do the RPC to get it. */
170171390Sthompsa	sdata->prog = txdr_unsigned(prog);
171171602Sthompsa	sdata->vers = txdr_unsigned(vers);
172171602Sthompsa	sdata->proto = txdr_unsigned(IPPROTO_UDP);
173191746Sthompsa	sdata->port = 0;
174171390Sthompsa
175171390Sthompsa	sin->sin_port = htons(PMAPPORT);
176171390Sthompsa	error = krpc_call(sin, PMAPPROG, PMAPVERS,
177171390Sthompsa	    PMAPPROC_GETPORT, &m, NULL, -1);
178178354Ssam	if (error)
179178354Ssam		return error;
180123474Swpaul
181123474Swpaul	if (m->m_len < sizeof(*rdata)) {
182123474Swpaul		m = m_pullup(m, sizeof(*rdata));
183123474Swpaul		if (m == NULL)
184194706Scokane			return ENOBUFS;
185194706Scokane	}
186127349Swpaul	rdata = mtod(m, struct rdata *);
187124821Swpaul	*portp = rdata->port;
188124821Swpaul
189123695Swpaul	m_freem(m);
190123695Swpaul	return 0;
191178929Sthompsa}
192201620Srpaulo
193151207Swpaul/*
194178354Ssam * Do a remote procedure call (RPC) and wait for its reply.
195178354Ssam * If from_p is non-null, then we are doing broadcast, and
196151207Swpaul * the address from whence the response came is saved there.
197178354Ssam * data:	input/output
198151207Swpaul * from_p:	output
199123474Swpaul */
200123474Swpaulint
201123474Swpaulkrpc_call(struct sockaddr_in *sa, u_int prog, u_int vers, u_int func,
202123474Swpaul    struct mbuf **data, struct mbuf **from_p, int retries)
203123474Swpaul{
204141524Swpaul	struct socket *so;
205123474Swpaul	struct sockaddr_in *sin;
206123474Swpaul	struct mbuf *m, *nam, *mhead, *from, *mopt;
207141524Swpaul	struct rpc_call *call;
208141524Swpaul	struct rpc_reply *reply;
209141524Swpaul	struct uio auio;
210141524Swpaul	int error, rcvflg, timo, secs, len, authlen;
211141524Swpaul	static u_int32_t xid = 0;
212141524Swpaul	char addr[INET_ADDRSTRLEN];
213141524Swpaul	int *ip;
214141524Swpaul	struct timeval tv;
215141524Swpaul
216141524Swpaul	/*
217141524Swpaul	 * Validate address family.
218141524Swpaul	 * Sorry, this is INET specific...
219141524Swpaul	 */
220141524Swpaul	if (sa->sin_family != AF_INET)
221141524Swpaul		return (EAFNOSUPPORT);
222141524Swpaul
223141524Swpaul	/* Free at end if not null. */
224141524Swpaul	nam = mhead = NULL;
225144888Swpaul	from = NULL;
226144888Swpaul
227146230Swpaul	/*
228146230Swpaul	 * Create socket and set its receive timeout.
229146230Swpaul	 */
230146230Swpaul	if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)))
231146230Swpaul		goto out;
232146230Swpaul
233146230Swpaul	m = m_get(M_WAIT, MT_SOOPTS);
234146230Swpaul	tv.tv_sec = 1;
235144888Swpaul	tv.tv_usec = 0;
236144888Swpaul	memcpy(mtod(m, struct timeval *), &tv, sizeof tv);
237144888Swpaul	m->m_len = sizeof(tv);
238144888Swpaul	error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m);
239141963Swpaul	m_freem(m);
240144888Swpaul	if (error)
241145895Swpaul		goto out;
242145895Swpaul
243145895Swpaul	/*
244145895Swpaul	 * Enable broadcast if necessary.
245145895Swpaul	 */
246145895Swpaul	if (from_p) {
247151451Swpaul		int32_t *on;
248151451Swpaul		m = m_get(M_WAIT, MT_SOOPTS);
249141524Swpaul		on = mtod(m, int32_t *);
250141524Swpaul		m->m_len = sizeof(*on);
251141524Swpaul		*on = 1;
252141524Swpaul		error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m);
253141524Swpaul		m_freem(m);
254145895Swpaul		if (error)
255141524Swpaul			goto out;
256141963Swpaul	}
257146230Swpaul
258146230Swpaul	/*
259146230Swpaul	 * Bind the local endpoint to a reserved port,
260146230Swpaul	 * because some NFS servers refuse requests from
261141963Swpaul	 * non-reserved (non-privileged) ports.
262141963Swpaul	 */
263141963Swpaul	MGET(mopt, M_WAIT, MT_SOOPTS);
264145895Swpaul	mopt->m_len = sizeof(int);
265145895Swpaul	ip = mtod(mopt, int *);
266145895Swpaul	*ip = IP_PORTRANGE_LOW;
267151451Swpaul	error = sosetopt(so, IPPROTO_IP, IP_PORTRANGE, mopt);
268141524Swpaul	m_freem(mopt);
269141524Swpaul	if (error)
270141524Swpaul		goto out;
271141524Swpaul
272141524Swpaul	MGET(m, M_WAIT, MT_SONAME);
273141524Swpaul	sin = mtod(m, struct sockaddr_in *);
274141524Swpaul	memset(sin, 0, sizeof(*sin));
275141524Swpaul	sin->sin_len = m->m_len = sizeof(struct sockaddr_in);
276141524Swpaul	sin->sin_family = AF_INET;
277141524Swpaul	sin->sin_addr.s_addr = INADDR_ANY;
278123474Swpaul	sin->sin_port = htons(0);
279123474Swpaul	solock(so);
280123474Swpaul	error = sobind(so, m, &proc0);
281123474Swpaul	sounlock(so);
282123474Swpaul	m_freem(m);
283123474Swpaul	if (error) {
284124709Swpaul		printf("bind failed\n");
285124709Swpaul		goto out;
286124709Swpaul	}
287124709Swpaul
288124709Swpaul	MGET(mopt, M_WAIT, MT_SOOPTS);
289147256Sbrooks	mopt->m_len = sizeof(int);
290124709Swpaul	ip = mtod(mopt, int *);
291131750Swpaul	*ip = IP_PORTRANGE_DEFAULT;
292124709Swpaul	error = sosetopt(so, IPPROTO_IP, IP_PORTRANGE, mopt);
293124709Swpaul	m_freem(mopt);
294124709Swpaul	if (error)
295124709Swpaul		goto out;
296124709Swpaul
297124709Swpaul	/*
298124709Swpaul	 * Setup socket address for the server.
299124709Swpaul	 */
300198786Srpaulo	nam = m_get(M_WAIT, MT_SONAME);
301151207Swpaul	sin = mtod(nam, struct sockaddr_in *);
302124709Swpaul	bcopy(sa, sin, (nam->m_len = sa->sin_len));
303124709Swpaul
304124709Swpaul	/*
305127552Swpaul	 * Prepend RPC message header.
306127552Swpaul	 */
307124709Swpaul	mhead = m_gethdr(M_WAIT, MT_DATA);
308124709Swpaul	mhead->m_next = *data;
309124709Swpaul	call = mtod(mhead, struct rpc_call *);
310124709Swpaul	mhead->m_len = sizeof(*call);
311125061Swpaul	memset(call, 0, sizeof(*call));
312124709Swpaul	/* rpc_call part */
313124709Swpaul	xid = krpc_get_xid();
314124709Swpaul	call->rp_xid = txdr_unsigned(xid);
315124709Swpaul	/* call->rp_direction = 0; */
316124709Swpaul	call->rp_rpcvers = txdr_unsigned(2);
317124709Swpaul	call->rp_prog = txdr_unsigned(prog);
318124709Swpaul	call->rp_vers = txdr_unsigned(vers);
319124709Swpaul	call->rp_proc = txdr_unsigned(func);
320124709Swpaul	/* rpc_auth part (auth_unix as root) */
321195049Srwatson	call->rpc_auth.authtype = txdr_unsigned(RPCAUTH_UNIX);
322124709Swpaul	call->rpc_auth.authlen  = txdr_unsigned(sizeof(struct auth_unix));
323124709Swpaul	/* rpc_verf part (auth_null) */
324124709Swpaul	call->rpc_verf.authtype = 0;
325124709Swpaul	call->rpc_verf.authlen  = 0;
326124709Swpaul
327124709Swpaul	/*
328124709Swpaul	 * Setup packet header
329195049Srwatson	 */
330124709Swpaul	m_calchdrlen(mhead);
331124709Swpaul	mhead->m_pkthdr.ph_ifidx = 0;
332124709Swpaul
333124709Swpaul	/*
334124709Swpaul	 * Send it, repeatedly, until a reply is received,
335195049Srwatson	 * but delay each re-send by an increasing amount.
336124709Swpaul	 * If the delay hits the maximum, start complaining.
337124709Swpaul	 */
338124709Swpaul	for (timo = 0; retries; retries--) {
339124709Swpaul		/* Send RPC request (or re-send). */
340198786Srpaulo		m = m_copym(mhead, 0, M_COPYALL, M_WAIT);
341124709Swpaul		if (m == NULL) {
342124709Swpaul			error = ENOBUFS;
343124709Swpaul			goto out;
344124709Swpaul		}
345124709Swpaul		error = sosend(so, nam, NULL, m, NULL, 0);
346124709Swpaul		if (error) {
347124709Swpaul			printf("krpc_call: sosend: %d\n", error);
348124709Swpaul			goto out;
349124709Swpaul		}
350124709Swpaul		m = NULL;
351124709Swpaul
352198786Srpaulo		/* Determine new timeout. */
353123474Swpaul		if (timo < MAX_RESEND_DELAY)
354123474Swpaul			timo++;
355123474Swpaul		else
356124821Swpaul			printf("RPC timeout for server %s (0x%x) prog %u\n",
357124821Swpaul			    inet_ntop(AF_INET, &sin->sin_addr,
358124821Swpaul				addr, sizeof(addr)),
359124821Swpaul			    ntohl(sin->sin_addr.s_addr), prog);
360124821Swpaul
361124821Swpaul		/*
362124821Swpaul		 * Wait for up to timo seconds for a reply.
363124821Swpaul		 * The socket receive timeout was set to 1 second.
364124821Swpaul		 */
365147256Sbrooks		secs = timo;
366124821Swpaul		while (secs > 0) {
367131750Swpaul			m_freem(from);
368198786Srpaulo			from = NULL;
369124821Swpaul
370124821Swpaul			m_freem(m);
371124821Swpaul			m = NULL;
372124821Swpaul
373124821Swpaul			auio.uio_resid = len = 1<<16;
374198786Srpaulo			auio.uio_procp = NULL;
375124821Swpaul			rcvflg = 0;
376124821Swpaul			error = soreceive(so, &from, &auio, &m, NULL, &rcvflg,
377198786Srpaulo			    0);
378124821Swpaul			if (error == EWOULDBLOCK) {
379124821Swpaul				secs--;
380124821Swpaul				continue;
381124821Swpaul			}
382124821Swpaul			if (error)
383124821Swpaul				goto out;
384124821Swpaul			len -= auio.uio_resid;
385198786Srpaulo
386124821Swpaul			/* Does the reply contain at least a header? */
387124821Swpaul			if (len < MIN_REPLY_HDR)
388124821Swpaul				continue;
389124821Swpaul			if (m->m_len < MIN_REPLY_HDR)
390124821Swpaul				continue;
391124821Swpaul			reply = mtod(m, struct rpc_reply *);
392124821Swpaul
393124821Swpaul			/* Is it the right reply? */
394124821Swpaul			if (reply->rp_direction != txdr_unsigned(RPC_REPLY))
395124821Swpaul				continue;
396124821Swpaul
397124821Swpaul			if (reply->rp_xid != txdr_unsigned(xid))
398124821Swpaul				continue;
399124821Swpaul
400124821Swpaul			/* Was RPC accepted? (authorization OK) */
401124821Swpaul			if (reply->rp_astatus != 0) {
402124821Swpaul				error = fxdr_unsigned(u_int32_t, reply->rp_errno);
403124821Swpaul				printf("rpc denied, error=%d\n", error);
404124821Swpaul				continue;
405124821Swpaul			}
406124821Swpaul
407124821Swpaul			/* Did the call succeed? */
408124821Swpaul			if (reply->rp_status != 0) {
409124821Swpaul				error = fxdr_unsigned(u_int32_t, reply->rp_status);
410124821Swpaul				printf("rpc denied, status=%d\n", error);
411124821Swpaul				continue;
412124821Swpaul			}
413124821Swpaul
414198786Srpaulo			goto gotreply;	/* break two levels */
415124821Swpaul
416124821Swpaul		} /* while secs */
417124821Swpaul	} /* forever send/receive */
418124821Swpaul
419124821Swpaul	error = ETIMEDOUT;
420124821Swpaul	goto out;
421124821Swpaul
422124821Swpaul gotreply:
423124821Swpaul
424124821Swpaul	/*
425124821Swpaul	 * Get RPC reply header into first mbuf,
426124821Swpaul	 * get its length, then strip it off.
427147256Sbrooks	 */
428124821Swpaul	len = sizeof(*reply);
429124821Swpaul	KASSERT(m->m_flags & M_PKTHDR);
430124821Swpaul	if (m->m_pkthdr.len < len) {
431124821Swpaul		error = EBADRPC;
432124821Swpaul		goto out;
433198786Srpaulo	}
434124821Swpaul	if (m->m_len < len) {
435124821Swpaul		m = m_pullup(m, len);
436124821Swpaul		if (m == NULL) {
437124821Swpaul			error = ENOBUFS;
438198786Srpaulo			goto out;
439124821Swpaul		}
440124821Swpaul	}
441124821Swpaul	reply = mtod(m, struct rpc_reply *);
442124821Swpaul	if (reply->rp_auth.authtype != 0) {
443124821Swpaul		authlen = fxdr_unsigned(u_int32_t, reply->rp_auth.authlen);
444124821Swpaul		if (authlen < 0 || authlen > RPCAUTH_MAXSIZ) {
445124821Swpaul			error = EBADRPC;
446124821Swpaul			goto out;
447124821Swpaul		}
448124821Swpaul		len += (authlen + 3) & ~3; /* XXX? */
449124821Swpaul	}
450198786Srpaulo	if (len < 0 || m->m_pkthdr.len < len) {
451124821Swpaul		error = EBADRPC;
452124821Swpaul		goto out;
453124821Swpaul	}
454124821Swpaul	m_adj(m, len);
455198786Srpaulo
456124821Swpaul	/* result */
457124821Swpaul	*data = m;
458124821Swpaul	if (from_p && error == 0) {
459124821Swpaul		*from_p = from;
460124821Swpaul		from = NULL;
461124821Swpaul	}
462124821Swpaul
463124821Swpaul out:
464124821Swpaul	m_freem(nam);
465124821Swpaul	m_freem(mhead);
466124821Swpaul	m_freem(from);
467124821Swpaul	soclose(so, 0);
468124821Swpaul	return error;
469124821Swpaul}
470124821Swpaul
471124821Swpaul/*
472124821Swpaul * eXternal Data Representation routines.
473124821Swpaul * (but with non-standard args...)
474124821Swpaul */
475124821Swpaul
476124821Swpaul/*
477124821Swpaul * String representation for RPC.
478124821Swpaul */
479124821Swpaulstruct xdr_string {
480198786Srpaulo	u_int32_t len;		/* length without null or padding */
481124821Swpaul	char data[4];	/* data (longer, of course) */
482124821Swpaul    /* data is padded to a long-word boundary */
483124821Swpaul};
484124821Swpaul
485124821Swpaulstruct mbuf *
486124821Swpaulxdr_string_encode(char *str, int len)
487124821Swpaul{
488124821Swpaul	struct mbuf *m;
489124821Swpaul	struct xdr_string *xs;
490124821Swpaul	int dlen;	/* padded string length */
491124821Swpaul	int mlen;	/* message length */
492124821Swpaul
493124821Swpaul	dlen = (len + 3) & ~3;
494124821Swpaul	mlen = dlen + 4;
495124821Swpaul
496124821Swpaul	if (mlen > MCLBYTES)		/* If too big, we just can't do it. */
497124821Swpaul		return (NULL);
498124821Swpaul
499124821Swpaul	m = m_get(M_WAIT, MT_DATA);
500124821Swpaul	if (mlen > MLEN) {
501124821Swpaul		MCLGET(m, M_WAIT);
502124821Swpaul		if ((m->m_flags & M_EXT) == 0) {
503124821Swpaul			(void) m_free(m);	/* There can be only one. */
504198786Srpaulo			return (NULL);
505124821Swpaul		}
506124821Swpaul	}
507171602Sthompsa	xs = mtod(m, struct xdr_string *);
508171602Sthompsa	m->m_len = mlen;
509171602Sthompsa	xs->len = txdr_unsigned(len);
510171602Sthompsa	bcopy(str, xs->data, len);
511171602Sthompsa	return (m);
512171602Sthompsa}
513171602Sthompsa
514171602Sthompsastruct mbuf *
515171602Sthompsaxdr_string_decode(struct mbuf *m, char *str, int *len_p)
516171602Sthompsa{
517171602Sthompsa	struct xdr_string *xs;
518171602Sthompsa	int mlen;	/* message length */
519171602Sthompsa	int slen;	/* string length */
520171602Sthompsa
521171602Sthompsa	mlen = sizeof(u_int32_t);
522171602Sthompsa	KASSERT(m->m_flags & M_PKTHDR);
523171602Sthompsa	if (m->m_pkthdr.len < mlen) {
524171602Sthompsa		m_freem(m);
525171602Sthompsa		return (NULL);
526171602Sthompsa	}
527171602Sthompsa	if (m->m_len < mlen) {
528171602Sthompsa		m = m_pullup(m, mlen);
529171602Sthompsa		if (m == NULL)
530171602Sthompsa			return (NULL);
531171602Sthompsa	}
532171602Sthompsa	xs = mtod(m, struct xdr_string *);
533123474Swpaul	slen = fxdr_unsigned(u_int32_t, xs->len);
534123474Swpaul	if (slen < 0 || slen > INT_MAX - 3 - mlen) {
535123474Swpaul		m_freem(m);
536123474Swpaul		return (NULL);
537126706Swpaul	}
538123474Swpaul	mlen += (slen + 3) & ~3;
539123474Swpaul
540123474Swpaul	if (slen > *len_p)
541123474Swpaul		slen = *len_p;
542126706Swpaul	if (m->m_pkthdr.len < mlen) {
543142399Swpaul		m_freem(m);
544141524Swpaul		return (NULL);
545124100Swpaul	}
546178354Ssam	m_copydata(m, 4, slen, str);
547178354Ssam	m_adj(m, mlen);
548171390Sthompsa
549123474Swpaul	str[slen] = '\0';
550123474Swpaul	*len_p = slen;
551123474Swpaul
552179723Scokane	return (m);
553179723Scokane}
554151451Swpaul
555189719Sweongyo
556189488Sweongyo/*
557151207Swpaul * Inet address in RPC messages
558189719Sweongyo * (Note, really four ints, NOT chars.  Blech.)
559189488Sweongyo */
560179498Scokanestruct xdr_inaddr {
561123474Swpaul	u_int32_t atype;
562132953Swpaul	u_int32_t addr[4];
563132953Swpaul};
564132953Swpaul
565132953Swpaulstruct mbuf *
566132953Swpaulxdr_inaddr_encode(struct in_addr *ia)
567132953Swpaul{
568132953Swpaul	struct mbuf *m;
569132953Swpaul	struct xdr_inaddr *xi;
570132953Swpaul	u_int8_t *cp;
571123474Swpaul	u_int32_t *ip;
572123474Swpaul
573123474Swpaul	m = m_get(M_WAIT, MT_DATA);
574142399Swpaul	xi = mtod(m, struct xdr_inaddr *);
575142399Swpaul	m->m_len = sizeof(*xi);
576142399Swpaul	xi->atype = txdr_unsigned(1);
577142804Swpaul	ip = xi->addr;
578142399Swpaul	cp = (u_int8_t *)&ia->s_addr;
579142804Swpaul	*ip++ = txdr_unsigned(*cp++);
580142399Swpaul	*ip++ = txdr_unsigned(*cp++);
581142804Swpaul	*ip++ = txdr_unsigned(*cp++);
582142399Swpaul	*ip++ = txdr_unsigned(*cp++);
583142399Swpaul
584141524Swpaul	return (m);
585141524Swpaul}
586141524Swpaul
587141524Swpaulstruct mbuf *
588141524Swpaulxdr_inaddr_decode(struct mbuf *m, struct in_addr *ia)
589141524Swpaul{
590145485Swpaul	struct xdr_inaddr *xi;
591141524Swpaul	u_int8_t *cp;
592141524Swpaul	u_int32_t *ip;
593141524Swpaul
594141524Swpaul	if (m->m_len < sizeof(*xi)) {
595123474Swpaul		m = m_pullup(m, sizeof(*xi));
596124116Swpaul		if (m == NULL)
597124116Swpaul			return (NULL);
598141524Swpaul	}
599141524Swpaul	xi = mtod(m, struct xdr_inaddr *);
600124116Swpaul	if (xi->atype != txdr_unsigned(1)) {
601123474Swpaul		ia->s_addr = INADDR_ANY;
602142399Swpaul		goto out;
603142399Swpaul	}
604142408Swpaul	ip = xi->addr;
605142408Swpaul	cp = (u_int8_t *)&ia->s_addr;
606123474Swpaul	*cp++ = fxdr_unsigned(u_int8_t, *ip++);
607123474Swpaul	*cp++ = fxdr_unsigned(u_int8_t, *ip++);
608141963Swpaul	*cp++ = fxdr_unsigned(u_int8_t, *ip++);
609141963Swpaul	*cp++ = fxdr_unsigned(u_int8_t, *ip++);
610146230Swpaul
611146230Swpaulout:
612146230Swpaul	m_adj(m, sizeof(*xi));
613123474Swpaul	return (m);
614155311Swpaul}
615155311Swpaul