]>
Commit | Line | Data |
---|---|---|
078d2b2e VS |
1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
3 | * Copyright (C) 2010,2011 Free Software Foundation, Inc. | |
4 | * | |
5 | * GRUB is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * GRUB is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include <grub/net.h> | |
20 | #include <grub/net/udp.h> | |
21 | #include <grub/command.h> | |
22 | #include <grub/i18n.h> | |
23 | #include <grub/err.h> | |
8d4e4fc0 | 24 | #include <grub/time.h> |
3f05d693 | 25 | #include <grub/safemath.h> |
078d2b2e VS |
26 | |
27 | struct dns_cache_element | |
28 | { | |
29 | char *name; | |
30 | grub_size_t naddresses; | |
31 | struct grub_net_network_level_address *addresses; | |
078d2b2e VS |
32 | grub_uint64_t limit_time; |
33 | }; | |
34 | ||
8d4e4fc0 VS |
35 | #define DNS_CACHE_SIZE 1021 |
36 | #define DNS_HASH_BASE 423 | |
37 | ||
9e236169 GLD |
38 | typedef enum grub_dns_qtype_id |
39 | { | |
40 | GRUB_DNS_QTYPE_A = 1, | |
41 | GRUB_DNS_QTYPE_AAAA = 28 | |
42 | } grub_dns_qtype_id_t; | |
43 | ||
8d4e4fc0 | 44 | static struct dns_cache_element dns_cache[DNS_CACHE_SIZE]; |
f0f4253c VS |
45 | static struct grub_net_network_level_address *dns_servers; |
46 | static grub_size_t dns_nservers, dns_servers_alloc; | |
47 | ||
48 | grub_err_t | |
49 | grub_net_add_dns_server (const struct grub_net_network_level_address *s) | |
50 | { | |
51 | if (dns_servers_alloc <= dns_nservers) | |
52 | { | |
53 | int na = dns_servers_alloc * 2; | |
54 | struct grub_net_network_level_address *ns; | |
3f05d693 PJ |
55 | grub_size_t sz; |
56 | ||
f0f4253c VS |
57 | if (na < 8) |
58 | na = 8; | |
3f05d693 PJ |
59 | |
60 | if (grub_mul (na, sizeof (ns[0]), &sz)) | |
61 | return GRUB_ERR_OUT_OF_RANGE; | |
62 | ||
63 | ns = grub_realloc (dns_servers, sz); | |
f0f4253c VS |
64 | if (!ns) |
65 | return grub_errno; | |
66 | dns_servers_alloc = na; | |
67 | dns_servers = ns; | |
68 | } | |
69 | dns_servers[dns_nservers++] = *s; | |
70 | return GRUB_ERR_NONE; | |
71 | } | |
72 | ||
73 | void | |
74 | grub_net_remove_dns_server (const struct grub_net_network_level_address *s) | |
75 | { | |
76 | grub_size_t i; | |
77 | for (i = 0; i < dns_nservers; i++) | |
78 | if (grub_net_addr_cmp (s, &dns_servers[i]) == 0) | |
79 | break; | |
80 | if (i < dns_nservers) | |
81 | { | |
82 | dns_servers[i] = dns_servers[dns_nservers - 1]; | |
83 | dns_nservers--; | |
84 | } | |
85 | } | |
8d4e4fc0 | 86 | |
078d2b2e VS |
87 | struct dns_header |
88 | { | |
89 | grub_uint16_t id; | |
90 | grub_uint8_t flags; | |
91 | grub_uint8_t ra_z_r_code; | |
92 | grub_uint16_t qdcount; | |
93 | grub_uint16_t ancount; | |
94 | grub_uint16_t nscount; | |
95 | grub_uint16_t arcount; | |
7e47e27b | 96 | } GRUB_PACKED; |
078d2b2e VS |
97 | |
98 | enum | |
99 | { | |
100 | FLAGS_RESPONSE = 0x80, | |
101 | FLAGS_OPCODE = 0x78, | |
102 | FLAGS_RD = 0x01 | |
103 | }; | |
104 | ||
105 | enum | |
106 | { | |
107 | ERRCODE_MASK = 0x0f | |
108 | }; | |
109 | ||
110 | enum | |
111 | { | |
112 | DNS_PORT = 53 | |
113 | }; | |
114 | ||
115 | struct recv_data | |
116 | { | |
117 | grub_size_t *naddresses; | |
118 | struct grub_net_network_level_address **addresses; | |
119 | int cache; | |
120 | grub_uint16_t id; | |
121 | int dns_err; | |
3729fcfc | 122 | char *name; |
8d4e4fc0 | 123 | const char *oname; |
96f7e60e | 124 | int stop; |
078d2b2e VS |
125 | }; |
126 | ||
8d4e4fc0 VS |
127 | static inline int |
128 | hash (const char *str) | |
129 | { | |
40f29060 | 130 | unsigned v = 0, xn = 1; |
8d4e4fc0 VS |
131 | const char *ptr; |
132 | for (ptr = str; *ptr; ) | |
133 | { | |
134 | v = (v + xn * *ptr); | |
135 | xn = (DNS_HASH_BASE * xn) % DNS_CACHE_SIZE; | |
136 | ptr++; | |
137 | if (((ptr - str) & 0x3ff) == 0) | |
138 | v %= DNS_CACHE_SIZE; | |
139 | } | |
140 | return v % DNS_CACHE_SIZE; | |
141 | } | |
142 | ||
078d2b2e | 143 | static int |
3729fcfc VS |
144 | check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head, |
145 | const grub_uint8_t *tail, const char *check_with, | |
146 | int *length, char *set) | |
078d2b2e VS |
147 | { |
148 | const char *readable_ptr = check_with; | |
149 | const grub_uint8_t *ptr; | |
3729fcfc | 150 | char *optr = set; |
078d2b2e | 151 | int bytes_processed = 0; |
3729fcfc VS |
152 | if (length) |
153 | *length = 0; | |
078d2b2e VS |
154 | for (ptr = name_at; ptr < tail && bytes_processed < tail - head + 2; ) |
155 | { | |
156 | /* End marker. */ | |
157 | if (!*ptr) | |
3729fcfc VS |
158 | { |
159 | if (length && *length) | |
160 | (*length)--; | |
161 | if (optr && optr != set) | |
162 | optr--; | |
163 | if (optr) | |
164 | *optr = 0; | |
165 | return !readable_ptr || (*readable_ptr == 0); | |
166 | } | |
078d2b2e VS |
167 | if (*ptr & 0xc0) |
168 | { | |
169 | bytes_processed += 2; | |
170 | if (ptr + 1 >= tail) | |
171 | return 0; | |
172 | ptr = head + (((ptr[0] & 0x3f) << 8) | ptr[1]); | |
173 | continue; | |
174 | } | |
3729fcfc | 175 | if (readable_ptr && grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0) |
078d2b2e VS |
176 | return 0; |
177 | if (grub_memchr (ptr + 1, 0, *ptr) | |
178 | || grub_memchr (ptr + 1, '.', *ptr)) | |
179 | return 0; | |
3729fcfc VS |
180 | if (readable_ptr) |
181 | readable_ptr += *ptr; | |
182 | if (readable_ptr && *readable_ptr != '.' && *readable_ptr != 0) | |
078d2b2e VS |
183 | return 0; |
184 | bytes_processed += *ptr + 1; | |
3729fcfc VS |
185 | if (length) |
186 | *length += *ptr + 1; | |
187 | if (optr) | |
188 | { | |
189 | grub_memcpy (optr, ptr + 1, *ptr); | |
190 | optr += *ptr; | |
191 | } | |
192 | if (optr) | |
193 | *optr++ = '.'; | |
194 | if (readable_ptr && *readable_ptr) | |
078d2b2e VS |
195 | readable_ptr++; |
196 | ptr += *ptr + 1; | |
197 | } | |
198 | return 0; | |
199 | } | |
200 | ||
3729fcfc VS |
201 | static int |
202 | check_name (const grub_uint8_t *name_at, const grub_uint8_t *head, | |
203 | const grub_uint8_t *tail, const char *check_with) | |
204 | { | |
205 | return check_name_real (name_at, head, tail, check_with, NULL, NULL); | |
206 | } | |
207 | ||
208 | static char * | |
209 | get_name (const grub_uint8_t *name_at, const grub_uint8_t *head, | |
210 | const grub_uint8_t *tail) | |
211 | { | |
212 | int length; | |
213 | char *ret; | |
214 | ||
215 | if (!check_name_real (name_at, head, tail, NULL, &length, NULL)) | |
216 | return NULL; | |
217 | ret = grub_malloc (length + 1); | |
218 | if (!ret) | |
219 | return NULL; | |
220 | if (!check_name_real (name_at, head, tail, NULL, NULL, ret)) | |
221 | { | |
222 | grub_free (ret); | |
223 | return NULL; | |
224 | } | |
225 | return ret; | |
226 | } | |
227 | ||
228 | enum | |
229 | { | |
230 | DNS_CLASS_A = 1, | |
231 | DNS_CLASS_CNAME = 5, | |
232 | DNS_CLASS_AAAA = 28 | |
233 | }; | |
234 | ||
078d2b2e VS |
235 | static grub_err_t |
236 | recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)), | |
237 | struct grub_net_buff *nb, | |
238 | void *data_) | |
239 | { | |
240 | struct dns_header *head; | |
241 | struct recv_data *data = data_; | |
242 | int i, j; | |
3729fcfc VS |
243 | grub_uint8_t *ptr, *reparse_ptr; |
244 | int redirect_cnt = 0; | |
245 | char *redirect_save = NULL; | |
8d4e4fc0 | 246 | grub_uint32_t ttl_all = ~0U; |
078d2b2e | 247 | |
52408aa9 AB |
248 | /* Code apparently assumed that only one packet is received as response. |
249 | We may get multiple responses due to network condition, so check here | |
250 | and quit early. */ | |
251 | if (*data->addresses) | |
252 | { | |
253 | grub_netbuff_free (nb); | |
254 | return GRUB_ERR_NONE; | |
255 | } | |
256 | ||
078d2b2e VS |
257 | head = (struct dns_header *) nb->data; |
258 | ptr = (grub_uint8_t *) (head + 1); | |
259 | if (ptr >= nb->tail) | |
260 | { | |
261 | grub_netbuff_free (nb); | |
262 | return GRUB_ERR_NONE; | |
263 | } | |
264 | ||
265 | if (head->id != data->id) | |
266 | { | |
267 | grub_netbuff_free (nb); | |
268 | return GRUB_ERR_NONE; | |
269 | } | |
270 | if (!(head->flags & FLAGS_RESPONSE) || (head->flags & FLAGS_OPCODE)) | |
271 | { | |
272 | grub_netbuff_free (nb); | |
273 | return GRUB_ERR_NONE; | |
274 | } | |
275 | if (head->ra_z_r_code & ERRCODE_MASK) | |
276 | { | |
277 | data->dns_err = 1; | |
278 | grub_netbuff_free (nb); | |
279 | return GRUB_ERR_NONE; | |
280 | } | |
16a7e723 | 281 | for (i = 0; i < grub_be_to_cpu16 (head->qdcount); i++) |
078d2b2e VS |
282 | { |
283 | if (ptr >= nb->tail) | |
284 | { | |
285 | grub_netbuff_free (nb); | |
286 | return GRUB_ERR_NONE; | |
287 | } | |
288 | while (ptr < nb->tail && !((*ptr & 0xc0) || *ptr == 0)) | |
289 | ptr += *ptr + 1; | |
290 | if (ptr < nb->tail && (*ptr & 0xc0)) | |
291 | ptr++; | |
292 | ptr++; | |
293 | ptr += 4; | |
294 | } | |
f725fa7c PJ |
295 | *data->addresses = grub_calloc (grub_be_to_cpu16 (head->ancount), |
296 | sizeof ((*data->addresses)[0])); | |
078d2b2e VS |
297 | if (!*data->addresses) |
298 | { | |
299 | grub_errno = GRUB_ERR_NONE; | |
300 | grub_netbuff_free (nb); | |
301 | return GRUB_ERR_NONE; | |
302 | } | |
3729fcfc VS |
303 | reparse_ptr = ptr; |
304 | reparse: | |
16a7e723 | 305 | for (i = 0, ptr = reparse_ptr; i < grub_be_to_cpu16 (head->ancount); i++) |
078d2b2e VS |
306 | { |
307 | int ignored = 0; | |
3729fcfc | 308 | grub_uint8_t class; |
078d2b2e VS |
309 | grub_uint32_t ttl = 0; |
310 | grub_uint16_t length; | |
311 | if (ptr >= nb->tail) | |
312 | { | |
313 | if (!*data->naddresses) | |
314 | grub_free (*data->addresses); | |
315 | return GRUB_ERR_NONE; | |
316 | } | |
317 | ignored = !check_name (ptr, nb->data, nb->tail, data->name); | |
318 | while (ptr < nb->tail && !((*ptr & 0xc0) || *ptr == 0)) | |
319 | ptr += *ptr + 1; | |
320 | if (ptr < nb->tail && (*ptr & 0xc0)) | |
321 | ptr++; | |
322 | ptr++; | |
323 | if (ptr + 10 >= nb->tail) | |
324 | { | |
325 | if (!*data->naddresses) | |
326 | grub_free (*data->addresses); | |
327 | grub_netbuff_free (nb); | |
328 | return GRUB_ERR_NONE; | |
329 | } | |
330 | if (*ptr++ != 0) | |
331 | ignored = 1; | |
3729fcfc | 332 | class = *ptr++; |
078d2b2e VS |
333 | if (*ptr++ != 0) |
334 | ignored = 1; | |
335 | if (*ptr++ != 1) | |
336 | ignored = 1; | |
337 | for (j = 0; j < 4; j++) | |
338 | { | |
8d4e4fc0 VS |
339 | ttl <<= 8; |
340 | ttl |= *ptr++; | |
078d2b2e VS |
341 | } |
342 | length = *ptr++ << 8; | |
343 | length |= *ptr++; | |
344 | if (ptr + length > nb->tail) | |
345 | { | |
346 | if (!*data->naddresses) | |
347 | grub_free (*data->addresses); | |
348 | grub_netbuff_free (nb); | |
349 | return GRUB_ERR_NONE; | |
350 | } | |
3729fcfc | 351 | if (!ignored) |
8d4e4fc0 VS |
352 | { |
353 | if (ttl_all > ttl) | |
354 | ttl_all = ttl; | |
355 | switch (class) | |
356 | { | |
357 | case DNS_CLASS_A: | |
358 | if (length != 4) | |
359 | break; | |
360 | (*data->addresses)[*data->naddresses].type | |
361 | = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; | |
362 | grub_memcpy (&(*data->addresses)[*data->naddresses].ipv4, | |
363 | ptr, 4); | |
364 | (*data->naddresses)++; | |
96f7e60e | 365 | data->stop = 1; |
3729fcfc | 366 | break; |
8d4e4fc0 VS |
367 | case DNS_CLASS_AAAA: |
368 | if (length != 16) | |
369 | break; | |
370 | (*data->addresses)[*data->naddresses].type | |
371 | = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; | |
372 | grub_memcpy (&(*data->addresses)[*data->naddresses].ipv6, | |
373 | ptr, 16); | |
374 | (*data->naddresses)++; | |
96f7e60e | 375 | data->stop = 1; |
3729fcfc | 376 | break; |
8d4e4fc0 VS |
377 | case DNS_CLASS_CNAME: |
378 | if (!(redirect_cnt & (redirect_cnt - 1))) | |
379 | { | |
380 | grub_free (redirect_save); | |
381 | redirect_save = data->name; | |
382 | } | |
383 | else | |
384 | grub_free (data->name); | |
385 | redirect_cnt++; | |
386 | data->name = get_name (ptr, nb->data, nb->tail); | |
387 | if (!data->name) | |
388 | { | |
389 | data->dns_err = 1; | |
390 | grub_errno = 0; | |
391 | return GRUB_ERR_NONE; | |
392 | } | |
393 | grub_dprintf ("dns", "CNAME %s\n", data->name); | |
394 | if (grub_strcmp (redirect_save, data->name) == 0) | |
395 | { | |
396 | data->dns_err = 1; | |
397 | grub_free (redirect_save); | |
398 | return GRUB_ERR_NONE; | |
399 | } | |
400 | goto reparse; | |
401 | } | |
078d2b2e VS |
402 | } |
403 | ptr += length; | |
404 | } | |
8d4e4fc0 VS |
405 | if (ttl_all && *data->naddresses && data->cache) |
406 | { | |
407 | int h; | |
408 | grub_dprintf ("dns", "caching for %d seconds\n", ttl_all); | |
409 | h = hash (data->oname); | |
410 | grub_free (dns_cache[h].name); | |
411 | dns_cache[h].name = 0; | |
412 | grub_free (dns_cache[h].addresses); | |
413 | dns_cache[h].addresses = 0; | |
414 | dns_cache[h].name = grub_strdup (data->oname); | |
415 | dns_cache[h].naddresses = *data->naddresses; | |
f725fa7c PJ |
416 | dns_cache[h].addresses = grub_calloc (*data->naddresses, |
417 | sizeof (dns_cache[h].addresses[0])); | |
8d4e4fc0 VS |
418 | dns_cache[h].limit_time = grub_get_time_ms () + 1000 * ttl_all; |
419 | if (!dns_cache[h].addresses || !dns_cache[h].name) | |
420 | { | |
421 | grub_free (dns_cache[h].name); | |
422 | dns_cache[h].name = 0; | |
423 | grub_free (dns_cache[h].addresses); | |
424 | dns_cache[h].addresses = 0; | |
425 | } | |
426 | grub_memcpy (dns_cache[h].addresses, *data->addresses, | |
427 | *data->naddresses | |
428 | * sizeof (dns_cache[h].addresses[0])); | |
429 | } | |
078d2b2e | 430 | grub_netbuff_free (nb); |
3729fcfc | 431 | grub_free (redirect_save); |
078d2b2e VS |
432 | return GRUB_ERR_NONE; |
433 | } | |
434 | ||
435 | grub_err_t | |
436 | grub_net_dns_lookup (const char *name, | |
437 | const struct grub_net_network_level_address *servers, | |
438 | grub_size_t n_servers, | |
439 | grub_size_t *naddresses, | |
440 | struct grub_net_network_level_address **addresses, | |
441 | int cache) | |
442 | { | |
443 | grub_size_t send_servers = 0; | |
444 | grub_size_t i, j; | |
445 | struct grub_net_buff *nb; | |
28153eb8 | 446 | grub_net_udp_socket_t *sockets; |
078d2b2e VS |
447 | grub_uint8_t *optr; |
448 | const char *iptr; | |
449 | struct dns_header *head; | |
450 | static grub_uint16_t id = 1; | |
9e236169 | 451 | grub_uint8_t *qtypeptr; |
078d2b2e VS |
452 | grub_err_t err = GRUB_ERR_NONE; |
453 | struct recv_data data = {naddresses, addresses, cache, | |
96f7e60e | 454 | grub_cpu_to_be16 (id++), 0, 0, name, 0}; |
078d2b2e | 455 | grub_uint8_t *nbd; |
a01ab698 | 456 | grub_size_t try_server = 0; |
078d2b2e | 457 | |
f0f4253c VS |
458 | if (!servers) |
459 | { | |
460 | servers = dns_servers; | |
461 | n_servers = dns_nservers; | |
462 | } | |
463 | ||
464 | if (!n_servers) | |
d61386e2 VS |
465 | return grub_error (GRUB_ERR_BAD_ARGUMENT, |
466 | N_("no DNS servers configured")); | |
f0f4253c | 467 | |
8d4e4fc0 VS |
468 | *naddresses = 0; |
469 | if (cache) | |
470 | { | |
471 | int h; | |
472 | h = hash (name); | |
473 | if (dns_cache[h].name && grub_strcmp (dns_cache[h].name, name) == 0 | |
474 | && grub_get_time_ms () < dns_cache[h].limit_time) | |
475 | { | |
476 | grub_dprintf ("dns", "retrieved from cache\n"); | |
477 | *addresses = grub_malloc (dns_cache[h].naddresses | |
478 | * sizeof ((*addresses)[0])); | |
479 | if (!*addresses) | |
480 | return grub_errno; | |
481 | *naddresses = dns_cache[h].naddresses; | |
482 | grub_memcpy (*addresses, dns_cache[h].addresses, | |
483 | dns_cache[h].naddresses | |
484 | * sizeof ((*addresses)[0])); | |
485 | return GRUB_ERR_NONE; | |
486 | } | |
487 | } | |
488 | ||
f725fa7c | 489 | sockets = grub_calloc (n_servers, sizeof (sockets[0])); |
28153eb8 VS |
490 | if (!sockets) |
491 | return grub_errno; | |
492 | ||
3729fcfc VS |
493 | data.name = grub_strdup (name); |
494 | if (!data.name) | |
28153eb8 VS |
495 | { |
496 | grub_free (sockets); | |
497 | return grub_errno; | |
498 | } | |
3729fcfc | 499 | |
078d2b2e VS |
500 | nb = grub_netbuff_alloc (GRUB_NET_OUR_MAX_IP_HEADER_SIZE |
501 | + GRUB_NET_MAX_LINK_HEADER_SIZE | |
502 | + GRUB_NET_UDP_HEADER_SIZE | |
503 | + sizeof (struct dns_header) | |
9e236169 | 504 | + grub_strlen (name) + 2 + 4); |
078d2b2e | 505 | if (!nb) |
3729fcfc | 506 | { |
28153eb8 | 507 | grub_free (sockets); |
3729fcfc VS |
508 | grub_free (data.name); |
509 | return grub_errno; | |
510 | } | |
078d2b2e VS |
511 | grub_netbuff_reserve (nb, GRUB_NET_OUR_MAX_IP_HEADER_SIZE |
512 | + GRUB_NET_MAX_LINK_HEADER_SIZE | |
513 | + GRUB_NET_UDP_HEADER_SIZE); | |
514 | grub_netbuff_put (nb, sizeof (struct dns_header) | |
9e236169 | 515 | + grub_strlen (name) + 2 + 4); |
078d2b2e VS |
516 | head = (struct dns_header *) nb->data; |
517 | optr = (grub_uint8_t *) (head + 1); | |
518 | for (iptr = name; *iptr; ) | |
519 | { | |
520 | const char *dot; | |
521 | dot = grub_strchr (iptr, '.'); | |
522 | if (!dot) | |
523 | dot = iptr + grub_strlen (iptr); | |
524 | if ((dot - iptr) >= 64) | |
3729fcfc | 525 | { |
28153eb8 | 526 | grub_free (sockets); |
3729fcfc VS |
527 | grub_free (data.name); |
528 | return grub_error (GRUB_ERR_BAD_ARGUMENT, | |
d61386e2 | 529 | N_("domain name component is too long")); |
3729fcfc | 530 | } |
078d2b2e VS |
531 | *optr = (dot - iptr); |
532 | optr++; | |
533 | grub_memcpy (optr, iptr, dot - iptr); | |
534 | optr += dot - iptr; | |
535 | iptr = dot; | |
536 | if (*iptr) | |
537 | iptr++; | |
538 | } | |
539 | *optr++ = 0; | |
540 | ||
9e236169 | 541 | /* Type. */ |
078d2b2e | 542 | *optr++ = 0; |
9e236169 | 543 | qtypeptr = optr++; |
078d2b2e VS |
544 | |
545 | /* Class. */ | |
546 | *optr++ = 0; | |
547 | *optr++ = 1; | |
548 | ||
549 | head->id = data.id; | |
550 | head->flags = FLAGS_RD; | |
551 | head->ra_z_r_code = 0; | |
9e236169 | 552 | head->qdcount = grub_cpu_to_be16_compile_time (1); |
078d2b2e VS |
553 | head->ancount = grub_cpu_to_be16_compile_time (0); |
554 | head->nscount = grub_cpu_to_be16_compile_time (0); | |
555 | head->arcount = grub_cpu_to_be16_compile_time (0); | |
556 | ||
557 | nbd = nb->data; | |
558 | ||
559 | for (i = 0; i < n_servers * 4; i++) | |
560 | { | |
561 | /* Connect to a next server. */ | |
a01ab698 | 562 | while (!(i & 1) && try_server < n_servers) |
078d2b2e | 563 | { |
a01ab698 | 564 | sockets[send_servers] = grub_net_udp_open (servers[try_server++], |
078d2b2e VS |
565 | DNS_PORT, |
566 | recv_hook, | |
567 | &data); | |
a01ab698 | 568 | if (!sockets[send_servers]) |
078d2b2e VS |
569 | { |
570 | err = grub_errno; | |
571 | grub_errno = GRUB_ERR_NONE; | |
572 | } | |
573 | else | |
574 | { | |
a01ab698 | 575 | send_servers++; |
078d2b2e VS |
576 | break; |
577 | } | |
578 | } | |
a01ab698 | 579 | if (!send_servers) |
078d2b2e VS |
580 | goto out; |
581 | if (*data.naddresses) | |
582 | goto out; | |
583 | for (j = 0; j < send_servers; j++) | |
584 | { | |
9e236169 | 585 | grub_err_t err2; |
a01ab698 | 586 | |
9e236169 GLD |
587 | grub_size_t t = 0; |
588 | do | |
589 | { | |
f9d1b442 | 590 | nb->data = nbd; |
9e236169 GLD |
591 | if (servers[j].option == DNS_OPTION_IPV4 || |
592 | ((servers[j].option == DNS_OPTION_PREFER_IPV4) && (t++ == 0)) || | |
593 | ((servers[j].option == DNS_OPTION_PREFER_IPV6) && (t++ == 1))) | |
594 | *qtypeptr = GRUB_DNS_QTYPE_A; | |
595 | else | |
596 | *qtypeptr = GRUB_DNS_QTYPE_AAAA; | |
597 | ||
598 | grub_dprintf ("dns", "QTYPE: %u QNAME: %s\n", *qtypeptr, name); | |
599 | ||
600 | err2 = grub_net_send_udp_packet (sockets[j], nb); | |
601 | if (err2) | |
602 | { | |
603 | grub_errno = GRUB_ERR_NONE; | |
604 | err = err2; | |
605 | } | |
606 | if (*data.naddresses) | |
607 | goto out; | |
608 | } | |
609 | while (t == 1); | |
078d2b2e | 610 | } |
96f7e60e | 611 | grub_net_poll_cards (200, &data.stop); |
078d2b2e VS |
612 | } |
613 | out: | |
3729fcfc | 614 | grub_free (data.name); |
078d2b2e VS |
615 | grub_netbuff_free (nb); |
616 | for (j = 0; j < send_servers; j++) | |
617 | grub_net_udp_close (sockets[j]); | |
3729fcfc | 618 | |
28153eb8 VS |
619 | grub_free (sockets); |
620 | ||
078d2b2e VS |
621 | if (*data.naddresses) |
622 | return GRUB_ERR_NONE; | |
623 | if (data.dns_err) | |
d61386e2 VS |
624 | return grub_error (GRUB_ERR_NET_NO_DOMAIN, |
625 | N_("no DNS record found")); | |
078d2b2e VS |
626 | |
627 | if (err) | |
628 | { | |
629 | grub_errno = err; | |
630 | return err; | |
631 | } | |
d61386e2 VS |
632 | return grub_error (GRUB_ERR_TIMEOUT, |
633 | N_("no DNS reply received")); | |
078d2b2e VS |
634 | } |
635 | ||
636 | static grub_err_t | |
637 | grub_cmd_nslookup (struct grub_command *cmd __attribute__ ((unused)), | |
638 | int argc, char **args) | |
639 | { | |
640 | grub_err_t err; | |
9e236169 GLD |
641 | struct grub_net_network_level_address cmd_server; |
642 | struct grub_net_network_level_address *servers; | |
643 | grub_size_t nservers, i, naddresses = 0; | |
418f45ab | 644 | struct grub_net_network_level_address *addresses = 0; |
9b55efe0 | 645 | if (argc != 2 && argc != 1) |
9c4b5c13 | 646 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); |
9b55efe0 VS |
647 | if (argc == 2) |
648 | { | |
9e236169 | 649 | err = grub_net_resolve_address (args[1], &cmd_server); |
9b55efe0 VS |
650 | if (err) |
651 | return err; | |
9e236169 GLD |
652 | servers = &cmd_server; |
653 | nservers = 1; | |
9b55efe0 VS |
654 | } |
655 | else | |
9e236169 GLD |
656 | { |
657 | servers = dns_servers; | |
658 | nservers = dns_nservers; | |
659 | } | |
660 | ||
661 | grub_net_dns_lookup (args[0], servers, nservers, &naddresses, | |
662 | &addresses, 0); | |
078d2b2e | 663 | |
078d2b2e VS |
664 | for (i = 0; i < naddresses; i++) |
665 | { | |
666 | char buf[GRUB_NET_MAX_STR_ADDR_LEN]; | |
667 | grub_net_addr_to_str (&addresses[i], buf); | |
668 | grub_printf ("%s\n", buf); | |
669 | } | |
f0f4253c | 670 | grub_free (addresses); |
9e236169 GLD |
671 | if (naddresses) |
672 | return GRUB_ERR_NONE; | |
673 | return grub_error (GRUB_ERR_NET_NO_DOMAIN, N_("no DNS record found")); | |
078d2b2e VS |
674 | } |
675 | ||
f0f4253c VS |
676 | static grub_err_t |
677 | grub_cmd_list_dns (struct grub_command *cmd __attribute__ ((unused)), | |
678 | int argc __attribute__ ((unused)), | |
679 | char **args __attribute__ ((unused))) | |
680 | { | |
681 | grub_size_t i; | |
9e236169 GLD |
682 | const char *strtype = ""; |
683 | ||
f0f4253c VS |
684 | for (i = 0; i < dns_nservers; i++) |
685 | { | |
9e236169 GLD |
686 | switch (dns_servers[i].option) |
687 | { | |
688 | case DNS_OPTION_IPV4: | |
636977b0 | 689 | strtype = _("only ipv4"); |
9e236169 GLD |
690 | break; |
691 | ||
692 | case DNS_OPTION_IPV6: | |
636977b0 | 693 | strtype = _("only ipv6"); |
9e236169 GLD |
694 | break; |
695 | ||
696 | case DNS_OPTION_PREFER_IPV4: | |
636977b0 | 697 | strtype = _("prefer ipv4"); |
9e236169 GLD |
698 | break; |
699 | ||
700 | case DNS_OPTION_PREFER_IPV6: | |
636977b0 | 701 | strtype = _("prefer ipv6"); |
9e236169 GLD |
702 | break; |
703 | } | |
704 | ||
f0f4253c VS |
705 | char buf[GRUB_NET_MAX_STR_ADDR_LEN]; |
706 | grub_net_addr_to_str (&dns_servers[i], buf); | |
9e236169 | 707 | grub_printf ("%s (%s)\n", buf, strtype); |
f0f4253c VS |
708 | } |
709 | return GRUB_ERR_NONE; | |
710 | } | |
711 | ||
712 | static grub_err_t | |
713 | grub_cmd_add_dns (struct grub_command *cmd __attribute__ ((unused)), | |
714 | int argc, char **args) | |
715 | { | |
716 | grub_err_t err; | |
717 | struct grub_net_network_level_address server; | |
718 | ||
9e236169 | 719 | if ((argc < 1) || (argc > 2)) |
9c4b5c13 | 720 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); |
9e236169 GLD |
721 | else if (argc == 1) |
722 | server.option = DNS_OPTION_PREFER_IPV4; | |
723 | else | |
724 | { | |
725 | if (grub_strcmp (args[1], "--only-ipv4") == 0) | |
726 | server.option = DNS_OPTION_IPV4; | |
727 | else if (grub_strcmp (args[1], "--only-ipv6") == 0) | |
728 | server.option = DNS_OPTION_IPV6; | |
729 | else if (grub_strcmp (args[1], "--prefer-ipv4") == 0) | |
730 | server.option = DNS_OPTION_PREFER_IPV4; | |
731 | else if (grub_strcmp (args[1], "--prefer-ipv6") == 0) | |
732 | server.option = DNS_OPTION_PREFER_IPV6; | |
733 | else | |
734 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid argument")); | |
735 | } | |
736 | ||
f0f4253c VS |
737 | err = grub_net_resolve_address (args[0], &server); |
738 | if (err) | |
739 | return err; | |
740 | ||
741 | return grub_net_add_dns_server (&server); | |
742 | } | |
743 | ||
744 | static grub_err_t | |
745 | grub_cmd_del_dns (struct grub_command *cmd __attribute__ ((unused)), | |
746 | int argc, char **args) | |
747 | { | |
748 | grub_err_t err; | |
749 | struct grub_net_network_level_address server; | |
750 | ||
751 | if (argc != 1) | |
9c4b5c13 | 752 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); |
f0f4253c VS |
753 | err = grub_net_resolve_address (args[1], &server); |
754 | if (err) | |
755 | return err; | |
756 | ||
757 | return grub_net_add_dns_server (&server); | |
758 | } | |
759 | ||
760 | static grub_command_t cmd, cmd_add, cmd_del, cmd_list; | |
078d2b2e VS |
761 | |
762 | void | |
763 | grub_dns_init (void) | |
764 | { | |
765 | cmd = grub_register_command ("net_nslookup", grub_cmd_nslookup, | |
4a9f8346 | 766 | N_("ADDRESS DNSSERVER"), |
aa7d2052 | 767 | N_("Perform a DNS lookup")); |
f0f4253c | 768 | cmd_add = grub_register_command ("net_add_dns", grub_cmd_add_dns, |
4a9f8346 | 769 | N_("DNSSERVER"), |
aa7d2052 | 770 | N_("Add a DNS server")); |
f0f4253c | 771 | cmd_del = grub_register_command ("net_del_dns", grub_cmd_del_dns, |
4a9f8346 | 772 | N_("DNSSERVER"), |
aa7d2052 VS |
773 | N_("Remove a DNS server")); |
774 | cmd_list = grub_register_command ("net_ls_dns", grub_cmd_list_dns, | |
775 | NULL, N_("List DNS servers")); | |
078d2b2e VS |
776 | } |
777 | ||
778 | void | |
779 | grub_dns_fini (void) | |
780 | { | |
781 | grub_unregister_command (cmd); | |
f0f4253c VS |
782 | grub_unregister_command (cmd_add); |
783 | grub_unregister_command (cmd_del); | |
784 | grub_unregister_command (cmd_list); | |
078d2b2e | 785 | } |