Skip to content

Commit

Permalink
Switch to new rpcclient based on latest upstream Conformal
Browse files Browse the repository at this point in the history
We will switch to upstream once they merge our patches.
  • Loading branch information
JeremyRand committed Oct 27, 2019
1 parent 006f537 commit 593e212
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 294 deletions.
11 changes: 4 additions & 7 deletions backend/backend.go
Expand Up @@ -19,7 +19,7 @@ import "time"
// Provides an abstract zone file for the Namecoin .bit TLD.
type Backend struct {
//s *Server
nc namecoin.Conn
nc *namecoin.Client
cache lru.Cache // items are of type *Domain
cacheMutex sync.Mutex
cfg Config
Expand All @@ -31,7 +31,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
Expand Down Expand Up @@ -65,9 +65,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.cache.MaxEntries = cfg.CacheMaxEntries
if b.cache.MaxEntries == 0 {
Expand Down Expand Up @@ -344,13 +341,13 @@ func (b *Backend) resolveName(name string) (jsonValue string, err error) {
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)
jsonValue, err = b.nc.NameQuery(name)
log.Errore(err, "failed to query namecoin")
result <- struct{}{}
}()
Expand Down
221 changes: 27 additions & 194 deletions namecoin/namecoin.go
@@ -1,215 +1,48 @@
package namecoin

// btcjson had to be modified a bit to get correct error reporting.
import (
extratypes "github.com/hlandau/ncbtcjsontypes"
"github.com/hlandauf/btcjson"
"github.com/namecoin/btcd/btcjson"
"github.com/namecoin/btcd/rpcclient"
"gopkg.in/hlandau/madns.v1/merr"

"expvar"
"fmt"
"sync/atomic"
"github.com/namecoin/ncrpcclient"
)

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
}

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).
// 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) (v string, err error) {
cQueryCalls.Add(1)

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)
if err != nil {
return nil, err
}

if r.Error != nil {
if r.Error.Code == rpcInvalidAddressOrKey {
return nil, ErrSyncNoSuchBlock
}
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")
// Client represents an ncrpcclient.Client with an additional DNS-friendly
// convenience wrapper around NameShow.
type Client struct {
*ncrpcclient.Client
}

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)
func New(config *rpcclient.ConnConfig, ntfnHandlers *rpcclient.NotificationHandlers) (*Client, error) {
ncClient, err := ncrpcclient.New(config, ntfnHandlers)
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")
return &Client{ncClient}, nil
}

func (nc *Conn) Scan(from string, count int) (names []extratypes.NameFilterItem, err error) {
cScanCalls.Add(1)

cmd, err := extratypes.NewNameScanCmd(newID(), from, count)
// 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) (string, error) {
nameData, err := c.NameShow(name)
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 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
}
}

if r.Result == nil {
return nil, fmt.Errorf("got nil result")
// Some error besides NXDOMAIN happened; pass that error
// through unaltered.
return "", err
}

if nsr, ok := r.Result.(extratypes.NameFilterReply); ok {
return []extratypes.NameFilterItem(nsr), nil
}
// TODO: check the "value_error" field for errors and report those to the caller.

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
30 changes: 25 additions & 5 deletions ncdt/ncdt.go
Expand Up @@ -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")
Expand All @@ -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)
}

Expand All @@ -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)
}
Expand Down Expand Up @@ -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]
Expand Down
10 changes: 5 additions & 5 deletions ncdumpzone/ncdumpzone.go
Expand Up @@ -8,12 +8,12 @@ import (
"github.com/hlandau/xlog"
"github.com/miekg/dns"

extratypes "github.com/hlandau/ncbtcjsontypes"
"github.com/namecoin/ncdns/namecoin"
"github.com/namecoin/ncdns/ncdomain"
"github.com/namecoin/ncdns/rrtourl"
"github.com/namecoin/ncdns/tlsoverridefirefox"
"github.com/namecoin/ncdns/util"
"github.com/namecoin/ncbtcjson"
)

var log, Log = xlog.New("ncdumpzone")
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
Expand Down

0 comments on commit 593e212

Please sign in to comment.