Ejemplo n.º 1
0
 def test_get_unique_name(self):
     # Ensure that get_unique_name() works
     client = DbusClient()
     client.connect('session')
     unique_name = client.get_unique_name()
     self.assertIsInstance(unique_name, six.text_type)
     self.assertTrue(unique_name.startswith(':'))
     client.close()
Ejemplo n.º 2
0
 def __init__(self):
     """Constructor."""
     super(AvahiLocationSource, self).__init__()
     handler = AvahiHandler(self._avahi_event)
     self.client = DbusClient(handler)
     self.client.connect('system')
     self.logger = logging.getLogger(__name__)
     self.callbacks = []
     self.neighbors = {}
     self.addresses = {}
     self._browser = None
     self._entry_groups = {}
Ejemplo n.º 3
0
 def test_auth_tcp(self):
     # Test that authentication works over TCP
     server = DbusServer(echo_app)
     addr = 'tcp:host=127.0.0.1,port=0'
     server.listen(addr)
     client = DbusClient()
     client.connect(server.addresses[0])
     cproto = client.connection[1]
     cauth = cproto._authenticator
     sproto = list(server.connections)[0][1]
     sauth = sproto._authenticator
     self.assertTrue(cauth.authenticationSucceeded())
     self.assertTrue(sauth.authenticationSucceeded())
     self.assertIsInstance(cproto.server_guid, six.text_type)
     self.assertTrue(cproto.server_guid.isalnum())
     self.assertEqual(cproto.server_guid, cauth.getGUID())
     self.assertEqual(cproto.server_guid, sproto.server_guid)
     self.assertEqual(sproto.server_guid, sauth.getGUID())
     self.assertEqual(cauth.getMechanismName(), sauth.getMechanismName())
     if hasattr(os, 'fork'):
         self.assertEqual(cauth.getMechanismName(), 'DBUS_COOKIE_SHA1')
     else:
         self.assertEqual(cauth.getMechanismName(), 'ANONYMOUS')
     client.close()
     server.close()
Ejemplo n.º 4
0
 def test_auth_pipe(self):
     # Test that authentication works over a Pipe.
     server = DbusServer(echo_app)
     addr = 'unix:path=' + self.pipename()
     server.listen(addr)
     client = DbusClient()
     client.connect(addr)
     cproto = client.connection[1]
     cauth = cproto._authenticator
     sproto = list(server.connections)[0][1]
     sauth = sproto._authenticator
     self.assertTrue(cauth.authenticationSucceeded())
     self.assertTrue(sauth.authenticationSucceeded())
     self.assertIsInstance(cproto.server_guid, six.text_type)
     self.assertTrue(cproto.server_guid.isalnum())
     self.assertEqual(cproto.server_guid, cauth.getGUID())
     self.assertEqual(cproto.server_guid, sproto.server_guid)
     self.assertEqual(sproto.server_guid, sauth.getGUID())
     self.assertEqual(cauth.getMechanismName(), sauth.getMechanismName())
     if hasattr(socket, 'SO_PEERCRED'):
         self.assertEqual(cauth.getMechanismName(), 'EXTERNAL')
     elif hasattr(os, 'fork'):
         self.assertEqual(cauth.getMechanismName(), 'DBUS_COOKIE_SHA1')
     else:
         self.assertEqual(cauth.getMechanismName(), 'ANONYMOUS')
     client.close()
     server.close()
Ejemplo n.º 5
0
 def test_call_method(self):
     # Ensure that calling a method over a Unix socket works.
     server = DbusServer(echo_app)
     addr = 'unix:path=' + self.pipename()
     server.listen(addr)
     client = DbusClient()
     client.connect(addr)
     result = client.call_method('bus.name', '/path', 'my.iface', 'Echo')
     self.assertEqual(result, ())
     server.close()
     client.close()
Ejemplo n.º 6
0
 def test_call_method_tcp(self):
     # Ensure that calling a method over TCP works.
     server = DbusServer(echo_app)
     addr = 'tcp:host=127.0.0.1,port=0'
     server.listen(addr)
     client = DbusClient()
     client.connect(server.addresses[0])
     result = client.call_method('bus.name', '/path', 'my.iface', 'Echo')
     self.assertEqual(result, ())
     server.close()
     client.close()
Ejemplo n.º 7
0
 def test_call_method_str_args(self):
     # Ensure that calling a method with string arguments works.
     server = DbusServer(echo_app)
     addr = 'unix:path=' + self.pipename()
     server.listen(addr)
     client = DbusClient()
     client.connect(addr)
     result = client.call_method('bus.name', '/path', 'my.iface', 'Echo',
                                 signature='s', args=['foo'])
     self.assertEqual(result, ('foo',))
     result = client.call_method('bus.name', '/path', 'my.iface', 'Echo',
                                 signature='ss', args=['foo', 'bar'])
     self.assertEqual(result, ('foo', 'bar'))
     server.close()
     client.close()
