blob: 0c05756f12b98105f2b47ee0a2f2017a326d95bf [file] [log] [blame]
Benny Prijono632be0a2008-06-26 19:51:01 +00001# $Id$
Benny Prijono7d578a72008-06-20 00:25:55 +00002#
3from socket import *
4import re
5import random
6import time
7import sys
8import inc_cfg as cfg
9from select import *
10
11# SIP request template
12req_templ = \
13"""$METHOD $TARGET_URI SIP/2.0\r
14Via: SIP/2.0/UDP $LOCAL_IP:$LOCAL_PORT;rport;branch=z9hG4bK$BRANCH\r
15Max-Forwards: 70\r
16From: <sip:caller@pjsip.org>$FROM_TAG\r
17To: <$TARGET_URI>$TO_TAG\r
18Contact: <sip:$LOCAL_IP:$LOCAL_PORT;transport=udp>\r
19Call-ID: $CALL_ID@pjsip.org\r
20CSeq: $CSEQ $METHOD\r
21Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, REFER\r
22Supported: replaces, 100rel, norefersub\r
23User-Agent: pjsip.org Python tester\r
24Content-Length: $CONTENT_LENGTH\r
25$SIP_HEADERS"""
26
27
28def is_request(msg):
29 return msg.split(" ", 1)[0] != "SIP/2.0"
30
31def is_response(msg):
32 return msg.split(" ", 1)[0] == "SIP/2.0"
33
34def get_code(msg):
35 if msg=="":
36 return 0
37 return int(msg.split(" ", 2)[1])
38
39def get_tag(msg, hdr="To"):
40 pat = "^" + hdr + ":.*"
41 result = re.search(pat, msg, re.M | re.I)
42 if result==None:
43 return ""
44 line = result.group()
45 #print "line=", line
46 tags = line.split(";tag=")
47 if len(tags)>1:
48 return tags[1]
49 return ""
50 #return re.split("[;& ]", s)
51
Benny Prijonoe9a82242008-07-07 20:14:41 +000052def get_header(msg, hname):
53 headers = msg.splitlines()
54 for hdr in headers:
55 hfields = hdr.split(": ", 2)
56 if hfields[0]==hname:
57 return hfields[1]
58 return None
Benny Prijono7d578a72008-06-20 00:25:55 +000059
60class Dialog:
61 sock = None
62 dst_addr = ""
63 dst_port = 5060
64 local_ip = ""
65 local_port = 0
66 tcp = False
67 call_id = str(random.random())
68 cseq = 0
69 local_tag = ";tag=" + str(random.random())
70 rem_tag = ""
71 last_resp_code = 0
72 inv_branch = ""
73 trace_enabled = True
74 last_request = ""
Benny Prijonoe9a82242008-07-07 20:14:41 +000075 def __init__(self, dst_addr, dst_port=5060, tcp=False, trace=True, local_port=0):
Benny Prijono7d578a72008-06-20 00:25:55 +000076 self.dst_addr = dst_addr
77 self.dst_port = dst_port
78 self.tcp = tcp
79 self.trace_enabled = trace
80 if tcp==True:
81 self.sock = socket(AF_INET, SOCK_STREAM)
82 self.sock.connect(dst_addr, dst_port)
83 else:
84 self.sock = socket(AF_INET, SOCK_DGRAM)
Benny Prijonoe9a82242008-07-07 20:14:41 +000085 self.sock.bind(("127.0.0.1", local_port))
Benny Prijono7d578a72008-06-20 00:25:55 +000086
87 self.local_ip, self.local_port = self.sock.getsockname()
88 self.trace("Dialog socket bound to " + self.local_ip + ":" + str(self.local_port))
89
90 def trace(self, txt):
91 if self.trace_enabled:
92 print str(time.strftime("%H:%M:%S ")) + txt
93
Benny Prijono18ece2f2009-02-06 07:56:09 +000094 def update_fields(self, msg):
Benny Prijono7d578a72008-06-20 00:25:55 +000095 if self.tcp:
96 transport_param = ";transport=tcp"
97 else:
98 transport_param = ""
99 msg = msg.replace("$TARGET_URI", "sip:"+self.dst_addr+":"+str(self.dst_port) + transport_param)
100 msg = msg.replace("$LOCAL_IP", self.local_ip)
101 msg = msg.replace("$LOCAL_PORT", str(self.local_port))
Benny Prijono7d578a72008-06-20 00:25:55 +0000102 msg = msg.replace("$FROM_TAG", self.local_tag)
103 msg = msg.replace("$TO_TAG", self.rem_tag)
104 msg = msg.replace("$CALL_ID", self.call_id)
105 msg = msg.replace("$CSEQ", str(self.cseq))
Benny Prijono18ece2f2009-02-06 07:56:09 +0000106 branch=str(random.random())
107 msg = msg.replace("$BRANCH", branch)
108 return msg
109
110 def create_req(self, method, sdp, branch="", extra_headers=""):
111 if branch=="":
112 self.cseq = self.cseq + 1
113 msg = req_templ
114 msg = msg.replace("$METHOD", method)
Benny Prijono7d578a72008-06-20 00:25:55 +0000115 msg = msg.replace("$SIP_HEADERS", extra_headers)
Benny Prijono18ece2f2009-02-06 07:56:09 +0000116 if branch=="":
117 branch=str(random.random())
118 msg = msg.replace("$BRANCH", branch)
Benny Prijono7d578a72008-06-20 00:25:55 +0000119 if sdp!="":
120 msg = msg.replace("$CONTENT_LENGTH", str(len(sdp)))
121 msg = msg + "Content-Type: application/sdp\r\n"
122 else:
123 msg = msg.replace("$CONTENT_LENGTH", "0")
124 msg = msg + "\r\n"
125 msg = msg + sdp
Benny Prijono18ece2f2009-02-06 07:56:09 +0000126 return self.update_fields(msg)
Benny Prijono7d578a72008-06-20 00:25:55 +0000127
Benny Prijonoe9a82242008-07-07 20:14:41 +0000128 def create_response(self, request, code, reason, to_tag=""):
129 response = "SIP/2.0 " + str(code) + " " + reason + "\r\n"
130 lines = request.splitlines()
131 for line in lines:
132 hdr = line.split(":", 1)[0]
133 if hdr in ["Via", "From", "To", "CSeq", "Call-ID"]:
134 if hdr=="To" and to_tag!="":
135 line = line + ";tag=" + to_tag
136 elif hdr=="Via":
137 line = line + ";received=127.0.0.1"
138 response = response + line + "\r\n"
139 return response
140
Benny Prijono7d578a72008-06-20 00:25:55 +0000141 def create_invite(self, sdp, extra_headers=""):
142 self.inv_branch = str(random.random())
143 return self.create_req("INVITE", sdp, branch=self.inv_branch, extra_headers=extra_headers)
144
145 def create_ack(self, sdp="", extra_headers=""):
146 return self.create_req("ACK", sdp, extra_headers=extra_headers, branch=self.inv_branch)
147
148 def create_bye(self, extra_headers=""):
149 return self.create_req("BYE", "", extra_headers)
150
Benny Prijonoe9a82242008-07-07 20:14:41 +0000151 def send_msg(self, msg, dst_addr=None):
Benny Prijono7d578a72008-06-20 00:25:55 +0000152 if (is_request(msg)):
153 self.last_request = msg.split(" ", 1)[0]
Benny Prijonoe9a82242008-07-07 20:14:41 +0000154 if not dst_addr:
155 dst_addr = (self.dst_addr, self.dst_port)
156 self.trace("============== TX MSG to " + str(dst_addr) + " ============= \n" + msg)
157 self.sock.sendto(msg, 0, dst_addr)
Benny Prijono7d578a72008-06-20 00:25:55 +0000158
Benny Prijonoe9a82242008-07-07 20:14:41 +0000159 def wait_msg_from(self, timeout):
Benny Prijono7d578a72008-06-20 00:25:55 +0000160 endtime = time.time() + timeout
161 msg = ""
Benny Prijonoe9a82242008-07-07 20:14:41 +0000162 src_addr = None
Benny Prijono7d578a72008-06-20 00:25:55 +0000163 while time.time() < endtime:
164 readset = select([self.sock], [], [], timeout)
165 if len(readset) < 1 or not self.sock in readset[0]:
166 if len(readset) < 1:
167 print "select() returns " + str(len(readset))
168 elif not self.sock in readset[0]:
169 print "select() alien socket"
170 else:
171 print "select other error"
172 continue
173 try:
Benny Prijonoe9a82242008-07-07 20:14:41 +0000174 msg, src_addr = self.sock.recvfrom(2048)
Benny Prijono7d578a72008-06-20 00:25:55 +0000175 except:
176 print "recv() exception: ", sys.exc_info()[0]
177 continue
178
179 if msg=="":
Benny Prijonoe9a82242008-07-07 20:14:41 +0000180 return "", None
Benny Prijono7d578a72008-06-20 00:25:55 +0000181 if self.last_request=="INVITE" and self.rem_tag=="":
182 self.rem_tag = get_tag(msg, "To")
183 self.rem_tag = self.rem_tag.rstrip("\r\n;")
184 if self.rem_tag != "":
185 self.rem_tag = ";tag=" + self.rem_tag
186 self.trace("=== rem_tag:" + self.rem_tag)
Benny Prijonoe9a82242008-07-07 20:14:41 +0000187 self.trace("=========== RX MSG from " + str(src_addr) + " ===========\n" + msg)
188 return (msg, src_addr)
Benny Prijono7d578a72008-06-20 00:25:55 +0000189
Benny Prijonoe9a82242008-07-07 20:14:41 +0000190 def wait_msg(self, timeout):
191 return self.wait_msg_from(timeout)[0]
192
Benny Prijono7d578a72008-06-20 00:25:55 +0000193 # Send request and wait for final response
194 def send_request_wait(self, msg, timeout):
195 t1 = 1.0
196 endtime = time.time() + timeout
197 resp = ""
198 code = 0
199 for i in range(0,5):
200 self.send_msg(msg)
201 resp = self.wait_msg(t1)
202 if resp!="" and is_response(resp):
203 code = get_code(resp)
204 break
205 last_resp = resp
206 while code < 200 and time.time() < endtime:
207 resp = self.wait_msg(endtime - time.time())
208 if resp != "" and is_response(resp):
209 code = get_code(resp)
210 last_resp = resp
211 elif resp=="":
212 break
213 return last_resp
214
215 def hangup(self, last_code=0):
216 self.trace("====== hangup =====")
217 if last_code!=0:
218 self.last_resp_code = last_code
219 if self.last_resp_code>0 and self.last_resp_code<200:
220 msg = self.create_req("CANCEL", "", branch=self.inv_branch, extra_headers="")
221 self.send_request_wait(msg, 5)
222 msg = self.create_ack()
223 self.send_msg(msg)
224 elif self.last_resp_code>=200 and self.last_resp_code<300:
225 msg = self.create_ack()
226 self.send_msg(msg)
227 msg = self.create_bye()
228 self.send_request_wait(msg, 5)
229 else:
230 msg = self.create_ack()
231 self.send_msg(msg)
232
233
234class SendtoCfg:
235 # Test name
236 name = ""
237 # pjsua InstanceParam
238 inst_param = None
Benny Prijono036911b2008-06-27 21:22:12 +0000239 # Complete INVITE message. If this is not empty, then this
240 # message will be sent instead and the "sdp" and "extra_headers"
241 # settings will be ignored.
242 complete_msg = ""
Benny Prijono7d578a72008-06-20 00:25:55 +0000243 # Initial SDP
244 sdp = ""
Benny Prijono632be0a2008-06-26 19:51:01 +0000245 # Extra headers to add to request
246 extra_headers = ""
Benny Prijono7d578a72008-06-20 00:25:55 +0000247 # Expected code
248 resp_code = 0
249 # Use TCP?
250 use_tcp = False
251 # List of RE patterns that must exist in response
252 resp_include = []
253 # List of RE patterns that must NOT exist in response
254 resp_exclude = []
255 # Constructor
Benny Prijono632be0a2008-06-26 19:51:01 +0000256 def __init__(self, name, pjsua_args, sdp, resp_code,
257 resp_inc=[], resp_exc=[], use_tcp=False,
Benny Prijono1e65e9a2008-06-27 23:53:00 +0000258 extra_headers="", complete_msg="",
259 enable_buffer = False):
Benny Prijono036911b2008-06-27 21:22:12 +0000260 self.complete_msg = complete_msg
Benny Prijono7d578a72008-06-20 00:25:55 +0000261 self.sdp = sdp
262 self.resp_code = resp_code
263 self.resp_include = resp_inc
264 self.resp_exclude = resp_exc
265 self.use_tcp = use_tcp
Benny Prijono632be0a2008-06-26 19:51:01 +0000266 self.extra_headers = extra_headers
Benny Prijono7d578a72008-06-20 00:25:55 +0000267 self.inst_param = cfg.InstanceParam("pjsua", pjsua_args)
Benny Prijono1e65e9a2008-06-27 23:53:00 +0000268 self.inst_param.enable_buffer = enable_buffer
Benny Prijono7d578a72008-06-20 00:25:55 +0000269
Benny Prijonoe9a82242008-07-07 20:14:41 +0000270
271class RecvfromTransaction:
272 # The test title for this transaction
273 title = ""
274 # Optinal list of pjsua command and optional expect patterns
275 # to be invoked to make pjsua send a request
276 # Sample:
277 # (to make call and wait for INVITE to be sent)
278 # cmds = [ ["m"], ["sip:127.0.0.1", "INVITE sip:"] ]
279 cmds = []
280 # Check if the CSeq must be greater than last Cseq?
281 check_cseq = True
282 # List of RE patterns that must exists in incoming request
283 include = []
284 # List of RE patterns that MUST NOT exist in incoming request
285 exclude = []
286 # Response code to send
287 resp_code = 0
288 # Additional list of headers to be sent on the response
289 # Note: no need to add CRLF on the header
290 resp_hdr = []
291 # Message body. This should include the Content-Type header too.
292 # Sample:
293 # body = """Content-Type: application/sdp\r\n
294 # \r\n
295 # v=0\r\n
296 # ...
297 # """
298 body = None
299 # Pattern to be expected on pjsua when receiving the response
300 expect = ""
301
302 def __init__(self, title, resp_code, check_cseq=True,
303 include=[], exclude=[], cmds=[], resp_hdr=[], resp_body=None, expect=""):
304 self.title = title
305 self.cmds = cmds
306 self.include = include
307 self.exclude = exclude
308 self.resp_code = resp_code
309 self.resp_hdr = resp_hdr
310 self.resp_body = resp_body
311 self.expect = expect
312
313
314class RecvfromCfg:
315 # Test name
316 name = ""
317 # pjsua InstanceParam
318 inst_param = None
319 # List of RecvfromTransaction
320 transaction = None
321 # Use TCP?
322 tcp = False
323
324 # Note:
325 # Any "$PORT" string in the pjsua_args will be replaced
326 # by server port
327 def __init__(self, name, pjsua_args, transaction, tcp=False):
328 self.name = name
329 self.inst_param = cfg.InstanceParam("pjsua", pjsua_args)
330 self.transaction = transaction
331 self.tcp=tcp
332
333
334
335