Blocking DBus call from python API - python

I am programming a BLE device and therefore need to get some information from the org.freedesktop.DBus.Properties interface, but can't get it to work from dbus python API. From the console this is no problem. For example, from dbus-send I can invoke following method call successfully (with correct mac address of course):
$ dbus-send --system --dest=org.bluez --print-reply "/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX" org.freedesktop.DBus.Properties.Get string:'org.bluez.Device1' string:'Paired'
>> method return time=1645780543.222377 sender=:1.7 -> destination=:1.329 serial=1113 reply_serial=2
variant boolean true
Now, what I'm trying to do is actually something like this:
import dbus
bus = dbus.SystemBus()
connected = bus.call_blocking(
'org.bluez', #bus_name
'/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX', #object_path
'org.freedesktop.DBus.Properties', #dbus_interface
'Get', #method
signature='(ss)', #signature
args=['org.bluez.Device1', 'Connected'], #args
)
print(connected)
which gives me the error: ERROR:dbus.connection:Unable to set arguments ['org.bluez.Device1', 'Paired'] according to signature '(ss)': <class 'TypeError'>: Fewer items found in struct's D-Bus signature than in Python arguments
I tried also with no signature with no success. And I also found a similar question here, but for C-API. So I tried to adapt it to the python dbus API, but still can't get it to work. Moreover, the official documentation isn't very helpful as well, as there is no clear statement on how the argument mechanism works here or a reference to such an explanation. This is pretty annoying, since I can invoke a blocking call for instance on the GetManagedObjects method from org.freedesktop.DBus.ObjectManager interface that way, but that one takes no arguments of course...
Any help appreciated.

There are more Pythonic libraries for D-Bus such as pydbus
device_path = "/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX"
bus = pydbus.SystemBus()
device = bus.get('org.bluez', device_path)
print(device.Connected)
If you did want to do it with the deprecated python-dbus library then I have always done it this way:
BLUEZ_SERVICE_NAME = 'org.bluez'
DEVICE_INTERFACE = 'org.bluez.Device1'
remote_device_path = device_path
remote_device_obj = self.bus.get_object(BLUEZ_SERVICE_NAME,
remote_device_path)
remote_device_props = dbus.Interface(remote_device_obj,
dbus.PROPERTIES_IFACE)
print(remote_device_props.Get(DEVICE_INTERFACE, 'Connected'))
If you want to do it with PyGObject library then an example of that is:
from gi.repository import Gio, GLib
bus_type = Gio.BusType.SYSTEM
bus_name = 'org.bluez'
object_path = '/org/bluez/hci0'
prop_iface = 'org.freedesktop.DBus.Properties'
adapter_iface = 'org.bluez.Adapter1'
adapter_props_proxy = Gio.DBusProxy.new_for_bus_sync(
bus_type=bus_type,
flags=Gio.DBusProxyFlags.NONE,
info=None,
name=bus_name,
object_path=object_path,
interface_name=prop_iface,
cancellable=None)
all_props = adapter_props_proxy.GetAll('(s)', adapter_iface)
print(all_props)
powered = adapter_props_proxy.Get('(ss)', adapter_iface, 'Powered')
print(powered)

Just for completeness and if someone stumble upon this:
You can get the deprecated API call to work if you change the signature to just ss instead of (ss) for some reason. This seems not to be consistent with other dbus APIs, which must have the signature as tuple.
connected = bus.call_blocking(
'org.bluez', #bus_name
'/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX', #object_path
'org.freedesktop.DBus.Properties', #dbus_interface
'Get', #method
signature='ss', #signature
args=['org.bluez.Device1', 'Connected'], #args
)

Related

Python D-Bus: Subscribe to signal and read property with dasbus

