1 #if !defined(lint) && !defined(SABER)
2 static const char rcsid[] = "$Id: nsupdate.c,v 8.21 1999/10/19 22:22:59 cyarnell Exp $";
6 * Copyright (c) 1996,1999 by Internet Software Consortium.
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
13 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
14 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
15 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
18 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
22 #include "port_before.h"
24 #include <sys/param.h>
25 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <arpa/nameser.h>
36 #include <res_update.h>
43 #include "port_after.h"
44 #include "../named/db_defs.h"
46 /* XXX all of this stuff should come from libbind.a */
49 * Map class and type names to number
56 struct map class_strs[] = {
61 #define M_CLASS_CNT (sizeof(class_strs) / sizeof(struct map))
63 struct map type_strs[] = {
84 { "nsap_ptr", T_NSAP_PTR },
91 { "nimloc", T_NIMLOC },
96 { "cert", ns_t_cert },
97 { "aaaa", ns_t_aaaa },
99 #define M_TYPE_CNT (sizeof(type_strs) / sizeof(struct map))
101 struct map section_strs[] = {
103 { "prereq", S_PREREQ },
104 { "update", S_UPDATE },
105 { "reserved", S_ADDT },
107 #define M_SECTION_CNT (sizeof(section_strs) / sizeof(struct map))
109 struct map opcode_strs[] = {
110 { "nxdomain", NXDOMAIN },
111 { "yxdomain", YXDOMAIN },
112 { "nxrrset", NXRRSET },
113 { "yxrrset", YXRRSET },
114 { "delete", DELETE },
117 #define M_OPCODE_CNT (sizeof(opcode_strs) / sizeof(struct map))
119 static int getcharstring(char *, char *, int, int, int);
120 static char *progname;
122 static void usage(void);
123 static int getword_str(char *, int, char **, char *);
125 static struct __res_state res;
127 int dns_findprimary (res_state, char *, struct ns_tsig_key *, char *,
128 int, struct in_addr *);
131 * format of file read by nsupdate is kept the same as the log
132 * file generated by updates, so that the log file can be fed
133 * to nsupdate to reconstruct lost updates.
135 * file is read on line at a time using fgets() rather than
136 * one word at a time using getword() so that it is easy to
137 * adapt nsupdate to read piped input from other scripts
139 * overloading of class/type has to be deferred to res_update()
140 * because class is needed by res_update() to determined the
141 * zone to which a resource record belongs
149 char buf[BUFSIZ], buf2[BUFSIZ], hostbuf[100], filebuf[100];
150 char dnbuf[MAXDNAME], data[MAXDATA];
151 u_char packet[PACKETSZ], answer[PACKETSZ];
152 char *host = hostbuf, *batchfile = filebuf;
153 char *r_dname, *cp, *startp, *endp, *svstartp;
154 char section[15], opcode[10];
155 int i, c, n, n1, inside, lineno = 0, vc = 0,
156 debug = 0, r_size, r_section, r_opcode,
157 prompt = 0, ret = 0, stringtobin = 0;
158 int16_t r_class, r_type;
163 struct in_addr hostaddr;
166 extern int optind, opterr, optopt;
168 char *keyfile=NULL, *keyname=NULL, *p, *pp;
169 int file_major, file_minor, alg;
175 while ((c = getopt(argc, argv, "dsvk:n:")) != -1) {
187 /* -k keydir:keyname */
190 if ((colon=strchr(optarg, ':'))==NULL) {
191 fprintf(stderr, "key option argument should be keydir:keyname\n");
209 if ((fp=fopen(keyfile, "r"))==NULL) {
210 perror("open keyfile");
213 /* now read the header info from the file */
214 if ((i=fread(buf, 1, BUFSIZ, fp)) < 5) {
223 n=strlen(p); /* get length of strings */
224 n1=strlen("Private-key-format: v");
225 if (n1 > n || strncmp(buf, "Private-key-format: v", n1)) {
226 fprintf(stderr, "Invalid key file format\n");
227 exit(1); /* not a match */
229 p+=n1; /* advance pointer */
230 sscanf((char *)p, "%d.%d", &file_major, &file_minor);
231 /* should do some error checking with these someday */
232 while (*p++!='\n'); /* skip to end of line */
234 n=strlen(p); /* get length of strings */
235 n1=strlen("Algorithm: ");
236 if (n1 > n || strncmp(p, "Algorithm: ", n1)) {
237 fprintf(stderr, "Invalid key file format\n");
238 exit(1); /* not a match */
240 p+=n1; /* advance pointer */
241 if (sscanf((char *)p, "%d", &alg)!=1) {
242 fprintf(stderr, "Invalid key file format\n");
245 while (*p++!='\n'); /* skip to end of line */
247 n=strlen(p); /* get length of strings */
249 if (n1 > n || strncmp(p, "Key: ", n1)) {
250 fprintf(stderr, "Invalid key file format\n");
251 exit(1); /* not a match */
253 p+=n1; /* advance pointer */
255 while (*pp++!='\n'); /* skip to end of line, terminate it */
258 key.data=malloc(1024*sizeof(char));
259 key.len=b64_pton(p, key.data, 1024);
261 strcpy(key.name, keyname);
262 strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT");
264 /* use the dst* routines to parse the key files
266 * This requires that both the .key and the .private files
267 * exist in your cwd, so the keyfile parmeter here is
268 * assumed to be a path in which the K*.{key,private} files
272 char cwd[PATH_MAX+1];
274 if (getcwd(cwd, PATH_MAX)==NULL) {
275 perror("unable to get current directory");
278 if (chdir(keyfile)<0) {
279 fprintf(stderr, "unable to chdir to %s: %s\n", keyfile,
285 dst_key = dst_read_key(keyname,
286 0 /* not used for private keys */,
287 KEY_HMAC_MD5, DST_PRIVATE);
289 fprintf(stderr, "dst_read_key: error reading key\n");
292 key.data=malloc(1024*sizeof(char));
293 dst_key_to_buffer(dst_key, key.data, 1024);
294 key.len=dst_key->dk_key_size;
296 strcpy(key.name, keyname);
297 strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT");
300 fprintf(stderr, "unable to chdir to %s: %s\n", cwd,
307 if ((argc - optind) == 0) {
308 /* no file specified, read from stdin */
309 ret = system("tty -s");
310 if (ret == 0) /* terminal */
312 else /* stdin redirect from a file or a pipe */
315 /* file specified, open it */
316 /* XXX - currently accepts only one filename */
317 if ((fp = fopen(argv[optind], "r")) == NULL) {
318 fprintf(stderr, "error opening file: %s\n", argv[optind]);
326 fprintf(stdout, "> ");
328 cp = fgets(buf, sizeof buf, stdin);
330 cp = fgets(buf, sizeof buf, fp);
331 if (cp == NULL) /* EOF */
335 /* get rid of the trailing newline */
340 endp = strchr(cp, ';');
346 /* verify section name */
347 if (!getword_str(section, sizeof section, &startp, endp)) {
352 /* inside the same update packet,
353 * continue accumulating records */
355 n1 = strlen(section);
356 if (section[n1-1] == ':')
357 section[--n1] = '\0';
358 for (mp = section_strs; mp < section_strs+M_SECTION_CNT; mp++)
359 if (!strcasecmp(section, mp->token)) {
363 if (r_section == -1) {
364 fprintf(stderr, "incorrect section name: %s\n", section);
367 if (r_section == S_ZONE) {
368 fprintf(stderr, "section ZONE not permitted\n");
371 /* read operation code */
372 if (!getword_str(opcode, sizeof opcode, &startp, endp)) {
373 fprintf(stderr, "failed to read operation code\n");
377 if (opcode[0] == '{') {
379 for (i = 0; i < n1; i++)
380 opcode[i] = opcode[i+1];
381 if (opcode[n1-2] == '}')
384 for (mp = opcode_strs; mp < opcode_strs+M_OPCODE_CNT; mp++) {
385 if (!strcasecmp(opcode, mp->token)) {
390 if (r_opcode == -1) {
391 fprintf(stderr, "incorrect operation code: %s\n", opcode);
394 /* read owner's domain name */
395 if (!getword_str(dnbuf, sizeof dnbuf, &startp, endp)) {
396 fprintf(stderr, "failed to read owner name\n");
402 r_class = C_IN; /* default to IN */
405 (void) getword_str(buf2, sizeof buf2, &startp, endp);
407 if (isdigit(buf2[0])) { /* ttl */
408 r_ttl = strtoul(buf2, 0, 10);
409 if (errno == ERANGE && r_ttl == ULONG_MAX) {
410 fprintf(stderr, "oversized ttl: %s\n", buf2);
413 (void) getword_str(buf2, sizeof buf2, &startp, endp);
416 if (buf2[0]) { /* possibly class */
417 for (mp = class_strs; mp < class_strs+M_CLASS_CNT; mp++) {
418 if (!strcasecmp(buf2, mp->token)) {
420 (void) getword_str(buf2, sizeof buf2, &startp, endp);
426 * type and rdata field may or may not be required depending
427 * on the section and operation
432 fprintf(stderr, "nonzero ttl in prereq section: %ul\n",
440 fprintf (stderr, "invalid field: %s, ignored\n",
448 for (mp = type_strs; mp < type_strs+M_TYPE_CNT; mp++)
449 if (!strcasecmp(buf2, mp->token)) {
454 fprintf (stderr, "invalid type for RRset: %s\n",
458 if (r_opcode == NXRRSET)
461 * for RRset exists (value dependent) case,
462 * nonempty rdata field will be present.
463 * simply copy the whole string now and let
464 * res_update() interpret the various fields
468 while (cp <= endp && isspace(*cp))
470 r_size = endp - cp + 1;
474 "unknown operation in prereq section\"%s\"\n",
484 /* read type, if specified */
486 for (mp = type_strs; mp < type_strs+M_TYPE_CNT; mp++)
487 if (!strcasecmp(buf2, mp->token)) {
490 (void) getword_str(buf2, sizeof buf2,
492 if (buf2[0]) /* unget preference */
496 /* read rdata portion, if specified */
498 while (cp <= endp && isspace(*cp))
500 r_size = endp - cp + 1;
505 "ttl must be specified for record to be added: %s\n", buf);
510 for (mp = type_strs; mp < type_strs+M_TYPE_CNT; mp++)
511 if (!strcasecmp(buf2, mp->token)) {
517 "invalid type for record to be added: %s\n", buf2);
520 /* read rdata portion */
522 while (cp < endp && isspace(*cp))
524 r_size = endp - cp + 1;
527 "nonempty rdata field needed to add the record at line %d\n",
534 "unknown operation in update section \"%s\"\n", opcode);
540 "unknown section identifier \"%s\"\n", section);
544 if ( !(rrecp = res_mkupdrec(r_section, r_dname, r_class,
546 (r_size > 0 && !(rrecp->r_data = (u_char *)malloc(r_size))) ) {
548 res_freeupdrec(rrecp);
549 fprintf(stderr, "saverrec error\n");
555 if (!getcharstring(buf,(char *)data,2,2,lineno))
560 if (!getcharstring(buf,(char *)data,1,2,lineno))
565 if (!getcharstring(buf,(char *)data,1,0,lineno))
570 if (!getcharstring(buf,(char *)data,1,1,lineno))
578 rrecp->r_opcode = r_opcode;
579 rrecp->r_size = r_size;
580 (void) strncpy((char *)rrecp->r_data, cp, r_size);
581 APPEND(listuprec, rrecp, r_link);
582 } else { /* end of an update packet */
583 (void) res_ninit(&res);
585 res.options |= RES_USEVC | RES_STAYOPEN;
587 res.options |= RES_DEBUG;
588 if (!EMPTY(listuprec)) {
589 n = res_nupdate(&res, HEAD(listuprec),
590 keyfile != NULL ? &key : NULL);
592 fprintf(stderr, "failed update packet\n");
593 while (!EMPTY(listuprec)) {
594 ns_updrec *tmprrecp = HEAD(listuprec);
596 UNLINK(listuprec, tmprrecp, r_link);
597 if (tmprrecp->r_size != 0)
598 free((char *)tmprrecp->r_data);
599 res_freeupdrec(tmprrecp);
609 fprintf(stderr, "Usage: %s [ -k keydir:keyname ] [-d] [-v] [file]\n",
615 * Get a whitespace delimited word from a string (not file)
616 * into buf. modify the start pointer to point after the
617 * word in the string.
620 getword_str(char *buf, int size, char **startpp, char *endp) {
624 for (cp = buf; *startpp <= endp; ) {
626 if (isspace(c) || c == '\0') {
627 if (cp != buf) /* trailing whitespace */
629 else { /* leading whitespace */
635 if (cp >= buf+size-1)
643 #define MAXCHARSTRING 255
646 getcharstring(char *buf, char *data,
647 int minfields, int maxfields, int lineno)
649 int nfield = 0, n = 0, i;
656 while(buf[i] && buf[i] != '"')
662 if (i > MAXCHARSTRING) {
664 "%d: RDATA field %d too long",
668 if (n + i + 1 > MAXDATA) {
670 "%d: total RDATA too long", lineno);
674 memmove(data + 1 + n, buf, i);
677 while(*buf && isspace(*buf))
679 } while (nfield < maxfields && *buf);
681 if (nfield < minfields) {
683 "%d: expected %d RDATA fields, only saw %d",
684 lineno, minfields, nfield);