clnt_udp.c 10.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
/* @(#)clnt_udp.c	2.2 88/08/01 4.0 RPCSRC */
/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 *
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 *
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 *
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 *
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 *
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */
#if !defined(lint) && defined(SCCSIDS)
static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";
#endif

/*
 * clnt_udp.c, Implements a UDP/IP based, client side RPC.
 *
 * Copyright (C) 1984, Sun Microsystems, Inc.
 */

#include <stdio.h>
#include <rpc/rpc.h>
#include <rtthread.h>

/*
 * UDP bases client side rpc operations
 */
static enum clnt_stat clntudp_call(register CLIENT *cl,         /* client handle */
	unsigned long proc,          /* procedure number */
	xdrproc_t xargs,             /* xdr routine for args */
	char* argsp,                 /* pointer to args */
	xdrproc_t xresults,          /* xdr routine for results */
	char* resultsp,              /* pointer to results */
	struct timeval utimeout);

static void clntudp_abort(void);
static void clntudp_geterr(CLIENT *, struct rpc_err *);
static bool_t clntudp_freeres(CLIENT *, xdrproc_t, char*);
static bool_t clntudp_control(CLIENT *, int, char *);
static void clntudp_destroy(CLIENT *);

static struct clnt_ops udp_ops =
{
	clntudp_call,
	clntudp_abort,
	clntudp_geterr,
	clntudp_freeres,
	clntudp_destroy,
	clntudp_control
};

/*
 * Private data kept per client handle
 */
struct cu_data
{
	int cu_sock;
	bool_t cu_closeit;
	struct sockaddr_in cu_raddr;
	int cu_rlen;
	struct timeval cu_wait;
	struct timeval cu_total;
	struct rpc_err cu_error;
	XDR cu_outxdrs;
	unsigned int cu_xdrpos;
	unsigned int cu_sendsz;
	char *cu_outbuf;
	unsigned int cu_recvsz;
	char cu_inbuf[1];
};

/*
 * Create a UDP based client handle.
 * If *sockp<0, *sockp is set to a newly created UPD socket.
 * If raddr->sin_port is 0 a binder on the remote machine
 * is consulted for the correct port number.
 * NB: It is the clients responsibility to close *sockp.
 * NB: The rpch->cl_auth is initialized to null authentication.
 *     Caller may wish to set this something more useful.
 *
 * wait is the amount of time used between retransmitting a call if
 * no response has been heard;  retransmition occurs until the actual
 * rpc call times out.
 *
 * sendsz and recvsz are the maximum allowable packet sizes that can be
 * sent and received.
 */
