Diff for /embedaddon/dnsmasq/src/forward.c between versions 1.1 and 1.1.1.3

version 1.1, 2013/07/29 19:37:40 version 1.1.1.3, 2016/11/02 09:57:01
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley
   
    This program is free software; you can redistribute it and/or modify     This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by     it under the terms of the GNU General Public License as published by
Line 16 Line 16
   
 #include "dnsmasq.h"  #include "dnsmasq.h"
   
static struct frec *lookup_frec(unsigned short id, unsigned int crc);static struct frec *lookup_frec(unsigned short id, void *hash);
 static struct frec *lookup_frec_by_sender(unsigned short id,  static struct frec *lookup_frec_by_sender(unsigned short id,
                                           union mysockaddr *addr,                                            union mysockaddr *addr,
                                          unsigned int crc);                                          void *hash);
static unsigned short get_id(unsigned int crc);static unsigned short get_id(void);
 static void free_frec(struct frec *f);  static void free_frec(struct frec *f);
 static struct randfd *allocate_rfd(int family);  
   
 /* Send a UDP packet with its source address set as "source"   /* Send a UDP packet with its source address set as "source" 
    unless nowild is true, when we just send it with the kernel default */     unless nowild is true, when we just send it with the kernel default */
Line 95  int send_from(int fd, int nowild, char *packet, size_t Line 94  int send_from(int fd, int nowild, char *packet, size_t
 #endif  #endif
     }      }
       
  while (sendmsg(fd, &msg, 0) == -1)  while (retry_send(sendmsg(fd, &msg, 0)));
 
   /* If interface is still in DAD, EINVAL results - ignore that. */
   if (errno != 0 && errno != EINVAL)
     {      {
       if (retry_send())  
         continue;  
         
       /* If interface is still in DAD, EINVAL results - ignore that. */  
       if (errno == EINVAL)  
         break;  
         
       my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno));        my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno));
       return 0;        return 0;
     }      }
Line 111  int send_from(int fd, int nowild, char *packet, size_t Line 106  int send_from(int fd, int nowild, char *packet, size_t
   return 1;    return 1;
 }  }
                       
