Search code examples
pythonsocketsnetwork-programmingpackettor

Understanding / decoding a received packet(s)


I am having a bit of an issue trying to figure out how many packets I have received, and what they represent.

I am trying to send a packet to a tor node and get the reponse back, the idea is that this is the start to the connection process that will let me make a chain of nodes to use the service.

I have taken my packet structure from this document:

3. Cell Packet format

   The basic unit of communication for onion routers and onion
   proxies is a fixed-width "cell".

   On a version 1 connection, each cell contains the following:
   fields:

        CircID                                [CIRCID_LEN bytes]
        Command                               [1 byte]
        Payload (padded with 0 bytes)         [PAYLOAD_LEN bytes]

   On a version 2 or higher connection, all cells are as in version 1
   connections, except for variable-length cells, whose format is:

        CircID                                [CIRCID_LEN octets]
        Command                               [1 octet]
        Length                                [2 octets; big-endian integer]
        Payload                               [Length bytes]

   On a version 2 connection, variable-length cells are indicated by a
   command byte equal to 7 ("VERSIONS").  On a version 3 or
   higher connection, variable-length cells are indicated by a command
   byte equal to 7 ("VERSIONS"), or greater than or equal to 128.

   CIRCID_LEN is 2 for link protocol versions 1, 2, and 3.  CIRCID_LEN
   is 4 for link protocol version 4 or higher.  The VERSIONS cell itself
   always has CIRCID_LEN == 2 for backward compatibility.

   The CircID field determines which circuit, if any, the cell is
   associated with.

   The 'Command' field of a fixed-length cell holds one of the following
   values:
         0 -- PADDING     (Padding)                 (See Sec 7.2)
         1 -- CREATE      (Create a circuit)        (See Sec 5.1)
         2 -- CREATED     (Acknowledge create)      (See Sec 5.1)
         3 -- RELAY       (End-to-end data)         (See Sec 5.5 and 6)
         4 -- DESTROY     (Stop using a circuit)    (See Sec 5.4)
         5 -- CREATE_FAST (Create a circuit, no PK) (See Sec 5.1)
         6 -- CREATED_FAST (Circuit created, no PK) (See Sec 5.1)
         8 -- NETINFO     (Time and address info)   (See Sec 4.5)
         9 -- RELAY_EARLY (End-to-end data; limited)(See Sec 5.6)
         10 -- CREATE2    (Extended CREATE cell)    (See Sec 5.1)
         11 -- CREATED2   (Extended CREATED cell)    (See Sec 5.1)

    Variable-length command values are:
         7 -- VERSIONS    (Negotiate proto version) (See Sec 4)
         128 -- VPADDING  (Variable-length padding) (See Sec 7.2)
         129 -- CERTS     (Certificates)            (See Sec 4.2)
         130 -- AUTH_CHALLENGE (Challenge value)    (See Sec 4.3)
         131 -- AUTHENTICATE (Client authentication)(See Sec 4.5)
         132 -- AUTHORIZE (Client authorization)    (Not yet used)

   The interpretation of 'Payload' depends on the type of the cell.
      PADDING: Payload is unused.
      CREATE:  Payload contains the handshake challenge.
      CREATED: Payload contains the handshake response.
      RELAY:   Payload contains the relay header and relay body.
      DESTROY: Payload contains a reason for closing the circuit.
               (see 5.4)
   Upon receiving any other value for the command field, an OR must
   drop the cell.  Since more cell types may be added in the future, ORs
   should generally not warn when encountering unrecognized commands.

   The payload is padded with 0 bytes.

   PADDING cells are currently used to implement connection keepalive.
   If there is no other traffic, ORs and OPs send one another a PADDING
   cell every few minutes.

   CREATE, CREATED, and DESTROY cells are used to manage circuits;
   see section 5 below.

   RELAY cells are used to send commands and data along a circuit; see
   section 6 below.

   VERSIONS and NETINFO cells are used to set up connections in link
   protocols v2 and higher; in link protocol v3 and higher, CERTS,
   AUTH_CHALLENGE, and AUTHENTICATE may also be used.  See section 4
   below.

https://gitweb.torproject.org/torspec.git?a=blob_plain;hb=HEAD;f=tor-spec.txt

I have managed to send a packet to a node :

import ssl
import socket
import struct
import binascii

s = socket.socket()
ssl_sock = ssl.wrap_socket(s)
ssl_sock.connect(("", 443))

