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

View File

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

View File

@ -167,7 +167,7 @@ Parser.prototype.parsePong = function parsePong(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)
return this._error('version packet is too small');
@ -178,6 +178,12 @@ Parser.prototype.parseVersion = function parseVersion(p) {
// Timestamp
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 = readU64(p, 72);
@ -194,10 +200,24 @@ Parser.prototype.parseVersion = function parseVersion(p) {
// Relay
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 {
v: v,
services: services,
ts: ts,
local: recv,
remote: from,
nonce: nonce,
agent: utils.stringify(agent),
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) {
if (p.length < 31)
return this._error('Invalid addr size');
var addrs = [];
var i, len, off, count, ts, service, ipv6, ipv4, port;
var i, off, count;
// count
len = utils.readIntv(p, 0);
off = len.off;
count = len.r;
count = utils.readIntv(p, 0);
off = count.off;
count = count.r;
p = p.slice(off);
for (i = 0; i < count && p.length; i++) {
// 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);
for (i = 0; i < count && off < p.length; i++) {
addrs.push(this._parseAddr(p, off, true));
off += 30;
}
return addrs;

View File

@ -532,15 +532,97 @@ utils.isIP = function isIP(ip) {
if (typeof ip !== 'string')
return 0;
if (ip.indexOf('.') !== -1)
if (/^\d+\.\d+\.\d+\.\d+$/.test(ip))
return 4;
if (ip.indexOf(':') !== -1)
if (/:[0-9a-f]{1,4}/i.test(ip))
return 6;
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) {
return msg
&& !Array.isArray(msg)
@ -737,6 +819,14 @@ utils.hash = function hash(obj, enc) {
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
//
@ -1011,7 +1101,7 @@ utils.write32BE = function write32BE(dst, num, off) {
};
utils.write64 = function write64(dst, num, off) {
var i;
var i, bytes;
if (!(num instanceof bn)) {
num = +num;
@ -1026,22 +1116,22 @@ utils.write64 = function write64(dst, num, off) {
off = off >>> 0;
num = num.maskn(64).toArray();
bytes = num.maskn(64).toArray();
while (num.length < 8)
num.unshift(0);
while (bytes.length < 8)
bytes.unshift(0);
if (num.isNeg())
num[0] |= 0x80;
bytes[0] |= 0x80;
for (i = num.length - 1; i >= 0; i--)
dst[off++] = num[i] & 0xff;
for (i = bytes.length - 1; i >= 0; i--)
dst[off++] = bytes[i] & 0xff;
return 8;
};
utils.write64BE = function write64BE(dst, num, off) {
var i;
var i, bytes;
if (!(num instanceof bn)) {
num = +num;
@ -1056,16 +1146,16 @@ utils.write64BE = function write64BE(dst, num, off) {
off = off >>> 0;
num = num.maskn(64).toArray();
bytes = num.maskn(64).toArray();
while (num.length < 8)
num.unshift(0);
while (bytes.length < 8)
bytes.unshift(0);
if (num.isNeg())
num[0] |= 0x80;
bytes[0] |= 0x80;
for (i = 0; i < num.length; i++)
dst[off++] = num[i] & 0xff;
for (i = 0; i < bytes.length; i++)
dst[off++] = bytes[i] & 0xff;
return 8;
};