From 531937cf6f3c08021c325ab882171dbea96e5838 Mon Sep 17 00:00:00 2001 From: T-X Date: Mon, 4 Oct 2021 21:23:29 +0200 Subject: [PATCH] gluon-neighbour-info: fix broken output with large results (#2322) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently a buffer with a fixed size of 8192 bytes is used. However the result can potentially be larger, which leads to a truncated JSON output on stdout. UDP packets, without compression and with IP fragmentation, can be up to 64KiB large. Instead of using a fixed size buffer on the stack ask the kernel first about the size of the UDP data and allocate a buffer of appropriate size on the heap before receiving the UDP data. The issue was observed with a custom respondd provider. Signed-off-by: Linus Lüssing --- .../src/gluon-neighbour-info.c | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/package/gluon-neighbour-info/src/gluon-neighbour-info.c b/package/gluon-neighbour-info/src/gluon-neighbour-info.c index 6470508c..119aaddc 100644 --- a/package/gluon-neighbour-info/src/gluon-neighbour-info.c +++ b/package/gluon-neighbour-info/src/gluon-neighbour-info.c @@ -69,8 +69,23 @@ void tv_subtract (struct timeval *r, const struct timeval *a, const struct timev } } -ssize_t recvtimeout(int socket, void *buffer, size_t length, int flags, const struct timeval *timeout) { +void resize_recvbuffer(char **recvbuffer, size_t *recvbuffer_len, size_t recvlen) +{ + free(*recvbuffer); + *recvbuffer = malloc(recvlen); + + if (!(*recvbuffer)) { + perror("Could not resize recvbuffer"); + exit(EXIT_FAILURE); + } + + *recvbuffer_len = recvlen; +} + +ssize_t recvtimeout(int socket, char **recvbuffer, size_t *recvbuffer_len, + const struct timeval *timeout) { struct timeval now, timeout_left; + ssize_t recvlen; getclock(&now); tv_subtract(&timeout_left, timeout, &now); @@ -79,18 +94,28 @@ ssize_t recvtimeout(int socket, void *buffer, size_t length, int flags, const st return -1; setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &timeout_left, sizeof(timeout_left)); - return recv(socket, buffer, length, flags); + + recvlen = recv(socket, NULL, 0, MSG_PEEK | MSG_TRUNC); + if (recvlen < 0) + return recvlen; + + if (recvlen > *recvbuffer_len) + resize_recvbuffer(recvbuffer, recvbuffer_len, recvlen); + + return recv(socket, *recvbuffer, *recvbuffer_len, 0); } -int request(const int sock, const struct sockaddr_in6 *client_addr, const char *request, const char *sse, double timeout, unsigned int max_count) { +int request(const int sock, char **recvbuffer, size_t *recvbuffer_len, + const struct sockaddr_in6 *client_addr, const char *request, + const char *sse, double timeout, unsigned int max_count) { ssize_t ret; - char buffer[8192]; unsigned int count = 0; ret = sendto(sock, request, strlen(request), 0, (struct sockaddr *)client_addr, sizeof(struct sockaddr_in6)); if (ret < 0) { perror("Error in sendto()"); + free(*recvbuffer); exit(EXIT_FAILURE); } @@ -105,7 +130,7 @@ int request(const int sock, const struct sockaddr_in6 *client_addr, const char * } do { - ret = recvtimeout(sock, buffer, sizeof(buffer), 0, &tv_timeout); + ret = recvtimeout(sock, recvbuffer, recvbuffer_len, &tv_timeout); if (ret < 0) break; @@ -116,7 +141,7 @@ int request(const int sock, const struct sockaddr_in6 *client_addr, const char * fputs("data: ", stdout); } - fwrite(buffer, sizeof(char), ret, stdout); + fwrite(*recvbuffer, sizeof(char), ret, stdout); if (sse) fputs("\n\n", stdout); @@ -137,6 +162,8 @@ int main(int argc, char **argv) { int sock; struct sockaddr_in6 client_addr = {}; char *request_string = NULL; + char *recvbuffer = NULL; + size_t recvbuffer_len = 0; sock = socket(PF_INET6, SOCK_DGRAM, 0); @@ -243,11 +270,13 @@ int main(int argc, char **argv) { } do { - ret = request(sock, &client_addr, request_string, sse, timeout, max_count); + ret = request(sock, &recvbuffer, &recvbuffer_len, &client_addr, + request_string, sse, timeout, max_count); } while(loop); if (sse) fputs("event: eot\ndata: null\n\n", stdout); + free(recvbuffer); return ret; }