4 #include "include/dns.h"
5 #include "include/dns_int.h"
11 extern int DNS_int_ParseRR(const void *buf, size_t ofs, size_t space, char* name_p, enum eTypes* type_p, enum eClass* class_p, uint32_t* ttl_p, size_t* rdlength_p);
13 static uint16_t get16(const void *buf);
14 static uint32_t get32(const void *buf);
15 static size_t put16(void *buf, uint16_t val);
18 size_t DNS_int_EncodeQuery(void *buf, size_t bufsize, const char *name, enum eTypes type, enum eClass class)
20 int namelen = DNS_EncodeName(NULL, name);
21 if( namelen >= 256 ) {
22 _SysDebug("DNS_int_EncodeQuery - ERROR: Name encoded to >= 256 bytes");
26 uint8_t *packet = buf;
27 if( (6*2) + (namelen + 2*2) > bufsize ) {
28 _SysDebug("DNS_int_EncodeQuery - ERROR: Passed buffer too small");
32 pos += put16(packet + pos, 0xAC00); // Identifier (arbitary)
33 pos += put16(packet + pos, (0 << 0) | (0 << 1) | (1 << 8) ); // Op : Query, Standard, Recursion
34 pos += put16(packet + pos, 1); // QDCount
35 pos += put16(packet + pos, 0); // ANCount
36 pos += put16(packet + pos, 0); // NSCount
37 pos += put16(packet + pos, 0); // ARCount
39 pos += DNS_EncodeName(packet + pos, name);
40 pos += put16(packet + pos, type); // QType
41 pos += put16(packet + pos, class); // QClass
43 assert(pos <= bufsize);
47 int DNS_int_ParseResponse(const void* buf, size_t return_len, void *info, handle_record_t* handle_record)
49 const uint8_t* packet = buf;
51 unsigned int id = get16(packet + 0);
53 _SysDebug("DNS_Query - Packet ID mismatch");
56 unsigned int flags = get16(packet + 2);
57 unsigned int qd_count = get16(packet + 4);
58 unsigned int an_count = get16(packet + 6);
59 unsigned int ns_count = get16(packet + 8);
60 unsigned int ar_count = get16(packet + 10);
62 // TODO: Can I safely assert / fail if qd_count is non-zero?
63 // - Questions, ignored
64 for( unsigned int i = 0; i < qd_count; i ++ ) {
65 int rv = DNS_DecodeName(rr_name, packet, pos, return_len);
67 _SysDebug("DNS_Query - Parse error in QD");
72 // - Answers, pass on to handler
73 for( unsigned int i = 0; i < an_count; i ++ )
79 int rv = DNS_int_ParseRR(packet, pos, return_len, rr_name, &type, &class, &ttl, &rdlength);
81 _SysDebug("DNS_Query - Parse error in AN");
86 if( handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos - rdlength) )
89 // Authority Records (should all be NS records)
90 for( unsigned int i = 0; i < ns_count; i ++ )
93 int rv = DNS_int_ParseRR(packet, pos, return_len, rr_name, NULL, NULL, NULL, &rdlength);
95 _SysDebug("DNS_Query - Parse error in NS");
100 // - Additional records, pass to handler
101 for( unsigned int i = 0; i < ar_count; i ++ )
107 int rv = DNS_int_ParseRR(packet, pos, return_len, rr_name, &type, &class, &ttl, &rdlength);
109 _SysDebug("DNS_Query - Parse error in AR");
114 if( handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos - rdlength) )
121 /// Encode a dotted name as a DNS name
122 size_t DNS_EncodeName(void *buf, const char *dotted_name)
125 const char *str = dotted_name;
129 const char *next = strchr(str, '.');
130 size_t seg_len = (next ? next - str : strlen(str));
132 // Oops, too long (truncate)
135 if( seg_len == 0 && next != NULL ) {
136 // '..' encountered, invalid (skip)
144 memcpy(buf8+ret+1, str, seg_len);
149 // No trailing '.', assume it's there? Yes, need to be NUL terminated
150 if(buf8) buf8[ret] = 0;
161 // Decode a name (including trailing . for root)
162 int DNS_DecodeName(char dotted_name[256], const void *buf, size_t ofs, size_t space)
166 const uint8_t *buf8 = (const uint8_t*)buf + ofs;
169 if( ofs + consumed + 1 > space ) {
170 _SysDebug("DNS_DecodeName - Len byte OOR space=%zi", space);
173 uint8_t seg_len = *buf8;
179 if( (seg_len & 0xC0) == 0xC0 )
181 // Backreference, the rest of the name is a backref
183 int ref_ofs = get16(buf8 - 1) & 0x3FFF;
184 consumed += 1, buf8 += 1; // Only one, previous inc still applies
185 //_SysDebug("DNS_DecodeName - Nested at %i", ref_ofs);
186 if( DNS_DecodeName(tmp, buf, ref_ofs, space) < 0 )
188 memcpy(dotted_name+out_pos, tmp, strlen(tmp));
189 out_pos += strlen(tmp);
192 // Protocol violation (segment too long)
193 if( seg_len >= 64 ) {
194 _SysDebug("DNS_DecodeName - Seg too long %i", seg_len);
197 // Protocol violation (overflowed end of buffer)
198 if( ofs + consumed + seg_len > space ) {
199 _SysDebug("DNS_DecodeName - Seg OOR %i+%i>%zi", consumed, seg_len, space);
202 // Protocol violation (name was too long)
203 if( out_pos + seg_len + 1 > 255 ) {
204 _SysDebug("DNS_DecodeName - Dotted name too long %i+%i+1 > %i",
205 out_pos, seg_len, 255);
209 //_SysDebug("DNS_DecodeName : Seg %i '%.*s'", seg_len, seg_len, buf8);
212 memcpy(dotted_name + out_pos, buf8, seg_len);
218 dotted_name[out_pos] = '.';
221 dotted_name[out_pos] = '\0';
222 //_SysDebug("DNS_DecodeName - '%s', consumed = %i", dotted_name, consumed);
226 // Parse a Resource Record
227 int DNS_int_ParseRR(const void *buf, size_t ofs, size_t space, char* name_p, enum eTypes* type_p, enum eClass* class_p, uint32_t* ttl_p, size_t* rdlength_p)
229 const uint8_t *buf8 = buf;
233 int rv = DNS_DecodeName(name_p, buf, ofs, space);
234 if(rv < 0) return -1;
236 ofs += rv, consumed += rv;
239 *type_p = get16(buf8 + ofs);
240 ofs += 2, consumed += 2;
243 *class_p = get16(buf8 + ofs);
244 ofs += 2, consumed += 2;
247 *ttl_p = get32(buf + ofs);
248 ofs += 4, consumed += 4;
250 size_t rdlength = get16(buf + ofs);
252 *rdlength_p = rdlength;
253 ofs += 2, consumed += 2;
255 _SysDebug("DNS_int_ParseRR - name='%s', rdlength=%zi", name_p, rdlength);
257 return consumed + rdlength;
260 static uint16_t get16(const void *buf) {
261 const uint8_t* buf8 = buf;
263 rv |= (uint16_t)buf8[0] << 8;
264 rv |= (uint16_t)buf8[1] << 0;
267 static uint32_t get32(const void *buf) {
268 const uint8_t* buf8 = buf;
270 rv |= (uint32_t)buf8[0] << 24;
271 rv |= (uint32_t)buf8[1] << 16;
272 rv |= (uint32_t)buf8[2] << 8;
273 rv |= (uint32_t)buf8[3] << 0;
276 static size_t put16(void *buf, uint16_t val) {
279 buf8[1] = val & 0xFF;