/* vim: set expandtab ts=4 sw=4: */ /* * You may redistribute this program and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "subnode/GetPeersResponder.h" #include "subnode/BoilerplateResponder.h" #include "dht/dhtcore/ReplySerializer.h" #include "util/Identity.h" #define GETPEERS_RESPONSE_NODES 8 struct GetPeersResponder_pvt { struct GetPeersResponder pub; struct MsgCore_Handler* handler; struct AddrSet* peers; struct Log* log; struct Allocator* alloc; struct MsgCore* msgCore; struct Address* selfAddr; struct BoilerplateResponder* br; struct EncodingScheme* myScheme; Identity }; static void onGetPeers(Dict* msg, struct Address* src, struct Allocator* tmpAlloc, struct MsgCore_Handler* handler) { struct GetPeersResponder_pvt* gprp = Identity_check((struct GetPeersResponder_pvt*) handler->userData); Log_debug(gprp->log, "Received getPeers req from [%s]", Address_toString(src, tmpAlloc)->bytes); String* txid = Dict_getStringC(msg, "txid"); if (!txid) { Log_debug(gprp->log, "getPeers missing txid"); return; } String* nearLabelStr = Dict_getStringC(msg, "tar"); uint64_t label; if (!nearLabelStr || nearLabelStr->len != 8) { Log_debug(gprp->log, "getPeers does not contain proper target"); return; } else { uint64_t label_be; Bits_memcpy(&label_be, nearLabelStr->bytes, 8); label = Endian_bigEndianToHost64(label_be); } struct Address** ptrList = Allocator_calloc(tmpAlloc, sizeof(uintptr_t), GETPEERS_RESPONSE_NODES+1); ptrList[GETPEERS_RESPONSE_NODES-1] = gprp->selfAddr; if (label > 1) { int formNum = EncodingScheme_getFormNum(gprp->myScheme, label); if (formNum > -1) { int bitsUsed = EncodingScheme_formSize(&gprp->myScheme->forms[formNum]); label = (label & Bits_maxBits64(bitsUsed)) | 1 << bitsUsed; } } for (int i = 0; i < gprp->peers->length; i++) { struct Address* peer = AddrSet_get(gprp->peers, i); if (peer->path < label) { continue; } int j; for (j = 0; j < GETPEERS_RESPONSE_NODES; j++) { if (!ptrList[j]) { continue; } if ((ptrList[j]->path - label) > (peer->path - label)) { continue; } break; } switch (j) { default: Bits_memmove(ptrList, &ptrList[1], (j - 1) * sizeof(char*)); Gcc_FALLTHRU; case 1: ptrList[j - 1] = peer; Gcc_FALLTHRU; case 0:; } } Assert_true(!ptrList[GETPEERS_RESPONSE_NODES]); struct Address* addrList = Allocator_calloc(tmpAlloc, sizeof(struct Address), GETPEERS_RESPONSE_NODES+1); int size = 0; for (int i = 0; i < GETPEERS_RESPONSE_NODES; i++) { if (!ptrList[i]) { continue; } Bits_memcpy(&addrList[size++], ptrList[i], sizeof(struct Address)); } Assert_true(Bits_isZero(&addrList[GETPEERS_RESPONSE_NODES], sizeof(struct Address))); Dict* responseDict = Dict_new(tmpAlloc); ReplySerializer_serialize((&(struct Address_List) { .elems = addrList, .length = size }), responseDict, src, tmpAlloc); Dict_putStringC(responseDict, "txid", txid, tmpAlloc); BoilerplateResponder_addBoilerplate(gprp->br, responseDict, src, tmpAlloc); MsgCore_sendResponse(gprp->msgCore, responseDict, src, tmpAlloc); } struct GetPeersResponder* GetPeersResponder_new(struct Allocator* allocator, struct Log* log, struct AddrSet* peers, struct Address* selfAddr, struct MsgCore* msgCore, struct BoilerplateResponder* br, struct EncodingScheme* myScheme) { struct Allocator* alloc = Allocator_child(allocator); struct GetPeersResponder_pvt* gprp = Allocator_calloc(alloc, sizeof(struct GetPeersResponder_pvt), 1); Identity_set(gprp); gprp->peers = peers; gprp->log = log; gprp->alloc = alloc; gprp->msgCore = msgCore; gprp->selfAddr = selfAddr; gprp->handler = MsgCore_onQuery(msgCore, "gp", alloc); gprp->handler->userData = gprp; gprp->handler->cb = onGetPeers; gprp->br = br; gprp->myScheme = myScheme; return &gprp->pub; }