static int closeInterface(struct Allocator_OnFreeJob* job) { struct Peer* toClose = Identity_check((struct Peer*) job->userData); sendPeer(0xffffffff, PFChan_Core_PEER_GONE, toClose); int index = Map_EndpointsBySockaddr_indexForHandle(toClose->handle, &toClose->ici->peerMap); Assert_true(index >= 0 && toClose->ici->peerMap.values[index] == toClose); Map_EndpointsBySockaddr_remove(index, &toClose->ici->peerMap); return 0; }
// 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_memcpyConst(ep->addr.key, ep->caSession->herPublicKey, 32); Address_getPrefix(&ep->addr); if (caState == CryptoAuth_ESTABLISHED) { moveEndpointIfNeeded(ep); sendPeer(0xffffffff, PFChan_Core_PEER, ep); } 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); }
static Iface_DEFUN incomingFromEventEmitterIf(struct Message* msg, struct Iface* eventEmitterIf) { struct InterfaceController_pvt* ic = Identity_containerOf(eventEmitterIf, struct InterfaceController_pvt, eventEmitterIf); Assert_true(Message_pop32(msg, NULL) == PFChan_Pathfinder_PEERS); uint32_t pathfinderId = Message_pop32(msg, NULL); Assert_true(!msg->length); for (int j = 0; j < ic->icis->length; j++) { struct InterfaceController_Iface_pvt* ici = ArrayList_OfIfaces_get(ic->icis, j); for (int i = 0; i < (int)ici->peerMap.count; i++) { struct Peer* peer = Identity_check((struct Peer*) ici->peerMap.values[i]); if (peer->state != InterfaceController_PeerState_ESTABLISHED) { continue; } sendPeer(pathfinderId, PFChan_Core_PEER, peer); } } return NULL; }
static void onPingResponse(struct SwitchPinger_Response* resp, void* onResponseContext) { if (SwitchPinger_Result_OK != resp->res) { return; } struct Peer* ep = Identity_check((struct Peer*) onResponseContext); struct InterfaceController_pvt* ic = Identity_check(ep->ici->ic); ep->addr.protocolVersion = resp->version; if (Defined(Log_DEBUG)) { String* addr = Address_toString(&ep->addr, resp->ping->pingAlloc); if (!Version_isCompatible(Version_CURRENT_PROTOCOL, resp->version)) { Log_debug(ic->logger, "got switch pong from node [%s] with incompatible version", addr->bytes); } else if (ep->addr.path != resp->label) { uint8_t sl[20]; AddrTools_printPath(sl, resp->label); Log_debug(ic->logger, "got switch pong from node [%s] mismatch label [%s]", addr->bytes, sl); } else { Log_debug(ic->logger, "got switch pong from node [%s]", addr->bytes); } } if (!Version_isCompatible(Version_CURRENT_PROTOCOL, resp->version)) { return; } if (ep->state == InterfaceController_PeerState_ESTABLISHED) { sendPeer(0xffffffff, PFChan_Core_PEER, ep); } ep->timeOfLastPing = Time_currentTimeMilliseconds(ic->eventBase); if (Defined(Log_DEBUG)) { String* addr = Address_toString(&ep->addr, resp->ping->pingAlloc); Log_debug(ic->logger, "Received [%s] from lazy endpoint [%s]", SwitchPinger_resultString(resp->res)->bytes, addr->bytes); } }
/** * 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; }
static void iciPing(struct InterfaceController_Iface_pvt* ici, struct InterfaceController_pvt* ic) { if (!ici->peerMap.count) { return; } uint64_t now = Time_currentTimeMilliseconds(ic->eventBase); // scan for endpoints have not sent anything recently. uint32_t startAt = Random_uint32(ic->rand) % ici->peerMap.count; for (uint32_t i = startAt, count = 0; count < ici->peerMap.count;) { i = (i + 1) % ici->peerMap.count; count++; struct Peer* ep = ici->peerMap.values[i]; if (now < ep->timeOfLastMessage + ic->pingAfterMilliseconds) { if (now < ep->timeOfLastPing + ic->pingAfterMilliseconds) { // Possibly an out-of-date node which is mangling packets, don't ping too often // because it causes the RumorMill to be filled with this node over and over. continue; } } uint8_t keyIfDebug[56]; if (Defined(Log_DEBUG)) { Base32_encode(keyIfDebug, 56, ep->caSession->herPublicKey, 32); } if (ep->isIncomingConnection && now > ep->timeOfLastMessage + ic->forgetAfterMilliseconds) { Log_debug(ic->logger, "Unresponsive peer [%s.k] has not responded in [%u] " "seconds, dropping connection", keyIfDebug, ic->forgetAfterMilliseconds / 1024); sendPeer(0xffffffff, PFChan_Core_PEER_GONE, ep); Allocator_free(ep->alloc); continue; } bool unresponsive = (now > ep->timeOfLastMessage + ic->unresponsiveAfterMilliseconds); if (unresponsive) { // our link to the peer is broken... // Lets skip 87% of pings when they're really down. if (ep->pingCount % 8) { ep->pingCount++; continue; } sendPeer(0xffffffff, PFChan_Core_PEER_GONE, ep); ep->state = InterfaceController_PeerState_UNRESPONSIVE; SwitchCore_setInterfaceState(&ep->switchIf, SwitchCore_setInterfaceState_ifaceState_DOWN); } Log_debug(ic->logger, "Pinging %s peer [%s.k] lag [%u]", (unresponsive ? "unresponsive" : "lazy"), keyIfDebug, (uint32_t)((now - ep->timeOfLastMessage) / 1024)); sendPing(ep); // we only ping one node return; } }