How to monitor and read Ubuntu's "Night Light" status via D-Bus using Python with dasbus? I can't figure out the API docs on how to read a property or subscribe to a signal.
Likely candidates:
dasbus.client.property.get()
GLibClient.subscribe()
The following is adapted from the basic examples and prints the interfaces and properties/signals of the object:
#!/usr/bin/env python3
from dasbus.connection import SessionMessageBus
bus = SessionMessageBus()
# dasbus.client.proxy.ObjectProxy
proxy = bus.get_proxy(
"org.gnome.SettingsDaemon.Color", # bus name
"/org/gnome/SettingsDaemon/Color", # object path
)
print(proxy.Introspect())
# read and print properties "NightLightActive" and "Temperature" from interface "org.gnome.SettingsDaemon.Color" in (callback) function
# subscribe to signal "PropertiesChanged" in interface "org.freedesktop.DBus.Properties" / register callback function
Resources
https://pypi.org/project/dbus-python/
What is recommended to use pydbus or dbus-python and what are the differences?
https://wiki.python.org/moin/DbusExamples
Migration from dbus to GDbus in Python 3
Looking at the dasbus examples and the Introspection data it looks like to get the property the dasbus is pythonic so proxy.<property name> works. For your example of NightLightActive it would be:
print("Night light active?", proxy.NightLightActive)
For the signal you need to connect to the signal on the proxy so that seems to take the form of proxy.<signal name>.connect so for example:
proxy.PropertiesChanged.connect(callback)
And this will need to have an EventLoop running.
My entire test was:
from dasbus.connection import SessionMessageBus
from dasbus.loop import EventLoop
bus = SessionMessageBus()
loop = EventLoop()
# dasbus.client.proxy.ObjectProxy
proxy = bus.get_proxy(
"org.gnome.SettingsDaemon.Color", # bus name
"/org/gnome/SettingsDaemon/Color", # object path
)
print("Night light active?", proxy.NightLightActive)
print("Temperature is set to:", proxy.Temperature)
def callback(iface, prop_changed, prop_invalidated):
print("The notification:",
iface, prop_changed, prop_invalidated)
proxy.PropertiesChanged.connect(callback)
loop.run()

Configure SOCK5 proxy in Libtorrent session (Using Python)

I have been trying to configure proxy for a libtorent session; went through the documentation yet could not figure out a solution.
I tried the approach mentioned here. Did not work for me. This is the error I got-
r = lt.proxy_settings()
AttributeError: module 'libtorrent' has no attribute 'proxy_settings'. Did you mean: 'pe_settings'?
I tried to look for pe_settings() is the documentation but could not find anything like that. Being hopeful, I decided to do as the error message suggests, hoping it would lead me to some other error(from where i can pick up). Hence, I changed lt.proxy_settings() to lt.pe_settings(). The error that I get now is-
ses.set_dht_proxy(r)
Boost.Python.ArgumentError: Python argument types in
session.set_dht_proxy(session, pe_settings)
did not match C++ signature:
set_dht_proxy(libtorrent::session {lvalue}, libtorrent::aux::proxy_settings)
Mismatch in argument while trying to call the setters.
I also tried to use settings_pack. That did not work as well.
Here is what the session configuration looks like-
ses = lt.session()
ses.listen_on(6881, 6891)
r = lt.pe_settings()
r.proxy_hostnames = True
r.proxy_peer_connections = True
r.hostname = self.proxy_ip
r.username = ""
r.password = ""
r.port = self.proxy_port
r.type = lt.proxy_type_t().socks5_pw
#print lt.proxy_type().socks5_pw
#ses.set_dht_proxy(r)
#ses.set_peer_proxy(r)
#ses.set_tracker_proxy(r)
#ses.set_web_seed_proxy(r)
ses.set_proxy(r)
t = ses.settings()
t.force_proxy = True
t.proxy_hostnames = True
t.proxy_peer_connections = True
#t.proxy_tracker_connections = True
t.anonymous_mode = True
ses.set_settings(t)
print (ses.get_settings())
#ses.dht_proxy()
ses.peer_proxy()
#ses.tracker_proxy()
ses.web_seed_proxy()
ses.proxy()
ses.set_settings(t)
Any suggestions/ comments/ insights will be highly appreciated! Thanks!
Versions and platforms-
1. python 3.9
2. libtorrent 2.0.7 (installed it using vcpkg dependency manager)
3. mac os- Monterey
Vcpkg dependency manager installation steps
settings_pack documentation
you configure the proxy via the same settings_pack all other session-wide settings are configured via. Specifically you want to set:
proxy_hostname
proxy_port
proxy_type
Maybe proxy_username and proxy_password (docs)

