From 75a518c6caaa40289737b0c8faaf8b96e5ce129a Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Fri, 23 Sep 2022 07:40:54 +0300 Subject: [PATCH] telnet: fix 'reverse' implementation sync and async dns functions for both terminal and this both use callbacks with IPAddress helper instead of using ip_addr_t (at least, for now. seems like it is in need of our own wrapper, too) --- code/espurna/espurna.h | 1 + code/espurna/main.cpp | 1 + code/espurna/network.cpp | 227 ++++++++++++++++++++++++++++++++++++++ code/espurna/network.h | 24 ++++ code/espurna/telnet.cpp | 54 ++++++--- code/espurna/terminal.cpp | 153 +------------------------ code/espurna/wifi.h | 2 +- 7 files changed, 293 insertions(+), 169 deletions(-) create mode 100644 code/espurna/network.cpp create mode 100644 code/espurna/network.h 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 };