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