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