summaryrefslogtreecommitdiffstats
path: root/push.c
diff options
context:
space:
mode:
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>2009-09-27 02:12:15 +0000
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>2009-09-27 02:12:15 +0000
commit3eee126eba7314e203a50b6398fa5333cbf12ff7 (patch)
tree2b07c330d53e1346c5383bfefe1a997d54bbde7c /push.c
parent673f583f76358b57e7f610084d3cb28bb2a9c4a2 (diff)
downloadopenvpn-3eee126eba7314e203a50b6398fa5333cbf12ff7.tar.gz
openvpn-3eee126eba7314e203a50b6398fa5333cbf12ff7.tar.xz
openvpn-3eee126eba7314e203a50b6398fa5333cbf12ff7.zip
Eliminated the limitation on the number of options that can be pushed
to clients, including routes. Previously, all pushed options needed to fit within a 1024 byte options string. Remember that to make use of this feature to allow many routes to be pushed to clients, the client config file must specify the max-routes option, and the number of pushed routes cannot exceed this limit. Also, both server and client must include this commit. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@4991 e7ae566f-a301-0410-adde-c780ea21d3b5
Diffstat (limited to 'push.c')
-rw-r--r--push.c182
1 files changed, 117 insertions, 65 deletions
diff --git a/push.c b/push.c
index cbc3c10..36febe1 100644
--- a/push.c
+++ b/push.c
@@ -99,9 +99,10 @@ incoming_push_message (struct context *c, const struct buffer *buffer)
if (status == PUSH_MSG_ERROR)
msg (D_PUSH_ERRORS, "WARNING: Received bad push/pull message: %s", BSTR (buffer));
- else if (status == PUSH_MSG_REPLY)
+ else if (status == PUSH_MSG_REPLY || status == PUSH_MSG_CONTINUATION)
{
- do_up (c, true, option_types_found); /* delay bringing tun/tap up until --push parms received from remote */
+ if (status == PUSH_MSG_REPLY)
+ do_up (c, true, option_types_found); /* delay bringing tun/tap up until --push parms received from remote */
event_timeout_clear (&c->c2.push_request_interval);
}
@@ -115,60 +116,114 @@ send_push_request (struct context *c)
}
#if P2MP_SERVER
+
bool
send_push_reply (struct context *c)
{
struct gc_arena gc = gc_new ();
- struct buffer buf = alloc_buf_gc (MAX_PUSH_LIST_LEN + 256, &gc);
- bool ret = false;
+ struct buffer buf = alloc_buf_gc (TLS_CHANNEL_BUF_SIZE, &gc);
+ struct push_entry *e = c->options.push_list.head;
+ bool multi_push = false;
+ static char cmd[] = "PUSH_REPLY";
+ const int extra = 64; /* extra space for possible trailing ifconfig and push-continuation */
+ const int safe_cap = BCAP (&buf) - extra;
- buf_printf (&buf, "PUSH_REPLY");
+ buf_printf (&buf, cmd);
- if (c->options.push_list && strlen (c->options.push_list->options))
- buf_printf (&buf, ",%s", c->options.push_list->options);
+ 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)
+ goto fail;
+ multi_push = true;
+ buf_reset_len (&buf);
+ buf_printf (&buf, cmd);
+ }
+ }
+ if (BLEN (&buf) + l >= safe_cap)
+ {
+ msg (M_WARN, "--push option is too long");
+ goto fail;
+ }
+ buf_printf (&buf, ",%s", e->option);
+ }
+ e = e->next;
+ }
if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask)
buf_printf (&buf, ",ifconfig %s %s",
print_in_addr_t (c->c2.push_ifconfig_local, 0, &gc),
print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, &gc));
+ if (multi_push)
+ buf_printf (&buf, ",push-continuation 1");
- if (strlen (BSTR (&buf)) < MAX_PUSH_LIST_LEN)
- ret = send_control_channel_string (c, BSTR (&buf), D_PUSH);
- else
- msg (M_WARN, "Maximum length of --push buffer (%d) has been exceeded", MAX_PUSH_LIST_LEN);
+ if (BLEN (&buf) > sizeof(cmd)-1)
+ {
+ const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
+ if (!status)
+ goto fail;
+ }
gc_free (&gc);
- return ret;
+ return true;
+
+ fail:
+ gc_free (&gc);
+ return false;
}
-void
-push_option (struct options *o, const char *opt, int msglevel)
+static void
+push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
{
- int len;
- bool first = false;
-
if (!string_class (opt, CC_ANY, CC_COMMA))
{
msg (msglevel, "PUSH OPTION FAILED (illegal comma (',') in string): '%s'", opt);
}
else
{
- if (!o->push_list)
+ struct push_entry *e;
+ ALLOC_OBJ_CLEAR_GC (e, struct push_entry, &o->gc);
+ e->enable = true;
+ e->option = opt;
+ if (o->push_list.head)
{
- ALLOC_OBJ_CLEAR_GC (o->push_list, struct push_list, &o->gc);
- first = true;
+ ASSERT(o->push_list.tail);
+ o->push_list.tail->next = e;
+ o->push_list.tail = e;
}
-
- len = strlen (o->push_list->options);
- if (len + strlen (opt) + 2 >= MAX_PUSH_LIST_LEN)
+ else
{
- msg (msglevel, "Maximum length of --push buffer (%d) has been exceeded", MAX_PUSH_LIST_LEN);
+ ASSERT(!o->push_list.tail);
+ o->push_list.head = e;
+ o->push_list.tail = e;
}
- else
+ }
+}
+
+void
+push_option (struct options *o, const char *opt, int msglevel)
+{
+ push_option_ex (o, opt, true, msglevel);
+}
+
+void
+clone_push_list (struct options *o)
+{
+ if (o->push_list.head)
+ {
+ const struct push_entry *e = o->push_list.head;
+ push_reset (o);
+ while (e)
{
- if (!first)
- strcat (o->push_list->options, ",");
- strcat (o->push_list->options, opt);
+ push_option_ex (o, string_alloc (e->option, &o->gc), true, M_FATAL);
+ e = e->next;
}
}
}
@@ -184,7 +239,7 @@ push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc)
void
push_reset (struct options *o)
{
- o->push_list = NULL;
+ CLEAR (o->push_list);
}
#endif
@@ -224,14 +279,31 @@ process_incoming_push_msg (struct context *c,
const uint8_t ch = buf_read_u8 (&buf);
if (ch == ',')
{
- pre_pull_restore (&c->options);
- c->c2.pulled_options_string = string_alloc (BSTR (&buf), &c->c2.gc);
+ struct buffer buf_orig = buf;
+ if (!c->c2.did_pre_pull_restore)
+ {
+ pre_pull_restore (&c->options);
+ md5_state_init (&c->c2.pulled_options_state);
+ c->c2.did_pre_pull_restore = true;
+ }
if (apply_push_options (&c->options,
&buf,
permission_mask,
option_types_found,
c->c2.es))
- ret = PUSH_MSG_REPLY;
+ switch (c->options.push_continuation)
+ {
+ case 0:
+ case 1:
+ md5_state_update (&c->c2.pulled_options_state, BPTR(&buf_orig), BLEN(&buf_orig));
+ md5_state_final (&c->c2.pulled_options_state, &c->c2.pulled_options_digest);
+ ret = PUSH_MSG_REPLY;
+ break;
+ case 2:
+ md5_state_update (&c->c2.pulled_options_state, BPTR(&buf_orig), BLEN(&buf_orig));
+ ret = PUSH_MSG_CONTINUATION;
+ break;
+ }
}
else if (ch == '\0')
{
@@ -243,36 +315,27 @@ process_incoming_push_msg (struct context *c,
}
#if P2MP_SERVER
+
/*
* Remove iroutes from the push_list.
*/
void
remove_iroutes_from_push_route_list (struct options *o)
{
- if (o && o->push_list && o->iroutes)
+ if (o && o->push_list.head && o->iroutes)
{
struct gc_arena gc = gc_new ();
- struct push_list *pl;
- struct buffer in, out;
- char *line;
- bool first = true;
-
- /* prepare input and output buffers */
- ALLOC_OBJ_CLEAR_GC (pl, struct push_list, &gc);
- ALLOC_ARRAY_CLEAR_GC (line, char, MAX_PUSH_LIST_LEN, &gc);
-
- buf_set_read (&in, (const uint8_t*) o->push_list->options, strlen (o->push_list->options));
- buf_set_write (&out, (uint8_t*) pl->options, sizeof (pl->options));
+ struct push_entry *e = o->push_list.head;
/* cycle through the push list */
- while (buf_parse (&in, ',', line, MAX_PUSH_LIST_LEN))
+ while (e)
{
char *p[MAX_PARMS];
- bool copy = true;
+ bool enable = true;
/* parse the push item */
CLEAR (p);
- if (parse_line (line, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc))
+ if (parse_line (e->option, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc))
{
/* is the push item a route directive? */
if (p[0] && !strcmp (p[0], "route") && !p[3])
@@ -292,7 +355,7 @@ remove_iroutes_from_push_route_list (struct options *o)
{
if (network == ir->network && netmask == netbits_to_netmask (ir->netbits >= 0 ? ir->netbits : 32))
{
- copy = false;
+ enable = false;
break;
}
}
@@ -301,28 +364,17 @@ remove_iroutes_from_push_route_list (struct options *o)
}
/* should we copy the push item? */
- if (copy)
- {
- if (!first)
- buf_printf (&out, ",");
- buf_printf (&out, "%s", line);
- first = false;
- }
- else
- msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", line);
- }
-
-#if 0
- msg (M_INFO, "BEFORE: '%s'", o->push_list->options);
- msg (M_INFO, "AFTER: '%s'", pl->options);
-#endif
+ e->enable = enable;
+ if (!enable)
+ msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", e->option);
- /* copy new push list back to options */
- *o->push_list = *pl;
+ e = e->next;
+ }
gc_free (&gc);
}
}
+
#endif
#endif