52408aa946
We may get more than one response before exiting out of loop in grub_net_dns_lookup, but buffer was allocated for the first response only, so storing answers from subsequent replies wrote past allocated size. We never really use more than the very first address during lookup so there is little point in collecting all of them. Just quit early if we already have some reply. Code needs serious redesign to actually collect multiple answers and select the best fit according to requested type (IPv4 or IPv6). Reported and tested by Michael Chang <mchang@suse.com>
778 lines
19 KiB
C
778 lines
19 KiB
C
/*
|
|
* GRUB -- GRand Unified Bootloader
|
|
* Copyright (C) 2010,2011 Free Software Foundation, Inc.
|
|
*
|
|
* GRUB is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GRUB is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <grub/net.h>
|
|
#include <grub/net/udp.h>
|
|
#include <grub/command.h>
|
|
#include <grub/i18n.h>
|
|
#include <grub/err.h>
|
|
#include <grub/time.h>
|
|
|
|
struct dns_cache_element
|
|
{
|
|
char *name;
|
|
grub_size_t naddresses;
|
|
struct grub_net_network_level_address *addresses;
|
|
grub_uint64_t limit_time;
|
|
};
|
|
|
|
#define DNS_CACHE_SIZE 1021
|
|
#define DNS_HASH_BASE 423
|
|
|
|
typedef enum grub_dns_qtype_id
|
|
{
|
|
GRUB_DNS_QTYPE_A = 1,
|
|
GRUB_DNS_QTYPE_AAAA = 28
|
|
} grub_dns_qtype_id_t;
|
|
|
|
static struct dns_cache_element dns_cache[DNS_CACHE_SIZE];
|
|
static struct grub_net_network_level_address *dns_servers;
|
|
static grub_size_t dns_nservers, dns_servers_alloc;
|
|
|
|
grub_err_t
|
|
grub_net_add_dns_server (const struct grub_net_network_level_address *s)
|
|
{
|
|
if (dns_servers_alloc <= dns_nservers)
|
|
{
|
|
int na = dns_servers_alloc * 2;
|
|
struct grub_net_network_level_address *ns;
|
|
if (na < 8)
|
|
na = 8;
|
|
ns = grub_realloc (dns_servers, na * sizeof (ns[0]));
|
|
if (!ns)
|
|
return grub_errno;
|
|
dns_servers_alloc = na;
|
|
dns_servers = ns;
|
|
}
|
|
dns_servers[dns_nservers++] = *s;
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
void
|
|
grub_net_remove_dns_server (const struct grub_net_network_level_address *s)
|
|
{
|
|
grub_size_t i;
|
|
for (i = 0; i < dns_nservers; i++)
|
|
if (grub_net_addr_cmp (s, &dns_servers[i]) == 0)
|
|
break;
|
|
if (i < dns_nservers)
|
|
{
|
|
dns_servers[i] = dns_servers[dns_nservers - 1];
|
|
dns_nservers--;
|
|
}
|
|
}
|
|
|
|
struct dns_header
|
|
{
|
|
grub_uint16_t id;
|
|
grub_uint8_t flags;
|
|
grub_uint8_t ra_z_r_code;
|
|
grub_uint16_t qdcount;
|
|
grub_uint16_t ancount;
|
|
grub_uint16_t nscount;
|
|
grub_uint16_t arcount;
|
|
} GRUB_PACKED;
|
|
|
|
enum
|
|
{
|
|
FLAGS_RESPONSE = 0x80,
|
|
FLAGS_OPCODE = 0x78,
|
|
FLAGS_RD = 0x01
|
|
};
|
|
|
|
enum
|
|
{
|
|
ERRCODE_MASK = 0x0f
|
|
};
|
|
|
|
enum
|
|
{
|
|
DNS_PORT = 53
|
|
};
|
|
|
|
struct recv_data
|
|
{
|
|
grub_size_t *naddresses;
|
|
struct grub_net_network_level_address **addresses;
|
|
int cache;
|
|
grub_uint16_t id;
|
|
int dns_err;
|
|
char *name;
|
|
const char *oname;
|
|
int stop;
|
|
};
|
|
|
|
static inline int
|
|
hash (const char *str)
|
|
{
|
|
unsigned v = 0, xn = 1;
|
|
const char *ptr;
|
|
for (ptr = str; *ptr; )
|
|
{
|
|
v = (v + xn * *ptr);
|
|
xn = (DNS_HASH_BASE * xn) % DNS_CACHE_SIZE;
|
|
ptr++;
|
|
if (((ptr - str) & 0x3ff) == 0)
|
|
v %= DNS_CACHE_SIZE;
|
|
}
|
|
return v % DNS_CACHE_SIZE;
|
|
}
|
|
|
|
static int
|
|
check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head,
|
|
const grub_uint8_t *tail, const char *check_with,
|
|
int *length, char *set)
|
|
{
|
|
const char *readable_ptr = check_with;
|
|
const grub_uint8_t *ptr;
|
|
char *optr = set;
|
|
int bytes_processed = 0;
|
|
if (length)
|
|
*length = 0;
|
|
for (ptr = name_at; ptr < tail && bytes_processed < tail - head + 2; )
|
|
{
|
|
/* End marker. */
|
|
if (!*ptr)
|
|
{
|
|
if (length && *length)
|
|
(*length)--;
|
|
if (optr && optr != set)
|
|
optr--;
|
|
if (optr)
|
|
*optr = 0;
|
|
return !readable_ptr || (*readable_ptr == 0);
|
|
}
|
|
if (*ptr & 0xc0)
|
|
{
|
|
bytes_processed += 2;
|
|
if (ptr + 1 >= tail)
|
|
return 0;
|
|
ptr = head + (((ptr[0] & 0x3f) << 8) | ptr[1]);
|
|
continue;
|
|
}
|
|
if (readable_ptr && grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0)
|
|
return 0;
|
|
if (grub_memchr (ptr + 1, 0, *ptr)
|
|
|| grub_memchr (ptr + 1, '.', *ptr))
|
|
return 0;
|
|
if (readable_ptr)
|
|
readable_ptr += *ptr;
|
|
if (readable_ptr && *readable_ptr != '.' && *readable_ptr != 0)
|
|
return 0;
|
|
bytes_processed += *ptr + 1;
|
|
if (length)
|
|
*length += *ptr + 1;
|
|
if (optr)
|
|
{
|
|
grub_memcpy (optr, ptr + 1, *ptr);
|
|
optr += *ptr;
|
|
}
|
|
if (optr)
|
|
*optr++ = '.';
|
|
if (readable_ptr && *readable_ptr)
|
|
readable_ptr++;
|
|
ptr += *ptr + 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
check_name (const grub_uint8_t *name_at, const grub_uint8_t *head,
|
|
const grub_uint8_t *tail, const char *check_with)
|
|
{
|
|
return check_name_real (name_at, head, tail, check_with, NULL, NULL);
|
|
}
|
|
|
|
static char *
|
|
get_name (const grub_uint8_t *name_at, const grub_uint8_t *head,
|
|
const grub_uint8_t *tail)
|
|
{
|
|
int length;
|
|
char *ret;
|
|
|
|
if (!check_name_real (name_at, head, tail, NULL, &length, NULL))
|
|
return NULL;
|
|
ret = grub_malloc (length + 1);
|
|
if (!ret)
|
|
return NULL;
|
|
if (!check_name_real (name_at, head, tail, NULL, NULL, ret))
|
|
{
|
|
grub_free (ret);
|
|
return NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
enum
|
|
{
|
|
DNS_CLASS_A = 1,
|
|
DNS_CLASS_CNAME = 5,
|
|
DNS_CLASS_AAAA = 28
|
|
};
|
|
|
|
static grub_err_t
|
|
recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)),
|
|
struct grub_net_buff *nb,
|
|
void *data_)
|
|
{
|
|
struct dns_header *head;
|
|
struct recv_data *data = data_;
|
|
int i, j;
|
|
grub_uint8_t *ptr, *reparse_ptr;
|
|
int redirect_cnt = 0;
|
|
char *redirect_save = NULL;
|
|
grub_uint32_t ttl_all = ~0U;
|
|
|
|
/* Code apparently assumed that only one packet is received as response.
|
|
We may get multiple responses due to network condition, so check here
|
|
and quit early. */
|
|
if (*data->addresses)
|
|
{
|
|
grub_netbuff_free (nb);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
head = (struct dns_header *) nb->data;
|
|
ptr = (grub_uint8_t *) (head + 1);
|
|
if (ptr >= nb->tail)
|
|
{
|
|
grub_netbuff_free (nb);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
if (head->id != data->id)
|
|
{
|
|
grub_netbuff_free (nb);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
if (!(head->flags & FLAGS_RESPONSE) || (head->flags & FLAGS_OPCODE))
|
|
{
|
|
grub_netbuff_free (nb);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
if (head->ra_z_r_code & ERRCODE_MASK)
|
|
{
|
|
data->dns_err = 1;
|
|
grub_netbuff_free (nb);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
for (i = 0; i < grub_be_to_cpu16 (head->qdcount); i++)
|
|
{
|
|
if (ptr >= nb->tail)
|
|
{
|
|
grub_netbuff_free (nb);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
while (ptr < nb->tail && !((*ptr & 0xc0) || *ptr == 0))
|
|
ptr += *ptr + 1;
|
|
if (ptr < nb->tail && (*ptr & 0xc0))
|
|
ptr++;
|
|
ptr++;
|
|
ptr += 4;
|
|
}
|
|
*data->addresses = grub_malloc (sizeof ((*data->addresses)[0])
|
|
* grub_be_to_cpu16 (head->ancount));
|
|
if (!*data->addresses)
|
|
{
|
|
grub_errno = GRUB_ERR_NONE;
|
|
grub_netbuff_free (nb);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
reparse_ptr = ptr;
|
|
reparse:
|
|
for (i = 0, ptr = reparse_ptr; i < grub_be_to_cpu16 (head->ancount); i++)
|
|
{
|
|
int ignored = 0;
|
|
grub_uint8_t class;
|
|
grub_uint32_t ttl = 0;
|
|
grub_uint16_t length;
|
|
if (ptr >= nb->tail)
|
|
{
|
|
if (!*data->naddresses)
|
|
grub_free (*data->addresses);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
ignored = !check_name (ptr, nb->data, nb->tail, data->name);
|
|
while (ptr < nb->tail && !((*ptr & 0xc0) || *ptr == 0))
|
|
ptr += *ptr + 1;
|
|
if (ptr < nb->tail && (*ptr & 0xc0))
|
|
ptr++;
|
|
ptr++;
|
|
if (ptr + 10 >= nb->tail)
|
|
{
|
|
if (!*data->naddresses)
|
|
grub_free (*data->addresses);
|
|
grub_netbuff_free (nb);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
if (*ptr++ != 0)
|
|
ignored = 1;
|
|
class = *ptr++;
|
|
if (*ptr++ != 0)
|
|
ignored = 1;
|
|
if (*ptr++ != 1)
|
|
ignored = 1;
|
|
for (j = 0; j < 4; j++)
|
|
{
|
|
ttl <<= 8;
|
|
ttl |= *ptr++;
|
|
}
|
|
length = *ptr++ << 8;
|
|
length |= *ptr++;
|
|
if (ptr + length > nb->tail)
|
|
{
|
|
if (!*data->naddresses)
|
|
grub_free (*data->addresses);
|
|
grub_netbuff_free (nb);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
if (!ignored)
|
|
{
|
|
if (ttl_all > ttl)
|
|
ttl_all = ttl;
|
|
switch (class)
|
|
{
|
|
case DNS_CLASS_A:
|
|
if (length != 4)
|
|
break;
|
|
(*data->addresses)[*data->naddresses].type
|
|
= GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
|
|
grub_memcpy (&(*data->addresses)[*data->naddresses].ipv4,
|
|
ptr, 4);
|
|
(*data->naddresses)++;
|
|
data->stop = 1;
|
|
break;
|
|
case DNS_CLASS_AAAA:
|
|
if (length != 16)
|
|
break;
|
|
(*data->addresses)[*data->naddresses].type
|
|
= GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
|
|
grub_memcpy (&(*data->addresses)[*data->naddresses].ipv6,
|
|
ptr, 16);
|
|
(*data->naddresses)++;
|
|
data->stop = 1;
|
|
break;
|
|
case DNS_CLASS_CNAME:
|
|
if (!(redirect_cnt & (redirect_cnt - 1)))
|
|
{
|
|
grub_free (redirect_save);
|
|
redirect_save = data->name;
|
|
}
|
|
else
|
|
grub_free (data->name);
|
|
redirect_cnt++;
|
|
data->name = get_name (ptr, nb->data, nb->tail);
|
|
if (!data->name)
|
|
{
|
|
data->dns_err = 1;
|
|
grub_errno = 0;
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
grub_dprintf ("dns", "CNAME %s\n", data->name);
|
|
if (grub_strcmp (redirect_save, data->name) == 0)
|
|
{
|
|
data->dns_err = 1;
|
|
grub_free (redirect_save);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
goto reparse;
|
|
}
|
|
}
|
|
ptr += length;
|
|
}
|
|
if (ttl_all && *data->naddresses && data->cache)
|
|
{
|
|
int h;
|
|
grub_dprintf ("dns", "caching for %d seconds\n", ttl_all);
|
|
h = hash (data->oname);
|
|
grub_free (dns_cache[h].name);
|
|
dns_cache[h].name = 0;
|
|
grub_free (dns_cache[h].addresses);
|
|
dns_cache[h].addresses = 0;
|
|
dns_cache[h].name = grub_strdup (data->oname);
|
|
dns_cache[h].naddresses = *data->naddresses;
|
|
dns_cache[h].addresses = grub_malloc (*data->naddresses
|
|
* sizeof (dns_cache[h].addresses[0]));
|
|
dns_cache[h].limit_time = grub_get_time_ms () + 1000 * ttl_all;
|
|
if (!dns_cache[h].addresses || !dns_cache[h].name)
|
|
{
|
|
grub_free (dns_cache[h].name);
|
|
dns_cache[h].name = 0;
|
|
grub_free (dns_cache[h].addresses);
|
|
dns_cache[h].addresses = 0;
|
|
}
|
|
grub_memcpy (dns_cache[h].addresses, *data->addresses,
|
|
*data->naddresses
|
|
* sizeof (dns_cache[h].addresses[0]));
|
|
}
|
|
grub_netbuff_free (nb);
|
|
grub_free (redirect_save);
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
grub_err_t
|
|
grub_net_dns_lookup (const char *name,
|
|
const struct grub_net_network_level_address *servers,
|
|
grub_size_t n_servers,
|
|
grub_size_t *naddresses,
|
|
struct grub_net_network_level_address **addresses,
|
|
int cache)
|
|
{
|
|
grub_size_t send_servers = 0;
|
|
grub_size_t i, j;
|
|
struct grub_net_buff *nb;
|
|
grub_net_udp_socket_t *sockets;
|
|
grub_uint8_t *optr;
|
|
const char *iptr;
|
|
struct dns_header *head;
|
|
static grub_uint16_t id = 1;
|
|
grub_uint8_t *qtypeptr;
|
|
grub_err_t err = GRUB_ERR_NONE;
|
|
struct recv_data data = {naddresses, addresses, cache,
|
|
grub_cpu_to_be16 (id++), 0, 0, name, 0};
|
|
grub_uint8_t *nbd;
|
|
grub_size_t try_server = 0;
|
|
|
|
if (!servers)
|
|
{
|
|
servers = dns_servers;
|
|
n_servers = dns_nservers;
|
|
}
|
|
|
|
if (!n_servers)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
N_("no DNS servers configured"));
|
|
|
|
*naddresses = 0;
|
|
if (cache)
|
|
{
|
|
int h;
|
|
h = hash (name);
|
|
if (dns_cache[h].name && grub_strcmp (dns_cache[h].name, name) == 0
|
|
&& grub_get_time_ms () < dns_cache[h].limit_time)
|
|
{
|
|
grub_dprintf ("dns", "retrieved from cache\n");
|
|
*addresses = grub_malloc (dns_cache[h].naddresses
|
|
* sizeof ((*addresses)[0]));
|
|
if (!*addresses)
|
|
return grub_errno;
|
|
*naddresses = dns_cache[h].naddresses;
|
|
grub_memcpy (*addresses, dns_cache[h].addresses,
|
|
dns_cache[h].naddresses
|
|
* sizeof ((*addresses)[0]));
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
}
|
|
|
|
sockets = grub_malloc (sizeof (sockets[0]) * n_servers);
|
|
if (!sockets)
|
|
return grub_errno;
|
|
|
|
data.name = grub_strdup (name);
|
|
if (!data.name)
|
|
{
|
|
grub_free (sockets);
|
|
return grub_errno;
|
|
}
|
|
|
|
nb = grub_netbuff_alloc (GRUB_NET_OUR_MAX_IP_HEADER_SIZE
|
|
+ GRUB_NET_MAX_LINK_HEADER_SIZE
|
|
+ GRUB_NET_UDP_HEADER_SIZE
|
|
+ sizeof (struct dns_header)
|
|
+ grub_strlen (name) + 2 + 4);
|
|
if (!nb)
|
|
{
|
|
grub_free (sockets);
|
|
grub_free (data.name);
|
|
return grub_errno;
|
|
}
|
|
grub_netbuff_reserve (nb, GRUB_NET_OUR_MAX_IP_HEADER_SIZE
|
|
+ GRUB_NET_MAX_LINK_HEADER_SIZE
|
|
+ GRUB_NET_UDP_HEADER_SIZE);
|
|
grub_netbuff_put (nb, sizeof (struct dns_header)
|
|
+ grub_strlen (name) + 2 + 4);
|
|
head = (struct dns_header *) nb->data;
|
|
optr = (grub_uint8_t *) (head + 1);
|
|
for (iptr = name; *iptr; )
|
|
{
|
|
const char *dot;
|
|
dot = grub_strchr (iptr, '.');
|
|
if (!dot)
|
|
dot = iptr + grub_strlen (iptr);
|
|
if ((dot - iptr) >= 64)
|
|
{
|
|
grub_free (sockets);
|
|
grub_free (data.name);
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
N_("domain name component is too long"));
|
|
}
|
|
*optr = (dot - iptr);
|
|
optr++;
|
|
grub_memcpy (optr, iptr, dot - iptr);
|
|
optr += dot - iptr;
|
|
iptr = dot;
|
|
if (*iptr)
|
|
iptr++;
|
|
}
|
|
*optr++ = 0;
|
|
|
|
/* Type. */
|
|
*optr++ = 0;
|
|
qtypeptr = optr++;
|
|
|
|
/* Class. */
|
|
*optr++ = 0;
|
|
*optr++ = 1;
|
|
|
|
head->id = data.id;
|
|
head->flags = FLAGS_RD;
|
|
head->ra_z_r_code = 0;
|
|
head->qdcount = grub_cpu_to_be16_compile_time (1);
|
|
head->ancount = grub_cpu_to_be16_compile_time (0);
|
|
head->nscount = grub_cpu_to_be16_compile_time (0);
|
|
head->arcount = grub_cpu_to_be16_compile_time (0);
|
|
|
|
nbd = nb->data;
|
|
|
|
for (i = 0; i < n_servers * 4; i++)
|
|
{
|
|
/* Connect to a next server. */
|
|
while (!(i & 1) && try_server < n_servers)
|
|
{
|
|
sockets[send_servers] = grub_net_udp_open (servers[try_server++],
|
|
DNS_PORT,
|
|
recv_hook,
|
|
&data);
|
|
if (!sockets[send_servers])
|
|
{
|
|
err = grub_errno;
|
|
grub_errno = GRUB_ERR_NONE;
|
|
}
|
|
else
|
|
{
|
|
send_servers++;
|
|
break;
|
|
}
|
|
}
|
|
if (!send_servers)
|
|
goto out;
|
|
if (*data.naddresses)
|
|
goto out;
|
|
for (j = 0; j < send_servers; j++)
|
|
{
|
|
grub_err_t err2;
|
|
|
|
grub_size_t t = 0;
|
|
do
|
|
{
|
|
nb->data = nbd;
|
|
if (servers[j].option == DNS_OPTION_IPV4 ||
|
|
((servers[j].option == DNS_OPTION_PREFER_IPV4) && (t++ == 0)) ||
|
|
((servers[j].option == DNS_OPTION_PREFER_IPV6) && (t++ == 1)))
|
|
*qtypeptr = GRUB_DNS_QTYPE_A;
|
|
else
|
|
*qtypeptr = GRUB_DNS_QTYPE_AAAA;
|
|
|
|
grub_dprintf ("dns", "QTYPE: %u QNAME: %s\n", *qtypeptr, name);
|
|
|
|
err2 = grub_net_send_udp_packet (sockets[j], nb);
|
|
if (err2)
|
|
{
|
|
grub_errno = GRUB_ERR_NONE;
|
|
err = err2;
|
|
}
|
|
if (*data.naddresses)
|
|
goto out;
|
|
}
|
|
while (t == 1);
|
|
}
|
|
grub_net_poll_cards (200, &data.stop);
|
|
}
|
|
out:
|
|
grub_free (data.name);
|
|
grub_netbuff_free (nb);
|
|
for (j = 0; j < send_servers; j++)
|
|
grub_net_udp_close (sockets[j]);
|
|
|
|
grub_free (sockets);
|
|
|
|
if (*data.naddresses)
|
|
return GRUB_ERR_NONE;
|
|
if (data.dns_err)
|
|
return grub_error (GRUB_ERR_NET_NO_DOMAIN,
|
|
N_("no DNS record found"));
|
|
|
|
if (err)
|
|
{
|
|
grub_errno = err;
|
|
return err;
|
|
}
|
|
return grub_error (GRUB_ERR_TIMEOUT,
|
|
N_("no DNS reply received"));
|
|
}
|
|
|
|
static grub_err_t
|
|
grub_cmd_nslookup (struct grub_command *cmd __attribute__ ((unused)),
|
|
int argc, char **args)
|
|
{
|
|
grub_err_t err;
|
|
struct grub_net_network_level_address cmd_server;
|
|
struct grub_net_network_level_address *servers;
|
|
grub_size_t nservers, i, naddresses = 0;
|
|
struct grub_net_network_level_address *addresses = 0;
|
|
if (argc != 2 && argc != 1)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected"));
|
|
if (argc == 2)
|
|
{
|
|
err = grub_net_resolve_address (args[1], &cmd_server);
|
|
if (err)
|
|
return err;
|
|
servers = &cmd_server;
|
|
nservers = 1;
|
|
}
|
|
else
|
|
{
|
|
servers = dns_servers;
|
|
nservers = dns_nservers;
|
|
}
|
|
|
|
grub_net_dns_lookup (args[0], servers, nservers, &naddresses,
|
|
&addresses, 0);
|
|
|
|
for (i = 0; i < naddresses; i++)
|
|
{
|
|
char buf[GRUB_NET_MAX_STR_ADDR_LEN];
|
|
grub_net_addr_to_str (&addresses[i], buf);
|
|
grub_printf ("%s\n", buf);
|
|
}
|
|
grub_free (addresses);
|
|
if (naddresses)
|
|
return GRUB_ERR_NONE;
|
|
return grub_error (GRUB_ERR_NET_NO_DOMAIN, N_("no DNS record found"));
|
|
}
|
|
|
|
static grub_err_t
|
|
grub_cmd_list_dns (struct grub_command *cmd __attribute__ ((unused)),
|
|
int argc __attribute__ ((unused)),
|
|
char **args __attribute__ ((unused)))
|
|
{
|
|
grub_size_t i;
|
|
const char *strtype = "";
|
|
|
|
for (i = 0; i < dns_nservers; i++)
|
|
{
|
|
switch (dns_servers[i].option)
|
|
{
|
|
case DNS_OPTION_IPV4:
|
|
strtype = _("only ipv4");
|
|
break;
|
|
|
|
case DNS_OPTION_IPV6:
|
|
strtype = _("only ipv6");
|
|
break;
|
|
|
|
case DNS_OPTION_PREFER_IPV4:
|
|
strtype = _("prefer ipv4");
|
|
break;
|
|
|
|
case DNS_OPTION_PREFER_IPV6:
|
|
strtype = _("prefer ipv6");
|
|
break;
|
|
}
|
|
|
|
char buf[GRUB_NET_MAX_STR_ADDR_LEN];
|
|
grub_net_addr_to_str (&dns_servers[i], buf);
|
|
grub_printf ("%s (%s)\n", buf, strtype);
|
|
}
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
static grub_err_t
|
|
grub_cmd_add_dns (struct grub_command *cmd __attribute__ ((unused)),
|
|
int argc, char **args)
|
|
{
|
|
grub_err_t err;
|
|
struct grub_net_network_level_address server;
|
|
|
|
if ((argc < 1) || (argc > 2))
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
|
|
else if (argc == 1)
|
|
server.option = DNS_OPTION_PREFER_IPV4;
|
|
else
|
|
{
|
|
if (grub_strcmp (args[1], "--only-ipv4") == 0)
|
|
server.option = DNS_OPTION_IPV4;
|
|
else if (grub_strcmp (args[1], "--only-ipv6") == 0)
|
|
server.option = DNS_OPTION_IPV6;
|
|
else if (grub_strcmp (args[1], "--prefer-ipv4") == 0)
|
|
server.option = DNS_OPTION_PREFER_IPV4;
|
|
else if (grub_strcmp (args[1], "--prefer-ipv6") == 0)
|
|
server.option = DNS_OPTION_PREFER_IPV6;
|
|
else
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid argument"));
|
|
}
|
|
|
|
err = grub_net_resolve_address (args[0], &server);
|
|
if (err)
|
|
return err;
|
|
|
|
return grub_net_add_dns_server (&server);
|
|
}
|
|
|
|
static grub_err_t
|
|
grub_cmd_del_dns (struct grub_command *cmd __attribute__ ((unused)),
|
|
int argc, char **args)
|
|
{
|
|
grub_err_t err;
|
|
struct grub_net_network_level_address server;
|
|
|
|
if (argc != 1)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
|
|
err = grub_net_resolve_address (args[1], &server);
|
|
if (err)
|
|
return err;
|
|
|
|
return grub_net_add_dns_server (&server);
|
|
}
|
|
|
|
static grub_command_t cmd, cmd_add, cmd_del, cmd_list;
|
|
|
|
void
|
|
grub_dns_init (void)
|
|
{
|
|
cmd = grub_register_command ("net_nslookup", grub_cmd_nslookup,
|
|
N_("ADDRESS DNSSERVER"),
|
|
N_("Perform a DNS lookup"));
|
|
cmd_add = grub_register_command ("net_add_dns", grub_cmd_add_dns,
|
|
N_("DNSSERVER"),
|
|
N_("Add a DNS server"));
|
|
cmd_del = grub_register_command ("net_del_dns", grub_cmd_del_dns,
|
|
N_("DNSSERVER"),
|
|
N_("Remove a DNS server"));
|
|
cmd_list = grub_register_command ("net_ls_dns", grub_cmd_list_dns,
|
|
NULL, N_("List DNS servers"));
|
|
}
|
|
|
|
void
|
|
grub_dns_fini (void)
|
|
{
|
|
grub_unregister_command (cmd);
|
|
grub_unregister_command (cmd_add);
|
|
grub_unregister_command (cmd_del);
|
|
grub_unregister_command (cmd_list);
|
|
}
|