]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Fix keepalive logic
authorTom Carpay <tom@nlnetlabs.nl>
Mon, 1 Nov 2021 15:01:07 +0000 (15:01 +0000)
committerTom Carpay <tom@nlnetlabs.nl>
Mon, 1 Nov 2021 15:01:07 +0000 (15:01 +0000)
daemon/worker.c
util/data/msgparse.c

index 67b2e3166cc25cf27468a62508c29a01fae8d2d2..6a13dbe290498a380711cac823e499e8c8198cd1 100644 (file)
@@ -1258,7 +1258,6 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
                goto send_reply;
        }
        if(edns.edns_present) {
-               struct edns_option* edns_opt;
                if(edns.edns_version != 0) {
                        edns.ext_rcode = (uint8_t)(EDNS_RCODE_BADVERS>>4);
                        edns.edns_version = EDNS_ADVERTISED_VERSION;
@@ -1286,29 +1285,6 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
                        log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
                        edns.udp_size = NORMAL_UDP_SIZE;
                }
-               if(c->type != comm_udp) {
-                       /* @TODO reuse what we found at parse time */
-                       edns_opt = edns_opt_list_find(edns.opt_list_in, LDNS_EDNS_KEEPALIVE);
-                       if(edns_opt && edns_opt->opt_len > 0) {
-                               edns.ext_rcode = 0;
-                               edns.edns_version = EDNS_ADVERTISED_VERSION;
-                               edns.udp_size = EDNS_ADVERTISED_SIZE;
-                               edns.bits &= EDNS_DO;
-                               edns.opt_list_in = NULL;
-                               edns.opt_list_out = NULL;
-                               edns.opt_list_modules_out = NULL;
-                               verbose(VERB_ALGO, "query with bad edns keepalive.");
-                               log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
-                               error_encode(c->buffer, LDNS_RCODE_FORMERR, &qinfo,
-                                       *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
-                                       sldns_buffer_read_u16_at(c->buffer, 2), NULL);
-                               if(sldns_buffer_capacity(c->buffer) >=
-                                  sldns_buffer_limit(c->buffer)+calc_edns_field_size(&edns))
-                                       attach_edns_record(c->buffer, &edns);
-                               regional_free_all(worker->scratchpad);
-                               goto send_reply;
-                       }
-               }
        }
        if(edns.udp_size > worker->daemon->cfg->max_udp_size &&
                c->type == comm_udp) {
index 1fc7f2d55fc96be3d87188bd0ae76d45cac745eb..35f58c2fed8926bf96b8e6b59b6dbb521bf0cd70 100644 (file)
@@ -940,14 +940,36 @@ parse_packet(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region)
        return 0;
 }
 
