diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 18:37:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 18:37:14 +0000 |
commit | ea648e70a989cca190cd7403fe892fd2dcc290b4 (patch) | |
tree | e2b6b1c647da68b0d4d66082835e256eb30970e8 /bin/nsupdate | |
parent | Initial commit. (diff) | |
download | bind9-upstream.tar.xz bind9-upstream.zip |
Adding upstream version 1:9.11.5.P4+dfsg.upstream/1%9.11.5.P4+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bin/nsupdate')
-rw-r--r-- | bin/nsupdate/Makefile.in | 95 | ||||
-rw-r--r-- | bin/nsupdate/nsupdate.1 | 524 | ||||
-rw-r--r-- | bin/nsupdate/nsupdate.c | 3318 | ||||
-rw-r--r-- | bin/nsupdate/nsupdate.docbook | 926 | ||||
-rw-r--r-- | bin/nsupdate/nsupdate.html | 783 | ||||
-rw-r--r-- | bin/nsupdate/win32/nsupdate.dsp.in | 103 | ||||
-rw-r--r-- | bin/nsupdate/win32/nsupdate.dsw | 29 | ||||
-rw-r--r-- | bin/nsupdate/win32/nsupdate.mak.in | 375 | ||||
-rw-r--r-- | bin/nsupdate/win32/nsupdate.vcxproj.filters.in | 18 | ||||
-rw-r--r-- | bin/nsupdate/win32/nsupdate.vcxproj.in | 110 | ||||
-rw-r--r-- | bin/nsupdate/win32/nsupdate.vcxproj.user | 3 |
11 files changed, 6284 insertions, 0 deletions
diff --git a/bin/nsupdate/Makefile.in b/bin/nsupdate/Makefile.in new file mode 100644 index 0000000..0276f69 --- /dev/null +++ b/bin/nsupdate/Makefile.in @@ -0,0 +1,95 @@ +# 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. + +# $Id: Makefile.in,v 1.36 2009/12/05 23:31:40 each Exp $ + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +VERSION=@BIND9_VERSION@ + +@BIND9_MAKE_INCLUDES@ + +READLINE_LIB = @READLINE_LIB@ + +DST_GSSAPI_INC = @DST_GSSAPI_INC@ + +CINCLUDES = ${LWRES_INCLUDES} ${DNS_INCLUDES} \ + ${BIND9_INCLUDES} ${ISC_INCLUDES} \ + ${ISCCFG_INCLUDES} ${DST_GSSAPI_INC} @DST_OPENSSL_INC@ + +CDEFINES = -DVERSION=\"${VERSION}\" @CRYPTO@ @USE_GSSAPI@ +CWARNINGS = + +LWRESLIBS = ../../lib/lwres/liblwres.@A@ +DNSLIBS = ../../lib/dns/libdns.@A@ @DNS_CRYPTO_LIBS@ +BIND9LIBS = ../../lib/bind9/libbind9.@A@ +ISCLIBS = ../../lib/isc/libisc.@A@ +ISCNOSYMLIBS = ../../lib/isc/libisc-nosymtbl.@A@ +ISCCFGLIBS = ../../lib/isccfg/libisccfg.@A@ + +LWRESDEPLIBS = ../../lib/lwres/liblwres.@A@ +DNSDEPLIBS = ../../lib/dns/libdns.@A@ +BIND9DEPLIBS = ../../lib/bind9/libbind9.@A@ +ISCDEPLIBS = ../../lib/isc/libisc.@A@ +ISCCFGDEPLIBS = ../../lib/isccfg/libisccfg.@A@ + +DEPLIBS = ${DNSDEPLIBS} ${BIND9DEPLIBS} ${ISCDEPLIBS} ${ISCCFGDEPLIBS} + +LIBS = ${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} ${ISCCFGLIBS} ${ISCLIBS} @LIBS@ + +NOSYMLIBS = ${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} ${ISCCFGLIBS} ${ISCNOSYMLIBS} @LIBS@ + +SUBDIRS = + +TARGETS = nsupdate@EXEEXT@ + +OBJS = nsupdate.@O@ + +UOBJS = + +SRCS = nsupdate.c + +MANPAGES = nsupdate.1 + +HTMLPAGES = nsupdate.html + +MANOBJS = ${MANPAGES} ${HTMLPAGES} + +@BIND9_MAKE_RULES@ + +nsupdate.@O@: nsupdate.c + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \ + -DSESSION_KEYFILE=\"${localstatedir}/run/named/session.key\" \ + -c ${srcdir}/nsupdate.c + +nsupdate@EXEEXT@: nsupdate.@O@ ${UOBJS} ${DEPLIBS} + export BASEOBJS="nsupdate.@O@ ${READLINE_LIB} ${UOBJS}"; \ + ${FINALBUILDCMD} + +doc man:: ${MANOBJS} + +docclean manclean maintainer-clean:: + rm -f ${MANOBJS} + +clean distclean:: + rm -f ${TARGETS} + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${bindir} + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${mandir}/man1 + +install:: nsupdate@EXEEXT@ installdirs + ${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} nsupdate@EXEEXT@ ${DESTDIR}${bindir} + ${INSTALL_DATA} ${srcdir}/nsupdate.1 ${DESTDIR}${mandir}/man1 + +uninstall:: + rm -f ${DESTDIR}${mandir}/man1/nsupdate.1 + ${LIBTOOL_MODE_UNINSTALL} rm -f ${DESTDIR}${bindir}/nsupdate@EXEEXT@ diff --git a/bin/nsupdate/nsupdate.1 b/bin/nsupdate/nsupdate.1 new file mode 100644 index 0000000..28f6191 --- /dev/null +++ b/bin/nsupdate/nsupdate.1 @@ -0,0 +1,524 @@ +.\" Copyright (C) 2000-2012, 2014-2019 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/. +.\" +.hy 0 +.ad l +'\" t +.\" Title: nsupdate +.\" Author: +.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> +.\" Date: 2014-04-18 +.\" Manual: BIND9 +.\" Source: ISC +.\" Language: English +.\" +.TH "NSUPDATE" "1" "2014\-04\-18" "ISC" "BIND9" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nsupdate \- Dynamic DNS update utility +.SH "SYNOPSIS" +.HP \w'\fBnsupdate\fR\ 'u +\fBnsupdate\fR [\fB\-d\fR] [\fB\-D\fR] [\fB\-i\fR] [\fB\-L\ \fR\fB\fIlevel\fR\fR] [[\fB\-g\fR] | [\fB\-o\fR] | [\fB\-l\fR] | [\fB\-y\ \fR\fB\fI[hmac:]\fR\fIkeyname:secret\fR\fR] | [\fB\-k\ \fR\fB\fIkeyfile\fR\fR]] [\fB\-t\ \fR\fB\fItimeout\fR\fR] [\fB\-u\ \fR\fB\fIudptimeout\fR\fR] [\fB\-r\ \fR\fB\fIudpretries\fR\fR] [\fB\-R\ \fR\fB\fIrandomdev\fR\fR] [\fB\-v\fR] [\fB\-T\fR] [\fB\-P\fR] [\fB\-V\fR] [filename] +.SH "DESCRIPTION" +.PP +\fBnsupdate\fR +is used to submit Dynamic DNS Update requests as defined in RFC 2136 to a name server\&. This allows resource records to be added or removed from a zone without manually editing the zone file\&. A single update request can contain requests to add or remove more than one resource record\&. +.PP +Zones that are under dynamic control via +\fBnsupdate\fR +or a DHCP server should not be edited by hand\&. Manual edits could conflict with dynamic updates and cause data to be lost\&. +.PP +The resource records that are dynamically added or removed with +\fBnsupdate\fR +have to be in the same zone\&. Requests are sent to the zone\*(Aqs master server\&. This is identified by the MNAME field of the zone\*(Aqs SOA record\&. +.PP +Transaction signatures can be used to authenticate the Dynamic DNS updates\&. These use the TSIG resource record type described in RFC 2845 or the SIG(0) record described in RFC 2535 and RFC 2931 or GSS\-TSIG as described in RFC 3645\&. +.PP +TSIG relies on a shared secret that should only be known to +\fBnsupdate\fR +and the name server\&. For instance, suitable +\fBkey\fR +and +\fBserver\fR +statements would be added to +/etc/named\&.conf +so that the name server can associate the appropriate secret key and algorithm with the IP address of the client application that will be using TSIG authentication\&. You can use +\fBddns\-confgen\fR +to generate suitable configuration fragments\&. +\fBnsupdate\fR +uses the +\fB\-y\fR +or +\fB\-k\fR +options to provide the TSIG shared secret\&. These options are mutually exclusive\&. +.PP +SIG(0) uses public key cryptography\&. To use a SIG(0) key, the public key must be stored in a KEY record in a zone served by the name server\&. +.PP +GSS\-TSIG uses Kerberos credentials\&. Standard GSS\-TSIG mode is switched on with the +\fB\-g\fR +flag\&. A non\-standards\-compliant variant of GSS\-TSIG used by Windows 2000 can be switched on with the +\fB\-o\fR +flag\&. +.SH "OPTIONS" +.PP +\-d +.RS 4 +Debug mode\&. This provides tracing information about the update requests that are made and the replies received from the name server\&. +.RE +.PP +\-D +.RS 4 +Extra debug mode\&. +.RE +.PP +\-i +.RS 4 +Force interactive mode, even when standard input is not a terminal\&. +.RE +.PP +\-k \fIkeyfile\fR +.RS 4 +The file containing the TSIG authentication key\&. Keyfiles may be in two formats: a single file containing a +named\&.conf\-format +\fBkey\fR +statement, which may be generated automatically by +\fBddns\-confgen\fR, or a pair of files whose names are of the format +K{name}\&.+157\&.+{random}\&.key +and +K{name}\&.+157\&.+{random}\&.private, which can be generated by +\fBdnssec\-keygen\fR\&. The +\fB\-k\fR +may also be used to specify a SIG(0) key used to authenticate Dynamic DNS update requests\&. In this case, the key specified is not an HMAC\-MD5 key\&. +.RE +.PP +\-l +.RS 4 +Local\-host only mode\&. This sets the server address to localhost (disabling the +\fBserver\fR +so that the server address cannot be overridden)\&. Connections to the local server will use a TSIG key found in +/var/run/named/session\&.key, which is automatically generated by +\fBnamed\fR +if any local master zone has set +\fBupdate\-policy\fR +to +\fBlocal\fR\&. The location of this key file can be overridden with the +\fB\-k\fR +option\&. +.RE +.PP +\-L \fIlevel\fR +.RS 4 +Set the logging debug level\&. If zero, logging is disabled\&. +.RE +.PP +\-p \fIport\fR +.RS 4 +Set the port to use for connections to a name server\&. The default is 53\&. +.RE +.PP +\-P +.RS 4 +Print the list of private BIND\-specific resource record types whose format is understood by +\fBnsupdate\fR\&. See also the +\fB\-T\fR +option\&. +.RE +.PP +\-r \fIudpretries\fR +.RS 4 +The number of UDP retries\&. The default is 3\&. If zero, only one update request will be made\&. +.RE +.PP +\-R \fIrandomdev\fR +.RS 4 +Where to obtain randomness\&. If the operating system does not provide a +/dev/random +or equivalent device, the default source of randomness is keyboard input\&. +randomdev +specifies the name of a character device or file containing random data to be used instead of the default\&. The special value +keyboard +indicates that keyboard input should be used\&. This option may be specified multiple times\&. +.RE +.PP +\-t \fItimeout\fR +.RS 4 +The maximum time an update request can take before it is aborted\&. The default is 300 seconds\&. Zero can be used to disable the timeout\&. +.RE +.PP +\-T +.RS 4 +Print the list of IANA standard resource record types whose format is understood by +\fBnsupdate\fR\&. +\fBnsupdate\fR +will exit after the lists are printed\&. The +\fB\-T\fR +option can be combined with the +\fB\-P\fR +option\&. +.sp +Other types can be entered using "TYPEXXXXX" where "XXXXX" is the decimal value of the type with no leading zeros\&. The rdata, if present, will be parsed using the UNKNOWN rdata format, (<backslash> <hash> <space> <length> <space> <hexstring>)\&. +.RE +.PP +\-u \fIudptimeout\fR +.RS 4 +The UDP retry interval\&. The default is 3 seconds\&. If zero, the interval will be computed from the timeout interval and number of UDP retries\&. +.RE +.PP +\-v +.RS 4 +Use TCP even for small update requests\&. By default, +\fBnsupdate\fR +uses UDP to send update requests to the name server unless they are too large to fit in a UDP request in which case TCP will be used\&. TCP may be preferable when a batch of update requests is made\&. +.RE +.PP +\-V +.RS 4 +Print the version number and exit\&. +.RE +.PP +\-y \fI[hmac:]\fR\fIkeyname:secret\fR +.RS 4 +Literal TSIG authentication key\&. +\fIkeyname\fR +is the name of the key, and +\fIsecret\fR +is the base64 encoded shared secret\&. +\fIhmac\fR +is the name of the key algorithm; valid choices are +hmac\-md5, +hmac\-sha1, +hmac\-sha224, +hmac\-sha256, +hmac\-sha384, or +hmac\-sha512\&. If +\fIhmac\fR +is not specified, the default is +hmac\-md5 +or if MD5 was disabled +hmac\-sha256\&. +.sp +NOTE: Use of the +\fB\-y\fR +option is discouraged because the shared secret is supplied as a command line argument in clear text\&. This may be visible in the output from +\fBps\fR(1) +or in a history file maintained by the user\*(Aqs shell\&. +.RE +.SH "INPUT FORMAT" +.PP +\fBnsupdate\fR +reads input from +\fIfilename\fR +or standard input\&. Each command is supplied on exactly one line of input\&. Some commands are for administrative purposes\&. The others are either update instructions or prerequisite checks on the contents of the zone\&. These checks set conditions that some name or set of resource records (RRset) either exists or is absent from the zone\&. These conditions must be met if the entire update request is to succeed\&. Updates will be rejected if the tests for the prerequisite conditions fail\&. +.PP +Every update request consists of zero or more prerequisites and zero or more updates\&. This allows a suitably authenticated update request to proceed if some specified resource records are present or missing from the zone\&. A blank input line (or the +\fBsend\fR +command) causes the accumulated commands to be sent as one Dynamic DNS update request to the name server\&. +.PP +The command formats and their meaning are as follows: +.PP +\fBserver\fR {servername} [port] +.RS 4 +Sends all dynamic update requests to the name server +\fIservername\fR\&. When no server statement is provided, +\fBnsupdate\fR +will send updates to the master server of the correct zone\&. The MNAME field of that zone\*(Aqs SOA record will identify the master server for that zone\&. +\fIport\fR +is the port number on +\fIservername\fR +where the dynamic update requests get sent\&. If no port number is specified, the default DNS port number of 53 is used\&. +.RE +.PP +\fBlocal\fR {address} [port] +.RS 4 +Sends all dynamic update requests using the local +\fIaddress\fR\&. When no local statement is provided, +\fBnsupdate\fR +will send updates using an address and port chosen by the system\&. +\fIport\fR +can additionally be used to make requests come from a specific port\&. If no port number is specified, the system will assign one\&. +.RE +.PP +\fBzone\fR {zonename} +.RS 4 +Specifies that all updates are to be made to the zone +\fIzonename\fR\&. If no +\fIzone\fR +statement is provided, +\fBnsupdate\fR +will attempt determine the correct zone to update based on the rest of the input\&. +.RE +.PP +\fBclass\fR {classname} +.RS 4 +Specify the default class\&. If no +\fIclass\fR +is specified, the default class is +\fIIN\fR\&. +.RE +.PP +\fBttl\fR {seconds} +.RS 4 +Specify the default time to live for records to be added\&. The value +\fInone\fR +will clear the default ttl\&. +.RE +.PP +\fBkey\fR [hmac:] {keyname} {secret} +.RS 4 +Specifies that all updates are to be TSIG\-signed using the +\fIkeyname\fR\fIsecret\fR +pair\&. If +\fIhmac\fR +is specified, then it sets the signing algorithm in use; the default is +hmac\-md5 +or if MD5 was disabled +hmac\-sha256\&. The +\fBkey\fR +command overrides any key specified on the command line via +\fB\-y\fR +or +\fB\-k\fR\&. +.RE +.PP +\fBgsstsig\fR +.RS 4 +Use GSS\-TSIG to sign the updated\&. This is equivalent to specifying +\fB\-g\fR +on the command line\&. +.RE +.PP +\fBoldgsstsig\fR +.RS 4 +Use the Windows 2000 version of GSS\-TSIG to sign the updated\&. This is equivalent to specifying +\fB\-o\fR +on the command line\&. +.RE +.PP +\fBrealm\fR {[realm_name]} +.RS 4 +When using GSS\-TSIG use +\fIrealm_name\fR +rather than the default realm in +krb5\&.conf\&. If no realm is specified the saved realm is cleared\&. +.RE +.PP +\fBcheck\-names\fR {[yes_or_no]} +.RS 4 +Turn on or off check\-names processing on records to be added\&. Check\-names has no effect on prerequisites or records to be deleted\&. By default check\-names processing is on\&. If check\-names processing fails the record will not be added to the UPDATE message\&. +.RE +.PP +\fB[prereq]\fR\fB nxdomain\fR {domain\-name} +.RS 4 +Requires that no resource record of any type exists with name +\fIdomain\-name\fR\&. +.RE +.PP +\fB[prereq]\fR\fB yxdomain\fR {domain\-name} +.RS 4 +Requires that +\fIdomain\-name\fR +exists (has as at least one resource record, of any type)\&. +.RE +.PP +\fB[prereq]\fR\fB nxrrset\fR {domain\-name} [class] {type} +.RS 4 +Requires that no resource record exists of the specified +\fItype\fR, +\fIclass\fR +and +\fIdomain\-name\fR\&. If +\fIclass\fR +is omitted, IN (internet) is assumed\&. +.RE +.PP +\fB[prereq]\fR\fB yxrrset\fR {domain\-name} [class] {type} +.RS 4 +This requires that a resource record of the specified +\fItype\fR, +\fIclass\fR +and +\fIdomain\-name\fR +must exist\&. If +\fIclass\fR +is omitted, IN (internet) is assumed\&. +.RE +.PP +\fB[prereq]\fR\fB yxrrset\fR {domain\-name} [class] {type} {data...} +.RS 4 +The +\fIdata\fR +from each set of prerequisites of this form sharing a common +\fItype\fR, +\fIclass\fR, and +\fIdomain\-name\fR +are combined to form a set of RRs\&. This set of RRs must exactly match the set of RRs existing in the zone at the given +\fItype\fR, +\fIclass\fR, and +\fIdomain\-name\fR\&. The +\fIdata\fR +are written in the standard text representation of the resource record\*(Aqs RDATA\&. +.RE +.PP +\fB[update]\fR\fB del\fR\fB[ete]\fR {domain\-name} [ttl] [class] [type\ [data...]] +.RS 4 +Deletes any resource records named +\fIdomain\-name\fR\&. If +\fItype\fR +and +\fIdata\fR +is provided, only matching resource records will be removed\&. The internet class is assumed if +\fIclass\fR +is not supplied\&. The +\fIttl\fR +is ignored, and is only allowed for compatibility\&. +.RE +.PP +\fB[update]\fR\fB add\fR {domain\-name} {ttl} [class] {type} {data...} +.RS 4 +Adds a new resource record with the specified +\fIttl\fR, +\fIclass\fR +and +\fIdata\fR\&. +.RE +.PP +\fBshow\fR +.RS 4 +Displays the current message, containing all of the prerequisites and updates specified since the last send\&. +.RE +.PP +\fBsend\fR +.RS 4 +Sends the current message\&. This is equivalent to entering a blank line\&. +.RE +.PP +\fBanswer\fR +.RS 4 +Displays the answer\&. +.RE +.PP +\fBdebug\fR +.RS 4 +Turn on debugging\&. +.RE +.PP +\fBversion\fR +.RS 4 +Print version number\&. +.RE +.PP +\fBhelp\fR +.RS 4 +Print a list of commands\&. +.RE +.PP +Lines beginning with a semicolon are comments and are ignored\&. +.SH "EXAMPLES" +.PP +The examples below show how +\fBnsupdate\fR +could be used to insert and delete resource records from the +\fBexample\&.com\fR +zone\&. Notice that the input in each example contains a trailing blank line so that a group of commands are sent as one dynamic update request to the master name server for +\fBexample\&.com\fR\&. +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nsupdate +> update delete oldhost\&.example\&.com A +> update add newhost\&.example\&.com 86400 A 172\&.16\&.1\&.1 +> send +.fi +.if n \{\ +.RE +.\} +.PP +Any A records for +\fBoldhost\&.example\&.com\fR +are deleted\&. And an A record for +\fBnewhost\&.example\&.com\fR +with IP address 172\&.16\&.1\&.1 is added\&. The newly\-added record has a 1 day TTL (86400 seconds)\&. +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nsupdate +> prereq nxdomain nickname\&.example\&.com +> update add nickname\&.example\&.com 86400 CNAME somehost\&.example\&.com +> send +.fi +.if n \{\ +.RE +.\} +.PP +The prerequisite condition gets the name server to check that there are no resource records of any type for +\fBnickname\&.example\&.com\fR\&. If there are, the update request fails\&. If this name does not exist, a CNAME for it is added\&. This ensures that when the CNAME is added, it cannot conflict with the long\-standing rule in RFC 1034 that a name must not exist as any other record type if it exists as a CNAME\&. (The rule has been updated for DNSSEC in RFC 2535 to allow CNAMEs to have RRSIG, DNSKEY and NSEC records\&.) +.SH "FILES" +.PP +\fB/etc/resolv\&.conf\fR +.RS 4 +used to identify default name server +.RE +.PP +\fB/var/run/named/session\&.key\fR +.RS 4 +sets the default TSIG key for use in local\-only mode +.RE +.PP +\fBK{name}\&.+157\&.+{random}\&.key\fR +.RS 4 +base\-64 encoding of HMAC\-MD5 key created by +\fBdnssec-keygen\fR(8)\&. +.RE +.PP +\fBK{name}\&.+157\&.+{random}\&.private\fR +.RS 4 +base\-64 encoding of HMAC\-MD5 key created by +\fBdnssec-keygen\fR(8)\&. +.RE +.SH "SEE ALSO" +.PP +RFC 2136, +RFC 3007, +RFC 2104, +RFC 2845, +RFC 1034, +RFC 2535, +RFC 2931, +\fBnamed\fR(8), +\fBddns-confgen\fR(8), +\fBdnssec-keygen\fR(8)\&. +.SH "BUGS" +.PP +The TSIG key is redundantly stored in two separate files\&. This is a consequence of nsupdate using the DST library for its cryptographic operations, and may change in future releases\&. +.SH "AUTHOR" +.PP +\fBInternet Systems Consortium, Inc\&.\fR +.SH "COPYRIGHT" +.br +Copyright \(co 2000-2012, 2014-2019 Internet Systems Consortium, Inc. ("ISC") +.br diff --git a/bin/nsupdate/nsupdate.c b/bin/nsupdate/nsupdate.c new file mode 100644 index 0000000..8d1da3b --- /dev/null +++ b/bin/nsupdate/nsupdate.c @@ -0,0 +1,3318 @@ +/* + * 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. + */ + +/*! \file */ + +#include <config.h> + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdlib.h> +#include <unistd.h> + +#include <isc/app.h> +#include <isc/base64.h> +#include <isc/buffer.h> +#include <isc/commandline.h> +#include <isc/entropy.h> +#include <isc/event.h> +#include <isc/file.h> +#include <isc/hash.h> +#include <isc/lex.h> +#include <isc/log.h> +#include <isc/mem.h> +#include <isc/parseint.h> +#include <isc/print.h> +#include <isc/random.h> +#include <isc/region.h> +#include <isc/sockaddr.h> +#include <isc/socket.h> +#include <isc/stdio.h> +#include <isc/string.h> +#include <isc/task.h> +#include <isc/timer.h> +#include <isc/types.h> +#include <isc/util.h> + +#include <pk11/site.h> + +#include <isccfg/namedconf.h> + +#include <dns/callbacks.h> +#include <dns/dispatch.h> +#include <dns/dnssec.h> +#include <dns/events.h> +#include <dns/fixedname.h> +#include <dns/log.h> +#include <dns/masterdump.h> +#include <dns/message.h> +#include <dns/name.h> +#include <dns/rcode.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/request.h> +#include <dns/result.h> +#include <dns/tkey.h> +#include <dns/tsig.h> + +#include <dst/dst.h> + +#include <lwres/lwres.h> +#include <lwres/net.h> + +#ifdef GSSAPI +#include <dst/gssapi.h> +#ifdef WIN32 +#include <krb5/krb5.h> +#else +#include ISC_PLATFORM_KRB5HEADER +#endif +#endif +#include <bind9/getaddresses.h> + +#if defined(HAVE_READLINE) +#if defined(HAVE_EDIT_READLINE_READLINE_H) +#include <edit/readline/readline.h> +#if defined(HAVE_EDIT_READLINE_HISTORY_H) +#include <edit/readline/history.h> +#endif +#elif defined(HAVE_EDITLINE_READLINE_H) +#include <editline/readline.h> +#else +#include <readline/readline.h> +#include <readline/history.h> +#endif +#endif + +#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 MAXCMD (128 * 1024) +#define MAXWIRE (64 * 1024) +#define PACKETSIZE ((64 * 1024) - 1) +#define INITTEXT (2 * 1024) +#define MAXTEXT (128 * 1024) +#define FIND_TIMEOUT 5 +#define TTL_MAX 2147483647U /* Maximum signed 32 bit integer. */ + +#define DNSDEFAULTPORT 53 + +/* Number of addresses to request from bind9_getaddresses() */ +#define MAX_SERVERADDRS 4 + +static uint16_t dnsport = DNSDEFAULTPORT; + +#ifndef RESOLV_CONF +#define RESOLV_CONF "/etc/resolv.conf" +#endif + +static bool debugging = false, ddebugging = false; +static bool memdebugging = false; +static bool have_ipv4 = false; +static bool have_ipv6 = false; +static bool is_dst_up = false; +static bool usevc = false; +static bool usegsstsig = false; +static bool use_win2k_gsstsig = false; +static bool tried_other_gsstsig = false; +static bool local_only = false; +static isc_taskmgr_t *taskmgr = NULL; +static isc_task_t *global_task = NULL; +static isc_event_t *global_event = NULL; +static isc_log_t *glctx = NULL; +static isc_mem_t *gmctx = NULL; +static dns_dispatchmgr_t *dispatchmgr = NULL; +static dns_requestmgr_t *requestmgr = NULL; +static isc_socketmgr_t *socketmgr = NULL; +static isc_timermgr_t *timermgr = NULL; +static dns_dispatch_t *dispatchv4 = NULL; +static dns_dispatch_t *dispatchv6 = NULL; +static dns_message_t *updatemsg = NULL; +static dns_fixedname_t fuserzone; +static dns_fixedname_t fzname; +static dns_name_t *userzone = NULL; +static dns_name_t *zname = NULL; +static dns_name_t tmpzonename; +static dns_name_t restart_master; +static dns_tsig_keyring_t *gssring = NULL; +static dns_tsigkey_t *tsigkey = NULL; +static dst_key_t *sig0key = NULL; +static lwres_context_t *lwctx = NULL; +static lwres_conf_t *lwconf; +static isc_sockaddr_t *servers = NULL; +static isc_sockaddr_t *master_servers = NULL; +static bool default_servers = true; +static int ns_inuse = 0; +static int master_inuse = 0; +static int ns_total = 0; +static int ns_alloc = 0; +static int master_total = 0; +static int master_alloc = 0; +static isc_sockaddr_t *localaddr4 = NULL; +static isc_sockaddr_t *localaddr6 = NULL; +static const char *keyfile = NULL; +static char *keystr = NULL; +static isc_entropy_t *entropy = NULL; +static bool shuttingdown = false; +static FILE *input; +static bool interactive = true; +static bool seenerror = false; +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; +static dns_rdataclass_t defaultclass = dns_rdataclass_in; +static dns_rdataclass_t zoneclass = dns_rdataclass_none; +static dns_message_t *answer = NULL; +static uint32_t default_ttl = 0; +static bool default_ttl_set = false; +static bool checknames = true; + +typedef struct nsu_requestinfo { + dns_message_t *msg; + isc_sockaddr_t *addr; +} nsu_requestinfo_t; + +static void +sendrequest(isc_sockaddr_t *destaddr, dns_message_t *msg, + dns_request_t **request); +static void +send_update(dns_name_t *zonename, isc_sockaddr_t *master); + +ISC_PLATFORM_NORETURN_PRE static void +fatal(const char *format, ...) +ISC_FORMAT_PRINTF(1, 2) ISC_PLATFORM_NORETURN_POST; + +static void +debug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +static void +ddebug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +#ifdef GSSAPI +static dns_fixedname_t fkname; +static isc_sockaddr_t *kserver = NULL; +static char *realm = NULL; +static char servicename[DNS_NAME_FORMATSIZE]; +static dns_name_t *keyname; +typedef struct nsu_gssinfo { + dns_message_t *msg; + isc_sockaddr_t *addr; + gss_ctx_id_t context; +} nsu_gssinfo_t; + +static void +failed_gssrequest(); +static void +start_gssrequest(dns_name_t *master); +static void +send_gssrequest(isc_sockaddr_t *destaddr, dns_message_t *msg, + dns_request_t **request, gss_ctx_id_t context); +static void +recvgss(isc_task_t *task, isc_event_t *event); +#endif /* GSSAPI */ + +static void +error(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +#define STATUS_MORE (uint16_t)0 +#define STATUS_SEND (uint16_t)1 +#define STATUS_QUIT (uint16_t)2 +#define STATUS_SYNTAX (uint16_t)3 + +typedef struct entropysource entropysource_t; + +struct entropysource { + isc_entropysource_t *source; + isc_mem_t *mctx; + ISC_LINK(entropysource_t) link; +}; + +static ISC_LIST(entropysource_t) sources; + +static void +setup_entropy(isc_mem_t *mctx, const char *randomfile, isc_entropy_t **ectx) { + isc_result_t result; + isc_entropysource_t *source = NULL; + entropysource_t *elt; + int usekeyboard = ISC_ENTROPY_KEYBOARDMAYBE; + + REQUIRE(ectx != NULL); + + if (*ectx == NULL) { + result = isc_entropy_create(mctx, ectx); + if (result != ISC_R_SUCCESS) + fatal("could not create entropy object"); + ISC_LIST_INIT(sources); + } + + if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) { + usekeyboard = ISC_ENTROPY_KEYBOARDYES; + randomfile = NULL; + } + + result = isc_entropy_usebestsource(*ectx, &source, randomfile, + usekeyboard); + + if (result != ISC_R_SUCCESS) + fatal("could not initialize entropy source: %s", + isc_result_totext(result)); + + if (source != NULL) { + elt = isc_mem_get(mctx, sizeof(*elt)); + if (elt == NULL) + fatal("out of memory"); + elt->source = source; + elt->mctx = mctx; + ISC_LINK_INIT(elt, link); + ISC_LIST_APPEND(sources, elt, link); + } +} + +static void +cleanup_entropy(isc_entropy_t **ectx) { + entropysource_t *source; + while (!ISC_LIST_EMPTY(sources)) { + source = ISC_LIST_HEAD(sources); + ISC_LIST_UNLINK(sources, source, link); + isc_entropy_destroysource(&source->source); + isc_mem_put(source->mctx, source, sizeof(*source)); + } + isc_entropy_detach(ectx); +} + +static void +master_from_servers(void) { + + if (master_servers != NULL && master_servers != servers) + isc_mem_put(gmctx, master_servers, + master_alloc * sizeof(isc_sockaddr_t)); + master_servers = servers; + master_total = ns_total; + master_alloc = ns_alloc; + master_inuse = ns_inuse; +} + +static dns_rdataclass_t +getzoneclass(void) { + if (zoneclass == dns_rdataclass_none) + zoneclass = defaultclass; + return (zoneclass); +} + +static bool +setzoneclass(dns_rdataclass_t rdclass) { + if (zoneclass == dns_rdataclass_none || + rdclass == dns_rdataclass_none) + zoneclass = rdclass; + if (zoneclass != rdclass) + return (false); + return (true); +} + +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 void +error(const char *format, ...) { + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); +} + +static void +debug(const char *format, ...) { + va_list args; + + if (debugging) { + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + } +} + +static void +ddebug(const char *format, ...) { + va_list args; + + if (ddebugging) { + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + } +} + +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 * +mem_alloc(void *arg, size_t size) { + return (isc_mem_get(arg, size)); +} + +static void +mem_free(void *arg, void *mem, size_t size) { + isc_mem_put(arg, mem, size); +} + +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 +reset_system(void) { + isc_result_t result; + + ddebug("reset_system()"); + /* If the update message is still around, destroy it */ + if (updatemsg != NULL) + dns_message_reset(updatemsg, DNS_MESSAGE_INTENTRENDER); + else { + result = dns_message_create(gmctx, DNS_MESSAGE_INTENTRENDER, + &updatemsg); + check_result(result, "dns_message_create"); + } + updatemsg->opcode = dns_opcode_update; + if (usegsstsig) { + if (tsigkey != NULL) + dns_tsigkey_detach(&tsigkey); + if (gssring != NULL) + dns_tsigkeyring_detach(&gssring); + tried_other_gsstsig = false; + } +} + +static bool +parse_hmac(dns_name_t **hmac, const char *hmacstr, size_t len, + uint16_t *digestbitsp) +{ + uint16_t digestbits = 0; + isc_result_t result; + char buf[20]; + + REQUIRE(hmac != NULL && *hmac == NULL); + REQUIRE(hmacstr != NULL); + + if (len >= sizeof(buf)) { + error("unknown key type '%.*s'", (int)(len), hmacstr); + return (false); + } + + /* Copy len bytes and NUL terminate. */ + strlcpy(buf, hmacstr, ISC_MIN(len + 1, sizeof(buf))); + +#ifndef PK11_MD5_DISABLE + if (strcasecmp(buf, "hmac-md5") == 0) { + *hmac = DNS_TSIG_HMACMD5_NAME; + } else if (strncasecmp(buf, "hmac-md5-", 9) == 0) { + *hmac = DNS_TSIG_HMACMD5_NAME; + result = isc_parse_uint16(&digestbits, &buf[9], 10); + if (result != ISC_R_SUCCESS || digestbits > 128) { + error("digest-bits out of range [0..128]"); + return (false); + } + *digestbitsp = (digestbits + 7) & ~0x7U; + } else +#endif + if (strcasecmp(buf, "hmac-sha1") == 0) { + *hmac = DNS_TSIG_HMACSHA1_NAME; + } else if (strncasecmp(buf, "hmac-sha1-", 10) == 0) { + *hmac = DNS_TSIG_HMACSHA1_NAME; + result = isc_parse_uint16(&digestbits, &buf[10], 10); + if (result != ISC_R_SUCCESS || digestbits > 160) { + error("digest-bits out of range [0..160]"); + return (false); + } + *digestbitsp = (digestbits + 7) & ~0x7U; + } else if (strcasecmp(buf, "hmac-sha224") == 0) { + *hmac = DNS_TSIG_HMACSHA224_NAME; + } else if (strncasecmp(buf, "hmac-sha224-", 12) == 0) { + *hmac = DNS_TSIG_HMACSHA224_NAME; + result = isc_parse_uint16(&digestbits, &buf[12], 10); + if (result != ISC_R_SUCCESS || digestbits > 224) { + error("digest-bits out of range [0..224]"); + return (false); + } + *digestbitsp = (digestbits + 7) & ~0x7U; + } else if (strcasecmp(buf, "hmac-sha256") == 0) { + *hmac = DNS_TSIG_HMACSHA256_NAME; + } else if (strncasecmp(buf, "hmac-sha256-", 12) == 0) { + *hmac = DNS_TSIG_HMACSHA256_NAME; + result = isc_parse_uint16(&digestbits, &buf[12], 10); + if (result != ISC_R_SUCCESS || digestbits > 256) { + error("digest-bits out of range [0..256]"); + return (false); + } + *digestbitsp = (digestbits + 7) & ~0x7U; + } else if (strcasecmp(buf, "hmac-sha384") == 0) { + *hmac = DNS_TSIG_HMACSHA384_NAME; + } else if (strncasecmp(buf, "hmac-sha384-", 12) == 0) { + *hmac = DNS_TSIG_HMACSHA384_NAME; + result = isc_parse_uint16(&digestbits, &buf[12], 10); + if (result != ISC_R_SUCCESS || digestbits > 384) { + error("digest-bits out of range [0..384]"); + return (false); + } + *digestbitsp = (digestbits + 7) & ~0x7U; + } else if (strcasecmp(buf, "hmac-sha512") == 0) { + *hmac = DNS_TSIG_HMACSHA512_NAME; + } else if (strncasecmp(buf, "hmac-sha512-", 12) == 0) { + *hmac = DNS_TSIG_HMACSHA512_NAME; + result = isc_parse_uint16(&digestbits, &buf[12], 10); + if (result != ISC_R_SUCCESS || digestbits > 512) { + error("digest-bits out of range [0..512]"); + return (false); + } + *digestbitsp = (digestbits + 7) & ~0x7U; + } else { + error("unknown key type '%s'", buf); + return (false); + } + return (true); +} + +static int +basenamelen(const char *file) { + int len = strlen(file); + + if (len > 1 && file[len - 1] == '.') + len -= 1; + else if (len > 8 && strcmp(file + len - 8, ".private") == 0) + len -= 8; + else if (len > 4 && strcmp(file + len - 4, ".key") == 0) + len -= 4; + return (len); +} + +static void +setup_keystr(void) { + unsigned char *secret = NULL; + int secretlen; + isc_buffer_t secretbuf; + isc_result_t result; + isc_buffer_t keynamesrc; + char *secretstr; + char *s, *n; + dns_fixedname_t fkeyname; + dns_name_t *mykeyname; + char *name; + dns_name_t *hmacname = NULL; + uint16_t digestbits = 0; + + mykeyname = dns_fixedname_initname(&fkeyname); + + debug("Creating key..."); + + s = strchr(keystr, ':'); + if (s == NULL || s == keystr || s[1] == 0) + fatal("key option must specify [hmac:]keyname:secret"); + secretstr = s + 1; + n = strchr(secretstr, ':'); + if (n != NULL) { + if (n == secretstr || n[1] == 0) + fatal("key option must specify [hmac:]keyname:secret"); + name = secretstr; + secretstr = n + 1; + if (!parse_hmac(&hmacname, keystr, s - keystr, &digestbits)) { + exit(1); + } + } else { +#ifndef PK11_MD5_DISABLE + hmacname = DNS_TSIG_HMACMD5_NAME; +#else + hmacname = DNS_TSIG_HMACSHA256_NAME; +#endif + name = keystr; + n = s; + } + + isc_buffer_init(&keynamesrc, name, (unsigned int)(n - name)); + isc_buffer_add(&keynamesrc, (unsigned int)(n - name)); + + debug("namefromtext"); + result = dns_name_fromtext(mykeyname, &keynamesrc, dns_rootname, 0, + NULL); + check_result(result, "dns_name_fromtext"); + + secretlen = strlen(secretstr) * 3 / 4; + secret = isc_mem_allocate(gmctx, secretlen); + if (secret == NULL) + fatal("out of memory"); + + isc_buffer_init(&secretbuf, secret, secretlen); + result = isc_base64_decodestring(secretstr, &secretbuf); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not create key from %s: %s\n", + keystr, isc_result_totext(result)); + goto failure; + } + + secretlen = isc_buffer_usedlength(&secretbuf); + + debug("keycreate"); + result = dns_tsigkey_create(mykeyname, hmacname, secret, secretlen, + false, NULL, 0, 0, gmctx, NULL, + &tsigkey); + if (result != ISC_R_SUCCESS) + fprintf(stderr, "could not create key from %s: %s\n", + keystr, dns_result_totext(result)); + else + dst_key_setbits(tsigkey->key, digestbits); + failure: + if (secret != NULL) + isc_mem_free(gmctx, secret); +} + +/* + * Get a key from a named.conf format keyfile + */ +static isc_result_t +read_sessionkey(isc_mem_t *mctx, isc_log_t *lctx) { + cfg_parser_t *pctx = NULL; + cfg_obj_t *sessionkey = NULL; + const cfg_obj_t *key = NULL; + const cfg_obj_t *secretobj = NULL; + const cfg_obj_t *algorithmobj = NULL; + const char *mykeyname; + const char *secretstr; + const char *algorithm; + isc_result_t result; + int len; + + if (! isc_file_exists(keyfile)) + return (ISC_R_FILENOTFOUND); + + result = cfg_parser_create(mctx, lctx, &pctx); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = cfg_parse_file(pctx, keyfile, &cfg_type_sessionkey, + &sessionkey); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = cfg_map_get(sessionkey, "key", &key); + if (result != ISC_R_SUCCESS) + goto cleanup; + + (void) cfg_map_get(key, "secret", &secretobj); + (void) cfg_map_get(key, "algorithm", &algorithmobj); + if (secretobj == NULL || algorithmobj == NULL) + fatal("key must have algorithm and secret"); + + mykeyname = cfg_obj_asstring(cfg_map_getname(key)); + secretstr = cfg_obj_asstring(secretobj); + algorithm = cfg_obj_asstring(algorithmobj); + + len = strlen(algorithm) + strlen(mykeyname) + strlen(secretstr) + 3; + keystr = isc_mem_allocate(mctx, len); + if (keystr == NULL) + fatal("out of memory"); + snprintf(keystr, len, "%s:%s:%s", algorithm, mykeyname, secretstr); + setup_keystr(); + + cleanup: + if (pctx != NULL) { + if (sessionkey != NULL) + cfg_obj_destroy(pctx, &sessionkey); + cfg_parser_destroy(&pctx); + } + + if (keystr != NULL) + isc_mem_free(mctx, keystr); + + return (result); +} + +static void +setup_keyfile(isc_mem_t *mctx, isc_log_t *lctx) { + dst_key_t *dstkey = NULL; + isc_result_t result; + dns_name_t *hmacname = NULL; + + debug("Creating key..."); + + if (sig0key != NULL) + dst_key_free(&sig0key); + + /* Try reading the key from a K* pair */ + result = dst_key_fromnamedfile(keyfile, NULL, + DST_TYPE_PRIVATE | DST_TYPE_KEY, mctx, + &dstkey); + + /* If that didn't work, try reading it as a session.key keyfile */ + if (result != ISC_R_SUCCESS) { + result = read_sessionkey(mctx, lctx); + if (result == ISC_R_SUCCESS) + return; + } + + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not read key from %.*s.{private,key}: " + "%s\n", basenamelen(keyfile), keyfile, + isc_result_totext(result)); + return; + } + + switch (dst_key_alg(dstkey)) { +#ifndef PK11_MD5_DISABLE + case DST_ALG_HMACMD5: + hmacname = DNS_TSIG_HMACMD5_NAME; + break; +#endif + case DST_ALG_HMACSHA1: + hmacname = DNS_TSIG_HMACSHA1_NAME; + break; + case DST_ALG_HMACSHA224: + hmacname = DNS_TSIG_HMACSHA224_NAME; + break; + case DST_ALG_HMACSHA256: + hmacname = DNS_TSIG_HMACSHA256_NAME; + break; + case DST_ALG_HMACSHA384: + hmacname = DNS_TSIG_HMACSHA384_NAME; + break; + case DST_ALG_HMACSHA512: + hmacname = DNS_TSIG_HMACSHA512_NAME; + break; + } + if (hmacname != NULL) { + result = dns_tsigkey_createfromkey(dst_key_name(dstkey), + hmacname, dstkey, false, + NULL, 0, 0, mctx, NULL, + &tsigkey); + dst_key_free(&dstkey); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not create key from %s: %s\n", + keyfile, isc_result_totext(result)); + return; + } + } else { + dst_key_attach(dstkey, &sig0key); + dst_key_free(&dstkey); + } +} + +static void +doshutdown(void) { + isc_task_detach(&global_task); + + /* + * The isc_mem_put of master_servers must be before the + * isc_mem_put of servers as it sets the servers pointer + * to NULL. + */ + if (master_servers != NULL && master_servers != servers) + isc_mem_put(gmctx, master_servers, + master_alloc * sizeof(isc_sockaddr_t)); + + if (servers != NULL) + isc_mem_put(gmctx, servers, ns_alloc * sizeof(isc_sockaddr_t)); + + if (localaddr4 != NULL) + isc_mem_put(gmctx, localaddr4, sizeof(isc_sockaddr_t)); + + if (localaddr6 != NULL) + isc_mem_put(gmctx, localaddr6, sizeof(isc_sockaddr_t)); + + if (tsigkey != NULL) { + ddebug("Freeing TSIG key"); + dns_tsigkey_detach(&tsigkey); + } + + if (sig0key != NULL) { + ddebug("Freeing SIG(0) key"); + dst_key_free(&sig0key); + } + + if (updatemsg != NULL) + dns_message_destroy(&updatemsg); + + if (is_dst_up) { + ddebug("Destroy DST lib"); + dst_lib_destroy(); + is_dst_up = false; + } + + cleanup_entropy(&entropy); + + lwres_conf_clear(lwctx); + lwres_context_destroy(&lwctx); + + ddebug("Destroying request manager"); + dns_requestmgr_detach(&requestmgr); + + ddebug("Freeing the dispatchers"); + if (have_ipv4) + dns_dispatch_detach(&dispatchv4); + if (have_ipv6) + dns_dispatch_detach(&dispatchv6); + + ddebug("Shutting down dispatch manager"); + dns_dispatchmgr_destroy(&dispatchmgr); + +} + +static void +maybeshutdown(void) { + ddebug("Shutting down request manager"); + dns_requestmgr_shutdown(requestmgr); + + if (requests != 0) + return; + + doshutdown(); +} + +static void +shutdown_program(isc_task_t *task, isc_event_t *event) { + REQUIRE(task == global_task); + UNUSED(task); + + ddebug("shutdown_program()"); + isc_event_free(&event); + + shuttingdown = true; + maybeshutdown(); +} + +static void +setup_system(void) { + isc_result_t result; + isc_sockaddr_t bind_any, bind_any6; + lwres_result_t lwresult; + unsigned int attrs, attrmask; + int i; + isc_logconfig_t *logconfig = NULL; + + ddebug("setup_system()"); + + dns_result_register(); + + result = isc_net_probeipv4(); + if (result == ISC_R_SUCCESS) + have_ipv4 = true; + + result = isc_net_probeipv6(); + if (result == ISC_R_SUCCESS) + have_ipv6 = true; + + if (!have_ipv4 && !have_ipv6) + fatal("could not find either IPv4 or IPv6"); + + result = isc_log_create(gmctx, &glctx, &logconfig); + check_result(result, "isc_log_create"); + + isc_log_setcontext(glctx); + dns_log_init(glctx); + dns_log_setcontext(glctx); + + result = isc_log_usechannel(logconfig, "default_debug", NULL, NULL); + check_result(result, "isc_log_usechannel"); + + isc_log_setdebuglevel(glctx, logdebuglevel); + + lwresult = lwres_context_create(&lwctx, gmctx, mem_alloc, mem_free, 1); + if (lwresult != LWRES_R_SUCCESS) + fatal("lwres_context_create failed"); + + (void)lwres_conf_parse(lwctx, RESOLV_CONF); + lwconf = lwres_conf_get(lwctx); + + if (servers != NULL) { + if (master_servers == servers) + master_servers = NULL; + isc_mem_put(gmctx, servers, ns_alloc * sizeof(isc_sockaddr_t)); + } + + ns_inuse = 0; + if (local_only || lwconf->nsnext <= 0) { + struct in_addr in; + struct in6_addr in6; + + if (local_only && keyfile == NULL) + keyfile = SESSION_KEYFILE; + + default_servers = !local_only; + + ns_total = ns_alloc = (have_ipv4 ? 1 : 0) + (have_ipv6 ? 1 : 0); + servers = isc_mem_get(gmctx, ns_alloc * sizeof(isc_sockaddr_t)); + if (servers == NULL) + fatal("out of memory"); + + if (have_ipv4) { + in.s_addr = htonl(INADDR_LOOPBACK); + isc_sockaddr_fromin(&servers[0], &in, dnsport); + } + if (have_ipv6) { + memset(&in6, 0, sizeof(in6)); + in6.s6_addr[15] = 1; + isc_sockaddr_fromin6(&servers[(have_ipv4 ? 1 : 0)], + &in6, dnsport); + } + } else { + ns_total = ns_alloc = lwconf->nsnext; + servers = isc_mem_get(gmctx, ns_alloc * sizeof(isc_sockaddr_t)); + if (servers == NULL) + fatal("out of memory"); + for (i = 0; i < ns_total; i++) { + if (lwconf->nameservers[i].family == LWRES_ADDRTYPE_V4) + { + struct in_addr in4; + memmove(&in4, + lwconf->nameservers[i].address, 4); + isc_sockaddr_fromin(&servers[i], + &in4, dnsport); + } else { + struct in6_addr in6; + memmove(&in6, + lwconf->nameservers[i].address, 16); + isc_sockaddr_fromin6(&servers[i], + &in6, dnsport); + } + } + } + + setup_entropy(gmctx, NULL, &entropy); + + result = isc_hash_create(gmctx, entropy, DNS_NAME_MAXWIRE); + check_result(result, "isc_hash_create"); + isc_hash_init(); + + result = dns_dispatchmgr_create(gmctx, entropy, &dispatchmgr); + check_result(result, "dns_dispatchmgr_create"); + + result = isc_socketmgr_create(gmctx, &socketmgr); + check_result(result, "dns_socketmgr_create"); + + result = isc_timermgr_create(gmctx, &timermgr); + check_result(result, "dns_timermgr_create"); + + result = isc_taskmgr_create(gmctx, 1, 0, &taskmgr); + check_result(result, "isc_taskmgr_create"); + + result = isc_task_create(taskmgr, 0, &global_task); + check_result(result, "isc_task_create"); + + result = isc_task_onshutdown(global_task, shutdown_program, NULL); + check_result(result, "isc_task_onshutdown"); + + result = dst_lib_init(gmctx, entropy, 0); + check_result(result, "dst_lib_init"); + is_dst_up = true; + + attrmask = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP; + attrmask |= DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6; + + if (have_ipv6) { + attrs = DNS_DISPATCHATTR_UDP; + attrs |= DNS_DISPATCHATTR_MAKEQUERY; + attrs |= DNS_DISPATCHATTR_IPV6; + isc_sockaddr_any6(&bind_any6); + result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr, + &bind_any6, PACKETSIZE, + 4, 2, 3, 5, + attrs, attrmask, &dispatchv6); + check_result(result, "dns_dispatch_getudp (v6)"); + } + + if (have_ipv4) { + attrs = DNS_DISPATCHATTR_UDP; + attrs |= DNS_DISPATCHATTR_MAKEQUERY; + attrs |= DNS_DISPATCHATTR_IPV4; + isc_sockaddr_any(&bind_any); + result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr, + &bind_any, PACKETSIZE, + 4, 2, 3, 5, + attrs, attrmask, &dispatchv4); + check_result(result, "dns_dispatch_getudp (v4)"); + } + + result = dns_requestmgr_create(gmctx, timermgr, + socketmgr, taskmgr, dispatchmgr, + dispatchv4, dispatchv6, &requestmgr); + check_result(result, "dns_requestmgr_create"); + + if (keystr != NULL) + setup_keystr(); + else if (local_only) { + result = read_sessionkey(gmctx, glctx); + if (result != ISC_R_SUCCESS) + fatal("can't read key from %s: %s\n", + keyfile, isc_result_totext(result)); + } else if (keyfile != NULL) + setup_keyfile(gmctx, glctx); +} + +static int +get_addresses(char *host, in_port_t port, + isc_sockaddr_t *sockaddr, int naddrs) +{ + int count = 0; + isc_result_t result; + + isc_app_block(); + result = bind9_getaddresses(host, port, sockaddr, naddrs, &count); + isc_app_unblock(); + if (result != ISC_R_SUCCESS) + error("couldn't get address for '%s': %s", + host, isc_result_totext(result)); + return (count); +} + +static void +version(void) { + fputs("nsupdate " VERSION "\n", stderr); +} + +#define PARSE_ARGS_FMT "dDML:y:ghilovk:p:Pr:R::t:Tu:V" + +static void +pre_parse_args(int argc, char **argv) { + dns_rdatatype_t t; + int ch; + char buf[100]; + bool doexit = false; + + while ((ch = isc_commandline_parse(argc, argv, PARSE_ARGS_FMT)) != -1) { + switch (ch) { + case 'M': /* was -dm */ + debugging = true; + ddebugging = true; + memdebugging = true; + isc_mem_debugging = ISC_MEM_DEBUGTRACE | + ISC_MEM_DEBUGRECORD; + break; + + case '?': + case 'h': + if (isc_commandline_option != '?') + fprintf(stderr, "%s: invalid argument -%c\n", + argv[0], isc_commandline_option); + fprintf(stderr, "usage: nsupdate [-dDi] [-L level] [-l]" + "[-g | -o | -y keyname:secret | -k keyfile] " + "[-v] [-V] [-P] [-T] [filename]\n"); + exit(1); + + case 'P': + for (t = 0xff00; t <= 0xfffe; t++) { + if (dns_rdatatype_ismeta(t)) + continue; + dns_rdatatype_format(t, buf, sizeof(buf)); + if (strncmp(buf, "TYPE", 4) != 0) + fprintf(stdout, "%s\n", buf); + } + doexit = true; + break; + + case 'T': + for (t = 1; t <= 0xfeff; t++) { + if (dns_rdatatype_ismeta(t)) + continue; + dns_rdatatype_format(t, buf, sizeof(buf)); + if (strncmp(buf, "TYPE", 4) != 0) + fprintf(stdout, "%s\n", buf); + } + doexit = true; + break; + + case 'V': + version(); + doexit = true; + break; + + default: + break; + } + } + if (doexit) + exit(0); + isc_commandline_reset = true; + isc_commandline_index = 1; +} + +static void +parse_args(int argc, char **argv, isc_mem_t *mctx, isc_entropy_t **ectx) { + int ch; + uint32_t i; + isc_result_t result; + bool force_interactive = false; + + debug("parse_args"); + while ((ch = isc_commandline_parse(argc, argv, PARSE_ARGS_FMT)) != -1) { + switch (ch) { + case 'd': + debugging = true; + break; + case 'D': /* was -dd */ + debugging = true; + ddebugging = true; + break; + case 'M': + break; + case 'i': + force_interactive = true; + interactive = true; + break; + case 'l': + local_only = true; + break; + case 'L': + result = isc_parse_uint32(&i, isc_commandline_argument, + 10); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "bad library debug value " + "'%s'\n", isc_commandline_argument); + exit(1); + } + logdebuglevel = i; + break; + case 'y': + keystr = isc_commandline_argument; + break; + case 'v': + usevc = true; + break; + case 'k': + keyfile = isc_commandline_argument; + break; + case 'g': + usegsstsig = true; + use_win2k_gsstsig = false; + break; + case 'o': + usegsstsig = true; + use_win2k_gsstsig = true; + break; + case 'p': + result = isc_parse_uint16(&dnsport, + isc_commandline_argument, 10); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "bad port number " + "'%s'\n", isc_commandline_argument); + exit(1); + } + break; + case 't': + result = isc_parse_uint32(&timeout, + isc_commandline_argument, 10); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "bad timeout '%s'\n", isc_commandline_argument); + exit(1); + } + if (timeout == 0) + timeout = UINT_MAX; + break; + case 'u': + result = isc_parse_uint32(&udp_timeout, + isc_commandline_argument, 10); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "bad udp timeout '%s'\n", isc_commandline_argument); + exit(1); + } + if (udp_timeout == 0) + udp_timeout = UINT_MAX; + break; + case 'r': + result = isc_parse_uint32(&udp_retries, + isc_commandline_argument, 10); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "bad udp retries '%s'\n", isc_commandline_argument); + exit(1); + } + break; + + case 'R': + setup_entropy(mctx, isc_commandline_argument, ectx); + break; + + default: + fprintf(stderr, "%s: unhandled option: %c\n", + argv[0], isc_commandline_option); + exit(1); + } + } + if (keyfile != NULL && keystr != NULL) { + fprintf(stderr, "%s: cannot specify both -k and -y\n", + argv[0]); + exit(1); + } + +#ifdef GSSAPI + if (usegsstsig && (keyfile != NULL || keystr != NULL)) { + fprintf(stderr, "%s: cannot specify -g with -k or -y\n", + argv[0]); + exit(1); + } +#else + if (usegsstsig) { + fprintf(stderr, "%s: cannot specify -g or -o, " \ + "program not linked with GSS API Library\n", + argv[0]); + exit(1); + } +#endif + + if (argv[isc_commandline_index] != NULL) { + if (strcmp(argv[isc_commandline_index], "-") == 0) { + input = stdin; + } else { + result = isc_stdio_open(argv[isc_commandline_index], + "r", &input); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not open '%s': %s\n", + argv[isc_commandline_index], + isc_result_totext(result)); + exit(1); + } + } + if (!force_interactive) { + interactive = false; + } + } +} + +static uint16_t +parse_name(char **cmdlinep, dns_message_t *msg, dns_name_t **namep) { + isc_result_t result; + char *word; + isc_buffer_t *namebuf = NULL; + isc_buffer_t source; + + word = nsu_strsep(cmdlinep, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read owner name\n"); + return (STATUS_SYNTAX); + } + + result = dns_message_gettempname(msg, namep); + check_result(result, "dns_message_gettempname"); + result = isc_buffer_allocate(gmctx, &namebuf, DNS_NAME_MAXWIRE); + check_result(result, "isc_buffer_allocate"); + dns_name_init(*namep, NULL); + dns_name_setbuffer(*namep, namebuf); + dns_message_takebuffer(msg, &namebuf); + isc_buffer_init(&source, word, strlen(word)); + isc_buffer_add(&source, strlen(word)); + result = dns_name_fromtext(*namep, &source, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + error("invalid owner name: %s", isc_result_totext(result)); + isc_buffer_invalidate(&source); + dns_message_puttempname(msg, namep); + return (STATUS_SYNTAX); + } + isc_buffer_invalidate(&source); + return (STATUS_MORE); +} + +static uint16_t +parse_rdata(char **cmdlinep, dns_rdataclass_t rdataclass, + dns_rdatatype_t rdatatype, dns_message_t *msg, + 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; + + if (cmdline == NULL) { + rdata->flags = DNS_RDATA_UPDATE; + return (STATUS_MORE); + } + + while (*cmdline != 0 && isspace((unsigned char)*cmdline)) + cmdline++; + + if (*cmdline != 0) { + dns_rdatacallbacks_init(&callbacks); + result = isc_lex_create(gmctx, 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(gmctx, &buf, MAXWIRE); + check_result(result, "isc_buffer_allocate"); + result = dns_rdata_fromtext(NULL, rdataclass, rdatatype, lex, + dns_rootname, 0, gmctx, buf, + &callbacks); + isc_lex_destroy(&lex); + if (result == ISC_R_SUCCESS) { + isc_buffer_usedregion(buf, &r); + result = isc_buffer_allocate(gmctx, &newbuf, r.length); + check_result(result, "isc_buffer_allocate"); + isc_buffer_putmem(newbuf, r.base, r.length); + isc_buffer_usedregion(newbuf, &r); + dns_rdata_fromregion(rdata, rdataclass, rdatatype, &r); + isc_buffer_free(&buf); + dns_message_takebuffer(msg, &newbuf); + } else { + fprintf(stderr, "invalid rdata format: %s\n", + isc_result_totext(result)); + isc_buffer_free(&buf); + return (STATUS_SYNTAX); + } + } else { + rdata->flags = DNS_RDATA_UPDATE; + } + *cmdlinep = cmdline; + return (STATUS_MORE); +} + +static uint16_t +make_prereq(char *cmdline, bool ispositive, bool isrrset) { + isc_result_t result; + char *word; + dns_name_t *name = NULL; + 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; + uint16_t retval; + + ddebug("make_prereq()"); + + /* + * Read the owner name + */ + retval = parse_name(&cmdline, updatemsg, &name); + if (retval != STATUS_MORE) + return (retval); + + /* + * 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"); + goto failure; + } + region.base = word; + region.length = strlen(word); + result = dns_rdataclass_fromtext(&rdataclass, ®ion); + if (result == ISC_R_SUCCESS) { + if (!setzoneclass(rdataclass)) { + fprintf(stderr, "class mismatch: %s\n", word); + goto failure; + } + /* + * Now read the type. + */ + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read type\n"); + goto failure; + } + region.base = word; + region.length = strlen(word); + result = dns_rdatatype_fromtext(&rdatatype, ®ion); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "invalid type: %s\n", word); + goto failure; + } + } else { + rdataclass = getzoneclass(); + result = dns_rdatatype_fromtext(&rdatatype, ®ion); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "invalid type: %s\n", word); + goto failure; + } + } + } else + rdatatype = dns_rdatatype_any; + + result = dns_message_gettemprdata(updatemsg, &rdata); + check_result(result, "dns_message_gettemprdata"); + + dns_rdata_init(rdata); + + if (isrrset && ispositive) { + retval = parse_rdata(&cmdline, rdataclass, rdatatype, + updatemsg, rdata); + if (retval != STATUS_MORE) + goto failure; + } else + rdata->flags = DNS_RDATA_UPDATE; + + result = dns_message_gettemprdatalist(updatemsg, &rdatalist); + check_result(result, "dns_message_gettemprdatalist"); + result = dns_message_gettemprdataset(updatemsg, &rdataset); + check_result(result, "dns_message_gettemprdataset"); + 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); + dns_rdatalist_tordataset(rdatalist, rdataset); + ISC_LIST_INIT(name->list); + ISC_LIST_APPEND(name->list, rdataset, link); + dns_message_addname(updatemsg, name, DNS_SECTION_PREREQUISITE); + return (STATUS_MORE); + + failure: + if (name != NULL) + dns_message_puttempname(updatemsg, &name); + return (STATUS_SYNTAX); +} + +static uint16_t +evaluate_prereq(char *cmdline) { + char *word; + bool ispositive, isrrset; + + ddebug("evaluate_prereq()"); + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read operation code\n"); + return (STATUS_SYNTAX); + } + 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); + return (STATUS_SYNTAX); + } + return (make_prereq(cmdline, ispositive, isrrset)); +} + +static uint16_t +evaluate_server(char *cmdline) { + char *word, *server; + long port; + + if (local_only) { + fprintf(stderr, "cannot reset server in localhost-only mode\n"); + return (STATUS_SYNTAX); + } + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read server name\n"); + return (STATUS_SYNTAX); + } + server = word; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) + port = dnsport; + else { + char *endp; + port = strtol(word, &endp, 10); + if (*endp != 0) { + fprintf(stderr, "port '%s' is not numeric\n", word); + return (STATUS_SYNTAX); + } else if (port < 1 || port > 65535) { + fprintf(stderr, "port '%s' is out of range " + "(1 to 65535)\n", word); + return (STATUS_SYNTAX); + } + } + + if (servers != NULL) { + if (master_servers == servers) + master_servers = NULL; + isc_mem_put(gmctx, servers, ns_alloc * sizeof(isc_sockaddr_t)); + } + + default_servers = false; + + ns_alloc = MAX_SERVERADDRS; + ns_inuse = 0; + servers = isc_mem_get(gmctx, ns_alloc * sizeof(isc_sockaddr_t)); + if (servers == NULL) + fatal("out of memory"); + + memset(servers, 0, ns_alloc * sizeof(isc_sockaddr_t)); + ns_total = get_addresses(server, (in_port_t)port, servers, ns_alloc); + if (ns_total == 0) { + return (STATUS_SYNTAX); + } + + return (STATUS_MORE); +} + +static uint16_t +evaluate_local(char *cmdline) { + char *word, *local; + long port; + struct in_addr in4; + struct in6_addr in6; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read server name\n"); + return (STATUS_SYNTAX); + } + local = word; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) + port = 0; + else { + char *endp; + port = strtol(word, &endp, 10); + if (*endp != 0) { + fprintf(stderr, "port '%s' is not numeric\n", word); + return (STATUS_SYNTAX); + } else if (port < 1 || port > 65535) { + fprintf(stderr, "port '%s' is out of range " + "(1 to 65535)\n", word); + return (STATUS_SYNTAX); + } + } + + if (have_ipv6 && inet_pton(AF_INET6, local, &in6) == 1) { + if (localaddr6 == NULL) + localaddr6 = isc_mem_get(gmctx, sizeof(isc_sockaddr_t)); + if (localaddr6 == NULL) + fatal("out of memory"); + isc_sockaddr_fromin6(localaddr6, &in6, (in_port_t)port); + } else if (have_ipv4 && inet_pton(AF_INET, local, &in4) == 1) { + if (localaddr4 == NULL) + localaddr4 = isc_mem_get(gmctx, sizeof(isc_sockaddr_t)); + if (localaddr4 == NULL) + fatal("out of memory"); + isc_sockaddr_fromin(localaddr4, &in4, (in_port_t)port); + } else { + fprintf(stderr, "invalid address %s", local); + return (STATUS_SYNTAX); + } + + return (STATUS_MORE); +} + +static uint16_t +evaluate_key(char *cmdline) { + char *namestr; + char *secretstr; + isc_buffer_t b; + isc_result_t result; + dns_fixedname_t fkeyname; + dns_name_t *mykeyname; + int secretlen; + unsigned char *secret = NULL; + isc_buffer_t secretbuf; + dns_name_t *hmacname = NULL; + uint16_t digestbits = 0; + char *n; + + namestr = nsu_strsep(&cmdline, " \t\r\n"); + if (namestr == NULL || *namestr == 0) { + fprintf(stderr, "could not read key name\n"); + return (STATUS_SYNTAX); + } + + mykeyname = dns_fixedname_initname(&fkeyname); + + n = strchr(namestr, ':'); + if (n != NULL) { + if (!parse_hmac(&hmacname, namestr, n - namestr, + &digestbits)) { + return (STATUS_SYNTAX); + } + namestr = n + 1; + } else +#ifndef PK11_MD5_DISABLE + hmacname = DNS_TSIG_HMACMD5_NAME; +#else + hmacname = DNS_TSIG_HMACSHA256_NAME; +#endif + + isc_buffer_init(&b, namestr, strlen(namestr)); + isc_buffer_add(&b, strlen(namestr)); + result = dns_name_fromtext(mykeyname, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not parse key name\n"); + return (STATUS_SYNTAX); + } + + secretstr = nsu_strsep(&cmdline, "\r\n"); + if (secretstr == NULL || *secretstr == 0) { + fprintf(stderr, "could not read key secret\n"); + return (STATUS_SYNTAX); + } + secretlen = strlen(secretstr) * 3 / 4; + secret = isc_mem_allocate(gmctx, secretlen); + if (secret == NULL) + fatal("out of memory"); + + isc_buffer_init(&secretbuf, secret, secretlen); + result = isc_base64_decodestring(secretstr, &secretbuf); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not create key from %s: %s\n", + secretstr, isc_result_totext(result)); + isc_mem_free(gmctx, secret); + return (STATUS_SYNTAX); + } + secretlen = isc_buffer_usedlength(&secretbuf); + + if (tsigkey != NULL) + dns_tsigkey_detach(&tsigkey); + result = dns_tsigkey_create(mykeyname, hmacname, secret, secretlen, + false, NULL, 0, 0, gmctx, NULL, + &tsigkey); + isc_mem_free(gmctx, secret); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not create key from %s %s: %s\n", + namestr, secretstr, dns_result_totext(result)); + return (STATUS_SYNTAX); + } + dst_key_setbits(tsigkey->key, digestbits); + return (STATUS_MORE); +} + +static uint16_t +evaluate_zone(char *cmdline) { + char *word; + isc_buffer_t b; + isc_result_t result; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read zone name\n"); + return (STATUS_SYNTAX); + } + + userzone = dns_fixedname_initname(&fuserzone); + isc_buffer_init(&b, word, strlen(word)); + isc_buffer_add(&b, strlen(word)); + result = dns_name_fromtext(userzone, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + userzone = NULL; /* Lest it point to an invalid name */ + fprintf(stderr, "could not parse zone name\n"); + return (STATUS_SYNTAX); + } + + return (STATUS_MORE); +} + +static uint16_t +evaluate_realm(char *cmdline) { +#ifdef GSSAPI + char *word; + char buf[1024]; + int n; + + if (realm != NULL) { + isc_mem_free(gmctx, realm); + realm = NULL; + } + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) + return (STATUS_MORE); + + n = snprintf(buf, sizeof(buf), "@%s", word); + if (n < 0 || (size_t)n >= sizeof(buf)) { + error("realm is too long"); + return (STATUS_SYNTAX); + } + realm = isc_mem_strdup(gmctx, buf); + if (realm == NULL) + fatal("out of memory"); + return (STATUS_MORE); +#else + UNUSED(cmdline); + return (STATUS_SYNTAX); +#endif +} + +static uint16_t +evaluate_ttl(char *cmdline) { + char *word; + isc_result_t result; + uint32_t ttl; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read ttl\n"); + return (STATUS_SYNTAX); + } + + if (!strcasecmp(word, "none")) { + default_ttl = 0; + default_ttl_set = false; + return (STATUS_MORE); + } + + result = isc_parse_uint32(&ttl, word, 10); + if (result != ISC_R_SUCCESS) + return (STATUS_SYNTAX); + + if (ttl > TTL_MAX) { + fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n", + word, TTL_MAX); + return (STATUS_SYNTAX); + } + default_ttl = ttl; + default_ttl_set = true; + + return (STATUS_MORE); +} + +static uint16_t +evaluate_class(char *cmdline) { + char *word; + isc_textregion_t r; + isc_result_t result; + dns_rdataclass_t rdclass; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read class name\n"); + return (STATUS_SYNTAX); + } + + r.base = word; + r.length = strlen(word); + result = dns_rdataclass_fromtext(&rdclass, &r); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not parse class name: %s\n", word); + return (STATUS_SYNTAX); + } + switch (rdclass) { + case dns_rdataclass_none: + case dns_rdataclass_any: + case dns_rdataclass_reserved0: + fprintf(stderr, "bad default class: %s\n", word); + return (STATUS_SYNTAX); + default: + defaultclass = rdclass; + } + + return (STATUS_MORE); +} + +static uint16_t +update_addordelete(char *cmdline, bool isdelete) { + isc_result_t result; + dns_name_t *name = NULL; + 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; + uint16_t retval; + + ddebug("update_addordelete()"); + + /* + * Read the owner name. + */ + retval = parse_name(&cmdline, updatemsg, &name); + if (retval != STATUS_MORE) + return (retval); + + result = dns_message_gettemprdata(updatemsg, &rdata); + check_result(result, "dns_message_gettemprdata"); + + 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"); + goto failure; + } + 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 if (default_ttl_set) { + ttl = default_ttl; + goto parseclass; + } else { + fprintf(stderr, "ttl '%s': %s\n", word, + isc_result_totext(result)); + goto failure; + } + } + + if (isdelete) + ttl = 0; + else if (ttl > TTL_MAX) { + fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n", + word, TTL_MAX); + goto failure; + } + + /* + * 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"); + goto failure; + } + } + region.base = word; + region.length = strlen(word); + rdataclass = dns_rdataclass_any; + result = dns_rdataclass_fromtext(&rdataclass, ®ion); + if (result == ISC_R_SUCCESS && rdataclass != dns_rdataclass_any) { + if (!setzoneclass(rdataclass)) { + fprintf(stderr, "class mismatch: %s\n", word); + goto failure; + } + /* + * 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"); + goto failure; + } + } + region.base = word; + region.length = strlen(word); + result = dns_rdatatype_fromtext(&rdatatype, ®ion); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "'%s' is not a valid type: %s\n", + word, isc_result_totext(result)); + goto failure; + } + } else { + rdataclass = getzoneclass(); + result = dns_rdatatype_fromtext(&rdatatype, ®ion); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "'%s' is not a valid class or type: " + "%s\n", word, isc_result_totext(result)); + goto failure; + } + } + + retval = parse_rdata(&cmdline, rdataclass, rdatatype, updatemsg, + rdata); + if (retval != STATUS_MORE) + goto failure; + + 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"); + goto failure; + } + } + + if (!isdelete && checknames) { + dns_fixedname_t fixed; + dns_name_t *bad; + + if (!dns_rdata_checkowner(name, rdata->rdclass, rdata->type, + true)) + { + char namebuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "check-names failed: bad owner '%s'\n", + namebuf); + goto failure; + } + + bad = dns_fixedname_initname(&fixed); + if (!dns_rdata_checknames(rdata, name, bad)) { + char namebuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(bad, namebuf, sizeof(namebuf)); + fprintf(stderr, "check-names failed: bad name '%s'\n", + namebuf); + goto failure; + } + } + + doneparsing: + + result = dns_message_gettemprdatalist(updatemsg, &rdatalist); + check_result(result, "dns_message_gettemprdatalist"); + result = dns_message_gettemprdataset(updatemsg, &rdataset); + check_result(result, "dns_message_gettemprdataset"); + rdatalist->type = rdatatype; + rdatalist->rdclass = rdataclass; + rdatalist->covers = rdatatype; + rdatalist->ttl = (dns_ttl_t)ttl; + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + dns_rdatalist_tordataset(rdatalist, rdataset); + ISC_LIST_INIT(name->list); + ISC_LIST_APPEND(name->list, rdataset, link); + dns_message_addname(updatemsg, name, DNS_SECTION_UPDATE); + return (STATUS_MORE); + + failure: + if (name != NULL) + dns_message_puttempname(updatemsg, &name); + dns_message_puttemprdata(updatemsg, &rdata); + return (STATUS_SYNTAX); +} + +static uint16_t +evaluate_update(char *cmdline) { + char *word; + bool isdelete; + + ddebug("evaluate_update()"); + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read operation code\n"); + return (STATUS_SYNTAX); + } + if (strcasecmp(word, "delete") == 0) + isdelete = true; + else if (strcasecmp(word, "del") == 0) + isdelete = true; + else if (strcasecmp(word, "add") == 0) + isdelete = false; + else { + fprintf(stderr, "incorrect operation code: %s\n", word); + return (STATUS_SYNTAX); + } + return (update_addordelete(cmdline, isdelete)); +} + +static uint16_t +evaluate_checknames(char *cmdline) { + char *word; + + ddebug("evaluate_checknames()"); + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read check-names directive\n"); + return (STATUS_SYNTAX); + } + if (strcasecmp(word, "yes") == 0 || + strcasecmp(word, "true") == 0 || + strcasecmp(word, "on") == 0) { + checknames = true; + } else if (strcasecmp(word, "no") == 0 || + strcasecmp(word, "false") == 0 || + strcasecmp(word, "off") == 0) { + checknames = false; + } else { + fprintf(stderr, "incorrect check-names directive: %s\n", word); + return (STATUS_SYNTAX); + } + return (STATUS_MORE); +} + +static void +setzone(dns_name_t *zonename) { + isc_result_t result; + dns_name_t *name = NULL; + dns_rdataset_t *rdataset = NULL; + + result = dns_message_firstname(updatemsg, DNS_SECTION_ZONE); + if (result == ISC_R_SUCCESS) { + dns_message_currentname(updatemsg, DNS_SECTION_ZONE, &name); + dns_message_removename(updatemsg, name, DNS_SECTION_ZONE); + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_HEAD(name->list)) { + ISC_LIST_UNLINK(name->list, rdataset, link); + dns_rdataset_disassociate(rdataset); + dns_message_puttemprdataset(updatemsg, &rdataset); + } + dns_message_puttempname(updatemsg, &name); + } + + if (zonename != NULL) { + result = dns_message_gettempname(updatemsg, &name); + check_result(result, "dns_message_gettempname"); + dns_name_init(name, NULL); + dns_name_clone(zonename, name); + result = dns_message_gettemprdataset(updatemsg, &rdataset); + check_result(result, "dns_message_gettemprdataset"); + dns_rdataset_makequestion(rdataset, getzoneclass(), + dns_rdatatype_soa); + ISC_LIST_INIT(name->list); + ISC_LIST_APPEND(name->list, rdataset, link); + dns_message_addname(updatemsg, name, DNS_SECTION_ZONE); + } +} + +static void +show_message(FILE *stream, dns_message_t *msg, const char *description) { + isc_result_t result; + isc_buffer_t *buf = NULL; + int bufsz; + + ddebug("show_message()"); + + setzone(userzone); + + bufsz = INITTEXT; + do { + if (bufsz > MAXTEXT) { + fprintf(stderr, "could not allocate large enough " + "buffer to display message\n"); + exit(1); + } + if (buf != NULL) + isc_buffer_free(&buf); + result = isc_buffer_allocate(gmctx, &buf, bufsz); + check_result(result, "isc_buffer_allocate"); + result = dns_message_totext(msg, style, 0, buf); + bufsz *= 2; + } while (result == ISC_R_NOSPACE); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not convert message to text format.\n"); + isc_buffer_free(&buf); + return; + } + fprintf(stream, "%s\n%.*s", description, + (int)isc_buffer_usedlength(buf), (char*)isc_buffer_base(buf)); + fflush(stream); + isc_buffer_free(&buf); +} + +static uint16_t +do_next_command(char *cmdline) { + char *word; + + ddebug("do_next_command()"); + word = nsu_strsep(&cmdline, " \t\r\n"); + + if (word == NULL || *word == 0) + return (STATUS_SEND); + if (word[0] == ';') + return (STATUS_MORE); + if (strcasecmp(word, "quit") == 0) + return (STATUS_QUIT); + if (strcasecmp(word, "prereq") == 0) + return (evaluate_prereq(cmdline)); + if (strcasecmp(word, "nxdomain") == 0) + return (make_prereq(cmdline, false, false)); + if (strcasecmp(word, "yxdomain") == 0) + return (make_prereq(cmdline, true, false)); + if (strcasecmp(word, "nxrrset") == 0) + return (make_prereq(cmdline, false, true)); + if (strcasecmp(word, "yxrrset") == 0) + return (make_prereq(cmdline, true, true)); + if (strcasecmp(word, "update") == 0) + return (evaluate_update(cmdline)); + if (strcasecmp(word, "delete") == 0) + return (update_addordelete(cmdline, true)); + if (strcasecmp(word, "del") == 0) + return (update_addordelete(cmdline, true)); + if (strcasecmp(word, "add") == 0) + return (update_addordelete(cmdline, false)); + if (strcasecmp(word, "server") == 0) + return (evaluate_server(cmdline)); + if (strcasecmp(word, "local") == 0) + return (evaluate_local(cmdline)); + if (strcasecmp(word, "zone") == 0) + return (evaluate_zone(cmdline)); + if (strcasecmp(word, "class") == 0) + return (evaluate_class(cmdline)); + if (strcasecmp(word, "send") == 0) + return (STATUS_SEND); + if (strcasecmp(word, "debug") == 0) { + if (debugging) + ddebugging = true; + else + debugging = true; + return (STATUS_MORE); + } + if (strcasecmp(word, "ttl") == 0) + return (evaluate_ttl(cmdline)); + if (strcasecmp(word, "show") == 0) { + show_message(stdout, updatemsg, "Outgoing update query:"); + return (STATUS_MORE); + } + if (strcasecmp(word, "answer") == 0) { + if (answer != NULL) + show_message(stdout, answer, "Answer:"); + return (STATUS_MORE); + } + if (strcasecmp(word, "key") == 0) { + usegsstsig = false; + return (evaluate_key(cmdline)); + } + if (strcasecmp(word, "realm") == 0) + return (evaluate_realm(cmdline)); + if (strcasecmp(word, "check-names") == 0 || + strcasecmp(word, "checknames") == 0) + return (evaluate_checknames(cmdline)); + if (strcasecmp(word, "gsstsig") == 0) { +#ifdef GSSAPI + usegsstsig = true; + use_win2k_gsstsig = false; +#else + fprintf(stderr, "gsstsig not supported\n"); +#endif + return (STATUS_MORE); + } + if (strcasecmp(word, "oldgsstsig") == 0) { +#ifdef GSSAPI + usegsstsig = true; + use_win2k_gsstsig = true; +#else + fprintf(stderr, "gsstsig not supported\n"); +#endif + return (STATUS_MORE); + } + if (strcasecmp(word, "help") == 0) { + fprintf(stdout, +"nsupdate " VERSION ":\n" +"local address [port] (set local resolver)\n" +"server address [port] (set master server for zone)\n" +"send (send the update request)\n" +"show (show the update request)\n" +"answer (show the answer to the last request)\n" +"quit (quit, any pending update is not sent\n" +"help (display this message_\n" +"key [hmac:]keyname secret (use TSIG to sign the request)\n" +"gsstsig (use GSS_TSIG to sign the request)\n" +"oldgsstsig (use Microsoft's GSS_TSIG to sign the request)\n" +"zone name (set the zone to be updated)\n" +"class CLASS (set the zone's DNS class, e.g. IN (default), CH)\n" +"check-names { on | off } (enable / disable check-names)\n" +"[prereq] nxdomain name (require that this name does not exist)\n" +"[prereq] yxdomain name (require that this name exists)\n" +"[prereq] nxrrset .... (require that this RRset does not exist)\n" +"[prereq] yxrrset .... (require that this RRset exists)\n" +"[update] add .... (add the given record to the zone)\n" +"[update] del[ete] .... (remove the given record(s) from the zone)\n"); + return (STATUS_MORE); + } + if (strcasecmp(word, "version") == 0) { + fprintf(stdout, "nsupdate " VERSION "\n"); + return (STATUS_MORE); + } + fprintf(stderr, "incorrect section name: %s\n", word); + return (STATUS_SYNTAX); +} + +static uint16_t +get_next_command(void) { + uint16_t result = STATUS_QUIT; + char cmdlinebuf[MAXCMD]; + char *cmdline; + + isc_app_block(); + if (interactive) { +#ifdef HAVE_READLINE + cmdline = readline("> "); + if (cmdline != NULL) + add_history(cmdline); +#else + fprintf(stdout, "> "); + fflush(stdout); + cmdline = fgets(cmdlinebuf, MAXCMD, input); +#endif + } else + cmdline = fgets(cmdlinebuf, MAXCMD, input); + isc_app_unblock(); + + if (cmdline != NULL) { + char *tmp = cmdline; + + /* + * Normalize input by removing any eol as readline() + * removes eol but fgets doesn't. + */ + (void)nsu_strsep(&tmp, "\r\n"); + result = do_next_command(cmdline); + } +#ifdef HAVE_READLINE + if (interactive) + free(cmdline); +#endif + return (result); +} + +static bool +user_interaction(void) { + uint16_t result = STATUS_MORE; + + ddebug("user_interaction()"); + while ((result == STATUS_MORE) || (result == STATUS_SYNTAX)) { + result = get_next_command(); + if (!interactive && result == STATUS_SYNTAX) + fatal("syntax error"); + } + if (result == STATUS_SEND) + return (true); + return (false); + +} + +static void +done_update(void) { + isc_event_t *event = global_event; + ddebug("done_update()"); + isc_task_send(global_task, &event); +} + +static void +check_tsig_error(dns_rdataset_t *rdataset, isc_buffer_t *b) { + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_any_tsig_t tsig; + + result = dns_rdataset_first(rdataset); + check_result(result, "dns_rdataset_first"); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &tsig, NULL); + check_result(result, "dns_rdata_tostruct"); + if (tsig.error != 0) { + if (isc_buffer_remaininglength(b) < 1) + check_result(ISC_R_NOSPACE, "isc_buffer_remaininglength"); + isc_buffer_putstr(b, "(" /*)*/); + result = dns_tsigrcode_totext(tsig.error, b); + check_result(result, "dns_tsigrcode_totext"); + if (isc_buffer_remaininglength(b) < 1) + check_result(ISC_R_NOSPACE, "isc_buffer_remaininglength"); + isc_buffer_putstr(b, /*(*/ ")"); + } +} + +static bool +next_master(const char *caller, isc_sockaddr_t *addr, isc_result_t eresult) { + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf)); + fprintf(stderr, "; Communication with %s failed: %s\n", + addrbuf, isc_result_totext(eresult)); + if (++master_inuse >= master_total) + return (false); + ddebug("%s: trying next server", caller); + return (true); +} + +static void +update_completed(isc_task_t *task, isc_event_t *event) { + dns_requestevent_t *reqev = NULL; + isc_result_t result; + dns_request_t *request; + + UNUSED(task); + + ddebug("update_completed()"); + + requests--; + + REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE); + reqev = (dns_requestevent_t *)event; + request = reqev->request; + + if (shuttingdown) { + dns_request_destroy(&request); + isc_event_free(&event); + maybeshutdown(); + return; + } + + if (reqev->result != ISC_R_SUCCESS) { + if (!next_master("update_completed", + &master_servers[master_inuse], + reqev->result)) + { + seenerror = true; + goto done; + } + + ddebug("Destroying request [%p]", request); + dns_request_destroy(&request); + dns_message_renderreset(updatemsg); + dns_message_settsigkey(updatemsg, NULL); + send_update(zname, &master_servers[master_inuse]); + isc_event_free(&event); + return; + } + + result = dns_message_create(gmctx, DNS_MESSAGE_INTENTPARSE, &answer); + check_result(result, "dns_message_create"); + result = dns_request_getresponse(request, answer, + DNS_MESSAGEPARSE_PRESERVEORDER); + 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 + if (usegsstsig && answer->rcode == dns_rcode_noerror) { + /* + * For MS DNS that violates RFC 2845, section 4.2 + */ + break; + } +#endif + fprintf(stderr, "; TSIG error with server: %s\n", + isc_result_totext(result)); + seenerror = true; + break; + default: + check_result(result, "dns_request_getresponse"); + } + + if (answer->rcode != dns_rcode_noerror) { + seenerror = true; + if (!debugging) { + char buf[64]; + isc_buffer_t b; + dns_rdataset_t *rds; + + isc_buffer_init(&b, buf, sizeof(buf) - 1); + result = dns_rcode_totext(answer->rcode, &b); + check_result(result, "dns_rcode_totext"); + rds = dns_message_gettsig(answer, NULL); + if (rds != NULL) + check_tsig_error(rds, &b); + fprintf(stderr, "update failed: %.*s\n", + (int)isc_buffer_usedlength(&b), buf); + } + } + if (debugging) + show_message(stderr, answer, "\nReply from update query:"); + + done: + dns_request_destroy(&request); + if (usegsstsig) { + dns_name_free(&tmpzonename, gmctx); + dns_name_free(&restart_master, gmctx); + } + isc_event_free(&event); + done_update(); +} + +static void +send_update(dns_name_t *zone, isc_sockaddr_t *master) { + isc_result_t result; + dns_request_t *request = NULL; + unsigned int options = DNS_REQUESTOPT_CASE; + isc_sockaddr_t *srcaddr; + + ddebug("send_update()"); + + setzone(zone); + + if (usevc) + options |= DNS_REQUESTOPT_TCP; + if (tsigkey == NULL && sig0key != NULL) { + result = dns_message_setsig0key(updatemsg, sig0key); + check_result(result, "dns_message_setsig0key"); + } + if (debugging) { + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(master, addrbuf, sizeof(addrbuf)); + fprintf(stderr, "Sending update to %s\n", addrbuf); + } + + if (isc_sockaddr_pf(master) == AF_INET6) + srcaddr = localaddr6; + else + srcaddr = localaddr4; + + /* Windows doesn't like the tsig name to be compressed. */ + if (updatemsg->tsigname) + updatemsg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; + + result = dns_request_createvia3(requestmgr, updatemsg, srcaddr, + master, options, tsigkey, timeout, + udp_timeout, udp_retries, global_task, + update_completed, NULL, &request); + check_result(result, "dns_request_createvia3"); + + if (debugging) + show_message(stdout, updatemsg, "Outgoing update query:"); + + requests++; +} + +static void +next_server(const char *caller, isc_sockaddr_t *addr, isc_result_t eresult) { + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf)); + fprintf(stderr, "; Communication with %s failed: %s\n", + addrbuf, isc_result_totext(eresult)); + if (++ns_inuse >= ns_total) + fatal("could not reach any name server"); + else + ddebug("%s: trying next server", caller); +} + +static void +recvsoa(isc_task_t *task, isc_event_t *event) { + dns_requestevent_t *reqev = NULL; + dns_request_t *request = NULL; + isc_result_t result, eresult; + dns_message_t *rcvmsg = NULL; + dns_section_t section; + dns_name_t *name = NULL; + dns_rdataset_t *soaset = NULL; + dns_rdata_soa_t soa; + dns_rdata_t soarr = DNS_RDATA_INIT; + int pass = 0; + dns_name_t master; + nsu_requestinfo_t *reqinfo; + dns_message_t *soaquery = NULL; + isc_sockaddr_t *addr; + isc_sockaddr_t *srcaddr; + bool seencname = false; + dns_name_t tname; + unsigned int nlabels; + + UNUSED(task); + + ddebug("recvsoa()"); + + requests--; + + REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE); + reqev = (dns_requestevent_t *)event; + request = reqev->request; + eresult = reqev->result; + reqinfo = reqev->ev_arg; + soaquery = reqinfo->msg; + addr = reqinfo->addr; + + if (shuttingdown) { + dns_request_destroy(&request); + dns_message_destroy(&soaquery); + isc_mem_put(gmctx, reqinfo, sizeof(nsu_requestinfo_t)); + isc_event_free(&event); + maybeshutdown(); + return; + } + + if (eresult != ISC_R_SUCCESS) { + next_server("recvsoa", addr, eresult); + ddebug("Destroying request [%p]", request); + dns_request_destroy(&request); + dns_message_renderreset(soaquery); + dns_message_settsigkey(soaquery, NULL); + sendrequest(&servers[ns_inuse], soaquery, &request); + isc_mem_put(gmctx, reqinfo, sizeof(nsu_requestinfo_t)); + isc_event_free(&event); + setzoneclass(dns_rdataclass_none); + return; + } + + isc_mem_put(gmctx, reqinfo, sizeof(nsu_requestinfo_t)); + reqinfo = NULL; + isc_event_free(&event); + reqev = NULL; + + ddebug("About to create rcvmsg"); + result = dns_message_create(gmctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg); + check_result(result, "dns_message_create"); + result = dns_request_getresponse(request, rcvmsg, + DNS_MESSAGEPARSE_PRESERVEORDER); + if (result == DNS_R_TSIGERRORSET && servers != NULL) { + dns_message_destroy(&rcvmsg); + ddebug("Destroying request [%p]", request); + dns_request_destroy(&request); + reqinfo = isc_mem_get(gmctx, sizeof(nsu_requestinfo_t)); + if (reqinfo == NULL) + fatal("out of memory"); + reqinfo->msg = soaquery; + reqinfo->addr = addr; + dns_message_renderreset(soaquery); + ddebug("retrying soa request without TSIG"); + + if (isc_sockaddr_pf(addr) == AF_INET6) + srcaddr = localaddr6; + else + srcaddr = localaddr4; + + result = dns_request_createvia3(requestmgr, soaquery, srcaddr, + addr, 0, NULL, + FIND_TIMEOUT * 20, + FIND_TIMEOUT, 3, + global_task, recvsoa, reqinfo, + &request); + check_result(result, "dns_request_createvia3"); + requests++; + return; + } + check_result(result, "dns_request_getresponse"); + section = DNS_SECTION_ANSWER; + POST(section); + if (debugging) + show_message(stderr, rcvmsg, "Reply from SOA query:"); + + if (rcvmsg->rcode != dns_rcode_noerror && + rcvmsg->rcode != dns_rcode_nxdomain) + fatal("response to SOA query was unsuccessful"); + + if (userzone != NULL && rcvmsg->rcode == dns_rcode_nxdomain) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(userzone, namebuf, sizeof(namebuf)); + error("specified zone '%s' does not exist (NXDOMAIN)", + namebuf); + dns_message_destroy(&rcvmsg); + dns_request_destroy(&request); + dns_message_destroy(&soaquery); + ddebug("Out of recvsoa"); + done_update(); + seenerror = true; + return; + } + + lookforsoa: + if (pass == 0) + section = DNS_SECTION_ANSWER; + else if (pass == 1) + section = DNS_SECTION_AUTHORITY; + else + goto droplabel; + + result = dns_message_firstname(rcvmsg, section); + if (result != ISC_R_SUCCESS) { + pass++; + goto lookforsoa; + } + while (result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(rcvmsg, section, &name); + soaset = NULL; + result = dns_message_findtype(name, dns_rdatatype_soa, 0, + &soaset); + if (result == ISC_R_SUCCESS) + break; + if (section == DNS_SECTION_ANSWER) { + dns_rdataset_t *tset = NULL; + if (dns_message_findtype(name, dns_rdatatype_cname, 0, + &tset) == ISC_R_SUCCESS || + dns_message_findtype(name, dns_rdatatype_dname, 0, + &tset) == ISC_R_SUCCESS ) { + seencname = true; + break; + } + } + + result = dns_message_nextname(rcvmsg, section); + } + + if (soaset == NULL && !seencname) { + pass++; + goto lookforsoa; + } + + if (seencname) + goto droplabel; + + if (debugging) { + char namestr[DNS_NAME_FORMATSIZE]; + dns_name_format(name, namestr, sizeof(namestr)); + fprintf(stderr, "Found zone name: %s\n", namestr); + } + + result = dns_rdataset_first(soaset); + check_result(result, "dns_rdataset_first"); + + dns_rdata_init(&soarr); + dns_rdataset_current(soaset, &soarr); + result = dns_rdata_tostruct(&soarr, &soa, NULL); + check_result(result, "dns_rdata_tostruct"); + + dns_name_init(&master, NULL); + dns_name_clone(&soa.origin, &master); + + if (userzone != NULL) { + zname = userzone; + } else { + /* + * Save the zone name in case we need to try a second + * address. + */ + zname = dns_fixedname_initname(&fzname); + dns_name_copy(name, zname, NULL); + } + + if (debugging) { + char namestr[DNS_NAME_FORMATSIZE]; + dns_name_format(&master, namestr, sizeof(namestr)); + fprintf(stderr, "The master is: %s\n", namestr); + } + + if (default_servers) { + char serverstr[DNS_NAME_MAXTEXT+1]; + isc_buffer_t buf; + size_t size; + + isc_buffer_init(&buf, serverstr, sizeof(serverstr)); + result = dns_name_totext(&master, true, &buf); + check_result(result, "dns_name_totext"); + serverstr[isc_buffer_usedlength(&buf)] = 0; + + if (master_servers != NULL && master_servers != servers) + isc_mem_put(gmctx, master_servers, + master_alloc * sizeof(isc_sockaddr_t)); + master_alloc = MAX_SERVERADDRS; + size = master_alloc * sizeof(isc_sockaddr_t); + master_servers = isc_mem_get(gmctx, size); + if (master_servers == NULL) + fatal("out of memory"); + + memset(master_servers, 0, size); + master_total = get_addresses(serverstr, dnsport, + master_servers, master_alloc); + if (master_total == 0) { + exit(1); + } + master_inuse = 0; + } else + master_from_servers(); + dns_rdata_freestruct(&soa); + +#ifdef GSSAPI + if (usegsstsig) { + dns_name_init(&tmpzonename, NULL); + dns_name_dup(zname, gmctx, &tmpzonename); + dns_name_init(&restart_master, NULL); + dns_name_dup(&master, gmctx, &restart_master); + start_gssrequest(&master); + } else { + send_update(zname, &master_servers[master_inuse]); + setzoneclass(dns_rdataclass_none); + } +#else + send_update(zname, &master_servers[master_inuse]); + setzoneclass(dns_rdataclass_none); +#endif + + dns_message_destroy(&soaquery); + dns_request_destroy(&request); + + out: + dns_message_destroy(&rcvmsg); + ddebug("Out of recvsoa"); + return; + + droplabel: + result = dns_message_firstname(soaquery, DNS_SECTION_QUESTION); + INSIST(result == ISC_R_SUCCESS); + name = NULL; + dns_message_currentname(soaquery, DNS_SECTION_QUESTION, &name); + nlabels = dns_name_countlabels(name); + if (nlabels == 1) + fatal("could not find enclosing zone"); + dns_name_init(&tname, NULL); + dns_name_getlabelsequence(name, 1, nlabels - 1, &tname); + dns_name_clone(&tname, name); + dns_request_destroy(&request); + dns_message_renderreset(soaquery); + dns_message_settsigkey(soaquery, NULL); + sendrequest(&servers[ns_inuse], soaquery, &request); + goto out; +} + +static void +sendrequest(isc_sockaddr_t *destaddr, dns_message_t *msg, + dns_request_t **request) +{ + isc_result_t result; + nsu_requestinfo_t *reqinfo; + isc_sockaddr_t *srcaddr; + + reqinfo = isc_mem_get(gmctx, sizeof(nsu_requestinfo_t)); + if (reqinfo == NULL) + fatal("out of memory"); + reqinfo->msg = msg; + reqinfo->addr = destaddr; + + if (isc_sockaddr_pf(destaddr) == AF_INET6) + srcaddr = localaddr6; + else + srcaddr = localaddr4; + + result = dns_request_createvia3(requestmgr, msg, srcaddr, destaddr, 0, + default_servers ? NULL : tsigkey, + FIND_TIMEOUT * 20, FIND_TIMEOUT, 3, + global_task, recvsoa, reqinfo, request); + check_result(result, "dns_request_createvia3"); + requests++; +} + +#ifdef GSSAPI + +/* + * Get the realm from the users kerberos ticket if possible + */ +static void +get_ticket_realm(isc_mem_t *mctx) { + krb5_context ctx; + krb5_error_code rc; + krb5_ccache ccache; + krb5_principal princ; + char *name; + const char * ticket_realm; + + rc = krb5_init_context(&ctx); + if (rc != 0) + return; + + rc = krb5_cc_default(ctx, &ccache); + if (rc != 0) { + krb5_free_context(ctx); + return; + } + + rc = krb5_cc_get_principal(ctx, ccache, &princ); + if (rc != 0) { + krb5_cc_close(ctx, ccache); + krb5_free_context(ctx); + return; + } + + rc = krb5_unparse_name(ctx, princ, &name); + if (rc != 0) { + krb5_free_principal(ctx, princ); + krb5_cc_close(ctx, ccache); + krb5_free_context(ctx); + return; + } + + ticket_realm = strrchr(name, '@'); + if (ticket_realm != NULL) { + realm = isc_mem_strdup(mctx, ticket_realm); + } + + free(name); + krb5_free_principal(ctx, princ); + krb5_cc_close(ctx, ccache); + krb5_free_context(ctx); + if (realm != NULL && debugging) + fprintf(stderr, "Found realm from ticket: %s\n", realm+1); +} + +static void +failed_gssrequest() { + seenerror = true; + + dns_name_free(&tmpzonename, gmctx); + dns_name_free(&restart_master, gmctx); + + done_update(); +} + +static void +start_gssrequest(dns_name_t *master) { + gss_ctx_id_t context; + isc_buffer_t buf; + isc_result_t result; + uint32_t val = 0; + dns_message_t *rmsg = NULL; + dns_request_t *request = NULL; + dns_name_t *servname; + dns_fixedname_t fname; + char namestr[DNS_NAME_FORMATSIZE]; + char mykeystr[DNS_NAME_FORMATSIZE]; + char *err_message = NULL; + + debug("start_gssrequest"); + usevc = true; + + if (gssring != NULL) + dns_tsigkeyring_detach(&gssring); + gssring = NULL; + result = dns_tsigkeyring_create(gmctx, &gssring); + + if (result != ISC_R_SUCCESS) + fatal("dns_tsigkeyring_create failed: %s", + isc_result_totext(result)); + + dns_name_format(master, namestr, sizeof(namestr)); + if (kserver == NULL) { + kserver = isc_mem_get(gmctx, sizeof(isc_sockaddr_t)); + if (kserver == NULL) + fatal("out of memory"); + } + + memmove(kserver, &master_servers[master_inuse], sizeof(isc_sockaddr_t)); + + servname = dns_fixedname_initname(&fname); + + if (realm == NULL) + get_ticket_realm(gmctx); + + result = isc_string_printf(servicename, sizeof(servicename), + "DNS/%s%s", namestr, realm ? realm : ""); + if (result != ISC_R_SUCCESS) + fatal("isc_string_printf(servicename) failed: %s", + isc_result_totext(result)); + isc_buffer_init(&buf, servicename, strlen(servicename)); + isc_buffer_add(&buf, strlen(servicename)); + result = dns_name_fromtext(servname, &buf, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) + fatal("dns_name_fromtext(servname) failed: %s", + isc_result_totext(result)); + + keyname = dns_fixedname_initname(&fkname); + + isc_random_get(&val); + result = isc_string_printf(mykeystr, sizeof(mykeystr), "%u.sig-%s", + val, namestr); + if (result != ISC_R_SUCCESS) + fatal("isc_string_printf(mykeystr) failed: %s", + isc_result_totext(result)); + isc_buffer_init(&buf, mykeystr, strlen(mykeystr)); + isc_buffer_add(&buf, strlen(mykeystr)); + + result = dns_name_fromtext(keyname, &buf, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) + fatal("dns_name_fromtext(keyname) failed: %s", + isc_result_totext(result)); + + /* Windows doesn't recognize name compression in the key name. */ + keyname->attributes |= DNS_NAMEATTR_NOCOMPRESS; + + rmsg = NULL; + result = dns_message_create(gmctx, DNS_MESSAGE_INTENTRENDER, &rmsg); + if (result != ISC_R_SUCCESS) + fatal("dns_message_create failed: %s", + isc_result_totext(result)); + + /* Build first request. */ + context = GSS_C_NO_CONTEXT; + result = dns_tkey_buildgssquery(rmsg, keyname, servname, NULL, 0, + &context, use_win2k_gsstsig, + gmctx, &err_message); + if (result == ISC_R_FAILURE) { + fprintf(stderr, "tkey query failed: %s\n", + err_message != NULL ? err_message : "unknown error"); + goto failure; + } + if (result != ISC_R_SUCCESS) + fatal("dns_tkey_buildgssquery failed: %s", + isc_result_totext(result)); + + send_gssrequest(kserver, rmsg, &request, context); + return; + +failure: + if (rmsg != NULL) + dns_message_destroy(&rmsg); + if (err_message != NULL) + isc_mem_free(gmctx, err_message); + failed_gssrequest(); +} + +static void +send_gssrequest(isc_sockaddr_t *destaddr, dns_message_t *msg, + dns_request_t **request, gss_ctx_id_t context) +{ + isc_result_t result; + nsu_gssinfo_t *reqinfo; + unsigned int options = 0; + isc_sockaddr_t *srcaddr; + + debug("send_gssrequest"); + reqinfo = isc_mem_get(gmctx, sizeof(nsu_gssinfo_t)); + if (reqinfo == NULL) + fatal("out of memory"); + reqinfo->msg = msg; + reqinfo->addr = destaddr; + reqinfo->context = context; + + options |= DNS_REQUESTOPT_TCP; + + if (isc_sockaddr_pf(destaddr) == AF_INET6) + srcaddr = localaddr6; + else + srcaddr = localaddr4; + + result = dns_request_createvia3(requestmgr, msg, srcaddr, destaddr, + options, tsigkey, FIND_TIMEOUT * 20, + FIND_TIMEOUT, 3, global_task, recvgss, + reqinfo, request); + check_result(result, "dns_request_createvia3"); + if (debugging) + show_message(stdout, msg, "Outgoing update query:"); + requests++; +} + +static void +recvgss(isc_task_t *task, isc_event_t *event) { + dns_requestevent_t *reqev = NULL; + dns_request_t *request = NULL; + isc_result_t result, eresult; + dns_message_t *rcvmsg = NULL; + nsu_gssinfo_t *reqinfo; + dns_message_t *tsigquery = NULL; + isc_sockaddr_t *addr; + gss_ctx_id_t context; + isc_buffer_t buf; + dns_name_t *servname; + dns_fixedname_t fname; + char *err_message = NULL; + + UNUSED(task); + + ddebug("recvgss()"); + + requests--; + + REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE); + reqev = (dns_requestevent_t *)event; + request = reqev->request; + eresult = reqev->result; + reqinfo = reqev->ev_arg; + tsigquery = reqinfo->msg; + context = reqinfo->context; + addr = reqinfo->addr; + + if (shuttingdown) { + dns_request_destroy(&request); + dns_message_destroy(&tsigquery); + isc_mem_put(gmctx, reqinfo, sizeof(nsu_gssinfo_t)); + isc_event_free(&event); + maybeshutdown(); + return; + } + + if (eresult != ISC_R_SUCCESS) { + ddebug("Destroying request [%p]", request); + dns_request_destroy(&request); + if (!next_master("recvgss", addr, eresult)) { + dns_message_destroy(&tsigquery); + failed_gssrequest(); + } else { + dns_message_renderreset(tsigquery); + memmove(kserver, &master_servers[master_inuse], + sizeof(isc_sockaddr_t)); + send_gssrequest(kserver, tsigquery, &request, context); + } + isc_mem_put(gmctx, reqinfo, sizeof(nsu_gssinfo_t)); + isc_event_free(&event); + return; + } + isc_mem_put(gmctx, reqinfo, sizeof(nsu_gssinfo_t)); + + isc_event_free(&event); + reqev = NULL; + + ddebug("recvgss creating rcvmsg"); + result = dns_message_create(gmctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg); + check_result(result, "dns_message_create"); + + result = dns_request_getresponse(request, rcvmsg, + DNS_MESSAGEPARSE_PRESERVEORDER); + check_result(result, "dns_request_getresponse"); + + if (debugging) + show_message(stderr, rcvmsg, + "recvmsg reply from GSS-TSIG query"); + + if (rcvmsg->rcode == dns_rcode_formerr && !tried_other_gsstsig) { + ddebug("recvgss trying %s GSS-TSIG", + use_win2k_gsstsig ? "Standard" : "Win2k"); + if (use_win2k_gsstsig) + use_win2k_gsstsig = false; + else + use_win2k_gsstsig = true; + tried_other_gsstsig = true; + start_gssrequest(&restart_master); + goto done; + } + + if (rcvmsg->rcode != dns_rcode_noerror && + rcvmsg->rcode != dns_rcode_nxdomain) + fatal("response to GSS-TSIG query was unsuccessful"); + + + servname = dns_fixedname_initname(&fname); + isc_buffer_init(&buf, servicename, strlen(servicename)); + isc_buffer_add(&buf, strlen(servicename)); + result = dns_name_fromtext(servname, &buf, dns_rootname, 0, NULL); + check_result(result, "dns_name_fromtext"); + + tsigkey = NULL; + result = dns_tkey_gssnegotiate(tsigquery, rcvmsg, servname, + &context, &tsigkey, gssring, + use_win2k_gsstsig, &err_message); + switch (result) { + + case DNS_R_CONTINUE: + dns_message_destroy(&rcvmsg); + dns_request_destroy(&request); + send_gssrequest(kserver, tsigquery, &request, context); + ddebug("Out of recvgss"); + return; + + 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 + * bizarre 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. + */ + rcvmsg->state = DNS_SECTION_ANY; + dns_message_setquerytsig(rcvmsg, NULL); + result = dns_message_settsigkey(rcvmsg, tsigkey); + check_result(result, "dns_message_settsigkey"); + result = dns_message_checksig(rcvmsg, NULL); + ddebug("tsig verification: %s", dns_result_totext(result)); + check_result(result, "dns_message_checksig"); +#endif /* 0 */ + + send_update(&tmpzonename, &master_servers[master_inuse]); + setzoneclass(dns_rdataclass_none); + break; + + default: + fatal("dns_tkey_gssnegotiate: %s %s", + isc_result_totext(result), + err_message != NULL ? err_message : ""); + } + + done: + dns_request_destroy(&request); + dns_message_destroy(&tsigquery); + + dns_message_destroy(&rcvmsg); + ddebug("Out of recvgss"); +} +#endif + +static void +start_update(void) { + isc_result_t result; + dns_rdataset_t *rdataset = NULL; + dns_name_t *name = NULL; + dns_request_t *request = NULL; + dns_message_t *soaquery = NULL; + dns_name_t *firstname; + dns_section_t section = DNS_SECTION_UPDATE; + + ddebug("start_update()"); + + if (answer != NULL) + dns_message_destroy(&answer); + + /* + * If we have both the zone and the servers we have enough information + * to send the update straight away otherwise we need to discover + * the zone and / or the master server. + */ + if (userzone != NULL && !default_servers && !usegsstsig) { + master_from_servers(); + send_update(userzone, &master_servers[master_inuse]); + setzoneclass(dns_rdataclass_none); + return; + } + + result = dns_message_create(gmctx, DNS_MESSAGE_INTENTRENDER, + &soaquery); + check_result(result, "dns_message_create"); + + if (default_servers) + soaquery->flags |= DNS_MESSAGEFLAG_RD; + + result = dns_message_gettempname(soaquery, &name); + check_result(result, "dns_message_gettempname"); + + result = dns_message_gettemprdataset(soaquery, &rdataset); + check_result(result, "dns_message_gettemprdataset"); + + dns_rdataset_makequestion(rdataset, getzoneclass(), dns_rdatatype_soa); + + if (userzone != NULL) { + dns_name_init(name, NULL); + dns_name_clone(userzone, name); + } else { + dns_rdataset_t *tmprdataset; + result = dns_message_firstname(updatemsg, section); + if (result == ISC_R_NOMORE) { + section = DNS_SECTION_PREREQUISITE; + result = dns_message_firstname(updatemsg, section); + } + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(soaquery, &name); + dns_rdataset_disassociate(rdataset); + dns_message_puttemprdataset(soaquery, &rdataset); + dns_message_destroy(&soaquery); + done_update(); + return; + } + firstname = NULL; + dns_message_currentname(updatemsg, section, &firstname); + dns_name_init(name, NULL); + dns_name_clone(firstname, name); + /* + * Looks to see if the first name references a DS record + * and if that name is not the root remove a label as DS + * records live in the parent zone so we need to start our + * search one label up. + */ + tmprdataset = ISC_LIST_HEAD(firstname->list); + if (section == DNS_SECTION_UPDATE && + !dns_name_equal(firstname, dns_rootname) && + tmprdataset->type == dns_rdatatype_ds) { + unsigned int labels = dns_name_countlabels(name); + dns_name_getlabelsequence(name, 1, labels - 1, name); + } + } + + ISC_LIST_INIT(name->list); + ISC_LIST_APPEND(name->list, rdataset, link); + dns_message_addname(soaquery, name, DNS_SECTION_QUESTION); + + ns_inuse = 0; + sendrequest(&servers[ns_inuse], soaquery, &request); +} + +static void +cleanup(void) { + ddebug("cleanup()"); + + if (answer != NULL) + dns_message_destroy(&answer); + +#ifdef GSSAPI + if (tsigkey != NULL) { + ddebug("detach tsigkey x%p", tsigkey); + dns_tsigkey_detach(&tsigkey); + } + if (gssring != NULL) { + ddebug("Detaching GSS-TSIG keyring"); + dns_tsigkeyring_detach(&gssring); + } + if (kserver != NULL) { + isc_mem_put(gmctx, kserver, sizeof(isc_sockaddr_t)); + kserver = NULL; + } + if (realm != NULL) { + isc_mem_free(gmctx, realm); + realm = NULL; + } +#endif + + if (sig0key != NULL) + dst_key_free(&sig0key); + + ddebug("Shutting down task manager"); + isc_taskmgr_destroy(&taskmgr); + + ddebug("Destroying event"); + isc_event_free(&global_event); + + ddebug("Shutting down socket manager"); + isc_socketmgr_destroy(&socketmgr); + + ddebug("Shutting down timer manager"); + isc_timermgr_destroy(&timermgr); + + ddebug("Destroying hash context"); + isc_hash_destroy(); + + ddebug("Destroying name state"); + dns_name_destroy(); + + ddebug("Removing log context"); + isc_log_destroy(&glctx); + + ddebug("Destroying memory context"); + if (memdebugging) + isc_mem_stats(gmctx, stderr); + isc_mem_destroy(&gmctx); +} + +static void +getinput(isc_task_t *task, isc_event_t *event) { + bool more; + + UNUSED(task); + + if (shuttingdown) { + maybeshutdown(); + return; + } + + if (global_event == NULL) + global_event = event; + + reset_system(); + more = user_interaction(); + if (!more) { + isc_app_shutdown(); + return; + } + start_update(); + return; +} + +int +main(int argc, char **argv) { + isc_result_t result; + style = &dns_master_style_debug; + + input = stdin; + + interactive = isatty(0); + + isc_app_start(); + + pre_parse_args(argc, argv); + + result = isc_mem_create(0, 0, &gmctx); + check_result(result, "isc_mem_create"); + + parse_args(argc, argv, gmctx, &entropy); + + setup_system(); + + result = isc_app_onrun(gmctx, global_task, getinput, NULL); + check_result(result, "isc_app_onrun"); + + (void)isc_app_run(); + + cleanup(); + + isc_app_finish(); + + if (seenerror) + return (2); + else + return (0); +} diff --git a/bin/nsupdate/nsupdate.docbook b/bin/nsupdate/nsupdate.docbook new file mode 100644 index 0000000..47f021d --- /dev/null +++ b/bin/nsupdate/nsupdate.docbook @@ -0,0 +1,926 @@ +<!-- + - 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. +--> + +<!-- Converted by db4-upgrade version 1.0 --> +<refentry xmlns:db="http://docbook.org/ns/docbook" version="5.0" xml:id="man.nsupdate"> + <info> + <date>2014-04-18</date> + </info> + <refentryinfo> + <corpname>ISC</corpname> + <corpauthor>Internet Systems Consortium, Inc.</corpauthor> + </refentryinfo> + + <refmeta> + <refentrytitle><application>nsupdate</application></refentrytitle> + <manvolnum>1</manvolnum> + <refmiscinfo>BIND9</refmiscinfo> + </refmeta> + <refnamediv> + <refname><application>nsupdate</application></refname> + <refpurpose>Dynamic DNS update utility</refpurpose> + </refnamediv> + + <docinfo> + <copyright> + <year>2000</year> + <year>2001</year> + <year>2002</year> + <year>2003</year> + <year>2004</year> + <year>2005</year> + <year>2006</year> + <year>2007</year> + <year>2008</year> + <year>2009</year> + <year>2010</year> + <year>2011</year> + <year>2012</year> + <year>2014</year> + <year>2015</year> + <year>2016</year> + <year>2017</year> + <year>2018</year> + <year>2019</year> + <holder>Internet Systems Consortium, Inc. ("ISC")</holder> + </copyright> + </docinfo> + + <refsynopsisdiv> + <cmdsynopsis sepchar=" "> + <command>nsupdate</command> + <arg choice="opt" rep="norepeat"><option>-d</option></arg> + <arg choice="opt" rep="norepeat"><option>-D</option></arg> + <arg choice="opt" rep="norepeat"><option>-i</option></arg> + <arg choice="opt" rep="norepeat"><option>-L <replaceable class="parameter">level</replaceable></option></arg> + <group choice="opt" rep="norepeat"> + <arg choice="opt" rep="norepeat"><option>-g</option></arg> + <arg choice="opt" rep="norepeat"><option>-o</option></arg> + <arg choice="opt" rep="norepeat"><option>-l</option></arg> + <arg choice="opt" rep="norepeat"><option>-y <replaceable class="parameter"><optional>hmac:</optional>keyname:secret</replaceable></option></arg> + <arg choice="opt" rep="norepeat"><option>-k <replaceable class="parameter">keyfile</replaceable></option></arg> + </group> + <arg choice="opt" rep="norepeat"><option>-t <replaceable class="parameter">timeout</replaceable></option></arg> + <arg choice="opt" rep="norepeat"><option>-u <replaceable class="parameter">udptimeout</replaceable></option></arg> + <arg choice="opt" rep="norepeat"><option>-r <replaceable class="parameter">udpretries</replaceable></option></arg> + <arg choice="opt" rep="norepeat"><option>-R <replaceable class="parameter">randomdev</replaceable></option></arg> + <arg choice="opt" rep="norepeat"><option>-v</option></arg> + <arg choice="opt" rep="norepeat"><option>-T</option></arg> + <arg choice="opt" rep="norepeat"><option>-P</option></arg> + <arg choice="opt" rep="norepeat"><option>-V</option></arg> + <arg choice="opt" rep="norepeat">filename</arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsection><info><title>DESCRIPTION</title></info> + + <para><command>nsupdate</command> + is used to submit Dynamic DNS Update requests as defined in RFC 2136 + to a name server. + This allows resource records to be added or removed from a zone + without manually editing the zone file. + A single update request can contain requests to add or remove more than + one + resource record. + </para> + <para> + Zones that are under dynamic control via + <command>nsupdate</command> + or a DHCP server should not be edited by hand. + Manual edits could + conflict with dynamic updates and cause data to be lost. + </para> + <para> + The resource records that are dynamically added or removed with + <command>nsupdate</command> + have to be in the same zone. + Requests are sent to the zone's master server. + This is identified by the MNAME field of the zone's SOA record. + </para> + <para> + Transaction signatures can be used to authenticate the Dynamic + DNS updates. These use the TSIG resource record type described + in RFC 2845 or the SIG(0) record described in RFC 2535 and + RFC 2931 or GSS-TSIG as described in RFC 3645. + </para> + <para> + TSIG relies on + a shared secret that should only be known to + <command>nsupdate</command> and the name server. + For instance, suitable <type>key</type> and + <type>server</type> statements would be added to + <filename>/etc/named.conf</filename> so that the name server + can associate the appropriate secret key and algorithm with + the IP address of the client application that will be using + TSIG authentication. You can use <command>ddns-confgen</command> + to generate suitable configuration fragments. + <command>nsupdate</command> + uses the <option>-y</option> or <option>-k</option> options + to provide the TSIG shared secret. These options are mutually exclusive. + </para> + <para> + SIG(0) uses public key cryptography. + To use a SIG(0) key, the public key must be stored in a KEY + record in a zone served by the name server. + </para> + <para> + GSS-TSIG uses Kerberos credentials. Standard GSS-TSIG mode + is switched on with the <option>-g</option> flag. A + non-standards-compliant variant of GSS-TSIG used by Windows + 2000 can be switched on with the <option>-o</option> flag. + </para> + </refsection> + + <refsection><info><title>OPTIONS</title></info> + + + <variablelist> + <varlistentry> + <term>-d</term> + <listitem> + <para> + Debug mode. This provides tracing information about the + update requests that are made and the replies received + from the name server. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-D</term> + <listitem> + <para> + Extra debug mode. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-i</term> + <listitem> + <para> + Force interactive mode, even when standard input is not a terminal. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-k <replaceable class="parameter">keyfile</replaceable></term> + <listitem> + <para> + The file containing the TSIG authentication key. + Keyfiles may be in two formats: a single file containing + a <filename>named.conf</filename>-format <command>key</command> + statement, which may be generated automatically by + <command>ddns-confgen</command>, or a pair of files whose names are + of the format <filename>K{name}.+157.+{random}.key</filename> and + <filename>K{name}.+157.+{random}.private</filename>, which can be + generated by <command>dnssec-keygen</command>. + The <option>-k</option> may also be used to specify a SIG(0) key used + to authenticate Dynamic DNS update requests. In this case, the key + specified is not an HMAC-MD5 key. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-l</term> + <listitem> + <para> + Local-host only mode. This sets the server address to + localhost (disabling the <command>server</command> so that the server + address cannot be overridden). Connections to the local server will + use a TSIG key found in <filename>/var/run/named/session.key</filename>, + which is automatically generated by <command>named</command> if any + local master zone has set <command>update-policy</command> to + <command>local</command>. The location of this key file can be + overridden with the <option>-k</option> option. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-L <replaceable class="parameter">level</replaceable></term> + <listitem> + <para> + Set the logging debug level. If zero, logging is disabled. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-p <replaceable class="parameter">port</replaceable></term> + <listitem> + <para> + Set the port to use for connections to a name server. The + default is 53. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-P</term> + <listitem> + <para> + Print the list of private BIND-specific resource record + types whose format is understood + by <command>nsupdate</command>. See also + the <option>-T</option> option. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-r <replaceable class="parameter">udpretries</replaceable></term> + <listitem> + <para> + The number of UDP retries. The default is 3. If zero, only + one update request will be made. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-R <replaceable class="parameter">randomdev</replaceable></term> + <listitem> + <para> + Where to obtain randomness. If the operating system + does not provide a <filename>/dev/random</filename> or + equivalent device, the default source of randomness is keyboard + input. <filename>randomdev</filename> specifies the name of + a character device or file containing random data to be used + instead of the default. The special value + <filename>keyboard</filename> indicates that keyboard input + should be used. This option may be specified multiple times. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-t <replaceable class="parameter">timeout</replaceable></term> + <listitem> + <para> + The maximum time an update request can take before it is + aborted. The default is 300 seconds. Zero can be used to + disable the timeout. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-T</term> + <listitem> + <para> + Print the list of IANA standard resource record types + whose format is understood by <command>nsupdate</command>. + <command>nsupdate</command> will exit after the lists are + printed. The <option>-T</option> option can be combined + with the <option>-P</option> option. + </para> + <para> + Other types can be entered using "TYPEXXXXX" where "XXXXX" is the + decimal value of the type with no leading zeros. The rdata, + if present, will be parsed using the UNKNOWN rdata format, + (<backslash> <hash> <space> <length> + <space> <hexstring>). + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-u <replaceable class="parameter">udptimeout</replaceable></term> + <listitem> + <para> + The UDP retry interval. The default is 3 seconds. If zero, + the interval will be computed from the timeout interval and + number of UDP retries. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-v</term> + <listitem> + <para> + Use TCP even for small update requests. + By default, <command>nsupdate</command> + uses UDP to send update requests to the name server unless they are too + large to fit in a UDP request in which case TCP will be used. + TCP may be preferable when a batch of update requests is made. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-V</term> + <listitem> + <para> + Print the version number and exit. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-y <replaceable class="parameter"><optional>hmac:</optional>keyname:secret</replaceable></term> + <listitem> + <para> + Literal TSIG authentication key. + <parameter>keyname</parameter> is the name of the key, and + <parameter>secret</parameter> is the base64 encoded shared secret. + <parameter>hmac</parameter> is the name of the key algorithm; + valid choices are <literal>hmac-md5</literal>, + <literal>hmac-sha1</literal>, <literal>hmac-sha224</literal>, + <literal>hmac-sha256</literal>, <literal>hmac-sha384</literal>, or + <literal>hmac-sha512</literal>. If <parameter>hmac</parameter> + is not specified, the default is <literal>hmac-md5</literal> + or if MD5 was disabled <literal>hmac-sha256</literal>. + </para> + <para> + NOTE: Use of the <option>-y</option> option is discouraged because the + shared secret is supplied as a command line argument in clear text. + This may be visible in the output from + <citerefentry> + <refentrytitle>ps</refentrytitle><manvolnum>1</manvolnum> + </citerefentry> + or in a history file maintained by the user's shell. + </para> + </listitem> + </varlistentry> + + </variablelist> + </refsection> + + <refsection><info><title>INPUT FORMAT</title></info> + + <para><command>nsupdate</command> + reads input from + <parameter>filename</parameter> + or standard input. + Each command is supplied on exactly one line of input. + Some commands are for administrative purposes. + The others are either update instructions or prerequisite checks on the + contents of the zone. + These checks set conditions that some name or set of + resource records (RRset) either exists or is absent from the zone. + These conditions must be met if the entire update request is to succeed. + Updates will be rejected if the tests for the prerequisite conditions + fail. + </para> + <para> + Every update request consists of zero or more prerequisites + and zero or more updates. + This allows a suitably authenticated update request to proceed if some + specified resource records are present or missing from the zone. + A blank input line (or the <command>send</command> command) + causes the + accumulated commands to be sent as one Dynamic DNS update request to the + name server. + </para> + <para> + The command formats and their meaning are as follows: + <variablelist> + + <varlistentry> + <term> + <command>server</command> + <arg choice="req" rep="norepeat">servername</arg> + <arg choice="opt" rep="norepeat">port</arg> + </term> + <listitem> + <para> + Sends all dynamic update requests to the name server + <parameter>servername</parameter>. + When no server statement is provided, + <command>nsupdate</command> + will send updates to the master server of the correct zone. + The MNAME field of that zone's SOA record will identify the + master + server for that zone. + <parameter>port</parameter> + is the port number on + <parameter>servername</parameter> + where the dynamic update requests get sent. + If no port number is specified, the default DNS port number of + 53 is + used. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>local</command> + <arg choice="req" rep="norepeat">address</arg> + <arg choice="opt" rep="norepeat">port</arg> + </term> + <listitem> + <para> + Sends all dynamic update requests using the local + <parameter>address</parameter>. + + When no local statement is provided, + <command>nsupdate</command> + will send updates using an address and port chosen by the + system. + <parameter>port</parameter> + can additionally be used to make requests come from a specific + port. + If no port number is specified, the system will assign one. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>zone</command> + <arg choice="req" rep="norepeat">zonename</arg> + </term> + <listitem> + <para> + Specifies that all updates are to be made to the zone + <parameter>zonename</parameter>. + If no + <parameter>zone</parameter> + statement is provided, + <command>nsupdate</command> + will attempt determine the correct zone to update based on the + rest of the input. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>class</command> + <arg choice="req" rep="norepeat">classname</arg> + </term> + <listitem> + <para> + Specify the default class. + If no <parameter>class</parameter> is specified, the + default class is + <parameter>IN</parameter>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>ttl</command> + <arg choice="req" rep="norepeat">seconds</arg> + </term> + <listitem> + <para> + Specify the default time to live for records to be added. + The value <parameter>none</parameter> will clear the default + ttl. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>key</command> + <arg choice="opt" rep="norepeat">hmac:</arg><arg choice="req" rep="norepeat">keyname</arg> + <arg choice="req" rep="norepeat">secret</arg> + </term> + <listitem> + <para> + Specifies that all updates are to be TSIG-signed using the + <parameter>keyname</parameter> <parameter>secret</parameter> pair. + If <parameter>hmac</parameter> is specified, then it sets the + signing algorithm in use; the default is + <literal>hmac-md5</literal> or if MD5 was disabled + <literal>hmac-sha256</literal>. The <command>key</command> + command overrides any key specified on the command line via + <option>-y</option> or <option>-k</option>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>gsstsig</command> + </term> + <listitem> + <para> + Use GSS-TSIG to sign the updated. This is equivalent to + specifying <option>-g</option> on the command line. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>oldgsstsig</command> + </term> + <listitem> + <para> + Use the Windows 2000 version of GSS-TSIG to sign the updated. + This is equivalent to specifying <option>-o</option> on the + command line. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>realm</command> + <arg choice="req" rep="norepeat"><optional>realm_name</optional></arg> + </term> + <listitem> + <para> + When using GSS-TSIG use <parameter>realm_name</parameter> rather + than the default realm in <filename>krb5.conf</filename>. If no + realm is specified the saved realm is cleared. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>check-names</command> + <arg choice="req" rep="norepeat"><optional>yes_or_no</optional></arg> + </term> + <listitem> + <para> + Turn on or off check-names processing on records to + be added. Check-names has no effect on prerequisites + or records to be deleted. By default check-names + processing is on. If check-names processing fails + the record will not be added to the UPDATE message. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command><optional>prereq</optional> nxdomain</command> + <arg choice="req" rep="norepeat">domain-name</arg> + </term> + <listitem> + <para> + Requires that no resource record of any type exists with name + <parameter>domain-name</parameter>. + </para> + </listitem> + </varlistentry> + + + <varlistentry> + <term> + <command><optional>prereq</optional> yxdomain</command> + <arg choice="req" rep="norepeat">domain-name</arg> + </term> + <listitem> + <para> + Requires that + <parameter>domain-name</parameter> + exists (has as at least one resource record, of any type). + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command><optional>prereq</optional> nxrrset</command> + <arg choice="req" rep="norepeat">domain-name</arg> + <arg choice="opt" rep="norepeat">class</arg> + <arg choice="req" rep="norepeat">type</arg> + </term> + <listitem> + <para> + Requires that no resource record exists of the specified + <parameter>type</parameter>, + <parameter>class</parameter> + and + <parameter>domain-name</parameter>. + If + <parameter>class</parameter> + is omitted, IN (internet) is assumed. + </para> + </listitem> + </varlistentry> + + + <varlistentry> + <term> + <command><optional>prereq</optional> yxrrset</command> + <arg choice="req" rep="norepeat">domain-name</arg> + <arg choice="opt" rep="norepeat">class</arg> + <arg choice="req" rep="norepeat">type</arg> + </term> + <listitem> + <para> + This requires that a resource record of the specified + <parameter>type</parameter>, + <parameter>class</parameter> + and + <parameter>domain-name</parameter> + must exist. + If + <parameter>class</parameter> + is omitted, IN (internet) is assumed. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command><optional>prereq</optional> yxrrset</command> + <arg choice="req" rep="norepeat">domain-name</arg> + <arg choice="opt" rep="norepeat">class</arg> + <arg choice="req" rep="norepeat">type</arg> + <arg choice="req" rep="repeat">data</arg> + </term> + <listitem> + <para> + The + <parameter>data</parameter> + from each set of prerequisites of this form + sharing a common + <parameter>type</parameter>, + <parameter>class</parameter>, + and + <parameter>domain-name</parameter> + are combined to form a set of RRs. This set of RRs must + exactly match the set of RRs existing in the zone at the + given + <parameter>type</parameter>, + <parameter>class</parameter>, + and + <parameter>domain-name</parameter>. + The + <parameter>data</parameter> + are written in the standard text representation of the resource + record's + RDATA. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command><optional>update</optional> del<optional>ete</optional></command> + <arg choice="req" rep="norepeat">domain-name</arg> + <arg choice="opt" rep="norepeat">ttl</arg> + <arg choice="opt" rep="norepeat">class</arg> + <arg choice="opt" rep="norepeat">type <arg choice="opt" rep="repeat">data</arg></arg> + </term> + <listitem> + <para> + Deletes any resource records named + <parameter>domain-name</parameter>. + If + <parameter>type</parameter> + and + <parameter>data</parameter> + is provided, only matching resource records will be removed. + The internet class is assumed if + <parameter>class</parameter> + is not supplied. The + <parameter>ttl</parameter> + is ignored, and is only allowed for compatibility. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command><optional>update</optional> add</command> + <arg choice="req" rep="norepeat">domain-name</arg> + <arg choice="req" rep="norepeat">ttl</arg> + <arg choice="opt" rep="norepeat">class</arg> + <arg choice="req" rep="norepeat">type</arg> + <arg choice="req" rep="repeat">data</arg> + </term> + <listitem> + <para> + Adds a new resource record with the specified + <parameter>ttl</parameter>, + <parameter>class</parameter> + and + <parameter>data</parameter>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>show</command> + </term> + <listitem> + <para> + Displays the current message, containing all of the + prerequisites and + updates specified since the last send. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>send</command> + </term> + <listitem> + <para> + Sends the current message. This is equivalent to entering a + blank line. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>answer</command> + </term> + <listitem> + <para> + Displays the answer. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>debug</command> + </term> + <listitem> + <para> + Turn on debugging. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>version</command> + </term> + <listitem> + <para> + Print version number. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <command>help</command> + </term> + <listitem> + <para> + Print a list of commands. + </para> + </listitem> + </varlistentry> + + </variablelist> + </para> + + <para> + Lines beginning with a semicolon are comments and are ignored. + </para> + + </refsection> + + <refsection><info><title>EXAMPLES</title></info> + + <para> + The examples below show how + <command>nsupdate</command> + could be used to insert and delete resource records from the + <type>example.com</type> + zone. + Notice that the input in each example contains a trailing blank line so + that + a group of commands are sent as one dynamic update request to the + master name server for + <type>example.com</type>. + + <programlisting> +# nsupdate +> update delete oldhost.example.com A +> update add newhost.example.com 86400 A 172.16.1.1 +> send +</programlisting> + </para> + <para> + Any A records for + <type>oldhost.example.com</type> + are deleted. + And an A record for + <type>newhost.example.com</type> + with IP address 172.16.1.1 is added. + The newly-added record has a 1 day TTL (86400 seconds). + <programlisting> +# nsupdate +> prereq nxdomain nickname.example.com +> update add nickname.example.com 86400 CNAME somehost.example.com +> send +</programlisting> + </para> + <para> + The prerequisite condition gets the name server to check that there + are no resource records of any type for + <type>nickname.example.com</type>. + + If there are, the update request fails. + If this name does not exist, a CNAME for it is added. + This ensures that when the CNAME is added, it cannot conflict with the + long-standing rule in RFC 1034 that a name must not exist as any other + record type if it exists as a CNAME. + (The rule has been updated for DNSSEC in RFC 2535 to allow CNAMEs to have + RRSIG, DNSKEY and NSEC records.) + </para> + </refsection> + + <refsection><info><title>FILES</title></info> + + + <variablelist> + <varlistentry> + <term><constant>/etc/resolv.conf</constant></term> + <listitem> + <para> + used to identify default name server + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><constant>/var/run/named/session.key</constant></term> + <listitem> + <para> + sets the default TSIG key for use in local-only mode + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><constant>K{name}.+157.+{random}.key</constant></term> + <listitem> + <para> + base-64 encoding of HMAC-MD5 key created by + <citerefentry> + <refentrytitle>dnssec-keygen</refentrytitle><manvolnum>8</manvolnum> + </citerefentry>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><constant>K{name}.+157.+{random}.private</constant></term> + <listitem> + <para> + base-64 encoding of HMAC-MD5 key created by + <citerefentry> + <refentrytitle>dnssec-keygen</refentrytitle><manvolnum>8</manvolnum> + </citerefentry>. + </para> + </listitem> + </varlistentry> + + </variablelist> + </refsection> + + <refsection><info><title>SEE ALSO</title></info> + + <para> + <citetitle>RFC 2136</citetitle>, + <citetitle>RFC 3007</citetitle>, + <citetitle>RFC 2104</citetitle>, + <citetitle>RFC 2845</citetitle>, + <citetitle>RFC 1034</citetitle>, + <citetitle>RFC 2535</citetitle>, + <citetitle>RFC 2931</citetitle>, + <citerefentry> + <refentrytitle>named</refentrytitle><manvolnum>8</manvolnum> + </citerefentry>, + <citerefentry> + <refentrytitle>ddns-confgen</refentrytitle><manvolnum>8</manvolnum> + </citerefentry>, + <citerefentry> + <refentrytitle>dnssec-keygen</refentrytitle><manvolnum>8</manvolnum> + </citerefentry>. + </para> + </refsection> + + <refsection><info><title>BUGS</title></info> + + <para> + The TSIG key is redundantly stored in two separate files. + This is a consequence of nsupdate using the DST library + for its cryptographic operations, and may change in future + releases. + </para> + </refsection> + +</refentry> diff --git a/bin/nsupdate/nsupdate.html b/bin/nsupdate/nsupdate.html new file mode 100644 index 0000000..133f706 --- /dev/null +++ b/bin/nsupdate/nsupdate.html @@ -0,0 +1,783 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<!-- + - Copyright (C) 2000-2012, 2014-2019 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/. +--> +<html lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> +<title>nsupdate</title> +<meta name="generator" content="DocBook XSL Stylesheets V1.78.1"> +</head> +<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry"> +<a name="man.nsupdate"></a><div class="titlepage"></div> + + + + + <div class="refnamediv"> +<h2>Name</h2> +<p> + <span class="application">nsupdate</span> + — Dynamic DNS update utility + </p> +</div> + + + + <div class="refsynopsisdiv"> +<h2>Synopsis</h2> + <div class="cmdsynopsis"><p> + <code class="command">nsupdate</code> + [<code class="option">-d</code>] + [<code class="option">-D</code>] + [<code class="option">-i</code>] + [<code class="option">-L <em class="replaceable"><code>level</code></em></code>] + [ + [<code class="option">-g</code>] + | [<code class="option">-o</code>] + | [<code class="option">-l</code>] + | [<code class="option">-y <em class="replaceable"><code>[<span class="optional">hmac:</span>]keyname:secret</code></em></code>] + | [<code class="option">-k <em class="replaceable"><code>keyfile</code></em></code>] + ] + [<code class="option">-t <em class="replaceable"><code>timeout</code></em></code>] + [<code class="option">-u <em class="replaceable"><code>udptimeout</code></em></code>] + [<code class="option">-r <em class="replaceable"><code>udpretries</code></em></code>] + [<code class="option">-R <em class="replaceable"><code>randomdev</code></em></code>] + [<code class="option">-v</code>] + [<code class="option">-T</code>] + [<code class="option">-P</code>] + [<code class="option">-V</code>] + [filename] + </p></div> + </div> + + <div class="refsection"> +<a name="id-1.7"></a><h2>DESCRIPTION</h2> + + <p><span class="command"><strong>nsupdate</strong></span> + is used to submit Dynamic DNS Update requests as defined in RFC 2136 + to a name server. + This allows resource records to be added or removed from a zone + without manually editing the zone file. + A single update request can contain requests to add or remove more than + one + resource record. + </p> + <p> + Zones that are under dynamic control via + <span class="command"><strong>nsupdate</strong></span> + or a DHCP server should not be edited by hand. + Manual edits could + conflict with dynamic updates and cause data to be lost. + </p> + <p> + The resource records that are dynamically added or removed with + <span class="command"><strong>nsupdate</strong></span> + have to be in the same zone. + Requests are sent to the zone's master server. + This is identified by the MNAME field of the zone's SOA record. + </p> + <p> + Transaction signatures can be used to authenticate the Dynamic + DNS updates. These use the TSIG resource record type described + in RFC 2845 or the SIG(0) record described in RFC 2535 and + RFC 2931 or GSS-TSIG as described in RFC 3645. + </p> + <p> + TSIG relies on + a shared secret that should only be known to + <span class="command"><strong>nsupdate</strong></span> and the name server. + For instance, suitable <span class="type">key</span> and + <span class="type">server</span> statements would be added to + <code class="filename">/etc/named.conf</code> so that the name server + can associate the appropriate secret key and algorithm with + the IP address of the client application that will be using + TSIG authentication. You can use <span class="command"><strong>ddns-confgen</strong></span> + to generate suitable configuration fragments. + <span class="command"><strong>nsupdate</strong></span> + uses the <code class="option">-y</code> or <code class="option">-k</code> options + to provide the TSIG shared secret. These options are mutually exclusive. + </p> + <p> + SIG(0) uses public key cryptography. + To use a SIG(0) key, the public key must be stored in a KEY + record in a zone served by the name server. + </p> + <p> + GSS-TSIG uses Kerberos credentials. Standard GSS-TSIG mode + is switched on with the <code class="option">-g</code> flag. A + non-standards-compliant variant of GSS-TSIG used by Windows + 2000 can be switched on with the <code class="option">-o</code> flag. + </p> + </div> + + <div class="refsection"> +<a name="id-1.8"></a><h2>OPTIONS</h2> + + + <div class="variablelist"><dl class="variablelist"> +<dt><span class="term">-d</span></dt> +<dd> + <p> + Debug mode. This provides tracing information about the + update requests that are made and the replies received + from the name server. + </p> + </dd> +<dt><span class="term">-D</span></dt> +<dd> + <p> + Extra debug mode. + </p> + </dd> +<dt><span class="term">-i</span></dt> +<dd> + <p> + Force interactive mode, even when standard input is not a terminal. + </p> + </dd> +<dt><span class="term">-k <em class="replaceable"><code>keyfile</code></em></span></dt> +<dd> + <p> + The file containing the TSIG authentication key. + Keyfiles may be in two formats: a single file containing + a <code class="filename">named.conf</code>-format <span class="command"><strong>key</strong></span> + statement, which may be generated automatically by + <span class="command"><strong>ddns-confgen</strong></span>, or a pair of files whose names are + of the format <code class="filename">K{name}.+157.+{random}.key</code> and + <code class="filename">K{name}.+157.+{random}.private</code>, which can be + generated by <span class="command"><strong>dnssec-keygen</strong></span>. + The <code class="option">-k</code> may also be used to specify a SIG(0) key used + to authenticate Dynamic DNS update requests. In this case, the key + specified is not an HMAC-MD5 key. + </p> + </dd> +<dt><span class="term">-l</span></dt> +<dd> + <p> + Local-host only mode. This sets the server address to + localhost (disabling the <span class="command"><strong>server</strong></span> so that the server + address cannot be overridden). Connections to the local server will + use a TSIG key found in <code class="filename">/var/run/named/session.key</code>, + which is automatically generated by <span class="command"><strong>named</strong></span> if any + local master zone has set <span class="command"><strong>update-policy</strong></span> to + <span class="command"><strong>local</strong></span>. The location of this key file can be + overridden with the <code class="option">-k</code> option. + </p> + </dd> +<dt><span class="term">-L <em class="replaceable"><code>level</code></em></span></dt> +<dd> + <p> + Set the logging debug level. If zero, logging is disabled. + </p> + </dd> +<dt><span class="term">-p <em class="replaceable"><code>port</code></em></span></dt> +<dd> + <p> + Set the port to use for connections to a name server. The + default is 53. + </p> + </dd> +<dt><span class="term">-P</span></dt> +<dd> + <p> + Print the list of private BIND-specific resource record + types whose format is understood + by <span class="command"><strong>nsupdate</strong></span>. See also + the <code class="option">-T</code> option. + </p> + </dd> +<dt><span class="term">-r <em class="replaceable"><code>udpretries</code></em></span></dt> +<dd> + <p> + The number of UDP retries. The default is 3. If zero, only + one update request will be made. + </p> + </dd> +<dt><span class="term">-R <em class="replaceable"><code>randomdev</code></em></span></dt> +<dd> + <p> + Where to obtain randomness. If the operating system + does not provide a <code class="filename">/dev/random</code> or + equivalent device, the default source of randomness is keyboard + input. <code class="filename">randomdev</code> specifies the name of + a character device or file containing random data to be used + instead of the default. The special value + <code class="filename">keyboard</code> indicates that keyboard input + should be used. This option may be specified multiple times. + </p> + </dd> +<dt><span class="term">-t <em class="replaceable"><code>timeout</code></em></span></dt> +<dd> + <p> + The maximum time an update request can take before it is + aborted. The default is 300 seconds. Zero can be used to + disable the timeout. + </p> + </dd> +<dt><span class="term">-T</span></dt> +<dd> + <p> + Print the list of IANA standard resource record types + whose format is understood by <span class="command"><strong>nsupdate</strong></span>. + <span class="command"><strong>nsupdate</strong></span> will exit after the lists are + printed. The <code class="option">-T</code> option can be combined + with the <code class="option">-P</code> option. + </p> + <p> + Other types can be entered using "TYPEXXXXX" where "XXXXX" is the + decimal value of the type with no leading zeros. The rdata, + if present, will be parsed using the UNKNOWN rdata format, + (<backslash> <hash> <space> <length> + <space> <hexstring>). + </p> + </dd> +<dt><span class="term">-u <em class="replaceable"><code>udptimeout</code></em></span></dt> +<dd> + <p> + The UDP retry interval. The default is 3 seconds. If zero, + the interval will be computed from the timeout interval and + number of UDP retries. + </p> + </dd> +<dt><span class="term">-v</span></dt> +<dd> + <p> + Use TCP even for small update requests. + By default, <span class="command"><strong>nsupdate</strong></span> + uses UDP to send update requests to the name server unless they are too + large to fit in a UDP request in which case TCP will be used. + TCP may be preferable when a batch of update requests is made. + </p> + </dd> +<dt><span class="term">-V</span></dt> +<dd> + <p> + Print the version number and exit. + </p> + </dd> +<dt><span class="term">-y <em class="replaceable"><code>[<span class="optional">hmac:</span>]keyname:secret</code></em></span></dt> +<dd> + <p> + Literal TSIG authentication key. + <em class="parameter"><code>keyname</code></em> is the name of the key, and + <em class="parameter"><code>secret</code></em> is the base64 encoded shared secret. + <em class="parameter"><code>hmac</code></em> is the name of the key algorithm; + valid choices are <code class="literal">hmac-md5</code>, + <code class="literal">hmac-sha1</code>, <code class="literal">hmac-sha224</code>, + <code class="literal">hmac-sha256</code>, <code class="literal">hmac-sha384</code>, or + <code class="literal">hmac-sha512</code>. If <em class="parameter"><code>hmac</code></em> + is not specified, the default is <code class="literal">hmac-md5</code> + or if MD5 was disabled <code class="literal">hmac-sha256</code>. + </p> + <p> + NOTE: Use of the <code class="option">-y</code> option is discouraged because the + shared secret is supplied as a command line argument in clear text. + This may be visible in the output from + <span class="citerefentry"> + <span class="refentrytitle">ps</span>(1) + </span> + or in a history file maintained by the user's shell. + </p> + </dd> +</dl></div> + </div> + + <div class="refsection"> +<a name="id-1.9"></a><h2>INPUT FORMAT</h2> + + <p><span class="command"><strong>nsupdate</strong></span> + reads input from + <em class="parameter"><code>filename</code></em> + or standard input. + Each command is supplied on exactly one line of input. + Some commands are for administrative purposes. + The others are either update instructions or prerequisite checks on the + contents of the zone. + These checks set conditions that some name or set of + resource records (RRset) either exists or is absent from the zone. + These conditions must be met if the entire update request is to succeed. + Updates will be rejected if the tests for the prerequisite conditions + fail. + </p> + <p> + Every update request consists of zero or more prerequisites + and zero or more updates. + This allows a suitably authenticated update request to proceed if some + specified resource records are present or missing from the zone. + A blank input line (or the <span class="command"><strong>send</strong></span> command) + causes the + accumulated commands to be sent as one Dynamic DNS update request to the + name server. + </p> + <p> + The command formats and their meaning are as follows: + </p> +<div class="variablelist"><dl class="variablelist"> +<dt><span class="term"> + <span class="command"><strong>server</strong></span> + {servername} + [port] + </span></dt> +<dd> + <p> + Sends all dynamic update requests to the name server + <em class="parameter"><code>servername</code></em>. + When no server statement is provided, + <span class="command"><strong>nsupdate</strong></span> + will send updates to the master server of the correct zone. + The MNAME field of that zone's SOA record will identify the + master + server for that zone. + <em class="parameter"><code>port</code></em> + is the port number on + <em class="parameter"><code>servername</code></em> + where the dynamic update requests get sent. + If no port number is specified, the default DNS port number of + 53 is + used. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>local</strong></span> + {address} + [port] + </span></dt> +<dd> + <p> + Sends all dynamic update requests using the local + <em class="parameter"><code>address</code></em>. + + When no local statement is provided, + <span class="command"><strong>nsupdate</strong></span> + will send updates using an address and port chosen by the + system. + <em class="parameter"><code>port</code></em> + can additionally be used to make requests come from a specific + port. + If no port number is specified, the system will assign one. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>zone</strong></span> + {zonename} + </span></dt> +<dd> + <p> + Specifies that all updates are to be made to the zone + <em class="parameter"><code>zonename</code></em>. + If no + <em class="parameter"><code>zone</code></em> + statement is provided, + <span class="command"><strong>nsupdate</strong></span> + will attempt determine the correct zone to update based on the + rest of the input. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>class</strong></span> + {classname} + </span></dt> +<dd> + <p> + Specify the default class. + If no <em class="parameter"><code>class</code></em> is specified, the + default class is + <em class="parameter"><code>IN</code></em>. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>ttl</strong></span> + {seconds} + </span></dt> +<dd> + <p> + Specify the default time to live for records to be added. + The value <em class="parameter"><code>none</code></em> will clear the default + ttl. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>key</strong></span> + [hmac:] {keyname} + {secret} + </span></dt> +<dd> + <p> + Specifies that all updates are to be TSIG-signed using the + <em class="parameter"><code>keyname</code></em> <em class="parameter"><code>secret</code></em> pair. + If <em class="parameter"><code>hmac</code></em> is specified, then it sets the + signing algorithm in use; the default is + <code class="literal">hmac-md5</code> or if MD5 was disabled + <code class="literal">hmac-sha256</code>. The <span class="command"><strong>key</strong></span> + command overrides any key specified on the command line via + <code class="option">-y</code> or <code class="option">-k</code>. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>gsstsig</strong></span> + </span></dt> +<dd> + <p> + Use GSS-TSIG to sign the updated. This is equivalent to + specifying <code class="option">-g</code> on the command line. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>oldgsstsig</strong></span> + </span></dt> +<dd> + <p> + Use the Windows 2000 version of GSS-TSIG to sign the updated. + This is equivalent to specifying <code class="option">-o</code> on the + command line. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>realm</strong></span> + {[<span class="optional">realm_name</span>]} + </span></dt> +<dd> + <p> + When using GSS-TSIG use <em class="parameter"><code>realm_name</code></em> rather + than the default realm in <code class="filename">krb5.conf</code>. If no + realm is specified the saved realm is cleared. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>check-names</strong></span> + {[<span class="optional">yes_or_no</span>]} + </span></dt> +<dd> + <p> + Turn on or off check-names processing on records to + be added. Check-names has no effect on prerequisites + or records to be deleted. By default check-names + processing is on. If check-names processing fails + the record will not be added to the UPDATE message. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>[<span class="optional">prereq</span>] nxdomain</strong></span> + {domain-name} + </span></dt> +<dd> + <p> + Requires that no resource record of any type exists with name + <em class="parameter"><code>domain-name</code></em>. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>[<span class="optional">prereq</span>] yxdomain</strong></span> + {domain-name} + </span></dt> +<dd> + <p> + Requires that + <em class="parameter"><code>domain-name</code></em> + exists (has as at least one resource record, of any type). + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>[<span class="optional">prereq</span>] nxrrset</strong></span> + {domain-name} + [class] + {type} + </span></dt> +<dd> + <p> + Requires that no resource record exists of the specified + <em class="parameter"><code>type</code></em>, + <em class="parameter"><code>class</code></em> + and + <em class="parameter"><code>domain-name</code></em>. + If + <em class="parameter"><code>class</code></em> + is omitted, IN (internet) is assumed. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>[<span class="optional">prereq</span>] yxrrset</strong></span> + {domain-name} + [class] + {type} + </span></dt> +<dd> + <p> + This requires that a resource record of the specified + <em class="parameter"><code>type</code></em>, + <em class="parameter"><code>class</code></em> + and + <em class="parameter"><code>domain-name</code></em> + must exist. + If + <em class="parameter"><code>class</code></em> + is omitted, IN (internet) is assumed. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>[<span class="optional">prereq</span>] yxrrset</strong></span> + {domain-name} + [class] + {type} + {data...} + </span></dt> +<dd> + <p> + The + <em class="parameter"><code>data</code></em> + from each set of prerequisites of this form + sharing a common + <em class="parameter"><code>type</code></em>, + <em class="parameter"><code>class</code></em>, + and + <em class="parameter"><code>domain-name</code></em> + are combined to form a set of RRs. This set of RRs must + exactly match the set of RRs existing in the zone at the + given + <em class="parameter"><code>type</code></em>, + <em class="parameter"><code>class</code></em>, + and + <em class="parameter"><code>domain-name</code></em>. + The + <em class="parameter"><code>data</code></em> + are written in the standard text representation of the resource + record's + RDATA. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>[<span class="optional">update</span>] del[<span class="optional">ete</span>]</strong></span> + {domain-name} + [ttl] + [class] + [type [data...]] + </span></dt> +<dd> + <p> + Deletes any resource records named + <em class="parameter"><code>domain-name</code></em>. + If + <em class="parameter"><code>type</code></em> + and + <em class="parameter"><code>data</code></em> + is provided, only matching resource records will be removed. + The internet class is assumed if + <em class="parameter"><code>class</code></em> + is not supplied. The + <em class="parameter"><code>ttl</code></em> + is ignored, and is only allowed for compatibility. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>[<span class="optional">update</span>] add</strong></span> + {domain-name} + {ttl} + [class] + {type} + {data...} + </span></dt> +<dd> + <p> + Adds a new resource record with the specified + <em class="parameter"><code>ttl</code></em>, + <em class="parameter"><code>class</code></em> + and + <em class="parameter"><code>data</code></em>. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>show</strong></span> + </span></dt> +<dd> + <p> + Displays the current message, containing all of the + prerequisites and + updates specified since the last send. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>send</strong></span> + </span></dt> +<dd> + <p> + Sends the current message. This is equivalent to entering a + blank line. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>answer</strong></span> + </span></dt> +<dd> + <p> + Displays the answer. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>debug</strong></span> + </span></dt> +<dd> + <p> + Turn on debugging. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>version</strong></span> + </span></dt> +<dd> + <p> + Print version number. + </p> + </dd> +<dt><span class="term"> + <span class="command"><strong>help</strong></span> + </span></dt> +<dd> + <p> + Print a list of commands. + </p> + </dd> +</dl></div> +<p> + </p> + + <p> + Lines beginning with a semicolon are comments and are ignored. + </p> + + </div> + + <div class="refsection"> +<a name="id-1.10"></a><h2>EXAMPLES</h2> + + <p> + The examples below show how + <span class="command"><strong>nsupdate</strong></span> + could be used to insert and delete resource records from the + <span class="type">example.com</span> + zone. + Notice that the input in each example contains a trailing blank line so + that + a group of commands are sent as one dynamic update request to the + master name server for + <span class="type">example.com</span>. + + </p> +<pre class="programlisting"> +# nsupdate +> update delete oldhost.example.com A +> update add newhost.example.com 86400 A 172.16.1.1 +> send +</pre> +<p> + </p> + <p> + Any A records for + <span class="type">oldhost.example.com</span> + are deleted. + And an A record for + <span class="type">newhost.example.com</span> + with IP address 172.16.1.1 is added. + The newly-added record has a 1 day TTL (86400 seconds). + </p> +<pre class="programlisting"> +# nsupdate +> prereq nxdomain nickname.example.com +> update add nickname.example.com 86400 CNAME somehost.example.com +> send +</pre> +<p> + </p> + <p> + The prerequisite condition gets the name server to check that there + are no resource records of any type for + <span class="type">nickname.example.com</span>. + + If there are, the update request fails. + If this name does not exist, a CNAME for it is added. + This ensures that when the CNAME is added, it cannot conflict with the + long-standing rule in RFC 1034 that a name must not exist as any other + record type if it exists as a CNAME. + (The rule has been updated for DNSSEC in RFC 2535 to allow CNAMEs to have + RRSIG, DNSKEY and NSEC records.) + </p> + </div> + + <div class="refsection"> +<a name="id-1.11"></a><h2>FILES</h2> + + + <div class="variablelist"><dl class="variablelist"> +<dt><span class="term"><code class="constant">/etc/resolv.conf</code></span></dt> +<dd> + <p> + used to identify default name server + </p> + </dd> +<dt><span class="term"><code class="constant">/var/run/named/session.key</code></span></dt> +<dd> + <p> + sets the default TSIG key for use in local-only mode + </p> + </dd> +<dt><span class="term"><code class="constant">K{name}.+157.+{random}.key</code></span></dt> +<dd> + <p> + base-64 encoding of HMAC-MD5 key created by + <span class="citerefentry"> + <span class="refentrytitle">dnssec-keygen</span>(8) + </span>. + </p> + </dd> +<dt><span class="term"><code class="constant">K{name}.+157.+{random}.private</code></span></dt> +<dd> + <p> + base-64 encoding of HMAC-MD5 key created by + <span class="citerefentry"> + <span class="refentrytitle">dnssec-keygen</span>(8) + </span>. + </p> + </dd> +</dl></div> + </div> + + <div class="refsection"> +<a name="id-1.12"></a><h2>SEE ALSO</h2> + + <p> + <em class="citetitle">RFC 2136</em>, + <em class="citetitle">RFC 3007</em>, + <em class="citetitle">RFC 2104</em>, + <em class="citetitle">RFC 2845</em>, + <em class="citetitle">RFC 1034</em>, + <em class="citetitle">RFC 2535</em>, + <em class="citetitle">RFC 2931</em>, + <span class="citerefentry"> + <span class="refentrytitle">named</span>(8) + </span>, + <span class="citerefentry"> + <span class="refentrytitle">ddns-confgen</span>(8) + </span>, + <span class="citerefentry"> + <span class="refentrytitle">dnssec-keygen</span>(8) + </span>. + </p> + </div> + + <div class="refsection"> +<a name="id-1.13"></a><h2>BUGS</h2> + + <p> + The TSIG key is redundantly stored in two separate files. + This is a consequence of nsupdate using the DST library + for its cryptographic operations, and may change in future + releases. + </p> + </div> + +</div></body> +</html> diff --git a/bin/nsupdate/win32/nsupdate.dsp.in b/bin/nsupdate/win32/nsupdate.dsp.in new file mode 100644 index 0000000..18442a5 --- /dev/null +++ b/bin/nsupdate/win32/nsupdate.dsp.in @@ -0,0 +1,103 @@ +# Microsoft Developer Studio Project File - Name="nsupdate" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "@PLATFORM@ (x86) Console Application" 0x0103 + +CFG=nsupdate - @PLATFORM@ Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "nsupdate.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "nsupdate.mak" CFG="nsupdate - @PLATFORM@ Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "nsupdate - @PLATFORM@ Release" (based on "@PLATFORM@ (x86) Console Application") +!MESSAGE "nsupdate - @PLATFORM@ Debug" (based on "@PLATFORM@ (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "nsupdate - @PLATFORM@ Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 @COPTX@ @COPTI@ /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" @COPTY@ /FD /c +# ADD CPP /nologo /MD /W3 @COPTX@ @COPTI@ /O2 /I "./" /I "../include" /I "../../../" @LIBXML2_INC@ @OPENSSL_INC@ @GSSAPI_INC@ @READLINE_INC@ /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/include" /I "../../../lib/lwres/win32/include" /I "../../../lib/lwres/include" /I "../../../lib/lwres/win32/include/lwres" /I "../../../lib/dns/include" /I "../../../lib/bind9/include" /I "../../../lib/isccfg/include" /D "WIN32" @CRYPTO@ @USE_GSSAPI@ /D "USE_READLINE_STATIC" /D "__STDC__" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" @COPTY@ /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console @MACHINE@ +# ADD LINK32 @GSSAPI_LIB@ @KRB5_LIB@ @READLINE_LIB@ ../../../lib/isc/win32/Release/libisc.lib ../../../lib/dns/win32/Release/libdns.lib ../../../lib/lwres/win32/Release/liblwres.lib user32.lib advapi32.lib ws2_32.lib ../../../lib/bind9/win32/Release/libbind9.lib ../../../lib/isccfg/win32/Release/libisccfg.lib /nologo /subsystem:console @MACHINE@ /out:"../../../Build/Release/nsupdate.exe" + +!ELSEIF "$(CFG)" == "nsupdate - @PLATFORM@ Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm @COPTX@ @COPTI@ /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" @COPTY@ /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm @COPTX@ @COPTI@ /ZI /Od /I "./" /I "../include" /I "../../../" @LIBXML2_INC@ @OPENSSL_INC@ @GSSAPI_INC@ @READLINE_INC@ /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/include" /I "../../../lib/lwres/win32/include" /I "../../../lib/lwres/include" /I "../../../lib/lwres/win32/include/lwres" /I "../../../lib/dns/include" /I "../../../lib/bind9/include" /I "../../../lib/isccfg/include" /D "WIN32" @CRYPTO@ @USE_GSSAPI@ /D "USE_READLINE_STATIC" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c +# SUBTRACT CPP /X /u @COPTY@ +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug @MACHINE@ /pdbtype:sept +# ADD LINK32 @GSSAPI_LIB@ @KRB5_LIB@ @READLINE_LIBD@ ../../../lib/isc/win32/Debug/libisc.lib ../../../lib/dns/win32/Debug/libdns.lib ../../../lib/lwres/win32/Debug/liblwres.lib user32.lib advapi32.lib ws2_32.lib ../../../lib/bind9/win32/Debug/libbind9.lib ../../../lib/isccfg/win32/Debug/libisccfg.lib /nologo /subsystem:console /debug @MACHINE@ /out:"../../../Build/Debug/nsupdate.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "nsupdate - @PLATFORM@ Release" +# Name "nsupdate - @PLATFORM@ Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\nsupdate.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/bin/nsupdate/win32/nsupdate.dsw b/bin/nsupdate/win32/nsupdate.dsw new file mode 100644 index 0000000..5f0ac36 --- /dev/null +++ b/bin/nsupdate/win32/nsupdate.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "nsupdate"=".\nsupdate.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/bin/nsupdate/win32/nsupdate.mak.in b/bin/nsupdate/win32/nsupdate.mak.in new file mode 100644 index 0000000..7095e13 --- /dev/null +++ b/bin/nsupdate/win32/nsupdate.mak.in @@ -0,0 +1,375 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on nsupdate.dsp +!IF "$(CFG)" == "" +CFG=nsupdate - @PLATFORM@ Debug +!MESSAGE No configuration specified. Defaulting to nsupdate - @PLATFORM@ Debug. +!ENDIF + +!IF "$(CFG)" != "nsupdate - @PLATFORM@ Release" && "$(CFG)" != "nsupdate - @PLATFORM@ Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "nsupdate.mak" CFG="nsupdate - @PLATFORM@ Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "nsupdate - @PLATFORM@ Release" (based on "@PLATFORM@ (x86) Console Application") +!MESSAGE "nsupdate - @PLATFORM@ Debug" (based on "@PLATFORM@ (x86) Console Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "nsupdate - @PLATFORM@ Release" +_VC_MANIFEST_INC=0 +_VC_MANIFEST_BASENAME=__VC80 +!ELSE +_VC_MANIFEST_INC=1 +_VC_MANIFEST_BASENAME=__VC80.Debug +!ENDIF + +#################################################### +# Specifying name of temporary resource file used only in incremental builds: + +!if "$(_VC_MANIFEST_INC)" == "1" +_VC_MANIFEST_AUTO_RES=$(_VC_MANIFEST_BASENAME).auto.res +!else +_VC_MANIFEST_AUTO_RES= +!endif + +#################################################### +# _VC_MANIFEST_EMBED_EXE - command to embed manifest in EXE: + +!if "$(_VC_MANIFEST_INC)" == "1" + +#MT_SPECIAL_RETURN=1090650113 +#MT_SPECIAL_SWITCH=-notify_resource_update +MT_SPECIAL_RETURN=0 +MT_SPECIAL_SWITCH= +_VC_MANIFEST_EMBED_EXE= \ +if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \ +if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \ +rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \ +link $** /out:$@ $(LFLAGS) + +!else + +_VC_MANIFEST_EMBED_EXE= \ +if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;1 + +!endif + +#################################################### +# _VC_MANIFEST_EMBED_DLL - command to embed manifest in DLL: + +!if "$(_VC_MANIFEST_INC)" == "1" + +#MT_SPECIAL_RETURN=1090650113 +#MT_SPECIAL_SWITCH=-notify_resource_update +MT_SPECIAL_RETURN=0 +MT_SPECIAL_SWITCH= +_VC_MANIFEST_EMBED_EXE= \ +if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \ +if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \ +rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \ +link $** /out:$@ $(LFLAGS) + +!else + +_VC_MANIFEST_EMBED_EXE= \ +if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;2 + +!endif +#################################################### +# _VC_MANIFEST_CLEAN - command to clean resources files generated temporarily: + +!if "$(_VC_MANIFEST_INC)" == "1" + +_VC_MANIFEST_CLEAN=-del $(_VC_MANIFEST_BASENAME).auto.res \ + $(_VC_MANIFEST_BASENAME).auto.rc \ + $(_VC_MANIFEST_BASENAME).auto.manifest + +!else + +_VC_MANIFEST_CLEAN= + +!endif + +!IF "$(CFG)" == "nsupdate - @PLATFORM@ Release" + +OUTDIR=.\Release +INTDIR=.\Release + +!IF "$(RECURSE)" == "0" + +ALL : "..\..\..\Build\Release\nsupdate.exe" + +!ELSE + +ALL : "libbind9 - @PLATFORM@ Release" "libisc - @PLATFORM@ Release" "libdns - @PLATFORM@ Release" "..\..\..\Build\Release\nsupdate.exe" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libdns - @PLATFORM@ ReleaseCLEAN" "libisc - @PLATFORM@ ReleaseCLEAN" "libbind9 - @PLATFORM@ ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\nsupdate.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "..\..\..\Build\Release\nsupdate.exe" + -@$(_VC_MANIFEST_CLEAN) + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP_PROJ=/nologo /MD /W3 @COPTX@ @COPTI@ /O2 /I "./" /I "../include" /I "../../../" @LIBXML2_INC@ @OPENSSL_INC@ @GSSAPI_INC@ @READLINE_INC@ /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/include" /I "../../../lib/lwres/win32/include" /I "../../../lib/lwres/include" /I "../../../lib/lwres/win32/include/lwres" /I "../../../lib/dns/include" /I "../../../lib/bind9/include" /I "../../../lib/isccfg/include" /D "WIN32" @CRYPTO@ @USE_GSSAPI@ /D "USE_READLINE_STATIC" /D "__STDC__" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\nsupdate.pch" @COPTY@ /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\nsupdate.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=../../../lib/isc/win32/Release/libisc.lib ../../../lib/dns/win32/Release/libdns.lib ../../../lib/lwres/win32/Release/liblwres.lib user32.lib advapi32.lib ws2_32.lib ../../../lib/bind9/win32/Release/libbind9.lib ../../../lib/isccfg/win32/Release/libisccfg.lib @GSSAPI_LIB@ @KRB5_LIB@ @READLINE_LIB@ /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\nsupdate.pdb" @MACHINE@ /out:"../../../Build/Release/nsupdate.exe" +LINK32_OBJS= \ + "$(INTDIR)\nsupdate.obj" \ + "..\..\..\lib\dns\win32\Release\libdns.lib" \ + "..\..\..\lib\isc\win32\Release\libisc.lib" \ + "..\..\..\lib\bind9\win32\Release\libbind9.lib" \ + "..\..\..\lib\isccfg\win32\Release\libisccfg.lib" + +"..\..\..\Build\Release\nsupdate.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + $(_VC_MANIFEST_EMBED_EXE) + +!ELSEIF "$(CFG)" == "nsupdate - @PLATFORM@ Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "..\..\..\Build\Debug\nsupdate.exe" "$(OUTDIR)\nsupdate.bsc" + +!ELSE + +ALL : "libbind9 - @PLATFORM@ Debug" "libisc - @PLATFORM@ Debug" "libdns - @PLATFORM@ Debug" "..\..\..\Build\Debug\nsupdate.exe" "$(OUTDIR)\nsupdate.bsc" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libdns - @PLATFORM@ DebugCLEAN" "libisc - @PLATFORM@ DebugCLEAN" "libbind9 - @PLATFORM@ DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\nsupdate.obj" + -@erase "$(INTDIR)\nsupdate.sbr" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(OUTDIR)\nsupdate.bsc" + -@erase "$(OUTDIR)\nsupdate.pdb" + -@erase "..\..\..\Build\Debug\nsupdate.exe" + -@erase "..\..\..\Build\Debug\nsupdate.ilk" + -@$(_VC_MANIFEST_CLEAN) + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP_PROJ=/nologo /MDd /W3 /Gm @COPTX@ @COPTI@ /ZI /Od /I "./" /I "../include" /I "../../../" @LIBXML2_INC@ @OPENSSL_INC@ @GSSAPI_INC@ @READLINE_INC@ /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/include" /I "../../../lib/lwres/win32/include" /I "../../../lib/lwres/include" /I "../../../lib/lwres/win32/include/lwres" /I "../../../lib/dns/include" /I "../../../lib/bind9/include" /I "../../../lib/isccfg/include" /D "WIN32" @CRYPTO@ @USE_GSSAPI@ /D "USE_READLINE_STATIC" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\nsupdate.bsc" +BSC32_SBRS= \ + "$(INTDIR)\nsupdate.sbr" + +"$(OUTDIR)\nsupdate.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +LINK32_FLAGS=../../../lib/isc/win32/Debug/libisc.lib ../../../lib/dns/win32/Debug/libdns.lib ../../../lib/lwres/win32/Debug/liblwres.lib user32.lib advapi32.lib ws2_32.lib ../../../lib/bind9/win32/Debug/libbind9.lib ../../../lib/isccfg/win32/Debug/libisccfg.lib @GSSAPI_LIB@ @KRB5_LIB@ @READLINE_LIBD@ /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\nsupdate.pdb" /debug @MACHINE@ /out:"../../../Build/Debug/nsupdate.exe" /pdbtype:sept +LINK32_OBJS= \ + "$(INTDIR)\nsupdate.obj" \ + "..\..\..\lib\dns\win32\Debug\libdns.lib" \ + "..\..\..\lib\isc\win32\Debug\libisc.lib" \ + "..\..\..\lib\bind9\win32\Debug\libbind9.lib" \ + "..\..\..\lib\isccfg\win32\Release\libisccfg.lib" + +"..\..\..\Build\Debug\nsupdate.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + $(_VC_MANIFEST_EMBED_EXE) + +!ENDIF + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("nsupdate.dep") +!INCLUDE "nsupdate.dep" +!ELSE +!MESSAGE Warning: cannot find "nsupdate.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "nsupdate - @PLATFORM@ Release" || "$(CFG)" == "nsupdate - @PLATFORM@ Debug" +SOURCE=..\nsupdate.c + +!IF "$(CFG)" == "nsupdate - @PLATFORM@ Release" + + +"$(INTDIR)\nsupdate.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "nsupdate - @PLATFORM@ Debug" + + +"$(INTDIR)\nsupdate.obj" "$(INTDIR)\nsupdate.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +!IF "$(CFG)" == "nsupdate - @PLATFORM@ Release" + +"libdns - @PLATFORM@ Release" : + cd "..\..\..\lib\dns\win32" + $(MAKE) /$(MAKEFLAGS) /F ".\libdns.mak" CFG="libdns - @PLATFORM@ Release" + cd "..\..\..\bin\nsupdate\win32" + +"libdns - @PLATFORM@ ReleaseCLEAN" : + cd "..\..\..\lib\dns\win32" + $(MAKE) /$(MAKEFLAGS) /F ".\libdns.mak" CFG="libdns - @PLATFORM@ Release" RECURSE=1 CLEAN + cd "..\..\..\bin\nsupdate\win32" + +!ELSEIF "$(CFG)" == "nsupdate - @PLATFORM@ Debug" + +"libdns - @PLATFORM@ Debug" : + cd "..\..\..\lib\dns\win32" + $(MAKE) /$(MAKEFLAGS) /F ".\libdns.mak" CFG="libdns - @PLATFORM@ Debug" + cd "..\..\..\bin\nsupdate\win32" + +"libdns - @PLATFORM@ DebugCLEAN" : + cd "..\..\..\lib\dns\win32" + $(MAKE) /$(MAKEFLAGS) /F ".\libdns.mak" CFG="libdns - @PLATFORM@ Debug" RECURSE=1 CLEAN + cd "..\..\..\bin\nsupdate\win32" + +!ENDIF + +!IF "$(CFG)" == "nsupdate - @PLATFORM@ Release" + +"libisc - @PLATFORM@ Release" : + cd "..\..\..\lib\isc\win32" + $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - @PLATFORM@ Release" + cd "..\..\..\bin\nsupdate\win32" + +"libisc - @PLATFORM@ ReleaseCLEAN" : + cd "..\..\..\lib\isc\win32" + $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - @PLATFORM@ Release" RECURSE=1 CLEAN + cd "..\..\..\bin\nsupdate\win32" + +!ELSEIF "$(CFG)" == "nsupdate - @PLATFORM@ Debug" + +"libisc - @PLATFORM@ Debug" : + cd "..\..\..\lib\isc\win32" + $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - @PLATFORM@ Debug" + cd "..\..\..\bin\nsupdate\win32" + +"libisc - @PLATFORM@ DebugCLEAN" : + cd "..\..\..\lib\isc\win32" + $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - @PLATFORM@ Debug" RECURSE=1 CLEAN + cd "..\..\..\bin\nsupdate\win32" + +!ENDIF + +!IF "$(CFG)" == "nsupdate - @PLATFORM@ Release" + +"libbind9 - @PLATFORM@ Release" : + cd "..\..\..\lib\bind9\win32" + $(MAKE) /$(MAKEFLAGS) /F ".\libbind9.mak" CFG="libbind9 - @PLATFORM@ Release" + cd "..\..\..\bin\nsupdate\win32" + +"libbind9 - @PLATFORM@ ReleaseCLEAN" : + cd "..\..\..\lib\bind9\win32" + $(MAKE) /$(MAKEFLAGS) /F ".\libbind9.mak" CFG="libbind9 - @PLATFORM@ Release" RECURSE=1 CLEAN + cd "..\..\..\bin\nsupdate\win32" + +!ELSEIF "$(CFG)" == "nsupdate - @PLATFORM@ Debug" + +"libbind9 - @PLATFORM@ Debug" : + cd "..\..\..\lib\bind9\win32" + $(MAKE) /$(MAKEFLAGS) /F ".\libbind9.mak" CFG="libbind9 - @PLATFORM@ Debug" + cd "..\..\..\bin\nsupdate\win32" + +"libbind9 - @PLATFORM@ DebugCLEAN" : + cd "..\..\..\lib\bind9\win32" + $(MAKE) /$(MAKEFLAGS) /F ".\libbind9.mak" CFG="libbind9 - @PLATFORM@ Debug" RECURSE=1 CLEAN + cd "..\..\..\bin\nsupdate\win32" + +!ENDIF + + +!ENDIF + +#################################################### +# Commands to generate initial empty manifest file and the RC file +# that references it, and for generating the .res file: + +$(_VC_MANIFEST_BASENAME).auto.res : $(_VC_MANIFEST_BASENAME).auto.rc + +$(_VC_MANIFEST_BASENAME).auto.rc : $(_VC_MANIFEST_BASENAME).auto.manifest + type <<$@ +#include <winuser.h> +1RT_MANIFEST"$(_VC_MANIFEST_BASENAME).auto.manifest" +<< KEEP + +$(_VC_MANIFEST_BASENAME).auto.manifest : + type <<$@ +<?xml version='1.0' encoding='UTF-8' standalone='yes'?> +<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'> +</assembly> +<< KEEP diff --git a/bin/nsupdate/win32/nsupdate.vcxproj.filters.in b/bin/nsupdate/win32/nsupdate.vcxproj.filters.in new file mode 100644 index 0000000..477847e --- /dev/null +++ b/bin/nsupdate/win32/nsupdate.vcxproj.filters.in @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\nsupdate.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/bin/nsupdate/win32/nsupdate.vcxproj.in b/bin/nsupdate/win32/nsupdate.vcxproj.in new file mode 100644 index 0000000..58a1a36 --- /dev/null +++ b/bin/nsupdate/win32/nsupdate.vcxproj.in @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|@PLATFORM@"> + <Configuration>Debug</Configuration> + <Platform>@PLATFORM@</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|@PLATFORM@"> + <Configuration>Release</Configuration> + <Platform>@PLATFORM@</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{C41266C7-E27E-4D60-9815-82D3B32BF82F}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>nsupdate</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>..\..\..\Build\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\..\..\Build\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;@CRYPTO@@USE_GSSAPI@USE_READLINE_STATIC;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <FunctionLevelLinking>true</FunctionLevelLinking> + <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation> + <ObjectFileName>.\$(Configuration)\</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName> + <BrowseInformation>true</BrowseInformation> + <AdditionalIncludeDirectories>.\;..\include;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@GSSAPI_INC@@READLINE_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\lwres\win32\include;..\..\..\lib\lwres\include;..\..\..\lib\lwres\win32\include\lwres;..\..\..\lib\dns\include;..\..\..\lib\bind9\include;..\..\..\lib\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <CompileAs>CompileAsC</CompileAs> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + <AdditionalLibraryDirectories>..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);..\..\..\lib\lwres\win32\$(Configuration);..\..\..\lib\bind9\win32\$(Configuration);..\..\..\lib\isccfg\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>@READLINE_LIBD@@GSSAPI_LIB@@KRB5_LIB@libisc.lib;libdns.lib;liblwres.lib;libbind9.lib;libisccfg.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;@CRYPTO@@USE_GSSAPI@USE_READLINE_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <WholeProgramOptimization>false</WholeProgramOptimization> + <StringPooling>true</StringPooling> + <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation> + <ObjectFileName>.\$(Configuration)\</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName> + <AdditionalIncludeDirectories>.\;..\include;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@GSSAPI_INC@@READLINE_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\lwres\win32\include;..\..\..\lib\lwres\include;..\..\..\lib\lwres\win32\include\lwres;..\..\..\lib\dns\include;..\..\..\lib\bind9\include;..\..\..\lib\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <CompileAs>CompileAsC</CompileAs> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <AdditionalLibraryDirectories>..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);..\..\..\lib\lwres\win32\$(Configuration);..\..\..\lib\bind9\win32\$(Configuration);..\..\..\lib\isccfg\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>@READLINE_LIB@@GSSAPI_LIB@@KRB5_LIB@libisc.lib;libdns.lib;liblwres.lib;libbind9.lib;libisccfg.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\nsupdate.c" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/bin/nsupdate/win32/nsupdate.vcxproj.user b/bin/nsupdate/win32/nsupdate.vcxproj.user new file mode 100644 index 0000000..695b5c7 --- /dev/null +++ b/bin/nsupdate/win32/nsupdate.vcxproj.user @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project>
\ No newline at end of file |