struct domain_hdr *hdr;
int err, offset = protocol_offset(protocol);
- DBG("");
+ DBG("sk %d", sk);
if (offset < 0)
return;
err = sendto(sk, buf, len, MSG_NOSIGNAL, to, tolen);
if (err < 0) {
- connman_error("Failed to send DNS response: %s",
- strerror(errno));
+ connman_error("Failed to send DNS response to %d: %s",
+ sk, strerror(errno));
return;
}
}
return type;
}
-static struct cache_entry *cache_check(gpointer request, int *qtype)
+static struct cache_entry *cache_check(gpointer request, int *qtype, int proto)
{
- char *question = request + 12;
+ char *question;
struct cache_entry *entry;
struct domain_question *q;
uint16_t type;
- int offset;
+ int offset, proto_offset;
+
+ if (request == NULL)
+ return NULL;
+
+ proto_offset = protocol_offset(proto);
+ if (proto_offset < 0)
+ return NULL;
+
+ question = request + proto_offset + 12;
offset = strlen(question) + 1;
q = (void *) (question + offset);
int offset = protocol_offset(srv->protocol);
int err, qlen, ttl = 0;
uint16_t answers = 0, type = 0, class = 0;
+ struct domain_hdr *hdr = (void *)(msg + offset);
struct domain_question *q;
struct cache_entry *entry;
struct cache_data *data;
next_refresh = current_time + 30;
}
-
- /* Continue only if response code is 0 (=ok) */
- if (msg[3] & 0x0f)
+ if (offset < 0)
return 0;
- if (offset < 0)
+ DBG("offset %d hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode);
+
+ /* Continue only if response code is 0 (=ok) */
+ if (hdr->rcode != 0)
return 0;
rsplen = sizeof(response) - 1;
* to cache the negative response.
*/
if ((err == -ENOMSG || err == -ENOBUFS) &&
- reply_query_type(msg, msg_len) == 28) {
+ reply_query_type(msg + offset,
+ msg_len - offset) == 28) {
entry = g_hash_table_lookup(cache, question);
if (entry && entry->ipv4 && entry->ipv6 == NULL) {
+ int cache_offset = 0;
+
data = g_try_new(struct cache_data, 1);
if (data == NULL)
return -ENOMEM;
data->inserted = entry->ipv4->inserted;
data->type = type;
- data->answers = msg[5];
+ data->answers = hdr->ancount;
data->timeout = entry->ipv4->timeout;
- data->data_len = msg_len;
- data->data = ptr = g_malloc(msg_len);
+ if (srv->protocol == IPPROTO_UDP)
+ cache_offset = 2;
+ data->data_len = msg_len + cache_offset;
+ data->data = ptr = g_malloc(data->data_len);
+ ptr[0] = (data->data_len - 2) / 256;
+ ptr[1] = (data->data_len - 2) - ptr[0] * 256;
+ if (srv->protocol == IPPROTO_UDP)
+ ptr += 2;
data->valid_until = entry->ipv4->valid_until;
data->cache_until = entry->ipv4->cache_until;
- memcpy(data->data, msg, msg_len);
+ memcpy(ptr, msg, msg_len);
entry->ipv6 = data;
/*
* we will get a "hit" when we serve the response
char *dot, *lookup = (char *) name;
struct cache_entry *entry;
- entry = cache_check(request, &type);
+ entry = cache_check(request, &type, req->protocol);
if (entry != NULL) {
int ttl_left = 0;
struct cache_data *data;
close(sk);
}
+ if (err < 0)
+ DBG("Cannot send msg, sk %d proto %d errno %d/%s", sk,
+ protocol, errno, strerror(errno));
+ else
+ DBG("proto %d sent %d bytes to %d", protocol, err, sk);
+
destroy_request_data(req);
return err;
{
GList *list;
- DBG("interface %s server %s", server->interface, server->server);
+ DBG("interface %s server %s sock %d", server->interface, server->server,
+ g_io_channel_unix_get_fd(server->channel));
server_list = g_slist_remove(server_list, server);
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
GSList *list;
hangup:
- DBG("TCP server channel closed");
+ DBG("TCP server channel closed, sk %d", sk);
/*
* Discard any partial response which is buffered; better
reply_len = reply_len_buf[1] | reply_len_buf[0] << 8;
reply_len += 2;
- DBG("TCP reply %d bytes", reply_len);
+ DBG("TCP reply %d bytes from %d", reply_len, sk);
reply = g_try_malloc(sizeof(*reply) + reply_len + 2);
if (!reply)
return NULL;
}
+ DBG("sk %d", sk);
+
if (interface != NULL) {
if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
interface, strlen(interface) + 1) < 0) {
socklen_t client_addr_len = sizeof(client_addr);
GSList *list;
struct listener_data *ifdata = user_data;
- int waiting_for_connect = FALSE;
+ int waiting_for_connect = FALSE, qtype = 0;
+ struct cache_entry *entry;
DBG("condition 0x%x", condition);
if (len < 2)
return TRUE;
- DBG("Received %d bytes (id 0x%04x)", len, buf[2] | buf[3] << 8);
+ DBG("Received %d bytes (id 0x%04x) from %d", len,
+ buf[2] | buf[3] << 8, client_sk);
err = parse_request(buf + 2, len - 2, query, sizeof(query));
if (err < 0 || (g_slist_length(server_list) == 0)) {
req->ifdata = (struct listener_data *) ifdata;
req->append_domain = FALSE;
+ /*
+ * Check if the answer is found in the cache before
+ * creating sockets to the server.
+ */
+ entry = cache_check(buf, &qtype, IPPROTO_TCP);
+ if (entry != NULL) {
+ int ttl_left = 0;
+ struct cache_data *data;
+
+ DBG("cache hit %s type %s", query, qtype == 1 ? "A" : "AAAA");
+ if (qtype == 1)
+ data = entry->ipv4;
+ else
+ data = entry->ipv6;
+
+ if (data != NULL) {
+ ttl_left = data->valid_until - time(NULL);
+ entry->hits++;
+
+ send_cached_response(client_sk, data->data,
+ data->data_len, NULL, 0, IPPROTO_TCP,
+ req->srcid, data->answers, ttl_left);
+
+ g_free(req);
+ return TRUE;
+ } else
+ DBG("data missing, ignoring cache for this query");
+ }
+
for (list = server_list; list; list = list->next) {
struct server_data *data = list->data;