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