blob: 04b5a96e6f274e6c343875b5988596be8947a4f7 [file] [log] [blame]
Benny Prijono7d578a72008-06-20 00:25:55 +00001# $Id:$
2#
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
52
53class Dialog:
54 sock = None
55 dst_addr = ""
56 dst_port = 5060
57 local_ip = ""
58 local_port = 0
59 tcp = False
60 call_id = str(random.random())
61 cseq = 0
62 local_tag = ";tag=" + str(random.random())
63 rem_tag = ""
64 last_resp_code = 0
65 inv_branch = ""
66 trace_enabled = True
67 last_request = ""
68 def __init__(self, dst_addr, dst_port=5060, tcp=False, trace=True):
69 self.dst_addr = dst_addr
70 self.dst_port = dst_port
71 self.tcp = tcp
72 self.trace_enabled = trace
73 if tcp==True:
74 self.sock = socket(AF_INET, SOCK_STREAM)
75 self.sock.connect(dst_addr, dst_port)
76 else:
77 self.sock = socket(AF_INET, SOCK_DGRAM)
78 self.sock.bind(("127.0.0.1", 0))
79
80 self.local_ip, self.local_port = self.sock.getsockname()
81 self.trace("Dialog socket bound to " + self.local_ip + ":" + str(self.local_port))
82
83 def trace(self, txt):
84 if self.trace_enabled:
85 print str(time.strftime("%H:%M:%S ")) + txt
86
87 def create_req(self, method, sdp, branch="", extra_headers=""):
88 if branch=="":
89 self.cseq = self.cseq + 1
90 msg = req_templ
91 msg = msg.replace("$METHOD", method)
92 if self.tcp:
93 transport_param = ";transport=tcp"
94 else:
95 transport_param = ""
96 msg = msg.replace("$TARGET_URI", "sip:"+self.dst_addr+":"+str(self.dst_port) + transport_param)
97 msg = msg.replace("$LOCAL_IP", self.local_ip)
98 msg = msg.replace("$LOCAL_PORT", str(self.local_port))
99 if branch=="":
100 branch=str(random.random())
101 msg = msg.replace("$BRANCH", branch)
102 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))
106 msg = msg.replace("$SIP_HEADERS", extra_headers)
107 if sdp!="":
108 msg = msg.replace("$CONTENT_LENGTH", str(len(sdp)))
109 msg = msg + "Content-Type: application/sdp\r\n"
110 else:
111 msg = msg.replace("$CONTENT_LENGTH", "0")
112 msg = msg + "\r\n"
113 msg = msg + sdp
114 return msg
115
116 def create_invite(self, sdp, extra_headers=""):
117 self.inv_branch = str(random.random())
118 return self.create_req("INVITE", sdp, branch=self.inv_branch, extra_headers=extra_headers)
119
120 def create_ack(self, sdp="", extra_headers=""):
121 return self.create_req("ACK", sdp, extra_headers=extra_headers, branch=self.inv_branch)
122
123 def create_bye(self, extra_headers=""):
124 return self.create_req("BYE", "", extra_headers)
125
126 def send_msg(self, msg):
127 if (is_request(msg)):
128 self.last_request = msg.split(" ", 1)[0]
129 self.trace("============== TX MSG ============= \n" + msg)
130 self.sock.sendto(msg, 0, (self.dst_addr, self.dst_port))
131
132 def wait_msg(self, timeout):
133 endtime = time.time() + timeout
134 msg = ""
135 while time.time() < endtime:
136 readset = select([self.sock], [], [], timeout)
137 if len(readset) < 1 or not self.sock in readset[0]:
138 if len(readset) < 1:
139 print "select() returns " + str(len(readset))
140 elif not self.sock in readset[0]:
141 print "select() alien socket"
142 else:
143 print "select other error"
144 continue
145 try:
146 msg = self.sock.recv(2048)
147 except:
148 print "recv() exception: ", sys.exc_info()[0]
149 continue
150
151 if msg=="":
152 return ""
153 if self.last_request=="INVITE" and self.rem_tag=="":
154 self.rem_tag = get_tag(msg, "To")
155 self.rem_tag = self.rem_tag.rstrip("\r\n;")
156 if self.rem_tag != "":
157 self.rem_tag = ";tag=" + self.rem_tag
158 self.trace("=== rem_tag:" + self.rem_tag)
159 self.trace("=========== RX MSG ===========\n" + msg)
160 return msg
161
162 # Send request and wait for final response
163 def send_request_wait(self, msg, timeout):
164 t1 = 1.0
165 endtime = time.time() + timeout
166 resp = ""
167 code = 0
168 for i in range(0,5):
169 self.send_msg(msg)
170 resp = self.wait_msg(t1)
171 if resp!="" and is_response(resp):
172 code = get_code(resp)
173 break
174 last_resp = resp
175 while code < 200 and time.time() < endtime:
176 resp = self.wait_msg(endtime - time.time())
177 if resp != "" and is_response(resp):
178 code = get_code(resp)
179 last_resp = resp
180 elif resp=="":
181 break
182 return last_resp
183
184 def hangup(self, last_code=0):
185 self.trace("====== hangup =====")
186 if last_code!=0:
187 self.last_resp_code = last_code
188 if self.last_resp_code>0 and self.last_resp_code<200:
189 msg = self.create_req("CANCEL", "", branch=self.inv_branch, extra_headers="")
190 self.send_request_wait(msg, 5)
191 msg = self.create_ack()
192 self.send_msg(msg)
193 elif self.last_resp_code>=200 and self.last_resp_code<300:
194 msg = self.create_ack()
195 self.send_msg(msg)
196 msg = self.create_bye()
197 self.send_request_wait(msg, 5)
198 else:
199 msg = self.create_ack()
200 self.send_msg(msg)
201
202
203class SendtoCfg:
204 # Test name
205 name = ""
206 # pjsua InstanceParam
207 inst_param = None
208 # Initial SDP
209 sdp = ""
210 # Expected code
211 resp_code = 0
212 # Use TCP?
213 use_tcp = False
214 # List of RE patterns that must exist in response
215 resp_include = []
216 # List of RE patterns that must NOT exist in response
217 resp_exclude = []
218 # Constructor
219 def __init__(self, name, pjsua_args, sdp, resp_code, resp_inc=[], resp_exc=[], use_tcp=False):
220 self.sdp = sdp
221 self.resp_code = resp_code
222 self.resp_include = resp_inc
223 self.resp_exclude = resp_exc
224 self.use_tcp = use_tcp
225 self.inst_param = cfg.InstanceParam("pjsua", pjsua_args)
226