xref: /freebsd/usr.sbin/rpc.statd/statd.c (revision 076ad2f836d5f49dc1375f1677335a48fe0d4b82)
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) __dead2;
76 
77 static void usage(void) __dead2;
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_family = si.si_af;
347 	hints.ai_socktype = si.si_socktype;
348 	hints.ai_protocol = si.si_proto;
349 
350 	/*
351 	 * Bind to specific IPs if asked to
352 	 */
353 	nhostsbak = nhosts;
354 	while (nhostsbak > 0) {
355 		--nhostsbak;
356 		sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
357 		if (sock_fd == NULL)
358 			out_of_mem();
359 		sock_fd[sock_fdcnt++] = -1;	/* Set invalid for now. */
360 		mallocd_res = 0;
361 		hints.ai_flags = AI_PASSIVE;
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(void)
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(void)
651 {
652 
653 	syslog(LOG_ERR, "out of memory");
654 	exit(2);
655 }
656