diff --git a/common.c b/common.c index 8974f57..a92bae4 100644 --- a/common.c +++ b/common.c @@ -95,6 +95,74 @@ xwrite (int fd, const char *data, size_t len, struct error **e) return true; } +// --- Simple network I/O ------------------------------------------------------ + +// TODO: move to liberty and remove from dwmstatus.c as well + +#define SOCKET_IO_OVERFLOW (8 << 20) ///< How large a read buffer can be + +enum socket_io_result +{ + SOCKET_IO_OK, ///< Completed successfully + SOCKET_IO_EOF, ///< Connection shut down by peer + SOCKET_IO_ERROR ///< Connection error +}; + +static enum socket_io_result +socket_io_try_read (int socket_fd, struct str *rb, struct error **e) +{ + // We allow buffering of a fair amount of data, however within reason, + // so that it's not so easy to flood us and cause an allocation failure + ssize_t n_read; + while (rb->len < SOCKET_IO_OVERFLOW) + { + str_ensure_space (rb, 4096); + n_read = recv (socket_fd, rb->str + rb->len, + rb->alloc - rb->len - 1 /* null byte */, 0); + + if (n_read > 0) + { + rb->str[rb->len += n_read] = '\0'; + continue; + } + if (n_read == 0) + return SOCKET_IO_EOF; + + if (errno == EAGAIN) + return SOCKET_IO_OK; + if (errno == EINTR) + continue; + + error_set (e, "%s", strerror (errno)); + return SOCKET_IO_ERROR; + } + return SOCKET_IO_OK; +} + +static enum socket_io_result +socket_io_try_write (int socket_fd, struct str *wb, struct error **e) +{ + ssize_t n_written; + while (wb->len) + { + n_written = send (socket_fd, wb->str, wb->len, 0); + if (n_written >= 0) + { + str_remove_slice (wb, 0, n_written); + continue; + } + + if (errno == EAGAIN) + return SOCKET_IO_OK; + if (errno == EINTR) + continue; + + error_set (e, "%s", strerror (errno)); + return SOCKET_IO_ERROR; + } + return SOCKET_IO_OK; +} + // --- Logging ----------------------------------------------------------------- static void diff --git a/degesch.c b/degesch.c index 4129e07..e1ec751 100644 --- a/degesch.c +++ b/degesch.c @@ -1139,13 +1139,6 @@ REF_COUNTABLE_METHODS (buffer) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -enum transport_io_result -{ - TRANSPORT_IO_OK = 0, ///< Completed successfully - TRANSPORT_IO_EOF, ///< Connection shut down by peer - TRANSPORT_IO_ERROR ///< Connection error -}; - // The only real purpose of this is to abstract away TLS struct transport { @@ -1155,9 +1148,9 @@ struct transport void (*cleanup) (struct server *s); /// The underlying socket may have become readable, update `read_buffer' - enum transport_io_result (*try_read) (struct server *s); + enum socket_io_result (*try_read) (struct server *s); /// The underlying socket may have become writeable, flush `write_buffer' - enum transport_io_result (*try_write) (struct server *s); + enum socket_io_result (*try_write) (struct server *s); /// Return event mask to use in the poller int (*get_poll_events) (struct server *s); @@ -4219,27 +4212,27 @@ irc_process_buffer_custom (struct server *s, struct str *buf) str_remove_slice (buf, 0, start - buf->str); } -static enum transport_io_result +static enum socket_io_result irc_try_read (struct server *s) { - enum transport_io_result result = s->transport->try_read (s); + enum socket_io_result result = s->transport->try_read (s); if (s->read_buffer.len >= (1 << 20)) { // XXX: this is stupid; if anything, count it in dependence of time log_server_error (s, s->buffer, "The IRC server seems to spew out data frantically"); - return TRANSPORT_IO_ERROR; + return SOCKET_IO_ERROR; } if (s->read_buffer.len) irc_process_buffer_custom (s, &s->read_buffer); return result; } -static enum transport_io_result +static enum socket_io_result irc_try_write (struct server *s) { - enum transport_io_result result = s->transport->try_write (s); - if (result == TRANSPORT_IO_OK) + enum socket_io_result result = s->transport->try_write (s); + if (result == SOCKET_IO_OK) { // If we're flushing the write buffer and our job is complete, we send // an EOF to the server, changing the state to IRC_HALF_CLOSED @@ -4252,10 +4245,10 @@ irc_try_write (struct server *s) static bool irc_try_read_write (struct server *s) { - enum transport_io_result read_result; - enum transport_io_result write_result; - if ((read_result = irc_try_read (s)) == TRANSPORT_IO_ERROR - || (write_result = irc_try_write (s)) == TRANSPORT_IO_ERROR) + enum socket_io_result read_result; + enum socket_io_result write_result; + if ((read_result = irc_try_read (s)) == SOCKET_IO_ERROR + || (write_result = irc_try_write (s)) == SOCKET_IO_ERROR) { log_server_error (s, s->buffer, "Server connection failed"); return false; @@ -4263,15 +4256,15 @@ irc_try_read_write (struct server *s) // FIXME: this may probably fire multiple times when we're flushing, // we should probably store a flag next to the state - if (read_result == TRANSPORT_IO_EOF - || write_result == TRANSPORT_IO_EOF) + if (read_result == SOCKET_IO_EOF + || write_result == SOCKET_IO_EOF) log_server_error (s, s->buffer, "Server closed the connection"); // If the write needs to read and we receive an EOF, we can't flush - if (write_result == TRANSPORT_IO_EOF) + if (write_result == SOCKET_IO_EOF) return false; - if (read_result == TRANSPORT_IO_EOF) + if (read_result == SOCKET_IO_EOF) { // Eventually initiate shutdown to flush the write buffer irc_shutdown (s); @@ -4299,60 +4292,32 @@ on_irc_ready (const struct pollfd *pfd, struct server *s) // --- Plain transport --------------------------------------------------------- -static enum transport_io_result +static enum socket_io_result transport_plain_try_read (struct server *s) { - struct str *buf = &s->read_buffer; - ssize_t n_read; - - while (true) + struct error *e = NULL; + enum socket_io_result result = + socket_io_try_read (s->socket, &s->read_buffer, &e); + if (e) { - str_ensure_space (buf, 512); - n_read = recv (s->socket, buf->str + buf->len, - buf->alloc - buf->len - 1 /* null byte */, 0); - - if (n_read > 0) - { - buf->str[buf->len += n_read] = '\0'; - continue; - } - if (n_read == 0) - return TRANSPORT_IO_EOF; - - if (errno == EAGAIN) - return TRANSPORT_IO_OK; - if (errno == EINTR) - continue; - - LOG_LIBC_FAILURE ("recv"); - return TRANSPORT_IO_ERROR; + print_debug ("%s: %s", __func__, e->message); + error_free (e); } + return result; } -static enum transport_io_result +static enum socket_io_result transport_plain_try_write (struct server *s) { - struct str *buf = &s->write_buffer; - ssize_t n_written; - - while (buf->len) + struct error *e = NULL; + enum socket_io_result result = + socket_io_try_write (s->socket, &s->write_buffer, &e); + if (e) { - n_written = send (s->socket, buf->str, buf->len, 0); - if (n_written >= 0) - { - str_remove_slice (buf, 0, n_written); - continue; - } - - if (errno == EAGAIN) - return TRANSPORT_IO_OK; - if (errno == EINTR) - continue; - - LOG_LIBC_FAILURE ("send"); - return TRANSPORT_IO_ERROR; + print_debug ("%s: %s", __func__, e->message); + error_free (e); } - return TRANSPORT_IO_OK; + return result; } static int @@ -4564,12 +4529,12 @@ transport_tls_cleanup (struct server *s) free (data); } -static enum transport_io_result +static enum socket_io_result transport_tls_try_read (struct server *s) { struct transport_tls_data *data = s->transport_data; if (data->ssl_tx_want_rx) - return TRANSPORT_IO_OK; + return SOCKET_IO_OK; struct str *buf = &s->read_buffer; data->ssl_rx_want_tx = false; @@ -4587,27 +4552,27 @@ transport_tls_try_read (struct server *s) buf->str[buf->len += n_read] = '\0'; continue; case SSL_ERROR_ZERO_RETURN: - return TRANSPORT_IO_EOF; + return SOCKET_IO_EOF; case SSL_ERROR_WANT_READ: - return TRANSPORT_IO_OK; + return SOCKET_IO_OK; case SSL_ERROR_WANT_WRITE: data->ssl_rx_want_tx = true; - return TRANSPORT_IO_OK; + return SOCKET_IO_OK; case XSSL_ERROR_TRY_AGAIN: continue; default: LOG_FUNC_FAILURE ("SSL_read", error_info); - return TRANSPORT_IO_ERROR; + return SOCKET_IO_ERROR; } } } -static enum transport_io_result +static enum socket_io_result transport_tls_try_write (struct server *s) { struct transport_tls_data *data = s->transport_data; if (data->ssl_rx_want_tx) - return TRANSPORT_IO_OK; + return SOCKET_IO_OK; struct str *buf = &s->write_buffer; data->ssl_tx_want_rx = false; @@ -4623,20 +4588,20 @@ transport_tls_try_write (struct server *s) str_remove_slice (buf, 0, n_written); continue; case SSL_ERROR_ZERO_RETURN: - return TRANSPORT_IO_EOF; + return SOCKET_IO_EOF; case SSL_ERROR_WANT_WRITE: - return TRANSPORT_IO_OK; + return SOCKET_IO_OK; case SSL_ERROR_WANT_READ: data->ssl_tx_want_rx = true; - return TRANSPORT_IO_OK; + return SOCKET_IO_OK; case XSSL_ERROR_TRY_AGAIN: continue; default: LOG_FUNC_FAILURE ("SSL_write", error_info); - return TRANSPORT_IO_ERROR; + return SOCKET_IO_ERROR; } } - return TRANSPORT_IO_OK; + return SOCKET_IO_OK; } static int