esp32_bluetooth_classic_sni.../libs/scapy/modules/voip.py
Matheus Eduardo Garbelini 86890704fd initial commit
todo: add documentation & wireshark dissector
2021-08-31 19:51:03 +08:00

176 lines
4.7 KiB
Python
Executable file

# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
VoIP (Voice over IP) related functions
"""
from __future__ import absolute_import
import subprocess
###################
# Listen VoIP #
###################
from scapy.sendrecv import sniff
from scapy.layers.inet import IP, UDP
from scapy.layers.rtp import RTP
from scapy.consts import WINDOWS
from scapy.config import conf
from scapy.modules.six.moves import range
sox_base = (["sox", "-t", ".ul"], ["-", "-t", "ossdsp", "/dev/dsp"])
if WINDOWS:
if conf.prog.sox is None:
raise OSError("Sox must be installed to play VoIP packets")
sox_base = ([conf.prog.sox, "-t", ".ul"], ["-", "-t", "waveaudio"])
def _merge_sound_bytes(x, y, sample_size=2):
# TODO: find a better way to merge sound bytes
# This will only add them one next to each other:
# \xff + \xff ==> \xff\xff
m = ""
ss = sample_size
min_ = 0
if len(x) >= len(y):
min_ = y
elif len(x) < len(y):
min_ = x
r_ = len(min_)
for i in range(r_ / ss):
m += x[ss * i:ss * (i + 1)] + y[ss * i:ss * (i + 1)]
return x[r_:], y[r_:], m
def voip_play(s1, lst=None, **kargs):
"""Play VoIP packets with RAW data that
are either sniffed either from an IP, or
specified as a list.
It will play only the incoming packets !
:param s1: The IP of the src of all VoIP packets.
:param lst: (optional) A list of packets to load
:type s1: string
:type lst: list
:Example:
>>> voip_play("64.2.142.189")
while calling '411@ideasip.com'
>>> voip_play("64.2.142.189", lst)
with list a list of packets with VoIP data
in their RAW layer
.. seealso:: voip_play2
to play both the outcoming and incoming packets
at the same time.
.. seealso:: voip_play3
to read RTP VoIP packets
"""
proc = subprocess.Popen(sox_base[0] + sox_base[1], stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
dsp, rd = proc.stdin, proc.stdout
def play(pkt):
if not pkt:
return
if not pkt.haslayer(UDP) or not pkt.haslayer(IP):
return
ip = pkt.getlayer(IP)
if s1 == ip.src:
dsp.write(pkt.getlayer(conf.raw_layer).load[12:])
try:
if lst is None:
sniff(store=0, prn=play, **kargs)
else:
for p in lst:
play(p)
finally:
dsp.close()
rd.close()
def voip_play1(s1, lst=None, **kargs):
"""Same than voip_play, backward compatibility
"""
return voip_play(s1, lst, **kargs)
def voip_play2(s1, **kargs):
"""
Same than voip_play, but will play
both incoming and outcoming packets.
The sound will surely suffer distortion.
Only supports sniffing.
.. seealso:: voip_play
to play only incoming packets.
"""
proc = subprocess.Popen(sox_base[0] + ["-c", "2"] + sox_base[1],
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
dsp, rd = proc.stdin, proc.stdout
global x1, x2
x1 = ""
x2 = ""
def play(pkt):
global x1, x2
if not pkt:
return
if not pkt.haslayer(UDP) or not pkt.haslayer(IP):
return
ip = pkt.getlayer(IP)
if s1 in [ip.src, ip.dst]:
if ip.dst == s1:
x1 += pkt.getlayer(conf.raw_layer).load[12:]
else:
x2 += pkt.getlayer(conf.raw_layer).load[12:]
x1, x2, r = _merge_sound_bytes(x1, x2)
dsp.write(r)
try:
sniff(store=0, prn=play, **kargs)
finally:
try:
dsp.close()
rd.close()
except Exception:
pass
def voip_play3(lst=None, **kargs):
"""Same than voip_play, but made to
read and play VoIP RTP packets, without
checking IP.
.. seealso:: voip_play
for basic VoIP packets
"""
proc = subprocess.Popen(sox_base[0] + sox_base[1], stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
dsp, rd = proc.stdin, proc.stdout
def play(pkt, dsp=dsp):
if pkt and pkt.haslayer(UDP) and pkt.haslayer(RTP):
dsp.write(pkt.getlayer(RTP).load)
try:
if lst is None:
sniff(store=0, prn=play, **kargs)
else:
for p in lst:
play(p)
finally:
try:
dsp.close()
rd.close()
except Exception:
pass