├── .gitignore ├── examples ├── app.xml ├── snom-notify-minibrowser-external.txt ├── mwi-off.txt ├── snom-pnp.txt ├── message.txt ├── mwi-on.txt ├── test-date.txt ├── register.txt ├── snom-check-sync-reboot.txt ├── snom-check-sync.txt ├── snom-led-off.txt ├── snom-check-sync-register.txt ├── snom-led-on.txt ├── snom-pickup.txt ├── snom-settings.txt ├── sip-options.txt ├── snom-notify-minibrowser.txt └── vq-report.txt ├── freeswitch_ping.tmpl ├── README.md └── sipping.py /.gitignore: -------------------------------------------------------------------------------- 1 | ve/ 2 | -------------------------------------------------------------------------------- /examples/app.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Loaded app 4 | Prompt Text 5 | 6 | This is a test application loaded from an external file 7 | 8 | 9 | -------------------------------------------------------------------------------- /freeswitch_ping.tmpl: -------------------------------------------------------------------------------- 1 | OPTIONS sip:%(dest_ip)s SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK001b84f6;rport 3 | From: "sip ping" 4 | To: 5 | Contact: 6 | Call-ID: 7066d2f12e6f22ec1dc1231f4cade6be\@%(source_ip)s 7 | User-Agent: SIPPing 8 | Date: %(date)s 9 | Allow: ACK, CANCEL 10 | Content-Length: 0 11 | -------------------------------------------------------------------------------- /examples/snom-notify-minibrowser-external.txt: -------------------------------------------------------------------------------- 1 | NOTIFY sip:%(dest_ip)s:%(dest_port)s SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s 3 | From: ;tag=2502 4 | To: ;tag=2502 5 | Call-ID: blablub@snom320xxx 6 | Max-Forwards: 70 7 | Event: xml 8 | Subscription-State: active;expires=30000 9 | Content-Type: application/snomxml 10 | 11 | %(appfile)s 12 | -------------------------------------------------------------------------------- /examples/mwi-off.txt: -------------------------------------------------------------------------------- 1 | NOTIFY sip:%(user)s@%(dest_ip)s:%(dest_port)s SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s 3 | From: 4 | To: 5 | Call-ID: %(callid)s 6 | CSeq: %(seq)s NOTIFY 7 | Max-Forwards: 70 8 | Event: message-summary 9 | Content-Type: application/simple-message-summary 10 | 11 | Messages-Waiting: no 12 | Voice-Message: 1/0 (0/0) 13 | -------------------------------------------------------------------------------- /examples/snom-pnp.txt: -------------------------------------------------------------------------------- 1 | SUBSCRIBE sip:MAC%%3a%(mac)s@fake SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;rport 3 | From: ;tag=658512961 4 | To: 5 | Call-ID: 1930770594@ciccio 6 | CSeq: 1 SUBSCRIBE 7 | Event: ua-profile;profile-type="device";vendor="snom";model="%(model)s";version="7.1.19" 8 | Expires: 0 9 | Accept: application/url 10 | Contact: 11 | Content-Length: 0 12 | -------------------------------------------------------------------------------- /examples/message.txt: -------------------------------------------------------------------------------- 1 | MESSAGE sip:%(to)s@%(dest_ip)s:%(dest_port)s;transport=udp SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK001b84f6;rport 3 | From: "%(from)s" ;tag=as2e95fad1 4 | To: 5 | Call-ID: 12341 6 | CSeq: %(cseq)s MESSAGE 7 | Max-Forwards: 70 8 | Contact: 9 | Content-Type: text/plain 10 | 11 | %(body)s 12 | -------------------------------------------------------------------------------- /examples/mwi-on.txt: -------------------------------------------------------------------------------- 1 | NOTIFY sip:%(user)s@%(dest_ip)s:%(dest_port)s SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s 3 | From: 4 | To: 5 | Call-ID: %(callid)s 6 | CSeq: %(seq)s NOTIFY 7 | Max-Forwards: 70 8 | Event: message-summary 9 | Content-Type: application/simple-message-summary 10 | 11 | Message-Account: sip:151@172.16.18.40 12 | Messages-Waiting: yes 13 | Voice-Message: 1/0 (0/0) 14 | -------------------------------------------------------------------------------- /examples/test-date.txt: -------------------------------------------------------------------------------- 1 | OPTIONS sip:%(dest_ip)s:%(dest_port)s SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s 3 | Max-Forwards: 70 4 | From: "fake" 5 | To: 6 | Contact: 7 | Call-ID: fake-id@%(source_ip)s 8 | User-Agent: SIPPing 9 | Date: %(date)s 10 | Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH 11 | Supported: replaces, timer 12 | -------------------------------------------------------------------------------- /examples/register.txt: -------------------------------------------------------------------------------- 1 | REGISTER sip:%(dest_ip)s SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK-p985iy;rport 3 | From: "fake" ;tag=as2e95fad1 4 | To: 5 | Contact: 6 | Call-ID: %(callid)s@fake 7 | CSeq: %(seq)d REGISTER 8 | Max-Forwards: 70 9 | Supported: path, outbound, gruu 10 | User-Agent: SIPPing fake UA 11 | Expires: 3600 12 | Content-Length: 0 13 | -------------------------------------------------------------------------------- /examples/snom-check-sync-reboot.txt: -------------------------------------------------------------------------------- 1 | NOTIFY sip:%(user)s@%(dest_ip)s SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK001b84f6;rport 3 | Max-Forwards: 70 4 | From: "fake" ;tag=as2e95fad1 5 | To: 6 | Contact: 7 | CSeq: 10 NOTIFY 8 | Contact: 9 | Call-ID: 1234@%(source_ip)s 10 | Event: check-sync;reboot=true 11 | 12 | -------------------------------------------------------------------------------- /examples/snom-check-sync.txt: -------------------------------------------------------------------------------- 1 | NOTIFY sip:%(user)s@%(dest_ip)s SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK001b84f6;rport 3 | Max-Forwards: 70 4 | User-Agent: SIPPing 5 | From: "fake" ;tag=as2e95fad1 6 | To: 7 | Contact: 8 | CSeq: 10 NOTIFY 9 | Contact: 10 | Call-ID: 1234@%(source_ip)s 11 | Event: check-sync;reboot=false 12 | 13 | -------------------------------------------------------------------------------- /examples/snom-led-off.txt: -------------------------------------------------------------------------------- 1 | MESSAGE sip:%(user)s@%(dest_ip)s:%(dest_port)s;transport=udp SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK001b84f6;rport 3 | From: "fake" ;tag=as2e95fad1 4 | To: 5 | Call-ID: %(callid)s@test 6 | CSeq: 59620 MESSAGE 7 | Max-Forwards: 70 8 | Contact: 9 | Subject: buttons 10 | Content-Type: application/x-buttons 11 | 12 | k=%(key)s 13 | c=off 14 | -------------------------------------------------------------------------------- /examples/snom-check-sync-register.txt: -------------------------------------------------------------------------------- 1 | NOTIFY sip:%(user)s@%(dest_ip)s SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK001b84f6;rport 3 | Max-Forwards: 70 4 | User-Agent: SIPPing 5 | From: "fake" ;tag=as2e95fad1 6 | To: 7 | Contact: 8 | CSeq: 10 NOTIFY 9 | Contact: 10 | Call-ID: 1234@%(source_ip)s 11 | Event: check-sync;reboot=reregister-only 12 | -------------------------------------------------------------------------------- /examples/snom-led-on.txt: -------------------------------------------------------------------------------- 1 | MESSAGE sip:%(user)s@%(dest_ip)s:%(dest_port)s;transport=udp SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK001b84f6;rport 3 | From: "fake" ;tag=as2e95fad1 4 | To: 5 | Call-ID: %(callid)s@test 6 | CSeq: 59620 MESSAGE 7 | Max-Forwards: 70 8 | Contact: 9 | Subject: buttons 10 | Content-Type: application/x-buttons 11 | 12 | k=%(key)s 13 | o=%(color)s 14 | c=on 15 | -------------------------------------------------------------------------------- /examples/snom-pickup.txt: -------------------------------------------------------------------------------- 1 | MESSAGE sip:%(user)s@%(dest_ip)s:%(dest_port)s;transport=udp SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK001b84f6;rport 3 | From: "fake" ;tag=as2e95fad1 4 | To: 5 | Call-ID: %(callid)s@test 6 | CSeq: 59620 MESSAGE 7 | Max-Forwards: 70 8 | Contact: 9 | Subject: buttons 10 | Content-Type: application/x-buttons 11 | 12 | k=%(key)s 13 | o=%(color)s 14 | c=pickup 15 | l=42 16 | n=*14 17 | i=123455 18 | a=invite 19 | -------------------------------------------------------------------------------- /examples/snom-settings.txt: -------------------------------------------------------------------------------- 1 | MESSAGE sip:%(user)s@%(dest_ip)s:%(dest_port)s;transport=udp SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK001b84f6;rport 3 | From: "%(from)s" ;tag=as2e95fad1 4 | To: 5 | Call-ID: 12341 6 | CSeq: 100 MESSAGE 7 | Max-Forwards: 70 8 | Contact: 9 | Content-Type: application/xml 10 | Event: snom-settings 11 | 12 | 13 | 14 | 10 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/sip-options.txt: -------------------------------------------------------------------------------- 1 | OPTIONS sip:%(user)s@%(dest_ip)s:%(dest_port)s;line=kutixubf SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK001b84f6;rport 3 | Max-Forwards: 70 4 | From: "fake" ;tag=as2e95fad1 5 | To: 6 | Contact: 7 | Call-ID: 7066d2f12e6f22ec1dc1231f4cade6be@172.16.18.40:5060 8 | User-Agent: SIPPing 9 | Date: Wed, 24 Apr 2013 20:35:23 GMT 10 | Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH 11 | Supported: replaces, timer 12 | -------------------------------------------------------------------------------- /examples/snom-notify-minibrowser.txt: -------------------------------------------------------------------------------- 1 | NOTIFY sip:%(dest_ip)s:%(dest_port)s SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s 3 | From: ;tag=2502 4 | To: ;tag=2502 5 | Call-ID: blablub@snom320xxx 6 | Max-Forwards: 70 7 | Event: xml 8 | Subscription-State: active;expires=30000 9 | Content-Type: application/snomxml 10 | 11 | 12 | 13 | Menutitle: %(seq)d 14 | Prompt 15 | 16 | John 17 | 123456789 18 | 19 | 20 | Doe 21 | 987654321 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/vq-report.txt: -------------------------------------------------------------------------------- 1 | PUBLISH sip:%(user)s@%(dest_ip)s:%(dest_port)s SIP/2.0 2 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK-2t9km7x85z4r;rport 3 | From: "fake" ;tag=as2e95fad1 4 | To: 5 | Call-ID: 3871ab8089fd-h90zx4r5mlfj 6 | CSeq: %(seq)d PUBLISH 7 | Max-Forwards: 70 8 | Contact: 9 | User-Agent: SIPPing 10 | Event: vq-rtcpxr 11 | Accept: application/sdp, message/sipfrag 12 | Content-Type: application/vq-rtcpxr 13 | 14 | VQSessionReport: CallTerm 15 | CallID:b4d42387@pbx 16 | LocalID:"John Doe" 17 | RemoteID:"Anonymous" 18 | OrigID:"Anonymous" 19 | LocalAddr:IP=172.16.18.187 PORT=60696 SSRC=0x2769A169 20 | RemoteAddr:IP=81.29.211.196 PORT=50086 SSRC=0x87F33D64 21 | DialogID:b4d42387@pbx;to-tag=1094356377;from-tag=ugyisema34 22 | x-UserAgent:snom710/8.7.3.201309022318 23 | LocalMetrics: 24 | Timestamps:START=2000-01-04T08:12:27Z STOP=2000-01-04T08:12:48Z 25 | SessionDesc:PT=0 PD=PCMU SR=8000 PPS=50 SSUP=off 26 | x-SIPterm:SDC=OK SDT=21 SDR=AN 27 | JitterBuffer:JBA=3 JBR=2 JBN=39 JBM=40 JBX=240 28 | PacketLoss:NLR=0.0 JDR=0.0 29 | BurstGapLoss:BLD=0.0 BD=0 GLD=0.0 GD=20440 GMIN=16 30 | Delay:RTD=34 ESD=0 IAJ=1 31 | QualityEst:MOSLQ=4.1 MOSCQ=3.9 32 | RemoteMetrics: 33 | JitterBuffer:JBA=0 JBR=0 JBN=0 JBM=0 JBX=0 34 | PacketLoss:NLR=0.0 JDR=0.0 35 | BurstGapLoss:BLD=0.0 BD=0 GLD=0.0 GD=18458 GMIN=16 36 | Delay:RTD=34 ESD=0 IAJ=0 37 | QualityEst:MOSLQ=4.1 MOSCQ=4.1 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SIPPing Python SIP Packet forging tool 2 | 3 | SIPPing is a simple SIP packet forging tool written in pure Python. 4 | 5 | With SIPPing you can create SIP Requests based on simple text templates. In the command line you can define variables that will be substituted in template. 6 | 7 | ### Features: 8 | 9 | * inline parsable templates for SIP messaging 10 | * "ping style" behavior: SIPPing sends out a SIP Request and wait for the response 11 | * aggressive mode: do not wait for any response and send out the request 12 | * you can use Python code for dynamic variable generation 13 | * you can dynamically load any python module used by your python variables 14 | * *Content-Length* and *CSeq* headers can be automatically generated (if not present in template file) 15 | * print on stdout received response with regex substitution applied 16 | 17 | ### Usage note: 18 | 19 | This software is released for didactical and debugging purposes. You're free to use it at your own risk. 20 | You can modify and redistribute this program under the [LGPLv3](http://www.gnu.org/licenses/lgpl-3.0.txt) license terms. 21 | 22 | ### Templates: 23 | 24 | SIPPing uses a simple templates mechanism based on Python dictionary-based string formatting. 25 | 26 | For example using this template saved on file *test-template.txt*: 27 | 28 | OPTIONS sip:%(user)s@%(destination)s:%(port)s;line=kutixubf SIP/2.0 29 | Via: SIP/2.0/UDP 192.168.10.1:5060;branch=z9hG4bK001b84f6;rport 30 | Max-Forwards: 70 31 | From: "fake" ;tag=as2e95fad1 32 | To: 33 | Contact: 34 | Call-ID: 7066d2f12e6f22ec1dc1231f4cade6be@172.16.18.40:5060 35 | User-Agent: SIPPing 36 | Date: Wed, 24 Apr 2013 20:35:23 GMT 37 | Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH 38 | Supported: replaces, timer 39 | 40 | You need to define three variables: **user**, **destination** and **port**, for example using this command line: 41 | 42 | sipping.py -r test-template.txt -d 172.16.18.35 -p 5060 -S 172.16.18.90 -P 5061 -c 3 -vuser:120 -v destination:192.168.20.1 -v port:5060 43 | 44 | * **-r test-template.txt** instruct the command to use the template file *test-template.txt* 45 | * **-d 172.16.18.35** define the destination IP for this SIP request 46 | * **-p 5060** define the destination port for this SIP request 47 | * **-S 172.16.18.90** define the local source address used to send this SIP request 48 | * **-P 5061** define the local source port 49 | * **-c 3** define the number of requests to send out 50 | * **-v user:120** define the template substitution variable **user** with value **120** 51 | * **-v destination:192.168.20.1** define the template substitution variable **destination** with value **192.168.20.1** 52 | * **-v port:5060** define the template substitution variable **port** with value **5060** 53 | 54 | This command execution will sends out 3 SIP request to 172.16.18.35 using 5060 as a source address and 5061 as a source port following the full SIP request: 55 | 56 | OPTIONS sip:120@192.168.20.1:5060;line=kutixubf SIP/2.0 57 | Content-Length: 0 58 | Via: SIP/2.0/UDP 192.168.10.1:5060;branch=z9hG4bK001b84f6;rport 59 | From: "fake" ;tag=as2e95fad1 60 | Supported: replaces, timer 61 | User-Agent: SIPPing 62 | To: 63 | Contact: 64 | CSeq: 0 OPTIONS 65 | Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH 66 | Call-ID: 7066d2f12e6f22ec1dc1231f4cade6be@172.16.18.40:5060 67 | Date: Wed, 24 Apr 2013 20:35:23 GMT 68 | Max-Forwards: 70 69 | 70 | OPTIONS sip:760.snomlabo.local:5060 SIP/2.0 71 | 72 | As you can see **user**, **destination** and **port** format strings are substituted with command line defined variables. You can notice also that **Content-Length** and **CSeq** header are automatically generated, you can avoid the automatic generation of these headers defining its in template file. 73 | 74 | Here the result of the command execution: 75 | 76 | sent Request OPTIONS to 172.16.18.35:5060 cseq=0 77 | received Response 200 OK from 172.16.18.35:5060 cseq=0 78 | sent Request OPTIONS to 172.16.18.35:5060 cseq=1 79 | received Response 200 OK from 172.16.18.35:5060 cseq=1 80 | sent Request OPTIONS to 172.16.18.35:5060 cseq=2 81 | received Response 200 OK from 172.16.18.35:5060 cseq=2 82 | 83 | --- statistics --- 84 | 3 packets transmitted, 3 packets received, 0.0% packet loss 85 | 86 | #### Runtime variables 87 | 88 | SIPPing already defines some variables compiled at runtime using command line parameters: 89 | 90 | * **dest_ip** destination IP address (extracted from the **-d** option ) 91 | * **dest_port** destination port (extracted from **-p** option) 92 | * **source_ip** source ip address (extracted from **-S** option) 93 | * **source_port** source port address (extracted from **-P** option) 94 | * **seq** seq number (auto generated if CSeq header is missing from the template) 95 | 96 | You don't need to define these variables with **-v** switch: values are already extracted from command line options. 97 | 98 | #### Python dynamic variables 99 | 100 | SIPPing supports also Python dynamic variables evaluated at runtime as a Python code. 101 | You need to define these variables with a name starting with a **dot** (**.**) in command line switch. 102 | 103 | Take a look on this example template that uses the **date** variable: 104 | 105 | **Template file** *test-date.txt* 106 | 107 | OPTIONS sip:%(dest_ip)s:%(dest_port)s SIP/2.0 108 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s 109 | Max-Forwards: 70 110 | From: "fake" 111 | To: 112 | Contact: 113 | Call-ID: fake-id@%(source_ip)s 114 | User-Agent: SIPPing 115 | Date: %(date)s 116 | Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH 117 | Supported: replaces, timer 118 | 119 | As you can see the header **Date** is defined trough the variable **date**. 120 | 121 | Using this command line: 122 | 123 | ./sipping.py -d 172.16.18.35 -S 172.16.18.90 -v '.date:time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())' -V -r test-date.txt 124 | 125 | **Note that the date name starts with a dot (.).** 126 | 127 | The **date** variable will be substitute with the Python code: 128 | 129 | time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) 130 | 131 | The previous command will sends out this SIP request: 132 | 133 | OPTIONS sip:172.16.18.35:5060 SIP/2.0 134 | Content-Length: 0 135 | Via: SIP/2.0/UDP 172.16.18.90:5060 136 | From: "fake" 137 | Supported: replaces, timer 138 | User-Agent: SIPPing 139 | To: 140 | Contact: 141 | CSeq: 1 OPTIONS 142 | Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH 143 | Call-ID: fake-id@172.16.18.90 144 | Date: Thu, 25 Apr 2013 003024 +0000 145 | Max-Forwards: 70 146 | 147 | As you can see the **Date** header has beer substituted with the generated date. 148 | If in your python code you need an additional module you can dynamically include with the **-m** switch. 149 | 150 | Here the list of available options: 151 | 152 | Options: 153 | -h, --help show this help message and exit 154 | -c COUNT Total number of queries to send 155 | -i WAIT Specify packet send interval time in seconds 156 | -T TIMEOUT Specify receiving timeout in seconds 157 | -v VAR add a template variable in format varname:value 158 | -V be verbose dumping full requests / responses 159 | -q be quiet and never print any report 160 | -a aggressive mode: ignore any response 161 | -S SOURCE_IP Specify ip address to bind for sending and receiving 162 | UDP datagrams 163 | -P SOURCE_PORT Specify the port number to use as a source port in UDP 164 | datagrams 165 | -d DEST_IP *mandatory* Specify the destination ip address 166 | -p DEST_PORT *mandatory* Specify the destination port number 167 | -r REQUEST_TEMPLATE Specify the request template file 168 | -t print the default request template 169 | -m MODULES load python modules used in python interpreted template 170 | variables 171 | -O OUT_REGEX regex to apply to response received, (default '(.* )*') 172 | -R OUT_REPLACE print this replace string applied to the response 173 | 174 | ## Examples 175 | 176 | Here a list of examples and some SIP templates contained in the examples/ directory. 177 | All following examples uses a snom phone as a target device and require these parameters configured on the phone: 178 | 179 | * [user_sipusername_as_line](http://wiki.snom.com/wiki/index.php/Settings/ user_sipusername_as_line) (aka "Support for broken registrar") to "on" 180 | * [filter_registrar](http://wiki.snom.com/wiki/index.php/Settings/filter_registrar) to "off" 181 | * [network_id_port](http://wiki.snom.com/Settings/network_id_port): 5060 182 | 183 | In my example command line I used these parameters: 184 | 185 | * **151** is a valid sip account 186 | * **172.16.18.35** is the phone IP 187 | * **5060** is the phone [network_id_port](http://wiki.snom.com/Settings/network_id_port) 188 | * **172.16.18.90** is the PC ip address 189 | * **5061** is the PC source port 190 | 191 | ### re-register a snom phone 192 | 193 | With this command you can force a re-registration of a snom phone. 194 | Be aware that you need to reconfigure these parameters on the phone: 195 | 196 | sipping.py -r examples/snom-check-sync-register.txt -v user:151 -d 172.16.18.35 -p 5060 -S 172.16.18.90 -P 5061 -c1 197 | 198 | 199 | ### trigger up a check-sync on a snom phone (no reboot) 200 | 201 | This command force a phone to synchronize its settings with the provisioning server 202 | 203 | sipping.py -r examples/snom-check-sync.txt -v user:151 -d 172.16.18.35 -p 5060 -S 172.16.18.90 -P 5060 -c1 204 | 205 | ### trigger up a check-sync on a snom phone (with reboot) 206 | 207 | This command force a phone to reboot and synchronize its settings with the provisioning server 208 | 209 | sipping.py -r examples/snom-check-sync-reboot.txt -v user:151 -d 172.16.18.35 -p 5060 -S 172.16.18.90 -P 5060 -c1 210 | 211 | ### send a snom PnP multicast request 212 | 213 | This command sends out a SUBSCRIBE for PnP provisioning 214 | 215 | sipping.py -d sip.mcast.net -p 5060 -S 172.16.18.91 -P 5060 -r examples/snom-pnp.txt -v model:snom720 -v mac:3C0754399E3D 216 | 217 | ### fire-up a minibrowser application on a snom phone 218 | 219 | This command sends to the phone a minibrowser XML application. 220 | 221 | This command requires the [xml_notify](http://wiki.snom.com/Settings/xml_notify) setting enabled on the phone. 222 | 223 | sipping.py -r examples/snom-notify-minibrowser.txt -d 172.16.18.35 -p 5060 -S 172.16.18.90 -P 5060 -c1 -a 224 | 225 | **Note:** this example uses also the *%(seq)d* formatter 226 | 227 | ### snom phones led controls 228 | 229 | With this example template you can turn on a phone button. 230 | This command uses a Python variable (**callid**) in order to generate a random Call-ID SIP header. 231 | You need to configure a function key as a button with number **1** (see **key** variable). 232 | 233 | ./sipping.py -a -q -d 172.16.18.35 -r examples/snom-led-on.txt -v user:202 -v key:1 -v .callid:"''.join(random.choice(string.ascii_lowercase + string.digits) for x in range(6))" -vcolor:$color -m string -m random -c1 234 | 235 | With this command you can turn off the phone led: 236 | 237 | ./sipping.py -d 172.16.18.35 -r examples/snom-led-off.txt -v user:202 -v key:$key -v .callid:"''.join(random.choice(string.ascii_lowercase + string.digits) for x in range(6))" -m string -m random -c1 -a -q 238 | 239 | More details about this protocol [here](http://wiki.snom.com/Category:HowTo:LED_Remote_Control) 240 | 241 | ## include an external file 242 | 243 | This command uses a python evaluated variable to include an external file: 244 | 245 | **file:** *examples/snom-notify-minibrowser-external.txt* 246 | 247 | NOTIFY sip:%(dest_ip)s:%(dest_port)s SIP/2.0 248 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s 249 | From: ;tag=2502 250 | To: ;tag=2502 251 | Call-ID: blablub@snom320xxx 252 | Max-Forwards: 70 253 | Event: xml 254 | Subscription-State: active;expires=30000 255 | Content-Type: application/snomxml 256 | 257 | %(appfile)s 258 | 259 | **file:** *examples/app.xml* 260 | 261 | 262 | 263 | Loaded app 264 | Prompt Text 265 | 266 | This is a test application loaded from an external file 267 | 268 | 269 | 270 | **Command executed:** 271 | 272 | ./sipping.py -d 172.16.18.35 -S 172.16.18.90 -v '.appfile:open("examples/app.xml", "r").read()' -r examples/snom-notify-minibrowser-external.txt -c 1 273 | 274 | **Request sent:** 275 | 276 | NOTIFY sip:172.16.18.35:5060 SIP/2.0 277 | Content-Length: 208 278 | Via: SIP/2.0/UDP 172.16.18.90:5060 279 | From: ;tag=2502 280 | Subscription-State: active;expires=30000 281 | To: ;tag=2502 282 | CSeq: 0 NOTIFY 283 | Max-Forwards: 70 284 | Call-ID: blablub@snom320xxx 285 | Content-Type: application/snomxml 286 | Event: xml 287 | 288 | 289 | 290 | Loaded app 291 | Prompt Text 292 | 293 | This is a test application loaded from an external file 294 | 295 | 296 | 297 | ## send contiuous REGISTER with incremental CSeq and random Call-ID headers 298 | 299 | This example shows how to send out REGISTER requests every 3 seconds (-i 3) with incremental CSeq header and random generated Call-ID. 300 | 301 | **file:** *examples/register.txt* 302 | 303 | REGISTER sip:%(dest_ip)s SIP/2.0 304 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s;branch=z9hG4bK-p985iy;rport 305 | From: "fake" ;tag=as2e95fad1 306 | To: 307 | Contact: 308 | Call-ID: %(callid)s@fake 309 | CSeq: %(seq)d REGISTER 310 | Max-Forwards: 70 311 | Supported: path, outbound, gruu 312 | User-Agent: SIPPing fake UA 313 | Expires: 3600 314 | Content-Length: 0 315 | 316 | **Command executed:** 317 | 318 | ./sipping.py -r examples/register.txt -d 172.16.18.35 -vuser:testuser -v .callid:"''.join(random.choice(string.ascii_lowercase + string.digits) for x in range(6))" -m string -m random -i 3 -S 172.16.18.90 319 | 320 | **First request sent:** 321 | 322 | REGISTER sip:172.16.18.35 SIP/2.0 323 | Content-Length: 0 324 | Via: SIP/2.0/UDP 172.16.18.90:5060;branch=z9hG4bK-p985iy;rport 325 | From: "fake" ;tag=as2e95fad1 326 | Supported: path, outbound, gruu 327 | Expires: 3600 328 | User-Agent: SIPPing fake UA 329 | To: 330 | Contact: 331 | CSeq: 1 REGISTER 332 | Call-ID: j8c5vl@fake 333 | Max-Forwards: 70 334 | 335 | **Second request sent:** 336 | 337 | REGISTER sip:172.16.18.35 SIP/2.0 338 | Content-Length: 0 339 | Via: SIP/2.0/UDP 172.16.18.90:5060;branch=z9hG4bK-p985iy;rport 340 | From: "fake" ;tag=as2e95fad1 341 | Supported: path, outbound, gruu 342 | Expires: 3600 343 | User-Agent: SIPPing fake UA 344 | To: 345 | Contact: 346 | CSeq: 2 REGISTER 347 | Call-ID: w6yj5g@fake 348 | Max-Forwards: 70 349 | 350 | and so on ... 351 | 352 | ## Installation 353 | You need to download the sipping.py script and run it: 354 | 355 | python sipping.py [options] 356 | or 357 | 358 | chmod +x sipping.py 359 | ./sipping.py [options] 360 | 361 | You can download SIPPing from the git master branch on [github](https://github.com/snom-it/SIPPing/archive/master.zip). 362 | 363 | #### Requirements: 364 | There is no particular requirements, this script runs on standard Python 2.X (tested with >= v2.4), no additional modules is required. Runs on GNU/Linux, MacOSX and Windows (untested at the moment). 365 | 366 | ## FAQ 367 | 368 | * **Q:** when I run sipping.py I receive this error: 369 | 370 | **ERROR: error in template processing. unsupported format character 'X' (0xYZ) at index K** 371 | 372 | * **R:** this means that you used a wrong format string: all format string must be wrote in this format: **%(NAME)s** where **NAME** is the variable name (pleas note the starting *%* char and the ending *s*). The only exception is for **%(seq)d** variable. You can read more information about dictionary based string formatting [here](http://www.diveintopython.net/html_processing/dictionary_based_string_formatting.html) and [here](http://docs.python.org/2/library/stdtypes.html#string-formatting). 373 | 374 | * **Q:** when I run sipping.py I receive this error: 375 | 376 | **ERROR: missing template variable. 'var_name'** 377 | 378 | * **A:** this means that a variable with name **var_name** is missing, you can declare via command line using the *-v var_name:value* switch. 379 | 380 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/pbertera/SIPPing/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 381 | -------------------------------------------------------------------------------- /sipping.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | # -*- Mode: Python -*- 4 | # 5 | # Copyright (C) 2013 snom technology 6 | # 7 | # This program is available under the Lesser General Public Licence (LGPL) Version 3 8 | # This software is released for didactical and debugging purposes. You're free to use it at your own risk. 9 | 10 | import socket 11 | import time 12 | import sys 13 | import optparse 14 | import select 15 | import cStringIO 16 | import re 17 | 18 | from string import Template 19 | 20 | 21 | def_request = """OPTIONS sip:%(dest_ip)s:%(dest_port)s SIP/2.0 22 | Via: SIP/2.0/UDP %(source_ip)s:%(source_port)s 23 | Max-Forwards: 70 24 | From: "fake" 25 | To: 26 | Contact: 27 | Call-ID: fake-id@%(source_ip)s 28 | User-Agent: SIPPing 29 | Date: Wed, 24 Apr 2013 20:35:23 GMT 30 | Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH 31 | Supported: replaces, timer""" 32 | 33 | 34 | def_out = "" 35 | 36 | 37 | class CustomTemplate(Template): 38 | idpattern = r'[a-z][\.\-_a-z0-9]*' 39 | 40 | 41 | class SipError(Exception): 42 | pass 43 | 44 | 45 | class SipUnpackError(SipError): 46 | pass 47 | 48 | 49 | class SipNeedData(SipUnpackError): 50 | pass 51 | 52 | 53 | class SipPackError(SipError): 54 | pass 55 | 56 | 57 | def canon_header(s): 58 | exception = { 59 | 'call-id': 'Call-ID', 60 | 'cseq': 'CSeq', 61 | 'www-authenticate': 'WWW-Authenticate' 62 | } 63 | short = ['allow-events', 'u', 'call-id', 'i', 'contact', 'm', 'content-encoding', 'e', 64 | 'content-length', 'l', 'content-type', 'c', 'event', 'o', 'from', 'f', 'subject', 's', 'supported', 'k', 'to', 't', 'via', 'v'] 65 | s = s.lower() 66 | return ((len(s) == 1) and s in short and canon_header(short[short.index(s) - 1])) \ 67 | or (s in exception and exception[s]) or '-'.join([x.capitalize() for x in s.split('-')]) 68 | 69 | 70 | def parse_headers(f): 71 | """Return dict of HTTP headers parsed from a file object.""" 72 | d = {} 73 | while 1: 74 | line = f.readline().strip() 75 | if not line: 76 | break 77 | l = line.split(None, 1) 78 | if not l[0].endswith(':'): 79 | raise SipUnpackError('invalid header: %r' % line) 80 | k = l[0][:-1].lower() 81 | d[k] = len(l) != 1 and l[1] or '' 82 | return d 83 | 84 | 85 | def parse_body(f, headers): 86 | """Return SIP body parsed from a file object, given HTTP header dict.""" 87 | if 'content-length' in headers: 88 | n = int(headers['content-length']) 89 | body = f.read(n) 90 | if len(body) != n: 91 | raise SipNeedData('short body (missing %d bytes)' % (n - len(body))) 92 | elif 'content-type' in headers: 93 | body = f.read() 94 | else: 95 | body = '' 96 | return body 97 | 98 | 99 | class Message: 100 | """SIP Protocol headers + body.""" 101 | __metaclass__ = type 102 | __hdr_defaults__ = {} 103 | headers = None 104 | body = None 105 | 106 | def __init__(self, *args, **kwargs): 107 | if args: 108 | self.unpack(args[0]) 109 | else: 110 | self.headers = {} 111 | self.body = '' 112 | for k, v in self.__hdr_defaults__.iteritems(): 113 | setattr(self, k, v) 114 | for k, v in kwargs.iteritems(): 115 | setattr(self, k, v) 116 | 117 | def unpack(self, buf): 118 | f = cStringIO.StringIO(buf) 119 | # Parse headers 120 | self.headers = parse_headers(f) 121 | # Parse body 122 | self.body = parse_body(f, self.headers) 123 | # Save the rest 124 | self.data = f.read() 125 | 126 | def pack_hdr(self): 127 | return ''.join(['%s: %s\r\n' % (canon_header(k), v) for k, v in self.headers.iteritems()]) 128 | 129 | def __len__(self): 130 | return len(str(self)) 131 | 132 | def __str__(self): 133 | return '%s\r\n%s' % (self.pack_hdr(), self.body) 134 | 135 | 136 | class Request(Message): 137 | """SIP request.""" 138 | __hdr_defaults__ = { 139 | 'method': 'INVITE', 140 | 'uri': 'sip:user@example.com', 141 | 'version': '2.0', 142 | 'headers': {'to': '', 'from': '', 'call-id': '', 'cseq': '', 'contact': ''} 143 | } 144 | __methods = dict.fromkeys(( 145 | 'ACK', 'BYE', 'CANCEL', 'INFO', 'INVITE', 'MESSAGE', 'NOTIFY', 146 | 'OPTIONS', 'PRACK', 'PUBLISH', 'REFER', 'REGISTER', 'SUBSCRIBE', 147 | 'UPDATE' 148 | )) 149 | __proto = 'SIP' 150 | 151 | def unpack(self, buf): 152 | f = cStringIO.StringIO(buf) 153 | line = f.readline() 154 | l = line.strip().split() 155 | if len(l) != 3 or l[0] not in self.__methods or not l[2].startswith(self.__proto): 156 | raise SipUnpackError('invalid request: %r' % line) 157 | self.method = l[0] 158 | self.uri = l[1] 159 | self.version = l[2][len(self.__proto) + 1:] 160 | Message.unpack(self, f.read()) 161 | 162 | def __str__(self): 163 | return '%s %s %s/%s\r\n' % (self.method, self.uri, self.__proto, 164 | self.version) + Message.__str__(self) 165 | 166 | 167 | class Response(Message): 168 | """SIP response.""" 169 | 170 | __hdr_defaults__ = { 171 | 'version': '2.0', 172 | 'status': '200', 173 | 'reason': 'OK', 174 | 'headers': {'to': '', 'from': '', 'call-id': '', 'cseq': '', 'contact': ''} 175 | } 176 | __proto = 'SIP' 177 | 178 | def unpack(self, buf): 179 | f = cStringIO.StringIO(buf) 180 | line = f.readline() 181 | l = line.strip().split(None, 2) 182 | if len(l) < 2 or not l[0].startswith(self.__proto) or not l[1].isdigit(): 183 | raise SipUnpackError('invalid response: %r' % line) 184 | self.version = l[0][len(self.__proto) + 1:] 185 | self.status = l[1] 186 | self.reason = l[2] 187 | Message.unpack(self, f.read()) 188 | 189 | def __str__(self): 190 | return '%s/%s %s %s\r\n' % (self.__proto, self.version, self.status, 191 | self.reason) + Message.__str__(self) 192 | 193 | 194 | def render_template(template, template_vars): 195 | for k in template_vars.keys(): 196 | if k.startswith("."): 197 | template_vars[k[1:]] = eval(template_vars[k]) 198 | try: 199 | ret = template % template_vars 200 | except KeyError, e: 201 | sys.stderr.write("ERROR: missing template variable. %s\n" % e) 202 | sys.exit(-1) 203 | except Exception, e: 204 | sys.stderr.write("ERROR: error in template processing. %s\n" % e) 205 | sys.exit(-1) 206 | return ret 207 | 208 | 209 | def gen_request(template_vars, options): 210 | 211 | for i in xrange(options.count): 212 | template_vars["seq"] = i 213 | for k in template_vars.keys(): 214 | if k.startswith("."): 215 | template_vars[k[1:]] = eval(template_vars[k]) 216 | 217 | if options.request_template is None: 218 | request = render_template(def_request, template_vars) 219 | else: 220 | try: 221 | f = open(options.request_template) 222 | file_request = f.read() 223 | f.close() 224 | request = render_template(file_request, template_vars) 225 | except Exception, e: 226 | sys.stderr.write("ERROR: cannot open file %s. %s\n" % (options.request_template, e)) 227 | sys.exit(-1) 228 | try: 229 | req = Request(request) 230 | except SipUnpackError, e: 231 | sys.stderr.write("ERROR: malformed SIP Request. %s\n" % e) 232 | sys.exit(-1) 233 | 234 | if "cseq" not in req.headers: 235 | req.headers["cseq"] = "%d %s" % (i, req.method) 236 | yield str(req) 237 | 238 | 239 | def open_sock(options): 240 | try: 241 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 242 | sock.setblocking(0) 243 | except Exception, e: 244 | sys.stderr.write("ERROR: cannot create socket. %s\n" % e) 245 | sys.exit(-1) 246 | try: 247 | sock.seckopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 248 | sock.seckopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) 249 | except AttributeError: 250 | pass 251 | if options.source_port: 252 | sock.bind((options.source_ip, options.source_port)) 253 | sock.settimeout(options.wait) 254 | return sock 255 | 256 | 257 | def print_reply(buf, template_vars=None, out_regex=None, out_replace=None, err=None, verbose=False, quiet=False): 258 | src_ip = buf[1][0] 259 | src_port = buf[1][1] 260 | template_vars['sock_src_ip'] = src_ip 261 | template_vars['sock_src_port'] = src_port 262 | 263 | try: 264 | resp = Response(buf[0]) 265 | except SipUnpackError, e: 266 | resp = Request(buf[0]) 267 | 268 | if resp.__class__.__name__ == "Response": 269 | template_vars['status'] = resp.status 270 | template_vars['reason'] = resp.reason 271 | if not out_regex: 272 | out_regex = '(.*\n)*' 273 | if not out_replace: 274 | out_replace = "received Response %(status)s %(reason)s from %(sock_src_ip)s:%(sock_src_port)s cseq=%(seq)s\n" % template_vars 275 | elif resp.__class__.__name__ == "Request": 276 | template_vars['method'] = resp.method 277 | template_vars['uri'] = resp.uri 278 | if not out_replace: 279 | out_regex = '(.*\n)*' 280 | if not out_replace: 281 | out_replace = "received Request %(method)s %(uri)s from %(sock_src_ip)s:%(sock_src_port)s cseq=%(seq)s\n" % template_vars 282 | if verbose: 283 | sys.stderr.write("%s\n" % resp) 284 | 285 | if True: 286 | try: 287 | out = re.sub(out_regex, out_replace, "%s" % resp) 288 | # replaced_out = CustomTemplate(out).safe_substitute(template_vars) 289 | replaced_out = out % template_vars 290 | print replaced_out 291 | except Exception, e: 292 | sys.stderr.write("ERROR: an issue is occoured applying regex substitution:\n") 293 | sys.stderr.write("\t-----------------------------------\n") 294 | sys.stderr.write("\t%s\n" % e) 295 | sys.stderr.write("\t-----------------------------------\n") 296 | sys.stderr.write("\tTemplate vars:\n") 297 | for k in template_vars: 298 | sys.stderr.write("\t\t%s:'%s'\n" % (k, template_vars[k])) 299 | sys.stderr.write("\tRegex: '%s'\n" % out_regex) 300 | sys.stderr.write("\tSubstitution string: '%s'\n" % out_replace) 301 | sys.stderr.write("\tSubstitution text: '%s'\n" % replaced_out) 302 | sys.stderr.write("\tResponse: \n") 303 | sys.stderr.write("\t-----------------------------------\n\t") 304 | sys.stderr.write("\n\t".join(resp.__str__().split("\n"))) 305 | sys.stderr.write("-----------------------------------\n") 306 | sys.stderr.write("%s\n" % resp) 307 | return True 308 | 309 | 310 | def main(): 311 | usage = """%prog [OPTIONS]""" 312 | opt = optparse.OptionParser(usage=usage) 313 | opt.add_option('-c', dest='count', type='int', default=sys.maxint, 314 | help='Total number of queries to send') 315 | opt.add_option('-i', dest='wait', type='float', default=1, 316 | help='Specify packet send interval time in seconds') 317 | opt.add_option('-T', dest='timeout', type='float', default=1, 318 | help='Specify receiving timeout in seconds') 319 | opt.add_option('-v', dest='var', type='string', default=[""], action='append', 320 | help='add a template variable in format varname:value') 321 | opt.add_option('-V', dest='verbose', default=False, action='store_true', 322 | help='be verbose dumping full requests / responses') 323 | opt.add_option('-q', dest='quiet', default=False, action='store_true', 324 | help='be quiet and never print any report') 325 | opt.add_option('-a', dest='aggressive', default=False, action='store_true', 326 | help='aggressive mode: ignore any response') 327 | opt.add_option('-S', dest='source_ip', type='string', default="0.0.0.0", 328 | help='Specify ip address to bind for sending and receiving UDP datagrams') 329 | opt.add_option('-P', dest='source_port', type='int', default=5060, 330 | help='Specify the port number to use as a source port in UDP datagrams') 331 | opt.add_option('-d', dest='dest_ip', type='string', default=None, 332 | help='*mandatory* Specify the destination ip address') 333 | opt.add_option('-p', dest='dest_port', type='int', default=5060, 334 | help='*mandatory* Specify the destination port number') 335 | opt.add_option('-r', dest='request_template', type='string', default=None, 336 | help='Specify the request template file') 337 | opt.add_option('-t', dest='print_template', action="store_true", default=False, 338 | help='print the default request template') 339 | opt.add_option('-m', dest='modules', type='string', default=[], action='append', 340 | help='load additionals Python modules used in Python interpreted template variables') 341 | opt.add_option('-O', dest='out_regex', type='string', default="", 342 | help='regex to apply to response received, (default \'(.*\n)*\')') 343 | opt.add_option('-R', dest='out_replace', type='string', default="", 344 | help='print this replace string applied to the response') 345 | 346 | options, args = opt.parse_args(sys.argv[1:]) 347 | if options.print_template: 348 | sys.stderr.write("%s\n" % def_request) 349 | sys.exit() 350 | 351 | for m in options.modules: 352 | globals()[m] = __import__(m) 353 | 354 | if not options.dest_ip: 355 | sys.stderr.write("ERROR: destination ip not defined\n") 356 | opt.print_help() 357 | sys.exit(-1) 358 | 359 | template_vars = { 360 | "source_ip": options.source_ip, 361 | "source_port": options.source_port, 362 | "dest_ip": options.dest_ip, 363 | "dest_port": options.dest_port 364 | } 365 | 366 | # first var is empty by default 367 | for v in options.var[1:]: 368 | try: 369 | key = v.split(":")[0] 370 | val = ":".join(v.split(":")[1:]) 371 | template_vars.update({key: val}) 372 | except IndexError: 373 | sys.stderr.write("ERROR: variables must be in format name:value. %s\n" % v) 374 | opt.print_help() 375 | sys.exit() 376 | 377 | if options.verbose: 378 | sys.stderr.write("=======================================\n") 379 | sys.stderr.write("I'm using these variables in templates: \n") 380 | sys.stderr.write("=======================================\n") 381 | for k in template_vars: 382 | sys.stderr.write("%s: %s\n" % (k, template_vars[k])) 383 | sys.stderr.write("=======================================\n\n") 384 | 385 | try: 386 | sock = open_sock(options) 387 | except Exception, e: 388 | sys.stderr.write("ERROR: cannot open socket. %s\n" % e) 389 | sys.exit(-1) 390 | 391 | sent = rcvd = 0 392 | 393 | try: 394 | for req in gen_request(template_vars, options): 395 | try: 396 | sip_req = Request(req) 397 | # Add Content-Lenght if missing 398 | if "content-length" not in sip_req.headers: 399 | sip_req.headers["content-length"] = len(sip_req.body) 400 | 401 | try: 402 | sock.sendto(str(sip_req), (options.dest_ip, options.dest_port)) 403 | except Exception, e: 404 | sys.stderr.write("ERROR: cannot send packet to %s:%d. %s\n" % (options.dest_ip, options.dest_port, e)) 405 | if not options.quiet: 406 | sys.stderr.write("sent Request %s to %s:%d cseq=%s len=%d\n" % (sip_req.method, options.dest_ip, options.dest_port, sip_req.headers['cseq'].split()[0], len(str(sip_req)))) 407 | if options.verbose: 408 | sys.stderr.write("\n=== Full Request sent ===\n\n") 409 | sys.stderr.write("%s\n" % sip_req) 410 | sent += 1 411 | 412 | if not options.aggressive: 413 | read = [sock] 414 | inputready, outputready, exceptready = select.select(read, [], [], options.timeout) 415 | for s in inputready: 416 | if s == sock: 417 | buf = None 418 | buf = sock.recvfrom(0xffff) 419 | print_reply(buf, template_vars, options.out_regex, options.out_replace, verbose=options.verbose, quiet=options.quiet) 420 | rcvd += 1 421 | 422 | except socket.timeout: 423 | pass 424 | time.sleep(options.wait) 425 | except KeyboardInterrupt: 426 | pass 427 | 428 | if not options.quiet: 429 | sys.stderr.write('\n--- statistics ---\n') 430 | sys.stderr.write('%d packets transmitted, %d packets received, %.1f%% packet loss\n' % (sent, rcvd, (float(sent - rcvd) / sent) * 100)) 431 | 432 | 433 | if __name__ == '__main__': 434 | main() 435 | --------------------------------------------------------------------------------