DNS cache support

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2011-12-16 15:08:46 +01:00
parent 3729fcfc1a
commit 8d4e4fc0e5

View file

@ -21,16 +21,21 @@
#include <grub/command.h> #include <grub/command.h>
#include <grub/i18n.h> #include <grub/i18n.h>
#include <grub/err.h> #include <grub/err.h>
#include <grub/time.h>
struct dns_cache_element struct dns_cache_element
{ {
char *name; char *name;
grub_size_t naddresses; grub_size_t naddresses;
struct grub_net_network_level_address *addresses; struct grub_net_network_level_address *addresses;
struct grub_net_network_level_address *source;
grub_uint64_t limit_time; grub_uint64_t limit_time;
}; };
#define DNS_CACHE_SIZE 1021
#define DNS_HASH_BASE 423
static struct dns_cache_element dns_cache[DNS_CACHE_SIZE];
struct dns_header struct dns_header
{ {
grub_uint16_t id; grub_uint16_t id;
@ -67,8 +72,25 @@ struct recv_data
grub_uint16_t id; grub_uint16_t id;
int dns_err; int dns_err;
char *name; char *name;
const char *oname;
}; };
static inline int
hash (const char *str)
{
int 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 static int
check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head, check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head,
const grub_uint8_t *tail, const char *check_with, const grub_uint8_t *tail, const char *check_with,
@ -172,6 +194,7 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)),
grub_uint8_t *ptr, *reparse_ptr; grub_uint8_t *ptr, *reparse_ptr;
int redirect_cnt = 0; int redirect_cnt = 0;
char *redirect_save = NULL; char *redirect_save = NULL;
grub_uint32_t ttl_all = ~0U;
head = (struct dns_header *) nb->data; head = (struct dns_header *) nb->data;
ptr = (grub_uint8_t *) (head + 1); ptr = (grub_uint8_t *) (head + 1);
@ -255,8 +278,8 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)),
ignored = 1; ignored = 1;
for (j = 0; j < 4; j++) for (j = 0; j < 4; j++)
{ {
ttl >>= 8; ttl <<= 8;
ttl = *ptr++ << 28; ttl |= *ptr++;
} }
length = *ptr++ << 8; length = *ptr++ << 8;
length |= *ptr++; length |= *ptr++;
@ -268,53 +291,82 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)),
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
if (!ignored) if (!ignored)
switch (class) {
{ if (ttl_all > ttl)
case DNS_CLASS_A: ttl_all = ttl;
if (length != 4) 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)++;
break; break;
(*data->addresses)[*data->naddresses].type case DNS_CLASS_AAAA:
= GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; if (length != 16)
grub_memcpy (&(*data->addresses)[*data->naddresses].ipv4, break;
ptr, 4); (*data->addresses)[*data->naddresses].type
(*data->naddresses)++; = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
break; grub_memcpy (&(*data->addresses)[*data->naddresses].ipv6,
case DNS_CLASS_AAAA: ptr, 16);
if (length != 16) (*data->naddresses)++;
break; break;
(*data->addresses)[*data->naddresses].type case DNS_CLASS_CNAME:
= GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; if (!(redirect_cnt & (redirect_cnt - 1)))
grub_memcpy (&(*data->addresses)[*data->naddresses].ipv6, {
ptr, 16); grub_free (redirect_save);
(*data->naddresses)++; redirect_save = data->name;
break; }
case DNS_CLASS_CNAME: else
if (!(redirect_cnt & (redirect_cnt - 1))) grub_free (data->name);
{ redirect_cnt++;
grub_free (redirect_save); data->name = get_name (ptr, nb->data, nb->tail);
redirect_save = data->name; if (!data->name)
} {
else data->dns_err = 1;
grub_free (data->name); grub_errno = 0;
redirect_cnt++; return GRUB_ERR_NONE;
data->name = get_name (ptr, nb->data, nb->tail); }
if (!data->name) grub_dprintf ("dns", "CNAME %s\n", data->name);
{ if (grub_strcmp (redirect_save, data->name) == 0)
data->dns_err = 1; {
grub_errno = 0; data->dns_err = 1;
return GRUB_ERR_NONE; grub_free (redirect_save);
} return GRUB_ERR_NONE;
grub_dprintf ("dns", "CNAME %s\n", data->name); }
if (grub_strcmp (redirect_save, data->name) == 0) goto reparse;
{ }
data->dns_err = 1;
grub_free (redirect_save);
return GRUB_ERR_NONE;
}
goto reparse;
} }
ptr += length; 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_netbuff_free (nb);
grub_free (redirect_save); grub_free (redirect_save);
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
@ -338,18 +390,35 @@ grub_net_dns_lookup (const char *name,
static grub_uint16_t id = 1; static grub_uint16_t id = 1;
grub_err_t err = GRUB_ERR_NONE; grub_err_t err = GRUB_ERR_NONE;
struct recv_data data = {naddresses, addresses, cache, struct recv_data data = {naddresses, addresses, cache,
grub_cpu_to_be16 (id++), 0, 0}; grub_cpu_to_be16 (id++), 0, 0, name};
grub_uint8_t *nbd; grub_uint8_t *nbd;
int have_server = 0; int have_server = 0;
*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;
}
}
data.name = grub_strdup (name); data.name = grub_strdup (name);
if (!data.name) if (!data.name)
return grub_errno; return grub_errno;
*naddresses = 0;
/* if (cache && cache_lookup (name, servers, n_servers, addresses))
return GRUB_ERR_NONE;*/
nb = grub_netbuff_alloc (GRUB_NET_OUR_MAX_IP_HEADER_SIZE nb = grub_netbuff_alloc (GRUB_NET_OUR_MAX_IP_HEADER_SIZE
+ GRUB_NET_MAX_LINK_HEADER_SIZE + GRUB_NET_MAX_LINK_HEADER_SIZE
+ GRUB_NET_UDP_HEADER_SIZE + GRUB_NET_UDP_HEADER_SIZE