| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 | 
							- /*
 
- Copyright (c) 2019 Tony Pottier
 
- Permission is hereby granted, free of charge, to any person obtaining a copy
 
- of this software and associated documentation files (the "Software"), to deal
 
- in the Software without restriction, including without limitation the rights
 
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
- copies of the Software, and to permit persons to whom the Software is
 
- furnished to do so, subject to the following conditions:
 
- The above copyright notice and this permission notice shall be included in all
 
- copies or substantial portions of the Software.
 
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
- SOFTWARE.
 
- @file dns_server.c
 
- @author Tony Pottier
 
- @brief Defines an extremely basic DNS server for captive portal functionality.
 
- It's basically a DNS hijack that replies to the esp's address no matter which
 
- request is sent to it.
 
- Contains the freeRTOS task for the DNS server that processes the requests.
 
- @see https://idyl.io
 
- @see https://github.com/tonyp7/esp32-wifi-manager
 
- */
 
- #include "dns_server.h"
 
- #include <lwip/sockets.h>
 
- #include <string.h>
 
- #include <freertos/FreeRTOS.h>
 
- #include <freertos/task.h>
 
- #include <freertos/event_groups.h>
 
- #include <esp_system.h>
 
- #include <esp_wifi.h>
 
- #include <esp_event_loop.h>
 
- #include <esp_log.h>
 
- #include <esp_err.h>
 
- #include <nvs_flash.h>
 
- #include <lwip/err.h>
 
- #include <lwip/sockets.h>
 
- #include <lwip/sys.h>
 
- #include <lwip/netdb.h>
 
- #include <lwip/dns.h>
 
- #include <byteswap.h>
 
- #include "wifi_manager.h"
 
- static const char TAG[] = "dns_server";
 
- static TaskHandle_t task_dns_server = NULL;
 
- int socket_fd;
 
- void CODE_RAM_LOCATION dns_server_start() {
 
-     xTaskCreate(&dns_server, "dns_server", 3072, NULL, WIFI_MANAGER_TASK_PRIORITY-1, &task_dns_server);
 
- }
 
- void CODE_RAM_LOCATION dns_server_stop(){
 
- 	if(task_dns_server){
 
- 		vTaskDelete(task_dns_server);
 
- 		close(socket_fd);
 
- 		task_dns_server = NULL;
 
- 	}
 
- }
 
- void CODE_RAM_LOCATION dns_server(void *pvParameters) {
 
-     struct sockaddr_in sa, ra;
 
-     /* Set redirection DNS hijack to the access point IP */
 
-     ip4_addr_t ip_resolved;
 
-     inet_pton(AF_INET, DEFAULT_AP_IP, &ip_resolved);
 
-     /* Create UDP socket */
 
-     socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
 
-     if (socket_fd < 0){
 
-         ESP_LOGE(TAG, "Failed to create socket");
 
-         exit(0);
 
-     }
 
-     memset(&sa, 0, sizeof(struct sockaddr_in));
 
-     /* Bind to port 53 (typical DNS Server port) */
 
-     tcpip_adapter_ip_info_t ip;
 
-     tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip);
 
-     ra.sin_family = AF_INET;
 
-     ra.sin_addr.s_addr = ip.ip.addr;
 
-     ra.sin_port = htons(53);
 
-     if (bind(socket_fd, (struct sockaddr *)&ra, sizeof(struct sockaddr_in)) == -1) {
 
-         ESP_LOGE(TAG, "Failed to bind to 53/udp");
 
-         close(socket_fd);
 
-         exit(1);
 
-     }
 
-     struct sockaddr_in client;
 
-     socklen_t client_len;
 
-     client_len = sizeof(client);
 
-     int length;
 
-     uint8_t data[DNS_QUERY_MAX_SIZE];	/* dns query buffer */
 
-     uint8_t response[DNS_ANSWER_MAX_SIZE]; /* dns response buffer */
 
-     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;
 
-     ESP_LOGI(TAG, "DNS Server listening on 53/udp");
 
-     /* Start loop to process DNS requests */
 
-     for(;;) {
 
-     	memset(data, 0x00,  sizeof(data)); /* reset buffer */
 
-         length = recvfrom(socket_fd, data, sizeof(data), 0, (struct sockaddr *)&client, &client_len); /* read udp request */
 
-         /*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. */
 
-         if ( length > 0   &&  ((length + sizeof(dns_answer_t)-1) < DNS_ANSWER_MAX_SIZE)   ) {
 
-         	data[length] = '\0'; /*in case there's a bogus domain name that isn't null terminated */
 
-             /* Generate header message */
 
-             memcpy(response, data, sizeof(dns_header_t));
 
-             dns_header_t *dns_header = (dns_header_t*)response;
 
-             dns_header->QR = 1; /*response bit */
 
-             dns_header->OPCode  = DNS_OPCODE_QUERY; /* no support for other type of response */
 
-             dns_header->AA = 1; /*authoritative answer */
 
-             dns_header->RCode = DNS_REPLY_CODE_NO_ERROR; /* no error */
 
-             dns_header->TC = 0; /*no truncation */
 
-             dns_header->RD = 0; /*no recursion */
 
-             dns_header->ANCount = dns_header->QDCount; /* set answer count = question count -- duhh! */
 
-             dns_header->NSCount = 0x0000; /* name server resource records = 0 */
 
-             dns_header->ARCount = 0x0000; /* resource records = 0 */
 
-             /* copy the rest of the query in the response */
 
-             memcpy(response + sizeof(dns_header_t), data + sizeof(dns_header_t), length - sizeof(dns_header_t));
 
-             /* 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 */
 
-             }
 
-             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 */
 
-             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);
 
-             if (err < 0) {
 
-             	ESP_LOGE(TAG, "UDP sendto failed: %d", err);
 
-             }
 
-         }
 
-         taskYIELD(); /* allows the freeRTOS scheduler to take over if needed. DNS daemon should not be taxing on the system */
 
-     }
 
-     close(socket_fd);
 
-     vTaskDelete ( NULL );
 
- }
 
 
  |