From d880a48b8523e69f56dc5a025f352406de31d2ce Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 24 Feb 2025 14:05:17 +1100 Subject: [PATCH] * companion radio: new CMD_SEND_LOGIN, CMD_SEND_STATUS_REQ --- examples/companion_radio/main.cpp | 82 ++++++++++++++++++++++++++++++- src/helpers/BaseChatMesh.cpp | 37 +++++++++++--- src/helpers/BaseChatMesh.h | 7 ++- 3 files changed, 115 insertions(+), 11 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index d1813dcf..c34cac26 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -124,6 +124,8 @@ static uint32_t _atoi(const char* sp) { #define CMD_EXPORT_PRIVATE_KEY 23 #define CMD_IMPORT_PRIVATE_KEY 24 #define CMD_SEND_RAW_DATA 25 +#define CMD_SEND_LOGIN 26 +#define CMD_SEND_STATUS_REQ 27 #define RESP_CODE_OK 0 #define RESP_CODE_ERR 1 @@ -148,6 +150,9 @@ static uint32_t _atoi(const char* sp) { #define PUSH_CODE_SEND_CONFIRMED 0x82 #define PUSH_CODE_MSG_WAITING 0x83 #define PUSH_CODE_RAW_DATA 0x84 +#define PUSH_CODE_LOGIN_SUCCESS 0x85 +#define PUSH_CODE_LOGIN_FAIL 0x86 +#define PUSH_CODE_STATUS_RESPONSE 0x87 /* -------------------------------------------------------------------------------------- */ @@ -172,6 +177,8 @@ class MyMesh : public BaseChatMesh { IdentityStore* _identity_store; NodePrefs _prefs; uint32_t expected_ack_crc; // TODO: keep table of expected ACKs + uint32_t pending_login; + uint32_t pending_status; mesh::GroupChannel* _public; BaseSerialInterface* _serial; unsigned long last_msg_sent; @@ -478,8 +485,36 @@ 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 sender_timestamp; + memcpy(&sender_timestamp, data, 4); + + if (memcmp(&pending_login, contact.id.pub_key, 4) == 0) { // check for login response + // yes, is response to pending sendLogin() + pending_login = 0; + + int i = 0; + if (memcmp(&data[4], "OK", 2) == 0) { // legacy Repeater login OK response + out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; + } else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response + // TODO: check the keep_alive_interval in data[] + out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; + } else { + out_frame[i++] = PUSH_CODE_LOGIN_FAIL; + } + out_frame[i++] = 0; // reserved + memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix + _serial->writeFrame(out_frame, i); + } else if (len > 4 && memcmp(&pending_status, contact.id.pub_key, 4) == 0) { // check for status response + // yes, is response to pending sendStatusRequest() + pending_status = 0; + + int i = 0; + out_frame[i++] = PUSH_CODE_STATUS_RESPONSE; + out_frame[i++] = 0; // reserved + memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix + memcpy(&out_frame[i], &data[4], len - 4); i += (len - 4); + _serial->writeFrame(out_frame, i); + } } void onRawDataRecv(mesh::Packet* packet) override { @@ -517,6 +552,7 @@ public: offline_queue_len = 0; app_target_ver = 0; _identity_store = NULL; + pending_login = pending_status = 0; // defaults memset(&_prefs, 0, sizeof(_prefs)); @@ -919,6 +955,48 @@ public: } else { writeErrFrame(); // flood, not supported (yet) } + } else if (cmd_frame[0] == CMD_SEND_LOGIN && len >= 1+PUB_KEY_SIZE) { + uint8_t* pub_key = &cmd_frame[1]; + ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); + char *password = (char *) &cmd_frame[1+PUB_KEY_SIZE]; + cmd_frame[len] = 0; // ensure null terminator in password + if (recipient) { + uint32_t est_timeout; + int result = sendLogin(*recipient, password, est_timeout); + if (result == MSG_SEND_FAILED) { + writeErrFrame(); + } else { + pending_status = 0; + memcpy(&pending_login, recipient->id.pub_key, 4); // match this to onContactResponse() + out_frame[0] = RESP_CODE_SENT; + out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0; + memcpy(&out_frame[2], &pending_login, 4); + memcpy(&out_frame[6], &est_timeout, 4); + _serial->writeFrame(out_frame, 10); + } + } else { + writeErrFrame(); // contact not found + } + } else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1+PUB_KEY_SIZE) { + uint8_t* pub_key = &cmd_frame[1]; + ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); + if (recipient) { + uint32_t est_timeout; + int result = sendStatusRequest(*recipient, est_timeout); + if (result == MSG_SEND_FAILED) { + writeErrFrame(); + } else { + pending_login = 0; + memcpy(&pending_status, recipient->id.pub_key, 4); // match this to onContactResponse() + out_frame[0] = RESP_CODE_SENT; + out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0; + memcpy(&out_frame[2], &pending_status, 4); + memcpy(&out_frame[6], &est_timeout, 4); + _serial->writeFrame(out_frame, 10); + } + } else { + writeErrFrame(); // contact not found + } } else { writeErrFrame(); MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 4d5cb168..bc0457ef 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -279,10 +279,7 @@ bool BaseChatMesh::importContact(const uint8_t src_buf[], uint8_t len) { return false; // error } -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 BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout) { int tlen; uint8_t temp[24]; uint32_t now = getRTCClock()->getCurrentTimeUnique(); @@ -298,20 +295,44 @@ bool BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password, tlen = 4 + len; } - auto pkt = createAnonDatagram(PAYLOAD_TYPE_ANON_REQ, self_id, recipient.id, shared_secret, temp, tlen); + auto pkt = createAnonDatagram(PAYLOAD_TYPE_ANON_REQ, self_id, recipient.id, recipient.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); est_timeout = calcFloodTimeoutMillisFor(t); + return MSG_SEND_SENT_FLOOD; } else { sendDirect(pkt, recipient.out_path, recipient.out_path_len); est_timeout = calcDirectTimeoutMillisFor(t, recipient.out_path_len); + return MSG_SEND_SENT_DIRECT; } - } else { - return false; // failed } - return true; // success + return MSG_SEND_FAILED; +} + +int BaseChatMesh::sendStatusRequest(const ContactInfo& recipient, uint32_t& est_timeout) { + uint8_t temp[13]; + uint32_t now = getRTCClock()->getCurrentTimeUnique(); + memcpy(temp, &now, 4); // mostly an extra blob to help make packet_hash unique + temp[4] = CMD_GET_STATUS; + memset(&temp[5], 0, 4); // reserved (possibly for 'since' param) + getRNG()->random(&temp[9], 4); // random blob to help make packet-hash unique + + auto pkt = createDatagram(PAYLOAD_TYPE_REQ, recipient.id, recipient.shared_secret, temp, sizeof(temp)); + if (pkt) { + uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2); + if (recipient.out_path_len < 0) { + sendFlood(pkt); + est_timeout = calcFloodTimeoutMillisFor(t); + return MSG_SEND_SENT_FLOOD; + } else { + sendDirect(pkt, recipient.out_path, recipient.out_path_len); + est_timeout = calcDirectTimeoutMillisFor(t, recipient.out_path_len); + return MSG_SEND_SENT_DIRECT; + } + } + return MSG_SEND_FAILED; } void BaseChatMesh::resetPathTo(ContactInfo& recipient) { diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index 814336b5..96671f94 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -27,6 +27,10 @@ struct ContactInfo { #define MSG_SEND_SENT_FLOOD 1 #define MSG_SEND_SENT_DIRECT 2 +#define CMD_GET_STATUS 0x01 // same as _GET_STATS + +#define RESP_SERVER_LOGIN_OK 0 // response to ANON_REQ + class ContactVisitor { public: virtual void onContactVisit(const ContactInfo& contact) = 0; @@ -108,7 +112,8 @@ 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); + int sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout); + int sendStatusRequest(const ContactInfo& recipient, uint32_t& est_timeout); bool shareContactZeroHop(const ContactInfo& contact); uint8_t exportContact(const ContactInfo& contact, uint8_t dest_buf[]); bool importContact(const uint8_t src_buf[], uint8_t len);