ESP32 as stanalone Webserver

dskw87
Posts: 2
Joined: Mon Feb 13, 2017 6:56 am

Re: ESP32 as stanalone Webserver

Postby dskw87 » Mon Feb 13, 2017 6:59 am

Cornelis wrote:I add this to my code, and after sum small changes, due to differences between Esp8266 and Esp32, this code Runs.

After Booting the AP comes up, and the captdns service is started.

Then when I connect to the AP, the Mobil device automatically send a request and
the captdns service tries to response on this.

But it ends with a Stack overflow.

After some debugging , it seems that the problem happens within

ret=recvfrom(sockFd, (u8 *)udp_msg, DNS_LEN, 0,(struct sockaddr *)&from,(socklen_t *)&fromlen);

This is a ESP8266 fuction which also exsist within the latest SDK of Espressif.

Does someone has a idea?

Thanks Cornelis
I'm also interested in your port of the dns code. I tried porting it myself with my limited knowledge of C and the current esp-idf branch, but it complains about a thousand things in that snippet.

andrew_p
Posts: 30
Joined: Sun Jan 01, 2017 5:37 pm

Re: ESP32 as stanalone Webserver

Postby andrew_p » Tue Feb 14, 2017 12:28 am

It appears multiple people looking for a captive portal for ESP32... if someone can create and share a good working example of a captive portal with the following features:
- fully compatible with/ built for the current esp_edf ESP32 release (FreeRTOS)
- image files, css
- ability to enter data in a field and store it in NVS

that would be really great... in fact, I would like to provide $25 gift card (Amazon or Starbucks) to the first one, who shares it on github :P :geek:

MalteJ
Posts: 62
Joined: Wed Sep 21, 2016 10:26 pm

Re: ESP32 as stanalone Webserver

Postby MalteJ » Tue Feb 14, 2017 7:24 pm

Be careful! Without encryption you are going to send your WiFi password in plain text over the air!

Malte

andrew_p
Posts: 30
Joined: Sun Jan 01, 2017 5:37 pm

Re: ESP32 as stanalone Webserver

Postby andrew_p » Tue Feb 14, 2017 7:41 pm

Good point about encryption, so we're looking for the following features of a captive portal:

- encrypted connection, HTTPS
- fully compatible with/ built for the current esp_edf ESP32 release (FreeRTOS)
- image files, css
- ability to enter data in a field and store it in NVS

dskw87
Posts: 2
Joined: Mon Feb 13, 2017 6:56 am

Re: ESP32 as stanalone Webserver

Postby dskw87 » Wed Feb 15, 2017 10:11 am

Since Cornelis had some trouble getting the DNS code running himself I would be glad to just have a look at his porting results.
A more advanced version as described by andrew would be nice of course, but I would rather have something short and simple for now :)

Cornelis
Posts: 9
Joined: Wed Feb 08, 2017 7:21 pm

Re: ESP32 as stanalone Webserver

Postby Cornelis » Wed Feb 15, 2017 1:04 pm

Hi to all,

here is the code from "jeroen Domburg" which I downloaded from the page "WiFive" posted.

I did some changes, so it can be compiled with "esp-idf-v2.0-rc1",
it could be that it still has some bugs because if I use it
it will end with a Stack overflow.

Here the code: of captions.c

/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/


/*
This is a 'captive portal' DNS server: it basically replies with a fixed IP (in this case:
the one of the SoftAP interface of this ESP module) for any and all DNS queries. This can
be used to send mobile phones, tablets etc which connect to the ESP in AP mode directly to
the internal webserver.
*/

//#include <esp8266.h>

#ifndef ESP32
#define ESP32
#endif

#ifndef FREERTOS
#define FREERTOS
#endif

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "lwip/sockets.h"
#include "lwip/err.h"
#include "tcpip_adapter.h"
#include "string.h"

static int sockFd;

#define DNS_LEN 512

typedef struct __attribute__ ((packed)) {
uint16_t id;
uint8_t flags;
uint8_t rcode;
uint16_t qdcount;
uint16_t ancount;
uint16_t nscount;
uint16_t arcount;
} DnsHeader;


typedef struct __attribute__ ((packed)) {
uint8_t len;
uint8_t data;
} DnsLabel;


typedef struct __attribute__ ((packed)) {
//before: label
uint16_t type;
uint16_t class;
} DnsQuestionFooter;


typedef struct __attribute__ ((packed)) {
//before: label
uint16_t type;
uint16_t class;
uint32_t ttl;
uint16_t rdlength;
//after: rdata
} DnsResourceFooter;

typedef struct __attribute__ ((packed)) {
uint16_t prio;
uint16_t weight;
} DnsUriHdr;


