]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/statd/sm-notify.c
Be more cautious about use for privilege ports (<1024).
[nfs-utils.git] / utils / statd / sm-notify.c
index cdadcd7509f223fbe9b3fff5cf7d79e95eedd65a..1059a888949cd21984837de1af31683fba90695f 100644 (file)
@@ -56,6 +56,7 @@ struct nsm_host {
        char *                  name;
        char *                  path;
        nsm_address             addr;
+       struct addrinfo         *ai;
        time_t                  last_used;
        time_t                  send_next;
        unsigned int            timeout;
@@ -83,10 +84,11 @@ static void         insert_host(struct nsm_host *);
 struct nsm_host *      find_host(uint32_t);
 static int             addr_get_port(nsm_address *);
 static void            addr_set_port(nsm_address *, int);
-static int             host_lookup(int, const char *, nsm_address *);
+static struct addrinfo *host_lookup(int, const char *);
 void                   nsm_log(int fac, const char *fmt, ...);
 static int             record_pid();
 static void            drop_privs(void);
+static void set_kernel_nsm_state(int state);
 
 static struct nsm_host *       hosts = NULL;
 
@@ -149,7 +151,7 @@ usage:              fprintf(stderr,
        }
 
        if (strcmp(_SM_BASE_PATH, BASEDIR) == 0) {
-               if (record_pid() == 0 && force == 0 && opt_update_state == 0)
+               if (record_pid() == 0 && force == 0 && opt_update_state == 1)
                        /* already run, don't try again */
                        exit(0);
        }
@@ -165,6 +167,10 @@ usage:             fprintf(stderr,
        backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH);
        get_hosts(_SM_BAK_PATH);
 
+       /* Get and update the NSM state. This will call sync() */
+       nsm_state = nsm_get_state(opt_update_state);
+       set_kernel_nsm_state(nsm_state);
+
        if (!opt_debug) {
                if (!opt_quiet)
                        printf("Backgrounding to notify hosts...\n");
@@ -183,9 +189,6 @@ usage:              fprintf(stderr,
                close(2);
        }
 
-       /* Get and update the NSM state. This will call sync() */
-       nsm_state = nsm_get_state(opt_update_state);
-
        notify();
 
        if (hosts) {
@@ -212,7 +215,9 @@ notify(void)
        nsm_address local_addr;
        time_t  failtime = 0;
        int     sock = -1;
+       int retry_cnt = 0;
 
+ retry:
        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock < 0) {
                perror("socket");
@@ -225,12 +230,14 @@ notify(void)
 
        /* Bind source IP if provided on command line */
        if (opt_srcaddr) {
-               if (!host_lookup(AF_INET, opt_srcaddr, &local_addr)) {
+               struct addrinfo *ai = host_lookup(AF_INET, opt_srcaddr);
+               if (!ai) {
                        nsm_log(LOG_WARNING,
                                "Not a valid hostname or address: \"%s\"\n",
                                opt_srcaddr);
                        exit(1);
                }
+               memcpy(&local_addr, ai->ai_addr, ai->ai_addrlen);
                /* We know it's IPv4 at this point */
        }
 
@@ -243,7 +250,15 @@ notify(void)
                        exit(1);
                }
        } else {
+               struct servent *se;
                (void) bindresvport(sock, (struct sockaddr_in *) &local_addr);
+               /* try to avoid known ports */
+               se = getservbyport(local_addr.sin_port, "udp");
+               if (se && retry_cnt < 100) {
+                       retry_cnt++;
+                       close(sock);
+                       goto retry;
+               }
        }
 
        if (opt_max_retry)
@@ -322,9 +337,19 @@ notify_host(int sock, struct nsm_host *host)
        *p++ = htonl(2);
 
        /* If we retransmitted 4 times, reset the port to force
-        * a new portmap lookup (in case statd was restarted)
+        * a new portmap lookup (in case statd was restarted).
+        * We also rotate through multiple IP addresses at this
+        * point.
         */
        if (host->retries >= 4) {
+               struct addrinfo *hold = host->ai;
+               struct addrinfo **next = &host->ai;
+               *next = hold->ai_next;
+               while ( *next )
+                       next = & (*next)->ai_next;
+               *next = hold;
+               hold->ai_next = NULL;
+               memcpy(&host->addr, hold->ai_addr, hold->ai_addrlen);
                addr_set_port(&host->addr, 0);
                host->retries = 0;
        }
@@ -437,6 +462,7 @@ recv_reply(int sock)
                        free(hp->name);
                        free(hp->path);
                        free(hp);
+                       freeaddrinfo(hp->ai);
                        return;
                }
        }
@@ -502,7 +528,11 @@ get_hosts(const char *dirname)
                        host = calloc(1, sizeof(*host));
 
                snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name);
-               if (!host_lookup(AF_UNSPEC, de->d_name, &host->addr)) {
+               if (stat(path, &stb) < 0)
+                       continue;
+
+               host->ai = host_lookup(AF_UNSPEC, de->d_name);
+               if (! host->ai) {
                        nsm_log(LOG_WARNING,
                                "%s doesn't seem to be a valid address, skipped",
                                de->d_name);
@@ -510,12 +540,11 @@ get_hosts(const char *dirname)
                        continue;
                }
 
-               if (stat(path, &stb) < 0)
-                       continue;
                host->last_used = stb.st_mtime;
                host->timeout = NSM_TIMEOUT;
                host->path = strdup(path);
                host->name = strdup(de->d_name);
+               host->retries = 100; /* force address retry */
 
                insert_host(host);
                host = NULL;
@@ -658,25 +687,19 @@ addr_set_port(nsm_address *addr, int port)
        }
 }
 
-static int
-host_lookup(int af, const char *name, nsm_address *addr)
+static struct addrinfo *
+host_lookup(int af, const char *name)
 {
        struct addrinfo hints, *ai;
-       int okay = 0;
 
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = af;
+       hints.ai_protocol = IPPROTO_UDP;
 
        if (getaddrinfo(name, NULL, &hints, &ai) != 0)
-               return 0;
-
-       if (ai->ai_addrlen < sizeof(*addr)) {
-               memcpy(addr, ai->ai_addr, ai->ai_addrlen);
-               okay = 1;
-       }
+               return NULL;
 
-       freeaddrinfo(ai);
-       return okay;
+       return ai;
 }
 
 /*
@@ -713,7 +736,7 @@ static int record_pid()
 
        snprintf(pid, 20, "%d\n", getpid());
        fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
-       if (!fd)
+       if (fd < 0)
                return 0;
        write(fd, pid, strlen(pid));
        close(fd);
@@ -747,3 +770,16 @@ static void drop_privs(void)
                exit(1);
        }
 }
+
+static void set_kernel_nsm_state(int state)
+{
+       int fd;
+
+       fd = open("/proc/sys/fs/nfs/nsm_local_state",O_WRONLY);
+       if (fd >= 0) {
+               char buf[20];
+               snprintf(buf, sizeof(buf), "%d", state);
+               write(fd, buf, strlen(buf));
+               close(fd);
+       }
+}