]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/resolve.c
IPv6 support.
[ngircd-alex.git] / src / ngircd / resolve.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2003 by Alexander Barton (alex@barton.de)
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  *
11  * Asynchronous resolver
12  */
13
14
15 #include "portab.h"
16
17 static char UNUSED id[] = "$Id: resolve.c,v 1.29 2008/02/26 22:04:17 fw Exp $";
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <netdb.h>
28
29 #ifdef IDENTAUTH
30 #ifdef HAVE_IDENT_H
31 #include <ident.h>
32 #endif
33 #endif
34
35 #include "conn.h"
36 #include "defines.h"
37 #include "log.h"
38
39 #include "exp.h"
40 #include "resolve.h"
41 #include "io.h"
42
43
44 static void Do_ResolveAddr PARAMS(( const ng_ipaddr_t *Addr, int Sock, int w_fd ));
45 static void Do_ResolveName PARAMS(( const char *Host, int w_fd ));
46 static bool register_callback PARAMS((RES_STAT *s, void (*cbfunc)(int, short)));
47
48
49 static pid_t
50 Resolver_fork(int *pipefds)
51 {
52         pid_t pid;
53
54         if (pipe(pipefds) != 0) {
55                 Log( LOG_ALERT, "Resolver: Can't create output pipe: %s!", strerror( errno ));
56                 return -1;
57         }
58
59         pid = fork();
60         switch(pid) {
61                 case -1:
62                         Log( LOG_CRIT, "Resolver: Can't fork: %s!", strerror( errno ));
63                         close(pipefds[0]);
64                         close(pipefds[1]);
65                         return -1;
66                 case 0: /* child */
67                         close(pipefds[0]);
68                         Log_Init_Resolver( );
69                         return 0;
70         }
71         /* parent */
72         close(pipefds[1]);
73         return pid; 
74 }
75
76
77 /**
78  * Resolve IP (asynchronous!).
79  */
80 GLOBAL bool
81 Resolve_Addr(RES_STAT * s, const ng_ipaddr_t *Addr, int identsock,
82              void (*cbfunc) (int, short))
83 {
84         int pipefd[2];
85         pid_t pid;
86
87         assert(s != NULL);
88
89         pid = Resolver_fork(pipefd);
90         if (pid > 0) {
91                 Log(LOG_DEBUG, "Resolver for %s created (PID %d).", ng_ipaddr_tostr(Addr), pid);
92
93                 s->pid = pid;
94                 s->resolver_fd = pipefd[0];
95                 return register_callback(s, cbfunc);
96         } else if( pid == 0 ) {
97                 /* Sub process */
98                 Do_ResolveAddr( Addr, identsock, pipefd[1]);
99                 Log_Exit_Resolver( );
100                 exit(0);
101         }
102         return false;
103 } /* Resolve_Addr */
104
105
106 /**
107  * Resolve hostname (asynchronous!).
108  */
109 GLOBAL bool
110 Resolve_Name( RES_STAT *s, const char *Host, void (*cbfunc)(int, short))
111 {
112         int pipefd[2];
113         pid_t pid;
114
115         assert(s != NULL);
116
117         pid = Resolver_fork(pipefd);
118         if (pid > 0) {
119                 /* Main process */
120 #ifdef DEBUG
121                 Log( LOG_DEBUG, "Resolver for \"%s\" created (PID %d).", Host, pid );
122 #endif
123                 s->pid = pid;
124                 s->resolver_fd = pipefd[0];
125                 return register_callback(s, cbfunc);
126         } else if( pid == 0 ) {
127                 /* Sub process */
128                 Do_ResolveName(Host, pipefd[1]);
129                 Log_Exit_Resolver( );
130                 exit(0);
131         }
132         return false;
133 } /* Resolve_Name */
134
135
136 GLOBAL void
137 Resolve_Init(RES_STAT *s)
138 {
139         assert(s != NULL);
140         s->resolver_fd = -1;
141         s->pid = 0;
142 }
143
144
145 #ifndef WANT_IPV6
146 #ifdef h_errno
147 static char *
148 Get_Error( int H_Error )
149 {
150         /* Get error message for H_Error */
151         switch( H_Error ) {
152         case HOST_NOT_FOUND:
153                 return "host not found";
154         case NO_DATA:
155                 return "name valid but no IP address defined";
156         case NO_RECOVERY:
157                 return "name server error";
158         case TRY_AGAIN:
159                 return "name server temporary not available";
160         }
161         return "unknown error";
162 }
163 #endif /* h_errno */
164 #endif /* WANT_IPV6 */
165
166
167 /* Do "IDENT" (aka "AUTH") lookup and append result to resolved_addr array */
168 static void
169 Do_IdentQuery(int identsock, array *resolved_addr)
170 {
171 #ifdef IDENTAUTH
172         char *res;
173
174         assert(identsock >= 0);
175
176 #ifdef DEBUG
177         Log_Resolver(LOG_DEBUG, "Doing IDENT lookup on socket %d ...", identsock);
178 #endif
179         if (identsock < 0)
180                 return;
181         res = ident_id( identsock, 10 );
182 #ifdef DEBUG
183         Log_Resolver(LOG_DEBUG, "Ok, IDENT lookup on socket %d done: \"%s\"",
184                                                 identsock, res ? res : "(NULL)" );
185 #endif
186         if (!res) /* no result */
187                 return;
188         if (!array_cats(resolved_addr, res))
189                 Log_Resolver(LOG_WARNING, "Resolver: Cannot copy IDENT result: %s!", strerror(errno));
190
191         free(res);
192 #else
193         (void) identsock;
194         (void) resolved_addr;
195 #endif
196 }
197
198
199 /**
200  * perform reverse DNS lookup and put result string into resbuf.
201  * If no hostname could be obtained, this function stores the string representation of
202  * the IP address in resbuf and returns false.
203  * @param IpAddr ip address to resolve
204  * @param resbuf result buffer to store DNS name/string representation of ip address
205  * @reslen size of result buffer (must be >= NGT_INET_ADDRSTRLEN)
206  * @return true if reverse lookup successful, false otherwise
207  */
208 static bool
209 ReverseLookup(const ng_ipaddr_t *IpAddr, char *resbuf, size_t reslen)
210 {
211         char tmp_ip_str[NG_INET_ADDRSTRLEN];
212         const char *errmsg;
213 #ifdef HAVE_GETNAMEINFO
214         static const char funcname[]="getnameinfo";
215         int res;
216
217         *resbuf = 0;
218
219         res = getnameinfo((struct sockaddr *) IpAddr, ng_ipaddr_salen(IpAddr),
220                                 resbuf, reslen, NULL, 0, NI_NAMEREQD);
221         if (res == 0)
222                 return true;
223
224         if (res == EAI_SYSTEM)
225                 errmsg = strerror(errno);
226         else
227                 errmsg = gai_strerror(res);
228 #else
229         const struct sockaddr_in *Addr = (const struct sockaddr_in *) IpAddr;
230         struct hostent *h;
231         static const char funcname[]="gethostbyaddr";
232
233         h = gethostbyaddr((char *)&Addr->sin_addr, sizeof(Addr->sin_addr), AF_INET);
234         if (h) {
235                 if (strlcpy(resbuf, h->h_name, reslen) < reslen)
236                         return true;
237                 errmsg = "hostname too long";
238         } else {
239 # ifdef h_errno
240                 errmsg = Get_Error(h_errno);
241 # else
242                 errmsg = "unknown error";
243 # endif /* h_errno */
244         }
245 #endif  /* HAVE_GETNAMEINFO */
246
247         assert(errmsg);
248         assert(reslen >= NG_INET_ADDRSTRLEN);
249         ng_ipaddr_tostr_r(IpAddr, tmp_ip_str);
250
251         Log_Resolver(LOG_WARNING, "%s: Can't resolve address \"%s\": %s",
252                                 funcname, tmp_ip_str, errmsg);
253         strlcpy(resbuf, tmp_ip_str, reslen);
254         return false;
255 }
256
257
258 /**
259  * perform DNS lookup of given host name and fill IpAddr with a list of
260  * ip addresses associated with that name.
261  * ip addresses found are stored in the "array *IpAddr" argument (type ng_ipaddr_t)
262  * @param hostname The domain name to look up.
263  * @param IpAddr pointer to empty and initialized array to store results
264  * @return true if lookup successful, false if domain name not found
265  */
266 static bool
267 ForwardLookup(const char *hostname, array *IpAddr)
268 {
269         ng_ipaddr_t addr;
270 #ifdef HAVE_GETADDRINFO
271         int res;
272         struct addrinfo *a, *ai_results;
273         static const struct addrinfo hints = {
274 #ifndef WANT_IPV6
275                 .ai_family = AF_INET,
276 #endif
277 #ifdef AI_ADDRCONFIG    /* glibc has this, but not e.g. netbsd 4.0 */
278                 .ai_flags = AI_ADDRCONFIG,
279 #endif
280                 .ai_socktype = SOCK_STREAM,
281                 .ai_protocol = IPPROTO_TCP
282         };
283         res = getaddrinfo(hostname, NULL, &hints, &ai_results);
284         switch (res) {
285         case 0: break;
286         case EAI_SYSTEM:
287                 Log_Resolver(LOG_WARNING, "Can't resolve \"%s\": %s", hostname, strerror(errno));
288                 return false;
289         default:
290                 Log_Resolver(LOG_WARNING, "Can't resolve \"%s\": %s", hostname, gai_strerror(res));
291                 return false;
292         }
293
294         for (a = ai_results; a != NULL; a = a->ai_next) {
295                 assert(a->ai_addrlen <= sizeof(addr));
296
297                 if (a->ai_addrlen > sizeof(addr))
298                         continue;
299
300                 memcpy(&addr, a->ai_addr, a->ai_addrlen);
301
302                 if (!array_catb(IpAddr, (char *)&addr, sizeof(addr)))
303                         break;
304         }
305
306         freeaddrinfo(ai_results);
307         return a == NULL;
308 #else
309         struct hostent *h = gethostbyname(hostname);
310
311         if (!h) {
312 #ifdef h_errno
313                 Log_Resolver(LOG_WARNING, "Can't resolve \"%s\": %s", hostname, Get_Error(h_errno));
314 #else
315                 Log_Resolver(LOG_WARNING, "Can't resolve \"%s\"", hostname);
316 #endif
317                 return false;
318         }
319         memset(&addr, 0, sizeof(addr));
320
321         addr.sin4.sin_family = AF_INET;
322         memcpy(&addr.sin4.sin_addr, h->h_addr, sizeof(struct in_addr));
323
324         return array_copyb(IpAddr, (char *)&addr, sizeof(addr));
325 #endif /* HAVE_GETADDRINFO */
326 }
327
328
329 static bool
330 Addr_in_list(const array *resolved_addr, const ng_ipaddr_t *Addr)
331 {
332         char tmp_ip_str[NG_INET_ADDRSTRLEN];
333         const ng_ipaddr_t *tmpAddrs = array_start(resolved_addr);
334         size_t len = array_length(resolved_addr, sizeof(*tmpAddrs));
335
336         assert(len > 0);
337         assert(tmpAddrs);
338
339         while (len > 0) {
340                 if (ng_ipaddr_ipequal(Addr, tmpAddrs))
341                         return true;
342                 tmpAddrs++;
343                 len--;
344         }
345         /* failed; print list of addresses */
346         ng_ipaddr_tostr_r(Addr, tmp_ip_str);
347         len = array_length(resolved_addr, sizeof(*tmpAddrs));
348         tmpAddrs = array_start(resolved_addr);
349
350         while (len > 0) {
351                 Log_Resolver(LOG_WARNING, "Address mismatch: %s != %s",
352                         tmp_ip_str, ng_ipaddr_tostr(tmpAddrs));
353                 tmpAddrs++;
354                 len--;
355         }
356
357         return false;
358 }
359
360
361 static void
362 Log_Forgery_NoIP(const char *ip, const char *host)
363 {
364         Log_Resolver(LOG_WARNING, "Possible forgery: %s resolved to %s "
365                 "(which has no ip address)", ip, host);
366 }
367
368 static void
369 Log_Forgery_WrongIP(const char *ip, const char *host)
370 {
371         Log_Resolver(LOG_WARNING,"Possible forgery: %s resolved to %s "
372                 "(which points to different address)", ip, host);
373 }
374
375
376 static void
377 ArrayWrite(int fd, const array *a)
378 {
379         size_t len = array_bytes(a);
380         const char *data = array_start(a);
381
382         assert(data);
383
384         if( (size_t)write(fd, data, len) != len )
385                 Log_Resolver( LOG_CRIT, "Resolver: Can't write to parent: %s!",
386                                                         strerror(errno));
387 }
388
389
390 static void
391 Do_ResolveAddr(const ng_ipaddr_t *Addr, int identsock, int w_fd)
392 {
393         /* Resolver sub-process: resolve IP address and write result into
394          * pipe to parent. */
395         char hostname[CLIENT_HOST_LEN];
396         char tmp_ip_str[NG_INET_ADDRSTRLEN];
397         size_t len;
398         array resolved_addr;
399
400         array_init(&resolved_addr);
401         ng_ipaddr_tostr_r(Addr, tmp_ip_str);
402 #ifdef DEBUG
403         Log_Resolver(LOG_DEBUG, "Now resolving %s ...", tmp_ip_str);
404 #endif
405         if (!ReverseLookup(Addr, hostname, sizeof(hostname)))
406                 goto dns_done;
407
408         if (ForwardLookup(hostname, &resolved_addr)) {
409                 if (!Addr_in_list(&resolved_addr, Addr)) {
410                         Log_Forgery_WrongIP(tmp_ip_str, hostname);
411                         strlcpy(hostname, tmp_ip_str, sizeof(hostname));
412                 }
413         } else {
414                 Log_Forgery_NoIP(tmp_ip_str, hostname);
415                 strlcpy(hostname, tmp_ip_str, sizeof(hostname));
416         }
417 #ifdef DEBUG
418         Log_Resolver(LOG_DEBUG, "Ok, translated %s to \"%s\".", tmp_ip_str, hostname);
419 #endif
420  dns_done:
421         len = strlen(hostname);
422         hostname[len] = '\n';
423         if (!array_copyb(&resolved_addr, hostname, ++len)) {
424                 Log_Resolver(LOG_CRIT, "Resolver: Can't copy resolved name: %s!", strerror(errno));
425                 array_free(&resolved_addr);
426                 return;
427         }
428
429         Do_IdentQuery(identsock, &resolved_addr);
430
431         ArrayWrite(w_fd, &resolved_addr);
432
433         array_free(&resolved_addr);
434 } /* Do_ResolveAddr */
435
436
437 static void
438 Do_ResolveName( const char *Host, int w_fd )
439 {
440         /* Resolver sub-process: resolve name and write result into pipe
441          * to parent. */
442         array IpAddrs;
443 #ifdef DEBUG
444         ng_ipaddr_t *addr;
445         size_t len;
446 #endif
447         Log_Resolver(LOG_DEBUG, "Now resolving \"%s\" ...", Host);
448
449         array_init(&IpAddrs);
450         /* Resolve hostname */
451         if (!ForwardLookup(Host, &IpAddrs)) {
452                 close(w_fd);
453                 return;
454         }
455 #ifdef DEBUG
456         len = array_length(&IpAddrs, sizeof(*addr));
457         assert(len > 0);
458         addr = array_start(&IpAddrs);
459         assert(addr);
460         for (; len > 0; --len,addr++) {
461                 Log_Resolver(LOG_DEBUG, "translated \"%s\" to %s.",
462                                         Host, ng_ipaddr_tostr(addr));
463         }
464 #endif
465         /* Write result into pipe to parent */
466         ArrayWrite(w_fd, &IpAddrs);
467
468         array_free(&IpAddrs);
469 } /* Do_ResolveName */
470
471
472 static bool
473 register_callback( RES_STAT *s, void (*cbfunc)(int, short))
474 {
475         assert(cbfunc != NULL);
476         assert(s != NULL);
477         assert(s->resolver_fd >= 0);
478
479         if (io_setnonblock(s->resolver_fd) &&
480                 io_event_create(s->resolver_fd, IO_WANTREAD, cbfunc))
481                         return true;
482
483         Log( LOG_CRIT, "Resolver: Could not register callback function: %s!", strerror(errno));
484         close(s->resolver_fd);
485         Resolve_Init(s);
486         return false;
487 }
488
489
490 GLOBAL bool
491 Resolve_Shutdown( RES_STAT *s)
492 {
493         bool ret = false;
494
495         assert(s != NULL);
496         assert(s->resolver_fd >= 0);
497
498         if (s->resolver_fd >= 0)
499                 ret = io_close(s->resolver_fd);
500
501         Resolve_Init(s);
502         return ret;
503 }
504
505
506 /**
507  * Read result of resolver sub-process from pipe
508  */
509 GLOBAL size_t
510 Resolve_Read( RES_STAT *s, void* readbuf, size_t buflen)
511 {
512         ssize_t bytes_read;
513
514         assert(buflen > 0);
515
516         /* Read result from pipe */
517         bytes_read = read(s->resolver_fd, readbuf, buflen);
518         if (bytes_read < 0) {
519                 if (errno == EAGAIN)
520                         return 0;
521
522                 Log( LOG_CRIT, "Resolver: Can't read result: %s!", strerror(errno));
523                 bytes_read = 0;
524         }
525 #ifdef DEBUG
526         else if (bytes_read == 0)
527                 Log( LOG_DEBUG, "Resolver: Can't read result: EOF");
528 #endif
529         Resolve_Shutdown(s);
530         return (size_t)bytes_read;
531 }
532 /* -eof- */
533