Reputation: 2042
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.
Upvotes: 5
Views: 10409
Reputation: 39075
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
Upvotes: 2
Reputation: 3541
You can try to use the SO_LINGER
socket option ( setsockopt
) with linger time set to 0. close
on socket with SO_LINGER
set with 0 seconds lingering time will result in RST instead of FIN.
Upvotes: 0