-
Notifications
You must be signed in to change notification settings - Fork 5
/
dnstask.c
155 lines (137 loc) · 3.66 KB
/
dnstask.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*-
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <mph@hoth.dk> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return Martin Topholm
* ----------------------------------------------------------------------------
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <event2/event.h>
#include <event2/dns.h>
#include "xping.h"
#define MAXHOST 64
extern int v4_flag;
extern int v6_flag;
extern int T_flag;
extern struct event_base *ev_base;
extern struct evdns_base *dns;
struct dnstask {
dnstask_cb_type cb;
void *thunk;
char host[MAXHOST];
struct event *ev_resolve;
};
/*
* Reschedule a DNS request.
*/
static void
reschedule(struct event *ev_resolve, int seconds)
{
struct timeval tv;
evutil_timerclear(&tv);
tv.tv_sec = seconds;
event_add(ev_resolve, &tv);
}
/*
* Callback for A-records resolver results. When failed (NXDOMAIN,
* SERVFAIL, at al), reschedule a new request.
*/
static void
response_ipv4(int result, char type, int count, int ttl, void *addresses,
void *thunk)
{
struct dnstask *task = thunk;
if (result == DNS_ERR_NONE && count > 0) {
task->cb(AF_INET, addresses, task->thunk);
/* Schedule new request, enforce a lower bound on ttl. */
if (T_flag)
reschedule(task->ev_resolve, MAX(ttl, 1));
} else {
task->cb(0, NULL, task->thunk);
/* neg-TTL might be search domain's */
reschedule(task->ev_resolve, 60);
}
}
/*
* Callback for AAAA-records resolver results. When failed (NXDOMAIN,
* SERVFAIL, at al), retry for A-record. Otherwise reschedule a new
* request. All diagnostics about the failed request are useless, since
* they might refer to a search domain request, which is done transparently
* after the real request.
*/
static void
response_ipv6(int result, char type, int count, int ttl, void *addresses,
void *thunk)
{
struct dnstask *task = thunk;
if (result == DNS_ERR_NONE && count > 0) {
task->cb(AF_INET6, addresses, task->thunk);
/* Schedule new request, enforce a lower bound on ttl. */
if (T_flag)
reschedule(task->ev_resolve, MAX(ttl, 1));
} else {
if (!v6_flag) {
evdns_base_resolve_ipv4(dns, task->host, 0,
response_ipv4, thunk);
} else {
/* neg-TTL might be search domain's */
reschedule(task->ev_resolve, 60);
task->cb(0, NULL, task->thunk);
}
}
}
/*
* Send DNS request using evdns. Try IPv6 first unless IPv4 is
* forced. Missing AAAA-records are handled in response_ipv6.
*/
static void
sendquery(int fd, short what, void *thunk)
{
struct dnstask *task = thunk;
if (!v4_flag) {
evdns_base_resolve_ipv6(dns, task->host, 0,
response_ipv6, task);
} else {
evdns_base_resolve_ipv4(dns, task->host, 0,
response_ipv4, task);
}
}
/*
* Set up a dnstask for resolving a given hostname and schedule
* initial resolving.
*/
struct dnstask *
dnstask_new(const char *hostname, dnstask_cb_type cb, void *thunk)
{
struct dnstask *task;
struct timeval tv;
task = calloc(1, sizeof(*task));
if (task == NULL)
return NULL;
assert(strlen(hostname) + 1 <= sizeof(task->host));
strncat(task->host, hostname, sizeof(task->host) - 1);
task->cb = cb;
task->thunk = thunk;
task->ev_resolve = event_new(ev_base, -1, 0, sendquery, task);
if (task->ev_resolve == NULL) {
free(task);
return NULL;
}
evutil_timerclear(&tv);
event_add(task->ev_resolve, &tv);
return task;
}
/*
* Remove and free a previous dnstask.
*/
void
dnstask_free(struct dnstask *task)
{
event_free(task->ev_resolve);
free(task);
}