libspf2  1.2.10
spf_dns_zone.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 #ifdef STDC_HEADERS
19 # include <stdio.h> /* stdin / stdout */
20 # include <stdlib.h> /* malloc / free */
21 #endif
22 
23 
24 #ifdef HAVE_STRING_H
25 # include <string.h> /* strstr / strdup */
26 #else
27 # ifdef HAVE_STRINGS_H
28 # include <strings.h> /* strstr / strdup */
29 # endif
30 #endif
31 
32 #ifdef HAVE_MEMORY_H
33 #include <memory.h>
34 #endif
35 #if TIME_WITH_SYS_TIME
36 # include <sys/time.h>
37 # include <time.h>
38 #else
39 # if HAVE_SYS_TIME_H
40 # include <sys/time.h>
41 # else
42 # include <time.h>
43 # endif
44 #endif
45 #ifdef HAVE_NETDB_H
46 # include <netdb.h>
47 #endif
48 #include <ctype.h>
49 
50 
51 #include "spf.h"
52 #include "spf_dns.h"
53 #include "spf_internal.h"
54 #include "spf_dns_internal.h"
55 #include "spf_dns_zone.h"
56 
57 
73 typedef struct
74 {
76  int num_zone; /* This one really is an int. */
77  int zone_buf_len; /* This one really is an int. */
80 
81 
82 
83 static inline SPF_dns_zone_config_t *SPF_voidp2spfhook( void *hook )
84  { return (SPF_dns_zone_config_t *)hook; }
85 static inline void *SPF_spfhook2voidp( SPF_dns_zone_config_t *spfhook )
86  { return (void *)spfhook; }
87 
88 
89 
90 
95 static SPF_dns_rr_t *
96 SPF_dns_zone_find(SPF_dns_server_t *spf_dns_server,
97  const char *domain, ns_type rr_type,
98  int exact)
99 {
100  SPF_dns_zone_config_t *spfhook;
101  int i;
102 
103  spfhook = SPF_voidp2spfhook(spf_dns_server->hook);
104 
105  if (spf_dns_server->debug)
106  SPF_debugf("zone: Searching for RR %s (%d)", domain, rr_type);
107 
108  /* If the record we want or are adding starts with '*.' then it must match
109  * exactly. */
110  if (exact || strncmp(domain, "*.", 2) == 0) {
111  for (i = 0; i < spfhook->num_zone; i++) {
112  if (spfhook->zone[i]->rr_type == rr_type
113  && strcasecmp(spfhook->zone[i]->domain, domain) == 0)
114  return spfhook->zone[i];
115  }
116  if (spf_dns_server->debug)
117  SPF_debugf("zone: Exact not found");
118  }
119  else {
120  /* We are looking up a record, so lookup-matching semantics apply. */
121  size_t domain_len = strlen(domain);
122  /* Real resolver would strip trailing '.', so we have to.
123  * FIXME: doesn't handle wildcard cases - we don't use
124  * those in test suite. */
125  if (domain_len && domain[domain_len - 1] == '.')
126  --domain_len;
127 
128  for (i = 0; i < spfhook->num_zone; i++) {
129  if (spfhook->zone[i]->rr_type != rr_type
130  && spfhook->zone[i]->rr_type != ns_t_any) {
131  if (spf_dns_server->debug)
132  SPF_debugf("zone: Ignoring record rrtype %d",
133  spfhook->zone[i]->rr_type);
134  continue;
135  }
136 
137  if (strncmp(spfhook->zone[i]->domain, "*.", 2) == 0) {
138  size_t zdomain_len = strlen(spfhook->zone[i]->domain) - 2;
139  if ((zdomain_len <= domain_len)
140  && strncasecmp(
141  spfhook->zone[i]->domain + 2,
142  domain + (domain_len - zdomain_len),
143  zdomain_len) == 0)
144  return spfhook->zone[i];
145  }
146  else if (strncasecmp(
147  spfhook->zone[i]->domain,
148  domain,
149  domain_len) == 0 &&
150  strlen(spfhook->zone[i]->domain) == domain_len) {
151  return spfhook->zone[i];
152  }
153  }
154  if (spf_dns_server->debug)
155  SPF_debugf("zone: Non-exact not found");
156  }
157 
158  return NULL;
159 }
160 
161 
162 
163 static SPF_dns_rr_t *
164 SPF_dns_zone_lookup(SPF_dns_server_t *spf_dns_server,
165  const char *domain, ns_type rr_type, int should_cache)
166 {
167  SPF_dns_zone_config_t *spfhook;
168  SPF_dns_rr_t *spfrr;
169 
170  spfrr = SPF_dns_zone_find(spf_dns_server, domain, rr_type, FALSE);
171  if (spfrr) {
172  SPF_dns_rr_dup(&spfrr, spfrr);
173  return spfrr;
174  }
175 
176  if (spf_dns_server->layer_below) {
177  return SPF_dns_lookup(spf_dns_server->layer_below,
178  domain, rr_type, should_cache);
179  }
180 
181  spfhook = SPF_voidp2spfhook(spf_dns_server->hook);
182  SPF_dns_rr_dup(&spfrr, spfhook->nxdomain);
183 
184  return spfrr;
185 }
186 
187 
189 SPF_dns_zone_add_str(SPF_dns_server_t *spf_dns_server,
190  const char *domain, ns_type rr_type,
191  SPF_dns_stat_t herrno, const char *data)
192 {
193  SPF_dns_zone_config_t *spfhook;
194  SPF_dns_rr_t *spfrr;
195 
196  int err;
197  int cnt;
198 
199  if (rr_type == ns_t_any) {
200  if (data)
201  SPF_error("RR type ANY can not have data.");
202  if (herrno == NETDB_SUCCESS)
203  SPF_error("RR type ANY must return a DNS error code.");
204  }
205 
206  spfhook = SPF_voidp2spfhook(spf_dns_server->hook);
207 
208  /* try to find an existing record */
209  spfrr = SPF_dns_zone_find(spf_dns_server, domain, rr_type, TRUE);
210 
211  /* create a new record */
212  if ( spfrr == NULL ) {
213  /* First make sure we have space for it. */
214  if ( spfhook->num_zone == spfhook->zone_buf_len ) {
215  int new_len;
216  SPF_dns_rr_t **new_zone;
217  int i;
218 
219  new_len = spfhook->zone_buf_len
220  + (spfhook->zone_buf_len >> 2) + 4;
221  new_zone = realloc( spfhook->zone,
222  new_len * sizeof( *new_zone ) );
223  if ( new_zone == NULL )
224  return SPF_E_NO_MEMORY;
225 
226  for( i = spfhook->zone_buf_len; i < new_len; i++ )
227  new_zone[i] = NULL;
228 
229  spfhook->zone_buf_len = new_len;
230  spfhook->zone = new_zone;
231  }
232 
233  /* Now make the new record. */
234  spfrr = SPF_dns_rr_new_init(spf_dns_server,
235  domain, rr_type, 24*60*60, herrno);
236  if (spfrr == NULL)
237  return SPF_E_NO_MEMORY;
238  spfhook->zone[spfhook->num_zone] = spfrr;
239  spfhook->num_zone++;
240 
241  /* We succeeded with the add, but with no data. */
242  if (herrno != NETDB_SUCCESS)
243  return SPF_E_SUCCESS;
244  }
245 
246 #define SPF_RR_TRY_REALLOC(rr, i, s) do { \
247  SPF_errcode_t __err = SPF_dns_rr_buf_realloc(rr, i, s); \
248  if (__err != SPF_E_SUCCESS) return __err; \
249  } while(0)
250 
251  /*
252  * initialize stuff
253  */
254  cnt = spfrr->num_rr;
255 
256  switch (rr_type) {
257  case ns_t_a:
258  SPF_RR_TRY_REALLOC(spfrr, cnt, sizeof( spfrr->rr[cnt]->a ));
259  err = inet_pton( AF_INET, data, &spfrr->rr[cnt]->a );
260  if ( err <= 0 )
261  return SPF_E_INVALID_IP4;
262  break;
263 
264  case ns_t_aaaa:
265  SPF_RR_TRY_REALLOC(spfrr, cnt, sizeof( spfrr->rr[cnt]->aaaa ));
266  err = inet_pton( AF_INET6, data, &spfrr->rr[cnt]->aaaa );
267  if ( err <= 0 )
268  return SPF_E_INVALID_IP6;
269  break;
270 
271  case ns_t_mx:
272  /* Caller passes priority<sp>domain. We don't use or
273  * store priority, so discard it. */
274  while (isdigit(*data)) data++;
275  while (isspace(*data)) data++;
276  SPF_RR_TRY_REALLOC(spfrr, cnt, strlen( data ) + 1);
277  strcpy( spfrr->rr[cnt]->mx, data );
278  break;
279 
280  case ns_t_txt:
281  case ns_t_spf:
282  SPF_RR_TRY_REALLOC(spfrr, cnt, strlen( data ) + 1);
283  strcpy( spfrr->rr[cnt]->txt, data );
284  break;
285 
286  case ns_t_ptr:
287  SPF_RR_TRY_REALLOC(spfrr, cnt, strlen( data ) + 1);
288  strcpy( spfrr->rr[cnt]->ptr, data );
289  break;
290 
291  case ns_t_any:
292  if ( data )
293  SPF_error( "RR type ANY can not have data.");
294  if ( herrno == NETDB_SUCCESS )
295  SPF_error( "RR type ANY must return a DNS error code.");
296  SPF_error( "RR type ANY can not have multiple RR.");
297  break;
298 
299  default:
300  SPF_error( "Invalid RR type" );
301  break;
302  }
303 
304  spfrr->num_rr = cnt + 1;
305 
306  return SPF_E_SUCCESS;
307 }
308 
309 
310 
311 static void
312 SPF_dns_zone_free(SPF_dns_server_t *spf_dns_server)
313 {
314  SPF_dns_zone_config_t *spfhook;
315  int i;
316 
317  SPF_ASSERT_NOTNULL(spf_dns_server);
318  spfhook = SPF_voidp2spfhook(spf_dns_server->hook);
319 
320  if (spfhook) {
321  if (spfhook->zone) {
322  for (i = 0; i < spfhook->zone_buf_len; i++) {
323  if (spfhook->zone[i])
324  SPF_dns_rr_free(spfhook->zone[i]);
325  }
326  free(spfhook->zone);
327  }
328  if (spfhook->nxdomain)
329  SPF_dns_rr_free(spfhook->nxdomain);
330  free(spfhook);
331  }
332 
333  free(spf_dns_server);
334 }
335 
336 SPF_dns_server_t *
337 SPF_dns_zone_new(SPF_dns_server_t *layer_below,
338  const char *name, int debug)
339 {
340  SPF_dns_server_t *spf_dns_server;
341  SPF_dns_zone_config_t *spfhook;
342 
343  spf_dns_server = malloc(sizeof(SPF_dns_server_t));
344  if (spf_dns_server == NULL)
345  return NULL;
346  memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
347 
348  spf_dns_server->hook = malloc(sizeof(SPF_dns_zone_config_t));
349  if (spf_dns_server->hook == NULL) {
350  free(spf_dns_server);
351  return NULL;
352  }
353  memset(spf_dns_server->hook, 0, sizeof(SPF_dns_zone_config_t));
354 
355  if (name == NULL)
356  name = "zone";
357 
358  spf_dns_server->destroy = SPF_dns_zone_free;
359  spf_dns_server->lookup = SPF_dns_zone_lookup;
360  spf_dns_server->get_spf = NULL;
361  spf_dns_server->get_exp = NULL;
362  spf_dns_server->add_cache = NULL;
363  spf_dns_server->layer_below = layer_below;
364  spf_dns_server->name = name;
365  spf_dns_server->debug = debug;
366 
367  spfhook = SPF_voidp2spfhook(spf_dns_server->hook);
368 
369  spfhook->zone_buf_len = 32;
370  spfhook->num_zone = 0;
371  spfhook->zone = calloc(spfhook->zone_buf_len, sizeof(*spfhook->zone));
372 
373  if (spfhook->zone == NULL) {
374  free(spfhook);
375  free(spf_dns_server);
376  return NULL;
377  }
378 
379  /* XXX This might have to return NO_DATA sometimes. */
380  spfhook->nxdomain = SPF_dns_rr_new_init(spf_dns_server,
381  "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
382  if (spfhook->nxdomain == NULL) {
383  free(spfhook->zone);
384  free(spfhook);
385  free(spf_dns_server);
386  return NULL;
387  }
388 
389  return spf_dns_server;
390 }
#define ns_t_spf
Definition: spf_dns.h:89
#define HOST_NOT_FOUND
Definition: spf_dns.h:103
#define NETDB_SUCCESS
Definition: spf_dns.h:102
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
SPF_dns_rr_t * SPF_dns_rr_new_init(SPF_dns_server_t *spf_dns_server, const char *domain, ns_type rr_type, int ttl, SPF_dns_stat_t herrno)
Definition: spf_dns_rr.c:61
void SPF_dns_rr_free(SPF_dns_rr_t *spfrr)
Definition: spf_dns_rr.c:114
SPF_errcode_t SPF_dns_rr_dup(SPF_dns_rr_t **dstp, SPF_dns_rr_t *src)
Definition: spf_dns_rr.c:189
A local DNS zone layer.
#define NULL
Definition: spf_internal.h:28
#define TRUE
Definition: spf_internal.h:23
#define FALSE
Definition: spf_internal.h:24
#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
SPF_errcode_t
Definition: spf_response.h:119
@ SPF_E_INVALID_IP6
Definition: spf_response.h:140
@ SPF_E_NO_MEMORY
Definition: spf_response.h:121
@ SPF_E_INVALID_IP4
Definition: spf_response.h:139
@ SPF_E_SUCCESS
Definition: spf_response.h:120
ns_type
Definition: arpa_nameser.h:300
@ ns_t_ptr
Definition: arpa_nameser.h:313
@ ns_t_mx
Definition: arpa_nameser.h:316
@ ns_t_any
Definition: arpa_nameser.h:350
@ ns_t_a
Definition: arpa_nameser.h:302
@ ns_t_txt
Definition: arpa_nameser.h:317
@ ns_t_aaaa
Definition: arpa_nameser.h:329
int strncasecmp(const char *s1, const char *s2, size_t n)
Definition: strncasecmp.c:11
SPF_errcode_t SPF_dns_zone_add_str(SPF_dns_server_t *spf_dns_server, const char *domain, ns_type rr_type, SPF_dns_stat_t herrno, const char *data)
Definition: spf_dns_zone.c:189
#define SPF_RR_TRY_REALLOC(rr, i, s)
SPF_dns_server_t * SPF_dns_zone_new(SPF_dns_server_t *layer_below, const char *name, int debug)
Definition: spf_dns_zone.c:337
#define debug
struct in_addr a
Definition: spf_dns_rr.h:33
struct in6_addr aaaa
Definition: spf_dns_rr.h:37
SPF_dns_rr_data_t ** rr
Definition: spf_dns_rr.h:60
char * domain
Definition: spf_dns_rr.h:53
ns_type rr_type
Definition: spf_dns_rr.h:56
SPF_dns_rr_t ** zone
Definition: spf_dns_zone.c:75
SPF_dns_rr_t * nxdomain
Definition: spf_dns_zone.c:78