esp8266_deauther/esp8266_deauther/wifi.cpp

451 lines
16 KiB
C++

/* This software is licensed under the MIT License: https://github.com/spacehuhntech/esp8266_deauther */
#include "wifi.h"
extern "C" {
#include "user_interface.h"
}
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <DNSServer.h>
#include <ESP8266mDNS.h>
#include <LittleFS.h>
#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();
}
}
}