#define FLAG_QR (1<<7)
#define FLAG_AA (1<<2)
#define FLAG_TC (1<<1)
#define FLAG_RD (1<<0)

#define QTYPE_A 1
#define QTYPE_NS 2
#define QTYPE_CNAME 5
#define QTYPE_SOA 6
#define QTYPE_WKS 11
#define QTYPE_PTR 12
#define QTYPE_HINFO 13
#define QTYPE_MINFO 14
#define QTYPE_MX 15
#define QTYPE_TXT 16
#define QTYPE_URI 256

#define QCLASS_IN 1
#define QCLASS_ANY 255
#define QCLASS_URI 256


//Function to put unaligned 16-bit network values
static void setn16(void *pp, int16_t n) {
char *p=pp;
*p++=(n>>8);
*p++=(n&0xff);
}

//Function to put unaligned 32-bit network values
static void setn32(void *pp, int32_t n) {
char *p=pp;
*p++=(n>>24)&0xff;
*p++=(n>>16)&0xff;
*p++=(n>>8)&0xff;
*p++=(n&0xff);
}

static uint16_t my_ntohs(uint16_t *in) {
char *p=(char*)in;
return ((p[0]<<8)&0xff00)|(p[1]&0xff);
}

//Parses a label into a C-string containing a dotted
//Returns pointer to start of next fields in packet
static char* labelToStr(char *packet, char *labelPtr, int packetSz, char *res, int resMaxLen) {
int i, j, k;
char *endPtr=NULL;
i=0;
do {
if ((*labelPtr&0xC0)==0) {
j=*labelPtr++; //skip past length
//Add separator period if there already is data in res
if (i<resMaxLen && i!=0) res[i++]='.';
//Copy label to res
for (k=0; k<j; k++) {
if ((labelPtr-packet)>packetSz) return NULL;
if (i<resMaxLen) res[i++]=*labelPtr++;
}
} else if ((*labelPtr&0xC0)==0xC0) {
//Compressed label pointer
endPtr=labelPtr+2;
int offset=my_ntohs(((uint16_t *)labelPtr))&0x3FFF;
//Check if offset points to somewhere outside of the packet
if (offset>packetSz) return NULL;
labelPtr=&packet[offset];
}
//check for out-of-bound-ness
if ((labelPtr-packet)>packetSz) return NULL;
} while (*labelPtr!=0);
res=0; //zero-terminate
if (endPtr==NULL) endPtr=labelPtr+1;
return endPtr;
}

//Converts a dotted hostname to the weird label form dns uses.
static char *strToLabel(char *str, char *label, int maxLen) {
char *len=label; //ptr to len byte
char *p=label+1; //ptr to next label byte to be written
while (1) {
if (*str=='.' || *str==0) {
*len=((p-len)-1); //write len of label bit
len=p; //pos of len for next part
p++; //data ptr is one past len
if (*str==0) break; //done
str++;
} else {
*p++=*str++; //copy byte
//if ((p-label)>maxLen) return NULL; //check out of bounds
}
}
*len=0;
return p; //ptr to first free byte in resp
}

