RAW 客户端-服务器套接字 python

RAW client-server socket python

我想在Python实现一个RAW socket,然后从Client向Server发送数据

与普通套接字不同,我尝试使用以下定义

s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)

但命令为

s.listen(1), s.connect()

不工作。 我不知道如何对 Client.py 和 Server.py 进行编程。 有人可以帮助我吗?

那是因为原始套接字根本不使用 Ethernet/TCP/IP 库。这是一个 RAW 套接字,您负责发送的任何数据。您还负责通过发送正确的 SYN/ACK 订单连接到您的同行。

传统套接字是一个“抽象”层,供您发送有效负载(数据)。 意思是你 connect 你的套接字到一个目的地,你告诉 socket 要发送什么数据,假设你使用的是基于 TCP 的套接字,你的数据将前面加上对应于 TCP 协议和版本的 header,您的数据可能会根据您尝试推送的数据量进行分段。

使用传统套接字,所有这些都会自动发生。

这是 TCP header 大致的样子(断章取意,但它会给你一个想法):

   0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |           |U|A|P|R|S|F|                               |
   | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
   |       |           |G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             data                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

请注意,数据 是您通常所做的,但是在使用 RAW 套接字时,您需要将所有这些信息块发送到您的以太网电缆上.

我可以 post 今晚晚些时候写一些代码,如果有人不抢我的话,但这里有一个很好的简短用法示例:How Do I Use Raw Socket in Python?


tl;博士:

您需要构建以太网 header 和 TCP header 并根据 RFC 标准向其中添加数据(这可能是一个很好的起点:https://www.rfc-editor.org/rfc/rfc793 ).然后您需要“简单地”将其发送到您的“套接字”。 RAW 套接字没有魔法,您使用源地址+目标地址构建 header,然后将有效负载发送到电缆上,希望您正确构建数据包。

回复评论:

socket.accept() - 此函数在传统套接字中用于“存储”session 信息(来源:端口 -> Destination:Port)。此函数从传入连接尝试的缓冲 queue 中获取客户端并“激活”它们。这不适用于原始套接字,原因是普通套接字的抽象层再次不存在。您的 RAW 套接字将侦听任何传入的 data(不是连接),这意味着您负责首先接收一个 SYN 数据包,您需要用 [=15] 响应该数据包=] 其中您将收到最终 ACK。此时,您可以使用正确的信息(源端口等)在你们之间发送数据。

这是一个很好的 (ASCII) flow-chart 抽象层,在 普通套接字中提供 :

                              +---------+ ---------\      active OPEN  
                              |  CLOSED |            \    -----------  
                              +---------+<---------\   \   create TCB  
                                |     ^              \   \  snd SYN    
                   passive OPEN |     |   CLOSE        \   \           
                   ------------ |     | ----------       \   \         
                    create TCB  |     | delete TCB         \   \       
                                V     |                      \   \     
                              +---------+            CLOSE    |    \   
                              |  LISTEN |          ---------- |     |  
                              +---------+          delete TCB |     |  
                   rcv SYN      |     |     SEND              |     |  
                  -----------   |     |    -------            |     V  
 +---------+      snd SYN,ACK  /       \   snd SYN          +---------+
 |         |<-----------------           ------------------>|         |
 |   SYN   |                    rcv SYN                     |   SYN   |
 |   RCVD  |<-----------------------------------------------|   SENT  |
 |         |                    snd ACK                     |         |
 |         |------------------           -------------------|         |
 +---------+   rcv ACK of SYN  \       /  rcv SYN,ACK       +---------+
   |           --------------   |     |   -----------                  
   |                  x         |     |     snd ACK                    
   |                            V     V                                
   |  CLOSE                   +---------+                              
   | -------                  |  ESTAB  |                              
   | snd FIN                  +---------+                              
   |                   CLOSE    |     |    rcv FIN                     
   V                  -------   |     |    -------                     
 +---------+          snd FIN  /       \   snd ACK          +---------+
 |  FIN    |<-----------------           ------------------>|  CLOSE  |
 | WAIT-1  |------------------                              |   WAIT  |
 +---------+          rcv FIN  \                            +---------+
   | rcv ACK of FIN   -------   |                            CLOSE  |  
   | --------------   snd ACK   |                           ------- |  
   V        x                   V                           snd FIN V  
 +---------+                  +---------+                   +---------+
 |FINWAIT-2|                  | CLOSING |                   | LAST-ACK|
 +---------+                  +---------+                   +---------+
   |                rcv ACK of FIN |                 rcv ACK of FIN |  
   |  rcv FIN       -------------- |    Timeout=2MSL -------------- |  
   |  -------              x       V    ------------        x       V  
    \ snd ACK                 +---------+delete TCB         +---------+
     ------------------------>|TIME WAIT|------------------>| CLOSED  |
                              +---------+                   +---------+

这是一个服务器示例:

#!/usr/bin/env python
from socket import socket, AF_PACKET, SOCK_RAW
s = socket(AF_PACKET, SOCK_RAW)
#s.bind(("eth1", 0))

# We're putting together an ethernet frame here, 
# NOTE: Not a full TCP frame, this is important to remember!
src_addr = "\x01\x02\x03\x04\x05\x06"
dst_addr = "\x01\x02\x03\x04\x05\x06"
payload = ("["*30)+"PAYLOAD"+("]"*30)
checksum = "\x1a\x2b\x3c\x4d"
ethertype = "\x08\x01"

s.send(dst_addr+src_addr+ethertype+payload+checksum)

找到一些我几乎没有开始使用的旧代码,可能会派上用场:https://github.com/Torxed/Scripts/tree/master/python/Laboratory

原始套接字是无连接的,这意味着 listen 和 accept 将不起作用。然而,您可以使用这个 Python 库:rawsocketpy 它允许在第 2 层使用原始套接字并实现类似服务器的选项。

#!/usr/bin/env python
from rawsocketpy import RawSocket

sock = RawSocket("wlp2s0", 0xEEFA)
sock.send("some data")
sock.send("personal data", dest="\xAA\xBB\xCC\xDD\xEE\xFF")

或服务器形式:

#!/usr/bin/env python
from rawsocketpy import RawRequestHandler, RawAsyncServerCallback
import time

def callback(handler, server):
    print("Testing")
    handler.setup()
    handler.handle()
    handler.finish()

class LongTaskTest(RawRequestHandler):
    def handle(self):
        time.sleep(1)
        print(self.packet)

    def finish(self):
        print("End")

    def setup(self):
        print("Begin") 

def main():
    rs = RawAsyncServerCallback("wlp2s0", 0xEEFA, LongTaskTest, callback)
    rs.spin()

if __name__ == '__main__':
    main()