From e1198b6e085310105e8d37effe221a1363d91c41 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 21 Jan 2019 22:22:20 +0100 Subject: macOS: Better highlighter default value and move c implementation Signed-off-by: Jason A. Donenfeld --- WireGuard/WireGuard.xcodeproj/project.pbxproj | 4 +- .../WireGuard/UI/macOS/View/ConfTextStorage.swift | 2 +- WireGuard/WireGuard/UI/macOS/View/highlighter.c | 620 +++++++++++++++++++++ WireGuard/WireGuard/UI/macOS/View/highlighter.h | 38 ++ WireGuard/WireGuard/UI/macOS/highlighter.c | 620 --------------------- WireGuard/WireGuard/UI/macOS/highlighter.h | 38 -- 6 files changed, 661 insertions(+), 661 deletions(-) create mode 100644 WireGuard/WireGuard/UI/macOS/View/highlighter.c create mode 100644 WireGuard/WireGuard/UI/macOS/View/highlighter.h delete mode 100644 WireGuard/WireGuard/UI/macOS/highlighter.c delete mode 100644 WireGuard/WireGuard/UI/macOS/highlighter.h (limited to 'WireGuard') diff --git a/WireGuard/WireGuard.xcodeproj/project.pbxproj b/WireGuard/WireGuard.xcodeproj/project.pbxproj index c63c721..943f5a9 100644 --- a/WireGuard/WireGuard.xcodeproj/project.pbxproj +++ b/WireGuard/WireGuard.xcodeproj/project.pbxproj @@ -391,6 +391,8 @@ 6F4DD16921DA556600690EAE /* View */ = { isa = PBXGroup; children = ( + 5F52D0C021E378C000283CEA /* highlighter.h */, + 5F52D0C121E378C000283CEA /* highlighter.c */, 6F4DD16A21DA558800690EAE /* TunnelListRow.swift */, 6F613D9A21DE33B8004B217A /* KeyValueRow.swift */, 5F52D0BA21E3781B00283CEA /* ConfTextView.swift */, @@ -529,8 +531,6 @@ 6FB1BD6721D2607E00A991BF /* WireGuard.entitlements */, 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */, 5F52D0BE21E3788900283CEA /* NSColor+Hex.swift */, - 5F52D0C021E378C000283CEA /* highlighter.h */, - 5F52D0C121E378C000283CEA /* highlighter.c */, 6FFACD1E21E4D89600E9A2A5 /* ParseError+WireGuardAppError.swift */, ); path = macOS; diff --git a/WireGuard/WireGuard/UI/macOS/View/ConfTextStorage.swift b/WireGuard/WireGuard/UI/macOS/View/ConfTextStorage.swift index fb8a382..688913c 100644 --- a/WireGuard/WireGuard/UI/macOS/View/ConfTextStorage.swift +++ b/WireGuard/WireGuard/UI/macOS/View/ConfTextStorage.swift @@ -101,7 +101,7 @@ class ConfTextStorage: NSTextStorage { let range = NSRange(location: span.start, length: span.len) backingStore.setAttributes(nonColorAttributes(for: span.type), range: range) - let color = textColorTheme.colorMap[span.type.rawValue] ?? textColorTheme.defaultColor + let color = textColorTheme.colorMap[span.type.rawValue, default: textColorTheme.defaultColor] backingStore.addAttribute(.foregroundColor, value: color, range: range) if span.type == HighlightError { diff --git a/WireGuard/WireGuard/UI/macOS/View/highlighter.c b/WireGuard/WireGuard/UI/macOS/View/highlighter.c new file mode 100644 index 0000000..171a84c --- /dev/null +++ b/WireGuard/WireGuard/UI/macOS/View/highlighter.c @@ -0,0 +1,620 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#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 < 43; ++i) { + if (!is_decimal(s.s[i]) && !is_alphabet(s.s[i]) && + s.s[i] != '/' && s.s[i] != '+') + 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 (s.s[i] - '0' < 10) + val = 16 * val + (s.s[i] - '0'); + else if ((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); +} + +static bool is_valid_dns(string_span_t s) +{ + 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: + append_highlight_span(ret, parent.s, s, is_valid_dns(s) ? HighlightIP : 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; +} diff --git a/WireGuard/WireGuard/UI/macOS/View/highlighter.h b/WireGuard/WireGuard/UI/macOS/View/highlighter.h new file mode 100644 index 0000000..885db2d --- /dev/null +++ b/WireGuard/WireGuard/UI/macOS/View/highlighter.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#define MOBILE_WGQUICK_SUBSET + +enum highlight_type { + HighlightSection, + HighlightField, + HighlightPrivateKey, + HighlightPublicKey, + HighlightPresharedKey, + HighlightIP, + HighlightCidr, + HighlightHost, + HighlightPort, + HighlightMTU, + HighlightKeepalive, + HighlightComment, + HighlightDelimiter, +#ifndef MOBILE_WGQUICK_SUBSET + HighlightTable, + HighlightFwMark, + HighlightSaveConfig, + HighlightCmd, +#endif + HighlightError, + HighlightEnd +}; + +struct highlight_span { + enum highlight_type type; + size_t start, len; +}; + +struct highlight_span *highlight_config(const char *config); diff --git a/WireGuard/WireGuard/UI/macOS/highlighter.c b/WireGuard/WireGuard/UI/macOS/highlighter.c deleted file mode 100644 index 171a84c..0000000 --- a/WireGuard/WireGuard/UI/macOS/highlighter.c +++ /dev/null @@ -1,620 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#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 < 43; ++i) { - if (!is_decimal(s.s[i]) && !is_alphabet(s.s[i]) && - s.s[i] != '/' && s.s[i] != '+') - 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 (s.s[i] - '0' < 10) - val = 16 * val + (s.s[i] - '0'); - else if ((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); -} - -static bool is_valid_dns(string_span_t s) -{ - 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: - append_highlight_span(ret, parent.s, s, is_valid_dns(s) ? HighlightIP : 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; -} diff --git a/WireGuard/WireGuard/UI/macOS/highlighter.h b/WireGuard/WireGuard/UI/macOS/highlighter.h deleted file mode 100644 index 885db2d..0000000 --- a/WireGuard/WireGuard/UI/macOS/highlighter.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. - */ - -#include -#define MOBILE_WGQUICK_SUBSET - -enum highlight_type { - HighlightSection, - HighlightField, - HighlightPrivateKey, - HighlightPublicKey, - HighlightPresharedKey, - HighlightIP, - HighlightCidr, - HighlightHost, - HighlightPort, - HighlightMTU, - HighlightKeepalive, - HighlightComment, - HighlightDelimiter, -#ifndef MOBILE_WGQUICK_SUBSET - HighlightTable, - HighlightFwMark, - HighlightSaveConfig, - HighlightCmd, -#endif - HighlightError, - HighlightEnd -}; - -struct highlight_span { - enum highlight_type type; - size_t start, len; -}; - -struct highlight_span *highlight_config(const char *config); -- cgit v1.2.3-59-g8ed1b