8000
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,21 @@ config WIFI_MANAGER_RETRY_TIMER
help
Defines the time to wait before an attempt to re-connect to a saved wifi is made after connection is lost or another unsuccesful attempt is made.

config WIFI_MANAGER_AUTOSTART_AP
bool "Automatically start the AP if the WiFi manager fails to connect to another AP"
default y

config WIFI_MANAGER_MAX_RETRY_START_AP
int "Max Retry before starting the AP"
int "Max Retry before starting the AP" if WIFI_MANAGER_AUTOSTART_AP
default 3
help
Defines the maximum number of failed retries allowed before the WiFi manager starts its own access point.
Defines the maximum number of failed retries allowed before the WiFi manager starts its own access point.

config WIFI_MANAGER_SHUTDOWN_AP_TIMER
int "Time (in ms) to wait before shutting down the AP"
default 60000
help
Defines the time (in ms) to wait after a succesful connection before shutting down the access point.
Defines the time (in ms) to wait after a succesful connection before shutting down the access point. Use a negative number to never shut down the access point.

config WEBAPP_LOCATION
string "Defines the URL where the wifi manager is located"
Expand All @@ -36,8 +40,18 @@ config DEFAULT_AP_SSID
help
SSID (network name) the the esp32 will broadcast.

config WIFI_MANAGER_APPEND_MAC
bool "Append MAC address to access point SSID"
default n
help
The last two octets of the device's MAC address will be appended (with a preceding space) to the configured access point SSID. For example, "esp32" -> "esp32 ad7e". This helps make the SSID unique if you might have multiple devices on at once.

config USE_RANDOM_AP_PASSWORD
bool "Use a randomly-generated Access Point password"
default y

config DEFAULT_AP_PASSWORD
string "Access Point Password"
string "Access Point Password" if !USE_RANDOM_AP_PASSWORD
default "esp32pwd"
help
Password used for the Access Point. Leave empty and set AUTH MODE to WIFI_AUTH_OPEN for no password.
Expand Down Expand Up @@ -78,4 +92,16 @@ config DEFAULT_AP_BEACON_INTERVAL
help
100ms is the recommended default.

config HARDCODED_SSID
string "Hardcoded SSID"
default "HardcodedSSID"
help
For factory configuration, the device will attempt to connect an an AP with this SSID if there are no user configured APs.

config HARDCODED_PASSWORD
string "Hardcoded password"
default "HardcodedPassword"
help
For factory configuration, the device will attempt to connect an an AP with this password if there are no user configured APs.

