]> source.dussan.org Git - rspamd.git/commitdiff
[Project] Further DNS over TCP architecturing
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Sat, 1 Jan 2022 22:49:48 +0000 (22:49 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Sat, 1 Jan 2022 22:49:48 +0000 (22:49 +0000)
contrib/librdns/dns_private.h
contrib/librdns/resolver.c
contrib/librdns/util.c

index 4429552bce17f85a2dff14086ed019f28da2954f..75fb5a3fabb9cd74eb36eb1fb6f1fa85b40e9394 100644 (file)
@@ -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
index d7d41915f0f8bb83ee89da6c230424e5a8106533..6ef6d4558dd2c442b430452ec8909982600f3721 100644 (file)
@@ -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)
 {
index 3cdb88fd032ccdb0ba321c3d6589597d3d563c3f..9b74b74666b6ae873ffec679b3dad3224958985f 100644 (file)
@@ -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);