@@ -45,6 +45,41 @@ static const int default_tcp_io_cnt = 2; | |||
#define RESOLV_CONF "/etc/resolv.conf" | |||
struct dns_header { | |||
unsigned int qid :16; | |||
#if BYTE_ORDER == BIG_ENDIAN | |||
unsigned int qr:1; | |||
unsigned int opcode:4; | |||
unsigned int aa:1; | |||
unsigned int tc:1; | |||
unsigned int rd:1; | |||
unsigned int ra:1; | |||
unsigned int cd : 1; | |||
unsigned int ad : 1; | |||
unsigned int z : 1; | |||
unsigned int rcode:4; | |||
#else | |||
unsigned int rd :1; | |||
unsigned int tc :1; | |||
unsigned int aa :1; | |||
unsigned int opcode :4; | |||
unsigned int qr :1; | |||
unsigned int rcode :4; | |||
unsigned int z : 1; | |||
unsigned int ad : 1; | |||
unsigned int cd : 1; | |||
unsigned int ra :1; | |||
#endif | |||
unsigned int qdcount :16; | |||
unsigned int ancount :16; | |||
unsigned int nscount :16; | |||
unsigned int arcount :16; | |||
}; | |||
/** | |||
* Represents DNS server | |||
*/ | |||
@@ -110,6 +145,33 @@ enum rdns_io_channel_flags { | |||
#define IS_CHANNEL_CONNECTED(ioc) (((ioc)->flags & RDNS_CHANNEL_CONNECTED) != 0) | |||
#define IS_CHANNEL_ACTIVE(ioc) (((ioc)->flags & RDNS_CHANNEL_ACTIVE) != 0) | |||
#define IS_CHANNEL_TCP(ioc) (((ioc)->flags & RDNS_CHANNEL_TCP) != 0) | |||
/** | |||
* Used to chain output DNS requests for a TCP connection | |||
*/ | |||
struct rdns_tcp_output_chain { | |||
uint16_t next_write_size; | |||
struct rdns_request *req; | |||
uint16_t cur_write; | |||
struct rdns_tcp_output_chain *prev, *next; | |||
}; | |||
/** | |||
* Specific stuff for a TCP IO chain | |||
*/ | |||
struct rdns_tcp_channel { | |||
uint16_t next_read_size; | |||
unsigned char *cur_read_buf; | |||
uint16_t cur_read; | |||
/* Chained set of the planned writes */ | |||
struct rdns_tcp_output_chain *output_chain; | |||
unsigned cur_output_chains; | |||
void *async_read; /** async read event */ | |||
void *async_write; /** async write event */ | |||
}; | |||
KHASH_DECLARE(rdns_requests_hash, int, struct rdns_request *); | |||
/** | |||
@@ -124,6 +186,15 @@ struct rdns_io_channel { | |||
int flags; /**< see enum rdns_io_channel_flags */ | |||
void *async_io; /** async opaque ptr */ | |||
khash_t(rdns_requests_hash) *requests; | |||
/* | |||
* For DNS replies parsing we use per-channel structure | |||
* which is used for two purposes: | |||
* 1) We read the next DNS header | |||
* 2) We find the corresponding request (if any) | |||
* 3) We read the remaining packet (associated with a request or dangling) | |||
* This structure is filled on each read-readiness for an IO channel | |||
*/ | |||
struct rdns_tcp_channel *tcp; | |||
uint64_t uses; | |||
ref_entry_t ref; | |||
}; | |||
@@ -167,46 +238,10 @@ struct rdns_resolver { | |||
ref_entry_t ref; | |||
}; | |||
struct dns_header; | |||
struct dns_query; | |||
/* Internal DNS structs */ | |||
struct dns_header { | |||
unsigned int qid :16; | |||
#if BYTE_ORDER == BIG_ENDIAN | |||
unsigned int qr:1; | |||
unsigned int opcode:4; | |||
unsigned int aa:1; | |||
unsigned int tc:1; | |||
unsigned int rd:1; | |||
unsigned int ra:1; | |||
unsigned int cd : 1; | |||
unsigned int ad : 1; | |||
unsigned int z : 1; | |||
unsigned int rcode:4; | |||
#else | |||
unsigned int rd :1; | |||
unsigned int tc :1; | |||
unsigned int aa :1; | |||
unsigned int opcode :4; | |||
unsigned int qr :1; | |||
unsigned int rcode :4; | |||
unsigned int z : 1; | |||
unsigned int ad : 1; | |||
unsigned int cd : 1; | |||
unsigned int ra :1; | |||
#endif | |||
unsigned int qdcount :16; | |||
unsigned int ancount :16; | |||
unsigned int nscount :16; | |||
unsigned int arcount :16; | |||
}; | |||
enum dns_section { | |||
DNS_S_QD = 0x01, | |||
#define DNS_S_QUESTION DNS_S_QD |
@@ -286,10 +286,43 @@ rdns_parse_reply (uint8_t *in, int r, struct rdns_request *req, | |||
return true; | |||
} | |||
void | |||
rdns_process_read (int fd, void *arg) | |||
static void | |||
rdns_process_tcp_read (int fd, struct rdns_io_channel *ioc) | |||
{ | |||
} | |||
static void | |||
rdns_process_tcp_connect (int fd, struct rdns_io_channel *ioc) | |||
{ | |||
struct rdns_resolver *resolver = ioc->resolver; | |||
struct rdns_server *serv = ioc->srv; | |||
int r = connect (ioc->sock, ioc->saddr, ioc->slen); | |||
if (r == -1) { | |||
if (errno != EAGAIN && errno != EINTR && errno != EINPROGRESS) { | |||
rdns_err ("cannot connect a TCP socket: %s for server %s", | |||
strerror(errno), serv->name); | |||
resolver->async->del_write (resolver->async->data, ioc->async_io); | |||
} | |||
else { | |||
/* We need to wait again for write readiness here */ | |||
ioc->async_io = resolver->async->add_write (resolver->async->data, | |||
ioc->sock, ioc); | |||
} | |||
} | |||
else { | |||
/* Always be ready to read from a TCP socket */ | |||
resolver->async->del_write (resolver->async->data, ioc->async_io); | |||
ioc->flags |= RDNS_CHANNEL_CONNECTED|RDNS_CHANNEL_ACTIVE; | |||
ioc->tcp->async_read = resolver->async->add_read(resolver->async->data, | |||
ioc->sock, ioc); | |||
} | |||
} | |||
static void | |||
rdns_process_udp_read (int fd, struct rdns_io_channel *ioc) | |||
{ | |||
struct rdns_io_channel *ioc = arg; | |||
struct rdns_resolver *resolver; | |||
struct rdns_request *req = NULL; | |||
ssize_t r; | |||
@@ -310,7 +343,7 @@ rdns_process_read (int fd, void *arg) | |||
sizeof (in), resolver->curve_plugin->data, &req, | |||
ioc->saddr, ioc->slen); | |||
if (req == NULL && | |||
r > (int)(sizeof (struct dns_header) + sizeof (struct dns_query))) { | |||
r > (int)(sizeof (struct dns_header) + sizeof (struct dns_query))) { | |||
req = rdns_find_dns_request (in, ioc); | |||
} | |||
} | |||
@@ -336,6 +369,25 @@ rdns_process_read (int fd, void *arg) | |||
} | |||
} | |||
void | |||
rdns_process_read (int fd, void *arg) | |||
{ | |||
struct rdns_io_channel *ioc = (struct rdns_io_channel *)arg; | |||
if (IS_CHANNEL_TCP(ioc)) { | |||
if (IS_CHANNEL_CONNECTED(ioc)) { | |||
rdns_process_tcp_read (fd, ioc); | |||
} | |||
else { | |||
rdns_process_tcp_connect (fd, ioc); | |||
} | |||
} | |||
else { | |||
rdns_process_udp_read (fd, ioc); | |||
} | |||
} | |||
void | |||
rdns_process_timer (void *arg) | |||
{ |
@@ -515,7 +515,16 @@ rdns_ioc_new (struct rdns_server *serv, | |||
struct rdns_resolver *resolver, | |||
bool is_tcp) | |||
{ | |||
struct rdns_io_channel *nioc = calloc (1, sizeof (struct rdns_io_channel)); | |||
struct rdns_io_channel *nioc; | |||
if (is_tcp) { | |||
nioc = calloc (1, sizeof (struct rdns_io_channel) | |||
+ sizeof (struct rdns_tcp_channel)); | |||
} | |||
else { | |||
nioc = calloc (1, sizeof (struct rdns_io_channel)); | |||
} | |||
if (nioc == NULL) { | |||
rdns_err ("calloc fails to allocate rdns_io_channel"); | |||
return NULL; | |||
@@ -531,7 +540,8 @@ rdns_ioc_new (struct rdns_server *serv, | |||
} | |||
if (is_tcp) { | |||
/* We also need to connect a TCP channel */ | |||
/* We also need to connect a TCP channel and set a TCP buffer */ | |||
nioc->tcp = (struct rdns_tcp_channel *)(((unsigned char *)nioc) + sizeof(*nioc)); | |||
int r = connect (nioc->sock, nioc->saddr, nioc->slen); | |||
if (r == -1) { | |||
@@ -550,7 +560,10 @@ rdns_ioc_new (struct rdns_server *serv, | |||
} | |||
} | |||
else { | |||
/* Always be ready to read from a TCP socket */ | |||
nioc->flags |= RDNS_CHANNEL_CONNECTED|RDNS_CHANNEL_ACTIVE; | |||
nioc->tcp->async_read = resolver->async->add_read(resolver->async->data, | |||
nioc->sock, nioc); | |||
} | |||
nioc->flags |= RDNS_CHANNEL_TCP; | |||
@@ -560,7 +573,7 @@ rdns_ioc_new (struct rdns_server *serv, | |||
nioc->resolver = resolver; | |||
/* If it is not NULL then we are in a delayed connection state */ | |||
if (nioc->async_io == NULL) { | |||
if (!is_tcp) { | |||
nioc->flags |= RDNS_CHANNEL_ACTIVE; | |||
nioc->async_io = resolver->async->add_read(resolver->async->data, | |||
nioc->sock, nioc); |