Skip to content

Commit

Permalink
refactor: update dependencies and enhance logging with slog
Browse files Browse the repository at this point in the history
  • Loading branch information
grishy committed Jan 10, 2025
1 parent 40d2d4b commit 25aab71
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 43 deletions.
32 changes: 32 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/go
{
"name": "Go",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/go:1-1.23-bookworm",

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
"settings": {},
"extensions": ["streetsidesoftware.code-spell-checker"]
}
},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [9000],

// Use 'portsAttributes' to set default properties for specific forwarded ports.
// More info: https://containers.dev/implementors/json_reference/#port-attributes
"portsAttributes": {},

// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "go mod download"

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
26 changes: 18 additions & 8 deletions avahi/publisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package avahi

import (
"fmt"
"log/slog"

"github.com/godbus/dbus/v5"
"github.com/holoplot/go-avahi"
Expand All @@ -25,24 +26,27 @@ type Publisher struct {

// NewPublisher creates a new service for Publisher.
func NewPublisher() (*Publisher, error) {
slog.Debug("creating new publisher")

conn, err := dbus.SystemBus()
if err != nil {
return nil, fmt.Errorf("failed to connect to system bus: %v", err)
return nil, fmt.Errorf("failed to connect to system bus: %w", err)
}

server, err := avahi.ServerNew(conn)
if err != nil {
return nil, fmt.Errorf("failed to create Avahi server: %v", err)
return nil, fmt.Errorf("failed to create Avahi server: %w", err)
}

avahiFqdn, err := server.GetHostNameFqdn()
if err != nil {
return nil, fmt.Errorf("failed to get FQDN from Avahi: %v", err)
return nil, fmt.Errorf("failed to get FQDN from Avahi: %w", err)
}
slog.Debug("got FQDN from Avahi", "fqdn", avahiFqdn)

group, err := server.EntryGroupNew()
if err != nil {
return nil, fmt.Errorf("failed to create entry group: %v", err)
return nil, fmt.Errorf("failed to create entry group: %w", err)
}

fqdn := dns.Fqdn(avahiFqdn)
Expand All @@ -52,9 +56,10 @@ func NewPublisher() (*Publisher, error) {
rdataField := make([]byte, len(fqdn)+1)
_, err = dns.PackDomainName(fqdn, rdataField, 0, nil, false)
if err != nil {
return nil, fmt.Errorf("failed to pack FQDN into RDATA: %v", err)
return nil, fmt.Errorf("failed to pack FQDN into RDATA: %w", err)
}

slog.Debug("publisher created successfully", "fqdn", fqdn)
return &Publisher{
dbusConn: conn,
avahiServer: server,
Expand All @@ -71,13 +76,16 @@ func (p *Publisher) Fqdn() string {

// PublishCNAMES send via Avahi-daemon CNAME records with the provided TTL.
func (p *Publisher) PublishCNAMES(cnames []string, ttl uint32) error {
slog.Debug("publishing CNAMEs", "count", len(cnames), "ttl", ttl)

// Reset the entry group to remove all records.
// Because we can't update records without it after the `Commit`.
if err := p.avahiEntryGroup.Reset(); err != nil {
return fmt.Errorf("failed to reset entry group: %v", err)
return fmt.Errorf("failed to reset entry group: %w", err)
}

for _, cname := range cnames {
slog.Debug("adding CNAME record", "cname", cname)
err := p.avahiEntryGroup.AddRecord(
avahi.InterfaceUnspec,
avahi.ProtoUnspec,
Expand All @@ -89,18 +97,20 @@ func (p *Publisher) PublishCNAMES(cnames []string, ttl uint32) error {
p.rdataField,
)
if err != nil {
return fmt.Errorf("failed to add record to entry group: %v", err)
return fmt.Errorf("failed to add record to entry group: %w", err)
}
}

if err := p.avahiEntryGroup.Commit(); err != nil {
return fmt.Errorf("failed to commit entry group: %v", err)
return fmt.Errorf("failed to commit entry group: %w", err)
}

slog.Debug("successfully published CNAMEs")
return nil
}

// Close associated resources.
func (p *Publisher) Close() {
slog.Debug("closing publisher")
p.avahiServer.Close() // It also closes the DBus connection and free the entry group
}
18 changes: 9 additions & 9 deletions cmd/cname.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package cmd
import (
"context"
"fmt"
"log"
"log/slog"
"time"

"github.com/miekg/dns"
Expand All @@ -14,16 +14,16 @@ import (

// formatCname formats CNAMEs by ensuring they are fully qualified domain names (FQDNs).
func formatCname(hostnameFqdn string, cnames []string) []string {
log.Println("Formatting CNAMEs:")
slog.Info("formatting CNAMEs")

formattedCnames := make([]string, len(cnames))
for i, cname := range cnames {
if !dns.IsFqdn(cname) {
formattedCnames[i] = dns.Fqdn(cname + "." + hostnameFqdn)
log.Printf(" > '%s' (added FQDN)", formattedCnames[i])
slog.Info("formatted CNAME", "cname", formattedCnames[i], "note", "added FQDN")
} else {
formattedCnames[i] = cname
log.Printf(" > '%s'", cname)
slog.Info("formatted CNAME", "cname", cname)
}
}

Expand All @@ -32,7 +32,7 @@ func formatCname(hostnameFqdn string, cnames []string) []string {

// publishLoop handles the continuous publishing of CNAME records.
func publishing(ctx context.Context, publisher *avahi.Publisher, cnames []string, ttl, interval uint32) error {
log.Printf("Publishing every %ds and CNAME TTL %ds", interval, ttl)
slog.Info("publishing CNAMEs", "interval", interval, "ttl", ttl)

resendDuration := time.Duration(interval) * time.Second
ticker := time.NewTicker(resendDuration)
Expand All @@ -52,7 +52,7 @@ func publishing(ctx context.Context, publisher *avahi.Publisher, cnames []string
}
case <-ctx.Done():
fmt.Println() // Add new line after ^C
log.Println("Closing publisher")
slog.Info("closing publisher")
publisher.Close()
return nil
}
Expand All @@ -61,7 +61,7 @@ func publishing(ctx context.Context, publisher *avahi.Publisher, cnames []string

// runCname sets up and starts the CNAME publishing process.
func runCname(ctx context.Context, publisher *avahi.Publisher, cnames []string, fqdn string, ttl, interval uint32) error {
log.Printf("FQDN: %s", fqdn)
slog.Info("running CNAME publisher", "fqdn", fqdn)

formattedCname := formatCname(fqdn, cnames)
return publishing(ctx, publisher, formattedCname, ttl, interval)
Expand Down Expand Up @@ -101,14 +101,14 @@ func Cname(ctx context.Context) *cli.Command {
return fmt.Errorf("at least one CNAME should be provided")
}

log.Println("Creating publisher")
slog.Info("creating publisher")
publisher, err := avahi.NewPublisher()
if err != nil {
return fmt.Errorf("failed to create publisher: %w", err)
}

if fqdn == "" {
log.Println("Getting FQDN from Avahi")
slog.Info("getting FQDN from Avahi")
fqdn = publisher.Fqdn()
}

Expand Down
31 changes: 21 additions & 10 deletions cmd/subdomain.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"context"
"errors"
"fmt"
"log"
"log/slog"
"net"
"strings"

Expand All @@ -21,6 +21,7 @@ type dnsMsg struct {

// listen creates a UDP connection to multicast for listening to DNS messages.
func listen() (*net.UDPConn, error) {
slog.Debug("creating multicast UDP connection", "ip", "224.0.0.251", "port", 5353)
addr := &net.UDPAddr{
IP: net.ParseIP("224.0.0.251"),
Port: 5353,
Expand All @@ -31,11 +32,13 @@ func listen() (*net.UDPConn, error) {
return nil, err
}

slog.Debug("multicast UDP connection created successfully")
return conn, nil
}

// reader reads DNS messages from the UDP connection. Assume that context is canceled before closing the connection.
func reader(ctx context.Context, conn *net.UDPConn) chan *dnsMsg {
slog.Debug("starting DNS message reader")
buf := make([]byte, 1500)

msgCh := make(chan *dnsMsg)
Expand All @@ -49,7 +52,7 @@ func reader(ctx context.Context, conn *net.UDPConn) chan *dnsMsg {
bytesRead, remoteAddress, err := conn.ReadFromUDP(buf)
if err != nil {
if ctx.Err() != nil {
log.Println("Closing reader")
slog.Info("closing reader")
close(msgCh)
return
}
Expand All @@ -58,12 +61,14 @@ func reader(ctx context.Context, conn *net.UDPConn) chan *dnsMsg {
msgCh <- dnsMsg
return
}
slog.Debug("received UDP message", "bytes", bytesRead, "from", remoteAddress)

if err := dnsMsg.msg.Unpack(buf[:bytesRead]); err != nil {
dnsMsg.err = errors.Join(dnsMsg.err, fmt.Errorf("failed to unpack message: %w", err))
msgCh <- dnsMsg
continue
}
slog.Debug("unpacked DNS message successfully")

msgCh <- dnsMsg
}
Expand All @@ -75,20 +80,24 @@ func reader(ctx context.Context, conn *net.UDPConn) chan *dnsMsg {
// selectQuestion filters and selects questions with the given FQDN suffix.
func selectQuestion(fqdn string, qs []dns.Question) (res []string) {
suffix := "." + fqdn
slog.Debug("filtering DNS questions", "suffix", suffix, "questions", len(qs))

for _, q := range qs {
if strings.HasSuffix(q.Name, suffix) {
slog.Debug("found matching question", "name", q.Name, "type", dns.TypeToString[q.Qtype])
res = append(res, q.Name)
}
}

slog.Debug("filtered questions", "matches", len(res))
return res
}

// runSubdomain starts listening for DNS messages, filters relevant questions, and publishes corresponding CNAMEs.
func runSubdomain(ctx context.Context, publisher *avahi.Publisher, fqdn string, ttl uint32) error {
log.Printf("FQDN: %s", fqdn)
slog.Info("running subdomain publisher", "fqdn", fqdn)

log.Println("Create connection to multicast")
slog.Info("creating connection to multicast")
conn, err := listen()
if err != nil {
return fmt.Errorf("failed to create connection: %w", err)
Expand All @@ -99,23 +108,25 @@ func runSubdomain(ctx context.Context, publisher *avahi.Publisher, fqdn string,
go func() {
<-ctx.Done()
fmt.Println() // Add new line after ^C
log.Println("Closing connection")
slog.Info("closing connection")
if err := conn.Close(); err != nil {
log.Printf("Failed to close connection: %v", err)
slog.Error("failed to close connection", "error", err)
}
}()

log.Println("Start listening")
slog.Info("start listening")
for m := range msgCh {
msg := m.msg
if m.err != nil {
log.Printf("Error: %v", m.err)
slog.Error("error processing message", "error", m.err)
continue
}
slog.Debug("processing DNS message", "questions", len(msg.Question))

found := selectQuestion(fqdn, msg.Question)

if len(found) > 0 {
slog.Debug("publishing matching CNAMEs", "count", len(found))
if err := publisher.PublishCNAMES(found, ttl); err != nil {
return fmt.Errorf("failed to publish CNAMEs: %w", err)
}
Expand Down Expand Up @@ -147,15 +158,15 @@ func Subdomain(ctx context.Context) *cli.Command {
ttl := uint32(cCtx.Uint("ttl"))
fqdn := cCtx.String("fqdn")

log.Println("Creating publisher")
slog.Info("creating publisher")
publisher, err := avahi.NewPublisher()
if err != nil {
return fmt.Errorf("failed to create publisher: %w", err)
}
defer publisher.Close()

if fqdn == "" {
log.Println("Getting FQDN from Avahi")
slog.Info("getting FQDN from Avahi")
fqdn = publisher.Fqdn()
}

Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ module github.com/grishy/go-avahi-cname
go 1.23.3

require (
github.com/carlmjohnson/versioninfo v0.22.5
github.com/earthboundkid/versioninfo/v2 v2.24.1
github.com/godbus/dbus/v5 v5.1.0
github.com/holoplot/go-avahi v1.0.1
github.com/lmittmann/tint v1.0.6
github.com/mattn/go-isatty v0.0.20
github.com/miekg/dns v1.1.62
github.com/urfave/cli/v2 v2.27.5
)
Expand Down
9 changes: 7 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc=
github.com/carlmjohnson/versioninfo v0.22.5/go.mod h1:QT9mph3wcVfISUKd0i9sZfVrPviHuSF+cUtLjm2WSf8=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/earthboundkid/versioninfo/v2 v2.24.1 h1:SJTMHaoUx3GzjjnUO1QzP3ZXK6Ee/nbWyCm58eY3oUg=
github.com/earthboundkid/versioninfo/v2 v2.24.1/go.mod h1:VcWEooDEuyUJnMfbdTh0uFN4cfEIg+kHMuWB2CDCLjw=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/holoplot/go-avahi v1.0.1 h1:XcqR2keL4qWRnlxHD5CAOdWpLFZJ+EOUK0vEuylfvvk=
github.com/holoplot/go-avahi v1.0.1/go.mod h1:qH5psEKb0DK+BRplMfc+RY4VMOlbf6mqfxgpMy6aP0M=
github.com/lmittmann/tint v1.0.6 h1:vkkuDAZXc0EFGNzYjWcV0h7eEX+uujH48f/ifSkJWgc=
github.com/lmittmann/tint v1.0.6/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
Expand All @@ -21,6 +25,7 @@ golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
Expand Down
Loading

0 comments on commit 25aab71

Please sign in to comment.