Avoid possible one-byte overrun in ioset_line_read().
[srvx.git] / src / ioset.c
index 72204135e8388589bfc7094ca2ec2f9bf47e8eb0..9cf1c433fe78ce20f029336ca9d2a000e7fa30dc 100644 (file)
@@ -142,9 +142,18 @@ ioset_add(int fd) {
     res->fd = fd;
     ioq_init(&res->send, 1024);
     ioq_init(&res->recv, 1024);
-    engine->add(res);
+#if defined(F_GETFL)
     flags = fcntl(fd, F_GETFL);
     fcntl(fd, F_SETFL, flags|O_NONBLOCK);
+    flags = fcntl(fd, F_GETFD);
+    fcntl(fd, F_SETFD, flags|FD_CLOEXEC);
+#else
+    /* I hope you're using the Win32 backend or something else that
+     * automatically marks the file descriptor non-blocking...
+     */
+    (void)flags;
+#endif
+    engine->add(res);
     return res;
 }
 
@@ -257,7 +266,6 @@ ioset_connect(struct sockaddr *local, unsigned int sa_size, const char *peer, un
         case EHOSTUNREACH:
         case ECONNREFUSED:
             ioset_close(io_fd, 1);
-            engine->update(io_fd);
             return NULL;
         }
     }
@@ -279,7 +287,9 @@ void ioset_update(struct io_fd *fd) {
 static void
 ioset_try_write(struct io_fd *fd) {
     int res;
-    unsigned int req = ioq_get_avail(&fd->send);
+    unsigned int req;
+
+    req = ioq_get_avail(&fd->send);
     res = write(fd->fd, fd->send.buf+fd->send.get, req);
     if (res < 0) {
         switch (errno) {
@@ -304,7 +314,21 @@ ioset_close(struct io_fd *fdp, int os_close) {
         active_fd = NULL;
     if (fdp->destroy_cb)
         fdp->destroy_cb(fdp);
-    if (fdp->send.get != fdp->send.put) {
+#if defined(HAVE_WSAEVENTSELECT)
+    /* This is one huge kludge.  Sorry! */
+    if (fdp->send.get != fdp->send.put && (os_close & 2)) {
+        engine->remove(fdp, 0);
+        ioset_try_write(fdp);
+        /* it may need to send the beginning of the buffer now.. */
+        if (fdp->send.get != fdp->send.put)
+            ioset_try_write(fdp);
+    }
+    free(fdp->send.buf);
+    free(fdp->recv.buf);
+    if (os_close & 1)
+        closesocket(fdp->fd);
+#else
+    if (fdp->send.get != fdp->send.put && (os_close & 2)) {
         int flags;
 
        flags = fcntl(fdp->fd, F_GETFL);
@@ -316,9 +340,10 @@ ioset_close(struct io_fd *fdp, int os_close) {
     }
     free(fdp->send.buf);
     free(fdp->recv.buf);
-    if (os_close)
+    if (os_close & 1)
         close(fdp->fd);
-    engine->remove(fdp);
+    engine->remove(fdp, os_close & 1);
+#endif
     free(fdp);
 }
 
@@ -406,7 +431,7 @@ ioset_buffered_read(struct io_fd *fd) {
         if (fd->recv.put == fd->recv.size)
             fd->recv.put = 0;
         fdnum = fd->fd;
-        while (fd->wants_reads && (fd->line_len > 0)) {
+        while (fd->line_len > 0) {
             struct io_fd *old_active;
             int died = 0;
 
@@ -427,18 +452,21 @@ ioset_buffered_read(struct io_fd *fd) {
 
 int
 ioset_line_read(struct io_fd *fd, char *dest, int max) {
-    int avail, done;
-    if ((fd->state == IO_CLOSED) && (!ioq_get_avail(&fd->recv) ||  (fd->line_len < 0)))
+    int line_len;
+    int avail;
+    int done;
+
+    line_len = fd->line_len;
+    if ((fd->state == IO_CLOSED) && (!ioq_get_avail(&fd->recv) ||  (line_len < 0)))
         return 0;
-    if (fd->line_len < 0)
+    if (line_len < 0)
         return -1;
-    if (fd->line_len < max)
-        max = fd->line_len;
+    if (line_len < max)
+        max = line_len;
     avail = ioq_get_avail(&fd->recv);
     if (max > avail) {
         memcpy(dest, fd->recv.buf + fd->recv.get, avail);
-        fd->recv.get += avail;
-        assert(fd->recv.get == fd->recv.size);
+        assert(fd->recv.get + avail == fd->recv.size);
         fd->recv.get = 0;
         done = avail;
     } else {
@@ -448,9 +476,9 @@ ioset_line_read(struct io_fd *fd, char *dest, int max) {
     fd->recv.get += max - done;
     if (fd->recv.get == fd->recv.size)
         fd->recv.get = 0;
-    dest[max] = 0;
+    dest[max - 1] = 0;
     ioset_find_line_length(fd);
-    return max;
+    return line_len;
 }
 
 void