From 15a913c297d12c0a4b14c01437420ac0df8b6675 Mon Sep 17 00:00:00 2001
From: "fredrik@DOLDA2000.COM"
 <fredrik@DOLDA2000.COM@959494ce-11ee-0310-bf91-de5d638817bd>
Date: Fri, 29 Apr 2005 02:59:24 +0000
Subject: [PATCH] Added config file. Added cache.

git-svn-id: svn+ssh://svn.dolda2000.com/srv/svn/repos/src/icmp-dn@220 959494ce-11ee-0310-bf91-de5d638817bd
---
 nss-icmp.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 257 insertions(+), 73 deletions(-)

diff --git a/nss-icmp.c b/nss-icmp.c
index 305573f..b9f1013 100644
--- a/nss-icmp.c
+++ b/nss-icmp.c
@@ -11,9 +11,147 @@
 #include <sys/types.h>
 #include <fcntl.h>
 
+#define CONFIGFILE "/etc/nss-icmp.conf"
+
+struct cache {
+    struct cache *next, *prev;
+    char *addr;
+    socklen_t addrlen;
+    int af;
+    int notfound;
+    char **names;
+};
+
+static int inited = 0;
+static int timeout = -1;
+static int usecache = 1;
+static struct cache *cache = NULL;
+
+static void readconfig(void)
+{
+    FILE *f;
+    char linebuf[1024];
+    char *p, *p2;
+    
+    if((f = fopen(CONFIGFILE, "r")) == NULL)
+	return;
+    
+    while(fgets(linebuf, sizeof(linebuf), f) != NULL) {
+	if(linebuf[0] == '#')
+	    continue;
+	if((p = strchr(linebuf, '\n')) != NULL)
+	    *p = 0;
+	if((p = strchr(linebuf, ' ')) != NULL) {
+	    p2 = p + 1;
+	    *p = 0;
+	}
+	if(!strcmp(linebuf, "timeout")) {
+	    if(p2 == NULL)
+		continue;
+	    timeout = atoi(p2);
+	}
+	if(!strcmp(linebuf, "nocache")) {
+	    usecache = 0;
+	}
+    }
+    
+    fclose(f);
+}
+
+static void freecache(struct cache *cc)
+{
+    int i;
+    
+    if(cc->next != NULL)
+	cc->next->prev = cc->prev;
+    if(cc->prev != NULL)
+	cc->prev->next = cc->next;
+    if(cc == cache)
+	cache = cc->next;
+    if(cc->addr != NULL)
+	free(cc->addr);
+    if(cc->names != NULL) {
+	for(i = 0; cc->names[i] != NULL; i++)
+	    free(cc->names[i]);
+	free(cc->names);
+    }
+    free(cc);
+}
+
+static void cachenotfound(const void *addr, socklen_t len, int af)
+{
+    struct cache *cc;
+    
+    for(cc = cache; cc != NULL; cc = cc->next) {
+	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(len)) == NULL) {
+	    freecache(cc);
+	    return;
+	}
+	memcpy(cc->addr, addr, len);
+	cc->addrlen = len;
+	cc->af = af;
+	
+	cc->notfound = 1;
+	
+	cc->next = cache;
+	if(cache != NULL)
+	    cache->prev = cc;
+	cache = cc;
+    }
+}
+
+static void updatecache(struct hostent *he)
+{
+    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))
+	    break;
+    }
+    if(cc == NULL) {
+	if((cc = malloc(sizeof(*cc))) == NULL)
+	    return;
+	memset(cc, 0, sizeof(*cc));
+	if((cc->addr = malloc(he->h_length)) == 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;
+	
+	for(i = 0; he->h_aliases[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) {
+		freecache(cc);
+		return;
+	    }
+	    strcpy(cc->names[i], he->h_aliases[i]);
+	}
+	
+	cc->next = cache;
+	if(cache != NULL)
+	    cache->prev = cc;
+	cache = cc;
+    }
+}
+
 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 ret;
