Ticket #2197: sip_2197.diff

File sip_2197.diff, 19.8 KB (added by therve, 10 years ago)
  • 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(":")
     
    266266        self.tag = tag
    267267        self.ttl = ttl
    268268        self.maddr = maddr
    269         if other == None:
     269        if other is None:
    270270            self.other = []
    271271        else:
    272272            self.other = other
    273         if headers == None:
     273        if headers is None:
    274274            self.headers = {}
    275275        else:
    276276            self.headers = headers
     
    301301
    302302    def __str__(self):
    303303        return self.toString()
    304    
     304
    305305    def __repr__(self):
    306306        return '<URL %s:%s@%s:%r/%s>' % (self.username, self.password, self.host, self.port, self.transport)
    307307
     
    419419    """A SIP message."""
    420420
    421421    length = None
    422    
     422
    423423    def __init__(self):
    424424        self.headers = util.OrderedDict() # map name to list of values
    425425        self.body = ""
    426426        self.finished = 0
    427    
     427
    428428    def addHeader(self, name, value):
    429429        name = name.lower()
    430430        name = longHeaders.get(name, name)
     
    434434
    435435    def bodyDataReceived(self, data):
    436436        self.body += data
    437    
     437
    438438    def creationFinished(self):
    439439        if (self.length != None) and (self.length != len(self.body)):
    440440            raise ValueError, "wrong body length"
     
    465465        else:
    466466            self.uri = parseURL(uri)
    467467            cleanRequestURL(self.uri)
    468    
     468
    469469    def __repr__(self):
    470470        return "<SIP Request %d:%s %s>" % (id(self), self.method, self.uri.toString())
    471471
     
    479479    def __init__(self, code, phrase=None, version="SIP/2.0"):
    480480        Message.__init__(self)
    481481        self.code = code
    482         if phrase == None:
     482        if phrase is None:
    483483            phrase = statusCodes[code]
    484484        self.phrase = phrase
    485485
     
    501501    acceptResponses = 1
    502502    acceptRequests = 1
    503503    state = "firstline" # or "headers", "body" or "invalid"
    504    
     504
    505505    debug = 0
    506    
     506
    507507    def __init__(self, messageReceivedCallback):
    508508        self.messageReceived = messageReceivedCallback
    509509        self.reset()
     
    514514        self.bodyReceived = 0 # how much of the body we received
    515515        self.message = None
    516516        self.setLineMode(remainingData)
    517    
     517
    518518    def invalidMessage(self):
    519519        self.state = "invalid"
    520520        self.setRawMode()
    521    
     521
    522522    def dataDone(self):
    523523        # clear out any buffered data that may be hanging around
    524524        self.clearLineBuffer()
     
    527527        if self.state != "body":
    528528            self.reset()
    529529            return
    530         if self.length == None:
     530        if self.length is None:
    531531            # no content-length header, so end of data signals message done
    532532            self.messageDone()
    533         elif self.length < self.bodyReceived:
     533        elif self.length > self.bodyReceived:
    534534            # aborted in the middle
    535535            self.reset()
    536536        else:
    537537            # we have enough data and message wasn't finished? something is wrong
    538538            raise RuntimeError, "this should never happen"
    539    
     539
    540540    def dataReceived(self, data):
    541541        try:
    542542            basic.LineReceiver.dataReceived(self, data)
    543543        except:
    544544            log.err()
    545545            self.invalidMessage()
    546    
     546
    547547    def handleFirstLine(self, line):
    548548        """Expected to create self.message."""
    549549        raise NotImplementedError
    550550
    551551    def lineLengthExceeded(self, line):
    552552        self.invalidMessage()
    553    
     553
    554554    def lineReceived(self, line):
    555555        if self.state == "firstline":
    556556            while line.startswith("\n") or line.startswith("\r"):
     
    608608        self.message.creationFinished()
    609609        self.messageReceived(self.message)
    610610        self.reset(remainingData)
    611    
     611
    612612    def rawDataReceived(self, data):
    613613        assert self.state in ("body", "invalid")
    614614        if self.state == "invalid":
    615615            return
    616         if self.length == None:
     616        if self.length is None:
    617617            self.message.bodyDataReceived(data)
    618618        else:
    619619            dataLen = len(data)
     
    631631
    632632class Base(protocol.DatagramProtocol):
    633633    """Base class for SIP clients and servers."""
    634    
     634
    635635    PORT = PORT
    636636    debug = False
    637    
     637
    638638    def __init__(self):
    639639        self.messages = []
    640640        self.parser = MessagesParser(self.addMessage)
     
    658658    def _fixupNAT(self, message, (srcHost, srcPort)):
    659659        # RFC 2543 6.40.2,
    660660        senderVia = parseViaHeader(message.headers["via"][0])
    661         if senderVia.host != srcHost:           
     661        if senderVia.host != srcHost:
    662662            senderVia.received = srcHost
    663663            if senderVia.port != srcPort:
    664664                senderVia.rport = srcPort
     
    709709
    710710    def handle_response(self, message, addr):
    711711        """Override to define behavior for responses received.
    712        
     712
    713713        @type message: C{Message}
    714714        @type addr: C{tuple}
    715715        """
     
    760760
    761761class Proxy(Base):
    762762    """SIP proxy."""
    763    
     763
    764764    PORT = PORT
    765765
    766766    locator = None # object implementing ILocator
    767    
     767
    768768    def __init__(self, host=None, port=PORT):
    769769        """Create new instance.
    770770
     
    774774        self.host = host or socket.getfqdn()
    775775        self.port = port
    776776        Base.__init__(self)
    777        
     777
    778778    def getVia(self):
    779779        """Return value of Via header for this proxy."""
    780780        return Via(host=self.host, port=self.port)
     
    797797                d.addErrback(lambda e:
    798798                    self.deliverResponse(self.responseFromRequest(e.code, message))
    799799                )
    800        
     800
    801801    def handle_request_default(self, message, (srcHost, srcPort)):
    802802        """Default request handler.
    803        
     803
    804804        Default behaviour for OPTIONS and unknown methods for proxies
    805805        is to forward message on to the client.
    806806
     
    808808        everything.
    809809        """
    810810        def _mungContactHeader(uri, message):
    811             message.headers['contact'][0] = uri.toString()           
     811            message.headers['contact'][0] = uri.toString()
    812812            return self.sendMessage(uri, message)
    813        
     813
    814814        viaHeader = self.getVia()
    815815        if viaHeader.toString() in message.headers["via"]:
    816816            # must be a loop, so drop message
     
    824824        d = self.locator.getAddress(uri)
    825825        d.addCallback(self.sendMessage, message)
    826826        d.addErrback(self._cantForwardRequest, message)
    827    
     827
    828828    def _cantForwardRequest(self, error, message):
    829829        error.trap(LookupError)
    830830        del message.headers["via"][0] # this'll be us
    831831        self.deliverResponse(self.responseFromRequest(404, message))
    832    
     832
    833833    def deliverResponse(self, responseMessage):
    834834        """Deliver response.
    835835
     
    838838        # XXX we don't do multicast yet
    839839        host = destVia.received or destVia.host
    840840        port = destVia.rport or destVia.port or self.PORT
    841        
     841
    842842        destAddr = URL(host=host, port=port)
    843843        self.sendMessage(destAddr, responseMessage)
    844844
     
    848848        for name in ("via", "to", "from", "call-id", "cseq"):
    849849            response.headers[name] = request.headers.get(name, [])[:]
    850850        return response
    851    
     851
    852852    def handle_response(self, message, addr):
    853853        """Default response handler."""
    854854        v = parseViaHeader(message.headers["via"][0])
     
    864864            self.gotResponse(message, addr)
    865865            return
    866866        self.deliverResponse(message)
    867    
     867
    868868    def gotResponse(self, message, addr):
    869869        """Called with responses that are addressed at this server."""
    870870        pass
     
    872872class IAuthorizer(Interface):
    873873    def getChallenge(peer):
    874874        """Generate a challenge the client may respond to.
    875        
     875
    876876        @type peer: C{tuple}
    877877        @param peer: The client's address
    878        
     878
    879879        @rtype: C{str}
    880880        @return: The challenge string
    881881        """
    882    
     882
    883883    def decode(response):
    884884        """Create a credentials object from the given response.
    885        
     885
    886886        @type response: C{str}
    887887        """
    888  
     888
    889889class BasicAuthorizer:
    890890    """Authorizer for insecure Basic (base64-encoded plaintext) authentication.
    891    
     891
    892892    This form of authentication is broken and insecure.  Do not use it.
    893893    """
    894894
    895895    implements(IAuthorizer)
    896    
     896
    897897    def getChallenge(self, peer):
    898898        return None
    899    
     899
    900900    def decode(self, response):
    901901        # At least one SIP client improperly pads its Base64 encoded messages
    902902        for i in range(3):
     
    917917
    918918class DigestedCredentials(cred.credentials.UsernameHashedPassword):
    919919    """Yet Another Simple Digest-MD5 authentication scheme"""
    920    
     920
    921921    def __init__(self, username, fields, challenges):
    922922        self.username = username
    923923        self.fields = fields
    924924        self.challenges = challenges
    925    
     925
    926926    def checkPassword(self, password):
    927927        method = 'REGISTER'
    928928        response = self.fields.get('response')
     
    937937        if opaque not in self.challenges:
    938938            return False
    939939        del self.challenges[opaque]
    940        
     940
    941941        user, domain = self.username.split('@', 1)
    942942        if uri is None:
    943943            uri = 'sip:' + domain
     
    946946            DigestCalcHA1(algo, user, domain, password, nonce, cnonce),
    947947            nonce, nc, cnonce, qop, method, uri, None,
    948948        )
    949        
     949
    950950        return expected == response
    951951
    952952class DigestAuthorizer:
    953953    CHALLENGE_LIFETIME = 15
    954    
     954
    955955    implements(IAuthorizer)
    956    
     956
    957957    def __init__(self):
    958958        self.outstanding = {}
    959    
     959
    960960    def generateNonce(self):
    961961        c = tuple([random.randrange(sys.maxint) for _ in range(3)])
    962962        c = '%d%d%d' % c
     
    975975            'qop-options="auth"',
    976976            'algorithm="MD5"',
    977977        ))
    978        
     978
    979979    def decode(self, response):
    980980        response = ' '.join(response.splitlines())
    981981        parts = response.split(',')
     
    10031003    authorizers = {
    10041004        'digest': DigestAuthorizer(),
    10051005    }
    1006    
     1006
    10071007    def __init__(self, *args, **kw):
    10081008        Proxy.__init__(self, *args, **kw)
    10091009        self.liveChallenges = {}
    1010        
     1010
    10111011    def handle_ACK_request(self, message, (host, port)):
    10121012        # XXX
    10131013        # ACKs are a client's way of indicating they got the last message
     
    10421042            m.headers.setdefault('www-authenticate', []).append(value)
    10431043        self.deliverResponse(m)
    10441044
    1045  
     1045
    10461046    def login(self, message, host, port):
    10471047        parts = message.headers['authorization'][0].split(None, 1)
    10481048        a = self.authorizers.get(parts[0].lower())
     
    10671067    def _cbLogin(self, (i, a, l), message, host, port):
    10681068        # It's stateless, matey.  What a joke.
    10691069        self.register(message, host, port)
    1070    
     1070
    10711071    def _ebLogin(self, failure, message, host, port):
    10721072        failure.trap(cred.error.UnauthorizedLogin)
    10731073        self.unauthorized(message, host, port)
     
    11371137    """A simplistic registry for a specific domain."""
    11381138
    11391139    implements(IRegistry, ILocator)
    1140    
     1140
    11411141    def __init__(self, domain):
    11421142        self.domain = domain # the domain we handle registration for
    11431143        self.users = {} # map username to (IDelayedCall for expiry, address URI)
     
    11501150            return defer.succeed(url)
    11511151        else:
    11521152            return defer.fail(LookupError("no such user"))
    1153            
     1153
    11541154    def getRegistrationInfo(self, userURI):
    11551155        if userURI.host != self.domain:
    11561156            return defer.fail(LookupError("unknown domain"))
     
    11591159            return defer.succeed(Registration(int(dc.getTime() - time.time()), url))
    11601160        else:
    11611161            return defer.fail(LookupError("no such user"))
    1162        
     1162
    11631163    def _expireRegistration(self, username):
    11641164        try:
    11651165            dc, url = self.users[username]
     
    11691169            dc.cancel()
    11701170            del self.users[username]
    11711171        return defer.succeed(Registration(0, url))
    1172    
     1172
    11731173    def registerAddress(self, domainURL, logicalURL, physicalURL):
    11741174        if domainURL.host != self.domain:
    11751175            log.msg("Registration for domain we don't handle.")
  • twisted/test/test_sip.py

     
    155155        self.validateMessage(l[0], "INVITE", "sip:foo",
    156156                             {"from": ["mo"], "to": ["joe"], "content-length": ["4"]},
    157157                             "abcd")
    158        
     158
    159159    def testSimpleResponse(self):
    160160        l = self.l
    161161        self.feedMessage(response1)
     
    167167        self.assertEquals(m.body, "")
    168168        self.assertEquals(m.finished, 1)
    169169
     170    def testIncomplete(self):
     171        # test for "aborted" request (body shorter than content-length)
     172        l = self.l
     173        self.feedMessage(request4[:-1])
     174        self.assertEquals(len(l), 2)
    170175
     176
    171177class MessageParsingTestCase2(MessageParsingTestCase):
    172178    """Same as base class, but feed data char by char."""
    173179
     
    208214        self.assertEquals(v1.transport, v2.transport)
    209215        self.assertEquals(v1.host, v2.host)
    210216        self.assertEquals(v1.port, v2.port)
    211    
     217
    212218    def testComplex(self):
    213219        s = "SIP/2.0/UDP first.example.com:4000;ttl=16;maddr=224.2.0.1 ;branch=a7c6a8dlze (Example)"
    214220        v = sip.parseViaHeader(s)
     
    222228        self.assertEquals(v.toString(),
    223229                          "SIP/2.0/UDP first.example.com:4000;ttl=16;branch=a7c6a8dlze;maddr=224.2.0.1")
    224230        self.checkRoundtrip(v)
    225    
     231
    226232    def testSimple(self):
    227233        s = "SIP/2.0/UDP example.com;hidden"
    228234        v = sip.parseViaHeader(s)
     
    236242        self.assertEquals(v.toString(),
    237243                          "SIP/2.0/UDP example.com:5060;hidden")
    238244        self.checkRoundtrip(v)
    239    
     245
    240246    def testSimpler(self):
    241247        v = sip.Via("example.com")
    242248        self.checkRoundtrip(v)
     
    253259        self.assertEquals(v.port, 5060)
    254260        self.assertEquals(v.received, "22.13.1.5")
    255261        self.assertEquals(v.rport, 12345)
    256        
     262
    257263        self.assertNotEquals(v.toString().find("rport=12345"), -1)
    258264
    259265class URLTestCase(unittest.TestCase):
     
    305311    implements(sip.ILocator)
    306312    def getAddress(self, logicalURL):
    307313        return defer.fail(LookupError())
    308    
    309314
     315
    310316class ProxyTestCase(unittest.TestCase):
    311317
    312318    def setUp(self):
     
    314320        self.proxy.locator = DummyLocator()
    315321        self.sent = []
    316322        self.proxy.sendMessage = lambda dest, msg: self.sent.append((dest, msg))
    317    
     323
    318324    def testRequestForward(self):
    319325        r = sip.Request("INVITE", "sip:foo")
    320326        r.addHeader("via", sip.Via("1.2.3.4").toString())
     
    334340                           "SIP/2.0/UDP 1.2.3.4:5060",
    335341                           "SIP/2.0/UDP 1.2.3.5:5060"])
    336342
    337    
     343
    338344    def testReceivedRequestForward(self):
    339345        r = sip.Request("INVITE", "sip:foo")
    340346        r.addHeader("via", sip.Via("1.2.3.4").toString())
     
    346352        self.assertEquals(m.headers["via"],
    347353                          ["SIP/2.0/UDP 127.0.0.1:5060",
    348354                           "SIP/2.0/UDP 1.2.3.4:5060;received=1.1.1.1"])
    349        
    350355
     356
    351357    def testResponseWrongVia(self):
    352358        # first via must match proxy's address
    353359        r = sip.Response(200)
    354360        r.addHeader("via", sip.Via("foo.com").toString())
    355361        self.proxy.datagramReceived(r.toString(), ("1.1.1.1", 5060))
    356362        self.assertEquals(len(self.sent), 0)
    357    
     363
    358364    def testResponseForward(self):
    359365        r = sip.Response(200)
    360366        r.addHeader("via", sip.Via("127.0.0.1").toString())
     
    365371        self.assertEquals((dest.host, dest.port), ("client.com", 1234))
    366372        self.assertEquals(m.code, 200)
    367373        self.assertEquals(m.headers["via"], ["SIP/2.0/UDP client.com:1234"])
    368        
     374
    369375    def testReceivedResponseForward(self):
    370376        r = sip.Response(200)
    371377        r.addHeader("via", sip.Via("127.0.0.1").toString())
     
    374380        self.assertEquals(len(self.sent), 1)
    375381        dest, m = self.sent[0]
    376382        self.assertEquals((dest.host, dest.port), ("client.com", 5060))
    377        
     383
    378384    def testResponseToUs(self):
    379385        r = sip.Response(200)
    380386        r.addHeader("via", sip.Via("127.0.0.1").toString())
     
    385391        m, addr = l[0]
    386392        self.assertEquals(len(m.headers.get("via", [])), 0)
    387393        self.assertEquals(m.code, 200)
    388    
     394
    389395    def testLoop(self):
    390396        r = sip.Request("INVITE", "sip:foo")
    391         r.addHeader("via", sip.Via("1.2.3.4").toString()) 
     397        r.addHeader("via", sip.Via("1.2.3.4").toString())
    392398        r.addHeader("via", sip.Via("127.0.0.1").toString())
    393399        self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
    394400        self.assertEquals(self.sent, [])
     
    431437        r.addHeader("contact", "sip:joe@client.com:1234")
    432438        r.addHeader("via", sip.Via("client.com").toString())
    433439        self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
    434    
     440
    435441    def unregister(self):
    436442        r = sip.Request("REGISTER", "sip:bell.example.com")
    437443        r.addHeader("to", "sip:joe@bell.example.com")
     
    439445        r.addHeader("via", sip.Via("client.com").toString())
    440446        r.addHeader("expires", "0")
    441447        self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
    442    
     448
    443449    def testRegister(self):
    444450        self.register()
    445451        dest, m = self.sent[0]
     
    482488    def testFailedAuthentication(self):
    483489        self.addPortal()
    484490        self.register()
    485        
     491
    486492        self.assertEquals(len(self.registry.users), 0)
    487493        self.assertEquals(len(self.sent), 1)
    488494        dest, m = self.sent[0]
     
    500506        r.addHeader("via", sip.Via("client.com").toString())
    501507        r.addHeader("authorization", "Basic " + "userXname:passXword".encode('base64'))
    502508        self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
    503        
     509
    504510        self.assertEquals(len(self.registry.users), 1)
    505511        self.assertEquals(len(self.sent), 1)
    506512        dest, m = self.sent[0]
    507513        self.assertEquals(m.code, 200)
    508514
    509    
     515
    510516    def testFailedBasicAuthentication(self):
    511517        self.addPortal()
    512518        self.proxy.authorizers = self.proxy.authorizers.copy()
     
    518524        r.addHeader("via", sip.Via("client.com").toString())
    519525        r.addHeader("authorization", "Basic " + "userXname:password".encode('base64'))
    520526        self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
    521        
     527
    522528        self.assertEquals(len(self.registry.users), 0)
    523529        self.assertEquals(len(self.sent), 1)
    524530        dest, m = self.sent[0]
     
    546552        d = self.proxy.locator.getAddress(url)
    547553        self.assertFailure(d, LookupError)
    548554        return d
    549    
     555
    550556    def testNoContactLookup(self):
    551557        self.register()
    552558        url = sip.URL(username="jane", host="bell.example.com")
     
    623629            self.assertEquals(r.code, 200)
    624630        d.addCallback(check)
    625631        return d
    626        
    627632
     633
    628634registerRequest = """
    629635REGISTER sip:intarweb.us SIP/2.0\r
    630636Via: SIP/2.0/UDP 192.168.1.100:50609\r
     
    724730        for d, uri in self.registry.users.values():
    725731            d.cancel()
    726732        del self.proxy
    727    
     733
    728734    def testChallenge(self):
    729735        self.proxy.datagramReceived(registerRequest, ("127.0.0.1", 5632))
    730736        self.assertEquals(