diff --git a/Makefile b/Makefile index afabc27..c5aadeb 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ chttp.c chttp.h: $(wildcard src/*.c src/*.h) misc/amalg.py Makefile python misc/amalg.py example: main.c chttp.c chttp.h - gcc main.c chttp.c -o example.exe + gcc main.c chttp.c -o example.exe -Wfatal-errors clean: rm chttp.c chttp.h diff --git a/chttp.c b/chttp.c index dec3879..024ab41 100644 --- a/chttp.c +++ b/chttp.c @@ -108,1560 +108,6 @@ void print_bytes(HTTP_String prefix, HTTP_String src) putc('\n', stream); } -//////////////////////////////////////////////////////////////////////////////////////// -// src/secure_context.c -//////////////////////////////////////////////////////////////////////////////////////// - -int global_secure_context_init(void) -{ - // TODO -} - -int global_secure_context_free(void) -{ - // TODO -} - -int client_secure_context_init(ClientSecureContext *ctx) -{ - // TODO -} - -int client_secure_context_free(ClientSecureContext *ctx) -{ - // TODO -} - -int server_secure_context_init(ServerSecureContext *ctx) -{ - // TODO -} - -int server_secure_context_free(ServerSecureContext *ctx) -{ - // TODO -} - -int server_secure_context_add_certificate(ServerSecureContext *ctx, - HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) -{ - // TODO -} - -//////////////////////////////////////////////////////////////////////////////////////// -// src/socket.c -//////////////////////////////////////////////////////////////////////////////////////// - -static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b) -{ -#ifdef _WIN32 - SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock == INVALID_SOCKET) - return -1; - - // Bind to loopback address with port 0 (dynamic port assignment) - struct sockaddr_in addr; - int addr_len = sizeof(addr); - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); // 127.0.0.1 - addr.sin_port = 0; // Let system choose port - - if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { - closesocket(sock); - return -1; - } - - if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) { - closesocket(sock); - return -1; - } - - if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { - closesocket(sock); - return -1; - } - - // Optional: Set socket to non-blocking mode - // This prevents send() from blocking if the receive buffer is full - u_long mode = 1; - ioctlsocket(sock, FIONBIO, &mode); // TODO: does this fail? - - *a = sock; - *b = sock; - return 0; -#else - int fds[2]; - if (pipe(fds) < 0) - return -1; - *a = fds[0]; - *b = fds[1]; - return 0; -#endif -} - -static int set_socket_blocking(NATIVE_SOCKET sock, bool value) -{ -#ifdef _WIN32 - u_long mode = !value; - if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR) - return -1; -#endif - -#ifdef __linux__ - int flags = fcntl(sock, F_GETFL, 0); - if (flags < 0) - return -1; - if (value) flags &= ~O_NONBLOCK; - else flags |= O_NONBLOCK; - if (fcntl(sock, F_SETFL, flags) < 0) - return -1; -#endif - - return 0; -} - -static NATIVE_SOCKET create_listen_socket(String addr, - Port port, bool reuse_addr, int backlog) -{ - NATIVE_SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock == NATIVE_SOCKET_INVALID) - return NATIVE_SOCKET_INVALID; - - if (set_socket_blocking(sock, false) < 0) { - CLOSE_NATIVE_SOCKET(sock); - return NATIVE_SOCKET_INVALID; - } - - if (reuse_addr) { - int one = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one)); - } - - struct in_addr addr_buf; - if (addr.len == 0) - addr_buf.s_addr = htonl(INADDR_ANY); - else { - - char copy[100]; - if (addr.len >= (int) sizeof(copy)) { - CLOSE_NATIVE_SOCKET(sock); - return NATIVE_SOCKET_INVALID; - } - memcpy(copy, addr.ptr, addr.len); - copy[addr.len] = '\0'; - - if (inet_pton(AF_INET, copy, &addr_buf) < 0) { - CLOSE_NATIVE_SOCKET(sock); - return NATIVE_SOCKET_INVALID; - } - } - - struct sockaddr_in bind_buf; - bind_buf.sin_family = AF_INET; - bind_buf.sin_addr = addr_buf; - bind_buf.sin_port = htons(port); - if (bind(sock, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) { // TODO: how does bind fail on windows? - CLOSE_NATIVE_SOCKET(sock); - return NATIVE_SOCKET_INVALID; - } - - if (listen(sock, backlog) < 0) { // TODO: how does listen fail on windows? - CLOSE_NATIVE_SOCKET(sock); - return NATIVE_SOCKET_INVALID; - } - - return sock; -} - -static void close_socket_pair(NATIVE_SOCKET a, NATIVE_SOCKET b) -{ -#ifdef _WIN32 - closesocket(a); - (void) b; -#else - close(a); - close(b); -#endif -} - -int socket_manager_init(SocketManager *sm, Socket *socks, - int num_socks) -{ - if (mutex_init(&sm->mutex) < 0) - return -1; - sm->plain_sock = NATIVE_SOCKET_INVALID; - sm->secure_sock = NATIVE_SOCKET_INVALID; - if (create_socket_pair(&sm->wait_sock, &sm->signal_sock) < 0) - return -1; - sm->at_least_one_secure_connect = false; - - sm->num_used = 0; - sm->max_used = num_socks; - sm->sockets = socks; - return 0; -} - -void socket_manager_free(SocketManager *sm) -{ - close_socket_pair(sm->wait_sock, sm->signal_sock); - - if (sm->secure_sock != NATIVE_SOCKET_INVALID) - server_secure_context_free(&sm->server_secure_context); - - if (sm->at_least_one_secure_connect) - client_secure_context_free(&sm->client_secure_context); - - if (sm->plain_sock != NATIVE_SOCKET_INVALID) - CLOSE_NATIVE_SOCKET(sm->plain_sock); - - if (sm->secure_sock != NATIVE_SOCKET_INVALID) - CLOSE_NATIVE_SOCKET(sm->secure_sock); - - mutex_free(&sm->mutex); -} - -int socket_manager_listen_tcp(SocketManager *sm, - String addr, Port port) -{ - if (sm->plain_sock != NATIVE_SOCKET_INVALID) - return -1; - - bool reuse_addr = false; - int backlog = 32; - sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog); - if (sm->plain_sock == NATIVE_SOCKET_INVALID) - return -1; - - return 0; -} - -int socket_manager_listen_tls(SocketManager *sm, - String addr, Port port, String cert_file_name, - String key_file_name) -{ - if (sm->secure_sock != NATIVE_SOCKET_INVALID) - return -1; - - bool reuse_addr = false; - int backlog = 32; - sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog); - if (sm->secure_sock == NATIVE_SOCKET_INVALID) - return -1; - - if (server_secure_context_init(&sm->server_secure_context) < 0) { - CLOSE_NATIVE_SOCKET(sm->secure_sock); - sm->secure_sock = NATIVE_SOCKET_INVALID; - return -1; - } - - return 0; -} - -int socket_manager_add_certificate(SocketManager *sm, - HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) -{ - if (sm->secure_sock == NATIVE_SOCKET_INVALID) - return -1; - - int ret = server_secure_context_add_certificate( - &sm->server_secure_context, domain, cert_file, key_file); - if (ret < 0) - return -1; - - return 0; -} - -static bool connect_pending(void) -{ -#ifdef _WIN32 - return WSAGetLastError() == WSAEWOULDBLOCK; -#else - return errno == EINPROGRESS; -#endif -} - -static bool -connect_failed_because_of_peer(void) -{ -#ifdef _WIN32 - int err = WSAGetLastError(); -#else - int err = errno; -#endif - return connect_failed_because_of_peer_2(err); -} - -static bool -connect_failed_because_of_peer_2(int err) -{ -#ifdef _WIN32 - return err == WSAECONNREFUSED - || err == WSAETIMEDOUT - || err == WSAENETUNREACH - || err == WSAEHOSTUNREACH; -#else - return err == ECONNREFUSED - || err == ETIMEDOUT - || err == ENETUNREACH - || err == EHOSTUNREACH; -#endif -} - -// This function moves the socket state machine -// to the next state until an I/O event would -// be required to continue. -static void socket_update(Socket *socket) -{ - // Each case of this switch encodes a state transition. - // If the evaluated case requires a given I/O event to - // continue, the loop will exit so that the caller can - // wait for that event. If the case can continue to a - // different case, the again flag is set, which causes - // a different case to be evaluated. - bool again; - do { - again = false; - switch (socket->state) { - case SOCKET_STATE_PENDING: - { - // This point may be reached because - // 1. The socket was just created by a connect - // operation. - // 2. Connecting to a host failed and now we - // need to try the next one. - // If (2) is true, we have some resources - // to clean up. - - if (socket->sock != NATIVE_SOCKET_INVALID) { - // This is not the first attempt - - CLOSE_NATIVE_SOCKET(socket->sock); - - socket->next_addr++; - if (socket->next_addr == socket->num_addr) { - assert(0); // TODO - } - } - AddressAndPort addr = socket->addrs[socket->next_addr]; - - int family = (addr.is_ipv4 ? AF_INET : AF_INET6); - NATIVE_SOCKET sock = socket(family, SOCK_STREAM, 0); - if (sock == NATIVE_SOCKET_INVALID) { - assert(0); // TODO - } - - if (set_socket_blocking(sock, false) < 0) { - assert(0); // TODO - } - - int ret; - if (addr.is_ipv4) { - struct sockaddr_in buf; - buf.sin_family = AF_INET; - buf.sin_port = htons(addr.port); - memset(&buf.sin_addr, &addr.ipv4, sizeof(IPv4)); - ret = connect(sock, (struct sockaddr*) &connect_buf, - sizeof(connect_buf)); - } else { - struct sockaddr_in6 buf; - buf.sin6_family = AF_INET6; - buf.sin6_port = htons(addr.port); - memset(&buf.sin6_addr, &addr.ipv6, sizeof(IPv6)); - ret = connect(sock, (struct sockaddr*) &connect_buf, - sizeof(connect_buf)); - } - - if (ret == 0) { - // Connect resolved immediately - socket->sock = sock; - socket->state = SOCKET_STATE_CONNECTED; - socket->events = 0; - again = true; - } else if (connect_pending()) { - // Connect is pending, which is expected - socket->sock = sock; - socket->state = SOCKET_STATE_CONNECTING; - socket->events = POLLOUT; - } else if (connect_failed_because_of_peer()) { - // Conenct failed due to the peer host - // We should try a different address. - socket->sock = sock; - socket->state = SOCKET_STATE_PENDING; - socket->events = 0; - again = true; - } else { - // An error occurred that we can't recover from - socket->sock = sock; - socket->state = SOCKET_STATE_DIED; - socket->events = 0; - again = true; - } - } - break; - - case SOCKET_STATE_CONNECTING: - { - // This point is reached when a connect() - // operation completes. - - int err = 0; - socklen_t len = sizeof(err); - if (getsockopt(socket->sock, SOL_SOCKET, SO_ERROR, (void*) &err, &len) < 0) { - assert(0); // TODO - } - - if (err == 0) { - // Connection succeded - socket->state = SOCKET_STATE_CONNECTED; - socket->events = 0; - again = true; - } else if (connect_failed_because_of_peer_2(err)) { - // Try the next address - socket->state = SOCKET_STATE_PENDING; - socket->events = 0; - again = true; - } else { - socket->state = SOCKET_STATE_DIED; - socket->events = 0; - } - } - break; - - case SOCKET_STATE_CONNECTED: - { - // We managed to connect to the peer. - // We can free the target array if it - // was allocated dynamically. - if (socket->num_addr > 1) - free(socket->addrs); - - if (!is_secure(socket)) { - socket->events = 0; - socket->state = SOCKET_STATE_ESTABLISHED_READY; - } else { -#ifdef HTTPS_ENABLED - assert(0); // TODO -#endif - } - } - break; - - case SOCKET_STATE_ESTABLISHED_WAIT: - socket->state = SOCKET_STATE_ESTABLISHED_READY; - socket->events = 0; - break; - - case SOCKET_STATE_SHUTDOWN: - { - if (!is_secure(socket)) { - socket->state = SOCKET_STATE_DIED; - socket->events = 0; - } else { -#ifdef HTTPS_ENABLED - assert(0); // TODO -#endif - } - } - break; - - default: - // Do nothing - break; - } - } while (again); -} - -int socket_manager_wakeup(SocketManager *sm) -{ - if (mutex_lock(&sm->mutex) < 0) - return -1; - - // TODO - - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return 0; -} - -static int socket_manager_register_events_nolock( - SocketManager *sm, struct pollfd *polled, int max_polled) -{ - // The poll array must be able to hold descriptors - // for a socket manager at full capacity. Note that - // other than having a number of connection sockets, - // the manager also needs 2 for the listeners and - // one for the wakeup self-pipe. - if (max_polled < sm->max_used+3) - return -1; - int num_polled = 0; - - polled[num_polled].fd = sm->wait_sock; - polled[num_polled].events = 0; - polled[num_polled].revents = 0; - num_polled++; - - // If the manager isn't at full capacity, monitor - // the listener sockets for incoming connections. - if (sm->num_used < sm->max_used) { - - if (sm->plain_sock != NATIVE_SOCKET_INVALID) { - polled[num_polled].fd = sm->plain_sock; - polled[num_polled].events = POLLIN; - polled[num_polled].revents = 0; - num_polled++; - } - - if (sm->secure_sock != NATIVE_SOCKET_INVALID) { - polled[num_polled].fd = sm->secure_sock; - polled[num_polled].events = POLLIN; - polled[num_polled].revents = 0; - num_polled++; - } - } - - // Iterate over each socket and register those that - // are waiting for I/O. If at least one socket that - // is ready to be processed exists, return an empty - // event registration list so that those entries can - // be processed immediately. - for (int i = 0, j = 0; j < sm->num_used; i++) { - Socket *s = &sm->sockets[i]; - if (s->state = SOCKET_STATE_FREE) - continue; - j++; - - if (s->state == SOCKET_STATE_DIED || s->state == SOCKET_STATE_ESTABLISHED_READY) - return 0; - - if (s->events) { - polled[num_polled].fd = s->sock; - polled[num_polled].events = s->events; - polled[num_polled].revents = 0; - num_polled++; - } - } - - return num_polled; -} - -int socket_manager_register_events(SocketManager *sm, - struct pollfd *polled, int max_polled) -{ - if (mutex_lock(&sm->mutex) < 0) - return -1; - - int ret = socket_manager_register_events_nolock( - sm, polled, max_polled); - - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return ret; -} - -static SocketHandle -socket_to_handle(SocketManager *sm, Socket *s) -{ - assert(0); // TODO -} - -static Socket *handle_to_socket(SocketManager *sm, SocketHandle handle) -{ - assert(0); // TODO -} - -static int socket_manager_translate_events_nolock( - SocketManager *sm, SocketEvent *events, int max_events, - struct pollfd *polled, int num_polled) -{ - int num_events = 0; - for (int i = 0; i < num_polled; i++) { - - if (polled[i].fd == sm->plain_sock || - polled[i].fd == sm->secure_sock) { - - // We only listen for input events from the listener - // if the socket pool isn't fool. This ensures that - // at least one socket struct is available. Note that - // it's still possible that we were at capacity MAX-1 - // and then got events from both the TCP and TCP/TLS - // listeners, causing one to be left witout a struct. - // This means we still need to check for full capacity. - // Fortunately, poll() is level-triggered, which means - // we'll handle this at the next iteration. - if (sm->num_used == sm->max_used) - continue; - - // Determine whether the event came from - // the encrypted listener or not. - bool secure = (polled[i].fd == sm->secure_sock); - - Socket *s = sm->sockets; - while (s->type != SOCKET_FREE) { - s++; - assert(s - sm->sockets < + sm->max_used); - } - - NATIVE_SOCKET sock = accept(polled[i].fd, NULL, NULL); - if (sock == NATIVE_SOCKET_INVALID) - continue; - - if (set_socket_blocking(sock, false) < 0) { - CLOSE_NATIVE_SOCKET(sock); - continue; - } - - s->state = SOCKET_STATE_ACCEPTED; - s->sock = sock; - s->events = 0; - s->user = NULL; - - socket_update(s); - if (s->state == SOCKET_STATE_DIED) { - CLOSE_NATIVE_SOCKET(sock); - s->state = SOCKET_STATE_FREE; - continue; - } - - pool->num_used++; - - } else if (polled[i].fd == sm->wait_sock) { - - // TODO: consume - - } else { - if (polled[i].revents) - socket_update(s); - } - } - - for (int i = 0, j = 0; j < sm->num_used; i++) { - Socket *s = &sm->sockets[i]; - if (s->state == SOCKET_FREE) - continue; - j++; - - if (num_events == max_events) - break; - - if (s->state == SOCKET_DIED) { - - events[num_events++] = (SocketEvent) { - SOCKET_EVENT_DISCONNECT, - SOCKET_HANDE_INVALID, - s->user - }; - - // Free resources associated to socket - s->state = SOCKET_FREE; - if (s->sock != NATIVE_SOCKET_INVALID) - CLOSE_NATIVE_SOCKET(s->sock); - if (s->sock == SOCKET_STATE_PENDING || - s->sock == SOCKET_STATE_CONNECTING) { - if (s->num_addr > 1) - free(s->addrs); - } - s->num_used--; - - } else if (s->state == SOCKET_STATE_ESTABLISHED_READY) { - events[num_events++] = (SocketEvent) { - SOCKET_EVENT_READY, - socket_to_handle(sm, s), - s->user - }; - } - } - - return num_events; -} - -int socket_manager_translate_events(SocketManager *sm, - SocketEvent *events, int max_events, struct pollfd *polled, - int num_polled) -{ - if (mutex_lock(&sm->mutex) < 0) - return -1; - - socket_manager_translate_events_nolock( - sm, events, max_events, polled, num_polled); - - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return 0; -} - -static int resolve_connect_targets(ConnectTarget *targets, - int num_targets, AddressAndPort *resolved, int max_resolved) -{ - int num_resolved = 0; - for (int i = 0; i < num_targets; i++) { - switch (targets[i].type) { - case CONNECT_TARGET_NAME: - { - char portstr[16]; - int len = snprintf(portstr, sizeof(portstr), "%u", port); - if (len < 0 || len >= (int) sizeof(portstr)) - return -1; - - struct addrinfo hints = {0}; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - struct addrinfo *res = NULL; - int ret = getaddrinfo(pending_connect->hostname, portstr, &hints, &res); - if (ret != 0) - return -1; - - for (struct addrinfo *rp = res; rp; rp = rp->ai_next) { - if (rp->ai_family == AF_INET) { - IPv4 ipv4 = *(IPv4*) &((struct sockaddr_in*)rp->ai_addr)->sin_addr; - if (num_resolved < max_resolved) { - resolved[num_resolved].is_ipv4 = true; - resolved[num_resolved].ipv4 = ipv4; - resolved[num_resolved].port = targets[i].port; - num_resolved++; - } - } else if (rp->ai_family == AF_INET6) { - IPv6 ipv6 = *(IPv6*) &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr; - if (num_resolved < max_resolved) { - resolved[num_resolved].is_ipv4 = false; - resolved[num_resolved].ipv4 = ipv6; - resolved[num_resolved].port = targets[i].port; - num_resolved++; - } - } - } - - freeaddrinfo(res); - } - break; - case CONNECT_TARGET_IPV4: - if (num_resolved < max_resolved) { - resolved[num_resolved].is_ipv4 = true; - resolved[num_resolved].ipv4 = targets[i].ipv4; - resolved[num_resolved].port = targets[i].port; - num_resolved++; - } - break; - case CONNECT_TARGET_IPV6: - if (num_resolved < max_resolved) { - resolved[num_resolved].is_ipv4 = false; - resolved[num_resolved].ipv6 = targets[i].ipv6; - resolved[num_resolved].port = targets[i].port; - num_resolved++; - } - break; - } - } - return num_resolved; -} - -int socket_connect(SocketManager *sm, int num_targets, - ConnectTarget *targets, bool secure, void *user) -{ - if (sm->num_used == sm->max_used) - return -1; - - AddressAndPort resolved[MAX_CONNECT_TARGETS]; - int num_resolved = resolve_connect_targets( - targets, num_targets, resolved, MAX_CONNECT_TARGETS); - - if (num_resolved <= 0) - return -1; - - Socket *s = sm->sockets; - while (s->type != SOCKET_FREE) { - s++; - assert(s - sm->sockets < + sm->max_used); - } - - if (num_resolved == 1) { - s->num_addr = 1; - s->next_addr = 0; - s->addr = resolved[0]; - } else { - s->num_addr = num_resolved; - s->next_addr = 0; - s->addrs = malloc(num_resolved * sizeof(AddressAndPort)); - if (s->addrs == NULL) - return -1; - for (int i = 0; i < num_resolved; i++) - s->addrs[i] = resolved[i]; - } - - s->type = SOCKET_STATE_PENDING; - s->sock = NATIVE_SOCKET_INVALID; - s->user = user; - s->num_used++; - return 0; -} - -static int socket_recv_nolock(SocketManager *sm, SocketHandle handle, - char *dst, int max) -{ - Socket *s = handle_to_socket(sm, handle); - if (s == NULL) - return 0; - - if (s->state != SOCKET_STATE_ESTABLISHED_READY) { - s->state = SOCKET_STATE_DIED; - s->events = 0; - return 0; - } - - if (!is_secure(s)) { - int ret = recv(s->sock, dst, max, 0); - if (ret == 0) { - s->state = SOCKET_STATE_DIED; - s->events = 0; - } else if (ret < 0) { - if (would_block()) { - s->state = SOCKET_STATE_ESTABLISHED_WAIT; - s->events = POLLIN; - } else if (!interrupted()) { - s->state = SOCKET_STATE_DIED; - s->events = 0; - } - ret = 0; - } - return 0; - } else { -#ifdef HTTPS_ENABLED - int ret = SSL_read(s->ssl, dst, max); - if (ret <= 0) { - int err = SSL_get_error(s->ssl, ret); - if (err == SSL_ERROR_WANT_READ) { - s->state = SOCKET_STATE_ESTABLISHED_WAIT; - s->events = POLLIN; - } else if (err == SSL_ERROR_WANT_WRITE) { - s->state = SOCKET_STATE_ESTABLISHED_WAIT; - s->events = POLLOUT; - } else { - s->state = SOCKET_STATE_DIED; - s->events = 0; - } - ret = 0; - } - return ret; -#endif - } -} - -int socket_recv(SocketManager *sm, SocketHandle handle, - char *dst, int max) -{ - if (mutex_lock(&sm->mutex) < 0) - return -1; - - int ret = socket_recv_nolock(sm, handle, dst, max); - - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return ret; -} - -static int socket_send_nolock(SocketManager *sm, SocketHandle handle, - char *src, int len) -{ - Socket *s = handle_to_socket(sm, handle); - if (s == NULL) - return 0; - - if (s->state != SOCKET_STATE_ESTABLISHED_READY) { - s->state = SOCKET_STATE_DIED; - s->events = 0; - return 0; - } - - if (!socket_secure(s)) { - int ret = send(s->sock, src, len, 0); - if (ret < 0) { - if (would_block()) { - s->state = SOCKET_STATE_ESTABLISHED_WAIT; - s->events = POLLOUT; - } else if (!interrupted()) { - s->state = SOCKET_DIED; - s->events = 0; - } - ret = 0; - } - return ret; - } else { -#ifdef HTTPS_ENABLED - int ret = SSL_write(s->ssl, src, len); - if (ret <= 0) { - int err = SSL_get_error(s->ssl, ret); - if (err == SSL_ERROR_WANT_READ) { - s->state = SOCKET_STATE_ESTABLISHED_WAIT; - s->events = POLLIN; - } else if (err == SSL_ERROR_WANT_WRITE) { - s->state = SOCKET_STATE_ESTABLISHED_WAIT; - s->events = POLLOUT; - } else { - s->state = SOCKET_STATE_DIED; - s->events = 0; - } - ret = 0; - } - return ret; -#endif - } -} - -int socket_send(SocketManager *sm, SocketHandle handle, - char *src, int len) -{ - if (mutex_lock(&sm->mutex) < 0) - return -1; - - int ret = socket_send_nolock(sm, handle, src, len); - - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return ret; -} - -void socket_close(SocketManager *sm, SocketHandle handle) -{ - if (mutex_lock(&sm->mutex) < 0) - return -1; - - // TODO: maybe we don't want to always set to SHUTDOWN. What if the socket is DIED for instance? - s->state = SOCKET_STATE_SHUTDOWN; - s->events = 0; - socket_update(s); - - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return 0; -} - -int socket_is_secure(SocketManager *sm, SocketHandle handle) -{ - if (mutex_lock(&sm->mutex) < 0) - return -1; - -#ifdef HTTPS_ENABLED - Socket *s = handle_to_socket(sm, handle); - - int ret; - if (s == NULL) - ret = -1; - else { - ret = (s->ssl != NULL); - } -#else - int ret = 0; -#endif - - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return ret; -} - -int socket_set_user(SocketManager *sm, SocketHandle handle) -{ - if (mutex_lock(&sm->mutex) < 0) - return -1; - - Socket *s = handle_to_socket(sm, handle); - if (s == NULL) - ret = -1; - else - s->user = user; - - if (mutex_unlock(&sm->mutex) < 0) - return -1; - return ret; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// src/thread.c -//////////////////////////////////////////////////////////////////////////////////////// - -int mutex_init(Mutex *mutex) -{ -#ifdef _WIN32 - InitializeCriticalSection(mutex); // TODO: mock? - return 0; -#else - if (pthread_mutex_init(mutex, NULL)) // TODO: mock - return -1; - return 0; -#endif -} - -int mutex_free(Mutex *mutex) -{ -#ifdef _WIN32 - DeleteCriticalSection(mutex); // TODO: mock? - return 0; -#else - if (pthread_mutex_destroy(mutex)) // TODO: mock - return -1; - return 0; -#endif -} - -int mutex_lock(Mutex *mutex) -{ -#ifdef _WIN32 - EnterCriticalSection(mutex); // TODO: mock? - return 0; -#else - if (pthread_mutex_lock(mutex)) // TODO: mock - return -1; - return 0; -#endif -} - -int mutex_unlock(Mutex *mutex) -{ -#ifdef _WIN32 - LeaveCriticalSection(mutex); // TODO: mock? - return 0; -#else - if (pthread_mutex_unlock(mutex)) // TODO: mock - return -1; - return 0; -#endif -} - -//////////////////////////////////////////////////////////////////////////////////////// -// src/byte_queue.c -//////////////////////////////////////////////////////////////////////////////////////// - -void byte_queue_init(ByteQueue *queue, uint32_t limit) -{ - queue->flags = 0; - queue->head = 0; - queue->size = 0; - queue->used = 0; - queue->curs = 0; - queue->limit = limit; - queue->data = NULL; - queue->read_target = NULL; -} - -// Deinitialize the queue -void byte_queue_free(ByteQueue *queue) -{ - if (queue->read_target) { - if (queue->read_target != queue->data) - free(queue->read_target); - queue->read_target = NULL; - queue->read_target_size = 0; - } - - free(queue->data); - queue->data = NULL; -} - -int byte_queue_error(ByteQueue *queue) -{ - return queue->flags & BYTE_QUEUE_ERROR; -} - -int byte_queue_empty(ByteQueue *queue) -{ - return queue->used == 0; -} - -int byte_queue_full(ByteQueue *queue) -{ - return queue->used == queue->limit; -} - -ByteView byte_queue_read_buf(ByteQueue *queue) -{ - if (queue->flags & BYTE_QUEUE_ERROR) - return (ByteView) {NULL, 0}; - - assert((queue->flags & BYTE_QUEUE_READ) == 0); - queue->flags |= BYTE_QUEUE_READ; - queue->read_target = queue->data; - queue->read_target_size = queue->size; - - if (queue->data == NULL) - return (ByteView) {NULL, 0}; - - return (ByteView) { queue->data + queue->head, queue->used }; -} - -void byte_queue_read_ack(ByteQueue *queue, uint32_t num) -{ - if (queue->flags & BYTE_QUEUE_ERROR) - return; - - if ((queue->flags & BYTE_QUEUE_READ) == 0) - return; - - queue->flags &= ~BYTE_QUEUE_READ; - - assert((uint32_t) num <= queue->used); - queue->head += (uint32_t) num; - queue->used -= (uint32_t) num; - queue->curs += (uint32_t) num; - - if (queue->read_target) { - if (queue->read_target != queue->data) - free(queue->read_target); - queue->read_target = NULL; - queue->read_target_size = 0; - } -} - -ByteView byte_queue_write_buf(ByteQueue *queue) -{ - if ((queue->flags & BYTE_QUEUE_ERROR) || queue->data == NULL) - return (ByteView) {NULL, 0}; - - assert((queue->flags & BYTE_QUEUE_WRITE) == 0); - queue->flags |= BYTE_QUEUE_WRITE; - - return (ByteView) { - queue->data + (queue->head + queue->used), - queue->size - (queue->head + queue->used), - }; -} - -void byte_queue_write_ack(ByteQueue *queue, uint32_t num) -{ - if (queue->flags & BYTE_QUEUE_ERROR) - return; - - if ((queue->flags & BYTE_QUEUE_WRITE) == 0) - return; - - queue->flags &= ~BYTE_QUEUE_WRITE; - queue->used += num; -} - -int byte_queue_write_setmincap(ByteQueue *queue, uint32_t mincap) -{ - // Sticky error - if (queue->flags & BYTE_QUEUE_ERROR) - return 0; - - // In general, the queue's contents look like this: - // - // size - // v - // [___xxxxxxxxxxxx________] - // ^ ^ ^ - // 0 head head + used - // - // This function needs to make sure that at least [mincap] - // bytes are available on the right side of the content. - // - // We have 3 cases: - // - // 1) If there is enough memory already, this function doesn't - // need to do anything. - // - // 2) If there isn't enough memory on the right but there is - // enough free memory if we cound the left unused region, - // then the content is moved back to the - // start of the buffer. - // - // 3) If there isn't enough memory considering both sides, this - // function needs to allocate a new buffer. - // - // If there are pending read or write operations, the application - // is holding pointers to the buffer, so we need to make sure - // to not invalidate them. The only real problem is pending reads - // since this function can only be called before starting a write - // opearation. - // - // To avoid invalidating the read pointer when we allocate a new - // buffer, we don't free the old buffer. Instead, we store the - // pointer in the "old" field so that the read ack function can - // free it. - // - // To avoid invalidating the pointer when we are moving back the - // content since there is enough memory at the start of the buffer, - // we just avoid that. Even if there is enough memory considering - // left and right free regions, we allocate a new buffer. - - assert((queue->flags & BYTE_QUEUE_WRITE) == 0); - - uint32_t total_free_space = queue->size - queue->used; - uint32_t free_space_after_data = queue->size - queue->used - queue->head; - - int moved = 0; - if (free_space_after_data < mincap) { - - if (total_free_space < mincap || (queue->read_target == queue->data)) { - // Resize required - - if (queue->used + mincap > queue->limit) { - queue->flags |= BYTE_QUEUE_ERROR; - return 0; - } - - uint32_t size; - if (queue->size > UINT32_MAX / 2) - size = UINT32_MAX; - else - size = 2 * queue->size; - - if (size < queue->used + mincap) - size = queue->used + mincap; - - if (size > queue->limit) - size = queue->limit; - - uint8_t *data = malloc(size); - if (!data) { - queue->flags |= BYTE_QUEUE_ERROR; - return 0; - } - - if (queue->used > 0) - memcpy(data, queue->data + queue->head, queue->used); - - if (queue->read_target != queue->data) - free(queue->data); - - queue->data = data; - queue->head = 0; - queue->size = size; - - } else { - // Move required - memmove(queue->data, queue->data + queue->head, queue->used); - queue->head = 0; - } - - moved = 1; - } - - return moved; -} - -void byte_queue_write(ByteQueue *queue, void *ptr, uint32_t len) -{ - byte_queue_write_setmincap(queue, len); - ByteView dst = byte_queue_write_buf(queue); - if (dst.ptr) { - memcpy(dst.ptr, ptr, len); - byte_queue_write_ack(queue, len); - } -} - -ByteQueueOffset byte_queue_offset(ByteQueue *queue) -{ - if (queue->flags & BYTE_QUEUE_ERROR) - return (ByteQueueOffset) { 0 }; - return (ByteQueueOffset) { queue->curs + queue->used }; -} - -void byte_queue_patch(ByteQueue *queue, ByteQueueOffset off, - void *src, uint32_t len) -{ - if (queue->flags & BYTE_QUEUE_ERROR) - return; - - // Check that the offset is in range - assert(off >= queue->curs && off - queue->curs < queue->used); - - // Check that the length is in range - assert(len <= queue->used - (off - queue->curs)); - - // Perform the patch - uint8_t *dst = queue->data + queue->head + (off - queue->curs); - memcpy(dst, src, len); -} - -uint32_t byte_queue_size_from_offset(ByteQueue *queue, ByteQueueOffset off) -{ - return queue->curs + queue->used - off; -} - -void byte_queue_remove_from_offset(ByteQueue *queue, ByteQueueOffset offset) -{ - if (queue->flags & BYTE_QUEUE_ERROR) - return; - - uint64_t num = (queue->curs + queue->used) - offset; - assert(num <= queue->used); - - queue->used -= num; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// src/cert.c -//////////////////////////////////////////////////////////////////////////////////////// - -#ifdef HTTPS_ENABLED - -static EVP_PKEY *generate_rsa_key_pair(int key_bits) -{ - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); - if (!ctx) - return NULL; - - if (EVP_PKEY_keygen_init(ctx) <= 0) { - EVP_PKEY_CTX_free(ctx); - return NULL; - } - - if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, key_bits) <= 0) { - EVP_PKEY_CTX_free(ctx); - return NULL; - } - - EVP_PKEY *pkey = NULL; - if (EVP_PKEY_keygen(ctx, &pkey) <= 0) { - EVP_PKEY_CTX_free(ctx); - return NULL; - } - - EVP_PKEY_CTX_free(ctx); - return pkey; -} - -static X509 *create_certificate(EVP_PKEY *pkey, HTTP_String C, HTTP_String O, HTTP_String CN, int days) -{ - X509 *x509 = X509_new(); - if (!x509) - return NULL; - - // Set version (version 3) - X509_set_version(x509, 2); - - // Set serial number - ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); - - // Set validity period - X509_gmtime_adj(X509_get_notBefore(x509), 0); - X509_gmtime_adj(X509_get_notAfter(x509), 31536000L * days); // days * seconds_per_year - - // Set public key - X509_set_pubkey(x509, pkey); - - // Set subject name - X509_NAME *name = X509_get_subject_name(x509); - X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char*) C.ptr, C.len, -1, 0); - X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char*) O.ptr, O.len, -1, 0); - X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*) CN.ptr, CN.len, -1, 0); - - // Set issuer name (same as subject for self-signed) - X509_set_issuer_name(x509, name); - - if (!X509_sign(x509, pkey, EVP_sha256())) { - X509_free(x509); - return NULL; - } - - return x509; -} - -static int save_private_key(EVP_PKEY *pkey, HTTP_String file) -{ - char copy[1<<10]; - if (file.len >= (int) sizeof(copy)) - return -1; - memcpy(copy, file.ptr, file.len); - copy[file.len] = '\0'; - - FILE *fp = fopen(copy, "wb"); - if (!fp) - return -1; - - // Write private key in PEM format - if (!PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL)) { - fclose(fp); - return -1; - } - - fclose(fp); - return 0; -} - -static int save_certificate(X509 *x509, HTTP_String file) -{ - char copy[1<<10]; - if (file.len >= (int) sizeof(copy)) - return -1; - memcpy(copy, file.ptr, file.len); - copy[file.len] = '\0'; - - FILE *fp = fopen(copy, "wb"); - if (!fp) - return -1; - - // Write certificate in PEM format - if (!PEM_write_X509(fp, x509)) { - fclose(fp); - return -1; - } - - fclose(fp); - return 0; -} - -int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN, - HTTP_String cert_file, HTTP_String key_file) -{ - EVP_PKEY *pkey = generate_rsa_key_pair(2048); - if (pkey == NULL) - return -1; - - X509 *x509 = create_certificate(pkey, C, O, CN, 1); - if (x509 == NULL) { - EVP_PKEY_free(pkey); - return -1; - } - - if (save_private_key(pkey, key_file) < 0) { - X509_free(x509); - EVP_PKEY_free(pkey); - return -1; - } - - if (save_certificate(x509, cert_file) < 0) { - X509_free(x509); - EVP_PKEY_free(pkey); - return -1; - } - - X509_free(x509); - EVP_PKEY_free(pkey); - return 0; -} - -#else - -int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN, - HTTP_String cert_file, HTTP_String key_file) -{ - (void) C; - (void) O; - (void) CN; - (void) cert_file; - (void) key_file; - return -1; -} - -#endif - -//////////////////////////////////////////////////////////////////////////////////////// -// src/client.c -//////////////////////////////////////////////////////////////////////////////////////// - -int http_client_init(HTTP_Client *client) -{ - client->num_conns = 0; - for (int i = 0; i < HTTP_CLIENT_CAPACITY; i++) - client->conns[i].state = HTTP_CLIENT_CONN_FREE; - - client->num_ready = 0; - client->ready_head = 0; - - if (socket_manager_init(&client->sockets, - client->socket_pool, HTTP_CLIENT_CAPACITY) < 0) - return -1; - return 0; -} - -void http_client_free(HTTP_Client *client) -{ - socket_manager_free(&client->sockets); - - for (int i = 0, j = 0; j < client->num_conns; i++) { - HTTP_ClientConn *conn = &client->conns[i]; - if (conn->state == HTTP_CLIENT_CONN_FREE) - continue; - j++; - - http_client_conn_free(conn); - } -} - -int http_client_wakeup(HTTP_Client *client) -{ - if (socket_manager_wakeup(&client->sockets) < 0) - return -1; - return 0; -} - -int http_client_get_builder(HTTP_Client *client, HTTP_RequestBuilder *builder) -{ - // TODO -} - -void http_request_builder_line(HTTP_RequestBuilder builder, xxxx) -{ - // TODO -} - -void http_request_builder_header(HTTP_RequestBuilder builder, String str) -{ - // TODO -} - -void http_request_builder_body(HTTP_RequestBuilder builder, String str) -{ - // TODO -} - -int http_request_builder_send(HTTP_RequestBuilder builder) -{ - // TODO -} - -int http_client_register_events(HTTP_Client *client, - struct pollfd *polled, int max_polled) -{ - return socket_manager_register_events( - &client->sockets, polled, max_polled); -} - -int http_client_process_events(HTTP_Client *client, - struct pollfd *polled, int num_polled) -{ - SocketEvent events[HTTP_CLIENT_CAPACITY]; - int num_events = socket_manager_translate_events( - &client->sockets, polled, num_polled); - - for (int i = 0; i < num_events; i++) { - - if (events[i].type == SOCKET_EVENT_DISCONNECT) { - - // TODO - - } else if (events[i].type == SOCKET_EVENT_READY) { - - // TODO - } - - // TODO - } - - return 0; -} - -bool http_client_next_response(HTTP_Client *client, - HTTP_Response **response); -{ - if (server->num_ready == 0) - return false; - - HTTP_ClientConn *conn = &client->conns[client->ready_head]; - client->ready_head = (client->ready_head + 1) % HTTP_CLIENT_CAPACITY; - client->num_ready--; - - assert(conn->state == HTTP_CLIENT_CONN_COMPLETE); - *response = &conn->response; - return true; -} - -void http_free_response(HTTP_Response *res) -{ - // TODO -} - //////////////////////////////////////////////////////////////////////////////////////// // src/parse.c //////////////////////////////////////////////////////////////////////////////////////// @@ -2971,12 +1417,1600 @@ bool http_match_host(HTTP_Request *req, HTTP_String domain, int port) return http_streq(host, domain); } +//////////////////////////////////////////////////////////////////////////////////////// +// src/thread.c +//////////////////////////////////////////////////////////////////////////////////////// + +int mutex_init(Mutex *mutex) +{ +#ifdef _WIN32 + InitializeCriticalSection(mutex); // TODO: mock? + return 0; +#else + if (pthread_mutex_init(mutex, NULL)) // TODO: mock + return -1; + return 0; +#endif +} + +int mutex_free(Mutex *mutex) +{ +#ifdef _WIN32 + DeleteCriticalSection(mutex); // TODO: mock? + return 0; +#else + if (pthread_mutex_destroy(mutex)) // TODO: mock + return -1; + return 0; +#endif +} + +int mutex_lock(Mutex *mutex) +{ +#ifdef _WIN32 + EnterCriticalSection(mutex); // TODO: mock? + return 0; +#else + if (pthread_mutex_lock(mutex)) // TODO: mock + return -1; + return 0; +#endif +} + +int mutex_unlock(Mutex *mutex) +{ +#ifdef _WIN32 + LeaveCriticalSection(mutex); // TODO: mock? + return 0; +#else + if (pthread_mutex_unlock(mutex)) // TODO: mock + return -1; + return 0; +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////// +// src/secure_context.c +//////////////////////////////////////////////////////////////////////////////////////// + +int global_secure_context_init(void) +{ + // TODO +} + +int global_secure_context_free(void) +{ + // TODO +} + +int client_secure_context_init(ClientSecureContext *ctx) +{ + // TODO +} + +int client_secure_context_free(ClientSecureContext *ctx) +{ + // TODO +} + +int server_secure_context_init(ServerSecureContext *ctx) +{ + // TODO +} + +int server_secure_context_free(ServerSecureContext *ctx) +{ + // TODO +} + +int server_secure_context_add_certificate(ServerSecureContext *ctx, + HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) +{ + // TODO +} + +//////////////////////////////////////////////////////////////////////////////////////// +// src/socket.c +//////////////////////////////////////////////////////////////////////////////////////// + +static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b) +{ +#ifdef _WIN32 + SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == INVALID_SOCKET) + return -1; + + // Bind to loopback address with port 0 (dynamic port assignment) + struct sockaddr_in addr; + int addr_len = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); // 127.0.0.1 + addr.sin_port = 0; // Let system choose port + + if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { + closesocket(sock); + return -1; + } + + if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) { + closesocket(sock); + return -1; + } + + if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { + closesocket(sock); + return -1; + } + + // Optional: Set socket to non-blocking mode + // This prevents send() from blocking if the receive buffer is full + u_long mode = 1; + ioctlsocket(sock, FIONBIO, &mode); // TODO: does this fail? + + *a = sock; + *b = sock; + return 0; +#else + int fds[2]; + if (pipe(fds) < 0) + return -1; + *a = fds[0]; + *b = fds[1]; + return 0; +#endif +} + +static int set_socket_blocking(NATIVE_SOCKET sock, bool value) +{ +#ifdef _WIN32 + u_long mode = !value; + if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR) + return -1; +#endif + +#ifdef __linux__ + int flags = fcntl(sock, F_GETFL, 0); + if (flags < 0) + return -1; + if (value) flags &= ~O_NONBLOCK; + else flags |= O_NONBLOCK; + if (fcntl(sock, F_SETFL, flags) < 0) + return -1; +#endif + + return 0; +} + +static NATIVE_SOCKET create_listen_socket(String addr, + Port port, bool reuse_addr, int backlog) +{ + NATIVE_SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == NATIVE_SOCKET_INVALID) + return NATIVE_SOCKET_INVALID; + + if (set_socket_blocking(sock, false) < 0) { + CLOSE_NATIVE_SOCKET(sock); + return NATIVE_SOCKET_INVALID; + } + + if (reuse_addr) { + int one = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one)); + } + + struct in_addr addr_buf; + if (addr.len == 0) + addr_buf.s_addr = htonl(INADDR_ANY); + else { + + char copy[100]; + if (addr.len >= (int) sizeof(copy)) { + CLOSE_NATIVE_SOCKET(sock); + return NATIVE_SOCKET_INVALID; + } + memcpy(copy, addr.ptr, addr.len); + copy[addr.len] = '\0'; + + if (inet_pton(AF_INET, copy, &addr_buf) < 0) { + CLOSE_NATIVE_SOCKET(sock); + return NATIVE_SOCKET_INVALID; + } + } + + struct sockaddr_in bind_buf; + bind_buf.sin_family = AF_INET; + bind_buf.sin_addr = addr_buf; + bind_buf.sin_port = htons(port); + if (bind(sock, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) { // TODO: how does bind fail on windows? + CLOSE_NATIVE_SOCKET(sock); + return NATIVE_SOCKET_INVALID; + } + + if (listen(sock, backlog) < 0) { // TODO: how does listen fail on windows? + CLOSE_NATIVE_SOCKET(sock); + return NATIVE_SOCKET_INVALID; + } + + return sock; +} + +static void close_socket_pair(NATIVE_SOCKET a, NATIVE_SOCKET b) +{ +#ifdef _WIN32 + closesocket(a); + (void) b; +#else + close(a); + close(b); +#endif +} + +int socket_manager_init(SocketManager *sm, Socket *socks, + int num_socks) +{ + if (mutex_init(&sm->mutex) < 0) + return -1; + sm->plain_sock = NATIVE_SOCKET_INVALID; + sm->secure_sock = NATIVE_SOCKET_INVALID; + if (create_socket_pair(&sm->wait_sock, &sm->signal_sock) < 0) + return -1; + sm->at_least_one_secure_connect = false; + + sm->num_used = 0; + sm->max_used = num_socks; + sm->sockets = socks; + return 0; +} + +void socket_manager_free(SocketManager *sm) +{ + close_socket_pair(sm->wait_sock, sm->signal_sock); + + if (sm->secure_sock != NATIVE_SOCKET_INVALID) + server_secure_context_free(&sm->server_secure_context); + + if (sm->at_least_one_secure_connect) + client_secure_context_free(&sm->client_secure_context); + + if (sm->plain_sock != NATIVE_SOCKET_INVALID) + CLOSE_NATIVE_SOCKET(sm->plain_sock); + + if (sm->secure_sock != NATIVE_SOCKET_INVALID) + CLOSE_NATIVE_SOCKET(sm->secure_sock); + + mutex_free(&sm->mutex); +} + +int socket_manager_listen_tcp(SocketManager *sm, + String addr, Port port) +{ + if (sm->plain_sock != NATIVE_SOCKET_INVALID) + return -1; + + bool reuse_addr = false; + int backlog = 32; + sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog); + if (sm->plain_sock == NATIVE_SOCKET_INVALID) + return -1; + + return 0; +} + +int socket_manager_listen_tls(SocketManager *sm, + String addr, Port port, String cert_file_name, + String key_file_name) +{ + if (sm->secure_sock != NATIVE_SOCKET_INVALID) + return -1; + + bool reuse_addr = false; + int backlog = 32; + sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog); + if (sm->secure_sock == NATIVE_SOCKET_INVALID) + return -1; + + if (server_secure_context_init(&sm->server_secure_context) < 0) { + CLOSE_NATIVE_SOCKET(sm->secure_sock); + sm->secure_sock = NATIVE_SOCKET_INVALID; + return -1; + } + + return 0; +} + +int socket_manager_add_certificate(SocketManager *sm, + HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) +{ + if (sm->secure_sock == NATIVE_SOCKET_INVALID) + return -1; + + int ret = server_secure_context_add_certificate( + &sm->server_secure_context, domain, cert_file, key_file); + if (ret < 0) + return -1; + + return 0; +} + +static bool is_secure(Socket *s) +{ +#ifdef HTTPS_ENABLED + return (s->ssl != NULL); +#else + return false; +#endif +} + +static bool connect_pending(void) +{ +#ifdef _WIN32 + return WSAGetLastError() == WSAEWOULDBLOCK; +#else + return errno == EINPROGRESS; +#endif +} + +static bool +connect_failed_because_of_peer_2(int err) +{ +#ifdef _WIN32 + return err == WSAECONNREFUSED + || err == WSAETIMEDOUT + || err == WSAENETUNREACH + || err == WSAEHOSTUNREACH; +#else + return err == ECONNREFUSED + || err == ETIMEDOUT + || err == ENETUNREACH + || err == EHOSTUNREACH; +#endif +} + +static bool +connect_failed_because_of_peer(void) +{ +#ifdef _WIN32 + int err = WSAGetLastError(); +#else + int err = errno; +#endif + return connect_failed_because_of_peer_2(err); +} + +// This function moves the socket state machine +// to the next state until an I/O event would +// be required to continue. +static void socket_update(Socket *s) +{ + // Each case of this switch encodes a state transition. + // If the evaluated case requires a given I/O event to + // continue, the loop will exit so that the caller can + // wait for that event. If the case can continue to a + // different case, the again flag is set, which causes + // a different case to be evaluated. + bool again; + do { + again = false; + switch (s->state) { + case SOCKET_STATE_PENDING: + { + // This point may be reached because + // 1. The socket was just created by a connect + // operation. + // 2. Connecting to a host failed and now we + // need to try the next one. + // If (2) is true, we have some resources + // to clean up. + + if (s->sock != NATIVE_SOCKET_INVALID) { + // This is not the first attempt + + CLOSE_NATIVE_SOCKET(s->sock); + + s->next_addr++; + if (s->next_addr == s->num_addr) { + assert(0); // TODO + } + } + AddressAndPort addr = s->addrs[s->next_addr]; + + int family = (addr.is_ipv4 ? AF_INET : AF_INET6); + NATIVE_SOCKET sock = socket(family, SOCK_STREAM, 0); + if (sock == NATIVE_SOCKET_INVALID) { + assert(0); // TODO + } + + if (set_socket_blocking(sock, false) < 0) { + assert(0); // TODO + } + + int ret; + if (addr.is_ipv4) { + struct sockaddr_in buf; + buf.sin_family = AF_INET; + buf.sin_port = htons(addr.port); + memcpy(&buf.sin_addr, &addr.ipv4, sizeof(IPv4)); + ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf)); + } else { + struct sockaddr_in6 buf; + buf.sin6_family = AF_INET6; + buf.sin6_port = htons(addr.port); + memcpy(&buf.sin6_addr, &addr.ipv6, sizeof(IPv6)); + ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf)); + } + + if (ret == 0) { + // Connect resolved immediately + s->sock = sock; + s->state = SOCKET_STATE_CONNECTED; + s->events = 0; + again = true; + } else if (connect_pending()) { + // Connect is pending, which is expected + s->sock = sock; + s->state = SOCKET_STATE_CONNECTING; + s->events = POLLOUT; + } else if (connect_failed_because_of_peer()) { + // Conenct failed due to the peer host + // We should try a different address. + s->sock = sock; + s->state = SOCKET_STATE_PENDING; + s->events = 0; + again = true; + } else { + // An error occurred that we can't recover from + s->sock = sock; + s->state = SOCKET_STATE_DIED; + s->events = 0; + again = true; + } + } + break; + + case SOCKET_STATE_CONNECTING: + { + // This point is reached when a connect() + // operation completes. + + int err = 0; + socklen_t len = sizeof(err); + if (getsockopt(s->sock, SOL_SOCKET, SO_ERROR, (void*) &err, &len) < 0) { + assert(0); // TODO + } + + if (err == 0) { + // Connection succeded + s->state = SOCKET_STATE_CONNECTED; + s->events = 0; + again = true; + } else if (connect_failed_because_of_peer_2(err)) { + // Try the next address + s->state = SOCKET_STATE_PENDING; + s->events = 0; + again = true; + } else { + s->state = SOCKET_STATE_DIED; + s->events = 0; + } + } + break; + + case SOCKET_STATE_CONNECTED: + { + // We managed to connect to the peer. + // We can free the target array if it + // was allocated dynamically. + if (s->num_addr > 1) + free(s->addrs); + + if (!is_secure(s)) { + s->events = 0; + s->state = SOCKET_STATE_ESTABLISHED_READY; + } else { +#ifdef HTTPS_ENABLED + assert(0); // TODO +#endif + } + } + break; + + case SOCKET_STATE_ACCEPTED: + { + if (!is_secure(s)) { + s->state = SOCKET_STATE_ESTABLISHED_READY; + s->events = 0; + } else { +#ifdef HTTPS_ENABLED + assert(0); // TODO +#endif + } + } + break; + + case SOCKET_STATE_ESTABLISHED_WAIT: + s->state = SOCKET_STATE_ESTABLISHED_READY; + s->events = 0; + break; + + case SOCKET_STATE_SHUTDOWN: + { + if (!is_secure(s)) { + s->state = SOCKET_STATE_DIED; + s->events = 0; + } else { +#ifdef HTTPS_ENABLED + assert(0); // TODO +#endif + } + } + break; + + default: + // Do nothing + break; + } + } while (again); +} + +int socket_manager_wakeup(SocketManager *sm) +{ + if (mutex_lock(&sm->mutex) < 0) + return -1; + + // TODO + + if (mutex_unlock(&sm->mutex) < 0) + return -1; + return 0; +} + +static int socket_manager_register_events_nolock( + SocketManager *sm, struct pollfd *polled, int max_polled) +{ + // The poll array must be able to hold descriptors + // for a socket manager at full capacity. Note that + // other than having a number of connection sockets, + // the manager also needs 2 for the listeners and + // one for the wakeup self-pipe. + if (max_polled < sm->max_used+3) + return -1; + int num_polled = 0; + + polled[num_polled].fd = sm->wait_sock; + polled[num_polled].events = 0; + polled[num_polled].revents = 0; + num_polled++; + + // If the manager isn't at full capacity, monitor + // the listener sockets for incoming connections. + if (sm->num_used < sm->max_used) { + + if (sm->plain_sock != NATIVE_SOCKET_INVALID) { + polled[num_polled].fd = sm->plain_sock; + polled[num_polled].events = POLLIN; + polled[num_polled].revents = 0; + num_polled++; + } + + if (sm->secure_sock != NATIVE_SOCKET_INVALID) { + polled[num_polled].fd = sm->secure_sock; + polled[num_polled].events = POLLIN; + polled[num_polled].revents = 0; + num_polled++; + } + } + + // Iterate over each socket and register those that + // are waiting for I/O. If at least one socket that + // is ready to be processed exists, return an empty + // event registration list so that those entries can + // be processed immediately. + for (int i = 0, j = 0; j < sm->num_used; i++) { + Socket *s = &sm->sockets[i]; + if (s->state = SOCKET_STATE_FREE) + continue; + j++; + + if (s->state == SOCKET_STATE_DIED || s->state == SOCKET_STATE_ESTABLISHED_READY) + return 0; + + if (s->events) { + polled[num_polled].fd = s->sock; + polled[num_polled].events = s->events; + polled[num_polled].revents = 0; + num_polled++; + } + } + + return num_polled; +} + +int socket_manager_register_events(SocketManager *sm, + struct pollfd *polled, int max_polled) +{ + if (mutex_lock(&sm->mutex) < 0) + return -1; + + int ret = socket_manager_register_events_nolock( + sm, polled, max_polled); + + if (mutex_unlock(&sm->mutex) < 0) + return -1; + return ret; +} + +static SocketHandle +socket_to_handle(SocketManager *sm, Socket *s) +{ + assert(0); // TODO +} + +static Socket *handle_to_socket(SocketManager *sm, SocketHandle handle) +{ + assert(0); // TODO +} + +static int socket_manager_translate_events_nolock( + SocketManager *sm, SocketEvent *events, int max_events, + struct pollfd *polled, int num_polled) +{ + int num_events = 0; + for (int i = 0; i < num_polled; i++) { + + if (polled[i].fd == sm->plain_sock || + polled[i].fd == sm->secure_sock) { + + // We only listen for input events from the listener + // if the socket pool isn't fool. This ensures that + // at least one socket struct is available. Note that + // it's still possible that we were at capacity MAX-1 + // and then got events from both the TCP and TCP/TLS + // listeners, causing one to be left witout a struct. + // This means we still need to check for full capacity. + // Fortunately, poll() is level-triggered, which means + // we'll handle this at the next iteration. + if (sm->num_used == sm->max_used) + continue; + + // Determine whether the event came from + // the encrypted listener or not. + bool secure = (polled[i].fd == sm->secure_sock); + + Socket *s = sm->sockets; + while (s->state != SOCKET_STATE_FREE) { + s++; + assert(s - sm->sockets < + sm->max_used); + } + + NATIVE_SOCKET sock = accept(polled[i].fd, NULL, NULL); + if (sock == NATIVE_SOCKET_INVALID) + continue; + + if (set_socket_blocking(sock, false) < 0) { + CLOSE_NATIVE_SOCKET(sock); + continue; + } + + s->state = SOCKET_STATE_ACCEPTED; + s->sock = sock; + s->events = 0; + s->user = NULL; + + socket_update(s); + if (s->state == SOCKET_STATE_DIED) { + CLOSE_NATIVE_SOCKET(sock); + s->state = SOCKET_STATE_FREE; + continue; + } + + sm->num_used++; + + } else if (polled[i].fd == sm->wait_sock) { + + // TODO: consume + + } else { + if (polled[i].revents) + socket_update(s); + } + } + + for (int i = 0, j = 0; j < sm->num_used; i++) { + Socket *s = &sm->sockets[i]; + if (s->state == SOCKET_STATE_FREE) + continue; + j++; + + if (num_events == max_events) + break; + + if (s->state == SOCKET_DIED) { + + events[num_events++] = (SocketEvent) { + SOCKET_EVENT_DISCONNECT, + SOCKET_HANDE_INVALID, + s->user + }; + + // Free resources associated to socket + s->state = SOCKET_STATE_FREE; + if (s->sock != NATIVE_SOCKET_INVALID) + CLOSE_NATIVE_SOCKET(s->sock); + if (s->sock == SOCKET_STATE_PENDING || + s->sock == SOCKET_STATE_CONNECTING) { + if (s->num_addr > 1) + free(s->addrs); + } + s->num_used--; + + } else if (s->state == SOCKET_STATE_ESTABLISHED_READY) { + events[num_events++] = (SocketEvent) { + SOCKET_EVENT_READY, + socket_to_handle(sm, s), + s->user + }; + } + } + + return num_events; +} + +int socket_manager_translate_events(SocketManager *sm, + SocketEvent *events, int max_events, struct pollfd *polled, + int num_polled) +{ + if (mutex_lock(&sm->mutex) < 0) + return -1; + + socket_manager_translate_events_nolock( + sm, events, max_events, polled, num_polled); + + if (mutex_unlock(&sm->mutex) < 0) + return -1; + return 0; +} + +static int resolve_connect_targets(ConnectTarget *targets, + int num_targets, AddressAndPort *resolved, int max_resolved) +{ + int num_resolved = 0; + for (int i = 0; i < num_targets; i++) { + switch (targets[i].type) { + case CONNECT_TARGET_NAME: + { + char portstr[16]; + int len = snprintf(portstr, sizeof(portstr), "%u", port); + if (len < 0 || len >= (int) sizeof(portstr)) + return -1; + + struct addrinfo hints = {0}; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + struct addrinfo *res = NULL; + int ret = getaddrinfo(pending_connect->hostname, portstr, &hints, &res); + if (ret != 0) + return -1; + + for (struct addrinfo *rp = res; rp; rp = rp->ai_next) { + if (rp->ai_family == AF_INET) { + IPv4 ipv4 = *(IPv4*) &((struct sockaddr_in*)rp->ai_addr)->sin_addr; + if (num_resolved < max_resolved) { + resolved[num_resolved].is_ipv4 = true; + resolved[num_resolved].ipv4 = ipv4; + resolved[num_resolved].port = targets[i].port; + num_resolved++; + } + } else if (rp->ai_family == AF_INET6) { + IPv6 ipv6 = *(IPv6*) &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr; + if (num_resolved < max_resolved) { + resolved[num_resolved].is_ipv4 = false; + resolved[num_resolved].ipv4 = ipv6; + resolved[num_resolved].port = targets[i].port; + num_resolved++; + } + } + } + + freeaddrinfo(res); + } + break; + case CONNECT_TARGET_IPV4: + if (num_resolved < max_resolved) { + resolved[num_resolved].is_ipv4 = true; + resolved[num_resolved].ipv4 = targets[i].ipv4; + resolved[num_resolved].port = targets[i].port; + num_resolved++; + } + break; + case CONNECT_TARGET_IPV6: + if (num_resolved < max_resolved) { + resolved[num_resolved].is_ipv4 = false; + resolved[num_resolved].ipv6 = targets[i].ipv6; + resolved[num_resolved].port = targets[i].port; + num_resolved++; + } + break; + } + } + return num_resolved; +} + +int socket_connect(SocketManager *sm, int num_targets, + ConnectTarget *targets, bool secure, void *user) +{ + if (sm->num_used == sm->max_used) + return -1; + + AddressAndPort resolved[MAX_CONNECT_TARGETS]; + int num_resolved = resolve_connect_targets( + targets, num_targets, resolved, MAX_CONNECT_TARGETS); + + if (num_resolved <= 0) + return -1; + + Socket *s = sm->sockets; + while (s->state != SOCKET_STATE_FREE) { + s++; + assert(s - sm->sockets < + sm->max_used); + } + + if (num_resolved == 1) { + s->num_addr = 1; + s->next_addr = 0; + s->addr = resolved[0]; + } else { + s->num_addr = num_resolved; + s->next_addr = 0; + s->addrs = malloc(num_resolved * sizeof(AddressAndPort)); + if (s->addrs == NULL) + return -1; + for (int i = 0; i < num_resolved; i++) + s->addrs[i] = resolved[i]; + } + + s->state = SOCKET_STATE_PENDING; + s->sock = NATIVE_SOCKET_INVALID; + s->user = user; + s->num_used++; + return 0; +} + +static int socket_recv_nolock(SocketManager *sm, SocketHandle handle, + char *dst, int max) +{ + Socket *s = handle_to_socket(sm, handle); + if (s == NULL) + return 0; + + if (s->state != SOCKET_STATE_ESTABLISHED_READY) { + s->state = SOCKET_STATE_DIED; + s->events = 0; + return 0; + } + + if (!is_secure(s)) { + int ret = recv(s->sock, dst, max, 0); + if (ret == 0) { + s->state = SOCKET_STATE_DIED; + s->events = 0; + } else if (ret < 0) { + if (would_block()) { + s->state = SOCKET_STATE_ESTABLISHED_WAIT; + s->events = POLLIN; + } else if (!interrupted()) { + s->state = SOCKET_STATE_DIED; + s->events = 0; + } + ret = 0; + } + return 0; + } else { +#ifdef HTTPS_ENABLED + int ret = SSL_read(s->ssl, dst, max); + if (ret <= 0) { + int err = SSL_get_error(s->ssl, ret); + if (err == SSL_ERROR_WANT_READ) { + s->state = SOCKET_STATE_ESTABLISHED_WAIT; + s->events = POLLIN; + } else if (err == SSL_ERROR_WANT_WRITE) { + s->state = SOCKET_STATE_ESTABLISHED_WAIT; + s->events = POLLOUT; + } else { + s->state = SOCKET_STATE_DIED; + s->events = 0; + } + ret = 0; + } + return ret; +#endif + } +} + +int socket_recv(SocketManager *sm, SocketHandle handle, + char *dst, int max) +{ + if (mutex_lock(&sm->mutex) < 0) + return -1; + + int ret = socket_recv_nolock(sm, handle, dst, max); + + if (mutex_unlock(&sm->mutex) < 0) + return -1; + return ret; +} + +static int socket_send_nolock(SocketManager *sm, SocketHandle handle, + char *src, int len) +{ + Socket *s = handle_to_socket(sm, handle); + if (s == NULL) + return 0; + + if (s->state != SOCKET_STATE_ESTABLISHED_READY) { + s->state = SOCKET_STATE_DIED; + s->events = 0; + return 0; + } + + if (!socket_secure(s)) { + int ret = send(s->sock, src, len, 0); + if (ret < 0) { + if (would_block()) { + s->state = SOCKET_STATE_ESTABLISHED_WAIT; + s->events = POLLOUT; + } else if (!interrupted()) { + s->state = SOCKET_DIED; + s->events = 0; + } + ret = 0; + } + return ret; + } else { +#ifdef HTTPS_ENABLED + int ret = SSL_write(s->ssl, src, len); + if (ret <= 0) { + int err = SSL_get_error(s->ssl, ret); + if (err == SSL_ERROR_WANT_READ) { + s->state = SOCKET_STATE_ESTABLISHED_WAIT; + s->events = POLLIN; + } else if (err == SSL_ERROR_WANT_WRITE) { + s->state = SOCKET_STATE_ESTABLISHED_WAIT; + s->events = POLLOUT; + } else { + s->state = SOCKET_STATE_DIED; + s->events = 0; + } + ret = 0; + } + return ret; +#endif + } +} + +int socket_send(SocketManager *sm, SocketHandle handle, + char *src, int len) +{ + if (mutex_lock(&sm->mutex) < 0) + return -1; + + int ret = socket_send_nolock(sm, handle, src, len); + + if (mutex_unlock(&sm->mutex) < 0) + return -1; + return ret; +} + +void socket_close(SocketManager *sm, SocketHandle handle) +{ + if (mutex_lock(&sm->mutex) < 0) + return -1; + + // TODO: maybe we don't want to always set to SHUTDOWN. What if the socket is DIED for instance? + s->state = SOCKET_STATE_SHUTDOWN; + s->events = 0; + socket_update(s); + + if (mutex_unlock(&sm->mutex) < 0) + return -1; + return 0; +} + +int socket_is_secure(SocketManager *sm, SocketHandle handle) +{ + if (mutex_lock(&sm->mutex) < 0) + return -1; + + Socket *s = handle_to_socket(sm, handle); + + int ret; + if (s == NULL) + ret = -1; + else + ret = is_secure(s); + + if (mutex_unlock(&sm->mutex) < 0) + return -1; + return ret; +} + +int socket_set_user(SocketManager *sm, SocketHandle handle, void *user) +{ + if (mutex_lock(&sm->mutex) < 0) + return -1; + + Socket *s = handle_to_socket(sm, handle); + if (s == NULL) + ret = -1; + else + s->user = user; + + if (mutex_unlock(&sm->mutex) < 0) + return -1; + return ret; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// src/byte_queue.c +//////////////////////////////////////////////////////////////////////////////////////// + +void byte_queue_init(ByteQueue *queue, uint32_t limit) +{ + queue->flags = 0; + queue->head = 0; + queue->size = 0; + queue->used = 0; + queue->curs = 0; + queue->limit = limit; + queue->data = NULL; + queue->read_target = NULL; +} + +// Deinitialize the queue +void byte_queue_free(ByteQueue *queue) +{ + if (queue->read_target) { + if (queue->read_target != queue->data) + free(queue->read_target); + queue->read_target = NULL; + queue->read_target_size = 0; + } + + free(queue->data); + queue->data = NULL; +} + +int byte_queue_error(ByteQueue *queue) +{ + return queue->flags & BYTE_QUEUE_ERROR; +} + +int byte_queue_empty(ByteQueue *queue) +{ + return queue->used == 0; +} + +int byte_queue_full(ByteQueue *queue) +{ + return queue->used == queue->limit; +} + +ByteView byte_queue_read_buf(ByteQueue *queue) +{ + if (queue->flags & BYTE_QUEUE_ERROR) + return (ByteView) {NULL, 0}; + + assert((queue->flags & BYTE_QUEUE_READ) == 0); + queue->flags |= BYTE_QUEUE_READ; + queue->read_target = queue->data; + queue->read_target_size = queue->size; + + if (queue->data == NULL) + return (ByteView) {NULL, 0}; + + return (ByteView) { queue->data + queue->head, queue->used }; +} + +void byte_queue_read_ack(ByteQueue *queue, uint32_t num) +{ + if (queue->flags & BYTE_QUEUE_ERROR) + return; + + if ((queue->flags & BYTE_QUEUE_READ) == 0) + return; + + queue->flags &= ~BYTE_QUEUE_READ; + + assert((uint32_t) num <= queue->used); + queue->head += (uint32_t) num; + queue->used -= (uint32_t) num; + queue->curs += (uint32_t) num; + + if (queue->read_target) { + if (queue->read_target != queue->data) + free(queue->read_target); + queue->read_target = NULL; + queue->read_target_size = 0; + } +} + +ByteView byte_queue_write_buf(ByteQueue *queue) +{ + if ((queue->flags & BYTE_QUEUE_ERROR) || queue->data == NULL) + return (ByteView) {NULL, 0}; + + assert((queue->flags & BYTE_QUEUE_WRITE) == 0); + queue->flags |= BYTE_QUEUE_WRITE; + + return (ByteView) { + queue->data + (queue->head + queue->used), + queue->size - (queue->head + queue->used), + }; +} + +void byte_queue_write_ack(ByteQueue *queue, uint32_t num) +{ + if (queue->flags & BYTE_QUEUE_ERROR) + return; + + if ((queue->flags & BYTE_QUEUE_WRITE) == 0) + return; + + queue->flags &= ~BYTE_QUEUE_WRITE; + queue->used += num; +} + +int byte_queue_write_setmincap(ByteQueue *queue, uint32_t mincap) +{ + // Sticky error + if (queue->flags & BYTE_QUEUE_ERROR) + return 0; + + // In general, the queue's contents look like this: + // + // size + // v + // [___xxxxxxxxxxxx________] + // ^ ^ ^ + // 0 head head + used + // + // This function needs to make sure that at least [mincap] + // bytes are available on the right side of the content. + // + // We have 3 cases: + // + // 1) If there is enough memory already, this function doesn't + // need to do anything. + // + // 2) If there isn't enough memory on the right but there is + // enough free memory if we cound the left unused region, + // then the content is moved back to the + // start of the buffer. + // + // 3) If there isn't enough memory considering both sides, this + // function needs to allocate a new buffer. + // + // If there are pending read or write operations, the application + // is holding pointers to the buffer, so we need to make sure + // to not invalidate them. The only real problem is pending reads + // since this function can only be called before starting a write + // opearation. + // + // To avoid invalidating the read pointer when we allocate a new + // buffer, we don't free the old buffer. Instead, we store the + // pointer in the "old" field so that the read ack function can + // free it. + // + // To avoid invalidating the pointer when we are moving back the + // content since there is enough memory at the start of the buffer, + // we just avoid that. Even if there is enough memory considering + // left and right free regions, we allocate a new buffer. + + assert((queue->flags & BYTE_QUEUE_WRITE) == 0); + + uint32_t total_free_space = queue->size - queue->used; + uint32_t free_space_after_data = queue->size - queue->used - queue->head; + + int moved = 0; + if (free_space_after_data < mincap) { + + if (total_free_space < mincap || (queue->read_target == queue->data)) { + // Resize required + + if (queue->used + mincap > queue->limit) { + queue->flags |= BYTE_QUEUE_ERROR; + return 0; + } + + uint32_t size; + if (queue->size > UINT32_MAX / 2) + size = UINT32_MAX; + else + size = 2 * queue->size; + + if (size < queue->used + mincap) + size = queue->used + mincap; + + if (size > queue->limit) + size = queue->limit; + + uint8_t *data = malloc(size); + if (!data) { + queue->flags |= BYTE_QUEUE_ERROR; + return 0; + } + + if (queue->used > 0) + memcpy(data, queue->data + queue->head, queue->used); + + if (queue->read_target != queue->data) + free(queue->data); + + queue->data = data; + queue->head = 0; + queue->size = size; + + } else { + // Move required + memmove(queue->data, queue->data + queue->head, queue->used); + queue->head = 0; + } + + moved = 1; + } + + return moved; +} + +void byte_queue_write(ByteQueue *queue, void *ptr, uint32_t len) +{ + byte_queue_write_setmincap(queue, len); + ByteView dst = byte_queue_write_buf(queue); + if (dst.ptr) { + memcpy(dst.ptr, ptr, len); + byte_queue_write_ack(queue, len); + } +} + +ByteQueueOffset byte_queue_offset(ByteQueue *queue) +{ + if (queue->flags & BYTE_QUEUE_ERROR) + return (ByteQueueOffset) { 0 }; + return (ByteQueueOffset) { queue->curs + queue->used }; +} + +void byte_queue_patch(ByteQueue *queue, ByteQueueOffset off, + void *src, uint32_t len) +{ + if (queue->flags & BYTE_QUEUE_ERROR) + return; + + // Check that the offset is in range + assert(off >= queue->curs && off - queue->curs < queue->used); + + // Check that the length is in range + assert(len <= queue->used - (off - queue->curs)); + + // Perform the patch + uint8_t *dst = queue->data + queue->head + (off - queue->curs); + memcpy(dst, src, len); +} + +uint32_t byte_queue_size_from_offset(ByteQueue *queue, ByteQueueOffset off) +{ + return queue->curs + queue->used - off; +} + +void byte_queue_remove_from_offset(ByteQueue *queue, ByteQueueOffset offset) +{ + if (queue->flags & BYTE_QUEUE_ERROR) + return; + + uint64_t num = (queue->curs + queue->used) - offset; + assert(num <= queue->used); + + queue->used -= num; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// src/cert.c +//////////////////////////////////////////////////////////////////////////////////////// + +#ifdef HTTPS_ENABLED + +static EVP_PKEY *generate_rsa_key_pair(int key_bits) +{ + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + if (!ctx) + return NULL; + + if (EVP_PKEY_keygen_init(ctx) <= 0) { + EVP_PKEY_CTX_free(ctx); + return NULL; + } + + if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, key_bits) <= 0) { + EVP_PKEY_CTX_free(ctx); + return NULL; + } + + EVP_PKEY *pkey = NULL; + if (EVP_PKEY_keygen(ctx, &pkey) <= 0) { + EVP_PKEY_CTX_free(ctx); + return NULL; + } + + EVP_PKEY_CTX_free(ctx); + return pkey; +} + +static X509 *create_certificate(EVP_PKEY *pkey, HTTP_String C, HTTP_String O, HTTP_String CN, int days) +{ + X509 *x509 = X509_new(); + if (!x509) + return NULL; + + // Set version (version 3) + X509_set_version(x509, 2); + + // Set serial number + ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); + + // Set validity period + X509_gmtime_adj(X509_get_notBefore(x509), 0); + X509_gmtime_adj(X509_get_notAfter(x509), 31536000L * days); // days * seconds_per_year + + // Set public key + X509_set_pubkey(x509, pkey); + + // Set subject name + X509_NAME *name = X509_get_subject_name(x509); + X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char*) C.ptr, C.len, -1, 0); + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char*) O.ptr, O.len, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*) CN.ptr, CN.len, -1, 0); + + // Set issuer name (same as subject for self-signed) + X509_set_issuer_name(x509, name); + + if (!X509_sign(x509, pkey, EVP_sha256())) { + X509_free(x509); + return NULL; + } + + return x509; +} + +static int save_private_key(EVP_PKEY *pkey, HTTP_String file) +{ + char copy[1<<10]; + if (file.len >= (int) sizeof(copy)) + return -1; + memcpy(copy, file.ptr, file.len); + copy[file.len] = '\0'; + + FILE *fp = fopen(copy, "wb"); + if (!fp) + return -1; + + // Write private key in PEM format + if (!PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL)) { + fclose(fp); + return -1; + } + + fclose(fp); + return 0; +} + +static int save_certificate(X509 *x509, HTTP_String file) +{ + char copy[1<<10]; + if (file.len >= (int) sizeof(copy)) + return -1; + memcpy(copy, file.ptr, file.len); + copy[file.len] = '\0'; + + FILE *fp = fopen(copy, "wb"); + if (!fp) + return -1; + + // Write certificate in PEM format + if (!PEM_write_X509(fp, x509)) { + fclose(fp); + return -1; + } + + fclose(fp); + return 0; +} + +int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN, + HTTP_String cert_file, HTTP_String key_file) +{ + EVP_PKEY *pkey = generate_rsa_key_pair(2048); + if (pkey == NULL) + return -1; + + X509 *x509 = create_certificate(pkey, C, O, CN, 1); + if (x509 == NULL) { + EVP_PKEY_free(pkey); + return -1; + } + + if (save_private_key(pkey, key_file) < 0) { + X509_free(x509); + EVP_PKEY_free(pkey); + return -1; + } + + if (save_certificate(x509, cert_file) < 0) { + X509_free(x509); + EVP_PKEY_free(pkey); + return -1; + } + + X509_free(x509); + EVP_PKEY_free(pkey); + return 0; +} + +#else + +int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN, + HTTP_String cert_file, HTTP_String key_file) +{ + (void) C; + (void) O; + (void) CN; + (void) cert_file; + (void) key_file; + return -1; +} + +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// src/client.c +//////////////////////////////////////////////////////////////////////////////////////// + +int http_client_init(HTTP_Client *client) +{ + client->num_conns = 0; + for (int i = 0; i < HTTP_CLIENT_CAPACITY; i++) + client->conns[i].state = HTTP_CLIENT_CONN_FREE; + + client->num_ready = 0; + client->ready_head = 0; + + if (socket_manager_init(&client->sockets, + client->socket_pool, HTTP_CLIENT_CAPACITY) < 0) + return -1; + return 0; +} + +void http_client_free(HTTP_Client *client) +{ + socket_manager_free(&client->sockets); + + for (int i = 0, j = 0; j < client->num_conns; i++) { + HTTP_ClientConn *conn = &client->conns[i]; + if (conn->state == HTTP_CLIENT_CONN_FREE) + continue; + j++; + + http_client_conn_free(conn); + } +} + +int http_client_wakeup(HTTP_Client *client) +{ + if (socket_manager_wakeup(&client->sockets) < 0) + return -1; + return 0; +} + +int http_client_get_builder(HTTP_Client *client, HTTP_RequestBuilder *builder) +{ + // TODO +} + +void http_request_builder_line(HTTP_RequestBuilder builder, xxxx) +{ + // TODO +} + +void http_request_builder_header(HTTP_RequestBuilder builder, String str) +{ + // TODO +} + +void http_request_builder_body(HTTP_RequestBuilder builder, String str) +{ + // TODO +} + +int http_request_builder_send(HTTP_RequestBuilder builder) +{ + // TODO +} + +int http_client_register_events(HTTP_Client *client, + struct pollfd *polled, int max_polled) +{ + return socket_manager_register_events( + &client->sockets, polled, max_polled); +} + +int http_client_process_events(HTTP_Client *client, + struct pollfd *polled, int num_polled) +{ + SocketEvent events[HTTP_CLIENT_CAPACITY]; + int num_events = socket_manager_translate_events( + &client->sockets, polled, num_polled); + + for (int i = 0; i < num_events; i++) { + + if (events[i].type == SOCKET_EVENT_DISCONNECT) { + + // TODO + + } else if (events[i].type == SOCKET_EVENT_READY) { + + // TODO + } + + // TODO + } + + return 0; +} + +bool http_client_next_response(HTTP_Client *client, + HTTP_Response **response); +{ + if (server->num_ready == 0) + return false; + + HTTP_ClientConn *conn = &client->conns[client->ready_head]; + client->ready_head = (client->ready_head + 1) % HTTP_CLIENT_CAPACITY; + client->num_ready--; + + assert(conn->state == HTTP_CLIENT_CONN_COMPLETE); + *response = &conn->response; + return true; +} + +void http_free_response(HTTP_Response *res) +{ + // TODO +} + //////////////////////////////////////////////////////////////////////////////////////// // src/server.c //////////////////////////////////////////////////////////////////////////////////////// +static void http_server_conn_init(HTTP_ServerConn *conn, + SocketHandle handle, uint32_t input_buffer_limit, + uint32_t output_buffer_limit) +{ + conn->state = HTTP_SERVER_CONN_BUFFERING; + conn->handle = handle; + byte_queue_init(&conn->input, input_buffer_limit); + byte_queue_init(&conn->output, output_buffer_limit); +} + +static void http_server_conn_free(HTTP_ServerConn *conn) +{ + byte_queue_free(&conn->output); + byte_queue_free(&conn->input); +} + int http_server_init(HTTP_Server *server) { + server->input_buffer_limit = 1<<20; + server->output_buffer_limit = 1<<20; + server->num_conns = 0; for (int i = 0; i < HTTP_SERVER_CAPACITY; i++) server->conns[i].state = HTTP_SERVER_CONN_FREE; @@ -2985,7 +3019,7 @@ int http_server_init(HTTP_Server *server) server->ready_head = 0; if (socket_manager_init(&server->sockets, - &server->socket_pool, HTTP_SERVER_CAPACITY) < 0) + server->socket_pool, HTTP_SERVER_CAPACITY) < 0) return -1; return 0; } @@ -3004,6 +3038,16 @@ void http_server_free(HTTP_Server *server) } } +void http_server_set_input_limit(HTTP_Server *server, uint32_t limit) +{ + server->input_buffer_limit = limit; +} + +void http_server_set_output_limit(HTTP_Server *server, uint32_t limit) +{ + server->output_buffer_limit = limit; +} + int http_server_listen_tcp(HTTP_Server *server, String addr, Port port) { @@ -3077,7 +3121,7 @@ check_request_buffer(HTTP_Server *server, HTTP_ServerConn *conn) // Ready assert(ret == 1); - conn->state = HTTP_SERVER_CONN_STATUS; + conn->state = HTTP_SERVER_CONN_WAIT_STATUS; conn->request_len = ret; conn->response_offset = byte_queue_offset(&conn->output); @@ -3109,7 +3153,7 @@ int http_server_process_events(HTTP_Server *server, struct pollfd *polled, int num_polled) { SocketEvent events[HTTP_SERVER_CAPACITY]; - int num_events = socket_manger_translate_events(&server->sockets, polled, num_polled); + int num_events = socket_manager_translate_events(&server->sockets, polled, num_polled); if (num_events < 0) return -1; @@ -3138,7 +3182,10 @@ int http_server_process_events(HTTP_Server *server, } conn = &server->conns[i]; - http_server_conn_init(conn, events[i].handle); + http_server_conn_init(conn, + events[i].handle, + server->input_buffer_limit, + server->output_buffer_limit); server->num_conns++; socket_set_user(&server->sockets, events[i].handle, conn); @@ -3202,11 +3249,11 @@ builder_to_conn(HTTP_ResponseBuilder builder) if (server == NULL) return NULL; - if (server->index > HTTP_SERVER_CAPACITY) + if (builder.index > HTTP_SERVER_CAPACITY) return NULL; - HTTP_ServerConn *conn = server->conns[server->index]; - if (conn->gen != builder.gen) + HTTP_ServerConn *conn = server->conns[builder.index]; + if (builder.gen != conn->gen) return NULL; return conn; @@ -3271,7 +3318,7 @@ void http_response_builder_body(HTTP_ResponseBuilder builder, String str) if (conn->state != HTTP_SERVER_CONN_WAIT_BODY) return; - byte_queue_write(&conn->output, str); + byte_queue_write(&conn->output, str.ptr, str.len); } void http_response_builder_send(HTTP_ResponseBuilder builder, String str) diff --git a/chttp.h b/chttp.h index 0b558c3..57dda4f 100644 --- a/chttp.h +++ b/chttp.h @@ -17,12 +17,14 @@ #define WIN32_LEAN_AND_MEAN #include #include +#include #else #include #include #include #include #include +#include #include #include #include @@ -82,6 +84,120 @@ void print_bytes(HTTP_String prefix, HTTP_String src); // TODO: comment #define HTTP_UNPACK(X) (X).len, (X).ptr +//////////////////////////////////////////////////////////////////////////////////////// +// src/parse.h +//////////////////////////////////////////////////////////////////////////////////////// + +#define HTTP_MAX_HEADERS 32 + +typedef struct { + unsigned int data; +} HTTP_IPv4; + +typedef struct { + unsigned short data[8]; +} HTTP_IPv6; + +typedef enum { + HTTP_HOST_MODE_VOID = 0, + HTTP_HOST_MODE_NAME, + HTTP_HOST_MODE_IPV4, + HTTP_HOST_MODE_IPV6, +} HTTP_HostMode; + +typedef struct { + HTTP_HostMode mode; + HTTP_String text; + union { + HTTP_String name; + HTTP_IPv4 ipv4; + HTTP_IPv6 ipv6; + }; +} HTTP_Host; + +typedef struct { + HTTP_String userinfo; + HTTP_Host host; + int port; +} HTTP_Authority; + +// ZII +typedef struct { + HTTP_String scheme; + HTTP_Authority authority; + HTTP_String path; + HTTP_String query; + HTTP_String fragment; +} HTTP_URL; + +typedef enum { + HTTP_METHOD_GET, + HTTP_METHOD_HEAD, + HTTP_METHOD_POST, + HTTP_METHOD_PUT, + HTTP_METHOD_DELETE, + HTTP_METHOD_CONNECT, + HTTP_METHOD_OPTIONS, + HTTP_METHOD_TRACE, + HTTP_METHOD_PATCH, +} HTTP_Method; + +typedef struct { + HTTP_String name; + HTTP_String value; +} HTTP_Header; + +typedef struct { + bool secure; + HTTP_Method method; + HTTP_URL url; + int minor; + int num_headers; + HTTP_Header headers[HTTP_MAX_HEADERS]; + HTTP_String body; +} HTTP_Request; + +typedef struct { + void* context; + int minor; + int status; + HTTP_String reason; + int num_headers; + HTTP_Header headers[HTTP_MAX_HEADERS]; + HTTP_String body; +} HTTP_Response; + +int http_parse_ipv4 (char *src, int len, HTTP_IPv4 *ipv4); +int http_parse_ipv6 (char *src, int len, HTTP_IPv6 *ipv6); +int http_parse_url (char *src, int len, HTTP_URL *url); +int http_parse_request (char *src, int len, HTTP_Request *req); +int http_parse_response (char *src, int len, HTTP_Response *res); + +int http_find_header (HTTP_Header *headers, int num_headers, HTTP_String name); + +HTTP_String http_get_cookie (HTTP_Request *req, HTTP_String name); +HTTP_String http_get_param (HTTP_String body, HTTP_String str, char *mem, int cap); +int http_get_param_i (HTTP_String body, HTTP_String str); + +// Checks whether the request was meant for the host with the given +// domain an port. If port is -1, the default value of 80 is assumed. +bool http_match_host(HTTP_Request *req, HTTP_String domain, int port); + +//////////////////////////////////////////////////////////////////////////////////////// +// src/thread.h +//////////////////////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 +typedef CRITICAL_SECTION Mutex; +#else +typedef pthread_mutex_t Mutex; +#endif + +int mutex_init(Mutex *mutex); +int mutex_free(Mutex *mutex); +int mutex_lock(Mutex *mutex); +int mutex_unlock(Mutex *mutex); + //////////////////////////////////////////////////////////////////////////////////////// // src/secure_context.h //////////////////////////////////////////////////////////////////////////////////////// @@ -222,10 +338,14 @@ typedef enum { // still pending. SOCKET_STATE_CONNECTING, - // The connection was estabished, but a TLS - // handshake may need to be performed. + // Outgoing connection was established, but + // a TLS handshake may need to be performed. SOCKET_STATE_CONNECTED, + // Incoming connection was established, but + // a TLS handshake may need to be performed. + SOCKET_STATE_ACCEPTED, + // The connection was esablished, but the user // wants to perform a read or write operation that // would block. @@ -419,20 +539,7 @@ void socket_close(SocketManager *sm, SocketHandle handle); int socket_is_secure(SocketManager *sm, SocketHandle handle); // Set the user pointer of a socket -void socket_set_user(SocketManager *sm, SocketHandle handle); - -//////////////////////////////////////////////////////////////////////////////////////// -// src/thread.h -//////////////////////////////////////////////////////////////////////////////////////// - -typedef struct { - char unused; // TODO -} Mutex; - -int mutex_init(Mutex *mutex); -int mutex_free(Mutex *mutex); -int mutex_lock(Mutex *mutex); -int mutex_unlock(Mutex *mutex); +void socket_set_user(SocketManager *sm, SocketHandle handle, void *user); //////////////////////////////////////////////////////////////////////////////////////// // src/byte_queue.h @@ -714,105 +821,6 @@ bool http_client_next_response(HTTP_Client *client, // again after this. void http_free_response(HTTP_Response *res); -//////////////////////////////////////////////////////////////////////////////////////// -// src/parse.h -//////////////////////////////////////////////////////////////////////////////////////// - -#define HTTP_MAX_HEADERS 32 - -typedef struct { - unsigned int data; -} HTTP_IPv4; - -typedef struct { - unsigned short data[8]; -} HTTP_IPv6; - -typedef enum { - HTTP_HOST_MODE_VOID = 0, - HTTP_HOST_MODE_NAME, - HTTP_HOST_MODE_IPV4, - HTTP_HOST_MODE_IPV6, -} HTTP_HostMode; - -typedef struct { - HTTP_HostMode mode; - HTTP_String text; - union { - HTTP_String name; - HTTP_IPv4 ipv4; - HTTP_IPv6 ipv6; - }; -} HTTP_Host; - -typedef struct { - HTTP_String userinfo; - HTTP_Host host; - int port; -} HTTP_Authority; - -// ZII -typedef struct { - HTTP_String scheme; - HTTP_Authority authority; - HTTP_String path; - HTTP_String query; - HTTP_String fragment; -} HTTP_URL; - -typedef enum { - HTTP_METHOD_GET, - HTTP_METHOD_HEAD, - HTTP_METHOD_POST, - HTTP_METHOD_PUT, - HTTP_METHOD_DELETE, - HTTP_METHOD_CONNECT, - HTTP_METHOD_OPTIONS, - HTTP_METHOD_TRACE, - HTTP_METHOD_PATCH, -} HTTP_Method; - -typedef struct { - HTTP_String name; - HTTP_String value; -} HTTP_Header; - -typedef struct { - bool secure; - HTTP_Method method; - HTTP_URL url; - int minor; - int num_headers; - HTTP_Header headers[HTTP_MAX_HEADERS]; - HTTP_String body; -} HTTP_Request; - -typedef struct { - void* context; - int minor; - int status; - HTTP_String reason; - int num_headers; - HTTP_Header headers[HTTP_MAX_HEADERS]; - HTTP_String body; -} HTTP_Response; - -int http_parse_ipv4 (char *src, int len, HTTP_IPv4 *ipv4); -int http_parse_ipv6 (char *src, int len, HTTP_IPv6 *ipv6); -int http_parse_url (char *src, int len, HTTP_URL *url); -int http_parse_request (char *src, int len, HTTP_Request *req); -int http_parse_response (char *src, int len, HTTP_Response *res); - -int http_find_header (HTTP_Header *headers, int num_headers, HTTP_String name); - -HTTP_String http_get_cookie (HTTP_Request *req, HTTP_String name); -HTTP_String http_get_param (HTTP_String body, HTTP_String str, char *mem, int cap); -int http_get_param_i (HTTP_String body, HTTP_String str); - -// Checks whether the request was meant for the host with the given -// domain an port. If port is -1, the default value of 80 is assumed. -bool http_match_host(HTTP_Request *req, HTTP_String domain, int port); - //////////////////////////////////////////////////////////////////////////////////////// // src/server.h //////////////////////////////////////////////////////////////////////////////////////// @@ -886,6 +894,11 @@ typedef struct { typedef struct { + // Size limit of the input and output buffer of each + // connection. + uint32_t input_buffer_limit; + uint32_t output_buffer_limit; + // Array of connections. The counter contains the // number of structs such that state=FREE. int num_conns; @@ -923,6 +936,11 @@ int http_server_init(HTTP_Server *server); // Release resources associated to the server. void http_server_free(HTTP_Server *server); +// Set input and output buffer size limit for any +// given connection. The default value is 1MB +void http_server_set_input_limit(HTTP_Server *server, uint32_t limit); +void http_server_set_output_limit(HTTP_Server *server, uint32_t limit); + // Enable listening for plain HTTP requests at the // specified interface. int http_server_listen_tcp(HTTP_Server *server, diff --git a/misc/amalg.py b/misc/amalg.py index 14c8409..537cae2 100644 --- a/misc/amalg.py +++ b/misc/amalg.py @@ -56,13 +56,13 @@ header = Amalgamator() header.append_text(desc) header.append_file("src/includes.h") header.append_file("src/basic.h") +header.append_file("src/parse.h") +header.append_file("src/thread.h") header.append_file("src/secure_context.h") header.append_file("src/socket.h") -header.append_file("src/thread.h") header.append_file("src/byte_queue.h") header.append_file("src/cert.h") header.append_file("src/client.h") -header.append_file("src/parse.h") header.append_file("src/server.h") header.append_text(license) header.save("chttp.h") @@ -74,13 +74,13 @@ source.append_text("#ifndef HTTP_DONT_INCLUDE\n") source.append_text('#include "chttp.h"\n') source.append_text("#endif\n") source.append_file("src/basic.c") +source.append_file("src/parse.c") +source.append_file("src/thread.c") source.append_file("src/secure_context.c") source.append_file("src/socket.c") -source.append_file("src/thread.c") source.append_file("src/byte_queue.c") source.append_file("src/cert.c") source.append_file("src/client.c") -source.append_file("src/parse.c") source.append_file("src/server.c") header.append_text(license) source.save("chttp.c") diff --git a/src/includes.h b/src/includes.h index 336caac..304be8d 100644 --- a/src/includes.h +++ b/src/includes.h @@ -9,6 +9,7 @@ #define WIN32_LEAN_AND_MEAN #include #include +#include #else #include #include diff --git a/src/server.c b/src/server.c index a5b5ce7..e160909 100644 --- a/src/server.c +++ b/src/server.c @@ -1,6 +1,25 @@ +static void http_server_conn_init(HTTP_ServerConn *conn, + SocketHandle handle, uint32_t input_buffer_limit, + uint32_t output_buffer_limit) +{ + conn->state = HTTP_SERVER_CONN_BUFFERING; + conn->handle = handle; + byte_queue_init(&conn->input, input_buffer_limit); + byte_queue_init(&conn->output, output_buffer_limit); +} + +static void http_server_conn_free(HTTP_ServerConn *conn) +{ + byte_queue_free(&conn->output); + byte_queue_free(&conn->input); +} + int http_server_init(HTTP_Server *server) { + server->input_buffer_limit = 1<<20; + server->output_buffer_limit = 1<<20; + server->num_conns = 0; for (int i = 0; i < HTTP_SERVER_CAPACITY; i++) server->conns[i].state = HTTP_SERVER_CONN_FREE; @@ -9,7 +28,7 @@ int http_server_init(HTTP_Server *server) server->ready_head = 0; if (socket_manager_init(&server->sockets, - &server->socket_pool, HTTP_SERVER_CAPACITY) < 0) + server->socket_pool, HTTP_SERVER_CAPACITY) < 0) return -1; return 0; } @@ -28,6 +47,16 @@ void http_server_free(HTTP_Server *server) } } +void http_server_set_input_limit(HTTP_Server *server, uint32_t limit) +{ + server->input_buffer_limit = limit; +} + +void http_server_set_output_limit(HTTP_Server *server, uint32_t limit) +{ + server->output_buffer_limit = limit; +} + int http_server_listen_tcp(HTTP_Server *server, String addr, Port port) { @@ -101,7 +130,7 @@ check_request_buffer(HTTP_Server *server, HTTP_ServerConn *conn) // Ready assert(ret == 1); - conn->state = HTTP_SERVER_CONN_STATUS; + conn->state = HTTP_SERVER_CONN_WAIT_STATUS; conn->request_len = ret; conn->response_offset = byte_queue_offset(&conn->output); @@ -133,7 +162,7 @@ int http_server_process_events(HTTP_Server *server, struct pollfd *polled, int num_polled) { SocketEvent events[HTTP_SERVER_CAPACITY]; - int num_events = socket_manger_translate_events(&server->sockets, polled, num_polled); + int num_events = socket_manager_translate_events(&server->sockets, polled, num_polled); if (num_events < 0) return -1; @@ -162,7 +191,10 @@ int http_server_process_events(HTTP_Server *server, } conn = &server->conns[i]; - http_server_conn_init(conn, events[i].handle); + http_server_conn_init(conn, + events[i].handle, + server->input_buffer_limit, + server->output_buffer_limit); server->num_conns++; socket_set_user(&server->sockets, events[i].handle, conn); @@ -226,11 +258,11 @@ builder_to_conn(HTTP_ResponseBuilder builder) if (server == NULL) return NULL; - if (server->index > HTTP_SERVER_CAPACITY) + if (builder.index > HTTP_SERVER_CAPACITY) return NULL; - HTTP_ServerConn *conn = server->conns[server->index]; - if (conn->gen != builder.gen) + HTTP_ServerConn *conn = server->conns[builder.index]; + if (builder.gen != conn->gen) return NULL; return conn; @@ -295,7 +327,7 @@ void http_response_builder_body(HTTP_ResponseBuilder builder, String str) if (conn->state != HTTP_SERVER_CONN_WAIT_BODY) return; - byte_queue_write(&conn->output, str); + byte_queue_write(&conn->output, str.ptr, str.len); } void http_response_builder_send(HTTP_ResponseBuilder builder, String str) diff --git a/src/server.h b/src/server.h index 4a6fb9b..2d16d56 100644 --- a/src/server.h +++ b/src/server.h @@ -68,6 +68,11 @@ typedef struct { typedef struct { + // Size limit of the input and output buffer of each + // connection. + uint32_t input_buffer_limit; + uint32_t output_buffer_limit; + // Array of connections. The counter contains the // number of structs such that state=FREE. int num_conns; @@ -105,6 +110,11 @@ int http_server_init(HTTP_Server *server); // Release resources associated to the server. void http_server_free(HTTP_Server *server); +// Set input and output buffer size limit for any +// given connection. The default value is 1MB +void http_server_set_input_limit(HTTP_Server *server, uint32_t limit); +void http_server_set_output_limit(HTTP_Server *server, uint32_t limit); + // Enable listening for plain HTTP requests at the // specified interface. int http_server_listen_tcp(HTTP_Server *server, diff --git a/src/socket.c b/src/socket.c index 6453f89..817cbc7 100644 --- a/src/socket.c +++ b/src/socket.c @@ -219,6 +219,15 @@ int socket_manager_add_certificate(SocketManager *sm, return 0; } +static bool is_secure(Socket *s) +{ +#ifdef HTTPS_ENABLED + return (s->ssl != NULL); +#else + return false; +#endif +} + static bool connect_pending(void) { #ifdef _WIN32 @@ -228,17 +237,6 @@ static bool connect_pending(void) #endif } -static bool -connect_failed_because_of_peer(void) -{ -#ifdef _WIN32 - int err = WSAGetLastError(); -#else - int err = errno; -#endif - return connect_failed_because_of_peer_2(err); -} - static bool connect_failed_because_of_peer_2(int err) { @@ -255,10 +253,21 @@ connect_failed_because_of_peer_2(int err) #endif } +static bool +connect_failed_because_of_peer(void) +{ +#ifdef _WIN32 + int err = WSAGetLastError(); +#else + int err = errno; +#endif + return connect_failed_because_of_peer_2(err); +} + // This function moves the socket state machine // to the next state until an I/O event would // be required to continue. -static void socket_update(Socket *socket) +static void socket_update(Socket *s) { // Each case of this switch encodes a state transition. // If the evaluated case requires a given I/O event to @@ -269,7 +278,7 @@ static void socket_update(Socket *socket) bool again; do { again = false; - switch (socket->state) { + switch (s->state) { case SOCKET_STATE_PENDING: { // This point may be reached because @@ -280,17 +289,17 @@ static void socket_update(Socket *socket) // If (2) is true, we have some resources // to clean up. - if (socket->sock != NATIVE_SOCKET_INVALID) { + if (s->sock != NATIVE_SOCKET_INVALID) { // This is not the first attempt - CLOSE_NATIVE_SOCKET(socket->sock); + CLOSE_NATIVE_SOCKET(s->sock); - socket->next_addr++; - if (socket->next_addr == socket->num_addr) { + s->next_addr++; + if (s->next_addr == s->num_addr) { assert(0); // TODO } } - AddressAndPort addr = socket->addrs[socket->next_addr]; + AddressAndPort addr = s->addrs[s->next_addr]; int family = (addr.is_ipv4 ? AF_INET : AF_INET6); NATIVE_SOCKET sock = socket(family, SOCK_STREAM, 0); @@ -307,41 +316,39 @@ static void socket_update(Socket *socket) struct sockaddr_in buf; buf.sin_family = AF_INET; buf.sin_port = htons(addr.port); - memset(&buf.sin_addr, &addr.ipv4, sizeof(IPv4)); - ret = connect(sock, (struct sockaddr*) &connect_buf, - sizeof(connect_buf)); + memcpy(&buf.sin_addr, &addr.ipv4, sizeof(IPv4)); + ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf)); } else { struct sockaddr_in6 buf; buf.sin6_family = AF_INET6; buf.sin6_port = htons(addr.port); - memset(&buf.sin6_addr, &addr.ipv6, sizeof(IPv6)); - ret = connect(sock, (struct sockaddr*) &connect_buf, - sizeof(connect_buf)); + memcpy(&buf.sin6_addr, &addr.ipv6, sizeof(IPv6)); + ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf)); } if (ret == 0) { // Connect resolved immediately - socket->sock = sock; - socket->state = SOCKET_STATE_CONNECTED; - socket->events = 0; + s->sock = sock; + s->state = SOCKET_STATE_CONNECTED; + s->events = 0; again = true; } else if (connect_pending()) { // Connect is pending, which is expected - socket->sock = sock; - socket->state = SOCKET_STATE_CONNECTING; - socket->events = POLLOUT; + s->sock = sock; + s->state = SOCKET_STATE_CONNECTING; + s->events = POLLOUT; } else if (connect_failed_because_of_peer()) { // Conenct failed due to the peer host // We should try a different address. - socket->sock = sock; - socket->state = SOCKET_STATE_PENDING; - socket->events = 0; + s->sock = sock; + s->state = SOCKET_STATE_PENDING; + s->events = 0; again = true; } else { // An error occurred that we can't recover from - socket->sock = sock; - socket->state = SOCKET_STATE_DIED; - socket->events = 0; + s->sock = sock; + s->state = SOCKET_STATE_DIED; + s->events = 0; again = true; } } @@ -354,23 +361,23 @@ static void socket_update(Socket *socket) int err = 0; socklen_t len = sizeof(err); - if (getsockopt(socket->sock, SOL_SOCKET, SO_ERROR, (void*) &err, &len) < 0) { + if (getsockopt(s->sock, SOL_SOCKET, SO_ERROR, (void*) &err, &len) < 0) { assert(0); // TODO } if (err == 0) { // Connection succeded - socket->state = SOCKET_STATE_CONNECTED; - socket->events = 0; + s->state = SOCKET_STATE_CONNECTED; + s->events = 0; again = true; } else if (connect_failed_because_of_peer_2(err)) { // Try the next address - socket->state = SOCKET_STATE_PENDING; - socket->events = 0; + s->state = SOCKET_STATE_PENDING; + s->events = 0; again = true; } else { - socket->state = SOCKET_STATE_DIED; - socket->events = 0; + s->state = SOCKET_STATE_DIED; + s->events = 0; } } break; @@ -380,12 +387,25 @@ static void socket_update(Socket *socket) // We managed to connect to the peer. // We can free the target array if it // was allocated dynamically. - if (socket->num_addr > 1) - free(socket->addrs); + if (s->num_addr > 1) + free(s->addrs); - if (!is_secure(socket)) { - socket->events = 0; - socket->state = SOCKET_STATE_ESTABLISHED_READY; + if (!is_secure(s)) { + s->events = 0; + s->state = SOCKET_STATE_ESTABLISHED_READY; + } else { +#ifdef HTTPS_ENABLED + assert(0); // TODO +#endif + } + } + break; + + case SOCKET_STATE_ACCEPTED: + { + if (!is_secure(s)) { + s->state = SOCKET_STATE_ESTABLISHED_READY; + s->events = 0; } else { #ifdef HTTPS_ENABLED assert(0); // TODO @@ -395,15 +415,15 @@ static void socket_update(Socket *socket) break; case SOCKET_STATE_ESTABLISHED_WAIT: - socket->state = SOCKET_STATE_ESTABLISHED_READY; - socket->events = 0; + s->state = SOCKET_STATE_ESTABLISHED_READY; + s->events = 0; break; case SOCKET_STATE_SHUTDOWN: { - if (!is_secure(socket)) { - socket->state = SOCKET_STATE_DIED; - socket->events = 0; + if (!is_secure(s)) { + s->state = SOCKET_STATE_DIED; + s->events = 0; } else { #ifdef HTTPS_ENABLED assert(0); // TODO @@ -544,7 +564,7 @@ static int socket_manager_translate_events_nolock( bool secure = (polled[i].fd == sm->secure_sock); Socket *s = sm->sockets; - while (s->type != SOCKET_FREE) { + while (s->state != SOCKET_STATE_FREE) { s++; assert(s - sm->sockets < + sm->max_used); } @@ -570,7 +590,7 @@ static int socket_manager_translate_events_nolock( continue; } - pool->num_used++; + sm->num_used++; } else if (polled[i].fd == sm->wait_sock) { @@ -584,7 +604,7 @@ static int socket_manager_translate_events_nolock( for (int i = 0, j = 0; j < sm->num_used; i++) { Socket *s = &sm->sockets[i]; - if (s->state == SOCKET_FREE) + if (s->state == SOCKET_STATE_FREE) continue; j++; @@ -600,7 +620,7 @@ static int socket_manager_translate_events_nolock( }; // Free resources associated to socket - s->state = SOCKET_FREE; + s->state = SOCKET_STATE_FREE; if (s->sock != NATIVE_SOCKET_INVALID) CLOSE_NATIVE_SOCKET(s->sock); if (s->sock == SOCKET_STATE_PENDING || @@ -717,7 +737,7 @@ int socket_connect(SocketManager *sm, int num_targets, return -1; Socket *s = sm->sockets; - while (s->type != SOCKET_FREE) { + while (s->state != SOCKET_STATE_FREE) { s++; assert(s - sm->sockets < + sm->max_used); } @@ -736,7 +756,7 @@ int socket_connect(SocketManager *sm, int num_targets, s->addrs[i] = resolved[i]; } - s->type = SOCKET_STATE_PENDING; + s->state = SOCKET_STATE_PENDING; s->sock = NATIVE_SOCKET_INVALID; s->user = user; s->num_used++; @@ -888,25 +908,20 @@ int socket_is_secure(SocketManager *sm, SocketHandle handle) if (mutex_lock(&sm->mutex) < 0) return -1; -#ifdef HTTPS_ENABLED Socket *s = handle_to_socket(sm, handle); int ret; if (s == NULL) ret = -1; - else { - ret = (s->ssl != NULL); - } -#else - int ret = 0; -#endif + else + ret = is_secure(s); if (mutex_unlock(&sm->mutex) < 0) return -1; return ret; } -int socket_set_user(SocketManager *sm, SocketHandle handle) +int socket_set_user(SocketManager *sm, SocketHandle handle, void *user) { if (mutex_lock(&sm->mutex) < 0) return -1; diff --git a/src/socket.h b/src/socket.h index 251c648..ec656c2 100644 --- a/src/socket.h +++ b/src/socket.h @@ -102,10 +102,14 @@ typedef enum { // still pending. SOCKET_STATE_CONNECTING, - // The connection was estabished, but a TLS - // handshake may need to be performed. + // Outgoing connection was established, but + // a TLS handshake may need to be performed. SOCKET_STATE_CONNECTED, + // Incoming connection was established, but + // a TLS handshake may need to be performed. + SOCKET_STATE_ACCEPTED, + // The connection was esablished, but the user // wants to perform a read or write operation that // would block. @@ -299,4 +303,4 @@ void socket_close(SocketManager *sm, SocketHandle handle); int socket_is_secure(SocketManager *sm, SocketHandle handle); // Set the user pointer of a socket -void socket_set_user(SocketManager *sm, SocketHandle handle); +void socket_set_user(SocketManager *sm, SocketHandle handle, void *user); diff --git a/src/thread.h b/src/thread.h index dc6da6a..4debe69 100644 --- a/src/thread.h +++ b/src/thread.h @@ -1,7 +1,9 @@ -typedef struct { - char unused; // TODO -} Mutex; +#ifdef _WIN32 +typedef CRITICAL_SECTION Mutex; +#else +typedef pthread_mutex_t Mutex; +#endif int mutex_init(Mutex *mutex); int mutex_free(Mutex *mutex);