xref: /openbsd-src/usr.sbin/nsd/axfr.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*
2  * axfr.c -- generating AXFR responses.
3  *
4  * Copyright (c) 2001-2011, 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 		/* Start AXFR.  */
46 		exact = namedb_lookup(nsd->db,
47 				      query->qname,
48 				      &closest_match,
49 				      &closest_encloser);
50 
51 		query->domain = closest_encloser;
52 		query->axfr_zone = domain_find_zone(closest_encloser);
53 
54 		if (!exact
55 		    || query->axfr_zone == NULL
56 		    || query->axfr_zone->apex != query->domain)
57 		{
58 			/* No SOA no transfer */
59 			RCODE_SET(query->packet, RCODE_NOTAUTH);
60 			return QUERY_PROCESSED;
61 		}
62 
63 		query->axfr_current_domain
64 			= (domain_type *) rbtree_first(nsd->db->domains->names_to_domains);
65 		query->axfr_current_rrset = NULL;
66 		query->axfr_current_rr = 0;
67 		if(query->tsig.status == TSIG_OK) {
68 			query->tsig_sign_it = 1; /* sign first packet in stream */
69 		}
70 
71 		query_add_compression_domain(query, query->domain, QHEADERSZ);
72 
73 		assert(query->axfr_zone->soa_rrset->rr_count == 1);
74 		added = packet_encode_rr(query,
75 					 query->axfr_zone->apex,
76 					 &query->axfr_zone->soa_rrset->rrs[0]);
77 		if (!added) {
78 			/* XXX: This should never happen... generate error code? */
79 			abort();
80 		}
81 		++total_added;
82 	} else {
83 		/*
84 		 * Query name and EDNS need not be repeated after the
85 		 * first response packet.
86 		 */
87 		query->edns.status = EDNS_NOT_PRESENT;
88 		buffer_set_limit(query->packet, QHEADERSZ);
89 		QDCOUNT_SET(query->packet, 0);
90 		query_prepare_response(query);
91 	}
92 
93 	/* Add zone RRs until answer is full.  */
94 	assert(query->axfr_current_domain);
95 
96 	while ((rbnode_t *) query->axfr_current_domain != RBTREE_NULL) {
97 		if (!query->axfr_current_rrset) {
98 			query->axfr_current_rrset = domain_find_any_rrset(
99 				query->axfr_current_domain,
100 				query->axfr_zone);
101 			query->axfr_current_rr = 0;
102 		}
103 		while (query->axfr_current_rrset) {
104 			if (query->axfr_current_rrset != query->axfr_zone->soa_rrset
105 			    && query->axfr_current_rrset->zone == query->axfr_zone)
106 			{
107 				while (query->axfr_current_rr < query->axfr_current_rrset->rr_count) {
108 					added = packet_encode_rr(
109 						query,
110 						query->axfr_current_domain,
111 						&query->axfr_current_rrset->rrs[query->axfr_current_rr]);
112 					if (!added)
113 						goto return_answer;
114 					++total_added;
115 					++query->axfr_current_rr;
116 				}
117 			}
118 
119 			query->axfr_current_rrset = query->axfr_current_rrset->next;
120 			query->axfr_current_rr = 0;
121 		}
122 		assert(query->axfr_current_domain);
123 		query->axfr_current_domain
124 			= (domain_type *) rbtree_next((rbnode_t *) query->axfr_current_domain);
125 	}
126 
127 	/* Add terminating SOA RR.  */
128 	assert(query->axfr_zone->soa_rrset->rr_count == 1);
129 	added = packet_encode_rr(query,
130 				 query->axfr_zone->apex,
131 				 &query->axfr_zone->soa_rrset->rrs[0]);
132 	if (added) {
133 		++total_added;
134 		query->tsig_sign_it = 1; /* sign last packet */
135 		query->axfr_is_done = 1;
136 	}
137 
138 return_answer:
139 	AA_SET(query->packet);
140 	ANCOUNT_SET(query->packet, total_added);
141 	NSCOUNT_SET(query->packet, 0);
142 	ARCOUNT_SET(query->packet, 0);
143 
144 	/* check if it needs tsig signatures */
145 	if(query->tsig.status == TSIG_OK) {
146 		if(query->tsig.updates_since_last_prepare >= AXFR_TSIG_SIGN_EVERY_NTH) {
147 			query->tsig_sign_it = 1;
148 		}
149 	}
150 	query_clear_compression_tables(query);
151 	return QUERY_IN_AXFR;
152 }
153 
154 /*
155  * Answer if this is an AXFR or IXFR query.
156  */
157 query_state_type
158 answer_axfr_ixfr(struct nsd *nsd, struct query *q)
159 {
160 	acl_options_t *acl;
161 	/* Is it AXFR? */
162 	switch (q->qtype) {
163 	case TYPE_AXFR:
164 		if (q->tcp) {
165 			zone_options_t* zone_opt;
166 			zone_opt = zone_options_find(nsd->options, q->qname);
167 			if(!zone_opt ||
168 			   acl_check_incoming(zone_opt->provide_xfr, q, &acl)==-1)
169 			{
170 				if (verbosity > 0) {
171 					char address[128];
172 					if (addr2ip(q->addr, address, sizeof(address))) {
173 						DEBUG(DEBUG_XFRD,1, (LOG_INFO,
174 							"addr2ip failed"));
175 						strlcpy(address, "[unknown]", sizeof(address));
176 					}
177 					VERBOSITY(1, (LOG_INFO, "axfr for zone %s from client %s refused, %s",
178 						dname_to_string(q->qname, NULL), address, acl?"blocked":"no acl matches"));
179 				}
180 				DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr refused, %s",
181 					acl?"blocked":"no acl matches"));
182 				if (!zone_opt) {
183 					RCODE_SET(q->packet, RCODE_NOTAUTH);
184 				} else {
185 					RCODE_SET(q->packet, RCODE_REFUSE);
186 				}
187 				return QUERY_PROCESSED;
188 			}
189 			DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr admitted acl %s %s",
190 				acl->ip_address_spec, acl->key_name?acl->key_name:"NOKEY"));
191 			return query_axfr(nsd, q);
192 		}
193 	case TYPE_IXFR:
194 		RCODE_SET(q->packet, RCODE_IMPL);
195 		return QUERY_PROCESSED;
196 	default:
197 		return QUERY_DISCARDED;
198 	}
199 }
200