Skip to content

Commit

Permalink
Add support for multiple zones
Browse files Browse the repository at this point in the history
* Fix dstapp#7 dstapp#18 by allowing updates by fqdn
  • Loading branch information
Golit committed Aug 31, 2020
1 parent 00fba17 commit bb38b1e
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 21 deletions.
2 changes: 1 addition & 1 deletion rest-api/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (flagsConf *ConfigFlags) setupFlags() {
flag.BoolVar(&flagsConf.DoNotLoadConfig, "noConfig", false, "Do not load the config file")
flag.StringVar(&flagsConf.ConfigFile, "c", "/etc/dyndns.json", "The configuration file")
flag.StringVar(&flagsConf.Server, "server", "localhost", "The address of the bind server")
flag.StringVar(&flagsConf.Zone, "zone", "localhost", "Zone")
flag.StringVar(&flagsConf.Zone, "zone", "", "Configuring a default zone will allow to send request with the hostname only as the domain")
flag.StringVar(&flagsConf.NsupdateBinary, "nsupdateBinary", "nsupdate", "Path to nsupdate program")
flag.IntVar(&flagsConf.RecordTTL, "recordTTL", 300, "RecordTTL")
flag.IntVar(&flagsConf.Port, "p", 8080, "Port")
Expand Down
7 changes: 5 additions & 2 deletions rest-api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ const (
var appConfig = &ConfigFlags{}

func main() {
defaultExtractor := defaultRequestDataExtractor{}
dynExtractor := dynRequestDataExtractor{}
defaultExtractor := defaultRequestDataExtractor{appConfig: &appConfig.Config}
dynExtractor := dynRequestDataExtractor{defaultRequestDataExtractor{appConfig: &appConfig.Config}}

appConfig.LoadConfig()

Expand Down Expand Up @@ -90,6 +90,9 @@ func updateDomains(r *http.Request, response *WebserviceResponse, onError func()
ipAddr: address.Address,
addrType: address.AddrType,
secret: extractor.Secret(r),
ddnsKeyName: extractor.DdnsKeyName(r, domain),
zone: extractor.Zone(r, domain),
fqdn: extractor.Fqdn(r, domain),
}
result := recordUpdate.updateRecord()

Expand Down
21 changes: 7 additions & 14 deletions rest-api/nsupdate.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,21 +104,14 @@ func escape(s string) string {

// UpdateRecord sends the record update request to the nsupdate program
func (nsupdate *NSUpdate) UpdateRecord(r RecordUpdateRequest) {
fqdn := escape(r.domain)
if appConfig.Zone != "" {
fqdn = escape(r.domain) + "." + appConfig.Zone
nsupdate.write("server %s\n", appConfig.Server)
if r.zone != "" {
nsupdate.write("zone %s\n", r.zone+".")
}

if r.secret != "" {
fqdnN := strings.TrimLeft(appConfig.Zone, ".")
nsupdate.write("key hmac-sha256:ddns-key.%s %s\n", fqdnN, escape(r.secret))
if r.ddnsKeyName != "" {
nsupdate.write("key hmac-sha256:ddns-key.%s %s\n", escape(r.ddnsKeyName), escape(r.secret))
}

nsupdate.write("server %s\n", appConfig.Server)
if appConfig.Zone != "" {
nsupdate.write("zone %s\n", appConfig.Zone)
}
nsupdate.write("update delete %s %s\n", fqdn, r.addrType)
nsupdate.write("update add %s %v %s %s\n", fqdn, appConfig.RecordTTL, r.addrType, escape(r.ipAddr))
nsupdate.write("update delete %s %s\n", r.fqdn, r.addrType)
nsupdate.write("update add %s %v %s %s\n", r.fqdn, appConfig.RecordTTL, r.addrType, escape(r.ipAddr))
nsupdate.write("send\n")
}
36 changes: 32 additions & 4 deletions rest-api/request_data_extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ package main
import (
"context"
"net/http"
"strings"
)

type requestDataExtractor interface {
Address(r *http.Request) string
Secret(r *http.Request) string
Domain(r *http.Request) string
DdnsKey(r *http.Request) string
DdnsKeyName(r *http.Request, domain string) string
Zone(r *http.Request, domain string) string
Fqdn(r *http.Request, domain string) string
}

type defaultRequestDataExtractor struct{}
type defaultRequestDataExtractor struct {
appConfig *Config
}

func (e defaultRequestDataExtractor) Address(r *http.Request) string {
return r.URL.Query().Get("addr")
Expand All @@ -23,8 +28,31 @@ func (e defaultRequestDataExtractor) Secret(r *http.Request) string {
func (e defaultRequestDataExtractor) Domain(r *http.Request) string {
return r.URL.Query().Get("domain")
}
func (e defaultRequestDataExtractor) DdnsKey(r *http.Request) string {
return r.URL.Query().Get("ddnskey")
func (e defaultRequestDataExtractor) DdnsKeyName(r *http.Request, domain string) string {
ddnsKeyName := r.URL.Query().Get("ddnskeyname")
if ddnsKeyName != "" {
return ddnsKeyName
}
ddnsKeyName = e.Zone(r, domain)
if ddnsKeyName != "" {
return ddnsKeyName
}
ddnsKeyName = e.Fqdn(r, domain)
return ddnsKeyName
}
func (e defaultRequestDataExtractor) Zone(r *http.Request, domain string) string {
zone := r.URL.Query().Get("zone")
if zone != "" {
return zone
}
zone = strings.TrimRight(e.appConfig.Zone, ".")
if domain[len(domain)-1:] == "." {
zone = ""
}
return zone
}
func (e defaultRequestDataExtractor) Fqdn(r *http.Request, domain string) string {
return strings.TrimRight(escape(domain)+"."+e.Zone(r, domain), ".")
}

type dynRequestDataExtractor struct{ defaultRequestDataExtractor }
Expand Down
95 changes: 95 additions & 0 deletions rest-api/request_data_extractor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package main

import (
"net/http"
"testing"
)

func verify(t *testing.T, r *http.Request, extractor requestDataExtractor, domain string, expected RecordUpdateRequest) {
rru := RecordUpdateRequest{
ddnsKeyName: extractor.DdnsKeyName(r, domain),
zone: extractor.Zone(r, domain),
fqdn: extractor.Fqdn(r, domain),
}
if rru.zone != expected.zone {
t.Fatalf("Zone not configured but not empty: %s != %s", rru.zone, expected.zone)
}

if rru.fqdn != expected.fqdn {
t.Fatalf("Wrong fqdn: %s != %s", rru.fqdn, expected.fqdn)
}

if rru.ddnsKeyName != expected.ddnsKeyName {
t.Fatalf("Wrong ddnskeyname: %s != %s", rru.ddnsKeyName, expected.ddnsKeyName)
}
}

func TestExtractorUnconfiguredZone(t *testing.T) {
var e = defaultRequestDataExtractor{appConfig: &Config{
Zone: "",
}}

domain := "foo.example.org"
req, _ := http.NewRequest("GET", "/update?secret=changeme&domain="+domain+"&addr=1.2.3.4", nil)
verify(t, req, e, domain, RecordUpdateRequest{
zone: "",
fqdn: "foo.example.org",
ddnsKeyName: "foo.example.org",
})
}

func TestExtractorUnconfiguredZoneWithZoneInRequest(t *testing.T) {
var e = defaultRequestDataExtractor{appConfig: &Config{
Zone: "",
}}

domain := "foo"
req, _ := http.NewRequest("GET", "/update?secret=changeme&domain="+domain+"&addr=1.2.3.4&zone=example.org", nil)
verify(t, req, e, domain, RecordUpdateRequest{
zone: "example.org",
fqdn: "foo.example.org",
ddnsKeyName: "example.org",
})
}

func TestExtractorUnconfiguredZoneWithDDnskeyInRequest(t *testing.T) {
var e = defaultRequestDataExtractor{appConfig: &Config{
Zone: "",
}}

domain := "foo.example.org"
req, _ := http.NewRequest("GET", "/update?secret=changeme&domain="+domain+"&addr=1.2.3.4&ddnskeyname=example.org", nil)
verify(t, req, e, domain, RecordUpdateRequest{
zone: "",
fqdn: "foo.example.org",
ddnsKeyName: "example.org",
})
}

func TestExtractorConfiguredZoneAndOnlyWithHostname(t *testing.T) {
var e = defaultRequestDataExtractor{appConfig: &Config{
Zone: "example.org.",
}}

domain := "foo"
req, _ := http.NewRequest("GET", "/update?secret=changeme&domain="+domain+"&addr=1.2.3.4", nil)
verify(t, req, e, domain, RecordUpdateRequest{
zone: "example.org",
fqdn: "foo.example.org",
ddnsKeyName: "example.org",
})
}

func TestExtractorConfiguredZoneAndOnlyWithFQDN(t *testing.T) {
var e = defaultRequestDataExtractor{appConfig: &Config{
Zone: "example.org.",
}}

domain := "foo.example.org."
req, _ := http.NewRequest("GET", "/update?secret=changeme&domain="+domain+"&addr=1.2.3.4", nil)
verify(t, req, e, domain, RecordUpdateRequest{
zone: "",
fqdn: "foo.example.org",
ddnsKeyName: "foo.example.org",
})
}

0 comments on commit bb38b1e

Please sign in to comment.