endmenu
24 changes: 24 additions & 0 deletions src/code.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,30 @@ docReady(async function () {
false
);

gel("togglepwd").addEventListener(
"click",
(e) => {
if (gel("pwd").type == "password") {
gel("pwd").type = "text";
} else {
gel("pwd").type = "password";
}
},
false
);

gel("manual_togglepwd").addEventListener(
"click",
(e) => {
if (gel("manual_pwd").type == "password") {
gel("manual_pwd").type = "text";
} else {
gel("manual_pwd").type = "password";
}
},
false
);

gel("ok-details").addEventListener(
"click",
() => {
Expand Down
85 changes: 67 additions & 18 deletions src/dns_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Contains the freeRTOS task for the DNS server that processes the requests.
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/event_groups.h>
#include <freertos/semphr.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <esp_event.h>
Expand All @@ -56,18 +57,29 @@ Contains the freeRTOS task for the DNS server that processes the requests.
static const char TAG[] = "dns_server";
static TaskHandle_t task_dns_server = NULL;
int socket_fd;
SemaphoreHandle_t socket_in_use_mutex = NULL;

void dns_server_start() {
if(task_dns_server == NULL){
socket_in_use_mutex = xSemaphoreCreateMutex();
xTaskCreate(&dns_server, "dns_server", 3072, NULL, WIFI_MANAGER_TASK_PRIORITY-1, &task_dns_server);
}
}

void dns_server_stop(){
if(task_dns_server){
vTaskDelete(task_dns_server);
/* If the task is deleted while recvfrom() is waiting, the socket
* will remain in use and will be in zombie state forever. Close the
* socket to cause recvfrom() to abort. */
close(socket_fd);
/* Wait for recvfrom() to return, signified by the mutex being
* released */
xSemaphoreTake(socket_in_use_mutex, pdMS_TO_TICKS(500));
/* Now it is safe to delete the task */
vTaskDelete(task_dns_server);
task_dns_server = NULL;
vSemaphoreDelete(socket_in_use_mutex);
socket_in_use_mutex = NULL;
}

}
Expand Down Expand Up @@ -114,14 +126,18 @@ void dns_server(void *pvParameters) {
char ip_address[INET_ADDRSTRLEN]; /* buffer to store IPs as text. This is only used for debug and serves no other purpose */
char *domain; /* This is only used for debug and serves no other purpose */
int err;
bool valid_request;
bool include_answer;

ESP_LOGI(TAG, "DNS Server listening on 53/udp");

/* Start loop to process DNS requests */
for(;;) {

memset(data, 0x00, sizeof(data)); /* reset buffer */
xSemaphoreTake(socket_in_use_mutex, portMAX_DELAY);
length = recvfrom(socket_fd, data, sizeof(data), 0, (struct sockaddr *)&client, &client_len); /* read udp request */
xSemaphoreGive(socket_in_use_mutex);

/*if the query is bigger than the buffer size we simply ignore it. This case should only happen in case of multiple
* queries within the same DNS packet and is not supported by this simple DNS hijack. */
Expand Down Expand Up @@ -149,25 +165,58 @@ void dns_server(void *pvParameters) {

/* extract domain name and request IP for debug */
inet_ntop(AF_INET, &(client.sin_addr), ip_address, INET_ADDRSTRLEN);
domain = (char*) &data[sizeof(dns_header_t) + 1];
for(char* c=domain; *c != '\0'; c++){
if(*c < ' ' || *c > 'z') *c = '.'; /* technically we should test if the first two bits are 00 (e.g. if( (*c & 0xC0) == 0x00) *c = '.') but this makes the code a lot more readable */
/* each label is preceded by a length octet, step over each label and replace
* the length octet with a '.' character to make the domain name easier to read */
valid_request = true;
include_answer = false;
uint8_t *current_octet = &(data[sizeof(dns_header_t)]);
uint8_t label_length = *current_octet;
while(label_length != 0){
current_octet += label_length + 1;
if((current_octet - data) >= length){
/* uh oh, buffer overflow */
valid_request = false;
break;
}
label_length = *current_octet;
if(label_length != 0) *current_octet = '.';
}
domain = (char*)&data[sizeof(dns_header_t) + 1];

/* check type of request */
current_octet++;
if((current_octet - data + 4) <= length){
uint16_t query_type = ((uint16_t)current_octet[0] << 8UL) | current_octet[1];
current_octet += 2;
uint16_t query_class = ((uint16_t)current_octet[0] << 8UL) | current_octet[1];
if((query_type == DNS_ANSWER_TYPE_A) && (query_class == DNS_ANSWER_CLASS_IN)){
/* only answer requests for A records */
include_answer = true;
ESP_LOGI(TAG, "Replying to DNS request for %s from %s", domain, ip_address);
}
}else{
valid_request = false;
}
ESP_LOGI(TAG, "Replying to DNS request for %s from %s", domain, ip_address);


/* create DNS answer at the end of the query*/
dns_answer_t *dns_answer = (dns_answer_t*)&response[length];
dns_answer->NAME = __bswap_16(0xC00C); /* This is a pointer to the beginning of the question. As per DNS standard, first two bits must be set to 11 for some odd reason hence 0xC0 */
dns_answer->TYPE = __bswap_16(DNS_ANSWER_TYPE_A);
dns_answer->CLASS = __bswap_16(DNS_ANSWER_CLASS_IN);
dns_answer->TTL = (uint32_t)0x00000000; /* no caching. Avoids DNS poisoning since this is a DNS hijack */
dn 292D s_answer->RDLENGTH = __bswap_16(0x0004); /* 4 byte => size of an ipv4 address */
dns_answer->RDATA = ip_resolved.addr;

err = sendto(socket_fd, response, length+sizeof(dns_answer_t), 0, (struct sockaddr *)&client, client_len);
if (err < 0) {
ESP_LOGE(TAG, "UDP sendto failed: %d", err);
if(valid_request){
if(include_answer){
/* create DNS answer at the end of the query*/
dns_answer_t *dns_answer = (dns_answer_t*)&response[length];
dns_answer->NAME = __bswap_16(0xC00C); /* This is a pointer to the beginning of the question. As per DNS standard, first two bits must be set to 11 for some odd reason hence 0xC0 */
dns_answer->TYPE = __bswap_16(DNS_ANSWER_TYPE_A);
dns_answer->CLASS = __bswap_16(DNS_ANSWER_CLASS_IN);
dns_answer->TTL = __bswap_32(0x00000005); /* set TTL to something small but not 0 to placate windows */
dns_answer->RDLENGTH = __bswap_16(0x0004); /* 4 byte => size of an ipv4 address */
dns_answer->RDATA = ip_resolved.addr;

err = sendto(socket_fd, response, length+sizeof(dns_answer_t), 0, (struct sockaddr *)&client, client_len);
}else{
dns_header->ANCount = 0x0000;
err = sendto(socket_fd, response, length, 0, (struct sockaddr *)&client, client_len);
}
if (err < 0) {
ESP_LOGE(TAG, "UDP sendto failed: %d", err);
}
}
}

Expand Down
7 changes: 2 additions & 5 deletions src/http_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,9 @@ static esp_err_t http_server_post_handler(httpd_req_t *req){
httpd_req_get_hdr_value_str(req, "X-Custom-ssid", ssid, ssid_len+1);
httpd_req_get_hdr_value_str(req, "X-Custom-pwd", password, password_len+1);

wifi_config_t* config = wifi_manager_get_wifi_sta_config();
memset(config, 0x00, sizeof(wifi_config_t));
memcpy(config->sta.ssid, ssid, ssid_len);
memcpy(config->sta.password, password, password_len);
ESP_LOGI(TAG, "ssid: %s, password: %s", ssid, password);
ESP_LOGD(TAG, "http_server_post_handler: wifi_manager_connect_async() call");
wifi_manager_connect_async();
wifi_manager_connect_async(true, ssid, password);

/* free memory */
free(ssid);
Expand Down Expand Up @@ -427,6 +423,7 @@ void http_app_start(bool lru_purge_enable){
* We could register all URLs one by one, but this would not work while the fake DNS is active */
config.uri_match_fn = httpd_uri_match_wildcard;
config.lru_purge_enable = lru_purge_enable;
config.max_open_sockets = 2;

/* generate the URLs */
if(http_root_url == NULL){
Expand Down
2 changes: 2 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ <h2>Manual Connection</span></h2>
<input id="manual_pwd" type="password" placeholder="Password" value="">
</section>
<div class="buttons">
<input id="manual_togglepwd" class="big-button" type="button" value="Show password" /><br>
<input id="manual_join" type="button" value="Join" data-connect="manual" />
<input id="manual_cancel" type="button" value="Cancel"/>
</div>
Expand All @@ -53,6 +54,7 @@ <h2>Password for <span id="ssid-pwd"></span></h2>
<input id="pwd" type="password" placeholder="Password" value="">
</section>
<div class="buttons">
<input id="togglepwd" class="big-button" type="button" value="Show password" /><br>
<input id="join" type="button" value="Join" />
<input id="cancel" type="button" value="Cancel"/>
</div>
Expand Down
5 changes: 5 additions & 0 deletions src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ input[type="button"] {
text-align: center;
display: block;
}
input[type="button"].big-button {
width: 90%;
max-width: 300px;
margin: auto;
}
p {
padding: 10px;
}
Expand Down
Loading
0