]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: set io events after receiving EAGAIN for TLS
authorIwan Timmer <irtimmer@gmail.com>
Thu, 26 Jul 2018 19:34:16 +0000 (20:34 +0100)
committerIwan Timmer <irtimmer@gmail.com>
Fri, 27 Jul 2018 20:23:17 +0000 (21:23 +0100)
During handshake and TLS session closing, messages needs to be exchanged. Therefore this patch overrides the requested IO events for the TCP stream when the TLS is waiting for sending or receiving of messages during theses periods. This fixes issues with correctly closing the TLS stream and prevents the handshake from hanging in rare cases (not seen yet).

src/resolve/resolved-dns-stream.c
src/resolve/resolved-dns-stream.h
src/resolve/resolved-dnstls-gnutls.c

index ee392efb9da13fb9146fea82649602e8f3ae1762..36205a895c884ebd0845afd5526788bcfa85d4d8 100644 (file)
@@ -36,6 +36,12 @@ static int dns_stream_update_io(DnsStream *s) {
         if (!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size)
                 f |= EPOLLIN;
 
+#if ENABLE_DNS_OVER_TLS
+        /* For handshake and clean closing purposes, TLS can override requested events */
+        if (s->dnstls_events)
+                f = s->dnstls_events;
+#endif
+
         return sd_event_source_set_io_events(s->io_event_source, f);
 }
 
@@ -274,10 +280,17 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
         if (s->encrypted) {
                 r = dnstls_stream_on_io(s);
 
-                if (r == DNSTLS_STREAM_CLOSED || r == -EAGAIN)
+                if (r == DNSTLS_STREAM_CLOSED)
                         return 0;
-                else if (r < 0)
+                else if (r == -EAGAIN)
+                        return dns_stream_update_io(s);
+                else if (r < 0) {
                         return dns_stream_complete(s, -r);
+                } else {
+                        r = dns_stream_update_io(s);
+                        if (r < 0)
+                                return r;
+                }
         }
 #endif
 
index d64dc3b1d42ab386bba79e8591037c4d84e942dc..72db2a1a56ac6d1d4393a787b1b537661ac17279 100644 (file)
@@ -42,6 +42,7 @@ struct DnsStream {
 
 #if ENABLE_DNS_OVER_TLS
         DnsTlsStreamData dnstls_data;
+        int dnstls_events;
 #endif
 
         sd_event_source *io_event_source;
index 9c348cef6789d127dc0a2df65048f13dbdac4786..36ee570b461f26c469a3fd0ce9be07e5eee0517a 100644 (file)
@@ -86,23 +86,28 @@ int dnstls_stream_on_io(DnsStream *stream) {
 
         if (stream->dnstls_data.shutdown) {
                 r = gnutls_bye(stream->dnstls_data.session, GNUTLS_SHUT_RDWR);
-                if (r == GNUTLS_E_AGAIN)
+                if (r == GNUTLS_E_AGAIN) {
+                        stream->dnstls_events = gnutls_record_get_direction(stream->dnstls_data.session) == 1 ? EPOLLOUT : EPOLLIN;
                         return -EAGAIN;
-                else if (r < 0)
+                else if (r < 0)
                         log_debug("Failed to invoke gnutls_bye: %s", gnutls_strerror(r));
 
+                stream->dnstls_events = 0;
                 stream->dnstls_data.shutdown = false;
                 dns_stream_unref(stream);
                 return DNSTLS_STREAM_CLOSED;
         } else if (stream->dnstls_data.handshake < 0) {
                 stream->dnstls_data.handshake = gnutls_handshake(stream->dnstls_data.session);
-                if (stream->dnstls_data.handshake == GNUTLS_E_AGAIN)
+                if (stream->dnstls_data.handshake == GNUTLS_E_AGAIN) {
+                        stream->dnstls_events = gnutls_record_get_direction(stream->dnstls_data.session) == 1 ? EPOLLOUT : EPOLLIN;
                         return -EAGAIN;
-                else if (stream->dnstls_data.handshake < 0) {
+                else if (stream->dnstls_data.handshake < 0) {
                         log_debug("Failed to invoke gnutls_handshake: %s", gnutls_strerror(stream->dnstls_data.handshake));
                         if (gnutls_error_is_fatal(stream->dnstls_data.handshake))
                                 return -ECONNREFUSED;
                 }
+
+                stream->dnstls_events = 0;
         }
 
         return 0;