This commit is contained in:
Mael G. 2020-12-12 23:01:04 -04:00
commit 36ce33e8f1
13 changed files with 708 additions and 0 deletions

16
Makefile Normal file
View file

@ -0,0 +1,16 @@
hello:
echo "Lighthouse"
build:
go build -o bin/lighthouse
run:
go run main.go
compile:
echo "Compiling for every OS and Platform"
GOOS=linux GOARCH=arm go build -o bin/lighthouse-linux-arm main.go
GOOS=linux GOARCH=arm64 go build -o bin/lighthouse-linux-arm64 main.go
GOOS=linux GOARCH=amd64 go build -o bin/lighthouse-linux-arm64 main.go
all: hello build

23
README.md Normal file
View file

@ -0,0 +1,23 @@
# LightHouse
LightHouse is a DNS authoritative nameserver made in Go with ``github.com/miekg/dns`` library.
Records are stored in a MySQL Database and cached using REDIS.
This software is currently in development and NOT ready for production.
## What is working
- Read records (stricts & wildcard) from MySQL
- Read and write records (stricts & wildcard) in REDIS
- Recursive wildcard for reverse DNS (IPv6 only)
- Generate dynamic reverse DNS (IPv6 only)
- Respond to all requested MySQL queries
## ToDo
- Recursive wildcard for reverse DNS (IPv4 part)
- Generate dynamic reverse DNS (IPv4 part)
- XFR
- DNSSEC
- Unit tests
- CI with auto packaging
- Optimization

20
core/handleDnsRequest.go Normal file
View file

@ -0,0 +1,20 @@
package core
import "github.com/miekg/dns"
//Handle the DNS request
func HandleDnsRequest(w dns.ResponseWriter, r *dns.Msg) {
//dns.Msg object
//Will be passed to the parseQuery() function
m := new(dns.Msg)
m.SetReply(r)
m.Compress = true //Less CPU usage (?)
switch r.Opcode { //Only respond to dns queries
case dns.OpcodeQuery:
parseQuery(m)
}
w.WriteMsg(m) //Write the DNS response
}

40
core/parseQuery.go Normal file
View file

@ -0,0 +1,40 @@
package core
import (
"fmt"
"github.com/miekg/dns"
"github.com/outout14/lighthouse/utils"
"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus"
)
/*
Qtype memo
A = 1
NS = 2
PTR = 12
TXT = 16
AAAA = 28
*/
//Function called by handleDnsRequest to parse the query from records
func parseQuery(m *dns.Msg) {
for _, q := range m.Question {
log.Infof("DNS : Query for %s (type : %v)\n", q.Name, q.Qtype) //Log
record := utils.GetRecord(utils.Record{Fqdn: q.Name, Qtype: q.Qtype})
if record.Content != "" { //If the record is found, return it
log.Infof("DNS : Record found for '%s' => '%s'\n", q.Name, record.Content)
rr, err := dns.NewRR(fmt.Sprintf("%s %v %s %s", q.Name, record.TTL, dns.TypeToString[q.Qtype], record.Content)) //Create the response
if err == nil { //If no err
m.Answer = append(m.Answer, rr)
}
} else {
logrus.Debugf("DNS : No record for '%s' (type '%v')\n", record.Fqdn, record.Qtype)
}
}
}

17
go.mod Normal file
View file

@ -0,0 +1,17 @@
module github.com/outout14/lighthouse
go 1.15
require (
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/go-redis/redis v6.15.9+incompatible
github.com/go-redis/redis/v8 v8.4.2
github.com/go-sql-driver/mysql v1.5.0
github.com/mattn/go-colorable v0.1.8
github.com/miekg/dns v1.1.35
github.com/sirupsen/logrus v1.7.0
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/snowzach/rotatefilehook v0.0.0-20180327172521-2f64f265f58c
gopkg.in/ini.v1 v1.62.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
)

107
go.sum Normal file
View file

@ -0,0 +1,107 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v8 v8.4.2 h1:gKRo1KZ+O3kXRfxeRblV5Tr470d2YJZJVIAv2/S8960=
github.com/go-redis/redis/v8 v8.4.2/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M=
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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/snowzach/rotatefilehook v0.0.0-20180327172521-2f64f265f58c h1:iUEy7/LRto3JqR/GLXDTEFP+s+qIjWw4pM8yzMfXC9A=
github.com/snowzach/rotatefilehook v0.0.0-20180327172521-2f64f265f58c/go.mod h1:ZLVe3VfhAuMYLYWliGEydMBoRnfib8EFSqkBYu1ck9E=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.opentelemetry.io/otel v0.14.0 h1:YFBEfjCk9MTjaytCNSUkp9Q8lF7QJezA06T71FbQxLQ=
go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
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=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

