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