libspf2 1.2.10
|
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 }