[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)