"Fossies" - the Fresh Open Source Software Archive

Member "nsd-4.3.7/edns.c" (22 Jul 2021, 8978 Bytes) of package /linux/misc/dns/nsd-4.3.7.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "edns.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.3.6_vs_4.3.7.

    1 /*
    2  * edns.c -- EDNS definitions (RFC 2671).
    3  *
    4  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
    5  *
    6  * See LICENSE for the license.
    7  *
    8  */
    9 
   10 
   11 #include "config.h"
   12 
   13 #include <string.h>
   14 #ifdef HAVE_SSL
   15 #include <openssl/opensslv.h>
   16 #include <openssl/evp.h>
   17 #endif
   18 
   19 #include "dns.h"
   20 #include "edns.h"
   21 #include "nsd.h"
   22 #include "query.h"
   23 
   24 void
   25 edns_init_data(edns_data_type *data, uint16_t max_length)
   26 {
   27     memset(data, 0, sizeof(edns_data_type));
   28     /* record type: OPT */
   29     data->ok[1] = (TYPE_OPT & 0xff00) >> 8; /* type_hi */
   30     data->ok[2] = TYPE_OPT & 0x00ff;    /* type_lo */
   31     /* udp payload size */
   32     data->ok[3] = (max_length & 0xff00) >> 8; /* size_hi */
   33     data->ok[4] = max_length & 0x00ff;    /* size_lo */
   34 
   35     data->error[1] = (TYPE_OPT & 0xff00) >> 8;  /* type_hi */
   36     data->error[2] = TYPE_OPT & 0x00ff;     /* type_lo */
   37     data->error[3] = (max_length & 0xff00) >> 8;    /* size_hi */
   38     data->error[4] = max_length & 0x00ff;       /* size_lo */
   39     data->error[5] = 1; /* XXX Extended RCODE=BAD VERS */
   40 
   41     /* COOKIE OPT HDR */
   42     data->cookie[0] = (COOKIE_CODE & 0xff00) >> 8;
   43     data->cookie[1] = (COOKIE_CODE & 0x00ff);
   44     data->cookie[2] = (24 & 0xff00) >> 8;
   45     data->cookie[3] = (24 & 0x00ff);
   46 }
   47 
   48 void
   49 edns_init_nsid(edns_data_type *data, uint16_t nsid_len)
   50 {
   51        /* NSID OPT HDR */
   52        data->nsid[0] = (NSID_CODE & 0xff00) >> 8;
   53        data->nsid[1] = (NSID_CODE & 0x00ff);
   54        data->nsid[2] = (nsid_len & 0xff00) >> 8;
   55        data->nsid[3] = (nsid_len & 0x00ff);
   56 }
   57 
   58 void
   59 edns_init_record(edns_record_type *edns)
   60 {
   61     edns->status = EDNS_NOT_PRESENT;
   62     edns->position = 0;
   63     edns->maxlen = 0;
   64     edns->opt_reserved_space = 0;
   65     edns->dnssec_ok = 0;
   66     edns->nsid = 0;
   67     edns->cookie_status = COOKIE_NOT_PRESENT;
   68     edns->cookie_len = 0;
   69     edns->ede = -1; /* -1 means no Extended DNS Error */
   70     edns->ede_text = NULL;
   71     edns->ede_text_len = 0;
   72 }
   73 
   74 /** handle a single edns option in the query */
   75 static int
   76 edns_handle_option(uint16_t optcode, uint16_t optlen, buffer_type* packet,
   77     edns_record_type* edns, struct query* query, nsd_type* nsd)
   78 {
   79     (void) query; /* in case edns options need the query structure */
   80     /* handle opt code and read the optlen bytes from the packet */
   81     switch(optcode) {
   82     case NSID_CODE:
   83         /* is NSID enabled? */
   84         if(nsd->nsid_len > 0) {
   85             edns->nsid = 1;
   86             /* we have to check optlen, and move the buffer along */
   87             buffer_skip(packet, optlen);
   88             /* in the reply we need space for optcode+optlen+nsid_bytes */
   89             edns->opt_reserved_space += OPT_HDR + nsd->nsid_len;
   90         } else {
   91             /* ignore option */
   92             buffer_skip(packet, optlen);
   93         }
   94         break;
   95     case COOKIE_CODE:
   96         /* Cookies enabled? */
   97         if(nsd->do_answer_cookie) {
   98             if (optlen == 8) 
   99                 edns->cookie_status = COOKIE_INVALID;
  100             else if (optlen < 16 || optlen > 40)
  101                 return 0; /* FORMERR */
  102             else
  103                 edns->cookie_status = COOKIE_UNVERIFIED;
  104 
  105             edns->cookie_len = optlen;
  106             memcpy(edns->cookie, buffer_current(packet), optlen);
  107             buffer_skip(packet, optlen);
  108             edns->opt_reserved_space += OPT_HDR + 24;
  109         } else {
  110             buffer_skip(packet, optlen);
  111         }
  112         break;
  113     default:
  114         buffer_skip(packet, optlen);
  115         break;
  116     }
  117     return 1;
  118 }
  119 
  120 int
  121 edns_parse_record(edns_record_type *edns, buffer_type *packet,
  122     query_type* query, nsd_type* nsd)
  123 {
  124     /* OPT record type... */
  125     uint8_t  opt_owner;
  126     uint16_t opt_type;
  127     uint16_t opt_class;
  128     uint8_t  opt_version;
  129     uint16_t opt_flags;
  130     uint16_t opt_rdlen;
  131 
  132     edns->position = buffer_position(packet);
  133 
  134     if (!buffer_available(packet, (OPT_LEN + OPT_RDATA)))
  135         return 0;
  136 
  137     opt_owner = buffer_read_u8(packet);
  138     opt_type = buffer_read_u16(packet);
  139     if (opt_owner != 0 || opt_type != TYPE_OPT) {
  140         /* Not EDNS.  */
  141         buffer_set_position(packet, edns->position);
  142         return 0;
  143     }
  144 
  145     opt_class = buffer_read_u16(packet);
  146     (void)buffer_read_u8(packet); /* opt_extended_rcode */
  147     opt_version = buffer_read_u8(packet);
  148     opt_flags = buffer_read_u16(packet);
  149     opt_rdlen = buffer_read_u16(packet);
  150 
  151     if (opt_version != 0) {
  152         /* The only error is VERSION not implemented */
  153         edns->status = EDNS_ERROR;
  154         return 1;
  155     }
  156 
  157     if (opt_rdlen > 0) {
  158         if(!buffer_available(packet, opt_rdlen))
  159             return 0;
  160         if(opt_rdlen > 65530)
  161             return 0;
  162         /* there is more to come, read opt code */
  163         while(opt_rdlen >= 4) {
  164             uint16_t optcode = buffer_read_u16(packet);
  165             uint16_t optlen = buffer_read_u16(packet);
  166             opt_rdlen -= 4;
  167             if(opt_rdlen < optlen)
  168                 return 0; /* opt too long, formerr */
  169             opt_rdlen -= optlen;
  170             if(!edns_handle_option(optcode, optlen, packet,
  171                 edns, query, nsd))
  172                 return 0;
  173         }
  174         if(opt_rdlen != 0)
  175             return 0;
  176     }
  177 
  178     edns->status = EDNS_OK;
  179     edns->maxlen = opt_class;
  180     edns->dnssec_ok = opt_flags & DNSSEC_OK_MASK;
  181     return 1;
  182 }
  183 
  184 size_t
  185 edns_reserved_space(edns_record_type *edns)
  186 {
  187     /* MIEK; when a pkt is too large?? */
  188     return edns->status == EDNS_NOT_PRESENT ? 0
  189          : (OPT_LEN + OPT_RDATA + edns->opt_reserved_space);
  190 }
  191 
  192 int siphash(const uint8_t *in, const size_t inlen,
  193                 const uint8_t *k, uint8_t *out, const size_t outlen);
  194 
  195 /** RFC 1982 comparison, uses unsigned integers, and tries to avoid
  196  * compiler optimization (eg. by avoiding a-b<0 comparisons),
  197  * this routine matches compare_serial(), for SOA serial number checks */
  198 static int
  199 compare_1982(uint32_t a, uint32_t b)
  200 {
  201     /* for 32 bit values */
  202     const uint32_t cutoff = ((uint32_t) 1 << (32 - 1));
  203 
  204     if (a == b) {
  205         return 0;
  206     } else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) {
  207         return -1;
  208     } else {
  209         return 1;
  210     }
  211 }
  212 
  213 /** if we know that b is larger than a, return the difference between them,
  214  * that is the distance between them. in RFC1982 arith */
  215 static uint32_t
  216 subtract_1982(uint32_t a, uint32_t b)
  217 {
  218     /* for 32 bit values */
  219     const uint32_t cutoff = ((uint32_t) 1 << (32 - 1));
  220 
  221     if(a == b)
  222         return 0;
  223     if(a < b && b - a < cutoff) {
  224         return b-a;
  225     }
  226     if(a > b && a - b > cutoff) {
  227         return ((uint32_t)0xffffffff) - (a-b-1);
  228     }
  229     /* wrong case, b smaller than a */
  230     return 0;
  231 }
  232 
  233 void cookie_verify(query_type *q, struct nsd* nsd, uint32_t *now_p) {
  234     uint8_t hash[8], hash2verify[8];
  235     uint32_t cookie_time, now_uint32;
  236     size_t verify_size;
  237     int i;
  238 
  239     /* We support only draft-sury-toorop-dnsop-server-cookies sizes */
  240     if(q->edns.cookie_len != 24)
  241         return;
  242 
  243     if(q->edns.cookie[8] != 1)
  244         return;
  245 
  246     q->edns.cookie_status = COOKIE_INVALID;
  247 
  248     cookie_time = (q->edns.cookie[12] << 24)
  249                 | (q->edns.cookie[13] << 16)
  250                 | (q->edns.cookie[14] <<  8)
  251                 |  q->edns.cookie[15];
  252     
  253     now_uint32 = *now_p ? *now_p : (*now_p = (uint32_t)time(NULL));
  254 
  255     if(compare_1982(now_uint32, cookie_time) > 0) {
  256         /* ignore cookies > 1 hour in past */
  257         if (subtract_1982(cookie_time, now_uint32) > 3600)
  258             return;
  259     } else if (subtract_1982(now_uint32, cookie_time) > 300) {
  260         /* ignore cookies > 5 minutes in future */
  261         return;
  262     }
  263 
  264     memcpy(hash2verify, q->edns.cookie + 16, 8);
  265 
  266 #ifdef INET6
  267     if(q->addr.ss_family == AF_INET6) {
  268         memcpy(q->edns.cookie + 16, &((struct sockaddr_in6 *)&q->addr)->sin6_addr, 16);
  269         verify_size = 32;
  270     } else {
  271         memcpy(q->edns.cookie + 16, &((struct sockaddr_in *)&q->addr)->sin_addr, 4);
  272         verify_size = 20;
  273     }
  274 #else
  275     memcpy( q->edns.cookie + 16, &q->addr.sin_addr, 4);
  276     verify_size = 20;
  277 #endif
  278 
  279     q->edns.cookie_status = COOKIE_INVALID;
  280     siphash(q->edns.cookie, verify_size,
  281         nsd->cookie_secrets[0].cookie_secret, hash, 8);
  282     if(CRYPTO_memcmp(hash2verify, hash, 8) == 0 ) {
  283         if (subtract_1982(cookie_time, now_uint32) < 1800) {
  284             q->edns.cookie_status = COOKIE_VALID_REUSE;
  285             memcpy(q->edns.cookie + 16, hash, 8);
  286         } else
  287             q->edns.cookie_status = COOKIE_VALID;
  288         return;
  289     }
  290     for(i = 1;
  291         i < (int)nsd->cookie_count && i < NSD_COOKIE_HISTORY_SIZE;
  292         i++) {
  293         siphash(q->edns.cookie, verify_size,
  294                 nsd->cookie_secrets[i].cookie_secret, hash, 8);
  295         if(CRYPTO_memcmp(hash2verify, hash, 8) == 0 ) {
  296             q->edns.cookie_status = COOKIE_VALID;
  297             return;
  298         }
  299     }
  300 }
  301 
  302 void cookie_create(query_type *q, struct nsd* nsd, uint32_t *now_p)
  303 {
  304     uint8_t  hash[8];
  305     uint32_t now_uint32;
  306        
  307     if (q->edns.cookie_status == COOKIE_VALID_REUSE)
  308         return;
  309 
  310     now_uint32 = *now_p ? *now_p : (*now_p = (uint32_t)time(NULL));
  311     q->edns.cookie[ 8] = 1;
  312     q->edns.cookie[ 9] = 0;
  313     q->edns.cookie[10] = 0;
  314     q->edns.cookie[11] = 0;
  315     q->edns.cookie[12] = (now_uint32 & 0xFF000000) >> 24;
  316     q->edns.cookie[13] = (now_uint32 & 0x00FF0000) >> 16;
  317     q->edns.cookie[14] = (now_uint32 & 0x0000FF00) >>  8;
  318     q->edns.cookie[15] =  now_uint32 & 0x000000FF;
  319 #ifdef INET6
  320     if (q->addr.ss_family == AF_INET6) {
  321         memcpy( q->edns.cookie + 16
  322               , &((struct sockaddr_in6 *)&q->addr)->sin6_addr, 16);
  323         siphash(q->edns.cookie, 32, nsd->cookie_secrets[0].cookie_secret, hash, 8);
  324     } else {
  325         memcpy( q->edns.cookie + 16
  326               , &((struct sockaddr_in *)&q->addr)->sin_addr, 4);
  327         siphash(q->edns.cookie, 20, nsd->cookie_secrets[0].cookie_secret, hash, 8);
  328     }
  329 #else
  330     memcpy( q->edns.cookie + 16, &q->addr.sin_addr, 4);
  331     siphash(q->edns.cookie, 20, nsd->cookie_secrets[0].cookie_secret, hash, 8);
  332 #endif
  333     memcpy(q->edns.cookie + 16, hash, 8);
  334 }
  335