/** See: NodeStore.h */ struct Node* NodeStore_getNode(const struct NodeStore* store, struct Address* addr) { uint32_t pfx = Address_getPrefix(addr); // If multiple nodes with the same address, get the one with the best reach. int32_t bestIndex = -1; uint32_t bestReach = 0; for (int32_t i = 0; i < (int32_t) store->size; i++) { if (pfx == store->headers[i].addressPrefix && memcmp(addr->key, store->nodes[i].address.key, Address_KEY_SIZE) == 0 && store->headers[i].reach >= bestReach) { bestIndex = i; bestReach = store->headers[i].reach; } } if (bestIndex == -1) { return NULL; } // Synchronize the reach values. store->nodes[bestIndex].reach = store->headers[bestIndex].reach; return &store->nodes[bestIndex]; }
void RumorMill__addNode(struct RumorMill* mill, struct Address* addr, const char* file, int line) { struct RumorMill_pvt* rm = Identity_check((struct RumorMill_pvt*) mill); Address_getPrefix(addr); for (int i = 0; i < rm->pub.count; i++) { if (rm->pub.addresses[i].path == addr->path && !Bits_memcmp(rm->pub.addresses[i].key, addr->key, 32)) { return; } } if (!Bits_memcmp(addr->key, rm->selfAddr->key, 32)) { return; } struct Address* replace; if (rm->pub.count < rm->capacity) { replace = &rm->pub.addresses[rm->pub.count++]; } else { replace = getWorst(rm); } Bits_memcpyConst(replace, addr, sizeof(struct Address)); if (Defined(Log_DEBUG)) { uint8_t addrStr[60]; Address_print(addrStr, addr); Log_debug(rm->log, "[%s] addNode(%s) count[%d] from [%s:%d]", rm->pub.name, addrStr, rm->pub.count, file, line); } }
static inline void replaceNode(struct Node* const nodeToReplace, struct NodeHeader* const headerToReplace, struct Address* addr) { headerToReplace->addressPrefix = Address_getPrefix(addr); headerToReplace->reach = 0; headerToReplace->switchIndex = getSwitchIndex(addr); memcpy(&nodeToReplace->address, addr, sizeof(struct Address)); }
// Incoming message which has passed through the cryptoauth and needs to be forwarded to the switch. static Iface_DEFUN receivedPostCryptoAuth(struct Message* msg, struct Peer* ep, struct InterfaceController_pvt* ic) { ep->bytesIn += msg->length; int caState = CryptoAuth_getState(ep->caSession); if (ep->state < InterfaceController_PeerState_ESTABLISHED) { // EP states track CryptoAuth states... ep->state = caState; SwitchCore_setInterfaceState(&ep->switchIf, SwitchCore_setInterfaceState_ifaceState_UP); Bits_memcpy(ep->addr.key, ep->caSession->herPublicKey, 32); Address_getPrefix(&ep->addr); if (caState == CryptoAuth_ESTABLISHED) { moveEndpointIfNeeded(ep); //sendPeer(0xffffffff, PFChan_Core_PEER, ep);// version is not known at this point. } else { // prevent some kinds of nasty things which could be done with packet replay. // This is checking the message switch header and will drop it unless the label // directs it to *this* router. if (msg->length < 8 || msg->bytes[7] != 1) { Log_info(ic->logger, "DROP message because CA is not established."); return 0; } else { // When a "server" gets a new connection from a "client" the router doesn't // know about that client so if the client sends a packet to the server, the // server will be unable to handle it until the client has sent inter-router // communication to the server. Here we will ping the client so when the // server gets the ping response, it will insert the client into its table // and know its version. // prevent DoS by limiting the number of times this can be called per second // limit it to 7, this will affect innocent packets but it doesn't matter much // since this is mostly just an optimization and for keeping the tests happy. if ((ep->pingCount + 1) % 7) { sendPing(ep); } } } } else if (ep->state == InterfaceController_PeerState_UNRESPONSIVE && caState == CryptoAuth_ESTABLISHED) { ep->state = InterfaceController_PeerState_ESTABLISHED; SwitchCore_setInterfaceState(&ep->switchIf, SwitchCore_setInterfaceState_ifaceState_UP); } else { ep->timeOfLastMessage = Time_currentTimeMilliseconds(ic->eventBase); } Identity_check(ep); Assert_true(!(msg->capacity % 4)); return Iface_next(&ep->switchIf, msg); }
struct NetCore* NetCore_new(uint8_t* privateKey, struct Allocator* allocator, struct EventBase* base, struct Random* rand, struct Log* log) { struct Allocator* alloc = Allocator_child(allocator); struct NetCore* nc = Allocator_calloc(alloc, sizeof(struct NetCore), 1); nc->alloc = alloc; nc->base = base; nc->rand = rand; nc->log = log; struct CryptoAuth* ca = nc->ca = CryptoAuth_new(alloc, privateKey, base, log, rand); struct EventEmitter* ee = nc->ee = EventEmitter_new(alloc, log, ca->publicKey); struct Address* myAddress = nc->myAddress = Allocator_calloc(alloc, sizeof(struct Address), 1); Bits_memcpy(myAddress->key, ca->publicKey, 32); Address_getPrefix(myAddress); myAddress->protocolVersion = Version_CURRENT_PROTOCOL; myAddress->path = 1; // lower half struct SwitchCore* switchCore = nc->switchCore = SwitchCore_new(log, alloc, base); struct SwitchAdapter* switchAdapter = nc->switchAdapter = SwitchAdapter_new(alloc, log); Iface_plumb(&switchAdapter->switchIf, switchCore->routerIf); struct ControlHandler* controlHandler = nc->controlHandler = ControlHandler_new(alloc, log, ee, ca->publicKey); Iface_plumb(&controlHandler->coreIf, &switchAdapter->controlIf); struct SwitchPinger* sp = nc->sp = SwitchPinger_new(base, rand, log, myAddress, alloc); Iface_plumb(&controlHandler->switchPingerIf, &sp->controlHandlerIf); nc->ifController = InterfaceController_new(ca, switchCore, log, base, sp, rand, alloc, ee); // session manager struct SessionManager* sm = nc->sm = SessionManager_new(alloc, base, ca, rand, log, ee); Iface_plumb(&switchAdapter->sessionManagerIf, &sm->switchIf); // upper half struct UpperDistributor* upper = nc->upper = UpperDistributor_new(alloc, log, ee, myAddress); Iface_plumb(&sm->insideIf, &upper->sessionManagerIf); struct TUNAdapter* tunAdapt = nc->tunAdapt = TUNAdapter_new(alloc, log, myAddress->ip6.bytes); Iface_plumb(&tunAdapt->upperDistributorIf, &upper->tunAdapterIf); return nc; }
struct RumorMill* RumorMill_new(struct Allocator* allocator, struct Address* selfAddr, int capacity, struct Log* log, const char* name) { struct Allocator* alloc = Allocator_child(allocator); Address_getPrefix(selfAddr); struct RumorMill_pvt* rm = Allocator_calloc(alloc, sizeof(struct RumorMill_pvt), 1); rm->pub.addresses = Allocator_calloc(alloc, sizeof(struct Address), capacity); rm->capacity = capacity; rm->selfAddr = Allocator_clone(alloc, selfAddr); rm->log = log; rm->pub.name = name; Identity_set(rm); return &rm->pub; }
struct Ducttape* Ducttape_register(uint8_t privateKey[32], struct DHTModuleRegistry* registry, struct RouterModule* routerModule, struct SwitchCore* switchCore, struct EventBase* eventBase, struct Allocator* allocator, struct Log* logger, struct IpTunnel* ipTun, struct Random* rand) { struct Ducttape_pvt* context = Allocator_calloc(allocator, sizeof(struct Ducttape_pvt), 1); context->registry = registry; context->routerModule = routerModule; context->logger = logger; context->eventBase = eventBase; context->alloc = allocator; Identity_set(context); context->ipTunnel = ipTun; ipTun->nodeInterface.receiveMessage = sendToNode; ipTun->nodeInterface.receiverContext = context; ipTun->tunInterface.receiveMessage = sendToTun; ipTun->tunInterface.receiverContext = context; struct CryptoAuth* cryptoAuth = CryptoAuth_new(allocator, privateKey, eventBase, logger, rand); Bits_memcpyConst(context->myAddr.key, cryptoAuth->publicKey, 32); Address_getPrefix(&context->myAddr); context->sm = SessionManager_new(incomingFromCryptoAuth, outgoingFromCryptoAuth, context, eventBase, cryptoAuth, allocator); context->pub.sessionManager = context->sm; Bits_memcpyConst(&context->module, (&(struct DHTModule) { .name = "Ducttape", .context = context, .handleOutgoing = handleOutgoing }), sizeof(struct DHTModule));
struct Ducttape* Ducttape_register(Dict* config, uint8_t privateKey[32], struct DHTModuleRegistry* registry, struct RouterModule* routerModule, struct Interface* routerIf, struct SwitchCore* switchCore, struct event_base* eventBase, struct Allocator* allocator, struct Log* logger, struct Admin* admin) { struct Ducttape* context = allocator->calloc(sizeof(struct Ducttape), 1, allocator); context->registry = registry; context->routerModule = routerModule; context->logger = logger; context->forwardTo = NULL; AddressMapper_init(&context->addrMap); struct CryptoAuth* cryptoAuth = CryptoAuth_new(config, allocator, privateKey, eventBase, logger); CryptoAuth_getPublicKey(context->myAddr.key, cryptoAuth); Address_getPrefix(&context->myAddr); context->sm = SessionManager_new(16, incomingFromCryptoAuth, outgoingFromCryptoAuth, context, eventBase, cryptoAuth, allocator); if (routerIf) { context->routerIf = routerIf; routerIf->receiveMessage = ip6FromTun; routerIf->receiverContext = context; } Bits_memcpyConst(&context->module, (&(struct DHTModule) { .name = "Ducttape", .context = context, .handleOutgoing = handleOutgoing }), sizeof(struct DHTModule));
static Iface_DEFUN connected(struct Pathfinder_pvt* pf, struct Message* msg) { Log_debug(pf->log, "INIT"); struct PFChan_Core_Connect conn; Message_pop(msg, &conn, PFChan_Core_Connect_SIZE, NULL); Assert_true(!msg->length); Bits_memcpy(pf->myAddr.key, conn.publicKey, 32); Address_getPrefix(&pf->myAddr); pf->myAddr.path = 1; // begin pf->registry = DHTModuleRegistry_new(pf->alloc); ReplyModule_register(pf->registry, pf->alloc); pf->rumorMill = RumorMill_new(pf->alloc, &pf->myAddr, RUMORMILL_CAPACITY, pf->log, "extern"); pf->nodeStore = NodeStore_new(&pf->myAddr, pf->alloc, pf->base, pf->log, pf->rumorMill); if (pf->pub.fullVerify) { NodeStore_setFullVerify(pf->nodeStore, true); } pf->nodeStore->onBestPathChange = onBestPathChange; pf->nodeStore->onBestPathChangeCtx = pf; struct RouterModule* routerModule = RouterModule_register(pf->registry, pf->alloc, pf->myAddr.key, pf->base, pf->log, pf->rand, pf->nodeStore); pf->searchRunner = SearchRunner_new(pf->nodeStore, pf->log, pf->base, routerModule, pf->myAddr.ip6.bytes, pf->rumorMill, pf->alloc); pf->janitor = Janitor_new(routerModule, pf->nodeStore, pf->searchRunner, pf->rumorMill, pf->log, pf->alloc, pf->base, pf->rand); EncodingSchemeModule_register(pf->registry, pf->log, pf->alloc); SerializationModule_register(pf->registry, pf->log, pf->alloc); DHTModuleRegistry_register(&pf->dhtModule, pf->registry); pf->router = Router_new(routerModule, pf->nodeStore, pf->searchRunner, pf->alloc); // Now the admin stuff... if (pf->admin) { NodeStore_admin_register(pf->nodeStore, pf->admin, pf->alloc); RouterModule_admin_register(routerModule, pf->router, pf->admin, pf->alloc); SearchRunner_admin_register(pf->searchRunner, pf->admin, pf->alloc); Janitor_admin_register(pf->janitor, pf->admin, pf->alloc); } pf->state = Pathfinder_pvt_state_RUNNING; return NULL; }
int InterfaceController_bootstrapPeer(struct InterfaceController* ifc, int interfaceNumber, uint8_t* herPublicKey, const struct Sockaddr* lladdrParm, String* password, String* login, String* user, struct Allocator* alloc) { struct InterfaceController_pvt* ic = Identity_check((struct InterfaceController_pvt*) ifc); Assert_true(herPublicKey); Assert_true(password); struct InterfaceController_Iface_pvt* ici = ArrayList_OfIfaces_get(ic->icis, interfaceNumber); if (!ici) { return InterfaceController_bootstrapPeer_BAD_IFNUM; } Log_debug(ic->logger, "bootstrapPeer total [%u]", ici->peerMap.count); uint8_t ip6[16]; AddressCalc_addressForPublicKey(ip6, herPublicKey); if (!AddressCalc_validAddress(ip6) || !Bits_memcmp(ic->ca->publicKey, herPublicKey, 32)) { return InterfaceController_bootstrapPeer_BAD_KEY; } struct Allocator* epAlloc = Allocator_child(ici->alloc); struct Sockaddr* lladdr = Sockaddr_clone(lladdrParm, epAlloc); // TODO(cjd): eps are created in 3 places, there should be a factory function. struct Peer* ep = Allocator_calloc(epAlloc, sizeof(struct Peer), 1); int index = Map_EndpointsBySockaddr_put(&lladdr, &ep, &ici->peerMap); Assert_true(index >= 0); ep->alloc = epAlloc; ep->handle = ici->peerMap.handles[index]; ep->lladdr = lladdr; ep->ici = ici; ep->isIncomingConnection = false; Bits_memcpy(ep->addr.key, herPublicKey, 32); Address_getPrefix(&ep->addr); Identity_set(ep); Allocator_onFree(epAlloc, closeInterface, ep); Allocator_onFree(alloc, freeAlloc, epAlloc); ep->peerLink = PeerLink_new(ic->eventBase, epAlloc); ep->caSession = CryptoAuth_newSession(ic->ca, epAlloc, herPublicKey, false, "outer"); CryptoAuth_setAuth(password, login, ep->caSession); if (user) { ep->caSession->displayName = String_clone(user, epAlloc); } ep->switchIf.send = sendFromSwitch; if (SwitchCore_addInterface(ic->switchCore, &ep->switchIf, epAlloc, &ep->addr.path)) { Log_debug(ic->logger, "bootstrapPeer() SwitchCore out of space"); Allocator_free(epAlloc); return InterfaceController_bootstrapPeer_OUT_OF_SPACE; } // We want the node to immedietly be pinged but we don't want it to appear unresponsive because // the pinger will only ping every (PING_INTERVAL * 8) so we set timeOfLastMessage to // (now - pingAfterMilliseconds - 1) so it will be considered a "lazy node". ep->timeOfLastMessage = Time_currentTimeMilliseconds(ic->eventBase) - ic->pingAfterMilliseconds - 1; if (Defined(Log_INFO)) { struct Allocator* tempAlloc = Allocator_child(alloc); String* addrStr = Address_toString(&ep->addr, tempAlloc); Log_info(ic->logger, "Adding peer [%s] from bootstrapPeer()", addrStr->bytes); Allocator_free(tempAlloc); } // We can't just add the node directly to the routing table because we do not know // the version. We'll send it a switch ping and when it responds, we will know it's // key (if we don't already) and version number. sendPing(ep); return 0; }
/** * Expects [ struct LLAddress ][ beacon ] */ static Iface_DEFUN handleBeacon(struct Message* msg, struct InterfaceController_Iface_pvt* ici) { struct InterfaceController_pvt* ic = ici->ic; if (!ici->beaconState) { // accepting beacons disabled. Log_debug(ic->logger, "[%s] Dropping beacon because beaconing is disabled", ici->name->bytes); return NULL; } if (msg->length < Headers_Beacon_SIZE) { Log_debug(ic->logger, "[%s] Dropping runt beacon", ici->name->bytes); return NULL; } struct Sockaddr* lladdrInmsg = (struct Sockaddr*) msg->bytes; // clear the bcast flag lladdrInmsg->flags = 0; Message_shift(msg, -lladdrInmsg->addrLen, NULL); struct Headers_Beacon beacon; Message_pop(msg, &beacon, Headers_Beacon_SIZE, NULL); if (Defined(Log_DEBUG)) { char* content = Hex_print(&beacon, Headers_Beacon_SIZE, msg->alloc); Log_debug(ici->ic->logger, "RECV BEACON CONTENT[%s]", content); } struct Address addr; Bits_memset(&addr, 0, sizeof(struct Address)); Bits_memcpy(addr.key, beacon.publicKey, 32); addr.protocolVersion = Endian_bigEndianToHost32(beacon.version_be); Address_getPrefix(&addr); String* printedAddr = NULL; if (Defined(Log_DEBUG)) { printedAddr = Address_toString(&addr, msg->alloc); } if (addr.ip6.bytes[0] != 0xfc || !Bits_memcmp(ic->ca->publicKey, addr.key, 32)) { Log_debug(ic->logger, "handleBeacon invalid key [%s]", printedAddr->bytes); return NULL; } if (!Version_isCompatible(addr.protocolVersion, Version_CURRENT_PROTOCOL)) { if (Defined(Log_DEBUG)) { Log_debug(ic->logger, "[%s] DROP beacon from [%s] which was version [%d] " "our version is [%d] making them incompatable", ici->name->bytes, printedAddr->bytes, addr.protocolVersion, Version_CURRENT_PROTOCOL); } return NULL; } String* beaconPass = String_newBinary(beacon.password, Headers_Beacon_PASSWORD_LEN, msg->alloc); int epIndex = Map_EndpointsBySockaddr_indexForKey(&lladdrInmsg, &ici->peerMap); if (epIndex > -1) { // The password might have changed! struct Peer* ep = ici->peerMap.values[epIndex]; CryptoAuth_setAuth(beaconPass, NULL, ep->caSession); return NULL; } struct Allocator* epAlloc = Allocator_child(ici->alloc); struct Peer* ep = Allocator_calloc(epAlloc, sizeof(struct Peer), 1); struct Sockaddr* lladdr = Sockaddr_clone(lladdrInmsg, epAlloc); ep->alloc = epAlloc; ep->ici = ici; ep->lladdr = lladdr; int setIndex = Map_EndpointsBySockaddr_put(&lladdr, &ep, &ici->peerMap); ep->handle = ici->peerMap.handles[setIndex]; ep->isIncomingConnection = true; Bits_memcpy(&ep->addr, &addr, sizeof(struct Address)); Identity_set(ep); Allocator_onFree(epAlloc, closeInterface, ep); ep->peerLink = PeerLink_new(ic->eventBase, epAlloc); ep->caSession = CryptoAuth_newSession(ic->ca, epAlloc, beacon.publicKey, false, "outer"); CryptoAuth_setAuth(beaconPass, NULL, ep->caSession); ep->switchIf.send = sendFromSwitch; if (SwitchCore_addInterface(ic->switchCore, &ep->switchIf, epAlloc, &ep->addr.path)) { Log_debug(ic->logger, "handleBeacon() SwitchCore out of space"); Allocator_free(epAlloc); return NULL; } // We want the node to immedietly be pinged but we don't want it to appear unresponsive because // the pinger will only ping every (PING_INTERVAL * 8) so we set timeOfLastMessage to // (now - pingAfterMilliseconds - 1) so it will be considered a "lazy node". ep->timeOfLastMessage = Time_currentTimeMilliseconds(ic->eventBase) - ic->pingAfterMilliseconds - 1; Log_info(ic->logger, "Added peer [%s] from beacon", Address_toString(&ep->addr, msg->alloc)->bytes); // This should be safe because this is an outgoing request and we're sure the node will not // be relocated by moveEndpointIfNeeded() sendPeer(0xffffffff, PFChan_Core_PEER, ep); return NULL; }
/** * For serializing and parsing responses to getPeers and search requests. */ struct Address_List* ReplySerializer_parse(struct Address* fromNode, Dict* result, struct Log* log, bool splicePath, struct Allocator* alloc) { String* nodes = Dict_getString(result, CJDHTConstants_NODES); if (!nodes) { return NULL; } if (nodes->len == 0 || nodes->len % Address_SERIALIZED_SIZE != 0) { Log_debug(log, "Dropping unrecognized reply"); return NULL; } struct VersionList* versions = NULL; String* versionsStr = Dict_getString(result, CJDHTConstants_NODE_PROTOCOLS); if (versionsStr) { versions = VersionList_parse(versionsStr, alloc); } if (!versions || versions->length != (nodes->len / Address_SERIALIZED_SIZE)) { Log_debug(log, "Reply with missing or invalid versions"); return NULL; } struct Address_List* out = Address_List_new(versions->length, alloc); uint32_t j = 0; for (uint32_t i = 0; nodes && i < nodes->len; i += Address_SERIALIZED_SIZE) { struct Address addr = { .path = 0 }; Address_parse(&addr, (uint8_t*) &nodes->bytes[i]); addr.protocolVersion = versions->versions[i / Address_SERIALIZED_SIZE]; // calculate the ipv6 Address_getPrefix(&addr); if (splicePath) { // We need to splice the given address on to the end of the // address of the node which gave it to us. uint64_t path = LabelSplicer_splice(addr.path, fromNode->path); if (path == UINT64_MAX) { /* common, lots of noise uint8_t discovered[60]; uint8_t fromAddr[60]; Address_print(discovered, &addr); Address_print(fromAddr, fromNode); Log_debug(log, "Dropping response [%s] from [%s] because route could not be spliced", discovered, fromAddr);*/ continue; } addr.path = path; } /*#ifdef Log_DEBUG uint8_t printedAddr[60]; Address_print(printedAddr, &addr); Log_debug(log, "discovered node [%s]", printedAddr); #endif*/ Address_getPrefix(&addr); if (!AddressCalc_validAddress(addr.ip6.bytes)) { struct Allocator* tmpAlloc = Allocator_child(alloc); String* printed = Address_toString(&addr, tmpAlloc); uint8_t ipPrinted[40]; Address_printIp(ipPrinted, &addr); Log_debug(log, "Was told garbage addr [%s] [%s]", printed->bytes, ipPrinted); Allocator_free(tmpAlloc); // This should never happen, badnode. continue; } Bits_memcpy(&out->elems[j++], &addr, sizeof(struct Address)); } out->length = j; return out; } void ReplySerializer_serialize(struct Address_List* addrs, Dict* out, struct Address* convertDirectorFor, struct Allocator* alloc) { if (!addrs->length) { return; } String* nodes = String_newBinary(NULL, addrs->length * Address_SERIALIZED_SIZE, alloc); struct VersionList* versions = VersionList_new(addrs->length, alloc); for (int i = 0; i < addrs->length; i++) { versions->versions[i] = addrs->elems[i].protocolVersion; if (!convertDirectorFor) { Address_serialize(&nodes->bytes[i * Address_SERIALIZED_SIZE], &addrs->elems[i]); } else { struct Address addr; Bits_memcpy(&addr, &addrs->elems[i], sizeof(struct Address)); addr.path = NumberCompress_getLabelFor(addr.path, convertDirectorFor->path); Address_serialize(&nodes->bytes[i * Address_SERIALIZED_SIZE], &addr); } } Dict_putStringC(out, "n", nodes, alloc); Dict_putStringC(out, "np", VersionList_stringify(versions, alloc), alloc); }
static inline int incomingFromRouter(struct Message* message, struct Ducttape_MessageHeader* dtHeader, struct SessionManager_Session* session, struct Ducttape_pvt* context) { uint8_t* pubKey = CryptoAuth_getHerPublicKey(&session->iface); if (!validEncryptedIP6(message)) { // Not valid cjdns IPv6, we'll try it as an IPv4 or ICANN-IPv6 packet // and check if we have an agreement with the node who sent it. Message_shift(message, IpTunnel_PacketInfoHeader_SIZE); struct IpTunnel_PacketInfoHeader* header = (struct IpTunnel_PacketInfoHeader*) message->bytes; uint8_t* addr = session->ip6; Bits_memcpyConst(header->nodeIp6Addr, addr, 16); Bits_memcpyConst(header->nodeKey, pubKey, 32); struct Interface* ipTun = &context->ipTunnel->nodeInterface; return ipTun->sendMessage(message, ipTun); } struct Address srcAddr = { .path = Endian_bigEndianToHost64(dtHeader->switchHeader->label_be) }; Bits_memcpyConst(srcAddr.key, pubKey, 32); //Log_debug(context->logger, "Got message from router.\n"); int ret = core(message, dtHeader, session, context); struct Node* n = RouterModule_getNode(srcAddr.path, context->routerModule); if (!n) { Address_getPrefix(&srcAddr); RouterModule_addNode(context->routerModule, &srcAddr, session->version); } else { n->reach += 1; RouterModule_updateReach(n, context->routerModule); } return ret; } static uint8_t incomingFromCryptoAuth(struct Message* message, struct Interface* iface) { struct Ducttape_pvt* context = Identity_cast((struct Ducttape_pvt*) iface->receiverContext); struct Ducttape_MessageHeader* dtHeader = getDtHeader(message, false); enum Ducttape_SessionLayer layer = dtHeader->layer; dtHeader->layer = Ducttape_SessionLayer_INVALID; struct SessionManager_Session* session = SessionManager_sessionForHandle(dtHeader->receiveHandle, context->sm); if (!session) { // This should never happen but there's no strong preventitive. Log_info(context->logger, "SESSION DISAPPEARED!"); return 0; } // If the packet came from a new session, put the send handle in the session. if (CryptoAuth_getState(iface) < CryptoAuth_ESTABLISHED) { // If this is true then the incoming message is definitely a handshake. if (message->length < 4) { debugHandles0(context->logger, session, "runt"); return Error_INVALID; } if (layer == Ducttape_SessionLayer_OUTER) { #ifdef Version_2_COMPAT if (dtHeader->currentSessionVersion >= 3) { session->version = dtHeader->currentSessionVersion; #endif Message_pop(message, &session->sendHandle_be, 4); #ifdef Version_2_COMPAT } else { session->sendHandle_be = dtHeader->currentSessionSendHandle_be; } #endif } else { // inner layer, always grab the handle Message_pop(message, &session->sendHandle_be, 4); debugHandles0(context->logger, session, "New session, incoming layer3"); } } switch (layer) { case Ducttape_SessionLayer_OUTER: return incomingFromRouter(message, dtHeader, session, context); case Ducttape_SessionLayer_INNER: return incomingForMe(message, dtHeader, session, context, CryptoAuth_getHerPublicKey(iface)); default: Assert_always(false); } // never reached. return 0; } static uint8_t outgoingFromCryptoAuth(struct Message* message, struct Interface* iface) { struct Ducttape_pvt* context = Identity_cast((struct Ducttape_pvt*) iface->senderContext); struct Ducttape_MessageHeader* dtHeader = getDtHeader(message, false); struct SessionManager_Session* session = SessionManager_sessionForHandle(dtHeader->receiveHandle, context->sm); enum Ducttape_SessionLayer layer = dtHeader->layer; dtHeader->layer = Ducttape_SessionLayer_INVALID; if (!session) { // This should never happen but there's no strong preventitive. Log_info(context->logger, "SESSION DISAPPEARED!"); return 0; } if (layer == Ducttape_SessionLayer_OUTER) { return sendToSwitch(message, dtHeader, session, context); } else if (layer == Ducttape_SessionLayer_INNER) { Log_debug(context->logger, "Sending layer3 message"); return outgoingFromMe(message, dtHeader, session, context); } else { Assert_true(0); } } /** * Handle an incoming control message from a switch. * * @param context the ducttape context. * @param message the control message, this should be alligned on the beginning of the content, * that is to say, after the end of the switch header. * @param switchHeader the header. * @param switchIf the interface which leads to the switch. */ static uint8_t handleControlMessage(struct Ducttape_pvt* context, struct Message* message, struct Headers_SwitchHeader* switchHeader, struct Interface* switchIf) { uint8_t labelStr[20]; uint64_t label = Endian_bigEndianToHost64(switchHeader->label_be); AddrTools_printPath(labelStr, label); if (message->length < Control_HEADER_SIZE) { Log_info(context->logger, "dropped runt ctrl packet from [%s]", labelStr); return Error_NONE; } struct Control* ctrl = (struct Control*) message->bytes; if (Checksum_engine(message->bytes, message->length)) { Log_info(context->logger, "ctrl packet from [%s] with invalid checksum.", labelStr); return Error_NONE; } bool pong = false; if (ctrl->type_be == Control_ERROR_be) { if (message->length < Control_Error_MIN_SIZE) { Log_info(context->logger, "dropped runt error packet from [%s]", labelStr); return Error_NONE; } uint64_t path = Endian_bigEndianToHost64(switchHeader->label_be); RouterModule_brokenPath(path, context->routerModule); uint8_t causeType = Headers_getMessageType(&ctrl->content.error.cause); if (causeType == Headers_SwitchHeader_TYPE_CONTROL) { if (message->length < Control_Error_MIN_SIZE + Control_HEADER_SIZE) { Log_info(context->logger, "error packet from [%s] containing runt cause packet", labelStr); return Error_NONE; } struct Control* causeCtrl = (struct Control*) &(&ctrl->content.error.cause)[1]; if (causeCtrl->type_be != Control_PING_be) { Log_info(context->logger, "error packet from [%s] caused by [%s] packet ([%u])", labelStr, Control_typeString(causeCtrl->type_be), Endian_bigEndianToHost16(causeCtrl->type_be)); } else { if (LabelSplicer_isOneHop(label) && ctrl->content.error.errorType_be == Endian_hostToBigEndian32(Error_UNDELIVERABLE)) { // this is our own InterfaceController complaining // because the node isn't responding to pings. return Error_NONE; } Log_debug(context->logger, "error packet from [%s] in response to ping, err [%u], length: [%u].", labelStr, Endian_bigEndianToHost32(ctrl->content.error.errorType_be), message->length); // errors resulting from pings are forwarded back to the pinger. pong = true; } } else if (causeType != Headers_SwitchHeader_TYPE_DATA) { Log_info(context->logger, "error packet from [%s] containing cause of unknown type [%u]", labelStr, causeType); } else { Log_info(context->logger, "error packet from [%s], error type [%u]", labelStr, Endian_bigEndianToHost32(ctrl->content.error.errorType_be)); } } else if (ctrl->type_be == Control_PONG_be) { pong = true; } else if (ctrl->type_be == Control_PING_be) { Message_shift(message, -Control_HEADER_SIZE); if (message->length < Control_Ping_MIN_SIZE) { Log_info(context->logger, "dropped runt ping"); return Error_INVALID; } struct Control_Ping* ping = (struct Control_Ping*) message->bytes; ping->magic = Control_Pong_MAGIC; ping->version_be = Endian_hostToBigEndian32(Version_CURRENT_PROTOCOL); Message_shift(message, Control_HEADER_SIZE); ctrl->type_be = Control_PONG_be; ctrl->checksum_be = 0; ctrl->checksum_be = Checksum_engine(message->bytes, message->length); Message_shift(message, Headers_SwitchHeader_SIZE); Log_info(context->logger, "got switch ping from [%s]", labelStr); switchIf->receiveMessage(message, switchIf); } else { Log_info(context->logger, "control packet of unknown type from [%s], type [%d]", labelStr, Endian_bigEndianToHost16(ctrl->type_be)); } if (pong && context->pub.switchPingerIf.receiveMessage) { // Shift back over the header Message_shift(message, Headers_SwitchHeader_SIZE); context->pub.switchPingerIf.receiveMessage( message, &context->pub.switchPingerIf); } return Error_NONE; }
static void responseCallback(struct RouterModule_Promise* promise, uint32_t lagMilliseconds, struct Node* fromNode, Dict* result) { struct RouteTracer_Trace* trace = Identity_cast((struct RouteTracer_Trace*)promise->userData); struct RouteTracer_pvt* ctx = Identity_cast((struct RouteTracer_pvt*)trace->tracer); if (!fromNode) { // trace has stalled. log(ctx->logger, trace, "STALLED request timed out"); noPeers(trace); return; } if (trace->pub.callback) { trace->pub.callback(&trace->pub, lagMilliseconds, fromNode, result); } String* nodes = Dict_getString(result, CJDHTConstants_NODES); if (nodes && (nodes->len == 0 || nodes->len % Address_SERIALIZED_SIZE != 0)) { log(ctx->logger, trace, "STALLED dropping unrecognized reply"); noPeers(trace); return; } struct VersionList* versions = NULL; String* versionsStr = Dict_getString(result, CJDHTConstants_NODE_PROTOCOLS); if (versionsStr) { versions = VersionList_parse(versionsStr, promise->alloc); #ifdef Version_1_COMPAT // Version 1 lies about the versions of other nodes, assume they're all v1. if (fromNode->version < 2) { for (int i = 0; i < (int)versions->length; i++) { versions->versions[i] = 1; } } #endif } struct Node* next = NULL; for (uint32_t i = 0; nodes && i < nodes->len; i += Address_SERIALIZED_SIZE) { struct Address addr; Address_parse(&addr, (uint8_t*) &nodes->bytes[i]); // calculate the ipv6 Address_getPrefix(&addr); // We need to splice the given address on to the end of the // address of the node which gave it to us. addr.path = LabelSplicer_splice(addr.path, fromNode->address.path); if (addr.path == UINT64_MAX) { log(ctx->logger, trace, "dropping node because route could not be spliced"); continue; } /*#ifdef Log_DEBUG uint8_t printedAddr[60]; Address_print(printedAddr, &addr); Log_debug(ctx->logger, "discovered node [%s]", printedAddr); #endif*/ if (!Bits_memcmp(ctx->myAddress, addr.ip6.bytes, 16)) { // Any path which loops back through us is necessarily a dead route. uint8_t printedAddr[60]; Address_print(printedAddr, &addr); Log_debug(ctx->logger, "Loop route [%s]", printedAddr); NodeStore_brokenPath(addr.path, ctx->nodeStore); continue; } if (!AddressCalc_validAddress(addr.ip6.bytes)) { log(ctx->logger, trace, "was told garbage"); // This should never happen, badnode. break; } // Nodes we are told about are inserted with 0 reach and assumed version 1. uint32_t version = (versions) ? versions->versions[i / Address_SERIALIZED_SIZE] : 1; struct Node* n = NodeStore_addNode(ctx->nodeStore, &addr, 0, version); if (!n) { // incompatible version, introduced to ourselves... } else if (!LabelSplicer_routesThrough(trace->target, n->address.path)) { // not on the way } else if (n->address.path <= fromNode->address.path) { // losing ground } else if (next && n->address.path >= next->address.path) { // not better than the one we have } else { next = n; } } if (fromNode->address.path == trace->target) { log(ctx->logger, trace, "Trace completed successfully"); noPeers(trace); return; } if (!nodes) { log(ctx->logger, trace, "No nodes in trace response"); } if (!next) { log(ctx->logger, trace, "STALLED no suitable peers in reply"); noPeers(trace); return; } if (!LabelSplicer_routesThrough(trace->target, next->address.path)) { log(ctx->logger, trace, "STALLED Nodestore broke the path of the best option"); noPeers(trace); return; } traceStep(trace, next); }
struct Node* NodeStore_getBest(struct Address* targetAddress, struct NodeStore* store) { struct NodeCollector_Element element = { .value = 0, .distance = UINT32_MAX, .node = NULL }; struct NodeCollector collector = { .capacity = 1, .targetPrefix = Address_getPrefix(targetAddress), .targetAddress = targetAddress, .nodes = &element, .logger = store->logger }; collector.thisNodeDistance = Address_getPrefix(store->thisNodeAddress) ^ collector.targetPrefix; for (uint32_t i = 0; i < store->size; i++) { NodeCollector_addNode(store->headers + i, store->nodes + i, &collector); } return element.node ? nodeForHeader(element.node, store) : NULL; } struct NodeList* NodeStore_getNodesByAddr(struct Address* address, const uint32_t max, const struct Allocator* allocator, struct NodeStore* store) { struct NodeCollector* collector = NodeCollector_new(address, max, store->thisNodeAddress, true, store->logger, allocator); for (uint32_t i = 0; i < store->size; i++) { DistanceNodeCollector_addNode(store->headers + i, store->nodes + i, collector); } struct NodeList* out = allocator->malloc(sizeof(struct NodeList), allocator); out->nodes = allocator->malloc(max * sizeof(char*), allocator); uint32_t outIndex = 0; for (uint32_t i = 0; i < max; i++) { if (collector->nodes[i].node != NULL && !memcmp(collector->nodes[i].body->address.ip6.bytes, address->ip6.bytes, 16)) { out->nodes[outIndex] = collector->nodes[i].body; outIndex++; } } out->size = outIndex; return out; } /** See: NodeStore.h */ struct NodeList* NodeStore_getClosestNodes(struct NodeStore* store, struct Address* targetAddress, struct Address* requestorsAddress, const uint32_t count, const bool allowNodesFartherThanUs, const struct Allocator* allocator) { struct NodeCollector* collector = NodeCollector_new(targetAddress, count, store->thisNodeAddress, allowNodesFartherThanUs, store->logger, allocator); // Don't send nodes which route back to the node which asked us. uint32_t index = (requestorsAddress) ? getSwitchIndex(requestorsAddress) : 0; // naive implementation, todo make this faster for (uint32_t i = 0; i < store->size; i++) { if (requestorsAddress && store->headers[i].switchIndex == index) { continue; } NodeCollector_addNode(store->headers + i, store->nodes + i, collector); } struct NodeList* out = allocator->malloc(sizeof(struct NodeList), allocator); out->nodes = allocator->malloc(count * sizeof(char*), allocator); uint32_t outIndex = 0; for (uint32_t i = 0; i < count; i++) { if (collector->nodes[i].node != NULL) { out->nodes[outIndex] = nodeForHeader(collector->nodes[i].node, store); outIndex++; } } out->size = outIndex; return out; } /** See: NodeStore.h */ void NodeStore_updateReach(const struct Node* const node, const struct NodeStore* const store) { store->headers[node - store->nodes].reach = node->reach; } uint32_t NodeStore_size(const struct NodeStore* const store) { return store->size; } struct Node* NodeStore_getNodeByNetworkAddr(uint64_t networkAddress_be, struct NodeStore* store) { for (uint32_t i = 0; i < store->size; i++) { if (networkAddress_be == store->nodes[i].address.networkAddress_be) { return &store->nodes[i]; } } return NULL; } void NodeStore_dumpTables(struct Writer* writer, struct NodeStore* store) { for (uint32_t i = 0; i < store->size; i++) { uint8_t out[60]; Address_print(out, &store->nodes[i].address); writer->write(out, 60, writer); snprintf((char*)out, 60, " link quality = %u\n", store->headers[i].reach); writer->write(out, strlen((char*)out), writer); } } void NodeStore_remove(struct Node* node, struct NodeStore* store) { assert(node >= store->nodes && node < store->nodes + store->size); #ifdef Log_DEBUG uint8_t addr[60]; Address_print(addr, &node->address); Log_debug1(store->logger, "Removing route to %s\n", addr); #endif store->size--; memcpy(node, &store->nodes[store->size], sizeof(struct Node)); struct NodeHeader* header = &store->headers[node - store->nodes]; memcpy(header, &store->headers[store->size], sizeof(struct NodeHeader)); } static void sendEntries(struct NodeStore* store, struct List_Item* last, String* txid, bool isMore) { struct Dict_Entry txidEntry = { .next = NULL, .key = CJDHTConstants_TXID, .val = &(Object) { .type = Object_STRING, .as.string = txid } }; struct Dict_Entry tableEntry = { .next = &txidEntry, .key = &(String) { .len = 12, .bytes = "routingTable" }, .val = &(Object) { .type = Object_LIST, .as.list = &last } }; Dict d; if (isMore) { struct Dict_Entry more = { .next = &tableEntry, .key = &(String) { .len = 4, .bytes = "more" }, .val = &(Object) { .type = Object_INTEGER, .as.number = 1 } }; d = &more; } else {
void NodeStore_addNode(struct NodeStore* store, struct Address* addr, const int64_t reachDifference) { Address_getPrefix(addr); if (memcmp(addr->ip6.bytes, store->thisNodeAddress, 16) == 0) { printf("got introduced to ourselves\n"); return; } if (addr->ip6.bytes[0] != 0xfc) { uint8_t address[60]; Address_print(address, addr); Log_critical1(store->logger, "tried to insert address %s which does not begin with 0xFC.\n", address); assert(false); } // TODO: maintain a sorted list. uint32_t pfx = Address_getPrefix(addr); if (store->size < store->capacity) { for (uint32_t i = 0; i < store->size; i++) { if (store->headers[i].addressPrefix == pfx && Address_isSameIp(&store->nodes[i].address, addr)) { int red = Address_checkRedundantRoute(&store->nodes[i].address, addr); if (red == 1) { #ifdef Log_DEBUG uint8_t nodeAddr[60]; Address_print(nodeAddr, &store->nodes[i].address); uint8_t newAddr[20]; Address_printNetworkAddress(newAddr, addr); Log_debug2(store->logger, "Found a better route to %s via %s\n", nodeAddr, newAddr); struct Node* n = NodeStore_getNodeByNetworkAddr(addr->networkAddress_be, store); if (n) { Log_warn(store->logger, "This route is probably invalid, giving up.\n"); continue; } #endif store->nodes[i].address.networkAddress_be = addr->networkAddress_be; } else if (red == 0 && store->nodes[i].address.networkAddress_be != addr->networkAddress_be) { // Completely different routes, store seperately. continue; } /*#ifdef Log_DEBUG uint32_t oldReach = store->headers[i].reach; #endif*/ adjustReach(&store->headers[i], reachDifference); /*#ifdef Log_DEBUG if (oldReach != store->headers[i].reach) { uint8_t nodeAddr[60]; Address_print(nodeAddr, addr); Log_debug3(store->logger, "Altering reach for node %s, old reach %u, new reach %u.\n", nodeAddr, oldReach, store->headers[i].reach); if (oldReach > store->headers[i].reach) { Log_debug(store->logger, "Reach was decreased!\n"); } } #endif*/ return; } #ifdef Log_DEBUG else if (store->headers[i].addressPrefix == pfx) { uint8_t realAddr[16]; AddressCalc_addressForPublicKey(realAddr, addr->key); assert(!memcmp(realAddr, addr->ip6.bytes, 16)); } #endif } #ifdef Log_DEBUG uint8_t nodeAddr[60]; Address_print(nodeAddr, addr); Log_debug2(store->logger, "Discovered node: %s reach %u\n", nodeAddr, reachDifference); #endif // Free space, regular insert. replaceNode(&store->nodes[store->size], &store->headers[store->size], addr); adjustReach(&store->headers[store->size], reachDifference); store->size++; return; } // The node whose reach OR distance is the least. // This means nodes who are close and have short reach will be removed uint32_t indexOfNodeToReplace = 0; uint32_t leastReachOrDistance = UINT32_MAX; for (uint32_t i = 0; i < store->size; i++) { uint32_t distance = store->headers[i].addressPrefix ^ pfx; if (distance == 0 && Address_isSame(&store->nodes[i].address, addr)) { // Node already exists adjustReach(&store->headers[store->size], reachDifference); return; } uint32_t reachOrDistance = store->headers[i].reach | distance; if (reachOrDistance < leastReachOrDistance) { leastReachOrDistance = reachOrDistance; indexOfNodeToReplace = i; } } replaceNode(&store->nodes[indexOfNodeToReplace], &store->headers[indexOfNodeToReplace], addr); adjustReach(&store->headers[indexOfNodeToReplace], reachDifference); }