static unsigned int search_servers(time_t now, struct all_addr **addrpp, static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigned int qtype,
                                     unsigned int qtype, char *qdomain, int *type, char **domain, int *norebind)                                   char *qdomain, int *type, char **domain, int *norebind)
                                                               
 {  {
   /* If the query ends in the domain in one of our servers, set    /* If the query ends in the domain in one of our servers, set
Line 156  static unsigned int search_servers(time_t now, struct  Line 151  static unsigned int search_servers(time_t now, struct 
             hostname_isequal(matchstart, serv->domain) &&              hostname_isequal(matchstart, serv->domain) &&
             (domainlen == 0 || namelen == domainlen || *(matchstart-1) == '.' ))              (domainlen == 0 || namelen == domainlen || *(matchstart-1) == '.' ))
           {            {
            if (serv->flags & SERV_NO_REBIND)             if ((serv->flags & SERV_NO_REBIND) && norebind)    
               *norebind = 1;                *norebind = 1;
             else              else
               {                {
Line 180  static unsigned int search_servers(time_t now, struct  Line 175  static unsigned int search_servers(time_t now, struct 
                                   
                 if (domainlen >= matchlen)                  if (domainlen >= matchlen)
                   {                    {
                    *type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND);                    *type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_DO_DNSSEC);
                     *domain = serv->domain;                      *domain = serv->domain;
                     matchlen = domainlen;                      matchlen = domainlen;
                     if (serv->flags & SERV_NO_ADDR)                      if (serv->flags & SERV_NO_ADDR)
Line 234  static unsigned int search_servers(time_t now, struct  Line 229  static unsigned int search_servers(time_t now, struct 
   
 static int forward_query(int udpfd, union mysockaddr *udpaddr,  static int forward_query(int udpfd, union mysockaddr *udpaddr,
                          struct all_addr *dst_addr, unsigned int dst_iface,                           struct all_addr *dst_addr, unsigned int dst_iface,
                         struct dns_header *header, size_t plen, time_t now, struct frec *forward)                         struct dns_header *header, size_t plen, time_t now, 
                          struct frec *forward, int ad_reqd, int do_bit)
 {  {
   char *domain = NULL;    char *domain = NULL;
  int type = 0, norebind = 0;  int type = SERV_DO_DNSSEC, norebind = 0;
   struct all_addr *addrp = NULL;    struct all_addr *addrp = NULL;
   unsigned int crc = questions_crc(header, plen, daemon->namebuff);  
   unsigned int flags = 0;    unsigned int flags = 0;
   unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);  
   struct server *start = NULL;    struct server *start = NULL;
  #ifdef HAVE_DNSSEC
  /* RFC 4035: sect 4.6 para 2 */  void *hash = hash_questions(header, plen, daemon->namebuff);
  header->hb4 &= ~HB4_AD;  int do_dnssec = 0;
  #else
   unsigned int crc = questions_crc(header, plen, daemon->namebuff);
   void *hash = &crc;
 #endif
  unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
 
  (void)do_bit;
 
   /* may be no servers available. */    /* may be no servers available. */
  if (!daemon->servers)  if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
    forward = NULL; 
  else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc))) 
     {      {
         /* If we didn't get an answer advertising a maximal packet in EDNS,
            fall back to 1280, which should work everywhere on IPv6.
            If that generates an answer, it will become the new default
            for this server */
         forward->flags |= FREC_TEST_PKTSZ;
         
   #ifdef HAVE_DNSSEC
         /* If we've already got an answer to this query, but we're awaiting keys for validation,
            there's no point retrying the query, retry the key query instead...... */
         if (forward->blocking_query)
           {
             int fd, is_sign;
             unsigned char *pheader;
             
             forward->flags &= ~FREC_TEST_PKTSZ;
             
             while (forward->blocking_query)
               forward = forward->blocking_query;
              
             forward->flags |= FREC_TEST_PKTSZ;
             
             blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
             plen = forward->stash_len;
             
             if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
               PUTSHORT(SAFE_PKTSZ, pheader);
   
             if (forward->sentto->addr.sa.sa_family == AF_INET) 
               log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
   #ifdef HAVE_IPV6
             else
               log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
   #endif
     
             if (forward->sentto->sfd)
               fd = forward->sentto->sfd->fd;
             else
               {
   #ifdef HAVE_IPV6
                 if (forward->sentto->addr.sa.sa_family == AF_INET6)
                   fd = forward->rfd6->fd;
                 else
   #endif
                   fd = forward->rfd4->fd;
               }
             
             while (retry_send( sendto(fd, (char *)header, plen, 0,
                                       &forward->sentto->addr.sa,
                                       sa_len(&forward->sentto->addr))));
             
             return 1;
           }
   #endif
   
       /* retry on existing query, send to all available servers  */        /* retry on existing query, send to all available servers  */
       domain = forward->sentto->domain;        domain = forward->sentto->domain;
       forward->sentto->failed_queries++;        forward->sentto->failed_queries++;
Line 261  static int forward_query(int udpfd, union mysockaddr * Line 314  static int forward_query(int udpfd, union mysockaddr *
           daemon->last_server = NULL;            daemon->last_server = NULL;
         }          }
       type = forward->sentto->flags & SERV_TYPE;        type = forward->sentto->flags & SERV_TYPE;
   #ifdef HAVE_DNSSEC
         do_dnssec = forward->sentto->flags & SERV_DO_DNSSEC;
   #endif
   
       if (!(start = forward->sentto->next))        if (!(start = forward->sentto->next))
         start = daemon->servers; /* at end of list, recycle */          start = daemon->servers; /* at end of list, recycle */
       header->id = htons(forward->new_id);        header->id = htons(forward->new_id);
Line 270  static int forward_query(int udpfd, union mysockaddr * Line 327  static int forward_query(int udpfd, union mysockaddr *
       if (gotname)        if (gotname)
         flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);          flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
               
      if (!flags && !(forward = get_new_frec(now, NULL)))#ifdef HAVE_DNSSEC
        /* table full - server failure. */      do_dnssec = type & SERV_DO_DNSSEC;
        flags = F_NEG;#endif
       type &= ~SERV_DO_DNSSEC;      
 
       if (daemon->servers && !flags)
         forward = get_new_frec(now, NULL, 0);
       /* table full - flags == 0, return REFUSED */
               
       if (forward)        if (forward)
         {          {
Line 280  static int forward_query(int udpfd, union mysockaddr * Line 342  static int forward_query(int udpfd, union mysockaddr *
           forward->dest = *dst_addr;            forward->dest = *dst_addr;
           forward->iface = dst_iface;            forward->iface = dst_iface;
           forward->orig_id = ntohs(header->id);            forward->orig_id = ntohs(header->id);
          forward->new_id = get_id(crc);          forward->new_id = get_id();
           forward->fd = udpfd;            forward->fd = udpfd;
          forward->crc = crc;          memcpy(forward->hash, hash, HASH_SIZE);
           forward->forwardall = 0;            forward->forwardall = 0;
             forward->flags = 0;
           if (norebind)            if (norebind)
             forward->flags |= FREC_NOREBIND;              forward->flags |= FREC_NOREBIND;
           if (header->hb4 & HB4_CD)            if (header->hb4 & HB4_CD)
             forward->flags |= FREC_CHECKING_DISABLED;              forward->flags |= FREC_CHECKING_DISABLED;
          if (ad_reqd)
             forward->flags |= FREC_AD_QUESTION;
 #ifdef HAVE_DNSSEC
           forward->work_counter = DNSSEC_WORK;
           if (do_bit)
             forward->flags |= FREC_DO_QUESTION;
 #endif
           
           header->id = htons(forward->new_id);            header->id = htons(forward->new_id);
                       
           /* In strict_order mode, always try servers in the order             /* In strict_order mode, always try servers in the order 
Line 326  static int forward_query(int udpfd, union mysockaddr * Line 396  static int forward_query(int udpfd, union mysockaddr *
   if (!flags && forward)    if (!flags && forward)
     {      {
       struct server *firstsentto = start;        struct server *firstsentto = start;
      int forwarded = 0;      int subnet, forwarded = 0;
       size_t edns0_len;
 
       /* If a query is retried, use the log_id for the retry when logging the answer. */
       forward->log_id = daemon->log_id;
               
      if (udpaddr && option_bool(OPT_ADD_MAC))      edns0_len  = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->source, now, &subnet);
        plen = add_mac(header, plen, ((char *) header) + PACKETSZ, udpaddr); 
               
         if (edns0_len != plen)
           {
             plen = edns0_len;
             forward->flags |= FREC_ADDED_PHEADER;
             
             if (subnet)
               forward->flags |= FREC_HAS_SUBNET;
           }
         
   #ifdef HAVE_DNSSEC
         if (option_bool(OPT_DNSSEC_VALID) && do_dnssec)
           {
             size_t new = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ);
            
             if (new != plen)
               forward->flags |= FREC_ADDED_PHEADER;
   
             plen = new;
                 
             /* For debugging, set Checking Disabled, otherwise, have the upstream check too,
                this allows it to select auth servers when one is returning bad data. */
             if (option_bool(OPT_DNSSEC_DEBUG))
               header->hb4 |= HB4_CD;
   
           }
   #endif
   
         /* If we're sending an EDNS0 with any options, we can't recreate the query from a reply. */
         if (find_pseudoheader(header, plen, &edns0_len, NULL, NULL, NULL) && edns0_len > 11)
           forward->flags |= FREC_HAS_EXTRADATA;
         
       while (1)        while (1)
         {           { 
           /* only send to servers dealing with our domain.            /* only send to servers dealing with our domain.
Line 339  static int forward_query(int udpfd, union mysockaddr * Line 443  static int forward_query(int udpfd, union mysockaddr *
                       
           if (type == (start->flags & SERV_TYPE) &&            if (type == (start->flags & SERV_TYPE) &&
               (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&                (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
              !(start->flags & SERV_LITERAL_ADDRESS))              !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
             {              {
               int fd;                int fd;
   
Line 372  static int forward_query(int udpfd, union mysockaddr * Line 476  static int forward_query(int udpfd, union mysockaddr *
                   if (option_bool(OPT_CONNTRACK))                    if (option_bool(OPT_CONNTRACK))
                     {                      {
                       unsigned int mark;                        unsigned int mark;
                      if (get_incoming_mark(udpaddr, dst_addr, 0, &mark))                      if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark))
                         setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));                          setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
                     }                      }
 #endif  #endif
                 }                  }
                               
              if (sendto(fd, (char *)header, plen, 0,#ifdef HAVE_DNSSEC
                         &start->addr.sa,              if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER))
                         sa_len(&start->addr)) == -1) 
                 {                  {
                  if (retry_send())                  /* Difficult one here. If our client didn't send EDNS0, we will have set the UDP
                    continue;                     packet size to 512. But that won't provide space for the RRSIGS in many cases.
                      The RRSIGS will be stripped out before the answer goes back, so the packet should
                      shrink again. So, if we added a do-bit, bump the udp packet size to the value
                      known to be OK for this server. We check returned size after stripping and set
                      the truncated bit if it's still too big. */                  
                   unsigned char *pheader;
                   int is_sign;
                   if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
                     PUTSHORT(start->edns_pktsz, pheader);
                 }                  }
              else#endif
 
               if (retry_send(sendto(fd, (char *)header, plen, 0,
                                     &start->addr.sa,
                                     sa_len(&start->addr))))
                 continue;
             
               if (errno == 0)
                 {                  {
                   /* Keep info in case we want to re-send this packet */                    /* Keep info in case we want to re-send this packet */
                   daemon->srv_save = start;                    daemon->srv_save = start;
Line 435  static int forward_query(int udpfd, union mysockaddr * Line 553  static int forward_query(int udpfd, union mysockaddr *
   return 0;    return 0;
 }  }
   
static size_t process_reply(struct dns_header *header, time_t now, static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind, 
                            struct server *server, size_t n, int check_rebind, int checking_disabled)                            int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader, 
                             int check_subnet, union mysockaddr *query_source)
 {  {
   unsigned char *pheader, *sizep;    unsigned char *pheader, *sizep;
   char **sets = 0;    char **sets = 0;
   int munged = 0, is_sign;    int munged = 0, is_sign;
   size_t plen;     size_t plen; 
   
     (void)ad_reqd;
     (void)do_bit;
     (void)bogusanswer;
   
 #ifdef HAVE_IPSET  #ifdef HAVE_IPSET
  /* Similar algorithm to search_servers. */  if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL))
  struct ipsets *ipset_pos; 
  unsigned int namelen = strlen(daemon->namebuff); 
  unsigned int matchlen = 0; 
  for (ipset_pos = daemon->ipsets; ipset_pos; ipset_pos = ipset_pos->next)  
     {      {
      unsigned int domainlen = strlen(ipset_pos->domain);      /* Similar algorithm to search_servers. */
      char *matchstart = daemon->namebuff + namelen - domainlen;      struct ipsets *ipset_pos;
      if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) &&      unsigned int namelen = strlen(daemon->namebuff);
          (domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) &&      unsigned int matchlen = 0;
          domainlen >= matchlen) {      for (ipset_pos = daemon->ipsets; ipset_pos; ipset_pos = ipset_pos->next) 
        matchlen = domainlen;        {
        sets = ipset_pos->sets;          unsigned int domainlen = strlen(ipset_pos->domain);
      }          char *matchstart = daemon->namebuff + namelen - domainlen;
           if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) &&
               (domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) &&
               domainlen >= matchlen) 
             {
               matchlen = domainlen;
               sets = ipset_pos->sets;
             }
         }
     }      }
 #endif  #endif
       
  /* If upstream is advertising a larger UDP packet size  if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
     than we allow, trim it so that we don't get overlarge 
     requests for the client. We can't do this for signed packets. */ 
 
  if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)) && !is_sign) 
     {      {
      unsigned short udpsz;      if (check_subnet && !check_source(header, plen, pheader, query_source))
      unsigned char *psave = sizep;        {
           my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
           return 0;
         }
               
      GETSHORT(udpsz, sizep);      if (!is_sign)
      if (udpsz > daemon->edns_pktsz)        {
        PUTSHORT(daemon->edns_pktsz, psave);          if (added_pheader)
    }            {
               /* client didn't send EDNS0, we added one, strip it off before returning answer. */
               n = rrfilter(header, n, 0);
               pheader = NULL;
             }
           else
             {
               unsigned short udpsz;
   
                 /* If upstream is advertising a larger UDP packet size
                    than we allow, trim it so that we don't get overlarge
                    requests for the client. We can't do this for signed packets. */
                 GETSHORT(udpsz, sizep);
                 if (udpsz > daemon->edns_pktsz)
                   {
                     sizep -= 2;
                     PUTSHORT(daemon->edns_pktsz, sizep);
                   }
   
   #ifdef HAVE_DNSSEC
                 /* If the client didn't set the do bit, but we did, reset it. */
                 if (option_bool(OPT_DNSSEC_VALID) && !do_bit)
                   {
                     unsigned short flags;
                     sizep += 2; /* skip RCODE */
                     GETSHORT(flags, sizep);
                     flags &= ~0x8000;
                     sizep -= 2;
                     PUTSHORT(flags, sizep);
                   }
   #endif
               }
           }
       }
     
   /* RFC 4035 sect 4.6 para 3 */    /* RFC 4035 sect 4.6 para 3 */
  if (!is_sign && !option_bool(OPT_DNSSEC))  if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
      header->hb4 &= ~HB4_AD;       header->hb4 &= ~HB4_AD;
  
   if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))    if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
    return n;    return resize_packet(header, n, pheader, plen);
       
   /* Complain loudly if the upstream server is non-recursive. */    /* Complain loudly if the upstream server is non-recursive. */
  if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR && ntohs(header->ancount) == 0 &&  if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR &&
       server && !(server->flags & SERV_WARNED_RECURSIVE))        server && !(server->flags & SERV_WARNED_RECURSIVE))
     {      {
       prettyprint_addr(&server->addr, daemon->namebuff);        prettyprint_addr(&server->addr, daemon->namebuff);
Line 491  static size_t process_reply(struct dns_header *header, Line 650  static size_t process_reply(struct dns_header *header,
       if (!option_bool(OPT_LOG))        if (!option_bool(OPT_LOG))
         server->flags |= SERV_WARNED_RECURSIVE;          server->flags |= SERV_WARNED_RECURSIVE;
     }        }  
    
   if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&    if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
       check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))        check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
     {      {
       munged = 1;        munged = 1;
       SET_RCODE(header, NXDOMAIN);        SET_RCODE(header, NXDOMAIN);
       header->hb3 &= ~HB3_AA;        header->hb3 &= ~HB3_AA;
         cache_secure = 0;
     }      }
   else     else 
     {      {
         int doctored = 0;
         
       if (RCODE(header) == NXDOMAIN &&         if (RCODE(header) == NXDOMAIN && 
           extract_request(header, n, daemon->namebuff, NULL) &&            extract_request(header, n, daemon->namebuff, NULL) &&
           check_for_local_domain(daemon->namebuff, now))            check_for_local_domain(daemon->namebuff, now))
Line 511  static size_t process_reply(struct dns_header *header, Line 673  static size_t process_reply(struct dns_header *header,
           munged = 1;            munged = 1;
           header->hb3 |= HB3_AA;            header->hb3 |= HB3_AA;
           SET_RCODE(header, NOERROR);            SET_RCODE(header, NOERROR);
             cache_secure = 0;
         }          }
               
      if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, checking_disabled))      if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
         {          {
           my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);            my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
           munged = 1;            munged = 1;
             cache_secure = 0;
         }          }
   
         if (doctored)
           cache_secure = 0;
     }      }
       
   #ifdef HAVE_DNSSEC
     if (bogusanswer && !(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG))
       {
         /* Bogus reply, turn into SERVFAIL */
         SET_RCODE(header, SERVFAIL);
         munged = 1;
       }
   
     if (option_bool(OPT_DNSSEC_VALID))
       {
         header->hb4 &= ~HB4_AD;
         
         if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
           header->hb4 |= HB4_AD;
         
         /* If the requestor didn't set the DO bit, don't return DNSSEC info. */
         if (!do_bit)
           n = rrfilter(header, n, 1);
       }
   #endif
   
   /* do this after extract_addresses. Ensure NODATA reply and remove    /* do this after extract_addresses. Ensure NODATA reply and remove
      nameserver info. */       nameserver info. */
       
