commit 02e027d: [Project] Further DNS over TCP architecturing
Vsevolod Stakhov
vsevolod at highsecure.ru
Wed Jan 5 11:28:07 UTC 2022
Author: Vsevolod Stakhov
Date: 2022-01-01 22:49:48 +0000
URL: https://github.com/rspamd/rspamd/commit/02e027d0c0a5ffed16e3d387c3893ce9ff5a2521
[Project] Further DNS over TCP architecturing
---
contrib/librdns/dns_private.h | 107 ++++++++++++++++++++++++++++--------------
contrib/librdns/resolver.c | 60 +++++++++++++++++++++--
contrib/librdns/util.c | 19 ++++++--
3 files changed, 143 insertions(+), 43 deletions(-)
diff --git a/contrib/librdns/dns_private.h b/contrib/librdns/dns_private.h
index 4429552bc..75fb5a3fa 100644
--- a/contrib/librdns/dns_private.h
+++ b/contrib/librdns/dns_private.h
@@ -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
diff --git a/contrib/librdns/resolver.c b/contrib/librdns/resolver.c
index d7d41915f..6ef6d4558 100644
--- a/contrib/librdns/resolver.c
+++ b/contrib/librdns/resolver.c
@@ -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)
{
diff --git a/contrib/librdns/util.c b/contrib/librdns/util.c
index 3cdb88fd0..9b74b7466 100644
--- a/contrib/librdns/util.c
+++ b/contrib/librdns/util.c
@@ -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);
More information about the Commits
mailing list