peer: implement response to getaddr.

Signed-off-by: Fedor Indutny <fedor@indutny.com>
This commit is contained in:
Christopher Jeffrey 2014-05-17 00:21:21 -05:00 committed by Fedor Indutny
parent d617edf3ab
commit d0b14008d0
3 changed files with 237 additions and 0 deletions

View File

@ -1,5 +1,7 @@
var inherits = require('inherits');
var EventEmitter = require('events').EventEmitter;
var net = require('net');
var os = require('os');
var bcoin = require('../bcoin');
var utils = bcoin.utils;
@ -276,6 +278,8 @@ Peer.prototype._onPacket = function onPacket(packet) {
return this._handlePing(payload);
else if (cmd === 'pong')
return this._handlePong(payload);
else if (cmd === 'getaddr')
return this._handleGetAddr();
if (cmd === 'merkleblock' || cmd === 'block') {
payload = bcoin.block(payload, cmd);
@ -334,6 +338,122 @@ Peer.prototype._handlePong = function handlePong() {
// No-op for now
};
Peer.prototype._handleGetAddr = function handleGetAddr() {
var used = [];
var own = this._getOwnIP();
var peers = [].concat(
this.pool.peers.pending,
this.pool.peers.block,
this.pool.peers.load
).filter(Boolean);
// NOTE: For IPv6 BTC uses:
// '0000:0000:0000:0000:0000:xxxx:xxxx:ffff'
peers = peers.map(function(peer) {
if (!peer.socket || !peer.socket.remoteAddress) return;
return {
host: peer.socket.remoteAddress,
port: peer.socket.remotePort || 8333
};
}).filter(function(peer) {
if (~used.indexOf(peer.host)) return;
used.push(peer.host);
return !!peer.host && net.isIP(peer.host);
}).map(function(peer) {
var ip = peer.host;
var ver = net.isIP(ip);
return {
ipv4: ver === 4 ? ip : '127.0.0.1',
ipv6: ver === 6 ? ip : '0000:0000:0000:0000:0000:0000:0000:ffff',
port: peer.port,
ver: ver
};
});
if (own) peers.push(own);
peers = peers.map(function(peer) {
if (peer.ver === 6) {
while (peer.ipv6.split(':').length < 8) {
peer.ipv6 = '0000:' + peer.ipv6;
}
if (peer.ipv6.split(':').length > 8) {
return;
}
}
peer.ipv4 = peer.ipv4.split('.').map(function(n) {
return +n;
});
peer.ipv6 = peer.ipv6.split(':').slice(5).map(function(n) {
return parseInt(n, 16);
});
peer.ipv4 = utils.readU32BE(peer.ipv4, 0);
peer.ipv6 = utils.readU32BE(peer.ipv6, 0)
* 0x10000 + utils.readU16BE(peer.ipv6, 4);
return peer;
}).filter(Boolean);
return this._write(this.framer.addr(peers));
};
Peer.prototype._getOwnIP = function getOwnIP() {
var interfaces = os.networkInterfaces()
, eth
, ipv4
, ipv6
, ip
, p4
, p6
, l;
ip = {
ipv4: '127.0.0.1',
ipv6: '0000:0000:0000:0000:0000:0000:0000:ffff',
port: 8333,
ver: 0
};
eth = (interfaces.eth0 && interfaces.eth0.length >= 2 && interfaces.eth0)
|| (interfaces.wlan0 && interfaces.wlan0.length >= 2 && interfaces.wlan0)
|| interfaces[Object.keys(interfaces).pop()]
|| [{ address: '127.0.0.1' }, { address: '::1' }];
ipv4 = eth[0] ? eth[0].address : '127.0.0.1';
ipv6 = eth[1] ? eth[1].address : '0000:0000:0000:0000:0000:0000:0000:ffff';
p4 = ipv4.split('.').map(function(n) { return +n; });
p6 = ipv6.split(':').map(function(n) {
return parseInt(n, 16);
}).reduce(function(out, n) {
out.push((n >> 8) & 0xff);
out.push(n & 0xff);
return out;
}, []);
l = p6.length;
// Try to figure out if we're behind a NAT or not public for some reason.
if ((p4[0] !== 127 && p4[1] !== 0 && p4[2] !== 0)
&& (p4[0] !== 10 && p4[1] !== 0)
&& (p4[0] !== 168 && p4[1] !== 0)) {
ip.ipv4 = ipv4;
ip.ver = 4;
} else if ((p6[0] !== 0 && p6[1] !== 0 && p6[2] !== 1)
&& (p6[l-1] !== 0xff && p6[l-2] !== 0xff && p6[l-3] !== 0xff)) {
ip.ipv6 = ipv6;
ip.ver = 6;
} else {
return false;
}
return ip;
};
Peer.prototype._handleInv = function handleInv(items) {
// Always request advertised TXs
var txs = items.filter(function(item) {

View File

@ -319,3 +319,48 @@ Framer.prototype.merkleBlock = function merkleBlock(block) {
// merkleblock here if we have them, as per the offical bitcoin client.
return this.packet(Framer.block(block, 'merkleblock'));
};
Framer.prototype.addr = function addr(peers) {
var p = [];
var i = 0;
var c = 0;
var peer;
// count
if (peers.length < 0xfd) {
p[c++] = peers.length;
} else if (peers.length <= 0xffff) {
p[c++] = 0xfd;
c += utils.writeU16(p, peers.length, c);
} else if (peers.length <= 0xffffffff) {
p[c++] = 0xfe;
c += utils.writeU32(p, peers.length, c);
} else if (peers.length <= 0xffffffffffffffff) {
p[c++] = 0xff;
c += utils.writeU64(p, peers.length, c);
} else {
return;
}
for (; i < peers.length; i++) {
peer = peers[i];
// date
c += utils.writeU32(p, Date.now() / 1000 | 0, c);
// NODE_NETWORK service
c += utils.writeU64(p, 1, c);
// ipv6
c += utils.writeU32BE(p, utils.readU32BE(peer.ipv6, 0), c);
c += utils.writeU16BE(p, utils.readU16BE(peer.ipv6, 4), c);
// ipv4
c += utils.writeU32BE(p, peer.ipv4, c);
// port
c += utils.writeU16BE(peer.port, c);
}
return this.packet('addr', p);
};

View File

@ -158,6 +158,14 @@ utils.readU64 = function readU64(arr, off) {
return utils.readU32(arr, off) + utils.readU32(arr, off + 4) * 0x100000000;
};
utils.writeU16 = function writeU16(dst, num, off) {
if (!off)
off = 0;
dst[off] = num & 0xff;
dst[off + 1] = (num >>> 8) & 0xff;
return 2;
};
utils.writeU32 = function writeU32(dst, num, off) {
if (!off)
off = 0;
@ -168,6 +176,70 @@ utils.writeU32 = function writeU32(dst, num, off) {
return 4;
};
utils.writeU64 = function writeU64(dst, num, off) {
if (!off)
off = 0;
var n = new bn(num);
var left = n.shrn(32);
//var right = n.andln(0xffffffff);
var right = ((n.words[1] & 0xff) << 24) | n.words[0];
if (right < 0) right += 0x100000000;
dst[off] = right & 0xff;
dst[off + 1] = (right >>> 8) & 0xff;
dst[off + 2] = (right >>> 16) & 0xff;
dst[off + 3] = (right >>> 24) & 0xff;
dst[off + 4] = left & 0xff;
dst[off + 5] = (left >>> 8) & 0xff;
dst[off + 6] = (left >>> 16) & 0xff;
dst[off + 7] = (left >>> 24) & 0xff;
return 8;
};
utils.writeU16BE = function writeU16BE(dst, num, off) {
if (!off)
off = 0;
dst[off] = (num >>> 8) & 0xff;
dst[off + 1] = num & 0xff;
return 2;
};
utils.writeU32BE = function writeU32BE(dst, num, off) {
if (!off)
off = 0;
dst[off] = (num >>> 24) & 0xff;
dst[off + 1] = (num >>> 16) & 0xff;
dst[off + 2] = (num >>> 8) & 0xff;
dst[off + 3] = num & 0xff;
return 4;
};
utils.writeU64BE = function writeU64BE(dst, num, off) {
if (!off)
off = 0;
var n = new bn(num);
var left = n.shrn(32);
//var right = n.andln(0xffffffff);
var right = ((n.words[1] & 0xff) << 24) | n.words[0];
if (right < 0) right += 0x100000000;
dst[off] = (left >>> 24) & 0xff;
dst[off + 1] = (left >>> 16) & 0xff;
dst[off + 2] = (left >>> 8) & 0xff;
dst[off + 3] = left & 0xff;
dst[off + 4] = (right >>> 24) & 0xff;
dst[off + 5] = (right >>> 16) & 0xff;
dst[off + 6] = (right >>> 8) & 0xff;
dst[off + 7] = right & 0xff;
return 8;
};
utils.readU16BE = function readU16BE(arr, off) {
if (!off)
off = 0;