// Returns a NodeDB ID that matches the specified criteria, with explicit nid/tnow. static int nodedbGetDBIDByID(struct s_nodedb *db, const int nid, const int tnow, const int max_lastseen, const int max_lastconnect, const int min_lastconntry) { int j, j_max, aid, ret; struct s_nodedb_addrdata *dbdata; struct s_map *addrset; addrset = mapGetValueByID(db->addrdb, nid); j_max = mapGetKeyCount(addrset); for(j=0; j<j_max; j++) { aid = mapGetNextKeyID(addrset); dbdata = mapGetValueByID(addrset, aid); if(dbdata != NULL) { if( ( (max_lastseen < 0) || ( (dbdata->lastseen > 0) && ((tnow - dbdata->lastseen_t) < max_lastseen) && 1)) && ( (max_lastconnect < 0) || ( (dbdata->lastconnect > 0) && ((tnow - dbdata->lastconnect_t) < max_lastconnect) && 1)) && ( (min_lastconntry < 0) || (!(dbdata->lastconntry > 0)) || ( (dbdata->lastseen > 0) && ((tnow - dbdata->lastconntry_t) >= min_lastconntry) && ((tnow - dbdata->lastconntry_t) >= ((tnow - dbdata->lastseen_t) / 2)) && 1)) && 1) { ret = (nid * db->num_peeraddrs) + aid; return ret; } } } return -1; }
// Returns a NodeDB ID that matches the specified criteria. static int nodedbGetDBID(struct s_nodedb *db, struct s_nodeid *nodeid, const int max_lastseen, const int max_lastconnect, const int min_lastconntry) { int i, i_max, nid, tnow, ret; tnow = utilGetClock(); i_max = mapGetKeyCount(db->addrdb); ret = -1; if(nodeid == NULL) { // find DBID for any NodeID i = 0; while((i < i_max) && (ret < 0)) { nid = mapGetNextKeyID(db->addrdb); ret = nodedbGetDBIDByID(db, nid, tnow, max_lastseen, max_lastconnect, min_lastconntry); i++; } } else { // find DBID for specified NodeID nid = mapGetKeyID(db->addrdb, nodeid->id); if(!(nid < 0)) { ret = nodedbGetDBIDByID(db, nid, tnow, max_lastseen, max_lastconnect, min_lastconntry); } } return ret; }
// Generate peerinfo packet. static void peermgtGenPacketPeerinfo(struct s_packet_data *data, struct s_peermgt *mgt) { const int peerinfo_size = (packet_PEERID_SIZE + nodeid_SIZE + peeraddr_SIZE); int peerinfo_max = mapGetKeyCount(&mgt->map); int peerinfo_count; int peerinfo_limit; int pos = 4; int i = 0; int infoid; unsigned char infocid[packet_PEERID_SIZE]; struct s_nodeid infonid; // randomize maximum length of peerinfo packet if((abs(cryptoRandInt()) % 2) == 1) { peerinfo_limit = 7; } else { peerinfo_limit = 5; } // generate peerinfo entries peerinfo_count = 0; while((i < peerinfo_max) && (peerinfo_count < peerinfo_limit) && (pos + peerinfo_size < data->pl_buf_size)) { infoid = peermgtGetNextID(mgt); if((infoid > 0) && (mgt->data[infoid].state == peermgt_STATE_COMPLETE) && (!peeraddrIsInternal(&mgt->data[infoid].remoteaddr))) { utilWriteInt32(infocid, infoid); memcpy(&data->pl_buf[pos], infocid, packet_PEERID_SIZE); peermgtGetNodeID(mgt, &infonid, infoid); memcpy(&data->pl_buf[(pos + packet_PEERID_SIZE)], infonid.id, nodeid_SIZE); memcpy(&data->pl_buf[(pos + packet_PEERID_SIZE + nodeid_SIZE)], &mgt->data[infoid].remoteaddr.addr, peeraddr_SIZE); pos = pos + peerinfo_size; peerinfo_count++; } i++; } // write peerinfo_count utilWriteInt32(data->pl_buf, peerinfo_count); // set packet metadata data->pl_length = (4 + (peerinfo_count * peerinfo_size)); data->pl_type = packet_PLTYPE_PEERINFO; data->pl_options = 0; }
static int mapTestsuiteGenerateASCIIString(struct s_map *map, char *str, const int strlen) { int size = strlen - 5; char c; int i; int j; int k; int l; int m; int n; int o; int p; int h; int x; int q = 8192; int *nidarray = NULL; int cpos = 0; if(mapGetKeyCount(map) > 0) { nidarray = malloc(q * sizeof(int)); for(i=0; i<q; i++) nidarray[i]=-1; if(nidarray == NULL) return 0; h = mapTestsuiteGenerateASCIIStringRecursive(map, nidarray, map->rootid, 0, 0); j = 0; l = 0; while(j < h) { k = 1 << (h-j); if(l) { m = k >> 1; o = 1; while(o < m) { i = 1 << j; x = 0; q = nidarray[mapTestsuiteGenerateASCIIStringCalcXY(x,j)]; n = 0; p = 0; c = '\0'; for(;;) { c = ' '; if(((n + o) % k == 0)) { c = '/'; p = !p; } if(((n - o) % k == 0) && (n > o)) c = '\\'; if(p && (!(q < 0))) str[cpos] = c; else str[cpos] = ' '; cpos++; if(!(cpos < size)) return 0; if((c == '\\') & p) { x++; q = nidarray[mapTestsuiteGenerateASCIIStringCalcXY(x,j)]; } if(!(x < i)) break; n++; } str[cpos] = '\n'; cpos++; if(!(cpos < size)) return 0; o++; } } else { x = 0; i = 1 << j; n = k >> 1; while(x < i) { while(n < k) { snprintf(&str[cpos], 3, " "); cpos = cpos + 2; if(!(cpos < size)) return 0; n++; } n = nidarray[mapTestsuiteGenerateASCIIStringCalcXY(x,j)]; if(n < 0) { snprintf(&str[cpos], 3, " "); } else { snprintf(&str[cpos-2], 5, "%4d", n); } cpos = cpos + 2; if(!(cpos < size)) return 0; n = 1; x++; } str[cpos] = '\n'; cpos++; if(!(cpos < size)) return 0; } if(l) j++; l = !l; }
// Generate NodeDB status report. static void nodedbStatus(struct s_nodedb *db, char *report, const int report_len) { int i; int j; int size; int rpsize; int pos; int tnow; unsigned char timediff[4]; struct s_map *addrset; struct s_nodedb_addrdata *addrdata; tnow = utilGetClock(); size = mapGetKeyCount(db->addrdb); rpsize = (95 * (1 + db->num_peeraddrs)) + 2; pos = 0; memcpy(&report[pos], "NodeID + Address LastSeen LastConn LastTry ", 94); pos = pos + 94; report[pos++] = '\n'; i = 0; while(i < size && pos < (report_len - rpsize)) { if(mapIsValidID(db->addrdb, i)) { utilByteArrayToHexstring(&report[pos], ((nodeid_SIZE * 2) + 2), mapGetKeyByID(db->addrdb, i), nodeid_SIZE); pos = pos + (nodeid_SIZE * 2); addrset = mapGetValueByID(db->addrdb, i); memcpy(&report[pos], " ", 30); pos = pos + 30; j = 0; while(j < db->num_peeraddrs) { if(mapIsValidID(addrset, j)) { addrdata = mapGetValueByID(addrset, j); report[pos++] = '\n'; memcpy(&report[pos], " ", 12); pos = pos + 12; report[pos++] = '-'; report[pos++] = '-'; report[pos++] = '>'; report[pos++] = ' '; utilByteArrayToHexstring(&report[pos], ((peeraddr_SIZE * 2) + 2), mapGetKeyByID(addrset, j), peeraddr_SIZE); pos = pos + (peeraddr_SIZE * 2); report[pos++] = ' '; report[pos++] = ' '; if(addrdata->lastseen) { utilWriteInt32(timediff, (tnow - addrdata->lastseen_t)); utilByteArrayToHexstring(&report[pos], 10, timediff, 4); } else { memcpy(&report[pos], "--------", 8); } pos = pos + 8; report[pos++] = ' '; report[pos++] = ' '; if(addrdata->lastconnect) { utilWriteInt32(timediff, (tnow - addrdata->lastconnect_t)); utilByteArrayToHexstring(&report[pos], 10, timediff, 4); } else { memcpy(&report[pos], "--------", 8); } pos = pos + 8; report[pos++] = ' '; report[pos++] = ' '; if(addrdata->lastconntry) { utilWriteInt32(timediff, (tnow - addrdata->lastconntry_t)); utilByteArrayToHexstring(&report[pos], 10, timediff, 4); } else { memcpy(&report[pos], "--------", 8); } pos = pos + 8; } j++; } report[pos++] = '\n'; } i++; } report[pos++] = '\0'; }
// Generate next peer manager packet. Returns length if successful. static int peermgtGetNextPacketGen(struct s_peermgt *mgt, unsigned char *pbuf, const int pbuf_size, const int tnow, struct s_peeraddr *target) { int used = mapGetKeyCount(&mgt->map); int len; int outlen; int fragoutlen; int peerid; int relayid; int usetargetaddr; int i; int fragcount; int fragpos; const int plbuf_size = peermgt_MSGSIZE_MIN; unsigned char plbuf[plbuf_size]; struct s_msg authmsg; struct s_packet_data data; // send out user data outlen = mgt->outmsg.len; fragoutlen = mgt->fragoutsize; if(outlen > 0 && (!(fragoutlen > 0))) { if(mgt->outmsgbroadcast) { // get PeerID for broadcast message do { peerid = peermgtGetNextID(mgt); mgt->outmsgbroadcastcount++; } while((!(peermgtIsActiveRemoteID(mgt, peerid) && peermgtGetRemoteFlag(mgt, peerid, peermgt_FLAG_USERDATA))) && (mgt->outmsgbroadcastcount < used)); if(mgt->outmsgbroadcastcount >= used) { mgt->outmsgbroadcast = 0; mgt->outmsg.len = 0; } } else { // get PeerID for unicast message peerid = mgt->outmsgpeerid; mgt->outmsg.len = 0; } if(peermgtIsActiveRemoteID(mgt, peerid)) { // check if session is active if(peermgtGetRemoteFlag(mgt, peerid, peermgt_FLAG_USERDATA)) { if((mgt->fragmentation > 0) && (outlen > peermgt_MSGSIZE_MIN)) { // start generating fragmented userdata packets mgt->fragoutpeerid = peerid; mgt->fragoutcount = (((outlen - 1) / peermgt_MSGSIZE_MIN) + 1); // calculate number of fragments mgt->fragoutsize = outlen; fragoutlen = outlen; mgt->fragoutpos = 0; } else { // generate userdata packet data.pl_buf = mgt->outmsg.msg; data.pl_buf_size = outlen; data.peerid = mgt->data[peerid].remoteid; data.seq = ++mgt->data[peerid].remoteseq; data.pl_length = outlen; data.pl_type = packet_PLTYPE_USERDATA; data.pl_options = 0; len = packetEncode(pbuf, pbuf_size, &data, &mgt->ctx[peerid]); if(len > 0) { mgt->data[peerid].lastsend = tnow; *target = mgt->data[peerid].remoteaddr; return len; } } } } } // send out fragments if(fragoutlen > 0) { fragcount = mgt->fragoutcount; fragpos = mgt->fragoutpos; peerid = mgt->fragoutpeerid; if(peermgtIsActiveRemoteID(mgt, peerid)) { // check if session is active // generate fragmented packet data.pl_buf = &mgt->outmsg.msg[(fragpos * peermgt_MSGSIZE_MIN)]; if(fragoutlen > peermgt_MSGSIZE_MIN) { // start or middle fragment data.pl_buf_size = peermgt_MSGSIZE_MIN; data.pl_length = peermgt_MSGSIZE_MIN; mgt->fragoutsize = (fragoutlen - peermgt_MSGSIZE_MIN); } else { // end fragment data.pl_buf_size = fragoutlen; data.pl_length = fragoutlen; mgt->fragoutsize = 0; } data.peerid = mgt->data[peerid].remoteid; data.seq = ++mgt->data[peerid].remoteseq; data.pl_type = packet_PLTYPE_USERDATA_FRAGMENT; data.pl_options = (fragcount << 4) | (fragpos); len = packetEncode(pbuf, pbuf_size, &data, &mgt->ctx[peerid]); mgt->fragoutpos = (fragpos + 1); if(len > 0) { mgt->data[peerid].lastsend = tnow; *target = mgt->data[peerid].remoteaddr; return len; } } else { // session not active anymore, abort sending fragments mgt->fragoutsize = 0; } } // send out request-response packet outlen = mgt->rrmsg.len; if(outlen > 0) { peerid = mgt->rrmsgpeerid; usetargetaddr = mgt->rrmsgusetargetaddr; mgt->rrmsg.len = 0; mgt->rrmsgusetargetaddr = 0; if((outlen < peermgt_MSGSIZE_MAX) && (peermgtIsActiveRemoteID(mgt, peerid))) { // check if session is active data.pl_buf = mgt->rrmsg.msg; data.pl_buf_size = peermgt_MSGSIZE_MAX; data.pl_length = outlen; data.pl_type = mgt->rrmsgtype; data.pl_options = 0; data.peerid = mgt->data[peerid].remoteid; data.seq = ++mgt->data[peerid].remoteseq; len = packetEncode(pbuf, pbuf_size, &data, &mgt->ctx[peerid]); if(len > 0) { if(usetargetaddr > 0) { *target = mgt->rrmsgtargetaddr; } else { mgt->data[peerid].lastsend = tnow; *target = mgt->data[peerid].remoteaddr; } return len; } } } // send peerinfo to peers for(i=0; i<used; i++) { peerid = peermgtGetNextID(mgt); if(peerid > 0) { if((tnow - mgt->data[peerid].lastrecv) < peermgt_RECV_TIMEOUT) { // check if session has expired if(mgt->data[peerid].state == peermgt_STATE_COMPLETE) { // check if session is active if(((tnow - mgt->data[peerid].lastsend) > peermgt_KEEPALIVE_INTERVAL) || ((tnow - mgt->data[peerid].lastpeerinfo) > peermgt_PEERINFO_INTERVAL)) { // check if we should send peerinfo packet data.pl_buf = plbuf; data.pl_buf_size = plbuf_size; data.peerid = mgt->data[peerid].remoteid; data.seq = ++mgt->data[peerid].remoteseq; peermgtGenPacketPeerinfo(&data, mgt); len = packetEncode(pbuf, pbuf_size, &data, &mgt->ctx[peerid]); if(len > 0) { mgt->data[peerid].lastsend = tnow; mgt->data[peerid].lastpeerinfo = tnow; *target = mgt->data[peerid].remoteaddr; return len; } } } } else { peermgtDeleteID(mgt, peerid); } } } // send auth manager message if(authmgtGetNextMsg(&mgt->authmgt, &authmsg, target)) { data.pl_buf = authmsg.msg; data.pl_buf_size = authmsg.len; data.peerid = 0; data.seq = 0; data.pl_length = authmsg.len; if(data.pl_length > 0) { data.pl_type = packet_PLTYPE_AUTH; data.pl_options = 0; len = packetEncode(pbuf, pbuf_size, &data, &mgt->ctx[0]); if(len > 0) { mgt->data[0].lastsend = tnow; return len; } } } // connect new peer if((authmgtUsedSlotCount(&mgt->authmgt) < ((authmgtSlotCount(&mgt->authmgt) / 4) * 3)) && ((tnow - mgt->lastconnect) > peermgt_NEWCONNECT_INTERVAL)) { i = nodedbNextID(&mgt->nodedb, peermgt_NEWCONNECT_MAX_AGE, 0, 1); if(!(i < 0)) { peerid = peermgtGetID(mgt, nodedbGetNodeID(&mgt->nodedb, i)); if(peerid < 0) { // check if node is already connected // node is not connected yet if(peermgtConnect(mgt, nodedbGetNodeAddress(&mgt->nodedb, i))) { // try to connect if(peermgtConnect(mgt, nodedbGetIndirectNodeAddress(&mgt->nodedb, i))) { // try to connect via relay relayid = 0; // on success, dont change indirect entry in NodeDB } else { relayid = -1; // on failure, delete indirect entry in NodeDB } nodedbUpdate(&mgt->nodedb, nodedbGetNodeID(&mgt->nodedb, i), NULL, 0, 0, 1, relayid, 0, 0); mgt->lastconnect = tnow; } } else { // node is already connected if(peeraddrIsInternal(&mgt->data[peerid].remoteaddr)) { peermgtSendPingToAddr(mgt, NULL, peerid, mgt->data[peerid].conntime, nodedbGetNodeAddress(&mgt->nodedb, i)); // try to switch peer to a direct connection nodedbUpdate(&mgt->nodedb, nodedbGetNodeID(&mgt->nodedb, i), NULL, 0, 0, 1, 0, 0, 0); mgt->lastconnect = tnow; } } } } return 0; }