xref: /dflybsd-src/contrib/ldns/update.c (revision 5340022ad92b5f5299ced3631de5d1bf90a7f749)
1825eb42bSJan Lentfer /* update.c
2825eb42bSJan Lentfer  *
3825eb42bSJan Lentfer  * Functions for RFC 2136 Dynamic Update
4825eb42bSJan Lentfer  *
5825eb42bSJan Lentfer  * Copyright (c) 2005-2008, NLnet Labs. All rights reserved.
6825eb42bSJan Lentfer  *
7825eb42bSJan Lentfer  * See LICENSE for the license.
8825eb42bSJan Lentfer  */
9825eb42bSJan Lentfer 
10825eb42bSJan Lentfer #include <ldns/config.h>
11825eb42bSJan Lentfer 
12825eb42bSJan Lentfer #include <ldns/ldns.h>
13825eb42bSJan Lentfer 
14825eb42bSJan Lentfer #include <strings.h>
15825eb42bSJan Lentfer #include <stdlib.h>
16825eb42bSJan Lentfer #include <limits.h>
17825eb42bSJan Lentfer 
18825eb42bSJan Lentfer /*
19825eb42bSJan Lentfer  * RFC 2136 sections mapped to RFC 1035:
20825eb42bSJan Lentfer  *              zone/ZO -- QD/question
21825eb42bSJan Lentfer  *     prerequisites/PR -- AN/answers
22825eb42bSJan Lentfer  *           updates/UP -- NS/authority records
23825eb42bSJan Lentfer  *   additional data/AD -- AR/additional records
24825eb42bSJan Lentfer  */
25825eb42bSJan Lentfer 
26825eb42bSJan Lentfer ldns_pkt *
ldns_update_pkt_new(ldns_rdf * zone_rdf,ldns_rr_class c,const ldns_rr_list * pr_rrlist,const ldns_rr_list * up_rrlist,const ldns_rr_list * ad_rrlist)27825eb42bSJan Lentfer ldns_update_pkt_new(ldns_rdf *zone_rdf, ldns_rr_class c,
28*5340022aSzrj     const ldns_rr_list *pr_rrlist, const ldns_rr_list *up_rrlist, const ldns_rr_list *ad_rrlist)
29825eb42bSJan Lentfer {
30825eb42bSJan Lentfer 	ldns_pkt *p;
31825eb42bSJan Lentfer 
32825eb42bSJan Lentfer 	if (!zone_rdf || !up_rrlist) {
33825eb42bSJan Lentfer 		return NULL;
34825eb42bSJan Lentfer 	}
35825eb42bSJan Lentfer 
36825eb42bSJan Lentfer 	if (c == 0) {
37825eb42bSJan Lentfer 		c = LDNS_RR_CLASS_IN;
38825eb42bSJan Lentfer 	}
39825eb42bSJan Lentfer 
40825eb42bSJan Lentfer 	/* Create packet, fill in Zone Section. */
41825eb42bSJan Lentfer 	p = ldns_pkt_query_new(zone_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD);
42825eb42bSJan Lentfer 	if (!p) {
43825eb42bSJan Lentfer 		return NULL;
44825eb42bSJan Lentfer 	}
45825eb42bSJan Lentfer 	zone_rdf = NULL; /* No longer safe to use. */
46825eb42bSJan Lentfer 
47825eb42bSJan Lentfer 	ldns_pkt_set_opcode(p, LDNS_PACKET_UPDATE);
48825eb42bSJan Lentfer 
49825eb42bSJan Lentfer 	ldns_rr_list_deep_free(p->_authority);
50825eb42bSJan Lentfer 
51825eb42bSJan Lentfer 	ldns_pkt_set_authority(p, ldns_rr_list_clone(up_rrlist));
52825eb42bSJan Lentfer 
53825eb42bSJan Lentfer 	ldns_update_set_upcount(p, ldns_rr_list_rr_count(up_rrlist));
54825eb42bSJan Lentfer 
55825eb42bSJan Lentfer 	if (pr_rrlist) {
56825eb42bSJan Lentfer 		ldns_rr_list_deep_free(p->_answer); /*XXX access function */
57825eb42bSJan Lentfer 		ldns_pkt_set_answer(p, ldns_rr_list_clone(pr_rrlist));
58825eb42bSJan Lentfer 		ldns_update_set_prcount(p, ldns_rr_list_rr_count(pr_rrlist));
59825eb42bSJan Lentfer 	}
60825eb42bSJan Lentfer 
61825eb42bSJan Lentfer 	if (ad_rrlist) {
62825eb42bSJan Lentfer 		ldns_rr_list_deep_free(p->_additional);
63825eb42bSJan Lentfer 		ldns_pkt_set_additional(p, ldns_rr_list_clone(ad_rrlist));
64825eb42bSJan Lentfer 		ldns_update_set_adcount(p, ldns_rr_list_rr_count(ad_rrlist));
65825eb42bSJan Lentfer 	}
66825eb42bSJan Lentfer 	return p;
67825eb42bSJan Lentfer }
68825eb42bSJan Lentfer 
69825eb42bSJan Lentfer ldns_status
ldns_update_pkt_tsig_add(ldns_pkt * p,const ldns_resolver * r)70*5340022aSzrj ldns_update_pkt_tsig_add(ldns_pkt *p, const ldns_resolver *r)
71825eb42bSJan Lentfer {
72825eb42bSJan Lentfer #ifdef HAVE_SSL
73825eb42bSJan Lentfer 	uint16_t fudge = 300; /* Recommended fudge. [RFC2845 6.4]  */
74825eb42bSJan Lentfer 	if (ldns_resolver_tsig_keyname(r) && ldns_resolver_tsig_keydata(r))
75825eb42bSJan Lentfer 		return ldns_pkt_tsig_sign(p, ldns_resolver_tsig_keyname(r),
76825eb42bSJan Lentfer 		    ldns_resolver_tsig_keydata(r), fudge,
77825eb42bSJan Lentfer 		    ldns_resolver_tsig_algorithm(r), NULL);
78825eb42bSJan Lentfer #else
79825eb42bSJan Lentfer 	/* do nothing */
80825eb42bSJan Lentfer 	(void)p;
81825eb42bSJan Lentfer 	(void)r;
82825eb42bSJan Lentfer #endif /* HAVE_SSL */
83825eb42bSJan Lentfer 	/* No TSIG to do. */
84825eb42bSJan Lentfer 	return LDNS_STATUS_OK;
85825eb42bSJan Lentfer }
86825eb42bSJan Lentfer 
87825eb42bSJan Lentfer /* Move to higher.c or similar? */
88825eb42bSJan Lentfer /* XXX doc */
89825eb42bSJan Lentfer ldns_status
ldns_update_soa_mname(ldns_rdf * zone,ldns_resolver * r,ldns_rr_class c,ldns_rdf ** mname)90825eb42bSJan Lentfer ldns_update_soa_mname(ldns_rdf *zone, ldns_resolver *r,
91825eb42bSJan Lentfer     ldns_rr_class c, ldns_rdf **mname)
92825eb42bSJan Lentfer {
93825eb42bSJan Lentfer 	ldns_rr		*soa_rr;
94825eb42bSJan Lentfer 	ldns_pkt	*query, *resp;
95825eb42bSJan Lentfer 
96825eb42bSJan Lentfer 	/* Nondestructive, so clone 'zone' here */
97825eb42bSJan Lentfer 	query = ldns_pkt_query_new(ldns_rdf_clone(zone), LDNS_RR_TYPE_SOA,
98825eb42bSJan Lentfer 	    c, LDNS_RD);
99825eb42bSJan Lentfer 	if (!query) {
100825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
101825eb42bSJan Lentfer 	}
102825eb42bSJan Lentfer 
103825eb42bSJan Lentfer 	ldns_pkt_set_random_id(query);
104825eb42bSJan Lentfer 	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
105825eb42bSJan Lentfer 		ldns_pkt_free(query);
106825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
107825eb42bSJan Lentfer 	}
108825eb42bSJan Lentfer 	ldns_pkt_free(query);
109825eb42bSJan Lentfer 	if (!resp) {
110825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
111825eb42bSJan Lentfer 	}
112825eb42bSJan Lentfer 
113825eb42bSJan Lentfer 	/* Expect a SOA answer. */
114825eb42bSJan Lentfer 	*mname = NULL;
115825eb42bSJan Lentfer 	while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_answer(resp)))) {
116b5dedccaSJan Lentfer 		if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
117b5dedccaSJan Lentfer 				|| ldns_rr_rdf(soa_rr, 0) == NULL)
118825eb42bSJan Lentfer 			continue;
119825eb42bSJan Lentfer 		/* [RFC1035 3.3.13] */
120825eb42bSJan Lentfer 		*mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
121825eb42bSJan Lentfer 		break;
122825eb42bSJan Lentfer 	}
123825eb42bSJan Lentfer 	ldns_pkt_free(resp);
124825eb42bSJan Lentfer 
125825eb42bSJan Lentfer 	return *mname ? LDNS_STATUS_OK : LDNS_STATUS_ERR;
126825eb42bSJan Lentfer }
127825eb42bSJan Lentfer 
128825eb42bSJan Lentfer /* Try to get zone and MNAME from SOA queries. */
129825eb42bSJan Lentfer ldns_status
ldns_update_soa_zone_mname(const char * fqdn,ldns_resolver * r,ldns_rr_class c,ldns_rdf ** zone_rdf,ldns_rdf ** mname_rdf)130825eb42bSJan Lentfer ldns_update_soa_zone_mname(const char *fqdn, ldns_resolver *r,
131825eb42bSJan Lentfer     ldns_rr_class c, ldns_rdf **zone_rdf, ldns_rdf **mname_rdf)
132825eb42bSJan Lentfer {
133825eb42bSJan Lentfer 	ldns_rr		*soa_rr, *rr;
134825eb42bSJan Lentfer 	ldns_rdf	*soa_zone = NULL, *soa_mname = NULL;
135825eb42bSJan Lentfer 	ldns_rdf	*ipaddr, *fqdn_rdf, *tmp;
136825eb42bSJan Lentfer 	ldns_rdf	**nslist;
137825eb42bSJan Lentfer 	ldns_pkt	*query, *resp;
138*5340022aSzrj 	ldns_resolver   *tmp_r;
139825eb42bSJan Lentfer 	size_t		i;
140825eb42bSJan Lentfer 
141825eb42bSJan Lentfer 	/*
142825eb42bSJan Lentfer 	 * XXX Ok, this cannot be the best way to find this...?
143825eb42bSJan Lentfer 	 * XXX (I run into weird cache-related stuff here)
144825eb42bSJan Lentfer 	 */
145825eb42bSJan Lentfer 
146825eb42bSJan Lentfer 	/* Step 1 - first find a nameserver that should know *something* */
147825eb42bSJan Lentfer 	fqdn_rdf = ldns_dname_new_frm_str(fqdn);
148825eb42bSJan Lentfer 	query = ldns_pkt_query_new(fqdn_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD);
149825eb42bSJan Lentfer 	if (!query) {
150825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
151825eb42bSJan Lentfer 	}
152825eb42bSJan Lentfer 	fqdn_rdf = NULL;
153825eb42bSJan Lentfer 
154825eb42bSJan Lentfer 	ldns_pkt_set_random_id(query);
155825eb42bSJan Lentfer 	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
156825eb42bSJan Lentfer 		ldns_pkt_free(query);
157825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
158825eb42bSJan Lentfer 	}
159825eb42bSJan Lentfer 	ldns_pkt_free(query);
160825eb42bSJan Lentfer 	if (!resp) {
161825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
162825eb42bSJan Lentfer 	}
163825eb42bSJan Lentfer 
164825eb42bSJan Lentfer 	/* XXX Is it safe to only look in authority section here? */
165825eb42bSJan Lentfer 	while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_authority(resp)))) {
166b5dedccaSJan Lentfer 		if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
167b5dedccaSJan Lentfer 				|| ldns_rr_rdf(soa_rr, 0) == NULL)
168825eb42bSJan Lentfer 			continue;
169825eb42bSJan Lentfer 		/* [RFC1035 3.3.13] */
170825eb42bSJan Lentfer 		soa_mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
171825eb42bSJan Lentfer 		break;
172825eb42bSJan Lentfer 	}
173825eb42bSJan Lentfer 	ldns_pkt_free(resp);
174825eb42bSJan Lentfer 	if (!soa_rr) {
175825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
176825eb42bSJan Lentfer 	}
177825eb42bSJan Lentfer 
178825eb42bSJan Lentfer 	/* Step 2 - find SOA MNAME IP address, add to resolver */
179825eb42bSJan Lentfer 	query = ldns_pkt_query_new(soa_mname, LDNS_RR_TYPE_A, c, LDNS_RD);
180825eb42bSJan Lentfer 	if (!query) {
181825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
182825eb42bSJan Lentfer 	}
183825eb42bSJan Lentfer 	soa_mname = NULL;
184825eb42bSJan Lentfer 
185825eb42bSJan Lentfer 	ldns_pkt_set_random_id(query);
186825eb42bSJan Lentfer 	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
187825eb42bSJan Lentfer 		ldns_pkt_free(query);
188825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
189825eb42bSJan Lentfer 	}
190825eb42bSJan Lentfer 	ldns_pkt_free(query);
191825eb42bSJan Lentfer 	if (!resp) {
192825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
193825eb42bSJan Lentfer 	}
194825eb42bSJan Lentfer 
195825eb42bSJan Lentfer 	if (ldns_pkt_ancount(resp) == 0) {
196825eb42bSJan Lentfer 		ldns_pkt_free(resp);
197825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
198825eb42bSJan Lentfer 	}
199825eb42bSJan Lentfer 
200825eb42bSJan Lentfer 	/* XXX There may be more than one answer RR here. */
201825eb42bSJan Lentfer 	rr = ldns_rr_list_pop_rr(ldns_pkt_answer(resp));
202825eb42bSJan Lentfer 	ipaddr = ldns_rr_rdf(rr, 0);
203825eb42bSJan Lentfer 
204825eb42bSJan Lentfer 	/* Put the SOA mname IP first in the nameserver list. */
205*5340022aSzrj 	if (!(tmp_r = ldns_resolver_clone(r))) {
206*5340022aSzrj 		return LDNS_STATUS_MEM_ERR;
207*5340022aSzrj 	}
208*5340022aSzrj 	nslist = ldns_resolver_nameservers(tmp_r);
209*5340022aSzrj 	for (i = 0; i < ldns_resolver_nameserver_count(tmp_r); i++) {
210825eb42bSJan Lentfer 		if (ldns_rdf_compare(ipaddr, nslist[i]) == 0) {
211825eb42bSJan Lentfer 			if (i) {
212825eb42bSJan Lentfer 				tmp = nslist[0];
213825eb42bSJan Lentfer 				nslist[0] = nslist[i];
214825eb42bSJan Lentfer 				nslist[i] = tmp;
215825eb42bSJan Lentfer 			}
216825eb42bSJan Lentfer 			break;
217825eb42bSJan Lentfer 		}
218825eb42bSJan Lentfer 	}
219*5340022aSzrj 	if (i >= ldns_resolver_nameserver_count(tmp_r)) {
220825eb42bSJan Lentfer 		/* SOA mname was not part of the resolver so add it first. */
221*5340022aSzrj 		(void) ldns_resolver_push_nameserver(tmp_r, ipaddr);
222*5340022aSzrj 		nslist = ldns_resolver_nameservers(tmp_r);
223*5340022aSzrj 		i = ldns_resolver_nameserver_count(tmp_r) - 1;
224825eb42bSJan Lentfer 		tmp = nslist[0];
225825eb42bSJan Lentfer 		nslist[0] = nslist[i];
226825eb42bSJan Lentfer 		nslist[i] = tmp;
227825eb42bSJan Lentfer 	}
228825eb42bSJan Lentfer 	ldns_pkt_free(resp);
229825eb42bSJan Lentfer 
230825eb42bSJan Lentfer 	/* Make sure to ask the first in the list, i.e SOA mname */
231*5340022aSzrj 	ldns_resolver_set_random(tmp_r, false);
232825eb42bSJan Lentfer 
233825eb42bSJan Lentfer 	/* Step 3 - Redo SOA query, sending to SOA MNAME directly. */
234825eb42bSJan Lentfer 	fqdn_rdf = ldns_dname_new_frm_str(fqdn);
235825eb42bSJan Lentfer 	query = ldns_pkt_query_new(fqdn_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD);
236825eb42bSJan Lentfer 	if (!query) {
237*5340022aSzrj 		ldns_resolver_free(tmp_r);
238825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
239825eb42bSJan Lentfer 	}
240825eb42bSJan Lentfer 	fqdn_rdf = NULL;
241825eb42bSJan Lentfer 
242825eb42bSJan Lentfer 	ldns_pkt_set_random_id(query);
243*5340022aSzrj 	if (ldns_resolver_send_pkt(&resp, tmp_r, query) != LDNS_STATUS_OK) {
244825eb42bSJan Lentfer 		ldns_pkt_free(query);
245*5340022aSzrj 		ldns_resolver_free(tmp_r);
246825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
247825eb42bSJan Lentfer 	}
248*5340022aSzrj 	ldns_resolver_free(tmp_r);
249825eb42bSJan Lentfer 	ldns_pkt_free(query);
250825eb42bSJan Lentfer 	if (!resp) {
251825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
252825eb42bSJan Lentfer 	}
253825eb42bSJan Lentfer 
254825eb42bSJan Lentfer 	/* XXX Is it safe to only look in authority section here, too? */
255825eb42bSJan Lentfer 	while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_authority(resp)))) {
256b5dedccaSJan Lentfer 		if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
257b5dedccaSJan Lentfer 				|| ldns_rr_rdf(soa_rr, 0) == NULL)
258825eb42bSJan Lentfer 			continue;
259825eb42bSJan Lentfer 		/* [RFC1035 3.3.13] */
260825eb42bSJan Lentfer 		soa_mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
261825eb42bSJan Lentfer 		soa_zone = ldns_rdf_clone(ldns_rr_owner(soa_rr));
262825eb42bSJan Lentfer 		break;
263825eb42bSJan Lentfer 	}
264825eb42bSJan Lentfer 	ldns_pkt_free(resp);
265825eb42bSJan Lentfer 	if (!soa_rr) {
266825eb42bSJan Lentfer 		return LDNS_STATUS_ERR;
267825eb42bSJan Lentfer 	}
268825eb42bSJan Lentfer 
269825eb42bSJan Lentfer 	/* That seems to have worked, pass results to caller. */
270825eb42bSJan Lentfer 	*zone_rdf = soa_zone;
271825eb42bSJan Lentfer 	*mname_rdf = soa_mname;
272825eb42bSJan Lentfer 	return LDNS_STATUS_OK;
273825eb42bSJan Lentfer }
274825eb42bSJan Lentfer 
275825eb42bSJan Lentfer /*
276825eb42bSJan Lentfer  * ldns_update_{get,set}_{zo,pr,up,ad}count
277825eb42bSJan Lentfer  */
278825eb42bSJan Lentfer 
279825eb42bSJan Lentfer uint16_t
ldns_update_zocount(const ldns_pkt * p)280825eb42bSJan Lentfer ldns_update_zocount(const ldns_pkt *p)
281825eb42bSJan Lentfer {
282825eb42bSJan Lentfer 	return ldns_pkt_qdcount(p);
283825eb42bSJan Lentfer }
284825eb42bSJan Lentfer 
285825eb42bSJan Lentfer uint16_t
ldns_update_prcount(const ldns_pkt * p)286825eb42bSJan Lentfer ldns_update_prcount(const ldns_pkt *p)
287825eb42bSJan Lentfer {
288825eb42bSJan Lentfer 	return ldns_pkt_ancount(p);
289825eb42bSJan Lentfer }
290825eb42bSJan Lentfer 
291825eb42bSJan Lentfer uint16_t
ldns_update_upcount(const ldns_pkt * p)292825eb42bSJan Lentfer ldns_update_upcount(const ldns_pkt *p)
293825eb42bSJan Lentfer {
294825eb42bSJan Lentfer 	return ldns_pkt_nscount(p);
295825eb42bSJan Lentfer }
296825eb42bSJan Lentfer 
297825eb42bSJan Lentfer uint16_t
ldns_update_ad(const ldns_pkt * p)298825eb42bSJan Lentfer ldns_update_ad(const ldns_pkt *p)
299825eb42bSJan Lentfer {
300825eb42bSJan Lentfer 	return ldns_pkt_arcount(p);
301825eb42bSJan Lentfer }
302825eb42bSJan Lentfer 
303825eb42bSJan Lentfer void
ldns_update_set_zo(ldns_pkt * p,uint16_t v)304825eb42bSJan Lentfer ldns_update_set_zo(ldns_pkt *p, uint16_t v)
305825eb42bSJan Lentfer {
306825eb42bSJan Lentfer 	ldns_pkt_set_qdcount(p, v);
307825eb42bSJan Lentfer }
308825eb42bSJan Lentfer 
309825eb42bSJan Lentfer void
ldns_update_set_prcount(ldns_pkt * p,uint16_t v)310825eb42bSJan Lentfer ldns_update_set_prcount(ldns_pkt *p, uint16_t v)
311825eb42bSJan Lentfer {
312825eb42bSJan Lentfer 	ldns_pkt_set_ancount(p, v);
313825eb42bSJan Lentfer }
314825eb42bSJan Lentfer 
315825eb42bSJan Lentfer void
ldns_update_set_upcount(ldns_pkt * p,uint16_t v)316825eb42bSJan Lentfer ldns_update_set_upcount(ldns_pkt *p, uint16_t v)
317825eb42bSJan Lentfer {
318825eb42bSJan Lentfer 	ldns_pkt_set_nscount(p, v);
319825eb42bSJan Lentfer }
320825eb42bSJan Lentfer 
321825eb42bSJan Lentfer void
ldns_update_set_adcount(ldns_pkt * p,uint16_t v)322825eb42bSJan Lentfer ldns_update_set_adcount(ldns_pkt *p, uint16_t v)
323825eb42bSJan Lentfer {
324825eb42bSJan Lentfer 	ldns_pkt_set_arcount(p, v);
325825eb42bSJan Lentfer }
326