"Fossies" - the Fresh Open Source Software Archive

Member "citadel/locate_host.c" (5 Jun 2021, 9200 Bytes) of package /linux/www/citadel.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "locate_host.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 8.24_vs_9.01.

    1 /*
    2  * Functions which handle hostname/address lookups and resolution
    3  *
    4  * Copyright (c) 1987-2019 by the citadel.org team
    5  *
    6  * This program is open source software; you can redistribute it and/or modify
    7  * it under the terms of the GNU General Public License, version 3.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  */
   14 
   15 #include "sysdep.h"
   16 #include <string.h>
   17 #include <stdio.h>
   18 #include <syslog.h>
   19 #include <ctype.h>
   20 #include <netdb.h>
   21 #include <netinet/in.h>
   22 #include <arpa/inet.h>
   23 #include <libcitadel.h>
   24 
   25 #include "context.h"
   26 #ifdef HAVE_RESOLV_H
   27 #include <arpa/nameser.h>
   28 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
   29 #include <arpa/nameser_compat.h>
   30 #endif
   31 #include <resolv.h>
   32 #endif
   33 
   34 #include "domain.h"
   35 #include "locate_host.h"
   36 
   37 /* START: some missing macros on OpenBSD 3.9 */
   38 #ifndef NS_CMPRSFLGS
   39 #define NS_CMPRSFLGS   0xc0
   40 #endif
   41 #if !defined(NS_MAXCDNAME) && defined (MAXCDNAME)
   42 #define NS_MAXCDNAME MAXCDNAME
   43 #endif
   44 #if !defined(NS_INT16SZ) && defined(INT16SZ)
   45 #define NS_INT16SZ INT16SZ
   46 #define NS_INT32SZ INT32SZ
   47 #endif
   48 #ifndef NS_GET16
   49 #  define NS_GET16 GETSHORT
   50 #endif
   51 /* END: some missing macros on OpenBSD 3.9 */
   52 
   53 
   54 /*
   55  * Given an open client socket, return the host name and IP address at the other end.
   56  * (IPv4 and IPv6 compatible)
   57  */
   58 void locate_host(char *tbuf, size_t n, char *abuf, size_t na, int client_socket)
   59 {
   60     struct sockaddr_in6 clientaddr;
   61     unsigned int addrlen = sizeof(clientaddr);
   62 
   63     tbuf[0] = 0;
   64     abuf[0] = 0;
   65 
   66     getpeername(client_socket, (struct sockaddr *)&clientaddr, &addrlen);
   67     getnameinfo((struct sockaddr *)&clientaddr, addrlen, tbuf, n, NULL, 0, 0);
   68     getnameinfo((struct sockaddr *)&clientaddr, addrlen, abuf, na, NULL, 0, NI_NUMERICHOST);
   69 
   70     /* Convert IPv6-mapped IPv4 addresses back to traditional dotted quad.
   71      *
   72      * Other code here, such as the RBL check, will expect IPv4 addresses to be represented
   73      * as dotted-quad, even if they come in over a hybrid IPv6/IPv4 socket.
   74      */
   75     if ( (strlen(abuf) > 7) && (!strncasecmp(abuf, "::ffff:", 7)) ) {
   76         if (!strcmp(abuf, tbuf)) strcpy(tbuf, &tbuf[7]);
   77         strcpy(abuf, &abuf[7]);
   78     }
   79 }
   80 
   81 
   82 /*
   83  * RBL check written by Edward S. Marshall [http://rblcheck.sourceforge.net]
   84  */
   85 #define RESULT_SIZE 4096 /* What is the longest result text we support? */
   86 int rblcheck_backend(char *domain, char *txtbuf, int txtbufsize) {
   87     int a, b, c;
   88     char *result = NULL;
   89     u_char fixedans[ PACKETSZ ];
   90     u_char *answer;
   91     int need_to_free_answer = 0;
   92     const u_char *cp;
   93     u_char *rp;
   94     const u_char *cend;
   95     const u_char *rend;
   96     int len;
   97     char *p = NULL;
   98     static int res_initted = 0;
   99 
  100     if (!res_initted) {     /* only have to do this once */
  101         res_init();
  102         res_initted = 1;
  103     }
  104 
  105     /* Make our DNS query. */
  106     answer = fixedans;
  107     if (server_shutting_down) {
  108         if (txtbuf != NULL) {
  109             snprintf(txtbuf, txtbufsize, "System shutting down");
  110         }
  111         return (1);
  112     }
  113     len = res_query(domain, C_IN, T_A, answer, PACKETSZ);
  114 
  115     /* Was there a problem? If so, the domain doesn't exist. */
  116     if (len == -1) {
  117         if (txtbuf != NULL) {
  118             strcpy(txtbuf, "");
  119         }
  120         return(0);
  121     }
  122 
  123     if (len > PACKETSZ) {
  124         answer = malloc(len);
  125         need_to_free_answer = 1;
  126         len = res_query(domain, C_IN, T_A, answer, len);
  127         if( len == -1 ) {
  128             if (txtbuf != NULL) {
  129                 snprintf(txtbuf, txtbufsize, "Message rejected due to known spammer source IP address");
  130             }
  131             if (need_to_free_answer) free(answer);
  132             return(1);
  133         }
  134     }
  135     if (server_shutting_down) {
  136         if (txtbuf != NULL) {
  137             snprintf(txtbuf, txtbufsize, "System shutting down");
  138         }
  139         if (need_to_free_answer) free(answer);
  140         return (1);
  141     }
  142 
  143     result = (char *) malloc(RESULT_SIZE);
  144     result[0] = '\0';
  145 
  146     /* Make another DNS query for textual data; this shouldn't
  147      * be a performance hit, since it'll now be cached at the
  148      * nameserver we're using.
  149      */
  150     len = res_query(domain, C_IN, T_TXT, answer, PACKETSZ);
  151     if (server_shutting_down) {
  152         if (txtbuf != NULL) {
  153             snprintf(txtbuf, txtbufsize, "System shutting down");
  154         }
  155         if (need_to_free_answer) free(answer);
  156         free(result);
  157         return (1);
  158     }
  159 
  160     /* Just in case there's no TXT record... */
  161     if (len ==(-1)) {
  162         if (txtbuf != NULL) {
  163             snprintf(txtbuf, txtbufsize, "Message rejected due to known spammer source IP address");
  164         }
  165         if (need_to_free_answer) free(answer);
  166         free(result);
  167         return(1);
  168     }
  169 
  170     /* Skip the header and the address we queried. */
  171     cp = answer + sizeof( HEADER );
  172     while( *cp != '\0' ) {
  173         a = *cp++;
  174         while( a-- )
  175             cp++;
  176     }
  177 
  178     /* This seems to be a bit of magic data that we need to
  179      * skip. I wish there were good online documentation
  180      * for programming for libresolv, so I'd know what I'm
  181      * skipping here. Anyone reading this, feel free to
  182      * enlighten me.
  183      */
  184     cp += 1 + NS_INT16SZ + NS_INT32SZ;
  185 
  186     /* Skip the type, class and ttl. */
  187     cp += (NS_INT16SZ * 2) + NS_INT32SZ;
  188 
  189     /* Get the length and end of the buffer. */
  190     NS_GET16(c, cp);
  191     cend = cp + c;
  192 
  193     /* Iterate over any multiple answers we might have. In
  194      * this context, it's unlikely, but anyway.
  195      */
  196     rp = (u_char *) result;
  197     rend = (u_char *) result + RESULT_SIZE - 1;
  198     while (cp < cend && rp < rend) {
  199         a = *cp++;
  200         if (a != 0) {
  201             for (b = a; b > 0 && cp < cend && rp < rend; b--) {
  202                 if (*cp == '\n' || *cp == '"' || *cp == '\\') {
  203                     *rp++ = '\\';
  204                 }
  205                 *rp++ = *cp++;
  206             }
  207         }
  208     }
  209     *rp = '\0';
  210     if (txtbuf != NULL) {
  211         long len;
  212         len = snprintf(txtbuf, txtbufsize, "%s", result);
  213     
  214         /* Remove nonprintable characters */
  215         for (p = txtbuf; *p != '\0'; p++) {
  216             if (!isprint(*p)) {
  217                 memmove (p,
  218                      p + 1,
  219                      len - (p - txtbuf) - 1);
  220             }
  221         }
  222     }
  223     if (need_to_free_answer) free(answer);
  224     free(result);
  225     return(1);
  226 }
  227 
  228 
  229 /*
  230  * Check to see if the client host is on some sort of spam list (RBL)
  231  * If spammer, returns nonzero and places reason in 'message_to_spammer'
  232  */
  233 int rbl_check(char *cs_addr, char *message_to_spammer)
  234 {
  235     char tbuf[256] = "";
  236     int suffix_pos = 0;
  237     int rbl;
  238     int rc;
  239     int num_rbl;
  240     char rbl_domains[SIZ];
  241     char txt_answer[1024];
  242     struct timeval tx_start;
  243     struct timeval tx_finish;
  244 
  245     rc = 0;
  246     strcpy(message_to_spammer, "ok");
  247     gettimeofday(&tx_start, NULL);      /* start a stopwatch for performance timing */
  248 
  249     if ((strchr(cs_addr, '.')) && (!strchr(cs_addr, ':'))) {
  250         int a1, a2, a3, a4;
  251 
  252         sscanf(cs_addr, "%d.%d.%d.%d", &a1, &a2, &a3, &a4);
  253         snprintf(tbuf, sizeof tbuf, "%d.%d.%d.%d.", a4, a3, a2, a1);
  254         suffix_pos = strlen(tbuf);
  255     }
  256     else if ((!strchr(cs_addr, '.')) && (strchr(cs_addr, ':'))) {
  257         int num_colons = 0;
  258         int i = 0;
  259         char workbuf[sizeof tbuf];
  260         char *ptr;
  261 
  262         /* tedious code to expand and reverse an IPv6 address */
  263         safestrncpy(tbuf, cs_addr, sizeof tbuf);
  264         num_colons = haschar(tbuf, ':');
  265         if ((num_colons < 2) || (num_colons > 7))
  266             goto finish_rbl;    /* badly formed address */
  267 
  268         /* expand the "::" shorthand */
  269         while (num_colons < 7) {
  270             ptr = strstr(tbuf, "::");
  271             if (!ptr)
  272                 goto finish_rbl;                /* badly formed address */
  273 
  274             ++ptr;
  275             strcpy(workbuf, ptr);
  276             strcpy(ptr, ":");
  277             strcat(ptr, workbuf);
  278             ++num_colons;
  279         }
  280 
  281         /* expand to 32 hex characters with no colons */
  282         strcpy(workbuf, tbuf);
  283         strcpy(tbuf, "00000000000000000000000000000000");
  284         for (i=0; i<8; ++i) {
  285             char tokbuf[5];
  286             extract_token(tokbuf, workbuf, i, ':', sizeof tokbuf);
  287             memcpy(&tbuf[ (i*4) + (4-strlen(tokbuf)) ], tokbuf, strlen(tokbuf) );
  288         }
  289         if (strlen(tbuf) != 32) {
  290             goto finish_rbl;
  291         }
  292 
  293         /* now reverse it and add dots */
  294         strcpy(workbuf, tbuf);
  295         for (i=0; i<32; ++i) {
  296             tbuf[i*2] = workbuf[31-i];
  297             tbuf[(i*2)+1] = '.';
  298         }
  299         tbuf[64] = 0;
  300         suffix_pos = 64;
  301     }
  302     else {
  303         goto finish_rbl;    /* unknown address format */
  304     }
  305 
  306     /* See if we have any RBL domains configured */
  307     num_rbl = get_hosts(rbl_domains, "rbl");
  308     if (num_rbl < 1)
  309     {
  310         goto finish_rbl;
  311     }
  312 
  313     /* Try all configured RBL's */
  314         for (rbl=0; rbl<num_rbl; ++rbl) {
  315                 extract_token(&tbuf[suffix_pos], rbl_domains, rbl, '|', (sizeof tbuf - suffix_pos));
  316 
  317         if (rblcheck_backend(tbuf, txt_answer, sizeof txt_answer)) {
  318             strcpy(message_to_spammer, txt_answer);
  319             syslog(LOG_INFO, "RBL: %s %s", cs_addr, txt_answer);
  320             rc = 1;
  321         }
  322     }
  323 finish_rbl:
  324     /* How long did this transaction take? */
  325     gettimeofday(&tx_finish, NULL);
  326 
  327     syslog(LOG_WARNING, "rbl: %s [%ld.%06ld] %s",
  328         cs_addr,
  329         ((tx_finish.tv_sec*1000000 + tx_finish.tv_usec) - (tx_start.tv_sec*1000000 + tx_start.tv_usec)) / 1000000,
  330         ((tx_finish.tv_sec*1000000 + tx_finish.tv_usec) - (tx_start.tv_sec*1000000 + tx_start.tv_usec)) % 1000000,
  331         (rc)?"found":"none found"
  332     );
  333 
  334     return rc;
  335 }
  336             
  337 
  338 /*
  339  * Convert a host name to a dotted quad address. 
  340  * Returns zero on success or nonzero on failure.
  341  *
  342  * FIXME this is obviously not IPv6 compatible.
  343  */
  344 int hostname_to_dotted_quad(char *addr, char *host) {
  345     struct hostent *ch;
  346     const char *i;
  347     int a1, a2, a3, a4;
  348 
  349     ch = gethostbyname(host);
  350     if (ch == NULL) {
  351         strcpy(addr, "0.0.0.0");
  352         return(1);
  353     }
  354 
  355     i = (const char *) ch->h_addr_list[0];
  356     a1 = ((*i++) & 0xff);
  357     a2 = ((*i++) & 0xff);
  358     a3 = ((*i++) & 0xff);
  359     a4 = ((*i++) & 0xff);
  360     sprintf(addr, "%d.%d.%d.%d", a1, a2, a3, a4);
  361     return(0);
  362 }