def __init__(self, interest): if interest != None: self._interest = Interest(interest) self._nRetriesLeft = 3 else: self._interest = Interest() self._nRetriesLeft = 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())
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)
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())
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)
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)
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 _nfdRegisterPrefix(self, registeredPrefixId, prefix, onInterest, onRegisterFailed, onRegisterSuccess, registrationOptions, 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(registrationOptions) if (registrationOptions.getOrigin() != None and registrationOptions.getOrigin() >= 0): controlParameters.setOrigin(registrationOptions.getOrigin()) # Remove the origin value from the flags since it is not used to encode. controlParameters.getForwardingFlags().setOrigin(None) commandInterest = Interest() commandInterest.setCanBePrefix(True) commandInterest.setMustBeFresh(True) 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)
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())
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 __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()
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)
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
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 _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)
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)
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()
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))))
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
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())
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)
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
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())
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)
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())
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 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)
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)
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()))
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))