Raw sockets in python3 - python

I want to write a packet sniffer that sniffs all incoming TCP packets.In one of the examples that I was looking instead of using SOCK_RAW instead of SOCK_STREAM?
try:
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
except socket.error as e:
print('Socket creation failed. Error Code {} Message {}'.format(str(e[0]),str(e[1])))
sys.exit()
#Include IP headers
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
packet = s.recvfrom(65565)
1) In the above case can I use SOCK_STREAM instead of SOCK_RAW.
2) What does recvfrom(65565) mean ?
Does it mean recvfrom all TCP ports instead of a specific TCP port?

If you use SOCK_STREAM instead of SOCK_RAW you won't be able to read the protocols headers, but only the data transmitted via TCP. In the other hand, SOCK_RAW will give you access to the full packet headers. In your case, as you want to build your own protocol analyzer (sniffer), SOCK_RAW should be your choice.
The method definition for recvfrom is:
socket.recvfrom(bufsize[, flags])
Receive data from the socket. The return value is a pair (string,
address) where
string is a string representing the data received and address is the address of the
socket sending the data
This method simply receives maximum bufsize bytes from the socket.

Related

Socket Fragmented Received Data

I'm trying to create some kind of client monitor, like a terminal, to receive data from a serial device over ethernet. I'm trying to use a socket with python, but the problem comes when I create the connection. I'm supposed to receive only one message from the server, and I get the whole message but split into two packets, like this:
Message expected:
b'-- VOID MESSAGE--'
Message received:
b'-- VOID'
b' MESSAGE--'
I don't know if is this a problem of buffer size, decoding or any other function
import socket
TCP_IP = '192.168.#.#'
TCP_PORT = ###
BUFFER_SIZE = 1024
data1=' '
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
while(1):
data = s.recv(BUFFER_SIZE)
print(data.decode('ASCII'))
s.close()
I've already tried with some codecs options like UTF-8, UTF-16 and ASCII but I still get the same result.
This function helped me to solve the issue.
while(1):
cadena += s.recv(1)
if (((cadena)[i])=='\n'):
print(cadena.decode('ASCII'))
cadena=b''
i=-1
i+=1
As it already was said - that's how sockets works.
Sent data could be splitted to chunks. So if you want to be sure, that you've received whole message that was sent you need to implement some kind of protocol, the part of which will be contain length of your message. For example:
First four bytes (integer) represents length of the message
Other bytes - content of the message
In such case algorithm to send a message will be look like:
Count length of the message
Write to socket integer (4 bytes) with message length
Write to socket content of the message
And reading algorithm:
Read bytes from socket and write read data to accumulator-buffer
Read first four bytes from buffer as integer - it will be message length
Check if buffer length is greater or equal "{message length} + 4"
If it's then read required amount of bytes and that will message that was sent.
Drop first "{message length} + 4" bytes from buffer
Repeat from second point
If it's not enough bytes to read message content repeat from first point.
One solution is to use UDP instead of TCP if you can live with the limitations:
There is a size limit, the data must fit into one packet
UDP is "unreliable".
A TCP connection transfer one single stream of bytes. OTOH UDP transfers individual datagrams (messages). If the sender sends N datagrams, the recipient shall receive the same N datagrams. Maybe out of order, maybe some will get lost, but each datagram is independent of all others.
Regarding the limitations, these are not so simple questions. There is plenty of information on these topics, just search.
The max size depends on factors like IPv4 or IPv6, fragmentation etc. and there is a best case and a worst case. Typically you can assume that one ethernet frame (for all headers + payload) is absolutely without problems.
The "unreliability" does not mean the quality of transfer is terrible. The network should work on "best effort" basis. It means there are no ACKs, timeouts and retransmits. You can live without it or you can add simple ACKs to your protocol.
You can use this example.
Server code: (read from client)
#!/usr/bin/python3
from socket import socket, gethostname
s = socket()
host = gethostname()
port = 3399
s.bind((host, port))
s.listen(5)
while True:
print("Listening for connections...")
connection, addr = s.accept()
try:
buffer = connection.recv(1024)
response = ''
while buffer:
response += buffer.decode('ASCII')
buffer = connection.recv(1024)
print(response)
connection.close()
except KeyboardInterrupt:
if connection:
connection.close()
break
Client code: (send message)
#!/usr/bin/python3
from socket import socket, gethostname
s = socket()
host = gethostname()
port = 3399
s.connect((host, port))
print("Sending text..")
s.sendall(b'-- VOID MESSAGE--')
print("Done sending..")
s.close()

