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