From 412231eecd0af2d66937968ba9ef4d233725d859 Mon Sep 17 00:00:00 2001 From: Rob Shearman Date: Wed, 1 Jun 2022 19:41:08 +0100 Subject: [PATCH] whitelist: Use a watcher for control socket reading rather than blocking Performing a stream read_all call (which is a blocking read) from within the accept callback has the issue that if a whitelist client is still connected whilst a shutdown of the charon deamon is triggered then that shutdown won't complete gracefully due to the accept task never exiting. So fix shutting down gracefully by using the socket watcher rather than a blocking read upon connection accept. Fall back to a blocking read for partial messages to avoid the complexity associated (i.e. storing state) for incomplete reads, which shouldn't block and cause the original problem if the client only sends whole messages. --- .../plugins/whitelist/whitelist_control.c | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/libcharon/plugins/whitelist/whitelist_control.c b/src/libcharon/plugins/whitelist/whitelist_control.c index e3a787ca70..c8dc7ef8d0 100644 --- a/src/libcharon/plugins/whitelist/whitelist_control.c +++ b/src/libcharon/plugins/whitelist/whitelist_control.c @@ -95,13 +95,32 @@ static void list(private_whitelist_control_t *this, /** * Dispatch a received message */ -static bool on_accept(private_whitelist_control_t *this, stream_t *stream) +CALLBACK(on_read, bool, + private_whitelist_control_t *this, stream_t *stream) { identification_t *id; whitelist_msg_t msg; + ssize_t n; - while (stream->read_all(stream, &msg, sizeof(msg))) + while (TRUE) { + n = stream->read(stream, &msg, sizeof(msg), FALSE); + if (n <= 0) + { + if (errno == EWOULDBLOCK) + { + break; + } + return FALSE; + } + if (n < sizeof(msg)) + { + if (!stream->read_all(stream, ((char *)&msg) + n, sizeof(msg) - n)) + { + return FALSE; + } + } + msg.id[sizeof(msg.id) - 1] = 0; id = identification_create_from_string(msg.id); switch (ntohl(msg.type)) @@ -131,7 +150,14 @@ static bool on_accept(private_whitelist_control_t *this, stream_t *stream) id->destroy(id); } - return FALSE; + return TRUE; +} + +CALLBACK(on_accept, bool, + private_whitelist_control_t *this, stream_t *stream) +{ + stream->on_read(stream, on_read, this); + return TRUE; } METHOD(whitelist_control_t, destroy, void, -- 2.47.2