version and addr packets.

This commit is contained in:
Christopher Jeffrey 2016-01-13 01:00:29 -08:00
parent a71da261ad
commit 10bd983899
4 changed files with 279 additions and 143 deletions

View File

@ -139,7 +139,7 @@ Peer.prototype._init = function init() {
} }
this._ping.timer = setInterval(function() { this._ping.timer = setInterval(function() {
self.challenge = self._nonce(); self.challenge = utils.nonce();
self._write(self.framer.ping({ self._write(self.framer.ping({
nonce: self.challenge nonce: self.challenge
})); }));
@ -430,7 +430,7 @@ Peer.prototype._handleAddr = function handleAddr(addrs) {
this.emit('addr', { this.emit('addr', {
date: new Date(addr.ts * 1000), date: new Date(addr.ts * 1000),
ts: addr.ts, ts: addr.ts,
service: addr.service, services: addr.services,
ipv4: addr.ipv4, ipv4: addr.ipv4,
ipv6: addr.ipv6, ipv6: addr.ipv6,
host: addr.ipv4, host: addr.ipv4,
@ -462,55 +462,44 @@ Peer.prototype._handlePong = function handlePong(data) {
}; };
Peer.prototype._handleGetAddr = function handleGetAddr() { Peer.prototype._handleGetAddr = function handleGetAddr() {
var used = []; var hosts = {};
var peers, addrs; var peers;
// NOTE: For IPv6 BTC uses:
// '0000:0000:0000:0000:0000:xxxx:xxxx:ffff'
peers = this.pool.peers.all.map(function(peer) { peers = this.pool.peers.all.map(function(peer) {
var ip, version, ipv4, ipv6;
if (!peer.socket || !peer.socket.remoteAddress) if (!peer.socket || !peer.socket.remoteAddress)
return; return;
return {
host: peer.socket.remoteAddress,
port: peer.socket.remotePort || network.port,
ts: peer.ts
};
}).filter(function(peer) {
if (!peer || ~used.indexOf(peer.host))
return;
used.push(peer.host);
return !!peer.host && utils.isIP(peer.host);
}).map(function(peer) {
var ip = peer.host;
var ver = utils.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,
ts: peer.ts,
ver: ver
};
});
addrs = peers.map(function(peer) { ip = peer.socket.remoteAddress;
if (peer.ver === 6) { version = utils.isIP(ip);
while (peer.ipv6.split(':').length < 8)
peer.ipv6 = '0000:' + peer.ipv6; if (!version)
if (peer.ipv6.split(':').length > 8) return;
return;
if (hosts[ip])
return;
hosts[ip] = true;
if (version === 4) {
ipv4 = utils.ip2array(ip, 4);
ipv6 = utils.ip2array(ipv4, 6);
} else if (version === 6) {
ipv6 = utils.ip2array(ip, 6);
ipv4 = utils.ip2array(ipv6, 4);
} }
peer.ipv4 = peer.ipv4.split('.').map(function(n) { return {
return +n; ts: peer.ts,
}); services: peer.version ? peer.version.services : null,
ipv4: ipv4,
peer.ipv6 = utils.toArray(peer.ipv6, 'hex'); ipv6: ipv6,
port: peer.socket.remotePort || network.port
return peer; };
}).filter(Boolean); }).filter(Boolean);
return this._write(this.framer.addr(addrs)); return this._write(this.framer.addr(peers));
}; };
Peer.prototype._handleInv = function handleInv(items) { Peer.prototype._handleInv = function handleInv(items) {
@ -598,14 +587,6 @@ Peer.prototype.reject = function reject(details) {
this._write(this.framer.reject(details)); this._write(this.framer.reject(details));
}; };
Peer.nonce = new bn(0xffffffff).ushln(32).uor(new bn(0xffffffff));
Peer.prototype._nonce = function _nonce() {
var nonce = Peer.nonce.clone();
nonce.imuln(Math.random());
return nonce;
};
/** /**
* Expose * Expose
*/ */

View File

@ -58,22 +58,58 @@ Framer.prototype.packet = function packet(cmd, payload) {
return h.concat(payload); return h.concat(payload);
}; };
Framer.prototype._addr = function addr(buf, off) { Framer.prototype._addr = function addr(p, off, data, full) {
writeU32(buf, 1, off); var start = off;
writeU32(buf, 0, off + 4);
writeU32(buf, 0, off + 8); if (!data)
writeU32(buf, 0, off + 12); data = {};
writeU32(buf, 0xffff0000, off + 16);
writeU32(buf, 0, off + 20); if (!data.ts)
buf[off + 24] = 0; data.ts = utils.now() - (process.uptime() | 0);
buf[off + 25] = 0;
return 26; if (!data.services)
data.services = constants.services.network;
if (!data.ipv4)
data.ipv4 = [];
if (!data.ipv6)
data.ipv6 = [];
if (!data.port)
data.port = network.port;
// timestamp
if (full)
off += utils.writeU32(p, data.ts, off);
// NODE_NETWORK services
off += utils.writeU64(p, data.services, off);
// Empty bytes after services
// (services takes the place of ts)
if (!full)
off += utils.writeU32(p, 0, off);
// ipv6
off += utils.writeU32BE(p, utils.readU32BE(data.ipv6, 0), off);
off += utils.writeU32BE(p, utils.readU32BE(data.ipv6, 4), off);
off += utils.writeU32BE(p, utils.readU32BE(data.ipv6, 8), off);
// ipv4
if (full)
off += utils.writeU32BE(p, utils.readU32BE(data.ipv4, 0), off);
// port
off += utils.writeU16BE(p, data.port, off);
return off - start;
}; };
Framer.prototype.version = function version(packet) { Framer.prototype.version = function version(packet) {
var p = new Array(86 + this.agent.length); var p = new Array(86 + this.agent.length);
var off = 0; var off = 0;
var ts, i; var i;
if (!packet) if (!packet)
packet = {}; packet = {};
@ -82,21 +118,19 @@ Framer.prototype.version = function version(packet) {
off += writeU32(p, constants.version, off); off += writeU32(p, constants.version, off);
// Services // Services
off += writeU32(p, constants.services.network, off); off += utils.writeU64(p, constants.services.network, off);
off += writeU32(p, 0, off);
// Timestamp // Timestamp
ts = utils.now(); off += utils.write64(p, utils.now(), off);
off += writeU32(p, ts, off);
off += writeU32(p, 0, off);
// Remote and local addresses // Their address (recv)
off += this._addr(p, off); off += this._addr(p, off, packet.remote || {});
off += this._addr(p, off);
// Our address (from)
off += this._addr(p, off, packet.local || {});
// Nonce, very dramatic // Nonce, very dramatic
off += writeU32(p, (Math.random() * 0xffffffff) | 0, off); off += utils.writeU64(p, utils.nonce(), off);
off += writeU32(p, (Math.random() * 0xffffffff) | 0, off);
// User-agent // User-agent
assert.equal(off, 80); assert.equal(off, 80);
@ -390,31 +424,20 @@ Framer.prototype.reject = function reject(details) {
Framer.prototype.addr = function addr(peers) { Framer.prototype.addr = function addr(peers) {
var p = []; var p = [];
var off = 0; var off = 0;
var start = utils.now() - (process.uptime() | 0);
var i, peer; var i, peer;
// count
off += utils.writeIntv(p, peers.length, off); off += utils.writeIntv(p, peers.length, off);
for (i = 0; i < peers.length; i++) { for (i = 0; i < peers.length; i++) {
peer = peers[i]; peer = peers[i];
// timestamp off += this._addr(p, off, {
off += utils.writeU32(p, peer.ts || start, off); ts: peer.ts,
services: 1,
// NODE_NETWORK service ipv6: peer.ipv6,
off += utils.writeU64(p, 1, off); ipv4: peer.ipv4,
port: peer.port
// ipv6 }, true);
off += utils.writeU32BE(p, utils.readU32BE(peer.ipv6, 4), off);
off += utils.writeU32BE(p, utils.readU32BE(peer.ipv6, 8), off);
off += utils.writeU32BE(p, utils.readU32BE(peer.ipv6, 12), off);
// ipv4
off += utils.writeU32BE(p, utils.readU32BE(peer.ipv4, 0), off);
// port
off += utils.writeU16BE(p, peer.port, off);
} }
return this.packet('addr', p); return this.packet('addr', p);

View File

@ -167,7 +167,7 @@ Parser.prototype.parsePong = function parsePong(p) {
}; };
Parser.prototype.parseVersion = function parseVersion(p) { Parser.prototype.parseVersion = function parseVersion(p) {
var v, services, ts, nonce, result, off, agent, height, relay; var v, services, ts, recv, from, nonce, result, off, agent, height, relay;
if (p.length < 85) if (p.length < 85)
return this._error('version packet is too small'); return this._error('version packet is too small');
@ -178,6 +178,12 @@ Parser.prototype.parseVersion = function parseVersion(p) {
// Timestamp // Timestamp
ts = utils.read64(p, 12); ts = utils.read64(p, 12);
// Our address (recv)
recv = this._parseAddr(p, 20);
// Their Address (from)
from = this._parseAddr(p, 46);
// Nonce, very dramatic // Nonce, very dramatic
nonce = readU64(p, 72); nonce = readU64(p, 72);
@ -194,10 +200,24 @@ Parser.prototype.parseVersion = function parseVersion(p) {
// Relay // Relay
relay = p.length > off ? p[off] === 1 : true; relay = p.length > off ? p[off] === 1 : true;
try {
ts = ts.toNumber();
} catch (e) {
ts = 0;
}
try {
services = services.toNumber();
} catch (e) {
services = 1;
}
return { return {
v: v, v: v,
services: services, services: services,
ts: ts, ts: ts,
local: recv,
remote: from,
nonce: nonce, nonce: nonce,
agent: utils.stringify(agent), agent: utils.stringify(agent),
height: height, height: height,
@ -490,50 +510,72 @@ Parser.prototype.parseReject = function parseReject(p) {
}; };
}; };
Parser.prototype._parseAddr = function _parseAddr(p, off, full) {
var ts, services, ip, ipv6, ipv4, port;
if (!off)
off = 0;
// timestamp - LE
if (full) {
ts = utils.readU32(p, off);
off += 4;
} else {
ts = 0;
}
// NODE_NETWORK services - LE
services = utils.readU64(p, off);
off += 8;
// Empty bytes after services
// (services takes the place of ts)
if (!full)
off += 4;
// ipv6 - BE
ipv6 = utils.toArray(p.slice(off, off + 12));
off += 12;
// ipv4 - BE
if (full) {
ipv4 = utils.toArray(p.slice(off, off + 4));
off += 4;
}
// port - BE
port = utils.readU16BE(p, off);
off += 2;
try {
services = services.toNumber();
} catch (e) {
services = 1;
}
return {
ts: ts,
services: services,
ipv6: utils.array2ip(ipv6, 6),
ipv4: utils.array2ip(ipv4 || ipv6, 4),
port: port
};
};
Parser.prototype.parseAddr = function parseAddr(p) { Parser.prototype.parseAddr = function parseAddr(p) {
if (p.length < 31) if (p.length < 31)
return this._error('Invalid addr size'); return this._error('Invalid addr size');
var addrs = []; var addrs = [];
var i, len, off, count, ts, service, ipv6, ipv4, port; var i, off, count;
// count count = utils.readIntv(p, 0);
len = utils.readIntv(p, 0); off = count.off;
off = len.off; count = count.r;
count = len.r;
p = p.slice(off); for (i = 0; i < count && off < p.length; i++) {
addrs.push(this._parseAddr(p, off, true));
for (i = 0; i < count && p.length; i++) { off += 30;
// timestamp - LE
ts = utils.readU32(p, 0);
// NODE_NETWORK service - LE
service = utils.readU64(p, 4);
// ipv6 - BE
ipv6 = utils.toHex(p.slice(12, 24));
ipv6 = '::' + ipv6.replace(/(.{4})/g, '$1:').slice(0, -1);
// ipv4 - BE
ipv4 = utils.readU32BE(p, 24);
ipv4 = ((ipv4 >> 24) & 0xff)
+ '.' + ((ipv4 >> 16) & 0xff)
+ '.' + ((ipv4 >> 8) & 0xff)
+ '.' + ((ipv4 >> 0) & 0xff);
// port - BE
port = utils.readU16BE(p, 28);
addrs.push({
ts: ts,
service: service,
ipv6: ipv6,
ipv4: ipv4,
port: port
});
p = p.slice(30);
} }
return addrs; return addrs;

View File

@ -532,15 +532,97 @@ utils.isIP = function isIP(ip) {
if (typeof ip !== 'string') if (typeof ip !== 'string')
return 0; return 0;
if (ip.indexOf('.') !== -1) if (/^\d+\.\d+\.\d+\.\d+$/.test(ip))
return 4; return 4;
if (ip.indexOf(':') !== -1) if (/:[0-9a-f]{1,4}/i.test(ip))
return 6; return 6;
return 0; return 0;
}; };
utils.ip2array = function ip2array(ip, version) {
if (Array.isArray(ip)) {
ip = ip.slice();
utils.assert(version === 4 || version === 6);
if (version === 4) {
ip = ip.slice(-4);
while (ip.length < 4)
ip.unshift(0);
return ip;
}
if (version === 6) {
while (ip.length < 4)
ip.unshift(0);
while (ip.length < 6)
ip.unshift(0xff);
while (ip.length < 16)
ip.unshift(0);
return ip;
}
return;
}
version = utils.isIP(ip);
if (!version)
return ip;
if (version === 4) {
ip = ip.split('.').map(function(n) {
return +n;
});
utils.assert(ip.length <= 4);
return utils.ip2array(ip, 4);
}
if (version === 6) {
ip = utils.toArray(ip.replace(/:/g, ''), 'hex');
utils.assert(ip.length <= 16);
return utils.ip2array(ip, 6);
}
};
utils.array2ip = function array2ip(ip, version) {
var out, i, hi, lo;
if (!Array.isArray(ip))
return ip;
utils.assert(version === 4 || version === 6);
utils.assert(ip.length <= 16);
ip = utils.ip2array(ip, version);
if (version === 4)
return ip.join('.');
if (version === 6) {
out = [];
for (i = 0; i < ip.length; i += 2) {
hi = ip[i].toString(16);
if (hi.length < 2)
hi = '0' + hi;
lo = ip[i + 1].toString(16);
if (lo.length < 2)
lo = '0' + lo;
out.push(hi + lo);
}
return out.join(':');
}
};
utils.isArrayLike = function isArrayLike(msg) { utils.isArrayLike = function isArrayLike(msg) {
return msg return msg
&& !Array.isArray(msg) && !Array.isArray(msg)
@ -737,6 +819,14 @@ utils.hash = function hash(obj, enc) {
throw new Error('Cannot get hash of object'); throw new Error('Cannot get hash of object');
}; };
utils.U64 = new bn(0xffffffff).ushln(32).uor(new bn(0xffffffff));
utils.nonce = function nonce() {
var nonce = utils.U64.clone();
nonce.imuln(Math.random());
return nonce;
};
// //
// Integer Functions // Integer Functions
// //
@ -1011,7 +1101,7 @@ utils.write32BE = function write32BE(dst, num, off) {
}; };
utils.write64 = function write64(dst, num, off) { utils.write64 = function write64(dst, num, off) {
var i; var i, bytes;
if (!(num instanceof bn)) { if (!(num instanceof bn)) {
num = +num; num = +num;
@ -1026,22 +1116,22 @@ utils.write64 = function write64(dst, num, off) {
off = off >>> 0; off = off >>> 0;
num = num.maskn(64).toArray(); bytes = num.maskn(64).toArray();
while (num.length < 8) while (bytes.length < 8)
num.unshift(0); bytes.unshift(0);
if (num.isNeg()) if (num.isNeg())
num[0] |= 0x80; bytes[0] |= 0x80;
for (i = num.length - 1; i >= 0; i--) for (i = bytes.length - 1; i >= 0; i--)
dst[off++] = num[i] & 0xff; dst[off++] = bytes[i] & 0xff;
return 8; return 8;
}; };
utils.write64BE = function write64BE(dst, num, off) { utils.write64BE = function write64BE(dst, num, off) {
var i; var i, bytes;
if (!(num instanceof bn)) { if (!(num instanceof bn)) {
num = +num; num = +num;
@ -1056,16 +1146,16 @@ utils.write64BE = function write64BE(dst, num, off) {
off = off >>> 0; off = off >>> 0;
num = num.maskn(64).toArray(); bytes = num.maskn(64).toArray();
while (num.length < 8) while (bytes.length < 8)
num.unshift(0); bytes.unshift(0);
if (num.isNeg()) if (num.isNeg())
num[0] |= 0x80; bytes[0] |= 0x80;
for (i = 0; i < num.length; i++) for (i = 0; i < bytes.length; i++)
dst[off++] = num[i] & 0xff; dst[off++] = bytes[i] & 0xff;
return 8; return 8;
}; };