[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[minion-cvs] Replace serverinfo cache with far simpler code. Remove ...
Update of /home/minion/cvsroot/src/minion/lib/mixminion
In directory moria.seul.org:/tmp/cvs-serv24879/lib/mixminion
Modified Files:
ClientMain.py
Log Message:
Replace serverinfo cache with far simpler code. Remove unimplemented functionality
Index: ClientMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ClientMain.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- ClientMain.py 21 Nov 2002 16:55:48 -0000 1.6
+++ ClientMain.py 22 Nov 2002 21:05:22 -0000 1.7
@@ -10,7 +10,6 @@
XXXX at least one configuration works.
"""
-
# The client needs to store:
# - config
# - keys for pending SURBs
@@ -33,156 +32,98 @@
from mixminion.Common import getLog, floorDiv, createPrivateDir, MixError, \
MixFatalError
import mixminion.Crypto
-import mixminion.BuildMessage
+from mixminion.BuildMessage import buildForwardMessage
import mixminion.MMTPClient
import mixminion.Modules
from mixminion.ServerInfo import ServerInfo
from mixminion.Config import ClientConfig
-class DirectoryCache:
- """Holds a set of directories and serverinfo objects persistently.
+class TrivialDirectoryCache:
+ def __init__(self, directory):
+ self.directory = directory
+ createPrivateDir(directory)
+ self.byNickname = {}
+ self.byFilename = {}
- XXXX THIS CLASS IS NOT USED YET. It's a bad design choice, since
- the first version of the directory code will mostly obsolete it.
+ for f in os.listdir(self.directory):
+ p = os.path.join(self.directory, f)
+ try:
+ info = ServerInfo(fname=p, assumeValid=0)
+ except ConfigError, e:
+ getLog().warn("Invalid server descriptor %s", p)
+ continue
- FFFF This should actually cache the nickname and liveness information
- FFFF rather than parsing and reparsing it each time. Of course, we'll
- FFFF want to re-do it entirely once we have directory support, so it
- FFFF doesn't matter so much right now.
- """
- ## Fields
- # dirname: the name of the storage directory. All files in the directory
- # should be of the form 'si_XXXX...X', and contain validated server
- # descriptors.
- # servers: a map from nickname to [list of serverinfo objects], or None
- # if no servers have been loaded
- # allServers: a list of serverinfo objects
- def __init__(self, dirname):
- dirname = os.path.join(dirname, 'servers')
- createPrivateDir(dirname)
- self.dirname = dirname
- self.servers = None
+ serverSection = info['Server']
+ nickname = serverSection['Nickname']
+
+ if '.' in f:
+ f = f[:f.rindex('.')]
+
+ now = time.time()
- def load(self, forceReload=0):
- """Retrieve a list of servers from disk. If 'forceReload' is false,
- only load the servers if we have not already done so."""
- if not (self.servers is None or forceReload):
- return
- now = time.time()
- self.servers = {}
- self.allServers = []
- self.highest_num = -1
- for fn in os.listdir(self.dirname):
- if not fn.startswith("si"):
+ if now < serverSec['Valid-After']:
+ getLog().warn("Ignoring future decriptor %s", p)
continue
- n = int(fn[2:])
- if n > self.highest_num:
- self.highest_num = n
- info = ServerInfo(fname=os.path.join(self.dirname, fn),
- assumeValid=1)
- nickname = info['Server']['Nickname']
- if info['Server']['Valid-Until'] < now:
- getLog().info("Removing expired descriptor for %s",
- nickname)
- os.unlink(os.path.join(self.dirname, fn))
+ if now >= serverSec['Valid-Until']:
+ getLog().warn("Ignoring expired decriptor %s", p)
continue
- self.allServers.append(info)
- if self.servers.has_key(nickname):
- self.servers[nickname].append(info)
- else:
- self.servers[nickname] = [ info ]
-
- def getCurrentServer(self,nickname, when=None, until=None):
- """Return a server descriptor valid during the interval
- when...until. If 'nickname' is a string, return only a
- server with the appropriate nickname. If 'nickname' is a
- server descriptor, return that same server descriptor.
-
- Raise 'MixError' if no appropriate descriptor is found. """
- self.load()
- if when is None:
- when = time.time()
- if until is None:
- until = when+1
- if isinstance(nickname, ServerInfo):
- serverList = [ nickname ]
- else:
- try:
- serverList = self.servers[nickname]
- except KeyError, _:
- raise MixError("Nothing known about server %s"%nickname)
- for info in serverList:
- #XXXX fail on DNE
- server = info['Server']
- if server['Valid-After'] <= when <= until <= server['Valid-Until']:
- return info
- raise MixError("No time-valid information for server %s"%nickname)
+ if now + 3*60*60 >= serverSec['Valid-Until']:
+ getLog().warn("Ignoring soon-to-expire decriptor %s", p)
+ continue
+ if self.byNickname.has_key(nickname):
+ getLog().warn(
+ "Ignoring descriptor %s with duplicate nickname %s",
+ p, nickname)
+ continue
+ if self.byFilename.has_key(fname):
+ getLog().warn(
+ "Ignoring descriptor %s with duplicate prefix %s",
+ p, f)
+ continue
+ self.byNickname[nickname] = info
+ self.byFilename[f] = info
- def getAllCurrentServers(self, when=None, until=None):
- """Return all ServerInfo objects valid during a given interval."""
- self.load()
- if when is None:
- when = time.time()
- if until is None:
- until = when+1
- result = []
- for nickname, infos in self.servers.items():
- for info in infos:
- server = info['Server']
- if server['Valid-After'] <= when <= until <= server['Valid-Until']:
- result.append(info)
- return result
+ def getServerInfo(self, name):
+ if isinstance(name, ServerInfo):
+ return name
+ if self.byNickname.has_key(name):
+ return self.byNickname(name)
+ if self.byFilename.has_key(name):
+ return self.byFilename(name)
+ return None
- def importServerInfo(self, fname, force=1, string=None):
- """Import a server descriptor from an external file into the internal
- cache. Return 1 on import; 0 on failure."""
- self.load()
- if string is None:
- f = open(fname)
- contents = f.read()
- f.close()
- else:
- assert fname is None
- contents = string
- info = ServerInfo(string=contents, assumeValid=0)
- now = time.time()
- if info['Server']['Valid-Until'] < now:
- getLog().error("Not importing descriptor %s: already expired",
- fname)
- return 0
- nickname = info['Server']['Nickname']
- identity_pk = info['Server']['Identity'].get_public_key()
- if self.servers.has_key(nickname):
- other = self.servers[nickname][0]
- if other['Server']['Identity'].get_public_key() != identity_pk:
- getLog().error("Possible spoofing: that's not the public key I remember for %s", nickname)
- if not force:
- getLog().error("I'm not going to import it.")
- return 0
+ def getPath(self, minLength, serverList):
+ path = []
+ for s in serverList:
+ if isinstance(s, ServerInfo):
+ path.append(s)
+ elif isinstance(s, types.StringType):
+ server = self.dirCache.getServer(s)
+ if server is not None:
+ path.append(server)
+ elif os.path.exists(s):
+ try:
+ server = ServerInfo(fname=s, assumeValid=0)
+ path.append(server)
+ except OSError, e:
+ getLog().error("Couldn't read descriptor %s: %s",
+ s, e)
+ sys.exit(1)
+ except ConfigError, e:
+ getLog().error("Couldn't parse descriptor %s: %s",
+ s, e)
+ sys.exit(1)
else:
- getLog().error("... importing anyway.")
-
- for other in self.servers[nickname]:
- if other['Server']['Digest'] == info['Server']['Digest']:
- getLog().warn("Duplicate server info; skipping")
- return 0
-
- self.servers[nickname].append(info)
- else:
- self.servers[nickname] = [ info ]
-
- self.allServers.append(info)
-
- self.highest_num += 1
- fname_new = "si%d" % self.highest_num
- fd = os.open(os.path.join(self.dirname, fname_new),
- os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0600)
- f = os.fdopen(fd, 'w')
- f.write(contents)
- f.close()
-
- return 1
+ getLog().error("Couldn't find descriptor %s")
+ sys.exit(1)
+ return path
+ def getRandomServers(self, prng, n):
+ vals = self.byNickname.values()
+ if len(vals) < n:
+ raise MixFatalError("Not enough servers (%s requested)", n)
+ return prng.shuffle(vals, n)
+
def installDefaultConfig(fname):
"""Create a default, 'fail-safe' configuration in a given file"""
getLog().warn("No configuration file found. Installing default file in %s",
@@ -234,130 +175,34 @@
userdir = self.config['User']['UserDir']
createPrivateDir(userdir)
createPrivateDir(os.path.join(userdir, 'surbs'))
+ createPrivateDir(os.path.join(userdir, 'servers'))
- # Get directory cache FFFF (not used yet!)
- self.dirCache = DirectoryCache(os.path.join(userdir,
- 'directory', 'servers'))
- self.dirCache.load()
+ # Get directory cache
+ self.dirCache = TrivialDirectoryCache(
+ os.path.join(userdir,"servers"))
# Initialize PRNG
self.prng = mixminion.Crypto.AESCounterPRNG()
- def getDirectoryCache(self):
- return self.dirCache
-
- def _getRandomPath(self, length=None):
- # FFFF Base-list functionality
- if not length:
- length = self.config['Security'].get('PathLength',8)
-
- # XXXX We only pick servers that will be good for 24 hours. That's
- # XXXX bad! It allows a delaying/partitioning attack.
- servers = self.dirCache.getAllCurrentServers(when=time.time(),
- until=time.time()+24*60*60)
-
- # XXXX Pick only servers that relay to all other servers!
- # XXXX Watch out for many servers with the same IP or nickname or...
-
- if length > len(servers):
- getLog().warn("I only know about %s servers; That's not enough to use distinct servers on your path.", len(servers))
- result = []
- while len(result) < length:
- result.extend(self.prng.shuffle(servers))
- return result[:length]
- else:
- return self.prng.shuffle(servers, length)
-
- def _getPath(self, minLength, startAt, endAt, serverList=None):
- if serverList is not None:
- if len(serverList) < minLength:
- raise MixError("Path must have at least %s hops", minLength)
-
- serverList = [ self.dirCache.getCurrentServer(s,startAt,endAt)
- for s in serverList ]
- else:
- serverList = self._getRandomPath()
-
- if len(serverList) < minLength:
- serverList += self._getRandomPath(minLength-len(serverList))
-
- return serverList
-
- def sendForwardMessage(self, address, payload,
- serverList=None):
- message, firstHop = self.generateForwardMessage(address,
- payload,
- serverList)
- self.sendMessages([message], firstHop)
+ def sendForwardMessage(self, address, payload, path1, path2):
+ message, firstHop = \
+ self.generateForwardMessage(address, payload, path1, path2)
- def sendReplyMessage(self, payload, replyBlock, serverList=None):
- message, firstHop = self.generateReplyMessage(payload,
- replyBlock,
- serverList)
self.sendMessages([message], firstHop)
- def generateForwardMessage(self, address, payload, serverList=None):
- serverList = self._getPath(2,
- #XXXX This is bogus; see above
- time.time(), time.time()+24*60*60,
- serverList)
-
- firstPathLen = floorDiv(len(serverList), 2)
- servers1,servers2 = serverList[:firstPathLen],serverList[firstPathLen:]
+ def generateForwardMessage(self, address, payload, path1, path2):
+ servers1 = self.dirCache.getPath(path1)
+ servers2 = self.dirCache.getPath(path2)
+ # XXXXencode payloadXXXX
routingType, routingInfo, lastHop = address.getRouting()
if lastHop != None:
- servers2.append(self.dirCache.getCurrentServer(lastHop))
- msg = mixminion.BuildMessage.buildForwardMessage(payload,
- routingType,
- routingInfo,
- servers1, servers2)
+ servers2.append(self.dirCache.getServerInfo(lastHop))
+ msg = buildForwardMessage(payload,
+ routingType, routingInfo,
+ servers1, servers2)
return msg, servers1[0]
- def generateReplyBlock(self, address, startAt=None, endAt=None,
- password=None, serverList=None):
- if startAt is None:
- startAt = time.time()
- if endAt is None:
- lifetime = self.config['Security'].get('SURBLifetime')
- if lifetime:
- endAt = startAt + lifetime[2]
- else:
- endAt = startAt + 24*60*60*7
-
- path = self._getPath(2,
- #XXXX This is bogus; see above
- startAt, endAt,
- serverList)
-
- if password:
- # XXXX Out of sync with spec.
- raise MixFatalError("Not implemented")
-
- handle = mixminion.Crypto.getBytes(16)
- rt, ri, lastHop = address.getRouting("RTRN"+handle)
- if lastHop is not None:
- path.append(lastHop)
- block, secrets = mixminion.BuildMessage.buildReplyBlock(path, rt, ri,
- endAt,
- self.prng)
-
- # XXXX Store secrets and expiry time
- return block
-
- def generateReplyMessage(self, payload, replyBlock, serverList=None):
- # XXXX Not in sync with spec
- path = self._getPath(1, time.time(), replyBlock.timestamp,
- serverList)
-
- msg = mixminion.BuildMessage.buildReplyMessage(payload,
- path,
- replyBlock)
- return msg, path[0]
-
- def decodeReplyMessage(self, tag, payload):
- pass
-
def sendMessages(self, msgList, server):
con = mixminion.MMTPClient.BlockingClientConnection(server.getAddr(),
server.getPort(),
@@ -369,10 +214,15 @@
finally:
con.shutdown()
+def parseAddress():
+
class Address:
- def getRouting(self, tag=None):
- # Return rt, ri, lasthop
- raise NotImplemented("Address.getRouting()")
+ def __init__(self, exitType, exitAddress, lastHop=None):
+ self.exitType = exitType
+ self.exitAddress = exitAddress
+ self.lastHop = lastHop
+ def getRouting(self):
+ return self.exitType, self.exitAddress, self.lastHop
def sendTestMessage(servers1, servers2):
assert len(servers1)
@@ -384,9 +234,8 @@
Here.
"""
rt, ri = 0xFFFE, "deliver"
- m = mixminion.BuildMessage.buildForwardMessage(payload,
- rt, ri,
- servers1, servers2)
+ m = buildForwardMessage(payload, rt, ri, servers1, servers2)
+
firstHop = servers1[0]
b = mixminion.MMTPClient.BlockingClientConnection(firstHop.getAddr(),
firstHop.getPort(),
@@ -395,7 +244,6 @@
b.sendPacket(m)
b.shutdown()
-
def readConfigFile(configFile):
try:
return ClientConfig(fname=configFile)
@@ -431,5 +279,3 @@
idx = floorDiv(len(servers),2)
sendTestMessage(servers[:idx], servers[idx:])
-
-