Ticket #2220: sip-fixes.diff

File sip-fixes.diff, 26.4 KB (added by antoine, 10 years ago)

patch for sip parsing bugs

  • 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
    ==================================================================
     
    9494    488: "Not Acceptable Here",
    9595    491: "Request Pending",
    9696    493: "Undecipherable",
    97    
     97
    9898    500: "Internal Server Error",
    9999    501: "Not Implemented",
    100100    502: "Bad Gateway", # no donut
     
    102102    504: "Server Time-out",
    103103    505: "SIP Version not supported",
    104104    513: "Message Too Large",
    105    
     105
    106106    600: "Busy Everywhere",
    107107    603: "Decline",
    108108    604: "Does not exist anywhere",
     
    167167        m.update(":")
    168168        m.update(pszHEntity)
    169169    HA2 = m.digest().encode('hex')
    170    
     170
    171171    m = md5.md5()
    172172    m.update(HA1)
    173173    m.update(":")
     
    185185    return hash
    186186
    187187class Via:
    188     """A SIP Via header."""
     188    """A SIP Via header. Unknown parameters are ignored."""
    189189
    190190    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):
    192192        self.transport = transport
    193193        self.host = host
    194194        self.port = port
     
    205205            s += ";hidden"
    206206        for n in "ttl", "branch", "maddr", "received", "rport":
    207207            value = getattr(self, n)
    208             if value == True:
     208            if value is True:
    209209                s += ";" + n
    210210            elif value != None:
    211211                s += ";%s=%s" % (n, value)
    212212        return s
    213213
    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
    237229        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)
    251250
     251    fromString = classmethod(fromString)
    252252
     253parseViaHeader = Via.fromString
     254
    253255class URL:
    254256    """A SIP URL."""
    255257
     
    301303
    302304    def __str__(self):
    303305        return self.toString()
    304    
     306
    305307    def __repr__(self):
    306308        return '<URL %s:%s@%s:%r/%s>' % (self.username, self.password, self.host, self.port, self.transport)
    307309
     
    419421    """A SIP message."""
    420422
    421423    length = None
    422    
     424
    423425    def __init__(self):
    424426        self.headers = util.OrderedDict() # map name to list of values
    425427        self.body = ""
    426428        self.finished = 0
    427    
     429
    428430    def addHeader(self, name, value):
    429431        name = name.lower()
    430432        name = longHeaders.get(name, name)
     
    434436
    435437    def bodyDataReceived(self, data):
    436438        self.body += data
    437    
     439
    438440    def creationFinished(self):
    439441        if (self.length != None) and (self.length != len(self.body)):
    440442            raise ValueError, "wrong body length"
     
    465467        else:
    466468            self.uri = parseURL(uri)
    467469            cleanRequestURL(self.uri)
    468    
     470
    469471    def __repr__(self):
    470472        return "<SIP Request %d:%s %s>" % (id(self), self.method, self.uri.toString())
    471473
     
    501503    acceptResponses = 1
    502504    acceptRequests = 1
    503505    state = "firstline" # or "headers", "body" or "invalid"
    504    
     506
    505507    debug = 0
    506    
     508
    507509    def __init__(self, messageReceivedCallback):
    508510        self.messageReceived = messageReceivedCallback
    509511        self.reset()
     
    513515        self.length = None # body length
    514516        self.bodyReceived = 0 # how much of the body we received
    515517        self.message = None
     518        self.header = None
    516519        self.setLineMode(remainingData)
    517    
     520
    518521    def invalidMessage(self):
    519522        self.state = "invalid"
    520523        self.setRawMode()
    521    
     524
    522525    def dataDone(self):
    523526        # clear out any buffered data that may be hanging around
    524527        self.clearLineBuffer()
     
    530533        if self.length == None:
    531534            # no content-length header, so end of data signals message done
    532535            self.messageDone()
    533         elif self.length < self.bodyReceived:
     536        elif self.length > self.bodyReceived:
    534537            # aborted in the middle
    535538            self.reset()
    536539        else:
    537540            # we have enough data and message wasn't finished? something is wrong
    538541            raise RuntimeError, "this should never happen"
    539    
     542
    540543    def dataReceived(self, data):
    541544        try:
    542545            basic.LineReceiver.dataReceived(self, data)
    543546        except:
    544547            log.err()
    545548            self.invalidMessage()
    546    
     549
    547550    def handleFirstLine(self, line):
    548551        """Expected to create self.message."""
    549552        raise NotImplementedError
    550553
    551554    def lineLengthExceeded(self, line):
    552555        self.invalidMessage()
    553    
     556
    554557    def lineReceived(self, line):
    555558        if self.state == "firstline":
    556559            while line.startswith("\n") or line.startswith("\r"):
     
    580583        else:
    581584            assert self.state == "headers"
    582585        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
    591595                try:
    592                     self.length = int(value.lstrip())
     596                    name, value = line.split(":", 1)
    593597                except ValueError:
    594598                    self.invalidMessage()
    595599                    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
    596608        else:
    597609            # CRLF, we now have message body until self.length bytes,
    598610            # or if no length was given, until there is no more data
    599611            # from the connection sending us data.
    600612            self.state = "body"
     613            if self.header:
     614                self.message.addHeader(*self.header)
     615                self.header = None
    601616            if self.length == 0:
    602617                self.messageDone()
    603618                return
     
    608623        self.message.creationFinished()
    609624        self.messageReceived(self.message)
    610625        self.reset(remainingData)
    611    
     626
    612627    def rawDataReceived(self, data):
    613628        assert self.state in ("body", "invalid")
    614629        if self.state == "invalid":
     
    631646
    632647class Base(protocol.DatagramProtocol):
    633648    """Base class for SIP clients and servers."""
    634    
     649
    635650    PORT = PORT
    636651    debug = False
    637    
     652
    638653    def __init__(self):
    639654        self.messages = []
    640655        self.parser = MessagesParser(self.addMessage)
     
    658673    def _fixupNAT(self, message, (srcHost, srcPort)):
    659674        # RFC 2543 6.40.2,
    660675        senderVia = parseViaHeader(message.headers["via"][0])
    661         if senderVia.host != srcHost:           
     676        if senderVia.host != srcHost:
    662677            senderVia.received = srcHost
    663678            if senderVia.port != srcPort:
    664679                senderVia.rport = srcPort
     
    709724
    710725    def handle_response(self, message, addr):
    711726        """Override to define behavior for responses received.
    712        
     727
    713728        @type message: C{Message}
    714729        @type addr: C{tuple}
    715730        """
     
    760775
    761776class Proxy(Base):
    762777    """SIP proxy."""
    763    
     778
    764779    PORT = PORT
    765780
    766781    locator = None # object implementing ILocator
    767    
     782
    768783    def __init__(self, host=None, port=PORT):
    769784        """Create new instance.
    770785
     
    774789        self.host = host or socket.getfqdn()
    775790        self.port = port
    776791        Base.__init__(self)
    777        
     792
    778793    def getVia(self):
    779794        """Return value of Via header for this proxy."""
    780795        return Via(host=self.host, port=self.port)
     
    797812                d.addErrback(lambda e:
    798813                    self.deliverResponse(self.responseFromRequest(e.code, message))
    799814                )
    800        
     815
    801816    def handle_request_default(self, message, (srcHost, srcPort)):
    802817        """Default request handler.
    803        
     818
    804819        Default behaviour for OPTIONS and unknown methods for proxies
    805820        is to forward message on to the client.
    806821
     
    808823        everything.
    809824        """
    810825        def _mungContactHeader(uri, message):
    811             message.headers['contact'][0] = uri.toString()           
     826            message.headers['contact'][0] = uri.toString()
    812827            return self.sendMessage(uri, message)
    813        
     828
    814829        viaHeader = self.getVia()
    815830        if viaHeader.toString() in message.headers["via"]:
    816831            # must be a loop, so drop message
     
    824839        d = self.locator.getAddress(uri)
    825840        d.addCallback(self.sendMessage, message)
    826841        d.addErrback(self._cantForwardRequest, message)
    827    
     842
    828843    def _cantForwardRequest(self, error, message):
    829844        error.trap(LookupError)
    830845        del message.headers["via"][0] # this'll be us
    831846        self.deliverResponse(self.responseFromRequest(404, message))
    832    
     847
    833848    def deliverResponse(self, responseMessage):
    834849        """Deliver response.
    835850
     
    838853        # XXX we don't do multicast yet
    839854        host = destVia.received or destVia.host
    840855        port = destVia.rport or destVia.port or self.PORT
    841        
     856
    842857        destAddr = URL(host=host, port=port)
    843858        self.sendMessage(destAddr, responseMessage)
    844859
     
    848863        for name in ("via", "to", "from", "call-id", "cseq"):
    849864            response.headers[name] = request.headers.get(name, [])[:]
    850865        return response
    851    
     866
    852867    def handle_response(self, message, addr):
    853868        """Default response handler."""
    854869        v = parseViaHeader(message.headers["via"][0])
     
    864879            self.gotResponse(message, addr)
    865880            return
    866881        self.deliverResponse(message)
    867    
     882
    868883    def gotResponse(self, message, addr):
    869884        """Called with responses that are addressed at this server."""
    870885        pass
     
    872887class IAuthorizer(Interface):
    873888    def getChallenge(peer):
    874889        """Generate a challenge the client may respond to.
    875        
     890
    876891        @type peer: C{tuple}
    877892        @param peer: The client's address
    878        
     893
    879894        @rtype: C{str}
    880895        @return: The challenge string
    881896        """
    882    
     897
    883898    def decode(response):
    884899        """Create a credentials object from the given response.
    885        
     900
    886901        @type response: C{str}
    887902        """
    888  
     903
    889904class BasicAuthorizer:
    890905    """Authorizer for insecure Basic (base64-encoded plaintext) authentication.
    891    
     906
    892907    This form of authentication is broken and insecure.  Do not use it.
    893908    """
    894909
    895910    implements(IAuthorizer)
    896    
     911
    897912    def getChallenge(self, peer):
    898913        return None
    899    
     914
    900915    def decode(self, response):
    901916        # At least one SIP client improperly pads its Base64 encoded messages
    902917        for i in range(3):
     
    917932
    918933class DigestedCredentials(cred.credentials.UsernameHashedPassword):
    919934    """Yet Another Simple Digest-MD5 authentication scheme"""
    920    
     935
    921936    def __init__(self, username, fields, challenges):
    922937        self.username = username
    923938        self.fields = fields
    924939        self.challenges = challenges
    925    
     940
    926941    def checkPassword(self, password):
    927942        method = 'REGISTER'
    928943        response = self.fields.get('response')
     
    937952        if opaque not in self.challenges:
    938953            return False
    939954        del self.challenges[opaque]
    940        
     955
    941956        user, domain = self.username.split('@', 1)
    942957        if uri is None:
    943958            uri = 'sip:' + domain
     
    946961            DigestCalcHA1(algo, user, domain, password, nonce, cnonce),
    947962            nonce, nc, cnonce, qop, method, uri, None,
    948963        )
    949        
     964
    950965        return expected == response
    951966
    952967class DigestAuthorizer:
    953968    CHALLENGE_LIFETIME = 15
    954    
     969
    955970    implements(IAuthorizer)
    956    
     971
    957972    def __init__(self):
    958973        self.outstanding = {}
    959    
     974
    960975    def generateNonce(self):
    961976        c = tuple([random.randrange(sys.maxint) for _ in range(3)])
    962977        c = '%d%d%d' % c
     
    975990            'qop-options="auth"',
    976991            'algorithm="MD5"',
    977992        ))
    978        
     993
    979994    def decode(self, response):
    980995        response = ' '.join(response.splitlines())
    981996        parts = response.split(',')
     
    10031018    authorizers = {
    10041019        'digest': DigestAuthorizer(),
    10051020    }
    1006    
     1021
    10071022    def __init__(self, *args, **kw):
    10081023        Proxy.__init__(self, *args, **kw)
    10091024        self.liveChallenges = {}
    1010        
     1025
    10111026    def handle_ACK_request(self, message, (host, port)):
    10121027        # XXX
    10131028        # ACKs are a client's way of indicating they got the last message
     
    10421057            m.headers.setdefault('www-authenticate', []).append(value)
    10431058        self.deliverResponse(m)
    10441059
    1045  
     1060
    10461061    def login(self, message, host, port):
    10471062        parts = message.headers['authorization'][0].split(None, 1)
    10481063        a = self.authorizers.get(parts[0].lower())
     
    10671082    def _cbLogin(self, (i, a, l), message, host, port):
    10681083        # It's stateless, matey.  What a joke.
    10691084        self.register(message, host, port)
    1070    
     1085
    10711086    def _ebLogin(self, failure, message, host, port):
    10721087        failure.trap(cred.error.UnauthorizedLogin)
    10731088        self.unauthorized(message, host, port)
     
    11371152    """A simplistic registry for a specific domain."""
    11381153
    11391154    implements(IRegistry, ILocator)
    1140    
     1155
    11411156    def __init__(self, domain):
    11421157        self.domain = domain # the domain we handle registration for
    11431158        self.users = {} # map username to (IDelayedCall for expiry, address URI)
     
    11501165            return defer.succeed(url)
    11511166        else:
    11521167            return defer.fail(LookupError("no such user"))
    1153            
     1168
    11541169    def getRegistrationInfo(self, userURI):
    11551170        if userURI.host != self.domain:
    11561171            return defer.fail(LookupError("unknown domain"))
     
    11591174            return defer.succeed(Registration(int(dc.getTime() - time.time()), url))
    11601175        else:
    11611176            return defer.fail(LookupError("no such user"))
    1162        
     1177
    11631178    def _expireRegistration(self, username):
    11641179        try:
    11651180            dc, url = self.users[username]
     
    11691184            dc.cancel()
    11701185            del self.users[username]
    11711186        return defer.succeed(Registration(0, url))
    1172    
     1187
    11731188    def registerAddress(self, domainURL, logicalURL, physicalURL):
    11741189        if domainURL.host != self.domain:
    11751190            log.msg("Registration for domain we don't handle.")
  • twisted/test/test_sip.py

    === twisted/test/test_sip.py
    ==================================================================
     
    8585
    8686""".replace("\n", "\r\n")
    8787
     88# multiline headers (example from RFC 3261)
     89response_multiline = """\
     90SIP/2.0 200 OK
     91Via: SIP/2.0/UDP server10.biloxi.com
     92    ;branch=z9hG4bKnashds8;received=192.0.2.3
     93Via: SIP/2.0/UDP bigbox3.site3.atlanta.com
     94    ;branch=z9hG4bK77ef4c2312983.1;received=192.0.2.2
     95Via: SIP/2.0/UDP pc33.atlanta.com
     96    ;branch=z9hG4bK776asdhds ;received=192.0.2.1
     97To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
     98From: Alice <sip:alice@atlanta.com>;tag=1928301774
     99Call-ID: a84b4c76e66710@pc33.atlanta.com
     100CSeq: 314159 INVITE
     101Contact: <sip:bob@192.0.2.4>
     102Content-Type: application/sdp
     103Content-Length: 0
     104\n""".replace("\n", "\r\n")
     105
     106
    88107class TestRealm:
    89108    def requestAvatar(self, avatarId, mind, *interfaces):
    90109        return sip.IContact, None, lambda: None
     
    155174        self.validateMessage(l[0], "INVITE", "sip:foo",
    156175                             {"from": ["mo"], "to": ["joe"], "content-length": ["4"]},
    157176                             "abcd")
    158        
     177
    159178    def testSimpleResponse(self):
    160179        l = self.l
    161180        self.feedMessage(response1)
     
    167186        self.assertEquals(m.body, "")
    168187        self.assertEquals(m.finished, 1)
    169188
     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)
    170194
     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
    171206class MessageParsingTestCase2(MessageParsingTestCase):
    172207    """Same as base class, but feed data char by char."""
    173208
     
    208243        self.assertEquals(v1.transport, v2.transport)
    209244        self.assertEquals(v1.host, v2.host)
    210245        self.assertEquals(v1.port, v2.port)
    211    
     246
    212247    def testComplex(self):
    213248        s = "SIP/2.0/UDP first.example.com:4000;ttl=16;maddr=224.2.0.1 ;branch=a7c6a8dlze (Example)"
    214249        v = sip.parseViaHeader(s)
     
    222257        self.assertEquals(v.toString(),
    223258                          "SIP/2.0/UDP first.example.com:4000;ttl=16;branch=a7c6a8dlze;maddr=224.2.0.1")
    224259        self.checkRoundtrip(v)
    225    
     260
    226261    def testSimple(self):
    227262        s = "SIP/2.0/UDP example.com;hidden"
    228263        v = sip.parseViaHeader(s)
     
    236271        self.assertEquals(v.toString(),
    237272                          "SIP/2.0/UDP example.com:5060;hidden")
    238273        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
    240281    def testSimpler(self):
    241282        v = sip.Via("example.com")
    242283        self.checkRoundtrip(v)
     
    253294        self.assertEquals(v.port, 5060)
    254295        self.assertEquals(v.received, "22.13.1.5")
    255296        self.assertEquals(v.rport, 12345)
    256        
     297
    257298        self.assertNotEquals(v.toString().find("rport=12345"), -1)
    258299
     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
    259305class URLTestCase(unittest.TestCase):
    260306
    261307    def testRoundtrip(self):
     
    305351    implements(sip.ILocator)
    306352    def getAddress(self, logicalURL):
    307353        return defer.fail(LookupError())
    308    
    309354
     355
    310356class ProxyTestCase(unittest.TestCase):
    311357
    312358    def setUp(self):
     
    314360        self.proxy.locator = DummyLocator()
    315361        self.sent = []
    316362        self.proxy.sendMessage = lambda dest, msg: self.sent.append((dest, msg))
    317    
     363
    318364    def testRequestForward(self):
    319365        r = sip.Request("INVITE", "sip:foo")
    320366        r.addHeader("via", sip.Via("1.2.3.4").toString())
     
    334380                           "SIP/2.0/UDP 1.2.3.4:5060",
    335381                           "SIP/2.0/UDP 1.2.3.5:5060"])
    336382
    337    
     383
    338384    def testReceivedRequestForward(self):
    339385        r = sip.Request("INVITE", "sip:foo")
    340386        r.addHeader("via", sip.Via("1.2.3.4").toString())
     
    346392        self.assertEquals(m.headers["via"],
    347393                          ["SIP/2.0/UDP 127.0.0.1:5060",
    348394                           "SIP/2.0/UDP 1.2.3.4:5060;received=1.1.1.1"])
    349        
    350395
     396
    351397    def testResponseWrongVia(self):
    352398        # first via must match proxy's address
    353399        r = sip.Response(200)
    354400        r.addHeader("via", sip.Via("foo.com").toString())
    355401        self.proxy.datagramReceived(r.toString(), ("1.1.1.1", 5060))
    356402        self.assertEquals(len(self.sent), 0)
    357    
     403
    358404    def testResponseForward(self):
    359405        r = sip.Response(200)
    360406        r.addHeader("via", sip.Via("127.0.0.1").toString())
     
    365411        self.assertEquals((dest.host, dest.port), ("client.com", 1234))
    366412        self.assertEquals(m.code, 200)
    367413        self.assertEquals(m.headers["via"], ["SIP/2.0/UDP client.com:1234"])
    368        
     414
    369415    def testReceivedResponseForward(self):
    370416        r = sip.Response(200)
    371417        r.addHeader("via", sip.Via("127.0.0.1").toString())
     
    374420        self.assertEquals(len(self.sent), 1)
    375421        dest, m = self.sent[0]
    376422        self.assertEquals((dest.host, dest.port), ("client.com", 5060))
    377        
     423
    378424    def testResponseToUs(self):
    379425        r = sip.Response(200)
    380426        r.addHeader("via", sip.Via("127.0.0.1").toString())
     
    385431        m, addr = l[0]
    386432        self.assertEquals(len(m.headers.get("via", [])), 0)
    387433        self.assertEquals(m.code, 200)
    388    
     434
    389435    def testLoop(self):
    390436        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())
    392438        r.addHeader("via", sip.Via("127.0.0.1").toString())
    393439        self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
    394440        self.assertEquals(self.sent, [])
     
    431477        r.addHeader("contact", "sip:joe@client.com:1234")
    432478        r.addHeader("via", sip.Via("client.com").toString())
    433479        self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
    434    
     480
    435481    def unregister(self):
    436482        r = sip.Request("REGISTER", "sip:bell.example.com")
    437483        r.addHeader("to", "sip:joe@bell.example.com")
     
    439485        r.addHeader("via", sip.Via("client.com").toString())
    440486        r.addHeader("expires", "0")
    441487        self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
    442    
     488
    443489    def testRegister(self):
    444490        self.register()
    445491        dest, m = self.sent[0]
     
    482528    def testFailedAuthentication(self):
    483529        self.addPortal()
    484530        self.register()
    485        
     531
    486532        self.assertEquals(len(self.registry.users), 0)
    487533        self.assertEquals(len(self.sent), 1)
    488534        dest, m = self.sent[0]
     
    500546        r.addHeader("via", sip.Via("client.com").toString())
    501547        r.addHeader("authorization", "Basic " + "userXname:passXword".encode('base64'))
    502548        self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
    503        
     549
    504550        self.assertEquals(len(self.registry.users), 1)
    505551        self.assertEquals(len(self.sent), 1)
    506552        dest, m = self.sent[0]
    507553        self.assertEquals(m.code, 200)
    508554
    509    
     555
    510556    def testFailedBasicAuthentication(self):
    511557        self.addPortal()
    512558        self.proxy.authorizers = self.proxy.authorizers.copy()
     
    518564        r.addHeader("via", sip.Via("client.com").toString())
    519565        r.addHeader("authorization", "Basic " + "userXname:password".encode('base64'))
    520566        self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
    521        
     567
    522568        self.assertEquals(len(self.registry.users), 0)
    523569        self.assertEquals(len(self.sent), 1)
    524570        dest, m = self.sent[0]
     
    546592        d = self.proxy.locator.getAddress(url)
    547593        self.assertFailure(d, LookupError)
    548594        return d
    549    
     595
    550596    def testNoContactLookup(self):
    551597        self.register()
    552598        url = sip.URL(username="jane", host="bell.example.com")
     
    623669            self.assertEquals(r.code, 200)
    624670        d.addCallback(check)
    625671        return d
    626        
    627672
     673
    628674registerRequest = """
    629675REGISTER sip:intarweb.us SIP/2.0\r
    630676Via: SIP/2.0/UDP 192.168.1.100:50609\r
     
    724770        for d, uri in self.registry.users.values():
    725771            d.cancel()
    726772        del self.proxy
    727    
     773
    728774    def testChallenge(self):
    729775        self.proxy.datagramReceived(registerRequest, ("127.0.0.1", 5632))
    730776        self.assertEquals(