//Receive a DNS packet and maybe send a response back
static void captdnsRecv(struct sockaddr_in *premote_addr, char *pusrdata, unsigned short length) {

char buff[DNS_LEN];
char reply[DNS_LEN];
int i;
char *rend=&reply[length];
char *p=pusrdata;
DnsHeader *hdr=(DnsHeader*)p;
DnsHeader *rhdr=(DnsHeader*)&reply[0];
p+=sizeof(DnsHeader);
// printf("DNS packet: id 0x%X flags 0x%X rcode 0x%X qcnt %d ancnt %d nscount %d arcount %d len %d\n",
// my_ntohs(&hdr->id), hdr->flags, hdr->rcode, my_ntohs(&hdr->qdcount), my_ntohs(&hdr->ancount), my_ntohs(&hdr->nscount), my_ntohs(&hdr->arcount), length);
//Some sanity checks:
if (length>DNS_LEN) return; //Packet is longer than DNS implementation allows
if (length<sizeof(DnsHeader)) return; //Packet is too short
if (hdr->ancount || hdr->nscount || hdr->arcount) return; //this is a reply, don't know what to do with it
if (hdr->flags&FLAG_TC) return; //truncated, can't use this
//Reply is basically the request plus the needed data
memcpy(reply, pusrdata, length);
rhdr->flags|=FLAG_QR;

for (i=0; i<my_ntohs(&hdr->qdcount); i++)
{
//Grab the labels in the q string
p=labelToStr(pusrdata, p, length, buff, sizeof(buff));
if (p==NULL) return;
DnsQuestionFooter *qf=(DnsQuestionFooter*)p;
p+=sizeof(DnsQuestionFooter);

printf("DNS: Q (type 0x%X class 0x%X) for %s\n", my_ntohs(&qf->type), my_ntohs(&qf->class), buff);


if (my_ntohs(&qf->type)==QTYPE_A) {
//They want to know the IPv4 address of something.
//Build the response.

rend=strToLabel(buff, rend, sizeof(reply)-(rend-reply)); //Add the label
if (rend==NULL) return;
DnsResourceFooter *rf=(DnsResourceFooter *)rend;
rend+=sizeof(DnsResourceFooter);
setn16(&rf->type, QTYPE_A);
setn16(&rf->class, QCLASS_IN);
setn32(&rf->ttl, 0);
setn16(&rf->rdlength, 4); //IPv4 addr is 4 bytes;
//Grab the current IP of the softap interface

tcpip_adapter_ip_info_t info;
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &info);
//struct ip_info info;
//wifi_get_ip_info(SOFTAP_IF, &info);
*rend++=ip4_addr1(&info.ip);
*rend++=ip4_addr2(&info.ip);
*rend++=ip4_addr3(&info.ip);
*rend++=ip4_addr4(&info.ip);
setn16(&rhdr->ancount, my_ntohs(&rhdr->ancount)+1);
//printf("Added A rec to resp. Resp len is %d\n", (rend-reply));

} else if (my_ntohs(&qf->type)==QTYPE_NS) {
//Give ns server. Basically can be whatever we want because it'll get resolved to our IP later anyway.
rend=strToLabel(buff, rend, sizeof(reply)-(rend-reply)); //Add the label
DnsResourceFooter *rf=(DnsResourceFooter *)rend;
rend+=sizeof(DnsResourceFooter);
setn16(&rf->type, QTYPE_NS);
setn16(&rf->class, QCLASS_IN);
setn16(&rf->ttl, 0);
setn16(&rf->rdlength, 4);
*rend++=2;
*rend++='n';
*rend++='s';
*rend++=0;
setn16(&rhdr->ancount, my_ntohs(&rhdr->ancount)+1);
//printf("Added NS rec to resp. Resp len is %d\n", (rend-reply));
} else if (my_ntohs(&qf->type)==QTYPE_URI) {
//Give uri to us
rend=strToLabel(buff, rend, sizeof(reply)-(rend-reply)); //Add the label
DnsResourceFooter *rf=(DnsResourceFooter *)rend;
rend+=sizeof(DnsResourceFooter);
DnsUriHdr *uh=(DnsUriHdr *)rend;
rend+=sizeof(DnsUriHdr);
setn16(&rf->type, QTYPE_URI);
setn16(&rf->class, QCLASS_URI);
setn16(&rf->ttl, 0);
setn16(&rf->rdlength, 4+16);
setn16(&uh->prio, 10);
setn16(&uh->weight, 1);
memcpy(rend, "http://esp.nonet", 16);
rend+=16;
setn16(&rhdr->ancount, my_ntohs(&rhdr->ancount)+1);
//printf("Added NS rec to resp. Resp len is %d\n", (rend-reply));
}
}
//Send the response
sendto(sockFd,(uint8_t*)reply, rend-reply, 0, (struct sockaddr *)premote_addr, sizeof(struct sockaddr_in));

}

static void captdnsTask(void *pvParameters) {
struct sockaddr_in server_addr;
uint32_t ret;
struct sockaddr_in from;
socklen_t fromlen;
//struct tcpip_adapter_ip_info_t ipconfig;
char udp_msg[DNS_LEN];

//memset(&ipconfig, 0, sizeof(ipconfig));
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(53);
server_addr.sin_len = sizeof(server_addr);

do {
sockFd=socket(AF_INET, SOCK_DGRAM, 0);
if (sockFd==-1) {
printf("captdns_task failed to create sock!\n");
vTaskDelay(1000/portTICK_RATE_MS);
}
} while (sockFd==-1);

do {
ret=bind(sockFd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret!=0) {
printf("captdns_task failed to bind sock!\n");
vTaskDelay(1000/portTICK_RATE_MS);
}
} while (ret!=0);

printf("CaptDNS inited.\n");

while(1) {
memset(&udp_msg, 0, sizeof(udp_msg));
fromlen=sizeof(struct sockaddr_in);
memset(&from, 0, sizeof(from));
printf("Waiting for Request\n");
ret=recvfrom(sockFd, (uint8_t *)udp_msg, DNS_LEN, 0,(struct sockaddr *)&from,(socklen_t *)&fromlen);
if (ret>0)
{
captdnsRecv(&from,udp_msg,ret);
}
}

close(sockFd);
vTaskDelete(NULL);
}

void captdnsInit(void)
{
xTaskCreate(captdnsTask, (const char *)"captdns_task", 1200, NULL, 3, NULL);
}


