// open must be e3x_channel_receive or e3x_channel_send next yet e3x_channel_t e3x_channel_new(lob_t open) { uint32_t id; char *type; e3x_channel_t c; if(!open) return LOG("open packet required"); id = (uint32_t)lob_get_int(open,"c"); if(!id) return LOG("invalid channel id"); type = lob_get(open,"type"); if(!type) return LOG("missing channel type"); c = malloc(sizeof (struct e3x_channel_struct)); memset(c,0,sizeof (struct e3x_channel_struct)); c->state = OPENING; c->id = id; sprintf(c->c,"%u",id); c->open = lob_copy(open); c->type = lob_get(open,"type"); c->capacity = 1024*1024; // 1MB total default // generate a unique id in hex _uids++; util_hex((uint8_t*)&_uids,4,c->uid); // reliability if(lob_get(open,"seq")) { c->seq = 1; } LOG("new %s channel %s %d %s",c->seq?"reliable":"unreliable",c->uid,id,type); return c; }
// process a decrypted channel packet link_t link_receive(link_t link, lob_t inner, pipe_t pipe) { chan_t chan; if(!link || !inner) return LOG("bad args"); // see if existing channel and send there if((chan = xht_get(link->index, lob_get(inner,"c")))) { if(channel3_receive(chan->c3, inner)) return LOG("channel receive error, dropping %s",lob_json(inner)); link_pipe(link,pipe); // we trust the pipe at this point if(chan->handle) chan->handle(link, chan->c3, chan->arg); // check if there's any packets to be sent back return link_flush(link, chan->c3, NULL); } // if it's an open, validate and fire event if(!lob_get(inner,"type")) return LOG("invalid channel open, no type %s",lob_json(inner)); if(!exchange3_cid(link->x, inner)) return LOG("invalid channel open id %s",lob_json(inner)); link_pipe(link,pipe); // we trust the pipe at this point inner = mesh_open(link->mesh,link,inner); if(inner) { LOG("unhandled channel open %s",lob_json(inner)); lob_free(inner); return NULL; } return link; }
int main(int argc, char **argv) { fail_unless(e3x_init(NULL) == 0); lob_t id = e3x_generate(); fail_unless(id); e3x_self_t self = e3x_self_new(id,NULL); fail_unless(self); int i, count = 0; for(i = 0; i < CS_MAX; i++) { if(!self->locals[i]) continue; LOG("self testing CS %d",i); count++; fail_unless(self->locals[i]); fail_unless(self->keys[i]); fail_unless(self->keys[i]->body_len); fail_unless(lob_get(self->keys[i],"key")); fail_unless(lob_get(self->keys[i],"hash")); fail_unless(strlen(lob_get(self->keys[i],"hash")) == 52); } fail_unless(count); e3x_self_free(self); lob_free(id); return 0; }
pipe_t tcp4_path(link_t link, lob_t path) { net_tcp4_t net; char *ip; int port; // just sanity check the path first if(!link || !path) return NULL; if(!(net = xht_get(link->mesh->index, MUID))) return NULL; if(util_cmp("tcp4",lob_get(path,"type"))) return NULL; if(!(ip = lob_get(path,"ip"))) return LOG("missing ip"); if((port = lob_get_int(path,"port")) <= 0) return LOG("missing port"); return tcp4_pipe(net, ip, port); }
// process an incoming handshake link_t link_receive_handshake(link_t link, lob_t inner, pipe_t pipe) { link_t ready; uint32_t out, err; seen_t seen; uint8_t csid = 0; char *hexid; lob_t attached, outer = lob_linked(inner); if(!link || !inner || !outer) return LOG("bad args"); hexid = lob_get(inner, "csid"); if(!lob_get(link->mesh->keys, hexid)) return LOG("unsupported csid %s",hexid); util_unhex(hexid, 2, &csid); attached = lob_parse(inner->body, inner->body_len); if(!link->key && link_key(link->mesh, attached, csid) != link) return LOG("invalid/mismatch link handshake"); if((err = e3x_exchange_verify(link->x,outer))) return LOG("handshake verification fail: %d",err); out = e3x_exchange_out(link->x,0); ready = link_up(link); // if bad at, always send current handshake if(e3x_exchange_in(link->x, lob_get_uint(inner,"at")) < out) { LOG("old/bad at: %s (%d,%d,%d)",lob_json(inner),lob_get_int(inner,"at"),e3x_exchange_in(link->x,0),e3x_exchange_out(link->x,0)); // just reset pipe seen and call link_sync to resend handshake for(seen = link->pipes;pipe && seen;seen = seen->next) if(seen->pipe == pipe) seen->at = 0; lob_free(link_sync(link)); return NULL; } // trust/add this pipe if(pipe) link_pipe(link,pipe); // try to sync ephemeral key if(!e3x_exchange_sync(link->x,outer)) return LOG("sync failed"); // we may need to re-sync if(out != e3x_exchange_out(link->x,0)) lob_free(link_sync(link)); // notify of ready state change if(!ready && link_up(link)) { LOG("link ready"); mesh_link(link->mesh, link); } return link; }
// add a custom outgoing handshake packet to all links mesh_t mesh_handshake(mesh_t mesh, lob_t handshake) { if(!mesh) return NULL; if(handshake && !lob_get(handshake,"type")) return LOG("handshake missing a type: %s",lob_json(handshake)); mesh->handshakes = lob_link(handshake, mesh->handshakes); return mesh; }
link_t mesh_add(mesh_t mesh, lob_t json, pipe_t pipe) { link_t link; lob_t keys, paths; uint8_t csid; if(!mesh || !json) return LOG("bad args"); LOG("mesh add %s",lob_json(json)); link = link_get(mesh, hashname_vchar(lob_get(json,"hashname"))); keys = lob_get_json(json,"keys"); paths = lob_get_array(json,"paths"); if(!link) link = link_keys(mesh, keys); if(!link) LOG("no hashname"); LOG("loading keys/paths"); if(keys && (csid = hashname_id(mesh->keys,keys))) link_load(link, csid, keys); // handle any pipe/paths if(pipe) link_pipe(link, pipe); lob_t path; for(path=paths;path;path = lob_next(path)) link_path(link,path); lob_free(keys); lob_freeall(paths); return link; }
// this will set the default inactivity timeout using this event timer and our uid uint32_t e3x_channel_timeout(e3x_channel_t c, e3x_event_t ev, uint32_t timeout) { if(!c) return 0; // un-set any if(ev != c->ev) { // cancel and clearn any previous timer state c->timeout = 0; if(c->ev) e3x_event_set(c->ev,NULL,c->uid,0); c->ev = NULL; lob_free(c->timer); c->timer = NULL; } // no event manager, no timeouts if(!ev) return 0; // no timeout, just return how much time is left if(!timeout) return _time_left(c); // add/update new timeout c->tsince = util_sys_seconds(); // start timer now c->timeout = timeout; c->ev = ev; c->timer = lob_new(); lob_set_uint(c->timer,"c",c->id); lob_set(c->timer, "id", c->uid); lob_set(c->timer, "err", "timeout"); e3x_event_set(c->ev, c->timer, lob_get(c->timer, "id"), timeout*1000); // ms in the future return _time_left(c); }
// create/track a new channel for this open channel3_t link_channel(link_t link, lob_t open) { chan_t chan; channel3_t c3; if(!link || !open) return LOG("bad args"); // add an outgoing cid if none set if(!lob_get_int(open,"c")) lob_set_int(open,"c",exchange3_cid(link->x, NULL)); c3 = channel3_new(open); if(!c3) return LOG("invalid open %s",lob_json(open)); LOG("new outgoing channel open: %s",lob_get(open,"type")); // add this channel to the link's channel index if(!(chan = malloc(sizeof (struct chan_struct)))) { channel3_free(c3); return LOG("OOM"); } memset(chan,0,sizeof (struct chan_struct)); chan->c3 = c3; xht_set(link->channels, channel3_uid(c3), chan); xht_set(link->index, channel3_c(c3), chan); return c3; }
// 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; }
int main(int argc, char **argv) { lob_t opts = lob_new(); fail_unless(e3x_init(opts) == 0); fail_unless(!lob_get(opts,"err")); fail_unless(!e3x_err()); lob_free(opts); return 0; }
uint8_t e3x_cipher_init(lob_t options) { e3x_cipher_default = NULL; memset(e3x_cipher_sets, 0, CS_MAX * sizeof(e3x_cipher_t)); e3x_cipher_sets[CS_1a] = cs1a_init(options); if(e3x_cipher_sets[CS_1a]) e3x_cipher_default = e3x_cipher_sets[CS_1a]; if(lob_get(options, "err")) return 1; e3x_cipher_sets[CS_2a] = cs2a_init(options); if(e3x_cipher_sets[CS_2a]) e3x_cipher_default = e3x_cipher_sets[CS_2a]; if(lob_get(options, "err")) return 1; e3x_cipher_sets[CS_3a] = cs3a_init(options); if(e3x_cipher_sets[CS_3a]) e3x_cipher_default = e3x_cipher_sets[CS_3a]; if(lob_get(options, "err")) return 1; return 0; }
uint8_t cipher3_init(lob_t options) { cipher3_default = NULL; memset(cipher3_sets, 0, CS_MAX * sizeof(cipher3_t)); cipher3_sets[CS_1a] = cs1a_init(options); if(cipher3_sets[CS_1a]) cipher3_default = cipher3_sets[CS_1a]; if(lob_get(options, "err")) return 1; cipher3_sets[CS_2a] = cs2a_init(options); if(cipher3_sets[CS_2a]) cipher3_default = cipher3_sets[CS_2a]; if(lob_get(options, "err")) return 1; cipher3_sets[CS_3a] = cs3a_init(options); if(cipher3_sets[CS_3a]) cipher3_default = cipher3_sets[CS_3a]; if(lob_get(options, "err")) return 1; return 0; }
// query the cache of handshakes for a matching one with a specific type lob_t mesh_handshakes(mesh_t mesh, lob_t handshake, char *type) { lob_t matched; char *id; if(!mesh || !type || !(id = lob_get(handshake,"id"))) return LOG("bad args"); for(matched = mesh->cached;matched;matched = lob_match(matched->next,"id",id)) { if(lob_get_cmp(matched,"type",type) == 0) return matched; } return NULL; }
uint8_t e3x_exchange_validate(e3x_exchange_t x, lob_t args, lob_t sig, uint8_t *data, size_t len) { remote_t remote = NULL; e3x_cipher_t cs = NULL; char *alg = lob_get(args,"alg"); if(!data || !len || !alg) return 1; cs = e3x_cipher_set(0,alg); if(!cs || !cs->remote_validate) { LOG("no validate support for %s",alg); return 1; } if(x && x->cs == cs) remote = x->remote; return cs->remote_validate(remote,args,sig,data,len); }
// 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; }
uint8_t hashname_id(lob_t a, lob_t b) { uint8_t id, best; uint32_t i; char *key; if(!a || !b) return 0; best = 0; for(i=0;(key = lob_get_index(a,i));i+=2) { if(strlen(key) != 2) continue; if(!lob_get(b,key)) continue; id = 0; util_unhex(key,2,&id); if(id > best) best = id; } return best; }
// 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; }
// get paths from host and query lob_t util_uri_paths(lob_t uri) { uint32_t i; uint16_t port; uint8_t *buf; size_t len; char *key, *value; lob_t paths, query = lob_linked(uri); if(!query) return NULL; paths = NULL; // gen paths from host/port if((port = lob_get_uint(uri,"port"))) { key = lob_get(uri,"host"); paths = lob_chain(paths); lob_set(paths,"type","upd4"); lob_set(paths,"ip",key); lob_set_uint(paths,"port",port); paths = lob_chain(paths); lob_set(paths,"type","tcp4"); lob_set(paths,"ip",key); lob_set_uint(paths,"port",port); } // loop through all keyval pairs to find paths buf = NULL; for(i=0;(key = lob_get_index(query,i));i+=2) { value = lob_get_index(query,i+1); if(util_cmp(key,"paths") != 0 || !value) continue; len = base32_decode_floor(strlen(value)); buf = util_reallocf(buf,len); if(!buf) continue; if(base32_decode(value,strlen(value),buf,len) < len) continue; paths = lob_link(lob_parse(buf,len), paths); } free(buf); return paths; }
int main(int argc, char **argv) { lob_t id; mesh_t mesh; lob_t opts = lob_new(); fail_unless(e3x_init(opts) == 0); fail_unless(!e3x_err()); // need cs1a support to continue testing e3x_cipher_t cs = e3x_cipher_set(0x1a,NULL); if(!cs) return 0; cs = e3x_cipher_set(0,"1a"); fail_unless(cs); fail_unless(cs->id == CS_1a); uint8_t buf[32]; fail_unless(e3x_rand(buf,32)); char hex[65]; util_hex(e3x_hash((uint8_t*)"foo",3,buf),32,hex); fail_unless(strcmp(hex,"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae") == 0); id = util_fjson("/Users/chrigel/.id.json"); if(!id) return -1; lob_t secrets = lob_get_json(id,"secrets"); fail_unless(secrets); fail_unless(lob_get(secrets,"1a")); lob_t keys = lob_get_json(id,"keys"); fail_unless(keys); fail_unless(lob_get(keys,"1a")); LOG("generated key %s secret %s",lob_get(keys,"1a"),lob_get(secrets,"1a")); local_t localA = cs->local_new(keys,secrets); fail_unless(localA); remote_t remoteA = cs->remote_new(lob_get_base32(keys,"1a"), NULL); fail_unless(remoteA); // create another to start testing real packets lob_t secretsB = e3x_generate(); fail_unless(lob_linked(secretsB)); printf("XX %s\n",lob_json(lob_linked(secretsB))); local_t localB = cs->local_new(lob_linked(secretsB),secretsB); fail_unless(localB); remote_t remoteB = cs->remote_new(lob_get_base32(lob_linked(secretsB),"1a"), NULL); fail_unless(remoteB); // generate a message lob_t messageAB = lob_new(); lob_set_int(messageAB,"a",42); lob_t outerAB = cs->remote_encrypt(remoteB,localA,messageAB); fail_unless(outerAB); fail_unless(lob_len(outerAB) == 42); // decrypt and verify it lob_t innerAB = cs->local_decrypt(localB,outerAB); fail_unless(innerAB); fail_unless(lob_get_int(innerAB,"a") == 42); fail_unless(cs->remote_verify(remoteA,localB,outerAB) == 0); ephemeral_t ephemBA = cs->ephemeral_new(remoteA,outerAB); fail_unless(ephemBA); lob_t channelBA = lob_new(); lob_set(channelBA,"type","foo"); lob_t couterBA = cs->ephemeral_encrypt(ephemBA,channelBA); fail_unless(couterBA); fail_unless(lob_len(couterBA) == 42); lob_t outerBA = cs->remote_encrypt(remoteA,localB,messageAB); fail_unless(outerBA); ephemeral_t ephemAB = cs->ephemeral_new(remoteB,outerBA); fail_unless(ephemAB); lob_t cinnerAB = cs->ephemeral_decrypt(ephemAB,couterBA); fail_unless(cinnerAB); fail_unless(util_cmp(lob_get(cinnerAB,"type"),"foo") == 0); return 0; }
int main(int argc, char **argv) { lob_t packet; packet = lob_new(); fail_unless(packet); lob_free(packet); uint8_t buf[1024]; char *hex = "001d7b2274797065223a2274657374222c22666f6f223a5b22626172225d7d616e792062696e61727921"; uint8_t len = strlen(hex)/2; util_unhex(hex,strlen(hex),buf); packet = lob_parse(buf,len); fail_unless(packet); fail_unless(lob_len(packet)); fail_unless(packet->head_len == 29); fail_unless(packet->body_len == 11); fail_unless(util_cmp(lob_get(packet,"type"),"test") == 0); fail_unless(util_cmp(lob_get(packet,"foo"),"[\"bar\"]") == 0); lob_free(packet); packet = lob_new(); lob_set_base32(packet,"32",buf,len); fail_unless(lob_get(packet,"32")); fail_unless(strlen(lob_get(packet,"32")) == (base32_encode_length(len)-1)); lob_t bin = lob_get_base32(packet,"32"); fail_unless(bin); fail_unless(bin->body_len == len); lob_set(packet,"key","value"); fail_unless(lob_keys(packet) == 2); // test sorting lob_set(packet,"zz","value"); lob_set(packet,"a","value"); lob_set(packet,"z","value"); lob_sort(packet); fail_unless(util_cmp(lob_get_index(packet,0),"32") == 0); fail_unless(util_cmp(lob_get_index(packet,2),"a") == 0); fail_unless(util_cmp(lob_get_index(packet,4),"key") == 0); fail_unless(util_cmp(lob_get_index(packet,6),"z") == 0); fail_unless(util_cmp(lob_get_index(packet,8),"zz") == 0); lob_free(packet); // minimal comparison test lob_t a = lob_new(); lob_set(a,"foo","bar"); lob_t b = lob_new(); lob_set(b,"foo","bar"); fail_unless(lob_cmp(a,b) == 0); lob_set(b,"bar","foo"); fail_unless(lob_cmp(a,b) != 0); // lots of basic list testing lob_t list = lob_new(); lob_t item = lob_new(); fail_unless(lob_push(list,item)); fail_unless(lob_pop(list) == item); list = item->next; fail_unless((list = lob_unshift(list,item))); fail_unless(lob_shift(list) == item); list = item->next; fail_unless(lob_push(list,item)); fail_unless(list->next == item); lob_t insert = lob_new(); fail_unless(lob_insert(list,list,insert)); fail_unless(list->next == insert); fail_unless(insert->next == item); fail_unless(lob_splice(list,insert)); fail_unless(list->next == item); lob_t array = lob_array(list); fail_unless(array); fail_unless(util_cmp(lob_json(array),"[,]") == 0); fail_unless(lob_freeall(list) == NULL); // simple index testing lob_t index = lob_new(); lob_t c1 = lob_new(); lob_set(c1,"id","c1"); lob_push(index,c1); lob_t c2 = lob_new(); lob_set(c2,"id","c2"); lob_push(index,c2); fail_unless(lob_match(index,"id","c1") == c1); fail_unless(lob_match(index,"id","c2") == c2); float f = 42.42; lob_t ft = lob_new(); lob_head(ft,(uint8_t*)"{\"foo\":42.42}",13); fail_unless(lob_get_float(ft,"foo") == f); lob_set_float(ft,"bar2",f,2); fail_unless(lob_get_float(ft,"bar2") == f); lob_set_float(ft,"bar1",f,1); fail_unless(lob_get_cmp(ft,"bar1","42.4") == 0); lob_set_float(ft,"bar0",f,0); fail_unless(lob_get_int(ft,"bar0") == 42); LOG("floats %s",lob_json(ft)); return 0; }
// process any unencrypted handshake packet link_t mesh_receive_handshake(mesh_t mesh, lob_t handshake, pipe_t pipe) { uint32_t now; hashname_t from; link_t link; if(!mesh || !handshake) return LOG("bad args"); if(!lob_get(handshake,"id")) { LOG("bad handshake, no id: %s",lob_json(handshake)); lob_free(handshake); return NULL; } now = util_sys_seconds(); // normalize handshake handshake->id = now; // save when we cached it if(!lob_get(handshake,"type")) lob_set(handshake,"type","link"); // default to link type if(!lob_get_uint(handshake,"at")) lob_set_uint(handshake,"at",now); // require an at LOG("handshake at %d id %s",now,lob_get(handshake,"id")); // validate/extend link handshakes immediately if(util_cmp(lob_get(handshake,"type"),"link") == 0) { // get the csid uint8_t csid = 0; lob_t outer; if((outer = lob_linked(handshake))) { csid = outer->head[0]; }else if(lob_get(handshake,"csid")){ util_unhex(lob_get(handshake,"csid"),2,&csid); } if(!csid) { LOG("bad link handshake, no csid: %s",lob_json(handshake)); lob_free(handshake); return NULL; } char hexid[3] = {0}; util_hex(&csid, 1, hexid); // get attached hashname lob_t tmp = lob_parse(handshake->body, handshake->body_len); from = hashname_vkey(tmp, csid); if(!from) { LOG("bad link handshake, no hashname: %s",lob_json(handshake)); lob_free(tmp); lob_free(handshake); return NULL; } lob_set(handshake,"csid",hexid); lob_set(handshake,"hashname",hashname_char(from)); lob_set_raw(handshake,hexid,2,"true",4); // intermediate format lob_body(handshake, tmp->body, tmp->body_len); // re-attach as raw key lob_free(tmp); // short-cut, if it's a key from an existing link, pass it on // TODO: using mesh_linked here is a stack issue during loopback peer test! if((link = mesh_linkid(mesh,from))) return link_receive_handshake(link, handshake, pipe); LOG("no link found for handshake from %s",hashname_char(from)); // extend the key json to make it compatible w/ normal patterns tmp = lob_new(); lob_set_base32(tmp,hexid,handshake->body,handshake->body_len); lob_set_raw(handshake,"keys",0,(char*)tmp->head,tmp->head_len); lob_free(tmp); // add the path if one if(pipe && pipe->path) { char *paths = malloc(pipe->path->head_len+3); sprintf(paths,"[%.*s]",(int)pipe->path->head_len,(char*)pipe->path->head); lob_set_raw(handshake,"paths",0,paths,pipe->path->head_len+2); free(paths); } } // always add to the front of the cached list if needed in the future mesh->cached = lob_unshift(mesh->cached, handshake); // tell anyone listening about the newly discovered handshake mesh_discover(mesh, handshake, pipe); return NULL; }
// this is a very simple single-pass telehash uri parser, no magic lob_t util_uri_parse(char *encoded) { size_t klen, vlen; char *at, *val; lob_t uri; if(!encoded) return LOG("bad args"); uri = lob_new(); lob_set(uri,"orig",encoded); // check for protocol:// prefix first if(!(at = strstr(encoded,"://"))) { lob_set(uri, "protocol", "link"); }else{ lob_set_len(uri, "protocol", 0, encoded, (size_t)(at - encoded)); encoded = at+3; } // check for user@ prefix next if((at = strchr(encoded,'@'))) { lob_set_len(uri, "auth", 0, encoded, (size_t)(at - encoded)); encoded = at+1; } // ensure there's at least a host if(!strlen(encoded) || !isalnum((int)encoded[0])) { lob_free(uri); return LOG("invalid host: '%s'",encoded); } // copy in host and parse hostname/port if((at = strchr(encoded,'/')) || (at = strchr(encoded,'?')) || (at = strchr(encoded,'#'))) { lob_set_len(uri, "host", 0, encoded, (size_t)(at - encoded)); }else{ lob_set_len(uri, "host", 0, encoded, strlen(encoded)); } // hostname+port val = lob_get(uri, "host"); if(val && (at = strchr(val,':'))) { lob_set_len(uri, "hostname", 0, val, (size_t)(at - val)); lob_set_uint(uri, "port", (uint16_t)strtoul(at+1,NULL,10)); }else{ lob_set(uri, "hostname", val); } // optional path if((at = strchr(encoded,'/'))) { encoded = at; if((at = strchr(encoded+1,'?')) || (at = strchr(encoded+1,'#'))) { lob_set_len(uri, "path", 0, encoded, (size_t)(at - encoded)); }else{ lob_set_len(uri, "path", 0, encoded, strlen(encoded)); } } // optional hash at the end if((at = strchr(encoded,'#'))) { lob_set_len(uri, "hash", 0, at+1, strlen(at+1)); } // optional query string if((at = strchr(encoded,'?'))) { uri->chain = lob_new(); encoded = at+1; if((at = strchr(encoded,'#'))) { klen = (size_t)(at - encoded); }else{ klen = strlen(encoded); } while(klen) { // skip any separator if(*encoded == '&') { encoded++; klen--; } // require the equals if(!(val = strchr(encoded,'='))) break; val++; if((at = strchr(val,'&'))) { vlen = (size_t)(at - val); }else{ vlen = strlen(val); } lob_set_len(uri->chain, encoded, (size_t)(val-encoded)-1, val, vlen); // skip past whole block klen -= (size_t)((val+vlen) - encoded); encoded = val + vlen; } } return uri; }
// serialize out from lob format to "uri" key and return it char *util_uri_format(lob_t uri) { char *part, *key, *value; uint32_t i, prev = 0; lob_t buf, query; if(!uri) return NULL; // use a lob body as the buffer to build it up buf = lob_new(); part = lob_get(uri, "protocol"); if(part) { lob_append_str(buf, part); }else{ lob_append_str(buf, "link"); } lob_append_str(buf, "://"); part = lob_get(uri, "hostname"); if(part) { lob_append_str(buf, part); part = lob_get(uri, "port"); if(part) { lob_append_str(buf, ":"); lob_append_str(buf, part); } }else{ part = lob_get(uri, "host"); if(part) lob_append_str(buf, part); } part = lob_get(uri, "path"); if(part) { lob_append_str(buf, part); }else{ lob_append_str(buf, "/"); } // append on any query string for(query = lob_linked(uri); query; query = lob_linked(query)) { for(i=0;(key = lob_get_index(query,i));i+=2) { value = lob_get_index(query,i+1); if(!strlen(key) || !value) continue; // paranoid lob_append_str(buf,(prev++)?"&":"?"); lob_append_str(buf,key); lob_append_str(buf,"="); lob_append_str(buf,value); } } if((part = lob_get(uri, "hash"))) { lob_append_str(buf, "#"); lob_append_str(buf, part); } lob_set_len(uri,"uri",3,(char*)buf->body,buf->body_len); lob_free(buf); return lob_get(uri,"uri"); }
// processes incoming packet, it will take ownership of p link_t mesh_receive(mesh_t mesh, lob_t outer, pipe_t pipe) { lob_t inner; link_t link; char token[17]; hashname_t id; if(!mesh || !outer || !pipe) return LOG("bad args"); LOG("mesh receiving %s to %s via pipe %s",outer->head_len?"handshake":"channel",hashname_short(mesh->id),pipe->id); // process handshakes if(outer->head_len == 1) { inner = e3x_self_decrypt(mesh->self, outer); if(!inner) { LOG("%02x handshake failed %s",outer->head[0],e3x_err()); lob_free(outer); return NULL; } // couple the two together, inner->outer lob_link(inner,outer); // set the unique id string based on some of the first 16 (routing token) bytes in the body base32_encode(outer->body,10,token,17); lob_set(inner,"id",token); // process the handshake return mesh_receive_handshake(mesh, inner, pipe); } // handle channel packets if(outer->head_len == 0) { if(outer->body_len < 16) { LOG("packet too small %d",outer->body_len); lob_free(outer); return NULL; } route_t route; for(route = mesh->routes;route;route = route->next) if(memcmp(route->token,outer->body,8) == 0) break; link = route ? route->link : NULL; if(!link) { LOG("dropping, no link for token %s",util_hex(outer->body,16,NULL)); lob_free(outer); return NULL; } // forward packet if(!route->flag) { LOG("forwarding route token %s via %s len %d",util_hex(route->token,8,NULL),hashname_short(link->id),lob_len(outer)); return link_send(link, outer); } inner = e3x_exchange_receive(link->x, outer); lob_free(outer); if(!inner) return LOG("channel decryption fail for link %s %s",hashname_short(link->id),e3x_err()); LOG("channel packet %d bytes from %s",lob_len(inner),hashname_short(link->id)); return link_receive(link,inner,pipe); } // transform incoming bare link json format into handshake for discovery if((inner = lob_get_json(outer,"keys"))) { if((id = hashname_vkeys(inner))) { lob_set(outer,"hashname",hashname_char(id)); lob_set_int(outer,"at",0); lob_set(outer,"type","link"); LOG("bare incoming link json being discovered %s",lob_json(outer)); } lob_free(inner); } // run everything else through discovery, usually plain handshakes mesh_discover(mesh, outer, pipe); link = mesh_linked(mesh, lob_get(outer,"hashname"), 0); lob_free(outer); return link; }