I have a socket proxy written in Python which when it receives a RST from a pair of communicating peers will close the connection to both peers by letting the sockets be garbage collected. This results in the other peer seeing a FIN rather than a RST.
This means the proxy effectively translates RST into FIN, which I don't think is ideal.
I found that in Linux it possible to reset a TCP connnection by calling connect with an address of family AF_UNSPEC
. But I haven't found a way to do this from a Python program.
How do I connect
to an AF_UNSPEC
address in Python?
What I have tried so far
I tried looking at the help
output for the relevant connect
method and found this:
Help on built-in function connect:
connect(...)
connect(address)
Connect the socket to a remote address. For IP sockets, the address
is a pair (host, port).
Unfortunately that doesn't tell me what the address
argument has to be in order to construct a AF_UNSPEC
address.
I attempted to wrap the original socket fd in a new socket object with family AF_UNSPEC
like this:
socket.fromfd(s.fileno(), socket.AF_UNSPEC, 0)
The resulting object produce the same help text and any attempt to call connect
on the newly constructed socket object results in
socket.error: getsockaddrarg: bad family
So it looks like using socket.fromfd
is probably not the answer to my question.
Looking at the current socket
package implementation in CPython, there is really no pythonic way (to connect a socket to an AF_UNSPEC
address, as of 2019-01 (i.e. to reset the connection on Linux).
The next best thing is to set the SO_LINGER
option on the accepted socket (either directly or via inheritance). When lingering is enabled (and set to a zero timeout) closing the socket yields a reset of the connection.
You have to be careful to set the SO_LINGER
option on the right sockets API level and to use the right encoding for the option value (it's a struct).
Example:
import socket
import struct
import time
s = socket.socket(socket.AF_INET6)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
# if we want to inherit this option:
#s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
s.bind(('', 2323))
s.listen()
con, addr = s.accept()
con.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
time.sleep(1)
con.close()
time.sleep(3)
Connecting to this port with curl:
$ curl localhost:2323
curl: (56) Recv failure: Connection reset by peer
Connecting to this port without sending anything:
$ socat - tcp:localhost:2323
When dumping the packets with e.g.
$ tshark -i lo -f 'tcp port 2323'
the last packet should be a RST (sent from server to client), in both cases - for example:
39 9758.478140247 127.0.0.1 → 127.0.0.1 TCP 66 2323 → 34494 [RST, ACK]
Seq=1 Ack=1 Win=43776 Len=0 TSval=2787120418 TSecr=2787119417
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With