int main() { printInfo(); volatile uint64_t b = 0x0123456789abcdef; volatile uint64_t sb = 0xefcdab8967452301; volatile uint32_t a = 0x01234567; volatile uint32_t sa = 0x67452301; volatile uint16_t c = 0xcabe; volatile uint16_t sc = 0xbeca; if (!Endian_isBigEndian()) { Assert_true(c == Endian_bigEndianToHost16(sc)); Assert_true(c == Endian_hostToBigEndian16(sc)); Assert_true(c == Endian_hostToLittleEndian16(c)); Assert_true(c == Endian_littleEndianToHost16(c)); Assert_true(a == Endian_bigEndianToHost32(sa)); Assert_true(a == Endian_hostToBigEndian32(sa)); Assert_true(a == Endian_hostToLittleEndian32(a)); Assert_true(a == Endian_littleEndianToHost32(a)); Assert_true(b == Endian_bigEndianToHost64(sb)); Assert_true(b == Endian_hostToBigEndian64(sb)); Assert_true(b == Endian_hostToLittleEndian64(b)); Assert_true(b == Endian_littleEndianToHost64(b)); } else { Assert_true(c == Endian_bigEndianToHost16(c)); Assert_true(c == Endian_hostToBigEndian16(c)); Assert_true(c == Endian_hostToLittleEndian16(sc)); Assert_true(c == Endian_littleEndianToHost16(sc)); Assert_true(a == Endian_bigEndianToHost32(a)); Assert_true(a == Endian_hostToBigEndian32(a)); Assert_true(a == Endian_hostToLittleEndian32(sa)); Assert_true(a == Endian_littleEndianToHost32(sa)); Assert_true(b == Endian_bigEndianToHost64(b)); Assert_true(b == Endian_hostToBigEndian64(b)); Assert_true(b == Endian_hostToLittleEndian64(sb)); Assert_true(b == Endian_littleEndianToHost64(sb)); } Assert_true(b == Endian_byteSwap64(sb)); Assert_true(a == Endian_byteSwap32(sa)); Assert_true(c == Endian_byteSwap16(sc)); return 0; }
static void sessionStats(Dict* args, void* vcontext, String* txid, struct Allocator* alloc) { struct Context* context = vcontext; int64_t* handleP = Dict_getInt(args, String_CONST("handle")); uint32_t handle = *handleP; struct SessionManager_Session* session = SessionManager_sessionForHandle(handle, context->sm); uint8_t* ip6 = SessionManager_getIp6(handle, context->sm); Dict* r = Dict_new(alloc); if (!session) { Dict_putString(r, String_CONST("error"), String_CONST("no such session"), alloc); Admin_sendMessage(r, txid, context->admin); return; } // both or neither Assert_true(ip6); uint8_t printedAddr[40]; AddrTools_printIp(printedAddr, ip6); Dict_putString(r, String_CONST("ip6"), String_new(printedAddr, alloc), alloc); Dict_putString(r, String_CONST("state"), String_new(CryptoAuth_stateString(session->cryptoAuthState), alloc), alloc); struct ReplayProtector* rp = CryptoAuth_getReplayProtector(session->internal); Dict_putInt(r, String_CONST("duplicates"), rp->duplicates, alloc); Dict_putInt(r, String_CONST("lostPackets"), rp->lostPackets, alloc); Dict_putInt(r, String_CONST("receivedOutOfRange"), rp->receivedOutOfRange, alloc); uint8_t* key = CryptoAuth_getHerPublicKey(session->internal); Dict_putString(r, String_CONST("publicKey"), Key_stringify(key, alloc), alloc); Dict_putInt(r, String_CONST("version"), session->version, alloc); Dict_putInt(r, String_CONST("handle"), Endian_bigEndianToHost32(session->receiveHandle_be), alloc); Dict_putInt(r, String_CONST("sendHandle"), Endian_bigEndianToHost32(session->sendHandle_be), alloc); Dict_putInt(r, String_CONST("timeOfLastIn"), session->timeOfLastIn, alloc); Dict_putInt(r, String_CONST("timeOfLastOut"), session->timeOfLastOut, alloc); Admin_sendMessage(r, txid, context->admin); return; }
static Iface_DEFUN switchErr(struct Message* msg, struct Pathfinder_pvt* pf) { struct PFChan_Core_SwitchErr switchErr; Message_pop(msg, &switchErr, PFChan_Core_SwitchErr_MIN_SIZE, NULL); uint64_t path = Endian_bigEndianToHost64(switchErr.sh.label_be); uint64_t pathAtErrorHop = Endian_bigEndianToHost64(switchErr.ctrlErr.cause.label_be); uint8_t pathStr[20]; AddrTools_printPath(pathStr, path); int err = Endian_bigEndianToHost32(switchErr.ctrlErr.errorType_be); Log_debug(pf->log, "switch err from [%s] type [%s][%d]", pathStr, Error_strerror(err), err); struct Node_Link* link = NodeStore_linkForPath(pf->nodeStore, path); uint8_t nodeAddr[16]; if (link) { Bits_memcpy(nodeAddr, link->child->address.ip6.bytes, 16); } NodeStore_brokenLink(pf->nodeStore, path, pathAtErrorHop); if (link) { // Don't touch the node again, it might be a dangling pointer SearchRunner_search(nodeAddr, 20, 3, pf->searchRunner, pf->alloc); } return NULL; }
struct EncodingScheme* EncodingScheme_fromList(List* scheme, struct Allocator* alloc) { struct EncodingScheme* list = Allocator_malloc(alloc, sizeof(struct EncodingScheme)); list->count = List_size(scheme); list->forms = Allocator_malloc(alloc, sizeof(struct EncodingScheme_Form) * list->count); for (int i = 0; i < (int)list->count; i++) { Dict* form = List_getDict(scheme, i); uint64_t* prefixLen = Dict_getInt(form, String_CONST("prefixLen")); uint64_t* bitCount = Dict_getInt(form, String_CONST("bitCount")); String* prefixStr = Dict_getString(form, String_CONST("prefix")); if (!prefixLen || !bitCount || !prefixStr || prefixStr->len != 8) { return NULL; } uint32_t prefix_be; if (Hex_decode((uint8_t*)&prefix_be, 4, prefixStr->bytes, 8) != 4) { return NULL; } list->forms[i].prefixLen = *prefixLen; list->forms[i].bitCount = *bitCount; list->forms[i].prefix = Endian_bigEndianToHost32(prefix_be); } if (!EncodingScheme_isSane(list)) { return NULL; } return list; }
static uint8_t sendMessageToIf1(struct Message* message, struct Interface* iface) { uint32_t nonce = Endian_bigEndianToHost32(((uint32_t*)message->bytes)[0]); printf("sent message <-- nonce=%d\n", nonce); Assert_always(message->length + message->padding <= BUFFER_SIZE); if1->receiveMessage(message, if1); return Error_NONE; }
static inline uint8_t incomingDHT(struct Message* message, struct Address* addr, struct Ducttape_pvt* context) { struct DHTMessage dht = { .address = addr, .binMessage = message, .allocator = message->alloc }; DHTModuleRegistry_handleIncoming(&dht, context->registry); // TODO(cjd): return something meaningful. return Error_NONE; } /** Header must not be encrypted and must be aligned on the beginning of the ipv6 header. */ static inline uint8_t sendToRouter(struct Message* message, struct Ducttape_MessageHeader* dtHeader, struct SessionManager_Session* session, struct Ducttape_pvt* context) { int safeDistance = SwitchHeader_SIZE; CryptoAuth_resetIfTimeout(session->internal); if (CryptoAuth_getState(session->internal) < CryptoAuth_HANDSHAKE3) { // Put the handle into the message so that it's authenticated. // see: sendToSwitch() //Log_debug(context->logger, "Sending receive handle under CryptoAuth"); Message_push(message, &session->receiveHandle_be, 4, NULL); safeDistance += CryptoHeader_SIZE; } else { // 16 for the authenticator, 4 for the nonce and 4 for the handle safeDistance += 24; } Message_shift(message, safeDistance, NULL); if (dtHeader->switchHeader) { if (message->bytes != (uint8_t*)dtHeader->switchHeader) { Bits_memmoveConst(message->bytes, dtHeader->switchHeader, SwitchHeader_SIZE); dtHeader->switchHeader = (struct SwitchHeader*) message->bytes; } } else { dtHeader->switchHeader = (struct SwitchHeader*) message->bytes; Bits_memset(dtHeader->switchHeader, 0, SwitchHeader_SIZE); } Message_shift(message, -safeDistance, NULL); SwitchHeader_setVersion(dtHeader->switchHeader, SwitchHeader_CURRENT_VERSION); SwitchHeader_setLabelShift(dtHeader->switchHeader, 0); dtHeader->switchHeader->label_be = Endian_hostToBigEndian64(dtHeader->switchLabel); // This comes out in outgoingFromCryptoAuth() then sendToSwitch() dtHeader->receiveHandle = Endian_bigEndianToHost32(session->receiveHandle_be); dtHeader->layer = Ducttape_SessionLayer_OUTER; return Interface_sendMessage(session->internal, message); }
static uint8_t receiveMessage(struct Message* received, struct Interface* interface) { struct CryptoAuth_Wrapper* wrapper = Identity_cast((struct CryptoAuth_Wrapper*) interface->receiverContext); union Headers_CryptoAuth* header = (union Headers_CryptoAuth*) received->bytes; if (received->length < (wrapper->authenticatePackets ? 20 : 4)) { cryptoAuthDebug0(wrapper, "Dropped runt"); return Error_UNDERSIZE_MESSAGE; } Assert_true(received->padding >= 12 || "need at least 12 bytes of padding in incoming message"); #ifdef Log_DEBUG Assert_true(!((uintptr_t)received->bytes % 4) || !"alignment fault"); #endif Message_shift(received, -4); uint32_t nonce = Endian_bigEndianToHost32(header->nonce); if (wrapper->nextNonce < 5) { if (nonce > 3 && nonce != UINT32_MAX && knowHerKey(wrapper)) { cryptoAuthDebug(wrapper, "Trying final handshake step, nonce=%u\n", nonce); uint8_t secret[32]; getSharedSecret(secret, wrapper->secret, wrapper->tempKey, NULL, wrapper->context->logger); // We'll optimistically advance the nextNonce value because decryptMessage() // passes the message on to the upper level and if this message causes a // response, we want the CA to be in ESTABLISHED state. // if the decryptMessage() call fails, we CryptoAuth_reset() it back. wrapper->nextNonce += 3; if (decryptMessage(wrapper, nonce, received, secret)) { cryptoAuthDebug0(wrapper, "Final handshake step succeeded"); Bits_memcpyConst(wrapper->secret, secret, 32); return callReceivedMessage(wrapper, received); } CryptoAuth_reset(&wrapper->externalInterface); cryptoAuthDebug0(wrapper, "Final handshake step failed"); return Error_UNDELIVERABLE; } } else if (nonce > 4) { if (decryptMessage(wrapper, nonce, received, wrapper->secret)) { return callReceivedMessage(wrapper, received); } else { cryptoAuthDebug0(wrapper, "Failed to decrypt message"); return Error_UNDELIVERABLE; } } else { cryptoAuthDebug0(wrapper, "Received handshake message during established connection"); } Message_shift(received, 4); return decryptHandshake(wrapper, nonce, received, header); }
static uint8_t sendMessageToIf2(struct Message* message, struct Interface* iface) { uint32_t nonce = Endian_bigEndianToHost32(((uint32_t*)message->bytes)[0]); printf("sent message --> nonce=%d%s\n", nonce, suppressMessages ? " SUPPRESSED" : ""); if (!suppressMessages) { Assert_always(message->length + message->padding <= BUFFER_SIZE); if2->receiveMessage(message, if2); } return Error_NONE; }
static void addressForNode(struct Address* addrOut, struct Message* msg) { struct PFChan_Node node; Message_pop(msg, &node, PFChan_Node_SIZE, NULL); Assert_true(!msg->length); addrOut->protocolVersion = Endian_bigEndianToHost32(node.version_be); addrOut->path = Endian_bigEndianToHost64(node.path_be); Bits_memcpy(addrOut->key, node.publicKey, 32); Bits_memcpy(addrOut->ip6.bytes, node.ip6, 16); }
static inline void translateVersion2(struct Message* message, struct Ducttape_MessageHeader* dtHeader) { uint32_t handle = Endian_bigEndianToHost32(((uint32_t*)message->bytes)[0]); uint32_t nonce = Endian_bigEndianToHost32(((uint32_t*)message->bytes)[1]); dtHeader->currentSessionVersion = 2; if (handle & HANDLE_FLAG_BIT) { // We have to doctor their handles to make them conform to the new protocol. // see sendToSwitch() where they are un-doctored when being sent back. handle &= ~HANDLE_FLAG_BIT; ((uint32_t*)message->bytes)[0] = Endian_bigEndianToHost32(handle); return; } // This has a 4 / 4294967296 risk of a false positive, losing a traffic packet // between 2 version2 nodes because the first 4 bytes of the content are mistaken // for a nonce. if (nonce <= 3) { dtHeader->currentSessionSendHandle_be = Endian_bigEndianToHost32(handle); Message_shift(message, -4); return; } dtHeader->currentSessionVersion = 3; }
/** Header must not be encrypted and must be aligned on the beginning of the ipv6 header. */ static inline uint8_t sendToRouter(struct Message* message, struct Ducttape_MessageHeader* dtHeader, struct SessionManager_Session* session, struct Ducttape_pvt* context) { int safeDistance = Headers_SwitchHeader_SIZE; if (CryptoAuth_getState(&session->iface) < CryptoAuth_HANDSHAKE3) { // Bug 104, see Version.h #ifdef Version_2_COMPAT if (session->version >= 3) { #endif // Put the handle into the message so that it's authenticated. // see: sendToSwitch() Log_debug(context->logger, "Sending receive handle under CryptoAuth"); Message_push(message, &session->receiveHandle_be, 4); #ifdef Version_2_COMPAT } else { // Otherwise it will be added on the other side. safeDistance += 4; } #endif safeDistance += Headers_CryptoAuth_SIZE; } else { // 16 for the authenticator, 4 for the nonce and 4 for the handle safeDistance += 24; } Message_shift(message, safeDistance); if (dtHeader->switchHeader) { if (message->bytes != (uint8_t*)dtHeader->switchHeader) { Bits_memmoveConst(message->bytes, dtHeader->switchHeader, Headers_SwitchHeader_SIZE); dtHeader->switchHeader = (struct Headers_SwitchHeader*) message->bytes; } } else { dtHeader->switchHeader = (struct Headers_SwitchHeader*) message->bytes; Bits_memset(dtHeader->switchHeader, 0, Headers_SwitchHeader_SIZE); } Message_shift(message, -safeDistance); dtHeader->switchHeader->label_be = Endian_hostToBigEndian64(dtHeader->switchLabel); // This comes out in outgoingFromCryptoAuth() then sendToSwitch() dtHeader->receiveHandle = Endian_bigEndianToHost32(session->receiveHandle_be); dtHeader->layer = Ducttape_SessionLayer_OUTER; return session->iface.sendMessage(message, &session->iface); }
static uint8_t receiveMessage(struct Message* received, struct Interface* interface) { struct CryptoAuth_Wrapper* wrapper = (struct CryptoAuth_Wrapper*) interface->receiverContext; union Headers_CryptoAuth* header = (union Headers_CryptoAuth*) received->bytes; if (received->length < (wrapper->authenticatePackets ? 20 : 4)) { Log_debug(wrapper->context->logger, "Dropped runt"); return Error_UNDERSIZE_MESSAGE; } Assert_true(received->padding >= 12 || "need at least 12 bytes of padding in incoming message"); #ifdef Log_DEBUG Assert_true(!((uintptr_t)received->bytes % 4) || !"alignment fault"); #endif Message_shift(received, -4); uint32_t nonce = Endian_bigEndianToHost32(header->nonce); if (wrapper->nextNonce < 5) { if (nonce > 3 && nonce != UINT32_MAX && knowHerKey(wrapper)) { Log_debug(wrapper->context->logger, "@%p Trying final handshake step, nonce=%u\n", (void*) wrapper, nonce); uint8_t secret[32]; getSharedSecret(secret, wrapper->secret, wrapper->tempKey, NULL, wrapper->context->logger); if (decryptMessage(wrapper, nonce, received, secret)) { Log_debug(wrapper->context->logger, "Final handshake step succeeded.\n"); wrapper->nextNonce += 3; Bits_memcpyConst(wrapper->secret, secret, 32); return Error_NONE; } CryptoAuth_reset(&wrapper->externalInterface); Log_debug(wrapper->context->logger, "Final handshake step failed.\n"); } } else if (nonce > 2 && decryptMessage(wrapper, nonce, received, wrapper->secret)) { // If decryptMessage returns false then we will try the packet as a handshake. return Error_NONE; } else { Log_debug(wrapper->context->logger, "Decryption failed, trying message as a handshake.\n"); } Message_shift(received, 4); return decryptHandshake(wrapper, nonce, received, header); }
static inline uint8_t* extractPublicKey(struct Message* message, uint64_t switchLabel_be, struct Log* logger) { union Headers_CryptoAuth* caHeader = (union Headers_CryptoAuth*) message->bytes; uint32_t nonce = Endian_bigEndianToHost32(caHeader->nonce); if (nonce > 3 && nonce < UINT32_MAX) { return NULL; } if (message->length < Headers_CryptoAuth_SIZE) { Log_debug(logger, "Dropped runt packet.\n"); return NULL; } return caHeader->handshake.publicKey; }
struct VersionList* VersionList_parse(String* str, struct Allocator* alloc) { const uint8_t numberSize = str->bytes[0]; if (str->len < 1 || numberSize == 0 || numberSize > 4) { return NULL; } uint32_t length = (str->len - 1) / numberSize; if ((length * numberSize) != (str->len - 1)) { return NULL; } struct VersionList* list = VersionList_new(length, alloc); struct Reader* r = ArrayReader_new(str->bytes + 1, str->len - 1, alloc); for (int i = 0; i < (int)list->length; i++) { uint32_t ver = 0; Reader_read(r, (uint8_t*) &ver, numberSize); ver = Endian_bigEndianToHost32(ver); list->versions[i] = ver >> ((4-numberSize) * 8); } return list; }
static int handleFromPathfinder(enum PFChan_Pathfinder ev, struct Message* msg, struct EventEmitter_pvt* ee, struct Pathfinder* pf) { switch (ev) { default: return false; case PFChan_Pathfinder_CONNECT: { struct PFChan_Pathfinder_Connect connect; Message_shift(msg, -8, NULL); Message_pop(msg, &connect, PFChan_Pathfinder_Connect_SIZE, NULL); pf->superiority = Endian_bigEndianToHost32(connect.superiority_be); pf->version = Endian_bigEndianToHost32(connect.version_be); Bits_memcpy(pf->userAgent, connect.userAgent, 64); pf->state = Pathfinder_state_CONNECTED; struct PFChan_Core_Connect resp; resp.version_be = Endian_bigEndianToHost32(Version_CURRENT_PROTOCOL); resp.pathfinderId_be = Endian_hostToBigEndian32(pf->pathfinderId); Bits_memcpy(resp.publicKey, ee->publicKey, 32); Message_push(msg, &resp, PFChan_Core_Connect_SIZE, NULL); Message_push32(msg, PFChan_Core_CONNECT, NULL); struct Message* sendMsg = Message_clone(msg, msg->alloc); Iface_CALL(sendToPathfinder, sendMsg, pf); break; } case PFChan_Pathfinder_SUPERIORITY: { Message_shift(msg, -8, NULL); pf->superiority = Message_pop32(msg, NULL); struct Message* resp = pathfinderMsg(PFChan_Core_PATHFINDER, pf, msg->alloc); Iface_CALL(incomingFromCore, resp, &ee->trickIf); break; } case PFChan_Pathfinder_PING: { struct Message* sendMsg = Message_clone(msg, msg->alloc); Iface_send(&pf->iface, sendMsg); break; } case PFChan_Pathfinder_PONG: { Message_shift(msg, -8, NULL); uint32_t cookie = Message_pop32(msg, NULL); uint32_t count = Message_pop32(msg, NULL); if (cookie != PING_MAGIC || count > pf->bytesSinceLastPing) { pf->state = Pathfinder_state_ERROR; struct Message* resp = pathfinderMsg(PFChan_Core_PATHFINDER_GONE, pf, msg->alloc); Iface_CALL(incomingFromCore, resp, &ee->trickIf); } else { pf->bytesSinceLastPing -= count; } break; } case PFChan_Pathfinder_PATHFINDERS: { for (int i = 0; i < ee->pathfinders->length; i++) { struct Pathfinder* xpf = ArrayList_Pathfinders_get(ee->pathfinders, i); if (!xpf || xpf->state != Pathfinder_state_CONNECTED) { continue; } struct Allocator* alloc = Allocator_child(msg->alloc); struct Message* resp = pathfinderMsg(PFChan_Core_PATHFINDER, pf, alloc); Iface_CALL(sendToPathfinder, resp, pf); Allocator_free(alloc); } break; } } return true; }
/** * 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; }
/** * This is called as sendMessage() by the switch. * There is only one switch interface which sends all traffic. * message is aligned on the beginning of the switch header. */ static uint8_t incomingFromSwitch(struct Message* message, struct Interface* switchIf) { struct Context* context = switchIf->senderContext; struct Headers_SwitchHeader* switchHeader = (struct Headers_SwitchHeader*) message->bytes; Message_shift(message, -Headers_SwitchHeader_SIZE); // The label comes in reversed from the switch because the switch doesn't know that we aren't // another switch ready to parse more bits, bit reversing the label yields the source address. switchHeader->label_be = Bits_bitReverse64(switchHeader->label_be); if (Headers_getMessageType(switchHeader) == MessageType_CONTROL) { struct Control* ctrl = (struct Control*) (switchHeader + 1); if (ctrl->type_be == Control_ERROR_be) { if (memcmp(&ctrl->content.error.cause.label_be, &switchHeader->label_be, 8)) { Log_warn(context->logger, "Different label for cause than return packet, this shouldn't happen. " "Perhaps a packet was corrupted.\n"); return 0; } uint32_t errType_be = ctrl->content.error.errorType_be; if (errType_be == Endian_bigEndianToHost32(Error_MALFORMED_ADDRESS)) { Log_info(context->logger, "Got malformed-address error, removing route.\n"); RouterModule_brokenPath(switchHeader->label_be, context->routerModule); return 0; } Log_info1(context->logger, "Got error packet, error type: %d", Endian_bigEndianToHost32(errType_be)); } return 0; } uint8_t* herKey = extractPublicKey(message, switchHeader->label_be, context->logger); int herAddrIndex; if (herKey) { uint8_t herAddrStore[16]; AddressCalc_addressForPublicKey(herAddrStore, herKey); if (herAddrStore[0] != 0xFC) { Log_debug(context->logger, "Got message from peer whose address is not in fc00::/8 range.\n"); return 0; } herAddrIndex = AddressMapper_put(switchHeader->label_be, herAddrStore, &context->addrMap); } else { herAddrIndex = AddressMapper_indexOf(switchHeader->label_be, &context->addrMap); if (herAddrIndex == -1) { struct Node* n = RouterModule_getNode(switchHeader->label_be, context->routerModule); if (n) { herAddrIndex = AddressMapper_put(switchHeader->label_be, n->address.ip6.bytes, &context->addrMap); } else { #ifdef Log_DEBUG uint8_t switchAddr[20]; struct Address addr; addr.networkAddress_be = switchHeader->label_be; Address_printNetworkAddress(switchAddr, &addr); Log_debug1(context->logger, "Dropped traffic packet from unknown node. (%s)\n", &switchAddr); #endif return 0; } } } uint8_t* herAddr = context->addrMap.addresses[herAddrIndex]; // This is needed so that the priority and other information // from the switch header can be passed on properly. context->switchHeader = switchHeader; context->session = SessionManager_getSession(herAddr, herKey, context->sm); // This goes to incomingFromCryptoAuth() // then incomingFromRouter() then core() context->layer = OUTER_LAYER; context->session->receiveMessage(message, context->session); return 0; }
/** * This is called as sendMessage() by the switch. * There is only one switch interface which sends all traffic. * message is aligned on the beginning of the switch header. */ static uint8_t incomingFromSwitch(struct Message* message, struct Interface* switchIf) { struct Ducttape_pvt* context = Identity_cast((struct Ducttape_pvt*)switchIf->senderContext); struct Ducttape_MessageHeader* dtHeader = getDtHeader(message, true); struct Headers_SwitchHeader* switchHeader = (struct Headers_SwitchHeader*) message->bytes; Message_shift(message, -Headers_SwitchHeader_SIZE); // The label comes in reversed from the switch because the switch doesn't know that we aren't // another switch ready to parse more bits, bit reversing the label yields the source address. switchHeader->label_be = Bits_bitReverse64(switchHeader->label_be); if (Headers_getMessageType(switchHeader) == Headers_SwitchHeader_TYPE_CONTROL) { return handleControlMessage(context, message, switchHeader, switchIf); } if (message->length < 8) { Log_info(context->logger, "runt"); return Error_INVALID; } #ifdef Version_2_COMPAT translateVersion2(message, dtHeader); #endif // #1 try to get the session using the handle. uint32_t nonceOrHandle = Endian_bigEndianToHost32(((uint32_t*)message->bytes)[0]); struct SessionManager_Session* session = NULL; if (nonceOrHandle > 3) { // Run message, it's a handle. session = SessionManager_sessionForHandle(nonceOrHandle, context->sm); Message_shift(message, -4); if (session) { uint32_t nonce = Endian_bigEndianToHost32(((uint32_t*)message->bytes)[0]); if (nonce == ~0u) { Log_debug(context->logger, "Got connectToMe packet at switch layer"); return 0; } debugHandlesAndLabel(context->logger, session, Endian_bigEndianToHost64(switchHeader->label_be), "running session nonce[%u]", nonce); dtHeader->receiveHandle = nonceOrHandle; } else { Log_debug(context->logger, "Got message with unrecognized handle"); } } else if (message->length >= Headers_CryptoAuth_SIZE) { union Headers_CryptoAuth* caHeader = (union Headers_CryptoAuth*) message->bytes; uint8_t ip6[16]; uint8_t* herKey = caHeader->handshake.publicKey; AddressCalc_addressForPublicKey(ip6, herKey); // a packet which claims to be "from us" causes problems if (AddressCalc_validAddress(ip6) && Bits_memcmp(ip6, &context->myAddr, 16)) { session = SessionManager_getSession(ip6, herKey, context->sm); debugHandlesAndLabel(context->logger, session, Endian_bigEndianToHost64(switchHeader->label_be), "new session nonce[%d]", nonceOrHandle); dtHeader->receiveHandle = Endian_bigEndianToHost32(session->receiveHandle_be); } else { Log_debug(context->logger, "Got message with invalid ip addr"); } } if (!session) { #ifdef Log_INFO uint8_t path[20]; AddrTools_printPath(path, Endian_bigEndianToHost64(switchHeader->label_be)); Log_info(context->logger, "Dropped traffic packet from unknown node. [%s]", path); #endif return 0; } // This is needed so that the priority and other information // from the switch header can be passed on properly. dtHeader->switchHeader = switchHeader; // This goes to incomingFromCryptoAuth() // then incomingFromRouter() then core() dtHeader->layer = Ducttape_SessionLayer_OUTER; if (session->iface.receiveMessage(message, &session->iface) == Error_AUTHENTICATION) { debugHandlesAndLabel(context->logger, session, Endian_bigEndianToHost64(switchHeader->label_be), "Failed decrypting message NoH[%d] state[%d]", nonceOrHandle, CryptoAuth_getState(&session->iface)); return Error_AUTHENTICATION; } return 0; }
static Iface_DEFUN handlePing(struct Message* msg, struct ControlHandler_pvt* ch, uint64_t label, uint8_t* labelStr, uint16_t messageType_be) { if (msg->length < handlePing_MIN_SIZE) { Log_info(ch->log, "DROP runt ping"); return NULL; } struct Control* ctrl = (struct Control*) msg->bytes; Message_shift(msg, -Control_Header_SIZE, NULL); // Ping and keyPing share version location struct Control_Ping* ping = (struct Control_Ping*) msg->bytes; uint32_t herVersion = Endian_bigEndianToHost32(ping->version_be); if (!Version_isCompatible(Version_CURRENT_PROTOCOL, herVersion)) { Log_debug(ch->log, "DROP ping from incompatible version [%d]", herVersion); return NULL; } if (messageType_be == Control_KEYPING_be) { Log_debug(ch->log, "got switch keyPing from [%s]", labelStr); if (msg->length < Control_KeyPing_HEADER_SIZE) { // min keyPing size is longer Log_debug(ch->log, "DROP runt keyPing"); return NULL; } if (msg->length > Control_KeyPing_MAX_SIZE) { Log_debug(ch->log, "DROP long keyPing"); return NULL; } if (ping->magic != Control_KeyPing_MAGIC) { Log_debug(ch->log, "DROP keyPing (bad magic)"); return NULL; } struct Control_KeyPing* keyPing = (struct Control_KeyPing*) msg->bytes; keyPing->magic = Control_KeyPong_MAGIC; ctrl->header.type_be = Control_KEYPONG_be; Bits_memcpy(keyPing->key, ch->myPublicKey, 32); } else if (messageType_be == Control_PING_be) { // Happens in benchmark. //Log_debug(ch->log, "got switch ping from [%s]", labelStr); if (ping->magic != Control_Ping_MAGIC) { Log_debug(ch->log, "DROP ping (bad magic)"); return NULL; } ping->magic = Control_Pong_MAGIC; ctrl->header.type_be = Control_PONG_be; } else { Assert_failure("2+2=5"); } ping->version_be = Endian_hostToBigEndian32(Version_CURRENT_PROTOCOL); Message_shift(msg, Control_Header_SIZE, NULL); ctrl->header.checksum_be = 0; ctrl->header.checksum_be = Checksum_engine(msg->bytes, msg->length); Message_shift(msg, RouteHeader_SIZE, NULL); struct RouteHeader* routeHeader = (struct RouteHeader*) msg->bytes; Bits_memset(routeHeader, 0, RouteHeader_SIZE); SwitchHeader_setVersion(&routeHeader->sh, SwitchHeader_CURRENT_VERSION); routeHeader->sh.label_be = Endian_hostToBigEndian64(label); routeHeader->flags |= RouteHeader_flags_CTRLMSG; return Iface_next(&ch->pub.coreIf, msg); }
// Called by the TUN device. static inline uint8_t incomingFromTun(struct Message* message, struct Interface* iface) { struct Ducttape_pvt* context = Identity_cast((struct Ducttape_pvt*) iface->receiverContext); uint16_t ethertype = TUNMessageType_pop(message); struct Headers_IP6Header* header = (struct Headers_IP6Header*) message->bytes; int version = Headers_getIpVersion(message->bytes); if ((ethertype == Ethernet_TYPE_IP4 && version != 4) || (ethertype == Ethernet_TYPE_IP6 && version != 6)) { Log_warn(context->logger, "dropped packet because ip version [%d] " "doesn't match ethertype [%u].", version, Endian_bigEndianToHost16(ethertype)); return Error_INVALID; } if (ethertype != Ethernet_TYPE_IP6 || !AddressCalc_validAddress(header->sourceAddr)) { return context->ipTunnel->tunInterface.sendMessage(message, &context->ipTunnel->tunInterface); } if (Bits_memcmp(header->sourceAddr, context->myAddr.ip6.bytes, 16)) { uint8_t expectedSource[40]; AddrTools_printIp(expectedSource, context->myAddr.ip6.bytes); uint8_t packetSource[40]; AddrTools_printIp(packetSource, header->sourceAddr); Log_warn(context->logger, "dropped packet from [%s] because all messages must have source address [%s]", (char*) packetSource, (char*) expectedSource); return Error_INVALID; } if (!Bits_memcmp(header->destinationAddr, context->myAddr.ip6.bytes, 16)) { // I'm Gonna Sit Right Down and Write Myself a Letter TUNMessageType_push(message, ethertype); iface->sendMessage(message, iface); return Error_NONE; } struct Ducttape_MessageHeader* dtHeader = getDtHeader(message, true); struct Node* bestNext = RouterModule_lookup(header->destinationAddr, context->routerModule); struct SessionManager_Session* nextHopSession; if (bestNext) { nextHopSession = SessionManager_getSession(bestNext->address.ip6.bytes, bestNext->address.key, context->sm); bestNext->version = nextHopSession->version = (bestNext->version > nextHopSession->version) ? bestNext->version : nextHopSession->version; dtHeader->switchLabel = bestNext->address.path; dtHeader->nextHopReceiveHandle = Endian_bigEndianToHost32(nextHopSession->receiveHandle_be); if (!Bits_memcmp(header->destinationAddr, bestNext->address.ip6.bytes, 16)) { // Direct send, skip the innermost layer of encryption. #ifdef Log_DEBUG uint8_t nhAddr[60]; Address_print(nhAddr, &bestNext->address); Log_debug(context->logger, "Forwarding data to %s (last hop)\n", nhAddr); #endif return sendToRouter(message, dtHeader, nextHopSession, context); } // else { the message will need to be 3 layer encrypted but since we already did a lookup // of the best node to forward to, we can skip doing another lookup by storing a pointer // to that node in the context (bestNext). } else { #ifdef Log_WARN uint8_t thisAddr[40]; uint8_t destAddr[40]; AddrTools_printIp(thisAddr, context->myAddr.ip6.bytes); AddrTools_printIp(destAddr, header->destinationAddr); Log_warn(context->logger, "Dropped message from TUN because this node [%s] is closest to dest [%s]", thisAddr, destAddr); #endif return Error_UNDELIVERABLE; } #ifdef Log_DEBUG uint8_t destAddr[40]; AddrTools_printIp(destAddr, header->destinationAddr); uint8_t nhAddr[60]; Address_print(nhAddr, &bestNext->address); Log_debug(context->logger, "Sending to [%s] via [%s]", destAddr, nhAddr); #endif struct SessionManager_Session* session = SessionManager_getSession(header->destinationAddr, NULL, context->sm); // Copy the IP6 header back from where the CA header will be placed. // this is a mess. // We can't just copy the header to a safe place because the CryptoAuth // might buffer the message and send a connect-to-me packet and when the // hello packet comes in return, the CA will send the message and the header // needs to be in the message buffer. // // The CryptoAuth may send a 120 byte CA header and it might only send a 4 byte // nonce and 16 byte authenticator depending on its state. if (CryptoAuth_getState(&session->iface) < CryptoAuth_HANDSHAKE3) { // shift, copy, shift because shifting asserts that there is enough buffer space. Message_shift(message, Headers_CryptoAuth_SIZE + 4); Bits_memcpyConst(message->bytes, header, Headers_IP6Header_SIZE); Message_shift(message, -(Headers_IP6Header_SIZE + Headers_CryptoAuth_SIZE + 4)); // now push the receive handle *under* the CA header. Message_push(message, &session->receiveHandle_be, 4); debugHandles0(context->logger, session, "layer3 sending start message"); } else { // shift, copy, shift because shifting asserts that there is enough buffer space. Message_shift(message, 20); Bits_memmoveConst(message->bytes, header, Headers_IP6Header_SIZE); Message_shift(message, -(20 + Headers_IP6Header_SIZE)); debugHandles0(context->logger, session, "layer3 sending run message"); } // This comes out at outgoingFromCryptoAuth() then outgoingFromMe() dtHeader->receiveHandle = Endian_bigEndianToHost32(session->receiveHandle_be); dtHeader->layer = Ducttape_SessionLayer_INNER; return session->iface.sendMessage(message, &session->iface); }
static void testAddr(struct Context* ctx, char* addr4, int prefix4, int alloc4, char* addr6, int prefix6, int alloc6) { struct Allocator* alloc = Allocator_child(ctx->alloc); struct IpTunnel* ipTun = IpTunnel_new(ctx->log, ctx->base, alloc, ctx->rand, NULL); struct Sockaddr* sa4 = NULL; struct Sockaddr_storage ip6ToGive; struct Sockaddr_storage ip4ToGive; if (addr4) { Assert_true(!Sockaddr_parse(addr4, &ip4ToGive)); sa4 = &ip4ToGive.addr; Assert_true(Sockaddr_getFamily(sa4) == Sockaddr_AF_INET); } struct Sockaddr* sa6 = NULL; if (addr6) { Assert_true(!Sockaddr_parse(addr6, &ip6ToGive)); sa6 = &ip6ToGive.addr; Assert_true(Sockaddr_getFamily(sa6) == Sockaddr_AF_INET6); } IpTunnel_allowConnection(ctx->pubKey, sa6, prefix6, alloc6, sa4, prefix4, alloc4, ipTun); struct Message* msg = Message_new(64, 512, alloc); const char* requestForAddresses = "d" "1:q" "21:IpTunnel_getAddresses" "4:txid" "4:abcd" "e"; CString_strcpy(msg->bytes, requestForAddresses); msg->length = CString_strlen(requestForAddresses); Message_push(msg, NULL, Headers_UDPHeader_SIZE, NULL); struct Headers_UDPHeader* uh = (struct Headers_UDPHeader*) msg->bytes; uh->length_be = Endian_hostToBigEndian16(msg->length - Headers_UDPHeader_SIZE); uint16_t* checksum = &((struct Headers_UDPHeader*) msg->bytes)->checksum_be; *checksum = 0; uint32_t length = msg->length; // Because of old reasons, we need to have at least an empty IPv6 header Message_push(msg, NULL, Headers_IP6Header_SIZE, NULL); struct Headers_IP6Header* ip = (struct Headers_IP6Header*) msg->bytes; Headers_setIpVersion(ip); ip->payloadLength_be = Endian_hostToBigEndian16(msg->length - Headers_IP6Header_SIZE); ip->nextHeader = 17; *checksum = Checksum_udpIp6(ip->sourceAddr, (uint8_t*) uh, length); pushRouteDataHeaders(ctx, msg); struct IfaceContext* nodeIf = Allocator_calloc(alloc, sizeof(struct IfaceContext), 1); nodeIf->ctx = ctx; nodeIf->iface.send = responseWithIpCallback; struct IfaceContext* tunIf = Allocator_calloc(alloc, sizeof(struct IfaceContext), 1); tunIf->ctx = ctx; tunIf->iface.send = messageToTun; Iface_plumb(&nodeIf->iface, &ipTun->nodeInterface); Iface_plumb(&tunIf->iface, &ipTun->tunInterface); ctx->expectedResponse = getExpectedResponse(sa4, prefix4, alloc4, sa6, prefix6, alloc6, alloc); Iface_send(&nodeIf->iface, msg); Assert_true(ctx->called == 2); ctx->called = 0; if (sa4) { uint8_t* addrBytes = NULL; Assert_true(Sockaddr_getAddress(sa4, &addrBytes) == 4); uint32_t addr; Bits_memcpy(&addr, addrBytes, 4); addr = Endian_bigEndianToHost32(addr); // Send from the address specified Assert_true(trySend4(alloc, addr, &nodeIf->iface, ctx)); if (alloc4 < 32) { // Send from another (random) address in the prefix uint32_t flip = Random_uint32(ctx->rand) >> alloc4; if (prefix4 != 32) { Assert_true(trySend4(alloc, addr ^ flip, &nodeIf->iface, ctx)); } else { // If netSize is not specified, we do not allow multi-address Assert_true(!trySend4(alloc, addr ^ flip, &nodeIf->iface, ctx)); } } else {
/** * Messages with content encrypted and header decrypted are sent here to be forwarded. * they may come from us, or from another node and may be to us or to any other node. * Message is aligned on the beginning of the ipv6 header. */ static inline int core(struct Message* message, struct Ducttape_MessageHeader* dtHeader, struct SessionManager_Session* session, struct Ducttape_pvt* context) { struct Headers_IP6Header* ip6Header = (struct Headers_IP6Header*) message->bytes; dtHeader->ip6Header = ip6Header; if (isForMe(message, context)) { Message_shift(message, -Headers_IP6Header_SIZE); if (Bits_memcmp(session->ip6, ip6Header->sourceAddr, 16)) { // triple encrypted // This call goes to incomingForMe() struct SessionManager_Session* session = SessionManager_getSession(ip6Header->sourceAddr, NULL, context->sm); #ifdef Log_DEBUG uint8_t addr[40]; AddrTools_printIp(addr, ip6Header->sourceAddr); Log_debug(context->logger, "Incoming layer3 message, ostensibly from [%s]", addr); #endif dtHeader->receiveHandle = Endian_bigEndianToHost32(session->receiveHandle_be); dtHeader->layer = Ducttape_SessionLayer_INNER; return session->iface.receiveMessage(message, &session->iface); } else { // double encrypted, inner layer plaintext. // The session is still set from the router-to-router traffic and that is the one we use // to determine the node's id. return incomingForMe(message, dtHeader, session, context, CryptoAuth_getHerPublicKey(&session->iface)); } } if (ip6Header->hopLimit == 0) { Log_debug(context->logger, "dropped message because hop limit has been exceeded.\n"); // TODO: send back an error message in response. return Error_UNDELIVERABLE; } ip6Header->hopLimit--; struct SessionManager_Session* nextHopSession = NULL; if (!dtHeader->nextHopReceiveHandle || !dtHeader->switchLabel) { struct Node* n = RouterModule_lookup(ip6Header->destinationAddr, context->routerModule); if (n) { nextHopSession = SessionManager_getSession(n->address.ip6.bytes, n->address.key, context->sm); dtHeader->switchLabel = n->address.path; } } else { nextHopSession = SessionManager_sessionForHandle(dtHeader->nextHopReceiveHandle, context->sm); } if (nextHopSession) { #ifdef Log_DEBUG struct Address addr; Bits_memcpyConst(addr.ip6.bytes, nextHopSession->ip6, 16); addr.path = dtHeader->switchLabel; uint8_t nhAddr[60]; Address_print(nhAddr, &addr); if (Bits_memcmp(ip6Header->destinationAddr, addr.ip6.bytes, 16)) { // Potentially forwarding for ourselves. struct Address destination; Bits_memcpyConst(destination.ip6.bytes, ip6Header->destinationAddr, 16); uint8_t ipAddr[40]; Address_printIp(ipAddr, &destination); Log_debug(context->logger, "Forwarding data to %s via %s\n", ipAddr, nhAddr); } else { // Definitely forwarding on behalf of someone else. Log_debug(context->logger, "Forwarding data to %s (last hop)\n", nhAddr); } #endif return sendToRouter(message, dtHeader, nextHopSession, context); } #ifdef Log_INFO struct Address destination; Bits_memcpyConst(destination.ip6.bytes, ip6Header->destinationAddr, 16); uint8_t ipAddr[40]; Address_printIp(ipAddr, &destination); Log_info(context->logger, "Dropped message because this node is the closest known " "node to the destination %s.", ipAddr); #endif return Error_UNDELIVERABLE; }
/** * This is called as sendMessage() by the switch. * There is only one switch interface which sends all traffic. * message is aligned on the beginning of the switch header. */ static uint8_t incomingFromSwitch(struct Message* message, struct Interface* switchIf) { struct Ducttape* context = switchIf->senderContext; struct Headers_SwitchHeader* switchHeader = (struct Headers_SwitchHeader*) message->bytes; Message_shift(message, -Headers_SwitchHeader_SIZE); // The label comes in reversed from the switch because the switch doesn't know that we aren't // another switch ready to parse more bits, bit reversing the label yields the source address. switchHeader->label_be = Bits_bitReverse64(switchHeader->label_be); if (Headers_getMessageType(switchHeader) == Headers_SwitchHeader_TYPE_CONTROL) { uint8_t labelStr[20]; uint64_t label = Endian_bigEndianToHost64(switchHeader->label_be); AddrTools_printPath(labelStr, label); if (message->length < Control_HEADER_SIZE) { Log_info1(context->logger, "dropped runt ctrl packet from [%s]", labelStr); return Error_NONE; } else { Log_debug1(context->logger, "ctrl packet from [%s]", labelStr); } struct Control* ctrl = (struct Control*) message->bytes; bool pong = false; if (ctrl->type_be == Control_ERROR_be) { if (message->length < Control_Error_MIN_SIZE) { Log_info1(context->logger, "dropped runt error packet from [%s]", labelStr); return Error_NONE; } Log_info2(context->logger, "error packet from [%s], error type [%d]", labelStr, Endian_bigEndianToHost32(ctrl->content.error.errorType_be)); RouterModule_brokenPath(Endian_bigEndianToHost64(switchHeader->label_be), 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_info1(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_info3(context->logger, "error packet from [%s] caused by [%s] packet ([%d])", labelStr, Control_typeString(causeCtrl->type_be), Endian_bigEndianToHost16(causeCtrl->type_be)); } else { Log_debug2(context->logger, "error packet from [%s] in response to ping, length: [%d].", labelStr, message->length); // errors resulting from pings are forwarded back to the pinger. pong = true; } } else if (causeType != Headers_SwitchHeader_TYPE_DATA) { Log_info1(context->logger, "error packet from [%s] containing cause of unknown type [%d]", labelStr); } } else if (ctrl->type_be == Control_PONG_be) { pong = true; } else if (ctrl->type_be == Control_PING_be) { ctrl->type_be = Control_PONG_be; Message_shift(message, Headers_SwitchHeader_SIZE); switchIf->receiveMessage(message, switchIf); } else { Log_info2(context->logger, "control packet of unknown type from [%s], type [%d]", labelStr, Endian_bigEndianToHost16(ctrl->type_be)); } if (pong) { // Shift back over the header Message_shift(message, Headers_SwitchHeader_SIZE); context->switchPingerIf->receiveMessage(message, context->switchPingerIf); } return Error_NONE; } uint8_t* herKey = extractPublicKey(message, switchHeader->label_be, context->logger); int herAddrIndex; if (herKey) { uint8_t herAddrStore[16]; AddressCalc_addressForPublicKey(herAddrStore, herKey); if (herAddrStore[0] != 0xFC) { Log_debug(context->logger, "Got message from peer whose address is not in fc00::/8 range.\n"); return 0; } herAddrIndex = AddressMapper_put(switchHeader->label_be, herAddrStore, &context->addrMap); } else { herAddrIndex = AddressMapper_indexOf(switchHeader->label_be, &context->addrMap); if (herAddrIndex == -1) { uint64_t label = Endian_bigEndianToHost64(switchHeader->label_be); struct Node* n = RouterModule_getNode(label, context->routerModule); if (n) { herAddrIndex = AddressMapper_put(switchHeader->label_be, n->address.ip6.bytes, &context->addrMap); } else { #ifdef Log_DEBUG uint8_t switchAddr[20]; AddrTools_printPath(switchAddr, Endian_bigEndianToHost64(switchHeader->label_be)); Log_debug1(context->logger, "Dropped traffic packet from unknown node. (%s)\n", &switchAddr); #endif return 0; } } } // If the source address is the same as the router address, no third layer of crypto. context->routerAddress = context->addrMap.entries[herAddrIndex].address; // This is needed so that the priority and other information // from the switch header can be passed on properly. context->switchHeader = switchHeader; context->session = SessionManager_getSession(context->routerAddress, herKey, context->sm); // This goes to incomingFromCryptoAuth() // then incomingFromRouter() then core() context->layer = OUTER_LAYER; context->session->receiveMessage(message, context->session); return 0; }
static uint8_t receiveMessage(struct Message* received, struct Interface* interface) { struct CryptoAuth_Wrapper* wrapper = Identity_check((struct CryptoAuth_Wrapper*) interface->receiverContext); union Headers_CryptoAuth* header = (union Headers_CryptoAuth*) received->bytes; if (received->length < 20) { cryptoAuthDebug0(wrapper, "DROP runt"); return Error_UNDERSIZE_MESSAGE; } Assert_true(received->padding >= 12 || "need at least 12 bytes of padding in incoming message"); Assert_true(!((uintptr_t)received->bytes % 4) || !"alignment fault"); Message_shift(received, -4, NULL); uint32_t nonce = Endian_bigEndianToHost32(header->nonce); if (!wrapper->established) { if (nonce > 3 && nonce != UINT32_MAX) { if (wrapper->nextNonce < 3) { // This is impossible because we have not exchanged hello and key messages. cryptoAuthDebug0(wrapper, "DROP Received a run message to an un-setup session"); return Error_UNDELIVERABLE; } cryptoAuthDebug(wrapper, "Trying final handshake step, nonce=%u\n", nonce); uint8_t secret[32]; Assert_ifParanoid(!Bits_isZero(wrapper->ourTempPrivKey, 32)); Assert_ifParanoid(!Bits_isZero(wrapper->herTempPubKey, 32)); getSharedSecret(secret, wrapper->ourTempPrivKey, wrapper->herTempPubKey, NULL, wrapper->context->logger); // We'll optimistically advance the nextNonce value because decryptMessage() // passes the message on to the upper level and if this message causes a // response, we want the CA to be in ESTABLISHED state. // if the decryptMessage() call fails, we CryptoAuth_reset() it back. wrapper->nextNonce += 3; if (decryptMessage(wrapper, nonce, received, secret)) { cryptoAuthDebug0(wrapper, "Final handshake step succeeded"); Bits_memcpyConst(wrapper->sharedSecret, secret, 32); // Now we're in run mode, no more handshake packets will be accepted Bits_memset(wrapper->ourTempPrivKey, 0, 32); Bits_memset(wrapper->ourTempPubKey, 0, 32); Bits_memset(wrapper->herTempPubKey, 0, 32); wrapper->established = true; return callReceivedMessage(wrapper, received); } CryptoAuth_reset(&wrapper->externalInterface); cryptoAuthDebug0(wrapper, "DROP Final handshake step failed"); return Error_UNDELIVERABLE; } Message_shift(received, 4, NULL); return decryptHandshake(wrapper, nonce, received, header); } else if (nonce > 3 && nonce != UINT32_MAX) { Assert_ifParanoid(!Bits_isZero(wrapper->sharedSecret, 32)); if (decryptMessage(wrapper, nonce, received, wrapper->sharedSecret)) { return callReceivedMessage(wrapper, received); } else { cryptoAuthDebug0(wrapper, "DROP Failed to decrypt message"); return Error_UNDELIVERABLE; } } else if (nonce < 2) { cryptoAuthDebug(wrapper, "hello packet during established session nonce=[%d]", nonce); Message_shift(received, 4, NULL); return decryptHandshake(wrapper, nonce, received, header); } else { // setup keys are already zeroed, not much we can do here. cryptoAuthDebug(wrapper, "DROP key packet during established session nonce=[%d]", nonce); return Error_UNDELIVERABLE; } Assert_true(0); }
static void handleBeacon(struct Message* msg, struct ETHInterface* context) { if (!context->beaconState) { // accepting beacons disabled. Log_debug(context->logger, "Dropping beacon because beaconing is disabled"); return; } struct sockaddr_ll addr; Bits_memcpyConst(&addr, &context->addrBase, sizeof(struct sockaddr_ll)); Message_pop(msg, addr.sll_addr, 8); if (msg->length < Headers_Beacon_SIZE) { // Oversize messages are ok because beacons may contain more information in the future. Log_debug(context->logger, "Dropping wrong size beacon, expected [%d] got [%d]", Headers_Beacon_SIZE, msg->length); return; } struct Headers_Beacon* beacon = (struct Headers_Beacon*) msg->bytes; uint32_t theirVersion = Endian_bigEndianToHost32(beacon->version_be); if (!Version_isCompatible(theirVersion, Version_CURRENT_PROTOCOL)) { #ifdef Log_DEBUG uint8_t mac[18]; AddrTools_printMac(mac, addr.sll_addr); Log_debug(context->logger, "Dropped beacon from [%s] which was version [%d] " "our version is [%d] making them incompatable", mac, theirVersion, Version_CURRENT_PROTOCOL); #endif return; } #ifdef Log_DEBUG uint8_t mac[18]; AddrTools_printMac(mac, addr.sll_addr); Log_debug(context->logger, "Got beacon from [%s]", mac); #endif String passStr = { .bytes = (char*) beacon->password, .len = Headers_Beacon_PASSWORD_LEN }; struct Interface* iface = MultiInterface_ifaceForKey(context->multiIface, addr.sll_addr); int ret = InterfaceController_registerPeer(context->ic, beacon->publicKey, &passStr, false, true, iface); if (ret != 0) { uint8_t mac[18]; AddrTools_printMac(mac, addr.sll_addr); Log_info(context->logger, "Got beacon from [%s] and registerPeer returned [%d]", mac, ret); } } static void sendBeacon(void* vcontext) { struct ETHInterface* context = Identity_cast((struct ETHInterface*) vcontext); if (context->beaconState != ETHInterface_beacon_ACCEPTING_AND_SENDING) { // beaconing disabled return; } struct { struct sockaddr_ll addr; struct Headers_Beacon beacon; } content; Bits_memcpyConst(&content.addr, &context->addrBase, sizeof(struct sockaddr_ll)); Bits_memset(content.addr.sll_addr, 0xff, 6); InterfaceController_populateBeacon(context->ic, &content.beacon); struct Message m = { .bytes=(uint8_t*)content.addr.sll_addr, .padding=0, .length=sizeof(struct Headers_Beacon) + 8 }; int ret; if ((ret = sendMessage(&m, &context->generic)) != 0) { Log_info(context->logger, "Got error [%d] sending beacon [%s]", ret, strerror(errno)); } } static void handleEvent(void* vcontext) { struct ETHInterface* context = Identity_cast((struct ETHInterface*) vcontext); struct Message message = { .bytes = context->messageBuff + PADDING, .padding = PADDING, .length = MAX_PACKET_SIZE }; struct sockaddr_ll addr; uint32_t addrLen = sizeof(struct sockaddr_ll); // Knock it out of alignment by 2 bytes so that it will be // aligned when the idAndPadding is shifted off. Message_shift(&message, 2); int rc = recvfrom(context->socket, message.bytes, message.length, 0, (struct sockaddr*) &addr, &addrLen); if (rc < 0) { Log_debug(context->logger, "Failed to receive eth frame"); return; } //Assert_true(addrLen == SOCKADDR_LL_LEN); // Pop the first 2 bytes of the message containing the node id and amount of padding. uint16_t idAndPadding_be; Message_pop(&message, &idAndPadding_be, 2); const uint16_t idAndPadding = Endian_bigEndianToHost16(idAndPadding_be); message.length = rc - 2 - ((idAndPadding & 7) * 8); const uint16_t id = idAndPadding >> 3; Message_push(&message, &id, 2); Message_push(&message, addr.sll_addr, 6); if (addr.sll_pkttype == PACKET_BROADCAST) { handleBeacon(&message, context); return; } /* Cut down on the noise uint8_t buff[sizeof(addr) * 2 + 1] = {0}; Hex_encode(buff, sizeof(buff), (uint8_t*)&addr, sizeof(addr)); Log_debug(context->logger, "Got ethernet frame from [%s]", buff); */ context->generic.receiveMessage(&message, &context->generic); } int ETHInterface_beginConnection(const char* macAddress, uint8_t cryptoKey[32], String* password, struct ETHInterface* ethIf) { Identity_check(ethIf); struct sockaddr_ll addr; Bits_memcpyConst(&addr, ðIf->addrBase, sizeof(struct sockaddr_ll)); if (AddrTools_parseMac(addr.sll_addr, (const uint8_t*)macAddress)) { return ETHInterface_beginConnection_BAD_MAC; } struct Interface* iface = MultiInterface_ifaceForKey(ethIf->multiIface, &addr); int ret = InterfaceController_registerPeer(ethIf->ic, cryptoKey, password, false, false, iface); if (ret) { Allocator_free(iface->allocator); switch(ret) { case InterfaceController_registerPeer_BAD_KEY: return ETHInterface_beginConnection_BAD_KEY; case InterfaceController_registerPeer_OUT_OF_SPACE: return ETHInterface_beginConnection_OUT_OF_SPACE; default: return ETHInterface_beginConnection_UNKNOWN_ERROR; } } return 0; } int ETHInterface_beacon(struct ETHInterface* ethIf, int* state) { Identity_check(ethIf); if (state) { ethIf->beaconState = *state; // Send out a beacon right away so we don't have to wait. if (ethIf->beaconState == ETHInterface_beacon_ACCEPTING_AND_SENDING) { sendBeacon(ethIf); } } return ethIf->beaconState; } struct ETHInterface* ETHInterface_new(struct EventBase* base, const char* bindDevice, struct Allocator* allocator, struct Except* exHandler, struct Log* logger, struct InterfaceController* ic) { struct ETHInterface* context = Allocator_clone(allocator, (&(struct ETHInterface) { .generic = { .sendMessage = sendMessage, .allocator = allocator }, .logger = logger, .ic = ic, .id = getpid() }));
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 Iface_DEFUN incoming(struct Message* msg, struct Iface* interRouterIf) { struct MsgCore_pvt* mcp = Identity_containerOf(interRouterIf, struct MsgCore_pvt, pub.interRouterIf); struct Address addr = { .padding = 0 }; struct RouteHeader* hdr = (struct RouteHeader*) msg->bytes; Message_shift(msg, -(RouteHeader_SIZE + DataHeader_SIZE), NULL); Bits_memcpy(addr.ip6.bytes, hdr->ip6, 16); Bits_memcpy(addr.key, hdr->publicKey, 32); addr.protocolVersion = Endian_bigEndianToHost32(hdr->version_be); addr.path = Endian_bigEndianToHost64(hdr->sh.label_be); Dict* content = NULL; uint8_t* msgBytes = msg->bytes; int length = msg->length; //Log_debug(mcp->log, "Receive msg [%s] from [%s]", // Escape_getEscaped(msg->bytes, msg->length, msg->alloc), // Address_toString(&addr, msg->alloc)->bytes); // BencMessageReader_readNoExcept(msg, msg->alloc, &content); if (!content) { char* esc = Escape_getEscaped(msgBytes, length, msg->alloc); Log_debug(mcp->log, "DROP Malformed message [%s]", esc); return NULL; } int64_t* verP = Dict_getIntC(content, "p"); if (!verP) { Log_debug(mcp->log, "DROP Message without version"); return NULL; } addr.protocolVersion = *verP; String* q = Dict_getStringC(content, "q"); if (!Defined(SUBNODE)) { String* txid = Dict_getStringC(content, "txid"); Assert_true(txid); if (q) { if (txid->bytes[0] == '0') { Log_debug(mcp->log, "DROP query which begins with 0 and is for old pathfinder"); return NULL; } } else { if (txid->bytes[0] != '1') { Log_debug(mcp->log, "DROP reply which does not begin with 1"); return NULL; } String* newTxid = String_newBinary(NULL, txid->len - 1, msg->alloc); Bits_memcpy(newTxid->bytes, &txid->bytes[1], txid->len - 1); Dict_putStringC(content, "txid", newTxid, msg->alloc); txid = newTxid; } } if (q) { return queryMsg(mcp, content, &addr, msg); } else { return replyMsg(mcp, content, &addr, msg); } } struct MsgCore* MsgCore_new(struct EventBase* base, struct Random* rand, struct Allocator* allocator, struct Log* log, struct EncodingScheme* scheme) { struct Allocator* alloc = Allocator_child(allocator); struct MsgCore_pvt* mcp = Allocator_calloc(alloc, sizeof(struct MsgCore_pvt), 1); Identity_set(mcp); mcp->pub.interRouterIf.send = incoming; mcp->qh = ArrayList_OfQueryHandlers_new(alloc); mcp->pinger = Pinger_new(base, rand, log, alloc); mcp->log = log; mcp->scheme = scheme; mcp->schemeDefinition = EncodingScheme_serialize(scheme, alloc); return &mcp->pub; }
static Iface_DEFUN incomingMsg(struct Message* msg, struct Pathfinder_pvt* pf) { struct Address addr; struct RouteHeader* hdr = (struct RouteHeader*) msg->bytes; Message_shift(msg, -(RouteHeader_SIZE + DataHeader_SIZE), NULL); Bits_memcpy(addr.ip6.bytes, hdr->ip6, 16); Bits_memcpy(addr.key, hdr->publicKey, 32); addr.protocolVersion = Endian_bigEndianToHost32(hdr->version_be); addr.padding = 0; addr.path = Endian_bigEndianToHost64(hdr->sh.label_be); //Log_debug(pf->log, "Incoming DHT"); struct DHTMessage dht = { .address = &addr, .binMessage = msg, .allocator = msg->alloc }; DHTModuleRegistry_handleIncoming(&dht, pf->registry); struct Message* nodeMsg = Message_new(0, 256, msg->alloc); Iface_CALL(sendNode, nodeMsg, &addr, 0xfffffff0u, pf); if (dht.pleaseRespond) { // what a beautiful hack, see incomingFromDHT return Iface_next(&pf->pub.eventIf, msg); } return NULL; } static Iface_DEFUN incomingFromEventIf(struct Message* msg, struct Iface* eventIf) { struct Pathfinder_pvt* pf = Identity_containerOf(eventIf, struct Pathfinder_pvt, pub.eventIf); enum PFChan_Core ev = Message_pop32(msg, NULL); if (Pathfinder_pvt_state_INITIALIZING == pf->state) { Assert_true(ev == PFChan_Core_CONNECT); return connected(pf, msg); } // Let the PF send another 128 path changes again because it's basically a new tick. pf->bestPathChanges = 0; switch (ev) { case PFChan_Core_SWITCH_ERR: return switchErr(msg, pf); case PFChan_Core_SEARCH_REQ: return searchReq(msg, pf); case PFChan_Core_PEER: return peer(msg, pf); case PFChan_Core_PEER_GONE: return peerGone(msg, pf); case PFChan_Core_SESSION: return session(msg, pf); case PFChan_Core_SESSION_ENDED: return sessionEnded(msg, pf); case PFChan_Core_DISCOVERED_PATH: return discoveredPath(msg, pf); case PFChan_Core_MSG: return incomingMsg(msg, pf); case PFChan_Core_PING: return handlePing(msg, pf); case PFChan_Core_PONG: return handlePong(msg, pf); case PFChan_Core_UNSETUP_SESSION: case PFChan_Core_LINK_STATE: case PFChan_Core_CTRL_MSG: return NULL; default:; } Assert_failure("unexpected event [%d]", ev); } static void sendEvent(struct Pathfinder_pvt* pf, enum PFChan_Pathfinder ev, void* data, int size) { struct Allocator* alloc = Allocator_child(pf->alloc); struct Message* msg = Message_new(0, 512+size, alloc); Message_push(msg, data, size, NULL); Message_push32(msg, ev, NULL); Iface_send(&pf->pub.eventIf, msg); Allocator_free(alloc); } static void init(void* vpf) { struct Pathfinder_pvt* pf = Identity_check((struct Pathfinder_pvt*) vpf); struct PFChan_Pathfinder_Connect conn = { .superiority_be = Endian_hostToBigEndian32(1), .version_be = Endian_hostToBigEndian32(Version_CURRENT_PROTOCOL) }; CString_strncpy(conn.userAgent, "Cjdns internal pathfinder", 64); sendEvent(pf, PFChan_Pathfinder_CONNECT, &conn, PFChan_Pathfinder_Connect_SIZE); } struct Pathfinder* Pathfinder_register(struct Allocator* allocator, struct Log* log, struct EventBase* base, struct Random* rand, struct Admin* admin) { struct Allocator* alloc = Allocator_child(allocator); struct Pathfinder_pvt* pf = Allocator_calloc(alloc, sizeof(struct Pathfinder_pvt), 1); Identity_set(pf); pf->alloc = alloc; pf->log = log; pf->base = base; pf->rand = rand; pf->admin = admin; pf->pub.eventIf.send = incomingFromEventIf; pf->dhtModule.context = pf; pf->dhtModule.handleOutgoing = incomingFromDHT; // This needs to be done asynchronously so the pf can be plumbed to the core Timeout_setTimeout(init, pf, 0, base, alloc); return &pf->pub; }
// incoming message from network, pointing to the beginning of the switch header. static uint8_t receiveMessage(struct Message* msg, struct Interface* iface) { struct SwitchPinger* ctx = Identity_check((struct SwitchPinger*) iface->receiverContext); struct SwitchHeader* switchHeader = (struct SwitchHeader*) msg->bytes; ctx->incomingLabel = Endian_bigEndianToHost64(switchHeader->label_be); ctx->incomingVersion = 0; Message_shift(msg, -SwitchHeader_SIZE, NULL); uint32_t handle = Message_pop32(msg, NULL); #ifdef Version_7_COMPAT if (handle != 0xffffffff) { Message_push32(msg, handle, NULL); handle = 0xffffffff; Assert_true(SwitchHeader_isV7Ctrl(switchHeader)); } #endif Assert_true(handle == 0xffffffff); struct Control* ctrl = (struct Control*) msg->bytes; if (ctrl->type_be == Control_PONG_be) { Message_shift(msg, -Control_HEADER_SIZE, NULL); ctx->error = Error_NONE; if (msg->length >= Control_Pong_MIN_SIZE) { struct Control_Ping* pongHeader = (struct Control_Ping*) msg->bytes; ctx->incomingVersion = Endian_bigEndianToHost32(pongHeader->version_be); if (pongHeader->magic != Control_Pong_MAGIC) { Log_debug(ctx->logger, "dropped invalid switch pong"); return Error_INVALID; } Message_shift(msg, -Control_Pong_HEADER_SIZE, NULL); } else { Log_debug(ctx->logger, "got runt pong message, length: [%d]", msg->length); return Error_INVALID; } } else if (ctrl->type_be == Control_KEYPONG_be) { Message_shift(msg, -Control_HEADER_SIZE, NULL); ctx->error = Error_NONE; if (msg->length >= Control_KeyPong_HEADER_SIZE && msg->length <= Control_KeyPong_MAX_SIZE) { struct Control_KeyPing* pongHeader = (struct Control_KeyPing*) msg->bytes; ctx->incomingVersion = Endian_bigEndianToHost32(pongHeader->version_be); if (pongHeader->magic != Control_KeyPong_MAGIC) { Log_debug(ctx->logger, "dropped invalid switch key-pong"); return Error_INVALID; } Bits_memcpyConst(ctx->incomingKey, pongHeader->key, 32); Message_shift(msg, -Control_KeyPong_HEADER_SIZE, NULL); } else if (msg->length > Control_KeyPong_MAX_SIZE) { Log_debug(ctx->logger, "got overlong key-pong message, length: [%d]", msg->length); return Error_INVALID; } else { Log_debug(ctx->logger, "got runt key-pong message, length: [%d]", msg->length); return Error_INVALID; } } else if (ctrl->type_be == Control_ERROR_be) { Message_shift(msg, -Control_HEADER_SIZE, NULL); Assert_true((uint8_t*)&ctrl->content.error.errorType_be == msg->bytes); if (msg->length < (Control_Error_HEADER_SIZE + SwitchHeader_SIZE + Control_HEADER_SIZE)) { Log_debug(ctx->logger, "runt error packet"); return Error_NONE; } ctx->error = Message_pop32(msg, NULL); Message_push32(msg, 0, NULL); Message_shift(msg, -(Control_Error_HEADER_SIZE + SwitchHeader_SIZE), NULL); struct Control* origCtrl = (struct Control*) msg->bytes; Log_debug(ctx->logger, "error [%s] was caused by our [%s]", Error_strerror(ctx->error), Control_typeString(origCtrl->type_be)); int shift; if (origCtrl->type_be == Control_PING_be) { shift = -(Control_HEADER_SIZE + Control_Ping_HEADER_SIZE); } else if (origCtrl->type_be == Control_KEYPING_be) { shift = -(Control_HEADER_SIZE + Control_KeyPing_HEADER_SIZE); } else { Assert_failure("problem in Ducttape.c"); } if (msg->length < -shift) { Log_debug(ctx->logger, "runt error packet"); } Message_shift(msg, shift, NULL); } else { // If it gets here then Ducttape.c is failing. Assert_true(false); } String* msgStr = &(String) { .bytes = (char*) msg->bytes, .len = msg->length }; Pinger_pongReceived(msgStr, ctx->pinger); Bits_memset(ctx->incomingKey, 0, 32); return Error_NONE; } static void onPingResponse(String* data, uint32_t milliseconds, void* vping) { struct Ping* p = Identity_check((struct Ping*) vping); enum SwitchPinger_Result err = SwitchPinger_Result_OK; uint64_t label = p->context->incomingLabel; if (data) { if (label != p->label) { err = SwitchPinger_Result_LABEL_MISMATCH; } else if ((p->data || data->len > 0) && !String_equals(data, p->data)) { err = SwitchPinger_Result_WRONG_DATA; } else if (p->context->error == Error_LOOP_ROUTE) { err = SwitchPinger_Result_LOOP_ROUTE; } else if (p->context->error) { err = SwitchPinger_Result_ERROR_RESPONSE; } } else { err = SwitchPinger_Result_TIMEOUT; } uint32_t version = p->context->incomingVersion; struct SwitchPinger_Response* resp = Allocator_calloc(p->pub.pingAlloc, sizeof(struct SwitchPinger_Response), 1); resp->version = p->context->incomingVersion; resp->res = err; resp->label = label; resp->data = data; resp->milliseconds = milliseconds; resp->version = version; Bits_memcpyConst(resp->key, p->context->incomingKey, 32); resp->ping = &p->pub; p->onResponse(resp, p->pub.onResponseContext); } static void sendPing(String* data, void* sendPingContext) { struct Ping* p = Identity_check((struct Ping*) sendPingContext); struct Message* msg = Message_new(0, data->len + 512, p->pub.pingAlloc); while (((uintptr_t)msg->bytes - data->len) % 4) { Message_push8(msg, 0, NULL); } msg->length = 0; Message_push(msg, data->bytes, data->len, NULL); Assert_true(!((uintptr_t)msg->bytes % 4) && "alignment fault"); if (p->pub.keyPing) { Message_shift(msg, Control_KeyPing_HEADER_SIZE, NULL); struct Control_KeyPing* keyPingHeader = (struct Control_KeyPing*) msg->bytes; keyPingHeader->magic = Control_KeyPing_MAGIC; keyPingHeader->version_be = Endian_hostToBigEndian32(Version_CURRENT_PROTOCOL); Bits_memcpyConst(keyPingHeader->key, p->context->myAddr->key, 32); } else { Message_shift(msg, Control_Ping_HEADER_SIZE, NULL); struct Control_Ping* pingHeader = (struct Control_Ping*) msg->bytes; pingHeader->magic = Control_Ping_MAGIC; pingHeader->version_be = Endian_hostToBigEndian32(Version_CURRENT_PROTOCOL); } Message_shift(msg, Control_HEADER_SIZE, NULL); struct Control* ctrl = (struct Control*) msg->bytes; ctrl->checksum_be = 0; ctrl->type_be = (p->pub.keyPing) ? Control_KEYPING_be : Control_PING_be; ctrl->checksum_be = Checksum_engine(msg->bytes, msg->length); #ifdef Version_7_COMPAT if (0) { #endif Message_push32(msg, 0xffffffff, NULL); #ifdef Version_7_COMPAT } #endif Message_shift(msg, SwitchHeader_SIZE, NULL); struct SwitchHeader* switchHeader = (struct SwitchHeader*) msg->bytes; switchHeader->label_be = Endian_hostToBigEndian64(p->label); SwitchHeader_setVersion(switchHeader, SwitchHeader_CURRENT_VERSION); SwitchHeader_setPenalty(switchHeader, 0); SwitchHeader_setCongestion(switchHeader, 0); #ifdef Version_7_COMPAT // v7 detects ctrl packets by the bit which has been // re-appropriated for suppression of errors. switchHeader->congestAndSuppressErrors = 1; SwitchHeader_setVersion(switchHeader, 0); #endif p->context->iface->sendMessage(msg, p->context->iface); } static String* RESULT_STRING_OK = String_CONST_SO("pong"); static String* RESULT_STRING_LABEL_MISMATCH = String_CONST_SO("diff_label"); static String* RESULT_STRING_WRONG_DATA = String_CONST_SO("diff_data"); static String* RESULT_STRING_ERROR_RESPONSE = String_CONST_SO("err_switch"); static String* RESULT_STRING_TIMEOUT = String_CONST_SO("timeout"); static String* RESULT_STRING_UNKNOWN = String_CONST_SO("err_unknown"); static String* RESULT_STRING_LOOP = String_CONST_SO("err_loop"); String* SwitchPinger_resultString(enum SwitchPinger_Result result) { switch (result) { case SwitchPinger_Result_OK: return RESULT_STRING_OK; case SwitchPinger_Result_LABEL_MISMATCH: return RESULT_STRING_LABEL_MISMATCH; case SwitchPinger_Result_WRONG_DATA: return RESULT_STRING_WRONG_DATA; case SwitchPinger_Result_ERROR_RESPONSE: return RESULT_STRING_ERROR_RESPONSE; case SwitchPinger_Result_TIMEOUT: return RESULT_STRING_TIMEOUT; case SwitchPinger_Result_LOOP_ROUTE: return RESULT_STRING_LOOP; default: return RESULT_STRING_UNKNOWN; }; } static int onPingFree(struct Allocator_OnFreeJob* job) { struct Ping* ping = Identity_check((struct Ping*)job->userData); struct SwitchPinger* ctx = Identity_check(ping->context); ctx->outstandingPings--; Assert_true(ctx->outstandingPings >= 0); return 0; } struct SwitchPinger_Ping* SwitchPinger_newPing(uint64_t label, String* data, uint32_t timeoutMilliseconds, SwitchPinger_ResponseCallback onResponse, struct Allocator* alloc, struct SwitchPinger* ctx) { if (data && data->len > Control_Ping_MAX_SIZE) { return NULL; } if (ctx->outstandingPings > ctx->maxConcurrentPings) { Log_debug(ctx->logger, "Skipping switch ping because there are already [%d] outstanding", ctx->outstandingPings); return NULL; } struct Pinger_Ping* pp = Pinger_newPing(data, onPingResponse, sendPing, timeoutMilliseconds, alloc, ctx->pinger); struct Ping* ping = Allocator_clone(pp->pingAlloc, (&(struct Ping) { .pub = { .pingAlloc = pp->pingAlloc }, .label = label, .data = String_clone(data, pp->pingAlloc), .context = ctx, .onResponse = onResponse, .pingerPing = pp }));
/** @return 0 on success, -1 otherwise. */ int CryptoAuth_decrypt(struct CryptoAuth_Session* sessionPub, struct Message* msg) { struct CryptoAuth_Session_pvt* session = Identity_check((struct CryptoAuth_Session_pvt*) sessionPub); union CryptoHeader* header = (union CryptoHeader*) msg->bytes; if (msg->length < 20) { cryptoAuthDebug0(session, "DROP runt"); return -1; } Assert_true(msg->padding >= 12 || "need at least 12 bytes of padding in incoming message"); Assert_true(!((uintptr_t)msg->bytes % 4) || !"alignment fault"); Assert_true(!(msg->capacity % 4) || !"length fault"); Message_shift(msg, -4, NULL); uint32_t nonce = Endian_bigEndianToHost32(header->nonce); if (!session->established) { if (nonce > 3) { if (session->nextNonce < 3) { // This is impossible because we have not exchanged hello and key messages. cryptoAuthDebug0(session, "DROP Received a run message to an un-setup session"); return -1; } cryptoAuthDebug(session, "Trying final handshake step, nonce=%u\n", nonce); uint8_t secret[32]; Assert_ifParanoid(!Bits_isZero(session->ourTempPrivKey, 32)); Assert_ifParanoid(!Bits_isZero(session->herTempPubKey, 32)); getSharedSecret(secret, session->ourTempPrivKey, session->herTempPubKey, NULL, session->context->logger); if (decryptMessage(session, nonce, msg, secret)) { cryptoAuthDebug0(session, "Final handshake step succeeded"); Bits_memcpyConst(session->sharedSecret, secret, 32); // Now we're in run mode, no more handshake packets will be accepted Bits_memset(session->ourTempPrivKey, 0, 32); Bits_memset(session->ourTempPubKey, 0, 32); Bits_memset(session->herTempPubKey, 0, 32); session->established = true; session->nextNonce += 3; updateTime(session, msg); return 0; } cryptoAuthDebug0(session, "DROP Final handshake step failed"); return -1; } Message_shift(msg, 4, NULL); return decryptHandshake(session, nonce, msg, header); } else if (nonce > 3) { Assert_ifParanoid(!Bits_isZero(session->sharedSecret, 32)); if (decryptMessage(session, nonce, msg, session->sharedSecret)) { updateTime(session, msg); return 0; } else { cryptoAuthDebug0(session, "DROP Failed to decrypt message"); return -1; } } else if (nonce < 2) { cryptoAuthDebug(session, "hello packet during established session nonce=[%d]", nonce); Message_shift(msg, 4, NULL); return decryptHandshake(session, nonce, msg, header); } else { // setup keys are already zeroed, not much we can do here. cryptoAuthDebug(session, "DROP key packet during established session nonce=[%d]", nonce); return -1; } Assert_true(0); }