diff --git a/Makefile.am b/Makefile.am index c99566d..ea5725f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,7 +13,7 @@ AM_CPPFLAGS = \ $(TIRPC_CFLAGS) if DEBUG -AM_CPPFLAGS += -DRPCBIND_DEBUG -DSVC_RUN_DEBUG -DDEBUG_RMTCALL +AM_CPPFLAGS += -DRPCBIND_DEBUG -DDEBUG_RMTCALL AM_CPPFLAGS += -DND_DEBUG -DBIND_DEBUG endif diff --git a/configure.ac b/configure.ac index 75e7e71..27496c7 100644 --- a/configure.ac +++ b/configure.ac @@ -55,4 +55,6 @@ AS_IF([test x$enable_libwrap = xyes], [ AC_SEARCH_LIBS([pthread_create], [pthread]) +AC_CHECK_HEADERS(nss.h) + AC_OUTPUT([Makefile]) diff --git a/src/rpcbind.c b/src/rpcbind.c index f7c71ee..35c45f5 100644 --- a/src/rpcbind.c +++ b/src/rpcbind.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #ifdef PORTMAP @@ -268,6 +269,13 @@ main(int argc, char *argv[]) network_init(); +#ifdef SYSTEMD + /* Try to notify system of successful startup, regardless of whether we + * used systemd socket activation or not. When started from the command + * line, this should not hurt either. + */ + sd_notify(0, "READY=1"); +#endif my_svc_run(); syslog(LOG_ERR, "svc_run returned unexpectedly"); rpcbind_abort(); @@ -277,6 +285,31 @@ main(int argc, char *argv[]) } /* + * Normally systemd will open sockets in dual ipv4/ipv6 mode. + * That won't work with netconfig and we'll only match + * the ipv6 socket. Convert it to IPV6_V6ONLY and issue + * a warning for the user to fix their systemd config. + */ +static int +handle_ipv6_socket(int fd) +{ + int opt; + socklen_t len = sizeof(opt); + + if (getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, &len)) { + syslog(LOG_ERR, "failed to get ipv6 socket opts: %m"); + return -1; + } + + if (opt) /* socket is already in V6ONLY mode */ + return 0; + + syslog(LOG_ERR, "systemd has passed an IPv4/IPv6 dual-mode socket."); + syslog(LOG_ERR, "Please fix your systemd config by specifying IPv4 and IPv6 sockets separately and using BindIPv6Only=ipv6-only."); + return -1; +} + +/* * Adds the entry into the rpcbind database. * If PORTMAP, then for UDP and TCP, it adds the entries for version 2 also * Returns 0 if succeeds, else fails @@ -361,6 +394,9 @@ init_transport(struct netconfig *nconf) goto error; } + if (sa.sa.sa_family == AF_INET6 && handle_ipv6_socket(fd)) + goto error; + /* Copy the address */ taddr.addr.maxlen = taddr.addr.len = addrlen; taddr.addr.buf = malloc(addrlen); @@ -389,18 +425,6 @@ init_transport(struct netconfig *nconf) if (my_xprt != NULL) goto got_socket; - /* - * XXX - using RPC library internal functions. For NC_TPI_CLTS - * we call this later, for each socket we like to bind. - */ - if (nconf->nc_semantics != NC_TPI_CLTS) { - if ((fd = __rpc_nconf2fd(nconf)) < 0) { - syslog(LOG_ERR, "cannot create socket for %s", - nconf->nc_netid); - return (1); - } - } - if ((strcmp(nconf->nc_netid, "local") == 0) || (strcmp(nconf->nc_netid, "unix") == 0)) { memset(&sun, 0, sizeof sun); @@ -451,11 +475,13 @@ init_transport(struct netconfig *nconf) nconf->nc_netid); return (1); } + + hints.ai_flags &= ~AI_NUMERICHOST; switch (hints.ai_family) { case AF_INET: if (inet_pton(AF_INET, hosts[nhostsbak], host_addr) == 1) { - hints.ai_flags &= AI_NUMERICHOST; + hints.ai_flags |= AI_NUMERICHOST; } else { /* * Skip if we have an AF_INET6 adress. @@ -468,7 +494,7 @@ init_transport(struct netconfig *nconf) case AF_INET6: if (inet_pton(AF_INET6, hosts[nhostsbak], host_addr) == 1) { - hints.ai_flags &= AI_NUMERICHOST; + hints.ai_flags |= AI_NUMERICHOST; } else { /* * Skip if we have an AF_INET adress. @@ -561,6 +587,12 @@ init_transport(struct netconfig *nconf) if (!checkbind) return 1; } else { /* NC_TPI_COTS */ + if ((fd = __rpc_nconf2fd(nconf)) < 0) { + syslog(LOG_ERR, "cannot create socket for %s", + nconf->nc_netid); + return (1); + } + if ((strcmp(nconf->nc_netid, "local") != 0) && (strcmp(nconf->nc_netid, "unix") != 0)) { if ((aicode = getaddrinfo(NULL, servname, &hints, &res))!= 0) { diff --git a/src/rpcinfo.c b/src/rpcinfo.c index 747eba3..9b46864 100644 --- a/src/rpcinfo.c +++ b/src/rpcinfo.c @@ -115,10 +115,8 @@ struct rpcbdump_short #ifdef PORTMAP static void ip_ping (u_short, char *, int, char **); -static CLIENT *clnt_com_create (struct sockaddr_in *, u_long, u_long, int *, - char *); static void pmapdump (int, char **); -static void get_inet_address (struct sockaddr_in *, char *); +static CLIENT *ip_getclient(const char *hostname, rpcprog_t prognum, rpcvers_t versnum, const char *proto); #endif static bool_t reply_proc (void *, struct netbuf *, struct netconfig *); @@ -356,38 +354,17 @@ local_rpcb (rpcprog_t prog, rpcvers_t vers) } #ifdef PORTMAP -static CLIENT * -clnt_com_create (addr, prog, vers, fdp, trans) - struct sockaddr_in *addr; - u_long prog; - u_long vers; - int *fdp; - char *trans; +static enum clnt_stat +ip_ping_one(client, vers) + CLIENT *client; + u_int32_t vers; { - CLIENT *clnt; - - if (strcmp (trans, "tcp") == 0) - { - clnt = clnttcp_create (addr, prog, vers, fdp, 0, 0); - } - else - { - struct timeval to; + struct timeval to = { .tv_sec = 10, .tv_usec = 0 }; - to.tv_sec = 5; - to.tv_usec = 0; - clnt = clntudp_create (addr, prog, vers, to, fdp); - } - if (clnt == (CLIENT *) NULL) - { - clnt_pcreateerror ("rpcinfo"); - if (vers == MIN_VERS) - printf ("program %lu is not available\n", prog); - else - printf ("program %lu version %lu is not available\n", prog, vers); - exit (1); - } - return (clnt); + (void) CLNT_CONTROL (client, CLSET_VERS, &vers); + return CLNT_CALL (client, NULLPROC, (xdrproc_t) xdr_void, + (char *) NULL, (xdrproc_t) xdr_void, (char *) NULL, + to); } /* @@ -398,17 +375,15 @@ clnt_com_create (addr, prog, vers, fdp, trans) * version 0 calls succeeds, it tries for MAXVERS call and repeats the same. */ static void -ip_ping (portnum, trans, argc, argv) +ip_ping (portnum, proto, argc, argv) u_short portnum; - char *trans; + char *proto; int argc; char **argv; { CLIENT *client; - int fd = RPC_ANYFD; - struct timeval to; - struct sockaddr_in addr; enum clnt_stat rpc_stat; + const char *hostname; u_long prognum, vers, minvers, maxvers; struct rpc_err rpcerr; int failure = 0; @@ -418,10 +393,9 @@ ip_ping (portnum, trans, argc, argv) usage (); exit (1); } - to.tv_sec = 10; - to.tv_usec = 0; + + hostname = argv[0]; prognum = getprognum (argv[1]); - get_inet_address (&addr, argv[0]); if (argc == 2) { /* Version number not known */ /* @@ -434,11 +408,10 @@ ip_ping (portnum, trans, argc, argv) { vers = getvers (argv[2]); } - addr.sin_port = htons (portnum); - client = clnt_com_create (&addr, prognum, vers, &fd, trans); - rpc_stat = CLNT_CALL (client, NULLPROC, (xdrproc_t) xdr_void, - (char *) NULL, (xdrproc_t) xdr_void, (char *) NULL, - to); + + client = ip_getclient(hostname, prognum, vers, proto); + + rpc_stat = ip_ping_one(client, vers); if (argc != 2) { /* Version number was known */ @@ -447,8 +420,8 @@ ip_ping (portnum, trans, argc, argv) (void) CLNT_DESTROY (client); return; } + /* Version number not known */ - (void) CLNT_CONTROL (client, CLSET_FD_NCLOSE, (char *) NULL); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr (client, &rpcerr); @@ -461,12 +434,7 @@ ip_ping (portnum, trans, argc, argv) * Oh dear, it DOES support version 0. * Let's try version MAX_VERS. */ - (void) CLNT_DESTROY (client); - addr.sin_port = htons (portnum); - client = clnt_com_create (&addr, prognum, MAX_VERS, &fd, trans); - rpc_stat = CLNT_CALL (client, NULLPROC, (xdrproc_t) xdr_void, - (char *) NULL, (xdrproc_t) xdr_void, - (char *) NULL, to); + rpc_stat = ip_ping_one(client, MAX_VERS); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr (client, &rpcerr); @@ -495,21 +463,15 @@ ip_ping (portnum, trans, argc, argv) (void) pstatus (client, prognum, (u_long) 0); exit (1); } - (void) CLNT_DESTROY (client); for (vers = minvers; vers <= maxvers; vers++) { - addr.sin_port = htons (portnum); - client = clnt_com_create (&addr, prognum, vers, &fd, trans); - rpc_stat = CLNT_CALL (client, NULLPROC, (xdrproc_t) xdr_void, - (char *) NULL, (xdrproc_t) xdr_void, - (char *) NULL, to); + rpc_stat = ip_ping_one(client, vers); if (pstatus (client, prognum, vers) < 0) failure = 1; - (void) CLNT_DESTROY (client); } if (failure) exit (1); - (void) close (fd); + (void) CLNT_DESTROY (client); return; } @@ -521,9 +483,7 @@ pmapdump (argc, argv) int argc; char **argv; { - struct sockaddr_in server_addr; struct pmaplist *head = NULL; - int socket = RPC_ANYSOCK; struct timeval minutetimeout; register CLIENT *client; struct rpcent *rpc; @@ -539,10 +499,13 @@ pmapdump (argc, argv) if (argc == 1) { host = argv[0]; - get_inet_address (&server_addr, host); - server_addr.sin_port = htons (PMAPPORT); - client = clnttcp_create (&server_addr, PMAPPROG, PMAPVERS, - &socket, 50, 500); + + /* This is a little bit more complicated than it should be. + * ip_getclient will do an rpcb_getaddr call to identify the + * port of the portmapper - but it works, and it's easier than + * creating a copy of ip_getclient that avoids the getaddr call. + */ + client = ip_getclient(host, PMAPPROG, PMAPVERS, "tcp"); } else client = local_rpcb (PMAPPROG, PMAPVERS); @@ -609,48 +572,74 @@ pmapdump (argc, argv) } } -static void -get_inet_address (addr, host) - struct sockaddr_in *addr; - char *host; +/* + * Try to obtain the address of a given host/program/version, using the + * specified protocol (one of udp or tcp). + * This loops over all netconfig entries (according to the order given by + * netpath and the config file), and tries to resolve the hostname, and obtain + * the address using rpcb_getaddr. + */ +CLIENT * +ip_getclient(hostname, prognum, versnum, proto) + const char *hostname; + rpcprog_t prognum; + rpcvers_t versnum; + const char *proto; { - struct netconfig *nconf; - struct addrinfo hints, *res; - int error; + void *handle; + enum clnt_stat saved_stat = RPC_SUCCESS; + struct netconfig *nconf, *result = NULL; + struct netbuf bind_address; + struct sockaddr_storage __sa; + CLIENT *client; + + memset(&bind_address, 0, sizeof(bind_address)); + bind_address.maxlen = sizeof(__sa); + bind_address.buf = &__sa; - (void) memset ((char *) addr, 0, sizeof (*addr)); - addr->sin_addr.s_addr = inet_addr (host); - if (addr->sin_addr.s_addr == -1 || addr->sin_addr.s_addr == 0) + handle = setnetconfig(); + while ((nconf = getnetconfig(handle)) != NULL) { - if ((nconf = __rpc_getconfip ("udp")) == NULL && - (nconf = __rpc_getconfip ("tcp")) == NULL) - { - fprintf (stderr, "rpcinfo: couldn't find a suitable transport\n"); - exit (1); - } + if (!strcmp(nconf->nc_proto, proto)) { + if (rpcb_getaddr(prognum, versnum, nconf, &bind_address, hostname)) + { + result = getnetconfigent(nconf->nc_netid); + endnetconfig(handle); + break; + } + + if (rpc_createerr.cf_stat != RPC_UNKNOWNHOST) + { + clnt_pcreateerror (hostname); + exit (1); + } + + saved_stat = rpc_createerr.cf_stat; + } + } + + if (result == NULL) + { + if (saved_stat != RPC_SUCCESS) + { + rpc_createerr.cf_stat = saved_stat; + clnt_pcreateerror (hostname); + } else - { - memset (&hints, 0, sizeof hints); - hints.ai_family = AF_INET; - if ((error = getaddrinfo (host, "rpcbind", &hints, &res)) != 0 && - (error = getaddrinfo (host, "portmapper", &hints, &res)) != 0) - { - fprintf (stderr, "rpcinfo: %s: %s\n", - host, gai_strerror (error)); - exit (1); - } - else - { - memcpy (addr, res->ai_addr, res->ai_addrlen); - freeaddrinfo (res); - } - (void) freenetconfigent (nconf); - } + fprintf (stderr, "Cannot find suitable transport for protocol %s\n", proto); + + exit (1); } - else + + client = clnt_tli_create(RPC_ANYFD, result, &bind_address, prognum, versnum, 0, 0); + if (client == NULL) { - addr->sin_family = AF_INET; + clnt_pcreateerror(hostname); + exit (1); } + + freenetconfigent(result); + return client; } #endif /* PORTMAP */ diff --git a/src/util.c b/src/util.c index 7d56479..a6c835b 100644 --- a/src/util.c +++ b/src/util.c @@ -71,9 +71,6 @@ static struct sockaddr_in6 *local_in6; #endif static int bitmaskcmp __P((void *, void *, void *, int)); -#ifdef INET6 -static void in6_fillscopeid __P((struct sockaddr_in6 *)); -#endif /* * For all bits set in "mask", compare the corresponding bits in @@ -93,28 +90,6 @@ bitmaskcmp(void *dst, void *src, void *mask, int bytelen) } /* - * Similar to code in ifconfig.c. Fill in the scope ID for link-local - * addresses returned by getifaddrs(). - */ -#ifdef INET6 -static void -in6_fillscopeid(struct sockaddr_in6 *sin6) -{ - u_int16_t ifindex; - u_int16_t *addr; - - if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { - addr = (u_int16_t *)&sin6->sin6_addr.s6_addr[2]; - ifindex = ntohs(*addr); - if (sin6->sin6_scope_id == 0 && ifindex != 0) { - sin6->sin6_scope_id = ifindex; - *addr = 0; - } - } -} -#endif - -/* * Find a server address that can be used by `caller' to contact * the local service specified by `serv_uaddr'. If `clnt_uaddr' is * non-NULL, it is used instead of `caller' as a hint suggesting @@ -211,7 +186,6 @@ addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr, * a link-local address then use the scope id to see * which one. */ - in6_fillscopeid(SA2SIN6(ifsa)); if (IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(ifsa)) && IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(caller_sa)) && IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(hint_sa))) { diff --git a/src/warmstart.c b/src/warmstart.c index d1bb971..b6eb73e 100644 --- a/src/warmstart.c +++ b/src/warmstart.c @@ -101,14 +101,15 @@ read_struct(char *filename, xdrproc_t structproc, void *list) { FILE *fp; XDR xdrs; - + if (debugging) fprintf(stderr, "rpcbind: using '%s' startup file\n", filename); if ((fp = fopen(filename, "r")) == NULL) { - syslog(LOG_ERR, - "Cannot open '%s' file for reading, errno %d (%s)", - filename, errno, strerror(errno)); + if (errno != ENOENT) + syslog(LOG_ERR, + "Cannot open '%s' file for reading, errno %d (%s)", + filename, errno, strerror(errno)); goto error; }