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

时间:2015-11-11 15:02:04

标签: python sockets client-server raw-sockets

我想在Python中实现一个RAW套接字,然后将数据从客户端发送到服务器。

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

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

但命令为

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

无效。 我不知道如何编写Client.py和Server.py。 有人能帮助我吗?

2 个答案:

答案 0 :(得分:4)

这是因为原始套接字根本不会影响以太网/ TCP / IP库。它是一个RAW套接字,您负责发送的任何数据。您还可以通过发送正确的SYN / ACK命令来连接到您的对等方。

传统的套接字是一种抽象的"用于发送有效负载(数据)的层。 这意味着您套接字连接到目的地,告诉套接字要发送哪些数据,假设您使用基于TCP的套接字,您的数据将被添加到对应于TCP协议和版本的标头,您的数据可能会根据您尝试推送的数据进行细分。

所有这些都是通过传统套接字自动发生的。

这是TCP标题的粗略看法(脱离上下文但会给你一个想法):

   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套接字时,您需要将所有这些信息块发送到以太网电缆

我可以在今晚晚些时候发布一些代码,也许是因为有人没有打败我,但这是一个很好的简短用法示例:How Do I Use Raw Socket in Python?

TL; DR:

您需要构建以太网标头和TCP标头,并根据RFC标准将数据添加到其中(这可能是一个很好的起点:https://tools.ietf.org/html/rfc793)。然后你需要"简单地"将它发送到你的" socket"。 RAW套接字没有任何神奇之处,您可以使用源+目标地址构建标头,然后将有效负载发送到电缆上,希望您正确构建数据包。

回答评论:

socket.accept() - 此功能在传统套接字中用于"存储"会话信息(来源:端口 - >目的地:端口)。此函数从缓冲的传入连接尝试队列中获取客户端,并且"激活"他们。这不适用于原始套接字,原因是普通套接字的抽象层再次出现,不存在。您的RAW套接字将侦听任何传入的数据(而非连接),这意味着您负责首先接收SYN数据包,您需要使用{{1}进行响应您将在其中收到最终SYN-ACK。此时,您可以使用正确的信息(源端口等)在您之间发送数据。

这是普通套接字中提供的抽象层的良好(ASCII)流程图:

ACK

这是一个服务器示例:

                              +---------+ ---------\      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  |
                              +---------+                   +---------+

发现一些我刚开始使用的旧代码,可能会派上用场:https://github.com/Torxed/Scripts/tree/master/python/Laboratory

答案 1 :(得分:1)

原始套接字是无连接的,这意味着侦听和接受将不起作用。但是,您可以使用以下Python库:rawsocketpy,它允许在第2层上使用原始套接字并实现类似option的服务器。

#!/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()