]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bind/bin/nsupdate/nsupdate.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.git] / contrib / bind / bin / nsupdate / nsupdate.c
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 $";
3 #endif /* not lint */
4
5 /*
6  * Copyright (c) 1996,1999 by Internet Software Consortium.
7  *
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.
11  *
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
19  * SOFTWARE.
20  */
21
22 #include "port_before.h"
23
24 #include <sys/param.h>
25 #include <sys/socket.h>
26
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <arpa/nameser.h>
30
31 #include <ctype.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <netdb.h>
35 #include <resolv.h>
36 #include <res_update.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <ctype.h>
42 #include <isc/dst.h>
43 #include "port_after.h"
44 #include "../named/db_defs.h"
45
46 /* XXX all of this stuff should come from libbind.a */
47
48 /*
49  * Map class and type names to number
50  */
51 struct map {
52         char    token[10];
53         int     val;
54 };
55
56 struct map class_strs[] = {
57         { "in",         C_IN },
58         { "chaos",      C_CHAOS },
59         { "hs",         C_HS },
60 };
61 #define M_CLASS_CNT (sizeof(class_strs) / sizeof(struct map))
62
63 struct map type_strs[] = {
64         { "a",          T_A },
65         { "ns",         T_NS },
66         { "cname",      T_CNAME },
67         { "soa",        T_SOA },
68         { "mb",         T_MB },
69         { "mg",         T_MG },
70         { "mr",         T_MR },
71         { "null",       T_NULL },
72         { "wks",        T_WKS },
73         { "ptr",        T_PTR },
74         { "hinfo",      T_HINFO },
75         { "minfo",      T_MINFO },
76         { "mx",         T_MX },
77         { "txt",        T_TXT },
78         { "rp",         T_RP },
79         { "afsdb",      T_AFSDB },
80         { "x25",        T_X25 },
81         { "isdn",       T_ISDN },
82         { "rt",         T_RT },
83         { "nsap",       T_NSAP },
84         { "nsap_ptr",   T_NSAP_PTR },
85         { "sig",        T_SIG },
86         { "key",        T_KEY },
87         { "px",         T_PX },
88         { "loc",        T_LOC },
89         { "nxt",        T_NXT },
90         { "eid",        T_EID },
91         { "nimloc",     T_NIMLOC },
92         { "srv",        T_SRV },
93         { "atma",       T_ATMA },
94         { "naptr",      T_NAPTR },
95         { "kx",         ns_t_kx },
96         { "cert",       ns_t_cert },
97         { "aaaa",       ns_t_aaaa },
98 };
99 #define M_TYPE_CNT (sizeof(type_strs) / sizeof(struct map))
100
101 struct map section_strs[] = {
102         { "zone",       S_ZONE },
103         { "prereq",     S_PREREQ },
104         { "update",     S_UPDATE },
105         { "reserved",   S_ADDT },
106 };
107 #define M_SECTION_CNT (sizeof(section_strs) / sizeof(struct map))
108
109 struct map opcode_strs[] = {
110         { "nxdomain",   NXDOMAIN },
111         { "yxdomain",   YXDOMAIN },
112         { "nxrrset",    NXRRSET },
113         { "yxrrset",    YXRRSET },
114         { "delete",     DELETE },
115         { "add",        ADD },
116 };
117 #define M_OPCODE_CNT (sizeof(opcode_strs) / sizeof(struct map))
118
119 static int getcharstring(char *, char *, int, int, int);
120 static char *progname;
121
122 static void usage(void);
123 static int getword_str(char *, int, char **, char *);
124
125 static struct __res_state res;
126
127 int dns_findprimary (res_state, char *, struct ns_tsig_key *, char *,
128                      int, struct in_addr *);
129
130 /*
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.
134  * 
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
138  *
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
142  */
143 int
144 main(argc, argv)
145         int argc;
146         char **argv;
147 {
148         FILE *fp = NULL;
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;
159         u_int32_t r_ttl;
160         struct map *mp;
161         ns_updrec *rrecp;
162         ns_updque listuprec;
163         struct in_addr hostaddr;
164         extern int getopt();
165         extern char *optarg;
166         extern int optind, opterr, optopt;
167         ns_tsig_key key;
168         char *keyfile=NULL, *keyname=NULL, *p, *pp;
169         int file_major, file_minor, alg;
170
171
172
173         progname = argv[0];
174
175         while ((c = getopt(argc, argv, "dsvk:n:")) != -1) {
176                 switch (c) {
177                 case 'v':
178                         vc = 1;
179                         break;
180                 case 'd':
181                         debug = 1;
182                         break;
183                 case 's':
184                         stringtobin = 1;
185                         break;
186                 case 'k': {
187                         /* -k keydir:keyname */
188                         char *colon;
189    
190                         if ((colon=strchr(optarg, ':'))==NULL) {
191                                 fprintf(stderr, "key option argument should be keydir:keyname\n");
192                                 exit(1);
193                         }
194                         keyname=colon+1;
195                         keyfile=optarg;
196                         *colon='\0';
197                         break;
198                 }
199                  case 'n':
200                         keyname=optarg;
201                         break;
202                 default:
203                         usage();
204                 }
205         }
206
207         if (keyfile) {
208 #ifdef PARSE_KEYFILE
209                 if ((fp=fopen(keyfile, "r"))==NULL) {
210                         perror("open keyfile");
211                         exit(1);
212                 }
213                 /* now read the header info from the file */
214                 if ((i=fread(buf, 1, BUFSIZ, fp)) < 5) {
215                         fclose(fp);
216                         exit(1);
217                 }
218                 fclose(fp);
219                 fp=NULL;
220
221                 p=buf;
222
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 */
228                 }
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 */
233
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 */
239                 }
240                 p+=n1;          /* advance pointer */
241                 if (sscanf((char *)p, "%d", &alg)!=1) {
242                         fprintf(stderr, "Invalid key file format\n");
243                         exit(1);
244                 }
245                 while (*p++!='\n');     /* skip to end of line */
246
247                 n=strlen(p);            /* get length of strings */
248                 n1=strlen("Key: ");
249                 if (n1 > n || strncmp(p, "Key: ", n1)) {
250                         fprintf(stderr, "Invalid key file format\n");
251                         exit(1);        /* not a match */
252                 }
253                 p+=n1;          /* advance pointer */
254                 pp=p;
255                 while (*pp++!='\n');    /* skip to end of line, terminate it */
256                 *--pp='\0';
257
258                 key.data=malloc(1024*sizeof(char));
259                 key.len=b64_pton(p, key.data, 1024);
260
261                 strcpy(key.name, keyname);
262                 strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT");
263 #else
264                 /* use the dst* routines to parse the key files
265                  * 
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
269                  * exist.
270                  */
271                 DST_KEY *dst_key;
272                 char cwd[PATH_MAX+1];
273
274                 if (getcwd(cwd, PATH_MAX)==NULL) {
275                         perror("unable to get current directory");
276                         exit(1);
277                 }
278                 if (chdir(keyfile)<0) {
279                         fprintf(stderr, "unable to chdir to %s: %s\n", keyfile,
280                                 strerror(errno));
281                         exit(1);
282                 }
283
284                 dst_init();
285                 dst_key = dst_read_key(keyname,
286                                        0 /* not used for private keys */,
287                                        KEY_HMAC_MD5, DST_PRIVATE);
288                 if (!dst_key) {
289                         fprintf(stderr, "dst_read_key: error reading key\n");
290                         exit(1);
291                 }
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;
295
296                 strcpy(key.name, keyname);
297                 strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT");
298
299                 if (chdir(cwd)<0) {
300                         fprintf(stderr, "unable to chdir to %s: %s\n", cwd,
301                                 strerror(errno));
302                         exit(1);
303                 }
304 #endif
305         }
306
307         if ((argc - optind) == 0) {
308             /* no file specified, read from stdin */
309             ret = system("tty -s");
310             if (ret == 0) /* terminal */
311                 prompt = 1;
312             else /* stdin redirect from a file or a pipe */
313                 prompt = 0;
314         } else {
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]);
319                 exit (1);
320             }
321         }
322         for (;;) {
323
324             inside = 1;
325             if (prompt)
326                 fprintf(stdout, "> ");
327             if (!fp)
328                 cp = fgets(buf, sizeof buf, stdin);
329             else
330                 cp = fgets(buf, sizeof buf, fp);
331             if (cp == NULL) /* EOF */
332                 break;
333             lineno++;
334
335             /* get rid of the trailing newline */
336             n = strlen(buf);
337             buf[--n] = '\0';
338  
339             startp = cp;
340             endp = strchr(cp, ';');
341             if (endp != NULL)
342                 endp--;
343             else
344                 endp = cp + n - 1;
345
346             /* verify section name */
347             if (!getword_str(section, sizeof section, &startp, endp)) {
348                 /* empty line */
349                 inside = 0;
350             }
351             if (inside) {
352                 /* inside the same update packet,
353                  * continue accumulating records */
354                 r_section = -1;
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)) {
360                         r_section = mp->val;
361                         break;
362                     }
363                 if (r_section == -1) {
364                     fprintf(stderr, "incorrect section name: %s\n", section);
365                     exit (1);
366                 }
367                 if (r_section == S_ZONE) {
368                     fprintf(stderr, "section ZONE not permitted\n");
369                     exit (1);
370                 }
371                 /* read operation code */
372                 if (!getword_str(opcode, sizeof opcode, &startp, endp)) {
373                         fprintf(stderr, "failed to read operation code\n");
374                         exit (1);
375                 }
376                 r_opcode = -1;
377                 if (opcode[0] == '{') {
378                     n1 = strlen(opcode);
379                     for (i = 0; i < n1; i++)
380                         opcode[i] = opcode[i+1];
381                     if (opcode[n1-2] == '}')
382                         opcode[n1-2] = '\0';
383                 }
384                 for (mp = opcode_strs; mp < opcode_strs+M_OPCODE_CNT; mp++) {
385                     if (!strcasecmp(opcode, mp->token)) {
386                         r_opcode = mp->val;
387                         break;
388                     }
389                 }
390                 if (r_opcode == -1) {
391                     fprintf(stderr, "incorrect operation code: %s\n", opcode);
392                     exit (1);
393                 }
394                 /* read owner's domain name */
395                 if (!getword_str(dnbuf, sizeof dnbuf, &startp, endp)) {
396                     fprintf(stderr, "failed to read owner name\n");
397                     exit (1);
398                 }
399                 r_dname = dnbuf;
400                 r_ttl = 0;
401                 r_type = -1;
402                 r_class = C_IN; /* default to IN */
403                 r_size = 0;
404
405                 (void) getword_str(buf2, sizeof buf2, &startp, endp);
406
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);
411                         exit (1);
412                     }
413                     (void) getword_str(buf2, sizeof buf2, &startp, endp);
414                 }
415
416                 if (buf2[0]) { /* possibly class */
417                     for (mp = class_strs; mp < class_strs+M_CLASS_CNT; mp++) {
418                         if (!strcasecmp(buf2, mp->token)) {
419                             r_class = mp->val;
420                             (void) getword_str(buf2, sizeof buf2, &startp, endp);
421                             break;
422                         }
423                     }
424                 }
425                 /*
426                  * type and rdata field may or may not be required depending
427                  * on the section and operation
428                  */
429                 switch (r_section) {
430                 case S_PREREQ:
431                     if (r_ttl) {
432                         fprintf(stderr, "nonzero ttl in prereq section: %ul\n",
433                                 r_ttl);
434                         r_ttl = 0;
435                     }
436                     switch (r_opcode) {
437                     case NXDOMAIN:
438                     case YXDOMAIN:
439                         if (buf2[0]) {
440                             fprintf (stderr, "invalid field: %s, ignored\n",
441                                      buf2);
442                             exit (1);
443                         }
444                         break;
445                     case NXRRSET:
446                     case YXRRSET:
447                         if (buf2[0])
448                             for (mp = type_strs; mp < type_strs+M_TYPE_CNT; mp++)
449                                 if (!strcasecmp(buf2, mp->token)) {
450                                     r_type = mp->val;
451                                     break;
452                                 }
453                         if (r_type == -1) {
454                             fprintf (stderr, "invalid type for RRset: %s\n",
455                                      buf2);
456                             exit (1);
457                         }
458                         if (r_opcode == NXRRSET)
459                             break;
460                         /*
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
465                          * depending on type
466                          */
467                         cp = startp;
468                         while (cp <= endp && isspace(*cp))
469                             cp++;
470                         r_size = endp - cp + 1;
471                         break;
472                     default:
473                         fprintf (stderr,
474                                  "unknown operation in prereq section\"%s\"\n",
475                                  opcode);
476                         exit (1);
477                     }
478                     break;
479                 case S_UPDATE:
480                     switch (r_opcode) {
481                     case DELETE:
482                         r_ttl = 0;
483                         r_type = T_ANY;
484                         /* read type, if specified */
485                         if (buf2[0])
486                             for (mp = type_strs; mp < type_strs+M_TYPE_CNT; mp++)
487                                 if (!strcasecmp(buf2, mp->token)) {
488                                     r_type = mp->val;
489                                     svstartp = startp;
490                                     (void) getword_str(buf2, sizeof buf2,
491                                                        &startp, endp);
492                                     if (buf2[0]) /* unget preference */
493                                         startp = svstartp;
494                                     break;
495                                 }
496                         /* read rdata portion, if specified */
497                         cp = startp;
498                         while (cp <= endp && isspace(*cp))
499                             cp++;
500                         r_size = endp - cp + 1;
501                         break;
502                     case ADD:
503                         if (r_ttl == 0) {
504                             fprintf (stderr,
505                 "ttl must be specified for record to be added: %s\n", buf);
506                             exit (1);
507                         }
508                         /* read type */
509                         if (buf2[0])
510                             for (mp = type_strs; mp < type_strs+M_TYPE_CNT; mp++)
511                                 if (!strcasecmp(buf2, mp->token)) {
512                                     r_type = mp->val;
513                                     break;
514                                 }
515                         if (r_type == -1) {
516                             fprintf(stderr,
517                 "invalid type for record to be added: %s\n", buf2);
518                             exit (1);
519                         }
520                         /* read rdata portion */
521                         cp = startp;
522                         while (cp < endp && isspace(*cp))
523                             cp++;
524                         r_size = endp - cp + 1;
525                         if (r_size <= 0) {
526                             fprintf(stderr,
527                 "nonempty rdata field needed to add the record at line %d\n",
528                                     lineno);
529                             exit (1);
530                         }
531                         break;
532                     default:
533                         fprintf(stderr,
534                 "unknown operation in update section \"%s\"\n", opcode);
535                         exit (1);
536                     }
537                     break;
538                 default:
539                     fprintf(stderr,
540                             "unknown section identifier \"%s\"\n", section);
541                     exit (1);
542                 }
543
544                 if ( !(rrecp = res_mkupdrec(r_section, r_dname, r_class,
545                                             r_type, r_ttl)) ||
546                      (r_size > 0 && !(rrecp->r_data = (u_char *)malloc(r_size))) ) {
547                         if (rrecp)
548                                 res_freeupdrec(rrecp);
549                         fprintf(stderr, "saverrec error\n");
550                         exit (1);
551                 }
552         if (stringtobin) {
553              switch(r_opcode)  {
554              case T_HINFO:
555                   if (!getcharstring(buf,(char *)data,2,2,lineno))
556                        exit(1);
557                   cp = data;
558                   break;
559              case T_ISDN:
560                   if (!getcharstring(buf,(char *)data,1,2,lineno))
561                        exit(1);
562                   cp = data;
563                   break;
564              case T_TXT:
565                   if (!getcharstring(buf,(char *)data,1,0,lineno))
566                        exit(1);
567                   cp = data;
568                   break;
569              case T_X25:
570                   if (!getcharstring(buf,(char *)data,1,1,lineno))
571                        exit(1);
572                   cp = data;
573                   break;
574              default:
575                   break;
576              }
577         }
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);
584                 if (vc)
585                     res.options |= RES_USEVC | RES_STAYOPEN;
586                 if (debug)
587                     res.options |= RES_DEBUG;
588                 if (!EMPTY(listuprec)) {
589                         n = res_nupdate(&res, HEAD(listuprec),
590                                         keyfile != NULL ? &key : NULL);
591                         if (n < 0)
592                                 fprintf(stderr, "failed update packet\n");
593                         while (!EMPTY(listuprec)) {
594                                 ns_updrec *tmprrecp = HEAD(listuprec);
595
596                                 UNLINK(listuprec, tmprrecp, r_link);
597                                 if (tmprrecp->r_size != 0)
598                                         free((char *)tmprrecp->r_data);
599                                 res_freeupdrec(tmprrecp);
600                         }
601                 }
602             }
603         } /* for */
604         return (0);
605 }
606
607 static void
608 usage() {
609         fprintf(stderr, "Usage: %s [ -k keydir:keyname ] [-d] [-v] [file]\n",
610                 progname);
611         exit(1);
612 }
613
614 /*
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.
618  */
619 static int
620 getword_str(char *buf, int size, char **startpp, char *endp) {
621         char *cp;
622         int c;
623  
624         for (cp = buf; *startpp <= endp; ) {
625                 c = **startpp;
626                 if (isspace(c) || c == '\0') {
627                         if (cp != buf) /* trailing whitespace */
628                                 break;
629                         else { /* leading whitespace */
630                                 (*startpp)++;
631                                 continue;
632                         }
633                 }
634                 (*startpp)++;
635                 if (cp >= buf+size-1)
636                         break;
637                 *cp++ = (u_char)c;
638         }
639         *cp = '\0';
640         return (cp != buf);
641 }
642
643 #define MAXCHARSTRING 255
644
645 static int
646 getcharstring(char *buf, char *data,
647          int minfields, int maxfields, int lineno)
648 {
649    int nfield = 0, n = 0, i;
650
651    do {
652         nfield++;
653         i = 0;
654         if (*buf == '"') {
655              buf++;
656              while(buf[i] && buf[i] != '"')
657                   i++;
658         } else {
659              while(isspace(*buf))
660                   i++;
661         }
662         if (i > MAXCHARSTRING) {
663              fprintf(stderr,
664                "%d: RDATA field %d too long",
665                lineno, nfield);
666              return(0);
667         }
668         if (n + i + 1 > MAXDATA) {
669              fprintf(stderr,
670                "%d: total RDATA too long", lineno);
671              return(0);
672         }
673         data[n]=i;
674         memmove(data + 1 + n, buf, i);
675         buf += i + 1;
676         n += i + 1;
677         while(*buf && isspace(*buf))
678              buf++;
679    } while (nfield < maxfields && *buf);
680
681    if (nfield < minfields) {
682         fprintf(stderr,
683              "%d: expected %d RDATA fields, only saw %d",
684              lineno, minfields, nfield);
685         return (0);
686    }
687
688    return (n);
689 }