Line 528  static size_t process_reply(struct dns_header *header, Line 716  static size_t process_reply(struct dns_header *header,
       header->ancount = htons(0);        header->ancount = htons(0);
       header->nscount = htons(0);        header->nscount = htons(0);
       header->arcount = htons(0);        header->arcount = htons(0);
         header->hb3 &= ~HB3_TC;
     }      }
       
   /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide    /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
Line 545  void reply_query(int fd, int family, time_t now) Line 734  void reply_query(int fd, int family, time_t now)
   union mysockaddr serveraddr;    union mysockaddr serveraddr;
   struct frec *forward;    struct frec *forward;
   socklen_t addrlen = sizeof(serveraddr);    socklen_t addrlen = sizeof(serveraddr);
  ssize_t n = recvfrom(fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);  ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen);
   size_t nn;    size_t nn;
   struct server *server;    struct server *server;
    void *hash;
 #ifndef HAVE_DNSSEC
   unsigned int crc;
 #endif
 
   /* packet buffer overwritten */    /* packet buffer overwritten */
   daemon->srv_save = NULL;    daemon->srv_save = NULL;
       
Line 559  void reply_query(int fd, int family, time_t now) Line 752  void reply_query(int fd, int family, time_t now)
     serveraddr.in6.sin6_flowinfo = 0;      serveraddr.in6.sin6_flowinfo = 0;
 #endif  #endif
       
     header = (struct dns_header *)daemon->packet;
     
     if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR))
       return;
     
   /* spoof check: answer must come from known server, */    /* spoof check: answer must come from known server, */
   for (server = daemon->servers; server; server = server->next)    for (server = daemon->servers; server; server = server->next)
     if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&      if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
         sockaddr_isequal(&server->addr, &serveraddr))          sockaddr_isequal(&server->addr, &serveraddr))
       break;        break;
      
   header = (struct dns_header *)daemon->packet;  
       
  if (!server ||  if (!server)
      n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) || 
      !(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff)))) 
     return;      return;
      
   server = forward->sentto;  
       
  if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&#ifdef HAVE_DNSSEC
   hash = hash_questions(header, n, daemon->namebuff);
 #else
   hash = &crc;
   crc = questions_crc(header, n, daemon->namebuff);
 #endif
   
   if (!(forward = lookup_frec(ntohs(header->id), hash)))
     return;
   
   /* log_query gets called indirectly all over the place, so 
      pass these in global variables - sorry. */
   daemon->log_display_id = forward->log_id;
   daemon->log_source_addr = &forward->source;
   
   if (daemon->ignore_addr && RCODE(header) == NOERROR &&
       check_for_ignored_address(header, n, daemon->ignore_addr))
     return;
 
   /* Note: if we send extra options in the EDNS0 header, we can't recreate
      the query from the reply. */
   if (RCODE(header) == REFUSED &&
       !option_bool(OPT_ORDER) &&        !option_bool(OPT_ORDER) &&
      forward->forwardall == 0)      forward->forwardall == 0 &&
       !(forward->flags & FREC_HAS_EXTRADATA))
     /* for broken servers, attempt to send to another one. */      /* for broken servers, attempt to send to another one. */
     {      {
       unsigned char *pheader;        unsigned char *pheader;
Line 584  void reply_query(int fd, int family, time_t now) Line 798  void reply_query(int fd, int family, time_t now)
       int is_sign;        int is_sign;
               
       /* recreate query from reply */        /* recreate query from reply */
      pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign);      pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign, NULL);
       if (!is_sign)        if (!is_sign)
         {          {
           header->ancount = htons(0);            header->ancount = htons(0);
Line 592  void reply_query(int fd, int family, time_t now) Line 806  void reply_query(int fd, int family, time_t now)
           header->arcount = htons(0);            header->arcount = htons(0);
           if ((nn = resize_packet(header, (size_t)n, pheader, plen)))            if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
             {              {
              header->hb3 &= ~(HB3_QR | HB3_TC);              header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
              forward_query(-1, NULL, NULL, 0, header, nn, now, forward);              header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD);
               if (forward->flags & FREC_CHECKING_DISABLED)
                 header->hb4 |= HB4_CD;
               if (forward->flags & FREC_AD_QUESTION)
                 header->hb4 |= HB4_AD;
               if (forward->flags & FREC_DO_QUESTION)
                 add_do_bit(header, nn,  (unsigned char *)pheader + plen);
               forward_query(-1, NULL, NULL, 0, header, nn, now, forward, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION);
               return;                return;
             }              }
         }          }
     }         }   
     
   server = forward->sentto;
   if ((forward->sentto->flags & SERV_TYPE) == 0)    if ((forward->sentto->flags & SERV_TYPE) == 0)
     {      {
      if (RCODE(header) == SERVFAIL || RCODE(header) == REFUSED)      if (RCODE(header) == REFUSED)
         server = NULL;          server = NULL;
       else        else
         {          {
Line 619  void reply_query(int fd, int family, time_t now) Line 841  void reply_query(int fd, int family, time_t now)
       if (!option_bool(OPT_ALL_SERVERS))        if (!option_bool(OPT_ALL_SERVERS))
         daemon->last_server = server;          daemon->last_server = server;
     }      }
    
     /* We tried resending to this server with a smaller maximum size and got an answer.
        Make that permanent. To avoid reduxing the packet size for an single dropped packet,
        only do this when we get a truncated answer, or one larger than the safe size. */
     if (server && (forward->flags & FREC_TEST_PKTSZ) && 
         ((header->hb3 & HB3_TC) || n >= SAFE_PKTSZ))
       server->edns_pktsz = SAFE_PKTSZ;
       
   /* If the answer is an error, keep the forward record in place in case    /* If the answer is an error, keep the forward record in place in case
      we get a good reply from another server. Kill it when we've       we get a good reply from another server. Kill it when we've
      had replies from all to avoid filling the forwarding table when       had replies from all to avoid filling the forwarding table when
      everything is broken */       everything is broken */
  if (forward->forwardall == 0 || --forward->forwardall == 1 ||   if (forward->forwardall == 0 || --forward->forwardall == 1 || RCODE(header) != REFUSED)
      (RCODE(header) != REFUSED && RCODE(header) != SERVFAIL)) 
     {      {
      int check_rebind = !(forward->flags & FREC_NOREBIND);      int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
   
      if (!option_bool(OPT_NO_REBIND))      if (option_bool(OPT_NO_REBIND))
        check_rebind = 0;        check_rebind = !(forward->flags & FREC_NOREBIND);
               
      if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, forward->flags & FREC_CHECKING_DISABLED)))      /*   Don't cache replies where DNSSEC validation was turned off, either
            the upstream server told us so, or the original query specified it.  */
       if ((header->hb4 & HB4_CD) || (forward->flags & FREC_CHECKING_DISABLED))
         no_cache_dnssec = 1;
       
 #ifdef HAVE_DNSSEC
       if (server && (server->flags & SERV_DO_DNSSEC) && 
           option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED))
         {          {
             int status = 0;
   
             /* We've had a reply already, which we're validating. Ignore this duplicate */
             if (forward->blocking_query)
               return;
             
              /* Truncated answer can't be validated.
                    If this is an answer to a DNSSEC-generated query, we still
                    need to get the client to retry over TCP, so return
                    an answer with the TC bit set, even if the actual answer fits.
                 */
             if (header->hb3 & HB3_TC)
               status = STAT_TRUNCATED;
             
             while (1)
               {
                 /* As soon as anything returns BOGUS, we stop and unwind, to do otherwise
                    would invite infinite loops, since the answers to DNSKEY and DS queries
                    will not be cached, so they'll be repeated. */
                 if (status != STAT_BOGUS && status != STAT_TRUNCATED && status != STAT_ABANDONED)
                   {
                     if (forward->flags & FREC_DNSKEY_QUERY)
                       status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
                     else if (forward->flags & FREC_DS_QUERY)
                       status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
                     else
                       status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, 
                                                      option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL);
                   }
                 
                 /* Can't validate, as we're missing key data. Put this
                    answer aside, whilst we get that. */     
                 if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
                   {
                     struct frec *new, *orig;
                     
                     /* Free any saved query */
                     if (forward->stash)
                       blockdata_free(forward->stash);
                     
                     /* Now save reply pending receipt of key data */
                     if (!(forward->stash = blockdata_alloc((char *)header, n)))
                       return;
                     forward->stash_len = n;
                     
                     /* Find the original query that started it all.... */
                     for (orig = forward; orig->dependent; orig = orig->dependent);
                     
                     if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1)))
                       status = STAT_ABANDONED;
                     else
                       {
                         int fd, type = SERV_DO_DNSSEC;
                         struct frec *next = new->next;
                         char *domain;
                         
                         *new = *forward; /* copy everything, then overwrite */
                         new->next = next;
                         new->blocking_query = NULL;
   
                         /* Find server to forward to. This will normally be the 
                            same as for the original query, but may be another if
                            servers for domains are involved. */                 
                         if (search_servers(now, NULL, F_QUERY, daemon->keyname, &type, &domain, NULL) == 0)
                           {
                             struct server *start = server, *new_server = NULL;
                              type &= ~SERV_DO_DNSSEC;
                              
                              while (1)
                                {
                                  if (type == (start->flags & SERV_TYPE) &&
                                      (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
                                      !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
                                    {
                                      new_server = start;
                                      if (server == start)
                                        {
                                          new_server = NULL;
                                          break;
                                        }
                                    }
                                  
                                  if (!(start = start->next))
                                    start = daemon->servers;
                                  if (start == server)
                                    break;
                                }
                           
                              if (new_server)
                                server = new_server;
                           }
   
                         new->sentto = server;
   
                         new->rfd4 = NULL;
   #ifdef HAVE_IPV6
                         new->rfd6 = NULL;
   #endif
                         new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
                         
                         new->dependent = forward; /* to find query awaiting new one. */
                         forward->blocking_query = new; /* for garbage cleaning */
                         /* validate routines leave name of required record in daemon->keyname */
                         if (status == STAT_NEED_KEY)
                           {
                             new->flags |= FREC_DNSKEY_QUERY; 
                             nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz,
                                                        daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz);
                           }
                         else 
                           {
                             new->flags |= FREC_DS_QUERY;
                             nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz,
                                                        daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
                           }
                         if ((hash = hash_questions(header, nn, daemon->namebuff)))
                           memcpy(new->hash, hash, HASH_SIZE);
                         new->new_id = get_id();
                         header->id = htons(new->new_id);
                         /* Save query for retransmission */
                         new->stash = blockdata_alloc((char *)header, nn);
                         new->stash_len = nn;
                         
                         /* Don't resend this. */
                         daemon->srv_save = NULL;
                         
                         if (server->sfd)
                           fd = server->sfd->fd;
                         else
                           {
                             fd = -1;
   #ifdef HAVE_IPV6
                             if (server->addr.sa.sa_family == AF_INET6)
                               {
                                 if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
                                   fd = new->rfd6->fd;
                               }
                             else
   #endif
                               {
                                 if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
                                   fd = new->rfd4->fd;
                               }
                           }
                         
                         if (fd != -1)
                           {
   #ifdef HAVE_CONNTRACK
                             /* Copy connection mark of incoming query to outgoing connection. */
                             if (option_bool(OPT_CONNTRACK))
                               {
                                 unsigned int mark;
                                 if (get_incoming_mark(&orig->source, &orig->dest, 0, &mark))
                                   setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
                               }
   #endif
                             while (retry_send(sendto(fd, (char *)header, nn, 0, 
                                                      &server->addr.sa, 
                                                      sa_len(&server->addr)))); 
                             server->queries++;
                           }
                       }             
                     return;
                   }
             
                 /* Validated original answer, all done. */
                 if (!forward->dependent)
                   break;
                 
                 /* validated subsdiary query, (and cached result)
                    pop that and return to the previous query we were working on. */
                 struct frec *prev = forward->dependent;
                 free_frec(forward);
                 forward = prev;
                 forward->blocking_query = NULL; /* already gone */
                 blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
                 n = forward->stash_len;
               }
           
             
             no_cache_dnssec = 0;
             
             if (status == STAT_TRUNCATED)
               header->hb3 |= HB3_TC;
             else
               {
                 char *result, *domain = "result";
                 
                 if (status == STAT_ABANDONED)
                   {
                     result = "ABANDONED";
                     status = STAT_BOGUS;
                   }
                 else
                   result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
                 
                 if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL))
                   domain = daemon->namebuff;
                 
                 log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
               }
             
             if (status == STAT_SECURE)
               cache_secure = 1;
             else if (status == STAT_BOGUS)
               {
                 no_cache_dnssec = 1;
                 bogusanswer = 1;
               }
           }
   #endif     
         
         /* restore CD bit to the value in the query */
         if (forward->flags & FREC_CHECKING_DISABLED)
           header->hb4 |= HB4_CD;
         else
           header->hb4 &= ~HB4_CD;
         
         if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, 
                                 forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, 
                                 forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source)))
           {
           header->id = htons(forward->orig_id);            header->id = htons(forward->orig_id);
           header->hb4 |= HB4_RA; /* recursion if available */            header->hb4 |= HB4_RA; /* recursion if available */
   #ifdef HAVE_DNSSEC
             /* We added an EDNSO header for the purpose of getting DNSSEC RRs, and set the value of the UDP payload size
                greater than the no-EDNS0-implied 512 to have if space for the RRSIGS. If, having stripped them and the EDNS0
                header, the answer is still bigger than 512, truncate it and mark it so. The client then retries with TCP. */
             if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER) && (nn > PACKETSZ))
               {
                 header->ancount = htons(0);
                 header->nscount = htons(0);
                 header->arcount = htons(0);
                 header->hb3 |= HB3_TC;
                 nn = resize_packet(header, nn, NULL, 0);
               }
   #endif
           send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,             send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, 
                     &forward->source, &forward->dest, forward->iface);                      &forward->source, &forward->dest, forward->iface);
         }          }
