2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010,2011 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <grub/net/udp.h>
21 #include <grub/command.h>
22 #include <grub/i18n.h>
24 #include <grub/time.h>
26 struct dns_cache_element
29 grub_size_t naddresses
;
30 struct grub_net_network_level_address
*addresses
;
31 grub_uint64_t limit_time
;
34 #define DNS_CACHE_SIZE 1021
35 #define DNS_HASH_BASE 423
37 static struct dns_cache_element dns_cache
[DNS_CACHE_SIZE
];
38 static struct grub_net_network_level_address
*dns_servers
;
39 static grub_size_t dns_nservers
, dns_servers_alloc
;
42 grub_net_add_dns_server (const struct grub_net_network_level_address
*s
)
44 if (dns_servers_alloc
<= dns_nservers
)
46 int na
= dns_servers_alloc
* 2;
47 struct grub_net_network_level_address
*ns
;
50 ns
= grub_realloc (dns_servers
, na
* sizeof (ns
[0]));
53 dns_servers_alloc
= na
;
56 dns_servers
[dns_nservers
++] = *s
;
61 grub_net_remove_dns_server (const struct grub_net_network_level_address
*s
)
64 for (i
= 0; i
< dns_nservers
; i
++)
65 if (grub_net_addr_cmp (s
, &dns_servers
[i
]) == 0)
69 dns_servers
[i
] = dns_servers
[dns_nservers
- 1];
78 grub_uint8_t ra_z_r_code
;
79 grub_uint16_t qdcount
;
80 grub_uint16_t ancount
;
81 grub_uint16_t nscount
;
82 grub_uint16_t arcount
;
83 } __attribute__ ((packed
));
87 FLAGS_RESPONSE
= 0x80,
104 grub_size_t
*naddresses
;
105 struct grub_net_network_level_address
**addresses
;
115 hash (const char *str
)
119 for (ptr
= str
; *ptr
; )
122 xn
= (DNS_HASH_BASE
* xn
) % DNS_CACHE_SIZE
;
124 if (((ptr
- str
) & 0x3ff) == 0)
127 return v
% DNS_CACHE_SIZE
;
131 check_name_real (const grub_uint8_t
*name_at
, const grub_uint8_t
*head
,
132 const grub_uint8_t
*tail
, const char *check_with
,
133 int *length
, char *set
)
135 const char *readable_ptr
= check_with
;
136 const grub_uint8_t
*ptr
;
138 int bytes_processed
= 0;
141 for (ptr
= name_at
; ptr
< tail
&& bytes_processed
< tail
- head
+ 2; )
146 if (length
&& *length
)
148 if (optr
&& optr
!= set
)
152 return !readable_ptr
|| (*readable_ptr
== 0);
156 bytes_processed
+= 2;
159 ptr
= head
+ (((ptr
[0] & 0x3f) << 8) | ptr
[1]);
162 if (readable_ptr
&& grub_memcmp (ptr
+ 1, readable_ptr
, *ptr
) != 0)
164 if (grub_memchr (ptr
+ 1, 0, *ptr
)
165 || grub_memchr (ptr
+ 1, '.', *ptr
))
168 readable_ptr
+= *ptr
;
169 if (readable_ptr
&& *readable_ptr
!= '.' && *readable_ptr
!= 0)
171 bytes_processed
+= *ptr
+ 1;
176 grub_memcpy (optr
, ptr
+ 1, *ptr
);
181 if (readable_ptr
&& *readable_ptr
)
189 check_name (const grub_uint8_t
*name_at
, const grub_uint8_t
*head
,
190 const grub_uint8_t
*tail
, const char *check_with
)
192 return check_name_real (name_at
, head
, tail
, check_with
, NULL
, NULL
);
196 get_name (const grub_uint8_t
*name_at
, const grub_uint8_t
*head
,
197 const grub_uint8_t
*tail
)
202 if (!check_name_real (name_at
, head
, tail
, NULL
, &length
, NULL
))
204 ret
= grub_malloc (length
+ 1);
207 if (!check_name_real (name_at
, head
, tail
, NULL
, NULL
, ret
))
223 recv_hook (grub_net_udp_socket_t sock
__attribute__ ((unused
)),
224 struct grub_net_buff
*nb
,
227 struct dns_header
*head
;
228 struct recv_data
*data
= data_
;
230 grub_uint8_t
*ptr
, *reparse_ptr
;
231 int redirect_cnt
= 0;
232 char *redirect_save
= NULL
;
233 grub_uint32_t ttl_all
= ~0U;
235 head
= (struct dns_header
*) nb
->data
;
236 ptr
= (grub_uint8_t
*) (head
+ 1);
239 grub_netbuff_free (nb
);
240 return GRUB_ERR_NONE
;
243 if (head
->id
!= data
->id
)
245 grub_netbuff_free (nb
);
246 return GRUB_ERR_NONE
;
248 if (!(head
->flags
& FLAGS_RESPONSE
) || (head
->flags
& FLAGS_OPCODE
))
250 grub_netbuff_free (nb
);
251 return GRUB_ERR_NONE
;
253 if (head
->ra_z_r_code
& ERRCODE_MASK
)
256 grub_netbuff_free (nb
);
257 return GRUB_ERR_NONE
;
259 for (i
= 0; i
< grub_cpu_to_be16 (head
->qdcount
); i
++)
263 grub_netbuff_free (nb
);
264 return GRUB_ERR_NONE
;
266 while (ptr
< nb
->tail
&& !((*ptr
& 0xc0) || *ptr
== 0))
268 if (ptr
< nb
->tail
&& (*ptr
& 0xc0))
273 *data
->addresses
= grub_malloc (sizeof ((*data
->addresses
)[0])
274 * grub_cpu_to_be16 (head
->ancount
));
275 if (!*data
->addresses
)
277 grub_errno
= GRUB_ERR_NONE
;
278 grub_netbuff_free (nb
);
279 return GRUB_ERR_NONE
;
283 for (i
= 0, ptr
= reparse_ptr
; i
< grub_cpu_to_be16 (head
->ancount
); i
++)
287 grub_uint32_t ttl
= 0;
288 grub_uint16_t length
;
291 if (!*data
->naddresses
)
292 grub_free (*data
->addresses
);
293 return GRUB_ERR_NONE
;
295 ignored
= !check_name (ptr
, nb
->data
, nb
->tail
, data
->name
);
296 while (ptr
< nb
->tail
&& !((*ptr
& 0xc0) || *ptr
== 0))
298 if (ptr
< nb
->tail
&& (*ptr
& 0xc0))
301 if (ptr
+ 10 >= nb
->tail
)
303 if (!*data
->naddresses
)
304 grub_free (*data
->addresses
);
305 grub_netbuff_free (nb
);
306 return GRUB_ERR_NONE
;
315 for (j
= 0; j
< 4; j
++)
320 length
= *ptr
++ << 8;
322 if (ptr
+ length
> nb
->tail
)
324 if (!*data
->naddresses
)
325 grub_free (*data
->addresses
);
326 grub_netbuff_free (nb
);
327 return GRUB_ERR_NONE
;
338 (*data
->addresses
)[*data
->naddresses
].type
339 = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4
;
340 grub_memcpy (&(*data
->addresses
)[*data
->naddresses
].ipv4
,
342 (*data
->naddresses
)++;
348 (*data
->addresses
)[*data
->naddresses
].type
349 = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6
;
350 grub_memcpy (&(*data
->addresses
)[*data
->naddresses
].ipv6
,
352 (*data
->naddresses
)++;
355 case DNS_CLASS_CNAME
:
356 if (!(redirect_cnt
& (redirect_cnt
- 1)))
358 grub_free (redirect_save
);
359 redirect_save
= data
->name
;
362 grub_free (data
->name
);
364 data
->name
= get_name (ptr
, nb
->data
, nb
->tail
);
369 return GRUB_ERR_NONE
;
371 grub_dprintf ("dns", "CNAME %s\n", data
->name
);
372 if (grub_strcmp (redirect_save
, data
->name
) == 0)
375 grub_free (redirect_save
);
376 return GRUB_ERR_NONE
;
383 if (ttl_all
&& *data
->naddresses
&& data
->cache
)
386 grub_dprintf ("dns", "caching for %d seconds\n", ttl_all
);
387 h
= hash (data
->oname
);
388 grub_free (dns_cache
[h
].name
);
389 dns_cache
[h
].name
= 0;
390 grub_free (dns_cache
[h
].addresses
);
391 dns_cache
[h
].addresses
= 0;
392 dns_cache
[h
].name
= grub_strdup (data
->oname
);
393 dns_cache
[h
].naddresses
= *data
->naddresses
;
394 dns_cache
[h
].addresses
= grub_malloc (*data
->naddresses
395 * sizeof (dns_cache
[h
].addresses
[0]));
396 dns_cache
[h
].limit_time
= grub_get_time_ms () + 1000 * ttl_all
;
397 if (!dns_cache
[h
].addresses
|| !dns_cache
[h
].name
)
399 grub_free (dns_cache
[h
].name
);
400 dns_cache
[h
].name
= 0;
401 grub_free (dns_cache
[h
].addresses
);
402 dns_cache
[h
].addresses
= 0;
404 grub_memcpy (dns_cache
[h
].addresses
, *data
->addresses
,
406 * sizeof (dns_cache
[h
].addresses
[0]));
408 grub_netbuff_free (nb
);
409 grub_free (redirect_save
);
410 return GRUB_ERR_NONE
;
414 grub_net_dns_lookup (const char *name
,
415 const struct grub_net_network_level_address
*servers
,
416 grub_size_t n_servers
,
417 grub_size_t
*naddresses
,
418 struct grub_net_network_level_address
**addresses
,
421 grub_size_t send_servers
= 0;
423 struct grub_net_buff
*nb
;
424 grub_net_udp_socket_t sockets
[n_servers
];
427 struct dns_header
*head
;
428 static grub_uint16_t id
= 1;
429 grub_err_t err
= GRUB_ERR_NONE
;
430 struct recv_data data
= {naddresses
, addresses
, cache
,
431 grub_cpu_to_be16 (id
++), 0, 0, name
, 0};
437 servers
= dns_servers
;
438 n_servers
= dns_nservers
;
442 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
443 N_("no DNS servers configured"));
450 if (dns_cache
[h
].name
&& grub_strcmp (dns_cache
[h
].name
, name
) == 0
451 && grub_get_time_ms () < dns_cache
[h
].limit_time
)
453 grub_dprintf ("dns", "retrieved from cache\n");
454 *addresses
= grub_malloc (dns_cache
[h
].naddresses
455 * sizeof ((*addresses
)[0]));
458 *naddresses
= dns_cache
[h
].naddresses
;
459 grub_memcpy (*addresses
, dns_cache
[h
].addresses
,
460 dns_cache
[h
].naddresses
461 * sizeof ((*addresses
)[0]));
462 return GRUB_ERR_NONE
;
466 data
.name
= grub_strdup (name
);
470 nb
= grub_netbuff_alloc (GRUB_NET_OUR_MAX_IP_HEADER_SIZE
471 + GRUB_NET_MAX_LINK_HEADER_SIZE
472 + GRUB_NET_UDP_HEADER_SIZE
473 + sizeof (struct dns_header
)
474 + grub_strlen (name
) + 2 + 4
478 grub_free (data
.name
);
481 grub_netbuff_reserve (nb
, GRUB_NET_OUR_MAX_IP_HEADER_SIZE
482 + GRUB_NET_MAX_LINK_HEADER_SIZE
483 + GRUB_NET_UDP_HEADER_SIZE
);
484 grub_netbuff_put (nb
, sizeof (struct dns_header
)
485 + grub_strlen (name
) + 2 + 4 + 2 + 4);
486 head
= (struct dns_header
*) nb
->data
;
487 optr
= (grub_uint8_t
*) (head
+ 1);
488 for (iptr
= name
; *iptr
; )
491 dot
= grub_strchr (iptr
, '.');
493 dot
= iptr
+ grub_strlen (iptr
);
494 if ((dot
- iptr
) >= 64)
496 grub_free (data
.name
);
497 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
498 N_("domain name component is too long"));
500 *optr
= (dot
- iptr
);
502 grub_memcpy (optr
, iptr
, dot
- iptr
);
518 /* Compressed name. */
530 head
->flags
= FLAGS_RD
;
531 head
->ra_z_r_code
= 0;
532 head
->qdcount
= grub_cpu_to_be16_compile_time (2);
533 head
->ancount
= grub_cpu_to_be16_compile_time (0);
534 head
->nscount
= grub_cpu_to_be16_compile_time (0);
535 head
->arcount
= grub_cpu_to_be16_compile_time (0);
539 for (i
= 0; i
< n_servers
* 4; i
++)
541 /* Connect to a next server. */
542 while (!(i
& 1) && send_servers
< n_servers
)
544 sockets
[send_servers
] = grub_net_udp_open (servers
[send_servers
],
549 if (!sockets
[send_servers
- 1])
552 grub_errno
= GRUB_ERR_NONE
;
562 if (*data
.naddresses
)
564 for (j
= 0; j
< send_servers
; j
++)
570 err2
= grub_net_send_udp_packet (sockets
[j
], nb
);
573 grub_errno
= GRUB_ERR_NONE
;
576 if (*data
.naddresses
)
579 grub_net_poll_cards (200, &data
.stop
);
582 grub_free (data
.name
);
583 grub_netbuff_free (nb
);
584 for (j
= 0; j
< send_servers
; j
++)
585 grub_net_udp_close (sockets
[j
]);
587 if (*data
.naddresses
)
588 return GRUB_ERR_NONE
;
590 return grub_error (GRUB_ERR_NET_NO_DOMAIN
,
591 N_("no DNS record found"));
598 return grub_error (GRUB_ERR_TIMEOUT
,
599 N_("no DNS reply received"));
603 grub_cmd_nslookup (struct grub_command
*cmd
__attribute__ ((unused
)),
604 int argc
, char **args
)
607 grub_size_t naddresses
, i
;
608 struct grub_net_network_level_address
*addresses
= 0;
609 if (argc
!= 2 && argc
!= 1)
610 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("two arguments expected"));
613 struct grub_net_network_level_address server
;
614 err
= grub_net_resolve_address (args
[1], &server
);
617 err
= grub_net_dns_lookup (args
[0], &server
, 1, &naddresses
,
621 err
= grub_net_dns_lookup (args
[0], dns_servers
, dns_nservers
, &naddresses
,
624 for (i
= 0; i
< naddresses
; i
++)
626 char buf
[GRUB_NET_MAX_STR_ADDR_LEN
];
627 grub_net_addr_to_str (&addresses
[i
], buf
);
628 grub_printf ("%s\n", buf
);
630 grub_free (addresses
);
631 return GRUB_ERR_NONE
;
635 grub_cmd_list_dns (struct grub_command
*cmd
__attribute__ ((unused
)),
636 int argc
__attribute__ ((unused
)),
637 char **args
__attribute__ ((unused
)))
640 for (i
= 0; i
< dns_nservers
; i
++)
642 char buf
[GRUB_NET_MAX_STR_ADDR_LEN
];
643 grub_net_addr_to_str (&dns_servers
[i
], buf
);
644 grub_printf ("%s\n", buf
);
646 return GRUB_ERR_NONE
;
650 grub_cmd_add_dns (struct grub_command
*cmd
__attribute__ ((unused
)),
651 int argc
, char **args
)
654 struct grub_net_network_level_address server
;
657 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("one argument expected"));
658 err
= grub_net_resolve_address (args
[0], &server
);
662 return grub_net_add_dns_server (&server
);
666 grub_cmd_del_dns (struct grub_command
*cmd
__attribute__ ((unused
)),
667 int argc
, char **args
)
670 struct grub_net_network_level_address server
;
673 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("one argument expected"));
674 err
= grub_net_resolve_address (args
[1], &server
);
678 return grub_net_add_dns_server (&server
);
681 static grub_command_t cmd
, cmd_add
, cmd_del
, cmd_list
;
686 cmd
= grub_register_command ("net_nslookup", grub_cmd_nslookup
,
687 N_("ADDRESS DNSSERVER"),
688 N_("Perform a DNS lookup"));
689 cmd_add
= grub_register_command ("net_add_dns", grub_cmd_add_dns
,
691 N_("Add a DNS server"));
692 cmd_del
= grub_register_command ("net_del_dns", grub_cmd_del_dns
,
694 N_("Remove a DNS server"));
695 cmd_list
= grub_register_command ("net_ls_dns", grub_cmd_list_dns
,
696 NULL
, N_("List DNS servers"));
702 grub_unregister_command (cmd
);
703 grub_unregister_command (cmd_add
);
704 grub_unregister_command (cmd_del
);
705 grub_unregister_command (cmd_list
);