296 lines
6.2 KiB
Go
296 lines
6.2 KiB
Go
package container
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
cmd "gitea.elkins.co/Networking/ccl/internal/pkg/command"
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
func (c *Container) makeDnsClient() *dns.Client {
|
|
cli := new(dns.Client)
|
|
if c.TSIGName != "" {
|
|
cli.TsigSecret = map[string]string{c.TSIGName: c.TSIGKey}
|
|
}
|
|
return cli
|
|
}
|
|
|
|
func (c *Container) killDnsReverse(ip string) error {
|
|
rv, err := dns.ReverseAddr(ip)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cli := c.makeDnsClient()
|
|
|
|
// Determine SOA of old reverse zone
|
|
msg := new(dns.Msg)
|
|
msg.SetQuestion(rv, dns.TypeSOA)
|
|
resp, _, err := cli.Exchange(msg, c.DnsServer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
soa := resp.Ns[0].Header().Name
|
|
|
|
// Remove the old PTR
|
|
ptr := dns.ANY{
|
|
Hdr: dns.RR_Header{
|
|
Name: rv,
|
|
Rrtype: dns.TypePTR,
|
|
},
|
|
}
|
|
msg = new(dns.Msg)
|
|
msg.SetUpdate(soa)
|
|
msg.RemoveName([]dns.RR{&ptr})
|
|
if c.TSIGName != "" {
|
|
msg.SetTsig(c.TSIGName, dns.HmacSHA256, 300, time.Now().Unix())
|
|
}
|
|
_, _, err = cli.Exchange(msg, c.DnsServer)
|
|
return err
|
|
}
|
|
|
|
// This is the same code for ipv4 or ipv6 so factor it out
|
|
func (c *Container) doDnsReverse(ips []net.IP, dn string, rrtype uint16) error {
|
|
rips := make([]string, len(ips))
|
|
for i := range ips {
|
|
rv, err := dns.ReverseAddr(ips[i].String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rips[i] = rv
|
|
}
|
|
|
|
cli := c.makeDnsClient()
|
|
|
|
// Delete any existing PTR
|
|
// Strategy:
|
|
// 1. find existing RR's of our given type
|
|
// 2. for each one, kill any reverse pointers to that address
|
|
msg := new(dns.Msg)
|
|
msg.SetQuestion(dn, rrtype)
|
|
resp, _, err := cli.Exchange(msg, c.DnsServer)
|
|
if err != nil {
|
|
return fmt.Errorf("error looking up existing RRs for %s type %d: %s", dn, rrtype, err)
|
|
}
|
|
for i := range resp.Answer {
|
|
var ip string
|
|
if rrtype == dns.TypeA {
|
|
x := (resp.Answer[i]).(*dns.A)
|
|
ip = x.A.String()
|
|
} else {
|
|
x := (resp.Answer[i]).(*dns.AAAA)
|
|
ip = x.AAAA.String()
|
|
}
|
|
if err := c.killDnsReverse(ip); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// map key is SOA
|
|
messages := map[string]*dns.Msg{}
|
|
|
|
for i := range rips {
|
|
// Determine SOA of reverse zone
|
|
msg = new(dns.Msg)
|
|
msg.SetQuestion(rips[i], dns.TypeSOA)
|
|
resp, _, err = cli.Exchange(msg, c.DnsServer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
soa := resp.Ns[0].Header().Name
|
|
|
|
if messages[soa] == nil {
|
|
msg := new(dns.Msg)
|
|
msg.SetUpdate(soa)
|
|
messages[soa] = msg
|
|
}
|
|
|
|
// Update the reverse record
|
|
ptr := dns.PTR{
|
|
Hdr: dns.RR_Header{
|
|
Name: rips[i],
|
|
Rrtype: dns.TypePTR,
|
|
Class: dns.ClassINET,
|
|
Ttl: 7200,
|
|
},
|
|
Ptr: dn,
|
|
}
|
|
|
|
messages[soa].Ns = append(messages[soa].Ns, &ptr)
|
|
}
|
|
|
|
// execute the messages
|
|
errs := make([]error, len(messages))
|
|
j := 0
|
|
for _, msg := range messages {
|
|
if c.TSIGName != "" {
|
|
msg.SetTsig(c.TSIGName, dns.HmacSHA256, 300, time.Now().Unix())
|
|
}
|
|
_, _, errs[j] = cli.Exchange(msg, c.DnsServer)
|
|
j += 1
|
|
}
|
|
|
|
// check for errors
|
|
for _, err := range errs {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Container) killDnsForward(name string, rrtype uint16) error {
|
|
cli := c.makeDnsClient()
|
|
|
|
msg := new(dns.Msg)
|
|
msg.SetQuestion(name, rrtype)
|
|
resp, _, err := cli.Exchange(msg, c.DnsServer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dn := dns.Fqdn(c.DomainName)
|
|
if !strings.HasSuffix(dn, ".") {
|
|
dn = dn + "."
|
|
}
|
|
|
|
if len(resp.Answer) > 0 {
|
|
msg := new(dns.Msg)
|
|
msg.SetUpdate(dn)
|
|
msg.RemoveRRset(resp.Answer)
|
|
if _, _, err := cli.Exchange(msg, c.DnsServer); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Container) doDnsForward(rr []string) error {
|
|
rrs := make([]dns.RR, len(rr))
|
|
|
|
for i := range rr {
|
|
rr_parsed, err := dns.NewRR(rr[i])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rrs[i] = rr_parsed
|
|
}
|
|
|
|
cli := c.makeDnsClient()
|
|
dn := dns.Fqdn(c.DomainName)
|
|
if !strings.HasSuffix(dn, ".") {
|
|
dn = dn + "."
|
|
}
|
|
|
|
// Update the forward record
|
|
msg := new(dns.Msg)
|
|
msg.SetUpdate(dn)
|
|
msg.Ns = append(msg.Ns, rrs...)
|
|
if c.TSIGName != "" {
|
|
msg.SetTsig(c.TSIGName, dns.HmacSHA256, 300, time.Now().Unix())
|
|
}
|
|
_, _, err := cli.Exchange(msg, c.DnsServer)
|
|
return err
|
|
}
|
|
|
|
func (c *Container) NsUpdateCommands() cmd.Set {
|
|
if len(c.Networks) == 0 {
|
|
return c.newCommandSet("NSUPDATE", []cmd.Command{
|
|
cmd.NewNop(),
|
|
})
|
|
}
|
|
|
|
// check that a server is configured
|
|
if c.DomainName == "" || c.DnsServer == "" {
|
|
return c.newCommandSet("NSUPDATE", []cmd.Command{
|
|
cmd.NewFunc("NSUPDATE_NOT_CONFIGURED", func() error {
|
|
return fmt.Errorf("nsupdate command requires `domain_name` and `dns_server` to be configured")
|
|
}),
|
|
})
|
|
}
|
|
|
|
// determine hostname
|
|
hostname := c.Hostname
|
|
if c.Hostname == "" {
|
|
hostname = c.Name
|
|
}
|
|
dn := dns.Fqdn(hostname + "." + c.DomainName)
|
|
if !strings.HasSuffix(dn, ".") {
|
|
dn = dn + "."
|
|
}
|
|
|
|
// prepare update commands
|
|
cmds := []cmd.Command{
|
|
cmd.NewFunc("kill_fwd_6", func() error {
|
|
return c.killDnsForward(dn, dns.TypeAAAA)
|
|
}),
|
|
cmd.NewFunc("kill_fwd_4", func() error {
|
|
return c.killDnsForward(dn, dns.TypeA)
|
|
}),
|
|
}
|
|
|
|
// gather all ip addresses to be mapped to this name
|
|
ip6s := []net.IP{}
|
|
ip4s := []net.IP{}
|
|
for i := range c.Networks {
|
|
n := &c.Networks[i]
|
|
if n.IPv6Address != nil && !n.IPv6Address.IsUnspecified() {
|
|
ip6s = append(ip6s, n.IPv6Address)
|
|
}
|
|
if n.IPv6Addresses != nil {
|
|
ip6s = append(ip6s, n.IPv6Addresses...)
|
|
}
|
|
if n.IPv4Address != nil && !n.IPv4Address.IsUnspecified() {
|
|
ip4s = append(ip4s, n.IPv4Address)
|
|
}
|
|
if n.IPv4Addresses != nil {
|
|
ip4s = append(ip4s, n.IPv4Addresses...)
|
|
}
|
|
}
|
|
|
|
// make reverse commands
|
|
rdns := func() error {
|
|
if err := c.doDnsReverse(ip6s, dn, dns.TypeAAAA); err != nil {
|
|
return err
|
|
}
|
|
return c.doDnsReverse(ip4s, dn, dns.TypeA)
|
|
}
|
|
cmds = append(cmds, cmd.NewFunc("reverse_dns", rdns))
|
|
|
|
// make foreard commands
|
|
rrs := []string{}
|
|
for _, i := range ip6s {
|
|
aaaa := dns.AAAA{
|
|
Hdr: dns.RR_Header{
|
|
Name: dn,
|
|
Rrtype: dns.TypeAAAA,
|
|
Class: dns.ClassINET,
|
|
Ttl: 7200,
|
|
},
|
|
AAAA: i,
|
|
}
|
|
rrs = append(rrs, aaaa.String())
|
|
}
|
|
for _, i := range ip4s {
|
|
a := dns.A{
|
|
Hdr: dns.RR_Header{
|
|
Name: dn,
|
|
Rrtype: dns.TypeA,
|
|
Class: dns.ClassINET,
|
|
Ttl: 7200,
|
|
},
|
|
A: i,
|
|
}
|
|
rrs = append(rrs, a.String())
|
|
}
|
|
fdns := func() error {
|
|
return c.doDnsForward(rrs)
|
|
}
|
|
cmds = append(cmds, cmd.NewFunc("forward_dns", fdns))
|
|
|
|
return c.newCommandSet("NSUPDATE", cmds)
|
|
}
|