#include "daemon/worker.h"
#include "util/log.h"
#include "util/config_file.h"
+#include "util/data/msgreply.h"
#include "services/listen_dnsport.h"
#include <signal.h>
signal_handling_record();
checklock_start();
daemon->need_to_exit = 0;
+ daemon->msg_cache = lruhash_create(HASH_DEFAULT_STARTARRAY,
+ HASH_DEFAULT_MAXMEM, msgreply_sizefunc, query_info_compare,
+ query_info_delete, reply_info_delete, NULL);
+ if(!daemon->msg_cache) {
+ free(daemon);
+ return NULL;
+ }
alloc_init(&daemon->superalloc, NULL);
return daemon;
}
if(!daemon)
return;
listening_ports_free(daemon->ports);
+ lruhash_delete(daemon->msg_cache);
alloc_clear(&daemon->superalloc);
free(daemon->cwd);
free(daemon->pidfile);
struct config_file;
struct worker;
struct listen_port;
+struct lruhash;
/**
* Structure holding worker list.
int need_to_exit;
/** master allocation cache */
struct alloc_cache superalloc;
+ /** the message cache, content is struct msgreply_entry* */
+ struct lruhash* msg_cache;
};
/**
#include <signal.h>
/** timeout in seconds for UDP queries to auth servers. TODO: proper rtt */
-#define UDP_QUERY_TIMEOUT 5
-/** the size of ID and flags, opcode, rcode in dns packet */
-#define ID_AND_FLAGS 4
+#define UDP_QUERY_TIMEOUT 4
void
worker_send_cmd(struct worker* worker, ldns_buffer* buffer,
static void
replyerror(int r, struct worker* worker)
{
- LDNS_QR_SET(ldns_buffer_begin(worker->query_reply.c->buffer));
- LDNS_RCODE_SET(ldns_buffer_begin(worker->query_reply.c->buffer), r);
+ ldns_buffer* buf = worker->query_reply.c->buffer;
+ uint16_t flags;
+ verbose(VERB_DETAIL, "reply with error");
+
+ ldns_buffer_clear(buf);
+ ldns_buffer_write_u16(buf, worker->query_id);
+ flags = (uint16_t)(0x8000 | r); /* QR and retcode*/
+ flags |= (worker->query_flags & 0x0100); /* copy RD bit */
+ ldns_buffer_write_u16(buf, flags);
+ flags = 1;
+ ldns_buffer_write_u16(buf, flags);
+ flags = 0;
+ ldns_buffer_write(buf, &flags, sizeof(uint16_t));
+ ldns_buffer_write(buf, &flags, sizeof(uint16_t));
+ ldns_buffer_write(buf, &flags, sizeof(uint16_t));
+ ldns_buffer_write(buf, worker->qinfo.qname, worker->qinfo.qnamesize);
+ ldns_buffer_write_u16(buf, worker->qinfo.qtype);
+ ldns_buffer_write_u16(buf, worker->qinfo.qclass);
+ ldns_buffer_flip(buf);
comm_point_send_reply(&worker->query_reply);
if(worker->num_requests == 1) {
/* no longer at max, start accepting again. */
listen_resume(worker->front);
}
worker->num_requests --;
+ query_info_clear(&worker->qinfo);
}
/** process incoming replies from the network */
struct comm_reply* ATTR_UNUSED(reply_info))
{
struct worker* worker = (struct worker*)arg;
+ struct reply_info* rep;
+ struct msgreply_entry* e;
verbose(VERB_DETAIL, "reply to query with stored ID %d",
worker->query_id);
LDNS_ID_SET(ldns_buffer_begin(worker->query_reply.c->buffer),
return 0;
}
/* woohoo a reply! */
- ldns_buffer_clear(worker->query_reply.c->buffer);
- ldns_buffer_skip(worker->query_reply.c->buffer, ID_AND_FLAGS);
- ldns_buffer_write(worker->query_reply.c->buffer,
- ldns_buffer_at(c->buffer, ID_AND_FLAGS),
- ldns_buffer_limit(c->buffer) - ID_AND_FLAGS);
- LDNS_QR_SET(ldns_buffer_begin(worker->query_reply.c->buffer));
- ldns_buffer_flip(worker->query_reply.c->buffer);
+ rep = (struct reply_info*)malloc(sizeof(struct reply_info));
+ if(!rep) {
+ log_err("out of memory");
+ replyerror(LDNS_RCODE_SERVFAIL, worker);
+ return 0;
+ }
+ rep->replysize = ldns_buffer_limit(c->buffer) - 2; /* minus ID */
+ log_info("got reply of size %d", rep->replysize);
+ rep->reply = (uint8_t*)malloc(rep->replysize);
+ if(!rep->reply) {
+ free(rep);
+ log_err("out of memory");
+ replyerror(LDNS_RCODE_SERVFAIL, worker);
+ return 0;
+ }
+ memmove(rep->reply, ldns_buffer_at(c->buffer, 2), rep->replysize);
+ ldns_buffer_write_u16_at(worker->query_reply.c->buffer, 0,
+ worker->query_id);
+ reply_info_answer(rep, worker->query_flags, worker->query_reply.c->
+ buffer);
comm_point_send_reply(&worker->query_reply);
if(worker->num_requests == 1) {
/* no longer at max, start accepting again. */
listen_resume(worker->front);
}
worker->num_requests --;
+ /* store or update reply in the cache */
+ if(!(e = query_info_entrysetup(&worker->qinfo, rep,
+ worker->query_hash))) {
+ log_err("out of memory");
+ return 0;
+ }
+ lruhash_insert(worker->daemon->msg_cache, worker->query_hash,
+ &e->entry, rep);
return 0;
}
/* query the forwarding address */
worker->query_id = LDNS_ID_WIRE(ldns_buffer_begin(
worker->query_reply.c->buffer));
+ worker->query_flags = ldns_buffer_read_u16_at(worker->
+ query_reply.c->buffer, 2);
verbose(VERB_DETAIL, "process_query ID %d", worker->query_id);
pending_udp_query(worker->back, worker->query_reply.c->buffer,
&worker->fwd_addr, worker->fwd_addrlen, UDP_QUERY_TIMEOUT,
{
struct worker* worker = (struct worker*)arg;
int ret;
+ hashvalue_t h;
+ struct lruhash_entry* e;
verbose(VERB_DETAIL, "worker handle request");
if(error != NETEVENT_NOERROR) {
log_err("called with err=%d", error);
comm_point_drop_reply(repinfo);
return 0;
}
+ /* see if query is in the cache */
+ if(!query_info_parse(&worker->qinfo, c->buffer)) {
+ LDNS_QR_SET(ldns_buffer_begin(c->buffer));
+ LDNS_RCODE_SET(ldns_buffer_begin(c->buffer),
+ LDNS_RCODE_FORMERR);
+ return 1;
+ }
+ h = query_info_hash(&worker->qinfo);
+ if((e=lruhash_lookup(worker->daemon->msg_cache, h, &worker->qinfo,
+ 0))) {
+ /* answer from cache */
+ query_info_clear(&worker->qinfo);
+ /* id is still in the buffer, no need to touch it */
+ reply_info_answer((struct reply_info*)e->data,
+ ldns_buffer_read_u16_at(c->buffer, 2), c->buffer);
+ return 1;
+ }
+ ldns_buffer_rewind(c->buffer);
+
/* answer it */
+ worker->query_hash = h;
worker->num_requests ++;
if(worker->num_requests >= 1) {
/* the max request number has been reached, stop accepting */
#include "util/netevent.h"
#include "util/locks.h"
#include "util/alloc.h"
+#include "util/data/msgreply.h"
struct listen_dnsport;
struct outside_network;
struct config_file;
struct comm_reply query_reply;
/** id of query */
uint16_t query_id;
+ /** flags uint16 from query */
+ uint16_t query_flags;
+ /** the query_info structure from the query */
+ struct query_info qinfo;
+ /** hash value of the query qinfo */
+ hashvalue_t query_hash;
/** address to forward to */
struct sockaddr_storage fwd_addr;
in files it has not yet read in.
- threaded hash table test.
- unit test runs lock-verify afterwards and checks result.
+ - need writelock to update data on hash_insert.
+ - message cache code, msgreply code.
21 March 2007: Wouter
- unit test of hash table, fixup locking problem in table_grow().
o in production mode, do not free memory on exit. In debug mode, test leaks.
o profile memory allocation, and if performance issues, use special memory
allocator. For example, with caches per thread.
+o do not malloc query_info qname of query for answers from cache. Use ptr
+ to incoming commpt buffer.
+o sendmsg, writev.
if(fread(&t, sizeof(t), 1, in) != 1 ||
fread(&thrno, sizeof(thrno), 1, in) != 1 ||
fread(&p, sizeof(p), 1, in) != 1) {
- fatal_exit("fread: %s", strerror(errno));
+ fatal_exit("fread failed");
}
/* check these values are sorta OK */
if(!have_values) {
fread(&o->id.instance, sizeof(int), 1, in) != 1 ||
!readup_str(&o->create_file, in) ||
fread(&o->create_line, sizeof(int), 1, in) != 1)
- fatal_exit("fread: %s", strerror(errno));
+ fatal_exit("fread failed");
o->smaller = rbtree_create(order_lock_cmp);
o->node.key = &o->id;
if(!rbtree_insert(all, &o->node)) {
fread(&now_id.instance, sizeof(int), 1, in) != 1 ||
!readup_str(&ref->file, in) ||
fread(&ref->line, sizeof(int), 1, in) != 1)
- fatal_exit("fread: %s", strerror(errno));
+ fatal_exit("fread failed");
if(verb) printf("read lock %u %u %u %u %s %d\n",
(unsigned)prev_id.thr, (unsigned)prev_id.instance,
(unsigned)now_id.thr, (unsigned)now_id.instance,
#include "config.h"
#include "util/data/msgreply.h"
+#include "util/storage/lookup3.h"
#include "util/log.h"
/** determine length of a dname in buffer, no compression pointers allowed. */
log_assert(ldns_buffer_position(query) == 0);
m->has_cd = (int)LDNS_CD_WIRE(q);
ldns_buffer_skip(query, LDNS_HEADER_SIZE);
- m->qname = ldns_buffer_current(query);
- m->qnamesize = query_dname_len(query);
+ q = ldns_buffer_current(query);
+ if((m->qnamesize = query_dname_len(query)) == 0)
+ return 0; /* parse error */
+ if(!(m->qname = (uint8_t*)malloc(m->qnamesize))) {
+ log_err("query_info_parse: out of memory");
+ return 0; /* out of memory */
+ }
+ memmove(m->qname, q, m->qnamesize);
+
if(ldns_buffer_remaining(query) < 4)
return 0; /* need qtype, qclass */
m->qtype = ldns_buffer_read_u16(query);
void query_info_clear(struct query_info* m)
{
+ free(m->qname);
m->qname = NULL;
}
-void msgreply_clear(struct msgreply* m)
+void reply_info_clear(struct reply_info* m)
{
- if(m->qname_malloced)
- free(m->q.qname);
free(m->reply);
+ m->reply = NULL;
+}
+
+size_t msgreply_sizefunc(void* k, void* d)
+{
+ struct query_info* q = (struct query_info*)k;
+ struct reply_info* r = (struct reply_info*)d;
+ return sizeof(struct msgreply_entry) + sizeof(struct reply_info)
+ + r->replysize + q->qnamesize;
+}
+
+void query_info_delete(void *k, void* ATTR_UNUSED(arg))
+{
+ struct query_info* q = (struct query_info*)k;
+ query_info_clear(q);
+ free(q);
+}
+
+void reply_info_delete(void* d, void* ATTR_UNUSED(arg))
+{
+ struct reply_info* r = (struct reply_info*)d;
+ reply_info_clear(r);
+ free(r);
+}
+
+hashvalue_t query_info_hash(struct query_info *q)
+{
+ hashvalue_t h = 0xab;
+ h = hashlittle(&q->qtype, sizeof(q->qtype), h);
+ h = hashlittle(&q->qclass, sizeof(q->qclass), h);
+ h = hashlittle(&q->has_cd, sizeof(q->has_cd), h);
+ h = hashlittle(q->qname, q->qnamesize, h);
+ return h;
+}
+
+void reply_info_answer(struct reply_info* rep, uint16_t qflags,
+ ldns_buffer* buffer)
+{
+ uint16_t flags;
+ ldns_buffer_clear(buffer);
+ ldns_buffer_skip(buffer, 2); /* ID */
+ flags = ldns_read_uint16(rep->reply+2);
+ flags |= (qflags & 0x0100); /* copy RD bit */
+ log_info("flags %x", flags);
+ ldns_buffer_write_u16(buffer, flags);
+ ldns_buffer_write(buffer, rep->reply+2, rep->replysize-2);
+ ldns_buffer_flip(buffer);
+}
+
+struct msgreply_entry* query_info_entrysetup(struct query_info* q,
+ struct reply_info* r, hashvalue_t h)
+{
+ struct msgreply_entry* e = (struct msgreply_entry*)malloc(
+ sizeof(struct msgreply_entry));
+ if(!e) return NULL;
+ memcpy(&e->key, q, sizeof(*q));
+ e->entry.hash = h;
+ e->entry.key = e;
+ e->entry.data = r;
+ lock_rw_init(&e->entry.lock);
+ lock_protect(&e->entry.lock, e, sizeof(*e));
+ lock_protect(&e->entry.lock, e->key.qname, e->key.qnamesize);
+ q->qname = NULL;
+ return e;
}
#ifndef UTIL_DATA_MSGREPLY_H
#define UTIL_DATA_MSGREPLY_H
+#include "util/storage/lruhash.h"
/**
* Structure to store query information that makes answers to queries
* To use it, copy over the flags from reply and modify using flags from
* the query (RD,CD if not AA). prepend ID.
*/
-struct msgreply {
- /** id of this entry. */
- struct query_info q;
- /** if q.qname is allocated by malloc or not */
- int qname_malloced;
+struct reply_info {
/** the reply packet, skips ID, starts with flags/opcode/rcode word */
uint8_t* reply;
/** the reply size */
size_t replysize;
};
+/**
+ * Structure to keep hash table entry for message replies.
+ */
+struct msgreply_entry {
+ /** the hash table key */
+ struct query_info key;
+ /** the hash table entry, data is struct reply_info* */
+ struct lruhash_entry entry;
+};
+
/**
* Parse wire query into a queryinfo structure, return 0 on parse error.
* initialises the (prealloced) queryinfo structure as well. sets reply to 0.
* allowed, returns 0 on failure. */
size_t query_dname_len(ldns_buffer* query);
-/** clear out msgreply structure */
-void msgreply_clear(struct msgreply* m);
+/** clear out reply info structure */
+void reply_info_clear(struct reply_info* m);
+
+/** calculate size of struct query_info + reply_info */
+size_t msgreply_sizefunc(void* k, void* d);
+
+/** delete query_info key structure */
+void query_info_delete(void *q, void* arg);
+
+/** delete reply_info data structure */
+void reply_info_delete(void* d, void* arg);
+
+/** calculate hash value of query_info */
+hashvalue_t query_info_hash(struct query_info *q);
+
+/**
+ * Generate answer from reply_info.
+ * @param rep: reply to fill in.
+ * @param qflags: flags word from the query.
+ * @param buf: buffer to put reply into. Note that the query ID must
+ * be put in the buffer by caller.
+ * The buffer must be large enough.
+ */
+void reply_info_answer(struct reply_info* rep, uint16_t qflags,
+ ldns_buffer* buf);
+
+/**
+ * Setup query info entry
+ * @param q: query info to copy. Emptied as if clear is called.
+ * @param r: reply to init data.
+ * @param h: hash value.
+ * @return: newly allocated message reply cache item.
+ */
+struct msgreply_entry* query_info_entrysetup(struct query_info* q,
+ struct reply_info* r, hashvalue_t h);
#endif /* UTIL_DATA_MSGREPLY_H */
bin_delete(struct lruhash* table, struct lruhash_bin* bin)
{
struct lruhash_entry* p, *np;
+ void *d;
if(!bin)
return;
lock_quick_destroy(&bin->lock);
bin->overflow_list = NULL;
while(p) {
np = p->overflow_next;
+ d = p->data;
(*table->delkeyfunc)(p->key, table->cb_arg);
- (*table->deldatafunc)(p->data, table->cb_arg);
+ (*table->deldatafunc)(d, table->cb_arg);
p = np;
}
}
table->num++;
table->space_used += need_size;
} else {
- /* if so: update data */
+ /* if so: update data - needs a writelock */
table->space_used += need_size -
(*table->sizefunc)(found->key, found->data);
(*table->delkeyfunc)(entry->key, table->cb_arg);
+ lru_touch(table, found);
+ lock_rw_wrlock(&found->lock);
(*table->deldatafunc)(found->data, table->cb_arg);
found->data = data;
- lru_touch(table, found);
+ lock_rw_unlock(&found->lock);
}
lock_quick_unlock(&bin->lock);
if(table->space_used > table->space_max)