1238104Sdes/* update.c 2238104Sdes * 3238104Sdes * Functions for RFC 2136 Dynamic Update 4238104Sdes * 5238104Sdes * Copyright (c) 2005-2008, NLnet Labs. All rights reserved. 6238104Sdes * 7238104Sdes * See LICENSE for the license. 8238104Sdes */ 9238104Sdes 10238104Sdes#include <ldns/config.h> 11238104Sdes 12238104Sdes#include <ldns/ldns.h> 13238104Sdes 14238104Sdes#include <strings.h> 15238104Sdes#include <stdlib.h> 16238104Sdes#include <limits.h> 17238104Sdes 18238104Sdes/* 19238104Sdes * RFC 2136 sections mapped to RFC 1035: 20238104Sdes * zone/ZO -- QD/question 21238104Sdes * prerequisites/PR -- AN/answers 22238104Sdes * updates/UP -- NS/authority records 23238104Sdes * additional data/AD -- AR/additional records 24238104Sdes */ 25238104Sdes 26238104Sdesldns_pkt * 27238104Sdesldns_update_pkt_new(ldns_rdf *zone_rdf, ldns_rr_class c, 28238104Sdes ldns_rr_list *pr_rrlist, ldns_rr_list *up_rrlist, ldns_rr_list *ad_rrlist) 29238104Sdes{ 30238104Sdes ldns_pkt *p; 31238104Sdes 32238104Sdes if (!zone_rdf || !up_rrlist) { 33238104Sdes return NULL; 34238104Sdes } 35238104Sdes 36238104Sdes if (c == 0) { 37238104Sdes c = LDNS_RR_CLASS_IN; 38238104Sdes } 39238104Sdes 40238104Sdes /* Create packet, fill in Zone Section. */ 41238104Sdes p = ldns_pkt_query_new(zone_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD); 42238104Sdes if (!p) { 43238104Sdes return NULL; 44238104Sdes } 45238104Sdes zone_rdf = NULL; /* No longer safe to use. */ 46238104Sdes 47238104Sdes ldns_pkt_set_opcode(p, LDNS_PACKET_UPDATE); 48238104Sdes 49238104Sdes ldns_rr_list_deep_free(p->_authority); 50238104Sdes 51238104Sdes ldns_pkt_set_authority(p, ldns_rr_list_clone(up_rrlist)); 52238104Sdes 53238104Sdes ldns_update_set_upcount(p, ldns_rr_list_rr_count(up_rrlist)); 54238104Sdes 55238104Sdes if (pr_rrlist) { 56238104Sdes ldns_rr_list_deep_free(p->_answer); /*XXX access function */ 57238104Sdes ldns_pkt_set_answer(p, ldns_rr_list_clone(pr_rrlist)); 58238104Sdes ldns_update_set_prcount(p, ldns_rr_list_rr_count(pr_rrlist)); 59238104Sdes } 60238104Sdes 61238104Sdes if (ad_rrlist) { 62238104Sdes ldns_rr_list_deep_free(p->_additional); 63238104Sdes ldns_pkt_set_additional(p, ldns_rr_list_clone(ad_rrlist)); 64238104Sdes ldns_update_set_adcount(p, ldns_rr_list_rr_count(ad_rrlist)); 65238104Sdes } 66238104Sdes return p; 67238104Sdes} 68238104Sdes 69238104Sdesldns_status 70238104Sdesldns_update_pkt_tsig_add(ldns_pkt *p, ldns_resolver *r) 71238104Sdes{ 72238104Sdes#ifdef HAVE_SSL 73238104Sdes uint16_t fudge = 300; /* Recommended fudge. [RFC2845 6.4] */ 74238104Sdes if (ldns_resolver_tsig_keyname(r) && ldns_resolver_tsig_keydata(r)) 75238104Sdes return ldns_pkt_tsig_sign(p, ldns_resolver_tsig_keyname(r), 76238104Sdes ldns_resolver_tsig_keydata(r), fudge, 77238104Sdes ldns_resolver_tsig_algorithm(r), NULL); 78238104Sdes#else 79238104Sdes /* do nothing */ 80238104Sdes (void)p; 81238104Sdes (void)r; 82238104Sdes#endif /* HAVE_SSL */ 83238104Sdes /* No TSIG to do. */ 84238104Sdes return LDNS_STATUS_OK; 85238104Sdes} 86238104Sdes 87238104Sdes/* Move to higher.c or similar? */ 88238104Sdes/* XXX doc */ 89238104Sdesldns_status 90238104Sdesldns_update_soa_mname(ldns_rdf *zone, ldns_resolver *r, 91238104Sdes ldns_rr_class c, ldns_rdf **mname) 92238104Sdes{ 93238104Sdes ldns_rr *soa_rr; 94238104Sdes ldns_pkt *query, *resp; 95238104Sdes 96238104Sdes /* Nondestructive, so clone 'zone' here */ 97238104Sdes query = ldns_pkt_query_new(ldns_rdf_clone(zone), LDNS_RR_TYPE_SOA, 98238104Sdes c, LDNS_RD); 99238104Sdes if (!query) { 100238104Sdes return LDNS_STATUS_ERR; 101238104Sdes } 102238104Sdes 103238104Sdes ldns_pkt_set_random_id(query); 104238104Sdes if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) { 105238104Sdes ldns_pkt_free(query); 106238104Sdes return LDNS_STATUS_ERR; 107238104Sdes } 108238104Sdes ldns_pkt_free(query); 109238104Sdes if (!resp) { 110238104Sdes return LDNS_STATUS_ERR; 111238104Sdes } 112238104Sdes 113238104Sdes /* Expect a SOA answer. */ 114238104Sdes *mname = NULL; 115238104Sdes while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_answer(resp)))) { 116238104Sdes if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA 117238104Sdes || ldns_rr_rdf(soa_rr, 0) == NULL) 118238104Sdes continue; 119238104Sdes /* [RFC1035 3.3.13] */ 120238104Sdes *mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0)); 121238104Sdes break; 122238104Sdes } 123238104Sdes ldns_pkt_free(resp); 124238104Sdes 125238104Sdes return *mname ? LDNS_STATUS_OK : LDNS_STATUS_ERR; 126238104Sdes} 127238104Sdes 128238104Sdes/* Try to get zone and MNAME from SOA queries. */ 129238104Sdesldns_status 130238104Sdesldns_update_soa_zone_mname(const char *fqdn, ldns_resolver *r, 131238104Sdes ldns_rr_class c, ldns_rdf **zone_rdf, ldns_rdf **mname_rdf) 132238104Sdes{ 133238104Sdes ldns_rr *soa_rr, *rr; 134238104Sdes ldns_rdf *soa_zone = NULL, *soa_mname = NULL; 135238104Sdes ldns_rdf *ipaddr, *fqdn_rdf, *tmp; 136238104Sdes ldns_rdf **nslist; 137238104Sdes ldns_pkt *query, *resp; 138238104Sdes size_t i; 139238104Sdes 140238104Sdes /* 141238104Sdes * XXX Ok, this cannot be the best way to find this...? 142238104Sdes * XXX (I run into weird cache-related stuff here) 143238104Sdes */ 144238104Sdes 145238104Sdes /* Step 1 - first find a nameserver that should know *something* */ 146238104Sdes fqdn_rdf = ldns_dname_new_frm_str(fqdn); 147238104Sdes query = ldns_pkt_query_new(fqdn_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD); 148238104Sdes if (!query) { 149238104Sdes return LDNS_STATUS_ERR; 150238104Sdes } 151238104Sdes fqdn_rdf = NULL; 152238104Sdes 153238104Sdes ldns_pkt_set_random_id(query); 154238104Sdes if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) { 155238104Sdes ldns_pkt_free(query); 156238104Sdes return LDNS_STATUS_ERR; 157238104Sdes } 158238104Sdes ldns_pkt_free(query); 159238104Sdes if (!resp) { 160238104Sdes return LDNS_STATUS_ERR; 161238104Sdes } 162238104Sdes 163238104Sdes /* XXX Is it safe to only look in authority section here? */ 164238104Sdes while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_authority(resp)))) { 165238104Sdes if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA 166238104Sdes || ldns_rr_rdf(soa_rr, 0) == NULL) 167238104Sdes continue; 168238104Sdes /* [RFC1035 3.3.13] */ 169238104Sdes soa_mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0)); 170238104Sdes break; 171238104Sdes } 172238104Sdes ldns_pkt_free(resp); 173238104Sdes if (!soa_rr) { 174238104Sdes return LDNS_STATUS_ERR; 175238104Sdes } 176238104Sdes 177238104Sdes /* Step 2 - find SOA MNAME IP address, add to resolver */ 178238104Sdes query = ldns_pkt_query_new(soa_mname, LDNS_RR_TYPE_A, c, LDNS_RD); 179238104Sdes if (!query) { 180238104Sdes return LDNS_STATUS_ERR; 181238104Sdes } 182238104Sdes soa_mname = NULL; 183238104Sdes 184238104Sdes ldns_pkt_set_random_id(query); 185238104Sdes if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) { 186238104Sdes ldns_pkt_free(query); 187238104Sdes return LDNS_STATUS_ERR; 188238104Sdes } 189238104Sdes ldns_pkt_free(query); 190238104Sdes if (!resp) { 191238104Sdes return LDNS_STATUS_ERR; 192238104Sdes } 193238104Sdes 194238104Sdes if (ldns_pkt_ancount(resp) == 0) { 195238104Sdes ldns_pkt_free(resp); 196238104Sdes return LDNS_STATUS_ERR; 197238104Sdes } 198238104Sdes 199238104Sdes /* XXX There may be more than one answer RR here. */ 200238104Sdes rr = ldns_rr_list_pop_rr(ldns_pkt_answer(resp)); 201238104Sdes ipaddr = ldns_rr_rdf(rr, 0); 202238104Sdes 203238104Sdes /* Put the SOA mname IP first in the nameserver list. */ 204238104Sdes nslist = ldns_resolver_nameservers(r); 205238104Sdes for (i = 0; i < ldns_resolver_nameserver_count(r); i++) { 206238104Sdes if (ldns_rdf_compare(ipaddr, nslist[i]) == 0) { 207238104Sdes if (i) { 208238104Sdes tmp = nslist[0]; 209238104Sdes nslist[0] = nslist[i]; 210238104Sdes nslist[i] = tmp; 211238104Sdes } 212238104Sdes break; 213238104Sdes } 214238104Sdes } 215238104Sdes if (i >= ldns_resolver_nameserver_count(r)) { 216238104Sdes /* SOA mname was not part of the resolver so add it first. */ 217238104Sdes (void) ldns_resolver_push_nameserver(r, ipaddr); 218238104Sdes nslist = ldns_resolver_nameservers(r); 219238104Sdes i = ldns_resolver_nameserver_count(r) - 1; 220238104Sdes tmp = nslist[0]; 221238104Sdes nslist[0] = nslist[i]; 222238104Sdes nslist[i] = tmp; 223238104Sdes } 224238104Sdes ldns_pkt_free(resp); 225238104Sdes 226238104Sdes /* Make sure to ask the first in the list, i.e SOA mname */ 227238104Sdes ldns_resolver_set_random(r, false); 228238104Sdes 229238104Sdes /* Step 3 - Redo SOA query, sending to SOA MNAME directly. */ 230238104Sdes fqdn_rdf = ldns_dname_new_frm_str(fqdn); 231238104Sdes query = ldns_pkt_query_new(fqdn_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD); 232238104Sdes if (!query) { 233238104Sdes return LDNS_STATUS_ERR; 234238104Sdes } 235238104Sdes fqdn_rdf = NULL; 236238104Sdes 237238104Sdes ldns_pkt_set_random_id(query); 238238104Sdes if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) { 239238104Sdes ldns_pkt_free(query); 240238104Sdes return LDNS_STATUS_ERR; 241238104Sdes } 242238104Sdes ldns_pkt_free(query); 243238104Sdes if (!resp) { 244238104Sdes return LDNS_STATUS_ERR; 245238104Sdes } 246238104Sdes 247238104Sdes /* XXX Is it safe to only look in authority section here, too? */ 248238104Sdes while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_authority(resp)))) { 249238104Sdes if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA 250238104Sdes || ldns_rr_rdf(soa_rr, 0) == NULL) 251238104Sdes continue; 252238104Sdes /* [RFC1035 3.3.13] */ 253238104Sdes soa_mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0)); 254238104Sdes soa_zone = ldns_rdf_clone(ldns_rr_owner(soa_rr)); 255238104Sdes break; 256238104Sdes } 257238104Sdes ldns_pkt_free(resp); 258238104Sdes if (!soa_rr) { 259238104Sdes return LDNS_STATUS_ERR; 260238104Sdes } 261238104Sdes 262238104Sdes /* That seems to have worked, pass results to caller. */ 263238104Sdes *zone_rdf = soa_zone; 264238104Sdes *mname_rdf = soa_mname; 265238104Sdes return LDNS_STATUS_OK; 266238104Sdes} 267238104Sdes 268238104Sdes/* 269238104Sdes * ldns_update_{get,set}_{zo,pr,up,ad}count 270238104Sdes */ 271238104Sdes 272238104Sdesuint16_t 273238104Sdesldns_update_zocount(const ldns_pkt *p) 274238104Sdes{ 275238104Sdes return ldns_pkt_qdcount(p); 276238104Sdes} 277238104Sdes 278238104Sdesuint16_t 279238104Sdesldns_update_prcount(const ldns_pkt *p) 280238104Sdes{ 281238104Sdes return ldns_pkt_ancount(p); 282238104Sdes} 283238104Sdes 284238104Sdesuint16_t 285238104Sdesldns_update_upcount(const ldns_pkt *p) 286238104Sdes{ 287238104Sdes return ldns_pkt_nscount(p); 288238104Sdes} 289238104Sdes 290238104Sdesuint16_t 291238104Sdesldns_update_ad(const ldns_pkt *p) 292238104Sdes{ 293238104Sdes return ldns_pkt_arcount(p); 294238104Sdes} 295238104Sdes 296238104Sdesvoid 297238104Sdesldns_update_set_zo(ldns_pkt *p, uint16_t v) 298238104Sdes{ 299238104Sdes ldns_pkt_set_qdcount(p, v); 300238104Sdes} 301238104Sdes 302238104Sdesvoid 303238104Sdesldns_update_set_prcount(ldns_pkt *p, uint16_t v) 304238104Sdes{ 305238104Sdes ldns_pkt_set_ancount(p, v); 306238104Sdes} 307238104Sdes 308238104Sdesvoid 309238104Sdesldns_update_set_upcount(ldns_pkt *p, uint16_t v) 310238104Sdes{ 311238104Sdes ldns_pkt_set_nscount(p, v); 312238104Sdes} 313238104Sdes 314238104Sdesvoid 315238104Sdesldns_update_set_adcount(ldns_pkt *p, uint16_t v) 316238104Sdes{ 317238104Sdes ldns_pkt_set_arcount(p, v); 318238104Sdes} 319