[or-cvs] r9068: start fleshing out some logic to build server support into e (in tor/trunk: . doc src/or)

nickm at seul.org nickm at seul.org
Tue Dec 12 02:49:46 UTC 2006


Author: nickm
Date: 2006-12-11 21:49:45 -0500 (Mon, 11 Dec 2006)
New Revision: 9068

Modified:
   tor/trunk/
   tor/trunk/doc/TODO
   tor/trunk/src/or/eventdns.c
   tor/trunk/src/or/eventdns.h
Log:
 r9099 at Kushana:  nickm | 2006-10-05 17:42:55 -0400
 start fleshing out some logic to build server support into eventdns.  Still needs work and testing.



Property changes on: tor/trunk
___________________________________________________________________
 svk:merge ticket from /tor/branches/eventdns [r9099] on c95137ef-5f19-0410-b913-86e773d04f59

Modified: tor/trunk/doc/TODO
===================================================================
--- tor/trunk/doc/TODO	2006-12-11 21:23:55 UTC (rev 9067)
+++ tor/trunk/doc/TODO	2006-12-12 02:49:45 UTC (rev 9068)
@@ -92,9 +92,20 @@
     . Asynchronous DNS
       o Document and rename SearchDomains, ResolvConf options
       D Make API closer to getaddrinfo()
-      - Teach it to be able to listen for A and PTR requests to be processed.
-        Interface should be set_request_listener(sock, cb); [ cb(request) ]
-        send_reply(request, answer);
+      - Teach it 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 Send responses to client
+          o Queue responses when we see EAGAIN
+          - Retry responses after a while
+      - Add some kind of general question/response API so libevent can be
+        flexible here.
 d     - Add option to use /etc/hosts?
 d     - Special-case localhost?
       - Verify that it works on windows

Modified: tor/trunk/src/or/eventdns.c
===================================================================
--- tor/trunk/src/or/eventdns.c	2006-12-11 21:23:55 UTC (rev 9067)
+++ tor/trunk/src/or/eventdns.c	2006-12-12 02:49:45 UTC (rev 9068)
@@ -8,7 +8,8 @@
  *
  * TODO:
  *   - Have a way to query for AAAA and A records simultaneously.
- *   - Improve request API.
+ *   - Improve request API: At the very least, add the ability to construct
+ *     a more-or-less arbitrary request and get a response.
  *   - (Can we suppress cnames? Should we?)
  *   - Replace all externally visible magic numbers with #defined constants.
  *   - Write documentation for APIs of all external functions.
@@ -382,6 +383,44 @@
 static struct request *req_head = NULL, *req_waiting_head = NULL;
 static struct nameserver *server_head = NULL;
 
+struct server_port {
+	int socket;
+	int refcnt;
+	char choaked;
+	evdns_request_callback_type user_callback;
+	void *user_data;
+	struct server_request *pending_replies;
+};
+
+struct server_request_section {
+	int n_items;
+	int j;
+	char buf[512];
+};
+
+struct server_request {
+	struct server_request *next_pending;
+	struct server_request *prev_pending;
+
+    u16 trans_id;
+	struct 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;
+
+    struct evdns_request base;
+};
+#define OFFSET_OF(st, member) ((off_t) (((char*)&((st*)0)->member)-(char*)0))
+
+#define TO_SERVER_REQUEST(base_ptr)										\
+	((struct server_request*)											\
+	 (((char*)(base_ptr) - OFFSET_OF(struct server_request, base))))
+
+static void evdns_request_free(struct evdns_request *_req);
+
 // The number of good nameservers that we have
 static int global_good_nameservers = 0;
 
@@ -868,9 +907,10 @@
 	return 0;
 }
 
-// parses a raw packet from the wire
+// parses a raw request from the wire
 static int
-reply_parse(u8 *packet, int length) {
+reply_parse(u8 *packet, int length)
+{
 	int j = 0;	// index into packet
 	u16 _t;	 // used by the macros
 	u32 _t32;  // used by the macros
@@ -879,7 +919,7 @@
 	u16 trans_id, flags, questions, answers, authority, additional, datalength;
 	u32 ttl, ttl_r = 0xffffffff;
 	struct reply reply;
-	struct request *req;
+	struct request *req = NULL;
 	unsigned int i;
 
 	GET16(trans_id);
@@ -891,8 +931,16 @@
 	(void) authority;
 	(void) additional;
 
+	// This macro skips a name in the DNS reply.
+#define SKIP_NAME														\
+	do { tmp_name[0] = '\0';											\
+		if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) \
+			return -1;													\
+	} while(0);
+
 	req = request_find_from_trans_id(trans_id);
 	if (!req) return -1;
+
 	// XXXX should the other return points also call reply_handle? -NM
 	// log("reqparse: trans was %d\n", (int)trans_id);
 
@@ -906,23 +954,16 @@
 	}
 	// if (!answers) return;  // must have an answer of some form
 