Twisted Python, using ssl.CertificateOptions when switching from plain text to secure connection

Following advice from Jean-Paul Calderone here on SO, I'm trying to modify the twisted "starttls_server" sample below to support the use of ssl.ClientCertificateOptions, to allow me to specify my private key, certificate, and trusted roots, as per http://twistedmatrix.com/documents/14.0.0/api/twisted.internet.ssl.CertificateOptions.html
from twisted.internet import ssl, protocol, defer, task, endpoints
from twisted.protocols.basic import LineReceiver
from twisted.python.modules import getModule
class TLSServer(LineReceiver):
def lineReceived(self, line):
print("received: " + line)
if line == "STARTTLS":
print("-- Switching to TLS")
self.sendLine('READY')
self.transport.startTLS(self.factory.options)
def main(reactor):
certData = getModule(__name__).filePath.sibling('server.pem').getContent()
cert = ssl.PrivateCertificate.loadPEM(certData)
factory = protocol.Factory.forProtocol(TLSServer)
factory.options = cert.options()
endpoint = endpoints.TCP4ServerEndpoint(reactor, 8000)
endpoint.listen(factory)
return defer.Deferred()
if __name__ == '__main__':
import starttls_server
task.react(starttls_server.main)
My understanding is that I effectively need to replace the cert = ssl.PrivateCertificate... and cert.options = ssl.PrivateCertificate.... lines with something like certopts = ssl.CertificateOptions(privateKey=pKeyData, certificate=certData, trustRoot=caCertsData) (having read the appropriate files in to certData, caCertsData, and pKeyData) and then pass this in to factory.options - but without pasting every variant of code I've tried, I've yet to work this out correctly - my efforts have produced varying results from the classic "OpenSSL.crypto.Error: []" - through to seemingly just dumping the contents of my 3 PEM files to screen and exiting!
Can anyone enlighten me? Thank you :)
cert.options() is already returning a CertificateOptions. The problem is that options takes authorities (as Certificate objects) as positional args, and doesn't let you pass through all the other configuration values through, so you probably want to construct a CertificateOptions directly.
Just change the factory.options = cert.options() line to factory.options = ssl.CertificateOptions(...).
However, CertificateOptions takes a pyOpenSSL PKey object as its privateKey, not the key data. So you'll need to use OpenSSL APIs to load that key, or you can extract it from a PrivateCertificate.
If you read the signature of CertificateOptions very carefully, the required types should be fairly clear. You may also need to consult the pyOpenSSL documentation as well.

how to enable connectiondraining with python boto modify_lb_attribute

I have been trying to enable ELB connection draining using the modify_lb_attribute method in the python boto module; however I haven't been able to get it working. According to the documentation here http://boto.readthedocs.org/en/latest/ref/elb.html I should be able to call it like his:
modify_lb_attribute(load_balancer_name, attribute, value)
Here is an example:
modify_lb_attribute('my-elb', 'connectionDraining', 120)
When I do this however I receive the following error:
File "/Library/Python/2.7/site-packages/boto/ec2/elb/init.py", line 421, in modify_lb_attribute
value.enabled and 'true' or 'false'
AttributeError: 'NoneType' object has no attribute 'enabled'
I have been able to get it to work successfully with crossZoneLoadBalancing.
For example this works:
modify_lb_attribute('my-elb', 'crossZoneLoadBalancing', 'true')
Any help or suggestions would be appreciated.
Thanks
Working syntax for instantiating a ConnectionDrainingAttribute and passing it a to a load balancer:
from boto.ec2.elb.attributes import ConnectionDrainingAttribute
import boto.ec2.elb
connection = boto.ec2.elb.connect_to_region("region")
cda = ConnectionDrainingAttribute(connection)
cda.enabled = True
cda.timeout = 120
connection.modify_lb_attribute(
load_balancer_name='my-elb',
attribute='connectionDraining',
value=cda
)
More information about the ConnectionDrainingAttribute class can be found here in the boto docs.
When you modify the connectionDraining attribute of a load balancer, there are actually two values you can supply. The first is a boolean indicating whether you are enabling or disabling the connection draining feature. The second is an integer indicating the timeout which obviously applies only if connection draining is being enabled.
To allow you to specify both of these values, boto defines a ConnectionDrainingAttribute class in boto.ec2.elb.attributes. You must pass an instance of this class as the value to modify_elb_attribute, e.g.:
from boto.ec2.elb.attributes import ConnectionDrainingAttribute
cda = ConnectionDrainingAttribute()
cda.enabled = True
cda.timeout = 120
...
modify_lb_attribute('my-elb', cda)

