dnscontrol/cmd/convertzone/main.go
Craig Peterson 42ef9f4b9e moving a bunch of packages under pkg (#124)
* moving a bunch of packages under pkg

* fix gen

* fix tst

* oops

* fix test fo real

* parse mx/a
2017-05-25 14:25:39 -04:00

156 lines
3.8 KiB
Go

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)
}
// 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()
}
}