开发者社区> 科技小能手> 正文

scapy-其中迭代器的实现细节

简介:
+关注继续查看
前面知道了在scapy中,不管是ip包,还是以太包,还有tcp包,或者它们集合在一起的包都是一个Packet,这是在数据包组织意义上讲的,另外从数据包最终组合的意义上讲,scapy最引人入胜的地方还在于Packet本身也是一个生成器,即generator,这是python中一个很重要的概念。Packet是一个生成器并不是为了方便或者美观,而是scapy本身要求的。
     由于Packet表示的是一个单独层的数据包或者多层组合后的数据包,比如tcp数据段作为ip数据报的载荷(payload),因此如果设计的好一些,我们希望能按照协议栈本身理解数据包的方式去组合数据包,也就是说,完全按照分层协议的自解释机制,来一层一层从下往上将数据包构建好,比如在链路层的时候,看到上层是ip,那么就生成一个IP对象,然后在ip层,看到上层是tcp,那么生成一个TCP对象,依次类推,这样就会很方便,如果按照这个思路去设计,我觉得任何人都能想到递归和枚举,剩下的就是针对不同语言的编码问题了,scapy由python实现,将Packet本身作为一个generoter也是一个很自然的想法,这是因为对于一个自链路层开始向上构建的数据包,它只知道ip是它的payload,而不知道ip上面是什么,对于ip来讲也是一样,可是不管怎样,任何的payload都是一个Packet,因此如果我们能从链路层开始对其payload做for...in...操作的话,就能遍历到整个的协议层,将以太层的payload设置为payloadS(复数,因为这个payload还包含有更上层的payloadS...),然后第一次我们取出eth本身,第二次取出ip1,第三次取出tcp1...,针对于每一层的Packet,它本身又是一个生成器,生成器生成一个迭代器,该迭代器返回的next即是它本身,这样再通过递归就可以将所有层次的数据包组合在一起了。
     所有这一切的开始都在str函数,在str中:
def __str__(self):
    return self.__iter__().next().build()
self.__iter__().next()得到了Ether对象,调用它的build即可先构建以太头,再构建payload:
def do_build(self):
    p=""
    for f in self.fields_desc:
        p = f.addfield(self, p, self.__getattr__(f))
    pkt = p+str(self.payload)
    return pkt
在do_build中,str(self.payload)又会开启一次针对payload的__str__()(间接的对payload的__iter__的调用)的调用,从而开始了构建IP对象的过程,依次类推。这样就需要将__iter__实现的非常好:
00def __iter__(self):
01    def loop(todo, done, self=self):
02      if todo:
03        eltname = todo.pop()  
04        elt = self.__getattr__(eltname)
05        if not isinstance(elt,  Gen):
06          if  self.fieldtype[eltname].islist:
07            elt = SetGen([elt])
08          else:
09            elt = SetGen(elt)
10        for e in elt:
11          done[eltname] = e
12          for x in loop(todo[:], done):
13            yield x  
14      else:
15        if isinstance(self.payload,NoPayload):
16          payloads = [None]
17        else:
18          payloads = self.payload
19          for payl in payloads:
20            done2 = done.copy()
21            for k in done2:
22              if isinstance(done2[k], RandNum):
23                done2[k] = int(done2[k])
24              pkt = self.__class__(**done2)
25              pkt.underlayer = self.underlayer
26              pkt.overload_fields = self.overload_fields.copy()
27              if payl is None:
28                yield pkt
29              else:
30                yield pkt/payl  
31  return loop(map(lambda x:str(x), self.fields.keys()), {}) 
其实它只有一行,那就是第31行,它调用了一个子函数,首先map函数将所有我们手工设置的该层Packet的fields转化成一个list,然后调用loop,整个loop分为两大块,以17行的else做分割。在前半部分,将fields链表中的字段一个个pop出,比如对于IP对象而言就是src,dst,ttl之类,直到没有了的时候再次在12行递归调用loop的时候会进入后半部分else分支,此时处理它的payloadS(所有的上层数据),这个核心的实现尽在第19行,一个for...in的调用,这里就会对payloadS调用其__iter__方法,过程又回到了第31行,只是这次的self变了,也就是对象变了,变成了上一层数据的Packet对象了,然后一切如故...。
     在L3PacketSocket的send中从str的调用开始构建整个以太帧,整个调用过程如下:
