Attention is currently required from: flichtenheld, plaisthos.
Hello plaisthos, flichtenheld,
I'd like you to do a code review.
Please visit
http://gerrit.openvpn.net/c/openvpn/+/1176?usp=email
to review the following change.
Change subject: push-update-server: comment about
`buf_string_compare_advance()` usage in `send_single_push_updaate()`
......................................................................
push-update-server: comment about `buf_string_compare_advance()` usage in
`send_single_push_updaate()`
Change-Id: Icc20f3adccb5689f11df8888c526d0402b32d806
Signed-off-by: Marco Baffo <[email protected]>
---
A src/openvpn/push_util.c
1 file changed, 303 insertions(+), 0 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/76/1176/1
diff --git a/src/openvpn/push_util.c b/src/openvpn/push_util.c
new file mode 100644
index 0000000..e641755
--- /dev/null
+++ b/src/openvpn/push_util.c
@@ -0,0 +1,303 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "push.h"
+#include "buffer.h"
+
+#ifdef ENABLE_MANAGEMENT
+#include "multi.h"
+#endif
+
+int
+process_incoming_push_update(struct context *c, unsigned int permission_mask,
+ unsigned int *option_types_found, struct buffer
*buf,
+ bool msg_sender)
+{
+ int ret = PUSH_MSG_ERROR;
+ const uint8_t ch = buf_read_u8(buf);
+ if (ch == ',')
+ {
+ if (apply_push_options(c, &c->options, buf, permission_mask,
option_types_found, c->c2.es,
+ true))
+ {
+ switch (c->options.push_continuation)
+ {
+ case 0:
+ case 1:
+ ret = PUSH_MSG_UPDATE;
+ break;
+
+ case 2:
+ ret = PUSH_MSG_CONTINUATION;
+ break;
+ }
+ }
+ else if (!msg_sender)
+ {
+ throw_signal_soft(SIGUSR1, "Offending option received from
server");
+ }
+ }
+ else if (ch == '\0')
+ {
+ ret = PUSH_MSG_UPDATE;
+ }
+
+ return ret;
+}
+
+#ifdef ENABLE_MANAGEMENT
+/**
+ * Return index of last `,` or `0` if it didn't find any.
+ * If there is a comma at index `0` it's an error anyway
+ */
+static int
+find_first_comma_of_next_bundle(const char *str, int ix)
+{
+ while (ix > 0)
+ {
+ if (str[ix] == ',')
+ {
+ return ix;
+ }
+ ix--;
+ }
+ return 0;
+}
+
+/* Allocate memory and assemble the final message */
+static struct buffer
+forge_msg(const char *src, const char *continuation, struct gc_arena *gc)
+{
+ int src_len = strlen(src);
+ int con_len = continuation ? strlen(continuation) : 0;
+ struct buffer buf = alloc_buf_gc(src_len + sizeof(push_update_cmd) +
con_len + 2, gc);
+
+ buf_printf(&buf, "%s,%s%s", push_update_cmd, src, continuation ?
continuation : "");
+
+ return buf;
+}
+
+static char *
+gc_strdup(const char *src, struct gc_arena *gc)
+{
+ char *ret = gc_malloc((strlen(src) + 1) * sizeof(char), true, gc);
+
+ strcpy(ret, src);
+ return ret;
+}
+
+/* It split the messagge (if necessay) and fill msgs with the message chunks.
+ * Return `false` on failure an `true` on success.
+ */
+static bool
+message_splitter(const char *s, struct buffer *msgs, struct gc_arena *gc,
const int safe_cap)
+{
+ if (!s || !*s)
+ {
+ return false;
+ }
+
+ char *str = gc_strdup(s, gc);
+ int i = 0;
+ int im = 0;
+
+ while (*str)
+ {
+ /* + ',' - '/0' */
+ if (strlen(str) > safe_cap)
+ {
+ int ci = find_first_comma_of_next_bundle(str, safe_cap);
+ if (!ci)
+ {
+ /* if no commas were found go to fail, do not send any message
*/
+ return false;
+ }
+ str[ci] = '\0';
+ /* copy from i to (ci -1) */
+ msgs[im] = forge_msg(str, ",push-continuation 2", gc);
+ i = ci + 1;
+ }
+ else
+ {
+ if (im)
+ {
+ msgs[im] = forge_msg(str, ",push-continuation 1", gc);
+ }
+ else
+ {
+ msgs[im] = forge_msg(str, NULL, gc);
+ }
+ i = strlen(str);
+ }
+ str = &str[i];
+ im++;
+ }
+ return true;
+}
+
+/* send the message(s) prepared to one single client */
+static bool
+send_single_push_update(struct context *c, struct buffer *msgs, unsigned int
*option_types_found)
+{
+ if (!msgs[0].data || !*(msgs[0].data))
+ {
+ return false;
+ }
+ int i = -1;
+
+ while (msgs[++i].data && *(msgs[i].data))
+ {
+ if (!send_control_channel_string(c, BSTR(&msgs[i]), D_PUSH))
+ {
+ return false;
+ }
+
+ /* After sending the control message, we update the options
server-side in the client's context
+ * so pushed options like ifconfig/ifconfig-ipv6 can actually work.
+ * If we don't do that, the packets arriving from the client with the
new address will be
+ * rejected because the value in the option is an old one.
+ * For the same reason we later update the vhash too in
`send_push_update()` function.
+ * Using `buf_string_compare_advance()` we mimic the behavior inside
`process_incoming_push_msg()`.
+ * However, we don't need to check the return value here because we
just want to `advance`,
+ * meaning we skip the `push_update_cmd' we added earlier. */
+ buf_string_compare_advance(&msgs[i], push_update_cmd);
+ if (process_incoming_push_update(c, pull_permission_mask(c),
option_types_found, &msgs[i], true) == PUSH_MSG_ERROR)
+ {
+ msg(M_WARN, "Failed to process push update message sent to client
ID: %u",
+ c->c2.tls_multi ? c->c2.tls_multi->peer_id : UINT32_MAX);
+ continue;
+ }
+ c->options.push_option_types_found |= *option_types_found;
+ if (!options_postprocess_pull(&c->options, c->c2.es))
+ {
+ msg(M_WARN, "Failed to post-process push update message sent to
client ID: %u",
+ c->c2.tls_multi ? c->c2.tls_multi->peer_id : UINT32_MAX);
+ }
+ }
+ return true;
+}
+
+int
+send_push_update(struct multi_context *m, const void *target, const char *msg,
const push_update_type type, const int push_bundle_size)
+{
+ if (!msg || !*msg || !m
+ || (!target && type != UPT_BROADCAST))
+ {
+ return -EINVAL;
+ }
+
+ struct gc_arena gc = gc_new();
+ /* extra space for possible trailing ifconfig and push-continuation */
+ const int extra = 84 + sizeof(push_update_cmd);
+ /* push_bundle_size is the maximum size of a message, so if the message
+ * we want to send exceeds that size we have to split it into smaller
messages */
+ const int safe_cap = push_bundle_size - extra;
+ int msgs_num = (strlen(msg) / safe_cap) + ((strlen(msg) % safe_cap) != 0);
+ struct buffer *msgs = gc_malloc((msgs_num + 1) * sizeof(struct buffer),
true, &gc);
+
+ unsigned int option_types_found = 0;
+
+ msgs[msgs_num].data = NULL;
+ if (!message_splitter(msg, msgs, &gc, safe_cap))
+ {
+ gc_free(&gc);
+ return -EINVAL;
+ }
+
+ if (type == UPT_BY_CID)
+ {
+ struct multi_instance *mi = lookup_by_cid(m, *((unsigned long
*)target));
+
+ if (!mi)
+ {
+ return -ENOENT;
+ }
+
+ const char *old_ip = mi->context.options.ifconfig_local;
+ const char *old_ipv6 = mi->context.options.ifconfig_ipv6_local;
+ if (!mi->halt
+ && send_single_push_update(&mi->context, msgs,
&option_types_found))
+ {
+ if (option_types_found & OPT_P_UP)
+ {
+ update_vhash(m, mi, old_ip, old_ipv6);
+ }
+ gc_free(&gc);
+ return 1;
+ }
+ else
+ {
+ gc_free(&gc);
+ return 0;
+ }
+ }
+
+ int count = 0;
+ struct hash_iterator hi;
+ const struct hash_element *he;
+
+ hash_iterator_init(m->iter, &hi);
+ while ((he = hash_iterator_next(&hi)))
+ {
+ struct multi_instance *curr_mi = he->value;
+
+ if (curr_mi->halt)
+ {
+ continue;
+ }
+
+ /* Type is UPT_BROADCAST so we update every client */
+ option_types_found = 0;
+ const char *old_ip = curr_mi->context.options.ifconfig_local;
+ const char *old_ipv6 = curr_mi->context.options.ifconfig_ipv6_local;
+ if (!send_single_push_update(&curr_mi->context, msgs,
&option_types_found))
+ {
+ msg(M_CLIENT, "ERROR: Peer ID: %u has not been updated",
+ curr_mi->context.c2.tls_multi ?
curr_mi->context.c2.tls_multi->peer_id : UINT32_MAX);
+ continue;
+ }
+ if (option_types_found & OPT_P_UP)
+ {
+ update_vhash(m, curr_mi, old_ip, old_ipv6);
+ }
+ count++;
+ }
+
+ hash_iterator_free(&hi);
+ gc_free(&gc);
+ return count;
+}
+
+#define RETURN_UPDATE_STATUS(n_sent) \
+ do \
+ { \
+ if ((n_sent) > 0) \
+ { \
+ msg(M_CLIENT, "SUCCESS: %d client(s) updated", (n_sent)); \
+ return true; \
+ } \
+ else \
+ { \
+ msg(M_CLIENT, "ERROR: no client updated"); \
+ return false; \
+ } \
+ } while (0)
+
+
+bool
+management_callback_send_push_update_broadcast(void *arg, const char *options)
+{
+ int n_sent = send_push_update(arg, NULL, options, UPT_BROADCAST,
PUSH_BUNDLE_SIZE);
+
+ RETURN_UPDATE_STATUS(n_sent);
+}
+
+bool
+management_callback_send_push_update_by_cid(void *arg, unsigned long cid,
const char *options)
+{
+ int n_sent = send_push_update(arg, &cid, options, UPT_BY_CID,
PUSH_BUNDLE_SIZE);
+
+ RETURN_UPDATE_STATUS(n_sent);
+}
+#endif /* ifdef ENABLE_MANAGEMENT */
--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/1176?usp=email
To unsubscribe, or for help writing mail filters, visit
http://gerrit.openvpn.net/settings
Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: Icc20f3adccb5689f11df8888c526d0402b32d806
Gerrit-Change-Number: 1176
Gerrit-PatchSet: 1
Gerrit-Owner: mrbff <[email protected]>
Gerrit-Reviewer: flichtenheld <[email protected]>
Gerrit-Reviewer: plaisthos <[email protected]>
Gerrit-CC: openvpn-devel <[email protected]>
Gerrit-Attention: plaisthos <[email protected]>
Gerrit-Attention: flichtenheld <[email protected]>
Gerrit-MessageType: newchange
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel