├── .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 | [](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 |
--------------------------------------------------------------------------------