]> git.proxmox.com Git - grub2.git/blame - grub-core/net/dns.c
malloc: Use overflow checking primitives where we do complex allocations
[grub2.git] / grub-core / net / dns.c
CommitLineData
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
27struct 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
38typedef 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 44static struct dns_cache_element dns_cache[DNS_CACHE_SIZE];
f0f4253c
VS
45static struct grub_net_network_level_address *dns_servers;
46static grub_size_t dns_nservers, dns_servers_alloc;
47
48grub_err_t
49grub_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
73void
74grub_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
87struct 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
98enum
99 {
100 FLAGS_RESPONSE = 0x80,
101 FLAGS_OPCODE = 0x78,
102 FLAGS_RD = 0x01
103 };
104
105enum
106 {
107 ERRCODE_MASK = 0x0f
108 };
109
110enum
111 {
112 DNS_PORT = 53
113 };
114
115struct 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
127static inline int
128hash (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 143static int
3729fcfc
VS
144check_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
201static int
202check_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
208static char *
209get_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
228enum
229 {
230 DNS_CLASS_A = 1,
231 DNS_CLASS_CNAME = 5,
232 DNS_CLASS_AAAA = 28
233 };
234
078d2b2e
VS
235static grub_err_t
236recv_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
435grub_err_t
436grub_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
636static grub_err_t
637grub_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
676static grub_err_t
677grub_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
712static grub_err_t
713grub_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
744static grub_err_t
745grub_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
760static grub_command_t cmd, cmd_add, cmd_del, cmd_list;
078d2b2e
VS
761
762void
763grub_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
778void
779grub_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}