Struct: unpack Requires String argument of length 16

For my Computer Networking class, I am trying to implement Traceroute using raw ICMP sockets. I need to create a package and then unzip the response package using the Python structure class. Here is the code to create the package:

header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, myChecksum, pid, 1) data = struct.pack("d", time.time()) packet = header + data 

Later I receive an ICMP packet in the same format with confirmation. Here is the code to unpack the package:

 request_code, request_type, checksum, packet_id, \ sequence, timeSent, data = struct.unpack("bbHHhd", recvPacket) 

But I get the following error: struct.error: unpack requires a string argument of length 16 .

I do not understand, because when I check struct.calcsize() on a format string, it returns 16.

Here is my complete program if you want to run it on your computer.

 from socket import * import socket import os import sys import struct import time import select import binascii ICMP_ECHO_REQUEST = 8 MAX_HOPS = 30 TIMEOUT = 2.0 TRIES = 2 # The packet that we shall send to each router along the path is the ICMP echo # request packet, which is exactly what we had used in the ICMP ping exercise. # We shall use the same packet that we built in the Ping exercise def checksum(str): csum = 0 countTo = (len(str) / 2) * 2 count = 0 while count < countTo: thisVal = ord(str[count+1]) * 256 + ord(str[count]) csum = csum + thisVal csum = csum & 0xffffffffL count = count + 2 if countTo < len(str): csum = csum + ord(str[len(str) - 1]) csum = csum & 0xffffffffL csum = (csum >> 16) + (csum & 0xffff) csum = csum + (csum >> 16) answer = ~csum answer = answer & 0xffff answer = answer >> 8 | (answer << 8 & 0xff00) return answer def build_packet(): # In the sendOnePing() method of the ICMP Ping exercise ,firstly the header of our # packet to be sent was made, secondly the checksum was appended to the header and # then finally the complete packet was sent to the destination. # Make the header in a similar way to the ping exercise. # Header is type (8), code (8), checksum (16), id (16), sequence (16) myChecksum = 0 pid = os.getpid() & 0xFFFF # Make a dummy header with a 0 checksum. # struct -- Interpret strings as packed binary data header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, myChecksum, pid, 1) #header = struct.pack("!HHHHH", ICMP_ECHO_REQUEST, 0, myChecksum, pid, 1) data = struct.pack("d", time.time()) # Calculate the checksum on the data and the dummy header. # Append checksum to the header. myChecksum = checksum(header + data) if sys.platform == 'darwin': myChecksum = socket.htons(myChecksum) & 0xffff #Convert 16-bit integers from host to network byte order. else: myChecksum = htons(myChecksum) packet = header + data return packet def get_route(hostname): timeLeft = TIMEOUT for ttl in xrange(1,MAX_HOPS): for tries in xrange(TRIES): destAddr = socket.gethostbyname(hostname) #Fill in start # Make a raw socket named mySocket mySocket = socket.socket(AF_INET, SOCK_RAW, getprotobyname("icmp")) mySocket.bind(("", 12000)); #Fill in end mySocket.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, struct.pack('I', ttl)) mySocket.settimeout(TIMEOUT) try: d = build_packet() mySocket.sendto(d, (hostname, 0)) t = time.time() startedSelect = time.time() whatReady = select.select([mySocket], [], [], timeLeft) howLongInSelect = (time.time() - startedSelect) if whatReady[0] == []: # Timeout print "* * * Request timed out." recvPacket, addr = mySocket.recvfrom(1024) print addr timeReceived = time.time() timeLeft = timeLeft - howLongInSelect if timeLeft <= 0: print "* * * Request timed out." except socket.timeout: continue else: #Fill in start # Fetch the icmp type from the IP packet print struct.calcsize("bbHHhd") request_code, request_type, checksum, packet_id, \ sequence, timeSent, data = struct.unpack("bbHHhd", recvPacket) #Fill in end if request_type == 11: bytes = struct.calcsize("d") timeSent = struct.unpack("d", recvPacket[28:28 + bytes])[0] print " %d rtt=%.0f ms %s" % (ttl,(timeReceived -t)*1000, addr[0]) elif request_type == 3: bytes = struct.calcsize("d") timeSent = struct.unpack("d", recvPacket[28:28 + bytes])[0] print " %d rtt=%.0f ms %s" % (ttl,(timeReceived -t)*1000, addr[0]) elif request_type == 0: bytes = struct.calcsize("d") timeSent = struct.unpack("d", recvPacket[28:28 + bytes])[0] print " %d rtt=%.0f ms %s" % (ttl,(timeReceived -timeSent)*1000, addr[0]) return else: print "error" break finally: mySocket.close() get_route("www.google.com") 
+4
source share
2 answers

recvPacket bigger than your structure. If your structure is the first piece of data, unpack only the bytes of the structure:

 pktFormat = 'bbHHhd' pktSize = struct.calcsize(pktFormat) ... = struct.unpack(pktFormat, recvPacket[:pktSize]) 
+2
source

The struct.unpack function requires that the data you pass to it exactly matches the length of the format string.

If you have a large buffer and you want to decode part of it, use the struct.unpack_from function struct.unpack_from . An additional argument is required that indicates the offset to start decoding, and accepts buffers larger than the format string:

 (request_code, request_type, checksum, packet_id, sequence, timeSent, data) = struct.unpack_from("bbHHhd", recvPacket, 0) 

You may find this feature useful if you want to decode other parts of the packet data after parsing the header.

+5
source

Source: https://habr.com/ru/post/1446873/


All Articles