2 * Copyright (C) 2009, 2010, 2012, 2013 Internet Systems Consortium, Inc. ("ISC")
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
17 /* $Id: sample-update.c,v 1.10 2010/12/09 00:54:34 marka Exp $ */
21 #include <sys/types.h>
22 #include <sys/socket.h>
24 #include <netinet/in.h>
26 #include <arpa/inet.h>
35 #include <isc/buffer.h>
39 #include <isc/parseint.h>
40 #include <isc/sockaddr.h>
43 #include <dns/callbacks.h>
44 #include <dns/client.h>
45 #include <dns/fixedname.h>
48 #include <dns/rdata.h>
49 #include <dns/rdataclass.h>
50 #include <dns/rdatalist.h>
51 #include <dns/rdataset.h>
52 #include <dns/rdatastruct.h>
53 #include <dns/rdatatype.h>
54 #include <dns/result.h>
55 #include <dns/secalg.h>
60 static dns_tsec_t *tsec = NULL;
61 static const dns_rdataclass_t default_rdataclass = dns_rdataclass_in;
62 static isc_bufferlist_t usedbuffers;
63 static ISC_LIST(dns_rdatalist_t) usedrdatalists;
65 static void setup_tsec(char *keyfile, isc_mem_t *mctx);
66 static void update_addordelete(isc_mem_t *mctx, char *cmdline,
67 isc_boolean_t isdelete, dns_name_t *name);
68 static void evaluate_prereq(isc_mem_t *mctx, char *cmdline, dns_name_t *name);
70 ISC_PLATFORM_NORETURN_PRE static void
71 usage(void) ISC_PLATFORM_NORETURN_POST;
75 fprintf(stderr, "sample-update "
79 "[-r recursive_server] "
81 "(add|delete) \"name TTL RRtype RDATA\"\n");
86 main(int argc, char *argv[]) {
88 struct addrinfo hints, *res;
90 dns_client_t *client = NULL;
91 char *zonenamestr = NULL;
92 char *keyfilename = NULL;
93 char *prereqstr = NULL;
94 isc_sockaddrlist_t auth_servers;
95 char *auth_server = NULL;
96 char *recursive_server = NULL;
97 isc_sockaddr_t sa_auth, sa_recursive;
98 isc_sockaddrlist_t rec_servers;
100 isc_boolean_t isdelete;
101 isc_buffer_t b, *buf;
102 dns_fixedname_t zname0, pname0, uname0;
104 dns_name_t *zname = NULL, *uname, *pname;
105 dns_rdataset_t *rdataset;
106 dns_rdatalist_t *rdatalist;
108 dns_namelist_t updatelist, prereqlist, *prereqlistp = NULL;
109 isc_mem_t *umctx = NULL;
111 while ((ch = getopt(argc, argv, "a:k:p:r:z:")) != -1) {
114 keyfilename = optarg;
117 auth_server = optarg;
123 recursive_server = optarg;
126 zonenamestr = optarg;
138 /* command line argument validation */
139 if (strcmp(argv[0], "delete") == 0)
141 else if (strcmp(argv[0], "add") == 0)
142 isdelete = ISC_FALSE;
144 fprintf(stderr, "invalid update command: %s\n", argv[0]);
148 if (auth_server == NULL && recursive_server == NULL) {
149 fprintf(stderr, "authoritative or recursive server "
150 "must be specified\n");
155 ISC_LIST_INIT(usedbuffers);
156 ISC_LIST_INIT(usedrdatalists);
157 ISC_LIST_INIT(prereqlist);
158 ISC_LIST_INIT(auth_servers);
160 result = dns_lib_init();
161 if (result != ISC_R_SUCCESS) {
162 fprintf(stderr, "dns_lib_init failed: %d\n", result);
165 result = isc_mem_create(0, 0, &umctx);
166 if (result != ISC_R_SUCCESS) {
167 fprintf(stderr, "failed to crate mctx\n");
171 result = dns_client_create(&client, 0);
172 if (result != ISC_R_SUCCESS) {
173 fprintf(stderr, "dns_client_create failed: %d\n", result);
177 /* Set the authoritative server */
178 if (auth_server != NULL) {
179 memset(&hints, 0, sizeof(hints));
180 hints.ai_family = AF_UNSPEC;
181 hints.ai_socktype = SOCK_DGRAM;
182 hints.ai_protocol = IPPROTO_UDP;
183 hints.ai_flags = AI_NUMERICHOST;
184 gai_error = getaddrinfo(auth_server, "53", &hints, &res);
185 if (gai_error != 0) {
186 fprintf(stderr, "getaddrinfo failed: %s\n",
187 gai_strerror(gai_error));
190 INSIST(res->ai_addrlen <= sizeof(sa_auth.type));
191 memcpy(&sa_auth.type, res->ai_addr, res->ai_addrlen);
193 sa_auth.length = res->ai_addrlen;
194 ISC_LINK_INIT(&sa_auth, link);
196 ISC_LIST_APPEND(auth_servers, &sa_auth, link);
199 /* Set the recursive server */
200 if (recursive_server != NULL) {
201 memset(&hints, 0, sizeof(hints));
202 hints.ai_family = AF_UNSPEC;
203 hints.ai_socktype = SOCK_DGRAM;
204 hints.ai_protocol = IPPROTO_UDP;
205 hints.ai_flags = AI_NUMERICHOST;
206 gai_error = getaddrinfo(recursive_server, "53", &hints, &res);
207 if (gai_error != 0) {
208 fprintf(stderr, "getaddrinfo failed: %s\n",
209 gai_strerror(gai_error));
212 INSIST(res->ai_addrlen <= sizeof(sa_recursive.type));
213 memcpy(&sa_recursive.type, res->ai_addr, res->ai_addrlen);
215 sa_recursive.length = res->ai_addrlen;
216 ISC_LINK_INIT(&sa_recursive, link);
217 ISC_LIST_INIT(rec_servers);
218 ISC_LIST_APPEND(rec_servers, &sa_recursive, link);
219 result = dns_client_setservers(client, dns_rdataclass_in,
221 if (result != ISC_R_SUCCESS) {
222 fprintf(stderr, "set server failed: %d\n", result);
227 /* Construct zone name */
229 if (zonenamestr != NULL) {
230 namelen = strlen(zonenamestr);
231 isc_buffer_init(&b, zonenamestr, namelen);
232 isc_buffer_add(&b, namelen);
233 dns_fixedname_init(&zname0);
234 zname = dns_fixedname_name(&zname0);
235 result = dns_name_fromtext(zname, &b, dns_rootname, 0, NULL);
236 if (result != ISC_R_SUCCESS)
237 fprintf(stderr, "failed to convert zone name: %d\n",
241 /* Construct prerequisite name (if given) */
242 if (prereqstr != NULL) {
243 dns_fixedname_init(&pname0);
244 pname = dns_fixedname_name(&pname0);
245 evaluate_prereq(umctx, prereqstr, pname);
246 ISC_LIST_APPEND(prereqlist, pname, link);
247 prereqlistp = &prereqlist;
250 /* Construct update name */
251 ISC_LIST_INIT(updatelist);
252 dns_fixedname_init(&uname0);
253 uname = dns_fixedname_name(&uname0);
254 update_addordelete(umctx, argv[1], isdelete, uname);
255 ISC_LIST_APPEND(updatelist, uname, link);
257 /* Set up TSIG/SIG(0) key (if given) */
258 if (keyfilename != NULL)
259 setup_tsec(keyfilename, umctx);
262 result = dns_client_update(client,
263 default_rdataclass, /* XXX: fixed */
264 zname, prereqlistp, &updatelist,
265 (auth_server == NULL) ? NULL :
266 &auth_servers, tsec, 0);
267 if (result != ISC_R_SUCCESS) {
269 "update failed: %s\n", dns_result_totext(result));
271 fprintf(stderr, "update succeeded\n");
274 while ((pname = ISC_LIST_HEAD(prereqlist)) != NULL) {
275 while ((rdataset = ISC_LIST_HEAD(pname->list)) != NULL) {
276 ISC_LIST_UNLINK(pname->list, rdataset, link);
277 dns_rdataset_disassociate(rdataset);
278 isc_mem_put(umctx, rdataset, sizeof(*rdataset));
280 ISC_LIST_UNLINK(prereqlist, pname, link);
282 while ((uname = ISC_LIST_HEAD(updatelist)) != NULL) {
283 while ((rdataset = ISC_LIST_HEAD(uname->list)) != NULL) {
284 ISC_LIST_UNLINK(uname->list, rdataset, link);
285 dns_rdataset_disassociate(rdataset);
286 isc_mem_put(umctx, rdataset, sizeof(*rdataset));
288 ISC_LIST_UNLINK(updatelist, uname, link);
290 while ((rdatalist = ISC_LIST_HEAD(usedrdatalists)) != NULL) {
291 while ((rdata = ISC_LIST_HEAD(rdatalist->rdata)) != NULL) {
292 ISC_LIST_UNLINK(rdatalist->rdata, rdata, link);
293 isc_mem_put(umctx, rdata, sizeof(*rdata));
295 ISC_LIST_UNLINK(usedrdatalists, rdatalist, link);
296 isc_mem_put(umctx, rdatalist, sizeof(*rdatalist));
298 while ((buf = ISC_LIST_HEAD(usedbuffers)) != NULL) {
299 ISC_LIST_UNLINK(usedbuffers, buf, link);
300 isc_buffer_free(&buf);
303 dns_tsec_destroy(&tsec);
304 isc_mem_destroy(&umctx);
305 dns_client_destroy(&client);
312 * Subroutines borrowed from nsupdate.c
314 #define MAXWIRE (64 * 1024)
315 #define TTL_MAX 2147483647U /* Maximum signed 32 bit integer. */
318 nsu_strsep(char **stringp, const char *delim) {
319 char *string = *stringp;
327 for (; *string != '\0'; string++) {
329 for (d = delim; (dc = *d) != '\0'; d++) {
337 for (s = string; *s != '\0'; s++) {
339 for (d = delim; (dc = *d) != '\0'; d++) {
352 fatal(const char *format, ...) {
355 va_start(args, format);
356 vfprintf(stderr, format, args);
358 fprintf(stderr, "\n");
363 check_result(isc_result_t result, const char *msg) {
364 if (result != ISC_R_SUCCESS)
365 fatal("%s: %s", msg, isc_result_totext(result));
369 parse_name(char **cmdlinep, dns_name_t *name) {
374 word = nsu_strsep(cmdlinep, " \t\r\n");
375 if (word == NULL || *word == 0) {
376 fprintf(stderr, "could not read owner name\n");
380 isc_buffer_init(&source, word, strlen(word));
381 isc_buffer_add(&source, strlen(word));
382 result = dns_name_fromtext(name, &source, dns_rootname, 0, NULL);
383 check_result(result, "dns_name_fromtext");
384 isc_buffer_invalidate(&source);
388 parse_rdata(isc_mem_t *mctx, char **cmdlinep, dns_rdataclass_t rdataclass,
389 dns_rdatatype_t rdatatype, dns_rdata_t *rdata)
391 char *cmdline = *cmdlinep;
392 isc_buffer_t source, *buf = NULL, *newbuf = NULL;
394 isc_lex_t *lex = NULL;
395 dns_rdatacallbacks_t callbacks;
398 while (cmdline != NULL && *cmdline != 0 &&
399 isspace((unsigned char)*cmdline))
402 if (cmdline != NULL && *cmdline != 0) {
403 dns_rdatacallbacks_init(&callbacks);
404 result = isc_lex_create(mctx, strlen(cmdline), &lex);
405 check_result(result, "isc_lex_create");
406 isc_buffer_init(&source, cmdline, strlen(cmdline));
407 isc_buffer_add(&source, strlen(cmdline));
408 result = isc_lex_openbuffer(lex, &source);
409 check_result(result, "isc_lex_openbuffer");
410 result = isc_buffer_allocate(mctx, &buf, MAXWIRE);
411 check_result(result, "isc_buffer_allocate");
412 result = dns_rdata_fromtext(rdata, rdataclass, rdatatype, lex,
413 dns_rootname, 0, mctx, buf,
415 isc_lex_destroy(&lex);
416 if (result == ISC_R_SUCCESS) {
417 isc_buffer_usedregion(buf, &r);
418 result = isc_buffer_allocate(mctx, &newbuf, r.length);
419 check_result(result, "isc_buffer_allocate");
420 isc_buffer_putmem(newbuf, r.base, r.length);
421 isc_buffer_usedregion(newbuf, &r);
422 dns_rdata_reset(rdata);
423 dns_rdata_fromregion(rdata, rdataclass, rdatatype, &r);
424 isc_buffer_free(&buf);
425 ISC_LIST_APPEND(usedbuffers, newbuf, link);
427 fprintf(stderr, "invalid rdata format: %s\n",
428 isc_result_totext(result));
429 isc_buffer_free(&buf);
433 rdata->flags = DNS_RDATA_UPDATE;
439 update_addordelete(isc_mem_t *mctx, char *cmdline, isc_boolean_t isdelete,
445 dns_rdataclass_t rdataclass;
446 dns_rdatatype_t rdatatype;
447 dns_rdata_t *rdata = NULL;
448 dns_rdatalist_t *rdatalist = NULL;
449 dns_rdataset_t *rdataset = NULL;
450 isc_textregion_t region;
453 * Read the owner name.
455 parse_name(&cmdline, name);
457 rdata = isc_mem_get(mctx, sizeof(*rdata));
459 fprintf(stderr, "memory allocation for rdata failed\n");
462 dns_rdata_init(rdata);
465 * If this is an add, read the TTL and verify that it's in range.
466 * If it's a delete, ignore a TTL if present (for compatibility).
468 word = nsu_strsep(&cmdline, " \t\r\n");
469 if (word == NULL || *word == 0) {
471 fprintf(stderr, "could not read owner ttl\n");
476 rdataclass = dns_rdataclass_any;
477 rdatatype = dns_rdatatype_any;
478 rdata->flags = DNS_RDATA_UPDATE;
482 result = isc_parse_uint32(&ttl, word, 10);
483 if (result != ISC_R_SUCCESS) {
488 fprintf(stderr, "ttl '%s': %s\n", word,
489 isc_result_totext(result));
496 else if (ttl > TTL_MAX) {
497 fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n",
503 * Read the class or type.
505 word = nsu_strsep(&cmdline, " \t\r\n");
507 if (word == NULL || *word == 0) {
509 rdataclass = dns_rdataclass_any;
510 rdatatype = dns_rdatatype_any;
511 rdata->flags = DNS_RDATA_UPDATE;
514 fprintf(stderr, "could not read class or type\n");
519 region.length = strlen(word);
520 result = dns_rdataclass_fromtext(&rdataclass, ®ion);
521 if (result == ISC_R_SUCCESS) {
525 word = nsu_strsep(&cmdline, " \t\r\n");
526 if (word == NULL || *word == 0) {
528 rdataclass = dns_rdataclass_any;
529 rdatatype = dns_rdatatype_any;
530 rdata->flags = DNS_RDATA_UPDATE;
533 fprintf(stderr, "could not read type\n");
538 region.length = strlen(word);
539 result = dns_rdatatype_fromtext(&rdatatype, ®ion);
540 if (result != ISC_R_SUCCESS) {
541 fprintf(stderr, "'%s' is not a valid type: %s\n",
542 word, isc_result_totext(result));
546 rdataclass = default_rdataclass;
547 result = dns_rdatatype_fromtext(&rdatatype, ®ion);
548 if (result != ISC_R_SUCCESS) {
549 fprintf(stderr, "'%s' is not a valid class or type: "
550 "%s\n", word, isc_result_totext(result));
555 parse_rdata(mctx, &cmdline, rdataclass, rdatatype, rdata);
558 if ((rdata->flags & DNS_RDATA_UPDATE) != 0)
559 rdataclass = dns_rdataclass_any;
561 rdataclass = dns_rdataclass_none;
563 if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
564 fprintf(stderr, "could not read rdata\n");
571 rdatalist = isc_mem_get(mctx, sizeof(*rdatalist));
572 if (rdatalist == NULL) {
573 fprintf(stderr, "memory allocation for rdatalist failed\n");
576 dns_rdatalist_init(rdatalist);
577 rdatalist->type = rdatatype;
578 rdatalist->rdclass = rdataclass;
579 rdatalist->covers = rdatatype;
580 rdatalist->ttl = (dns_ttl_t)ttl;
581 ISC_LIST_INIT(rdatalist->rdata);
582 ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
583 ISC_LIST_APPEND(usedrdatalists, rdatalist, link);
585 rdataset = isc_mem_get(mctx, sizeof(*rdataset));
586 if (rdataset == NULL) {
587 fprintf(stderr, "memory allocation for rdataset failed\n");
590 dns_rdataset_init(rdataset);
591 dns_rdatalist_tordataset(rdatalist, rdataset);
592 ISC_LIST_INIT(name->list);
593 ISC_LIST_APPEND(name->list, rdataset, link);
597 make_prereq(isc_mem_t *mctx, char *cmdline, isc_boolean_t ispositive,
598 isc_boolean_t isrrset, dns_name_t *name)
602 isc_textregion_t region;
603 dns_rdataset_t *rdataset = NULL;
604 dns_rdatalist_t *rdatalist = NULL;
605 dns_rdataclass_t rdataclass;
606 dns_rdatatype_t rdatatype;
607 dns_rdata_t *rdata = NULL;
610 * Read the owner name
612 parse_name(&cmdline, name);
615 * If this is an rrset prereq, read the class or type.
618 word = nsu_strsep(&cmdline, " \t\r\n");
619 if (word == NULL || *word == 0) {
620 fprintf(stderr, "could not read class or type\n");
624 region.length = strlen(word);
625 result = dns_rdataclass_fromtext(&rdataclass, ®ion);
626 if (result == ISC_R_SUCCESS) {
630 word = nsu_strsep(&cmdline, " \t\r\n");
631 if (word == NULL || *word == 0) {
632 fprintf(stderr, "could not read type\n");
636 region.length = strlen(word);
637 result = dns_rdatatype_fromtext(&rdatatype, ®ion);
638 if (result != ISC_R_SUCCESS) {
639 fprintf(stderr, "invalid type: %s\n", word);
643 rdataclass = default_rdataclass;
644 result = dns_rdatatype_fromtext(&rdatatype, ®ion);
645 if (result != ISC_R_SUCCESS) {
646 fprintf(stderr, "invalid type: %s\n", word);
651 rdatatype = dns_rdatatype_any;
653 rdata = isc_mem_get(mctx, sizeof(*rdata));
655 fprintf(stderr, "memory allocation for rdata failed\n");
658 dns_rdata_init(rdata);
660 if (isrrset && ispositive)
661 parse_rdata(mctx, &cmdline, rdataclass, rdatatype, rdata);
663 rdata->flags = DNS_RDATA_UPDATE;
665 rdatalist = isc_mem_get(mctx, sizeof(*rdatalist));
666 if (rdatalist == NULL) {
667 fprintf(stderr, "memory allocation for rdatalist failed\n");
670 dns_rdatalist_init(rdatalist);
671 rdatalist->type = rdatatype;
673 if (isrrset && rdata->data != NULL)
674 rdatalist->rdclass = rdataclass;
676 rdatalist->rdclass = dns_rdataclass_any;
678 rdatalist->rdclass = dns_rdataclass_none;
679 rdatalist->covers = 0;
681 rdata->rdclass = rdatalist->rdclass;
682 rdata->type = rdatatype;
683 ISC_LIST_INIT(rdatalist->rdata);
684 ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
685 ISC_LIST_APPEND(usedrdatalists, rdatalist, link);
687 rdataset = isc_mem_get(mctx, sizeof(*rdataset));
688 if (rdataset == NULL) {
689 fprintf(stderr, "memory allocation for rdataset failed\n");
692 dns_rdataset_init(rdataset);
693 dns_rdatalist_tordataset(rdatalist, rdataset);
694 ISC_LIST_INIT(name->list);
695 ISC_LIST_APPEND(name->list, rdataset, link);
699 evaluate_prereq(isc_mem_t *mctx, char *cmdline, dns_name_t *name) {
701 isc_boolean_t ispositive, isrrset;
703 word = nsu_strsep(&cmdline, " \t\r\n");
704 if (word == NULL || *word == 0) {
705 fprintf(stderr, "could not read operation code\n");
708 if (strcasecmp(word, "nxdomain") == 0) {
709 ispositive = ISC_FALSE;
711 } else if (strcasecmp(word, "yxdomain") == 0) {
712 ispositive = ISC_TRUE;
714 } else if (strcasecmp(word, "nxrrset") == 0) {
715 ispositive = ISC_FALSE;
717 } else if (strcasecmp(word, "yxrrset") == 0) {
718 ispositive = ISC_TRUE;
721 fprintf(stderr, "incorrect operation code: %s\n", word);
725 make_prereq(mctx, cmdline, ispositive, isrrset, name);
729 setup_tsec(char *keyfile, isc_mem_t *mctx) {
730 dst_key_t *dstkey = NULL;
732 dns_tsectype_t tsectype;
734 result = dst_key_fromnamedfile(keyfile, NULL,
735 DST_TYPE_PRIVATE | DST_TYPE_KEY, mctx,
737 if (result != ISC_R_SUCCESS) {
738 fprintf(stderr, "could not read key from %s: %s\n",
739 keyfile, isc_result_totext(result));
743 if (dst_key_alg(dstkey) == DST_ALG_HMACMD5)
744 tsectype = dns_tsectype_tsig;
746 tsectype = dns_tsectype_sig0;
748 result = dns_tsec_create(mctx, tsectype, dstkey, &tsec);
749 dst_key_free(&dstkey);
750 if (result != ISC_R_SUCCESS) {
751 fprintf(stderr, "could not create tsec: %s\n",
752 isc_result_totext(result));