update.c revision 238104
1193323Sed/* update.c
2193323Sed *
3193323Sed * Functions for RFC 2136 Dynamic Update
4193323Sed *
5193323Sed * Copyright (c) 2005-2008, NLnet Labs. All rights reserved.
6193323Sed *
7193323Sed * See LICENSE for the license.
8193323Sed */
9193323Sed
10193323Sed#include <ldns/config.h>
11193323Sed
12193323Sed#include <ldns/ldns.h>
13193323Sed
14193323Sed#include <strings.h>
15249423Sdim#include <stdlib.h>
16249423Sdim#include <limits.h>
17193323Sed
18251662Sdim/*
19193323Sed * RFC 2136 sections mapped to RFC 1035:
20203954Srdivacky *              zone/ZO -- QD/question
21198892Srdivacky *     prerequisites/PR -- AN/answers
22249423Sdim *           updates/UP -- NS/authority records
23234353Sdim *   additional data/AD -- AR/additional records
24249423Sdim */
25198090Srdivacky
26193323Sedldns_pkt *
27234353Sdimldns_update_pkt_new(ldns_rdf *zone_rdf, ldns_rr_class c,
28234353Sdim    ldns_rr_list *pr_rrlist, ldns_rr_list *up_rrlist, ldns_rr_list *ad_rrlist)
29234353Sdim{
30249423Sdim	ldns_pkt *p;
31193323Sed
32193323Sed	if (!zone_rdf || !up_rrlist) {
33193323Sed		return NULL;
34193323Sed	}
35193323Sed
36263508Sdim	if (c == 0) {
37198090Srdivacky		c = LDNS_RR_CLASS_IN;
38193323Sed	}
39193323Sed
40193323Sed	/* Create packet, fill in Zone Section. */
41195098Sed	p = ldns_pkt_query_new(zone_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD);
42195098Sed	if (!p) {
43195098Sed		return NULL;
44193323Sed	}
45195098Sed	zone_rdf = NULL; /* No longer safe to use. */
46251662Sdim
47263508Sdim	ldns_pkt_set_opcode(p, LDNS_PACKET_UPDATE);
48234353Sdim
49193323Sed	ldns_rr_list_deep_free(p->_authority);
50193323Sed
51218893Sdim	ldns_pkt_set_authority(p, ldns_rr_list_clone(up_rrlist));
52218893Sdim
53193323Sed	ldns_update_set_upcount(p, ldns_rr_list_rr_count(up_rrlist));
54198090Srdivacky
55198892Srdivacky	if (pr_rrlist) {
56198892Srdivacky		ldns_rr_list_deep_free(p->_answer); /*XXX access function */
57198892Srdivacky		ldns_pkt_set_answer(p, ldns_rr_list_clone(pr_rrlist));
58198892Srdivacky		ldns_update_set_prcount(p, ldns_rr_list_rr_count(pr_rrlist));
59198892Srdivacky	}
60198892Srdivacky
61198892Srdivacky	if (ad_rrlist) {
62198090Srdivacky		ldns_rr_list_deep_free(p->_additional);
63198892Srdivacky		ldns_pkt_set_additional(p, ldns_rr_list_clone(ad_rrlist));
64198892Srdivacky		ldns_update_set_adcount(p, ldns_rr_list_rr_count(ad_rrlist));
65198090Srdivacky	}
66193323Sed	return p;
67198090Srdivacky}
68198090Srdivacky
69193323Sedldns_status
70193323Sedldns_update_pkt_tsig_add(ldns_pkt *p, ldns_resolver *r)
71198892Srdivacky{
72193323Sed#ifdef HAVE_SSL
73193323Sed	uint16_t fudge = 300; /* Recommended fudge. [RFC2845 6.4]  */
74193323Sed	if (ldns_resolver_tsig_keyname(r) && ldns_resolver_tsig_keydata(r))
75193323Sed		return ldns_pkt_tsig_sign(p, ldns_resolver_tsig_keyname(r),
76193323Sed		    ldns_resolver_tsig_keydata(r), fudge,
77198090Srdivacky		    ldns_resolver_tsig_algorithm(r), NULL);
78193323Sed#else
79193323Sed	/* do nothing */
80198892Srdivacky	(void)p;
81198090Srdivacky	(void)r;
82218893Sdim#endif /* HAVE_SSL */
83193323Sed	/* No TSIG to do. */
84193323Sed	return LDNS_STATUS_OK;
85193323Sed}
86198090Srdivacky
87193323Sed/* Move to higher.c or similar? */
88193323Sed/* XXX doc */
89193323Sedldns_status
90198090Srdivackyldns_update_soa_mname(ldns_rdf *zone, ldns_resolver *r,
91218893Sdim    ldns_rr_class c, ldns_rdf **mname)
92218893Sdim{
93243830Sdim	ldns_rr		*soa_rr;
94198090Srdivacky	ldns_pkt	*query, *resp;
95193323Sed
96193323Sed	/* Nondestructive, so clone 'zone' here */
97218893Sdim	query = ldns_pkt_query_new(ldns_rdf_clone(zone), LDNS_RR_TYPE_SOA,
98218893Sdim	    c, LDNS_RD);
99218893Sdim	if (!query) {
100218893Sdim		return LDNS_STATUS_ERR;
101218893Sdim	}
102218893Sdim
103218893Sdim	ldns_pkt_set_random_id(query);
104218893Sdim	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
105218893Sdim		ldns_pkt_free(query);
106218893Sdim		return LDNS_STATUS_ERR;
107193323Sed	}
108218893Sdim	ldns_pkt_free(query);
109243830Sdim	if (!resp) {
110218893Sdim		return LDNS_STATUS_ERR;
111218893Sdim	}
112198892Srdivacky
113218893Sdim	/* Expect a SOA answer. */
114218893Sdim	*mname = NULL;
115193323Sed	while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_answer(resp)))) {
116218893Sdim		if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
117218893Sdim				|| ldns_rr_rdf(soa_rr, 0) == NULL)
118218893Sdim			continue;
119193323Sed		/* [RFC1035 3.3.13] */
120193323Sed		*mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
121198090Srdivacky		break;
122198090Srdivacky	}
123193323Sed	ldns_pkt_free(resp);
124218893Sdim
125218893Sdim	return *mname ? LDNS_STATUS_OK : LDNS_STATUS_ERR;
126203954Srdivacky}
127221345Sdim
128243830Sdim/* Try to get zone and MNAME from SOA queries. */
129221345Sdimldns_status
130193323Sedldns_update_soa_zone_mname(const char *fqdn, ldns_resolver *r,
131218893Sdim    ldns_rr_class c, ldns_rdf **zone_rdf, ldns_rdf **mname_rdf)
132193323Sed{
133193323Sed	ldns_rr		*soa_rr, *rr;
134218893Sdim	ldns_rdf	*soa_zone = NULL, *soa_mname = NULL;
135218893Sdim	ldns_rdf	*ipaddr, *fqdn_rdf, *tmp;
136203954Srdivacky	ldns_rdf	**nslist;
137203954Srdivacky	ldns_pkt	*query, *resp;
138203954Srdivacky	size_t		i;
139203954Srdivacky
140203954Srdivacky	/*
141223017Sdim	 * XXX Ok, this cannot be the best way to find this...?
142218893Sdim	 * XXX (I run into weird cache-related stuff here)
143218893Sdim	 */
144218893Sdim
145263508Sdim	/* Step 1 - first find a nameserver that should know *something* */
146218893Sdim	fqdn_rdf = ldns_dname_new_frm_str(fqdn);
147223017Sdim	query = ldns_pkt_query_new(fqdn_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD);
148226633Sdim	if (!query) {
149193323Sed		return LDNS_STATUS_ERR;
150193323Sed	}
151218893Sdim	fqdn_rdf = NULL;
152218893Sdim
153218893Sdim	ldns_pkt_set_random_id(query);
154221345Sdim	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
155193323Sed		ldns_pkt_free(query);
156263508Sdim		return LDNS_STATUS_ERR;
157193323Sed	}
158193323Sed	ldns_pkt_free(query);
159218893Sdim	if (!resp) {
160193323Sed		return LDNS_STATUS_ERR;
161193323Sed	}
162193323Sed
163193323Sed	/* XXX Is it safe to only look in authority section here? */
164193323Sed	while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_authority(resp)))) {
165193323Sed		if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
166193323Sed				|| ldns_rr_rdf(soa_rr, 0) == NULL)
167193323Sed			continue;
168193323Sed		/* [RFC1035 3.3.13] */
169203954Srdivacky		soa_mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
170218893Sdim		break;
171218893Sdim	}
172218893Sdim	ldns_pkt_free(resp);
173218893Sdim	if (!soa_rr) {
174221345Sdim		return LDNS_STATUS_ERR;
175218893Sdim	}
176218893Sdim
177203954Srdivacky	/* Step 2 - find SOA MNAME IP address, add to resolver */
178193323Sed	query = ldns_pkt_query_new(soa_mname, LDNS_RR_TYPE_A, c, LDNS_RD);
179193323Sed	if (!query) {
180193323Sed		return LDNS_STATUS_ERR;
181226633Sdim	}
182198090Srdivacky	soa_mname = NULL;
183198090Srdivacky
184193323Sed	ldns_pkt_set_random_id(query);
185193323Sed	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
186203954Srdivacky		ldns_pkt_free(query);
187198090Srdivacky		return LDNS_STATUS_ERR;
188198090Srdivacky	}
189198090Srdivacky	ldns_pkt_free(query);
190203954Srdivacky	if (!resp) {
191193323Sed		return LDNS_STATUS_ERR;
192193323Sed	}
193193323Sed
194226633Sdim	if (ldns_pkt_ancount(resp) == 0) {
195199481Srdivacky		ldns_pkt_free(resp);
196226633Sdim		return LDNS_STATUS_ERR;
197210299Sed	}
198226633Sdim
199193323Sed	/* XXX There may be more than one answer RR here. */
200203954Srdivacky	rr = ldns_rr_list_pop_rr(ldns_pkt_answer(resp));
201203954Srdivacky	ipaddr = ldns_rr_rdf(rr, 0);
202203954Srdivacky
203203954Srdivacky	/* Put the SOA mname IP first in the nameserver list. */
204203954Srdivacky	nslist = ldns_resolver_nameservers(r);
205193323Sed	for (i = 0; i < ldns_resolver_nameserver_count(r); i++) {
206221345Sdim		if (ldns_rdf_compare(ipaddr, nslist[i]) == 0) {
207218893Sdim			if (i) {
208193323Sed				tmp = nslist[0];
209243830Sdim				nslist[0] = nslist[i];
210193323Sed				nslist[i] = tmp;
211203954Srdivacky			}
212203954Srdivacky			break;
213203954Srdivacky		}
214193323Sed	}
215193323Sed	if (i >= ldns_resolver_nameserver_count(r)) {
216193323Sed		/* SOA mname was not part of the resolver so add it first. */
217193323Sed		(void) ldns_resolver_push_nameserver(r, ipaddr);
218263508Sdim		nslist = ldns_resolver_nameservers(r);
219221345Sdim		i = ldns_resolver_nameserver_count(r) - 1;
220193323Sed		tmp = nslist[0];
221193323Sed		nslist[0] = nslist[i];
222193323Sed		nslist[i] = tmp;
223193323Sed	}
224193323Sed	ldns_pkt_free(resp);
225234353Sdim
226234353Sdim	/* Make sure to ask the first in the list, i.e SOA mname */
227234353Sdim	ldns_resolver_set_random(r, false);
228234353Sdim
229234353Sdim	/* Step 3 - Redo SOA query, sending to SOA MNAME directly. */
230234353Sdim	fqdn_rdf = ldns_dname_new_frm_str(fqdn);
231234353Sdim	query = ldns_pkt_query_new(fqdn_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD);
232234353Sdim	if (!query) {
233263508Sdim		return LDNS_STATUS_ERR;
234263508Sdim	}
235263508Sdim	fqdn_rdf = NULL;
236263508Sdim
237263508Sdim	ldns_pkt_set_random_id(query);
238234353Sdim	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
239234353Sdim		ldns_pkt_free(query);
240234353Sdim		return LDNS_STATUS_ERR;
241234353Sdim	}
242234353Sdim	ldns_pkt_free(query);
243234353Sdim	if (!resp) {
244234353Sdim		return LDNS_STATUS_ERR;
245243830Sdim	}
246234353Sdim
247234353Sdim	/* XXX Is it safe to only look in authority section here, too? */
248234353Sdim	while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_authority(resp)))) {
249234353Sdim		if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
250263508Sdim				|| ldns_rr_rdf(soa_rr, 0) == NULL)
251263508Sdim			continue;
252263508Sdim		/* [RFC1035 3.3.13] */
253263508Sdim		soa_mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
254263508Sdim		soa_zone = ldns_rdf_clone(ldns_rr_owner(soa_rr));
255263508Sdim		break;
256263508Sdim	}
257263508Sdim	ldns_pkt_free(resp);
258263508Sdim	if (!soa_rr) {
259263508Sdim		return LDNS_STATUS_ERR;
260263508Sdim	}
261263508Sdim
262263508Sdim	/* That seems to have worked, pass results to caller. */
263263508Sdim	*zone_rdf = soa_zone;
264263508Sdim	*mname_rdf = soa_mname;
265263508Sdim	return LDNS_STATUS_OK;
266263508Sdim}
267263508Sdim
268263508Sdim/*
269263508Sdim * ldns_update_{get,set}_{zo,pr,up,ad}count
270263508Sdim */
271263508Sdim
272263508Sdimuint16_t
273263508Sdimldns_update_zocount(const ldns_pkt *p)
274263508Sdim{
275243830Sdim	return ldns_pkt_qdcount(p);
276243830Sdim}
277193323Sed
278218893Sdimuint16_t
279218893Sdimldns_update_prcount(const ldns_pkt *p)
280218893Sdim{
281263508Sdim	return ldns_pkt_ancount(p);
282218893Sdim}
283193323Sed
284218893Sdimuint16_t
285218893Sdimldns_update_upcount(const ldns_pkt *p)
286218893Sdim{
287193323Sed	return ldns_pkt_nscount(p);
288221345Sdim}
289221345Sdim
290193323Seduint16_t
291193323Sedldns_update_ad(const ldns_pkt *p)
292193323Sed{
293193323Sed	return ldns_pkt_arcount(p);
294193323Sed}
295193323Sed
296193323Sedvoid
297193323Sedldns_update_set_zo(ldns_pkt *p, uint16_t v)
298193323Sed{
299193323Sed	ldns_pkt_set_qdcount(p, v);
300193323Sed}
301198090Srdivacky
302198090Srdivackyvoid
303193323Sedldns_update_set_prcount(ldns_pkt *p, uint16_t v)
304221345Sdim{
305218893Sdim	ldns_pkt_set_ancount(p, v);
306218893Sdim}
307193323Sed
308221345Sdimvoid
309193323Sedldns_update_set_upcount(ldns_pkt *p, uint16_t v)
310193323Sed{
311193323Sed	ldns_pkt_set_nscount(p, v);
312221345Sdim}
313193323Sed
314193323Sedvoid
315193323Sedldns_update_set_adcount(ldns_pkt *p, uint16_t v)
316193323Sed{
317193323Sed	ldns_pkt_set_arcount(p, v);
318221345Sdim}
319193323Sed