diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2022-01-03 11:31:30 +0000 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2022-01-03 11:31:30 +0000 |
commit | 578f51a5114e5835fb545d7231ae3618aa015d11 (patch) | |
tree | 4f6daad28b2fd090190c7931ef8cbcf6e6bf1d4d /contrib | |
parent | e9ef523d5ee730c7d93a0bcc97ebfe04c98d3a60 (diff) | |
download | rspamd-578f51a5114e5835fb545d7231ae3618aa015d11.tar.gz rspamd-578f51a5114e5835fb545d7231ae3618aa015d11.zip |
[Project] Rdns: Implement TCP writing logic
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/librdns/dns_private.h | 4 | ||||
-rw-r--r-- | contrib/librdns/resolver.c | 80 |
2 files changed, 82 insertions, 2 deletions
diff --git a/contrib/librdns/dns_private.h b/contrib/librdns/dns_private.h index 38b6c8cc1..cc2d48683 100644 --- a/contrib/librdns/dns_private.h +++ b/contrib/librdns/dns_private.h @@ -152,8 +152,8 @@ enum rdns_io_channel_flags { */ struct rdns_tcp_output_chain { uint16_t next_write_size; - struct rdns_request *req; uint16_t cur_write; + struct rdns_request *req; struct rdns_tcp_output_chain *prev, *next; }; @@ -162,8 +162,8 @@ struct rdns_tcp_output_chain { */ struct rdns_tcp_channel { uint16_t next_read_size; - unsigned char *cur_read_buf; uint16_t cur_read; + unsigned char *cur_read_buf; /* Chained set of the planned writes */ struct rdns_tcp_output_chain *output_chain; diff --git a/contrib/librdns/resolver.c b/contrib/librdns/resolver.c index 264b46b24..8598cfdf5 100644 --- a/contrib/librdns/resolver.c +++ b/contrib/librdns/resolver.c @@ -31,6 +31,7 @@ #include <string.h> #include <errno.h> #include <stdarg.h> +#include <sys/uio.h> #include "rdns.h" #include "dns_private.h" @@ -607,9 +608,88 @@ rdns_process_udp_retransmit (int fd, struct rdns_request *req) } } +static ssize_t +rdns_write_output_chain (struct rdns_io_channel *ioc, struct rdns_tcp_output_chain *oc) +{ + ssize_t r; + struct iovec iov[2]; + int niov, already_written; + + switch (oc->cur_write) { + case 0: + /* Size + DNS request in full */ + iov[0].iov_base = &oc->next_write_size; + iov[0].iov_len = sizeof (oc->next_write_size); + iov[1].iov_base = oc->req->packet; + iov[1].iov_len = oc->req->packet_len; + niov = 2; + break; + case 1: + /* Partial Size + DNS request in full */ + iov[0].iov_base = ((unsigned char *)&oc->next_write_size) + 1; + iov[0].iov_len = 1; + iov[1].iov_base = oc->req->packet; + iov[1].iov_len = oc->req->packet_len; + niov = 2; + break; + default: + /* Merely DNS packet */ + already_written = oc->cur_write - 2; + if (oc->req->packet_len <= already_written) { + errno = EINVAL; + return -1; + } + iov[0].iov_base = oc->req->packet + already_written; + iov[0].iov_len = oc->req->packet_len - already_written; + niov = 1; + break; + } + + r = writev(ioc->sock, iov, niov); + + if (r > 0) { + oc->cur_write += r; + } + + return r; +} + static void rdns_process_tcp_write (int fd, struct rdns_io_channel *ioc) { + struct rdns_resolver *resolver = ioc->resolver; + + + /* Try to write as much as we can */ + struct rdns_tcp_output_chain *oc, *tmp; + DL_FOREACH_SAFE(ioc->tcp->output_chain, oc, tmp) { + ssize_t r = rdns_write_output_chain (ioc, oc); + + if (r == -1) { + if (errno == EAGAIN || errno == EINTR) { + /* Write even is persistent */ + return; + } + else { + rdns_err ("error when trying to write request to %s: %s", + ioc->srv->name, strerror (errno)); + rdns_ioc_tcp_reset (ioc); + return; + } + } + else if (oc->next_write_size < oc->cur_write) { + /* Packet has been fully written, remove it */ + DL_DELETE(ioc->tcp->output_chain, oc); + /* Data in output buffer belongs to request */ + free (oc); + ioc->tcp->cur_output_chains --; + } + else { + /* Buffer is not yet processed, stop unless we can continue */ + break; + } + } + if (ioc->tcp->cur_output_chains == 0) { /* Unregister write event */ ioc->resolver->async->del_write (ioc->resolver->async->data, |