libspf2 1.2.11
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))
67static SPF_errcode_t
68SPF_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. */
80#endif
81 return SPF_E_SUCCESS;
82}
83
84static void
85SPF_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
101static void
102SPF_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
131SPF_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:
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:
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:
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
179SPF_server_t *
180SPF_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
199void
200SPF_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
215SPF_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
228SPF_server_set_sanitize(SPF_server_t *sp, int sanitize)
229{
230 sp->sanitize = sanitize;
231 return SPF_E_SUCCESS;
232}
233
235SPF_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
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
267SPF_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
316SPF_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;
350retry:
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 if (rr_type == ns_t_spf) {
387 rr_type = ns_t_txt;
388 goto retry;
389 }
390 spf_response->result = SPF_RESULT_TEMPERROR;
391 spf_response->reason = SPF_REASON_FAILURE;
392 return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
393 "Temporary DNS failure for '%s'.", domain);
394 // break;
395
396 case NO_RECOVERY:
397 if (spf_server->debug > 0)
398 SPF_debugf("get_record(%s): NO_RECOVERY", domain);
399 SPF_dns_rr_free(rr_txt);
400 if (rr_type == ns_t_spf) {
401 rr_type = ns_t_txt;
402 goto retry;
403 }
404 spf_response->result = SPF_RESULT_PERMERROR;
405 spf_response->reason = SPF_REASON_FAILURE;
406 return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
407 "Unrecoverable DNS failure for '%s'.", domain);
408 // break;
409
410 case NETDB_SUCCESS:
411 if (spf_server->debug > 0)
412 SPF_debugf("get_record(%s): NETDB_SUCCESS", domain);
413 break;
414
415 default:
416 if (spf_server->debug > 0)
417 SPF_debugf("get_record(%s): UNKNOWN_ERROR", domain);
418 herrno = rr_txt->herrno; // Avoid use-after-free
419 SPF_dns_rr_free(rr_txt);
420 if (rr_type == ns_t_spf) {
421 rr_type = ns_t_txt;
422 goto retry;
423 }
424 return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
425 "Unknown DNS failure for '%s': %d.",
426 domain, herrno);
427 // break;
428 }
429
430 if (rr_txt->num_rr == 0) {
431 SPF_dns_rr_free(rr_txt);
432 if (rr_type == ns_t_spf) {
433 rr_type = ns_t_txt;
434 goto retry;
435 }
436 return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
437 "No TXT records returned from DNS lookup for '%s'",
438 domain);
439 }
440
441 /* Actually, this could never be used uninitialised anyway. */
442 idx_found = 0;
443
444 /* check for multiple SPF records */
445 num_found = 0;
446 for (i = 0; i < rr_txt->num_rr; i++) {
447 /*
448 if (spf_server->debug > 1)
449 SPF_debugf("Comparing '%s' with '%s'",
450 SPF_VER_STR " ", rr_txt->rr[i]->txt);
451 */
452 if (strncasecmp(rr_txt->rr[i]->txt,
453 SPF_VER_STR, sizeof(SPF_VER_STR) - 1) == 0) {
454 char e = rr_txt->rr[i]->txt[sizeof(SPF_VER_STR) - 1];
455 if (e == ' ' || e == '\0') {
456 if (spf_server->debug > 0)
457 SPF_debugf("found SPF record: %s", rr_txt->rr[i]->txt);
458 num_found++;
459 idx_found = i;
460 }
461 }
462 }
463
464 if (num_found == 0) {
465 SPF_dns_rr_free(rr_txt);
466 if (rr_type == ns_t_spf) {
467 rr_type = ns_t_txt;
468 goto retry;
469 }
470 spf_response->result = SPF_RESULT_NONE;
471 spf_response->reason = SPF_REASON_FAILURE;
472 return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
473 "No SPF records for '%s'", domain);
474 }
475 if (num_found > 1) {
476 SPF_dns_rr_free(rr_txt);
477 // rfc4408 requires permerror here.
478 /* XXX This could be refactored with SPF_i_done. */
479 spf_response->result = SPF_RESULT_PERMERROR;
480 spf_response->reason = SPF_REASON_FAILURE;
482 "Multiple SPF records for '%s'", domain);
483 }
484
485 /* try to compile the SPF record */
486 err = SPF_record_compile(spf_server,
487 spf_response, spf_recordp,
488 rr_txt->rr[idx_found]->txt );
489 SPF_dns_rr_free(rr_txt);
490
491 /* FIXME: support multiple versions */
492 if (err != SPF_E_SUCCESS)
493 return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
494 "Failed to compile SPF record for '%s'", domain);
495
496 return SPF_E_SUCCESS;
497}
498
504#define SPF_ACCESS_INT(f) \
505 SPF_errcode_t SPF_server_set_ ## f(SPF_server_t *s, int n) { \
506 s->f = n; return SPF_E_SUCCESS; \
507 } \
508 int SPF_server_get_ ## f(SPF_server_t *s) { \
509 return s->f; \
510 }
511
517SPF_ACCESS_INT(max_dns_mech);
518SPF_ACCESS_INT(max_dns_ptr);
519SPF_ACCESS_INT(max_dns_mx);
int strncasecmp(const char *s1, const char *s2, size_t n)
Definition strncasecmp.c:11
ns_type
SPF_server_dnstype_t
Definition spf_server.h:72
@ SPF_DNS_RESOLV
Definition spf_server.h:73
@ SPF_DNS_CACHE
Definition spf_server.h:73
@ SPF_DNS_ZONE
Definition spf_server.h:73
A local DNS zone layer.
SPF_dns_server_t * SPF_dns_zone_new(SPF_dns_server_t *layer_below, const char *name, int debug)
#define ns_t_spf
Definition spf_dns.h:89
#define NO_DATA
Definition spf_dns.h:106
#define NO_RECOVERY
Definition spf_dns.h:105
#define HOST_NOT_FOUND
Definition spf_dns.h:103
#define NETDB_SUCCESS
Definition spf_dns.h:102
void SPF_dns_free(SPF_dns_server_t *spf_dns_server)
Definition spf_dns.c:116
#define ns_t_txt
Definition spf_dns.h:80
int SPF_dns_stat_t
Definition spf_dns.h:108
SPF_dns_rr_t * SPF_dns_lookup(SPF_dns_server_t *spf_dns_server, const char *domain, ns_type rr_type, int should_cache)
Definition spf_dns.c:133
#define TRY_AGAIN
Definition spf_dns.h:104
@ SPF_RESULT_PERMERROR
@ SPF_RESULT_NONE
@ SPF_RESULT_TEMPERROR
SPF_response_t * SPF_response_new(SPF_request_t *spf_request)
SPF_errcode_t SPF_response_add_error(SPF_response_t *rp, SPF_errcode_t code, const char *format,...)
void SPF_response_free(SPF_response_t *rp)
int SPF_response_messages(SPF_response_t *rp)
@ SPF_REASON_FAILURE
SPF_errcode_t
@ SPF_E_DNS_ERROR
@ SPF_E_INTERNAL_ERROR
@ SPF_E_NO_MEMORY
@ SPF_E_NOT_SPF
@ SPF_E_SUCCESS
@ SPF_E_MULTIPLE_RECORDS
void SPF_macro_free(SPF_macro_t *mac)
Definition spf_record.c:73
SPF_errcode_t SPF_record_compile(SPF_server_t *spf_server, SPF_response_t *spf_response, SPF_record_t **spf_recordp, const char *record)
void SPF_record_free(SPF_record_t *rp)
Definition spf_record.c:63
SPF_errcode_t SPF_record_compile_macro(SPF_server_t *spf_server, SPF_response_t *spf_response, SPF_macro_t **spf_macrop, const char *record)
#define SPF_VER_STR
Definition spf.h:35
#define SPF_DEFAULT_WHITELIST
Definition spf.h:63
#define SPF_DEFAULT_EXP
Definition spf.h:67
#define __attribute__(x)
Definition spf.h:17
SPF_dns_server_t * SPF_dns_cache_new(SPF_dns_server_t *layer_below, const char *name, int debug, int cache_bits)
SPF_dns_server_t * SPF_dns_resolv_new(SPF_dns_server_t *layer_below, const char *name, int debug)
void SPF_dns_rr_free(SPF_dns_rr_t *spfrr)
Definition spf_dns_rr.c:114
#define SPF_errorf
Definition spf_log.h:77
#define SPF_ASSERT_NOTNULL(x)
Definition spf_log.h:118
#define SPF_error(errmsg)
Definition spf_log.h:40
#define SPF_debugf
Definition spf_log.h:80
#define NULL
#define SPF_MAX_DNS_MECH
#define SPF_MAX_DNS_MX
#define TRUE
#define SPF_MAX_DNS_PTR
#define debug
SPF_server_t * SPF_server_new(SPF_server_dnstype_t dnstype, int debug)
Definition spf_server.c:132
#define SPF_ACCESS_INT(f)
Definition spf_server.c:504
SPF_errcode_t SPF_server_set_explanation(SPF_server_t *sp, const char *exp, SPF_response_t **spf_responsep)
Definition spf_server.c:235
void SPF_server_free(SPF_server_t *sp)
Definition spf_server.c:200
SPF_errcode_t SPF_server_get_record(SPF_server_t *spf_server, SPF_request_t *spf_request, SPF_response_t *spf_response, SPF_record_t **spf_recordp)
Definition spf_server.c:316
SPF_errcode_t SPF_server_set_sanitize(SPF_server_t *sp, int sanitize)
Definition spf_server.c:228
SPF_errcode_t SPF_server_set_localpolicy(SPF_server_t *sp, const char *policy, int use_default_whitelist, SPF_response_t **spf_responsep)
Definition spf_server.c:267
SPF_errcode_t SPF_server_set_rec_dom(SPF_server_t *sp, const char *dom)
Definition spf_server.c:215
#define HOST_NAME_MAX
Definition spf_server.c:50
SPF_server_t * SPF_server_new_dns(SPF_dns_server_t *dns, int debug)
Definition spf_server.c:180
SPF_dns_rr_data_t ** rr
Definition spf_dns_rr.h:60
SPF_dns_stat_t herrno
Definition spf_dns_rr.h:66