Ejemplo n.º 1
0
 def __init__(self, interest):
     if interest != None:
         self._interest = Interest(interest)
         self._nRetriesLeft = 3
     else:
         self._interest = Interest()
         self._nRetriesLeft = 0
Ejemplo n.º 2
0
Archivo: node.py Proyecto: MAHIS/PyNDN2
    def onReceivedElement(self, element):
        """
        This is called by the transport's ElementReader to process an
        entire received Data or Interest element.

        :param element: The bytes of the incoming element.
        :type element: An array type with int elements
        """
        # First, decode as Interest or Data.
        interest = None
        data = None
        decoder = TlvDecoder(element)
        if decoder.peekType(Tlv.Interest, len(element)):
            interest = Interest()
            interest.wireDecode(element, TlvWireFormat.get())
        elif decoder.peekType(Tlv.Data, len(element)):
            data = Data()
            data.wireDecode(element, TlvWireFormat.get())

        # Now process as Interest or Data.
        if interest != None:
            # Call all interest filter callbacks which match.
            for i in range(len(self._interestFilterTable)):
                entry = self._interestFilterTable[i]
                if entry.getFilter().doesMatch(interest.getName()):
                    includeFilter = True
                    # Use getcallargs to test if onInterest accepts 5 args.
                    try:
                        inspect.getcallargs(entry.getOnInterest(),
                          None, None, None, None, None)
                    except TypeError:
                        # Assume onInterest is old-style with 4 arguments.
                        includeFilter = False

                    if includeFilter:
                        try:
                            entry.getOnInterest()(
                              entry.getFilter().getPrefix(), interest,
                              entry.getFace(), entry.getInterestFilterId(),
                              entry.getFilter())
                        except:
                            logging.exception("Error in onInterest")
                    else:
                        # Old-style onInterest without the filter argument. We
                        # still pass a Face instead of Transport since Face also
                        # has a send method.
                        try:
                            entry.getOnInterest()(
                              entry.getFilter().getPrefix(), interest,
                              entry.getFace(), entry.getInterestFilterId())
                        except:
                            logging.exception("Error in onInterest")
        elif data != None:
            pendingInterests = self._extractEntriesForExpressedInterest(
              data.getName())
            for pendingInterest in pendingInterests:
                try:
                    pendingInterest.getOnData()(pendingInterest.getInterest(), data)
                except:
                    logging.exception("Error in onData")
Ejemplo n.º 3
0
    def _sendKeyInterest(self, interest, timeSlot, onEncryptedKeys, onError):
        """
        Send an interest with the given name through the face with callbacks to
          _handleCoveringKey, _handleTimeout and _handleNetworkNack.

        :param Interest interest: The interest to send.
        :param float timeSlot: The time slot, passed to _handleCoveringKey,
          _handleTimeout and _handleNetworkNack.
        :param onEncryptedKeys: The OnEncryptedKeys callback, passed to
          _handleCoveringKey, _handleTimeout and _handleNetworkNack.
        :type onEncryptedKeys: function object
        :param onError: This calls onError(errorCode, message) for an error.
        :type onError: function object
        """
        def onKey(interest, data):
            self._handleCoveringKey(interest, data, timeSlot, onEncryptedKeys,
                                    onError)

        def onTimeout(interest):
            self._handleTimeout(interest, timeSlot, onEncryptedKeys, onError)

        def onNetworkNack(interest, networkNack):
            self._handleNetworkNack(interest, networkNack, timeSlot,
                                    onEncryptedKeys, onError)

        if self._keyRetrievalLink.getDelegations().size() == 0:
            # We can use the supplied interest without copying.
            request = interest
        else:
            # Copy the supplied interest and add the Link.
            request = Interest(interest)
            # This will use a cached encoding if available.
            request.setLinkWireEncoding(self._keyRetrievalLink.wireEncode())

        self._face.expressInterest(request, onKey, onTimeout, onNetworkNack)
Ejemplo n.º 4
0
    def _sendKeyInterest(self, interest, timeSlot, onEncryptedKeys, onError):
        """
        Send an interest with the given name through the face with callbacks to
          _handleCoveringKey, _handleTimeout and _handleNetworkNack.

        :param Interest interest: The interest to send.
        :param float timeSlot: The time slot, passed to _handleCoveringKey,
          _handleTimeout and _handleNetworkNack.
        :param onEncryptedKeys: The OnEncryptedKeys callback, passed to
          _handleCoveringKey, _handleTimeout and _handleNetworkNack.
        :type onEncryptedKeys: function object
        :param onError: This calls onError(errorCode, message) for an error.
        :type onError: function object
        """

        def onKey(interest, data):
            self._handleCoveringKey(interest, data, timeSlot, onEncryptedKeys, onError)

        def onTimeout(interest):
            self._handleTimeout(interest, timeSlot, onEncryptedKeys, onError)

        def onNetworkNack(interest, networkNack):
            self._handleNetworkNack(interest, networkNack, timeSlot, onEncryptedKeys, onError)

        if self._keyRetrievalLink.getDelegations().size() == 0:
            # We can use the supplied interest without copying.
            request = interest
        else:
            # Copy the supplied interest and add the Link.
            request = Interest(interest)
            # This will use a cached encoding if available.
            request.setLinkWireEncoding(self._keyRetrievalLink.wireEncode())

        self._face.expressInterest(request, onKey, onTimeout, onNetworkNack)
Ejemplo n.º 5
0
    def _initialTimeOut(self, interest):
        """
        Initial sync interest timeout, which means there are no other publishers
        yet.
        """
        if not self._enabled:
            # Ignore callbacks after the application calls shutdown().
            return

        logging.getLogger(__name__).info("initial sync timeout")
        logging.getLogger(__name__).info("no other people")
        self._sequenceNo += 1
        if self._sequenceNo != 0:
            # Since there were no other users, we expect sequence no 0.
            raise RuntimeError(
              "ChronoSync: sequenceNo_ is not the expected value of 0 for first use.")

        tempContent = sync_state_pb2.SyncStateMsg()
        content = getattr(tempContent, "ss").add()
        content.name = self._applicationDataPrefixUri
        content.type = SyncState_UPDATE
        content.seqno.seq = self._sequenceNo
        content.seqno.session = self._sessionNo
        self._update(getattr(tempContent, "ss"))

        self._onInitialized()

        name = Name(self._applicationBroadcastPrefix)
        name.append(self._digestTree.getRoot())
        retryInterest = Interest(name)
        retryInterest.setInterestLifetimeMilliseconds(self._syncLifetime)
        self._face.expressInterest(retryInterest, self._onData, self._syncTimeout)
        logging.getLogger(__name__).info("Syncinterest expressed:")
        logging.getLogger(__name__).info("%s", name.toUri())
Ejemplo n.º 6
0
    def _onData(self, interest, data):
        """
        Process Sync Data.
        """
        if not self._enabled:
            # Ignore callbacks after the application calls shutdown().
            return

        logging.getLogger(__name__).info(
            "Sync ContentObject received in callback")
        logging.getLogger(__name__).info("name: %s", data.getName().toUri())
        # TODO: Check if this works in Python 3.
        tempContent = SyncStateMsg()
        #pylint: disable=E1103
        tempContent.ParseFromString(data.getContent().toBytes())
        #pylint: enable=E1103
        content = getattr(tempContent, "ss")
        if self._digestTree.getRoot() == "00":
            isRecovery = True
            #processing initial sync data
            self._initialOndata(content)
        else:
            self._update(content)
            if (interest.getName().size() ==
                    self._applicationBroadcastPrefix.size() + 2):
                # Assume this is a recovery interest.
                isRecovery = True
            else:
                isRecovery = False

        # Send the interests to fetch the application data.
        syncStates = []
        for i in range(len(content)):
            syncState = content[i]

            # Only report UPDATE sync states.
            if syncState.type == SyncState_UPDATE:
                if len(syncState.application_info) > 0:
                    applicationInfo = Blob(syncState.application_info, True)
                else:
                    applicationInfo = Blob()

                syncStates.append(
                    self.SyncState(syncState.name, syncState.seqno.session,
                                   syncState.seqno.seq, applicationInfo))

        try:
            self._onReceivedSyncState(syncStates, isRecovery)
        except:
            logging.exception("Error in onReceivedSyncState")

        name = Name(self._applicationBroadcastPrefix)
        name.append(self._digestTree.getRoot())
        syncInterest = Interest(name)
        syncInterest.setInterestLifetimeMilliseconds(self._syncLifetime)
        self._face.expressInterest(syncInterest, self._onData,
                                   self._syncTimeout)
        logging.getLogger(__name__).info("Syncinterest expressed:")
        logging.getLogger(__name__).info("%s", name.toUri())
Ejemplo n.º 7
0
    def _onData(self, interest, data):
        """
        Process Sync Data.
        """
        if not self._enabled:
            # Ignore callbacks after the application calls shutdown().
            return

        logging.getLogger(__name__).info(
          "Sync ContentObject received in callback")
        logging.getLogger(__name__).info(
            "name: %s", data.getName().toUri())
        # TODO: Check if this works in Python 3.
        tempContent = SyncStateMsg()
#pylint: disable=E1103
        tempContent.ParseFromString(data.getContent().toBytes())
#pylint: enable=E1103
        content = getattr(tempContent, "ss")
        if self._digestTree.getRoot() == "00":
            isRecovery = True
            #processing initial sync data
            self._initialOndata(content)
        else:
            self._update(content)
            if (interest.getName().size() ==
                self._applicationBroadcastPrefix.size() + 2):
                # Assume this is a recovery interest.
                isRecovery = True
            else:
                isRecovery = False

        # Send the interests to fetch the application data.
        syncStates = []
        for i in range(len(content)):
            syncState = content[i]

            # Only report UPDATE sync states.
            if syncState.type == SyncState_UPDATE:
                if len(syncState.application_info) > 0:
                    applicationInfo = Blob(syncState.application_info, True)
                else:
                    applicationInfo = Blob()

                syncStates.append(self.SyncState(
                  syncState.name, syncState.seqno.session,
                  syncState.seqno.seq, applicationInfo))

        try:
            self._onReceivedSyncState(syncStates, isRecovery)
        except:
            logging.exception("Error in onReceivedSyncState")

        name = Name(self._applicationBroadcastPrefix)
        name.append(self._digestTree.getRoot())
        syncInterest = Interest(name)
        syncInterest.setInterestLifetimeMilliseconds(self._syncLifetime)
        self._face.expressInterest(syncInterest, self._onData, self._syncTimeout)
        logging.getLogger(__name__).info("Syncinterest expressed:")
        logging.getLogger(__name__).info("%s", name.toUri())
Ejemplo n.º 8
0
 def _fetchNextSegment(self, originalInterest, dataName, segment):
     # Start with the original Interest to preserve any special selectors.
     interest = Interest(originalInterest)
     # Changing a field clears the nonce so that the library will
     #   generate a new one.
     interest.setMustBeFresh(False)
     interest.setName(dataName.getPrefix(-1).appendSegment(segment))
     self._face.expressInterest(interest, self._onData, self._onTimeout)
Ejemplo n.º 9
0
    def __init__(self, onReceivedSyncState, onInitialized,
      applicationDataPrefix, applicationBroadcastPrefix, sessionNo, face,
      keyChain, certificateName, syncLifetime, onRegisterFailed):
        self._onReceivedSyncState = onReceivedSyncState
        self._onInitialized = onInitialized
        self._applicationDataPrefixUri = applicationDataPrefix.toUri()
        self._applicationBroadcastPrefix = Name(applicationBroadcastPrefix)
        self._sessionNo = sessionNo
        self._face = face
        self._keyChain = keyChain
        self._certificateName = Name(certificateName)
        self._syncLifetime = syncLifetime
        self._contentCache = MemoryContentCache(face)

        self._digestLog = [] # of _DigestLogEntry
        self._digestTree = DigestTree()
        self._sequenceNo = -1
        self._enabled = True

        emptyContent = sync_state_pb2.SyncStateMsg()
        # Use getattr to avoid pylint errors.
        self._digestLog.append(self._DigestLogEntry("00", getattr(emptyContent, "ss")))

        # Register the prefix with the contentCache_ and use our own onInterest
        #   as the onDataNotFound fallback.
        self._contentCache.registerPrefix(
          self._applicationBroadcastPrefix, onRegisterFailed, self._onInterest)

        interest = Interest(self._applicationBroadcastPrefix)
        interest.getName().append("00")
        interest.setInterestLifetimeMilliseconds(1000)
        interest.setMustBeFresh(True)
        face.expressInterest(interest, self._onData, self._initialTimeOut)
        logging.getLogger(__name__).info("initial sync expressed")
        logging.getLogger(__name__).info("%s", interest.getName().toUri())
Ejemplo n.º 10
0
 def _sendRecovery(self, syncDigest):
     """
     Send Recovery Interest.
     """
     logging.getLogger(__name__).info("unknown digest: ")
     name = Name(self._applicationBroadcastPrefix)
     name.append("recovery").append(syncDigest)
     interest = Interest(name)
     interest.setInterestLifetimeMilliseconds(self._syncLifetime)
     self._face.expressInterest(interest, self._onData, self._syncTimeout)
     logging.getLogger(__name__).info("Recovery Syncinterest expressed:")
     logging.getLogger(__name__).info("%s", name.toUri())
Ejemplo n.º 11
0
 def __init__(self, transport, connectionInfo):
     self._transport = transport
     self._connectionInfo = connectionInfo
     # An array of PendintInterest
     self._pendingInterestTable = []
     # An array of RegisteredPrefix
     self._registeredPrefixTable = []
     self._ndndIdFetcherInterest = Interest(
         Name("/%C1.M.S.localhost/%C1.M.SRV/ndnd/KEY"))
     self._ndndIdFetcherInterest.setInterestLifetimeMilliseconds(4000.0)
     self._ndndId = None
     self._commandInterestGenerator = CommandInterestGenerator()
Ejemplo n.º 12
0
 def _sendRecovery(self, syncDigest):
     """
     Send Recovery Interest.
     """
     logging.getLogger(__name__).info("unknown digest: ")
     name = Name(self._applicationBroadcastPrefix)
     name.append("recovery").append(syncDigest)
     interest = Interest(name)
     interest.setInterestLifetimeMilliseconds(self._syncLifetime)
     self._face.expressInterest(interest, self._onData, self._syncTimeout)
     logging.getLogger(__name__).info("Recovery Syncinterest expressed:")
     logging.getLogger(__name__).info("%s", name.toUri())
Ejemplo n.º 13
0
    def __init__(self, interest, successCallback, failureCallback):
        super(InterestValidationState, self).__init__()

        # Make a copy.
        self._interest = Interest(interest)
        self._successCallbacks = [successCallback] # of SuccessCallback function object
        self._failureCallback = failureCallback

        if successCallback == None:
            raise ValueError("The successCallback is None")
        if self._failureCallback == None:
            raise ValueError("The failureCallback is None")
Ejemplo n.º 14
0
    def __init__(self, onReceivedSyncState, onInitialized,
      applicationDataPrefix, applicationBroadcastPrefix, sessionNo, face,
      keyChain, certificateName, syncLifetime, onRegisterFailed):
        self._onReceivedSyncState = onReceivedSyncState
        self._onInitialized = onInitialized
        self._applicationDataPrefixUri = applicationDataPrefix.toUri()
        self._applicationBroadcastPrefix = Name(applicationBroadcastPrefix)
        self._sessionNo = sessionNo
        self._face = face
        self._keyChain = keyChain
        self._certificateName = Name(certificateName)
        self._syncLifetime = syncLifetime
        self._contentCache = MemoryContentCache(face)

        self._digestLog = [] # of _DigestLogEntry
        self._digestTree = DigestTree()
        self._sequenceNo = -1
        self._enabled = True

        emptyContent = SyncStateMsg()
        # Use getattr to avoid pylint errors.
        self._digestLog.append(self._DigestLogEntry("00", getattr(emptyContent, "ss")))

        # Register the prefix with the contentCache_ and use our own onInterest
        #   as the onDataNotFound fallback.
        self._contentCache.registerPrefix(
          self._applicationBroadcastPrefix, onRegisterFailed, self._onInterest)

        interest = Interest(self._applicationBroadcastPrefix)
        interest.getName().append("00")
        interest.setInterestLifetimeMilliseconds(1000)
        interest.setMustBeFresh(True)
        face.expressInterest(interest, self._onData, self._initialTimeOut)
        logging.getLogger(__name__).info("initial sync expressed")
        logging.getLogger(__name__).info("%s", interest.getName().toUri())