Need help using M2Crypto.Engine to access USB Token

I am using M2Crypto-0.20.2. I want to use engine_pkcs11 from the OpenSC project and the Aladdin PKI client for token based authentication making xmlrpc calls over ssl.
from M2Crypto import Engine
Engine.load_dynamic()
dynamic = Engine.Engine('dynamic')
# Load the engine_pkcs from the OpenSC project
dynamic.ctrl_cmd_string("SO_PATH", "/usr/local/ssl/lib/engines/engine_pkcs11.so")
Engine.cleanup()
Engine.load_dynamic()
# Load the Aladdin PKI Client
aladdin = Engine.Engine('dynamic')
aladdin.ctrl_cmd_string("SO_PATH", "/usr/lib/libeTPkcs11.so")
key = aladdin.load_private_key("PIN","password")
This is the error I receive:
key = pkcs.load_private_key("PIN","eT0ken")
File "/usr/local/lib/python2.4/site-packages/M2Crypto/Engine.py", line 70, in load_private_key
return self._engine_load_key(m2.engine_load_private_key, name, pin)
File "/usr/local/lib/python2.4/site-packages/M2Crypto/Engine.py", line 60, in _engine_load_key
raise EngineError(Err.get_error())
M2Crypto.Engine.EngineError: 23730:error:26096075:engine routines:ENGINE_load_private_key:not initialised:eng_pkey.c:112:
For load_private_key(), what should be passed as the first argument? The M2Crypto documentation does not explain it.
I don't get any errors loading the engines, but I'm not sure if I'm loading them correctly. It seems like the engine ID has to be a specific name but I don't find that list anywhere. 'dynamic' is working for me.
Any help would be appreciated!
Found !!!!
Yes, exactly the way where I came from.
So, actually the ENGINE_init() is not implemented in M2Crypto.Engine. So, only one solution: patching!!! (very small...) so I've created a new Engine method (in Engine.py)
def engine_initz(self):
"""Return engine name"""
return m2.engine_initz(self._ptr)
Why engine_initz ? because engine_init is already define in SWIG/_engine.i,:
void engine_init(PyObject *engine_err) {
Py_INCREF(engine_err);
_engine_err = engine_err;
}
I don't really know what is done, so I've prefered creating a new one... So I've just added the following to SWIG/_engine.i:
%rename(engine_initz) ENGINE_init;
extern int ENGINE_init(ENGINE *);
And recompile the __m2crypto.so, now just add a "pkcs11.engine_initz()" before launching the private key, and it works.....
I don't know what and why the engine_init code present in current M2Crypto is supposed to do. Exposing ENGINE_init() as engine_init2 with the following patch to M2Crypto helps:
Index: SWIG/_engine.i
===================================================================
--- SWIG/_engine.i (revision 719)
+++ SWIG/_engine.i (working copy)
## -44,6 +44,9 ##
%rename(engine_free) ENGINE_free;
extern int ENGINE_free(ENGINE *);
+%rename(engine_init2) ENGINE_init;
+extern int ENGINE_init(ENGINE *);
+
/*
* Engine id/name functions
*/
After this, the following code takes me further (but urllib does not fully work for me currently):
import sys, os, time, cgi, urllib, urlparse
from M2Crypto import m2urllib2 as urllib2
from M2Crypto import m2, SSL, Engine
# load dynamic engine
e = Engine.load_dynamic_engine("pkcs11", "/Users/martin/prefix/lib/engines/engine_pkcs11.so")
pk = Engine.Engine("pkcs11")
pk.ctrl_cmd_string("MODULE_PATH", "/Library/OpenSC/lib/opensc-pkcs11.so")
m2.engine_init2(m2.engine_by_id("pkcs11")) # This makes the trick
cert = e.load_certificate("slot_01-id_01")
key = e.load_private_key("slot_01-id_01", sys.argv[1])
ctx = SSL.Context("sslv23")
ctx.set_cipher_list("HIGH:!aNULL:!eNULL:#STRENGTH")
ctx.set_session_id_ctx("foobar")
m2.ssl_ctx_use_x509(ctx.ctx, cert.x509)
m2.ssl_ctx_use_pkey_privkey(ctx.ctx, key.pkey)
opener = urllib2.build_opener(ctx)
urllib2.install_opener(opener)
Looking at the pastebin link Becky provided, I believe it translates to something like this in the new API:
from M2Crypto import Engine, m2
dynamic = Engine.load_dynamic_engine("pkcs11", "/Users/martin/prefix/lib/engines/engine_pkcs11.so")
pkcs11 = Engine.Engine("pkcs11")
pkcs11.ctrl_cmd_string("MODULE_PATH", "/Library/OpenSC/lib/opensc-pkcs11.so")
r = pkcs11.ctrl_cmd_string("PIN", sys.argv[1])
key = pkcs11.load_private_key("id_01")
So I am betting that if you substitute "/Users/martin/prefix/lib/engines/engine_pkcs11.so" with "/usr/local/ssl/lib/engines/engine_pkcs11.so" and "/Library/OpenSC/lib/opensc-pkcs11.so" with "/usr/lib/libeTPkcs11.so" you might get it to work with Aladdin.
That is exactly the code I've tried. But It ended with the following error:
Traceback (most recent call last):
File "prog9.py", line 13, in <module>
key = pkcs11.load_private_key("id_45")
File "/usr/lib/pymodules/python2.5/M2Crypto/Engine.py", line 70, in load_private_key
return self._engine_load_key(m2.engine_load_private_key, name, pin)
File "/usr/lib/pymodules/python2.5/M2Crypto/Engine.py", line 60, in _engine_load_key
raise EngineError(Err.get_error())
M2Crypto.Engine.EngineError: 11814:error:26096075:engine outines:ENGINE_load_private_key:not initialised:eng_pkey.c:112:
I'm using OpenSC PKCS11 lib, not aladdin lib. But I don't think the problem is closed.
I tried the code that Heikki suggested (minus one line) and got the same error as Erlo. For load_private_key(), how do I know what to put in for the argument?
dynamic = Engine.load_dynamic_engine("pkcs11", "/usr/local/ssl/lib/engines/engine_pkcs11.so")
# m2.engine_free(dynamic) this line gave me an error TypeError: in method 'engine_free', argument 1 of type 'ENGINE *'
pkcs11 = Engine.Engine("pkcs11")
pkcs11.ctrl_cmd_string("MODULE_PATH", "/usr/lib/libeTPkcs11.so")
r = pkcs11.ctrl_cmd_string("PIN", "password")
key = pkcs11.load_private_key("id_01")
I think the problem is not really the "load_private_key()". It's like something is missing between "MODULE_PATH" definition and the load_private_key() call. What happen if you remplace "/usr/lib/libeTPkcs11.so" by a wrong path ? In my case I have no error related to this.
I've run "pcscd" in foreground with high debug level, there is no call to smartcard during the python execution... So definitly, I don't understand what's wrong...
The equivalent in "openssl" is using "-pre" command. The "-pre" (by opposite to the "-post") are command sent to the engine before loading. Perhaps we need to call a methode which "load" the engine after all "ctrl_cmd_string" calls ?? ...
Lost :-/

Categories