Blob Blame History Raw
Index: services/outside_network.c
===================================================================
--- services/outside_network.c	(revision 2491)
+++ services/outside_network.c	(revision 2493)
@@ -1199,6 +1199,7 @@
 		if(sq->status == serviced_query_UDP_EDNS ||
 			sq->status == serviced_query_UDP ||
 			sq->status == serviced_query_PROBE_EDNS ||
+			sq->status == serviced_query_UDP_EDNS_FRAG ||
 			sq->status == serviced_query_UDP_EDNS_fallback) {
 			struct pending* p = (struct pending*)sq->pending;
 			if(p->pc)
@@ -1280,7 +1281,19 @@
 		edns.edns_present = 1;
 		edns.ext_rcode = 0;
 		edns.edns_version = EDNS_ADVERTISED_VERSION;
-		edns.udp_size = EDNS_ADVERTISED_SIZE;
+		if(sq->status == serviced_query_UDP_EDNS_FRAG) {
+			if(addr_is_ip6(&sq->addr, sq->addrlen)) {
+				if(EDNS_FRAG_SIZE_IP6 < EDNS_ADVERTISED_SIZE)
+					edns.udp_size = EDNS_FRAG_SIZE_IP6;
+				else	edns.udp_size = EDNS_ADVERTISED_SIZE;
+			} else {
+				if(EDNS_FRAG_SIZE_IP4 < EDNS_ADVERTISED_SIZE)
+					edns.udp_size = EDNS_FRAG_SIZE_IP4;
+				else	edns.udp_size = EDNS_ADVERTISED_SIZE;
+			}
+		} else {
+			edns.udp_size = EDNS_ADVERTISED_SIZE;
+		}
 		edns.bits = 0;
 		if(sq->dnssec & EDNS_DO)
 			edns.bits = EDNS_DO;
@@ -1324,7 +1337,8 @@
 			sq->status = serviced_query_UDP; 
 		}
 	}
-	serviced_encode(sq, buff, sq->status == serviced_query_UDP_EDNS);
+	serviced_encode(sq, buff, (sq->status == serviced_query_UDP_EDNS) ||
+		(sq->status == serviced_query_UDP_EDNS_FRAG));
 	sq->last_sent_time = *sq->outnet->now_tv;
 	sq->edns_lame_known = (int)edns_lame_known;
 	verbose(VERB_ALGO, "serviced query UDP timeout=%d msec", rtt);
@@ -1564,6 +1578,20 @@
 			 * by EDNS. */
 			sq->status = serviced_query_UDP_EDNS;
 		}
+		if(sq->status == serviced_query_UDP_EDNS) {
+			/* fallback to 1480/1280 */
+			sq->status = serviced_query_UDP_EDNS_FRAG;
+			log_name_addr(VERB_ALGO, "try edns1xx0", sq->qbuf+10,
+				&sq->addr, sq->addrlen);
+			if(!serviced_udp_send(sq, c->buffer)) {
+				serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
+			}
+			return 0;
+		}
+		if(sq->status == serviced_query_UDP_EDNS_FRAG) {
+			/* fragmentation size did not fix it */
+			sq->status = serviced_query_UDP_EDNS;
+		}
 		sq->retry++;
 		if(!(rto=infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen,
 			-1, sq->last_rtt, (uint32_t)now.tv_sec)))
@@ -1589,7 +1617,8 @@
 		return 0;
 	}
 	if(!fallback_tcp) {
-	    if(sq->status == serviced_query_UDP_EDNS 
+	    if( (sq->status == serviced_query_UDP_EDNS 
+	        ||sq->status == serviced_query_UDP_EDNS_FRAG)
 		&& (LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) 
 			== LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(
 			ldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOTIMPL)) {
@@ -1866,6 +1895,7 @@
 	if(sq->status == serviced_query_UDP_EDNS ||
 		sq->status == serviced_query_UDP ||
 		sq->status == serviced_query_PROBE_EDNS ||
+		sq->status == serviced_query_UDP_EDNS_FRAG ||
 		sq->status == serviced_query_UDP_EDNS_fallback) {
 		s += sizeof(struct pending);
 		s += comm_timer_get_mem(NULL);
Index: services/outside_network.h
===================================================================
--- services/outside_network.h	(revision 2491)
+++ services/outside_network.h	(revision 2493)
@@ -274,6 +274,11 @@
 	void* cb_arg;
 };
 
+/** fallback size for fragmentation for EDNS in IPv4 */
+#define EDNS_FRAG_SIZE_IP4 1480
+/** fallback size for EDNS in IPv6, fits one fragment with ip6-tunnel-ids */
+#define EDNS_FRAG_SIZE_IP6 1260
+
 /**
  * Query service record.
  * Contains query and destination. UDP, TCP, EDNS are all tried.
@@ -314,7 +319,9 @@
 		/** probe to test noEDNS0 (EDNS gives FORMERRorNOTIMP) */
 		serviced_query_UDP_EDNS_fallback,
 		/** probe to test TCP noEDNS0 (EDNS gives FORMERRorNOTIMP) */
-		serviced_query_TCP_EDNS_fallback
+		serviced_query_TCP_EDNS_fallback,
+		/** send UDP query with EDNS1480 (or 1280) */
+		serviced_query_UDP_EDNS_FRAG
 	} 	
 		/** variable with current status */ 
 		status;