libspf2 1.2.11
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
73typedef 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
83static inline SPF_dns_zone_config_t *SPF_voidp2spfhook( void *hook )
84 { return (SPF_dns_zone_config_t *)hook; }
85static inline void *SPF_spfhook2voidp( SPF_dns_zone_config_t *spfhook )
86 { return (void *)spfhook; }
87
88
89
90
95static SPF_dns_rr_t *
96SPF_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
163static SPF_dns_rr_t *
164SPF_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
189SPF_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
311static void
312SPF_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
336SPF_dns_server_t *
337SPF_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}
int strncasecmp(const char *s1, const char *s2, size_t n)
Definition strncasecmp.c:11
ns_type
A local DNS zone layer.
#define ns_t_spf
Definition spf_dns.h:89
#define ns_t_any
Definition spf_dns.h:83
#define ns_t_mx
Definition spf_dns.h:79
#define ns_t_aaaa
Definition spf_dns.h:81
#define ns_t_a
Definition spf_dns.h:75
#define ns_t_ptr
Definition spf_dns.h:78
#define HOST_NOT_FOUND
Definition spf_dns.h:103
#define NETDB_SUCCESS
Definition spf_dns.h:102
#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
SPF_errcode_t
@ SPF_E_INVALID_IP6
@ SPF_E_NO_MEMORY
@ SPF_E_INVALID_IP4
@ SPF_E_SUCCESS
void SPF_dns_rr_free(SPF_dns_rr_t *spfrr)
Definition spf_dns_rr.c:114
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
SPF_errcode_t SPF_dns_rr_dup(SPF_dns_rr_t **dstp, SPF_dns_rr_t *src)
Definition spf_dns_rr.c:189
#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 TRUE
#define FALSE
SPF_dns_server_t * SPF_dns_zone_new(SPF_dns_server_t *layer_below, const char *name, int debug)
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)
#define SPF_RR_TRY_REALLOC(rr, i, s)
#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
SPF_dns_rr_t * nxdomain