2020-03-26 06:48:34 +00:00
|
|
|
/*
|
|
|
|
* lws-minimal-secure-streams-policy2c
|
|
|
|
*
|
|
|
|
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
|
|
|
|
*
|
|
|
|
* This file is made available under the Creative Commons CC0 1.0
|
|
|
|
* Universal Public Domain Dedication.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* This reads policy JSON on stdin and emits it as compileable
|
|
|
|
* C structs.
|
|
|
|
*
|
|
|
|
* It's useful if your platform is too space-constrained for a
|
|
|
|
* JSON policy and needs to build a static policy in C via
|
|
|
|
* LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY... this way you can
|
|
|
|
* still create and maintain the JSON policy but implement it directly
|
|
|
|
* as C structs in your code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <libwebsockets.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <signal.h>
|
2020-06-18 08:53:09 +01:00
|
|
|
#include <stdio.h>
|
2020-03-26 06:48:34 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
static int interrupted, bad = 1;
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
sigint_handler(int sig)
|
|
|
|
{
|
|
|
|
interrupted = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct aggstr {
|
|
|
|
struct aggstr *next;
|
|
|
|
|
|
|
|
const char *orig;
|
|
|
|
size_t offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct aggstr *rbomap, /* retry / backoff object map */
|
|
|
|
*trustmap, /* trust store map */
|
|
|
|
*certmap; /* x.509 cert map */
|
|
|
|
static size_t last_offset;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
purify_csymbol(const char *in, char *temp, size_t templen)
|
|
|
|
{
|
|
|
|
const char *otemp = temp;
|
|
|
|
|
|
|
|
assert (strlen(in) < templen);
|
|
|
|
|
|
|
|
while (*in) {
|
|
|
|
if ((*in >= 'a' && *in <= 'z') || (*in >= 'A' && *in <= 'Z') ||
|
|
|
|
(*in >= '0' && *in <= '9'))
|
|
|
|
*temp++ = *in;
|
|
|
|
else
|
|
|
|
*temp++ = '_';
|
|
|
|
|
|
|
|
in++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*temp = '\0';
|
|
|
|
|
|
|
|
return otemp;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, const char **argv)
|
|
|
|
{
|
|
|
|
const lws_ss_policy_t *pol, *lastpol = NULL;
|
|
|
|
struct lws_context_creation_info info;
|
|
|
|
size_t json_size = 0, est = 0;
|
|
|
|
struct lws_context *context;
|
2020-08-19 11:21:58 +01:00
|
|
|
const lws_ss_auth_t *auth;
|
2020-03-26 06:48:34 +00:00
|
|
|
char prev[128], curr[128];
|
|
|
|
int unique_rbo = 0, m, n;
|
|
|
|
char buf[64], buf1[64];
|
|
|
|
lws_ss_metadata_t *md;
|
|
|
|
struct aggstr *a, *a1;
|
|
|
|
|
|
|
|
signal(SIGINT, sigint_handler);
|
|
|
|
|
|
|
|
memset(&info, 0, sizeof info);
|
|
|
|
lws_cmdline_option_handle_builtin(argc, argv, &info);
|
|
|
|
|
|
|
|
lwsl_user("LWS secure streams policy2c [-d<verb>]\n");
|
|
|
|
|
|
|
|
info.fd_limit_per_thread = 1 + 6 + 1;
|
|
|
|
info.port = CONTEXT_PORT_NO_LISTEN;
|
|
|
|
|
|
|
|
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
|
|
|
|
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
|
|
|
|
|
|
|
/* create the context */
|
|
|
|
|
|
|
|
context = lws_create_context(&info);
|
|
|
|
if (!context) {
|
|
|
|
lwsl_err("lws init failed\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
lws_ss_policy_parse_begin(context, 0);
|
|
|
|
|
|
|
|
printf("/*\n * Autogenerated from the following JSON policy\n */\n\n#if 0\n");
|
|
|
|
|
|
|
|
do {
|
2020-12-12 06:21:40 +00:00
|
|
|
int m, n = (int)read(0, buf, sizeof(buf));
|
2020-03-26 06:48:34 +00:00
|
|
|
|
|
|
|
if (n < 1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
m = lws_ss_policy_parse(context, (uint8_t *)buf, (size_t)n);
|
|
|
|
|
|
|
|
printf("%.*s", n, buf);
|
2020-12-12 06:21:40 +00:00
|
|
|
json_size += (unsigned int)n;
|
2020-03-26 06:48:34 +00:00
|
|
|
|
|
|
|
if (m < 0 && m != LEJP_CONTINUE) {
|
|
|
|
lwsl_err("%s: policy parse failed... lws has WITH_ROLEs"
|
|
|
|
"for what's in the JSON?\n", __func__);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
} while (1);
|
|
|
|
|
|
|
|
printf("\n\n Original JSON size: %zu\n#endif\n\n", json_size);
|
|
|
|
|
|
|
|
lwsl_notice("%s: parsed JSON\n", __func__);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Well, this is fun, isn't it... we have parsed the JSON into in-memory
|
|
|
|
* policy objects, and it has set the context policy pointer to the head
|
|
|
|
* of those but has not set the new policy (which would free the x.509).
|
|
|
|
*
|
|
|
|
* We want to walk the streamtype list first discovering unique objects
|
|
|
|
* and strings referenced there and emitting them compactly as C data,
|
|
|
|
* and then second to emit the streamtype linked-list referring to those
|
|
|
|
* objects.
|
|
|
|
*
|
|
|
|
* For const strings, we aggregate them and avoid generating extra
|
|
|
|
* pointers by encoding the reference as &_lws_ss_staticpol_str[xxx]
|
|
|
|
* where xxx is the fixed offset in the aggregated monster-string. When
|
|
|
|
* doing that, we keep a map of original pointers to offsets.
|
|
|
|
*
|
|
|
|
* Although we want to minimize memory used by the emitted C, we don't
|
|
|
|
* have to sweat memory during this conversion since it's happening on a
|
|
|
|
* PC
|
|
|
|
*/
|
|
|
|
|
|
|
|
pol = lws_ss_policy_get(context);
|
ss: static policy: dynamic vhost instantiation
Presently a vh is allocated per trust store at policy parsing-time, this
is no problem on a linux-class device or if you decide you need a dynamic
policy for functionality reasons.
However if you're in a constrained enough situation that the static policy
makes sense, in the case your trust stores do not have 100% duty cycle, ie,
are anyway always in use, the currently-unused vhosts and their x.509 stack
are sitting there taking up heap for no immediate benefit.
This patch modifies behaviour in ..._STATIC_POLICY_ONLY so that vhosts and
associated x.509 tls contexts are not instantiated until a secure stream using
them is created; they are refcounted, and when the last logical secure
stream using a vhost is destroyed, the vhost and its tls context is also
destroyed.
If another ss connection is created that wants to use the trust store, the
vhost and x.509 context is regenerated again as needed.
Currently the refcounting is by ss, it's also possible to move the refcounting
to be by connection. The choice is between the delay to generate the vh
being visisble at logical ss creation-time, or at connection-time. It's anyway
not preferable to have ss instantiated and taking up space with no associated
connection or connection attempt underway.
NB you will need to reprocess any static policies after this patch so they
conform to the trust_store changes.
2020-07-20 07:28:28 +01:00
|
|
|
|
2020-03-26 06:48:34 +00:00
|
|
|
while (pol) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk the metadata list gathering strings and issuing the
|
|
|
|
* C struct
|
|
|
|
*/
|
|
|
|
|
|
|
|
md = pol->metadata;
|
|
|
|
|
|
|
|
if (md) {
|
|
|
|
int idx = 0;
|
|
|
|
|
|
|
|
printf("\nstatic const lws_ss_metadata_t ");
|
|
|
|
|
|
|
|
prev[0] = '\0';
|
|
|
|
md = pol->metadata;
|
|
|
|
while (md) {
|
|
|
|
|
|
|
|
est += sizeof(lws_ss_metadata_t);
|
|
|
|
|
|
|
|
lws_snprintf(curr, sizeof(curr), "_md_%s_%s",
|
|
|
|
purify_csymbol(pol->streamtype, buf,
|
|
|
|
sizeof(buf)),
|
|
|
|
purify_csymbol(md->name, buf1,
|
|
|
|
sizeof(buf1)));
|
|
|
|
|
|
|
|
printf("%s = {\n", curr);
|
|
|
|
if (prev[0])
|
|
|
|
printf("\t.next = (void *)&%s, \n", prev);
|
|
|
|
|
|
|
|
printf("\t.name = \"%s\",\n", (const char *)md->name);
|
2020-10-11 07:29:47 +01:00
|
|
|
if (md->value__may_own_heap) {
|
|
|
|
printf("\t.value__may_own_heap = (void *)\"%s\",\n",
|
|
|
|
(const char *)md->value__may_own_heap);
|
ss: policy: response code mapping
This adds a per-streamtype JSON mapping table in the policy.
In addition to the previous flow, it lets you generate custom
SS state notifications for specific http response codes, eg:
"http_resp_map": [ { "530": 1530 }, { "531": 1531 } ],
It's not recommended to overload the transport-layer response
code with application layer responses. It's better to return
a 200 and then in the application protocol inside http, explain
what happened from the application perspective, usually with
JSON. But this is designed to let you handle existing systems
that do overload the transport layer response code.
SS states for user use start at LWSSSCS_USER_BASE, which is
1000.
You can do a basic test with minimal-secure-streams and --respmap
flag, this will go to httpbin.org and get a 404, and the warmcat.com
policy has the mapping for 404 -> LWSSSCS_USER_BASE (1000).
Since the mapping emits states, these are serialized and handled
like any other state in the proxy case.
The policy2c example / tool is also updated to handle the additional
mapping tables.
2020-09-15 09:11:06 +01:00
|
|
|
printf("\t.value_length = 0x%x,\n",
|
ss: rx metadata
At the moment you can define and set per-stream metadata at the client,
which will be string-substituted and if configured in the policy, set in
related outgoing protocol specific content like h1 headers.
This patch extends the metadata concept to also check incoming protocol-
specific content like h1 headers and where it matches the binding in the
streamtype's metadata entry, make it available to the client by name, via
a new lws_ss_get_metadata() api.
Currently warmcat.com has additional headers for
server: lwsws (well-known header name)
test-custom-header: hello (custom header name)
minimal-secure-streams test is updated to try to recover these both
in direct and -client (via proxy) versions. The corresponding metadata
part of the "mintest" stream policy from warmcat.com is
{
"srv": "server:"
}, {
"test": "test-custom-header:"
},
If built direct, or at the proxy, the stream has access to the static
policy metadata definitions and can store the rx metadata in the stream
metadata allocation, with heap-allocated a value. For client side that
talks to a proxy, only the proxy knows the policy, and it returns rx
metadata inside the serialized link to the client, which stores it on
the heap attached to the stream.
In addition an optimization for mapping static policy metadata definitions
to individual stream handle metadata is changed to match by name.
2020-09-10 06:43:43 +01:00
|
|
|
(unsigned int)strlen(
|
2020-10-11 07:29:47 +01:00
|
|
|
(const char *)md->value__may_own_heap));
|
ss: policy: response code mapping
This adds a per-streamtype JSON mapping table in the policy.
In addition to the previous flow, it lets you generate custom
SS state notifications for specific http response codes, eg:
"http_resp_map": [ { "530": 1530 }, { "531": 1531 } ],
It's not recommended to overload the transport-layer response
code with application layer responses. It's better to return
a 200 and then in the application protocol inside http, explain
what happened from the application perspective, usually with
JSON. But this is designed to let you handle existing systems
that do overload the transport layer response code.
SS states for user use start at LWSSSCS_USER_BASE, which is
1000.
You can do a basic test with minimal-secure-streams and --respmap
flag, this will go to httpbin.org and get a 404, and the warmcat.com
policy has the mapping for 404 -> LWSSSCS_USER_BASE (1000).
Since the mapping emits states, these are serialized and handled
like any other state in the proxy case.
The policy2c example / tool is also updated to handle the additional
mapping tables.
2020-09-15 09:11:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
printf("\t.length = %d,\n", idx++); // md->length);
|
ss: rx metadata
At the moment you can define and set per-stream metadata at the client,
which will be string-substituted and if configured in the policy, set in
related outgoing protocol specific content like h1 headers.
This patch extends the metadata concept to also check incoming protocol-
specific content like h1 headers and where it matches the binding in the
streamtype's metadata entry, make it available to the client by name, via
a new lws_ss_get_metadata() api.
Currently warmcat.com has additional headers for
server: lwsws (well-known header name)
test-custom-header: hello (custom header name)
minimal-secure-streams test is updated to try to recover these both
in direct and -client (via proxy) versions. The corresponding metadata
part of the "mintest" stream policy from warmcat.com is
{
"srv": "server:"
}, {
"test": "test-custom-header:"
},
If built direct, or at the proxy, the stream has access to the static
policy metadata definitions and can store the rx metadata in the stream
metadata allocation, with heap-allocated a value. For client side that
talks to a proxy, only the proxy knows the policy, and it returns rx
metadata inside the serialized link to the client, which stores it on
the heap attached to the stream.
In addition an optimization for mapping static policy metadata definitions
to individual stream handle metadata is changed to match by name.
2020-09-10 06:43:43 +01:00
|
|
|
printf("\t.value_is_http_token = 0x%x,\n",
|
|
|
|
(unsigned int)md->value_is_http_token);
|
2020-03-26 06:48:34 +00:00
|
|
|
printf("}");
|
|
|
|
if (md->next)
|
|
|
|
printf(",\n");
|
|
|
|
|
|
|
|
lws_strncpy(prev, curr, sizeof(prev));
|
|
|
|
|
|
|
|
md = md->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf(";\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create unique retry policies... have we seen this guy?
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (pol->retry_bo) {
|
|
|
|
a = rbomap;
|
|
|
|
while (a) {
|
|
|
|
if (a->orig == (const char *)pol->retry_bo)
|
|
|
|
break;
|
|
|
|
|
|
|
|
a = a->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!a) {
|
|
|
|
|
|
|
|
/* We haven't seen it before and need to create it */
|
|
|
|
|
|
|
|
a = malloc(sizeof(*a));
|
|
|
|
if (!a)
|
|
|
|
goto bail;
|
|
|
|
a->next = rbomap;
|
2020-12-12 06:21:40 +00:00
|
|
|
a->offset = (unsigned int)unique_rbo++;
|
2020-03-26 06:48:34 +00:00
|
|
|
a->orig = (const char *)pol->retry_bo;
|
|
|
|
rbomap = a;
|
|
|
|
|
|
|
|
printf("static const uint32_t _rbo_bo_%zu[] = {\n",
|
|
|
|
a->offset);
|
|
|
|
for (n = 0; n < pol->retry_bo->retry_ms_table_count; n++)
|
|
|
|
printf(" %u, ", (unsigned int)
|
|
|
|
pol->retry_bo->retry_ms_table[n]);
|
|
|
|
|
|
|
|
est += sizeof(uint32_t) *
|
|
|
|
pol->retry_bo->retry_ms_table_count;
|
|
|
|
|
|
|
|
printf("\n};\nstatic const "
|
|
|
|
"lws_retry_bo_t _rbo_%zu = {\n", a->offset);
|
|
|
|
|
|
|
|
printf("\t.retry_ms_table = _rbo_bo_%zu,\n",
|
|
|
|
a->offset);
|
|
|
|
printf("\t.retry_ms_table_count = %u,\n",
|
|
|
|
pol->retry_bo->retry_ms_table_count);
|
|
|
|
printf("\t.conceal_count = %u,\n",
|
|
|
|
pol->retry_bo->conceal_count);
|
|
|
|
printf("\t.secs_since_valid_ping = %u,\n",
|
|
|
|
pol->retry_bo->secs_since_valid_ping);
|
|
|
|
printf("\t.secs_since_valid_hangup = %u,\n",
|
|
|
|
pol->retry_bo->secs_since_valid_hangup);
|
|
|
|
printf("\t.jitter_percent = %u,\n",
|
|
|
|
pol->retry_bo->jitter_percent);
|
|
|
|
printf("};\n");
|
|
|
|
|
|
|
|
est += sizeof(lws_retry_bo_t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* How about his trust store, it's new to us?
|
|
|
|
*/
|
|
|
|
|
2020-07-27 10:03:12 +01:00
|
|
|
if (pol->trust.store) {
|
2020-03-26 06:48:34 +00:00
|
|
|
a = trustmap;
|
|
|
|
while (a) {
|
2020-07-27 10:03:12 +01:00
|
|
|
if (a->orig == (const char *)pol->trust.store)
|
2020-03-26 06:48:34 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
a = a->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!a) {
|
|
|
|
|
|
|
|
/* it's new to us... */
|
|
|
|
|
|
|
|
a = malloc(sizeof(*a));
|
|
|
|
if (!a)
|
|
|
|
goto bail;
|
|
|
|
a->next = trustmap;
|
|
|
|
a->offset = 0; /* don't care, just track seen */
|
2020-07-27 10:03:12 +01:00
|
|
|
a->orig = (const char *)pol->trust.store;
|
2020-03-26 06:48:34 +00:00
|
|
|
trustmap = a;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Have a look through his x.509 stack...
|
|
|
|
* any that're new to us?
|
|
|
|
*/
|
|
|
|
|
2020-07-27 10:03:12 +01:00
|
|
|
for (n = 0; n < pol->trust.store->count; n++) {
|
|
|
|
if (!pol->trust.store->ssx509[n])
|
2020-03-26 06:48:34 +00:00
|
|
|
continue;
|
|
|
|
a1 = certmap;
|
|
|
|
while (a1) {
|
2020-07-27 10:03:12 +01:00
|
|
|
if (a1->orig == (const char *)pol->trust.store->ssx509[n])
|
2020-03-26 06:48:34 +00:00
|
|
|
break;
|
|
|
|
a1 = a1->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!a1) {
|
|
|
|
/*
|
|
|
|
* This x.509 cert is new to us...
|
|
|
|
* let's capture the DER
|
|
|
|
*/
|
|
|
|
|
|
|
|
a1 = malloc(sizeof(*a1));
|
|
|
|
if (!a1)
|
|
|
|
goto bail;
|
|
|
|
a1->next = certmap;
|
|
|
|
a1->offset = 0; /* don't care, just track seen */
|
2020-07-27 10:03:12 +01:00
|
|
|
a1->orig = (const char *)pol->trust.store->ssx509[n];
|
2020-03-26 06:48:34 +00:00
|
|
|
certmap = a1;
|
|
|
|
|
|
|
|
printf("static const uint8_t _ss_der_%s[] = {\n",
|
2020-07-27 10:03:12 +01:00
|
|
|
purify_csymbol(pol->trust.store->ssx509[n]->vhost_name,
|
2020-03-26 06:48:34 +00:00
|
|
|
buf, sizeof(buf)));
|
|
|
|
|
2020-07-27 10:03:12 +01:00
|
|
|
for (m = 0; m < (int)pol->trust.store->ssx509[n]->ca_der_len; m++) {
|
2020-03-26 06:48:34 +00:00
|
|
|
if ((m & 7) == 0)
|
|
|
|
printf("\t/* 0x%3x */ ", m);
|
|
|
|
|
2020-07-27 10:03:12 +01:00
|
|
|
printf("0x%02X, ", pol->trust.store->ssx509[n]->ca_der[m]);
|
2020-03-26 06:48:34 +00:00
|
|
|
if ((m & 7) == 7)
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\n};\nstatic const lws_ss_x509_t _ss_x509_%s = {\n",
|
2020-07-27 10:03:12 +01:00
|
|
|
purify_csymbol(pol->trust.store->ssx509[n]->vhost_name,
|
2020-03-26 06:48:34 +00:00
|
|
|
buf, sizeof(buf)));
|
2020-07-27 10:03:12 +01:00
|
|
|
printf("\t.vhost_name = \"%s\",\n", pol->trust.store->ssx509[n]->vhost_name);
|
2020-03-26 06:48:34 +00:00
|
|
|
printf("\t.ca_der = _ss_der_%s,\n",
|
2020-07-27 10:03:12 +01:00
|
|
|
purify_csymbol(pol->trust.store->ssx509[n]->vhost_name,
|
2020-03-26 06:48:34 +00:00
|
|
|
buf, sizeof(buf)));
|
2020-07-27 10:03:12 +01:00
|
|
|
printf("\t.ca_der_len = %zu,\n", pol->trust.store->ssx509[n]->ca_der_len);
|
2020-03-26 06:48:34 +00:00
|
|
|
printf("};\n");
|
|
|
|
|
2020-07-27 10:03:12 +01:00
|
|
|
est += sizeof(lws_ss_x509_t) + pol->trust.store->ssx509[n]->ca_der_len;
|
2020-03-26 06:48:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
printf("static const lws_ss_trust_store_t _ss_ts_%s = {\n",
|
2020-07-27 10:03:12 +01:00
|
|
|
purify_csymbol(pol->trust.store->name,
|
2020-03-26 06:48:34 +00:00
|
|
|
buf, sizeof(buf)));
|
|
|
|
|
2020-07-27 10:03:12 +01:00
|
|
|
printf("\t.name = \"%s\",\n", pol->trust.store->name);
|
2020-03-26 06:48:34 +00:00
|
|
|
printf("\t.ssx509 = {\n");
|
|
|
|
|
2020-07-27 10:03:12 +01:00
|
|
|
for (n = pol->trust.store->count - 1; n >= 0 ; n--)
|
2020-03-26 06:48:34 +00:00
|
|
|
printf("\t\t&_ss_x509_%s,\n",
|
2020-07-27 10:03:12 +01:00
|
|
|
pol->trust.store->ssx509[n]->vhost_name);
|
2020-03-26 06:48:34 +00:00
|
|
|
|
|
|
|
printf("\t}\n};\n");
|
|
|
|
|
|
|
|
est += sizeof(lws_ss_trust_store_t);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pol = pol->next;
|
|
|
|
}
|
|
|
|
|
2020-08-19 11:21:58 +01:00
|
|
|
|
ss: policy: response code mapping
This adds a per-streamtype JSON mapping table in the policy.
In addition to the previous flow, it lets you generate custom
SS state notifications for specific http response codes, eg:
"http_resp_map": [ { "530": 1530 }, { "531": 1531 } ],
It's not recommended to overload the transport-layer response
code with application layer responses. It's better to return
a 200 and then in the application protocol inside http, explain
what happened from the application perspective, usually with
JSON. But this is designed to let you handle existing systems
that do overload the transport layer response code.
SS states for user use start at LWSSSCS_USER_BASE, which is
1000.
You can do a basic test with minimal-secure-streams and --respmap
flag, this will go to httpbin.org and get a 404, and the warmcat.com
policy has the mapping for 404 -> LWSSSCS_USER_BASE (1000).
Since the mapping emits states, these are serialized and handled
like any other state in the proxy case.
The policy2c example / tool is also updated to handle the additional
mapping tables.
2020-09-15 09:11:06 +01:00
|
|
|
/* dump any streamtype's http resp map */
|
|
|
|
|
|
|
|
pol = lws_ss_policy_get(context);
|
|
|
|
m = 0;
|
|
|
|
|
|
|
|
while (pol) {
|
|
|
|
|
|
|
|
lws_snprintf(curr, sizeof(curr), "_ssp_%s",
|
|
|
|
purify_csymbol(pol->streamtype, buf, sizeof(buf)));
|
|
|
|
|
|
|
|
/* if relevant, dump http resp map */
|
|
|
|
|
|
|
|
switch (pol->protocol) {
|
|
|
|
case LWSSSP_H1:
|
|
|
|
case LWSSSP_H2:
|
|
|
|
case LWSSSP_WS:
|
|
|
|
|
|
|
|
if (!pol->u.http.count_respmap)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!m)
|
|
|
|
printf("\nstatic const lws_ss_http_respmap_t ");
|
|
|
|
else
|
|
|
|
printf(",\n");
|
|
|
|
m++;
|
|
|
|
|
|
|
|
printf("%s_http_respmap[] = {\n", curr);
|
|
|
|
for (n = 0; n < pol->u.http.count_respmap; n++) {
|
|
|
|
printf("\t{ %d, 0x%x },\n",
|
|
|
|
pol->u.http.respmap[n].resp,
|
|
|
|
pol->u.http.respmap[n].state);
|
|
|
|
|
|
|
|
est += sizeof(lws_ss_http_respmap_t);
|
|
|
|
}
|
|
|
|
printf("}");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pol = pol->next;
|
|
|
|
}
|
2020-03-26 06:48:34 +00:00
|
|
|
|
ss: policy: response code mapping
This adds a per-streamtype JSON mapping table in the policy.
In addition to the previous flow, it lets you generate custom
SS state notifications for specific http response codes, eg:
"http_resp_map": [ { "530": 1530 }, { "531": 1531 } ],
It's not recommended to overload the transport-layer response
code with application layer responses. It's better to return
a 200 and then in the application protocol inside http, explain
what happened from the application perspective, usually with
JSON. But this is designed to let you handle existing systems
that do overload the transport layer response code.
SS states for user use start at LWSSSCS_USER_BASE, which is
1000.
You can do a basic test with minimal-secure-streams and --respmap
flag, this will go to httpbin.org and get a 404, and the warmcat.com
policy has the mapping for 404 -> LWSSSCS_USER_BASE (1000).
Since the mapping emits states, these are serialized and handled
like any other state in the proxy case.
The policy2c example / tool is also updated to handle the additional
mapping tables.
2020-09-15 09:11:06 +01:00
|
|
|
if (m)
|
|
|
|
printf(";\n");
|
2020-03-26 06:48:34 +00:00
|
|
|
|
2020-08-19 11:21:58 +01:00
|
|
|
/*
|
|
|
|
* The auth map
|
|
|
|
*/
|
|
|
|
|
|
|
|
auth = lws_ss_auth_get(context);
|
|
|
|
if (auth)
|
|
|
|
printf("\nstatic const lws_ss_auth_t ");
|
|
|
|
prev[0] = '\0';
|
|
|
|
|
|
|
|
while (auth) {
|
|
|
|
lws_snprintf(curr, sizeof(curr), "_ssau_%s",
|
|
|
|
purify_csymbol(auth->name, buf, sizeof(buf)));
|
|
|
|
|
|
|
|
printf("%s = {\n", curr);
|
|
|
|
if (prev[0])
|
|
|
|
printf("\t.next = (void *)&%s,\n", prev);
|
|
|
|
|
|
|
|
printf("\t.name = \"%s\",\n", auth->name);
|
|
|
|
printf("\t.streamtype = \"%s\",\n", auth->streamtype);
|
|
|
|
printf("\t.blob = %d,\n", auth->blob_index);
|
|
|
|
printf("}");
|
|
|
|
if (auth->next)
|
|
|
|
printf(",");
|
|
|
|
else
|
|
|
|
printf(";");
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
lws_strncpy(prev, curr, sizeof(prev));
|
|
|
|
|
|
|
|
auth = auth->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lws_ss_auth_get(context))
|
|
|
|
printf("\n");
|
|
|
|
|
2020-03-26 06:48:34 +00:00
|
|
|
/*
|
|
|
|
* The streamtypes
|
|
|
|
*/
|
|
|
|
|
|
|
|
pol = lws_ss_policy_get(context);
|
|
|
|
|
|
|
|
printf("\nstatic const lws_ss_policy_t ");
|
|
|
|
prev[0] = '\0';
|
|
|
|
|
|
|
|
while (pol) {
|
|
|
|
|
|
|
|
est += sizeof(*pol);
|
|
|
|
|
|
|
|
lws_snprintf(curr, sizeof(curr), "_ssp_%s",
|
|
|
|
purify_csymbol(pol->streamtype, buf, sizeof(buf)));
|
|
|
|
|
ss: policy: response code mapping
This adds a per-streamtype JSON mapping table in the policy.
In addition to the previous flow, it lets you generate custom
SS state notifications for specific http response codes, eg:
"http_resp_map": [ { "530": 1530 }, { "531": 1531 } ],
It's not recommended to overload the transport-layer response
code with application layer responses. It's better to return
a 200 and then in the application protocol inside http, explain
what happened from the application perspective, usually with
JSON. But this is designed to let you handle existing systems
that do overload the transport layer response code.
SS states for user use start at LWSSSCS_USER_BASE, which is
1000.
You can do a basic test with minimal-secure-streams and --respmap
flag, this will go to httpbin.org and get a 404, and the warmcat.com
policy has the mapping for 404 -> LWSSSCS_USER_BASE (1000).
Since the mapping emits states, these are serialized and handled
like any other state in the proxy case.
The policy2c example / tool is also updated to handle the additional
mapping tables.
2020-09-15 09:11:06 +01:00
|
|
|
printf("%s = {\n", curr);
|
2020-03-26 06:48:34 +00:00
|
|
|
|
|
|
|
if (prev[0])
|
|
|
|
printf("\t.next = (void *)&%s,\n", prev);
|
|
|
|
|
|
|
|
printf("\t.streamtype = \"%s\",\n", pol->streamtype);
|
|
|
|
if (pol->endpoint)
|
|
|
|
printf("\t.endpoint = \"%s\",\n", pol->endpoint);
|
|
|
|
if (pol->rideshare_streamtype)
|
|
|
|
printf("\t.rideshare_streamtype = \"%s\",\n",
|
|
|
|
pol->rideshare_streamtype);
|
|
|
|
if (pol->payload_fmt)
|
|
|
|
printf("\t.payload_fmt = \"%s\",\n",
|
|
|
|
pol->payload_fmt);
|
|
|
|
if (pol->socks5_proxy)
|
|
|
|
printf("\t.socks5_proxy = \"%s\",\n",
|
|
|
|
pol->socks5_proxy);
|
|
|
|
|
2020-08-19 11:21:58 +01:00
|
|
|
if (pol->auth)
|
|
|
|
printf("\t.auth = &_ssau_%s,\n",
|
|
|
|
purify_csymbol(pol->auth->name, buf, sizeof(buf)));
|
|
|
|
|
2020-03-26 06:48:34 +00:00
|
|
|
{
|
|
|
|
lws_ss_metadata_t *nv = pol->metadata, *last = NULL;
|
|
|
|
|
|
|
|
while (nv) {
|
|
|
|
last = nv;
|
|
|
|
nv = nv->next;
|
|
|
|
}
|
|
|
|
if (pol->metadata)
|
|
|
|
printf("\t.metadata = (void *)&_md_%s_%s,\n",
|
|
|
|
purify_csymbol(pol->streamtype, buf, sizeof(buf)),
|
|
|
|
purify_csymbol(last->name, buf1, sizeof(buf1)));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (pol->protocol) {
|
|
|
|
case LWSSSP_H1:
|
|
|
|
case LWSSSP_H2:
|
|
|
|
case LWSSSP_WS:
|
|
|
|
|
|
|
|
printf("\t.u = {\n\t\t.http = {\n");
|
|
|
|
|
|
|
|
if (pol->u.http.method)
|
|
|
|
printf("\t\t\t.method = \"%s\",\n",
|
|
|
|
pol->u.http.method);
|
|
|
|
if (pol->u.http.url)
|
|
|
|
printf("\t\t\t.url = \"%s\",\n",
|
|
|
|
pol->u.http.url);
|
|
|
|
if (pol->u.http.multipart_name)
|
|
|
|
printf("\t\t\t.multipart_name = \"%s\",\n",
|
|
|
|
pol->u.http.multipart_name);
|
|
|
|
if (pol->u.http.multipart_filename)
|
|
|
|
printf("\t\t\t.multipart_filename = \"%s\",\n",
|
|
|
|
pol->u.http.multipart_filename);
|
|
|
|
if (pol->u.http.multipart_content_type)
|
|
|
|
printf("\t\t\t.multipart_content_type = \"%s\",\n",
|
|
|
|
pol->u.http.multipart_content_type);
|
|
|
|
if (pol->u.http.auth_preamble)
|
|
|
|
printf("\t\t\t.auth_preamble = \"%s\",\n",
|
|
|
|
pol->u.http.auth_preamble);
|
|
|
|
|
ss: policy: response code mapping
This adds a per-streamtype JSON mapping table in the policy.
In addition to the previous flow, it lets you generate custom
SS state notifications for specific http response codes, eg:
"http_resp_map": [ { "530": 1530 }, { "531": 1531 } ],
It's not recommended to overload the transport-layer response
code with application layer responses. It's better to return
a 200 and then in the application protocol inside http, explain
what happened from the application perspective, usually with
JSON. But this is designed to let you handle existing systems
that do overload the transport layer response code.
SS states for user use start at LWSSSCS_USER_BASE, which is
1000.
You can do a basic test with minimal-secure-streams and --respmap
flag, this will go to httpbin.org and get a 404, and the warmcat.com
policy has the mapping for 404 -> LWSSSCS_USER_BASE (1000).
Since the mapping emits states, these are serialized and handled
like any other state in the proxy case.
The policy2c example / tool is also updated to handle the additional
mapping tables.
2020-09-15 09:11:06 +01:00
|
|
|
if (pol->u.http.respmap) {
|
|
|
|
printf("\t\t\t.respmap = &%s_http_respmap,\n",
|
|
|
|
curr);
|
|
|
|
printf("\t\t\t.count_respmap = %d,\n",
|
|
|
|
pol->u.http.count_respmap);
|
|
|
|
}
|
|
|
|
|
2020-03-26 06:48:34 +00:00
|
|
|
if (pol->u.http.blob_header[0]) {
|
|
|
|
printf("\t\t\t.blob_header = {\n");
|
|
|
|
for (n = 0; n < (int)LWS_ARRAY_SIZE(pol->u.http.blob_header); n++)
|
|
|
|
if (pol->u.http.blob_header[n])
|
|
|
|
printf("\t\t\t\t\"%s\",\n",
|
|
|
|
pol->u.http.blob_header[n]);
|
|
|
|
|
|
|
|
printf("\t\t\t},\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pol->protocol == LWSSSP_WS) {
|
|
|
|
printf("\t\t\t.u = {\n\t\t\t\t.ws = {\n");
|
|
|
|
if (pol->u.http.u.ws.subprotocol)
|
|
|
|
printf("\t\t\t\t\t.subprotocol = \"%s\",\n",
|
|
|
|
pol->u.http.u.ws.subprotocol);
|
|
|
|
printf("\t\t\t\t\t.binary = %u\n", pol->u.http.u.ws.binary);
|
|
|
|
printf("\t\t\t\t}\n\t\t\t},\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pol->u.http.resp_expect)
|
|
|
|
printf("\t\t\t.resp_expect = %u,\n", pol->u.http.resp_expect);
|
|
|
|
if (pol->u.http.fail_redirect)
|
|
|
|
printf("\t\t\t.fail_redirect = %u,\n", pol->u.http.fail_redirect);
|
|
|
|
|
|
|
|
printf("\t\t}\n\t},\n");
|
|
|
|
|
|
|
|
break;
|
|
|
|
case LWSSSP_MQTT:
|
|
|
|
|
|
|
|
printf("\t.u = {\n\t\t.mqtt = {\n");
|
|
|
|
|
|
|
|
if (pol->u.mqtt.topic)
|
|
|
|
printf("\t\t\t.topic = \"%s\",\n",
|
|
|
|
pol->u.mqtt.topic);
|
|
|
|
if (pol->u.mqtt.subscribe)
|
|
|
|
printf("\t\t\t.subscribe = \"%s\",\n",
|
|
|
|
pol->u.mqtt.subscribe);
|
|
|
|
if (pol->u.mqtt.will_topic)
|
|
|
|
printf("\t\t\t.will_topic = \"%s\",\n",
|
|
|
|
pol->u.mqtt.will_topic);
|
|
|
|
if (pol->u.mqtt.will_message)
|
|
|
|
printf("\t\t\t.will_message = \"%s\",\n",
|
|
|
|
pol->u.mqtt.will_message);
|
|
|
|
|
|
|
|
if (pol->u.mqtt.keep_alive)
|
|
|
|
printf("\t\t\t.keep_alive = %u,\n",
|
|
|
|
pol->u.mqtt.keep_alive);
|
|
|
|
if (pol->u.mqtt.qos)
|
|
|
|
printf("\t\t\t.qos = %u,\n",
|
|
|
|
pol->u.mqtt.qos);
|
|
|
|
if (pol->u.mqtt.clean_start)
|
|
|
|
printf("\t\t\t.clean_start = %u,\n",
|
|
|
|
pol->u.mqtt.clean_start);
|
|
|
|
if (pol->u.mqtt.will_qos)
|
|
|
|
printf("\t\t\t.will_qos = %u,\n",
|
|
|
|
pol->u.mqtt.will_qos);
|
|
|
|
if (pol->u.mqtt.will_retain)
|
|
|
|
printf("\t\t\t.will_retain = %u,\n",
|
|
|
|
pol->u.mqtt.will_retain);
|
|
|
|
|
|
|
|
printf("\t\t}\n\t},\n");
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
lwsl_err("%s: unknown ss protocol index %d\n", __func__,
|
|
|
|
pol->protocol);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
const lws_ss_trust_store_t *trust_store; /**< CA certs needed for conn
|
|
|
|
validation, only set between policy parsing and vhost creation */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (pol->retry_bo) {
|
|
|
|
a = rbomap;
|
|
|
|
while (a) {
|
|
|
|
if (a->orig == (const char *)pol->retry_bo)
|
|
|
|
break;
|
|
|
|
|
|
|
|
a = a->next;
|
|
|
|
}
|
|
|
|
if (!a)
|
|
|
|
goto bail;
|
|
|
|
|
|
|
|
printf("\t.retry_bo = &_rbo_%zu,\n", a->offset);
|
|
|
|
}
|
|
|
|
|
2020-06-30 16:42:37 +01:00
|
|
|
if (pol->timeout_ms)
|
|
|
|
printf("\t.timeout_ms = %u,\n", pol->timeout_ms);
|
2020-03-26 06:48:34 +00:00
|
|
|
if (pol->flags)
|
|
|
|
printf("\t.flags = 0x%x,\n", pol->flags);
|
2021-02-04 09:08:17 +00:00
|
|
|
if (pol->flags)
|
|
|
|
printf("\t.priority = 0x%x,\n", (unsigned int)pol->priority);
|
2020-03-26 06:48:34 +00:00
|
|
|
if (pol->port)
|
|
|
|
printf("\t.port = %u,\n", pol->port);
|
|
|
|
if (pol->metadata_count)
|
|
|
|
printf("\t.metadata_count = %u,\n", pol->metadata_count);
|
|
|
|
printf("\t.protocol = %u,\n", pol->protocol);
|
|
|
|
if (pol->client_cert)
|
|
|
|
printf("\t.client_cert = %u,\n", pol->client_cert);
|
|
|
|
|
2020-07-27 10:03:12 +01:00
|
|
|
if (pol->trust.store)
|
|
|
|
printf("\t.trust = {.store = &_ss_ts_%s},\n",
|
|
|
|
purify_csymbol(pol->trust.store->name,
|
|
|
|
buf, sizeof(buf)));
|
2020-03-26 06:48:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
printf("}");
|
|
|
|
if (pol->next)
|
|
|
|
printf(",\n");
|
|
|
|
|
|
|
|
lws_strncpy(prev, curr, sizeof(prev));
|
|
|
|
|
|
|
|
lastpol = pol;
|
|
|
|
|
|
|
|
pol = pol->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf(";\n");
|
|
|
|
if (lastpol)
|
|
|
|
printf("#define _ss_static_policy_entry _ssp_%s\n",
|
|
|
|
purify_csymbol(lastpol->streamtype, buf, sizeof(buf)));
|
|
|
|
|
|
|
|
est += last_offset;
|
|
|
|
|
|
|
|
printf("/* estimated footprint %zu (when sizeof void * = %zu) */\n",
|
|
|
|
est, sizeof(void *));
|
|
|
|
|
|
|
|
lws_ss_policy_parse_abandon(context);
|
|
|
|
bad = 0;
|
|
|
|
|
|
|
|
bail:
|
|
|
|
|
|
|
|
|
|
|
|
lws_context_destroy(context);
|
|
|
|
|
|
|
|
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
|
|
|
|
|
|
|
|
return bad;
|
|
|
|
}
|