doc: give examples of command to create key/dh files
[re6stnet.git] / re6st / cli / registry.py
1 #!/usr/bin/python
2 import httplib, logging, os, socket, sys
3 from BaseHTTPServer import BaseHTTPRequestHandler
4 from SocketServer import ThreadingTCPServer
5 from urlparse import parse_qsl
6 if 're6st' not in sys.modules:
7 sys.path[0] = os.path.dirname(os.path.dirname(sys.path[0]))
8 from re6st import ctl, registry, utils, version
9
10 # To generate server ca and key with serial for 2001:db8:42::/48
11 # openssl req -nodes -new -x509 -key ca.key -set_serial 0x120010db80042 -days 3650 -out ca.crt
12
13 IPV6_V6ONLY = 26
14 SOL_IPV6 = 41
15
16
17 class RequestHandler(BaseHTTPRequestHandler):
18
19 if __import__("sys").version_info < (2, 7, 4):
20 def address_string(self):
21 # Workaround for http://bugs.python.org/issue6085
22 return self.client_address[0]
23
24 def do_GET(self):
25 try:
26 try:
27 path, query = self.path.split('?', 1)
28 except ValueError:
29 path = self.path
30 query = {}
31 else:
32 query = dict(parse_qsl(query, keep_blank_values=1,
33 strict_parsing=1))
34 _, path = path.split('/')
35 if not _:
36 return self.server.handle_request(self, path, query)
37 except Exception:
38 logging.info(self.requestline, exc_info=1)
39 self.send_error(httplib.BAD_REQUEST)
40
41 def log_error(*args):
42 pass
43
44
45 class HTTPServer4(ThreadingTCPServer):
46
47 allow_reuse_address = True
48 daemon_threads = True
49
50
51 class HTTPServer6(HTTPServer4):
52
53 address_family = socket.AF_INET6
54
55 def server_bind(self):
56 self.socket.setsockopt(SOL_IPV6, IPV6_V6ONLY, 1)
57 HTTPServer4.server_bind(self)
58
59
60 def main():
61 parser = utils.ArgParser(fromfile_prefix_chars='@',
62 description="re6stnet registry used to bootstrap nodes"
63 " and deliver certificates.")
64 _ = parser.add_argument
65 _('--port', type=int, default=80,
66 help="Port on which the server will listen.")
67 _('-4', dest='bind4', default='0.0.0.0',
68 help="Bind server to this IPv4.")
69 _('-6', dest='bind6', default='::',
70 help="Bind server to this IPv6.")
71 _('--db', default='/var/lib/re6stnet/registry.db',
72 help="Path to SQLite database file. It is automatically initialized"
73 " if the file does not exist.")
74 _('--dh',
75 help="File containing Diffie-Hellman parameters in .pem format."
76 " To generate them, you can use something like:\n"
77 "openssl dhparam -out dh2048.pem 2048")
78 _('--ca', required=True, help=parser._ca_help)
79 _('--key', required=True,
80 help="CA private key in .pem format. For example:\nopenssl"
81 " genpkey -out ca.key -algorithm rsa -pkeyopt rsa_keygen_bits:2048")
82 _('--mailhost', required=True,
83 help="SMTP host to send confirmation emails. For debugging"
84 " purpose, it can also be an absolute or existing path to"
85 " a mailbox file")
86 _('--prefix-length', default=16, type=int,
87 help="Default length of allocated prefixes.")
88 _('--anonymous-prefix-length', type=int,
89 help="Length of allocated anonymous prefixes."
90 " If 0 or unset, registration by email is required")
91 _('--ipv4', nargs=2, metavar=("IP/N", "PLEN"),
92 help="Enable ipv4. Each node is assigned a subnet of length PLEN"
93 " inside network IP/N.")
94 _('-l', '--logfile', default='/var/log/re6stnet/registry.log',
95 help="Path to logging file.")
96 _('-r', '--run', default='/var/run/re6stnet',
97 help="Path to re6stnet runtime directory:\n"
98 "- babeld.sock (option -R of babeld)\n")
99 _('-v', '--verbose', default=1, type=int,
100 help="Log level. 0 disables logging. 1=WARNING, 2=INFO,"
101 " 3=DEBUG, 4=TRACE. Use SIGUSR1 to reopen log.")
102 _('--min-protocol', default=version.min_protocol, type=int,
103 help="Reject nodes that are too old. Current is %s." % version.protocol)
104
105 _ = parser.add_argument_group('routing').add_argument
106 _('--hello', type=int, default=15,
107 help="Hello interval in seconds, for both wired and wireless"
108 " connections. OpenVPN ping-exit option is set to 4 times the"
109 " hello interval. It takes between 3 and 4 times the"
110 " hello interval for Babel to re-establish connection with a"
111 " node for which the direct connection has been cut.")
112
113 _ = parser.add_argument_group('tunnelling').add_argument
114 _('--encrypt', action='store_true',
115 help='Specify that tunnels should be encrypted.')
116 _('--client-count', default=10, type=int,
117 help="Number of client tunnels to set up.")
118 _('--max-clients', type=int,
119 help="Maximum number of accepted clients per OpenVPN server. (default:"
120 " client-count * 2, which actually represents the average number"
121 " of tunnels to other peers)")
122 _('--tunnel-refresh', default=300, type=int,
123 help="Interval in seconds between two tunnel refresh: the worst"
124 " tunnel is closed if the number of client tunnels has reached"
125 " its maximum number (client-count).")
126
127 config = parser.parse_args()
128
129 if not version.min_protocol <= config.min_protocol <= version.protocol:
130 parser.error("--min-protocol: value must between %s and %s (included)"
131 % (version.min_protocol, version.protocol))
132
133 if config.ipv4:
134 ipv4, plen = config.ipv4
135 try:
136 ip, n = ipv4.split('/')
137 config.ipv4 = "%s/%s" % (socket.inet_ntoa(socket.inet_aton(ip)),
138 int(n)), int(plen)
139 except (socket.error, ValueError):
140 parser.error("invalid argument --ipv4")
141
142 utils.setupLog(config.verbose, config.logfile)
143
144 if config.max_clients is None:
145 config.max_clients = config.client_count * 2
146
147 server = registry.RegistryServer(config)
148 def requestHandler(request, client_address, _):
149 RequestHandler(request, client_address, server)
150
151 server_dict = {}
152 if config.bind4:
153 r = HTTPServer4((config.bind4, config.port), requestHandler)
154 server_dict[r.fileno()] = r._handle_request_noblock
155 if config.bind6:
156 r = HTTPServer6((config.bind6, config.port), requestHandler)
157 server_dict[r.fileno()] = r._handle_request_noblock
158 if server_dict:
159 while True:
160 args = server_dict.copy(), {}, []
161 server.select(*args)
162 utils.select(*args)
163
164
165 if __name__ == "__main__":
166 main()