diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index cc5fd34f..b91831eb 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -173,14 +173,13 @@ class MyMesh : public BaseChatMesh { ContactInfo c; uint8_t pub_key[32]; uint8_t unused; - uint32_t reserved; bool success = (file.read(pub_key, 32) == 32); success = success && (file.read((uint8_t *) &c.name, 32) == 32); success = success && (file.read(&c.type, 1) == 1); success = success && (file.read(&c.flags, 1) == 1); success = success && (file.read(&unused, 1) == 1); - success = success && (file.read((uint8_t *) &reserved, 4) == 4); + success = success && (file.read((uint8_t *) &c.sync_since, 4) == 4); // was 'reserved' success = success && (file.read((uint8_t *) &c.out_path_len, 1) == 1); success = success && (file.read((uint8_t *) &c.last_advert_timestamp, 4) == 4); success = success && (file.read(c.out_path, 64) == 64); @@ -209,7 +208,6 @@ class MyMesh : public BaseChatMesh { ContactsIterator iter; ContactInfo c; uint8_t unused = 0; - uint32_t reserved = 0; while (iter.hasNext(this, c)) { bool success = (file.write(c.id.pub_key, 32) == 32); @@ -217,7 +215,7 @@ class MyMesh : public BaseChatMesh { success = success && (file.write(&c.type, 1) == 1); success = success && (file.write(&c.flags, 1) == 1); success = success && (file.write(&unused, 1) == 1); - success = success && (file.write((uint8_t *) &reserved, 4) == 4); + success = success && (file.write((uint8_t *) &c.sync_since, 4) == 4); success = success && (file.write((uint8_t *) &c.out_path_len, 1) == 1); success = success && (file.write((uint8_t *) &c.last_advert_timestamp, 4) == 4); success = success && (file.write(c.out_path, 64) == 64); @@ -384,6 +382,11 @@ protected: } } + void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) override { + // TODO: check for login response + // TODO: check for Get Stats response + } + uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override { return SEND_TIMEOUT_BASE_MILLIS + (FLOOD_SEND_TIMEOUT_FACTOR * pkt_airtime_millis); } @@ -632,6 +635,7 @@ public: ContactInfo contact; updateContactFromFrame(contact, cmd_frame, len); contact.lastmod = getRTCClock()->getCurrentTime(); + contact.sync_since = 0; if (addContact(contact)) { saveContacts(); writeOKFrame(); diff --git a/examples/simple_secure_chat/main.cpp b/examples/simple_secure_chat/main.cpp index 95aa12ac..006fda0a 100644 --- a/examples/simple_secure_chat/main.cpp +++ b/examples/simple_secure_chat/main.cpp @@ -269,6 +269,10 @@ protected: Serial.printf(" %s\n", text); } + void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) override { + // not supported + } + uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override { return SEND_TIMEOUT_BASE_MILLIS + (FLOOD_SEND_TIMEOUT_FACTOR * pkt_airtime_millis); } diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 760f6f0a..cf61768c 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -40,6 +40,7 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, from->out_path_len = -1; // initially out_path is unknown from->gps_lat = 0; // initially unknown GPS loc from->gps_lon = 0; + from->sync_since = 0; // only need to calculate the shared_secret once, for better performance self_id.calcSharedSecret(from->shared_secret, id); @@ -84,15 +85,15 @@ void BaseChatMesh::getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) { } void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) { + int i = matching_peer_indexes[sender_idx]; + if (i < 0 || i >= num_contacts) { + MESH_DEBUG_PRINTLN("onPeerDataRecv: Invalid sender idx: %d", i); + return; + } + + ContactInfo& from = contacts[i]; + if (type == PAYLOAD_TYPE_TXT_MSG && len > 5) { - int i = matching_peer_indexes[sender_idx]; - if (i < 0 || i >= num_contacts) { - MESH_DEBUG_PRINTLN("onPeerDataRecv: Invalid sender idx: %d", i); - return; - } - - ContactInfo& from = contacts[i]; - uint32_t timestamp; memcpy(×tamp, data, 4); // timestamp (by sender's RTC clock - which could be wrong) uint flags = data[4]; // message attempt number, and other flags @@ -125,6 +126,8 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender } else { MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported message type: %u", (uint32_t) (flags >> 2)); } + } else if (type == PAYLOAD_TYPE_RESPONSE && len > 0) { + onContactResponse(from, data, len); } } @@ -149,6 +152,8 @@ bool BaseChatMesh::onPeerPathRecv(mesh::Packet* packet, int sender_idx, const ui if (processAck(extra)) { txt_send_timeout = 0; // matched one we're waiting for, cancel timeout timer } + } else if (extra_type == PAYLOAD_TYPE_RESPONSE && extra_len > 0) { + onContactResponse(from, extra, extra_len); } return true; // send reciprocal path if necessary } @@ -241,6 +246,41 @@ bool BaseChatMesh::sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& chan return false; } +bool BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout) { + uint8_t shared_secret[32]; + self_id.calcSharedSecret(shared_secret, recipient.id); // TODO: cache this + + int tlen; + uint8_t temp[24]; + uint32_t now = getRTCClock()->getCurrentTime(); + memcpy(temp, &now, 4); // mostly an extra blob to help make packet_hash unique + if (recipient.type == ADV_TYPE_ROOM) { + memcpy(&temp[4], &recipient.sync_since, 4); + int len = strlen(password); if (len > 15) len = 15; // max 15 chars currently + memcpy(&temp[8], password, len); + tlen = 8 + len; + } else { + int len = strlen(password); if (len > 15) len = 15; // max 15 chars currently + memcpy(&temp[4], password, len); + tlen = 4 + len; + } + + auto pkt = createAnonDatagram(PAYLOAD_TYPE_ANON_REQ, self_id, recipient.id, shared_secret, temp, tlen); + if (pkt) { + uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2); + if (recipient.out_path_len < 0) { + sendFlood(pkt); + txt_send_timeout = futureMillis(est_timeout = calcFloodTimeoutMillisFor(t)); + } else { + sendDirect(pkt, recipient.out_path, recipient.out_path_len); + txt_send_timeout = futureMillis(est_timeout = calcDirectTimeoutMillisFor(t, recipient.out_path_len)); + } + } else { + return false; // failed + } + return true; // success +} + void BaseChatMesh::resetPathTo(ContactInfo& recipient) { recipient.out_path_len = -1; } diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index b0241f82..c01e18ab 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -18,6 +18,7 @@ struct ContactInfo { uint8_t shared_secret[PUB_KEY_SIZE]; uint32_t lastmod; // by OUR clock int32_t gps_lat, gps_lon; // 6 dec places + uint32_t sync_since; }; #define MAX_SEARCH_RESULTS 8 @@ -82,6 +83,7 @@ protected: virtual uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const = 0; virtual void onSendTimeout() = 0; virtual void onChannelMessageRecv(const mesh::GroupChannel& channel, int in_path_len, uint32_t timestamp, const char *text) = 0; + virtual void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) = 0; // Mesh overrides void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) override; @@ -99,6 +101,7 @@ public: mesh::Packet* createSelfAdvert(const char* name, double lat=0.0, double lon=0.0); int sendMessage(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& expected_ack, uint32_t& est_timeout); bool sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len); + bool sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout); void resetPathTo(ContactInfo& recipient); void scanRecentContacts(int last_n, ContactVisitor* visitor); ContactInfo* searchContactsByPrefix(const char* name_prefix);