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

Reply via email to