static bool 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, &gc); bool multi_push = false; const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */ const int safe_cap = BCAP(&buf) - extra; bool push_sent = false; buf_printf(&buf, "%s", push_reply_cmd); /* send options which are common to all clients */ if (!send_push_options(c, &buf, &c->options.push_list, safe_cap, &push_sent, &multi_push)) { goto fail; } /* send client-specific options */ if (!send_push_options(c, &buf, per_client_push_list, safe_cap, &push_sent, &multi_push)) { goto fail; } if (multi_push) { buf_printf(&buf, ",push-continuation 1"); } if (BLEN(&buf) > sizeof(push_reply_cmd)-1) { const bool status = send_control_channel_string(c, BSTR(&buf), D_PUSH); if (!status) { goto fail; } push_sent = true; } /* If nothing have been pushed, send an empty push, * as the client is expecting a response */ if (!push_sent) { bool status = false; buf_reset_len(&buf); buf_printf(&buf, "%s", push_reply_cmd); status = send_control_channel_string(c, BSTR(&buf), D_PUSH); if (!status) { goto fail; } } gc_free(&gc); return true; fail: gc_free(&gc); return false; }
bool send_push_reply (struct context *c) { struct gc_arena gc = gc_new (); struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc); 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 */ const int safe_cap = BCAP (&buf) - extra; bool push_sent = false; msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap ); buf_printf (&buf, "%s", cmd); if ( c->c2.push_ifconfig_ipv6_defined ) { /* IPv6 is put into buffer first, could be lengthy */ buf_printf( &buf, ",ifconfig-ipv6 %s/%d %s", print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc), c->c2.push_ifconfig_ipv6_netbits, print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) ); if (BLEN (&buf) >= safe_cap) { msg (M_WARN, "--push ifconfig-ipv6 option is too long"); goto fail; } } 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; push_sent = true; multi_push = true; buf_reset_len (&buf); buf_printf (&buf, "%s", 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) { in_addr_t ifconfig_local = c->c2.push_ifconfig_local; #ifdef ENABLE_CLIENT_NAT if (c->c2.push_ifconfig_local_alias) ifconfig_local = c->c2.push_ifconfig_local_alias; #endif buf_printf (&buf, ",ifconfig %s %s", print_in_addr_t (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 (BLEN (&buf) > sizeof(cmd)-1) { const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH); if (!status) goto fail; push_sent = true; } /* If nothing have been pushed, send an empty push, * as the client is expecting a response */ if (!push_sent) { bool status = false; buf_reset_len (&buf); buf_printf (&buf, "%s", cmd); status = send_control_channel_string (c, BSTR(&buf), D_PUSH); if (!status) goto fail; } gc_free (&gc); return true; fail: gc_free (&gc); return false; }