154 lines
3.6 KiB
Go
154 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"github.com/miekg/dns"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
listen_addr = flag.String("addr", "0.0.0.0:8053", "ip:port")
|
|
zone_file = flag.String("zone", "zone.db", "zone file")
|
|
)
|
|
|
|
var zone = map[string]map[uint16][]dns.RR{}
|
|
|
|
func serve_dns(w dns.ResponseWriter, r *dns.Msg) {
|
|
msg := dns.Msg{}
|
|
msg.SetReply(r)
|
|
msg.Authoritative = true
|
|
domain := msg.Question[0].Name
|
|
println(" -> ", msg.Question[0].String(), domain)
|
|
|
|
if _, zok := zone[domain]; zok { // serve zone file
|
|
if rr_resp, rok := zone[domain][msg.Question[0].Qtype]; rok {
|
|
msg.Answer = rr_resp
|
|
w.WriteMsg(&msg)
|
|
return
|
|
}
|
|
}
|
|
|
|
start := strings.SplitN(domain, ".", 2)[0]
|
|
if start == "reload-zone" { // reload hook
|
|
msg.Answer = []dns.RR{&dns.TXT{
|
|
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 60},
|
|
Txt: []string{"reloaded"},
|
|
}}
|
|
reload_zone()
|
|
w.WriteMsg(&msg)
|
|
return
|
|
}
|
|
|
|
if start == "time" { // return local time
|
|
switch r.Question[0].Qtype {
|
|
case dns.TypeA:
|
|
msg.Answer = []dns.RR{&dns.A{
|
|
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60},
|
|
A: time_ip(),
|
|
}}
|
|
case dns.TypeTXT:
|
|
msg.Answer = []dns.RR{&dns.TXT{
|
|
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 60},
|
|
Txt: []string{time.Now().Format("15h04")},
|
|
}}
|
|
|
|
}
|
|
w.WriteMsg(&msg)
|
|
return
|
|
}
|
|
|
|
switch r.Question[0].Qtype {
|
|
case dns.TypeA:
|
|
// sub.domains.192.168.1.1.xip.sub.domain.tld returns 192.168.1.1
|
|
parts := strings.Split(domain, ".")
|
|
xindex := -1
|
|
for i := 0; i < len(parts); i++ {
|
|
if parts[i] == "nip" {
|
|
xindex = i
|
|
}
|
|
}
|
|
if xindex-4 >= 0 {
|
|
ip0, er0 := strconv.ParseInt(parts[xindex-1], 10, 16)
|
|
ip1, er1 := strconv.ParseInt(parts[xindex-2], 10, 16)
|
|
ip2, er2 := strconv.ParseInt(parts[xindex-3], 10, 16)
|
|
ip3, er3 := strconv.ParseInt(parts[xindex-4], 10, 16)
|
|
if er0 != nil || er1 != nil || er2 != nil || er3 != nil {
|
|
msg.SetRcode(r, dns.RcodeServerFailure)
|
|
}
|
|
ip := net.IPv4(byte(ip3), byte(ip2), byte(ip1), byte(ip0))
|
|
|
|
msg.Answer = append(msg.Answer, &dns.A{
|
|
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60},
|
|
A: ip,
|
|
})
|
|
} else {
|
|
msg.SetRcode(r, dns.RcodeServerFailure)
|
|
}
|
|
|
|
case dns.TypeTXT:
|
|
msg.Answer = append(msg.Answer, &dns.TXT{
|
|
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 60},
|
|
Txt: []string{"hey texte dynamique: " + domain, "lol"},
|
|
})
|
|
}
|
|
w.WriteMsg(&msg)
|
|
|
|
}
|
|
|
|
func time_ip() net.IP {
|
|
now := time.Now()
|
|
return net.IPv4(byte(now.Hour()), byte(now.Minute()), byte(now.Second()), 0)
|
|
}
|
|
|
|
func parsezone() {
|
|
f, ferr := os.Open(*zone_file)
|
|
if ferr != nil {
|
|
println("failed to open file", ferr)
|
|
return
|
|
} else {
|
|
defer f.Close()
|
|
}
|
|
zp := dns.NewZoneParser(f, ".", "zone.db")
|
|
//zp := dns.NewZoneParser(f, ".", *zone_file)
|
|
ok := true
|
|
var rr dns.RR
|
|
for ok {
|
|
rr, ok = zp.Next()
|
|
if ok {
|
|
hdr := rr.Header()
|
|
if _, ok := zone[hdr.Name]; ok {
|
|
zone[hdr.Name][hdr.Rrtype] = append(zone[hdr.Name][hdr.Rrtype], rr)
|
|
} else {
|
|
zone[hdr.Name] = map[uint16][]dns.RR{}
|
|
zone[hdr.Name][hdr.Rrtype] = []dns.RR{rr}
|
|
}
|
|
println(rr.String())
|
|
}
|
|
}
|
|
if er := zp.Err(); er != nil {
|
|
println(er.Error())
|
|
}
|
|
}
|
|
|
|
func reload_zone() {
|
|
defer func() { recover() }()
|
|
zone = map[string]map[uint16][]dns.RR{}
|
|
parsezone()
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
parsezone()
|
|
dns.HandleFunc(".", serve_dns)
|
|
srv := &dns.Server{Addr: *listen_addr, Net: "udp"}
|
|
if err := srv.ListenAndServe(); err != nil {
|
|
log.Fatalf("Failed to set udp listener %s\n", err.Error())
|
|
}
|
|
|
|
}
|