2 * Acess2 Networking Toolkit
3 * By John Hodge (thePowersGang)
6 * - Hostname<->Address resolution
8 #include <stddef.h> // size_t / NULL
9 #include <stdint.h> // uint*_t
10 #include <string.h> // memcpy, strchr
12 #include <acess/sys.h> // for _SysSelect
13 #include <acess/fd_set.h> // FD_SET
15 #include "include/dns.h"
18 //int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eTypes type, enum eClass class, handle_record_t* handle_record, void *info);
19 int DNS_int_ParseResponse(const void* packet, size_t return_len, void *info, handle_record_t* handle_record_t);
20 size_t DNS_EncodeName(void *buf, const char *dotted_name);
21 int DNS_DecodeName(char dotted_name[256], const void *buf, size_t ofs, size_t space);
22 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);
24 static uint16_t get16(const void *buf);
25 static uint32_t get32(const void *buf);
26 static size_t put16(void *buf, uint16_t val);
30 int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eTypes type, enum eClass class, handle_record_t* handle_record, void *info)
32 int namelen = DNS_EncodeName(NULL, name);
33 assert(namelen < 256);
36 assert( (6*2) + (namelen + 2*2) < 512 );
38 pos += put16(packet + pos, 0xAC00); // Identifier (arbitary)
39 pos += put16(packet + pos, (0 << 0) | (0 << 1) | (1 << 8) ); // Op : Query, Standard, Recursion
40 pos += put16(packet + pos, 1); // QDCount
41 pos += put16(packet + pos, 0); // ANCount
42 pos += put16(packet + pos, 0); // NSCount
43 pos += put16(packet + pos, 0); // ARCount
45 pos += DNS_EncodeName(packet + pos, name);
46 pos += put16(packet + pos, type); // QType
47 pos += put16(packet + pos, class); // QClass
49 assert(pos <= sizeof(packet));
51 // Send and wait for reply
53 // > TODO: Lock DNS queries
55 int sock = Net_OpenSocket_UDP(ServerAType, ServerAddr, 53, 0);
58 _SysDebug("DNS_Query - UDP open failed");
59 // TODO: Correctly report this failure with a useful error code
62 int rv = Net_UDP_SendTo(sock, 53, ServerAType, ServerAddr, pos, packet);
64 _SysDebug("DNS_Query - Write failed");
65 // TODO: Error reporting
75 int64_t timeout = 2000; // Give it two seconds, should be long enough
76 rv = _SysSelect(nfd, &fds, NULL, NULL, &timeout, 0);
78 // Timeout with no reply, give up
79 _SysDebug("DNS_Query - Timeout");
84 // Oops, select failed
85 _SysDebug("DNS_Query - Select failure");
90 int return_len = Net_UDP_RecvFrom(sock, NULL, NULL, NULL, sizeof(packet), packet);
91 if( return_len <= 0 ) {
92 // TODO: Error reporting
93 _SysDebug("DNS_Query - Read failure");
99 // > TODO: Lock DNS queries
101 // For each response in the answer (and additional) sections, call the passed callback
102 return DNS_int_ParseResponse(packet, return_len, info, handle_record);
105 int DNS_int_ParseResponse(const void* buf, size_t return_len, void *info, handle_record_t* handle_record)
107 const uint8_t* packet = buf;
109 unsigned int id = get16(packet + 0);
111 _SysDebug("DNS_Query - Packet ID mismatch");
114 unsigned int flags = get16(packet + 2);
115 unsigned int qd_count = get16(packet + 4);
116 unsigned int an_count = get16(packet + 6);
117 unsigned int ns_count = get16(packet + 8);
118 unsigned int ar_count = get16(packet + 10);
120 // TODO: Can I safely assert / fail if qd_count is non-zero?
121 // - Questions, ignored
122 for( unsigned int i = 0; i < qd_count; i ++ ) {
123 int rv = DNS_DecodeName(rr_name, packet, pos, return_len);
125 _SysDebug("DNS_Query - Parse error in QD");
130 // - Answers, pass on to handler
131 for( unsigned int i = 0; i < an_count; i ++ )
137 int rv = DNS_int_ParseRR(packet, pos, return_len, rr_name, &type, &class, &ttl, &rdlength);
139 _SysDebug("DNS_Query - Parse error in AN");
144 handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos - rdlength);
146 // Authority Records (should all be NS records)
147 for( unsigned int i = 0; i < ns_count; i ++ )
150 int rv = DNS_int_ParseRR(packet, pos, return_len, rr_name, NULL, NULL, NULL, &rdlength);
152 _SysDebug("DNS_Query - Parse error in NS");
157 // - Additional records, pass to handler
158 for( unsigned int i = 0; i < ar_count; i ++ )
164 int rv = DNS_int_ParseRR(packet, pos, return_len, rr_name, &type, &class, &ttl, &rdlength);
166 _SysDebug("DNS_Query - Parse error in AR");
171 handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos - rdlength);
177 /// Encode a dotted name as a DNS name
178 size_t DNS_EncodeName(void *buf, const char *dotted_name)
181 const char *str = dotted_name;
185 const char *next = strchr(str, '.');
186 size_t seg_len = (next ? next - str : strlen(str));
188 // Oops, too long (truncate)
191 if( seg_len == 0 && next != NULL ) {
192 // '..' encountered, invalid (skip)
200 memcpy(buf8+ret+1, str, seg_len);
205 // No trailing '.', assume it's there? Yes, need to be NUL terminated
206 if(buf8) buf8[ret] = 0;
217 // Decode a name (including trailing . for root)
218 int DNS_DecodeName(char dotted_name[256], const void *buf, size_t ofs, size_t space)
222 const uint8_t *buf8 = (const uint8_t*)buf + ofs;
225 if( ofs + consumed + 1 > space ) {
226 _SysDebug("DNS_DecodeName - Len byte OOR space=%i", space);
229 uint8_t seg_len = *buf8;
235 if( (seg_len & 0xC0) == 0xC0 )
237 // Backreference, the rest of the name is a backref
239 int ref_ofs = get16(buf8 - 1) & 0x3FFF;
240 consumed += 1, buf8 += 1; // Only one, previous inc still applies
241 _SysDebug("DNS_DecodeName - Nested at %i", ref_ofs);
242 if( DNS_DecodeName(tmp, buf, ref_ofs, space) < 0 )
244 memcpy(dotted_name+out_pos, tmp, strlen(tmp));
245 out_pos += strlen(tmp);
248 // Protocol violation (segment too long)
249 if( seg_len >= 64 ) {
250 _SysDebug("DNS_DecodeName - Seg too long %i", seg_len);
253 // Protocol violation (overflowed end of buffer)
254 if( ofs + consumed + seg_len > space ) {
255 _SysDebug("DNS_DecodeName - Seg OOR %i+%i>%i", consumed, seg_len, space);
258 // Protocol violation (name was too long)
259 if( out_pos + seg_len + 1 > 255 ) {
260 _SysDebug("DNS_DecodeName - Dotted name too long %i+%i+1 > %i",
261 out_pos, seg_len, 255);
265 _SysDebug("DNS_DecodeName : Seg %i '%.*s'", seg_len, seg_len, buf8);
268 memcpy(dotted_name + out_pos, buf8, seg_len);
274 dotted_name[out_pos] = '.';
277 dotted_name[out_pos] = '\0';
278 _SysDebug("DNS_DecodeName - '%s', consumed = %i", dotted_name, consumed);
282 // Parse a Resource Record
283 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)
285 const uint8_t *buf8 = buf;
289 int rv = DNS_DecodeName(name_p, buf, ofs, space);
290 if(rv < 0) return -1;
292 ofs += rv, consumed += rv;
295 *type_p = get16(buf8 + ofs);
296 ofs += 2, consumed += 2;
299 *class_p = get16(buf8 + ofs);
300 ofs += 2, consumed += 2;
303 *ttl_p = get32(buf + ofs);
304 ofs += 4, consumed += 4;
306 size_t rdlength = get16(buf + ofs);
308 *rdlength_p = rdlength;
309 ofs += 2, consumed += 2;
311 _SysDebug("DNS_int_ParseRR - name='%s', rdlength=%i", name_p, rdlength);
313 return consumed + rdlength;
316 static uint16_t get16(const void *buf) {
317 const uint8_t* buf8 = buf;
319 rv |= (uint16_t)buf8[0] << 8;
320 rv |= (uint16_t)buf8[1] << 0;
323 static uint32_t get32(const void *buf) {
324 const uint8_t* buf8 = buf;
326 rv |= (uint32_t)buf8[0] << 24;
327 rv |= (uint32_t)buf8[1] << 16;
328 rv |= (uint32_t)buf8[2] << 8;
329 rv |= (uint32_t)buf8[3] << 0;
332 static size_t put16(void *buf, uint16_t val) {
335 buf8[1] = val & 0xFF;