Line 648  void receive_query(struct listener *listen, time_t now Line 1118  void receive_query(struct listener *listen, time_t now
 {  {
   struct dns_header *header = (struct dns_header *)daemon->packet;    struct dns_header *header = (struct dns_header *)daemon->packet;
   union mysockaddr source_addr;    union mysockaddr source_addr;
  unsigned short type;  unsigned char *pheader;
   unsigned short type, udp_size = PACKETSZ; /* default if no EDNS0 */
   struct all_addr dst_addr;    struct all_addr dst_addr;
   struct in_addr netmask, dst_addr_4;    struct in_addr netmask, dst_addr_4;
   size_t m;    size_t m;
   ssize_t n;    ssize_t n;
  int if_index = 0;  int if_index = 0, auth_dns = 0, do_bit = 0, have_pseudoheader = 0;
  int auth_dns = 0;#ifdef HAVE_AUTH
   int local_auth = 0;
 #endif
   struct iovec iov[1];    struct iovec iov[1];
   struct msghdr msg;    struct msghdr msg;
   struct cmsghdr *cmptr;    struct cmsghdr *cmptr;
Line 673  void receive_query(struct listener *listen, time_t now Line 1146  void receive_query(struct listener *listen, time_t now
                  CMSG_SPACE(sizeof(struct sockaddr_dl))];                   CMSG_SPACE(sizeof(struct sockaddr_dl))];
 #endif  #endif
   } control_u;    } control_u;
  #ifdef HAVE_IPV6
    /* Can always get recvd interface for IPv6 */
   int check_dst = !option_bool(OPT_NOWILD) || listen->family == AF_INET6;
 #else
   int check_dst = !option_bool(OPT_NOWILD);
 #endif
 
   /* packet buffer overwritten */    /* packet buffer overwritten */
   daemon->srv_save = NULL;    daemon->srv_save = NULL;
       
  dst_addr_4.s_addr = 0;  dst_addr_4.s_addr = dst_addr.addr.addr4.s_addr = 0;
   netmask.s_addr = 0;    netmask.s_addr = 0;
       
   if (option_bool(OPT_NOWILD) && listen->iface)    if (option_bool(OPT_NOWILD) && listen->iface)
Line 686  void receive_query(struct listener *listen, time_t now Line 1165  void receive_query(struct listener *listen, time_t now
             
       if (listen->family == AF_INET)        if (listen->family == AF_INET)
         {          {
          dst_addr_4 = listen->iface->addr.in.sin_addr;          dst_addr_4 = dst_addr.addr.addr4 = listen->iface->addr.in.sin_addr;
           netmask = listen->iface->netmask;            netmask = listen->iface->netmask;
         }          }
     }      }
Line 711  void receive_query(struct listener *listen, time_t now Line 1190  void receive_query(struct listener *listen, time_t now
     return;      return;
       
   source_addr.sa.sa_family = listen->family;    source_addr.sa.sa_family = listen->family;
     
     if (listen->family == AF_INET)
       {
          /* Source-port == 0 is an error, we can't send back to that. 
             http://www.ietf.org/mail-archive/web/dnsop/current/msg11441.html */
         if (source_addr.in.sin_port == 0)
           return;
       }
 #ifdef HAVE_IPV6  #ifdef HAVE_IPV6
  if (listen->family == AF_INET6)  else
    source_addr.in6.sin6_flowinfo = 0;    {
       /* Source-port == 0 is an error, we can't send back to that. */
       if (source_addr.in6.sin6_port == 0)
         return;
       source_addr.in6.sin6_flowinfo = 0;
     }
 #endif  #endif
  
  if (!option_bool(OPT_NOWILD))  /* We can be configured to only accept queries from at-most-one-hop-away addresses. */
   if (option_bool(OPT_LOCAL_SERVICE))
     {      {
         struct addrlist *addr;
   #ifdef HAVE_IPV6
         if (listen->family == AF_INET6) 
           {
             for (addr = daemon->interface_addrs; addr; addr = addr->next)
               if ((addr->flags & ADDRLIST_IPV6) &&
                   is_same_net6(&addr->addr.addr.addr6, &source_addr.in6.sin6_addr, addr->prefixlen))
                 break;
           }
         else
   #endif
           {
             struct in_addr netmask;
             for (addr = daemon->interface_addrs; addr; addr = addr->next)
               {
                 netmask.s_addr = htonl(~(in_addr_t)0 << (32 - addr->prefixlen));
                 if (!(addr->flags & ADDRLIST_IPV6) &&
                     is_same_net(addr->addr.addr.addr4, source_addr.in.sin_addr, netmask))
                   break;
               }
           }
         if (!addr)
           {
             static int warned = 0;
             if (!warned)
               {
                 my_syslog(LOG_WARNING, _("Ignoring query from non-local network"));
                 warned = 1;
               }
             return;
           }
       }
                   
     if (check_dst)
       {
       struct ifreq ifr;        struct ifreq ifr;
   
       if (msg.msg_controllen < sizeof(struct cmsghdr))        if (msg.msg_controllen < sizeof(struct cmsghdr))
Line 788  void receive_query(struct listener *listen, time_t now Line 1316  void receive_query(struct listener *listen, time_t now
       if (!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))        if (!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))
         {          {
            if (!option_bool(OPT_CLEVERBIND))             if (!option_bool(OPT_CLEVERBIND))
             enumerate_interfaces();              enumerate_interfaces(0); 
           if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name))           if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name) &&
                !label_exception(if_index, listen->family, &dst_addr))
              return;               return;
         }          }
   
Line 807  void receive_query(struct listener *listen, time_t now Line 1336  void receive_query(struct listener *listen, time_t now
                       
           /* interface may be new */            /* interface may be new */
           if (!iface && !option_bool(OPT_CLEVERBIND))            if (!iface && !option_bool(OPT_CLEVERBIND))
            enumerate_interfaces();             enumerate_interfaces(0); 
                       
           for (iface = daemon->interfaces; iface; iface = iface->next)            for (iface = daemon->interfaces; iface; iface = iface->next)
             if (iface->addr.sa.sa_family == AF_INET &&              if (iface->addr.sa.sa_family == AF_INET &&
Line 821  void receive_query(struct listener *listen, time_t now Line 1350  void receive_query(struct listener *listen, time_t now
             dst_addr_4.s_addr = 0;              dst_addr_4.s_addr = 0;
         }          }
     }      }
      
     /* log_query gets called indirectly all over the place, so 
        pass these in global variables - sorry. */
     daemon->log_display_id = ++daemon->log_id;
     daemon->log_source_addr = &source_addr;
       
   if (extract_request(header, (size_t)n, daemon->namebuff, &type))    if (extract_request(header, (size_t)n, daemon->namebuff, &type))
     {      {
      char types[20];#ifdef HAVE_AUTH
      struct auth_zone *zone;
      querystr(auth_dns ? "auth" : "query", types, type);#endif
      char *types = querystr(auth_dns ? "auth" : "query", type);
       
       if (listen->family == AF_INET)         if (listen->family == AF_INET) 
         log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,           log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
                   (struct all_addr *)&source_addr.in.sin_addr, types);                    (struct all_addr *)&source_addr.in.sin_addr, types);
Line 836  void receive_query(struct listener *listen, time_t now Line 1371  void receive_query(struct listener *listen, time_t now
         log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,           log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
                   (struct all_addr *)&source_addr.in6.sin6_addr, types);                    (struct all_addr *)&source_addr.in6.sin6_addr, types);
 #endif  #endif
   
   #ifdef HAVE_AUTH
         /* find queries for zones we're authoritative for, and answer them directly */
         if (!auth_dns && !option_bool(OPT_LOCALISE))
           for (zone = daemon->auth_zones; zone; zone = zone->next)
             if (in_zone(zone, daemon->namebuff, NULL))
               {
                 auth_dns = 1;
                 local_auth = 1;
                 break;
               }
   #endif
         
   #ifdef HAVE_LOOP
         /* Check for forwarding loop */
         if (detect_loop(daemon->namebuff, type))
           return;
   #endif
     }      }
     
     if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL))
       { 
         unsigned short flags;
         
         have_pseudoheader = 1;
         GETSHORT(udp_size, pheader);
         pheader += 2; /* ext_rcode */
         GETSHORT(flags, pheader);
         
         if (flags & 0x8000)
           do_bit = 1;/* do bit */ 
           
         /* If the client provides an EDNS0 UDP size, use that to limit our reply.
            (bounded by the maximum configured). If no EDNS0, then it
            defaults to 512 */
         if (udp_size > daemon->edns_pktsz)
           udp_size = daemon->edns_pktsz;
       }
   
 #ifdef HAVE_AUTH  #ifdef HAVE_AUTH
   if (auth_dns)    if (auth_dns)
     {      {
      m = answer_auth(header, ((char *) header) + PACKETSZ, (size_t)n, now, &source_addr);      m = answer_auth(header, ((char *) header) + udp_size, (size_t)n, now, &source_addr
                       local_auth, do_bit, have_pseudoheader);
       if (m >= 1)        if (m >= 1)
        send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),        {
                  (char *)header, m, &source_addr, &dst_addr, if_index);          send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
                     (char *)header, m, &source_addr, &dst_addr, if_index);
           daemon->auth_answer++;
         }
     }      }
   else    else
 #endif  #endif
     {      {
      m = answer_request(header, ((char *) header) + PACKETSZ, (size_t)n,       int ad_reqd = do_bit;
                         dst_addr_4, netmask, now);       /* RFC 6840 5.7 */
       if (header->hb4 & HB4_AD)
         ad_reqd = 1;
 
       m = answer_request(header, ((char *) header) + udp_size, (size_t)n, 
                          dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
               
       if (m >= 1)        if (m >= 1)
         {          {
Line 859  void receive_query(struct listener *listen, time_t now Line 1440  void receive_query(struct listener *listen, time_t now
           daemon->local_answer++;            daemon->local_answer++;
         }          }
       else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,        else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
                             header, (size_t)n, now, NULL))                             header, (size_t)n, now, NULL, ad_reqd, do_bit))
         daemon->queries_forwarded++;          daemon->queries_forwarded++;
       else        else
         daemon->local_answer++;          daemon->local_answer++;
     }      }
 }  }
   
   #ifdef HAVE_DNSSEC
   /* Recurse up the key heirarchy */
   static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n, 
                              int class, char *name, char *keyname, struct server *server, 
                              int have_mark, unsigned int mark, int *keycount)
   {
     int new_status;
     unsigned char *packet = NULL;
     unsigned char *payload = NULL;
     struct dns_header *new_header = NULL;
     u16 *length = NULL;
   
     while (1)
       {
         int type = SERV_DO_DNSSEC;
         char *domain;
         size_t m; 
         unsigned char c1, c2;
               
         /* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
         if (--(*keycount) == 0)
           new_status = STAT_ABANDONED;
         else if (status == STAT_NEED_KEY)
           new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
         else if (status == STAT_NEED_DS)
           new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
         else 
           new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL);
         
         if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY)
           break;
   
         /* Can't validate because we need a key/DS whose name now in keyname.
            Make query for same, and recurse to validate */
         if (!packet)
           {
             packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
             payload = &packet[2];
             new_header = (struct dns_header *)payload;
             length = (u16 *)packet;
           }
         
         if (!packet)
           {
             new_status = STAT_ABANDONED;
             break;
           }
            
         m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, 
                                   new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz);
         
         *length = htons(m);
   
         /* Find server to forward to. This will normally be the 
            same as for the original query, but may be another if
            servers for domains are involved. */                 
         if (search_servers(now, NULL, F_QUERY, keyname, &type, &domain, NULL) == 0)
           {
             struct server *start = server, *new_server = NULL;
             type &= ~SERV_DO_DNSSEC;
                              
             while (1)
               {
                 if (type == (start->flags & SERV_TYPE) &&
                     (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
                     !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
                   {
                     new_server = start;
                     if (server == start)
                       {
                         new_server = NULL;
                         break;
                       }
                   }
                                  
                 if (!(start = start->next))
                   start = daemon->servers;
                 if (start == server)
                   break;
               }
          
   
             if (new_server)
               {
                 server = new_server;
                 /* may need to make new connection. */
                 if (server->tcpfd == -1)
                   {
                     if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
                       {
                         new_status = STAT_ABANDONED;
                         break;
                       }
   
   #ifdef HAVE_CONNTRACK
                     /* Copy connection mark of incoming query to outgoing connection. */
                     if (have_mark)
                       setsockopt(server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
   #endif  
                             
                     if (!local_bind(server->tcpfd,  &server->source_addr, server->interface, 1) ||
                         connect(server->tcpfd, &server->addr.sa, sa_len(&server->addr)) == -1)
                       {
                         close(server->tcpfd);
                         server->tcpfd = -1;
                         new_status = STAT_ABANDONED;
                         break;
                       }
   
                   }
               }
           }
   
         
         if (!read_write(server->tcpfd, packet, m + sizeof(u16), 0) ||
             !read_write(server->tcpfd, &c1, 1, 1) ||
             !read_write(server->tcpfd, &c2, 1, 1) ||
             !read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
           {
             new_status = STAT_ABANDONED;
             break;
           }
   
         m = (c1 << 8) | c2;
         
         new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount);
         
         if (new_status != STAT_OK)
           break;
       }
   
     if (packet)
       free(packet);
       
     return new_status;
   }
   #endif
   
   
 /* The daemon forks before calling this: it should deal with one connection,  /* The daemon forks before calling this: it should deal with one connection,
    blocking as neccessary, and then return. Note, need to be a bit careful     blocking as neccessary, and then return. Note, need to be a bit careful
    about resources for debug mode, when the fork is suppressed: that's     about resources for debug mode, when the fork is suppressed: that's
Line 875  unsigned char *tcp_request(int confd, time_t now, Line 1595  unsigned char *tcp_request(int confd, time_t now,
 {  {
   size_t size = 0;    size_t size = 0;
   int norebind = 0;    int norebind = 0;
  int checking_disabled;#ifdef HAVE_AUTH
   int local_auth = 0;
 #endif
   int checking_disabled, do_bit, added_pheader = 0, have_pseudoheader = 0;
   int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
   size_t m;    size_t m;
   unsigned short qtype;    unsigned short qtype;
   unsigned int gotname;    unsigned int gotname;
   unsigned char c1, c2;    unsigned char c1, c2;
  /* Max TCP packet + slop */  /* Max TCP packet + slop + size */
  unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);  unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
  struct dns_header *header;  unsigned char *payload = &packet[2];
   /* largest field in header is 16-bits, so this is still sufficiently aligned */
   struct dns_header *header = (struct dns_header *)payload;
   u16 *length = (u16 *)packet;
   struct server *last_server;    struct server *last_server;
   struct in_addr dst_addr_4;    struct in_addr dst_addr_4;
   union mysockaddr peer_addr;    union mysockaddr peer_addr;
   socklen_t peer_len = sizeof(union mysockaddr);    socklen_t peer_len = sizeof(union mysockaddr);
    int query_count = 0;
   unsigned char *pheader;
   unsigned int mark = 0;
   int have_mark = 0;
 
   (void)mark;
   (void)have_mark;
 
   if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)    if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
     return packet;      return packet;
   
   #ifdef HAVE_CONNTRACK
     /* Get connection mark of incoming query to set on outgoing connections. */
     if (option_bool(OPT_CONNTRACK))
       {
         struct all_addr local;
   #ifdef HAVE_IPV6                      
         if (local_addr->sa.sa_family == AF_INET6)
           local.addr.addr6 = local_addr->in6.sin6_addr;
         else
   #endif
           local.addr.addr4 = local_addr->in.sin_addr;
         
         have_mark = get_incoming_mark(&peer_addr, &local, 1, &mark);
       }
   #endif  
   
     /* We can be configured to only accept queries from at-most-one-hop-away addresses. */
     if (option_bool(OPT_LOCAL_SERVICE))
       {
         struct addrlist *addr;
   #ifdef HAVE_IPV6
         if (peer_addr.sa.sa_family == AF_INET6) 
           {
             for (addr = daemon->interface_addrs; addr; addr = addr->next)
               if ((addr->flags & ADDRLIST_IPV6) &&
                   is_same_net6(&addr->addr.addr.addr6, &peer_addr.in6.sin6_addr, addr->prefixlen))
                 break;
           }
         else
   #endif
           {
             struct in_addr netmask;
             for (addr = daemon->interface_addrs; addr; addr = addr->next)
               {
                 netmask.s_addr = htonl(~(in_addr_t)0 << (32 - addr->prefixlen));
                 if (!(addr->flags & ADDRLIST_IPV6) && 
                     is_same_net(addr->addr.addr.addr4, peer_addr.in.sin_addr, netmask))
                   break;
               }
           }
         if (!addr)
           {
             my_syslog(LOG_WARNING, _("Ignoring query from non-local network"));
             return packet;
           }
       }
   
   while (1)    while (1)
     {      {
      if (!packet ||      if (query_count == TCP_MAX_QUERIES ||
           !packet ||
           !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||            !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
           !(size = c1 << 8 | c2) ||            !(size = c1 << 8 | c2) ||
          !read_write(confd, packet, size, 1))          !read_write(confd, payload, size, 1))
         return packet;           return packet; 
       
       if (size < (int)sizeof(struct dns_header))        if (size < (int)sizeof(struct dns_header))
         continue;          continue;
               
      header = (struct dns_header *)packet;      query_count++;
   
         /* log_query gets called indirectly all over the place, so 
            pass these in global variables - sorry. */
         daemon->log_display_id = ++daemon->log_id;
         daemon->log_source_addr = &peer_addr;
         
       /* save state of "cd" flag in query */        /* save state of "cd" flag in query */
      checking_disabled = header->hb4 & HB4_CD;      if ((checking_disabled = header->hb4 & HB4_CD))
         no_cache_dnssec = 1;
                 
       /* RFC 4035: sect 4.6 para 2 */  
       header->hb4 &= ~HB4_AD;  
         
       if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))        if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
         {          {
          char types[20];#ifdef HAVE_AUTH
           struct auth_zone *zone;
 #endif
           char *types = querystr(auth_dns ? "auth" : "query", qtype);
                       
           querystr(auth_dns ? "auth" : "query", types, qtype);  
             
           if (peer_addr.sa.sa_family == AF_INET)             if (peer_addr.sa.sa_family == AF_INET) 
             log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,               log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
                       (struct all_addr *)&peer_addr.in.sin_addr, types);                        (struct all_addr *)&peer_addr.in.sin_addr, types);
