Init
This commit is contained in:
commit
36ce33e8f1
13 changed files with 708 additions and 0 deletions
16
Makefile
Normal file
16
Makefile
Normal 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
23
README.md
Normal 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
20
core/handleDnsRequest.go
Normal 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
40
core/parseQuery.go
Normal 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
17
go.mod
Normal 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
107
go.sum
Normal 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
45
main.go
Normal 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
77
utils/queries.go
Normal 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
68
utils/redis.go
Normal 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
89
utils/reverse.go
Normal 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
87
utils/sql.go
Normal 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
50
utils/structs.go
Normal 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
69
utils/utils.go
Normal 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
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue