diff --git a/daemon/worker.c b/daemon/worker.c index a5dd9bc02..b4e50b345 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -1550,6 +1550,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, return 0; } query_error(c->buffer, LDNS_RCODE_FORMERR, 0); + sldns_buffer_copy(c->dnscrypt_buffer, c->buffer); return 1; } dname_str(qinfo.qname, buf); @@ -1568,6 +1569,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, query_error(c->buffer, LDNS_RCODE_SERVFAIL, qinfo.qname_len); worker->stats.num_query_dnscrypt_cleartext++; + sldns_buffer_copy(c->dnscrypt_buffer, c->buffer); return 1; } worker->stats.num_query_dnscrypt_cert++; @@ -1828,7 +1830,13 @@ worker_handle_request(struct comm_point* c, void* arg, int error, server_stats_insquery(&worker->stats, c, qinfo.qtype, qinfo.qclass, &edns, repinfo); if(c->type != comm_udp) +#ifdef USE_DNSCRYPT + edns.udp_size = (c->dnscrypt && repinfo->is_dnscrypted) + ? sldns_buffer_capacity(c->buffer) - DNSCRYPT_REPLY_HEADER_SIZE + : 65535; +#else edns.udp_size = 65535; /* max size for TCP replies */ +#endif if(qinfo.qclass == LDNS_RR_CLASS_CH && answer_chaos(worker, &qinfo, &edns, repinfo, c->buffer)) { regional_free_all(worker->scratchpad); @@ -2112,7 +2120,7 @@ send_reply_rc: } } #ifdef USE_DNSCRYPT - if(!dnsc_handle_uncurved_request(repinfo)) { + if(!dnsc_handle_uncurved_request(repinfo, c->buffer)) { return 0; } #endif diff --git a/dnscrypt/dnscrypt.c b/dnscrypt/dnscrypt.c index 173484cdf..08f9dcb8c 100644 --- a/dnscrypt/dnscrypt.c +++ b/dnscrypt/dnscrypt.c @@ -474,10 +474,18 @@ dnscrypt_server_curve(const dnsccert *cert, uint8_t *const buf = sldns_buffer_begin(buffer); size_t len = sldns_buffer_limit(buffer); + if(len + DNSCRYPT_REPLY_HEADER_SIZE > sldns_buffer_capacity(buffer)) + return -1; + sldns_buffer_clear(buffer); + if(udp){ if (max_len > max_reply_size) max_len = max_reply_size; } + if(max_len > sldns_buffer_capacity(buffer)) + max_len = sldns_buffer_capacity(buffer); + if(max_len > 65535) + max_len = 65535; memcpy(nonce, client_nonce, crypto_box_HALF_NONCEBYTES); @@ -520,6 +528,7 @@ dnscrypt_server_curve(const dnsccert *cert, DNSCRYPT_MAGIC_HEADER_LEN, nonce, crypto_box_NONCEBYTES); + sldns_buffer_flip(buffer); sldns_buffer_set_limit(buffer, len + DNSCRYPT_REPLY_HEADER_SIZE); return 0; } @@ -912,12 +921,13 @@ dnsc_handle_curved_request(struct dnsc_env* dnscenv, } int -dnsc_handle_uncurved_request(struct comm_reply *repinfo) +dnsc_handle_uncurved_request(struct comm_reply *repinfo, + struct sldns_buffer* buffer) { if(!repinfo->c->dnscrypt) { return 1; } - sldns_buffer_copy(repinfo->c->dnscrypt_buffer, repinfo->c->buffer); + sldns_buffer_copy(repinfo->c->dnscrypt_buffer, buffer); if(!repinfo->is_dnscrypted) { return 1; } diff --git a/dnscrypt/dnscrypt.h b/dnscrypt/dnscrypt.h index b0da9b732..998237d48 100644 --- a/dnscrypt/dnscrypt.h +++ b/dnscrypt/dnscrypt.h @@ -128,7 +128,8 @@ int dnsc_handle_curved_request(struct dnsc_env* dnscenv, * \return 0 in case of failure. */ -int dnsc_handle_uncurved_request(struct comm_reply *repinfo); +int dnsc_handle_uncurved_request(struct comm_reply *repinfo, + struct sldns_buffer* buffer); /** * Computes the size of the shared secret cache entry. diff --git a/testdata/dnscrypt_cert.tdir/dnscrypt_cert.test b/testdata/dnscrypt_cert.tdir/dnscrypt_cert.test index fdb88e8f9..4631d02e1 100644 --- a/testdata/dnscrypt_cert.tdir/dnscrypt_cert.test +++ b/testdata/dnscrypt_cert.tdir/dnscrypt_cert.test @@ -9,20 +9,21 @@ PRE="../.." # do the test -# Query plain request over DNSCrypt channel get closed -# We use TCP to avoid hanging on waiting for UDP. -# We expect `outfile` to contain no DNS payload -echo "> dig TCP www.example.com. DNSCrypt port" -dig +tcp @127.0.0.1 -p $DNSCRYPT_PORT www.example.com. A | tee outfile +# Query plain request over DNSCrypt. +# This used to close the channel; now it returns SERVFAIL. +# Old: We use TCP to avoid hanging on waiting for UDP. +# We expect `outfile` to contain no DNS payload +echo "> dig www.example.com. DNSCrypt port" +dig @127.0.0.1 -p $DNSCRYPT_PORT www.example.com. A | tee outfile echo "> cat logfiles" cat fwd.log cat unbound.log echo "> check answer" -if grep "QUESTION SECTION" outfile; then +if grep "SERVFAIL" outfile; then + echo "OK" +else echo "NOK" exit 1 -else - echo "OK" fi diff --git a/testdata/dnscrypt_cert_chacha.tdir/dnscrypt_cert_chacha.test b/testdata/dnscrypt_cert_chacha.tdir/dnscrypt_cert_chacha.test index 2db073ad6..a92f196f5 100644 --- a/testdata/dnscrypt_cert_chacha.tdir/dnscrypt_cert_chacha.test +++ b/testdata/dnscrypt_cert_chacha.tdir/dnscrypt_cert_chacha.test @@ -9,20 +9,21 @@ PRE="../.." # do the test -# Query plain request over DNSCrypt channel get closed -# We use TCP to avoid hanging on waiting for UDP. -# We expect `outfile` to contain no DNS payload -echo "> dig TCP www.example.com. DNSCrypt port" -dig +tcp @127.0.0.1 -p $DNSCRYPT_PORT www.example.com. A | tee outfile +# Query plain request over DNSCrypt. +# This used to close the channel; now it returns SERVFAIL. +# Old: We use TCP to avoid hanging on waiting for UDP. +# We expect `outfile` to contain no DNS payload +echo "> dig www.example.com. DNSCrypt port" +dig @127.0.0.1 -p $DNSCRYPT_PORT www.example.com. A | tee outfile echo "> cat logfiles" cat fwd.log cat unbound.log echo "> check answer" -if grep "QUESTION SECTION" outfile; then +if grep "SERVFAIL" outfile; then + echo "OK" +else echo "NOK" exit 1 -else - echo "OK" fi diff --git a/util/netevent.c b/util/netevent.c index a86e22518..c5287e6c9 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -6685,7 +6685,9 @@ comm_point_send_reply(struct comm_reply *repinfo) log_assert(repinfo && repinfo->c); #ifdef USE_DNSCRYPT buffer = repinfo->c->dnscrypt_buffer; - if(!dnsc_handle_uncurved_request(repinfo)) { + if(!dnsc_handle_uncurved_request(repinfo, + repinfo->c->tcp_req_info? + repinfo->c->tcp_req_info->spool_buffer:repinfo->c->buffer)) { return; } #else