Line 924  unsigned char *tcp_request(int confd, time_t now, Line 1710  unsigned char *tcp_request(int confd, time_t now,
             log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,               log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
                       (struct all_addr *)&peer_addr.in6.sin6_addr, types);                        (struct all_addr *)&peer_addr.in6.sin6_addr, types);
 #endif  #endif
             
   #ifdef HAVE_AUTH
             /* find queries for zones we're authoritative for, and answer them directly */
             if (!auth_dns && !option_bool(OPT_LOCALISE))
               for (zone = daemon->auth_zones; zone; zone = zone->next)
                 if (in_zone(zone, daemon->namebuff, NULL))
                   {
                     auth_dns = 1;
                     local_auth = 1;
                     break;
                   }
   #endif
         }          }
               
       if (local_addr->sa.sa_family == AF_INET)        if (local_addr->sa.sa_family == AF_INET)
Line 931  unsigned char *tcp_request(int confd, time_t now, Line 1729  unsigned char *tcp_request(int confd, time_t now,
       else        else
         dst_addr_4.s_addr = 0;          dst_addr_4.s_addr = 0;
               
         do_bit = 0;
   
         if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))
           { 
             unsigned short flags;
             
             have_pseudoheader = 1;
             pheader += 4; /* udp_size, ext_rcode */
             GETSHORT(flags, pheader);
         
             if (flags & 0x8000)
               do_bit = 1; /* do bit */ 
           }
   
 #ifdef HAVE_AUTH  #ifdef HAVE_AUTH
       if (auth_dns)        if (auth_dns)
        m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr);        m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr
                         local_auth, do_bit, have_pseudoheader);
       else        else
 #endif  #endif
         {          {
          /* m > 0 if answered from cache */           int ad_reqd = do_bit;
          m = answer_request(header, ((char *) header) + 65536, (size_t)size,            /* RFC 6840 5.7 */
                             dst_addr_4, netmask, now);           if (header->hb4 & HB4_AD)
              ad_reqd = 1;
            
            /* m > 0 if answered from cache */
            m = answer_request(header, ((char *) header) + 65536, (size_t)size, 
                               dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
                       
           /* Do this by steam now we're not in the select() loop */            /* Do this by steam now we're not in the select() loop */
          check_log_writer(NULL);           check_log_writer(1); 
                       
           if (m == 0)            if (m == 0)
             {              {
               unsigned int flags = 0;                unsigned int flags = 0;
               struct all_addr *addrp = NULL;                struct all_addr *addrp = NULL;
              int type = 0;              int type = SERV_DO_DNSSEC;
               char *domain = NULL;                char *domain = NULL;
                 size_t new_size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet);
   
                 if (size != new_size)
                   {
                     added_pheader = 1;
                     size = new_size;
                   }
                               
               if (option_bool(OPT_ADD_MAC))  
                 size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);  
                 
               if (gotname)                if (gotname)
                 flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);                  flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
                               
                 type &= ~SERV_DO_DNSSEC;
                 
               if (type != 0  || option_bool(OPT_ORDER) || !daemon->last_server)                if (type != 0  || option_bool(OPT_ORDER) || !daemon->last_server)
                 last_server = daemon->servers;                  last_server = daemon->servers;
               else                else
Line 965  unsigned char *tcp_request(int confd, time_t now, Line 1789  unsigned char *tcp_request(int confd, time_t now,
               if (!flags && last_server)                if (!flags && last_server)
                 {                  {
                   struct server *firstsendto = NULL;                    struct server *firstsendto = NULL;
   #ifdef HAVE_DNSSEC
                     unsigned char *newhash, hash[HASH_SIZE];
                     if ((newhash = hash_questions(header, (unsigned int)size, daemon->namebuff)))
                       memcpy(hash, newhash, HASH_SIZE);
                     else
                       memset(hash, 0, HASH_SIZE);
   #else
                   unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);                    unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
                  #endif            
                   /* Loop round available servers until we succeed in connecting to one.                    /* Loop round available servers until we succeed in connecting to one.
                      Note that this code subtley ensures that consecutive queries on this connection                       Note that this code subtley ensures that consecutive queries on this connection
                      which can go to the same server, do so. */                       which can go to the same server, do so. */
Line 985  unsigned char *tcp_request(int confd, time_t now, Line 1816  unsigned char *tcp_request(int confd, time_t now,
                                               
                       /* server for wrong domain */                        /* server for wrong domain */
                       if (type != (last_server->flags & SERV_TYPE) ||                        if (type != (last_server->flags & SERV_TYPE) ||
                          (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))                          (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)) ||
                           (last_server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
                         continue;                          continue;
                                               
                       if (last_server->tcpfd == -1)                        if (last_server->tcpfd == -1)
Line 993  unsigned char *tcp_request(int confd, time_t now, Line 1825  unsigned char *tcp_request(int confd, time_t now,
                           if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)                            if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
                             continue;                              continue;
                                                       
   #ifdef HAVE_CONNTRACK
                             /* Copy connection mark of incoming query to outgoing connection. */
                             if (have_mark)
                               setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
   #endif  
                         
                           if ((!local_bind(last_server->tcpfd,  &last_server->source_addr, last_server->interface, 1) ||                            if ((!local_bind(last_server->tcpfd,  &last_server->source_addr, last_server->interface, 1) ||
                                connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))                                 connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
                             {                              {
Line 1001  unsigned char *tcp_request(int confd, time_t now, Line 1839  unsigned char *tcp_request(int confd, time_t now,
                               continue;                                continue;
                             }                              }
                                                       
#ifdef HAVE_CONNTRACK#ifdef HAVE_DNSSEC
                          /* Copy connection mark of incoming query to outgoing connection. */                          if (option_bool(OPT_DNSSEC_VALID) && (last_server->flags & SERV_DO_DNSSEC))
                          if (option_bool(OPT_CONNTRACK)) 
                             {                              {
                              unsigned int mark;                              new_size = add_do_bit(header, size, ((unsigned char *) header) + 65536);
                              struct all_addr local; 
#ifdef HAVE_IPV6                       
                              if (local_addr->sa.sa_family == AF_INET6) 
                                local.addr.addr6 = local_addr->in6.sin6_addr; 
                              else 
#endif 
                                local.addr.addr4 = local_addr->in.sin_addr; 
                                                               
                              if (get_incoming_mark(&peer_addr, &local, 1, &mark))                              if (size != new_size)
                                setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));                                {
                                   added_pheader = 1;
                                   size = new_size;
                                 }
                               
                               /* For debugging, set Checking Disabled, otherwise, have the upstream check too,
                                  this allows it to select auth servers when one is returning bad data. */
                               if (option_bool(OPT_DNSSEC_DEBUG))
                                 header->hb4 |= HB4_CD;
                             }                              }
#endif  #endif
                         }                          }
                                               
                      c1 = size >> 8;                      *length = htons(size);
                      c2 = size;
                       /* get query name again for logging - may have been overwritten */
                       if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
                         strcpy(daemon->namebuff, "query");
                                               
                      if (!read_write(last_server->tcpfd, &c1, 1, 0) ||                      if (!read_write(last_server->tcpfd, packet, size + sizeof(u16), 0) ||
                          !read_write(last_server->tcpfd, &c2, 1, 0) || 
                          !read_write(last_server->tcpfd, packet, size, 0) || 
                           !read_write(last_server->tcpfd, &c1, 1, 1) ||                            !read_write(last_server->tcpfd, &c1, 1, 1) ||
                          !read_write(last_server->tcpfd, &c2, 1, 1))                          !read_write(last_server->tcpfd, &c2, 1, 1) ||
                           !read_write(last_server->tcpfd, payload, (c1 << 8) | c2, 1))
                         {                          {
                           close(last_server->tcpfd);                            close(last_server->tcpfd);
                           last_server->tcpfd = -1;                            last_server->tcpfd = -1;
Line 1035  unsigned char *tcp_request(int confd, time_t now, Line 1875  unsigned char *tcp_request(int confd, time_t now,
                         }                           } 
                                               
                       m = (c1 << 8) | c2;                        m = (c1 << 8) | c2;
                       if (!read_write(last_server->tcpfd, packet, m, 1))  
                         return packet;  
                                               
                       if (!gotname)  
                         strcpy(daemon->namebuff, "query");  
                       if (last_server->addr.sa.sa_family == AF_INET)                        if (last_server->addr.sa.sa_family == AF_INET)
                         log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,                           log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
                                   (struct all_addr *)&last_server->addr.in.sin_addr, NULL);                                     (struct all_addr *)&last_server->addr.in.sin_addr, NULL); 
Line 1048  unsigned char *tcp_request(int confd, time_t now, Line 1884  unsigned char *tcp_request(int confd, time_t now,
                         log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,                           log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
                                   (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);                                    (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
 #endif   #endif 
   
   #ifdef HAVE_DNSSEC
                         if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (last_server->flags & SERV_DO_DNSSEC))
                           {
                             int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
                             int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname, 
                                                          last_server, have_mark, mark, &keycount);
                             char *result, *domain = "result";
                             
                             if (status == STAT_ABANDONED)
                               {
                                 result = "ABANDONED";
                                 status = STAT_BOGUS;
                               }
                             else
                               result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
                             
                             if (status == STAT_BOGUS && extract_request(header, m, daemon->namebuff, NULL))
                               domain = daemon->namebuff;
   
                             log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
                             
                             if (status == STAT_BOGUS)
                               {
                                 no_cache_dnssec = 1;
                                 bogusanswer = 1;
                               }
   
                             if (status == STAT_SECURE)
                               cache_secure = 1;
                           }
   #endif
   
                         /* restore CD bit to the value in the query */
                         if (checking_disabled)
                           header->hb4 |= HB4_CD;
                         else
                           header->hb4 &= ~HB4_CD;
                                               
                       /* There's no point in updating the cache, since this process will exit and                        /* There's no point in updating the cache, since this process will exit and
                          lose the information after a few queries. We make this call for the alias and                            lose the information after a few queries. We make this call for the alias and 
Line 1055  unsigned char *tcp_request(int confd, time_t now, Line 1929  unsigned char *tcp_request(int confd, time_t now,
                       /* If the crc of the question section doesn't match the crc we sent, then                        /* If the crc of the question section doesn't match the crc we sent, then
                          someone might be attempting to insert bogus values into the cache by                            someone might be attempting to insert bogus values into the cache by 
                          sending replies containing questions and bogus answers. */                           sending replies containing questions and bogus answers. */
                      if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))#ifdef HAVE_DNSSEC
                        m = process_reply(header, now, last_server, (unsigned int)m,                       newhash = hash_questions(header, (unsigned int)m, daemon->namebuff);
                                          option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);                      if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0)
                         { 
                           m = 0;
                           break;
                         }
 #else                          
                       if (crc != questions_crc(header, (unsigned int)m, daemon->namebuff))
                         {
                           m = 0;
                           break;
                         }
 #endif
 
                       m = process_reply(header, now, last_server, (unsigned int)m, 
                                         option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
                                         ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr); 
                                               
                       break;                        break;
                     }                      }
Line 1069  unsigned char *tcp_request(int confd, time_t now, Line 1958  unsigned char *tcp_request(int confd, time_t now,
             }              }
         }          }
                       
      check_log_writer(NULL);      check_log_writer(1);
               
      c1 = m>>8;      *length = htons(m);
      c2 = m;           
      if (m == 0 ||      if (m == 0 || !read_write(confd, packet, m + sizeof(u16), 0))
          !read_write(confd, &c1, 1, 0) || 
          !read_write(confd, &c2, 1, 0) ||  
          !read_write(confd, packet, m, 0)) 
         return packet;          return packet;
     }      }
 }  }
Line 1095  static struct frec *allocate_frec(time_t now) Line 1981  static struct frec *allocate_frec(time_t now)
 #ifdef HAVE_IPV6  #ifdef HAVE_IPV6
       f->rfd6 = NULL;        f->rfd6 = NULL;
 #endif  #endif
   #ifdef HAVE_DNSSEC
         f->dependent = NULL;
         f->blocking_query = NULL;
         f->stash = NULL;
   #endif
       daemon->frec_list = f;        daemon->frec_list = f;
     }      }
   
   return f;    return f;
 }  }
   
static struct randfd *allocate_rfd(int family)struct randfd *allocate_rfd(int family)
 {  {
   static int finger = 0;    static int finger = 0;
   int i;    int i;
Line 1137  static struct randfd *allocate_rfd(int family) Line 2028  static struct randfd *allocate_rfd(int family)
   return NULL; /* doom */    return NULL; /* doom */
 }  }
   
   void free_rfd(struct randfd *rfd)
   {
     if (rfd && --(rfd->refcount) == 0)
       close(rfd->fd);
   }
   
 static void free_frec(struct frec *f)  static void free_frec(struct frec *f)
 {  {
  if (f->rfd4 && --(f->rfd4->refcount) == 0)  free_rfd(f->rfd4);
    close(f->rfd4->fd); 
     
   f->rfd4 = NULL;    f->rfd4 = NULL;
   f->sentto = NULL;    f->sentto = NULL;
   f->flags = 0;    f->flags = 0;
       
 #ifdef HAVE_IPV6  #ifdef HAVE_IPV6
  if (f->rfd6 && --(f->rfd6->refcount) == 0)  free_rfd(f->rfd6);
    close(f->rfd6->fd); 
     
   f->rfd6 = NULL;    f->rfd6 = NULL;
 #endif  #endif
   
   #ifdef HAVE_DNSSEC
     if (f->stash)
       {
         blockdata_free(f->stash);
         f->stash = NULL;
       }
   
     /* Anything we're waiting on is pointless now, too */
     if (f->blocking_query)
       free_frec(f->blocking_query);
     f->blocking_query = NULL;
     f->dependent = NULL;
   #endif
 }  }
   
 /* if wait==NULL return a free or older than TIMEOUT record.  /* if wait==NULL return a free or older than TIMEOUT record.
    else return *wait zero if one available, or *wait is delay to     else return *wait zero if one available, or *wait is delay to
    when the oldest in-use record will expire. Impose an absolute     when the oldest in-use record will expire. Impose an absolute
   limit of 4*TIMEOUT before we wipe things (for random sockets) */   limit of 4*TIMEOUT before we wipe things (for random sockets).
