zonefile

package module
v0.0.0-...-efb3585 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2024 License: LGPL-3.0 Imports: 5 Imported by: 0

README

go-zonefile

Go package to edit DNS/Bind zone files (preserving formatting & comments)

go-zonefile is not finished: interfaces, documentation and convenience functions are missing. All the lexing and parsing is finished, though.

Bundled as an example (and actually the original reason why I wrote this package) is the inc-zonefile-serial utility, which increments the serial in a zonefile:

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"

	"git.sr.ht/~whynothugo/go-zonefile"
)

// Increments the serial of a zonefile
func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage:", os.Args[0], "<path to zonefile>")
		os.Exit(1)
	}

	// Load zonefile
	data, ioerr := ioutil.ReadFile(os.Args[1])
	if ioerr != nil {
		fmt.Println(os.Args[1], ioerr)
		os.Exit(2)
	}

	zf, perr := zonefile.Load(data)
	if perr != nil {
		fmt.Println(os.Args[1], perr.LineNo(), perr)
		os.Exit(3)
	}

	// Find SOA entry
	ok := false
	for _, e := range zf.Entries() {
		if !bytes.Equal(e.Type(), []byte("SOA")) {
			continue
		}
		vs := e.Values()
		if len(vs) != 7 {
			fmt.Println("Wrong number of parameters to SOA line")
			os.Exit(4)
		}
		serial, err := strconv.Atoi(string(vs[2]))
		if err != nil {
			fmt.Println("Could not parse serial:", err)
			os.Exit(5)
		}
		e.SetValue(2, []byte(strconv.Itoa(serial+1)))
		ok = true
		break
	}
	if !ok {
		fmt.Println("Could not find SOA entry")
		os.Exit(6)
	}

	fh, err := os.OpenFile(os.Args[1], os.O_WRONLY, 0)
	if err != nil {
		fmt.Println(os.Args[1], err)
		os.Exit(7)
	}

	_, err = fh.Write(zf.Save())
	if err != nil {
		fmt.Println(os.Args[1], err)
		os.Exit(8)
	}
}

Documentation

Overview

Parse DNS masterfiles a.k.a. zonefiles. See the Load function.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Load

func Load(data []byte) (r *Zonefile, e ParsingError)

Parse bytestring containing a zonefile

Example
zf, err := zonefile.Load([]byte(
	"@	IN	SOA	NS1.NAMESERVER.NET.	HOSTMASTER.MYDOMAIN.COM.	(\n" +
		"            1406291485	 ;serial\n" +
		"            3600	 ;refresh\n" +
		"            600	 ;retry\n" +
		"            604800	 ;expire\n" +
		"            86400	 ;minimum ttl\n" +
		")\n" +
		"\n" +
		"@	NS	NS1.NAMESERVER.NET.\n" +
		"@	NS	NS2.NAMESERVER.NET.\n"))
if err != nil {
	fmt.Println("Parsing error", err, "on line", err.LineNo())
	return
}
fmt.Println(len(zf.Entries()))
Output:

3

func ParseEntry

func ParseEntry(data []byte) (e Entry, err ParsingError)

Create a new entry from a bytestring

Example
entry, err := zonefile.ParseEntry([]byte(" IN MX 100 alpha.example.com."))
if err != nil {
	fmt.Println("Parsing error", err, "on line", err.LineNo())
	return
}
fmt.Println(entry)
Output:

<Entry dom="" ttl="" cls="IN" typ="MX" ["100" "alpha.example.com."]>

Types

type Entry

type Entry struct {
	// contains filtered or unexported fields
}

Represents an entry in the zonefile

func (Entry) Class

func (e Entry) Class() []byte

The class specified for the entry.

Example
entry, _ := zonefile.ParseEntry([]byte("irc A 1.2.3.4"))
fmt.Printf("%q\n", entry.Class())
entry, _ = zonefile.ParseEntry([]byte("irc IN A 4.3.2.1"))
fmt.Printf("%q\n", entry.Class())
Output:

""
"IN"

func (Entry) Command

func (e Entry) Command() []byte

For a control entry, returns its command (e.g. $TTL, $ORIGIN, ...)

Example
entry, _ := zonefile.ParseEntry([]byte("$TTL 123"))
fmt.Printf("%q\n", entry.Command())
Output:

"$TTL"

func (Entry) Domain

func (e Entry) Domain() []byte

The domain specified for the entry.

