mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-28 10:44:43 +08:00
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.
This commit is contained in:
1 parent
bcb3731968
commit
384810efce
49 files changed
+476
-89
No files matched your search
+4
-1
@@ -12,7 +12,9 @@
|
||||
# reserved-identifier: the codebase uses _prefixed names and vendored
|
||||
# uthash.h/generated config.h trip them ~600 times) or produce false
|
||||
# positives with the event callback style (casting-through-void,
|
||||
# multi-level-implicit-pointer-conversion).
|
||||
# multi-level-implicit-pointer-conversion). dynamic-static-initializers
|
||||
# fires on every extern variable declaration in the C headers once they are
|
||||
# included from C++; none of them is defined or initialised in a header.
|
||||
Checks: >
|
||||
clang-analyzer-*,
|
||||
bugprone-*,
|
||||
@@ -20,6 +22,7 @@ Checks: >
|
||||
-bugprone-assignment-in-if-condition,
|
||||
-bugprone-branch-clone,
|
||||
-bugprone-casting-through-void,
|
||||
-bugprone-dynamic-static-initializers,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-inc-dec-in-conditions,
|
||||
-bugprone-implicit-widening-of-multiplication-result,
|
||||
|
||||
@@ -103,9 +103,9 @@ A library `libshadowsocks-c` (legacy names `libshadowsocks-libev.*` are kept) is
|
||||
|
||||
**Shared by all binaries:**
|
||||
- `utils.c` - logging, system utilities, `ss_malloc`/`ss_free`
|
||||
- `jconf.c` / `json.c` - JSON config file parsing
|
||||
- `jconf.c` / `json.cpp` - JSON config file parsing
|
||||
- `netutils.c` - network address utilities
|
||||
- `cache.c` - uthash-based connection cache (UDP relay, AEAD-2022 tables)
|
||||
- `cache.cpp` - uthash-based connection cache (UDP relay, AEAD-2022 tables)
|
||||
- `udprelay.c` - UDP relay implementation (shared, but uses `#ifdef MODULE_*` for per-binary behavior)
|
||||
- `ss_event.c` / `ss_event.h` - event loop API over libuv
|
||||
- `resolv.c` - asynchronous DNS via c-ares
|
||||
@@ -115,11 +115,11 @@ A library `libshadowsocks-c` (legacy names `libshadowsocks-libev.*` are kept) is
|
||||
- `stream.c` - legacy stream ciphers
|
||||
- `aead.c` - AEAD ciphers (AES-GCM via Mbed TLS/libsodium, ChaCha20-Poly1305 via libsodium)
|
||||
- `aead2022.c` / `aead2022_udp.c` - Shadowsocks 2022 (SIP022), BLAKE3 from `src/blake3`
|
||||
- `ppbloom.c` - ping-pong bloom filter for salt replay detection (`third_party/bloom`)
|
||||
- `ppbloom.cpp` - ping-pong bloom filter for salt replay detection (`third_party/bloom`)
|
||||
|
||||
**ACL:** `acl.c` / `rule.c` / `ipset.c` - IP prefix sets and PCRE2/literal domain rules.
|
||||
**ACL:** `acl.c` / `rule.cpp` / `ipset.cpp` - IP prefix sets and PCRE2/literal domain rules.
|
||||
|
||||
**Plugins:** `plugin.c` / `process.c` - SIP003 plugin subprocess management.
|
||||
**Plugins:** `plugin.c` / `process.cpp` - SIP003 plugin subprocess management.
|
||||
|
||||
**C++ core:** `src/core/*.hpp` - header-only ownership and allocation primitives (pools, handles, fixed
|
||||
buffers, arena, fixed hash map/LRU, intrusive list, bounded heap). See `docs/cpp-core.md`.
|
||||
|
||||
@@ -26,9 +26,16 @@ function(ss_project_options target)
|
||||
else()
|
||||
target_compile_options(${target} PRIVATE -Wall -Wno-deprecated-declarations -fno-strict-aliasing)
|
||||
# C++ sources: no exceptions or RTTI, and no locks around function
|
||||
# statics (the event loop is single-threaded).
|
||||
# statics (the event loop is single-threaded). New C++ code also opts
|
||||
# into -Wextra through ss_strict_warnings(); modules ported from C get
|
||||
# it when they are rewritten.
|
||||
target_compile_options(${target} PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wextra -fno-exceptions -fno-rtti -fno-threadsafe-statics>)
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions -fno-rtti -fno-threadsafe-statics>)
|
||||
# GCC's C++ -Wall adds -Wsign-compare and -Wclass-memaccess, which C's
|
||||
# -Wall does not have. Modules converted from C keep the warning set
|
||||
# they were written against; ss_strict_warnings() turns both back on.
|
||||
target_compile_options(${target} PRIVATE
|
||||
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wno-sign-compare -Wno-class-memaccess>)
|
||||
if(SS_WARNINGS_AS_ERRORS)
|
||||
target_compile_options(${target} PRIVATE -Werror)
|
||||
endif()
|
||||
@@ -54,3 +61,12 @@ function(ss_project_options target)
|
||||
target_link_options(${target} PRIVATE --coverage)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Extra warnings for code written as C++ from the start (src/core and its
|
||||
# tests).
|
||||
function(ss_strict_warnings target)
|
||||
if(NOT MSVC)
|
||||
target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wextra>
|
||||
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wsign-compare -Wclass-memaccess>)
|
||||
endif()
|
||||
endfunction()
|
||||
Vendored
+3
-3
@@ -41,11 +41,11 @@ Files: scripts/build_deb.sh
|
||||
Copyright: 2018 Roger Shimizu <rosh@debian.org>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/base64.c src/base64.h
|
||||
Files: src/base64.cpp src/base64.h
|
||||
Copyright: 2006 Ryan Martell <rdm4@martellventures.com>
|
||||
License: LGPL-2.1+
|
||||
|
||||
Files: src/json.c src/json.h
|
||||
Files: src/json.cpp src/json.h
|
||||
Copyright: 2012-2014, James McLaughlin et al.
|
||||
License: BSD-2-clause
|
||||
|
||||
@@ -53,7 +53,7 @@ Files: src/resolv.c src/resolv.h
|
||||
Copyright: 2011-2014, Dustin Lundquist <dustin@null-ptr.net>
|
||||
License: BSD-2-clause
|
||||
|
||||
Files: src/rule.c src/rule.h
|
||||
Files: src/rule.cpp src/rule.h
|
||||
Copyright: 2011-2012, Dustin Lundquist <dustin@null-ptr.net>
|
||||
2011, Manuel Kasper <mk@neon1.net>
|
||||
License: BSD-2-clause
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ All binaries are still built from the C sources.
|
||||
| `ref.hpp` | `RefCounted<T>`/`RefPtr<T>`, intrusive shared ownership for the few genuinely shared objects (AEAD-2022 UDP sessions). Used instead of `std::shared_ptr` because its control block cannot be pooled portably. |
|
||||
| `buffer.hpp` | `FixedBuffer<N>`: inline bytes with `std::span` views; replaces `buffer_t`. |
|
||||
| `arena.hpp` | `MonotonicArena` for configuration data that lives as long as the process. |
|
||||
| `fixed_map.hpp` | `FixedHashMap` (open addressing, backward-shift deletion) and `FixedLru`; replace uthash and `cache.c`. |
|
||||
| `fixed_map.hpp` | `FixedHashMap` (open addressing, backward-shift deletion) and `FixedLru`; replace uthash and `cache.cpp`. |
|
||||
| `intrusive_list.hpp` | `IntrusiveList`/`ListNode`; replaces `ss_list`. Nodes unlink themselves when destroyed. |
|
||||
| `fixed_heap.hpp` | Bounded size-class allocator to hand to libuv, c-ares and Mbed TLS through their allocator hooks. |
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""Fail if programs or libraries need the C++ runtime library.
|
||||
|
||||
The C++ core is built without exceptions or RTTI and uses only header-only
|
||||
parts of the standard library, so the programs and libshadowsocks-c stay
|
||||
consumable from plain C: a C program linking libshadowsocks-c.a must not need
|
||||
libstdc++ or libc++. This script lists the undefined symbols of each file with
|
||||
nm and rejects anything served by the C++ runtime (operator new/delete,
|
||||
exception and RTTI support, std:: functions).
|
||||
|
||||
Usage: check_cxx_runtime.py [--nm NM] FILE...
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# Itanium-ABI names (with or without the Mach-O leading underscore) that only
|
||||
# the C++ runtime defines.
|
||||
RUNTIME = re.compile(
|
||||
r"^_?(?:"
|
||||
r"_Zn[wa]|_Zd[la]|" # operator new / delete
|
||||
r"_ZSt|_ZNSt|_ZNKSt|" # std:: functions and members
|
||||
r"_ZTI|_ZTS|_ZTV|" # typeinfo / vtables (RTTI, classes from the runtime)
|
||||
r"__cxa_(?!atexit$|finalize$|thread_atexit_impl$)|" # exception/guard support; libc owns these three
|
||||
r"__gxx_personality|_Unwind_|__dynamic_cast"
|
||||
r")"
|
||||
)
|
||||
|
||||
|
||||
def undefined_symbols(nm, path):
|
||||
result = subprocess.run([nm, "-u", path], capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise SystemExit(f"{nm} failed on {path}: {result.stderr.strip()}")
|
||||
symbols = set()
|
||||
for line in result.stdout.splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.endswith(":"):
|
||||
continue # archive member headers
|
||||
# glibc nm prints versioned names (__cxa_atexit@GLIBC_2.2.5).
|
||||
symbols.add(line.split()[-1].split("@")[0])
|
||||
return symbols
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
parser.add_argument("--nm", default=shutil.which("nm") or "nm")
|
||||
parser.add_argument("files", nargs="+")
|
||||
args = parser.parse_args()
|
||||
failed = False
|
||||
for path in args.files:
|
||||
bad = sorted(s for s in undefined_symbols(args.nm, path) if RUNTIME.match(s))
|
||||
if bad:
|
||||
failed = True
|
||||
print(f"{path}: needs the C++ runtime:")
|
||||
for symbol in bad:
|
||||
print(f" {symbol}")
|
||||
else:
|
||||
print(f"{path}: ok")
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -14,7 +14,12 @@ A line that must allocate (a startup reservation) carries a justification:
|
||||
|
||||
void *base = std::calloc(1, size); // heap-ok: startup reservation
|
||||
|
||||
Usage: check_no_heap.py [--root DIR] [--self-test]
|
||||
Modules converted mechanically from C still allocate the way the C code did
|
||||
until they are rewritten. scripts/no_heap_baseline.txt lists them with their
|
||||
current violation counts, and works as a ratchet: a file may not gain
|
||||
violations, and when it loses some the baseline must be lowered to match.
|
||||
|
||||
Usage: check_no_heap.py [--root DIR] [--baseline FILE] [--self-test]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
@@ -89,16 +94,42 @@ def check_text(text):
|
||||
return problems
|
||||
|
||||
|
||||
def check_tree(root):
|
||||
def read_baseline(path):
|
||||
baseline = {}
|
||||
if path is None or not path.exists():
|
||||
return baseline
|
||||
for raw in path.read_text(encoding="utf-8").splitlines():
|
||||
line = raw.split("#", 1)[0].strip()
|
||||
if line:
|
||||
name, count = line.split()
|
||||
baseline[name] = int(count)
|
||||
return baseline
|
||||
|
||||
|
||||
def check_tree(root, baseline):
|
||||
src = root / "src"
|
||||
files = sorted(p for p in src.rglob("*") if p.suffix in (".cpp", ".hpp") and "blake3" not in p.parts)
|
||||
failures = 0
|
||||
ok = True
|
||||
total = 0
|
||||
for path in files:
|
||||
for number, why, line in check_text(path.read_text(encoding="utf-8")):
|
||||
print(f"{path.relative_to(root)}:{number}: {why}: {line}")
|
||||
failures += 1
|
||||
print(f"check_no_heap: {len(files)} C++ files, {failures} violation(s)")
|
||||
return failures == 0
|
||||
name = path.relative_to(root).as_posix()
|
||||
problems = check_text(path.read_text(encoding="utf-8"))
|
||||
allowed = baseline.pop(name, 0)
|
||||
total += len(problems)
|
||||
if len(problems) > allowed:
|
||||
for number, why, line in problems:
|
||||
print(f"{name}:{number}: {why}: {line}")
|
||||
print(f"{name}: {len(problems)} violation(s), baseline allows {allowed}")
|
||||
ok = False
|
||||
elif len(problems) < allowed:
|
||||
print(f"{name}: {len(problems)} violation(s), below the baseline of {allowed}; "
|
||||
"lower it in scripts/no_heap_baseline.txt")
|
||||
ok = False
|
||||
for name in sorted(baseline):
|
||||
print(f"{name}: listed in the baseline but not a C++ source; remove the entry")
|
||||
ok = False
|
||||
print(f"check_no_heap: {len(files)} C++ files, {total} baselined or new violation(s)")
|
||||
return ok
|
||||
|
||||
|
||||
def self_test():
|
||||
@@ -144,11 +175,15 @@ def self_test():
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
parser.add_argument("--root", type=pathlib.Path, default=pathlib.Path(__file__).resolve().parent.parent)
|
||||
parser.add_argument("--baseline", type=pathlib.Path,
|
||||
help="ratchet file (default: scripts/no_heap_baseline.txt under --root)")
|
||||
parser.add_argument("--self-test", action="store_true", help="run the pattern self-test first")
|
||||
args = parser.parse_args()
|
||||
if args.self_test and not self_test():
|
||||
return 1
|
||||
return 0 if check_tree(args.root.resolve()) else 1
|
||||
root = args.root.resolve()
|
||||
baseline = read_baseline(args.baseline or root / "scripts" / "no_heap_baseline.txt")
|
||||
return 0 if check_tree(root, baseline) else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Heap-allocation ratchet for C++ sources converted mechanically from C.
|
||||
# Format: <path> <violations>. check_no_heap.py fails if a file gains
|
||||
# violations, and also when it loses some, so the number here always goes
|
||||
# down. Remove a file once it is rewritten on the src/core primitives.
|
||||
src/cache.cpp 3
|
||||
src/ipset.cpp 3
|
||||
src/json.cpp 2
|
||||
src/process.cpp 26
|
||||
src/rule.cpp 2
|
||||
src/ssurl.cpp 3
|
||||
+11
-11
@@ -10,8 +10,8 @@ endif ()
|
||||
|
||||
set(SS_ACL_SOURCE
|
||||
acl.c
|
||||
ipset.c
|
||||
rule.c
|
||||
ipset.cpp
|
||||
rule.cpp
|
||||
)
|
||||
|
||||
set(SS_CRYPTO_SOURCE
|
||||
@@ -19,7 +19,7 @@ set(SS_CRYPTO_SOURCE
|
||||
aead.c
|
||||
aead2022.c
|
||||
aead2022_udp.c
|
||||
base64.c
|
||||
base64.cpp
|
||||
)
|
||||
|
||||
# BLAKE3 (vendored) provides the key derivation for the 2022 ciphers. Only
|
||||
@@ -43,16 +43,16 @@ if(SS_ENABLE_LEGACY)
|
||||
list(APPEND SS_CRYPTO_SOURCE stream.c)
|
||||
endif()
|
||||
if(SS_ENABLE_PLUGINS)
|
||||
set(SS_PLUGIN_SOURCE plugin.c process.c)
|
||||
set(SS_PLUGIN_SOURCE plugin.c process.cpp)
|
||||
else()
|
||||
set(SS_PLUGIN_SOURCE plugin_disabled.c)
|
||||
endif()
|
||||
|
||||
set(SS_SHARED_SOURCES
|
||||
ppbloom.c
|
||||
ppbloom.cpp
|
||||
utils.c
|
||||
jconf.c
|
||||
json.c
|
||||
json.cpp
|
||||
netutils.c
|
||||
)
|
||||
if (MINGW)
|
||||
@@ -62,10 +62,10 @@ endif ()
|
||||
set(LIBSHADOWSOCKS_LIBEV_SOURCE
|
||||
${SS_SHARED_SOURCES}
|
||||
udprelay.c
|
||||
cache.c
|
||||
cache.cpp
|
||||
local.c
|
||||
resolv.c
|
||||
ssurl.c
|
||||
ssurl.cpp
|
||||
${SS_CRYPTO_SOURCE}
|
||||
${SS_PLUGIN_SOURCE}
|
||||
${SS_ACL_SOURCE}
|
||||
@@ -79,7 +79,7 @@ set(SS_LOCAL_SOURCE
|
||||
set(SS_TUNNEL_SOURCE
|
||||
${SS_SHARED_SOURCES}
|
||||
udprelay.c
|
||||
cache.c
|
||||
cache.cpp
|
||||
tunnel.c
|
||||
${SS_CRYPTO_SOURCE}
|
||||
${SS_PLUGIN_SOURCE}
|
||||
@@ -88,7 +88,7 @@ set(SS_TUNNEL_SOURCE
|
||||
set(SS_SERVER_SOURCE
|
||||
${SS_SHARED_SOURCES}
|
||||
udprelay.c
|
||||
cache.c
|
||||
cache.cpp
|
||||
resolv.c
|
||||
server.c
|
||||
${SS_CRYPTO_SOURCE}
|
||||
@@ -104,7 +104,7 @@ set(SS_MANAGER_SOURCE
|
||||
set(SS_REDIR_SOURCE
|
||||
${SS_SHARED_SOURCES}
|
||||
udprelay.c
|
||||
cache.c
|
||||
cache.cpp
|
||||
redir.c
|
||||
${SS_CRYPTO_SOURCE}
|
||||
${SS_PLUGIN_SOURCE}
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
#ifndef _ACL_H
|
||||
#define _ACL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BLACK_LIST 0
|
||||
#define WHITE_LIST 1
|
||||
|
||||
@@ -37,4 +41,8 @@ int get_acl_mode(void);
|
||||
|
||||
int outbound_block_match_host(const char *host);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _ACL_H
|
||||
@@ -25,6 +25,10 @@
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// currently, XCHACHA20POLY1305IETF is not released yet
|
||||
// XCHACHA20POLY1305 is removed in upstream
|
||||
#ifdef FS_HAVE_XCHACHA20IETF
|
||||
@@ -52,4 +56,8 @@ int aead_2022_encrypt_udp(buffer_t *, cipher_t *, size_t, void **);
|
||||
int aead_2022_decrypt_udp(buffer_t *, cipher_t *, size_t, void **);
|
||||
void aead_2022_udp_session_release(void *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _AEAD_H
|
||||
@@ -25,6 +25,10 @@
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* AEAD method identifiers (indexes into the supported_aead_ciphers tables) */
|
||||
#define AES128GCM 0
|
||||
#define AES192GCM 1
|
||||
@@ -73,4 +77,8 @@ int ss2022_tcp_encrypt(buffer_t *plaintext, cipher_ctx_t *cipher_ctx,
|
||||
int ss2022_tcp_decrypt(buffer_t *ciphertext, cipher_ctx_t *cipher_ctx,
|
||||
size_t capacity);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _AEAD_INTERNAL_H
|
||||
File renamed without changes.
@@ -23,6 +23,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Decode a base64-encoded string.
|
||||
*
|
||||
@@ -51,4 +55,8 @@ char *base64_encode(char *out, int out_size, const uint8_t *in, int in_size);
|
||||
*/
|
||||
#define BASE64_SIZE(x) (((x)+2) / 3 * 4 + 1)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BASE64_H */
|
||||
+11
-11
@@ -50,20 +50,20 @@ int
|
||||
cache_create(struct cache **dst, const size_t capacity,
|
||||
void (*free_cb)(void *key, void *element))
|
||||
{
|
||||
struct cache *new = NULL;
|
||||
struct cache *created = NULL;
|
||||
|
||||
if (!dst) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((new = malloc(sizeof(*new))) == NULL) {
|
||||
if ((created = (struct cache *)malloc(sizeof(*created))) == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
new->max_entries = capacity;
|
||||
new->entries = NULL;
|
||||
new->free_cb = free_cb;
|
||||
*dst = new;
|
||||
created->max_entries = capacity;
|
||||
created->entries = NULL;
|
||||
created->free_cb = free_cb;
|
||||
*dst = created;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ int
|
||||
cache_lookup(struct cache *cache, char *key, size_t key_len, void *result)
|
||||
{
|
||||
struct cache_entry *tmp = NULL;
|
||||
char **dirty_hack = result;
|
||||
void **out = (void **)result;
|
||||
|
||||
if (!cache || !key || !result) {
|
||||
return EINVAL;
|
||||
@@ -224,9 +224,9 @@ cache_lookup(struct cache *cache, char *key, size_t key_len, void *result)
|
||||
tmp->ts = ss_monotonic_time();
|
||||
// NOLINTNEXTLINE(clang-analyzer-core.DivideZero): uthash bucket count is never zero
|
||||
HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp);
|
||||
*dirty_hack = tmp->data;
|
||||
*out = tmp->data;
|
||||
} else {
|
||||
*dirty_hack = result = NULL;
|
||||
*out = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -279,11 +279,11 @@ cache_insert(struct cache *cache, char *key, size_t key_len, void *data)
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((entry = malloc(sizeof(*entry))) == NULL) {
|
||||
if ((entry = (struct cache_entry *)malloc(sizeof(*entry))) == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
entry->key = ss_malloc(key_len + 1);
|
||||
entry->key = (char *)ss_malloc(key_len + 1);
|
||||
memcpy(entry->key, key, key_len);
|
||||
entry->key[key_len] = 0;
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
|
||||
#include "uthash.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A cache entry
|
||||
*/
|
||||
@@ -58,4 +62,8 @@ int cache_insert(struct cache *cache, char *key, size_t key_len, void *data);
|
||||
int cache_remove(struct cache *cache, char *key, size_t key_len);
|
||||
int cache_key_exist(struct cache *cache, char *key, size_t key_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -32,6 +32,10 @@
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int init_udprelay(const char *server_host, const char *server_port,
|
||||
#ifdef MODULE_LOCAL
|
||||
const struct sockaddr *remote_addr, const int remote_addr_len,
|
||||
@@ -83,4 +87,8 @@ enum {
|
||||
GETOPT_VAL_IPV4_FIRST
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _COMMON_H
|
||||
@@ -6,6 +6,10 @@
|
||||
#include <stdint.h>
|
||||
#include "platform.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ss_container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))
|
||||
|
||||
struct ss_list_item {
|
||||
@@ -71,4 +75,8 @@ ss_ip_init(struct ss_ip *ip, const char *text)
|
||||
ip->version = 0;
|
||||
return -1;
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -2,7 +2,7 @@
|
||||
#ifndef SS_CORE_FIXED_MAP_HPP
|
||||
#define SS_CORE_FIXED_MAP_HPP
|
||||
|
||||
// Fixed-capacity associative containers. Replace uthash (cache.c,
|
||||
// Fixed-capacity associative containers. Replace uthash (cache.cpp,
|
||||
// ss_event.c, manager.c) without any per-entry allocation.
|
||||
//
|
||||
// FixedHashMap<K, V>
|
||||
@@ -15,7 +15,7 @@
|
||||
// FixedLru<K, V>
|
||||
// Map plus recency order. Values live in a slab and never move, so the
|
||||
// pointer returned by insert() or find() stays valid until that entry
|
||||
// is erased or evicted. Replaces the pseudo-LRU in cache.c.
|
||||
// is erased or evicted. Replaces the pseudo-LRU in cache.cpp.
|
||||
//
|
||||
// Keys from the network (UDP peer addresses, session IDs) are hashed with a
|
||||
// per-table seed. Seed it from a CSPRNG in production code so a peer cannot
|
||||
|
||||
@@ -42,6 +42,10 @@ typedef crypto_aead_aes256gcm_state aes256gcm_ctx;
|
||||
/* Definitions for mbedTLS */
|
||||
#include <mbedtls/cipher.h>
|
||||
#include <mbedtls/md.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
typedef mbedtls_cipher_info_t cipher_kt_t;
|
||||
typedef mbedtls_cipher_context_t cipher_evp_t;
|
||||
typedef mbedtls_md_info_t digest_type_t;
|
||||
@@ -206,4 +210,8 @@ extern struct cache *nonce_cache;
|
||||
extern const char *supported_stream_ciphers[];
|
||||
extern const char *supported_aead_ciphers[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _CRYPTO_H
|
||||
@@ -14,7 +14,7 @@ struct ss_ipset_node {
|
||||
static struct ss_ipset_node *
|
||||
node_new(bool full)
|
||||
{
|
||||
struct ss_ipset_node *node = ss_malloc(sizeof(*node));
|
||||
struct ss_ipset_node *node = (struct ss_ipset_node *)ss_malloc(sizeof(*node));
|
||||
node->child[0] = node->child[1] = NULL;
|
||||
node->full = full;
|
||||
return node;
|
||||
@@ -4,6 +4,10 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ss_ipset_node;
|
||||
struct ss_ipset {
|
||||
struct ss_ipset_node *root;
|
||||
@@ -15,4 +19,8 @@ void ss_ipset_clear(struct ss_ipset *set);
|
||||
void ss_ipset_assign(struct ss_ipset *set, const uint8_t *address,
|
||||
unsigned width, unsigned prefix, bool present);
|
||||
bool ss_ipset_contains(const struct ss_ipset *set, const uint8_t *address, unsigned width);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -22,6 +22,10 @@
|
||||
#ifndef _JCONF_H
|
||||
#define _JCONF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MAX_PORT_NUM 1024
|
||||
#define MAX_REMOTE_NUM 10
|
||||
#define MAX_DSCP_NUM 64
|
||||
@@ -99,4 +103,8 @@ void free_addr(ss_addr_t *addr);
|
||||
int parse_server_endpoint(const char *str, ss_addr_t *addr);
|
||||
int complete_server_ports(ss_addr_t *addr, int count, const char *fallback, int plugin);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _JCONF_H
|
||||
File renamed without changes.
@@ -36,6 +36,10 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct listen_ctx {
|
||||
ss_io io;
|
||||
char *iface;
|
||||
@@ -100,4 +104,8 @@ typedef struct remote {
|
||||
struct sockaddr_storage addr;
|
||||
} remote_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _LOCAL_H
|
||||
@@ -33,6 +33,10 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct manager_ctx {
|
||||
ss_io io;
|
||||
int fd;
|
||||
@@ -75,4 +79,8 @@ struct server {
|
||||
uint64_t traffic;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _MANAGER_H
|
||||
@@ -39,6 +39,10 @@
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Hard coded defines for TCP fast open on Android */
|
||||
#ifdef __ANDROID__
|
||||
#ifndef TCP_FASTOPEN
|
||||
@@ -127,4 +131,8 @@ int validate_hostname(const char *hostname, const int hostname_len);
|
||||
|
||||
int is_ipv6only(ss_addr_t *servers, size_t server_num, int ipv6first);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -17,6 +17,10 @@ typedef SOCKET ss_socket_t;
|
||||
typedef int ss_socket_t;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Socket errors are separate from CRT errno on Windows. Return normalized
|
||||
* errno constants for portable branching without redefining libc names. */
|
||||
static inline int
|
||||
@@ -117,4 +121,8 @@ ss_monotonic_time(void)
|
||||
return (double)value.tv_sec + (double)value.tv_nsec / 1000000000.0;
|
||||
#endif
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -23,6 +23,10 @@
|
||||
#ifndef _PLUGIN_H
|
||||
#define _PLUGIN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PLUGIN_EXIT_ERROR -2
|
||||
#define PLUGIN_EXIT_NORMAL -1
|
||||
#define PLUGIN_RUNNING 0
|
||||
@@ -77,4 +81,8 @@ uint16_t get_local_port();
|
||||
void stop_plugin();
|
||||
int is_plugin_running();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _PLUGIN_H
|
||||
File renamed without changes.
@@ -23,9 +23,17 @@
|
||||
#ifndef _PPBLOOM_
|
||||
#define _PPBLOOM_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int ppbloom_init(int entries, double error);
|
||||
int ppbloom_check(const void *buffer, int len);
|
||||
int ppbloom_add(const void *buffer, int len);
|
||||
void ppbloom_free(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -32,7 +32,7 @@ static char *
|
||||
copy_string(const char *text)
|
||||
{
|
||||
size_t size = strlen(text) + 1;
|
||||
char *copy = ss_malloc(size);
|
||||
char *copy = (char *)ss_malloc(size);
|
||||
memcpy(copy, text, size);
|
||||
return copy;
|
||||
}
|
||||
@@ -40,7 +40,7 @@ copy_string(const char *text)
|
||||
struct ss_process *
|
||||
ss_process_new(const char *program)
|
||||
{
|
||||
struct ss_process *process = ss_malloc(sizeof(*process));
|
||||
struct ss_process *process = (struct ss_process *)ss_malloc(sizeof(*process));
|
||||
memset(process, 0, sizeof(*process));
|
||||
process->program = copy_string(program);
|
||||
return process;
|
||||
@@ -49,7 +49,7 @@ ss_process_new(const char *program)
|
||||
void
|
||||
ss_process_arg(struct ss_process *process, const char *argument)
|
||||
{
|
||||
process->argv = ss_realloc(process->argv, (process->argc + 2) * sizeof(char *));
|
||||
process->argv = (char **)ss_realloc(process->argv, (process->argc + 2) * sizeof(char *));
|
||||
process->argv[process->argc++] = copy_string(argument);
|
||||
process->argv[process->argc] = NULL;
|
||||
}
|
||||
@@ -58,9 +58,9 @@ void
|
||||
ss_process_env(struct ss_process *process, const char *name, const char *value)
|
||||
{
|
||||
size_t size = strlen(name) + strlen(value) + 2;
|
||||
char *entry = ss_malloc(size);
|
||||
char *entry = (char *)ss_malloc(size);
|
||||
snprintf(entry, size, "%s=%s", name, value);
|
||||
process->env = ss_realloc(process->env, (process->envc + 2) * sizeof(char *));
|
||||
process->env = (char **)ss_realloc(process->env, (process->envc + 2) * sizeof(char *));
|
||||
process->env[process->envc++] = entry;
|
||||
process->env[process->envc] = NULL;
|
||||
}
|
||||
@@ -90,7 +90,7 @@ ss_process_start(struct ss_process *process, uint16_t control_port)
|
||||
while (environ[count] != NULL) {
|
||||
count++;
|
||||
}
|
||||
char **env = ss_malloc((count + process->envc + 1) * sizeof(char *));
|
||||
char **env = (char **)ss_malloc((count + process->envc + 1) * sizeof(char *));
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (!overridden(process, environ[i])) {
|
||||
env[used++] = environ[i];
|
||||
@@ -164,7 +164,7 @@ wide_string(const char *text)
|
||||
{
|
||||
int count = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, -1, NULL, 0);
|
||||
if (count == 0) return NULL;
|
||||
wchar_t *wide = ss_malloc((size_t)count * sizeof(wchar_t));
|
||||
wchar_t *wide = (wchar_t *)ss_malloc((size_t)count * sizeof(wchar_t));
|
||||
if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, -1, wide, count)) {
|
||||
free(wide);
|
||||
return NULL;
|
||||
@@ -175,7 +175,7 @@ wide_string(const char *text)
|
||||
static DWORD WINAPI
|
||||
monitor_process(void *argument)
|
||||
{
|
||||
struct ss_process *process = argument;
|
||||
struct ss_process *process = (struct ss_process *)argument;
|
||||
WaitForSingleObject(process->child, INFINITE);
|
||||
if (process->control_port != 0) {
|
||||
SOCKET fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
@@ -207,11 +207,11 @@ sort_environment(wchar_t *env)
|
||||
count++;
|
||||
length += wcslen(entry) + 1;
|
||||
}
|
||||
wchar_t **entries = ss_malloc((count + 1) * sizeof(*entries));
|
||||
wchar_t **entries = (wchar_t **)ss_malloc((count + 1) * sizeof(*entries));
|
||||
size_t index = 0;
|
||||
for (wchar_t *entry = env; *entry; entry += wcslen(entry) + 1) entries[index++] = entry;
|
||||
qsort(entries, count, sizeof(*entries), compare_environment);
|
||||
wchar_t *sorted = ss_malloc((length + 1) * sizeof(*sorted)), *out = sorted;
|
||||
wchar_t *sorted = (wchar_t *)ss_malloc((length + 1) * sizeof(*sorted)), *out = sorted;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
size_t size = wcslen(entries[i]) + 1;
|
||||
memcpy(out, entries[i], size * sizeof(*out));
|
||||
@@ -231,7 +231,7 @@ ss_process_start(struct ss_process *process, uint16_t control_port)
|
||||
* Double backslashes before a quote or the closing quote. */
|
||||
size_t capacity = 1;
|
||||
for (size_t i = 0; i < process->argc; i++) capacity += 2 * strlen(process->argv[i]) + 4;
|
||||
char *command = ss_malloc(capacity), *out = command;
|
||||
char *command = (char *)ss_malloc(capacity), *out = command;
|
||||
for (size_t i = 0; i < process->argc; i++) {
|
||||
if (i != 0) *out++ = ' ';
|
||||
*out++ = '"';
|
||||
@@ -254,7 +254,7 @@ ss_process_start(struct ss_process *process, uint16_t control_port)
|
||||
|
||||
wchar_t *inherited = GetEnvironmentStringsW();
|
||||
if (inherited == NULL) { free(wide_command); return -1; }
|
||||
wchar_t **overrides = ss_malloc((process->envc + 1) * sizeof(wchar_t *));
|
||||
wchar_t **overrides = (wchar_t **)ss_malloc((process->envc + 1) * sizeof(wchar_t *));
|
||||
size_t total = 2;
|
||||
for (const wchar_t *entry = inherited; *entry; entry += wcslen(entry) + 1) total += wcslen(entry) + 1;
|
||||
for (size_t i = 0; i < process->envc; i++) {
|
||||
@@ -266,7 +266,7 @@ ss_process_start(struct ss_process *process, uint16_t control_port)
|
||||
}
|
||||
total += wcslen(overrides[i]) + 1;
|
||||
}
|
||||
wchar_t *env = ss_malloc(total * sizeof(wchar_t)), *dest = env;
|
||||
wchar_t *env = (wchar_t *)ss_malloc(total * sizeof(wchar_t)), *dest = env;
|
||||
for (const wchar_t *entry = inherited; *entry; entry += wcslen(entry) + 1) {
|
||||
const wchar_t *equals = wcschr(entry, L'=');
|
||||
bool replace = false;
|
||||
@@ -292,7 +292,7 @@ ss_process_start(struct ss_process *process, uint16_t control_port)
|
||||
PROCESS_INFORMATION child = {0};
|
||||
startup.cb = sizeof(startup);
|
||||
process->job = CreateJobObjectW(NULL, NULL);
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limits = {0};
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limits = {};
|
||||
limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
BOOL ok = process->job != NULL && SetInformationJobObject(process->job,
|
||||
JobObjectExtendedLimitInformation, &limits, sizeof(limits));
|
||||
@@ -27,6 +27,10 @@
|
||||
#include "crypto.h"
|
||||
#include "jconf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct listen_ctx {
|
||||
ss_io io;
|
||||
int remote_num;
|
||||
@@ -75,4 +79,8 @@ typedef struct remote {
|
||||
struct sockaddr *addr;
|
||||
} remote_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _REDIR_H
|
||||
@@ -35,6 +35,10 @@
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ss_loop;
|
||||
struct resolv_query;
|
||||
|
||||
@@ -44,4 +48,8 @@ void resolv_start(const char *hostname, uint16_t port,
|
||||
void (*free_cb)(void *), void *data);
|
||||
void resolv_shutdown(struct ss_loop *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -40,7 +40,7 @@ new_rule()
|
||||
{
|
||||
rule_t *rule;
|
||||
|
||||
rule = calloc(1, sizeof(rule_t));
|
||||
rule = (rule_t *)calloc(1, sizeof(rule_t));
|
||||
if (rule == NULL) {
|
||||
ERROR("malloc");
|
||||
return NULL;
|
||||
@@ -38,6 +38,10 @@
|
||||
#include <pcre2.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct rule {
|
||||
struct ss_list_item entries;
|
||||
|
||||
@@ -61,4 +65,8 @@ void remove_rule(rule_t *);
|
||||
rule_t *new_rule();
|
||||
int accept_rule_arg(rule_t *, const char *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -38,6 +38,10 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct listen_ctx {
|
||||
ss_io io;
|
||||
int fd;
|
||||
@@ -114,4 +118,8 @@ typedef struct remote {
|
||||
struct server *server;
|
||||
} remote_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _SERVER_H
|
||||
@@ -23,6 +23,10 @@
|
||||
#ifndef _SOCKS5_H
|
||||
#define _SOCKS5_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SVERSION 0x05
|
||||
#define METHOD_NOAUTH 0x00
|
||||
#define METHOD_UNACCEPTABLE 0xff
|
||||
@@ -72,4 +76,8 @@ struct socks5_response {
|
||||
unsigned char atyp;
|
||||
} __attribute__((packed, aligned(1)));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _SOCKS5_H
|
||||
@@ -3,6 +3,10 @@
|
||||
#ifndef SS_EVENT_H
|
||||
#define SS_EVENT_H
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* Keep platform headers private: Linux TCP headers differ from libc headers. */
|
||||
struct uv_loop_s;
|
||||
struct ss_loop;
|
||||
@@ -55,4 +59,8 @@ void ss_timer_again(struct ss_loop *, ss_timer *);
|
||||
void ss_signal_init(ss_signal *, void (*)(struct ss_loop *, ss_signal *, int), int);
|
||||
void ss_signal_start(struct ss_loop *, ss_signal *);
|
||||
void ss_signal_stop(struct ss_loop *, ss_signal *);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -3,6 +3,10 @@
|
||||
#define SS_PROCESS_H
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
struct ss_process;
|
||||
struct ss_process *ss_process_new(const char *program);
|
||||
void ss_process_arg(struct ss_process *process, const char *argument);
|
||||
@@ -10,4 +14,8 @@ void ss_process_env(struct ss_process *process, const char *name, const char *va
|
||||
int ss_process_start(struct ss_process *process, uint16_t control_port);
|
||||
bool ss_process_running(struct ss_process *process);
|
||||
void ss_process_free(struct ss_process *process);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -44,6 +44,10 @@
|
||||
#include <ws2tcpip.h>
|
||||
#include <mswsock.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Check if ConnectEx supported in header
|
||||
#ifdef WSAID_CONNECTEX
|
||||
// Hardcode TCP fast open option
|
||||
@@ -80,6 +84,10 @@ LPFN_CONNECTEX winsock_getconnectex(void);
|
||||
int winsock_dummybind(SOCKET fd, struct sockaddr *sa);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __MINGW32__
|
||||
|
||||
#endif // SS_WINSOCK_COMPAT_H
|
||||
@@ -50,7 +50,7 @@ find_last(const char *s, size_t len, char c)
|
||||
static char *
|
||||
str_ndup(const char *s, size_t n)
|
||||
{
|
||||
char *out = ss_malloc(n + 1);
|
||||
char *out = (char *)ss_malloc(n + 1);
|
||||
|
||||
memcpy(out, s, n);
|
||||
out[n] = '\0';
|
||||
@@ -73,7 +73,7 @@ hex_value(char c)
|
||||
static char *
|
||||
percent_decode(const char *s, size_t len)
|
||||
{
|
||||
char *out = ss_malloc(len + 1);
|
||||
char *out = (char *)ss_malloc(len + 1);
|
||||
size_t o = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
@@ -106,7 +106,7 @@ base64_dup(const char *s, size_t len)
|
||||
|
||||
char *in = str_ndup(s, len);
|
||||
/* decoded output is always shorter than the encoded input */
|
||||
uint8_t *out = ss_malloc(len + 1);
|
||||
uint8_t *out = (uint8_t *)ss_malloc(len + 1);
|
||||
|
||||
int out_len = base64_decode(out, in, (int)len);
|
||||
ss_free(in);
|
||||
@@ -131,7 +131,7 @@ split_host_port(const char *s, size_t len, ss_url_t *url)
|
||||
return -1;
|
||||
|
||||
if (s[0] == '[') {
|
||||
const char *close = memchr(s, ']', len);
|
||||
const char *close = (const char *)memchr(s, ']', len);
|
||||
if (close == NULL)
|
||||
return -1;
|
||||
|
||||
@@ -186,9 +186,9 @@ parse_query(const char *s, size_t len, ss_url_t *url)
|
||||
const char *end = s + len;
|
||||
|
||||
while (s < end) {
|
||||
const char *amp = memchr(s, '&', (size_t)(end - s));
|
||||
const char *amp = (const char *)memchr(s, '&', (size_t)(end - s));
|
||||
const char *pair_end = amp ? amp : end;
|
||||
const char *eq = memchr(s, '=', (size_t)(pair_end - s));
|
||||
const char *eq = (const char *)memchr(s, '=', (size_t)(pair_end - s));
|
||||
|
||||
if (eq != NULL) {
|
||||
size_t name_len = (size_t)(eq - s);
|
||||
@@ -244,13 +244,15 @@ ss_url_parse(const char *uri, ss_url_t *url)
|
||||
return -1;
|
||||
|
||||
/* The fragment is a human-readable tag and is not part of the config. */
|
||||
const char *hash = memchr(body, '#', body_len);
|
||||
// NOLINTNEXTLINE(bugprone-not-null-terminated-result): memchr searches a bounded span, no string is produced
|
||||
const char *hash = (const char *)memchr(body, '#', body_len);
|
||||
if (hash != NULL) {
|
||||
url->tag = percent_decode(hash + 1, body_len - (size_t)(hash - body) - 1);
|
||||
body_len = (size_t)(hash - body);
|
||||
}
|
||||
|
||||
const char *query = memchr(body, '?', body_len);
|
||||
// NOLINTNEXTLINE(bugprone-not-null-terminated-result): memchr searches a bounded span, no string is produced
|
||||
const char *query = (const char *)memchr(body, '?', body_len);
|
||||
size_t head_len = query ? (size_t)(query - body) : body_len;
|
||||
|
||||
/*
|
||||
@@ -23,6 +23,10 @@
|
||||
#ifndef _SSURL_H
|
||||
#define _SSURL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* A parsed ss:// URI. Every field is heap-allocated and owned by the
|
||||
* struct; release them all with ss_url_free(). Fields that the URI did not
|
||||
@@ -55,4 +59,8 @@ int ss_url_parse(const char *uri, ss_url_t *url);
|
||||
/* Free every field and zero the struct. Safe on a zeroed struct. */
|
||||
void ss_url_free(ss_url_t *url);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _SSURL_H
|
||||
@@ -41,6 +41,10 @@
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int stream_encrypt_all(buffer_t *, cipher_t *, size_t);
|
||||
int stream_decrypt_all(buffer_t *, cipher_t *, size_t);
|
||||
int stream_encrypt(buffer_t *, cipher_ctx_t *, size_t);
|
||||
@@ -51,4 +55,8 @@ void stream_ctx_release(cipher_ctx_t *);
|
||||
|
||||
cipher_t *stream_init(const char *pass, const char *key, const char *method);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _STREAM_H
|
||||
@@ -34,6 +34,10 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct listen_ctx {
|
||||
ss_io io;
|
||||
ss_addr_t tunnel_addr;
|
||||
@@ -84,4 +88,8 @@ typedef struct remote {
|
||||
uint32_t counter;
|
||||
} remote_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _TUNNEL_H
|
||||
@@ -38,6 +38,10 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MAX_UDP_PACKET_SIZE (65507)
|
||||
|
||||
#define PACKET_HEADER_SIZE (1 + 28 + 2 + 64)
|
||||
@@ -92,4 +96,8 @@ typedef struct remote_ctx {
|
||||
struct server_ctx *server_ctx;
|
||||
} remote_ctx_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _UDPRELAY_H
|
||||
+8
-6
@@ -164,8 +164,10 @@ run_as(const char *user)
|
||||
size_t buflen;
|
||||
int err;
|
||||
|
||||
/* The loop below gives up beyond 16 KiB, so one fixed buffer covers
|
||||
* every attempt. */
|
||||
char buf[16 * 1024];
|
||||
for (buflen = 128;; buflen *= 2) {
|
||||
char buf[buflen]; /* variable length array */
|
||||
|
||||
/* Note that we use getpwnam_r() instead of getpwnam(),
|
||||
* which returns its result in a statically allocated buffer and
|
||||
@@ -256,7 +258,7 @@ ss_strndup(const char *s, size_t n)
|
||||
len++;
|
||||
}
|
||||
|
||||
ret = ss_malloc(len + 1);
|
||||
ret = (char *)ss_malloc(len + 1);
|
||||
memcpy(ret, s, len);
|
||||
ret[len] = '\0';
|
||||
return ret;
|
||||
@@ -302,13 +304,13 @@ ss_aligned_malloc(size_t size)
|
||||
void *
|
||||
ss_realloc(void *ptr, size_t new_size)
|
||||
{
|
||||
void *new = realloc(ptr, new_size);
|
||||
if (new == NULL) {
|
||||
void *resized = realloc(ptr, new_size);
|
||||
if (resized == NULL) {
|
||||
free(ptr);
|
||||
ptr = NULL;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return new;
|
||||
return resized;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -803,7 +805,7 @@ daemonize(const char *path)
|
||||
int
|
||||
set_nofile(int nofile)
|
||||
{
|
||||
struct rlimit limit = { nofile, nofile }; /* set both soft and hard limit */
|
||||
struct rlimit limit = { (rlim_t)nofile, (rlim_t)nofile }; /* set both soft and hard limit */
|
||||
|
||||
if (nofile <= 0) {
|
||||
FATAL("nofile must be greater than 0\n");
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PORTSTRLEN 16
|
||||
#define SS_ADDRSTRLEN (INET6_ADDRSTRLEN + PORTSTRLEN + 1)
|
||||
|
||||
@@ -252,4 +256,8 @@ char *get_default_conf(void);
|
||||
uint16_t load16_be(const void *s);
|
||||
int get_mptcp(int enable);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _UTILS_H
|
||||
+34
-14
@@ -60,6 +60,7 @@ endfunction()
|
||||
# C++ core (src/core): header-only ownership and allocation primitives.
|
||||
foreach(core_test result pool buffer arena fixed_map ownership)
|
||||
ss_add_test(test_core_${core_test} "test_core_${core_test}.cpp" "")
|
||||
ss_strict_warnings(test_core_${core_test})
|
||||
endforeach()
|
||||
|
||||
# C++ sources under src/ must not allocate on the heap (see the script).
|
||||
@@ -68,11 +69,30 @@ if(Python3_Interpreter_FOUND)
|
||||
add_test(NAME check_no_heap
|
||||
COMMAND ${Python3_EXECUTABLE} ${PROJECT_SOURCE_DIR}/scripts/check_no_heap.py --self-test)
|
||||
set_tests_properties(check_no_heap PROPERTIES LABELS "unit")
|
||||
|
||||
# The programs and the static library must stay usable without a C++
|
||||
# runtime library (see docs/cpp-core.md). Sanitizer runtimes bring their
|
||||
# own C++ symbols, so skip the check there.
|
||||
set(SS_RUNTIME_CHECKED)
|
||||
foreach(target ss-local ss-server ss-tunnel ss-redir ss-manager shadowsocks-c-static)
|
||||
if(TARGET ${target})
|
||||
get_target_property(excluded ${target} EXCLUDE_FROM_ALL)
|
||||
if(NOT excluded)
|
||||
list(APPEND SS_RUNTIME_CHECKED $<TARGET_FILE:${target}>)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
if(SS_RUNTIME_CHECKED AND CMAKE_NM AND NOT ENABLE_SANITIZERS)
|
||||
add_test(NAME check_cxx_runtime
|
||||
COMMAND ${Python3_EXECUTABLE} ${PROJECT_SOURCE_DIR}/scripts/check_cxx_runtime.py
|
||||
--nm ${CMAKE_NM} ${SS_RUNTIME_CHECKED})
|
||||
set_tests_properties(check_cxx_runtime PROPERTIES LABELS "unit")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# test_base64 - no external deps
|
||||
ss_add_test(test_base64
|
||||
"test_base64.c;${PROJECT_SOURCE_DIR}/src/base64.c"
|
||||
"test_base64.c;${PROJECT_SOURCE_DIR}/src/base64.cpp"
|
||||
"")
|
||||
|
||||
# test_utils - needs sodium (for ss_malloc -> uses sodium in utils.c)
|
||||
@@ -82,12 +102,12 @@ ss_add_test(test_utils
|
||||
|
||||
# test_json - standalone json parser
|
||||
ss_add_test(test_json
|
||||
"test_json.c;${PROJECT_SOURCE_DIR}/src/json.c"
|
||||
"test_json.c;${PROJECT_SOURCE_DIR}/src/json.cpp"
|
||||
"${SS_MATH_LIBRARY}")
|
||||
|
||||
# test_ssurl - ss:// URI parsing (SIP002 and legacy)
|
||||
ss_add_test(test_ssurl
|
||||
"test_ssurl.c;${PROJECT_SOURCE_DIR}/src/ssurl.c;${PROJECT_SOURCE_DIR}/src/base64.c;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"test_ssurl.c;${PROJECT_SOURCE_DIR}/src/ssurl.cpp;${PROJECT_SOURCE_DIR}/src/base64.cpp;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"ss_sodium")
|
||||
|
||||
# test_netutils - address parsing and resolver integration
|
||||
@@ -102,14 +122,14 @@ ss_add_test(test_netutils
|
||||
|
||||
# test_cache - expiration uses a platform monotonic clock
|
||||
ss_add_test(test_cache
|
||||
"test_cache.c;${PROJECT_SOURCE_DIR}/src/cache.c;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"test_cache.c;${PROJECT_SOURCE_DIR}/src/cache.cpp;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"ss_sodium")
|
||||
|
||||
# test_ppbloom - needs bloom
|
||||
set(TEST_PPBLOOM_LIBS "")
|
||||
list(APPEND TEST_PPBLOOM_LIBS bloom)
|
||||
ss_add_test(test_ppbloom
|
||||
"test_ppbloom.c;${PROJECT_SOURCE_DIR}/src/ppbloom.c;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"test_ppbloom.c;${PROJECT_SOURCE_DIR}/src/ppbloom.cpp;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"${TEST_PPBLOOM_LIBS};ss_sodium;${SS_MATH_LIBRARY}")
|
||||
|
||||
if(SS_ENABLE_REGEX)
|
||||
@@ -119,13 +139,13 @@ set(TEST_RULE_LIBS
|
||||
ss_sodium
|
||||
)
|
||||
ss_add_test(test_rule
|
||||
"test_rule.c;${PROJECT_SOURCE_DIR}/src/rule.c;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"test_rule.c;${PROJECT_SOURCE_DIR}/src/rule.cpp;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"${TEST_RULE_LIBS}")
|
||||
|
||||
# test_acl - ACL precedence and prefix matching
|
||||
set(TEST_ACL_LIBS ${TEST_RULE_LIBS})
|
||||
ss_add_test(test_acl
|
||||
"test_acl.c;${PROJECT_SOURCE_DIR}/src/acl.c;${PROJECT_SOURCE_DIR}/src/ipset.c;${PROJECT_SOURCE_DIR}/src/rule.c;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"test_acl.c;${PROJECT_SOURCE_DIR}/src/acl.c;${PROJECT_SOURCE_DIR}/src/ipset.cpp;${PROJECT_SOURCE_DIR}/src/rule.cpp;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"${TEST_ACL_LIBS}")
|
||||
|
||||
endif()
|
||||
@@ -137,7 +157,7 @@ set(TEST_JCONF_LIBS
|
||||
ss_cares
|
||||
)
|
||||
ss_add_test(test_jconf
|
||||
"test_jconf.c;${PROJECT_SOURCE_DIR}/src/jconf.c;${PROJECT_SOURCE_DIR}/src/json.c;${PROJECT_SOURCE_DIR}/src/utils.c;${PROJECT_SOURCE_DIR}/src/netutils.c"
|
||||
"test_jconf.c;${PROJECT_SOURCE_DIR}/src/jconf.c;${PROJECT_SOURCE_DIR}/src/json.cpp;${PROJECT_SOURCE_DIR}/src/utils.c;${PROJECT_SOURCE_DIR}/src/netutils.c"
|
||||
"${TEST_JCONF_LIBS};${SS_MATH_LIBRARY}")
|
||||
|
||||
# Crypto sources shared by the tests that link the crypto layer. The AEAD-2022
|
||||
@@ -148,9 +168,9 @@ set(SS_CRYPTO_TEST_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/src/aead.c
|
||||
${PROJECT_SOURCE_DIR}/src/aead2022.c
|
||||
${PROJECT_SOURCE_DIR}/src/aead2022_udp.c
|
||||
${PROJECT_SOURCE_DIR}/src/ppbloom.c
|
||||
${PROJECT_SOURCE_DIR}/src/cache.c
|
||||
${PROJECT_SOURCE_DIR}/src/base64.c
|
||||
${PROJECT_SOURCE_DIR}/src/ppbloom.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/cache.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/base64.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/utils.c
|
||||
)
|
||||
|
||||
@@ -200,11 +220,11 @@ add_test(NAME test_ss_setup
|
||||
set_tests_properties(test_ss_setup PROPERTIES LABELS "integration")
|
||||
|
||||
ss_add_test(test_ipset
|
||||
"test_ipset.c;${PROJECT_SOURCE_DIR}/src/ipset.c;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"test_ipset.c;${PROJECT_SOURCE_DIR}/src/ipset.cpp;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"ss_sodium")
|
||||
|
||||
ss_add_test(test_process
|
||||
"test_process.c;${PROJECT_SOURCE_DIR}/src/process.c;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"test_process.c;${PROJECT_SOURCE_DIR}/src/process.cpp;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"ss_sodium")
|
||||
set_tests_properties(test_process PROPERTIES TIMEOUT 20)
|
||||
|
||||
@@ -218,7 +238,7 @@ if(SS_ENABLE_REGEX)
|
||||
list(APPEND TEST_LITERAL_LIBS ss_pcre2)
|
||||
endif()
|
||||
ss_add_test(test_acl_literal
|
||||
"test_acl_literal.c;${PROJECT_SOURCE_DIR}/src/acl.c;${PROJECT_SOURCE_DIR}/src/ipset.c;${PROJECT_SOURCE_DIR}/src/rule.c;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"test_acl_literal.c;${PROJECT_SOURCE_DIR}/src/acl.c;${PROJECT_SOURCE_DIR}/src/ipset.cpp;${PROJECT_SOURCE_DIR}/src/rule.cpp;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"${TEST_LITERAL_LIBS}")
|
||||
|
||||
ss_add_test(test_event "test_event.c" "ss_ev")
|
||||
|
||||
Reference in new issue
Block a user