struct frec *get_new_frec(time_t now, int *wait)   If force is set, always return a result, even if we have
    to allocate above the limit. */
 struct frec *get_new_frec(time_t now, int *wait, int force)
 {  {
   struct frec *f, *oldest, *target;    struct frec *f, *oldest, *target;
   int count;    int count;
Line 1171  struct frec *get_new_frec(time_t now, int *wait) Line 2080  struct frec *get_new_frec(time_t now, int *wait)
       target = f;        target = f;
     else       else 
       {        {
        if (difftime(now, f->time) >= 4*TIMEOUT)#ifdef HAVE_DNSSEC
          {            /* Don't free DNSSEC sub-queries here, as we may end up with
            free_frec(f);               dangling references to them. They'll go when their "real" query 
            target = f;               is freed. */
          }            if (!f->dependent)
        #endif
        if (!oldest || difftime(f->time, oldest->time) <= 0)              {
          oldest = f;                if (difftime(now, f->time) >= 4*TIMEOUT)
                   {
                     free_frec(f);
                     target = f;
                   }
              
             
                 if (!oldest || difftime(f->time, oldest->time) <= 0)
                   oldest = f;
               }
       }        }
   
   if (target)    if (target)
Line 1207  struct frec *get_new_frec(time_t now, int *wait) Line 2125  struct frec *get_new_frec(time_t now, int *wait)
     }      }
       
   /* none available, calculate time 'till oldest record expires */    /* none available, calculate time 'till oldest record expires */
  if (count > daemon->ftabsize)  if (!force && count > daemon->ftabsize)
     {      {
         static time_t last_log = 0;
         
       if (oldest && wait)        if (oldest && wait)
         *wait = oldest->time + (time_t)TIMEOUT - now;          *wait = oldest->time + (time_t)TIMEOUT - now;
         
         if ((int)difftime(now, last_log) > 5)
           {
             last_log = now;
             my_syslog(LOG_WARNING, _("Maximum number of concurrent DNS queries reached (max: %d)"), daemon->ftabsize);
           }
   
       return NULL;        return NULL;
     }      }
       