pkt = struct.pack(">HBHH", 0, 7, 2, 3)
ssl_sock.send(pkt)

recv_pkt = ssl_sock.recv(1500)
print ":".join("{:02x}".format(ord(c)) for c in recv_pkt)

This sends a packet to the node and I get back the following:

00:00:07:00:04:00:03:00:04:00:00:81:03:88:02:01:01:be:30:82:01:ba:30:82:01:23:a0:03:02:01:02:02:08:78:ef:3f:c3:c7:b8:06:ad:30:0d:06:09:2a:86:48:86:f7:0d:01:01:05:05:00:30:22:31:20:30:1e:06:03:55:04:03:13:17:77:77:77:2e:6b:7a:6e:79:74:72:7a:62:33:6e:6d:6e:32:63:68:2e:63:6f:6d:30:1e:17:0d:31:34:30:33:33:31:30:30:30:30:30:30:5a:17:0d:31:35:30:33:30:34:32:33:35:39:35:39:5a:30:1d:31:1b:30:19:06:03:55:04:03:13:12:77:77:77:2e:78:79:32:71:69:75:70:72:34:74:2e:6e:65:74:30:81:9f:30:0d:06:09:2a:86:48:86:f7:0d:01:01:01:05:00:03:81:8d:00:30:81:89:02:81:81:00:d1:66:cd:6c:14:14:7b:ff:79:1a:5a:30:0e:2d:fd:d9:85:79:a9:38:5d:9e:c1:09:35:fb:59:c7:1f:5b:ab:cc:1d:65:03:ed:62:d1:58:38:a4:e3:35:97:da:29:04:41:1d:e0:fa:50:f5:48:ba:87:fc:b2:9a:b6:0e:93:b9:a3:2e:fa:13:d1:75:ae:51:0f:95:2c:9a:fc:fc:b8:77:49:19:2a:4e:cb:9d:61:e4:cf:ef:38:0c:1f:a7:91:fd:98:de:57:5c:0e:29:1d:d9:36:df:ac:17:0b:cb:a0:a2:02:29:93:8b:5f:48:66:95:56:b9:4d:c0:c0:77:41:9c:91:02:03:01:00:01:30:0d:06:09:2a:86:48:86:f7:0d:01:01:05:05:00:03:81:81:00:76:c2:9e:31:93:46:47:03:e7:da:6b:cb:6e:ea:a9:4b:4c:98:e7:78:91:59:13:2d:8f:cf:f4:3f:49:b5:79:2a:b9:f2:f7:ec:75:24:1d:a2:78:ec:af:07:75:01:c6:cd:55:4b:1f:6f:02:18:22:b6:13:4a:0c:ee:d2:69:d2:42:08:f3:e0:c1:a0:d0:b1:ba:cf:38:20:dc:14:77:01:cd:93:49:b4:23:ce:f3:a8:fb:cf:ea:2c:33:67:16:d8:71:53:30:d3:6d:13:3e:b3:db:9c:b7:be:c1:e8:b2:ab:56:c9:cb:33:02:96:8c:48:04:61:f9:a5:65:a7:13:0b:40:02:01:c3:30:82:01:bf:30:82:01:28:a0:03:02:01:02:02:08:0c:25:64:87:83:b0:53:b4:30:0d:06:09:2a:86:48:86:f7:0d:01:01:05:05:00:30:22:31:20:30:1e:06:03:55:04:03:13:17:77:77:77:2e:6b:7a:6e:79:74:72:7a:62:33:6e:6d:6e:32:63:68:2e:63:6f:6d:30:1e:17:0d:31:33:30:37:31:30:30:30:30:30:30:30:5a:17:0d:31:34:30:37:31:30:30:30:30:30:30:30:5a:30:22:31:20:30:1e:06:03:55:04:03:13:17:77:77:77:2e:6b:7a:6e:79:74:72:7a:62:33:6e:6d:6e:32:63:68:2e:63:6f:6d:30:81:9f:30:0d:06:09:2a:86:48:86:f7:0d:01:01:01:05:00:03:81:8d:00:30:81:89:02:81:81:00:c4:20:57:68:17:2d:b5:e0:7a:c5:a0:78:0b:23:f0:fd:54:be:6f:f3:89:ba:c5:5e:ac:03:ab:6e:e5:2c:a3:60:58:66:06:5f:66:b1:dc:bc:93:de:7c:c8:d5:8e:68:2e:ce:24:3c:a4:ec:30:ce:5f:b7:d6:a8:93:07:f8:d7:b9:4a:a0:24:65:65:cf:69:b5:f4:5b:83:10:f1:f7:47:3d:1a:d5:95:9a:4a:e6:0e:f0:8a:69:ee:7d:4c:ca:17:22:21:9f:66:2c:16:4f:a2:8a:ab:96:66:f7:f1:6e:d8:89:77:07:6b:c7:1a:42:89:60:a8:b1:9a:20:49:44:aa:7f:02:03:01:00:01:30:0d:06:09:2a:86:48:86:f7:0d:01:01:05:05:00:03:81:81:00:a1:7f:4a:f8:51:3e:fe:34:1b:10:4a:42:15:d9:8c:39:f7:4b:7c:97:27:f6:96:67:45:f6:f8:95:91:f5:53:72:0b:ee:47:1a:94:b3:48:be:ab:0a:be:5b:52:45:05:ea:35:6f:50:85:ea:fb:88:3c:ee:33:04:76:55:7a:9f:69:eb:5d:c5:20:a6:3c:04:e0:62:1a:8d:21:fd:16:0a:85:93:ef:7a:3e:23:e5:36:3c:27:82:e1:d0:02:f7:d1:2d:0c:6f:f9:f7:a1:48:06:92:75:78:3f:c1:64:05:15:9f:db:f4:a6:22:f0:d6:95:56:83:c7:bf:03:cd:9f:dd:9c:00:00:82:00:24:be:24:79:44:ec:06:96:f9:dd:a5:77:16:53:f9:eb:90:20:e8:a4:37:7e:0a:8d:5f:6d:6a:45:a4:33:bd:72:ac:00:01:00:01:00:00:08:53:85:8e:a1:04:04:94:c5:86:ea:02:04:04:56:3b:15:26:06:10:20:01:08:58:00:02:00:02:aa:bb:00:00:56:3b:15:26:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00

