]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/rpc.statd/statd.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / rpc.statd / statd.c
1 /*
2  * Copyright (c) 1995
3  *      A.R. Gordon (andrew.gordon@net-tel.co.uk).  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 for the FreeBSD project
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 ANDREW GORDON 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 THE AUTHOR 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 /* main() function for status monitor daemon.  Some of the code in this */
35 /* file was generated by running rpcgen /usr/include/rpcsvc/sm_inter.x  */
36 /* The actual program logic is in the file procs.c                      */
37
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40
41 #include <err.h>
42 #include <errno.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <rpc/rpc.h>
46 #include <rpc/rpc_com.h>
47 #include <string.h>
48 #include <syslog.h>
49 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <sys/wait.h>
52 #include <netinet/in.h>
53 #include <arpa/inet.h>
54 #include <netdb.h>
55 #include <signal.h>
56 #include <unistd.h>
57 #include "statd.h"
58
59 #define GETPORT_MAXTRY  20      /* Max tries to get a port # */
60
61 int debug = 0;          /* Controls syslog() calls for debug messages   */
62
63 char **hosts, *svcport_str = NULL;
64 int nhosts = 0;
65 int xcreated = 0;
66 static int      mallocd_svcport = 0;
67 static int      *sock_fd;
68 static int      sock_fdcnt;
69 static int      sock_fdpos;
70
71 static int      create_service(struct netconfig *nconf);
72 static void     complete_service(struct netconfig *nconf, char *port_str);
73 static void     clearout_service(void);
74 static void handle_sigchld(int sig);
75 void out_of_mem(void);
76
77 static void usage(void);
78
79 int
80 main(int argc, char **argv)
81 {
82   struct sigaction sa;
83   struct netconfig *nconf;
84   void *nc_handle;
85   in_port_t svcport;
86   int ch, i, s;
87   char *endptr, **hosts_bak;
88   int have_v6 = 1;
89   int maxrec = RPC_MAXDATASIZE;
90   int attempt_cnt, port_len, port_pos, ret;
91   char **port_list;
92
93   while ((ch = getopt(argc, argv, "dh:p:")) != -1)
94     switch (ch) {
95     case 'd':
96       debug = 1;
97       break;
98     case 'h':
99       ++nhosts;
100       hosts_bak = hosts;
101       hosts_bak = realloc(hosts, nhosts * sizeof(char *));
102       if (hosts_bak == NULL) {
103               if (hosts != NULL) {
104                       for (i = 0; i < nhosts; i++) 
105                               free(hosts[i]);
106                       free(hosts);
107                       out_of_mem();
108               }
109       }
110       hosts = hosts_bak;
111       hosts[nhosts - 1] = strdup(optarg);
112       if (hosts[nhosts - 1] == NULL) {
113               for (i = 0; i < (nhosts - 1); i++) 
114                       free(hosts[i]);
115               free(hosts);
116               out_of_mem();
117       }
118       break;
119     case 'p':
120       endptr = NULL;
121       svcport = (in_port_t)strtoul(optarg, &endptr, 10);
122       if (endptr == NULL || *endptr != '\0' || svcport == 0 || 
123           svcport >= IPPORT_MAX)
124         usage();
125       
126       svcport_str = strdup(optarg);
127       break;
128     default:
129       usage();
130     }
131   argc -= optind;
132   argv += optind;
133
134   (void)rpcb_unset(SM_PROG, SM_VERS, NULL);
135
136   /*
137    * Check if IPv6 support is present.
138    */
139   s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
140   if (s < 0)
141       have_v6 = 0;
142   else 
143       close(s);
144
145   rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
146
147   /*
148    * If no hosts were specified, add a wildcard entry to bind to
149    * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
150    * list.
151    */
152   if (nhosts == 0) {
153           hosts = malloc(sizeof(char**));
154           if (hosts == NULL)
155                   out_of_mem();
156
157           hosts[0] = "*";
158           nhosts = 1;
159   } else {
160           hosts_bak = hosts;
161           if (have_v6) {
162                   hosts_bak = realloc(hosts, (nhosts + 2) *
163                       sizeof(char *));
164                   if (hosts_bak == NULL) {
165                           for (i = 0; i < nhosts; i++)
166                                   free(hosts[i]);
167                           free(hosts);
168                           out_of_mem();
169                   } else
170                           hosts = hosts_bak;
171
172                   nhosts += 2;
173                   hosts[nhosts - 2] = "::1";
174           } else {
175                   hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
176                   if (hosts_bak == NULL) {
177                           for (i = 0; i < nhosts; i++)
178                                   free(hosts[i]);
179
180                           free(hosts);
181                           out_of_mem();
182                   } else {
183                           nhosts += 1;
184                           hosts = hosts_bak;
185                   }
186           }
187           hosts[nhosts - 1] = "127.0.0.1";
188   }
189
190   attempt_cnt = 1;
191   sock_fdcnt = 0;
192   sock_fd = NULL;
193   port_list = NULL;
194   port_len = 0;
195   nc_handle = setnetconfig();
196   while ((nconf = getnetconfig(nc_handle))) {
197           /* We want to listen only on udp6, tcp6, udp, tcp transports */
198           if (nconf->nc_flag & NC_VISIBLE) {
199                   /* Skip if there's no IPv6 support */
200                   if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
201               /* DO NOTHING */
202                   } else {
203                         ret = create_service(nconf);
204                         if (ret == 1)
205                                 /* Ignore this call */
206                                 continue;
207                         if (ret < 0) {
208                                 /*
209                                  * Failed to bind port, so close off
210                                  * all sockets created and try again
211                                  * if the port# was dynamically
212                                  * assigned via bind(2).
213                                  */
214                                 clearout_service();
215                                 if (mallocd_svcport != 0 &&
216                                     attempt_cnt < GETPORT_MAXTRY) {
217                                         free(svcport_str);
218                                         svcport_str = NULL;
219                                         mallocd_svcport = 0;
220                                 } else {
221                                         errno = EADDRINUSE;
222                                         syslog(LOG_ERR,
223                                             "bindresvport_sa: %m");
224                                         exit(1);
225                                 }
226
227                                 /* Start over at the first service. */
228                                 free(sock_fd);
229                                 sock_fdcnt = 0;
230                                 sock_fd = NULL;
231                                 nc_handle = setnetconfig();
232                                 attempt_cnt++;
233                         } else if (mallocd_svcport != 0 &&
234                             attempt_cnt == GETPORT_MAXTRY) {
235                                 /*
236                                  * For the last attempt, allow
237                                  * different port #s for each nconf
238                                  * by saving the svcport_str and
239                                  * setting it back to NULL.
240                                  */
241                                 port_list = realloc(port_list,
242                                     (port_len + 1) * sizeof(char *));
243                                 if (port_list == NULL)
244                                         out_of_mem();
245                                 port_list[port_len++] = svcport_str;
246                                 svcport_str = NULL;
247                                 mallocd_svcport = 0;
248                         }
249                   }
250           }
251   }
252
253   /*
254    * Successfully bound the ports, so call complete_service() to
255    * do the rest of the setup on the service(s).
256    */
257   sock_fdpos = 0;
258   port_pos = 0;
259   nc_handle = setnetconfig();
260   while ((nconf = getnetconfig(nc_handle))) {
261           /* We want to listen only on udp6, tcp6, udp, tcp transports */
262           if (nconf->nc_flag & NC_VISIBLE) {
263                   /* Skip if there's no IPv6 support */
264                   if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
265               /* DO NOTHING */
266                   } else if (port_list != NULL) {
267                         if (port_pos >= port_len) {
268                                 syslog(LOG_ERR, "too many port#s");
269                                 exit(1);
270                         }
271                         complete_service(nconf, port_list[port_pos++]);
272                   } else
273                         complete_service(nconf, svcport_str);
274           }
275   }
276   endnetconfig(nc_handle);
277   free(sock_fd);
278   if (port_list != NULL) {
279         for (port_pos = 0; port_pos < port_len; port_pos++)
280                 free(port_list[port_pos]);
281         free(port_list);
282   }
283
284   init_file("/var/db/statd.status");
285
286   /* Note that it is NOT sensible to run this program from inetd - the  */
287   /* protocol assumes that it will run immediately at boot time.        */
288   daemon(0, 0);
289   openlog("rpc.statd", 0, LOG_DAEMON);
290   if (debug) syslog(LOG_INFO, "Starting - debug enabled");
291   else syslog(LOG_INFO, "Starting");
292
293   /* Install signal handler to collect exit status of child processes   */
294   sa.sa_handler = handle_sigchld;
295   sigemptyset(&sa.sa_mask);
296   sigaddset(&sa.sa_mask, SIGCHLD);
297   sa.sa_flags = SA_RESTART;
298   sigaction(SIGCHLD, &sa, NULL);
299
300   /* Initialisation now complete - start operating                      */
301   notify_hosts();       /* Forks a process (if necessary) to do the     */
302                         /* SM_NOTIFY calls, which may be slow.          */
303
304   svc_run();    /* Should never return                                  */
305   exit(1);
306 }
307
308 /*
309  * This routine creates and binds sockets on the appropriate
310  * addresses. It gets called one time for each transport.
311  * It returns 0 upon success, 1 for ingore the call and -1 to indicate
312  * bind failed with EADDRINUSE.
313  * Any file descriptors that have been created are stored in sock_fd and
314  * the total count of them is maintained in sock_fdcnt.
315  */
316 static int
317 create_service(struct netconfig *nconf)
318 {
319         struct addrinfo hints, *res = NULL;
320         struct sockaddr_in *sin;
321         struct sockaddr_in6 *sin6;
322         struct __rpc_sockinfo si;
323         int aicode;
324         int fd;
325         int nhostsbak;
326         int r;
327         u_int32_t host_addr[4];  /* IPv4 or IPv6 */
328         int mallocd_res;
329
330         if ((nconf->nc_semantics != NC_TPI_CLTS) &&
331             (nconf->nc_semantics != NC_TPI_COTS) &&
332             (nconf->nc_semantics != NC_TPI_COTS_ORD))
333                 return (1);     /* not my type */
334
335         /*
336          * XXX - using RPC library internal functions.
337          */
338         if (!__rpc_nconf2sockinfo(nconf, &si)) {
339                 syslog(LOG_ERR, "cannot get information for %s",
340                     nconf->nc_netid);
341                 return (1);
342         }
343
344         /* Get rpc.statd's address on this transport */
345         memset(&hints, 0, sizeof hints);
346         hints.ai_flags = AI_PASSIVE;
347         hints.ai_family = si.si_af;
348         hints.ai_socktype = si.si_socktype;
349         hints.ai_protocol = si.si_proto;
350
351         /*
352          * Bind to specific IPs if asked to
353          */
354         nhostsbak = nhosts;
355         while (nhostsbak > 0) {
356                 --nhostsbak;
357                 sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
358                 if (sock_fd == NULL)
359                         out_of_mem();
360                 sock_fd[sock_fdcnt++] = -1;     /* Set invalid for now. */
361                 mallocd_res = 0;
362
363                 /*      
364                  * XXX - using RPC library internal functions.
365                  */
366                 if ((fd = __rpc_nconf2fd(nconf)) < 0) {
367                         syslog(LOG_ERR, "cannot create socket for %s",
368                             nconf->nc_netid);
369                         continue;
370                 }
371                 switch (hints.ai_family) {
372                 case AF_INET:
373                         if (inet_pton(AF_INET, hosts[nhostsbak],
374                             host_addr) == 1) {
375                                 hints.ai_flags |= AI_NUMERICHOST;
376                         } else {
377                                 /*
378                                  * Skip if we have an AF_INET6 address.
379                                  */
380                                 if (inet_pton(AF_INET6, hosts[nhostsbak],
381                                     host_addr) == 1) {
382                                         close(fd);
383                                         continue;
384                                 }
385                         }
386                         break;
387                 case AF_INET6:
388                         if (inet_pton(AF_INET6, hosts[nhostsbak],
389                             host_addr) == 1) {
390                                 hints.ai_flags |= AI_NUMERICHOST;
391                         } else {
392                                 /*
393                                  * Skip if we have an AF_INET address.
394                                  */
395                                 if (inet_pton(AF_INET, hosts[nhostsbak],
396                                     host_addr) == 1) {
397                                         close(fd);
398                                         continue;
399                                 }
400                         }
401                         break;
402                 default:
403                         break;
404                 }
405
406                 /*
407                  * If no hosts were specified, just bind to INADDR_ANY
408                  */
409                 if (strcmp("*", hosts[nhostsbak]) == 0) {
410                         if (svcport_str == NULL) {
411                                 res = malloc(sizeof(struct addrinfo));
412                                 if (res == NULL) 
413                                         out_of_mem();
414                                 mallocd_res = 1;
415                                 res->ai_flags = hints.ai_flags;
416                                 res->ai_family = hints.ai_family;
417                                 res->ai_protocol = hints.ai_protocol;
418                                 switch (res->ai_family) {
419                                 case AF_INET:
420                                         sin = malloc(sizeof(struct sockaddr_in));
421                                         if (sin == NULL) 
422                                                 out_of_mem();
423                                         sin->sin_family = AF_INET;
424                                         sin->sin_port = htons(0);
425                                         sin->sin_addr.s_addr = htonl(INADDR_ANY);
426                                         res->ai_addr = (struct sockaddr*) sin;
427                                         res->ai_addrlen = (socklen_t)
428                                             sizeof(struct sockaddr_in);
429                                         break;
430                                 case AF_INET6:
431                                         sin6 = malloc(sizeof(struct sockaddr_in6));
432                                         if (sin6 == NULL)
433                                                 out_of_mem();
434                                         sin6->sin6_family = AF_INET6;
435                                         sin6->sin6_port = htons(0);
436                                         sin6->sin6_addr = in6addr_any;
437                                         res->ai_addr = (struct sockaddr*) sin6;
438                                         res->ai_addrlen = (socklen_t)
439                                             sizeof(struct sockaddr_in6);
440                                         break;
441                                 default:
442                                         syslog(LOG_ERR, "bad addr fam %d",
443                                             res->ai_family);
444                                         exit(1);
445                                 }
446                         } else { 
447                                 if ((aicode = getaddrinfo(NULL, svcport_str,
448                                     &hints, &res)) != 0) {
449                                         syslog(LOG_ERR,
450                                             "cannot get local address for %s: %s",
451                                             nconf->nc_netid,
452                                             gai_strerror(aicode));
453                                         close(fd);
454                                         continue;
455                                 }
456                         }
457                 } else {
458                         if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
459                             &hints, &res)) != 0) {
460                                 syslog(LOG_ERR,
461                                     "cannot get local address for %s: %s",
462                                     nconf->nc_netid, gai_strerror(aicode));
463                                 close(fd);
464                                 continue;
465                         }
466                 }
467
468                 /* Store the fd. */
469                 sock_fd[sock_fdcnt - 1] = fd;
470
471                 /* Now, attempt the bind. */
472                 r = bindresvport_sa(fd, res->ai_addr);
473                 if (r != 0) {
474                         if (errno == EADDRINUSE && mallocd_svcport != 0) {
475                                 if (mallocd_res != 0) {
476                                         free(res->ai_addr);
477                                         free(res);
478                                 } else
479                                         freeaddrinfo(res);
480                                 return (-1);
481                         }
482                         syslog(LOG_ERR, "bindresvport_sa: %m");
483                         exit(1);
484                 }
485
486                 if (svcport_str == NULL) {
487                         svcport_str = malloc(NI_MAXSERV * sizeof(char));
488                         if (svcport_str == NULL)
489                                 out_of_mem();
490                         mallocd_svcport = 1;
491
492                         if (getnameinfo(res->ai_addr,
493                             res->ai_addr->sa_len, NULL, NI_MAXHOST,
494                             svcport_str, NI_MAXSERV * sizeof(char),
495                             NI_NUMERICHOST | NI_NUMERICSERV))
496                                 errx(1, "Cannot get port number");
497                 }
498                 if (mallocd_res != 0) {
499                         free(res->ai_addr);
500                         free(res);
501                 } else
502                         freeaddrinfo(res);
503                 res = NULL;
504         }
505         return (0);
506 }
507
508 /*
509  * Called after all the create_service() calls have succeeded, to complete
510  * the setup and registration.
511  */
512 static void
513 complete_service(struct netconfig *nconf, char *port_str)
514 {
515         struct addrinfo hints, *res = NULL;
516         struct __rpc_sockinfo si;
517         struct netbuf servaddr;
518         SVCXPRT *transp = NULL;
519         int aicode, fd, nhostsbak;
520         int registered = 0;
521
522         if ((nconf->nc_semantics != NC_TPI_CLTS) &&
523             (nconf->nc_semantics != NC_TPI_COTS) &&
524             (nconf->nc_semantics != NC_TPI_COTS_ORD))
525                 return; /* not my type */
526
527         /*
528          * XXX - using RPC library internal functions.
529          */
530         if (!__rpc_nconf2sockinfo(nconf, &si)) {
531                 syslog(LOG_ERR, "cannot get information for %s",
532                     nconf->nc_netid);
533                 return;
534         }
535
536         nhostsbak = nhosts;
537         while (nhostsbak > 0) {
538                 --nhostsbak;
539                 if (sock_fdpos >= sock_fdcnt) {
540                         /* Should never happen. */
541                         syslog(LOG_ERR, "Ran out of socket fd's");
542                         return;
543                 }
544                 fd = sock_fd[sock_fdpos++];
545                 if (fd < 0)
546                         continue;
547
548                 if (nconf->nc_semantics != NC_TPI_CLTS)
549                         listen(fd, SOMAXCONN);
550
551                 transp = svc_tli_create(fd, nconf, NULL,
552                 RPC_MAXDATASIZE, RPC_MAXDATASIZE);
553
554                 if (transp != (SVCXPRT *) NULL) {
555                         if (!svc_register(transp, SM_PROG, SM_VERS,
556                             sm_prog_1, 0)) {
557                                 syslog(LOG_ERR, "can't register on %s",
558                                     nconf->nc_netid);
559                         } else {
560                                 if (!svc_reg(transp, SM_PROG, SM_VERS,
561                                     sm_prog_1, NULL)) 
562                                         syslog(LOG_ERR,
563                                             "can't register %s SM_PROG service",
564                                             nconf->nc_netid);
565                         }
566                 } else 
567                         syslog(LOG_WARNING, "can't create %s services",
568                             nconf->nc_netid);
569
570                 if (registered == 0) {
571                         registered = 1;
572                         memset(&hints, 0, sizeof hints);
573                         hints.ai_flags = AI_PASSIVE;
574                         hints.ai_family = si.si_af;
575                         hints.ai_socktype = si.si_socktype;
576                         hints.ai_protocol = si.si_proto;
577
578
579                         if ((aicode = getaddrinfo(NULL, port_str, &hints,
580                             &res)) != 0) {
581                                 syslog(LOG_ERR, "cannot get local address: %s",
582                                     gai_strerror(aicode));
583                                 exit(1);
584                         }
585
586                         servaddr.buf = malloc(res->ai_addrlen);
587                         memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
588                         servaddr.len = res->ai_addrlen;
589
590                         rpcb_set(SM_PROG, SM_VERS, nconf, &servaddr);
591
592                         xcreated++;
593                         freeaddrinfo(res);
594                 }
595         } /* end while */
596 }
597
598 /*
599  * Clear out sockets after a failure to bind one of them, so that the
600  * cycle of socket creation/binding can start anew.
601  */
602 static void
603 clearout_service(void)
604 {
605         int i;
606
607         for (i = 0; i < sock_fdcnt; i++) {
608                 if (sock_fd[i] >= 0) {
609                         shutdown(sock_fd[i], SHUT_RDWR);
610                         close(sock_fd[i]);
611                 }
612         }
613 }
614
615 static void
616 usage()
617 {
618       fprintf(stderr, "usage: rpc.statd [-d] [-h <bindip>] [-p <port>]\n");
619       exit(1);
620 }
621
622 /* handle_sigchld ---------------------------------------------------------- */
623 /*
624    Purpose:     Catch SIGCHLD and collect process status
625    Retruns:     Nothing.
626    Notes:       No special action required, other than to collect the
627                 process status and hence allow the child to die:
628                 we only use child processes for asynchronous transmission
629                 of SM_NOTIFY to other systems, so it is normal for the
630                 children to exit when they have done their work.
631 */
632
633 static void handle_sigchld(int sig __unused)
634 {
635   int pid, status;
636   pid = wait4(-1, &status, WNOHANG, (struct rusage*)0);
637   if (!pid) syslog(LOG_ERR, "Phantom SIGCHLD??");
638   else if (status == 0)
639   {
640     if (debug) syslog(LOG_DEBUG, "Child %d exited OK", pid);
641   }
642   else syslog(LOG_ERR, "Child %d failed with status %d", pid,
643     WEXITSTATUS(status));
644 }
645
646 /*
647  * Out of memory, fatal
648  */
649 void
650 out_of_mem()
651 {
652
653         syslog(LOG_ERR, "out of memory");
654         exit(2);
655 }