diff --git a/src/communication.c b/src/communication.c index 892b807..9262a09 100644 --- a/src/communication.c +++ b/src/communication.c @@ -177,14 +177,13 @@ struct ipc_error ipc_read (const struct ipc_ctx *ctx, uint32_t index, struct ipc { T_R ((m == NULL), IPC_ERROR_READ__NO_MESSAGE_PARAM); - char *buf = NULL; size_t msize = IPC_MAX_MESSAGE_SIZE; + SECURE_BUFFER_DECLARATION (char, buf, msize); + char *pbuf = buf; // On error or closed recipient, the buffer already freed. - TEST_IPC_RETURN_ON_ERROR (usock_recv (ctx->pollfd[index].fd, &buf, &msize)); - TEST_IPC_RETURN_ON_ERROR_FREE (ipc_message_format_read (m, buf, msize), buf); - - free (buf); + TEST_IPC_RETURN_ON_ERROR (usock_recv (ctx->pollfd[index].fd, &pbuf, &msize)); + TEST_IPC_RETURN_ON_ERROR (ipc_message_format_read (m, buf, msize)); IPC_RETURN_NO_ERROR; // propagates ipc_message_format return } diff --git a/src/ipc.h b/src/ipc.h index 231a5b1..b00bae7 100644 --- a/src/ipc.h +++ b/src/ipc.h @@ -85,6 +85,14 @@ enum ipc_event_type { , IPC_EVENT_TYPE_TX = 9 // Message sent. }; +// For IO callbacks (switching). +enum ipccb { + IPC_CB_NO_ERROR = 0 + , IPC_CB_FD_CLOSING = 1 + , IPC_CB_FD_ERROR = 2 + , IPC_CB_PARSING_ERROR = 3 +}; + /** * Error codes. * libipc tend to use unique error codes in the whole library, allowing easier debugging. @@ -100,7 +108,7 @@ enum ipc_error_code { , IPC_ERROR_SERVER_INIT__NO_SERVER_NAME_PARAM = 9 , IPC_ERROR_SERVER_INIT__MALLOC = 10 , IPC_ERROR_WRITE__NO_MESSAGE_PARAM = 11 - , IPC_ERROR_WRITE_FD__NOT_ENOUGH_DATA = 12 + , IPC_ERROR_WRITE_FD__NOT_ENOUGH_DATA = 12 , IPC_ERROR_READ__NO_MESSAGE_PARAM = 13 , IPC_ERROR_CONNECTION__NO_SERVICE_NAME = 15 , IPC_ERROR_CONNECTION__NO_ENVIRONMENT_PARAM = 16 diff --git a/src/network.c b/src/network.c index 50a246e..7f5326b 100644 --- a/src/network.c +++ b/src/network.c @@ -138,6 +138,28 @@ int ipc_switching_del (struct ipc_switchings *is, int fd) return -1; } +/** + * 0 = fd is origin + * 1 = fd is dest + * -1 = not found + */ +int ipc_switching_get_ (const struct ipc_switchings *is + , int fd + , struct ipc_switching *s) +{ + for (size_t i = 0; i < is->size; i++) { + if (is->collection[i].orig == fd) { + *s = is->collection[i]; + return 0; + } else if (is->collection[i].dest == fd) { + *s = is->collection[i]; + return 1; + } + } + + return -1; +} + int ipc_switching_get (struct ipc_switchings *is, int fd) { for (size_t i = 0; i < is->size; i++) { @@ -163,74 +185,117 @@ void ipc_switching_free (struct ipc_switchings *is) is->size = 0; } -/** - * switching_messages allows to send messages from a fd to another. - */ -struct ipc_error fd_switching (struct ipc_event *event, struct ipc_ctx *ctx, int index) +enum ipccb +default_cb_in(int fd, struct ipc_message *m) { - // If the socket is associated to another one for ipcd: - // read and write automatically and provide a new IPC_EVENT_TYPE indicating the switch. - T_R ((ctx->switchdb.size == 0), IPC_ERROR_FD_SWITCHING__NO_FD_RECORD); + // TODO: fix buffer size for switching messages + size_t msize = 4096; + char buf[msize]; + char *pbuf = buf; - int talkingfd = ctx->pollfd[index].fd; - int correspondingfd = ipc_switching_get (&ctx->switchdb, talkingfd); - - T_R ((correspondingfd == -1), IPC_ERROR_FD_SWITCHING__NO_FD_RECORD); - - char *buf = NULL; - size_t msize = 0; + // By default, usock_read (a wrapper around read(2)) is used. { /** Some macros use "ret" as a variable name, so this is to be sure. */ - struct ipc_error ret = usock_recv (talkingfd, &buf, &msize); - if (ret.error_code != IPC_ERROR_NONE && ret.error_code != IPC_ERROR_CLOSED_RECIPIENT) { - if (buf != NULL) - free (buf); - IPC_EVENT_SET (event, IPC_EVENT_TYPE_ERROR, index, talkingfd, NULL) - return ret; + struct ipc_error ret = usock_recv (fd, &pbuf, &msize); + if (ret.error_code != IPC_ERROR_NONE) { + if (ret.error_code == IPC_ERROR_CLOSED_RECIPIENT) { + return IPC_CB_FD_CLOSING; + } + return IPC_CB_FD_ERROR; } } /** There is a message, send it to the corresponding fd **/ if (msize > 0) { - size_t nbytes_sent = 0; - TEST_IPC_RETURN_ON_ERROR_FREE (usock_send (correspondingfd, buf, msize, &nbytes_sent), buf); - - if (nbytes_sent != msize) { - // LOG_ERROR ("wrote not enough data from %d to fd %d", talkingfd, correspondingfd); - IPC_EVENT_SET (event, IPC_EVENT_TYPE_ERROR, index, ctx->pollfd[index].fd, NULL); - IPC_RETURN_NO_ERROR; // FIXME: return something else, maybe? + struct ipc_error ret = ipc_message_format_read (m, buf, msize); + if (ret.error_code != IPC_ERROR_NONE) { + return IPC_CB_PARSING_ERROR; } - // LOG_DEBUG ("received a message on fd %d => switch to fd %d", talkingfd, correspondingfd); - - if (buf != NULL) - free (buf); - - // Everything is OK: inform ipcd of a successful transfer. - IPC_EVENT_SET (event, IPC_EVENT_TYPE_SWITCH, index, ctx->pollfd[index].fd, NULL); - IPC_RETURN_NO_ERROR; - } else if (msize == 0) { - int delfd; - - delfd = ipc_switching_del (&ctx->switchdb, talkingfd); - if (delfd >= 0) { - close (delfd); - ipc_del_fd (ctx, delfd); - } - - close (talkingfd); - ipc_del_fd (ctx, talkingfd); - -#if 0 - if (delfd >= 0) { - LOG_DEBUG ("disconnection of %d (and related fd %d)", talkingfd, delfd); - } else { - LOG_DEBUG ("disconnection of %d", talkingfd); - } -#endif - - IPC_EVENT_SET (event, IPC_EVENT_TYPE_DISCONNECTION, index, talkingfd, NULL); - IPC_RETURN_ERROR (IPC_ERROR_CLOSED_RECIPIENT); + return IPC_CB_NO_ERROR; } - IPC_RETURN_NO_ERROR; + // By default, if msize <= 0 the fd should be closed. + return IPC_CB_FD_CLOSING; +} + +enum ipccb +default_cb_out(int fd, struct ipc_message *m) +{ + printf("TODO: default_cb_out\n"); + return IPC_CB_NO_ERROR; +} + +/** + * switching_messages allows to send messages from a fd to another. + */ +struct ipc_error fd_switching (struct ipc_event *event, struct ipc_ctx *ctx, int index) +{ + printf ("fd_switching\n"); + + // If the socket is associated to another one for ipcd: + // read and write automatically and provide a new IPC_EVENT_TYPE indicating the switch. + T_R ((ctx->switchdb.size == 0), IPC_ERROR_FD_SWITCHING__NO_FD_RECORD); + + int talkingfd = ctx->pollfd[index].fd; + struct ipc_switching sw; + struct ipc_message m; + + enum ipccb r; + int is_valid = 0; + + is_valid = ipc_switching_get_ (&ctx->switchdb, talkingfd, &sw); + + T_R ((is_valid == -1), IPC_ERROR_FD_SWITCHING__NO_FD_RECORD); + + if (sw.orig == talkingfd) { + if (sw.orig_in == NULL) { + r = default_cb_in (talkingfd, &m); + } + else { + r = (*sw.orig_in)(talkingfd, &m); + } + } + else { + if (sw.dest_in == NULL) { + r = default_cb_in (talkingfd, &m); + } + else { + r = (*sw.dest_in)(talkingfd, &m); + } + } + + // Message reception OK: reading the message and put it in the list of messages to send. + if (r == IPC_CB_NO_ERROR) { + // In case of message reception: + // 1. put the message in the list to be sent + ipc_write (ctx, &m); + // 2. set event IPC_EVENT_TYPE_SWITCH, inform ipcd of a successful reception. + IPC_EVENT_SET (event, IPC_EVENT_TYPE_SWITCH, index, ctx->pollfd[index].fd, NULL); + // 3. IPC_RETURN_NO_ERROR + IPC_RETURN_NO_ERROR; + } + + /** + * NOTE: In any other case, the fd is, or should be closed. + */ + + // 1. close and remove both fd from switchdb + int delfd = ipc_switching_del (&ctx->switchdb, talkingfd); + if (delfd >= 0) { + close (delfd); + ipc_del_fd (ctx, delfd); + } + close (talkingfd); + ipc_del_fd (ctx, talkingfd); + + // 2. set event (either error or disconnection) + if (r == IPC_CB_FD_CLOSING) { + IPC_EVENT_SET (event, IPC_EVENT_TYPE_DISCONNECTION, index, talkingfd, NULL); + } + else { + IPC_EVENT_SET (event, IPC_EVENT_TYPE_ERROR, index, talkingfd, NULL); + } + + // 3. return IPC_ERROR_CLOSED_RECIPIENT + IPC_RETURN_ERROR (IPC_ERROR_CLOSED_RECIPIENT); }