diff --git a/cmd/convertzone/README.md b/cmd/convertzone/README.md deleted file mode 100644 index 4f2e76bca..000000000 --- a/cmd/convertzone/README.md +++ /dev/null @@ -1,105 +0,0 @@ - -!!! NOTE: This command has been replaced by the "dnscontrol get-zones" -!!! subcommand. It can do everything convertzone does and more, with -!!! fewer bugs. This command will be removed from the distribution soon. - -# convertzone -- Converts a standard DNS zonefile into tsv, pretty, or DSL - -This is a crude hack we put together to read a couple common zonefile -formats and output them in a few different formats. Current input -formats are BIND zonefiles and OctoDNS "config" YAML files. Current -output formats as BIND zonefiles, tab separated records, or a draft -DNSControl dnsconfig.js file. For dnsconfig.js, it does about 90% -of the work, but should be manually verified. - -The primary purpose of this program is to convert BIND-style -zonefiles to DNSControl dnsconfig.js files. Nearly all DNS Service -providers include the ability to export records as a BIND-style zonefile. -This makes it easy to import DNS data from other systems into DNSControl. -Later OctoDNS input was added because we had the parser (as part of -the OctoDNS provider), so why not use it? - -## Building the software - -Build the software and install in your personal bin: - -```bash -cd cmd/convertzone -go build -cp convertzone ~/bin/. -``` - -## Usage Overview - -convertzone: Read and write DNS zone files. - -convertzone [-in=INPUT] [-out=OUTPUT] zonename [filename] - -Input format: --in=bind BIND-style zonefiles (DEFAULT) --in=octodns OctoDNS YAML "config" files. - -Output format: - --out=dsl DNSControl DSL language (dnsconfig.js) (DEFAULT) --out=tsv TAB-separated values --out=pretty pretty-printed (BIND-style zonefiles) - -zonename The FQDN of the zone name. -filename File to read (optional. Defaults to stdin) - -The DSL output format is useful for creating the first -draft of your dnsconfig.js when importing zones from -other services. - -The TSV format makes it easy to process a zonefile with -shell tools. - -The PRETTY format is just a nice way to clean up a zonefile. - -If no filename is specified, stdin is assumed. -Output is sent to stdout. - -The zonename is required as it can not be guessed automatically from the input. - -Example: - - convertzone stackoverflow.com zone.stackoverflow.com >new/draft.js - - -### -out=tsv: - -This is useful for `awk` and other systems that expect a very -uniform set of input. - -Example: Print all CNAMEs: - - convertzone -out=tsv foo.com " $5 }' - - -### -out=pretty: - -This is useful for cleaning up a zonefile. It sorts the records, -moving SOA and NS records to the top of the zone; all other records -are alphabetically sorted; if a label has mutiple records, they are -listed in a logical (not numeric) order, multiple A records are -listed sorted by IP address, MX records are sorted by priority, -etc. Use `-ttl` to set a default TTL. - -Example: Clean up a zone file: - - convertzone -out=pretty foo.com new/zone.foo.com - - -### -out=dsl: - -This is useful for generating your draft `dnsconfig.js` configuration. -The output can be appended to the `dnsconfig.js` file as a good first draft. - -Example: Generate statements for a dnsconfig.js file: - - convertzone -out=dsl foo.com first-draft.js - -Note: The conversion is not perfect. You'll need to manually clean -it up and insert it into `dnsconfig.js`. More instructions in the -DNSControl [migration doc]({site.github.url}}/migration). diff --git a/cmd/convertzone/main.go b/cmd/convertzone/main.go deleted file mode 100644 index 9904f1933..000000000 --- a/cmd/convertzone/main.go +++ /dev/null @@ -1,221 +0,0 @@ -package main - -/* -convertzone: Read and write DNS zone files. - - convertzone [-in=INPUT] [-out=OUTPUT] zonename [filename] - - Input format: - -in=bind BIND-style zonefiles (DEFAULT) - -in=octodns OctoDNS YAML "config" files. - - Output format: - - -out=dsl DNSControl DSL language (dnsconfig.js) (DEFAULT) - -out=tsv TAB-separated values - -out=pretty pretty-printed (BIND-style zonefiles) - - zonename The FQDN of the zone name. - filename File to read (optional. Defaults to stdin) - - The DSL output format is useful for creating the first - draft of your dnsconfig.js when importing zones from - other services. - - The TSV format makes it easy to process a zonefile with - shell tools. `awk -F"\t" $2 = "A" { print $3 }` - - The PRETTY format is just a nice way to clean up a zonefile. - - If no filename is specified, stdin is assumed. - Output is sent to stdout. - - The zonename is required as it can not be guessed automatically from the input. -*/ - -import ( - "bufio" - "flag" - "fmt" - "io" - "log" - "os" - "strconv" - "strings" - - "github.com/miekg/dns" - "github.com/miekg/dns/dnsutil" - - "github.com/StackExchange/dnscontrol/v3/pkg/prettyzone" - "github.com/StackExchange/dnscontrol/v3/providers/octodns/octoyaml" -) - -var flagInfmt = flag.String("in", "zone", "zone|octodns") -var flagOutfmt = flag.String("out", "dsl", "dsl|tsv|pretty") -var flagDefaultTTL = flag.Uint("ttl", 300, "Default TTL") -var flagRegText = flag.String("registrar", "REG_FILL_IN", "registrar text") -var flagProviderText = flag.String("provider", "DNS_FILL_IN", "provider text") - -func main() { - flag.Parse() - zonename, filename, reader, err := parseargs(flag.Args()) - if err != nil { - fmt.Printf("ERROR: %v\n\n", err) - fmt.Println("convertzone [-flags] ZONENAME FILENAME") - flag.Usage() - os.Exit(1) - } - - defTTL := uint32(*flagDefaultTTL) - - var recs []dns.RR - - // Read it in: - - switch *flagInfmt { - case "zone": - recs = readZone(zonename, reader, filename) - case "oct", "octo", "octodns": - recs = readOctodns(zonename, reader, filename) - } - - // Write it out: - - switch *flagOutfmt { - case "pretty": - prettyzone.WriteZoneFileRR(os.Stdout, recs, zonename) - case "dsl": - fmt.Printf(`D("%s", %s, DnsProvider(%s)`, zonename, *flagRegText, *flagProviderText) - rrFormat(zonename, filename, recs, defTTL, true) - fmt.Println("\n)") - case "tsv": - rrFormat(zonename, filename, recs, defTTL, false) - default: - fmt.Println("convertzone [-flags] ZONENAME FILENAME") - flag.Usage() - } - -} - -// parseargs parses the non-flag arguments. -func parseargs(args []string) (zonename string, filename string, r io.Reader, err error) { - // 1 args: first arg is the zonename. Read stdin. - // 2 args: first arg is the zonename. 2nd is the filename. - // Anything else returns an error. - - if len(args) < 2 { - return "", "", nil, fmt.Errorf("no command line parameters. Zone name required") - } - - zonename = args[0] - - if len(args) == 1 { - filename = "stdin" - r = bufio.NewReader(os.Stdin) - } else if len(args) == 2 { - filename = flag.Arg(1) - r, err = os.Open(filename) - if err != nil { - return "", "", nil, fmt.Errorf("could not open file: %s: %w", filename, err) - } - } else { - return "", "", nil, fmt.Errorf("too many command line parameters") - } - - return zonename, filename, r, nil -} - -func readZone(zonename string, r io.Reader, filename string) []dns.RR { - - zp := dns.NewZoneParser(r, zonename, filename) - - var parsed []dns.RR - for rr, ok := zp.Next(); ok; rr, ok = zp.Next() { - parsed = append(parsed, rr) - } - if err := zp.Err(); err != nil { - log.Fatalf("Error in zonefile: %v", err) - } - return parsed -} - -func readOctodns(zonename string, r io.Reader, filename string) []dns.RR { - var l []dns.RR - - foundRecords, err := octoyaml.ReadYaml(r, zonename) - if err != nil { - log.Println(fmt.Errorf("can not get corrections: %w", err)) - } - - for _, x := range foundRecords { - l = append(l, x.ToRR()) - } - return l -} - -// rrFormat outputs the zonefile in either DSL or TSV format. -func rrFormat(zonename string, filename string, recs []dns.RR, defaultTTL uint32, dsl bool) { - zonenamedot := zonename + "." - - for _, x := range recs { - - // Skip comments. Parse the formatted version. - line := x.String() - if line[0] == ';' { - continue - } - items := strings.SplitN(line, "\t", 5) - if len(items) < 5 { - log.Fatalf("Too few items in: %v", line) - } - - target := items[4] - - hdr := x.Header() - nameFqdn := hdr.Name - name := dnsutil.TrimDomainName(nameFqdn, zonenamedot) - ttl := strconv.FormatUint(uint64(hdr.Ttl), 10) - classStr := dns.ClassToString[hdr.Class] - typeStr := dns.TypeToString[hdr.Rrtype] - - // MX records should split out the prio vs. target. - if hdr.Rrtype == dns.TypeMX { - target = strings.Replace(target, " ", "\t", 1) - } - - var ttlop string - if hdr.Ttl == defaultTTL { - ttlop = "" - } else { - ttlop = fmt.Sprintf(", TTL(%d)", hdr.Ttl) - } - - // NS records at the apex should be NAMESERVER() records. - if hdr.Rrtype == dns.TypeNS && name == "@" { - fmt.Printf(",\n\tNAMESERVER('%s'%s)", target, ttlop) - continue - } - - if !dsl { // TSV format: - fmt.Printf("%s\t%s\t%s\t%s\t%s\n", name, ttl, classStr, typeStr, target) - } else { // DSL format: - switch hdr.Rrtype { // #rtype_variations - case dns.TypeMX: - m := strings.SplitN(target, "\t", 2) - target = m[0] + ", '" + m[1] + "'" - case dns.TypeSOA: - continue - case dns.TypeTXT: - if len(x.(*dns.TXT).Txt) == 1 { - target = `'` + x.(*dns.TXT).Txt[0] + `'` - } else { - target = `['` + strings.Join(x.(*dns.TXT).Txt, `', '`) + `']` - } - default: - target = "'" + target + "'" - } - fmt.Printf(",\n\t%s('%s', %s%s)", typeStr, name, target, ttlop) - } - } - -}