Ejemplo n.º 8
0
 def test_call_method_int_args(self):
     # Ensure that calling a method with integer arguments works.
     server = DbusServer(echo_app)
     addr = 'unix:path=' + self.pipename()
     server.listen(addr)
     client = DbusClient()
     client.connect(addr)
     result = client.call_method('bus.name', '/path', 'my.iface', 'Echo',
                                 signature='i', args=[1])
     self.assertEqual(result, (1,))
     result = client.call_method('bus.name', '/path', 'my.iface', 'Echo',
                                 signature='ii', args=[1, 2])
     self.assertEqual(result, (1, 2))
     server.close()
     client.close()
Ejemplo n.º 9
0
 def perf_message_throughput_tcp(self):
     # Test roundtrips of a simple method call over TCP.
     server = DbusServer(echo_app)
     addr = 'tcp:host=127.0.0.1,port=0'
     server.listen(addr)
     client = DbusClient()
     client.connect(server.addresses[0])
     nmessages = 0
     t0 = t1 = time.time()
     while t1 - t0 < 0.2:
         client.call_method('bus.name', '/path', 'my.iface', 'Echo')
         t1 = time.time()
         nmessages += 1
     throughput = nmessages / (t1 - t0)
     self.add_result(throughput)
     server.close()
     client.close()
Ejemplo n.º 10
0
 def perf_message_throughput_pipe(self):
     # Test roundtrips of a simple method call over a Pipe
     server = DbusServer(echo_app)
     addr = 'unix:path=' + self.pipename()
     server.listen(addr)
     client = DbusClient()
     client.connect(addr)
     nmessages = 0
     t0 = t1 = time.time()
     while t1 - t0 < 0.2:
         client.call_method('bus.name', '/path', 'my.iface', 'Echo')
         t1 = time.time()
         nmessages += 1
     throughput = nmessages / (t1 - t0)
     self.add_result(throughput)
     server.close()
     client.close()
Ejemplo n.º 11
0
 def test_get_unique_name(self):
     # Ensure that get_unique_name() works client and server side
     server = DbusServer(echo_app)
     addr = 'unix:path=' + self.pipename()
     server.listen(addr)
     client = DbusClient()
     client.connect(addr)
     unique_name = client.get_unique_name()
     self.assertIsInstance(unique_name, six.text_type)
     self.assertTrue(unique_name.startswith(':'))
     sproto = list(server.connections)[0][1]
     self.assertEqual(unique_name, sproto.get_unique_name())
     server.close()
     client.close()
Ejemplo n.º 12
0
 def test_call_listnames(self):
     # Call the ListNames() bus method and ensure the results are a list of
     # strings.
     client = DbusClient()
     client.connect('session')
     result = client.call_method('org.freedesktop.DBus', '/org/freedesktop/DBus',
                                 'org.freedesktop.DBus', 'ListNames')
     self.assertIsInstance(result, tuple)
     self.assertEqual(len(result), 1)
     names = result[0]
     self.assertIsInstance(names, list)
     self.assertGreater(len(names), 0)
     for name in names:
         self.assertIsInstance(name, six.text_type)
     client.close()
Ejemplo n.º 13
0
 def perf_message_throughput_tcp(self):
     # Test roundtrips of a simple method call over TCP.
     server = DbusServer(echo_app)
     addr = 'tcp:host=127.0.0.1,port=0'
     server.listen(addr)
     client = DbusClient()
     client.connect(server.addresses[0])
     nmessages = 0
     t0 = t1 = time.time()
     while t1 - t0 < 0.2:
         client.call_method('bus.name', '/path', 'my.iface', 'Echo')
         t1 = time.time()
         nmessages += 1
     throughput = nmessages / (t1 - t0)
     self.add_result(throughput)
     server.close()
     client.close()
Ejemplo n.º 14
0
 def perf_message_throughput_pipe(self):
     # Test roundtrips of a simple method call over a Pipe
     server = DbusServer(echo_app)
     addr = 'unix:path=' + self.pipename()
     server.listen(addr)
     client = DbusClient()
     client.connect(addr)
     nmessages = 0
     t0 = t1 = time.time()
     while t1 - t0 < 0.2:
         client.call_method('bus.name', '/path', 'my.iface', 'Echo')
         t1 = time.time()
         nmessages += 1
     throughput = nmessages / (t1 - t0)
     self.add_result(throughput)
     server.close()
     client.close()
