From: Richard Russon Date: Mon, 17 Sep 2018 12:56:25 +0000 (+0100) Subject: reorg socket functions X-Git-Tag: 2019-10-25~644 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=43221622d16b6b971bd717ff7fff19b7df8e2eed;p=neomutt reorg socket functions --- diff --git a/conn/conn_raw.c b/conn/conn_raw.c index 52633da14..05324d77f 100644 --- a/conn/conn_raw.c +++ b/conn/conn_raw.c @@ -112,128 +112,7 @@ static int socket_connect(int fd, struct sockaddr *sa) } /** - * raw_socket_close - Close a socket - * @param conn Connection to a server - * @retval 0 Success - * @retval -1 Error, see errno - */ -int raw_socket_close(struct Connection *conn) -{ - return close(conn->fd); -} - -/** - * raw_socket_read - Read data from a socket - * @param conn Connection to a server - * @param buf Buffer to store the data - * @param len Number of bytes to read - * @retval >0 Success, number of bytes read - * @retval -1 Error, see errno - */ -int raw_socket_read(struct Connection *conn, char *buf, size_t len) -{ - int rc; - - mutt_sig_allow_interrupt(1); - rc = read(conn->fd, buf, len); - if (rc == -1) - { - mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno)); - SigInt = 0; - } - mutt_sig_allow_interrupt(0); - - if (SigInt) - { - mutt_error(_("Connection to %s has been aborted"), conn->account.host); - SigInt = 0; - rc = -1; - } - - return rc; -} - -/** - * raw_socket_write - Write data to a socket - * @param conn Connection to a server - * @param buf Buffer to read into - * @param count Number of bytes to read - * @retval >0 Success, number of bytes written - * @retval -1 Error, see errno - */ -int raw_socket_write(struct Connection *conn, const char *buf, size_t count) -{ - int rc; - - mutt_sig_allow_interrupt(1); - rc = write(conn->fd, buf, count); - if (rc == -1) - { - mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno)); - SigInt = 0; - } - mutt_sig_allow_interrupt(0); - - if (SigInt) - { - mutt_error(_("Connection to %s has been aborted"), conn->account.host); - SigInt = 0; - rc = -1; - } - - return rc; -} - -/** - * raw_socket_poll - Checks whether reads would block - * @param conn Connection to a server - * @param wait_secs How long to wait for a response - * @retval >0 There is data to read - * @retval 0 Read would block - * @retval -1 Connection doesn't support polling - */ -int raw_socket_poll(struct Connection *conn, time_t wait_secs) -{ - fd_set rfds; - unsigned long wait_millis; - struct timeval tv, pre_t, post_t; - - if (conn->fd < 0) - return -1; - - wait_millis = wait_secs * 1000UL; - - while (true) - { - tv.tv_sec = wait_millis / 1000; - tv.tv_usec = (wait_millis % 1000) * 1000; - - FD_ZERO(&rfds); - FD_SET(conn->fd, &rfds); - - gettimeofday(&pre_t, NULL); - const int rc = select(conn->fd + 1, &rfds, NULL, NULL, &tv); - gettimeofday(&post_t, NULL); - - if (rc > 0 || (rc < 0 && errno != EINTR)) - return rc; - - if (SigInt) - mutt_query_exit(); - - wait_millis += (pre_t.tv_sec * 1000UL) + (pre_t.tv_usec / 1000); - const unsigned long post_t_millis = (post_t.tv_sec * 1000UL) + (post_t.tv_usec / 1000); - if (wait_millis <= post_t_millis) - return 0; - wait_millis -= post_t_millis; - } -} - -/** - * raw_socket_open - Open a socket - * @param conn Connection to a server - * @retval 0 Success - * @retval -1 Error + * raw_socket_open - Open a socket - Implements Connection::conn_open() */ int raw_socket_open(struct Connection *conn) { @@ -379,3 +258,103 @@ int raw_socket_open(struct Connection *conn) return 0; } + +/** + * raw_socket_read - Read data from a socket - Implements Connection::conn_read() + */ +int raw_socket_read(struct Connection *conn, char *buf, size_t len) +{ + int rc; + + mutt_sig_allow_interrupt(1); + rc = read(conn->fd, buf, len); + if (rc == -1) + { + mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno)); + SigInt = 0; + } + mutt_sig_allow_interrupt(0); + + if (SigInt) + { + mutt_error(_("Connection to %s has been aborted"), conn->account.host); + SigInt = 0; + rc = -1; + } + + return rc; +} + +/** + * raw_socket_write - Write data to a socket - Implements Connection::conn_write() + */ +int raw_socket_write(struct Connection *conn, const char *buf, size_t count) +{ + int rc; + + mutt_sig_allow_interrupt(1); + rc = write(conn->fd, buf, count); + if (rc == -1) + { + mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno)); + SigInt = 0; + } + mutt_sig_allow_interrupt(0); + + if (SigInt) + { + mutt_error(_("Connection to %s has been aborted"), conn->account.host); + SigInt = 0; + rc = -1; + } + + return rc; +} + +/** + * raw_socket_poll - Checks whether reads would block - Implements Connection::conn_poll() + */ +int raw_socket_poll(struct Connection *conn, time_t wait_secs) +{ + fd_set rfds; + unsigned long wait_millis; + struct timeval tv, pre_t, post_t; + + if (conn->fd < 0) + return -1; + + wait_millis = wait_secs * 1000UL; + + while (true) + { + tv.tv_sec = wait_millis / 1000; + tv.tv_usec = (wait_millis % 1000) * 1000; + + FD_ZERO(&rfds); + FD_SET(conn->fd, &rfds); + + gettimeofday(&pre_t, NULL); + const int rc = select(conn->fd + 1, &rfds, NULL, NULL, &tv); + gettimeofday(&post_t, NULL); + + if (rc > 0 || (rc < 0 && errno != EINTR)) + return rc; + + if (SigInt) + mutt_query_exit(); + + wait_millis += (pre_t.tv_sec * 1000UL) + (pre_t.tv_usec / 1000); + const unsigned long post_t_millis = (post_t.tv_sec * 1000UL) + (post_t.tv_usec / 1000); + if (wait_millis <= post_t_millis) + return 0; + wait_millis -= post_t_millis; + } +} + +/** + * raw_socket_close - Close a socket - Implements Connection::conn_close() + */ +int raw_socket_close(struct Connection *conn) +{ + return close(conn->fd); +} diff --git a/conn/connection.h b/conn/connection.h index 201330762..6a392393c 100644 --- a/conn/connection.h +++ b/conn/connection.h @@ -47,11 +47,48 @@ struct Connection TAILQ_ENTRY(Connection) entries; void *sockdata; - int (*conn_read)(struct Connection *conn, char *buf, size_t len); + + /** + * conn_open - Open a socket Connection + * @param conn Connection to a server + * @retval 0 Success + * @retval -1 Error + */ + int (*conn_open) (struct Connection *conn); + /** + * conn_read - Read from a socket Connection + * @param conn Connection to a server + * @param buf Buffer to store the data + * @param len Number of bytes to read + * @retval >0 Success, number of bytes read + * @retval -1 Error, see errno + */ + int (*conn_read) (struct Connection *conn, char *buf, size_t len); + /** + * conn_write - Write to a socket Connection + * @param conn Connection to a server + * @param buf Buffer to read into + * @param len Number of bytes to read + * @retval >0 Success, number of bytes written + * @retval -1 Error, see errno + */ int (*conn_write)(struct Connection *conn, const char *buf, size_t count); - int (*conn_open)(struct Connection *conn); + /** + * conn_poll - Check whether a socket read would block + * @param conn Connection to a server + * @param wait_secs How long to wait for a response + * @retval >0 There is data to read + * @retval 0 Read would block + * @retval -1 Connection doesn't support polling + */ + int (*conn_poll) (struct Connection *conn, time_t wait_secs); + /** + * conn_close - Close a socket Connection + * @param conn Connection to a server + * @retval 0 Success + * @retval -1 Error, see errno + */ int (*conn_close)(struct Connection *conn); - int (*conn_poll)(struct Connection *conn, time_t wait_secs); }; #endif /* MUTT_CONN_CONNECTION_H */ diff --git a/conn/sasl.c b/conn/sasl.c index 25de5871d..1d6521054 100644 --- a/conn/sasl.c +++ b/conn/sasl.c @@ -355,10 +355,10 @@ static int mutt_sasl_conn_close(struct Connection *conn) /* restore connection's underlying methods */ conn->sockdata = sasldata->sockdata; conn->conn_open = sasldata->msasl_open; - conn->conn_close = sasldata->msasl_close; conn->conn_read = sasldata->msasl_read; conn->conn_write = sasldata->msasl_write; conn->conn_poll = sasldata->msasl_poll; + conn->conn_close = sasldata->msasl_close; /* release sasl resources */ sasl_dispose(&sasldata->saslconn); @@ -689,18 +689,18 @@ void mutt_sasl_setup_conn(struct Connection *conn, sasl_conn_t *saslconn) /* preserve old functions */ sasldata->sockdata = conn->sockdata; sasldata->msasl_open = conn->conn_open; - sasldata->msasl_close = conn->conn_close; sasldata->msasl_read = conn->conn_read; sasldata->msasl_write = conn->conn_write; sasldata->msasl_poll = conn->conn_poll; + sasldata->msasl_close = conn->conn_close; /* and set up new functions */ conn->sockdata = sasldata; conn->conn_open = mutt_sasl_conn_open; - conn->conn_close = mutt_sasl_conn_close; conn->conn_read = mutt_sasl_conn_read; conn->conn_write = mutt_sasl_conn_write; conn->conn_poll = mutt_sasl_conn_poll; + conn->conn_close = mutt_sasl_conn_close; } /** diff --git a/conn/sasl.h b/conn/sasl.h index a75f1c9c3..3f6aa7251 100644 --- a/conn/sasl.h +++ b/conn/sasl.h @@ -52,11 +52,11 @@ struct SaslData /* underlying socket data */ void *sockdata; - int (*msasl_open)(struct Connection *conn); + int (*msasl_open) (struct Connection *conn); int (*msasl_close)(struct Connection *conn); - int (*msasl_read)(struct Connection *conn, char *buf, size_t len); + int (*msasl_read) (struct Connection *conn, char *buf, size_t len); int (*msasl_write)(struct Connection *conn, const char *buf, size_t count); - int (*msasl_poll)(struct Connection *conn, time_t wait_secs); + int (*msasl_poll) (struct Connection *conn, time_t wait_secs); }; #endif /* MUTT_CONN_SASL_H */ diff --git a/conn/ssl.c b/conn/ssl.c index 4850729f3..338bed8e3 100644 --- a/conn/ssl.c +++ b/conn/ssl.c @@ -95,6 +95,8 @@ static int SkipModeExDataIndex = -1; * open up another connection to the same server in this session */ static STACK_OF(X509) *SslSessionCerts = NULL; +static int ssl_socket_close(struct Connection *conn); + /** * struct SslSockData - SSL socket data */ @@ -370,31 +372,6 @@ static int ssl_socket_open_err(struct Connection *conn) return -1; } -/** - * ssl_socket_close - Close an SSL connection - * @param conn Connection to a server - * @retval 0 Success - * @retval -1 Error, see errno - */ -static int ssl_socket_close(struct Connection *conn) -{ - struct SslSockData *data = conn->sockdata; - - if (data) - { - if (data->isopen) - SSL_shutdown(data->ssl); - - /* hold onto this for the life of neomutt, in case we want to reconnect. - * The purist in me wants a mutt_exit hook. */ - SSL_free(data->ssl); - SSL_CTX_free(data->ctx); - FREE(&conn->sockdata); - } - - return raw_socket_close(conn); -} - /** * x509_get_part - Retrieve from X509 data * @param name Name of data to retrieve @@ -626,59 +603,6 @@ static int ssl_init(void) return 0; } -/** - * ssl_socket_read - Read data from an SSL socket - * @param conn Connection to a server - * @param buf Buffer to store the data - * @param len Number of bytes to read - * @retval >0 Success, number of bytes read - * @retval -1 Error, see errno - */ -static int ssl_socket_read(struct Connection *conn, char *buf, size_t len) -{ - struct SslSockData *data = conn->sockdata; - int rc; - - rc = SSL_read(data->ssl, buf, len); - if (rc <= 0 || errno == EINTR) - { - if (errno == EINTR) - { - rc = -1; - } - data->isopen = 0; - ssl_err(data, rc); - } - - return rc; -} - -/** - * ssl_socket_write - Write data to an SSL socket - * @param conn Connection to a server - * @param buf Buffer to read into - * @param len Number of bytes to read - * @retval >0 Success, number of bytes written - * @retval -1 Error, see errno - */ -static int ssl_socket_write(struct Connection *conn, const char *buf, size_t len) -{ - struct SslSockData *data = conn->sockdata; - int rc; - - rc = SSL_write(data->ssl, buf, len); - if (rc <= 0 || errno == EINTR) - { - if (errno == EINTR) - { - rc = -1; - } - ssl_err(data, rc); - } - - return rc; -} - /** * ssl_get_client_cert - Get the client certificate for an SSL connection * @param ssldata SSL socket data @@ -1397,25 +1321,85 @@ free_sasldata: } /** - * ssl_socket_open - Open an SSL socket - * @param conn Connection to a server - * @retval 0 Success - * @retval -1 Error + * ssl_socket_open - Open an SSL socket - Implements Connection::conn_open() */ static int ssl_socket_open(struct Connection *conn) { - int ret; - if (raw_socket_open(conn) < 0) return -1; - ret = ssl_setup(conn); + int ret = ssl_setup(conn); if (ret) raw_socket_close(conn); return ret; } +/** + * ssl_socket_read - Read data from an SSL socket - Implements Connection::conn_read() + */ +static int ssl_socket_read(struct Connection *conn, char *buf, size_t len) +{ + struct SslSockData *data = conn->sockdata; + int rc; + + rc = SSL_read(data->ssl, buf, len); + if (rc <= 0 || errno == EINTR) + { + if (errno == EINTR) + { + rc = -1; + } + data->isopen = 0; + ssl_err(data, rc); + } + + return rc; +} + +/** + * ssl_socket_write - Write data to an SSL socket - Implements Connection::conn_write() + */ +static int ssl_socket_write(struct Connection *conn, const char *buf, size_t len) +{ + struct SslSockData *data = conn->sockdata; + int rc; + + rc = SSL_write(data->ssl, buf, len); + if (rc <= 0 || errno == EINTR) + { + if (errno == EINTR) + { + rc = -1; + } + ssl_err(data, rc); + } + + return rc; +} + +/** + * ssl_socket_close - Close an SSL connection - Implements Connection::conn_close() + */ +static int ssl_socket_close(struct Connection *conn) +{ + struct SslSockData *data = conn->sockdata; + + if (data) + { + if (data->isopen) + SSL_shutdown(data->ssl); + + /* hold onto this for the life of neomutt, in case we want to reconnect. + * The purist in me wants a mutt_exit hook. */ + SSL_free(data->ssl); + SSL_CTX_free(data->ctx); + FREE(&conn->sockdata); + } + + return raw_socket_close(conn); +} + /** * mutt_ssl_starttls - Negotiate TLS over an already opened connection * @param conn Connection to a server @@ -1424,12 +1408,10 @@ static int ssl_socket_open(struct Connection *conn) */ int mutt_ssl_starttls(struct Connection *conn) { - int ret; - if (ssl_init()) return -1; - ret = ssl_setup(conn); + int ret = ssl_setup(conn); /* hmm. watch out if we're starting TLS over any method other than raw. */ conn->conn_read = ssl_socket_read; @@ -1456,8 +1438,8 @@ int mutt_ssl_socket_setup(struct Connection *conn) conn->conn_open = ssl_socket_open; conn->conn_read = ssl_socket_read; conn->conn_write = ssl_socket_write; - conn->conn_close = ssl_socket_close; conn->conn_poll = raw_socket_poll; + conn->conn_close = ssl_socket_close; return 0; } diff --git a/conn/ssl_gnutls.c b/conn/ssl_gnutls.c index 59b5ae204..42ccb811b 100644 --- a/conn/ssl_gnutls.c +++ b/conn/ssl_gnutls.c @@ -63,6 +63,8 @@ #define CERT_SEP "-----BEGIN" +static int tls_socket_close(struct Connection *conn); + /** * struct TlsSockData - TLS socket data */ @@ -96,104 +98,6 @@ static int tls_init(void) return 0; } -/** - * tls_socket_read - Read data from a TLS socket - * @param conn Connection to a server - * @param buf Buffer to store the data - * @param len Number of bytes to read - * @retval >0 Success, number of bytes read - * @retval -1 Error, see errno - */ -static int tls_socket_read(struct Connection *conn, char *buf, size_t len) -{ - struct TlsSockData *data = conn->sockdata; - int rc; - - if (!data) - { - mutt_error(_("Error: no TLS socket open")); - return -1; - } - - do - { - rc = gnutls_record_recv(data->state, buf, len); - if ((rc < 0 && gnutls_error_is_fatal(rc) == 1) || rc == GNUTLS_E_INTERRUPTED) - { - mutt_error("tls_socket_read (%s)", gnutls_strerror(rc)); - return -1; - } - } while (rc == GNUTLS_E_AGAIN); - - return rc; -} - -/** - * tls_socket_write - Write data to a TLS socket - * @param conn Connection to a server - * @param buf Buffer to read into - * @param len Number of bytes to read - * @retval >0 Success, number of bytes written - * @retval -1 Error, see errno - */ -static int tls_socket_write(struct Connection *conn, const char *buf, size_t len) -{ - struct TlsSockData *data = conn->sockdata; - size_t sent = 0; - - if (!data) - { - mutt_error(_("Error: no TLS socket open")); - return -1; - } - - do - { - const int ret = gnutls_record_send(data->state, buf + sent, len - sent); - if (ret < 0) - { - if (gnutls_error_is_fatal(ret) == 1 || ret == GNUTLS_E_INTERRUPTED) - { - mutt_error("tls_socket_write (%s)", gnutls_strerror(ret)); - return -1; - } - return ret; - } - sent += ret; - } while (sent < len); - - return sent; -} - -/** - * tls_socket_close - Close a TLS socket - * @param conn Connection to a server - * @retval 0 Success - * @retval -1 Error, see errno - */ -static int tls_socket_close(struct Connection *conn) -{ - struct TlsSockData *data = conn->sockdata; - if (data) - { - /* shut down only the write half to avoid hanging waiting for the remote to respond. - * - * RFC5246 7.2.1. "Closure Alerts" - * - * It is not required for the initiator of the close to wait for the - * responding close_notify alert before closing the read side of the - * connection. - */ - gnutls_bye(data->state, GNUTLS_SHUT_WR); - - gnutls_certificate_free_credentials(data->xcred); - gnutls_deinit(data->state); - FREE(&conn->sockdata); - } - - return raw_socket_close(conn); -} - /** * tls_starttls_close - Close a TLS connection * @param conn Connection to a server @@ -1234,10 +1138,7 @@ fail: } /** - * tls_socket_open - Open a TLS socket - * @param conn Connection to a server - * @retval 0 Success - * @retval -1 Error + * tls_socket_open - Open a TLS socket - Implements Connection::conn_open() */ static int tls_socket_open(struct Connection *conn) { @@ -1253,6 +1154,91 @@ static int tls_socket_open(struct Connection *conn) return 0; } +/** + * tls_socket_read - Read data from a TLS socket - Implements Connection::conn_read() + */ +static int tls_socket_read(struct Connection *conn, char *buf, size_t len) +{ + struct TlsSockData *data = conn->sockdata; + int rc; + + if (!data) + { + mutt_error(_("Error: no TLS socket open")); + return -1; + } + + do + { + rc = gnutls_record_recv(data->state, buf, len); + if ((rc < 0 && gnutls_error_is_fatal(rc) == 1) || rc == GNUTLS_E_INTERRUPTED) + { + mutt_error("tls_socket_read (%s)", gnutls_strerror(rc)); + return -1; + } + } while (rc == GNUTLS_E_AGAIN); + + return rc; +} + +/** + * tls_socket_write - Write data to a TLS socket - Implements Connection::conn_write() + */ +static int tls_socket_write(struct Connection *conn, const char *buf, size_t len) +{ + struct TlsSockData *data = conn->sockdata; + size_t sent = 0; + + if (!data) + { + mutt_error(_("Error: no TLS socket open")); + return -1; + } + + do + { + const int ret = gnutls_record_send(data->state, buf + sent, len - sent); + if (ret < 0) + { + if (gnutls_error_is_fatal(ret) == 1 || ret == GNUTLS_E_INTERRUPTED) + { + mutt_error("tls_socket_write (%s)", gnutls_strerror(ret)); + return -1; + } + return ret; + } + sent += ret; + } while (sent < len); + + return sent; +} + +/** + * tls_socket_close - Close a TLS socket - Implements Connection::conn_close() + */ +static int tls_socket_close(struct Connection *conn) +{ + struct TlsSockData *data = conn->sockdata; + if (data) + { + /* shut down only the write half to avoid hanging waiting for the remote to respond. + * + * RFC5246 7.2.1. "Closure Alerts" + * + * It is not required for the initiator of the close to wait for the + * responding close_notify alert before closing the read side of the + * connection. + */ + gnutls_bye(data->state, GNUTLS_SHUT_WR); + + gnutls_certificate_free_credentials(data->xcred); + gnutls_deinit(data->state); + FREE(&conn->sockdata); + } + + return raw_socket_close(conn); +} + /** * mutt_ssl_socket_setup - Set up SSL socket mulitplexor * @param conn Connection to a server diff --git a/conn/tunnel.c b/conn/tunnel.c index 4be00e655..3b7e88081 100644 --- a/conn/tunnel.c +++ b/conn/tunnel.c @@ -53,10 +53,7 @@ struct TunnelData }; /** - * tunnel_socket_open - Open a tunnel socket - * @param conn Connection to a server - * @retval 0 Success - * @retval -1 Error + * tunnel_socket_open - Open a tunnel socket - Implements Connection::conn_open() */ static int tunnel_socket_open(struct Connection *conn) { @@ -135,36 +132,7 @@ static int tunnel_socket_open(struct Connection *conn) } /** - * tunnel_socket_close - Close a tunnel socket - * @param conn Connection to a server - * @retval 0 Success - * @retval -1 Error, see errno - */ -static int tunnel_socket_close(struct Connection *conn) -{ - struct TunnelData *tunnel = conn->sockdata; - int status; - - close(tunnel->readfd); - close(tunnel->writefd); - waitpid(tunnel->pid, &status, 0); - if (!WIFEXITED(status) || WEXITSTATUS(status)) - { - mutt_error(_("Tunnel to %s returned error %d (%s)"), conn->account.host, - WEXITSTATUS(status), NONULL(mutt_str_sysexit(WEXITSTATUS(status)))); - } - FREE(&conn->sockdata); - - return 0; -} - -/** - * tunnel_socket_read - Read data from a tunnel socket - * @param conn Connection to a server - * @param buf Buffer to store the data - * @param len Number of bytes to read - * @retval >0 Success, number of bytes read - * @retval -1 Error, see errno + * tunnel_socket_read - Read data from a tunnel socket - Implements Connection::conn_read() */ static int tunnel_socket_read(struct Connection *conn, char *buf, size_t len) { @@ -181,12 +149,7 @@ static int tunnel_socket_read(struct Connection *conn, char *buf, size_t len) } /** - * tunnel_socket_write - Write data to a tunnel socket - * @param conn Connection to a server - * @param buf Buffer to read into - * @param len Number of bytes to read - * @retval >0 Success, number of bytes written - * @retval -1 Error, see errno + * tunnel_socket_write - Write data to a tunnel socket - Implements Connection::conn_write() */ static int tunnel_socket_write(struct Connection *conn, const char *buf, size_t len) { @@ -203,12 +166,7 @@ static int tunnel_socket_write(struct Connection *conn, const char *buf, size_t } /** - * tunnel_socket_poll - Checks whether tunnel reads would block - * @param conn Connection to a server - * @param wait_secs How long to wait for a response - * @retval >0 There is data to read - * @retval 0 Read would block - * @retval -1 Connection doesn't support polling + * tunnel_socket_poll - Checks whether tunnel reads would block - Implements Connection::conn_poll() */ static int tunnel_socket_poll(struct Connection *conn, time_t wait_secs) { @@ -224,6 +182,27 @@ static int tunnel_socket_poll(struct Connection *conn, time_t wait_secs) return rc; } +/** + * tunnel_socket_close - Close a tunnel socket - Implements Connection::conn_close() + */ +static int tunnel_socket_close(struct Connection *conn) +{ + struct TunnelData *tunnel = conn->sockdata; + int status; + + close(tunnel->readfd); + close(tunnel->writefd); + waitpid(tunnel->pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status)) + { + mutt_error(_("Tunnel to %s returned error %d (%s)"), conn->account.host, + WEXITSTATUS(status), NONULL(mutt_str_sysexit(WEXITSTATUS(status)))); + } + FREE(&conn->sockdata); + + return 0; +} + /** * mutt_tunnel_socket_setup - sets up tunnel connection functions * @param conn Connection to assign functions to