X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Flocate_host.c;h=26c844024515bd2c354a4d46ff358c1c54fad74a;hb=7c94d5bb68f1c448770d31205ec5ae12bd8a02ed;hp=9ca4e0e8ca2a0a1ab94bfddbbb2501127ab56645;hpb=b6d0c160695778081b012f34f3beed78bd7cd36d;p=citadel.git diff --git a/citadel/locate_host.c b/citadel/locate_host.c index 9ca4e0e8c..26c844024 100644 --- a/citadel/locate_host.c +++ b/citadel/locate_host.c @@ -1,50 +1,372 @@ /* - * locate the originating host - * $Id$ + * Functions which handle hostname/address lookups and resolution + * + * 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, version 3. + * + * 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. */ -#include -#include +#include "sysdep.h" +#include #include -#include -#include -#include -#include -#include +#include +#include #include -#include -#include -#include "sysdep.h" -#include "citadel.h" -#include "server.h" +#include +#include + +#include + + +#include "context.h" +#ifdef HAVE_RESOLV_H +#include +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include +#endif +#include +#endif + +#include "domain.h" #include "locate_host.h" -#include "config.h" -void locate_host(char *tbuf) +/** START:some missing macros on OpenBSD 3.9 */ +#ifndef NS_CMPRSFLGS +#define NS_CMPRSFLGS 0xc0 +#endif +#if !defined(NS_MAXCDNAME) && defined (MAXCDNAME) +#define NS_MAXCDNAME MAXCDNAME +#endif +#if !defined(NS_INT16SZ) && defined(INT16SZ) +#define NS_INT16SZ INT16SZ +#define NS_INT32SZ INT32SZ +#endif +#ifndef NS_GET16 +# define NS_GET16 GETSHORT +#endif + +/** END:some missing macros on OpenBSD 3.9 */ + +/* + * Given an open client socket, return the host name and IP address at the other end. + * (IPv4 and IPv6 compatible) + */ +void locate_host(char *tbuf, size_t n, char *abuf, size_t na, int client_socket) +{ + 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]); + } +} + + +/* + * RBL check written by Edward S. Marshall [http://rblcheck.sourceforge.net] + */ +#define RESULT_SIZE 4096 /* What is the longest result text we support? */ +int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) { + int a, b, c; + char *result = NULL; + u_char fixedans[ PACKETSZ ]; + u_char *answer; + int need_to_free_answer = 0; + const u_char *cp; + u_char *rp; + const u_char *cend; + 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. */ + answer = fixedans; + if (server_shutting_down) + { + if (txtbuf != NULL) { + snprintf(txtbuf, txtbufsize, "System shutting down"); + } + return (1); + } + 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 (txtbuf != NULL) { + strcpy(txtbuf, ""); + } + return(0); + } + + if( len > PACKETSZ ) + { + answer = malloc(len); + need_to_free_answer = 1; + len = res_query(domain, C_IN, T_A, answer, len); + if( len == -1 ) { + if (txtbuf != NULL) { + snprintf(txtbuf, txtbufsize, + "Message rejected due to known spammer source IP address"); + } + if (need_to_free_answer) free(answer); + return(1); + } + } + if (server_shutting_down) + { + if (txtbuf != NULL) + snprintf(txtbuf, txtbufsize, "System shutting down"); + if (need_to_free_answer) free(answer); + return (1); + } + + 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. + */ + len = res_query(domain, C_IN, T_TXT, answer, PACKETSZ); + if (server_shutting_down) + { + 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 (txtbuf != NULL) { + snprintf(txtbuf, txtbufsize, + "Message rejected due to known spammer source IP address"); + } + if (need_to_free_answer) free(answer); + free(result); + return(1); + } + + /* Skip the header and the address we queried. */ + cp = answer + sizeof( HEADER ); + while( *cp != '\0' ) + { + a = *cp++; + while( a-- ) + cp++; + } + + /* 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. + */ + cp += 1 + NS_INT16SZ + NS_INT32SZ; + + /* Skip the type, class and ttl. */ + cp += (NS_INT16SZ * 2) + NS_INT32SZ; + + /* Get the length and end of the buffer. */ + NS_GET16(c, cp); + cend = cp + c; + + /* Iterate over any multiple answers we might have. In + * this context, it's unlikely, but anyway. + */ + rp = (u_char *) result; + rend = (u_char *) result + RESULT_SIZE - 1; + while (cp < cend && rp < rend) + { + a = *cp++; + if( a != 0 ) + for (b = a; b > 0 && cp < cend && rp < rend; b--) + { + if (*cp == '\n' || *cp == '"' || *cp == '\\') + { + *rp++ = '\\'; + } + *rp++ = *cp++; + } + } + *rp = '\0'; + if (txtbuf != NULL) { + long len; + len = snprintf(txtbuf, txtbufsize, "%s", result); + + /* Remove nonprintable characters */ + for (p = txtbuf; *p != '\0'; p++) { + if (!isprint(*p)) { + memmove (p, + p + 1, + len - (p - txtbuf) - 1); + } + } + } + if (need_to_free_answer) free(answer); + free(result); + return(1); +} + + +/* + * 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(char *message_to_spammer) { - struct sockaddr_in cs; - struct hostent * ch; - int len; - char *i; - int a1,a2,a3,a4; - - len = sizeof(cs); - if (getpeername(CC->client_socket, (struct sockaddr *)&cs,&len) < 0){ - strcpy(tbuf,config.c_fqdn); - return; - } - - if((ch = gethostbyaddr((char *) &cs.sin_addr, sizeof(cs.sin_addr), - AF_INET)) == NULL) { - i = (char *) &cs.sin_addr; - a1 = ((*i++)&0xff); - a2 = ((*i++)&0xff); - a3 = ((*i++)&0xff); - a4 = ((*i++)&0xff); - sprintf(tbuf,"%d.%d.%d.%d",a1,a2,a3,a4); - return; - } - - strncpy(tbuf,ch->h_name, 24); - tbuf[24] = 0; + char tbuf[256] = ""; + int suffix_pos = 0; + int rbl; + int rc; + int num_rbl; + char rbl_domains[SIZ]; + char txt_answer[1024]; + struct timeval tx_start; + struct timeval tx_finish; + + rc = 0; + strcpy(message_to_spammer, "ok"); + gettimeofday(&tx_start, NULL); /* start a stopwatch for performance timing */ + + 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)) + goto finish_rbl; /* badly formed address */ + + /* expand the "::" shorthand */ + while (num_colons < 7) { + ptr = strstr(tbuf, "::"); + if (!ptr) + goto finish_rbl; /* 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) + goto finish_rbl; + + /* 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 { + goto finish_rbl; /* unknown address format */ + } + + /* See if we have any RBL domains configured */ + num_rbl = get_hosts(rbl_domains, "rbl"); + if (num_rbl < 1) + { + goto finish_rbl; + } + + /* Try all configured RBL's */ + for (rbl=0; rblh_addr_list[0]; + a1 = ((*i++) & 0xff); + a2 = ((*i++) & 0xff); + a3 = ((*i++) & 0xff); + a4 = ((*i++) & 0xff); + sprintf(addr, "%d.%d.%d.%d", a1, a2, a3, a4); + return(0); +}