Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
![]() |
adf836979a |
24 changed files with 829 additions and 531 deletions
3
LICENSE
3
LICENSE
|
@ -1,7 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Benjamin Bärthlein
|
||||
Copyright (c) 2016 David Prandzioch
|
||||
Copyright (c) 2022 Benjamin Bärthlein
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
13
README.md
13
README.md
|
@ -8,7 +8,9 @@
|
|||

|
||||

|
||||
|
||||
With docker-ddns-server you can setup your own dynamic DNS server. This project is inspired by https://github.com/dprandzioch/docker-ddns . In addition to the original version, you can setup and maintain your dyndns entries via simple web ui.
|
||||
With docker-ddns-server you can setup your own dynamic DNS server. You can setup and maintain your dyndns entries via simple web ui.
|
||||
The application uses its own dns server and has no dependencies to C or the os. In general you can compile
|
||||
and run it on every os and architecture supported by the golang compiler.
|
||||
|
||||
<p float="left">
|
||||
<img src="https://raw.githubusercontent.com/benjaminbear/docker-ddns-server/master/img/addhost.png" width="285">
|
||||
|
@ -20,6 +22,15 @@ With docker-ddns-server you can setup your own dynamic DNS server. This project
|
|||
|
||||
You can either take the docker image or build it on your own.
|
||||
|
||||
### Using as binary
|
||||
|
||||
You can build and install it on your own
|
||||
```
|
||||
go install github.com/benjaminbear/docker-ddns-server/v2@latest
|
||||
```
|
||||
or download the package on the release page.
|
||||
Don't forget to add the static and views folder from the repository.
|
||||
|
||||
### Using the docker image
|
||||
|
||||
Just customize this to your needs and run:
|
||||
|
|
|
@ -1,30 +1,17 @@
|
|||
FROM golang:1.18 as builder
|
||||
|
||||
ENV GO111MODULE=on
|
||||
ENV GOPATH=/root/go
|
||||
RUN mkdir -p /root/go/src
|
||||
COPY dyndns /root/go/src/dyndns
|
||||
WORKDIR /root/go/src/dyndns
|
||||
# temp sqlite3 error fix
|
||||
ENV CGO_CFLAGS "-g -O2 -Wno-return-local-addr"
|
||||
RUN go mod tidy
|
||||
RUN GOOS=linux go build -o /root/go/bin/dyndns && go test -v
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /root/go/bin/dyndns && go test -v
|
||||
|
||||
FROM debian:11-slim
|
||||
FROM scratch
|
||||
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
|
||||
apt-get install -q -y bind9 dnsutils curl && \
|
||||
apt-get clean
|
||||
|
||||
RUN chmod 770 /var/cache/bind
|
||||
COPY deployment/setup.sh /root/setup.sh
|
||||
RUN chmod +x /root/setup.sh
|
||||
COPY deployment/named.conf.options /etc/bind/named.conf.options
|
||||
|
||||
WORKDIR /root
|
||||
COPY --from=builder /root/go/bin/dyndns /root/dyndns
|
||||
COPY dyndns/views /root/views
|
||||
COPY dyndns/static /root/static
|
||||
COPY --from=builder /root/go/bin/dyndns /dyndns
|
||||
COPY dyndns/views /views
|
||||
COPY dyndns/static /static
|
||||
|
||||
EXPOSE 53 8080
|
||||
CMD ["sh", "-c", "/root/setup.sh ; service named start ; /root/dyndns"]
|
||||
CMD ["/dyndns"]
|
|
@ -1,8 +0,0 @@
|
|||
options {
|
||||
directory "/var/cache/bind";
|
||||
dnssec-validation auto;
|
||||
recursion no;
|
||||
allow-transfer { none; };
|
||||
auth-nxdomain no;
|
||||
listen-on-v6 { any; };
|
||||
};
|
|
@ -1,51 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
#[ -z "$DDNS_ADMIN_LOGIN" ] && echo "DDNS_ADMIN_LOGIN not set" && exit 1;
|
||||
[ -z "$DDNS_DOMAINS" ] && echo "DDNS_DOMAINS not set" && exit 1;
|
||||
[ -z "$DDNS_PARENT_NS" ] && echo "DDNS_PARENT_NS not set" && exit 1;
|
||||
[ -z "$DDNS_DEFAULT_TTL" ] && echo "DDNS_DEFAULT_TTL not set" && exit 1;
|
||||
|
||||
DDNS_IP=$(curl icanhazip.com)
|
||||
|
||||
for d in ${DDNS_DOMAINS//,/ }
|
||||
do
|
||||
if ! grep 'zone "'$d'"' /etc/bind/named.conf > /dev/null
|
||||
then
|
||||
echo "creating zone...";
|
||||
cat >> /etc/bind/named.conf <<EOF
|
||||
zone "$d" {
|
||||
type master;
|
||||
file "$d.zone";
|
||||
allow-query { any; };
|
||||
allow-transfer { none; };
|
||||
allow-update { localhost; };
|
||||
};
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ ! -f /var/cache/bind/$d.zone ]
|
||||
then
|
||||
echo "creating zone file..."
|
||||
cat > /var/cache/bind/$d.zone <<EOF
|
||||
\$ORIGIN .
|
||||
\$TTL 86400 ; 1 day
|
||||
$d IN SOA ${DDNS_PARENT_NS}. root.${d}. (
|
||||
74 ; serial
|
||||
3600 ; refresh (1 hour)
|
||||
900 ; retry (15 minutes)
|
||||
604800 ; expire (1 week)
|
||||
86400 ; minimum (1 day)
|
||||
)
|
||||
NS ${DDNS_PARENT_NS}.
|
||||
A ${DDNS_IP}
|
||||
\$ORIGIN ${d}.
|
||||
\$TTL ${DDNS_DEFAULT_TTL}
|
||||
EOF
|
||||
fi
|
||||
done
|
||||
|
||||
# If /var/cache/bind is a volume, permissions are probably not ok
|
||||
chown root:bind /var/cache/bind
|
||||
chown bind:bind /var/cache/bind/*
|
||||
chmod 770 /var/cache/bind
|
||||
chmod 644 /var/cache/bind/*
|
99
dyndns/config/config.go
Normal file
99
dyndns/config/config.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/caarlos0/env/v6"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
AdminLogin string `env:"ADMIN_LOGIN"`
|
||||
Title string `env:"TITLE" envDefault:"TheBBCloud DynDNS"`
|
||||
LogoutUrl string `env:"LOGOUT_URL"`
|
||||
ClearLogInterval int `env:"CLEAR_LOG_INTERVAL"`
|
||||
Domains []string `env:"DOMAINS,notEmpty" envSeparator:";"`
|
||||
ParentNS string `env:"PARENT_NS,notEmpty"`
|
||||
DefaultTTL int `env:"DEFAULT_TTL,notEmpty"`
|
||||
AllowWildcard bool `env:"ALLOW_WILDCARD"`
|
||||
ExternalIP net.IP `env:"EXTERNAL_IP"`
|
||||
ExternalIPResolver url.URL `env:"EXTERNAL_IP_RESOLVER" envDefault:"http://icanhazip.com"`
|
||||
}
|
||||
|
||||
// ParseEnvs parses all needed environment variables:
|
||||
// DDNS_ADMIN_LOGIN: The basic auth login string in htpasswd style.
|
||||
// DDNS_DOMAINS: All domains that will be handled by the dyndns server.
|
||||
func ParseEnvs() (*Config, error) {
|
||||
fmt.Println("Reading environment variables")
|
||||
|
||||
c := &Config{}
|
||||
err := env.Parse(c, env.Options{Prefix: "DDNS_"})
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
|
||||
err = c.validateExternalIP()
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
|
||||
c.printConfig()
|
||||
|
||||
return c, err
|
||||
}
|
||||
|
||||
func (c *Config) printConfig() {
|
||||
if c.AdminLogin == "" {
|
||||
fmt.Println("No Auth! DDNS_ADMIN_LOGIN should be set")
|
||||
}
|
||||
|
||||
if c.AllowWildcard {
|
||||
fmt.Println("Wildcard allowed")
|
||||
}
|
||||
|
||||
fmt.Println("External IP set:", c.ExternalIP)
|
||||
fmt.Println("External IP Resolver:", c.ExternalIPResolver.String())
|
||||
fmt.Println("Domains:", c.Domains)
|
||||
fmt.Println("Parent Namespace:", c.ParentNS)
|
||||
fmt.Println("Default TTL:", c.DefaultTTL)
|
||||
fmt.Println("Web UI Title:", c.Title)
|
||||
|
||||
if c.LogoutUrl != "" {
|
||||
fmt.Println("Logout URL set:", c.LogoutUrl)
|
||||
}
|
||||
|
||||
if c.ClearLogInterval > 0 {
|
||||
fmt.Println("Log clear interval found:", c.ClearLogInterval, "days")
|
||||
}
|
||||
}
|
||||
|
||||
// Parse external IP or get it yourself.
|
||||
func (c *Config) validateExternalIP() error {
|
||||
if c.ExternalIP != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, err := http.Get(c.ExternalIPResolver.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ip := strings.TrimSpace(string(body))
|
||||
c.ExternalIP = net.ParseIP(ip)
|
||||
if c.ExternalIP == nil {
|
||||
return fmt.Errorf("%s is not a valid ip", ip)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package model
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/jinzhu/gorm"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CName is a dns cname entry.
|
37
dyndns/db/db.go
Normal file
37
dyndns/db/db.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
dbDir = "database"
|
||||
dbFile = "ddns.db"
|
||||
)
|
||||
|
||||
// InitDB creates an empty database and creates all tables if there isn't already one, or opens the existing one.
|
||||
func InitDB() (*gorm.DB, error) {
|
||||
if _, err := os.Stat(dbDir); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dbDir, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(filepath.Join(dbDir, dbFile)))
|
||||
if err != nil {
|
||||
return db, err
|
||||
}
|
||||
|
||||
path, _ := filepath.Abs(filepath.Join(dbDir, dbFile))
|
||||
fmt.Println("Database path:", path)
|
||||
|
||||
err = db.AutoMigrate(&Host{}, &CName{}, &Log{})
|
||||
|
||||
return db, err
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
package model
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Host is a dns host entry.
|
||||
|
@ -34,3 +35,7 @@ func (h *Host) UpdateHost(updateHost *Host) (updateRecord bool) {
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Host) FullDomain() string {
|
||||
return fmt.Sprintf("%s.%s", h.Hostname, h.Domain)
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
package model
|
||||
package db
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Log defines a log entry.
|
64
dyndns/dnsserver/answer.go
Normal file
64
dyndns/dnsserver/answer.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
package dnsserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// IPAnswer creates an A or AAAA entry dns answer.
|
||||
// Returns error if ip is not a valid ipv4 or ipv6.
|
||||
func IPAnswer(domainName string, ip net.IP, ttl int) ([]dns.RR, error) {
|
||||
rrType := ipType(ip.String())
|
||||
if rrType == dns.TypeNone {
|
||||
return nil, fmt.Errorf("ip is not a valid ipv4 or ipv6")
|
||||
}
|
||||
|
||||
rrHeader := dns.RR_Header{
|
||||
Name: domainName,
|
||||
Rrtype: rrType,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: uint32(ttl),
|
||||
}
|
||||
|
||||
if rrType == dns.TypeAAAA {
|
||||
return []dns.RR{&dns.AAAA{Hdr: rrHeader, AAAA: ip}}, nil
|
||||
}
|
||||
|
||||
return []dns.RR{&dns.A{Hdr: rrHeader, A: ip}}, nil
|
||||
}
|
||||
|
||||
// CNameAnswer creates a CNAME entry dns answer.
|
||||
func CNameAnswer(domainName string, target string, ttl int) []dns.RR {
|
||||
rrHeader := dns.RR_Header{
|
||||
Name: domainName,
|
||||
Rrtype: dns.TypeCNAME,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: uint32(ttl),
|
||||
}
|
||||
|
||||
return []dns.RR{&dns.CNAME{Hdr: rrHeader, Target: target}}
|
||||
}
|
||||
|
||||
// SOAAnswer creates a SOA entry dns answer.
|
||||
func SOAAnswer(domainName string, nameServer string, ttl int) []dns.RR {
|
||||
rrHeader := dns.RR_Header{
|
||||
Name: domainName,
|
||||
Rrtype: dns.TypeSOA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: uint32(ttl),
|
||||
}
|
||||
|
||||
return []dns.RR{&dns.SOA{
|
||||
Hdr: rrHeader,
|
||||
Serial: uint32(time.Now().Unix()),
|
||||
Refresh: uint32(3600),
|
||||
Retry: uint32(900),
|
||||
Expire: uint32(604800),
|
||||
Minttl: uint32(86400),
|
||||
Ns: nameServer,
|
||||
Mbox: "root." + domainName,
|
||||
}}
|
||||
}
|
142
dyndns/dnsserver/handler.go
Normal file
142
dyndns/dnsserver/handler.go
Normal file
|
@ -0,0 +1,142 @@
|
|||
package dnsserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/ipparser"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/config"
|
||||
|
||||
"github.com/labstack/gommon/log"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
Config *config.Config
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func (h *Handler) Do(w dns.ResponseWriter, req *dns.Msg) {
|
||||
for _, q := range req.Question {
|
||||
log.Infof(prettyQuestion(q))
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.Authoritative = true
|
||||
m.SetReply(req)
|
||||
|
||||
// Refuse if requested domain is not supported
|
||||
_, _, err := h.checkDomain(UnFqdn(q.Name))
|
||||
if errors.Is(err, ErrUnsupportedDomain) {
|
||||
m.Rcode = dns.RcodeRefused
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
// Resolve
|
||||
answers, err := h.Resolve(q)
|
||||
if err != nil || len(answers) == 0 {
|
||||
answers, _ = h.ResolveSOA(q.Name)
|
||||
m.Ns = append(m.Ns, answers...)
|
||||
m.Rcode = dns.RcodeNameError
|
||||
w.WriteMsg(m)
|
||||
fmt.Println("return SOA as excuse with", answers, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("answer to be added is", answers)
|
||||
m.Answer = append(m.Answer, answers...)
|
||||
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Resolve(question dns.Question) ([]dns.RR, error) {
|
||||
emptyAnswers := make([]dns.RR, 0)
|
||||
|
||||
if question.Qclass != dns.ClassINET {
|
||||
return emptyAnswers, nil
|
||||
}
|
||||
|
||||
switch question.Qtype {
|
||||
case dns.TypeA:
|
||||
fallthrough
|
||||
case dns.TypeAAAA:
|
||||
// ResolveIP
|
||||
fmt.Println("IPResolve")
|
||||
ipResolveChain := []func(fqdn string) ([]dns.RR, error){h.ResolveDNSA, h.ResolveA, h.ResolveCName}
|
||||
|
||||
for i, resolveFunc := range ipResolveChain {
|
||||
answers, err := resolveFunc(question.Name)
|
||||
fmt.Println("finished chain run", i, "with", answers, err)
|
||||
if err == nil && len(answers) > 0 {
|
||||
fmt.Println("returning", answers, err)
|
||||
return answers, err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("finished ip w/o success")
|
||||
case dns.TypeCNAME:
|
||||
fmt.Println("CNameResolve")
|
||||
answers, err := h.ResolveCName(question.Name)
|
||||
|
||||
return answers, err
|
||||
case dns.TypeTXT:
|
||||
// return TXT
|
||||
fmt.Println("TXTResolve")
|
||||
case dns.TypeSOA:
|
||||
fmt.Println("SOAResolve")
|
||||
// ignore errors, it's just empty slices
|
||||
soaAnswer, _ := h.ResolveSOA(question.Name)
|
||||
|
||||
return soaAnswer, nil
|
||||
}
|
||||
|
||||
fmt.Println("returning empty")
|
||||
return emptyAnswers, nil
|
||||
}
|
||||
|
||||
var ErrUnsupportedDomain = errors.New("domain not supported")
|
||||
var ErrIsDomain = errors.New("fqdn is domain")
|
||||
|
||||
func (h *Handler) checkDomain(fullDomainName string) (hostname string, domain string, err error) {
|
||||
for _, d := range h.Config.Domains {
|
||||
if fullDomainName == d {
|
||||
return hostname, d, ErrIsDomain
|
||||
}
|
||||
|
||||
if strings.HasSuffix(fullDomainName, "."+d) && fullDomainName != "."+d {
|
||||
hostname = strings.TrimSuffix(fullDomainName, "."+d)
|
||||
|
||||
return hostname, d, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", "", ErrUnsupportedDomain
|
||||
}
|
||||
|
||||
func UnFqdn(s string) string {
|
||||
if dns.IsFqdn(s) {
|
||||
return s[:len(s)-1]
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func ipType(ip string) uint16 {
|
||||
if ipparser.ValidIP4(ip) {
|
||||
return dns.TypeA
|
||||
} else if ipparser.ValidIP6(ip) {
|
||||
return dns.TypeAAAA
|
||||
}
|
||||
|
||||
return dns.TypeNone
|
||||
}
|
||||
|
||||
func prettyQuestion(question dns.Question) string {
|
||||
return fmt.Sprintf("request for %s %s %s", question.Name, dns.ClassToString[question.Qclass], dns.TypeToString[question.Qtype])
|
||||
}
|
94
dyndns/dnsserver/resolver.go
Normal file
94
dyndns/dnsserver/resolver.go
Normal file
|
@ -0,0 +1,94 @@
|
|||
package dnsserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/db"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// ResolveDNSA checks if the requested domain is the domain of the ddns webserver
|
||||
// and returns the corresponding A dns entry.
|
||||
func (h *Handler) ResolveDNSA(fqdn string) ([]dns.RR, error) {
|
||||
for _, d := range h.Config.Domains {
|
||||
if d == UnFqdn(fqdn) {
|
||||
answers, err := IPAnswer(fqdn, h.Config.ExternalIP, h.Config.DefaultTTL)
|
||||
|
||||
return answers, err
|
||||
}
|
||||
}
|
||||
|
||||
return []dns.RR{}, nil
|
||||
}
|
||||
|
||||
// ResolveA checks if the requested domain is a valid A entry from the database
|
||||
// and returns the corresponding A dns entry.
|
||||
func (h *Handler) ResolveA(fqdn string) ([]dns.RR, error) {
|
||||
qHostname, qDomain, err := h.checkDomain(UnFqdn(fqdn))
|
||||
if err != nil {
|
||||
// return SOA
|
||||
return []dns.RR{}, err
|
||||
}
|
||||
|
||||
hosts := new([]db.Host)
|
||||
if num := h.DB.Where(&db.Host{Hostname: qHostname, Domain: qDomain}).Find(hosts).RowsAffected; num < 1 {
|
||||
|
||||
return []dns.RR{}, nil
|
||||
}
|
||||
|
||||
host := (*hosts)[0]
|
||||
answers, err := IPAnswer(fqdn, net.ParseIP(host.Ip), host.Ttl)
|
||||
if err != nil {
|
||||
// ip not supported
|
||||
return []dns.RR{}, err
|
||||
}
|
||||
|
||||
return answers, nil
|
||||
}
|
||||
|
||||
// ResolveCName checks if the requested domain is a valid CNAME entry from the database
|
||||
// and returns the corresponding CNAME and A dns entry.
|
||||
func (h *Handler) ResolveCName(fqdn string) ([]dns.RR, error) {
|
||||
qHostname, qDomain, err := h.checkDomain(UnFqdn(fqdn))
|
||||
if err != nil {
|
||||
// return SOA
|
||||
return []dns.RR{}, err
|
||||
}
|
||||
|
||||
cnames := new([]db.CName)
|
||||
if num := h.DB.Joins("Target").Where(&db.CName{Hostname: qHostname}).Find(cnames, "Target.domain = ?", qDomain).RowsAffected; num < 1 {
|
||||
|
||||
return []dns.RR{}, nil
|
||||
}
|
||||
|
||||
cname := (*cnames)[0]
|
||||
cnameAnswers := CNameAnswer(fqdn, cname.Target.FullDomain()+".", cname.Ttl)
|
||||
|
||||
aAnswers, err := IPAnswer(cname.Target.FullDomain()+".", net.ParseIP(cname.Target.Ip), cname.Target.Ttl)
|
||||
if err != nil {
|
||||
// return SOA
|
||||
return []dns.RR{}, err
|
||||
}
|
||||
|
||||
return append(cnameAnswers, aAnswers...), nil
|
||||
}
|
||||
|
||||
func (h *Handler) ResolveSOA(fqdn string) ([]dns.RR, error) {
|
||||
_, qDomain, err := h.checkDomain(UnFqdn(fqdn))
|
||||
fmt.Println(qDomain, err)
|
||||
if errors.Is(err, ErrIsDomain) {
|
||||
qDomain = UnFqdn(fqdn)
|
||||
} else if err != nil {
|
||||
return []dns.RR{}, err
|
||||
}
|
||||
|
||||
for _, d := range h.Config.Domains {
|
||||
if d == qDomain {
|
||||
return SOAAnswer(d+".", h.Config.ParentNS+".", h.Config.DefaultTTL), nil
|
||||
}
|
||||
}
|
||||
|
||||
return []dns.RR{}, fmt.Errorf("requesting for unsupported domain")
|
||||
}
|
56
dyndns/dnsserver/server.go
Normal file
56
dyndns/dnsserver/server.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package dnsserver
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Host string
|
||||
Port int
|
||||
RTimeout time.Duration
|
||||
WTimeout time.Duration
|
||||
}
|
||||
|
||||
func (s *Server) Addr() string {
|
||||
return net.JoinHostPort(s.Host, strconv.Itoa(s.Port))
|
||||
}
|
||||
|
||||
func (s *Server) Run(handler *Handler) {
|
||||
tcpHandler := dns.NewServeMux()
|
||||
tcpHandler.HandleFunc(".", handler.Do)
|
||||
|
||||
udpHandler := dns.NewServeMux()
|
||||
udpHandler.HandleFunc(".", handler.Do)
|
||||
|
||||
tcpServer := &dns.Server{Addr: s.Addr(),
|
||||
Net: "tcp",
|
||||
Handler: tcpHandler,
|
||||
ReadTimeout: s.RTimeout,
|
||||
WriteTimeout: s.WTimeout}
|
||||
|
||||
udpServer := &dns.Server{Addr: s.Addr(),
|
||||
Net: "udp",
|
||||
Handler: udpHandler,
|
||||
UDPSize: 65535,
|
||||
ReadTimeout: s.RTimeout,
|
||||
WriteTimeout: s.WTimeout}
|
||||
|
||||
go s.start(udpServer)
|
||||
go s.start(tcpServer)
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) start(ds *dns.Server) {
|
||||
log.Printf("Start %s listener on %s\n", ds.Net, s.Addr())
|
||||
|
||||
err := ds.ListenAndServe()
|
||||
if err != nil {
|
||||
log.Fatalf("Start %s listener on %s failed:%s\n", ds.Net, s.Addr(), err.Error())
|
||||
}
|
||||
|
||||
}
|
|
@ -3,29 +3,42 @@ module github.com/benjaminbear/docker-ddns-server/dyndns
|
|||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/caarlos0/env/v6 v6.9.3
|
||||
github.com/foolin/goview v0.3.0
|
||||
github.com/glebarez/sqlite v1.4.5
|
||||
github.com/go-playground/validator/v10 v10.11.0
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
github.com/labstack/echo/v4 v4.7.2
|
||||
github.com/labstack/gommon v0.3.1
|
||||
github.com/miekg/dns v1.1.49
|
||||
github.com/tg123/go-htpasswd v1.2.0
|
||||
gorm.io/gorm v1.23.5
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 // indirect
|
||||
github.com/glebarez/go-sqlite v1.17.2 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
|
||||
golang.org/x/net v0.0.0-20220524220425-1d687d428aca // indirect
|
||||
golang.org/x/mod v0.4.2 // indirect
|
||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93 // indirect
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
modernc.org/libc v1.16.8 // indirect
|
||||
modernc.org/mathutil v1.4.1 // indirect
|
||||
modernc.org/memory v1.1.1 // indirect
|
||||
modernc.org/sqlite v1.17.2 // indirect
|
||||
)
|
||||
|
|
109
dyndns/go.sum
109
dyndns/go.sum
|
@ -2,23 +2,24 @@ github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo
|
|||
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw=
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/caarlos0/env/v6 v6.9.3 h1:Tyg69hoVXDnpO5Qvpsu8EoquarbPyQb+YwExWHP8wWU=
|
||||
github.com/caarlos0/env/v6 v6.9.3/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/foolin/goview v0.3.0 h1:q5wKwXKEFb20dMRfYd59uj5qGCo7q4L9eVHHUjmMWrg=
|
||||
github.com/foolin/goview v0.3.0/go.mod h1:OC1VHC4FfpWymhShj8L1Tc3qipFmrmm+luAEdTvkos4=
|
||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||
github.com/glebarez/go-sqlite v1.17.2 h1:gyTyFr2RFFQd2gp6fOOdfnTvUn99zwvVOrQFHA4S+DY=
|
||||
github.com/glebarez/go-sqlite v1.17.2/go.mod h1:lakPjzvnJ6uSIARV+5dPALDuSLL3879PlzHFMEpbceM=
|
||||
github.com/glebarez/sqlite v1.4.5 h1:oaJupO4X9iTn4sXRvP5Vs15BNvKh9dx5AQfciKlDvV4=
|
||||
github.com/glebarez/sqlite v1.4.5/go.mod h1:6D+bB+DdXlEC4mO+pUFJWixVcnrHTIAJ9U6Ynnn4Lxk=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
|
@ -27,21 +28,20 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j
|
|||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
|
||||
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
|
||||
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
|
@ -57,32 +57,35 @@ github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o
|
|||
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
|
||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8=
|
||||
github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tg123/go-htpasswd v1.2.0 h1:UKp34m9H467/xklxUxU15wKRru7fwXoTojtxg25ITF0=
|
||||
github.com/tg123/go-htpasswd v1.2.0/go.mod h1:h7IzlfpvIWnVJhNZ0nQ9HaFxHb7pn5uFJYLlEUJa2sM=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
|
@ -91,43 +94,58 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
|
|||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220524220425-1d687d428aca h1:xTaFYiPROfpPhqrfTIDXj0ri1SpfueYT951s4bAuDO8=
|
||||
golang.org/x/net v0.0.0-20220524220425-1d687d428aca/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA=
|
||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190609082536-301114b31cce/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
|
@ -135,6 +153,15 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2Y
|
|||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
@ -145,3 +172,31 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/gorm v1.23.5 h1:TnlF26wScKSvknUC/Rn8t0NLLM22fypYBlvj1+aH6dM=
|
||||
gorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
|
||||
modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
|
||||
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
|
||||
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
|
||||
modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
|
||||
modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
|
||||
modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o=
|
||||
modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
|
||||
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
|
||||
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
|
||||
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.17.2 h1:TjmF36Wi5QcPYqRoAacV1cAyJ7xB/CD0ExpVUEMebnw=
|
||||
modernc.org/sqlite v1.17.2/go.mod h1:GOQmuiXd6pTTes1Fi2s9apiCcD/wbKQtBZ0Nw6/etjM=
|
||||
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
|
||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
|
||||
|
|
|
@ -1,200 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/labstack/gommon/log"
|
||||
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/model"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/tg123/go-htpasswd"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
DB *gorm.DB
|
||||
AuthAdmin bool
|
||||
Config Envs
|
||||
Title string
|
||||
DisableAdminAuth bool
|
||||
LastClearedLogs time.Time
|
||||
ClearInterval uint64
|
||||
AllowWildcard bool
|
||||
LogoutUrl string
|
||||
}
|
||||
|
||||
type Envs struct {
|
||||
AdminLogin string
|
||||
Domains []string
|
||||
}
|
||||
|
||||
type CustomValidator struct {
|
||||
Validator *validator.Validate
|
||||
}
|
||||
|
||||
// Validate implements the Validator.
|
||||
func (cv *CustomValidator) Validate(i interface{}) error {
|
||||
return cv.Validator.Struct(i)
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Authenticate is the method the website admin user and the host update user have to authenticate against.
|
||||
// To gather admin rights the username password combination must match with the credentials given by the env var.
|
||||
func (h *Handler) AuthenticateUpdate(username, password string, c echo.Context) (bool, error) {
|
||||
h.CheckClearInterval()
|
||||
reqParameter := c.QueryParam("hostname")
|
||||
reqArr := strings.SplitN(reqParameter, ".", 2)
|
||||
if len(reqArr) != 2 {
|
||||
log.Error("Error: Something wrong with the hostname parameter")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
host := &model.Host{}
|
||||
if err := h.DB.Where(&model.Host{UserName: username, Password: password, Hostname: reqArr[0], Domain: reqArr[1]}).First(host).Error; err != nil {
|
||||
log.Error("Error: ", err)
|
||||
return false, nil
|
||||
}
|
||||
if host.ID == 0 {
|
||||
log.Error("hostname or user user credentials unknown")
|
||||
return false, nil
|
||||
}
|
||||
c.Set("updateHost", host)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
func (h *Handler) AuthenticateAdmin(username, password string, c echo.Context) (bool, error) {
|
||||
h.AuthAdmin = false
|
||||
ok, err := h.authByEnv(username, password)
|
||||
if err != nil {
|
||||
log.Error("Error:", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if ok {
|
||||
h.AuthAdmin = true
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
func (h *Handler) authByEnv(username, password string) (bool, error) {
|
||||
hashReader := strings.NewReader(h.Config.AdminLogin)
|
||||
|
||||
pw, err := htpasswd.NewFromReader(hashReader, htpasswd.DefaultSystems, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if ok := pw.Match(username, password); ok {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// ParseEnvs parses all needed environment variables:
|
||||
// DDNS_ADMIN_LOGIN: The basic auth login string in htpasswd style.
|
||||
// DDNS_DOMAINS: All domains that will be handled by the dyndns server.
|
||||
func (h *Handler) ParseEnvs() (adminAuth bool, err error) {
|
||||
log.Info("Read environment variables")
|
||||
h.Config = Envs{}
|
||||
adminAuth = true
|
||||
h.Config.AdminLogin = os.Getenv("DDNS_ADMIN_LOGIN")
|
||||
if h.Config.AdminLogin == "" {
|
||||
log.Info("No Auth! DDNS_ADMIN_LOGIN should be set")
|
||||
adminAuth = false
|
||||
h.AuthAdmin = true
|
||||
h.DisableAdminAuth = true
|
||||
}
|
||||
var ok bool
|
||||
h.Title, ok = os.LookupEnv("DDNS_TITLE")
|
||||
if !ok {
|
||||
h.Title = "TheBBCloud DynDNS"
|
||||
}
|
||||
allowWildcard, ok := os.LookupEnv("DDNS_ALLOW_WILDCARD")
|
||||
if ok {
|
||||
h.AllowWildcard, err = strconv.ParseBool(allowWildcard)
|
||||
if err == nil {
|
||||
log.Info("Wildcard allowed")
|
||||
}
|
||||
}
|
||||
logoutUrl, ok := os.LookupEnv("DDNS_LOGOUT_URL")
|
||||
if ok {
|
||||
if len(logoutUrl) > 0 {
|
||||
log.Info("Logout url set: ", logoutUrl)
|
||||
h.LogoutUrl = logoutUrl
|
||||
}
|
||||
}
|
||||
|
||||
clearEnv := os.Getenv("DDNS_CLEAR_LOG_INTERVAL")
|
||||
clearInterval, err := strconv.ParseUint(clearEnv, 10, 32)
|
||||
if err != nil {
|
||||
log.Info("No log clear interval found")
|
||||
} else {
|
||||
log.Info("log clear interval found: ", clearInterval, "days")
|
||||
h.ClearInterval = clearInterval
|
||||
if clearInterval > 0 {
|
||||
h.LastClearedLogs = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
h.Config.Domains = strings.Split(os.Getenv("DDNS_DOMAINS"), ",")
|
||||
if len(h.Config.Domains) < 1 {
|
||||
return adminAuth, fmt.Errorf("environment variable DDNS_DOMAINS has to be set")
|
||||
}
|
||||
|
||||
return adminAuth, nil
|
||||
}
|
||||
|
||||
// InitDB creates an empty database and creates all tables if there isn't already one, or opens the existing one.
|
||||
func (h *Handler) InitDB() (err error) {
|
||||
if _, err := os.Stat("database"); os.IsNotExist(err) {
|
||||
err = os.MkdirAll("database", os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
h.DB, err = gorm.Open("sqlite3", "database/ddns.db")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !h.DB.HasTable(&model.Host{}) {
|
||||
h.DB.CreateTable(&model.Host{})
|
||||
}
|
||||
|
||||
if !h.DB.HasTable(&model.CName{}) {
|
||||
h.DB.CreateTable(&model.CName{})
|
||||
}
|
||||
|
||||
if !h.DB.HasTable(&model.Log{}) {
|
||||
h.DB.CreateTable(&model.Log{})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if a log cleaning is needed
|
||||
func (h *Handler) CheckClearInterval() {
|
||||
if !h.LastClearedLogs.IsZero() {
|
||||
if !DateEqual(time.Now(), h.LastClearedLogs) {
|
||||
go h.ClearLogs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compare two dates
|
||||
func DateEqual(date1, date2 time.Time) bool {
|
||||
y1, m1, d1 := date1.Date()
|
||||
y2, m2, d2 := date2.Date()
|
||||
return y1 == y2 && m1 == m2 && d1 == d2
|
||||
}
|
|
@ -1,20 +1,33 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/handler"
|
||||
"github.com/foolin/goview"
|
||||
"github.com/foolin/goview/supports/echoview-v4"
|
||||
"github.com/go-playground/validator/v10"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/labstack/gommon/log"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/db"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/dnsserver"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/config"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/webserver"
|
||||
|
||||
"github.com/foolin/goview"
|
||||
"github.com/foolin/goview/supports/echoview-v4"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/labstack/gommon/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Parse Config
|
||||
conf, err := config.ParseEnvs()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Set new instance
|
||||
e := echo.New()
|
||||
|
||||
|
@ -36,25 +49,20 @@ func main() {
|
|||
})
|
||||
|
||||
// Set Validator
|
||||
e.Validator = &handler.CustomValidator{Validator: validator.New()}
|
||||
e.Validator = &webserver.CustomValidator{Validator: validator.New()}
|
||||
|
||||
// Set Statics
|
||||
e.Static("/static", "static")
|
||||
|
||||
// Initialize handler
|
||||
h := &handler.Handler{}
|
||||
|
||||
// Database connection
|
||||
if err := h.InitDB(); err != nil {
|
||||
e.Logger.Fatal(err)
|
||||
}
|
||||
defer h.DB.Close()
|
||||
|
||||
authAdmin, err := h.ParseEnvs()
|
||||
dbConn, err := db.InitDB()
|
||||
if err != nil {
|
||||
e.Logger.Fatal(err)
|
||||
}
|
||||
|
||||
// Initialize webserver
|
||||
h := webserver.New(conf, dbConn)
|
||||
|
||||
// UI Routes
|
||||
groupPublic := e.Group("/")
|
||||
groupPublic.GET("*", func(c echo.Context) error {
|
||||
|
@ -62,7 +70,7 @@ func main() {
|
|||
return c.Redirect(301, "./admin/")
|
||||
})
|
||||
groupAdmin := e.Group("/admin")
|
||||
if authAdmin {
|
||||
if conf.AdminLogin != "" {
|
||||
groupAdmin.Use(middleware.BasicAuth(h.AuthenticateAdmin))
|
||||
}
|
||||
|
||||
|
@ -82,8 +90,8 @@ func main() {
|
|||
//redirect to logout
|
||||
groupAdmin.GET("/logout", func(c echo.Context) error {
|
||||
// either custom url
|
||||
if len(h.LogoutUrl) > 0 {
|
||||
return c.Redirect(302, h.LogoutUrl)
|
||||
if conf.LogoutUrl != "" {
|
||||
return c.Redirect(302, conf.LogoutUrl)
|
||||
}
|
||||
// or standard url
|
||||
return c.Redirect(302, "../")
|
||||
|
@ -108,12 +116,27 @@ func main() {
|
|||
|
||||
// health-check
|
||||
e.GET("/ping", func(c echo.Context) error {
|
||||
u := &handler.Error{
|
||||
u := &webserver.Error{
|
||||
Message: "OK",
|
||||
}
|
||||
return c.JSON(http.StatusOK, u)
|
||||
})
|
||||
|
||||
// Start DNS Server
|
||||
dnsServer := &dnsserver.Server{
|
||||
Host: "",
|
||||
Port: 53,
|
||||
RTimeout: 5 * time.Second,
|
||||
WTimeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
handler := &dnsserver.Handler{
|
||||
Config: conf,
|
||||
DB: dbConn,
|
||||
}
|
||||
|
||||
dnsServer.Run(handler)
|
||||
|
||||
// Start server
|
||||
e.Logger.Fatal(e.Start(":8080"))
|
||||
}
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
package nswrapper
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/labstack/gommon/log"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// UpdateRecord builds a nsupdate file and updates a record by executing it with nsupdate.
|
||||
func UpdateRecord(hostname string, target string, addrType string, zone string, ttl int, enableWildcard bool) error {
|
||||
log.Info(fmt.Sprintf("%s record update request: %s -> %s", addrType, hostname, target))
|
||||
|
||||
f, err := ioutil.TempFile(os.TempDir(), "dyndns")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer os.Remove(f.Name())
|
||||
w := bufio.NewWriter(f)
|
||||
|
||||
w.WriteString(fmt.Sprintf("server %s\n", "localhost"))
|
||||
w.WriteString(fmt.Sprintf("zone %s\n", zone))
|
||||
w.WriteString(fmt.Sprintf("update delete %s.%s %s\n", hostname, zone, addrType))
|
||||
if enableWildcard {
|
||||
w.WriteString(fmt.Sprintf("update delete %s.%s %s\n", "*."+hostname, zone, addrType))
|
||||
}
|
||||
w.WriteString(fmt.Sprintf("update add %s.%s %v %s %s\n", hostname, zone, ttl, addrType, target))
|
||||
if enableWildcard {
|
||||
w.WriteString(fmt.Sprintf("update add %s.%s %v %s %s\n", "*."+hostname, zone, ttl, addrType, target))
|
||||
}
|
||||
w.WriteString("send\n")
|
||||
|
||||
w.Flush()
|
||||
f.Close()
|
||||
|
||||
cmd := exec.Command("/usr/bin/nsupdate", f.Name())
|
||||
var out bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v: %v", err, stderr.String())
|
||||
}
|
||||
|
||||
if out.String() != "" {
|
||||
return fmt.Errorf(out.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRecord builds a nsupdate file and deletes a record by executing it with nsupdate.
|
||||
func DeleteRecord(hostname string, zone string, enableWildcard bool) error {
|
||||
fmt.Printf("record delete request: %s\n", hostname)
|
||||
|
||||
f, err := ioutil.TempFile(os.TempDir(), "dyndns")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer os.Remove(f.Name())
|
||||
w := bufio.NewWriter(f)
|
||||
|
||||
w.WriteString(fmt.Sprintf("server %s\n", "localhost"))
|
||||
w.WriteString(fmt.Sprintf("zone %s\n", zone))
|
||||
w.WriteString(fmt.Sprintf("update delete %s.%s\n", hostname, zone))
|
||||
if enableWildcard {
|
||||
w.WriteString(fmt.Sprintf("update delete %s.%s\n", "*."+hostname, zone))
|
||||
}
|
||||
w.WriteString("send\n")
|
||||
|
||||
w.Flush()
|
||||
f.Close()
|
||||
|
||||
cmd := exec.Command("/usr/bin/nsupdate", f.Name())
|
||||
var out bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v: %v", err, stderr.String())
|
||||
}
|
||||
|
||||
if out.String() != "" {
|
||||
return fmt.Errorf(out.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -34,14 +34,11 @@
|
|||
|
||||
<div class="btn-group" id="host_{{.ID}}" >
|
||||
<button id="{{.ID}}" class="editHost btn btn-outline-secondary btn-sm"><img
|
||||
src="/static/icons/pencil.svg" alt="" width="16" height="16" title="Edit"></button>
|
||||
<button
|
||||
id="{{.ID}}" class="deleteHost btn btn-outline-secondary btn-sm"><img
|
||||
src="/static/icons/trash.svg"
|
||||
alt="" width="16" height="16"
|
||||
title="Delete"></button>
|
||||
src="/static/icons/pencil.svg" alt="" width="16" height="16" title="Edit"></button>
|
||||
<button id="{{.ID}}" class="deleteHost btn btn-outline-secondary btn-sm"><img
|
||||
src="/static/icons/trash.svg" alt="" width="16" height="16" title="Delete"></button>
|
||||
<button id="{{.ID}}" class="showHostLog btn btn-outline-secondary btn-sm"><img
|
||||
src="/static/icons/table.svg" alt="" width="16" height="16" title="Logs"></button>
|
||||
src="/static/icons/table.svg" alt="" width="16" height="16" title="Logs"></button>
|
||||
<button id="{{.ID}}" class="copyUrlToClipboard btn btn-outline-secondary btn-sm"><img
|
||||
src="/static/icons/clipboard.svg" alt="" width="16" height="16" title="Copy URL to clipboard"></button>
|
||||
</div>
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
package handler
|
||||
package webserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/model"
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/nswrapper"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/db"
|
||||
"github.com/labstack/echo/v4"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ListCNames fetches all cnames from database and lists them on the website.
|
||||
|
@ -17,14 +15,14 @@ func (h *Handler) ListCNames(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusUnauthorized, &Error{UNAUTHORIZED})
|
||||
}
|
||||
|
||||
cnames := new([]model.CName)
|
||||
cnames := new([]db.CName)
|
||||
if err = h.DB.Preload("Target").Find(cnames).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "listcnames", echo.Map{
|
||||
"cnames": cnames,
|
||||
"title": h.Title,
|
||||
"title": h.Config.Title,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -35,7 +33,7 @@ func (h *Handler) AddCName(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusUnauthorized, &Error{UNAUTHORIZED})
|
||||
}
|
||||
|
||||
hosts := new([]model.Host)
|
||||
hosts := new([]db.Host)
|
||||
if err = h.DB.Find(hosts).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
@ -43,7 +41,7 @@ func (h *Handler) AddCName(c echo.Context) (err error) {
|
|||
return c.Render(http.StatusOK, "addcname", echo.Map{
|
||||
"config": h.Config,
|
||||
"hosts": hosts,
|
||||
"title": h.Title,
|
||||
"title": h.Config.Title,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -55,12 +53,12 @@ func (h *Handler) CreateCName(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusUnauthorized, &Error{UNAUTHORIZED})
|
||||
}
|
||||
|
||||
cname := &model.CName{}
|
||||
cname := &db.CName{}
|
||||
if err = c.Bind(cname); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
host := &model.Host{}
|
||||
host := &db.Host{}
|
||||
if err = h.DB.First(host, c.FormValue("target_id")).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
@ -79,10 +77,6 @@ func (h *Handler) CreateCName(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
if err = nswrapper.UpdateRecord(cname.Hostname, fmt.Sprintf("%s.%s", cname.Target.Hostname, cname.Target.Domain), "CNAME", cname.Target.Domain, cname.Ttl, h.AllowWildcard); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, cname)
|
||||
}
|
||||
|
||||
|
@ -98,7 +92,7 @@ func (h *Handler) DeleteCName(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
cname := &model.CName{}
|
||||
cname := &db.CName{}
|
||||
if err = h.DB.Preload("Target").First(cname, id).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
@ -114,9 +108,5 @@ func (h *Handler) DeleteCName(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
if err = nswrapper.DeleteRecord(cname.Hostname, cname.Target.Domain, h.AllowWildcard); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, id)
|
||||
}
|
117
dyndns/webserver/handler.go
Normal file
117
dyndns/webserver/handler.go
Normal file
|
@ -0,0 +1,117 @@
|
|||
package webserver
|
||||
|
||||
import (
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/config"
|
||||
"github.com/labstack/gommon/log"
|
||||
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/db"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/tg123/go-htpasswd"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
DB *gorm.DB
|
||||
AuthAdmin bool
|
||||
Config *config.Config
|
||||
LastClearedLogs time.Time
|
||||
}
|
||||
|
||||
type CustomValidator struct {
|
||||
Validator *validator.Validate
|
||||
}
|
||||
|
||||
// Validate implements the Validator.
|
||||
func (cv *CustomValidator) Validate(i interface{}) error {
|
||||
return cv.Validator.Struct(i)
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func New(config *config.Config, db *gorm.DB) *Handler {
|
||||
h := &Handler{Config: config, DB: db}
|
||||
|
||||
if config.AdminLogin == "" {
|
||||
h.AuthAdmin = true
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// Authenticate is the method the website admin user and the host update user have to authenticate against.
|
||||
// To gather admin rights the username password combination must match with the credentials given by the env var.
|
||||
func (h *Handler) AuthenticateUpdate(username, password string, c echo.Context) (bool, error) {
|
||||
h.CheckClearInterval()
|
||||
reqParameter := c.QueryParam("hostname")
|
||||
reqArr := strings.SplitN(reqParameter, ".", 2)
|
||||
if len(reqArr) != 2 {
|
||||
log.Error("Error: Something wrong with the hostname parameter")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
host := &db.Host{}
|
||||
if err := h.DB.Where(&db.Host{UserName: username, Password: password, Hostname: reqArr[0], Domain: reqArr[1]}).First(host).Error; err != nil {
|
||||
log.Error("Error: ", err)
|
||||
return false, nil
|
||||
}
|
||||
if host.ID == 0 {
|
||||
log.Error("hostname or user user credentials unknown")
|
||||
return false, nil
|
||||
}
|
||||
c.Set("updateHost", host)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (h *Handler) AuthenticateAdmin(username, password string, c echo.Context) (bool, error) {
|
||||
h.AuthAdmin = false
|
||||
ok, err := h.authByEnv(username, password)
|
||||
if err != nil {
|
||||
log.Error("Error:", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if ok {
|
||||
h.AuthAdmin = true
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (h *Handler) authByEnv(username, password string) (bool, error) {
|
||||
hashReader := strings.NewReader(h.Config.AdminLogin)
|
||||
|
||||
pw, err := htpasswd.NewFromReader(hashReader, htpasswd.DefaultSystems, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if ok := pw.Match(username, password); ok {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Check if a log cleaning is needed
|
||||
func (h *Handler) CheckClearInterval() {
|
||||
if h.Config.ClearLogInterval > 0 {
|
||||
if !equalDate(time.Now(), h.LastClearedLogs) {
|
||||
go h.ClearLogs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compare two dates
|
||||
func equalDate(date1, date2 time.Time) bool {
|
||||
y1, m1, d1 := date1.Date()
|
||||
y2, m2, d2 := date2.Date()
|
||||
return y1 == y2 && m1 == m2 && d1 == d2
|
||||
}
|
|
@ -1,18 +1,19 @@
|
|||
package handler
|
||||
package webserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
l "github.com/labstack/gommon/log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
l "github.com/labstack/gommon/log"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/nswrapper"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/model"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/db"
|
||||
"github.com/labstack/echo/v4"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -30,7 +31,7 @@ func (h *Handler) GetHost(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
host := &model.Host{}
|
||||
host := &db.Host{}
|
||||
if err = h.DB.First(host, id).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
@ -45,14 +46,14 @@ func (h *Handler) ListHosts(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusUnauthorized, &Error{UNAUTHORIZED})
|
||||
}
|
||||
|
||||
hosts := new([]model.Host)
|
||||
hosts := new([]db.Host)
|
||||
if err = h.DB.Find(hosts).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "listhosts", echo.Map{
|
||||
"hosts": hosts,
|
||||
"title": h.Title,
|
||||
"title": h.Config.Title,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -65,7 +66,7 @@ func (h *Handler) AddHost(c echo.Context) (err error) {
|
|||
return c.Render(http.StatusOK, "edithost", echo.Map{
|
||||
"addEdit": "add",
|
||||
"config": h.Config,
|
||||
"title": h.Title,
|
||||
"title": h.Config.Title,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -80,7 +81,7 @@ func (h *Handler) EditHost(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
host := &model.Host{}
|
||||
host := &db.Host{}
|
||||
if err = h.DB.First(host, id).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
@ -89,7 +90,7 @@ func (h *Handler) EditHost(c echo.Context) (err error) {
|
|||
"host": host,
|
||||
"addEdit": "edit",
|
||||
"config": h.Config,
|
||||
"title": h.Title,
|
||||
"title": h.Config.Title,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -101,7 +102,7 @@ func (h *Handler) CreateHost(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusUnauthorized, &Error{UNAUTHORIZED})
|
||||
}
|
||||
|
||||
host := &model.Host{}
|
||||
host := &db.Host{}
|
||||
if err = c.Bind(host); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
@ -118,18 +119,6 @@ func (h *Handler) CreateHost(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
// If a ip is set create dns entry
|
||||
if host.Ip != "" {
|
||||
ipType := nswrapper.GetIPType(host.Ip)
|
||||
if ipType == "" {
|
||||
return c.JSON(http.StatusBadRequest, &Error{fmt.Sprintf("ip %s is not a valid ip", host.Ip)})
|
||||
}
|
||||
|
||||
if err = nswrapper.UpdateRecord(host.Hostname, host.Ip, ipType, host.Domain, host.Ttl, h.AllowWildcard); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, host)
|
||||
}
|
||||
|
||||
|
@ -141,7 +130,7 @@ func (h *Handler) UpdateHost(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusUnauthorized, &Error{UNAUTHORIZED})
|
||||
}
|
||||
|
||||
hostUpdate := &model.Host{}
|
||||
hostUpdate := &db.Host{}
|
||||
if err = c.Bind(hostUpdate); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
@ -151,12 +140,11 @@ func (h *Handler) UpdateHost(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
host := &model.Host{}
|
||||
host := &db.Host{}
|
||||
if err = h.DB.First(host, id).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
forceRecordUpdate := host.UpdateHost(hostUpdate)
|
||||
if err = c.Validate(host); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
@ -165,18 +153,6 @@ func (h *Handler) UpdateHost(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
// If ip or ttl changed update dns entry
|
||||
if forceRecordUpdate {
|
||||
ipType := nswrapper.GetIPType(host.Ip)
|
||||
if ipType == "" {
|
||||
return c.JSON(http.StatusBadRequest, &Error{fmt.Sprintf("ip %s is not a valid ip", host.Ip)})
|
||||
}
|
||||
|
||||
if err = nswrapper.UpdateRecord(host.Hostname, host.Ip, ipType, host.Domain, host.Ttl, h.AllowWildcard); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, host)
|
||||
}
|
||||
|
||||
|
@ -192,7 +168,7 @@ func (h *Handler) DeleteHost(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
host := &model.Host{}
|
||||
host := &db.Host{}
|
||||
if err = h.DB.First(host, id).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
@ -202,11 +178,11 @@ func (h *Handler) DeleteHost(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
if err = tx.Where(&model.Log{HostID: uint(id)}).Delete(&model.Log{}).Error; err != nil {
|
||||
if err = tx.Where(&db.Log{HostID: uint(id)}).Delete(&db.Log{}).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
if err = tx.Where(&model.CName{TargetID: uint(id)}).Delete(&model.CName{}).Error; err != nil {
|
||||
if err = tx.Where(&db.CName{TargetID: uint(id)}).Delete(&db.CName{}).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
|
@ -216,10 +192,6 @@ func (h *Handler) DeleteHost(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
if err = nswrapper.DeleteRecord(host.Hostname, host.Domain, h.AllowWildcard); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, id)
|
||||
}
|
||||
|
||||
|
@ -227,12 +199,12 @@ func (h *Handler) DeleteHost(c echo.Context) (err error) {
|
|||
// Hostname, IP and senders IP are validated, a log entry is created
|
||||
// and finally if everything is ok, the DNS Server will be updated
|
||||
func (h *Handler) UpdateIP(c echo.Context) (err error) {
|
||||
host, ok := c.Get("updateHost").(*model.Host)
|
||||
host, ok := c.Get("updateHost").(*db.Host)
|
||||
if !ok {
|
||||
return c.String(http.StatusBadRequest, "badauth\n")
|
||||
}
|
||||
|
||||
log := &model.Log{Status: false, Host: *host, TimeStamp: time.Now(), UserAgent: nswrapper.ShrinkUserAgent(c.Request().UserAgent())}
|
||||
log := &db.Log{Status: false, Host: *host, TimeStamp: time.Now(), UserAgent: nswrapper.ShrinkUserAgent(c.Request().UserAgent())}
|
||||
log.SentIP = c.QueryParam(("myip"))
|
||||
|
||||
// Get caller IP
|
||||
|
@ -275,16 +247,6 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
// add/update DNS record
|
||||
if err = nswrapper.UpdateRecord(log.Host.Hostname, log.SentIP, ipType, log.Host.Domain, log.Host.Ttl, h.AllowWildcard); err != nil {
|
||||
log.Message = fmt.Sprintf("DNS error: %v", err)
|
||||
l.Error(log.Message)
|
||||
if err = h.CreateLogEntry(log); err != nil {
|
||||
l.Error(err)
|
||||
}
|
||||
return c.String(http.StatusBadRequest, "dnserr\n")
|
||||
}
|
||||
|
||||
log.Host.Ip = log.SentIP
|
||||
log.Host.LastUpdate = log.TimeStamp
|
||||
log.Status = true
|
||||
|
@ -297,8 +259,8 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
|
|||
}
|
||||
|
||||
func (h *Handler) checkUniqueHostname(hostname, domain string) error {
|
||||
hosts := new([]model.Host)
|
||||
if err := h.DB.Where(&model.Host{Hostname: hostname, Domain: domain}).Find(hosts).Error; err != nil {
|
||||
hosts := new([]db.Host)
|
||||
if err := h.DB.Where(&db.Host{Hostname: hostname, Domain: domain}).Find(hosts).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -306,8 +268,8 @@ func (h *Handler) checkUniqueHostname(hostname, domain string) error {
|
|||
return fmt.Errorf("hostname already exists")
|
||||
}
|
||||
|
||||
cnames := new([]model.CName)
|
||||
if err := h.DB.Preload("Target").Where(&model.CName{Hostname: hostname}).Find(cnames).Error; err != nil {
|
||||
cnames := new([]db.CName)
|
||||
if err := h.DB.Preload("Target").Where(&db.CName{Hostname: hostname}).Find(cnames).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
package handler
|
||||
package webserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/model"
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/db"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// CreateLogEntry simply adds a log entry to the database.
|
||||
func (h *Handler) CreateLogEntry(log *model.Log) (err error) {
|
||||
func (h *Handler) CreateLogEntry(log *db.Log) (err error) {
|
||||
if err = h.DB.Create(log).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -25,14 +26,14 @@ func (h *Handler) ShowLogs(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusUnauthorized, &Error{UNAUTHORIZED})
|
||||
}
|
||||
|
||||
logs := new([]model.Log)
|
||||
logs := new([]db.Log)
|
||||
if err = h.DB.Preload("Host").Limit(30).Order("created_at desc").Find(logs).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "listlogs", echo.Map{
|
||||
"logs": logs,
|
||||
"title": h.Title,
|
||||
"title": h.Config.Title,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -47,19 +48,19 @@ func (h *Handler) ShowHostLogs(c echo.Context) (err error) {
|
|||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
logs := new([]model.Log)
|
||||
if err = h.DB.Preload("Host").Where(&model.Log{HostID: uint(id)}).Order("created_at desc").Limit(30).Find(logs).Error; err != nil {
|
||||
logs := new([]db.Log)
|
||||
if err = h.DB.Preload("Host").Where(&db.Log{HostID: uint(id)}).Order("created_at desc").Limit(30).Find(logs).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "listlogs", echo.Map{
|
||||
"logs": logs,
|
||||
"title": h.Title,
|
||||
"title": h.Config.Title,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) ClearLogs() {
|
||||
var clearInterval = strconv.FormatUint(h.ClearInterval, 10) + " day"
|
||||
clearInterval := fmt.Sprintf("%d day", h.Config.ClearLogInterval)
|
||||
h.DB.Exec("DELETE FROM LOGS WHERE created_at < datetime('now', '-" + clearInterval + "');REINDEX LOGS;")
|
||||
h.LastClearedLogs = time.Now()
|
||||
log.Print("logs cleared")
|
Loading…
Add table
Reference in a new issue