nsupdate.c revision 289ae548d52bc8f982d9823af64cafda7bd92232
/*
* Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 2000-2003 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: nsupdate.c,v 1.145 2006/12/04 01:52:45 marka Exp $ */
/*! \file */
#include <config.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <isc/commandline.h>
#include <isc/parseint.h>
#include <isc/sockaddr.h>
#include <dns/callbacks.h>
#include <dns/dispatch.h>
#include <dns/fixedname.h>
#include <dns/masterdump.h>
#include <dns/rdataclass.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/rdatatype.h>
#ifdef GSSAPI
#endif
#include <bind9/getaddresses.h>
#ifdef HAVE_ADDRINFO
#ifdef HAVE_GETADDRINFO
#ifdef HAVE_GAISTRERROR
#define USE_GETADDRINFO
#endif
#endif
#endif
#ifndef USE_GETADDRINFO
#ifndef ISC_PLATFORM_NONSTDHERRNO
extern int h_errno;
#endif
#endif
#define FIND_TIMEOUT 5
#define DNSDEFAULTPORT 53
#ifndef RESOLV_CONF
#define RESOLV_CONF "/etc/resolv.conf"
#endif
static dns_fixedname_t fuserzone;
static dns_name_t tmpzonename;
static dns_name_t restart_master;
static lwres_conf_t *lwconf;
static isc_sockaddr_t *servers;
static int ns_inuse = 0;
static int ns_total = 0;
static isc_sockaddr_t tempaddr;
static const dns_master_style_t *style;
static int requests = 0;
static unsigned int logdebuglevel = 0;
static unsigned int timeout = 300;
static unsigned int udp_timeout = 3;
static unsigned int udp_retries = 3;
typedef struct nsu_requestinfo {
static void
static void
static void
static void
#ifdef GSSAPI
static dns_fixedname_t fkname;
static char servicename[DNS_NAME_FORMATSIZE];
static dns_name_t *keyname;
typedef struct nsu_gssinfo {
static void
static void
static void
#endif /* GSSAPI */
#define STATUS_MORE (isc_uint16_t)0
static dns_rdataclass_t
getzoneclass(void) {
if (zoneclass == dns_rdataclass_none)
return (zoneclass);
}
static isc_boolean_t
if (zoneclass == dns_rdataclass_none ||
return (ISC_FALSE);
return (ISC_TRUE);
}
static void
exit(1);
}
static void
if (debugging) {
}
}
static void
if (ddebugging) {
}
}
static inline void
if (result != ISC_R_SUCCESS)
}
static void *
}
static void
}
static char *
char *s;
const char *d;
return (NULL);
break;
}
if (dc == 0)
break;
}
for (s = string; *s != '\0'; s++) {
sc = *s;
*s++ = '\0';
*stringp = s;
return (string);
}
}
}
return (string);
}
static void
reset_system(void) {
ddebug("reset_system()");
/* If the update message is still around, destroy it */
else {
&updatemsg);
}
if (usegsstsig) {
}
}
static isc_uint16_t
isc_uint16_t digestbits = 0;
char buf[20];
fatal("digest-bits out of range [0..128]");
fatal("digest-bits out of range [0..160]");
fatal("digest-bits out of range [0..224]");
fatal("digest-bits out of range [0..256]");
fatal("digest-bits out of range [0..384]");
fatal("digest-bits out of range [0..512]");
} else
return (digestbits);
}
static void
setup_keystr(void) {
int secretlen;
char *secretstr;
char *s, *n;
char *name;
isc_uint16_t digestbits = 0;
debug("Creating key...");
fatal("key option must specify [hmac:]keyname:secret");
secretstr = s + 1;
if (n != NULL) {
if (n == secretstr || n[1] == 0)
fatal("key option must specify [hmac:]keyname:secret");
secretstr = n + 1;
} else {
n = s;
}
debug("namefromtext");
fatal("out of memory");
if (result != ISC_R_SUCCESS) {
goto failure;
}
debug("keycreate");
if (result != ISC_R_SUCCESS)
else
}
static void
setup_keyfile(void) {
debug("Creating key...");
&dstkey);
if (result != ISC_R_SUCCESS) {
return;
}
switch (dst_key_alg(dstkey)) {
case DST_ALG_HMACMD5:
break;
case DST_ALG_HMACSHA1:
break;
case DST_ALG_HMACSHA224:
break;
case DST_ALG_HMACSHA256:
break;
case DST_ALG_HMACSHA384:
break;
case DST_ALG_HMACSHA512:
break;
}
&tsigkey);
if (result != ISC_R_SUCCESS) {
return;
}
} else
}
static void
doshutdown(void) {
if (userserver != NULL)
ddebug("Freeing TSIG key");
}
ddebug("Freeing SIG(0) key");
}
if (is_dst_up) {
ddebug("Destroy DST lib");
}
ddebug("Detach from entropy");
}
ddebug("Destroying request manager");
ddebug("Freeing the dispatchers");
if (have_ipv4)
if (have_ipv6)
ddebug("Shutting down dispatch manager");
}
static void
maybeshutdown(void) {
ddebug("Shutting down request manager");
if (requests != 0)
return;
doshutdown();
}
static void
ddebug("shutdown_program()");
}
static void
setup_system(void) {
int i;
ddebug("setup_system()");
result = isc_net_probeipv4();
if (result == ISC_R_SUCCESS)
result = isc_net_probeipv6();
if (result == ISC_R_SUCCESS)
fatal("could not find either IPv4 or IPv6");
if (lwresult != LWRES_R_SUCCESS)
fatal("lwres_context_create failed");
if (ns_total <= 0) {
/* No name servers in resolv.conf; default to loopback. */
ns_total = 1;
fatal("out of memory");
} else {
fatal("out of memory");
for (i = 0; i < ns_total; i++) {
} else {
}
}
}
if (have_ipv6) {
4, 2, 3, 5,
}
if (have_ipv4) {
4, 2, 3, 5,
}
setup_keystr();
}
static void
int count;
if (result != ISC_R_SUCCESS)
fatal("couldn't get address for '%s': %s",
}
static void
int ch;
isc_uint32_t i;
debug("parse_args");
) != -1)
{
switch (ch) {
case 'd':
break;
case 'D': /* was -dd */
break;
case 'M': /* was -dm */
break;
case 'l':
10);
if (result != ISC_R_SUCCESS) {
"'%s'\n", isc_commandline_argument);
exit(1);
}
logdebuglevel = i;
break;
case 'y':
break;
case 'v':
break;
case 'k':
break;
case 'g':
break;
case 'o':
break;
case 't':
isc_commandline_argument, 10);
if (result != ISC_R_SUCCESS) {
exit(1);
}
if (timeout == 0)
break;
case 'u':
isc_commandline_argument, 10);
if (result != ISC_R_SUCCESS) {
exit(1);
}
if (udp_timeout == 0)
break;
case 'r':
isc_commandline_argument, 10);
if (result != ISC_R_SUCCESS) {
exit(1);
}
break;
default:
"[-g | -o | -y keyname:secret | -k keyfile] "
"[-v] [filename]\n");
exit(1);
}
}
argv[0]);
exit(1);
}
#ifdef GSSAPI
argv[0]);
exit(1);
}
#else
if (usegsstsig) {
"program not linked with GSS API Library\n",
argv[0]);
exit(1);
}
#endif
} else {
"r", &input);
if (result != ISC_R_SUCCESS) {
exit(1);
}
}
}
}
static isc_uint16_t
char *word;
if (*word == 0) {
return (STATUS_SYNTAX);
}
return (STATUS_MORE);
}
static isc_uint16_t
{
isc_region_t r;
cmdline++;
if (*cmdline != 0) {
&callbacks);
if (result == ISC_R_SUCCESS) {
isc_buffer_usedregion(buf, &r);
isc_buffer_usedregion(newbuf, &r);
} else {
return (STATUS_SYNTAX);
}
} else {
}
return (STATUS_MORE);
}
static isc_uint16_t
char *word;
ddebug("make_prereq()");
/*
* Read the owner name
*/
if (retval != STATUS_MORE)
return (retval);
/*
* If this is an rrset prereq, read the class or type.
*/
if (isrrset) {
if (*word == 0) {
goto failure;
}
if (result == ISC_R_SUCCESS) {
if (!setzoneclass(rdataclass)) {
goto failure;
}
/*
* Now read the type.
*/
if (*word == 0) {
goto failure;
}
if (result != ISC_R_SUCCESS) {
goto failure;
}
} else {
rdataclass = getzoneclass();
if (result != ISC_R_SUCCESS) {
goto failure;
}
}
} else
if (isrrset && ispositive) {
if (retval != STATUS_MORE)
goto failure;
} else
if (ispositive) {
else
} else
return (STATUS_MORE);
return (STATUS_SYNTAX);
}
static isc_uint16_t
evaluate_prereq(char *cmdline) {
char *word;
ddebug("evaluate_prereq()");
if (*word == 0) {
return (STATUS_SYNTAX);
}
} else {
return (STATUS_SYNTAX);
}
}
static isc_uint16_t
evaluate_server(char *cmdline) {
long port;
if (*word == 0) {
return (STATUS_SYNTAX);
}
if (*word == 0)
else {
char *endp;
if (*endp != 0) {
return (STATUS_SYNTAX);
"(1 to 65535)\n", word);
return (STATUS_SYNTAX);
}
}
if (userserver == NULL) {
if (userserver == NULL)
fatal("out of memory");
}
return (STATUS_MORE);
}
static isc_uint16_t
evaluate_local(char *cmdline) {
long port;
if (*word == 0) {
return (STATUS_SYNTAX);
}
if (*word == 0)
port = 0;
else {
char *endp;
if (*endp != 0) {
return (STATUS_SYNTAX);
"(1 to 65535)\n", word);
return (STATUS_SYNTAX);
}
}
fatal("out of memory");
}
else {
return (STATUS_SYNTAX);
}
return (STATUS_MORE);
}
static isc_uint16_t
evaluate_key(char *cmdline) {
char *namestr;
char *secretstr;
isc_buffer_t b;
int secretlen;
isc_uint16_t digestbits = 0;
char *n;
if (*namestr == 0) {
return (STATUS_SYNTAX);
}
if (n != NULL) {
namestr = n + 1;
} else
if (result != ISC_R_SUCCESS) {
return (STATUS_SYNTAX);
}
if (*secretstr == 0) {
return (STATUS_SYNTAX);
}
fatal("out of memory");
if (result != ISC_R_SUCCESS) {
return (STATUS_SYNTAX);
}
&tsigkey);
if (result != ISC_R_SUCCESS) {
return (STATUS_SYNTAX);
}
return (STATUS_MORE);
}
static isc_uint16_t
evaluate_zone(char *cmdline) {
char *word;
isc_buffer_t b;
if (*word == 0) {
return (STATUS_SYNTAX);
}
NULL);
if (result != ISC_R_SUCCESS) {
return (STATUS_SYNTAX);
}
return (STATUS_MORE);
}
static isc_uint16_t
evaluate_class(char *cmdline) {
char *word;
if (*word == 0) {
return (STATUS_SYNTAX);
}
if (result != ISC_R_SUCCESS) {
return (STATUS_SYNTAX);
}
switch (rdclass) {
case dns_rdataclass_none:
case dns_rdataclass_any:
case dns_rdataclass_reserved0:
return (STATUS_SYNTAX);
default:
}
return (STATUS_MORE);
}
static isc_uint16_t
char *word;
ddebug("update_addordelete()");
/*
* Read the owner name.
*/
if (retval != STATUS_MORE)
return (retval);
/*
* 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).
*/
if (*word == 0) {
if (!isdelete) {
goto failure;
}
else {
ttl = 0;
goto doneparsing;
}
}
if (result != ISC_R_SUCCESS) {
if (isdelete) {
ttl = 0;
goto parseclass;
} else {
goto failure;
}
}
if (isdelete)
ttl = 0;
goto failure;
}
/*
* Read the class or type.
*/
if (*word == 0) {
if (isdelete) {
goto doneparsing;
} else {
goto failure;
}
}
if (result == ISC_R_SUCCESS) {
if (!setzoneclass(rdataclass)) {
goto failure;
}
/*
* Now read the type.
*/
if (*word == 0) {
if (isdelete) {
goto doneparsing;
} else {
goto failure;
}
}
if (result != ISC_R_SUCCESS) {
goto failure;
}
} else {
rdataclass = getzoneclass();
if (result != ISC_R_SUCCESS) {
goto failure;
}
}
rdata);
if (retval != STATUS_MORE)
goto failure;
if (isdelete) {
else
} else {
goto failure;
}
}
return (STATUS_MORE);
return (STATUS_SYNTAX);
}
static isc_uint16_t
evaluate_update(char *cmdline) {
char *word;
ddebug("evaluate_update()");
if (*word == 0) {
return (STATUS_SYNTAX);
}
else {
return (STATUS_SYNTAX);
}
}
static void
if (result == ISC_R_SUCCESS) {
}
}
}
}
static void
int bufsz;
ddebug("show_message()");
do {
"buffer to display message\n");
exit(1);
}
bufsz *= 2;
} while (result == ISC_R_NOSPACE);
if (result != ISC_R_SUCCESS) {
return;
}
}
static isc_uint16_t
get_next_command(void) {
char cmdlinebuf[MAXCMD];
char *cmdline;
char *word;
ddebug("get_next_command()");
if (interactive) {
}
return (STATUS_QUIT);
return (STATUS_QUIT);
if (*word == 0)
return (STATUS_SEND);
if (word[0] == ';')
return (STATUS_MORE);
return (STATUS_QUIT);
return (evaluate_prereq(cmdline));
return (evaluate_update(cmdline));
return (evaluate_server(cmdline));
return (evaluate_local(cmdline));
return (evaluate_zone(cmdline));
return (evaluate_class(cmdline));
return (STATUS_SEND);
return (STATUS_MORE);
}
return (STATUS_MORE);
}
return (evaluate_key(cmdline));
#ifdef GSSAPI
#else
#endif
return (STATUS_MORE);
}
#ifdef GSSAPI
#else
#endif
return (STATUS_MORE);
}
return (STATUS_SYNTAX);
}
static isc_boolean_t
user_interaction(void) {
ddebug("user_interaction()");
result = get_next_command();
if (result == STATUS_SEND)
return (ISC_TRUE);
return (ISC_FALSE);
}
static void
done_update(void) {
ddebug("done_update()");
}
static void
if (isc_buffer_remaininglength(b) < 1)
if (isc_buffer_remaininglength(b) < 1)
}
}
static void
ddebug("update_completed()");
requests--;
if (shuttingdown) {
return;
}
goto done;
}
switch (result) {
case ISC_R_SUCCESS:
if (answer->verify_attempted)
ddebug("tsig verification successful");
break;
case DNS_R_CLOCKSKEW:
case DNS_R_EXPECTEDTSIG:
case DNS_R_TSIGERRORSET:
case DNS_R_TSIGVERIFYFAILURE:
case DNS_R_UNEXPECTEDTSIG:
case ISC_R_FAILURE:
#if 0
/*
* For MS DNS that violates RFC 2845, section 4.2
*/
break;
}
#endif
break;
default:
}
if (!debugging) {
char buf[64];
isc_buffer_t b;
check_tsig_error(rds, &b);
(int)isc_buffer_usedlength(&b), buf);
}
}
if (debugging)
done:
if (usegsstsig) {
}
done_update();
}
static void
{
unsigned int options = 0;
ddebug("send_update()");
if (usevc)
}
if (debugging) {
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
}
if (debugging)
requests++;
}
static void
int pass = 0;
unsigned int nlabels;
ddebug("recvsoa()");
requests--;
if (shuttingdown) {
return;
}
if (eresult != ISC_R_SUCCESS) {
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
if (userserver != NULL)
fatal("could not talk to specified name server");
fatal("could not talk to any default name server");
return;
}
ddebug("About to create rcvmsg");
fatal("out of memory");
ddebug("retrying soa request without TSIG");
FIND_TIMEOUT * 20,
FIND_TIMEOUT, 3,
&request);
requests++;
return;
}
if (debugging)
fatal("response to SOA query was unsuccessful");
if (pass == 0)
else if (pass == 1)
else
goto droplabel;
if (result != ISC_R_SUCCESS) {
pass++;
goto lookforsoa;
}
while (result == ISC_R_SUCCESS) {
&soaset);
if (result == ISC_R_SUCCESS)
break;
if (section == DNS_SECTION_ANSWER) {
&tset) == ISC_R_SUCCESS ||
&tset) == ISC_R_SUCCESS ) {
break;
}
}
}
pass++;
goto lookforsoa;
}
if (seencname)
goto droplabel;
if (debugging) {
char namestr[DNS_NAME_FORMATSIZE];
}
else
if (debugging) {
char namestr[DNS_NAME_FORMATSIZE];
}
if (userserver != NULL)
else {
serveraddr = &tempaddr;
}
#ifdef GSSAPI
if (usegsstsig) {
} else {
}
#else
#endif
out:
ddebug("Out of recvsoa");
return;
if (nlabels == 1)
fatal("could not find enclosing zone");
if (userserver != NULL)
else
goto out;
}
static void
{
fatal("out of memory");
requests++;
}
#ifdef GSSAPI
static void
{
isc_uint32_t val = 0;
char namestr[DNS_NAME_FORMATSIZE];
char keystr[DNS_NAME_FORMATSIZE];
debug("start_gssrequest");
fatal("out of memory");
}
if (userserver == NULL)
else
/* Windows doesn't recognize name compression in the key name. */
/* Build first request. */
if (result == ISC_R_FAILURE)
fatal("Check your Kerberos ticket, it may have expired.");
}
static void
{
unsigned int options = 0;
debug("send_gssrequest");
fatal("out of memory");
if (debugging)
requests++;
}
static void
ddebug("recvgss()");
requests--;
if (shuttingdown) {
return;
}
if (eresult != ISC_R_SUCCESS) {
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
if (userserver != NULL)
fatal("could not talk to specified name server");
fatal("could not talk to any default name server");
&request);
return;
}
ddebug("recvgss creating rcvmsg");
if (debugging)
"recvmsg reply from GSS-TSIG query");
ddebug("recvgss trying %s GSS-TSIG",
if (use_win2k_gsstsig)
else
goto done;
}
fatal("response to GSS-TSIG query was unsuccessful");
switch (result) {
case DNS_R_CONTINUE:
context);
break;
case ISC_R_SUCCESS:
/*
* XXXSRA Waaay too much fun here. There's no good
* reason why we need a TSIG here (the people who put
* it into the spec admitted at the time that it was
* not a security issue), and Windows clients don't
* seem to work if named complies with the spec and
* includes the gratuitous TSIG. So we're in the
* bizzare situation of having to choose between
* complying with a useless requirement in the spec
* and interoperating. This is nuts. If we can
* confirm this behavior, we should ask the WG to
* consider removing the requirement for the
* gratuitous TSIG here. For the moment, we ignore
* the TSIG -- this too is a spec violation, but it's
* the least insane thing to do.
*/
#if 0
/*
* Verify the signature.
*/
#endif /* 0 */
break;
default:
}
done:
ddebug("Out of recvgss");
}
#endif
static void
start_update(void) {
ddebug("start_update()");
if (result == ISC_R_NOMORE) {
}
if (result != ISC_R_SUCCESS) {
done_update();
return;
}
return;
}
&soaquery);
if (userserver != NULL)
else {
ns_inuse = 0;
}
}
static void
cleanup(void) {
ddebug("cleanup()");
#ifdef GSSAPI
if (usegsstsig) {
}
ddebug("Destroying GSS-TSIG keyring");
}
#endif
ddebug("Shutting down task manager");
ddebug("Destroying event");
ddebug("Shutting down socket manager");
ddebug("Shutting down timer manager");
ddebug("Destroying hash context");
ddebug("Destroying name state");
ddebug("Removing log context");
ddebug("Destroying memory context");
if (memdebugging)
}
static void
if (shuttingdown) {
return;
}
if (global_event == NULL)
reset_system();
more = user_interaction();
if (!more) {
return;
}
start_update();
return;
}
int
setup_system();
(void)isc_app_run();
cleanup();
if (seenerror)
return (2);
else
return (0);
}