// internal to clean up written data util_chunks_t _util_chunks_gc(util_chunks_t chunks) { size_t len; if(!chunks) return NULL; // LOG("CHUNK GC %d %d %s",chunks,chunks->writeat,util_hex(chunks->writing,chunks->writelen,NULL)); // nothing to do if(!chunks->writing || !chunks->writeat || !chunks->writelen) return chunks; len = chunks->writing[0]+1; if(len > chunks->writelen) return LOG("bad chunk write data"); // the current chunk hasn't beeen written yet if(chunks->writeat < len) return chunks; // remove the current chunk chunks->writelen -= len; chunks->writeat -= len; memmove(chunks->writing,chunks->writing+len,chunks->writelen); chunks->writing = util_reallocf(chunks->writing, chunks->writelen); // tail recurse to eat any more chunks return _util_chunks_gc(chunks); }
// get any packets that have been reassembled from incoming chunks lob_t util_chunks_receive(util_chunks_t chunks) { uint32_t at, len, start; uint8_t *buf, *append; lob_t ret; if(!chunks || !chunks->reading) return NULL; // skip over any 0 acks in the start for(start = 0; start < chunks->readlen && chunks->reading[start] == 0; start += 1); // check for complete packet and get its length for(len = 0, at = start;at < chunks->readlen && chunks->reading[at]; at += chunks->reading[at]+1) len += chunks->reading[at]; if(!len || at >= chunks->readlen) return NULL; if(!(buf = malloc(len))) return LOG("OOM %d",len); // copy in the body of each chunk for(at = start, append = buf; chunks->reading[at]; append += chunks->reading[at], at += chunks->reading[at]+1) { memcpy(append, chunks->reading+(at+1), chunks->reading[at]); } ret = lob_decloak(buf,len); free(buf); // advance the reading buffer the whole packet, shrink at++; chunks->readlen -= at; memmove(chunks->reading,chunks->reading+at,chunks->readlen); chunks->reading = util_reallocf(chunks->reading,chunks->readlen); return ret; }
// internal to append read data util_chunks_t _util_chunks_append(util_chunks_t chunks, uint8_t *block, size_t len) { if(!chunks || !block || !len) return chunks; if(!chunks->reading) chunks->readlen = chunks->acked = 0; // be paranoid chunks->readlen += len; if(!(chunks->reading = util_reallocf(chunks->reading, chunks->readlen))) return LOG("OOM"); memcpy(chunks->reading+(chunks->readlen-len),block,len); return chunks; }
// 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; }
// sends an ack if neccessary, after any more chunks have been received and none waiting to send util_chunks_t util_chunks_ack(util_chunks_t chunks) { uint32_t count = 0, zeros = 0, at; if(!chunks->readlen) return NULL; // walk through read data and count chunks for(at = chunks->reading[0];at < chunks->readlen; at += chunks->reading[at]) { count++; if(chunks->reading[at] == 0) zeros++; else zeros = 0; at++; // add chunk size byte } // LOG("count %d acked %d first %d len %d zeros %d",count,chunks->acked,chunks->reading[0],chunks->readlen,zeros); // no new chunks if(count == chunks->acked) return NULL; // implicitly unblock after any new chunks util_chunks_next(chunks); // don't ack if the last received was an ack if(zeros > 1 && (count - chunks->acked) == 1) return NULL; chunks->acked = count; // skip the ack if there's already a chunk waiting if(chunks->writeat != chunks->writelen) return chunks; // write a zero ack chunk if(!(chunks->writing = util_reallocf(chunks->writing, chunks->writelen+1))) return LOG("OOM"); memset(chunks->writing+chunks->writelen,0,1); // zeros are acks chunks->writelen++; return chunks; }
// turn this packet into chunks util_chunks_t util_chunks_send(util_chunks_t chunks, lob_t out) { uint32_t start, at; size_t len; uint8_t *raw, size, rounds = 1; // TODO random rounds? // validate and gc first if(!_util_chunks_gc(chunks) || !(len = lob_len(out))) return chunks; if(chunks->cloak) len += (8*rounds); start = chunks->writelen; chunks->writelen += len; chunks->writelen += CEIL(len,chunks->space); // include space for per-chunk start byte chunks->writelen++; // space for terminating 0 if(!(chunks->writing = util_reallocf(chunks->writing, chunks->writelen))) { chunks->writelen = chunks->writeat = 0; return LOG("OOM"); } raw = lob_raw(out); if(chunks->cloak) raw = lob_cloak(out, rounds); for(at = 0; at < len;) { size = ((len-at) < chunks->space) ? (uint8_t)(len-at) : chunks->space; chunks->writing[start] = size; start++; memcpy(chunks->writing+start,raw+at,size); at += size; start += size; } chunks->writing[start] = 0; // end of chunks, full packet if(chunks->cloak) free(raw); return chunks; }
// intermediate hashes in the json, if id is given it is attached as BODY instead lob_t hashname_im(lob_t keys, uint8_t id) { uint32_t i; size_t len; uint8_t *buf, hash[32]; char *key, *value, hex[3]; lob_t im; if(!keys) return LOG("bad args"); // loop through all keys and create intermediates im = lob_new(); buf = NULL; util_hex(&id,1,hex); for(i=0;(key = lob_get_index(keys,i));i+=2) { value = lob_get_index(keys,i+1); if(strlen(key) != 2 || !value) continue; // skip non-csid keys len = base32_decode_floor(strlen(value)); // save to body raw or as a base32 intermediate value if(id && util_cmp(hex,key) == 0) { lob_body(im,NULL,len); if(base32_decode(value,strlen(value),im->body,len) != len) continue; lob_set_raw(im,key,0,"true",4); }else{ buf = util_reallocf(buf,len); if(!buf) return lob_free(im); if(base32_decode(value,strlen(value),buf,len) != len) continue; // store the hash intermediate value e3x_hash(buf,len,hash); lob_set_base32(im,key,hash,32); } } if(buf) free(buf); return im; }