diff --git a/rpcserver.go b/rpcserver.go index dcacd28f..bd060858 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -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() diff --git a/server.go b/server.go index 599b2320..3b84d153 100644 --- a/server.go +++ b/server.go @@ -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 {