Ticket #2197: sip_2197.diff

File sip_2197.diff, 19.8 KB (added by therve, 8 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(