Honor the `nocache' config option.
[icmp-dn.git] / nss-icmp.c
CommitLineData
2dec74ab
DC
1#include <stdlib.h>
2#include <stdio.h>
3#include <unistd.h>
4#include <string.h>
5#include <errno.h>
6#include <sys/socket.h>
7#include <netinet/in.h>
8#include <netdb.h>
9#include <arpa/inet.h>
10#include <nss.h>
11#include <sys/types.h>
12#include <fcntl.h>
13
15a913c2
DC
14#define CONFIGFILE "/etc/nss-icmp.conf"
15
16struct cache {
17 struct cache *next, *prev;
18 char *addr;
19 socklen_t addrlen;
20 int af;
21 int notfound;
22 char **names;
23};
24
25static int inited = 0;
26static int timeout = -1;
27static int usecache = 1;
28static struct cache *cache = NULL;
29
30static void readconfig(void)
31{
32 FILE *f;
33 char linebuf[1024];
34 char *p, *p2;
35
36 if((f = fopen(CONFIGFILE, "r")) == NULL)
37 return;
38
39 while(fgets(linebuf, sizeof(linebuf), f) != NULL) {
40 if(linebuf[0] == '#')
41 continue;
42 if((p = strchr(linebuf, '\n')) != NULL)
43 *p = 0;
44 if((p = strchr(linebuf, ' ')) != NULL) {
45 p2 = p + 1;
46 *p = 0;
47 }
48 if(!strcmp(linebuf, "timeout")) {
49 if(p2 == NULL)
50 continue;
51 timeout = atoi(p2);
52 }
53 if(!strcmp(linebuf, "nocache")) {
54 usecache = 0;
55 }
56 }
57
58 fclose(f);
59}
60
61static void freecache(struct cache *cc)
62{
63 int i;
64
65 if(cc->next != NULL)
66 cc->next->prev = cc->prev;
67 if(cc->prev != NULL)
68 cc->prev->next = cc->next;
69 if(cc == cache)
70 cache = cc->next;
71 if(cc->addr != NULL)
72 free(cc->addr);
73 if(cc->names != NULL) {
74 for(i = 0; cc->names[i] != NULL; i++)
75 free(cc->names[i]);
76 free(cc->names);
77 }
78 free(cc);
79}
80
81static void cachenotfound(const void *addr, socklen_t len, int af)
82{
83 struct cache *cc;
84
85 for(cc = cache; cc != NULL; cc = cc->next) {
86 if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
87 break;
88 }
89 if(cc == NULL) {
90 if((cc = malloc(sizeof(*cc))) == NULL)
91 return;
92 memset(cc, 0, sizeof(*cc));
93 if((cc->addr = malloc(len)) == NULL) {
94 freecache(cc);
95 return;
96 }
97 memcpy(cc->addr, addr, len);
98 cc->addrlen = len;
99 cc->af = af;
100
101 cc->notfound = 1;
102
103 cc->next = cache;
104 if(cache != NULL)
105 cache->prev = cc;
106 cache = cc;
107 }
108}
109
110static void updatecache(struct hostent *he)
111{
112 int i;
113 struct cache *cc;
114
115 for(cc = cache; cc != NULL; cc = cc->next) {
116 if((cc->af == he->h_addrtype) && (cc->addrlen == he->h_length) && !memcmp(cc->addr, he->h_addr_list[0], he->h_length))
117 break;
118 }
119 if(cc == NULL) {
120 if((cc = malloc(sizeof(*cc))) == NULL)
121 return;
122 memset(cc, 0, sizeof(*cc));
123 if((cc->addr = malloc(he->h_length)) == NULL) {
124 freecache(cc);
125 return;
126 }
127 memcpy(cc->addr, he->h_addr_list[0], he->h_length);
128 cc->addrlen = he->h_length;
129 cc->af = he->h_addrtype;
130
131 for(i = 0; he->h_aliases[i] != NULL; i++);
132 if((cc->names = malloc(sizeof(*(cc->names)) * (i + 1))) == NULL) {
133 freecache(cc);
134 return;
135 }
136 memset(cc->names, 0, sizeof(*(cc->names)) * (i + 1));
137 for(i = 0; he->h_aliases[i] != NULL; i++) {
138 if((cc->names[i] = malloc(strlen(he->h_aliases[i]) + 1)) == NULL) {
139 freecache(cc);
140 return;
141 }
142 strcpy(cc->names[i], he->h_aliases[i]);
143 }
144
145 cc->next = cache;
146 if(cache != NULL)
147 cache->prev = cc;
148 cache = cc;
149 }
150}
151
2dec74ab
DC
152enum 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)
153{
15a913c2 154 int i, ret;
2dec74ab
DC
155 struct retstruct {
156 char *aliaslist[16];
157 char *addrlist[2];
158 char retaddr[16];
159 } *retbuf;
160 char addrbuf[1024];
03511d7b 161 int an, thislen;
2dec74ab
DC
162 char *p, *p2, *p3;
163 u_int8_t *ap;
164 pid_t child;
165 int pfd[2];
166 int rl;
15a913c2
DC
167 struct cache *cc;
168
169 if(!inited) {
170 readconfig();
171 inited = 1;
172 }
2dec74ab
DC
173
174 retbuf = (struct retstruct *)buffer;
175 if((buflen < sizeof(*retbuf)) || (len > sizeof(retbuf->retaddr))) {
176 *errnop = ENOMEM;
177 *h_errnop = NETDB_INTERNAL;
178 return(NSS_STATUS_UNAVAIL);
179 }
180
f416a8cd
DC
181 if(usecache) {
182 for(cc = cache; cc != NULL; cc = cc->next) {
183 if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
184 break;
185 }
186 } else {
187 cc = NULL;
2dec74ab
DC
188 }
189
15a913c2
DC
190 if(cc == NULL) {
191 ap = (u_int8_t *)addr;
192 if(inet_ntop(af, addr, addrbuf, sizeof(addrbuf)) == NULL) {
2dec74ab
DC
193 *errnop = errno;
194 *h_errnop = NETDB_INTERNAL;
2dec74ab
DC
195 return(NSS_STATUS_UNAVAIL);
196 }
15a913c2
DC
197
198 if(pipe(pfd)) {
199 *errnop = errno;
2dec74ab 200 *h_errnop = NETDB_INTERNAL;
2dec74ab
DC
201 return(NSS_STATUS_UNAVAIL);
202 }
15a913c2
DC
203 /* I honestly don't know if it is considered OK to fork in other
204 * people's programs. We need a SUID worker, though, so there's
205 * little choice that I can see. */
206 if((child = fork()) < 0) {
207 *errnop = errno;
2dec74ab
DC
208 *h_errnop = NETDB_INTERNAL;
209 return(NSS_STATUS_UNAVAIL);
210 }
15a913c2
DC
211
212 if(child == 0) {
213 int i, fd;
214 char timeoutbuf[128];
215
216 if((fd = open("/dev/null", O_WRONLY)) < 0)
217 exit(127);
218 close(pfd[0]);
219 dup2(pfd[1], 1);
220 dup2(fd, 2);
221 for(i = 3; i < FD_SETSIZE; i++)
222 close(i);
223
224 if(timeout != -1) {
225 snprintf(timeoutbuf, sizeof(timeoutbuf), "%i", timeout);
226 execlp("idnlookup", "idnlookup", "-t", timeoutbuf, addrbuf, NULL);
227 } else {
228 execlp("idnlookup", "idnlookup", addrbuf, NULL);
229 }
230 exit(127);
231 }
232
233 close(pfd[1]);
234
235 rl = 0;
236 do {
237 ret = read(pfd[0], addrbuf + rl, sizeof(addrbuf) - rl);
238 if(ret < 0) {
239 *errnop = errno;
240 *h_errnop = NETDB_INTERNAL;
241 close(pfd[0]);
242 return(NSS_STATUS_UNAVAIL);
243 }
244 rl += ret;
245 if(rl >= sizeof(addrbuf) - 1) {
246 *errnop = ENOMEM;
247 *h_errnop = NETDB_INTERNAL;
248 close(pfd[0]);
249 return(NSS_STATUS_UNAVAIL);
250 }
251 } while(ret != 0);
252 addrbuf[rl] = 0;
253 close(pfd[0]);
254
255 an = 0;
256 p = addrbuf;
257 p3 = buffer + sizeof(*retbuf);
258 while((p2 = strchr(p, '\n')) != NULL) {
259 *p2 = 0;
260 thislen = p2 - p;
261 if(thislen == 0)
262 continue;
263 if((p3 - buffer) + thislen + 1 > buflen) {
264 *errnop = ENOMEM;
265 *h_errnop = NETDB_INTERNAL;
266 return(NSS_STATUS_UNAVAIL);
267 }
268 memcpy(p3, p, thislen + 1);
269 retbuf->aliaslist[an] = p3;
270 p3 += thislen + 1;
271 p = p2 + 1;
272 if(++an == 16) {
273 *errnop = ENOMEM;
274 *h_errnop = NETDB_INTERNAL;
275 return(NSS_STATUS_UNAVAIL);
276 }
277 }
278 if(an == 0) {
f416a8cd
DC
279 if(usecache)
280 cachenotfound(addr, len, af);
15a913c2
DC
281 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
282 return(NSS_STATUS_NOTFOUND);
283 }
284 retbuf->aliaslist[an] = NULL;
285 } else {
286 if(cc->notfound) {
287 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
288 return(NSS_STATUS_NOTFOUND);
289 }
290
291 p3 = buffer + sizeof(*retbuf);
292 for(i = 0; cc->names[i] != NULL; i++) {
293 thislen = strlen(cc->names[i]);
294 if((p3 - buffer) + thislen + 1 > buflen) {
295 *errnop = ENOMEM;
296 *h_errnop = NETDB_INTERNAL;
297 return(NSS_STATUS_UNAVAIL);
298 }
299 memcpy(p3, cc->names[i], thislen + 1);
300 retbuf->aliaslist[an] = p3;
301 p3 += thislen + 1;
302 if(++an == 16) {
303 *errnop = ENOMEM;
304 *h_errnop = NETDB_INTERNAL;
305 return(NSS_STATUS_UNAVAIL);
306 }
2dec74ab
DC
307 }
308 }
2dec74ab
DC
309
310 memcpy(retbuf->retaddr, addr, len);
311 retbuf->addrlist[0] = retbuf->retaddr;
312 retbuf->addrlist[1] = NULL;
313 result->h_name = retbuf->aliaslist[0];
314 result->h_aliases = retbuf->aliaslist;
315 result->h_addr_list = retbuf->addrlist;
316 result->h_addrtype = af;
317 result->h_length = len;
318
f416a8cd 319 if((cc == NULL) && usecache)
15a913c2
DC
320 updatecache(result);
321
2dec74ab
DC
322 *h_errnop = NETDB_SUCCESS;
323 return(NSS_STATUS_SUCCESS);
324}