freeswitch/libs/ldns/examples/ldns-update.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);
}