diff options
Diffstat (limited to 'gnutls_network.c')
-rw-r--r-- | gnutls_network.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/gnutls_network.c b/gnutls_network.c new file mode 100644 index 0000000..5105ce1 --- /dev/null +++ b/gnutls_network.c @@ -0,0 +1,234 @@ +// 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 <gnutls/gnutls.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> + +#include "config.h" +#include "general_network.h" +#include "gnutls_network.h" + +gnutls_certificate_credentials_t gnutls_cert_creds; + +int init_gnutls_network(void) { + if (gnutls_global_init() < 0) + return 1; + + if (gnutls_certificate_allocate_credentials(&gnutls_cert_creds) != GNUTLS_E_SUCCESS) + return 2; + + if (GNUTLS_USE_SYSTEM_TRUST && (gnutls_certificate_set_x509_system_trust(gnutls_cert_creds) < 0)) + return 3; + + if (gnutls_certificate_set_x509_key_file(gnutls_cert_creds, GNUTLS_CERT_PATH, GNUTLS_KEY_PATH, GNUTLS_X509_FMT_PEM) < 0) + return 4; + + return 0; +} + +int gnutls_send(void *session, struct string msg) { + do { + ssize_t res; + do { + res = gnutls_record_send(*((gnutls_session_t*)session), msg.data, msg.len); + } while (res == -GNUTLS_E_INTERRUPTED || res == -GNUTLS_E_AGAIN); + + if (res < 0 || (size_t)res > msg.len) { // res > len shouldn't be possible, but is still an error + return 1; + } else if (res > 0) { + msg.len -= (size_t)res; + msg.data += (size_t)res; + } + } while (msg.len > 0); + + return 0; +} + +size_t gnutls_recv(void *session, char *data, size_t len, char *err) { + ssize_t res; + do { + res = gnutls_record_recv(*((gnutls_session_t*)session), data, len); + } while (res < 0 && (res == -GNUTLS_E_INTERRUPTED || res == -GNUTLS_E_AGAIN)); + + if (res < 0) { + if (res == -GNUTLS_E_TIMEDOUT) { + *err = 1; + } else { + *err = 3; + } + return 0; + } else if (res == 0) { + *err = 2; + return 0; + } + *err = 0; + + return (size_t)res; +} + +int gnutls_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 gnutls_connect_close; + + gnutls_session_t *session; + session = malloc(sizeof(*session)); + if (session == 0) + goto gnutls_connect_close; + *handle = session; + + if (gnutls_init(session, GNUTLS_CLIENT) != GNUTLS_E_SUCCESS) + goto gnutls_connect_free_session; + + if (gnutls_server_name_set(*session, GNUTLS_NAME_DNS, address.data, address.len) != GNUTLS_E_SUCCESS) + goto gnutls_connect_deinit_session; + + if (gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, gnutls_cert_creds) != GNUTLS_E_SUCCESS) + goto gnutls_connect_deinit_session; + + if (gnutls_set_default_priority(*session) != GNUTLS_E_SUCCESS) + goto gnutls_connect_deinit_session; + + gnutls_transport_set_int(*session, fd); + + gnutls_handshake_set_timeout(*session, PING_INTERVAL * 1000); + gnutls_record_set_timeout(*session, PING_INTERVAL * 1000); + + do { + res = gnutls_handshake(*session); + } while (res == -GNUTLS_E_INTERRUPTED || res == -GNUTLS_E_AGAIN); + if (res < 0) + goto gnutls_connect_deinit_session; + + 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(*session); + gnutls_connect_free_session: + free(session); + gnutls_connect_close: + close(fd); + return -1; +} + +int gnutls_accept(int listen_fd, void **handle, struct string *addr) { + 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; + + addr->data = malloc(address_len); + if (addr->data == 0 && address_len != 0) + goto gnutls_accept_close; + + memcpy(addr->data, &address, address_len); + addr->len = address_len; + + gnutls_session_t *session; + session = malloc(sizeof(*session)); + if (!session) + goto gnutls_accept_free_addr_data; + *handle = session; + + if (gnutls_init(session, GNUTLS_SERVER) != GNUTLS_E_SUCCESS) + goto gnutls_accept_free_session; + + if (gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, gnutls_cert_creds) != GNUTLS_E_SUCCESS) + goto gnutls_accept_deinit_session; + + if (gnutls_set_default_priority(*session) != GNUTLS_E_SUCCESS) + goto gnutls_accept_deinit_session; + + gnutls_transport_set_int(*session, con_fd); + + gnutls_handshake_set_timeout(*session, PING_INTERVAL * 1000); + gnutls_record_set_timeout(*session, PING_INTERVAL * 1000); + + int res; + do { + res = gnutls_handshake(*session); + } while (res == -GNUTLS_E_INTERRUPTED || res == -GNUTLS_E_AGAIN); + if (res != GNUTLS_E_SUCCESS) + goto gnutls_accept_deinit_session; + + return con_fd; + + gnutls_accept_deinit_session: + gnutls_deinit(*session); + gnutls_accept_free_session: + free(session); + gnutls_accept_free_addr_data: + free(addr->data); + gnutls_accept_close: + close(con_fd); + return -1; +} + +void gnutls_close(int fd, void *handle) { + gnutls_deinit(*((gnutls_session_t*)handle)); + free(handle); + close(fd); +} |