diff options
Diffstat (limited to 'networks')
-rw-r--r-- | networks/gnutls_buffered.c | 436 | ||||
-rw-r--r-- | networks/gnutls_buffered.h | 45 | ||||
-rw-r--r-- | networks/openssl_buffered.c | 600 | ||||
-rw-r--r-- | networks/openssl_buffered.h | 45 | ||||
-rw-r--r-- | networks/plaintext_buffered.c | 27 |
5 files changed, 1140 insertions, 13 deletions
diff --git a/networks/gnutls_buffered.c b/networks/gnutls_buffered.c new file mode 100644 index 0000000..28a50ca --- /dev/null +++ b/networks/gnutls_buffered.c @@ -0,0 +1,436 @@ +// 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 <gnutls/gnutls.h> +#include <poll.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> + +#include "../config.h" +#include "../general_network.h" +#include "../main.h" +#include "../mutex.h" +#include "gnutls_buffered.h" + +struct gnutls_buffered_handle { + gnutls_session_t session; + MUTEX_TYPE mutex; + int fd; + char valid; +}; + +gnutls_certificate_credentials_t gnutls_buffered_cert_creds; + +int init_gnutls_buffered_network(void) { + if (gnutls_global_init() < 0) + return 1; + + if (gnutls_certificate_allocate_credentials(&gnutls_buffered_cert_creds) != GNUTLS_E_SUCCESS) + return 2; + + if (GNUTLS_USE_SYSTEM_TRUST && (gnutls_certificate_set_x509_system_trust(gnutls_buffered_cert_creds) < 0)) + return 3; + + if (GNUTLS_KEY_PATH && GNUTLS_CERT_PATH && gnutls_certificate_set_x509_key_file(gnutls_buffered_cert_creds, GNUTLS_CERT_PATH, GNUTLS_KEY_PATH, GNUTLS_X509_FMT_PEM) < 0) + return 4; + + return 0; +} + +int gnutls_buffered_send(void *handle, struct string msg) { + if (msg.len == 0) + return 0; + + struct gnutls_buffered_handle *gnutls_handle = handle; + + mutex_lock(&(gnutls_handle->mutex)); + + if (!gnutls_handle->valid) + goto gnutls_send_error_unlock; + + struct pollfd pollfd = { + .fd = gnutls_handle->fd, + }; + do { + ssize_t gnutls_res; + int poll_res; + gnutls_res = gnutls_record_send(gnutls_handle->session, msg.data, msg.len); + if (gnutls_res <= 0) { + if (gnutls_res == GNUTLS_E_INTERRUPTED) { + continue; + } else if (gnutls_res == GNUTLS_E_AGAIN) { + pollfd.events = POLLIN | POLLOUT; + do { + poll_res = poll(&pollfd, 1, 0); + } while (poll_res < 0 && errno == EINTR); + if (poll_res < 0) + goto gnutls_send_error_unlock; + + if ((pollfd.revents & (POLLIN | POLLOUT)) == (POLLIN | POLLOUT)) + continue; + else if (pollfd.revents & (~(POLLIN | POLLOUT))) + goto gnutls_send_error_unlock; + else + pollfd.events = (pollfd.revents & (POLLIN | POLLOUT)) ^ (POLLIN | POLLOUT); + } else { + goto gnutls_send_error_unlock; + } + } else { + break; + } + + do { + poll_res = poll(&pollfd, 1, PING_INTERVAL*1000); + } while (poll_res < 0 && errno == EINTR); + if (poll_res < 0) + goto gnutls_send_error_unlock; + if (poll_res == 0) // Timed out + goto gnutls_send_error_unlock; + if ((pollfd.revents & (POLLIN | POLLOUT)) == 0) + goto gnutls_send_error_unlock; + } while (1); + + mutex_unlock(&(gnutls_handle->mutex)); + return 0; + + gnutls_send_error_unlock: + if (gnutls_handle->valid) { + mutex_unlock(&(gnutls_handle->mutex)); + gnutls_buffered_shutdown(gnutls_handle); + } else { + mutex_unlock(&(gnutls_handle->mutex)); + } + return 1; +} + +size_t gnutls_buffered_recv(void *handle, char *data, size_t len, char *err) { + struct gnutls_buffered_handle *gnutls_handle = handle; + + struct pollfd pollfd = { + .fd = gnutls_handle->fd, + }; + ssize_t gnutls_res; + do { + int poll_res; + mutex_lock(&(gnutls_handle->mutex)); + if (!gnutls_handle->valid) { + mutex_unlock(&(gnutls_handle->mutex)); + *err = 3; + return 0; + } + do { + gnutls_res = gnutls_record_recv(gnutls_handle->session, data, len); + } while (gnutls_res == GNUTLS_E_INTERRUPTED); + mutex_unlock(&(gnutls_handle->mutex)); + if (gnutls_res < 0) { + if (gnutls_res == GNUTLS_E_AGAIN) { + pollfd.events = POLLIN | POLLOUT; + do { + poll_res = poll(&pollfd, 1, 0); + } while (poll_res < 0 && errno == EINTR); + if (poll_res < 0) { + *err = 3; + return 0; + } + + if ((pollfd.revents & (POLLIN | POLLOUT)) == (POLLIN | POLLOUT)) + continue; + else + pollfd.events = (pollfd.revents & (POLLIN | POLLOUT)) ^ (POLLIN | POLLOUT); + } else { + *err = 3; + return 0; + } + } else if (gnutls_res == 0) { + *err = 2; + return 0; + } else { + break; + } + + do { + poll_res = poll(&pollfd, 1, PING_INTERVAL*1000); + } while (poll_res < 0 && errno == EINTR); + if (poll_res < 0) { + *err = 3; + return 0; + } if (poll_res == 0) { // Timed out + *err = 1; + return 0; + } if ((pollfd.revents & (POLLIN | POLLOUT)) == 0) { + *err = 3; + return 0; + } + } while (1); + + *err = 0; + + return (size_t)gnutls_res; +} + +int gnutls_buffered_connect(void **handle, struct string address, struct string port, struct string *addr_out) { + struct gnutls_buffered_handle *gnutls_handle; + gnutls_handle = malloc(sizeof(*gnutls_handle)); + if (!gnutls_handle) + return -1; + + *handle = gnutls_handle; + + int res = mutex_init(&(gnutls_handle->mutex)); + if (res != 0) + goto gnutls_connect_free_gnutls_handle; + + struct sockaddr sockaddr; + if (resolve(address, port, &sockaddr) != 0) + goto gnutls_connect_destroy_mutex; + + int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd == -1) + goto gnutls_connect_destroy_mutex; + + gnutls_handle->fd = fd; + gnutls_handle->valid = 1; + + do { + res = connect(fd, &sockaddr, sizeof(sockaddr)); + } while (res < 0 && errno == EINTR); + if (res < 0) + goto gnutls_connect_close; + + int flags = fcntl(fd, F_GETFL); + if (flags == -1) + goto gnutls_connect_close; + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) + goto gnutls_connect_close; + + if (gnutls_init(&(gnutls_handle->session), GNUTLS_CLIENT | GNUTLS_NONBLOCK) != GNUTLS_E_SUCCESS) + goto gnutls_connect_close; + + if (gnutls_server_name_set(gnutls_handle->session, GNUTLS_NAME_DNS, address.data, address.len) != GNUTLS_E_SUCCESS) + goto gnutls_connect_deinit_session; + + if (gnutls_credentials_set(gnutls_handle->session, GNUTLS_CRD_CERTIFICATE, gnutls_buffered_cert_creds) != GNUTLS_E_SUCCESS) + goto gnutls_connect_deinit_session; + + if (gnutls_set_default_priority(gnutls_handle->session) != GNUTLS_E_SUCCESS) + goto gnutls_connect_deinit_session; + + gnutls_transport_set_int(gnutls_handle->session, fd); + + struct pollfd pollfd = { + .fd = fd, + }; + ssize_t gnutls_res; + do { + int poll_res; + do { + gnutls_res = gnutls_handshake(gnutls_handle->session); + } while (res == GNUTLS_E_INTERRUPTED); + if (gnutls_res < 0) { + if (gnutls_res == GNUTLS_E_AGAIN) { + pollfd.events = POLLIN | POLLOUT; + do { + poll_res = poll(&pollfd, 1, 0); + } while (poll_res < 0 && errno == EINTR); + if (poll_res < 0) + goto gnutls_connect_deinit_session; + + if ((pollfd.revents & (POLLIN | POLLOUT)) == (POLLIN | POLLOUT)) + continue; + else if (pollfd.revents & (~(POLLIN | POLLOUT))) + goto gnutls_connect_deinit_session; + else + pollfd.events = pollfd.revents ^ (POLLIN | POLLOUT); + } + } else { + break; + } + + do { + poll_res = poll(&pollfd, 1, PING_INTERVAL*1000); + } while (poll_res < 0 && errno == EINTR); + if (poll_res < 0) + goto gnutls_connect_deinit_session; + if (poll_res == 0) // Timed out + goto gnutls_connect_deinit_session; + if ((pollfd.revents & (POLLIN | POLLOUT)) == 0) + goto gnutls_connect_deinit_session; + } while (1); + + addr_out->data = malloc(sizeof(sockaddr)); + if (!addr_out->data) + goto gnutls_connect_deinit_session; + + memcpy(addr_out->data, &sockaddr, sizeof(sockaddr)); + addr_out->len = sizeof(sockaddr); + + return fd; + + gnutls_connect_deinit_session: + gnutls_deinit(gnutls_handle->session); + gnutls_connect_close: + close(fd); + gnutls_connect_destroy_mutex: + mutex_destroy(&(gnutls_handle->mutex)); + gnutls_connect_free_gnutls_handle: + free(gnutls_handle); + + return -1; +} + +int gnutls_buffered_accept(int listen_fd, void **handle, struct string *addr) { + if (!GNUTLS_CERT_PATH || !GNUTLS_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 gnutls_accept_close; + if (fcntl(con_fd, F_SETFL, flags | O_NONBLOCK) == -1) + goto gnutls_accept_close; + + struct gnutls_buffered_handle *gnutls_handle; + gnutls_handle = malloc(sizeof(*gnutls_handle)); + if (!gnutls_handle) + goto gnutls_accept_close; + + *handle = gnutls_handle; + gnutls_handle->valid = 1; + gnutls_handle->fd = con_fd; + + int res = mutex_init(&(gnutls_handle->mutex)); + if (res != 0) + goto gnutls_accept_free_gnutls_handle; + + addr->data = malloc(address_len); + if (addr->data == 0 && address_len != 0) + goto gnutls_accept_destroy_mutex; + + memcpy(addr->data, &address, address_len); + addr->len = address_len; + + if (gnutls_init(&(gnutls_handle->session), GNUTLS_SERVER | GNUTLS_NONBLOCK) != GNUTLS_E_SUCCESS) + goto gnutls_accept_free_addr_data; + + if (gnutls_credentials_set(gnutls_handle->session, GNUTLS_CRD_CERTIFICATE, gnutls_buffered_cert_creds) != GNUTLS_E_SUCCESS) + goto gnutls_accept_deinit_session; + + if (gnutls_set_default_priority(gnutls_handle->session) != GNUTLS_E_SUCCESS) + goto gnutls_accept_deinit_session; + + gnutls_transport_set_int(gnutls_handle->session, con_fd); + + gnutls_handshake_set_timeout(gnutls_handle->session, PING_INTERVAL * 1000); + gnutls_record_set_timeout(gnutls_handle->session, PING_INTERVAL * 1000); + + struct pollfd pollfd = { + .fd = con_fd, + }; + ssize_t gnutls_res; + do { + int poll_res; + do { + gnutls_res = gnutls_handshake(gnutls_handle->session); + } while (res == GNUTLS_E_INTERRUPTED); + if (gnutls_res < 0) { + if (gnutls_res == GNUTLS_E_AGAIN) { + pollfd.events = POLLIN | POLLOUT; + do { + poll_res = poll(&pollfd, 1, 0); + } while (poll_res < 0 && errno == EINTR); + if (poll_res < 0) + goto gnutls_accept_deinit_session; + + if ((pollfd.revents & (POLLIN | POLLOUT)) == (POLLIN | POLLOUT)) + continue; + else if (pollfd.revents & (~(POLLIN | POLLOUT))) + goto gnutls_accept_deinit_session; + else + pollfd.events = pollfd.revents ^ (POLLIN | POLLOUT); + } + } else { + break; + } + + do { + poll_res = poll(&pollfd, 1, PING_INTERVAL*1000); + } while (poll_res < 0 && errno == EINTR); + if (poll_res < 0) + goto gnutls_accept_deinit_session; + if (poll_res == 0) // Timed out + goto gnutls_accept_deinit_session; + if ((pollfd.revents & (POLLIN | POLLOUT)) == 0) + goto gnutls_accept_deinit_session; + } while (1); + + return con_fd; + + gnutls_accept_deinit_session: + gnutls_deinit(gnutls_handle->session); + gnutls_accept_free_addr_data: + free(addr->data); + gnutls_accept_destroy_mutex: + mutex_destroy(&(gnutls_handle->mutex)); + gnutls_accept_free_gnutls_handle: + free(gnutls_handle); + gnutls_accept_close: + close(con_fd); + return -1; +} + +void gnutls_buffered_shutdown(void *handle) { + struct gnutls_buffered_handle *gnutls_handle = handle; + mutex_lock(&(gnutls_handle->mutex)); + shutdown(gnutls_handle->fd, SHUT_RDWR); + gnutls_handle->valid = 0; + mutex_unlock(&(gnutls_handle->mutex)); +} + +void gnutls_buffered_close(int fd, void *handle) { + struct gnutls_buffered_handle *gnutls_handle = handle; + mutex_destroy(&(gnutls_handle->mutex)); + gnutls_deinit(gnutls_handle->session); + free(gnutls_handle); + close(fd); +} diff --git a/networks/gnutls_buffered.h b/networks/gnutls_buffered.h new file mode 100644 index 0000000..a12d4cd --- /dev/null +++ b/networks/gnutls_buffered.h @@ -0,0 +1,45 @@ +// One of the headers 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. + +#pragma once + +#include <stddef.h> + +#include "../haxstring.h" +#include "../general_network.h" + +int init_gnutls_buffered_network(void); + +int gnutls_buffered_send(void *fd, struct string msg); +size_t gnutls_buffered_recv(void *fd, char *data, size_t len, char *err); + +int gnutls_buffered_connect(void **handle, struct string address, struct string port, struct string *addr_out); +int gnutls_buffered_accept(int listen_fd, void **handle, struct string *addr); + +void gnutls_buffered_shutdown(void *handle); +void gnutls_buffered_close(int fd, void *handle); 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)); +} diff --git a/networks/openssl_buffered.h b/networks/openssl_buffered.h new file mode 100644 index 0000000..def2ec7 --- /dev/null +++ b/networks/openssl_buffered.h @@ -0,0 +1,45 @@ +// One of the headers 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. + +#pragma once + +#include <stddef.h> + +#include "../haxstring.h" +#include "../general_network.h" + +int init_openssl_buffered_network(void); + +int openssl_buffered_send(void *handle, struct string msg); +size_t openssl_buffered_recv(void *handle, char *data, size_t len, char *err); + +int openssl_buffered_connect(void **handle, struct string address, struct string port, struct string *addr_out); +int openssl_buffered_accept(int listen_fd, void **handle, struct string *addr); + +void openssl_buffered_shutdown(void *handle); +void openssl_buffered_close(int fd, void *handle); diff --git a/networks/plaintext_buffered.c b/networks/plaintext_buffered.c index 6ac3e1c..99bd085 100644 --- a/networks/plaintext_buffered.c +++ b/networks/plaintext_buffered.c @@ -65,27 +65,31 @@ struct plaintext_buffered_handle { size_t buffer_len; }; +int init_plaintext_buffered_network(void) { + return 0; +} + void * plaintext_buffered_send_thread(void *handle) { struct plaintext_buffered_handle *info = handle; size_t read_buffer_index = 0; mutex_lock(&(info->mutex)); while (1) { + if (!info->valid) + goto plaintext_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; - mutex_unlock(&(info->release_write)); #else sem_trywait(&(info->release_write)); sem_post(&(info->release_write)); #endif - if (!info->valid) - goto plaintext_buffered_send_thread_error_unlock; - mutex_unlock(&(info->mutex)); #ifdef USE_FUTEX @@ -132,7 +136,6 @@ void * plaintext_buffered_send_thread(void *handle) { sem_trywait(&(info->release_write)); sem_post(&(info->release_write)); #endif - // TODO: Sane solution that works with ptread mutexes while (1) { mutex_lock(&(info->mutex)); if (info->close) { @@ -165,10 +168,6 @@ void * plaintext_buffered_send_thread(void *handle) { return 0; } -int init_plaintext_buffered_network(void) { - return 0; -} - int plaintext_buffered_send(void *handle, struct string msg) { struct plaintext_buffered_handle *plaintext_handle = handle; while (msg.len > 0) { @@ -197,9 +196,6 @@ int plaintext_buffered_send(void *handle, struct string msg) { } #endif memcpy(&(plaintext_handle->buffer[plaintext_handle->write_buffer_index]), msg.data, len); - plaintext_handle->write_buffer_index += len; - if (plaintext_handle->write_buffer_index >= PLAINTEXT_BUFFERED_LEN) - plaintext_handle->write_buffer_index = 0; plaintext_handle->buffer_len += len; #ifdef USE_FUTEX mutex_unlock(&(plaintext_handle->release_read)); @@ -208,7 +204,11 @@ int plaintext_buffered_send(void *handle, struct string msg) { sem_post(&(plaintext_handle->release_read)); #endif mutex_unlock(&(plaintext_handle->mutex)); + plaintext_handle->write_buffer_index += len; + if (plaintext_handle->write_buffer_index >= PLAINTEXT_BUFFERED_LEN) + plaintext_handle->write_buffer_index = 0; msg.len -= len; + msg.data += len; } return 0; @@ -268,6 +268,7 @@ int plaintext_buffered_connect(void **handle, struct string address, struct stri goto plaintext_buffered_connect_close; *handle = plaintext_handle; plaintext_handle->valid = 1; + plaintext_handle->close = 0; plaintext_handle->fd = fd; addr_out->data = malloc(sizeof(sockaddr)); @@ -335,7 +336,7 @@ int plaintext_buffered_accept(int listen_fd, void **handle, struct string *addr) goto plaintext_buffered_accept_close; *handle = plaintext_handle; plaintext_handle->valid = 1; - plaintext_handle->close = 1; + plaintext_handle->close = 0; plaintext_handle->fd = con_fd; addr->data = malloc(address_len); |