1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00

NEW: convertzone/ Read BIND zone files, output records as DSL, TSV, o… (#49)

* NEW: convertzone/ Read BIND zone files, output records as DSL, TSV, or pretty-print.

* Refactored to be more gostyle.

* Fix buffering issue.

* Delete the hacky awk script.

* Linting.
This commit is contained in:
Tom Limoncelli
2017-03-20 14:27:40 -04:00
committed by Craig Peterson
parent 4fef4a8550
commit 9817c284d7
12 changed files with 743 additions and 216 deletions

View File

@@ -0,0 +1,27 @@
# convertzone -- Converts a standard DNS zonefile into tsv, pretty, or DSL
convertzone converts an old-style DNS zone file into one of three formats:
* -mode=pretty Output the zone pretty-printed.
* -mode=tsv Output the zone recoreds as tab-separated values
* -mode=dsl Output the zone records as the DNSControl DSL language.
NOTE: mode=dsl is not perfect. The result is something you'll want
to fine-tune. However it is better than manually converting big
zones.
You must give the script both the zone name (i.e. "stackoverflow.com")
and the filename of the zonefile to read.
Output is sent to stdout.
Example:
"""
./convertzone stackoverflow.com zone.stackoverflow.com >new/stackoverflow.com
"""
Caveats:
* `$INCLUDE` may not be handled correctly if you are not in the right directory.

156
misc/convertzone/main.go Normal file
View File

@@ -0,0 +1,156 @@
package main
/*
convertzone: Read BIND-style zonefile and output.
convertzone [-mode=MODE] zonename [filename]
-mode=tsv TAB-separated values (default)
-mode=dsl DNSControl DSL
-mode=pretty Sort and pretty-print records.
zonename The FQDN of the zone name.
filename File to read (default: stdin)
*/
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"github.com/StackExchange/dnscontrol/providers/bind"
"github.com/miekg/dns"
"github.com/miekg/dns/dnsutil"
"github.com/pkg/errors"
)
var flagMode = flag.String("mode", "tsv", "tsv|dsl|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")
// 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) < 1 {
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, errors.Wrapf(err, "Could not open file: %s", filename)
}
} else {
return "", "", nil, fmt.Errorf("too many command line parameters")
}
return zonename, filename, r, nil
}
// pretty outputs the zonefile using the prettyprinter.
func pretty(zonename string, filename string, r io.Reader, defaultTTL uint32) {
var l []dns.RR
for x := range dns.ParseZone(r, zonename, filename) {
if x.Error == nil {
l = append(l, x.RR)
}
}
bind.WriteZoneFile(os.Stdout, l, zonename, defaultTTL)
}
// rrFormat outputs the zonefile in either DSL or TSV format.
func rrFormat(zonename string, filename string, r io.Reader, defaultTTL uint32, dsl bool) {
zonenamedot := zonename + "."
for x := range dns.ParseZone(r, zonename, filename) {
if x.Error != nil {
continue
}
// 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)
}
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 {
case dns.TypeMX:
m := strings.SplitN(target, "\t", 2)
target = m[0] + ", '" + m[1] + "'"
case dns.TypeSOA:
continue
case dns.TypeTXT:
// Leave target as-is.
default:
target = "'" + target + "'"
}
if hdr.Ttl == defaultTTL {
ttl = ""
} else {
ttl = fmt.Sprintf(", TTL(%d)", hdr.Ttl)
}
fmt.Printf(",\n\t%s('%s', %s%s)", typeStr, name, target, ttl)
}
}
}
func main() {
flag.Parse()
zonename, filename, reader, err := parseargs(flag.Args())
if err != nil {
flag.Usage()
}
defTTL := uint32(*flagDefaultTTL)
switch *flagMode {
case "pretty":
pretty(zonename, filename, reader, defTTL)
case "dsl":
fmt.Printf(`D("%s", %s, DnsProvider(%s)`, zonename, *flagRegText, *flagProviderText)
rrFormat(zonename, filename, reader, defTTL, true)
fmt.Println("\n)")
case "tsv":
rrFormat(zonename, filename, reader, defTTL, false)
default:
flag.Usage()
}
}