diff --git a/doc/scapy/installation.rst b/doc/scapy/installation.rst index ac5ab68bf76f8fed5cae380346e6f12d78da1e71..df184ca496c4765b8d932deba816df357f51aeab 100644 --- a/doc/scapy/installation.rst +++ b/doc/scapy/installation.rst @@ -472,7 +472,6 @@ Known bugs * You may not be able to capture WLAN traffic on Windows. Reasons are explained on the Wireshark wiki and in the WinPcap FAQ. Try switching off promiscuous mode with ``conf.sniff_promisc=False``. * Packets sometimes cannot be sent to localhost (or local IP addresses on your own host). - * The ``voip_play()`` functions do not work because they output the sound via ``/dev/dsp`` which is not available on Windows. Build the documentation offline diff --git a/scapy/arch/windows/__init__.py b/scapy/arch/windows/__init__.py index 5329cafe4a44327313d7cb89220916408438cd2a..fe5cd4350b63078686ac8ef641b81887bbbe80b0 100755 --- a/scapy/arch/windows/__init__.py +++ b/scapy/arch/windows/__init__.py @@ -188,9 +188,7 @@ def _where(filename, dirs=None, env="PATH"): for path in paths: for match in glob(os.path.join(path, filename)): if match: - p_match = os.path.normpath(match) - if os.path.isfile(p_match): - return p_match + return os.path.normpath(match) raise IOError("File not found: %s" % filename) def win_find_exe(filename, installsubdir=None, env="ProgramFiles"): @@ -251,6 +249,8 @@ class WinProgPath(ConfClass): conf.prog = WinProgPath() if conf.prog.powershell == "powershell": conf.prog.powershell = None +if conf.prog.sox == "sox": + conf.prog.sox = None class PcapNameNotFoundError(Scapy_Exception): pass diff --git a/scapy/modules/voip.py b/scapy/modules/voip.py index 52c7893daee017d738e8f6c076df6122ab2e8d3b..53036a7899b7782e9c1fceee47377da6b652aeed 100644 --- a/scapy/modules/voip.py +++ b/scapy/modules/voip.py @@ -9,138 +9,141 @@ VoIP (Voice over IP) related functions import os ################### -## Testing stuff ## +## Listen VoIP ## ################### -from fcntl import fcntl from scapy.sendrecv import sniff from scapy.layers.inet import IP,UDP from scapy.layers.rtp import RTP -from scapy.utils import get_temp_file +from scapy.consts import WINDOWS +from scapy.config import conf -def merge(x,y,sample_size=2): - if len(x) > len(y): - y += "\x00"*(len(x)-len(y)) - elif len(x) < len(y): - x += "\x00"*(len(y)-len(x)) +sox_base = "sox -t .ul %s - -t ossdsp /dev/dsp" + +if WINDOWS: + if conf.prog.sox is None: + raise OSError("Sox must be installed to play VoIP packets") + finally: + if p_test: + p_test.terminate() + sox_base = "\"" + conf.prog.sox + "\" -t .ul %s - -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 - for i in xrange(len(x)/ss): + min_ = 0 + if len(x) >= len(y): + min_ = y + elif len(x) < len(y): + min_ = x + r_ = len(min_) + for i in xrange(r_/ss): m += x[ss*i:ss*(i+1)]+y[ss*i:ss*(i+1)] - return m -# return "".join(map(str.__add__, x, y)) + return x[r_:], y[r_:], m -def voip_play(s1,list=None,**kargs): - FIFO=get_temp_file() - FIFO1=FIFO % 1 - FIFO2=FIFO % 2 - - os.mkfifo(FIFO1) - os.mkfifo(FIFO2) - try: - os.system("soxmix -t .ul %s -t .ul %s -t ossdsp /dev/dsp &" % (FIFO1,FIFO2)) - - c1=open(FIFO1,"w", 4096) - c2=open(FIFO2,"w", 4096) - fcntl.fcntl(c1.fileno(),fcntl.F_SETFL, os.O_NONBLOCK) - fcntl.fcntl(c2.fileno(),fcntl.F_SETFL, os.O_NONBLOCK) - - # dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp") - def play(pkt, last=None): - if last is None: - last = [] - if not pkt: - return - if not pkt.haslayer(UDP): - return - ip=pkt.getlayer(IP) - if s1 in [ip.src, ip.dst]: - if not last: - last.append(pkt) - return - load=last.pop() - # x1 = load.load[12:] - c1.write(load.load[12:]) - if load.getlayer(IP).src == ip.src: - # x2 = "" - c2.write("\x00"*len(load.load[12:])) - last.append(pkt) - else: - # x2 = pkt.load[:12] - c2.write(pkt.load[12:]) - # dsp.write(merge(x1,x2)) +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 ! - if list is None: - sniff(store=0, prn=play, **kargs) - else: - for p in list: - play(p) - finally: - os.unlink(FIFO1) - os.unlink(FIFO2) + :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 -def voip_play1(s1,list=None,**kargs): + .. seealso:: voip_play2 + to play both the outcoming and incoming packets + at the same time. + .. seealso:: voip_play3 + to read RTP VoIP packets + """ - dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp") + dsp, rd = os.popen2(sox_base % "") def play(pkt): if not pkt: return - if not pkt.haslayer(UDP): + if not pkt.haslayer(UDP) or not pkt.haslayer(IP): return ip=pkt.getlayer(IP) - if s1 in [ip.src, ip.dst]: - from scapy.config import conf + if s1 == ip.src: dsp.write(pkt.getlayer(conf.raw_layer).load[12:]) try: - if list is None: + if lst is None: sniff(store=0, prn=play, **kargs) else: - for p in list: + 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): - dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp") - def play(pkt, last=None): - if last is None: - last = [] + """ + 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. + """ + dsp,rd = os.popen2(sox_base % "-c 2") + global x1, x2 + x1 = "" + x2 = "" + def play(pkt): + global x1, x2 if not pkt: return - if not pkt.haslayer(UDP): + if not pkt.haslayer(UDP) or not pkt.haslayer(IP): return ip=pkt.getlayer(IP) if s1 in [ip.src, ip.dst]: - if not last: - last.append(pkt) - return - load=last.pop() - x1 = load.load[12:] -# c1.write(load.load[12:]) - if load.getlayer(IP).src == ip.src: - x2 = "" -# c2.write("\x00"*len(load.load[12:])) - last.append(pkt) + if ip.dst == s1: + x1 += pkt.getlayer(conf.raw_layer).load[12:] else: - x2 = pkt.load[:12] -# c2.write(pkt.load[12:]) - dsp.write(merge(x1,x2)) + x2 += pkt.getlayer(conf.raw_layer).load[12:] + x1, x2, r = _merge_sound_bytes(x1, x2) + dsp.write(r) sniff(store=0, prn=play, **kargs) def voip_play3(lst=None,**kargs): - dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp") + """Same than voip_play, but made to + read and play VoIP RTP packets, without + checking IP. + + .. seealso:: voip_play + for basic VoIP packets + """ + dsp,rd = os.popen2(sox_base % "") + def play(pkt, dsp=dsp): + if pkt and pkt.haslayer(UDP) and pkt.haslayer(RTP): + dsp.write(pkt.getlayer(RTP).load) try: - def play(pkt, dsp=dsp): - from scapy.config import conf - if pkt and pkt.haslayer(UDP) and pkt.haslayer(conf.raw_layer): - dsp.write(pkt.getlayer(RTP).load) if lst is None: sniff(store=0, prn=play, **kargs) else: