Esempio n. 1
0
    def preprocessRoutingInformationForRequest(self, connectedSIPMessageToSend):
        '''
        https://tools.ietf.org/html/rfc3261#section-16.4

        '''
        # TODO - for now, skip strict route stuff in the first paragraph of 16.4
        sipMessage = connectedSIPMessageToSend.sipMessage
        requestURI = SIPURI.newParsedFrom(sipMessage.requestURI)
        maddr = requestURI.parameterNamed('maddr')
        if maddr:
            # TODO - for now, skip the maddr processing step.
            pass
        routeURIs = sipMessage.routeURIs
        if routeURIs:
            if self.sipURIMatchesUs(routeURIs[0]):
                # TODO - remove first route header, because it matches us.
                # TODO - need to test that removeFirstHeaderFieldOfClass() works correctly.  Header field tests should be written for that.
                sipMessage.removeFirstHeaderFieldOfClass(RouteSIPHeaderField)
                # TODO:  This line is a work-around for a problem that we really
                # need to address: when you hack a header or header field value, that
                # does not clear the SIPMessage's rawString.  Fixing that is not trivial,
                # considering that we observe good layering practices in the message - header - hf
                # object complex.  We will probably need to use events for that.  Don't blow this
                # off, but for now, just manually clear the rawString.
                sipMessage.clearRawString()
    def run_01_atlantaToBiloxi(self):
        self.aliceTransport.connections[0].sendString(self.aliceRequestString)
        self.assertEqual(0, len(self.aliceReceivedRequests))
        # self.assertEqual(1, len(self.aliceReceivedResponses))
        self.assertEqual(1, len(self.atlantaReceivedRequests))
        self.assertEqual(1, len(self.biloxiReceivedRequests))
        self.assertEqual(1, len(self.atlantaReceivedResponses))
        self.assertEqual(0, len(self.biloxiReceivedResponses))
        self.assertEqual(0, len(self.bobReceivedRequests))
        self.assertEqual(0, len(self.bobReceivedResponses))

        self.assertEqual(self.aliceBindAddress, self.atlantaReceivedRequests[0].connection.remoteAddress)
        self.assertEqual(self.aliceBindPort, self.atlantaReceivedRequests[0].connection.remotePort)
        self.assertEqual(self.atlantaBindAddress, self.atlantaReceivedRequests[0].connection.bindAddress)
        self.assertEqual(self.atlantaBindPort, self.atlantaReceivedRequests[0].connection.bindPort)
        atlantaReceivedRequest = self.atlantaReceivedRequests[0].sipMessage
        rURI = SIPURI.newParsedFrom(atlantaReceivedRequest.startLine.requestURI)
        self.assertEqual(self.aliceRequestString, atlantaReceivedRequest.rawString)
        self.assertEqual('INVITE', atlantaReceivedRequest.startLine.sipMethod)
        self.assertEqual(self.biloxiBindAddress, rURI.host)
        self.assertEqual(1, len(atlantaReceivedRequest.vias))
        self.assertEqual(self.aliceRequestString, atlantaReceivedRequest.rawString)
        self.assertIsNone(atlantaReceivedRequest.header.toTag)

        self.assertEqual(self.atlantaBindAddress, self.biloxiReceivedRequests[0].connection.remoteAddress)
        self.assertEqual(self.atlantaBindPort, self.biloxiReceivedRequests[0].connection.remotePort)
        self.assertEqual(self.biloxiBindAddress, self.biloxiReceivedRequests[0].connection.bindAddress)
        self.assertEqual(self.biloxiBindPort, self.biloxiReceivedRequests[0].connection.bindPort)
        biloxiReceivedRequest = self.biloxiReceivedRequests[0].sipMessage
        self.assertEqual(atlantaReceivedRequest.startLine.requestURI, biloxiReceivedRequest.startLine.requestURI)
        self.assertEqual('INVITE', biloxiReceivedRequest.startLine.sipMethod)
        self.assertEqual(2, len(biloxiReceivedRequest.vias))
        self.assertNotEqual(self.aliceRequestString, biloxiReceivedRequest.rawString)
        self.assertIsNone(biloxiReceivedRequest.header.toTag)

        self.assertEqual(self.biloxiBindAddress, self.atlantaReceivedResponses[0].connection.remoteAddress)
        self.assertEqual(self.biloxiBindPort, self.atlantaReceivedResponses[0].connection.remotePort)
        self.assertEqual(self.atlantaBindAddress, self.atlantaReceivedResponses[0].connection.bindAddress)
        self.assertEqual(self.atlantaBindPort, self.atlantaReceivedResponses[0].connection.bindPort)
        atlantaReceivedResponse = self.atlantaReceivedResponses[0].sipMessage
        self.assertIsNotNone(atlantaReceivedResponse.header.toTag)
        self.assertEqual(2, len(atlantaReceivedResponse.vias))

        self.assertEqual(self.atlantaBindAddress, self.aliceReceivedResponses[0].connection.remoteAddress)
        self.assertEqual(self.atlantaBindPort, self.aliceReceivedResponses[0].connection.remotePort)
        self.assertEqual(self.aliceBindAddress, self.aliceReceivedResponses[0].connection.bindAddress)
        self.assertEqual(self.aliceBindPort, self.aliceReceivedResponses[0].connection.bindPort)
        aliceReceivedResponse = self.aliceReceivedResponses[0].sipMessage
        self.assertIsNotNone(aliceReceivedResponse.header.toTag)
        self.assertEqual(1, len(aliceReceivedResponse.vias))

        self.assertEqual(self.aliceBindAddress, atlantaReceivedRequest.viaHeaderFields[0].host)
        # TODO: This 404 nonsense is temporary.  Alice sends to a biloxi domain via atlanta, atlanta forwards her request to biloxi,
        # Biloxi sees that it is responsible for the request, and for right now, just answers 404.
        self.assertEqual(404, self.atlantaReceivedResponses[0].sipMessage.startLine.statusCode)
        self.assertEqual(404, self.aliceReceivedResponses[0].sipMessage.startLine.statusCode)
