-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathlink.c
471 lines (391 loc) · 10.5 KB
/
link.c
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
#include "telehash.h"
#include <string.h>
#include <stdlib.h>
#include "telehash.h"
#include "telehash.h"
link_t link_new(mesh_t mesh, hashname_t id)
{
link_t link;
if(!mesh || !id) return LOG("invalid args");
LOG("adding link %s",hashname_short(id));
if(!(link = malloc(sizeof (struct link_struct)))) return LOG("OOM");
memset(link,0,sizeof (struct link_struct));
link->id = hashname_dup(id);
link->csid = 0x01; // default state
link->mesh = mesh;
link->next = mesh->links;
mesh->links = link;
return link;
}
void link_free(link_t link)
{
if(!link) return;
LOG("dropping link %s",hashname_short(link->id));
mesh_t mesh = link->mesh;
if(mesh->links == link)
{
mesh->links = link->next;
}else{
link_t li;
for(li = mesh->links;li;li = li->next) if(li->next == link)
{
li->next = link->next;
}
}
// drop
if(link->x)
{
e3x_exchange_free(link->x);
link->x = NULL;
}
// notify pipe w/ NULL packet
if(link->send_cb) link->send_cb(link, NULL, link->send_arg);
// go through link->chans
chan_t c, cnext;
for(c = link->chans;c;c = cnext)
{
cnext = chan_next(c);
chan_free(c);
}
hashname_free(link->id);
lob_free(link->key);
lob_free(link->handshake);
free(link);
}
link_t link_get(mesh_t mesh, hashname_t id)
{
link_t link;
if(!mesh || !id) return LOG("invalid args");
for(link = mesh->links;link;link = link->next) if(hashname_cmp(id,link->id) == 0) return link;
return link_new(mesh,id);
}
// simple accessors
hashname_t link_id(link_t link)
{
if(!link) return NULL;
return link->id;
}
lob_t link_key(link_t link)
{
if(!link) return NULL;
return link->key;
}
// get existing channel id if any
chan_t link_chan_get(link_t link, uint32_t id)
{
chan_t c;
if(!link || !id) return NULL;
for(c = link->chans;c;c = chan_next(c)) {
LOG("<%d><%d>",chan_id(c), id);
if(chan_id(c) == id) return c;
}
return NULL;
}
// get link info json
lob_t link_json(link_t link)
{
char hex[3];
lob_t json;
if(!link) return LOG("bad args");
json = lob_new();
lob_set(json,"hashname",hashname_char(link->id));
lob_set(json,"csid",util_hex(&link->csid, 1, hex));
lob_set_base32(json,"key",link->key->body,link->key->body_len);
// paths = lob_array(mesh->paths);
// lob_set_raw(json,"paths",0,(char*)paths->head,paths->head_len);
// lob_free(paths);
return json;
}
link_t link_get_keys(mesh_t mesh, lob_t keys)
{
uint8_t csid;
if(!mesh || !keys) return LOG("invalid args");
csid = hashname_id(mesh->keys,keys);
if(!csid) return LOG("no supported key");
lob_t key = hashname_im(keys,csid);
link_t ret = link_get_key(mesh, key, csid);
lob_free(key);
return ret;
}
link_t link_get_key(mesh_t mesh, lob_t key, uint8_t csid)
{
link_t link;
if(!mesh || !key) return LOG("invalid args");
if(hashname_id(mesh->keys,key) > csid) return LOG("invalid csid");
link = link_get(mesh, hashname_vkey(key, csid));
if(!link) return LOG("invalid key");
// load key if it's not yet
if(!link->key) return link_load(link, csid, key);
return link;
}
// load in the key to existing link
link_t link_load(link_t link, uint8_t csid, lob_t key)
{
char hex[3];
lob_t copy;
if(!link || !csid || !key) return LOG("bad args");
if(link->x)
{
link->csid = link->x->csid; // repair in case mesh_unlink was called, any better place?
return link;
}
LOG("adding %x key to link %s",csid,hashname_short(link->id));
// key must be bin
if(key->body_len)
{
copy = lob_new();
lob_body(copy,key->body,key->body_len);
}else{
util_hex(&csid,1,hex);
copy = lob_get_base32(key,hex);
}
link->x = e3x_exchange_new(link->mesh->self, csid, copy);
if(!link->x)
{
LOG("invalid %x key %s %s",csid,util_hex(copy->body,copy->body_len,NULL),lob_json(key));
lob_free(copy);
return NULL;
}
link->csid = csid;
link->key = copy;
e3x_exchange_out(link->x, util_sys_seconds());
LOG("new exchange session to %s",hashname_short(link->id));
return link;
}
// add a delivery pipe to this link
link_t link_pipe(link_t link, link_t (*send)(link_t link, lob_t packet, void *arg), void *arg)
{
if(!link || !send) return NULL;
if(send == link->send_cb && arg == link->send_arg) return link; // noop
if(link->send_cb) LOG_INFO("replacing existing pipe on link");
link->send_cb = send;
link->send_arg = arg;
// flush handshake
return link_sync(link);
}
// is the link ready/available
link_t link_up(link_t link)
{
if(!link) return NULL;
if(!link->x) return NULL;
if(!e3x_exchange_out(link->x,0)) return NULL;
if(!e3x_exchange_in(link->x,0)) return NULL;
return link;
}
// process an incoming handshake
link_t link_receive_handshake(link_t link, lob_t inner)
{
uint32_t out, at, err;
uint8_t csid = 0;
lob_t outer = lob_linked(inner);
if(!link || !inner || !outer) return LOG("bad args");
// inner/link must be validated by caller already, we just load if missing
if(!link->key)
{
util_unhex(lob_get(inner, "csid"), 2, &csid);
if(!link_load(link, csid, inner))
{
lob_free(inner);
return LOG("load key failed for %s %u %s",hashname_short(link->id),csid,util_hex(inner->body,inner->body_len,NULL));
}
}
if((err = e3x_exchange_verify(link->x,outer)))
{
lob_free(inner);
return LOG("handshake verification fail: %d",err);
}
out = e3x_exchange_out(link->x,0);
at = lob_get_uint(inner,"at");
link_t ready = link_up(link);
// if bad at, always send current handshake
if(e3x_exchange_in(link->x, at) < out)
{
LOG("old handshake: %s (%d,%d,%d)",lob_json(inner),at,out);
link_sync(link);
lob_free(inner);
return link;
}
// try to sync ephemeral key
if(!e3x_exchange_sync(link->x,outer))
{
lob_free(inner);
return LOG("sync failed");
}
// we may need to re-sync
if(out != e3x_exchange_out(link->x,0)) link_sync(link);
// notify of ready state change
if(!ready && link_up(link))
{
LOG("link ready");
mesh_link(link->mesh, link);
}
link->handshake = lob_free(link->handshake);
link->handshake = inner;
return link;
}
// forward declare
chan_t link_process_chan(chan_t c, uint32_t now);
// process a decrypted channel packet
link_t link_receive(link_t link, lob_t inner)
{
chan_t c;
if(!link || !inner) return LOG("bad args");
LOG("<-- %d",lob_get_int(inner,"c"));
// see if existing channel and send there
if((c = link_chan_get(link, lob_get_int(inner,"c"))))
{
LOG("found chan");
// consume inner
chan_receive(c, inner);
// process any changes
link->chans = link_process_chan(link->chans, 0);
return link;
}
// if it's an open, validate and fire event
if(!lob_get(inner,"type"))
{
LOG("invalid channel open, no type %s",lob_json(inner));
lob_free(inner);
return NULL;
}
if(!e3x_exchange_cid(link->x, inner))
{
LOG("invalid channel open id %s",lob_json(inner));
lob_free(inner);
return NULL;
}
inner = mesh_open(link->mesh,link,inner);
if(inner)
{
LOG("unhandled channel open %s",lob_json(inner));
lob_free(inner);
return NULL;
}
return link;
}
// deliver this packet
link_t link_send(link_t link, lob_t outer)
{
if(!outer) return LOG_INFO("send packet missing");
if(!link || !link->send_cb)
{
lob_free(outer);
return LOG_WARN("no network");
}
if(!link->send_cb(link, outer, link->send_arg))
{
lob_free(outer);
return LOG_WARN("delivery failed");
}
return link;
}
lob_t link_handshake(link_t link)
{
if(!link) return NULL;
if(!link->x) return LOG_DEBUG("no exchange");
LOG_DEBUG("generating a new handshake in %lu out %lu",link->x->in,link->x->out);
lob_t handshake = lob_copy(link->mesh->handshake);
lob_t tmp = hashname_im(link->mesh->keys, link->csid);
lob_body(handshake, lob_raw(tmp), lob_len(tmp));
lob_free(tmp);
// encrypt it
tmp = handshake;
handshake = e3x_exchange_handshake(link->x, tmp);
lob_free(tmp);
return handshake;
}
// send current handshake
link_t link_sync(link_t link)
{
if(!link) return LOG("bad args");
if(!link->x) return LOG("no exchange");
if(!link->send_cb) return LOG("no network");
return link_send(link, link_handshake(link));
}
// trigger a new exchange sync
link_t link_resync(link_t link)
{
if(!link) return LOG("bad args");
if(!link->x) return LOG("no exchange");
// force a higher at, triggers all to sync
e3x_exchange_out(link->x,e3x_exchange_out(link->x,0)+1);
return link_sync(link);
}
// create/track a new channel for this open
chan_t link_chan(link_t link, lob_t open)
{
chan_t c;
if(!link || !open) return LOG("bad args");
// add an outgoing cid if none set
if(!lob_get_int(open,"c")) lob_set_uint(open,"c",e3x_exchange_cid(link->x, NULL));
c = chan_new(open);
if(!c) return LOG("invalid open %s",lob_json(open));
LOG("new outgoing channel %d open: %s",chan_id(c), lob_get(open,"type"));
c->link = link;
c->next = link->chans;
link->chans = c;
return c;
}
// encrypt and send this one packet on this pipe
link_t link_direct(link_t link, lob_t inner)
{
if(!link || !inner) return LOG("bad args");
if(!link->send_cb)
{
LOG_WARN("no network, dropping %s",lob_json(inner));
return NULL;
}
// add an outgoing cid if none set
if(!lob_get_int(inner,"c")) lob_set_uint(inner,"c",e3x_exchange_cid(link->x, NULL));
lob_t outer = e3x_exchange_send(link->x, inner);
lob_free(inner);
return link_send(link, outer);
}
// force link down, end channels and generate all events
link_t link_down(link_t link)
{
if(!link) return NULL;
LOG("forcing link down for %s",hashname_short(link->id));
// generate down event if up
if(link_up(link))
{
e3x_exchange_down(link->x);
mesh_link(link->mesh, link);
}
// end all channels
chan_t c, cnext;
for(c = link->chans;c;c = cnext)
{
cnext = chan_next(c);
chan_err(c, "disconnected");
chan_process(c, 0);
}
// remove pipe
if(link->send_cb)
{
link->send_cb(link, NULL, link->send_arg); // notify jic
link->send_cb = NULL;
link->send_arg = NULL;
}
return NULL;
}
// recursive to handle deletes
chan_t link_process_chan(chan_t c, uint32_t now)
{
if(!c) return NULL;
chan_t next = link_process_chan(chan_next(c), now);
if(!chan_process(c, now)) return next;
c->next = next;
return c;
}
// process any channel timeouts based on the current/given time
link_t link_process(link_t link, uint32_t now)
{
if(!link || !now) return LOG("bad args");
link->chans = link_process_chan(link->chans, now);
if(link->csid) return link;
// flagged to remove, do that now
link_down(link);
link_free(link);
return NULL;
}