Browse Source

Handle more errnos from accept() without closing the connection. This may fix a bug that could close OR listeners when (a) TCP connections were hung up before accept() could be called, or (b) during FD exhaustion.

svn:r2579
Nick Mathewson 21 years ago
parent
commit
5d53828c57
2 changed files with 18 additions and 2 deletions
  1. 10 0
      src/common/util.h
  2. 8 2
      src/or/connection.c

+ 10 - 0
src/common/util.h

@@ -269,12 +269,22 @@ int parse_addr_and_port_range(const char *s, uint32_t *addr_out,
 /** Return true if e is EINPROGRESS or the local equivalent as returned by
  * a call to connect(). */
 #define ERRNO_IS_CONN_EINPROGRESS(e) ((e) == WSAEINPROGRESS || (e)== WSAEINVAL)
+/** Return true if e is EAGAIN or another error indicating that a call to
+ * accept() has no pending connections to return. */
+#define ERRNO_IS_ACCEPT_EAGAIN(e)    ERRNO_IS_EAGAIN(e)
+/** Return true if e is EMFILE or another error indicating that a call to
+ * accept() has failed because we're out of fds or something. */
+#define ERRNO_IS_ACCEPT_RESOURCE_LIMIT(e) \
+  ((e) == WSAEMFILE || (e) == WSAENOBUFS)
 int tor_socket_errno(int sock);
 const char *tor_socket_strerror(int e);
 #else
 #define ERRNO_IS_EAGAIN(e)           ((e) == EAGAIN)
 #define ERRNO_IS_EINPROGRESS(e)      ((e) == EINPROGRESS)
 #define ERRNO_IS_CONN_EINPROGRESS(e) ((e) == EINPROGRESS)
+#define ERRNO_IS_ACCEPT_EAGAIN(e)    ((e) == EAGAIN || (e) == ECONNABORTED)
+#define ERRNO_IS_ACCEPT_RESOURCE_LIMIT(e) \
+  ((e) == EMFILE || (e) == ENFILE || (e) == ENOBUFS || (e) == ENOMEM)
 #define tor_socket_errno(sock)       (errno)
 #define tor_socket_strerror(e)       strerror(e)
 #endif

+ 8 - 2
src/or/connection.c

@@ -385,11 +385,17 @@ static int connection_handle_listener_read(connection_t *conn, int new_type) {
 
   news = accept(conn->s,(struct sockaddr *)&remote,&remotelen);
   if (news == -1) { /* accept() error */
-    if(ERRNO_IS_EAGAIN(tor_socket_errno(conn->s))) {
+    int e = tor_socket_errno(conn->s);
+    if (ERRNO_IS_ACCEPT_EAGAIN(e)) {
       return 0; /* he hung up before we could accept(). that's fine. */
+    } else if (ERRNO_IS_ACCEPT_RESOURCE_LIMIT(e)) {
+      log_fn(LOG_WARN,"accept failed: %s. Dropping incomming connection.",
+             tor_socket_strerror(e));
+      return 0;
     }
     /* else there was a real error. */
-    log_fn(LOG_WARN,"accept() failed. Closing listener.");
+    log_fn(LOG_WARN,"accept() failed: %s. Closing listener.", 
+           tor_socket_strerror(e));
     connection_mark_for_close(conn);
     return -1;
   }