Esempio n. 3
0
 def determineTargetForRequest(self, connectedSIPMessageToSend):
     '''
     https://tools.ietf.org/html/rfc3261#section-16.5
     Special consideration for stateless proxies explained in section 16.11
     - Choose only one target (not forking), based on time-invariant stuff.
     '''
     sipMessage = connectedSIPMessageToSend.sipMessage
     requestURI = SIPURI.newParsedFrom(sipMessage.requestURI)
     maddr = requestURI.parameterNamed('maddr')
     if maddr:
         return requestURI
     if not self.sipURIMatchesUs(requestURI):
         return requestURI
     # TODO - we are responsible for this request.  We will have a registrar
     # or location service, probably implemented using the Strategy pattern,
     # but for now, let's just make a degenerate behavior, by answering a 404.  We will
     # implement that location service later.  Also allow other developers to write their
     # own Strategy objects.
     raise SendResponseSIPEntityException(statusCodeInteger=404, reasonPhraseString='Not Found')
Esempio n. 4
0
 def validateRequest(self, receivedConnectedSIPMessage):
     '''
     https://tools.ietf.org/html/rfc3261#section-16.3
     Validate the request
          - isMalformed is False
          - Check for a merged request (i.e. 482 (Loop Detected))
          - Check the request URI scheme, to ensure that we understand it (i.e. "sip" or "sips") 416 (Unsupported URI Scheme)
          - Check Max-Forwards.  If 0, don't forward the request, 483 (Too Many Hops)
          - (optional) Loop check.  Via header with sent-by value that's already been placed into previous requests by us
             - Maybe only appropriate for stateful proxy.
          - Proxy-Require test - 420 (Bad Extension)
          - Proxy-Authorization check -
     '''
     sipMessage = receivedConnectedSIPMessage.sipMessage
     if sipMessage.isMalformed:
         # TODO - do we need to drop the connection if malformed?  What does the RFC say about that?
         raise DropMessageSIPEntityException(descriptionString='Received SIP message was malformed.')
     # TODO - we instantiate a first-class SIPURI object here.  We probably want to do that in the start line object instead.
     requestURI = SIPURI.newParsedFrom(sipMessage.requestURI)
     if requestURI.scheme not in ['sip', 'sips']:
         raise SendResponseSIPEntityException(statusCodeInteger=416, reasonPhraseString='Unsupported URI Scheme')
     if sipMessage.maxForwards is not None:
         if sipMessage.maxForwards <= 0:
             raise SendResponseSIPEntityException(statusCodeInteger=483, reasonPhraseString='Too many hops')
