I'm using Python to communicate data over a TCP socket between a server and client application. I need to send a 4 bytes which represent a data sample. The initial sample is an 32-bit unsigned integer. How can I send those 4 bytes of raw data through the socket?
I want to send the data: 0x12345678 and 0xFEDCBA98
The raw data sent over the socket should be exactly that if I read it on wireshark/tcpdump/etc. I don't want each value in the 8 hex numbers to be represented as an ascii character, I want the raw data to remain intact.
Thank you
The main method to send binary data in Python is using the struct module.
For example, packing 3 4-byte unsigned integers is done like this
In [3]: struct.pack("III", 3, 4, 5)
Out[3]: '\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00'
Note to keep the endianess correct, using "<", ">", and so on.
you could use bytes(). Thats what I use to send strings over a network but you can use it for ints as well. Its usage is bytes([3])
Edit: bytes converts an int (3) to bytes. a string of bytes is represented as b'\x03'. so like your byte string: 0x12345678 would be b'\x12345678'
check the docs for more info:
https://docs.python.org/3.1/library/functions.html#bytes
Related
I implemented a simple socket in Python, for sending integer values via UDP:
socket_client.sendto(str(value).encode('utf-8'), (UDP_IP, UDP_PORT))
Now I would like to send two different values with only one packet, concatenating as a 64 bit length payload.
Problem is that converted and encoded values have no fixed length so receiver cannot figure how to split the received payload...
My goal would be to send a 64 length payload as to be able to split in two different 32 bits integers... Is it possible?
Binary encoding
Python integers have a built-in to_bytes method, which conveniently serialises integers to bytes objects of a given length.
You have to specify the byteorder and need to decide whether integers can be negative.
For example, this results in 32-bit big-endian unsigned representation (can represent 0..4294967295):
>>> x = 127
>>> b = x.to_bytes(4, 'big', signed=False)
>>> b
b'\x00\x00\x00\x7f'
You can simply concatenate two of those to get a 64-bit package.
NB: Send the bytes objects as such. Don't call str(...).encode() on them.
On the receiver side, you can use the int.from_bytes class method to parse a 4-byte string:
>>> int.from_bytes(b'\x00\x00\x00\x7f', 'big', signed=False)
127
Encoding with decimal digits
Your post isn't entirely clear.
Your snippet contains str(value).encode('utf8'), which could suggest that you intend to send numbers encoded with ASCII digits.
This is also possible – simply pad your numbers with zeros or spaces:
>>> '{:04d}'.format(127)
'0127'
>>> '{: 4d}'.format(127)
' 127'
On the receiver, parse the 4-byte strings with int(...).
Note that this representation only allows for the range 0..9999.
I'm trying to play with a security tool using scapy to spoof ASCII characters in a UDP checksum. I can do it, but only when I hardcode the bytes in Hex notation. But I can't convert the ASCII string word into binary notation. This works to send the bytes of "He" (first two chars of "Hello world"):
sr1(IP(dst=server)/UDP(dport=53, chksum=0x4865)/DNS(rd=1,qd=DNSQR(qname=query)),verbose=0)
But whenever I try to use a variable of test2 instead of 0x4865, the DNS packet is not transmitted over the network. This should create binary for this ASCII:
test2 = bin(int(binascii.hexlify('He'),16))
sr1(IP(dst=server)/UDP(dport=53, chksum=test2)/DNS(rd=1,qd=DNSQR(qname=query)),verbose=0)
When I print test2 variable is shows correct binary notation representation.
How do I convert a string such as He so that is shows in the checksum notation accepted by scapy, of 0x4865 ??
I was able to get this working by removing the bin(). This works:
test2 = int(binascii.hexlify('He'),16)
There are couple of devices which are sending socket data over TCP/IP to socket server. Some of the devices are sending data as Binary encoded Hexadecimal string, others are ASCII string.
Eg.;
If device sending data in ASCII string type, script is begin to process immediately without any conversion.
If device sending Binary encoded HEX string, script should has to convert Binary encoded Hex string into Hex string first with;
data = binascii.hexlify(data)
There are two scripts running for different data types for that simple single line. But, I think this could be done in one script if script be aware of the incoming data type. Is there a way to discover type of the incoming socket data in Python?
If you can you should make the sending devices signal what data they are sending eg by using different TCP ports or prepending each message with an "h" for hex or an "a" for ascii - possibly even use an established protocol like XML-RPC
Actually you can only be sure in some cases as all hex-encoded strings are valid ascii and some ascii-strings are valid hex like "CAFE".
You can make sure you can decode a string as hex with
import string
def is_possibly_hex(s):
return all(c in string.hexdigits for c in s)
or
import binascii
def is_possibly_hex(s):
try:
binascii.unhexlify(s)
except binascii.Error:
return False
return True
I was trying to send a byte containing hex value over serial port using pyserial. The hex value has to be in a variable (so that I can do some manipulations before sending). Sample code will explain my intent:
import serial
com=serial.Serial('COM1')
a_var=64
a_var=a_var+1
com.write(a_var) #This of course throws error
I want to receive 'A' or 0x41 on the other side. I could send hex using
com.write(b'\x41')
but not using a variable. Converting it to string or character or encoding the string did not help. I am using python 3.5.
Thanks
At first the name choice of your variable was not optimal. input is a built-in function and you might shadow it.
There are many way to put bytes into a variable:
to_send = b'A'
to_send = b'\x41'
to_send = bytes([65])
You see how to use an ASCII character, the escape sequence for hex numbers and the list of integers.
Now send via
com.write(to_send)
bytearray can be used to send bytes (as hex or ascii). They are mutable, hence numerical manipulations are possible. Any number of bytes can be sent using it.
import serial
com=serial.Serial('COM2')
elements=[65,67,69,71] #Create an array of your choice
a_var=bytearray(elements) #Create byte array of it
com.write(a_var[0:3]) #Write desired elements at serial port
a_var[0]=a_var[0]+1 #Do your mathematical manipulation
com.write(a_var[0:1]) #Write again as desired
com.close()
serial.write() method in pyserial seems to only send string data. I have arrays like [0xc0,0x04,0x00] and want to be able to send/receive them via the serial port? Are there any separate methods for raw I/O?
I think I might need to change the arrays to ['\xc0','\x04','\x00'], still, null character might pose a problem.
An alternative method, without using the array module:
def a2s(arr):
""" Array of integer byte values --> binary string
"""
return ''.join(chr(b) for b in arr)
You need to convert your data to a string
"\xc0\x04\x00"
Null characters are not a problem in Python -- strings are not null-terminated the zero byte behaves just like another byte "\x00".
One way to do this:
>>> import array
>>> array.array('B', [0xc0, 0x04, 0x00]).tostring()
'\xc0\x04\x00'
I faced a similar (but arguably worse) issue, having to send control bits through a UART from a python script to test an embedded device. My data definition was "field1: 8 bits , field2: 3 bits, field3 7 bits", etc. It turns out you can build a robust and clean interface for this using the BitArray library. Here's a snippet (minus the serial set-up)
from bitstring import BitArray
cmdbuf = BitArray(length = 50) # 50 byte BitArray
cmdbuf.overwrite('0xAA', 0) # Init the marker byte at the head
Here's where it gets flexible. The command below replaces the 4 bits at
bit position 23 with the 4 bits passed. Note that it took a binary
bit value, given in string form. I can set/clear any bits at any location
in the buffer this way, without having to worry about stepping on
values in adjacent bytes or bits.
cmdbuf.overwrite('0b0110', 23)
# To send on the (previously opened) serial port
ser.write( cmdbuf )