From dec1137abc5d866b5d5be9c3ec0503426d28b878 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 8 Jun 2015 17:50:29 +0800 Subject: [PATCH] DNS resolver for captive portal --- user/captdns.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++ user/captdns.h | 5 ++ user/user_main.c | 3 + 3 files changed, 215 insertions(+) create mode 100644 user/captdns.c create mode 100644 user/captdns.h diff --git a/user/captdns.c b/user/captdns.c new file mode 100644 index 0000000..2865e19 --- /dev/null +++ b/user/captdns.c @@ -0,0 +1,207 @@ +#include + +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; + + + + +#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 QCLASS_IN 1 +#define QCLASS_ANY 255 + + +//Function to put unaligned 16-bit network values +static void ICACHE_FLASH_ATTR 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 ICACHE_FLASH_ATTR 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 ntohs(uint16_t *in) { + char *p=(char*)in; + return ((p[0]<<8)&0xff00)|(p[1]&0xff); +} + + +//Parses a label. +//Returns pointer to start of next fields in packet +static char* ICACHE_FLASH_ATTR 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 (ipacketSz) return NULL; + if (ipacketSz) return NULL; + labelPtr=&packet[offset]; + } + //check for out-of-bound-ness + if ((labelPtr-packet)>packetSz) return NULL; + } while (*labelPtr!=0); + res[i]=0; //zero-terminate + if (endPtr==NULL) endPtr=labelPtr+1; + return endPtr; +} + + +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 +} + + + +static void ICACHE_FLASH_ATTR captdnsRecv(void* arg, char *pusrdata, unsigned short length) { + struct espconn *conn=(struct espconn *)arg; + char buff[512]; + char reply[512]; + int i; + char *rend=&reply[length]; + char *p=pusrdata; + DnsHeader *hdr=(DnsHeader*)p; + DnsHeader *rhdr=(DnsHeader*)&reply[0]; + p+=sizeof(DnsHeader); + os_printf("DNS packet: id 0x%X flags 0x%X rcode 0x%X qcnt %d ancnt %d nscount %d arcount %d len %d\n", + ntohs(&hdr->id), hdr->flags, hdr->rcode, ntohs(&hdr->qdcount), ntohs(&hdr->ancount), ntohs(&hdr->nscount), ntohs(&hdr->arcount), length); + //Some sanity checks: + if (length>512) return; //Packet is longer than DNS implementation allows + if (lengthancount || 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 + os_memcpy(reply, pusrdata, length); + rhdr->flags|=FLAG_QR; + for (i=0; iqdcount); 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); + os_printf("Q %d (type 0x%X class 0x%X) for %s\n", i, ntohs(&qf->type), ntohs(&qf->class), buff); + if (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, 1); + setn16(&rf->rdlength, 4); //IPv4 addr is 4 bytes; + *rend++=192; //hardcoded :X + *rend++=168; + *rend++=4; + *rend++=1; + setn16(&rhdr->ancount, ntohs(&rhdr->ancount)+1); + os_printf("Added A rec to resp. Resp len is %d\n", (rend-reply)); + } else if (ntohs(&qf->type)==QTYPE_NS) { + //Give ns server. Basically is whatever. + 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, 1); + setn16(&rf->rdlength, 4); + *rend++=2; + *rend++='n'; + *rend++='s'; + *rend++=0; + setn16(&rhdr->ancount, ntohs(&rhdr->ancount)+1); + os_printf("Added NS rec to resp. Resp len is %d\n", (rend-reply)); + } + } + espconn_sent(conn, (uint8*)reply, rend-reply); +} + +void ICACHE_FLASH_ATTR captdnsInit(void) { + static struct espconn conn; + static esp_udp udpconn; + conn.type=ESPCONN_UDP; + conn.proto.udp=&udpconn; + conn.proto.udp->local_port = 53; + espconn_regist_recvcb(&conn, captdnsRecv); + espconn_create(&conn); +} diff --git a/user/captdns.h b/user/captdns.h new file mode 100644 index 0000000..ba17ebd --- /dev/null +++ b/user/captdns.h @@ -0,0 +1,5 @@ +#ifndef CAPTDNS_H +#define CAPTDNS_H +void ICACHE_FLASH_ATTR captdnsInit(void); + +#endif \ No newline at end of file diff --git a/user/user_main.c b/user/user_main.c index f82ba75..0940bfa 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -20,6 +20,7 @@ #include "stdout.h" #include "auth.h" #include "espfs.h" +#include "captdns.h" //#define SHOW_HEAP_USE @@ -89,6 +90,8 @@ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) { void user_init(void) { stdoutInit(); ioInit(); + captdnsInit(); + // 0x40200000 is the base address for spi flash memory mapping, ESPFS_POS is the position // where image is written in flash that is defined in Makefile.