xref: /freebsd/usr.sbin/ypbind/yp_ping.c (revision d2ba5111c125104b09aa1acd1bfe8af2a24c79cc)
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 
33 /*-
34  * Copyright (c) 2009, Sun Microsystems, Inc.
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions are met:
39  * - Redistributions of source code must retain the above copyright notice,
40  *   this list of conditions and the following disclaimer.
41  * - Redistributions in binary form must reproduce the above copyright notice,
42  *   this list of conditions and the following disclaimer in the documentation
43  *   and/or other materials provided with the distribution.
44  * - Neither the name of Sun Microsystems, Inc. nor the names of its
45  *   contributors may be used to endorse or promote products derived
46  *   from this software without specific prior written permission.
47  *
48  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
49  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
52  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
55  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
58  * POSSIBILITY OF SUCH DAMAGE.
59  */
60 #if 0
61 #ifndef lint
62 static char *sccsid = "@(#)from: clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";
63 static char *sccsid = "@(#)from: clnt_udp.c	2.2 88/08/01 4.0 RPCSRC";
64 #endif
65 #endif
66 #include <sys/cdefs.h>
67 __FBSDID("$FreeBSD$");
68 
69 /*
70  * clnt_udp.c, Implements a UDP/IP based, client side RPC.
71  *
72  * Copyright (C) 1984, Sun Microsystems, Inc.
73  */
74 
75 #include <errno.h>
76 #include <netdb.h>
77 #include <stdio.h>
78 #include <stdlib.h>
79 #include <string.h>
80 #include <unistd.h>
81 #include <pthread.h>
82 #include <rpc/rpc.h>
83 #include <rpc/pmap_clnt.h>
84 #include <rpc/pmap_prot.h>
85 #include <rpcsvc/yp.h>
86 #include <sys/types.h>
87 #include <sys/poll.h>
88 #include <sys/socket.h>
89 #include <sys/signal.h>
90 #include <sys/ioctl.h>
91 #include <arpa/inet.h>
92 #include <net/if.h>
93 
94 #include "yp_ping.h"
95 
96 /*
97  * pmap_getport.c
98  * Client interface to pmap rpc service.
99  *
100  * Copyright (C) 1984, Sun Microsystems, Inc.
101  */
102 
103 
104 static struct timeval timeout = { 1, 0 };
105 static struct timeval tottimeout = { 1, 0 };
106 
107 /*
108  * Find the mapped port for program,version.
109  * Calls the pmap service remotely to do the lookup.
110  * Returns 0 if no map exists.
111  */
112 static u_short
113 __pmap_getport(struct sockaddr_in *address, u_long program, u_long version,
114     u_int protocol)
115 {
116 	u_short port = 0;
117 	int sock = -1;
118 	register CLIENT *client;
119 	struct pmap parms;
120 
121 	address->sin_port = htons(PMAPPORT);
122 
123 	client = clntudp_bufcreate(address, PMAPPROG,
124 	    PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
125 	if (client != (CLIENT *)NULL) {
126 		parms.pm_prog = program;
127 		parms.pm_vers = version;
128 		parms.pm_prot = protocol;
129 		parms.pm_port = 0;  /* not needed or used */
130 		if (CLNT_CALL(client, PMAPPROC_GETPORT,
131 			(xdrproc_t)xdr_pmap, &parms,
132 			(xdrproc_t)xdr_u_short, &port,
133 			tottimeout) != RPC_SUCCESS){
134 			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
135 			clnt_geterr(client, &rpc_createerr.cf_error);
136 		} else if (port == 0) {
137 			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
138 		}
139 		CLNT_DESTROY(client);
140 	}
141 	if (sock != -1)
142 		(void)close(sock);
143 	address->sin_port = 0;
144 	return (port);
145 }
146 
147 /*
148  * Transmit to YPPROC_DOMAIN_NONACK, return immediately.
149  */
150 static bool_t *
151 ypproc_domain_nonack_2_send(domainname *argp, CLIENT *clnt)
152 {
153 	static bool_t clnt_res;
154 	struct timeval TIMEOUT = { 0, 0 };
155 
156 	memset((char *)&clnt_res, 0, sizeof (clnt_res));
157 	if (clnt_call(clnt, YPPROC_DOMAIN_NONACK,
158 		(xdrproc_t) xdr_domainname, (caddr_t) argp,
159 		(xdrproc_t) xdr_bool, (caddr_t) &clnt_res,
160 		TIMEOUT) != RPC_SUCCESS) {
161 		return (NULL);
162 	}
163 	return (&clnt_res);
164 }
165 
166 /*
167  * Receive response from YPPROC_DOMAIN_NONACK asynchronously.
168  */
169 static bool_t *
170 ypproc_domain_nonack_2_recv(domainname *argp, CLIENT *clnt)
171 {
172 	static bool_t clnt_res;
173 	struct timeval TIMEOUT = { 0, 0 };
174 
175 	memset((char *)&clnt_res, 0, sizeof (clnt_res));
176 	if (clnt_call(clnt, YPPROC_DOMAIN_NONACK,
177 		(xdrproc_t) NULL, (caddr_t) argp,
178 		(xdrproc_t) xdr_bool, (caddr_t) &clnt_res,
179 		TIMEOUT) != RPC_SUCCESS) {
180 		return (NULL);
181 	}
182 	return (&clnt_res);
183 }
184 
185 /*
186  * "We have the machine that goes 'ping!'" -- Monty Python
187  *
188  * This function blasts packets at the YPPROC_DOMAIN_NONACK procedures
189  * of the NIS servers listed in restricted_addrs structure.
190  * Whoever replies the fastest becomes our chosen server.
191  *
192  * Note: THIS IS NOT A BROADCAST OPERATION! We could use clnt_broadcast()
193  * for this, but that has the following problems:
194  * - We only get the address of the machine that replied in the
195  *   'eachresult' callback, and on multi-homed machines this can
196  *   lead to confusion.
197  * - clnt_broadcast() only transmits to local networks, whereas with
198  *   NIS+ you can have a perfectly good server located anywhere on or
199  *   off the local network.
200  * - clnt_broadcast() blocks for an arbitrary amount of time which the
201  *   caller can't control -- we want to avoid that.
202  *
203  * Also note that this has nothing to do with the NIS_PING procedure used
204  * for replica updates.
205  */
206 
207 struct ping_req {
208 	struct sockaddr_in	sin;
209 	u_int32_t		xid;
210 };
211 
212 int
213 __yp_ping(struct in_addr *restricted_addrs, int cnt, char *dom, short *port)
214 {
215 	struct timeval		tv = { 5, 0 };
216 	struct ping_req		**reqs;
217 	unsigned long		i;
218 	int			async;
219 	struct sockaddr_in	sin, *any = NULL;
220 	struct netbuf		addr;
221 	int			winner = -1;
222 	u_int32_t		xid_seed, xid_lookup;
223 	int			sock, dontblock = 1;
224 	CLIENT			*clnt;
225 	char			*foo = dom;
226 	int			validsrvs = 0;
227 
228 	/* Set up handles. */
229 	reqs = calloc(cnt, sizeof(struct ping_req *));
230 	xid_seed = time(NULL) ^ getpid();
231 
232 	for (i = 0; i < cnt; i++) {
233 		bzero((char *)&sin, sizeof(sin));
234 		sin.sin_family = AF_INET;
235 		bcopy((char *)&restricted_addrs[i],
236 			(char *)&sin.sin_addr, sizeof(struct in_addr));
237 		sin.sin_port = htons(__pmap_getport(&sin, YPPROG,
238 					YPVERS, IPPROTO_UDP));
239 		if (sin.sin_port == 0)
240 			continue;
241 		reqs[i] = calloc(1, sizeof(struct ping_req));
242 		bcopy((char *)&sin, (char *)&reqs[i]->sin, sizeof(sin));
243 		any = &reqs[i]->sin;
244 		reqs[i]->xid = xid_seed;
245 		xid_seed++;
246 		validsrvs++;
247 	}
248 
249 	/* Make sure at least one server was assigned */
250 	if (!validsrvs) {
251 		free(reqs);
252 		return(-1);
253 	}
254 
255 	/* Create RPC handle */
256 	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
257 	clnt = clntudp_create(any, YPPROG, YPVERS, tv, &sock);
258 	if (clnt == NULL) {
259 		close(sock);
260 		for (i = 0; i < cnt; i++)
261 			if (reqs[i] != NULL)
262 				free(reqs[i]);
263 		free(reqs);
264 		return(-1);
265 	}
266 	clnt->cl_auth = authunix_create_default();
267 	tv.tv_sec = 0;
268 
269 	clnt_control(clnt, CLSET_TIMEOUT, (char *)&tv);
270 	async = TRUE;
271 	clnt_control(clnt, CLSET_ASYNC, (char *)&async);
272 	ioctl(sock, FIONBIO, &dontblock);
273 
274 	/* Transmit */
275 	for (i = 0; i < cnt; i++) {
276 		if (reqs[i] != NULL) {
277 			clnt_control(clnt, CLSET_XID, (char *)&reqs[i]->xid);
278 			addr.len = sizeof(reqs[i]->sin);
279 			addr.buf = (char *) &reqs[i]->sin;
280 			clnt_control(clnt, CLSET_SVC_ADDR, &addr);
281 			ypproc_domain_nonack_2_send(&foo, clnt);
282 		}
283 	}
284 
285 	/* Receive reply */
286 	ypproc_domain_nonack_2_recv(&foo, clnt);
287 
288 	/* Got a winner -- look him up. */
289 	clnt_control(clnt, CLGET_XID, (char *)&xid_lookup);
290 	for (i = 0; i < cnt; i++) {
291 		if (reqs[i] != NULL && reqs[i]->xid == xid_lookup) {
292 			winner = i;
293 			*port = reqs[i]->sin.sin_port;
294 		}
295 	}
296 
297 	/* Shut everything down */
298 	auth_destroy(clnt->cl_auth);
299 	clnt_destroy(clnt);
300 	close(sock);
301 
302 	for (i = 0; i < cnt; i++)
303 		if (reqs[i] != NULL)
304 			free(reqs[i]);
305 	free(reqs);
306 
307 	return(winner);
308 }
309