/* This software is licensed under the MIT License: https://github.com/spacehuhntech/esp8266_deauther */ #include "wifi.h" extern "C" { #include "user_interface.h" } #include #include #include #include #include #include #include "language.h" #include "debug.h" #include "settings.h" #include "CLI.h" #include "Attack.h" #include "Scan.h" extern bool progmemToSpiffs(const char* adr, int len, String path); #include "webfiles.h" extern Scan scan; extern CLI cli; extern Attack attack; typedef enum wifi_mode_t { off = 0, ap = 1, st = 2 } wifi_mode_t; typedef struct ap_settings_t { char path[33]; char ssid[33]; char password[65]; uint8_t channel; bool hidden; bool captive_portal; } ap_settings_t; namespace wifi { // ===== PRIVATE ===== // wifi_mode_t mode; ap_settings_t ap_settings; // Server and other global objects ESP8266WebServer server(80); DNSServer dns; IPAddress ip WEB_IP_ADDR; IPAddress netmask(255, 255, 255, 0); void setPath(String path) { if (path.charAt(0) != '/') { path = '/' + path; } if (path.length() > 32) { debuglnF("ERROR: Path longer than 32 characters"); } else { strncpy(ap_settings.path, path.c_str(), 32); } } void setSSID(String ssid) { if (ssid.length() > 32) { debuglnF("ERROR: SSID longer than 32 characters"); } else { strncpy(ap_settings.ssid, ssid.c_str(), 32); } } void setPassword(String password) { if (password.length() > 64) { debuglnF("ERROR: Password longer than 64 characters"); } else if (password.length() < 8) { debuglnF("ERROR: Password must be at least 8 characters long"); } else { strncpy(ap_settings.password, password.c_str(), 64); } } void setChannel(uint8_t ch) { if ((ch < 1) && (ch > 14)) { debuglnF("ERROR: Channel must be withing the range of 1-14"); } else { ap_settings.channel = ch; } } void setHidden(bool hidden) { ap_settings.hidden = hidden; } void setCaptivePortal(bool captivePortal) { ap_settings.captive_portal = captivePortal; } void handleFileList() { if (!server.hasArg("dir")) { server.send(500, str(W_TXT), str(W_BAD_ARGS)); return; } String path = server.arg("dir"); // debugF("handleFileList: "); // debugln(path); Dir dir = LittleFS.openDir(path); String output = String('{'); // { File entry; bool first = true; while (dir.next()) { entry = dir.openFile("r"); if (first) first = false; else output += ','; // , output += '['; // [ output += '"' + entry.name() + '"'; // "filename" output += ']'; // ] entry.close(); } output += CLOSE_BRACKET; server.send(200, str(W_JSON).c_str(), output); } String getContentType(String filename) { if (server.hasArg("download")) return String(F("application/octet-stream")); else if (filename.endsWith(str(W_DOT_GZIP))) filename = filename.substring(0, filename.length() - 3); else if (filename.endsWith(str(W_DOT_HTM))) return str(W_HTML); else if (filename.endsWith(str(W_DOT_HTML))) return str(W_HTML); else if (filename.endsWith(str(W_DOT_CSS))) return str(W_CSS); else if (filename.endsWith(str(W_DOT_JS))) return str(W_JS); else if (filename.endsWith(str(W_DOT_PNG))) return str(W_PNG); else if (filename.endsWith(str(W_DOT_GIF))) return str(W_GIF); else if (filename.endsWith(str(W_DOT_JPG))) return str(W_JPG); else if (filename.endsWith(str(W_DOT_ICON))) return str(W_ICON); else if (filename.endsWith(str(W_DOT_XML))) return str(W_XML); else if (filename.endsWith(str(W_DOT_PDF))) return str(W_XPDF); else if (filename.endsWith(str(W_DOT_ZIP))) return str(W_XZIP); else if (filename.endsWith(str(W_DOT_JSON))) return str(W_JSON); return str(W_TXT); } bool handleFileRead(String path) { // prnt(W_AP_REQUEST); // prnt(path); if (path.charAt(0) != '/') path = '/' + path; if (path.charAt(path.length() - 1) == '/') path += String(F("index.html")); String contentType = getContentType(path); if (!LittleFS.exists(path)) { if (LittleFS.exists(path + str(W_DOT_GZIP))) path += str(W_DOT_GZIP); else if (LittleFS.exists(String(ap_settings.path) + path)) path = String(ap_settings.path) + path; else if (LittleFS.exists(String(ap_settings.path) + path + str(W_DOT_GZIP))) path = String(ap_settings.path) + path + str(W_DOT_GZIP); else { // prntln(W_NOT_FOUND); return false; } } File file = LittleFS.open(path, "r"); server.streamFile(file, contentType); file.close(); // prnt(SPACE); // prntln(W_OK); return true; } void sendProgmem(const char* ptr, size_t size, const char* type) { server.sendHeader("Content-Encoding", "gzip"); server.sendHeader("Cache-Control", "max-age=3600"); server.send_P(200, str(type).c_str(), ptr, size); } // ===== PUBLIC ====== // void begin() { // Set settings setPath("/web"); setSSID(settings::getAccessPointSettings().ssid); setPassword(settings::getAccessPointSettings().password); setChannel(1); setHidden(settings::getAccessPointSettings().hidden); setCaptivePortal(settings::getWebSettings().captive_portal); // Set mode mode = wifi_mode_t::off; WiFi.mode(WIFI_OFF); wifi_set_opmode(STATION_MODE); // Set mac address wifi_set_macaddr(STATION_IF, (uint8_t*)settings::getWifiSettings().mac_st); wifi_set_macaddr(SOFTAP_IF, (uint8_t*)settings::getWifiSettings().mac_ap); } String getMode() { switch (mode) { case wifi_mode_t::off: return "OFF"; case wifi_mode_t::ap: return "AP"; case wifi_mode_t::st: return "ST"; default: return String(); } } void printStatus() { prnt(String(F("[WiFi] Path: '"))); prnt(ap_settings.path); prnt(String(F("', Mode: '"))); prnt(getMode()); prnt(String(F("', SSID: '"))); prnt(ap_settings.ssid); prnt(String(F("', password: '"))); prnt(ap_settings.password); prnt(String(F("', channel: '"))); prnt(ap_settings.channel); prnt(String(F("', hidden: "))); prnt(b2s(ap_settings.hidden)); prnt(String(F(", captive-portal: "))); prntln(b2s(ap_settings.captive_portal)); } void startNewAP(String path, String ssid, String password, uint8_t ch, bool hidden, bool captivePortal) { setPath(path); setSSID(ssid); setPassword(password); setChannel(ch); setHidden(hidden); setCaptivePortal(captivePortal); startAP(); } /* void startAP(String path) { setPath(path): startAP(); } */ void startAP() { WiFi.softAPConfig(ip, ip, netmask); WiFi.softAP(ap_settings.ssid, ap_settings.password, ap_settings.channel, ap_settings.hidden); dns.setErrorReplyCode(DNSReplyCode::NoError); dns.start(53, "*", ip); MDNS.begin(WEB_URL); server.on("/list", HTTP_GET, handleFileList); // list directory #ifdef USE_PROGMEM_WEB_FILES // ================================================================ // paste here the output of the webConverter.py if (!settings::getWebSettings().use_spiffs) { server.on("/", HTTP_GET, []() { sendProgmem(indexhtml, sizeof(indexhtml), W_HTML); }); server.on("/index.html", HTTP_GET, []() { sendProgmem(indexhtml, sizeof(indexhtml), W_HTML); }); server.on("/scan.html", HTTP_GET, []() { sendProgmem(scanhtml, sizeof(scanhtml), W_HTML); }); server.on("/info.html", HTTP_GET, []() { sendProgmem(infohtml, sizeof(infohtml), W_HTML); }); server.on("/ssids.html", HTTP_GET, []() { sendProgmem(ssidshtml, sizeof(ssidshtml), W_HTML); }); server.on("/attack.html", HTTP_GET, []() { sendProgmem(attackhtml, sizeof(attackhtml), W_HTML); }); server.on("/settings.html", HTTP_GET, []() { sendProgmem(settingshtml, sizeof(settingshtml), W_HTML); }); server.on("/style.css", HTTP_GET, []() { sendProgmem(stylecss, sizeof(stylecss), W_CSS); }); server.on("/js/ssids.js", HTTP_GET, []() { sendProgmem(ssidsjs, sizeof(ssidsjs), W_JS); }); server.on("/js/site.js", HTTP_GET, []() { sendProgmem(sitejs, sizeof(sitejs), W_JS); }); server.on("/js/attack.js", HTTP_GET, []() { sendProgmem(attackjs, sizeof(attackjs), W_JS); }); server.on("/js/scan.js", HTTP_GET, []() { sendProgmem(scanjs, sizeof(scanjs), W_JS); }); server.on("/js/settings.js", HTTP_GET, []() { sendProgmem(settingsjs, sizeof(settingsjs), W_JS); }); server.on("/lang/hu.lang", HTTP_GET, []() { sendProgmem(hulang, sizeof(hulang), W_JSON); }); server.on("/lang/ja.lang", HTTP_GET, []() { sendProgmem(jalang, sizeof(jalang), W_JSON); }); server.on("/lang/nl.lang", HTTP_GET, []() { sendProgmem(nllang, sizeof(nllang), W_JSON); }); server.on("/lang/fi.lang", HTTP_GET, []() { sendProgmem(filang, sizeof(filang), W_JSON); }); server.on("/lang/cn.lang", HTTP_GET, []() { sendProgmem(cnlang, sizeof(cnlang), W_JSON); }); server.on("/lang/ru.lang", HTTP_GET, []() { sendProgmem(rulang, sizeof(rulang), W_JSON); }); server.on("/lang/de.lang", HTTP_GET, []() { sendProgmem(delang, sizeof(delang), W_JSON); }); server.on("/lang/it.lang", HTTP_GET, []() { sendProgmem(itlang, sizeof(itlang), W_JSON); }); server.on("/lang/en.lang", HTTP_GET, []() { sendProgmem(enlang, sizeof(enlang), W_JSON); }); server.on("/lang/fr.lang", HTTP_GET, []() { sendProgmem(frlang, sizeof(frlang), W_JSON); }); server.on("/lang/in.lang", HTTP_GET, []() { sendProgmem(inlang, sizeof(inlang), W_JSON); }); server.on("/lang/ro.lang", HTTP_GET, []() { sendProgmem(rolang, sizeof(rolang), W_JSON); }); server.on("/lang/da.lang", HTTP_GET, []() { sendProgmem(dalang, sizeof(dalang), W_JSON); }); server.on("/lang/ptbr.lang", HTTP_GET, []() { sendProgmem(ptbrlang, sizeof(ptbrlang), W_JSON); }); server.on("/lang/cs.lang", HTTP_GET, []() { sendProgmem(cslang, sizeof(cslang), W_JSON); }); server.on("/lang/tlh.lang", HTTP_GET, []() { sendProgmem(tlhlang, sizeof(tlhlang), W_JSON); }); server.on("/lang/es.lang", HTTP_GET, []() { sendProgmem(eslang, sizeof(eslang), W_JSON); }); server.on("/lang/th.lang", HTTP_GET, []() { sendProgmem(thlang, sizeof(thlang), W_JSON); }); } server.on("/lang/default.lang", HTTP_GET, []() { if (!settings::getWebSettings().use_spiffs) { if (String(settings::getWebSettings().lang) == "hu") sendProgmem(hulang, sizeof(hulang), W_JSON); else if (String(settings::getWebSettings().lang) == "ja") sendProgmem(jalang, sizeof(jalang), W_JSON); else if (String(settings::getWebSettings().lang) == "nl") sendProgmem(nllang, sizeof(nllang), W_JSON); else if (String(settings::getWebSettings().lang) == "fi") sendProgmem(filang, sizeof(filang), W_JSON); else if (String(settings::getWebSettings().lang) == "cn") sendProgmem(cnlang, sizeof(cnlang), W_JSON); else if (String(settings::getWebSettings().lang) == "ru") sendProgmem(rulang, sizeof(rulang), W_JSON); else if (String(settings::getWebSettings().lang) == "de") sendProgmem(delang, sizeof(delang), W_JSON); else if (String(settings::getWebSettings().lang) == "it") sendProgmem(itlang, sizeof(itlang), W_JSON); else if (String(settings::getWebSettings().lang) == "en") sendProgmem(enlang, sizeof(enlang), W_JSON); else if (String(settings::getWebSettings().lang) == "fr") sendProgmem(frlang, sizeof(frlang), W_JSON); else if (String(settings::getWebSettings().lang) == "in") sendProgmem(inlang, sizeof(inlang), W_JSON); else if (String(settings::getWebSettings().lang) == "ro") sendProgmem(rolang, sizeof(rolang), W_JSON); else if (String(settings::getWebSettings().lang) == "da") sendProgmem(dalang, sizeof(dalang), W_JSON); else if (String(settings::getWebSettings().lang) == "ptbr") sendProgmem(ptbrlang, sizeof(ptbrlang), W_JSON); else if (String(settings::getWebSettings().lang) == "cs") sendProgmem(cslang, sizeof(cslang), W_JSON); else if (String(settings::getWebSettings().lang) == "tlh") sendProgmem(tlhlang, sizeof(tlhlang), W_JSON); else if (String(settings::getWebSettings().lang) == "es") sendProgmem(eslang, sizeof(eslang), W_JSON); else if (String(settings::getWebSettings().lang) == "th") sendProgmem(thlang, sizeof(thlang), W_JSON); else handleFileRead("/web/lang/"+String(settings::getWebSettings().lang)+".lang"); } else { handleFileRead("/web/lang/"+String(settings::getWebSettings().lang)+".lang"); } }); // ================================================================ #endif /* ifdef USE_PROGMEM_WEB_FILES */ server.on("/run", HTTP_GET, []() { server.send(200, str(W_TXT), str(W_OK).c_str()); String input = server.arg("cmd"); cli.exec(input); }); server.on("/attack.json", HTTP_GET, []() { server.send(200, str(W_JSON), attack.getStatusJSON()); }); // aggressively caching static assets server.serveStatic("/js", LittleFS, String(String(ap_settings.path) + "/js").c_str(), "max-age=86400"); // called when the url is not defined here // use it to load content from SPIFFS server.onNotFound([]() { if (!handleFileRead(server.uri())) { if (settings::getWebSettings().captive_portal) sendProgmem(indexhtml, sizeof(indexhtml), W_HTML); else server.send(404, str(W_TXT), str(W_FILE_NOT_FOUND)); } }); server.begin(); mode = wifi_mode_t::ap; prntln(W_STARTED_AP); printStatus(); } void stopAP() { if (mode == wifi_mode_t::ap) { wifi_promiscuous_enable(0); WiFi.persistent(false); WiFi.disconnect(true); wifi_set_opmode(STATION_MODE); prntln(W_STOPPED_AP); mode = wifi_mode_t::st; } } void resumeAP() { if (mode != wifi_mode_t::ap) { mode = wifi_mode_t::ap; wifi_promiscuous_enable(0); WiFi.softAPConfig(ip, ip, netmask); WiFi.softAP(ap_settings.ssid, ap_settings.password, ap_settings.channel, ap_settings.hidden); prntln(W_STARTED_AP); } } void update() { if ((mode != wifi_mode_t::off) && !scan.isScanning()) { server.handleClient(); dns.processNextRequest(); } } }