class _CurveCPBaseTransport(DatagramProtocol): timeouts = 1, 1, 2, 3, 5, 8, 13 _generateKey = staticmethod(PrivateKey.generate) _generateKeydir = staticmethod(EphemeralKey) def __init__(self, clock, serverKey, factory): self._clock = clock self._serverKey = serverKey self._factory = factory self._received = IntervalSet() self._weAcked = IntervalSet() self._sent = IntervalSet() self._theyAcked = IntervalSet() self._sentMessages = set() self._previousID = 0 self._fragment = [] self._congestion = Chicago() self._sentMessageAt = {} self._delayedCalls = {} self._messageQueue = [] self._enqueuedMessages = set() self._deferred = defer.Deferred() self._nonce = 0 self._theirLastNonce = 0 self._counter = 1 self._ourStreamEnd = None self._theirStreamEnd = None self._reads = self._writes = None self._done = False self._outstandingMessages = 0 self._onDone = [] def _now(self): return self._clock.seconds() def _timedOutHandshaking(self): self._deferred.errback(e.HandshakeTimeout()) def _write(self, data): self.transport.write(data, self._peerHost) def _retrySendingForHandshake(self, data): mt = MultiTimeout(self._clock, self.timeouts, self._timedOutHandshaking, self._write, data) mt.reset() return mt messageMap = {} def datagramReceived(self, data, host_port): if self._done: return handler = self._messageMap.get(data[:8]) if not handler: return meth = getattr(self, '_datagram_' + handler) meth(data, host_port) _nonceInfix = '' def _encryptForNonce(self, which, box, data): packedNonce = _nonceStruct.pack(self._nonce) self._nonce += 1 nonce = 'CurveCP-%s-%s%s' % (self._nonceInfix, which, packedNonce) return packedNonce + box.encrypt(data, nonce).ciphertext def _verifyNonce(self, nonce): unpacked, = _nonceStruct.unpack(nonce) ret = unpacked >= self._theirLastNonce self._theirLastNonce = unpacked return ret def _serializeMessage(self, message): return '' def _sendMessage(self, message): packet = self._serializeMessage(message) self._write(packet) if message.id: self._sentMessageAt[ message.id] = self._congestion.lastSentAt = self._now() self._weAcked.update(message.ranges) def _parseMessage(self, now, message): message = parseMessage(message) sentAt = self._sentMessageAt.pop(message.previousID, None) if sentAt is not None: self._congestion.processDelta(now, now - sentAt) if message.id: self._clock.callLater(0, self._sendAMessage, ack=message.id) self._theyAcked.update(message.ranges) for qd in list(self._sentMessages): if qd.interval & self._theyAcked: qd.interval.difference_update(self._theyAcked) if not qd.interval: for d in qd.deferreds: d.callback(now) self._cancel(qd) self._outstandingMessages -= 1 self._sentMessages.remove(qd) if message.resolution and self._theirStreamEnd is None: self._theirStreamEnd = message.dataPos self._theirResolution = message.resolution self._received.add(halfOpen(message.dataPos, message.dataPos + 1)) self._reads = 'closing' self._checkTheirResolution() return elif not message.data: return i = halfOpen(message.dataPos, message.dataPos + len(message.data)) new = IntervalSet([i]) - self._received if not new: return self._received.add(i) newData = message.data[new.lower_bound() - i.lower_bound:new.upper_bound() - i.upper_bound or None] bisect.insort(self._fragment, (i.lower_bound, newData)) if len(self._received) > 1 or self._received.lower_bound() != 0: return newData = ''.join([d for _, d in self._fragment]) self._protocol.dataReceived(newData) self._fragment = [] self._checkTheirResolution() def _checkTheirResolution(self): if self._theirStreamEnd is None: return if len(self._received) != 1 or self._received.lower_bound() != 0: return self._reads = 'closed' self._protocol.readConnectionLost() self._checkBothResolutions() def notifyFinish(self): if self._done: return defer.succeed(None) d = defer.Deferred() self._onDone.append(d) return d def _checkBothResolutions(self): if self._reads == self._writes == 'closed' and not self._done: self._protocol.connectionLost( Failure(e.resolution_map[self._theirResolution]())) self._cancel('message') deferreds, self._onDone = self._onDone, None for d in deferreds: d.callback(None) # this used to be done on a callLater, but I can't remember why self._done = True def _sendAMessage(self, ack=None): now = self._now() nextActionIn = None message = Message( id=self._counter, previousID=0, ranges=list(self._received)[:6], resolution=None, dataPos=0, data='', ) if ack is not None: message = message._replace(id=0, previousID=ack) elif self._messageQueue: _, _, qd = heapq.heappop(self._messageQueue) self._enqueuedMessages.remove(qd) message = qd.fillInMessage(message) self._counter += 1 if qd.sentAt: self._congestion.timedOut(now) self._sentMessageAt.pop(qd.messageIDs[-1], None) elif self._congestion.window is not None and self._outstandingMessages > self._congestion.window: self._enqueue(1, qd) return else: self._outstandingMessages += 1 qd.sentAt.append(now) qd.messageIDs.append(message.id) self._sentMessages.add(qd) self._reschedule(qd) else: return 60 self._sendMessage(message) return nextActionIn def _reschedule(self, what, nextActionIn=None): now = self._now() if nextActionIn is None: if what == 'message': nextActionIn = self._congestion.nextMessageIn(now) else: nextActionIn = self._congestion.nextTimeoutIn(now, what) delayedCall = self._delayedCalls.get(what) if delayedCall is not None and delayedCall.active(): delayedCall.reset(nextActionIn) else: self._delayedCalls[what] = self._clock.callLater( nextActionIn, self._scheduledAction, what) def _cancel(self, what): delayedCall = self._delayedCalls.pop(what, None) if delayedCall is not None and delayedCall.active(): delayedCall.cancel() def _scheduledAction(self, what): nextActionIn = None if what == 'message': nextActionIn = self._sendAMessage() self._reschedule(what, nextActionIn=nextActionIn) else: self._sentMessages.remove(what) if what.interval: self._enqueue(0, what) def _enqueue(self, priority, *data): self._reschedule('message') for datum in data: if datum not in self._enqueuedMessages and datum.interval: heapq.heappush(self._messageQueue, (priority, datum.lowerBound, datum)) self._enqueuedMessages.add(datum) def write(self, data): if not data: return defer.succeed(None) elif self._writes in ('closing', 'closed'): return defer.fail( e.CurveCPConnectionDone( 'attempted a write after closing writes')) d = defer.Deferred() qds = [] lowerBound = self._sent.upper_bound() if self._sent else 0 while data: ds = [] queueableData = data[:1024] dataRange = halfOpen(lowerBound, lowerBound + len(queueableData)) qd = QueuedData(IntervalSet([dataRange]), lowerBound, ds, [], [], queueableData) self._sent.add(dataRange) lowerBound += len(queueableData) data = data[1024:] qds.append(qd) ds.append(d) self._enqueue(1, *qds) return d def _peerEstablished(self): self._protocol = self._factory.buildProtocol(self.getPeer()) self._protocol.makeConnection(self) self._deferred.callback(self._protocol) self._reads = 'open' self._writes = 'open' def _doneWritingAcked(self, when): self._writes = 'closed' self._checkBothResolutions() return when def loseConnection(self, success=True): d = defer.Deferred() d.addCallback(self._doneWritingAcked) streamEnd = self._ourStreamEnd = self._sent.upper_bound( ) if self._sent else 0 resolution = self._ourResolution = 'success' if success else 'failure' interval = IntervalSet([halfOpen(streamEnd, streamEnd + 1)]) self._enqueue( 1, QueuedResolution(interval, streamEnd, [d], [], [], resolution)) self._writes = 'closing' return d
class _CurveCPBaseTransport(DatagramProtocol): timeouts = 1, 1, 2, 3, 5, 8, 13 _generateKey = staticmethod(PrivateKey.generate) _generateKeydir = staticmethod(EphemeralKey) def __init__(self, clock, serverKey, factory): self._clock = clock self._serverKey = serverKey self._factory = factory self._received = IntervalSet() self._weAcked = IntervalSet() self._sent = IntervalSet() self._theyAcked = IntervalSet() self._sentMessages = set() self._previousID = 0 self._fragment = [] self._congestion = Chicago() self._sentMessageAt = {} self._delayedCalls = {} self._messageQueue = [] self._enqueuedMessages = set() self._deferred = defer.Deferred() self._nonce = 0 self._theirLastNonce = 0 self._counter = 1 self._ourStreamEnd = None self._theirStreamEnd = None self._reads = self._writes = None self._done = False self._outstandingMessages = 0 self._onDone = [] def _now(self): return self._clock.seconds() def _timedOutHandshaking(self): self._deferred.errback(e.HandshakeTimeout()) def _write(self, data): self.transport.write(data, self._peerHost) def _retrySendingForHandshake(self, data): mt = MultiTimeout( self._clock, self.timeouts, self._timedOutHandshaking, self._write, data) mt.reset() return mt messageMap = {} def datagramReceived(self, data, host_port): if self._done: return handler = self._messageMap.get(data[:8]) if not handler: return meth = getattr(self, '_datagram_' + handler) meth(data, host_port) _nonceInfix = '' def _encryptForNonce(self, which, box, data): packedNonce = _nonceStruct.pack(self._nonce) self._nonce += 1 nonce = 'CurveCP-%s-%s%s' % (self._nonceInfix, which, packedNonce) return packedNonce + box.encrypt(data, nonce).ciphertext def _verifyNonce(self, nonce): unpacked, = _nonceStruct.unpack(nonce) ret = unpacked >= self._theirLastNonce self._theirLastNonce = unpacked return ret def _serializeMessage(self, message): return '' def _sendMessage(self, message): packet = self._serializeMessage(message) self._write(packet) if message.id: self._sentMessageAt[message.id] = self._congestion.lastSentAt = self._now() self._weAcked.update(message.ranges) def _parseMessage(self, now, message): message = parseMessage(message) sentAt = self._sentMessageAt.pop(message.previousID, None) if sentAt is not None: self._congestion.processDelta(now, now - sentAt) if message.id: self._clock.callLater(0, self._sendAMessage, ack=message.id) self._theyAcked.update(message.ranges) for qd in list(self._sentMessages): if qd.interval & self._theyAcked: qd.interval.difference_update(self._theyAcked) if not qd.interval: for d in qd.deferreds: d.callback(now) self._cancel(qd) self._outstandingMessages -= 1 self._sentMessages.remove(qd) if message.resolution and self._theirStreamEnd is None: self._theirStreamEnd = message.dataPos self._theirResolution = message.resolution self._received.add(halfOpen(message.dataPos, message.dataPos + 1)) self._reads = 'closing' self._checkTheirResolution() return elif not message.data: return i = halfOpen(message.dataPos, message.dataPos + len(message.data)) new = IntervalSet([i]) - self._received if not new: return self._received.add(i) newData = message.data[new.lower_bound() - i.lower_bound:new.upper_bound() - i.upper_bound or None] bisect.insort(self._fragment, (i.lower_bound, newData)) if len(self._received) > 1 or self._received.lower_bound() != 0: return newData = ''.join([d for _, d in self._fragment]) self._protocol.dataReceived(newData) self._fragment = [] self._checkTheirResolution() def _checkTheirResolution(self): if self._theirStreamEnd is None: return if len(self._received) != 1 or self._received.lower_bound() != 0: return self._reads = 'closed' self._protocol.readConnectionLost() self._checkBothResolutions() def notifyFinish(self): if self._done: return defer.succeed(None) d = defer.Deferred() self._onDone.append(d) return d def _checkBothResolutions(self): if self._reads == self._writes == 'closed' and not self._done: self._protocol.connectionLost(Failure(e.resolution_map[self._theirResolution]())) self._cancel('message') deferreds, self._onDone = self._onDone, None for d in deferreds: d.callback(None) # this used to be done on a callLater, but I can't remember why self._done = True def _sendAMessage(self, ack=None): now = self._now() nextActionIn = None message = Message( id=self._counter, previousID=0, ranges=list(self._received)[:6], resolution=None, dataPos=0, data='', ) if ack is not None: message = message._replace(id=0, previousID=ack) elif self._messageQueue: _, _, qd = heapq.heappop(self._messageQueue) self._enqueuedMessages.remove(qd) message = qd.fillInMessage(message) self._counter += 1 if qd.sentAt: self._congestion.timedOut(now) self._sentMessageAt.pop(qd.messageIDs[-1], None) elif self._congestion.window is not None and self._outstandingMessages > self._congestion.window: self._enqueue(1, qd) return else: self._outstandingMessages += 1 qd.sentAt.append(now) qd.messageIDs.append(message.id) self._sentMessages.add(qd) self._reschedule(qd) else: return 60 self._sendMessage(message) return nextActionIn def _reschedule(self, what, nextActionIn=None): now = self._now() if nextActionIn is None: if what == 'message': nextActionIn = self._congestion.nextMessageIn(now) else: nextActionIn = self._congestion.nextTimeoutIn(now, what) delayedCall = self._delayedCalls.get(what) if delayedCall is not None and delayedCall.active(): delayedCall.reset(nextActionIn) else: self._delayedCalls[what] = self._clock.callLater( nextActionIn, self._scheduledAction, what) def _cancel(self, what): delayedCall = self._delayedCalls.pop(what, None) if delayedCall is not None and delayedCall.active(): delayedCall.cancel() def _scheduledAction(self, what): nextActionIn = None if what == 'message': nextActionIn = self._sendAMessage() self._reschedule(what, nextActionIn=nextActionIn) else: self._sentMessages.remove(what) if what.interval: self._enqueue(0, what) def _enqueue(self, priority, *data): self._reschedule('message') for datum in data: if datum not in self._enqueuedMessages and datum.interval: heapq.heappush(self._messageQueue, (priority, datum.lowerBound, datum)) self._enqueuedMessages.add(datum) def write(self, data): if not data: return defer.succeed(None) elif self._writes in ('closing', 'closed'): return defer.fail(e.CurveCPConnectionDone( 'attempted a write after closing writes')) d = defer.Deferred() qds = [] lowerBound = self._sent.upper_bound() if self._sent else 0 while data: ds = [] queueableData = data[:1024] dataRange = halfOpen(lowerBound, lowerBound + len(queueableData)) qd = QueuedData( IntervalSet([dataRange]), lowerBound, ds, [], [], queueableData) self._sent.add(dataRange) lowerBound += len(queueableData) data = data[1024:] qds.append(qd) ds.append(d) self._enqueue(1, *qds) return d def _peerEstablished(self): self._protocol = self._factory.buildProtocol(self.getPeer()) self._protocol.makeConnection(self) self._deferred.callback(self._protocol) self._reads = 'open' self._writes = 'open' def _doneWritingAcked(self, when): self._writes = 'closed' self._checkBothResolutions() return when def loseConnection(self, success=True): d = defer.Deferred() d.addCallback(self._doneWritingAcked) streamEnd = self._ourStreamEnd = self._sent.upper_bound() if self._sent else 0 resolution = self._ourResolution = 'success' if success else 'failure' interval = IntervalSet([halfOpen(streamEnd, streamEnd + 1)]) self._enqueue(1, QueuedResolution(interval, streamEnd, [d], [], [], resolution)) self._writes = 'closing' return d