Files
shadowsocks-libev/src/rule.cpp
T
Max Lv 384810efce C++20 port 2/14: compile the leaf modules as C++ (#3066)
* Make headers and leaf modules compile as C++

Wrap the declarations of every project header in extern "C" (after the
system includes, which must keep C++ linkage), so converted modules and
the remaining C files agree on linkage.

In the leaf modules, cast allocator and memchr results explicitly,
rename identifiers called new, replace the getpwnam_r VLA with the
fixed 16 KiB buffer the loop already capped itself at, and make the
rlimit initialiser conversion explicit. All of this is still valid C.

* Compile the leaf modules as C++

Rename base64, json, ipset, process, ppbloom, rule, ssurl and cache from
.c to .cpp without touching their contents, and update the CMake source
lists, debian/copyright and the docs.

Ported code keeps -Wall; -Wextra is now opt-in through
ss_strict_warnings() for code written as C++ (the core tests).
check_no_heap.py gains a ratchet baseline for the converted modules,
which still allocate the way the C code did. A new check_cxx_runtime
test verifies with nm that the programs and libshadowsocks-c.a still
need no C++ runtime library, so C consumers are unaffected.

* Fix the Windows branches of process.cpp for C++

Cast allocator results and the thread argument explicitly and
value-initialise JOBOBJECT_EXTENDED_LIMIT_INFORMATION with {}. Found by
the zig x86_64-windows-gnu cross build.

* Keep clang-tidy clean for the converted modules

Once the C headers are included from C++ translation units,
bugprone-dynamic-static-initializers reports each extern variable
declaration in them; none is defined in a header, so disable the check.
Mark the two bounded memchr searches in ssurl.cpp, which
bugprone-not-null-terminated-result misreads as string copies.

* check_cxx_runtime: ignore glibc symbol versions

nm on glibc prints __cxa_atexit@GLIBC_2.2.5, which the allowlist for the
libc-provided __cxa_atexit/__cxa_finalize did not match.

* Keep GCC's C++-only warnings off for modules converted from C

GCC's C++ -Wall includes -Wsign-compare and -Wclass-memaccess, which C's
-Wall does not, so the converted modules failed -Werror on GCC (Linux,
UCRT64) although nothing in them changed. They keep the warning set they
were written against; code under ss_strict_warnings() re-enables both.
2026-10-15 09:48:18 +08:00

180 lines
5.1 KiB
C++

/*
* Copyright (c) 2011 and 2012, Dustin Lundquist <dustin@null-ptr.net>
* Copyright (c) 2011 Manuel Kasper <mk@neon1.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include "rule.h"
#include "utils.h"
rule_t *
new_rule()
{
rule_t *rule;
rule = (rule_t *)calloc(1, sizeof(rule_t));
if (rule == NULL) {
ERROR("malloc");
return NULL;
}
return rule;
}
int
accept_rule_arg(rule_t *rule, const char *arg)
{
if (rule->pattern == NULL) {
rule->pattern = strdup(arg);
if (rule->pattern == NULL) {
ERROR("strdup failed");
return -1;
}
} else {
LOGE("Unexpected table rule argument: %s", arg);
return -1;
}
return 1;
}
void
add_rule(struct ss_list *rules, rule_t *rule)
{
ss_list_add(rules, &rule->entries);
}
int
init_rule(rule_t *rule)
{
if (strncmp(rule->pattern, "full:", 5) == 0) {
rule->kind = 1;
rule->literal = rule->pattern + 5;
} else if (strncmp(rule->pattern, "suffix:", 7) == 0) {
rule->kind = 2;
rule->literal = rule->pattern + 7;
}
if (rule->kind != 0) {
return *rule->literal != '\0' ? 1 : 0;
}
#if SS_ENABLE_REGEX
if (rule->pattern_re == NULL) {
int errcode;
PCRE2_SIZE erroffset;
rule->pattern_re = pcre2_compile((PCRE2_SPTR)rule->pattern,
PCRE2_ZERO_TERMINATED, 0, &errcode, &erroffset, NULL);
if (rule->pattern_re == NULL) {
PCRE2_UCHAR errbuf[256];
pcre2_get_error_message(errcode, errbuf, sizeof(errbuf));
LOGE("Regex compilation of \"%s\" failed: %s, offset %d",
rule->pattern, errbuf, (int)erroffset);
return 0;
}
rule->match_data = pcre2_match_data_create_from_pattern(rule->pattern_re, NULL);
if (rule->match_data == NULL) {
pcre2_code_free(rule->pattern_re);
rule->pattern_re = NULL;
return 0;
}
}
return 1;
#else
LOGE("Regex ACL support is disabled; use full:domain or suffix:domain: %s", rule->pattern);
return -1;
#endif
}
static bool
literal_equal(const char *a, const char *b, size_t length)
{
for (size_t i = 0; i < length; i++) {
unsigned char x = (unsigned char)a[i], y = (unsigned char)b[i];
if (x >= 'A' && x <= 'Z') x += 'a' - 'A';
if (y >= 'A' && y <= 'Z') y += 'a' - 'A';
if (x != y) return false;
}
return true;
}
rule_t *
lookup_rule(const struct ss_list *rules, const char *name, size_t name_len)
{
struct ss_list_item *curr, *next;
if (name == NULL) {
name = "";
name_len = 0;
}
for (curr = ss_list_start(rules); !ss_list_is_end(rules, curr);
curr = next) {
next = curr->next;
rule_t *rule = ss_container_of(curr, rule_t, entries);
if (rule->kind != 0) {
size_t length = strlen(rule->literal);
if (name_len >= length &&
(name_len == length || (rule->kind == 2 && name[name_len - length - 1] == '.')) &&
literal_equal(name + name_len - length, rule->literal, length)) return rule;
continue;
}
#if SS_ENABLE_REGEX
if (pcre2_match(rule->pattern_re, (PCRE2_SPTR)name,
name_len, 0, 0, rule->match_data, NULL) >= 0)
return rule;
#endif
}
return NULL;
}
void
remove_rule(rule_t *rule)
{
ss_list_remove(&rule->entries);
free_rule(rule);
}
void
free_rule(rule_t *rule)
{
if (rule == NULL)
return;
ss_free(rule->pattern);
#if SS_ENABLE_REGEX
if (rule->match_data != NULL)
pcre2_match_data_free(rule->match_data);
if (rule->pattern_re != NULL)
pcre2_code_free(rule->pattern_re);
#endif
ss_free(rule);
}