libspf2  1.2.10
spf_id2str.c
Go to the documentation of this file.
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 #include "spf_sys_config.h"
17 
18 
19 
20 #ifdef STDC_HEADERS
21 # include <stdio.h> /* stdin / stdout */
22 # include <stdlib.h> /* malloc / free */
23 # include <ctype.h> /* isupper / tolower */
24 #endif
25 
26 #ifdef HAVE_INTTYPES_H
27 #include <inttypes.h>
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 
39 #include "spf.h"
40 #include "spf_internal.h"
41 
42 
43 static SPF_errcode_t
44 SPF_record_stringify_data(SPF_data_t *data, SPF_data_t *data_end,
45  char **p_p, char *p_end,
46  int is_mod, int cidr_ok, int debug )
47 {
48  char *p = *p_p;
49 
50  size_t len;
51 
52  SPF_data_t *cidr_data;
53 
54  if (debug)
55  SPF_debugf(" string data: Building");
56 
57  if (p_end - p <= 0)
58  return SPF_E_INTERNAL_ERROR;
59 
60  cidr_data = NULL;
61  if ( data < data_end && data->dc.parm_type == PARM_CIDR )
62  {
63  if (debug)
64  SPF_debugf(" string data: Found a CIDR at %p", data);
65  if ( !cidr_ok )
66  return SPF_E_INTERNAL_ERROR;
67 
68  cidr_data = data;
69  data = SPF_data_next( data );
70  }
71 
72 
73  for( ; data < data_end; data = SPF_data_next( data ) )
74  {
75  if (debug)
76  SPF_debugf(" string data: Handling data type %d at %p",
77  data->ds.parm_type, data);
78  if ( data->ds.parm_type == PARM_STRING )
79  {
80  char *s = SPF_data_str( data );
81  char *s_end = s + data->ds.len;
82  if (debug)
83  SPF_debugf(" string data: String is [%d] '%*.*s'",
84  data->ds.len, data->ds.len, data->ds.len, s);
85 
86  if (p_end - (p + data->ds.len) <= 0)
87  return SPF_E_INTERNAL_ERROR;
88 
89  while (s < s_end) {
90  if (*s == ' ') {
91  *p++ = '%';
92  *p++ = '_';
93  s++;
94  }
95  else if (*s == '%') {
96  *p++ = '%';
97  s++;
98  if (s[0] == '2' && s[1] == '0') {
99  *p++ = '-';
100  s += 2;
101  }
102  else {
103  *p++ = '%';
104  // *p++ = '%';
105  }
106  }
107  else {
108  *p++ = *s++;
109  }
110  }
111 
112  if (p_end - p <= 0)
113  return SPF_E_INTERNAL_ERROR;
114  }
115  else if (data->dc.parm_type == PARM_CIDR) {
116  /* Two CIDRs in a row is invalid. */
117  return SPF_E_INVALID_CIDR;
118  }
119  else {
120  len = snprintf( p, p_end - p, "%%{" );
121  p += len;
122  if ( p_end - p <= 0 ) return SPF_E_INTERNAL_ERROR;
123 
124 
125  if ( p_end - p <= 1 ) return SPF_E_INTERNAL_ERROR;
126  switch( data->dv.parm_type )
127  {
128  case PARM_LP_FROM: /* local-part of envelope-sender */
129  *p = 'l';
130  break;
131 
132  case PARM_ENV_FROM: /* envelope-sender */
133  *p = 's';
134  break;
135 
136  case PARM_DP_FROM: /* envelope-domain */
137  *p = 'o';
138  break;
139 
140  case PARM_CUR_DOM: /* current-domain */
141  *p = 'd';
142  break;
143 
144  case PARM_CLIENT_IP: /* SMTP client IP */
145  *p = 'i';
146  break;
147 
148  case PARM_CLIENT_IP_P: /* SMTP client IP (pretty) */
149  *p = 'c';
150  break;
151 
152  case PARM_TIME: /* time in UTC epoch secs */
153  if ( !is_mod )
154  return SPF_E_INVALID_VAR;
155  *p = 't';
156  break;
157 
158  case PARM_CLIENT_DOM: /* SMTP client domain name */
159  *p = 'p';
160  break;
161 
162  case PARM_CLIENT_VER: /* IP ver str - in-addr/ip6 */
163  *p = 'v';
164  break;
165 
166  case PARM_HELO_DOM: /* HELO/EHLO domain */
167  *p = 'h';
168  break;
169 
170  case PARM_REC_DOM: /* receiving domain */
171  *p = 'r';
172  break;
173 
174  default:
175  return SPF_E_INVALID_VAR;
176  break;
177  }
178  if ( data->dv.url_encode )
179  *p = toupper( *p );
180  p++;
181  if ( p_end - p <= 0 ) return SPF_E_INTERNAL_ERROR;
182 
183 
184  if ( data->dv.num_rhs )
185  {
186  len = snprintf( p, p_end - p, "%d", data->dv.num_rhs );
187  p += len;
188  if ( p_end - p <= 0 ) return SPF_E_INTERNAL_ERROR;
189  }
190 
191 
192  if ( p_end - p <= 8 ) return SPF_E_INTERNAL_ERROR;
193  if ( data->dv.rev )
194  *p++ = 'r';
195 
196  if ( data->dv.delim_dot
197  && ( data->dv.delim_dash
198  || data->dv.delim_plus
199  || data->dv.delim_equal
200  || data->dv.delim_bar
201  || data->dv.delim_under
202  )
203  )
204  *p++ = '.';
205  if ( data->dv.delim_dash )
206  *p++ = '-';
207  if ( data->dv.delim_plus )
208  *p++ = '+';
209  if ( data->dv.delim_equal )
210  *p++ = '=';
211  if ( data->dv.delim_bar )
212  *p++ = '|';
213  if ( data->dv.delim_under )
214  *p++ = '_';
215 
216  *p++ = '}';
217  if ( p_end - p <= 0 ) return SPF_E_INTERNAL_ERROR;
218  }
219  }
220 
221 
222  if ( cidr_data )
223  {
224  if ( cidr_data->dc.ipv4 )
225  {
226  len = snprintf( p, p_end - p, "/%d", cidr_data->dc.ipv4 );
227  p += len;
228  if ( p_end - p <= 0 ) return SPF_E_INTERNAL_ERROR;
229  }
230 
231  if ( cidr_data->dc.ipv6 )
232  {
233  len = snprintf( p, p_end - p, "//%d", cidr_data->dc.ipv6 );
234  p += len;
235  if ( p_end - p <= 0 ) return SPF_E_INTERNAL_ERROR;
236  }
237  }
238 
239  *p_p = p;
240  return SPF_E_SUCCESS;
241 }
242 
243 
245 SPF_record_stringify( SPF_record_t *spf_record, char **bufp, size_t *buflenp)
246 {
247  int i;
248  SPF_mech_t *mech;
249  SPF_mod_t *mod;
250 
251  SPF_data_t *data, *data_end;
252 
253  size_t len;
254  const char *p_err;
255  char *p, *p_end;
256 
257  char ip4_buf[ INET_ADDRSTRLEN ];
258  char ip6_buf[ INET6_ADDRSTRLEN ];
259 
260  int cidr_ok;
261  SPF_errcode_t err;
262 
263 #define debug spf_record->spf_server->debug
264 
265  SPF_ASSERT_NOTNULL(spf_record);
266 
267  /*
268  * make sure the return buffer is big enough
269  *
270  * The worse case for the version string:
271  * "v=spf1 " = 6 = 4
272  * The worst cases for mechanisms
273  * "ip4:111.222.333.444/31 " = 23 < 6 * 3.9
274  * "ip6:<full-ipv6-spec>/126 " = 49 < 18 * 2.8
275  * "-include:x " = 11 = 5 * 2.2
276  * "-all " = 5 = 2 * 2.5
277  *
278  * The worst case for modifiers:
279  * "a=%{i15r.-+=|_} " = 16 = 5 * 3.2
280  */
281 
282  len = sizeof( SPF_VER_STR )
283  + spf_record->mech_len * 4 + spf_record->mod_len * 4 /* data */
284  + sizeof( "\0" );
285 
286  err = SPF_recalloc(bufp, buflenp, len);
287  if (err != SPF_E_SUCCESS)
288  return err;
289 
290  p = *bufp;
291  p_end = *bufp + *buflenp;
292 
293  if (debug)
294  SPF_debugf("stringify: Buffer length is %lu\n", (unsigned long)*buflenp);
295 
296 
297  /*
298  * generate SPF version string
299  */
300  len = snprintf(p, p_end - p, "v=spf%d", spf_record->version);
301  p += len;
302  if (p_end - p <= 0)
303  return SPF_E_INTERNAL_ERROR;
304 
305 
306  /*
307  * generate mechanisms
308  */
309 
310  mech = spf_record->mech_first;
311  for (i = 0; i < spf_record->num_mech; i++) {
312  if (debug)
313  SPF_debugf("stringify: Handling mechanism %d/%d at %p",
314  i, spf_record->num_mech, mech);
315  if ( p_end - p <= 1 ) return SPF_E_INTERNAL_ERROR;
316  *p++ = ' ';
317 
318 
319  if ( p_end - p <= 1 ) return SPF_E_INTERNAL_ERROR;
320  switch( mech->prefix_type )
321  {
322  case PREFIX_PASS:
323  /* *p++ = '+'; */
324  break;
325 
326  case PREFIX_FAIL:
327  *p++ = '-';
328  break;
329 
330  case PREFIX_SOFTFAIL:
331  *p++ = '~';
332  break;
333 
334  case PREFIX_NEUTRAL:
335  *p++ = '?';
336  break;
337 
338  case PREFIX_UNKNOWN:
339  return SPF_E_RESULT_UNKNOWN;
340  break;
341 
342  default:
343  return SPF_E_INVALID_PREFIX;
344  break;
345  }
346 
347  if (debug)
348  SPF_debugf("Mechanism type is %d", mech->mech_type);
349 
350  switch( mech->mech_type )
351  {
352  case MECH_A:
353  len = snprintf( p, p_end - p, "a" );
354  break;
355 
356  case MECH_MX:
357  len = snprintf( p, p_end - p, "mx" );
358  break;
359 
360  case MECH_PTR:
361  len = snprintf( p, p_end - p, "ptr" );
362  break;
363 
364  case MECH_INCLUDE:
365  len = snprintf( p, p_end - p, "include" );
366  break;
367 
368  case MECH_IP4:
369  p_err = inet_ntop( AF_INET, SPF_mech_ip4_data( mech ),
370  ip4_buf, sizeof( ip4_buf ) );
371  if ( p_err == NULL )
372  return SPF_E_INTERNAL_ERROR;
373  if ( mech->mech_len )
374  len = snprintf( p, p_end - p, "ip4:%s/%d",
375  ip4_buf, mech->mech_len );
376  else
377  len = snprintf( p, p_end - p, "ip4:%s", ip4_buf );
378  break;
379 
380  case MECH_IP6:
381  p_err = inet_ntop( AF_INET6, SPF_mech_ip6_data( mech ),
382  ip6_buf, sizeof( ip6_buf ) );
383  if ( p_err == NULL )
384  return SPF_E_INTERNAL_ERROR;
385  if ( mech->mech_len )
386  len = snprintf( p, p_end - p, "ip6:%s/%d",
387  ip6_buf, mech->mech_len );
388  else
389  len = snprintf( p, p_end - p, "ip6:%s", ip6_buf );
390  break;
391 
392  case MECH_EXISTS:
393  len = snprintf( p, p_end - p, "exists" );
394  break;
395 
396  case MECH_ALL:
397  len = snprintf( p, p_end - p, "all" );
398  break;
399 
400  case MECH_REDIRECT:
401  len = snprintf( p, p_end - p, "redirect" );
402  break;
403 
404  default:
405  return SPF_E_UNKNOWN_MECH;
406  break;
407  }
408  p += len;
409  if ( p_end - p <= 0 ) return SPF_E_INTERNAL_ERROR;
410 
411  if (debug)
412  SPF_debugf("stringify: Buffer so far is %s", p);
413 
414  if ( mech->mech_type != MECH_IP4 && mech->mech_type != MECH_IP6 )
415  {
416  data = SPF_mech_data( mech );
417  data_end = SPF_mech_end_data( mech );
418 
419  if (SPF_mech_data_len(mech) > 0
420  /* We have an immediate string literal */
421  && (data->dc.parm_type != PARM_CIDR
422  /* Some data follows the CIDR */
423  || SPF_data_next( data ) < data_end)
424  ) {
425  *p++ = ':';
426  }
427 
428  cidr_ok = mech->mech_type == MECH_A || mech->mech_type == MECH_MX;
429  err = SPF_record_stringify_data(
430  data, data_end,
431  &p, p_end,
432  FALSE, cidr_ok, debug );
433 
434  if ( err != SPF_E_SUCCESS )
435  return err;
436  }
437 
438  mech = SPF_mech_next( mech );
439  }
440 
441 
442  /*
443  * generate modifiers
444  */
445 
446  mod = spf_record->mod_first;
447  for( i = 0; i < spf_record->num_mod; i++ )
448  {
449  if (debug)
450  SPF_debugf("stringify: Handling modifier %d/%d at %p",
451  i, spf_record->num_mod, mod);
452  if ( p_end - p <= 1 ) return SPF_E_INTERNAL_ERROR;
453  *p++ = ' ';
454 
455  len = snprintf( p, p_end - p, "%.*s=",
456  mod->name_len, SPF_mod_name( mod ) );
457  p += len;
458  if ( p_end - p <= 0 ) return SPF_E_INTERNAL_ERROR;
459 
460  data = SPF_mod_data( mod );
461  data_end = SPF_mod_end_data( mod );
462 
463  err = SPF_record_stringify_data(
464  data, data_end,
465  &p, p_end,
466  TRUE, TRUE, debug );
467 
468  if ( err != SPF_E_SUCCESS )
469  return err;
470 
471 
472  mod = SPF_mod_next( mod );
473  }
474 
475 
476 
477  *p++ = '\0';
478 
479  return SPF_E_SUCCESS;
480 }
#define SPF_VER_STR
Definition: spf.h:35
unsigned char len
Definition: spf_record.h:107
#define SPF_debugf
Definition: spf_log.h:80
#define MECH_ALL
Definition: spf_record.h:170
#define FALSE
Definition: spf_internal.h:24
#define PARM_ENV_FROM
Definition: spf_record.h:89
unsigned short url_encode
Definition: spf_record.h:120
unsigned char mech_type
Definition: spf_record.h:177
#define PREFIX_UNKNOWN
Definition: spf_record.h:157
#define PREFIX_SOFTFAIL
Definition: spf_record.h:155
#define MECH_REDIRECT
Definition: spf_record.h:171
#define PARM_TIME
Definition: spf_record.h:94
#define PARM_CLIENT_DOM
Definition: spf_record.h:95
#define PARM_LP_FROM
Definition: spf_record.h:88
#define PREFIX_PASS
Definition: spf_record.h:153
unsigned short name_len
Definition: spf_record.h:189
unsigned short delim_under
Definition: spf_record.h:126
#define PARM_CLIENT_IP_P
Definition: spf_record.h:93
unsigned short rev
Definition: spf_record.h:119
unsigned char parm_type
Definition: spf_record.h:106
#define MECH_PTR
Definition: spf_record.h:165
unsigned char ipv4
Definition: spf_record.h:133
unsigned char parm_type
Definition: spf_record.h:117
unsigned short delim_dot
Definition: spf_record.h:121
SPF_errcode_t
Definition: spf_response.h:118
SPF_data_str_t ds
Definition: spf_record.h:144
#define MECH_IP6
Definition: spf_record.h:168
#define PARM_CLIENT_VER
Definition: spf_record.h:96
SPF_errcode_t SPF_recalloc(char **bufp, size_t *buflenp, size_t buflen) __attribute__((warn_unused_result))
Definition: spf_utils.c:188
unsigned short delim_dash
Definition: spf_record.h:122
unsigned char ipv6
Definition: spf_record.h:134
#define PARM_HELO_DOM
Definition: spf_record.h:97
#define TRUE
Definition: spf_internal.h:23
#define debug
#define NULL
Definition: spf_internal.h:28
#define MECH_A
Definition: spf_record.h:163
unsigned char num_rhs
Definition: spf_record.h:118
#define MECH_MX
Definition: spf_record.h:164
#define MECH_IP4
Definition: spf_record.h:167
unsigned short delim_plus
Definition: spf_record.h:123
#define SPF_ASSERT_NOTNULL(x)
Definition: spf_log.h:118
#define MECH_EXISTS
Definition: spf_record.h:169
unsigned char prefix_type
Definition: spf_record.h:176
unsigned short delim_equal
Definition: spf_record.h:124
SPF_errcode_t SPF_record_stringify(SPF_record_t *spf_record, char **bufp, size_t *buflenp)
Definition: spf_id2str.c:245
unsigned short mech_len
Definition: spf_record.h:178
SPF_data_cidr_t dc
Definition: spf_record.h:145
#define PARM_CUR_DOM
Definition: spf_record.h:91
#define PARM_CLIENT_IP
Definition: spf_record.h:92
#define MECH_INCLUDE
Definition: spf_record.h:166
#define PARM_CIDR
Definition: spf_record.h:99
unsigned char parm_type
Definition: spf_record.h:132
#define PARM_STRING
Definition: spf_record.h:100
#define PREFIX_FAIL
Definition: spf_record.h:154
#define PARM_REC_DOM
Definition: spf_record.h:98
#define PARM_DP_FROM
Definition: spf_record.h:90
SPF_data_var_t dv
Definition: spf_record.h:143
#define PREFIX_NEUTRAL
Definition: spf_record.h:156
unsigned short delim_bar
Definition: spf_record.h:125