|
|
|
@ -13,6 +13,7 @@ |
|
|
|
#include <helpers/StaticPoolPacketManager.h> |
|
|
|
#include <helpers/SimpleMeshTables.h> |
|
|
|
#include <helpers/IdentityStore.h> |
|
|
|
#include <RTClib.h> |
|
|
|
|
|
|
|
/* ------------------------------ Config -------------------------------- */ |
|
|
|
|
|
|
|
@ -262,39 +263,81 @@ protected: |
|
|
|
} |
|
|
|
|
|
|
|
void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override { |
|
|
|
if (type == PAYLOAD_TYPE_REQ) { // request (from a Known admin client!)
|
|
|
|
int i = matching_peer_indexes[sender_idx]; |
|
|
|
int i = matching_peer_indexes[sender_idx]; |
|
|
|
if (i < 0 || i >= num_clients) { // get from our known_clients table (sender SHOULD already be known in this context)
|
|
|
|
MESH_DEBUG_PRINTLN("onPeerDataRecv: invalid peer idx: %d", i); |
|
|
|
return; |
|
|
|
} |
|
|
|
auto client = &known_clients[i]; |
|
|
|
if (type == PAYLOAD_TYPE_REQ) { // request (from a Known admin client!)
|
|
|
|
uint32_t timestamp; |
|
|
|
memcpy(×tamp, data, 4); |
|
|
|
|
|
|
|
if (timestamp > client->last_timestamp) { // prevent replay attacks
|
|
|
|
int reply_len = handleRequest(client, &data[4], len - 4); |
|
|
|
if (reply_len == 0) return; // invalid command
|
|
|
|
|
|
|
|
if (i >= 0 && i < num_clients) { // get from our known_clients table (sender SHOULD already be known in this context)
|
|
|
|
auto client = &known_clients[i]; |
|
|
|
client->last_timestamp = timestamp; |
|
|
|
|
|
|
|
if (packet->isRouteFlood()) { |
|
|
|
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
|
|
|
|
mesh::Packet* path = createPathReturn(client->id, secret, packet->path, packet->path_len, |
|
|
|
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len); |
|
|
|
if (path) sendFlood(path); |
|
|
|
} else { |
|
|
|
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len); |
|
|
|
if (reply) { |
|
|
|
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
|
|
|
|
sendDirect(reply, client->out_path, client->out_path_len); |
|
|
|
} else { |
|
|
|
sendFlood(reply); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (type == PAYLOAD_TYPE_TXT_MSG && len > 5) { // a CLI command
|
|
|
|
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
|
|
|
|
|
|
|
|
uint32_t timestamp; |
|
|
|
memcpy(×tamp, data, 4); |
|
|
|
if (flags == 0 && timestamp > client->last_timestamp) { // prevent replay attacks
|
|
|
|
client->last_timestamp = timestamp; |
|
|
|
|
|
|
|
if (timestamp > client->last_timestamp) { // prevent replay attacks
|
|
|
|
int reply_len = handleRequest(client, &data[4], len - 4); |
|
|
|
if (reply_len == 0) return; // invalid command
|
|
|
|
// len can be > original length, but 'text' will be padded with zeroes
|
|
|
|
data[len] = 0; // need to make a C string again, with null terminator
|
|
|
|
|
|
|
|
client->last_timestamp = timestamp; |
|
|
|
uint32_t ack_hash; // calc truncated hash of the message timestamp + text + sender pub_key, to prove to sender that we got it
|
|
|
|
mesh::Utils::sha256((uint8_t *) &ack_hash, 4, data, 5 + strlen((char *)&data[5]), client->id.pub_key, PUB_KEY_SIZE); |
|
|
|
|
|
|
|
if (packet->isRouteFlood()) { |
|
|
|
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
|
|
|
|
mesh::Packet* path = createPathReturn(client->id, secret, packet->path, packet->path_len, |
|
|
|
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len); |
|
|
|
if (path) sendFlood(path); |
|
|
|
mesh::Packet* ack = createAck(ack_hash); |
|
|
|
if (ack) { |
|
|
|
if (client->out_path_len < 0) { |
|
|
|
sendFlood(ack); |
|
|
|
} else { |
|
|
|
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len); |
|
|
|
if (reply) { |
|
|
|
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
|
|
|
|
sendDirect(reply, client->out_path, client->out_path_len); |
|
|
|
} else { |
|
|
|
sendFlood(reply); |
|
|
|
} |
|
|
|
sendDirect(ack, client->out_path, client->out_path_len); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
uint8_t temp[166]; |
|
|
|
handleCommand(timestamp, (const char *) &data[5], (char *) &temp[5]); |
|
|
|
int text_len = strlen((char *) &temp[5]); |
|
|
|
if (text_len > 0) { |
|
|
|
uint32_t timestamp = getRTCClock()->getCurrentTime(); |
|
|
|
memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique
|
|
|
|
temp[4] = 0; |
|
|
|
|
|
|
|
// calc expected ACK reply
|
|
|
|
//mesh::Utils::sha256((uint8_t *)&expected_ack_crc, 4, temp, 5 + text_len, self_id.pub_key, PUB_KEY_SIZE);
|
|
|
|
|
|
|
|
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len); |
|
|
|
if (reply) { |
|
|
|
if (client->out_path_len < 0) { |
|
|
|
sendFlood(reply); |
|
|
|
} else { |
|
|
|
sendDirect(reply, client->out_path, client->out_path_len); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
MESH_DEBUG_PRINTLN("onPeerDataRecv: invalid peer idx: %d", i); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -359,6 +402,31 @@ public: |
|
|
|
MESH_DEBUG_PRINTLN("ERROR: unable to create advertisement packet!"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void handleCommand(uint32_t sender_timestamp, const char* command, char reply[]) { |
|
|
|
while (*command == ' ') command++; // skip leading spaces
|
|
|
|
|
|
|
|
if (memcmp(command, "reboot", 6) == 0) { |
|
|
|
board.reboot(); // doesn't return
|
|
|
|
} else if (memcmp(command, "advert", 6) == 0) { |
|
|
|
sendSelfAdvertisement(); |
|
|
|
strcpy(reply, "OK - Advert sent"); |
|
|
|
} else if (memcmp(command, "clock sync", 10) == 0) { |
|
|
|
uint32_t curr = getRTCClock()->getCurrentTime(); |
|
|
|
if (sender_timestamp > curr) { |
|
|
|
getRTCClock()->setCurrentTime(sender_timestamp); |
|
|
|
strcpy(reply, "OK - clock set"); |
|
|
|
} else { |
|
|
|
strcpy(reply, "ERR: clock cannot go backwards"); |
|
|
|
} |
|
|
|
} else if (memcmp(command, "clock", 5) == 0) { |
|
|
|
uint32_t now = getRTCClock()->getCurrentTime(); |
|
|
|
DateTime dt = DateTime(now); |
|
|
|
sprintf(reply, "%02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year()); |
|
|
|
} else { |
|
|
|
sprintf(reply, "Unknown: %s (commands: reboot, advert, clock)", command); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
#if defined(NRF52_PLATFORM) |
|
|
|
@ -456,13 +524,10 @@ void loop() { |
|
|
|
|
|
|
|
if (len > 0 && command[len - 1] == '\r') { // received complete line
|
|
|
|
command[len - 1] = 0; // replace newline with C string null terminator
|
|
|
|
if (strcmp(command, "reboot") == 0) { |
|
|
|
board.reboot(); // doesn't return
|
|
|
|
} else if (strcmp(command, "advert") == 0) { |
|
|
|
the_mesh.sendSelfAdvertisement(); |
|
|
|
} else { |
|
|
|
Serial.print(" ERROR: unknown command: "); Serial.println(command); |
|
|
|
Serial.println(" (commands: reboot, advert)"); |
|
|
|
char reply[160]; |
|
|
|
the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial!
|
|
|
|
if (reply[0]) { |
|
|
|
Serial.print(" -> "); Serial.println(reply); |
|
|
|
} |
|
|
|
|
|
|
|
command[0] = 0; // reset command buffer
|
|
|
|
|