X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Flocate_host.c;h=2a7568b232e4ce1fbb05d6f05c7e88fe154ccf1c;hb=e26a8dee20d1726b4995821f717f867f50fc5659;hp=7f8e068d49a4dd8ef7be6a58c430f77e8eee82ab;hpb=2a557d1e1aeee5b4bb8d829a1cff40c37f8d4d2c;p=citadel.git diff --git a/citadel/locate_host.c b/citadel/locate_host.c index 7f8e068d4..2a7568b23 100644 --- a/citadel/locate_host.c +++ b/citadel/locate_host.c @@ -1,8 +1,21 @@ /* - * $Id$ + * Functions which handle hostname/address lookups and resolution * - * locate the originating host + * Copyright (c) 1987-2011 by the citadel.org team * + * This program is open source software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "sysdep.h" @@ -19,6 +32,7 @@ #include #include #include +#include #include #include "citadel.h" #include "server.h" @@ -38,89 +52,31 @@ #endif -/* Hacks to work around nameser.h declarations missing on OpenBSD - * see also: http://search.cpan.org/src/MIKER/Net-DNS-ToolKit-0.30/ToolKit.h +/* + * Given an open client socket, return the host name and IP address at the other end. + * (IPv4 and IPv6 compatible) */ - -#ifndef NS_INT16SZ -# ifdef INT16SZ -# define NS_INT16SZ INT16SZ -# endif -#endif - -#ifndef NS_INT32SZ -# ifdef INT32SZ -# define NS_INT32SZ INT32SZ -# endif -#endif - -#ifndef NS_GET16 -# ifdef GETSHORT -# define NS_GET16 GETSHORT -# endif -#endif - - -/***************************************************************************/ - - -void locate_host(char *tbuf, size_t n, - char *abuf, size_t na, - const struct in_addr *addr) +void locate_host(char *tbuf, size_t n, char *abuf, size_t na, int client_socket) { - struct hostent *ch; - const char *i; - char *j; - int a1, a2, a3, a4; - char address_string[SIZ]; - - -#ifdef HAVE_NONREENTRANT_NETDB - begin_critical_section(S_NETDB); -#endif - - i = (const char *) addr; - a1 = ((*i++) & 0xff); - a2 = ((*i++) & 0xff); - a3 = ((*i++) & 0xff); - a4 = ((*i++) & 0xff); - sprintf(address_string, "%d.%d.%d.%d", a1, a2, a3, a4); - - if (abuf != NULL) { - safestrncpy(abuf, address_string, na); + struct sockaddr_in6 clientaddr; + unsigned int addrlen = sizeof(clientaddr); + + tbuf[0] = 0; + abuf[0] = 0; + + getpeername(client_socket, (struct sockaddr *)&clientaddr, &addrlen); + getnameinfo((struct sockaddr *)&clientaddr, addrlen, tbuf, n, NULL, 0, 0); + getnameinfo((struct sockaddr *)&clientaddr, addrlen, abuf, na, NULL, 0, NI_NUMERICHOST); + + /* Convert IPv6-mapped IPv4 addresses back to traditional dotted quad. + * + * Other code here, such as the RBL check, will expect IPv4 addresses to be represented + * as dotted-quad, even if they come in over a hybrid IPv6/IPv4 socket. + */ + if ( (strlen(abuf) > 7) && (!strncasecmp(abuf, "::ffff:", 7)) ) { + if (!strcmp(abuf, tbuf)) strcpy(tbuf, &tbuf[7]); + strcpy(abuf, &abuf[7]); } - - if ((ch = gethostbyaddr((const char *) addr, - sizeof(*addr), AF_INET)) == NULL) { -bad_dns: - safestrncpy(tbuf, address_string, n); - goto end; /* because we might need to end the critical - section */ - } - /* check if the forward DNS agrees; if not, they're spoofing */ - j = strdup(ch->h_name); - ch = gethostbyname(j); - free(j); - if (ch == NULL) - goto bad_dns; - - /* check address for consistency */ - for (; *ch->h_addr_list; ch->h_addr_list++) - if (!memcmp(*ch->h_addr_list, addr, - sizeof *addr)) { - safestrncpy(tbuf, ch->h_name, 63); - goto end; - } - goto bad_dns; /* they were spoofing. report a numeric IP - address. */ - - end: - -#ifdef HAVE_NONREENTRANT_NETDB - end_critical_section(S_NETDB); -#endif - - tbuf[63] = 0; } @@ -140,20 +96,26 @@ int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) { const u_char *rend; int len; char *p = NULL; + static int res_initted = 0; + + if (!res_initted) { /* only have to do this once */ + res_init(); + res_initted = 1; + } /* Make our DNS query. */ - //res_init(); answer = fixedans; - if (CtdlThreadCheckStop()) + if (server_shutting_down) { - if (txtbuf != NULL) + if (txtbuf != NULL) { snprintf(txtbuf, txtbufsize, "System shutting down"); + } return (1); } - len = res_query( domain, C_IN, T_A, answer, PACKETSZ ); + len = res_query(domain, C_IN, T_A, answer, PACKETSZ); /* Was there a problem? If so, the domain doesn't exist. */ - if( len == -1 ) { + if (len == -1) { if (txtbuf != NULL) { strcpy(txtbuf, ""); } @@ -162,9 +124,9 @@ int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) { if( len > PACKETSZ ) { - answer = malloc( len ); + answer = malloc(len); need_to_free_answer = 1; - len = res_query( domain, C_IN, T_A, answer, len ); + len = res_query(domain, C_IN, T_A, answer, len); if( len == -1 ) { if (txtbuf != NULL) { snprintf(txtbuf, txtbufsize, @@ -174,7 +136,7 @@ int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) { return(1); } } - if (CtdlThreadCheckStop()) + if (server_shutting_down) { if (txtbuf != NULL) snprintf(txtbuf, txtbufsize, "System shutting down"); @@ -182,26 +144,27 @@ int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) { return (1); } - result = ( char * )malloc( RESULT_SIZE ); + result = (char *) malloc(RESULT_SIZE); result[ 0 ] = '\0'; /* Make another DNS query for textual data; this shouldn't - be a performance hit, since it'll now be cached at the - nameserver we're using. */ - res_init(); - len = res_query( domain, C_IN, T_TXT, answer, PACKETSZ ); - if (CtdlThreadCheckStop()) + * be a performance hit, since it'll now be cached at the + * nameserver we're using. + */ + len = res_query(domain, C_IN, T_TXT, answer, PACKETSZ); + if (server_shutting_down) { - if (txtbuf != NULL) + if (txtbuf != NULL) { snprintf(txtbuf, txtbufsize, "System shutting down"); + } if (need_to_free_answer) free(answer); free(result); return (1); } /* Just in case there's no TXT record... */ - if( len == -1 ) + if (len ==(-1)) { if (txtbuf != NULL) { snprintf(txtbuf, txtbufsize, @@ -222,32 +185,32 @@ int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) { } /* This seems to be a bit of magic data that we need to - skip. I wish there were good online documentation - for programming for libresolv, so I'd know what I'm - skipping here. Anyone reading this, feel free to - enlighten me. */ + * skip. I wish there were good online documentation + * for programming for libresolv, so I'd know what I'm + * skipping here. Anyone reading this, feel free to + * enlighten me. + */ cp += 1 + NS_INT16SZ + NS_INT32SZ; /* Skip the type, class and ttl. */ - cp += ( NS_INT16SZ * 2 ) + NS_INT32SZ; + cp += (NS_INT16SZ * 2) + NS_INT32SZ; /* Get the length and end of the buffer. */ - NS_GET16( c, cp ); + NS_GET16(c, cp); cend = cp + c; /* Iterate over any multiple answers we might have. In - this context, it's unlikely, but anyway. */ + * this context, it's unlikely, but anyway. + */ rp = (u_char *) result; rend = (u_char *) result + RESULT_SIZE - 1; - while( cp < cend && rp < rend ) + while (cp < cend && rp < rend) { a = *cp++; if( a != 0 ) - for( b = a; b > 0 && cp < cend && rp < rend; - b-- ) + for (b = a; b > 0 && cp < cend && rp < rend; b--) { - if( *cp == '\n' || *cp == '"' || - *cp == '\\' ) + if (*cp == '\n' || *cp == '"' || *cp == '\\') { *rp++ = '\\'; } @@ -269,22 +232,72 @@ int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) { /* - * Check to see if a host is on some sort of spam list (RBL) + * Check to see if the client host is on some sort of spam list (RBL) * If spammer, returns nonzero and places reason in 'message_to_spammer' */ -int rbl_check_addr(struct in_addr *addr, char *message_to_spammer) +int rbl_check(char *message_to_spammer) { - int a1, a2, a3, a4; - char tbuf[256]; + char tbuf[256] = ""; + int suffix_pos = 0; int rbl; int num_rbl; char rbl_domains[SIZ]; char txt_answer[1024]; - char dotted_quad[32]; strcpy(message_to_spammer, "ok"); - safestrncpy(dotted_quad, inet_ntoa(*addr), sizeof dotted_quad); - sscanf(dotted_quad, "%d.%d.%d.%d", &a1, &a2, &a3, &a4); + + if ((strchr(CC->cs_addr, '.')) && (!strchr(CC->cs_addr, ':'))) { + int a1, a2, a3, a4; + + sscanf(CC->cs_addr, "%d.%d.%d.%d", &a1, &a2, &a3, &a4); + snprintf(tbuf, sizeof tbuf, "%d.%d.%d.%d.", a4, a3, a2, a1); + suffix_pos = strlen(tbuf); + } + else if ((!strchr(CC->cs_addr, '.')) && (strchr(CC->cs_addr, ':'))) { + int num_colons = 0; + int i = 0; + char workbuf[sizeof tbuf]; + char *ptr; + + /* tedious code to expand and reverse an IPv6 address */ + safestrncpy(tbuf, CC->cs_addr, sizeof tbuf); + num_colons = haschar(tbuf, ':'); + if ((num_colons < 2) || (num_colons > 7)) return(0); /* badly formed address */ + + /* expand the "::" shorthand */ + while (num_colons < 7) { + ptr = strstr(tbuf, "::"); + if (!ptr) return(0); /* badly formed address */ + ++ptr; + strcpy(workbuf, ptr); + strcpy(ptr, ":"); + strcat(ptr, workbuf); + ++num_colons; + } + + /* expand to 32 hex characters with no colons */ + strcpy(workbuf, tbuf); + strcpy(tbuf, "00000000000000000000000000000000"); + for (i=0; i<8; ++i) { + char tokbuf[5]; + extract_token(tokbuf, workbuf, i, ':', sizeof tokbuf); + + memcpy(&tbuf[ (i*4) + (4-strlen(tokbuf)) ], tokbuf, strlen(tokbuf) ); + } + if (strlen(tbuf) != 32) return(0); + + /* now reverse it and add dots */ + strcpy(workbuf, tbuf); + for (i=0; i<32; ++i) { + tbuf[i*2] = workbuf[31-i]; + tbuf[(i*2)+1] = '.'; + } + tbuf[64] = 0; + suffix_pos = 64; + } + else { + return(0); /* unknown address format */ + } /* See if we have any RBL domains configured */ num_rbl = get_hosts(rbl_domains, "rbl"); @@ -292,14 +305,11 @@ int rbl_check_addr(struct in_addr *addr, char *message_to_spammer) /* Try all configured RBL's */ for (rbl=0; rblclient_socket, &peer, &peer_len); - if (r == 0) { - return(rbl_check_addr(&peer.sin_addr, message_to_spammer)); - } - else { - CtdlLogPrintf(CTDL_INFO, "RBL getpeername() failed: %s\n", strerror(errno)); - } - return(0); -} - /* * Convert a host name to a dotted quad address. * Returns zero on success or nonzero on failure. + * + * FIXME this is obviously not IPv6 compatible. */ int hostname_to_dotted_quad(char *addr, char *host) { struct hostent *ch;