Python server:
import socket
import re
from base64 import b64encode
from hashlib import sha1
import base64
import struct
from queue import Queue
import threading
import select
def decodea(data):
buf = data
payload_start = 2
if len(buf) < 3:
return
b = (buf[0])
fin = b & 0x80
opcode = b & 0x0f
b2 = (buf[1])
mask = b2 & 0x80
length = b2 & 0x7f
if len(buf) < payload_start + 4:
return
elif length == 126:
length, = struct.unpack(">H", buf[2:4])
payload_start += 2
elif length == 127:
length, = struct.unpack(">I", buf[2:6])
payload_start += 4
if mask:
mask_bytes = [(b) for b in buf[payload_start:payload_start + 4]]
payload_start += 4
if len(buf) < payload_start + length:
return
payload = buf[payload_start:payload_start + length]
if mask:
unmasked = [mask_bytes[i % 4] ^ (b)
for b, i in zip(payload, range(len(payload)))]
payload = "".join([chr(c) for c in unmasked])
return [payload.encode('latin-1'), length]
def status(decoded):
status_ = ''
status_16 = 0
if(decoded[1] == 2):
for c in decoded[0]:
status_ += (str('%02x' % ord(chr(c))))
status_16 = int(status_, 16)
if(status_16 > 0):
cases = {
1000: "Normal Closure",
1001: "Going Away",
1002: "Protocol error",
1003: "Unsupported Data",
1004: "---Reserved----",
1005: "No Status Rcvd",
1006: "Abnormal Closure",
1007: "Invalid frame payload data",
1008: "Policy Violation",
1009: "Message Too Big",
1010: "Mandatory Ext.",
1011: "Internal Server Error",
1015: "TLS handshake"
}
if(status_16 in cases):
return status_16
return 0
def handshake(conn, globals__):
data = conn.recv(1024)
key = (re.search('Sec-WebSocket-Key:\s+(.*?)[\n\r]+', data.decode('utf-8'))
.groups()[0]
.strip())
sha1f = sha1()
sha1f.update(key.encode('utf-8') + globals__['GUID'].encode('utf-8'))
response_key = b64encode(sha1f.digest()).decode('utf-8')
response = '\r\n'.join(globals__['websocket_answer']).format(key=response_key)
conn.send(response.encode('utf-8'))
def socket_accept__(lock__, globals__):
lock__.acquire()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((globals__['socket_settings']['HOST'],globals__['socket_settings']['PORT']))
s.listen(globals__['socket_settings']['LISTEN'])
globals__['client_list'].append(s)
lock__.release()
while True:
lock__.acquire()
read_sockets,write_sockets,error_sockets = select.select(globals__['client_list'],[],[])
for sock in read_sockets:
if(sock == s):
conn, addr = s.accept()
handshake(conn, globals__)
globals__['client_list'].append(conn)
else:
for client in globals__['client_list']:
try:
client.settimeout(0.001)
data = client.recv(1024)
print(decodea(data)[0].decode('UTF-8'))
except(socket.timeout):
continue
lock__.release()
#thead_queue = Queue()
lock_ = threading.Lock()
globals_ = {
'GUID':'258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
'websocket_answer': (
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
'Sec-WebSocket-Accept: {key}\r\n\r\n'
),
'client_list': [],
'socket_settings': {
'HOST': '10.10.10.12',
'PORT': 8999,
'LISTEN': 200
},
'threads':[]
}
globals_['threads'].append(threading.Thread(target=socket_accept__, args=(lock_,globals_)))
globals_['threads'][0].setDaemon(True)
for threadi in globals_['threads']:
threadi.start()
for threadi in globals_['threads']:
threadi.join()
#thread2.join()
HTML5:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script type="text/javascript">
var s = new WebSocket('ws://10.10.10.12:8999');
s.onmessage = function(t){console.log(t); alert(t); };
s.onopen = function(){
s.send('hello from client');
s.send('my name is richard');
}
alert('load');
</script>
</head>
<body>
</body>
</html>
Output:
Hello from client
Expected output:
hello from client
my name is richard
I am sure this is because client.settimout(0.001) is not fast enough?
I am pretty lost for words, as i do not know why this is happening.
No message is being lost due to communication problems, it's just that it is not being decoded. It has nothing to do with client.settimeout(0.001).
When two or more messages from a client arrive close together (in time), both messages will be received in a single data = client.recv(1024) call.
That means that data can contain multiple messages. The decodea() function, however, only handles one message. Any additional message is completely ignored by the decoder, and that is why you seem to be losing messages.
You can write your decoder to decode and return multiple messages, perhaps changing it to a generator function so that you can yield each message in turn. The calling code would then loop over the messages.
Alternatively you could inspect the incoming message by reading just the first few bytes in order to determine the message's length. Then read the remaining bytes from the socket and decode the message. Any additional messages will be decoded during the next iteration.
One thing worth mentioning is that iterating over the client list with
for client in globals__['client_list']:
seems wrong as each client is just a socket object anyway, and you already know which sockets have data pending: those in the read_sockets list. You could write that code like this:
while True:
lock__.acquire()
read_sockets,write_sockets,error_sockets = select.select(globals__['client_list'],[],[])
for sock in read_sockets:
if(sock == s):
conn, addr = s.accept()
handshake(conn, globals__)
globals__['client_list'].append(conn)
else:
data = sock.recv(1024)
print(decodea(data)[0].decode('UTF-8'))
But you still need to figure out how to handle multiple messages arriving together - either in the decoder, or by ensuring that your code reads only one message at a time.
Related
I have written my own implementation of a websocket in python to teach myself their inner workings. I was going to be sending large repetitive JSON objects over the websocket so I am trying to implement permessage-deflate. The compression works in the client->server direction, but not in the server -> client direction
This is the header exchange:
Request
Host: awebsite.com:port
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
Upgrade: websocket
Origin: http://awebsite.com
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Sec-WebSocket-Key: JItmF32mfGXXKYyhcEoW/A==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Response
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Accept: zYQKJ6gvwlTU/j2xw1Kf0BErg9c=
When I do this, I get compressed data from the client as expected and it inflates as expected.
When I send an uncompressed message, I get a normal response on the client, Ie i send "hello" and I get "hello"
When I try to deflate my message using this simple python function:
def deflate(self,data,B64_encode=False):
data=zlib.compress(data)
if B64_encode:
return base64.b64encode(data[2:-4])
else:
return data[2:-4]
I get an error message about the characters not being utf-8, and when I base64 encode the compressed message, I just get the base64 encoded string. I also tried sending the data as Binary over the websocket, and get a blob at the other end. I've been scouring the internet for a while now and haven't heard of this happening. My Guess is that I am compressing the data at the wrong step. Below is the function I use to send the data. So far I've been feeding in the compressed message into the send() function because from what I've read permessage compression happens on the message level, and all the other data remains uncompressed.
def send(self, string, TYPE="TEXT"):
import struct
conn = self.conn
datatypes = {
"TEXT": 0x01,
"BINARY": 0x02,
"CLOSE": 0X08,
"PING": 0x09,
"PONG": 0x0A}
b1 = 0x80
b2 = 0
message = ""
if TYPE == "TEXT":
if type(string) == unicode:
b1 |= datatypes["TEXT"]
payload = string.encode("UTF8")
elif type(string) == str:
b1 |= datatypes["TEXT"]
payload = string
message += chr(b1)
else:
b1 |= datatypes[TYPE]
payload = string
message += chr(b1)
length = len(payload)
if length < 126:
b2 |= length
message += chr(b2)
elif length < (2 ** 16) - 1:
b2 |= 126
message += chr(b2)
l = struct.pack(">H", length)
message += l
else:
l = struct.pack(">Q", length)
b2 |= 127
message += chr(b2)
message += l
message += payload
try:
conn.send(str(message))
except socket.error:
traceback.print_exc()
conn.close()
if TYPE == "CLOSE":
self.Die = True
conn.shutdown(2)
conn.close()
print self.myid,"Closed"
After a lot of sleuthing, I found out my problem was a case of "RTFM". In the third paragraph of (a?) the manual on perMessage Compression it says
A WebSocket client may offer multiple PMCEs during the WebSocket
opening handshake. A peer WebSocket server received those offers may
choose and accept preferred one or decline all of them. PMCEs use
the RSV1 bit of the WebSocket frame header to indicate whether a
message is compressed or not, so that an endpoint can choose not to
compress messages with incompressible contents.
I didn't know what the rsv bits did when I first set this up, and had them set to 0 by default. My code now allows for compression to be set in the send() function in my program. It nicely shrinks my messages from 30200 bytes to 149 bytes.
My Modified code now looks like this:
def deflate2(self,data):
data=zlib.compress(data)
data=data[2:-4]
return data
def send(self, string, TYPE="TEXT",deflate=False):
import struct
if (deflate):
string=self.deflate(string)
conn = self.conn
datatypes = {
"TEXT": 0x01,
"BINARY": 0x02,
"CLOSE": 0X08,
"PING": 0x09,
"PONG": 0x0A}
b1 = 0x80 #0b100000000
if(deflate): b1=0xC0 #0b110000000 sets RSV1 to 1 for compression
b2 = 0
message = ""
if TYPE == "TEXT":
if type(string) == unicode:
b1 |= datatypes["TEXT"]
payload = string.encode("UTF8")
elif type(string) == str:
b1 |= datatypes["TEXT"]
payload = string
message += chr(b1)
else:
b1 |= datatypes[TYPE]
payload = string
message += chr(b1)
length = len(payload)
if length < 126:
b2 |= length
message += chr(b2)
elif length < (2 ** 16) - 1:
b2 |= 126
message += chr(b2)
l = struct.pack(">H", length)
message += l
else:
l = struct.pack(">Q", length)
b2 |= 127
message += chr(b2)
message += l
message += payload
try:
if self.debug:
for x in message: print("S>: ",x,hex(ord(x)),ord(x))
conn.send(str(message))
except socket.error:
traceback.print_exc()
conn.close()
if TYPE == "CLOSE":
self.Die = True
conn.shutdown(2)
conn.close()
print self.myid,"Closed"
I am using a multithreaded server to answer possible multiple clients.
The server code (in c):
//C SERVER CODE
void server()
{
int serverSocket, newSocket;
struct sockaddr_in serverAddr;
struct sockaddr_storage serverStorage;
socklen_t addr_size;
int opt = TRUE;
//Create socket
serverSocket = socket(PF_INET, SOCK_STREAM, 0);
if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt,
sizeof(opt))<0)
error("SETSOCKOPT ERROR");
//Configure setting of the server address struct
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(PORT);
//Set all bits of the padding field to 0
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
//Bind the address struct to the socket
bind(serverSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
//Listen on the socket, with max 40 connection requests queued
if(listen(serverSocket,40)==0)
printf("\nListening\n");
else
printf("\nError\n");
pthread_t tid[60];
int i = 0;
while(1)
{
//Accept call creates a new socket for the incoming connection
addr_size = sizeof serverStorage;
newSocket = accept(serverSocket, (struct sockaddr *) &serverStorage, &addr_size);
//for each client request creates a thread and assign the client request to it to process
//so the main thread can entertain next request
if( pthread_create(&tid[i], NULL, socketThread, &newSocket) != 0 )
printf("\nFailed to create thread\n");
else
{
printf("\nSOCKET CREATED\n");
}
if( i >= 50)
{
i = 0;
while(i < 50)
{
pthread_join(tid[i++],NULL);
}
i = 0;
}
}
}
void error(const char *msg){
perror(msg);
exit(EXIT_FAILURE);
}
void * socketThread(void *arg)
{
int newSocket = *((int *)arg);
//Send message to client socket
//pthread_mutex_unlock(&lock);
char buffer[14];
int counter = 0;
for (;;){
char message[14];
bzero (buffer, 14);
createEthernetPacket(message);
strcpy(buffer, message);
write(newSocket, buffer, sizeof(buffer));
counter = counter +1;
printf("\r Number of packets sent: %d", counter);
}
}
The ethernet packet I send is : b'A_dd_d(d)(d)_d(d)(d)_Z', where the A and Z are markers for the beginning and end of the packet, and the d are digits with the brackets representing that the digits might be there, but are not required (eg. 1 vs 12 vs 123).
These packets are created from CANBus data, which is handled by the mainloop in a multithreaded environment:
//9. Make threads
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, readCANdata, "READ_CAN");
pthread_create(&thread2, NULL, server, "SERVER");
while(1==1)
{
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
}
The client side looks like this, and is written in Python:
#PY CLIENT CODE
import socket
import sys
import time
def parse_data(data, last_data):
if (len(data)>0 and data[0] == 'A' and data[-1] == 'Z'):
valid = True
return [data[2:-2],valid] #REMOVE START AND END MARKERS WITH SPACES
else:
valid = False
return [last_data,valid]
def try_connect(address):
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(address)
connected = True
return [client, connected]
#SETTINGS
ip=socket.gethostbyname("192.168.0.13")
port=8080
address=(ip,port)
#INITIALIZE
can_id = '0'
an1 = '0'
anVar = '0'
data = b''
last_data = "START-UP"
t0 = time.time()
connected = False
#CONNECT
while not connected:
try:
[client, connected] = try_connect(address)
except:
print('\033[91m' + "\r The server does not seem to be active. Check your settings or reboot the server." + '\033[0m', end="")
#Get data
while True:
try:
data = client.recv(14)
except:
connected = False
try:
[client, connected] = try_connect(address)
data = client.recv(14)
except:
client.close()
connection_status = '\033[91m' + 'DISCONNECTED' + '\033[0m'
time_since_valid = time.time() - t0
print('\r' + connection_status + ' Last valid packet [' + can_id + ',' + an1 + ',' + anVar + '] was received ' + str(
round(time_since_valid)) + ' seconds ago', end='')
if data == b'':
connected = False
valid = False
client.close()
elif data == b'HELLO NEW SOCK':
connected = True
valid = True
else:
connected = True
data = data.decode('utf-8').rstrip('\x00')
[data,valid] = parse_data(data, last_data)
last_data = data
data_list = data.split(' ')
can_id = data_list[0]
an1 = data_list[1]
anVar = data_list[2]
if valid:
t0 = time.time()
t1 = time.time()
else:
t1 = time.time()
time_since_valid = t1 - t0
if connected:
connection_status = '\033[92m'+ 'CONNECTED' + '\033[0m'
else:
connection_status = '\033[91m' + 'DISCONNECTED' + '\033[0m'
print('\r'+connection_status+' Last valid packet ['+can_id+','+an1+','+anVar+'] was received '+str(round(time_since_valid))+' seconds ago', end='')
Now for the problem:
Whenever only 1 client is connected, this works perfectly. Until I start using multiple clients. Then, whenever I restart the server, all the clients disconnect (so far so good), but only a few clients reconnect. The reconnected clients are always at random and always at least one reconnects. When I print debug lines ("here 1", "here 2", etc.) it seems to stop at client.recv(14).
Additionally, the server crashes when one of the clients disconnect.
First problem solved:
In the socket thread:
int newSocket = ((uintptr_t *)arg);
In server:
if( pthread_create(&tid[i], NULL, socketThread, (void *)(uintptr_t) newSocket) != 0 )
I am trying to measure the responses back from DNS servers. Making a sniffer for a typical DNS response that is less than 512 bytes is no big deal. My issue is receiving large 3000+ byte responses - in some cases 5000+ bytes. I haven't been able to get a socket working that can receive that data reliably. Is there a way with Python sockets to receive from a specific source address?
Here is what I have so far:
import socket
import struct
def craft_dns(Qdns):
iden = struct.pack('!H', randint(0, 65535))
QR_thru_RD = chr(int('00000001', 2)) # '\x01'
RA_thru_RCode = chr(int('00100000', 2)) # '\x00'
Qcount = '\x00\x01' # question count is 1
ANcount = '\x00\x00'
NScount = '\x00\x00'
ARcount = '\x00\x01' # additional resource count is 1
pad = '\x00' #
Rtype_ANY = '\x00\xff' # Request ANY record
PROtype = '\x00\x01' # Protocol IN || '\x00\xff' # Protocol ANY
DNSsec_do = chr(int('10000000', 2)) # flips DNSsec bit to enable
edns0 = '\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00' # DNSsec disabled
domain = Qdns.split('.')
quest = ''
for x in domain:
quest += struct.pack('!B', len(x)) + x
packet = (iden+QR_thru_RD+RA_thru_RCode+Qcount+ANcount+NScount+ARcount+
quest+pad+Rtype_ANY+PROtype+edns0) # remove pad if asking <root>
return packet
def craft_ip(target, resolv):
ip_ver_len = int('01000101', 2) # IPvers: 4, 0100 | IP_hdr len: 5, 0101 = 69
ipvers = 4
ip_tos = 0
ip_len = 0 # socket will put in the right length
iden = randint(0, 65535)
ip_frag = 0 # off
ttl = 255
ip_proto = socket.IPPROTO_UDP # dns, brah
chksm = 0 # socket will do the checksum
s_addr = socket.inet_aton(target)
d_addr = socket.inet_aton(resolv)
ip_hdr = struct.pack('!BBHHHBBH4s4s', ip_ver_len, ip_tos, ip_len, iden,
ip_frag, ttl, ip_proto, chksm, s_addr, d_addr)
return ip_hdr
def craft_udp(sport, dest_port, packet):
#sport = randint(0, 65535) # not recommended to do a random port generation
udp_len = 8 + len(packet) # calculate length of UDP frame in bytes.
chksm = 0 # socket fills in
udp_hdr = struct.pack('!HHHH', sport, dest_port, udp_len, chksm)
return udp_hdr
def get_len(resolv, domain):
target = "10.0.0.3"
d_port = 53
s_port = 5353
ip_hdr = craft_ip(target, resolv)
dns_payload = craft_dns(domain) # '\x00' for root
udp_hdr = craft_udp(s_port, d_port, dns_payload)
packet = ip_hdr + udp_hdr + dns_payload
buf = bytearray("-" * 60000)
recvSock = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0800))
recvSock.settimeout(1)
sendSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
sendSock.settimeout(1)
sendSock.connect((resolv, d_port))
sendSock.send(packet)
msglen = 0
while True:
try:
pkt = recvSock.recvfrom(65535)
msglen += len(pkt[0])
print repr(pkt[0])
except socket.timeout as e:
break
sendSock.close()
recvSock.close()
return msglen
result = get_len('75.75.75.75', 'isc.org')
print result
For some reason doing
pkt = sendSock.recvfrom(65535)
Recieves nothing at all. Since I'm using SOCK_RAW the above code is less than ideal, but it works - sort of. If the socket is extremely noisy (like on a WLAN), I could end up receiving well beyond the DNS packets, because I have no way to know when to stop receiving packets when receiving a multipacket DNS answer. For a quiet network, like a lab VM, it works.
Is there a better way to use a receiving socket in this case?
Obviously from the code, I'm not that strong with Python sockets.
I have to send with SOCK_RAW because I am constructing the packet in a raw format. If I use SOCK_DGRAM the custom packet will be malformed when sending to a DNS resolver.
The only way I could see is to use the raw sockets receiver (recvSock.recv or recvfrom) and unpack each packet, look if the source and dest address match within what is supplied in get_len(), then look to see if the fragment bit is flipped. Then record the byte length of each packet with len(). I'd rather not do that. It just seems there is a better way.
Ok I was stupid and didn't look at the protocol for the receiving socket. Socket gets kind of flaky when you try to receive packets on a IPPROTO_RAW protocol, so we do need two sockets. By changing to IPPROTO_UDP and then binding it, the socket was able to follow the complete DNS response over multiple requests. I got rid of the try/catch and the while loop, as it was no longer necessary and I'm able to pull the response length with this block:
recvSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
recvSock.settimeout(.3)
recvSock.bind((target, s_port))
sendSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
#sendSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sendSock.settimeout(.3)
sendSock.bind((target, s_port))
sendSock.connect((resolv, d_port))
sendSock.send(packet)
pkt = recvSock.recvfrom(65535)
msglen = len(pkt[0])
Now the method will return the exact bytes received from a DNS query. I'll leave this up in case anyone else needs to do something similar :)
At sender side I have the following code using processing language (portion code):
udp = new UDP( this, 6002 ); // create a new datagram connection on port 6000
//udp.log( true ); // <-- printout the connection activity
udp.listen( true ); // and wait for incoming message
escribeUDPLog3(1,TRANSMIT,getTime()); //call function
int[] getTime(){
int year = year();
int month = month()
int day = day();
int hour = hour();
int minute = minute();
int second = second();
int[] time_constructed = {year, month,day,hour,minute,second};
return time_constructed;
}
void escribeUDPLog3(int pkg_type, int state, int[] time){
short year = (short)(time[0]); //>> 8;
byte year_msb = byte(year >> 8);
byte year_lsb = byte(year & 0x00FF);
byte month = byte(time[1]);
byte day = byte(time[2]);
byte hour = byte(time[3]);
byte minute = byte(time[4]);
byte second = byte(time[5]);
byte[] payload = {byte(pkg_type), byte(state), year_msb, year_lsb, month, day, hour, minute,second};
try {
if (UDP5_flag) {udp.send(payload, UDP5_IP, UDP5_PORT);}
}
catch (Exception e) {
e.printStackTrace();
}
}
At receiver side I'm using SocketServer python structure to set up a server listening for udp datagrams, as following.
from datetime import datetime
import csv
import SocketServer
def nodeStateCheckout(nodeid, state, nodeState):
if (state == ord(nodeState)):
return "OK"
else:
return "FAIL"
def timeConstructor(time):
year = str(ord(time[0]) << 8 | ord(time[1]))
month = str(ord(time[2]))
day = str(ord(time[3]))
hour = str(ord(time[4]))
minute = str(ord(time[5]))
second = str(ord(time[6]))
time_formatted = year + "-" + month + "-" + day \
+ " " + hour + ":" + minute + ":" + second
return time_formatted
class MyUDPHandler(SocketServer.BaseRequestHandler):
"""
This class works similar to the TCP handler class, except that
self.request consists of a pair of data and client socket, and since
there is no connection the client address must be given explicitly
when sending data back via sendto().
"""
def handle(self):
try:
data = self.request[0].strip()
socket = self.request[1]
#print "{} wrote:".format(self.client_address[0])
pkg_type = ord(data[0])
if pkg_type == 1: # log 3
state = ord(data[1])
csvfile = open("log3.csv", "a+")
csvwriter = csv.writer(csvfile, delimiter=',')
time_reconstructed = timeConstructor(data[2:9])
if state == 3:
csvwriter.writerow(["STOP",time_reconstructed])
elif state == 2:
csvwriter.writerow(["START",time_reconstructed])
else:
print "unknown state"
csvfile.close()
else:
print "packet not known"
except IndexError:
print "Bad parsed byte"
if __name__ == "__main__":
HOST, PORT = "localhost", 8892
server = SocketServer.UDPServer((HOST, PORT), MyUDPHandler)
server.serve_forever()
Edited:
I have problem specifically when using timeConstructor(data[2:9]), because I'm accessing out of index data, sometimes (with the help of print) I can't received second byte from data, and one time it get me out of index because I didn't received minute and second. Most of the time the code works well, but this type of error get me curious.
Old:
The problem is when reading the payload, sometimes its seems that some bytes doesn't arrive, even when I captured the whole payload using Wireshark (but Wireshark didn't tell me if this is the sent packet or received packet because I'm using loopback interfaces, maybe duplicated info?). If the datagram has 16 bytes payload long, sometimes I received 15 because when parsing from data I get out of index error.
I think that there are some buffer problems. Isn't it? How to configured it properly? I know that I can get packet loss because of connectionless protocol but I dont think that bytes get lost. It is supposed that "data" has all payload data from one udp datagram.
I believe your problem is that socket.sendto() does not always send all the bytes that you give it. It returns the number of bytes sent and you may have to call it again. You might be better off with opening the socket yourself and calling socket.sendall()
I have created a simple RAW socket based packet sniffer. But when I run it, it rarely captures up a packet. First I created this to capture packets in 1 second time intervals, but seeing no packets are captured I commented that line. I was connected to internet and a lot of http traffic are going here and there, but I could not capture a one. Is there a problem in this in the code where I created the socket? Please someone give me a solution. I am fairly new to python programming and could not understand how to solve this.
import socket, binascii, struct
import time
sock = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x800))
print "Waiting.."
pkt = sock.recv(2048)
print "received"
def processEth(data):
#some code to process source mac and dest. mac
return [smac, dmac]
def processIP(data):
sip = str(binascii.hexlify(data[1]))
dip = str(binascii.hexlify(data[2]))
return [sip, dip]
def processTCP(data):
sport = str(data[0])
dport = str(data[1])
return [sport, dport]
while len(pkt) > 0 :
if(len(pkt)) > 54:
pkt = sock.recv(2048)
ethHeader = pkt[0][0:14]
ipHeader = pkt[0][14:34]
tcpHeader = pkt[0][34:54]
ethH = struct.unpack("!6s6s2s",ethHeader)
ethdata = processEth(ethH)
ipH = struct.unpack("!12s4s4s",ipHeader)
ipdata = processIP(ipH)
tcpH = struct.unpack("!HH16", tcpHeader)
tcpdata = processTCP(tcpH)
print "S.mac "+ethdata[0]+" D.mac "+ethdata[1]+" from: "+ipdata[0]+":"+tcpdata[0]+" to: "+ipdata[1]+":"+tcpdata[1]
#time.sleep(1);
else:
continue
If you showed all the code, you are running into an endless loop.
Whenever a paket is coming in which has not a length greater then 54 bytes, you end up reading the same packet all the time.
Additionally, socket.recv() returns a string/byte sequence; your approach of accessing the data is wrong. pkt[0] returns a string with length 1; pkt[0][x:y] will not return something useful.
I am not familiar with using sockets, but with some changes I got output that might look similar to what you intended (there is something missing in processEth() I think...).
[...]
while len(pkt) > 0:
print "Waiting.."
pkt = sock.recv(2048)
print "received"
if(len(pkt)) > 54:
ethHeader = pkt[0:14]
ipHeader = pkt[14:34]
tcpHeader = pkt[34:38]
ethH = struct.unpack("!6s6s2s",ethHeader)
ethdata = processEth(ethH)
ipH = struct.unpack("!12s4s4s",ipHeader)
ipdata = processIP(ipH)
tcpH = struct.unpack("!HH16", tcpHeader)
tcpdata = processTCP(tcpH)
print "S.mac "+ethdata[0]+" D.mac "+ethdata[1]+" from: "+ipdata[0]+":"+tcpdata[0]+" to: "+ipdata[1]+":"+tcpdata[1]
#time.sleep(1);
else:
continue