Can socket receive infinite data stream python?

While trying to attempt to go for python based socket, I have 2 questions which I am not able to resolve. Kindly help me. Here is my sample socket code:
import socket
import threading
import chardet
bind_ip = '0.0.0.0'
bind_port = 9999
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((bind_ip, bind_port))
server.listen(1) # max backlog of connections
print (('Listening on {}:{}').format(bind_ip, bind_port))
def handle_client_connection(client_socket):
request = client_socket.recv(4096 )
result = chardet.detect(request)
print(result)
print (request.decode(result['encoding']))
client_socket.send('ACK!'.encode(result['encoding']))
client_socket.close()
while True:
client_sock, address = server.accept()
print (('Accepted connection from {}:{}').format(address[0], address[1]))
client_handler = threading.Thread(
target=handle_client_connection,
args=(client_sock,) # without comma you'd get a... TypeError: handle_client_connection() argument after * must be a sequence, not _socketobject
)
client_handler.start()
The above one is server and the below is client:
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 9999))
client.send(str('test data').encode("utf-16"))
response = client.recv(4096)
print(response.decode("utf-16"))
Now the questions:
1) What is the meaning of the number in this statement: client.recv(4096)? What is 4096, is it bit or byte of kilobyte for data receiving?
2) Can I receive infinite data stream through the socket? Meaning, as this statement client.recv(4096), whatever is the menaing of 4096, may be byte, then the socket will receive the 4096 bytes of data only. I do not have a control on the size of data received through the socket, hence, can I generalize it to accept any size of data through socket?
Please help me get the answers to above queries. I tried the python documentation for socket but didn't found much. I guess I miss something. Please help me get through it.
According to documentation
"If a message is too long to fit in the supplied buffer, excess bytes may be discarded depending on the type of socket the message is received from."
found here
"https://linux.die.net/man/2/recv"
which was from python 3.6 docs
"he maximum amount of data to be received at once is specified by bufsize. See the Unix manual page recv(2)"
found here
"https://docs.python.org/3.6/library/socket.html"
so it is a bytes object and it may actually truncate the message depending on the protocol. So message integrity is handled by the next layer up. So presumably you would loose part of a UDP packet, and you would get either a retry or an additional packet for TCP if the message was too large to fit in the buffer.

TypeError: str, bytes or bytearray expected, not int [duplicate]

I'm trying to create a simple server to client based chat program and the issue is that when I try to execute c.sendto(data,client) this error appears saying that Client is an int but it's a tuple containing the port number and the address. I'm I supposed to convert the tuple to bytes so I can send the message to the clients?
Server Script
import socket
clients = []
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("127.0.0.1",7999))
s.listen()
print("Waiting for connection")
c, addr = s.accept()
while True:
data , addr = c.recvfrom(1024)
print(data)
if addr not in clients:
clients.append(addr)
print(clients[0])
if data:
for client in clients:
print(client)
c.sendto(data,client)
s.close()
Client Script
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
addr = ("127.0.0.1",7999)
s.connect(addr)
while True:
string = input(">>")
s.send(string.encode("utf-8"))
data =s.recv(1024)
s.close()
Server Output
The problem is that you're using sendto() with a connection-mode socket. I think you want c.send(data) instead.
Details:
The Python docs for sendto say "The socket should not be connected to a remote socket, since the destination socket is specified by address." Also the man page for sendto says "If sendto() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET) socket, the arguments dest_addr and addrlen are ignored (and the error EISCONN may be returned when they are not NULL and 0)." I somewhat suspect that this is happening and Python is misreporting the error in a confusing way.
The sockets interface and networking in general can be pretty confusing but basically sendto() is reserved for SOCK_DGRAM which is UDP/IP type internet traffic, which you can think of as sending letters or postcards to a recipient. Each one goes out with a recipient address on it and there's no guarantee on order of receipt. On the other hand, connection-mode sockets like SOCK_STREAM use TCP/IP which is a bit more like a phone call in that you make a connection for a certain duration and and each thing you send is delivered in order at each end.
Since your code seems to be designed for communication over a connection I think you just want c.send(data) and not sendto.
You must first convert the string that contains the IP address into a byte or a string of bytes and then start communicating.
According to the code below, your error will be resolved.
Make sure your code is working correctly overall.
string = '192.168.1.102'
new_string = bytearray(string,"ascii")
ip_receiver = new_string
s.sendto(text.encode(), (ip_receiver, 5252))

