Browse code

Add remote-count and remote-entry query via management

Selecting the remote host via the management interface
(management-query-remote) provides a restrictive user
experience as there is no easy way to tabulate all available
remote entries and show a list to the user to choose from.
Fix that.

Two new commands for querying the management interface are added:
(i) remote-entry-count : returns the number of remotes specified
in the config file. Example result:
10
END

(ii) remote-entry-get i [j]: returns the remote entry at index i
in the form index,host,port,protocol. Or, if j is present
all entries from index i to j-1 are returned, one per line.

Example result for i = 2:
2,ovpn.example.com,1194,udp
END
Example result for i = 2, j = 4
2,ovpn.example.com,1194,udp
3,ovpn.example.com,443,tcp-client
END

remote-entry-get all: returns all remote entries.

v2: use independent callback functions for the two commands
v3: return results as 0 or more lines terminated by END, as done
for all other similar commands. v1 was fashioned after
pkcs11-id-count and pkcs11-id-get which uses a format not
consistent with the rest of the management commands.

See also management-notes.txt

Signed-off-by: Selva Nair <selva.nair@gmail.com>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Message-Id: <20210907223126.8440-1-selva.nair@gmail.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg22815.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Selva Nair authored on 2021/09/08 07:31:24
Showing 5 changed files
... ...
@@ -10,6 +10,11 @@ instead: https://github.com/OpenVPN/openvpn/issues
10 10
 
11 11
 New features
12 12
 ------------
13
+New management commands to enumerate and list remote entries
14
+    Use ``remote-entry-count`` and ``remote-entry-get``
15
+    commands from the management interface to get the number of
16
+    remote entries and the entries themselves.
17
+
13 18
 Keying Material Exporters (RFC 5705) based key generation
14 19
     As part of the cipher negotiation OpenVPN will automatically prefer
15 20
     the RFC5705 based key material generation to the current custom
... ...
@@ -785,6 +785,66 @@ Immediately kill a client instance by CID.
785 785
 CID -- client ID.  See documentation for ">CLIENT:" notification for more
786 786
 info.
787 787
 
788
+COMMAND -- remote-entry-count (OpenVPN 2.6+ management version > 3)
789
+-------------------------------------------------------------------
790
+
791
+Retrieve available number of remote host/port entries
792
+
793
+Example:
794
+
795
+  Management interface client sends:
796
+
797
+    remote-entry-count
798
+
799
+  OpenVPN daemon responds with
800
+
801
+  5
802
+  END
803
+
804
+COMMAND -- remote-entry-get (OpenVPN 2.6+ management version > 3)
805
+------------------------------------------------------------------
806
+
807
+  remote-entry-get <start> [<end>]
808
+
809
+Retrieve remote entry (host, port and protocol) for index
810
+<start> or indices from <start> to <end>+1. Alternatively
811
+<start> = "all" retrieves all remote entries.
812
+
813
+Example 1:
814
+
815
+  Management interface client sends:
816
+
817
+    remote-entry-get 1
818
+
819
+  OpenVPN daemon responds with
820
+
821
+  1,vpn.example.com,1194,udp
822
+  END
823
+
824
+Example 2:
825
+
826
+  Management interface client sends:
827
+
828
+    remote-entry-get 1 3
829
+
830
+  OpenVPN daemon responds with
831
+
832
+    1,vpn.example.com,1194,udp
833
+    2,vpn.example.net,443,tcp-client
834
+    END
835
+
836
+Example 3:
837
+  Management interface client sends:
838
+
839
+    remote-entry-get all
840
+
841
+  OpenVPN daemon with 3 connection entries responds with
842
+
843
+    1,vpn.example.com,1194,udp
844
+    2,vpn.example.com,443,tcp-client
845
+    3,vpn.example.net,443,udp
846
+    END
847
+
788 848
 COMMAND -- remote  (OpenVPN AS 2.1.5/OpenVPN 2.3 or higher)
789 849
 --------------------------------------------
790 850
 
... ...
@@ -329,6 +329,48 @@ management_callback_send_cc_message(void *arg,
329 329
     return status;
330 330
 }
331 331
 
332
+static unsigned int
333
+management_callback_remote_entry_count(void *arg)
334
+{
335
+    assert(arg);
336
+    struct context *c = (struct context *) arg;
337
+    struct connection_list *l = c->options.connection_list;
338
+
339
+    return l->len;
340
+}
341
+
342
+static bool
343
+management_callback_remote_entry_get(void *arg, unsigned int index, char **remote)
344
+{
345
+    assert(arg);
346
+    assert(remote);
347
+
348
+    struct context *c = (struct context *) arg;
349
+    struct connection_list *l = c->options.connection_list;
350
+    bool ret = true;
351
+
352
+    if (index < l->len)
353
+    {
354
+        struct connection_entry *ce = l->array[index];
355
+        const char *proto = proto2ascii(ce->proto, ce->af, false);
356
+
357
+        /* space for output including 2 commas and a nul */
358
+        int len = strlen(ce->remote) + strlen(ce->remote_port) + strlen(proto) + 2 + 1;
359
+        char *out = malloc(len);
360
+        check_malloc_return(out);
361
+
362
+        openvpn_snprintf(out, len, "%s,%s,%s", ce->remote, ce->remote_port, proto);
363
+        *remote = out;
364
+    }
365
+    else
366
+    {
367
+        ret = false;
368
+        msg(M_WARN, "Out of bounds index in management query for remote entry: index = %u", index);
369
+    }
370
+
371
+    return ret;
372
+}
373
+
332 374
 static bool
