diff --git a/src/modules/wifi/scan_hosts.cpp b/src/modules/wifi/scan_hosts.cpp index e5eb82bd..393c89b3 100644 --- a/src/modules/wifi/scan_hosts.cpp +++ b/src/modules/wifi/scan_hosts.cpp @@ -1,144 +1,93 @@ -#include "lwip/etharp.h" #include "core/globals.h" #include "core/display.h" #include "core/mykeyboard.h" #include "core/wifi_common.h" #include "scan_hosts.h" #include "clients.h" +#include +#include //thx to 7h30th3r0n3, which made scanHosts faster using ARP -std::vector hostslist; - -void read_arp_table(char * from_ip, int read_from, int read_to, std::vector& hostslist) { - Serial.printf("Reading ARP table from: %d to %d\n", read_from, read_to); - for (int i = read_from; i <= read_to; i++) { - char test[32]; - sprintf(test, "%s%d", from_ip, i); - ip4_addr_t test_ip; - ipaddr_aton(test, (ip_addr_t*)&test_ip); - - const ip4_addr_t *ipaddr_ret = NULL; - struct eth_addr *eth_ret = NULL; - if (etharp_find_addr(NULL, &test_ip, ð_ret, &ipaddr_ret) >= 0) { - IPAddress foundIP; - foundIP.fromString(ipaddr_ntoa((ip_addr_t*)&test_ip)); - hostslist.push_back(foundIP); - //tft.println(foundIP.toString()); - Serial.printf("Adding found IP: %s\n", ipaddr_ntoa((ip_addr_t*)&test_ip)); - } - } -} +static std::vector hostslist; -void send_arp(char * from_ip, std::vector& hostslist) { - Serial.println("Sending ARP requests to the whole network"); - const TickType_t xDelay = (10) / portTICK_PERIOD_MS; - void * netif = NULL; - tcpip_adapter_get_netif(TCPIP_ADAPTER_IF_STA, &netif); - struct netif *netif_interface = (struct netif *)netif; - - for (char i = 1; i < 254; i++) { - char test[32]; - sprintf(test, "%s%d", from_ip, i); - ip4_addr_t test_ip; - ipaddr_aton(test, (ip_addr_t*)&test_ip); - - int8_t arp_request_ret = etharp_request(netif_interface, &test_ip); - vTaskDelay(xDelay); - } - // Lire toutes les entrĂ©es de la table ARP - read_arp_table(from_ip, 1, 254, hostslist); -} +// TODO: resolve clients name when in host mode through dhcp -void logARPResult(IPAddress host, bool responded) { - char buffer[64]; - if (responded) { - sprintf(buffer, "Host %s respond to ARP.", host.toString().c_str()); - } else { - sprintf(buffer, "Host %s did not respond to ARP.", host.toString().c_str()); - } - Serial.println(buffer); -} +// TODO: move to a config +TickType_t arpRequestDelay = 20u / portTICK_PERIOD_MS; // can be relatively low, helps to not overwhelm the stream -bool arpRequest(IPAddress host) { - char ipStr[16]; - sprintf(ipStr, "%s", host.toString().c_str()); - ip4_addr_t test_ip; - ipaddr_aton(ipStr, (ip_addr_t*)&test_ip); - - struct eth_addr *eth_ret = NULL; - const ip4_addr_t *ipaddr_ret = NULL; - bool responded = etharp_find_addr(NULL, &test_ip, ð_ret, &ipaddr_ret) >= 0; - logARPResult(host, responded); - return responded; +void readArpTable(netif * iface) { + for( uint32_t i = 0; i < ARP_TABLE_SIZE; ++i ){ + ip4_addr_t* ip_ret; + eth_addr* eth_ret; + if( etharp_get_entry(i, &ip_ret, &iface, ð_ret) ){ + hostslist.emplace_back(ip_ret, eth_ret); + } + } + etharp_cleanup_netif(iface); } - void local_scan_setup() { bool doScan = true; if(!wifiConnected) doScan=wifiConnectMenu(false); if (doScan) { hostslist.clear(); - int lastDot = WiFi.localIP().toString().lastIndexOf('.'); - String networkRange = WiFi.localIP().toString().substring(0, lastDot + 1); - char networkRangeChar[12]; - - networkRange.toCharArray(networkRangeChar, sizeof(networkRangeChar)); - - send_arp(networkRangeChar, hostslist); - - IPAddress gatewayIP; - IPAddress subnetMask; - //std::vector hostslist; - - gatewayIP = WiFi.gatewayIP(); - subnetMask = WiFi.subnetMask(); - - IPAddress network = WiFi.localIP(); - network[3] = 0; - - int numHosts = 254 - subnetMask[3]; - - displayRedStripe("Probing " + String(numHosts) + " hosts",TFT_WHITE, FGCOLOR); - - bool foundHosts; - bool stopScan; - - char base_ip[16]; - sprintf(base_ip, "%d.%d.%d.", network[0], network[1], network[2]); - - send_arp(base_ip, hostslist); - for (int i = 1; i <= numHosts; i++) { - if (stopScan) { - break; - } - - IPAddress currentIP = network; - currentIP[3] = i; - - if (arpRequest(currentIP)) { - hostslist.push_back(currentIP); - foundHosts = true; - } + // IPAddress uint32_t op returns number in big-endian + // for simplicity of iteration and arithmetics convert to little-endian + const uint32_t localIp = ntohl(WiFi.localIP()); + const IPAddress gateway = WiFi.gatewayIP(); + const uint32_t subnetMask = ntohl(WiFi.subnetMask()); + const uint32_t networkAddress = ntohl(gateway) & subnetMask; + const uint32_t broadcast = networkAddress | ~subnetMask; + + // get iface + void * netif = nullptr; + tcpip_adapter_get_netif(TCPIP_ADAPTER_IF_STA, &netif); + struct netif *net_iface = (struct netif *)netif; + etharp_cleanup_netif(net_iface); // to avoid gateway duplication + + displayRedStripe("Probing " + String(broadcast - networkAddress - 1) + " hosts",TFT_WHITE, FGCOLOR); // minus broadcast and subnet mask + + // send arp requests, read table each ARP_TABLE_SIZE requests + uint16_t tableReadCounter = 0; + for( uint32_t ip_le = networkAddress + 1; ip_le < broadcast; ++ip_le ){ + if( ip_le == localIp ) continue; + + ip4_addr_t ip_be{htonl(ip_le)}; // big endian + err_t res = etharp_request(net_iface, &ip_be); + + if( res != ERR_OK ){ + Serial.println("Arp req for: " + IPAddress(ip_be.addr).toString() + "failed with ec: " + res); + } else { + ++tableReadCounter; + } + + vTaskDelay(arpRequestDelay); + + // read table if we sent ARP_TABLE_SIZE requests + if( tableReadCounter == ARP_TABLE_SIZE ){ + readArpTable(net_iface); + tableReadCounter = 0; + } + } + + ScanHostMenu: + if (hostslist.empty()) { + tft.println("No hosts found"); + delay(2000); + return; } - - - ScanHostMenu: + options = {}; for(auto host:hostslist) { - String result = host.toString().substring(host.toString().lastIndexOf('.') - 1); - options.push_back({result.c_str(), [=](){afterScanOptions(host); }}); + String result = host.ip.toString().substring(host.ip.toString().lastIndexOf('.') - 1); + if( host.ip == gateway ) result += "(GATE)"; + options.push_back({result.c_str(), [=](){ afterScanOptions(host); }}); } options.push_back({"Main Menu", [=]() { backToMenu(); }}); - if (!foundHosts) { - tft.println("No hosts found"); - delay(2000); - return; - } - delay(200); loopOptions(options); delay(200); @@ -147,33 +96,66 @@ void local_scan_setup() { hostslist.clear(); } - -void afterScanOptions(IPAddress ip) { +void afterScanOptions(const Host& host) { options = { - {"Scan Ports", [=](){ scanPorts(ip); }}, + {"Host info", [=](){ hostInfo(host); }}, #ifndef LITE_VERSION - {"SSH Connect", [=](){ ssh_setup(ip.toString()); }}, + {"SSH Connect", [=](){ ssh_setup(host.ip.toString()); }}, #endif }; loopOptions(options); delay(200); } +// TODO create config option to toggle this info +String getManufacturer(const String& mac){ + // TODO move it to core functions that checks internet access + if( !Ping.ping(IPAddress(8,8,8,8)) ){ return "NO_INTERNET_ACCESS"; } + + // there is an official(IEEE) doc that contains all registered mac prefixes + // but it is around 700kb and i don't know a way to get specific part + // without downloading the whole txt + HTTPClient http; + http.begin("https://api.maclookup.app/v2/macs/" + mac); + int httpCode = http.GET(); // Send the request + if( httpCode != 200 ){ http.end(); return "GET failed"; } + + // payload is a json of the format + // {"success":true,"found":true,"macPrefix":"2C3358","company":"Intel Corporate","address":"Lot 8, Jalan Hi-Tech 2/3, Kulim Kedah 09000, MY","country":"MY","blockStart":"2C3358000000","blockEnd":"2C3358FFFFFF","blockSize":16777215,"blockType":"MA-L","updated":"2021-10-13","isRand":false,"isPrivate":false} + // company field is going to be empty if none found + String payload{http.getString()}; + size_t company_start_idx = payload.indexOf("company") + 10; // + 7(company) + 3(":") + String manufacturer = payload.substring(company_start_idx, payload.indexOf('"', company_start_idx)); + if( manufacturer.isEmpty() ) return "UNKNOWN"; + + return manufacturer; +} -void scanPorts(IPAddress host) { +void hostInfo(const Host& host) { WiFiClient client; const int ports[] = {20, 21, 22, 23, 25, 80, 137, 139, 443, 3389, 8080, 8443, 9090}; const int numPorts = sizeof(ports) / sizeof(ports[0]); drawMainBorder(); tft.setTextSize(FP); tft.setCursor(8,30); - tft.print("Host: " + host.toString()); + + tft.print("Host: " + host.ip.toString()); tft.setCursor(8,42); + + tft.print("Mac: " + host.mac); + tft.setCursor(8,54); + + tft.print("Manufacturer: " + getManufacturer(host.mac)); + tft.setCursor(8,66); + tft.print("Ports Opened: "); - //for (int port = start; port <= stop; port++) { for (int i = 0; i < numPorts; i++) { int port = ports[i]; - if (client.connect(host, port)) { + + // TODO: print the ports being scanned + // right now there is no idea to understand which ports are being scanned + // without looking at the code + if (client.connect(host.ip, port)) { if (tft.getCursorX()>(240-LW*4)) tft.setCursor(7,tft.getCursorY() + LH); tft.print(port); tft.print(", "); diff --git a/src/modules/wifi/scan_hosts.h b/src/modules/wifi/scan_hosts.h index ac4b12de..1628bce3 100644 --- a/src/modules/wifi/scan_hosts.h +++ b/src/modules/wifi/scan_hosts.h @@ -1,13 +1,25 @@ - #include #include #include -#include -#include -#include + +#include "lwip/etharp.h" +// sets number of maximum of pending requests to table size +#define ARP_MAXPENDING ARP_TABLE_SIZE + +struct Host { + Host(ip4_addr_t* ip, eth_addr* eth) : ip(ip->addr){ + char macStr[18]; + snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", + eth->addr[0], eth->addr[1], eth->addr[2], + eth->addr[3], eth->addr[4], eth->addr[5]); + mac = macStr; + } + IPAddress ip; + String mac; +}; void local_scan_setup(); -void scanPorts(IPAddress host); +void hostInfo(const Host& host); -void afterScanOptions(IPAddress ip); \ No newline at end of file +void afterScanOptions(const Host& ip); \ No newline at end of file