[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[minion-cvs] Halftested] Add support to MMTP for connection padding,...
Update of /home/minion/cvsroot/src/minion/lib/mixminion
In directory moria.mit.edu:/tmp/cvs-serv9624/lib/mixminion
Modified Files:
MMTPClient.py test.py
Log Message:
[Halftested] Add support to MMTP for connection padding, key renegotiation,
protocol negotiation. Bump protocol version to 0.2, since older servers don't
receive padding correctly.
Index: MMTPClient.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/MMTPClient.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- MMTPClient.py 16 Dec 2002 02:40:11 -0000 1.14
+++ MMTPClient.py 12 Jan 2003 04:27:19 -0000 1.15
@@ -19,7 +19,7 @@
import socket
import mixminion._minionlib as _ml
-from mixminion.Crypto import sha1
+from mixminion.Crypto import sha1, getCommonPRNG
from mixminion.Common import MixProtocolError, LOG
class BlockingClientConnection:
@@ -34,6 +34,8 @@
# context: a TLSContext object; used to create connections.
# sock: a TCP socket, open to the server.
# tls: a TLS socket, wrapping sock.
+ #DOCDOC protocol
+ PROTOCOL_VERSIONS = ['0.2', '0.1']
def __init__(self, targetIP, targetPort, targetKeyID):
"""Open a new connection."""
self.targetIP = targetIP
@@ -72,31 +74,53 @@
# change our mind between now and a release candidate, and so we
# can obsolete betas come release time.
LOG.debug("Negotiatiating MMTP protocol")
- self.tls.write("MMTP 0.1\r\n")
- inp = self.tls.read(len("MMTP 0.1\r\n"))
- if inp != "MMTP 0.1\r\n":
+ self.tls.write("MMTP %s\r\n" % ",".join(self.PROTOCOL_VERSIONS))
+ # This is ugly, but we have no choice if we want to read up to the
+ # first newline.
+ # we don't really want 100; we just want up to the newline.
+ inp = self.tls.read(100)
+ while "\n" not in inp and len(inp) < 100:
+ inp += self.tls.read(100)
+ self.protocol = None
+ for p in self.PROTOCOL_VERSIONS:
+ if inp == 'MMTP %s\r\n'%p:
+ self.protocol = p
+ break
+ if not self.protocol:
raise MixProtocolError("Protocol negotiation failed")
- LOG.debug("MMTP protocol negotated: version 0.1")
+ LOG.debug("MMTP protocol negotated: version %s", self.protocol)
- def sendPacket(self, packet):
+ def renegotiate(self):
+ self.tls.renegotiate()
+ self.tls.do_handshake()
+
+ def sendPacket(self, packet,
+ control="SEND\r\n", serverControl="RECEIVED\r\n",
+ hashExtra="SEND",serverHashExtra="RECEIVED"):
"""Send a single packet to a server."""
assert len(packet) == 1<<15
LOG.debug("Sending packet")
##
# We write: "SEND\r\n", 28KB of data, and sha1(packet|"SEND").
- self.tls.write("SEND\r\n")
+ self.tls.write(control)
self.tls.write(packet)
- self.tls.write(sha1(packet+"SEND"))
+ self.tls.write(sha1(packet+hashExtra))
LOG.debug("Packet sent; waiting for ACK")
# And we expect, "RECEIVED\r\n", and sha1(packet|"RECEIVED")
- inp = self.tls.read(len("RECEIVED\r\n")+20)
- if inp != "RECEIVED\r\n"+sha1(packet+"RECEIVED"):
+ inp = self.tls.read(len(serverControl)+20)
+ if inp != serverControl+sha1(packet+serverHashExtra):
raise MixProtocolError("Bad ACK received")
LOG.debug("ACK received; packet successfully delivered")
- # FFFF we need a sendJunkPacket method.
-
+ def sendJunkPacket(self, packet):
+ if self.protocol == '0.1':
+ LOG.debug("Not sending junk to a v0.1 server")
+ return
+ self.sendPacket(packet,
+ control="JUNK\r\n", serverControl="RECEIVED\r\n",
+ hashExtra="JUNK", serverHashExtra="RECEIVED JUNK")
+
def shutdown(self):
"""Close this connection."""
LOG.debug("Shutting down connection to %s:%s",
@@ -108,11 +132,29 @@
LOG.debug("Connection closed")
def sendMessages(targetIP, targetPort, targetKeyID, packetList):
- """Sends a list of messages to a server."""
+ """Sends a list of messages to a server.
+ DOCDOC arguments
+ DOCDOC "JUNK", "RENEGOTIATE"
+ """
+ # Generate junk before opening connection to avoid timing attacks
+ packets = []
+ for p in packetList:
+ if p == 'JUNK':
+ packets.append(("JUNK", getCommonPRNG().getBytes(1<<15)))
+ elif p == 'RENEGOTIATE':
+ packets.append(("RENEGOTIATE", None))
+ else:
+ packets.append(("MSG", p))
+
con = BlockingClientConnection(targetIP, targetPort, targetKeyID)
try:
con.connect()
- for p in packetList:
- con.sendPacket(p)
+ for t,p in packets:
+ if t == "JUNK":
+ con.sendJunkPacket(p)
+ elif t == "RENEGOTIATE":
+ con.renegotiate()
+ else:
+ con.sendPacket(p)
finally:
con.shutdown()
Index: test.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/test.py,v
retrieving revision 1.71
retrieving revision 1.72
diff -u -d -r1.71 -r1.72
--- test.py 10 Jan 2003 20:12:05 -0000 1.71
+++ test.py 12 Jan 2003 04:27:19 -0000 1.72
@@ -2352,7 +2352,6 @@
# (Fails less than once in 2 **30 tests.)
self.assert_(messageLens[0] <= 30)
self.assert_(messageLens[-1] >= 30)
- #print messageLens
bcmq.removeAll()
bcmq.cleanQueue()
@@ -2644,7 +2643,6 @@
server.process(0.1)
t.join()
-
def _testNonblockingTransmission(self):
server, listener, messagesIn, keyid = _getMMTPServer()
self.listener = listener
@@ -2654,7 +2652,8 @@
messages = ["helloxxx"*4096, "helloyyy"*4096]
async = mixminion.server.MMTPServer.AsyncServer()
clientcon = mixminion.server.MMTPServer.MMTPClientConnection(
- _getTLSContext(0), "127.0.0.1", TEST_PORT, keyid, messages[:],
+ _getTLSContext(0), "127.0.0.1", TEST_PORT, keyid,
+ messages[:]+["JUNK","RENEGOTIATE","JUNK"],
[None, None], None)
clientcon.register(async)
def clientThread(clientcon=clientcon, async=async):
@@ -2710,43 +2709,33 @@
self.listener = listener
self.server = server
- # This is a little tricky. We want to test connection timeouts, so we
- # concoct a fake list object that blocks before returning its second
- # element so that we can make MMTPClient.sendMessages pause for a
- # while.
- class SlowMessageList:
- def __init__(self):
- self.pausing = 50
- def __getitem__(self, i):
- if i == 0:
- return "helloxxx"*4096
- elif i == 1:
- # We use a counter here so that we can make the thread
- # holding this list end quickly when we want it to.
- while self.pausing > 0:
- time.sleep(0.1)
- self.pausing -= 0.1
- return "helloyyy"*4096
- else:
- raise IndexError
-
# This function wraps MMTPClient.sendMessages, but catches exceptions.
# Since we're going to run this function in a thread, we pass the
# exception back through a list argument.
- def sendAndCaptureException(lst, *args):
+ def sendSlowlyAndCaptureException(exlst, pausing, targetIP, targetPort,
+ targetKeyID, msgFast, msgSlow):
try:
- mixminion.MMTPClient.sendMessages(*args)
+ con = mixminion.MMTPClient.BlockingClientConnection(
+ targetIP,targetPort,targetKeyID)
+ con.connect()
+ con.sendPacket(msgFast)
+ while pausing[0] > 0:
+ time.sleep(.1)
+ pausing[0] -= .1
+ con.sendPacket(msgSlow)
+ con.close()
except:
- lst.append(sys.exc_info())
+ exlst.append(sys.exc_info())
# Manually set the server's timeout threshold to 600 msec.
server._timeout = 0.6
server.process(0.1)
excList = []
- msgList = SlowMessageList()
+ pausing = [10]
t = threading.Thread(None,
- sendAndCaptureException,
- args=(excList, "127.0.0.1", TEST_PORT, keyid, msgList))
+ sendSlowlyAndCaptureException,
+ args=(excList, pausing, "127.0.0.1", TEST_PORT, keyid,
+ "helloxxx"*4096, "helloyyy"*4096))
t.start()
timedOut = 0 # flag: have we really timed out?
try:
@@ -2764,14 +2753,14 @@
self.assert_(timedOut)
finally:
logMessage = resumeLog()
- # Did we log the timeout?
- self.assert_(stringContains(logMessage, "timed out"))
+ # Did we log the timeout?
+ self.assert_(stringContains(logMessage, "timed out"))#XXXX
# Was the one message we expected in fact transmitted?
self.assertEquals([messagesIn[0]], ["helloxxx"*4096])
# Now stop the transmitting thread. It will notice that its
# connection has been forcibly closed.
- msgList.pausing = 0
+ pausing[0] = 0
t.join()
# Was an exception raised?
self.assertEquals(1, len(excList))
@@ -4030,7 +4019,7 @@
self.assert_(not os.path.exists(os.path.join(dir, "2")))
finally:
m = resumeLog()
- self.assert_(m.endswith("Unable to deliver message\n"))
+ self.assert_(m.endswith("Unable to deliver message\n"))
try:
suspendLog()
@@ -4071,7 +4060,7 @@
queue.sendReadyMessages()
finally:
m = resumeLog()
- self.assert_(m.endswith("[ERROR] Unable to deliver message\n"))
+ self.assert_(m.endswith("[ERROR] Unable to deliver message\n"))
# After delivery: 91 and 92 go through, 93 stays, and 94 gets dropped.
self.assertEquals(1, queue.count())
self.assertEquals(5, len(os.listdir(dir)))
@@ -4304,7 +4293,6 @@
conf.getModuleManager().configure(conf)
finally:
resumeLog()
- pass
# Now, for each starting time, generate a server desciprtor.
_EXAMPLE_DESCRIPTORS[nickname] = []
@@ -4413,7 +4401,7 @@
self.assert_(ks.getServerInfo(si) is None)
finally:
s = resumeLog()
- self.assert_(stringContains(s, "Server is not currently"))
+ self.assert_(stringContains(s, "Server is not currently"))
##
# Now try out the directory. This is tricky; we add the other
@@ -4860,7 +4848,7 @@
## Test generateForwardMessage.
# We replace 'buildForwardMessage' to make this easier to test.
replaceFunction(mixminion.BuildMessage, "buildForwardMessage",
- lambda *a:"X")
+ lambda *a, **k:"X")
try:
getCalls = getReplacedFunctionCallLog
clearCalls = clearReplacedFunctionCallLog
@@ -4980,8 +4968,7 @@
tc = loader.loadTestsFromTestCase
if 0:
- suite.addTest(tc(ClientMainTests))
- suite.addTest(tc(ModuleTests))
+ suite.addTest(tc(MMTPTests))
return suite
suite.addTest(tc(MiscTests))