[PATCH 4/4] alfred: handle short reads when waiting for daemon answer
Sven Eckelmann <[email protected]> Fri, 31 Jul 2026 09:40:16 +0200
| Newsgroups | org.open-mesh.lists.batman |
|---|---|
| Message-ID | <[email protected]> |
The answer of the alfred daemon is received over a SOCK_STREAM unix socket
which doesn't preserve message boundaries. read() can therefore return less
than the requested number of bytes. Such a short read was treated like an
end of stream and the processing was stopped.
Retry the read until the requested amount of data, end of stream or a real
error was received (ignoring temporary interruptions EINTR).
Fixes: 23359bbf72aa ("alfred: initial commit")
Signed-off-by: Sven Eckelmann <[email protected]>
---
alfred.h | 1 +
client.c | 66 +++++++++++++++++++++++++++++++++++--------------------------
unix_sock.c | 26 ++++++++++++------------
util.c | 25 +++++++++++++++++++++++
4 files changed, 77 insertions(+), 41 deletions(-)
diff --git a/alfred.h b/alfred.h
index df47d54..1d915a8 100644
--- a/alfred.h
+++ b/alfred.h
@@ -224,6 +224,7 @@ void netsock_reopen(struct globals *globals);
int netsock_own_address(const struct globals *globals,
const alfred_addr *address);
/* util.c */
+ssize_t read_full(int fd, void *buf, size_t count);
int time_diff(struct timespec *tv1, struct timespec *tv2,
struct timespec *tvdiff);
void time_random_seed(void);
diff --git a/client.c b/client.c
index 1967f69..1e1f1ef 100644
--- a/client.c
+++ b/client.c
@@ -53,7 +53,7 @@ int alfred_client_request_data(struct globals *globals)
push = (struct alfred_push_data_v0 *)buf;
tlv = (struct alfred_tlv *)buf;
- while ((ret = read(globals->unix_sock, buf, sizeof(*tlv))) > 0) {
+ while ((ret = read_full(globals->unix_sock, buf, sizeof(*tlv))) > 0) {
if (ret < (int)sizeof(*tlv))
break;
@@ -64,16 +64,16 @@ int alfred_client_request_data(struct globals *globals)
break;
/* read the rest of the header */
- ret = read(globals->unix_sock, buf + sizeof(*tlv),
- sizeof(*push) - sizeof(*tlv));
+ ret = read_full(globals->unix_sock, buf + sizeof(*tlv),
+ sizeof(*push) - sizeof(*tlv));
/* too short */
if (ret < (int)(sizeof(*push) - (int)sizeof(*tlv)))
break;
/* read the rest of the header */
- ret = read(globals->unix_sock, buf + sizeof(*push),
- sizeof(*data));
+ ret = read_full(globals->unix_sock, buf + sizeof(*push),
+ sizeof(*data));
if (ret < (ssize_t)sizeof(*data))
break;
@@ -86,8 +86,8 @@ int alfred_client_request_data(struct globals *globals)
break;
/* read the data */
- ret = read(globals->unix_sock,
- buf + sizeof(*push) + sizeof(*data), data_len);
+ ret = read_full(globals->unix_sock,
+ buf + sizeof(*push) + sizeof(*data), data_len);
/* again too short */
if (ret < data_len)
@@ -124,8 +124,8 @@ int alfred_client_request_data(struct globals *globals)
recv_err:
/* read the rest of the status message */
- ret = read(globals->unix_sock, buf + sizeof(*tlv),
- sizeof(*status) - sizeof(*tlv));
+ ret = read_full(globals->unix_sock, buf + sizeof(*tlv),
+ sizeof(*status) - sizeof(*tlv));
/* too short */
if (ret < (int)(sizeof(*status) - sizeof(*tlv)))
@@ -377,42 +377,49 @@ int alfred_client_server_status(struct globals *globals)
fprintf(stderr, "%s: only wrote %d of %d bytes: %s\n",
__func__, ret, len, strerror(errno));
- len = read(globals->unix_sock, buf, sizeof(buf));
- if (len <= 0) {
+ ret = -1;
+ status_rep = (struct alfred_server_status_rep_v0 *)buf;
+ headsize = sizeof(status_rep->header);
+
+ /* drop too small packets */
+ len = read_full(globals->unix_sock, buf, headsize);
+ if (len < 0) {
perror("read from unix socket failed");
goto err;
}
- ret = -1;
- status_rep = (struct alfred_server_status_rep_v0 *)buf;
-
- /* drop too small packets */
- headsize = sizeof(status_rep->header);
if (len < headsize) {
- perror("unexpected header size received from unix socket");
- goto err;
- }
-
- if ((len - headsize) < ((int)ntohs(status_rep->header.length))) {
- perror("unexpected packet size received from unix socket");
+ fprintf(stderr, "unexpected header size received from unix socket\n");
goto err;
}
if (status_rep->header.type != ALFRED_SERVER_STATUS) {
- perror("alfred server_status type mismatch");
+ fprintf(stderr, "alfred server_status type mismatch\n");
goto err;
}
if (status_rep->header.version != ALFRED_VERSION) {
- perror("alfred version mismatch");
+ fprintf(stderr, "alfred version mismatch\n");
goto err;
}
- headsize = ntohs(status_rep->header.length);
+ tlvsize = ntohs(status_rep->header.length);
- if (headsize < (int)(sizeof(*status_rep) - sizeof(status_rep->header)))
+ if (tlvsize < (int)(sizeof(*status_rep) - sizeof(status_rep->header)))
goto err;
+ if (tlvsize > (int)(sizeof(buf) - headsize)) {
+ fprintf(stderr, "unexpected packet size received from unix socket\n");
+ goto err;
+ }
+
+ /* read the announced rest of the reply */
+ if (read_full(globals->unix_sock, buf + headsize, tlvsize) < tlvsize) {
+ fprintf(stderr, "unexpected packet size received from unix socket\n");
+ goto err;
+ }
+
+ len = headsize + tlvsize;
consumed = sizeof(*status_rep);
while (len - consumed > 0) {
@@ -473,9 +480,11 @@ int alfred_client_server_status(struct globals *globals)
consumed += tlvsize;
}
+ ret = 0;
+
err:
unix_sock_close(globals);
- return 0;
+ return ret;
}
int alfred_client_event_monitor(struct globals *globals)
@@ -502,7 +511,8 @@ int alfred_client_event_monitor(struct globals *globals)
}
while (true) {
- len = read(globals->unix_sock, &event_notify, sizeof(event_notify));
+ len = read_full(globals->unix_sock, &event_notify,
+ sizeof(event_notify));
if (len == 0) {
fprintf(stdout, "Server closed the connection\n");
goto err;
diff --git a/unix_sock.c b/unix_sock.c
index d4261ba..e5ccc35 100644
--- a/unix_sock.c
+++ b/unix_sock.c
@@ -542,26 +542,26 @@ static void unix_sock_read(struct globals *globals,
return;
}
- /* we assume that we can instantly read here. */
- length = read(client_sock, buf, sizeof(buf));
- if (length <= 0) {
+ headsize = sizeof(*packet);
+ packet = (struct alfred_tlv *)buf;
+
+ /* drop too small packets */
+ if (read_full(client_sock, buf, headsize) < headsize) {
perror("read from unix socket failed");
goto err;
}
- /* drop too small packets */
- headsize = sizeof(*packet);
- if (length < headsize)
- goto err;
-
- packet = (struct alfred_tlv *)buf;
-
- if ((length - headsize) < ((int)ntohs(packet->length)))
- goto err;
-
if (packet->version != ALFRED_VERSION)
goto err;
+ length = ntohs(packet->length);
+ if (length > (int)(sizeof(buf) - headsize))
+ goto err;
+
+ /* read the announced rest of the request */
+ if (read_full(client_sock, buf + headsize, length) < length)
+ goto err;
+
switch (packet->type) {
case ALFRED_PUSH_DATA:
unix_sock_add_data(globals,
diff --git a/util.c b/util.c
index b00b953..1a552ea 100644
--- a/util.c
+++ b/util.c
@@ -6,6 +6,7 @@
* License-Filename: LICENSES/preferred/GPL-2.0
*/
+#include <errno.h>
#include <netinet/ether.h>
#include <stdbool.h>
#include <stddef.h>
@@ -19,6 +20,30 @@
#include <unistd.h>
#include "alfred.h"
+ssize_t read_full(int fd, void *buf, size_t count)
+{
+ size_t read_len = 0;
+ uint8_t *pos = buf;
+ ssize_t ret;
+
+ while (read_len < count) {
+ ret = read(fd, pos + read_len, count - read_len);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+
+ return ret;
+ }
+
+ if (ret == 0)
+ break;
+
+ read_len += ret;
+ }
+
+ return read_len;
+}
+
int time_diff(struct timespec *tv1, struct timespec *tv2,
struct timespec *tvdiff) {
tvdiff->tv_sec = tv1->tv_sec - tv2->tv_sec;
--
2.47.3