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