ze-filter  (ze-filter-0.8.0-develop-180218)
ze-dns.c
Go to the documentation of this file.
1 /*
2  *
3  * ze-filter - Mail Server Filter for sendmail
4  *
5  * Copyright (c) 2001-2018 - Jose-Marcio Martins da Cruz
6  *
7  * Auteur : Jose Marcio Martins da Cruz
8  * jose.marcio.mc@gmail.org
9  *
10  * Historique :
11  * Creation : janvier 2002
12  *
13  * This program is free software, but with restricted license :
14  *
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19  *
20  * More details about ze-filter license can be found at ze-filter
21  * web site : http://foss.jose-marcio.org
22  */
23 /*
24  * Copyright (c) 2000-2003 Sendmail, Inc. and its suppliers.
25  * All rights reserved.
26  *
27  * By using this file, you agree to the terms and conditions set
28  * forth in the LICENSE file which can be found at the top level of
29  * the sendmail distribution.
30  *
31  */
32 
33 /*
34  * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
35  * (Royal Institute of Technology, Stockholm, Sweden).
36  * All rights reserved.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  *
42  * 1. Redistributions of source code must retain the above copyright
43  * notice, this list of conditions and the following disclaimer.
44  *
45  * 2. Redistributions in binary form must reproduce the above copyright
46  * notice, this list of conditions and the following disclaimer in the
47  * documentation and/or other materials provided with the distribution.
48  *
49  * 3. Neither the name of the Institute nor the names of its contributors
50  * may be used to endorse or promote products derived from this software
51  * without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63  * SUCH DAMAGE.
64  */
65 
66 
67 #include <ze-sys.h>
68 
69 #include "ze-libjc.h"
70 
71 #include "ze-dns.h"
72 
73 #ifndef MAXHOSTNAMELEN
74 # define MAXHOSTNAMELEN 256
75 #endif
76 
77 
78 static struct stot
79 {
80  const char *st_name;
81  int st_type;
82 }
83 stot[] =
84 {
85  {
86  "A", T_A}
87  ,
88 # if NETINET6
89  {
90  "AAAA", T_AAAA}
91  ,
92 # endif /* NETINET6 */
93  {
94  "NS", T_NS}
95  ,
96  {
97  "CNAME", T_CNAME}
98  ,
99  {
100  "PTR", T_PTR}
101  ,
102  {
103  "MX", T_MX}
104  ,
105  {
106  "TXT", T_TXT}
107  ,
108  {
109  "AFSDB", T_AFSDB}
110  ,
111  {
112  "SRV", T_SRV}
113  ,
114  {
115  NULL, 0}
116 };
117 
118 static int dns_lookup_int(const char *, int, int, time_t, int,
119  DNS_REPLY_T *);
120 
121 /* ****************************************************************************
122  * *
123  * *
124  **************************************************************************** */
125 /*
126 ** DNS_STRING_TO_TYPE -- convert resource record name into type
127 **
128 ** Parameters:
129 ** name -- name of resource record type
130 **
131 ** Returns:
132 ** type if succeeded.
133 ** -1 otherwise.
134 */
135 
136 int
138  const char *name;
139 {
140  struct stot *p = stot;
141 
142  for (p = stot; p->st_name != NULL; p++)
143  if (strcasecmp(name, p->st_name) == 0)
144  return p->st_type;
145  return -1;
146 }
147 
148 /* ****************************************************************************
149  * *
150  * *
151  **************************************************************************** */
152 /*
153 ** DNS_TYPE_TO_STRING -- convert resource record type into name
154 **
155 ** Parameters:
156 ** type -- resource record type
157 **
158 ** Returns:
159 ** name if succeeded.
160 ** NULL otherwise.
161 */
162 
163 const char *
165  int type;
166 {
167  struct stot *p = stot;
168 
169  for (p = stot; p->st_name != NULL; p++)
170  if (type == p->st_type)
171  return p->st_name;
172  return NULL;
173 }
174 
175 /* ****************************************************************************
176  * *
177  * *
178  **************************************************************************** */
179 /*
180 ** DNS_FREE_DATA -- free all components of a DNS_REPLY_T
181 **
182 ** Parameters:
183 ** r -- pointer to DNS_REPLY_T
184 **
185 ** Returns:
186 ** none.
187 */
188 
189 void
191  DNS_REPLY_T *r;
192 {
193  RR_RECORD_T *rr;
194 
195  if (r == NULL)
196  return;
197 
198  if (r->dns_signature != SIGNATURE)
199  return;
200 
201  if (r->dns_r_q.dns_q_domain != NULL)
202  free(r->dns_r_q.dns_q_domain);
203  r->dns_r_q.dns_q_domain = NULL;
204  for (rr = r->dns_r_head; rr != NULL;)
205  {
206  RR_RECORD_T *tmp = rr;
207 
208  if (rr->rr_domain != NULL)
209  free(rr->rr_domain);
210  if (rr->rr_u.rr_data != NULL)
211  free(rr->rr_u.rr_data);
212  rr = rr->rr_next;
213  free(tmp);
214  }
215 
216  memset(r, 0, sizeof (*r));
217 }
218 
219 /* ****************************************************************************
220  * *
221  * *
222  **************************************************************************** */
223 
224 /*
225 ** PARSE_DNS_REPLY -- parse DNS reply data.
226 **
227 ** Parameters:
228 ** data -- pointer to dns data
229 ** len -- len of data
230 **
231 ** Returns:
232 ** pointer to DNS_REPLY_T if succeeded.
233 ** NULL otherwise.
234 */
235 
236 int
237 parse_dns_reply(reply, data, len)
238  DNS_REPLY_T *reply;
239  unsigned char *data;
240  long len;
241 {
242  unsigned char *p;
243  int status;
244  size_t l;
245  char host[MAXHOSTNAMELEN];
246  RR_RECORD_T **rr;
247 
248  if (reply == NULL)
249  return DNS_LOC_ERR;
250 
251  memset(reply, 0, sizeof (*reply));
252  reply->dns_signature = SIGNATURE;
253 
254  p = data;
255 
256  /* doesn't work on Crays? */
257  memcpy(&reply->dns_r_h, p, sizeof (reply->dns_r_h));
258  p += sizeof (reply->dns_r_h);
259  status = dn_expand(data, data + len, p, host, sizeof host);
260  if (status < 0)
261  {
262  dns_free_data(reply);
263  return DNS_LOC_ERR;
264  }
265  reply->dns_r_q.dns_q_domain = strdup(host);
266  if (reply->dns_r_q.dns_q_domain == NULL)
267  {
268  dns_free_data(reply);
269  return DNS_LOC_ERR;
270  }
271  p += status;
272  GETSHORT(reply->dns_r_q.dns_q_type, p);
273  GETSHORT(reply->dns_r_q.dns_q_class, p);
274  rr = &reply->dns_r_head;
275  while (p < data + len)
276  {
277  int type, class, ttl, size, txtlen;
278 
279  status = dn_expand(data, data + len, p, host, sizeof host);
280  if (status < 0)
281  {
282  dns_free_data(reply);
283  return DNS_LOC_ERR;
284  }
285  p += status;
286  GETSHORT(type, p);
287  GETSHORT(class, p);
288  GETLONG(ttl, p);
289  GETSHORT(size, p);
290  if (p + size > data + len)
291  {
292  /*
293  ** announced size of data exceeds length of
294  ** data paket: someone is cheating.
295  */
296 
297  syslog(LOG_WARNING,
298  "ERROR: DNS RDLENGTH=%d > data len=%ld", size, len - (p - data));
299  dns_free_data(reply);
300  return DNS_LOC_ERR;
301  }
302  *rr = (RR_RECORD_T *) malloc(sizeof (**rr));
303  if (*rr == NULL)
304  {
305  dns_free_data(reply);
306  return DNS_LOC_ERR;
307  }
308  memset(*rr, 0, sizeof (**rr));
309  (*rr)->rr_domain = strdup(host);
310  if ((*rr)->rr_domain == NULL)
311  {
312  dns_free_data(reply);
313  return DNS_LOC_ERR;
314  }
315  (*rr)->rr_type = type;
316  (*rr)->rr_class = class;
317  (*rr)->rr_ttl = ttl;
318  (*rr)->rr_size = size;
319  switch (type)
320  {
321  case T_NS:
322  case T_CNAME:
323  case T_PTR:
324  status = dn_expand(data, data + len, p, host, sizeof host);
325  if (status < 0)
326  {
327  dns_free_data(reply);
328  return DNS_LOC_ERR;
329  }
330  (*rr)->rr_u.rr_txt = strdup(host);
331  if ((*rr)->rr_u.rr_txt == NULL)
332  {
333  dns_free_data(reply);
334  return DNS_LOC_ERR;
335  }
336  break;
337 
338  case T_MX:
339  case T_AFSDB:
340  status = dn_expand(data, data + len, p + 2, host, sizeof host);
341  if (status < 0)
342  {
343  dns_free_data(reply);
344  return DNS_LOC_ERR;
345  }
346  l = strlen(host) + 1;
347  (*rr)->rr_u.rr_mx =
348  (MX_RECORD_T *) malloc(sizeof (*((*rr)->rr_u.rr_mx)) + l);
349  if ((*rr)->rr_u.rr_mx == NULL)
350  {
351  dns_free_data(reply);
352  return DNS_LOC_ERR;
353  }
354  (*rr)->rr_u.rr_mx->mx_r_preference = (p[0] << 8) | p[1];
355  (void) strlcpy((*rr)->rr_u.rr_mx->mx_r_domain, host, l);
356  break;
357 
358  case T_SRV:
359  status = dn_expand(data, data + len, p + 6, host, sizeof host);
360  if (status < 0)
361  {
362  dns_free_data(reply);
363  return DNS_LOC_ERR;
364  }
365  l = strlen(host) + 1;
366  (*rr)->rr_u.rr_srv = (SRV_RECORDT_T *)
367  malloc(sizeof (*((*rr)->rr_u.rr_srv)) + l);
368  if ((*rr)->rr_u.rr_srv == NULL)
369  {
370  dns_free_data(reply);
371  return DNS_LOC_ERR;
372  }
373  (*rr)->rr_u.rr_srv->srv_r_priority = (p[0] << 8) | p[1];
374  (*rr)->rr_u.rr_srv->srv_r_weight = (p[2] << 8) | p[3];
375  (*rr)->rr_u.rr_srv->srv_r_port = (p[4] << 8) | p[5];
376  (void) strlcpy((*rr)->rr_u.rr_srv->srv_r_target, host, l);
377  break;
378 
379  case T_TXT:
380 
381  /*
382  ** The TXT record contains the length as
383  ** leading byte, hence the value is restricted
384  ** to 255, which is less than the maximum value
385  ** of RDLENGTH (size). Nevertheless, txtlen
386  ** must be less than size because the latter
387  ** specifies the length of the entire TXT
388  ** record.
389  */
390 
391  txtlen = *p;
392  if (txtlen >= size)
393  {
394  syslog(LOG_WARNING,
395  "ERROR: DNS TXT record size=%d <= text len=%d", size, txtlen);
396  dns_free_data(reply);
397  return DNS_LOC_ERR;
398  }
399  (*rr)->rr_u.rr_txt = (char *) malloc(txtlen + 1);
400  if ((*rr)->rr_u.rr_txt == NULL)
401  {
402  dns_free_data(reply);
403  return DNS_LOC_ERR;
404  }
405  (void) strlcpy((*rr)->rr_u.rr_txt, (char *) p + 1, txtlen + 1);
406  break;
407 
408  default:
409  (*rr)->rr_u.rr_data = (unsigned char *) malloc(size);
410  if ((*rr)->rr_u.rr_data == NULL)
411  {
412  dns_free_data(reply);
413  return DNS_LOC_ERR;
414  }
415  (void) memcpy((*rr)->rr_u.rr_data, p, size);
416  break;
417  }
418  p += size;
419  rr = &(*rr)->rr_next;
420  }
421  *rr = NULL;
422 
423  return DNS_NO_ERR;
424 }
425 
426 /*
427 ** DNS_LOOKUP_INT -- perform dns map lookup (internal helper routine)
428 **
429 ** Parameters:
430 ** domain -- name to lookup
431 ** rr_class -- resource record class
432 ** rr_type -- resource record type
433 ** retrans -- retransmission timeout
434 ** retry -- number of retries
435 **
436 ** Returns:
437 ** result of lookup if succeeded.
438 ** NULL otherwise.
439 */
440 
441 #undef MT_RES
442 #if HAVE_RES_NQUERY || HAVE___RES_NQUERY
443 # define MT_RES 1
444 #endif
445 
446 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
447 
448 #if MT_RES
449 
450 
451 # define DNS_INIT_LOCK() MUTEX_LOCK(&mutex)
452 # define DNS_INIT_UNLOCK() MUTEX_UNLOCK(&mutex)
453 
454 # define DNS_LOCK()
455 # define DNS_UNLOCK()
456 
457 #else /* MT_RES */
458 
459 # define DNS_INIT_LOCK()
460 # define DNS_INIT_UNLOCK()
461 
462 #if !BSD_RES_THREAD_SAFE
463 
464 # define DNS_LOCK() MUTEX_LOCK(&mutex)
465 # define DNS_UNLOCK() MUTEX_UNLOCK(&mutex)
466 
467 #else /* BSD_RES_THREAD_SAFE */
468 # define DNS_LOCK()
469 # define DNS_UNLOCK()
470 #endif /* BSD_RES_THREAD_SAFE */
471 
472 #endif /* MT_RES */
473 
474 #define DNS_DEBUG 1
475 #undef DNS_DEBUG
476 
477 #if MT_RES
478 # define STATPM (*statp)
479 # define MAC_H_ERRNO (statp->res_h_errno)
480 #else
481 # define STATPM _res
482 # define MAC_H_ERRNO h_errno
483 #endif
484 
485 #define SM_SET_H_ERRNO(i) {MAC_H_ERRNO = (i);}
486 
487 #if HAVE_RES_NQUERY
488 # define jc_res_query(st,a,b,c,d,e) res_nquery(st, a, b, c, d, e)
489 #elif HAVE___RES_NQUERY
490 # define jc_res_query(st,a,b,c,d,e) __res_nquery(st, a, b, c, d, e)
491 #else
492 # define jc_res_query(st,a,b,c,d,e) res_query(a, b, c, d, e)
493 #endif
494 
495 #if HAVE_RES_NINIT
496 # define jc_res_init(st) res_ninit(st)
497 #elif HAVE___RES_NINIT
498 # define jc_res_init(st) __res_ninit(st)
499 #else
500 # define jc_res_init(st)
501 #endif
502 
503 #if HAVE_RES_NCLOSE
504 # define jc_res_close() res_nclose(st)
505 #elif HAVE___RES_NQUERY
506 # define jc_res_close() __res_nclose(st)
507 #else
508 # define jc_res_close()
509 #endif
510 
511 
512 static int
513 dns_lookup_int(domain, rr_class, rr_type, retrans, retry, r)
514  const char *domain;
515  int rr_class;
516  int rr_type;
517  time_t retrans;
518  int retry;
519  DNS_REPLY_T *r;
520 {
521  int len;
522  unsigned char reply[2048];
523  int result = DNS_NO_ERR;
524 
525  time_t save_retrans = 0;
526  int save_retry = 0;
527 
528 #if MT_RES
529  static bool statok = FALSE;
530  static struct __res_state statrs;
531  struct __res_state statr;
532  res_state statp = &statr;
533 #else
534  static bool statok = FALSE;
535  static int statrs;
536  int statr;
537  int *statp = &statr;
538 #endif /* MT_RES */
539 
540  if (r == NULL)
541  {
542  ZE_LogMsgError(0, "reply argument shall not be NULL");
543  return DNS_LOC_ERR;
544  }
545 #if MT_RES
546  if (!statok)
547  {
548  DNS_INIT_LOCK();
549  if (!statok)
550  {
551  memset(&statrs, 0, sizeof (statrs));
552  (void) jc_res_init(&statrs);
553  statok = TRUE;
554  }
555  DNS_INIT_UNLOCK();
556  }
557  statr = statrs;
558 #endif /* MT_RES */
559 
560  DNS_LOCK();
561 
562 #if DNS_DEBUG
563  {
564  ZE_MessageInfo(11, "dns_lookup(%s, %d, %s)\n", domain, rr_class,
565  dns_type_to_string(rr_type));
566  }
567 #endif
568 
569  if (retrans > 0)
570  {
571  save_retrans = STATPM.retrans;
572  STATPM.retrans = retrans;
573  }
574 
575  if (retry > 0)
576  {
577  save_retry = STATPM.retry;
578  STATPM.retry = retry;
579  }
580 
581  SM_SET_H_ERRNO(0);
582  errno = 0;
583 
584  len = jc_res_query(&statr, domain, rr_class, rr_type, reply, sizeof reply);
585 
586 #if DNS_DEBUG
587  {
588  ZE_MessageInfo(11, "dns_lookup(%s, %d, %s) --> %d\n",
589  domain, rr_class, dns_type_to_string(rr_type), len);
590  }
591 #endif
592 
593  if (retrans > 0)
594  STATPM.retrans = save_retrans;
595 
596  if (retry > 0)
597  STATPM.retry = save_retry;
598 
599  if (len < 0)
600  {
601  switch (MAC_H_ERRNO)
602  {
603  case NO_DATA:
604  /* FALLTHROUGH */
605  result = DNS_ERR_NOTFOUND;
606  break;
607  case NO_RECOVERY:
608  /* no MX data on this host */
609  result = DNS_ERR_NOTFOUND;
610  break;
611  case HOST_NOT_FOUND:
612  result = DNS_ERR_NOTFOUND;
613  break;
614  case TRY_AGAIN:
615  case -1:
616  /* couldn't connect to the name server */
617  /* it might come up later; better queue it up */
618  result = DNS_ERR_TMPFAIL;
619  break;
620  default:
621  result = DNS_ERR_SRV;
622  break;
623  }
624  }
625 
626  if (len >= 0)
627  result = parse_dns_reply(r, reply, len);
628 
629  DNS_UNLOCK();
630 
631  return result;
632 }
633 
634 /* ****************************************************************************
635  * *
636  * *
637  **************************************************************************** */
638 
639 int
640 dns_lookup(domain, type_name, retrans, retry, r)
641  const char *domain;
642  const char *type_name;
643  time_t retrans;
644  int retry;
645  DNS_REPLY_T *r;
646 {
647  int type;
648  int result = DNS_NO_ERR;
649 
650  if (r == NULL)
651  {
652  ZE_LogMsgError(0, "reply argument shall not be NULL");
653  return DNS_LOC_ERR;
654  }
655 
656  dns_free_data(r);
657  memset(r, 0, sizeof (*r));
659 
660  type = dns_string_to_type(type_name);
661  if (type == -1)
662  {
663  ZE_LogMsgError(0, "dns_lookup: unknown resource type: %s", type_name);
664  return DNS_LOC_ERR;
665  }
666 
667  result = dns_lookup_int(domain, C_IN, type, retrans, retry, r);
668  if (result < 0)
669  dns_free_data(r);
670  return result;
671 }
#define T_TXT
Definition: ze-dns.h:58
#define DNS_INIT_LOCK()
Definition: ze-dns.c:459
#define jc_res_init(st)
Definition: ze-dns.c:500
DNS_QUERY_T dns_r_q
Definition: ze-dns.h:125
#define MAXHOSTNAMELEN
Definition: ze-dns.c:74
pthread_mutex_t mutex
Definition: ze-connopen.c:63
#define STATPM
Definition: ze-dns.c:481
#define FALSE
Definition: macros.h:160
RR_RECORD_T * rr_next
Definition: ze-dns.h:113
char * dns_q_domain
Definition: ze-dns.h:72
#define strlcpy
Definition: zeString.h:32
void * rr_data
Definition: ze-dns.h:103
#define ZE_LogMsgError(level,...)
Definition: zeSyslog.h:113
HEADER dns_r_h
Definition: ze-dns.h:124
int dns_lookup(char *domain, const char *type_name, time_t retrans, int retry, DNS_REPLY_T *r) const
Definition: ze-dns.c:640
union resource_record::@0 rr_u
#define DNS_ERR_TMPFAIL
Definition: ze-dns.h:141
const char * dns_type_to_string(int type)
Definition: ze-dns.c:164
#define T_AFSDB
Definition: ze-dns.h:61
#define MAC_H_ERRNO
Definition: ze-dns.c:482
#define DNS_INIT_UNLOCK()
Definition: ze-dns.c:460
#define ZE_MessageInfo(level,...)
Definition: zeSyslog.h:90
#define TRUE
Definition: macros.h:157
void dns_free_data(DNS_REPLY_T *r)
Definition: ze-dns.c:190
unsigned int dns_q_class
Definition: ze-dns.h:74
uint32_t dns_signature
Definition: ze-dns.h:123
#define DNS_LOC_ERR
Definition: ze-dns.h:144
#define DNS_UNLOCK()
Definition: ze-dns.c:465
#define memcpy(d, s, n)
Definition: ze-sys.h:224
RR_RECORD_T * dns_r_head
Definition: ze-dns.h:126
char * rr_domain
Definition: ze-dns.h:96
#define DNS_ERR_SRV
Definition: ze-dns.h:143
unsigned int dns_q_type
Definition: ze-dns.h:73
#define T_SRV
Definition: ze-dns.h:64
#define DNS_LOCK()
Definition: ze-dns.c:464
int parse_dns_reply(DNS_REPLY_T *reply, unsigned char *data, long len)
Definition: ze-dns.c:237
#define SIGNATURE
Definition: ze-libjc.h:75
#define SM_SET_H_ERRNO(i)
Definition: ze-dns.c:485
int dns_string_to_type(char *name) const
Definition: ze-dns.c:137
char domain[]
#define DNS_NO_ERR
Definition: ze-dns.h:140
#define DNS_ERR_NOTFOUND
Definition: ze-dns.h:142
#define jc_res_query(st, a, b, c, d, e)
Definition: ze-dns.c:492