CLIENT *clntudp_bufcreate(struct sockaddr_in *raddr, 
	unsigned long program, 
	unsigned long version,
	struct timeval wait, 
	int *sockp, 
	unsigned int sendsz,
	unsigned int recvsz)
{
	CLIENT *cl;
	register struct cu_data *cu = NULL;
	struct rpc_msg call_msg;
	static int xid_count = 0;

	cl = (CLIENT *) rt_malloc (sizeof(CLIENT));
	if (cl == NULL)
	{
		rt_kprintf("clntudp_create: out of memory\n");
		goto fooy;
	}
	sendsz = ((sendsz + 3) / 4) * 4;
	recvsz = ((recvsz + 3) / 4) * 4;
	cu = (struct cu_data *) rt_malloc (sizeof(*cu) + sendsz + recvsz);
	if (cu == NULL)
	{
		rt_kprintf("clntudp_create: out of memory\n");
		goto fooy;
	}
	cu->cu_outbuf = &cu->cu_inbuf[recvsz];

	if (raddr->sin_port == 0) {
		unsigned short port;
B
bernard 已提交
138 139 140 141
		extern unsigned short pmap_getport(struct sockaddr_in *address, 
			unsigned long program, 
			unsigned long version, 
			unsigned int protocol);
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204

		if ((port =
			 pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
			goto fooy;
		}
		raddr->sin_port = htons(port);
	}

	cl->cl_ops = &udp_ops;
	cl->cl_private = (char*) cu;
	cu->cu_raddr = *raddr;
	cu->cu_rlen = sizeof(cu->cu_raddr);
	cu->cu_wait = wait;
	cu->cu_total.tv_sec = -1;
	cu->cu_total.tv_usec = -1;
	cu->cu_sendsz = sendsz;
	cu->cu_recvsz = recvsz;
	call_msg.rm_xid = ((unsigned long)rt_thread_self()) ^ ((unsigned long)rt_tick_get()) ^ (xid_count++);
	call_msg.rm_direction = CALL;
	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
	call_msg.rm_call.cb_prog = program;
	call_msg.rm_call.cb_vers = version;
	xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
	if (!xdr_callhdr(&(cu->cu_outxdrs), &call_msg))
	{
		goto fooy;
	}
	cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
	if (*sockp < 0)
	{
		*sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
		if (*sockp < 0)
		{
			rt_kprintf("create socket error\n");
			goto fooy;
		}
		cu->cu_closeit = TRUE;
	}
	else
	{
		cu->cu_closeit = FALSE;
	}
	cu->cu_sock = *sockp;
	cl->cl_auth = authnone_create();
	return (cl);

fooy:
	if (cu) rt_free(cu);
	if (cl) rt_free(cl);

	return ((CLIENT *) NULL);
}

CLIENT *clntudp_create(struct sockaddr_in *raddr, 
	unsigned long program, 
	unsigned long version, 
	struct timeval wait, 
	int *sockp)
{
	return (clntudp_bufcreate(raddr, program, version, wait, sockp,
							  UDPMSGSIZE, UDPMSGSIZE));
}

B
bernard.xiong@gmail.com 已提交
205
static enum clnt_stat clntudp_call(CLIENT *cl, unsigned long proc, 
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
	xdrproc_t xargs, char* argsp, 
	xdrproc_t xresults, char* resultsp, 
	struct timeval utimeout)
{
	register struct cu_data *cu = (struct cu_data *) cl->cl_private;
	register XDR *xdrs;
	register int outlen;
	register int inlen;
	socklen_t fromlen;

	struct sockaddr_in from;
	struct rpc_msg reply_msg;
	XDR reply_xdrs;
	bool_t ok;
	int nrefreshes = 2;			/* number of times to refresh cred */

call_again:
	xdrs = &(cu->cu_outxdrs);
	xdrs->x_op = XDR_ENCODE;
	XDR_SETPOS(xdrs, cu->cu_xdrpos);
B
bernard.xiong@gmail.com 已提交
226

227 228 229
	/*
	 * the transaction is the first thing in the out buffer
	 */
B
bernard.xiong@gmail.com 已提交
230 231
	(*(unsigned long *) (cu->cu_outbuf))++;

232 233
	if ((!XDR_PUTLONG(xdrs, (long *) &proc)) ||
			(!AUTH_MARSHALL(cl->cl_auth, xdrs)) || (!(*xargs) (xdrs, argsp)))
B
bernard 已提交
234 235 236 237
    {
        cu->cu_error.re_status = RPC_CANTENCODEARGS;
		return RPC_CANTENCODEARGS;
    }
238 239 240 241 242 243 244 245
	outlen = (int) XDR_GETPOS(xdrs);

send_again:
	if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
			   (struct sockaddr *) &(cu->cu_raddr), cu->cu_rlen)
			!= outlen)
	{
		cu->cu_error.re_errno = errno;
B
bernard 已提交
246 247 248
        cu->cu_error.re_status = RPC_CANTSEND;
        
		return RPC_CANTSEND;
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
	}

	/*
	 * sub-optimal code appears here because we have
	 * some clock time to spare while the packets are in flight.
	 * (We assume that this is actually only executed once.)
	 */
	reply_msg.acpted_rply.ar_verf = _null_auth;
	reply_msg.acpted_rply.ar_results.where = resultsp;
	reply_msg.acpted_rply.ar_results.proc = xresults;

	/* do recv */
	do
	{
		fromlen = sizeof(struct sockaddr);

		inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
						 (int) cu->cu_recvsz, 0,
						 (struct sockaddr *) &from, &fromlen);
	}while (inlen < 0 && errno == EINTR);

	if (inlen < 4)
	{
		rt_kprintf("recv error, len %d\n", inlen);
		cu->cu_error.re_errno = errno;
B
bernard 已提交
274 275 276
		cu->cu_error.re_status = RPC_CANTRECV;
		
		return RPC_CANTRECV;
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
	}

	/* see if reply transaction id matches sent id */
	if (*((uint32_t *) (cu->cu_inbuf)) != *((uint32_t *) (cu->cu_outbuf)))
		goto send_again;

	/* we now assume we have the proper reply */

	/*
	 * now decode and validate the response
	 */
	xdrmem_create(&reply_xdrs, cu->cu_inbuf, (unsigned int) inlen, XDR_DECODE);
	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
	/* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
	if (ok)
	{
		_seterr_reply(&reply_msg, &(cu->cu_error));
		if (cu->cu_error.re_status == RPC_SUCCESS)
		{
			if (!AUTH_VALIDATE(cl->cl_auth,
							   &reply_msg.acpted_rply.ar_verf))
			{
				cu->cu_error.re_status = RPC_AUTHERROR;
				cu->cu_error.re_why = AUTH_INVALIDRESP;
			}
			if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
			{
B
bernard 已提交
304 305
				extern bool_t xdr_opaque_auth(XDR *xdrs, struct opaque_auth *ap);
				
306
				xdrs->x_op = XDR_FREE;
B
bernard.xiong@gmail.com 已提交
307
				(void) xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
308 309 310 311 312 313 314 315 316 317
			}
		} /* end successful completion */
		else
		{
			/* maybe our credentials need to be refreshed ... */
			if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth))
			{
				nrefreshes--;
				goto call_again;
			}