Esempio n. 5
0
    def forwardRequestToTarget(self, connectedSIPMessageToSend, targetURI=None, transportIDForHeader=None):
        '''
        https://tools.ietf.org/html/rfc3261#section-16.6
        Special consideration for stateless proxies explained in section 16.11
        - The unique branch id must be invariant for requests with identical headers.
        - Item 10 will send the forwarded message directly to a transportConnection, not transaction.
        '''
        sipMessage = connectedSIPMessageToSend.sipMessage
        # TODO: in progress
        # 1. Copy request
        #   (already copied it)
        # 2.  Update the Request-URI
        # TODO: need to remove any URI parameters that are not allowed in a Request URI.
        # TODO: When we set an attribute on the start line, we may need to manually mark the sip message as dirty.
        if targetURI:
            sipMessage.startLine.requestURI = targetURI.rawString
        # 3.  Update the Max-Forwards header field
        if sipMessage.maxForwards is not None:
            # TODO:  When you use -= 1 on an integer sip header field's integerValue parameter, does that work?  Need to write a test.
            sipMessage.header.maxForwardsHeaderField.integerValue -= 1
        else:
            # TODO: inserting a header field - we should make a method that inserts it using an aesthetically-pleasing order
            # relative to other header fields.  Each header field concrete subclass would have an integer sort attribute for that.
            sipMessage.header.addHeaderField(MaxForwardsSIPHeaderField.newForIntegerValue(70))
        # 4.  Optionally add a Record-route header field value
        # TODO: is this the best way to get the URI host?
        # Are record-route header fields only used for INVITE?  Should we only do this if it's an INVITE?
        #    Answer:  No.  See RFC3261 16.6  point 4
        # TODO: sip or sips scheme?  Should that be derived from the transportConnection?  For now, hard-code to 'sip'
        recordRouteURI = SIPURI.newForAttributes(host=self.transports[0].bindAddress, port=self.transports[0].bindPort, scheme='sip', parameterNamesAndValueStrings={'lr': None})
        recordRouteHeaderField = RecordRouteSIPHeaderField.newForAttributes(recordRouteURI)
        if transportIDForHeader:
            # Do we want to put that state into this header or the Via?
            #    Answer:  we want it here.  See RFC3261 16.6  point 4
            recordRouteHeaderField.parameterNamedPut('bobstackTransportID', transportIDForHeader)
        sipMessage.header.addHeaderFieldBeforeHeaderFieldsOfSameClass(recordRouteHeaderField)

        # 5.  Optionally add additional header fields
        # TODO: make the server header field a user-settable parameter.
        sipMessage.header.addHeaderField(ServerSIPHeaderField.newForValueString('BobStack'))

        # 6.  Postprocess routing information
        routeURIs = sipMessage.header.routeURIs
        if routeURIs:
            if 'lr' not in routeURIs[0].parameterNames:
                # TODO: When we set an attribute on the start line, we may need to manually mark the sip message as dirty.
                sipMessage.header.addHeaderFieldAfterHeaderFieldsOfSameClass(RouteSIPHeaderField.newForAttributes(SIPURI.newParsedFrom(sipMessage.startLine.requestURI)))
                sipMessage.startLine.requestURI = routeURIs[0].rawString
                # TODO: Is the class actually the same object, considering that we're accessing it from a different directory?  Trap for young players.
                # No, as a matter of fact, it is not the same object, and that's a problem.  We've worked around it over there,
                # but we need to do better.
                sipMessage.header.removeFirstHeaderFieldOfClass(RouteSIPHeaderField)
                uriToDetermineNextHop = sipMessage.requestURI
            else:
                uriToDetermineNextHop = routeURIs[0]
        else:
            # TODO: seriously, we need to make start lines use first-class SIPURIs.
            uriToDetermineNextHop = SIPURI.newParsedFrom(sipMessage.requestURI)

        # 7.  Determine the next-hop address, port, and transport
        nextHopConnectedTransportConnection = self.connectedTransportConnectionForSIPURI(uriToDetermineNextHop)
        if not nextHopConnectedTransportConnection:
            # TODO:  we could not connect to the next-hop.  Return some error code.  Which error code and reason phrase?  400 and could not connect are NOT correct.
            raise SendResponseSIPEntityException(statusCodeInteger=400, reasonPhraseString='Could not connect')

        # 8.  Add a Via header field value
        # TODO:  For now, don't do the loop / spiral detection stuff.
        sipMessage.header.addHeaderFieldBeforeHeaderFieldsOfSameClass(self.newViaHeaderFieldForSIPMessage(sipMessage))

        # 9.  Add a Content-Length header field if necessary
        # TODO: if the target transport is stream-oriented, e.g. TLS or TCP, and the message has no Content-Length: header field, add one.
        # TODO: So we'll need to wait until step 7 is done, to know the transport.
        if not sipMessage.header.contentLengthHeaderField:
            if nextHopConnectedTransportConnection.isStateful:
                # TODO: if the target transport is stream-oriented, e.g. TLS or TCP, and the message has no Content-Length: header field, add one.
                sipMessage.header.addHeaderField(self.newContentLengthHeaderFieldForSIPMessage(sipMessage))

        # TODO:  This line is a work-around for a problem that we really
        # need to address: when you hack a header or header field value, that
        # does not clear the SIPMessage's rawString.  Fixing that is not trivial,
        # considering that we observe good layering practices in the message - header - hf
        # object complex.  We will probably need to use events for that.  Don't blow this
        # off, but for now, just manually clear the rawString.
        sipMessage.clearRawString()

        # 10. Forward the new request
        # TODO:  exception handling?
        nextHopConnectedTransportConnection.sendMessage(sipMessage)