Blob Blame History Raw
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 <sys/file.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <netinet/in.h>
 #include <rpc/rpc.h>
 #include <rpc/rpc_com.h>
 #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;
 	}