2020-11-28 04:10:19 +00:00
|
|
|
#include "MeshPlugin.h"
|
2021-03-11 02:01:57 +00:00
|
|
|
#include "Channels.h"
|
2020-12-07 02:18:11 +00:00
|
|
|
#include "MeshService.h"
|
2021-03-05 03:44:45 +00:00
|
|
|
#include "NodeDB.h"
|
2021-03-12 06:10:36 +00:00
|
|
|
#include "plugins/RoutingPlugin.h"
|
2020-11-28 04:10:19 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
2020-11-28 05:25:03 +00:00
|
|
|
std::vector<MeshPlugin *> *MeshPlugin::plugins;
|
2020-11-28 04:10:19 +00:00
|
|
|
|
2020-12-13 04:57:37 +00:00
|
|
|
const MeshPacket *MeshPlugin::currentRequest;
|
|
|
|
|
2021-03-05 03:44:45 +00:00
|
|
|
/**
|
|
|
|
* If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow
|
|
|
|
* the RoutingPlugin to avoid sending redundant acks
|
|
|
|
*/
|
2021-03-11 02:01:57 +00:00
|
|
|
MeshPacket *MeshPlugin::currentReply;
|
2021-03-05 03:44:45 +00:00
|
|
|
|
2020-11-28 05:25:03 +00:00
|
|
|
MeshPlugin::MeshPlugin(const char *_name) : name(_name)
|
|
|
|
{
|
|
|
|
// Can't trust static initalizer order, so we check each time
|
2021-03-05 03:44:45 +00:00
|
|
|
if (!plugins)
|
2020-11-28 05:25:03 +00:00
|
|
|
plugins = new std::vector<MeshPlugin *>();
|
|
|
|
|
|
|
|
plugins->push_back(this);
|
|
|
|
}
|
|
|
|
|
2021-03-05 03:44:45 +00:00
|
|
|
void MeshPlugin::setup() {}
|
2020-11-28 04:10:19 +00:00
|
|
|
|
|
|
|
MeshPlugin::~MeshPlugin()
|
|
|
|
{
|
|
|
|
assert(0); // FIXME - remove from list of plugins once someone needs this feature
|
|
|
|
}
|
|
|
|
|
2021-03-23 03:44:51 +00:00
|
|
|
MeshPacket *MeshPlugin::allocAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
|
|
|
|
{
|
|
|
|
Routing c = Routing_init_default;
|
|
|
|
|
|
|
|
c.error_reason = err;
|
|
|
|
|
|
|
|
// Now that we have moded sendAckNak up one level into the class heirarchy we can no longer assume we are a RoutingPlugin
|
|
|
|
// So we manually call pb_encode_to_bytes and specify routing port number
|
|
|
|
// auto p = allocDataProtobuf(c);
|
|
|
|
MeshPacket *p = router->allocForSending();
|
|
|
|
p->decoded.portnum = PortNum_ROUTING_APP;
|
|
|
|
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), Routing_fields, &c);
|
|
|
|
|
|
|
|
p->priority = MeshPacket_Priority_ACK;
|
|
|
|
|
|
|
|
p->hop_limit = 0; // Assume just immediate neighbors for now
|
|
|
|
p->to = to;
|
|
|
|
p->decoded.request_id = idFrom;
|
|
|
|
p->channel = chIndex;
|
|
|
|
DEBUG_MSG("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id);
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
MeshPacket *MeshPlugin::allocErrorResponse(Routing_Error err, const MeshPacket *p)
|
|
|
|
{
|
|
|
|
auto r = allocAckNak(err, getFrom(p), p->id, p->channel);
|
|
|
|
|
|
|
|
setReplyTo(r, *p);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2020-11-28 04:10:19 +00:00
|
|
|
void MeshPlugin::callPlugins(const MeshPacket &mp)
|
|
|
|
{
|
2020-11-28 05:51:51 +00:00
|
|
|
// DEBUG_MSG("In call plugins\n");
|
2020-12-13 07:59:26 +00:00
|
|
|
bool pluginFound = false;
|
2021-02-17 11:04:41 +00:00
|
|
|
|
|
|
|
assert(mp.which_payloadVariant == MeshPacket_decoded_tag); // I think we are guarnteed the packet is decoded by this point?
|
|
|
|
|
2021-03-05 03:44:45 +00:00
|
|
|
currentReply = NULL; // No reply yet
|
|
|
|
|
2021-02-17 11:04:41 +00:00
|
|
|
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
|
2021-02-23 03:43:30 +00:00
|
|
|
auto ourNodeNum = nodeDB.getNodeNum();
|
|
|
|
bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum;
|
2020-11-28 05:25:03 +00:00
|
|
|
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
|
|
|
|
auto &pi = **i;
|
2020-12-13 04:57:37 +00:00
|
|
|
|
|
|
|
pi.currentRequest = ∓
|
2021-02-17 11:04:41 +00:00
|
|
|
|
2021-03-11 02:01:57 +00:00
|
|
|
/// received channel
|
|
|
|
auto ch = channels.getByIndex(mp.channel);
|
|
|
|
assert(ch.has_settings);
|
|
|
|
|
|
|
|
/// We only call plugins that are interested in the packet (and the message is destined to us or we are promiscious)
|
2021-03-23 04:07:04 +00:00
|
|
|
bool wantsPacket = (pi.isPromiscuous || toUs) && pi.wantPacket(&mp);
|
|
|
|
|
|
|
|
if (wantsPacket) {
|
2021-03-23 03:44:51 +00:00
|
|
|
// DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket);
|
2020-12-13 07:59:26 +00:00
|
|
|
pluginFound = true;
|
|
|
|
|
2021-03-23 04:07:04 +00:00
|
|
|
/// Is the channel this packet arrived on acceptable? (security check)
|
|
|
|
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcmp(ch.settings.name, pi.boundChannel) == 0);
|
2020-12-05 03:15:06 +00:00
|
|
|
|
2021-03-23 04:07:04 +00:00
|
|
|
if (!rxChannelOk) {
|
|
|
|
// no one should have already replied!
|
|
|
|
assert(!currentReply);
|
2021-02-26 12:10:41 +00:00
|
|
|
|
2021-03-23 04:07:04 +00:00
|
|
|
if (mp.decoded.want_response) {
|
|
|
|
DEBUG_MSG("packet on wrong channel, returning error\n");
|
|
|
|
currentReply = pi.allocErrorResponse(Routing_Error_NOT_AUTHORIZED, &mp);
|
|
|
|
} else
|
|
|
|
DEBUG_MSG("packet on wrong channel, but client didn't want response\n");
|
2021-03-05 03:44:45 +00:00
|
|
|
} else {
|
2021-03-23 04:07:04 +00:00
|
|
|
|
|
|
|
bool handled = pi.handleReceived(mp);
|
|
|
|
|
|
|
|
// Possibly send replies (but only if the message was directed to us specifically, i.e. not for promiscious
|
|
|
|
// sniffing) also: we only let the one plugin send a reply, once that happens, remaining plugins are not
|
|
|
|
// considered
|
|
|
|
|
|
|
|
// NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary
|
|
|
|
// because currently when the phone sends things, it sends things using the local node ID as the from address. A
|
|
|
|
// better solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like
|
|
|
|
// any other node.
|
|
|
|
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
|
|
|
|
pi.sendResponse(mp);
|
|
|
|
DEBUG_MSG("Plugin %s sent a response\n", pi.name);
|
|
|
|
} else {
|
|
|
|
DEBUG_MSG("Plugin %s considered\n", pi.name);
|
|
|
|
}
|
|
|
|
if (handled) {
|
|
|
|
DEBUG_MSG("Plugin %s handled and skipped other processing\n", pi.name);
|
|
|
|
break;
|
|
|
|
}
|
2021-02-17 11:04:41 +00:00
|
|
|
}
|
2020-11-28 05:25:03 +00:00
|
|
|
}
|
2021-03-05 03:44:45 +00:00
|
|
|
|
2020-12-13 04:57:37 +00:00
|
|
|
pi.currentRequest = NULL;
|
2020-11-28 04:10:19 +00:00
|
|
|
}
|
2020-12-13 07:59:26 +00:00
|
|
|
|
2021-03-12 06:10:36 +00:00
|
|
|
if (mp.decoded.want_response && toUs) {
|
|
|
|
if (currentReply) {
|
|
|
|
DEBUG_MSG("Sending response\n");
|
|
|
|
service.sendToMesh(currentReply);
|
|
|
|
currentReply = NULL;
|
2021-03-23 03:44:51 +00:00
|
|
|
} else {
|
2021-03-12 06:10:36 +00:00
|
|
|
// No one wanted to reply to this requst, tell the requster that happened
|
|
|
|
DEBUG_MSG("No one responded, send a nak\n");
|
|
|
|
routingPlugin->sendAckNak(Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel);
|
|
|
|
}
|
2021-03-05 03:44:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!pluginFound)
|
2021-02-17 05:06:23 +00:00
|
|
|
DEBUG_MSG("No plugins interested in portnum=%d\n", mp.decoded.portnum);
|
2020-12-07 02:18:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
|
|
|
* so that subclasses can (optionally) send a response back to the original sender. Implementing this method
|
|
|
|
* is optional
|
|
|
|
*/
|
2021-03-05 03:44:45 +00:00
|
|
|
void MeshPlugin::sendResponse(const MeshPacket &req)
|
|
|
|
{
|
2020-12-07 02:18:11 +00:00
|
|
|
auto r = allocReply();
|
2021-03-05 03:44:45 +00:00
|
|
|
if (r) {
|
2020-12-07 02:18:11 +00:00
|
|
|
setReplyTo(r, req);
|
2021-03-11 02:01:57 +00:00
|
|
|
currentReply = r;
|
2021-03-05 03:44:45 +00:00
|
|
|
} else {
|
2021-02-23 06:35:34 +00:00
|
|
|
// Ignore - this is now expected behavior for routing plugin (because it ignores some replies)
|
|
|
|
// DEBUG_MSG("WARNING: Client requested response but this plugin did not provide\n");
|
2020-12-07 02:18:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-05 03:44:45 +00:00
|
|
|
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
2020-12-07 02:18:11 +00:00
|
|
|
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
|
2021-03-05 03:44:45 +00:00
|
|
|
*/
|
|
|
|
void setReplyTo(MeshPacket *p, const MeshPacket &to)
|
|
|
|
{
|
2021-02-26 12:36:22 +00:00
|
|
|
assert(p->which_payloadVariant == MeshPacket_decoded_tag); // Should already be set by now
|
2021-03-05 02:19:27 +00:00
|
|
|
p->to = getFrom(&to);
|
2021-03-11 02:01:57 +00:00
|
|
|
p->channel = to.channel; // Use the same channel that the request came in on
|
2021-03-05 04:37:26 +00:00
|
|
|
|
|
|
|
// No need for an ack if we are just delivering locally (it just generates an ignored ack)
|
|
|
|
p->want_ack = (to.from != 0) ? to.want_ack : false;
|
2021-03-11 02:01:57 +00:00
|
|
|
if (p->priority == MeshPacket_Priority_UNSET)
|
2021-03-05 04:37:26 +00:00
|
|
|
p->priority = MeshPacket_Priority_RELIABLE;
|
2021-02-26 12:36:22 +00:00
|
|
|
p->decoded.request_id = to.id;
|
2021-02-21 21:46:46 +00:00
|
|
|
}
|
|
|
|
|
2021-03-05 03:44:45 +00:00
|
|
|
std::vector<MeshPlugin *> MeshPlugin::GetMeshPluginsWithUIFrames()
|
|
|
|
{
|
2021-02-21 21:46:46 +00:00
|
|
|
|
2021-03-05 03:44:45 +00:00
|
|
|
std::vector<MeshPlugin *> pluginsWithUIFrames;
|
2021-02-21 21:46:46 +00:00
|
|
|
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
|
|
|
|
auto &pi = **i;
|
2021-03-05 03:44:45 +00:00
|
|
|
if (pi.wantUIFrame()) {
|
2021-02-21 21:46:46 +00:00
|
|
|
DEBUG_MSG("Plugin wants a UI Frame\n");
|
|
|
|
pluginsWithUIFrames.push_back(&pi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return pluginsWithUIFrames;
|
|
|
|
}
|