What I am trying to figure out is how many packets I have received above, and what information they contain, I think there should be 4 packets received but am unsure it would be a massive help if anyone can help, as I really do need to try and understand this.

EDIT :

This is my current code but having issues still understanding the recieved data :(

import ssl
import socket
import struct
import binascii

s = socket.socket()
ssl_sock = ssl.wrap_socket(s)
ssl_sock.connect((" ", 443))


pkt = struct.pack(">HBHH", 0, 7, 2, 3)
ssl_sock.send(pkt)

peerAddr= [int(x) for x in ssl_sock.getpeername()[0].split(".")]

#recv_pkt = sock.recv(payload_len)
#print recv_pkt

#All recieved data printed
recv_pkt = ssl_sock.recv(1500)
#print recv_pkt
print ":".join("{:02x}".format(ord(c)) for c in recv_pkt)
print "Data recieved is above"
#while len(recv_pkt)<= 512
# payload = struct.unpack(">H")

# 512 - COMMAND_LEN - PAYLOAD_LEN = 512 - 1 - 509 = 2
CIRCID_LEN = 2

# default to v1
PAYLOAD_LEN = 509

#currently locking up at this section
circ_id, command = ssl_sock.recv(CIRCID_LEN), ssl_sock.recv(1)
print circ_id
print command

# according to spec, command is 7 if not using v1
# -> if not v1
if command != 7:
    # Negotiate proto version and set PAYLOAD_LEN accordingly
    PAYLOAD_LEN = 0

# finally, get the entire payload, and just that
payload = ssl_sock.recv(PAYLOAD_LEN)

print payload

#CellNetInfo
#cnetinf = recv_pkt(ssl_sock, 'CellNetInfo')
#ssl_sock.send(cnetinf.pack())

Solution

  • I haven't tested this, but it should give you an idea of what's needed:

    # Not sure if this is correct, I got it by:
    # 512 - COMMAND_LEN - PAYLOAD_LEN = 512 - 1 - 509 = 2
    CIRCID_LEN = 2
    
    # default to v1
    PAYLOAD_LEN = 509
    
    circ_id, command = ssl_sock.recv(CIRCID_LEN), ssl_sock.recv(1)
    
    # according to spec, command is 7 if not using v1
    # -> if not v1
    if command != 7:
        # Here you need to "Negotiate proto version"
        # Then, set PAYLOAD_LEN accordingly:
        PAYLOAD_LEN = 0
    
    # finally, get the entire payload, and just that
    payload = ssl_sock.recv(PAYLOAD_LEN)
    

    Also, it might be nice to have try/except blocks because of the sockets.