Skip to content

Commit 89be820

Browse files
ArHTaosflyingcys
authored andcommitted
add http mqtt
1 parent 1834c0d commit 89be820

File tree

22 files changed

+1410
-91
lines changed

22 files changed

+1410
-91
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include <WiFi.h>
2+
#include <DNSServer.h>
3+
4+
const byte DNS_PORT = 53;
5+
IPAddress apIP(8,8,4,4); // The default android DNS
6+
DNSServer dnsServer;
7+
WiFiServer server;
8+
9+
String responseHTML = ""
10+
"<!DOCTYPE html><html><head><title>CaptivePortal</title></head><body>"
11+
"<h1>Hello World!</h1><p>This is a captive portal example. All requests will "
12+
"be redirected here.</p></body></html>";
13+
14+
void setup() {
15+
WiFi.mode(WIFI_AP);
16+
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
17+
WiFi.softAP("your_ssid","your_passwd");
18+
// if DNSServer is started with "*" for domain name, it will reply with
19+
// provided IP to all DNS request
20+
dnsServer.start(DNS_PORT, "*", apIP);
21+
22+
server.begin(80);
23+
}
24+
25+
void loop() {
26+
dnsServer.processNextRequest();
27+
WiFiClient client = server.available(); // listen for incoming clients
28+
29+
if (client) {
30+
String currentLine = "";
31+
while (client.connected()) {
32+
if (client.available()) {
33+
char c = client.read();
34+
if (c == '\n') {
35+
if (currentLine.length() == 0) {
36+
client.println("HTTP/1.1 200 OK");
37+
client.println("Content-type:text/html");
38+
client.println();
39+
client.print(responseHTML);
40+
break;
41+
} else {
42+
currentLine = "";
43+
}
44+
} else if (c != '\r') {
45+
currentLine += c;
46+
}
47+
}
48+
}
49+
client.stop();
50+
}
51+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name=DNSServer
2+
3+
architectures=*
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
#include "DNSServer.h"
2+
#include <lwip/def.h>
3+
#include <Arduino.h>
4+
5+
DNSServer::DNSServer()
6+
{
7+
_ttl = htonl(DNS_DEFAULT_TTL);
8+
_ErrReplyCode = DNSReplyCode::NonExistentDomain;
9+
_DnsHeader = (DNSHeader*) malloc( sizeof(DNSHeader) ) ;
10+
_DnsQuestion = (DNSQuestion*) malloc( sizeof(DNSQuestion) ) ;
11+
_buf = NULL;
12+
_CurPacketSize = 0;
13+
_port = 0;
14+
}
15+
16+
DNSServer::~DNSServer()
17+
{
18+
if (_DnsHeader) {
19+
free(_DnsHeader);
20+
_DnsHeader = NULL;
21+
}
22+
if (_DnsQuestion) {
23+
free(_DnsQuestion);
24+
_DnsQuestion = NULL;
25+
}
26+
if (_buf) {
27+
free(_buf);
28+
_buf = NULL;
29+
}
30+
}
31+
32+
bool DNSServer::start(const uint16_t &port, const String &domainName,
33+
const IPAddress &resolvedIP)
34+
{
35+
_port = port;
36+
_buf = NULL;
37+
_domain = domainName;
38+
_ResolveIP[0] = resolvedIP[0];
39+
_ResolveIP[1] = resolvedIP[1];
40+
_ResolveIP[2] = resolvedIP[2];
41+
_ResolveIP[3] = resolvedIP[3];
42+
downcaseAndRemoveWwwPrefix(_domain);
43+
return _Udp.begin(_port) == 1;
44+
}
45+
46+
void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode)
47+
{
48+
_ErrReplyCode = replyCode;
49+
}
50+
51+
void DNSServer::setTTL(const uint32_t &ttl)
52+
{
53+
_ttl = htonl(ttl);
54+
}
55+
56+
void DNSServer::stop()
57+
{
58+
_Udp.stop();
59+
free(_buf);
60+
_buf = NULL;
61+
}
62+
63+
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName)
64+
{
65+
domainName.toLowerCase();
66+
domainName.replace("www.", "");
67+
}
68+
69+
void DNSServer::processNextRequest()
70+
{
71+
_CurPacketSize = _Udp.parsePacket();
72+
if (_CurPacketSize)
73+
{
74+
// Allocate buffer for the DNS query
75+
if (_buf != NULL)
76+
free(_buf);
77+
_buf = (unsigned char*)malloc(_CurPacketSize * sizeof(char));
78+
if (_buf == NULL)
79+
return;
80+
81+
// Put the packet received in the buffer and get DNS header (beginning of message)
82+
// and the question
83+
_Udp.read(_buf, _CurPacketSize);
84+
memcpy( _DnsHeader, _buf, DNS_HEADER_SIZE ) ;
85+
if ( requestIncludesOnlyOneQuestion() )
86+
{
87+
// The QName has a variable length, maximum 255 bytes and is comprised of multiple labels.
88+
// Each label contains a byte to describe its length and the label itself. The list of
89+
// labels terminates with a zero-valued byte. In "github.com", we have two labels "github" & "com"
90+
// Iterate through the labels and copy them as they come into a single buffer (for simplicity's sake)
91+
_DnsQuestion->QNameLength = 0 ;
92+
while ( _buf[ DNS_HEADER_SIZE + _DnsQuestion->QNameLength ] != 0 )
93+
{
94+
memcpy( (void*) &_DnsQuestion->QName[_DnsQuestion->QNameLength], (void*) &_buf[DNS_HEADER_SIZE + _DnsQuestion->QNameLength], _buf[DNS_HEADER_SIZE + _DnsQuestion->QNameLength] + 1 ) ;
95+
_DnsQuestion->QNameLength += _buf[DNS_HEADER_SIZE + _DnsQuestion->QNameLength] + 1 ;
96+
}
97+
_DnsQuestion->QName[_DnsQuestion->QNameLength] = 0 ;
98+
_DnsQuestion->QNameLength++ ;
99+
100+
// Copy the QType and QClass
101+
memcpy( &_DnsQuestion->QType, (void*) &_buf[DNS_HEADER_SIZE + _DnsQuestion->QNameLength], sizeof(_DnsQuestion->QType) ) ;
102+
memcpy( &_DnsQuestion->QClass, (void*) &_buf[DNS_HEADER_SIZE + _DnsQuestion->QNameLength + sizeof(_DnsQuestion->QType)], sizeof(_DnsQuestion->QClass) ) ;
103+
}
104+
105+
106+
if (_DnsHeader->QRFlag == DNS_QR_QUERY &&
107+
_DnsHeader->OPCode == DNS_OPCODE_QUERY &&
108+
requestIncludesOnlyOneQuestion() &&
109+
(_domain == "*" || getDomainNameWithoutWwwPrefix() == _domain)
110+
)
111+
{
112+
replyWithIP();
113+
}
114+
else if (_DnsHeader->QRFlag == DNS_QR_QUERY)
115+
{
116+
replyWithCustomCode();
117+
}
118+
119+
free(_buf);
120+
_buf = NULL;
121+
}
122+
}
123+
124+
bool DNSServer::requestIncludesOnlyOneQuestion()
125+
{
126+
return ntohs(_DnsHeader->QuesCnt) == 1 &&
127+
_DnsHeader->AnsCnt == 0 &&
128+
_DnsHeader->AuthCnt == 0 &&
129+
_DnsHeader->ResCnt == 0;
130+
}
131+
132+
133+
String DNSServer::getDomainNameWithoutWwwPrefix()
134+
{
135+
// Error checking : if the buffer containing the DNS request is a null pointer, return an empty domain
136+
String parsedDomainName = "";
137+
if (_buf == NULL)
138+
return parsedDomainName;
139+
140+
// Set the start of the domain just after the header (12 bytes). If equal to null character, return an empty domain
141+
unsigned char *start = _buf + DNS_OFFSET_DOMAIN_NAME;
142+
if (*start == 0)
143+
{
144+
return parsedDomainName;
145+
}
146+
147+
int pos = 0;
148+
while(true)
149+
{
150+
unsigned char labelLength = *(start + pos);
151+
for(int i = 0; i < labelLength; i++)
152+
{
153+
pos++;
154+
parsedDomainName += (char)*(start + pos);
155+
}
156+
pos++;
157+
if (*(start + pos) == 0)
158+
{
159+
downcaseAndRemoveWwwPrefix(parsedDomainName);
160+
return parsedDomainName;
161+
}
162+
else
163+
{
164+
parsedDomainName += ".";
165+
}
166+
}
167+
}
168+
169+
void DNSServer::replyWithIP()
170+
{
171+
_Udp.beginPacket(_Udp.remoteIP(), _Udp.remotePort());
172+
173+
// Change the type of message to a response and set the number of answers equal to
174+
// the number of questions in the header
175+
_DnsHeader->QRFlag = DNS_QR_RESPONSE;
176+
_DnsHeader->AnsCnt = _DnsHeader->QuesCnt;
177+
_Udp.write( (unsigned char*) _DnsHeader, DNS_HEADER_SIZE ) ;
178+
179+
// Write the question
180+
_Udp.write(_DnsQuestion->QName, _DnsQuestion->QNameLength) ;
181+
_Udp.write( (unsigned char*) &_DnsQuestion->QType, 2 ) ;
182+
_Udp.write( (unsigned char*) &_DnsQuestion->QClass, 2 ) ;
183+
184+
// Write the answer
185+
// Use DNS name compression : instead of repeating the name in this RNAME occurence,
186+
// set the two MSB of the byte corresponding normally to the length to 1. The following
187+
// 14 bits must be used to specify the offset of the domain name in the message
188+
// (<255 here so the first byte has the 6 LSB at 0)
189+
_Udp.write((uint8_t) 0xC0);
190+
_Udp.write((uint8_t) DNS_OFFSET_DOMAIN_NAME);
191+
192+
// DNS type A : host address, DNS class IN for INternet, returning an IPv4 address
193+
uint16_t answerType = htons(DNS_TYPE_HOST_ADDRESS), answerClass = htons(DNS_CLASS_IN), answerIPv4 = htons(DNS_RDLENGTH_IPV4) ;
194+
_Udp.write((unsigned char*) &answerType, 2 );
195+
_Udp.write((unsigned char*) &answerClass, 2 );
196+
_Udp.write((unsigned char*) &_ttl, 4); // DNS Time To Live
197+
_Udp.write((unsigned char*) &answerIPv4, 2 );
198+
_Udp.write(_ResolveIP, sizeof(_ResolveIP)); // The IP address to return
199+
_Udp.endPacket();
200+
201+
}
202+
203+
void DNSServer::replyWithCustomCode()
204+
{
205+
_DnsHeader->QRFlag = DNS_QR_RESPONSE;
206+
_DnsHeader->RspCode = (unsigned char)_ErrReplyCode;
207+
_DnsHeader->QuesCnt = 0;
208+
209+
_Udp.beginPacket(_Udp.remoteIP(), _Udp.remotePort());
210+
_Udp.write((unsigned char*)_DnsHeader, sizeof(DNSHeader));
211+
_Udp.endPacket();
212+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#ifndef __DNSServer_H
2+
#define __DNSServer_H
3+
#include <WiFiUdp.h>
4+
5+
#define DNS_QR_QUERY 0
6+
#define DNS_QR_RESPONSE 1
7+
#define DNS_OPCODE_QUERY 0
8+
#define DNS_DEFAULT_TTL 60 // Default Time To Live : time interval in seconds that the resource record should be cached before being discarded
9+
#define DNS_OFFSET_DOMAIN_NAME 12 // Offset in bytes to reach the domain name in the DNS message
10+
#define DNS_HEADER_SIZE 12
11+
12+
enum class DNSReplyCode
13+
{
14+
NoError = 0,
15+
FormError = 1,
16+
ServerFailure = 2,
17+
NonExistentDomain = 3,
18+
NotImplemented = 4,
19+
Refused = 5,
20+
YXDomain = 6,
21+
YXRRSet = 7,
22+
NXRRSet = 8
23+
};
24+
25+
enum DNSType
26+
{
27+
DNS_TYPE_HOST_ADDRESS = 1, // Host Address
28+
DNS_TYPE_IPV6_ADDRESS = 28, // IPv6 Address
29+
DNS_TYPE_SOA = 6, // Start Of a zone of Authority
30+
DNS_TYPE_DOMAIN_PTR = 12, // Domain name PoinTeR
31+
DNS_TYPE_DELENAME = 39 // Delegation Name
32+
} ;
33+
34+
enum DNSClass
35+
{
36+
DNS_CLASS_IN = 1, // INternet
37+
DNS_CLASS_CH = 3 // CHaos
38+
} ;
39+
40+
enum DNSRDLength
41+
{
42+
DNS_RDLENGTH_IPV4 = 4 // 4 bytes for an IPv4 address
43+
} ;
44+
45+
struct DNSHeader
46+
{
47+
uint16_t ID; // identification number
48+
union {
49+
struct {
50+
uint16_t RD : 1; // recursion desired
51+
uint16_t Trunc : 1; // truncated message
52+
uint16_t AuthAns : 1; // authoritive answer
53+
uint16_t OPCode : 4; // message_type
54+
uint16_t QRFlag : 1; // query/response flag
55+
uint16_t RspCode : 4; // response code
56+
uint16_t Z : 3; // its z! reserved
57+
uint16_t RecAvail : 1; // recursion available
58+
};
59+
uint16_t Flags;
60+
};
61+
uint16_t QuesCnt; // number of question entries
62+
uint16_t AnsCnt; // number of answer entries
63+
uint16_t AuthCnt; // number of authority entries
64+
uint16_t ResCnt; // number of resource entries
65+
};
66+
67+
struct DNSQuestion
68+
{
69+
uint8_t QName[256] ; //need 1 Byte for zero termination!
70+
uint16_t QNameLength ;
71+
uint16_t QType ;
72+
uint16_t QClass ;
73+
} ;
74+
75+
class DNSServer
76+
{
77+
public:
78+
DNSServer();
79+
~DNSServer();
80+
void processNextRequest();
81+
void setErrorReplyCode(const DNSReplyCode &replyCode);
82+
void setTTL(const uint32_t &ttl);
83+
84+
// Returns true if successful, false if there are no sockets available
85+
bool start(const uint16_t &port,
86+
const String &domainName,
87+
const IPAddress &resolvedIP);
88+
// stops the DNS server
89+
void stop();
90+
91+
private:
92+
WiFiUDP _Udp;
93+
uint16_t _port;
94+
String _domain;
95+
unsigned char _ResolveIP[4];
96+
int _CurPacketSize;
97+
unsigned char* _buf;
98+
DNSHeader* _DnsHeader;
99+
uint32_t _ttl;
100+
DNSReplyCode _ErrReplyCode;
101+
DNSQuestion* _DnsQuestion ;
102+
103+
104+
void downcaseAndRemoveWwwPrefix(String &domainName);
105+
String getDomainNameWithoutWwwPrefix();
106+
bool requestIncludesOnlyOneQuestion();
107+
void replyWithIP();
108+
void replyWithCustomCode();
109+
};
110+
#endif

0 commit comments

Comments
 (0)