#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
*/
#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 *);
/**
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;
};
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
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;
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);
}
}
}
}
+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)
{
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;
}
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) {
}
}
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;
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);