"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 }