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