45
main.go Normal file
View file

@ -0,0 +1,45 @@
package main
import (
"database/sql"
"strconv"
"github.com/go-redis/redis"
"github.com/miekg/dns"
"github.com/outout14/lighthouse/core"
"github.com/outout14/lighthouse/utils"
"github.com/sirupsen/logrus"
"gopkg.in/ini.v1"
)
//Global vars
var conf *utils.Conf
var DB *sql.DB
var redisDb *redis.Client
//Main loop
func main() {
//Load Configuration
conf = new(utils.Conf)
err := ini.MapTo(conf, "./config.ini")
utils.CheckErr(err)
utils.InitLogger(conf)
// attach request handler func
dns.HandleFunc(".", core.HandleDnsRequest)
//Init redis database
utils.RedisDatabase(conf)
//Init sql database
utils.SqlDatabase(conf)
// start server
server := &dns.Server{Addr: conf.App.Ip + strconv.Itoa(conf.App.Port), Net: "udp"} //define the server
logrus.WithFields(logrus.Fields{"ip": conf.App.Ip, "port": conf.App.Port}).Infof("SERVER : Started")
err = server.ListenAndServe() //start it
utils.CheckErr(err)
defer server.Shutdown() //shut down on application closing
}

77
utils/queries.go Normal file
View file

@ -0,0 +1,77 @@
package utils
import (
"fmt"
"strings"
"github.com/go-redis/redis/v8"
"github.com/sirupsen/logrus"
)
func getDomain(fqdn string) *Domain {
domain := new(Domain)
err := DB.QueryRow(
"SELECT id, friendly_name, fqdn, owner_id, last_edit FROM lighthouse.domains WHERE `fqdn` = ?;", fqdn).Scan(
&domain.ID,
&domain.FriendlyName,
&domain.Fqdn,
&domain.OwnerId,
&domain.LastEdit,
)
print(err)
return domain
}
func GetRecord(entry Record) Record {
//Check for strict record in Redis cache
redisKey := entry.Fqdn + "--" + fmt.Sprint(entry.Qtype)
result, redisErr := redisCheckForRecord(redisKey, entry)
var sqlErr int
//If reverse DNS
reverseCheck := IsReverse(entry.Fqdn)
if reverseCheck > 0 {
if redisErr == redis.Nil {
logrus.Debug("QUERIES : Check for strict reverse in MySQL")
result, sqlErr = sqlCheckForRecord(redisKey, entry.Fqdn, entry)
if sqlErr == 1 {
logrus.Debug("QUERIES : Check for wildcard reverse in MySQL")
result, redisErr = sqlCheckForReverse6Wildcard(redisKey, entry.Fqdn, entry)
}
}
//For dynamic reverse dns
if strings.Contains(result.Content, "%s") {
record := ExtractAddressFromReverse(entry.Fqdn)
var recordFormated string
if reverseCheck == 1 {
recordFormated = strings.Replace(record, ".", "-", -1)
} else {
recordFormated = strings.Replace(record, ":", "-", -1)
}
result.Content = fmt.Sprintf(result.Content, recordFormated)
}
} else if redisErr == redis.Nil { //If strict record NOT in Redis cache & not Reverse
//Check for wildcard in Redis cache
logrus.Debug("QUERIES : Check for wildcard in redis cache")
mainDomainKey := fmt.Sprintf("*%s", entry.Fqdn[strings.Index(entry.Fqdn, "."):]) //Remove the last subdomain
redismdKey := fmt.Sprintf("%s--%v", mainDomainKey, entry.Qtype)
result, redisErr = redisCheckForRecord(redismdKey, entry)
//If none of both check in mysql
if redisErr == redis.Nil {
//Check for strict record in mysql
logrus.Debug("QUERIES : Check for strict record in MSQL")
result, sqlErr = sqlCheckForRecord(redisKey, entry.Fqdn, entry)
if sqlErr == 1 {
//Check for wildcard record in mysql
logrus.Debug("QUERIES : Check for wildcard in MSQL")
result, sqlErr = sqlCheckForRecord(redismdKey, fmt.Sprint(mainDomainKey), entry)
}
}
}
return result
}

68
utils/redis.go Normal file
View file

