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 */
clntudp_bufcreate(struct sockaddr_in * raddr,unsigned long program,unsigned long version,struct timeval wait,int * sockp,unsigned int sendsz,unsigned int recvsz)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
clntudp_create(struct sockaddr_in * raddr,unsigned long program,unsigned long version,struct timeval wait,int * sockp)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
clntudp_call(CLIENT * cl,unsigned long proc,xdrproc_t xargs,char * argsp,xdrproc_t xresults,char * resultsp,struct timeval utimeout)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
clntudp_geterr(CLIENT * cl,struct rpc_err * errp)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
clntudp_freeres(CLIENT * cl,xdrproc_t xdr_res,char * res_ptr)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
clntudp_abort()352 static void clntudp_abort()
353 {
354 }
355
clntudp_control(CLIENT * cl,int request,char * info)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
clntudp_destroy(CLIENT * cl)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