I'm trying to send packets using sockets, and was able to do so just fine until this morning. I'm not sure what's going on. The packets are showing up in tcpdump but the server and the client cannot connect to each other.
netcat.py
import socket
import argparse
import sys
import os
import re
import threading
def convertContent(content: str = "") -> bytes:
byteContent = []
# grab the hex from the content
for i in range(len(content)):
if content[i] == "\\" and content[i+1] == "x":
byteContent.append(f"{content[i+2]}{content[i+3]}")
# grab the non hex from the content, split it on the hex
stringContent = re.split(r"\\x.{2}", content)
byteIndex = 0
newContent = b""
# Re add the non-hex content, and the hex content
for word in stringContent:
newContent += word.encode()
if byteIndex < len(byteContent):
newContent += bytes.fromhex(byteContent[byteIndex])
byteIndex += 1
newContent = newContent.replace(b"\\n", b"\n").replace(b"\\r", b"\r")
return newContent
class Netcat():
'''
Netcat class that can be used to send/receive TCP packets
'''
BUFFER_SIZE = 1024
def __init__(self):
pass
#classmethod
def createSocket(cls):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Address might be in a TIME_WAIT status, ignore this
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Port might be in a TIME_WAIT status, ignore this
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
return sock
#classmethod
def send(cls, hostname: str = "127.0.0.1", srcPort: int = 0, destPort: int = 9999, content: str = "", buffer_size: int = 1024):
srcPort = int(srcPort)
destPort = int(destPort)
try:
content = convertContent(content=content)
except:
pass
sock = cls.createSocket()
# Set the source port before sending
sock.connect((hostname, destPort))
sock.sendall(content)
# shutdown might be redundant/unnecessary (tells connected host that we're done sending data)
sock.shutdown(socket.SHUT_WR)
while True:
data = sock.recv(buffer_size)
if len(data) == 0:
break
sock.close()
#classmethod
def receive(cls, port: int = 9999, buffer_size: int = 1024):
if port <= 1024 and os.geteuid() != 0:
print(f"Listening on port {port} requires superuser privileges!")
return
host = ""
sock = cls.createSocket()
sock.bind((host, port))
sock.listen(10)
conn, addr = sock.accept()
while True:
data = conn.recv(buffer_size)
if not data:
break
conn.close()
threading.Thread(target=Netcat.receive,daemon=True).start()
Netcat.send(content="test")
Note: I am sending the packets from one VM to another, rather than sending to myself, but it would be a lot to ask people to spin up a bunch of VMs to reproduce this. The hostname param in the send method should be the actual IP of the receiving machine
I've thrown some print statements, and the server stops on sock.accept(), while the client hangs on sock.connect((hostname, destPort))
I checked the hostname for the server, and it's listening on (0.0.0.0, 8888) (assuming 8888 is the port param), which means its listening on all interfaces on that port, so I dont know why its refusing to connect
I tcpdumped on the server, and its getting the packets, it gets a SYN, then sends out a SYN, ACK, but the rest of the packets are marked as re-transmissions.
I've tried looping the accept & connect lines, thinking maybe some sort of race condition was occurring, but no matter what I do the client can't connect to the server.
Edit: This works on my local machine, but still breaks when I try to send packets over the network. The first 2 steps of the handshake go through SYN & SYN, ACK, but not the third ACK
Don't bind in the client. Working example below, but minor changes to make a standalone script:
import socket
import threading
def receive(port: int = 9999, buffer_size: int = 1024):
host = ""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Address might be in a TIME_WAIT status, ignore this
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen()
conn, addr = sock.accept()
while True:
data = conn.recv(buffer_size)
if not data:
break
print(data)
conn.close()
def send(hostname: str = "127.0.0.1", destPort: int = 9999, content: str = b"test", buffer_size: int = 1024):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Address might be in a TIME_WAIT status, ignore this
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Removed bind
sock.connect((hostname, destPort))
sock.sendall(content)
# shutdown might be redundant/unnecessary (tells connected host that we're done sending data)
sock.shutdown(socket.SHUT_WR)
while True:
data = sock.recv(buffer_size)
if len(data) == 0:
break
sock.close()
threading.Thread(target=receive,daemon=True).start()
send()
Output:
b'test'
Related
I have a listener on a tcp localhost:
HOST = '127.0.0.1' # The server's hostname or IP address
PORT = 8192 # The port used by the server
def client_socket():
while 1:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP,TCP_PORT))
s.listen(1)
while 1:
print 'Listening for client...'
conn, addr = s.accept()
print 'Connection address:', addr
data = conn.recv(BUFFER_SIZE)
if data == ";" :
conn.close()
print "Received all the data"
i=0
for x in param:
print x
#break
elif data:
print "received data: ", data
param.insert(i,data)
i+=1
#print "End of transmission"
s.close()
I am trying to send a JSON object to the same port on the local host:
HOST = '127.0.0.1' # The server's hostname or IP address
PORT = 8192 # The port used by the server
def json_message(direction):
local_ip = socket.gethostbyname(socket.gethostname())
data = {
'sender' : local_ip,
'instruction' : direction
}
json_data = json.dumps(data, sort_keys=False, indent=2)
print("data %s" % json_data)
send_message(json_data)
return json_data
def send_message(data):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
s.sendall(data)
data = s.recv(1024)
print('Received', repr(data))
However, I get a socket error:
socket.error: [Errno 98] Address already in use
What am I doing wrong? Will this work or do I need to serialize the JSON object?
There are a few problems with your code, but the one that will likely address your issue is setting the SO_REUSEADDR socket option with:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
after you create the socket (with socket.socket(...) but before you attempt to bind to an address (with s.bind().
In terms of other things, the two "halves" of the code are pretty inconsistent -- like you copied and pasted code from two different places and tried to use them?
(One uses a context manager and Python 3 print syntax while the other uses Python 2 print syntax...)
But I've written enough socket programs that I can decipher pretty much anything, so here's a working version of your code (with some pretty suboptimal parameters e.g. a buffer size of 1, but how else would you expect to catch a single ;?)
Server:
import socket
HOST = '127.0.0.1' # The server's hostname or IP address
PORT = 8192 # The port used by the server
BUFFER_SIZE = 1
def server_socket():
data = []
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST,PORT))
s.listen()
while 1: # Accept connections from multiple clients
print('Listening for client...')
conn, addr = s.accept()
print('Connection address:', addr)
while 1: # Accept multiple messages from each client
buffer = conn.recv(BUFFER_SIZE)
buffer = buffer.decode()
if buffer == ";":
conn.close()
print("Received all the data")
for x in data:
print(x)
break
elif buffer:
print("received data: ", buffer)
data.append(buffer)
else:
break
server_socket()
Client:
import socket
import json
HOST = '127.0.0.1' # The server's hostname or IP address
PORT = 8192 # The port used by the server
def json_message(direction):
local_ip = socket.gethostbyname(socket.gethostname())
data = {
'sender': local_ip,
'instruction': direction
}
json_data = json.dumps(data, sort_keys=False, indent=2)
print("data %s" % json_data)
send_message(json_data + ";")
return json_data
def send_message(data):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
s.sendall(data.encode())
data = s.recv(1024)
print('Received', repr(data))
json_message("SOME_DIRECTION")
Linux Kernel offers a few ways to get timestamps for received (SO_TIMESTAMP, SO_TIMESTAMPNS, SO_TIMESTAMPING) or sent (SO_TIMESTAMPING) packets.
Kernel Doc: https://www.kernel.org/doc/Documentation/networking/timestamping.txt
Is there a way I can use that with Python? I don't see any SO_TIMESTAMP constant inside the Python sources. Tried 3.6.2 and GitHub master branch.
Right now, I can only use SIOCGSTAMP that gives me the timestamp of the last received packet and nothing seems available for sent packet timestamp.
Finally, I have been able to get the SO_TIMESTAMPNS value like this:
SO_TIMESTAMPNS = 35
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(3))
s.setsockopt(socket.SOL_SOCKET, SO_TIMESTAMPNS, 1)
raw_data, ancdata, flags, address = s.recvmsg(65535, 1024)
ancdata[0][2] is the hardware timestamp as a timespec(ulong, ulong).
Does work on Linux but not on Mac OS X. Not tested on Windows.
complete code, send and receive using python3
import struct
import time
import select
import socket
import sys
if(len(sys.argv)!=2):
print("usage: ",sys.argv[0]," <send|receive>")
sys.exit()
print(sys.argv[1]);
if(sys.argv[1]=='send'):
MESSAGE = "Hello, World!"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
s.connect(('localhost', 10000))
s.send(MESSAGE)
time.sleep(0.0001)
#time.sleep(5)
s.send(MESSAGE)
s.close()
else:
SO_TIMESTAMPNS = 35
#s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(3))
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)
server.setsockopt(socket.SOL_SOCKET, SO_TIMESTAMPNS, 1)
server.bind(('localhost', 10000))
server.listen(5)
inputs = [ server ]
message_queues = {}
outputs = []
while inputs:
print('\nwaiting for the next event')
readable, writable, exceptional = select.select(inputs, outputs, inputs)
for s in readable:
if s is server:
connection, client_address = s.accept()
print('new connection from', client_address)
connection.setblocking(0)
inputs.append(connection)
else:
raw_data, ancdata, flags, address = s.recvmsg(65535, 1024)
print('received ', raw_data, '-',ancdata,'-',flags,'-',address)
if(len(ancdata)>0):
#print(len(ancdata),len(ancdata[0]),ancdata[0][0],ancdata[0][1],ancdata[0][2])
#print('ancdata[0][2]:',type(ancdata[0][2])," - ",ancdata[0][2], " - ",len(ancdata[0][2]));
for i in ancdata:
print('ancdata: (cmsg_level, cmsg_type, cmsg_data)=(',i[0],",",i[1],", (",len(i[2]),") ",i[2],")");
if(i[0]!=socket.SOL_SOCKET or i[1]!=SO_TIMESTAMPNS):
continue
tmp=(struct.unpack("iiii",i[2]))
timestamp = tmp[0] + tmp[2]*1e-10
print("SCM_TIMESTAMPNS,", tmp, ", timestamp=",timestamp)
if(not raw_data):
print('closing after reading no data')
# Stop listening for input on the connection
if s in outputs:
outputs.remove(s)
inputs.remove(s)
s.close()
I want to listen port in Python3.
import socket
sock = socket.socket()
sock.bind(('', 9090))
sock.listen(1)
conn, addr = sock.accept()
print 'connected:', addr
while True:
data = conn.recv(1024)
if not data:
break
conn.send(data.upper())
conn.close()
The data which I want to listen, looks like:
8,0,0,test,0,2016/07/19,14:40:57.938,2016/07/19,14:40:57.938,,,,,,,,,,,,0
8,0,0,test,0,2016/07/19,14:40:57.965,2016/07/19,14:40:57.965,,,,,,,,,,,,0
3,0,0,test,0,2016/07/19,14:41:04.687,2016/07/19,14:41:04.687,,2475,,,,,,,,,,0
..
that is I need read until '\n'
So I need to change this block, but I dont know how..
data = conn.recv(1024)
if not data:
break
conn.send(data.upper())
I want replace nc:
nc -k -l -p 30003 | python3 script.py
where script.py
while True:
for string in sys.stdin:
Also I need reconnect if something wrong, server must be ready take all data any time, just like nc -k -l -p 30003 | python3 script.py
The main idea is to read until you find \n character in your stream. Of course \n may be beyond 1024 bytes that you are reading thus you need to store everything you read in a buffer. This can be emulated with for example such class:
class SocketLineReader:
def __init__(self, socket):
self.socket = socket
self._buffer = b''
def readline(self):
pre, separator, post = self._buffer.partition(b'\n')
if separator:
self._buffer = post
return pre + separator
while True:
data = self.socket.recv(1024)
if not data:
return None
pre, separator, post = data.partition(b'\n')
if not separator:
self._buffer += data
else:
data = self._buffer + pre + separator
self._buffer = post
return data
And usage:
import socket
sock = socket.socket()
sock.bind(('', 9090))
sock.listen(1)
conn, addr = sock.accept()
print('connected:', addr)
reader = SocketLineReader(conn)
while True:
data = reader.readline()
print(data)
if not data:
break
conn.send(data.upper())
conn.close()
If you wish for the server to serve data forever use another while loop:
import socket
sock = socket.socket()
sock.bind(('', 9090))
sock.listen(1)
while True:
conn, addr = sock.accept()
print('connected:', addr)
reader = SocketLineReader(conn)
# The other code goes here
The problem with this approach is that there is no parallelism. Your server won't handle parallel connections. One way to fix that is to send each new connection to a separate thread:
import socket
import threading
def handle(conn):
print('connected:', addr)
reader = SocketLineReader(conn)
# The other code goes here
sock = socket.socket()
sock.bind(('', 9090))
sock.listen(1)
while True:
conn, addr = sock.accept()
threading.Thread(target=handle, args=(conn,)).start()
This should be fine until you hit performance limit. There are ways to improve efficiency (e.g. event loops) but I supposes it's beyond this question.
All my clients sockets do the same thing: send a package every second(22 bytes)
Server code as below:
import select
import socket
import datetime
SList = []
class Tserver:
def __init__(self, portNum):
host = '127.0.0.1'
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind((host, portNum))
self.server.listen(1)
def GETPACK():
# function for CRC check
def CRC(DATA_STR):
return 1
# generate 100 sockets to listen
for x in range(100):
SList.append(Tserver(x+10000))
inputs = []
# put in inputs
for x in range(100):
inputs.append(SList[x].server)
while(True):
ready_socks, _, _ = select.select(inputs, [], [])
for sock in ready_socks:
c, addr = sock.accept()
while(True):
data = c.recv(22)
if len(data) == 22: # To make sure the data-length is 22
# Turn the pack string into bytearray
data_bytes = bytearray()
data_bytes.extend(data)
if CRC(data_bytes) == 1:
print "Connected from client IP Address:" + str(addr)
# ID
ID = 256*data_bytes[1] + data_bytes[2]
print "ID: ", ID
now = datetime.datetime.now()
print "now: ", str(now)
if __name__ == "__main__":
GETPACK()
My server can only print the packages sent by the first connected socket.
And my question is how to print out all message from each ports whenever a package is sent to the server.
See this PyMOTW entry for a detailed explanation of how to use the select module to write a select-based server.
The main differences between that example and your code are:
You just create one listening socket - server. There is no need to listen on multiple ports.
The variable inputs will be a list consisting of server and any other open socket connections to clients.
Your service loop will look like:
while true:
readable, _, _ = select.select(inputs, [], [])
for r in readable:
if r is server:
# handle a new incoming connection
# this will add an entry to the variable inputs
else:
# read some data from socket r and process it
When you attempt to read from a client socket and get an EOF condition, you can close that socket and remove it from the inputs variable.
#ErikR Thanks for your help, i changed my code, and it worked fine.
The reason that my code doesn't work was because of two things:
1.I only create one connection to recv data from my clients.
2.The same connection can't be accepted again for recv, if the clients does't reconnect.(my code doesn't check the exception when clients shutdown)
Code as below:
import select, socket, datetime
SList = []
SconnList = []
class Tserver:
def __init__(self, portNum):
host = '127.0.0.1'
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
self.server.bind((host,portNum))
self.server.listen(1)
print "Server ports: "+str(portNum)
class Sconn:
def __init__(self, sock):
self.conn, self.addr = sock.accept()
def GETPACK():
# function for CRC check
def CRC(DATA_STR):
return 1
# generate 100 sockets to listen
for x in range(100):
SList.append(Tserver(x+10000))
inputs = []
# put in inputs
for x in range(100):
inputs.append(SList[x].server)
while(True):
ready_socks,_,_ = select.select(inputs, [], [])
for sock in ready_socks:
try:
SconnList.append(Sconn(sock))
SconnList.reverse()
inputs.append(SconnList[0].conn)
except:
data = sock.recv(22)
if len(data) == 22: # To make sure the data-length is 22
#Turn the pack string into bytearray
data_bytes = bytearray()
data_bytes.extend(data)
if CRC(data_bytes) == 1:
print "IP Address:" + str(sock.getsockname())
#ID
ID = 256*data_bytes[1] + data_bytes[2]
print "ID: ",ID
now = datetime.datetime.now()
print "now: ",str(now)
print ""
print ""
if __name__ == "__main__":
GETPACK()
I don't know how to make the server to take the command from the user ... I have untill now this code for the server
import socket, sys
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#HOST = sys.argv.pop() if len(sys.argv) == 3 else '127.0.0.1'
HOST = 'localhost'
PORT = 8080
def recv_all(sock, length):
data = ''
while len(data) < length:
more = sock.recv(length - len(data)).decode()
if not more:
raise EOFError('socket closed %d bytes into a %d-byte message' %
(len(data), length))
data += more
return data
#if sys.argv[1:] == ['server']:
if True:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(1)
while True:
print('Listening at', s.getsockname())
sc, sockname = s.accept()
print('We have accepted a connection from', sockname)
print('Socket connects', sc.getsockname(), 'and', sc.getpeername())
message = recv_all(sc, 16)
print('The incoming sixteen-octet message says', repr(message))
sc.sendall('Farewell, client'.encode())
This is the rest from srver-side and i can't get working it from here , in rest everything works perfect! ....
if repr(message) == 'exit':
sc.close()
print('Reply sent, socket closed')
else:
print(sys.stderr, 'usage: tcp_local.py server|client [host]')
And this code for the client
import socket, sys
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# HOST = sys.argv.pop() if len(sys.argv) == 3 else '127.0.0.1'
HOST = 'localhost'
PORT = 8080
def recv_all(sock, length):
data = ''
while len(data) < length:
more = sock.recv(length - len(data)).decode()
if not more:
raise EOFError('socket closed %d bytes into a %d-byte message'
% (len(data), length))
data += more
return data
#elif sys.argv[1:] == ['client']:
if True:
print('Connecting to server on: ',HOST,' Port: ',PORT)
s.connect((HOST, PORT))
print('Client has been assigned socket name', s.getsockname())
s.sendall('Hi there, server'.encode())
print('Hi there, server, has been send')
reply = recv_all(s, 16)
print('The server said', repr(reply))
command = (input('enter:').encode())
s.sendall(command)
if s.send(command) == 'exit':
s.close()
else:
print(sys.stderr, 'usage: tcp_local.py server|client [host]')
I can get the client to ask the user for input , and when I enter the string 'exit' (without the quotation marks) in the client side I don't know how to make the server read the raw text and convert it into close server function ... any help would be greatly appreciated ... have a nice Sunday everyone !
I get "Type error : 'str' does not support the buffer interface"
Everythong works fine now , I've added the .encode() at the command = (input('enter:').encode()) part . but how do i make the server to close after this ?
I am not sure of what I am saying but for what I know, if you are using python 3.x, you need to convert string to bytes before sending to through an socket. On the client you should do command = bytes(input('enter:'), "utf-8") and on the server you have to put it back as string so, message = recv_all(sc, 16).decode("utf-8"). Not tested...