Example
entry, _ := zonefile.ParseEntry([]byte("irc IN A 1.2.3.4"))
fmt.Printf("%q\n", entry.Domain())
entry, _ = zonefile.ParseEntry([]byte(" IN A 4.3.2.1"))
fmt.Printf("%q\n", entry.Domain())
Output:

"irc"
""

func (*Entry) RemoveTTL

func (e *Entry) RemoveTTL() error

Remove the TTL from the entry, if there is any

Example
entry, _ := zonefile.ParseEntry([]byte("irc 12 IN A 1.2.3.4"))
fmt.Println(entry)
entry.RemoveTTL()
fmt.Println(entry)
Output:

<Entry dom="irc" ttl="12" cls="IN" typ="A" ["1.2.3.4"]>
<Entry dom="irc" ttl="" cls="IN" typ="A" ["1.2.3.4"]>

func (*Entry) SetClass

func (e *Entry) SetClass(v []byte) error

Change the class in the entry

Example
entry, _ := zonefile.ParseEntry([]byte("irc IN A 1.2.3.4"))
fmt.Println(entry)
entry.SetClass([]byte(""))
fmt.Println(entry)
entry.SetClass([]byte("IN"))
fmt.Println(entry)
Output:

<Entry dom="irc" ttl="" cls="IN" typ="A" ["1.2.3.4"]>
<Entry dom="irc" ttl="" cls="" typ="A" ["1.2.3.4"]>
<Entry dom="irc" ttl="" cls="IN" typ="A" ["1.2.3.4"]>

func (*Entry) SetDomain

func (e *Entry) SetDomain(v []byte) error

Changes the domain in the entry

Example
entry, _ := zonefile.ParseEntry([]byte("irc IN A 1.2.3.4"))
fmt.Println(entry)
entry.SetDomain([]byte(""))
fmt.Println(entry)
entry.SetDomain([]byte("chat"))
fmt.Println(entry)
Output:

<Entry dom="irc" ttl="" cls="IN" typ="A" ["1.2.3.4"]>
<Entry dom="" ttl="" cls="IN" typ="A" ["1.2.3.4"]>
<Entry dom="chat" ttl="" cls="IN" typ="A" ["1.2.3.4"]>

func (*Entry) SetTTL

func (e *Entry) SetTTL(v int) error

Change the class in the entry

Example
entry, _ := zonefile.ParseEntry([]byte("irc 12 IN A 1.2.3.4"))
fmt.Println(entry)
entry.SetTTL(14)
fmt.Println(entry)
Output:

<Entry dom="irc" ttl="12" cls="IN" typ="A" ["1.2.3.4"]>
<Entry dom="irc" ttl="14" cls="IN" typ="A" ["1.2.3.4"]>

func (*Entry) SetValue

func (e *Entry) SetValue(i int, v []byte) error

Set the the ith value of the entry

Example
entry, _ := zonefile.ParseEntry([]byte("irc IN A 1.2.3.4"))
fmt.Println(entry)
entry.SetValue(0, []byte("4.3.2.1"))
fmt.Println(entry)
Output:

<Entry dom="irc" ttl="" cls="IN" typ="A" ["1.2.3.4"]>
<Entry dom="irc" ttl="" cls="IN" typ="A" ["4.3.2.1"]>

func (Entry) String

func (e Entry) String() string

func (Entry) TTL

func (e Entry) TTL() *int

The TTL specified for the entry

Example
entry, _ := zonefile.ParseEntry([]byte("irc A 1.2.3.4"))
fmt.Printf("%v\n", entry.TTL() == nil)
entry, _ = zonefile.ParseEntry([]byte("irc 12 A 1.2.3.4"))
fmt.Printf("%v\n", *entry.TTL())
Output:

true
12

func (Entry) Type

func (e Entry) Type() []byte

The type specified for the entry.

Example
entry, _ := zonefile.ParseEntry([]byte("irc A 1.2.3.4"))
fmt.Printf("%q\n", entry.Type())
entry, _ = zonefile.ParseEntry([]byte("irc AAAA ::1"))
fmt.Printf("%q\n", entry.Type())
Output:

"A"
"AAAA"

func (Entry) Values

func (e Entry) Values() (ret [][]byte)

The values specified for the entry

Example
entry, _ := zonefile.ParseEntry([]byte(`
@	IN	SOA	NS1.NAMESERVER.NET.	HOSTMASTER.MYDOMAIN.COM.	(
			1406291485	 ;serial
			3600	 ;refresh
			600	 ;retry
			604800	 ;expire
			86400	 ;minimum ttl
)`))
fmt.Printf("%q\n", entry.Values())
Output:

["NS1.NAMESERVER.NET." "HOSTMASTER.MYDOMAIN.COM." "1406291485" "3600" "600" "604800" "86400"]

type ParsingError

type ParsingError interface {
	error
	LineNo() int
	ColNo() int
}

type Zonefile

type Zonefile struct {
	// contains filtered or unexported fields
}

Represents a DNS masterfile a.k.a. a zonefile

func New

func New() (z *Zonefile)

Create a new empty zonefile

Example
z := zonefile.New()
z.AddA("", "3.2.3.2")
z.AddA("www", "1.2.3.4")
z.AddA("irc", "2.2.2.2").SetTTL(12)
fmt.Println(z)
Output:

<Zonefile [<Entry dom="" ttl="" cls="" typ="" ["3.2.3.2"]> <Entry dom="www" ttl="" cls="" typ="" ["1.2.3.4"]> <Entry dom="irc" ttl="12" cls="" typ="" ["2.2.2.2"]>]>

func (*Zonefile) AddA

func (z *Zonefile) AddA(domain string, val string) *Entry

Add an A entry to the zonefile

Example
z := zonefile.New()
z.AddA("", "3.2.3.2")
z.AddA("www", "1.2.3.4")
z.AddA("irc", "2.2.2.2").SetTTL(12)
fmt.Println(z)
Output:

<Zonefile [<Entry dom="" ttl="" cls="" typ="" ["3.2.3.2"]> <Entry dom="www" ttl="" cls="" typ="" ["1.2.3.4"]> <Entry dom="irc" ttl="12" cls="" typ="" ["2.2.2.2"]>]>

func (*Zonefile) AddEntry

func (z *Zonefile) AddEntry(e Entry) *Entry

Add an entry to the zonefile

Example
z := zonefile.New()
entry, _ := zonefile.ParseEntry([]byte("irc IN A 1.2.3.4"))
z.AddEntry(entry)
fmt.Println(z)
Output:

<Zonefile [<Entry dom="irc" ttl="" cls="IN" typ="A" ["1.2.3.4"]>]>

func (*Zonefile) Entries

func (z *Zonefile) Entries() (r []Entry)

List entries in the zonefile

Example
zf, err := zonefile.Load([]byte(`
$TTL 3600
@	IN	SOA	NS1.NAMESERVER.NET.	HOSTMASTER.MYDOMAIN.COM.	(
			1406291485	 ;serial
			3600	 ;refresh
			600	 ;retry
			604800	 ;expire
			86400	 ;minimum ttl
)

	A	1.1.1.1
@	A	127.0.0.1
www	A	127.0.0.1
mail	A	127.0.0.1
			A 1.2.3.4
tst 300 IN A 101.228.10.127;this is a comment`))
if err != nil {
	fmt.Println("Error parsing zonefile:", err)
	return
}
for _, e := range zf.Entries() {
	fmt.Println(e)
}
Output:

<Entry cmd="$TTL" ["3600"]>
<Entry dom="@" ttl="" cls="IN" typ="SOA" ["NS1.NAMESERVER.NET." "HOSTMASTER.MYDOMAIN.COM." "1406291485" "3600" "600" "604800" "86400"]>
<Entry dom="" ttl="" cls="" typ="A" ["1.1.1.1"]>
<Entry dom="@" ttl="" cls="" typ="A" ["127.0.0.1"]>
<Entry dom="www" ttl="" cls="" typ="A" ["127.0.0.1"]>
<Entry dom="mail" ttl="" cls="" typ="A" ["127.0.0.1"]>
<Entry dom="" ttl="" cls="" typ="A" ["1.2.3.4"]>
<Entry dom="tst" ttl="300" cls="IN" typ="A" ["101.228.10.127"]>

func (*Zonefile) Save

func (z *Zonefile) Save() []byte

Write the zonefile to a bytearray

Example
z := zonefile.New()
entry, _ := zonefile.ParseEntry([]byte("irc IN A 1.2.3.4"))
z.AddEntry(entry)
entry, _ = zonefile.ParseEntry([]byte("www IN A 2.1.4.3"))
z.AddEntry(entry)
fmt.Println(string(z.Save()))
Output:

irc IN A 1.2.3.4
www IN A 2.1.4.3

func (Zonefile) String

func (z Zonefile) String() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL