Commit 7c5b3b8e authored by Nick Mathewson's avatar Nick Mathewson 👉
Browse files

r17219@catbus: nickm | 2007-12-17 22:33:55 -0500

 Add python logging support, better message templates, and support for partially empty rings.


svn:r12849
parent 45f60221
......@@ -9,6 +9,11 @@ RUN_IN_DIR = "~/run/"
# Either a file in which to write our pid, or None
PIDFILE = "bridgedb.pid"
# Either a file to log to, or None if we should log to the console.
LOGFILE = "bridgedb.log"
# One of "DEBUG", "INFO", "WARNING", "ERROR"...
LOGLEVEL = "INFO"
# Files from which we read descriptors on start and SIGHUP
# XXXX SIGHUP not implemented.
BRIDGE_FILES = [ "./cached-descriptors", "./cached-descriptors.new" ]
......
......@@ -5,6 +5,7 @@
import binascii
import bisect
import hmac
import logging
import re
import sha
import socket
......@@ -153,6 +154,7 @@ class BridgeRing(BridgeHolder):
self.hmac = get_hmac_fn(key, hex=False)
self.isSorted = False
self.sortedKeys = []
self.name = "Ring"
def __len__(self):
return len(self.bridgesByID)
......@@ -165,6 +167,7 @@ class BridgeRing(BridgeHolder):
self.isSorted = False
self.bridges[pos] = bridge
self.bridgesByID[id] = bridge
logging.debug("Adding %s to %s", bridge.getConfigLine(), self.name)
def sort(self):
if not self.isSorted:
......@@ -270,7 +273,7 @@ class FixedBridgeSplitter(BridgeHolder):
class UnallocatedHolder(BridgeHolder):
def insert(self, bridge):
pass
logging.debug("Leaving %s unallocated", bridge.getConfigLine())
def assignmentsArePersistent(self):
return False
......
......@@ -4,6 +4,7 @@
import bridgedb.Bridges
import logging
import re
import socket
......@@ -24,6 +25,7 @@ class IPBasedDistributor(bridgedb.Bridges.BridgeHolder):
for n in xrange(nClusters):
key1 = bridgedb.Bridges.get_hmac(key, "Order-Bridges-In-Ring-%d"%n)
self.rings.append( bridgedb.Bridges.BridgeRing(key1) )
self.rings[-1].name = "IP ring %s"%len(self.rings)
key2 = bridgedb.Bridges.get_hmac(key, "Assign-Bridges-To-Rings")
self.splitter = bridgedb.Bridges.FixedBridgeSplitter(key2, self.rings)
......@@ -38,20 +40,25 @@ class IPBasedDistributor(bridgedb.Bridges.BridgeHolder):
self.splitter.insert(bridge)
def getBridgesForIP(self, ip, epoch, N=1):
if not len(self.splitter):
return []
area = self.areaMapper(ip)
# Which bridge cluster should we look at?
h = int( self.areaClusterHmac(area)[:8], 16 )
h = int( self.areaClusterHmac(area)[:8], 16)
clusterNum = h % len(self.rings)
ring = self.rings[clusterNum]
# If a ring is empty, consider the next.
while not len(ring):
clusterNum = (clusterNum + 1) % len(self.rings)
ring = self.rings[clusterNum]
# Now get the bridge.
pos = self.areaOrderHmac("<%s>%s" % (epoch, area))
return ring.getBridges(pos, N)
# These characters are the ones that RFC2822 allows.
#ASPECIAL = '!#$%&*+-/=?^_`{|}~'
#ASPECIAL += "\\\'"
......@@ -129,6 +136,7 @@ class EmailBasedDistributor(bridgedb.Bridges.BridgeHolder):
key2 = bridgedb.Bridges.get_hmac(key, "Order-Bridges-In-Ring")
self.ring = bridgedb.Bridges.BridgeRing(key2)
self.ring.name = "email ring"
self.store = store
self.domainmap = domainmap
......@@ -141,8 +149,11 @@ class EmailBasedDistributor(bridgedb.Bridges.BridgeHolder):
return [] #XXXX raise an exception.
if self.store.has_key(emailaddress):
result = []
ids = self.store[emailaddress]
for id in bridgedb.Bridges.chopString(ids, bridgedb.Bridges.ID_LEN):
ids_str = self.store[emailaddress]
ids = bridgedb.Bridges.chopString(ids_str, bridgedb.Bridges.ID_LEN)
logging.info("We've seen %r before. Sending the same %d bridges"
" as last time", emailaddress, len(ids))
for id in ids:
b = self.ring.getBridgeByID(id)
if b != None:
result.append(b)
......@@ -165,4 +176,3 @@ if __name__ == '__main__':
print normal
except BadEmail, e:
print line, e
......@@ -6,6 +6,7 @@ import anydbm
import os
import signal
import sys
import logging
from twisted.internet import reactor
......@@ -22,6 +23,9 @@ CONFIG = Conf(
RUN_IN_DIR = ".",
PIDFILE = "bridgedb.pid",
LOGFILE = None,
LOGLEVEL = "DEBUG",
BRIDGE_FILES = [ "./cached-descriptors", "./cached-descriptors.new" ],
BRIDGE_PURPOSE = "bridge",
DB_FILE = "./bridgedist.db",
......@@ -53,6 +57,18 @@ CONFIG = Conf(
RESERVED_SHARE=2,
)
def configureLogging(cfg):
level = getattr(cfg, 'LOGLEVEL', 'WARNING')
level = getattr(logging, level)
extra = {}
if getattr(cfg, "LOGFILE"):
extra['filename'] = cfg.LOGFILE
logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s',
datefmt="%b %d %H:%M:%S",
level=level,
**extra)
def getKey(fname):
"""Load the key stored in fname, or create a new 32-byte key and store
it in fname.
......@@ -97,7 +113,7 @@ def _handleSIGHUP(*args):
def startup(cfg):
cfg.BRIDGE_FILES = [ os.path.expanduser(fn) for fn in cfg.BRIDGE_FILES ]
for key in ("RUN_IN_DIR", "DB_FILE", "DB_LOG_FILE", "MASTER_KEY_FILE",
"HTTPS_CERT_FILE", "HTTPS_KEY_FILE", "PIDFILE"):
"HTTPS_CERT_FILE", "HTTPS_KEY_FILE", "PIDFILE", "LOGFILE"):
v = getattr(cfg, key)
if v:
setattr(cfg, key, os.path.expanduser(v))
......@@ -110,6 +126,8 @@ def startup(cfg):
f.write("%s\n"%os.getpid())
f.close()
configureLogging(cfg)
key = getKey(cfg.MASTER_KEY_FILE)
dblogfile = None
emailDistributor = ipDistributor = None
......@@ -149,14 +167,15 @@ def startup(cfg):
Bridges.PrefixStore(store, "ls|"))
splitter.addTracker(stats)
print "Loading bridges"
logging.info("Loading bridges")
load(cfg, splitter)
print "%d bridges loaded" % len(splitter)
logging.info("%d bridges loaded", len(splitter))
if emailDistributor:
print "%d for email" % len(emailDistributor.ring)
logging.info("%d for email", len(emailDistributor.ring))
if ipDistributor:
print "%d for web:" % len(ipDistributor.splitter)
print " by location set:", " ".join(str(len(r)) for r in ipDistributor.rings)
logging.info("%d for web:", len(ipDistributor.splitter))
logging.info(" by location set: %s",
" ".join(str(len(r)) for r in ipDistributor.rings))
if cfg.HTTPS_DIST and cfg.HTTPS_SHARE:
Server.addWebServer(cfg, ipDistributor, webSchedule)
......@@ -172,7 +191,7 @@ def startup(cfg):
signal.signal(signal.SIGHUP, _handleSIGHUP)
try:
print "Starting reactors."
logging.info("Starting reactors.")
Server.runServers()
finally:
baseStore.close()
......
......@@ -82,6 +82,7 @@ class WebResource(twisted.web.resource.Resource):
else:
answer = "No bridges available."
logging.info("Replying to web request from %s", ip)
return HTML_MESSAGE_TEMPLAY % answer
def addWebServer(cfg, dist, sched):
......@@ -125,13 +126,14 @@ def getMailResponse(lines, ctx):
elif clientFromAddr and clientFromAddr[1]:
clientAddr = clientFromAddr[1]
else:
print "No from header. WTF."
logging.info("No From or Sender header on incoming mail.")
return None,None
for ln in lines:
if ln.strip().lower() in ("get bridges", "subject: get bridges"):
break
else:
print "No request for bridges."
logging.info("Got a mail from %r with no bridge request; dropping",
clientAddr)
return None,None
try:
......@@ -139,10 +141,11 @@ def getMailResponse(lines, ctx):
bridges = ctx.distributor.getBridgesForEmail(clientAddr,
interval, ctx.N)
except bridgedb.Dist.BadEmail, e:
print "Bad email addr in request: %s"%e
logging.info("Got a mail from a bad email address %r: %s.",
clientAddr, e)
return None, None
if not bridges:
print "No bridges available."
logging.warning("No bridges available to send to %r", clientAddr)
return None, None
# Generate the message.
......@@ -164,7 +167,7 @@ def getMailResponse(lines, ctx):
return clientAddr, f
def replyToMail(lines, ctx):
print "Got complete email; attempting to reply."
logging.info("Got a completed email; attempting to reply.")
sendToUser, response = getMailResponse(lines, ctx)
if response is None:
return
......@@ -176,7 +179,7 @@ def replyToMail(lines, ctx):
response,
d)
reactor.connectTCP(ctx.smtpServer, ctx.smtpPort, factory)
print "Sending reply."
logging.info("Sending reply to %r", sendToUser)
return d
class MailContext:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment