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