Include config.h only when HAVE_CONFIG_H is defined.
[icmp-dn.git] / src / nss-icmp.c
CommitLineData
732b8359
DC
1/*
2 * nss-icmp or libnss_icmp - GNU C Library NSS module to query host
3 * names by ICMP.
4 * Copyright (C) 2005 Fredrik Tolf <fredrik@dolda2000.com>
5 *
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.
10 *
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.
15 *
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,
19 * MA 02111-1307, USA
20 */
21
2dec74ab
DC
22#include <stdlib.h>
23#include <stdio.h>
24#include <unistd.h>
25#include <string.h>
26#include <errno.h>
f0df8f66 27#include <time.h>
2dec74ab
DC
28#include <sys/socket.h>
29#include <netinet/in.h>
30#include <netdb.h>
31#include <arpa/inet.h>
32#include <nss.h>
33#include <sys/types.h>
34#include <fcntl.h>
46c36380 35#include <sys/wait.h>
738f3f12 36#ifdef HAVE_CONFIG_H
2269406c 37#include "config.h"
738f3f12 38#endif
2dec74ab 39
2269406c 40#ifndef CONFIGFILE
15a913c2 41#define CONFIGFILE "/etc/nss-icmp.conf"
2269406c
DC
42#endif
43
78d913fb
DC
44#if 0
45#define DEBUGP(format...) fprintf(stderr, "nss-icmp: " format);
46#else
47#define DEBUGP(format...)
48#endif
15a913c2
DC
49
50struct cache {
51 struct cache *next, *prev;
52 char *addr;
53 socklen_t addrlen;
54 int af;
55 int notfound;
56 char **names;
f0df8f66 57 time_t at, ttl;
15a913c2
DC
58};
59
60static int inited = 0;
61static int timeout = -1;
62static int usecache = 1;
f0df8f66 63static time_t nfttl = 300;
15a913c2
DC
64static struct cache *cache = NULL;
65
66static void readconfig(void)
67{
68 FILE *f;
69 char linebuf[1024];
70 char *p, *p2;
71
72 if((f = fopen(CONFIGFILE, "r")) == NULL)
73 return;
74
75 while(fgets(linebuf, sizeof(linebuf), f) != NULL) {
76 if(linebuf[0] == '#')
77 continue;
78 if((p = strchr(linebuf, '\n')) != NULL)
79 *p = 0;
80 if((p = strchr(linebuf, ' ')) != NULL) {
81 p2 = p + 1;
82 *p = 0;
83 }
84 if(!strcmp(linebuf, "timeout")) {
85 if(p2 == NULL)
86 continue;
87 timeout = atoi(p2);
88 }
f0df8f66
DC
89 if(!strcmp(linebuf, "ttlnotfound")) {
90 if(p2 == NULL)
91 continue;
92 nfttl = atoi(p2);
93 }
15a913c2
DC
94 if(!strcmp(linebuf, "nocache")) {
95 usecache = 0;
96 }
97 }
98
99 fclose(f);
100}
101
102static void freecache(struct cache *cc)
103{
104 int i;
105
106 if(cc->next != NULL)
107 cc->next->prev = cc->prev;
108 if(cc->prev != NULL)
109 cc->prev->next = cc->next;
110 if(cc == cache)
111 cache = cc->next;
112 if(cc->addr != NULL)
113 free(cc->addr);
114 if(cc->names != NULL) {
115 for(i = 0; cc->names[i] != NULL; i++)
116 free(cc->names[i]);
117 free(cc->names);
118 }
119 free(cc);
120}
121
f0df8f66 122static void cachenotfound(const void *addr, socklen_t len, int af, time_t ttl)
15a913c2
DC
123{
124 struct cache *cc;
125
126 for(cc = cache; cc != NULL; cc = cc->next) {
127 if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
128 break;
129 }
130 if(cc == NULL) {
131 if((cc = malloc(sizeof(*cc))) == NULL)
132 return;
133 memset(cc, 0, sizeof(*cc));
134 if((cc->addr = malloc(len)) == NULL) {
135 freecache(cc);
136 return;
137 }
138 memcpy(cc->addr, addr, len);
139 cc->addrlen = len;
140 cc->af = af;
f0df8f66
DC
141 cc->at = time(NULL);
142 cc->ttl = ttl;
15a913c2
DC
143
144 cc->notfound = 1;
145
146 cc->next = cache;
147 if(cache != NULL)
148 cache->prev = cc;
149 cache = cc;
150 }
151}
152
f0df8f66 153static void updatecache(const void *addr, socklen_t len, int af, char **names, time_t ttl)
15a913c2
DC
154{
155 int i;
156 struct cache *cc;
157
158 for(cc = cache; cc != NULL; cc = cc->next) {
f0df8f66 159 if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
15a913c2
DC
160 break;
161 }
162 if(cc == NULL) {
163 if((cc = malloc(sizeof(*cc))) == NULL)
164 return;
165 memset(cc, 0, sizeof(*cc));
f0df8f66 166 if((cc->addr = malloc(len)) == NULL) {
15a913c2
DC
167 freecache(cc);
168 return;
169 }
f0df8f66
DC
170 memcpy(cc->addr, addr, len);
171 cc->addrlen = len;
172 cc->af = af;
173 cc->at = time(NULL);
174 cc->ttl = ttl;
15a913c2 175
f0df8f66 176 for(i = 0; names[i] != NULL; i++);
15a913c2
DC
177 if((cc->names = malloc(sizeof(*(cc->names)) * (i + 1))) == NULL) {
178 freecache(cc);
179 return;
180 }
181 memset(cc->names, 0, sizeof(*(cc->names)) * (i + 1));
f0df8f66
DC
182 for(i = 0; names[i] != NULL; i++) {
183 if((cc->names[i] = malloc(strlen(names[i]) + 1)) == NULL) {
15a913c2
DC
184 freecache(cc);
185 return;
186 }
f0df8f66 187 strcpy(cc->names[i], names[i]);
15a913c2
DC
188 }
189
190 cc->next = cache;
191 if(cache != NULL)
192 cache->prev = cc;
193 cache = cc;
194 }
195}
196
f0df8f66
DC
197static void expirecache(void)
198{
199 struct cache *cc, *next;
200 time_t now;
201
202 now = time(NULL);
203 for(cc = cache; cc != NULL; cc = next) {
204 next = cc->next;
205 if(now - cc->at > cc->ttl) {
206 freecache(cc);
207 continue;
208 }
209 }
210}
211
2dec74ab
DC
212enum 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)
213{
15a913c2 214 int i, ret;
2dec74ab
DC
215 struct retstruct {
216 char *aliaslist[16];
217 char *addrlist[2];
218 char retaddr[16];
219 } *retbuf;
220 char addrbuf[1024];
f0df8f66 221 int an, thislen, ttl;
2dec74ab
DC
222 char *p, *p2, *p3;
223 u_int8_t *ap;
224 pid_t child;
225 int pfd[2];
226 int rl;
46c36380 227 int status;
15a913c2
DC
228 struct cache *cc;
229
230 if(!inited) {
231 readconfig();
232 inited = 1;
233 }
2dec74ab
DC
234
235 retbuf = (struct retstruct *)buffer;
236 if((buflen < sizeof(*retbuf)) || (len > sizeof(retbuf->retaddr))) {
237 *errnop = ENOMEM;
238 *h_errnop = NETDB_INTERNAL;
239 return(NSS_STATUS_UNAVAIL);
240 }
241
78d913fb
DC
242 DEBUGP("starting lookup\n");
243
f416a8cd 244 if(usecache) {
f0df8f66 245 expirecache();
f416a8cd
DC
246 for(cc = cache; cc != NULL; cc = cc->next) {
247 if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
248 break;
249 }
250 } else {
251 cc = NULL;
2dec74ab
DC
252 }
253
15a913c2 254 if(cc == NULL) {
78d913fb 255 DEBUGP("address not in cache, looking up for real\n");
15a913c2
DC
256 ap = (u_int8_t *)addr;
257 if(inet_ntop(af, addr, addrbuf, sizeof(addrbuf)) == NULL) {
2dec74ab
DC
258 *errnop = errno;
259 *h_errnop = NETDB_INTERNAL;
2dec74ab
DC
260 return(NSS_STATUS_UNAVAIL);
261 }
78d913fb 262 DEBUGP("address is %s\n", addrbuf);
15a913c2
DC
263
264 if(pipe(pfd)) {
265 *errnop = errno;
2dec74ab 266 *h_errnop = NETDB_INTERNAL;
2dec74ab
DC
267 return(NSS_STATUS_UNAVAIL);
268 }
15a913c2
DC
269 /* I honestly don't know if it is considered OK to fork in other
270 * people's programs. We need a SUID worker, though, so there's
271 * little choice that I can see. */
272 if((child = fork()) < 0) {
273 *errnop = errno;
2dec74ab
DC
274 *h_errnop = NETDB_INTERNAL;
275 return(NSS_STATUS_UNAVAIL);
276 }
15a913c2
DC
277
278 if(child == 0) {
279 int i, fd;
280 char timeoutbuf[128];
281
282 if((fd = open("/dev/null", O_WRONLY)) < 0)
283 exit(127);
284 close(pfd[0]);
285 dup2(pfd[1], 1);
286 dup2(fd, 2);
287 for(i = 3; i < FD_SETSIZE; i++)
288 close(i);
289
290 if(timeout != -1) {
291 snprintf(timeoutbuf, sizeof(timeoutbuf), "%i", timeout);
f0df8f66 292 execlp("idnlookup", "idnlookup", "-Tt", timeoutbuf, addrbuf, NULL);
15a913c2 293 } else {
f0df8f66 294 execlp("idnlookup", "idnlookup", "-T", addrbuf, NULL);
15a913c2
DC
295 }
296 exit(127);
297 }
298
299 close(pfd[1]);
300
301 rl = 0;
302 do {
303 ret = read(pfd[0], addrbuf + rl, sizeof(addrbuf) - rl);
304 if(ret < 0) {
305 *errnop = errno;
306 *h_errnop = NETDB_INTERNAL;
307 close(pfd[0]);
308 return(NSS_STATUS_UNAVAIL);
309 }
310 rl += ret;
311 if(rl >= sizeof(addrbuf) - 1) {
312 *errnop = ENOMEM;
313 *h_errnop = NETDB_INTERNAL;
314 close(pfd[0]);
315 return(NSS_STATUS_UNAVAIL);
316 }
317 } while(ret != 0);
318 addrbuf[rl] = 0;
319 close(pfd[0]);
f0df8f66 320
46c36380
DC
321 waitpid(child, &status, 0);
322
f0df8f66
DC
323 if((p = strchr(addrbuf, '\n')) == NULL) {
324 if(usecache)
325 cachenotfound(addr, len, af, nfttl);
326 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
327 return(NSS_STATUS_NOTFOUND);
328 }
329 *(p++) = 0;
330 ttl = atoi(addrbuf);
331
15a913c2 332 an = 0;
15a913c2
DC
333 p3 = buffer + sizeof(*retbuf);
334 while((p2 = strchr(p, '\n')) != NULL) {
335 *p2 = 0;
336 thislen = p2 - p;
337 if(thislen == 0)
338 continue;
339 if((p3 - buffer) + thislen + 1 > buflen) {
340 *errnop = ENOMEM;
341 *h_errnop = NETDB_INTERNAL;
342 return(NSS_STATUS_UNAVAIL);
343 }
344 memcpy(p3, p, thislen + 1);
345 retbuf->aliaslist[an] = p3;
346 p3 += thislen + 1;
347 p = p2 + 1;
348 if(++an == 16) {
349 *errnop = ENOMEM;
350 *h_errnop = NETDB_INTERNAL;
351 return(NSS_STATUS_UNAVAIL);
352 }
353 }
354 if(an == 0) {
f416a8cd 355 if(usecache)
f0df8f66 356 cachenotfound(addr, len, af, nfttl);
15a913c2
DC
357 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
358 return(NSS_STATUS_NOTFOUND);
359 }
360 retbuf->aliaslist[an] = NULL;
f0df8f66
DC
361
362 if(usecache)
363 updatecache(addr, len, af, retbuf->aliaslist, ttl);
15a913c2 364 } else {
78d913fb 365 DEBUGP("address found in cache\n");
15a913c2
DC
366 if(cc->notfound) {
367 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
368 return(NSS_STATUS_NOTFOUND);
369 }
370
371 p3 = buffer + sizeof(*retbuf);
372 for(i = 0; cc->names[i] != NULL; i++) {
373 thislen = strlen(cc->names[i]);
78d913fb 374 DEBUGP("filling in address %s, length %i\n", cc->names[i], thislen);
15a913c2
DC
375 if((p3 - buffer) + thislen + 1 > buflen) {
376 *errnop = ENOMEM;
377 *h_errnop = NETDB_INTERNAL;
378 return(NSS_STATUS_UNAVAIL);
379 }
380 memcpy(p3, cc->names[i], thislen + 1);
78d913fb 381 retbuf->aliaslist[i] = p3;
15a913c2 382 p3 += thislen + 1;
2dec74ab 383 }
78d913fb 384 retbuf->aliaslist[i] = NULL;
2dec74ab 385 }
2dec74ab 386
78d913fb 387 DEBUGP("returning hostent\n");
2dec74ab
DC
388 memcpy(retbuf->retaddr, addr, len);
389 retbuf->addrlist[0] = retbuf->retaddr;
390 retbuf->addrlist[1] = NULL;
391 result->h_name = retbuf->aliaslist[0];
392 result->h_aliases = retbuf->aliaslist;
393 result->h_addr_list = retbuf->addrlist;
394 result->h_addrtype = af;
395 result->h_length = len;
396
397 *h_errnop = NETDB_SUCCESS;
78d913fb 398 DEBUGP("returning\n");
2dec74ab
DC
399 return(NSS_STATUS_SUCCESS);
400}
732b8359
DC
401
402/*
403 * Local Variables:
404 * compile-command: "gcc -shared -Wall -g -o libnss_icmp.so.2 nss-icmp.c"
405 * End:
406 */