telehash
Version:
A telehash library for node and browserify
130 lines (107 loc) • 4.7 kB
JavaScript
var hashname = require('hashname');
var lob = require('lob-enc');
var crypto = require('crypto');
var handshakelib = require('../lib/util/handshake');
var log = require("../lib/util/log")("Peer");
// handle type:peer paths to create peer channels, https://github.com/telehash/telehash.org/blob/v3/v3/channels/peer.md
exports.name = 'peer';
exports.mesh = function(mesh, cbExt)
{
var Pipe = mesh.lib.Pipe;
var pipes = [];
var peer = {};
peer.open = {};
// actually create/return the pipe
function piper(to, cbPiper)
{
if(pipes[to]) return cbPiper(pipes[to]);
// make a new pipe for this peering
var pipe = pipes[to] = new Pipe('peer');
pipe.to = to;
pipe.path = {type:'peer',hn:to};
// handle any peer delivery through the router
function peer_send(packet, link, cbSend)
{
var router = mesh.index[to];
if(!router) return cbSend('cannot peer to an unknown router: '+pipe.to);
if(!router.x) return cbSend('cannot peer yet via this router: '+pipe.to);
if(!link) return cbSend('requires link');
// no packet means try to send our keys
if(!packet)
{
Object.keys(mesh.keys).forEach(function(csid){
if(link.csid && link.csid != csid) return; // if we know the csid, only send that key
var json = {type:'peer',peer:link.hashname,c:router.x.cid()};
var body = lob.encode(hashname.intermediates(mesh.keys), hashname.key(csid, mesh.keys));
var attach = lob.encode({type:'link', csid:csid}, body);
log.debug('sending peer key to',router.hashname,json,csid);
router.x.send({json:json,body:attach});
});
return;
}
// if it's an encrypted channel packet, pass through direct to router
if(packet.head.length == 0) return router.x.sending(packet);
// otherwise we're always creating a new peer channel to carry the request
var json = {type:'peer',peer:link.hashname,c:router.x.cid()};
var body = lob.encode(packet);
log.debug('sending peer handshake to',router.hashname,json,body);
router.x.send({json:json,body:body});
cbSend();
}
pipe.on('send', function(context,a1, a2, a3, a4){
peer_send.call(context,a1,a2,a3,a4)
})
cbPiper(pipe);
}
peer.pipe = function(link, path, cbPipe){
if(path.type != 'peer') return;
if(!hashname.isHashname(path.hn)) return log.warn(link.hashname,'given invalid peer path',path);
// TODO clean up link.json.paths remove any if to a default router
piper(path.hn, cbPipe);
}
// exchange handlers for new opens
peer.open.peer = function(args, open, cbOpen){
// be extra paranoid when routing
var from = this;
if(typeof open.json.peer != 'string') return log.debug('invalid peer request',open.json.peer);
if(open.json.peer == mesh.hashname) return log.debug('invalid peer to self');
var to = mesh.index[open.json.peer];
if(!to) return log.debug('dropping peer, no link to',open.json.peer);
if(!to.x) return log.debug('dropping peer to unknown link',open.json.peer);
if(to.down && from.down) return log.debug('can only route when either to or from is up',to.down,from.down);
// if a handshake, grab the token for routing
var attached = lob.decode(open.body);
if(attached && attached.head.length == 1)
{
var token = crypto.createHash('sha256').update(attached.body.slice(0,16)).digest().slice(0,16).toString('hex');
log.debug('setting route for',token,args.pipe);
mesh.routes[token] = args.pipe;
}
// just forward the body directly as a connect
var json = {type:'connect',c:to.x.cid(),peer:from.hashname};
log.debug('sending connect to',to.hashname,json,open.body);
to.x.send({json:json,body:open.body});
}
// handle incoming connect requests
peer.open.connect = function(args, open, cbOpen){
var via = this;
var attached = lob.decode(open.body);
if(!attached) return log.debug('dropping connect, invalid attached');
if(attached.head.length == 0) log.debug('dropping connect, encrypted attached');
// get the peer pipe
piper(via.hashname, function(pipe){
log.debug('handling connect',attached.head.length==1?'handshake':'keys',pipe.path);
if(attached.head.length == 1)
{
// handle a handshake by delivering via a peer pipe AND the router's pipe
mesh.receive(attached, pipe);
mesh.receive(attached, args.pipe);
}else{
// otherwise try processing it as an un-encrypted handshake
handshakelib.collect(mesh,open.json.peer||via.hashname, attached, pipe);
}
cbOpen();
});
}
cbExt(undefined, peer);
}