7 #include <sys/socket.h>
8 #include <netinet/in.h>
10 #include <arpa/inet.h>
12 #include <sys/types.h>
15 #define CONFIGFILE "/etc/nss-icmp.conf"
17 #define DEBUGP(format...) fprintf(stderr, "nss-icmp: " format);
19 #define DEBUGP(format...)
23 struct cache *next, *prev;
32 static int inited = 0;
33 static int timeout = -1;
34 static int usecache = 1;
35 static time_t nfttl = 300;
36 static struct cache *cache = NULL;
38 static void readconfig(void)
44 if((f = fopen(CONFIGFILE, "r")) == NULL)
47 while(fgets(linebuf, sizeof(linebuf), f) != NULL) {
50 if((p = strchr(linebuf, '\n')) != NULL)
52 if((p = strchr(linebuf, ' ')) != NULL) {
56 if(!strcmp(linebuf, "timeout")) {
61 if(!strcmp(linebuf, "ttlnotfound")) {
66 if(!strcmp(linebuf, "nocache")) {
74 static void freecache(struct cache *cc)
79 cc->next->prev = cc->prev;
81 cc->prev->next = cc->next;
86 if(cc->names != NULL) {
87 for(i = 0; cc->names[i] != NULL; i++)
94 static void cachenotfound(const void *addr, socklen_t len, int af, time_t ttl)
98 for(cc = cache; cc != NULL; cc = cc->next) {
99 if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
103 if((cc = malloc(sizeof(*cc))) == NULL)
105 memset(cc, 0, sizeof(*cc));
106 if((cc->addr = malloc(len)) == NULL) {
110 memcpy(cc->addr, addr, len);
125 static void updatecache(const void *addr, socklen_t len, int af, char **names, time_t ttl)
130 for(cc = cache; cc != NULL; cc = cc->next) {
131 if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
135 if((cc = malloc(sizeof(*cc))) == NULL)
137 memset(cc, 0, sizeof(*cc));
138 if((cc->addr = malloc(len)) == NULL) {
142 memcpy(cc->addr, addr, len);
148 for(i = 0; names[i] != NULL; i++);
149 if((cc->names = malloc(sizeof(*(cc->names)) * (i + 1))) == NULL) {
153 memset(cc->names, 0, sizeof(*(cc->names)) * (i + 1));
154 for(i = 0; names[i] != NULL; i++) {
155 if((cc->names[i] = malloc(strlen(names[i]) + 1)) == NULL) {
159 strcpy(cc->names[i], names[i]);
169 static void expirecache(void)
171 struct cache *cc, *next;
175 for(cc = cache; cc != NULL; cc = next) {
177 if(now - cc->at > cc->ttl) {
184 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)
193 int an, thislen, ttl;
206 retbuf = (struct retstruct *)buffer;
207 if((buflen < sizeof(*retbuf)) || (len > sizeof(retbuf->retaddr))) {
209 *h_errnop = NETDB_INTERNAL;
210 return(NSS_STATUS_UNAVAIL);
213 DEBUGP("starting lookup\n");
217 for(cc = cache; cc != NULL; cc = cc->next) {
218 if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
226 DEBUGP("address not in cache, looking up for real\n");
227 ap = (u_int8_t *)addr;
228 if(inet_ntop(af, addr, addrbuf, sizeof(addrbuf)) == NULL) {
230 *h_errnop = NETDB_INTERNAL;
231 return(NSS_STATUS_UNAVAIL);
233 DEBUGP("address is %s\n", addrbuf);
237 *h_errnop = NETDB_INTERNAL;
238 return(NSS_STATUS_UNAVAIL);
240 /* I honestly don't know if it is considered OK to fork in other
241 * people's programs. We need a SUID worker, though, so there's
242 * little choice that I can see. */
243 if((child = fork()) < 0) {
245 *h_errnop = NETDB_INTERNAL;
246 return(NSS_STATUS_UNAVAIL);
251 char timeoutbuf[128];
253 if((fd = open("/dev/null", O_WRONLY)) < 0)
258 for(i = 3; i < FD_SETSIZE; i++)
262 snprintf(timeoutbuf, sizeof(timeoutbuf), "%i", timeout);
263 execlp("idnlookup", "idnlookup", "-Tt", timeoutbuf, addrbuf, NULL);
265 execlp("idnlookup", "idnlookup", "-T", addrbuf, NULL);
274 ret = read(pfd[0], addrbuf + rl, sizeof(addrbuf) - rl);
277 *h_errnop = NETDB_INTERNAL;
279 return(NSS_STATUS_UNAVAIL);
282 if(rl >= sizeof(addrbuf) - 1) {
284 *h_errnop = NETDB_INTERNAL;
286 return(NSS_STATUS_UNAVAIL);
292 if((p = strchr(addrbuf, '\n')) == NULL) {
294 cachenotfound(addr, len, af, nfttl);
295 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
296 return(NSS_STATUS_NOTFOUND);
302 p3 = buffer + sizeof(*retbuf);
303 while((p2 = strchr(p, '\n')) != NULL) {
308 if((p3 - buffer) + thislen + 1 > buflen) {
310 *h_errnop = NETDB_INTERNAL;
311 return(NSS_STATUS_UNAVAIL);
313 memcpy(p3, p, thislen + 1);
314 retbuf->aliaslist[an] = p3;
319 *h_errnop = NETDB_INTERNAL;
320 return(NSS_STATUS_UNAVAIL);
325 cachenotfound(addr, len, af, nfttl);
326 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
327 return(NSS_STATUS_NOTFOUND);
329 retbuf->aliaslist[an] = NULL;
332 updatecache(addr, len, af, retbuf->aliaslist, ttl);
334 DEBUGP("address found in cache\n");
336 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
337 return(NSS_STATUS_NOTFOUND);
340 p3 = buffer + sizeof(*retbuf);
341 for(i = 0; cc->names[i] != NULL; i++) {
342 thislen = strlen(cc->names[i]);
343 DEBUGP("filling in address %s, length %i\n", cc->names[i], thislen);
344 if((p3 - buffer) + thislen + 1 > buflen) {
346 *h_errnop = NETDB_INTERNAL;
347 return(NSS_STATUS_UNAVAIL);
349 memcpy(p3, cc->names[i], thislen + 1);
350 retbuf->aliaslist[i] = p3;
353 retbuf->aliaslist[i] = NULL;
356 DEBUGP("returning hostent\n");
357 memcpy(retbuf->retaddr, addr, len);
358 retbuf->addrlist[0] = retbuf->retaddr;
359 retbuf->addrlist[1] = NULL;
360 result->h_name = retbuf->aliaslist[0];
361 result->h_aliases = retbuf->aliaslist;
362 result->h_addr_list = retbuf->addrlist;
363 result->h_addrtype = af;
364 result->h_length = len;
366 *h_errnop = NETDB_SUCCESS;
367 DEBUGP("returning\n");
368 return(NSS_STATUS_SUCCESS);