Ejemplo n.º 15
0
 def test_call_method_error(self):
     # Ensure that a method can return an error and that in this case a
     # DbusMethodCallError is raised.
     server = DbusServer(echo_app)
     addr = 'unix:path=' + self.pipename()
     server.listen(addr)
     client = DbusClient()
     client.connect(addr)
     exc = self.assertRaises(DbusMethodCallError, client.call_method,
                            'bus.name', '/path', 'my.iface', 'Error')
     self.assertEqual(exc.error, 'Echo.Error')
     self.assertEqual(exc.args, ())
     server.close()
     client.close()
Ejemplo n.º 16
0
 def test_call_method_error_args(self):
     # Call a method that will return an error with arguments. The arguments
     # should be available from the exception.
     server = DbusServer(echo_app)
     addr = 'unix:path=' + self.pipename()
     server.listen(addr)
     client = DbusClient()
     client.connect(addr)
     exc = self.assertRaises(DbusMethodCallError, client.call_method,
                            'bus.name', '/path', 'my.iface', 'Error',
                            signature='ss', args=('foo', 'bar'))
     self.assertEqual(exc.error, 'Echo.Error')
     self.assertEqual(exc.args, ('foo', 'bar'))
     server.close()
     client.close()
Ejemplo n.º 17
0
 def test_send_garbage(self):
     # Send random garbage and ensure the connection gets dropped.
     server = DbusServer(echo_app)
     addr = 'unix:path=' + self.pipename()
     server.listen(addr)
     client = DbusClient()
     client.connect(addr)
     exc = None
     try:
         while True:
             chunk = os.urandom(100)
             client.transport.write(chunk)
             gruvi.sleep(0)
     except Exception as e:
         exc = e
     self.assertIsInstance(exc, TransportError)
     server.close()
     client.close()
Ejemplo n.º 18
0
 def test_connection_limit(self):
     # Establish more connections than the DBUS server is willing to accept.
     # The connections should be closed.
     server = DbusServer(echo_app)
     addr = 'unix:path=' + self.pipename()
     server.listen(addr)
     server.max_connections = 10
     clients = []
     exc = None
     try:
         for i in range(15):
             client = DbusClient()
             client.connect(addr)
             clients.append(client)
     except Exception as e:
         exc = e
     self.assertIsInstance(exc, TransportError)
     self.assertLessEqual(len(server.connections), server.max_connections)
     for client in clients:
         client.close()
     server.close()
