Ticket #2220: sip-fixes.diff
File sip-fixes.diff, 26.4 KB (added by , 16 years ago) |
---|
-
twisted/protocols/sip.py
==== Patch <sip-fixes> level 3 Source: f7f26e75-0421-0410-8811-d6d8ddf80b0f:/local/twisted:305 [local] Target: bbbe8e31-12d6-0310-92fd-ac37d47ddeeb:/trunk:18594 [mirrored] (svn://svn.twistedmatrix.com/svn/Twisted/trunk) Log: r296@antoine-ubuntu: antoine | 2006-10-30 11:33:09 +0100 creating local branch r297@antoine-ubuntu: antoine | 2006-10-30 12:16:54 +0100 bug fixes + tests r303@antoine-ubuntu: antoine | 2006-11-03 18:46:59 +0100 redo multiline header fix r304@antoine-ubuntu: antoine | 2006-11-03 18:53:30 +0100 Via parsing as class method r305@antoine-ubuntu: antoine | 2006-11-06 10:46:56 +0100 test case for unknown params in Via header === twisted/protocols/sip.py ==================================================================
94 94 488: "Not Acceptable Here", 95 95 491: "Request Pending", 96 96 493: "Undecipherable", 97 97 98 98 500: "Internal Server Error", 99 99 501: "Not Implemented", 100 100 502: "Bad Gateway", # no donut … … 102 102 504: "Server Time-out", 103 103 505: "SIP Version not supported", 104 104 513: "Message Too Large", 105 105 106 106 600: "Busy Everywhere", 107 107 603: "Decline", 108 108 604: "Does not exist anywhere", … … 167 167 m.update(":") 168 168 m.update(pszHEntity) 169 169 HA2 = m.digest().encode('hex') 170 170 171 171 m = md5.md5() 172 172 m.update(HA1) 173 173 m.update(":") … … 185 185 return hash 186 186 187 187 class Via: 188 """A SIP Via header. """188 """A SIP Via header. Unknown parameters are ignored.""" 189 189 190 190 def __init__(self, host, port=PORT, transport="UDP", ttl=None, hidden=False, 191 received=None, rport=None, branch=None, maddr=None ):191 received=None, rport=None, branch=None, maddr=None, **kargs): 192 192 self.transport = transport 193 193 self.host = host 194 194 self.port = port … … 205 205 s += ";hidden" 206 206 for n in "ttl", "branch", "maddr", "received", "rport": 207 207 value = getattr(self, n) 208 if value ==True:208 if value is True: 209 209 s += ";" + n 210 210 elif value != None: 211 211 s += ";%s=%s" % (n, value) 212 212 return s 213 213 214 215 def parseViaHeader(value): 216 """Parse a Via header, returning Via class instance.""" 217 parts = value.split(";") 218 sent, params = parts[0], parts[1:] 219 protocolinfo, by = sent.split(" ", 1) 220 by = by.strip() 221 result = {} 222 pname, pversion, transport = protocolinfo.split("/") 223 if pname != "SIP" or pversion != "2.0": 224 raise ValueError, "wrong protocol or version: %r" % value 225 result["transport"] = transport 226 if ":" in by: 227 host, port = by.split(":") 228 result["port"] = int(port) 229 result["host"] = host 230 else: 231 result["host"] = by 232 for p in params: 233 # it's the comment-striping dance! 234 p = p.strip().split(" ", 1) 235 if len(p) == 1: 236 p, comment = p[0], "" 214 def fromString(cls, value): 215 """Parse a Via header, returning Via class instance.""" 216 parts = value.split(";") 217 sent, params = parts[0], parts[1:] 218 protocolinfo, by = sent.split(" ", 1) 219 by = by.strip() 220 result = {} 221 pname, pversion, transport = protocolinfo.split("/") 222 if pname != "SIP" or pversion != "2.0": 223 raise ValueError, "wrong protocol or version: %r" % value 224 result["transport"] = transport 225 if ":" in by: 226 host, port = by.split(":") 227 result["port"] = int(port) 228 result["host"] = host 237 229 else: 238 p, comment = p 239 if p == "hidden": 240 result["hidden"] = True 241 continue 242 parts = p.split("=", 1) 243 if len(parts) == 1: 244 name, value = parts[0], True 245 else: 246 name, value = parts 247 if name in ("rport", "ttl"): 248 value = int(value) 249 result[name] = value 250 return Via(**result) 230 result["host"] = by 231 for p in params: 232 # it's the comment-striping dance! 233 p = p.strip().split(" ", 1) 234 if len(p) == 1: 235 p, comment = p[0], "" 236 else: 237 p, comment = p 238 if p == "hidden": 239 result["hidden"] = True 240 continue 241 parts = p.split("=", 1) 242 if len(parts) == 1: 243 name, value = parts[0], True 244 else: 245 name, value = parts 246 if name in ("rport", "ttl"): 247 value = int(value) 248 result[name] = value 249 return cls(**result) 251 250 251 fromString = classmethod(fromString) 252 252 253 parseViaHeader = Via.fromString 254 253 255 class URL: 254 256 """A SIP URL.""" 255 257 … … 301 303 302 304 def __str__(self): 303 305 return self.toString() 304 306 305 307 def __repr__(self): 306 308 return '<URL %s:%s@%s:%r/%s>' % (self.username, self.password, self.host, self.port, self.transport) 307 309 … … 419 421 """A SIP message.""" 420 422 421 423 length = None 422 424 423 425 def __init__(self): 424 426 self.headers = util.OrderedDict() # map name to list of values 425 427 self.body = "" 426 428 self.finished = 0 427 429 428 430 def addHeader(self, name, value): 429 431 name = name.lower() 430 432 name = longHeaders.get(name, name) … … 434 436 435 437 def bodyDataReceived(self, data): 436 438 self.body += data 437 439 438 440 def creationFinished(self): 439 441 if (self.length != None) and (self.length != len(self.body)): 440 442 raise ValueError, "wrong body length" … … 465 467 else: 466 468 self.uri = parseURL(uri) 467 469 cleanRequestURL(self.uri) 468 470 469 471 def __repr__(self): 470 472 return "<SIP Request %d:%s %s>" % (id(self), self.method, self.uri.toString()) 471 473 … … 501 503 acceptResponses = 1 502 504 acceptRequests = 1 503 505 state = "firstline" # or "headers", "body" or "invalid" 504 506 505 507 debug = 0 506 508 507 509 def __init__(self, messageReceivedCallback): 508 510 self.messageReceived = messageReceivedCallback 509 511 self.reset() … … 513 515 self.length = None # body length 514 516 self.bodyReceived = 0 # how much of the body we received 515 517 self.message = None 518 self.header = None 516 519 self.setLineMode(remainingData) 517 520 518 521 def invalidMessage(self): 519 522 self.state = "invalid" 520 523 self.setRawMode() 521 524 522 525 def dataDone(self): 523 526 # clear out any buffered data that may be hanging around 524 527 self.clearLineBuffer() … … 530 533 if self.length == None: 531 534 # no content-length header, so end of data signals message done 532 535 self.messageDone() 533 elif self.length <self.bodyReceived:536 elif self.length > self.bodyReceived: 534 537 # aborted in the middle 535 538 self.reset() 536 539 else: 537 540 # we have enough data and message wasn't finished? something is wrong 538 541 raise RuntimeError, "this should never happen" 539 542 540 543 def dataReceived(self, data): 541 544 try: 542 545 basic.LineReceiver.dataReceived(self, data) 543 546 except: 544 547 log.err() 545 548 self.invalidMessage() 546 549 547 550 def handleFirstLine(self, line): 548 551 """Expected to create self.message.""" 549 552 raise NotImplementedError 550 553 551 554 def lineLengthExceeded(self, line): 552 555 self.invalidMessage() 553 556 554 557 def lineReceived(self, line): 555 558 if self.state == "firstline": 556 559 while line.startswith("\n") or line.startswith("\r"): … … 580 583 else: 581 584 assert self.state == "headers" 582 585 if line: 583 # XXX support multi-line headers 584 try: 585 name, value = line.split(":", 1) 586 except ValueError: 587 self.invalidMessage() 588 return 589 self.message.addHeader(name, value.lstrip()) 590 if name.lower() == "content-length": 586 # multi-line header 587 if line.startswith(" ") or line.startswith("\t"): 588 name, value = self.header 589 self.header = name, (value + line.lstrip()) 590 else: 591 # new header 592 if self.header: 593 self.message.addHeader(*self.header) 594 self.header = None 591 595 try: 592 self.length = int(value.lstrip())596 name, value = line.split(":", 1) 593 597 except ValueError: 594 598 self.invalidMessage() 595 599 return 600 self.header = name, value.lstrip() 601 # XXX we assume content-length won't be multiline 602 if name.lower() == "content-length": 603 try: 604 self.length = int(value.lstrip()) 605 except ValueError: 606 self.invalidMessage() 607 return 596 608 else: 597 609 # CRLF, we now have message body until self.length bytes, 598 610 # or if no length was given, until there is no more data 599 611 # from the connection sending us data. 600 612 self.state = "body" 613 if self.header: 614 self.message.addHeader(*self.header) 615 self.header = None 601 616 if self.length == 0: 602 617 self.messageDone() 603 618 return … … 608 623 self.message.creationFinished() 609 624 self.messageReceived(self.message) 610 625 self.reset(remainingData) 611 626 612 627 def rawDataReceived(self, data): 613 628 assert self.state in ("body", "invalid") 614 629 if self.state == "invalid": … … 631 646 632 647 class Base(protocol.DatagramProtocol): 633 648 """Base class for SIP clients and servers.""" 634 649 635 650 PORT = PORT 636 651 debug = False 637 652 638 653 def __init__(self): 639 654 self.messages = [] 640 655 self.parser = MessagesParser(self.addMessage) … … 658 673 def _fixupNAT(self, message, (srcHost, srcPort)): 659 674 # RFC 2543 6.40.2, 660 675 senderVia = parseViaHeader(message.headers["via"][0]) 661 if senderVia.host != srcHost: 676 if senderVia.host != srcHost: 662 677 senderVia.received = srcHost 663 678 if senderVia.port != srcPort: 664 679 senderVia.rport = srcPort … … 709 724 710 725 def handle_response(self, message, addr): 711 726 """Override to define behavior for responses received. 712 727 713 728 @type message: C{Message} 714 729 @type addr: C{tuple} 715 730 """ … … 760 775 761 776 class Proxy(Base): 762 777 """SIP proxy.""" 763 778 764 779 PORT = PORT 765 780 766 781 locator = None # object implementing ILocator 767 782 768 783 def __init__(self, host=None, port=PORT): 769 784 """Create new instance. 770 785 … … 774 789 self.host = host or socket.getfqdn() 775 790 self.port = port 776 791 Base.__init__(self) 777 792 778 793 def getVia(self): 779 794 """Return value of Via header for this proxy.""" 780 795 return Via(host=self.host, port=self.port) … … 797 812 d.addErrback(lambda e: 798 813 self.deliverResponse(self.responseFromRequest(e.code, message)) 799 814 ) 800 815 801 816 def handle_request_default(self, message, (srcHost, srcPort)): 802 817 """Default request handler. 803 818 804 819 Default behaviour for OPTIONS and unknown methods for proxies 805 820 is to forward message on to the client. 806 821 … … 808 823 everything. 809 824 """ 810 825 def _mungContactHeader(uri, message): 811 message.headers['contact'][0] = uri.toString() 826 message.headers['contact'][0] = uri.toString() 812 827 return self.sendMessage(uri, message) 813 828 814 829 viaHeader = self.getVia() 815 830 if viaHeader.toString() in message.headers["via"]: 816 831 # must be a loop, so drop message … … 824 839 d = self.locator.getAddress(uri) 825 840 d.addCallback(self.sendMessage, message) 826 841 d.addErrback(self._cantForwardRequest, message) 827 842 828 843 def _cantForwardRequest(self, error, message): 829 844 error.trap(LookupError) 830 845 del message.headers["via"][0] # this'll be us 831 846 self.deliverResponse(self.responseFromRequest(404, message)) 832 847 833 848 def deliverResponse(self, responseMessage): 834 849 """Deliver response. 835 850 … … 838 853 # XXX we don't do multicast yet 839 854 host = destVia.received or destVia.host 840 855 port = destVia.rport or destVia.port or self.PORT 841 856 842 857 destAddr = URL(host=host, port=port) 843 858 self.sendMessage(destAddr, responseMessage) 844 859 … … 848 863 for name in ("via", "to", "from", "call-id", "cseq"): 849 864 response.headers[name] = request.headers.get(name, [])[:] 850 865 return response 851 866 852 867 def handle_response(self, message, addr): 853 868 """Default response handler.""" 854 869 v = parseViaHeader(message.headers["via"][0]) … … 864 879 self.gotResponse(message, addr) 865 880 return 866 881 self.deliverResponse(message) 867 882 868 883 def gotResponse(self, message, addr): 869 884 """Called with responses that are addressed at this server.""" 870 885 pass … … 872 887 class IAuthorizer(Interface): 873 888 def getChallenge(peer): 874 889 """Generate a challenge the client may respond to. 875 890 876 891 @type peer: C{tuple} 877 892 @param peer: The client's address 878 893 879 894 @rtype: C{str} 880 895 @return: The challenge string 881 896 """ 882 897 883 898 def decode(response): 884 899 """Create a credentials object from the given response. 885 900 886 901 @type response: C{str} 887 902 """ 888 903 889 904 class BasicAuthorizer: 890 905 """Authorizer for insecure Basic (base64-encoded plaintext) authentication. 891 906 892 907 This form of authentication is broken and insecure. Do not use it. 893 908 """ 894 909 895 910 implements(IAuthorizer) 896 911 897 912 def getChallenge(self, peer): 898 913 return None 899 914 900 915 def decode(self, response): 901 916 # At least one SIP client improperly pads its Base64 encoded messages 902 917 for i in range(3): … … 917 932 918 933 class DigestedCredentials(cred.credentials.UsernameHashedPassword): 919 934 """Yet Another Simple Digest-MD5 authentication scheme""" 920 935 921 936 def __init__(self, username, fields, challenges): 922 937 self.username = username 923 938 self.fields = fields 924 939 self.challenges = challenges 925 940 926 941 def checkPassword(self, password): 927 942 method = 'REGISTER' 928 943 response = self.fields.get('response') … … 937 952 if opaque not in self.challenges: 938 953 return False 939 954 del self.challenges[opaque] 940 955 941 956 user, domain = self.username.split('@', 1) 942 957 if uri is None: 943 958 uri = 'sip:' + domain … … 946 961 DigestCalcHA1(algo, user, domain, password, nonce, cnonce), 947 962 nonce, nc, cnonce, qop, method, uri, None, 948 963 ) 949 964 950 965 return expected == response 951 966 952 967 class DigestAuthorizer: 953 968 CHALLENGE_LIFETIME = 15 954 969 955 970 implements(IAuthorizer) 956 971 957 972 def __init__(self): 958 973 self.outstanding = {} 959 974 960 975 def generateNonce(self): 961 976 c = tuple([random.randrange(sys.maxint) for _ in range(3)]) 962 977 c = '%d%d%d' % c … … 975 990 'qop-options="auth"', 976 991 'algorithm="MD5"', 977 992 )) 978 993 979 994 def decode(self, response): 980 995 response = ' '.join(response.splitlines()) 981 996 parts = response.split(',') … … 1003 1018 authorizers = { 1004 1019 'digest': DigestAuthorizer(), 1005 1020 } 1006 1021 1007 1022 def __init__(self, *args, **kw): 1008 1023 Proxy.__init__(self, *args, **kw) 1009 1024 self.liveChallenges = {} 1010 1025 1011 1026 def handle_ACK_request(self, message, (host, port)): 1012 1027 # XXX 1013 1028 # ACKs are a client's way of indicating they got the last message … … 1042 1057 m.headers.setdefault('www-authenticate', []).append(value) 1043 1058 self.deliverResponse(m) 1044 1059 1045 1060 1046 1061 def login(self, message, host, port): 1047 1062 parts = message.headers['authorization'][0].split(None, 1) 1048 1063 a = self.authorizers.get(parts[0].lower()) … … 1067 1082 def _cbLogin(self, (i, a, l), message, host, port): 1068 1083 # It's stateless, matey. What a joke. 1069 1084 self.register(message, host, port) 1070 1085 1071 1086 def _ebLogin(self, failure, message, host, port): 1072 1087 failure.trap(cred.error.UnauthorizedLogin) 1073 1088 self.unauthorized(message, host, port) … … 1137 1152 """A simplistic registry for a specific domain.""" 1138 1153 1139 1154 implements(IRegistry, ILocator) 1140 1155 1141 1156 def __init__(self, domain): 1142 1157 self.domain = domain # the domain we handle registration for 1143 1158 self.users = {} # map username to (IDelayedCall for expiry, address URI) … … 1150 1165 return defer.succeed(url) 1151 1166 else: 1152 1167 return defer.fail(LookupError("no such user")) 1153 1168 1154 1169 def getRegistrationInfo(self, userURI): 1155 1170 if userURI.host != self.domain: 1156 1171 return defer.fail(LookupError("unknown domain")) … … 1159 1174 return defer.succeed(Registration(int(dc.getTime() - time.time()), url)) 1160 1175 else: 1161 1176 return defer.fail(LookupError("no such user")) 1162 1177 1163 1178 def _expireRegistration(self, username): 1164 1179 try: 1165 1180 dc, url = self.users[username] … … 1169 1184 dc.cancel() 1170 1185 del self.users[username] 1171 1186 return defer.succeed(Registration(0, url)) 1172 1187 1173 1188 def registerAddress(self, domainURL, logicalURL, physicalURL): 1174 1189 if domainURL.host != self.domain: 1175 1190 log.msg("Registration for domain we don't handle.") -
twisted/test/test_sip.py
=== twisted/test/test_sip.py ==================================================================
85 85 86 86 """.replace("\n", "\r\n") 87 87 88 # multiline headers (example from RFC 3261) 89 response_multiline = """\ 90 SIP/2.0 200 OK 91 Via: SIP/2.0/UDP server10.biloxi.com 92 ;branch=z9hG4bKnashds8;received=192.0.2.3 93 Via: SIP/2.0/UDP bigbox3.site3.atlanta.com 94 ;branch=z9hG4bK77ef4c2312983.1;received=192.0.2.2 95 Via: SIP/2.0/UDP pc33.atlanta.com 96 ;branch=z9hG4bK776asdhds ;received=192.0.2.1 97 To: Bob <sip:bob@biloxi.com>;tag=a6c85cf 98 From: Alice <sip:alice@atlanta.com>;tag=1928301774 99 Call-ID: a84b4c76e66710@pc33.atlanta.com 100 CSeq: 314159 INVITE 101 Contact: <sip:bob@192.0.2.4> 102 Content-Type: application/sdp 103 Content-Length: 0 104 \n""".replace("\n", "\r\n") 105 106 88 107 class TestRealm: 89 108 def requestAvatar(self, avatarId, mind, *interfaces): 90 109 return sip.IContact, None, lambda: None … … 155 174 self.validateMessage(l[0], "INVITE", "sip:foo", 156 175 {"from": ["mo"], "to": ["joe"], "content-length": ["4"]}, 157 176 "abcd") 158 177 159 178 def testSimpleResponse(self): 160 179 l = self.l 161 180 self.feedMessage(response1) … … 167 186 self.assertEquals(m.body, "") 168 187 self.assertEquals(m.finished, 1) 169 188 189 def testIncomplete(self): 190 # test for "aborted" request (body shorter than content-length) 191 l = self.l 192 self.feedMessage(request4[:-1]) 193 self.assertEquals(len(l), 2) 170 194 195 def testMultiLine(self): 196 l = self.l 197 self.feedMessage(response_multiline) 198 self.assertEquals(len(l), 1) 199 m = l[0] 200 self.assertEquals(m.headers['via'][0], 201 "SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bKnashds8;received=192.0.2.3") 202 self.assertEquals(m.headers['via'][2], 203 "SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds ;received=192.0.2.1") 204 205 171 206 class MessageParsingTestCase2(MessageParsingTestCase): 172 207 """Same as base class, but feed data char by char.""" 173 208 … … 208 243 self.assertEquals(v1.transport, v2.transport) 209 244 self.assertEquals(v1.host, v2.host) 210 245 self.assertEquals(v1.port, v2.port) 211 246 212 247 def testComplex(self): 213 248 s = "SIP/2.0/UDP first.example.com:4000;ttl=16;maddr=224.2.0.1 ;branch=a7c6a8dlze (Example)" 214 249 v = sip.parseViaHeader(s) … … 222 257 self.assertEquals(v.toString(), 223 258 "SIP/2.0/UDP first.example.com:4000;ttl=16;branch=a7c6a8dlze;maddr=224.2.0.1") 224 259 self.checkRoundtrip(v) 225 260 226 261 def testSimple(self): 227 262 s = "SIP/2.0/UDP example.com;hidden" 228 263 v = sip.parseViaHeader(s) … … 236 271 self.assertEquals(v.toString(), 237 272 "SIP/2.0/UDP example.com:5060;hidden") 238 273 self.checkRoundtrip(v) 239 274 275 def testOneIsNotTrue(self): 276 s = "SIP/2.0/UDP example.com;ttl=1" 277 v = sip.parseViaHeader(s) 278 self.assertEquals(v.toString().rsplit(';')[1], "ttl=1") 279 self.checkRoundtrip(v) 280 240 281 def testSimpler(self): 241 282 v = sip.Via("example.com") 242 283 self.checkRoundtrip(v) … … 253 294 self.assertEquals(v.port, 5060) 254 295 self.assertEquals(v.received, "22.13.1.5") 255 296 self.assertEquals(v.rport, 12345) 256 297 257 298 self.assertNotEquals(v.toString().find("rport=12345"), -1) 258 299 300 def testUnknownParam(self): 301 s = "SIP/2.0/UDP example.com;alias;eggs;spam=1234" 302 v = sip.parseViaHeader(s) 303 self.checkRoundtrip(v) 304 259 305 class URLTestCase(unittest.TestCase): 260 306 261 307 def testRoundtrip(self): … … 305 351 implements(sip.ILocator) 306 352 def getAddress(self, logicalURL): 307 353 return defer.fail(LookupError()) 308 309 354 355 310 356 class ProxyTestCase(unittest.TestCase): 311 357 312 358 def setUp(self): … … 314 360 self.proxy.locator = DummyLocator() 315 361 self.sent = [] 316 362 self.proxy.sendMessage = lambda dest, msg: self.sent.append((dest, msg)) 317 363 318 364 def testRequestForward(self): 319 365 r = sip.Request("INVITE", "sip:foo") 320 366 r.addHeader("via", sip.Via("1.2.3.4").toString()) … … 334 380 "SIP/2.0/UDP 1.2.3.4:5060", 335 381 "SIP/2.0/UDP 1.2.3.5:5060"]) 336 382 337 383 338 384 def testReceivedRequestForward(self): 339 385 r = sip.Request("INVITE", "sip:foo") 340 386 r.addHeader("via", sip.Via("1.2.3.4").toString()) … … 346 392 self.assertEquals(m.headers["via"], 347 393 ["SIP/2.0/UDP 127.0.0.1:5060", 348 394 "SIP/2.0/UDP 1.2.3.4:5060;received=1.1.1.1"]) 349 350 395 396 351 397 def testResponseWrongVia(self): 352 398 # first via must match proxy's address 353 399 r = sip.Response(200) 354 400 r.addHeader("via", sip.Via("foo.com").toString()) 355 401 self.proxy.datagramReceived(r.toString(), ("1.1.1.1", 5060)) 356 402 self.assertEquals(len(self.sent), 0) 357 403 358 404 def testResponseForward(self): 359 405 r = sip.Response(200) 360 406 r.addHeader("via", sip.Via("127.0.0.1").toString()) … … 365 411 self.assertEquals((dest.host, dest.port), ("client.com", 1234)) 366 412 self.assertEquals(m.code, 200) 367 413 self.assertEquals(m.headers["via"], ["SIP/2.0/UDP client.com:1234"]) 368 414 369 415 def testReceivedResponseForward(self): 370 416 r = sip.Response(200) 371 417 r.addHeader("via", sip.Via("127.0.0.1").toString()) … … 374 420 self.assertEquals(len(self.sent), 1) 375 421 dest, m = self.sent[0] 376 422 self.assertEquals((dest.host, dest.port), ("client.com", 5060)) 377 423 378 424 def testResponseToUs(self): 379 425 r = sip.Response(200) 380 426 r.addHeader("via", sip.Via("127.0.0.1").toString()) … … 385 431 m, addr = l[0] 386 432 self.assertEquals(len(m.headers.get("via", [])), 0) 387 433 self.assertEquals(m.code, 200) 388 434 389 435 def testLoop(self): 390 436 r = sip.Request("INVITE", "sip:foo") 391 r.addHeader("via", sip.Via("1.2.3.4").toString()) 437 r.addHeader("via", sip.Via("1.2.3.4").toString()) 392 438 r.addHeader("via", sip.Via("127.0.0.1").toString()) 393 439 self.proxy.datagramReceived(r.toString(), ("client.com", 5060)) 394 440 self.assertEquals(self.sent, []) … … 431 477 r.addHeader("contact", "sip:joe@client.com:1234") 432 478 r.addHeader("via", sip.Via("client.com").toString()) 433 479 self.proxy.datagramReceived(r.toString(), ("client.com", 5060)) 434 480 435 481 def unregister(self): 436 482 r = sip.Request("REGISTER", "sip:bell.example.com") 437 483 r.addHeader("to", "sip:joe@bell.example.com") … … 439 485 r.addHeader("via", sip.Via("client.com").toString()) 440 486 r.addHeader("expires", "0") 441 487 self.proxy.datagramReceived(r.toString(), ("client.com", 5060)) 442 488 443 489 def testRegister(self): 444 490 self.register() 445 491 dest, m = self.sent[0] … … 482 528 def testFailedAuthentication(self): 483 529 self.addPortal() 484 530 self.register() 485 531 486 532 self.assertEquals(len(self.registry.users), 0) 487 533 self.assertEquals(len(self.sent), 1) 488 534 dest, m = self.sent[0] … … 500 546 r.addHeader("via", sip.Via("client.com").toString()) 501 547 r.addHeader("authorization", "Basic " + "userXname:passXword".encode('base64')) 502 548 self.proxy.datagramReceived(r.toString(), ("client.com", 5060)) 503 549 504 550 self.assertEquals(len(self.registry.users), 1) 505 551 self.assertEquals(len(self.sent), 1) 506 552 dest, m = self.sent[0] 507 553 self.assertEquals(m.code, 200) 508 554 509 555 510 556 def testFailedBasicAuthentication(self): 511 557 self.addPortal() 512 558 self.proxy.authorizers = self.proxy.authorizers.copy() … … 518 564 r.addHeader("via", sip.Via("client.com").toString()) 519 565 r.addHeader("authorization", "Basic " + "userXname:password".encode('base64')) 520 566 self.proxy.datagramReceived(r.toString(), ("client.com", 5060)) 521 567 522 568 self.assertEquals(len(self.registry.users), 0) 523 569 self.assertEquals(len(self.sent), 1) 524 570 dest, m = self.sent[0] … … 546 592 d = self.proxy.locator.getAddress(url) 547 593 self.assertFailure(d, LookupError) 548 594 return d 549 595 550 596 def testNoContactLookup(self): 551 597 self.register() 552 598 url = sip.URL(username="jane", host="bell.example.com") … … 623 669 self.assertEquals(r.code, 200) 624 670 d.addCallback(check) 625 671 return d 626 627 672 673 628 674 registerRequest = """ 629 675 REGISTER sip:intarweb.us SIP/2.0\r 630 676 Via: SIP/2.0/UDP 192.168.1.100:50609\r … … 724 770 for d, uri in self.registry.users.values(): 725 771 d.cancel() 726 772 del self.proxy 727 773 728 774 def testChallenge(self): 729 775 self.proxy.datagramReceived(registerRequest, ("127.0.0.1", 5632)) 730 776 self.assertEquals(