333 375
 management_callback_remote_cmd(void *arg, const char **p)
334 376
 {
... ...
@@ -4085,6 +4127,8 @@ init_management_callback_p2p(struct context *c)
4085 4085
 #ifdef TARGET_ANDROID
4086 4086
         cb.network_change = management_callback_network_change;
4087 4087
 #endif
4088
+        cb.remote_entry_count = management_callback_remote_entry_count;
4089
+        cb.remote_entry_get = management_callback_remote_entry_get;
4088 4090
         management_set_callback(management, &cb);
4089 4091
     }
4090 4092
 #endif
... ...
@@ -96,6 +96,8 @@ man_help(void)
96 96
     msg(M_CLIENT, "net                    : (Windows only) Show network info and routing table.");
97 97
     msg(M_CLIENT, "password type p        : Enter password p for a queried OpenVPN password.");
98 98
     msg(M_CLIENT, "remote type [host port] : Override remote directive, type=ACCEPT|MOD|SKIP.");
99
+    msg(M_CLIENT, "remote-entry-count     : Get number of available remote entries.");
100
+    msg(M_CLIENT, "remote-entry-get  i|all [j]: Get remote entry at index = i to to j-1 or all.");
99 101
     msg(M_CLIENT, "proxy type [host port flags] : Enter dynamic proxy server info.");
100 102
     msg(M_CLIENT, "pid                    : Show process ID of the current OpenVPN process.");
101 103
 #ifdef ENABLE_PKCS11
... ...
@@ -842,6 +844,63 @@ man_pkcs11_id_get(struct management *man, const int index)
842 842
 #endif /* ifdef ENABLE_PKCS11 */
843 843
 
844 844
 static void
845
+man_remote_entry_count(struct management *man)
846
+{
847
+    unsigned count = 0;
848
+    if (man->persist.callback.remote_entry_count)
849
+    {
850
+        count = (*man->persist.callback.remote_entry_count)(man->persist.callback.arg);
851
+        msg(M_CLIENT, "%u", count);
852
+        msg(M_CLIENT, "END");
853
+    }
854
+    else
855
+    {
856
+        msg(M_CLIENT, "ERROR: The remote-entry-count command is not supported by the current daemon mode");
857
+    }
858
+}
859
+
860
+#define min(a, b) ((a) < (b) ? (a) : (b))
861
+
862
+static void
863
+man_remote_entry_get(struct management *man, const char *p1, const char *p2)
864
+{
865
+    ASSERT(p1);
866
+
867
+    if (man->persist.callback.remote_entry_get
868
+        && man->persist.callback.remote_entry_count)
869
+    {
870
+        bool res;
871
+        unsigned int from, to;
872
+        unsigned int count = (*man->persist.callback.remote_entry_count)(man->persist.callback.arg);
873
+
874
+        from = (unsigned int) atoi(p1);
875
+        to = p2 ? (unsigned int) atoi(p2) : from + 1;
876
+
877
+        if (!strcmp(p1, "all"))
878
+        {
879
+            from = 0;
880
+            to = count;
881
+        }
882
+
883
+        for (unsigned int i = from; i < min(to, count); i++)
884
+        {
885
+            char *remote = NULL;
886
+            res = (*man->persist.callback.remote_entry_get)(man->persist.callback.arg, i, &remote);
887
+            if (res && remote)
888
+            {
889
+                msg(M_CLIENT, "%u,%s", i, remote);
890
+            }
891
+            free(remote);
892
+        }
893
+        msg(M_CLIENT, "END");
894
+    }
895
+    else
896
+    {
897
+        msg(M_CLIENT, "ERROR: The remote-entry command is not supported by the current daemon mode");
898
+    }
899
+}
900
+
901
+static void
845 902
 man_hold(struct management *man, const char *cmd)
846 903
 {
847 904
     if (cmd)
... ...
@@ -1563,6 +1622,17 @@ man_dispatch_command(struct management *man, struct status_output *so, const cha
1563 1563
         }
1564 1564
     }
1565 1565
 #endif
1566
+    else if (streq(p[0], "remote-entry-count"))
1567
+    {
1568
+        man_remote_entry_count(man);
1569
+    }
1570
+    else if (streq(p[0], "remote-entry-get"))
1571
+    {
1572
+        if (man_need(man, p, 1, MN_AT_LEAST))
1573
+        {
1574
+            man_remote_entry_get(man, p[1], p[2]);
1575
+        }
1576
+    }
1566 1577
     else if (streq(p[0], "proxy"))
1567 1578
     {
1568 1579
         if (man_need(man, p, 1, MN_AT_LEAST))
... ...
@@ -31,7 +31,7 @@
31 31
 #include "socket.h"
32 32
 #include "mroute.h"
33 33
 
34
-#define MANAGEMENT_VERSION                      3
34
+#define MANAGEMENT_VERSION                      4
35 35
 #define MANAGEMENT_N_PASSWORD_RETRIES           3
36 36
 #define MANAGEMENT_LOG_HISTORY_INITIAL_SIZE   100
37 37
 #define MANAGEMENT_ECHO_BUFFER_SIZE           100
... ...
@@ -181,6 +181,8 @@ struct management_callback
181 181
 #ifdef TARGET_ANDROID
182 182
     int (*network_change)(void *arg, bool samenetwork);
183 183
 #endif
184
+    unsigned int (*remote_entry_count)(void *arg);
185
+    bool (*remote_entry_get)(void *arg, unsigned int index, char **remote);
184 186
 };
185 187
 
186 188
 /*