Webserver

This commit is contained in:
2021-10-18 03:03:05 +02:00
parent 4c2a201622
commit aa90500616
6 changed files with 313 additions and 44 deletions

View File

@@ -1,18 +1,38 @@
package config
type Configuration struct {
DNS DNSConfiguration
HTTP HTTPConfiguration
}
type DNSConfiguration struct {
Port uint16
Host string
Domains []string
}
type HTTPConfiguration struct {
Port uint16
Host string
Socket string
SocketFileMode uint32
}
var (
Config Configuration
defaultConfig = Configuration{
Port: 5353,
Host: "0.0.0.0",
Domains: []string{},
DNS: DNSConfiguration{
Port: 5353,
Host: "0.0.0.0",
Domains: []string{},
},
HTTP: HTTPConfiguration{
Port: 8080,
Host: "127.0.0.1",
Socket: "",
SocketFileMode: 0644,
},
}
)

View File

@@ -1,6 +1,7 @@
package dns
import (
"errors"
"fmt"
"log"
"strconv"
@@ -11,11 +12,11 @@ import (
)
type domain struct {
root string
mutv4 sync.RWMutex
mutv6 sync.RWMutex
ipv4 map[string]string
ipv6 map[string]string
Root string
Mutv4 sync.RWMutex
Mutv6 sync.RWMutex
Ipv4 map[string]string
Ipv6 map[string]string
}
var domains = []*domain{}
@@ -25,9 +26,9 @@ func parseQuery(m *dns.Msg, currentDomain *domain) {
switch q.Qtype {
case dns.TypeA:
log.Printf("Query for A record of %s\n", q.Name)
currentDomain.mutv4.RLock()
ip := currentDomain.ipv4[q.Name]
currentDomain.mutv4.RUnlock()
currentDomain.Mutv4.RLock()
ip := currentDomain.Ipv4[q.Name]
currentDomain.Mutv4.RUnlock()
if ip != "" {
rr, err := dns.NewRR(fmt.Sprintf("%s A %s", q.Name, ip))
if err == nil {
@@ -36,9 +37,9 @@ func parseQuery(m *dns.Msg, currentDomain *domain) {
}
case dns.TypeAAAA:
log.Printf("Query for AAAA record of %s\n", q.Name)
currentDomain.mutv6.RLock()
ip := currentDomain.ipv6[q.Name]
currentDomain.mutv6.RUnlock()
currentDomain.Mutv6.RLock()
ip := currentDomain.Ipv6[q.Name]
currentDomain.Mutv6.RUnlock()
if ip != "" {
rr, err := dns.NewRR(fmt.Sprintf("%s AAAA %s", q.Name, ip))
if err == nil {
@@ -64,10 +65,13 @@ func handleDnsRequest(currentDomain *domain) func(w dns.ResponseWriter, r *dns.M
}
}
func Load() {
for _, currentDomain := range config.Config.Domains {
func Init() {
for _, currentDomain := range config.Config.DNS.Domains {
log.Printf("Added domain root: %s\n", currentDomain)
domains = append(domains, &domain{
root: currentDomain,
Root: currentDomain,
Ipv4: make(map[string]string),
Ipv6: make(map[string]string),
})
}
}
@@ -75,12 +79,12 @@ func Load() {
func Run() (*dns.Server, error) {
// attach request handler func
for _, currentDomain := range domains {
dns.HandleFunc(currentDomain.root, handleDnsRequest(currentDomain))
dns.HandleFunc(currentDomain.Root, handleDnsRequest(currentDomain))
}
// start server
server := &dns.Server{Addr: ":" + strconv.Itoa(int(config.Config.Port)), Net: "udp"}
log.Printf("Starting DNS at %d\n", config.Config.Port)
server := &dns.Server{Addr: ":" + strconv.Itoa(int(config.Config.DNS.Port)), Net: "udp"}
log.Printf("Starting DNS at %d\n", config.Config.DNS.Port)
err := server.ListenAndServe()
if err != nil {
server.Shutdown()
@@ -90,24 +94,34 @@ func Run() (*dns.Server, error) {
return server, nil
}
func UpdateIpv6(domain string, ipv6 string) {
func UpdateIpv6(domain string, ipv6 string) error {
for _, currentDomain := range domains {
if dns.IsSubDomain(currentDomain.root, domain) {
currentDomain.mutv6.Lock()
currentDomain.ipv6[domain] = ipv6
currentDomain.mutv6.Unlock()
break
if dns.IsSubDomain(currentDomain.Root, domain) {
log.Printf("Updating domain %s AAAA %s\n", domain, ipv6)
currentDomain.Mutv6.Lock()
currentDomain.Ipv6[domain] = ipv6
currentDomain.Mutv6.Unlock()
return nil
}
}
return errors.New("no root found")
}
func UpdateIpv4(domain string, ipv4 string) {
func UpdateIpv4(domain string, ipv4 string) (err error) {
for _, currentDomain := range domains {
if dns.IsSubDomain(currentDomain.root, domain) {
currentDomain.mutv4.Lock()
currentDomain.ipv4[domain] = ipv4
currentDomain.mutv4.Unlock()
break
if dns.IsSubDomain(currentDomain.Root, domain) {
log.Printf("Updating domain %s A %s\n", domain, ipv4)
currentDomain.Mutv4.Lock()
currentDomain.Ipv4[domain] = ipv4
currentDomain.Mutv4.Unlock()
return nil
}
}
return errors.New("no root found")
}
func Get() []*domain {
return domains
}

16
go.mod
View File

@@ -2,12 +2,20 @@ module github.com/MarekWojt/gertdns
go 1.17
require github.com/miekg/dns v1.1.43
require (
github.com/fasthttp/router v1.4.4
github.com/gookit/color v1.4.2
github.com/miekg/dns v1.1.43
github.com/pelletier/go-toml/v2 v2.0.0-beta.3
github.com/valyala/fasthttp v1.31.0
)
require (
github.com/gookit/color v1.4.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.0-beta.3 // indirect
github.com/andybalholm/brotli v1.0.2 // indirect
github.com/klauspost/compress v1.13.4 // indirect
github.com/savsgio/gotils v0.0.0-20210921075833-21a6215cb0e4 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect
golang.org/x/sys v0.0.0-20211015200801-69063c4bb744 // indirect
)

27
go.sum
View File

@@ -1,28 +1,51 @@
github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E=
github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fasthttp/router v1.4.4 h1:Z025tHFTjDp6T6QMBjloyGL6KV5wtakW365K/7KiE1c=
github.com/fasthttp/router v1.4.4/go.mod h1:TiyF2kc+mogKcTxqkhUbiXpwklouv5dN58A0ZUo8J6s=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/klauspost/compress v1.13.4 h1:0zhec2I8zGnjWcKyLl6i3gPqKANCCn5e9xmviEEeX6s=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/pelletier/go-toml/v2 v2.0.0-beta.3 h1:PNCTU4naEJ8mKal97P3A2qDU74QRQGlv4FXiL1XDqi4=
github.com/pelletier/go-toml/v2 v2.0.0-beta.3/go.mod h1:aNseLYu/uKskg0zpr/kbr2z8yGuWtotWf/0BpGIAL2Y=
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/savsgio/gotils v0.0.0-20210921075833-21a6215cb0e4 h1:ocK/D6lCgLji37Z2so4xhMl46se1ntReQQCUIU4BWI8=
github.com/savsgio/gotils v0.0.0-20210921075833-21a6215cb0e4/go.mod h1:oejLrk1Y/5zOF+c/aHtXqn3TFlzzbAgPWg8zBiAHDas=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942 h1:t0lM6y/M5IiUZyvbBTcngso8SZEZICH7is9B6g/obVU=
github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.31.0 h1:lrauRLII19afgCs2fnWRJ4M5IkV0lo2FqA61uGkNBfE=
github.com/valyala/fasthttp v1.31.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04 h1:cEhElsAv9LUt9ZUUocxzWe05oFLVd+AA2nstydTeI8g=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/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-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211015200801-69063c4bb744 h1:KzbpndAYEM+4oHRp9JmB2ewj0NHHxO3Z0g7Gus2O1kk=
golang.org/x/sys v0.0.0-20211015200801-69063c4bb744/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/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

52
main.go
View File

@@ -6,12 +6,19 @@ import (
"github.com/MarekWojt/gertdns/config"
"github.com/MarekWojt/gertdns/dns"
"github.com/MarekWojt/gertdns/web"
rootDns "github.com/miekg/dns"
)
var (
configFile = flag.String("configFile", "conf.toml", "Path to configuration file")
)
type dnsResult struct {
server *rootDns.Server
err error
}
func main() {
flag.Parse()
@@ -20,10 +27,43 @@ func main() {
log.Fatalf("Failed to load configuration: %s\n ", err.Error())
}
dns.Load()
server, err := dns.Run()
if err != nil {
log.Fatalf("Failed to start DNS server: %s\n ", err.Error())
}
defer server.Shutdown()
dns.Init()
web.Init()
webChan := make(chan error)
dnsChan := make(chan dnsResult)
go func() {
server, err := dns.Run()
if err != nil {
log.Fatalf("Failed to start DNS server: %s\n ", err.Error())
}
dnsChan <- dnsResult{
server: server,
err: err,
}
}()
go func() {
err := web.RunSocket()
if err != nil {
log.Fatalf("Failed to start HTTP socket: %s\n ", err.Error())
}
webChan <- err
}()
go func() {
err := web.RunHTTP()
if err != nil {
log.Fatalf("Failed to start HTTP server: %s\n ", err.Error())
}
webChan <- err
}()
currentDnsResult := <-dnsChan
defer currentDnsResult.server.Shutdown()
<-webChan
<-webChan
}

View File

@@ -1 +1,165 @@
package web
import (
"flag"
"fmt"
"io/fs"
"log"
"github.com/MarekWojt/gertdns/auth"
"github.com/MarekWojt/gertdns/config"
"github.com/MarekWojt/gertdns/dns"
"github.com/fasthttp/router"
"github.com/valyala/fasthttp"
)
var enableDebugMode *bool = flag.Bool("enable-debug-mode", false, "Enables debug mode, will output a list of ")
func index(ctx *fasthttp.RequestCtx) {
if !*enableDebugMode {
ctx.WriteString("Working")
} else {
domains := dns.Get()
ctx.SetContentType("text/html")
ctx.WriteString("<!DOCTYPE html><html><head><meta charset='utf-8'><title>gertdns</title></head><body><table><tr><th>Type</th><th>Domain</th><th>IP</th><th>Root domain</th></tr>")
for _, currentDomain := range domains {
currentDomain.Mutv4.RLock()
copiedIpv4s := currentDomain.Ipv4
currentDomain.Mutv4.RUnlock()
for name, ip := range copiedIpv4s {
ctx.WriteString(fmt.Sprintf("<tr><td>A</td><td>%s</td><td>%s</td><td>%s</td><tr>", name, ip, currentDomain.Root))
}
currentDomain.Mutv6.RLock()
copiedIpv6s := currentDomain.Ipv6
currentDomain.Mutv6.RUnlock()
for name, ip := range copiedIpv6s {
ctx.WriteString(fmt.Sprintf("<tr><td>AAAA</td><td>%s</td><td>%s</td><td>%s</td><tr>", name, ip, currentDomain.Root))
}
}
ctx.WriteString("</table></body></html>")
}
}
var ipv4Param = []byte("ipv4")
var ipv6Param = []byte("ipv6")
var userParam = []byte("user")
var passwordParam = []byte("password")
func updateV4(ctx *fasthttp.RequestCtx) {
domain := ctx.UserValue("domain").(string)
ipv4 := string(ctx.QueryArgs().PeekBytes(ipv4Param))
if ipv4 == "" {
ctx.WriteString("Missing ipv4 query parameter")
ctx.SetStatusCode(fasthttp.StatusBadRequest)
return
}
err := dns.UpdateIpv4(domain, ipv4)
if err != nil {
ctx.WriteString(err.Error())
ctx.SetStatusCode(fasthttp.StatusNotFound)
return
}
ctx.WriteString("OK")
}
func updateV6(ctx *fasthttp.RequestCtx) {
domain := ctx.UserValue("domain").(string)
ipv6 := string(ctx.QueryArgs().PeekBytes(ipv6Param))
if ipv6 == "" {
ctx.WriteString("Missing ipv6 query parameter")
ctx.SetStatusCode(fasthttp.StatusBadRequest)
return
}
err := dns.UpdateIpv6(domain, ipv6)
if err != nil {
ctx.WriteString(err.Error())
ctx.SetStatusCode(fasthttp.StatusNotFound)
return
}
ctx.WriteString("OK")
}
func authenticatedRequest(request func(ctx *fasthttp.RequestCtx)) func(ctx *fasthttp.RequestCtx) {
return func(ctx *fasthttp.RequestCtx) {
domain, ok := ctx.UserValue("domain").(string)
if !ok {
ctx.WriteString("Missing domain")
ctx.SetStatusCode(fasthttp.StatusBadRequest)
return
}
user := string(ctx.QueryArgs().PeekBytes(userParam))
if user == "" {
ctx.WriteString("Missing user query parameter")
ctx.SetStatusCode(fasthttp.StatusBadRequest)
return
}
password := string(ctx.QueryArgs().PeekBytes(passwordParam))
if user == "" {
ctx.WriteString("Missing password query parameter")
ctx.SetStatusCode(fasthttp.StatusBadRequest)
return
}
authRequest := auth.AuthenticationRequest{
Domain: domain,
User: user,
Password: password,
}
authenticated, err := auth.IsAuthenticated(authRequest)
if err != nil {
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
return
}
if !authenticated {
ctx.SetStatusCode(fasthttp.StatusForbidden)
return
}
request(ctx)
}
}
var r *router.Router
func Init() {
r = router.New()
r.GET("/", index)
r.GET("/update/{domain}/v4", authenticatedRequest(updateV4))
r.GET("/update/{domain}/v6", authenticatedRequest(updateV6))
}
func RunSocket() error {
httpConfig := config.Config.HTTP
if httpConfig.Socket != "" {
log.Printf("Starting HTTP socket in %s with permission %d\n", httpConfig.Socket, httpConfig.SocketFileMode)
err := fasthttp.ListenAndServeUNIX(httpConfig.Socket, fs.FileMode(httpConfig.SocketFileMode), r.Handler)
if err != nil {
return err
}
}
return nil
}
func RunHTTP() error {
httpConfig := config.Config.HTTP
if httpConfig.Host != "" {
log.Printf("Starting HTTP server on %s:%d\n", httpConfig.Host, httpConfig.Port)
err := fasthttp.ListenAndServe(fmt.Sprintf("%s:%d", httpConfig.Host, httpConfig.Port), r.Handler)
if err != nil {
return err
}
}
return nil
}