def listMessages(self): """Return a map from pretty-printed message ID to dicts mapping: 'size' to the size of the message, in bytes 'nym' to the pseudonym receiving the message 'have' to the number of packets we have so far 'need' to the number of additional packets we need. """ result = {} for msgid in self.states.keys(): state = self.states[msgid] have, need = state.getCompleteness() result[disp64(msgid,12)] = { 'size' : state.params.length, 'nym' : state.nym, 'have' : have, 'need' : need } return result
def listMessages(self): """Return a map from pretty-printed message ID to dicts mapping: 'size' to the size of the message, in bytes 'nym' to the pseudonym receiving the message 'have' to the number of packets we have so far 'need' to the number of additional packets we need. """ result = {} for msgid in self.states.keys(): state = self.states[msgid] have, need = state.getCompleteness() result[disp64(msgid, 12)] = { 'size': state.params.length, 'nym': state.nym, 'have': have, 'need': need } return result
def addFragment(self, fragmentPacket, nym=None, now=None, verbose=0): """Given an instance of mixminion.Packet.FragmentPayload, record the fragment if appropriate and update the state of the fragment pool if necessary. Returns the message ID that was updated, or None if the fragment was redundant or misformed. fragmentPacket -- the new fragment to add. nym -- a string representing the identity that received this fragment. [Tracking nyms is important, to prevent an attack where we send 2 fragments to 'MarkTwain' and 2 fragments to 'SClemens', and see that the message is reconstructed.] verbose -- if true, log information at the INFO level; otherwise, log at DEBUG. """ if verbose: say = LOG.info else: say = LOG.debug if now is None: now = time.time() today = previousMidnight(now) # If the message has already been rejected or completed, we can # drop this packet. s = self.db.getStatusAndTime(fragmentPacket.msgID) if s: say("Dropping fragment of %s message %r", s[0].lower(), disp64(fragmentPacket.msgID,12)) return None # Otherwise, create a new metadata object for this fragment... meta = FragmentMetadata(messageid=fragmentPacket.msgID, idx=fragmentPacket.index, size=fragmentPacket.msgLen, isChunk=0, chunkNum=None, overhead=fragmentPacket.getOverhead(), insertedDate=today, nym=nym, digest=sha1(fragmentPacket.data)) # ... and allocate or find the MessageState for this message. state = self._getState(meta) try: # Check whether we can/should add this message, but do not # add it. state.addFragment(None, meta, noop=1) # No exception was thrown; queue the message. h = self.store.queueMessageAndMetadata(fragmentPacket.data, meta) # And *now* update the message state. state.addFragment(h, meta) say("Stored fragment %s of message %s", fragmentPacket.index+1, disp64(fragmentPacket.msgID,12)) return fragmentPacket.msgID except MismatchedFragment, s: # Remove the other fragments, mark msgid as bad. LOG.warn("Found inconsistent fragment %s in message %s: %s", fragmentPacket.index+1, disp64(fragmentPacket.msgID,12), s) self._deleteMessageIDs({ meta.messageid : 1}, "REJECTED", now) return None
def addFragment(self, fragmentPacket, nym=None, now=None, verbose=0): """Given an instance of mixminion.Packet.FragmentPayload, record the fragment if appropriate and update the state of the fragment pool if necessary. Returns the message ID that was updated, or None if the fragment was redundant or misformed. fragmentPacket -- the new fragment to add. nym -- a string representing the identity that received this fragment. [Tracking nyms is important, to prevent an attack where we send 2 fragments to 'MarkTwain' and 2 fragments to 'SClemens', and see that the message is reconstructed.] verbose -- if true, log information at the INFO level; otherwise, log at DEBUG. """ if verbose: say = LOG.info else: say = LOG.debug if now is None: now = time.time() today = previousMidnight(now) # If the message has already been rejected or completed, we can # drop this packet. s = self.db.getStatusAndTime(fragmentPacket.msgID) if s: say("Dropping fragment of %s message %r", s[0].lower(), disp64(fragmentPacket.msgID, 12)) return None # Otherwise, create a new metadata object for this fragment... meta = FragmentMetadata(messageid=fragmentPacket.msgID, idx=fragmentPacket.index, size=fragmentPacket.msgLen, isChunk=0, chunkNum=None, overhead=fragmentPacket.getOverhead(), insertedDate=today, nym=nym, digest=sha1(fragmentPacket.data)) # ... and allocate or find the MessageState for this message. state = self._getState(meta) try: # Check whether we can/should add this message, but do not # add it. state.addFragment(None, meta, noop=1) # No exception was thrown; queue the message. h = self.store.queueMessageAndMetadata(fragmentPacket.data, meta) # And *now* update the message state. state.addFragment(h, meta) say("Stored fragment %s of message %s", fragmentPacket.index + 1, disp64(fragmentPacket.msgID, 12)) return fragmentPacket.msgID except MismatchedFragment, s: # Remove the other fragments, mark msgid as bad. LOG.warn("Found inconsistent fragment %s in message %s: %s", fragmentPacket.index + 1, disp64(fragmentPacket.msgID, 12), s) self._deleteMessageIDs({meta.messageid: 1}, "REJECTED", now) return None
class FragmentPool: """Class to hold and manage fragmented messages as they are reconstructed.""" ## Fields: # states -- map from messageid to MessageState. Reconstructed by # rescan(). # db -- instance of FragmentDB. # store -- instance of StringMetadataStore. The messages are either # the contents of invidual fragments or reconstructed chunks. # The metadata are instances of FragmentMetadata. def __init__(self, dir): """Open a FragmentPool storing fragments in 'dir' and records of old messages in 'dir_db'. """ self.store = mixminion.Filestore.StringMetadataStore(dir, create=1) self.db = FragmentDB(dir + "_db") self.rescan() def cleanQueue(self, deleteFn=None): """Expunge all removed fragments from disk. See Filestore.cleanQueue""" self.store.cleanQueue(deleteFn) def sync(self): """Flush pending changes to disk.""" self.db.sync() def close(self): """Release open resources for this pool.""" self.db.close() del self.db del self.store del self.states def addFragment(self, fragmentPacket, nym=None, now=None, verbose=0): """Given an instance of mixminion.Packet.FragmentPayload, record the fragment if appropriate and update the state of the fragment pool if necessary. Returns the message ID that was updated, or None if the fragment was redundant or misformed. fragmentPacket -- the new fragment to add. nym -- a string representing the identity that received this fragment. [Tracking nyms is important, to prevent an attack where we send 2 fragments to 'MarkTwain' and 2 fragments to 'SClemens', and see that the message is reconstructed.] verbose -- if true, log information at the INFO level; otherwise, log at DEBUG. """ if verbose: say = LOG.info else: say = LOG.debug if now is None: now = time.time() today = previousMidnight(now) # If the message has already been rejected or completed, we can # drop this packet. s = self.db.getStatusAndTime(fragmentPacket.msgID) if s: say("Dropping fragment of %s message %r", s[0].lower(), disp64(fragmentPacket.msgID, 12)) return None # Otherwise, create a new metadata object for this fragment... meta = FragmentMetadata(messageid=fragmentPacket.msgID, idx=fragmentPacket.index, size=fragmentPacket.msgLen, isChunk=0, chunkNum=None, overhead=fragmentPacket.getOverhead(), insertedDate=today, nym=nym, digest=sha1(fragmentPacket.data)) # ... and allocate or find the MessageState for this message. state = self._getState(meta) try: # Check whether we can/should add this message, but do not # add it. state.addFragment(None, meta, noop=1) # No exception was thrown; queue the message. h = self.store.queueMessageAndMetadata(fragmentPacket.data, meta) # And *now* update the message state. state.addFragment(h, meta) say("Stored fragment %s of message %s", fragmentPacket.index + 1, disp64(fragmentPacket.msgID, 12)) return fragmentPacket.msgID except MismatchedFragment, s: # Remove the other fragments, mark msgid as bad. LOG.warn("Found inconsistent fragment %s in message %s: %s", fragmentPacket.index + 1, disp64(fragmentPacket.msgID, 12), s) self._deleteMessageIDs({meta.messageid: 1}, "REJECTED", now) return None except UnneededFragment: # Discard this fragment; we don't need it. say("Dropping unneeded fragment %s of message %s", fragmentPacket.index + 1, disp64(fragmentPacket.msgID, 12)) return None