1 |
#include <stdio.h> |
2 |
#include <string.h> |
3 |
#include <sys/socket.h> |
4 |
#include <netinet/in.h> |
5 |
#include "my_socket.h" |
6 |
|
7 |
#define DNS_IPV4 1 |
8 |
#define DNS_IPV6 2 |
9 |
#define DNS_REVERSE 3 |
10 |
|
11 |
typedef struct dns_query { |
12 |
struct dns_query *next; |
13 |
char *query; |
14 |
int id; |
15 |
int (*callback)(void *client_data, const char *query, const char *result); |
16 |
void *client_data; |
17 |
} dns_query_t; |
18 |
|
19 |
typedef struct { |
20 |
unsigned short id; |
21 |
unsigned short flags; |
22 |
unsigned short question_count; |
23 |
unsigned short answer_count; |
24 |
unsigned short ns_count; |
25 |
unsigned short ar_count; |
26 |
} dns_header_t; |
27 |
|
28 |
typedef struct { |
29 |
/* char name[]; */ |
30 |
unsigned short type; |
31 |
unsigned short class; |
32 |
int ttl; |
33 |
unsigned short rdlength; |
34 |
/* char rdata[]; */ |
35 |
} dns_rr_t; |
36 |
|
37 |
/* Entries from resolv.conf */ |
38 |
typedef struct dns_server { |
39 |
char *ip; |
40 |
int idx; |
41 |
} dns_server_t; |
42 |
|
43 |
/* Entries from hosts */ |
44 |
typedef struct { |
45 |
char *host, *ip; |
46 |
} dns_host_t; |
47 |
|
48 |
static int query_id = 1; |
49 |
static dns_header_t _dns_header = {0}; |
50 |
static dns_query_t *query_head = NULL; |
51 |
static dns_host_t *hosts = NULL; |
52 |
static int nhosts = 0; |
53 |
static dns_server_t *servers = NULL; |
54 |
static int nservers = 0; |
55 |
static int curserver = -1; |
56 |
|
57 |
static char separators[] = " ,\t"; |
58 |
|
59 |
static void read_resolv(char *fname); |
60 |
static void read_hosts(char *fname); |
61 |
|
62 |
/* Read in .hosts and /etc/hosts and .resolv.conf and /etc/resolv.conf */ |
63 |
int dns_init() |
64 |
{ |
65 |
FILE *fp; |
66 |
_dns_header.flags = htons(1 << 8 | 1 << 7); |
67 |
_dns_header.question_count = htons(1); |
68 |
read_resolv("/etc/resolv.conf"); |
69 |
read_resolv(".resolv.conf"); |
70 |
read_hosts("/etc/hosts"); |
71 |
read_hosts(".hosts"); |
72 |
return(0); |
73 |
} |
74 |
|
75 |
const char *dns_next_server() |
76 |
{ |
77 |
static int cur_server = 0; |
78 |
|
79 |
if (!servers || nservers < 1) return("127.0.0.1"); |
80 |
if (cur_server >= nservers) cur_server = 0; |
81 |
return(servers[cur_server].ip); |
82 |
} |
83 |
|
84 |
static void add_server(char *ip) |
85 |
{ |
86 |
servers = (dns_server_t *)realloc(servers, (nservers+1)*sizeof(*servers)); |
87 |
servers[nservers].ip = strdup(ip); |
88 |
servers[nservers].idx = -1; |
89 |
nservers++; |
90 |
} |
91 |
|
92 |
static void add_host(char *host, char *ip) |
93 |
{ |
94 |
hosts = (dns_host_t *)realloc(hosts, (nhosts+1)*sizeof(*hosts)); |
95 |
hosts[nhosts].host = strdup(host); |
96 |
hosts[nhosts].ip = strdup(ip); |
97 |
nhosts++; |
98 |
} |
99 |
|
100 |
static int read_thing(char *buf, char *ip) |
101 |
{ |
102 |
int skip, len; |
103 |
|
104 |
skip = strspn(buf, separators); |
105 |
buf += skip; |
106 |
len = strcspn(buf, separators); |
107 |
memcpy(ip, buf, len); |
108 |
ip[len] = 0; |
109 |
return(skip + len); |
110 |
} |
111 |
|
112 |
static void read_resolv(char *fname) |
113 |
{ |
114 |
FILE *fp; |
115 |
char buf[512], ip[512]; |
116 |
|
117 |
fp = fopen(fname, "r"); |
118 |
if (!fp) return; |
119 |
while (fgets(buf, sizeof(buf), fp)) { |
120 |
if (!strncasecmp(buf, "nameserver", 10)) { |
121 |
read_thing(buf+10, ip); |
122 |
if (strlen(ip)) add_server(ip); |
123 |
} |
124 |
} |
125 |
fclose(fp); |
126 |
} |
127 |
|
128 |
static void read_hosts(char *fname) |
129 |
{ |
130 |
FILE *fp; |
131 |
char buf[512], ip[512], host[512]; |
132 |
int skip, n; |
133 |
|
134 |
fp = fopen(fname, "r"); |
135 |
if (!fp) return; |
136 |
while (fgets(buf, sizeof(buf), fp)) { |
137 |
if (strchr(buf, '#')) continue; |
138 |
skip = read_thing(buf, ip); |
139 |
if (!strlen(ip)) continue; |
140 |
while (n = read_thing(buf+skip, host)) { |
141 |
skip += n; |
142 |
if (strlen(host)) add_host(ip, host); |
143 |
} |
144 |
} |
145 |
} |
146 |
|
147 |
static int make_header(char *buf, int id) |
148 |
{ |
149 |
_dns_header.id = htons(id); |
150 |
memcpy(buf, &_dns_header, 12); |
151 |
return(12); |
152 |
} |
153 |
|
154 |
static int cut_host(char *host, char *query) |
155 |
{ |
156 |
char *period, *orig; |
157 |
int len; |
158 |
|
159 |
orig = query; |
160 |
while (period = strchr(host, '.')) { |
161 |
len = period - host; |
162 |
if (len > 63) return(-1); |
163 |
*query++ = len; |
164 |
memcpy(query, host, len); |
165 |
query += len; |
166 |
host = period+1; |
167 |
} |
168 |
len = strlen(host); |
169 |
if (len) { |
170 |
*query++ = len; |
171 |
memcpy(query, host, len); |
172 |
query += len; |
173 |
} |
174 |
*query++ = 0; |
175 |
return(query-orig); |
176 |
} |
177 |
|
178 |
static int reverse_ip(char *host, char *reverse) |
179 |
{ |
180 |
char *period; |
181 |
int offset, len; |
182 |
|
183 |
printf("reversing %s\n", host); |
184 |
period = strchr(host, '.'); |
185 |
if (!period) { |
186 |
len = strlen(host); |
187 |
memcpy(reverse, host, len); |
188 |
return(len); |
189 |
} |
190 |
else { |
191 |
len = period - host; |
192 |
offset = reverse_ip(host+len+1, reverse); |
193 |
reverse[offset++] = '.'; |
194 |
memcpy(reverse+offset, host, len); |
195 |
reverse[offset+len] = 0; |
196 |
return(offset+len); |
197 |
} |
198 |
} |
199 |
|
200 |
int dns_make_query(char *host, int type, char **buf, int *query_len, int (*callback)(), void *client_data) |
201 |
{ |
202 |
char *newhost = NULL; |
203 |
int len = 0; |
204 |
int ns_type = 0; |
205 |
dns_query_t *q; |
206 |
|
207 |
if (type == DNS_IPV4) ns_type = 1; /* IPv4 */ |
208 |
else if (type == DNS_IPV6) ns_type = 28; /* IPv6 */ |
209 |
else if (type == DNS_REVERSE) { |
210 |
/* We need to transform the ip address into the proper form |
211 |
* for reverse lookup. */ |
212 |
newhost = (char *)malloc(strlen(host) + 14); |
213 |
reverse_ip(host, newhost); |
214 |
strcat(newhost, ".in-addr.arpa"); |
215 |
printf("newhost: %s\n", newhost); |
216 |
host = newhost; |
217 |
ns_type = 12; /* PTR (reverse lookup) */ |
218 |
} |
219 |
else return(-1); |
220 |
|
221 |
*buf = (char *)malloc(strlen(host) + 512); |
222 |
len = make_header(*buf, query_id); |
223 |
len += cut_host(host, *buf + len); |
224 |
(*buf)[len] = 0; len++; (*buf)[len] = ns_type; len++; |
225 |
(*buf)[len] = 0; len++; (*buf)[len] = 1; len++; |
226 |
if (newhost) free(newhost); |
227 |
*query_len = len; |
228 |
|
229 |
q = calloc(1, sizeof(*q)); |
230 |
q->id = query_id; |
231 |
query_id++; |
232 |
q->callback = callback; |
233 |
q->client_data = client_data; |
234 |
if (query_head) q->next = query_head->next; |
235 |
query_head = q; |
236 |
return(q->id); |
237 |
} |
238 |
|
239 |
static int dns_cancel_query(int id, int issue_callback) |
240 |
{ |
241 |
dns_query_t *q, *prev; |
242 |
|
243 |
prev = NULL; |
244 |
for (q = query_head; q; q = q->next) { |
245 |
if (q->id == id) break; |
246 |
prev = q; |
247 |
} |
248 |
if (!q) return(-1); |
249 |
if (prev) prev->next = q->next; |
250 |
else query_head = q->next; |
251 |
|
252 |
if (issue_callback) q->callback(q->client_data, q->query, NULL); |
253 |
free(q); |
254 |
return(0); |
255 |
} |
256 |
|
257 |
static int skip_name(char *ptr, char *end) |
258 |
{ |
259 |
int len; |
260 |
char *start = ptr; |
261 |
|
262 |
while ((len = *ptr++) > 0) { |
263 |
if (len > 63) { |
264 |
ptr++; |
265 |
break; |
266 |
} |
267 |
else { |
268 |
ptr += len; |
269 |
} |
270 |
} |
271 |
return(ptr - start); |
272 |
} |
273 |
|
274 |
static void got_answer(int id, char *answer) |
275 |
{ |
276 |
dns_query_t *q, *prev; |
277 |
|
278 |
printf("got_answer for id %d: %s\n", id, answer); |
279 |
prev = NULL; |
280 |
for (q = query_head; q; q = q->next) { |
281 |
if (q->id == id) break; |
282 |
prev = q; |
283 |
} |
284 |
if (!q) return; |
285 |
|
286 |
if (prev) prev->next = q->next; |
287 |
else query_head = q->next; |
288 |
|
289 |
q->callback(q->client_data, q->query, answer); |
290 |
free(q->query); |
291 |
free(q); |
292 |
} |
293 |
|
294 |
static void parse_reply(char *response, int nbytes) |
295 |
{ |
296 |
dns_header_t header; |
297 |
dns_rr_t reply; |
298 |
char result[512]; |
299 |
char *ptr, *end; |
300 |
int i; |
301 |
|
302 |
ptr = response; |
303 |
memcpy(&header, ptr, 12); |
304 |
ptr += 12; |
305 |
|
306 |
header.id = ntohs(header.id); |
307 |
header.question_count = ntohs(header.question_count); |
308 |
header.answer_count = ntohs(header.answer_count); |
309 |
|
310 |
/* Pass over the question. */ |
311 |
ptr += skip_name(ptr, end); |
312 |
ptr += 4; |
313 |
/* End of question. */ |
314 |
|
315 |
for (i = 0; i < header.answer_count; i++) { |
316 |
result[0] = 0; |
317 |
/* Read in the answer. */ |
318 |
ptr += skip_name(ptr, end); |
319 |
memcpy(&reply, ptr, 10); |
320 |
reply.type = ntohs(reply.type); |
321 |
reply.rdlength = ntohs(reply.rdlength); |
322 |
ptr += 10; |
323 |
if (reply.type == 1) { |
324 |
//printf("ipv4 reply\n"); |
325 |
inet_ntop(AF_INET, ptr, result, 512); |
326 |
got_answer(header.id, result); |
327 |
return; |
328 |
} |
329 |
else if (reply.type == 28) { |
330 |
//printf("ipv6 reply\n"); |
331 |
inet_ntop(AF_INET6, ptr, result, 512); |
332 |
got_answer(header.id, result); |
333 |
return; |
334 |
} |
335 |
else if (reply.type == 12) { |
336 |
char *placeholder; |
337 |
int len, dot; |
338 |
|
339 |
//printf("reverse-lookup reply\n"); |
340 |
placeholder = ptr; |
341 |
result[0] = 0; |
342 |
while ((len = *ptr++) != 0) { |
343 |
if (len > 63) { |
344 |
ptr++; |
345 |
break; |
346 |
} |
347 |
else { |
348 |
dot = ptr[len]; |
349 |
ptr[len] = 0; |
350 |
strcat(result, ptr); |
351 |
strcat(result, "."); |
352 |
ptr[len] = dot; |
353 |
ptr += len; |
354 |
} |
355 |
} |
356 |
if (strlen(result)) { |
357 |
result[strlen(result)-1] = 0; |
358 |
got_answer(header.id, result); |
359 |
return; |
360 |
} |
361 |
ptr = placeholder; |
362 |
} |
363 |
ptr += reply.rdlength; |
364 |
} |
365 |
got_answer(header.id, NULL); |
366 |
} |
367 |
|
368 |
int dns_lookup(const char *host, int (*callback)()) |
369 |
{ |
370 |
} |
371 |
|
372 |
main (int argc, char *argv[]) |
373 |
{ |
374 |
char *query, response[512], *ptr, buf[512]; |
375 |
int i, len, sock; |
376 |
struct sockaddr_in server; |
377 |
dns_header_t header; |
378 |
dns_rr_t reply; |
379 |
unsigned long addr; |
380 |
|
381 |
if (argc != 3) { |
382 |
printf("usage: %s <host> <type>\n", argv[0]); |
383 |
printf(" <type> can be 1 (ipv4), 2 (ipv4), or 3 (reverse lookup)\n"); |
384 |
return(0); |
385 |
} |
386 |
|
387 |
dns_init(); |
388 |
if (!nservers) return(0); |
389 |
server.sin_family = AF_INET; |
390 |
server.sin_port = htons(53); |
391 |
server.sin_addr.s_addr = inet_addr(servers[0].ip); |
392 |
|
393 |
len = dns_make_query(argv[1], atoi(argv[2]), &query); |
394 |
|
395 |
sock = socket(AF_INET, SOCK_DGRAM, 0); |
396 |
|
397 |
if (sock < 0) { |
398 |
perror("socket"); |
399 |
return(1); |
400 |
} |
401 |
|
402 |
connect(sock, (struct sockaddr *)&server, sizeof(server)); |
403 |
write(sock, query, len); |
404 |
len = read(sock, response, 512); |
405 |
printf("parsing reply, %d bytes\n", len); |
406 |
parse_reply(response, len); |
407 |
write(sock, query, len); |
408 |
len = read(sock, response, 512); |
409 |
printf("parsing next reply, %d bytes\n", len); |
410 |
parse_reply(response, len); |
411 |
return(0); |
412 |
} |