]>
Commit | Line | Data |
---|---|---|
7d2852b4 AC |
1 | /* |
2 | * dns.c: An interface to the resolver module in authd | |
3 | * (based somewhat on ircd-ratbox dns.c) | |
4 | * | |
5 | * Copyright (C) 2005 Aaron Sethman <androsyn@ratbox.org> | |
6 | * Copyright (C) 2005-2012 ircd-ratbox development team | |
7 | * Copyright (C) 2016 William Pitcock <nenolod@dereferenced.org> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 | |
22 | * USA | |
23 | */ | |
24 | ||
394b8dde EM |
25 | #include "stdinc.h" |
26 | #include "rb_lib.h" | |
27 | #include "client.h" | |
28 | #include "ircd_defs.h" | |
29 | #include "parse.h" | |
30 | #include "dns.h" | |
31 | #include "match.h" | |
32 | #include "logger.h" | |
33 | #include "s_conf.h" | |
34 | #include "client.h" | |
35 | #include "send.h" | |
36 | #include "numeric.h" | |
37 | #include "msg.h" | |
38 | #include "hash.h" | |
7d2852b4 | 39 | |
1bf29198 AC |
40 | #define DNS_HOST_IPV4 ((char)'4') |
41 | #define DNS_HOST_IPV6 ((char)'6') | |
42 | #define DNS_REVERSE_IPV4 ((char)'R') | |
43 | #define DNS_REVERSE_IPV6 ((char)'S') | |
7d2852b4 | 44 | |
068c6c4a EM |
45 | static void submit_dns(uint32_t uid, char type, const char *addr); |
46 | static void submit_dns_stat(uint32_t uid); | |
7d2852b4 | 47 | |
7d2852b4 AC |
48 | struct dnsreq |
49 | { | |
50 | DNSCB callback; | |
51 | void *data; | |
52 | }; | |
53 | ||
394b8dde EM |
54 | struct dnsstatreq |
55 | { | |
56 | DNSLISTCB callback; | |
57 | void *data; | |
58 | }; | |
59 | ||
068c6c4a | 60 | /* These serve as a form of sparse array */ |
4177311e EM |
61 | static rb_dictionary *query_dict; |
62 | static rb_dictionary *stat_dict; | |
068c6c4a | 63 | |
8a26cd19 EM |
64 | rb_dlink_list nameservers; |
65 | ||
068c6c4a EM |
66 | static uint32_t query_id = 0; |
67 | static uint32_t stat_id = 0; | |
7d2852b4 | 68 | |
068c6c4a | 69 | #define ASSIGN_ID(id) (id++) |
7d2852b4 | 70 | |
394b8dde | 71 | |
7d2852b4 | 72 | static void |
068c6c4a | 73 | handle_dns_failure(uint32_t xid) |
7d2852b4 | 74 | { |
068c6c4a EM |
75 | struct dnsreq *req = rb_dictionary_retrieve(query_dict, RB_UINT_TO_POINTER(xid)); |
76 | s_assert(req); | |
7d2852b4 | 77 | |
7d2852b4 AC |
78 | if(req->callback == NULL) |
79 | return; | |
80 | ||
81 | req->callback("FAILED", 0, 0, req->data); | |
82 | req->callback = NULL; | |
83 | req->data = NULL; | |
84 | } | |
85 | ||
394b8dde | 86 | static void |
068c6c4a | 87 | handle_dns_stat_failure(uint32_t xid) |
394b8dde | 88 | { |
068c6c4a EM |
89 | struct dnsstatreq *req = rb_dictionary_retrieve(stat_dict, RB_UINT_TO_POINTER(xid)); |
90 | s_assert(req); | |
394b8dde | 91 | |
394b8dde EM |
92 | if(req->callback == NULL) |
93 | return; | |
94 | ||
068c6c4a | 95 | req->callback(1, NULL, 2, req->data); |
394b8dde EM |
96 | req->callback = NULL; |
97 | req->data = NULL; | |
98 | } | |
99 | ||
068c6c4a | 100 | |
7d2852b4 | 101 | void |
068c6c4a | 102 | cancel_lookup(uint32_t xid) |
7d2852b4 | 103 | { |
068c6c4a EM |
104 | struct dnsreq *req = rb_dictionary_retrieve(query_dict, RB_UINT_TO_POINTER(xid)); |
105 | s_assert(req); | |
106 | req->callback = NULL; | |
107 | req->data = NULL; | |
7d2852b4 AC |
108 | } |
109 | ||
394b8dde | 110 | void |
068c6c4a | 111 | cancel_dns_stats(uint32_t xid) |
394b8dde | 112 | { |
068c6c4a EM |
113 | struct dnsstatreq *req = rb_dictionary_retrieve(stat_dict, RB_UINT_TO_POINTER(xid)); |
114 | s_assert(req); | |
115 | req->callback = NULL; | |
116 | req->data = NULL; | |
394b8dde EM |
117 | } |
118 | ||
068c6c4a EM |
119 | |
120 | uint32_t | |
7d2852b4 AC |
121 | lookup_hostname(const char *hostname, int aftype, DNSCB callback, void *data) |
122 | { | |
068c6c4a | 123 | struct dnsreq *req = rb_malloc(sizeof(struct dnsreq)); |
7d2852b4 | 124 | int aft; |
068c6c4a EM |
125 | uint32_t rid = ASSIGN_ID(query_id); |
126 | ||
7d2852b4 | 127 | check_authd(); |
7d2852b4 | 128 | |
068c6c4a | 129 | rb_dictionary_add(query_dict, RB_UINT_TO_POINTER(rid), req); |
7d2852b4 AC |
130 | |
131 | req->callback = callback; | |
132 | req->data = data; | |
133 | ||
134 | #ifdef RB_IPV6 | |
135 | if(aftype == AF_INET6) | |
136 | aft = 6; | |
137 | else | |
138 | #endif | |
139 | aft = 4; | |
140 | ||
068c6c4a EM |
141 | submit_dns(rid, aft == 4 ? DNS_HOST_IPV4 : DNS_HOST_IPV6, hostname); |
142 | return (rid); | |
7d2852b4 AC |
143 | } |
144 | ||
068c6c4a | 145 | uint32_t |
7d2852b4 AC |
146 | lookup_ip(const char *addr, int aftype, DNSCB callback, void *data) |
147 | { | |
068c6c4a | 148 | struct dnsreq *req = rb_malloc(sizeof(struct dnsreq)); |
7d2852b4 | 149 | int aft; |
068c6c4a | 150 | uint32_t rid = ASSIGN_ID(query_id); |
7d2852b4 | 151 | |
068c6c4a | 152 | check_authd(); |
394b8dde | 153 | |
068c6c4a | 154 | rb_dictionary_add(query_dict, RB_UINT_TO_POINTER(rid), req); |
7d2852b4 AC |
155 | |
156 | req->callback = callback; | |
157 | req->data = data; | |
158 | ||
159 | #ifdef RB_IPV6 | |
160 | if(aftype == AF_INET6) | |
161 | aft = 6; | |
162 | else | |
163 | #endif | |
164 | aft = 4; | |
165 | ||
068c6c4a EM |
166 | submit_dns(rid, aft == 4 ? DNS_REVERSE_IPV4 : DNS_REVERSE_IPV6, addr); |
167 | return (rid); | |
7d2852b4 AC |
168 | } |
169 | ||
068c6c4a | 170 | uint32_t |
394b8dde EM |
171 | get_nameservers(DNSLISTCB callback, void *data) |
172 | { | |
068c6c4a EM |
173 | struct dnsstatreq *req = rb_malloc(sizeof(struct dnsstatreq)); |
174 | uint32_t qid = ASSIGN_ID(stat_id); | |
175 | ||
394b8dde EM |
176 | check_authd(); |
177 | ||
068c6c4a | 178 | rb_dictionary_add(stat_dict, RB_UINT_TO_POINTER(qid), req); |
394b8dde | 179 | |
394b8dde EM |
180 | req->callback = callback; |
181 | req->data = data; | |
182 | ||
068c6c4a EM |
183 | submit_dns_stat(qid); |
184 | return (qid); | |
394b8dde EM |
185 | } |
186 | ||
068c6c4a | 187 | |
fb7d74ef | 188 | void |
1bf29198 | 189 | dns_results_callback(const char *callid, const char *status, const char *type, const char *results) |
7d2852b4 AC |
190 | { |
191 | struct dnsreq *req; | |
068c6c4a | 192 | uint32_t rid; |
7d2852b4 AC |
193 | int st; |
194 | int aft; | |
068c6c4a | 195 | long lrid = strtol(callid, NULL, 16); |
7d2852b4 | 196 | |
068c6c4a | 197 | if(lrid > UINT32_MAX) |
7d2852b4 | 198 | return; |
068c6c4a EM |
199 | |
200 | rid = (uint32_t)lrid; | |
201 | req = rb_dictionary_retrieve(query_dict, RB_UINT_TO_POINTER(rid)); | |
202 | if(req == NULL) | |
203 | return; | |
204 | ||
394b8dde | 205 | st = (*status == 'O'); |
1bf29198 | 206 | aft = *type == '6' || *type == 'S' ? 6 : 4; |
7d2852b4 AC |
207 | if(req->callback == NULL) |
208 | { | |
209 | /* got cancelled..oh well */ | |
210 | req->data = NULL; | |
211 | return; | |
212 | } | |
213 | #ifdef RB_IPV6 | |
214 | if(aft == 6) | |
215 | aft = AF_INET6; | |
216 | else | |
217 | #endif | |
218 | aft = AF_INET; | |
219 | ||
220 | req->callback(results, st, aft, req->data); | |
068c6c4a EM |
221 | |
222 | rb_free(req); | |
223 | rb_dictionary_delete(query_dict, RB_UINT_TO_POINTER(rid)); | |
7d2852b4 AC |
224 | } |
225 | ||
7d2852b4 | 226 | void |
394b8dde | 227 | dns_stats_results_callback(const char *callid, const char *status, int resc, const char *resv[]) |
7d2852b4 | 228 | { |
394b8dde | 229 | struct dnsstatreq *req; |
068c6c4a | 230 | uint32_t qid; |
e23126c8 | 231 | int st; |
068c6c4a | 232 | long lqid = strtol(callid, NULL, 16); |
394b8dde | 233 | |
068c6c4a | 234 | if(lqid > UINT32_MAX) |
394b8dde | 235 | return; |
068c6c4a EM |
236 | |
237 | qid = (uint32_t)lqid; | |
238 | req = rb_dictionary_retrieve(stat_dict, RB_UINT_TO_POINTER(qid)); | |
239 | ||
240 | s_assert(req); | |
394b8dde EM |
241 | |
242 | if(req->callback == NULL) | |
7d2852b4 | 243 | { |
394b8dde EM |
244 | req->data = NULL; |
245 | return; | |
7d2852b4 | 246 | } |
394b8dde EM |
247 | |
248 | switch(*status) | |
249 | { | |
250 | case 'Y': | |
251 | st = 0; | |
252 | break; | |
253 | case 'X': | |
254 | /* Error */ | |
255 | st = 1; | |
256 | break; | |
257 | default: | |
258 | /* Shouldn't happen... */ | |
259 | return; | |
260 | } | |
261 | ||
262 | /* Query complete */ | |
068c6c4a | 263 | req->callback(resc, resv, st, req->data); |
394b8dde | 264 | |
068c6c4a EM |
265 | rb_free(req); |
266 | rb_dictionary_delete(stat_dict, RB_UINT_TO_POINTER(qid)); | |
394b8dde EM |
267 | } |
268 | ||
269 | static void | |
068c6c4a | 270 | stats_results_callback(int resc, const char *resv[], int status, void *data) |
394b8dde | 271 | { |
394b8dde EM |
272 | if(status == 0) |
273 | { | |
8a26cd19 EM |
274 | rb_dlink_node *n, *tn; |
275 | ||
276 | RB_DLINK_FOREACH_SAFE(n, tn, nameservers.head) | |
277 | { | |
278 | /* Clean up old nameservers */ | |
279 | rb_free(n->data); | |
280 | rb_dlinkDestroy(n, &nameservers); | |
281 | } | |
282 | ||
394b8dde | 283 | for(int i = 0; i < resc; i++) |
8a26cd19 | 284 | rb_dlinkAddAlloc(rb_strdup(resv[i]), &nameservers); |
394b8dde EM |
285 | } |
286 | else | |
287 | { | |
8a26cd19 | 288 | const char *error = resc ? resv[resc] : "Unknown error"; |
881acf00 | 289 | iwarn("Error getting DNS servers: %s", error); |
394b8dde | 290 | } |
394b8dde EM |
291 | } |
292 | ||
068c6c4a | 293 | |
8a26cd19 | 294 | void |
068c6c4a | 295 | init_dns(void) |
8a26cd19 | 296 | { |
068c6c4a EM |
297 | query_dict = rb_dictionary_create("dns queries", rb_uint32cmp); |
298 | stat_dict = rb_dictionary_create("dns stat queries", rb_uint32cmp); | |
299 | (void)get_nameservers(stats_results_callback, NULL); | |
7d2852b4 AC |
300 | } |
301 | ||
068c6c4a | 302 | void |
6445c1cf EM |
303 | reload_nameservers(void) |
304 | { | |
068c6c4a | 305 | check_authd(); |
122ae255 | 306 | rb_helper_write(authd_helper, "R D"); |
068c6c4a | 307 | (void)get_nameservers(stats_results_callback, NULL); |
6445c1cf EM |
308 | } |
309 | ||
310 | ||
7d2852b4 | 311 | static void |
068c6c4a | 312 | submit_dns(uint32_t nid, char type, const char *addr) |
7d2852b4 AC |
313 | { |
314 | if(authd_helper == NULL) | |
315 | { | |
316 | handle_dns_failure(nid); | |
317 | return; | |
318 | } | |
1bf29198 | 319 | rb_helper_write(authd_helper, "D %x %c %s", nid, type, addr); |
7d2852b4 | 320 | } |
394b8dde EM |
321 | |
322 | static void | |
068c6c4a | 323 | submit_dns_stat(uint32_t nid) |
394b8dde EM |
324 | { |
325 | if(authd_helper == NULL) | |
326 | { | |
327 | handle_dns_stat_failure(nid); | |
328 | return; | |
329 | } | |
330 | rb_helper_write(authd_helper, "S %x D", nid); | |
331 | } |