/* $Id: ldns-update.c,v 1.1 2005/09/13 09:37:05 ho Exp $ */ /* * Example of the update functionality * * See the file LICENSE for the license */ #include "config.h" #include #include /* dynamic update stuff */ static ldns_resolver * ldns_update_resolver_new(const char *fqdn, const char *zone, ldns_rr_class class, uint16_t port, 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; size_t i; ldns_status s; if (class == 0) { class = LDNS_RR_CLASS_IN; } if (port == 0) { port = LDNS_PORT; } /* First, get data from /etc/resolv.conf */ s = ldns_resolver_new_frm_file(&r1, NULL); if (s != LDNS_STATUS_OK) { return NULL; } r2 = ldns_resolver_new(); if (!r2) { goto bad; } ldns_resolver_set_port(r2, port); /* 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(tsig_cred)); ldns_resolver_set_tsig_keydata(r2, ldns_tsig_keydata(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_soa_mname(soa_zone, r1, class, &soa_mname) != LDNS_STATUS_OK) { goto bad; } } else { if (ldns_update_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); (void) 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); (void) ldns_resolver_push_nameserver_rr_list(r2, iplist); } } 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; } static ldns_status ldns_update_send_simple_addr(const char *fqdn, const char *zone, const char *ipaddr, uint16_t p, uint32_t ttl, ldns_tsig_credentials *tsig_cred) { ldns_resolver *res; ldns_pkt *u_pkt = NULL, *r_pkt; ldns_rr_list *up_rrlist; ldns_rr *up_rr; ldns_rdf *zone_rdf; char *rrstr; uint32_t rrstrlen, status = LDNS_STATUS_OK; if (!fqdn || strlen(fqdn) == 0) return LDNS_STATUS_ERR; /* Create resolver */ res = ldns_update_resolver_new(fqdn, zone, 0, p, tsig_cred, &zone_rdf); if (!res || !zone_rdf) { goto cleanup; } /* Set up the update section. */ up_rrlist = ldns_rr_list_new(); if (!up_rrlist) { goto cleanup; } /* Create input for ldns_rr_new_frm_str() */ if (ipaddr) { /* We're adding A or AAAA */ rrstrlen = strlen(fqdn) + sizeof (" IN AAAA ") + strlen(ipaddr) + 1; rrstr = (char *)malloc(rrstrlen); if (!rrstr) { ldns_rr_list_deep_free(up_rrlist); goto cleanup; } snprintf(rrstr, rrstrlen, "%s IN %s %s", fqdn, strchr(ipaddr, ':') ? "AAAA" : "A", ipaddr); if (ldns_rr_new_frm_str(&up_rr, rrstr, ttl, NULL, NULL) != LDNS_STATUS_OK) { ldns_rr_list_deep_free(up_rrlist); free(rrstr); goto cleanup; } free(rrstr); ldns_rr_list_push_rr(up_rrlist, up_rr); } else { /* We're removing A and/or AAAA from 'fqdn'. [RFC2136 2.5.2] */ up_rr = ldns_rr_new(); ldns_rr_set_owner(up_rr, ldns_dname_new_frm_str(fqdn)); ldns_rr_set_ttl(up_rr, 0); ldns_rr_set_class(up_rr, LDNS_RR_CLASS_ANY); ldns_rr_set_type(up_rr, LDNS_RR_TYPE_A); ldns_rr_list_push_rr(up_rrlist, ldns_rr_clone(up_rr)); ldns_rr_set_type(up_rr, LDNS_RR_TYPE_AAAA); ldns_rr_list_push_rr(up_rrlist, up_rr); } /* Create update packet. */ u_pkt = ldns_update_pkt_new(zone_rdf, LDNS_RR_CLASS_IN, NULL, up_rrlist, NULL); zone_rdf = NULL; if (!u_pkt) { ldns_rr_list_deep_free(up_rrlist); goto cleanup; } ldns_pkt_set_random_id(u_pkt); /* Add TSIG */ if (tsig_cred) if (ldns_update_pkt_tsig_add(u_pkt, res) != LDNS_STATUS_OK) { goto cleanup; } if (ldns_resolver_send_pkt(&r_pkt, res, u_pkt) != LDNS_STATUS_OK) { goto cleanup; } ldns_pkt_free(u_pkt); if (!r_pkt) { goto cleanup; } if (ldns_pkt_get_rcode(r_pkt) != LDNS_RCODE_NOERROR) { ldns_lookup_table *t = ldns_lookup_by_id(ldns_rcodes, (int)ldns_pkt_get_rcode(r_pkt)); if (t) { dprintf(";; UPDATE response was %s\n", t->name); } else { dprintf(";; UPDATE response was (%d)\n", ldns_pkt_get_rcode(r_pkt)); } status = LDNS_STATUS_ERR; } ldns_pkt_free(r_pkt); ldns_resolver_deep_free(res); return status; cleanup: if (res) ldns_resolver_deep_free(res); if (u_pkt) ldns_pkt_free(u_pkt); return LDNS_STATUS_ERR; } static void usage(FILE *fp, char *prog) { fprintf(fp, "%s domain [zone] ip tsig_name tsig_alg tsig_hmac\n", prog); fprintf(fp, " send a dynamic update packet to \n\n"); fprintf(fp, " Use 'none' instead of ip to remove any previous address\n"); fprintf(fp, " If 'zone' is not specified, try to figure it out from the zone's SOA\n"); fprintf(fp, " Example: %s my.example.org 1.2.3.4\n", prog); } int main(int argc, char **argv) { char *fqdn, *ipaddr, *zone, *prog; ldns_status ret; ldns_tsig_credentials tsig_cr, *tsig_cred; int c = 2; uint32_t defttl = 300; uint32_t port = 5353; prog = strdup(argv[0]); switch (argc) { case 3: case 4: case 6: case 7: break; default: usage(stderr, prog); exit(EXIT_FAILURE); } 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 = (char*)"hmac-sha1."; } else if (strncasecmp(argv[c], "hmac-md5", 8) == 0) { tsig_cr.algorithm = (char*)"hmac-md5.sig-alg.reg.int."; } else { fprintf(stderr, "Unknown algorithm, try \"hmac-md5\" " "or \"hmac-sha1\".\n"); exit(EXIT_FAILURE); } 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 : ""); printf(";; tsig: \"%s\" \"%s\" \"%s\"\n", tsig_cr.keyname, tsig_cr.algorithm, tsig_cr.keydata); ret = ldns_update_send_simple_addr(fqdn, zone, ipaddr, port, defttl, tsig_cred); exit(ret); }