[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[minion-cvs] Add support for blocking servers as entries, exits, or ...
Update of /home/minion/cvsroot/src/minion/lib/mixminion
In directory moria.mit.edu:/tmp/cvs-serv32560/lib/mixminion
Modified Files:
ClientDirectory.py Config.py ServerInfo.py
Log Message:
Add support for blocking servers as entries, exits, or entirely
Index: ClientDirectory.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ClientDirectory.py,v
retrieving revision 1.28
retrieving revision 1.29
diff -u -d -r1.28 -r1.29
--- ClientDirectory.py 17 Jan 2004 04:23:37 -0000 1.28
+++ ClientDirectory.py 27 Jan 2004 05:55:54 -0000 1.29
@@ -60,11 +60,9 @@
# byNickname: Map from nickname.lower() to list of (ServerInfo, source)
# tuples.
# byKeyID: Map from desc.getKeyDigest() to list of (ServerInfo,source)
- # byCapability: Map from capability ('mbox'/'smtp'/'relay'/None) to
- # list of (ServerInfo, source) tuples.
- # allServers: Same as byCapability[None]
+ # allServers: DOCDOC
# __scanning: Flag to prevent recursive invocation of self.rescan().
- # __downloading: Flag to prevent simultaneous invocation of
+ # __downloading: Flag to prevent simultaneous invocation of
# downloadDirectory()
# clientVersions: String of allowable client versions as retrieved
# from most recent directory.
@@ -100,6 +98,8 @@
self.__scanning = 0
self.__downloading = 0
self._lock = RWLock()
+ self.config = None
+ self.blockedNicknames = {} #nickname->['*','entry','exit']
if diskLock is None:
self._diskLock = threading.RLock()
else:
@@ -111,6 +111,26 @@
finally:
self._diskLock.release()
+ def configure(self, config):
+ """DOCDOC"""
+ sec = config.get("Security", {})
+ blocked = {}
+ for lst in sec.get("BlockServers", []):
+ for nn in lst:
+ blocked[nn] = ['*']
+ for lst in sec.get("BlockEntries", []):
+ for nn in lst:
+ blocked.setdefault(nn,[]).append('entry')
+ for lst in sec.get("BlockExits", []):
+ for nn in lst:
+ blocked.setdefault(nn,[]).append('exit')
+ self._lock.write_in()
+ try:
+ self.blockedNicknames = blocked
+ finally:
+ self._lock.write_out()
+
+
def updateDirectory(self, forceDownload=0, timeout=None, now=None):
"""Download a directory from the network as needed."""
if now is None:
@@ -133,7 +153,7 @@
self.__downloading = 1
finally:
self._lock.write_out()
-
+
try:
self._downloadDirectory(timeout)
finally:
@@ -322,7 +342,7 @@
self.serverVersions = s_serverVersions
self.goodServerNicknames = s_goodServerNicknames
self.digestMap = s_digestMap
-
+
# Regenerate the cache
self.__save()
# Now try reloading, to make sure we can, and for __rebuildTables.
@@ -416,7 +436,7 @@
self._lock.write_in()
try:
-
+
self._diskLock.acquire()
try:
# Copy the server into DIR/servers.
@@ -464,34 +484,28 @@
return len(badSources)
def __rebuildTables(self):
- """Helper method. Reconstruct byNickname, byKeyID,
- allServers, and byCapability from the internal start of
- this object. Caller must hold write lock."""
+ """Helper method. Reconstruct byNickname, byKeyID, and allServers
+ from the internal state of this object. Caller must hold write
+ lock."""
self.byNickname = {}
self.byKeyID = {}
self.allServers = []
- self.byCapability = { 'mbox': [],
- 'smtp': [],
- 'relay': [],
- 'frag': [],
- None: self.allServers }
+ self.byCapability = { } #DOCDOC disused
self.goodServerNicknames = {}
for info, where in self.serverList:
nn = info.getNickname().lower()
- lists = [ self.allServers, self.byNickname.setdefault(nn, []),
- self.byKeyID.setdefault(info.getKeyDigest(), []) ]
- for c in info.getCaps():
- lists.append( self.byCapability[c] )
- for lst in lists:
- lst.append((info, where))
+ self.allServers.append((info,where))
self.goodServerNicknames[nn] = 1
for info, where in self.fullServerList:
nn = info.getNickname().lower()
- if self.goodServerNicknames.get(nn):
- continue
- self.byNickname.setdefault(nn, []).append((info, where))
+ lists = [
+ self.byNickname.setdefault(nn, []),
+ self.byKeyID.setdefault(info.getKeyDigest(), [])
+ ]
+ for lst in lists:
+ lst.append((info, where))
def getFeatureMap(self, features, at=None, goodOnly=0):
"""Given a list of feature names (see Config.resolveFeatureName for
@@ -525,6 +539,7 @@
continue
nickname = sd.getNickname()
isGood = self.goodServerNicknames.get(nickname.lower(), 0)
+ blocked = self.blockedNicknames.get(nickname.lower(), [])
if goodOnly and not isGood:
continue
va = sd['Server']['Valid-After']
@@ -533,10 +548,20 @@
for feature,(sec,ent) in resFeatures:
if sec == '+':
if ent == 'status':
- if isGood:
- d['status'] = "(ok)"
+ stat = []
+ if not isGood:
+ stat.append("not recommended")
+ if '*' in blocked:
+ stat.append("blocked")
else:
- d['status'] = "(not recommended)"
+ if 'entry' in blocked:
+ stat.append("blocked as entry")
+ if 'exit' in blocked:
+ stat.append("blocked as exit")
+ if stat:
+ d['status'] = "(%s)"%(",".join(stat))
+ else:
+ d['status'] = "(ok)"
else:
raise AssertionError # Unreached.
else:
@@ -574,6 +599,41 @@
return u.values()
+ def __nicknameIsBlocked(self, nn, isEntry=0, isExit=0):
+ """DOCDOC"""
+ b = self.blockedNicknames.get(nn.lower(), None)
+ if b is None:
+ return 0
+ elif '*' in b:
+ return 1
+ elif isEntry and 'entry' in b:
+ return 1
+ elif isExit and 'exit' in b:
+ return 1
+ else:
+ return 0
+
+ def __excludeBlocked(self, lst, isEntry=0, isExit=0):
+ """DOCDOC"""
+ res = []
+ for info in lst:
+ if not self.__nicknameIsBlocked(info.getNickname(),isEntry,isExit):
+ res.append(info)
+ return res
+
+ def getNicknameByIP(self, ip):
+ """DOCDOC"""
+ self._lock.read_in()
+ try:
+ nicknames = []
+ for desc,where in self.fullServerList:
+ if desc.getIP() == ip:
+ if desc.getNickname() not in nicknames:
+ nicknames.append(desc.getNickname())
+ return "/".join(nicknames)
+ finally:
+ self._lock.read_out()
+
def getNicknameByKeyID(self, keyid):
"""Given a keyid, return the nickname of the server with that keyid.
Return None if no such server is known."""
@@ -584,7 +644,7 @@
return None
r = []
for (desc,_) in s:
- if desc.getNickname().lower() not in r:
+ if desc.getNickname() not in r:
r.append(desc.getNickname())
return "/".join(r)
finally:
@@ -614,10 +674,11 @@
else:
return nn
- def getLiveServers(self, startAt=None, endAt=None):
+ def getLiveServers(self, startAt=None, endAt=None, isEntry=0, isExit=0):
"""Return a list of all server desthat are live from startAt through
endAt. The list is in the standard (ServerInfo,where) format,
as returned by __find.
+ DOCDOC no blocked or notrecommended
"""
if startAt is None:
startAt = time.time()
@@ -625,7 +686,9 @@
endAt = time.time()+self.DEFAULT_REQUIRED_LIFETIME
self._lock.read_in()
try:
- return self.__find(self.serverList, startAt, endAt)
+ return self.__excludeBlocked(
+ self.__find(self.allServers, startAt, endAt),
+ isEntry=isEntry, isExit=isExit)
finally:
self._lock.read_out()
@@ -680,7 +743,7 @@
val = getattr(self, field)
val = [ (i,w) for i,w in val if not badSources.has_key(w) ]
setattr(self,field,val)
- for field in 'byNickname', 'byKeyID', 'byCapability':
+ for field in 'byNickname', 'byKeyID':
d = getattr(self,field)
for k,v in d.items():
v = [ (i,w) for i,w in v if not badSources.has_key(w) ]
@@ -704,8 +767,7 @@
such server is found, return None.
name -- A ServerInfo object, a nickname, or a filename.
- """
-
+ """
if startAt is None:
startAt = time.time()
if endAt is None:
@@ -860,7 +922,7 @@
if prng is None:
prng = mixminion.Crypto.getCommonPRNG()
- # Resolve explicitly-provided servers
+ # Resolve explicitly-provided servers (we already warned.)
servers = []
for name in template:
if name is None:
@@ -869,7 +931,8 @@
servers.append(self.getServerInfo(name, startAt, endAt, 1))
# Now figure out which relays we haven't used yet.
- relays = self.__find(self.byCapability['relay'], startAt, endAt)
+ relays = self.__find(self.allServers, startAt, endAt)
+ relays = self.__excludeBlocked(relays)
if not relays:
raise UIError("No relays known")
elif len(relays) == 2:
@@ -893,6 +956,14 @@
# ...and see if there are any relays left that aren't adjacent?
candidates = []
for c in relays:
+ # Skip blocked entry points
+ if i==0 and self.__nicknameIsBlocked(c.getNickname(),
+ isEntry=1):
+ continue
+ # Skip blocked exit points
+ if i==(len(servers)-1) and self.__nicknameIsBlocked(
+ c.getNickname(), isExit=1):
+ continue
# Avoid same-server hops
if ((prev and c.hasSameNicknameAs(prev)) or
(next and c.hasSameNicknameAs(next))):
@@ -906,7 +977,7 @@
continue
candidates.append(c)
if candidates:
- # Good. There aresome okay servers/
+ # Good. There are some okay servers.
servers[i] = prng.pick(candidates)
else:
# Nope. Can we duplicate a relay?
@@ -947,7 +1018,7 @@
self._lock.read_out()
def _validatePath(self, pathSpec, exitAddress, startAt=None, endAt=None,
- warnUnrecommended=1):
+ warnUnrecommended=1):
"""Helper: implement validatePath without getting lock"""
if startAt is None: startAt = time.time()
if endAt is None: endAt = startAt+self.DEFAULT_REQUIRED_LIFETIME
@@ -996,19 +1067,33 @@
if not warnUnrecommended:
return
warned = {}
- for e in p:
+ for i in xrange(len(p)):
+ e = p[i]
fixed = e.getFixedServer(self, startAt, endAt)
if not fixed: continue
- lc_nickname = fixed.getNickname().lower()
+ nick = fixed.getNickname()
+ lc_nickname = nick.lower()
+ if warned.has_key(lc_nickname):
+ continue
if not self.goodServerNicknames.has_key(lc_nickname):
- if warned.has_key(lc_nickname):
- continue
warned[lc_nickname] = 1
- LOG.warn("Server %s is not recommended",fixed.getNickname())
+ LOG.warn("Server %s is not recommended", nick)
+ b = self.blockedNicknames.get(lc_nickname,None)
+ if not b: continue
+ if '*' in b:
+ warned[lc_nickname] = 1
+ LOG.warn("Server %s is blocked", nick)
+ else:
+ if i == 0 and 'entry' in b:
+ warned[lc_nickname] = 1
+ LOG.warn("Server %s is blocked as an entry", nick)
+ elif i==(len(p)-1) and 'exit' in b:
+ warned[lc_nickname] = 1
+ LOG.warn("Server %s is blocked as an exit", nick)
def checkSoftwareVersion(self,client=1):
"""Check the current client's version against the stated version in
- the most recently downloaded directory; print a warning if this
+ the most recently downloaded directory; log a warning if this
version isn't listed as recommended.
"""
self._lock.read_in()
@@ -1338,7 +1423,7 @@
exit address.
"""
assert self.lastHop is None
- liveServers = directory.getLiveServers(startAt, endAt)
+ liveServers = directory.getLiveServers(startAt, endAt, isExit=1)
result = [ desc for desc in liveServers
if self.isSupportedByServer(desc) and
desc.supportsPacketVersion() ]
Index: Config.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Config.py,v
retrieving revision 1.76
retrieving revision 1.77
diff -u -d -r1.76 -r1.77
--- Config.py 27 Jan 2004 05:30:23 -0000 1.76
+++ Config.py 27 Jan 2004 05:55:54 -0000 1.77
@@ -985,13 +985,16 @@
'ForwardPath' : ('ALLOW', None, "*6"),
'ReplyPath' : ('ALLOW', None, "*4"),
'SURBPath' : ('ALLOW', None, "*4"),
+ #DOCDOC and add to .mixminionrc
+ 'BlockServers' : ('ALLOW*', 'seq', ""),
+ 'BlockEntries' : ('ALLOW*', 'seq', ""),
+ 'BlockExits' : ('ALLOW*', 'seq', ""),
},
'Network' : { 'ConnectionTimeout' : ('ALLOW', "interval", "2 minutes")}
}
def __init__(self, fname=None, string=None):
_ConfigFile.__init__(self, fname, string)
-
def prevalidate(self, contents):
# See if we've been passed a server configuration.
foundServer = 0
Index: ServerInfo.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ServerInfo.py,v
retrieving revision 1.77
retrieving revision 1.78
diff -u -d -r1.77 -r1.78
--- ServerInfo.py 27 Jan 2004 05:14:31 -0000 1.77
+++ ServerInfo.py 27 Jan 2004 05:55:54 -0000 1.78
@@ -394,6 +394,10 @@
return 1
return 0
+ def canRelay(self):
+ """Return 1"""
+ return 1
+
def canStartAt(self):
"""Return true iff this server is one we (that is, this
version of Mixminion) can send packets to directly."""