Line 1222  struct frec *get_new_frec(time_t now, int *wait) Line 2149  struct frec *get_new_frec(time_t now, int *wait)
 }  }
     
 /* crc is all-ones if not known. */  /* crc is all-ones if not known. */
static struct frec *lookup_frec(unsigned short id, unsigned int crc)static struct frec *lookup_frec(unsigned short id, void *hash)
 {  {
   struct frec *f;    struct frec *f;
   
   for(f = daemon->frec_list; f; f = f->next)    for(f = daemon->frec_list; f; f = f->next)
     if (f->sentto && f->new_id == id &&       if (f->sentto && f->new_id == id && 
        (f->crc == crc || crc == 0xffffffff))        (!hash || memcmp(hash, f->hash, HASH_SIZE) == 0))
       return f;        return f;
               
   return NULL;    return NULL;
Line 1236  static struct frec *lookup_frec(unsigned short id, uns Line 2163  static struct frec *lookup_frec(unsigned short id, uns
   
 static struct frec *lookup_frec_by_sender(unsigned short id,  static struct frec *lookup_frec_by_sender(unsigned short id,
                                           union mysockaddr *addr,                                            union mysockaddr *addr,
                                          unsigned int crc)                                          void *hash)
 {  {
   struct frec *f;    struct frec *f;
       
   for(f = daemon->frec_list; f; f = f->next)    for(f = daemon->frec_list; f; f = f->next)
     if (f->sentto &&      if (f->sentto &&
         f->orig_id == id &&           f->orig_id == id && 
        f->crc == crc &&        memcmp(hash, f->hash, HASH_SIZE) == 0 &&
         sockaddr_isequal(&f->source, addr))          sockaddr_isequal(&f->source, addr))
       return f;        return f;
         
   return NULL;    return NULL;
 }  }
    
   /* Send query packet again, if we can. */
   void resend_query()
   {
     if (daemon->srv_save)
       {
         int fd;
         
         if (daemon->srv_save->sfd)
           fd = daemon->srv_save->sfd->fd;
         else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
           fd = daemon->rfd_save->fd;
         else
           return;
         
         while(retry_send(sendto(fd, daemon->packet, daemon->packet_len, 0,
                                 &daemon->srv_save->addr.sa, 
                                 sa_len(&daemon->srv_save->addr)))); 
       }
   }
   
 /* A server record is going away, remove references to it */  /* A server record is going away, remove references to it */
 void server_gone(struct server *server)  void server_gone(struct server *server)
Line 1267  void server_gone(struct server *server) Line 2214  void server_gone(struct server *server)
 }  }
   
 /* return unique random ids. */  /* return unique random ids. */
static unsigned short get_id(unsigned int crc)static unsigned short get_id(void)
 {  {
   unsigned short ret = 0;    unsigned short ret = 0;
       
   do     do 
     ret = rand16();      ret = rand16();
  while (lookup_frec(ret, crc));  while (lookup_frec(ret, NULL));
       
   return ret;    return ret;
 }  }

Removed from v.1.1  
changed lines
  Added in v.1.1.1.3


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>