You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ncdns/server/server.go

202 lines
5.4 KiB
Go

10 years ago
package server
10 years ago
9 years ago
import "gopkg.in/hlandau/madns.v1"
10 years ago
import "github.com/hlandau/ncdns/backend"
10 years ago
import "github.com/hlandau/ncdns/namecoin"
10 years ago
import "github.com/miekg/dns"
import "os"
10 years ago
import "net"
10 years ago
import "fmt"
import "sync"
10 years ago
import "strings"
10 years ago
import "path/filepath"
import "crypto"
9 years ago
import "github.com/hlandau/xlog"
10 years ago
const version = "1.0"
9 years ago
var log, Log = xlog.New("ncdns.server")
10 years ago
type Server struct {
10 years ago
cfg ServerConfig
10 years ago
10 years ago
engine madns.Engine
namecoinConn namecoin.Conn
10 years ago
10 years ago
mux *dns.ServeMux
udpListener *dns.Server
tcpListener *dns.Server
wgStart sync.WaitGroup
10 years ago
}
type ServerConfig struct {
10 years ago
Bind string `default:":53" usage:"Address to bind to (e.g. 0.0.0.0:53)"`
10 years ago
PublicKey string `default:"" usage:"Path to the DNSKEY KSK public key file"`
PrivateKey string `default:"" usage:"Path to the KSK's corresponding private key file"`
10 years ago
ZonePublicKey string `default:"" usage:"Path to the DNSKEY ZSK public key file; if one is not specified, a temporary one is generated on startup and used only for the duration of that process"`
ZonePrivateKey string `default:"" usage:"Path to the ZSK's corresponding private key file"`
NamecoinRPCUsername string `default:"" usage:"Namecoin RPC username"`
NamecoinRPCPassword string `default:"" usage:"Namecoin RPC password"`
NamecoinRPCAddress string `default:"localhost:8336" usage:"Namecoin RPC server address"`
10 years ago
CacheMaxEntries int `default:"100" usage:"Maximum name cache entries"`
10 years ago
SelfName string `default:"" usage:"The FQDN of this nameserver. If empty, a psuedo-hostname is generated."`
10 years ago
SelfIP string `default:"127.127.127.127" usage:"The canonical IP address for this service"`
10 years ago
HTTPListenAddr string `default:"" usage:"Address for webserver to listen at (default: disabled)"`
CanonicalSuffix string `default:"bit" usage:"Suffix to advertise via HTTP"`
CanonicalNameservers string `default:"" usage:"Comma-separated list of nameservers to use for NS records. If blank, SelfName (or autogenerated psuedo-hostname) is used."`
canonicalNameservers []string
Hostmaster string `default:"" usage:"Hostmaster e. mail address"`
VanityIPs string `default:"" usage:"Comma separated list of IP addresses to place in A/AAAA records at the zone apex (default: don't add any records)"`
vanityIPs []net.IP
9 years ago
TplSet string `default:"std" usage:"The template set to use"`
TplPath string `default:"" usage:"The path to the tpl directory (empty: autodetect)"`
10 years ago
10 years ago
ConfigDir string // path to interpret filenames relative to
10 years ago
}
func (cfg *ServerConfig) cpath(s string) string {
return filepath.Join(cfg.ConfigDir, s)
10 years ago
}
func NewServer(cfg *ServerConfig) (s *Server, err error) {
9 years ago
s = &Server{
cfg: *cfg,
namecoinConn: namecoin.Conn{
Username: cfg.NamecoinRPCUsername,
Password: cfg.NamecoinRPCPassword,
Server: cfg.NamecoinRPCAddress,
},
}
10 years ago
10 years ago
s.cfg.canonicalNameservers = strings.Split(s.cfg.CanonicalNameservers, ",")
for i := range s.cfg.canonicalNameservers {
s.cfg.canonicalNameservers[i] = dns.Fqdn(s.cfg.canonicalNameservers[i])
}
10 years ago
if s.cfg.VanityIPs != "" {
vanityIPs := strings.Split(s.cfg.VanityIPs, ",")
for _, ips := range vanityIPs {
ip := net.ParseIP(ips)
if ip == nil {
return nil, fmt.Errorf("Couldn't parse IP: %s", ips)
}
s.cfg.vanityIPs = append(s.cfg.vanityIPs, ip)
10 years ago
}
}
9 years ago
b, err := backend.New(&backend.Config{
10 years ago
NamecoinConn: s.namecoinConn,
CacheMaxEntries: cfg.CacheMaxEntries,
SelfIP: cfg.SelfIP,
Hostmaster: cfg.Hostmaster,
CanonicalNameservers: s.cfg.canonicalNameservers,
VanityIPs: s.cfg.vanityIPs,
9 years ago
})
10 years ago
if err != nil {
return
}
ecfg := &madns.EngineConfig{
10 years ago
Backend: b,
VersionString: "ncdns/" + version,
10 years ago
}
// key setup
if cfg.PublicKey != "" {
10 years ago
ecfg.KSK, ecfg.KSKPrivate, err = s.loadKey(cfg.PublicKey, cfg.PrivateKey)
10 years ago
if err != nil {
return nil, err
}
}
if cfg.ZonePublicKey != "" {
10 years ago
ecfg.ZSK, ecfg.ZSKPrivate, err = s.loadKey(cfg.ZonePublicKey, cfg.ZonePrivateKey)
10 years ago
if err != nil {
return nil, err
}
}
if ecfg.KSK != nil && ecfg.ZSK == nil {
return nil, fmt.Errorf("Must specify ZSK if KSK is specified")
}
9 years ago
s.engine, err = madns.NewEngine(ecfg)
10 years ago
if err != nil {
return
}
10 years ago
if cfg.HTTPListenAddr != "" {
err = webStart(cfg.HTTPListenAddr, s)
if err != nil {
return
}
}
10 years ago
return
10 years ago
}
func (s *Server) loadKey(fn, privateFn string) (k *dns.DNSKEY, privatek crypto.PrivateKey, err error) {
10 years ago
fn = s.cfg.cpath(fn)
privateFn = s.cfg.cpath(privateFn)
10 years ago
f, err := os.Open(fn)
if err != nil {
return
}
rr, err := dns.ReadRR(f, fn)
if err != nil {
return
}
k, ok := rr.(*dns.DNSKEY)
if !ok {
err = fmt.Errorf("Loaded record from key file, but it wasn't a DNSKEY")
return
}
privatef, err := os.Open(privateFn)
if err != nil {
return
}
privatek, err = k.ReadPrivateKey(privatef, privateFn)
log.Fatale(err)
return
10 years ago
}
func (s *Server) Start() error {
10 years ago
s.mux = dns.NewServeMux()
s.mux.Handle(".", s.engine)
s.wgStart.Add(2)
10 years ago
s.udpListener = s.runListener("udp")
s.tcpListener = s.runListener("tcp")
s.wgStart.Wait()
10 years ago
9 years ago
log.Info("Listeners started")
return nil
}
10 years ago
func (s *Server) doRunListener(ds *dns.Server) {
10 years ago
err := ds.ListenAndServe()
log.Fatale(err)
10 years ago
}
func (s *Server) runListener(net string) *dns.Server {
10 years ago
ds := &dns.Server{
Addr: s.cfg.Bind,
Net: net,
Handler: s.mux,
NotifyStartedFunc: func() {
s.wgStart.Done()
},
10 years ago
}
go s.doRunListener(ds)
return ds
10 years ago
}