-	// This macro skips a name in the DNS reply.
-#define SKIP_NAME														\
-	do { tmp_name[0] = '\0';											\
-		if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) \
-			return -1;													\
-	} while(0);
-
 	reply.type = req->request_type;
 
-	// skip over each question in the reply
+    // skip over each question in the reply
 	for (i = 0; i < questions; ++i) {
 		// the question looks like
 		//	 <label:name><u16:type><u16:class>
 		SKIP_NAME;
 		j += 4;
 		if (j >= length) return -1;
-	}
+    }
 
 	// now we have the answer section which looks like
 	// <label:name><u16:type><u16:class><u32:ttl><u16:len><data...>
@@ -940,7 +981,7 @@
 
 		// log("@%d, Name %s, type %d, class %d, j=%d", pre, tmp_name, (int)type, (int)class, j);
 
-		if (type == TYPE_A && class == CLASS_INET) {
+        if (type == TYPE_A && class == CLASS_INET) {
 			int addrcount, addrtocopy;
 			if (req->request_type != TYPE_A) {
 				j += datalength; continue;
@@ -981,12 +1022,89 @@
 
 	reply_handle(req, flags, ttl_r, &reply);
 	return 0;
+}
 #undef SKIP_NAME
 #undef GET32
 #undef GET16
 #undef GET8
+
+static int
+request_parse(u8 *packet, int length, struct server_port *port, struct sockaddr *addr, socklen_t addrlen)
+{
+	int j = 0;	// index into packet
+	u16 _t;	 // used by the macros
+	char tmp_name[256]; // used by the macros
+
+#define GET16(x) do { if (j + 2 > length) goto err; memcpy(&_t, packet + j, 2); j += 2; x = ntohs(_t); } while(0);
+#define GET8(x) do { if (j >= length) goto err; x = packet[j++]; } while(0);
+
+	int i;
+	u16 trans_id, flags, questions, answers, authority, additional;
+	struct server_request *server_req = NULL;
+
+	GET16(trans_id);
+	GET16(flags);
+	GET16(questions);
+	GET16(answers);
+	GET16(authority);
+	GET16(additional);
+
+	if (flags & 0x8000) return -1; // Must not be an answer.
+
+	server_req = malloc(sizeof(struct server_request));
+	if (server_req == NULL) return -1;
+	memset(server_req, 0, sizeof(struct server_request));
+
+	server_req->trans_id = trans_id;
+	memcpy(&server_req->addr, addr, addrlen);
+	server_req->addrlen = addrlen;
+
+	server_req->base.flags = flags;
+	server_req->base.nquestions = 0;
+	server_req->base.questions = malloc(sizeof(struct evdns_question *) * questions);
+	if (server_req->base.questions == NULL)
+		goto err;
+
+	for (i = 0; i < questions; ++i) {
+		u16 type, class;
+		struct evdns_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);
+		if (!q)
+			goto err;
+		q->type = type;
+		q->class = class;
+		memcpy(q->name, tmp_name, namelen+1);
+		server_req->base.questions[server_req->base.nquestions++] = q;
+	}
+
+	// Do nothing with rest of packet -- safe?
+
+	server_req->port = port;
+	port->refcnt++;
+	port->user_callback(&(server_req->base), port->user_data);
+
+	return 0;
+err:
+	if (server_req) {
+		if (server_req->base.questions) {
+			for (i = 0; i < server_req->base.nquestions; ++i)
+				free(server_req->base.questions[i]);
+			free(server_req->base.questions);
+		}
+		free(server_req);
+	}
+	return -1;
 }
+#undef GET16
+#undef GET8
 
+
 // Try to choose a strong transaction id which isn't already in flight
 static u16
 transaction_id_pick(void) {
@@ -1086,6 +1204,27 @@
 	}
 }
 
+static void
+server_port_read(struct server_port *s) {
+	u8 packet[1500];
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+	int r;
+
+	for (;;) {
+		addrlen = sizeof(struct sockaddr_storage);
+		r = recvfrom(s->socket, packet, sizeof(packet), 0,
+					 (struct sockaddr*) &addr, &addrlen);
+		if (r < 0) {
+			int err = last_error(s->socket);
+			if (error_is_eagain(err)) return;
+			// XXXX log error; not much else to do. -NM
+			return;
+		}
+		request_parse(packet, r, s, (struct sockaddr*) &addr, addrlen);
+	}
+}
+
 // 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.
@@ -1200,7 +1339,15 @@
         memcpy(buf + j, &_t, 2);                   \
         j += 2;                                    \
     } while (0)
