Add support for getaddednodeinfo RPC command.

This commit adds full support for the getaddednodeinfo RPC command
including DNS lookups which abide by proxy/onion/tor rules when the DNS
flag is specified.  Note that it returns an array of strings when the DNS
flag is not set which is different than the current version of bitcoind
which is bugged and scheduled to be fixed per issue 3581 on the bitcoind
issue tracker.
This commit is contained in:
Dave Collins 2014-01-24 14:10:02 -06:00
parent 5736dc05ae
commit dcef4128b8
2 changed files with 109 additions and 4 deletions

View file

@ -54,6 +54,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
"debuglevel": handleDebugLevel,
"decoderawtransaction": handleDecodeRawTransaction,
"decodescript": handleDecodeScript,
"getaddednodeinfo": handleGetAddedNodeInfo,
"getbestblockhash": handleGetBestBlockHash,
"getblock": handleGetBlock,
"getblockcount": handleGetBlockCount,
@ -127,7 +128,6 @@ var rpcAskWallet = map[string]bool{
// Commands that are temporarily unimplemented.
var rpcUnimplemented = map[string]bool{
"getaddednodeinfo": true,
"getblocktemplate": true,
"getinfo": true,
"getmininginfo": true,
@ -704,6 +704,89 @@ func handleDecodeScript(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
return reply, nil
}
// handleGetAddedNodeInfo handles getaddednodeinfo commands.
func handleGetAddedNodeInfo(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
c := cmd.(*btcjson.GetAddedNodeInfoCmd)
// Retrieve a list of persistent (added) peers from the bitcoin server
// and filter the list of peer per the specified address (if any).
peers := s.server.AddedNodeInfo()
if c.Node != "" {
found := false
for i, peer := range peers {
if peer.addr == c.Node {
peers = peers[i : i+1]
found = true
}
}
if !found {
return nil, btcjson.Error{
Code: -24,
Message: "Node has not been added.",
}
}
}
// Without the dns flag, the result is just a slice of the adddresses
// as strings.
if !c.Dns {
results := make([]string, 0, len(peers))
for _, peer := range peers {
results = append(results, peer.addr)
}
return results, nil
}
// With the dns flag, the result is an array of JSON objects which
// include the result of DNS lookups for each peer.
results := make([]*btcjson.GetAddedNodeInfoResult, 0, len(peers))
for _, peer := range peers {
// Set the "address" of the peer which could be an ip address
// or a domain name.
var result btcjson.GetAddedNodeInfoResult
result.AddedNode = peer.addr
isConnected := peer.Connected()
result.Connected = &isConnected
// Split the address into host and port portions so we can do
// a DNS lookup against the host. When no port is specified in
// the address, just use the address as the host.
host, _, err := net.SplitHostPort(peer.addr)
if err != nil {
host = peer.addr
}
// Do a DNS lookup for the address. If the lookup fails, just
// use the host.
var ipList []string
ips, err := btcdLookup(host)
if err == nil {
ipList = make([]string, 0, len(ips))
for _, ip := range ips {
ipList = append(ipList, ip.String())
}
} else {
ipList = make([]string, 1)
ipList[0] = host
}
// Add the addresses and connection info to the result.
addrs := make([]btcjson.GetAddedNodeInfoResultAddr, 0, len(ipList))
for _, ip := range ipList {
var addr btcjson.GetAddedNodeInfoResultAddr
addr.Address = ip
addr.Connected = "false"
if ip == host && peer.Connected() {
addr.Connected = directionString(peer.inbound)
}
addrs = append(addrs, addr)
}
result.Addresses = &addrs
results = append(results, &result)
}
return results, nil
}
// handleGetBestBlockHash implements the getbestblockhash command.
func handleGetBestBlockHash(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
sha, _, err := s.server.db.NewestSha()

View file

@ -215,7 +215,7 @@ func forAllOutboundPeers(state *peerState, closure func(p *peer)) {
}
// forAllPeers is a helper function that runs closure on all peers known to
// peerSTate.
// peerState.
func forAllPeers(state *peerState, closure func(p *peer)) {
for e := state.peers.Front(); e != nil; e = e.Next() {
closure(e.Value.(*peer))
@ -296,20 +296,23 @@ type delNodeMsg struct {
reply chan error
}
type getAddedNodesMsg struct {
reply chan []*peer
}
// handleQuery is the central handler for all queries and commands from other
// goroutines related to peer state.
func (s *server) handleQuery(querymsg interface{}, state *peerState) {
switch msg := querymsg.(type) {
case getConnCountMsg:
nconnected := 0
forAllPeers(state, func(p *peer) {
if p.Connected() {
nconnected++
}
})
msg.reply <- nconnected
case getPeerInfoMsg:
infos := make([]*PeerInfo, 0, state.peers.Len())
forAllPeers(state, func(p *peer) {
@ -346,6 +349,7 @@ func (s *server) handleQuery(querymsg interface{}, state *peerState) {
infos = append(infos, info)
})
msg.reply <- infos
case addNodeMsg:
// XXX(oga) duplicate oneshots?
if msg.permanent {
@ -387,6 +391,16 @@ func (s *server) handleQuery(querymsg interface{}, state *peerState) {
} else {
msg.reply <- errors.New("peer not found")
}
// Request a list of the persistent (added) peers.
case getAddedNodesMsg:
// Respond with a slice of the relavent peers.
peers := make([]*peer, 0, state.persistentPeers.Len())
for e := state.persistentPeers.Front(); e != nil; e = e.Next() {
peer := e.Value.(*peer)
peers = append(peers, peer)
}
msg.reply <- peers
}
}
@ -639,6 +653,14 @@ func (s *server) ConnectedCount() int {
return <-replyChan
}
// AddedNodeInfo returns an array of btcjson.GetAddedNodeInfoResult structures
// describing the persistent (added) nodes.
func (s *server) AddedNodeInfo() []*peer {
replyChan := make(chan []*peer)
s.query <- getAddedNodesMsg{reply: replyChan}
return <-replyChan
}
// PeerInfo returns an array of PeerInfo structures describing all connected
// peers.
func (s *server) PeerInfo() []*PeerInfo {