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  }