* Initial work on IPv6-enabling citserver
[citadel.git] / citadel / locate_host.c
index 561595453ad1080953efb954ac8d3617d5753168..d8a2c5b919f1def7cbd3b5923d62542a49fccea0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * $Id$
  *
- * locate the originating host
+ * Functions which handle hostname/address lookups and resolution
  *
  */
 
@@ -9,21 +9,25 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
+#include <ctype.h>
 #include <signal.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 #include <limits.h>
 #include <netdb.h>
 #include <string.h>
+#include <errno.h>
+#include <libcitadel.h>
 #include "citadel.h"
 #include "server.h"
-#include "serv_extensions.h"
 #include "locate_host.h"
 #include "sysdep_decls.h"
 #include "config.h"
-#include "tools.h"
 #include "domain.h"
+#include "context.h"
+#include "ctdl_module.h"
 
 #ifdef HAVE_RESOLV_H
 #include <arpa/nameser.h>
 #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];
+       struct sockaddr_in6 clientaddr;
+       unsigned int addrlen = sizeof(clientaddr);
 
+       tbuf[0] = 0;
+       abuf[0] = 0;
 
-#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);
+       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);
 
-       if (abuf != NULL) {
-               safestrncpy(abuf, address_string, na);
+       /* Convert IPv6-mapped IPv4 addresses back to traditional dotted quad */
+       if ( (strlen(abuf) > 7) && (!strncasecmp(abuf, "::ffff:", 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;
 }
 
 
+/*
+ * 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;
@@ -107,10 +72,17 @@ int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) {
        const u_char *cend;
        const u_char *rend;
        int len;
+       char *p = NULL;
 
        /* Make our DNS query. */
        //res_init();
        answer = fixedans;
+       if (CtdlThreadCheckStop())
+       {
+               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. */
@@ -135,6 +107,13 @@ int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) {
                        return(1);
                }
        }
+       if (CtdlThreadCheckStop())
+       {
+               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';
@@ -145,6 +124,14 @@ int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) {
           nameserver we're using. */
        res_init();
        len = res_query( domain, C_IN, T_TXT, answer, PACKETSZ );
+       if (CtdlThreadCheckStop())
+       {
+               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 )
@@ -183,8 +170,8 @@ int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) {
 
        /* Iterate over any multiple answers we might have. In
           this context, it's unlikely, but anyway. */
-       rp = result;
-       rend = result + RESULT_SIZE - 1;
+       rp = (u_char *) result;
+       rend = (u_char *) result + RESULT_SIZE - 1;
        while( cp < cend && rp < rend )
        {
                a = *cp++;
@@ -204,6 +191,10 @@ int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) {
        if (txtbuf != NULL) {
                snprintf(txtbuf, txtbufsize, "%s", result);
        }
+       /* Remove nonprintable characters */
+       for (p=txtbuf; *p; ++p) {
+               if (!isprint(*p)) strcpy(p, p+1);
+       }
        if (need_to_free_answer) free(answer);
        free(result);
        return(1);
@@ -216,21 +207,17 @@ int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) {
  */
 int rbl_check_addr(struct in_addr *addr, char *message_to_spammer)
 {
-       const char *i;
        int a1, a2, a3, a4;
        char tbuf[256];
        int rbl;
        int num_rbl;
        char rbl_domains[SIZ];
        char txt_answer[1024];
+       char dotted_quad[32];
 
        strcpy(message_to_spammer, "ok");
-
-       i = (const char *) addr;
-       a1 = ((*i++) & 0xff);
-       a2 = ((*i++) & 0xff);
-       a3 = ((*i++) & 0xff);
-       a4 = ((*i++) & 0xff);
+       safestrncpy(dotted_quad, inet_ntoa(*addr), sizeof dotted_quad);
+       sscanf(dotted_quad, "%d.%d.%d.%d", &a1, &a2, &a3, &a4);
 
        /* See if we have any RBL domains configured */
        num_rbl = get_hosts(rbl_domains, "rbl");
@@ -244,8 +231,8 @@ int rbl_check_addr(struct in_addr *addr, char *message_to_spammer)
                 extract_token(&tbuf[strlen(tbuf)], rbl_domains, rbl, '|', (sizeof tbuf - strlen(tbuf)));
 
                if (rblcheck_backend(tbuf, txt_answer, sizeof txt_answer)) {
-                       sprintf(message_to_spammer, "5.7.1 %s", txt_answer);
-                       lprintf(CTDL_INFO, "RBL: %s\n", txt_answer);
+                       strcpy(message_to_spammer, txt_answer);
+                       CtdlLogPrintf(CTDL_INFO, "RBL: %s\n", txt_answer);
                        return(1);
                }
        }
@@ -257,13 +244,23 @@ int rbl_check_addr(struct in_addr *addr, char *message_to_spammer)
 /*
  * 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'
+ *
+ * PORTABILITY NOTE!  I've made my best effort to rewrite this in a portable fashion.
+ * If anyone makes changes to this function, please shout-out so we can test it to
+ * make sure it didn't break on Linux!
  */
 int rbl_check(char *message_to_spammer) {
-       struct sockaddr_in sin;
-       int len;        /* should be socklen_t but doesn't work on Macintosh */
-
-       if (!getpeername(CC->client_socket, (struct sockaddr *) &sin, (socklen_t *)&len)) {
-               return(rbl_check_addr(&sin.sin_addr, message_to_spammer));
+       int r;
+       struct sockaddr_in peer;
+       socklen_t peer_len = 0;
+
+       peer_len = sizeof(peer);
+       r = getpeername(CC->client_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);
 }