diff --git a/backend/backend.go b/backend/backend.go index a86596b..0681437 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -31,13 +31,6 @@ var log, Log = xlog.New("ncdns.backend") type Config struct { NamecoinConn namecoin.Conn - // Username and password to use for connecting to the Namecoin JSON-RPC interface. - //RPCUsername string - //RPCPassword string - - // hostname:port to use for connecting to the Namecoin JSON-RPC interface. - //RPCAddress string - // Maximum entries to permit in name cache. If zero, a default value is used. CacheMaxEntries int diff --git a/namecoin/namecoin.go b/namecoin/namecoin.go index f5a033b..c79445a 100644 --- a/namecoin/namecoin.go +++ b/namecoin/namecoin.go @@ -1,13 +1,15 @@ package namecoin // btcjson had to be modified a bit to get correct error reporting. -import "github.com/hlandauf/btcjson" -import "gopkg.in/hlandau/madns.v1/merr" -import extratypes "github.com/hlandau/ncbtcjsontypes" +import ( + extratypes "github.com/hlandau/ncbtcjsontypes" + "github.com/hlandauf/btcjson" + "gopkg.in/hlandau/madns.v1/merr" -import "fmt" -import "expvar" -import "sync/atomic" + "expvar" + "fmt" + "sync/atomic" +) var cQueryCalls = expvar.NewInt("ncdns.namecoin.numQueryCalls") var cSyncCalls = expvar.NewInt("ncdns.namecoin.numSyncCalls") @@ -27,7 +29,29 @@ func newID() int32 { type Conn struct { Username string Password string - Server string + + // If set, this is called to obtain the username and password instead of + // using the Username and Password fields. + GetAuth func() (username, password string, err error) + + Server string +} + +func (nc *Conn) getAuth() (username string, password string, err error) { + if nc.GetAuth == nil { + return nc.Username, nc.Password, nil + } + + return nc.GetAuth() +} + +func (nc *Conn) rpcSend(cmd btcjson.Cmd) (btcjson.Reply, error) { + username, password, err := nc.getAuth() + if err != nil { + return btcjson.Reply{}, err + } + + return btcjson.RpcSend(username, password, nc.Server, cmd) } // Query the Namecoin daemon for a Namecoin domain (e.g. d/example). @@ -42,7 +66,7 @@ func (nc *Conn) Query(name string) (v string, err error) { return "", err } - r, err := btcjson.RpcSend(nc.Username, nc.Password, nc.Server, cmd) + r, err := nc.rpcSend(cmd) if err != nil { return "", err } @@ -81,7 +105,7 @@ func (nc *Conn) Sync(hash string, count int, wait bool) ([]extratypes.NameSyncEv return nil, err } - r, err := btcjson.RpcSend(nc.Username, nc.Password, nc.Server, cmd) + r, err := nc.rpcSend(cmd) if err != nil { return nil, err } @@ -112,7 +136,7 @@ func (nc *Conn) CurHeight() (int, error) { return 0, err } - r, err := btcjson.RpcSend(nc.Username, nc.Password, nc.Server, cmd) + r, err := nc.rpcSend(cmd) if err != nil { return 0, err } @@ -140,7 +164,7 @@ func (nc *Conn) Filter(regexp string, maxage, from, count int) (names []extratyp return nil, err } - r, err := btcjson.RpcSend(nc.Username, nc.Password, nc.Server, cmd) + r, err := nc.rpcSend(cmd) if err != nil { return nil, err } @@ -168,7 +192,7 @@ func (nc *Conn) Scan(from string, count int) (names []extratypes.NameFilterItem, return nil, err } - r, err := btcjson.RpcSend(nc.Username, nc.Password, nc.Server, cmd) + r, err := nc.rpcSend(cmd) if err != nil { return nil, err } diff --git a/server/cookiefile.go b/server/cookiefile.go new file mode 100644 index 0000000..cc43ae3 --- /dev/null +++ b/server/cookiefile.go @@ -0,0 +1,59 @@ +package server + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + "time" +) + +func readCookieFile(path string) (username, password string, err error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return + } + + s := strings.TrimSpace(string(b)) + parts := strings.SplitN(s, ":", 2) + if len(parts) != 2 { + err = fmt.Errorf("malformed cookie file") + return + } + + username, password = parts[0], parts[1] + return +} + +func cookieRetriever(path string) func() (username, password string, err error) { + lastCheckTime := time.Time{} + lastModTime := time.Time{} + + curUsername, curPassword := "", "" + var curError error + + doUpdate := func() { + if !lastCheckTime.IsZero() && time.Now().Before(lastCheckTime.Add(30*time.Second)) { + return + } + + lastCheckTime = time.Now() + + st, err := os.Stat(path) + if err != nil { + curError = err + return + } + + modTime := st.ModTime() + if !modTime.Equal(lastModTime) { + lastModTime = modTime + curUsername, curPassword, curError = readCookieFile(path) + } + } + + return func() (username, password string, err error) { + doUpdate() + return curUsername, curPassword, curError + } +} diff --git a/server/server.go b/server/server.go index 28171fd..e683e79 100644 --- a/server/server.go +++ b/server/server.go @@ -39,12 +39,13 @@ type Config struct { 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"` - 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"` + 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"` HTTPListenAddr string `default:"" usage:"Address for webserver to listen at (default: disabled)"` @@ -78,6 +79,10 @@ func New(cfg *Config) (s *Server, err error) { }, } + 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 {