Ejemplo n.º 15
0
    def publishNextSequenceNo(self, applicationInfo = None):
        """
        Increment the sequence number, create a sync message with the new
        sequence number and publish a data packet where the name is
        the applicationBroadcastPrefix + the root digest of the current digest
        tree. Then add the sync message to the digest tree and digest log which
        creates a new root digest. Finally, express an interest for the next sync
        update with the name applicationBroadcastPrefix + the new root digest.
        After this, your application should publish the content for the new
        sequence number. You can get the new sequence number with getSequenceNo().
        Note: Your application must call processEvents. Since processEvents
        modifies the internal ChronoSync data structures, your application should
        make sure that it calls processEvents in the same thread as
        publishNextSequenceNo() (which also modifies the data structures).

        :param Blob applicationInfo: (optional) This appends applicationInfo to
          the content of the sync messages. This same info is provided to the
          receiving application in the SyncState state object provided to the
          onReceivedSyncState callback.
        """
        applicationInfo = (applicationInfo if isinstance(applicationInfo, Blob)
          else Blob(applicationInfo))

        self._sequenceNo += 1

        syncMessage = SyncStateMsg()
        content = getattr(syncMessage, "ss").add()
        content.name = self._applicationDataPrefixUri
        content.type = SyncState_UPDATE
        content.seqno.seq = self._sequenceNo
        content.seqno.session = self._sessionNo
        if not applicationInfo.isNull() and applicationInfo.size() > 0:
            content.application_info = applicationInfo.toBytes()

        self._broadcastSyncState(self._digestTree.getRoot(), syncMessage)

        if not self._update(getattr(syncMessage, "ss")):
          # Since we incremented the sequence number, we expect there to be a
          #   new digest log entry.
          raise RuntimeError(
            "ChronoSync: update did not create a new digest log entry")

        # TODO: Should we have an option to not express an interest if this is the
        #   final publish of the session?
        interest = Interest(self._applicationBroadcastPrefix)
        interest.getName().append(self._digestTree.getRoot())
        interest.setInterestLifetimeMilliseconds(self._syncLifetime)
        self._face.expressInterest(interest, self._onData, self._syncTimeout)
Ejemplo n.º 16
0
 def _fetchNextSegment(self, originalInterest, dataName, segment):
     # Start with the original Interest to preserve any special selectors.
     interest = Interest(originalInterest)
     # Changing a field clears the nonce so that the library will
     #   generate a new one.
     interest.setChildSelector(0)
     interest.setMustBeFresh(False)
     interest.setName(dataName.getPrefix(-1).appendSegment(segment))
     self._face.expressInterest(interest, self._onData, self._onTimeout)
Ejemplo n.º 17
0
    def _onInterest(self, prefix, interest, face, interestFilterId, filter):
        """
        Process the sync interest from the applicationBroadcastPrefix. If we
        can't satisfy the interest, add it to the pending interest table in
        the _contentCache so that a future call to contentCacheAdd may satisfy it.
        """
        if not self._enabled:
            # Ignore callbacks after the application calls shutdown().
            return

        # Search if the digest already exists in the digest log.
        logging.getLogger(__name__).info("Sync Interest received in callback.")
        logging.getLogger(__name__).info("%s", interest.getName().toUri())

        syncDigest = interest.getName().get(
            self._applicationBroadcastPrefix.size()).toEscapedString()
        if interest.getName().size(
        ) == self._applicationBroadcastPrefix.size() + 2:
            # Assume this is a recovery interest.
            syncDigest = interest.getName().get(
                self._applicationBroadcastPrefix.size() + 1).toEscapedString()
        logging.getLogger(__name__).info("syncDigest: %s", syncDigest)
        if (interest.getName().size()
                == self._applicationBroadcastPrefix.size() + 2
                or syncDigest == "00"):
            # Recovery interest or newcomer interest.
            self._processRecoveryInterest(interest, syncDigest, face)
        else:
            self._contentCache.storePendingInterest(interest, face)

            if syncDigest != self._digestTree.getRoot():
                index = self._logFind(syncDigest)
                if index == -1:
                    # To see whether there is any data packet coming back, wait
                    #   2 seconds using the Interest timeout mechanism.
                    # TODO: Are we sure using a "/local/timeout" interest is the
                    #   best future call approach?
                    timeout = Interest(Name("/local/timeout"))
                    timeout.setInterestLifetimeMilliseconds(2000)
                    self._face.expressInterest(
                        timeout, self._dummyOnData,
                        self._makeJudgeRecovery(syncDigest, face))
                    logging.getLogger(__name__).info("set timer recover")
                else:
                    # common interest processing
                    self._processSyncInterest(index, syncDigest, face)
Ejemplo n.º 18
0
    def _onInterest(self, prefix, interest, face, interestFilterId, filter):
        """
        Process the sync interest from the applicationBroadcastPrefix. If we
        can't satisfy the interest, add it to the pending interest table in
        the _contentCache so that a future call to contentCacheAdd may satisfy it.
        """
        if not self._enabled:
            # Ignore callbacks after the application calls shutdown().
            return

        # Search if the digest already exists in the digest log.
        logging.getLogger(__name__).info("Sync Interest received in callback.")
        logging.getLogger(__name__).info("%s", interest.getName().toUri())

        syncDigest = interest.getName().get(
          self._applicationBroadcastPrefix.size()).toEscapedString()
        if interest.getName().size() == self._applicationBroadcastPrefix.size() + 2:
            # Assume this is a recovery interest.
            syncDigest = interest.getName().get(
              self._applicationBroadcastPrefix.size() + 1).toEscapedString()
        logging.getLogger(__name__).info("syncDigest: %s", syncDigest)
        if (interest.getName().size() == self._applicationBroadcastPrefix.size() + 2 or
             syncDigest == "00"):
            # Recovery interest or newcomer interest.
            self._processRecoveryInterest(interest, syncDigest, face)
        else:
            self._contentCache.storePendingInterest(interest, face)

            if syncDigest != self._digestTree.getRoot():
                index = self._logFind(syncDigest)
                if index == -1:
                    # To see whether there is any data packet coming back, wait
                    #   2 seconds using the Interest timeout mechanism.
                    # TODO: Are we sure using a "/local/timeout" interest is the
                    #   best future call approach?
                    timeout = Interest(Name("/local/timeout"))
                    timeout.setInterestLifetimeMilliseconds(2000)
                    self._face.expressInterest(
                      timeout, self._dummyOnData,
                      self._makeJudgeRecovery(syncDigest, face))
                    logging.getLogger(__name__).info("set timer recover")
                else:
                    # common interest processing
                    self._processSyncInterest(index, syncDigest, face)
Ejemplo n.º 19
0
Archivo: node.py Proyecto: cawka/PyNDN2
 def __init__(self, transport, connectionInfo):
     self._transport = transport
     self._connectionInfo = connectionInfo
     # An array of PendintInterest
     self._pendingInterestTable = []
     # An array of RegisteredPrefix
     self._registeredPrefixTable = []
     self._ndndIdFetcherInterest = Interest(
       Name("/%C1.M.S.localhost/%C1.M.SRV/ndnd/KEY"))
     self._ndndIdFetcherInterest.setInterestLifetimeMilliseconds(4000.0)
     self._ndndId = None
Ejemplo n.º 20
0
    def _nfdRegisterPrefix(
      self, registeredPrefixId, prefix, onInterest, onRegisterFailed, flags,
      commandKeyChain, commandCertificateName):
        """
        Do the work of registerPrefix to register with NFD.

        :param int registeredPrefixId: The
          _RegisteredPrefix.getNextRegisteredPrefixId() which registerPrefix got
          so it could return it to the caller. If this is 0, then don't add to
          _registeredPrefixTable (assuming it has already been done).
        """
        if commandKeyChain == None:
            raise RuntimeError(
              "registerPrefix: The command KeyChain has not been set. You must call setCommandSigningInfo.")
        if commandCertificateName.size() == 0:
            raise RuntimeError(
              "registerPrefix: The command certificate name has not been set. You must call setCommandSigningInfo.")

        controlParameters = ControlParameters()
        controlParameters.setName(prefix)

        commandInterest = Interest(Name("/localhost/nfd/rib/register"))
        # NFD only accepts TlvWireFormat packets.
        commandInterest.getName().append(controlParameters.wireEncode(TlvWireFormat.get()))
        self.makeCommandInterest(
          commandInterest, commandKeyChain, commandCertificateName,
          TlvWireFormat.get())
        # The interest is answered by the local host, so set a short timeout.
        commandInterest.setInterestLifetimeMilliseconds(2000.0)

        if registeredPrefixId != 0:
            # Save the onInterest callback and send the registration interest.
            self._registeredPrefixTable.append(Node._RegisteredPrefix(
              registeredPrefixId, prefix, onInterest))

        response = Node._RegisterResponse(
          self, prefix, onInterest, onRegisterFailed, flags,
          TlvWireFormat.get(), True)
        self.expressInterest(
          commandInterest, response.onData, response.onTimeout,
          TlvWireFormat.get())
Ejemplo n.º 21
0
    def _registerPrefixHelper(self, registeredPrefixId, prefix, onInterest,
                              onRegisterFailed, flags, wireFormat):
        """
        Do the work of registerPrefix to register with NDNx once we have an 
        ndndId_.
        
        :param int registeredPrefixId: The 
          _RegisteredPrefix.getNextRegisteredPrefixId() which registerPrefix got
          so it could return it to the caller. If this is 0, then don't add to 
          registeredPrefixTable_ (assuming it has already been done).  
        """
        # Create a ForwardingEntry.
        # Note: ndnd ignores any freshness that is larger than 3600 seconds and
        #   sets 300 seconds instead. To register "forever", (=2000000000 sec),
        #   the freshness period must be omitted.
        forwardingEntry = ForwardingEntry()
        forwardingEntry.setAction("selfreg")
        forwardingEntry.setPrefix(prefix)
        forwardingEntry.setForwardingFlags(flags)
        content = forwardingEntry.wireEncode(wireFormat)

        # Set the ForwardingEntry as the content of a Data packet and sign.
        data = Data()
        data.setContent(content)
        # Set the name to a random value so that each request is unique.
        nonce = bytearray(4)
        for i in range(len(nonce)):
            nonce[i] = _systemRandom.randint(0, 0xff)
        data.getName().append(nonce)
        # The ndnd ignores the signature, so set to blank values.
        data.getSignature().getKeyLocator().setType(
            KeyLocatorType.KEY_LOCATOR_DIGEST)
        data.getSignature().getKeyLocator().setKeyData(
            Blob(bytearray(32), False))
        data.getSignature().setSignature(Blob(bytearray(128), False))
        encodedData = data.wireEncode(wireFormat)

        # Create an interest where the name has the encoded Data packet.
        interestName = Name().append("ndnx").append(
            self._ndndId).append("selfreg").append(encodedData)

        interest = Interest(interestName)
        interest.setInterestLifetimeMilliseconds(4000.0)
        interest.setScope(1)
        encodedInterest = interest.wireEncode(wireFormat)

        if registeredPrefixId != 0:
            # Save the onInterest callback and send the registration interest.
            self._registeredPrefixTable.append(
                Node._RegisteredPrefix(registeredPrefixId, prefix, onInterest))

        response = Node._RegisterResponse(self, prefix, onInterest,
                                          onRegisterFailed, flags, wireFormat,
                                          False)
        self.expressInterest(interest, response.onData, response.onTimeout,
                             wireFormat)
Ejemplo n.º 22
0
    def _onInterest(self, prefix, interest, transport, registerPrefixId):
        if not self._enabled:
            # Ignore callbacks after the application calls shutdown().
            return

        # Search if the digest already exists in the digest log.
        logging.getLogger(__name__).info("Sync Interest received in callback.")
        logging.getLogger(__name__).info("%s", interest.getName().toUri())

        syncDigest = interest.getName().get(
          self._applicationBroadcastPrefix.size()).toEscapedString()
        if interest.getName().size() == self._applicationBroadcastPrefix.size() + 2:
            # Assume this is a recovery interest.
            syncDigest = interest.getName().get(
              self._applicationBroadcastPrefix.size() + 1).toEscapedString()
        logging.getLogger(__name__).info("syncDigest: %s", syncDigest)
        if (interest.getName().size() == self._applicationBroadcastPrefix.size() + 2 or
             syncDigest == "00"):
            # Recovery interest or newcomer interest.
            self._processRecoveryInterest(interest, syncDigest, transport)
        else:
            # Save the unanswered interest in our local pending interest table.
            self._pendingInterestTable.append(self._PendingInterest(
              interest, transport))

            if syncDigest != self._digestTree.getRoot():
                index = self._logFind(syncDigest)
                if index == -1:
                    # To see whether there is any data packet coming back, wait
                    #   2 seconds using the Interest timeout mechanism.
                    # TODO: Are we sure using a "/local/timeout" interest is the
                    #   best future call approach?
                    timeout = Interest(Name("/local/timeout"))
                    timeout.setInterestLifetimeMilliseconds(2000)
                    self._face.expressInterest(
                      timeout, self._dummyOnData,
                      self._makeJudgeRecovery(syncDigest, transport))
                    logging.getLogger(__name__).info("set timer recover")
                else:
                    # common interest processing
                    self._processSyncInterest(index, syncDigest, transport)
Ejemplo n.º 23
0
    def _syncTimeout(self, interest):
        """
        Sync interest time out.  If the interest is the static one send again.
        """
        if not self._enabled:
            # Ignore callbacks after the application calls shutdown().
            return

        logging.getLogger(__name__).info("Sync Interest time out.")
        logging.getLogger(__name__).info(
          "Sync Interest name: %s", interest.getName().toUri())
        component = interest.getName().get(4).toEscapedString()
        if component == self._digestTree.getRoot():
            name = Name(interest.getName())
            retryInterest = Interest(interest.getName())
            retryInterest.setInterestLifetimeMilliseconds(self._syncLifetime)
            self._face.expressInterest(
              retryInterest, self._onData, self._syncTimeout)

            logging.getLogger(__name__).info("Syncinterest expressed:")
            logging.getLogger(__name__).info("%s", name.toUri())
Ejemplo n.º 24
0
    def _syncTimeout(self, interest):
        """
        Sync interest time out.  If the interest is the static one send again.
        """
        if not self._enabled:
            # Ignore callbacks after the application calls shutdown().
            return

        logging.getLogger(__name__).info("Sync Interest time out.")
        logging.getLogger(__name__).info("Sync Interest name: %s",
                                         interest.getName().toUri())
        component = interest.getName().get(4).toEscapedString()
        if component == self._digestTree.getRoot():
            name = Name(interest.getName())
            retryInterest = Interest(interest.getName())
            retryInterest.setInterestLifetimeMilliseconds(self._syncLifetime)
            self._face.expressInterest(retryInterest, self._onData,
                                       self._syncTimeout)

            logging.getLogger(__name__).info("Syncinterest expressed:")
            logging.getLogger(__name__).info("%s", name.toUri())
Ejemplo n.º 25
0
    def onReceivedElement(self, element):
        """
        This is called by the transport's ElementReader to process an
        entire received Data or Interest element.
        
        :param element: The bytes of the incoming element.
        :type element: An array type with int elements
        """
        # The type codes for TLV Interest and Data packets are chosen to not
        #   conflict with the first byte of a binary XML packet, so we canjust
        #   look at the first byte.
        if not (element[0] == Tlv.Interest or element[0] == Tlv.Data):
            # Ignore non-TLV elements.
            return

        # First, decode as Interest or Data.
        interest = None
        data = None
        decoder = TlvDecoder(element)
        if decoder.peekType(Tlv.Interest, len(element)):
            interest = Interest()
            interest.wireDecode(element, TlvWireFormat.get())
        elif decoder.peekType(Tlv.Data, len(element)):
            data = Data()
            data.wireDecode(element, TlvWireFormat.get())

        # Now process as Interest or Data.
        if interest != None:
            entry = self._getEntryForRegisteredPrefix(interest.getName())
            if entry != None:
                entry.getOnInterest()(entry.getPrefix(), interest,
                                      self._transport,
                                      entry.getRegisteredPrefixId())
        elif data != None:
            pendingInterests = self._extractEntriesForExpressedInterest(
                data.getName())
            for pendingInterest in pendingInterests:
                pendingInterest.getOnData()(pendingInterest.getInterest(),
                                            data)
Ejemplo n.º 26
0
 def __init__(self, transport, connectionInfo):
     self._transport = transport
     self._connectionInfo = connectionInfo
     # An array of _PendingInterest
     self._pendingInterestTable = []
     # An array of _RegisteredPrefix
     self._registeredPrefixTable = []
     # An array of _InterestFilterEntry
     self._interestFilterTable = []
     # An array of _DelayedCall
     self._delayedCallTable = []
     # An array of function objects
     self._onConnectedCallbacks = []
     self._ndndIdFetcherInterest = Interest(
       Name("/%C1.M.S.localhost/%C1.M.SRV/ndnd/KEY"))
     self._ndndIdFetcherInterest.setInterestLifetimeMilliseconds(4000.0)
     self._ndndId = None
     self._commandInterestGenerator = CommandInterestGenerator()
     self._timeoutPrefix = Name("/local/timeout")
     self._lastEntryId = 0
     self._lastEntryIdLock = threading.Lock()
     self._connectStatus = Node._ConnectStatus.UNCONNECTED
Ejemplo n.º 27
0
    def _initialTimeOut(self, interest):
        """
        Initial sync interest timeout, which means there are no other publishers
        yet.
        """
        if not self._enabled:
            # Ignore callbacks after the application calls shutdown().
            return

        logging.getLogger(__name__).info("initial sync timeout")
        logging.getLogger(__name__).info("no other people")
        self._sequenceNo += 1
        if self._sequenceNo != 0:
            # Since there were no other users, we expect sequence no 0.
            raise RuntimeError(
                "ChronoSync: sequenceNo_ is not the expected value of 0 for first use."
            )

        tempContent = SyncStateMsg()
        content = getattr(tempContent, "ss").add()
        content.name = self._applicationDataPrefixUri
        content.type = SyncState_UPDATE
        content.seqno.seq = self._sequenceNo
        content.seqno.session = self._sessionNo
        self._update(getattr(tempContent, "ss"))

        try:
            self._onInitialized()
        except:
            logging.exception("Error in onInitialized")

        name = Name(self._applicationBroadcastPrefix)
        name.append(self._digestTree.getRoot())
        retryInterest = Interest(name)
        retryInterest.setInterestLifetimeMilliseconds(self._syncLifetime)
        self._face.expressInterest(retryInterest, self._onData,
                                   self._syncTimeout)
        logging.getLogger(__name__).info("Syncinterest expressed:")
        logging.getLogger(__name__).info("%s", name.toUri())
Ejemplo n.º 28
0
    def _decryptCKey(self, cKeyData, onPlainText, onError):
        """
        Decrypt cKeyData.

        :param Data cKeyData: The C-KEY data packet.
        :param onPlainText: When the data packet is decrypted, this calls
          onPlainText(decryptedBlob) with the decrypted blob.
        :type onPlainText: function object
        :param onError: This calls onError(errorCode, message) for an error,
          where errorCode is from EncryptError.ErrorCode and message is a str.
        :type onError: function object
        """
        # Get the encrypted content.
        cKeyContent = cKeyData.getContent()
        cKeyEncryptedContent = EncryptedContent()
        try:
            cKeyEncryptedContent.wireDecode(cKeyContent)
        except Exception as ex:
            try:
                onError(EncryptError.ErrorCode.InvalidEncryptedFormat,
                        repr(ex))
            except:
                logging.exception("Error in onError")
            return
        eKeyName = cKeyEncryptedContent.getKeyLocator().getKeyName()
        dKeyName = eKeyName.getPrefix(-3)
        dKeyName.append(Encryptor.NAME_COMPONENT_D_KEY).append(
            eKeyName.getSubName(-2))

        # Check if the decryption key is already in the store.
        if dKeyName in self._dKeyMap:
            dKey = self._dKeyMap[dKeyName]
            Consumer._decrypt(cKeyEncryptedContent, dKey, onPlainText, onError)
        else:
            # Get the D-Key Data.
            interestName = Name(dKeyName)
            interestName.append(Encryptor.NAME_COMPONENT_FOR).append(
                self._consumerName)
            interest = Interest(interestName)

            def onVerified(validDKeyData):
                def localOnPlainText(dKeyBits):
                    # dKeyName is already a local copy.
                    self._dKeyMap[dKeyName] = dKeyBits
                    Consumer._decrypt(cKeyEncryptedContent, dKeyBits,
                                      onPlainText, onError)

                self._decryptDKey(validDKeyData, localOnPlainText, onError)

            self._sendInterest(interest, 1, self._dKeyLink, onVerified,
                               onError)
Ejemplo n.º 29
0
    def publishNextSequenceNo(self):
        """
        Increment the sequence number, create a sync message with the new
        sequence number and publish a data packet where the name is
        the applicationBroadcastPrefix + the root digest of the current digest
        tree. Then add the sync message to the digest tree and digest log which
        creates a new root digest. Finally, express an interest for the next sync
        update with the name applicationBroadcastPrefix + the new root digest.
        After this, your application should publish the content for the new
        sequence number. You can get the new sequence number with getSequenceNo().
        Note: Your application must call processEvents. Since processEvents
        modifies the internal ChronoSync data structures, your application should
        make sure that it calls processEvents in the same thread as
        publishNextSequenceNo() (which also modifies the data structures).
        """
        self._sequenceNo += 1

        syncMessage = sync_state_pb2.SyncStateMsg()
        content = getattr(syncMessage, "ss").add()
        content.name = self._applicationDataPrefixUri
        content.type = SyncState_UPDATE
        content.seqno.seq = self._sequenceNo
        content.seqno.session = self._sessionNo

        self._broadcastSyncState(self._digestTree.getRoot(), syncMessage)

        if not self._update(getattr(syncMessage, "ss")):
          # Since we incremented the sequence number, we expect there to be a
          #   new digest log entry.
          raise RuntimeError(
            "ChronoSync: update did not create a new digest log entry")

        # TODO: Should we have an option to not express an interest if this is the
        #   final publish of the session?
        interest = Interest(self._applicationBroadcastPrefix)
        interest.getName().append(self._digestTree.getRoot())
        interest.setInterestLifetimeMilliseconds(self._syncLifetime)
        self._face.expressInterest(interest, self._onData, self._syncTimeout)
Ejemplo n.º 30
0
    def onReceivedElement(self, element):
        """
        This is called by the transport's ElementReader to process an
        entire received Data or Interest element.

        :param element: The bytes of the incoming element.
        :type element: An array type with int elements
        """
        # The type codes for TLV Interest and Data packets are chosen to not
        #   conflict with the first byte of a binary XML packet, so we canjust
        #   look at the first byte.
        if not (element[0] == Tlv.Interest or element[0] == Tlv.Data):
            # Ignore non-TLV elements.
            return

        # First, decode as Interest or Data.
        interest = None
        data = None
        decoder = TlvDecoder(element)
        if decoder.peekType(Tlv.Interest, len(element)):
            interest = Interest()
            interest.wireDecode(element, TlvWireFormat.get())
        elif decoder.peekType(Tlv.Data, len(element)):
            data = Data()
            data.wireDecode(element, TlvWireFormat.get())

        # Now process as Interest or Data.
        if interest != None:
            entry = self._getEntryForRegisteredPrefix(interest.getName())
            if entry != None:
                entry.getOnInterest()(
                  entry.getPrefix(), interest, self._transport,
                  entry.getRegisteredPrefixId())
        elif data != None:
            pendingInterests = self._extractEntriesForExpressedInterest(
              data.getName())
            for pendingInterest in pendingInterests:
                pendingInterest.getOnData()(pendingInterest.getInterest(), data)
Ejemplo n.º 31
0
    def _sendSyncInterest(self):
        """
        Send the sync interest for full synchronization. This forms the interest
        name: /<sync-prefix>/<own-IBLT>. This cancels any pending sync interest
        we sent earlier on the face.
        """
        # Debug: Implement stopping an ongoing fetch.
        ## If we send two sync interest one after the other
        ## since there is no new data in the network yet,
        ## when data is available it may satisfy both of them
        #if self._fetcher != None:
        #    self._fetcher.stop()

        # Sync Interest format for full sync: /<sync-prefix>/<ourLatestIBF>
        syncInterestName = Name(self._syncPrefix)

        # Append our latest IBLT.
        syncInterestName.append(self._iblt.encode())

        self._outstandingInterestName = syncInterestName

        # random1 is from 0.0 to 1.0.
        random1 = self._systemRandom.random()
        # Get a jitter of +/- syncInterestLifetime_ * 0.2 .
        jitter = (random1 - 0.5) * (self._syncInterestLifetime * 0.2)

        self._face.callLater(
          self._syncInterestLifetime / 2 + jitter, self._sendSyncInterest)

        syncInterest = Interest(syncInterestName)
        syncInterest.setInterestLifetimeMilliseconds(self._syncInterestLifetime)
        syncInterest.setNonce(Blob([0, 0, 0, 0]))
        syncInterest.refreshNonce()

        SegmentFetcher.fetch(
          self._face, syncInterest, None,
          lambda content: self._onSyncData(content, syncInterest),
          FullPSync2017._onError)

        logging.getLogger(__name__).debug("sendFullSyncInterest, nonce: " +
          syncInterest.getNonce().toHex() + ", hash: " +
          str(abs(hash(syncInterestName))))
Ejemplo n.º 32
0
    def _handleCoveringKey(self, interest, data, timeSlot, onEncryptedKeys,
                           onError):
        """
        This is called from an expressInterest OnData to check that the
        encryption key contained in data fits the timeSlot. This sends a refined
        interest if required.

        :param Interest interest: The interest given to expressInterest.
        :param Data data: The fetched Data packet.
        :param float timeSlot: The time slot as milliseconds since Jan 1, 1970 UTC.
        :param onEncryptedKeys: When there are no more interests to process,
          this calls onEncryptedKeys(keys) where keys is a list of encrypted
          content key Data packets. If onEncryptedKeys is None, this does not
          use it.
        :type onEncryptedKeys: function object
        :param onError: This calls onError(errorCode, message) for an error.
        :type onError: function object
        """
        timeCount = round(timeSlot)
        keyRequest = self._keyRequests[timeCount]

        interestName = interest.getName()
        keyName = data.getName()

        begin = Schedule.fromIsoString(
            str(keyName.get(Producer.START_TIME_STAMP_INDEX).getValue()))
        end = Schedule.fromIsoString(
            str(keyName.get(Producer.END_TIME_STAMP_INDEX).getValue()))

        if timeSlot >= end:
            # If the received E-KEY covers some earlier period, try to retrieve
            # an E-KEY covering a later one.
            timeRange = Exclude(interest.getExclude())
            Producer.excludeBefore(
                timeRange, keyName.get(Producer.START_TIME_STAMP_INDEX))
            keyRequest.repeatAttempts[interestName] = 0
            self._sendKeyInterest(
                Interest(interestName).setExclude(timeRange).setChildSelector(
                    1), timeSlot, onEncryptedKeys, onError)
        else:
            # If the received E-KEY covers the content key, encrypt the content.
            encryptionKey = data.getContent()
            # If everything is correct, save the E-KEY as the current key.
            if self._encryptContentKey(encryptionKey, keyName, timeSlot,
                                       onEncryptedKeys, onError):
                keyInfo = self._eKeyInfo[interestName]
                keyInfo.beginTimeSlot = begin
                keyInfo.endTimeSlot = end
                keyInfo.keyBits = encryptionKey
Ejemplo n.º 33
0
    def _onTimeout(self, interest):
        interestLifetime = interest.getInterestLifetimeMilliseconds()
        if interestLifetime == None:
            # Can't re-express.
            if self._callerOnTimeout != None:
                try:
                    self._callerOnTimeout(interest)
                except:
                    logging.exception("Error in onTimeout")
            return

        nextInterestLifetime = interestLifetime * 2
        if nextInterestLifetime > self._maxInterestLifetime:
            if self._callerOnTimeout != None:
                try:
                    self._callerOnTimeout(interest)
                except:
                    logging.exception("Error in onTimeout")
            return

        nextInterest = Interest(interest)
        nextInterest.setInterestLifetimeMilliseconds(nextInterestLifetime)
        self._face.expressInterest(
          nextInterest, self._callerOnData, self._onTimeout)
Ejemplo n.º 34
0
    def _onTimeout(self, interest):
        interestLifetime = interest.getInterestLifetimeMilliseconds()
        if interestLifetime == None:
            # Can't re-express.
            if self._callerOnTimeout != None:
                try:
                    self._callerOnTimeout(interest)
                except:
                    logging.exception("Error in onTimeout")
            return

        nextInterestLifetime = interestLifetime * 2
        if nextInterestLifetime > self._maxInterestLifetime:
            if self._callerOnTimeout != None:
                try:
                    self._callerOnTimeout(interest)
                except:
                    logging.exception("Error in onTimeout")
            return

        nextInterest = Interest(interest)
        nextInterest.setInterestLifetimeMilliseconds(nextInterestLifetime)
        self._face.expressInterest(
          nextInterest, self._callerOnData, self._onTimeout)
Ejemplo n.º 35
0
    def _registerPrefixHelper(
      self, registeredPrefixId, prefix, onInterest, onRegisterFailed, flags,
      wireFormat):
        """
        Do the work of registerPrefix to register with NDNx once we have an
        _ndndId.

        :param int registeredPrefixId: The
          _RegisteredPrefix.getNextRegisteredPrefixId() which registerPrefix got
          so it could return it to the caller. If this is 0, then don't add to
          _registeredPrefixTable (assuming it has already been done).
        """
        # Create a ForwardingEntry.
        # Note: ndnd ignores any freshness that is larger than 3600 seconds and
        #   sets 300 seconds instead. To register "forever", (=2000000000 sec),
        #   the freshness period must be omitted.
        forwardingEntry = ForwardingEntry()
        forwardingEntry.setAction("selfreg")
        forwardingEntry.setPrefix(prefix)
        forwardingEntry.setForwardingFlags(flags)
        content = forwardingEntry.wireEncode(wireFormat)

        # Set the ForwardingEntry as the content of a Data packet and sign.
        data = Data()
        data.setContent(content)
        # Set the name to a random value so that each request is unique.
        nonce = bytearray(4)
        for i in range(len(nonce)):
            nonce[i] = _systemRandom.randint(0, 0xff)
        data.getName().append(nonce)
        # The ndnd ignores the signature, so set to blank values.
        data.getSignature().getKeyLocator().setType(
          KeyLocatorType.KEY_LOCATOR_DIGEST)
        data.getSignature().getKeyLocator().setKeyData(
          Blob(bytearray(32), False))
        data.getSignature().setSignature(Blob(bytearray(128), False))
        encodedData = data.wireEncode(wireFormat)

        # Create an interest where the name has the encoded Data packet.
        interestName = Name().append("ndnx").append(self._ndndId).append(
          "selfreg").append(encodedData)

        interest = Interest(interestName)
        interest.setInterestLifetimeMilliseconds(4000.0)
        interest.setScope(1)
        encodedInterest = interest.wireEncode(wireFormat)

        if registeredPrefixId != 0:
            # Save the onInterest callback and send the registration interest.
            self._registeredPrefixTable.append(Node._RegisteredPrefix(
              registeredPrefixId, prefix, onInterest))

        response = Node._RegisterResponse(
          self, prefix, onInterest, onRegisterFailed, flags, wireFormat, False)
        self.expressInterest(
          interest, response.onData, response.onTimeout, wireFormat)
Ejemplo n.º 36
0
    def _decryptContent(self, data, onPlainText, onError):
        """
        Decrypt the data packet.

        :param Data data: The data packet. This does not verify the packet.
        :param onPlainText: When the data packet is decrypted, this calls
          onPlainText(decryptedBlob) with the decrypted blob.
        :type onPlainText: function object
        :param onError: This calls onError(errorCode, message) for an error,
          where errorCode is from EncryptError.ErrorCode and message is a str.
        :type onError: function object
        """
        # Get the encrypted content.
        dataEncryptedContent = EncryptedContent()
        try:
            dataEncryptedContent.wireDecode(data.getContent())
        except Exception as ex:
            Consumer._callOnError(
                onError, EncryptError.ErrorCode.InvalidEncryptedFormat,
                repr(ex))
            return
        cKeyName = dataEncryptedContent.getKeyLocator().getKeyName()

        # Check if the content key is already in the store.
        if cKeyName in self._cKeyMap:
            cKey = self._cKeyMap[cKeyName]
            self._decrypt(dataEncryptedContent, cKey, onPlainText, onError)
        else:
            # Retrieve the C-KEY Data from the network.
            interestName = Name(cKeyName)
            interestName.append(Encryptor.NAME_COMPONENT_FOR).append(
                self._groupName)
            interest = Interest(interestName)

            def onVerified(validCKeyData):
                def localOnPlainText(cKeyBits):
                    # cKeyName is already a copy inside the local
                    #   dataEncryptedContent.
                    self._cKeyMap[cKeyName] = cKeyBits
                    Consumer._decrypt(dataEncryptedContent, cKeyBits,
                                      onPlainText, onError)

                self._decryptCKey(validCKeyData, localOnPlainText, onError)

            self._sendInterest(interest, 1, self._cKeyLink, onVerified,
                               onError)
Ejemplo n.º 37
0
    def _checkPolicyHelper(self, keyName, state, continueValidation):
        """
        :param Name keyName:
        :param ValidationState state:
        :param continueValidation:
        :type continueValidation: function object
        """
        try:
            identity = self._pib.getIdentity(
                PibKey.extractIdentityFromKeyName(keyName))
        except Exception as ex:
            state.fail(
                ValidationError(
                    ValidationError.CANNOT_RETRIEVE_CERTIFICATE,
                    "Cannot get the PIB identity for key " + keyName.toUri() +
                    ": " + repr(ex)))
            return

        try:
            key = identity.getKey(keyName)
        except Exception as ex:
            state.fail(
                ValidationError(
                    ValidationError.CANNOT_RETRIEVE_CERTIFICATE,
                    "Cannot get the PIB key " + keyName.toUri() + ": " +
                    repr(ex)))
            return

        try:
            certificate = key.getDefaultCertificate()
        except Exception as ex:
            state.fail(
                ValidationError(
                    ValidationError.CANNOT_RETRIEVE_CERTIFICATE,
                    "Cannot get the default certificate for key " +
                    keyName.toUri() + ": " + repr(ex)))
            return

        # Add the certificate as the temporary trust anchor.
        self._validator.resetAnchors()
        self._validator.loadAnchor("", certificate)
        continueValidation(CertificateRequest(Interest(keyName)), state)
        # Clear the temporary trust anchor.
        self._validator.resetAnchors()
Ejemplo n.º 38
0
    def makeCommandInterest(self, name, params=None, wireFormat=None):
        """
        Append the timestamp and nonce name components to the supplied name,
        create an Interest object and signs it with the KeyChain given to the
        constructor. This ensures that the timestamp is greater than the
        timestamp used in the previous call.

        :param Name name: The Name for the Interest, which is copied.
        :param SigningInfo params: (optional) The signing parameters. If omitted,
          use a default SigningInfo().
        :param WireFormat wireFormat: (optional) A WireFormat object used to
          encode the SignatureInfo and to encode interest name for signing. If
          omitted, use WireFormat getDefaultWireFormat().
        :return: The new command Interest object.
        :rtype: Interest
        """
        arg2 = params
        arg3 = wireFormat
        if isinstance(arg2, SigningInfo):
            params = arg2
        else:
            params = None

        if isinstance(arg2, WireFormat):
            wireFormat = arg2
        elif isinstance(arg3, WireFormat):
            wireFormat = arg3
        else:
            wireFormat = None

        if params == None:
            params = SigningInfo()

        if wireFormat == None:
            wireFormat = WireFormat.getDefaultWireFormat()

        # This copies the Name.
        commandInterest = Interest(name)

        self.prepareCommandInterestName(commandInterest, wireFormat)
        self._keyChain.sign(commandInterest, params, wireFormat)

        return commandInterest
Ejemplo n.º 39
0
    def consume(self, contentName, onConsumeComplete, onError, link=None):
        """
        Express an Interest to fetch the content packet with contentName, and
        decrypt it, fetching keys as needed.

        :param Name contentName: The name of the content packet.
        :param onConsumeComplete: When the content packet is fetched and
          decrypted, this calls onConsumeComplete(contentData, result) where
          contentData is the fetched Data packet and result is the decrypted
          plain text Blob.
          NOTE: The library will log any exceptions raised by this callback, but
          for better error handling the callback should catch and properly
          handle any exceptions.
        :type onPlainText: function object
        :param onError: This calls onError(errorCode, message) for an error,
          where errorCode is from EncryptError.ErrorCode and message is a str.
          NOTE: The library will log any exceptions raised by this callback, but
          for better error handling the callback should catch and properly
          handle any exceptions.
        :type onError: function object
        :param Link link: (optional) The Link object to use in Interests for
          data retrieval. This makes a copy of the Link object. If the Link
          object's getDelegations().size() is zero, don't use it. If omitted,
          don't use a Link object.
        """
        if link == None:
            link = Consumer.NO_LINK

        interest = Interest(contentName)

        def onVerified(validData):
            # Decrypt the content.
            def onPlainText(plainText):
                try:
                    onConsumeComplete(validData, plainText)
                except:
                    logging.exception("Error in onConsumeComplete")

            self._decryptContent(validData, onPlainText, onError)

        # Copy the Link object since the passed link may become invalid.
        self._sendInterest(interest, 1, Link(link), onVerified, onError)
Ejemplo n.º 40
0
    def publishNextSequenceNo(self, applicationInfo=None):
        """
        Increment the sequence number, create a sync message with the new
        sequence number and publish a data packet where the name is
        the applicationBroadcastPrefix + the root digest of the current digest
        tree. Then add the sync message to the digest tree and digest log which
        creates a new root digest. Finally, express an interest for the next sync
        update with the name applicationBroadcastPrefix + the new root digest.
        After this, your application should publish the content for the new
        sequence number. You can get the new sequence number with getSequenceNo().
        Note: Your application must call processEvents. Since processEvents
        modifies the internal ChronoSync data structures, your application should
        make sure that it calls processEvents in the same thread as
        publishNextSequenceNo() (which also modifies the data structures).

        :param Blob applicationInfo: (optional) This appends applicationInfo to
          the content of the sync messages. This same info is provided to the
          receiving application in the SyncState state object provided to the
          onReceivedSyncState callback.
        """
        applicationInfo = (applicationInfo if isinstance(
            applicationInfo, Blob) else Blob(applicationInfo))

        self._sequenceNo += 1

        syncMessage = SyncStateMsg()
        content = getattr(syncMessage, "ss").add()
        content.name = self._applicationDataPrefixUri
        content.type = SyncState_UPDATE
        content.seqno.seq = self._sequenceNo
        content.seqno.session = self._sessionNo
        if not applicationInfo.isNull() and applicationInfo.size() > 0:
            content.application_info = applicationInfo.toBytes()

        self._broadcastSyncState(self._digestTree.getRoot(), syncMessage)

        if not self._update(getattr(syncMessage, "ss")):
            # Since we incremented the sequence number, we expect there to be a
            #   new digest log entry.
            raise RuntimeError(
                "ChronoSync: update did not create a new digest log entry")

        # TODO: Should we have an option to not express an interest if this is the
        #   final publish of the session?
        interest = Interest(self._applicationBroadcastPrefix)
        interest.getName().append(self._digestTree.getRoot())
        interest.setInterestLifetimeMilliseconds(self._syncLifetime)
        self._face.expressInterest(interest, self._onData, self._syncTimeout)
    def checkPolicy(self, dataOrInterest, state, continueValidation):
        """
        :param dataOrInterest:
        :type dataOrInterest: Data or Interest
        :param ValidationState state:
        :param continueValidation:
        :type continueValidation: function object
        """
        keyLocatorName = ValidationPolicy.getKeyLocatorName(
            dataOrInterest, state)
        if state.isOutcomeFailed():
            # Already called state.fail().
            return

        if keyLocatorName.getPrefix(-2).isPrefixOf(dataOrInterest.getName()):
            continueValidation(CertificateRequest(Interest(keyLocatorName)),
                               state)
        else:
            state.fail(
                ValidationError(
                    ValidationError.INVALID_KEY_LOCATOR,
                    "Signing policy violation for " +
                    dataOrInterest.getName().toUri() + " by " +
                    keyLocatorName.toUri()))
Ejemplo n.º 42
0
    def _nfdRegisterPrefix(self, registeredPrefixId, prefix, onInterest,
                           onRegisterFailed, flags, commandKeyChain,
                           commandCertificateName):
        """
        Do the work of registerPrefix to register with NFD.
        
        :param int registeredPrefixId: The 
          _RegisteredPrefix.getNextRegisteredPrefixId() which registerPrefix got
          so it could return it to the caller. If this is 0, then don't add to 
          registeredPrefixTable_ (assuming it has already been done).  
        """
        if commandKeyChain == None:
            raise RuntimeError(
                "registerPrefix: The command KeyChain has not been set. You must call setCommandSigningInfo."
            )
        if commandCertificateName.size() == 0:
            raise RuntimeError(
                "registerPrefix: The command certificate name has not been set. You must call setCommandSigningInfo."
            )

        controlParameters = ControlParameters()
        controlParameters.setName(prefix)

        commandInterest = Interest(Name("/localhost/nfd/rib/register"))
        # NFD only accepts TlvWireFormat packets.
        commandInterest.getName().append(
            controlParameters.wireEncode(TlvWireFormat.get()))
        self.makeCommandInterest(commandInterest, commandKeyChain,
                                 commandCertificateName, TlvWireFormat.get())
        # The interest is answered by the local host, so set a short timeout.
        commandInterest.setInterestLifetimeMilliseconds(2000.0)

        if registeredPrefixId != 0:
            # Save the onInterest callback and send the registration interest.
            self._registeredPrefixTable.append(
                Node._RegisteredPrefix(registeredPrefixId, prefix, onInterest))

        response = Node._RegisterResponse(self, prefix, onInterest,
                                          onRegisterFailed, flags,
                                          TlvWireFormat.get(), True)
        self.expressInterest(commandInterest, response.onData,
                             response.onTimeout, TlvWireFormat.get())
Ejemplo n.º 43
0
    def _makeNotificationInterest(self):
        """
        Make and return a new Interest where the name is
        _applicationBroadcastPrefix plus the encoding of _stateVector. Also
        use _hmacKey to sign it with HmacWithSha256.

        :return: The new signed notification interest.
        :rtype: Interest
        """
        interest = Interest(self._applicationBroadcastPrefix)
        interest.setInterestLifetimeMilliseconds(
            self._notificationInterestLifetime)
        interest.getName().append(
            StateVectorSync2018.encodeStateVector(self._stateVector,
                                                  self._sortedStateVectorKeys))

        # TODO: Should we just use key name /A ?
        KeyChain.signWithHmacWithSha256(interest, self._hmacKey, Name("/A"))

        return interest
Ejemplo n.º 44
0
    def _sendSyncInterest(self):
        """
        Send the sync interest for full synchronization. This forms the interest
        name: /<sync-prefix>/<own-IBLT>. This cancels any pending sync interest
        we sent earlier on the face.
        """
        # Debug: Implement stopping an ongoing fetch.
        ## If we send two sync interest one after the other
        ## since there is no new data in the network yet,
        ## when data is available it may satisfy both of them
        #if self._fetcher != None:
        #    self._fetcher.stop()

        # Sync Interest format for full sync: /<sync-prefix>/<ourLatestIBF>
        syncInterestName = Name(self._syncPrefix)

        # Append our latest IBLT.
        syncInterestName.append(self._iblt.encode())

        self._outstandingInterestName = syncInterestName

        # random1 is from 0.0 to 1.0.
        random1 = self._systemRandom.random()
        # Get a jitter of +/- syncInterestLifetime_ * 0.2 .
        jitter = (random1 - 0.5) * (self._syncInterestLifetime * 0.2)

        self._face.callLater(
          self._syncInterestLifetime / 2 + jitter, self._sendSyncInterest)

        syncInterest = Interest(syncInterestName)
        syncInterest.setInterestLifetimeMilliseconds(self._syncInterestLifetime)
        syncInterest.refreshNonce()

        SegmentFetcher.fetch(
          self._face, syncInterest, None,
          lambda content: self._onSyncData(content, syncInterest),
          FullPSync2017._onError)

        logging.getLogger(__name__).debug("sendFullSyncInterest, nonce: " +
          syncInterest.getNonce().toHex() + ", hash: " +
          str(abs(hash(syncInterestName))))
Ejemplo n.º 45
0
    def publishNextSequenceNo(self):
        """
        Increment the sequence number, create a sync message with the new
        sequence number and publish a data packet where the name is
        the applicationBroadcastPrefix + the root digest of the current digest
        tree. Then add the sync message to the digest tree and digest log which
        creates a new root digest. Finally, express an interest for the next sync
        update with the name applicationBroadcastPrefix + the new root digest.
        After this, your application should publish the content for the new
        sequence number. You can get the new sequence number with getSequenceNo().
        Note: Your application must call processEvents. Since processEvents
        modifies the internal ChronoSync data structures, your application should
        make sure that it calls processEvents in the same thread as
        publishNextSequenceNo() (which also modifies the data structures).
        """
        self._sequenceNo += 1

        syncMessage = sync_state_pb2.SyncStateMsg()
        content = getattr(syncMessage, "ss").add()
        content.name = self._applicationDataPrefixUri
        content.type = SyncState_UPDATE
        content.seqno.seq = self._sequenceNo
        content.seqno.session = self._sessionNo

        self._broadcastSyncState(self._digestTree.getRoot(), syncMessage)

        if not self._update(getattr(syncMessage, "ss")):
          # Since we incremented the sequence number, we expect there to be a
          #   new digest log entry.
          raise RuntimeError(
            "ChronoSync: update did not create a new digest log entry")

        # TODO: Should we have an option to not express an interest if this is the
        #   final publish of the session?
        interest = Interest(self._applicationBroadcastPrefix)
        interest.getName().append(self._digestTree.getRoot())
        interest.setInterestLifetimeMilliseconds(self._syncLifetime)
        self._face.expressInterest(interest, self._onData, self._syncTimeout)
Ejemplo n.º 46
0
    def _fetchKekAndPublishCkData(self, onReady, onError, nTriesLeft):
        """
        Create an Interest for <access-prefix>/KEK to retrieve the
        <access-prefix>/KEK/<key-id> KEK Data packet, and set _kekData.

        :param onReady: When the KEK is retrieved and published, this calls
          onReady().
        :type onError: function object
        :param onError: On failure, this calls onError(errorCode, message)
          where errorCode is from EncryptError.ErrorCode, and message is an
          error string.
        :type onError: function object
        :param int nTriesLeft: The number of retries for expressInterest timeouts.
        """
        logging.getLogger(__name__).info("Fetching KEK: " + Name(
            self._accessPrefix).append(EncryptorV2.NAME_COMPONENT_KEK).toUri())

        if self._kekPendingInterestId > 0:
            onError(
                EncryptError.ErrorCode.General,
                "fetchKekAndPublishCkData: There is already a _kekPendingInterestId"
            )
            return

        def onData(interest, kekData):
            self._kekPendingInterestId = 0
            # TODO: Verify if the key is legitimate.
            self._kekData = kekData
            if self._makeAndPublishCkData(onError):
                onReady()
            # Otherwise, failure has already been reported.

        def onTimeout(interest):
            self._kekPendingInterestId = 0
            if nTriesLeft > 1:
                self._fetchKekAndPublishCkData(onReady, onError,
                                               nTriesLeft - 1)
            else:
                onError(
                    EncryptError.ErrorCode.KekRetrievalTimeout,
                    "Retrieval of KEK [" + interest.getName().toUri() +
                    "] timed out")
                logging.getLogger(__name__).info(
                    "Scheduling retry after all timeouts")
                self._face.callLater(EncryptorV2.RETRY_DELAY_KEK_RETRIEVAL_MS,
                                     self._retryFetchingKek)

        def onNetworkNack(interest, networkNack):
            self._kekPendingInterestId = 0
            if nTriesLeft > 1:

                def callback():
                    self._fetchKekAndPublishCkData(onReady, onError,
                                                   nTriesLeft - 1)

                self._face.callLater(EncryptorV2.RETRY_DELAY_AFTER_NACK_MS,
                                     callback)
            else:
                onError(
                    EncryptError.ErrorCode.KekRetrievalFailure,
                    "Retrieval of KEK [" + interest.getName().toUri() +
                    "] failed. Got NACK (" + str(networkNack.getReason()) +
                    ")")
                logging.getLogger(__name__).info("Scheduling retry from NACK")
                self._face.callLater(EncryptorV2.RETRY_DELAY_KEK_RETRIEVAL_MS,
                                     self._retryFetchingKek)

        try:
            self._kekPendingInterestId = self._face.expressInterest(
                Interest(
                    Name(self._accessPrefix).append(
                        EncryptorV2.NAME_COMPONENT_KEK)).setMustBeFresh(
                            True).setCanBePrefix(True), onData, onTimeout,
                onNetworkNack)
        except Exception as ex:
            onError(EncryptError.ErrorCode.General,
                    "expressInterest error: " + repr(ex))
Ejemplo n.º 47
0
class InterestValidationState(ValidationState):
    """
    Create a InterestValidationState for the Interest packet. The caller must
    ensure that the state instance is valid until the validation finishes (i.e.,
    until validateCertificateChain() and validateOriginalPacket() have been
    called).

    :param Interest interest: The Date packet being validated, which is copied.
    :param successCallback: This calls successCallback(interest) to report a
      successful Interest validation.
    :type successCallback: function object
    :param failureCallback: This calls failureCallback(interest, error) to
      report a failed Interest validation, where error is a ValidationError.
    :type failureCallback: function object
    """
    def __init__(self, interest, successCallback, failureCallback):
        super(InterestValidationState, self).__init__()

        # Make a copy.
        self._interest = Interest(interest)
        self._successCallbacks = [successCallback
                                  ]  # of SuccessCallback function object
        self._failureCallback = failureCallback

        if successCallback == None:
            raise ValueError("The successCallback is None")
        if self._failureCallback == None:
            raise ValueError("The failureCallback is None")

    def fail(self, error):
        """
        Call the failure callback.

        :param ValidationError error:
        """
        logging.getLogger(__name__).info("" + str(error))
        try:
            self._failureCallback(self._interest, error)
        except:
            logging.exception("Error in failureCallback")

        self.setOutcome(False)

    def getOriginalInterest(self):
        """
        Get the original Interest packet being validated which was given to the
        constructor.

        :return: The original Interest packet.
        :rtype: Interest
        """
        return self._interest

    def addSuccessCallback(self, successCallback):
        """
        :param successCallback: This calls successCallback(interest).
        :type successCallback: function object
        """
        self._successCallbacks.append(successCallback)

    def _verifyOriginalPacket(self, trustedCertificate):
        """
        Verify the signature of the original packet. This is only called by the
        Validator class.

        :param CertificateV2 trustedCertificate: The certificate that signs the
          original packet.
        """
        if VerificationHelpers.verifyInterestSignature(self._interest,
                                                       trustedCertificate):
            logging.getLogger(__name__).info("OK signature for interest `" +
                                             self._interest.getName().toUri() +
                                             "`")
            for i in range(len(self._successCallbacks)):
                try:
                    self._successCallbacks[i](self._interest)
                except:
                    logging.exception("Error in successCallback")

            self.setOutcome(True)
        else:
            self.fail(
                ValidationError(
                    ValidationError.INVALID_SIGNATURE,
                    "Invalid signature of interest `" +
                    self._interest.getName().toUri() + "`"))

    def _bypassValidation(self):
        """
        Call the success callback of the original packet without signature
        validation. This is only called by the Validator class.
        """
        logging.getLogger(
            __name__).info("Signature verification bypassed for interest `" +
                           self._interest.getName().toUri() + "`")
        for i in range(len(self._successCallbacks)):
            try:
                self._successCallbacks[i](self._interest)
            except:
                logging.exception("Error in successCallback")

        self.setOutcome(True)
Ejemplo n.º 48
0
 def _fetchFirstSegment(self, baseInterest):
     interest = Interest(baseInterest)
     interest.setChildSelector(1)
     interest.setMustBeFresh(True)
     self._face.expressInterest(interest, self._onData, self._onTimeout)
Ejemplo n.º 49
0
class InterestValidationState(ValidationState):
    """
    Create a InterestValidationState for the Interest packet. The caller must
    ensure that the state instance is valid until the validation finishes (i.e.,
    until validateCertificateChain() and validateOriginalPacket() have been
    called).

    :param Interest interest: The Date packet being validated, which is copied.
    :param successCallback: This calls successCallback(interest) to report a
      successful Interest validation.
    :type successCallback: function object
    :param failureCallback: This calls failureCallback(interest, error) to
      report a failed Interest validation, where error is a ValidationError.
    :type failureCallback: function object
    """
    def __init__(self, interest, successCallback, failureCallback):
        super(InterestValidationState, self).__init__()

        # Make a copy.
        self._interest = Interest(interest)
        self._successCallbacks = [successCallback] # of SuccessCallback function object
        self._failureCallback = failureCallback

        if successCallback == None:
            raise ValueError("The successCallback is None")
        if self._failureCallback == None:
            raise ValueError("The failureCallback is None")

    def fail(self, error):
        """
        Call the failure callback.

        :param ValidationError error:
        """
        logging.getLogger(__name__).info("" + str(error))
        try:
            self._failureCallback(self._interest, error)
        except:
            logging.exception("Error in failureCallback")

        self.setOutcome(False)

    def getOriginalInterest(self):
        """
        Get the original Interest packet being validated which was given to the
        constructor.

        :return: The original Interest packet.
        :rtype: Interest
        """
        return self._interest

    def addSuccessCallback(self, successCallback):
        """
        :param successCallback: This calls successCallback(interest).
        :type successCallback: function object
        """
        self._successCallbacks.append(successCallback)

    def _verifyOriginalPacket(self, trustedCertificate):
        """
        Verify the signature of the original packet. This is only called by the
        Validator class.

        :param CertificateV2 trustedCertificate: The certificate that signs the
          original packet.
        """
        if VerificationHelpers.verifyInterestSignature(
              self._interest, trustedCertificate):
            logging.getLogger(__name__).info("OK signature for interest `" +
              self._interest.getName().toUri() + "`")
            for i in range(len(self._successCallbacks)):
                try:
                    self._successCallbacks[i](self._interest)
                except:
                    logging.exception("Error in successCallback")

            self.setOutcome(True)
        else:
          self.fail(ValidationError(ValidationError.INVALID_SIGNATURE,
            "Invalid signature of interest `" + self._interest.getName().toUri() +
            "`"))

    def _bypassValidation(self):
        """
        Call the success callback of the original packet without signature
        validation. This is only called by the Validator class.
        """
        logging.getLogger(__name__).info(
          "Signature verification bypassed for interest `" +
          self._interest.getName().toUri() + "`")
        for i in range(len(self._successCallbacks)):
            try:
                self._successCallbacks[i](self._interest)
            except:
                logging.exception("Error in successCallback")

        self.setOutcome(True)
Ejemplo n.º 50
0
    def _nfdRegisterPrefix(
      self, registeredPrefixId, prefix, onInterest, onRegisterFailed,
      onRegisterSuccess, flags, commandKeyChain, commandCertificateName, face):
        """
        Do the work of registerPrefix to register with NFD.

        :param int registeredPrefixId: The getNextEntryId() which registerPrefix
          got so it could return it to the caller. If this is 0, then don't add
          to _registeredPrefixTable (assuming it has already been done).
        """
        if commandKeyChain == None:
            raise RuntimeError(
              "registerPrefix: The command KeyChain has not been set. You must call setCommandSigningInfo.")
        if commandCertificateName.size() == 0:
            raise RuntimeError(
              "registerPrefix: The command certificate name has not been set. You must call setCommandSigningInfo.")

        controlParameters = ControlParameters()
        controlParameters.setName(prefix)
        controlParameters.setForwardingFlags(flags)

        commandInterest = Interest()
        if self.isLocal():
            commandInterest.setName(Name("/localhost/nfd/rib/register"))
            # The interest is answered by the local host, so set a short timeout.
            commandInterest.setInterestLifetimeMilliseconds(2000.0)
        else:
            commandInterest.setName(Name("/localhop/nfd/rib/register"))
            # The host is remote, so set a longer timeout.
            commandInterest.setInterestLifetimeMilliseconds(4000.0)
        # NFD only accepts TlvWireFormat packets.
        commandInterest.getName().append(controlParameters.wireEncode(TlvWireFormat.get()))
        self.makeCommandInterest(
          commandInterest, commandKeyChain, commandCertificateName,
          TlvWireFormat.get())

        # Send the registration interest.
        response = Node._RegisterResponse(
          prefix, onRegisterFailed, onRegisterSuccess, registeredPrefixId, self,
          onInterest, face)
        self.expressInterest(
          self.getNextEntryId(), commandInterest, response.onData,
          response.onTimeout, None, TlvWireFormat.get(), face)
Ejemplo n.º 51
0
    def onReceivedElement(self, element):
        """
        This is called by the transport's ElementReader to process an
        entire received Data or Interest element.

        :param element: The bytes of the incoming element.
        :type element: An array type with int elements
        """

        lpPacket = None
        if element[0] == Tlv.LpPacket_LpPacket:
            # Decode the LpPacket and replace element with the fragment.
            lpPacket = LpPacket()
            # Set copy False so that the fragment is a slice which will be
            # copied below. The header fields are all integers and don't need to
            # be copied.
            TlvWireFormat.get().decodeLpPacket(lpPacket, element, False)
            element = lpPacket.getFragmentWireEncoding().buf()

        # First, decode as Interest or Data.
        interest = None
        data = None
        decoder = TlvDecoder(element)
        if decoder.peekType(Tlv.Interest, len(element)):
            interest = Interest()
            interest.wireDecode(element, TlvWireFormat.get())

            if lpPacket != None:
                interest.setLpPacket(lpPacket)
        elif decoder.peekType(Tlv.Data, len(element)):
            data = Data()
            data.wireDecode(element, TlvWireFormat.get())

            if lpPacket != None:
                data.setLpPacket(lpPacket)

        if lpPacket != None:
            # We have decoded the fragment, so remove the wire encoding to save
            #   memory.
            lpPacket.setFragmentWireEncoding(Blob())

            networkNack = NetworkNack.getFirstHeader(lpPacket)
            if networkNack != None:
                if interest == None:
                    # We got a Nack but not for an Interest, so drop the packet.
                    return

                pendingInterests = []
                self._pendingInterestTable.extractEntriesForNackInterest(
                  interest, pendingInterests)
                for pendingInterest in pendingInterests:
                    try:
                        pendingInterest.getOnNetworkNack()(
                          pendingInterest.getInterest(), networkNack)
                    except:
                        logging.exception("Error in onNetworkNack")

                # We have processed the network Nack packet.
                return

        # Now process as Interest or Data.
        if interest != None:
            # Call all interest filter callbacks which match.
            matchedFilters = []
            self._interestFilterTable.getMatchedFilters(interest, matchedFilters)
            for i in range(len(matchedFilters)):
                entry = matchedFilters[i]
                includeFilter = True
                onInterestCall = entry.getOnInterest()
                # If onInterest is not a function nor a method assumes it is a
                # calleable object
                if (not inspect.isfunction(onInterestCall) and
                    not inspect.ismethod(onInterestCall)):
                    onInterestCall = onInterestCall.__call__
                # Use getcallargs to test if onInterest accepts 5 args.
                try:
                    inspect.getcallargs(onInterestCall,
                      None, None, None, None, None)
                except TypeError:
                    # Assume onInterest is old-style with 4 arguments.
                    includeFilter = False

                if includeFilter:
                    try:
                        entry.getOnInterest()(
                          entry.getFilter().getPrefix(), interest,
                          entry.getFace(), entry.getInterestFilterId(),
                          entry.getFilter())
                    except:
                        logging.exception("Error in onInterest")
                else:
                    # Old-style onInterest without the filter argument. We
                    # still pass a Face instead of Transport since Face also
                    # has a send method.
                    try:
                        entry.getOnInterest()(
                          entry.getFilter().getPrefix(), interest,
                          entry.getFace(), entry.getInterestFilterId())
                    except:
                        logging.exception("Error in onInterest")
        elif data != None:
            pendingInterests = []
            self._pendingInterestTable.extractEntriesForExpressedInterest(
              data, pendingInterests)
            for pendingInterest in pendingInterests:
                try:
                    pendingInterest.getOnData()(pendingInterest.getInterest(), data)
                except:
                    logging.exception("Error in onData")
Ejemplo n.º 52
0
class Node(object):
    """
    Create a new Node for communication with an NDN hub with the given Transport
    object and connectionInfo.

    :param Transport transport: An object of a subclass of Transport used for
      communication.
    :param Transport.ConnectionInfo connectionInfo: An object of a subclass of
      Transport.ConnectionInfo to be used to connect to the transport.
    """
    def __init__(self, transport, connectionInfo):
        self._transport = transport
        self._connectionInfo = connectionInfo
        # An array of _PendingInterest
        self._pendingInterestTable = []
        # An array of _RegisteredPrefix
        self._registeredPrefixTable = []
        # An array of _InterestFilterEntry
        self._interestFilterTable = []
        # An array of _DelayedCall
        self._delayedCallTable = []
        # An array of function objects
        self._onConnectedCallbacks = []
        self._ndndIdFetcherInterest = Interest(
          Name("/%C1.M.S.localhost/%C1.M.SRV/ndnd/KEY"))
        self._ndndIdFetcherInterest.setInterestLifetimeMilliseconds(4000.0)
        self._ndndId = None
        self._commandInterestGenerator = CommandInterestGenerator()
        self._timeoutPrefix = Name("/local/timeout")
        self._lastEntryId = 0
        self._lastEntryIdLock = threading.Lock()
        self._connectStatus = Node._ConnectStatus.UNCONNECTED

    def expressInterest(
      self, pendingInterestId, interestCopy, onData, onTimeout, wireFormat, face):
        """
        Send the Interest through the transport, read the entire response and
        call onData(interest, data).

        :param int pendingInterestId: The getNextEntryId() for the pending
          interest ID which Face got so it could return it to the caller.
        :param Interest interestCopy: The Interest which is NOT copied for this
          internal Node method.  The Face expressInterest is responsible for
          making a copy for Node to use.
        :param onData: A function object to call when a matching data packet is
          received.
        :type onData: function object
        :param onTimeout: A function object to call if the interest times out.
          If onTimeout is None, this does not use it.
        :type onTimeout: function object
        :param wireFormat: A WireFormat object used to encode the message.
        :type wireFormat: a subclass of WireFormat
        :param Face face: The face which has the callLater method, used for
          interest timeouts. The callLater method may be overridden in a
          subclass of Face.
        :throws: RuntimeError If the encoded interest size exceeds
          getMaxNdnPacketSize().
        """
        # TODO: Properly check if we are already connected to the expected host.
        if self._connectStatus == self._ConnectStatus.CONNECT_COMPLETE:
            # We are connected. Simply send the interest.
            self._expressInterestHelper(
              pendingInterestId, interestCopy, onData, onTimeout, wireFormat,
              face)
            return

        if self._connectStatus == Node._ConnectStatus.UNCONNECTED:
            self._connectStatus = Node._ConnectStatus.CONNECT_REQUESTED

            # expressInterestHelper will be called by onConnected.
            self._onConnectedCallbacks.append(
              lambda: self._expressInterestHelper
                (pendingInterestId, interestCopy, onData, onTimeout, wireFormat,
                 face))

            def onConnected():
                # Assume that further calls to expressInterest dispatched to the
                # event loop are queued and won't enter expressInterest until
                # this method completes and sets CONNECT_COMPLETE.
                # Call each callback added while the connection was opening.
                for onConnectedCallback in self._onConnectedCallbacks:
                    onConnectedCallback()
                self._onConnectedCallbacks = []

                # Make future calls to expressInterest send directly to the
                # Transport.
                self._connectStatus = Node._ConnectStatus.CONNECT_COMPLETE
                    
            self._transport.connect(self._connectionInfo, self, onConnected)
        elif self._connectStatus == self._ConnectStatus.CONNECT_REQUESTED:
            # Still connecting. add to the interests to express by onConnected.
            self._onConnectedCallbacks.append(
              lambda: self._expressInterestHelper
                (pendingInterestId, interestCopy, onData, onTimeout, wireFormat,
                 face))
        else:
            # Don't expect this to happen.
            raise RuntimeError(
              "Node: Unrecognized _connectStatus " + str(self._connectStatus))

    def removePendingInterest(self, pendingInterestId):
        """
        Remove the pending interest entry with the pendingInterestId from the
        pending interest table. This does not affect another pending interest
        with a different pendingInterestId, even if it has the same interest
        name. If there is no entry with the pendingInterestId, do nothing.

        :param int pendingInterestId: The ID returned from expressInterest.
        """
        count = 0
        # Go backwards through the list so we can erase entries.
        # Remove all entries even though pendingInterestId should be unique.
        i = len(self._pendingInterestTable) - 1
        while i >= 0:
            if (self._pendingInterestTable[i].getPendingInterestId() ==
                  pendingInterestId):
                count += 1
                # For efficiency, mark this as removed so that
                # _processInterestTimeout doesn't look for it.
                self._pendingInterestTable[i].setIsRemoved()
                self._pendingInterestTable.pop(i)
            i -= 1

        if count == 0:
            logging.getLogger(__name__).debug(
              "removePendingInterest: Didn't find pendingInterestId " + pendingInterestId)

    def makeCommandInterest(self, interest, keyChain, certificateName, wireFormat):
        """
        Append a timestamp component and a random value component to interest's
        name. Then use the keyChain and certificateName to sign the interest.
        If the interest lifetime is not set, this sets it.

        :param Interest interest: The interest whose name is append with
          components.
        :param KeyChain keyChain: The KeyChain for calling sign.
        :param Name certificateName: The certificate name of the key to use for
          signing.
        :param wireFormat: A WireFormat object used to encode the
          SignatureInfo and to encode the interest name for signing.
        :type wireFormat: A subclass of WireFormat
        """
        self._commandInterestGenerator.generate(
          interest, keyChain, certificateName, wireFormat)

    def registerPrefix(
      self, registeredPrefixId, prefixCopy, onInterest, onRegisterFailed, flags,
      wireFormat, commandKeyChain, commandCertificateName, face):
        """
        Register prefix with the connected NDN hub and call onInterest when a
        matching interest is received.

        :param int registeredPrefixId: The getNextEntryId() for the registered
          prefix ID which Face got so it could return it to the caller.
        :param Name prefixCopy: The Name for the prefix to register which is NOT
          copied for this internal Node method. The Face registerPrefix is
          responsible for making a copy for Node to use.
        :param onInterest: (optional) If not None, this creates an interest
          filter from prefixCopy so that when an Interest is received which matches
          the filter, this calls
          onInterest(prefix, interest, face, interestFilterId, filter).
          NOTE: You must not change the prefix or filter objects - if you need to
          change them then make a copy. If onInterest is None, it is ignored and
          you must call setInterestFilter.
        :type onInterest: function object
        :param onRegisterFailed: A function object to call if failed to retrieve
          the connected hub's ID or failed to register the prefix.
        :type onRegisterFailed: function object
        :param ForwardingFlags flags: The flags for finer control of which
          interests are forwardedto the application.
        :param wireFormat: A WireFormat object used to encode the message.
        :type wireFormat: a subclass of WireFormat
        :param KeyChain commandKeyChain: The KeyChain object for signing
          interests. If null, assume we are connected to a legacy NDNx forwarder.
        :param Name commandCertificateName: The certificate name for signing
          interests.
        :param Face face: The face which is passed to the onInterest callback.
          If onInterest is None, this is ignored.
        """
        # If we have an _ndndId, we know we already connected to NDNx.
        if self._ndndId != None or commandKeyChain == None:
            # Assume we are connected to a legacy NDNx server.
            if not WireFormat.ENABLE_NDNX:
                raise RuntimeError(
                  "registerPrefix with NDNx is deprecated. To enable while you upgrade your code to use NFD, set WireFormat.ENABLE_NDNX = True")

            if self._ndndId == None:
                # First fetch the ndndId of the connected hub.
                fetcher = Node._NdndIdFetcher(
                  self, registeredPrefixId, prefixCopy, onInterest, onRegisterFailed,
                  flags, wireFormat, face)
                # We send the interest using the given wire format so that the hub
                # receives (and sends) in the application's desired wire format.
                self.expressInterest(
                  self.getNextEntryId(), self._ndndIdFetcherInterest,
                  fetcher.onData, fetcher.onTimeout, wireFormat, face)
            else:
                self._registerPrefixHelper(
                  registeredPrefixId, prefixCopy, onInterest, onRegisterFailed,
                  flags, wireFormat, face)
        else:
            # The application set the KeyChain for signing NFD interests.
            self._nfdRegisterPrefix(
              registeredPrefixId, prefixCopy, onInterest,
              onRegisterFailed, flags, commandKeyChain, commandCertificateName,
              face)

    def removeRegisteredPrefix(self, registeredPrefixId):
        """
        Remove the registered prefix entry with the registeredPrefixId from the
        registered prefix table. This does not affect another registered prefix
        with a different registeredPrefixId, even if it has the same prefix
        name. If an interest filter was automatically created by registerPrefix,
        also remove it. If there is no entry with the registeredPrefixId, do
        nothing.

        :param int registeredPrefixId: The ID returned from registerPrefix.
        """
        count = 0
        # Go backwards through the list so we can erase entries.
        # Remove all entries even though registeredPrefixId should be unique.
        i = len(self._registeredPrefixTable) - 1
        while i >= 0:
            entry = self._registeredPrefixTable[i]
            if (entry.getRegisteredPrefixId() == registeredPrefixId):
                count += 1

                if entry.getRelatedInterestFilterId() > 0:
                    # Remove the related interest filter.
                    self.unsetInterestFilter(entry.getRelatedInterestFilterId())

                self._registeredPrefixTable.pop(i)
            i -= 1

        if count == 0:
            logging.getLogger(__name__).debug(
              "removeRegisteredPrefix: Didn't find registeredPrefixId " + registeredPrefixId)

    def setInterestFilter(self, interestFilterId, filterCopy, onInterest, face):
        """
        Add an entry to the local interest filter table to call the onInterest
        callback for a matching incoming Interest. This method only modifies the
        library's local callback table and does not register the prefix with the
        forwarder. It will always succeed. To register a prefix with the
        forwarder, use registerPrefix.

        :param int interestFilterId: The getNextEntryId() for the interest
          filter ID which Face got so it could return it to the caller.
        :param InterestFilter filterCopy: The InterestFilter with a prefix and
          optional regex filter used to match the name of an incoming Interest,
          which is NOT copied for this internal Node method. The Face
          setInterestFilter is responsible for making a copy for Node to use.
        :param onInterest: When an Interest is received which matches the filter,
          this calls onInterest(prefix, interest, face, interestFilterId, filter).
        :type onInterest: function object
        :param Face face: The face which is passed to the onInterest callback.
        """
        self._interestFilterTable.append(Node._InterestFilterEntry
          (interestFilterId, filterCopy, onInterest, face))

    def unsetInterestFilter(self, interestFilterId):
        """
        Remove the interest filter entry which has the interestFilterId from the
        interest filter table. This does not affect another interest filter with
        a different interestFilterId, even if it has the same prefix name. If
        there is no entry with the interestFilterId, do nothing.

        :param int interestFilterId: The ID returned from setInterestFilter.
        """
        count = 0
        # Go backwards through the list so we can erase entries.
        # Remove all entries even though interestFilterId should be unique.
        i = len(self._interestFilterTable) - 1
        while i >= 0:
            if (self._interestFilterTable[i].getInterestFilterId() ==
                  interestFilterId):
                count += 1
                self._interestFilterTable.pop(i)
            i -= 1

        if count == 0:
            logging.getLogger(__name__).debug(
              "unsetInterestFilter: Didn't find interestFilterId " + interestFilterId)

    def send(self, encoding):
        """
        Send the encoded packet out through the transport.

        :param encoding: The array of bytes for the encoded packet to send.
        :type encoding: An array type with int elements
        :throws: RuntimeError If the packet size exceeds getMaxNdnPacketSize().
        """
        if len(encoding) > self.getMaxNdnPacketSize():
            raise RuntimeError(
              "The encoded packet size exceeds the maximum limit getMaxNdnPacketSize()")

        self._transport.send(encoding)

    def processEvents(self):
        """
        Process any packets to receive and call callbacks such as onData,
        onInterest or onTimeout. This returns immediately if there is no data to
        receive. This blocks while calling the callbacks. You should repeatedly
        call this from an event loop, with calls to sleep as needed so that the
        loop doesn't use 100% of the CPU. Since processEvents modifies the pending
        interest table, your application should make sure that it calls
        processEvents in the same thread as expressInterest (which also modifies
        the pending interest table).

        :raises: This may raise an exception for reading data or in the callback
          for processing the data.  If you call this from an main event loop,
          you may want to catch and log/disregard all exceptions.
        """
        self._transport.processEvents()

        # Check for delayed calls. Since callLater does a sorted insert into
        # _delayedCallTable, the check for timeouts is quick and does not
        # require searching the entire table. If callLater is overridden to use
        # a different mechanism, then processEvents is not needed to check for
        # delayed calls.
        now = Common.getNowMilliseconds()
        # _delayedCallTable is sorted on _callTime, so we only need to process
        # the timed-out entries at the front, then quit.
        while (len(self._delayedCallTable) > 0 and
               self._delayedCallTable[0].getCallTime() <= now):
            delayedCall = self._delayedCallTable[0]
            del self._delayedCallTable[0]
            delayedCall.callCallback()

    def getTransport(self):
        """
        Get the transport object given to the constructor.

        :return: The transport object.
        :rtype: Transport
        """
        return self._transport

    def getConnectionInfo(self):
        """
        Get the connectionInfo object given to the constructor.

        :return: The connectionInfo object.
        :rtype: Transport.ConnectionInfo
        """
        return self._connectionInfo

    def onReceivedElement(self, element):
        """
        This is called by the transport's ElementReader to process an
        entire received Data or Interest element.

        :param element: The bytes of the incoming element.
        :type element: An array type with int elements
        """
        # The type codes for TLV Interest and Data packets are chosen to not
        #   conflict with the first byte of a binary XML packet, so we canjust
        #   look at the first byte.
        if not (element[0] == Tlv.Interest or element[0] == Tlv.Data):
            # Ignore non-TLV elements.
            # Assume it is Binary XML.
            if not WireFormat.ENABLE_NDNX:
                raise RuntimeError(
                  "BinaryXmlWireFormat (NDNx) is deprecated. To enable while you upgrade your network to use NDN-TLV, set WireFormat.ENABLE_NDNX = True")

            return

        # First, decode as Interest or Data.
        interest = None
        data = None
        decoder = TlvDecoder(element)
        if decoder.peekType(Tlv.Interest, len(element)):
            interest = Interest()
            interest.wireDecode(element, TlvWireFormat.get())
        elif decoder.peekType(Tlv.Data, len(element)):
            data = Data()
            data.wireDecode(element, TlvWireFormat.get())

        # Now process as Interest or Data.
        if interest != None:
            # Call all interest filter callbacks which match.
            for i in range(len(self._interestFilterTable)):
                entry = self._interestFilterTable[i]
                if entry.getFilter().doesMatch(interest.getName()):
                    includeFilter = True
                    # Use getcallargs to test if onInterest accepts 5 args.
                    try:
                        inspect.getcallargs(entry.getOnInterest(),
                          None, None, None, None, None)
                    except TypeError:
                        # Assume onInterest is old-style with 4 arguments.
                        includeFilter = False

                    if includeFilter:
                        entry.getOnInterest()(
                          entry.getFilter().getPrefix(), interest,
                          entry.getFace(), entry.getInterestFilterId(),
                          entry.getFilter())
                    else:
                        # Old-style onInterest without the filter argument. We
                        # still pass a Face instead of Transport since Face also
                        # has a send method.
                        entry.getOnInterest()(
                          entry.getFilter().getPrefix(), interest,
                          entry.getFace(), entry.getInterestFilterId())
        elif data != None:
            pendingInterests = self._extractEntriesForExpressedInterest(
              data.getName())
            for pendingInterest in pendingInterests:
                pendingInterest.getOnData()(pendingInterest.getInterest(), data)

    def isLocal(self):
        """
        Check if the face is local based on the current connection through the
        Transport; some Transport may cause network I/O (e.g. an IP host name
        lookup).

        :return: True if the face is local, False if not.
        :rtype bool:
        """
        return self._transport.isLocal(self._connectionInfo)

    def shutdown(self):
        """
        Call getTransport().close().
        """
        self._transport.close()

    @staticmethod
    def getMaxNdnPacketSize():
        """
        Get the practical limit of the size of a network-layer packet. If a packet
        is larger than this, the library or application MAY drop it.

        :return: The maximum NDN packet size.
        :rtype: int
        """
        return Common.MAX_NDN_PACKET_SIZE

    def _expressInterestHelper(
      self, pendingInterestId, interestCopy, onData, onTimeout, wireFormat, face):
        """
        Do the work of expressInterest once we know we are connected. Add the
        entry to the PIT, encode and send the interest.

        :param int pendingInterestId: The getNextEntryId() for the pending
          interest ID which Face got so it could return it to the caller.
        :param Interest interestCopy: The Interest to send, which has already
          been copied.
        :param onData: A function object to call when a matching data packet is
          received.
        :type onData: function object
        :param onTimeout: A function object to call if the interest times out.
          If onTimeout is None, this does not use it.
        :type onTimeout: function object
        :param wireFormat: A WireFormat object used to encode the message.
        :type wireFormat: a subclass of WireFormat
        :param Face face: The face which has the callLater method, used for
          interest timeouts. The callLater method may be overridden in a
          subclass of Face.
        :throws: RuntimeError If the encoded interest size exceeds
          getMaxNdnPacketSize().
        """
        pendingInterest = Node._PendingInterest(
          pendingInterestId, interestCopy, onData, onTimeout)
        self._pendingInterestTable.append(pendingInterest)
        if (interestCopy.getInterestLifetimeMilliseconds() != None and
            interestCopy.getInterestLifetimeMilliseconds() >= 0.0):
            # Set up the timeout.
            face.callLater(interestCopy.getInterestLifetimeMilliseconds(),
                           lambda: self._processInterestTimeout(pendingInterest))

        # Special case: For _timeoutPrefix we don't actually send the interest.
        if not self._timeoutPrefix.match(interestCopy.getName()):
            encoding = interestCopy.wireEncode(wireFormat)
            if encoding.size() > self.getMaxNdnPacketSize():
                raise RuntimeError(
                  "The encoded interest size exceeds the maximum limit getMaxNdnPacketSize()")

            self._transport.send(encoding.toBuffer())

    def _extractEntriesForExpressedInterest(self, name):
        """
        Find all entries from the _pendingInterestTable where the name conforms
        to the entry's interest selectors, remove the entries from the table
        and return them.

        :param Name name: The name to find the interest for (from the incoming
          data packet).
        :return: The matching entries from the _pendingInterestTable, or []
          if none are found.
        :rtype: array of _PendingInterest
        """
        result = []

        # Go backwards through the list so we can erase entries.
        i = len(self._pendingInterestTable) - 1
        while i >= 0:
            pendingInterest = self._pendingInterestTable[i]

            if pendingInterest.getInterest().matchesName(name):
                result.append(pendingInterest)
                # We let the callback from callLater call _processInterestTimeout,
                # but for efficiency, mark this as removed so that it returns
                # right away.
                self._pendingInterestTable.pop(i)
                pendingInterest.setIsRemoved()
            i -= 1

        return result

    def _registerPrefixHelper(
      self, registeredPrefixId, prefix, onInterest, onRegisterFailed, flags,
      wireFormat, face):
        """
        Do the work of registerPrefix to register with NDNx once we have an
        _ndndId.

        :param int registeredPrefixId: The getNextEntryId() which registerPrefix
          got so it could return it to the caller. If this is 0, then don't add
          to _registeredPrefixTable (assuming it has already been done).
        """
        if not WireFormat.ENABLE_NDNX:
            # We can get here if the command signing info is set, but running NDNx.
            raise RuntimeError(
              "registerPrefix with NDNx is deprecated. To enable while you upgrade your code to use NFD, set WireFormat.ENABLE_NDNX = True")

        # Create a ForwardingEntry.
        # Note: ndnd ignores any freshness that is larger than 3600 seconds and
        #   sets 300 seconds instead. To register "forever", (=2000000000 sec),
        #   the freshness period must be omitted.
        forwardingEntry = ForwardingEntry()
        forwardingEntry.setAction("selfreg")
        forwardingEntry.setPrefix(prefix)
        forwardingEntry.setForwardingFlags(flags)
        content = forwardingEntry.wireEncode(wireFormat)

        # Set the ForwardingEntry as the content of a Data packet and sign.
        data = Data()
        data.setContent(content)
        # Set the name to a random value so that each request is unique.
        nonce = bytearray(4)
        for i in range(len(nonce)):
            nonce[i] = _systemRandom.randint(0, 0xff)
        data.getName().append(nonce)
        # The ndnd ignores the signature, so set to blank values.
        data.getSignature().getKeyLocator().setType(
          KeyLocatorType.KEY_LOCATOR_DIGEST)
        data.getSignature().getKeyLocator().setKeyData(
          Blob(bytearray(32), False))
        data.getSignature().setSignature(Blob(bytearray(128), False))
        encodedData = data.wireEncode(wireFormat)

        # Create an interest where the name has the encoded Data packet.
        interestName = Name().append("ndnx").append(self._ndndId).append(
          "selfreg").append(encodedData)

        interest = Interest(interestName)
        interest.setInterestLifetimeMilliseconds(4000.0)
        interest.setScope(1)

        if registeredPrefixId != 0:
            interestFilterId = 0
            if onInterest != None:
                # registerPrefix was called with the "combined" form that includes
                # the callback, so add an InterestFilterEntry.
                interestFilterId = self.getNextEntryId()
                self.setInterestFilter(
                  interestFilterId, InterestFilter(prefix), onInterest, face)

            self._registeredPrefixTable.append(Node._RegisteredPrefix(
              registeredPrefixId, prefix, interestFilterId))

        # Send the registration interest.
        response = Node._RegisterResponse(
          self, prefix, onInterest, onRegisterFailed, flags, wireFormat, False,
          face)
        self.expressInterest(
          self.getNextEntryId(), interest, response.onData, response.onTimeout,
          wireFormat, face)

    def _nfdRegisterPrefix(
      self, registeredPrefixId, prefix, onInterest, onRegisterFailed, flags,
      commandKeyChain, commandCertificateName, face):
        """
        Do the work of registerPrefix to register with NFD.

        :param int registeredPrefixId: The getNextEntryId() which registerPrefix
          got so it could return it to the caller. If this is 0, then don't add
          to _registeredPrefixTable (assuming it has already been done).
        """
        if commandKeyChain == None:
            raise RuntimeError(
              "registerPrefix: The command KeyChain has not been set. You must call setCommandSigningInfo.")
        if commandCertificateName.size() == 0:
            raise RuntimeError(
              "registerPrefix: The command certificate name has not been set. You must call setCommandSigningInfo.")

        controlParameters = ControlParameters()
        controlParameters.setName(prefix)
        controlParameters.setForwardingFlags(flags)

        commandInterest = Interest()
        if self.isLocal():
            commandInterest.setName(Name("/localhost/nfd/rib/register"))
            # The interest is answered by the local host, so set a short timeout.
            commandInterest.setInterestLifetimeMilliseconds(2000.0)
        else:
            commandInterest.setName(Name("/localhop/nfd/rib/register"))
            # The host is remote, so set a longer timeout.
            commandInterest.setInterestLifetimeMilliseconds(4000.0)
        # NFD only accepts TlvWireFormat packets.
        commandInterest.getName().append(controlParameters.wireEncode(TlvWireFormat.get()))
        self.makeCommandInterest(
          commandInterest, commandKeyChain, commandCertificateName,
          TlvWireFormat.get())

        if registeredPrefixId != 0:
            interestFilterId = 0
            if onInterest != None:
                # registerPrefix was called with the "combined" form that includes
                # the callback, so add an InterestFilterEntry.
                interestFilterId = self.getNextEntryId()
                self.setInterestFilter(
                  interestFilterId, InterestFilter(prefix), onInterest, face)

            self._registeredPrefixTable.append(Node._RegisteredPrefix(
              registeredPrefixId, prefix, interestFilterId))

        # Send the registration interest.
        response = Node._RegisterResponse(
          self, prefix, onInterest, onRegisterFailed, flags,
          TlvWireFormat.get(), True, face)
        self.expressInterest(
          self.getNextEntryId(), commandInterest, response.onData,
          response.onTimeout, TlvWireFormat.get(), face)

    def callLater(self, delayMilliseconds, callback):
        """
        Call callback() after the given delay. This adds to
        self._delayedCallTable which is used by processEvents().

        :param float delayMilliseconds: The delay in milliseconds.
        :param callback: This calls callback() after the delay.
        :type callback: function object
        """
        delayedCall = Node._DelayedCall(delayMilliseconds, callback)
        # Insert into _delayedCallTable, sorted on delayedCall.getCallTime().
        # Search from the back since we expect it to go there.
        i = len(self._delayedCallTable) - 1
        while i >= 0:
            if (self._delayedCallTable[i].getCallTime() <= delayedCall.getCallTime()):
                break
            i -= 1

        # Element i is the greatest less than or equal to
        # delayedCall.getCallTime(), so insert after it.
        self._delayedCallTable.insert(i + 1, delayedCall)

    def _processInterestTimeout(self, pendingInterest):
        """
        This is used in callLater for when the pending interest expires. If
        the pendingInterest is still in the _pendingInterestTable, remove it and
        call its onTimeout callback.
        """
        if pendingInterest.getIsRemoved():
            # _extractEntriesForExpressedInterest or removePendingInterest has
            # removed pendingInterest from _pendingInterestTable, so we don't
            # need to look for it. Do nothing.
            return

        try:
            index = self._pendingInterestTable.index(pendingInterest)
        except ValueError:
            # The pending interest has been removed. Do nothing.
            return

        del self._pendingInterestTable[index]
        pendingInterest.callTimeout()

    def getNextEntryId(self):
        """
        Get the next unique entry ID for the pending interest table, interest
        filter table, etc. This uses a threading.Lock() to be thread safe. Most
        entry IDs are for the pending interest table (there usually are not many
        interest filter table entries) so we use a common pool to only have to
        do the thread safe lock in one method which is called by Face.

        :return: The next entry ID.
        :rtype: int
        """
        with self._lastEntryIdLock:
            self._lastEntryId += 1
            return self._lastEntryId

    class _ConnectStatus(object):
        UNCONNECTED = 1
        CONNECT_REQUESTED = 2
        CONNECT_COMPLETE = 3

    class _DelayedCall(object):
        """
        _DelayedCall is a private class for the members of the _delayedCallTable.
        Create a new _DelayedCall and set the call time based on the current
        time and the delayMilliseconds.

        :param float delayMilliseconds: The delay in milliseconds.
        :param callback: This calls callback() after the delay.
        :type callback: function object
        """
        def __init__(self, delayMilliseconds, callback):
            self._callback = callback
            self._callTime = Common.getNowMilliseconds() + delayMilliseconds

        def getCallTime(self):
            """
            Get the time at which the callback should be called.

            :return: The call time in milliseconds, similar to
              Common.getNowMilliseconds().
            :rtype: float
            """
            return self._callTime

        def callCallback(self):
            """
            Call the callback given to the constructor. This does not catch
            exceptions.
            """
            self._callback()

    class _PendingInterest(object):
        """
        _PendingInterest is a private class for the members of the
        _pendingInterestTable.

        :param int pendingInterestId: A unique ID for this entry, which you
          should get with getNextEntryId().
        :param Interest interest: The interest.
        :param onData: A function object to call when a matching data packet is
          received.
        :type onData: function object
        :param onTimeout: A function object to call if the interest times out.
          If onTimeout is None, this does not use it.
        :type onTimeout: function object
        """
        def __init__(self, pendingInterestId, interest, onData, onTimeout):
            self._pendingInterestId = pendingInterestId
            self._interest = interest
            self._onData = onData
            self._onTimeout = onTimeout
            self._isRemoved = False

        def getPendingInterestId(self):
            """
            Get the pendingInterestId given to the constructor.

            :return: The pending interest ID.
            :rtype: int
            """
            return self._pendingInterestId

        def getInterest(self):
            """
            Get the interest given to the constructor.

            :return: The interest.
            :rtype: int
            """
            return self._interest

        def getOnData(self):
            """
            Get the onData function object given to the constructor.

            :return: The onData function object.
            :rtype: function object
            """
            return self._onData

        def callTimeout(self):
            """
            Call _onTimeout (if defined).  This ignores exceptions from
            _onTimeout.
            """
            if self._onTimeout:
                # Ignore all exceptions.
                try:
                    self._onTimeout(self._interest)
                except:
                    pass

        def setIsRemoved(self):
            """
            Set the isRemoved flag which is returned by getIsRemoved().
            """
            self._isRemoved = True

        def getIsRemoved(self):
            """
            Check if setIsRemoved() was called.

            :return: True if setIsRemoved() was called.
            :rtype: bool
            """
            return self._isRemoved

    class _RegisteredPrefix(object):
        """
        A _RegisteredPrefix holds a registeredPrefixId and information necessary
        to remove the registration later. It optionally holds a related
        interestFilterId if the InterestFilter was set in the same
        registerPrefix operation.

        :param int registeredPrefixId: A unique ID for this entry, which you
          should get with getNextEntryId().
        :param Name prefix: The name prefix.
        :param int relatedInterestFilterId: (optional) The related
          interestFilterId for the filter set in the same registerPrefix
          operation. If omitted, set to 0.
        """
        def __init__(self, registeredPrefixId, prefix, relatedInterestFilterId):
            self._registeredPrefixId = registeredPrefixId
            self._prefix = prefix
            self._relatedInterestFilterId = relatedInterestFilterId

        def getRegisteredPrefixId(self):
            """
            Get the registeredPrefixId given to the constructor.

            :return: The registered prefix ID.
            :rtype: int
            """
            return self._registeredPrefixId

        def getPrefix(self):
            """
            Get the name prefix to the constructor.

            :return: The name prefix.
            :rtype: Name
            """
            return self._prefix

        def getRelatedInterestFilterId(self):
            """
            Get the related interestFilterId given to the constructor.

            :return: The related interestFilterId.
            :rtype: int
            """
            return self._relatedInterestFilterId

    class _InterestFilterEntry(object):
        """
        An _InterestFilterEntry holds an interestFilterId, an InterestFilter
        and the OnInterestCallback with its related Face.
        Create a new InterestFilterEntry with the given values.

        :param int interestFilterId: The ID from getNextEntryId().
        :param InterestFilter filter: The InterestFilter for this entry.
        :param onInterest: The callback to call.
        :type onInterest: function object
        :param Face face: The face on which was called registerPrefix or
          setInterestFilter which is passed to the onInterest callback.
        """
        def __init__(self, interestFilterId, filter, onInterest, face):
            self._interestFilterId = interestFilterId
            self._filter = filter
            self._onInterest = onInterest
            self._face = face

        def getInterestFilterId(self):
            """
            Get the interestFilterId given to the constructor.

            :return: The interestFilterId.
            :rtype: int
            """
            return self._interestFilterId

        def getFilter(self):
            """
            Get the InterestFilter given to the constructor.

            :return: The InterestFilter.
            :rtype: InterestFilter
            """
            return self._filter

        def getOnInterest(self):
            """
            Get the OnInterestCallback given to the constructor.

            :return: The OnInterestCallback.
            :rtype: function object
            """
            return self._onInterest

        def getFace(self):
            """
            Get the Face given to the constructor.

            :return: The Face.
            :rtype: Face
            """
            return self._face

    class _NdndIdFetcher(object):
        """
        An _NdndIdFetcher receives the Data packet with the publisher public key
        digest for the connected NDN hub.
        """
        def __init__(self, node, registeredPrefixId, prefix, onInterest,
                     onRegisterFailed, flags, wireFormat, face):
            self._node = node
            self._registeredPrefixId = registeredPrefixId
            self._prefix = prefix
            self._onInterest = onInterest
            self._onRegisterFailed = onRegisterFailed
            self._flags = flags
            self._wireFormat = wireFormat
            self._face = face

        def onData(self, interest, ndndIdData):
            """
            We received the ndnd ID.
            """
            # Assume that the content is a DER encoded public key of the ndnd.
            #   Do a quick check that the first byte is for DER encoding.
            if (ndndIdData.getContent().size() < 1 or
                  ndndIdData.getContent().buf()[0] != 0x30):
                logging.getLogger(__name__).info(
                  "Register prefix failed: The content returned when fetching the NDNx ID does not appear to be a public key")
                self._onRegisterFailed(self._prefix)
                return

            # Get the digest of the public key.
            digest = bytearray(
              hashlib.sha256(ndndIdData.getContent().toBuffer()).digest())

            # Set the _ndndId and continue.
            # TODO: If there are multiple connected hubs, the NDN ID is really
            #   stored per connected hub.
            self._node._ndndId = Blob(digest, False)
            self._node._registerPrefixHelper(
              self._registeredPrefixId, self._prefix, self._onInterest,
              self._onRegisterFailed, self._flags, self._wireFormat, self._face)

        def onTimeout(self, interest):
            """
            We timed out fetching the ndnd ID.
            """
            logging.getLogger(__name__).info(
              "Register prefix failed: Timeout fetching the NDNx ID")
            self._onRegisterFailed(self._prefix)

    class _RegisterResponse(object):
        """
        A _RegisterResponse receives the response Data packet from the register
        prefix interest sent to the connected NDN hub. If this gets a bad
        response or a timeout, call onRegisterFailed.
        """
        def __init__(self, node, prefix, onInterest, onRegisterFailed, flags,
                     wireFormat, isNfdCommand, face):
            self._node = node
            self._prefix = prefix
            self._onInterest = onInterest
            self._onRegisterFailed = onRegisterFailed
            self._flags = flags
            self._wireFormat = wireFormat
            self._isNfdCommand = isNfdCommand
            self._face = face

        def onData(self, interest, responseData):
            """
            We received the response. Do a quick check of expected name
            components.
            """
            if self._isNfdCommand:
                # Decode responseData->getContent() and check for a success code.
                # TODO: Move this into the TLV code.
                statusCode = None
                try:
                    decoder = TlvDecoder(responseData.getContent().buf())
                    decoder.readNestedTlvsStart(Tlv.NfdCommand_ControlResponse)
                    statusCode = decoder.readNonNegativeIntegerTlv(Tlv.NfdCommand_StatusCode)
                except ValueError as ex:
                    logging.getLogger(__name__).info(
                      "Register prefix failed: Error decoding the NFD response: %s",
                      str(ex))
                    self._onRegisterFailed(self._prefix)
                    return

                # Status code 200 is "OK".
                if statusCode != 200:
                  logging.getLogger(__name__).info(
                    "Register prefix failed: Expected NFD status code 200, got: %d",
                    statusCode)
                  self._onRegisterFailed(self._prefix)
                  return

                logging.getLogger(__name__).info(
                  "Register prefix succeeded with the NFD forwarder for prefix %s",
                  self._prefix.toUri())
            else:
                expectedName = Name("/ndnx/.../selfreg")
                if (responseData.getName().size() < 4 or
                      responseData.getName()[0] != expectedName[0] or
                      responseData.getName()[2] != expectedName[2]):
                    logging.getLogger(__name__).info(
                      "Register prefix failed: Unexpected name in NDNx response: %s",
                      responseData.getName().toUri())
                    self._onRegisterFailed(self._prefix)
                    return

                logging.getLogger(__name__).info(
                  "Register prefix succeeded with the NDNx forwarder for prefix %s",
                  self._prefix.toUri())

        def onTimeout(self, interest):
            """
            We timed out waiting for the response.
            """
            if self._isNfdCommand:
                logging.getLogger(__name__).info(
                  "Timeout for NFD register prefix command. Attempting an NDNx command...")
                # The application set the commandKeyChain, but we may be
                #   connected to NDNx.
                if self._node._ndndId == None:
                    # First fetch the ndndId of the connected hub.
                    # Pass 0 for registeredPrefixId since the entry was already added to
                    #   _registeredPrefixTable on the first try.
                    fetcher = Node._NdndIdFetcher(
                      self._node, 0, self._prefix, self._onInterest,
                      self._onRegisterFailed, self._flags, self._wireFormat,
                      self._face)
                    # We send the interest using the given wire format so that the hub
                    # receives (and sends) in the application's desired wire format.
                    self._node.expressInterest(
                      self._node.getNextEntryId(), self._node._ndndIdFetcherInterest,
                      fetcher.onData, fetcher.onTimeout, self._wireFormat,
                      self._face)
                else:
                    # Pass 0 for registeredPrefixId since the entry was already
                    #   added to _registeredPrefixTable on the first try.
                    self._node._registerPrefixHelper(
                      0, self._prefix, self._onInterest, self._onRegisterFailed,
                      self._flags, self._wireFormat)
            else:
                # An NDNx command was sent because there is no commandKeyChain,
                #   so we can't try an NFD command. Or it was sent from this
                #   callback after trying an NFD command. Fail.
                logging.getLogger(__name__).info(
                  "Register prefix failed: Timeout waiting for the response from the register prefix interest")
                self._onRegisterFailed(self._prefix)
