目标:我正在尝试将一个Python脚本放在一起,以捕获由于执行代码块而发生的网络流量。为简单起见,假设我要记录对的调用产生的网络流量socket.gethostbyname('example.com')
。注意:我不能只是简单地tcpdump
在gethostbyname()
返回时终止,因为要测量的实际代码块会触发其他外部代码,因此我无法确定该外部代码何时完成执行(因此,我必须离开tcpdump
运行足够长的时间)因为很有可能我记录了此外部代码生成的所有流量)。
方法:我正在使用subprocess
start tcpdump
,使用它的and选项告诉我几秒钟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()
不会捕获到呼叫所产生的部分/全部流量。我显然可以time.sleep(x)
在调用之前添加一个时间,gethostbyname()
以便给tcpdump
它一些时间来加速启动,但这不是一个可移植的解决方案(我不能随便选择一些方法,x < duration
因为功能强大的系统会比功能较弱的系统更早地捕获数据包)。
为了解决这个问题,我的想法是解析tcpdump
输出,以寻找以下内容stderr
:
tcpdump: listening on en0, link-type EN10MB (Ethernet), capture size 262144 bytes
因此,我需要附加到stderr
,但是问题是我不想承诺读取其所有输出,因为我需要继续执行我的代码以实际执行我要测量的代码块(gethostbyname()
在此示例中),而不是被困在一个循环中,从读取stderr
。
我可以通过添加一个信号量来解决此问题,该信号量会阻止主线程继续进行gethostbyname()
调用,并stderr
在它从中读取上述字符串时让后台线程从该信号量读取并减少该信号量(以使主线程继续前进)stderr
,但是我d如果可能,请保持代码为单线程。
据我了解,这是一个很大的NONO,可subprocess.PIPE
用于stderr
并且stdout
不承诺读取所有输出,因为当缓冲区填满时,子级最终将阻塞。但是,如果你仅对读取输出的第一部分感兴趣,你可以“分离”(销毁)管道中期执行吗?从本质上讲,我想得到这样的结果:
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
如果提前打电话,我会提早终止)?
也很可能是我错过了一些显而易见的事情,并且有一种更好的方法来实现我想要的目标-如果是这样,请启发我:)。
提前致谢 :)
收到消息后,你可以在detach()
或close()
stderr上使用listening on
:
import subprocess
import time
duration = 10
nif = 'eth0'
pcap = 'dns.pcap'
cmd = ['tcpdump', '-G', str(duration), '-W', '1', '-i', nif, '-w', pcap]
proc = subprocess.Popen(
cmd, shell=False, stderr=subprocess.PIPE, bufsize=1, text=True
)
for i, line in enumerate(proc.stderr):
print('read %d lines from stderr' % i)
if 'listening on' in line:
print('detach stderr!')
proc.stderr.detach()
break
while proc.poll() is None:
print("doing something else while tcpdump is runnning!")
time.sleep(2)
print(proc.returncode)
print(proc.stderr.read())
出去:
read 0 lines from stderr
detach stderr!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
0
Traceback (most recent call last):
File "x.py", line 24, in <module>
print(proc.stderr.read())
ValueError: underlying buffer has been detached
笔记:
我尚未检查stderr数据实际上发生了什么,但是分离stderr似乎对tcpdump没有任何影响。