From c3f4a52b6d14598c3a7566bdfbdf6ef125a1c23e Mon Sep 17 00:00:00 2001 From: cozis Date: Wed, 1 Nov 2023 21:33:49 +0100 Subject: [PATCH] simpler socket api. Now the blocking of functions and error codes aren't function parameters but attributes of the socket object with their respective getters and setters --- examples/echo.c | 20 +- examples/http/main.c | 59 +----- examples/http/xhttp.c | 73 +++---- examples/http/xhttp.h | 5 +- src/microtcp.c | 439 +++++++++++++++++++++++++----------------- src/microtcp.h | 30 ++- 6 files changed, 322 insertions(+), 304 deletions(-) diff --git a/examples/echo.c b/examples/echo.c index 6ebcd3a..ed4bae9 100644 --- a/examples/echo.c +++ b/examples/echo.c @@ -3,7 +3,6 @@ int main(void) { - microtcp_errcode_t err; microtcp_t *mtcp = microtcp_create("10.0.0.5", "10.0.0.4", NULL, NULL); if (mtcp == NULL) { fprintf(stderr, "Error: Couldn't create MicroTCP instance\n"); @@ -11,24 +10,27 @@ int main(void) } uint16_t port = 8081; - microtcp_socket_t *server = microtcp_open(mtcp, port, &err); + microtcp_socket_t *server = microtcp_open(mtcp, port); if (server == NULL) { - fprintf(stderr, "Error: %s\n", microtcp_strerror(err)); + fprintf(stderr, "Error: %s\n", microtcp_strerror(microtcp_get_error(mtcp))); microtcp_destroy(mtcp); return -1; } while (1) { - microtcp_socket_t *client = microtcp_accept(server, false, &err); + + microtcp_socket_t *client = microtcp_accept(server); if (client == NULL) { - fprintf(stderr, "Error: %s\n", microtcp_strerror(err)); + fprintf(stderr, "Error: %s\n", microtcp_strerror(microtcp_get_socket_error(server))); break; } - + char buffer[1024]; - size_t num = microtcp_recv(client, buffer, sizeof(buffer), false, NULL); - microtcp_send(client, "echo: ", 6, false, NULL); - microtcp_send(client, buffer, num, false, NULL); + int num = microtcp_recv(client, buffer, sizeof(buffer)); + if (num > 0) { + microtcp_send(client, "echo: ", 6); + microtcp_send(client, buffer, num); + } microtcp_close(client); } diff --git a/examples/http/main.c b/examples/http/main.c index 4d38321..bdb6b48 100644 --- a/examples/http/main.c +++ b/examples/http/main.c @@ -5,8 +5,6 @@ #include #include "xhttp.h" -static xh_handle handle; - static void callback(xh_request *req, xh_response *res, void *userp) { (void) req; @@ -16,63 +14,10 @@ static void callback(xh_request *req, xh_response *res, void *userp) res->body.str = "Hello, world!"; xh_header_add(res, "Content-Type", "text/plain"); } -/* -static void handle_sigterm(int signum) -{ - (void) signum; - xh_quit(handle); -} -*/ + int main(void) { -/* - signal(SIGTERM, handle_sigterm); - signal(SIGINT, handle_sigterm); - -#ifndef _WIN32 - signal(SIGQUIT, handle_sigterm); -#endif -*/ - char ip[] = "10.0.0.4"; - char mac[] = "00:01:00:01:00:00"; - - struct device *dev = tuntap_init(); - if (!dev) { - fprintf(stderr, "Error: Couldn't initialize the TAP library\n"); - return -1; - } - - // This must be set AFTER tuntap_init because - // it sets the callback function to the default - // callback which writes to stderr. - //tuntap_log_set_cb(NULL); - - int netmask = 24; // TODO: Make this configurable - - if (tuntap_start(dev, TUNTAP_MODE_ETHERNET, TUNTAP_ID_ANY)) { - fprintf(stderr, "Error: Couldn't set up the TAP device\n"); - tuntap_release(dev); - return -1; - } - - tuntap_set_ip(dev, "10.0.0.5", netmask); - tuntap_set_hwaddr(dev, mac); - - if (tuntap_up(dev)) { - fprintf(stderr, "Error: Couldn't activate the TAP device\n"); - tuntap_release(dev); - } - - microhttp_config_t config = { - .userp = dev, - .ip = ip, - .mac = mac, - .recv_frame = (int(*)(void*, void*, size_t)) tuntap_read, - .send_frame = (int(*)(void*, const void*, size_t)) tuntap_write, - }; - - const char *error = xhttp(80, callback, NULL, &handle, config); - tuntap_release(dev); + const char *error = xhttp(80, callback, NULL); if(error != NULL) { fprintf(stderr, "Error: %s\n", error); return 1; diff --git a/examples/http/xhttp.c b/examples/http/xhttp.c index 0711341..c598210 100644 --- a/examples/http/xhttp.c +++ b/examples/http/xhttp.c @@ -512,11 +512,9 @@ static void req_deinit(xh_request *req) static conn_t *accept_connection(context_t *ctx) { - microtcp_errcode_t errcode; - microtcp_socket_t *accepted_sock = microtcp_accept(ctx->sock, true, &errcode); - - if(accepted_sock == NULL) - return NULL; // Failed to accept. + microtcp_socket_t *accepted_sock = microtcp_accept(ctx->sock); + if(accepted_sock == NULL) return NULL; + microtcp_set_blocking(accepted_sock, false); if(ctx->freelist == NULL) { @@ -953,11 +951,12 @@ static bool upload(conn_t *conn) while(sent < total) { - microtcp_errcode_t errcode; - size_t n = microtcp_send(conn->sock, conn->out.data + sent, total - sent, true, &errcode); - - if(n == 0) + int n = microtcp_send(conn->sock, conn->out.data + sent, total - sent); + if(n <= 0) { + microtcp_errcode_t errcode = microtcp_get_socket_error(conn->sock); + microtcp_clear_socket_error(conn->sock); + if (errcode == MICROTCP_ERRCODE_WOULDBLOCK) break; @@ -1254,26 +1253,24 @@ static void when_data_is_ready_to_be_read(context_t *ctx, conn_t *conn) assert(b->size > b->used); - microtcp_errcode_t errcode; - size_t n = microtcp_recv(conn->sock, b->data + b->used, b->size - b->used, true, &errcode); - - if(n == 0) + int n = microtcp_recv(conn->sock, b->data + b->used, b->size - b->used); + if (n == 0) { + // Peer disconnected. + close_connection(ctx, conn); + return; + } + + if(n < 0) { - // Peer disconnected or an error occurred + microtcp_errcode_t errcode = microtcp_get_socket_error(conn->sock); + microtcp_clear_socket_error(conn->sock); if(errcode == MICROTCP_ERRCODE_WOULDBLOCK) break; // Done downloading. - if (errcode != MICROTCP_ERRCODE_NONE) - { #ifdef DEBUG - fprintf(stderr, "XHTTP :: %s\n", microtcp_strerror(errcode)); + fprintf(stderr, "XHTTP :: %s\n", microtcp_strerror(errcode)); #endif - // An error occurred. - close_connection(ctx, conn); - return; - } - - // Peer disconnected. + // An error occurred. close_connection(ctx, conn); return; } @@ -1407,31 +1404,23 @@ void xh_quit(xh_handle handle) ctx->exiting = 1; } -static const char *init(context_t *context, unsigned short port, - microhttp_config_t config) +static const char *init(context_t *context, unsigned short port) { - microtcp_callbacks_t callbacks = { - .data = config.userp, - .free = NULL, - .recv = config.recv_frame, - .send = config.send_frame, - }; - microtcp_t *mtcp = microtcp_create_using_callbacks(config.ip, config.mac, callbacks); + microtcp_t *mtcp = microtcp_create("10.0.0.5", "10.0.0.4", NULL, NULL); if (!mtcp) return "Failed to initialize TCP"; context->mtcp = mtcp; - microtcp_errcode_t errcode; - - context->sock = microtcp_open(mtcp, port, &errcode); + context->sock = microtcp_open(mtcp, port); if (!context->sock) - return microtcp_strerror(errcode); + return microtcp_strerror(microtcp_get_error(mtcp)); + microtcp_set_blocking(context->sock, false); context->mux = microtcp_mux_create(mtcp); if (!context->mux) { microtcp_close(context->sock); - return microtcp_strerror(errcode); + return "Couldn't craete mux"; } if (!microtcp_mux_register(context->mux, context->sock, MICROTCP_MUX_ACCEPT, NULL)) @@ -1454,27 +1443,23 @@ static const char *init(context_t *context, unsigned short port, return NULL; } -const char *xhttp(unsigned short port, xh_callback callback, - void *userp, xh_handle *handle, - microhttp_config_t config) +const char *xhttp(unsigned short port, xh_callback callback, xh_handle *handle) { context_t context; - const char *error = init(&context, port, config); + const char *error = init(&context, port); if(error != NULL) return error; context.callback = callback; - context.userp = userp; + context.userp = NULL; if(handle) *handle = &context; while(!context.exiting) { - microtcp_step(context.mtcp); - microtcp_muxevent_t ev; if (!microtcp_mux_wait(context.mux, &ev)) continue; diff --git a/examples/http/xhttp.h b/examples/http/xhttp.h index 9c6ba00..aef9cf4 100644 --- a/examples/http/xhttp.h +++ b/examples/http/xhttp.h @@ -58,9 +58,7 @@ typedef struct { typedef void (*xh_callback)(xh_request*, xh_response*, void*); -const char *xhttp(unsigned short port, xh_callback callback, - void *userp, xh_handle *handle, - microhttp_config_t config); +const char *xhttp(unsigned short port, xh_callback callback, xh_handle *handle); void xh_quit(xh_handle handle); void xh_header_add(xh_response *res, const char *name, const char *valfmt, ...); @@ -71,7 +69,6 @@ _Bool xh_header_cmp(const char *a, const char *b); int xh_urlcmp(const char *URL, const char *fmt, ...); int xh_vurlcmp(const char *URL, const char *fmt, va_list va); - #define xh_string_new(s, l) \ ((xh_string) { (s), ((int) (l)) < 0 ? (int) strlen(s) : (int) (l) }) diff --git a/src/microtcp.c b/src/microtcp.c index 924d405..be2584e 100644 --- a/src/microtcp.c +++ b/src/microtcp.c @@ -1,6 +1,7 @@ -#include // time() +#include #include -#include // strerror() +#include +#include #include #include #include @@ -19,7 +20,6 @@ #define MICROTCP_DEBUG_LOG(...) do {} while (0); #endif - typedef struct mux_entry_t mux_entry_t; struct mux_entry_t { mux_entry_t **mux_prev; @@ -63,10 +63,12 @@ struct microtcp_socket_t { microtcp_t *mtcp; microtcp_socket_t *prev; microtcp_socket_t *next; + microtcp_errcode_t errcode; socket_type_t type; + bool block; // If true, operations on this socket will block execution, else they wont union { tcp_listener_t *listener; - tcp_connection_t *connection; + tcp_connection_t *conn; }; union { @@ -84,6 +86,8 @@ struct microtcp_t { uint64_t last_update_time_ms; + microtcp_errcode_t errcode; + bool thread_should_stop; thrd_t thread_id; mtx_t lock; @@ -107,10 +111,31 @@ struct microtcp_t { microtcp_socket_t socket_pool[MICROTCP_MAX_SOCKETS]; }; +microtcp_errcode_t microtcp_get_error(microtcp_t *mtcp) +{ + return mtcp->errcode; +} + +void microtcp_clear_error(microtcp_t *mtcp) +{ + mtcp->errcode = MICROTCP_ERRCODE_NONE; +} + +microtcp_errcode_t microtcp_get_socket_error(microtcp_socket_t *sock) +{ + return sock->errcode; +} + +void microtcp_clear_socket_error(microtcp_socket_t *sock) +{ + sock->errcode = MICROTCP_ERRCODE_NONE; +} + const char *microtcp_strerror(microtcp_errcode_t errcode) { switch (errcode) { case MICROTCP_ERRCODE_NONE: return "No error occurred"; + case MICROTCP_ERRCODE_NOCLEAR: return "Uncleared error"; case MICROTCP_ERRCODE_SOCKETLIMIT: return "Can't create a socket because the socket limit per microtcp instance was reached"; case MICROTCP_ERRCODE_TCPERROR: return "An error occurred at the TCP layer"; case MICROTCP_ERRCODE_BADCONDVAR: return "Condition variable error"; @@ -366,11 +391,17 @@ process_packet(microtcp_t *mtcp, const void *packet, size_t len) } } -void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len) +bool microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len) { + if (mtcp->errcode != MICROTCP_ERRCODE_NONE) { + mtcp->errcode = MICROTCP_ERRCODE_NOCLEAR; + return false; + } + mtx_lock(&mtcp->lock); process_packet(mtcp, packet, len); mtx_unlock(&mtcp->lock); + return true; } static uint64_t get_time_in_ms(void) @@ -380,8 +411,13 @@ static uint64_t get_time_in_ms(void) return t.tv_sec * 1000 + t.tv_nsec / 1000000; } -void microtcp_step(microtcp_t *mtcp) +bool microtcp_step(microtcp_t *mtcp) { + if (mtcp->errcode != MICROTCP_ERRCODE_NONE) { + mtcp->errcode = MICROTCP_ERRCODE_NOCLEAR; + return false; + } + char packet[1024]; // This buffer is the bottleneck for the // maximum packet size that can be processed. @@ -390,7 +426,7 @@ void microtcp_step(microtcp_t *mtcp) // threads the ability to progress in the mean time. int size = mtcp->callbacks.recv(mtcp->callbacks.data, packet, sizeof(packet)); if (size < 0) - return; + return true; uint64_t current_time_ms = get_time_in_ms(); @@ -408,6 +444,7 @@ void microtcp_step(microtcp_t *mtcp) } } mtx_unlock(&mtcp->lock); + return true; } static int loop(void *data) @@ -438,6 +475,7 @@ microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac, if (mtcp == NULL) return NULL; + mtcp->errcode = MICROTCP_ERRCODE_NONE; mtcp->ip = parsed_ip; mtcp->mac = parsed_mac; mtcp->callbacks = callbacks; @@ -665,48 +703,51 @@ static void listener_event_callback(void *data, tcp_listenevent_t event) signal_events_to_muxes_associated_to_socket(socket, flags); } -microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port, - microtcp_errcode_t *errcode) +microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port) { - microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE; + if (mtcp->errcode != MICROTCP_ERRCODE_NONE) { + mtcp->errcode = MICROTCP_ERRCODE_NOCLEAR; + return NULL; + } + microtcp_socket_t *socket = NULL; mtx_lock(&mtcp->lock); - { - socket = pop_socket_struct_from_free_list(mtcp); - if (!socket) { - errcode2 = MICROTCP_ERRCODE_SOCKETLIMIT; - goto unlock_and_exit; // Socket limit reached - } - - tcp_listener_t *listener = tcp_listener_create(&mtcp->tcp_state, port, false, socket, listener_event_callback); - if (listener == NULL) { - // FIXME: This error code should be more specific, - // but the TCP module isn't stable yet - errcode2 = MICROTCP_ERRCODE_TCPERROR; - push_unlinked_socket_into_free_list(mtcp, socket); - goto unlock_and_exit; - } - - socket->mtcp = mtcp; - socket->prev = NULL; - socket->next = NULL; - socket->type = SOCKET_LISTENER; - socket->listener = listener; - socket->mux_list = NULL; - - if (cnd_init(&socket->something_to_accept) != thrd_success) { - errcode2 = MICROTCP_ERRCODE_BADCONDVAR; - push_unlinked_socket_into_free_list(mtcp, socket); - tcp_listener_destroy(listener); - goto unlock_and_exit; - } - - push_unlinked_socket_into_used_list(socket); + + socket = pop_socket_struct_from_free_list(mtcp); + if (!socket) { + mtcp->errcode = MICROTCP_ERRCODE_SOCKETLIMIT; + goto unlock_and_exit; // Socket limit reached } + + tcp_listener_t *listener = tcp_listener_create(&mtcp->tcp_state, port, false, socket, listener_event_callback); + if (listener == NULL) { + // FIXME: This error code should be more specific, + // but the TCP module isn't stable yet + mtcp->errcode = MICROTCP_ERRCODE_TCPERROR; + push_unlinked_socket_into_free_list(mtcp, socket); + goto unlock_and_exit; + } + + socket->mtcp = mtcp; + socket->prev = NULL; + socket->next = NULL; + socket->type = SOCKET_LISTENER; + socket->block = true; + socket->errcode = MICROTCP_ERRCODE_NONE; + socket->listener = listener; + socket->mux_list = NULL; + + if (cnd_init(&socket->something_to_accept) != thrd_success) { + mtcp->errcode = MICROTCP_ERRCODE_BADCONDVAR; + push_unlinked_socket_into_free_list(mtcp, socket); + tcp_listener_destroy(listener); + goto unlock_and_exit; + } + + push_unlinked_socket_into_used_list(socket); + unlock_and_exit: mtx_unlock(&mtcp->lock); - if (errcode) - *errcode = errcode2; return socket; } @@ -738,9 +779,9 @@ void microtcp_close(microtcp_socket_t *socket) break; case SOCKET_CONNECTION: - if (socket->connection) // Only need to close the connection + if (socket->conn) // Only need to close the connection // if the peer didn't already. - tcp_connection_destroy(socket->connection); + tcp_connection_destroy(socket->conn); break; } @@ -771,7 +812,7 @@ static void conn_event_callback(void *data, tcp_connevent_t event) case TCP_CONNEVENT_RESET: case TCP_CONNEVENT_CLOSE: - socket->connection = NULL; + socket->conn = NULL; flags = 0; // TODO: Maybe signal closing and reset events to muxes? break; } @@ -779,167 +820,205 @@ static void conn_event_callback(void *data, tcp_connevent_t event) signal_events_to_muxes_associated_to_socket(socket, flags); } -microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket, - bool no_block, - microtcp_errcode_t *errcode) +void microtcp_set_blocking(microtcp_socket_t *socket, bool block) +{ + socket->block = block; +} + +static bool init_conn_socket(microtcp_socket_t *socket, + microtcp_socket_t *parent, + tcp_connection_t *conn) +{ + socket->mtcp = parent->mtcp; + socket->prev = NULL; + socket->next = NULL; + socket->type = SOCKET_CONNECTION; + socket->block = true; + socket->conn = conn; + socket->errcode = MICROTCP_ERRCODE_NONE; + socket->mux_list = NULL; + + if (cnd_init(&socket->something_to_recv) != thrd_success) { + socket->errcode = MICROTCP_ERRCODE_BADCONDVAR; + return false; + } + + if (cnd_init(&socket->something_to_send) != thrd_success) { + socket->errcode = MICROTCP_ERRCODE_BADCONDVAR; + cnd_destroy(&socket->something_to_recv); + return false; + } + + return true; +} + +static microtcp_socket_t *accept_inner(microtcp_socket_t *socket) { - microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE; microtcp_t *mtcp = socket->mtcp; - microtcp_socket_t *socket2 = NULL; - mtx_lock(&mtcp->lock); - { - if (socket->type != SOCKET_LISTENER) { - errcode2 = MICROTCP_ERRCODE_NOTLISTENER; - goto unlock_and_exit; // Can't accept from a non-listening socket + if (socket->errcode != MICROTCP_ERRCODE_NONE) { + socket->errcode = MICROTCP_ERRCODE_NOCLEAR; + return NULL; + } + + if (socket->type != SOCKET_LISTENER) { + socket->errcode = MICROTCP_ERRCODE_NOTLISTENER; + return NULL; // Can't accept from a non-listening socket + } + + microtcp_socket_t *accepted = pop_socket_struct_from_free_list(mtcp); + if (!accepted) { + socket->errcode = MICROTCP_ERRCODE_SOCKETLIMIT; + return NULL; // Socket limit reached + } + + tcp_connection_t *conn = tcp_listener_accept(socket->listener, accepted, conn_event_callback); + if (conn == NULL) { + + if (!socket->block) { + socket->errcode = MICROTCP_ERRCODE_WOULDBLOCK; + return NULL; } - socket2 = pop_socket_struct_from_free_list(mtcp); - if (!socket2) { - errcode2 = MICROTCP_ERRCODE_SOCKETLIMIT; - goto unlock_and_exit; // Socket limit reached - } - - tcp_connection_t *connection = tcp_listener_accept(socket->listener, socket2, conn_event_callback); - - while (!connection && !no_block) { + do { if (cnd_wait(&socket->something_to_accept, &mtcp->lock) != thrd_success) { - errcode2 = MICROTCP_ERRCODE_BADCONDVAR; - push_unlinked_socket_into_free_list(mtcp, socket2); - goto unlock_and_exit; + socket->errcode = MICROTCP_ERRCODE_BADCONDVAR; + push_unlinked_socket_into_free_list(mtcp, accepted); + return NULL; } - connection = tcp_listener_accept(socket->listener, socket2, conn_event_callback); - } - - socket2->mtcp = mtcp; - socket2->prev = NULL; - socket2->next = NULL; - socket2->type = SOCKET_CONNECTION; - socket2->connection = connection; - socket2->mux_list = NULL; - - if (cnd_init(&socket2->something_to_recv) != thrd_success) { - errcode2 = MICROTCP_ERRCODE_BADCONDVAR; - push_unlinked_socket_into_free_list(mtcp, socket2); - goto unlock_and_exit; - } - if (cnd_init(&socket2->something_to_send) != thrd_success) { - errcode2 = MICROTCP_ERRCODE_BADCONDVAR; - cnd_destroy(&socket2->something_to_recv); - push_unlinked_socket_into_free_list(mtcp, socket2); - goto unlock_and_exit; - } - - push_unlinked_socket_into_used_list(socket2); + conn = tcp_listener_accept(socket->listener, accepted, conn_event_callback); + } while (!conn); } -unlock_and_exit: - mtx_unlock(&mtcp->lock); + if (!init_conn_socket(accepted, socket, conn)) { + push_unlinked_socket_into_free_list(mtcp, accepted); + return NULL; + } - if (errcode) - *errcode = errcode2; - - return socket2; + push_unlinked_socket_into_used_list(accepted); + return accepted; } -size_t microtcp_recv(microtcp_socket_t *socket, - void *dst, size_t len, - bool no_block, - microtcp_errcode_t *errcode) +microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket) { - if (!socket || socket->type != SOCKET_CONNECTION) { - if (errcode) - *errcode = MICROTCP_ERRCODE_NOTCONNECTION; - return 0; - } - - if (socket->connection == NULL) { - if (errcode) - *errcode = MICROTCP_ERRCODE_PEERCLOSED; - return 0; - } - - size_t num; + if (!socket) + return NULL; + microtcp_socket_t *accepted; microtcp_t *mtcp = socket->mtcp; - microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE; - mtx_lock(&mtcp->lock); - { - num = tcp_connection_recv(socket->connection, dst, len); + accepted = accept_inner(socket); + mtx_unlock(&mtcp->lock); + return accepted; +} - while (num == 0 && !no_block) { +static int recv_inner(microtcp_socket_t *socket, + void *dst, size_t len) +{ + microtcp_t *mtcp = socket->mtcp; + + if (socket->errcode != MICROTCP_ERRCODE_NONE) { + socket->errcode = MICROTCP_ERRCODE_NOCLEAR; + return -1; + } + + if (socket->type != SOCKET_CONNECTION) { + socket->errcode = MICROTCP_ERRCODE_NOTCONNECTION; + return -1; + } + + if (socket->conn == NULL) + return 0; + + // Don't read more bytes than the maximum number + // representable by an "int" to not overflow the + // return value. + if (len > (size_t) INT_MAX) len = INT_MAX; + + size_t num = tcp_connection_recv(socket->conn, dst, len); + + if (num == 0) { + + if (!socket->block) { + socket->errcode = MICROTCP_ERRCODE_WOULDBLOCK; + return -1; + } + + do { if (cnd_wait(&socket->something_to_recv, &mtcp->lock) != thrd_success) { - errcode2 = MICROTCP_ERRCODE_BADCONDVAR; - goto unlock_and_exit; + socket->errcode = MICROTCP_ERRCODE_BADCONDVAR; + return -1; } - num = tcp_connection_recv(socket->connection, dst, len); - } - - if (num == 0) { - if (no_block) - errcode2 = MICROTCP_ERRCODE_WOULDBLOCK; - else - errcode2 = MICROTCP_ERRCODE_CANTBLOCK; - } + num = tcp_connection_recv(socket->conn, dst, len); + } while (num == 0); } - - goto unlock_and_exit; // Warning - unlock_and_exit: - - mtx_unlock(&mtcp->lock); - if (errcode) - *errcode = errcode2; - return num; + assert(num <= INT_MAX); + return (int) num; } -size_t microtcp_send(microtcp_socket_t *socket, - const void *src, size_t len, - bool no_block, - microtcp_errcode_t *errcode) +int microtcp_recv(microtcp_socket_t *socket, + void *dst, size_t len) { - if (!socket || socket->type != SOCKET_CONNECTION) { - if (errcode) - *errcode = MICROTCP_ERRCODE_NOTCONNECTION; - return 0; - } - - if (socket->connection == NULL) { - if (errcode) - *errcode = MICROTCP_ERRCODE_PEERCLOSED; - return 0; - } - - size_t num; + if (!socket) + return -1; + int res; microtcp_t *mtcp = socket->mtcp; - microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE; - mtx_lock(&mtcp->lock); - { - num = tcp_connection_send(socket->connection, src, len); - - while (num == 0 && !no_block) { - if (cnd_wait(&socket->something_to_send, &mtcp->lock) != thrd_success) { - errcode2 = MICROTCP_ERRCODE_BADCONDVAR; - goto unlock_and_exit; - } - num = tcp_connection_send(socket->connection, src, len); - } - - if (num == 0) { - if (no_block) - errcode2 = MICROTCP_ERRCODE_WOULDBLOCK; - else - errcode2 = MICROTCP_ERRCODE_CANTBLOCK; - } - } - goto unlock_and_exit; // Warning - unlock_and_exit: + res = recv_inner(socket, dst, len); mtx_unlock(&mtcp->lock); + return res; +} - if (errcode) - *errcode = errcode2; - return num; +static int send_inner(microtcp_socket_t *socket, + const void *src, size_t len) +{ + microtcp_t *mtcp = socket->mtcp; + + if (socket->type != SOCKET_CONNECTION) { + socket->errcode = MICROTCP_ERRCODE_NOTCONNECTION; + return -1; + } + + if (socket->conn == NULL) + return 0; + + // As for "recv", never send a number of bytes + // bigger than what can be represented by the + // return value. + if (len > INT_MAX) len = INT_MAX; + + size_t num = tcp_connection_send(socket->conn, src, len); + if (num == 0) { + + if (!socket->block) { + socket->errcode = MICROTCP_ERRCODE_WOULDBLOCK; + return -1; + } + + do { + if (cnd_wait(&socket->something_to_send, &mtcp->lock) != thrd_success) { + socket->errcode = MICROTCP_ERRCODE_BADCONDVAR; + return -1; + } + num = tcp_connection_send(socket->conn, src, len); + } while (num == 0); + } + + assert(num <= INT_MAX); + return (int) num; +} + +int microtcp_send(microtcp_socket_t *socket, + const void *src, size_t len) +{ + if (!socket) + return -1; + int res; + microtcp_t *mtcp = socket->mtcp; + mtx_lock(&mtcp->lock); + res = send_inner(socket, src, len); + mtx_unlock(&mtcp->lock); + return res; } microtcp_mux_t *microtcp_mux_create(microtcp_t *mtcp) diff --git a/src/microtcp.h b/src/microtcp.h index 9219836..61b64ca 100644 --- a/src/microtcp.h +++ b/src/microtcp.h @@ -17,6 +17,8 @@ typedef enum { MICROTCP_ERRCODE_NONE = 0, + MICROTCP_ERRCODE_NOCLEAR, + // Returned by microtcp_open and microtcp_accept MICROTCP_ERRCODE_SOCKETLIMIT, @@ -48,22 +50,30 @@ typedef struct { bool microtcp_callbacks_create_for_tap(const char *ip, const char *mac, microtcp_callbacks_t *callbacks); microtcp_t *microtcp_create(const char *tap_ip, const char *stack_ip, const char *tap_mac, const char *stack_mac); - microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac, microtcp_callbacks_t callbacks); void microtcp_destroy(microtcp_t *mtcp); +microtcp_errcode_t microtcp_get_error(microtcp_t *mtcp); +void microtcp_clear_error(microtcp_t *mtcp); + +microtcp_errcode_t microtcp_get_socket_error(microtcp_socket_t *sock); +void microtcp_clear_socket_error(microtcp_socket_t *sock); + const char *microtcp_strerror(microtcp_errcode_t errcode); -microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port, microtcp_errcode_t *errcode); -microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket, bool no_block, microtcp_errcode_t *errcode); +microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port); +microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket); void microtcp_close(microtcp_socket_t *socket); -size_t microtcp_send(microtcp_socket_t *socket, const void *src, size_t len, bool no_block, microtcp_errcode_t *errcode); -size_t microtcp_recv(microtcp_socket_t *socket, void *dst, size_t len, bool no_block, microtcp_errcode_t *errcode); -void microtcp_step(microtcp_t *mtcp); -void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len); + +int microtcp_send(microtcp_socket_t *socket, const void *src, size_t len); +int microtcp_recv(microtcp_socket_t *socket, void *dst, size_t len); + +bool microtcp_step(microtcp_t *mtcp); +void microtcp_set_blocking(microtcp_socket_t *socket, bool block); +bool microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len); typedef enum { - MICROTCP_MUX_ACCEPT = 1, - MICROTCP_MUX_RECV = 2, - MICROTCP_MUX_SEND = 4, + MICROTCP_MUX_ACCEPT = 1 << 0, + MICROTCP_MUX_RECV = 1 << 1, + MICROTCP_MUX_SEND = 1 << 2, } microtcp_muxeventid_t; typedef struct {