Ejemplo n.º 53
0
    def _registerPrefixHelper(
      self, registeredPrefixId, prefix, onInterest, onRegisterFailed, flags,
      wireFormat, face):
        """
        Do the work of registerPrefix to register with NDNx once we have an
        _ndndId.

        :param int registeredPrefixId: The getNextEntryId() which registerPrefix
          got so it could return it to the caller. If this is 0, then don't add
          to _registeredPrefixTable (assuming it has already been done).
        """
        if not WireFormat.ENABLE_NDNX:
            # We can get here if the command signing info is set, but running NDNx.
            raise RuntimeError(
              "registerPrefix with NDNx is deprecated. To enable while you upgrade your code to use NFD, set WireFormat.ENABLE_NDNX = True")

        # Create a ForwardingEntry.
        # Note: ndnd ignores any freshness that is larger than 3600 seconds and
        #   sets 300 seconds instead. To register "forever", (=2000000000 sec),
        #   the freshness period must be omitted.
        forwardingEntry = ForwardingEntry()
        forwardingEntry.setAction("selfreg")
        forwardingEntry.setPrefix(prefix)
        forwardingEntry.setForwardingFlags(flags)
        content = forwardingEntry.wireEncode(wireFormat)

        # Set the ForwardingEntry as the content of a Data packet and sign.
        data = Data()
        data.setContent(content)
        # Set the name to a random value so that each request is unique.
        nonce = bytearray(4)
        for i in range(len(nonce)):
            nonce[i] = _systemRandom.randint(0, 0xff)
        data.getName().append(nonce)
        # The ndnd ignores the signature, so set to blank values.
        data.getSignature().getKeyLocator().setType(
          KeyLocatorType.KEY_LOCATOR_DIGEST)
        data.getSignature().getKeyLocator().setKeyData(
          Blob(bytearray(32), False))
        data.getSignature().setSignature(Blob(bytearray(128), False))
        encodedData = data.wireEncode(wireFormat)

        # Create an interest where the name has the encoded Data packet.
        interestName = Name().append("ndnx").append(self._ndndId).append(
          "selfreg").append(encodedData)

        interest = Interest(interestName)
        interest.setInterestLifetimeMilliseconds(4000.0)
        interest.setScope(1)

        if registeredPrefixId != 0:
            interestFilterId = 0
            if onInterest != None:
                # registerPrefix was called with the "combined" form that includes
                # the callback, so add an InterestFilterEntry.
                interestFilterId = self.getNextEntryId()
                self.setInterestFilter(
                  interestFilterId, InterestFilter(prefix), onInterest, face)

            self._registeredPrefixTable.append(Node._RegisteredPrefix(
              registeredPrefixId, prefix, interestFilterId))

        # Send the registration interest.
        response = Node._RegisterResponse(
          self, prefix, onInterest, onRegisterFailed, flags, wireFormat, False,
          face)
        self.expressInterest(
          self.getNextEntryId(), interest, response.onData, response.onTimeout,
          wireFormat, face)
