From ba150ecc5d21f93b337a6c70e7672d776b793d8b Mon Sep 17 00:00:00 2001 From: matteo Date: Fri, 2 Nov 2007 14:51:53 +0000 Subject: Add the -h option to rpc.lockd, similar to the one in nfsd(8), in mountd(8), and in rpc.statd(8) -h bindip Specify specific IP addresses to bind to for TCP and UDP requests. This option may be specified multiple times. If no -h option is specified, rpc.lockd will bind to INADDR_ANY. Note that when specifying IP addresses with -h, rpc.lockd will automatically add 127.0.0.1 and if IPv6 is enabled, ::1 to the list. PR: bin/98500 MFC after: 1 week --- usr.sbin/rpc.lockd/lockd.c | 439 ++++++++++++++++++++++++++++++----------- usr.sbin/rpc.lockd/rpc.lockd.8 | 20 +- 2 files changed, 348 insertions(+), 111 deletions(-) (limited to 'usr.sbin/rpc.lockd') diff --git a/usr.sbin/rpc.lockd/lockd.c b/usr.sbin/rpc.lockd/lockd.c index 15c9d69..6c2e2c7 100644 --- a/usr.sbin/rpc.lockd/lockd.c +++ b/usr.sbin/rpc.lockd/lockd.c @@ -49,6 +49,9 @@ __RCSID("$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $"); #include #include +#include +#include + #include #include #include @@ -59,6 +62,7 @@ __RCSID("$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $"); #include #include #include +#include #include #include @@ -74,35 +78,35 @@ int grace_expired; int nsm_state; pid_t client_pid; struct mon mon_host; +char **hosts, *svcport_str = NULL; +int nhosts = 0; +int xcreated = 0; +void create_service(struct netconfig *nconf); void init_nsm(void); void nlm_prog_0(struct svc_req *, SVCXPRT *); void nlm_prog_1(struct svc_req *, SVCXPRT *); void nlm_prog_3(struct svc_req *, SVCXPRT *); void nlm_prog_4(struct svc_req *, SVCXPRT *); +void out_of_mem(void); void usage(void); void sigalarm_handler(void); -const char *transports[] = { "udp", "tcp", "udp6", "tcp6" }; - int -main(argc, argv) - int argc; - char **argv; +main(int argc, char **argv) { - SVCXPRT *transp; - int ch, i, maxindex, r, s, sock; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - char *endptr; + int ch, i, s; + void *nc_handle; + char *endptr, **hosts_bak; struct sigaction sigalarm; int grace_period = 30; struct netconfig *nconf; + int have_v6 = 1; int maxrec = RPC_MAXDATASIZE; in_port_t svcport = 0; - while ((ch = getopt(argc, argv, "d:g:p:")) != (-1)) { + while ((ch = getopt(argc, argv, "d:g:h:p:")) != (-1)) { switch (ch) { case 'd': debug_level = atoi(optarg); @@ -118,12 +122,34 @@ main(argc, argv) /* NOTREACHED */ } 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: case '?': @@ -146,113 +172,68 @@ main(argc, 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 (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); - } - } - } - 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); + /* + * 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(); + + 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 { - transp = svc_tli_create(RPC_ANYFD, nconf, NULL, - RPC_MAXDATASIZE, RPC_MAXDATASIZE); + 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; + } } + hosts[nhosts - 1] = "127.0.0.1"; + } - if (transp == NULL) { - errx(1, "cannot create %s service.", transports[i]); - /* NOTREACHED */ - } - if (!svc_reg(transp, NLM_PROG, NLM_SM, nlm_prog_0, nconf)) { - errx(1, "unable to register (NLM_PROG, NLM_SM, %s)", - transports[i]); - /* NOTREACHED */ - } - if (!svc_reg(transp, NLM_PROG, NLM_VERS, nlm_prog_1, nconf)) { - errx(1, "unable to register (NLM_PROG, NLM_VERS, %s)", - transports[i]); - /* NOTREACHED */ - } - if (!svc_reg(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, nconf)) { - errx(1, "unable to register (NLM_PROG, NLM_VERSX, %s)", - transports[i]); - /* NOTREACHED */ - } - if (!svc_reg(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, nconf)) { - errx(1, "unable to register (NLM_PROG, NLM_VERS4, %s)", - transports[i]); - /* NOTREACHED */ + 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); + } } - freenetconfigent(nconf); } + endnetconfig(nc_handle); /* * Note that it is NOT sensible to run this program from inetd - the @@ -289,6 +270,235 @@ main(argc, 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_reg(transp, NLM_PROG, NLM_SM, nlm_prog_0, + NULL)) + syslog(LOG_ERR, + "can't register %s NLM_PROG, NLM_SM service", + nconf->nc_netid); + + if (!svc_reg(transp, NLM_PROG, NLM_VERS, nlm_prog_1, + NULL)) + syslog(LOG_ERR, + "can't register %s NLM_PROG, NLM_VERS service", + nconf->nc_netid); + + if (!svc_reg(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, + NULL)) + syslog(LOG_ERR, + "can't register %s NLM_PROG, NLM_VERSX service", + nconf->nc_netid); + + if (!svc_reg(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, + NULL)) + syslog(LOG_ERR, + "can't register %s NLM_PROG, NLM_VERS4 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(NLM_PROG, NLM_SM, nconf, &servaddr); + rpcb_set(NLM_PROG, NLM_VERS, nconf, &servaddr); + rpcb_set(NLM_PROG, NLM_VERSX, nconf, &servaddr); + rpcb_set(NLM_PROG, NLM_VERS4, nconf, &servaddr); + + xcreated++; + freeaddrinfo(res); + } + } /* end while */ +} + void sigalarm_handler(void) { @@ -300,7 +510,7 @@ void usage() { errx(1, "usage: rpc.lockd [-d ]" - " [-g ] [-p ]"); + " [-g ] [-h ] [-p ]"); } /* @@ -353,3 +563,12 @@ init_nsm(void) mon_host.mon_id.my_id.my_vers = NLM_SM; mon_host.mon_id.my_id.my_proc = NLM_SM_NOTIFY; /* bsdi addition */ } + +/* + * Out of memory, fatal + */ +void out_of_mem() +{ + syslog(LOG_ERR, "out of memory"); + exit(2); +} diff --git a/usr.sbin/rpc.lockd/rpc.lockd.8 b/usr.sbin/rpc.lockd/rpc.lockd.8 index 0b5d348..4fabe5d 100644 --- a/usr.sbin/rpc.lockd/rpc.lockd.8 +++ b/usr.sbin/rpc.lockd/rpc.lockd.8 @@ -33,7 +33,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 3, 2007 +.Dd November 2, 2007 .Dt RPC.LOCKD 8 .Os .Sh NAME @@ -43,6 +43,7 @@ .Nm .Op Fl d Ar debug_level .Op Fl g Ar grace period +.Op Fl h Ar bindip .Op Fl p Ar port .Sh DESCRIPTION The @@ -84,6 +85,23 @@ During the grace period only accepts requests from hosts which are reinitialising locks which existed before the server restart. Default is 30 seconds. +.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 -- cgit v1.1