mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-16 00:00:07 +01:00
573 lines
14 KiB
C
573 lines
14 KiB
C
/*
|
|
* 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>
|
|
#include <stdio.h>
|
|
#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;
|
|
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 {
|
|
int m, n = read(0, buf, sizeof(buf));
|
|
|
|
if (n < 1)
|
|
break;
|
|
|
|
m = lws_ss_policy_parse(context, (uint8_t *)buf, (size_t)n);
|
|
|
|
printf("%.*s", n, buf);
|
|
json_size += n;
|
|
|
|
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);
|
|
lwsl_notice("%s: pol %p\n", __func__, pol);
|
|
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);
|
|
if (md->value)
|
|
printf("\t.value = (void *)\"%s\",\n", (const char *)md->value);
|
|
|
|
printf("\t.length = %d,\n", idx++); // md->length);
|
|
|
|
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;
|
|
a->offset = unique_rbo++;
|
|
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?
|
|
*/
|
|
|
|
if (pol->trust_store) {
|
|
a = trustmap;
|
|
while (a) {
|
|
if (a->orig == (const char *)pol->trust_store)
|
|
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 */
|
|
a->orig = (const char *)pol->trust_store;
|
|
trustmap = a;
|
|
|
|
/*
|
|
* Have a look through his x.509 stack...
|
|
* any that're new to us?
|
|
*/
|
|
|
|
for (n = 0; n < pol->trust_store->count; n++) {
|
|
if (!pol->trust_store->ssx509[n])
|
|
continue;
|
|
a1 = certmap;
|
|
while (a1) {
|
|
if (a1->orig == (const char *)pol->trust_store->ssx509[n])
|
|
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 */
|
|
a1->orig = (const char *)pol->trust_store->ssx509[n];
|
|
certmap = a1;
|
|
|
|
printf("static const uint8_t _ss_der_%s[] = {\n",
|
|
purify_csymbol(pol->trust_store->ssx509[n]->vhost_name,
|
|
buf, sizeof(buf)));
|
|
|
|
for (m = 0; m < (int)pol->trust_store->ssx509[n]->ca_der_len; m++) {
|
|
if ((m & 7) == 0)
|
|
printf("\t/* 0x%3x */ ", m);
|
|
|
|
printf("0x%02X, ", pol->trust_store->ssx509[n]->ca_der[m]);
|
|
if ((m & 7) == 7)
|
|
printf("\n");
|
|
}
|
|
|
|
printf("\n};\nstatic const lws_ss_x509_t _ss_x509_%s = {\n",
|
|
purify_csymbol(pol->trust_store->ssx509[n]->vhost_name,
|
|
buf, sizeof(buf)));
|
|
printf("\t.vhost_name = \"%s\",\n", pol->trust_store->ssx509[n]->vhost_name);
|
|
printf("\t.ca_der = _ss_der_%s,\n",
|
|
purify_csymbol(pol->trust_store->ssx509[n]->vhost_name,
|
|
buf, sizeof(buf)));
|
|
printf("\t.ca_der_len = %zu,\n", pol->trust_store->ssx509[n]->ca_der_len);
|
|
printf("};\n");
|
|
|
|
est += sizeof(lws_ss_x509_t) + pol->trust_store->ssx509[n]->ca_der_len;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
printf("static const lws_ss_trust_store_t _ss_ts_%s = {\n",
|
|
purify_csymbol(pol->trust_store->name,
|
|
buf, sizeof(buf)));
|
|
|
|
printf("\t.name = \"%s\",\n", pol->trust_store->name);
|
|
printf("\t.ssx509 = {\n");
|
|
|
|
for (n = pol->trust_store->count - 1; n >= 0 ; n--)
|
|
printf("\t\t&_ss_x509_%s,\n",
|
|
pol->trust_store->ssx509[n]->vhost_name);
|
|
|
|
printf("\t}\n};\n");
|
|
|
|
est += sizeof(lws_ss_trust_store_t);
|
|
|
|
}
|
|
}
|
|
|
|
pol = pol->next;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* The streamtypes
|
|
*/
|
|
|
|
pol = lws_ss_policy_get(context);
|
|
lwsl_notice("%s: pol %p\n", __func__, pol);
|
|
|
|
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)));
|
|
printf("%s = {\n", curr);
|
|
|
|
|
|
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);
|
|
|
|
{
|
|
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);
|
|
|
|
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);
|
|
}
|
|
|
|
if (pol->timeout_ms)
|
|
printf("\t.timeout_ms = %u,\n", pol->timeout_ms);
|
|
if (pol->flags)
|
|
printf("\t.flags = 0x%x,\n", pol->flags);
|
|
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);
|
|
|
|
if (pol->trust_store)
|
|
printf("\t.trust_store = &_ss_ts_%s,\n",
|
|
purify_csymbol(pol->trust_store->name, buf, sizeof(buf)));
|
|
|
|
|
|
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;
|
|
}
|