Move client-specific push options (currently peer-id and cipher) to
separate list, which is deallocated after push_reply
has been send. This makes sure that options are fit into buf,
not duplicated nor leak memory on renegotiation.
Signed-off-by: Lev Stipakov
---
src/openvpn/push.c | 142 +++--
1 file changed, 83 insertions(+), 59 deletions(-)
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index a1b999e..a968c29 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -40,26 +40,29 @@
#if P2MP
+static char push_reply_cmd[] = "PUSH_REPLY";
+
/**
- * Add an option to the push list by providing a format string.
+ * Add an option to the given push list by providing a format string.
*
* The string added to the push options is allocated in o->gc, so the caller
* does not have to preserve anything.
*
- * @param oThe current connection's options
- * @param msglevel The message level to use when printing errors
+ * @param gc GC arena where options are allocated
+ * @param push_list Push list containing options
+ * @param msglevel The message level to use when printing errors
* @param fmt Format string for the option
* @param ... Format string arguments
*
* @return true on success, false on failure.
*/
-static bool push_option_fmt(struct options *o, int msglevel,
+static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list,
int msglevel,
const char *fmt, ...)
#ifdef __GNUC__
#if __USE_MINGW_ANSI_STDIO
-__attribute__ ((format (gnu_printf, 3, 4)))
+__attribute__ ((format (gnu_printf, 4, 5)))
#else
-__attribute__ ((format (__printf__, 3, 4)))
+__attribute__ ((format (__printf__, 4, 5)))
#endif
#endif
;
@@ -296,12 +299,14 @@ send_push_request (struct context *c)
* Prepare push options, based on local options and available peer info.
*
* @param options Connection options
+ * @param gc gc arena for allocating push options
+ * @param push_list push list to where options are added
* @param tls_multiTLS state structure for the current tunnel
*
* @return true on success, false on failure.
*/
static bool
-prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
+prepare_push_reply (struct options *o, struct gc_arena *gc, struct push_list
*push_list, struct tls_multi *tls_multi)
{
const char *optstr = NULL;
const char * const peer_info = tls_multi->peer_info;
@@ -314,7 +319,7 @@ prepare_push_reply (struct options *o, struct tls_multi
*tls_multi)
int r = sscanf(optstr, "IV_PROTO=%d", );
if ((r == 1) && (proto >= 2))
{
- push_option_fmt(o, M_USAGE, "peer-id %d", tls_multi->peer_id);
+ push_option_fmt(gc, push_list, M_USAGE, "peer-id %d",
tls_multi->peer_id);
}
}
@@ -335,29 +340,63 @@ prepare_push_reply (struct options *o, struct tls_multi
*tls_multi)
{
/* Push the first cipher from --ncp-ciphers to the client.
* TODO: actual negotiation, instead of server dictatorship. */
- char *push_cipher = string_alloc(o->ncp_ciphers, >gc);
+ char *push_cipher = string_alloc(o->ncp_ciphers, gc);
o->ciphername = strtok (push_cipher, ":");
- push_option_fmt(o, M_USAGE, "cipher %s", o->ciphername);
+ push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername);
}
}
return true;
}
static bool
-send_push_reply (struct context *c)
+send_push_options (struct context *c, struct buffer *buf, struct push_list
*push_list,
+int safe_cap, bool *push_sent, bool *multi_push)
+{
+ struct push_entry *e = push_list->head;
+
+ while (e)
+{
+ if (e->enable)
+ {
+ const int l = strlen (e->option);
+ if (BLEN (buf) + l >= safe_cap)
+ {
+ buf_printf (buf, ",push-continuation 2");
+ {
+ const bool status = send_control_channel_string (c, BSTR
(buf), D_PUSH);
+ if (!status)
+ return false;
+ *push_sent = true;
+ *multi_push = true;
+ buf_reset_len (buf);
+ buf_printf (buf, "%s", push_reply_cmd);
+ }
+ }
+ if (BLEN (buf) + l >= safe_cap)
+ {
+ msg (M_WARN, "--push option is too long");
+ return false;
+ }
+ buf_printf (buf, ",%s", e->option);
+ }
+ e = e->next;
+}
+ return true;
+}
+
+static bool
+send_push_reply (struct context *c, struct push_list *per_client_push_list)
{
struct gc_arena gc = gc_new ();
struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, );
- struct push_entry *e = c->options.push_list.head;
bool multi_push = false;
- static char cmd[] = "PUSH_REPLY";
const int extra = 84; /* extra space for possible trailing ifconfig and
push-continuation */