X-Git-Url: http://git.dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Fnss-icmp.c;fp=src%2Fnss-icmp.c;h=5bc5ec7db9cbfe7dbf529da8200a04651b9a3289;hb=21c92d5d484277cf22f62ecc8e60db1608e062ad;hp=0000000000000000000000000000000000000000;hpb=bbb05b86699cdb941ecbd93e38654e4199d0a150;p=icmp-dn.git diff --git a/src/nss-icmp.c b/src/nss-icmp.c new file mode 100644 index 0000000..5bc5ec7 --- /dev/null +++ b/src/nss-icmp.c @@ -0,0 +1,400 @@ +/* + * nss-icmp or libnss_icmp - GNU C Library NSS module to query host + * names by ICMP. + * Copyright (C) 2005 Fredrik Tolf + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + char *addr; + socklen_t addrlen; + 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) +{ + 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, "ttlnotfound")) { + if(p2 == NULL) + continue; + nfttl = 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, time_t ttl) +{ + 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->at = time(NULL); + cc->ttl = ttl; + + cc->notfound = 1; + + cc->next = cache; + if(cache != NULL) + cache->prev = cc; + cache = cc; + } +} + +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 == 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->at = time(NULL); + cc->ttl = ttl; + + 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; names[i] != NULL; i++) { + if((cc->names[i] = malloc(strlen(names[i]) + 1)) == NULL) { + freecache(cc); + return; + } + strcpy(cc->names[i], names[i]); + } + + cc->next = cache; + if(cache != NULL) + cache->prev = cc; + cache = cc; + } +} + +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; + struct retstruct { + char *aliaslist[16]; + char *addrlist[2]; + char retaddr[16]; + } *retbuf; + char addrbuf[1024]; + 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) { + readconfig(); + inited = 1; + } + + retbuf = (struct retstruct *)buffer; + if((buflen < sizeof(*retbuf)) || (len > sizeof(retbuf->retaddr))) { + *errnop = ENOMEM; + *h_errnop = NETDB_INTERNAL; + return(NSS_STATUS_UNAVAIL); + } + + 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; + *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; + 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", "-Tt", timeoutbuf, addrbuf, NULL); + } else { + execlp("idnlookup", "idnlookup", "-T", 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]); + + 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; + 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) { + 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[i] = p3; + p3 += thislen + 1; + } + retbuf->aliaslist[i] = NULL; + } + + DEBUGP("returning hostent\n"); + memcpy(retbuf->retaddr, addr, len); + retbuf->addrlist[0] = retbuf->retaddr; + retbuf->addrlist[1] = NULL; + result->h_name = retbuf->aliaslist[0]; + result->h_aliases = retbuf->aliaslist; + result->h_addr_list = retbuf->addrlist; + result->h_addrtype = af; + result->h_length = len; + + *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: + */