]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/ypbind/ypbind.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / ypbind / ypbind.c
1 /*
2  * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
3  * 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. The name of the author may not be used to endorse or promote
14  *    products derived from this software without specific prior written
15  *    permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <sys/ioctl.h>
37 #include <sys/mman.h>
38 #include <sys/signal.h>
39 #include <sys/socket.h>
40 #include <sys/file.h>
41 #include <sys/fcntl.h>
42 #include <sys/stat.h>
43 #include <sys/uio.h>
44 #include <ctype.h>
45 #include <dirent.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <netdb.h>
49 #include <signal.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <syslog.h>
54 #include <unistd.h>
55 #include <rpc/rpc.h>
56 #include <rpc/xdr.h>
57 #include <net/if.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
60 #include <rpc/pmap_clnt.h>
61 #include <rpc/pmap_prot.h>
62 #include <rpc/pmap_rmt.h>
63 #include <rpc/rpc_com.h>
64 #include <rpcsvc/yp.h>
65 #include <rpcsvc/ypclnt.h>
66 #include "yp_ping.h"
67
68 #ifndef BINDINGDIR
69 #define BINDINGDIR "/var/yp/binding"
70 #endif
71
72 #ifndef YPBINDLOCK
73 #define YPBINDLOCK "/var/run/ypbind.lock"
74 #endif
75
76 struct _dom_binding {
77         struct _dom_binding *dom_pnext;
78         char dom_domain[YPMAXDOMAIN + 1];
79         struct sockaddr_in dom_server_addr;
80         long int dom_vers;
81         int dom_lockfd;
82         int dom_alive;
83         int dom_broadcast_pid;
84         int dom_pipe_fds[2];
85         int dom_default;
86 };
87
88 #define READFD ypdb->dom_pipe_fds[0]
89 #define WRITEFD ypdb->dom_pipe_fds[1]
90 #define BROADFD broad_domain->dom_pipe_fds[1]
91
92 extern bool_t xdr_domainname(), xdr_ypbind_resp();
93 extern bool_t xdr_ypreq_key(), xdr_ypresp_val();
94 extern bool_t xdr_ypbind_setdom();
95
96 void    checkwork(void);
97 void    *ypbindproc_null_2_yp(SVCXPRT *, void *, CLIENT *);
98 void    *ypbindproc_setdom_2_yp(SVCXPRT *, struct ypbind_setdom *, CLIENT *);
99 void    rpc_received(char *, struct sockaddr_in *, int);
100 void    broadcast(struct _dom_binding *);
101 int     ping(struct _dom_binding *);
102 int     tell_parent(char *, struct sockaddr_in *);
103 void    handle_children(struct _dom_binding *);
104 void    reaper(int);
105 void    terminate(int);
106 void    yp_restricted_mode(char *);
107 int     verify(struct in_addr);
108
109 char *domain_name;
110 struct _dom_binding *ypbindlist;
111 static struct _dom_binding *broad_domain;
112
113 #define YPSET_NO        0
114 #define YPSET_LOCAL     1
115 #define YPSET_ALL       2
116 int ypsetmode = YPSET_NO;
117 int ypsecuremode = 0;
118 int ppid;
119
120 #define NOT_RESPONDING_HYSTERESIS 10
121 static int not_responding_count = 0;
122
123 /*
124  * Special restricted mode variables: when in restricted mode, only the
125  * specified restricted_domain will be bound, and only the servers listed
126  * in restricted_addrs will be used for binding.
127  */
128 #define RESTRICTED_SERVERS 10
129 int yp_restricted = 0;
130 int yp_manycast = 0;
131 struct in_addr restricted_addrs[RESTRICTED_SERVERS];
132
133 /* No more than MAX_CHILDREN child broadcasters at a time. */
134 #ifndef MAX_CHILDREN
135 #define MAX_CHILDREN 5
136 #endif
137 /* No more than MAX_DOMAINS simultaneous domains */
138 #ifndef MAX_DOMAINS
139 #define MAX_DOMAINS 200
140 #endif
141 /* RPC timeout value */
142 #ifndef FAIL_THRESHOLD
143 #define FAIL_THRESHOLD 20
144 #endif
145
146 /* Number of times to fish for a response froma particular set of hosts */
147 #ifndef MAX_RETRIES
148 #define MAX_RETRIES 30
149 #endif
150
151 int retries = 0;
152 int children = 0;
153 int domains = 0;
154 int yplockfd;
155 fd_set fdsr;
156
157 SVCXPRT *udptransp, *tcptransp;
158
159 void *
160 ypbindproc_null_2_yp(SVCXPRT *transp, void *argp, CLIENT *clnt)
161 {
162         static char res;
163
164         bzero(&res, sizeof(res));
165         return &res;
166 }
167
168 struct ypbind_resp *
169 ypbindproc_domain_2_yp(SVCXPRT *transp, domainname *argp, CLIENT *clnt)
170 {
171         static struct ypbind_resp res;
172         struct _dom_binding *ypdb;
173         char path[MAXPATHLEN];
174
175         bzero(&res, sizeof res);
176         res.ypbind_status = YPBIND_FAIL_VAL;
177         res.ypbind_resp_u.ypbind_error = YPBIND_ERR_NOSERV;
178
179         if (strchr(*argp, '/')) {
180                 syslog(LOG_WARNING, "Domain name '%s' has embedded slash -- \
181 rejecting.", *argp);
182                 return(&res);
183         }
184
185         for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
186                 if (strcmp(ypdb->dom_domain, *argp) == 0)
187                         break;
188                 }
189
190         if (ypdb == NULL) {
191                 if (yp_restricted) {
192                         syslog(LOG_NOTICE, "Running in restricted mode -- request to bind domain \"%s\" rejected.\n", *argp);
193                         return (&res);
194                 }
195
196                 if (domains >= MAX_DOMAINS) {
197                         syslog(LOG_WARNING, "domain limit (%d) exceeded",
198                                                         MAX_DOMAINS);
199                         res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
200                         return (&res);
201                 }
202                 ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
203                 if (ypdb == NULL) {
204                         syslog(LOG_WARNING, "malloc: %m");
205                         res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
206                         return (&res);
207                 }
208                 bzero(ypdb, sizeof *ypdb);
209                 strncpy(ypdb->dom_domain, *argp, sizeof ypdb->dom_domain);
210                 ypdb->dom_vers = YPVERS;
211                 ypdb->dom_alive = 0;
212                 ypdb->dom_default = 0;
213                 ypdb->dom_lockfd = -1;
214                 sprintf(path, "%s/%s.%ld", BINDINGDIR,
215                                         ypdb->dom_domain, ypdb->dom_vers);
216                 unlink(path);
217                 ypdb->dom_pnext = ypbindlist;
218                 ypbindlist = ypdb;
219                 domains++;
220         }
221
222         if (ping(ypdb)) {
223                 return (&res);
224         }
225
226         res.ypbind_status = YPBIND_SUCC_VAL;
227         res.ypbind_resp_u.ypbind_error = 0; /* Success */
228         *(u_int32_t *)&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr =
229                 ypdb->dom_server_addr.sin_addr.s_addr;
230         *(u_short *)&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port =
231                 ypdb->dom_server_addr.sin_port;
232         /*printf("domain %s at %s/%d\n", ypdb->dom_domain,
233                 inet_ntoa(ypdb->dom_server_addr.sin_addr),
234                 ntohs(ypdb->dom_server_addr.sin_port));*/
235         return (&res);
236 }
237
238 void *
239 ypbindproc_setdom_2_yp(SVCXPRT *transp, ypbind_setdom *argp, CLIENT *clnt)
240 {
241         struct sockaddr_in *fromsin, bindsin;
242         static char             *result = NULL;
243
244         if (strchr(argp->ypsetdom_domain, '/')) {
245                 syslog(LOG_WARNING, "Domain name '%s' has embedded slash -- \
246 rejecting.", argp->ypsetdom_domain);
247                 return(NULL);
248         }
249         fromsin = svc_getcaller(transp);
250
251         switch (ypsetmode) {
252         case YPSET_LOCAL:
253                 if (fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
254                         svcerr_noprog(transp);
255                         return(NULL);
256                 }
257                 break;
258         case YPSET_ALL:
259                 break;
260         case YPSET_NO:
261         default:
262                 svcerr_noprog(transp);
263                 return(NULL);
264         }
265
266         if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) {
267                 svcerr_noprog(transp);
268                 return(NULL);
269         }
270
271         if (argp->ypsetdom_vers != YPVERS) {
272                 svcerr_noprog(transp);
273                 return(NULL);
274         }
275
276         bzero(&bindsin, sizeof bindsin);
277         bindsin.sin_family = AF_INET;
278         bindsin.sin_addr.s_addr = *(u_int32_t *)argp->ypsetdom_binding.ypbind_binding_addr;
279         bindsin.sin_port = *(u_short *)argp->ypsetdom_binding.ypbind_binding_port;
280         rpc_received(argp->ypsetdom_domain, &bindsin, 1);
281
282         return((void *) &result);
283 }
284
285 void
286 ypbindprog_2(struct svc_req *rqstp, register SVCXPRT *transp)
287 {
288         union {
289                 domainname ypbindproc_domain_2_arg;
290                 struct ypbind_setdom ypbindproc_setdom_2_arg;
291         } argument;
292         struct authunix_parms *creds;
293         char *result;
294         bool_t (*xdr_argument)(), (*xdr_result)();
295         char *(*local)();
296
297         switch (rqstp->rq_proc) {
298         case YPBINDPROC_NULL:
299                 xdr_argument = xdr_void;
300                 xdr_result = xdr_void;
301                 local = (char *(*)()) ypbindproc_null_2_yp;
302                 break;
303
304         case YPBINDPROC_DOMAIN:
305                 xdr_argument = xdr_domainname;
306                 xdr_result = xdr_ypbind_resp;
307                 local = (char *(*)()) ypbindproc_domain_2_yp;
308                 break;
309
310         case YPBINDPROC_SETDOM:
311                 switch (rqstp->rq_cred.oa_flavor) {
312                 case AUTH_UNIX:
313                         creds = (struct authunix_parms *)rqstp->rq_clntcred;
314                         if (creds->aup_uid != 0) {
315                                 svcerr_auth(transp, AUTH_BADCRED);
316                                 return;
317                         }
318                         break;
319                 default:
320                         svcerr_auth(transp, AUTH_TOOWEAK);
321                         return;
322                 }
323
324                 xdr_argument = xdr_ypbind_setdom;
325                 xdr_result = xdr_void;
326                 local = (char *(*)()) ypbindproc_setdom_2_yp;
327                 break;
328
329         default:
330                 svcerr_noproc(transp);
331                 return;
332         }
333         bzero(&argument, sizeof(argument));
334         if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) {
335                 svcerr_decode(transp);
336                 return;
337         }
338         result = (*local)(transp, &argument, rqstp);
339         if (result != NULL &&
340             !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) {
341                 svcerr_systemerr(transp);
342         }
343         return;
344 }
345
346 /* Jack the reaper */
347 void
348 reaper(int sig)
349 {
350         int st;
351
352         while (wait3(&st, WNOHANG, NULL) > 0)
353                 children--;
354 }
355
356 void
357 terminate(int sig)
358 {
359         struct _dom_binding *ypdb;
360         char path[MAXPATHLEN];
361
362         if (ppid != getpid())
363                 exit(0);
364
365         for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
366                 close(ypdb->dom_lockfd);
367                 if (ypdb->dom_broadcast_pid)
368                         kill(ypdb->dom_broadcast_pid, SIGINT);
369                 sprintf(path, "%s/%s.%ld", BINDINGDIR,
370                         ypdb->dom_domain, ypdb->dom_vers);
371                 unlink(path);
372         }
373         close(yplockfd);
374         unlink(YPBINDLOCK);
375         pmap_unset(YPBINDPROG, YPBINDVERS);
376         exit(0);
377 }
378
379 int
380 main(int argc, char *argv[])
381 {
382         struct timeval tv;
383         int i;
384         DIR *dird;
385         struct dirent *dirp;
386         struct _dom_binding *ypdb, *next;
387
388         /* Check that another ypbind isn't already running. */
389         if ((yplockfd = (open(YPBINDLOCK, O_RDONLY|O_CREAT, 0444))) == -1)
390                 err(1, "%s", YPBINDLOCK);
391
392         if (flock(yplockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
393                 errx(1, "another ypbind is already running. Aborting");
394
395         /* XXX domainname will be overriden if we use restricted mode */
396         yp_get_default_domain(&domain_name);
397         if (domain_name[0] == '\0')
398                 errx(1, "domainname not set. Aborting");
399
400         for (i = 1; i<argc; i++) {
401                 if (strcmp("-ypset", argv[i]) == 0)
402                         ypsetmode = YPSET_ALL;
403                 else if (strcmp("-ypsetme", argv[i]) == 0)
404                         ypsetmode = YPSET_LOCAL;
405                 else if (strcmp("-s", argv[i]) == 0)
406                         ypsecuremode++;
407                 else if (strcmp("-S", argv[i]) == 0 && argc > i)
408                         yp_restricted_mode(argv[++i]);
409                 else if (strcmp("-m", argv[i]) == 0)
410                         yp_manycast++;
411                 else
412                         errx(1, "unknown option: %s", argv[i]);
413         }
414
415         /* blow away everything in BINDINGDIR (if it exists) */
416
417         if ((dird = opendir(BINDINGDIR)) != NULL) {
418                 char path[MAXPATHLEN];
419                 while ((dirp = readdir(dird)) != NULL)
420                         if (strcmp(dirp->d_name, ".") &&
421                             strcmp(dirp->d_name, "..")) {
422                                 sprintf(path,"%s/%s",BINDINGDIR,dirp->d_name);
423                                 unlink(path);
424                         }
425                 closedir(dird);
426         }
427
428 #ifdef DAEMON
429         if (daemon(0,0))
430                 err(1, "fork");
431 #endif
432
433         pmap_unset(YPBINDPROG, YPBINDVERS);
434
435         udptransp = svcudp_create(RPC_ANYSOCK);
436         if (udptransp == NULL)
437                 errx(1, "cannot create udp service");
438         if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
439             IPPROTO_UDP))
440                 errx(1, "unable to register (YPBINDPROG, YPBINDVERS, udp)");
441
442         tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
443         if (tcptransp == NULL)
444                 errx(1, "cannot create tcp service");
445
446         if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
447             IPPROTO_TCP))
448                 errx(1, "unable to register (YPBINDPROG, YPBINDVERS, tcp)");
449
450         /* build initial domain binding, make it "unsuccessful" */
451         ypbindlist = (struct _dom_binding *)malloc(sizeof *ypbindlist);
452         if (ypbindlist == NULL)
453                 errx(1, "malloc");
454         bzero(ypbindlist, sizeof *ypbindlist);
455         strncpy(ypbindlist->dom_domain, domain_name, sizeof ypbindlist->dom_domain);
456         ypbindlist->dom_vers = YPVERS;
457         ypbindlist->dom_alive = 0;
458         ypbindlist->dom_lockfd = -1;
459         ypbindlist->dom_default = 1;
460         domains++;
461
462         signal(SIGCHLD, reaper);
463         signal(SIGTERM, terminate);
464
465         ppid = getpid(); /* Remember who we are. */
466
467         openlog(argv[0], LOG_PID, LOG_DAEMON);
468
469         if (madvise(NULL, 0, MADV_PROTECT) != 0)
470                 syslog(LOG_WARNING, "madvise(): %m");
471
472         /* Kick off the default domain */
473         broadcast(ypbindlist);
474
475         while (1) {
476                 fdsr = svc_fdset;
477
478                 tv.tv_sec = 60;
479                 tv.tv_usec = 0;
480
481                 switch (select(_rpc_dtablesize(), &fdsr, NULL, NULL, &tv)) {
482                 case 0:
483                         checkwork();
484                         break;
485                 case -1:
486                         if (errno != EINTR)
487                                 syslog(LOG_WARNING, "select: %m");
488                         break;
489                 default:
490                         for (ypdb = ypbindlist; ypdb; ypdb = next) {
491                                 next = ypdb->dom_pnext;
492                                 if (READFD > 0 && FD_ISSET(READFD, &fdsr)) {
493                                         handle_children(ypdb);
494                                         if (children == (MAX_CHILDREN - 1))
495                                                 checkwork();
496                                 }
497                         }
498                         svc_getreqset(&fdsr);
499                         break;
500                 }
501         }
502
503         /* NOTREACHED */
504         exit(1);
505 }
506
507 void
508 checkwork(void)
509 {
510         struct _dom_binding *ypdb;
511
512         for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
513                 ping(ypdb);
514 }
515
516 /* The clnt_broadcast() callback mechanism sucks. */
517
518 /*
519  * Receive results from broadcaster. Don't worry about passing
520  * bogus info to rpc_received() -- it can handle it. Note that we
521  * must be sure to invalidate the dom_pipe_fds descriptors here:
522  * since descriptors can be re-used, we have to make sure we
523  * don't mistake one of the RPC descriptors for one of the pipes.
524  * What's weird is that forgetting to invalidate the pipe descriptors
525  * doesn't always result in an error (otherwise I would have caught
526  * the mistake much sooner), even though logically it should.
527  */
528 void
529 handle_children(struct _dom_binding *ypdb)
530 {
531         char buf[YPMAXDOMAIN + 1];
532         struct sockaddr_in addr;
533         int d = 0, a = 0;
534         struct _dom_binding *y, *prev = NULL;
535         char path[MAXPATHLEN];
536
537         if ((d = read(READFD, &buf, sizeof(buf))) <= 0)
538                 syslog(LOG_WARNING, "could not read from child: %m");
539
540         if ((a = read(READFD, &addr, sizeof(struct sockaddr_in))) < 0)
541                 syslog(LOG_WARNING, "could not read from child: %m");
542
543         close(READFD);
544         FD_CLR(READFD, &fdsr);
545         FD_CLR(READFD, &svc_fdset);
546         READFD = WRITEFD = -1;
547         if (d > 0 && a > 0)
548                 rpc_received(buf, &addr, 0);
549         else {
550                 for (y = ypbindlist; y; y = y->dom_pnext) {
551                         if (y == ypdb)
552                                 break;
553                         prev = y;
554                 }
555                 switch (ypdb->dom_default) {
556                 case 0:
557                         if (prev == NULL)
558                                 ypbindlist = y->dom_pnext;
559                         else
560                                 prev->dom_pnext = y->dom_pnext;
561                         sprintf(path, "%s/%s.%ld", BINDINGDIR,
562                                 ypdb->dom_domain, YPVERS);
563                         close(ypdb->dom_lockfd);
564                         unlink(path);
565                         free(ypdb);
566                         domains--;
567                         return;
568                 case 1:
569                         ypdb->dom_broadcast_pid = 0;
570                         ypdb->dom_alive = 0;
571                         broadcast(ypdb);
572                         return;
573                 default:
574                         break;
575                 }
576         }
577
578         return;
579 }
580
581 /*
582  * Send our dying words back to our parent before we perish.
583  */
584 int
585 tell_parent(char *dom, struct sockaddr_in *addr)
586 {
587         char buf[YPMAXDOMAIN + 1];
588         struct timeval timeout;
589         fd_set fds;
590
591         timeout.tv_sec = 5;
592         timeout.tv_usec = 0;
593
594         sprintf(buf, "%s", broad_domain->dom_domain);
595         if (write(BROADFD, &buf, sizeof(buf)) < 0)
596                 return(1);
597
598         /*
599          * Stay in sync with parent: wait for it to read our first
600          * message before sending the second.
601          */
602
603         FD_ZERO(&fds);
604         FD_SET(BROADFD, &fds);
605         if (select(FD_SETSIZE, NULL, &fds, NULL, &timeout) == -1)
606                 return(1);
607         if (FD_ISSET(BROADFD, &fds)) {
608                 if (write(BROADFD, addr, sizeof(struct sockaddr_in)) < 0)
609                         return(1);
610         } else {
611                 return(1);
612         }
613
614         close(BROADFD);
615         return (0);
616 }
617
618 bool_t broadcast_result(out, addr)
619 bool_t *out;
620 struct sockaddr_in *addr;
621 {
622         if (retries >= MAX_RETRIES) {
623                 bzero(addr, sizeof(struct sockaddr_in));
624                 if (tell_parent(broad_domain->dom_domain, addr))
625                         syslog(LOG_WARNING, "lost connection to parent");
626                 return (TRUE);
627         }
628
629         if (yp_restricted && verify(addr->sin_addr)) {
630                 retries++;
631                 syslog(LOG_NOTICE, "NIS server at %s not in restricted mode access list -- rejecting.\n",inet_ntoa(addr->sin_addr));
632                 return (FALSE);
633         } else {
634                 if (tell_parent(broad_domain->dom_domain, addr))
635                         syslog(LOG_WARNING, "lost connection to parent");
636                 return (TRUE);
637         }
638 }
639
640 /*
641  * The right way to send RPC broadcasts.
642  * Use the clnt_broadcast() RPC service. Unfortunately, clnt_broadcast()
643  * blocks while waiting for replies, so we have to fork off separate
644  * broadcaster processes that do the waiting and then transmit their
645  * results back to the parent for processing. We also have to remember
646  * to save the name of the domain we're trying to bind in a global
647  * variable since clnt_broadcast() provides no way to pass things to
648  * the 'eachresult' callback function.
649  */
650 void
651 broadcast(struct _dom_binding *ypdb)
652 {
653         bool_t out = FALSE;
654         enum clnt_stat stat;
655
656         if (children >= MAX_CHILDREN || ypdb->dom_broadcast_pid)
657                 return;
658
659         if (pipe(ypdb->dom_pipe_fds) < 0) {
660                 syslog(LOG_WARNING, "pipe: %m");
661                 return;
662         }
663
664         if (ypdb->dom_vers == -1 && (long)ypdb->dom_server_addr.sin_addr.s_addr) {
665                 if (not_responding_count++ >= NOT_RESPONDING_HYSTERESIS) {
666                         not_responding_count = NOT_RESPONDING_HYSTERESIS;
667                         syslog(LOG_WARNING, "NIS server [%s] for domain \"%s\" not responding",
668                             inet_ntoa(ypdb->dom_server_addr.sin_addr), ypdb->dom_domain);
669                 }
670         }
671
672         broad_domain = ypdb;
673         flock(ypdb->dom_lockfd, LOCK_UN);
674
675         switch ((ypdb->dom_broadcast_pid = fork())) {
676         case 0:
677                 close(READFD);
678                 signal(SIGCHLD, SIG_DFL);
679                 signal(SIGTERM, SIG_DFL);
680                 break;
681         case -1:
682                 syslog(LOG_WARNING, "fork: %m");
683                 close(READFD);
684                 close(WRITEFD);
685                 return;
686         default:
687                 close(WRITEFD);
688                 FD_SET(READFD, &svc_fdset);
689                 children++;
690                 return;
691         }
692
693         /* Release all locks before doing anything else. */
694         while (ypbindlist) {
695                 close(ypbindlist->dom_lockfd);
696                 ypbindlist = ypbindlist->dom_pnext;
697         }
698         close(yplockfd);
699
700         /*
701          * Special 'many-cast' behavior. If we're in restricted mode,
702          * we have a list of possible server addresses to try. What
703          * we can do is transmit to each ypserv's YPPROC_DOMAIN_NONACK
704          * procedure and time the replies. Whoever replies fastest
705          * gets to be our server. Note that this is not a broadcast
706          * operation: we transmit uni-cast datagrams only.
707          */
708         if (yp_restricted && yp_manycast) {
709                 short                   port;
710                 int                     i;
711                 struct sockaddr_in      sin;
712
713                 i = __yp_ping(restricted_addrs, yp_restricted,
714                                 ypdb->dom_domain, &port);
715                 if (i == -1) {
716                         bzero(&ypdb->dom_server_addr,
717                             sizeof(struct sockaddr_in));
718                         if (tell_parent(ypdb->dom_domain,
719                                 &ypdb->dom_server_addr))
720                         syslog(LOG_WARNING, "lost connection to parent");
721                 } else {
722                         bzero(&sin, sizeof(struct sockaddr_in));
723                         bcopy(&restricted_addrs[i],
724                             &sin.sin_addr, sizeof(struct in_addr));
725                         sin.sin_family = AF_INET;
726                         sin.sin_port = port;
727                         if (tell_parent(broad_domain->dom_domain, &sin))
728                                 syslog(LOG_WARNING,
729                                         "lost connection to parent");
730                 }
731                 _exit(0);
732         }
733
734         retries = 0;
735
736         {
737                 char *ptr;
738
739                 ptr = ypdb->dom_domain;
740                 stat = clnt_broadcast(YPPROG, YPVERS, YPPROC_DOMAIN_NONACK,
741                         (xdrproc_t)xdr_domainname, &ptr,
742                         (xdrproc_t)xdr_bool, &out,
743                         (resultproc_t)broadcast_result);
744         }
745
746         if (stat != RPC_SUCCESS) {
747                 bzero(&ypdb->dom_server_addr,
748                     sizeof(struct sockaddr_in));
749                 if (tell_parent(ypdb->dom_domain, &ypdb->dom_server_addr))
750                         syslog(LOG_WARNING, "lost connection to parent");
751         }
752
753         _exit(0);
754 }
755
756 /*
757  * The right way to check if a server is alive.
758  * Attempt to get a client handle pointing to the server and send a
759  * YPPROC_DOMAIN. If we can't get a handle or we get a reply of FALSE,
760  * we invalidate this binding entry and send out a broadcast to try to
761  * establish a new binding. Note that we treat non-default domains
762  * specially: once bound, we keep tabs on our server, but if it
763  * goes away and fails to respond after one round of broadcasting, we
764  * abandon it until a client specifically references it again. We make
765  * every effort to keep our default domain bound, however, since we
766  * need it to keep the system on its feet.
767  */
768 int
769 ping(struct _dom_binding *ypdb)
770 {
771         bool_t out;
772         struct timeval interval, timeout;
773         enum clnt_stat stat;
774         int rpcsock = RPC_ANYSOCK;
775         CLIENT *client_handle;
776
777         interval.tv_sec = FAIL_THRESHOLD;
778         interval.tv_usec = 0;
779         timeout.tv_sec = FAIL_THRESHOLD;
780         timeout.tv_usec = 0;
781
782         if (ypdb->dom_broadcast_pid)
783                 return(1);
784
785         if ((client_handle = clntudp_bufcreate(&ypdb->dom_server_addr,
786                 YPPROG, YPVERS, interval, &rpcsock, RPCSMALLMSGSIZE,
787                 RPCSMALLMSGSIZE)) == (CLIENT *)NULL) {
788                 /* Can't get a handle: we're dead. */
789                 ypdb->dom_alive = 0;
790                 ypdb->dom_vers = -1;
791                 broadcast(ypdb);
792                 return(1);
793         }
794
795         {
796                 char *ptr;
797
798                 ptr = ypdb->dom_domain;
799
800                 stat = clnt_call(client_handle, YPPROC_DOMAIN,
801                     (xdrproc_t)xdr_domainname, &ptr,
802                     (xdrproc_t)xdr_bool, &out, timeout);
803                 if (stat != RPC_SUCCESS || out == FALSE) {
804                         ypdb->dom_alive = 0;
805                         ypdb->dom_vers = -1;
806                         clnt_destroy(client_handle);
807                         broadcast(ypdb);
808                         return(1);
809                 }
810         }
811
812         clnt_destroy(client_handle);
813         return(0);
814 }
815
816 void
817 rpc_received(char *dom, struct sockaddr_in *raddrp, int force)
818 {
819         struct _dom_binding *ypdb, *prev = NULL;
820         struct iovec iov[2];
821         struct ypbind_resp ybr;
822         char path[MAXPATHLEN];
823         int fd;
824
825         /*printf("returned from %s/%d about %s\n", inet_ntoa(raddrp->sin_addr),
826                ntohs(raddrp->sin_port), dom);*/
827
828         if (dom == NULL)
829                 return;
830
831         for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
832                 if (strcmp(ypdb->dom_domain, dom) == 0)
833                         break;
834                 prev = ypdb;
835         }
836
837         if (ypdb && force) {
838                 if (ypdb->dom_broadcast_pid) {
839                         kill(ypdb->dom_broadcast_pid, SIGINT);
840                         close(READFD);
841                         FD_CLR(READFD, &fdsr);
842                         FD_CLR(READFD, &svc_fdset);
843                         READFD = WRITEFD = -1;
844                 }
845         }
846
847         /* if in secure mode, check originating port number */
848         if ((ypsecuremode && (ntohs(raddrp->sin_port) >= IPPORT_RESERVED))) {
849             syslog(LOG_WARNING, "Rejected NIS server on [%s/%d] for domain %s.",
850                    inet_ntoa(raddrp->sin_addr), ntohs(raddrp->sin_port),
851                    dom);
852             if (ypdb != NULL) {
853                 ypdb->dom_broadcast_pid = 0;
854                 ypdb->dom_alive = 0;
855             }
856             return;
857         }
858
859         if (raddrp->sin_addr.s_addr == (long)0) {
860                 switch (ypdb->dom_default) {
861                 case 0:
862                         if (prev == NULL)
863                                 ypbindlist = ypdb->dom_pnext;
864                         else
865                                 prev->dom_pnext = ypdb->dom_pnext;
866                         sprintf(path, "%s/%s.%ld", BINDINGDIR,
867                                 ypdb->dom_domain, YPVERS);
868                         close(ypdb->dom_lockfd);
869                         unlink(path);
870                         free(ypdb);
871                         domains--;
872                         return;
873                 case 1:
874                         ypdb->dom_broadcast_pid = 0;
875                         ypdb->dom_alive = 0;
876                         broadcast(ypdb);
877                         return;
878                 default:
879                         break;
880                 }
881         }
882
883         if (ypdb == NULL) {
884                 if (force == 0)
885                         return;
886                 ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
887                 if (ypdb == NULL) {
888                         syslog(LOG_WARNING, "malloc: %m");
889                         return;
890                 }
891                 bzero(ypdb, sizeof *ypdb);
892                 strncpy(ypdb->dom_domain, dom, sizeof ypdb->dom_domain);
893                 ypdb->dom_lockfd = -1;
894                 ypdb->dom_default = 0;
895                 ypdb->dom_pnext = ypbindlist;
896                 ypbindlist = ypdb;
897         }
898
899         /* We've recovered from a crash: inform the world. */
900         if (ypdb->dom_vers == -1 && ypdb->dom_server_addr.sin_addr.s_addr) {
901                 if (not_responding_count >= NOT_RESPONDING_HYSTERESIS) {
902                         not_responding_count = 0;
903                         syslog(LOG_WARNING, "NIS server [%s] for domain \"%s\" OK",
904                             inet_ntoa(raddrp->sin_addr), ypdb->dom_domain);
905                 }
906         }
907
908         bcopy(raddrp, &ypdb->dom_server_addr,
909                 sizeof ypdb->dom_server_addr);
910
911         ypdb->dom_vers = YPVERS;
912         ypdb->dom_alive = 1;
913         ypdb->dom_broadcast_pid = 0;
914
915         if (ypdb->dom_lockfd != -1)
916                 close(ypdb->dom_lockfd);
917
918         sprintf(path, "%s/%s.%ld", BINDINGDIR,
919                 ypdb->dom_domain, ypdb->dom_vers);
920 #ifdef O_SHLOCK
921         if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) {
922                 (void)mkdir(BINDINGDIR, 0755);
923                 if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1)
924                         return;
925         }
926 #else
927         if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) {
928                 (void)mkdir(BINDINGDIR, 0755);
929                 if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1)
930                         return;
931         }
932         flock(fd, LOCK_SH);
933 #endif
934
935         /*
936          * ok, if BINDINGDIR exists, and we can create the binding file,
937          * then write to it..
938          */
939         ypdb->dom_lockfd = fd;
940
941         iov[0].iov_base = (char *)&(udptransp->xp_port);
942         iov[0].iov_len = sizeof udptransp->xp_port;
943         iov[1].iov_base = (char *)&ybr;
944         iov[1].iov_len = sizeof ybr;
945
946         bzero(&ybr, sizeof ybr);
947         ybr.ypbind_status = YPBIND_SUCC_VAL;
948         *(u_int32_t *)&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr = raddrp->sin_addr.s_addr;
949         *(u_short *)&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port = raddrp->sin_port;
950
951         if (writev(ypdb->dom_lockfd, iov, 2) != iov[0].iov_len + iov[1].iov_len) {
952                 syslog(LOG_WARNING, "write: %m");
953                 close(ypdb->dom_lockfd);
954                 ypdb->dom_lockfd = -1;
955                 return;
956         }
957 }
958
959 /*
960  * Check address against list of allowed servers. Return 0 if okay,
961  * 1 if not matched.
962  */
963 int
964 verify(struct in_addr addr)
965 {
966         int i;
967
968         for (i = 0; i < RESTRICTED_SERVERS; i++)
969                 if (!bcmp(&addr, &restricted_addrs[i], sizeof(struct in_addr)))
970                         return(0);
971
972         return(1);
973 }
974
975 /*
976  * Try to set restricted mode. We default to normal mode if we can't
977  * resolve the specified hostnames.
978  */
979 void
980 yp_restricted_mode(char *args)
981 {
982         struct hostent *h;
983         int i = 0;
984         char *s;
985
986         /* Find the restricted domain. */
987         if ((s = strsep(&args, ",")) == NULL)
988                 return;
989         domain_name = s;
990
991         /* Get the addresses of the servers. */
992         while ((s = strsep(&args, ",")) != NULL && i < RESTRICTED_SERVERS) {
993                 if ((h = gethostbyname(s)) == NULL)
994                         return;
995                 bcopy (h->h_addr_list[0], &restricted_addrs[i],
996                     sizeof(struct in_addr));
997                 i++;
998         }
999
1000         /* ypset and ypsetme not allowed with restricted mode */
1001         ypsetmode = YPSET_NO;
1002
1003         yp_restricted = i;
1004         return;
1005 }