Main.py 4.97 KB
Newer Older
1
2
3
# BridgeDB by Nick Mathewson.
# Copyright (c) 2007, The Tor Project, Inc.
# See LICENSE for licensing informatino
4
5
6
7
8
9

import anydbm

import os
import sys

10
11
import bridgedb.Bridges as Bridges
import bridgedb.Dist as Dist
12
13
import bridgedb.Time as Time
import bridgedb.Server as Server
14
15
16
17
18
19

class Conf:
    def __init__(self, **attrs):
        self.__dict__.update(attrs)

CONFIG = Conf(
20
21
    RUN_IN_DIR = ".",

22
23
    BRIDGE_FILES = [ "./cached-descriptors", "./cached-descriptors.new" ],
    BRIDGE_PURPOSE = "bridge",
24
25
    DB_FILE = "./bridgedist.db",
    DB_LOG_FILE = "./bridgedist.log",
26

27
28
    N_IP_CLUSTERS = 4,
    MASTER_KEY_FILE = "./secret_key",
29
30

    HTTPS_DIST = True,
31
    HTTPS_SHARE=10,
32
    HTTPS_BIND_IP=None,
33
34
    HTTPS_PORT=6789,
    HTTPS_CERT_FILE="cert",
35
    HTTPS_KEY_FILE="privkey.pem",
36
    HTTP_UNENCRYPTED_BIND_IP=None,
37
38
39
40
    HTTP_UNENCRYPTED_PORT=6788,
    HTTPS_N_BRIDGES_PER_ANSWER=2,

    EMAIL_DIST = True,
41
    EMAIL_SHARE=10,
42
    EMAIL_DOMAINS = [ "gmail.com", "yahoo.com", "catbus.wangafu.net" ],
43
44
    EMAIL_DOMAIN_MAP = { "mail.google.com" : "gmail.com",
                         "googlemail.com" : "gmail.com", },
45
    EMAIL_RESTRICT_IPS=[],
46
    EMAIL_BIND_IP=None,
47
    EMAIL_PORT=6725,
48
    EMAIL_N_BRIDGES_PER_ANSWER=2,
49

50
    RESERVED_SHARE=2,
51
52
53
  )

def getKey(fname):
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
    """Load the key stored in fname, or create a new 32-byte key and store
       it in fname.

    >>> name = os.tmpnam()
    >>> os.path.exists(name)
    False
    >>> k1 = getKey(name)
    >>> os.path.exists(name)
    True
    >>> open(name).read() == k1
    True
    >>> k2 = getKey(name)
    >>> k1 == k2
    True
    """
69
    try:
70
        f = open(fname, 'rb')
71
72
    except IOError:
        k = os.urandom(32)
73
        flags = os.O_WRONLY|os.O_TRUNC|os.O_CREAT|getattr(os, "O_BIN", 0)
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
        fd = os.open(fname, flags, 0400)
        os.write(fd, k)
        os.close(fd)
    else:
        k = f.read()
        f.close()

    return k

def load(cfg, splitter):
    for fname in cfg.BRIDGE_FILES:
        f = open(fname, 'r')
        for bridge in Bridges.parseDescFile(f, cfg.BRIDGE_PURPOSE):
            splitter.insert(bridge)
        f.close()

def startup(cfg):
91
92
93
94
95
96
97
98
99
100
101
    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"):
        v = getattr(cfg, key)
        if v:
            setattr(cfg, key, os.path.expanduser(v))

    if cfg.RUN_IN_DIR:
        os.chdir(cfg.RUN_IN_DIR)

    key = getKey(cfg.MASTER_KEY_FILE)
102
    dblogfile = None
103
    emailDistributor = ipDistributor = None
104

105
    baseStore = store = anydbm.open(cfg.DB_FILE, "c", 0600)
106
    if cfg.DB_LOG_FILE:
107
        dblogfile = open(cfg.DB_LOG_FILE, "a+", 0)
108
        store = Bridges.LogDB("db", store, dblogfile)
109
110
111
112
113

    splitter = Bridges.BridgeSplitter(Bridges.get_hmac(key, "Splitter-Key"),
                                      Bridges.PrefixStore(store, "sp|"))

    if cfg.HTTPS_DIST and cfg.HTTPS_SHARE:
114
115
116
117
        ipDistributor = Dist.IPBasedDistributor(
            Dist.uniformMap,
            cfg.N_IP_CLUSTERS,
            Bridges.get_hmac(key, "HTTPS-IP-Dist-Key"))
118
        splitter.addRing(ipDistributor, "https", cfg.HTTPS_SHARE)
119
        webSchedule = Time.IntervalSchedule("day", 2)
120
121

    if cfg.EMAIL_DIST and cfg.EMAIL_SHARE:
122
123
        for d in cfg.EMAIL_DOMAINS:
            cfg.EMAIL_DOMAIN_MAP[d] = d
124
        emailDistributor = Dist.EmailBasedDistributor(
125
            Bridges.get_hmac(key, "Email-Dist-Key"),
126
127
            Bridges.PrefixStore(store, "em|"),
            cfg.EMAIL_DOMAIN_MAP.copy())
128
        splitter.addRing(emailDistributor, "email", cfg.EMAIL_SHARE)
129
        emailSchedule = Time.IntervalSchedule("day", 1)
130
131
132
133
134
135

    if cfg.RESERVED_SHARE:
        splitter.addRing(Bridges.UnallocatedHolder(),
                         "unallocated",
                         cfg.RESERVED_SHARE)

136
137
    stats = Bridges.BridgeTracker(Bridges.PrefixStore(store, "fs|"),
                                  Bridges.PrefixStore(store, "ls|"))
138
139
    splitter.addTracker(stats)

140
    print "Loading bridges"
141
    load(cfg, splitter)
142
143
144
145
146
147
    print "%d bridges loaded" % len(splitter)
    if emailDistributor:
        print "%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)
148

149
150
151
152
153
154
155
    if cfg.HTTPS_DIST and cfg.HTTPS_SHARE:
        Server.addWebServer(cfg, ipDistributor, webSchedule)

    if cfg.EMAIL_DIST and cfg.EMAIL_SHARE:
        Server.addSMTPServer(cfg, emailDistributor, emailSchedule)

    try:
156
157
        print "Starting reactors."
        Server.runServers()
158
159
160
161
    finally:
        baseStore.close()
        if dblogfile is not None:
            dblogfile.close()
162

163
164
165
166
167
168
169
170
171
172
173
if __name__ == '__main__':
    if len(sys.argv) != 2:
        print "Syntax: %s [config file]" % sys.argv[0]
        sys.exit(1)
    if sys.argv[1] == "TESTING":
        configuration = CONFIG
    else:
        configuration = {}
        execfile(sys.argv[1], configuration)

    startup(CONFIG)