-
Notifications
You must be signed in to change notification settings - Fork 48
/
peer.js
129 lines (107 loc) · 4.58 KB
/
peer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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);
}