xref: /freebsd/contrib/ldns/update.c (revision 3cbb4cc200f8a0ad7ed08233425ea54524a21f1c)
1 /* update.c
2  *
3  * Functions for RFC 2136 Dynamic Update
4  *
5  * Copyright (c) 2005-2008, NLnet Labs. All rights reserved.
6  *
7  * See LICENSE for the license.
8  */
9 
10 #include <ldns/config.h>
11 
12 #include <ldns/ldns.h>
13 
14 #include <strings.h>
15 #include <stdlib.h>
16 #include <limits.h>
17 
18 /*
19  * RFC 2136 sections mapped to RFC 1035:
20  *              zone/ZO -- QD/question
21  *     prerequisites/PR -- AN/answers
22  *           updates/UP -- NS/authority records
23  *   additional data/AD -- AR/additional records
24  */
25 
26 ldns_pkt *
27 ldns_update_pkt_new(ldns_rdf *zone_rdf, ldns_rr_class c,
28     const ldns_rr_list *pr_rrlist, const ldns_rr_list *up_rrlist, const ldns_rr_list *ad_rrlist)
29 {
30 	ldns_pkt *p;
31 
32 	if (!zone_rdf || !up_rrlist) {
33 		return NULL;
34 	}
35 
36 	if (c == 0) {
37 		c = LDNS_RR_CLASS_IN;
38 	}
39 
40 	/* Create packet, fill in Zone Section. */
41 	p = ldns_pkt_query_new(zone_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD);
42 	if (!p) {
43 		return NULL;
44 	}
45 	zone_rdf = NULL; /* No longer safe to use. */
46 
47 	ldns_pkt_set_opcode(p, LDNS_PACKET_UPDATE);
48 
49 	ldns_rr_list_deep_free(p->_authority);
50 
51 	ldns_pkt_set_authority(p, ldns_rr_list_clone(up_rrlist));
52 
53 	ldns_update_set_upcount(p, ldns_rr_list_rr_count(up_rrlist));
54 
55 	if (pr_rrlist) {
56 		ldns_rr_list_deep_free(p->_answer); /*XXX access function */
57 		ldns_pkt_set_answer(p, ldns_rr_list_clone(pr_rrlist));
58 		ldns_update_set_prcount(p, ldns_rr_list_rr_count(pr_rrlist));
59 	}
60 
61 	if (ad_rrlist) {
62 		ldns_rr_list_deep_free(p->_additional);
63 		ldns_pkt_set_additional(p, ldns_rr_list_clone(ad_rrlist));
64 		ldns_update_set_adcount(p, ldns_rr_list_rr_count(ad_rrlist));
65 	}
66 	return p;
67 }
68 
69 ldns_status
70 ldns_update_pkt_tsig_add(ldns_pkt *p, const ldns_resolver *r)
71 {
72 #ifdef HAVE_SSL
73 	uint16_t fudge = 300; /* Recommended fudge. [RFC2845 6.4]  */
74 	if (ldns_resolver_tsig_keyname(r) && ldns_resolver_tsig_keydata(r))
75 		return ldns_pkt_tsig_sign(p, ldns_resolver_tsig_keyname(r),
76 		    ldns_resolver_tsig_keydata(r), fudge,
77 		    ldns_resolver_tsig_algorithm(r), NULL);
78 #else
79 	/* do nothing */
80 	(void)p;
81 	(void)r;
82 #endif /* HAVE_SSL */
83 	/* No TSIG to do. */
84 	return LDNS_STATUS_OK;
85 }
86 
87 /* Move to higher.c or similar? */
88 /* XXX doc */
89 ldns_status
90 ldns_update_soa_mname(ldns_rdf *zone, ldns_resolver *r,
91     ldns_rr_class c, ldns_rdf **mname)
92 {
93 	ldns_rr		*soa_rr;
94 	ldns_pkt	*query, *resp;
95 
96 	/* Nondestructive, so clone 'zone' here */
97 	query = ldns_pkt_query_new(ldns_rdf_clone(zone), LDNS_RR_TYPE_SOA,
98 	    c, LDNS_RD);
99 	if (!query) {
100 		return LDNS_STATUS_ERR;
101 	}
102 
103 	ldns_pkt_set_random_id(query);
104 	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
105 		ldns_pkt_free(query);
106 		return LDNS_STATUS_ERR;
107 	}
108 	ldns_pkt_free(query);
109 	if (!resp) {
110 		return LDNS_STATUS_ERR;
111 	}
112 
113 	/* Expect a SOA answer. */
114 	*mname = NULL;
115 	while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_answer(resp)))) {
116 		if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
117 				|| ldns_rr_rdf(soa_rr, 0) == NULL)
118 			continue;
119 		/* [RFC1035 3.3.13] */
120 		*mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
121 		break;
122 	}
123 	ldns_pkt_free(resp);
124 
125 	return *mname ? LDNS_STATUS_OK : LDNS_STATUS_ERR;
126 }
127 
128 /* Try to get zone and MNAME from SOA queries. */
129 ldns_status
130 ldns_update_soa_zone_mname(const char *fqdn, ldns_resolver *r,
131     ldns_rr_class c, ldns_rdf **zone_rdf, ldns_rdf **mname_rdf)
132 {
133 	ldns_rr		*soa_rr, *rr;
134 	ldns_rdf	*soa_zone = NULL, *soa_mname = NULL;
135 	ldns_rdf	*ipaddr, *fqdn_rdf, *tmp;
136 	ldns_rdf	**nslist;
137 	ldns_pkt	*query, *resp;
138 	ldns_resolver   *tmp_r;
139 	size_t		i;
140 
141 	/*
142 	 * XXX Ok, this cannot be the best way to find this...?
143 	 * XXX (I run into weird cache-related stuff here)
144 	 */
145 
146 	/* Step 1 - first find a nameserver that should know *something* */
147 	fqdn_rdf = ldns_dname_new_frm_str(fqdn);
148 	query = ldns_pkt_query_new(fqdn_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD);
149 	if (!query) {
150 		return LDNS_STATUS_ERR;
151 	}
152 	fqdn_rdf = NULL;
153 
154 	ldns_pkt_set_random_id(query);
155 	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
156 		ldns_pkt_free(query);
157 		return LDNS_STATUS_ERR;
158 	}
159 	ldns_pkt_free(query);
160 	if (!resp) {
161 		return LDNS_STATUS_ERR;
162 	}
163 
164 	/* XXX Is it safe to only look in authority section here? */
165 	while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_authority(resp)))) {
166 		if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
167 				|| ldns_rr_rdf(soa_rr, 0) == NULL)
168 			continue;
169 		/* [RFC1035 3.3.13] */
170 		soa_mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
171 		break;
172 	}
173 	ldns_pkt_free(resp);
174 	if (!soa_rr) {
175 		return LDNS_STATUS_ERR;
176 	}
177 
178 	/* Step 2 - find SOA MNAME IP address, add to resolver */
179 	query = ldns_pkt_query_new(soa_mname, LDNS_RR_TYPE_A, c, LDNS_RD);
180 	if (!query) {
181 		return LDNS_STATUS_ERR;
182 	}
183 	soa_mname = NULL;
184 
185 	ldns_pkt_set_random_id(query);
186 	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
187 		ldns_pkt_free(query);
188 		return LDNS_STATUS_ERR;
189 	}
190 	ldns_pkt_free(query);
191 	if (!resp) {
192 		return LDNS_STATUS_ERR;
193 	}
194 
195 	if (ldns_pkt_ancount(resp) == 0) {
196 		ldns_pkt_free(resp);
197 		return LDNS_STATUS_ERR;
198 	}
199 
200 	/* XXX There may be more than one answer RR here. */
201 	rr = ldns_rr_list_pop_rr(ldns_pkt_answer(resp));
202 	ipaddr = ldns_rr_rdf(rr, 0);
203 
204 	/* Put the SOA mname IP first in the nameserver list. */
205 	if (!(tmp_r = ldns_resolver_clone(r))) {
206 		return LDNS_STATUS_MEM_ERR;
207 	}
208 	nslist = ldns_resolver_nameservers(tmp_r);
209 	for (i = 0; i < ldns_resolver_nameserver_count(tmp_r); i++) {
210 		if (ldns_rdf_compare(ipaddr, nslist[i]) == 0) {
211 			if (i) {
212 				tmp = nslist[0];
213 				nslist[0] = nslist[i];
214 				nslist[i] = tmp;
215 			}
216 			break;
217 		}
218 	}
219 	if (i >= ldns_resolver_nameserver_count(tmp_r)) {
220 		/* SOA mname was not part of the resolver so add it first. */
221 		(void) ldns_resolver_push_nameserver(tmp_r, ipaddr);
222 		nslist = ldns_resolver_nameservers(tmp_r);
223 		i = ldns_resolver_nameserver_count(tmp_r) - 1;
224 		tmp = nslist[0];
225 		nslist[0] = nslist[i];
226 		nslist[i] = tmp;
227 	}
228 	ldns_pkt_free(resp);
229 
230 	/* Make sure to ask the first in the list, i.e SOA mname */
231 	ldns_resolver_set_random(tmp_r, false);
232 
233 	/* Step 3 - Redo SOA query, sending to SOA MNAME directly. */
234 	fqdn_rdf = ldns_dname_new_frm_str(fqdn);
235 	query = ldns_pkt_query_new(fqdn_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD);
236 	if (!query) {
237 		ldns_resolver_free(tmp_r);
238 		return LDNS_STATUS_ERR;
239 	}
240 	fqdn_rdf = NULL;
241 
242 	ldns_pkt_set_random_id(query);
243 	if (ldns_resolver_send_pkt(&resp, tmp_r, query) != LDNS_STATUS_OK) {
244 		ldns_pkt_free(query);
245 		ldns_resolver_free(tmp_r);
246 		return LDNS_STATUS_ERR;
247 	}
248 	ldns_resolver_free(tmp_r);
249 	ldns_pkt_free(query);
250 	if (!resp) {
251 		return LDNS_STATUS_ERR;
252 	}
253 
254 	/* XXX Is it safe to only look in authority section here, too? */
255 	while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_authority(resp)))) {
256 		if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
257 				|| ldns_rr_rdf(soa_rr, 0) == NULL)
258 			continue;
259 		/* [RFC1035 3.3.13] */
260 		soa_mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
261 		soa_zone = ldns_rdf_clone(ldns_rr_owner(soa_rr));
262 		break;
263 	}
264 	ldns_pkt_free(resp);
265 	if (!soa_rr) {
266 		return LDNS_STATUS_ERR;
267 	}
268 
269 	/* That seems to have worked, pass results to caller. */
270 	*zone_rdf = soa_zone;
271 	*mname_rdf = soa_mname;
272 	return LDNS_STATUS_OK;
273 }
274 
275 /*
276  * ldns_update_{get,set}_{zo,pr,up,ad}count
277  */
278 
279 uint16_t
280 ldns_update_zocount(const ldns_pkt *p)
281 {
282 	return ldns_pkt_qdcount(p);
283 }
284 
285 uint16_t
286 ldns_update_prcount(const ldns_pkt *p)
287 {
288 	return ldns_pkt_ancount(p);
289 }
290 
291 uint16_t
292 ldns_update_upcount(const ldns_pkt *p)
293 {
294 	return ldns_pkt_nscount(p);
295 }
296 
297 uint16_t
298 ldns_update_ad(const ldns_pkt *p)
299 {
300 	return ldns_pkt_arcount(p);
301 }
302 
303 void
304 ldns_update_set_zo(ldns_pkt *p, uint16_t v)
305 {
306 	ldns_pkt_set_qdcount(p, v);
307 }
308 
309 void
310 ldns_update_set_prcount(ldns_pkt *p, uint16_t v)
311 {
312 	ldns_pkt_set_ancount(p, v);
313 }
314 
315 void
316 ldns_update_set_upcount(ldns_pkt *p, uint16_t v)
317 {
318 	ldns_pkt_set_nscount(p, v);
319 }
320 
321 void
322 ldns_update_set_adcount(ldns_pkt *p, uint16_t v)
323 {
324 	ldns_pkt_set_arcount(p, v);
325 }
326