理解/解码收到的数据包

时间:2014-05-28 07:40:53

标签: python sockets networking packet tor

我在尝试弄清楚我收到了多少数据包以及它们代表什么时遇到了一些问题。

我正在尝试将数据包发送到tor节点并获得响应,这个想法是,这是连接过程的开始,这将让我建立一个节点链来使用该服务。

我从本文档中获取了数据包结构:

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

我设法将数据包发送到节点:

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)

这会向节点发送一个数据包,我会收到以下内容:

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

我想知道的是我上面收到了多少数据包,以及它们包含了哪些信息,我认为应该收到4个数据包,但我不确定如果有人可以提供帮助,这将是一个巨大的帮助,因为我真的确实需要尝试理解这一点。

编辑:

这是我目前的代码,但仍有问题仍然了解收到的数据:(

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())

2 个答案:

答案 0 :(得分:4)

我没有测试过这个,但它应该让你知道需要什么:

# 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)

此外,由于套接字,尝试/除了块可能会很好。

答案 1 :(得分:1)

手动,我将解码您在示例中提供的数据,如下所示(冒号替换为空格以指示字段边界):

00:00 07 00:04 00:03:00:04

这意味着&#34;没有电路&#34;,cmd 7(VERSIONS),4个字节的有效载荷,表示服务器支持版本3和4.由于您的代码请求版本3,协议版本将为3(即CIRCID_LEN = 2用于以下单元格)。下一个单元格是

00:00 81 03:88 ...

这意味着仍然没有电路,命令0x81 = 129(CERTS),有效载荷长度0x388 = 904。以下904字节应为有效负载,由证书数据组成。在此之后,一个新的单元格开始。

为了自动解析服务器响应,最简单的方法是不要尝试直接使用struct.unpack,而是逐个处理单元包格式的不同字段。这允许处理可变字段长度。这是一个实现这个目标的骨架程序:

import ssl
import socket
import struct

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

versions_in = set([1,2,3,4])

pkt = struct.pack(">HBH", 0, 7, 2*len(versions_in))
for v in versions_in:
    pkt += struct.pack(">H", v)
ssl_sock.send(pkt)

recv_pkt = ssl_sock.recv(1500)

class Parser(object):

    def __init__(self):
        self.version = -1
        self.idx = 1

    def parse(self, data):
        print "packet", self.idx
        self.idx += 1

        CIRCID_LEN = 2
        if self.version >= 4:
            CIRCID_LEN = 4
        print "CIRCID_LEN =", CIRCID_LEN

        circ_id, data = data[:CIRCID_LEN], data[CIRCID_LEN:]
        print "circ_id =", ":".join("{:02x}".format(ord(c)) for c in circ_id)

        cmd = ord(data[0])
        data = data[1:]
        print "cmd = {:d}".format(cmd)

        PAYLOAD_LEN = 509
        if cmd == 7 or cmd >= 128:
            PAYLOAD_LEN = struct.unpack_from(">H", data)[0]
            data = data[2:]
        print "PAYLOAD_LEN =", PAYLOAD_LEN

        payload, data = data[:PAYLOAD_LEN], data[PAYLOAD_LEN:]
        print "payload =", ":".join("{:02x}".format(ord(c)) for c in payload)

        if cmd == 7:
            versions = set()
            while payload:
                versions.add(struct.unpack_from(">H", payload)[0])
                payload = payload[2:]
            self.version = max(versions_in&versions)
            print "our versions:", versions_in
            print "server supported versions:", versions
            print "thus we use protocol version", self.version

        print
        return data

p = Parser()
while recv_pkt:
    recv_pkt = p.parse(recv_pkt)

我引入了一个新类Parser来保存协议版本信息。版本由第一个单元格(命令代码7)确定,然后必须用于所有后续单元格。该代码仅解析cmd代码7的有效负载(因为这是解码以下单元格所必需的),其他有效负载仅打印到屏幕上。