[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r15143: Add support for the stable flag and for proxy lists. (in bridgedb/trunk: . lib/bridgedb)
Author: nickm
Date: 2008-06-11 18:10:27 -0400 (Wed, 11 Jun 2008)
New Revision: 15143
Modified:
bridgedb/trunk/bridgedb.conf
bridgedb/trunk/lib/bridgedb/Bridges.py
bridgedb/trunk/lib/bridgedb/Main.py
Log:
Add support for the stable flag and for proxy lists.
Modified: bridgedb/trunk/bridgedb.conf
===================================================================
--- bridgedb/trunk/bridgedb.conf 2008-06-11 20:44:22 UTC (rev 15142)
+++ bridgedb/trunk/bridgedb.conf 2008-06-11 22:10:27 UTC (rev 15143)
@@ -37,6 +37,15 @@
# This is a list of (port,minimum) tuples.
FORCE_PORTS = [ (443, 1) ]
+# If possible, always give a certain number of answers with a given flag.
+# Only "stable" is now supported. This is a list of (flag,minimum) tuples.
+FORCE_FLAGS = [ ("Stable", 1) ]
+
+# A list of filenames that contain IP addresses (one per line) of proxies.
+# All IP-based distributors that see an incoming connection from a proxy
+# will treat them specially.
+PROXY_LIST_FILES = [ ]
+
#==========
# Options related to HTTPS
Modified: bridgedb/trunk/lib/bridgedb/Bridges.py
===================================================================
--- bridgedb/trunk/lib/bridgedb/Bridges.py 2008-06-11 20:44:22 UTC (rev 15142)
+++ bridgedb/trunk/lib/bridgedb/Bridges.py 2008-06-11 22:10:27 UTC (rev 15143)
@@ -106,7 +106,7 @@
self.nickname = nickname
self.ip = ip
self.orport = orport
- self.running = None
+ self.running = self.stable = None
if id_digest is not None:
assert fingerprint is None
if len(id_digest) != DIGEST_LEN:
@@ -138,9 +138,11 @@
assert is_valid_fingerprint(self.fingerprint)
assert 1 <= self.orport <= 65535
- def setStatus(self, running=None):
+ def setStatus(self, running=None, stable=None):
if running is not None:
self.running = running
+ if stable is not None:
+ self.stable = stable
def parseDescFile(f, bridge_purpose='bridge'):
@@ -190,11 +192,7 @@
logging.warn("Unparseable base64 ID %r", line.split()[2])
elif ID and line.startswith("s "):
flags = line.split()
- if "Running" in flags:
- yield ID, True
- else:
- yield ID, False
- ID = None
+ yield ID, ("Running" in flags), ("Stable" in flags)
class BridgeHolder:
"""Abstract base class for all classes that hold bridges."""
@@ -206,15 +204,22 @@
class BridgeRingParameters:
"""DOCDOC"""
- def __init__(self, needPorts=()):
+ def __init__(self, needPorts=(), needFlags=()):
"""DOCDOC takes list of port, count"""
for port,count in needPorts:
if not (1 <= port <= 65535):
raise TypeError("Port %s out of range."%port)
if count <= 0:
raise TypeError("Count %s out of range."%count)
+ for flag, count in needFlags:
+ flag = flag.lower()
+ if flag not in [ "stable" ]:
+ raise TypeError("Unsupported flag %s"%flag)
+ if count <= 0:
+ raise TypeError("Count %s out of range."%count)
self.needPorts = needPorts[:]
+ self.needFlags = needFlags[:]
class BridgeRing(BridgeHolder):
"""Arranges bridges in a ring based on an hmac function."""
@@ -235,19 +240,24 @@
answerParameters = BridgeRingParameters()
self.answerParameters = answerParameters
- self.portSubrings = [] #DOCDOC
+ self.subrings = [] #DOCDOC
for port,count in self.answerParameters.needPorts:
#note that we really need to use the same key here, so that
# the mapping is in the same order for all subrings.
- self.portSubrings.append( (port,count,BridgeRing(key,None)) )
+ self.subrings.append( ('port',port,count,BridgeRing(key,None)) )
+ for flag,count in self.answerParameters.needFlags:
+ self.subrings.append( ('flag',flag,count,BridgeRing(key,None)) )
self.setName("Ring")
def setName(self, name):
"""DOCDOC"""
self.name = name
- for port,_,subring in self.portSubrings:
- subring.setName("%s (port-%s subring)"%(name, port))
+ for tp,val,_,subring in self.subrings:
+ if tp == 'port':
+ subring.setName("%s (port-%s subring)"%(name, val))
+ else:
+ subring.setname("%s (%s subring)"%(name, val))
def __len__(self):
return len(self.bridges)
@@ -255,9 +265,14 @@
def insert(self, bridge):
"""Add a bridge to the ring. If the bridge is already there,
replace the old one."""
- for port,_,subring in self.portSubrings:
- if port == bridge.orport:
- subring.insert(bridge)
+ for tp,val,_,subring in self.subrings:
+ if tp == 'port':
+ if val == bridge.orport:
+ subring.insert(bridge)
+ else:
+ assert tp == 'flag' and val == 'stable'
+ if val == 'stable' and bridge.stable:
+ subring.insert(bridge)
ident = bridge.getID()
pos = self.hmac(ident)
@@ -293,7 +308,7 @@
def getBridges(self, pos, N=1):
"""Return the N bridges appearing in the ring after position pos"""
forced = []
- for _,count,subring in self.portSubrings:
+ for _,_,count,subring in self.subrings:
if len(subring) < count:
count = len.subring
forced.extend(subring._getBridgeKeysAt(pos, count))
@@ -309,7 +324,7 @@
def getBridgeByID(self, fp):
"""Return the bridge whose identity digest is fp, or None if no such
bridge exists."""
- for _,_,subring in self.portSubrings:
+ for _,_,_,subring in self.subrings:
b = subring.getBridgeByID(fp)
if b is not None:
return b
Modified: bridgedb/trunk/lib/bridgedb/Main.py
===================================================================
--- bridgedb/trunk/lib/bridgedb/Main.py 2008-06-11 20:44:22 UTC (rev 15142)
+++ bridgedb/trunk/lib/bridgedb/Main.py 2008-06-11 22:10:27 UTC (rev 15143)
@@ -43,7 +43,9 @@
N_IP_CLUSTERS = 4,
MASTER_KEY_FILE = "./secret_key",
- REQUIRE_ORPORTS = [(443, 1)],
+ FORCE_PORTS = [(443, 1)],
+ FORCE_FLAGS = [("Stable", 1)],
+ PROXY_LIST_FILES = [ ],
HTTPS_DIST = True,
HTTPS_SHARE=10,
@@ -127,24 +129,47 @@
status = {}
if hasattr(cfg, "STATUS_FILE"):
f = open(cfg.STATUS_FILE, 'r')
- for ID, running in Bridges.parseStatusFile(f):
- status[ID] = running
+ for ID, running, stable in Bridges.parseStatusFile(f):
+ status[ID] = running, stable
for fname in cfg.BRIDGE_FILES:
f = open(fname, 'r')
for bridge in Bridges.parseDescFile(f, cfg.BRIDGE_PURPOSE):
- running = status.get(bridge.getID())
- if running is not None:
- bridge.setStatus(running=running)
+ s = status.get(bridge.getID())
+ if s is not None:
+ running, stable = s
+ bridge.setStatus(running=running, stable=stable)
splitter.insert(bridge)
f.close()
+def loadProxyList(cfg):
+ ipset = {}
+ for fname in cfg.PROXY_LIST_FILES:
+ f = open(fname, 'r')
+ for line in f:
+ line = line.strip()
+ if Bridges.is_valid_ip(line):
+ ipset[line] = True
+ elif line:
+ logging.info("Skipping line %r in %s: not an IP.",
+ line, fname)
+ f.close()
+ return ipset
+
_reloadFn = lambda: True
def _handleSIGHUP(*args):
"""Called when we receive a SIGHUP; invokes _reloadFn."""
reactor.callLater(0, _reloadFn)
+class ProxyCategory:
+ def __init__(self):
+ self.ipset = {}
+ def contains(self, ip):
+ return self.ipset.has_key(ip)
+ def replaceProxyList(self, ipset):
+ self.ipset = ipset
+
def startup(cfg):
- """Parse bridges,
+ """Parse bridges,
"""
# Expand any ~ characters in paths in the configuration.
cfg.BRIDGE_FILES = [ os.path.expanduser(fn) for fn in cfg.BRIDGE_FILES ]
@@ -154,6 +179,11 @@
v = getattr(cfg, key, None)
if v:
setattr(cfg, key, os.path.expanduser(v))
+ if hasattr(cfg, "PROXY_LIST_FILES"):
+ cfg.PROXY_LIST_FILES = [
+ os.path.expanduser(v) for v in cfg.PROXY_LIST_FILES ]
+ else:
+ cfg.PROXY_LIST_FILES = [ ]
# Change to the directory where we're supposed to run.
if cfg.RUN_IN_DIR:
@@ -178,6 +208,10 @@
dblogfile = open(cfg.DB_LOG_FILE, "a+", 0)
store = Bridges.LogDB(None, store, dblogfile)
+ # Get a proxy list.
+ proxyList = ProxyCategory()
+ proxyList.replaceProxyList(loadProxyList(cfg))
+
# Create a BridgeSplitter to assign the bridges to the different
# distributors.
splitter = Bridges.BridgeSplitter(Bridges.get_hmac(key, "Splitter-Key"),
@@ -185,15 +219,23 @@
# Create ring parameters.
forcePorts = getattr(cfg, "FORCE_PORTS")
- ringParams=Bridges.BridgeRingParameters(forcePorts=forcePorts)
+ forceFlags = getattr(cfg, "FORCE_FLAGS")
+ if not forcePorts: forcePorts = []
+ if not forceFlags: forceFlags = []
+ ringParams=Bridges.BridgeRingParameters(needPorts=forcePorts,
+ needFlags=forceFlags)
emailDistributor = ipDistributor = None
# As appropriate, create an IP-based distributor.
if cfg.HTTPS_DIST and cfg.HTTPS_SHARE:
+ categories = []
+ if proxyList.ipset:
+ categories.append(proxyList)
ipDistributor = Dist.IPBasedDistributor(
Dist.uniformMap,
cfg.N_IP_CLUSTERS,
Bridges.get_hmac(key, "HTTPS-IP-Dist-Key"),
+ categories,
answerParameters=ringParams)
splitter.addRing(ipDistributor, "https", cfg.HTTPS_SHARE)
webSchedule = Time.IntervalSchedule("day", 2)
@@ -226,6 +268,7 @@
def reload():
logging.info("Caught SIGHUP")
load(cfg, splitter)
+ proxyList.replaceProxyList(loadProxyList(cfg))
logging.info("%d bridges loaded", len(splitter))
if emailDistributor:
logging.info("%d for email", len(emailDistributor.ring))
@@ -233,6 +276,7 @@
logging.info("%d for web:", len(ipDistributor.splitter))
logging.info(" by location set: %s",
" ".join(str(len(r)) for r in ipDistributor.rings))
+
global _reloadFn
_reloadFn = reload
signal.signal(signal.SIGHUP, _handleSIGHUP)