[Openvpn-devel] [L] Change in openvpn[master]: Add python scripts that demonstrate using the dpc1 ACC protocol
"plaisthos \(Code Review\) via Openvpn-devel" <[email protected]>
| Newsgroups | net.sourceforge.lists.openvpn-devel |
|---|---|
| Message-ID | <[email protected]> |
plaisthos has uploaded this change for review. ( http://gerrit.openvpn.net/c/openvpn/+/1860?usp=email ) Change subject: Add python scripts that demonstrate using the dpc1 ACC protocol ...................................................................... Add python scripts that demonstrate using the dpc1 ACC protocol The dpc1 protocol is used by the OpenVPN Connect clients and OpenVPN Connexa. Change-Id: Ie20289737d502a25eedc60677d3c350ddc3b3c22 Signed-off-by: Arne Schwabe <[email protected]> --- A sample/management/acc_dpc1_client_demo.py A sample/management/acc_dpc1_server_demo.py 2 files changed, 291 insertions(+), 0 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/60/1860/1 diff --git a/sample/management/acc_dpc1_client_demo.py b/sample/management/acc_dpc1_client_demo.py new file mode 100755 index 0000000..baaa9af --- /dev/null +++ b/sample/management/acc_dpc1_client_demo.py @@ -0,0 +1,121 @@ +#!/opt/homebrew/bin/python3 +#!/usr/bin/env python3 +# Copyright (c) 2026 OpenVPN Inc <[email protected]> +# Copyright (c) 2026 Arne Schwabe <[email protected]> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see <https://www.gnu.org/licenses/>. +import asyncio +import base64 +import json +import logging +import platform + +from omi import OmiProtocol +from omi_tools import run_main, start_management_protocol, ConnectData + +logger = logging.getLogger(__name__) + +class OmiAccProtocol(OmiProtocol): + def __init__(self, finish_future, connect_data:ConnectData): + super().__init__(finish_future, connect_data) + self.accmsg = "" + + def recv_notify_ACC(self, args): + # dpc1,0,eyJkcGNfcmVx[...] + protocol, fragment, b64msg = args.split(",", 2) + if protocol != "dpc1": + logging.info(f"Received unknown ACC protocol: {protocol}") + return + + msg = base64.decodebytes(b64msg.encode()) + self.accmsg += msg.decode() + + # Message is not yet complete. Wait for more fragments. + if fragment != "0": + return + + self.parseDpc1(self.accmsg) + self.accmsg = "" + + def parseDpc1(self, accmsg): + msg = json.loads(accmsg) + + logging.error(f"Received sys_info message: {accmsg}") + + dpc_request = msg["dpc_request"] + ver = dpc_request["ver"] + if ver != "1.0": + logging.error(f"Received unknown ACC protocol version: {ver}") + return + + client_info = dpc_request.get("client_info", False) + antivirus = "antivirus" in dpc_request + disk_encryption = "disk_encryption" in dpc_request + + logger.info(f"Received DPC1 request: antivirus={antivirus}, disk_encryption={disk_encryption}, client_info={client_info}") + + if client_info: + self._sendDPC1ClientInfo() + + def _sendDPC1ClientInfo(self): + system = platform.system() + if system == "Darwin": + system = "MacOS" + + response = { + "dpc_response": { + "ver": "1.0", + "client_info": { + "os": { + "type": system, + "version": platform.release(), + "extra": + { + "arch": platform.machine(), + } + } + } + } + } + + b64msg = base64.b64encode(json.dumps(response).encode()).decode() + + command = f"acc-msg\ndpc1\n6\n{b64msg}\nEND" + self.queue_command(command) + +async def main(connect_data:ConnectData): + transport, protocol, finish_future = await start_management_protocol(connect_data, OmiAccProtocol) + while True: + await asyncio.sleep(1) + print(".", end="", flush=True) + + management_version = await protocol.management_ready() + print(f"Management interface version: {management_version}") + + await protocol.set_bytecount_interval(5) + + await finish_future + + +if __name__ == '__main__': + run_main(main) \ No newline at end of file diff --git a/sample/management/acc_dpc1_server_demo.py b/sample/management/acc_dpc1_server_demo.py new file mode 100644 index 0000000..a62d22c --- /dev/null +++ b/sample/management/acc_dpc1_server_demo.py @@ -0,0 +1,170 @@ +#!/usr/bin/env /usr/bin/python3 +# Copyright (c) 2026 OpenVPN Inc <[email protected]> +# Copyright (c) 2026 Arne Schwabe <[email protected]> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see <https://www.gnu.org/licenses/>. +import asyncio +import base64 +import json +import logging +import platform +from pprint import pprint + +from sample.management.omi import OMIServerProtocol + +logger = logging.getLogger(__name__) + +class OmiDPCServerProtocol(OMIServerProtocol): + def __init__(self, finish_future): + super().__init__(finish_future) + self.accmsg = "" + + def recv_notify_ACC(self, args): + # 7,3,dpc1,0,eyJkcGNfcmVx[...] + protocol, fragment, b64msg = args.split(",", 2) + if protocol != "dpc1": + logging.info(f"Received unknown ACC protocol: {protocol}") + return + + msg = base64.decodebytes(b64msg.encode()) + self.accmsg += msg.decode() + + # Message is not yet complete. Wait for more fragments. + if fragment != "0": + return + + try: + self.parseDpc1(self.accmsg) + self.accmsg = "" + except Exception as e: + logger.error(f"Error parsing DPC1 message: {e}", exc_info=True) + + def parseDpc1(self, accmsg): + print(f"Received DPC1 message: {accmsg!r}") + msg = json.loads(accmsg) + pprint(msg) + + dpc_response = msg["dpc_response"] + ver = dpc_response["ver"] + if ver != "1.0": + logging.error(f"Received unknown ACC protocol version: {ver}") + return + + client_info = dpc_response.get("client_info", {}) + antivirus = "antivirus" in dpc_response + disk_encryption = "disk_encryption" in dpc_response + + logger.info(f"Received DPC1 response: antivirus={antivirus}, disk_encryption={disk_encryption}, client_info={client_info}") + + + def _sendDPC1ClientInfo(self): + system = platform.system() + if system == "Darwin": + system = "MacOS" + + response = { + "dpc_response": { + "ver": "1.0", + "client_info": { + "os": { + "type": system, + "version": platform.release(), + "extra": + { + "arch": platform.machine(), + } + } + } + } + } + + b64msg = base64.b64encode(json.dumps(response).encode()).decode() + + command = f"acc-msg\ndpc1\n6\n{b64msg}\nEND" + self.queue_command(command) + + def client_event_ESTABLISHED(self, client, kid, env, extra): + print(f"CLient connected: {client}") + pprint(env) + + def client_event_CONNECT(self, client, kid, env, extra): + print(f"Authenticating client {client.cid}, {kid}") + + iv_acc = env.get("IV_ACC") + if not iv_acc: + self.queue_command(f"client-auth-nt {client.cid} {kid}") + + return + else: + pass + #cmd = f"client-auth {client.cid} {kid}\npush \"custom-control 1280 A:6 dpc1:cck1\"\nEND" + #self.queue_command(cmd) + + self._send_dpc1_command(client.cid, kid) + + def _send_dpc1_command(self, cid, kid): + dpc_cmd = "{\"dpc_request\":{\"ver\":\"1.0\",\"correlation_id\":\"deb4446f-2086-4af5-aa6d-ac42285ef3fe\",\"timestamp\":\"Fri Jul 17 14:08:02.395 2026\",\"client_info\":true}}" + dpc_cmd = '{"dpc_request":{"ver":"1.0","correlation_id":"846fe236-efa0-48aa-b92e-35ae5823d2f7","timestamp":"Fri Aug 07 17:47:24.370 2026","client_info":true}}' + dpc_cmd = '{"dpc_request":{"ver":"1.0","correlation_id":"n/a","timestamp":"yesterday","client_info":true, "disk_encryption": {"full": true}, "antivirus":{}}}' + + + self.queue_command(f"client-pending-auth {cid} {kid} ACC 180") + + dpc_cmd_base64 = base64.encodebytes(dpc_cmd.encode()).decode() + acc_protocol = "dpc1" + + # TODO: Implement fragmentation + cmd = f"client-acc-msg {cid} {kid}\n{acc_protocol}\n6\n{dpc_cmd_base64}\nEND" + + + self.queue_command(cmd) + + + +async def main(): + # Get a reference to the event loop as we plan to use + # low-level APIs. + loop = asyncio.get_running_loop() + + finish_future = loop.create_future() + path = '/tmp/test-omi-server' + + transport, protocol = await loop.create_unix_connection(lambda: OmiDPCServerProtocol(finish_future), path) + + management_version = await protocol.management_ready() + print(f"Management interface version: {management_version}") + await protocol.enable_log() + + bs = await protocol.set_bytecount_interval(30) + + print(f"byte set result {bs}") + + while not finish_future.done(): + await asyncio.sleep(5) + #cs:OmiCommandResult = await protocol.get_client_status() + #print("\n".join(cs.result)) + + + +if __name__ == '__main__': + asyncio.run(main()) \ No newline at end of file -- To view, visit http://gerrit.openvpn.net/c/openvpn/+/1860?usp=email To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings?usp=email Gerrit-MessageType: newchange Gerrit-Project: openvpn Gerrit-Branch: master Gerrit-Change-Id: Ie20289737d502a25eedc60677d3c350ddc3b3c22 Gerrit-Change-Number: 1860 Gerrit-PatchSet: 1 Gerrit-Owner: plaisthos <[email protected]> Gerrit-CC: openvpn-devel <[email protected]> _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel