98c3e1d2410f23e37a4056b90beca098b44b54e1
[dragonfly.git] / usr.sbin / ypbind / yp_ping.c
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.2 2003/06/17 04:30:04 dillon 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 compatable 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 }