Ejemplo n.º 19
0
class AvahiLocationSource(ZeroconfLocationSource):
    """Avahi Zeroconf location source.
    
    This location source provides loation services for a local network
    using DNS-SD. This source is for freedesktop like platforms and uses
    Avahi via its D-BUS interface.

    DNS-SD is used in the following way for vault discovery:

    1. The Bluepass service is registered as a PTR record under:

        _bluepass._tcp.local

    2. The previous PTR record will resolve to a list of SRV and TXT records.
       Instead of using the vault UUID as the service name, this uses the
       vault's node UUID because a vault can be replicated over many Bluepass
       instances and is therefore not unique.

        <node_uuid>._bluepass._tcp.local

       The TXT records specify a set of properteis, with at least a
       "vault" property containing the UUID of the vault. A "visible" property
       may also be set, indicating whether this node currently accepts pairing
       requests.
    """

    name = 'avahi-zeroconf'

    def __init__(self):
        """Constructor."""
        super(AvahiLocationSource, self).__init__()
        handler = AvahiHandler(self._avahi_event)
        self.client = DbusClient(handler)
        self.client.connect('system')
        self.logger = logging.getLogger(__name__)
        self.callbacks = []
        self.neighbors = {}
        self.addresses = {}
        self._browser = None
        self._entry_groups = {}

    def _call_avahi(self, path, method, interface, signature=None, args=None):
        """INTERNAL: call into Avahi."""
        try:
            reply = self.client.call_method(DBUS_NAME, path, interface, method,
                                            signature=signature, args=args)
        except DbusError as e:
            msg = 'Encounted a D-BUS error for method %s: %s'
            self.logger.error(msg, method, str(e))
            raise LocationError(msg % (method, str(e)))
        return reply[0] if len(reply) == 1 else reply

    def _run_callbacks(self, event, *args):
        """Run all registered callbacks."""
        for callback in self.callbacks:
            callback(event, *args)

    def _proto_to_family(self, proto):
        """Convert an Avahi protocol ID to an address family."""
        if proto == PROTO_INET:
            family = socket.AF_INET
        elif proto == PROTO_INET6:
            family = socket.AF_INET6
        else:
            family = -1
        return family

    def _avahi_event(self, event, *args):
        """Single unified callback for AvahiHandler."""
        logger = self.logger
        if event == 'Found':
            node = args[2]
            neighbor = { 'node': node, 'source': 'LAN' }
            txt = decode_txt(args[9])
            properties = neighbor['properties'] = {}
            for name,value in txt.items():
                if name in ('nodename', 'vault', 'vaultname'):
                    neighbor[name] = value
                else:
                    properties[name] = value
            for name in ('nodename', 'vault', 'vaultname'):
                if not neighbor.get(name):
                    logger.error('node %s lacks TXT field "%s"', node, name)
                    return
            event = 'NeighborUpdated' if node in self.neighbors else 'NeighborDiscovered'
            family = self._proto_to_family(args[6])
            if family != socket.AF_INET:
                return
            addr = { 'family': family, 'host': args[5], 'addr': (args[7], args[8]) }
            addr['id'] = '%s:%s:%s' % (family, args[7], args[8])
            # There can be multiple addresses per node for different
            # interfaces and/or address families. We keep track of this
            # so we distinghuish address changes from new addresses that
            # become available.
            key = '%d:%d' % (args[0], args[1])
            if node not in self.addresses:
                self.addresses[node] = {}
            self.addresses[node][key] = addr
            neighbor['addresses'] = list(self.addresses[node].values())
            self.neighbors[node] = neighbor
            self._run_callbacks(event, neighbor)
        elif event == 'ItemRemove':
            node = args[2]
            key = '%d:%d' % (args[0], args[1])
            if node not in self.neighbors or key not in self.addresses[node]:
                logger.error('ItemRemove event for unknown node "%s"', node)
                return
            del self.addresses[node][key]
            neighbor = self.neighbors[node]
            neighbor['addresses'] = list(self.addresses[node].values())
            if not neighbor['addresses']:
                del self.addresses[node]
                del self.neighbors[node]
            event = 'NeighbordUpdated' if node in self.neighbors else 'NeighborDisappeared'
            self._run_callbacks(event, neighbor)

    def isavailable(self):
        """Return wheter Avahi is available or not."""
        try:
            version = self._call_avahi(PATH_SERVER, 'GetVersionString', IFACE_SERVER)
        except LocationError:
            return False
        self.logger.info('Found Avahi version %s', version)
        state = self._call_avahi(PATH_SERVER, 'GetState', IFACE_SERVER)
        if state != SERVER_RUNNING:
            self.logger.error('Avahi not in the RUNNING state (instead: %s)', state)
            return False
        return True

    def add_callback(self, callback):
        """Add a callback for this location source. When the first callback is
        added, we start browsing the zeroconf domain."""
        self.callbacks.append(callback)
        if self._browser is not None:
            return
        args = (IFACE_UNSPEC, PROTO_INET, self.service, self.domain, 0)
        self._browser = self._call_avahi(PATH_SERVER, 'ServiceBrowserNew',
                                 IFACE_SERVER, 'iissu', args)

    def register(self, node, nodename, vault, vaultname, address, properties=None):
        """Register a service instance."""
        group = self._call_avahi(PATH_SERVER, 'EntryGroupNew', IFACE_SERVER)
        host = self._call_avahi(PATH_SERVER, 'GetHostNameFqdn', IFACE_SERVER)
        port = address[1]
        properties = properties.copy() if properties else {}
        properties['nodename'] = nodename
        properties['vault'] = vault
        properties['vaultname'] = vaultname
        args = (IFACE_UNSPEC, PROTO_INET, 0, node, self.service, self.domain,
                host, port, encode_txt(properties))
        self._call_avahi(group, 'AddService', IFACE_ENTRY_GROUP, 'iiussssqaay', args)
        self._call_avahi(group, 'Commit', IFACE_ENTRY_GROUP)
        self._entry_groups[node] = (group, properties)

    def set_property(self, node, name, value):
        """Update a property."""
        if node not in self._entry_groups:
            raise RuntimeError('Node is not registered yet')
        group, properties = self._entry_groups[node]
        properties[name] = value
        args = (IFACE_UNSPEC, PROTO_INET, 0, node, self.service, self.domain,
                encode_txt(properties))
        self._call_avahi(group, 'UpdateServiceTxt', IFACE_ENTRY_GROUP, 'iiusssaay', args)

    def unregister(self, node):
        """Release our registration."""
        if node not in self._entry_groups:
            raise RuntimeError('Node is not registered yet')
        group, properties = self._entry_groups[node]
        self._call_avahi(group, 'Free', IFACE_ENTRY_GROUP)
        del self._entry_groups[node]