Annotation of ircnowd/src/ngircd/resolve.c, Revision 1.1
1.1 ! tomglok 1: /*
! 2: * ngIRCd -- The Next Generation IRC Daemon
! 3: * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors.
! 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:
! 12: #define RESOLVER_TIMEOUT (Conf_PongTimeout*3)/4
! 13:
! 14: #include "portab.h"
! 15:
! 16: /**
! 17: * @file
! 18: * Asynchronous resolver
! 19: */
! 20:
! 21: #include <assert.h>
! 22: #include <errno.h>
! 23: #include <stdlib.h>
! 24: #include <string.h>
! 25: #include <unistd.h>
! 26: #include <sys/stat.h>
! 27: #include <sys/types.h>
! 28: #include <sys/socket.h>
! 29: #include <netinet/in.h>
! 30: #include <netdb.h>
! 31:
! 32: #ifdef IDENTAUTH
! 33: #ifdef HAVE_IDENT_H
! 34: #include <ident.h>
! 35: #endif
! 36: #endif
! 37:
! 38: #include "conn.h"
! 39: #include "conf.h"
! 40: #include "log.h"
! 41: #include "ng_ipaddr.h"
! 42:
! 43: #include "resolve.h"
! 44:
! 45: static void Do_ResolveAddr PARAMS(( const ng_ipaddr_t *Addr, int Sock, int w_fd ));
! 46: static void Do_ResolveName PARAMS(( const char *Host, int w_fd ));
! 47:
! 48: #ifdef WANT_IPV6
! 49: extern bool Conf_ConnectIPv4;
! 50: extern bool Conf_ConnectIPv6;
! 51: #endif
! 52:
! 53:
! 54: /**
! 55: * Resolve IP (asynchronous!).
! 56: */
! 57: GLOBAL bool
! 58: Resolve_Addr(PROC_STAT * s, const ng_ipaddr_t *Addr, int identsock,
! 59: void (*cbfunc) (int, short))
! 60: {
! 61: int pipefd[2];
! 62: pid_t pid;
! 63:
! 64: assert(s != NULL);
! 65:
! 66: pid = Proc_Fork(s, pipefd, cbfunc, RESOLVER_TIMEOUT);
! 67: if (pid > 0) {
! 68: LogDebug("Resolver for %s created (PID %d).", ng_ipaddr_tostr(Addr), pid);
! 69: return true;
! 70: } else if( pid == 0 ) {
! 71: /* Sub process */
! 72: Log_Init_Subprocess("Resolver");
! 73: Conn_CloseAllSockets(identsock);
! 74: Do_ResolveAddr(Addr, identsock, pipefd[1]);
! 75: Log_Exit_Subprocess("Resolver");
! 76: exit(0);
! 77: }
! 78: return false;
! 79: } /* Resolve_Addr */
! 80:
! 81:
! 82: /**
! 83: * Resolve hostname (asynchronous!).
! 84: */
! 85: GLOBAL bool
! 86: Resolve_Name( PROC_STAT *s, const char *Host, void (*cbfunc)(int, short))
! 87: {
! 88: int pipefd[2];
! 89: pid_t pid;
! 90:
! 91: assert(s != NULL);
! 92:
! 93: pid = Proc_Fork(s, pipefd, cbfunc, RESOLVER_TIMEOUT);
! 94: if (pid > 0) {
! 95: /* Main process */
! 96: #ifdef DEBUG
! 97: Log( LOG_DEBUG, "Resolver for \"%s\" created (PID %d).", Host, pid );
! 98: #endif
! 99: return true;
! 100: } else if( pid == 0 ) {
! 101: /* Sub process */
! 102: Log_Init_Subprocess("Resolver");
! 103: Conn_CloseAllSockets(NONE);
! 104: Do_ResolveName(Host, pipefd[1]);
! 105: Log_Exit_Subprocess("Resolver");
! 106: exit(0);
! 107: }
! 108: return false;
! 109: } /* Resolve_Name */
! 110:
! 111: #if !defined(HAVE_WORKING_GETADDRINFO) || !defined(HAVE_GETNAMEINFO)
! 112: #ifdef h_errno
! 113: static char *
! 114: Get_Error( int H_Error )
! 115: {
! 116: /* Get error message for H_Error */
! 117: switch( H_Error ) {
! 118: case HOST_NOT_FOUND:
! 119: return "host not found";
! 120: case NO_DATA:
! 121: return "name valid but no IP address defined";
! 122: case NO_RECOVERY:
! 123: return "name server error";
! 124: case TRY_AGAIN:
! 125: return "name server temporary not available";
! 126: }
! 127: return "unknown error";
! 128: }
! 129: #endif
! 130: #endif
! 131:
! 132:
! 133: /* Do "IDENT" (aka "AUTH") lookup and append result to resolved_addr array */
! 134: static void
! 135: Do_IdentQuery(int identsock, array *resolved_addr)
! 136: {
! 137: #ifdef IDENTAUTH
! 138: char *res;
! 139:
! 140: if (identsock < 0)
! 141: return;
! 142:
! 143: #ifdef DEBUG
! 144: Log_Subprocess(LOG_DEBUG, "Doing IDENT lookup on socket %d ...",
! 145: identsock);
! 146: #endif
! 147: res = ident_id( identsock, 10 );
! 148: #ifdef DEBUG
! 149: Log_Subprocess(LOG_DEBUG, "Ok, IDENT lookup on socket %d done: \"%s\"",
! 150: identsock, res ? res : "(NULL)");
! 151: #endif
! 152: if (!res) /* no result */
! 153: return;
! 154: if (!array_cats(resolved_addr, res))
! 155: Log_Subprocess(LOG_WARNING,
! 156: "Resolver: Cannot copy IDENT result: %s!",
! 157: strerror(errno));
! 158:
! 159: free(res);
! 160: #else
! 161: (void) identsock;
! 162: (void) resolved_addr;
! 163: #endif
! 164: }
! 165:
! 166:
! 167: /**
! 168: * perform reverse DNS lookup and put result string into resbuf.
! 169: * If no hostname could be obtained, this function stores the string representation of
! 170: * the IP address in resbuf and returns false.
! 171: * @param IpAddr ip address to resolve
! 172: * @param resbuf result buffer to store DNS name/string representation of ip address
! 173: * @param reslen size of result buffer (must be >= NGT_INET_ADDRSTRLEN)
! 174: * @return true if reverse lookup successful, false otherwise
! 175: */
! 176: static bool
! 177: ReverseLookup(const ng_ipaddr_t *IpAddr, char *resbuf, size_t reslen)
! 178: {
! 179: char tmp_ip_str[NG_INET_ADDRSTRLEN];
! 180: const char *errmsg;
! 181: #ifdef HAVE_GETNAMEINFO
! 182: static const char funcname[]="getnameinfo";
! 183: int res;
! 184:
! 185: *resbuf = 0;
! 186:
! 187: res = getnameinfo((struct sockaddr *) IpAddr, ng_ipaddr_salen(IpAddr),
! 188: resbuf, (socklen_t)reslen, NULL, 0, NI_NAMEREQD);
! 189: if (res == 0)
! 190: return true;
! 191:
! 192: if (res == EAI_SYSTEM)
! 193: errmsg = strerror(errno);
! 194: else
! 195: errmsg = gai_strerror(res);
! 196: #else
! 197: const struct sockaddr_in *Addr = (const struct sockaddr_in *) IpAddr;
! 198: struct hostent *h;
! 199: static const char funcname[]="gethostbyaddr";
! 200:
! 201: h = gethostbyaddr((char *)&Addr->sin_addr, sizeof(Addr->sin_addr), AF_INET);
! 202: if (h) {
! 203: if (strlcpy(resbuf, h->h_name, reslen) < reslen)
! 204: return true;
! 205: errmsg = "hostname too long";
! 206: } else {
! 207: # ifdef h_errno
! 208: errmsg = Get_Error(h_errno);
! 209: # else
! 210: errmsg = "unknown error";
! 211: # endif /* h_errno */
! 212: }
! 213: #endif /* HAVE_GETNAMEINFO */
! 214:
! 215: assert(errmsg);
! 216: assert(reslen >= NG_INET_ADDRSTRLEN);
! 217: ng_ipaddr_tostr_r(IpAddr, tmp_ip_str);
! 218:
! 219: Log_Subprocess(LOG_WARNING, "Can't resolve address \"%s\": %s [%s].",
! 220: tmp_ip_str, errmsg, funcname);
! 221: strlcpy(resbuf, tmp_ip_str, reslen);
! 222: return false;
! 223: }
! 224:
! 225:
! 226: /**
! 227: * perform DNS lookup of given host name and fill IpAddr with a list of
! 228: * ip addresses associated with that name.
! 229: * ip addresses found are stored in the "array *IpAddr" argument (type ng_ipaddr_t)
! 230: * @param hostname The domain name to look up.
! 231: * @param IpAddr pointer to empty and initialized array to store results
! 232: * @return true if lookup successful, false if domain name not found
! 233: */
! 234: static bool
! 235: #ifdef HAVE_WORKING_GETADDRINFO
! 236: ForwardLookup(const char *hostname, array *IpAddr, int af)
! 237: #else
! 238: ForwardLookup(const char *hostname, array *IpAddr, UNUSED int af)
! 239: #endif
! 240: {
! 241: ng_ipaddr_t addr;
! 242:
! 243: #ifdef HAVE_WORKING_GETADDRINFO
! 244: int res;
! 245: struct addrinfo *a, *ai_results;
! 246: static struct addrinfo hints;
! 247:
! 248: hints.ai_socktype = SOCK_STREAM;
! 249: hints.ai_protocol = IPPROTO_TCP;
! 250: hints.ai_family = af;
! 251:
! 252: memset(&addr, 0, sizeof(addr));
! 253:
! 254: res = getaddrinfo(hostname, NULL, &hints, &ai_results);
! 255: switch (res) {
! 256: case 0: break;
! 257: case EAI_SYSTEM:
! 258: Log_Subprocess(LOG_WARNING, "Can't resolve \"%s\": %s", hostname, strerror(errno));
! 259: return false;
! 260: default:
! 261: Log_Subprocess(LOG_WARNING, "Can't resolve \"%s\": %s", hostname, gai_strerror(res));
! 262: return false;
! 263: }
! 264:
! 265: for (a = ai_results; a != NULL; a = a->ai_next) {
! 266: assert((size_t)a->ai_addrlen <= sizeof(addr));
! 267:
! 268: if ((size_t)a->ai_addrlen > sizeof(addr))
! 269: continue;
! 270:
! 271: memcpy(&addr, a->ai_addr, a->ai_addrlen);
! 272:
! 273: if (!array_catb(IpAddr, (char *)&addr, sizeof(addr)))
! 274: break;
! 275: }
! 276:
! 277: freeaddrinfo(ai_results);
! 278: return a == NULL;
! 279: #else
! 280: struct hostent *h = gethostbyname(hostname);
! 281:
! 282: if (!h) {
! 283: #ifdef h_errno
! 284: Log_Subprocess(LOG_WARNING, "Can't resolve \"%s\": %s",
! 285: hostname, Get_Error(h_errno));
! 286: #else
! 287: Log_Subprocess(LOG_WARNING, "Can't resolve \"%s\"", hostname);
! 288: #endif
! 289: return false;
! 290: }
! 291: memset(&addr, 0, sizeof(addr));
! 292:
! 293: addr.sin4.sin_family = AF_INET;
! 294: memcpy(&addr.sin4.sin_addr, h->h_addr, sizeof(struct in_addr));
! 295:
! 296: return array_copyb(IpAddr, (char *)&addr, sizeof(addr));
! 297: #endif /* HAVE_GETADDRINFO */
! 298: }
! 299:
! 300:
! 301: static bool
! 302: Addr_in_list(const array *resolved_addr, const ng_ipaddr_t *Addr)
! 303: {
! 304: char tmp_ip_str[NG_INET_ADDRSTRLEN];
! 305: const ng_ipaddr_t *tmpAddrs = array_start(resolved_addr);
! 306: size_t len = array_length(resolved_addr, sizeof(*tmpAddrs));
! 307:
! 308: assert(len > 0);
! 309: assert(tmpAddrs);
! 310:
! 311: while (len > 0) {
! 312: if (ng_ipaddr_ipequal(Addr, tmpAddrs))
! 313: return true;
! 314: tmpAddrs++;
! 315: len--;
! 316: }
! 317: /* failed; print list of addresses */
! 318: ng_ipaddr_tostr_r(Addr, tmp_ip_str);
! 319: len = array_length(resolved_addr, sizeof(*tmpAddrs));
! 320: tmpAddrs = array_start(resolved_addr);
! 321:
! 322: while (len > 0) {
! 323: Log_Subprocess(LOG_WARNING, "Address mismatch: %s != %s",
! 324: tmp_ip_str, ng_ipaddr_tostr(tmpAddrs));
! 325: tmpAddrs++;
! 326: len--;
! 327: }
! 328:
! 329: return false;
! 330: }
! 331:
! 332:
! 333: static void
! 334: Log_Forgery_NoIP(const char *ip, const char *host)
! 335: {
! 336: Log_Subprocess(LOG_WARNING,
! 337: "Possible forgery: %s resolved to \"%s\", which has no IP address!",
! 338: ip, host);
! 339: }
! 340:
! 341: static void
! 342: Log_Forgery_WrongIP(const char *ip, const char *host)
! 343: {
! 344: Log_Subprocess(LOG_WARNING,
! 345: "Possible forgery: %s resolved to \"%s\", which points to a different address!",
! 346: ip, host);
! 347: }
! 348:
! 349:
! 350: static void
! 351: ArrayWrite(int fd, const array *a)
! 352: {
! 353: size_t len = array_bytes(a);
! 354: const char *data = array_start(a);
! 355:
! 356: assert(data);
! 357:
! 358: if( (size_t)write(fd, data, len) != len )
! 359: Log_Subprocess( LOG_CRIT, "Resolver: Can't write to parent: %s!",
! 360: strerror(errno));
! 361: }
! 362:
! 363:
! 364: static void
! 365: Do_ResolveAddr(const ng_ipaddr_t *Addr, int identsock, int w_fd)
! 366: {
! 367: /* Resolver sub-process: resolve IP address and write result into
! 368: * pipe to parent. */
! 369: char hostname[CLIENT_HOST_LEN];
! 370: char tmp_ip_str[NG_INET_ADDRSTRLEN];
! 371: size_t len;
! 372: array resolved_addr;
! 373:
! 374: array_init(&resolved_addr);
! 375: ng_ipaddr_tostr_r(Addr, tmp_ip_str);
! 376: #ifdef DEBUG
! 377: Log_Subprocess(LOG_DEBUG, "Now resolving %s ...", tmp_ip_str);
! 378: #endif
! 379: if (!ReverseLookup(Addr, hostname, sizeof(hostname)))
! 380: goto dns_done;
! 381:
! 382: if (ForwardLookup(hostname, &resolved_addr, ng_ipaddr_af(Addr))) {
! 383: if (!Addr_in_list(&resolved_addr, Addr)) {
! 384: Log_Forgery_WrongIP(tmp_ip_str, hostname);
! 385: strlcpy(hostname, tmp_ip_str, sizeof(hostname));
! 386: }
! 387: } else {
! 388: Log_Forgery_NoIP(tmp_ip_str, hostname);
! 389: strlcpy(hostname, tmp_ip_str, sizeof(hostname));
! 390: }
! 391: #ifdef DEBUG
! 392: Log_Subprocess(LOG_DEBUG, "Ok, translated %s to \"%s\".", tmp_ip_str, hostname);
! 393: #endif
! 394: dns_done:
! 395: len = strlen(hostname);
! 396: hostname[len] = '\n';
! 397: if (!array_copyb(&resolved_addr, hostname, ++len)) {
! 398: Log_Subprocess(LOG_CRIT,
! 399: "Resolver: Can't copy resolved name: %s!",
! 400: strerror(errno));
! 401: array_free(&resolved_addr);
! 402: return;
! 403: }
! 404:
! 405: Do_IdentQuery(identsock, &resolved_addr);
! 406:
! 407: ArrayWrite(w_fd, &resolved_addr);
! 408:
! 409: array_free(&resolved_addr);
! 410: } /* Do_ResolveAddr */
! 411:
! 412:
! 413: static void
! 414: Do_ResolveName( const char *Host, int w_fd )
! 415: {
! 416: /* Resolver sub-process: resolve name and write result into pipe
! 417: * to parent. */
! 418: array IpAddrs;
! 419: int af;
! 420: #ifdef DEBUG
! 421: ng_ipaddr_t *addr;
! 422: size_t len;
! 423: #endif
! 424: Log_Subprocess(LOG_DEBUG, "Now resolving \"%s\" ...", Host);
! 425:
! 426: array_init(&IpAddrs);
! 427:
! 428: #ifdef WANT_IPV6
! 429: af = AF_UNSPEC;
! 430: assert(Conf_ConnectIPv6 || Conf_ConnectIPv4);
! 431:
! 432: if (!Conf_ConnectIPv6)
! 433: af = AF_INET;
! 434: if (!Conf_ConnectIPv4)
! 435: af = AF_INET6;
! 436: #else
! 437: af = AF_INET;
! 438: #endif
! 439: if (!ForwardLookup(Host, &IpAddrs, af)) {
! 440: close(w_fd);
! 441: return;
! 442: }
! 443: #ifdef DEBUG
! 444: len = array_length(&IpAddrs, sizeof(*addr));
! 445: assert(len > 0);
! 446: addr = array_start(&IpAddrs);
! 447: assert(addr);
! 448: for (; len > 0; --len,addr++) {
! 449: Log_Subprocess(LOG_DEBUG, "translated \"%s\" to %s.",
! 450: Host, ng_ipaddr_tostr(addr));
! 451: }
! 452: #endif
! 453: /* Write result into pipe to parent */
! 454: ArrayWrite(w_fd, &IpAddrs);
! 455:
! 456: array_free(&IpAddrs);
! 457: } /* Do_ResolveName */
! 458:
! 459:
! 460: /* -eof- */
CVSweb