B
bernard.xiong@gmail.com 已提交
318
		} /* end of unsuccessful completion */
319 320 321 322 323 324
	} /* end of valid reply message */
	else
	{
		cu->cu_error.re_status = RPC_CANTDECODERES;
	}

B
bernard 已提交
325
	return (enum clnt_stat)(cu->cu_error.re_status);
326 327
}

B
bernard.xiong 已提交
328
static void clntudp_geterr(CLIENT *cl, struct rpc_err *errp)
329 330 331 332 333 334
{
	register struct cu_data *cu = (struct cu_data *) cl->cl_private;

	*errp = cu->cu_error;
}

B
bernard.xiong 已提交
335
static bool_t clntudp_freeres(CLIENT *cl, xdrproc_t xdr_res, char* res_ptr)
336 337 338 339 340 341 342 343
{
	register struct cu_data *cu = (struct cu_data *) cl->cl_private;
	register XDR *xdrs = &(cu->cu_outxdrs);

	xdrs->x_op = XDR_FREE;
	return ((*xdr_res) (xdrs, res_ptr));
}

B
bernard.xiong 已提交
344
static void clntudp_abort()
345 346 347
{
}

B
bernard.xiong 已提交
348
static bool_t clntudp_control(CLIENT *cl, int request, char *info)
349 350 351 352 353 354
{
	register struct cu_data *cu = (struct cu_data *) cl->cl_private;

	switch (request)
	{
	case CLSET_TIMEOUT:
B
bernard.xiong@gmail.com 已提交
355 356 357
		{
		int mtimeout;

358
		cu->cu_total = *(struct timeval *) info;
B
bernard.xiong@gmail.com 已提交
359
		mtimeout = ((cu->cu_total.tv_sec * 1000) + ((cu->cu_total.tv_usec + 500)/1000));
360

B
bernard.xiong@gmail.com 已提交
361 362 363 364
		/* set socket option, note: lwip only support msecond timeout */
		setsockopt(cu->cu_sock, SOL_SOCKET, SO_RCVTIMEO, 
			&mtimeout, sizeof(mtimeout));
		}
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
		break;
	case CLGET_TIMEOUT:
		*(struct timeval *) info = cu->cu_total;
		break;
	case CLSET_RETRY_TIMEOUT:
		cu->cu_wait = *(struct timeval *) info;
		break;
	case CLGET_RETRY_TIMEOUT:
		*(struct timeval *) info = cu->cu_wait;
		break;
	case CLGET_SERVER_ADDR:
		*(struct sockaddr_in *) info = cu->cu_raddr;
		break;
	default:
		return (FALSE);
	}
	return (TRUE);
}

B
bernard.xiong 已提交
384
static void clntudp_destroy(CLIENT *cl)
385 386 387 388 389 390 391 392 393 394 395 396
{
	register struct cu_data *cu = (struct cu_data *) cl->cl_private;

	if (cu->cu_closeit)
	{
		lwip_close(cu->cu_sock);
	}

	XDR_DESTROY(&(cu->cu_outxdrs));
	rt_free(cu);
	rt_free(cl);
}