Should have thought of this...
[icmp-dn.git] / nss-icmp.c
index b9f1013..5bc5ec7 100644 (file)
@@ -1,8 +1,30 @@
+/*
+ *  nss-icmp or libnss_icmp - GNU C Library NSS module to query host
+ *  names by ICMP.
+ *  Copyright (C) 2005 Fredrik Tolf <fredrik@dolda2000.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ *  MA 02111-1307, USA
+ */
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <time.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
 #include <nss.h>
 #include <sys/types.h>
 #include <fcntl.h>
+#include <sys/wait.h>
 
 #define CONFIGFILE "/etc/nss-icmp.conf"
+#if 0
+#define DEBUGP(format...) fprintf(stderr, "nss-icmp: " format);
+#else
+#define DEBUGP(format...)
+#endif
 
 struct cache {
     struct cache *next, *prev;
@@ -20,11 +48,13 @@ struct cache {
     int af;
     int notfound;
     char **names;
+    time_t at, ttl;
 };
 
 static int inited = 0;
 static int timeout = -1;
 static int usecache = 1;
+static time_t nfttl = 300;
 static struct cache *cache = NULL;
 
 static void readconfig(void)
@@ -50,6 +80,11 @@ static void readconfig(void)
                continue;
            timeout = atoi(p2);
        }
+       if(!strcmp(linebuf, "ttlnotfound")) {
+           if(p2 == NULL)
+               continue;
+           nfttl = atoi(p2);
+       }
        if(!strcmp(linebuf, "nocache")) {
            usecache = 0;
        }
@@ -78,7 +113,7 @@ static void freecache(struct cache *cc)
     free(cc);
 }
 
-static void cachenotfound(const void *addr, socklen_t len, int af)
+static void cachenotfound(const void *addr, socklen_t len, int af, time_t ttl)
 {
     struct cache *cc;
     
@@ -97,6 +132,8 @@ static void cachenotfound(const void *addr, socklen_t len, int af)
        memcpy(cc->addr, addr, len);
        cc->addrlen = len;
        cc->af = af;
+       cc->at = time(NULL);
+       cc->ttl = ttl;
        
        cc->notfound = 1;
        
@@ -107,39 +144,41 @@ static void cachenotfound(const void *addr, socklen_t len, int af)
     }
 }
 
