1 /*
2  * Copyright (c) 2006-2021, 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             rt_kprintf("pmap_getport failure\n");
154             goto fooy;
155         }
156         raddr->sin_port = htons(port);
157     }
158 
159     cl->cl_ops = &udp_ops;
160     cl->cl_private = (char*) cu;
161     cu->cu_raddr = *raddr;
162     cu->cu_rlen = sizeof(cu->cu_raddr);
163     cu->cu_wait = wait;
164     cu->cu_total.tv_sec = -1;
165     cu->cu_total.tv_usec = -1;
166     cu->cu_sendsz = sendsz;
167     cu->cu_recvsz = recvsz;
168     call_msg.rm_xid = (uint32_t)(((unsigned long)rt_thread_self()) ^ ((unsigned long)rt_tick_get()) ^ (xid_count++));
169     call_msg.rm_direction = CALL;
170     call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
171     call_msg.rm_call.cb_prog = program;
172     call_msg.rm_call.cb_vers = version;
173     xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
174     if (!xdr_callhdr(&(cu->cu_outxdrs), &call_msg))
175     {
176         rt_kprintf("xdr_callhdr failure\n");
177         goto fooy;
178     }
179     cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
180     if (*sockp < 0)
181     {
182         *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
183         if (*sockp < 0)
184         {
185             rt_kprintf("create socket error\n");
186             goto fooy;
187         }
188         cu->cu_closeit = TRUE;
189     }
190     else
191     {
192         cu->cu_closeit = FALSE;
193     }
194     cu->cu_sock = *sockp;
195     cl->cl_auth = authnone_create();
196     return (cl);
197 
198 fooy:
199     if (cu) rt_free(cu);
200     if (cl) rt_free(cl);
201 
202     return ((CLIENT *) NULL);
203 }
204 
clntudp_create(struct sockaddr_in * raddr,unsigned long program,unsigned long version,struct timeval wait,int * sockp)205 CLIENT *clntudp_create(struct sockaddr_in *raddr,
206     unsigned long program,
207     unsigned long version,
208     struct timeval wait,
209     int *sockp)
210 {
211     return (clntudp_bufcreate(raddr, program, version, wait, sockp,
212                               UDPMSGSIZE, UDPMSGSIZE));
213 }
214 
clntudp_call(CLIENT * cl,unsigned long proc,xdrproc_t xargs,char * argsp,xdrproc_t xresults,char * resultsp,struct timeval utimeout)215 static enum clnt_stat clntudp_call(CLIENT *cl, unsigned long proc,
216     xdrproc_t xargs, char* argsp,
217     xdrproc_t xresults, char* resultsp,
218     struct timeval utimeout)
219 {
220     register struct cu_data *cu = (struct cu_data *) cl->cl_private;
221     register XDR *xdrs;
222     register int outlen;
223     register int inlen;
224     socklen_t fromlen;
225 
226     struct sockaddr_in from;
227     struct rpc_msg reply_msg;
228     XDR reply_xdrs;
229     bool_t ok;
230     int nrefreshes = 2;         /* number of times to refresh cred */
231 
232 call_again:
233     xdrs = &(cu->cu_outxdrs);
234     xdrs->x_op = XDR_ENCODE;
235     XDR_SETPOS(xdrs, cu->cu_xdrpos);
236 
237     /*
238      * the transaction is the first thing in the out buffer
239      */
240     (*(unsigned long *) (cu->cu_outbuf))++;
241 
242     if ((!XDR_PUTLONG(xdrs, (long *) &proc)) ||
243             (!AUTH_MARSHALL(cl->cl_auth, xdrs)) || (!(*xargs) (xdrs, argsp)))
244     {
245         cu->cu_error.re_status = RPC_CANTENCODEARGS;
246         return RPC_CANTENCODEARGS;
247     }
248     outlen = (int) XDR_GETPOS(xdrs);
249 
250 send_again:
251     if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
252                (struct sockaddr *) &(cu->cu_raddr), cu->cu_rlen)
253             != outlen)
254     {
255         cu->cu_error.re_errno = errno;
256         cu->cu_error.re_status = RPC_CANTSEND;
257 
258         return RPC_CANTSEND;
259     }
260 
261     /*
262      * sub-optimal code appears here because we have
263      * some clock time to spare while the packets are in flight.
264      * (We assume that this is actually only executed once.)
265      */
266     reply_msg.acpted_rply.ar_verf = _null_auth;
267     reply_msg.acpted_rply.ar_results.where = resultsp;
268     reply_msg.acpted_rply.ar_results.proc = xresults;
269 
270     /* do recv */
271     do
272     {
273         fromlen = sizeof(struct sockaddr);
274 
275         inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
276                          (int) cu->cu_recvsz, 0,
277                          (struct sockaddr *) &from, &fromlen);
278     }while (inlen < 0 && errno == EINTR);
279 
280     if (inlen < 4)
281     {
282         rt_kprintf("recv error, len %d\n", inlen);
283         cu->cu_error.re_errno = errno;
284         cu->cu_error.re_status = RPC_CANTRECV;
285 
286         return RPC_CANTRECV;
287     }
288 
289     /* see if reply transaction id matches sent id */
290     if (*((uint32_t *) (cu->cu_inbuf)) != *((uint32_t *) (cu->cu_outbuf)))
291         goto send_again;
292 
293     /* we now assume we have the proper reply */
294 
295     /*
296      * now decode and validate the response
297      */
298     xdrmem_create(&reply_xdrs, cu->cu_inbuf, (unsigned int) inlen, XDR_DECODE);
299     ok = xdr_replymsg(&reply_xdrs, &reply_msg);
300     /* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
301     if (ok)
302     {
303         _seterr_reply(&reply_msg, &(cu->cu_error));
304         if (cu->cu_error.re_status == RPC_SUCCESS)
305         {
306             if (!AUTH_VALIDATE(cl->cl_auth,
307                                &reply_msg.acpted_rply.ar_verf))
308             {
309                 cu->cu_error.re_status = RPC_AUTHERROR;
310                 cu->cu_error.re_why = AUTH_INVALIDRESP;
311             }
312             if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
313             {
314                 extern bool_t xdr_opaque_auth(XDR *xdrs, struct opaque_auth *ap);
315 
316                 xdrs->x_op = XDR_FREE;
317                 (void) xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
318             }
319         } /* end successful completion */
320         else
321         {
322             /* maybe our credentials need to be refreshed ... */
323             if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth))
324             {
325                 nrefreshes--;
326                 goto call_again;
327             }
328         } /* end of unsuccessful completion */
329     } /* end of valid reply message */
330     else
331     {
332         cu->cu_error.re_status = RPC_CANTDECODERES;
333     }
334 
335     return (enum clnt_stat)(cu->cu_error.re_status);
336 }
337 
clntudp_geterr(CLIENT * cl,struct rpc_err * errp)338 static void clntudp_geterr(CLIENT *cl, struct rpc_err *errp)
339 {
340     register struct cu_data *cu = (struct cu_data *) cl->cl_private;
341 
342     *errp = cu->cu_error;
343 }
344 
clntudp_freeres(CLIENT * cl,xdrproc_t xdr_res,char * res_ptr)345 static bool_t clntudp_freeres(CLIENT *cl, xdrproc_t xdr_res, char* res_ptr)
346 {
347     register struct cu_data *cu = (struct cu_data *) cl->cl_private;
348     register XDR *xdrs = &(cu->cu_outxdrs);
349 
350     xdrs->x_op = XDR_FREE;
351     return ((*xdr_res) (xdrs, res_ptr));
352 }
353 
clntudp_abort()354 static void clntudp_abort()
355 {
356 }
357 
clntudp_control(CLIENT * cl,int request,char * info)358 static bool_t clntudp_control(CLIENT *cl, int request, char *info)
359 {
360     register struct cu_data *cu = (struct cu_data *) cl->cl_private;
361 
362     switch (request)
363     {
364     case CLSET_TIMEOUT:
365         {
366         int mtimeout;
367 
368         cu->cu_total = *(struct timeval *) info;
369         mtimeout = ((cu->cu_total.tv_sec * 1000) + ((cu->cu_total.tv_usec + 500)/1000));
370 
371         /* set socket option, note: lwip only support msecond timeout */
372         setsockopt(cu->cu_sock, SOL_SOCKET, SO_RCVTIMEO,
373             &mtimeout, sizeof(mtimeout));
374         }
375         break;
376     case CLGET_TIMEOUT:
377         *(struct timeval *) info = cu->cu_total;
378         break;
379     case CLSET_RETRY_TIMEOUT:
380         cu->cu_wait = *(struct timeval *) info;
381         break;
382     case CLGET_RETRY_TIMEOUT:
383         *(struct timeval *) info = cu->cu_wait;
384         break;
385     case CLGET_SERVER_ADDR:
386         *(struct sockaddr_in *) info = cu->cu_raddr;
387         break;
388     default:
389         return (FALSE);
390     }
391     return (TRUE);
392 }
393 
clntudp_destroy(CLIENT * cl)394 static void clntudp_destroy(CLIENT *cl)
395 {
396     register struct cu_data *cu = (struct cu_data *) cl->cl_private;
397 
398     if (cu->cu_closeit)
399     {
400         lwip_close(cu->cu_sock);
401     }
402 
403     XDR_DESTROY(&(cu->cu_outxdrs));
404     rt_free(cu);
405     rt_free(cl);
406 }
407