00 call Ethernet obj-__str__ 1 
01  call Ethernet obj-__iter__ 2
02   call Ethernet obj-__iter__-loop 3
03    没有为Ether设置属性,因此直接进入else
04    call IP obj-__iter__ 4
05     call IP obj-__iter__-loop 5
06     取出dst
07      call IP obj-__iter__-loop 6
08       取出src
09       call IP obj-__iter__-loop 7
10        没有其它属性了,进入else
11        call TCP obj-__iter__ 8
12         call TCP obj-__iter__-loop 9
13          取出sport
14          call TCP obj-__iter__-loop 10
15           取出dport
16           call TCP obj-__iter__-loop 11
17            没有其它属性了,进入else
18            call None obj-__iter__ 12
19             11返回TCP obj
20          10返回TCP obj
21         9返回TCP obj
22        8返回IP obj
23       7返回IP obj
24      6返回IP obj
25    4返回Ether obj
26  2返回Ether obj
27  call Ethernet obj-__iter__-next 13
28   13返回Ether obj
29   call Ether obj-build 14
30    call Ether obj.payload-str 15
31     call IP obj-__str__
32      从第4行到第24行
33 ...

从32行可以看出,在从下到上的构建过程中,在构建上一层的时候也即是构建此层的payload的时候,需要对payload调用str函数,由于在第一次构建Ether obj的时候已经做过这件事了,也就是说,在构建链路层数据包的时候调用str(Ether obj),由于__iter__的实现有递归的性质,以上各层的Packet对象其实已经构造好了,实在没有必要再来一次,就是说,如果现在有4件事,分别分1,2,3,4,从1开始做,我们希望的是1-2-3-4这个顺序,或者1,2,3,4也可以,然而scapy的方式却是:1,2,3,4-2,3,4-3,4-4这实在是一个我认为可以改进的地方。不过既然scapy实现了如此复杂又美妙的__iter__以及数据包兼作generator的角色,我觉得已经可以抵消美中之不足矣!


 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1271134



版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【c++】:反向迭代器适配器:每天学一点点优秀的思想
【c++】:反向迭代器适配器:每天学一点点优秀的思想
10 0
Python 三大利器:迭代器、生成器、装饰器
Python 三大利器:迭代器、生成器、装饰器
14 0
一日一技:如何通过迭代器精简你的代码
一日一技:如何通过迭代器精简你的代码
8 0
理解 Python 迭代对象、迭代器、生成器
理解 Python 迭代对象、迭代器、生成器
51 0
一文彻底搞懂迭代器与生成器函数
参考mdn上解释,迭代器是一个对象,每次调用next方法返回一个{done: false, value: ''},每次调用next返回当前值,直至最后一次调用时返回 {value:undefined,done: true}时结束,无论后面调用next方法都只会返回{value: undefined,done:true}
90 0
【Python零基础入门篇 · 20】:可迭代对象和迭代器的转换、自定义迭代器类、异常类、生成器
【Python零基础入门篇 · 20】:可迭代对象和迭代器的转换、自定义迭代器类、异常类、生成器
58 0
Python 迭代器介绍及其作用(上)
Python 学习的人都知道,Python 中存在两种循环语句:while 和 for。for 循环可以用于 Python 中的任何序列,包括列表、元组、字符串。
38 0
Python 迭代器介绍及其作用(下)
Python 学习的人都知道,Python 中存在两种循环语句:while 和 for。for 循环可以用于 Python 中的任何序列,包括列表、元组、字符串。
67 0
python中的迭代对象、迭代器、生成器
在python中对于list和tuple,有很多的方式得到里面的元素,如果要输出里面的所有元素,可以采用for循环的形式,得到list或tuple中的每个元素,这就是迭代,凡是能够进行for循环的对象就是可迭代对象。 在java中的迭代方式有两种,一种直接for循环,一种采用迭代器 for循环的使用:
58 0
【重温基础】13.迭代器和生成器
【重温基础】13.迭代器和生成器
68 0
+关注
科技小能手
文章
问答
视频
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载