Skip to content

Commit 004da4b

Browse files
committed
Fix FIN_WAIT_2 accumulation by draining sockets before close
After shutdown(SHUT_WR) sends a FIN to the remote, tinyproxy was calling close() without waiting for the remote FIN. The socket was orphaned in FIN_WAIT_2 state. On Linux this is masked by tcp_fin_timeout reaping orphaned sockets after 60s. On OpenBSD, without SO_KEEPALIVE, these sockets persist indefinitely and accumulate until the proxy stalls. Add close_socket() that calls shutdown(SHUT_WR), then drains the socket with a 2-second receive timeout to allow the remote FIN to arrive before calling close(). Use it in conn_destroy_contents() for both client and server file descriptors, covering all exit paths from relay_connection() including idle timeout and poll error returns. Also add the missing shutdown(server_fd, SHUT_WR) in relay_connection() after flushing remaining data to the upstream server, so the server receives a proper FIN rather than relying on the implicit close().
1 parent 969852c commit 004da4b

2 files changed

Lines changed: 36 additions & 6 deletions

File tree

src/conns.c

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,48 @@ int conn_init_contents (struct conn_s *connptr, const char *ipaddr,
7777
return 0;
7878
}
7979

80+
/*
81+
* Gracefully close a socket by completing the TCP close handshake.
82+
* Send FIN via shutdown(SHUT_WR), then drain remaining data with a
83+
* short timeout to receive the remote FIN. This prevents connections
84+
* from getting stuck in FIN_WAIT_2 on systems that do not aggressively
85+
* reap orphaned sockets (e.g. OpenBSD without SO_KEEPALIVE).
86+
*/
87+
static void close_socket (int fd)
88+
{
89+
char drain[4096];
90+
ssize_t n;
91+
struct timeval tv;
92+
93+
shutdown (fd, SHUT_WR);
94+
95+
tv.tv_sec = 2;
96+
tv.tv_usec = 0;
97+
setsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv));
98+
99+
for (;;) {
100+
n = read (fd, drain, sizeof (drain));
101+
if (n > 0)
102+
continue;
103+
if (n == 0)
104+
break;
105+
if (errno == EINTR)
106+
continue;
107+
break;
108+
}
109+
110+
close (fd);
111+
}
112+
80113
void conn_destroy_contents (struct conn_s *connptr)
81114
{
82115
assert (connptr != NULL);
83116

84117
if (connptr->client_fd != -1)
85-
if (close (connptr->client_fd) < 0)
86-
log_message (LOG_INFO, "Client (%d) close message: %s",
87-
connptr->client_fd, strerror (errno));
118+
close_socket (connptr->client_fd);
88119
connptr->client_fd = -1;
89120
if (connptr->server_fd != -1)
90-
if (close (connptr->server_fd) < 0)
91-
log_message (LOG_INFO, "Server (%d) close message: %s",
92-
connptr->server_fd, strerror (errno));
121+
close_socket (connptr->server_fd);
93122
connptr->server_fd = -1;
94123

95124
if (connptr->cbuffer)

src/reqs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,7 @@ static void relay_connection (struct conn_s *connptr)
12811281
if (write_buffer (connptr->server_fd, connptr->cbuffer) < 0)
12821282
break;
12831283
}
1284+
shutdown (connptr->server_fd, SHUT_WR);
12841285

12851286
return;
12861287
}

0 commit comments

Comments
 (0)