[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r9068: start fleshing out some logic to build server support into e (in tor/trunk: . doc src/or)
- To: or-cvs@xxxxxxxxxxxxx
- Subject: [or-cvs] r9068: start fleshing out some logic to build server support into e (in tor/trunk: . doc src/or)
- From: nickm@xxxxxxxx
- Date: Mon, 11 Dec 2006 21:49:46 -0500 (EST)
- Delivered-to: archiver@seul.org
- Delivered-to: or-cvs-outgoing@seul.org
- Delivered-to: or-cvs@seul.org
- Delivery-date: Mon, 11 Dec 2006 21:49:55 -0500
- Reply-to: or-talk@xxxxxxxxxxxxx
- Sender: owner-or-cvs@xxxxxxxxxxxxx
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@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