2 * nss-icmp or libnss_icmp - GNU C Library NSS module to query host
4 * Copyright (C) 2005 Fredrik Tolf <fredrik@dolda2000.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
28 #include <sys/socket.h>
29 #include <netinet/in.h>
31 #include <arpa/inet.h>
33 #include <sys/types.h>
39 #define CONFIGFILE "/etc/nss-icmp.conf"
43 #define DEBUGP(format...) fprintf(stderr, "nss-icmp: " format);
45 #define DEBUGP(format...)
49 struct cache *next, *prev;
58 static int inited = 0;
59 static int timeout = -1;
60 static int usecache = 1;
61 static time_t nfttl = 300;
62 static struct cache *cache = NULL;
64 static void readconfig(void)
70 if((f = fopen(CONFIGFILE, "r")) == NULL)
73 while(fgets(linebuf, sizeof(linebuf), f) != NULL) {
76 if((p = strchr(linebuf, '\n')) != NULL)
78 if((p = strchr(linebuf, ' ')) != NULL) {
82 if(!strcmp(linebuf, "timeout")) {
87 if(!strcmp(linebuf, "ttlnotfound")) {
92 if(!strcmp(linebuf, "nocache")) {
100 static void freecache(struct cache *cc)
105 cc->next->prev = cc->prev;
107 cc->prev->next = cc->next;
112 if(cc->names != NULL) {
113 for(i = 0; cc->names[i] != NULL; i++)
120 static void cachenotfound(const void *addr, socklen_t len, int af, time_t ttl)
124 for(cc = cache; cc != NULL; cc = cc->next) {
125 if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
129 if((cc = malloc(sizeof(*cc))) == NULL)
131 memset(cc, 0, sizeof(*cc));
132 if((cc->addr = malloc(len)) == NULL) {
136 memcpy(cc->addr, addr, len);
151 static void updatecache(const void *addr, socklen_t len, int af, char **names, time_t ttl)
156 for(cc = cache; cc != NULL; cc = cc->next) {
157 if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
161 if((cc = malloc(sizeof(*cc))) == NULL)
163 memset(cc, 0, sizeof(*cc));
164 if((cc->addr = malloc(len)) == NULL) {
168 memcpy(cc->addr, addr, len);
174 for(i = 0; names[i] != NULL; i++);
175 if((cc->names = malloc(sizeof(*(cc->names)) * (i + 1))) == NULL) {
179 memset(cc->names, 0, sizeof(*(cc->names)) * (i + 1));
180 for(i = 0; names[i] != NULL; i++) {
181 if((cc->names[i] = malloc(strlen(names[i]) + 1)) == NULL) {
185 strcpy(cc->names[i], names[i]);
195 static void expirecache(void)
197 struct cache *cc, *next;
201 for(cc = cache; cc != NULL; cc = next) {
203 if(now - cc->at > cc->ttl) {
210 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)
219 int an, thislen, ttl;
233 retbuf = (struct retstruct *)buffer;
234 if((buflen < sizeof(*retbuf)) || (len > sizeof(retbuf->retaddr))) {
236 *h_errnop = NETDB_INTERNAL;
237 return(NSS_STATUS_UNAVAIL);
240 DEBUGP("starting lookup\n");
244 for(cc = cache; cc != NULL; cc = cc->next) {
245 if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
253 DEBUGP("address not in cache, looking up for real\n");
254 ap = (u_int8_t *)addr;
255 if(inet_ntop(af, addr, addrbuf, sizeof(addrbuf)) == NULL) {
257 *h_errnop = NETDB_INTERNAL;
258 return(NSS_STATUS_UNAVAIL);
260 DEBUGP("address is %s\n", addrbuf);
264 *h_errnop = NETDB_INTERNAL;
265 return(NSS_STATUS_UNAVAIL);
267 /* I honestly don't know if it is considered OK to fork in other
268 * people's programs. We need a SUID worker, though, so there's
269 * little choice that I can see. */
270 if((child = fork()) < 0) {
272 *h_errnop = NETDB_INTERNAL;
273 return(NSS_STATUS_UNAVAIL);
278 char timeoutbuf[128];
280 if((fd = open("/dev/null", O_WRONLY)) < 0)
285 for(i = 3; i < FD_SETSIZE; i++)
289 snprintf(timeoutbuf, sizeof(timeoutbuf), "%i", timeout);
290 execlp("idnlookup", "idnlookup", "-Tt", timeoutbuf, addrbuf, NULL);
292 execlp("idnlookup", "idnlookup", "-T", addrbuf, NULL);
301 ret = read(pfd[0], addrbuf + rl, sizeof(addrbuf) - rl);
304 *h_errnop = NETDB_INTERNAL;
306 return(NSS_STATUS_UNAVAIL);
309 if(rl >= sizeof(addrbuf) - 1) {
311 *h_errnop = NETDB_INTERNAL;
313 return(NSS_STATUS_UNAVAIL);
319 waitpid(child, &status, 0);
321 if((p = strchr(addrbuf, '\n')) == NULL) {
323 cachenotfound(addr, len, af, nfttl);
324 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
325 return(NSS_STATUS_NOTFOUND);
331 p3 = buffer + sizeof(*retbuf);
332 while((p2 = strchr(p, '\n')) != NULL) {
337 if((p3 - buffer) + thislen + 1 > buflen) {
339 *h_errnop = NETDB_INTERNAL;
340 return(NSS_STATUS_UNAVAIL);
342 memcpy(p3, p, thislen + 1);
343 retbuf->aliaslist[an] = p3;
348 *h_errnop = NETDB_INTERNAL;
349 return(NSS_STATUS_UNAVAIL);
354 cachenotfound(addr, len, af, nfttl);
355 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
356 return(NSS_STATUS_NOTFOUND);
358 retbuf->aliaslist[an] = NULL;
361 updatecache(addr, len, af, retbuf->aliaslist, ttl);
363 DEBUGP("address found in cache\n");
365 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
366 return(NSS_STATUS_NOTFOUND);
369 p3 = buffer + sizeof(*retbuf);
370 for(i = 0; cc->names[i] != NULL; i++) {
371 thislen = strlen(cc->names[i]);
372 DEBUGP("filling in address %s, length %i\n", cc->names[i], thislen);
373 if((p3 - buffer) + thislen + 1 > buflen) {
375 *h_errnop = NETDB_INTERNAL;
376 return(NSS_STATUS_UNAVAIL);
378 memcpy(p3, cc->names[i], thislen + 1);
379 retbuf->aliaslist[i] = p3;
382 retbuf->aliaslist[i] = NULL;
385 DEBUGP("returning hostent\n");
386 memcpy(retbuf->retaddr, addr, len);
387 retbuf->addrlist[0] = retbuf->retaddr;
388 retbuf->addrlist[1] = NULL;
389 result->h_name = retbuf->aliaslist[0];
390 result->h_aliases = retbuf->aliaslist;
391 result->h_addr_list = retbuf->addrlist;
392 result->h_addrtype = af;
393 result->h_length = len;
395 *h_errnop = NETDB_SUCCESS;
396 DEBUGP("returning\n");
397 return(NSS_STATUS_SUCCESS);
402 * compile-command: "gcc -shared -Wall -g -o libnss_icmp.so.2 nss-icmp.c"