github.com/pmoroney/dnscontrol@v0.2.4-0.20171024134423-fad98f73f44a/cmd/convertzone/main.go (about) 1 package main 2 3 /* 4 convertzone: Read BIND-style zonefile and output. 5 6 convertzone [-mode=MODE] zonename [filename] 7 8 -mode=tsv TAB-separated values (default) 9 -mode=dsl DNSControl DSL 10 -mode=pretty Sort and pretty-print records. 11 12 zonename The FQDN of the zone name. 13 filename File to read (default: stdin) 14 */ 15 16 import ( 17 "bufio" 18 "flag" 19 "fmt" 20 "io" 21 "log" 22 "os" 23 "strconv" 24 "strings" 25 26 "github.com/StackExchange/dnscontrol/providers/bind" 27 "github.com/miekg/dns" 28 "github.com/miekg/dns/dnsutil" 29 "github.com/pkg/errors" 30 ) 31 32 var flagMode = flag.String("mode", "tsv", "tsv|dsl|pretty") 33 var flagDefaultTTL = flag.Uint("ttl", 300, "Default TTL") 34 var flagRegText = flag.String("registrar", "REG_FILL_IN", "registrar text") 35 var flagProviderText = flag.String("provider", "DNS_FILL_IN", "provider text") 36 37 // parseargs parses the non-flag arguments. 38 func parseargs(args []string) (zonename string, filename string, r io.Reader, err error) { 39 // 1 args: first arg is the zonename. Read stdin. 40 // 2 args: first arg is the zonename. 2nd is the filename. 41 // Anything else returns an error. 42 43 if len(args) < 1 { 44 return "", "", nil, fmt.Errorf("no command line parameters. Zone name required") 45 } 46 47 zonename = args[0] 48 49 if len(args) == 1 { 50 filename = "stdin" 51 r = bufio.NewReader(os.Stdin) 52 } else if len(args) == 2 { 53 filename = flag.Arg(1) 54 r, err = os.Open(filename) 55 if err != nil { 56 return "", "", nil, errors.Wrapf(err, "Could not open file: %s", filename) 57 } 58 } else { 59 return "", "", nil, fmt.Errorf("too many command line parameters") 60 } 61 62 return zonename, filename, r, nil 63 } 64 65 // pretty outputs the zonefile using the prettyprinter. 66 func pretty(zonename string, filename string, r io.Reader, defaultTTL uint32) { 67 var l []dns.RR 68 for x := range dns.ParseZone(r, zonename, filename) { 69 if x.Error == nil { 70 l = append(l, x.RR) 71 } 72 } 73 bind.WriteZoneFile(os.Stdout, l, zonename) 74 } 75 76 // rrFormat outputs the zonefile in either DSL or TSV format. 77 func rrFormat(zonename string, filename string, r io.Reader, defaultTTL uint32, dsl bool) { 78 zonenamedot := zonename + "." 79 80 for x := range dns.ParseZone(r, zonename, filename) { 81 if x.Error != nil { 82 continue 83 } 84 85 // Skip comments. Parse the formatted version. 86 line := x.String() 87 if line[0] == ';' { 88 continue 89 } 90 items := strings.SplitN(line, "\t", 5) 91 if len(items) < 5 { 92 log.Fatalf("Too few items in: %v", line) 93 } 94 95 target := items[4] 96 97 hdr := x.Header() 98 nameFqdn := hdr.Name 99 name := dnsutil.TrimDomainName(nameFqdn, zonenamedot) 100 ttl := strconv.FormatUint(uint64(hdr.Ttl), 10) 101 classStr := dns.ClassToString[hdr.Class] 102 typeStr := dns.TypeToString[hdr.Rrtype] 103 104 // MX records should split out the prio vs. target. 105 if hdr.Rrtype == dns.TypeMX { 106 target = strings.Replace(target, " ", "\t", 1) 107 } 108 109 // NS records at the apex should be NAMESERVER() records. 110 if hdr.Rrtype == dns.TypeNS && name == "@" { 111 typeStr = "NAMESERVER" 112 } 113 114 if !dsl { // TSV format: 115 fmt.Printf("%s\t%s\t%s\t%s\t%s\n", name, ttl, classStr, typeStr, target) 116 } else { // DSL format: 117 switch hdr.Rrtype { // #rtype_variations 118 case dns.TypeMX: 119 m := strings.SplitN(target, "\t", 2) 120 target = m[0] + ", '" + m[1] + "'" 121 case dns.TypeSOA: 122 continue 123 case dns.TypeTXT: 124 // Leave target as-is. 125 default: 126 target = "'" + target + "'" 127 } 128 if hdr.Ttl == defaultTTL { 129 ttl = "" 130 } else { 131 ttl = fmt.Sprintf(", TTL(%d)", hdr.Ttl) 132 } 133 fmt.Printf(",\n\t%s('%s', %s%s)", typeStr, name, target, ttl) 134 } 135 } 136 137 } 138 139 func main() { 140 flag.Parse() 141 zonename, filename, reader, err := parseargs(flag.Args()) 142 if err != nil { 143 flag.Usage() 144 } 145 146 defTTL := uint32(*flagDefaultTTL) 147 148 switch *flagMode { 149 case "pretty": 150 pretty(zonename, filename, reader, defTTL) 151 case "dsl": 152 fmt.Printf(`D("%s", %s, DnsProvider(%s)`, zonename, *flagRegText, *flagProviderText) 153 rrFormat(zonename, filename, reader, defTTL, true) 154 fmt.Println("\n)") 155 case "tsv": 156 rrFormat(zonename, filename, reader, defTTL, false) 157 default: 158 flag.Usage() 159 } 160 161 }