Files
bind9/lib/samples/sample-update.c
T

820 lines
20 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#ifndef WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#endif /* ifndef WIN32 */
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <isc/buffer.h>
#include <isc/commandline.h>
#include <isc/lex.h>
#include <isc/lib.h>
#include <isc/mem.h>
#include <isc/parseint.h>
#include <isc/print.h>
#include <isc/sockaddr.h>
#include <isc/string.h>
#include <isc/util.h>
#include <dns/callbacks.h>
#include <dns/client.h>
#include <dns/fixedname.h>
#include <dns/lib.h>
#include <dns/name.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/rdatatype.h>
#include <dns/result.h>
#include <dns/secalg.h>
#include <dns/tsec.h>
#include <dst/dst.h>
static dns_tsec_t*tsec = NULL;
static const dns_rdataclass_t default_rdataclass = dns_rdataclass_in;
static isc_bufferlist_t usedbuffers;
static ISC_LIST(dns_rdatalist_t) usedrdatalists;
static const char*port = "53";
static void setup_tsec(char*keyfile,isc_mem_t*mctx);
static void update_addordelete(isc_mem_t*mctx,
char*cmdline,
bool isdelete,
dns_name_t*name);
static void evaluate_prereq(isc_mem_t*mctx,char*cmdline,dns_name_t*name);
ISC_PLATFORM_NORETURN_PRE static void
usage(void) ISC_PLATFORM_NORETURN_POST;
static void
usage(void) {
fprintf(stderr,"sample-update "
"-s "
"[-a auth_server] "
"[-k keyfile] "
"[-p prerequisite] "
"[-r recursive_server] "
"[-z zonename] "
"(add|delete) \"name TTL RRtype RDATA\"\n");
exit(1);
}
#ifdef _WIN32
static void
InitSockets(void) {
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2,0);
err = WSAStartup(wVersionRequested,&wsaData);
if (err != 0) {
fprintf(stderr,"WSAStartup() failed: %d\n",err);
exit(1);
}
}
static void
DestroySockets(void) {
WSACleanup();
}
#else /* ifdef _WIN32 */
#define InitSockets() ((void)0)
#define DestroySockets() ((void)0)
#endif /* ifdef _WIN32 */
static bool
addserver(const char*server,isc_sockaddrlist_t*list,isc_sockaddr_t*sockaddr)
{
struct addrinfo hints,*res;
int gaierror;
memset(&hints,0,sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
#ifdef AI_NUMERICHOST
hints.ai_flags |= AI_NUMERICHOST;
#endif /* ifdef AI_NUMERICHOST */
#ifdef AI_NUMERICSERV
hints.ai_flags |= AI_NUMERICSERV;
#endif /* ifdef AI_NUMERICSERV */
InitSockets();
gaierror = getaddrinfo(server,port,&hints,&res);
if (gaierror != 0) {
fprintf(stderr,"getaddrinfo(%s) failed: %s\n",
server,gai_strerror(gaierror));
DestroySockets();
return (false);
}
INSIST(res->ai_addrlen <= sizeof(sockaddr->type));
memmove(&sockaddr->type,res->ai_addr,res->ai_addrlen);
sockaddr->length = (unsigned int)res->ai_addrlen;
ISC_LINK_INIT(sockaddr,link);
ISC_LIST_APPEND(*list,sockaddr,link);
freeaddrinfo(res);
DestroySockets();
return (true);
}
int
main(int argc,char*argv[]) {
int ch;
dns_client_t*client = NULL;
char*zonenamestr = NULL;
char*keyfilename = NULL;
char*prereqstr = NULL;
isc_sockaddr_t sa_auth[10],sa_recursive[10];
unsigned int nsa_auth = 0,nsa_recursive = 0;
isc_sockaddrlist_t rec_servers;
isc_sockaddrlist_t auth_servers,*auth_serversp = &auth_servers;
isc_result_t result;
bool isdelete;
isc_buffer_t b,*buf;
dns_fixedname_t zname0,pname0,uname0;
unsigned int namelen;
dns_name_t*zname = NULL,*uname,*pname;
dns_rdataset_t*rdataset;
dns_rdatalist_t*rdatalist;
dns_rdata_t*rdata;
dns_namelist_t updatelist,prereqlist,*prereqlistp = NULL;
isc_mem_t*umctx = NULL;
bool sendtwice = false;
ISC_LIST_INIT(auth_servers);
ISC_LIST_INIT(rec_servers);
while ((ch = isc_commandline_parse(argc,argv,
"a:k:p:P:r:sz:")) != EOF)
{
switch (ch) {
case 'k':
keyfilename = isc_commandline_argument;
break;
case 'a':
if (nsa_auth < sizeof(sa_auth) / sizeof(*sa_auth) &&
addserver(isc_commandline_argument,&auth_servers,
&sa_auth[nsa_auth])) {
nsa_auth++;
}
break;
case 'p':
prereqstr = isc_commandline_argument;
break;
case 'P':
port = isc_commandline_argument;
break;
case 'r':
if (nsa_recursive <
sizeof(sa_recursive) / sizeof(*sa_recursive) &&
addserver(isc_commandline_argument,&rec_servers,
&sa_recursive[nsa_recursive])) {
nsa_recursive++;
}
break;
case 's':
sendtwice = true;
break;
case 'z':
zonenamestr = isc_commandline_argument;
break;
default:
usage();
}
}
argc -= isc_commandline_index;
argv += isc_commandline_index;
if (argc < 2) {
usage();
}
/* command line argument validation */
if (strcmp(argv[0],"delete") == 0) {
isdelete = true;
} else if (strcmp(argv[0],"add") == 0) {
isdelete = false;
} else {
fprintf(stderr,"invalid update command: %s\n",argv[0]);
exit(1);
}
if (ISC_LIST_HEAD(auth_servers) == NULL &&
ISC_LIST_HEAD(rec_servers) == NULL) {
fprintf(stderr,"authoritative or recursive servers "
"must be specified\n");
usage();
}
/* Initialization */
ISC_LIST_INIT(usedbuffers);
ISC_LIST_INIT(usedrdatalists);
ISC_LIST_INIT(prereqlist);
isc_lib_register();
result = dns_lib_init();
if (result != ISC_R_SUCCESS) {
fprintf(stderr,"dns_lib_init failed: %u\n",result);
exit(1);
}
result = isc_mem_create(0,0,&umctx);
if (result != ISC_R_SUCCESS) {
fprintf(stderr,"failed to crate mctx\n");
exit(1);
}
result = dns_client_create(&client,0);
if (result != ISC_R_SUCCESS) {
fprintf(stderr,"dns_client_create failed: %u\n",result);
exit(1);
}
/* Construct zone name */
zname = NULL;
if (zonenamestr != NULL) {
namelen = strlen(zonenamestr);
isc_buffer_init(&b,zonenamestr,namelen);
isc_buffer_add(&b,namelen);
zname = dns_fixedname_initname(&zname0);
result = dns_name_fromtext(zname,&b,dns_rootname,0,NULL);
if (result != ISC_R_SUCCESS) {
fprintf(stderr,"failed to convert zone name: %u\n",
result);
}
}
/* Construct prerequisite name (if given) */
if (prereqstr != NULL) {
pname = dns_fixedname_initname(&pname0);
evaluate_prereq(umctx,prereqstr,pname);
ISC_LIST_APPEND(prereqlist,pname,link);
prereqlistp = &prereqlist;
}
/* Construct update name */
ISC_LIST_INIT(updatelist);
uname = dns_fixedname_initname(&uname0);
update_addordelete(umctx,argv[1],isdelete,uname);
ISC_LIST_APPEND(updatelist,uname,link);
/* Set up TSIG/SIG(0) key (if given) */
if (keyfilename != NULL) {
setup_tsec(keyfilename,umctx);
}
if (ISC_LIST_HEAD(auth_servers) == NULL) {
auth_serversp = NULL;
}
/* Perform update */
result = dns_client_update(client,
default_rdataclass, /* XXX: fixed */
zname,prereqlistp,&updatelist,
auth_serversp,tsec,0);
if (result != ISC_R_SUCCESS) {
fprintf(stderr,
"update failed: %s\n",dns_result_totext(result));
} else {
fprintf(stderr,"update succeeded\n");
}
if (sendtwice) {
/* Perform 2nd update */
result = dns_client_update(client,
default_rdataclass, /* XXX: fixed */
zname,prereqlistp,&updatelist,
auth_serversp,tsec,0);
if (result != ISC_R_SUCCESS) {
fprintf(stderr,"2nd update failed: %s\n",
dns_result_totext(result));
} else {
fprintf(stderr,"2nd update succeeded\n");
}
}
/* Cleanup */
while ((pname = ISC_LIST_HEAD(prereqlist)) != NULL) {
while ((rdataset = ISC_LIST_HEAD(pname->list)) != NULL) {
ISC_LIST_UNLINK(pname->list,rdataset,link);
dns_rdataset_disassociate(rdataset);
isc_mem_put(umctx,rdataset,sizeof(*rdataset));
}
ISC_LIST_UNLINK(prereqlist,pname,link);
}
while ((uname = ISC_LIST_HEAD(updatelist)) != NULL) {
while ((rdataset = ISC_LIST_HEAD(uname->list)) != NULL) {
ISC_LIST_UNLINK(uname->list,rdataset,link);
dns_rdataset_disassociate(rdataset);
isc_mem_put(umctx,rdataset,sizeof(*rdataset));
}
ISC_LIST_UNLINK(updatelist,uname,link);
}
while ((rdatalist = ISC_LIST_HEAD(usedrdatalists)) != NULL) {
while ((rdata = ISC_LIST_HEAD(rdatalist->rdata)) != NULL) {
ISC_LIST_UNLINK(rdatalist->rdata,rdata,link);
isc_mem_put(umctx,rdata,sizeof(*rdata));
}
ISC_LIST_UNLINK(usedrdatalists,rdatalist,link);
isc_mem_put(umctx,rdatalist,sizeof(*rdatalist));
}
while ((buf = ISC_LIST_HEAD(usedbuffers)) != NULL) {
ISC_LIST_UNLINK(usedbuffers,buf,link);
isc_buffer_free(&buf);
}
if (tsec != NULL) {
dns_tsec_destroy(&tsec);
}
isc_mem_destroy(&umctx);
dns_client_destroy(&client);
dns_lib_shutdown();
return (0);
}
/*
* Subroutines borrowed from nsupdate.c
*/
#define MAXWIRE (64* 1024)
#define TTL_MAX 2147483647U /* Maximum signed 32 bit integer. */
static char*
nsu_strsep(char**stringp,const char*delim) {
char*string = *stringp;
char*s;
const char*d;
char sc,dc;
if (string == NULL) {
return (NULL);
}
for (; *string != '\0'; string++) {
sc = *string;
for (d = delim; (dc = *d) != '\0'; d++) {
if (sc == dc) {
break;
}
}
if (dc == 0) {
break;
}
}
for (s = string; *s != '\0'; s++) {
sc = *s;
for (d = delim; (dc = *d) != '\0'; d++) {
if (sc == dc) {
*s++ = '\0';
*stringp = s;
return (string);
}
}
}
*stringp = NULL;
return (string);
}
static void
fatal(const char*format,...) {
va_list args;
va_start(args,format);
vfprintf(stderr,format,args);
va_end(args);
fprintf(stderr,"\n");
exit(1);
}
static inline void
check_result(isc_result_t result,const char*msg) {
if (result != ISC_R_SUCCESS) {
fatal("%s: %s",msg,isc_result_totext(result));
}
}
static void
parse_name(char**cmdlinep,dns_name_t*name) {
isc_result_t result;
char*word;
isc_buffer_t source;
word = nsu_strsep(cmdlinep," \t\r\n");
if (word == NULL || *word == 0) {
fprintf(stderr,"could not read owner name\n");
exit(1);
}
isc_buffer_init(&source,word,strlen(word));
isc_buffer_add(&source,strlen(word));
result = dns_name_fromtext(name,&source,dns_rootname,0,NULL);
check_result(result,"dns_name_fromtext");
isc_buffer_invalidate(&source);
}
static void
parse_rdata(isc_mem_t*mctx,
char**cmdlinep,
dns_rdataclass_t rdataclass,
dns_rdatatype_t rdatatype,
dns_rdata_t*rdata)
{
char*cmdline = *cmdlinep;
isc_buffer_t source,*buf = NULL,*newbuf = NULL;
isc_region_t r;
isc_lex_t*lex = NULL;
dns_rdatacallbacks_t callbacks;
isc_result_t result;
while (cmdline != NULL && *cmdline != 0 &&
isspace((unsigned char)*cmdline))
{
cmdline++;
}
if (cmdline != NULL && *cmdline != 0) {
dns_rdatacallbacks_init(&callbacks);
result = isc_lex_create(mctx,strlen(cmdline),&lex);
check_result(result,"isc_lex_create");
isc_buffer_init(&source,cmdline,strlen(cmdline));
isc_buffer_add(&source,strlen(cmdline));
result = isc_lex_openbuffer(lex,&source);
check_result(result,"isc_lex_openbuffer");
result = isc_buffer_allocate(mctx,&buf,MAXWIRE);
check_result(result,"isc_buffer_allocate");
result = dns_rdata_fromtext(rdata,rdataclass,rdatatype,lex,
dns_rootname,0,mctx,buf,
&callbacks);
isc_lex_destroy(&lex);
if (result == ISC_R_SUCCESS) {
isc_buffer_usedregion(buf,&r);
result = isc_buffer_allocate(mctx,&newbuf,r.length);
check_result(result,"isc_buffer_allocate");
isc_buffer_putmem(newbuf,r.base,r.length);
isc_buffer_usedregion(newbuf,&r);
dns_rdata_reset(rdata);
dns_rdata_fromregion(rdata,rdataclass,rdatatype,&r);
isc_buffer_free(&buf);
ISC_LIST_APPEND(usedbuffers,newbuf,link);
} else {
fprintf(stderr,"invalid rdata format: %s\n",
isc_result_totext(result));
isc_buffer_free(&buf);
exit(1);
}
} else {
rdata->flags = DNS_RDATA_UPDATE;
}
*cmdlinep = cmdline;
}
static void
update_addordelete(isc_mem_t*mctx,char*cmdline,bool isdelete,dns_name_t*name)
{
isc_result_t result;
uint32_t ttl;
char*word;
dns_rdataclass_t rdataclass;
dns_rdatatype_t rdatatype;
dns_rdata_t*rdata = NULL;
dns_rdatalist_t*rdatalist = NULL;
dns_rdataset_t*rdataset = NULL;
isc_textregion_t region;
/*
* Read the owner name.
*/
parse_name(&cmdline,name);
rdata = isc_mem_get(mctx,sizeof(*rdata));
if (rdata == NULL) {
fprintf(stderr,"memory allocation for rdata failed\n");
exit(1);
}
dns_rdata_init(rdata);
/*
* If this is an add, read the TTL and verify that it's in range.
* If it's a delete, ignore a TTL if present (for compatibility).
*/
word = nsu_strsep(&cmdline," \t\r\n");
if (word == NULL || *word == 0) {
if (!isdelete) {
fprintf(stderr,"could not read owner ttl\n");
exit(1);
} else {
ttl = 0;
rdataclass = dns_rdataclass_any;
rdatatype = dns_rdatatype_any;
rdata->flags = DNS_RDATA_UPDATE;
goto doneparsing;
}
}
result = isc_parse_uint32(&ttl,word,10);
if (result != ISC_R_SUCCESS) {
if (isdelete) {
ttl = 0;
goto parseclass;
} else {
fprintf(stderr,"ttl '%s': %s\n",word,
isc_result_totext(result));
exit(1);
}
}
if (isdelete) {
ttl = 0;
} else if (ttl > TTL_MAX) {
fprintf(stderr,"ttl '%s' is out of range (0 to %u)\n",
word,TTL_MAX);
exit(1);
}
/*
* Read the class or type.
*/
word = nsu_strsep(&cmdline," \t\r\n");
parseclass:
if (word == NULL || *word == 0) {
if (isdelete) {
rdataclass = dns_rdataclass_any;
rdatatype = dns_rdatatype_any;
rdata->flags = DNS_RDATA_UPDATE;
goto doneparsing;
} else {
fprintf(stderr,"could not read class or type\n");
exit(1);
}
}
region.base = word;
region.length = strlen(word);
result = dns_rdataclass_fromtext(&rdataclass,&region);
if (result == ISC_R_SUCCESS) {
/*
* Now read the type.
*/
word = nsu_strsep(&cmdline," \t\r\n");
if (word == NULL || *word == 0) {
if (isdelete) {
rdataclass = dns_rdataclass_any;
rdatatype = dns_rdatatype_any;
rdata->flags = DNS_RDATA_UPDATE;
goto doneparsing;
} else {
fprintf(stderr,"could not read type\n");
exit(1);
}
}
region.base = word;
region.length = strlen(word);
result = dns_rdatatype_fromtext(&rdatatype,&region);
if (result != ISC_R_SUCCESS) {
fprintf(stderr,"'%s' is not a valid type: %s\n",
word,isc_result_totext(result));
exit(1);
}
} else {
rdataclass = default_rdataclass;
result = dns_rdatatype_fromtext(&rdatatype,&region);
if (result != ISC_R_SUCCESS) {
fprintf(stderr,"'%s' is not a valid class or type: "
"%s\n",word,isc_result_totext(result));
exit(1);
}
}
parse_rdata(mctx,&cmdline,rdataclass,rdatatype,rdata);
if (isdelete) {
if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
rdataclass = dns_rdataclass_any;
} else {
rdataclass = dns_rdataclass_none;
}
} else {
if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
fprintf(stderr,"could not read rdata\n");
exit(1);
}
}
doneparsing:
rdatalist = isc_mem_get(mctx,sizeof(*rdatalist));
if (rdatalist == NULL) {
fprintf(stderr,"memory allocation for rdatalist failed\n");
exit(1);
}
dns_rdatalist_init(rdatalist);
rdatalist->type = rdatatype;
rdatalist->rdclass = rdataclass;
rdatalist->covers = rdatatype;
rdatalist->ttl = (dns_ttl_t)ttl;
ISC_LIST_APPEND(rdatalist->rdata,rdata,link);
ISC_LIST_APPEND(usedrdatalists,rdatalist,link);
rdataset = isc_mem_get(mctx,sizeof(*rdataset));
if (rdataset == NULL) {
fprintf(stderr,"memory allocation for rdataset failed\n");
exit(1);
}
dns_rdataset_init(rdataset);
dns_rdatalist_tordataset(rdatalist,rdataset);
dns_rdataset_setownercase(rdataset,name);
ISC_LIST_INIT(name->list);
ISC_LIST_APPEND(name->list,rdataset,link);
}
static void
make_prereq(isc_mem_t*mctx,
char*cmdline,
bool ispositive,
bool isrrset,
dns_name_t*name)
{
isc_result_t result;
char*word;
isc_textregion_t region;
dns_rdataset_t*rdataset = NULL;
dns_rdatalist_t*rdatalist = NULL;
dns_rdataclass_t rdataclass;
dns_rdatatype_t rdatatype;
dns_rdata_t*rdata = NULL;
/*
* Read the owner name
*/
parse_name(&cmdline,name);
/*
* If this is an rrset prereq, read the class or type.
*/
if (isrrset) {
word = nsu_strsep(&cmdline," \t\r\n");
if (word == NULL || *word == 0) {
fprintf(stderr,"could not read class or type\n");
exit(1);
}
region.base = word;
region.length = strlen(word);
result = dns_rdataclass_fromtext(&rdataclass,&region);
if (result == ISC_R_SUCCESS) {
/*
* Now read the type.
*/
word = nsu_strsep(&cmdline," \t\r\n");
if (word == NULL || *word == 0) {
fprintf(stderr,"could not read type\n");
exit(1);
}
region.base = word;
region.length = strlen(word);
result = dns_rdatatype_fromtext(&rdatatype,&region);
if (result != ISC_R_SUCCESS) {
fprintf(stderr,"invalid type: %s\n",word);
exit(1);
}
} else {
rdataclass = default_rdataclass;
result = dns_rdatatype_fromtext(&rdatatype,&region);
if (result != ISC_R_SUCCESS) {
fprintf(stderr,"invalid type: %s\n",word);
exit(1);
}
}
} else {
rdatatype = dns_rdatatype_any;
}
rdata = isc_mem_get(mctx,sizeof(*rdata));
if (rdata == NULL) {
fprintf(stderr,"memory allocation for rdata failed\n");
exit(1);
}
dns_rdata_init(rdata);
if (isrrset && ispositive) {
parse_rdata(mctx,&cmdline,rdataclass,rdatatype,rdata);
} else {
rdata->flags = DNS_RDATA_UPDATE;
}
rdatalist = isc_mem_get(mctx,sizeof(*rdatalist));
if (rdatalist == NULL) {
fprintf(stderr,"memory allocation for rdatalist failed\n");
exit(1);
}
dns_rdatalist_init(rdatalist);
rdatalist->type = rdatatype;
if (ispositive) {
if (isrrset && rdata->data != NULL) {
rdatalist->rdclass = rdataclass;
} else {
rdatalist->rdclass = dns_rdataclass_any;
}
} else {
rdatalist->rdclass = dns_rdataclass_none;
}
rdata->rdclass = rdatalist->rdclass;
rdata->type = rdatatype;
ISC_LIST_APPEND(rdatalist->rdata,rdata,link);
ISC_LIST_APPEND(usedrdatalists,rdatalist,link);
rdataset = isc_mem_get(mctx,sizeof(*rdataset));
if (rdataset == NULL) {
fprintf(stderr,"memory allocation for rdataset failed\n");
exit(1);
}
dns_rdataset_init(rdataset);
dns_rdatalist_tordataset(rdatalist,rdataset);
dns_rdataset_setownercase(rdataset,name);
ISC_LIST_INIT(name->list);
ISC_LIST_APPEND(name->list,rdataset,link);
}
static void
evaluate_prereq(isc_mem_t*mctx,char*cmdline,dns_name_t*name) {
char*word;
bool ispositive,isrrset;
word = nsu_strsep(&cmdline," \t\r\n");
if (word == NULL || *word == 0) {
fprintf(stderr,"could not read operation code\n");
exit(1);
}
if (strcasecmp(word,"nxdomain") == 0) {
ispositive = false;
isrrset = false;
} else if (strcasecmp(word,"yxdomain") == 0) {
ispositive = true;
isrrset = false;
} else if (strcasecmp(word,"nxrrset") == 0) {
ispositive = false;
isrrset = true;
} else if (strcasecmp(word,"yxrrset") == 0) {
ispositive = true;
isrrset = true;
} else {
fprintf(stderr,"incorrect operation code: %s\n",word);
exit(1);
}
make_prereq(mctx,cmdline,ispositive,isrrset,name);
}
static void
setup_tsec(char*keyfile,isc_mem_t*mctx) {
dst_key_t*dstkey = NULL;
isc_result_t result;
dns_tsectype_t tsectype;
result = dst_key_fromnamedfile(keyfile,NULL,
DST_TYPE_PRIVATE | DST_TYPE_KEY,mctx,
&dstkey);
if (result != ISC_R_SUCCESS) {
fprintf(stderr,"could not read key from %s: %s\n",
keyfile,isc_result_totext(result));
exit(1);
}
if (dst_key_alg(dstkey) == DST_ALG_HMACMD5) {
tsectype = dns_tsectype_tsig;
} else {
tsectype = dns_tsectype_sig0;
}
result = dns_tsec_create(mctx,tsectype,dstkey,&tsec);
dst_key_free(&dstkey);
if (result != ISC_R_SUCCESS) {
fprintf(stderr,"could not create tsec: %s\n",
isc_result_totext(result));
exit(1);
}
}