+#define APPEND32(x) do {                           \
+        if (j + 4 > buf_len)                       \
+            return (-1);                           \
+        _t32 = htonl(x);                           \
+        memcpy(buf + j, &_t32, 4);                 \
+        j += 4;                                    \
+    } while (0)
 
+
 	APPEND16(trans_id);
 	APPEND16(0x0100);  // standard query, recusion needed
 	APPEND16(1);  // one question
@@ -1226,11 +1373,164 @@
 
 	APPEND16(type);
 	APPEND16(class);
-#undef APPEND16
 
 	return (int)j;
 }
 
+// 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)
+{
+	struct server_request *req = TO_SERVER_REQUEST(_req);
+	struct server_request_section **secp;
+	int j;
+	char *buf;
+	int buf_len;
+	u16 _t;	 // used by the macros
+	u32 _t32; // used by the macros
+	u8 *labels;
+	int labels_len, name_len;
+
+	switch (section) {
+	case EVDNS_ANSWER_SECTION:
+		secp = &req->answer;
+		break;
+	case EVDNS_AUTHORITY_SECTION:
+		secp = &req->authority;
+		break;
+	case EVDNS_ADDITIONAL_SECTION:
+		secp = &req->additional;
+		break;
+	default:
+		return -1;
+	}
+	if (!*secp) {
+		if (!(*secp = malloc(sizeof(struct server_request_section))))
+			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...>
+	name_len = strlen(name);
+
+	labels = (u8 *) malloc(name_len + 2);
+	if (labels == NULL)
+		return -1;
+	labels_len = dnsname_to_labels(labels, name, name_len);
+	if (labels_len < 0) {
+		free(labels);
+		return labels_len;
+	}
+	if ((size_t)(j + labels_len) > buf_len) {
+		free(labels);
+		return (-1);
+	}
+	memcpy(buf + j, labels, labels_len);
+	j += labels_len;
+	free(labels);
+
+	APPEND16(type);
+	APPEND16(class);
+	APPEND32(ttl);
+	APPEND16(datalen);
+	if ((size_t)(j + datalen) > buf_len)
+		return -1;
+	memcpy(buf + j, data, datalen);
+	j += datalen;
+
+	(*secp)->j = j;
+	(*secp)->n_items++;
+	return 0;
+}
+
+// exported function
+int
+evdns_request_add_a_reply(struct evdns_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, addrs);
+}
+
+// exported function
+int
+eventdns_request_respond(struct evdns_request *_req, int err, 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.
+
+	r = sendto(req->port->socket, response, responselen, 0,
+			   (struct sockaddr*) &req->addr, req->addrlen);
+	if (r<0) {
+		int err = last_error(req->port->socket);
+		if (! error_is_eagain(err))
+			return -1;
+
+		if (req->port->pending_replies) {
+			req->prev_pending = req->port->pending_replies->prev_pending;
+			req->next_pending = req->port->pending_replies;
+			req->prev_pending->next_pending =
+				req->next_pending->prev_pending = req;
+		} else {
+			req->prev_pending = req->next_pending = req;
+			req->port->pending_replies = req;
+			req->port->choaked = 1;
+		}
+		return 0;
+	}
+	// XXXX process pending replies.
+
+	evdns_request_free(_req);
+	return 0;
+}
+
+static void
+evdns_request_free(struct evdns_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)
+				req->port->pending_replies = req->next_pending;
+			else
+				req->port->pending_replies = NULL;
+		}
+		--req->port->refcnt; /* release? XXXX NM*/
+	}
+
+	if (req->next_pending && req->next_pending != req) {
+		req->next_pending->prev_pending = req->prev_pending;
+		req->prev_pending->next_pending = req->next_pending;
+	}
+
+	free(req);
+}
+
+#undef APPEND16
+#undef APPEND32
+
+
 // this is a libevent callback function which is called when a request
 // has timed out.
 static void
@@ -2243,72 +2543,7 @@
 }
 #endif
 
-#ifdef EVDNS_MAIN
-void
-main_callback(int result, char type, int count, int ttl,
-			  void *addrs, void *orig) {
-	char *n = (char*)orig;
-	int i;
-	for (i = 0; i < count; ++i) {
-		if (type == DNS_IPv4_A) {
-			printf("%s: %s\n", n, debug_ntoa(((u32*)addrs)[i]));
-		} else if (type == DNS_PTR) {
-			printf("%s: %s\n", n, ((char**)addrs)[i]);
-		}
-	}
-	if (!count) {
-		printf("%s: No answer (%d)\n", n, result);
-	}
-	fflush(stdout);
-}
-
-void
-logfn(const char *msg) {
-  fprintf(stderr, "%s\n", msg);
-}
 int
