inetaf / tcpproxy

Proxy TCP connections based on static rules, HTTP Host headers, and SNI server names (Go package or binary)

Home Page:https://pkg.go.dev/github.com/inetaf/tcpproxy

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

tcpproxy doesn't do a graceful shutdown of the tcp connections

databus23 opened this issue · comments

Hi,

reading through the code it seems to me the teardown of tcp connections is not graceful and might close connections to early.
HandleConn calls Close() on both tcp connections when it returns (using defer) and
HandleConn returns as soon as a one of the two proxyCopy goroutines exits.
This will shutdown the second proxyCopy goroutine which still might want to transfer data in the opposite direction.

I wrote a little contrived rpc scenario where a tcp client closes its write stream to signal an EOF and waits for a reply:

Client:

tcpConn.Write([]byte(`ping`))
tcpConn.CloseWrite()
// Wait for response 
response, err := ioutil.ReadAll(tcpConn)
fmt.Println("response:", string(response), "err:", err)

Server:

request, err := ioutil.ReadAll(conn)
time.Sleep(100 * time.Millisecond)
conn.Write([]byte(`pong`))

When I connect the client and server directly this works but when putting tcpproxy in the middle the client gets an empty response because the tcp connections are closed as soon as the client issues CloseWrite. So in this case tcpproxy causes different behaviour as when connecting directly.

Looking at the manpage of socat I can find this section which seems related:

When one of the streams effectively reaches EOF, the closing phase begins. Socat transfers the EOF condition to the other stream, i.e. tries to shutdown only its write stream, giving it a chance to terminate gracefully. For a defined time socat continues to transfer data in the other direction, but then closes all remaining channels and terminates.

So my question would be if it would be better to propagate the WriteClose() instead of just closing the tcp connection.