diff --git a/src/conns.c b/src/conns.c index 19aaa49c..b70fbdeb 100644 --- a/src/conns.c +++ b/src/conns.c @@ -28,6 +28,7 @@ #include "conns.h" #include "heap.h" #include "log.h" +#include "sock.h" #include "stats.h" void conn_struct_init(struct conn_s *connptr) { @@ -82,14 +83,10 @@ void conn_destroy_contents (struct conn_s *connptr) assert (connptr != NULL); if (connptr->client_fd != -1) - if (close (connptr->client_fd) < 0) - log_message (LOG_INFO, "Client (%d) close message: %s", - connptr->client_fd, strerror (errno)); + close_socket (connptr->client_fd); connptr->client_fd = -1; if (connptr->server_fd != -1) - if (close (connptr->server_fd) < 0) - log_message (LOG_INFO, "Server (%d) close message: %s", - connptr->server_fd, strerror (errno)); + close_socket (connptr->server_fd); connptr->server_fd = -1; if (connptr->cbuffer) diff --git a/src/reqs.c b/src/reqs.c index 94ce7673..e3e7e311 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1281,6 +1281,7 @@ static void relay_connection (struct conn_s *connptr) if (write_buffer (connptr->server_fd, connptr->cbuffer) < 0) break; } + shutdown (connptr->server_fd, SHUT_WR); return; } diff --git a/src/sock.c b/src/sock.c index b3b0920d..50385506 100644 --- a/src/sock.c +++ b/src/sock.c @@ -212,6 +212,43 @@ int opensock (const char *host, int port, const char *bind_to) return sockfd; } +/* + * Gracefully close a socket by completing the TCP close handshake. + * Send FIN via shutdown(SHUT_WR), then drain remaining data until + * the remote FIN arrives (read returns 0). This keeps the fd open + * until both sides have exchanged FINs, preventing the socket from + * being orphaned in FIN_WAIT_2. + * + * Without this, close() orphans the socket while still in FIN_WAIT_2. + * Linux reaps orphaned FIN_WAIT_2 via net.ipv4.tcp_fin_timeout, but + * OpenBSD has no equivalent, so they persist indefinitely. + */ +void close_socket (int fd) +{ + char drain[4096]; + ssize_t n; + struct timeval tv; + + shutdown (fd, SHUT_WR); + + tv.tv_sec = 10; + tv.tv_usec = 0; + setsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)); + + for (;;) { + n = read (fd, drain, sizeof (drain)); + if (n > 0) + continue; + if (n == 0) + break; + if (errno == EINTR) + continue; + break; + } + + close (fd); +} + /* * Set the socket to non blocking -rjkaes */ diff --git a/src/sock.h b/src/sock.h index 70c44739..700e958e 100644 --- a/src/sock.h +++ b/src/sock.h @@ -53,6 +53,7 @@ union sockaddr_union { extern int opensock (const char *host, int port, const char *bind_to); extern int listen_sock (const char *addr, uint16_t port, sblist* listen_fds); +extern void close_socket (int fd); extern int socket_nonblocking (int sock); extern int socket_blocking (int sock);