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