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