rewrite most code; removes database to solely rely on config file
This commit is contained in:
parent
32bf03dc07
commit
ef22e29cef
10 changed files with 246 additions and 249 deletions
37
auth.go
37
auth.go
|
|
@ -1,37 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func authenticateUser(db *sql.DB, username, password string) bool {
|
|
||||||
hashedPassword, ok := getPasswordForUser(db, username)
|
|
||||||
if ok {
|
|
||||||
err := bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
|
|
||||||
//TODO: print error message?
|
|
||||||
return err == nil
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func basicAuth(db *sql.DB) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user, pass, ok := r.BasicAuth()
|
|
||||||
if !ok || !authenticateUser(db, user, pass) {
|
|
||||||
w.Header().Set("WWW-Authenticate", `Basic realm="dyndns"`)
|
|
||||||
w.WriteHeader(401)
|
|
||||||
w.Write([]byte("badauth"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userdata, err := getDataForUser(db, user)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
handleRequest(w, r, userdata)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
68
backend.go
68
backend.go
|
|
@ -1,68 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// check if net.ParseIP returns nil!!
|
|
||||||
func getIP(r *http.Request) net.IP {
|
|
||||||
addr := r.URL.Query().Get("myip")
|
|
||||||
if len(addr) > 0 {
|
|
||||||
return net.ParseIP(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
addr = r.Header.Get(http.CanonicalHeaderKey("X-Forwarded-For"))
|
|
||||||
if len(addr) > 0 {
|
|
||||||
end := strings.Index(addr, ", ")
|
|
||||||
if end == -1 {
|
|
||||||
end = len(addr)
|
|
||||||
}
|
|
||||||
return net.ParseIP(addr[:end])
|
|
||||||
}
|
|
||||||
|
|
||||||
addr = r.Header.Get(http.CanonicalHeaderKey("X-Real-IP"))
|
|
||||||
if len(addr) > 0 {
|
|
||||||
return net.ParseIP(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
addr, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return net.ParseIP(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleRequest(w http.ResponseWriter, r *http.Request, data userData) {
|
|
||||||
msg := "good"
|
|
||||||
defer func() {
|
|
||||||
w.Write([]byte(msg))
|
|
||||||
}()
|
|
||||||
|
|
||||||
hostname := r.URL.Query().Get("hostname")
|
|
||||||
if len(hostname) <= 0 || hostname != data.hostname {
|
|
||||||
msg = "nohost"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ipaddr := getIP(r)
|
|
||||||
if ipaddr == nil {
|
|
||||||
//TODO:
|
|
||||||
msg = "error"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
msg = "Hostname: " + hostname + " , IP: " + ipaddr.String()
|
|
||||||
|
|
||||||
updated, err := updateNameserver(data, ipaddr)
|
|
||||||
if err != nil {
|
|
||||||
msg = "dnserr"
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !updated {
|
|
||||||
msg = "nochg"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
[database]
|
|
||||||
host = "127.0.0.1"
|
|
||||||
port = 5432
|
port = 5432
|
||||||
user = "username"
|
|
||||||
pass = "password"
|
[[rrconfig]]
|
||||||
name = "database"
|
username = "username"
|
||||||
|
password = "password"
|
||||||
|
nameserver = "ns1.example.org"
|
||||||
|
zonename = "example.org"
|
||||||
|
hostname = "somerecord.example.org"
|
||||||
|
tsigkey = "<tsig-key>"
|
||||||
|
|
|
||||||
48
config.go
Normal file
48
config.go
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/pelletier/go-toml"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
ServerPort uint16 `toml:"port"`
|
||||||
|
RRConfigs []RRConfig `toml:"rrconfig"`
|
||||||
|
|
||||||
|
rrconfigs map[string]*RRConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type RRConfig struct {
|
||||||
|
Username string `toml:"username"`
|
||||||
|
Password string `toml:"password"`
|
||||||
|
Nameserver string `toml:"nameserver"`
|
||||||
|
Zonename string `toml:"zonename"`
|
||||||
|
Hostname string `toml:"hostname"`
|
||||||
|
Tsigkey string `toml:"tsigkey"`
|
||||||
|
Ttl int `toml:"ttl" default:"60"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadConfig(path string) (*Config, error) {
|
||||||
|
var cfg Config
|
||||||
|
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder := toml.NewDecoder(f).Strict(true)
|
||||||
|
err = decoder.Decode(&cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.rrconfigs = map[string]*RRConfig{}
|
||||||
|
for _, entry := range cfg.RRConfigs {
|
||||||
|
if _, ok := cfg.rrconfigs[entry.Username]; ok {
|
||||||
|
return nil, fmt.Errorf("Duplicate username detected")
|
||||||
|
}
|
||||||
|
cfg.rrconfigs[entry.Username] = &entry
|
||||||
|
}
|
||||||
|
return &cfg, nil
|
||||||
|
}
|
||||||
72
data.go
72
data.go
|
|
@ -1,72 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"database/sql"
|
|
||||||
_ "github.com/lib/pq"
|
|
||||||
)
|
|
||||||
|
|
||||||
type databaseConfig struct {
|
|
||||||
Host string
|
|
||||||
Port int
|
|
||||||
User string
|
|
||||||
Pass string
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
type userData struct {
|
|
||||||
nameserver string
|
|
||||||
zonename string
|
|
||||||
hostname string
|
|
||||||
tsigkey string
|
|
||||||
}
|
|
||||||
|
|
||||||
func prepareDatabase() (*sql.DB, error) {
|
|
||||||
dbconf := config.Database
|
|
||||||
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s " +
|
|
||||||
"sslmode=disable", dbconf.Host, dbconf.Port,
|
|
||||||
dbconf.User, dbconf.Pass, dbconf.Name)
|
|
||||||
db, err := sql.Open("postgres", psqlInfo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("CREATE TABLE IF NOT EXISTS users " +
|
|
||||||
"(id SERIAL PRIMARY KEY, username VARCHAR(255) NULL, " +
|
|
||||||
"password VARCHAR(255) NULL, salt VARCHAR(255) NULL, " +
|
|
||||||
"nameserver VARCHAR(255) NULL, zone VARCHAR(255) NULL, " +
|
|
||||||
"hostname VARCHAR(255) NULL, tsig VARCHAR(255) NULL)")
|
|
||||||
if err != nil {
|
|
||||||
db.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return db, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPasswordForUser(db *sql.DB, username string) ([]byte, bool) {
|
|
||||||
var password []byte
|
|
||||||
|
|
||||||
row := db.QueryRow("SELECT password FROM users WHERE username=$1", username)
|
|
||||||
err := row.Scan(&password)
|
|
||||||
if err != nil {
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return nil, false
|
|
||||||
} else {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return password, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDataForUser(db *sql.DB, username string) (userData, error) {
|
|
||||||
var data userData
|
|
||||||
|
|
||||||
row := db.QueryRow("SELECT nameserver, zone, hostname, tsig " +
|
|
||||||
"FROM users WHERE username=$1", username)
|
|
||||||
err := row.Scan(&data.nameserver, &data.zonename, &data.hostname,
|
|
||||||
&data.tsigkey)
|
|
||||||
if err != nil {
|
|
||||||
return data, err
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
8
go.mod
Normal file
8
go.mod
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
module git.preisner.eu/preisi/dyndns
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/pelletier/go-toml v1.9.3
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||||
|
)
|
||||||
12
go.sum
Normal file
12
go.sum
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
||||||
|
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
|
||||||
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
20
main.go
20
main.go
|
|
@ -2,34 +2,22 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"github.com/BurntSushi/toml"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type tomlConfig struct {
|
|
||||||
Database databaseConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
var config tomlConfig
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var configPath string
|
var configPath string
|
||||||
flag.StringVar(&configPath, "config", "config.toml", "path to config file")
|
flag.StringVar(&configPath, "config", "config.toml", "path to config file")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
_, err := toml.DecodeFile(configPath, &config)
|
cfg, err := LoadConfig(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := prepareDatabase()
|
http.HandleFunc("/", RequestHandler(cfg))
|
||||||
if err != nil {
|
err = http.ListenAndServe(fmt.Sprintf(":%d", cfg.ServerPort), nil)
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
http.HandleFunc("/", basicAuth(db))
|
|
||||||
err = http.ListenAndServe(":3002", nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,70 +1,59 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createQuery(data userData, addr net.IP, deleteRecord bool) string {
|
func requiresRRUpdate(entry *RRConfig, addr net.IP) bool {
|
||||||
var query strings.Builder
|
// TODO: use custom resolver to query authoritive nameserver instead of
|
||||||
|
// local resolver
|
||||||
query.WriteString(fmt.Sprintf("key %s\n", data.tsigkey))
|
addrs, err := net.LookupIP(entry.Hostname)
|
||||||
query.WriteString(fmt.Sprintf("server %s\n", data.nameserver))
|
if err != nil {
|
||||||
query.WriteString(fmt.Sprintf("zone %s\n", data.zonename))
|
log.Printf("dns lookup failed: %s", err)
|
||||||
if deleteRecord {
|
// enforce update, it's better than not trying at all
|
||||||
query.WriteString(fmt.Sprintf("update delete %s. A\n", data.hostname))
|
return true
|
||||||
}
|
}
|
||||||
query.WriteString(fmt.Sprintf("update add %s. A %s\n", data.hostname,
|
|
||||||
addr.String()))
|
|
||||||
query.WriteString("show\n")
|
|
||||||
query.WriteString("send\n")
|
|
||||||
|
|
||||||
return query.String()
|
// check if the current ip matches
|
||||||
|
for _, ip := range addrs {
|
||||||
|
if ip.Equal(addr) {
|
||||||
|
// the ip seems to be still up-to-date -> no update required
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryNameserver(query string) error {
|
func updateRR(entry *RRConfig, addr net.IP) error {
|
||||||
var (
|
query := generateQuery(entry, addr)
|
||||||
stdout bytes.Buffer
|
return executeQuery(query)
|
||||||
stderr bytes.Buffer
|
}
|
||||||
)
|
|
||||||
|
|
||||||
|
func generateQuery(entry *RRConfig, addr net.IP) string {
|
||||||
|
var q strings.Builder
|
||||||
|
|
||||||
|
fmt.Fprintf(&q, "key %s\n", entry.Tsigkey)
|
||||||
|
fmt.Fprintf(&q, "server %s\n", entry.Nameserver)
|
||||||
|
fmt.Fprintf(&q, "zone %s\n", entry.Zonename)
|
||||||
|
// TODO: check if addr is ipv4 or ipv6 (-> update A or AAAA)
|
||||||
|
fmt.Fprintf(&q, "add %s. %d A %s\n", entry.Hostname, entry.Ttl, addr.String())
|
||||||
|
fmt.Fprintf(&q, "send\n")
|
||||||
|
|
||||||
|
return q.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeQuery(query string) error {
|
||||||
cmd := exec.Command("knsupdate", "-v")
|
cmd := exec.Command("knsupdate", "-v")
|
||||||
cmd.Stdin = strings.NewReader(query)
|
cmd.Stdin = strings.NewReader(query)
|
||||||
cmd.Stdout = &stdout
|
output, err := cmd.CombinedOutput()
|
||||||
cmd.Stderr = &stderr
|
// TODO: is outputting stdout+stderr on failure even necessary?
|
||||||
err := cmd.Run()
|
if err != nil {
|
||||||
fmt.Printf(stdout.String())
|
log.Printf("executeQuery failed: %s", output)
|
||||||
fmt.Printf(stderr.String())
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateNameserver(data userData, addr net.IP) (bool, error) {
|
|
||||||
updateRecord := true
|
|
||||||
deleteRecord := true
|
|
||||||
|
|
||||||
addrs, err := net.LookupIP(data.hostname)
|
|
||||||
if err != nil {
|
|
||||||
deleteRecord = false
|
|
||||||
} else {
|
|
||||||
for _, ip := range addrs {
|
|
||||||
if ip.Equal(addr) {
|
|
||||||
updateRecord = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !updateRecord {
|
|
||||||
return false, nil
|
|
||||||
} else {
|
|
||||||
query := createQuery(data, addr, deleteRecord)
|
|
||||||
err = queryNameserver(query)
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
126
web.go
Normal file
126
web.go
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isAuthenticated(cfg *Config, r *http.Request) *RRConfig {
|
||||||
|
user, pw, ok := r.BasicAuth()
|
||||||
|
if !ok {
|
||||||
|
// no basic auth header detected
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
entry, ok := cfg.rrconfigs[user]
|
||||||
|
if !ok {
|
||||||
|
// non-existent username
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := bcrypt.CompareHashAndPassword([]byte(entry.Password), []byte(pw))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Config contains invalid password hash for user %q", user)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIpAddress(r *http.Request) net.IP {
|
||||||
|
addr := r.URL.Query().Get("myip")
|
||||||
|
if len(addr) > 0 {
|
||||||
|
// If myip was supplied, parse it and return the result regardless of
|
||||||
|
// success, failure will be handled later.
|
||||||
|
return net.ParseIP(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check http headers and request for other possible ip address sources
|
||||||
|
addr = r.Header.Get(http.CanonicalHeaderKey("X-Forwarded-For"))
|
||||||
|
if len(addr) > 0 {
|
||||||
|
tokens := strings.Split(addr, ", ")
|
||||||
|
// tokens is always at least 1 element long
|
||||||
|
// -> return first element as it contains the client's ip address
|
||||||
|
return net.ParseIP(tokens[0])
|
||||||
|
}
|
||||||
|
// TODO: support newer standarized "Forwarded"-Header
|
||||||
|
|
||||||
|
addr = r.Header.Get(http.CanonicalHeaderKey("X-Real-IP"))
|
||||||
|
if len(addr) > 0 {
|
||||||
|
return net.ParseIP(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Retrieving IP-Address failed: %s", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return net.ParseIP(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns api-response on failure
|
||||||
|
func verifyHostname(entry *RRConfig, hostname string) string {
|
||||||
|
if len(hostname) <= 0 {
|
||||||
|
return "nohost"
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: allow single user to update multiple hostnames
|
||||||
|
// TODO: return ntfqdn -> differentiate between 'hostname doesnt exist' and
|
||||||
|
// 'hostname is not fqdn'
|
||||||
|
if hostname != entry.Hostname {
|
||||||
|
return "nohost"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func RequestHandler(cfg *Config) func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
userdata := isAuthenticated(cfg, r)
|
||||||
|
if userdata == nil {
|
||||||
|
w.Header().Set("WWW-Authenticate", `Basic realm="dyndns"`)
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
w.Write([]byte("badauth"))
|
||||||
|
fmt.Fprintln(w, "badauth")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// all remaining responses should come with status code 200
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
// check for 'valid' useragent, should not be strictly necessary but
|
||||||
|
// the protocol demands it
|
||||||
|
if len(r.UserAgent()) <= 0 {
|
||||||
|
fmt.Fprintln(w, "badagent")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname := r.URL.Query().Get("hostname")
|
||||||
|
response := verifyHostname(userdata, hostname)
|
||||||
|
if response != "" {
|
||||||
|
fmt.Fprintln(w, response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ipaddr := getIpAddress(r)
|
||||||
|
if ipaddr == nil {
|
||||||
|
fmt.Fprintln(w, "911")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !requiresRRUpdate(userdata, ipaddr) {
|
||||||
|
fmt.Fprintf(w, "nochg %s\n", ipaddr.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := updateRR(userdata, ipaddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("updating RR failed: %s", err)
|
||||||
|
fmt.Fprintln(w, "911")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "good %s\n", ipaddr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue