spf_expand.c

Go to the documentation of this file.
00001 /*
00002  * This program is free software; you can redistribute it and/or modify
00003  * it under the terms of either:
00004  *
00005  *   a) The GNU Lesser General Public License as published by the Free
00006  *        Software Foundation; either version 2.1, or (at your option) any
00007  *        later version,
00008  *
00009  *   OR
00010  *
00011  *   b) The two-clause BSD license.
00012  *
00013  * These licenses can be found with the distribution in the file LICENSES
00014  */
00015 
00021 #include "spf_sys_config.h"
00022 
00023 
00024 #ifdef STDC_HEADERS
00025 # include <stdio.h>             /* stdin / stdout */
00026 # include <stdlib.h>       /* malloc / free */
00027 # include <ctype.h>             /* isupper / tolower */
00028 #endif
00029 
00030 #ifdef HAVE_STRING_H
00031 # include <string.h>       /* strstr / strdup */
00032 #else
00033 # ifdef HAVE_STRINGS_H
00034 #  include <strings.h>     /* strstr / strdup */
00035 # endif
00036 #endif
00037 
00038 #if TIME_WITH_SYS_TIME
00039 # include <sys/time.h>
00040 # include <time.h>
00041 #else
00042 # if HAVE_SYS_TIME_H
00043 #  include <sys/time.h>
00044 # else
00045 #  include <time.h>
00046 # endif
00047 #endif
00048 #ifdef HAVE_STRING_H
00049 #include <string.h>
00050 #endif
00051 
00052 
00053 #include "spf.h"
00054 #include "spf_internal.h"
00055 #include "spf_record.h"
00056 
00057 
00058 // #define DEBUG
00059 
00060 static const char               client_ver_ipv4[] = "in-addr";
00061 static const char               client_ver_ipv6[] = "ip6";
00062 
00063 
00064 static inline int
00065 SPF_delim_valid(SPF_data_t *d, char c)
00066 {
00067         return (   ( d->dv.delim_dot   && c == '.' )
00068                         || ( d->dv.delim_dash  && c == '-' )
00069                         || ( d->dv.delim_plus  && c == '+' )
00070                         || ( d->dv.delim_equal && c == '=' )
00071                         || ( d->dv.delim_bar   && c == '|' )
00072                         || ( d->dv.delim_under && c == '_' ) );
00073 }
00074 
00080 SPF_errcode_t
00081 SPF_record_expand_data(SPF_server_t *spf_server,
00082                                 SPF_request_t *spf_request,
00083                                 SPF_response_t *spf_response,
00084                                 SPF_data_t *data, size_t data_len,
00085                                 char **bufp, size_t *buflenp)
00086 {
00087         SPF_data_t      *d, *data_end;
00088 
00089         size_t           len;
00090         const char      *p_err; // XXX Check this value, when returned.
00091         char            *p, *p_end;
00092         const char      *p_read;
00093         const char      *p_read_end;
00094         char            *p_write;
00095         char            *p2, *p2_end;
00096 
00097 
00098         const char      *var;
00099         char            *munged_var = NULL;
00100         char            *url_var = NULL;
00101 
00102                         /* Pretty-printing buffers. */
00103         char            ip4_buf[ INET_ADDRSTRLEN ];
00104         char            ip6_buf[ INET6_ADDRSTRLEN ];
00105                         /* Hex buffer for ipv6 (size in nibbles) */
00106         char            ip6_rbuf[ sizeof( struct in6_addr ) * 4 + 1 ];
00107 
00108         char            time_buf[ sizeof( "4294967296" ) ]; /* 2^32 seconds max         */
00109 
00110         int                     num_found;
00111         int                     i;
00112         size_t          buflen;
00113         int                     compute_length;
00114         SPF_errcode_t    err;
00115 
00116 
00117         /*
00118          * make sure we were passed valid data to work with
00119          */
00120         SPF_ASSERT_NOTNULL(spf_server);
00121         SPF_ASSERT_NOTNULL(data);
00122         SPF_ASSERT_NOTNULL(bufp);
00123         SPF_ASSERT_NOTNULL(buflenp);
00124 
00125         buflen = 1;     /* For the terminating '\0' */
00126         compute_length = 1;
00127         p = NULL;
00128         p_end = NULL;
00129 
00130         /* data_end = SPF_mech_end_data( mech ); */ /* doesn't work for mods */
00131         data_end = (SPF_data_t *)((char *)data + data_len);
00132 
00133 top:
00134 #ifdef DEBUG
00135         fprintf(stderr, "Pass start compute_length=%d\n", compute_length);
00136 #endif
00137         /*
00138          * expand the data
00139          */
00140         for (d = data; d < data_end; d = SPF_data_next(d)) {
00141 #ifdef DEBUG
00142                 fprintf(stderr, " Item type=%d at %p\n", d->dc.parm_type, d);
00143 #endif
00144                 if (d->dc.parm_type == PARM_CIDR)
00145                         continue;
00146 
00147                 if (d->ds.parm_type == PARM_STRING) {
00148                         if (compute_length) {
00149                                 buflen += d->ds.len;
00150                                 continue;
00151                         }
00152                         /* This should NEVER happen now. */
00153                         if (p_end - (p + d->ds.len) <= 0)
00154                                         SPF_error("Failed to allocate enough memory "
00155                                                                 "to expand string.");
00156                         memcpy(p, SPF_data_str(d), d->ds.len);
00157                         p += d->ds.len;
00158                         continue;
00159                 }
00160 
00161                 /* Otherwise, it's a variable. */
00162 
00163                 var = NULL;
00164                 switch (d->dv.parm_type) {
00165                 case PARM_LP_FROM:              /* local-part of envelope-sender */
00166                         var = spf_request->env_from_lp;
00167                         break;
00168 
00169                 case PARM_ENV_FROM:             /* envelope-sender                              */
00170                         var = spf_request->env_from;
00171                         break;
00172 
00173                 case PARM_DP_FROM:              /* envelope-domain                              */
00174                         var = spf_request->env_from_dp;
00175                         break;
00176 
00177                 case PARM_CUR_DOM:              /* current-domain                               */
00178                         var = spf_request->cur_dom;
00179                         break;
00180 
00181                 case PARM_CLIENT_IP:            /* SMTP client IP                               */
00182                         if (compute_length) {
00183                                 len = sizeof(ip6_rbuf);
00184                                 if (d->dv.url_encode)
00185                                         len *= 3;
00186                                 buflen += len;
00187                                 continue;
00188                         }
00189                         if (spf_request->client_ver == AF_INET) {
00190                                 p_err = inet_ntop(AF_INET, &spf_request->ipv4,
00191                                                                    ip4_buf, sizeof(ip4_buf));
00192                                 var = ip4_buf;
00193                         }
00194                         else if (spf_request->client_ver == AF_INET6) {
00195                                 p2 = ip6_rbuf;
00196                                 p2_end = p2 + sizeof(ip6_rbuf);
00197 
00198                                 for (i = 0; i < array_elem(spf_request->ipv6.s6_addr); i++) {
00199                                         p2 += snprintf(p2, p2_end - p2, "%.1x.%.1x.",
00200                                                                         spf_request->ipv6.s6_addr[i] >> 4,
00201                                                                         spf_request->ipv6.s6_addr[i] & 0xf);
00202                                 }
00203 
00204                                 /* squash the final '.' */
00205                                 ip6_rbuf[sizeof(struct in6_addr) * 4 - 1] = '\0';
00206 
00207                                 var = ip6_rbuf;
00208                         }
00209                         break;
00210 
00211                 case PARM_CLIENT_IP_P:          /* SMTP client IP (pretty)              */
00212                         if (compute_length) {
00213                                 len = sizeof(ip6_rbuf);
00214                                 if (d->dv.url_encode)
00215                                         len *= 3;
00216                                 buflen += len;
00217                                 continue;
00218                         }
00219                         if (spf_request->client_ver == AF_INET) {
00220                                 p_err = inet_ntop(AF_INET, &spf_request->ipv4,
00221                                                                    ip4_buf, sizeof(ip4_buf));
00222                                 var = ip4_buf;
00223                         }
00224                         else if (spf_request->client_ver == AF_INET6) {
00225                                 p_err = inet_ntop(AF_INET6, &spf_request->ipv6,
00226                                                                    ip6_buf, sizeof(ip6_buf));
00227                                 var = ip6_buf;
00228                         }
00229                         break;
00230 
00231                 case PARM_TIME:                         /* time in UTC epoch secs               */
00232                         if (compute_length) {
00233                                 len = sizeof(time_buf);
00234                                 /* This never gets bigger using URL encoding. */
00235                                 buflen += len;
00236                                 continue;
00237                         }
00238                         snprintf(time_buf, sizeof(time_buf), "%ld",
00239                                           (long)time(NULL));
00240                         var = time_buf;
00241                         break;
00242 
00243                 case PARM_CLIENT_DOM:           /* SMTP client domain name              */
00244                         var = SPF_request_get_client_dom(spf_request);
00245                         if (! var)
00246                                 return SPF_E_NO_MEMORY;
00247                         break;
00248 
00249                 case PARM_CLIENT_VER:           /* IP ver str - in-addr/ip6             */
00250                         if (spf_request->client_ver == AF_INET)
00251                                 var = client_ver_ipv4;
00252                         else if (spf_request->client_ver == AF_INET6)
00253                                 var = client_ver_ipv6;
00254                         break;
00255 
00256                 case PARM_HELO_DOM:             /* HELO/EHLO domain                             */
00257                         var = spf_request->helo_dom;
00258                         break;
00259 
00260                 case PARM_REC_DOM:              /* receiving domain                             */
00261                         var = SPF_request_get_rec_dom(spf_request);
00262                         break;
00263 
00264                 default:
00265 #ifdef DEBUG
00266                         fprintf(stderr, "Invalid variable %d\n", d->dv.parm_type);
00267 #endif
00268                         return SPF_E_INVALID_VAR;
00269                         break;
00270                 }
00271 
00272                 if (var == NULL)
00273                         return SPF_E_UNINIT_VAR;
00274 
00275                 len = strlen(var);
00276                 if (compute_length) {
00277                         if (d->dv.url_encode)
00278                                 len *= 3;
00279                         buflen += len;
00280                         continue;
00281                 }
00282 
00283                 /* Now we put 'var' through the munging procedure. */
00284                 munged_var = (char *)malloc(len + 1);
00285                 if (munged_var == NULL)
00286                         return SPF_E_NO_MEMORY;
00287                 memset(munged_var, 0, len + 1);
00288 
00289                 p_read_end = var + len;
00290                 p_write = munged_var;
00291 
00292                 /* reverse */
00293 
00294 /* The following code confuses both me and Coverity. Shevek. */
00295 
00296                 if (d->dv.rev) {
00297                         p_read = p_read_end - 1;
00298 
00299                         while ( p_read >= var ) {
00300                                 if ( SPF_delim_valid(d, *p_read) ) {
00301                                         /* Subtract 1 because p_read points to delim, and
00302                                          * p_read_end points to the following delim. */
00303                                         len = p_read_end - p_read - 1;
00304                                         memcpy( p_write, p_read + 1, len );
00305                                         p_write += len;
00306                                         *p_write++ = '.';
00307 
00308                                         p_read_end = p_read;
00309                                 }
00310                                 p_read--;
00311                         }
00312 
00313                         /* Now p_read_end should point one before the start of the
00314                          * string. p_read_end might also point there if the string
00315                          * starts with a delimiter. */
00316                         if (p_read_end >= p_read) {
00317                                 len = p_read_end - p_read - 1;
00318                                 memcpy( p_write, p_read + 1, len );
00319                                 p_write += len;
00320                                 *p_write++ = '.';
00321                         }
00322 
00323                         /* p_write always points to the 'next' character. */
00324                         p_write--;
00325                         *p_write = '\0';
00326                 }
00327                 else {
00328                         p_read = var;
00329 
00330                         while (p_read < p_read_end) {
00331                                 if (SPF_delim_valid(d, *p_read))
00332                                         *p_write++ = '.';
00333                                 else
00334                                         *p_write++ = *p_read;
00335                                 p_read++;
00336                         }
00337 
00338                         *p_write = '\0';
00339                 }
00340 
00341                 /* Now munged_var is a copy of var, possibly reversed, and
00342                  * thus len == strlen(munged_var). However, we continue to
00343                  * manipulate the underlying munged_var since var is const. */
00344 
00345                 /* truncate, from the right hand side. */
00346                 if (d->dv.num_rhs > 0) {
00347                         p_read_end = munged_var + len;          /* const, at '\0' */
00348                         p_write = munged_var + len - 1;
00349                         num_found = 0;
00350                         while (p_write > munged_var) {
00351                                 if (*p_write == '.')
00352                                         num_found++;
00353                                 if (num_found == d->dv.num_rhs)
00354                                         break;
00355                                 p_write--;
00356                         }
00357                         p_write++;              /* Move to just after the '.' */
00358                         /* This moves the '\0' as well. */
00359                         len = p_read_end - p_write;
00360                         memmove(munged_var, p_write, len + 1);
00361                 }
00362 
00363                 var = munged_var;
00364                 /* Now, we have 'var', of length 'len' */
00365 
00366                 /* URL encode */
00367 
00368                 if (d->dv.url_encode) {
00369                         url_var = malloc(len * 3 + 1);
00370                         if (url_var == NULL) {
00371                                 if (munged_var)
00372                                         free(munged_var);
00373                                 return SPF_E_NO_MEMORY;
00374                         }
00375 
00376                         p_read = var;
00377                         p_write = url_var;
00378 
00379                         /* escape non-uric characters (rfc2396) */
00380                         while ( *p_read != '\0' )
00381                         {
00382                                 if ( isalnum( (unsigned char)( *p_read  ) ) )
00383                                         *p_write++ = *p_read++;
00384                                 else
00385                                 {
00386                                         switch( *p_read )
00387                                         {
00388                                         case '-':
00389                                         case '_':
00390                                         case '.':
00391                                         case '!':
00392                                         case '~':
00393                                         case '*':
00394                                         case '\'':
00395                                         case '(':
00396                                         case ')':
00397                                                 *p_write++ = *p_read++;
00398                                                 break;
00399 
00400                                         default:
00401                                                 /* No point doing snprintf with a const '4'
00402                                                  * because we know we're going to get 4
00403                                                  * characters anyway. */
00404                                                 sprintf( p_write, "%%%02x", *p_read );
00405                                                 p_write += 3;
00406                                                 p_read++;
00407                                                 break;
00408                                         }
00409                                 }
00410                         }
00411                         *p_write = '\0';
00412 
00413                         var = url_var;
00414                         len = p_write - url_var;                /* Not actually used. */
00415                 }
00416 
00417 
00418                 /* finish up */
00419                 len = snprintf(p, p_end - p, "%s", var);
00420                 p += len;
00421                 if (p_end - p <= 0) {
00422                         if (munged_var)
00423                                 free(munged_var);
00424                         if (url_var)
00425                                 free(url_var);
00426                         return SPF_E_INTERNAL_ERROR;
00427                 }
00428 
00429                 if (munged_var)
00430                         free(munged_var);
00431                 munged_var = NULL;
00432                 if (url_var)
00433                         free(url_var);
00434                 url_var = NULL;
00435         }
00436 #ifdef DEBUG
00437         fprintf(stderr, "Pass end compute_length=%d\n", compute_length);
00438 #endif
00439 
00440         if (compute_length) {
00441                 compute_length = 0;
00442                 /* Do something about (re-)allocating the buffer. */
00443                 err = SPF_recalloc(bufp, buflenp, buflen);
00444                 if (err != SPF_E_SUCCESS)
00445                         return err;
00446                 p = *bufp;
00447                 p_end = *bufp + *buflenp;
00448                 goto top;
00449         }
00450 
00451         *p++ = '\0';
00452 
00453         return SPF_E_SUCCESS;
00454 }

Generated on 20 May 2019 for libspf2 by  doxygen 1.6.1