libspf2  1.2.10
spf_dns_resolv.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 
29 #ifndef _WIN32
30 
31 #include "spf_sys_config.h"
32 
33 #ifdef HAVE_ERRNO_H
34 #include <errno.h>
35 #endif
36 
37 #ifdef STDC_HEADERS
38 # include <stdio.h> /* stdin / stdout */
39 # include <stdlib.h> /* malloc / free */
40 #endif
41 
42 #ifdef HAVE_STRING_H
43 # include <string.h> /* strstr / strdup */
44 #else
45 # ifdef HAVE_STRINGS_H
46 # include <strings.h> /* strstr / strdup */
47 # endif
48 #endif
49 
50 #ifdef HAVE_RESOLV_H
51 # include <resolv.h> /* dn_skipname */
52 #endif
53 #ifdef HAVE_NETDB_H
54 # include <netdb.h>
55 #endif
56 
57 #ifdef HAVE_PTHREAD_H
58 # include <pthread.h>
59 #endif
60 
61 #include "spf.h"
62 #include "spf_dns.h"
63 #include "spf_internal.h"
64 #include "spf_dns_internal.h"
65 #include "spf_dns_resolv.h"
66 
72 static const struct res_sym ns_sects[] = {
73  { ns_s_qd, "QUESTION", "Question" },
74  { ns_s_an, "ANSWER", "Answer" },
75  { ns_s_ns, "AUTHORITY", "Authority" },
76  { ns_s_ar, "ADDITIONAL", "Additional" },
77 };
78 
79 static const int num_ns_sect = sizeof(ns_sects) / sizeof(*ns_sects);
80 
81 
82 #if HAVE_DECL_RES_NINIT
83 # define SPF_h_errno res_state->res_h_errno
84 #else
85 # define SPF_h_errno h_errno
86 #endif
87 
88 #if HAVE_DECL_RES_NINIT
89 static pthread_once_t res_state_control = PTHREAD_ONCE_INIT;
90 static pthread_key_t res_state_key;
91 
92 static void
93 SPF_dns_resolv_thread_term(void *arg)
94 {
95 #if HAVE_DECL_RES_NDESTROY
96  res_ndestroy( (struct __res_state *)arg );
97 #else
98  res_nclose( (struct __res_state *)arg );
99 #endif
100  free(arg);
101 }
102 
103 static void
104 SPF_dns_resolv_init_key(void)
105 {
106  pthread_key_create(&res_state_key, SPF_dns_resolv_thread_term);
107 }
108 #endif
109 
111 static void
112 SPF_dns_resolv_debug(SPF_dns_server_t *spf_dns_server, ns_rr rr,
113  const u_char *responsebuf, size_t responselen,
114  const u_char *rdata, size_t rdlen)
115 {
116  char ip4_buf[ INET_ADDRSTRLEN ];
117  char ip6_buf[ INET6_ADDRSTRLEN ];
118  char name_buf[ NS_MAXDNAME ];
119  int prio;
120  int err;
121 
122  switch (ns_rr_type(rr)) {
123  case ns_t_a:
124  if (rdlen != 4)
125  SPF_debugf("A: wrong rdlen %lu", (unsigned long)rdlen);
126  else
127  SPF_debugf("A: %s",
128  inet_ntop(AF_INET, rdata,
129  ip4_buf, sizeof(ip4_buf)));
130  break;
131 
132  case ns_t_aaaa:
133  if (rdlen != 16)
134  SPF_debugf("AAAA: wrong rdlen %lu", (unsigned long)rdlen);
135  else
136  SPF_debugf("AAAA: %s",
137  inet_ntop(AF_INET6, rdata,
138  ip6_buf, sizeof(ip6_buf)));
139  break;
140 
141  case ns_t_ns:
142  err = ns_name_uncompress(responsebuf,
143  responsebuf + responselen,
144  rdata,
145  name_buf, sizeof(name_buf));
146  if (err < 0) /* 0 or -1 */
147  SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
148  err, strerror(errno), errno);
149  else
150  SPF_debugf("NS: %s", name_buf);
151  break;
152 
153  case ns_t_cname:
154  err = ns_name_uncompress(responsebuf,
155  responsebuf + responselen,
156  rdata,
157  name_buf, sizeof(name_buf));
158  if ( err < 0 ) /* 0 or -1 */
159  SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
160  err, strerror(errno), errno );
161  else
162  SPF_debugf("CNAME: %s", name_buf);
163  break;
164 
165  case ns_t_mx:
166  if (rdlen < NS_INT16SZ) {
167  SPF_debugf("MX: rdlen too short: %lu", (unsigned long)rdlen);
168  break;
169  }
170  prio = ns_get16(rdata);
171  err = ns_name_uncompress(responsebuf,
172  responsebuf + responselen,
173  rdata + NS_INT16SZ,
174  name_buf, sizeof(name_buf));
175  if (err < 0) /* 0 or -1 */
176  SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
177  err, strerror(errno), errno);
178  else
179  SPF_debugf("MX: %d %s", prio, name_buf);
180  break;
181 
182  case ns_t_txt:
183  if (rdlen < 1) {
184  SPF_debugf("TXT: rdlen too short: %lu", (unsigned long)rdlen);
185  break;
186  }
187  /* XXX I think this is wrong/unsafe. Shevek. */
188  /* XXX doesn't parse the different TXT "sections" */
189  SPF_debugf("TXT: (%lu) \"%.*s\"",
190  (unsigned long)rdlen, (int)rdlen - 1, rdata + 1);
191  break;
192 
193  case ns_t_ptr:
194  err = ns_name_uncompress(responsebuf,
195  responsebuf + responselen,
196  rdata,
197  name_buf, sizeof(name_buf));
198  if (err < 0) /* 0 or -1 */
199  SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
200  err, strerror(errno), errno);
201  else
202  SPF_debugf("PTR: %s", name_buf);
203  break;
204 
205  default:
206  SPF_debugf("not parsed: type: %d", ns_rr_type(rr));
207  break;
208  }
209 
210 }
211 
217 static SPF_dns_rr_t *
218 SPF_dns_resolv_lookup(SPF_dns_server_t *spf_dns_server,
219  const char *domain, ns_type rr_type, int should_cache)
220 {
221  SPF_dns_rr_t *spfrr;
222 
223  int err;
224  int i;
225  int nrec;
226  int cnt;
227 
228  u_char *responsebuf;
229  size_t responselen;
230 
231  ns_msg ns_handle;
232  ns_rr rr;
233 
234  int ns_sect;
235  // int num_ns_sect = sizeof( ns_sects ) / sizeof( *ns_sects );
236 
237  char name_buf[ NS_MAXDNAME ];
238 
239  size_t rdlen;
240  const u_char *rdata;
241 
242 #if HAVE_DECL_RES_NINIT
243  void *res_spec;
244  struct __res_state *res_state;
245 #endif
246 
247  SPF_ASSERT_NOTNULL(spf_dns_server);
248 
249 #if HAVE_DECL_RES_NINIT
250 
251  res_spec = pthread_getspecific(res_state_key);
252  if (res_spec == NULL) {
253  res_state = (struct __res_state *)
254  malloc(sizeof(struct __res_state));
255  /* XXX The interface doesn't allow to communicate back failure
256  * to allocate memory, but SPF_errorf aborts anyway. */
257  if (! res_state)
258  SPF_errorf("Failed to allocate %lu bytes for res_state",
259  (unsigned long)sizeof(struct __res_state));
260  memset(res_state, 0, sizeof(struct __res_state));
261  if (res_ninit(res_state) != 0)
262  SPF_error("Failed to call res_ninit()");
263  pthread_setspecific(res_state_key, (void *)res_state);
264  }
265  else {
266  res_state = (struct __res_state *)res_spec;
267  }
268 #endif
269 
270  responselen = 2048;
271  responsebuf = (u_char *)malloc(responselen);
272  if (! responsebuf)
273  return NULL; /* NULL always means OOM from DNS lookup. */
274  memset(responsebuf, 0, responselen);
275 
276  /*
277  * Retry the lookup until our response buffer is big enough.
278  *
279  * This loop repeats until either we fail a lookup or we succeed.
280  * The size of the response buffer is monotonic increasing, so eventually we
281  * must either succeed, or we try to malloc more RAM than we can.
282  *
283  * The Linux man pages do not describe res_nquery adequately. Solaris says:
284  *
285  * The res_nquery() and res_query() routines return a length that may be bigger
286  * than anslen. In that case, retry the query with a larger buf. The answer to the
287  * second query may be larger still], so it is recommended that you supply a buf
288  * larger than the answer returned by the previous query. answer must be large
289  * enough to receive a maximum UDP response from the server or parts of the answer
290  * will be silently discarded. The default maximum UDP response size is 512 bytes.
291  */
292  for (;;) {
293  int dns_len;
294 
295 #if HAVE_DECL_RES_NINIT
296  /* Resolve the name. */
297  dns_len = res_nquery(res_state, domain, ns_c_in, rr_type,
298  responsebuf, responselen);
299 #else
300  dns_len = res_query(domain, ns_c_in, rr_type,
301  responsebuf, responselen);
302 #endif
303 
304  if (dns_len < 0) {
305  /* We failed to perform a lookup. */
306  /* This block returns unconditionally. */
307  free(responsebuf);
308  if (spf_dns_server->debug)
309  SPF_debugf("query failed: err = %d %s (%d): %s",
310  dns_len, hstrerror(SPF_h_errno), SPF_h_errno,
311  domain);
312  if ((SPF_h_errno == HOST_NOT_FOUND) &&
313  (spf_dns_server->layer_below != NULL)) {
314  return SPF_dns_lookup(spf_dns_server->layer_below,
315  domain, rr_type, should_cache);
316  }
317  return SPF_dns_rr_new_init(spf_dns_server,
318  domain, rr_type, 0, SPF_h_errno);
319  }
320  else if (dns_len > responselen) {
321  void *tmp;
322  /* We managed a lookup but our buffer was too small. */
323  responselen = dns_len + (dns_len >> 1);
324 #if 0
325  /* Sanity-trap - we should never hit this. */
326  if (responselen > 1048576) { /* One megabyte. */
327  free(responsebuf);
328  return SPF_dns_rr_new_init(spf_dns_server,
329  domain, rr_type, 0, SPF_h_errno);
330  }
331 #endif
332  tmp = realloc(responsebuf, responselen);
333  if (!tmp) {
334  free(responsebuf);
335  return NULL;
336  }
337  responsebuf = tmp;
338  }
339  else {
340  /* We managed a lookup, and our buffer was large enough. */
341  responselen = dns_len;
342  break;
343  }
344  }
345 
346 
347 
348  /*
349  * initialize stuff
350  */
351  spfrr = SPF_dns_rr_new_init(spf_dns_server,
352  domain, rr_type, 0, NETDB_SUCCESS);
353  if (!spfrr) {
354  free(responsebuf);
355  return NULL;
356  }
357 
358  err = ns_initparse(responsebuf, responselen, &ns_handle);
359 
360  if (err < 0) { /* 0 or -1 */
361  if (spf_dns_server->debug)
362  SPF_debugf("ns_initparse failed: err = %d %s (%d)",
363  err, strerror(errno), errno);
364  free(responsebuf);
365  /* XXX Do we really want to return success with no data
366  * on parse failure? */
367  spfrr->herrno = NO_RECOVERY;
368  return spfrr;
369  }
370 
371 
372  if (spf_dns_server->debug > 1) {
373  SPF_debugf("msg id: %d", ns_msg_id(ns_handle));
374  SPF_debugf("ns_f_qr quest/resp: %d", ns_msg_getflag(ns_handle, ns_f_qr));
375  SPF_debugf("ns_f_opcode: %d", ns_msg_getflag(ns_handle, ns_f_opcode));
376  SPF_debugf("ns_f_aa auth ans: %d", ns_msg_getflag(ns_handle, ns_f_aa));
377  SPF_debugf("ns_f_tc truncated: %d", ns_msg_getflag(ns_handle, ns_f_tc));
378  SPF_debugf("ns_f_rd rec desire: %d", ns_msg_getflag(ns_handle, ns_f_rd));
379  SPF_debugf("ns_f_ra rec avail: %d", ns_msg_getflag(ns_handle, ns_f_ra));
380  SPF_debugf("ns_f_rcode: %d", ns_msg_getflag(ns_handle, ns_f_rcode));
381  }
382 
383 
384  /* FIXME the error handling from here on is suspect at best */
385  for (ns_sect = 0; ns_sect < num_ns_sect; ns_sect++) {
386  /* We pass this point if:
387  * - We are the 'answer' section.
388  * - We are debugging.
389  * Otherwise, we continue to the next section.
390  */
391  if (ns_sects[ns_sect].number != ns_s_an && spf_dns_server->debug <= 1)
392  continue;
393 
394  nrec = ns_msg_count(ns_handle, ns_sects[ns_sect].number);
395 
396  if (spf_dns_server->debug > 1)
397  SPF_debugf("%s: %d", ns_sects[ns_sect].name, nrec);
398 
399  spfrr->num_rr = 0;
400  cnt = 0;
401  for (i = 0; i < nrec; i++) {
402  err = ns_parserr(&ns_handle, ns_sects[ns_sect].number, i, &rr);
403  if (err < 0) { /* 0 or -1 */
404  if (spf_dns_server->debug > 1)
405  SPF_debugf("ns_parserr failed: err = %d %s (%d)",
406  err, strerror(errno), errno);
407  free(responsebuf);
408  /* XXX Do we really want to return partial data
409  * on parse failures? */
410  spfrr->herrno = NO_RECOVERY;
411  return spfrr;
412  }
413 
414  rdlen = ns_rr_rdlen(rr);
415  if (spf_dns_server->debug > 1)
416  SPF_debugf("name: %s type: %d class: %d ttl: %d rdlen: %lu",
417  ns_rr_name(rr), ns_rr_type(rr), ns_rr_class(rr),
418  ns_rr_ttl(rr), (unsigned long)rdlen);
419 
420  if (rdlen <= 0)
421  continue;
422 
423  rdata = ns_rr_rdata(rr);
424 
425  if (spf_dns_server->debug > 1)
426  SPF_dns_resolv_debug(spf_dns_server, rr,
427  responsebuf, responselen, rdata, rdlen);
428 
429  /* And now, if we aren't the answer section, we skip the section. */
430  if (ns_sects[ns_sect].number != ns_s_an)
431  continue;
432 
433  /* Now, we are in the answer section. */
434  if (ns_rr_type(rr) != spfrr->rr_type && ns_rr_type(rr) != ns_t_cname) {
435  SPF_debugf("unexpected rr type: %d expected: %d",
436  ns_rr_type(rr), rr_type);
437  continue;
438  }
439 
440  switch (ns_rr_type(rr)) {
441  case ns_t_a:
442  if (rdlen != 4) {
443  /* XXX Error handling. */
444  free(responsebuf);
445  return spfrr;
446  }
447  if (SPF_dns_rr_buf_realloc(spfrr, cnt,
448  sizeof(spfrr->rr[cnt]->a)) != SPF_E_SUCCESS) {
449  free(responsebuf);
450  /* XXX Do we really want to return partial data
451  * on out of memory conditions? */
452  return spfrr;
453  }
454  memcpy(&spfrr->rr[cnt]->a, rdata, sizeof(spfrr->rr[cnt]->a));
455  cnt++;
456  break;
457 
458  case ns_t_aaaa:
459  if (rdlen != 16) {
460  /* XXX Error handling. */
461  free(responsebuf);
462  return spfrr;
463  }
464  if (SPF_dns_rr_buf_realloc(spfrr, cnt,
465  sizeof(spfrr->rr[cnt]->aaaa)) != SPF_E_SUCCESS) {
466  free(responsebuf);
467  /* XXX Do we really want to return partial data
468  * on out of memory conditions? */
469  return spfrr;
470  }
471  memcpy(&spfrr->rr[cnt]->aaaa, rdata, sizeof(spfrr->rr[cnt]->aaaa));
472  cnt++;
473  break;
474 
475  case ns_t_ns:
476  break;
477 
478  case ns_t_cname:
479  /* FIXME: are CNAMEs always sent with the real RR? */
480  break;
481 
482  case ns_t_mx:
483  if (rdlen < NS_INT16SZ) {
484  /* XXX Error handling. */
485  free(responsebuf);
486  return spfrr;
487  }
488  err = ns_name_uncompress(responsebuf,
489  responsebuf + responselen,
490  rdata + NS_INT16SZ,
491  name_buf, sizeof(name_buf));
492  if (err < 0) { /* 0 or -1 */
493  if (spf_dns_server->debug > 1)
494  SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
495  err, strerror(errno), errno);
496  free(responsebuf);
497  /* XXX Do we really want to return partial data
498  * on parse error? */
499  return spfrr;
500  }
501 
502  if (SPF_dns_rr_buf_realloc(spfrr, cnt,
503  strlen(name_buf) + 1 ) != SPF_E_SUCCESS) {
504  free(responsebuf);
505  /* XXX Do we really want to return partial data
506  * on out of memory conditions? */
507  return spfrr;
508  }
509  strcpy(spfrr->rr[cnt]->mx, name_buf);
510  cnt++;
511  break;
512 
513  case ns_t_txt:
514  if (rdlen > 1) {
515  u_char *src, *dst;
516  size_t len;
517 
518  /* Just rdlen is enough because there is at least one
519  * length byte, which we do not copy. */
520  if (SPF_dns_rr_buf_realloc(spfrr, cnt, rdlen) != SPF_E_SUCCESS) {
521  free(responsebuf);
522  /* XXX Do we really want to return partial data
523  * on out of memory conditions? */
524  return spfrr;
525  }
526 
527  dst = (u_char *)spfrr->rr[cnt]->txt;
528  src = (u_char *)rdata;
529  len = 0;
530  while (rdlen > 0) {
531  /* Consume one byte into a length. */
532  len = *src;
533  src++;
534  rdlen--;
535 
536  /* Avoid buffer overrun if len is junk. */
537  /* XXX don't we rather want to flag this as error? */
538  if (len > rdlen)
539  len = rdlen;
540  memcpy(dst, src, len);
541 
542  /* Consume the data. */
543  src += len;
544  dst += len;
545  rdlen -= len;
546  }
547  *dst = '\0';
548  }
549  else {
550  if (SPF_dns_rr_buf_realloc(spfrr, cnt, 1) != SPF_E_SUCCESS) {
551  free(responsebuf);
552  /* XXX Do we really want to return partial data
553  * on out of memory conditions? */
554  return spfrr;
555  }
556  spfrr->rr[cnt]->txt[0] = '\0';
557  }
558 
559  cnt++;
560  break;
561 
562  case ns_t_ptr:
563  err = ns_name_uncompress(responsebuf,
564  responsebuf + responselen,
565  rdata,
566  name_buf, sizeof(name_buf));
567  if (err < 0) { /* 0 or -1 */
568  if (spf_dns_server->debug > 1)
569  SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
570  err, strerror(errno), errno);
571  free(responsebuf);
572  /* XXX Do we really want to return partial data
573  * on parse error? */
574  return spfrr;
575  }
576 
577  if (SPF_dns_rr_buf_realloc(spfrr, cnt,
578  strlen(name_buf) + 1) != SPF_E_SUCCESS) {
579  free(responsebuf);
580  /* XXX Do we really want to return partial data
581  * on out of memory conditions? */
582  return spfrr;
583  }
584  strcpy(spfrr->rr[cnt]->ptr, name_buf);
585  cnt++;
586  break;
587 
588  default:
589  break;
590  }
591  }
592 
593  spfrr->num_rr = cnt;
594  }
595 
596  if (spfrr->num_rr == 0)
597  spfrr->herrno = NO_DATA;
598 
599  free(responsebuf);
600  return spfrr;
601 }
602 
603 
604 static void
605 SPF_dns_resolv_free(SPF_dns_server_t *spf_dns_server)
606 {
607  SPF_ASSERT_NOTNULL(spf_dns_server);
608 
609 #if ! HAVE_DECL_RES_NINIT
610  res_close();
611 #endif
612 
613  free(spf_dns_server);
614 }
615 
616 SPF_dns_server_t *
617 SPF_dns_resolv_new(SPF_dns_server_t *layer_below,
618  const char *name, int debug)
619 {
620  SPF_dns_server_t *spf_dns_server;
621 
622 #if HAVE_DECL_RES_NINIT
623  pthread_once(&res_state_control, SPF_dns_resolv_init_key);
624 #else
625  if (res_init() != 0) {
626  SPF_warning("Failed to call res_init()");
627  return NULL;
628  }
629 #endif
630 
631  spf_dns_server = malloc(sizeof(SPF_dns_server_t));
632  if (spf_dns_server == NULL)
633  return NULL;
634  memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
635 
636  if (name == NULL)
637  name = "resolv";
638 
639  spf_dns_server->destroy = SPF_dns_resolv_free;
640  spf_dns_server->lookup = SPF_dns_resolv_lookup;
641  spf_dns_server->get_spf = NULL;
642  spf_dns_server->get_exp = NULL;
643  spf_dns_server->add_cache = NULL;
644  spf_dns_server->layer_below = layer_below;
645  spf_dns_server->name = name;
646  spf_dns_server->debug = debug;
647 
648  return spf_dns_server;
649 }
650 
651 #endif /* _WIN32 */
#define debug
#define SPF_errorf
Definition: spf_log.h:77
SPF_dns_stat_t herrno
Definition: spf_dns_rr.h:66
#define NULL
Definition: spf_internal.h:28
#define SPF_h_errno
#define NETDB_SUCCESS
Definition: spf_dns.h:102
SPF_dns_server_t * SPF_dns_resolv_new(SPF_dns_server_t *layer_below, const char *name, int debug)
#define SPF_ASSERT_NOTNULL(x)
Definition: spf_log.h:118
#define ns_t_ns
Definition: spf_dns.h:76
#define ns_t_a
Definition: spf_dns.h:75
struct in_addr a
Definition: spf_dns_rr.h:33
SPF_errcode_t SPF_dns_rr_buf_realloc(SPF_dns_rr_t *spfrr, int idx, size_t len)
Definition: spf_dns_rr.c:134
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
struct in6_addr aaaa
Definition: spf_dns_rr.h:37
ns_type rr_type
Definition: spf_dns_rr.h:56
#define ns_t_cname
Definition: spf_dns.h:77
#define ns_t_aaaa
Definition: spf_dns.h:81
#define ns_t_ptr
Definition: spf_dns.h:78
#define NO_DATA
Definition: spf_dns.h:106
int ns_type
Definition: spf_dns.h:85
#define SPF_debugf
Definition: spf_log.h:80
#define NO_RECOVERY
Definition: spf_dns.h:105
#define SPF_warning(errmsg)
Definition: spf_log.h:45
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
#define ns_t_mx
Definition: spf_dns.h:79
#define SPF_error(errmsg)
Definition: spf_log.h:40
#define ns_t_txt
Definition: spf_dns.h:80
SPF_dns_rr_data_t ** rr
Definition: spf_dns_rr.h:60
#define HOST_NOT_FOUND
Definition: spf_dns.h:103