ccl/internal/pkg/container/nsupdate.go

266 lines
5.6 KiB
Go
Raw Normal View History

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())
}
2024-03-25 11:38:47 -05:00
_, _, 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(ip string, dn string, rrtype uint16) error {
rv, err := dns.ReverseAddr(ip)
if err != nil {
return err
}
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
}
}
// Determine SOA of 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
// Update the reverse record
ptr := dns.PTR{
Hdr: dns.RR_Header{
Name: rv,
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 7200,
},
Ptr: dn,
}
msg = new(dns.Msg)
msg.SetUpdate(soa)
msg.Ns = append(msg.Ns, &ptr)
if c.TSIGName != "" {
msg.SetTsig(c.TSIGName, dns.HmacSHA256, 300, time.Now().Unix())
}
_, _, err = cli.Exchange(msg, c.DnsServer)
2024-03-25 11:38:47 -05:00
return err
}
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 {
rr_parsed, err := dns.NewRR(rr)
if err != nil {
return err
}
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, rr_parsed)
if c.TSIGName != "" {
msg.SetTsig(c.TSIGName, dns.HmacSHA256, 300, time.Now().Unix())
}
2024-03-25 11:38:47 -05:00
_, _, err = cli.Exchange(msg, c.DnsServer)
return err
}
func (c *Container) NsUpdateCommands() cmd.Set {
// check that a server is configured
2023-12-17 12:49:22 -06:00
if c.DomainName == "" || c.DnsServer == "" {
return cmd.Set{ID: "NSUPDATE", Commands: []cmd.Command{
cmd.NewFunc("NSUPDATE_NOT_CONFIGURED", func() error {
return fmt.Errorf("nsupdate command requires `domain_name` and `dns_server` to be configured")
}),
}}
2023-12-17 12:49:22 -06:00
}
// 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{}
for i := range c.Networks {
n := &c.Networks[i]
ip6s := []net.IP{}
if n.IPv6Address != nil && !n.IPv6Address.IsUnspecified() {
ip6s = append(ip6s, n.IPv6Address)
}
if n.IPv6Addresses != nil {
ip6s = append(ip6s, n.IPv6Addresses...)
}
f_6 := func() error {
for _, a6 := range ip6s {
if err := c.doDnsReverse(a6.String(), dn, dns.TypeAAAA); err != nil {
return err
}
}
if err := c.killDnsForward(dn, dns.TypeAAAA); err != nil {
return err
}
for _, a6 := range ip6s {
aaaa := dns.AAAA{
Hdr: dns.RR_Header{
Name: dn,
Rrtype: dns.TypeAAAA,
Class: dns.ClassINET,
Ttl: 7200,
},
AAAA: a6,
}
if err := c.doDnsForward(aaaa.String()); err != nil {
return err
}
}
return nil
}
cmds = append(cmds, cmd.NewFunc("nsupate6", f_6))
ip4s := []net.IP{}
if n.IPv4Address != nil && !n.IPv4Address.IsUnspecified() {
ip4s = append(ip4s, n.IPv4Address)
}
if n.IPv4Addresses != nil {
ip4s = append(ip4s, n.IPv4Addresses...)
}
f_4 := func() error {
for _, a4 := range ip4s {
if err := c.doDnsReverse(a4.String(), dn, dns.TypeA); err != nil {
return err
}
}
if err := c.killDnsForward(dn, dns.TypeA); err != nil {
return err
}
for _, a4 := range ip4s {
a := dns.A{
Hdr: dns.RR_Header{
Name: dn,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 7200,
},
A: a4,
}
if err := c.doDnsForward(a.String()); err != nil {
return err
}
}
return nil
}
cmds = append(cmds, cmd.NewFunc("nsupate4", f_4))
}
return c.newCommandSet("NSUPDATE", cmds)
}