xref: /openbsd/usr.sbin/nsd/axfr.c (revision 771fbea0)
1 /*
2  * axfr.c -- generating AXFR responses.
3  *
4  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
5  *
6  * See LICENSE for the license.
7  *
8  */
9 
10 #include "config.h"
11 
12 #include "axfr.h"
13 #include "dns.h"
14 #include "packet.h"
15 #include "options.h"
16 
17 /* draft-ietf-dnsop-rfc2845bis-06, section 5.3.1 says to sign every packet */
18 #define AXFR_TSIG_SIGN_EVERY_NTH	0	/* tsig sign every N packets. */
19 
20 query_state_type
21 query_axfr(struct nsd *nsd, struct query *query)
22 {
23 	domain_type *closest_match;
24 	domain_type *closest_encloser;
25 	int exact;
26 	int added;
27 	uint16_t total_added = 0;
28 
29 	if (query->axfr_is_done)
30 		return QUERY_PROCESSED;
31 
32 	if (query->maxlen > AXFR_MAX_MESSAGE_LEN)
33 		query->maxlen = AXFR_MAX_MESSAGE_LEN;
34 
35 	assert(!query_overflow(query));
36 	/* only keep running values for most packets */
37 	query->tsig_prepare_it = 0;
38 	query->tsig_update_it = 1;
39 	if(query->tsig_sign_it) {
40 		/* prepare for next updates */
41 		query->tsig_prepare_it = 1;
42 		query->tsig_sign_it = 0;
43 	}
44 
45 	if (query->axfr_zone == NULL) {
46 		domain_type* qdomain;
47 		/* Start AXFR.  */
48 		STATUP(nsd, raxfr);
49 		exact = namedb_lookup(nsd->db,
50 				      query->qname,
51 				      &closest_match,
52 				      &closest_encloser);
53 
54 		qdomain = closest_encloser;
55 		query->axfr_zone = domain_find_zone(nsd->db, closest_encloser);
56 
57 		if (!exact
58 		    || query->axfr_zone == NULL
59 		    || query->axfr_zone->apex != qdomain
60 		    || query->axfr_zone->soa_rrset == NULL)
61 		{
62 			/* No SOA no transfer */
63 			RCODE_SET(query->packet, RCODE_NOTAUTH);
64 			return QUERY_PROCESSED;
65 		}
66 		ZTATUP(nsd, query->axfr_zone, raxfr);
67 
68 		query->axfr_current_domain = qdomain;
69 		query->axfr_current_rrset = NULL;
70 		query->axfr_current_rr = 0;
71 		if(query->tsig.status == TSIG_OK) {
72 			query->tsig_sign_it = 1; /* sign first packet in stream */
73 		}
74 
75 		query_add_compression_domain(query, qdomain, QHEADERSZ);
76 
77 		assert(query->axfr_zone->soa_rrset->rr_count == 1);
78 		added = packet_encode_rr(query,
79 					 query->axfr_zone->apex,
80 					 &query->axfr_zone->soa_rrset->rrs[0],
81 					 query->axfr_zone->soa_rrset->rrs[0].ttl);
82 		if (!added) {
83 			/* XXX: This should never happen... generate error code? */
84 			abort();
85 		}
86 		++total_added;
87 	} else {
88 		/*
89 		 * Query name and EDNS need not be repeated after the
90 		 * first response packet.
91 		 */
92 		query->edns.status = EDNS_NOT_PRESENT;
93 		buffer_set_limit(query->packet, QHEADERSZ);
94 		QDCOUNT_SET(query->packet, 0);
95 		query_prepare_response(query);
96 	}
97 
98 	/* Add zone RRs until answer is full.  */
99 	while (query->axfr_current_domain != NULL &&
100 			domain_is_subdomain(query->axfr_current_domain,
101 					    query->axfr_zone->apex))
102 	{
103 		if (!query->axfr_current_rrset) {
104 			query->axfr_current_rrset = domain_find_any_rrset(
105 				query->axfr_current_domain,
106 				query->axfr_zone);
107 			query->axfr_current_rr = 0;
108 		}
109 		while (query->axfr_current_rrset) {
110 			if (query->axfr_current_rrset != query->axfr_zone->soa_rrset
111 			    && query->axfr_current_rrset->zone == query->axfr_zone)
112 			{
113 				while (query->axfr_current_rr < query->axfr_current_rrset->rr_count) {
114 					added = packet_encode_rr(
115 						query,
116 						query->axfr_current_domain,
117 						&query->axfr_current_rrset->rrs[query->axfr_current_rr],
118 						query->axfr_current_rrset->rrs[query->axfr_current_rr].ttl);
119 					if (!added)
120 						goto return_answer;
121 					++total_added;
122 					++query->axfr_current_rr;
123 				}
124 			}
125 
126 			query->axfr_current_rrset = query->axfr_current_rrset->next;
127 			query->axfr_current_rr = 0;
128 		}
129 		assert(query->axfr_current_domain);
130 		query->axfr_current_domain
131 			= domain_next(query->axfr_current_domain);
132 	}
133 
134 	/* Add terminating SOA RR.  */
135 	assert(query->axfr_zone->soa_rrset->rr_count == 1);
136 	added = packet_encode_rr(query,
137 				 query->axfr_zone->apex,
138 				 &query->axfr_zone->soa_rrset->rrs[0],
139 				 query->axfr_zone->soa_rrset->rrs[0].ttl);
140 	if (added) {
141 		++total_added;
142 		query->tsig_sign_it = 1; /* sign last packet */
143 		query->axfr_is_done = 1;
144 	}
145 
146 return_answer:
147 	AA_SET(query->packet);
148 	ANCOUNT_SET(query->packet, total_added);
149 	NSCOUNT_SET(query->packet, 0);
150 	ARCOUNT_SET(query->packet, 0);
151 
152 	/* check if it needs tsig signatures */
153 	if(query->tsig.status == TSIG_OK) {
154 #if AXFR_TSIG_SIGN_EVERY_NTH > 0
155 		if(query->tsig.updates_since_last_prepare >= AXFR_TSIG_SIGN_EVERY_NTH) {
156 #endif
157 			query->tsig_sign_it = 1;
158 #if AXFR_TSIG_SIGN_EVERY_NTH > 0
159 		}
160 #endif
161 	}
162 	query_clear_compression_tables(query);
163 	return QUERY_IN_AXFR;
164 }
165 
166 /*
167  * Answer if this is an AXFR or IXFR query.
168  */
169 query_state_type
170 answer_axfr_ixfr(struct nsd *nsd, struct query *q)
171 {
172 	struct acl_options *acl = NULL;
173 	/* Is it AXFR? */
174 	switch (q->qtype) {
175 	case TYPE_AXFR:
176 		if (q->tcp) {
177 			struct zone_options* zone_opt;
178 			zone_opt = zone_options_find(nsd->options, q->qname);
179 			if(!zone_opt ||
180 			   acl_check_incoming(zone_opt->pattern->provide_xfr, q, &acl)==-1)
181 			{
182 				if (verbosity >= 2) {
183 					char a[128];
184 					addr2str(&q->addr, a, sizeof(a));
185 					VERBOSITY(2, (LOG_INFO, "axfr for %s from %s refused, %s",
186 						dname_to_string(q->qname, NULL), a, acl?"blocked":"no acl matches"));
187 				}
188 				DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr refused, %s",
189 					acl?"blocked":"no acl matches"));
190 				if (!zone_opt) {
191 					RCODE_SET(q->packet, RCODE_NOTAUTH);
192 				} else {
193 					RCODE_SET(q->packet, RCODE_REFUSE);
194 					/* RFC8914 - Extended DNS Errors
195 					 * 4.19.  Extended DNS Error Code 18 - Prohibited */
196 					q->edns.ede = EDE_PROHIBITED;
197 				}
198 				return QUERY_PROCESSED;
199 			}
200 			DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr admitted acl %s %s",
201 				acl->ip_address_spec, acl->key_name?acl->key_name:"NOKEY"));
202 			if (verbosity >= 1) {
203 				char a[128];
204 				addr2str(&q->addr, a, sizeof(a));
205 				VERBOSITY(1, (LOG_INFO, "%s for %s from %s",
206 					(q->qtype==TYPE_AXFR?"axfr":"ixfr"),
207 					dname_to_string(q->qname, NULL), a));
208 			}
209 			return query_axfr(nsd, q);
210 		}
211 		/* AXFR over UDP queries are discarded. */
212 		RCODE_SET(q->packet, RCODE_IMPL);
213 		return QUERY_PROCESSED;
214 	case TYPE_IXFR:
215 		/* get rid of authority section, if present */
216 		NSCOUNT_SET(q->packet, 0);
217 		if(QDCOUNT(q->packet) > 0 && (size_t)QHEADERSZ+4+
218 			q->qname->name_size <= buffer_limit(q->packet)) {
219 			buffer_set_position(q->packet, QHEADERSZ+4+
220 				q->qname->name_size);
221 		}
222 		RCODE_SET(q->packet, RCODE_IMPL);
223 		return QUERY_PROCESSED;
224 	default:
225 		return QUERY_DISCARDED;
226 	}
227 }
228