Switch to new rpcclient based on latest upstream Conformal

We will switch to upstream once they merge our patches.
pull/103/head
JeremyRand 5 years ago
parent 1e2eea58c7
commit 2b1eed8f53
No known key found for this signature in database
GPG Key ID: B3F2D165786D6570

@ -18,7 +18,7 @@ import "time"
// Provides an abstract zone file for the Namecoin .bit TLD.
type Backend struct {
//s *Server
nc namecoin.Conn
nc *namecoin.Client
// caches map keys are stream isolation ID's; items are of type *Domain
caches map[string]*lru.Cache
cacheMutex sync.Mutex
@ -29,7 +29,7 @@ var log, Log = xlog.New("ncdns.backend")
// Backend configuration.
type Config struct {
NamecoinConn namecoin.Conn
NamecoinConn *namecoin.Client
// Timeout (in milliseconds) for Namecoin RPC requests
NamecoinTimeout int
@ -63,9 +63,6 @@ func New(cfg *Config) (backend *Backend, err error) {
b.cfg = *cfg
b.nc = b.cfg.NamecoinConn
//b.nc.Username = cfg.RPCUsername
//b.nc.Password = cfg.RPCPassword
//b.nc.Server = cfg.RPCAddress
b.caches = make(map[string]*lru.Cache)
@ -355,13 +352,13 @@ func (b *Backend) resolveName(name, streamIsolationID string) (jsonValue string,
return fv, nil
}
// The btcjson package has quite a long timeout, far in excess of standard
// The rpcclient package has quite a long timeout, far in excess of standard
// DNS timeouts. We need to return an error response rapidly if we can't
// query the backend. Be generous with the timeout as responses from the
// Namecoin JSON-RPC seem sluggish sometimes.
result := make(chan struct{}, 1)
go func() {
jsonValue, err = b.nc.Query(name, streamIsolationID)
jsonValue, err = b.nc.NameQuery(name, streamIsolationID)
log.Errore(err, "failed to query namecoin")
result <- struct{}{}
}()

@ -1,220 +1,55 @@
package namecoin
// btcjson had to be modified a bit to get correct error reporting.
import (
extratypes "github.com/hlandau/ncbtcjsontypes"
"github.com/hlandauf/btcjson"
"gopkg.in/hlandau/madns.v2/merr"
"expvar"
"fmt"
"sync/atomic"
)
var cQueryCalls = expvar.NewInt("ncdns.namecoin.numQueryCalls")
var cSyncCalls = expvar.NewInt("ncdns.namecoin.numSyncCalls")
var cFilterCalls = expvar.NewInt("ncdns.namecoin.numFilterCalls")
var cScanCalls = expvar.NewInt("ncdns.namecoin.numScanCalls")
var cCurHeightCalls = expvar.NewInt("ncdns.namecoin.numCurHeightCalls")
// Used for generating IDs for JSON-RPC requests.
var idCounter int32
func newID() int32 {
return atomic.AddInt32(&idCounter, 1)
}
// Used to query a Namecoin JSON-RPC interface. Initialize the struct with a
// username, password, and address (hostname:port).
type Conn struct {
Username string
Password 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
}
"github.com/namecoin/btcd/btcjson"
"github.com/namecoin/btcd/rpcclient"
"gopkg.in/hlandau/madns.v2/merr"
func (nc *Conn) getAuth() (username string, password string, err error) {
if nc.GetAuth == nil {
return nc.Username, nc.Password, nil
}
"github.com/namecoin/ncrpcclient"
)
return nc.GetAuth()
// Client represents an ncrpcclient.Client with an additional DNS-friendly
// convenience wrapper around NameShow.
type Client struct {
*ncrpcclient.Client
}
func (nc *Conn) rpcSend(cmd btcjson.Cmd) (btcjson.Reply, error) {
username, password, err := nc.getAuth()
func New(config *rpcclient.ConnConfig, ntfnHandlers *rpcclient.NotificationHandlers) (*Client, error) {
ncClient, err := ncrpcclient.New(config, ntfnHandlers)
if err != nil {
return btcjson.Reply{}, err
return nil, err
}
return btcjson.RpcSend(username, password, nc.Server, cmd)
return &Client{ncClient}, nil
}
// Query the Namecoin daemon for a Namecoin domain (e.g. d/example).
// If the domain exists, returns the value stored in Namecoin, which should be JSON.
// Note that this will return domain data even if the domain is expired.
func (nc *Conn) Query(name string, streamIsolationID string) (v string, err error) {
cQueryCalls.Add(1)
// NameQuery returns the value of a name. If the name doesn't exist, the error
// returned will be merr.ErrNoSuchDomain.
func (c *Client) NameQuery(name string, streamIsolationID string) (string, error) {
// TODO: Pass stream isolation ID to namecoind, and remove this error
if streamIsolationID != "" {
return "", fmt.Errorf("Stream isolation ID '%s' is not yet passed to namecoind", streamIsolationID)
}
cmd, err := extratypes.NewNameShowCmd(newID(), name)
if err != nil {
//log.Info("NC NEWCMD ", err)
return "", err
}
r, err := nc.rpcSend(cmd)
if err != nil {
return "", err
}
if r.Error != nil {
//log.Info("RPC error: ", r.Error)
if r.Error.Code == -4 {
return "", merr.ErrNoSuchDomain
}
return "", r.Error
}
if r.Result == nil {
//log.Info("NC NILRESULT")
return "", fmt.Errorf("got nil result")
}
if nsr, ok := r.Result.(*extratypes.NameShowReply); ok {
//log.Info("NC OK")
return nsr.Value, nil
}
//log.Info("NC BADREPLY")
return "", fmt.Errorf("bad reply")
}
var ErrSyncNoSuchBlock = fmt.Errorf("no block exists with given hash")
const rpcInvalidAddressOrKey = -5
func (nc *Conn) Sync(hash string, count int, wait bool) ([]extratypes.NameSyncEvent, error) {
cSyncCalls.Add(1)
cmd, err := extratypes.NewNameSyncCmd(newID(), hash, count, wait)
if err != nil {
return nil, err
}
r, err := nc.rpcSend(cmd)
nameData, err := c.NameShow(name)
if err != nil {
return nil, err
}
if r.Error != nil {
if r.Error.Code == rpcInvalidAddressOrKey {
return nil, ErrSyncNoSuchBlock
if jerr, ok := err.(*btcjson.RPCError); ok {
if jerr.Code == btcjson.ErrRPCWallet {
// ErrRPCWallet from name_show indicates that
// the name does not exist.
return "", merr.ErrNoSuchDomain
}
}
return nil, r.Error
}
if r.Result == nil {
return nil, fmt.Errorf("got nil result")
}
if nsr, ok := r.Result.(extratypes.NameSyncReply); ok {
return []extratypes.NameSyncEvent(nsr), nil
}
return nil, fmt.Errorf("bad reply")
}
func (nc *Conn) CurHeight() (int, error) {
cCurHeightCalls.Add(1)
cmd, err := btcjson.NewGetInfoCmd(newID())
if err != nil {
return 0, err
}
r, err := nc.rpcSend(cmd)
if err != nil {
return 0, err
}
if r.Error != nil {
return 0, r.Error
}
if r.Result == nil {
return 0, fmt.Errorf("got nil result")
}
if rep, ok := r.Result.(*btcjson.InfoResult); ok {
return int(rep.Blocks), nil
}
return 0, fmt.Errorf("bad reply")
}
func (nc *Conn) Filter(regexp string, maxage, from, count int) (names []extratypes.NameFilterItem, err error) {
cFilterCalls.Add(1)
cmd, err := extratypes.NewNameFilterCmd(newID(), regexp, maxage, from, count)
if err != nil {
return nil, err
}
r, err := nc.rpcSend(cmd)
if err != nil {
return nil, err
}
if r.Error != nil {
return nil, r.Error
}
if r.Result == nil {
return nil, fmt.Errorf("got nil result")
}
if nsr, ok := r.Result.(extratypes.NameFilterReply); ok {
return []extratypes.NameFilterItem(nsr), nil
}
return nil, fmt.Errorf("bad reply")
}
func (nc *Conn) Scan(from string, count int) (names []extratypes.NameFilterItem, err error) {
cScanCalls.Add(1)
cmd, err := extratypes.NewNameScanCmd(newID(), from, count)
if err != nil {
return nil, err
}
r, err := nc.rpcSend(cmd)
if err != nil {
return nil, err
}
if r.Error != nil {
return nil, r.Error
// Some error besides NXDOMAIN happened; pass that error
// through unaltered.
return "", err
}
if r.Result == nil {
return nil, fmt.Errorf("got nil result")
}
// TODO: check the "value_error" field for errors and report those to the caller.
if nsr, ok := r.Result.(extratypes.NameFilterReply); ok {
return []extratypes.NameFilterItem(nsr), nil
}
return nil, fmt.Errorf("bad reply")
// We got the name data. Return the value.
return nameData.Value, nil
}
// © 2014 Hugo Landau <hlandau@devever.net> GPLv3 or later

@ -7,12 +7,14 @@ import "fmt"
import "os"
import "strconv"
import "io/ioutil"
import "github.com/namecoin/btcd/rpcclient"
import "github.com/namecoin/ncdns/util"
var rpchost = flag.String("rpchost", "", "Namecoin RPC host:port")
var rpcuser = flag.String("rpcuser", "", "Namecoin RPC username")
var rpcpass = flag.String("rpcpass", "", "Namecoin RPC password")
var conn namecoin.Conn
var rpccookiepath = flag.String("rpccookiepath", "", "Namecoin RPC cookie path (used if password is unspecified)")
var conn *namecoin.Client
func usage() {
fmt.Fprintf(os.Stderr, "Usage: ncdt [options] <d/example> <JSON value> [<d/imported-example> <JSON value> ...]\n")
@ -21,6 +23,7 @@ func usage() {
fmt.Fprintf(os.Stderr, " -rpchost=host:port Namecoin RPC server address } only required for RPC retrieval\n")
fmt.Fprintf(os.Stderr, " -rpcuser=username Namecoin RPC username }\n")
fmt.Fprintf(os.Stderr, " -rpcpass=password Namecoin RPC password }\n")
fmt.Fprintf(os.Stderr, " -rpccookiepath=path Namecoin RPC cookie path }\n")
os.Exit(2)
}
@ -41,7 +44,7 @@ func translateValue(k, v string) (string, error) {
f = os.NewFile(uintptr(n), "-")
} else if len(v) == 1 {
return conn.Query(k, "")
return conn.NameQuery(k, "")
} else {
f, err = os.Open(v)
}
@ -71,9 +74,26 @@ func main() {
usage()
}
conn.Username = *rpcuser
conn.Password = *rpcpass
conn.Server = *rpchost
// Connect to local namecoin core RPC server using HTTP POST mode.
connCfg := &rpcclient.ConnConfig{
Host: *rpchost,
User: *rpcuser,
Pass: *rpcpass,
CookiePath: *rpccookiepath,
HTTPPostMode: true, // Namecoin core only supports HTTP POST mode
DisableTLS: true, // Namecoin core does not provide TLS by default
}
var err error
// Notice the notification parameter is nil since notifications are
// not supported in HTTP POST mode.
conn, err = namecoin.New(connCfg, nil)
if err != nil {
fmt.Fprintf(os.Stderr, "Error creating RPC client: %v\n", err)
os.Exit(1)
}
defer conn.Shutdown()
for i := 0; i+1 < len(args); i += 2 {
k := args[i]
@ -83,7 +103,7 @@ func main() {
os.Exit(1)
}
v, err := translateValue(k, v)
v, err = translateValue(k, v)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to translate value: %v\n", err)
os.Exit(1)

@ -8,7 +8,7 @@ import (
"github.com/hlandau/xlog"
"github.com/miekg/dns"
extratypes "github.com/hlandau/ncbtcjsontypes"
"github.com/namecoin/ncbtcjson"
"github.com/namecoin/ncdns/namecoin"
"github.com/namecoin/ncdns/ncdomain"
"github.com/namecoin/ncdns/rrtourl"
@ -41,7 +41,7 @@ func dumpRR(rr dns.RR, dest io.Writer, format string) error {
return nil
}
func dumpName(item *extratypes.NameFilterItem, conn namecoin.Conn,
func dumpName(item *ncbtcjson.NameShowResult, conn *namecoin.Client,
dest io.Writer, format string) error {
// The order in which name_scan returns results is seemingly rather
// random, so we can't stop when we see a non-d/ name, so just skip it.
@ -55,7 +55,7 @@ func dumpName(item *extratypes.NameFilterItem, conn namecoin.Conn,
}
getNameFunc := func(k string) (string, error) {
return conn.Query(k, "")
return conn.NameQuery(k, "")
}
var errors []error
@ -83,7 +83,7 @@ func dumpName(item *extratypes.NameFilterItem, conn namecoin.Conn,
// Dump extracts all domain names from conn, formats them according to the
// specified format, and writes the result to dest.
func Dump(conn namecoin.Conn, dest io.Writer, format string) error {
func Dump(conn *namecoin.Client, dest io.Writer, format string) error {
if format != "zonefile" && format != "firefox-override" &&
format != "url-list" {
return fmt.Errorf("Invalid \"format\" argument: %s", format)
@ -93,7 +93,7 @@ func Dump(conn namecoin.Conn, dest io.Writer, format string) error {
continuing := 0
for {
results, err := conn.Scan(currentName, perCall)
results, err := conn.NameScan(currentName, perCall)
if err != nil {
return fmt.Errorf("scan: %s", err)
}

@ -4,6 +4,7 @@ import (
"os"
"github.com/hlandau/xlog"
"github.com/namecoin/btcd/rpcclient"
"gopkg.in/hlandau/easyconfig.v1"
"gopkg.in/hlandau/easyconfig.v1/cflag"
@ -21,13 +22,15 @@ var (
"Namecoin RPC username")
rpcpassFlag = cflag.String(flagGroup, "namecoinrpcpassword", "",
"Namecoin RPC password")
rpccookiepathFlag = cflag.String(flagGroup, "namecoinrpccookiepath", "",
"Namecoin RPC cookie path (used if password is unspecified)")
formatFlag = cflag.String(flagGroup, "format", "zonefile", "Output "+
"format. \"zonefile\" = DNS zone file. "+
"\"firefox-override\" = Firefox cert_override.txt format. "+
"\"url-list\" = URL list.")
)
var conn namecoin.Conn
var conn *namecoin.Client
var config = easyconfig.Configurator{
ProgramName: "ncdumpzone",
@ -39,9 +42,23 @@ func main() {
log.Fatalf("Couldn't parse configuration: %s", err)
}
conn.Server = rpchostFlag.Value()
conn.Username = rpcuserFlag.Value()
conn.Password = rpcpassFlag.Value()
// Connect to local namecoin core RPC server using HTTP POST mode.
connCfg := &rpcclient.ConnConfig{
Host: rpchostFlag.Value(),
User: rpcuserFlag.Value(),
Pass: rpcpassFlag.Value(),
CookiePath: rpccookiepathFlag.Value(),
HTTPPostMode: true, // Namecoin core only supports HTTP POST mode
DisableTLS: true, // Namecoin core does not provide TLS by default
}
// Notice the notification parameter is nil since notifications are
// not supported in HTTP POST mode.
conn, err = namecoin.New(connCfg, nil)
if err != nil {
log.Fatalf("Couldn't create RPC client: %s", err)
}
defer conn.Shutdown()
err = ncdumpzone.Dump(conn, os.Stdout, formatFlag.Value())
if err != nil {

@ -1,59 +0,0 @@
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
}
}

@ -12,9 +12,10 @@ import (
"github.com/hlandau/buildinfo"
"github.com/hlandau/xlog"
"github.com/miekg/dns"
"github.com/namecoin/btcd/rpcclient"
"github.com/namecoin/ncdns/backend"
"github.com/namecoin/ncdns/namecoin"
"gopkg.in/hlandau/madns.v2"
madns "gopkg.in/hlandau/madns.v2"
)
var log, Log = xlog.New("ncdns.server")
@ -23,7 +24,7 @@ type Server struct {
cfg Config
engine madns.Engine
namecoinConn namecoin.Conn
namecoinConn *namecoin.Client
mux *dns.ServeMux
udpServer *dns.Server
@ -43,7 +44,7 @@ type Config struct {
NamecoinRPCUsername string `default:"" usage:"Namecoin RPC username"`
NamecoinRPCPassword string `default:"" usage:"Namecoin RPC password"`
NamecoinRPCAddress string `default:"127.0.0.1:8336" usage:"Namecoin RPC server address"`
NamecoinRPCCookiePath string `default:"" usage:"Namecoin RPC cookie path (if set, used instead of password)"`
NamecoinRPCCookiePath string `default:"" usage:"Namecoin RPC cookie path (used if password is unspecified)"`
NamecoinRPCTimeout int `default:"1500" usage:"Timeout (in milliseconds) for Namecoin RPC requests"`
CacheMaxEntries int `default:"100" usage:"Maximum name cache entries"`
SelfName string `default:"" usage:"The FQDN of this nameserver. If empty, a pseudo-hostname is generated."`
@ -72,17 +73,26 @@ var ncdnsVersion string
func New(cfg *Config) (s *Server, err error) {
ncdnsVersion = buildinfo.VersionSummary("github.com/namecoin/ncdns", "ncdns")
s = &Server{
cfg: *cfg,
namecoinConn: namecoin.Conn{
Username: cfg.NamecoinRPCUsername,
Password: cfg.NamecoinRPCPassword,
Server: cfg.NamecoinRPCAddress,
},
// Connect to local namecoin core RPC server using HTTP POST mode.
connCfg := &rpcclient.ConnConfig{
Host: cfg.NamecoinRPCAddress,
User: cfg.NamecoinRPCUsername,
Pass: cfg.NamecoinRPCPassword,
CookiePath: cfg.NamecoinRPCCookiePath,
HTTPPostMode: true, // Namecoin core only supports HTTP POST mode
DisableTLS: true, // Namecoin core does not provide TLS by default
}
// Notice the notification parameter is nil since notifications are
// not supported in HTTP POST mode.
client, err := namecoin.New(connCfg, nil)
if err != nil {
return nil, err
}
if s.cfg.NamecoinRPCCookiePath != "" {
s.namecoinConn.GetAuth = cookieRetriever(s.cfg.NamecoinRPCCookiePath)
s = &Server{
cfg: *cfg,
namecoinConn: client,
}
if s.cfg.CanonicalNameservers != "" {

@ -140,7 +140,7 @@ func (ws *webServer) handleLookup(rw http.ResponseWriter, req *http.Request) {
info.JSONValue = req.FormValue("value")
info.Value = strings.Trim(info.JSONValue, " \t\r\n")
if info.Value == "" {
info.Value, info.ExistenceError = ws.s.namecoinConn.Query(info.NamecoinName, "")
info.Value, info.ExistenceError = ws.s.namecoinConn.NameQuery(info.NamecoinName, "")
if info.ExistenceError != nil {
return
}
@ -170,7 +170,7 @@ func (ws *webServer) handleLookup(rw http.ResponseWriter, req *http.Request) {
}
func (ws *webServer) resolveFunc(name string) (string, error) {
return ws.s.namecoinConn.Query(name, "")
return ws.s.namecoinConn.NameQuery(name, "")
}
func (ws *webServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {

@ -37,7 +37,7 @@ var zoneDataMux sync.Mutex
// situation, .bit domains must stop resolving until the issue is corrected.
// Forcing ncdns to exit is the least complex way to achieve this.
func watchZone(conn namecoin.Conn) {
func watchZone(conn *namecoin.Client) {
for {
var result bytes.Buffer
@ -125,7 +125,7 @@ func profileInUse() bool {
// Start starts 2 background threads that synchronize the blockchain's TLSA
// records to a Firefox profile's cert_override.txt. It accepts a connection
// to access Namecoin Core, as well as a host suffix (usually "bit").
func Start(conn namecoin.Conn, suffix string) error {
func Start(conn *namecoin.Client, suffix string) error {
if syncEnableFlag.Value() {
go watchZone(conn)
go watchProfile(suffix)

Loading…
Cancel
Save