+    int i, ret;
     struct retstruct {
 	char *aliaslist[16];
 	char *addrlist[2];
@@ -26,6 +164,12 @@ enum nss_status _nss_icmp_gethostbyaddr_r(const void *addr, socklen_t len, int a
     pid_t child;
     int pfd[2];
     int rl;
+    struct cache *cc;
+    
+    if(!inited) {
+	readconfig();
+	inited = 1;
+    }
     
     retbuf = (struct retstruct *)buffer;
     if((buflen < sizeof(*retbuf)) || (len > sizeof(retbuf->retaddr))) {
@@ -34,92 +178,129 @@ enum nss_status _nss_icmp_gethostbyaddr_r(const void *addr, socklen_t len, int a
 	return(NSS_STATUS_UNAVAIL);
     }
     
-    ap = (u_int8_t *)addr;
-    if(inet_ntop(af, addr, addrbuf, sizeof(addrbuf)) == NULL) {
-	*errnop = errno;
-	*h_errnop = NETDB_INTERNAL;
-	return(NSS_STATUS_UNAVAIL);
-    }
-    
-    if(pipe(pfd)) {
-	*errnop = errno;
-	*h_errnop = NETDB_INTERNAL;
-	return(NSS_STATUS_UNAVAIL);
-    }
-    /* I honestly don't know if it is considered OK to fork in other
-     * people's programs. We need a SUID worker, though, so there's
-     * little choice that I can see. */
-    if((child = fork()) < 0) {
-	*errnop = errno;
-	*h_errnop = NETDB_INTERNAL;
-	return(NSS_STATUS_UNAVAIL);
-    }
-    
-    if(child == 0) {
-	int i, fd;
-	
-	if((fd = open("/dev/null", O_WRONLY)) < 0)
-	    exit(127);
-	close(pfd[0]);
-	dup2(pfd[1], 1);
-	dup2(fd, 2);
-	for(i = 3; i < FD_SETSIZE; i++)
-	    close(i);
-	
-	execlp("idnlookup", "idnlookup", addrbuf, NULL);
-	exit(127);
+    for(cc = cache; cc != NULL; cc = cc->next) {
+	if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
+	    break;
     }
     
-    close(pfd[1]);
-    
-    rl = 0;
-    do {
-	ret = read(pfd[0], addrbuf + rl, sizeof(addrbuf) - rl);
-	if(ret < 0) {
+    if(cc == NULL) {
+	ap = (u_int8_t *)addr;
+	if(inet_ntop(af, addr, addrbuf, sizeof(addrbuf)) == NULL) {
 	    *errnop = errno;
 	    *h_errnop = NETDB_INTERNAL;
-	    close(pfd[0]);
 	    return(NSS_STATUS_UNAVAIL);
 	}
-	rl += ret;
-	if(rl >= sizeof(addrbuf) - 1) {
-	    *errnop = ENOMEM;
+    
+	if(pipe(pfd)) {
+	    *errnop = errno;
 	    *h_errnop = NETDB_INTERNAL;
-	    close(pfd[0]);
 	    return(NSS_STATUS_UNAVAIL);
 	}
-    } while(ret != 0);
-    addrbuf[rl] = 0;
-    close(pfd[0]);
-    
-    an = 0;
-    p = addrbuf;
-    p3 = buffer + sizeof(*retbuf);
-    while((p2 = strchr(p, '\n')) != NULL) {
-	*p2 = 0;
-	thislen = p2 - p;
-	if(thislen == 0)
-	    continue;
-	if((p3 - buffer) + thislen + 1 > buflen) {
-	    *errnop = ENOMEM;
+	/* I honestly don't know if it is considered OK to fork in other
+	 * people's programs. We need a SUID worker, though, so there's
+	 * little choice that I can see. */
+	if((child = fork()) < 0) {
+	    *errnop = errno;
 	    *h_errnop = NETDB_INTERNAL;
 	    return(NSS_STATUS_UNAVAIL);
 	}
-	memcpy(p3, p, thislen + 1);
-	retbuf->aliaslist[an] = p3;
-	p3 += thislen + 1;
-	p = p2 + 1;
-	if(++an == 16) {
-	    *errnop = ENOMEM;
-	    *h_errnop = NETDB_INTERNAL;
-	    return(NSS_STATUS_UNAVAIL);
+    
+	if(child == 0) {
+	    int i, fd;
+	    char timeoutbuf[128];
+	
+	    if((fd = open("/dev/null", O_WRONLY)) < 0)
+		exit(127);
+	    close(pfd[0]);
+	    dup2(pfd[1], 1);
+	    dup2(fd, 2);
+	    for(i = 3; i < FD_SETSIZE; i++)
+		close(i);
+	
+	    if(timeout != -1) {
+		snprintf(timeoutbuf, sizeof(timeoutbuf), "%i", timeout);
+		execlp("idnlookup", "idnlookup", "-t", timeoutbuf, addrbuf, NULL);
+	    } else {
+		execlp("idnlookup", "idnlookup", addrbuf, NULL);
+	    }
+	    exit(127);
+	}
+    
+	close(pfd[1]);
+    
+	rl = 0;
+	do {
+	    ret = read(pfd[0], addrbuf + rl, sizeof(addrbuf) - rl);
+	    if(ret < 0) {
+		*errnop = errno;
+		*h_errnop = NETDB_INTERNAL;
+		close(pfd[0]);
+		return(NSS_STATUS_UNAVAIL);
+	    }
+	    rl += ret;
+	    if(rl >= sizeof(addrbuf) - 1) {
+		*errnop = ENOMEM;
+		*h_errnop = NETDB_INTERNAL;
+		close(pfd[0]);
+		return(NSS_STATUS_UNAVAIL);
+	    }
+	} while(ret != 0);
+	addrbuf[rl] = 0;
+	close(pfd[0]);
+    
+	an = 0;
+	p = addrbuf;
+	p3 = buffer + sizeof(*retbuf);
+	while((p2 = strchr(p, '\n')) != NULL) {
+	    *p2 = 0;
+	    thislen = p2 - p;
+	    if(thislen == 0)
+		continue;
+	    if((p3 - buffer) + thislen + 1 > buflen) {
+		*errnop = ENOMEM;
+		*h_errnop = NETDB_INTERNAL;
+		return(NSS_STATUS_UNAVAIL);
+	    }
+	    memcpy(p3, p, thislen + 1);
+	    retbuf->aliaslist[an] = p3;
+	    p3 += thislen + 1;
+	    p = p2 + 1;
+	    if(++an == 16) {
+		*errnop = ENOMEM;
+		*h_errnop = NETDB_INTERNAL;
+		return(NSS_STATUS_UNAVAIL);
+	    }
+	}
+	if(an == 0) {
+	    cachenotfound(addr, len, af);
+	    *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
+	    return(NSS_STATUS_NOTFOUND);
+	}
+	retbuf->aliaslist[an] = NULL;
+    } else {
+	if(cc->notfound) {
+	    *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
+	    return(NSS_STATUS_NOTFOUND);
+	}
+	
+	p3 = buffer + sizeof(*retbuf);
+	for(i = 0; cc->names[i] != NULL; i++) {
+	    thislen = strlen(cc->names[i]);
+	    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;
+	    p3 += thislen + 1;
+	    if(++an == 16) {
+		*errnop = ENOMEM;
+		*h_errnop = NETDB_INTERNAL;
+		return(NSS_STATUS_UNAVAIL);
+	    }
 	}
     }
-    if(an == 0) {
-	*h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
-	return(NSS_STATUS_NOTFOUND);
-    }
-    retbuf->aliaslist[an] = NULL;
     
     memcpy(retbuf->retaddr, addr, len);
     retbuf->addrlist[0] = retbuf->retaddr;
@@ -130,6 +311,9 @@ 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;
     return(NSS_STATUS_SUCCESS);
 }
-- 
2.11.0