@ -0,0 +1,68 @@
package utils
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/go-redis/redis/v8"
"github.com/sirupsen/logrus"
)
var ctx = context.Background()
var redisDb *redis.Client
func RedisDatabase(conf *Conf) *redis.Client {
logrus.WithFields(logrus.Fields{"ip": conf.Redis.Ip, "port": conf.Redis.Port}).Infof("REDIS : Connection to DB")
rdb := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%v", conf.Redis.Ip, conf.Redis.Port),
Password: conf.Redis.Password,
DB: conf.Redis.Db,
})
//Test Redis connection
err := rdb.Set(ctx, "alive", 1, 0).Err()
alive, err := rdb.Get(ctx, "alive").Result()
CheckErr(err)
if alive != "1" {
logrus.WithFields(logrus.Fields{"alive": alive}).Panic("REDIS : Test not passed. alive != 1")
}
CheckErr(err)
logrus.WithFields(logrus.Fields{"db": conf.Redis.Db}).Info("REDIS : Successfull connection")
redisDb = rdb
return rdb
}
func redisCheckForRecord(redisKey string, entry Record) (Record, error) {
val, err := redisDb.Get(ctx, redisKey).Result()
//If Record in Redis cache
if err == nil {
err := json.Unmarshal([]byte(val), &entry)
logrus.Debugf("REDIS : %s => %s", redisKey, entry.Content)
return entry, err
} else {
//Else return nil
return entry, redis.Nil
}
}
func redisSet(c *redis.Client, key string, ttl time.Duration, value interface{}) error {
p, err := json.Marshal(value)
if err != nil {
return err
}
return c.Set(ctx, key, p, ttl).Err()
}
func redisGet(c *redis.Client, key string, dest interface{}) error {
p, err := c.Get(ctx, key).Result()
if err != nil {
return err
}
return json.Unmarshal([]byte(p), dest)
}

89
utils/reverse.go Normal file
View file

@ -0,0 +1,89 @@
package utils
import (
"net"
"strings"
"github.com/sirupsen/logrus"
)
// Based on github.com/coredns/coredns/plugin/pkg/dnsutil/reverse.go
// IsReverse returns 0 is name is not in a reverse zone. Anything > 0 indicates
// name is in a reverse zone. The returned integer will be 1 for in-addr.arpa. (IPv4)
// and 2 for ip6.arpa. (IPv6).
func IsReverse(name string) int {
if strings.HasSuffix(name, IP4arpa) {
return 1
}
if strings.HasSuffix(name, IP6arpa) {
return 2
}
logrus.Debug("REVERSE : Not Reverse")
return 0
}
// ExtractAddressFromReverse turns a standard PTR reverse record name
// into an IP address. This works for ipv4 or ipv6.
//
// 54.119.58.176.in-addr.arpa. becomes 176.58.119.54. If the conversion
// fails the empty string is returned.
func ExtractAddressFromReverse(reverseName string) string {
search := ""
f := reverse
switch {
case strings.HasSuffix(reverseName, IP4arpa):
search = strings.TrimSuffix(reverseName, IP4arpa)
case strings.HasSuffix(reverseName, IP6arpa):
search = strings.TrimSuffix(reverseName, IP6arpa)
f = reverse6
default:
logrus.Debug("REVERSE : ExtractAddressFromReverse : No result")
return ""
}
// Reverse the segments and then combine them.
return f(strings.Split(search, "."))
}
func reverse(slice []string) string {
for i := 0; i < len(slice)/2; i++ {
j := len(slice) - i - 1
slice[i], slice[j] = slice[j], slice[i]
}
ip := net.ParseIP(strings.Join(slice, ".")).To4()
if ip == nil {
logrus.Debug("REVERSE : reverse : No result")
logrus.Debug(ip)
return ""
}
return ip.String()
}
// reverse6 reverse the segments and combine them according to RFC3596:
// b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2
// is reversed to 2001:db8::567:89ab
func reverse6(slice []string) string {
for i := 0; i < len(slice)/2; i++ {
j := len(slice) - i - 1
slice[i], slice[j] = slice[j], slice[i]
}
slice6 := []string{}
for i := 0; i < len(slice)/4; i++ {
slice6 = append(slice6, strings.Join(slice[i*4:i*4+4], ""))
}
ip := net.ParseIP(strings.Join(slice6, ":")).To16()
if ip == nil {
return ""
}
return ip.String()
}
const (
// IP4arpa is the reverse tree suffix for v4 IP addresses.
IP4arpa = ".in-addr.arpa."
// IP6arpa is the reverse tree suffix for v6 IP addresses.
IP6arpa = ".ip6.arpa."
)

87
utils/sql.go Normal file
View file

