"Fossies" - the Fresh Open Source Software Archive

Member "libspf2-1.2.10/src/libspf2/spf_expand.c" (10 Jun 2013, 10240 Bytes) of package /linux/privat/libspf2-1.2.10.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 "spf_expand.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * This program is free software; you can redistribute it and/or modify
    3  * it under the terms of either:
    4  *
    5  *   a) The GNU Lesser General Public License as published by the Free
    6  *    Software Foundation; either version 2.1, or (at your option) any
    7  *    later version,
    8  *
    9  *   OR
   10  *
   11  *   b) The two-clause BSD license.
   12  *
   13  * These licenses can be found with the distribution in the file LICENSES
   14  */
   15 
   16 /**
   17  * @file
   18  * @brief Expansion routine for SPF macros.
   19  */
   20 
   21 #include "spf_sys_config.h"
   22 
   23 
   24 #ifdef STDC_HEADERS
   25 # include <stdio.h>     /* stdin / stdout */
   26 # include <stdlib.h>       /* malloc / free */
   27 # include <ctype.h>     /* isupper / tolower */
   28 #endif
   29 
   30 #ifdef HAVE_STRING_H
   31 # include <string.h>       /* strstr / strdup */
   32 #else
   33 # ifdef HAVE_STRINGS_H
   34 #  include <strings.h>     /* strstr / strdup */
   35 # endif
   36 #endif
   37 
   38 #if TIME_WITH_SYS_TIME
   39 # include <sys/time.h>
   40 # include <time.h>
   41 #else
   42 # if HAVE_SYS_TIME_H
   43 #  include <sys/time.h>
   44 # else
   45 #  include <time.h>
   46 # endif
   47 #endif
   48 #ifdef HAVE_STRING_H
   49 #include <string.h>
   50 #endif
   51 
   52 
   53 #include "spf.h"
   54 #include "spf_internal.h"
   55 #include "spf_record.h"
   56 
   57 
   58 // #define DEBUG
   59 
   60 static const char       client_ver_ipv4[] = "in-addr";
   61 static const char       client_ver_ipv6[] = "ip6";
   62 
   63 
   64 static inline int
   65 SPF_delim_valid(SPF_data_t *d, char c)
   66 {
   67     return (   ( d->dv.delim_dot   && c == '.' )
   68             || ( d->dv.delim_dash  && c == '-' )
   69             || ( d->dv.delim_plus  && c == '+' )
   70             || ( d->dv.delim_equal && c == '=' )
   71             || ( d->dv.delim_bar   && c == '|' )
   72             || ( d->dv.delim_under && c == '_' ) );
   73 }
   74 
   75 /**
   76  * This could better collect errors, like the compiler does.
   77  * This requires that *bufp be either malloced to *buflenp, or NULL
   78  * This may realloc *bufp.
   79  */
   80 SPF_errcode_t
   81 SPF_record_expand_data(SPF_server_t *spf_server,
   82                 SPF_request_t *spf_request,
   83                 SPF_response_t *spf_response,
   84                 SPF_data_t *data, size_t data_len,
   85                 char **bufp, size_t *buflenp)
   86 {
   87     SPF_data_t  *d, *data_end;
   88 
   89     size_t       len;
   90     const char  *p_err; // XXX Check this value, when returned.
   91     char        *p, *p_end;
   92     const char  *p_read;
   93     const char  *p_read_end;
   94     char        *p_write;
   95     char        *p2, *p2_end;
   96 
   97 
   98     const char  *var;
   99     char        *munged_var = NULL;
  100     char        *url_var = NULL;
  101 
  102             /* Pretty-printing buffers. */
  103     char        ip4_buf[ INET_ADDRSTRLEN ];
  104     char        ip6_buf[ INET6_ADDRSTRLEN ];
  105             /* Hex buffer for ipv6 (size in nibbles) */
  106     char        ip6_rbuf[ sizeof( struct in6_addr ) * 4 + 1 ];
  107 
  108     char        time_buf[ sizeof( "4294967296" ) ]; /* 2^32 seconds max     */
  109 
  110     int         num_found;
  111     int         i;
  112     size_t      buflen;
  113     int         compute_length;
  114     SPF_errcode_t    err;
  115 
  116 
  117     /*
  118      * make sure we were passed valid data to work with
  119      */
  120     SPF_ASSERT_NOTNULL(spf_server);
  121     SPF_ASSERT_NOTNULL(data);
  122     SPF_ASSERT_NOTNULL(bufp);
  123     SPF_ASSERT_NOTNULL(buflenp);
  124 
  125     buflen = 1; /* For the terminating '\0' */
  126     compute_length = 1;
  127     p = NULL;
  128     p_end = NULL;
  129 
  130     /* data_end = SPF_mech_end_data( mech ); */ /* doesn't work for mods */
  131     data_end = (SPF_data_t *)((char *)data + data_len);
  132 
  133 top:
  134 #ifdef DEBUG
  135     fprintf(stderr, "Pass start compute_length=%d\n", compute_length);
  136 #endif
  137     /*
  138      * expand the data
  139      */
  140     for (d = data; d < data_end; d = SPF_data_next(d)) {
  141 #ifdef DEBUG
  142         fprintf(stderr, " Item type=%d at %p\n", d->dc.parm_type, d);
  143 #endif
  144         if (d->dc.parm_type == PARM_CIDR)
  145             continue;
  146 
  147         if (d->ds.parm_type == PARM_STRING) {
  148             if (compute_length) {
  149                 buflen += d->ds.len;
  150                 continue;
  151             }
  152             /* This should NEVER happen now. */
  153             if (p_end - (p + d->ds.len) <= 0)
  154                     SPF_error("Failed to allocate enough memory "
  155                                 "to expand string.");
  156             memcpy(p, SPF_data_str(d), d->ds.len);
  157             p += d->ds.len;
  158             continue;
  159         }
  160 
  161         /* Otherwise, it's a variable. */
  162 
  163         var = NULL;
  164         switch (d->dv.parm_type) {
  165         case PARM_LP_FROM:      /* local-part of envelope-sender */
  166             var = spf_request->env_from_lp;
  167             break;
  168 
  169         case PARM_ENV_FROM:     /* envelope-sender              */
  170             var = spf_request->env_from;
  171             break;
  172 
  173         case PARM_DP_FROM:      /* envelope-domain              */
  174             var = spf_request->env_from_dp;
  175             break;
  176 
  177         case PARM_CUR_DOM:      /* current-domain               */
  178             var = spf_request->cur_dom;
  179             break;
  180 
  181         case PARM_CLIENT_IP:        /* SMTP client IP               */
  182             if (compute_length) {
  183                 len = sizeof(ip6_rbuf);
  184                 if (d->dv.url_encode)
  185                     len *= 3;
  186                 buflen += len;
  187                 continue;
  188             }
  189             if (spf_request->client_ver == AF_INET) {
  190                 p_err = inet_ntop(AF_INET, &spf_request->ipv4,
  191                                    ip4_buf, sizeof(ip4_buf));
  192                 var = ip4_buf;
  193             }
  194             else if (spf_request->client_ver == AF_INET6) {
  195                 p2 = ip6_rbuf;
  196                 p2_end = p2 + sizeof(ip6_rbuf);
  197 
  198                 for (i = 0; i < array_elem(spf_request->ipv6.s6_addr); i++) {
  199                     p2 += snprintf(p2, p2_end - p2, "%.1x.%.1x.",
  200                                     spf_request->ipv6.s6_addr[i] >> 4,
  201                                     spf_request->ipv6.s6_addr[i] & 0xf);
  202                 }
  203 
  204                 /* squash the final '.' */
  205                 ip6_rbuf[sizeof(struct in6_addr) * 4 - 1] = '\0';
  206 
  207                 var = ip6_rbuf;
  208             }
  209             break;
  210 
  211         case PARM_CLIENT_IP_P:      /* SMTP client IP (pretty)      */
  212             if (compute_length) {
  213                 len = sizeof(ip6_rbuf);
  214                 if (d->dv.url_encode)
  215                     len *= 3;
  216                 buflen += len;
  217                 continue;
  218             }
  219             if (spf_request->client_ver == AF_INET) {
  220                 p_err = inet_ntop(AF_INET, &spf_request->ipv4,
  221                                    ip4_buf, sizeof(ip4_buf));
  222                 var = ip4_buf;
  223             }
  224             else if (spf_request->client_ver == AF_INET6) {
  225                 p_err = inet_ntop(AF_INET6, &spf_request->ipv6,
  226                                    ip6_buf, sizeof(ip6_buf));
  227                 var = ip6_buf;
  228             }
  229             break;
  230 
  231         case PARM_TIME:             /* time in UTC epoch secs       */
  232             if (compute_length) {
  233                 len = sizeof(time_buf);
  234                 /* This never gets bigger using URL encoding. */
  235                 buflen += len;
  236                 continue;
  237             }
  238             snprintf(time_buf, sizeof(time_buf), "%ld",
  239                       (long)time(NULL));
  240             var = time_buf;
  241             break;
  242 
  243         case PARM_CLIENT_DOM:       /* SMTP client domain name      */
  244             var = SPF_request_get_client_dom(spf_request);
  245             if (! var)
  246                 return SPF_E_NO_MEMORY;
  247             break;
  248 
  249         case PARM_CLIENT_VER:       /* IP ver str - in-addr/ip6     */
  250             if (spf_request->client_ver == AF_INET)
  251                 var = client_ver_ipv4;
  252             else if (spf_request->client_ver == AF_INET6)
  253                 var = client_ver_ipv6;
  254             break;
  255 
  256         case PARM_HELO_DOM:     /* HELO/EHLO domain             */
  257             var = spf_request->helo_dom;
  258             break;
  259 
  260         case PARM_REC_DOM:      /* receiving domain             */
  261             var = SPF_request_get_rec_dom(spf_request);
  262             break;
  263 
  264         default:
  265 #ifdef DEBUG
  266             fprintf(stderr, "Invalid variable %d\n", d->dv.parm_type);
  267 #endif
  268             return SPF_E_INVALID_VAR;
  269             break;
  270         }
  271 
  272         if (var == NULL)
  273             return SPF_E_UNINIT_VAR;
  274 
  275         len = strlen(var);
  276         if (compute_length) {
  277             if (d->dv.url_encode)
  278                 len *= 3;
  279             buflen += len;
  280             continue;
  281         }
  282 
  283         /* Now we put 'var' through the munging procedure. */
  284         munged_var = (char *)malloc(len + 1);
  285         if (munged_var == NULL)
  286             return SPF_E_NO_MEMORY;
  287         memset(munged_var, 0, len + 1);
  288 
  289         p_read_end = var + len;
  290         p_write = munged_var;
  291 
  292         /* reverse */
  293 
  294 /* The following code confuses both me and Coverity. Shevek. */
  295 
  296         if (d->dv.rev) {
  297             p_read = p_read_end - 1;
  298 
  299             while ( p_read >= var ) {
  300                 if ( SPF_delim_valid(d, *p_read) ) {
  301                     /* Subtract 1 because p_read points to delim, and
  302                      * p_read_end points to the following delim. */
  303                     len = p_read_end - p_read - 1;
  304                     memcpy( p_write, p_read + 1, len );
  305                     p_write += len;
  306                     *p_write++ = '.';
  307 
  308                     p_read_end = p_read;
  309                 }
  310                 p_read--;
  311             }
  312 
  313             /* Now p_read_end should point one before the start of the
  314              * string. p_read_end might also point there if the string
  315              * starts with a delimiter. */
  316             if (p_read_end >= p_read) {
  317                 len = p_read_end - p_read - 1;
  318                 memcpy( p_write, p_read + 1, len );
  319                 p_write += len;
  320                 *p_write++ = '.';
  321             }
  322 
  323             /* p_write always points to the 'next' character. */
  324             p_write--;
  325             *p_write = '\0';
  326         }
  327         else {
  328             p_read = var;
  329 
  330             while (p_read < p_read_end) {
  331                 if (SPF_delim_valid(d, *p_read))
  332                     *p_write++ = '.';
  333                 else
  334                     *p_write++ = *p_read;
  335                 p_read++;
  336             }
  337 
  338             *p_write = '\0';
  339         }
  340 
  341         /* Now munged_var is a copy of var, possibly reversed, and
  342          * thus len == strlen(munged_var). However, we continue to
  343          * manipulate the underlying munged_var since var is const. */
  344 
  345         /* truncate, from the right hand side. */
  346         if (d->dv.num_rhs > 0) {
  347             p_read_end = munged_var + len;      /* const, at '\0' */
  348             p_write = munged_var + len - 1;
  349             num_found = 0;
  350             while (p_write > munged_var) {
  351                 if (*p_write == '.')
  352                     num_found++;
  353                 if (num_found == d->dv.num_rhs)
  354                     break;
  355                 p_write--;
  356             }
  357             p_write++;      /* Move to just after the '.' */
  358             /* This moves the '\0' as well. */
  359             len = p_read_end - p_write;
  360             memmove(munged_var, p_write, len + 1);
  361         }
  362 
  363         var = munged_var;
  364         /* Now, we have 'var', of length 'len' */
  365 
  366         /* URL encode */
  367 
  368         if (d->dv.url_encode) {
  369             url_var = malloc(len * 3 + 1);
  370             if (url_var == NULL) {
  371                 if (munged_var)
  372                     free(munged_var);
  373                 return SPF_E_NO_MEMORY;
  374             }
  375 
  376             p_read = var;
  377             p_write = url_var;
  378 
  379             /* escape non-uric characters (rfc2396) */
  380             while ( *p_read != '\0' )
  381             {
  382                 if ( isalnum( (unsigned char)( *p_read  ) ) )
  383                     *p_write++ = *p_read++;
  384                 else
  385                 {
  386                     switch( *p_read )
  387                     {
  388                     case '-':
  389                     case '_':
  390                     case '.':
  391                     case '!':
  392                     case '~':
  393                     case '*':
  394                     case '\'':
  395                     case '(':
  396                     case ')':
  397                         *p_write++ = *p_read++;
  398                         break;
  399 
  400                     default:
  401                         /* No point doing snprintf with a const '4'
  402                          * because we know we're going to get 4
  403                          * characters anyway. */
  404                         sprintf( p_write, "%%%02x", *p_read );
  405                         p_write += 3;
  406                         p_read++;
  407                         break;
  408                     }
  409                 }
  410             }
  411             *p_write = '\0';
  412 
  413             var = url_var;
  414             len = p_write - url_var;        /* Not actually used. */
  415         }
  416 
  417 
  418         /* finish up */
  419         len = snprintf(p, p_end - p, "%s", var);
  420         p += len;
  421         if (p_end - p <= 0) {
  422             if (munged_var)
  423                 free(munged_var);
  424             if (url_var)
  425                 free(url_var);
  426             return SPF_E_INTERNAL_ERROR;
  427         }
  428 
  429         if (munged_var)
  430             free(munged_var);
  431         munged_var = NULL;
  432         if (url_var)
  433             free(url_var);
  434         url_var = NULL;
  435     }
  436 #ifdef DEBUG
  437     fprintf(stderr, "Pass end compute_length=%d\n", compute_length);
  438 #endif
  439 
  440     if (compute_length) {
  441         compute_length = 0;
  442         /* Do something about (re-)allocating the buffer. */
  443         err = SPF_recalloc(bufp, buflenp, buflen);
  444         if (err != SPF_E_SUCCESS)
  445             return err;
  446         p = *bufp;
  447         p_end = *bufp + *buflenp;
  448         goto top;
  449     }
  450 
  451     *p++ = '\0';
  452 
  453     return SPF_E_SUCCESS;
  454 }