libspf2  1.2.10
spf_server.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 #ifdef STDC_HEADERS
20 # include <stdio.h> /* stdin / stdout */
21 # include <stdlib.h> /* malloc / free */
22 # include <ctype.h> /* isupper / tolower */
23 #endif
24 
25 #ifdef HAVE_INTTYPES_H
26 #include <inttypes.h>
27 #endif
28 
29 #ifdef HAVE_NETDB_H
30 #include <netdb.h>
31 #endif
32 
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 
37 #ifdef HAVE_STRING_H
38 # include <string.h> /* strstr / strdup */
39 #else
40 # ifdef HAVE_STRINGS_H
41 # include <strings.h> /* strstr / strdup */
42 # endif
43 #endif
44 
45 #ifdef HAVE_NETDB_H
46 # include <netdb.h>
47 #endif
48 
49 #ifndef HOST_NAME_MAX
50 #define HOST_NAME_MAX 255
51 #endif
52 
53 
54 #include "spf.h"
55 #include "spf_response.h"
56 #include "spf_record.h"
57 #include "spf_server.h"
58 #include "spf_dns.h"
59 #include "spf_dns_resolv.h"
60 #include "spf_dns_cache.h"
61 #include "spf_dns_zone.h"
62 #include "spf_internal.h"
63 #include "spf_dns_internal.h"
64 
65 
66 __attribute__((warn_unused_result))
67 static SPF_errcode_t
68 SPF_server_set_rec_dom_ghbn(SPF_server_t *sp)
69 {
70  sp->rec_dom = malloc(HOST_NAME_MAX);
71  if (! sp->rec_dom)
72  return SPF_E_NO_MEMORY;
73 #ifdef _WIN32
74  gethostnameFQDN(sp->rec_dom, HOST_NAME_MAX);
75  return 0; /* XXX FIXME? */
76 #else
77  if (gethostname(sp->rec_dom, HOST_NAME_MAX) < 0)
78  /* XXX Error using strerror. */
79  return SPF_E_INTERNAL_ERROR;
80 #endif
81  return SPF_E_SUCCESS;
82 }
83 
84 static void
85 SPF_server_new_common_pre(SPF_server_t *sp, int debug)
86 {
87  SPF_errcode_t err;
88 
89  memset(sp, 0, sizeof(SPF_server_t));
90 
91  sp->max_dns_mech = SPF_MAX_DNS_MECH;
92  sp->max_dns_ptr = SPF_MAX_DNS_PTR;
93  sp->max_dns_mx = SPF_MAX_DNS_MX;
94  sp->debug = debug;
95 
96  err = SPF_server_set_rec_dom_ghbn(sp);
97  if (err != SPF_E_SUCCESS)
98  SPF_error("Failed to set rec_dom using gethostname()");
99 }
100 
101 static void
102 SPF_server_new_common_post(SPF_server_t *sp)
103 {
104  SPF_response_t *spf_response;
105  SPF_errcode_t err;
106 
107  spf_response = NULL;
109  &spf_response);
110  if (err != SPF_E_SUCCESS)
111  SPF_errorf("Error code %d compiling default explanation", err);
112  if (spf_response) {
113  /* XXX Print the errors?! */
114  if (SPF_response_messages(spf_response) > 0)
115  SPF_error("Response errors compiling default explanation");
116  SPF_response_free(spf_response);
117  }
118 
119  spf_response = NULL;
120  err = SPF_server_set_localpolicy(sp, "", 0, &spf_response);
121  if (err != SPF_E_SUCCESS)
122  SPF_errorf("Error code %d compiling default whitelist", err);
123  if (spf_response) {
124  /* XXX Print the errors?! */
125  if (SPF_response_messages(spf_response) > 0)
126  SPF_error("Response errors compiling default whitelist");
127  SPF_response_free(spf_response);
128  }
129 }
130 
131 SPF_server_t *
133 {
134  SPF_dns_server_t *dc_r;
135  SPF_dns_server_t *dc_c;
136  SPF_dns_server_t *dc_z;
137  SPF_server_t *sp;
138 
139  sp = (SPF_server_t *)malloc(sizeof(SPF_server_t));
140  if (! sp)
141  return sp;
142  SPF_server_new_common_pre(sp, debug);
143  sp->destroy_resolver = 1;
144 
145  switch (dnstype) {
146  case SPF_DNS_RESOLV:
147  dc_r = SPF_dns_resolv_new(NULL, NULL, debug);
148  if (dc_r == NULL)
149  SPF_error("Failed to create DNS resolver");
150  sp->resolver = dc_r;
151  break;
152 
153  case SPF_DNS_CACHE:
154  dc_r = SPF_dns_resolv_new(NULL, NULL, debug);
155  if (dc_r == NULL)
156  SPF_error("Failed to create DNS resolver");
157  dc_c = SPF_dns_cache_new(dc_r, NULL, debug, 8);
158  if (dc_c == NULL)
159  SPF_error("Failed to create DNS cache");
160  sp->resolver = dc_c;
161  break;
162 
163  case SPF_DNS_ZONE:
164  dc_z = SPF_dns_zone_new(NULL, NULL, debug);
165  if (dc_z == NULL)
166  SPF_error("Failed to create DNS zone");
167  sp->resolver = dc_z;
168  break;
169 
170  default:
171  SPF_errorf("Unknown DNS type %d", dnstype);
172  }
173 
174  SPF_server_new_common_post(sp);
175 
176  return sp;
177 }
178 
179 SPF_server_t *
180 SPF_server_new_dns(SPF_dns_server_t *dns, int debug)
181 {
182  SPF_server_t *sp;
183 
184  sp = (SPF_server_t *)malloc(sizeof(SPF_server_t));
185  if (! sp)
186  return sp;
187  SPF_server_new_common_pre(sp, debug);
188  sp->destroy_resolver = 0;
189  sp->resolver = dns;
190  SPF_server_new_common_post(sp);
191  return sp;
192 }
193 
199 void
200 SPF_server_free(SPF_server_t *sp)
201 {
202  if (sp->resolver && sp->destroy_resolver)
203  SPF_dns_free(sp->resolver);
204  if (sp->local_policy)
205  SPF_record_free(sp->local_policy);
206  if (sp->explanation)
207  SPF_macro_free(sp->explanation);
208  if (sp->rec_dom)
209  free(sp->rec_dom);
210  /* XXX TODO: Free other parts of the structure. */
211  free(sp);
212 }
213 
215 SPF_server_set_rec_dom(SPF_server_t *sp, const char *dom)
216 {
217  if (sp->rec_dom)
218  free(sp->rec_dom);
219  if (dom == NULL)
220  return SPF_server_set_rec_dom_ghbn(sp);
221  sp->rec_dom = strdup(dom);
222  if (! sp->rec_dom)
223  return SPF_E_NO_MEMORY;
224  return SPF_E_SUCCESS;
225 }
226 
228 SPF_server_set_sanitize(SPF_server_t *sp, int sanitize)
229 {
230  sp->sanitize = sanitize;
231  return SPF_E_SUCCESS;
232 }
233 
235 SPF_server_set_explanation(SPF_server_t *sp, const char *exp,
236  SPF_response_t **spf_responsep)
237 {
238  SPF_macro_t *spf_macro = NULL;
239  SPF_errcode_t err;
240 
241  SPF_ASSERT_NOTNULL(exp);
242 
243  /* This is a hackish way to get the errors. */
244  if (! *spf_responsep) {
245  *spf_responsep = SPF_response_new(NULL);
246  if (! *spf_responsep)
247  return SPF_E_NO_MEMORY;
248  }
249 
250  err = SPF_record_compile_macro(sp, *spf_responsep, &spf_macro, exp);
251  if (err == SPF_E_SUCCESS) {
252  if (sp->explanation)
253  SPF_macro_free(sp->explanation);
254  sp->explanation = spf_macro;
255  }
256  else {
257  SPF_response_add_error(*spf_responsep, err,
258  "Failed to compile explanation '%s'", exp);
259  if (spf_macro)
260  SPF_macro_free(spf_macro);
261  }
262 
263  return err;
264 }
265 
267 SPF_server_set_localpolicy(SPF_server_t *sp, const char *policy,
268  int use_default_whitelist,
269  SPF_response_t **spf_responsep)
270 {
271  SPF_record_t *spf_record = NULL;
272  SPF_errcode_t err;
273  char *record;
274  size_t len;
275 
276  SPF_ASSERT_NOTNULL(policy);
277 
278  /* This is a hackish way to get the errors. */
279  if (! *spf_responsep) {
280  *spf_responsep = SPF_response_new(NULL);
281  if (! *spf_responsep)
282  return SPF_E_NO_MEMORY;
283  }
284 
285  len = sizeof(SPF_VER_STR) + strlen(policy) + 20;
286  if (use_default_whitelist)
287  len += sizeof(SPF_DEFAULT_WHITELIST);
288  record = malloc(len);
289  if (! record)
290  return SPF_E_NO_MEMORY;
291  if (use_default_whitelist)
292  snprintf(record, len, "%s %s %s",
294  else
295  snprintf(record, len, "%s %s", SPF_VER_STR, policy);
296 
297  err = SPF_record_compile(sp, *spf_responsep, &spf_record, record);
298  if (err == SPF_E_SUCCESS) {
299  if (sp->local_policy)
300  SPF_record_free(sp->local_policy);
301  sp->local_policy = spf_record;
302  }
303  else {
304  SPF_response_add_error(*spf_responsep, err,
305  "Failed to compile local policy '%s'", policy);
306  if (spf_record)
307  SPF_record_free(spf_record);
308  }
309 
310  free(record);
311 
312  return err;
313 }
314 
316 SPF_server_get_record(SPF_server_t *spf_server,
317  SPF_request_t *spf_request,
318  SPF_response_t *spf_response,
319  SPF_record_t **spf_recordp)
320 {
321  SPF_dns_server_t *resolver;
322  SPF_dns_rr_t *rr_txt;
323  SPF_errcode_t err;
324  SPF_dns_stat_t herrno;
325  const char *domain;
326  ns_type rr_type;
327  int num_found;
328  int idx_found;
329  int i;
330 
331 
332  SPF_ASSERT_NOTNULL(spf_server);
333  SPF_ASSERT_NOTNULL(spf_request);
334  SPF_ASSERT_NOTNULL(spf_server->resolver);
335  SPF_ASSERT_NOTNULL(spf_recordp);
336 
337  domain = spf_request->cur_dom;
338  SPF_ASSERT_NOTNULL(domain);
339 
340  *spf_recordp = NULL;
341 
342  resolver = spf_server->resolver;
343 
344  if (resolver->get_spf)
345  return resolver->get_spf(spf_server, spf_request,
346  spf_response, spf_recordp);
347 
348  /* I am VERY, VERY sorry about the gotos. Shevek. */
349  rr_type = ns_t_spf;
350 retry:
351  rr_txt = SPF_dns_lookup(resolver, domain, rr_type, TRUE);
352 
353  switch (rr_txt->herrno) {
354  case HOST_NOT_FOUND:
355  if (spf_server->debug > 0)
356  SPF_debugf("get_record(%s): HOST_NOT_FOUND", domain);
357  SPF_dns_rr_free(rr_txt);
358  if (rr_type == ns_t_spf) {
359  rr_type = ns_t_txt;
360  goto retry;
361  }
362  spf_response->result = SPF_RESULT_NONE;
363  spf_response->reason = SPF_REASON_FAILURE;
364  return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
365  "Host '%s' not found.", domain);
366  // break;
367 
368  case NO_DATA:
369  if (spf_server->debug > 0)
370  SPF_debugf("get_record(%s): NO_DATA", domain);
371  SPF_dns_rr_free(rr_txt);
372  if (rr_type == ns_t_spf) {
373  rr_type = ns_t_txt;
374  goto retry;
375  }
376  spf_response->result = SPF_RESULT_NONE;
377  spf_response->reason = SPF_REASON_FAILURE;
378  return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
379  "No DNS data for '%s'.", domain);
380  // break;
381 
382  case TRY_AGAIN:
383  if (spf_server->debug > 0)
384  SPF_debugf("get_record(%s): TRY_AGAIN", domain);
385  SPF_dns_rr_free(rr_txt);
386  return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
387  "Temporary DNS failure for '%s'.", domain);
388  // break;
389 
390  case NO_RECOVERY:
391  if (spf_server->debug > 0)
392  SPF_debugf("get_record(%s): NO_RECOERY", domain);
393  SPF_dns_rr_free(rr_txt);
394  return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
395  "Unrecoverable DNS failure for '%s'.", domain);
396  // break;
397 
398  case NETDB_SUCCESS:
399  if (spf_server->debug > 0)
400  SPF_debugf("get_record(%s): NETDB_SUCCESS", domain);
401  break;
402 
403  default:
404  if (spf_server->debug > 0)
405  SPF_debugf("get_record(%s): UNKNOWN_ERROR", domain);
406  herrno = rr_txt->herrno; // Avoid use-after-free
407  SPF_dns_rr_free(rr_txt);
408  return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
409  "Unknown DNS failure for '%s': %d.",
410  domain, herrno);
411  // break;
412  }
413 
414  if (rr_txt->num_rr == 0) {
415  SPF_dns_rr_free(rr_txt);
416  if (rr_type == ns_t_spf) {
417  rr_type = ns_t_txt;
418  goto retry;
419  }
420  return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
421  "No TXT records returned from DNS lookup for '%s'",
422  domain);
423  }
424 
425  /* Actually, this could never be used uninitialised anyway. */
426  idx_found = 0;
427 
428  /* check for multiple SPF records */
429  num_found = 0;
430  for (i = 0; i < rr_txt->num_rr; i++) {
431  /*
432  if (spf_server->debug > 1)
433  SPF_debugf("Comparing '%s' with '%s'",
434  SPF_VER_STR " ", rr_txt->rr[i]->txt);
435  */
436  if (strncasecmp(rr_txt->rr[i]->txt,
437  SPF_VER_STR, sizeof(SPF_VER_STR) - 1) == 0) {
438  char e = rr_txt->rr[i]->txt[sizeof(SPF_VER_STR) - 1];
439  if (e == ' ' || e == '\0') {
440  if (spf_server->debug > 0)
441  SPF_debugf("found SPF record: %s", rr_txt->rr[i]->txt);
442  num_found++;
443  idx_found = i;
444  }
445  }
446  }
447 
448  if (num_found == 0) {
449  SPF_dns_rr_free(rr_txt);
450  if (rr_type == ns_t_spf) {
451  rr_type = ns_t_txt;
452  goto retry;
453  }
454  spf_response->result = SPF_RESULT_NONE;
455  spf_response->reason = SPF_REASON_FAILURE;
456  return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
457  "No SPF records for '%s'", domain);
458  }
459  if (num_found > 1) {
460  SPF_dns_rr_free(rr_txt);
461  // rfc4408 requires permerror here.
462  /* XXX This could be refactored with SPF_i_done. */
463  spf_response->result = SPF_RESULT_PERMERROR;
464  spf_response->reason = SPF_REASON_FAILURE;
465  return SPF_response_add_error(spf_response, SPF_E_MULTIPLE_RECORDS,
466  "Multiple SPF records for '%s'", domain);
467  }
468 
469  /* try to compile the SPF record */
470  err = SPF_record_compile(spf_server,
471  spf_response, spf_recordp,
472  rr_txt->rr[idx_found]->txt );
473  SPF_dns_rr_free(rr_txt);
474 
475  /* FIXME: support multiple versions */
476  if (err != SPF_E_SUCCESS)
477  return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
478  "Failed to compile SPF record for '%s'", domain);
479 
480  return SPF_E_SUCCESS;
481 }
482 
488 #define SPF_ACCESS_INT(f) \
489  SPF_errcode_t SPF_server_set_ ## f(SPF_server_t *s, int n) { \
490  s->f = n; return SPF_E_SUCCESS; \
491  } \
492  int SPF_server_get_ ## f(SPF_server_t *s) { \
493  return s->f; \
494  }
495 
501 SPF_ACCESS_INT(max_dns_mech);
502 SPF_ACCESS_INT(max_dns_ptr);
503 SPF_ACCESS_INT(max_dns_mx);