This code put in captions.h

#ifndef CAPTDNS_H
#define CAPTDNS_H
void captdnsInit(void);

#endif

And put both files in your directory to be compiled in my case
esp/myapp/main/*

But again I am still struggling with the Stackoverflow

Cornelis
Posts: 9
Joined: Wed Feb 08, 2017 7:21 pm

Re: ESP32 as stanalone Webserver

Postby Cornelis » Wed Feb 15, 2017 1:13 pm

Sorry,

stupid auto correction the files are named captdns.c and captdns.h

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: ESP32 as stanalone Webserver

Postby WiFive » Wed Feb 15, 2017 1:17 pm

Try changing

Code: Select all

xTaskCreate(captdnsTask, (const char *)"captdns_task", 1200, NULL, 3, NULL);
To

Code: Select all

xTaskCreate(captdnsTask, (const char *)"captdns_task", 2048, NULL, 3, NULL);

Cornelis
Posts: 9
Joined: Wed Feb 08, 2017 7:21 pm

Re: ESP32 as stanalone Webserver

Postby Cornelis » Wed Feb 15, 2017 1:47 pm

Thanks to Kolban now it starts running.

Leaving...
Hard resetting...
--- forcing DTR inactive
--- forcing RTS inactive
--- Miniterm on /dev/tty.usbserial-DN02BB9V 115200,8,N,1 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
ets Jun 8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
ets Jun 8 2016 00:22:57

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0x00
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0008,len:8
load:0x3fff0010,len:2388
load:0x40078000,len:6788
load:0x40080000,len:252
entry 0x40080034
I (771) heap_alloc_caps: Initializing. RAM available for dynamic allocation:
I (771) heap_alloc_caps: At 3FFB6CF0 len 00029310 (164 KiB): DRAM
I (781) heap_alloc_caps: At 3FFE8000 len 00018000 (96 KiB): D/IRAM
I (802) heap_alloc_caps: At 4009C634 len 000039CC (14 KiB): IRAM
I (822) cpu_start: Pro cpu up.
I (834) cpu_start: Single core mode
I (847) cpu_start: Pro cpu start user code
I (1439) phy: phy_version: 258, Nov 29 2016, 15:51:07, 1, 0
I (2550) cpu_start: Starting scheduler on PRO CPU.
I (2558) wifi: frc2_timer_task_hdl:3ffbc628, prio:22, stack:2048
I (2562) wifi: Init lldesc rx mblock:25
I (2562) wifi: Init lldesc rx ampdu len mblock:7
I (2565) wifi: Init lldesc rx ampdu entry mblock:4
I (2570) wifi: pp_task_hdl : 3ffc94f0, prio:23, stack:8192
E (2575) wifi: wifi_set_chan_range 1489 ret=1
I (2580) wifi: mode : softAP (24:0a:c4:00:c6:83)
Event = 000C CaptDNS inited.
Waiting for Request
I (1433133) wifi: n:4 0, o:4 0, ap:4 1, sta:255 255, prof:4
I (1433133) wifi: station: b8:53:ac:b6:64:49 join, AID=1, g, 20
Event = 000E DNS: Q (type 0x1 class 0x1) for apple.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for www.icloud.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for guzzoni.apple.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for iphone-ld.apple.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for www.apple.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for cl3.apple.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for iphone-ld.apple.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for mesu.apple.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for p29-ckdatabase.icloud.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for www1.sedecatastro.gob.es
Waiting for Request
DNS: Q (type 0x1 class 0x1) for dmp.theadex.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for static.chartbeat.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for static.plista.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for script.ioam.de
Waiting for Request
DNS: Q (type 0x1 class 0x1) for quick-yen-88111.appspot.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for farm.plista.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for www.google.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for www.googletagservices.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for stats.g.doubleclick.net
Waiting for Request
DNS: Q (type 0x1 class 0x1) for mab.chartbeat.com
Waiting for Request
DNS: Q (type 0x1 class 0x1) for pagead2.googlesyndication.com
Waiting for Request

Now I can go on , I have to bring this thing that it sends back a IP Adress to go to.

:) :)

Cornelis

Cornelis
Posts: 9
Joined: Wed Feb 08, 2017 7:21 pm

Re: ESP32 as stanalone Webserver

Postby Cornelis » Wed Feb 15, 2017 1:53 pm

To All,

small question I put in my main.c following

ip_addr_t dns_addr;
IP_ADDR4(&dns_addr, 192,168,4,100);
dns_setserver(0, &dns_addr);
dns_init();

What does not fail,
but I still don't get 192.168.4.99 from the ESP32 DHCP service I
still get 192.168.4.1

Does anyone has a Idea where exact to put this code?

Cornelis

Who is online

Users browsing this forum: MicroController and 120 guests