2014-10-22 15:14:16 +00:00
package server
2014-11-13 10:55:51 +00:00
2016-11-12 11:44:14 +00:00
import (
"crypto"
"fmt"
"github.com/hlandau/buildinfo"
"github.com/hlandau/xlog"
"github.com/miekg/dns"
2017-06-07 04:14:50 +00:00
"github.com/namecoin/ncdns/backend"
"github.com/namecoin/ncdns/namecoin"
2016-11-12 11:44:14 +00:00
"gopkg.in/hlandau/madns.v1"
"net"
"os"
"path/filepath"
"strings"
"sync"
)
2014-11-14 08:51:57 +00:00
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
2016-11-12 11:44:14 +00:00
udpServer * dns . Server
udpConn * net . UDPConn
tcpServer * dns . Server
tcpListener net . Listener
2014-12-12 13:56:40 +00:00
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" `
2017-04-16 03:38:57 +00:00
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
}
2016-11-12 11:44:14 +00:00
var ncdnsVersion string
2015-09-29 08:13:59 +00:00
func New ( cfg * Config ) ( s * Server , err error ) {
2017-06-07 04:14:50 +00:00
ncdnsVersion = buildinfo . VersionSummary ( "github.com/namecoin/ncdns" , "ncdns" )
2016-11-12 11:44:14 +00:00
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
2017-04-16 03:38:57 +00:00
if s . cfg . NamecoinRPCCookiePath != "" {
s . namecoinConn . GetAuth = cookieRetriever ( s . cfg . NamecoinRPCCookiePath )
}
2017-04-09 15:26:53 +00:00
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 ,
2016-11-12 11:44:14 +00:00
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
}
2016-11-12 11:44:14 +00:00
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
}
2015-08-31 06:31:13 +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
}
2014-11-14 08:51:57 +00:00
func ( s * Server ) Start ( ) error {
2014-12-12 13:56:40 +00:00
s . wgStart . Add ( 2 )
2016-11-12 11:44:14 +00:00
s . udpServer = s . runListener ( "udp" )
s . tcpServer = s . runListener ( "tcp" )
2014-12-12 13:56:40 +00:00
s . wgStart . Wait ( )
2015-09-17 19:13:38 +00:00
log . Info ( "Listeners started" )
2014-11-14 08:51:57 +00:00
return nil
}
2014-10-22 15:14:16 +00:00
func ( s * Server ) doRunListener ( ds * dns . Server ) {
2016-11-12 11:44:14 +00:00
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 ,
2014-12-12 13:56:40 +00:00
NotifyStartedFunc : func ( ) {
s . wgStart . Done ( )
} ,
2014-11-13 10:55:51 +00:00
}
2016-11-12 11:44:14 +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
}