aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui/syntax/highlighter.c
diff options
context:
space:
mode:
Diffstat (limited to 'ui/syntax/highlighter.c')
-rw-r--r--ui/syntax/highlighter.c641
1 files changed, 0 insertions, 641 deletions
diff --git a/ui/syntax/highlighter.c b/ui/syntax/highlighter.c
deleted file mode 100644
index d89feda1..00000000
--- a/ui/syntax/highlighter.c
+++ /dev/null
@@ -1,641 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
- */
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include "highlighter.h"
-
-typedef struct {
- const char *s;
- size_t len;
-} string_span_t;
-
-static bool is_decimal(char c)
-{
- return c >= '0' && c <= '9';
-}
-
-static bool is_hexadecimal(char c)
-{
- return is_decimal(c) || ((c | 32) >= 'a' && (c | 32) <= 'f');
-}
-
-static bool is_alphabet(char c)
-{
- return (c | 32) >= 'a' && (c | 32) <= 'z';
-}
-
-static bool is_same(string_span_t s, const char *c)
-{
- size_t len = strlen(c);
-
- if (len != s.len)
- return false;
- return !memcmp(s.s, c, len);
-}
-
-static bool is_caseless_same(string_span_t s, const char *c)
-{
- size_t len = strlen(c);
-
- if (len != s.len)
- return false;
- for (size_t i = 0; i < len; ++i) {
- char a = c[i], b = s.s[i];
- if ((unsigned)a - 'a' < 26)
- a &= 95;
- if ((unsigned)b - 'a' < 26)
- b &= 95;
- if (a != b)
- return false;
- }
- return true;
-}
-
-static bool is_valid_key(string_span_t s)
-{
- if (s.len != 44 || s.s[43] != '=')
- return false;
-
- for (size_t i = 0; i < 42; ++i) {
- if (!is_decimal(s.s[i]) && !is_alphabet(s.s[i]) &&
- s.s[i] != '/' && s.s[i] != '+')
- return false;
- }
- switch (s.s[42]) {
- case 'A':
- case 'E':
- case 'I':
- case 'M':
- case 'Q':
- case 'U':
- case 'Y':
- case 'c':
- case 'g':
- case 'k':
- case 'o':
- case 's':
- case 'w':
- case '4':
- case '8':
- case '0':
- break;
- default:
- return false;
- }
- return true;
-}
-
-static bool is_valid_hostname(string_span_t s)
-{
- size_t num_digit = 0, num_entity = s.len;
-
- if (s.len > 63 || !s.len)
- return false;
- if (s.s[0] == '-' || s.s[s.len - 1] == '-')
- return false;
- if (s.s[0] == '.' || s.s[s.len - 1] == '.')
- return false;
-
- for (size_t i = 0; i < s.len; ++i) {
- if (is_decimal(s.s[i])) {
- ++num_digit;
- continue;
- }
- if (s.s[i] == '.') {
- --num_entity;
- continue;
- }
-
- if (!is_alphabet(s.s[i]) && s.s[i] != '-')
- return false;
-
- if (i && s.s[i] == '.' && s.s[i - 1] == '.')
- return false;
- }
- return num_digit != num_entity;
-}
-
-static bool is_valid_ipv4(string_span_t s)
-{
- for (size_t j, i = 0, pos = 0; i < 4 && pos < s.len; ++i) {
- uint32_t val = 0;
-
- for (j = 0; j < 3 && pos + j < s.len && is_decimal(s.s[pos + j]); ++j)
- val = 10 * val + s.s[pos + j] - '0';
- if (j == 0 || (j > 1 && s.s[pos] == '0') || val > 255)
- return false;
- if (pos + j == s.len && i == 3)
- return true;
- if (s.s[pos + j] != '.')
- return false;
- pos += j + 1;
- }
- return false;
-}
-
-static bool is_valid_ipv6(string_span_t s)
-{
- size_t pos = 0;
- bool seen_colon = false;
-
- if (s.len < 2)
- return false;
- if (s.s[pos] == ':' && s.s[++pos] != ':')
- return false;
- if (s.s[s.len - 1] == ':' && s.s[s.len - 2] != ':')
- return false;
-
- for (size_t j, i = 0; pos < s.len; ++i) {
- if (s.s[pos] == ':' && !seen_colon) {
- seen_colon = true;
- if (++pos == s.len)
- break;
- if (i == 7)
- return false;
- continue;
- }
- for (j = 0; j < 4 && pos + j < s.len && is_hexadecimal(s.s[pos + j]); ++j);
- if (j == 0)
- return false;
- if (pos + j == s.len && (seen_colon || i == 7))
- break;
- if (i == 7)
- return false;
- if (s.s[pos + j] != ':') {
- if (s.s[pos + j] != '.' || (i < 6 && !seen_colon))
- return false;
- return is_valid_ipv4((string_span_t){ s.s + pos, s.len - pos });
- }
- pos += j + 1;
- }
- return true;
-}
-
-static bool is_valid_uint(string_span_t s, bool support_hex, uint64_t min, uint64_t max)
-{
- uint64_t val = 0;
-
- /* Bound this around 32 bits, so that we don't have to write overflow logic. */
- if (s.len > 10 || !s.len)
- return false;
-
- if (support_hex && s.len > 2 && s.s[0] == '0' && s.s[1] == 'x') {
- for (size_t i = 2; i < s.len; ++i) {
- if ((unsigned)s.s[i] - '0' < 10)
- val = 16 * val + (s.s[i] - '0');
- else if (((unsigned)s.s[i] | 32) - 'a' < 6)
- val = 16 * val + (s.s[i] | 32) - 'a' + 10;
- else
- return false;
- }
- } else {
- for (size_t i = 0; i < s.len; ++i) {
- if (!is_decimal(s.s[i]))
- return false;
- val = 10 * val + s.s[i] - '0';
- }
- }
- return val <= max && val >= min;
-}
-
-static bool is_valid_port(string_span_t s)
-{
- return is_valid_uint(s, false, 0, 65535);
-}
-
-static bool is_valid_mtu(string_span_t s)
-{
- return is_valid_uint(s, false, 576, 65535);
-}
-
-static bool is_valid_persistentkeepalive(string_span_t s)
-{
- if (is_same(s, "off"))
- return true;
- return is_valid_uint(s, false, 0, 65535);
-}
-
-#ifndef MOBILE_WGQUICK_SUBSET
-
-static bool is_valid_fwmark(string_span_t s)
-{
- if (is_same(s, "off"))
- return true;
- return is_valid_uint(s, true, 0, 4294967295);
-}
-
-static bool is_valid_table(string_span_t s)
-{
- if (is_same(s, "auto"))
- return true;
- if (is_same(s, "off"))
- return true;
- /* This pretty much invalidates the other checks, but rt_names.c's
- * fread_id_name does no validation aside from this. */
- if (s.len < 512)
- return true;
- return is_valid_uint(s, false, 0, 4294967295);
-}
-
-static bool is_valid_saveconfig(string_span_t s)
-{
- return is_same(s, "true") || is_same(s, "false");
-}
-
-static bool is_valid_prepostupdown(string_span_t s)
-{
- /* It's probably not worthwhile to try to validate a bash expression.
- * So instead we just demand non-zero length. */
- return s.len;
-}
-#endif
-
-static bool is_valid_scope(string_span_t s)
-{
- if (s.len > 64 || !s.len)
- return false;
- for (size_t i = 0; i < s.len; ++i) {
- if (!is_alphabet(s.s[i]) && !is_decimal(s.s[i]) &&
- s.s[i] != '_' && s.s[i] != '=' && s.s[i] != '+' &&
- s.s[i] != '.' && s.s[i] != '-')
- return false;
- }
- return true;
-}
-
-static bool is_valid_endpoint(string_span_t s)
-{
-
- if (!s.len)
- return false;
-
- if (s.s[0] == '[') {
- bool seen_scope = false;
- string_span_t hostspan = { s.s + 1, 0 };
-
- for (size_t i = 1; i < s.len; ++i) {
- if (s.s[i] == '%') {
- if (seen_scope)
- return false;
- seen_scope = true;
- if (!is_valid_ipv6(hostspan))
- return false;
- hostspan = (string_span_t){ s.s + i + 1, 0 };
- } else if (s.s[i] == ']') {
- if (seen_scope) {
- if (!is_valid_scope(hostspan))
- return false;
- } else if (!is_valid_ipv6(hostspan)) {
- return false;
- }
- if (i == s.len - 1 || s.s[i + 1] != ':')
- return false;
- return is_valid_port((string_span_t){ s.s + i + 2, s.len - i - 2 });
- } else {
- ++hostspan.len;
- }
- }
- return false;
- }
- for (size_t i = 0; i < s.len; ++i) {
- if (s.s[i] == ':') {
- string_span_t host = { s.s, i }, port = { s.s + i + 1, s.len - i - 1};
- return is_valid_port(port) && (is_valid_ipv4(host) || is_valid_hostname(host));
- }
- }
- return false;
-}
-
-static bool is_valid_network(string_span_t s)
-{
- for (size_t i = 0; i < s.len; ++i) {
- if (s.s[i] == '/') {
- string_span_t ip = { s.s, i }, cidr = { s.s + i + 1, s.len - i - 1};
- uint16_t cidrval = 0;
-
- if (cidr.len > 3 || !cidr.len)
- return false;
-
- for (size_t j = 0; j < cidr.len; ++j) {
- if (!is_decimal(cidr.s[j]))
- return false;
- cidrval = 10 * cidrval + cidr.s[j] - '0';
- }
- if (is_valid_ipv4(ip))
- return cidrval <= 32;
- else if (is_valid_ipv6(ip))
- return cidrval <= 128;
- return false;
- }
- }
- return is_valid_ipv4(s) || is_valid_ipv6(s);
-}
-
-enum field {
- InterfaceSection,
- PrivateKey,
- ListenPort,
- Address,
- DNS,
- MTU,
-#ifndef MOBILE_WGQUICK_SUBSET
- FwMark,
- Table,
- PreUp, PostUp, PreDown, PostDown,
- SaveConfig,
-#endif
-
- PeerSection,
- PublicKey,
- PresharedKey,
- AllowedIPs,
- Endpoint,
- PersistentKeepalive,
-
- Invalid
-};
-
-static enum field section_for_field(enum field t)
-{
- if (t > InterfaceSection && t < PeerSection)
- return InterfaceSection;
- if (t > PeerSection && t < Invalid)
- return PeerSection;
- return Invalid;
-}
-
-static enum field get_field(string_span_t s)
-{
-#define check_enum(t) do { if (is_caseless_same(s, #t)) return t; } while (0)
- check_enum(PrivateKey);
- check_enum(ListenPort);
- check_enum(Address);
- check_enum(DNS);
- check_enum(MTU);
- check_enum(PublicKey);
- check_enum(PresharedKey);
- check_enum(AllowedIPs);
- check_enum(Endpoint);
- check_enum(PersistentKeepalive);
-#ifndef MOBILE_WGQUICK_SUBSET
- check_enum(FwMark);
- check_enum(Table);
- check_enum(PreUp);
- check_enum(PostUp);
- check_enum(PreDown);
- check_enum(PostDown);
- check_enum(SaveConfig);
-#endif
- return Invalid;
-#undef check_enum
-}
-
-static enum field get_sectiontype(string_span_t s)
-{
- if (is_caseless_same(s, "[Peer]"))
- return PeerSection;
- if (is_caseless_same(s, "[Interface]"))
- return InterfaceSection;
- return Invalid;
-}
-
-struct highlight_span_array {
- size_t len, capacity;
- struct highlight_span *spans;
-};
-
-/* A useful OpenBSD-ism. */
-static void *realloc_array(void *optr, size_t nmemb, size_t size)
-{
- if ((nmemb >= (size_t)1 << (sizeof(size_t) * 4) ||
- size >= (size_t)1 << (sizeof(size_t) * 4)) &&
- nmemb > 0 && SIZE_MAX / nmemb < size) {
- errno = ENOMEM;
- return NULL;
- }
- return realloc(optr, size * nmemb);
-}
-
-static bool append_highlight_span(struct highlight_span_array *a, const char *o, string_span_t s, enum highlight_type t)
-{
- if (!s.len)
- return true;
- if (a->len >= a->capacity) {
- struct highlight_span *resized;
-
- a->capacity = a->capacity ? a->capacity * 2 : 64;
- resized = realloc_array(a->spans, a->capacity, sizeof(*resized));
- if (!resized) {
- free(a->spans);
- memset(a, 0, sizeof(*a));
- return false;
- }
- a->spans = resized;
- }
- a->spans[a->len++] = (struct highlight_span){ t, s.s - o, s.len };
- return true;
-}
-
-static void highlight_multivalue_value(struct highlight_span_array *ret, const string_span_t parent, const string_span_t s, enum field section)
-{
- switch (section) {
- case DNS:
- if (is_valid_ipv4(s) || is_valid_ipv6(s))
- append_highlight_span(ret, parent.s, s, HighlightIP);
- else if (is_valid_hostname(s))
- append_highlight_span(ret, parent.s, s, HighlightHost);
- else
- append_highlight_span(ret, parent.s, s, HighlightError);
- break;
- case Address:
- case AllowedIPs: {
- size_t slash;
-
- if (!is_valid_network(s)) {
- append_highlight_span(ret, parent.s, s, HighlightError);
- break;
- }
- for (slash = 0; slash < s.len; ++slash) {
- if (s.s[slash] == '/')
- break;
- }
- if (slash == s.len) {
- append_highlight_span(ret, parent.s, s, HighlightIP);
- } else {
- append_highlight_span(ret, parent.s, (string_span_t){ s.s, slash }, HighlightIP);
- append_highlight_span(ret, parent.s, (string_span_t){ s.s + slash, 1 }, HighlightDelimiter);
- append_highlight_span(ret, parent.s, (string_span_t){ s.s + slash + 1, s.len - slash - 1 }, HighlightCidr);
- }
- break;
- }
- default:
- append_highlight_span(ret, parent.s, s, HighlightError);
- }
-}
-
-static void highlight_multivalue(struct highlight_span_array *ret, const string_span_t parent, const string_span_t s, enum field section)
-{
- string_span_t current_span = { s.s, 0 };
- size_t len_at_last_space = 0;
-
- for (size_t i = 0; i < s.len; ++i) {
- if (s.s[i] == ',') {
- current_span.len = len_at_last_space;
- highlight_multivalue_value(ret, parent, current_span, section);
- append_highlight_span(ret, parent.s, (string_span_t){ s.s + i, 1 }, HighlightDelimiter);
- len_at_last_space = 0;
- current_span = (string_span_t){ s.s + i + 1, 0 };
- } else if (s.s[i] == ' ' || s.s[i] == '\t') {
- if (&s.s[i] == current_span.s && !current_span.len)
- ++current_span.s;
- else
- ++current_span.len;
- } else {
- len_at_last_space = ++current_span.len;
- }
- }
- current_span.len = len_at_last_space;
- if (current_span.len)
- highlight_multivalue_value(ret, parent, current_span, section);
- else if (ret->spans[ret->len - 1].type == HighlightDelimiter)
- ret->spans[ret->len - 1].type = HighlightError;
-}
-
-static void highlight_value(struct highlight_span_array *ret, const string_span_t parent, const string_span_t s, enum field section)
-{
- switch (section) {
- case PrivateKey:
- append_highlight_span(ret, parent.s, s, is_valid_key(s) ? HighlightPrivateKey : HighlightError);
- break;
- case PublicKey:
- append_highlight_span(ret, parent.s, s, is_valid_key(s) ? HighlightPublicKey : HighlightError);
- break;
- case PresharedKey:
- append_highlight_span(ret, parent.s, s, is_valid_key(s) ? HighlightPresharedKey : HighlightError);
- break;
- case MTU:
- append_highlight_span(ret, parent.s, s, is_valid_mtu(s) ? HighlightMTU : HighlightError);
- break;
-#ifndef MOBILE_WGQUICK_SUBSET
- case SaveConfig:
- append_highlight_span(ret, parent.s, s, is_valid_saveconfig(s) ? HighlightSaveConfig : HighlightError);
- break;
- case FwMark:
- append_highlight_span(ret, parent.s, s, is_valid_fwmark(s) ? HighlightFwMark : HighlightError);
- break;
- case Table:
- append_highlight_span(ret, parent.s, s, is_valid_table(s) ? HighlightTable : HighlightError);
- break;
- case PreUp:
- case PostUp:
- case PreDown:
- case PostDown:
- append_highlight_span(ret, parent.s, s, is_valid_prepostupdown(s) ? HighlightCmd : HighlightError);
- break;
-#endif
- case ListenPort:
- append_highlight_span(ret, parent.s, s, is_valid_port(s) ? HighlightPort : HighlightError);
- break;
- case PersistentKeepalive:
- append_highlight_span(ret, parent.s, s, is_valid_persistentkeepalive(s) ? HighlightKeepalive : HighlightError);
- break;
- case Endpoint: {
- size_t colon;
-
- if (!is_valid_endpoint(s)) {
- append_highlight_span(ret, parent.s, s, HighlightError);
- break;
- }
- for (colon = s.len; colon --> 0;) {
- if (s.s[colon] == ':')
- break;
- }
- append_highlight_span(ret, parent.s, (string_span_t){ s.s, colon }, HighlightHost);
- append_highlight_span(ret, parent.s, (string_span_t){ s.s + colon, 1 }, HighlightDelimiter);
- append_highlight_span(ret, parent.s, (string_span_t){ s.s + colon + 1, s.len - colon - 1 }, HighlightPort);
- break;
- }
- case Address:
- case DNS:
- case AllowedIPs:
- highlight_multivalue(ret, parent, s, section);
- break;
- default:
- append_highlight_span(ret, parent.s, s, HighlightError);
- }
-}
-
-struct highlight_span *highlight_config(const char *config)
-{
- struct highlight_span_array ret = { 0 };
- const string_span_t s = { config, strlen(config) };
- string_span_t current_span = { s.s, 0 };
- enum field current_section = Invalid, current_field = Invalid;
- enum { OnNone, OnKey, OnValue, OnComment, OnSection } state = OnNone;
- size_t len_at_last_space = 0, equals_location = 0;
-
- for (size_t i = 0; i <= s.len; ++i) {
- if (i == s.len || s.s[i] == '\n' || (state != OnComment && s.s[i] == '#')) {
- if (state == OnKey) {
- current_span.len = len_at_last_space;
- append_highlight_span(&ret, s.s, current_span, HighlightError);
- } else if (state == OnValue) {
- if (current_span.len) {
- append_highlight_span(&ret, s.s, (string_span_t){ s.s + equals_location, 1 }, HighlightDelimiter);
- current_span.len = len_at_last_space;
- highlight_value(&ret, s, current_span, current_field);
- } else {
- append_highlight_span(&ret, s.s, (string_span_t){ s.s + equals_location, 1 }, HighlightError);
- }
- } else if (state == OnSection) {
- current_span.len = len_at_last_space;
- current_section = get_sectiontype(current_span);
- append_highlight_span(&ret, s.s, current_span, current_section == Invalid ? HighlightError : HighlightSection);
- } else if (state == OnComment) {
- append_highlight_span(&ret, s.s, current_span, HighlightComment);
- }
- if (i == s.len)
- break;
- len_at_last_space = 0;
- current_field = Invalid;
- if (s.s[i] == '#') {
- current_span = (string_span_t){ s.s + i, 1 };
- state = OnComment;
- } else {
- current_span = (string_span_t){ s.s + i + 1, 0 };
- state = OnNone;
- }
- } else if (state == OnComment) {
- ++current_span.len;
- } else if (s.s[i] == ' ' || s.s[i] == '\t') {
- if (&s.s[i] == current_span.s && !current_span.len)
- ++current_span.s;
- else
- ++current_span.len;
- } else if (s.s[i] == '=' && state == OnKey) {
- current_span.len = len_at_last_space;
- current_field = get_field(current_span);
- enum field section = section_for_field(current_field);
- if (section == Invalid || current_field == Invalid || section != current_section)
- append_highlight_span(&ret, s.s, current_span, HighlightError);
- else
- append_highlight_span(&ret, s.s, current_span, HighlightField);
- equals_location = i;
- current_span = (string_span_t){ s.s + i + 1, 0 };
- state = OnValue;
- } else {
- if (state == OnNone)
- state = s.s[i] == '[' ? OnSection : OnKey;
- len_at_last_space = ++current_span.len;
- }
- }
-
- append_highlight_span(&ret, s.s, (string_span_t){ s.s, -1 }, HighlightEnd);
- return ret.spans;
-}