libspf2  1.2.11
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_spf:
183  case ns_t_txt:
184  if (rdlen < 1) {
185  SPF_debugf(ns_rr_type(rr) == ns_t_txt ? "TXT" : "SPF" ": rdlen too short: %lu", (unsigned long)rdlen);
186  break;
187  }
188  /* XXX I think this is wrong/unsafe. Shevek. */
189  /* XXX doesn't parse the different TXT "sections" */
190  SPF_debugf(ns_rr_type(rr) == ns_t_txt ? "TXT" : "SPF" ": (%lu) \"%.*s\"",
191  (unsigned long)rdlen, (int)rdlen - 1, rdata + 1);
192  break;
193 
194  case ns_t_ptr:
195  err = ns_name_uncompress(responsebuf,
196  responsebuf + responselen,
197  rdata,
198  name_buf, sizeof(name_buf));
199  if (err < 0) /* 0 or -1 */
200  SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
201  err, strerror(errno), errno);
202  else
203  SPF_debugf("PTR: %s", name_buf);
204  break;
205 
206  default:
207  SPF_debugf("not parsed: type: %d", ns_rr_type(rr));
208  break;
209  }
210 
211 }
212 
218 static SPF_dns_rr_t *
219 SPF_dns_resolv_lookup(SPF_dns_server_t *spf_dns_server,
220  const char *domain, ns_type rr_type, int should_cache)
221 {
222  SPF_dns_rr_t *spfrr;
223 
224  int err;
225  int i;
226  int nrec;
227  int cnt;
228 
229  u_char *responsebuf;
230  size_t responselen;
231 
232  ns_msg ns_handle;
233  ns_rr rr;
234 
235  int ns_sect;
236  // int num_ns_sect = sizeof( ns_sects ) / sizeof( *ns_sects );
237 
238  char name_buf[ NS_MAXDNAME ];
239 
240  size_t rdlen;
241  const u_char *rdata;
242 
243 #if HAVE_DECL_RES_NINIT
244  void *res_spec;
245  struct __res_state *res_state;
246 #endif
247 
248  SPF_ASSERT_NOTNULL(spf_dns_server);
249 
250 #if HAVE_DECL_RES_NINIT
251 
252  res_spec = pthread_getspecific(res_state_key);
253  if (res_spec == NULL) {
254  res_state = (struct __res_state *)
255  malloc(sizeof(struct __res_state));
256  /* XXX The interface doesn't allow to communicate back failure
257  * to allocate memory, but SPF_errorf aborts anyway. */
258  if (! res_state)
259  SPF_errorf("Failed to allocate %lu bytes for res_state",
260  (unsigned long)sizeof(struct __res_state));
261  memset(res_state, 0, sizeof(struct __res_state));
262  if (res_ninit(res_state) != 0)
263  SPF_error("Failed to call res_ninit()");
264  pthread_setspecific(res_state_key, (void *)res_state);
265  }
266  else {
267  res_state = (struct __res_state *)res_spec;
268  }
269 #endif
270 
271  responselen = 65536;
272  responsebuf = (u_char *)malloc(responselen);
273  if (! responsebuf)
274  return NULL; /* NULL always means OOM from DNS lookup. */
275  memset(responsebuf, 0, responselen);
276 
277  /*
278  * Retry the lookup until our response buffer is big enough.
279  *
280  * This loop repeats until either we fail a lookup or we succeed.
281  * The size of the response buffer is monotonic increasing, so eventually we
282  * must either succeed, or we try to malloc more RAM than we can.
283  *
284  * The Linux man pages do not describe res_nquery adequately. Solaris says:
285  *
286  * The res_nquery() and res_query() routines return a length that may be bigger
287  * than anslen. In that case, retry the query with a larger buf. The answer to the
288  * second query may be larger still], so it is recommended that you supply a buf
289  * larger than the answer returned by the previous query. answer must be large
290  * enough to receive a maximum UDP response from the server or parts of the answer
291  * will be silently discarded. The default maximum UDP response size is 512 bytes.
292  */
293  for (;;) {
294  int dns_len;
295 
296 #if HAVE_DECL_RES_NINIT
297  /* Resolve the name. */
298  dns_len = res_nquery(res_state, domain, ns_c_in, rr_type,
299  responsebuf, responselen);
300 #else
301  dns_len = res_query(domain, ns_c_in, rr_type,
302  responsebuf, responselen);
303 #endif
304 
305  if (dns_len < 0) {
306  /* We failed to perform a lookup. */
307  /* This block returns unconditionally. */
308  free(responsebuf);
309  if (spf_dns_server->debug)
310  SPF_debugf("query failed: err = %d %s (%d): %s",
311  dns_len, hstrerror(SPF_h_errno), SPF_h_errno,
312  domain);
313  if ((SPF_h_errno == HOST_NOT_FOUND) &&
314  (spf_dns_server->layer_below != NULL)) {
315  return SPF_dns_lookup(spf_dns_server->layer_below,
316  domain, rr_type, should_cache);
317  }
318  return SPF_dns_rr_new_init(spf_dns_server,
319  domain, rr_type, 0, SPF_h_errno);
320  }
321  else if (dns_len > responselen) {
322  free(responsebuf);
323  return NULL;
324  }
325  else {
326  /* We managed a lookup, and our buffer was large enough. */
327  responselen = dns_len;
328  break;
329  }
330  }
331 
332 
333 
334  /*
335  * initialize stuff
336  */
337  spfrr = SPF_dns_rr_new_init(spf_dns_server,
338  domain, rr_type, 0, NETDB_SUCCESS);
339  if (!spfrr) {
340  free(responsebuf);
341  return NULL;
342  }
343 
344  err = ns_initparse(responsebuf, responselen, &ns_handle);
345 
346  if (err < 0) { /* 0 or -1 */
347  if (spf_dns_server->debug)
348  SPF_debugf("ns_initparse failed: err = %d %s (%d)",
349  err, strerror(errno), errno);
350  free(responsebuf);
351  /* XXX Do we really want to return success with no data
352  * on parse failure? */
353  spfrr->herrno = NO_RECOVERY;
354  return spfrr;
355  }
356 
357 
358  if (spf_dns_server->debug > 1) {
359  SPF_debugf("msg id: %d", ns_msg_id(ns_handle));
360  SPF_debugf("ns_f_qr quest/resp: %d", ns_msg_getflag(ns_handle, ns_f_qr));
361  SPF_debugf("ns_f_opcode: %d", ns_msg_getflag(ns_handle, ns_f_opcode));
362  SPF_debugf("ns_f_aa auth ans: %d", ns_msg_getflag(ns_handle, ns_f_aa));
363  SPF_debugf("ns_f_tc truncated: %d", ns_msg_getflag(ns_handle, ns_f_tc));
364  SPF_debugf("ns_f_rd rec desire: %d", ns_msg_getflag(ns_handle, ns_f_rd));
365  SPF_debugf("ns_f_ra rec avail: %d", ns_msg_getflag(ns_handle, ns_f_ra));
366  SPF_debugf("ns_f_rcode: %d", ns_msg_getflag(ns_handle, ns_f_rcode));
367  }
368 
369 
370  /* FIXME the error handling from here on is suspect at best */
371  for (ns_sect = 0; ns_sect < num_ns_sect; ns_sect++) {
372  /* We pass this point if:
373  * - We are the 'answer' section.
374  * - We are debugging.
375  * Otherwise, we continue to the next section.
376  */
377  if (ns_sects[ns_sect].number != ns_s_an && spf_dns_server->debug <= 1)
378  continue;
379 
380  nrec = ns_msg_count(ns_handle, ns_sects[ns_sect].number);
381 
382  if (spf_dns_server->debug > 1)
383  SPF_debugf("%s: %d", ns_sects[ns_sect].name, nrec);
384 
385  spfrr->num_rr = 0;
386  cnt = 0;
387  for (i = 0; i < nrec; i++) {
388  err = ns_parserr(&ns_handle, ns_sects[ns_sect].number, i, &rr);
389  if (err < 0) { /* 0 or -1 */
390  if (spf_dns_server->debug > 1)
391  SPF_debugf("ns_parserr failed: err = %d %s (%d)",
392  err, strerror(errno), errno);
393  free(responsebuf);
394  /* XXX Do we really want to return partial data
395  * on parse failures? */
396  spfrr->herrno = NO_RECOVERY;
397  return spfrr;
398  }
399 
400  rdlen = ns_rr_rdlen(rr);
401  if (spf_dns_server->debug > 1)
402  SPF_debugf("name: %s type: %d class: %d ttl: %d rdlen: %lu",
403  ns_rr_name(rr), ns_rr_type(rr), ns_rr_class(rr),
404  ns_rr_ttl(rr), (unsigned long)rdlen);
405 
406  if (rdlen <= 0)
407  continue;
408 
409  rdata = ns_rr_rdata(rr);
410 
411  if (spf_dns_server->debug > 1)
412  SPF_dns_resolv_debug(spf_dns_server, rr,
413  responsebuf, responselen, rdata, rdlen);
414 
415  /* And now, if we aren't the answer section, we skip the section. */
416  if (ns_sects[ns_sect].number != ns_s_an)
417  continue;
418 
419  /* Now, we are in the answer section. */
420  if (ns_rr_type(rr) != spfrr->rr_type && ns_rr_type(rr) != ns_t_cname) {
421  SPF_debugf("unexpected rr type: %d expected: %d",
422  ns_rr_type(rr), rr_type);
423  continue;
424  }
425 
426  switch (ns_rr_type(rr)) {
427  case ns_t_a:
428  if (rdlen != 4) {
429  /* XXX Error handling. */
430  free(responsebuf);
431  return spfrr;
432  }
433  if (SPF_dns_rr_buf_realloc(spfrr, cnt,
434  sizeof(spfrr->rr[cnt]->a)) != SPF_E_SUCCESS) {
435  free(responsebuf);
436  /* XXX Do we really want to return partial data
437  * on out of memory conditions? */
438  return spfrr;
439  }
440  memcpy(&spfrr->rr[cnt]->a, rdata, sizeof(spfrr->rr[cnt]->a));
441  cnt++;
442  break;
443 
444  case ns_t_aaaa:
445  if (rdlen != 16) {
446  /* XXX Error handling. */
447  free(responsebuf);
448  return spfrr;
449  }
450  if (SPF_dns_rr_buf_realloc(spfrr, cnt,
451  sizeof(spfrr->rr[cnt]->aaaa)) != SPF_E_SUCCESS) {
452  free(responsebuf);
453  /* XXX Do we really want to return partial data
454  * on out of memory conditions? */
455  return spfrr;
456  }
457  memcpy(&spfrr->rr[cnt]->aaaa, rdata, sizeof(spfrr->rr[cnt]->aaaa));
458  cnt++;
459  break;
460 
461  case ns_t_ns:
462  break;
463 
464  case ns_t_cname:
465  /* FIXME: are CNAMEs always sent with the real RR? */
466  break;
467 
468  case ns_t_mx:
469  if (rdlen < NS_INT16SZ) {
470  /* XXX Error handling. */
471  free(responsebuf);
472  return spfrr;
473  }
474  err = ns_name_uncompress(responsebuf,
475  responsebuf + responselen,
476  rdata + NS_INT16SZ,
477  name_buf, sizeof(name_buf));
478  if (err < 0) { /* 0 or -1 */
479  if (spf_dns_server->debug > 1)
480  SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
481  err, strerror(errno), errno);
482  free(responsebuf);
483  /* XXX Do we really want to return partial data
484  * on parse error? */
485  return spfrr;
486  }
487 
488  if (SPF_dns_rr_buf_realloc(spfrr, cnt,
489  strlen(name_buf) + 1 ) != SPF_E_SUCCESS) {
490  free(responsebuf);
491  /* XXX Do we really want to return partial data
492  * on out of memory conditions? */
493  return spfrr;
494  }
495  strcpy(spfrr->rr[cnt]->mx, name_buf);
496  cnt++;
497  break;
498 
499  case ns_t_spf:
500  case ns_t_txt:
501  if (rdlen > 1) {
502  u_char *src, *dst;
503  size_t len;
504 
505  /* Just rdlen is enough because there is at least one
506  * length byte, which we do not copy. */
507  if (SPF_dns_rr_buf_realloc(spfrr, cnt, rdlen) != SPF_E_SUCCESS) {
508  free(responsebuf);
509  /* XXX Do we really want to return partial data
510  * on out of memory conditions? */
511  return spfrr;
512  }
513 
514  dst = (u_char *)spfrr->rr[cnt]->txt;
515  src = (u_char *)rdata;
516  len = 0;
517  while (rdlen > 0) {
518  /* Consume one byte into a length. */
519  len = *src;
520  src++;
521  rdlen--;
522 
523  /* Avoid buffer overrun if len is junk. */
524  /* XXX don't we rather want to flag this as error? */
525  if (len > rdlen)
526  len = rdlen;
527  memcpy(dst, src, len);
528 
529  /* Consume the data. */
530  src += len;
531  dst += len;
532  rdlen -= len;
533  }
534  *dst = '\0';
535  }
536  else {
537  if (SPF_dns_rr_buf_realloc(spfrr, cnt, 1) != SPF_E_SUCCESS) {
538  free(responsebuf);
539  /* XXX Do we really want to return partial data
540  * on out of memory conditions? */
541  return spfrr;
542  }
543  spfrr->rr[cnt]->txt[0] = '\0';
544  }
545 
546  cnt++;
547  break;
548 
549  case ns_t_ptr:
550  err = ns_name_uncompress(responsebuf,
551  responsebuf + responselen,
552  rdata,
553  name_buf, sizeof(name_buf));
554  if (err < 0) { /* 0 or -1 */
555  if (spf_dns_server->debug > 1)
556  SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
557  err, strerror(errno), errno);
558  free(responsebuf);
559  /* XXX Do we really want to return partial data
560  * on parse error? */
561  return spfrr;
562  }
563 
564  if (SPF_dns_rr_buf_realloc(spfrr, cnt,
565  strlen(name_buf) + 1) != SPF_E_SUCCESS) {
566  free(responsebuf);
567  /* XXX Do we really want to return partial data
568  * on out of memory conditions? */
569  return spfrr;
570  }
571  strcpy(spfrr->rr[cnt]->ptr, name_buf);
572  cnt++;
573  break;
574 
575  default:
576  break;
577  }
578  }
579 
580  spfrr->num_rr = cnt;
581  }
582 
583  if (spfrr->num_rr == 0)
584  spfrr->herrno = NO_DATA;
585 
586  free(responsebuf);
587  return spfrr;
588 }
589 
590 
591 static void
592 SPF_dns_resolv_free(SPF_dns_server_t *spf_dns_server)
593 {
594  SPF_ASSERT_NOTNULL(spf_dns_server);
595 
596 #if ! HAVE_DECL_RES_NINIT
597  res_close();
598 #endif
599 
600  free(spf_dns_server);
601 }
602 
603 SPF_dns_server_t *
604 SPF_dns_resolv_new(SPF_dns_server_t *layer_below,
605  const char *name, int debug)
606 {
607  SPF_dns_server_t *spf_dns_server;
608 
609 #if HAVE_DECL_RES_NINIT
610  pthread_once(&res_state_control, SPF_dns_resolv_init_key);
611 #else
612  if (res_init() != 0) {
613  SPF_warning("Failed to call res_init()");
614  return NULL;
615  }
616 #endif
617 
618  spf_dns_server = malloc(sizeof(SPF_dns_server_t));
619  if (spf_dns_server == NULL)
620  return NULL;
621  memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
622 
623  if (name == NULL)
624  name = "resolv";
625 
626  spf_dns_server->destroy = SPF_dns_resolv_free;
627  spf_dns_server->lookup = SPF_dns_resolv_lookup;
628  spf_dns_server->get_spf = NULL;
629  spf_dns_server->get_exp = NULL;
630  spf_dns_server->add_cache = NULL;
631  spf_dns_server->layer_below = layer_below;
632  spf_dns_server->name = name;
633  spf_dns_server->debug = debug;
634 
635  return spf_dns_server;
636 }
637 
638 #endif /* _WIN32 */
#define ns_msg_count(handle, section)
Definition: arpa_nameser.h:181
#define NS_INT16SZ
Definition: arpa_nameser.h:135
#define SPF_errorf
Definition: spf_log.h:77
#define NO_RECOVERY
Definition: spf_dns.h:105
#define ns_rr_type(rr)
Definition: arpa_nameser.h:197
#define SPF_ASSERT_NOTNULL(x)
Definition: spf_log.h:118
#define ns_msg_id(handle)
Definition: arpa_nameser.h:177
#define debug
#define NULL
Definition: spf_internal.h:28
ns_type
Definition: arpa_nameser.h:300
#define NS_MAXDNAME
Definition: arpa_nameser.h:127
#define ns_t_spf
Definition: spf_dns.h:89
struct in6_addr aaaa
Definition: spf_dns_rr.h:37
#define ns_rr_class(rr)
Definition: arpa_nameser.h:198
#define ns_rr_rdata(rr)
Definition: arpa_nameser.h:201
struct in_addr a
Definition: spf_dns_rr.h:33
SPF_dns_stat_t herrno
Definition: spf_dns_rr.h:66
SPF_dns_server_t * SPF_dns_resolv_new(SPF_dns_server_t *layer_below, const char *name, int debug)
#define HOST_NOT_FOUND
Definition: spf_dns.h:103
SPF_errcode_t SPF_dns_rr_buf_realloc(SPF_dns_rr_t *spfrr, int idx, size_t len)
Definition: spf_dns_rr.c:134
#define ns_msg_getflag
Definition: arpa_nameser.h:524
#define NO_DATA
Definition: spf_dns.h:106
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 SPF_warning(errmsg)
Definition: spf_log.h:45
#define ns_get16
Definition: arpa_nameser.h:525
#define ns_rr_name(rr)
Definition: arpa_nameser.h:196
SPF_dns_rr_data_t ** rr
Definition: spf_dns_rr.h:60
#define ns_rr_rdlen(rr)
Definition: arpa_nameser.h:200
#define SPF_h_errno
int ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr)
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 SPF_error(errmsg)
Definition: spf_log.h:40
#define SPF_debugf
Definition: spf_log.h:80
ns_type rr_type
Definition: spf_dns_rr.h:56
#define ns_rr_ttl(rr)
Definition: arpa_nameser.h:199
int ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, char *dst, size_t dstsiz)
ns_sect
Definition: arpa_nameser.h:146
#define NETDB_SUCCESS
Definition: spf_dns.h:102
int ns_initparse(const u_char *msg, int msglen, ns_msg *handle)