Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(111)

Unified Diff: third_party/twisted_8_1/twisted/protocols/sip.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/twisted_8_1/twisted/protocols/sip.py
diff --git a/third_party/twisted_8_1/twisted/protocols/sip.py b/third_party/twisted_8_1/twisted/protocols/sip.py
deleted file mode 100644
index 857c3fbb1dcff2cb8df02bcd73aeab22c01c57af..0000000000000000000000000000000000000000
--- a/third_party/twisted_8_1/twisted/protocols/sip.py
+++ /dev/null
@@ -1,1190 +0,0 @@
-# -*- test-case-name: twisted.test.test_sip -*-
-
-# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-
-"""Session Initialization Protocol.
-
-Documented in RFC 2543.
-[Superceded by 3261]
-"""
-
-# system imports
-import socket
-import random
-import time
-import md5
-import sys
-from zope.interface import implements, Interface
-
-# twisted imports
-from twisted.python import log, util
-from twisted.internet import protocol, defer, reactor
-
-from twisted import cred
-import twisted.cred.credentials
-import twisted.cred.error
-
-# sibling imports
-from twisted.protocols import basic
-
-PORT = 5060
-
-# SIP headers have short forms
-shortHeaders = {"call-id": "i",
- "contact": "m",
- "content-encoding": "e",
- "content-length": "l",
- "content-type": "c",
- "from": "f",
- "subject": "s",
- "to": "t",
- "via": "v",
- }
-
-longHeaders = {}
-for k, v in shortHeaders.items():
- longHeaders[v] = k
-del k, v
-
-statusCodes = {
- 100: "Trying",
- 180: "Ringing",
- 181: "Call Is Being Forwarded",
- 182: "Queued",
- 183: "Session Progress",
-
- 200: "OK",
-
- 300: "Multiple Choices",
- 301: "Moved Permanently",
- 302: "Moved Temporarily",
- 303: "See Other",
- 305: "Use Proxy",
- 380: "Alternative Service",
-
- 400: "Bad Request",
- 401: "Unauthorized",
- 402: "Payment Required",
- 403: "Forbidden",
- 404: "Not Found",
- 405: "Method Not Allowed",
- 406: "Not Acceptable",
- 407: "Proxy Authentication Required",
- 408: "Request Timeout",
- 409: "Conflict", # Not in RFC3261
- 410: "Gone",
- 411: "Length Required", # Not in RFC3261
- 413: "Request Entity Too Large",
- 414: "Request-URI Too Large",
- 415: "Unsupported Media Type",
- 416: "Unsupported URI Scheme",
- 420: "Bad Extension",
- 421: "Extension Required",
- 423: "Interval Too Brief",
- 480: "Temporarily Unavailable",
- 481: "Call/Transaction Does Not Exist",
- 482: "Loop Detected",
- 483: "Too Many Hops",
- 484: "Address Incomplete",
- 485: "Ambiguous",
- 486: "Busy Here",
- 487: "Request Terminated",
- 488: "Not Acceptable Here",
- 491: "Request Pending",
- 493: "Undecipherable",
-
- 500: "Internal Server Error",
- 501: "Not Implemented",
- 502: "Bad Gateway", # no donut
- 503: "Service Unavailable",
- 504: "Server Time-out",
- 505: "SIP Version not supported",
- 513: "Message Too Large",
-
- 600: "Busy Everywhere",
- 603: "Decline",
- 604: "Does not exist anywhere",
- 606: "Not Acceptable",
-}
-
-specialCases = {
- 'cseq': 'CSeq',
- 'call-id': 'Call-ID',
- 'www-authenticate': 'WWW-Authenticate',
-}
-
-def dashCapitalize(s):
- ''' Capitalize a string, making sure to treat - as a word seperator '''
- return '-'.join([ x.capitalize() for x in s.split('-')])
-
-def unq(s):
- if s[0] == s[-1] == '"':
- return s[1:-1]
- return s
-
-def DigestCalcHA1(
- pszAlg,
- pszUserName,
- pszRealm,
- pszPassword,
- pszNonce,
- pszCNonce,
-):
- m = md5.md5()
- m.update(pszUserName)
- m.update(":")
- m.update(pszRealm)
- m.update(":")
- m.update(pszPassword)
- HA1 = m.digest()
- if pszAlg == "md5-sess":
- m = md5.md5()
- m.update(HA1)
- m.update(":")
- m.update(pszNonce)
- m.update(":")
- m.update(pszCNonce)
- HA1 = m.digest()
- return HA1.encode('hex')
-
-def DigestCalcResponse(
- HA1,
- pszNonce,
- pszNonceCount,
- pszCNonce,
- pszQop,
- pszMethod,
- pszDigestUri,
- pszHEntity,
-):
- m = md5.md5()
- m.update(pszMethod)
- m.update(":")
- m.update(pszDigestUri)
- if pszQop == "auth-int":
- m.update(":")
- m.update(pszHEntity)
- HA2 = m.digest().encode('hex')
-
- m = md5.md5()
- m.update(HA1)
- m.update(":")
- m.update(pszNonce)
- m.update(":")
- if pszNonceCount and pszCNonce: # pszQop:
- m.update(pszNonceCount)
- m.update(":")
- m.update(pszCNonce)
- m.update(":")
- m.update(pszQop)
- m.update(":")
- m.update(HA2)
- hash = m.digest().encode('hex')
- return hash
-
-class Via:
- """A SIP Via header."""
-
- def __init__(self, host, port=PORT, transport="UDP", ttl=None, hidden=False,
- received=None, rport=None, branch=None, maddr=None):
- self.transport = transport
- self.host = host
- self.port = port
- self.ttl = ttl
- self.hidden = hidden
- self.received = received
- self.rport = rport
- self.branch = branch
- self.maddr = maddr
-
- def toString(self):
- s = "SIP/2.0/%s %s:%s" % (self.transport, self.host, self.port)
- if self.hidden:
- s += ";hidden"
- for n in "ttl", "branch", "maddr", "received", "rport":
- value = getattr(self, n)
- if value == True:
- s += ";" + n
- elif value != None:
- s += ";%s=%s" % (n, value)
- return s
-
-
-def parseViaHeader(value):
- """Parse a Via header, returning Via class instance."""
- parts = value.split(";")
- sent, params = parts[0], parts[1:]
- protocolinfo, by = sent.split(" ", 1)
- by = by.strip()
- result = {}
- pname, pversion, transport = protocolinfo.split("/")
- if pname != "SIP" or pversion != "2.0":
- raise ValueError, "wrong protocol or version: %r" % value
- result["transport"] = transport
- if ":" in by:
- host, port = by.split(":")
- result["port"] = int(port)
- result["host"] = host
- else:
- result["host"] = by
- for p in params:
- # it's the comment-striping dance!
- p = p.strip().split(" ", 1)
- if len(p) == 1:
- p, comment = p[0], ""
- else:
- p, comment = p
- if p == "hidden":
- result["hidden"] = True
- continue
- parts = p.split("=", 1)
- if len(parts) == 1:
- name, value = parts[0], True
- else:
- name, value = parts
- if name in ("rport", "ttl"):
- value = int(value)
- result[name] = value
- return Via(**result)
-
-
-class URL:
- """A SIP URL."""
-
- def __init__(self, host, username=None, password=None, port=None,
- transport=None, usertype=None, method=None,
- ttl=None, maddr=None, tag=None, other=None, headers=None):
- self.username = username
- self.host = host
- self.password = password
- self.port = port
- self.transport = transport
- self.usertype = usertype
- self.method = method
- self.tag = tag
- self.ttl = ttl
- self.maddr = maddr
- if other == None:
- self.other = []
- else:
- self.other = other
- if headers == None:
- self.headers = {}
- else:
- self.headers = headers
-
- def toString(self):
- l = []; w = l.append
- w("sip:")
- if self.username != None:
- w(self.username)
- if self.password != None:
- w(":%s" % self.password)
- w("@")
- w(self.host)
- if self.port != None:
- w(":%d" % self.port)
- if self.usertype != None:
- w(";user=%s" % self.usertype)
- for n in ("transport", "ttl", "maddr", "method", "tag"):
- v = getattr(self, n)
- if v != None:
- w(";%s=%s" % (n, v))
- for v in self.other:
- w(";%s" % v)
- if self.headers:
- w("?")
- w("&".join([("%s=%s" % (specialCases.get(h) or dashCapitalize(h), v)) for (h, v) in self.headers.items()]))
- return "".join(l)
-
- def __str__(self):
- return self.toString()
-
- def __repr__(self):
- return '<URL %s:%s@%s:%r/%s>' % (self.username, self.password, self.host, self.port, self.transport)
-
-
-def parseURL(url, host=None, port=None):
- """Return string into URL object.
-
- URIs are of of form 'sip:user@example.com'.
- """
- d = {}
- if not url.startswith("sip:"):
- raise ValueError("unsupported scheme: " + url[:4])
- parts = url[4:].split(";")
- userdomain, params = parts[0], parts[1:]
- udparts = userdomain.split("@", 1)
- if len(udparts) == 2:
- userpass, hostport = udparts
- upparts = userpass.split(":", 1)
- if len(upparts) == 1:
- d["username"] = upparts[0]
- else:
- d["username"] = upparts[0]
- d["password"] = upparts[1]
- else:
- hostport = udparts[0]
- hpparts = hostport.split(":", 1)
- if len(hpparts) == 1:
- d["host"] = hpparts[0]
- else:
- d["host"] = hpparts[0]
- d["port"] = int(hpparts[1])
- if host != None:
- d["host"] = host
- if port != None:
- d["port"] = port
- for p in params:
- if p == params[-1] and "?" in p:
- d["headers"] = h = {}
- p, headers = p.split("?", 1)
- for header in headers.split("&"):
- k, v = header.split("=")
- h[k] = v
- nv = p.split("=", 1)
- if len(nv) == 1:
- d.setdefault("other", []).append(p)
- continue
- name, value = nv
- if name == "user":
- d["usertype"] = value
- elif name in ("transport", "ttl", "maddr", "method", "tag"):
- if name == "ttl":
- value = int(value)
- d[name] = value
- else:
- d.setdefault("other", []).append(p)
- return URL(**d)
-
-
-def cleanRequestURL(url):
- """Clean a URL from a Request line."""
- url.transport = None
- url.maddr = None
- url.ttl = None
- url.headers = {}
-
-
-def parseAddress(address, host=None, port=None, clean=0):
- """Return (name, uri, params) for From/To/Contact header.
-
- @param clean: remove unnecessary info, usually for From and To headers.
- """
- address = address.strip()
- # simple 'sip:foo' case
- if address.startswith("sip:"):
- return "", parseURL(address, host=host, port=port), {}
- params = {}
- name, url = address.split("<", 1)
- name = name.strip()
- if name.startswith('"'):
- name = name[1:]
- if name.endswith('"'):
- name = name[:-1]
- url, paramstring = url.split(">", 1)
- url = parseURL(url, host=host, port=port)
- paramstring = paramstring.strip()
- if paramstring:
- for l in paramstring.split(";"):
- if not l:
- continue
- k, v = l.split("=")
- params[k] = v
- if clean:
- # rfc 2543 6.21
- url.ttl = None
- url.headers = {}
- url.transport = None
- url.maddr = None
- return name, url, params
-
-
-class SIPError(Exception):
- def __init__(self, code, phrase=None):
- if phrase is None:
- phrase = statusCodes[code]
- Exception.__init__(self, "SIP error (%d): %s" % (code, phrase))
- self.code = code
- self.phrase = phrase
-
-
-class RegistrationError(SIPError):
- """Registration was not possible."""
-
-
-class Message:
- """A SIP message."""
-
- length = None
-
- def __init__(self):
- self.headers = util.OrderedDict() # map name to list of values
- self.body = ""
- self.finished = 0
-
- def addHeader(self, name, value):
- name = name.lower()
- name = longHeaders.get(name, name)
- if name == "content-length":
- self.length = int(value)
- self.headers.setdefault(name,[]).append(value)
-
- def bodyDataReceived(self, data):
- self.body += data
-
- def creationFinished(self):
- if (self.length != None) and (self.length != len(self.body)):
- raise ValueError, "wrong body length"
- self.finished = 1
-
- def toString(self):
- s = "%s\r\n" % self._getHeaderLine()
- for n, vs in self.headers.items():
- for v in vs:
- s += "%s: %s\r\n" % (specialCases.get(n) or dashCapitalize(n), v)
- s += "\r\n"
- s += self.body
- return s
-
- def _getHeaderLine(self):
- raise NotImplementedError
-
-
-class Request(Message):
- """A Request for a URI"""
-
-
- def __init__(self, method, uri, version="SIP/2.0"):
- Message.__init__(self)
- self.method = method
- if isinstance(uri, URL):
- self.uri = uri
- else:
- self.uri = parseURL(uri)
- cleanRequestURL(self.uri)
-
- def __repr__(self):
- return "<SIP Request %d:%s %s>" % (id(self), self.method, self.uri.toString())
-
- def _getHeaderLine(self):
- return "%s %s SIP/2.0" % (self.method, self.uri.toString())
-
-
-class Response(Message):
- """A Response to a URI Request"""
-
- def __init__(self, code, phrase=None, version="SIP/2.0"):
- Message.__init__(self)
- self.code = code
- if phrase == None:
- phrase = statusCodes[code]
- self.phrase = phrase
-
- def __repr__(self):
- return "<SIP Response %d:%s>" % (id(self), self.code)
-
- def _getHeaderLine(self):
- return "SIP/2.0 %s %s" % (self.code, self.phrase)
-
-
-class MessagesParser(basic.LineReceiver):
- """A SIP messages parser.
-
- Expects dataReceived, dataDone repeatedly,
- in that order. Shouldn't be connected to actual transport.
- """
-
- version = "SIP/2.0"
- acceptResponses = 1
- acceptRequests = 1
- state = "firstline" # or "headers", "body" or "invalid"
-
- debug = 0
-
- def __init__(self, messageReceivedCallback):
- self.messageReceived = messageReceivedCallback
- self.reset()
-
- def reset(self, remainingData=""):
- self.state = "firstline"
- self.length = None # body length
- self.bodyReceived = 0 # how much of the body we received
- self.message = None
- self.setLineMode(remainingData)
-
- def invalidMessage(self):
- self.state = "invalid"
- self.setRawMode()
-
- def dataDone(self):
- # clear out any buffered data that may be hanging around
- self.clearLineBuffer()
- if self.state == "firstline":
- return
- if self.state != "body":
- self.reset()
- return
- if self.length == None:
- # no content-length header, so end of data signals message done
- self.messageDone()
- elif self.length < self.bodyReceived:
- # aborted in the middle
- self.reset()
- else:
- # we have enough data and message wasn't finished? something is wrong
- raise RuntimeError, "this should never happen"
-
- def dataReceived(self, data):
- try:
- basic.LineReceiver.dataReceived(self, data)
- except:
- log.err()
- self.invalidMessage()
-
- def handleFirstLine(self, line):
- """Expected to create self.message."""
- raise NotImplementedError
-
- def lineLengthExceeded(self, line):
- self.invalidMessage()
-
- def lineReceived(self, line):
- if self.state == "firstline":
- while line.startswith("\n") or line.startswith("\r"):
- line = line[1:]
- if not line:
- return
- try:
- a, b, c = line.split(" ", 2)
- except ValueError:
- self.invalidMessage()
- return
- if a == "SIP/2.0" and self.acceptResponses:
- # response
- try:
- code = int(b)
- except ValueError:
- self.invalidMessage()
- return
- self.message = Response(code, c)
- elif c == "SIP/2.0" and self.acceptRequests:
- self.message = Request(a, b)
- else:
- self.invalidMessage()
- return
- self.state = "headers"
- return
- else:
- assert self.state == "headers"
- if line:
- # XXX support multi-line headers
- try:
- name, value = line.split(":", 1)
- except ValueError:
- self.invalidMessage()
- return
- self.message.addHeader(name, value.lstrip())
- if name.lower() == "content-length":
- try:
- self.length = int(value.lstrip())
- except ValueError:
- self.invalidMessage()
- return
- else:
- # CRLF, we now have message body until self.length bytes,
- # or if no length was given, until there is no more data
- # from the connection sending us data.
- self.state = "body"
- if self.length == 0:
- self.messageDone()
- return
- self.setRawMode()
-
- def messageDone(self, remainingData=""):
- assert self.state == "body"
- self.message.creationFinished()
- self.messageReceived(self.message)
- self.reset(remainingData)
-
- def rawDataReceived(self, data):
- assert self.state in ("body", "invalid")
- if self.state == "invalid":
- return
- if self.length == None:
- self.message.bodyDataReceived(data)
- else:
- dataLen = len(data)
- expectedLen = self.length - self.bodyReceived
- if dataLen > expectedLen:
- self.message.bodyDataReceived(data[:expectedLen])
- self.messageDone(data[expectedLen:])
- return
- else:
- self.bodyReceived += dataLen
- self.message.bodyDataReceived(data)
- if self.bodyReceived == self.length:
- self.messageDone()
-
-
-class Base(protocol.DatagramProtocol):
- """Base class for SIP clients and servers."""
-
- PORT = PORT
- debug = False
-
- def __init__(self):
- self.messages = []
- self.parser = MessagesParser(self.addMessage)
-
- def addMessage(self, msg):
- self.messages.append(msg)
-
- def datagramReceived(self, data, addr):
- self.parser.dataReceived(data)
- self.parser.dataDone()
- for m in self.messages:
- self._fixupNAT(m, addr)
- if self.debug:
- log.msg("Received %r from %r" % (m.toString(), addr))
- if isinstance(m, Request):
- self.handle_request(m, addr)
- else:
- self.handle_response(m, addr)
- self.messages[:] = []
-
- def _fixupNAT(self, message, (srcHost, srcPort)):
- # RFC 2543 6.40.2,
- senderVia = parseViaHeader(message.headers["via"][0])
- if senderVia.host != srcHost:
- senderVia.received = srcHost
- if senderVia.port != srcPort:
- senderVia.rport = srcPort
- message.headers["via"][0] = senderVia.toString()
- elif senderVia.rport == True:
- senderVia.received = srcHost
- senderVia.rport = srcPort
- message.headers["via"][0] = senderVia.toString()
-
- def deliverResponse(self, responseMessage):
- """Deliver response.
-
- Destination is based on topmost Via header."""
- destVia = parseViaHeader(responseMessage.headers["via"][0])
- # XXX we don't do multicast yet
- host = destVia.received or destVia.host
- port = destVia.rport or destVia.port or self.PORT
- destAddr = URL(host=host, port=port)
- self.sendMessage(destAddr, responseMessage)
-
- def responseFromRequest(self, code, request):
- """Create a response to a request message."""
- response = Response(code)
- for name in ("via", "to", "from", "call-id", "cseq"):
- response.headers[name] = request.headers.get(name, [])[:]
-
- return response
-
- def sendMessage(self, destURL, message):
- """Send a message.
-
- @param destURL: C{URL}. This should be a *physical* URL, not a logical one.
- @param message: The message to send.
- """
- if destURL.transport not in ("udp", None):
- raise RuntimeError, "only UDP currently supported"
- if self.debug:
- log.msg("Sending %r to %r" % (message.toString(), destURL))
- self.transport.write(message.toString(), (destURL.host, destURL.port or self.PORT))
-
- def handle_request(self, message, addr):
- """Override to define behavior for requests received
-
- @type message: C{Message}
- @type addr: C{tuple}
- """
- raise NotImplementedError
-
- def handle_response(self, message, addr):
- """Override to define behavior for responses received.
-
- @type message: C{Message}
- @type addr: C{tuple}
- """
- raise NotImplementedError
-
-
-class IContact(Interface):
- """A user of a registrar or proxy"""
-
-
-class Registration:
- def __init__(self, secondsToExpiry, contactURL):
- self.secondsToExpiry = secondsToExpiry
- self.contactURL = contactURL
-
-class IRegistry(Interface):
- """Allows registration of logical->physical URL mapping."""
-
- def registerAddress(domainURL, logicalURL, physicalURL):
- """Register the physical address of a logical URL.
-
- @return: Deferred of C{Registration} or failure with RegistrationError.
- """
-
- def unregisterAddress(domainURL, logicalURL, physicalURL):
- """Unregister the physical address of a logical URL.
-
- @return: Deferred of C{Registration} or failure with RegistrationError.
- """
-
- def getRegistrationInfo(logicalURL):
- """Get registration info for logical URL.
-
- @return: Deferred of C{Registration} object or failure of LookupError.
- """
-
-
-class ILocator(Interface):
- """Allow looking up physical address for logical URL."""
-
- def getAddress(logicalURL):
- """Return physical URL of server for logical URL of user.
-
- @param logicalURL: a logical C{URL}.
- @return: Deferred which becomes URL or fails with LookupError.
- """
-
-
-class Proxy(Base):
- """SIP proxy."""
-
- PORT = PORT
-
- locator = None # object implementing ILocator
-
- def __init__(self, host=None, port=PORT):
- """Create new instance.
-
- @param host: our hostname/IP as set in Via headers.
- @param port: our port as set in Via headers.
- """
- self.host = host or socket.getfqdn()
- self.port = port
- Base.__init__(self)
-
- def getVia(self):
- """Return value of Via header for this proxy."""
- return Via(host=self.host, port=self.port)
-
- def handle_request(self, message, addr):
- # send immediate 100/trying message before processing
- #self.deliverResponse(self.responseFromRequest(100, message))
- f = getattr(self, "handle_%s_request" % message.method, None)
- if f is None:
- f = self.handle_request_default
- try:
- d = f(message, addr)
- except SIPError, e:
- self.deliverResponse(self.responseFromRequest(e.code, message))
- except:
- log.err()
- self.deliverResponse(self.responseFromRequest(500, message))
- else:
- if d is not None:
- d.addErrback(lambda e:
- self.deliverResponse(self.responseFromRequest(e.code, message))
- )
-
- def handle_request_default(self, message, (srcHost, srcPort)):
- """Default request handler.
-
- Default behaviour for OPTIONS and unknown methods for proxies
- is to forward message on to the client.
-
- Since at the moment we are stateless proxy, thats basically
- everything.
- """
- def _mungContactHeader(uri, message):
- message.headers['contact'][0] = uri.toString()
- return self.sendMessage(uri, message)
-
- viaHeader = self.getVia()
- if viaHeader.toString() in message.headers["via"]:
- # must be a loop, so drop message
- log.msg("Dropping looped message.")
- return
-
- message.headers["via"].insert(0, viaHeader.toString())
- name, uri, tags = parseAddress(message.headers["to"][0], clean=1)
-
- # this is broken and needs refactoring to use cred
- d = self.locator.getAddress(uri)
- d.addCallback(self.sendMessage, message)
- d.addErrback(self._cantForwardRequest, message)
-
- def _cantForwardRequest(self, error, message):
- error.trap(LookupError)
- del message.headers["via"][0] # this'll be us
- self.deliverResponse(self.responseFromRequest(404, message))
-
- def deliverResponse(self, responseMessage):
- """Deliver response.
-
- Destination is based on topmost Via header."""
- destVia = parseViaHeader(responseMessage.headers["via"][0])
- # XXX we don't do multicast yet
- host = destVia.received or destVia.host
- port = destVia.rport or destVia.port or self.PORT
-
- destAddr = URL(host=host, port=port)
- self.sendMessage(destAddr, responseMessage)
-
- def responseFromRequest(self, code, request):
- """Create a response to a request message."""
- response = Response(code)
- for name in ("via", "to", "from", "call-id", "cseq"):
- response.headers[name] = request.headers.get(name, [])[:]
- return response
-
- def handle_response(self, message, addr):
- """Default response handler."""
- v = parseViaHeader(message.headers["via"][0])
- if (v.host, v.port) != (self.host, self.port):
- # we got a message not intended for us?
- # XXX note this check breaks if we have multiple external IPs
- # yay for suck protocols
- log.msg("Dropping incorrectly addressed message")
- return
- del message.headers["via"][0]
- if not message.headers["via"]:
- # this message is addressed to us
- self.gotResponse(message, addr)
- return
- self.deliverResponse(message)
-
- def gotResponse(self, message, addr):
- """Called with responses that are addressed at this server."""
- pass
-
-class IAuthorizer(Interface):
- def getChallenge(peer):
- """Generate a challenge the client may respond to.
-
- @type peer: C{tuple}
- @param peer: The client's address
-
- @rtype: C{str}
- @return: The challenge string
- """
-
- def decode(response):
- """Create a credentials object from the given response.
-
- @type response: C{str}
- """
-
-class BasicAuthorizer:
- """Authorizer for insecure Basic (base64-encoded plaintext) authentication.
-
- This form of authentication is broken and insecure. Do not use it.
- """
-
- implements(IAuthorizer)
-
- def getChallenge(self, peer):
- return None
-
- def decode(self, response):
- # At least one SIP client improperly pads its Base64 encoded messages
- for i in range(3):
- try:
- creds = (response + ('=' * i)).decode('base64')
- except:
- pass
- else:
- break
- else:
- # Totally bogus
- raise SIPError(400)
- p = creds.split(':', 1)
- if len(p) == 2:
- return cred.credentials.UsernamePassword(*p)
- raise SIPError(400)
-
-
-class DigestedCredentials(cred.credentials.UsernameHashedPassword):
- """Yet Another Simple Digest-MD5 authentication scheme"""
-
- def __init__(self, username, fields, challenges):
- self.username = username
- self.fields = fields
- self.challenges = challenges
-
- def checkPassword(self, password):
- method = 'REGISTER'
- response = self.fields.get('response')
- uri = self.fields.get('uri')
- nonce = self.fields.get('nonce')
- cnonce = self.fields.get('cnonce')
- nc = self.fields.get('nc')
- algo = self.fields.get('algorithm', 'MD5')
- qop = self.fields.get('qop-options', 'auth')
- opaque = self.fields.get('opaque')
-
- if opaque not in self.challenges:
- return False
- del self.challenges[opaque]
-
- user, domain = self.username.split('@', 1)
- if uri is None:
- uri = 'sip:' + domain
-
- expected = DigestCalcResponse(
- DigestCalcHA1(algo, user, domain, password, nonce, cnonce),
- nonce, nc, cnonce, qop, method, uri, None,
- )
-
- return expected == response
-
-class DigestAuthorizer:
- CHALLENGE_LIFETIME = 15
-
- implements(IAuthorizer)
-
- def __init__(self):
- self.outstanding = {}
-
- def generateNonce(self):
- c = tuple([random.randrange(sys.maxint) for _ in range(3)])
- c = '%d%d%d' % c
- return c
-
- def generateOpaque(self):
- return str(random.randrange(sys.maxint))
-
- def getChallenge(self, peer):
- c = self.generateNonce()
- o = self.generateOpaque()
- self.outstanding[o] = c
- return ','.join((
- 'nonce="%s"' % c,
- 'opaque="%s"' % o,
- 'qop-options="auth"',
- 'algorithm="MD5"',
- ))
-
- def decode(self, response):
- response = ' '.join(response.splitlines())
- parts = response.split(',')
- auth = dict([(k.strip(), unq(v.strip())) for (k, v) in [p.split('=', 1) for p in parts]])
- try:
- username = auth['username']
- except KeyError:
- raise SIPError(401)
- try:
- return DigestedCredentials(username, auth, self.outstanding)
- except:
- raise SIPError(400)
-
-
-class RegisterProxy(Proxy):
- """A proxy that allows registration for a specific domain.
-
- Unregistered users won't be handled.
- """
-
- portal = None
-
- registry = None # should implement IRegistry
-
- authorizers = {
- 'digest': DigestAuthorizer(),
- }
-
- def __init__(self, *args, **kw):
- Proxy.__init__(self, *args, **kw)
- self.liveChallenges = {}
-
- def handle_ACK_request(self, message, (host, port)):
- # XXX
- # ACKs are a client's way of indicating they got the last message
- # Responding to them is not a good idea.
- # However, we should keep track of terminal messages and re-transmit
- # if no ACK is received.
- pass
-
- def handle_REGISTER_request(self, message, (host, port)):
- """Handle a registration request.
-
- Currently registration is not proxied.
- """
- if self.portal is None:
- # There is no portal. Let anyone in.
- self.register(message, host, port)
- else:
- # There is a portal. Check for credentials.
- if not message.headers.has_key("authorization"):
- return self.unauthorized(message, host, port)
- else:
- return self.login(message, host, port)
-
- def unauthorized(self, message, host, port):
- m = self.responseFromRequest(401, message)
- for (scheme, auth) in self.authorizers.iteritems():
- chal = auth.getChallenge((host, port))
- if chal is None:
- value = '%s realm="%s"' % (scheme.title(), self.host)
- else:
- value = '%s %s,realm="%s"' % (scheme.title(), chal, self.host)
- m.headers.setdefault('www-authenticate', []).append(value)
- self.deliverResponse(m)
-
-
- def login(self, message, host, port):
- parts = message.headers['authorization'][0].split(None, 1)
- a = self.authorizers.get(parts[0].lower())
- if a:
- try:
- c = a.decode(parts[1])
- except SIPError:
- raise
- except:
- log.err()
- self.deliverResponse(self.responseFromRequest(500, message))
- else:
- c.username += '@' + self.host
- self.portal.login(c, None, IContact
- ).addCallback(self._cbLogin, message, host, port
- ).addErrback(self._ebLogin, message, host, port
- ).addErrback(log.err
- )
- else:
- self.deliverResponse(self.responseFromRequest(501, message))
-
- def _cbLogin(self, (i, a, l), message, host, port):
- # It's stateless, matey. What a joke.
- self.register(message, host, port)
-
- def _ebLogin(self, failure, message, host, port):
- failure.trap(cred.error.UnauthorizedLogin)
- self.unauthorized(message, host, port)
-
- def register(self, message, host, port):
- """Allow all users to register"""
- name, toURL, params = parseAddress(message.headers["to"][0], clean=1)
- contact = None
- if message.headers.has_key("contact"):
- contact = message.headers["contact"][0]
-
- if message.headers.get("expires", [None])[0] == "0":
- self.unregister(message, toURL, contact)
- else:
- # XXX Check expires on appropriate URL, and pass it to registry
- # instead of having registry hardcode it.
- if contact is not None:
- name, contactURL, params = parseAddress(contact, host=host, port=port)
- d = self.registry.registerAddress(message.uri, toURL, contactURL)
- else:
- d = self.registry.getRegistrationInfo(toURL)
- d.addCallbacks(self._cbRegister, self._ebRegister,
- callbackArgs=(message,),
- errbackArgs=(message,)
- )
-
- def _cbRegister(self, registration, message):
- response = self.responseFromRequest(200, message)
- if registration.contactURL != None:
- response.addHeader("contact", registration.contactURL.toString())
- response.addHeader("expires", "%d" % registration.secondsToExpiry)
- response.addHeader("content-length", "0")
- self.deliverResponse(response)
-
- def _ebRegister(self, error, message):
- error.trap(RegistrationError, LookupError)
- # XXX return error message, and alter tests to deal with
- # this, currently tests assume no message sent on failure
-
- def unregister(self, message, toURL, contact):
- try:
- expires = int(message.headers["expires"][0])
- except ValueError:
- self.deliverResponse(self.responseFromRequest(400, message))
- else:
- if expires == 0:
- if contact == "*":
- contactURL = "*"
- else:
- name, contactURL, params = parseAddress(contact)
- d = self.registry.unregisterAddress(message.uri, toURL, contactURL)
- d.addCallback(self._cbUnregister, message
- ).addErrback(self._ebUnregister, message
- )
-
- def _cbUnregister(self, registration, message):
- msg = self.responseFromRequest(200, message)
- msg.headers.setdefault('contact', []).append(registration.contactURL.toString())
- msg.addHeader("expires", "0")
- self.deliverResponse(msg)
-
- def _ebUnregister(self, registration, message):
- pass
-
-
-class InMemoryRegistry:
- """A simplistic registry for a specific domain."""
-
- implements(IRegistry, ILocator)
-
- def __init__(self, domain):
- self.domain = domain # the domain we handle registration for
- self.users = {} # map username to (IDelayedCall for expiry, address URI)
-
- def getAddress(self, userURI):
- if userURI.host != self.domain:
- return defer.fail(LookupError("unknown domain"))
- if self.users.has_key(userURI.username):
- dc, url = self.users[userURI.username]
- return defer.succeed(url)
- else:
- return defer.fail(LookupError("no such user"))
-
- def getRegistrationInfo(self, userURI):
- if userURI.host != self.domain:
- return defer.fail(LookupError("unknown domain"))
- if self.users.has_key(userURI.username):
- dc, url = self.users[userURI.username]
- return defer.succeed(Registration(int(dc.getTime() - time.time()), url))
- else:
- return defer.fail(LookupError("no such user"))
-
- def _expireRegistration(self, username):
- try:
- dc, url = self.users[username]
- except KeyError:
- return defer.fail(LookupError("no such user"))
- else:
- dc.cancel()
- del self.users[username]
- return defer.succeed(Registration(0, url))
-
- def registerAddress(self, domainURL, logicalURL, physicalURL):
- if domainURL.host != self.domain:
- log.msg("Registration for domain we don't handle.")
- return defer.fail(RegistrationError(404))
- if logicalURL.host != self.domain:
- log.msg("Registration for domain we don't handle.")
- return defer.fail(RegistrationError(404))
- if self.users.has_key(logicalURL.username):
- dc, old = self.users[logicalURL.username]
- dc.reset(3600)
- else:
- dc = reactor.callLater(3600, self._expireRegistration, logicalURL.username)
- log.msg("Registered %s at %s" % (logicalURL.toString(), physicalURL.toString()))
- self.users[logicalURL.username] = (dc, physicalURL)
- return defer.succeed(Registration(int(dc.getTime() - time.time()), physicalURL))
-
- def unregisterAddress(self, domainURL, logicalURL, physicalURL):
- return self._expireRegistration(logicalURL.username)
« no previous file with comments | « third_party/twisted_8_1/twisted/protocols/shoutcast.py ('k') | third_party/twisted_8_1/twisted/protocols/smtp.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698