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_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() 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 } 656