aboutsummaryrefslogtreecommitdiff
path: root/networks/openssl_buffered.c
diff options
context:
space:
mode:
Diffstat (limited to 'networks/openssl_buffered.c')
-rw-r--r--networks/openssl_buffered.c600
1 files changed, 600 insertions, 0 deletions
diff --git a/networks/openssl_buffered.c b/networks/openssl_buffered.c
new file mode 100644
index 0000000..f929145
--- /dev/null
+++ b/networks/openssl_buffered.c
@@ -0,0 +1,600 @@
+// One of the code files for HaxServ
+//
+// Written by: Test_User <hax@andrewyu.org>
+//
+// This is free and unencumbered software released into the public
+// domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+//
+// In jurisdictions that recognize copyright laws, the author or authors
+// of this software dedicate any and all copyright interest in the
+// software to the public domain. We make this dedication for the benefit
+// of the public at large and to the detriment of our heirs and
+// successors. We intend this dedication to be an overt act of
+// relinquishment in perpetuity of all present and future rights to this
+// software under copyright law.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <openssl/ssl.h>
+#include <openssl/tls1.h>
+#include <poll.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#ifdef USE_FUTEX
+#else
+#include <semaphore.h>
+#endif
+
+#include "../config.h"
+#include "../main.h"
+#include "../mutex.h"
+#include "openssl_buffered.h"
+
+struct openssl_buffered_handle {
+ SSL *ssl;
+ MUTEX_TYPE mutex;
+ int fd;
+ char valid;
+ char close;
+#ifdef USE_FUTEX
+ MUTEX_TYPE release_read;
+ MUTEX_TYPE release_write;
+#else
+ sem_t release_read;
+ sem_t release_write;
+#endif
+ char *buffer;
+ size_t write_buffer_index;
+ size_t buffer_len;
+};
+
+SSL_CTX *openssl_buffered_ctx;
+
+int init_openssl_buffered_network(void) {
+ SSL_library_init();
+
+ openssl_buffered_ctx = SSL_CTX_new(TLS_method());
+ if (OPENSSL_CERT_PATH && OPENSSL_KEY_PATH) {
+ if (SSL_CTX_use_certificate_file(openssl_buffered_ctx, OPENSSL_CERT_PATH, SSL_FILETYPE_PEM) != 1)
+ return 1;
+ if (SSL_CTX_use_PrivateKey_file(openssl_buffered_ctx, OPENSSL_KEY_PATH, SSL_FILETYPE_PEM) != 1)
+ return 1;
+ }
+
+ if (OPENSSL_USE_SYSTEM_TRUST) {
+ if (SSL_CTX_set_default_verify_paths(openssl_buffered_ctx) != 1) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void * openssl_buffered_send_thread(void *handle) {
+ struct openssl_buffered_handle *info = handle;
+
+ size_t read_buffer_index = 0;
+ mutex_lock(&(info->mutex));
+ while (1) {
+ if (!info->valid)
+ goto openssl_buffered_send_thread_error_unlock;
+
+ size_t len;
+ len = info->buffer_len;
+
+#ifdef USE_FUTEX
+ mutex_unlock(&(info->release_write));
+ if (len == 0)
+ info->release_read = 1;
+#else
+ sem_trywait(&(info->release_write));
+ sem_post(&(info->release_write));
+#endif
+
+ mutex_unlock(&(info->mutex));
+
+#ifdef USE_FUTEX
+ if (len == 0) {
+ mutex_lock(&(info->release_read));
+ mutex_lock(&(info->mutex));
+ continue;
+ }
+#else
+ if (len == 0) {
+ sem_wait(&(info->release_read));
+ mutex_lock(&(info->mutex));
+ continue;
+ }
+#endif
+
+ if (read_buffer_index + len > OPENSSL_BUFFERED_LEN)
+ len = OPENSSL_BUFFERED_LEN - read_buffer_index;
+
+ struct pollfd pollfd = {
+ .fd = info->fd,
+ };
+ int res;
+ do {
+ mutex_lock(&(info->mutex));
+ res = SSL_write(info->ssl, &(info->buffer[read_buffer_index]), len);
+ if (res <= 0) {
+ switch(SSL_get_error(info->ssl, res)) {
+ case SSL_ERROR_WANT_READ:
+ pollfd.events = POLLIN;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ pollfd.events = POLLOUT;
+ break;
+ default:
+ goto openssl_buffered_send_thread_error_unlock;
+ }
+ } else {
+ break;
+ }
+ mutex_unlock(&(info->mutex));
+
+ do {
+ res = poll(&pollfd, 1, PING_INTERVAL*1000);
+ } while (res < 0 && errno == EINTR);
+ if (res < 0)
+ goto openssl_buffered_send_thread_error;
+ if (res == 0) // Timed out... maybe handle differently later
+ goto openssl_buffered_send_thread_error;
+ if ((pollfd.revents & (POLLIN | POLLOUT)) == 0) // Only errors returned
+ goto openssl_buffered_send_thread_error;
+ } while (1);
+
+ read_buffer_index += len;
+ if (read_buffer_index >= OPENSSL_BUFFERED_LEN)
+ read_buffer_index = 0;
+
+ info->buffer_len -= len;
+ }
+
+ openssl_buffered_send_thread_error:
+ mutex_lock(&(info->mutex));
+ openssl_buffered_send_thread_error_unlock:
+ info->valid = 0;
+ mutex_unlock(&(info->mutex));
+
+#ifdef USE_FUTEX
+ mutex_unlock(&(info->release_write));
+#else
+ sem_trywait(&(info->release_write));
+ sem_post(&(info->release_write));
+#endif
+ while (1) {
+ mutex_lock(&(info->mutex));
+ if (info->close) {
+ mutex_unlock(&(info->mutex));
+ mutex_destroy(&(info->mutex));
+ SSL_free(info->ssl);
+ close(info->fd);
+ free(info->buffer);
+#ifdef USE_FUTEX
+#else
+ sem_destroy(&(info->release_read));
+ sem_destroy(&(info->release_write));
+#endif
+ free(info);
+ return 0;
+ } else {
+#ifdef USE_FUTEX
+ info->release_read = 1;
+ mutex_unlock(&(info->mutex));
+ mutex_lock(&(info->release_read));
+ continue;
+#else
+ mutex_unlock(&(info->mutex));
+ sem_wait(&(info->release_read));
+ continue;
+#endif
+ }
+ mutex_unlock(&(info->mutex));
+ }
+}
+
+int openssl_buffered_send(void *handle, struct string msg) {
+ struct openssl_buffered_handle *openssl_handle = handle;
+ while (msg.len > 0) {
+ size_t len = msg.len;
+ if (len > OPENSSL_BUFFERED_LEN - openssl_handle->write_buffer_index)
+ len = OPENSSL_BUFFERED_LEN - openssl_handle->write_buffer_index;
+ mutex_lock(&(openssl_handle->mutex));
+ if (!openssl_handle->valid) {
+ mutex_unlock(&(openssl_handle->mutex));
+ return 1;
+ }
+ if (len > OPENSSL_BUFFERED_LEN - openssl_handle->buffer_len)
+ len = OPENSSL_BUFFERED_LEN - openssl_handle->buffer_len;
+#ifdef USE_FUTEX
+ if (len == 0) {
+ openssl_handle->release_write = 1;
+ mutex_unlock(&(openssl_handle->mutex));
+ mutex_lock(&(openssl_handle->release_write));
+ continue;
+ }
+#else
+ if (len == 0) {
+ mutex_unlock(&(openssl_hndle->mutex));
+ sem_wait(&(openssl_handle->release_write));
+ continue;
+ }
+#endif
+ memcpy(&(openssl_handle->buffer[openssl_handle->write_buffer_index]), msg.data, len);
+ openssl_handle->buffer_len += len;
+#ifdef USE_FUTEX
+ mutex_unlock(&(openssl_handle->release_read));
+#else
+ sem_trywait(&(openssl_handle->release_read));
+ sem_post(&(openssl_handle->release_read));
+#endif
+ mutex_unlock(&(openssl_handle->mutex));
+ openssl_handle->write_buffer_index += len;
+ if (openssl_handle->write_buffer_index >= OPENSSL_BUFFERED_LEN)
+ openssl_handle->write_buffer_index = 0;
+ msg.len -= len;
+ msg.data += len;
+ }
+
+ return 0;
+}
+
+size_t openssl_buffered_recv(void *handle, char *data, size_t len, char *err) {
+ struct openssl_buffered_handle *openssl_handle = handle;
+
+ struct pollfd pollfd = {
+ .fd = openssl_handle->fd,
+ };
+ int res;
+ do {
+ mutex_lock(&(openssl_handle->mutex));
+ if (!openssl_handle->valid) {
+ mutex_unlock(&(openssl_handle->mutex));
+ *err = 3;
+ return 0;
+ }
+ res = SSL_read(openssl_handle->ssl, data, len);
+ mutex_unlock(&(openssl_handle->mutex));
+ if (res <= 0) {
+ switch(SSL_get_error(openssl_handle->ssl, res)) {
+ case SSL_ERROR_WANT_READ:
+ pollfd.events = POLLIN;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ pollfd.events = POLLOUT;
+ break;
+ default:
+ *err = 3;
+ return 0;
+ }
+ } else {
+ break;
+ }
+
+ res = poll(&pollfd, 1, PING_INTERVAL*1000);
+ if (res == 0) { // Timeout
+ *err = 1;
+ return 0;
+ }
+ if (res == -1) {
+ if (errno != EINTR) {
+ continue;
+ } else {
+ *err = 3;
+ return 0;
+ }
+ }
+ if ((pollfd.revents & (POLLIN | POLLOUT)) == 0) { // Only errors returned
+ if (pollfd.revents & POLLHUP) {
+ *err = 2;
+ } else {
+ *err = 3;
+ }
+
+ return 0;
+ }
+ } while (1);
+
+ *err = 0;
+ return (size_t)res;
+}
+
+int openssl_buffered_connect(void **handle, struct string address, struct string port, struct string *addr_out) {
+ struct sockaddr sockaddr;
+ if (resolve(address, port, &sockaddr) != 0)
+ return -1;
+
+ int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd == -1)
+ return -1;
+
+ {
+ struct timeval timeout = {
+ .tv_sec = PING_INTERVAL,
+ .tv_usec = 0,
+ };
+
+ setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
+ }
+
+ int res;
+ do {
+ res = connect(fd, &sockaddr, sizeof(sockaddr));
+ } while (res < 0 && errno == EINTR);
+ if (res < 0)
+ goto openssl_connect_close;
+
+ int flags = fcntl(fd, F_GETFL);
+ if (flags == -1)
+ goto openssl_connect_close;
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ goto openssl_connect_close;
+
+ addr_out->data = malloc(sizeof(sockaddr));
+ if (!addr_out->data)
+ goto openssl_connect_close;
+ memcpy(addr_out->data, &sockaddr, sizeof(sockaddr));
+ addr_out->len = sizeof(sockaddr);
+
+ struct openssl_buffered_handle *openssl_handle;
+ openssl_handle = malloc(sizeof(*openssl_handle));
+ if (!openssl_handle)
+ goto openssl_connect_free_addr_data;
+ *handle = openssl_handle;
+
+ openssl_handle->fd = fd;
+
+ openssl_handle->ssl = SSL_new(openssl_buffered_ctx);
+ if (!openssl_handle->ssl)
+ goto openssl_connect_free_openssl_handle;
+ SSL_set_fd(openssl_handle->ssl, fd);
+
+ res = mutex_init(&(openssl_handle->mutex));
+ if (res != 0)
+ goto openssl_connect_free_ssl;
+
+ struct pollfd pollfd = {
+ .fd = fd,
+ };
+ do {
+ res = SSL_connect(openssl_handle->ssl);
+ if (res == 0)
+ goto openssl_connect_destroy_mutex;
+ if (res < 0) {
+ switch(SSL_get_error(openssl_handle->ssl, res)) {
+ case SSL_ERROR_WANT_READ:
+ pollfd.events = POLLIN;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ pollfd.events = POLLOUT;
+ break;
+ default:
+ goto openssl_connect_destroy_mutex;
+ }
+ } else {
+ break;
+ }
+
+ res = poll(&pollfd, 1, PING_INTERVAL*1000);
+ if (res == 0) // Timeout
+ goto openssl_connect_destroy_mutex;
+ if (res == -1) {
+ if (errno != EINTR) {
+ continue;
+ } else {
+ goto openssl_connect_destroy_mutex;
+ }
+ }
+ if ((pollfd.revents & (POLLIN | POLLOUT)) == 0) // Only errors returned
+ goto openssl_connect_destroy_mutex;
+ } while (1);
+
+ openssl_handle->valid = 1;
+ openssl_handle->close = 0;
+ openssl_handle->write_buffer_index = 0;
+ openssl_handle->buffer_len = 0;
+
+ openssl_handle->buffer = malloc(OPENSSL_BUFFERED_LEN);
+ if (!openssl_handle->buffer)
+ goto openssl_connect_destroy_mutex;
+
+#ifdef USE_FUTEX
+ openssl_handle->release_read = 0;
+ openssl_handle->release_write = 0;
+#else
+ sem_init(&(openssl_handle->release_read), 0, 0);
+ sem_init(&(openssl_handle->release_write), 0, 0);
+#endif
+
+ pthread_t trash;
+ if (pthread_create(&trash, &pthread_attr, openssl_buffered_send_thread, openssl_handle) != 0)
+ goto openssl_connect_destroy_releases;
+
+ return fd;
+
+ openssl_connect_destroy_releases:
+#ifdef USE_FUTEX
+#else
+ sem_destroy(&(openssl_handle->release_read));
+ sem_destroy(&(openssl_handle->release_write));
+#endif
+ openssl_connect_destroy_mutex:
+ mutex_destroy(&(openssl_handle->mutex));
+ openssl_connect_free_ssl:
+ SSL_free(openssl_handle->ssl);
+ openssl_connect_free_openssl_handle:
+ free(openssl_handle);
+ openssl_connect_free_addr_data:
+ free(addr_out->data);
+ openssl_connect_close:
+ close(fd);
+
+ return -1;
+}
+
+int openssl_buffered_accept(int listen_fd, void **handle, struct string *addr) {
+ if (!OPENSSL_CERT_PATH || !OPENSSL_KEY_PATH)
+ return -1;
+
+ struct sockaddr address;
+ socklen_t address_len = sizeof(address);
+
+ int con_fd;
+ do {
+ con_fd = accept(listen_fd, &address, &address_len);
+ } while (con_fd == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK || errno == ENETDOWN || errno == EPROTO || errno == ENOPROTOOPT || errno == EHOSTDOWN || errno == ENONET || errno == EHOSTUNREACH || errno == EOPNOTSUPP || errno == ENETUNREACH));
+
+ if (con_fd == -1)
+ return -1;
+
+ int flags = fcntl(con_fd, F_GETFL);
+ if (flags == -1)
+ goto openssl_accept_close;
+ if (fcntl(con_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ goto openssl_accept_close;
+
+ addr->data = malloc(address_len);
+ if (addr->data == 0 && address_len != 0)
+ goto openssl_accept_close;
+
+ memcpy(addr->data, &address, address_len);
+ addr->len = address_len;
+
+ struct openssl_buffered_handle *openssl_handle;
+ openssl_handle = malloc(sizeof(*openssl_handle));
+ if (!openssl_handle)
+ goto openssl_accept_free_addr_data;
+
+ *handle = openssl_handle;
+
+ openssl_handle->fd = con_fd;
+
+ openssl_handle->ssl = SSL_new(openssl_buffered_ctx);
+ if (!openssl_handle->ssl)
+ goto openssl_accept_free_openssl_handle;
+
+ SSL_set_fd(openssl_handle->ssl, con_fd);
+
+ int res = mutex_init(&(openssl_handle->mutex));
+ if (res != 0)
+ goto openssl_accept_free_ssl;
+
+ struct pollfd pollfd = {
+ .fd = con_fd,
+ };
+ do {
+ res = SSL_accept(openssl_handle->ssl);
+ if (res == 0)
+ goto openssl_accept_destroy_mutex;
+ if (res < 0) {
+ switch(SSL_get_error(openssl_handle->ssl, res)) {
+ case SSL_ERROR_WANT_READ:
+ pollfd.events = POLLIN;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ pollfd.events = POLLOUT;
+ break;
+ default:
+ goto openssl_accept_destroy_mutex;
+ }
+ } else {
+ break;
+ }
+
+ res = poll(&pollfd, 1, PING_INTERVAL*1000);
+ if (res == 0) // Timeout
+ goto openssl_accept_destroy_mutex;
+ if (res == -1) {
+ if (errno != EINTR) {
+ continue;
+ } else {
+ goto openssl_accept_destroy_mutex;
+ }
+ }
+ if ((pollfd.revents & (POLLIN | POLLOUT)) == 0) // Only errors returned
+ goto openssl_accept_destroy_mutex;
+ } while (1);
+
+ openssl_handle->valid = 1;
+
+ openssl_handle->buffer = malloc(OPENSSL_BUFFERED_LEN);
+ if (!openssl_handle->buffer)
+ goto openssl_accept_destroy_mutex;
+
+#ifdef USE_FUTEX
+ openssl_handle->release_read = 0;
+ openssl_handle->release_write = 0;
+#else
+ sem_init(&(openssl_handle->release_read), 0, 0);
+ sem_init(&(openssl_handle->release_write), 0, 0);
+#endif
+
+ pthread_t trash;
+ if (pthread_create(&trash, &pthread_attr, openssl_buffered_send_thread, openssl_handle) != 0)
+ goto openssl_accept_destroy_releases;
+
+ return con_fd;
+
+ openssl_accept_destroy_releases:
+#ifdef USE_FUTEX
+#else
+ sem_destroy(&(openssl_handle->release_read));
+ sem_destroy(&(openssl_handle->release_write));
+#endif
+ openssl_accept_destroy_mutex:
+ mutex_destroy(&(openssl_handle->mutex));
+ openssl_accept_free_ssl:
+ SSL_free(openssl_handle->ssl);
+ openssl_accept_free_openssl_handle:
+ free(openssl_handle);
+ openssl_accept_free_addr_data:
+ free(addr->data);
+ openssl_accept_close:
+ close(con_fd);
+
+ return -1;
+}
+
+void openssl_buffered_shutdown(void *handle) {
+ struct openssl_buffered_handle *openssl_handle = handle;
+ mutex_lock(&(openssl_handle->mutex));
+ openssl_handle->valid = 0;
+#ifdef USE_FUTEX
+ mutex_unlock(&(openssl_handle->release_read));
+#else
+ sem_trywait(&(openssl_handle->release_read));
+ sem_post(&(openssl_handle->release_read));
+#endif
+ mutex_unlock(&(openssl_handle->mutex));
+ shutdown(openssl_handle->fd, SHUT_RDWR);
+}
+
+void openssl_buffered_close(int fd, void *handle) {
+ struct openssl_buffered_handle *openssl_handle = handle;
+ mutex_lock(&(openssl_handle->mutex));
+ openssl_handle->valid = 0;
+#ifdef USE_FUTEX
+ mutex_unlock(&(openssl_handle->release_read));
+#else
+ sem_trywait(&(openssl_handle->release_read));
+ sem_post(&(openssl_handle->release_read));
+#endif
+ openssl_handle->close = 1;
+ mutex_unlock(&(openssl_handle->mutex));
+}