2
0
mirror of https://github.com/namecoin/ncdns synced 2024-11-13 13:10:33 +00:00
ncdns/server/server.go

248 lines
6.3 KiB
Go
Raw Normal View History

2014-10-22 15:14:16 +00:00
package server
2014-11-13 10:55:51 +00:00
import (
"crypto"
"fmt"
"github.com/hlandau/buildinfo"
"github.com/hlandau/xlog"
"github.com/miekg/dns"
"github.com/namecoin/ncdns/backend"
"github.com/namecoin/ncdns/namecoin"
"gopkg.in/hlandau/madns.v1"
"net"
"os"
"path/filepath"
"strings"
"sync"
)
2015-09-17 19:13:38 +00:00
var log, Log = xlog.New("ncdns.server")
2014-10-22 15:14:16 +00:00
type Server struct {
2015-09-29 08:13:59 +00:00
cfg Config
2014-10-22 15:14:16 +00:00
2014-12-06 09:30:14 +00:00
engine madns.Engine
namecoinConn namecoin.Conn
2014-10-22 15:14:16 +00:00
2014-11-13 10:55:51 +00:00
mux *dns.ServeMux
udpServer *dns.Server
udpConn *net.UDPConn
tcpServer *dns.Server
tcpListener net.Listener
wgStart sync.WaitGroup
2014-10-22 15:14:16 +00:00
}
2015-09-29 08:13:59 +00:00
type Config struct {
2014-11-13 10:55:51 +00:00
Bind string `default:":53" usage:"Address to bind to (e.g. 0.0.0.0:53)"`
2014-11-13 11:14:15 +00:00
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"`
2014-11-13 10:55:51 +00:00
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"`
NamecoinRPCCookiePath string `default:"" usage:"Namecoin RPC cookie path (if set, used instead of password)"`
CacheMaxEntries int `default:"100" usage:"Maximum name cache entries"`
SelfName string `default:"" usage:"The FQDN of this nameserver. If empty, a psuedo-hostname is generated."`
SelfIP string `default:"127.127.127.127" usage:"The canonical IP address for this service"`
2014-11-13 10:55:51 +00:00
2014-12-06 09:30:14 +00:00
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
2015-09-17 19:13:38 +00:00
TplSet string `default:"std" usage:"The template set to use"`
TplPath string `default:"" usage:"The path to the tpl directory (empty: autodetect)"`
2014-12-06 09:30:14 +00:00
2014-11-25 13:50:51 +00:00
ConfigDir string // path to interpret filenames relative to
2014-11-14 09:40:46 +00:00
}
2015-09-29 08:13:59 +00:00
func (cfg *Config) cpath(s string) string {
2014-11-14 09:40:46 +00:00
return filepath.Join(cfg.ConfigDir, s)
2014-10-22 15:14:16 +00:00
}
var ncdnsVersion string
2015-09-29 08:13:59 +00:00
func New(cfg *Config) (s *Server, err error) {
ncdnsVersion = buildinfo.VersionSummary("github.com/namecoin/ncdns", "ncdns")
2015-09-23 08:29:00 +00:00
s = &Server{
cfg: *cfg,
namecoinConn: namecoin.Conn{
Username: cfg.NamecoinRPCUsername,
Password: cfg.NamecoinRPCPassword,
Server: cfg.NamecoinRPCAddress,
},
}
2014-11-13 10:55:51 +00:00
if s.cfg.NamecoinRPCCookiePath != "" {
s.namecoinConn.GetAuth = cookieRetriever(s.cfg.NamecoinRPCCookiePath)
}
if s.cfg.CanonicalNameservers != "" {
s.cfg.canonicalNameservers = strings.Split(s.cfg.CanonicalNameservers, ",")
for i := range s.cfg.canonicalNameservers {
s.cfg.canonicalNameservers[i] = dns.Fqdn(s.cfg.canonicalNameservers[i])
}
2014-12-06 09:30:14 +00:00
}
2014-12-06 11:12:45 +00:00
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)
2014-12-06 09:30:14 +00:00
}
}
2015-09-23 08:29:00 +00:00
b, err := backend.New(&backend.Config{
2014-12-06 09:30:14 +00:00
NamecoinConn: s.namecoinConn,
CacheMaxEntries: cfg.CacheMaxEntries,
SelfIP: cfg.SelfIP,
Hostmaster: cfg.Hostmaster,
CanonicalNameservers: s.cfg.canonicalNameservers,
VanityIPs: s.cfg.vanityIPs,
2015-09-23 08:29:00 +00:00
})
2014-11-13 10:55:51 +00:00
if err != nil {
return
}
ecfg := &madns.EngineConfig{
2014-11-25 13:50:51 +00:00
Backend: b,
VersionString: ncdnsVersion,
2014-11-13 10:55:51 +00:00
}
// key setup
if cfg.PublicKey != "" {
2014-12-04 12:04:14 +00:00
ecfg.KSK, ecfg.KSKPrivate, err = s.loadKey(cfg.PublicKey, cfg.PrivateKey)
2014-11-13 10:55:51 +00:00
if err != nil {
return nil, err
}
}
if cfg.ZonePublicKey != "" {
2014-12-04 12:04:14 +00:00
ecfg.ZSK, ecfg.ZSKPrivate, err = s.loadKey(cfg.ZonePublicKey, cfg.ZonePrivateKey)
2014-11-13 10:55:51 +00:00
if err != nil {
return nil, err
}
}
if ecfg.KSK != nil && ecfg.ZSK == nil {
return nil, fmt.Errorf("Must specify ZSK if KSK is specified")
}
2015-09-23 08:29:00 +00:00
s.engine, err = madns.NewEngine(ecfg)
2014-11-13 10:55:51 +00:00
if err != nil {
return
}
s.mux = dns.NewServeMux()
s.mux.Handle(".", s.engine)
tcpAddr, err := net.ResolveTCPAddr("tcp", s.cfg.Bind)
if err != nil {
return
}
s.tcpListener, err = net.ListenTCP("tcp", tcpAddr)
if err != nil {
return
}
udpAddr, err := net.ResolveUDPAddr("udp", s.cfg.Bind)
if err != nil {
return
}
s.udpConn, err = net.ListenUDP("udp", udpAddr)
if err != nil {
return
}
2014-12-06 09:30:14 +00:00
if cfg.HTTPListenAddr != "" {
err = webStart(cfg.HTTPListenAddr, s)
if err != nil {
return
}
}
2014-11-13 10:55:51 +00:00
return
2014-10-22 15:14:16 +00:00
}
func (s *Server) loadKey(fn, privateFn string) (k *dns.DNSKEY, privatek crypto.PrivateKey, err error) {
2014-11-14 09:40:46 +00:00
fn = s.cfg.cpath(fn)
privateFn = s.cfg.cpath(privateFn)
2014-11-13 10:55:51 +00:00
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
2014-10-22 15:14:16 +00:00
}
func (s *Server) Start() error {
s.wgStart.Add(2)
s.udpServer = s.runListener("udp")
s.tcpServer = s.runListener("tcp")
s.wgStart.Wait()
2015-09-17 19:13:38 +00:00
log.Info("Listeners started")
return nil
}
2014-10-22 15:14:16 +00:00
func (s *Server) doRunListener(ds *dns.Server) {
err := ds.ActivateAndServe()
2014-11-13 10:55:51 +00:00
log.Fatale(err)
2014-10-22 15:14:16 +00:00
}
func (s *Server) runListener(net string) *dns.Server {
2014-11-13 10:55:51 +00:00
ds := &dns.Server{
Addr: s.cfg.Bind,
Net: net,
Handler: s.mux,
NotifyStartedFunc: func() {
s.wgStart.Done()
},
2014-11-13 10:55:51 +00:00
}
switch net {
case "tcp":
ds.Listener = s.tcpListener
case "udp":
ds.PacketConn = s.udpConn
default:
panic("unreachable")
}
2014-11-13 10:55:51 +00:00
go s.doRunListener(ds)
return ds
2014-10-22 15:14:16 +00:00
}
2015-09-29 08:13:59 +00:00
func (s *Server) Stop() error {
return nil // TODO
}