xref: /dragonfly/usr.sbin/ypbind/yp_ping.c (revision 4caa7869)
1 /*
2  * Copyright (c) 1996, 1997
3  *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by Bill Paul.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#)from: clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro
33  * @(#)from: clnt_udp.c	2.2 88/08/01 4.0 RPCSRC
34  * $FreeBSD: src/usr.sbin/ypbind/yp_ping.c,v 1.6.2.1 2002/02/15 00:46:59 des Exp $
35  * $DragonFly: src/usr.sbin/ypbind/yp_ping.c,v 1.3 2003/11/22 19:30:57 asmodai Exp $
36  */
37 
38 /*
39  * What follows is a special version of clntudp_call() that has been
40  * hacked to send requests and receive replies asynchronously. Similar
41  * magic is used inside rpc.nisd(8) for the special non-blocking,
42  * non-fork()ing, non-threading callback support.
43  */
44 
45 /*
46  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
47  * unrestricted use provided that this legend is included on all tape
48  * media and as a part of the software program in whole or part.  Users
49  * may copy or modify Sun RPC without charge, but are not authorized
50  * to license or distribute it to anyone else except as part of a product or
51  * program developed by the user.
52  *
53  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
54  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
55  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
56  *
57  * Sun RPC is provided with no support and without any obligation on the
58  * part of Sun Microsystems, Inc. to assist in its use, correction,
59  * modification or enhancement.
60  *
61  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
62  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
63  * OR ANY PART THEREOF.
64  *
65  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
66  * or profits or other special, indirect and consequential damages, even if
67  * Sun has been advised of the possibility of such damages.
68  *
69  * Sun Microsystems, Inc.
70  * 2550 Garcia Avenue
71  * Mountain View, California  94043
72  */
73 
74 /*
75  * clnt_udp.c, Implements a UDP/IP based, client side RPC.
76  *
77  * Copyright (C) 1984, Sun Microsystems, Inc.
78  */
79 
80 #include <errno.h>
81 #include <netdb.h>
82 #include <stdio.h>
83 #include <stdlib.h>
84 #include <string.h>
85 #include <unistd.h>
86 #include <rpc/rpc.h>
87 #include <rpc/pmap_clnt.h>
88 #include <rpc/pmap_prot.h>
89 #include <rpcsvc/yp.h>
90 #include <sys/socket.h>
91 #include <sys/ioctl.h>
92 #include <net/if.h>
93 #include "yp_ping.h"
94 
95 #ifndef timeradd
96 #ifndef _KERNEL		/* use timevaladd/timevalsub in kernel */
97 /* NetBSD/OpenBSD compatible interfaces */
98 #define timeradd(tvp, uvp, vvp)						\
99 	do {								\
100 		(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;		\
101 		(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;	\
102 		if ((vvp)->tv_usec >= 1000000) {			\
103 			(vvp)->tv_sec++;				\
104 			(vvp)->tv_usec -= 1000000;			\
105 		}							\
106 	} while (0)
107 #define timersub(tvp, uvp, vvp)						\
108 	do {								\
109 		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
110 		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
111 		if ((vvp)->tv_usec < 0) {				\
112 			(vvp)->tv_sec--;				\
113 			(vvp)->tv_usec += 1000000;			\
114 		}							\
115 	} while (0)
116 #endif
117 #endif
118 
119 /*
120  * Private data kept per client handle
121  */
122 struct cu_data {
123 	int		   cu_sock;
124 	bool_t		   cu_closeit;
125 	struct sockaddr_in cu_raddr;
126 	int		   cu_rlen;
127 	struct timeval	   cu_wait;
128 	struct timeval     cu_total;
129 	struct rpc_err	   cu_error;
130 	XDR		   cu_outxdrs;
131 	u_int		   cu_xdrpos;
132 	u_int		   cu_sendsz;
133 	char		   *cu_outbuf;
134 	u_int		   cu_recvsz;
135 	char		   cu_inbuf[1];
136 };
137 
138 static enum clnt_stat
139 clntudp_a_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
140 	register CLIENT	*cl;		/* client handle */
141 	u_long		proc;		/* procedure number */
142 	xdrproc_t	xargs;		/* xdr routine for args */
143 	caddr_t		argsp;		/* pointer to args */
144 	xdrproc_t	xresults;	/* xdr routine for results */
145 	caddr_t		resultsp;	/* pointer to results */
146 	struct timeval	utimeout;	/* seconds to wait before giving up */
147 {
148 	register struct cu_data *cu = (struct cu_data *)cl->cl_private;
149 	register XDR *xdrs;
150 	register int outlen = 0;
151 	register int inlen;
152 	int fromlen;
153 	fd_set *fds, readfds;
154 	struct sockaddr_in from;
155 	struct rpc_msg reply_msg;
156 	XDR reply_xdrs;
157 	struct timeval time_waited, start, after, tmp1, tmp2, tv;
158 	bool_t ok;
159 	int nrefreshes = 2;	/* number of times to refresh cred */
160 	struct timeval timeout;
161 
162 	if (cu->cu_total.tv_usec == -1)
163 		timeout = utimeout;     /* use supplied timeout */
164 	else
165 		timeout = cu->cu_total; /* use default timeout */
166 
167 	if (cu->cu_sock + 1 > FD_SETSIZE) {
168 		int bytes = howmany(cu->cu_sock + 1, NFDBITS) * sizeof(fd_mask);
169 		fds = (fd_set *)malloc(bytes);
170 		if (fds == NULL)
171 			return (cu->cu_error.re_status = RPC_CANTSEND);
172 		memset(fds, 0, bytes);
173 	} else {
174 		fds = &readfds;
175 		FD_ZERO(fds);
176 	}
177 
178 	timerclear(&time_waited);
179 
180 call_again:
181 	xdrs = &(cu->cu_outxdrs);
182 	if (xargs == NULL)
183 		goto get_reply;
184 	xdrs->x_op = XDR_ENCODE;
185 	XDR_SETPOS(xdrs, cu->cu_xdrpos);
186 	/*
187 	 * the transaction is the first thing in the out buffer
188 	 */
189 	(*(u_short *)(cu->cu_outbuf))++;
190 	if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
191 	    (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
192 	    (! (*xargs)(xdrs, argsp))) {
193 		if (fds != &readfds)
194 			free(fds);
195 		return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
196 	}
197 	outlen = (int)XDR_GETPOS(xdrs);
198 
199 send_again:
200 	if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
201 	    (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen) {
202 		cu->cu_error.re_errno = errno;
203 		if (fds != &readfds)
204 			free(fds);
205 		return (cu->cu_error.re_status = RPC_CANTSEND);
206 	}
207 
208 	/*
209 	 * Hack to provide rpc-based message passing
210 	 */
211 	if (!timerisset(&timeout)) {
212 		if (fds != &readfds)
213 			free(fds);
214 		return (cu->cu_error.re_status = RPC_TIMEDOUT);
215 	}
216 
217 get_reply:
218 
219 	/*
220 	 * sub-optimal code appears here because we have
221 	 * some clock time to spare while the packets are in flight.
222 	 * (We assume that this is actually only executed once.)
223 	 */
224 	reply_msg.acpted_rply.ar_verf = _null_auth;
225 	reply_msg.acpted_rply.ar_results.where = resultsp;
226 	reply_msg.acpted_rply.ar_results.proc = xresults;
227 
228 	gettimeofday(&start, NULL);
229 	for (;;) {
230 		/* XXX we know the other bits are still clear */
231 		FD_SET(cu->cu_sock, fds);
232 		tv = cu->cu_wait;
233 		switch (select(cu->cu_sock+1, fds, NULL, NULL, &tv)) {
234 
235 		case 0:
236 			timeradd(&time_waited, &cu->cu_wait, &tmp1);
237 			time_waited = tmp1;
238 			if (timercmp(&time_waited, &timeout, <))
239 				goto send_again;
240 			if (fds != &readfds)
241 				free(fds);
242 			return (cu->cu_error.re_status = RPC_TIMEDOUT);
243 
244 		case -1:
245 			if (errno == EINTR) {
246 				gettimeofday(&after, NULL);
247 				timersub(&after, &start, &tmp1);
248 				timeradd(&time_waited, &tmp1, &tmp2);
249 				time_waited = tmp2;
250 				if (timercmp(&time_waited, &timeout, <))
251 					continue;
252 				if (fds != &readfds)
253 					free(fds);
254 				return (cu->cu_error.re_status = RPC_TIMEDOUT);
255 			}
256 			cu->cu_error.re_errno = errno;
257 			if (fds != &readfds)
258 				free(fds);
259 			return (cu->cu_error.re_status = RPC_CANTRECV);
260 		}
261 
262 		do {
263 			fromlen = sizeof(struct sockaddr);
264 			inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
265 				(int) cu->cu_recvsz, 0,
266 				(struct sockaddr *)&from, &fromlen);
267 		} while (inlen < 0 && errno == EINTR);
268 		if (inlen < 0) {
269 			if (errno == EWOULDBLOCK)
270 				continue;
271 			cu->cu_error.re_errno = errno;
272 			if (fds != &readfds)
273 				free(fds);
274 			return (cu->cu_error.re_status = RPC_CANTRECV);
275 		}
276 		if (inlen < sizeof(u_int32_t))
277 			continue;
278 #ifdef dont_check_xid
279 		/* see if reply transaction id matches sent id */
280 		if (*((u_int32_t *)(cu->cu_inbuf)) != *((u_int32_t *)(cu->cu_outbuf)))
281 			continue;
282 #endif
283 		/* we now assume we have the proper reply */
284 		break;
285 	}
286 
287 	/*
288 	 * now decode and validate the response
289 	 */
290 	xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
291 	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
292 	/* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
293 	if (ok) {
294 		_seterr_reply(&reply_msg, &(cu->cu_error));
295 		if (cu->cu_error.re_status == RPC_SUCCESS) {
296 			if (! AUTH_VALIDATE(cl->cl_auth,
297 				&reply_msg.acpted_rply.ar_verf)) {
298 				cu->cu_error.re_status = RPC_AUTHERROR;
299 				cu->cu_error.re_why = AUTH_INVALIDRESP;
300 			}
301 			if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
302 				xdrs->x_op = XDR_FREE;
303 				(void)xdr_opaque_auth(xdrs,
304 				    &(reply_msg.acpted_rply.ar_verf));
305 			}
306 		}  /* end successful completion */
307 		else {
308 			/* maybe our credentials need to be refreshed ... */
309 			if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
310 				nrefreshes--;
311 				goto call_again;
312 			}
313 		}  /* end of unsuccessful completion */
314 	}  /* end of valid reply message */
315 	else {
316 		cu->cu_error.re_status = RPC_CANTDECODERES;
317 	}
318 	if (fds != &readfds)
319 		free(fds);
320 	return (cu->cu_error.re_status);
321 }
322 
323 
324 /*
325  * pmap_getport.c
326  * Client interface to pmap rpc service.
327  *
328  * Copyright (C) 1984, Sun Microsystems, Inc.
329  */
330 
331 
332 static struct timeval timeout = { 1, 0 };
333 static struct timeval tottimeout = { 1, 0 };
334 
335 /*
336  * Find the mapped port for program,version.
337  * Calls the pmap service remotely to do the lookup.
338  * Returns 0 if no map exists.
339  */
340 static u_short
341 __pmap_getport(address, program, version, protocol)
342 	struct sockaddr_in *address;
343 	u_long program;
344 	u_long version;
345 	u_int protocol;
346 {
347 	u_short port = 0;
348 	int sock = -1;
349 	register CLIENT *client;
350 	struct pmap parms;
351 
352 	address->sin_port = htons(PMAPPORT);
353 
354 	client = clntudp_bufcreate(address, PMAPPROG,
355 	    PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
356 	if (client != (CLIENT *)NULL) {
357 		parms.pm_prog = program;
358 		parms.pm_vers = version;
359 		parms.pm_prot = protocol;
360 		parms.pm_port = 0;  /* not needed or used */
361 		if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms,
362 		    xdr_u_short, &port, tottimeout) != RPC_SUCCESS){
363 			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
364 			clnt_geterr(client, &rpc_createerr.cf_error);
365 		} else if (port == 0) {
366 			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
367 		}
368 		CLNT_DESTROY(client);
369 	}
370 	if (sock != -1)
371 		(void)close(sock);
372 	address->sin_port = 0;
373 	return (port);
374 }
375 
376 /*
377  * Transmit to YPPROC_DOMAIN_NONACK, return immediately.
378  */
379 static bool_t *
380 ypproc_domain_nonack_2_send(domainname *argp, CLIENT *clnt)
381 {
382 	static bool_t clnt_res;
383 	struct timeval TIMEOUT = { 0, 0 };
384 
385 	memset((char *)&clnt_res, 0, sizeof (clnt_res));
386 	if (clnt_call(clnt, YPPROC_DOMAIN_NONACK,
387 		(xdrproc_t) xdr_domainname, (caddr_t) argp,
388 		(xdrproc_t) xdr_bool, (caddr_t) &clnt_res,
389 		TIMEOUT) != RPC_SUCCESS) {
390 		return (NULL);
391 	}
392 	return (&clnt_res);
393 }
394 
395 /*
396  * Receive response from YPPROC_DOMAIN_NONACK asynchronously.
397  */
398 static bool_t *
399 ypproc_domain_nonack_2_recv(domainname *argp, CLIENT *clnt)
400 {
401 	static bool_t clnt_res;
402 	struct timeval TIMEOUT = { 0, 0 };
403 
404 	memset((char *)&clnt_res, 0, sizeof (clnt_res));
405 	if (clnt_call(clnt, YPPROC_DOMAIN_NONACK,
406 		(xdrproc_t) NULL, (caddr_t) argp,
407 		(xdrproc_t) xdr_bool, (caddr_t) &clnt_res,
408 		TIMEOUT) != RPC_SUCCESS) {
409 		return (NULL);
410 	}
411 	return (&clnt_res);
412 }
413 
414 /*
415  * "We have the machine that goes 'ping!'" -- Monty Python
416  *
417  * This function blasts packets at the YPPROC_DOMAIN_NONACK procedures
418  * of the NIS servers listed in restricted_addrs structure.
419  * Whoever replies the fastest becomes our chosen server.
420  *
421  * Note: THIS IS NOT A BROADCAST OPERATION! We could use clnt_broadcast()
422  * for this, but that has the following problems:
423  * - We only get the address of the machine that replied in the
424  *   'eachresult' callback, and on multi-homed machines this can
425  *   lead to confusion.
426  * - clnt_broadcast() only transmits to local networks, whereas with
427  *   NIS+ you can have a perfectly good server located anywhere on or
428  *   off the local network.
429  * - clnt_broadcast() blocks for an arbitrary amount of time which the
430  *   caller can't control -- we want to avoid that.
431  *
432  * Also note that this has nothing to do with the NIS_PING procedure used
433  * for replica updates.
434  */
435 
436 struct ping_req {
437 	struct sockaddr_in	sin;
438 	unsigned long		xid;
439 };
440 
441 int __yp_ping(restricted_addrs, cnt, dom, port)
442 	struct in_addr		*restricted_addrs;
443 	int			cnt;
444 	char			*dom;
445 	short			*port;
446 {
447 	struct timeval		tv = { 5, 0 };
448 	struct ping_req		**reqs;
449 	unsigned long		i;
450 	struct sockaddr_in	sin, *any = NULL;
451 	int			winner = -1;
452 	time_t			xid_seed, xid_lookup;
453 	int			sock, dontblock = 1;
454 	CLIENT			*clnt;
455 	char			*foo = dom;
456 	struct cu_data		*cu;
457 	enum clnt_stat		(*oldfunc)();
458 	int			validsrvs = 0;
459 
460 	/* Set up handles. */
461 	reqs = calloc(1, sizeof(struct ping_req *) * cnt);
462 	xid_seed = time(NULL) ^ getpid();
463 
464 	for (i = 0; i < cnt; i++) {
465 		bzero((char *)&sin, sizeof(sin));
466 		sin.sin_family = AF_INET;
467 		bcopy((char *)&restricted_addrs[i],
468 			(char *)&sin.sin_addr, sizeof(struct in_addr));
469 		sin.sin_port = htons(__pmap_getport(&sin, YPPROG,
470 					YPVERS, IPPROTO_UDP));
471 		if (sin.sin_port == 0)
472 			continue;
473 		reqs[i] = calloc(1, sizeof(struct ping_req));
474 		bcopy((char *)&sin, (char *)&reqs[i]->sin, sizeof(sin));
475 		any = &reqs[i]->sin;
476 		reqs[i]->xid = xid_seed;
477 		xid_seed++;
478 		validsrvs++;
479 	}
480 
481 	/* Make sure at least one server was assigned */
482 	if (!validsrvs) {
483 		free(reqs);
484 		return(-1);
485 	}
486 
487 	/* Create RPC handle */
488 	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
489 	clnt = clntudp_create(any, YPPROG, YPVERS, tv, &sock);
490 	if (clnt == NULL) {
491 		close(sock);
492 		for (i = 0; i < cnt; i++)
493 			if (reqs[i] != NULL)
494 				free(reqs[i]);
495 		free(reqs);
496 		return(-1);
497 	}
498 	clnt->cl_auth = authunix_create_default();
499 	cu = (struct cu_data *)clnt->cl_private;
500 	tv.tv_sec = 0;
501 	clnt_control(clnt, CLSET_TIMEOUT, &tv);
502 	ioctl(sock, FIONBIO, &dontblock);
503 	oldfunc = clnt->cl_ops->cl_call;
504 	clnt->cl_ops->cl_call = clntudp_a_call;
505 
506 	/* Transmit */
507 	for (i = 0; i < cnt; i++) {
508 		if (reqs[i] != NULL) {
509 			/* subtract one; clntudp_call() will increment */
510 			*((u_int32_t *)(cu->cu_outbuf)) = reqs[i]->xid - 1;
511 			bcopy((char *)&reqs[i]->sin, (char *)&cu->cu_raddr,
512 				sizeof(struct sockaddr_in));
513 			ypproc_domain_nonack_2_send(&foo, clnt);
514 		}
515 	}
516 
517 	/* Receive reply */
518 	ypproc_domain_nonack_2_recv(&foo, clnt);
519 
520 	/* Got a winner -- look him up. */
521 	xid_lookup = *((u_int32_t *)(cu->cu_inbuf));
522 	for (i = 0; i < cnt; i++) {
523 		if (reqs[i] != NULL && reqs[i]->xid == xid_lookup) {
524 			winner = i;
525 			*port = reqs[i]->sin.sin_port;
526 		}
527 	}
528 
529 	/* Shut everything down */
530 	clnt->cl_ops->cl_call = oldfunc;
531 	auth_destroy(clnt->cl_auth);
532 	clnt_destroy(clnt);
533 	close(sock);
534 
535 	for (i = 0; i < cnt; i++)
536 		if (reqs[i] != NULL)
537 			free(reqs[i]);
538 	free(reqs);
539 
540 	return(winner);
541 }
542