-main(int c, char **v) {
-	int idx;
-	int reverse = 0, verbose = 1;
-	if (c<2) {
-		fprintf(stderr, "syntax: %s [-x] [-v] hostname\n", v[0]);
-		return 1;
-	}
-	idx = 1;
-	while (idx < c && v[idx][0] == '-') {
-		if (!strcmp(v[idx], "-x"))
-			reverse = 1;
-		else if (!strcmp(v[idx], "-v"))
-			verbose = 1;
-		else
-			fprintf(stderr, "Unknown option %s\n", v[idx]);
-		++idx;
-	}
-	event_init();
-	if (verbose)
-		evdns_set_log_fn(logfn);
-	evdns_resolv_conf_parse(DNS_OPTION_NAMESERVERS, "/etc/resolv.conf");
-	for (; idx < c; ++idx) {
-		if (reverse) {
-			struct in_addr addr;
-			if (!inet_aton(v[idx], &addr)) {
-				fprintf(stderr, "Skipping non-IP %s\n", v[idx]);
-				continue;
-			}
-			fprintf(stderr, "resolving %s...\n",v[idx]);
-			evdns_resolve_reverse(&addr, 0, main_callback, v[idx]);
-		} else {
-			fprintf(stderr, "resolving (fwd) %s...\n",v[idx]);
-			evdns_resolve_ipv4(v[idx], 0, main_callback, v[idx]);
-		}
-	}
-	fflush(stdout);
-	event_dispatch();
-	return 0;
-}
-#endif
-
-int
 evdns_init(void)
 {
         int res = 0;
@@ -2381,6 +2616,71 @@
 	evdns_log_fn = NULL;
 }
 
+#ifdef EVDNS_MAIN
+void
+main_callback(int result, char type, int count, int ttl,
+			  void *addrs, void *orig) {
+	char *n = (char*)orig;
+	int i;
+	for (i = 0; i < count; ++i) {
+		if (type == DNS_IPv4_A) {
+			printf("%s: %s\n", n, debug_ntoa(((u32*)addrs)[i]));
+		} else if (type == DNS_PTR) {
+			printf("%s: %s\n", n, ((char**)addrs)[i]);
+		}
+	}
+	if (!count) {
+		printf("%s: No answer (%d)\n", n, result);
+	}
+	fflush(stdout);
+}
+
+void
+logfn(const char *msg) {
+  fprintf(stderr, "%s\n", msg);
+}
+int
+main(int c, char **v) {
+	int idx;
+	int reverse = 0, verbose = 1;
+	if (c<2) {
+		fprintf(stderr, "syntax: %s [-x] [-v] hostname\n", v[0]);
+		return 1;
+	}
+	idx = 1;
+	while (idx < c && v[idx][0] == '-') {
+		if (!strcmp(v[idx], "-x"))
+			reverse = 1;
+		else if (!strcmp(v[idx], "-v"))
+			verbose = 1;
+		else
+			fprintf(stderr, "Unknown option %s\n", v[idx]);
+		++idx;
+	}
+	event_init();
+	if (verbose)
+		evdns_set_log_fn(logfn);
+	evdns_resolv_conf_parse(DNS_OPTION_NAMESERVERS, "/etc/resolv.conf");
+	for (; idx < c; ++idx) {
+		if (reverse) {
+			struct in_addr addr;
+			if (!inet_aton(v[idx], &addr)) {
+				fprintf(stderr, "Skipping non-IP %s\n", v[idx]);
+				continue;
+			}
+			fprintf(stderr, "resolving %s...\n",v[idx]);
+			evdns_resolve_reverse(&addr, 0, main_callback, v[idx]);
+		} else {
+			fprintf(stderr, "resolving (fwd) %s...\n",v[idx]);
+			evdns_resolve_ipv4(v[idx], 0, main_callback, v[idx]);
+		}
+	}
+	fflush(stdout);
+	event_dispatch();
+	return 0;
+}
+#endif
+
 // Local Variables:
 // tab-width: 4
 // c-basic-offset: 4

Modified: tor/trunk/src/or/eventdns.h
===================================================================
--- tor/trunk/src/or/eventdns.h	2006-12-11 21:23:55 UTC (rev 9067)
+++ tor/trunk/src/or/eventdns.h	2006-12-12 02:49:45 UTC (rev 9068)
@@ -75,4 +75,25 @@
 
 #define DNS_NO_SEARCH 1
 
+struct evdns_request {
+	int flags;
+	int nquestions;
+	struct evdns_question **questions;
+};
+struct evdns_question {
+	int type;
+	int class;
+	char name[1];
+};
+typedef void (*evdns_request_callback_type)(struct evdns_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);
+
 #endif  // !EVENTDNS_H



More information about the tor-commits mailing list