@ -0,0 +1,87 @@
package utils
import (
"database/sql"
"fmt"
"time"
"github.com/go-redis/redis"
_ "github.com/go-sql-driver/mysql"
"github.com/sirupsen/logrus"
)
var DB *sql.DB
func SqlDatabase(conf *Conf) {
logrus.WithFields(logrus.Fields{"database": conf.Database.Db}).Infof("SQL : Connection to DB")
//db conn
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", conf.Database.Username, conf.Database.Password, conf.Database.Ip, conf.Database.Port, conf.Database.Db))
CheckErr(err)
DB = db
// if there is an error opening the connection, handle it
CheckErr(err)
}
func SqlTest() {
results, err := DB.Query("SELECT name, content FROM records")
CheckErr(err)
for results.Next() {
var record Record
err = results.Scan(&record.Fqdn, &record.Content)
CheckErr(err)
logrus.Debugf(record.Content)
}
}
func sqlCheckForRecord(redisKey string, dKey string, entry Record) (Record, int) {
dbg := DB.QueryRow(
"SELECT id, content, ttl FROM lighthouse.records WHERE `name` = ? AND `type` = ?;", dKey, entry.Qtype).Scan(
&entry.Id,
&entry.Content,
&entry.TTL,
)
//logrus.WithFields(logrus.Fields{"name": dKey, "type": entry.Qtype}).Debugf("SQL : ")
if dbg != nil {
logrus.Debugf("SQL : %v", dbg)
}
logrus.Debugf("SQL : %s => %s", entry.Fqdn, entry.Content)
if entry.Content != "" {
//Cache the request in Redis if any result
logrus.Debugf("REDIS : Set entry for %s", redisKey)
logrus.Warningf("REDIS : %s", redisKey)
_ = redisSet(redisDb, redisKey, 30*time.Second, entry)
return entry, 0
} else {
//Else return nil
return entry, 1
}
}
func sqlCheckForReverse6Wildcard(redisKey string, dKey string, entry Record) (Record, error) {
returnedEntry := entry
results, err := DB.Query("SELECT id, content, name FROM lighthouse.records WHERE name LIKE '*%.ip6.arpa.';")
for results.Next() {
err = results.Scan(&returnedEntry.Id, &returnedEntry.Content, &returnedEntry.Fqdn)
CheckErr(err)
if checkReverse6(entry, returnedEntry) {
logrus.Debug("REVERSE : Correct wildcard reverse.")
//Cache the request in Redis if any result
_ = redisSet(redisDb, redisKey, 10*time.Second, returnedEntry)
return returnedEntry, err
} else {
logrus.Debug("REVERSE : WRONG wildcard reverse .")
}
}
return entry, redis.Nil
}

50
utils/structs.go Normal file
View file

@ -0,0 +1,50 @@
package utils
//Structs for configuration
type App struct {
Port int
Ip string
Logdir string
Logfile bool
}
type Database struct {
Ip string
Port string
Username string
Password string
Db string
}
type Redis struct {
Ip string
Port int
Password string
Db int
Ttl int
}
type Conf struct {
App_mode string
App
Database
Redis
}
type Domain struct {
ID int `json:"id"`
FriendlyName string
Fqdn string
OwnerId int
LastEdit string
}
type Record struct {
Id int
DomainId int
Fqdn string
Content string
Type int
Qtype uint16
TTL int
}

69
utils/utils.go Normal file
View file

@ -0,0 +1,69 @@
package utils
import (
"strings"
"time"
"github.com/mattn/go-colorable"
"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus"
"github.com/snowzach/rotatefilehook"
)
func CheckErr(err error) {
if err != nil {
log.Fatalf("%s\n ", err.Error())
panic(err)
}
}
func InitLogger(conf *Conf) {
var logLevel = logrus.InfoLevel
if conf.App_mode != "production" {
logLevel = logrus.DebugLevel
}
rotateFileHook, err := rotatefilehook.NewRotateFileHook(rotatefilehook.RotateFileConfig{
Filename: conf.App.Logdir + "/console.log",
MaxSize: 50, // megabytes
MaxBackups: 3,
MaxAge: 28, //days
Level: logLevel,
Formatter: &logrus.TextFormatter{
DisableColors: true,
TimestampFormat: "2006-01-02 15:04:05",
FullTimestamp: true,
},
})
if err != nil {
logrus.Fatalf("Failed to initialize file rotate hook: %v", err)
}
logrus.SetLevel(logLevel)
logrus.SetOutput(colorable.NewColorableStdout())
logrus.SetFormatter(&logrus.TextFormatter{
ForceColors: false,
FullTimestamp: true,
TimestampFormat: time.RFC822,
})
if conf.App.Logfile {
logrus.AddHook(rotateFileHook)
}
log.WithFields(log.Fields{"app_mode": conf.App_mode}).Info("Application mode")
log.WithFields(log.Fields{"logLevel": logLevel}).Debug("Log level")
}
func checkReverse6(entry Record, result Record) bool {
check := strings.Replace(entry.Fqdn, result.Fqdn[1:], "", 1)
logrus.WithFields(logrus.Fields{"entry": entry.Fqdn, "result": result.Fqdn[1:]}).Debug("REVERSE checkReverse6 :")
logrus.Debugf("REVERSE checkReverse6 : %s", check)
if strings.Contains(check, IP6arpa) {
return false
} else {
return true
}
}