--- /dev/null
+/* $Id: ldns-update.c,v 1.1 2005/09/13 09:37:05 ho Exp $ */
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#include <ldns/dns.h>
+
+int
+main(int argc, char **argv)
+{
+ char *fqdn, *ipaddr, *zone;
+ u_int16_t defttl = 300;
+ ldns_status ret;
+ ldns_tsig_credentials tsig_cr, *tsig_cred;
+ int c = 2;
+
+ switch (argc) {
+ case 3:
+ case 4:
+ case 6:
+ case 7:
+ break;
+ default:
+ fprintf(stderr, "usage: %s FQDN [zone] IP "
+ "[tsig_name tsig_alg tsig_hmac]\n", argv[0]);
+ fprintf(stderr, "Example: %s my.host.org 1.2.3.4\n", argv[0]);
+ fprintf(stderr, "Use 'none' instead of IP to remove any "
+ "previous address.\n");
+ fprintf(stderr, "If 'zone' is not specified, "
+ "try to figure it from SOA.\n");
+ exit(1);
+ }
+
+ fqdn = argv[1];
+ c = 2;
+ if (argc == 4 || argc == 7)
+ zone = argv[c++];
+ else
+ zone = NULL;
+
+ if (strcmp(argv[c], "none") == 0)
+ ipaddr = NULL;
+ else
+ ipaddr = argv[c];
+ c++;
+ if (argc == 6 || argc == 7) {
+ tsig_cr.keyname = argv[c++];
+ if (strncasecmp(argv[c], "hmac-sha1", 9) == 0)
+ tsig_cr.algorithm = "hmac-sha1.";
+ else if (strncasecmp(argv[c], "hmac-md5", 8) == 0)
+ tsig_cr.algorithm = "hmac-md5.sig-alg.reg.int.";
+ else {
+ fprintf(stderr, "Unknown algorithm, try \"hmac-md5\" "
+ "or \"hmac-sha1\".\n");
+ exit(1);
+ }
+ tsig_cr.keydata = argv[++c];
+ tsig_cred = &tsig_cr;
+ } else
+ tsig_cred = NULL;
+
+ printf(";; trying UPDATE with FQDN \"%s\" and IP \"%s\"\n",
+ fqdn, ipaddr ? ipaddr : "<none>");
+ printf(";; tsig: \"%s\" \"%s\" \"%s\"\n", tsig_cr.keyname,
+ tsig_cr.algorithm, tsig_cr.keydata);
+
+ ret = ldns_update_send_simple_A(fqdn, zone, ipaddr, defttl, tsig_cred);
+ exit(ret);
+}
--- /dev/null
+/*
+ * tsig.h -- defines for TSIG [RFC2845]
+ *
+ * Copyright (c) 2001-2005, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ */
+
+#ifndef _LDNS_TSIG_H_
+#define _LDNS_TSIG_H_
+
+#include <ldns/common.h>
+#include <ldns/dns.h>
+#include <ldns/packet.h>
+#include <ldns/zone.h>
+
+typedef struct _ldns_tsig_credentials
+{
+ char *algorithm;
+ char *keyname;
+ char *keydata;
+ /* XXX More eventually. */
+} ldns_tsig_credentials;
+
+char *ldns_tsig_algorithm(ldns_tsig_credentials *);
+char *ldns_tsig_keyname(ldns_tsig_credentials *);
+char *ldns_tsig_keydata(ldns_tsig_credentials *);
+char *ldns_tsig_keyname_clone(ldns_tsig_credentials *);
+char *ldns_tsig_keydata_clone(ldns_tsig_credentials *);
+
+/**
+ * verifies the tsig rr for the given packet and key (string?).
+ * The wire must be given too because tsig does not sign normalized packets.
+ *
+ * \return true if tsig is correct, false if not, or if tsig is not set
+ */
+bool ldns_pkt_tsig_verify(ldns_pkt *pkt, uint8_t *wire, size_t wire_size,
+ const char *key_name, const char *key_data, ldns_rdf *mac);
+
+/**
+ * creates a tsig rr for the given packet and key (string?).
+ * \param[in] pkt the packet to sign
+ * \param[in] key_name the name of the shared key
+ * \param[in] key_data the key in base 64 format
+ * \param[in] fudge seconds of error permitted in time signed
+ * \param[in] algorithm_name the name of the algorithm used (TODO more than only hmac-md5.sig-alg.reg.int.?)
+ * \param[in] query_mac is added to the digest if not NULL (so NULL is for signing queries, not NULL is for signing answers)
+ * \return status (OK if success)
+ */
+ldns_status ldns_pkt_tsig_sign(ldns_pkt *pkt, const char *key_name,
+ const char *key_data, uint16_t fudge, const char *algorithm_name,
+ ldns_rdf *query_mac);
+
+#endif /* _LDNS_TSIG_H_ */
--- /dev/null
+/*
+ * update.h
+ *
+ * Functions for RFC 2136 Dynamic Update
+ *
+ * Copyright (c) 2005, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ */
+
+#ifndef _LDNS_UPDATE_H
+#define _LDNS_UPDATE_H
+
+ldns_pkt *ldns_update_pkt_new(ldns_rdf *, ldns_rr_class, ldns_rr_list *,
+ ldns_rr_list *, ldns_rr_list *);
+ldns_status ldns_update_pkt_tsig_add(ldns_pkt *, ldns_resolver *);
+ldns_resolver *ldns_update_resolver_new(const char *, const char *,
+ ldns_rr_class, ldns_tsig_credentials *, ldns_rdf **);
+
+uint16_t ldns_update_get_zo(const ldns_pkt *);
+uint16_t ldns_update_get_pr(const ldns_pkt *);
+uint16_t ldns_update_get_up(const ldns_pkt *);
+uint16_t ldns_update_get_ad(const ldns_pkt *);
+
+void ldns_update_set_zo(ldns_pkt *, u_int16_t);
+void ldns_update_set_pr(ldns_pkt *, u_int16_t);
+void ldns_update_set_up(ldns_pkt *, u_int16_t);
+void ldns_update_set_ad(ldns_pkt *, u_int16_t);
+
+#endif /* !_LDNS_UPDATE_H */
--- /dev/null
+/*
+ * tsig.c
+ *
+ * contains the functions needed for TSIG [RFC2845]
+ *
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/dns.h>
+
+#include <strings.h>
+
+#include <openssl/hmac.h>
+#include <openssl/md5.h>
+
+char *
+ldns_tsig_algorithm(ldns_tsig_credentials *tc)
+{
+ return tc->algorithm;
+}
+
+char *
+ldns_tsig_keyname(ldns_tsig_credentials *tc)
+{
+ return tc->keyname;
+}
+
+char *
+ldns_tsig_keydata(ldns_tsig_credentials *tc)
+{
+ return tc->keydata;
+}
+
+char *
+ldns_tsig_keyname_clone(ldns_tsig_credentials *tc)
+{
+ return strdup(tc->keyname);
+}
+
+char *
+ldns_tsig_keydata_clone(ldns_tsig_credentials *tc)
+{
+ return strdup(tc->keydata);
+}
+
+/*
+ * Makes an exact copy of the wire, but with the tsig rr removed
+ */
+uint8_t *
+ldns_tsig_prepare_pkt_wire(uint8_t *wire, size_t wire_len, size_t *result_len)
+{
+ uint8_t *wire2 = NULL;
+ uint16_t qd_count;
+ uint16_t an_count;
+ uint16_t ns_count;
+ uint16_t ar_count;
+ ldns_rr *rr;
+
+ size_t pos;
+ uint16_t i;
+
+ ldns_status status;
+
+ /* fake parse the wire */
+ qd_count = LDNS_QDCOUNT(wire);
+ an_count = LDNS_ANCOUNT(wire);
+ ns_count = LDNS_NSCOUNT(wire);
+ ar_count = LDNS_ARCOUNT(wire);
+
+ if (ar_count > 0) {
+ ar_count--;
+ } else {
+ return NULL;
+ }
+
+ pos = LDNS_HEADER_SIZE;
+
+ for (i = 0; i < qd_count; i++) {
+ status = ldns_wire2rr(&rr, wire, wire_len, &pos,
+ LDNS_SECTION_QUESTION);
+ if (status != LDNS_STATUS_OK) {
+ return NULL;
+ }
+ ldns_rr_free(rr);
+ }
+
+ for (i = 0; i < an_count; i++) {
+ status = ldns_wire2rr(&rr, wire, wire_len, &pos,
+ LDNS_SECTION_ANSWER);
+ if (status != LDNS_STATUS_OK) {
+ return NULL;
+ }
+ ldns_rr_free(rr);
+ }
+
+ for (i = 0; i < ns_count; i++) {
+ status = ldns_wire2rr(&rr, wire, wire_len, &pos,
+ LDNS_SECTION_AUTHORITY);
+ if (status != LDNS_STATUS_OK) {
+ return NULL;
+ }
+ ldns_rr_free(rr);
+ }
+
+ for (i = 0; i < ar_count; i++) {
+ status = ldns_wire2rr(&rr, wire, wire_len, &pos,
+ LDNS_SECTION_ADDITIONAL);
+ if (status != LDNS_STATUS_OK) {
+ return NULL;
+ }
+ ldns_rr_free(rr);
+ }
+
+ *result_len = pos;
+ wire2 = LDNS_XMALLOC(uint8_t, *result_len);
+ memcpy(wire2, wire, *result_len);
+
+ ldns_write_uint16(wire2 + LDNS_ARCOUNT_OFF, ar_count);
+
+ return wire2;
+}
+
+const EVP_MD *
+ldns_get_digest_function(char *name)
+{
+ /* TODO replace with openssl's EVP_get_digestbyname
+ (need init somewhere for that)
+ */
+ if (strlen(name) == 10 && strncasecmp(name, "hmac-sha1.", 9) == 0)
+ return EVP_sha1();
+ else if (strlen(name) == 25 && strncasecmp(name,
+ "hmac-md5.sig-alg.reg.int.", 25) == 0)
+ return EVP_md5();
+ else
+ return NULL;
+}
+
+ldns_status
+ldns_create_tsig_mac(
+ ldns_rdf **tsig_mac,
+ uint8_t *pkt_wire,
+ size_t pkt_wire_size,
+ const char *key_data,
+ ldns_rdf *key_name_rdf,
+ ldns_rdf *fudge_rdf,
+ ldns_rdf *algorithm_rdf,
+ ldns_rdf *time_signed_rdf,
+ ldns_rdf *error_rdf,
+ ldns_rdf *other_data_rdf,
+ ldns_rdf *orig_mac_rdf
+)
+{
+ ldns_buffer *data_buffer = NULL;
+ char *wireformat;
+ int wiresize;
+ unsigned char *mac_bytes;
+ unsigned int md_len = EVP_MAX_MD_SIZE;
+ unsigned char *key_bytes;
+ int key_size;
+ const EVP_MD *digester;
+ char *algorithm_name;
+ ldns_rdf *result = NULL;
+
+ /*
+ * prepare the digestable information
+ */
+ data_buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+ /* if orig_mac is not NULL, add it too */
+ if (orig_mac_rdf) {
+ (void) ldns_rdf2buffer_wire(data_buffer, orig_mac_rdf);
+ }
+ ldns_buffer_write(data_buffer, pkt_wire, pkt_wire_size);
+ (void) ldns_rdf2buffer_wire(data_buffer, key_name_rdf);
+ ldns_buffer_write_u16(data_buffer, LDNS_RR_CLASS_ANY);
+ ldns_buffer_write_u32(data_buffer, 0);
+ (void) ldns_rdf2buffer_wire(data_buffer, algorithm_rdf);
+ (void) ldns_rdf2buffer_wire(data_buffer, time_signed_rdf);
+ (void) ldns_rdf2buffer_wire(data_buffer, fudge_rdf);
+ (void) ldns_rdf2buffer_wire(data_buffer, error_rdf);
+ (void) ldns_rdf2buffer_wire(data_buffer, other_data_rdf);
+
+ wireformat = (char *) data_buffer->_data;
+ wiresize = (int) ldns_buffer_position(data_buffer);
+
+ algorithm_name = ldns_rdf2str(algorithm_rdf);
+
+ /* prepare the key */
+ key_bytes = LDNS_XMALLOC(unsigned char, b64_pton_calculate_size(strlen(key_data)));
+ key_size = b64_pton(key_data, key_bytes, strlen(key_data) * 2);
+ if (key_size < 0) {
+ /* LDNS_STATUS_INVALID_B64 */
+ dprintf("%s\n", "Bad base64 string");
+ return LDNS_STATUS_INVALID_B64;
+ }
+ /* hmac it */
+ /* 2 spare bytes for the length */
+ mac_bytes = LDNS_XMALLOC(unsigned char, md_len);
+ memset(mac_bytes, 0, md_len);
+
+ digester = ldns_get_digest_function(algorithm_name);
+
+ if (digester) {
+ (void) HMAC(digester, key_bytes, key_size, (void *)wireformat, wiresize, mac_bytes + 2, &md_len);
+
+ ldns_write_uint16(mac_bytes, md_len);
+ result = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT16_DATA, md_len + 2, mac_bytes);
+ } else {
+ /*dprintf("No digest found for %s\n", algorithm_name);*/
+ return LDNS_STATUS_CRYPTO_UNKNOWN_ALGO;
+ }
+
+ LDNS_FREE(algorithm_name);
+ LDNS_FREE(mac_bytes);
+ LDNS_FREE(key_bytes);
+ ldns_buffer_free(data_buffer);
+
+ *tsig_mac = result;
+
+ return LDNS_STATUS_OK;
+}
+
+
+/* THIS FUNC WILL REMOVE TSIG ITSELF */
+bool
+ldns_pkt_tsig_verify(ldns_pkt *pkt,
+ uint8_t *wire,
+ size_t wirelen,
+ const char *key_name,
+ const char *key_data,
+ ldns_rdf *orig_mac_rdf)
+{
+ ldns_rdf *fudge_rdf;
+ ldns_rdf *algorithm_rdf;
+ ldns_rdf *time_signed_rdf;
+ ldns_rdf *orig_id_rdf;
+ ldns_rdf *error_rdf;
+ ldns_rdf *other_data_rdf;
+ ldns_rdf *pkt_mac_rdf;
+ ldns_rdf *my_mac_rdf;
+ ldns_rdf *key_name_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, key_name);
+ uint16_t pkt_id, orig_pkt_id;
+ ldns_status status;
+
+ uint8_t *prepared_wire = NULL;
+ size_t prepared_wire_size = 0;
+
+ ldns_rr *orig_tsig = ldns_pkt_tsig(pkt);
+
+ if (!orig_tsig) {
+ ldns_rdf_deep_free(key_name_rdf);
+ return false;
+ }
+ algorithm_rdf = ldns_rr_rdf(orig_tsig, 0);
+ time_signed_rdf = ldns_rr_rdf(orig_tsig, 1);
+ fudge_rdf = ldns_rr_rdf(orig_tsig, 2);
+ pkt_mac_rdf = ldns_rr_rdf(orig_tsig, 3);
+ orig_id_rdf = ldns_rr_rdf(orig_tsig, 4);
+ error_rdf = ldns_rr_rdf(orig_tsig, 5);
+ other_data_rdf = ldns_rr_rdf(orig_tsig, 6);
+
+ /* remove temporarily */
+ ldns_pkt_set_tsig(pkt, NULL);
+ /* temporarily change the id to the original id */
+ pkt_id = ldns_pkt_id(pkt);
+ orig_pkt_id = ldns_rdf2native_int16(orig_id_rdf);
+ ldns_pkt_set_id(pkt, orig_pkt_id);
+
+ prepared_wire = ldns_tsig_prepare_pkt_wire(wire, wirelen, &prepared_wire_size);
+
+ status = ldns_create_tsig_mac(&my_mac_rdf,
+ prepared_wire,
+ prepared_wire_size,
+ key_data,
+ key_name_rdf,
+ fudge_rdf,
+ algorithm_rdf,
+ time_signed_rdf,
+ error_rdf,
+ other_data_rdf,
+ orig_mac_rdf
+ );
+
+ LDNS_FREE(prepared_wire);
+
+ if (status != LDNS_STATUS_OK) {
+ ldns_rdf_deep_free(key_name_rdf);
+ return false;
+ }
+ /* Put back the values */
+ ldns_pkt_set_tsig(pkt, orig_tsig);
+ ldns_pkt_set_id(pkt, pkt_id);
+
+ ldns_rdf_deep_free(key_name_rdf);
+
+ if (ldns_rdf_compare(pkt_mac_rdf, my_mac_rdf) == 0) {
+ ldns_rdf_deep_free(my_mac_rdf);
+ return true;
+ } else {
+ ldns_rdf_deep_free(my_mac_rdf);
+ return false;
+ }
+}
+
+/* TODO: memory :p */
+ldns_status
+ldns_pkt_tsig_sign(ldns_pkt *pkt, const char *key_name, const char *key_data, uint16_t fudge, const char *algorithm_name, ldns_rdf *query_mac)
+{
+ ldns_rr *tsig_rr;
+ ldns_rdf *key_name_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, key_name);
+ ldns_rdf *fudge_rdf = NULL;
+ ldns_rdf *orig_id_rdf = NULL;
+ ldns_rdf *algorithm_rdf;
+ ldns_rdf *error_rdf = NULL;
+ ldns_rdf *mac_rdf = NULL;
+ ldns_rdf *other_data_rdf = NULL;
+
+ ldns_status status = LDNS_STATUS_OK;
+
+ uint8_t *pkt_wire = NULL;
+ size_t pkt_wire_len;
+
+ struct timeval tv_time_signed;
+ uint8_t *time_signed = NULL;
+ ldns_rdf *time_signed_rdf = NULL;
+
+ algorithm_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, algorithm_name);
+
+ /* eww don't have create tsigtime rdf yet :( */
+ /* bleh :p */
+ if (gettimeofday(&tv_time_signed, NULL) == 0) {
+ time_signed = LDNS_XMALLOC(uint8_t, 6);
+ ldns_write_uint64_as_uint48(time_signed, tv_time_signed.tv_sec);
+ } else {
+ status = LDNS_STATUS_INTERNAL_ERR;
+ goto clean;
+ }
+
+ time_signed_rdf = ldns_rdf_new(LDNS_RDF_TYPE_TSIGTIME, 6, time_signed);
+
+ fudge_rdf = ldns_native2rdf_int16(LDNS_RDF_TYPE_INT16, fudge);
+
+ orig_id_rdf = ldns_native2rdf_int16(LDNS_RDF_TYPE_INT16, ldns_pkt_id(pkt));
+
+ error_rdf = ldns_native2rdf_int16(LDNS_RDF_TYPE_INT16, 0);
+
+ other_data_rdf = ldns_native2rdf_int16_data(0, NULL);
+
+ if (ldns_pkt2wire(&pkt_wire, pkt, &pkt_wire_len) != LDNS_STATUS_OK) {
+ status = LDNS_STATUS_ERR;
+ goto clean;
+ }
+
+ status = ldns_create_tsig_mac(&mac_rdf,
+ pkt_wire,
+ pkt_wire_len,
+ key_data,
+ key_name_rdf,
+ fudge_rdf,
+ algorithm_rdf,
+ time_signed_rdf,
+ error_rdf,
+ other_data_rdf,
+ query_mac
+ );
+
+ if (!mac_rdf) {
+ goto clean;
+ }
+
+ LDNS_FREE(pkt_wire);
+
+ /* Create the TSIG RR */
+ tsig_rr = ldns_rr_new();
+ ldns_rr_set_owner(tsig_rr, key_name_rdf);
+ ldns_rr_set_class(tsig_rr, LDNS_RR_CLASS_ANY);
+ ldns_rr_set_type(tsig_rr, LDNS_RR_TYPE_TSIG);
+ ldns_rr_set_ttl(tsig_rr, 0);
+
+ ldns_rr_push_rdf(tsig_rr, algorithm_rdf);
+ ldns_rr_push_rdf(tsig_rr, time_signed_rdf);
+ ldns_rr_push_rdf(tsig_rr, fudge_rdf);
+ ldns_rr_push_rdf(tsig_rr, mac_rdf);
+ ldns_rr_push_rdf(tsig_rr, orig_id_rdf);
+ ldns_rr_push_rdf(tsig_rr, error_rdf);
+ ldns_rr_push_rdf(tsig_rr, other_data_rdf);
+
+ ldns_pkt_set_tsig(pkt, tsig_rr);
+
+ return status;
+
+ clean:
+ ldns_rdf_free(key_name_rdf);
+ ldns_rdf_free(algorithm_rdf);
+ ldns_rdf_free(time_signed_rdf);
+ ldns_rdf_free(fudge_rdf);
+ ldns_rdf_free(orig_id_rdf);
+ ldns_rdf_free(error_rdf);
+ ldns_rdf_free(other_data_rdf);
+ return status;
+}
+
+
--- /dev/null
+/* update.c
+ *
+ * Functions for RFC 2136 Dynamic Update
+ *
+ * Copyright (c) 2005, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/dns.h>
+
+#include <strings.h>
+#include <stdlib.h>
+#include <limits.h>
+
+/*
+ * RFC 2136 sections mapped to RFC 1035:
+ * zone/ZO -- QD/question
+ * prerequisites/PR -- AN/answers
+ * updates/UP -- NS/authority records
+ * additional data/AD -- AR/additional records
+ */
+
+#define _zone _question
+#define _prereq _answer
+#define _updates _authority
+
+/**
+ * create an update packet from zone name, class and the rr lists
+ * \param[in] zone name of the zone
+ * \param[in] class zone class
+ * \param[in] pr_rrlist list of Prerequisite Section RRs
+ * \param[in] up_rrlist list of Updates Section RRs
+ * \param[in] ad_rrlist list of Additional Data Section RRs (currently unused)
+ */
+ldns_pkt *
+ldns_update_pkt_new(ldns_rdf *zone_rdf, ldns_rr_class class,
+ ldns_rr_list *pr_rrlist, ldns_rr_list *up_rrlist, ldns_rr_list *ad_rrlist)
+{
+ ldns_pkt *p;
+
+ if (!zone_rdf || !up_rrlist) {
+ dprintf("%s", "bad input to ldns_update_pkt_new()\n");
+ return NULL;
+ }
+
+ if (class == 0)
+ class = LDNS_RR_CLASS_IN;
+
+ /* Create packet, fill in Zone Section. */
+ p = ldns_pkt_query_new(zone_rdf, LDNS_RR_TYPE_SOA, class, LDNS_RD);
+ if (!p)
+ return NULL;
+ zone_rdf = NULL; /* No longer safe to use. */
+
+ ldns_pkt_set_opcode(p, LDNS_PACKET_UPDATE);
+
+ ldns_rr_list_deep_free(p->_updates);
+ p->_updates = ldns_rr_list_clone(up_rrlist);
+ ldns_update_set_up(p, ldns_rr_list_rr_count(up_rrlist));
+
+ if (pr_rrlist) {
+ ldns_rr_list_deep_free(p->_prereq);
+ p->_prereq = ldns_rr_list_clone(pr_rrlist);
+ ldns_update_set_pr(p, ldns_rr_list_rr_count(pr_rrlist));
+ }
+
+ if (ad_rrlist) {
+ ldns_rr_list_deep_free(p->_additional);
+ p->_additional = ldns_rr_list_clone(ad_rrlist);
+ ldns_update_set_ad(p, ldns_rr_list_rr_count(ad_rrlist));
+ }
+
+ return p;
+}
+
+ldns_status
+ldns_update_pkt_tsig_add(ldns_pkt *p, ldns_resolver *r)
+{
+ u_int16_t fudge = 300; /* Recommended fudge. [RFC2845 6.4] */
+
+ if (ldns_resolver_tsig_keyname(r) && ldns_resolver_tsig_keydata(r))
+ return ldns_pkt_tsig_sign(p, ldns_resolver_tsig_keyname(r),
+ ldns_resolver_tsig_keydata(r), fudge,
+ ldns_resolver_tsig_algorithm(r), NULL);
+
+ /* No TSIG to do. */
+ return LDNS_STATUS_OK;
+}
+
+/* Move to higher.c or similar? */
+
+ldns_status
+ldns_update_get_soa_mname(ldns_rdf *zone, ldns_resolver *r,
+ ldns_rr_class class, ldns_rdf **mname)
+{
+ ldns_rr *soa_rr;
+ ldns_pkt *query, *resp;
+
+ /* Nondestructive, so clone 'zone' here */
+ query = ldns_pkt_query_new(ldns_rdf_clone(zone), LDNS_RR_TYPE_SOA,
+ class, LDNS_RD);
+ if (!query)
+ return LDNS_STATUS_ERR;
+
+ ldns_pkt_set_random_id(query);
+ if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
+ dprintf("%s", "SOA query failed (MNAME)\n");
+ ldns_pkt_free(query);
+ return LDNS_STATUS_ERR;
+ }
+ ldns_pkt_free(query);
+ if (!resp)
+ return LDNS_STATUS_ERR;
+
+ /* Expect a SOA answer. */
+ *mname = NULL;
+ while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_answer(resp)))) {
+ if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA)
+ continue;
+ /* [RFC1035 3.3.13] */
+ *mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
+ break;
+ }
+ ldns_pkt_free(resp);
+
+ return *mname ? LDNS_STATUS_OK : LDNS_STATUS_ERR;
+}
+
+/* Try to get zone and MNAME from SOA queries. */
+ldns_status
+ldns_update_get_soa_zone_mname(const char *fqdn, ldns_resolver *r,
+ ldns_rr_class class, ldns_rdf **zone_rdf, ldns_rdf **mname_rdf)
+{
+ ldns_rr *soa_rr, *rr;
+ ldns_rdf *soa_zone = NULL, *soa_mname = NULL;
+ ldns_rdf *ipaddr, *fqdn_rdf, *tmp;
+ ldns_rdf **nslist;
+ ldns_pkt *query, *resp;
+ int i;
+
+ /*
+ * XXX Ok, this cannot be the best way to find this...?
+ * XXX (I run into weird cache-related stuff here)
+ */
+
+ /* Step 1 - first find a nameserver that should know *something* */
+ fqdn_rdf = ldns_dname_new_frm_str(fqdn);
+ query = ldns_pkt_query_new(fqdn_rdf, LDNS_RR_TYPE_SOA, class, LDNS_RD);
+ if (!query)
+ return LDNS_STATUS_ERR;
+ fqdn_rdf = NULL;
+
+ ldns_pkt_set_random_id(query);
+ if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
+ dprintf("%s", "SOA query failed\n");
+ ldns_pkt_free(query);
+ return LDNS_STATUS_ERR;
+ }
+ ldns_pkt_free(query);
+ if (!resp)
+ return LDNS_STATUS_ERR;
+
+ /* XXX Is it safe to only look in authority section here? */
+ while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_authority(resp)))) {
+ if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA)
+ continue;
+ /* [RFC1035 3.3.13] */
+ soa_mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
+ break;
+ }
+ ldns_pkt_free(resp);
+ if (!soa_rr)
+ return LDNS_STATUS_ERR;
+
+ /* Step 2 - find SOA MNAME IP address, add to resolver */
+ query = ldns_pkt_query_new(soa_mname, LDNS_RR_TYPE_A, class, LDNS_RD);
+ if (!query)
+ return LDNS_STATUS_ERR;
+ soa_mname = NULL;
+
+ ldns_pkt_set_random_id(query);
+ if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
+ dprintf("%s", "SOA query 2 failed\n");
+ ldns_pkt_free(query);
+ return LDNS_STATUS_ERR;
+ }
+ ldns_pkt_free(query);
+ if (!resp)
+ return LDNS_STATUS_ERR;
+
+ if (ldns_pkt_ancount(resp) == 0) {
+ ldns_pkt_free(resp);
+ return LDNS_STATUS_ERR;
+ }
+
+ /* XXX There may be more than one answer RR here. */
+ rr = ldns_rr_list_pop_rr(ldns_pkt_answer(resp));
+ ipaddr = ldns_rr_rdf(rr, 0);
+
+ /* Put the SOA mname IP first in the nameserver list. */
+ nslist = ldns_resolver_nameservers(r);
+ for (i = 0; i < ldns_resolver_nameserver_count(r); i++) {
+ if (ldns_rdf_compare(ipaddr, nslist[i]) == 0) {
+ if (i) {
+ tmp = nslist[0];
+ nslist[0] = nslist[i];
+ nslist[i] = tmp;
+ }
+ break;
+ }
+ }
+ if (i >= ldns_resolver_nameserver_count(r)) {
+ /* SOA mname was not part of the resolver so add it first. */
+ ldns_resolver_push_nameserver(r, ipaddr);
+ nslist = ldns_resolver_nameservers(r);
+ i = ldns_resolver_nameserver_count(r) - 1;
+ tmp = nslist[0];
+ nslist[0] = nslist[i];
+ nslist[i] = tmp;
+ }
+ ldns_pkt_free(resp);
+
+ /* Make sure to ask the first in the list, i.e SOA mname */
+ ldns_resolver_set_random(r, false);
+
+ /* Step 3 - Redo SOA query, sending to SOA MNAME directly. */
+ fqdn_rdf = ldns_dname_new_frm_str(fqdn);
+ query = ldns_pkt_query_new(fqdn_rdf, LDNS_RR_TYPE_SOA, class, LDNS_RD);
+ if (!query)
+ return LDNS_STATUS_ERR;
+ fqdn_rdf = NULL;
+
+ ldns_pkt_set_random_id(query);
+ if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
+ dprintf("%s", "SOA query failed\n");
+ ldns_pkt_free(query);
+ return LDNS_STATUS_ERR;
+ }
+ ldns_pkt_free(query);
+ if (!resp)
+ return LDNS_STATUS_ERR;
+
+ /* XXX Is it safe to only look in authority section here, too? */
+ while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_authority(resp)))) {
+ if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA)
+ continue;
+ /* [RFC1035 3.3.13] */
+ soa_mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
+ soa_zone = ldns_rdf_clone(ldns_rr_owner(soa_rr));
+ break;
+ }
+ ldns_pkt_free(resp);
+ if (!soa_rr)
+ return LDNS_STATUS_ERR;
+
+ /* That seems to have worked, pass results to caller. */
+ *zone_rdf = soa_zone;
+ *mname_rdf = soa_mname;
+ return LDNS_STATUS_OK;
+}
+
+/**
+ * Create a resolver suitable for use with UPDATE. [RFC2136 4.3]
+ * SOA MNAME is used as the "primary master".
+ * \param[in] fqdn FQDN of a host in a zone
+ * \param[in] zone zone name, if explicitly given, otherwise use SOA
+ * \param[in] class zone class
+ * \param[in] tsig_cred TSIG credentials
+ * \param[out] zone returns zone/owner rdf from the 'fqdn' SOA MNAME query
+ */
+ldns_resolver *
+ldns_update_resolver_new(const char *fqdn, const char *zone,
+ ldns_rr_class class, ldns_tsig_credentials *tsig_cred, ldns_rdf **zone_rdf)
+{
+ ldns_resolver *r1, *r2;
+ ldns_pkt *query = NULL, *resp;
+ ldns_rr_list *nslist, *iplist;
+ ldns_rdf *soa_zone, *soa_mname, *ns_name;
+ int i;
+
+ if (class == 0)
+ class = LDNS_RR_CLASS_IN;
+
+ /* First, get data from /etc/resolv.conf */
+ r1 = ldns_resolver_new_frm_file(NULL);
+ if (!r1)
+ return NULL;
+
+ r2 = ldns_resolver_new();
+ if (!r2)
+ goto bad;
+
+ /* TSIG key data available? Copy into the resolver. */
+ if (tsig_cred) {
+ ldns_resolver_set_tsig_algorithm(r2,
+ ldns_tsig_algorithm(tsig_cred));
+ ldns_resolver_set_tsig_keyname(r2,
+ ldns_tsig_keyname_clone(tsig_cred));
+ /*
+ * XXX Weird that ldns_resolver_deep_free() will free()
+ * keyname but not hmac key data?
+ */
+ ldns_resolver_set_tsig_keydata(r2,
+ ldns_tsig_keydata_clone(tsig_cred));
+ }
+
+ /* Now get SOA zone, mname, NS, and construct r2. [RFC2136 4.3] */
+
+ /* Explicit 'zone' or no? */
+ if (zone) {
+ soa_zone = ldns_dname_new_frm_str(zone);
+ if (ldns_update_get_soa_mname(soa_zone, r1, class, &soa_mname)
+ != LDNS_STATUS_OK)
+ goto bad;
+ } else {
+ if (ldns_update_get_soa_zone_mname(fqdn, r1, class, &soa_zone,
+ &soa_mname) != LDNS_STATUS_OK)
+ goto bad;
+ }
+
+ /* Pass zone_rdf on upwards. */
+ *zone_rdf = ldns_rdf_clone(soa_zone);
+
+ /* NS */
+ query = ldns_pkt_query_new(soa_zone, LDNS_RR_TYPE_NS, class, LDNS_RD);
+ if (!query)
+ goto bad;
+ soa_zone = NULL;
+
+ ldns_pkt_set_random_id(query);
+ if (ldns_resolver_send_pkt(&resp, r1, query) != LDNS_STATUS_OK) {
+ dprintf("%s", "NS query failed!\n");
+ goto bad;
+ }
+ ldns_pkt_free(query);
+ if (!resp)
+ goto bad;
+
+ /* Match SOA MNAME to NS list, adding it first */
+ nslist = ldns_pkt_answer(resp);
+ for (i = 0; i < ldns_rr_list_rr_count(nslist); i++) {
+ ns_name = ldns_rr_rdf(ldns_rr_list_rr(nslist, i), 0);
+ if (!ns_name)
+ continue;
+ if (ldns_rdf_compare(soa_mname, ns_name) == 0) {
+ /* Match */
+ iplist = ldns_get_rr_list_addr_by_name(r1, ns_name,
+ class, 0);
+ ldns_resolver_push_nameserver_rr_list(r2, iplist);
+ break;
+ }
+ }
+
+ /* Then all the other NSs. XXX Randomize? */
+ for (i = 0; i < ldns_rr_list_rr_count(nslist); i++) {
+ ns_name = ldns_rr_rdf(ldns_rr_list_rr(nslist, i), 0);
+ if (!ns_name)
+ continue;
+ if (ldns_rdf_compare(soa_mname, ns_name) != 0) {
+ /* No match, add it now. */
+ iplist = ldns_get_rr_list_addr_by_name(r1, ns_name,
+ class, 0);
+ ldns_resolver_push_nameserver_rr_list(r2, iplist);
+ }
+ }
+
+ /* Cleanup and return. */
+ ldns_resolver_set_random(r2, false);
+ ldns_pkt_free(resp);
+ ldns_resolver_deep_free(r1);
+ return r2;
+
+ bad:
+ if (r1)
+ ldns_resolver_deep_free(r1);
+ if (r2)
+ ldns_resolver_deep_free(r2);
+ if (query)
+ ldns_pkt_free(query);
+ if (resp)
+ ldns_pkt_free(resp);
+ return NULL;
+}
+
+/*
+ * ldns_update_{get,set}_{zo,pr,up,ad}.
+ */
+
+uint16_t
+ldns_update_get_zo(const ldns_pkt *p)
+{
+ return ldns_pkt_qdcount(p);
+}
+
+uint16_t
+ldns_update_get_pr(const ldns_pkt *p)
+{
+ return ldns_pkt_ancount(p);
+}
+
+uint16_t
+ldns_update_get_up(const ldns_pkt *p)
+{
+ return ldns_pkt_nscount(p);
+}
+
+uint16_t
+ldns_update_get_ad(const ldns_pkt *p)
+{
+ return ldns_pkt_arcount(p);
+}
+
+void
+ldns_update_set_zo(ldns_pkt *p, u_int16_t v)
+{
+ ldns_pkt_set_qdcount(p, v);
+}
+
+void
+ldns_update_set_pr(ldns_pkt *p, u_int16_t v)
+{
+ ldns_pkt_set_ancount(p, v);
+}
+
+void
+ldns_update_set_up(ldns_pkt *p, u_int16_t v)
+{
+ ldns_pkt_set_nscount(p, v);
+}
+
+void
+ldns_update_set_ad(ldns_pkt *p, u_int16_t v)
+{
+ ldns_pkt_set_arcount(p, v);
+}
+
+