#include <unistd.h>
#include <string.h>
#include <errno.h>
+#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.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;
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)
continue;
timeout = atoi(p2);
}
+ if(!strcmp(linebuf, "ttlnotfound")) {
+ if(p2 == NULL)
+ continue;
+ nfttl = atoi(p2);
+ }
if(!strcmp(linebuf, "nocache")) {
usecache = 0;
}
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;
memcpy(cc->addr, addr, len);
cc->addrlen = len;
cc->af = af;
+ cc->at = time(NULL);
+ cc->ttl = ttl;
cc->notfound = 1;
}
}
-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;
}
}
+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;
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;
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;
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);
}
} while(ret != 0);
addrbuf[rl] = 0;
close(pfd[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;
}
}
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);
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;
result->h_addrtype = af;
result->h_length = len;
- if(cc == NULL)
- updatecache(result);
-
*h_errnop = NETDB_SUCCESS;
+ DEBUGP("returning\n");
return(NSS_STATUS_SUCCESS);
}