311 lines
9.4 KiB
C
311 lines
9.4 KiB
C
|
/* $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 <strings.h>
|
||
|
#include <ldns/ldns.h>
|
||
|
|
||
|
/* 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 <ip>\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 : "<none>");
|
||
|
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);
|
||
|
}
|