]>
Commit | Line | Data |
---|---|---|
1 | /* authd/dns.c - authd DNS functions | |
2 | * Copyright (c) 2016 Ariadne Conill <ariadne@dereferenced.org> | |
3 | * | |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice is present in all copies. | |
7 | * | |
8 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
9 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
10 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
11 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | |
12 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
13 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
14 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
15 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
16 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
17 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
18 | * POSSIBILITY OF SUCH DAMAGE. | |
19 | */ | |
20 | ||
21 | #include "authd.h" | |
22 | #include "dns.h" | |
23 | #include "notice.h" | |
24 | #include "res.h" | |
25 | ||
26 | static void handle_lookup_ip_reply(void *data, struct DNSReply *reply); | |
27 | static void handle_lookup_hostname_reply(void *data, struct DNSReply *reply); | |
28 | ||
29 | uint64_t query_count = 0; | |
30 | ||
31 | /* A bit different from ircd... you just get a dns_query object. | |
32 | * | |
33 | * It gets freed whenever the res code gets back to us. | |
34 | */ | |
35 | struct dns_query * | |
36 | lookup_ip(const char *host, int aftype, DNSCB callback, void *data) | |
37 | { | |
38 | struct dns_query *query = rb_malloc(sizeof(struct dns_query)); | |
39 | int g_type; | |
40 | ||
41 | if(aftype == AF_INET) | |
42 | { | |
43 | query->type = QUERY_A; | |
44 | g_type = T_A; | |
45 | } | |
46 | else if(aftype == AF_INET6) | |
47 | { | |
48 | query->type = QUERY_AAAA; | |
49 | g_type = T_AAAA; | |
50 | } | |
51 | else | |
52 | { | |
53 | rb_free(query); | |
54 | return NULL; | |
55 | } | |
56 | ||
57 | query->id = query_count++; | |
58 | query->callback = callback; | |
59 | query->data = data; | |
60 | ||
61 | query->query.ptr = query; | |
62 | query->query.callback = handle_lookup_ip_reply; | |
63 | ||
64 | gethost_byname_type(host, &query->query, g_type); | |
65 | ||
66 | return query; | |
67 | } | |
68 | ||
69 | /* See lookup_ip's comment */ | |
70 | struct dns_query * | |
71 | lookup_hostname(const char *ip, DNSCB callback, void *data) | |
72 | { | |
73 | struct dns_query *query = rb_malloc(sizeof(struct dns_query)); | |
74 | int aftype; | |
75 | ||
76 | if(!rb_inet_pton_sock(ip, &query->addr)) | |
77 | { | |
78 | rb_free(query); | |
79 | return NULL; | |
80 | } | |
81 | ||
82 | aftype = GET_SS_FAMILY(&query->addr); | |
83 | ||
84 | if(aftype == AF_INET) | |
85 | query->type = QUERY_PTR_A; | |
86 | else if(aftype == AF_INET6) | |
87 | query->type = QUERY_PTR_AAAA; | |
88 | else | |
89 | { | |
90 | rb_free(query); | |
91 | return NULL; | |
92 | } | |
93 | ||
94 | query->id = query_count++; | |
95 | query->callback = callback; | |
96 | query->data = data; | |
97 | ||
98 | query->query.ptr = query; | |
99 | query->query.callback = handle_lookup_hostname_reply; | |
100 | ||
101 | gethost_byaddr(&query->addr, &query->query); | |
102 | ||
103 | return query; | |
104 | } | |
105 | ||
106 | /* Cancel a pending query */ | |
107 | void | |
108 | cancel_query(struct dns_query *query) | |
109 | { | |
110 | query->callback = query->data = NULL; | |
111 | } | |
112 | ||
113 | /* Callback from gethost_byname_type */ | |
114 | static void | |
115 | handle_lookup_ip_reply(void *data, struct DNSReply *reply) | |
116 | { | |
117 | struct dns_query *query = data; | |
118 | char ip[HOSTIPLEN] = "*"; | |
119 | ||
120 | if(query == NULL) | |
121 | { | |
122 | /* Shouldn't happen */ | |
123 | warn_opers(L_CRIT, "DNS: handle_lookup_ip_reply: query == NULL!"); | |
124 | exit(EX_DNS_ERROR); | |
125 | } | |
126 | ||
127 | if(reply == NULL) | |
128 | goto end; | |
129 | ||
130 | switch(query->type) | |
131 | { | |
132 | case QUERY_A: | |
133 | if(GET_SS_FAMILY(&reply->addr) == AF_INET) | |
134 | rb_inet_ntop_sock((struct sockaddr *)&reply->addr, ip, sizeof(ip)); | |
135 | break; | |
136 | case QUERY_AAAA: | |
137 | if(GET_SS_FAMILY(&reply->addr) == AF_INET6) | |
138 | { | |
139 | rb_inet_ntop_sock((struct sockaddr *)&reply->addr, ip, sizeof(ip)); | |
140 | if(ip[0] == ':') | |
141 | { | |
142 | memmove(&ip[1], ip, strlen(ip)); | |
143 | ip[0] = '0'; | |
144 | } | |
145 | } | |
146 | break; | |
147 | default: | |
148 | warn_opers(L_CRIT, "DNS: handle_lookup_ip_reply: unknown query type %d", | |
149 | query->type); | |
150 | exit(EX_DNS_ERROR); | |
151 | } | |
152 | ||
153 | end: | |
154 | if(query->callback) | |
155 | query->callback(ip, ip[0] != '*', query->type, query->data); | |
156 | ||
157 | rb_free(query); | |
158 | } | |
159 | ||
160 | /* Callback from gethost_byaddr */ | |
161 | static void | |
162 | handle_lookup_hostname_reply(void *data, struct DNSReply *reply) | |
163 | { | |
164 | struct dns_query *query = data; | |
165 | char *hostname = NULL; | |
166 | ||
167 | if(query == NULL) | |
168 | { | |
169 | /* Shouldn't happen */ | |
170 | warn_opers(L_CRIT, "DNS: handle_lookup_hostname_reply: query == NULL!"); | |
171 | exit(EX_DNS_ERROR); | |
172 | } | |
173 | ||
174 | if(reply == NULL) | |
175 | goto end; | |
176 | ||
177 | if(query->type == QUERY_PTR_A) | |
178 | { | |
179 | struct sockaddr_in *ip, *ip_fwd; | |
180 | ip = (struct sockaddr_in *) &query->addr; | |
181 | ip_fwd = (struct sockaddr_in *) &reply->addr; | |
182 | ||
183 | if(ip->sin_addr.s_addr == ip_fwd->sin_addr.s_addr) | |
184 | hostname = reply->h_name; | |
185 | } | |
186 | else if(query->type == QUERY_PTR_AAAA) | |
187 | { | |
188 | struct sockaddr_in6 *ip, *ip_fwd; | |
189 | ip = (struct sockaddr_in6 *) &query->addr; | |
190 | ip_fwd = (struct sockaddr_in6 *) &reply->addr; | |
191 | ||
192 | if(memcmp(&ip->sin6_addr, &ip_fwd->sin6_addr, sizeof(struct in6_addr)) == 0) | |
193 | hostname = reply->h_name; | |
194 | } | |
195 | else | |
196 | { | |
197 | /* Shouldn't happen */ | |
198 | warn_opers(L_CRIT, "DNS: handle_lookup_hostname_reply: unknown query type %d", | |
199 | query->type); | |
200 | exit(EX_DNS_ERROR); | |
201 | } | |
202 | end: | |
203 | if(query->callback) | |
204 | query->callback(hostname, hostname != NULL, query->type, query->data); | |
205 | ||
206 | rb_free(query); | |
207 | } | |
208 | ||
209 | static void | |
210 | submit_dns_answer(const char *reply, bool status, query_type type, void *data) | |
211 | { | |
212 | char *id = data; | |
213 | ||
214 | if(!id || type == QUERY_INVALID) | |
215 | { | |
216 | warn_opers(L_CRIT, "DNS: submit_dns_answer gave us a bad query"); | |
217 | exit(EX_DNS_ERROR); | |
218 | } | |
219 | ||
220 | if(reply == NULL || status == false) | |
221 | { | |
222 | rb_helper_write(authd_helper, "E %s E %c *", id, type); | |
223 | rb_free(id); | |
224 | return; | |
225 | } | |
226 | ||
227 | rb_helper_write(authd_helper, "E %s O %c %s", id, type, reply); | |
228 | rb_free(id); | |
229 | } | |
230 | ||
231 | void | |
232 | handle_resolve_dns(int parc, char *parv[]) | |
233 | { | |
234 | char *id = rb_strdup(parv[1]); | |
235 | char qtype = *parv[2]; | |
236 | char *record = parv[3]; | |
237 | int aftype = AF_INET; | |
238 | ||
239 | switch(qtype) | |
240 | { | |
241 | case '6': | |
242 | aftype = AF_INET6; | |
243 | case '4': | |
244 | if(!lookup_ip(record, aftype, submit_dns_answer, id)) | |
245 | submit_dns_answer(NULL, false, qtype, NULL); | |
246 | break; | |
247 | case 'S': | |
248 | case 'R': | |
249 | if(!lookup_hostname(record, submit_dns_answer, id)) | |
250 | submit_dns_answer(NULL, false, qtype, NULL); | |
251 | break; | |
252 | default: | |
253 | warn_opers(L_CRIT, "DNS: handle_resolve_dns got an unknown query: %c", qtype); | |
254 | exit(EX_DNS_ERROR); | |
255 | } | |
256 | } | |
257 | ||
258 | void | |
259 | enumerate_nameservers(uint32_t rid, const char letter) | |
260 | { | |
261 | char buf[(HOSTIPLEN + 1) * IRCD_MAXNS]; | |
262 | size_t s = 0; | |
263 | ||
264 | if (!irc_nscount) | |
265 | { | |
266 | /* Shouldn't happen */ | |
267 | warn_opers(L_CRIT, "DNS: no name servers!"); | |
268 | stats_error(rid, letter, "NONAMESERVERS"); | |
269 | exit(EX_DNS_ERROR); | |
270 | } | |
271 | ||
272 | for(int i = 0; i < irc_nscount; i++) | |
273 | { | |
274 | char addr[HOSTIPLEN]; | |
275 | size_t addrlen; | |
276 | ||
277 | rb_inet_ntop_sock((struct sockaddr *)&irc_nsaddr_list[i], addr, sizeof(addr)); | |
278 | ||
279 | if (!addr[0]) | |
280 | { | |
281 | /* Shouldn't happen */ | |
282 | warn_opers(L_CRIT, "DNS: bad nameserver!"); | |
283 | stats_error(rid, letter, "INVALIDNAMESERVER"); | |
284 | exit(EX_DNS_ERROR); | |
285 | } | |
286 | ||
287 | addrlen = strlen(addr) + 1; | |
288 | (void)snprintf(&buf[s], sizeof(buf) - s, "%s ", addr); | |
289 | s += addrlen; | |
290 | } | |
291 | ||
292 | if(s > 0) | |
293 | buf[--s] = '\0'; | |
294 | ||
295 | stats_result(rid, letter, "%s", buf); | |
296 | } | |
297 | ||
298 | void | |
299 | reload_nameservers(const char letter) | |
300 | { | |
301 | /* Not a whole lot to it */ | |
302 | restart_resolver(); | |
303 | } |