+ if (size_written < 0) {
+ LOG_PERROR(WARNING, "read_from_write_to(): write()");
+ return -1;
+ }
+ if (size_read != size_written) {
+ LOG(ERROR, "read_from_write_to(): only written %zu of %zu bytes!",
+ size_written, size_read);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Transfer data between client and server TLS connection until one closes the
+ * connection. */
+static void transfer_data_tls(int client, int server,
+ gnutls_session_t client_session,
+ gnutls_session_t server_session) {
+ size_t buffer_size;
+
+ struct pollfd fds[2];
+ fds[0].fd = client;
+ fds[0].events = POLLIN | POLLPRI | POLLHUP | POLLERR;
+ fds[0].revents = 0;
+ fds[1].fd = server;
+ fds[1].events = POLLIN | POLLPRI | POLLHUP | POLLERR;
+ fds[1].revents = 0;
+
+ /* Get maximum possible buffer size. */
+ buffer_size = gnutls_record_get_max_size(client_session);
+ if (gnutls_record_get_max_size(server_session) < buffer_size) {
+ buffer_size = gnutls_record_get_max_size(server_session);
+ }
+ LOG(DEBUG2, "transfer_data_tls(): suggested buffer size: %zu",
+ buffer_size);
+
+ for (;;) {
+ int result = poll(fds, 2 /* fd count */, -1 /* no timeout */);
+ if (result < 0) {
+ LOG_PERROR(ERROR, "transfer_data(): poll()");
+ return;
+ }
+
+ /* Data available from client. */
+ if (fds[0].revents & POLLIN || fds[0].revents & POLLPRI) {
+ if (read_from_write_to_tls(client_session, server_session,
+ buffer_size) != 0) {
+ /* EOF (or other error) */
+ break;
+ }
+ }
+ /* Data available from server. */
+ if (fds[1].revents & POLLIN || fds[1].revents & POLLPRI) {
+ if (read_from_write_to_tls(server_session, client_session,
+ buffer_size) != 0) {
+ /* EOF (or other error) */
+ break;
+ }
+ }
+
+ /* Client closed connection. */
+ if (fds[0].revents & POLLERR || fds[0].revents & POLLHUP) {
+ break;
+ }
+ /* Server closed connection. */
+ if (fds[1].revents & POLLERR || fds[1].revents & POLLHUP) {
+ break;
+ }
+ }
+}
+
+/* Read available data from session from and write to session to. */
+static int read_from_write_to_tls(gnutls_session_t from,
+ gnutls_session_t to,
+ size_t buffer_size) {
+ ssize_t size_read;
+ ssize_t size_written;
+ char buffer[16384]; /* GnuTLS default maximum */
+
+ if (buffer_size > sizeof(buffer)) {
+ LOG(WARNING, "read_from_write_to_tls(): reduced buffer size to %zu",
+ sizeof(buffer));
+ buffer_size = sizeof(buffer);
+ }
+
+ size_read = gnutls_record_recv(from, buffer, buffer_size);
+ if (size_read < 0) {
+ LOG(WARNING, "read_from_write_to_tls(): gnutls_record_recv(): %s",
+ gnutls_strerror((int)size_read));
+ return -1;
+ /* EOF */
+ } else if (size_read == 0) {
+ return -1;
+ }
+
+ size_written = gnutls_record_send(to, buffer, (size_t)size_read);
+ if (size_written < 0) {
+ LOG(WARNING, "read_from_write_to_tls(): gnutls_record_send(): %s",
+ gnutls_strerror((int)size_written));