]> jfr.im git - solanum.git/blob - ircd/dns.c
librb: linebuf: reduce the number of "put" implementations from 4 to 1
[solanum.git] / ircd / dns.c
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
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"
39 #include "s_assert.h"
40
41 #define DNS_HOST_IPV4 ((char)'4')
42 #define DNS_HOST_IPV6 ((char)'6')
43 #define DNS_REVERSE_IPV4 ((char)'R')
44 #define DNS_REVERSE_IPV6 ((char)'S')
45
46 static void submit_dns(uint32_t uid, char type, const char *addr);
47 static void submit_dns_stat(uint32_t uid);
48
49 struct dnsreq
50 {
51 DNSCB callback;
52 void *data;
53 };
54
55 struct dnsstatreq
56 {
57 DNSLISTCB callback;
58 void *data;
59 };
60
61 /* These serve as a form of sparse array */
62 static rb_dictionary *query_dict;
63 static rb_dictionary *stat_dict;
64
65 rb_dlink_list nameservers;
66
67 static uint32_t query_id = 0;
68 static uint32_t stat_id = 0;
69
70
71 static inline uint32_t
72 assign_id(uint32_t *id)
73 {
74 if(++(*id) == 0)
75 *id = 1;
76
77 return *id;
78 }
79
80 static void
81 handle_dns_failure(uint32_t xid)
82 {
83 struct dnsreq *req = rb_dictionary_retrieve(query_dict, RB_UINT_TO_POINTER(xid));
84 s_assert(req);
85
86 if(req == NULL || req->callback == NULL)
87 return;
88
89 req->callback("FAILED", 0, 0, req->data);
90 req->callback = NULL;
91 req->data = NULL;
92 }
93
94 static void
95 handle_dns_stat_failure(uint32_t xid)
96 {
97 struct dnsstatreq *req = rb_dictionary_retrieve(stat_dict, RB_UINT_TO_POINTER(xid));
98 s_assert(req);
99
100 if(req == NULL || req->callback == NULL)
101 return;
102
103 req->callback(1, NULL, 2, req->data);
104 req->callback = NULL;
105 req->data = NULL;
106 }
107
108
109 void
110 cancel_lookup(uint32_t xid)
111 {
112 struct dnsreq *req = rb_dictionary_retrieve(query_dict, RB_UINT_TO_POINTER(xid));
113 s_assert(req);
114
115 if (req == NULL)
116 return;
117
118 req->callback = NULL;
119 req->data = NULL;
120 }
121
122 void
123 cancel_dns_stats(uint32_t xid)
124 {
125 struct dnsstatreq *req = rb_dictionary_retrieve(stat_dict, RB_UINT_TO_POINTER(xid));
126 s_assert(req);
127
128 if (req == NULL)
129 return;
130
131 req->callback = NULL;
132 req->data = NULL;
133 }
134
135
136 uint32_t
137 lookup_hostname(const char *hostname, int aftype, DNSCB callback, void *data)
138 {
139 struct dnsreq *req = rb_malloc(sizeof(struct dnsreq));
140 int aft;
141 uint32_t rid = assign_id(&query_id);
142
143 check_authd();
144
145 rb_dictionary_add(query_dict, RB_UINT_TO_POINTER(rid), req);
146
147 req->callback = callback;
148 req->data = data;
149
150 #ifdef RB_IPV6
151 if(aftype == AF_INET6)
152 aft = 6;
153 else
154 #endif
155 aft = 4;
156
157 submit_dns(rid, aft == 4 ? DNS_HOST_IPV4 : DNS_HOST_IPV6, hostname);
158 return (rid);
159 }
160
161 uint32_t
162 lookup_ip(const char *addr, int aftype, DNSCB callback, void *data)
163 {
164 struct dnsreq *req = rb_malloc(sizeof(struct dnsreq));
165 int aft;
166 uint32_t rid = assign_id(&query_id);
167
168 check_authd();
169
170 rb_dictionary_add(query_dict, RB_UINT_TO_POINTER(rid), req);
171
172 req->callback = callback;
173 req->data = data;
174
175 #ifdef RB_IPV6
176 if(aftype == AF_INET6)
177 aft = 6;
178 else
179 #endif
180 aft = 4;
181
182 submit_dns(rid, aft == 4 ? DNS_REVERSE_IPV4 : DNS_REVERSE_IPV6, addr);
183 return (rid);
184 }
185
186 static uint32_t
187 get_nameservers(DNSLISTCB callback, void *data)
188 {
189 struct dnsstatreq *req = rb_malloc(sizeof(struct dnsstatreq));
190 uint32_t qid = assign_id(&stat_id);
191
192 check_authd();
193
194 rb_dictionary_add(stat_dict, RB_UINT_TO_POINTER(qid), req);
195
196 req->callback = callback;
197 req->data = data;
198
199 submit_dns_stat(qid);
200 return (qid);
201 }
202
203
204 void
205 dns_results_callback(const char *callid, const char *status, const char *type, const char *results)
206 {
207 struct dnsreq *req;
208 uint32_t rid;
209 int st;
210 int aft;
211 long lrid = strtol(callid, NULL, 16);
212
213 if(lrid > UINT32_MAX)
214 return;
215
216 rid = (uint32_t)lrid;
217 req = rb_dictionary_retrieve(query_dict, RB_UINT_TO_POINTER(rid));
218 if(req == NULL)
219 return;
220
221 st = (*status == 'O');
222 aft = *type == '6' || *type == 'S' ? 6 : 4;
223 if(req->callback == NULL)
224 {
225 /* got cancelled..oh well */
226 req->data = NULL;
227 return;
228 }
229 #ifdef RB_IPV6
230 if(aft == 6)
231 aft = AF_INET6;
232 else
233 #endif
234 aft = AF_INET;
235
236 req->callback(results, st, aft, req->data);
237
238 rb_free(req);
239 rb_dictionary_delete(query_dict, RB_UINT_TO_POINTER(rid));
240 }
241
242 void
243 dns_stats_results_callback(const char *callid, const char *status, int resc, const char *resv[])
244 {
245 struct dnsstatreq *req;
246 uint32_t qid;
247 int st;
248 long lqid = strtol(callid, NULL, 16);
249
250 if(lqid > UINT32_MAX)
251 return;
252
253 qid = (uint32_t)lqid;
254 req = rb_dictionary_retrieve(stat_dict, RB_UINT_TO_POINTER(qid));
255
256 s_assert(req);
257 if (req == NULL)
258 return;
259
260 if(req->callback == NULL)
261 {
262 req->data = NULL;
263 return;
264 }
265
266 switch(*status)
267 {
268 case 'Y':
269 st = 0;
270 break;
271 case 'X':
272 /* Error */
273 st = 1;
274 break;
275 default:
276 /* Shouldn't happen... */
277 return;
278 }
279
280 /* Query complete */
281 req->callback(resc, resv, st, req->data);
282
283 rb_free(req);
284 rb_dictionary_delete(stat_dict, RB_UINT_TO_POINTER(qid));
285 }
286
287 static void
288 stats_results_callback(int resc, const char *resv[], int status, void *data)
289 {
290 if(status == 0)
291 {
292 rb_dlink_node *n, *tn;
293
294 RB_DLINK_FOREACH_SAFE(n, tn, nameservers.head)
295 {
296 /* Clean up old nameservers */
297 rb_free(n->data);
298 rb_dlinkDestroy(n, &nameservers);
299 }
300
301 for(int i = 0; i < resc; i++)
302 rb_dlinkAddAlloc(rb_strdup(resv[i]), &nameservers);
303 }
304 else
305 {
306 const char *error = resc ? resv[resc - 1] : "Unknown error";
307 iwarn("Error getting DNS servers: %s", error);
308 }
309 }
310
311
312 void
313 init_dns(void)
314 {
315 query_dict = rb_dictionary_create("dns queries", rb_uint32cmp);
316 stat_dict = rb_dictionary_create("dns stat queries", rb_uint32cmp);
317 (void)get_nameservers(stats_results_callback, NULL);
318 }
319
320 void
321 reload_nameservers(void)
322 {
323 check_authd();
324 rb_helper_write(authd_helper, "R D");
325 (void)get_nameservers(stats_results_callback, NULL);
326 }
327
328
329 static void
330 submit_dns(uint32_t nid, char type, const char *addr)
331 {
332 if(authd_helper == NULL)
333 {
334 handle_dns_failure(nid);
335 return;
336 }
337 rb_helper_write(authd_helper, "D %x %c %s", nid, type, addr);
338 }
339
340 static void
341 submit_dns_stat(uint32_t nid)
342 {
343 if(authd_helper == NULL)
344 {
345 handle_dns_stat_failure(nid);
346 return;
347 }
348 rb_helper_write(authd_helper, "S %x D", nid);
349 }