Skip to content

Commit dde3f2c

Browse files
knovoselicigrr
authored andcommitted
Added a DNSServer library
1 parent 67a07ab commit dde3f2c

File tree

5 files changed

+263
-18
lines changed

5 files changed

+263
-18
lines changed

README.md

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ This is mostly similar to WiFi shield library. Differences include:
102102
- ```WiFi.RSSI()``` doesn't work
103103
- ```WiFi.printDiag(Serial);``` will print out some diagnostic info
104104
- ```WiFiUDP``` class supports sending and receiving multicast packets on STA interface.
105-
When sending a multicast packet, replace ```udp.beginPacket(addr, port)``` with
105+
When sending a multicast packet, replace ```udp.beginPacket(addr, port)``` with
106106
```udp.beginPacketMulticast(addr, port, WiFi.localIP())```.
107-
When listening to multicast packets, replace ```udp.begin(port)``` with
107+
When listening to multicast packets, replace ```udp.begin(port)``` with
108108
```udp.beginMulticast(WiFi.localIP(), multicast_ip_addr, port)```.
109109
You can use ```udp.destinationIP()``` to tell whether the packet received was
110110
sent to the multicast or unicast address.
@@ -118,7 +118,7 @@ You can see more commands here: [http://www.arduino.cc/en/Reference/WiFi](http:/
118118

119119
Library for calling functions repeatedly with a certain period. Two examples included.
120120

121-
It is currently not recommended to do blocking IO operations (network, serial, file) from Ticker
121+
It is currently not recommended to do blocking IO operations (network, serial, file) from Ticker
122122
callback functions. Instead, set a flag inside the ticker callback and check for that flag inside the loop function.
123123

124124
#### EEPROM ####
@@ -184,6 +184,11 @@ Allows the sketch to respond to multicast DNS queries for domain names like "foo
184184
Currently the library only works on STA interface, AP interface is not supported.
185185
See attached example and library README file for details.
186186

187+
#### DNS server (DNSServer library) ####
188+
189+
Implements a simple DNS server that can be used in both STA and AP modes. The DNS server currently supports only one domain (for all other domains it will reply with NXDOMAIN or custom status code). With it clients can open a web server running on ESP8266 using a domain name, not an IP address.
190+
See attached example for details.
191+
187192
#### Servo ####
188193

189194
This library exposes the ability to control RC (hobby) servo motors. It will support upto 24 servos on any available output pin. By defualt the first 12 servos will use Timer0 and currently this will not interfere with any other support. Servo counts above 12 will use Timer1 and features that use it will be effected.
@@ -212,10 +217,10 @@ You need to put ESP8266 into bootloader mode before uploading code.
212217
For stable use of the ESP8266 a power supply with 3V3 and >= 250mA is required.
213218

214219
* Note
215-
- using Power from USB to Serial is may unstable, they not deliver enough current.
216-
220+
- using Power from USB to Serial is may unstable, they not deliver enough current.
221+
217222
#### Serial Adapter ####
218-
223+
219224
There are many different USB to Serial adapters / boards.
220225

221226
* Note
@@ -224,17 +229,17 @@ There are many different USB to Serial adapters / boards.
224229
- not all board have all pins of the ICs as breakout (check before order)
225230
- CTS and DSR are not useful for upload (they are Inputs)
226231

227-
* Working ICs
232+
* Working ICs
228233
- FT232RL
229234
- CP2102
230235
- may others (drop a comment)
231-
236+
232237
#### Minimal hardware Setup for Bootloading and usage ####
233238

234239
ESPxx Hardware
235240

236241
| PIN | Resistor | Serial Adapter |
237-
| ------------- | -------- | -------------- |
242+
| ------------- | -------- | -------------- |
238243
| VCC | | VCC (3.3V) |
239244
| GND | | GND |
240245
| TX or GPIO2* | | RX |
@@ -244,19 +249,19 @@ ESPxx Hardware
244249
| GPIO15* | PullDown | |
245250
| CH_PD | PullUp | |
246251

247-
* Note
252+
* Note
248253
- GPIO15 is also named MTDO
249254
- Reset is also named RSBT or REST (adding PullUp improves the stability of the Module)
250255
- GPIO2 is alternative TX for the boot loader mode
251-
256+
252257
###### esp to Serial
253258
![ESP to Serial](https://raw.githubusercontent.com/Links2004/Arduino/esp8266/docs/ESP_to_serial.png)
254259

255260
#### Minimal hardware Setup for Bootloading only ####
256261
ESPxx Hardware
257262

258263
| PIN | Resistor | Serial Adapter |
259-
| ------------- | -------- | --------------- |
264+
| ------------- | -------- | --------------- |
260265
| VCC | | VCC (3.3V) |
261266
| GND | | GND |
262267
| TX or GPIO2 | | RX |
@@ -266,15 +271,15 @@ ESPxx Hardware
266271
| GPIO15 | PullDown | |
267272
| CH_PD | PullUp | |
268273

269-
* Note
274+
* Note
270275
- if no RTS is used a manual power toggle is needed
271-
276+
272277
#### Minimal hardware Setup for running only ####
273278

274279
ESPxx Hardware
275280

276281
| PIN | Resistor | Power supply |
277-
| ------------- | -------- | --------------- |
282+
| ------------- | -------- | --------------- |
278283
| VCC | | VCC (3.3V) |
279284
| GND | | GND |
280285
| GPIO0 | PullUp | |
@@ -285,7 +290,7 @@ ESPxx Hardware
285290
![ESP min](https://raw.githubusercontent.com/Links2004/Arduino/esp8266/docs/ESP_min.png)
286291

287292
###### improved stability
288-
![ESP improved stability](https://raw.githubusercontent.com/Links2004/Arduino/esp8266/docs/ESP_improved_stability.png)
293+
![ESP improved stability](https://raw.githubusercontent.com/Links2004/Arduino/esp8266/docs/ESP_improved_stability.png)
289294

290295
### Issues and support ###
291296

@@ -306,5 +311,3 @@ Esptool written by Christian Klippel is licensed under GPLv2, currently maintain
306311
ESP8266 core support, ESP8266WiFi, Ticker, ESP8266WebServer libraries were written by Ivan Grokhotkov, [email protected].
307312

308313
[SPI Flash File System (SPIFFS)](https://github.com/pellepl/spiffs) written by Peter Andersson is used in this project. It is distributed under MIT license.
309-
310-
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include <ESP8266WiFi.h>
2+
#include <DNSServer.h>
3+
4+
const byte DNS_PORT = 53;
5+
IPAddress apIP(192, 168, 1, 1);
6+
DNSServer dnsServer;
7+
8+
void setup() {
9+
WiFi.mode(WIFI_AP);
10+
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
11+
WiFi.softAP("DNSServer example");
12+
13+
// modify TTL associated with the domain name (in seconds)
14+
// default is 60 seconds
15+
dnsServer.setTTL(300);
16+
// set which return code will be used for all other domains (e.g. sending
17+
// ServerFailure instead of NonExistentDomain will reduce number of queries
18+
// sent by clients)
19+
// default is DNSReplyCode::NonExistentDomain
20+
dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure);
21+
22+
//start DNS server for a specific domain name
23+
dnsServer.start(DNS_PORT, "www.example.com", apIP);
24+
}
25+
26+
void loop() {
27+
dnsServer.processNextRequest();
28+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name=DNSServer
2+
version=1.0.0
3+
author=Kristijan Novoselić
4+
maintainer=Kristijan Novoselić, <[email protected]>
5+
sentence=A simple DNS server for ESP8266.
6+
paragraph=This library implements a simple DNS server.
7+
category=Communication
8+
url=
9+
architectures=esp8266
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#include "DNSServer.h"
2+
#include <lwip/def.h>
3+
#include <Arduino.h>
4+
5+
DNSServer::DNSServer()
6+
{
7+
_ttl = htonl(60);
8+
_errorReplyCode = DNSReplyCode::NonExistentDomain;
9+
}
10+
11+
bool DNSServer::start(const uint16_t &port, const String &domainName,
12+
const IPAddress &resolvedIP)
13+
{
14+
_port = port;
15+
_domainName = domainName;
16+
_resolvedIP[0] = resolvedIP[0];
17+
_resolvedIP[1] = resolvedIP[1];
18+
_resolvedIP[2] = resolvedIP[2];
19+
_resolvedIP[3] = resolvedIP[3];
20+
downcaseAndRemoveWwwPrefix(_domainName);
21+
return _udp.begin(_port) == 1;
22+
}
23+
24+
void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode)
25+
{
26+
_errorReplyCode = replyCode;
27+
}
28+
29+
void DNSServer::setTTL(const uint32_t &ttl)
30+
{
31+
_ttl = htonl(ttl);
32+
}
33+
34+
void DNSServer::stop()
35+
{
36+
_udp.stop();
37+
}
38+
39+
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName)
40+
{
41+
domainName.toLowerCase();
42+
domainName.replace("www.", "");
43+
}
44+
45+
void DNSServer::processNextRequest()
46+
{
47+
_currentPacketSize = _udp.parsePacket();
48+
if (_currentPacketSize)
49+
{
50+
_buffer = (unsigned char*)malloc(_currentPacketSize * sizeof(char));
51+
_udp.read(_buffer, _currentPacketSize);
52+
_dnsHeader = (DNSHeader*) _buffer;
53+
54+
if (_dnsHeader->QR == DNS_QR_QUERY &&
55+
_dnsHeader->OPCode == DNS_OPCODE_QUERY &&
56+
requestIncludesOnlyOneQuestion() &&
57+
getDomainNameWithoutWwwPrefix() == _domainName)
58+
{
59+
replyWithIP();
60+
}
61+
else if (_dnsHeader->QR == DNS_QR_QUERY)
62+
{
63+
replyWithCustomCode();
64+
}
65+
66+
free(_buffer);
67+
}
68+
}
69+
70+
bool DNSServer::requestIncludesOnlyOneQuestion()
71+
{
72+
return ntohs(_dnsHeader->QDCount) == 1 &&
73+
_dnsHeader->ANCount == 0 &&
74+
_dnsHeader->NSCount == 0 &&
75+
_dnsHeader->ARCount == 0;
76+
}
77+
78+
String DNSServer::getDomainNameWithoutWwwPrefix()
79+
{
80+
String parsedDomainName = "";
81+
unsigned char *start = _buffer + 12;
82+
if (*start == 0)
83+
{
84+
return parsedDomainName;
85+
}
86+
int pos = 0;
87+
while(true)
88+
{
89+
unsigned char labelLength = *(start + pos);
90+
for(int i = 0; i < labelLength; i++)
91+
{
92+
pos++;
93+
parsedDomainName += (char)*(start + pos);
94+
}
95+
pos++;
96+
if (*(start + pos) == 0)
97+
{
98+
downcaseAndRemoveWwwPrefix(parsedDomainName);
99+
return parsedDomainName;
100+
}
101+
else
102+
{
103+
parsedDomainName += ".";
104+
}
105+
}
106+
}
107+
108+
void DNSServer::replyWithIP()
109+
{
110+
_dnsHeader->QR = DNS_QR_RESPONSE;
111+
_dnsHeader->ANCount = _dnsHeader->QDCount;
112+
_dnsHeader->QDCount = 0;
113+
114+
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
115+
_udp.write(_buffer, _currentPacketSize);
116+
_udp.write((unsigned char*)&_ttl, 4);
117+
// Length of RData is 4 bytes (because, in this case, RData is IPv4)
118+
_udp.write((uint8_t)0);
119+
_udp.write((uint8_t)4);
120+
_udp.write(_resolvedIP, sizeof(_resolvedIP));
121+
_udp.endPacket();
122+
}
123+
124+
void DNSServer::replyWithCustomCode()
125+
{
126+
_dnsHeader->QR = DNS_QR_RESPONSE;
127+
_dnsHeader->RCode = (unsigned char)_errorReplyCode;
128+
_dnsHeader->QDCount = 0;
129+
130+
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
131+
_udp.write(_buffer, sizeof(DNSHeader));
132+
_udp.endPacket();
133+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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+
9+
enum class DNSReplyCode
10+
{
11+
NoError = 0,
12+
FormError = 1,
13+
ServerFailure = 2,
14+
NonExistentDomain = 3,
15+
NotImplemented = 4,
16+
Refused = 5,
17+
YXDomain = 6,
18+
YXRRSet = 7,
19+
NXRRSet = 8
20+
};
21+
22+
struct DNSHeader
23+
{
24+
uint16_t ID; // identification number
25+
unsigned char RD : 1; // recursion desired
26+
unsigned char TC : 1; // truncated message
27+
unsigned char AA : 1; // authoritive answer
28+
unsigned char OPCode : 4; // message_type
29+
unsigned char QR : 1; // query/response flag
30+
unsigned char RCode : 4; // response code
31+
unsigned char Z : 3; // its z! reserved
32+
unsigned char RA : 1; // recursion available
33+
uint16_t QDCount; // number of question entries
34+
uint16_t ANCount; // number of answer entries
35+
uint16_t NSCount; // number of authority entries
36+
uint16_t ARCount; // number of resource entries
37+
};
38+
39+
class DNSServer
40+
{
41+
public:
42+
DNSServer();
43+
void processNextRequest();
44+
void setErrorReplyCode(const DNSReplyCode &replyCode);
45+
void setTTL(const uint32_t &ttl);
46+
47+
// Returns true if successful, false if there are no sockets available
48+
bool start(const uint16_t &port,
49+
const String &domainName,
50+
const IPAddress &resolvedIP);
51+
// stops the DNS server
52+
void stop();
53+
54+
private:
55+
WiFiUDP _udp;
56+
uint16_t _port;
57+
String _domainName;
58+
unsigned char _resolvedIP[4];
59+
int _currentPacketSize;
60+
unsigned char* _buffer;
61+
DNSHeader* _dnsHeader;
62+
uint32_t _ttl;
63+
DNSReplyCode _errorReplyCode;
64+
65+
66+
void downcaseAndRemoveWwwPrefix(String &domainName);
67+
String getDomainNameWithoutWwwPrefix();
68+
bool requestIncludesOnlyOneQuestion();
69+
void replyWithIP();
70+
void replyWithCustomCode();
71+
};
72+
#endif

0 commit comments

Comments
 (0)