]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
dnstap io, ssl handshake.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 5 Feb 2020 12:59:56 +0000 (13:59 +0100)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 5 Feb 2020 12:59:56 +0000 (13:59 +0100)
dnstap/dtstream.c
dnstap/dtstream.h
dnstap/unbound-dnstap-socket.c
testcode/testbound.c

index 6e3ade352270109edde5102d1f8581ee99cff478..5db9d602af0c1672925cd17056b7d6a5b92f1121 100644 (file)
@@ -66,6 +66,7 @@
 /** the msec to wait for reconnect max after backoff */
 #define DTIO_RECONNECT_TIMEOUT_MAX 1000
 
+struct stop_flush_info;
 /** DTIO command channel commands */
 enum {
        /** DTIO command channel stop */
@@ -77,9 +78,11 @@ enum {
 /** open the output channel */
 static void dtio_open_output(struct dt_io_thread* dtio);
 /** add output event for read and write */
-static void dtio_add_output_event_write(struct dt_io_thread* dtio);
+static int dtio_add_output_event_write(struct dt_io_thread* dtio);
 /** start reconnection attempts */
 static void dtio_reconnect_enable(struct dt_io_thread* dtio);
+/** stop from stop_flush event loop */
+static void dtio_stop_flush_exit(struct stop_flush_info* info);
 
 struct dt_msg_queue*
 dt_msg_queue_create(void)
@@ -395,7 +398,8 @@ static void dtio_reconnect_timeout_cb(int ATTR_UNUSED(fd),
 
        dtio_open_output(dtio);
        if(dtio->event) {
-               dtio_add_output_event_write(dtio);
+               if(!dtio_add_output_event_write(dtio))
+                       return;
                /* nothing wrong so far, wait on the output event */
                return;
        }
@@ -573,14 +577,6 @@ static int dtio_write_buf(struct dt_io_thread* dtio, uint8_t* buf,
        ssize_t ret;
        if(dtio->fd == -1)
                return -1;
-       if(dtio->check_nb_connect) {
-               int connect_err = dtio_check_nb_connect(dtio);
-               if(connect_err == -1) {
-                       return -1;
-               } else if(connect_err == 0) {
-                       return 0;
-               }
-       }
        ret = send(dtio->fd, (void*)buf, len, 0);
        if(ret == -1) {
 #ifndef USE_WINSOCK
@@ -612,17 +608,6 @@ static int dtio_write_with_writev(struct dt_io_thread* dtio)
        uint32_t sendlen = htonl(dtio->cur_msg_len);
        struct iovec iov[2];
        ssize_t r;
-       if(dtio->check_nb_connect) {
-               int connect_err = dtio_check_nb_connect(dtio);
-               if(connect_err == -1) {
-                       /* close the channel */
-                       dtio_del_output_event(dtio);
-                       dtio_close_output(dtio);
-                       return 0;
-               } else if(connect_err == 0) {
-                       return 0;
-               }
-       }
        iov[0].iov_base = ((uint8_t*)&sendlen)+dtio->cur_msg_len_done;
        iov[0].iov_len = sizeof(sendlen)-dtio->cur_msg_len_done;
        iov[1].iov_base = dtio->cur_msg;
@@ -794,12 +779,12 @@ static int dtio_check_close(struct dt_io_thread* dtio)
 }
 
 /** add the output file descriptor event for listening, read only */
-static void dtio_add_output_event_read(struct dt_io_thread* dtio)
+static int dtio_add_output_event_read(struct dt_io_thread* dtio)
 {
        if(!dtio->event)
-               return;
+               return 0;
        if(dtio->event_added && !dtio->event_added_is_write)
-               return;
+               return 1;
        /* we have to (re-)register the event */
        if(dtio->event_added)
                ub_event_del(dtio->event);
@@ -810,19 +795,20 @@ static void dtio_add_output_event_read(struct dt_io_thread* dtio)
                dtio->event_added_is_write = 0;
                /* close output and start reattempts to open it */
                dtio_close_output(dtio);
-               return;
+               return 0;
        }
        dtio->event_added = 1;
        dtio->event_added_is_write = 0;
+       return 1;
 }
 
 /** add the output file descriptor event for listening, read and write */
-static void dtio_add_output_event_write(struct dt_io_thread* dtio)
+static int dtio_add_output_event_write(struct dt_io_thread* dtio)
 {
        if(!dtio->event)
-               return;
+               return 0;
        if(dtio->event_added && dtio->event_added_is_write)
-               return;
+               return 1;
        /* we have to (re-)register the event */
        if(dtio->event_added)
                ub_event_del(dtio->event);
@@ -833,10 +819,11 @@ static void dtio_add_output_event_write(struct dt_io_thread* dtio)
                dtio->event_added_is_write = 0;
                /* close output and start reattempts to open it */
                dtio_close_output(dtio);
-               return;
+               return 0;
        }
        dtio->event_added = 1;
        dtio->event_added_is_write = 1;
+       return 1;
 }
 
 /** put the dtio thread to sleep */
@@ -844,7 +831,117 @@ static void dtio_sleep(struct dt_io_thread* dtio)
 {
        /* unregister the event polling for write, because there is
         * nothing to be written */
-       dtio_add_output_event_read(dtio);
+       (void)dtio_add_output_event_read(dtio);
+}
+
+/** enable the brief read condition */
+static int dtio_enable_brief_read(struct dt_io_thread* dtio)
+{
+       dtio->ssl_brief_read = 1;
+       if(dtio->stop_flush_event) {
+               ub_event_del(dtio->stop_flush_event);
+               ub_event_del_bits(dtio->stop_flush_event, UB_EV_WRITE);
+               if(ub_event_add(dtio->stop_flush_event, NULL) != 0) {
+                       log_err("dnstap io, stop flush, could not ub_event_add");
+                       return 0;
+               }
+               return 1;
+       }
+       return dtio_add_output_event_read(dtio);
+}
+
+/** disable the brief read condition */
+static int dtio_disable_brief_read(struct dt_io_thread* dtio)
+{
+       dtio->ssl_brief_read = 0;
+       if(dtio->stop_flush_event) {
+               ub_event_del(dtio->stop_flush_event);
+               ub_event_add_bits(dtio->stop_flush_event, UB_EV_WRITE);
+               if(ub_event_add(dtio->stop_flush_event, NULL) != 0) {
+                       log_err("dnstap io, stop flush, could not ub_event_add");
+                       return 0;
+               }
+               return 1;
+       }
+       return dtio_add_output_event_write(dtio);
+}
+
+/** perform ssl handshake, returns 1 if okay, 0 to stop */
+static int dtio_ssl_handshake(struct dt_io_thread* dtio,
+       struct stop_flush_info* info)
+{
+       int r;
+       if(dtio->ssl_brief_read) {
+               /* assume the brief read condition is satisfied,
+                * if we need more or again, we can set it again */
+               if(!dtio_disable_brief_read(dtio)) {
+                       if(info) dtio_stop_flush_exit(info);
+                       return 0;
+               }
+       }
+       if(dtio->ssl_handshake_done)
+               return 1;
+
+       ERR_clear_error();
+       r = SSL_do_handshake(dtio->ssl);
+       if(r != 1) {
+               int want = SSL_get_error(dtio->ssl, r);
+               if(want == SSL_ERROR_WANT_READ) {
+                       /* we want to read on the connection */
+                       if(!dtio_enable_brief_read(dtio)) {
+                               if(info) dtio_stop_flush_exit(info);
+                               return 0;
+                       }
+                       return 0;
+               } else if(want == SSL_ERROR_WANT_WRITE) {
+                       /* we want to write on the connection */
+                       return 0;
+               } else if(r == 0) {
+                       /* closed */
+                       if(info) dtio_stop_flush_exit(info);
+                       dtio_del_output_event(dtio);
+                       dtio_close_output(dtio);
+                       return 0;
+               } else if(want == SSL_ERROR_SYSCALL) {
+                       /* SYSCALL and errno==0 means closed uncleanly */
+                       int silent = 0;
+#ifdef EPIPE
+                       if(errno == EPIPE && verbosity < 2)
+                               silent = 1; /* silence 'broken pipe' */
+#endif
+#ifdef ECONNRESET
+                       if(errno == ECONNRESET && verbosity < 2)
+                               silent = 1; /* silence reset by peer */
+#endif
+                       if(errno == 0)
+                               silent = 1;
+                       if(!silent)
+                               log_err("dnstap io, SSL_handshake syscall: %s",
+                                       strerror(errno));
+                       /* closed */
+                       if(info) dtio_stop_flush_exit(info);
+                       dtio_del_output_event(dtio);
+                       dtio_close_output(dtio);
+                       return 0;
+               } else {
+                       unsigned long err = ERR_get_error();
+                       if(!squelch_err_ssl_handshake(err)) {
+                               log_crypto_err_code("dnstap io, ssl handshake failed",
+                                       err);
+                               verbose(VERB_OPS, "dnstap io, ssl handshake failed "
+                                       "from %s", dtio->ip_str);
+                       }
+                       /* closed */
+                       if(info) dtio_stop_flush_exit(info);
+                       dtio_del_output_event(dtio);
+                       dtio_close_output(dtio);
+                       return 0;
+               }
+
+       }
+       /* check peer verification */
+       dtio->ssl_handshake_done = 1;
+       return 1;
 }
 
 /** callback for the dnstap events, to write to the output */
@@ -853,6 +950,26 @@ static void dtio_output_cb(int ATTR_UNUSED(fd), short bits, void* arg)
        struct dt_io_thread* dtio = (struct dt_io_thread*)arg;
        int i;
 
+       if(dtio->check_nb_connect) {
+               int connect_err = dtio_check_nb_connect(dtio);
+               if(connect_err == -1) {
+                       /* close the channel */
+                       dtio_del_output_event(dtio);
+                       dtio_close_output(dtio);
+                       return;
+               } else if(connect_err == 0) {
+                       /* try again later */
+                       return;
+               }
+               /* nonblocking connect check passed, continue */
+       }
+
+       if(dtio->ssl &&
+               (!dtio->ssl_handshake_done || dtio->ssl_brief_read)) {
+               if(!dtio_ssl_handshake(dtio, NULL))
+                       return;
+       }
+
        if((bits&UB_EV_READ)) {
                if(!dtio_check_close(dtio))
                        return;
@@ -919,7 +1036,8 @@ static void dtio_cmd_cb(int fd, short ATTR_UNUSED(bits), void* arg)
        } else if(r == 1 && cmd == DTIO_COMMAND_WAKEUP) {
                verbose(VERB_ALGO, "dnstap io: cmd channel cmd wakeup");
                /* reregister event */
-               dtio_add_output_event_write(dtio);
+               if(!dtio_add_output_event_write(dtio))
+                       return;
                return;
        } else if(r == 1) {
                verbose(VERB_ALGO, "dnstap io: cmd channel unknown command");
@@ -1045,6 +1163,28 @@ static void dtio_stop_ev_cb(int ATTR_UNUSED(fd), short bits, void* arg)
        struct dt_io_thread* dtio = info->dtio;
        if(info->want_to_exit_flush)
                return;
+       if(dtio->check_nb_connect) {
+               /* we don't start the stop_flush if connect still
+                * in progress, but the check code is here, just in case */
+               int connect_err = dtio_check_nb_connect(dtio);
+               if(connect_err == -1) {
+                       /* close the channel, exit the stop flush */
+                       dtio_stop_flush_exit(info);
+                       dtio_del_output_event(dtio);
+                       dtio_close_output(dtio);
+                       return;
+               } else if(connect_err == 0) {
+                       /* try again later */
+                       return;
+               }
+               /* nonblocking connect check passed, continue */
+       }
+       if(dtio->ssl &&
+               (!dtio->ssl_handshake_done || dtio->ssl_brief_read)) {
+               if(!dtio_ssl_handshake(dtio, info))
+                       return;
+       }
+
        if((bits&UB_EV_READ)) {
                if(!dtio_check_close(dtio)) {
                        if(dtio->fd == -1) {
@@ -1098,6 +1238,10 @@ static void dtio_control_stop_flush(struct dt_io_thread* dtio)
                 * sent yet, so nothing to stop or flush */
                return;
        }
+       if(dtio->ssl && !dtio->ssl_handshake_done) {
+               /* no SSL connection has been established yet */
+               return;
+       }
 
        memset(&info, 0, sizeof(info));
        memset(&now, 0, sizeof(now));
@@ -1308,6 +1452,8 @@ static int dtio_setup_ssl(struct dt_io_thread* dtio)
 {
        dtio->ssl = outgoing_ssl_fd(dtio->ssl_ctx, dtio->fd);
        if(!dtio->ssl) return 0;
+       dtio->ssl_handshake_done = 0;
+       dtio->ssl_brief_read = 0;
        return 1;
 }
 
@@ -1376,7 +1522,8 @@ static void dtio_setup_on_base(struct dt_io_thread* dtio)
        dtio_setup_cmd(dtio);
        dtio_setup_reconnect(dtio);
        dtio_open_output(dtio);
-       dtio_add_output_event_write(dtio);
+       if(!dtio_add_output_event_write(dtio))
+               return;
 }
 
 #ifndef THREADS_DISABLED
index b388752d5a87edf6b861f7572a8f191b58161cd0..bb6cebbe677bfaadb1e5c8479e508d9873a7304f 100644 (file)
@@ -122,6 +122,12 @@ struct dt_io_thread {
        int check_nb_connect;
        /** ssl for current connection, type SSL* */
        void* ssl;
+       /** true if the handshake for SSL is done, 0 if not */
+       int ssl_handshake_done;
+       /** true if briefly the SSL wants a read event, 0 if not.
+        * This happens during negotiation, we then do not want to write,
+        * but wait for a read event. */
+       int ssl_brief_read;
 
        /** the buffer that currently getting written, or NULL if no
         * (partial) message written now */
index 011d015dc6f2610066c414cb479f633cb0694c2f..e5a52967a86c24b61a1e47b9c781deda57071924 100644 (file)
@@ -891,15 +891,18 @@ static int tap_handshake(struct tap_data* data)
                        return 0;
                } else if(want == SSL_ERROR_SYSCALL) {
                        /* SYSCALL and errno==0 means closed uncleanly */
+                       int silent = 0;
 #ifdef EPIPE
                        if(errno == EPIPE && verbosity < 2)
-                               return 0; /* silence 'broken pipe' */
+                               silent = 1; /* silence 'broken pipe' */
 #endif
 #ifdef ECONNRESET
                        if(errno == ECONNRESET && verbosity < 2)
-                               return 0; /* silence reset by peer */
+                               silent = 1; /* silence reset by peer */
 #endif
-                       if(errno != 0)
+                       if(errno == 0)
+                               silent = 1;
+                       if(!silent)
                                log_err("SSL_handshake syscall: %s",
                                        strerror(errno));
                        tap_data_free(data);
@@ -912,6 +915,8 @@ static int tap_handshake(struct tap_data* data)
                                verbose(VERB_OPS, "ssl handshake failed "
                                        "from %s", data->id);
                        }
+                       tap_data_free(data);
+                       return 0;
                }
        }
        /* check peer verification */
index fdbc0b92d1878f0ef690be903e266a95a1d3999d..602dffaff14a918b5eedc90dc524e4f896fa967c 100644 (file)
@@ -582,3 +582,8 @@ int tcp_connect_errno_needs_log(struct sockaddr* ATTR_UNUSED(addr),
 {
        return 1;
 }
+
+int squelch_err_ssl_handshake(unsigned long ATTR_UNUSED(err))
+{
+       return 0;
+}