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
13 #include "include/dns.h"
16 size_t DNS_EncodeName(void *buf, const char *dotted_name);
17 int DNS_DecodeName(char dotted_name[256], const void *buf, size_t space);
18 int DNS_int_ParseRR(const void *buf, size_t space, char* name_p, enum eTypes* type_p, enum eClass* class_p, uint32_t* ttl_p, size_t* rdlength_p);
19 static uint16_t get16(const void *buf);
20 static size_t put16(void *buf, uint16_t val);
24 int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eTypes type, enum eClass class, handle_record_t* handle_record, void *info)
26 int namelen = DNS_EncodeName(NULL, name);
27 assert(namelen < 256);
30 assert( (6*2) + (namelen + 2*2) < 512 );
32 pos += put16(packet + pos, 0xAC00); // Identifier (arbitary)
33 pos += put16(packet + pos, (0 << 0) | (0 << 1) ); // Op : Query, Standard, no other flags
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 <= sizeof(packet));
45 // Send and wait for reply
47 // > TODO: Lock DNS queries
49 int sock = Net_OpenSocket_UDP(ServerAType, ServerAddr, 53, 0);
52 // TODO: Correctly report this failure with a useful error code
55 int rv = _SysWrite(sock, packet, pos);
57 // TODO: Error reporting
64 return_len = _SysRead(sock, packet, sizeof(packet));
65 } while( return_len == 0 );
66 if( return_len < 0 ) {
67 // TODO: Error reporting
73 // > TODO: Lock DNS queries
75 // For each response in the answer (and additional) sections, call the passed callback
77 unsigned int qd_count = get16(packet + 4);
78 unsigned int an_count = get16(packet + 6);
79 unsigned int ns_count = get16(packet + 8);
80 unsigned int ar_count = get16(packet + 10);
82 // TODO: Can I safely assert / fail if qd_count is non-zero?
83 // - Questions, ignored
84 for( unsigned int i = 0; i < qd_count; i ++ ) {
85 pos += DNS_DecodeName(NULL, packet + pos, return_len - pos);
88 // - Answers, pass on to handler
89 for( unsigned int i = 0; i < an_count; i ++ )
95 int rv = DNS_int_ParseRR(packet + pos, return_len - pos, rr_name, &type, &class, &ttl, &rdlength);
101 handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos);
103 // Authority Records (should all be NS records)
104 for( unsigned int i = 0; i < ns_count; i ++ )
107 int rv = DNS_int_ParseRR(packet + pos, return_len - pos, rr_name, NULL, NULL, NULL, &rdlength);
113 // - Additional records, pass to handler
114 for( unsigned int i = 0; i < ar_count; i ++ )
120 int rv = DNS_int_ParseRR(packet + pos, return_len - pos, rr_name, &type, &class, &ttl, &rdlength);
126 handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos);
132 /// Encode a dotted name as a DNS name
133 size_t DNS_EncodeName(void *buf, const char *dotted_name)
136 const char *str = dotted_name;
140 const char *next = strchr(str, '.');
141 size_t seg_len = (next ? next - str : strlen(str));
143 // Oops, too long (truncate)
146 if( seg_len == 0 && next != NULL ) {
147 // '..' encountered, invalid (skip)
155 memcpy(buf8+ret+1, str, seg_len);
160 // No trailing '.', assume it's there? Yes, need to be NUL terminated
161 if(buf8) buf8[ret] = 0;
172 // Decode a name (including trailing . for root)
173 int DNS_DecodeName(char dotted_name[256], const void *buf, size_t space)
177 const uint8_t *buf8 = buf;
178 while( *buf8 && space > 0 )
180 if( consumed + 1 > space ) return -1;
181 uint8_t seg_len = *buf8;
184 // Protocol violation (overflowed end of buffer)
185 if( consumed + seg_len > space )
187 // Protocol violation (segment too long)
190 // Protocol violation (name was too long)
191 if( out_pos + seg_len + 1 > sizeof(dotted_name)-1 )
195 memcpy(dotted_name + out_pos, buf8, seg_len);
200 dotted_name[out_pos+seg_len+1] = '.';
201 // Increment output counter
202 out_pos += seg_len + 1;
205 dotted_name[out_pos] = '\0';
209 // Parse a Resource Record
210 int DNS_int_ParseRR(const void *buf, size_t space, char* name_p, enum eTypes* type_p, enum eClass* class_p, uint32_t* ttl_p, size_t* rdlength_p)
212 const uint8_t *buf8 = buf;
216 int rv = DNS_DecodeName(name_p, buf8, space);
217 if(rv < 0) return -1;
219 buf8 += rv, consumed += rv;
222 *type_p = get16(buf8);
223 buf8 += 2, consumed += 2;
226 *class_p = get16(buf8);
227 buf8 += 2, consumed += 2;
232 static uint16_t get16(const void *buf) {
233 const uint8_t* buf8 = buf;
236 rv |= (uint16_t)buf8[1] << 8;
239 static size_t put16(void *buf, uint16_t val) {
241 buf8[0] = val & 0xFF;