-static void updatecache(struct hostent *he)
+static void updatecache(const void *addr, socklen_t len, int af, char **names, time_t ttl)
 {
     int i;
     struct cache *cc;
     
     for(cc = cache; cc != NULL; cc = cc->next) {
-       if((cc->af == he->h_addrtype) && (cc->addrlen == he->h_length) && !memcmp(cc->addr, he->h_addr_list[0], he->h_length))
+       if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
            break;
     }
     if(cc == NULL) {
        if((cc = malloc(sizeof(*cc))) == NULL)
            return;
        memset(cc, 0, sizeof(*cc));
-       if((cc->addr = malloc(he->h_length)) == NULL) {
+       if((cc->addr = malloc(len)) == NULL) {
            freecache(cc);
            return;
        }
-       memcpy(cc->addr, he->h_addr_list[0], he->h_length);
-       cc->addrlen = he->h_length;
-       cc->af = he->h_addrtype;
+       memcpy(cc->addr, addr, len);
+       cc->addrlen = len;
+       cc->af = af;
+       cc->at = time(NULL);
+       cc->ttl = ttl;
        
-       for(i = 0; he->h_aliases[i] != NULL; i++);
+       for(i = 0; names[i] != NULL; i++);
        if((cc->names = malloc(sizeof(*(cc->names)) * (i + 1))) == NULL) {
            freecache(cc);
            return;
        }
        memset(cc->names, 0, sizeof(*(cc->names)) * (i + 1));
-       for(i = 0; he->h_aliases[i] != NULL; i++) {
-           if((cc->names[i] = malloc(strlen(he->h_aliases[i]) + 1)) == NULL) {
+       for(i = 0; names[i] != NULL; i++) {
+           if((cc->names[i] = malloc(strlen(names[i]) + 1)) == NULL) {
                freecache(cc);
                return;
            }
-           strcpy(cc->names[i], he->h_aliases[i]);
+           strcpy(cc->names[i], names[i]);
        }
        
        cc->next = cache;
@@ -149,6 +188,21 @@ static void updatecache(struct hostent *he)
     }
 }
 
+static void expirecache(void)
+{
+    struct cache *cc, *next;
+    time_t now;
+    
+    now = time(NULL);
+    for(cc = cache; cc != NULL; cc = next) {
+       next = cc->next;
+       if(now - cc->at > cc->ttl) {
+           freecache(cc);
+           continue;
+       }
+    }
+}
+
 enum nss_status _nss_icmp_gethostbyaddr_r(const void *addr, socklen_t len, int af, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop)
 {
     int i, ret;
@@ -158,12 +212,13 @@ enum nss_status _nss_icmp_gethostbyaddr_r(const void *addr, socklen_t len, int a
        char retaddr[16];
     } *retbuf;
     char addrbuf[1024];
-    int an, thislen;
+    int an, thislen, ttl;
     char *p, *p2, *p3;
     u_int8_t *ap;
     pid_t child;
     int pfd[2];
     int rl;
+    int status;
     struct cache *cc;
     
     if(!inited) {
@@ -178,18 +233,27 @@ enum nss_status _nss_icmp_gethostbyaddr_r(const void *addr, socklen_t len, int a
        return(NSS_STATUS_UNAVAIL);
     }
     
-    for(cc = cache; cc != NULL; cc = cc->next) {
-       if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
-           break;
+    DEBUGP("starting lookup\n");
+    
+    if(usecache) {
+       expirecache();
+       for(cc = cache; cc != NULL; cc = cc->next) {
+           if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
+               break;
+       }
+    } else {
+       cc = NULL;
     }
     
     if(cc == NULL) {
+       DEBUGP("address not in cache, looking up for real\n");
        ap = (u_int8_t *)addr;
        if(inet_ntop(af, addr, addrbuf, sizeof(addrbuf)) == NULL) {
            *errnop = errno;
            *h_errnop = NETDB_INTERNAL;
            return(NSS_STATUS_UNAVAIL);
        }
+       DEBUGP("address is %s\n", addrbuf);
     
        if(pipe(pfd)) {
            *errnop = errno;
@@ -219,9 +283,9 @@ enum nss_status _nss_icmp_gethostbyaddr_r(const void *addr, socklen_t len, int a
        
            if(timeout != -1) {
                snprintf(timeoutbuf, sizeof(timeoutbuf), "%i", timeout);
-               execlp("idnlookup", "idnlookup", "-t", timeoutbuf, addrbuf, NULL);
+               execlp("idnlookup", "idnlookup", "-Tt", timeoutbuf, addrbuf, NULL);
            } else {
-               execlp("idnlookup", "idnlookup", addrbuf, NULL);
+               execlp("idnlookup", "idnlookup", "-T", addrbuf, NULL);
            }
            exit(127);
        }
@@ -247,9 +311,19 @@ enum nss_status _nss_icmp_gethostbyaddr_r(const void *addr, socklen_t len, int a
        } while(ret != 0);
        addrbuf[rl] = 0;
        close(pfd[0]);
-    
+       
+       waitpid(child, &status, 0);
+       
+       if((p = strchr(addrbuf, '\n')) == NULL) {
+           if(usecache)
+               cachenotfound(addr, len, af, nfttl);
+           *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
+           return(NSS_STATUS_NOTFOUND);
+       }
+       *(p++) = 0;
+       ttl = atoi(addrbuf);
+       
        an = 0;
-       p = addrbuf;
        p3 = buffer + sizeof(*retbuf);
        while((p2 = strchr(p, '\n')) != NULL) {
            *p2 = 0;
@@ -272,12 +346,17 @@ enum nss_status _nss_icmp_gethostbyaddr_r(const void *addr, socklen_t len, int a
            }
        }
        if(an == 0) {
-           cachenotfound(addr, len, af);
+           if(usecache)
+               cachenotfound(addr, len, af, nfttl);
            *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
            return(NSS_STATUS_NOTFOUND);
        }
        retbuf->aliaslist[an] = NULL;
+       
+       if(usecache)
+           updatecache(addr, len, af, retbuf->aliaslist, ttl);
     } else {
+       DEBUGP("address found in cache\n");
        if(cc->notfound) {
            *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
            return(NSS_STATUS_NOTFOUND);
@@ -286,22 +365,20 @@ enum nss_status _nss_icmp_gethostbyaddr_r(const void *addr, socklen_t len, int a
        p3 = buffer + sizeof(*retbuf);
        for(i = 0; cc->names[i] != NULL; i++) {
            thislen = strlen(cc->names[i]);
+           DEBUGP("filling in address %s, length %i\n", cc->names[i], thislen);
            if((p3 - buffer) + thislen + 1 > buflen) {
                *errnop = ENOMEM;
                *h_errnop = NETDB_INTERNAL;
                return(NSS_STATUS_UNAVAIL);
            }
            memcpy(p3, cc->names[i], thislen + 1);
-           retbuf->aliaslist[an] = p3;
+           retbuf->aliaslist[i] = p3;
            p3 += thislen + 1;
-           if(++an == 16) {
-               *errnop = ENOMEM;
-               *h_errnop = NETDB_INTERNAL;
-               return(NSS_STATUS_UNAVAIL);
-           }
        }
+       retbuf->aliaslist[i] = NULL;
     }
     
+    DEBUGP("returning hostent\n");
     memcpy(retbuf->retaddr, addr, len);
     retbuf->addrlist[0] = retbuf->retaddr;
     retbuf->addrlist[1] = NULL;
@@ -311,9 +388,13 @@ enum nss_status _nss_icmp_gethostbyaddr_r(const void *addr, socklen_t len, int a
     result->h_addrtype = af;
     result->h_length = len;
     
-    if(cc == NULL)
-       updatecache(result);
-    
     *h_errnop = NETDB_SUCCESS;
+    DEBUGP("returning\n");
     return(NSS_STATUS_SUCCESS);
 }
+
+/*
+ * Local Variables:
+ * compile-command: "gcc -shared -Wall -g -o libnss_icmp.so.2 nss-icmp.c"
+ * End:
+ */