Currently trying to make handshake process on python using raw sockets but for some reason I can't send any packet with TCP protocol receiving OSError: [WinError 10022] An invalid argument was supplied. Here is my code:
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
s.sendto(packet, ('8.8.8.8', 80))
As packet I have tried to use scapy's TCP packet, TCP bytes from wireshark after successful sendings with other libraries and also hand-made byte strings:
def chksum(packet: bytes) -> int:
if len(packet) % 2 != 0:
packet += b'\0'
res = sum(array.array("H", packet))
res = (res >> 16) + (res & 0xffff)
res += res >> 16
return (~res) & 0xffff
class TCPPacket:
def __init__(self,
src_host: str,
src_port: int,
dst_host: str,
dst_port: int,
flags: int = 0):
self.src_host = src_host
self.src_port = src_port
self.dst_host = dst_host
self.dst_port = dst_port
self.flags = flags
def build(self) -> bytes:
packet = struct.pack(
'!HHIIBBHHH',
self.src_port, # Source Port
self.dst_port, # Destination Port
0, # Sequence Number
0, # Acknoledgement Number
5 << 4, # Data Offset
self.flags, # Flags
8192, # Window
0, # Checksum (initial value)
0 # Urgent pointer
)
pseudo_hdr = struct.pack(
'!4s4sHH',
socket.inet_aton(self.src_host), # Source Address
socket.inet_aton(self.dst_host), # Destination Address
socket.IPPROTO_TCP, # PTCL
len(packet) # TCP Length
)
checksum = chksum(pseudo_hdr + packet)
packet = packet[:16] + struct.pack('H', checksum) + packet[18:]
return packet
So literally no idea why my socket doesn't like any packet
I found out what is wrong. Windows doesn't allow to send TCP packets with raw sockets so this code will never work. Probably it is possible to write the same with scapy or using other libraries but that's not what I need so the only way to make it work is to run on linux. Still not sure if the packet creation is correct but TCP protocol with raw sockets sure works fine on linux.
I have threaded Python 2.7.12 code that waits for connections from external devices, and then spins off a thread to work with each device. 80% of the time this works fine, but every once in a while I get unwanted RST from the OS or Python, I'm not sure which.
The TCP connection starts off normally and then abrubptly is reset.
SYN, SYN/ACK, ACK, RST
The server initiates the RST. The timing is also extremely tight so I feel like this is probably Python's socket bugging out or the OS taking over for some reason. How could I debug this? Since I'm not seeing any errors in my Python code and logging itself, is there a ways to debug the sockets code directly used by Python?
Here is the code:
while True:
host = socket.gethostbyname(socket.gethostname())
port = 3001
try:
tcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpsock.bind((host,port))
tcpsock.listen(5)
print ("\nListening for incoming connections...")
(clientsock, (ip, port)) = tcpsock.accept()
newthread = ClientThread(ip, port, clientsock)
newthread.start()
threads = 0
for t in threading.enumerate():
threads += 1
logger.info('######################## THREADS = %s' % (threads))
except Exception as e:
logger.critical('Exception: %s. Error initializing thread' % (e))
EDIT1 - Adding ClientThread code:
class ClientThread(threading.Thread):
def __init__(self,ip,port,clientsocket):
threading.Thread.__init__(self)
self.ip = ip
self.port = port
self.csocket = clientsocket
The rest of the code just starts working with the device.
The RST packet means "I don't have a connection open for the packet you just sent me". You're right, if you follow the simple examples on python.org, then Python will generate RST packets. For example:
#!/usr/bin/python2.7
import socket, sys, time
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind the socket to the port
server_port = 10000
print 'starting up on port %s' % server_port
sock.bind(('', server_port))
# Listen for incoming connections
sock.listen(1)
print 'waiting for a connection'
conn, cli_addr = sock.accept()
print 'connection from', cli_addr
data = "Hello, World!\n" \
+ "Your IP is " + cli_addr[0] + "\n"
HTTP = "HTTP/1.1 200 OK\nContent-Type: text/html\nContent-Length:"
conn.sendall(HTTP + str(len(data)) + "\n\n" + data);
conn.close()
tcpdump then says:
10:59:48.847443 IP 127.0.0.1.38330 > 127.0.0.1.10000: Flags [S], seq 1326297161, win 43690, options [mss 65495,sackOK,TS val 3126487416 ecr 0,nop,wscale 7], length 0
10:59:48.847462 IP 127.0.0.1.10000 > 127.0.0.1.38330: Flags [S.], seq 1677934801, ack 1326297162, win 43690, options [mss 65495,sackOK,TS val 3126487416 ecr 3126487416,nop,wscale 7], length 0
10:59:48.847475 IP 127.0.0.1.38330 > 127.0.0.1.10000: Flags [.], ack 1, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 0
10:59:48.847547 IP 127.0.0.1.38330 > 127.0.0.1.10000: Flags [P.], seq 1:80, ack 1, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 79
10:59:48.847557 IP 127.0.0.1.10000 > 127.0.0.1.38330: Flags [.], ack 80, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 0
10:59:48.847767 IP 127.0.0.1.10000 > 127.0.0.1.38330: Flags [P.], seq 1:95, ack 80, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 94
10:59:48.847790 IP 127.0.0.1.38330 > 127.0.0.1.10000: Flags [.], ack 95, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 0
10:59:48.847861 IP 127.0.0.1.10000 > 127.0.0.1.38330: Flags [R.], seq 95, ack 80, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 0
10:59:48.847862 IP 127.0.0.1.38330 > 127.0.0.1.10000: Flags [F.], seq 80, ack 95, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 0
10:59:48.847878 IP 127.0.0.1.10000 > 127.0.0.1.38330: Flags [R], seq 1677934896, win 0, length 0
The problem is that conn.close() destroys the socket data structure, so the OS doesn't recognize the next packet. Notice that the client (curl) is sending a nice FIN packet, but the server (Python code above) is not.
To solve this, you need to send a FIN packet by calling conn.shutdown(socket.SHUT_WR) and then leave the socket structure around long enough to catch any straggling packets (you could do this in another thread). So instead of conn.close(), do:
conn.shutdown(socket.SHUT_WR)
time.sleep(1)
conn.close()
With this change, both sides send FIN, and the RST are missing from tcpdump:
11:05:50.201352 IP 127.0.0.1.38338 > 127.0.0.1.10000: Flags [S], seq 4249130338, win 43690, options [mss 65495,sackOK,TS val 3126848770 ecr 0,nop,wscale 7], length 0
11:05:50.201368 IP 127.0.0.1.10000 > 127.0.0.1.38338: Flags [S.], seq 1410528158, ack 4249130339, win 43690, options [mss 65495,sackOK,TS val 3126848770 ecr 3126848770,nop,wscale 7], length 0
11:05:50.201381 IP 127.0.0.1.38338 > 127.0.0.1.10000: Flags [.], ack 1, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 0
11:05:50.201441 IP 127.0.0.1.38338 > 127.0.0.1.10000: Flags [P.], seq 1:80, ack 1, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 79
11:05:50.201450 IP 127.0.0.1.10000 > 127.0.0.1.38338: Flags [.], ack 80, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 0
11:05:50.201568 IP 127.0.0.1.10000 > 127.0.0.1.38338: Flags [P.], seq 1:95, ack 80, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 94
11:05:50.201594 IP 127.0.0.1.38338 > 127.0.0.1.10000: Flags [.], ack 95, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 0
11:05:50.201643 IP 127.0.0.1.10000 > 127.0.0.1.38338: Flags [F.], seq 95, ack 80, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 0
11:05:50.201691 IP 127.0.0.1.38338 > 127.0.0.1.10000: Flags [F.], seq 80, ack 96, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 0
11:05:50.201707 IP 127.0.0.1.10000 > 127.0.0.1.38338: Flags [.], ack 81, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 0
I want to use scapy to do tcp 3-way hand shake, my code is below:
from scapy.all import *
# VARIABLES
src = sys.argv[1]
dst = sys.argv[2]
sport = random.randint(20000,65535)
dport = int(sys.argv[3])
print src
print dst
print sport
print dport
# SYN
ip=IP(src=src,dst=dst)
SYN=TCP(sport=sport,dport=dport,flags='S',seq=1000)
SYNACK=sr1(ip/SYN)
SYN.show()
SYNACK.show()
# ACK
seq=SYNACK.ack + 1
ack=SYNACK.seq + 1
print seq
print ack
ACK=TCP(sport=sport, dport=dport, flags='A', seq=seq, ack=ack)
sr1(ip/ACK)
ACK.show()
exec command:
python 3WSK.py 10.16.11.21 10.11.157.130 26789
And the SYNACK result is below:
###[ IP ]###
version = 4L
ihl = 5L
tos = 0x0
len = 44
id = 0
flags = DF
frag = 0L
ttl = 60
proto = tcp
chksum = 0x821a
src = 10.11.157.130
dst = 10.16.11.21
\options \
###[ TCP ]###
sport = 26789
dport = 35067
seq = 1918207620
ack = 1001
dataofs = 6L
reserved = 0L
flags = SA
window = 5840
chksum = 0xd630
urgptr = 0
options = [('MSS', 1460)]
###[ Padding ]###
load = '\x00\x00'
but my tcp server not log any thing.
for test:
I use telnet 10.11.157.130 26789 to connect my tcp server, then my tcp server can log something like: "INFO l.t.TcpServer - in, /10.16.11.21:34906"
So, I`m not sure my scapy code is connect successful or not, I also want to know
how to validate whether tcp connect is work or not. thanks
Check if you see a RST being sent from your os when the SYN+ACK arrives. If so have a look at this answer and drop the RST with iptables:
iptables -A OUTPUT -p tcp --tcp-flags RST RST -s <your_ip> -j DROP
If thats not the case have a look at this gist which works fine for me.
I'm just trying out zeromq for a project in python. I'm trying to bind the 'client' side and connect the server to the fixed location. I have a simple REQ/REP setup that works fine locally but seems to do nothing over a network. If I reverse the binding, then this also works over the network.
The relevant code is:
def respond(sock):
message = sock.recv()
response = "world"
sock.send(response)
print("Received '{0:s}', sent '{1:s}'.".format(message, response) )
def request(sock):
message = "Hello"
sock.send(message)
response = sock.recv()
print("Sent '{0:s}', recieved '{1:s}'".format(message, response) )
def main():
opts = get_opts()
if opts.client:
sock = CONTEXT.socket(zmq.REQ)
sock.bind("tcp://*:{0:d}".format(opts.port) )
request(sock)
if opts.server:
sock = CONTEXT.socket(zmq.REP)
sock.connect("tcp://{0:s}:{1:d}".format(opts.address, opts.port) )
while True:
respond(sock)
And a (non-)working example is here: https://gist.github.com/4071783
When connecting to a remote address, nothing seems to happen. If I check with tcpdump, I can certainly see activity on the port:
12:20:18.846927 IP server.58387 > client.5555: Flags [.], ack 1, win 3650, options [nop,nop,TS val 718051 ecr 46170252], length 0
12:20:18.847156 IP client.5555 > server.58387: Flags [P.], seq 1:3, ack 1, win 227, options [nop,nop,TS val 46170252 ecr 718051], length 2
12:20:18.847349 IP server.58387 > client.5555: Flags [P.], seq 1:3, ack 1, win 3650, options [nop,nop,TS val 718051 ecr 46170252], length 2
12:20:18.847373 IP client.5555 > server.58387: Flags [.], ack 3, win 227, options [nop,nop,TS val 46170252 ecr 718051], length 0
12:20:18.847553 IP client.5555 > server.58387: Flags [P.], seq 3:16, ack 3, win 227, options [nop,nop,TS val 46170252 ecr 718051], length 13
12:20:18.847645 IP server.58387 > client.5555: Flags [.], ack 3, win 3650, options [nop,nop,TS val 718051 ecr 46170252], length 0
12:20:18.848286 IP server.58387 > client.5555: Flags [.], ack 16, win 3650, options [nop,nop,TS val 718051 ecr 46170252], length 0
But the send() and recv() are still blocked as if waiting for a connection. Does anyone know what this is or could suggest how to debug it?
Can you ping the remote address? 0MQ is just using TCP at this level so if the connect fails, it's because the address you're trying to connect to is not reachable.
I have a raw Python socket initialized like so:
mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
mySocket.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
My problem is that I can't send any packet from the socket where the protocol field in my IP header is specified as '1' (ICMP). If this field is anything else it'll work just fine, including '17' (UDP) for example. I don't get any errors, the packet just doesn't display in Wireshark, and I can't receive it on my server.
I'm generating the IP header like so:
def getIPHeader(src, dst, proto):
version = 4
IHL = 5
DSCP = 0
ECN = 0
totalLength = 40
identification = 333
flags = 0
fragmentOffset = 0
timeToLive = 128
protocol = proto
headerChecksum = 0
sourceIP = socket.inet_aton(src)
destIP = socket.inet_aton(dst)
options = 0
version_IHL = (version << 4) | IHL
DSCP_ECN = (DSCP << 2) | ECN
flags_fragmentOffset = (flags << 13) | fragmentOffset
# The '!' ensures all arguments are converted to network byte order
header = struct.pack("!BBHHHBBH4s4s", version_IHL, DSCP_ECN, totalLength, identification, flags_fragmentOffset, timeToLive, protocol, headerChecksum, sourceIP, destIP)
return header
And sending like so:
icmpHeader = struct.pack("!bbHHh", 8, 0, 0, 666, 0)
packet = getIPHeader("192.168.2.15", "8.8.8.8", 1) + icmpHeader
mySocket.sendto(packet, ("8.8.8.8", 0))
Sending with UDP protocol set works (obviously malformed):
icmpHeader = struct.pack("!bbHHh", 8, 0, 0, 666, 0)
packet = getIPHeader("192.168.2.15", "8.8.8.8", 17) + icmpHeader
mySocket.sendto(packet, ("8.8.8.8", 0))
The worst part is if I send a ping from the command line, then copy the exact hex from Wireshark of the IP and ICMP data into my code, it STILL doesn't work, like so:
packet = b'\x45\x00\x00\x3c\x24\xad\x00\x00\x80\x01\x43\x4d\xc0\xa8\x02\x0f\x08\x08\x08\x08\x08\x00\x4d\x44\x00\x01\x00\x17\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x61\x62\x63\x64\x65\x66\x67\x68\x69'
mySocket.sendto(packet, ("8.8.8.8", 0))
However, as before, if I change the 10th byte of the hard coded data to '\x11' (17 decimal, UDP), I can then see it in Wireshark (obviously with a malformed UDP section as the rest of the packet is ICMP).
I'm running Windows 10, Python 3.4.1, and my firewall is off. Any ideas?
UPDATE: I've tried this on two different computers, a Windows 8 and a Windows 10 machine, both worked, so it's something to do with my computer, the plot thickens...