1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2015, 2016 LEAP
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 The server for the events mechanism.
27 from abc import ABCMeta
29 # XXX some distros don't package libsodium, so we have to be prepared for
33 from zmq.auth.thread import ThreadAuthenticator
37 from txzmq.connection import ZmqEndpoint, ZmqEndpointType
39 from leap.common.config import flags, get_path_prefix
40 from leap.common.zmq_utils import zmq_has_curve
42 from leap.common.zmq_utils import maybe_create_and_get_certificates
43 from leap.common.zmq_utils import PUBLIC_KEYS_PREFIX
46 logger = logging.getLogger(__name__)
49 ADDRESS_RE = re.compile("^([a-z]+)://([^:]+):?(\d+)?$")
52 class TxZmqComponent(object):
54 A twisted-powered zmq events component.
56 _factory = txzmq.ZmqFactory()
57 _factory.registerForShutdown()
59 __metaclass__ = ABCMeta
61 _component_type = None
63 def __init__(self, path_prefix=None, enable_curve=True):
65 Initialize the txzmq component.
67 if path_prefix is None:
68 path_prefix = get_path_prefix(flags.STANDALONE)
69 self._config_prefix = os.path.join(path_prefix, "leap", "events")
70 self._connections = []
72 self.use_curve = zmq_has_curve()
74 self.use_curve = False
77 def component_type(self):
78 if not self._component_type:
80 "Make sure implementations of TxZmqComponent"
81 "define a self._component_type!")
82 return self._component_type
84 def _zmq_connect(self, connClass, address):
86 Connect to an address.
88 :param connClass: The connection class to be used.
89 :type connClass: txzmq.ZmqConnection
90 :param address: The address to connect to.
93 :return: The binded connection.
94 :rtype: txzmq.ZmqConnection
96 endpoint = ZmqEndpoint(ZmqEndpointType.connect, address)
97 connection = connClass(self._factory)
100 socket = connection.socket
101 public, secret = maybe_create_and_get_certificates(
102 self._config_prefix, self.component_type)
103 server_public_file = os.path.join(
104 self._config_prefix, PUBLIC_KEYS_PREFIX, "server.key")
106 server_public, _ = zmq.auth.load_certificate(server_public_file)
107 socket.curve_publickey = public
108 socket.curve_secretkey = secret
109 socket.curve_serverkey = server_public
111 connection.addEndpoints([endpoint])
114 def _zmq_bind(self, connClass, address):
118 :param connClass: The connection class to be used.
119 :type connClass: txzmq.ZmqConnection
120 :param address: The address to bind to.
123 :return: The binded connection and port.
124 :rtype: (txzmq.ZmqConnection, int)
126 proto, addr, port = ADDRESS_RE.search(address).groups()
128 endpoint = ZmqEndpoint(ZmqEndpointType.bind, address)
129 connection = connClass(self._factory)
132 socket = connection.socket
134 public, secret = maybe_create_and_get_certificates(
135 self._config_prefix, self.component_type)
136 socket.curve_publickey = public
137 socket.curve_secretkey = secret
138 self._start_thread_auth(connection.socket)
140 connection.addEndpoints([endpoint])
141 return connection, port
143 def _start_thread_auth(self, socket):
145 Start the zmq curve thread authenticator.
147 :param socket: The socket in which to configure the authenticator.
148 :type socket: zmq.Socket
150 # TODO re-implement without threads.
151 logger.debug("Starting thread authenticator...")
152 authenticator = ThreadAuthenticator(self._factory.context)
154 # Temporary fix until we understand what the problem is
155 # See https://leap.se/code/issues/7536
158 authenticator.start()
159 # XXX do not hardcode this here.
160 authenticator.allow('127.0.0.1')
161 # tell authenticator to use the certificate in a directory
162 public_keys_dir = os.path.join(self._config_prefix, PUBLIC_KEYS_PREFIX)
163 authenticator.configure_curve(domain="*", location=public_keys_dir)
164 socket.curve_server = True # must come before bind
167 class TxZmqServerComponent(TxZmqComponent):
169 A txZMQ server component.
172 _component_type = "server"
175 class TxZmqClientComponent(TxZmqComponent):
177 A txZMQ client component.
180 _component_type = "client"