Ejemplo n.º 54
0
    def onReceivedElement(self, element):
        """
        This is called by the transport's ElementReader to process an
        entire received Data or Interest element.

        :param element: The bytes of the incoming element.
        :type element: An array type with int elements
        """
        # The type codes for TLV Interest and Data packets are chosen to not
        #   conflict with the first byte of a binary XML packet, so we canjust
        #   look at the first byte.
        if not (element[0] == Tlv.Interest or element[0] == Tlv.Data):
            # Ignore non-TLV elements.
            # Assume it is Binary XML.
            if not WireFormat.ENABLE_NDNX:
                raise RuntimeError(
                  "BinaryXmlWireFormat (NDNx) is deprecated. To enable while you upgrade your network to use NDN-TLV, set WireFormat.ENABLE_NDNX = True")

            return

        # First, decode as Interest or Data.
        interest = None
        data = None
        decoder = TlvDecoder(element)
        if decoder.peekType(Tlv.Interest, len(element)):
            interest = Interest()
            interest.wireDecode(element, TlvWireFormat.get())
        elif decoder.peekType(Tlv.Data, len(element)):
            data = Data()
            data.wireDecode(element, TlvWireFormat.get())

        # Now process as Interest or Data.
        if interest != None:
            # Call all interest filter callbacks which match.
            for i in range(len(self._interestFilterTable)):
                entry = self._interestFilterTable[i]
                if entry.getFilter().doesMatch(interest.getName()):
                    includeFilter = True
                    # Use getcallargs to test if onInterest accepts 5 args.
                    try:
                        inspect.getcallargs(entry.getOnInterest(),
                          None, None, None, None, None)
                    except TypeError:
                        # Assume onInterest is old-style with 4 arguments.
                        includeFilter = False

                    if includeFilter:
                        entry.getOnInterest()(
                          entry.getFilter().getPrefix(), interest,
                          entry.getFace(), entry.getInterestFilterId(),
                          entry.getFilter())
                    else:
                        # Old-style onInterest without the filter argument. We
                        # still pass a Face instead of Transport since Face also
                        # has a send method.
                        entry.getOnInterest()(
                          entry.getFilter().getPrefix(), interest,
                          entry.getFace(), entry.getInterestFilterId())
        elif data != None:
            pendingInterests = self._extractEntriesForExpressedInterest(
              data.getName())
            for pendingInterest in pendingInterests:
                pendingInterest.getOnData()(pendingInterest.getInterest(), data)
Ejemplo n.º 55
0
    def onReceivedElement(self, element):
        """
        This is called by the transport's ElementReader to process an
        entire received Data or Interest element.

        :param element: The bytes of the incoming element.
        :type element: An array type with int elements
        """

        lpPacket = None
        if element[0] == Tlv.LpPacket_LpPacket:
            # Decode the LpPacket and replace element with the fragment.
            lpPacket = LpPacket()
            # Set copy False so that the fragment is a slice which will be
            # copied below. The header fields are all integers and don't need to
            # be copied.
            TlvWireFormat.get().decodeLpPacket(lpPacket, element, False)
            element = lpPacket.getFragmentWireEncoding().buf()

        # First, decode as Interest or Data.
        interest = None
        data = None
        decoder = TlvDecoder(element)
        if decoder.peekType(Tlv.Interest, len(element)):
            interest = Interest()
            interest.wireDecode(element, TlvWireFormat.get())

            if lpPacket != None:
                interest.setLpPacket(lpPacket)
        elif decoder.peekType(Tlv.Data, len(element)):
            data = Data()
            data.wireDecode(element, TlvWireFormat.get())

            if lpPacket != None:
                data.setLpPacket(lpPacket)

        if lpPacket != None:
            # We have decoded the fragment, so remove the wire encoding to save
            #   memory.
            lpPacket.setFragmentWireEncoding(Blob())

            networkNack = NetworkNack.getFirstHeader(lpPacket)
            if networkNack != None:
                if interest == None:
                    # We got a Nack but not for an Interest, so drop the packet.
                    return

                pendingInterests = []
                self._pendingInterestTable.extractEntriesForNackInterest(
                  interest, pendingInterests)
                for pendingInterest in pendingInterests:
                    try:
                        pendingInterest.getOnNetworkNack()(
                          pendingInterest.getInterest(), networkNack)
                    except:
                        logging.exception("Error in onNetworkNack")

                # We have processed the network Nack packet.
                return

        # Now process as Interest or Data.
        if interest != None:
            self._dispatchInterest(interest)
        elif data != None:
            self._satisfyPendingInterests(data)
Ejemplo n.º 56
0
    def _getExpressInterestArgs(self, interestOrName, arg2, arg3, arg4, arg5, arg6):
        """
        This is a protected helper method to resolve the different overloaded
        forms of Face.expressInterest and return the arguments to pass to
        Node.expressInterest. This is necessary to prepare arguments such as
        interestCopy before dispatching to Node.expressInterest.

        :return: A dictionary with the following keys: 'pendingInterestId',
          'interestCopy', 'onData', 'onTimeout', 'onNetworkNack' and 'wireFormat'.
        :rtype: dict
        """
        if isinstance(interestOrName, Interest):
            # Node.expressInterest requires a copy of the interest.
            interestCopy = Interest(interestOrName)
        else:
            # The first argument is a name. Make the interest from the name and
            #   possible template.
            if isinstance(arg2, Interest):
                template = arg2
                # Copy the template.
                interestCopy = Interest(template)
                interestCopy.setName(interestOrName)

                # Shift the remaining args to be processed below.
                arg2 = arg3
                arg3 = arg4
                arg4 = arg5
                arg5 = arg6
            else:
                # No template.
                interestCopy = Interest(interestOrName)
                # Set a default interest lifetime.
                interestCopy.setInterestLifetimeMilliseconds(4000.0)

        onData = arg2
        # arg3,       arg4,          arg5 may be:
        # OnTimeout,  OnNetworkNack, WireFormat
        # OnTimeout,  OnNetworkNack, None
        # OnTimeout,  WireFormat,    None
        # OnTimeout,  None,          None
        # WireFormat, None,          None
        # None,       None,          None
        if isinstance(arg3, collections.Callable):
            onTimeout = arg3
        else:
            onTimeout = None

        if isinstance(arg4, collections.Callable):
            onNetworkNack = arg4
        else:
            onNetworkNack = None

        if isinstance(arg3, WireFormat):
            wireFormat = arg3
        elif isinstance(arg4, WireFormat):
            wireFormat = arg4
        elif isinstance(arg5, WireFormat):
            wireFormat = arg5
        else:
            wireFormat = WireFormat.getDefaultWireFormat()

        return { 'pendingInterestId': self._node.getNextEntryId(),
          'interestCopy': interestCopy, 'onData': onData, 'onTimeout': onTimeout,
          'onNetworkNack': onNetworkNack, 'wireFormat': wireFormat }
Ejemplo n.º 57
0
    def _sendInterest(self, interest, nRetrials, link, onVerified, onError):
        """
        Express the interest, call verifyData for the fetched Data packet and
        call onVerified if verify succeeds. If verify fails, call
        onError(EncryptError.ErrorCode.Validation, "verifyData failed"). If the
        interest times out, re-express nRetrials times. If the interest times
        out nRetrials times, or for a network Nack, call
        onError(EncryptError.ErrorCode.DataRetrievalFailure, interest.getName().toUri()).

        :param Interest interest: The Interest to express.
        :param int nRetrials: The number of retrials left after a timeout.
        :param Link link: The Link object to use in the Interest. This does not
          make a copy of the Link object. If the Link object's
          getDelegations().size() is zero, don't use it.
        :param onVerified: When the fetched Data packet validation succeeds,
          this calls onVerified(data).
        :type onVerified: function object
        :param onError: This calls onError(errorCode, message) for an error,
          where errorCode is from EncryptError.ErrorCode and message is a str.
        :type onError: function object
        """
        # Prepare the callback functions.
        def onData(contentInterest, contentData):
            # The Interest has no selectors, so assume the library correctly
            # matched with the Data name before calling onData.

            try:
                self._keyChain.verifyData(
                    contentData, onVerified,
                    lambda d, reason: Consumer._callOnError(
                      onError, EncryptError.ErrorCode.Validation,
                      "verifyData failed. Reason: " + reason))
            except Exception as ex:
                try:
                    onError(EncryptError.ErrorCode.General, "verifyData error: " + repr(ex))
                except:
                    logging.exception("Error in onError")

        def onNetworkNack(interest, networkNack):
            # We have run out of options. Report a retrieval failure.
            try:
                onError(EncryptError.ErrorCode.DataRetrievalFailure,
                        interest.getName().toUri())
            except:
                logging.exception("Error in onError")

        def onTimeout(interest):
            if nRetrials > 0:
                self._sendInterest(interest, nRetrials - 1, link, onVerified, onError)
            else:
                onNetworkNack(interest, NetworkNack())

        if link.getDelegations().size() == 0:
            # We can use the supplied interest without copying.
            request = interest
        else:
            # Copy the supplied interest and add the Link.
            request = Interest(interest)
            # This will use a cached encoding if available.
            request.setLinkWireEncoding(link.wireEncode())

        try:
            self._face.expressInterest(request, onData, onTimeout, onNetworkNack)
        except Exception as ex:
            try:
                onError(EncryptError.ErrorCode.General,
                        "expressInterest error: " + repr(ex))
            except:
                logging.exception("Error in onError")
Ejemplo n.º 58
0
 def _fetchFirstSegment(self, baseInterest):
     interest = Interest(baseInterest)
     interest.setChildSelector(1)
     interest.setMustBeFresh(True)
     self._face.expressInterest(interest, self._onData, self._onTimeout)