xref: /nrf52832-nimble/rt-thread/components/dfs/filesystems/nfs/rpc/clnt_udp.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
1 /*
2  * Copyright (c) 2006-2018, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  */
9 /* @(#)clnt_udp.c	2.2 88/08/01 4.0 RPCSRC */
10 /*
11  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
12  * unrestricted use provided that this legend is included on all tape
13  * media and as a part of the software program in whole or part.  Users
14  * may copy or modify Sun RPC without charge, but are not authorized
15  * to license or distribute it to anyone else except as part of a product or
16  * program developed by the user.
17  *
18  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
19  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
21  *
22  * Sun RPC is provided with no support and without any obligation on the
23  * part of Sun Microsystems, Inc. to assist in its use, correction,
24  * modification or enhancement.
25  *
26  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
27  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
28  * OR ANY PART THEREOF.
29  *
30  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
31  * or profits or other special, indirect and consequential damages, even if
32  * Sun has been advised of the possibility of such damages.
33  *
34  * Sun Microsystems, Inc.
35  * 2550 Garcia Avenue
36  * Mountain View, California  94043
37  */
38 #if !defined(lint) && defined(SCCSIDS)
39 static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";
40 #endif
41 
42 /*
43  * clnt_udp.c, Implements a UDP/IP based, client side RPC.
44  *
45  * Copyright (C) 1984, Sun Microsystems, Inc.
46  */
47 
48 #include <stdio.h>
49 #include <rpc/rpc.h>
50 #include <rtthread.h>
51 
52 /*
53  * UDP bases client side rpc operations
54  */
55 static enum clnt_stat clntudp_call(register CLIENT *cl,         /* client handle */
56 	unsigned long proc,          /* procedure number */
57 	xdrproc_t xargs,             /* xdr routine for args */
58 	char* argsp,                 /* pointer to args */
59 	xdrproc_t xresults,          /* xdr routine for results */
60 	char* resultsp,              /* pointer to results */
61 	struct timeval utimeout);
62 
63 static void clntudp_abort(void);
64 static void clntudp_geterr(CLIENT *, struct rpc_err *);
65 static bool_t clntudp_freeres(CLIENT *, xdrproc_t, char*);
66 static bool_t clntudp_control(CLIENT *, int, char *);
67 static void clntudp_destroy(CLIENT *);
68 
69 static struct clnt_ops udp_ops =
70 {
71 	clntudp_call,
72 	clntudp_abort,
73 	clntudp_geterr,
74 	clntudp_freeres,
75 	clntudp_destroy,
76 	clntudp_control
77 };
78 
79 /*
80  * Private data kept per client handle
81  */
82 struct cu_data
83 {
84 	int cu_sock;
85 	bool_t cu_closeit;
86 	struct sockaddr_in cu_raddr;
87 	int cu_rlen;
88 	struct timeval cu_wait;
89 	struct timeval cu_total;
90 	struct rpc_err cu_error;
91 	XDR cu_outxdrs;
92 	unsigned int cu_xdrpos;
93 	unsigned int cu_sendsz;
94 	char *cu_outbuf;
95 	unsigned int cu_recvsz;
96 	char cu_inbuf[1];
97 };
98 
99 /*
100  * Create a UDP based client handle.
101  * If *sockp<0, *sockp is set to a newly created UPD socket.
102  * If raddr->sin_port is 0 a binder on the remote machine
103  * is consulted for the correct port number.
104  * NB: It is the clients responsibility to close *sockp.
105  * NB: The rpch->cl_auth is initialized to null authentication.
106  *     Caller may wish to set this something more useful.
107  *
108  * wait is the amount of time used between retransmitting a call if
109  * no response has been heard;  retransmition occurs until the actual
110  * rpc call times out.
111  *
112  * sendsz and recvsz are the maximum allowable packet sizes that can be
113  * sent and received.
114  */
115 CLIENT *clntudp_bufcreate(struct sockaddr_in *raddr,
116 	unsigned long program,
117 	unsigned long version,
118 	struct timeval wait,
119 	int *sockp,
120 	unsigned int sendsz,
121 	unsigned int recvsz)
122 {
123 	CLIENT *cl;
124 	register struct cu_data *cu = NULL;
125 	struct rpc_msg call_msg;
126 	static int xid_count = 0;
127 
128 	cl = (CLIENT *) rt_malloc (sizeof(CLIENT));
129 	if (cl == NULL)
130 	{
131 		rt_kprintf("clntudp_create: out of memory\n");
132 		goto fooy;
133 	}
134 	sendsz = ((sendsz + 3) / 4) * 4;
135 	recvsz = ((recvsz + 3) / 4) * 4;
136 	cu = (struct cu_data *) rt_malloc (sizeof(*cu) + sendsz + recvsz);
137 	if (cu == NULL)
138 	{
139 		rt_kprintf("clntudp_create: out of memory\n");
140 		goto fooy;
141 	}
142 	cu->cu_outbuf = &cu->cu_inbuf[recvsz];
143 
144 	if (raddr->sin_port == 0) {
145 		unsigned short port;
146 		extern unsigned short pmap_getport(struct sockaddr_in *address,
147 			unsigned long program,
148 			unsigned long version,
149 			unsigned int protocol);
150 
151 		if ((port =
152 			 pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
153 			goto fooy;
154 		}
155 		raddr->sin_port = htons(port);
156 	}
157 
158 	cl->cl_ops = &udp_ops;
159 	cl->cl_private = (char*) cu;
160 	cu->cu_raddr = *raddr;
161 	cu->cu_rlen = sizeof(cu->cu_raddr);
162 	cu->cu_wait = wait;
163 	cu->cu_total.tv_sec = -1;
164 	cu->cu_total.tv_usec = -1;
165 	cu->cu_sendsz = sendsz;
166 	cu->cu_recvsz = recvsz;
167 	call_msg.rm_xid = ((unsigned long)rt_thread_self()) ^ ((unsigned long)rt_tick_get()) ^ (xid_count++);
168 	call_msg.rm_direction = CALL;
169 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
170 	call_msg.rm_call.cb_prog = program;
171 	call_msg.rm_call.cb_vers = version;
172 	xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
173 	if (!xdr_callhdr(&(cu->cu_outxdrs), &call_msg))
174 	{
175 		goto fooy;
176 	}
177 	cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
178 	if (*sockp < 0)
179 	{
180 		*sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
181 		if (*sockp < 0)
182 		{
183 			rt_kprintf("create socket error\n");
184 			goto fooy;
185 		}
186 		cu->cu_closeit = TRUE;
187 	}
188 	else
189 	{
190 		cu->cu_closeit = FALSE;
191 	}
192 	cu->cu_sock = *sockp;
193 	cl->cl_auth = authnone_create();
194 	return (cl);
195 
196 fooy:
197 	if (cu) rt_free(cu);
198 	if (cl) rt_free(cl);
199 
200 	return ((CLIENT *) NULL);
201 }
202 
203 CLIENT *clntudp_create(struct sockaddr_in *raddr,
204 	unsigned long program,
205 	unsigned long version,
206 	struct timeval wait,
207 	int *sockp)
208 {
209 	return (clntudp_bufcreate(raddr, program, version, wait, sockp,
210 							  UDPMSGSIZE, UDPMSGSIZE));
211 }
212 
213 static enum clnt_stat clntudp_call(CLIENT *cl, unsigned long proc,
214 	xdrproc_t xargs, char* argsp,
215 	xdrproc_t xresults, char* resultsp,
216 	struct timeval utimeout)
217 {
218 	register struct cu_data *cu = (struct cu_data *) cl->cl_private;
219 	register XDR *xdrs;
220 	register int outlen;
221 	register int inlen;
222 	socklen_t fromlen;
223 
224 	struct sockaddr_in from;
225 	struct rpc_msg reply_msg;
226 	XDR reply_xdrs;
227 	bool_t ok;
228 	int nrefreshes = 2;			/* number of times to refresh cred */
229 
230 call_again:
231 	xdrs = &(cu->cu_outxdrs);
232 	xdrs->x_op = XDR_ENCODE;
233 	XDR_SETPOS(xdrs, cu->cu_xdrpos);
234 
235 	/*
236 	 * the transaction is the first thing in the out buffer
237 	 */
238 	(*(unsigned long *) (cu->cu_outbuf))++;
239 
240 	if ((!XDR_PUTLONG(xdrs, (long *) &proc)) ||
241 			(!AUTH_MARSHALL(cl->cl_auth, xdrs)) || (!(*xargs) (xdrs, argsp)))
242     {
243         cu->cu_error.re_status = RPC_CANTENCODEARGS;
244 		return RPC_CANTENCODEARGS;
245     }
246 	outlen = (int) XDR_GETPOS(xdrs);
247 
248 send_again:
249 	if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
250 			   (struct sockaddr *) &(cu->cu_raddr), cu->cu_rlen)
251 			!= outlen)
252 	{
253 		cu->cu_error.re_errno = errno;
254         cu->cu_error.re_status = RPC_CANTSEND;
255 
256 		return RPC_CANTSEND;
257 	}
258 
259 	/*
260 	 * sub-optimal code appears here because we have
261 	 * some clock time to spare while the packets are in flight.
262 	 * (We assume that this is actually only executed once.)
263 	 */
264 	reply_msg.acpted_rply.ar_verf = _null_auth;
265 	reply_msg.acpted_rply.ar_results.where = resultsp;
266 	reply_msg.acpted_rply.ar_results.proc = xresults;
267 
268 	/* do recv */
269 	do
270 	{
271 		fromlen = sizeof(struct sockaddr);
272 
273 		inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
274 						 (int) cu->cu_recvsz, 0,
275 						 (struct sockaddr *) &from, &fromlen);
276 	}while (inlen < 0 && errno == EINTR);
277 
278 	if (inlen < 4)
279 	{
280 		rt_kprintf("recv error, len %d\n", inlen);
281 		cu->cu_error.re_errno = errno;
282 		cu->cu_error.re_status = RPC_CANTRECV;
283 
284 		return RPC_CANTRECV;
285 	}
286 
287 	/* see if reply transaction id matches sent id */
288 	if (*((uint32_t *) (cu->cu_inbuf)) != *((uint32_t *) (cu->cu_outbuf)))
289 		goto send_again;
290 
291 	/* we now assume we have the proper reply */
292 
293 	/*
294 	 * now decode and validate the response
295 	 */
296 	xdrmem_create(&reply_xdrs, cu->cu_inbuf, (unsigned int) inlen, XDR_DECODE);
297 	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
298 	/* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
299 	if (ok)
300 	{
301 		_seterr_reply(&reply_msg, &(cu->cu_error));
302 		if (cu->cu_error.re_status == RPC_SUCCESS)
303 		{
304 			if (!AUTH_VALIDATE(cl->cl_auth,
305 							   &reply_msg.acpted_rply.ar_verf))
306 			{
307 				cu->cu_error.re_status = RPC_AUTHERROR;
308 				cu->cu_error.re_why = AUTH_INVALIDRESP;
309 			}
310 			if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
311 			{
312 				extern bool_t xdr_opaque_auth(XDR *xdrs, struct opaque_auth *ap);
313 
314 				xdrs->x_op = XDR_FREE;
315 				(void) xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
316 			}
317 		} /* end successful completion */
318 		else
319 		{
320 			/* maybe our credentials need to be refreshed ... */
321 			if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth))
322 			{
323 				nrefreshes--;
324 				goto call_again;
325 			}
326 		} /* end of unsuccessful completion */
327 	} /* end of valid reply message */
328 	else
329 	{
330 		cu->cu_error.re_status = RPC_CANTDECODERES;
331 	}
332 
333 	return (enum clnt_stat)(cu->cu_error.re_status);
334 }
335 
336 static void clntudp_geterr(CLIENT *cl, struct rpc_err *errp)
337 {
338 	register struct cu_data *cu = (struct cu_data *) cl->cl_private;
339 
340 	*errp = cu->cu_error;
341 }
342 
343 static bool_t clntudp_freeres(CLIENT *cl, xdrproc_t xdr_res, char* res_ptr)
344 {
345 	register struct cu_data *cu = (struct cu_data *) cl->cl_private;
346 	register XDR *xdrs = &(cu->cu_outxdrs);
347 
348 	xdrs->x_op = XDR_FREE;
349 	return ((*xdr_res) (xdrs, res_ptr));
350 }
351 
352 static void clntudp_abort()
353 {
354 }
355 
356 static bool_t clntudp_control(CLIENT *cl, int request, char *info)
357 {
358 	register struct cu_data *cu = (struct cu_data *) cl->cl_private;
359 
360 	switch (request)
361 	{
362 	case CLSET_TIMEOUT:
363 		{
364 		int mtimeout;
365 
366 		cu->cu_total = *(struct timeval *) info;
367 		mtimeout = ((cu->cu_total.tv_sec * 1000) + ((cu->cu_total.tv_usec + 500)/1000));
368 
369 		/* set socket option, note: lwip only support msecond timeout */
370 		setsockopt(cu->cu_sock, SOL_SOCKET, SO_RCVTIMEO,
371 			&mtimeout, sizeof(mtimeout));
372 		}
373 		break;
374 	case CLGET_TIMEOUT:
375 		*(struct timeval *) info = cu->cu_total;
376 		break;
377 	case CLSET_RETRY_TIMEOUT:
378 		cu->cu_wait = *(struct timeval *) info;
379 		break;
380 	case CLGET_RETRY_TIMEOUT:
381 		*(struct timeval *) info = cu->cu_wait;
382 		break;
383 	case CLGET_SERVER_ADDR:
384 		*(struct sockaddr_in *) info = cu->cu_raddr;
385 		break;
386 	default:
387 		return (FALSE);
388 	}
389 	return (TRUE);
390 }
391 
392 static void clntudp_destroy(CLIENT *cl)
393 {
394 	register struct cu_data *cu = (struct cu_data *) cl->cl_private;
395 
396 	if (cu->cu_closeit)
397 	{
398 		lwip_close(cu->cu_sock);
399 	}
400 
401 	XDR_DESTROY(&(cu->cu_outxdrs));
402 	rt_free(cu);
403 	rt_free(cl);
404 }
405