Added config file.
[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
15a913c2
DC
181 for(cc = cache; cc != NULL; cc = cc->next) {
182 if((cc->af == af) && (cc->addrlen == len) && !memcmp(cc->addr, addr, len))
183 break;
2dec74ab
DC
184 }
185
15a913c2
DC
186 if(cc == NULL) {
187 ap = (u_int8_t *)addr;
188 if(inet_ntop(af, addr, addrbuf, sizeof(addrbuf)) == NULL) {
2dec74ab
DC
189 *errnop = errno;
190 *h_errnop = NETDB_INTERNAL;
2dec74ab
DC
191 return(NSS_STATUS_UNAVAIL);
192 }
15a913c2
DC
193
194 if(pipe(pfd)) {
195 *errnop = errno;
2dec74ab 196 *h_errnop = NETDB_INTERNAL;
2dec74ab
DC
197 return(NSS_STATUS_UNAVAIL);
198 }
15a913c2
DC
199 /* I honestly don't know if it is considered OK to fork in other
200 * people's programs. We need a SUID worker, though, so there's
201 * little choice that I can see. */
202 if((child = fork()) < 0) {
203 *errnop = errno;
2dec74ab
DC
204 *h_errnop = NETDB_INTERNAL;
205 return(NSS_STATUS_UNAVAIL);
206 }
15a913c2
DC
207
208 if(child == 0) {
209 int i, fd;
210 char timeoutbuf[128];
211
212 if((fd = open("/dev/null", O_WRONLY)) < 0)
213 exit(127);
214 close(pfd[0]);
215 dup2(pfd[1], 1);
216 dup2(fd, 2);
217 for(i = 3; i < FD_SETSIZE; i++)
218 close(i);
219
220 if(timeout != -1) {
221 snprintf(timeoutbuf, sizeof(timeoutbuf), "%i", timeout);
222 execlp("idnlookup", "idnlookup", "-t", timeoutbuf, addrbuf, NULL);
223 } else {
224 execlp("idnlookup", "idnlookup", addrbuf, NULL);
225 }
226 exit(127);
227 }
228
229 close(pfd[1]);
230
231 rl = 0;
232 do {
233 ret = read(pfd[0], addrbuf + rl, sizeof(addrbuf) - rl);
234 if(ret < 0) {
235 *errnop = errno;
236 *h_errnop = NETDB_INTERNAL;
237 close(pfd[0]);
238 return(NSS_STATUS_UNAVAIL);
239 }
240 rl += ret;
241 if(rl >= sizeof(addrbuf) - 1) {
242 *errnop = ENOMEM;
243 *h_errnop = NETDB_INTERNAL;
244 close(pfd[0]);
245 return(NSS_STATUS_UNAVAIL);
246 }
247 } while(ret != 0);
248 addrbuf[rl] = 0;
249 close(pfd[0]);
250
251 an = 0;
252 p = addrbuf;
253 p3 = buffer + sizeof(*retbuf);
254 while((p2 = strchr(p, '\n')) != NULL) {
255 *p2 = 0;
256 thislen = p2 - p;
257 if(thislen == 0)
258 continue;
259 if((p3 - buffer) + thislen + 1 > buflen) {
260 *errnop = ENOMEM;
261 *h_errnop = NETDB_INTERNAL;
262 return(NSS_STATUS_UNAVAIL);
263 }
264 memcpy(p3, p, thislen + 1);
265 retbuf->aliaslist[an] = p3;
266 p3 += thislen + 1;
267 p = p2 + 1;
268 if(++an == 16) {
269 *errnop = ENOMEM;
270 *h_errnop = NETDB_INTERNAL;
271 return(NSS_STATUS_UNAVAIL);
272 }
273 }
274 if(an == 0) {
275 cachenotfound(addr, len, af);
276 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
277 return(NSS_STATUS_NOTFOUND);
278 }
279 retbuf->aliaslist[an] = NULL;
280 } else {
281 if(cc->notfound) {
282 *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
283 return(NSS_STATUS_NOTFOUND);
284 }
285
286 p3 = buffer + sizeof(*retbuf);
287 for(i = 0; cc->names[i] != NULL; i++) {
288 thislen = strlen(cc->names[i]);
289 if((p3 - buffer) + thislen + 1 > buflen) {
290 *errnop = ENOMEM;
291 *h_errnop = NETDB_INTERNAL;
292 return(NSS_STATUS_UNAVAIL);
293 }
294 memcpy(p3, cc->names[i], thislen + 1);
295 retbuf->aliaslist[an] = p3;
296 p3 += thislen + 1;
297 if(++an == 16) {
298 *errnop = ENOMEM;
299 *h_errnop = NETDB_INTERNAL;
300 return(NSS_STATUS_UNAVAIL);
301 }
2dec74ab
DC
302 }
303 }
2dec74ab
DC
304
305 memcpy(retbuf->retaddr, addr, len);
306 retbuf->addrlist[0] = retbuf->retaddr;
307 retbuf->addrlist[1] = NULL;
308 result->h_name = retbuf->aliaslist[0];
309 result->h_aliases = retbuf->aliaslist;
310 result->h_addr_list = retbuf->addrlist;
311 result->h_addrtype = af;
312 result->h_length = len;
313
15a913c2
DC
314 if(cc == NULL)
315 updatecache(result);
316
2dec74ab
DC
317 *h_errnop = NETDB_SUCCESS;
318 return(NSS_STATUS_SUCCESS);
319}