+static int
+edns_opt_list_append_keepalive(struct edns_option** list, int msec,
+               struct regional* region)
+{
+       uint8_t data[2]; /* For keepalive value */
+       data[0] = (uint8_t)((msec >> 8) & 0xff);
+       data[1] = (uint8_t)(msec & 0xff);
+       return edns_opt_list_append(list, LDNS_EDNS_KEEPALIVE, sizeof(data),
+                       data, region);
+}
+
 /** parse EDNS options from EDNS wireformat rdata */
 static int
 parse_edns_options(uint8_t* rdata_ptr, size_t rdata_len,
        struct edns_data* edns, struct config_file* cfg, struct comm_point* c,
        struct regional* region)
 {
-       int keepalive;
-       uint8_t data[2]; /* For keepalive value */
+       /* To respond with a Keepalive option, the client connection must have
+        * received one message with a TCP Keepalive EDNS option, and that
+        * option must have 0 length data. Subsequent messages sent on that
+        * connection will have a TCP Keepalive option.
+        */
+       if (cfg && cfg->do_tcp_keepalive && c && c->type != comm_udp && c->tcp_keepalive) {
+               if(!edns_opt_list_append_keepalive(&edns->opt_list_out,
+                                       c->tcp_timeout_msec / 100, region)) {
+                       log_err("out of memory");
+                       return LDNS_RCODE_SERVFAIL;
+               }
+               c->tcp_keepalive = 1;
+       }
 
        /* while still more options, and have code+len to read */
        /* ignores partial content (i.e. rdata len 3) */
@@ -962,13 +984,13 @@ parse_edns_options(uint8_t* rdata_ptr, size_t rdata_len,
                /* handle parse time edns options here */
                switch(opt_code) {
                case LDNS_EDNS_NSID:
-                       if (!cfg->nsid)
+                       if (!cfg || !cfg->nsid)
                                break;
                        if(!edns_opt_list_append(&edns->opt_list_out,
                                                LDNS_EDNS_NSID, cfg->nsid_len,
                                                cfg->nsid, region)) {
                                log_err("out of memory");
-                               return 0;
+                               return LDNS_RCODE_SERVFAIL;
                        }
                        break;
 
@@ -979,29 +1001,31 @@ parse_edns_options(uint8_t* rdata_ptr, size_t rdata_len,
                         * length data. Subsequent messages sent on that
                         * connection will have a TCP Keepalive option.
                         */
-                       if (!cfg->do_tcp_keepalive || c->type != comm_udp ||
-                                       !c->tcp_keepalive)
+                       if (!cfg || !cfg->do_tcp_keepalive || !c ||
+                                       c->type == comm_udp || c->tcp_keepalive)
                                break;
-                       keepalive = c->tcp_timeout_msec / 100;
-                       data[0] = (uint8_t)((keepalive >> 8) & 0xff);
-                       data[1] = (uint8_t)(keepalive & 0xff);
-                       if(!edns_opt_list_append(&edns->opt_list_out,
-                                               LDNS_EDNS_KEEPALIVE,
-                                               sizeof(data), data, region)) {
+                       if(opt_len) {
+                               verbose(VERB_ALGO, "query with bad edns keepalive.");
+                               return LDNS_RCODE_FORMERR;
+                       }
+                       if(!edns_opt_list_append_keepalive(&edns->opt_list_out,
+                                               c->tcp_timeout_msec / 100,
+                                               region)) {
                                log_err("out of memory");
-                               return 0;
+                               return LDNS_RCODE_SERVFAIL;
                        }
                        c->tcp_keepalive = 1;
                        break;
 
                case LDNS_EDNS_PADDING:
-                       if(!cfg->pad_responses || c->type != comm_tcp ||!c->ssl)
+                       if(!cfg || !cfg->pad_responses ||
+                                       !c || c->type != comm_tcp ||!c->ssl)
                                break;
                        if(!edns_opt_list_append(&edns->opt_list_out,
                                                LDNS_EDNS_PADDING,
                                                0, NULL, region)) {
                                log_err("out of memory");
-                               return 0;
+                               return LDNS_RCODE_SERVFAIL;
                        }
                        edns->padding_block_size = cfg->pad_responses_block_size;
                        break;
@@ -1010,14 +1034,14 @@ parse_edns_options(uint8_t* rdata_ptr, size_t rdata_len,
                        if(!edns_opt_list_append(&edns->opt_list_in,
                                        opt_code, opt_len, rdata_ptr, region)) {
                                log_err("out of memory");
-                               return 0;
+                               return LDNS_RCODE_SERVFAIL;
                        }
                        break;
                }
                rdata_ptr += opt_len;
                rdata_len -= opt_len;
        }
-       return 1;
+       return 0;
 }
 
 int 
@@ -1084,7 +1108,7 @@ parse_extract_edns(struct msg_parse* msg, struct edns_data* edns,
        /* take the options */
        rdata_len = found->rr_first->size-2;
        rdata_ptr = found->rr_first->ttl_data+6;
-       if(!parse_edns_options(rdata_ptr, rdata_len, edns, NULL, NULL, region))
+       if(parse_edns_options(rdata_ptr, rdata_len, edns, NULL, NULL, region))
                return 0;
 
        /* ignore rrsigs */
@@ -1122,6 +1146,7 @@ int
 parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns,
        struct config_file* cfg, struct comm_point* c, struct regional* region)
 {
+       int rcode;
        size_t rdata_len;
        uint8_t* rdata_ptr;
        log_assert(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) == 1);
@@ -1162,8 +1187,9 @@ parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns,
        if(sldns_buffer_remaining(pkt) < rdata_len)
                return LDNS_RCODE_FORMERR;
        rdata_ptr = sldns_buffer_current(pkt);
-       if(!parse_edns_options(rdata_ptr, rdata_len, edns, cfg, c, region))
-               return LDNS_RCODE_SERVFAIL;
+       rcode = parse_edns_options(rdata_ptr, rdata_len, edns, cfg, c, region);
+       if(rcode)
+               return rcode;
 
        /* ignore rrsigs */