• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1## This file is part of Scapy
2## See http://www.secdev.org/projects/scapy for more informations
3## Copyright (C) Philippe Biondi <phil@secdev.org>
4## This program is published under a GPLv2 license
5
6"""
7VoIP (Voice over IP) related functions
8"""
9
10from __future__ import absolute_import
11import os
12###################
13##  Listen VoIP  ##
14###################
15
16from scapy.sendrecv import sniff
17from scapy.layers.inet import IP,UDP
18from scapy.layers.rtp import RTP
19from scapy.consts import WINDOWS
20from scapy.config import conf
21from scapy.modules.six.moves import range
22
23
24sox_base = "sox -t .ul %s - -t ossdsp /dev/dsp"
25
26if WINDOWS:
27    if conf.prog.sox is None:
28        raise OSError("Sox must be installed to play VoIP packets")
29    sox_base = "\"" + conf.prog.sox + "\" -t .ul %s - -t waveaudio"
30
31def _merge_sound_bytes(x,y,sample_size=2):
32    # TODO: find a better way to merge sound bytes
33    # This will only add them one next to each other:
34    # \xff + \xff ==> \xff\xff
35    m = ""
36    ss=sample_size
37    min_ = 0
38    if len(x) >= len(y):
39        min_ = y
40    elif len(x) < len(y):
41        min_ = x
42    r_ = len(min_)
43    for i in range(r_/ss):
44        m += x[ss*i:ss*(i+1)]+y[ss*i:ss*(i+1)]
45    return x[r_:], y[r_:], m
46
47
48def voip_play(s1, lst=None, **kargs):
49    """Play VoIP packets with RAW data that
50    are either sniffed either from an IP, or
51    specified as a list.
52
53    It will play only the incoming packets !
54
55    :param s1: The IP of the src of all VoIP packets.
56    :param lst: (optional) A list of packets to load
57    :type s1: string
58    :type lst: list
59
60    :Example:
61
62    >>> voip_play("64.2.142.189")
63    while calling '411@ideasip.com'
64
65    >>> voip_play("64.2.142.189", lst)
66    with list a list of packets with VoIP data
67    in their RAW layer
68
69    .. seealso:: voip_play2
70    to play both the outcoming and incoming packets
71    at the same time.
72
73    .. seealso:: voip_play3
74    to read RTP VoIP packets
75    """
76
77    dsp, rd = os.popen2(sox_base % "")
78    def play(pkt):
79        if not pkt:
80            return
81        if not pkt.haslayer(UDP) or not pkt.haslayer(IP):
82            return
83        ip=pkt.getlayer(IP)
84        if s1 == ip.src:
85            dsp.write(pkt.getlayer(conf.raw_layer).load[12:])
86    try:
87        if lst is None:
88            sniff(store=0, prn=play, **kargs)
89        else:
90            for p in lst:
91                play(p)
92    finally:
93        dsp.close()
94        rd.close()
95
96def voip_play1(s1, lst=None, **kargs):
97    """Same than voip_play, backward compatibility
98    """
99    return voip_play(s1, lst, **kargs)
100
101def voip_play2(s1,**kargs):
102    """
103    Same than voip_play, but will play
104    both incoming and outcoming packets.
105    The sound will surely suffer distortion.
106
107    Only supports sniffing.
108
109    .. seealso:: voip_play
110    to play only incoming packets.
111    """
112    dsp,rd = os.popen2(sox_base % "-c 2")
113    global x1, x2
114    x1 = ""
115    x2 = ""
116    def play(pkt):
117        global x1, x2
118        if not pkt:
119            return
120        if not pkt.haslayer(UDP) or not pkt.haslayer(IP):
121            return
122        ip=pkt.getlayer(IP)
123        if s1 in [ip.src, ip.dst]:
124            if ip.dst == s1:
125                x1 += pkt.getlayer(conf.raw_layer).load[12:]
126            else:
127                x2 += pkt.getlayer(conf.raw_layer).load[12:]
128            x1, x2, r = _merge_sound_bytes(x1, x2)
129            dsp.write(r)
130
131    sniff(store=0, prn=play, **kargs)
132
133def voip_play3(lst=None,**kargs):
134    """Same than voip_play, but made to
135    read and play VoIP RTP packets, without
136    checking IP.
137
138    .. seealso:: voip_play
139    for basic VoIP packets
140    """
141    dsp,rd = os.popen2(sox_base % "")
142    def play(pkt, dsp=dsp):
143        if pkt and pkt.haslayer(UDP) and pkt.haslayer(RTP):
144            dsp.write(pkt.getlayer(RTP).load)
145    try:
146        if lst is None:
147            sniff(store=0, prn=play, **kargs)
148        else:
149            for p in lst:
150                play(p)
151    finally:
152        try:
153            dsp.close()
154            rd.close()
155        except:
156            pass
157
158