Packet sniffer in Python

I want to do a packet sniffer in Python 3.5 which captures UDP, TCP and ICMP. This is a short example of it:
import socket
import struct
# the public network interface
HOST = socket.gethostbyname(socket.gethostname())
# create a raw socket and bind it to the public interface
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.bind((HOST,0))
# Include IP headers
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# receive all packages
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# receive a package
n=1
while(n<=400):
print('Number ', n)
data=s.recvfrom(65565)
packet=data[0]
address= data[1]
header=struct.unpack('!BBHHHBBHBBBBBBBB', packet[:20])
if(header[6]==6): #header[6] is the field of the Protocol
print("Protocol = TCP")
elif(header[6]==17):
print("Protocol = UDP")
elif(header[5]==1):
print("Protocol = ICMP")
n=n+1
The problem is that it only captures UDP packets :(
Output:
Number 1 Protocol = UDP Number 2 Protocol = UDP Number 3 Protocol = UDP Number 4 Protocol = UDP Number 5 Protocol = UDP Number 6 Protocol = UDP Number 7
There are 2 options:
The sniffer can only capture UDP packets.
I'm just receiving UDP packets.
I think that the most logical answer is my sniffer doesn't work correctly and it's just capturing UDP. Any idea?
I myself am in the stage of creating a python packet parser/sniffer and in my research I found that, to be able parse all the incoming packets like TCP, ICMP, UDP, ARP ..etc., you must not use the below socket type because socket.IPPROTO_IP gives out only IP packets and is a dummy protocol
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
rather you must use this and works best with Linux systems
s = socket.socket( socket.AF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003))
You are using "gethostbyname" and this method obtains one between all ip adresses in your computer.
In place of this method you must use "gethostbyname_ex" that obtains a list with several addreses. You must select the address used for the browser, and you will capure TCP, UDP and ICMP packets.

Python connected datagram socket

I'm building a simple client-server multiplayer game and I want to have connected UDP sockets. However, when I call the listen() method it produces Operation not supported exception.
try:
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind((host, port))
server.listen(5)
except socket.error, (value, message):
print "E: " + message # E: Operation not supported
Is there a way to have connected datagram sockets?
UDP protocol is connectionless and thus you really cannot create connections between 2 sockets in the same manner as with the TCP client and server; thus you also cannot do the listen system call on an UDP socket, as it concerns only the TCP server sockets. Instead you use socket.recvfrom to receive datagrams from any address:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((host, port))
data, addr = sock.recvfrom(65536) # this will fit the maximum datagram
You can respond to the client (if they still have a socket open), by destructuring the addr which is a host, port tuple for socket.AF_INET, socket.SOCK_DGRAM.
client_host, client_port = addr
You can send data back there with sendto
sock.sendto(data, (client_host, client_port))
If you want to use a well-known port, you will bind the socket; if not, the system will assign a port for you.
It is possible to do a connect system call with datagram sockets on many socket implementations; this serves as a sort of filter, that drops the packets coming from unwanted sources, and for setting the default outgoing address for sock.send (it is still OK to use sock.sendto on such socket to another address, though the response might be dropped because of the "connection"). This is very useable on a client or between 2 nodes if they agree to use 2 well known ports with each other.
However if you do connect on server, it cannot serve any other requests on this socket. Also, listen with its queues only relates to SOCK_STREAM sockets.
Thus in case of many clients to 1 server, you could have the server listen to socket 12345, and when a client contacts the server, the server could respond from server:12345 with a message that contains another port number that the client should use with the server.

Categories