【发布时间】:2020-12-04 08:34:51
【问题描述】:
目标:我正在尝试将 Python 脚本放在一起,以捕获由于执行代码块而发生的网络流量。为简单起见,假设我想记录调用socket.gethostbyname('example.com') 产生的网络流量。注意:当gethostbyname() 返回时,我不能简单地终止tcpdump,因为我要测量的实际代码块会触发其他外部代码,并且我无法确定此外部代码何时完成执行(所以我必须让tcpdump 运行“足够长”,以便我很可能记录了此外部代码生成的所有流量。
方法:我使用subprocess 启动tcpdump,告诉tcpdump 在duration 秒后使用其-G 和-W 选项终止自身,例如:
duration = 15
nif = 'en0'
pcap = 'dns.pcap'
cmd = ['tcpdump', '-G', str(duration), '-W', '1', '-i', nif, '-w', pcap]
tcpdump_proc = subprocess.Popen(cmd)
socket.gethostbyname('example.com')
time.sleep(duration + 5) # sleep longer than tcpdump is running
问题在于Popen() 返回之前 tcpdump 完全启动并运行,因此调用gethostbyname() 产生的部分/全部流量将不会被捕获。我显然可以在调用gethostbyname() 之前添加一个time.sleep(x) 给tcpdump 一点时间来启动,但这不是一个可移植的解决方案(我不能随便选择一些x < duration,因为一个强大的系统会启动比功能较弱的系统更早地捕获数据包)。
为了解决这个问题,我的想法是解析tcpdump 的输出以查找何时将以下内容写入其stderr,因为这似乎表明捕获已启动并完全运行:
tcpdump: listening on en0, link-type EN10MB (Ethernet), capture size 262144 bytes
因此我需要附加到stderr,但问题是我不想承诺读取它的所有输出,因为我需要我的代码继续执行我想要测量的代码块( gethostbyname() 在这个例子中)而不是陷入从 stderr 读取的循环中。
我可以通过添加一个信号量来解决这个问题,该信号量阻止主线程继续进行gethostbyname() 调用,并从stderr 读取一个后台线程,并在它时减少信号量(让主线程继续运行)从stderr 读取上面的字符串,但如果可能,我想保持代码单线程。
据我了解,将subprocess.PIPE 用于stderr 和stdout 而不承诺读取所有输出是一个很大的NONO,因为当缓冲区填满时,孩子最终会阻塞。但是,如果您只对读取输出的第一部分感兴趣,您可以“分离”(销毁?)管道中间执行吗?基本上我想得到这样的结果:
duration = 15
nif = 'en0'
pcap = 'dns.pcap'
cmd = ['tcpdump', '-G', str(duration), '-W', '1', '-i', nif, '-w', pcap]
tcpdump_proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, text=True)
for l in tcpdump_proc.stderr:
if 'tcpdump: listening on' in l:
break
socket.gethostbyname('example.com')
time.sleep(duration) # sleep at least as long as tcpdump is running
我还需要在if 块中添加什么来“重新分配”负责阅读stderr 的人?我可以将stderr 设置回None (tcpdump_proc.stderr = None) 吗?或者我应该打电话给tcpdump_proc.stderr.close()(如果我这样做,tcpdump 会提前终止吗)?
也很可能是我错过了一些明显的东西,并且有更好的方法来实现我想要的 - 如果是这样,请赐教:)。
提前致谢:)
【问题讨论】:
-
为什么不在 dns.pcap 捕获文件创建后继续?这发生在 “正在侦听” 消息写入
stderr之后,因此它发生在更接近tcpdump完全准备好时。作为奖励,您也不必担心写给stderr的“正在监听” 消息的确切文本是什么,所以如果它应该改变,那将是无关紧要的给你。请务必在启动tcpdump之前删除捕获文件,以防文件已存在。 -
@ChristopherMaynard 太好了,非常感谢您提供这些信息!我假设文件将首先创建(以便在某个地方存储数据包)之前
tcpdump开始侦听数据包(并打印“正在侦听”)。你怎么知道它是相反的?从源代码?如果是这样,您介意分享一下具体在哪里吗(我自己找不到,抱歉)? -
好的,我实际上看过一个较旧的 4.1.1。发布。最新的 4.9.3 略有不同,现在似乎在打开捕获文件之后打印了“正在侦听”消息,我猜这是您所期望的。不过,如果您等待创建文件,我认为这应该足以满足您的目的。好吧,如果您想探索它,这是一个选择。
标签: python subprocess pipe tcpdump