diff --git a/usr.sbin/rpc.statd/rpc.statd.8 b/usr.sbin/rpc.statd/rpc.statd.8 index 14d06e10b161..5b53ff1e9924 100644 --- a/usr.sbin/rpc.statd/rpc.statd.8 +++ b/usr.sbin/rpc.statd/rpc.statd.8 @@ -33,7 +33,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 3, 2007 +.Dd November 1, 2007 .Dt RPC.STATD 8 .Os .Sh NAME @@ -42,6 +42,7 @@ .Sh SYNOPSIS .Nm .Op Fl d +.Op Fl h Ar bindip .Op Fl p Ar port .Sh DESCRIPTION The @@ -74,6 +75,23 @@ These messages are logged with level LOG_DEBUG and facility LOG_DAEMON. Error conditions are logged irrespective of this option, using level LOG_ERR. +.It Fl h Ar bindip +Specify specific IP addresses to bind to. +This option may be specified multiple times. +If no +.Fl h +option is specified, +.Nm +will bind to +.Dv INADDR_ANY . +Note that when specifying IP addresses with +.Fl h , +.Nm +will automatically add +.Li 127.0.0.1 +and if IPv6 is enabled, +.Li ::1 +to the list. .It Fl p The .Fl p diff --git a/usr.sbin/rpc.statd/statd.c b/usr.sbin/rpc.statd/statd.c index 4348865ea891..3b6d010c34df 100644 --- a/usr.sbin/rpc.statd/statd.c +++ b/usr.sbin/rpc.statd/statd.c @@ -46,42 +46,73 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include +#include +#include +#include #include #include #include "statd.h" int debug = 0; /* Controls syslog() calls for debug messages */ -static void handle_sigchld(int sig); -static void usage(void); +char **hosts, *svcport_str = NULL; +int nhosts = 0; +int xcreated = 0; -const char *transports[] = { "udp", "tcp", "udp6", "tcp6" }; +void create_service(struct netconfig *nconf); +static void handle_sigchld(int sig); +void out_of_mem(void); + +static void usage(void); int main(int argc, char **argv) { - SVCXPRT *transp; struct sigaction sa; struct netconfig *nconf; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - int ch, i, maxindex, r, s, sock; - char *endptr; + void *nc_handle; + in_port_t svcport; + int ch, i, s; + char *endptr, **hosts_bak; + int have_v6 = 1; int maxrec = RPC_MAXDATASIZE; - in_port_t svcport = 0; - while ((ch = getopt(argc, argv, "dp:")) != -1) + while ((ch = getopt(argc, argv, "dh:p:")) != -1) switch (ch) { case 'd': debug = 1; break; + case 'h': + ++nhosts; + hosts_bak = hosts; + hosts_bak = realloc(hosts, nhosts * sizeof(char *)); + if (hosts_bak == NULL) { + if (hosts != NULL) { + for (i = 0; i < nhosts; i++) + free(hosts[i]); + free(hosts); + out_of_mem(); + } + } + hosts = hosts_bak; + hosts[nhosts - 1] = strdup(optarg); + if (hosts[nhosts - 1] == NULL) { + for (i = 0; i < (nhosts - 1); i++) + free(hosts[i]); + free(hosts); + out_of_mem(); + } + break; case 'p': endptr = NULL; svcport = (in_port_t)strtoul(optarg, &endptr, 10); if (endptr == NULL || *endptr != '\0' || svcport == 0 || svcport >= IPPORT_MAX) usage(); + + svcport_str = strdup(optarg); break; default: usage(); @@ -96,95 +127,68 @@ main(int argc, char **argv) */ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (s < 0) - maxindex = 2; - else { + have_v6 = 0; + else close(s); - maxindex = 4; - } - - if (svcport != 0) { - bzero(&sin, sizeof(struct sockaddr_in)); - sin.sin_len = sizeof(struct sockaddr_in); - sin.sin_family = AF_INET; - sin.sin_port = htons(svcport); - - bzero(&sin6, sizeof(struct sockaddr_in6)); - sin6.sin6_len = sizeof(struct sockaddr_in6); - sin6.sin6_family = AF_INET6; - sin6.sin6_port = htons(svcport); - } rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); - for (i = 0; i < maxindex; i++) { - nconf = getnetconfigent(transports[i]); - if (nconf == NULL) - errx(1, "cannot get %s netconf: %s.", transports[i], - nc_sperror()); + /* + * If no hosts were specified, add a wildcard entry to bind to + * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the + * list. + */ + if (nhosts == 0) { + hosts = malloc(sizeof(char**)); + if (hosts == NULL) + out_of_mem(); - if (svcport != 0) { - if (strcmp(nconf->nc_netid, "udp6") == 0) { - sock = socket(AF_INET6, SOCK_DGRAM, - IPPROTO_UDP); - if (sock != -1) { - r = bindresvport_sa(sock, - (struct sockaddr *)&sin6); - if (r != 0) { - syslog(LOG_ERR, "bindresvport: %m"); - exit(1); + hosts[0] = "*"; + nhosts = 1; + } else { + hosts_bak = hosts; + if (have_v6) { + hosts_bak = realloc(hosts, (nhosts + 2) * + sizeof(char *)); + if (hosts_bak == NULL) { + for (i = 0; i < nhosts; i++) + free(hosts[i]); + free(hosts); + out_of_mem(); + } else + hosts = hosts_bak; + + nhosts += 2; + hosts[nhosts - 2] = "::1"; + } else { + hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *)); + if (hosts_bak == NULL) { + for (i = 0; i < nhosts; i++) + free(hosts[i]); + + free(hosts); + out_of_mem(); + } else { + nhosts += 1; + hosts = hosts_bak; } - } - } else if (strcmp(nconf->nc_netid, "udp") == 0) { - sock = socket(AF_INET, SOCK_DGRAM, - IPPROTO_UDP); - if (sock != -1) { - r = bindresvport(sock, &sin); - if (r != 0) { - syslog(LOG_ERR, "bindresvport: %m"); - exit(1); - } - } - } else if (strcmp(nconf->nc_netid, "tcp6") == 0) { - sock = socket(AF_INET6, SOCK_STREAM, - IPPROTO_TCP); - if (sock != -1) { - r = bindresvport_sa(sock, - (struct sockaddr *)&sin6); - if (r != 0) { - syslog(LOG_ERR, "bindresvport: %m"); - exit(1); - } - } - } else if (strcmp(nconf->nc_netid, "tcp") == 0) { - sock = socket(AF_INET, SOCK_STREAM, - IPPROTO_TCP); - if (sock != -1) { - r = bindresvport(sock, &sin); - if (r != 0) { - syslog(LOG_ERR, "bindresvport: %m"); - exit(1); - } - } } - - transp = svc_tli_create(sock, nconf, NULL, - RPC_MAXDATASIZE, RPC_MAXDATASIZE); - } else { - transp = svc_tli_create(RPC_ANYFD, nconf, NULL, - RPC_MAXDATASIZE, RPC_MAXDATASIZE); - } - - if (transp == NULL) { - errx(1, "cannot create %s service.", transports[i]); - /* NOTREACHED */ - } - if (!svc_reg(transp, SM_PROG, SM_VERS, sm_prog_1, nconf)) { - errx(1, "unable to register (SM_PROG, NLM_SM, %s)", - transports[i]); - /* NOTREACHED */ - } - freenetconfigent(nconf); + hosts[nhosts - 1] = "127.0.0.1"; } + + nc_handle = setnetconfig(); + while ((nconf = getnetconfig(nc_handle))) { + /* We want to listen only on udp6, tcp6, udp, tcp transports */ + if (nconf->nc_flag & NC_VISIBLE) { + /* Skip if there's no IPv6 support */ + if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) { + /* DO NOTHING */ + } else { + create_service(nconf); + } + } + } + endnetconfig(nc_handle); init_file("/var/db/statd.status"); /* Note that it is NOT sensible to run this program from inetd - the */ @@ -209,10 +213,222 @@ main(int argc, char **argv) exit(1); } +/* + * This routine creates and binds sockets on the appropriate + * addresses. It gets called one time for each transport and + * registrates the service with rpcbind on that trasport. + */ +void +create_service(struct netconfig *nconf) +{ + struct addrinfo hints, *res = NULL; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + struct __rpc_sockinfo si; + struct netbuf servaddr; + SVCXPRT *transp = NULL; + int aicode; + int fd; + int nhostsbak; + int r; + int registered = 0; + u_int32_t host_addr[4]; /* IPv4 or IPv6 */ + + if ((nconf->nc_semantics != NC_TPI_CLTS) && + (nconf->nc_semantics != NC_TPI_COTS) && + (nconf->nc_semantics != NC_TPI_COTS_ORD)) + return; /* not my type */ + + /* + * XXX - using RPC library internal functions. + */ + if (!__rpc_nconf2sockinfo(nconf, &si)) { + syslog(LOG_ERR, "cannot get information for %s", + nconf->nc_netid); + return; + } + + /* Get rpc.statd's address on this transport */ + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = si.si_af; + hints.ai_socktype = si.si_socktype; + hints.ai_protocol = si.si_proto; + + /* + * Bind to specific IPs if asked to + */ + nhostsbak = nhosts; + while (nhostsbak > 0) { + --nhostsbak; + + /* + * XXX - using RPC library internal functions. + */ + if ((fd = __rpc_nconf2fd(nconf)) < 0) { + syslog(LOG_ERR, "cannot create socket for %s", + nconf->nc_netid); + continue; + } + switch (hints.ai_family) { + case AF_INET: + if (inet_pton(AF_INET, hosts[nhostsbak], + host_addr) == 1) { + hints.ai_flags &= AI_NUMERICHOST; + } else { + /* + * Skip if we have an AF_INET6 address. + */ + if (inet_pton(AF_INET6, hosts[nhostsbak], + host_addr) == 1) { + close(fd); + continue; + } + } + break; + case AF_INET6: + if (inet_pton(AF_INET6, hosts[nhostsbak], + host_addr) == 1) { + hints.ai_flags &= AI_NUMERICHOST; + } else { + /* + * Skip if we have an AF_INET address. + */ + if (inet_pton(AF_INET, hosts[nhostsbak], + host_addr) == 1) { + close(fd); + continue; + } + } + break; + default: + break; + } + + /* + * If no hosts were specified, just bind to INADDR_ANY + */ + if (strcmp("*", hosts[nhostsbak]) == 0) { + if (svcport_str == NULL) { + res = malloc(sizeof(struct addrinfo)); + if (res == NULL) + out_of_mem(); + res->ai_flags = hints.ai_flags; + res->ai_family = hints.ai_family; + res->ai_protocol = hints.ai_protocol; + switch (res->ai_family) { + case AF_INET: + sin = malloc(sizeof(struct sockaddr_in)); + if (sin == NULL) + out_of_mem(); + sin->sin_family = AF_INET; + sin->sin_port = htons(0); + sin->sin_addr.s_addr = htonl(INADDR_ANY); + res->ai_addr = (struct sockaddr*) sin; + res->ai_addrlen = (socklen_t) + sizeof(res->ai_addr); + break; + case AF_INET6: + sin6 = malloc(sizeof(struct sockaddr_in6)); + if (res->ai_addr == NULL) + out_of_mem(); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(0); + sin6->sin6_addr = in6addr_any; + res->ai_addr = (struct sockaddr*) sin6; + res->ai_addrlen = (socklen_t) sizeof(res->ai_addr); + break; + default: + break; + } + } else { + if ((aicode = getaddrinfo(NULL, svcport_str, + &hints, &res)) != 0) { + syslog(LOG_ERR, + "cannot get local address for %s: %s", + nconf->nc_netid, + gai_strerror(aicode)); + continue; + } + } + } else { + if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str, + &hints, &res)) != 0) { + syslog(LOG_ERR, + "cannot get local address for %s: %s", + nconf->nc_netid, gai_strerror(aicode)); + continue; + } + } + + r = bindresvport_sa(fd, res->ai_addr); + if (r != 0) { + syslog(LOG_ERR, "bindresvport_sa: %m"); + exit(1); + } + + transp = svc_tli_create(fd, nconf, NULL, + RPC_MAXDATASIZE, RPC_MAXDATASIZE); + + if (transp != (SVCXPRT *) NULL) { + if (!svc_register(transp, SM_PROG, SM_VERS, + sm_prog_1, 0)) { + syslog(LOG_ERR, "can't register on %s", + nconf->nc_netid); + } else { + if (!svc_reg(transp, SM_PROG, SM_VERS, + sm_prog_1, NULL)) + syslog(LOG_ERR, + "can't register %s SM_PROG service", + nconf->nc_netid); + } + } else + syslog(LOG_WARNING, "can't create %s services", + nconf->nc_netid); + + if (registered == 0) { + registered = 1; + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = si.si_af; + hints.ai_socktype = si.si_socktype; + hints.ai_protocol = si.si_proto; + + if (svcport_str == NULL) { + svcport_str = malloc(NI_MAXSERV * sizeof(char)); + if (svcport_str == NULL) + out_of_mem(); + + if (getnameinfo(res->ai_addr, + res->ai_addr->sa_len, NULL, NI_MAXHOST, + svcport_str, NI_MAXSERV * sizeof(char), + NI_NUMERICHOST | NI_NUMERICSERV)) + errx(1, "Cannot get port number"); + } + + if((aicode = getaddrinfo(NULL, svcport_str, &hints, + &res)) != 0) { + syslog(LOG_ERR, "cannot get local address: %s", + gai_strerror(aicode)); + exit(1); + } + + servaddr.buf = malloc(res->ai_addrlen); + memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen); + servaddr.len = res->ai_addrlen; + + rpcb_set(SM_PROG, SM_VERS, nconf, &servaddr); + + xcreated++; + freeaddrinfo(res); + } + } /* end while */ +} + static void usage() { - fprintf(stderr, "usage: rpc.statd [-d] [-p ]\n"); + fprintf(stderr, "usage: rpc.statd [-d] [-h ] [-p ]\n"); exit(1); } @@ -240,3 +456,13 @@ static void handle_sigchld(int sig __unused) WEXITSTATUS(status)); } +/* + * Out of memory, fatal + */ +void +out_of_mem() +{ + + syslog(LOG_ERR, "out of memory"); + exit(2); +}