]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
RTR Server: poll before writing
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 14 Jul 2021 15:25:43 +0000 (10:25 -0500)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 14 Jul 2021 15:25:43 +0000 (10:25 -0500)
Problem:

write() was sometimes failing with EAGAIN when Fort tried to send PDUs
to clients.

Diagnosis:

Obviously, it's because RTR client sockets now ship with O_NONBLOCK
enabled. Fort wants O_NONBLOCK for reading, but not for writing.

This bug was introduced in the previous commit.

Solution:

Make sure the socket is writable (via poll()) before calling write().

src/rtr/pdu_sender.c
src/rtr/rtr.c

index bee37a79cf24439a1d33862dc3263b94331aa571..fc4857f2bc3e7c2a2128e7a5403cf91e553a09ee 100644 (file)
@@ -1,6 +1,7 @@
 #include "pdu_sender.h"
 
 #include <errno.h>
+#include <poll.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
@@ -29,10 +30,25 @@ set_header_values(struct pdu_header *header, uint8_t version, uint8_t type,
 static int
 send_response(int fd, uint8_t pdu_type, unsigned char *data, size_t data_len)
 {
+       struct pollfd pfd;
        int error;
 
        pr_op_debug("Sending %s to client.", pdutype2str(pdu_type));
 
+       pfd.fd = fd;
+       pfd.events = POLLOUT;
+
+       do {
+               pfd.revents = 0;
+               error = poll(&pfd, 1, -1);
+               if (error < 0)
+                       return pr_op_err("poll() error: %d", error);
+               if (error == 0)
+                       return pr_op_err("poll() returned 0, even though there's no timeout.");
+               if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL))
+                       return pr_op_err("poll() returned revents %u.", pfd.revents);
+       } while (!(pfd.revents & POLLOUT));
+
        error = write(fd, data, data_len);
        if (error < 0)
                return pr_op_errno(errno, "Error sending %s to client.",
index c5ff459b336966aee1700c81154765d1bdc2e953..61560567fc7e4466d1496dd758ed80b237437bab 100644 (file)
@@ -199,6 +199,14 @@ init_addrinfo(char const *hostname, char const *service,
        return 0;
 }
 
+/*
+ * By the way: man 2 poll says
+ *
+ * > The operation of poll() and ppoll() is not affected by the O_NONBLOCK flag.
+ *
+ * Which appears to be untrue. If I remove this function, both client and server
+ * hang forever, apparently after the TCP handshake.
+ */
 static int
 set_nonblock(int fd)
 {