mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2025-02-23 22:47:12 +01:00
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
This commit is contained in:
parent
991308fac9
commit
c58d9494df
3 changed files with 293 additions and 88 deletions
11
doc/TODO
11
doc/TODO
|
@ -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?
|
||||
|
|
|
@ -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 evdns_request base;
|
||||
struct server_request_item *answer;
|
||||
struct server_request_item *authority;
|
||||
struct server_request_item *additional;
|
||||
|
||||
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))))
|
||||
return -1;
|
||||
memset(*secp, 0, sizeof(struct server_request_section));
|
||||
while (*itemp) {
|
||||
itemp = &((*itemp)->next);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
APPEND16(type);
|
||||
APPEND16(class);
|
||||
APPEND32(ttl);
|
||||
APPEND16(datalen);
|
||||
if ((size_t)(j + datalen) > buf_len)
|
||||
item = malloc(sizeof(struct server_request_item));
|
||||
if (!item)
|
||||
return -1;
|
||||
memcpy(buf + j, data, datalen);
|
||||
j += datalen;
|
||||
|
||||
(*secp)->j = j;
|
||||
(*secp)->n_items++;
|
||||
item->next = NULL;
|
||||
if (!(item->name = strdup(name))) {
|
||||
free(item);
|
||||
return -1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
item->datalen = -1;
|
||||
} else {
|
||||
if (!(item->data = malloc(datalen))) {
|
||||
free(item->name);
|
||||
free(item);
|
||||
return -1;
|
||||
}
|
||||
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, addrs);
|
||||
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, -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;
|
||||
if (!req->response) {
|
||||
if ((r = evdns_request_response_format(req, flags))<0)
|
||||
return r;
|
||||
}
|
||||
|
||||
// XXXX make a response and store it somewhere. Where? Oops; structs
|
||||
// may be wrong.
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue