RECEIVER_PORT = 5005 # target port
conn = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(3))
while True:
raw_data, addr = conn.recvfrom(65536)
dest_mac, src_mac, eth_proto, data = ethernet_frame(raw_data)
if eth_proto == 8:
(version, header_length, ttl, proto, src, target, data) = ipv4_Packet(data)
# Sniff UDP Communication between the sender and the receiver
if proto == 17:
src_port, dest_port, length, data = udp_seg(data)
if dest_port == RECEIVER_PORT:
print(TAB_2 + 'Source Port: {}, Destination Port: {}, Length: {}'.format(src_port, dest_port, length))
My python packet sniffer monitors communication between two applications: a sender and a receiver. It is particularly interested when a packet is being sent to the receiver. In addition, the receiver ONLY receives packets from the sender. Whenever a packet is sent from the sender to the receiver, my program executes "if dest_port == RECEIVER_PORT" twice, and I do not understand why? I want my program to execute that statement once, since I only send one packet from the sender to the receiver.
Related
I'm following Black Hat Python (2ed.), in which I'm writing a network scanning tool. The tool is in theory supposed to send UDP packets out to a given subnet, and if a host is up on that subnet, the response packet is decoded, found to contain the message in the original datagram, and used to indicate the host is up. This seems to generally be working well to capture packets; I can go to a website, or ping another host, and the tool reliably provides the correct source and destination addresses for those cases.
Here is the meat of the code (I have not included the class creation, or the passing of the host argument for brevity, but the host is 192.168.10.85).
class IP:
"""layer 3 (IP) packet header decoder"""
def __init__(self, buff=None):
header = struct.unpack('<BBHHHBBH4s4s', buff)
self.ver = header[0] >> 4
self.ihl = header[0] & 0xF
self.tos = header[1]
self.len = header[2]
self.id = header[3]
self.offset = header[4]
self.ttl = header[5]
self.protocol_num = header[6]
self.sum = header[7]
self.src = header[8]
self.dst = header[9]
# make IP addrs human readable
self.src_address = ipaddress.ip_address(self.src)
self.dst_address = ipaddress.ip_address(self.dst)
# the protocol_num is actually a code for the protocol name
self.protocol_name = {1: 'ICMP', 6: 'TCP', 17: 'UDP'}
# try to provide the human version of the protocol, otherwise just give the code
try:
self.protocol = self.protocol_name[self.protocol_num]
except KeyError as error:
self.protocol = self.protocol_num
print(f'Protocol is unrecognized, try googling "IP protocol {self.protocol_num}"')
class ICMP:
"""layer 4 (ICMP) packet header decoder"""
def __init__(self, buff):
header = struct.unpack('<BBHHH', buff)
self.type = header[0]
self.code = header[1]
self.checksum = header[2]
self.ident = header[3]
self.seq_num = header[4]
def udp_sender():
# blasts udp packets into the network to solicit responses
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sender:
for ip in ipaddress.ip_network(SUBNET).hosts():
# time.sleep(1)
print(f'sending a test message to {ip}')
# send our test message out to port 65212 on the destination
sender.sendto(bytes(MESSAGE, 'utf8'), (str(ip), 65212))
class Scanner:
def __init__(self, host):
self.host = host
# create raw socket, bind to public interface
# if windows:
if os.name == 'nt':
socket_protocol = socket.IPPROTO_IP
# if linux/mac:
else:
socket_protocol = socket.IPPROTO_ICMP
self.socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
self.socket.bind((host, 0))
# socket options, include header
self.socket.setsockopt(socket_protocol, socket.IP_HDRINCL, 1)
# enable promiscuous mode for windows
if os.name == 'nt':
self.socket.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
def sniff(self):
# set of all hosts that are up (respond to our ICMP message)
hosts_up = {f'{str(self.host)} *'}
try:
while True:
# read a packet, and parse the IP header
raw_buffer = self.socket.recvfrom(65535)[0]
# create IP header from the first 20 bytes
ip_header = IP(raw_buffer[0:20])
# if the protocol is ICMP, do some additional things
# print(f'src={ip_header.src_address}, dst={ip_header.dst_address}, prot_name={ip_header.protocol}')
if ip_header.protocol == 'ICMP':
# calculate where the ICMP packet starts
offset = ip_header.ihl * 4
buf = raw_buffer[offset:offset + 8]
# create ICMP structure
icmp_header = ICMP(buf)
print(f'type: {icmp_header.type}, code: {icmp_header.code}')
print(f'src={ip_header.src_address}, dst={ip_header.dst_address}, prot_name={ip_header.protocol}')
if icmp_header.type == 3 and icmp_header.code == 3:
print(f'type: {icmp_header.type}, code: {icmp_header.code}')
print(f'src={ip_header.src_address}, dst={ip_header.dst_address}, prot_name={ip_header.protocol}')
if ipaddress.ip_address(ip_header.src_address) in ipaddress.IPv4Network(SUBNET):
# make sure the packet has our test message
if raw_buffer[len(raw_buffer) - len(MESSAGE):] == bytes(MESSAGE, 'utf8'):
tgt = str(ip_header.src_address)
if tgt != self.host and tgt not in hosts_up:
hosts_up.add(str(ip_header.src_address))
print(f'Host Up: {tgt}')
However, when receiving the ICMP responses as a result of my datagram, the tool reports that the source and destination addresses are the same (my host, 192.168.10.85). Furthermore, while I should be receiving responses with Type 3 and Code 3 (destination unreachable, and port unreachable), but I am receiving (in my program) Type 3 and Code 1.
Here is an example of the output when I issue a ping command while the scanner is running, which seems correct:
src=192.168.10.85, dst=192.168.10.200, prot_name=ICMP type: 0, code: 0 src=192.168.10.200, dst=192.168.10.85, prot_name=ICMP type: 8, code: 0
Here is an example of the output to what I am assuming is the UDP packet response, which seems incorrect):
src=192.168.10.85, dst=192.168.10.85, prot_name=ICMP type: 3, code: 1
If I open wireshark while I'm running my code, I can correctly see the ICMP Type 3/Code 3 responses, so I know they are going through, here is a screen grab of one host on the target subnet as an example:
Why is my scanner not seeing these responses that are in wireshark?
I've tried running wireshark alongside my program, to see if the packets are being correctly decoded, and that the message in the UDP packet is properly in place. All signs indicate that the packets are going out to the hosts I'm trying to detect, and the correct responses are coming back, but my scanner refuses to find them.
I'm trying to code a packet sniffer that detects the different websites that are visited by the devices connected to my wifi. The problem is that I'm only getting private IPs, here are the counted IPs I get from a 1 hour sniffing :
IPS Number_Of_Values
224.0.0.251 1068
192.168.1.255 387
255.255.255.255 32
224.0.0.22 28
224.0.0.1 28
192.168.1.111 6
192.168.1.115 5
224.0.1.187 2
Here is a part of the sniffer, ethernet_frame is getting mac addresses and proto, ipv4_packet unpacks the IP header and gets the IPs (source and target) and finally get_packets proceeds to the different unpackings:
def ethernet_frame(data):
dest_mac, src_mac, proto = struct.unpack('! 6s 6s H', data[:14])
return get_mac_addr(dest_mac), get_mac_addr(src_mac), socket.htons(proto), data[14:]
def ipv4_packet(data):
version_header_length = data[0]
version = version_header_length >> 4
header_length = (version_header_length & 15) * 4
ttl, proto, src, target = struct.unpack('! 8x B B 2x 4s 4s', data[:20])
return version, header_length, ttl, proto, ipv4(src), ipv4(target), data[header_length:]
def get__packets():
conn = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(3))
while True:
raw_data, addr = conn.recvfrom(65536)
dest_mac, src_mac, eth_proto, data = ethernet_frame(raw_data)
# 8 for ipv4
if eth_proto == 8:
(version, header_length, ttl, proto, src_ip, target_ip, data) = ipv4_packet(data)
Why am I only getting private IPs? How can I get websites IPs? If it's not possible, how to get the http header from a TCP packet in order to get the url?
(I'm coding it on my raspberry pi 4 - Linux)
i am using this code for
server:
import socket
ip = "127.0.0.1"
localIP = "127.0.0.1"
localPort = 20001
bufferSize = 1024
msgFromServer = "Hello UDP Client"
bytesToSend = str.encode(msgFromServer)
# Create a datagram socket
UDPServerSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
# Bind to address and ip
UDPServerSocket.bind((localIP, localPort))
print("UDP server up and listening")
# Listen for incoming datagrams
while (True):
bytesAddressPair = UDPServerSocket.recvfrom(bufferSize)
message = bytesAddressPair[0]
address = bytesAddressPair[1]
clientMsg = "Message from Client:{}".format(message)
clientIP = "Client IP Address:{}".format(address)
client = ip
if localIP == ip:
print(clientIP)
print(clientMsg)
else:
print("ip not correct")
# Sending a reply to client
UDPServerSocket.sendto(bytesToSend, address)
CLIENT CODE:
import socket
import time
count = 0
while count < 5:
msgFromClient = "Hello"
bytesToSend = str.encode(msgFromClient)
serverAddressPort = ("127.0.0.1", 20001)
bufferSize = 1024
# Create a UDP socket at client side
UDPClientSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
# Send to server using created UDP socket
UDPClientSocket.sendto(bytesToSend, serverAddressPort)
msgFromServer = UDPClientSocket.recvfrom(bufferSize)
msg = "Message from Server {}".format(msgFromServer[0])
print(msg)
time.sleep(3)
It seems like the client's IP is saved to the address variable. The list syntax in Python is the following:
listName = ["thing number 1", "thing number 2", 3, "and", 4]
To add entries to a list you use the .append() method
listName.append(address)
Best of luck
I'm writing a proxy with tcp connection that listens to multiple ports from a client and forward it to a server.
The problem is that the software hangs on the sock.accept.
Maybe I'm doing a messy logic here, but I need a client that connects to a PC, and that PC connects to another device. So I wrote that small proxy, and I get INVALID ARGUMENT ERROR in socket.accept()
import select
import socket
import threading
class Proxy(object):
def __init__(self, ip, ports):
self._ip = ip
self._sockets = []
self._proxy = {}
for port in ports:
self._proxy[port] = self.add_socket(port)
def add_socket(self, port=None):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if port:
# sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sock.bind(('0.0.0.0',port))
self._sockets.append(sock)
return sock
def get_client(self, src_sock, src_addr):
src_ip, src_port = src_addr
_, dst_port = src_sock.getsockname()
if src_ip == self._ip:
# got packet from device
dst_addr = ("10.8.8.210", dst_port)
else:
# got packet from client
dst_addr = self._ip, dst_port
print(">", src_port, dst_addr)
dst_sock = self._proxy[src_port]
return dst_sock, dst_addr
def run(self):
while True:
read_list, _, _ = select.select(self._sockets, [], [])
if read_list:
for sock in read_list:
try:
conn, addr = sock.accept()
data = conn.recvfrom(16*2024)
# print("got data from {} {}".format(sock, addr))
dst_sock, dst_addr = self.get_client(sock, addr)
# print("forwarding data from {} to {}".format(addr, dst_addr, len(data)))
dst_sock.sendto(data, dst_addr)
except:
raise # pass # print("no recipient for data")
for s in self._sockets:
s.close()
ports = [30001,30002,30003, 30070, 30071,30072,30075]
p = Proxy("192.168.2.10", ports)
p.run()
You have to call listen on the socket before accept:
adding sock.listen(1) to add_socket after bind
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('0.0.0.0', port))
sock.listen(1)
self._sockets.append(sock)
then allows you to call accept without the error. You may want to set the listen backlog to a greater number.
Below are my three scripts. I need to send the message from sricpt 1(Sensor.py) to script 2.(Client.py). And then the script 2 should send the message to script 3(Server.py).
It works fine till script 2 but the message isn't being received at the script 3 and the recvfrom() keeps waiting. There is no error but the script 3 doesn't show the message. Im using UDP. Please help.
SCRIPT 1(Sensor.py)
from socket import *
from time import ctime
CLIENT_IP = '192.168.1.109'
PORT = 23567
BUFSIZE = 1024
ADDR = (CLIENT_IP, PORT)
udpCliSock = socket(AF_INET, SOCK_DGRAM)
while True:
sendData = input("> ")
if sendData is None:
break
udpCliSock.sendto(sendData.encode(), ADDR)
udpCliSock.close()
SCRIPT 2(Client.py)
from socket import *
from time import ctime
HOST = '192.168.1.103'
CLIENT_IP='192.168.1.109'
PORT = 5005
SENSOR_PORT_NO=23567
BUFSIZE = 1024
ADDR = (HOST, PORT)
CLIENT_ADDR=(CLIENT_IP,SENSOR_PORT_NO)
udpCliSock = socket(AF_INET, SOCK_DGRAM)
client = socket(AF_INET, SOCK_DGRAM)
client.bind(CLIENT_ADDR)
while True:
print("...waiting for response...")
#recv_data, ADDR = udpCliSock.recvfrom(BUFSIZE)
recv_data, ADDR = client.recvfrom(1024)
if recv_data is not None:
recv_data = recv_data.decode()
print("[%s]: receiving data from server %s:%s :%s" % (ctime(),ADDR[0], ADDR[1], recv_data))
sendData = recv_data
udpCliSock.sendto(sendData.encode(), ADDR)
udpCliSock.close()
SCRIPT 3(Server.py)
from socket import *
from time import ctime
HOST = '192.168.1.103'
PORT = 5005
BUFSIZE = 1024
ADDR = (HOST, PORT)
udpSerSock = socket(AF_INET, SOCK_DGRAM)
udpSerSock.bind(ADDR)
while True:
print("...waiting for message...")
data, ADDR = udpSerSock.recvfrom(BUFSIZE)
print(data.decode)
if data is None:
break
print("[%s]: From Address %s:%s receive data: %s" % (ctime(),ADDR[0],ADDR[1], data.decode()))
udpSerSock.close()
The problem is that, in the Client.py, you are overwriting the address of the server when you receive the datagram from the sensor. Here:
recv_data, ADDR = client.recvfrom(1024)
ADDR starts out with the server's socket address, but this function overwrites that variable with the sensor's socket address. So when you try to send, you're sending it back to the sensor (who of course isn't ever receiving it).
A better method is to connect that socket to the server at the beginning. Then you can just use send instead of sendto since the address won't be changing. Should work after that:
...
udpCliSock = socket(AF_INET, SOCK_DGRAM)
udpCliSock.connect(ADDR) # <<<<<<===============
client = socket(AF_INET, SOCK_DGRAM)
client.bind(CLIENT_ADDR)
while True:
print("...waiting for response...")
recv_data, addr = client.recvfrom(1024)
if recv_data is not None:
recv_data = recv_data.decode()
print("[%s]: receiving data from server %s:%s :%s" % (ctime(),addr[0], addr[1], recv_data))
sendData = recv_data
udpCliSock.send(sendData.encode()) # <<<<<<===============
udpCliSock.close()