xref: /dragonfly/usr.sbin/ypbind/yp_ping.c (revision 2cd2d2b5)
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.5 2004/03/31 21:03:38 cpressey 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(CLIENT *cl,		/* client handle */
140 	       u_long proc,		/* procedure number */
141 	       xdrproc_t xargs,		/* xdr routine for args */
142 	       caddr_t argsp,		/* pointer to args */
143 	       xdrproc_t xresults,	/* xdr routine for results */
144 	       caddr_t resultsp,	/* pointer to results */
145 	       struct timeval utimeout)	/* seconds to wait before giving up */
146 {
147 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
148 	XDR *xdrs;
149 	int outlen = 0;
150 	int inlen;
151 	int fromlen;
152 	fd_set *fds, readfds;
153 	struct sockaddr_in from;
154 	struct rpc_msg reply_msg;
155 	XDR reply_xdrs;
156 	struct timeval time_waited, start, after, tmp1, tmp2, tv;
157 	bool_t ok;
158 	int nrefreshes = 2;	/* number of times to refresh cred */
159 	struct timeval timeout;
160 
161 	if (cu->cu_total.tv_usec == -1)
162 		timeout = utimeout;     /* use supplied timeout */
163 	else
164 		timeout = cu->cu_total; /* use default timeout */
165 
166 	if (cu->cu_sock + 1 > FD_SETSIZE) {
167 		int bytes = howmany(cu->cu_sock + 1, NFDBITS) * sizeof(fd_mask);
168 		fds = (fd_set *)malloc(bytes);
169 		if (fds == NULL)
170 			return (cu->cu_error.re_status = RPC_CANTSEND);
171 		memset(fds, 0, bytes);
172 	} else {
173 		fds = &readfds;
174 		FD_ZERO(fds);
175 	}
176 
177 	timerclear(&time_waited);
178 
179 call_again:
180 	xdrs = &(cu->cu_outxdrs);
181 	if (xargs == NULL)
182 		goto get_reply;
183 	xdrs->x_op = XDR_ENCODE;
184 	XDR_SETPOS(xdrs, cu->cu_xdrpos);
185 	/*
186 	 * the transaction is the first thing in the out buffer
187 	 */
188 	(*(u_short *)(cu->cu_outbuf))++;
189 	if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
190 	    (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
191 	    (! (*xargs)(xdrs, argsp))) {
192 		if (fds != &readfds)
193 			free(fds);
194 		return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
195 	}
196 	outlen = (int)XDR_GETPOS(xdrs);
197 
198 send_again:
199 	if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
200 	    (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen) {
201 		cu->cu_error.re_errno = errno;
202 		if (fds != &readfds)
203 			free(fds);
204 		return (cu->cu_error.re_status = RPC_CANTSEND);
205 	}
206 
207 	/*
208 	 * Hack to provide rpc-based message passing
209 	 */
210 	if (!timerisset(&timeout)) {
211 		if (fds != &readfds)
212 			free(fds);
213 		return (cu->cu_error.re_status = RPC_TIMEDOUT);
214 	}
215 
216 get_reply:
217 
218 	/*
219 	 * sub-optimal code appears here because we have
220 	 * some clock time to spare while the packets are in flight.
221 	 * (We assume that this is actually only executed once.)
222 	 */
223 	reply_msg.acpted_rply.ar_verf = _null_auth;
224 	reply_msg.acpted_rply.ar_results.where = resultsp;
225 	reply_msg.acpted_rply.ar_results.proc = xresults;
226 
227 	gettimeofday(&start, NULL);
228 	for (;;) {
229 		/* XXX we know the other bits are still clear */
230 		FD_SET(cu->cu_sock, fds);
231 		tv = cu->cu_wait;
232 		switch (select(cu->cu_sock+1, fds, NULL, NULL, &tv)) {
233 
234 		case 0:
235 			timeradd(&time_waited, &cu->cu_wait, &tmp1);
236 			time_waited = tmp1;
237 			if (timercmp(&time_waited, &timeout, <))
238 				goto send_again;
239 			if (fds != &readfds)
240 				free(fds);
241 			return (cu->cu_error.re_status = RPC_TIMEDOUT);
242 
243 		case -1:
244 			if (errno == EINTR) {
245 				gettimeofday(&after, NULL);
246 				timersub(&after, &start, &tmp1);
247 				timeradd(&time_waited, &tmp1, &tmp2);
248 				time_waited = tmp2;
249 				if (timercmp(&time_waited, &timeout, <))
250 					continue;
251 				if (fds != &readfds)
252 					free(fds);
253 				return (cu->cu_error.re_status = RPC_TIMEDOUT);
254 			}
255 			cu->cu_error.re_errno = errno;
256 			if (fds != &readfds)
257 				free(fds);
258 			return (cu->cu_error.re_status = RPC_CANTRECV);
259 		}
260 
261 		do {
262 			fromlen = sizeof(struct sockaddr);
263 			inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
264 				(int) cu->cu_recvsz, 0,
265 				(struct sockaddr *)&from, &fromlen);
266 		} while (inlen < 0 && errno == EINTR);
267 		if (inlen < 0) {
268 			if (errno == EWOULDBLOCK)
269 				continue;
270 			cu->cu_error.re_errno = errno;
271 			if (fds != &readfds)
272 				free(fds);
273 			return (cu->cu_error.re_status = RPC_CANTRECV);
274 		}
275 		if (inlen < sizeof(u_int32_t))
276 			continue;
277 #ifdef dont_check_xid
278 		/* see if reply transaction id matches sent id */
279 		if (*((u_int32_t *)(cu->cu_inbuf)) != *((u_int32_t *)(cu->cu_outbuf)))
280 			continue;
281 #endif
282 		/* we now assume we have the proper reply */
283 		break;
284 	}
285 
286 	/*
287 	 * now decode and validate the response
288 	 */
289 	xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
290 	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
291 	/* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
292 	if (ok) {
293 		_seterr_reply(&reply_msg, &(cu->cu_error));
294 		if (cu->cu_error.re_status == RPC_SUCCESS) {
295 			if (! AUTH_VALIDATE(cl->cl_auth,
296 				&reply_msg.acpted_rply.ar_verf)) {
297 				cu->cu_error.re_status = RPC_AUTHERROR;
298 				cu->cu_error.re_why = AUTH_INVALIDRESP;
299 			}
300 			if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
301 				xdrs->x_op = XDR_FREE;
302 				(void)xdr_opaque_auth(xdrs,
303 				    &(reply_msg.acpted_rply.ar_verf));
304 			}
305 		}  /* end successful completion */
306 		else {
307 			/* maybe our credentials need to be refreshed ... */
308 			if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
309 				nrefreshes--;
310 				goto call_again;
311 			}
312 		}  /* end of unsuccessful completion */
313 	}  /* end of valid reply message */
314 	else {
315 		cu->cu_error.re_status = RPC_CANTDECODERES;
316 	}
317 	if (fds != &readfds)
318 		free(fds);
319 	return (cu->cu_error.re_status);
320 }
321 
322 
323 /*
324  * pmap_getport.c
325  * Client interface to pmap rpc service.
326  *
327  * Copyright (C) 1984, Sun Microsystems, Inc.
328  */
329 
330 
331 static struct timeval timeout = { 1, 0 };
332 static struct timeval tottimeout = { 1, 0 };
333 
334 /*
335  * Find the mapped port for program,version.
336  * Calls the pmap service remotely to do the lookup.
337  * Returns 0 if no map exists.
338  */
339 static u_short
340 __pmap_getport(struct sockaddr_in *address,
341 	       u_long program, u_long version, u_int protocol)
342 {
343 	u_short port = 0;
344 	int sock = -1;
345 	CLIENT *client;
346 	struct pmap parms;
347 
348 	address->sin_port = htons(PMAPPORT);
349 
350 	client = clntudp_bufcreate(address, PMAPPROG,
351 	    PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
352 	if (client != (CLIENT *)NULL) {
353 		parms.pm_prog = program;
354 		parms.pm_vers = version;
355 		parms.pm_prot = protocol;
356 		parms.pm_port = 0;  /* not needed or used */
357 		if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms,
358 		    xdr_u_short, &port, tottimeout) != RPC_SUCCESS){
359 			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
360 			clnt_geterr(client, &rpc_createerr.cf_error);
361 		} else if (port == 0) {
362 			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
363 		}
364 		CLNT_DESTROY(client);
365 	}
366 	if (sock != -1)
367 		(void)close(sock);
368 	address->sin_port = 0;
369 	return (port);
370 }
371 
372 /*
373  * Transmit to YPPROC_DOMAIN_NONACK, return immediately.
374  */
375 static bool_t *
376 ypproc_domain_nonack_2_send(domainname *argp, CLIENT *clnt)
377 {
378 	static bool_t clnt_res;
379 	struct timeval TIMEOUT = { 0, 0 };
380 
381 	memset((char *)&clnt_res, 0, sizeof (clnt_res));
382 	if (clnt_call(clnt, YPPROC_DOMAIN_NONACK,
383 		(xdrproc_t) xdr_domainname, (caddr_t) argp,
384 		(xdrproc_t) xdr_bool, (caddr_t) &clnt_res,
385 		TIMEOUT) != RPC_SUCCESS) {
386 		return (NULL);
387 	}
388 	return (&clnt_res);
389 }
390 
391 /*
392  * Receive response from YPPROC_DOMAIN_NONACK asynchronously.
393  */
394 static bool_t *
395 ypproc_domain_nonack_2_recv(domainname *argp, CLIENT *clnt)
396 {
397 	static bool_t clnt_res;
398 	struct timeval TIMEOUT = { 0, 0 };
399 
400 	memset((char *)&clnt_res, 0, sizeof (clnt_res));
401 	if (clnt_call(clnt, YPPROC_DOMAIN_NONACK,
402 		(xdrproc_t) NULL, (caddr_t) argp,
403 		(xdrproc_t) xdr_bool, (caddr_t) &clnt_res,
404 		TIMEOUT) != RPC_SUCCESS) {
405 		return (NULL);
406 	}
407 	return (&clnt_res);
408 }
409 
410 /*
411  * "We have the machine that goes 'ping!'" -- Monty Python
412  *
413  * This function blasts packets at the YPPROC_DOMAIN_NONACK procedures
414  * of the NIS servers listed in restricted_addrs structure.
415  * Whoever replies the fastest becomes our chosen server.
416  *
417  * Note: THIS IS NOT A BROADCAST OPERATION! We could use clnt_broadcast()
418  * for this, but that has the following problems:
419  * - We only get the address of the machine that replied in the
420  *   'eachresult' callback, and on multi-homed machines this can
421  *   lead to confusion.
422  * - clnt_broadcast() only transmits to local networks, whereas with
423  *   NIS+ you can have a perfectly good server located anywhere on or
424  *   off the local network.
425  * - clnt_broadcast() blocks for an arbitrary amount of time which the
426  *   caller can't control -- we want to avoid that.
427  *
428  * Also note that this has nothing to do with the NIS_PING procedure used
429  * for replica updates.
430  */
431 
432 struct ping_req {
433 	struct sockaddr_in	sin;
434 	unsigned long		xid;
435 };
436 
437 int __yp_ping(struct in_addr *restricted_addrs, int cnt,
438 	      char *dom, short int *port)
439 {
440 	struct timeval		tv = { 5, 0 };
441 	struct ping_req		**reqs;
442 	unsigned long		i;
443 	struct sockaddr_in	sin, *any = NULL;
444 	int			winner = -1;
445 	time_t			xid_seed, xid_lookup;
446 	int			sock, dontblock = 1;
447 	CLIENT			*clnt;
448 	char			*foo = dom;
449 	struct cu_data		*cu;
450 	enum clnt_stat		(*oldfunc)();
451 	int			validsrvs = 0;
452 
453 	/* Set up handles. */
454 	reqs = calloc(1, sizeof(struct ping_req *) * cnt);
455 	xid_seed = time(NULL) ^ getpid();
456 
457 	for (i = 0; i < cnt; i++) {
458 		bzero((char *)&sin, sizeof(sin));
459 		sin.sin_family = AF_INET;
460 		bcopy((char *)&restricted_addrs[i],
461 			(char *)&sin.sin_addr, sizeof(struct in_addr));
462 		sin.sin_port = htons(__pmap_getport(&sin, YPPROG,
463 					YPVERS, IPPROTO_UDP));
464 		if (sin.sin_port == 0)
465 			continue;
466 		reqs[i] = calloc(1, sizeof(struct ping_req));
467 		bcopy((char *)&sin, (char *)&reqs[i]->sin, sizeof(sin));
468 		any = &reqs[i]->sin;
469 		reqs[i]->xid = xid_seed;
470 		xid_seed++;
471 		validsrvs++;
472 	}
473 
474 	/* Make sure at least one server was assigned */
475 	if (!validsrvs) {
476 		free(reqs);
477 		return(-1);
478 	}
479 
480 	/* Create RPC handle */
481 	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
482 	clnt = clntudp_create(any, YPPROG, YPVERS, tv, &sock);
483 	if (clnt == NULL) {
484 		close(sock);
485 		for (i = 0; i < cnt; i++)
486 			if (reqs[i] != NULL)
487 				free(reqs[i]);
488 		free(reqs);
489 		return(-1);
490 	}
491 	clnt->cl_auth = authunix_create_default();
492 	cu = (struct cu_data *)clnt->cl_private;
493 	tv.tv_sec = 0;
494 	clnt_control(clnt, CLSET_TIMEOUT, &tv);
495 	ioctl(sock, FIONBIO, &dontblock);
496 	oldfunc = clnt->cl_ops->cl_call;
497 	clnt->cl_ops->cl_call = clntudp_a_call;
498 
499 	/* Transmit */
500 	for (i = 0; i < cnt; i++) {
501 		if (reqs[i] != NULL) {
502 			/* subtract one; clntudp_call() will increment */
503 			*((u_int32_t *)(cu->cu_outbuf)) = reqs[i]->xid - 1;
504 			bcopy((char *)&reqs[i]->sin, (char *)&cu->cu_raddr,
505 				sizeof(struct sockaddr_in));
506 			ypproc_domain_nonack_2_send(&foo, clnt);
507 		}
508 	}
509 
510 	/* Receive reply */
511 	ypproc_domain_nonack_2_recv(&foo, clnt);
512 
513 	/* Got a winner -- look him up. */
514 	xid_lookup = *((u_int32_t *)(cu->cu_inbuf));
515 	for (i = 0; i < cnt; i++) {
516 		if (reqs[i] != NULL && reqs[i]->xid == xid_lookup) {
517 			winner = i;
518 			*port = reqs[i]->sin.sin_port;
519 		}
520 	}
521 
522 	/* Shut everything down */
523 	clnt->cl_ops->cl_call = oldfunc;
524 	auth_destroy(clnt->cl_auth);
525 	clnt_destroy(clnt);
526 	close(sock);
527 
528 	for (i = 0; i < cnt; i++)
529 		if (reqs[i] != NULL)
530 			free(reqs[i]);
531 	free(reqs);
532 
533 	return(winner);
534 }
535