Skip to content

Commit

Permalink
(WIP) Switch to rpcclient from upstream Conformal
Browse files Browse the repository at this point in the history
TODO: Wait for Conformal to merge the symbol exporting patch
TODO: Fix cookie authentication
  • Loading branch information
JeremyRand committed Aug 2, 2019
1 parent 006f537 commit 72ccb06
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 228 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/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/rpcclient"
"gopkg.in/hlandau/madns.v1/merr"

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

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 nmcrpcclient.Client with an additional DNS-friendly
// convenience wrapper around NameShow.
type Client struct {
*nmcrpcclient.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) {
nmcClient, err := nmcrpcclient.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{nmcClient}, 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
27 changes: 22 additions & 5 deletions ncdt/ncdt.go
Expand Up @@ -7,12 +7,13 @@ import "fmt"
import "os"
import "strconv"
import "io/ioutil"
import "github.com/btcsuite/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 conn *namecoin.Client

func usage() {
fmt.Fprintf(os.Stderr, "Usage: ncdt [options] <d/example> <JSON value> [<d/imported-example> <JSON value> ...]\n")
Expand Down Expand Up @@ -41,7 +42,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 +72,25 @@ 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,
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/nmcjson"
)

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 *nmcjson.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 72ccb06

Please sign in to comment.