Merge from vendor branch BIND:
[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.7 2005/11/24 22:23:02 swildner 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                                 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                 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
438 __yp_ping(struct in_addr *restricted_addrs, int cnt,
439           char *dom, short int *port)
440 {
441         struct timeval          tv = { 5, 0 };
442         struct ping_req         **reqs;
443         unsigned long           i;
444         struct sockaddr_in      sin, *any = NULL;
445         int                     winner = -1;
446         time_t                  xid_seed, xid_lookup;
447         int                     sock, dontblock = 1;
448         CLIENT                  *clnt;
449         char                    *foo = dom;
450         struct cu_data          *cu;
451         enum clnt_stat          (*oldfunc)();
452         int                     validsrvs = 0;
453
454         /* Set up handles. */
455         reqs = calloc(1, sizeof(struct ping_req *) * cnt);
456         xid_seed = time(NULL) ^ getpid();
457
458         for (i = 0; i < cnt; i++) {
459                 bzero((char *)&sin, sizeof(sin));
460                 sin.sin_family = AF_INET;
461                 bcopy((char *)&restricted_addrs[i],
462                         (char *)&sin.sin_addr, sizeof(struct in_addr));
463                 sin.sin_port = htons(__pmap_getport(&sin, YPPROG,
464                                         YPVERS, IPPROTO_UDP));
465                 if (sin.sin_port == 0)
466                         continue;
467                 reqs[i] = calloc(1, sizeof(struct ping_req));
468                 bcopy((char *)&sin, (char *)&reqs[i]->sin, sizeof(sin));
469                 any = &reqs[i]->sin;
470                 reqs[i]->xid = xid_seed;
471                 xid_seed++;
472                 validsrvs++;
473         }
474
475         /* Make sure at least one server was assigned */
476         if (!validsrvs) {
477                 free(reqs);
478                 return(-1);
479         }
480
481         /* Create RPC handle */
482         sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
483         clnt = clntudp_create(any, YPPROG, YPVERS, tv, &sock);
484         if (clnt == NULL) {
485                 close(sock);
486                 for (i = 0; i < cnt; i++)
487                         if (reqs[i] != NULL)
488                                 free(reqs[i]);
489                 free(reqs);
490                 return(-1);
491         }
492         clnt->cl_auth = authunix_create_default();
493         cu = (struct cu_data *)clnt->cl_private;
494         tv.tv_sec = 0;
495         clnt_control(clnt, CLSET_TIMEOUT, &tv);
496         ioctl(sock, FIONBIO, &dontblock);
497         oldfunc = clnt->cl_ops->cl_call;
498         clnt->cl_ops->cl_call = clntudp_a_call;
499
500         /* Transmit */
501         for (i = 0; i < cnt; i++) {
502                 if (reqs[i] != NULL) {
503                         /* subtract one; clntudp_call() will increment */
504                         *((u_int32_t *)(cu->cu_outbuf)) = reqs[i]->xid - 1;
505                         bcopy((char *)&reqs[i]->sin, (char *)&cu->cu_raddr,
506                                 sizeof(struct sockaddr_in));
507                         ypproc_domain_nonack_2_send(&foo, clnt);
508                 }
509         }
510
511         /* Receive reply */
512         ypproc_domain_nonack_2_recv(&foo, clnt);
513
514         /* Got a winner -- look him up. */
515         xid_lookup = *((u_int32_t *)(cu->cu_inbuf));
516         for (i = 0; i < cnt; i++) {
517                 if (reqs[i] != NULL && reqs[i]->xid == xid_lookup) {
518                         winner = i;
519                         *port = reqs[i]->sin.sin_port;
520                 }
521         }
522
523         /* Shut everything down */
524         clnt->cl_ops->cl_call = oldfunc;
525         auth_destroy(clnt->cl_auth);
526         clnt_destroy(clnt);
527         close(sock);
528
529         for (i = 0; i < cnt; i++)
530                 if (reqs[i] != NULL)
531                         free(reqs[i]);
532         free(reqs);
533
534         return(winner);
535 }