// 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; }
// 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; }
net_tcp4_t net_tcp4_new(mesh_t mesh, lob_t options) { int port, sock, pipes, opt = 1; net_tcp4_t net; struct sockaddr_in sa; socklen_t size = sizeof(struct sockaddr_in); port = lob_get_int(options,"port"); pipes = lob_get_int(options,"pipes"); if(!pipes) pipes = 11; // hashtable for active pipes // create a udp socket if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) ) < 0 ) return LOG("failed to create socket %s",strerror(errno)); memset(&sa,0,sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(sock, (struct sockaddr*)&sa, size) < 0) return LOG("bind failed %s",strerror(errno)); getsockname(sock, (struct sockaddr*)&sa, &size); if(listen(sock, 10) < 0) return LOG("listen failed %s",strerror(errno)); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt , sizeof(int)); fcntl(sock, F_SETFL, O_NONBLOCK); if(!(net = malloc(sizeof (struct net_tcp4_struct)))) return LOG("OOM"); memset(net,0,sizeof (struct net_tcp4_struct)); net->server = sock; net->port = ntohs(sa.sin_port); net->pipes = xht_new(pipes); // connect us to this mesh net->mesh = mesh; xht_set(mesh->index, MUID, net); mesh_on_path(mesh, MUID, tcp4_path); // convenience net->path = lob_new(); lob_set(net->path,"type","tcp4"); lob_set(net->path,"ip","127.0.0.1"); lob_set_int(net->path,"port",net->port); return net; }
// 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; }
// encrypt and send this one packet on this pipe link_t link_direct(link_t link, lob_t inner, pipe_t pipe) { if(!link || !inner) return LOG("bad args"); if(!pipe && (!link->pipes || !(pipe = link->pipes->pipe))) return LOG("no network"); // add an outgoing cid if none set if(!lob_get_int(inner,"c")) lob_set_uint(inner,"c",e3x_exchange_cid(link->x, NULL)); pipe->send(pipe, e3x_exchange_send(link->x, inner), link); return link; }
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_handshake(link_t link, lob_t inner, lob_t outer, pipe_t pipe) { link_t ready; uint32_t out; if(!link || !inner || !outer) return LOG("bad args"); if(!link->key && link_key(link->mesh,inner) != link) return LOG("invalid/mismatch handshake key"); out = exchange3_out(link->x,0); ready = link_ready(link); // if bad at, always send current handshake if(exchange3_in(link->x, lob_get_int(inner,"at")) < out) { LOG("old/bad at: %s (%d,%d,%d)",lob_json(inner),lob_get_int(inner,"at"),exchange3_in(link->x,0),exchange3_out(link->x,0)); if(pipe) pipe->send(pipe,exchange3_handshake(link->x),link); return NULL; } // trust/add this pipe if(pipe) link_pipe(link,pipe); // try to sync ephemeral key if(!exchange3_sync(link->x,outer)) return LOG("sync failed"); // we may need to re-sync if(out != exchange3_out(link->x,0)) link_sync(link); // notify of ready state change if(!ready && link_ready(link)) { LOG("link ready"); mesh_link(link->mesh, link); } return link; }
// 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; }
// 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); }
// 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; }
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; }
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; }