diff --git a/code/espurna/espurna.h b/code/espurna/espurna.h
index 1883fb0d..0f896527 100644
--- a/code/espurna/espurna.h
+++ b/code/espurna/espurna.h
@@ -35,6 +35,7 @@ along with this program. If not, see .
#include "system.h"
#include "terminal.h"
#include "utils.h"
+#include "network.h"
#include "wifi.h"
#include
diff --git a/code/espurna/main.cpp b/code/espurna/main.cpp
index aeefdaa1..f3a5e484 100644
--- a/code/espurna/main.cpp
+++ b/code/espurna/main.cpp
@@ -127,6 +127,7 @@ void setup() {
terminalSetup();
#endif
+ networkSetup();
wifiSetup();
otaSetup();
diff --git a/code/espurna/network.cpp b/code/espurna/network.cpp
new file mode 100644
index 00000000..dc8129af
--- /dev/null
+++ b/code/espurna/network.cpp
@@ -0,0 +1,227 @@
+/*
+
+NETWORKING MODULE
+
+Copyright (C) 2022 by Maxim Prokhorov
+
+*/
+
+#include "espurna.h"
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include "libs/URL.h"
+
+// not yet CONNECTING or LISTENING
+extern "C" struct tcp_pcb *tcp_bound_pcbs;
+// accepting or sending data
+extern "C" struct tcp_pcb *tcp_active_pcbs;
+// // TIME-WAIT status
+extern "C" struct tcp_pcb *tcp_tw_pcbs;
+
+namespace espurna {
+namespace network {
+namespace {
+
+namespace dns {
+namespace internal {
+
+struct Task {
+ Task() = delete;
+ explicit Task(String hostname, IpFoundCallback callback) :
+ _hostname(std::move(hostname)),
+ _callback(std::move(callback))
+ {}
+
+ IPAddress addr() const {
+ return _addr;
+ }
+
+ const String& hostname() const {
+ return _hostname;
+ }
+
+ void found_callback(const char* name, const ip_addr_t* addr, void*) {
+ _callback(name, addr);
+ }
+
+ void found_callback() {
+ _callback(_hostname, _addr);
+ }
+
+private:
+ IPAddress _addr { IPADDR_NONE };
+ String _hostname;
+
+ IpFoundCallback _callback;
+};
+
+using TaskPtr = std::unique_ptr;
+TaskPtr task;
+
+void found_callback(const char* name, const ip_addr_t* addr, void* arg) {
+ if (task) {
+ task->found_callback(name, addr, arg);
+ task.reset();
+ }
+}
+
+} // namespace internal
+
+bool started() {
+ return static_cast(internal::task);
+}
+
+void start(String hostname, IpFoundCallback callback) {
+ auto task = std::make_unique(
+ std::move(hostname), std::move(callback));
+
+ const auto result = dns_gethostbyname(
+ task->hostname().c_str(), task->addr(),
+ internal::found_callback, nullptr);
+
+ switch (result) {
+ // No need to wait, return result immediately
+ case ERR_OK:
+ task->found_callback();
+ break;
+ // Task needs to linger for a bit
+ case ERR_INPROGRESS:
+ internal::task = std::move(task);
+ break;
+ }
+}
+
+} // namespace dns
+
+#if TERMINAL_SUPPORT
+namespace terminal {
+namespace commands {
+
+void host(::terminal::CommandContext&& ctx) {
+ if (ctx.argv.size() != 2) {
+ terminalError(ctx, F("HOST "));
+ return;
+ }
+
+ dns::start(std::move(ctx.argv[1]),
+ [&](const String& name, IPAddress addr) {
+ if (!addr) {
+ ctx.output.printf_P(PSTR("%s not found\n"), name.c_str());
+ return;
+ }
+
+ ctx.output.printf_P(PSTR("%s has address %s\n"),
+ name.c_str(), addr.toString().c_str());
+ });
+
+ while (dns::started()) {
+ delay(10);
+ }
+}
+
+void netstat(::terminal::CommandContext&& ctx) {
+ const struct tcp_pcb* pcbs[] {
+ tcp_active_pcbs,
+ tcp_tw_pcbs,
+ tcp_bound_pcbs,
+ };
+
+ for (const auto* list : pcbs) {
+ for (const tcp_pcb* pcb = list; pcb != nullptr; pcb = pcb->next) {
+ ctx.output.printf_P(PSTR("state %s local %s:%hu remote %s:%hu\n"),
+ tcp_debug_state_str(pcb->state),
+ IPAddress(pcb->local_ip).toString().c_str(),
+ pcb->local_port,
+ IPAddress(pcb->remote_ip).toString().c_str(),
+ pcb->remote_port);
+ }
+ }
+}
+
+#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
+void mfln_probe(::terminal::CommandContext&& ctx) {
+ if (ctx.argv.size() != 3) {
+ terminalError(ctx, F("MFLN.PROBE "));
+ return;
+ }
+
+ URL parsed(std::move(ctx.argv[1]));
+
+ const auto parse_mfln = espurna::settings::internal::convert;
+ uint16_t mfln = parse_mfln(ctx.argv[2]);
+
+ auto client = std::make_unique();
+ client->setInsecure();
+
+ if (client->probeMaxFragmentLength(parsed.host.c_str(), parsed.port, mfln)) {
+ terminalOK(ctx);
+ return;
+ }
+
+ terminalError(ctx, F("Buffer size not supported"));
+}
+#endif
+
+} // namespace commands
+
+void setup() {
+ terminalRegisterCommand(F("NETSTAT"), commands::netstat);
+ terminalRegisterCommand(F("HOST"), commands::host);
+#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
+ terminalRegisterCommand(F("MFLN.PROBE"), commands::mfln_probe);
+#endif
+}
+
+} // namespace terminal
+#endif
+
+void gethostbyname(String hostname, IpFoundCallback callback) {
+ dns::start(std::move(hostname), std::move(callback));
+}
+
+IPAddress gethostbyname(String hostname) {
+ IPAddress out;
+
+ dns::start(std::move(hostname),
+ [&](const String& name, IPAddress ip) {
+ if (!ip.isSet()) {
+ return;
+ }
+
+ out = ip;
+ });
+
+ while (dns::started()) {
+ delay(10);
+ }
+
+ return out;
+}
+
+void setup() {
+#if TERMINAL_SUPPORT
+ terminal::setup();
+#endif
+}
+
+} // namespace
+} // namespace network
+} // namespace espurna
+
+void networkGetHostByName(String hostname, espurna::network::IpFoundCallback callback) {
+ return espurna::network::gethostbyname(std::move(hostname), std::move(callback));
+}
+
+IPAddress networkGetHostByName(String hostname) {
+ return espurna::network::gethostbyname(std::move(hostname));
+}
+
+void networkSetup() {
+ espurna::network::setup();
+}
diff --git a/code/espurna/network.h b/code/espurna/network.h
new file mode 100644
index 00000000..36e79e59
--- /dev/null
+++ b/code/espurna/network.h
@@ -0,0 +1,24 @@
+/*
+
+NETWORKING MODULE
+
+Copyright (C) 2022 by Maxim Prokhorov
+
+*/
+
+#pragma once
+
+#include
+#include
+
+namespace espurna {
+namespace network {
+
+using IpFoundCallback = std::function;
+
+} // namespace network
+} // namespace espurna
+
+IPAddress networkGetHostByName(String);
+void networkGetHostByName(String, espurna::network::IpFoundCallback);
+void networkSetup();
diff --git a/code/espurna/telnet.cpp b/code/espurna/telnet.cpp
index 344d9e88..01835f44 100644
--- a/code/espurna/telnet.cpp
+++ b/code/espurna/telnet.cpp
@@ -829,7 +829,7 @@ bool listen() {
#if TELNET_REVERSE_SUPPORT
namespace reverse {
-bool connect(Remote remote) {
+bool connect(Address address) {
auto* pcb = tcp_new();
if (!pcb) {
return false;
@@ -838,7 +838,7 @@ bool connect(Remote remote) {
// wait until the connection attempt happens
// or, we could also fail right here as well
auto client = make_client(pcb, false);
- if (!client->connect(remote)) {
+ if (!client->connect(address)) {
return false;
}
@@ -855,15 +855,14 @@ namespace terminal {
void setup() {
terminalRegisterCommand(F("TELNET.REVERSE"), [](::terminal::CommandContext&& ctx) {
- if (ctx.argc != 3) {
- terminalError(ctx, F(" "));
+ if (ctx.argv.size() != 3) {
+ terminalError(ctx, F("TELNET.REVERSE "));
return;
}
- const auto convert_addr = espurna::settings::internal::convert;
- auto addr = convert_addr(ctx.argv[1]);
- if (!addr.isSet()) {
- terminalError(ctx, F("Address not set"));
+ const auto ip = networkGetHostByName(ctx.argv[1]);
+ if (!ip.isSet()) {
+ terminalError(ctx, F("Host not found"));
return;
}
@@ -874,7 +873,12 @@ void setup() {
return;
}
- if (telnet::connect(addr, port)) {
+ const auto address = Address{
+ .ip = ip,
+ .port = port,
+ };
+
+ if (connect(address)) {
terminalOK(ctx);
return;
}
@@ -889,25 +893,43 @@ void setup() {
#if MQTT_SUPPORT
namespace mqtt {
+void connect_url(String url) {
+ URL parsed(std::move(url));
+ if (!parsed.host.length() || !parsed.port) {
+ DEBUG_MSG_P(PSTR("[TELNET] Cannot parse the url\n"));
+ return;
+ }
+
+ const auto port = parsed.port;
+ networkGetHostByName(std::move(parsed.host),
+ [port](const String& host, IPAddress ip) {
+ const auto addr = Address{
+ .ip = ip,
+ .port = port,
+ };
+
+ if (!connect(addr)) {
+ DEBUG_MSG_P(PSTR("[TELNET] Cannot connect to %s:%hu\n"),
+ host.c_str(), port);
+ }
+ });
+}
+
void setup() {
mqttRegister([](unsigned int type, const char* topic, const char* payload) {
switch (type) {
case MQTT_CONNECT_EVENT:
mqttSubscribe(MQTT_TOPIC_TELNET_REVERSE);
break;
+
case MQTT_MESSAGE_EVENT: {
auto t = mqttMagnitude(topic);
if (t.equals(MQTT_TOPIC_TELNET_REVERSE)) {
- URL url(payload);
-
- IPAddress addr;
- addr.fromString(url.host);
- if (addr.isSet()) {
- telnet::connect(addr, url.port);
- }
+ connect_url(payload);
}
break;
}
+
}
});
}
diff --git a/code/espurna/terminal.cpp b/code/espurna/terminal.cpp
index c2298e2b..fc739c6c 100644
--- a/code/espurna/terminal.cpp
+++ b/code/espurna/terminal.cpp
@@ -3,7 +3,7 @@
TERMINAL MODULE
Copyright (C) 2016-2019 by Xose Pérez
-Copyright (C) 2020 by Maxim Prokhorov
+Copyright (C) 2020-2022 by Maxim Prokhorov
*/
@@ -22,8 +22,6 @@ Copyright (C) 2020 by Maxim Prokhorov
#include "wifi.h"
#include "ws.h"
-#include "libs/URL.h"
-#include "libs/StreamAdapter.h"
#include "libs/PrintString.h"
#include "web_asyncwebprint.ipp"
@@ -34,13 +32,6 @@ Copyright (C) 2020 by Maxim Prokhorov
#include
#include
-// not yet CONNECTING or LISTENING
-extern "C" struct tcp_pcb *tcp_bound_pcbs;
-// accepting or sending data
-extern "C" struct tcp_pcb *tcp_active_pcbs;
-// // TIME-WAIT status
-extern "C" struct tcp_pcb *tcp_tw_pcbs;
-
// FS 'range', declared at compile time via .ld script PROVIDE declarations
// (althought, in recent Core versions, these may be set at runtime)
extern "C" uint32_t _FS_start;
@@ -86,142 +77,6 @@ void help(CommandContext&& ctx) {
terminalOK(ctx);
}
-void netstat(CommandContext&& ctx) {
- const struct tcp_pcb* pcbs[] {
- tcp_active_pcbs,
- tcp_tw_pcbs,
- tcp_bound_pcbs,
- };
-
- for (const auto* list : pcbs) {
- for (const tcp_pcb* pcb = list; pcb != nullptr; pcb = pcb->next) {
- ctx.output.printf_P(PSTR("state %s local %s:%hu remote %s:%hu\n"),
- tcp_debug_state_str(pcb->state),
- IPAddress(pcb->local_ip).toString().c_str(),
- pcb->local_port,
- IPAddress(pcb->remote_ip).toString().c_str(),
- pcb->remote_port);
- }
- }
-}
-
-namespace dns {
-
-using FoundCallback = std::function;
-
-namespace internal {
-
-struct Task {
- Task() = delete;
- explicit Task(String hostname, FoundCallback callback) :
- _hostname(std::move(hostname)),
- _callback(std::move(callback))
- {}
-
- ip_addr_t* addr() {
- return &_addr;
- }
-
- const String& hostname() const {
- return _hostname;
- }
-
- void found_callback(const char* name, const ip_addr_t* addr, void* arg) {
- _callback(name, addr, arg);
- }
-
- void found_callback() {
- _callback(_hostname.c_str(), &_addr, nullptr);
- }
-
-private:
- String _hostname;
- FoundCallback _callback;
- ip_addr_t _addr { IPADDR_NONE };
-};
-
-using TaskPtr = std::unique_ptr;
-TaskPtr task;
-
-void found_callback(const char* name, const ip_addr_t* addr, void* arg) {
- if (task) {
- task->found_callback(name, addr, arg);
- task.reset();
- }
-}
-
-} // namespace internal
-
-bool started() {
- return static_cast(internal::task);
-}
-
-void start(String hostname, FoundCallback callback) {
- auto task = std::make_unique(
- std::move(hostname), std::move(callback));
-
- const auto result = dns_gethostbyname(
- task->hostname().c_str(), task->addr(),
- internal::found_callback, nullptr);
-
- switch (result) {
- // No need to wait, return result immediately
- case ERR_OK:
- task->found_callback();
- break;
- // Task needs to linger for a bit
- case ERR_INPROGRESS:
- internal::task = std::move(task);
- break;
- }
-}
-
-} // namespace dns
-
-void host(CommandContext&& ctx) {
- if (ctx.argv.size() != 2) {
- terminalError(ctx, F("HOST "));
- return;
- }
-
- dns::start(std::move(ctx.argv[1]),
- [&](const char* name, const ip_addr_t* addr, void*) {
- if (!addr) {
- ctx.output.printf_P(PSTR("%s not found\n"), name);
- return;
- }
-
- ctx.output.printf_P(PSTR("%s has address %s\n"),
- name, IPAddress(addr).toString().c_str());
- });
-
- while (dns::started()) {
- delay(100);
- }
-}
-
-#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
-void mfln_probe(CommandContext&& ctx) {
- if (ctx.argv.size() != 3) {
- terminalError(ctx, F(" "));
- return;
- }
-
- URL _url(std::move(ctx.argv[1]));
- uint16_t requested_mfln = atol(ctx.argv[2].c_str());
-
- auto client = std::make_unique();
- client->setInsecure();
-
- if (client->probeMaxFragmentLength(_url.host.c_str(), _url.port, requested_mfln)) {
- terminalOK(ctx);
- return;
- }
-
- terminalError(ctx, F("Buffer size not supported"));
-}
-#endif
-
void reset(CommandContext&& ctx) {
prepareReset(CustomResetReason::Terminal);
terminalOK(ctx);
@@ -478,12 +333,6 @@ void setup() {
terminalRegisterCommand(F("UPTIME"), commands::uptime);
terminalRegisterCommand(F("HEAP"), commands::heap);
- terminalRegisterCommand(F("NETSTAT"), commands::netstat);
- terminalRegisterCommand(F("HOST"), commands::host);
-#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
- terminalRegisterCommand(F("MFLN.PROBE"), commands::mfln_probe);
-#endif
-
terminalRegisterCommand(F("ADC"), commands::adc);
terminalRegisterCommand(F("RESET"), commands::reset);
diff --git a/code/espurna/wifi.h b/code/espurna/wifi.h
index eb3cebfe..c1fd7d6f 100644
--- a/code/espurna/wifi.h
+++ b/code/espurna/wifi.h
@@ -30,7 +30,7 @@ extern "C" {
#include
#include // ip_addr_t
#include // ERR_x
- #include // dns_gethostbyname
+ #include // dns_getserver, dns_gethostbyname
#include // ip4/ip6 helpers
};