Commit c58d9494 authored by Nick Mathewson's avatar Nick Mathewson 🦀
Browse files

r11491@Kushana: nickm | 2006-12-11 12:12:57 -0500

 More DNS server hacking: everything except testing, and retries, and documentation, and Tor integration.


svn:r9080
parent 991308fa
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -92,20 +92,21 @@ N - DNS improvements
    . Asynchronous DNS
      o Document and rename SearchDomains, ResolvConf options
      D Make API closer to getaddrinfo()
      - Teach it to be able to listen for requests to be processed.
      - Teach evdns to be able to listen for requests to be processed.
        . Design interface.
          - Rename stuff; current names suck.
        . Design backend.
        - Implement
          . Listen for questions
          o Parse questions, tell user code
          . Let user code tell us the answer
          - Generate responses
          o Let user code tell us the answer
          o Generate responses
          o Send responses to client
          o Queue responses when we see EAGAIN
          - Retry responses after a while
          - Be efficient about labels.
          - Be more memory-efficient
          o Be efficient about labels.
          - Test
d         - Be more memory-efficient
      - Add some kind of general question/response API so libevent can be
        flexible here.
d     - Add option to use /etc/hosts?
+272 −73
Original line number Diff line number Diff line
@@ -383,19 +383,25 @@ struct nameserver {
static struct request *req_head = NULL, *req_waiting_head = NULL;
static struct nameserver *server_head = NULL;

struct server_port {
struct evdns_server_port {
	int socket;
	int refcnt;
	char choaked;
	evdns_request_callback_type user_callback;
	evdns_request_callback_fn_type user_callback;
	void *user_data;
	struct event event;
	struct server_request *pending_replies;
};

struct server_request_section {
	int n_items;
	int j;
	u8 buf[512];
struct server_request_item {
	struct server_request_item *next;
	char *name;
	unsigned int type : 16;
	unsigned int class : 16;
	int ttl;
	unsigned is_name : 1;
	int datalen : 31;
	void *data;
};

struct server_request {
@@ -403,15 +409,22 @@ struct server_request {
	struct server_request *prev_pending;

    u16 trans_id;
	struct server_port *port;
	struct evdns_server_port *port;
	struct sockaddr_storage addr;
	socklen_t addrlen;

	struct server_request_section *answer;
	struct server_request_section *authority;
	struct server_request_section *additional;
	int n_answer;
	int n_authority;
	int n_additional;

	struct server_request_item *answer;
	struct server_request_item *authority;
	struct server_request_item *additional;

    struct evdns_request base;
	char *response;
	size_t response_len;

    struct evdns_server_request base;
};
#define OFFSET_OF(st, member) ((off_t) (((char*)&((st*)0)->member)-(char*)0))

@@ -419,7 +432,8 @@ struct server_request {
	((struct server_request*)											\
	 (((char*)(base_ptr) - OFFSET_OF(struct server_request, base))))

static void evdns_request_free(struct evdns_request *_req);
static void evdns_server_request_free(struct server_request *req);
static void evdns_server_request_free_answers(struct server_request *req);

// The number of good nameservers that we have
static int global_good_nameservers = 0;
@@ -449,6 +463,7 @@ const char *const evdns_error_strings[] = {"no error", "The name server was unab
static struct nameserver *nameserver_pick(void);
static void evdns_request_insert(struct request *req, struct request **head);
static void nameserver_ready_callback(int fd, short events, void *arg);
static void server_port_ready_callback(int fd, short events, void *arg);
static int evdns_transmit(void);
static int evdns_request_transmit(struct request *req);
static void nameserver_send_probe(struct nameserver *const ns);
@@ -907,7 +922,7 @@ name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) {
	return 0;
}

// parses a raw request from the wire
// parses a raw request from a nameserver.
static int
reply_parse(u8 *packet, int length)
{
@@ -1029,7 +1044,7 @@ reply_parse(u8 *packet, int length)
#undef GET8

static int
request_parse(u8 *packet, int length, struct server_port *port, struct sockaddr *addr, socklen_t addrlen)
request_parse(u8 *packet, int length, struct evdns_server_port *port, struct sockaddr *addr, socklen_t addrlen)
{
	int j = 0;	// index into packet
	u16 _t;	 // used by the macros
@@ -1061,20 +1076,20 @@ request_parse(u8 *packet, int length, struct server_port *port, struct sockaddr

	server_req->base.flags = flags;
	server_req->base.nquestions = 0;
	server_req->base.questions = malloc(sizeof(struct evdns_question *) * questions);
	server_req->base.questions = malloc(sizeof(struct evdns_server_question *) * questions);
	if (server_req->base.questions == NULL)
		goto err;

	for (i = 0; i < questions; ++i) {
		u16 type, class;
		struct evdns_question *q;
		struct evdns_server_question *q;
		int namelen;
		if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0)
			goto err;
		GET16(type);
		GET16(class);
		namelen = strlen(tmp_name);
		q = malloc(sizeof(struct evdns_question) + namelen);
		q = malloc(sizeof(struct evdns_server_question) + namelen);
		if (!q)
			goto err;
		q->type = type;
@@ -1205,7 +1220,7 @@ nameserver_read(struct nameserver *ns) {
}

static void
server_port_read(struct server_port *s) {
server_port_read(struct evdns_server_port *s) {
	u8 packet[1500];
	struct sockaddr_storage addr;
	socklen_t addrlen;
@@ -1225,6 +1240,13 @@ server_port_read(struct server_port *s) {
	}
}

static void
server_port_flush(struct evdns_server_port *port)
{
	// XXXX Writeme.
	(void)port;
}

// set if we are waiting for the ability to write to this server.
// if waiting is true then we ask libevent for EV_WRITE events, otherwise
// we stop these events.
@@ -1261,6 +1283,22 @@ nameserver_ready_callback(int fd, short events, void *arg) {
	}
}

// a callback function. Called by libevent when the kernel says that
// a server socket is ready for writing or reading.
static void
server_port_ready_callback(int fd, short events, void *arg) {
	struct evdns_server_port *port = (struct evdns_server_port *) arg;
	(void) fd;

	if (events & EV_WRITE) {
		port->choaked = 0;
		server_port_flush(port);
	}
	if (events & EV_READ) {
		server_port_read(port);
	}
}

/* This is an inefficient representation; only use it via the dnslabel_table_*
 * functions. */
#define MAX_LABELS 128
@@ -1431,84 +1469,215 @@ evdns_request_data_build(const char *const name, const int name_len, const u16 t
	return (int)j;
}

// exported function
struct evdns_server_port *
evdns_add_server_port(int socket, int is_tcp, evdns_request_callback_fn_type cb, void *user_data)
{
	struct evdns_server_port *port;
	if (!(port = malloc(sizeof(struct evdns_server_port))))
		return NULL;

	assert(!is_tcp); // TCP sockets not yet implemented
	port->socket = socket;
	port->refcnt = 1;
	port->choaked = 0;
	port->user_callback = cb;
	port->user_data = user_data;
	port->pending_replies = NULL;
	event_set(&port->event, port->socket, EV_READ | EV_PERSIST,
			  server_port_ready_callback, port);
	event_add(&port->event, NULL); // check return.
	return port;
}

// exported function
int
evdns_request_add_reply(struct evdns_request *_req, int section, const char *name, int type, int class, int ttl, int datalen, const char *data)
evdns_request_add_reply(struct evdns_server_request *_req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data)
{
	struct server_request *req = TO_SERVER_REQUEST(_req);
	struct server_request_section **secp;
	int j;
	u8 *buf;
	int buf_len;
	u16 _t;	 // used by the macros
	u32 _t32; // used by the macros
	struct server_request_item **itemp, *item;
	int *countp;

	if (req->response) /* have we already answered? */
		return -1;

	switch (section) {
	case EVDNS_ANSWER_SECTION:
		secp = &req->answer;
		itemp = &req->answer;
		countp = &req->n_answer;
		break;
	case EVDNS_AUTHORITY_SECTION:
		secp = &req->authority;
		itemp = &req->authority;
		countp = &req->n_authority;
		break;
	case EVDNS_ADDITIONAL_SECTION:
		secp = &req->additional;
		itemp = &req->additional;
		countp = &req->n_additional;
		break;
	default:
		return -1;
	}
	if (!*secp) {
		if (!(*secp = malloc(sizeof(struct server_request_section))))
	while (*itemp) {
		itemp = &((*itemp)->next);
	}
	item = malloc(sizeof(struct server_request_item));
	if (!item)
		return -1;
	item->next = NULL;
	if (!(item->name = strdup(name))) {
		free(item);
		return -1;
		memset(*secp, 0, sizeof(struct server_request_section));
	}
	buf = (*secp)->buf;
	buf_len = sizeof((*secp)->buf);
	j = (*secp)->j;

	// format is <label:name><u16:type><u16:class><u32:ttl><u16:len><data...>

	j = dnsname_to_labels(buf, buf_len, j, name, strlen(name), NULL);
	if (j < 0) {
		return j;
	item->type = type;
	item->class = class;
	item->ttl = ttl;
	item->is_name = is_name != 0;
	item->datalen = 0;
	item->data = NULL;
	if (data) {
		if (item->is_name) {
			if (!(item->data = strdup(data))) {
				free(item->name);
				free(item);
				return -1;
			}

	APPEND16(type);
	APPEND16(class);
	APPEND32(ttl);
	APPEND16(datalen);
	if ((size_t)(j + datalen) > buf_len)
			item->datalen = -1;
		} else {
			if (!(item->data = malloc(datalen))) {
				free(item->name);
				free(item);
				return -1;
	memcpy(buf + j, data, datalen);
	j += datalen;

	(*secp)->j = j;
	(*secp)->n_items++;
			}
			item->datalen = datalen;
			memcpy(item->data, data, datalen);
		}
	}

	*itemp = item;
	++(*countp);
	return 0;
}

// exported function
int
evdns_request_add_a_reply(struct evdns_request *req, const char *name, int n, void *addrs, int ttl)
evdns_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl)
{
	return evdns_request_add_reply(
		  req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET,
		  ttl, n*4, 0, addrs);
}

int
evdns_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl)
{
	return evdns_request_add_reply(
		  req, EVDNS_ANSWER_SECTION, name, TYPE_AAAA, CLASS_INET,
		  ttl, n*16, 0, addrs);
}

int
evdns_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *name, int ttl)
{
	u32 a;
	char buf[32];
	assert(in);
	a = ntohl(in->s_addr);
	sprintf(buf, "%d.%d.%d.%d.in-addr.arpa",
			(int)(u8)((a	)&0xff),
			(int)(u8)((a>>8 )&0xff),
			(int)(u8)((a>>16)&0xff),
			(int)(u8)((a>>24)&0xff));
	return evdns_request_add_reply(
		  req, EVDNS_ANSWER_SECTION, buf, TYPE_PTR, CLASS_INET,
		  ttl, -1, 1, name);
}

int
evdns_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl)
{
	return evdns_request_add_reply(
		  req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET,
		  ttl, n*4, addrs);
		  ttl, -1, 1, cname);
}

static int
evdns_request_response_format(struct server_request *req, int flags)
{
	unsigned char buf[1500];
	size_t buf_len = sizeof(buf);
	off_t j = 0;
	u16 _t;
	u32 _t32;
	int i;
	struct dnslabel_table table;

	dnslabel_table_init(&table); // XXXX need to call dnslable_table_clear.
	APPEND16(req->trans_id);
	APPEND16(flags);
	APPEND16(0); /* questions */
	APPEND16(req->n_answer);
	APPEND16(req->n_authority);
	APPEND16(req->n_additional);

	/* Add questions : none. */
	for (i=0; i<3; ++i) {
		struct server_request_item *item;
		if (i==0)
			item = req->answer;
		else if (i==1)
			item = req->authority;
		else
			item = req->additional;
		while (item) {
			j = dnsname_to_labels(buf, buf_len, j, item->name, strlen(item->name), &table);
			if (j < 0)
				return (int) j;

			APPEND16(item->type);
			APPEND16(item->class);
			APPEND32(item->ttl);
			if (item->is_name) {
				off_t len_idx = j, name_start;
				j += 2;
				name_start = j;
				j = dnsname_to_labels(buf, buf_len, j, item->data, strlen(item->data), &table);
				if (j < 0)
					return (int) j;
				_t = htons( (j-name_start) );
				memcpy(buf+len_idx, &_t, 2);
			} else {
				APPEND16(item->datalen);
				if (j+item->datalen > (off_t)buf_len)
					return -1;
				memcpy(buf+j, item->data, item->datalen);
				j += item->datalen;
			}
			item = item->next;
		}
	}

	req->response_len = j;
	if (!(req->response = malloc(req->response_len)))
		return -1;
	memcpy(req->response, buf, req->response_len);

	evdns_server_request_free_answers(req);
	dnslabel_clear(&table);
	return 0;
}

// exported function
int
eventdns_request_respond(struct evdns_request *_req, int err, int flags)
evdns_request_respond(struct evdns_server_request *_req, int flags)
{
	struct server_request *req = TO_SERVER_REQUEST(_req);
	int r;
	char response[1500];
	size_t responselen = 10;

	// XXXX make a response and store it somewhere.  Where? Oops; structs
	//      may be wrong.
	if (!req->response) {
		if ((r = evdns_request_response_format(req, flags))<0)
			return r;
	}

	r = sendto(req->port->socket, response, responselen, 0,
	r = sendto(req->port->socket, req->response, req->response_len, 0,
			   (struct sockaddr*) &req->addr, req->addrlen);
	if (r<0) {
		int err = last_error(req->port->socket);
@@ -1529,27 +1698,44 @@ eventdns_request_respond(struct evdns_request *_req, int err, int flags)
	}
	// XXXX process pending replies.

	evdns_request_free(_req);
	evdns_server_request_free(req);
	return 0;
}

static void
evdns_request_free(struct evdns_request *_req)
evdns_server_request_free_answers(struct server_request *req)
{
	struct server_request_item *victim, *next, **list;
	int i;
	for (i = 0; i < 3; ++i) {
		if (i==0)
			list = &req->answer;
		else if (i==1)
			list = &req->authority;
		else
			list = &req->additional;

		victim = *list;
		while (victim) {
			next = victim->next;
			free(victim->name);
			if (victim->data)
				free(victim->data);
			victim = next;
		}
		*list = NULL;
	}
}

static void
evdns_server_request_free(struct server_request *req)
{
	struct server_request *req = TO_SERVER_REQUEST(_req);
	int i;
	if (req->base.questions) {
		for (i = 0; i < req->base.nquestions; ++i)
			free(req->base.questions[i]);
	}

	if (req->answer)
		free(req->answer);
	if (req->answer)
		free(req->authority);
	if (req->answer)
		free(req->additional);

	if (req->port) {
		if (req->port->pending_replies == req) {
			if (req->next_pending)
@@ -1560,6 +1746,11 @@ evdns_request_free(struct evdns_request *_req)
		--req->port->refcnt; /* release? XXXX NM*/
	}

	if (req->response)
		free(req->response);

	evdns_server_request_free_answers(req);

	if (req->next_pending && req->next_pending != req) {
		req->next_pending->prev_pending = req->prev_pending;
		req->prev_pending->next_pending = req->next_pending;
@@ -1568,10 +1759,18 @@ evdns_request_free(struct evdns_request *_req)
	free(req);
}

// exported function
int
evdns_request_drop(struct evdns_server_request *_req)
{
	struct server_request *req = TO_SERVER_REQUEST(_req);
	evdns_server_request_free(req);
	return 0;
}

#undef APPEND16
#undef APPEND32


// this is a libevent callback function which is called when a request
// has timed out.
static void
+15 −10
Original line number Diff line number Diff line
@@ -75,25 +75,30 @@ void evdns_set_log_fn(evdns_debug_log_fn_type fn);

#define DNS_NO_SEARCH 1

struct evdns_request {
struct evdns_server_request {
	int flags;
	int nquestions;
	struct evdns_question **questions;
	struct evdns_server_question **questions;
};
struct evdns_question {
struct evdns_server_question {
	int type;
	int class;
	char name[1];
};
typedef void (*evdns_request_callback_type)(struct evdns_request *, void *);
typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, void *);
#define EVDNS_ANSWER_SECTION 0
#define EVDNS_AUTHORITY_SECTION 1
#define EVDNS_ADDITIONAL_SECTION 2
int evdns_request_add_reply(struct evdns_request *req, int section, const char *name, int type, int class, int ttl, int datalen, const char *data);
int evdns_request_add_a_reply(struct evdns_request *req, const char *name, int n, void *addrs, int ttl);
int evdns_request_add_ptr_reply(struct evdns_request *req, struct in_addr *in, const char *name, int ttl);
int evdns_request_add_cname_reply(struct evdns_request *req, const char *name, const char *cname, int ttl);
int evdns_request_respond(struct evdns_request *req, int err, int flags);
int evdns_request_drop(struct evdns_request *req);
struct evdns_server_port *evdns_add_server_port(int socket, int is_tcp, evdns_request_callback_fn_type callback, void *user_data);
void evdns_close_server_port(struct evdns_server_port *port);

int evdns_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data);
int evdns_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl);
int evdns_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl);
int evdns_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *name, int ttl);
int evdns_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl);

int evdns_request_respond(struct evdns_server_request *req, int flags);
int evdns_request_drop(struct evdns_server_request *req);

#endif  // !EVENTDNS_H