15
15
DNSServer::DNSServer () : _port(DNS_DEFAULT_PORT), _ttl(htonl(DNS_DEFAULT_TTL)), _errorReplyCode(DNSReplyCode::NonExistentDomain) {}
16
16
17
17
DNSServer::DNSServer (const String &domainName)
18
- : _port(DNS_DEFAULT_PORT), _ttl(htonl(DNS_DEFAULT_TTL)), _errorReplyCode(DNSReplyCode::NonExistentDomain), _domainName(domainName){};
18
+ : _port(DNS_DEFAULT_PORT), _ttl(htonl(DNS_DEFAULT_TTL)), _errorReplyCode(DNSReplyCode::NonExistentDomain), _domainName(domainName) {};
19
19
20
20
bool DNSServer::start () {
21
21
if (_resolvedIP.operator uint32_t () == 0 ) { // no address is set, try to obtain AP interface's IP
@@ -111,16 +111,22 @@ void DNSServer::_handleUDP(AsyncUDPPacket &pkt) {
111
111
// will reply with IP only to "*" or if domain matches without www. subdomain
112
112
if (dnsHeader.OPCode == DNS_OPCODE_QUERY && requestIncludesOnlyOneQuestion (dnsHeader)
113
113
&& (_domainName.isEmpty () || getDomainNameWithoutWwwPrefix (static_cast <const unsigned char *>(dnsQuestion.QName ), dnsQuestion.QNameLength ) == _domainName)) {
114
- replyWithIP (pkt, dnsHeader, dnsQuestion);
114
+
115
+ // Qtype = A (1) or ANY (255): send an A record otherwise an empty response
116
+ if (ntohs (dnsQuestion.QType ) == 1 || ntohs (dnsQuestion.QType ) == 255 ) {
117
+ replyWithIP (pkt, dnsHeader, dnsQuestion);
118
+ } else {
119
+ replyWithNoAnsw (pkt, dnsHeader, dnsQuestion);
120
+ }
115
121
return ;
116
122
}
117
-
118
123
// otherwise reply with custom code
119
124
replyWithCustomCode (pkt, dnsHeader);
120
125
}
121
126
122
127
bool DNSServer::requestIncludesOnlyOneQuestion (DNSHeader &dnsHeader) {
123
- return ntohs (dnsHeader.QDCount ) == 1 && dnsHeader.ANCount == 0 && dnsHeader.NSCount == 0 && dnsHeader.ARCount == 0 ;
128
+ dnsHeader.ARCount = 0 ; // We assume that if ARCount !=0 there is a EDNS OPT packet, just ignore
129
+ return ntohs (dnsHeader.QDCount ) == 1 && dnsHeader.ANCount == 0 && dnsHeader.NSCount == 0 ; // && dnsHeader.ARCount == 0;
124
130
}
125
131
126
132
String DNSServer::getDomainNameWithoutWwwPrefix (const unsigned char *start, size_t len) {
@@ -139,7 +145,6 @@ String DNSServer::getDomainNameWithoutWwwPrefix(const unsigned char *start, size
139
145
140
146
void DNSServer::replyWithIP (AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion) {
141
147
AsyncUDPMessage rpl;
142
-
143
148
// Change the type of message to a response and set the number of answers equal to
144
149
// the number of questions in the header
145
150
dnsHeader.QR = DNS_QR_RESPONSE;
@@ -187,3 +192,77 @@ void DNSServer::replyWithCustomCode(AsyncUDPPacket &req, DNSHeader &dnsHeader) {
187
192
rpl.write (reinterpret_cast <const uint8_t *>(&dnsHeader), sizeof (DNSHeader));
188
193
_udp.sendTo (rpl, req.remoteIP (), req.remotePort ());
189
194
}
195
+
196
+ void DNSServer::replyWithNoAnsw (AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion) {
197
+
198
+ dnsHeader.QR = DNS_QR_RESPONSE;
199
+ // dnsHeader.QDCount = 1;
200
+ dnsHeader.ANCount = 0 ;
201
+ dnsHeader.NSCount = htons (1 );
202
+
203
+ AsyncUDPMessage rpl;
204
+ rpl.write (reinterpret_cast <const uint8_t *>(&dnsHeader), sizeof (DNSHeader));
205
+
206
+ // Write the question
207
+ rpl.write (dnsQuestion.QName , dnsQuestion.QNameLength );
208
+ rpl.write ((uint8_t *)&dnsQuestion.QType , 2 );
209
+ rpl.write ((uint8_t *)&dnsQuestion.QClass , 2 );
210
+
211
+ // An empty answer contains an authority section with a SOA,
212
+ // We take the name of the query as the root of the zone for which the SOA is generated
213
+ // and use a value of DNS_MINIMAL_TTL seconds in order to minimize negative caching
214
+ // Write the authority section:
215
+ // The SOA RR's ownername is set equal to the query name, and we use made up names for
216
+ // the MNAME and RNAME - it doesn't really matter from a protocol perspective - as for
217
+ // a no such QTYPE answer only the timing fields are used.
218
+ // a protocol perspective - it
219
+ // Use DNS name compression : instead of repeating the name in this RNAME occurrence,
220
+ // set the two MSB of the byte corresponding normally to the length to 1. The following
221
+ // 14 bits must be used to specify the offset of the domain name in the message
222
+ // (<255 here so the first byte has the 6 LSB at 0)
223
+ rpl.write ((uint8_t )0xC0 );
224
+ rpl.write ((uint8_t )DNS_OFFSET_DOMAIN_NAME);
225
+
226
+ // DNS type A : host address, DNS class IN for INternet, returning an IPv4 address
227
+ uint16_t answerType = htons (DNS_TYPE_SOA), answerClass = htons (DNS_CLASS_IN);
228
+ uint32_t Serial = htonl (DNS_SOA_SERIAL); // Date type serial based on the date this piece of code was written
229
+ uint32_t Refresh = htonl (DNS_SOA_REFRESH); // These timers don't matter, we don't serve zone transfers
230
+ uint32_t Retry = htonl (DNS_SOA_RETRY);
231
+ uint32_t Expire = htonl (DNS_SOA_EXPIRE);
232
+ uint32_t MinTTL = htonl (DNS_MINIMAL_TTL); // See RFC2308 section 5
233
+ char MLabel[] = DNS_SOA_MNAME_LABEL;
234
+ char RLabel[] = DNS_SOA_RNAME_LABEL;
235
+ char PostFixLabel[] = DNS_SOA_POSTFIX_LABEL;
236
+
237
+ // 4 accounts for len fields and for both rname
238
+ // and lname and their postfix labels and there are 5 32 bit fields
239
+
240
+ uint16_t RdataLength = htons ((uint16_t )(strlen (MLabel) + strlen (RLabel) + 2 * strlen (PostFixLabel) + 4 + 5 * sizeof (Serial)));
241
+
242
+ rpl.write ((unsigned char *)&answerType, 2 );
243
+ rpl.write ((unsigned char *)&answerClass, 2 );
244
+ rpl.write ((unsigned char *)&MinTTL, 4 ); // DNS Time To Live
245
+
246
+ rpl.write ((unsigned char *)&RdataLength, 2 );
247
+
248
+ rpl.write ((uint8_t )strlen (MLabel));
249
+ rpl.write ((unsigned char *)&MLabel, strlen (MLabel));
250
+
251
+ rpl.write ((unsigned char *)&PostFixLabel, strlen (PostFixLabel));
252
+ rpl.write ((uint8_t )0 );
253
+ // rpl.write((uint8_t)0xC0);
254
+ // rpl.write((uint8_t)DNS_OFFSET_DOMAIN_NAME);
255
+
256
+ rpl.write ((uint8_t )strlen (RLabel));
257
+ rpl.write ((unsigned char *)&RLabel, strlen (RLabel));
258
+ rpl.write ((unsigned char *)&PostFixLabel, strlen (PostFixLabel));
259
+ rpl.write ((uint8_t )0 );
260
+
261
+ rpl.write ((unsigned char *)&Serial, 4 );
262
+ rpl.write ((unsigned char *)&Refresh, 4 );
263
+ rpl.write ((unsigned char *)&Retry, 4 );
264
+ rpl.write ((unsigned char *)&Expire, 4 );
265
+ rpl.write ((unsigned char *)&MinTTL, 4 );
266
+
267
+ _udp.sendTo (rpl, req.remoteIP (), req.remotePort ());
268
+ }
0 commit comments