DNS resolver for captive portal
This commit is contained in:
+207
@@ -0,0 +1,207 @@
|
||||
#include <esp8266.h>
|
||||
|
||||
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 (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=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[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 (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
|
||||
os_memcpy(reply, pusrdata, length);
|
||||
rhdr->flags|=FLAG_QR;
|
||||
for (i=0; i<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);
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#ifndef CAPTDNS_H
|
||||
#define CAPTDNS_H
|
||||
void ICACHE_FLASH_ATTR captdnsInit(void);
|
||||
|
||||
#endif
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user