diff --git a/.clang-tidy b/.clang-tidy index 6c2d98e86..6bb01cefb 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,4 +1,4 @@ -# clang-tidy configuration for shadowsocks-libev project sources (src/ only). +# clang-tidy configuration for shadowsocks-c project sources (src/ only). # Bundled sources under third_party/ and src/blake3/ are third-party code and # are excluded by the file regex used in CI: # run-clang-tidy -p build '/src/[^/]+\.c$' @@ -8,7 +8,7 @@ # Checks disabled below are either stylistic churn (macro-parentheses, # reserved-identifier: the codebase uses _prefixed names and vendored # uthash.h/generated config.h trip them ~600 times) or produce false -# positives with the libev callback style (casting-through-void, +# positives with the event callback style (casting-through-void, # multi-level-implicit-pointer-conversion). Checks: > clang-analyzer-*, diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 340561992..453054152 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,11 +20,11 @@ jobs: if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install -y libpcre2-dev libmbedtls-dev libsodium-dev libev-dev libc-ares-dev + sudo apt-get install -y libpcre2-dev libmbedtls-dev libsodium-dev libuv1-dev libc-ares-dev - name: Install dependencies (macOS) if: runner.os == 'macOS' - run: brew install mbedtls@3 libsodium libev c-ares pcre2 + run: brew install mbedtls@3 libsodium libuv c-ares pcre2 - name: Build run: | @@ -41,7 +41,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y libpcre2-dev libmbedtls-dev libsodium-dev libev-dev libc-ares-dev + sudo apt-get install -y libpcre2-dev libmbedtls-dev libsodium-dev libuv1-dev libc-ares-dev sudo apt-get install -y --no-install-recommends \ build-essential cmake debhelper dpkg-dev fakeroot asciidoc-base xmlto pkg-config diff --git a/.github/workflows/portability.yml b/.github/workflows/portability.yml index 04c150706..37eee2bd2 100644 --- a/.github/workflows/portability.yml +++ b/.github/workflows/portability.yml @@ -24,6 +24,8 @@ jobs: ctest --test-dir build -L 'unit|vendor' --output-on-failure --no-tests=error - name: Real TCP and UDP relay run: python3 tests/interop.py --self --bin build/bin + - name: Asynchronous hostname ACL bypass + run: python3 tests/async_dns_interop.py --bin build/bin - name: SIP003 transport and child cleanup if: matrix.minimal == 'OFF' run: python3 tests/interop.py --self --bin build/bin --plugin tests/sip003_fixture.py @@ -35,6 +37,10 @@ jobs: cmake --build consumer ./consumer/consumer_static ./consumer/consumer_shared + cmake -S tests/consumer -B consumer-legacy -DSS_PACKAGE_NAME=shadowsocks-libev -DCMAKE_PREFIX_PATH="$PWD/relocated" + cmake --build consumer-legacy + ./consumer-legacy/consumer_static + ./consumer-legacy/consumer_shared windows: runs-on: windows-latest @@ -59,11 +65,14 @@ jobs: ctest --test-dir build -L 'unit|vendor' --output-on-failure --no-tests=error objdump -p build/bin/ss-server.exe | sed -n '/DLL Name/p' python tests/interop.py --self --bin build/bin --isolate-windows-runtime + python tests/async_dns_interop.py --bin build/bin --isolate-windows-runtime - name: Installed consumers run: | cmake --install build --prefix "$PWD/install" cmake -S tests/consumer -B consumer -G Ninja -DCMAKE_PREFIX_PATH="$PWD/install" cmake --build consumer + cmake -S tests/consumer -B consumer-legacy -G Ninja -DSS_PACKAGE_NAME=shadowsocks-libev -DCMAKE_PREFIX_PATH="$PWD/install" + cmake --build consumer-legacy - name: Run installed consumers without toolchain DLLs shell: pwsh run: | @@ -72,6 +81,10 @@ jobs: if ($LASTEXITCODE -ne 0) { throw "Static consumer failed: $LASTEXITCODE" } & ./consumer/consumer_shared.exe if ($LASTEXITCODE -ne 0) { throw "Shared consumer failed: $LASTEXITCODE" } + & ./consumer-legacy/consumer_static.exe + if ($LASTEXITCODE -ne 0) { throw "Legacy static consumer failed: $LASTEXITCODE" } + & ./consumer-legacy/consumer_shared.exe + if ($LASTEXITCODE -ne 0) { throw "Legacy shared consumer failed: $LASTEXITCODE" } static-macos: runs-on: macos-latest @@ -95,6 +108,7 @@ jobs: - name: Exercise installed static programs run: | python3 tests/interop.py --self --bin relocated-static/bin + python3 tests/async_dns_interop.py --bin relocated-static/bin python3 tests/interop.py --self --bin relocated-static/bin --plugin tests/sip003_fixture.py - uses: actions/upload-artifact@v4 with: @@ -132,7 +146,9 @@ jobs: - name: Reject third-party DLL imports, including delay imports run: python tests/check_static_runtime.py relocated-static/bin/ss-*.exe consumer-static/consumer_static.exe - name: Exercise installed programs without toolchain DLLs - run: python tests/interop.py --self --bin relocated-static/bin --isolate-windows-runtime + run: | + python tests/interop.py --self --bin relocated-static/bin --isolate-windows-runtime + python tests/async_dns_interop.py --bin relocated-static/bin --isolate-windows-runtime - name: Run static consumer with only Windows system directories on PATH shell: pwsh run: | @@ -171,6 +187,7 @@ jobs: cmake --build build --parallel 4 ctest --test-dir build -L 'unit|vendor' --output-on-failure --no-tests=error python3 tests/interop.py --self --bin build/bin + python3 tests/async_dns_interop.py --bin build/bin cmake --install build --prefix "$PWD/install" cmake -S tests/consumer -B consumer -G Ninja -DCMAKE_PREFIX_PATH="$PWD/install" cmake --build consumer @@ -182,7 +199,7 @@ jobs: steps: - uses: actions/checkout@v6 - name: Source archive - run: python3 scripts/source_archive.py release/shadowsocks-libev.tar.gz + run: python3 scripts/source_archive.py release/shadowsocks-c.tar.gz - name: Prepare compiler image run: | printf 'FROM alpine:3.24\nRUN apk add --no-cache build-base cmake python3 linux-headers\n' > /tmp/ss-toolchain.Dockerfile @@ -191,7 +208,7 @@ jobs: run: | docker run --rm --network none -v "$PWD/release:/release:ro" ss-toolchain sh -ec ' mkdir /src - tar xzf /release/shadowsocks-libev.tar.gz -C /src --strip-components=1 + tar xzf /release/shadowsocks-c.tar.gz -C /src --strip-components=1 cmake -S /src -B /build -DCMAKE_DISABLE_FIND_PACKAGE_Python3=TRUE cmake --build /build --parallel 4 ctest --test-dir /build -L "unit|vendor" --output-on-failure --no-tests=error diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9af7418d9..3bfcfd5ce 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,6 +7,26 @@ on: branches: [master] jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 + with: + go-version: '1.25' + cache: false + - uses: actions/setup-python@v6 + with: + python-version: '3.12' + - name: Install pinned linters + run: | + go install github.com/rhysd/actionlint/cmd/actionlint@v1.7.12 + python -m pip install ruff==0.15.6 + - name: Lint GitHub Actions workflows + run: actionlint -shellcheck= -pyflakes= + - name: Lint Python scripts and tests + run: ruff check --select E9,F63,F7,F82 tests scripts + tests: strategy: fail-fast: false @@ -20,11 +40,11 @@ jobs: if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install -y libpcre2-dev libmbedtls-dev libsodium-dev libev-dev libc-ares-dev valgrind + sudo apt-get install -y libpcre2-dev libmbedtls-dev libsodium-dev libuv1-dev libc-ares-dev valgrind - name: Install dependencies (macOS) if: runner.os == 'macOS' - run: brew install mbedtls@3 libsodium libev c-ares pcre2 + run: brew install mbedtls@3 libsodium libuv c-ares pcre2 - name: Build test binaries run: | @@ -62,7 +82,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y libpcre2-dev libmbedtls-dev libsodium-dev libev-dev libc-ares-dev + sudo apt-get install -y libpcre2-dev libmbedtls-dev libsodium-dev libuv1-dev libc-ares-dev - name: Build with ASan + UBSan run: | @@ -91,14 +111,16 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y clang-tidy-18 clang-tools-18 libpcre2-dev libmbedtls-dev libsodium-dev libev-dev libc-ares-dev + sudo apt-get install -y clang-tidy-18 clang-tools-18 libpcre2-dev libmbedtls-dev libsodium-dev libuv1-dev libc-ares-dev - name: Configure (compile_commands.json) run: cmake -S . -B build - name: Run clang-tidy on project sources run: | - run-clang-tidy-18 -quiet -p build "$PWD/src/[^/]+\.c$" 2>/dev/null | tee tidy.log || true + set -o pipefail + tidy_status=0 + run-clang-tidy-18 -quiet -p build "$PWD/src/[^/]+\.c$" 2>&1 | tee tidy.log || tidy_status=$? grep 'warning:' tidy.log | sort -u > warnings.txt || true count="$(wc -l < warnings.txt)" echo "clang-tidy warnings: $count (max allowed: $MAX_WARNINGS)" @@ -108,8 +130,8 @@ jobs: cat warnings.txt echo '```' } >> "$GITHUB_STEP_SUMMARY" - if ! grep -q . warnings.txt && grep -qE 'error:' tidy.log; then - echo "clang-tidy produced errors:"; grep -E 'error:' tidy.log | sort -u + if [ "$tidy_status" -ne 0 ] || grep -qE 'error:' tidy.log; then + echo "clang-tidy failed (exit status: $tidy_status). See tidy.log for diagnostics." exit 1 fi if [ "$count" -gt "$MAX_WARNINGS" ]; then @@ -118,6 +140,16 @@ jobs: exit 1 fi + - name: Upload clang-tidy diagnostics + if: always() + uses: actions/upload-artifact@v4 + with: + name: clang-tidy-diagnostics + path: | + tidy.log + warnings.txt + if-no-files-found: ignore + coverage: runs-on: ubuntu-latest steps: @@ -126,7 +158,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y lcov libpcre2-dev libmbedtls-dev libsodium-dev libev-dev libc-ares-dev + sudo apt-get install -y lcov libpcre2-dev libmbedtls-dev libsodium-dev libuv1-dev libc-ares-dev - name: Build with coverage instrumentation run: | @@ -160,7 +192,7 @@ jobs: steps: - uses: actions/checkout@v6 - name: Install dependencies and independent peer - run: brew install mbedtls@3 libsodium libev c-ares pcre2 shadowsocks-rust + run: brew install mbedtls@3 libsodium libuv c-ares pcre2 shadowsocks-rust - name: Build run: | cmake -S . -B build -DSS_DEPENDENCY_MODE=system -DWITH_STATIC=OFF diff --git a/CMakeLists.txt b/CMakeLists.txt index 76188be45..e3732e696 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) -set(PROJECT_NAME shadowsocks-libev) +set(PROJECT_NAME shadowsocks-c) set(RELEASE_DATE 2026-02-09) set(PROJECT_VERSION "3.3.6") set(PROJECT_DESC "a lightweight secured socks5 proxy") @@ -98,6 +98,7 @@ endif() include(${PROJECT_SOURCE_DIR}/cmake/configure.cmake) configure_file(${PROJECT_SOURCE_DIR}/cmake/config.h.cmake ${PROJECT_BINARY_DIR}/src/config.h) include(cmake/ProjectOptions.cmake) +ss_project_options(ss_ev) include_directories(${PROJECT_BINARY_DIR}/src) add_compile_definitions(HAVE_CONFIG_H) diff --git a/README.md b/README.md index 234467d90..a42c34b7a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# shadowsocks-libev +# shadowsocks-c [![Build Status](https://travis-ci.com/shadowsocks/shadowsocks-libev.svg?branch=master)](https://travis-ci.com/shadowsocks/shadowsocks-libev) [![Snap Status](https://snapcraft.io/shadowsocks-libev/badge.svg)](https://snapcraft.io/shadowsocks-libev) ## Intro -[Shadowsocks-libev](https://shadowsocks.org) is a lightweight secured SOCKS5 +[shadowsocks-c](https://shadowsocks.org) is a lightweight secured SOCKS5 proxy for embedded devices and low-end boxes. It is a port of [Shadowsocks](https://github.com/shadowsocks/shadowsocks) @@ -15,12 +15,19 @@ Current version: 3.3.6 | [Changelog](debian/changelog) ## Features -Shadowsocks-libev is written in pure C and depends on [libev](http://software.schmorp.de/pkg/libev.html). It's designed +shadowsocks-c is written in pure C and depends on [libuv](https://libuv.org/). It's designed to be a lightweight implementation of shadowsocks protocol, in order to keep the resource usage as low as possible. For a full list of feature comparison between different versions of shadowsocks, refer to the [Wiki page](https://github.com/shadowsocks/shadowsocks/wiki/Feature-Comparison-across-Different-Versions). +The project is now **shadowsocks-c**. Commands (`ss-local`, `ss-server`, etc.) +and the `shadowsocks.h` API remain compatible. New builds produce +`libshadowsocks-c` with CMake/pkg-config package `shadowsocks-c`; legacy library +filenames and package lookup names remain available as compatibility aliases. +Existing configuration paths, distro package names and service names are retained. +Repository links below still point to the current GitHub repository. + ## Quick Start Snap is the recommended way to install the latest binaries. @@ -93,7 +100,7 @@ IPv4/IPv6 CIDRs, `full:example.com` for exact domains, and case and respect label boundaries. Unsupported regex rules are rejected. Distribution packages can use `-DSS_DEPENDENCY_MODE=system -DWITH_STATIC=OFF` -with libev, c-ares, libsodium, Mbed TLS 3.x, and PCRE2 development packages. +with libuv, c-ares, libsodium, Mbed TLS 3.x, and PCRE2 development packages. Use `-DCMAKE_PREFIX_PATH=/opt/homebrew/opt/mbedtls@3` when needed on macOS. | Option | Default | Purpose | @@ -110,7 +117,7 @@ Use `-DCMAKE_PREFIX_PATH=/opt/homebrew/opt/mbedtls@3` when needed on macOS. | `ENABLE_SANITIZERS` | `OFF` | AddressSanitizer and UndefinedBehaviorSanitizer | | `ENABLE_CONNMARKTOS` / `ENABLE_NFTABLES` | `OFF` | Optional Linux firewall integrations | -CMake consumers can use `find_package(shadowsocks-libev CONFIG REQUIRED)` and +CMake consumers can use `find_package(shadowsocks-c CONFIG REQUIRED)` and link `shadowsocks::static`, `shadowsocks::shared`, or `shadowsocks::shadowsocks` (which prefers the shared library when installed). A pkg-config file is also installed. Dependency provenance and update instructions are in @@ -196,7 +203,7 @@ ctest --test-dir build -L 'unit|vendor' --output-on-failure sudo cmake --install build ``` -Distribution packagers can install `libpcre2-dev libev-dev libc-ares-dev +Distribution packagers can install `libpcre2-dev libuv1-dev libc-ares-dev libmbedtls-dev libsodium-dev` and select `-DSS_DEPENDENCY_MODE=system -DWITH_STATIC=OFF`. Documentation additionally needs asciidoc and xmlto. @@ -275,7 +282,7 @@ cmake --build build-windows --parallel Cross-compilation does not run Windows tests. The portability workflow runs native UCRT64 tests and TCP/UDP relay checks on Windows. Bundled mode is required -because libev must use the project's Winsock handle adapter. MSVC remains a +with a bundled libuv IOCP backend. MSVC remains a separate, unsupported milestone; configuration reports this explicitly. The historical Autotools scripts in `docker/mingw` are superseded by this build. diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 3dd28b451..70ce18d0d 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -15,7 +15,7 @@ elseif(NOT SS_DEPENDENCY_MODE STREQUAL "system") message(FATAL_ERROR "SS_DEPENDENCY_MODE must be bundled or system") endif() if(WIN32) - message(FATAL_ERROR "Windows requires bundled dependencies: libev must use this project's Winsock handle adapter") + message(FATAL_ERROR "Windows requires bundled dependencies: the bundled libuv configuration is the validated Windows build") endif() # Select one linkage mode consistently for programs, libraries, and tests. @@ -30,10 +30,8 @@ endif() find_package(MbedTLS REQUIRED) find_package(Sodium REQUIRED) find_package(Cares REQUIRED) -find_path(LIBEV_INCLUDE_DIR ev.h) -find_library(LIBEV_LIBRARY NAMES ev) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libev REQUIRED_VARS LIBEV_LIBRARY LIBEV_INCLUDE_DIR) +find_path(LIBUV_INCLUDE_DIR uv.h REQUIRED) +find_library(LIBUV_LIBRARY NAMES uv REQUIRED) find_package(Threads REQUIRED) if(WITH_STATIC) set(CMAKE_FIND_LIBRARY_SUFFIXES ${_ss_saved_suffixes}) @@ -51,7 +49,8 @@ ss_import_dependency(ss_cares "${CARES_LIBRARY}" "${CARES_INCLUDE_DIRS}") if(SS_ENABLE_REGEX) ss_import_dependency(ss_pcre2 "${PCRE2_LIBRARY}" "${PCRE2_INCLUDE_DIRS}") endif() -ss_import_dependency(ss_ev "${LIBEV_LIBRARY}" "${LIBEV_INCLUDE_DIR}") +ss_import_dependency(ss_uv "${LIBUV_LIBRARY}" "${LIBUV_INCLUDE_DIR}") +include(${PROJECT_SOURCE_DIR}/cmake/EventLoop.cmake) if(WIN32) set_property(TARGET ss_cares APPEND PROPERTY INTERFACE_LINK_LIBRARIES ws2_32 iphlpapi) set_property(TARGET ss_ev APPEND PROPERTY INTERFACE_LINK_LIBRARIES ws2_32) diff --git a/cmake/EventLoop.cmake b/cmake/EventLoop.cmake new file mode 100644 index 000000000..f60dd3032 --- /dev/null +++ b/cmake/EventLoop.cmake @@ -0,0 +1,6 @@ +add_library(ss_ev STATIC ${PROJECT_SOURCE_DIR}/src/ss_event.c) +target_include_directories(ss_ev PUBLIC ${PROJECT_SOURCE_DIR}/src) +target_link_libraries(ss_ev PUBLIC ss_uv Threads::Threads ${SS_MATH_LIBRARY} ${CMAKE_DL_LIBS}) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(ss_ev PUBLIC rt) +endif() diff --git a/cmake/InstallPackage.cmake b/cmake/InstallPackage.cmake index 4db28a13f..16cc97ef3 100644 --- a/cmake/InstallPackage.cmake +++ b/cmake/InstallPackage.cmake @@ -5,37 +5,41 @@ set(SS_PC_PRIVATE "") set(private_links) set(private_content "# Generated private linkage for the installed static library.\n") if(SS_BUILD_STATIC_LIBRARY) - set(private_targets bloom blake3) + set(private_targets bloom blake3 ss_ev) if(SS_DEPENDENCY_MODE STREQUAL "bundled") - list(APPEND private_targets ss_sodium ss_mbedcrypto ss_ev ss_cares) + list(APPEND private_targets ss_sodium ss_mbedcrypto ss_uv ss_cares) if(SS_ENABLE_REGEX) list(APPEND private_targets ss_pcre2) endif() else() set(SS_SYSTEM_DEPENDENCIES - "list(APPEND CMAKE_MODULE_PATH \"\${CMAKE_CURRENT_LIST_DIR}/modules\")\n find_dependency(MbedTLS ${MBEDTLS_VERSION} EXACT)\n find_dependency(Sodium)\n find_dependency(Cares)\n find_library(SS_CONSUMER_EV NAMES ev REQUIRED)") + "list(APPEND CMAKE_MODULE_PATH \"\${CMAKE_CURRENT_LIST_DIR}/modules\")\n find_dependency(MbedTLS ${MBEDTLS_VERSION} EXACT)\n find_dependency(Sodium)\n find_dependency(Cares)\n find_library(SS_CONSUMER_UV NAMES uv REQUIRED)") if(SS_ENABLE_REGEX) string(APPEND SS_SYSTEM_DEPENDENCIES "\n find_dependency(PCRE2)") list(APPEND private_links "\${PCRE2_LIBRARY}") string(APPEND SS_PC_PRIVATE " -lpcre2-8") endif() list(APPEND private_links "\${SODIUM_LIBRARY}" "\${MBEDTLS_CRYPTO_LIBRARY}" - "\${CARES_LIBRARY}" "\${SS_CONSUMER_EV}") - string(APPEND SS_PC_PRIVATE " -lsodium -lmbedcrypto -lcares -lev") + "\${CARES_LIBRARY}" ) + string(APPEND SS_PC_PRIVATE " -lsodium -lmbedcrypto -lcares") install(FILES cmake/FindMbedTLS.cmake cmake/FindSodium.cmake cmake/FindCares.cmake cmake/FindPCRE2.cmake cmake/mbedtls_version_check.c - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/shadowsocks-libev/modules) + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/shadowsocks-c/modules) endif() foreach(target IN LISTS private_targets) install(FILES $ - DESTINATION ${CMAKE_INSTALL_LIBDIR}/shadowsocks-libev) - list(APPEND private_links "\${_ss_libdir}/shadowsocks-libev/$") - string(APPEND SS_PC_PRIVATE " \${libdir}/shadowsocks-libev/$") + DESTINATION ${CMAKE_INSTALL_LIBDIR}/shadowsocks-c) + list(APPEND private_links "\${_ss_libdir}/shadowsocks-c/$") + string(APPEND SS_PC_PRIVATE " \${libdir}/shadowsocks-c/$") endforeach() + if(SS_DEPENDENCY_MODE STREQUAL "system") + list(APPEND private_links "\${SS_CONSUMER_UV}") + string(APPEND SS_PC_PRIVATE " -luv") + endif() list(APPEND private_links Threads::Threads ${SS_MATH_LIBRARY} ${CMAKE_DL_LIBS}) if(WIN32) - list(APPEND private_links ws2_32 iphlpapi bcrypt advapi32) - string(APPEND SS_PC_PRIVATE " -lws2_32 -liphlpapi -lbcrypt -ladvapi32") + list(APPEND private_links ws2_32 iphlpapi bcrypt advapi32 user32 userenv psapi dbghelp ole32 shell32) + string(APPEND SS_PC_PRIVATE " -lws2_32 -liphlpapi -lbcrypt -ladvapi32 -luser32 -luserenv -lpsapi -ldbghelp -lole32 -lshell32") else() string(APPEND SS_PC_PRIVATE " -lm -pthread") if(APPLE) @@ -43,36 +47,42 @@ if(SS_BUILD_STATIC_LIBRARY) string(APPEND SS_PC_PRIVATE " -lresolv") endif() endif() + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + list(APPEND private_links rt) + string(APPEND SS_PC_PRIVATE " -lrt") + endif() # Resolve libdir relative to this installed config, supporting --prefix relocation. - file(RELATIVE_PATH libdir_from_config "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/shadowsocks-libev" "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}") + file(RELATIVE_PATH libdir_from_config "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/shadowsocks-c" "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}") string(APPEND private_content "get_filename_component(_ss_libdir \"\${CMAKE_CURRENT_LIST_DIR}/${libdir_from_config}\" ABSOLUTE)\n") string(APPEND private_content "set_property(TARGET shadowsocks::static PROPERTY INTERFACE_LINK_LIBRARIES \"${private_links}\")\n") endif() file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-private.cmake" CONTENT "${private_content}") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-private.cmake" - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/shadowsocks-libev) + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/shadowsocks-c) -set(SS_STATIC_FILENAME "${CMAKE_STATIC_LIBRARY_PREFIX}shadowsocks-libev${CMAKE_STATIC_LIBRARY_SUFFIX}") -set(SS_SHARED_FILENAME "${CMAKE_SHARED_LIBRARY_PREFIX}shadowsocks-libev${CMAKE_SHARED_LIBRARY_SUFFIX}") -set(SS_SHARED_IMPLIB "${CMAKE_IMPORT_LIBRARY_PREFIX}shadowsocks-libev${CMAKE_IMPORT_LIBRARY_SUFFIX}") -configure_package_config_file(cmake/shadowsocks-libev-config.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-libev-config.cmake" - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/shadowsocks-libev +set(SS_STATIC_FILENAME "${CMAKE_STATIC_LIBRARY_PREFIX}shadowsocks-c${CMAKE_STATIC_LIBRARY_SUFFIX}") +set(SS_SHARED_FILENAME "${CMAKE_SHARED_LIBRARY_PREFIX}shadowsocks-c${CMAKE_SHARED_LIBRARY_SUFFIX}") +set(SS_SHARED_IMPLIB "${CMAKE_IMPORT_LIBRARY_PREFIX}shadowsocks-c${CMAKE_IMPORT_LIBRARY_SUFFIX}") +configure_package_config_file(cmake/shadowsocks-c-config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-c-config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/shadowsocks-c PATH_VARS CMAKE_INSTALL_LIBDIR CMAKE_INSTALL_BINDIR CMAKE_INSTALL_INCLUDEDIR) -write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-libev-config-version.cmake" +write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-c-config-version.cmake" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-libev-config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-libev-config-version.cmake" - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/shadowsocks-libev) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-c-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-c-config-version.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/shadowsocks-c) file(RELATIVE_PATH SS_PC_PREFIX_RELATIVE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/pkgconfig" "${CMAKE_CURRENT_BINARY_DIR}") -configure_file(cmake/shadowsocks-libev.pc.cmake "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-libev.pc.in" @ONLY) -file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/shadowsocks-libev.pc" - INPUT "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-libev.pc.in") -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/shadowsocks-libev.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +configure_file(cmake/shadowsocks-c.pc.cmake "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-c.pc.in" @ONLY) +file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/shadowsocks-c.pc" + INPUT "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-c.pc.in") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/shadowsocks-c.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) endif() install(FILES COPYING LICENSE third_party/README.md third_party/manifest.json - DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-libev) -install(FILES third_party/bloom/LICENSE DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-libev/bloom) -install(DIRECTORY src/blake3/ DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-libev/blake3 + DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-c) +install(FILES third_party/bloom/LICENSE DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-c/bloom) +install(DIRECTORY src/blake3/ DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-c/blake3 FILES_MATCHING PATTERN "LICENSE*") + +include(${PROJECT_SOURCE_DIR}/cmake/LegacyPackage.cmake) diff --git a/cmake/LegacyPackage.cmake b/cmake/LegacyPackage.cmake new file mode 100644 index 000000000..9aa02ff9a --- /dev/null +++ b/cmake/LegacyPackage.cmake @@ -0,0 +1,50 @@ +# Preserve installed library filenames for existing build scripts and binaries. +# Unix aliases are relative symlinks so the installation remains relocatable. +# Windows uses copies because creating symlinks may require elevated privileges. +function(ss_legacy_library target directory filename source) + # filename is supplied with its canonical project name replaced below. + string(REPLACE "shadowsocks-c" "shadowsocks-libev" legacy "${filename}") + if(WIN32) + install(FILES "${source}" DESTINATION "${directory}" RENAME "${legacy}") + else() + install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${directory}\") + file(CREATE_LINK \"$\" + \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${directory}/${legacy}\" SYMBOLIC)") + endif() +endfunction() + +foreach(kind STATIC SHARED) + if(SS_BUILD_${kind}_LIBRARY) + string(TOLOWER "${kind}" suffix) + set(target shadowsocks-c-${suffix}) + if(WIN32 AND kind STREQUAL "SHARED") + ss_legacy_library(${target} "${CMAKE_INSTALL_BINDIR}" + "${CMAKE_SHARED_LIBRARY_PREFIX}shadowsocks-c${CMAKE_SHARED_LIBRARY_SUFFIX}" "$") + ss_legacy_library(${target} "${CMAKE_INSTALL_LIBDIR}" + "${CMAKE_IMPORT_LIBRARY_PREFIX}shadowsocks-c${CMAKE_IMPORT_LIBRARY_SUFFIX}" "$") + elseif(kind STREQUAL "STATIC") + ss_legacy_library(${target} "${CMAKE_INSTALL_LIBDIR}" + "${CMAKE_STATIC_LIBRARY_PREFIX}shadowsocks-c${CMAKE_STATIC_LIBRARY_SUFFIX}" "$") + else() + ss_legacy_library(${target} "${CMAKE_INSTALL_LIBDIR}" + "${CMAKE_SHARED_LIBRARY_PREFIX}shadowsocks-c${CMAKE_SHARED_LIBRARY_SUFFIX}" "$") + if(APPLE) + set(legacy_soname "${CMAKE_SHARED_LIBRARY_PREFIX}shadowsocks-c.2${CMAKE_SHARED_LIBRARY_SUFFIX}") + else() + set(legacy_soname "${CMAKE_SHARED_LIBRARY_PREFIX}shadowsocks-c${CMAKE_SHARED_LIBRARY_SUFFIX}.2") + endif() + ss_legacy_library(${target} "${CMAKE_INSTALL_LIBDIR}" "${legacy_soname}" "$") + endif() + endif() +endforeach() + +if(SS_BUILD_STATIC_LIBRARY OR SS_BUILD_SHARED_LIBRARY) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-libev-config.cmake" + "include(CMakeFindDependencyMacro)\nfind_dependency(shadowsocks-c \${shadowsocks-libev_FIND_VERSION} CONFIG PATHS \"\${CMAKE_CURRENT_LIST_DIR}/../shadowsocks-c\" NO_DEFAULT_PATH)\n") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-libev-config.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/shadowsocks-libev) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/package/shadowsocks-c-config-version.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/shadowsocks-libev RENAME shadowsocks-libev-config-version.cmake) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/shadowsocks-c.pc" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig RENAME shadowsocks-libev.pc) +endif() diff --git a/cmake/config.h.cmake b/cmake/config.h.cmake index de889cc81..bf19aa559 100644 --- a/cmake/config.h.cmake +++ b/cmake/config.h.cmake @@ -17,8 +17,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_DLFCN_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_EV_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_FCNTL_H 1 diff --git a/cmake/configure.cmake b/cmake/configure.cmake index 2c6d2496d..1f25c2300 100644 --- a/cmake/configure.cmake +++ b/cmake/configure.cmake @@ -26,7 +26,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL Darwin) endif () check_include_files(dlfcn.h HAVE_DLFCN_H) -check_include_files(ev.h HAVE_EV_H) check_include_files(fcntl.h HAVE_FCNTL_H) check_function_exists(fork HAVE_FORK) check_function_exists(getpwnam_r HAVE_GETPWNAM_R) diff --git a/cmake/shadowsocks-libev-config.cmake.in b/cmake/shadowsocks-c-config.cmake.in similarity index 97% rename from cmake/shadowsocks-libev-config.cmake.in rename to cmake/shadowsocks-c-config.cmake.in index 6708b8fef..708089f92 100644 --- a/cmake/shadowsocks-libev-config.cmake.in +++ b/cmake/shadowsocks-c-config.cmake.in @@ -31,4 +31,4 @@ if(NOT TARGET shadowsocks::shadowsocks) add_library(shadowsocks::shadowsocks ALIAS shadowsocks::static) endif() endif() -check_required_components(shadowsocks-libev) +check_required_components(shadowsocks-c) diff --git a/cmake/shadowsocks-libev.pc.cmake b/cmake/shadowsocks-c.pc.cmake similarity index 89% rename from cmake/shadowsocks-libev.pc.cmake rename to cmake/shadowsocks-c.pc.cmake index eb1dfa339..3303d18a6 100644 --- a/cmake/shadowsocks-libev.pc.cmake +++ b/cmake/shadowsocks-c.pc.cmake @@ -9,5 +9,5 @@ URL: @PROJECT_URL@ Version: @PROJECT_VERSION@ Requires: Cflags: -I${includedir} -Libs: -L${libdir} -lshadowsocks-libev +Libs: -L${libdir} -lshadowsocks-c Libs.private: @SS_PC_PRIVATE@ diff --git a/debian/control b/debian/control index 30317194f..afccd13e7 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,7 @@ Build-Depends: asciidoc-base | asciidoc, debhelper (>= 10), libc-ares-dev, - libev-dev, + libuv1-dev, libmbedtls-dev, libpcre2-dev, libsodium-dev (>= 1.0.12), @@ -53,7 +53,7 @@ Breaks: shadowsocks-libev (<< 2.4.0) Depends: libshadowsocks-libev2 (= ${binary:Version}), libc-ares-dev, - libev-dev, + libuv1-dev, libmbedtls-dev, libpcre2-dev, libsodium-dev, diff --git a/debian/libshadowsocks-libev-dev.install b/debian/libshadowsocks-libev-dev.install index 60b6f6461..49fcd1c51 100644 --- a/debian/libshadowsocks-libev-dev.install +++ b/debian/libshadowsocks-libev-dev.install @@ -1,6 +1,9 @@ usr/include/ usr/lib/*/libshadowsocks-libev.so usr/lib/*/libshadowsocks-libev.a -usr/lib/*/shadowsocks-libev/ +usr/lib/*/shadowsocks-c/ usr/lib/*/cmake/shadowsocks-libev/ usr/lib/*/pkgconfig/ +usr/lib/*/libshadowsocks-c.so +usr/lib/*/libshadowsocks-c.a +usr/lib/*/cmake/shadowsocks-c/ diff --git a/debian/libshadowsocks-libev2.install b/debian/libshadowsocks-libev2.install index 590078a3d..8b8b1860b 100644 --- a/debian/libshadowsocks-libev2.install +++ b/debian/libshadowsocks-libev2.install @@ -1 +1,2 @@ usr/lib/*/libshadowsocks-libev.so.* +usr/lib/*/libshadowsocks-c.so.* diff --git a/debian/shadowsocks-libev.install b/debian/shadowsocks-libev.install index cad7d5a6b..c7de49830 100644 --- a/debian/shadowsocks-libev.install +++ b/debian/shadowsocks-libev.install @@ -4,5 +4,5 @@ debian/config.json usr/share/shadowsocks-libev debian/shadowsocks-libev-*.service lib/systemd/system usr/bin/ usr/share/man/ -usr/share/doc/shadowsocks-libev/ -usr/share/licenses/shadowsocks-libev/ +usr/share/doc/shadowsocks-c/ +usr/share/licenses/shadowsocks-c/ diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 8c47c85ff..b996f9eef 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -27,7 +27,7 @@ set(ASCIIDOC_XML_OPTS -b docbook -d manpage -f ${DOC_DIR}/asciidoc.conf -aversio set(ASCIIDOC_HTML_OPTS -b html4 -d article -f ${DOC_DIR}/asciidoc.conf -aversion=${PROJECT_VERSION}) -set(MAN_NAMES ss-local.1 ss-manager.1 ss-nat.1 ss-redir.1 ss-server.1 ss-tunnel.1 shadowsocks-libev.8) +set(MAN_NAMES ss-local.1 ss-manager.1 ss-nat.1 ss-redir.1 ss-server.1 ss-tunnel.1 shadowsocks-c.8) set(MAN_FILES) set(HTML_FILES) @@ -86,3 +86,6 @@ endif () # This is required for custom command file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/man) file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/html) + +# Retain the former manual lookup name. +file(WRITE "${PROJECT_BINARY_DIR}/man/shadowsocks-libev.8" ".so man8/shadowsocks-c.8\n") diff --git a/doc/shadowsocks-libev.asciidoc b/doc/shadowsocks-c.asciidoc similarity index 96% rename from doc/shadowsocks-libev.asciidoc rename to doc/shadowsocks-c.asciidoc index 078b0849c..9ca5d9fd6 100644 --- a/doc/shadowsocks-libev.asciidoc +++ b/doc/shadowsocks-c.asciidoc @@ -1,9 +1,9 @@ -shadowsocks-libev(8) -==================== +shadowsocks-c(8) +================ NAME ---- -shadowsocks-libev - a lightweight and secure socks5 proxy +shadowsocks-c - a lightweight and secure socks5 proxy SYNOPSIS -------- @@ -13,12 +13,12 @@ SYNOPSIS DESCRIPTION ----------- -*Shadowsocks-libev* is a lightweight and secure socks5 proxy. +*shadowsocks-c* is a lightweight and secure socks5 proxy. It is a port of the original shadowsocks created by clowwindy. -*Shadowsocks-libev* is written in pure C and takes advantage of *libev* +*shadowsocks-c* is written in pure C and takes advantage of *libev* to achieve both high performance and low resource consumption. -*Shadowsocks-libev* consists of five components. One is `ss-server`(1) +*shadowsocks-c* consists of five components. One is `ss-server`(1) that runs on a remote server to provide secured tunnel service. `ss-local`(1) and `ss-redir`(1) are clients on your local machines to proxy traffic(TCP/UDP or both). @@ -56,7 +56,7 @@ Not available in manager mode. -m :: Set the cipher. + -*Shadowsocks-libev* accepts 22 different ciphers: +*shadowsocks-c* accepts 22 different ciphers: + 2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, 2022-blake3-chacha20-poly1305, diff --git a/docker/static/Dockerfile b/docker/static/Dockerfile index 76fdaf454..69e5c2c9c 100644 --- a/docker/static/Dockerfile +++ b/docker/static/Dockerfile @@ -16,6 +16,7 @@ RUN --network=none cmake -S . -B /build \ && cmake --build /build --parallel 4 \ && ctest --test-dir /build -L 'unit|vendor' --output-on-failure --no-tests=error \ && python3 tests/interop.py --self --bin /build/bin \ + && python3 tests/async_dns_interop.py --bin /build/bin \ && cmake --install /build --strip \ && for binary in /out/bin/ss-*; do \ file "$binary"; \ diff --git a/docker/static/README.md b/docker/static/README.md index a946206e7..c8765c0f3 100644 --- a/docker/static/README.md +++ b/docker/static/README.md @@ -29,9 +29,9 @@ Build the scratch runtime image using the same cached build stage: ```sh docker build --platform linux/arm64 -f docker/static/Dockerfile \ - --target runtime -t shadowsocks-libev:static-arm64 . + --target runtime -t shadowsocks-c:static-arm64 . docker run --rm -p 8388:8388/tcp -p 8388:8388/udp \ - shadowsocks-libev:static-arm64 \ + shadowsocks-c:static-arm64 \ -s 0.0.0.0 -p 8388 -k example-password -m aes-256-gcm -u ``` diff --git a/docs/modernization.md b/docs/modernization.md index 4a9aaad79..ed8f19f99 100644 --- a/docs/modernization.md +++ b/docs/modernization.md @@ -2,7 +2,7 @@ Status: complete for the agreed modernization scope. Native MSVC remains an explicitly deferred milestone. Implementation branch: feature/self-contained-portable-c; -review: https://github.com/shadowsocks/shadowsocks-libev/pull/3050 (draft). +review: https://github.com/shadowsocks/shadowsocks-libev/pull/3050 (merged). ## Compatibility contract @@ -59,11 +59,27 @@ SS_BUILD_EXECUTABLES, SS_BUILD_STATIC_LIBRARY and SS_BUILD_SHARED_LIBRARY contro outputs independently. Docs and platform shell tools are explicit opt-ins. Bundled mode is the default and the `bundled` / `minimal` presets exercise it. Maintainers stage the intended source files, then run -`scripts/source_archive.py build-artifacts/shadowsocks-libev.tar.gz`. +`scripts/source_archive.py build-artifacts/shadowsocks-c.tar.gz`. The archive includes tracked working-tree contents (so uncommitted changes are included); record the final commit with a published release and use a clean checkout for publishing. No submodules are accepted in the archive. +## Lint and static analysis + +The `tests` workflow runs actionlint 1.7.12 on GitHub Actions workflows and +Ruff 0.15.6 on Python scripts/tests (`E9,F63,F7,F82`: syntax errors, invalid +constructs and undefined names). Run the same checks locally: + +```sh +actionlint -shellcheck= -pyflakes= +uvx --from ruff==0.15.6 ruff check --select E9,F63,F7,F82 tests scripts +``` + +Project C builds treat compiler warnings as errors. The separate clang-tidy-18 +job analyzes project sources using the CMake compilation database and allows +zero warnings. Tool failures and compiler errors also fail the job; complete +diagnostics are uploaded even on failure. Vendored sources remain excluded. + ## Platform validation Implementation validation at commit cdb192a passes the full platform matrix: @@ -132,7 +148,7 @@ MSVC is an explicitly deferred milestone, not a supported build today. MinGW-w64 supplies the POSIX compatibility headers/functions (`unistd.h`, `getopt`, `ssize_t`, string helpers) used throughout the existing CLI. Native MSVC needs those interfaces isolated, compiler-specific flags in the libsodium adapter, -libev/Win32 build verification and export/import validation. CMake now rejects +libuv/Win32 build verification and export/import validation. CMake now rejects MSVC early with the supported UCRT64/Zig alternatives. Removing the event loop or inventing replacement crypto implementations is outside this modernization. @@ -163,3 +179,34 @@ mean no separately distributed project, crypto, event-loop or toolchain shared libraries. The `static-linux` job builds the Clang/musl Dockerfile, which also links libc statically and rejects ELF interpreters and `NEEDED` entries. All three jobs publish the tested installations as workflow artifacts. + +## Native event backends and asynchronous DNS + +Libuv 1.52.1 replaces libev in bundled builds; system builds use the distro's +libuv. Windows uses libuv's IOCP-backed AFD socket polling and macOS uses kqueue. +The internal event API supports independent read/write watchers on one socket, +including stopping/freeing watchers during callbacks. Handles close separately +from application objects. Native tests exercise 256 descriptors and verify the +IOCP handle or kqueue descriptor on the corresponding platform. + +c-ares drives asynchronous A/AAAA lookups for TCP/UDP destinations and client +hostname ACL bypass, with a dynamically sized DNS watcher list. Manager hostname +updates also use this resolver. Runtime calls to the numeric-address helper +cannot fall back to blocking DNS. Initial configuration/listener/proxy-address +resolution remains synchronous before serving traffic. Tests use a local delayed +DNS server to verify both address families, NXDOMAIN, cancellation, exactly-once +callbacks, and timer progress while requests wait. Hostname ACL integration uses +an unreachable proxy upstream so successful transfers prove direct resolution. + +## Project name and compatibility + +The project is named **shadowsocks-c**. New library outputs are +`libshadowsocks-c`, and CMake/pkg-config consumers use `shadowsocks-c`. +`shadowsocks.h`, its ABI version, and all `ss-*` commands remain stable. +Legacy library filenames are installed as relative symlinks on Unix and copies +on Windows; `find_package(shadowsocks-libev)` and the old pkg-config name resolve +to the new library. Private dependency archives and license files install under +`shadowsocks-c`. Source archives and the main manual use the new name. + +Existing configuration paths, Debian package identifiers and service names +remain compatible. GitHub URLs retain the current repository location. diff --git a/scripts/git_archive.sh b/scripts/git_archive.sh index 5b365b99f..e24e62531 100755 --- a/scripts/git_archive.sh +++ b/scripts/git_archive.sh @@ -2,7 +2,7 @@ # Backward-compatible maintainer entrypoint for the offline release archive. set -euo pipefail root=$(git rev-parse --show-toplevel) -name=shadowsocks-libev +name=shadowsocks-c directory=$root while getopts "n:o:" opt; do case "$opt" in diff --git a/scripts/source_archive.py b/scripts/source_archive.py index dd2e2fb03..921b0bdac 100755 --- a/scripts/source_archive.py +++ b/scripts/source_archive.py @@ -16,7 +16,7 @@ def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("output", type=Path) - parser.add_argument("--prefix", default="shadowsocks-libev-3.3.6") + parser.add_argument("--prefix", default="shadowsocks-c-3.3.6") args = parser.parse_args() root = Path(__file__).resolve().parents[1] if "/" in args.prefix or args.prefix in ("", ".", ".."): diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 32685e04f..255bd791f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -64,6 +64,7 @@ set(LIBSHADOWSOCKS_LIBEV_SOURCE udprelay.c cache.c local.c + resolv.c ssurl.c ${SS_CRYPTO_SOURCE} ${SS_PLUGIN_SOURCE} @@ -148,14 +149,15 @@ endif() foreach(kind STATIC SHARED) if(SS_BUILD_${kind}_LIBRARY) string(TOLOWER "${kind}" suffix) - set(target shadowsocks-libev-${suffix}) + set(target shadowsocks-c-${suffix}) add_library(${target} ${kind} ${LIBSHADOWSOCKS_LIBEV_SOURCE}) + add_library(shadowsocks-libev-${suffix} ALIAS ${target}) target_compile_definitions(${target} PRIVATE MODULE_LOCAL LIB_ONLY) target_include_directories(${target} PUBLIC $ $) target_link_libraries(${target} PRIVATE ${SS_CORE_DEPS} ${SS_ACL_DEPS}) - set_target_properties(${target} PROPERTIES OUTPUT_NAME shadowsocks-libev) + set_target_properties(${target} PROPERTIES OUTPUT_NAME shadowsocks-c) if(kind STREQUAL "SHARED") set_target_properties(${target} PROPERTIES VERSION 2.0.0 SOVERSION 2) endif() diff --git a/src/local.c b/src/local.c index 94f0402f8..4b006a348 100644 --- a/src/local.c +++ b/src/local.c @@ -52,6 +52,7 @@ #include "core.h" #include "netutils.h" +#include "resolv.h" #include "ssurl.h" #include "utils.h" #include "socks5.h" @@ -89,7 +90,7 @@ int tcp_outgoing_rcvbuf = 0; int vpn = 0; uint64_t tx = 0; uint64_t rx = 0; -ev_tstamp last = 0; +ss_tstamp last = 0; char *stat_path = NULL; #endif @@ -104,15 +105,15 @@ static int no_delay = 0; static int udp_fd = 0; static int ret_val = 0; -static struct ev_signal sigint_watcher; -static struct ev_signal sigterm_watcher; +static struct ss_signal sigint_watcher; +static struct ss_signal sigterm_watcher; #ifndef __MINGW32__ -static struct ev_signal sigchld_watcher; -static struct ev_signal sigusr1_watcher; +static struct ss_signal sigchld_watcher; +static struct ss_signal sigusr1_watcher; #else #ifndef LIB_ONLY static struct plugin_watcher_t { - ev_io io; + ss_io io; SOCKET fd; uint16_t port; int valid; @@ -126,14 +127,14 @@ static int nofile = 0; #endif #endif -static void server_recv_cb(EV_P_ ev_io *w, int revents); -static void server_send_cb(EV_P_ ev_io *w, int revents); -static void remote_recv_cb(EV_P_ ev_io *w, int revents); -static void remote_send_cb(EV_P_ ev_io *w, int revents); -static void accept_cb(EV_P_ ev_io *w, int revents); -static void signal_cb(EV_P_ ev_signal *w, int revents); +static void server_recv_cb(SS_P_ ss_io *w, int revents); +static void server_send_cb(SS_P_ ss_io *w, int revents); +static void remote_recv_cb(SS_P_ ss_io *w, int revents); +static void remote_send_cb(SS_P_ ss_io *w, int revents); +static void accept_cb(SS_P_ ss_io *w, int revents); +static void signal_cb(SS_P_ ss_signal *w, int revents); #if defined(__MINGW32__) && !defined(LIB_ONLY) -static void plugin_watcher_cb(EV_P_ ev_io *w, int revents); +static void plugin_watcher_cb(SS_P_ ss_io *w, int revents); #endif static int create_and_bind(const char *addr, const char *port); @@ -142,9 +143,9 @@ static int launch_or_create(const char *addr, const char *port); #endif static remote_t *create_remote(listen_ctx_t *listener, struct sockaddr *addr, int direct); static void free_remote(remote_t *remote); -static void close_and_free_remote(EV_P_ remote_t *remote); +static void close_and_free_remote(SS_P_ remote_t *remote); static void free_server(server_t *server); -static void close_and_free_server(EV_P_ server_t *server); +static void close_and_free_server(SS_P_ server_t *server); static remote_t *new_remote(int fd, int timeout); static server_t *new_server(int fd); @@ -263,7 +264,7 @@ launch_or_create(const char *addr, const char *port) #endif static void -free_connections(struct ev_loop *loop) +free_connections(struct ss_loop *loop) { struct ss_list_item *curr, *next; ss_list_foreach_void(&connections, curr, next) { @@ -275,16 +276,16 @@ free_connections(struct ev_loop *loop) } static void -delayed_connect_cb(EV_P_ ev_timer *watcher, int revents) +delayed_connect_cb(SS_P_ ss_timer *watcher, int revents) { server_t *server = ss_container_of(watcher, server_t, delayed_connect_watcher); - server_recv_cb(EV_A_ & server->recv_ctx->io, revents); + server_recv_cb(SS_A_ & server->recv_ctx->io, revents); } static int -server_handshake_reply(EV_P_ ev_io *w, int udp_assc, struct socks5_response *response) +server_handshake_reply(SS_P_ ss_io *w, int udp_assc, struct socks5_response *response) { server_ctx_t *server_recv_ctx = (server_ctx_t *)w; server_t *server = server_recv_ctx->server; @@ -299,8 +300,8 @@ server_handshake_reply(EV_P_ ev_io *w, int udp_assc, struct socks5_response *res LOGE("getsockname: %s", strerror(ss_socket_error())); response->rep = SOCKS5_REP_CONN_REFUSED; send(server->fd, (char *)response, sizeof(struct socks5_response), 0); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return -1; } } else @@ -326,8 +327,8 @@ server_handshake_reply(EV_P_ ev_io *w, int udp_assc, struct socks5_response *res if (s < reply_size) { LOGE("failed to send fake reply"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return -1; } if (udp_assc) { @@ -337,8 +338,34 @@ server_handshake_reply(EV_P_ ev_io *w, int udp_assc, struct socks5_response *res return 0; } +struct local_dns_query { + server_t *server; + struct ss_loop *loop; +}; +static void local_dns_free(void *data) { ss_free(data); } +static void local_dns_done(struct sockaddr *address, void *data) +{ + struct local_dns_query *query = data; + server_t *server = query->server; + if (!server) return; + server->dns_query = NULL; + server->dns_done = 1; + server->dns_success = address != NULL; + if (address) + memcpy(&server->dns_address, address, get_sockaddr_len(address)); + ss_io_start(query->loop, &server->recv_ctx->io); + /* Resume the buffered handshake without reading additional socket data. */ + server_recv_cb(query->loop, &server->recv_ctx->io, SS_TIMER); +} +static int local_dns_address(server_t *server, struct sockaddr_storage *address) +{ + if (!server->dns_success) return -1; + *address = server->dns_address; + return 0; +} + static int -server_handshake(EV_P_ ev_io *w, buffer_t *buf) +server_handshake(SS_P_ ss_io *w, buffer_t *buf) { server_ctx_t *server_recv_ctx = (server_ctx_t *)w; server_t *server = server_recv_ctx->server; @@ -361,14 +388,14 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf) if (verbose) { LOGI("udp assc request accepted"); } - return server_handshake_reply(EV_A_ w, 1, &response); + return server_handshake_reply(SS_A_ w, 1, &response); } else if (request->cmd != SOCKS5_CMD_CONNECT) { LOGE("unsupported command: %d", request->cmd); response.rep = SOCKS5_REP_CMD_NOT_SUPPORTED; char *send_buf = (char *)&response; send(server->fd, send_buf, 4, 0); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return -1; } @@ -439,12 +466,32 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf) response.rep = SOCKS5_REP_ADDRTYPE_NOT_SUPPORTED; char *send_buf = (char *)&response; send(server->fd, send_buf, 4, 0); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return -1; } - if (server_handshake_reply(EV_A_ w, 0, &response) < 0) + if (acl && atyp == SOCKS5_ATYP_DOMAIN && !server->dns_done +#ifdef __ANDROID__ + && !vpn +#endif + && acl_match_host(host) >= 0) { + uint16_t numeric_port; + if (parse_numeric_port(port, &numeric_port) != 0) { + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); + return -1; + } + struct local_dns_query *query = ss_malloc(sizeof(*query)); + query->server = server; + query->loop = loop; + server->dns_query = query; + ss_io_stop(loop, &server->recv_ctx->io); + resolv_start(host, htons(numeric_port), local_dns_done, local_dns_free, query); + return -1; + } + + if (server_handshake_reply(SS_A_ w, 0, &response) < 0) return -1; server->stage = STAGE_STREAM; @@ -487,7 +534,7 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf) && !vpn #endif ) { // resolve domain so we can bypass domain with geoip - if (get_sockaddr(host, port, &storage, 0, ipv6first)) + if (local_dns_address(server, &storage)) goto not_bypass; resolved = 1; switch (((struct sockaddr *)&storage)->sa_family) { @@ -541,7 +588,7 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf) goto not_bypass; else #endif - err = get_sockaddr(host, port, &storage, 0, ipv6first); + err = local_dns_address(server, &storage); else err = get_sockaddr(ip, port, &storage, 0, ipv6first); if (err != -1) { @@ -558,7 +605,7 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf) if (remote == NULL) { LOGE("invalid remote addr"); - close_and_free_server(EV_A_ server); + close_and_free_server(SS_A_ server); return -1; } @@ -566,8 +613,8 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf) int err = crypto->encrypt(abuf, server->e_ctx, SOCKET_BUF_SIZE); if (err) { LOGE("invalid password or cipher"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return -1; } } @@ -583,14 +630,14 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf) if (buf->len > 0) { return 0; } else { - ev_timer_start(EV_A_ & server->delayed_connect_watcher); + ss_timer_start(SS_A_ & server->delayed_connect_watcher); } return -1; } static void -server_stream(EV_P_ ev_io *w, buffer_t *buf) +server_stream(SS_P_ ss_io *w, buffer_t *buf) { server_ctx_t *server_recv_ctx = (server_ctx_t *)w; server_t *server = server_recv_ctx->server; @@ -598,7 +645,7 @@ server_stream(EV_P_ ev_io *w, buffer_t *buf) if (remote == NULL) { LOGE("invalid remote"); - close_and_free_server(EV_A_ server); + close_and_free_server(SS_A_ server); return; } @@ -611,8 +658,8 @@ server_stream(EV_P_ ev_io *w, buffer_t *buf) if (err) { LOGE("invalid password or cipher"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } @@ -636,8 +683,8 @@ server_stream(EV_P_ ev_io *w, buffer_t *buf) if (!not_protect) { if (protect_socket(remote->fd) == -1) { ERROR("protect_socket"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -652,15 +699,15 @@ server_stream(EV_P_ ev_io *w, buffer_t *buf) if (r == -1 && ss_socket_error() != CONNECT_IN_PROGRESS) { ERROR("connect"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } // wait on remote connected event - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); - ev_timer_start(EV_A_ & remote->send_ctx->watcher); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); + ss_timer_start(SS_A_ & remote->send_ctx->watcher); } else { #if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) int s = -1; @@ -738,8 +785,8 @@ server_stream(EV_P_ ev_io *w, buffer_t *buf) if (ss_socket_error() == CONNECT_IN_PROGRESS) { // in progress, wait until connected remote->buf->idx = 0; - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); return; } else { if (ss_socket_error() == EOPNOTSUPP || ss_socket_error() == EPROTONOSUPPORT || @@ -750,17 +797,17 @@ server_stream(EV_P_ ev_io *w, buffer_t *buf) } else { ERROR("fast_open_connect"); } - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } else { remote->buf->len -= s; remote->buf->idx = s; - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); - ev_timer_start(EV_A_ & remote->send_ctx->watcher); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); + ss_timer_start(SS_A_ & remote->send_ctx->watcher); return; } } @@ -770,20 +817,20 @@ server_stream(EV_P_ ev_io *w, buffer_t *buf) if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { // no data, wait for send remote->buf->idx = 0; - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); return; } else { ERROR("server_recv_cb_send"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } else if (s < (int)(remote->buf->len)) { remote->buf->len -= s; remote->buf->idx = s; - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); return; } else { remote->buf->idx = 0; @@ -793,7 +840,7 @@ server_stream(EV_P_ ev_io *w, buffer_t *buf) } static void -server_recv_cb(EV_P_ ev_io *w, int revents) +server_recv_cb(SS_P_ ss_io *w, int revents) { server_ctx_t *server_recv_ctx = (server_ctx_t *)w; server_t *server = server_recv_ctx->server; @@ -801,7 +848,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) buffer_t *buf; ssize_t r; - ev_timer_stop(EV_A_ & server->delayed_connect_watcher); + ss_timer_stop(SS_A_ & server->delayed_connect_watcher); if (remote == NULL) { buf = server->buf; @@ -809,13 +856,13 @@ server_recv_cb(EV_P_ ev_io *w, int revents) buf = remote->buf; } - if (revents != EV_TIMER) { + if (revents != SS_TIMER) { r = recv(server->fd, buf->data + buf->len, SOCKET_BUF_SIZE - buf->len, 0); if (r == 0) { // connection closed - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else if (r == -1) { if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { @@ -825,8 +872,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents) } else { if (verbose) ERROR("server_recv_cb_recv"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -836,7 +883,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) while (1) { // local socks5 server if (server->stage == STAGE_STREAM) { - server_stream(EV_A_ w, buf); + server_stream(SS_A_ w, buf); // all processed return; @@ -852,8 +899,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (buf->len < 1) return; if (buf->data[0] != SVERSION) { - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } if (buf->len < sizeof(struct method_select_request)) { @@ -876,8 +923,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents) char *send_buf = (char *)&response; send(server->fd, send_buf, sizeof(response), 0); if (response.method == METHOD_UNACCEPTABLE) { - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } @@ -892,7 +939,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) buf->len = 0; return; } else if (server->stage == STAGE_HANDSHAKE) { - int ret = server_handshake(EV_A_ w, buf); + int ret = server_handshake(SS_A_ w, buf); if (ret) return; } @@ -900,15 +947,15 @@ server_recv_cb(EV_P_ ev_io *w, int revents) } static void -server_send_cb(EV_P_ ev_io *w, int revents) +server_send_cb(SS_P_ ss_io *w, int revents) { server_ctx_t *server_send_ctx = (server_ctx_t *)w; server_t *server = server_send_ctx->server; remote_t *remote = server->remote; if (server->buf->len == 0) { // close and free - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else { // has data to send @@ -917,8 +964,8 @@ server_send_cb(EV_P_ ev_io *w, int revents) if (s == -1) { if (ss_socket_error() != EAGAIN && ss_socket_error() != EWOULDBLOCK) { ERROR("server_send_cb_send"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } return; } else if (s < (ssize_t)(server->buf->len)) { @@ -930,8 +977,8 @@ server_send_cb(EV_P_ ev_io *w, int revents) // all sent out, wait for reading server->buf->len = 0; server->buf->idx = 0; - ev_io_stop(EV_A_ & server_send_ctx->io); - ev_io_start(EV_A_ & remote->recv_ctx->io); + ss_io_stop(SS_A_ & server_send_ctx->io); + ss_io_start(SS_A_ & remote->recv_ctx->io); return; } } @@ -941,7 +988,7 @@ server_send_cb(EV_P_ ev_io *w, int revents) void stat_update_cb() { - ev_tstamp now = ev_time(); + ss_tstamp now = ss_time(); if (now - last > 0.5) { send_traffic_stat(tx, rx); last = now; @@ -951,7 +998,7 @@ stat_update_cb() #endif static void -remote_timeout_cb(EV_P_ ev_timer *watcher, int revents) +remote_timeout_cb(SS_P_ ss_timer *watcher, int revents) { remote_ctx_t *remote_ctx = ss_container_of(watcher, remote_ctx_t, watcher); @@ -963,12 +1010,12 @@ remote_timeout_cb(EV_P_ ev_timer *watcher, int revents) LOGI("TCP connection timeout"); } - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } static void -remote_recv_cb(EV_P_ ev_io *w, int revents) +remote_recv_cb(SS_P_ ss_io *w, int revents) { remote_ctx_t *remote_recv_ctx = (remote_ctx_t *)w; remote_t *remote = remote_recv_ctx->remote; @@ -978,8 +1025,8 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) if (r == 0) { // connection closed - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else if (r == -1) { if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { @@ -988,8 +1035,8 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) return; } else { ERROR("remote_recv_cb_recv"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -1004,8 +1051,8 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) int err = crypto->decrypt(server->buf, server->d_ctx, SOCKET_BUF_SIZE); if (err == CRYPTO_ERROR) { LOGE("invalid password or cipher"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else if (err == CRYPTO_NEED_MORE) { return; // Wait for more @@ -1018,19 +1065,19 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { // no data, wait for send server->buf->idx = 0; - ev_io_stop(EV_A_ & remote_recv_ctx->io); - ev_io_start(EV_A_ & server->send_ctx->io); + ss_io_stop(SS_A_ & remote_recv_ctx->io); + ss_io_start(SS_A_ & server->send_ctx->io); } else { ERROR("remote_recv_cb_send"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } else if (s < (int)(server->buf->len)) { server->buf->len -= s; server->buf->idx = s; - ev_io_stop(EV_A_ & remote_recv_ctx->io); - ev_io_start(EV_A_ & server->send_ctx->io); + ss_io_stop(SS_A_ & remote_recv_ctx->io); + ss_io_start(SS_A_ & server->send_ctx->io); } // Disable TCP_NODELAY after the first response are sent @@ -1043,7 +1090,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) } static void -remote_send_cb(EV_P_ ev_io *w, int revents) +remote_send_cb(SS_P_ ss_io *w, int revents) { remote_ctx_t *remote_send_ctx = (remote_ctx_t *)w; remote_t *remote = remote_send_ctx->remote; @@ -1068,8 +1115,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) } else { ERROR("WSAGetOverlappedResult"); // not connected - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -1086,28 +1133,28 @@ remote_send_cb(EV_P_ ev_io *w, int revents) int r = getpeername(remote->fd, (struct sockaddr *)&addr, &len); if (r == 0) { remote_send_ctx->connected = 1; - ev_timer_stop(EV_A_ & remote_send_ctx->watcher); - ev_io_start(EV_A_ & remote->recv_ctx->io); + ss_timer_stop(SS_A_ & remote_send_ctx->watcher); + ss_io_start(SS_A_ & remote->recv_ctx->io); // no need to send any data if (remote->buf->len == 0) { - ev_io_stop(EV_A_ & remote_send_ctx->io); - ev_io_start(EV_A_ & server->recv_ctx->io); + ss_io_stop(SS_A_ & remote_send_ctx->io); + ss_io_start(SS_A_ & server->recv_ctx->io); return; } } else { // not connected ERROR("getpeername"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } if (remote->buf->len == 0) { // close and free - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else { // has data to send @@ -1117,8 +1164,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (ss_socket_error() != EAGAIN && ss_socket_error() != EWOULDBLOCK) { ERROR("remote_send_cb_send"); // close and free - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } return; } else if (s < (ssize_t)(remote->buf->len)) { @@ -1130,8 +1177,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) // all sent out, wait for reading remote->buf->len = 0; remote->buf->idx = 0; - ev_io_stop(EV_A_ & remote_send_ctx->io); - ev_io_start(EV_A_ & server->recv_ctx->io); + ss_io_stop(SS_A_ & remote_send_ctx->io); + ss_io_start(SS_A_ & server->recv_ctx->io); } } } @@ -1156,9 +1203,9 @@ new_remote(int fd, int timeout) remote->recv_ctx->remote = remote; remote->send_ctx->remote = remote; - ev_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, EV_READ); - ev_io_init(&remote->send_ctx->io, remote_send_cb, fd, EV_WRITE); - ev_timer_init(&remote->send_ctx->watcher, remote_timeout_cb, + ss_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, SS_READ); + ss_io_init(&remote->send_ctx->io, remote_send_cb, fd, SS_WRITE); + ss_timer_init(&remote->send_ctx->watcher, remote_timeout_cb, min(MAX_CONNECT_TIMEOUT, timeout), 0); return remote; @@ -1180,12 +1227,12 @@ free_remote(remote_t *remote) } static void -close_and_free_remote(EV_P_ remote_t *remote) +close_and_free_remote(SS_P_ remote_t *remote) { if (remote != NULL) { - ev_timer_stop(EV_A_ & remote->send_ctx->watcher); - ev_io_stop(EV_A_ & remote->send_ctx->io); - ev_io_stop(EV_A_ & remote->recv_ctx->io); + ss_timer_stop(SS_A_ & remote->send_ctx->watcher); + ss_io_stop(SS_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & remote->recv_ctx->io); ss_socket_close(remote->fd); free_remote(remote); } @@ -1220,10 +1267,10 @@ new_server(int fd) crypto->ctx_init(crypto->cipher, server->d_ctx, 0); cipher_ctx_pair(server->e_ctx, server->d_ctx); - ev_io_init(&server->recv_ctx->io, server_recv_cb, fd, EV_READ); - ev_io_init(&server->send_ctx->io, server_send_cb, fd, EV_WRITE); + ss_io_init(&server->recv_ctx->io, server_recv_cb, fd, SS_READ); + ss_io_init(&server->send_ctx->io, server_send_cb, fd, SS_WRITE); - ev_timer_init(&server->delayed_connect_watcher, + ss_timer_init(&server->delayed_connect_watcher, delayed_connect_cb, 0.05, 0); ss_list_add(&connections, &server->entries); @@ -1261,12 +1308,16 @@ free_server(server_t *server) } static void -close_and_free_server(EV_P_ server_t *server) +close_and_free_server(SS_P_ server_t *server) { if (server != NULL) { - ev_io_stop(EV_A_ & server->send_ctx->io); - ev_io_stop(EV_A_ & server->recv_ctx->io); - ev_timer_stop(EV_A_ & server->delayed_connect_watcher); + if (server->dns_query) { + server->dns_query->server = NULL; + server->dns_query = NULL; + } + ss_io_stop(SS_A_ & server->send_ctx->io); + ss_io_stop(SS_A_ & server->recv_ctx->io); + ss_timer_stop(SS_A_ & server->delayed_connect_watcher); ss_socket_close(server->fd); free_server(server); } @@ -1353,9 +1404,9 @@ create_remote(listen_ctx_t *listener, } static void -signal_cb(EV_P_ ev_signal *w, int revents) +signal_cb(SS_P_ ss_signal *w, int revents) { - if (revents & EV_SIGNAL) { + if (revents & SS_SIGNAL) { switch (w->signum) { #ifndef __MINGW32__ case SIGCHLD: @@ -1368,24 +1419,24 @@ signal_cb(EV_P_ ev_signal *w, int revents) #endif case SIGINT: case SIGTERM: - ev_signal_stop(EV_DEFAULT, &sigint_watcher); - ev_signal_stop(EV_DEFAULT, &sigterm_watcher); + ss_signal_stop(SS_DEFAULT, &sigint_watcher); + ss_signal_stop(SS_DEFAULT, &sigterm_watcher); #ifndef __MINGW32__ - ev_signal_stop(EV_DEFAULT, &sigchld_watcher); - ev_signal_stop(EV_DEFAULT, &sigusr1_watcher); + ss_signal_stop(SS_DEFAULT, &sigchld_watcher); + ss_signal_stop(SS_DEFAULT, &sigusr1_watcher); #else #ifndef LIB_ONLY - ev_io_stop(EV_DEFAULT, &plugin_watcher.io); + ss_io_stop(SS_DEFAULT, &plugin_watcher.io); #endif #endif - ev_unloop(EV_A_ EVUNLOOP_ALL); + ss_unloop(SS_A_ SS_UNLOOP_ALL); } } } #if defined(__MINGW32__) && !defined(LIB_ONLY) static void -plugin_watcher_cb(EV_P_ ev_io *w, int revents) +plugin_watcher_cb(SS_P_ ss_io *w, int revents) { char buf[1]; SOCKET fd = ss_accept(plugin_watcher.fd, NULL, NULL); @@ -1396,16 +1447,16 @@ plugin_watcher_cb(EV_P_ ev_io *w, int revents) closesocket(fd); LOGE("plugin service exit unexpectedly"); ret_val = -1; - ev_signal_stop(EV_DEFAULT, &sigint_watcher); - ev_signal_stop(EV_DEFAULT, &sigterm_watcher); - ev_io_stop(EV_DEFAULT, &plugin_watcher.io); - ev_unloop(EV_A_ EVUNLOOP_ALL); + ss_signal_stop(SS_DEFAULT, &sigint_watcher); + ss_signal_stop(SS_DEFAULT, &sigterm_watcher); + ss_io_stop(SS_DEFAULT, &plugin_watcher.io); + ss_unloop(SS_A_ SS_UNLOOP_ALL); } #endif void -accept_cb(EV_P_ ev_io *w, int revents) +accept_cb(SS_P_ ss_io *w, int revents) { listen_ctx_t *listener = (listen_ctx_t *)w; int serverfd = ss_accept(listener->fd, NULL, NULL); @@ -1423,7 +1474,7 @@ accept_cb(EV_P_ ev_io *w, int revents) server_t *server = new_server(serverfd); server->listener = listener; - ev_io_start(EV_A_ & server->recv_ctx->io); + ss_io_start(SS_A_ & server->recv_ctx->io); } #ifndef LIB_ONLY @@ -1896,8 +1947,8 @@ main(int argc, char **argv) break; } plugin_watcher.fd = fd; - ev_io_init(&plugin_watcher.io, plugin_watcher_cb, fd, EV_READ); - ev_io_start(EV_DEFAULT, &plugin_watcher.io); + ss_io_init(&plugin_watcher.io, plugin_watcher_cb, fd, SS_READ); + ss_io_start(SS_DEFAULT, &plugin_watcher.io); plugin_watcher.valid = 1; } while (0); if (!plugin_watcher.valid) { @@ -1972,13 +2023,13 @@ main(int argc, char **argv) listen_ctx.mptcp = mptcp; // Setup signal handler - ev_signal_init(&sigint_watcher, signal_cb, SIGINT); - ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); - ev_signal_start(EV_DEFAULT, &sigint_watcher); - ev_signal_start(EV_DEFAULT, &sigterm_watcher); + ss_signal_init(&sigint_watcher, signal_cb, SIGINT); + ss_signal_init(&sigterm_watcher, signal_cb, SIGTERM); + ss_signal_start(SS_DEFAULT, &sigint_watcher); + ss_signal_start(SS_DEFAULT, &sigterm_watcher); #ifndef __MINGW32__ - ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); - ev_signal_start(EV_DEFAULT, &sigchld_watcher); + ss_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); + ss_signal_start(SS_DEFAULT, &sigchld_watcher); #endif if (ss_is_ipv6addr(local_addr)) @@ -1986,7 +2037,8 @@ main(int argc, char **argv) else LOGI("listening at %s:%s", local_addr, local_port); - struct ev_loop *loop = EV_DEFAULT; + struct ss_loop *loop = SS_DEFAULT; + if (acl) resolv_init(loop, NULL, ipv6first); if (mode != UDP_ONLY) { // Setup socket @@ -2006,8 +2058,8 @@ main(int argc, char **argv) listen_ctx.fd = listenfd; - ev_io_init(&listen_ctx.io, accept_cb, listenfd, EV_READ); - ev_io_start(loop, &listen_ctx.io); + ss_io_init(&listen_ctx.io, accept_cb, listenfd, SS_READ); + ss_io_start(loop, &listen_ctx.io); } // Setup UDP @@ -2049,7 +2101,7 @@ main(int argc, char **argv) ss_list_init(&connections); // Enter the loop - ev_run(loop, 0); + ss_run(loop, 0); if (verbose) { LOGI("closed gracefully"); @@ -2061,7 +2113,7 @@ main(int argc, char **argv) } if (mode != UDP_ONLY) { - ev_io_stop(loop, &listen_ctx.io); + ss_io_stop(loop, &listen_ctx.io); free_connections(loop); for (i = 0; i < listen_ctx.remote_num; i++) @@ -2072,6 +2124,8 @@ main(int argc, char **argv) if (mode != TCP_ONLY) { free_udprelay(); } + if (acl) resolv_shutdown(loop); + ss_loop_destroy(loop); #ifdef __MINGW32__ if (plugin_watcher.valid) { @@ -2132,13 +2186,13 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat signal(SIGABRT, SIG_IGN); #endif - ev_signal_init(&sigint_watcher, signal_cb, SIGINT); - ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); - ev_signal_start(EV_DEFAULT, &sigint_watcher); - ev_signal_start(EV_DEFAULT, &sigterm_watcher); + ss_signal_init(&sigint_watcher, signal_cb, SIGINT); + ss_signal_init(&sigterm_watcher, signal_cb, SIGTERM); + ss_signal_start(SS_DEFAULT, &sigint_watcher); + ss_signal_start(SS_DEFAULT, &sigterm_watcher); #ifndef __MINGW32__ - ev_signal_init(&sigusr1_watcher, signal_cb, SIGUSR1); - ev_signal_start(EV_DEFAULT, &sigusr1_watcher); + ss_signal_init(&sigusr1_watcher, signal_cb, SIGUSR1); + ss_signal_start(SS_DEFAULT, &sigusr1_watcher); #endif // Setup keys @@ -2149,12 +2203,13 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat struct sockaddr_storage storage; memset(&storage, 0, sizeof(struct sockaddr_storage)); - if (get_sockaddr(remote_host, remote_port_str, &storage, 0, ipv6first) == -1) { + if (get_sockaddr(remote_host, remote_port_str, &storage, 1, ipv6first) == -1) { return -1; } // Setup proxy context - struct ev_loop *loop = EV_DEFAULT; + struct ss_loop *loop = SS_DEFAULT; + if (acl) resolv_init(loop, NULL, ipv6first); struct sockaddr *remote_addr_tmp[MAX_REMOTE_NUM]; listen_ctx_t listen_ctx; @@ -2188,8 +2243,8 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat listen_ctx.fd = listenfd; - ev_io_init(&listen_ctx.io, accept_cb, listenfd, EV_READ); - ev_io_start(loop, &listen_ctx.io); + ss_io_init(&listen_ctx.io, accept_cb, listenfd, SS_READ); + ss_io_start(loop, &listen_ctx.io); } // Setup UDP @@ -2211,7 +2266,7 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat } // Enter the loop - ev_run(loop, 0); + ss_run(loop, 0); if (verbose) { LOGI("closed gracefully"); @@ -2219,7 +2274,7 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat // Clean up if (mode != UDP_ONLY) { - ev_io_stop(loop, &listen_ctx.io); + ss_io_stop(loop, &listen_ctx.io); free_connections(loop); ss_socket_close(listen_ctx.fd); } @@ -2227,6 +2282,8 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat if (mode != TCP_ONLY) { free_udprelay(); } + if (acl) resolv_shutdown(loop); + ss_loop_destroy(loop); #ifdef __MINGW32__ winsock_cleanup(); diff --git a/src/local.h b/src/local.h index 1cc457213..5de6261e7 100644 --- a/src/local.h +++ b/src/local.h @@ -25,11 +25,7 @@ #include "core.h" -#ifdef HAVE_LIBEV_EV_H -#include -#else -#include -#endif +#include "ss_event.h" #ifdef __MINGW32__ #include "ss_windows.h" @@ -41,7 +37,7 @@ #include "common.h" typedef struct listen_ctx { - ev_io io; + ss_io io; char *iface; int remote_num; int timeout; @@ -51,7 +47,7 @@ typedef struct listen_ctx { } listen_ctx_t; typedef struct server_ctx { - ev_io io; + ss_io io; int connected; struct server *server; } server_ctx_t; @@ -59,6 +55,9 @@ typedef struct server_ctx { typedef struct server { int fd; int stage; + struct local_dns_query *dns_query; + int dns_done, dns_success; + struct sockaddr_storage dns_address; cipher_ctx_t *e_ctx; cipher_ctx_t *d_ctx; @@ -70,14 +69,14 @@ typedef struct server { buffer_t *buf; buffer_t *abuf; - ev_timer delayed_connect_watcher; + ss_timer delayed_connect_watcher; struct ss_list_item entries; } server_t; typedef struct remote_ctx { - ev_io io; - ev_timer watcher; + ss_io io; + ss_timer watcher; int connected; struct remote *remote; diff --git a/src/manager.c b/src/manager.c index bb2c2f681..162c8162c 100644 --- a/src/manager.c +++ b/src/manager.c @@ -952,7 +952,7 @@ update_stat(char *port, uint64_t traffic) } static void -manager_recv_cb(EV_P_ ev_io *w, int revents) +manager_recv_cb(SS_P_ ss_io *w, int revents) { struct manager_ctx *manager = (struct manager_ctx *)w; socklen_t len; @@ -1146,13 +1146,13 @@ manager_recv_cb(EV_P_ ev_io *w, int revents) } static void -signal_cb(EV_P_ ev_signal *w, int revents) +signal_cb(SS_P_ ss_signal *w, int revents) { - if (revents & EV_SIGNAL) { + if (revents & SS_SIGNAL) { switch (w->signum) { case SIGINT: case SIGTERM: - ev_unloop(EV_A_ EVUNLOOP_ALL); + ss_unloop(SS_A_ SS_UNLOOP_ALL); } } } @@ -1551,12 +1551,12 @@ main(int argc, char **argv) signal(SIGCHLD, SIG_IGN); signal(SIGABRT, SIG_IGN); - struct ev_signal sigint_watcher; - struct ev_signal sigterm_watcher; - ev_signal_init(&sigint_watcher, signal_cb, SIGINT); - ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); - ev_signal_start(EV_DEFAULT, &sigint_watcher); - ev_signal_start(EV_DEFAULT, &sigterm_watcher); + struct ss_signal sigint_watcher; + struct ss_signal sigterm_watcher; + ss_signal_init(&sigint_watcher, signal_cb, SIGINT); + ss_signal_init(&sigterm_watcher, signal_cb, SIGTERM); + ss_signal_start(SS_DEFAULT, &sigint_watcher); + ss_signal_start(SS_DEFAULT, &sigterm_watcher); struct manager_ctx manager; memset(&manager, 0, sizeof(struct manager_ctx)); @@ -1586,7 +1586,7 @@ main(int argc, char **argv) #endif // initialize ev loop - struct ev_loop *loop = EV_DEFAULT; + struct ss_loop *loop = SS_DEFAULT; // Clean up all existed processes DIR *dp; @@ -1673,11 +1673,11 @@ main(int argc, char **argv) } manager.fd = sfd; - ev_io_init(&manager.io, manager_recv_cb, manager.fd, EV_READ); - ev_io_start(loop, &manager.io); + ss_io_init(&manager.io, manager_recv_cb, manager.fd, SS_READ); + ss_io_start(loop, &manager.io); // start ev loop - ev_run(loop, 0); + ss_run(loop, 0); if (verbose) { LOGI("closed gracefully"); @@ -1692,8 +1692,8 @@ main(int argc, char **argv) ss_free(server); } - ev_signal_stop(EV_DEFAULT, &sigint_watcher); - ev_signal_stop(EV_DEFAULT, &sigterm_watcher); + ss_signal_stop(SS_DEFAULT, &sigint_watcher); + ss_signal_stop(SS_DEFAULT, &sigterm_watcher); ss_free(working_dir); free_addr(&ip_addr); diff --git a/src/manager.h b/src/manager.h index 11e6c728c..5d58fd10e 100644 --- a/src/manager.h +++ b/src/manager.h @@ -27,18 +27,14 @@ #include "core.h" #include "uthash.h" -#ifdef HAVE_LIBEV_EV_H -#include -#else -#include -#endif +#include "ss_event.h" #include "jconf.h" #include "common.h" struct manager_ctx { - ev_io io; + ss_io io; int fd; int fast_open; int no_delay; diff --git a/src/netutils.c b/src/netutils.c index 44f0cb410..4b75b58c3 100644 --- a/src/netutils.c +++ b/src/netutils.c @@ -57,7 +57,7 @@ extern int verbose; static const char valid_label_bytes[] = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; -static int +int parse_numeric_port(const char *port, uint16_t *port_out) { char *endptr; @@ -178,6 +178,8 @@ get_sockaddr(char *host, char *port, } return 0; } else { + /* Event-loop callers must use the asynchronous resolver for names. */ + if (!block) return -1; #ifdef __ANDROID__ extern int vpn; if (vpn) { diff --git a/src/netutils.h b/src/netutils.h index 445d4018d..a8cb880d5 100644 --- a/src/netutils.h +++ b/src/netutils.h @@ -85,6 +85,7 @@ static const char mptcp_enabled_values[] = { MPTCP_ENABLED, 0 }; /** byte size of ip6 address */ #define INET6_SIZE 16 +int parse_numeric_port(const char *port, uint16_t *port_out); size_t get_sockaddr_len(struct sockaddr *addr); ssize_t get_sockaddr(char *host, char *port, struct sockaddr_storage *storage, int block, diff --git a/src/redir.c b/src/redir.c index 6f1b223c8..6b4989035 100644 --- a/src/redir.c +++ b/src/redir.c @@ -73,19 +73,19 @@ #define IPV6_TRANSPARENT 75 #endif -static void accept_cb(EV_P_ ev_io *w, int revents); -static void server_recv_cb(EV_P_ ev_io *w, int revents); -static void server_send_cb(EV_P_ ev_io *w, int revents); -static void remote_recv_cb(EV_P_ ev_io *w, int revents); -static void remote_send_cb(EV_P_ ev_io *w, int revents); +static void accept_cb(SS_P_ ss_io *w, int revents); +static void server_recv_cb(SS_P_ ss_io *w, int revents); +static void server_send_cb(SS_P_ ss_io *w, int revents); +static void remote_recv_cb(SS_P_ ss_io *w, int revents); +static void remote_send_cb(SS_P_ ss_io *w, int revents); static remote_t *new_remote(int fd, int timeout); static server_t *new_server(int fd); static void free_remote(remote_t *remote); -static void close_and_free_remote(EV_P_ remote_t *remote); +static void close_and_free_remote(SS_P_ remote_t *remote); static void free_server(server_t *server); -static void close_and_free_server(EV_P_ server_t *server); +static void close_and_free_server(SS_P_ server_t *server); int verbose = 0; int reuse_port = 0; @@ -105,9 +105,9 @@ int fast_open = 0; static int no_delay = 0; static int ret_val = 0; -static struct ev_signal sigint_watcher; -static struct ev_signal sigterm_watcher; -static struct ev_signal sigchld_watcher; +static struct ss_signal sigint_watcher; +static struct ss_signal sigterm_watcher; +static struct ss_signal sigchld_watcher; static int tcp_tproxy = 0; /* use tproxy instead of redirect (for tcp) */ @@ -227,21 +227,21 @@ create_and_bind(const char *addr, const char *port) } static void -server_recv_cb(EV_P_ ev_io *w, int revents) +server_recv_cb(SS_P_ ss_io *w, int revents) { server_ctx_t *server_recv_ctx = (server_ctx_t *)w; server_t *server = server_recv_ctx->server; remote_t *remote = server->remote; - ev_timer_stop(EV_A_ & server->delayed_connect_watcher); + ss_timer_stop(SS_A_ & server->delayed_connect_watcher); ssize_t r = recv(server->fd, remote->buf->data + remote->buf->len, SOCKET_BUF_SIZE - remote->buf->len, 0); if (r == 0) { // connection closed - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else if (r == -1) { if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { @@ -250,8 +250,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents) return; } else { ERROR("server recv"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -277,8 +277,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents) } if (!remote->send_ctx->connected) { - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); return; } @@ -286,8 +286,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (err) { LOGE("invalid password or cipher"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } @@ -297,20 +297,20 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { // no data, wait for send remote->buf->idx = 0; - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); return; } else { ERROR("send"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } else if (s < remote->buf->len) { remote->buf->len -= s; remote->buf->idx = s; - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); return; } else { remote->buf->idx = 0; @@ -319,15 +319,15 @@ server_recv_cb(EV_P_ ev_io *w, int revents) } static void -server_send_cb(EV_P_ ev_io *w, int revents) +server_send_cb(SS_P_ ss_io *w, int revents) { server_ctx_t *server_send_ctx = (server_ctx_t *)w; server_t *server = server_send_ctx->server; remote_t *remote = server->remote; if (server->buf->len == 0) { // close and free - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else { // has data to send @@ -336,8 +336,8 @@ server_send_cb(EV_P_ ev_io *w, int revents) if (s == -1) { if (ss_socket_error() != EAGAIN && ss_socket_error() != EWOULDBLOCK) { ERROR("send"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } return; } else if (s < server->buf->len) { @@ -349,14 +349,14 @@ server_send_cb(EV_P_ ev_io *w, int revents) // all sent out, wait for reading server->buf->len = 0; server->buf->idx = 0; - ev_io_stop(EV_A_ & server_send_ctx->io); - ev_io_start(EV_A_ & remote->recv_ctx->io); + ss_io_stop(SS_A_ & server_send_ctx->io); + ss_io_start(SS_A_ & remote->recv_ctx->io); } } } static void -delayed_connect_cb(EV_P_ ev_timer *watcher, int revents) +delayed_connect_cb(SS_P_ ss_timer *watcher, int revents) { server_t *server = ss_container_of(watcher, server_t, delayed_connect_watcher); @@ -369,18 +369,18 @@ delayed_connect_cb(EV_P_ ev_timer *watcher, int revents) if (r == -1 && ss_socket_error() != CONNECT_IN_PROGRESS) { ERROR("connect"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else { // listen to remote connected event - ev_io_start(EV_A_ & remote->send_ctx->io); - ev_timer_start(EV_A_ & remote->send_ctx->watcher); + ss_io_start(SS_A_ & remote->send_ctx->io); + ss_timer_start(SS_A_ & remote->send_ctx->watcher); } } static void -remote_timeout_cb(EV_P_ ev_timer *watcher, int revents) +remote_timeout_cb(SS_P_ ss_timer *watcher, int revents) { remote_ctx_t *remote_ctx = ss_container_of(watcher, remote_ctx_t, watcher); @@ -388,14 +388,14 @@ remote_timeout_cb(EV_P_ ev_timer *watcher, int revents) remote_t *remote = remote_ctx->remote; server_t *server = remote->server; - ev_timer_stop(EV_A_ watcher); + ss_timer_stop(SS_A_ watcher); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } static void -remote_recv_cb(EV_P_ ev_io *w, int revents) +remote_recv_cb(SS_P_ ss_io *w, int revents) { remote_ctx_t *remote_recv_ctx = (remote_ctx_t *)w; remote_t *remote = remote_recv_ctx->remote; @@ -405,8 +405,8 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) if (r == 0) { // connection closed - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else if (r == -1) { if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { @@ -415,8 +415,8 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) return; } else { ERROR("remote recv"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -426,8 +426,8 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) int err = crypto->decrypt(server->buf, server->d_ctx, SOCKET_BUF_SIZE); if (err == CRYPTO_ERROR) { LOGE("invalid password or cipher"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else if (err == CRYPTO_NEED_MORE) { return; // Wait for more @@ -439,19 +439,19 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { // no data, wait for send server->buf->idx = 0; - ev_io_stop(EV_A_ & remote_recv_ctx->io); - ev_io_start(EV_A_ & server->send_ctx->io); + ss_io_stop(SS_A_ & remote_recv_ctx->io); + ss_io_start(SS_A_ & server->send_ctx->io); } else { ERROR("send"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } else if (s < server->buf->len) { server->buf->len -= s; server->buf->idx = s; - ev_io_stop(EV_A_ & remote_recv_ctx->io); - ev_io_start(EV_A_ & server->send_ctx->io); + ss_io_stop(SS_A_ & remote_recv_ctx->io); + ss_io_start(SS_A_ & server->send_ctx->io); } // Disable TCP_NODELAY after the first response are sent @@ -464,13 +464,13 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) } static void -remote_send_cb(EV_P_ ev_io *w, int revents) +remote_send_cb(SS_P_ ss_io *w, int revents) { remote_ctx_t *remote_send_ctx = (remote_ctx_t *)w; remote_t *remote = remote_send_ctx->remote; server_t *server = remote->server; - ev_timer_stop(EV_A_ & remote_send_ctx->watcher); + ss_timer_stop(SS_A_ & remote_send_ctx->watcher); if (!remote_send_ctx->connected) { int r = 0; @@ -483,9 +483,9 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (r == 0) { remote_send_ctx->connected = 1; - ev_io_stop(EV_A_ & remote_send_ctx->io); - ev_io_stop(EV_A_ & server->recv_ctx->io); - ev_io_start(EV_A_ & remote->recv_ctx->io); + ss_io_stop(SS_A_ & remote_send_ctx->io); + ss_io_stop(SS_A_ & server->recv_ctx->io); + ss_io_start(SS_A_ & remote->recv_ctx->io); // send destaddr buffer_t ss_addr_to_send; @@ -520,8 +520,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (err) { LOGE("invalid password or cipher"); bfree(abuf); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } @@ -529,8 +529,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (err) { LOGE("invalid password or cipher"); bfree(abuf); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } @@ -539,16 +539,16 @@ remote_send_cb(EV_P_ ev_io *w, int revents) } else { ERROR("getpeername"); // not connected - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } if (remote->buf->len == 0) { // close and free - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else { // has data to send @@ -575,8 +575,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (s == -1) { if (ss_socket_error() == CONNECT_IN_PROGRESS) { - ev_io_start(EV_A_ & remote_send_ctx->io); - ev_timer_start(EV_A_ & remote_send_ctx->watcher); + ss_io_start(SS_A_ & remote_send_ctx->io); + ss_timer_start(SS_A_ & remote_send_ctx->watcher); } else { if (ss_socket_error() == EOPNOTSUPP || ss_socket_error() == EPROTONOSUPPORT || ss_socket_error() == ENOPROTOOPT) { @@ -585,8 +585,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) } else { ERROR("fast_open_connect"); } - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } return; } @@ -599,22 +599,22 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (ss_socket_error() != EAGAIN && ss_socket_error() != EWOULDBLOCK) { ERROR("send"); // close and free - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } return; } else if (s < remote->buf->len) { // partly sent, move memory, wait for the next time to send remote->buf->len -= s; remote->buf->idx += s; - ev_io_start(EV_A_ & remote_send_ctx->io); + ss_io_start(SS_A_ & remote_send_ctx->io); return; } else { // all sent out, wait for reading remote->buf->len = 0; remote->buf->idx = 0; - ev_io_stop(EV_A_ & remote_send_ctx->io); - ev_io_start(EV_A_ & server->recv_ctx->io); + ss_io_stop(SS_A_ & remote_send_ctx->io); + ss_io_start(SS_A_ & server->recv_ctx->io); } } } @@ -637,9 +637,9 @@ new_remote(int fd, int timeout) remote->send_ctx->remote = remote; remote->send_ctx->connected = 0; - ev_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, EV_READ); - ev_io_init(&remote->send_ctx->io, remote_send_cb, fd, EV_WRITE); - ev_timer_init(&remote->send_ctx->watcher, remote_timeout_cb, + ss_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, SS_READ); + ss_io_init(&remote->send_ctx->io, remote_send_cb, fd, SS_WRITE); + ss_timer_init(&remote->send_ctx->watcher, remote_timeout_cb, min(MAX_CONNECT_TIMEOUT, timeout), 0); return remote; @@ -661,12 +661,12 @@ free_remote(remote_t *remote) } static void -close_and_free_remote(EV_P_ remote_t *remote) +close_and_free_remote(SS_P_ remote_t *remote) { if (remote != NULL) { - ev_timer_stop(EV_A_ & remote->send_ctx->watcher); - ev_io_stop(EV_A_ & remote->send_ctx->io); - ev_io_stop(EV_A_ & remote->recv_ctx->io); + ss_timer_stop(SS_A_ & remote->send_ctx->watcher); + ss_io_stop(SS_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & remote->recv_ctx->io); ss_socket_close(remote->fd); free_remote(remote); } @@ -696,10 +696,10 @@ new_server(int fd) crypto->ctx_init(crypto->cipher, server->d_ctx, 0); cipher_ctx_pair(server->e_ctx, server->d_ctx); - ev_io_init(&server->recv_ctx->io, server_recv_cb, fd, EV_READ); - ev_io_init(&server->send_ctx->io, server_send_cb, fd, EV_WRITE); + ss_io_init(&server->recv_ctx->io, server_recv_cb, fd, SS_READ); + ss_io_init(&server->send_ctx->io, server_send_cb, fd, SS_WRITE); - ev_timer_init(&server->delayed_connect_watcher, delayed_connect_cb, 0.05, + ss_timer_init(&server->delayed_connect_watcher, delayed_connect_cb, 0.05, 0); return server; @@ -729,19 +729,19 @@ free_server(server_t *server) } static void -close_and_free_server(EV_P_ server_t *server) +close_and_free_server(SS_P_ server_t *server) { if (server != NULL) { - ev_io_stop(EV_A_ & server->send_ctx->io); - ev_io_stop(EV_A_ & server->recv_ctx->io); - ev_timer_stop(EV_A_ & server->delayed_connect_watcher); + ss_io_stop(SS_A_ & server->send_ctx->io); + ss_io_stop(SS_A_ & server->recv_ctx->io); + ss_timer_stop(SS_A_ & server->delayed_connect_watcher); ss_socket_close(server->fd); free_server(server); } } static void -accept_cb(EV_P_ ev_io *w, int revents) +accept_cb(SS_P_ ss_io *w, int revents) { listen_ctx_t *listener = (listen_ctx_t *)w; struct sockaddr_storage destaddr; @@ -850,27 +850,27 @@ accept_cb(EV_P_ ev_io *w, int revents) if (fast_open) { // save remote addr for fast open remote->addr = remote_addr; - ev_timer_start(EV_A_ & server->delayed_connect_watcher); + ss_timer_start(SS_A_ & server->delayed_connect_watcher); } else { int r = connect(remotefd, remote_addr, get_sockaddr_len(remote_addr)); if (r == -1 && ss_socket_error() != CONNECT_IN_PROGRESS) { ERROR("connect"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } // listen to remote connected event - ev_io_start(EV_A_ & remote->send_ctx->io); - ev_timer_start(EV_A_ & remote->send_ctx->watcher); + ss_io_start(SS_A_ & remote->send_ctx->io); + ss_timer_start(SS_A_ & remote->send_ctx->watcher); } - ev_io_start(EV_A_ & server->recv_ctx->io); + ss_io_start(SS_A_ & server->recv_ctx->io); } static void -signal_cb(EV_P_ ev_signal *w, int revents) +signal_cb(SS_P_ ss_signal *w, int revents) { - if (revents & EV_SIGNAL) { + if (revents & SS_SIGNAL) { switch (w->signum) { case SIGCHLD: if (!is_plugin_running()) { @@ -880,11 +880,11 @@ signal_cb(EV_P_ ev_signal *w, int revents) return; case SIGINT: case SIGTERM: - ev_signal_stop(EV_DEFAULT, &sigint_watcher); - ev_signal_stop(EV_DEFAULT, &sigterm_watcher); - ev_signal_stop(EV_DEFAULT, &sigchld_watcher); + ss_signal_stop(SS_DEFAULT, &sigint_watcher); + ss_signal_stop(SS_DEFAULT, &sigterm_watcher); + ss_signal_stop(SS_DEFAULT, &sigchld_watcher); - ev_unloop(EV_A_ EVUNLOOP_ALL); + ss_unloop(SS_A_ SS_UNLOOP_ALL); } } } @@ -1291,12 +1291,12 @@ main(int argc, char **argv) signal(SIGPIPE, SIG_IGN); signal(SIGABRT, SIG_IGN); - ev_signal_init(&sigint_watcher, signal_cb, SIGINT); - ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); - ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); - ev_signal_start(EV_DEFAULT, &sigint_watcher); - ev_signal_start(EV_DEFAULT, &sigterm_watcher); - ev_signal_start(EV_DEFAULT, &sigchld_watcher); + ss_signal_init(&sigint_watcher, signal_cb, SIGINT); + ss_signal_init(&sigterm_watcher, signal_cb, SIGTERM); + ss_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); + ss_signal_start(SS_DEFAULT, &sigint_watcher); + ss_signal_start(SS_DEFAULT, &sigterm_watcher); + ss_signal_start(SS_DEFAULT, &sigchld_watcher); // Setup keys LOGI("initializing ciphers... %s", method); @@ -1331,7 +1331,7 @@ main(int argc, char **argv) listen_ctx.timeout = timeout_secs; listen_ctx.mptcp = mptcp; - struct ev_loop *loop = EV_DEFAULT; + struct ss_loop *loop = SS_DEFAULT; listen_ctx_t *listen_ctx_current = &listen_ctx; do { @@ -1355,8 +1355,8 @@ main(int argc, char **argv) listen_ctx_current->fd = listenfd; - ev_io_init(&listen_ctx_current->io, accept_cb, listenfd, EV_READ); - ev_io_start(loop, &listen_ctx_current->io); + ss_io_init(&listen_ctx_current->io, accept_cb, listenfd, SS_READ); + ss_io_start(loop, &listen_ctx_current->io); } // Setup UDP @@ -1399,7 +1399,7 @@ main(int argc, char **argv) LOGI("running from root user"); } - ev_run(loop, 0); + ss_run(loop, 0); if (plugin != NULL) { stop_plugin(); diff --git a/src/redir.h b/src/redir.h index 2544d65eb..a03bf9cff 100644 --- a/src/redir.h +++ b/src/redir.h @@ -22,17 +22,13 @@ #ifndef _REDIR_H #define _REDIR_H -#ifdef HAVE_LIBEV_EV_H -#include -#else -#include -#endif +#include "ss_event.h" #include "crypto.h" #include "jconf.h" typedef struct listen_ctx { - ev_io io; + ss_io io; int remote_num; int timeout; int fd; @@ -42,7 +38,7 @@ typedef struct listen_ctx { } listen_ctx_t; typedef struct server_ctx { - ev_io io; + ss_io io; int connected; struct server *server; } server_ctx_t; @@ -59,12 +55,12 @@ typedef struct server { struct remote *remote; struct sockaddr_storage destaddr; - ev_timer delayed_connect_watcher; + ss_timer delayed_connect_watcher; } server_t; typedef struct remote_ctx { - ev_io io; - ev_timer watcher; + ss_io io; + ss_timer watcher; int connected; struct remote *remote; } remote_ctx_t; diff --git a/src/resolv.c b/src/resolv.c index 9c42d2f75..5c6a7bf82 100644 --- a/src/resolv.c +++ b/src/resolv.c @@ -43,11 +43,7 @@ #endif #include -#ifdef HAVE_LIBEV_EV_H -#include -#else -#include -#endif +#include "ss_event.h" #include "core.h" @@ -59,14 +55,17 @@ * Implement DNS resolution interface using libc-ares */ -#define SS_NUM_IOS 6 -#define SS_INVALID_FD -1 #define SS_TIMER_AFTER 1.0 +struct resolv_io { + ss_io io; + struct resolv_io *next; +}; + struct resolv_ctx { - struct ev_io ios[SS_NUM_IOS]; - struct ev_timer timer; - ev_tstamp last_tick; + struct resolv_io *ios; + struct ss_timer timer; + ss_tstamp last_tick; ares_channel channel; struct ares_options options; @@ -90,7 +89,7 @@ struct resolv_query { extern int verbose; static struct resolv_ctx default_ctx; -static struct ev_loop *default_loop; +static struct ss_loop *default_loop; enum { MODE_IPV4_FIRST = 0, @@ -99,8 +98,8 @@ enum { static int resolv_mode = MODE_IPV4_FIRST; -static void resolv_sock_cb(struct ev_loop *, struct ev_io *, int); -static void resolv_timer_cb(struct ev_loop *, struct ev_timer *, int); +static void resolv_sock_cb(struct ss_loop *, struct ss_io *, int); +static void resolv_timer_cb(struct ss_loop *, struct ss_timer *, int); static void resolv_sock_state_cb(void *, ares_socket_t, int, int); static void dns_query_v4_cb(void *, int, int, struct hostent *); @@ -116,22 +115,22 @@ static struct sockaddr *choose_any(struct resolv_query *); * DNS UDP socket activity callback */ static void -resolv_sock_cb(EV_P_ ev_io *w, int revents) +resolv_sock_cb(SS_P_ ss_io *w, int revents) { ares_socket_t rfd = ARES_SOCKET_BAD, wfd = ARES_SOCKET_BAD; - if (revents & EV_READ) + if (revents & SS_READ) rfd = w->fd; - if (revents & EV_WRITE) + if (revents & SS_WRITE) wfd = w->fd; - default_ctx.last_tick = ev_now(default_loop); + default_ctx.last_tick = ss_now(default_loop); ares_process_fd(default_ctx.channel, rfd, wfd); } int -resolv_init(struct ev_loop *loop, char *nameservers, int ipv6first) +resolv_init(struct ss_loop *loop, char *nameservers, int ipv6first) { int status; @@ -176,22 +175,23 @@ resolv_init(struct ev_loop *loop, char *nameservers, int ipv6first) FATAL("failed to set nameservers"); } - for (int i = 0; i < SS_NUM_IOS; i++) - ev_io_init(&default_ctx.ios[i], resolv_sock_cb, SS_INVALID_FD, 0); - - default_ctx.last_tick = ev_now(default_loop); - ev_init(&default_ctx.timer, resolv_timer_cb); + default_ctx.last_tick = ss_now(default_loop); + ss_timer_init(&default_ctx.timer, resolv_timer_cb, 0, 0); resolv_timer_cb(default_loop, &default_ctx.timer, 0); return 0; } void -resolv_shutdown(struct ev_loop *loop) +resolv_shutdown(struct ss_loop *loop) { - ev_timer_stop(default_loop, &default_ctx.timer); - for (int i = 0; i < SS_NUM_IOS; i++) - ev_io_stop(default_loop, &default_ctx.ios[i]); + ss_timer_stop(default_loop, &default_ctx.timer); + while (default_ctx.ios) { + struct resolv_io *node = default_ctx.ios; + default_ctx.ios = node->next; + ss_io_stop(default_loop, &node->io); + ss_free(node); + } ares_cancel(default_ctx.channel); ares_destroy(default_ctx.channel); @@ -431,23 +431,23 @@ all_requests_are_null(struct resolv_query *query) * Timer callback */ static void -resolv_timer_cb(struct ev_loop *loop, struct ev_timer *w, int revents) +resolv_timer_cb(struct ss_loop *loop, struct ss_timer *w, int revents) { - struct resolv_ctx *ctx = ss_container_of(w, struct resolv_ctx, timer); + struct resolv_ctx *ctx = &default_ctx; - ev_tstamp now = ev_now(default_loop); - ev_tstamp after = ctx->last_tick - now + SS_TIMER_AFTER; + ss_tstamp now = ss_now(default_loop); + ss_tstamp after = ctx->last_tick - now + SS_TIMER_AFTER; if (after < 0.0) { ctx->last_tick = now; ares_process_fd(ctx->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); - ev_timer_set(w, SS_TIMER_AFTER, 0.0); + ss_timer_set(w, SS_TIMER_AFTER, 0.0); } else { - ev_timer_set(w, after, 0.0); + ss_timer_set(w, after, 0.0); } - ev_timer_start(loop, w); + ss_timer_start(loop, w); } /* @@ -457,35 +457,25 @@ static void resolv_sock_state_cb(void *data, ares_socket_t s, int read, int write) { struct resolv_ctx *ctx = (struct resolv_ctx *)data; - int events = (read ? EV_READ : 0) | (write ? EV_WRITE : 0); - - int i = 0, ffi = -1; - for (; i < SS_NUM_IOS; i++) { - if (ctx->ios[i].fd == s) { - break; - } - - if (ffi < 0 && ctx->ios[i].fd == SS_INVALID_FD) { - // first free index - ffi = i; + int events = (read ? SS_READ : 0) | (write ? SS_WRITE : 0); + + struct resolv_io **entry = &ctx->ios; + while (*entry && (ares_socket_t)(unsigned int)(*entry)->io.fd != s) + entry = &(*entry)->next; + struct resolv_io *node = *entry; + if (node) ss_io_stop(default_loop, &node->io); + if (!events) { + if (node) { + *entry = node->next; + ss_free(node); } + return; } - - if (i < SS_NUM_IOS) { - ev_io_stop(default_loop, &ctx->ios[i]); - } else if (ffi > -1) { - i = ffi; - } else { - LOGE("failed to find free I/O watcher slot for DNS query"); - // last resort: stop io and re-use slot, will cause timeout - i = 0; - ev_io_stop(default_loop, &ctx->ios[i]); - } - - if (events) { - ev_io_set(&ctx->ios[i], s, events); - ev_io_start(default_loop, &ctx->ios[i]); - } else { - ev_io_set(&ctx->ios[i], SS_INVALID_FD, 0); - } + if (!node) { + node = ss_malloc(sizeof(*node)); + node->next = NULL; + *entry = node; + ss_io_init(&node->io, resolv_sock_cb, (int)s, events); + } else ss_io_set(&node->io, (int)s, events); + ss_io_start(default_loop, &node->io); } diff --git a/src/resolv.h b/src/resolv.h index e211abc66..326eca0d3 100644 --- a/src/resolv.h +++ b/src/resolv.h @@ -35,12 +35,13 @@ #include #endif +struct ss_loop; struct resolv_query; -int resolv_init(struct ev_loop *, char *, int); +int resolv_init(struct ss_loop *, char *, int); void resolv_start(const char *hostname, uint16_t port, void (*client_cb)(struct sockaddr *, void *), void (*free_cb)(void *), void *data); -void resolv_shutdown(struct ev_loop *); +void resolv_shutdown(struct ss_loop *); #endif diff --git a/src/server.c b/src/server.c index fd7c05f90..57d85a8a5 100644 --- a/src/server.c +++ b/src/server.c @@ -98,23 +98,23 @@ enum datatypes { #endif -static void signal_cb(EV_P_ ev_signal *w, int revents); -static void accept_cb(EV_P_ ev_io *w, int revents); -static void server_send_cb(EV_P_ ev_io *w, int revents); -static void server_recv_cb(EV_P_ ev_io *w, int revents); -static void remote_recv_cb(EV_P_ ev_io *w, int revents); -static void remote_send_cb(EV_P_ ev_io *w, int revents); -static void server_timeout_cb(EV_P_ ev_timer *watcher, int revents); +static void signal_cb(SS_P_ ss_signal *w, int revents); +static void accept_cb(SS_P_ ss_io *w, int revents); +static void server_send_cb(SS_P_ ss_io *w, int revents); +static void server_recv_cb(SS_P_ ss_io *w, int revents); +static void remote_recv_cb(SS_P_ ss_io *w, int revents); +static void remote_send_cb(SS_P_ ss_io *w, int revents); +static void server_timeout_cb(SS_P_ ss_timer *watcher, int revents); static remote_t *new_remote(int fd); static server_t *new_server(int fd, listen_ctx_t *listener); -static remote_t *connect_to_remote(EV_P_ struct addrinfo *res, +static remote_t *connect_to_remote(SS_P_ struct addrinfo *res, server_t *server); static void free_remote(remote_t *remote); -static void close_and_free_remote(EV_P_ remote_t *remote); +static void close_and_free_remote(SS_P_ remote_t *remote); static void free_server(server_t *server); -static void close_and_free_server(EV_P_ server_t *server); +static void close_and_free_server(SS_P_ server_t *server); static void resolv_cb(struct sockaddr *addr, void *data); static void resolv_free_cb(void *data); @@ -151,16 +151,16 @@ uint64_t tx = 0; uint64_t rx = 0; #ifndef __MINGW32__ -ev_timer stat_update_watcher; +ss_timer stat_update_watcher; #endif -static struct ev_signal sigint_watcher; -static struct ev_signal sigterm_watcher; +static struct ss_signal sigint_watcher; +static struct ss_signal sigterm_watcher; #ifndef __MINGW32__ -static struct ev_signal sigchld_watcher; +static struct ss_signal sigchld_watcher; #else static struct plugin_watcher_t { - ev_io io; + ss_io io; SOCKET fd; uint16_t port; int valid; @@ -170,8 +170,22 @@ static struct plugin_watcher_t { static struct ss_list connections; #ifndef __MINGW32__ +static int setnonblocking(int fd); +static void stat_resolved(struct sockaddr *address, void *data) +{ + if (!address) return; + int fd = ss_socket(address->sa_family, SOCK_DGRAM, 0); + if (fd == -1) return; + setnonblocking(fd); + const char *message = data; + size_t length = strlen(message) + 1; + if (sendto(fd, message, length, 0, address, get_sockaddr_len(address)) != (ssize_t)length) + ERROR("stat_sendto"); + ss_socket_close(fd); +} + static void -stat_update_cb(EV_P_ ev_timer *watcher, int revents) +stat_update_cb(SS_P_ ss_timer *watcher, int revents) { struct sockaddr_un svaddr, claddr; int sfd = -1; @@ -189,6 +203,8 @@ stat_update_cb(EV_P_ ev_timer *watcher, int revents) parse_addr(manager_addr, &ip_addr); if (ip_addr.host == NULL || ip_addr.port == NULL) { + ss_free(ip_addr.host); + ss_free(ip_addr.port); sfd = ss_socket(AF_UNIX, SOCK_DGRAM, 0); if (sfd == -1) { ERROR("stat_socket"); @@ -226,27 +242,15 @@ stat_update_cb(EV_P_ ev_timer *watcher, int revents) unlink(claddr.sun_path); } else { - struct sockaddr_storage storage; - memset(&storage, 0, sizeof(struct sockaddr_storage)); - if (get_sockaddr(ip_addr.host, ip_addr.port, &storage, 0, ipv6first) == -1) { - ERROR("failed to parse the manager addr"); - return; - } - - sfd = ss_socket(storage.ss_family, SOCK_DGRAM, 0); - - if (sfd == -1) { - ERROR("stat_socket"); - return; - } - - size_t addr_len = get_sockaddr_len((struct sockaddr *)&storage); - if (sendto(sfd, resp, strlen(resp) + 1, 0, (struct sockaddr *)&storage, - addr_len) != msgLen) { - ERROR("stat_sendto"); - ss_socket_close(sfd); - return; - } + uint16_t port; + if (parse_numeric_port(ip_addr.port, &port) == 0) { + char *message = strdup(resp); + if (!message) FATAL("out of memory"); + resolv_start(ip_addr.host, htons(port), stat_resolved, NULL, message); + } + ss_free(ip_addr.host); + ss_free(ip_addr.port); + return; } ss_socket_close(sfd); @@ -255,7 +259,7 @@ stat_update_cb(EV_P_ ev_timer *watcher, int revents) #endif static void -free_connections(struct ev_loop *loop) +free_connections(struct ss_loop *loop) { struct ss_list_item *curr, *next; ss_list_foreach_void(&connections, curr, next) { @@ -290,7 +294,7 @@ get_peer_name(int fd) } static void -stop_server(EV_P_ server_t *server) +stop_server(SS_P_ server_t *server) { server->stage = STAGE_STOP; } @@ -669,7 +673,7 @@ create_and_bind(const char *host, const char *port, int mptcp) } static remote_t * -connect_to_remote(EV_P_ struct addrinfo *res, +connect_to_remote(SS_P_ struct addrinfo *res, server_t *server) { int sockfd; @@ -847,7 +851,7 @@ connect_to_remote(EV_P_ struct addrinfo *res, if (r == -1 && ss_socket_error() != CONNECT_IN_PROGRESS) { ERROR("connect"); - close_and_free_remote(EV_A_ remote); + close_and_free_remote(SS_A_ remote); return NULL; } } @@ -947,7 +951,7 @@ setTosFromConnmark(remote_t *remote, server_t *server) #endif static void -server_recv_cb(EV_P_ ev_io *w, int revents) +server_recv_cb(SS_P_ ss_io *w, int revents) { server_ctx_t *server_recv_ctx = (server_ctx_t *)w; server_t *server = server_recv_ctx->server; @@ -960,15 +964,15 @@ server_recv_cb(EV_P_ ev_io *w, int revents) buf = remote->buf; // Only timer the watcher if a valid connection is established - ev_timer_again(EV_A_ & server->recv_ctx->watcher); + ss_timer_again(SS_A_ & server->recv_ctx->watcher); } ssize_t r = recv(server->fd, buf->data, SOCKET_BUF_SIZE, 0); if (r == 0) { // connection closed - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else if (r == -1) { if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { @@ -977,8 +981,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents) return; } else { ERROR("server recv"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -995,7 +999,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (err == CRYPTO_ERROR) { report_addr(server->fd, "authentication error"); - stop_server(EV_A_ server); + stop_server(SS_A_ server); return; } else if (err == CRYPTO_NEED_MORE) { if (server->stage != STAGE_STREAM) { @@ -1011,18 +1015,18 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { // no data, wait for send remote->buf->idx = 0; - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); } else { ERROR("server_recv_send"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } } else if (s < remote->buf->len) { remote->buf->len -= s; remote->buf->idx = s; - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); } return; } else if (server->stage == STAGE_INIT) { @@ -1041,7 +1045,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) int need_query = 0; if (server->buf->len < 1) { report_addr(server->fd, "missing address type"); - stop_server(EV_A_ server); + stop_server(SS_A_ server); return; } char atyp = server->buf->data[offset++]; @@ -1065,7 +1069,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) offset += in_addr_len; } else { report_addr(server->fd, "invalid length for ipv4 address"); - stop_server(EV_A_ server); + stop_server(SS_A_ server); return; } memcpy(&addr->sin_port, server->buf->data + offset, sizeof(uint16_t)); @@ -1078,7 +1082,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) // Domain name if (server->buf->len < offset + 1) { report_addr(server->fd, "missing host name length"); - stop_server(EV_A_ server); + stop_server(SS_A_ server); return; } uint8_t name_len = *(uint8_t *)(server->buf->data + offset); @@ -1088,13 +1092,13 @@ server_recv_cb(EV_P_ ev_io *w, int revents) offset += name_len + 1; } else { report_addr(server->fd, "invalid host name length"); - stop_server(EV_A_ server); + stop_server(SS_A_ server); return; } if (acl && outbound_block_match_host(host) == 1) { if (verbose) LOGI("outbound blocked %s", host); - close_and_free_server(EV_A_ server); + close_and_free_server(SS_A_ server); return; } struct ss_ip ip; @@ -1121,7 +1125,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) } else { if (!validate_hostname(host, name_len)) { report_addr(server->fd, "invalid host name"); - stop_server(EV_A_ server); + stop_server(SS_A_ server); return; } need_query = 1; @@ -1139,7 +1143,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) } else { LOGE("invalid header with addr type %d", atyp); report_addr(server->fd, "invalid length for ipv6 address"); - stop_server(EV_A_ server); + stop_server(SS_A_ server); return; } memcpy(&addr->sin6_port, server->buf->data + offset, sizeof(uint16_t)); @@ -1152,7 +1156,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (offset == 1) { report_addr(server->fd, "invalid address type"); - stop_server(EV_A_ server); + stop_server(SS_A_ server); return; } @@ -1162,7 +1166,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (server->buf->len < offset) { report_addr(server->fd, "invalid request length"); - stop_server(EV_A_ server); + stop_server(SS_A_ server); return; } else { server->buf->len -= offset; @@ -1177,11 +1181,11 @@ server_recv_cb(EV_P_ ev_io *w, int revents) } if (!need_query) { - remote_t *remote = connect_to_remote(EV_A_ & info, server); + remote_t *remote = connect_to_remote(SS_A_ & info, server); if (remote == NULL) { LOGE("connect error"); - close_and_free_server(EV_A_ server); + close_and_free_server(SS_A_ server); return; } else { server->remote = remote; @@ -1199,11 +1203,11 @@ server_recv_cb(EV_P_ ev_io *w, int revents) } // waiting on remote connected event - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); } } else { - ev_io_stop(EV_A_ & server_recv_ctx->io); + ss_io_stop(SS_A_ & server_recv_ctx->io); query_t *query = ss_malloc(sizeof(query_t)); memset(query, 0, sizeof(query_t)); @@ -1222,7 +1226,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) } static void -server_send_cb(EV_P_ ev_io *w, int revents) +server_send_cb(SS_P_ ss_io *w, int revents) { server_ctx_t *server_send_ctx = (server_ctx_t *)w; server_t *server = server_send_ctx->server; @@ -1230,14 +1234,14 @@ server_send_cb(EV_P_ ev_io *w, int revents) if (remote == NULL) { LOGE("invalid server"); - close_and_free_server(EV_A_ server); + close_and_free_server(SS_A_ server); return; } if (server->buf->len == 0) { // close and free - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else { // has data to send @@ -1246,8 +1250,8 @@ server_send_cb(EV_P_ ev_io *w, int revents) if (s == -1) { if (ss_socket_error() != EAGAIN && ss_socket_error() != EWOULDBLOCK) { ERROR("server_send_send"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } return; } else if (s < server->buf->len) { @@ -1259,14 +1263,14 @@ server_send_cb(EV_P_ ev_io *w, int revents) // all sent out, wait for reading server->buf->len = 0; server->buf->idx = 0; - ev_io_stop(EV_A_ & server_send_ctx->io); + ss_io_stop(SS_A_ & server_send_ctx->io); if (remote != NULL) { - ev_io_start(EV_A_ & remote->recv_ctx->io); + ss_io_start(SS_A_ & remote->recv_ctx->io); return; } else { LOGE("invalid remote"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -1274,7 +1278,7 @@ server_send_cb(EV_P_ ev_io *w, int revents) } static void -server_timeout_cb(EV_P_ ev_timer *watcher, int revents) +server_timeout_cb(SS_P_ ss_timer *watcher, int revents) { server_ctx_t *server_ctx = ss_container_of(watcher, server_ctx_t, watcher); @@ -1285,8 +1289,8 @@ server_timeout_cb(EV_P_ ev_timer *watcher, int revents) LOGI("TCP connection timeout"); } - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } static void @@ -1310,11 +1314,11 @@ resolv_cb(struct sockaddr *addr, void *data) if (server == NULL) return; - struct ev_loop *loop = server->listen_ctx->loop; + struct ss_loop *loop = server->listen_ctx->loop; if (addr == NULL) { LOGE("unable to resolve %s", query->hostname); - close_and_free_server(EV_A_ server); + close_and_free_server(SS_A_ server); } else { if (verbose) { LOGI("successfully resolved %s", query->hostname); @@ -1334,10 +1338,10 @@ resolv_cb(struct sockaddr *addr, void *data) info.ai_addrlen = sizeof(struct sockaddr_in6); } - remote_t *remote = connect_to_remote(EV_A_ & info, server); + remote_t *remote = connect_to_remote(SS_A_ & info, server); if (remote == NULL) { - close_and_free_server(EV_A_ server); + close_and_free_server(SS_A_ server); } else { server->remote = remote; remote->server = server; @@ -1354,13 +1358,13 @@ resolv_cb(struct sockaddr *addr, void *data) } // listen to remote connected event - ev_io_start(EV_A_ & remote->send_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); } } } static void -remote_recv_cb(EV_P_ ev_io *w, int revents) +remote_recv_cb(SS_P_ ss_io *w, int revents) { remote_ctx_t *remote_recv_ctx = (remote_ctx_t *)w; remote_t *remote = remote_recv_ctx->remote; @@ -1368,18 +1372,18 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) if (server == NULL) { LOGE("invalid server"); - close_and_free_remote(EV_A_ remote); + close_and_free_remote(SS_A_ remote); return; } - ev_timer_again(EV_A_ & server->recv_ctx->watcher); + ss_timer_again(SS_A_ & server->recv_ctx->watcher); ssize_t r = recv(remote->fd, server->buf->data, SOCKET_BUF_SIZE, 0); if (r == 0) { // connection closed - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else if (r == -1) { if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { @@ -1388,8 +1392,8 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) return; } else { ERROR("remote recv"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -1406,8 +1410,8 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) if (err) { LOGE("invalid password or cipher"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } @@ -1420,19 +1424,19 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { // no data, wait for send server->buf->idx = 0; - ev_io_stop(EV_A_ & remote_recv_ctx->io); - ev_io_start(EV_A_ & server->send_ctx->io); + ss_io_stop(SS_A_ & remote_recv_ctx->io); + ss_io_start(SS_A_ & server->send_ctx->io); } else { ERROR("remote_recv_send"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } else if (s < server->buf->len) { server->buf->len -= s; server->buf->idx = s; - ev_io_stop(EV_A_ & remote_recv_ctx->io); - ev_io_start(EV_A_ & server->send_ctx->io); + ss_io_stop(SS_A_ & remote_recv_ctx->io); + ss_io_start(SS_A_ & server->send_ctx->io); } // Disable TCP_NODELAY after the first response are sent @@ -1445,7 +1449,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) } static void -remote_send_cb(EV_P_ ev_io *w, int revents) +remote_send_cb(SS_P_ ss_io *w, int revents) { remote_ctx_t *remote_send_ctx = (remote_ctx_t *)w; remote_t *remote = remote_send_ctx->remote; @@ -1453,7 +1457,7 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (server == NULL) { LOGE("invalid server"); - close_and_free_remote(EV_A_ remote); + close_and_free_remote(SS_A_ remote); return; } @@ -1476,8 +1480,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) } else { ERROR("WSAGetOverlappedResult"); // not connected - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -1500,24 +1504,24 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (remote->buf->len == 0) { server->stage = STAGE_STREAM; - ev_io_stop(EV_A_ & remote_send_ctx->io); - ev_io_start(EV_A_ & server->recv_ctx->io); - ev_io_start(EV_A_ & remote->recv_ctx->io); + ss_io_stop(SS_A_ & remote_send_ctx->io); + ss_io_start(SS_A_ & server->recv_ctx->io); + ss_io_start(SS_A_ & remote->recv_ctx->io); return; } } else { ERROR("getpeername"); // not connected - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } if (remote->buf->len == 0) { // close and free - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else { // has data to send @@ -1527,8 +1531,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (ss_socket_error() != EAGAIN && ss_socket_error() != EWOULDBLOCK) { ERROR("remote_send_send"); // close and free - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } return; } else if (s < remote->buf->len) { @@ -1540,17 +1544,17 @@ remote_send_cb(EV_P_ ev_io *w, int revents) // all sent out, wait for reading remote->buf->len = 0; remote->buf->idx = 0; - ev_io_stop(EV_A_ & remote_send_ctx->io); + ss_io_stop(SS_A_ & remote_send_ctx->io); if (server != NULL) { - ev_io_start(EV_A_ & server->recv_ctx->io); + ss_io_start(SS_A_ & server->recv_ctx->io); if (server->stage != STAGE_STREAM) { server->stage = STAGE_STREAM; - ev_io_start(EV_A_ & remote->recv_ctx->io); + ss_io_start(SS_A_ & remote->recv_ctx->io); } } else { LOGE("invalid server"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } return; } @@ -1581,8 +1585,8 @@ new_remote(int fd) remote->send_ctx->connected = 0; remote->server = NULL; - ev_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, EV_READ); - ev_io_init(&remote->send_ctx->io, remote_send_cb, fd, EV_WRITE); + ss_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, SS_READ); + ss_io_init(&remote->send_ctx->io, remote_send_cb, fd, SS_WRITE); return remote; } @@ -1603,11 +1607,11 @@ free_remote(remote_t *remote) } static void -close_and_free_remote(EV_P_ remote_t *remote) +close_and_free_remote(SS_P_ remote_t *remote) { if (remote != NULL) { - ev_io_stop(EV_A_ & remote->send_ctx->io); - ev_io_stop(EV_A_ & remote->recv_ctx->io); + ss_io_stop(SS_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & remote->recv_ctx->io); ss_socket_close(remote->fd); free_remote(remote); if (verbose) { @@ -1654,9 +1658,9 @@ new_server(int fd, listen_ctx_t *listener) cipher_ctx_pair(server->e_ctx, server->d_ctx); int timeout = max(MIN_TCP_IDLE_TIMEOUT, server->listen_ctx->timeout); - ev_io_init(&server->recv_ctx->io, server_recv_cb, fd, EV_READ); - ev_io_init(&server->send_ctx->io, server_send_cb, fd, EV_WRITE); - ev_timer_init(&server->recv_ctx->watcher, server_timeout_cb, + ss_io_init(&server->recv_ctx->io, server_recv_cb, fd, SS_READ); + ss_io_init(&server->send_ctx->io, server_send_cb, fd, SS_WRITE); + ss_timer_init(&server->recv_ctx->watcher, server_timeout_cb, timeout, timeout); ss_list_add(&connections, &server->entries); @@ -1702,16 +1706,16 @@ free_server(server_t *server) } static void -close_and_free_server(EV_P_ server_t *server) +close_and_free_server(SS_P_ server_t *server) { if (server != NULL) { if (server->query != NULL) { server->query->server = NULL; server->query = NULL; } - ev_io_stop(EV_A_ & server->send_ctx->io); - ev_io_stop(EV_A_ & server->recv_ctx->io); - ev_timer_stop(EV_A_ & server->recv_ctx->watcher); + ss_io_stop(SS_A_ & server->send_ctx->io); + ss_io_stop(SS_A_ & server->recv_ctx->io); + ss_timer_stop(SS_A_ & server->recv_ctx->watcher); ss_socket_close(server->fd); free_server(server); if (verbose) { @@ -1722,9 +1726,9 @@ close_and_free_server(EV_P_ server_t *server) } static void -signal_cb(EV_P_ ev_signal *w, int revents) +signal_cb(SS_P_ ss_signal *w, int revents) { - if (revents & EV_SIGNAL) { + if (revents & SS_SIGNAL) { switch (w->signum) { #ifndef __MINGW32__ case SIGCHLD: @@ -1736,21 +1740,21 @@ signal_cb(EV_P_ ev_signal *w, int revents) #endif case SIGINT: case SIGTERM: - ev_signal_stop(EV_DEFAULT, &sigint_watcher); - ev_signal_stop(EV_DEFAULT, &sigterm_watcher); + ss_signal_stop(SS_DEFAULT, &sigint_watcher); + ss_signal_stop(SS_DEFAULT, &sigterm_watcher); #ifndef __MINGW32__ - ev_signal_stop(EV_DEFAULT, &sigchld_watcher); + ss_signal_stop(SS_DEFAULT, &sigchld_watcher); #else - ev_io_stop(EV_DEFAULT, &plugin_watcher.io); + ss_io_stop(SS_DEFAULT, &plugin_watcher.io); #endif - ev_unloop(EV_A_ EVUNLOOP_ALL); + ss_unloop(SS_A_ SS_UNLOOP_ALL); } } } #ifdef __MINGW32__ static void -plugin_watcher_cb(EV_P_ ev_io *w, int revents) +plugin_watcher_cb(SS_P_ ss_io *w, int revents) { char buf[1]; SOCKET fd = ss_accept(plugin_watcher.fd, NULL, NULL); @@ -1761,16 +1765,16 @@ plugin_watcher_cb(EV_P_ ev_io *w, int revents) closesocket(fd); LOGE("plugin service exit unexpectedly"); ret_val = -1; - ev_signal_stop(EV_DEFAULT, &sigint_watcher); - ev_signal_stop(EV_DEFAULT, &sigterm_watcher); - ev_io_stop(EV_DEFAULT, &plugin_watcher.io); - ev_unloop(EV_A_ EVUNLOOP_ALL); + ss_signal_stop(SS_DEFAULT, &sigint_watcher); + ss_signal_stop(SS_DEFAULT, &sigterm_watcher); + ss_io_stop(SS_DEFAULT, &plugin_watcher.io); + ss_unloop(SS_A_ SS_UNLOOP_ALL); } #endif static void -accept_cb(EV_P_ ev_io *w, int revents) +accept_cb(SS_P_ ss_io *w, int revents) { listen_ctx_t *listener = (listen_ctx_t *)w; int serverfd = ss_accept(listener->fd, NULL, NULL); @@ -1800,8 +1804,8 @@ accept_cb(EV_P_ ev_io *w, int revents) setnonblocking(serverfd); server_t *server = new_server(serverfd, listener); - ev_io_start(EV_A_ & server->recv_ctx->io); - ev_timer_start(EV_A_ & server->recv_ctx->watcher); + ss_io_start(SS_A_ & server->recv_ctx->io); + ss_timer_start(SS_A_ & server->recv_ctx->watcher); } int @@ -2232,13 +2236,13 @@ main(int argc, char **argv) signal(SIGABRT, SIG_IGN); #endif - ev_signal_init(&sigint_watcher, signal_cb, SIGINT); - ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); - ev_signal_start(EV_DEFAULT, &sigint_watcher); - ev_signal_start(EV_DEFAULT, &sigterm_watcher); + ss_signal_init(&sigint_watcher, signal_cb, SIGINT); + ss_signal_init(&sigterm_watcher, signal_cb, SIGTERM); + ss_signal_start(SS_DEFAULT, &sigint_watcher); + ss_signal_start(SS_DEFAULT, &sigterm_watcher); #ifndef __MINGW32__ - ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); - ev_signal_start(EV_DEFAULT, &sigchld_watcher); + ss_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); + ss_signal_start(SS_DEFAULT, &sigchld_watcher); #endif // setup keys @@ -2248,7 +2252,7 @@ main(int argc, char **argv) FATAL("failed to initialize ciphers"); // initialize ev loop - struct ev_loop *loop = EV_DEFAULT; + struct ss_loop *loop = SS_DEFAULT; // setup dns resolv_init(loop, nameservers, ipv6first); @@ -2278,8 +2282,8 @@ main(int argc, char **argv) break; } plugin_watcher.fd = fd; - ev_io_init(&plugin_watcher.io, plugin_watcher_cb, fd, EV_READ); - ev_io_start(EV_DEFAULT, &plugin_watcher.io); + ss_io_init(&plugin_watcher.io, plugin_watcher_cb, fd, SS_READ); + ss_io_start(SS_DEFAULT, &plugin_watcher.io); plugin_watcher.valid = 1; } while (0); if (!plugin_watcher.valid) { @@ -2354,8 +2358,8 @@ main(int argc, char **argv) listen_ctx->iface = iface; listen_ctx->loop = loop; - ev_io_init(&listen_ctx->io, accept_cb, listenfd, EV_READ); - ev_io_start(loop, &listen_ctx->io); + ss_io_init(&listen_ctx->io, accept_cb, listenfd, SS_READ); + ss_io_start(loop, &listen_ctx->io); num_listen_ctx++; @@ -2394,8 +2398,8 @@ main(int argc, char **argv) #ifndef __MINGW32__ if (manager_addr != NULL) { - ev_timer_init(&stat_update_watcher, stat_update_cb, UPDATE_INTERVAL, UPDATE_INTERVAL); - ev_timer_start(EV_DEFAULT, &stat_update_watcher); + ss_timer_init(&stat_update_watcher, stat_update_cb, UPDATE_INTERVAL, UPDATE_INTERVAL); + ss_timer_start(SS_DEFAULT, &stat_update_watcher); } #endif @@ -2414,7 +2418,7 @@ main(int argc, char **argv) ss_list_init(&connections); // start ev loop - ev_run(loop, 0); + ss_run(loop, 0); if (verbose) { LOGI("closed gracefully"); @@ -2422,7 +2426,7 @@ main(int argc, char **argv) #ifndef __MINGW32__ if (manager_addr != NULL) { - ev_timer_stop(EV_DEFAULT, &stat_update_watcher); + ss_timer_stop(SS_DEFAULT, &stat_update_watcher); } #endif @@ -2437,7 +2441,7 @@ main(int argc, char **argv) for (int i = 0; i < server_num; i++) { listen_ctx_t *listen_ctx = &listen_ctx_list[i]; if (mode != UDP_ONLY) { - ev_io_stop(loop, &listen_ctx->io); + ss_io_stop(loop, &listen_ctx->io); ss_socket_close(listen_ctx->fd); } if (plugin != NULL) @@ -2451,6 +2455,7 @@ main(int argc, char **argv) if (mode != TCP_ONLY) { free_udprelay(); } + ss_loop_destroy(loop); #ifdef __MINGW32__ if (plugin_watcher.valid) { diff --git a/src/server.h b/src/server.h index 7cd5b4e67..8e0762f69 100644 --- a/src/server.h +++ b/src/server.h @@ -26,11 +26,7 @@ #include #include "core.h" -#ifdef HAVE_LIBEV_EV_H -#include -#else -#include -#endif +#include "ss_event.h" #ifdef __MINGW32__ #include "ss_windows.h" @@ -43,16 +39,16 @@ #include "common.h" typedef struct listen_ctx { - ev_io io; + ss_io io; int fd; int timeout; char *iface; - struct ev_loop *loop; + struct ss_loop *loop; } listen_ctx_t; typedef struct server_ctx { - ev_io io; - ev_timer watcher; + ss_io io; + ss_timer watcher; int connected; struct server *server; } server_ctx_t; @@ -101,7 +97,7 @@ typedef struct query { } query_t; typedef struct remote_ctx { - ev_io io; + ss_io io; int connected; struct remote *remote; } remote_ctx_t; diff --git a/src/ss_event.c b/src/ss_event.c new file mode 100644 index 000000000..5ff2b7bc0 --- /dev/null +++ b/src/ss_event.c @@ -0,0 +1,266 @@ +#include "ss_event.h" +#include +#include "uthash.h" +#include +#include +#include +#include +#include + +struct poll_state { + uv_poll_t handle; + struct ss_loop *loop; + ss_io *watchers; + int fd, events; + UT_hash_handle hh; +}; +struct timer_state { + uv_timer_t handle; + struct ss_loop *loop; + ss_timer *watcher; +}; +struct signal_state { + uv_signal_t handle; + struct ss_loop *loop; + ss_signal *watcher; +}; +struct ss_loop { + uv_loop_t native; + struct poll_state *polls; + uint64_t generation; +}; +static struct ss_loop *default_loop; +static void checked(int status) +{ + if (status < 0) { + fprintf(stderr, "event loop: %s\n", uv_strerror(status)); + abort(); + } +} +static void *allocate(size_t size) +{ + void *p = calloc(1, size); + if (!p) abort(); + return p; +} +static void closed(uv_handle_t *handle) { free(handle); } +uv_loop_t *ss_native_loop(struct ss_loop *loop) { return &loop->native; } +const char *ss_backend_name(void) +{ +#ifdef _WIN32 + return "iocp"; +#elif defined(__APPLE__) || defined(__FreeBSD__) + return "kqueue"; +#elif defined(__linux__) + return "epoll"; +#else + return "libuv"; +#endif +} +struct ss_loop *ss_loop_new(unsigned flags) +{ + (void)flags; + struct ss_loop *loop = allocate(sizeof(*loop)); + if (uv_loop_init(&loop->native)) { free(loop); return NULL; } + return loop; +} +struct ss_loop *ss_default_loop(void) +{ + if (!default_loop) { + default_loop = ss_loop_new(0); + if (!default_loop) abort(); + } + return default_loop; +} +static void close_walk(uv_handle_t *handle, void *arg) +{ + (void)arg; + if (!uv_is_closing(handle)) uv_close(handle, closed); +} +void ss_loop_destroy(struct ss_loop *loop) +{ + HASH_CLEAR(hh, loop->polls); + uv_walk(&loop->native, close_walk, NULL); + uv_run(&loop->native, UV_RUN_DEFAULT); + checked(uv_loop_close(&loop->native)); + if (loop == default_loop) default_loop = NULL; + free(loop); +} +int ss_run(struct ss_loop *loop, int flags) +{ + return uv_run(&loop->native, flags & SS_RUN_NOWAIT ? UV_RUN_NOWAIT : UV_RUN_DEFAULT); +} +void ss_unloop(struct ss_loop *loop, int how) +{ + (void)how; + uv_stop(&loop->native); +} +double ss_now(struct ss_loop *loop) { return uv_now(&loop->native) / 1000.0; } +double ss_time(void) +{ + uv_timeval64_t now; + checked(uv_gettimeofday(&now)); + return (double)now.tv_sec + now.tv_usec / 1000000.0; +} +struct pending { ss_io *watcher; uint64_t generation; }; +static void polled(uv_poll_t *handle, int status, int events) +{ + struct poll_state *state = (struct poll_state *)handle; + int ready = (events & UV_READABLE ? SS_READ : 0) | + (events & UV_WRITABLE ? SS_WRITE : 0); + if (status < 0) ready = SS_READ | SS_WRITE | SS_ERROR; + size_t count = 0; + for (ss_io *w = state->watchers; w; w = w->next) count++; + struct pending local[8]; + struct pending *pending = count > 8 ? allocate(count * sizeof(*pending)) : local; + size_t n = 0; + for (ss_io *w = state->watchers; w; w = w->next) + pending[n++] = (struct pending){w, w->generation}; + /* A callback can stop/free itself or another watcher, or reuse its memory. + * Revalidate both membership and generation before dereferencing it. */ + for (size_t i = 0; i < count; i++) { + for (ss_io *w = state->watchers; w; w = w->next) { + if (w == pending[i].watcher && w->generation == pending[i].generation) { + int deliver = ready & (w->events | SS_ERROR); + if (deliver) w->cb(state->loop, w, deliver); + break; + } + } + } + if (pending != local) free(pending); +} +static void update_poll(struct poll_state *state) +{ + int events = 0; + for (ss_io *w = state->watchers; w; w = w->next) events |= w->events; + if (events == state->events) return; + state->events = events; + checked(uv_poll_start(&state->handle, + (events & SS_READ ? UV_READABLE : 0) | (events & SS_WRITE ? UV_WRITABLE : 0), polled)); +} +void ss_io_init(ss_io *w, void (*cb)(struct ss_loop *, ss_io *, int), int fd, int events) +{ + *w = (ss_io){.fd = fd, .events = events, .cb = cb}; +} +void ss_io_set(ss_io *w, int fd, int events) +{ + assert(!w->backend); + w->fd = fd; w->events = events; +} +void ss_io_start(struct ss_loop *loop, ss_io *w) +{ + if (w->backend) return; + struct poll_state *state; + HASH_FIND_INT(loop->polls, &w->fd, state); + if (!state) { + state = allocate(sizeof(*state)); + state->fd = w->fd; state->loop = loop; +#ifdef _WIN32 + checked(uv_poll_init_socket(&loop->native, &state->handle, (SOCKET)(unsigned int)w->fd)); +#else + checked(uv_poll_init(&loop->native, &state->handle, w->fd)); +#endif + // NOLINTNEXTLINE(clang-analyzer-core.DivideZero): uthash expands a nonempty table, so ceil(num_items / num_buckets) is positive. + HASH_ADD_INT(loop->polls, fd, state); + } + w->backend = state; + w->generation = ++loop->generation; + w->next = state->watchers; + state->watchers = w; + update_poll(state); +} +void ss_io_stop(struct ss_loop *loop, ss_io *w) +{ + struct poll_state *state = w->backend; + if (!state) return; + assert(state->loop == loop); + ss_io **item = &state->watchers; + while (*item != w) item = &(*item)->next; + *item = w->next; + w->backend = NULL; w->next = NULL; + if (!state->watchers) { + checked(uv_poll_stop(&state->handle)); + HASH_DEL(loop->polls, state); + uv_close((uv_handle_t *)&state->handle, closed); + } else update_poll(state); +} +static uint64_t milliseconds(double seconds) +{ + return seconds > 0 ? (uint64_t)ceil(seconds * 1000.0) : 0; +} +static void timed(uv_timer_t *handle) +{ + struct timer_state *state = (struct timer_state *)handle; + ss_timer *w = state->watcher; + struct ss_loop *loop = state->loop; + if (!w->repeat) ss_timer_stop(loop, w); + w->cb(loop, w, SS_TIMER); +} +void ss_timer_init(ss_timer *w, void (*cb)(struct ss_loop *, ss_timer *, int), double after, double repeat) +{ + *w = (ss_timer){.after = after, .repeat = repeat, .cb = cb}; +} +void ss_timer_set(ss_timer *w, double after, double repeat) +{ + w->after = after; w->repeat = repeat; +} +void ss_timer_start(struct ss_loop *loop, ss_timer *w) +{ + if (w->backend) return; + struct timer_state *state = allocate(sizeof(*state)); + state->loop = loop; state->watcher = w; + checked(uv_timer_init(&loop->native, &state->handle)); + w->backend = state; + uv_update_time(&loop->native); + checked(uv_timer_start(&state->handle, timed, milliseconds(w->after), milliseconds(w->repeat))); +} +void ss_timer_stop(struct ss_loop *loop, ss_timer *w) +{ + (void)loop; + struct timer_state *state = w->backend; + if (!state) return; + w->backend = NULL; + checked(uv_timer_stop(&state->handle)); + uv_close((uv_handle_t *)&state->handle, closed); +} +void ss_timer_again(struct ss_loop *loop, ss_timer *w) +{ + ss_timer_stop(loop, w); + if (w->repeat > 0) { + w->after = w->repeat; + ss_timer_start(loop, w); + } +} +static void signaled(uv_signal_t *handle, int signum) +{ + (void)signum; + struct signal_state *state = (struct signal_state *)handle; + ss_signal *w = state->watcher; + w->cb(state->loop, w, SS_SIGNAL); +} +void ss_signal_init(ss_signal *w, void (*cb)(struct ss_loop *, ss_signal *, int), int signum) +{ + *w = (ss_signal){.signum = signum, .cb = cb}; +} +void ss_signal_start(struct ss_loop *loop, ss_signal *w) +{ + if (w->backend) return; +#ifdef _WIN32 + /* Windows has no SIGTERM delivery; process termination is an OS operation. */ + if (w->signum == SIGTERM) return; +#endif + struct signal_state *state = allocate(sizeof(*state)); + state->loop = loop; state->watcher = w; + checked(uv_signal_init(&loop->native, &state->handle)); + checked(uv_signal_start(&state->handle, signaled, w->signum)); + w->backend = state; +} +void ss_signal_stop(struct ss_loop *loop, ss_signal *w) +{ + (void)loop; + struct signal_state *state = w->backend; + if (!state) return; + w->backend = NULL; + checked(uv_signal_stop(&state->handle)); + uv_close((uv_handle_t *)&state->handle, closed); +} diff --git a/src/ss_event.h b/src/ss_event.h new file mode 100644 index 000000000..a92bd1036 --- /dev/null +++ b/src/ss_event.h @@ -0,0 +1,58 @@ +/* Project event API backed by bundled/system libuv. Watchers may be freed + * immediately after stop; libuv handle storage has a separate lifetime. */ +#ifndef SS_EVENT_H +#define SS_EVENT_H +#include +/* Keep platform headers private: Linux TCP headers differ from libc headers. */ +struct uv_loop_s; +struct ss_loop; +typedef double ss_tstamp; +#define SS_READ 1 +#define SS_WRITE 2 +#define SS_TIMER 4 +#define SS_SIGNAL 8 +#define SS_ERROR 16 +#define SS_RUN_NOWAIT 1 +#define SS_UNLOOP_ALL 2 +#define SS_P_ struct ss_loop *loop, +#define SS_A_ loop, +#define SS_DEFAULT ss_default_loop() +typedef struct ss_io { + void *backend; + struct ss_io *next; + uint64_t generation; + int fd, events; + void (*cb)(struct ss_loop *, struct ss_io *, int); +} ss_io; +typedef struct ss_timer { + void *backend; + double after, repeat; + void (*cb)(struct ss_loop *, struct ss_timer *, int); +} ss_timer; +typedef struct ss_signal { + void *backend; + int signum; + void (*cb)(struct ss_loop *, struct ss_signal *, int); +} ss_signal; +struct ss_loop *ss_default_loop(void); +struct ss_loop *ss_loop_new(unsigned flags); +void ss_loop_destroy(struct ss_loop *loop); +struct uv_loop_s *ss_native_loop(struct ss_loop *loop); +const char *ss_backend_name(void); +int ss_run(struct ss_loop *loop, int flags); +void ss_unloop(struct ss_loop *loop, int how); +double ss_now(struct ss_loop *loop); +double ss_time(void); +void ss_io_init(ss_io *, void (*)(struct ss_loop *, ss_io *, int), int, int); +void ss_io_set(ss_io *, int, int); +void ss_io_start(struct ss_loop *, ss_io *); +void ss_io_stop(struct ss_loop *, ss_io *); +void ss_timer_init(ss_timer *, void (*)(struct ss_loop *, ss_timer *, int), double, double); +void ss_timer_set(ss_timer *, double, double); +void ss_timer_start(struct ss_loop *, ss_timer *); +void ss_timer_stop(struct ss_loop *, ss_timer *); +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 *); +#endif diff --git a/src/tunnel.c b/src/tunnel.c index bd5470bc5..60d917581 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -63,19 +63,19 @@ #define EWOULDBLOCK EAGAIN #endif -static void accept_cb(EV_P_ ev_io *w, int revents); -static void server_recv_cb(EV_P_ ev_io *w, int revents); -static void server_send_cb(EV_P_ ev_io *w, int revents); -static void remote_recv_cb(EV_P_ ev_io *w, int revents); -static void remote_send_cb(EV_P_ ev_io *w, int revents); +static void accept_cb(SS_P_ ss_io *w, int revents); +static void server_recv_cb(SS_P_ ss_io *w, int revents); +static void server_send_cb(SS_P_ ss_io *w, int revents); +static void remote_recv_cb(SS_P_ ss_io *w, int revents); +static void remote_send_cb(SS_P_ ss_io *w, int revents); static remote_t *new_remote(int fd, int timeout); static server_t *new_server(int fd); static void free_remote(remote_t *remote); -static void close_and_free_remote(EV_P_ remote_t *remote); +static void close_and_free_remote(SS_P_ remote_t *remote); static void free_server(server_t *server); -static void close_and_free_server(EV_P_ server_t *server); +static void close_and_free_server(SS_P_ server_t *server); static int validate_tunnel_addr(const ss_addr_t *addr) @@ -121,13 +121,13 @@ static int no_delay = 0; int fast_open = 0; static int ret_val = 0; -static struct ev_signal sigint_watcher; -static struct ev_signal sigterm_watcher; +static struct ss_signal sigint_watcher; +static struct ss_signal sigterm_watcher; #ifndef __MINGW32__ -static struct ev_signal sigchld_watcher; +static struct ss_signal sigchld_watcher; #else static struct plugin_watcher_t { - ev_io io; + ss_io io; SOCKET fd; uint16_t port; int valid; @@ -215,14 +215,14 @@ create_and_bind(const char *addr, const char *port) } static void -server_recv_cb(EV_P_ ev_io *w, int revents) +server_recv_cb(SS_P_ ss_io *w, int revents) { server_ctx_t *server_recv_ctx = (server_ctx_t *)w; server_t *server = server_recv_ctx->server; remote_t *remote = server->remote; if (remote == NULL) { - close_and_free_server(EV_A_ server); + close_and_free_server(SS_A_ server); return; } @@ -230,8 +230,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (r == 0) { // connection closed - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else if (r == -1) { if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { @@ -240,8 +240,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents) return; } else { ERROR("server recv"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -252,8 +252,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (err) { LOGE("invalid password or cipher"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } @@ -263,34 +263,34 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { // no data, wait for send remote->buf->idx = 0; - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); return; } else { ERROR("send"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } else if (s < remote->buf->len) { remote->buf->len -= s; remote->buf->idx = s; - ev_io_stop(EV_A_ & server_recv_ctx->io); - ev_io_start(EV_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & server_recv_ctx->io); + ss_io_start(SS_A_ & remote->send_ctx->io); return; } } static void -server_send_cb(EV_P_ ev_io *w, int revents) +server_send_cb(SS_P_ ss_io *w, int revents) { server_ctx_t *server_send_ctx = (server_ctx_t *)w; server_t *server = server_send_ctx->server; remote_t *remote = server->remote; if (server->buf->len == 0) { // close and free - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else { // has data to send @@ -299,8 +299,8 @@ server_send_cb(EV_P_ ev_io *w, int revents) if (s == -1) { if (ss_socket_error() != EAGAIN && ss_socket_error() != EWOULDBLOCK) { ERROR("send"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } return; } else if (s < server->buf->len) { @@ -312,12 +312,12 @@ server_send_cb(EV_P_ ev_io *w, int revents) // all sent out, wait for reading server->buf->len = 0; server->buf->idx = 0; - ev_io_stop(EV_A_ & server_send_ctx->io); + ss_io_stop(SS_A_ & server_send_ctx->io); if (remote != NULL) { - ev_io_start(EV_A_ & remote->recv_ctx->io); + ss_io_start(SS_A_ & remote->recv_ctx->io); } else { - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -325,7 +325,7 @@ server_send_cb(EV_P_ ev_io *w, int revents) } static void -remote_timeout_cb(EV_P_ ev_timer *watcher, int revents) +remote_timeout_cb(SS_P_ ss_timer *watcher, int revents) { remote_ctx_t *remote_ctx = ss_container_of(watcher, remote_ctx_t, watcher); @@ -337,14 +337,14 @@ remote_timeout_cb(EV_P_ ev_timer *watcher, int revents) LOGI("TCP connection timeout"); } - ev_timer_stop(EV_A_ watcher); + ss_timer_stop(SS_A_ watcher); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } static void -remote_recv_cb(EV_P_ ev_io *w, int revents) +remote_recv_cb(SS_P_ ss_io *w, int revents) { remote_ctx_t *remote_recv_ctx = (remote_ctx_t *)w; remote_t *remote = remote_recv_ctx->remote; @@ -354,8 +354,8 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) if (r == 0) { // connection closed - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else if (r == -1) { if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { @@ -364,8 +364,8 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) return; } else { ERROR("remote recv"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } @@ -375,8 +375,8 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) int err = crypto->decrypt(server->buf, server->d_ctx, SOCKET_BUF_SIZE); if (err == CRYPTO_ERROR) { LOGE("invalid password or cipher"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else if (err == CRYPTO_NEED_MORE) { return; // Wait for more @@ -388,19 +388,19 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) if (ss_socket_error() == EAGAIN || ss_socket_error() == EWOULDBLOCK) { // no data, wait for send server->buf->idx = 0; - ev_io_stop(EV_A_ & remote_recv_ctx->io); - ev_io_start(EV_A_ & server->send_ctx->io); + ss_io_stop(SS_A_ & remote_recv_ctx->io); + ss_io_start(SS_A_ & server->send_ctx->io); } else { ERROR("send"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } else if (s < server->buf->len) { server->buf->len -= s; server->buf->idx = s; - ev_io_stop(EV_A_ & remote_recv_ctx->io); - ev_io_start(EV_A_ & server->send_ctx->io); + ss_io_stop(SS_A_ & remote_recv_ctx->io); + ss_io_start(SS_A_ & server->send_ctx->io); } // Disable TCP_NODELAY after the first response are sent @@ -413,13 +413,13 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) } static void -remote_send_cb(EV_P_ ev_io *w, int revents) +remote_send_cb(SS_P_ ss_io *w, int revents) { remote_ctx_t *remote_send_ctx = (remote_ctx_t *)w; remote_t *remote = remote_send_ctx->remote; server_t *server = remote->server; - ev_timer_stop(EV_A_ & remote_send_ctx->watcher); + ss_timer_stop(SS_A_ & remote_send_ctx->watcher); if (!remote_send_ctx->connected) { int r = 0; @@ -435,8 +435,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (remote->buf->len != 0) { LOGE("unexpected pending tunnel data"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } buffer_t *abuf = remote->buf; @@ -477,8 +477,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (host_len > UINT8_MAX) { LOGE("tunnel host name is too long"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } abuf->data[abuf->len++] = 3; @@ -490,8 +490,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) uint16_t port_num; if (ss_parse_uint16_port(sa->port, &port_num) == -1) { LOGE("invalid tunnel port"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } uint16_t port = htons(port_num); @@ -502,25 +502,25 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (err) { LOGE("invalid password or cipher"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } - ev_io_start(EV_A_ & remote->recv_ctx->io); + ss_io_start(SS_A_ & remote->recv_ctx->io); } else { ERROR("getpeername"); // not connected - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } if (remote->buf->len == 0) { // close and free - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } else { // has data to send @@ -599,8 +599,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (s == -1) { if (ss_socket_error() == CONNECT_IN_PROGRESS) { - ev_io_start(EV_A_ & remote_send_ctx->io); - ev_timer_start(EV_A_ & remote_send_ctx->watcher); + ss_io_start(SS_A_ & remote_send_ctx->io); + ss_timer_start(SS_A_ & remote_send_ctx->watcher); } else { fast_open = 0; if (ss_socket_error() == EOPNOTSUPP || ss_socket_error() == EPROTONOSUPPORT || @@ -609,8 +609,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) } else { ERROR("fast_open_connect"); } - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } return; } @@ -623,8 +623,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) if (ss_socket_error() != EAGAIN && ss_socket_error() != EWOULDBLOCK) { ERROR("send"); // close and free - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); } return; } else if (s < remote->buf->len) { @@ -636,8 +636,8 @@ remote_send_cb(EV_P_ ev_io *w, int revents) // all sent out, wait for reading remote->buf->len = 0; remote->buf->idx = 0; - ev_io_stop(EV_A_ & remote_send_ctx->io); - ev_io_start(EV_A_ & server->recv_ctx->io); + ss_io_stop(SS_A_ & remote_send_ctx->io); + ss_io_start(SS_A_ & server->recv_ctx->io); } } } @@ -660,9 +660,9 @@ new_remote(int fd, int timeout) remote->send_ctx->remote = remote; remote->send_ctx->connected = 0; - ev_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, EV_READ); - ev_io_init(&remote->send_ctx->io, remote_send_cb, fd, EV_WRITE); - ev_timer_init(&remote->send_ctx->watcher, remote_timeout_cb, + ss_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, SS_READ); + ss_io_init(&remote->send_ctx->io, remote_send_cb, fd, SS_WRITE); + ss_timer_init(&remote->send_ctx->watcher, remote_timeout_cb, min(MAX_CONNECT_TIMEOUT, timeout), 0); return remote; @@ -684,12 +684,12 @@ free_remote(remote_t *remote) } static void -close_and_free_remote(EV_P_ remote_t *remote) +close_and_free_remote(SS_P_ remote_t *remote) { if (remote != NULL) { - ev_timer_stop(EV_A_ & remote->send_ctx->watcher); - ev_io_stop(EV_A_ & remote->send_ctx->io); - ev_io_stop(EV_A_ & remote->recv_ctx->io); + ss_timer_stop(SS_A_ & remote->send_ctx->watcher); + ss_io_stop(SS_A_ & remote->send_ctx->io); + ss_io_stop(SS_A_ & remote->recv_ctx->io); ss_socket_close(remote->fd); free_remote(remote); } @@ -719,8 +719,8 @@ new_server(int fd) crypto->ctx_init(crypto->cipher, server->d_ctx, 0); cipher_ctx_pair(server->e_ctx, server->d_ctx); - ev_io_init(&server->recv_ctx->io, server_recv_cb, fd, EV_READ); - ev_io_init(&server->send_ctx->io, server_send_cb, fd, EV_WRITE); + ss_io_init(&server->recv_ctx->io, server_recv_cb, fd, SS_READ); + ss_io_init(&server->send_ctx->io, server_send_cb, fd, SS_WRITE); return server; } @@ -750,18 +750,18 @@ free_server(server_t *server) } static void -close_and_free_server(EV_P_ server_t *server) +close_and_free_server(SS_P_ server_t *server) { if (server != NULL) { - ev_io_stop(EV_A_ & server->send_ctx->io); - ev_io_stop(EV_A_ & server->recv_ctx->io); + ss_io_stop(SS_A_ & server->send_ctx->io); + ss_io_stop(SS_A_ & server->recv_ctx->io); ss_socket_close(server->fd); free_server(server); } } static void -accept_cb(EV_P_ ev_io *w, int revents) +accept_cb(SS_P_ ss_io *w, int revents) { struct listen_ctx *listener = (struct listen_ctx *)w; int serverfd = ss_accept(listener->fd, NULL, NULL); @@ -864,21 +864,21 @@ accept_cb(EV_P_ ev_io *w, int revents) if (r == -1 && ss_socket_error() != CONNECT_IN_PROGRESS) { ERROR("connect"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); + close_and_free_remote(SS_A_ remote); + close_and_free_server(SS_A_ server); return; } } // listen to remote connected event - ev_io_start(EV_A_ & remote->send_ctx->io); - ev_timer_start(EV_A_ & remote->send_ctx->watcher); + ss_io_start(SS_A_ & remote->send_ctx->io); + ss_timer_start(SS_A_ & remote->send_ctx->watcher); } static void -signal_cb(EV_P_ ev_signal *w, int revents) +signal_cb(SS_P_ ss_signal *w, int revents) { - if (revents & EV_SIGNAL) { + if (revents & SS_SIGNAL) { switch (w->signum) { #ifndef __MINGW32__ case SIGCHLD: @@ -890,21 +890,21 @@ signal_cb(EV_P_ ev_signal *w, int revents) #endif case SIGINT: case SIGTERM: - ev_signal_stop(EV_DEFAULT, &sigint_watcher); - ev_signal_stop(EV_DEFAULT, &sigterm_watcher); + ss_signal_stop(SS_DEFAULT, &sigint_watcher); + ss_signal_stop(SS_DEFAULT, &sigterm_watcher); #ifndef __MINGW32__ - ev_signal_stop(EV_DEFAULT, &sigchld_watcher); + ss_signal_stop(SS_DEFAULT, &sigchld_watcher); #else - ev_io_stop(EV_DEFAULT, &plugin_watcher.io); + ss_io_stop(SS_DEFAULT, &plugin_watcher.io); #endif - ev_unloop(EV_A_ EVUNLOOP_ALL); + ss_unloop(SS_A_ SS_UNLOOP_ALL); } } } #ifdef __MINGW32__ static void -plugin_watcher_cb(EV_P_ ev_io *w, int revents) +plugin_watcher_cb(SS_P_ ss_io *w, int revents) { char buf[1]; SOCKET fd = ss_accept(plugin_watcher.fd, NULL, NULL); @@ -915,10 +915,10 @@ plugin_watcher_cb(EV_P_ ev_io *w, int revents) closesocket(fd); LOGE("plugin service exit unexpectedly"); ret_val = -1; - ev_signal_stop(EV_DEFAULT, &sigint_watcher); - ev_signal_stop(EV_DEFAULT, &sigterm_watcher); - ev_io_stop(EV_DEFAULT, &plugin_watcher.io); - ev_unloop(EV_A_ EVUNLOOP_ALL); + ss_signal_stop(SS_DEFAULT, &sigint_watcher); + ss_signal_stop(SS_DEFAULT, &sigterm_watcher); + ss_io_stop(SS_DEFAULT, &plugin_watcher.io); + ss_unloop(SS_A_ SS_UNLOOP_ALL); } #endif @@ -1352,8 +1352,8 @@ main(int argc, char **argv) break; } plugin_watcher.fd = fd; - ev_io_init(&plugin_watcher.io, plugin_watcher_cb, fd, EV_READ); - ev_io_start(EV_DEFAULT, &plugin_watcher.io); + ss_io_init(&plugin_watcher.io, plugin_watcher_cb, fd, SS_READ); + ss_io_start(SS_DEFAULT, &plugin_watcher.io); plugin_watcher.valid = 1; } while (0); if (!plugin_watcher.valid) { @@ -1393,13 +1393,13 @@ main(int argc, char **argv) signal(SIGABRT, SIG_IGN); #endif - ev_signal_init(&sigint_watcher, signal_cb, SIGINT); - ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); - ev_signal_start(EV_DEFAULT, &sigint_watcher); - ev_signal_start(EV_DEFAULT, &sigterm_watcher); + ss_signal_init(&sigint_watcher, signal_cb, SIGINT); + ss_signal_init(&sigterm_watcher, signal_cb, SIGTERM); + ss_signal_start(SS_DEFAULT, &sigint_watcher); + ss_signal_start(SS_DEFAULT, &sigterm_watcher); #ifndef __MINGW32__ - ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); - ev_signal_start(EV_DEFAULT, &sigchld_watcher); + ss_signal_init(&sigchld_watcher, signal_cb, SIGCHLD); + ss_signal_start(SS_DEFAULT, &sigchld_watcher); #endif // Setup keys @@ -1438,7 +1438,7 @@ main(int argc, char **argv) LOGI("listening at %s:%s", local_addr, local_port); - struct ev_loop *loop = EV_DEFAULT; + struct ss_loop *loop = SS_DEFAULT; if (mode != UDP_ONLY) { // Setup socket @@ -1454,8 +1454,8 @@ main(int argc, char **argv) listen_ctx.fd = listenfd; - ev_io_init(&listen_ctx.io, accept_cb, listenfd, EV_READ); - ev_io_start(loop, &listen_ctx.io); + ss_io_init(&listen_ctx.io, accept_cb, listenfd, SS_READ); + ss_io_start(loop, &listen_ctx.io); } // Setup UDP @@ -1490,7 +1490,7 @@ main(int argc, char **argv) } #endif - ev_run(loop, 0); + ss_run(loop, 0); if (plugin != NULL) { stop_plugin(); diff --git a/src/tunnel.h b/src/tunnel.h index 871e8b6b0..76e0b3942 100644 --- a/src/tunnel.h +++ b/src/tunnel.h @@ -23,11 +23,7 @@ #ifndef _TUNNEL_H #define _TUNNEL_H -#ifdef HAVE_LIBEV_EV_H -#include -#else -#include -#endif +#include "ss_event.h" #ifdef __MINGW32__ #include "ss_windows.h" @@ -39,7 +35,7 @@ #include "common.h" typedef struct listen_ctx { - ev_io io; + ss_io io; ss_addr_t tunnel_addr; char *iface; int remote_num; @@ -50,7 +46,7 @@ typedef struct listen_ctx { } listen_ctx_t; typedef struct server_ctx { - ev_io io; + ss_io io; int connected; struct server *server; } server_ctx_t; @@ -68,8 +64,8 @@ typedef struct server { } server_t; typedef struct remote_ctx { - ev_io io; - ev_timer watcher; + ss_io io; + ss_timer watcher; int connected; struct remote *remote; } remote_ctx_t; diff --git a/src/udprelay.c b/src/udprelay.c index 252633336..624173910 100644 --- a/src/udprelay.c +++ b/src/udprelay.c @@ -74,16 +74,16 @@ #define EWOULDBLOCK EAGAIN #endif -static void server_recv_cb(EV_P_ ev_io *w, int revents); -static void remote_recv_cb(EV_P_ ev_io *w, int revents); -static void remote_timeout_cb(EV_P_ ev_timer *watcher, int revents); +static void server_recv_cb(SS_P_ ss_io *w, int revents); +static void remote_recv_cb(SS_P_ ss_io *w, int revents); +static void remote_timeout_cb(SS_P_ ss_timer *watcher, int revents); static char *hash_key(const int af, const struct sockaddr_storage *addr); #ifdef MODULE_REMOTE static void resolv_free_cb(void *data); static void resolv_cb(struct sockaddr *addr, void *data); #endif -static void close_and_free_remote(EV_P_ remote_ctx_t *ctx); +static void close_and_free_remote(SS_P_ remote_ctx_t *ctx); static remote_ctx_t *new_remote(int fd, server_ctx_t *server_ctx); #ifdef __ANDROID__ @@ -587,8 +587,8 @@ new_remote(int fd, server_ctx_t *server_ctx) ctx->server_ctx = server_ctx; ctx->af = AF_UNSPEC; - ev_io_init(&ctx->io, remote_recv_cb, fd, EV_READ); - ev_timer_init(&ctx->watcher, remote_timeout_cb, server_ctx->timeout, + ss_io_init(&ctx->io, remote_recv_cb, fd, SS_READ); + ss_timer_init(&ctx->watcher, remote_timeout_cb, server_ctx->timeout, server_ctx->timeout); return ctx; @@ -602,7 +602,7 @@ new_server_ctx(int fd) ctx->fd = fd; - ev_io_init(&ctx->io, server_recv_cb, fd, EV_READ); + ss_io_init(&ctx->io, server_recv_cb, fd, SS_READ); return ctx; } @@ -621,7 +621,7 @@ new_query_ctx(char *buf, size_t len) } void -close_and_free_query(EV_P_ struct query_ctx *ctx) +close_and_free_query(SS_P_ struct query_ctx *ctx) { if (ctx != NULL) { if (ctx->buf != NULL) { @@ -635,11 +635,11 @@ close_and_free_query(EV_P_ struct query_ctx *ctx) #endif void -close_and_free_remote(EV_P_ remote_ctx_t *ctx) +close_and_free_remote(SS_P_ remote_ctx_t *ctx) { if (ctx != NULL) { - ev_timer_stop(EV_A_ & ctx->watcher); - ev_io_stop(EV_A_ & ctx->io); + ss_timer_stop(SS_A_ & ctx->watcher); + ss_io_stop(SS_A_ & ctx->io); ss_socket_close(ctx->fd); if (ctx->udp_session != NULL && ctx->server_ctx != NULL @@ -651,7 +651,7 @@ close_and_free_remote(EV_P_ remote_ctx_t *ctx) } static void -remote_timeout_cb(EV_P_ ev_timer *watcher, int revents) +remote_timeout_cb(SS_P_ ss_timer *watcher, int revents) { remote_ctx_t *remote_ctx = ss_container_of(watcher, remote_ctx_t, watcher); @@ -680,7 +680,7 @@ static void resolv_cb(struct sockaddr *addr, void *data) { struct query_ctx *query_ctx = (struct query_ctx *)data; - struct ev_loop *loop = query_ctx->server_ctx->loop; + struct ss_loop *loop = query_ctx->server_ctx->loop; if (addr == NULL) { LOGE("[udp] unable to resolve"); @@ -747,15 +747,15 @@ resolv_cb(struct sockaddr *addr, void *data) if (s == -1) { ERROR("[udp] sendto_remote"); if (!cache_hit) { - close_and_free_remote(EV_A_ remote_ctx); + close_and_free_remote(SS_A_ remote_ctx); } } else { if (!cache_hit) { // Add to conn cache char *key = hash_key(AF_UNSPEC, &remote_ctx->src_addr); cache_insert(query_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); - ev_io_start(EV_A_ & remote_ctx->io); - ev_timer_start(EV_A_ & remote_ctx->watcher); + ss_io_start(SS_A_ & remote_ctx->io); + ss_timer_start(SS_A_ & remote_ctx->watcher); } } } @@ -788,7 +788,7 @@ void convert_ipv4_mapped_ipv6(struct sockaddr_storage* addr) { } static void -remote_recv_cb(EV_P_ ev_io *w, int revents) +remote_recv_cb(SS_P_ ss_io *w, int revents) { ssize_t r; remote_ctx_t *remote_ctx = (remote_ctx_t *)w; @@ -797,7 +797,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) // server has been closed if (server_ctx == NULL) { LOGE("[udp] invalid server"); - close_and_free_remote(EV_A_ remote_ctx); + close_and_free_remote(SS_A_ remote_ctx); return; } @@ -984,7 +984,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) // handle the UDP packet successfully, // triger the timer - ev_timer_again(EV_A_ & remote_ctx->watcher); + ss_timer_again(SS_A_ & remote_ctx->watcher); CLEAN_UP: @@ -993,7 +993,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) } static void -server_recv_cb(EV_P_ ev_io *w, int revents) +server_recv_cb(SS_P_ ss_io *w, int revents) { server_ctx_t *server_ctx = (server_ctx_t *)w; struct sockaddr_storage src_addr; @@ -1250,7 +1250,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) // reset the timer if (remote_ctx != NULL) { - ev_timer_again(EV_A_ & remote_ctx->watcher); + ss_timer_again(SS_A_ & remote_ctx->watcher); } if (remote_ctx == NULL) { @@ -1345,8 +1345,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents) cache_insert(conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); // Start remote io - ev_io_start(EV_A_ & remote_ctx->io); - ev_timer_start(EV_A_ & remote_ctx->watcher); + ss_io_start(SS_A_ & remote_ctx->io); + ss_timer_start(SS_A_ & remote_ctx->watcher); } if (offset > 0) { @@ -1451,7 +1451,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (s == -1) { ERROR("[udp] sendto_remote"); if (!cache_hit) { - close_and_free_remote(EV_A_ remote_ctx); + close_and_free_remote(SS_A_ remote_ctx); } } else { if (!cache_hit) { @@ -1460,8 +1460,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents) char *key = hash_key(remote_ctx->af, &remote_ctx->src_addr); cache_insert(server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); - ev_io_start(EV_A_ & remote_ctx->io); - ev_timer_start(EV_A_ & remote_ctx->watcher); + ss_io_start(SS_A_ & remote_ctx->io); + ss_timer_start(SS_A_ & remote_ctx->watcher); } } } else { @@ -1500,7 +1500,7 @@ free_cb(void *key, void *element) LOGI("[udp] one connection freed"); } - close_and_free_remote(EV_DEFAULT, remote_ctx); + close_and_free_remote(SS_DEFAULT, remote_ctx); } int @@ -1515,7 +1515,7 @@ init_udprelay(const char *server_host, const char *server_port, { s_port = server_port; // Initialize ev loop - struct ev_loop *loop = EV_DEFAULT; + struct ss_loop *loop = SS_DEFAULT; // Initialize MTU if (mtu > 0) { @@ -1556,7 +1556,7 @@ init_udprelay(const char *server_host, const char *server_port, #endif #endif - ev_io_start(loop, &server_ctx->io); + ss_io_start(loop, &server_ctx->io); server_ctx_list[server_num++] = server_ctx; @@ -1566,10 +1566,10 @@ init_udprelay(const char *server_host, const char *server_port, void free_udprelay() { - struct ev_loop *loop = EV_DEFAULT; + struct ss_loop *loop = SS_DEFAULT; while (server_num > 0) { server_ctx_t *server_ctx = server_ctx_list[--server_num]; - ev_io_stop(loop, &server_ctx->io); + ss_io_stop(loop, &server_ctx->io); ss_socket_close(server_ctx->fd); cache_delete(server_ctx->conn_cache, 0); #ifdef MODULE_LOCAL diff --git a/src/udprelay.h b/src/udprelay.h index 0048a6319..e020ec996 100644 --- a/src/udprelay.h +++ b/src/udprelay.h @@ -25,11 +25,7 @@ #include -#ifdef HAVE_LIBEV_EV_H -#include -#else -#include -#endif +#include "ss_event.h" #include "crypto.h" #include "jconf.h" @@ -49,7 +45,7 @@ #define MAX_ADDR_HEADER_SIZE (1 + 256 + 2) // 1-byte atyp + 256-byte hostname + 2-byte port typedef struct server_ctx { - ev_io io; + ss_io io; int fd; crypto_t *crypto; int timeout; @@ -63,7 +59,7 @@ typedef struct server_ctx { #endif #endif #ifdef MODULE_REMOTE - struct ev_loop *loop; + struct ss_loop *loop; #endif } server_ctx_t; @@ -79,8 +75,8 @@ typedef struct query_ctx { #endif typedef struct remote_ctx { - ev_io io; - ev_timer watcher; + ss_io io; + ss_timer watcher; int af; int fd; struct sockaddr_storage src_addr; diff --git a/src/utils.c b/src/utils.c index 75acd8629..5d22ff619 100644 --- a/src/utils.c +++ b/src/utils.c @@ -321,7 +321,7 @@ void usage() { printf("\n"); - printf("shadowsocks-libev %s\n\n", VERSION); + printf("shadowsocks-c %s\n\n", VERSION); printf( " maintained by Max Lv and Linus Yang \n\n"); printf(" usage:\n\n"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 80fbf2eab..64d4423ed 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -199,3 +199,10 @@ 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_LITERAL_LIBS}") + +ss_add_test(test_event "test_event.c" "ss_ev") +set_tests_properties(test_event PROPERTIES TIMEOUT 30) +ss_add_test(test_resolv_async + "test_resolv_async.c;${PROJECT_SOURCE_DIR}/src/resolv.c;${PROJECT_SOURCE_DIR}/src/utils.c" + "ss_ev;ss_cares;ss_sodium") +set_tests_properties(test_resolv_async PROPERTIES TIMEOUT 30) diff --git a/tests/async_dns_interop.py b/tests/async_dns_interop.py new file mode 100644 index 000000000..ebce559b8 --- /dev/null +++ b/tests/async_dns_interop.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +"""Exercise client hostname ACL bypass with an unreachable proxy upstream.""" +import argparse +import concurrent.futures +import os +from pathlib import Path +import tempfile +import threading +from interop import TCPOrigin, TCPHandler, free_port, peers, tcp_case + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--bin', required=True, type=Path) + parser.add_argument('--isolate-windows-runtime', action='store_true') + args = parser.parse_args() + environment = None + if args.isolate_windows_runtime: + environment = os.environ.copy() + root = os.environ['SYSTEMROOT'] + environment['PATH'] = root + '\\System32;' + root + local = args.bin.resolve() / ('ss-local.exe' if os.name == 'nt' else 'ss-local') + with tempfile.TemporaryDirectory() as temp, TCPOrigin(('127.0.0.1', 0), TCPHandler) as origin: + threading.Thread(target=origin.serve_forever, daemon=True).start() + try: + for policy in ('[bypass_all]\n', '[proxy_all]\n[bypass_list]\nfull:localhost\n'): + acl = Path(temp) / 'acl' + acl.write_text(policy) + port, upstream = free_port(), free_port() + command = [str(local), '-s', '127.0.0.1', '-p', str(upstream), '-l', str(port), + '-k', 'dns-integration', '-m', 'aes-128-gcm', '--acl', str(acl)] + with peers([command], [port], environment): + with concurrent.futures.ThreadPoolExecutor(max_workers=3) as pool: + futures = [pool.submit(tcp_case, port, origin.server_address[1], size) + for size in (1, 65536, 1048576)] + for future in futures: future.result(timeout=15) + print('PASS hostname ACL bypass: ' + policy.splitlines()[0], flush=True) + finally: + origin.shutdown() + + +if __name__ == '__main__': + main() diff --git a/tests/check_static_runtime.py b/tests/check_static_runtime.py index 3ff4d93b3..f47af3a00 100644 --- a/tests/check_static_runtime.py +++ b/tests/check_static_runtime.py @@ -8,7 +8,7 @@ WINDOWS_SYSTEM_DLLS = { 'advapi32.dll', 'bcrypt.dll', 'crypt32.dll', 'dnsapi.dll', 'iphlpapi.dll', - 'kernel32.dll', 'msvcrt.dll', 'ntdll.dll', 'ole32.dll', 'psapi.dll', + 'dbghelp.dll', 'kernel32.dll', 'msvcrt.dll', 'ntdll.dll', 'ole32.dll', 'psapi.dll', 'rpcrt4.dll', 'secur32.dll', 'shell32.dll', 'ucrtbase.dll', 'user32.dll', 'userenv.dll', 'winmm.dll', 'ws2_32.dll', } diff --git a/tests/consumer/CMakeLists.txt b/tests/consumer/CMakeLists.txt index 6f635b879..0bbe775f1 100644 --- a/tests/consumer/CMakeLists.txt +++ b/tests/consumer/CMakeLists.txt @@ -1,8 +1,21 @@ cmake_minimum_required(VERSION 3.20) project(shadowsocks_consumer LANGUAGES C) -find_package(shadowsocks-libev CONFIG REQUIRED) +set(SS_PACKAGE_NAME shadowsocks-c CACHE STRING "Installed package to test") +find_package(${SS_PACKAGE_NAME} CONFIG REQUIRED) foreach(kind shared static) if(TARGET shadowsocks::${kind}) + if(SS_PACKAGE_NAME STREQUAL "shadowsocks-libev") + # Exercise legacy filenames as well as legacy package discovery. + foreach(property IMPORTED_LOCATION IMPORTED_IMPLIB) + get_target_property(location shadowsocks::${kind} ${property}) + if(location) + get_filename_component(directory "${location}" DIRECTORY) + get_filename_component(filename "${location}" NAME) + string(REPLACE "shadowsocks-c" "shadowsocks-libev" filename "${filename}") + set_property(TARGET shadowsocks::${kind} PROPERTY ${property} "${directory}/${filename}") + endif() + endforeach() + endif() add_executable(consumer_${kind} main.c) target_link_libraries(consumer_${kind} PRIVATE shadowsocks::${kind}) endif() diff --git a/tests/test_deb_build.sh b/tests/test_deb_build.sh index 7dbca66dc..92294ed64 100644 --- a/tests/test_deb_build.sh +++ b/tests/test_deb_build.sh @@ -172,10 +172,10 @@ done # Shared library should be findable by ldconfig sudo ldconfig -if ldconfig -p | grep -q libshadowsocks-libev; then - check_pass "libshadowsocks-libev found by ldconfig" +if ldconfig -p | grep -q libshadowsocks-c; then + check_pass "libshadowsocks-c found by ldconfig" else - check_fail "libshadowsocks-libev not found by ldconfig" + check_fail "libshadowsocks-c not found by ldconfig" fi # Header should be in the include path diff --git a/tests/test_event.c b/tests/test_event.c new file mode 100644 index 000000000..ab250dd71 --- /dev/null +++ b/tests/test_event.c @@ -0,0 +1,84 @@ +#include "test_helpers.h" +#include +#include "ss_event.h" +#include +#ifdef __APPLE__ +#include +#endif +int verbose = 0; +struct receiver { ss_io io; int count; }; +static void receive(struct ss_loop *loop, ss_io *io, int events) +{ + struct receiver *r = (struct receiver *)io; + char byte; + assert(events & SS_READ); + assert(recv(io->fd, &byte, 1, 0) == 1 && byte == 'x'); + r->count++; + ss_io_stop(loop, io); +} +struct shared { ss_io io; ss_io *other; int *calls; }; +static void destroy_peer(struct ss_loop *loop, ss_io *io, int events) +{ + (void)events; + struct shared *s = (struct shared *)io; + (*s->calls)++; + ss_io_stop(loop, s->other); + free(s->other); + ss_io_stop(loop, io); + free(s); +} +static void must_not_run(struct ss_loop *loop, ss_io *io, int events) +{ + (void)loop; (void)io; (void)events; + assert(!"stopped watcher was invoked"); +} +int main(void) +{ + test_network_init(); + struct ss_loop *loop = ss_loop_new(0); + assert(loop); +#ifdef _WIN32 + assert(strcmp(ss_backend_name(), "iocp") == 0); + assert(ss_native_loop(loop)->iocp != NULL); +#elif defined(__APPLE__) + assert(strcmp(ss_backend_name(), "kqueue") == 0); + struct timespec zero = {0}; + assert(kevent(uv_backend_fd(ss_native_loop(loop)), NULL, 0, NULL, 0, &zero) == 0); +#endif + /* Exceed Winsock select's default 64 descriptors. */ + struct receiver receivers[256] = {0}; + ss_socket_t sender = socket(AF_INET, SOCK_DGRAM, 0); + for (unsigned i = 0; i < 256; i++) { + int fd = (int)socket(AF_INET, SOCK_DGRAM, 0); + struct sockaddr_in address = {0}; + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + assert(bind(fd, (struct sockaddr *)&address, sizeof(address)) == 0); + socklen_t length = sizeof(address); + assert(getsockname(fd, (struct sockaddr *)&address, &length) == 0); + ss_io_init(&receivers[i].io, receive, fd, SS_READ); + ss_io_start(loop, &receivers[i].io); + assert(sendto(sender, "x", 1, 0, (struct sockaddr *)&address, length) == 1); + } + ss_run(loop, 0); + for (unsigned i = 0; i < 256; i++) { + assert(receivers[i].count == 1); + ss_socket_close(receivers[i].io.fd); + } + /* Both directions share one poll handle; freeing a pending peer is safe. */ + int calls = 0; + ss_io *other = calloc(1, sizeof(*other)); + struct shared *owner = calloc(1, sizeof(*owner)); + assert(other && owner); + ss_io_init(other, must_not_run, (int)sender, SS_WRITE); + ss_io_init(&owner->io, destroy_peer, (int)sender, SS_WRITE); + owner->other = other; owner->calls = &calls; + ss_io_start(loop, other); + ss_io_start(loop, &owner->io); + ss_run(loop, 0); + assert(calls == 1); + ss_socket_close(sender); + assert(ss_run(loop, SS_RUN_NOWAIT) == 0); + ss_loop_destroy(loop); + return 0; +} diff --git a/tests/test_netutils.c b/tests/test_netutils.c index b47000de6..763ca968f 100644 --- a/tests/test_netutils.c +++ b/tests/test_netutils.c @@ -113,6 +113,7 @@ test_get_sockaddr_rejects_invalid_ip_literal_ports(void) struct sockaddr_in *addr4 = (struct sockaddr_in *)&storage; memset(&storage, 0, sizeof(storage)); + assert(get_sockaddr("localhost", "8388", &storage, 0, 0) == -1); assert(get_sockaddr("127.0.0.1", "8388", &storage, 0, 0) == 0); assert(addr4->sin_family == AF_INET); assert(ntohs(addr4->sin_port) == 8388); diff --git a/tests/test_resolv.c b/tests/test_resolv.c index 87b2d9b29..743811e57 100644 --- a/tests/test_resolv.c +++ b/tests/test_resolv.c @@ -1,6 +1,6 @@ /* Exercise real c-ares cancellation with outstanding A and AAAA requests. */ #include "test_helpers.h" -#include +#include "ss_event.h" #include "resolv.h" int verbose = 0; @@ -32,7 +32,7 @@ int main(void) assert(getsockname(sink, (struct sockaddr *)&address, &size) == 0); char nameserver[64]; snprintf(nameserver, sizeof(nameserver), "127.0.0.1:%u", ntohs(address.sin_port)); - struct ev_loop *loop = ev_loop_new(0); + struct ss_loop *loop = ss_loop_new(0); assert(loop != NULL); for (unsigned round = 0; round < 3; round++) { struct result results[64] = {{0}}; @@ -42,15 +42,15 @@ int main(void) snprintf(hostname, sizeof(hostname), "pending-%u.invalid", i); resolv_start(hostname, htons(443), resolved, released, &results[i]); } - ev_run(loop, EVRUN_NOWAIT); + ss_run(loop, SS_RUN_NOWAIT); resolv_shutdown(loop); for (unsigned i = 0; i < 64; i++) { assert(results[i].called == 1); assert(results[i].freed == 1); } - assert(ev_run(loop, EVRUN_NOWAIT) == 0); + assert(ss_run(loop, SS_RUN_NOWAIT) == 0); } - ev_loop_destroy(loop); + ss_loop_destroy(loop); ss_socket_close(sink); return 0; } diff --git a/tests/test_resolv_async.c b/tests/test_resolv_async.c new file mode 100644 index 000000000..164af2172 --- /dev/null +++ b/tests/test_resolv_async.c @@ -0,0 +1,140 @@ +/* Local delayed DNS server proves A/AAAA/NXDOMAIN completion without blocking. */ +#include "test_helpers.h" +#include +#include "ss_event.h" +#include "resolv.h" +int verbose = 0; +struct packet { + struct packet *next; + struct sockaddr_storage peer; + socklen_t peer_len; + unsigned char bytes[512]; + size_t length; +}; +static struct packet *packets; +static ss_io dns_io; +static ss_timer reply_timer, ticker, deadline; +static unsigned ticks, completed; +static int family; +struct result { unsigned called, freed; int missing; }; +static void reply(struct ss_loop *loop, ss_timer *timer, int events) +{ + (void)loop; (void)timer; (void)events; + while (packets) { + struct packet *p = packets; + packets = p->next; + assert(sendto(dns_io.fd, (const char *)p->bytes, (int)p->length, 0, + (struct sockaddr *)&p->peer, p->peer_len) == (int)p->length); + free(p); + } +} +static void question(struct ss_loop *loop, ss_io *io, int events) +{ + (void)events; + struct packet *p = calloc(1, sizeof(*p)); + assert(p); + p->peer_len = sizeof(p->peer); + int count = (int)recvfrom(io->fd, (char *)p->bytes, sizeof(p->bytes), 0, + (struct sockaddr *)&p->peer, &p->peer_len); + assert(count > 16 && p->bytes[4] == 0 && p->bytes[5] == 1); + size_t end = 12; + while (end < (size_t)count && p->bytes[end]) end += p->bytes[end] + 1; + end += 5; /* terminating zero, QTYPE, QCLASS */ + assert(end <= (size_t)count && end + 28 <= sizeof(p->bytes)); + unsigned type = p->bytes[end - 3]; + int missing = p->bytes[13] == 'm'; + p->bytes[2] = 0x81; p->bytes[3] = missing ? 0x83 : 0x80; + p->bytes[6] = 0; p->bytes[7] = missing ? 0 : 1; + memset(p->bytes + 8, 0, 4); + p->length = end; + if (!missing) { + unsigned char answer[] = {0xc0,0x0c,0,0,0,1,0,0,0,1,0,0}; + assert(type == 1 || type == 28); + answer[3] = (unsigned char)type; + answer[11] = type == 1 ? 4 : 16; + memcpy(p->bytes + end, answer, sizeof(answer)); + p->length += sizeof(answer); + memset(p->bytes + p->length, 0, 16); + if (type == 1) { p->bytes[p->length] = 127; p->bytes[p->length+3] = 1; } + else p->bytes[p->length+15] = 1; + p->length += answer[11]; + } + p->next = packets; packets = p; + ss_timer_start(loop, &reply_timer); +} +static void tick(struct ss_loop *loop, ss_timer *timer, int events) +{ + (void)loop; (void)timer; (void)events; ticks++; +} +static void timed_out(struct ss_loop *loop, ss_timer *timer, int events) +{ + (void)loop; (void)timer; (void)events; + assert(!"asynchronous DNS did not complete"); +} +static struct ss_loop *test_loop; +static void resolved(struct sockaddr *address, void *data) +{ + struct result *r = data; + assert(!r->called && !r->freed && ticks > 2); + r->called++; + if (r->missing) assert(address == NULL); + else { + assert(address && address->sa_family == family); + if (family == AF_INET) assert(((struct sockaddr_in *)address)->sin_port == htons(443)); + else assert(((struct sockaddr_in6 *)address)->sin6_port == htons(443)); + } + if (++completed == 33) ss_unloop(test_loop, SS_UNLOOP_ALL); +} +static void released(void *data) +{ + struct result *r = data; + assert(r->called == 1 && !r->freed); + r->freed++; +} +int main(void) +{ + test_network_init(); + test_loop = ss_loop_new(0); + int fd = (int)socket(AF_INET, SOCK_DGRAM, 0); + struct sockaddr_in address = {0}; + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + assert(bind(fd, (struct sockaddr *)&address, sizeof(address)) == 0); + socklen_t length = sizeof(address); + assert(getsockname(fd, (struct sockaddr *)&address, &length) == 0); + char nameserver[64]; + snprintf(nameserver, sizeof(nameserver), "127.0.0.1:%u", ntohs(address.sin_port)); + ss_io_init(&dns_io, question, fd, SS_READ); + ss_timer_init(&reply_timer, reply, .05, 0); + ss_timer_init(&ticker, tick, .001, .001); + ss_timer_init(&deadline, timed_out, 10, 0); + for (int ipv6 = 0; ipv6 < 2; ipv6++) { + struct result results[33] = {{0}}; + results[32].missing = 1; + family = ipv6 ? AF_INET6 : AF_INET; + ticks = completed = 0; + assert(resolv_init(test_loop, nameserver, ipv6) == 0); + ss_io_start(test_loop, &dns_io); + ss_timer_start(test_loop, &ticker); + ss_timer_start(test_loop, &deadline); + for (unsigned i = 0; i < 33; i++) { + char host[64]; + snprintf(host, sizeof(host), "%s-%u.invalid", i == 32 ? "missing" : "resolved", i); + resolv_start(host, htons(443), resolved, released, &results[i]); + } + assert(completed == 0); + ss_run(test_loop, 0); + assert(completed == 33); + for (unsigned i = 0; i < 33; i++) assert(results[i].called == 1 && results[i].freed == 1); + resolv_shutdown(test_loop); + ss_io_stop(test_loop, &dns_io); + ss_timer_stop(test_loop, &ticker); + ss_timer_stop(test_loop, &deadline); + ss_timer_stop(test_loop, &reply_timer); + assert(!packets); + assert(ss_run(test_loop, SS_RUN_NOWAIT) == 0); + } + ss_socket_close(fd); + ss_loop_destroy(test_loop); + return 0; +} diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 9c2413dd0..68178963e 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -63,20 +63,20 @@ set(PCRE2_INCLUDE_DIRS "${CMAKE_CURRENT_BINARY_DIR}/pcre2" PARENT_SCOPE) endif() -add_library(ss_ev STATIC "${ev_SOURCE}/ev.c") -target_compile_definitions(ss_ev PRIVATE EV_STANDALONE=1) -target_include_directories(ss_ev PUBLIC "${ev_SOURCE}") -if(WIN32) - target_link_libraries(ss_ev PUBLIC ws2_32) - target_compile_options(ss_ev PRIVATE -include "${CMAKE_CURRENT_SOURCE_DIR}/libev_windows.h") -endif() +set(LIBUV_BUILD_SHARED OFF CACHE BOOL "" FORCE) +set(LIBUV_BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(LIBUV_BUILD_BENCH OFF CACHE BOOL "" FORCE) +include(LibuvWindows.cmake) +add_subdirectory("${uv_SOURCE}" uv EXCLUDE_FROM_ALL) +add_library(ss_uv ALIAS uv_a) +include(${PROJECT_SOURCE_DIR}/cmake/EventLoop.cmake) include(Sodium.cmake) -foreach(name sodium ev mbedtls) - install(FILES "${${name}_SOURCE}/LICENSE" DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-libev/${name}") +foreach(name sodium uv mbedtls) + install(FILES "${${name}_SOURCE}/LICENSE" DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-c/${name}") endforeach() -install(FILES "${cares_SOURCE}/LICENSE.md" DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-libev/cares") +install(FILES "${cares_SOURCE}/LICENSE.md" DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-c/cares") if(SS_ENABLE_REGEX) - install(FILES "${pcre2_SOURCE}/LICENCE.md" DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-libev/pcre2") + install(FILES "${pcre2_SOURCE}/LICENCE.md" DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-c/pcre2") endif() diff --git a/third_party/LibuvWindows.cmake b/third_party/LibuvWindows.cmake new file mode 100644 index 000000000..e9cf9f020 --- /dev/null +++ b/third_party/LibuvWindows.cmake @@ -0,0 +1,23 @@ +# libuv 1.52.1 passes const char ** to a converter that writes char **. +# GCC 14+ rejects this. Keep the archive untouched and use a mutable temporary +# in the extracted source, assigning it to the const public field afterwards. +if(WIN32) + set(uv_util "${uv_SOURCE}/src/win/util.c") + file(READ "${uv_util}" uv_util_content) + set(uv_old [=[ uv__convert_utf16_to_utf8(cpu_brand, + cpu_brand_size / sizeof(WCHAR), + &(cpu_info->model));]=]) + set(uv_new [=[ char* model = NULL; + uv__convert_utf16_to_utf8(cpu_brand, + cpu_brand_size / sizeof(WCHAR), + &model); + cpu_info->model = model;]=]) + string(FIND "${uv_util_content}" "${uv_old}" uv_old_offset) + string(FIND "${uv_util_content}" "${uv_new}" uv_new_offset) + if(NOT uv_old_offset EQUAL -1) + string(REPLACE "${uv_old}" "${uv_new}" uv_util_content "${uv_util_content}") + file(WRITE "${uv_util}" "${uv_util_content}") + elseif(uv_new_offset EQUAL -1) + message(FATAL_ERROR "Bundled libuv changed; review the Windows CPU-info compatibility fix") + endif() +endif() diff --git a/third_party/README.md b/third_party/README.md index 41f6725e2..fc57c5861 100644 --- a/third_party/README.md +++ b/third_party/README.md @@ -10,22 +10,28 @@ signature verification. - libsodium 1.0.22: ISC; see LICENSE in the archive. - c-ares 1.34.8: MIT; see LICENSE.md in the archive. - PCRE2 10.48: BSD-3-Clause; see LICENCE.md in the archive, including third-party notices. -- libev 4.33: BSD-2-Clause OR GPL-2.0-or-later; see LICENSE and source notices. +- libuv 1.52.1: MIT; see LICENSE in the archive, including third-party notices. - bloom: copied unchanged from the former shadowsocks/libbloom submodule, commit 437e1add5a2b9a87797d8c648df7cf5f3ee155a8. BSD-2-Clause; license and MurmurHash2 source notices retained alongside the sources. - BLAKE3 remains in src/blake3; its existing license files remain there. - uthash remains in src/uthash.h with its upstream copyright/license notice. -Mbed TLS, c-ares and PCRE2 use their upstream CMake builds. The local libsodium +Mbed TLS, c-ares, libuv and PCRE2 use their upstream CMake builds. The local libsodium CMake adapter compiles the portable C backends listed in upstream Makefile.am; it is maintained by this project, not upstream. Upstream known-answer tests are built and run under the `vendor` CTest label. SIMD acceleration needs its own compiler probes and validation before enabling additional backends. -On Windows, `libev_windows.h` selects raw Winsock handles rather than CRT file -descriptors, consistently for application I/O and libev's wakeup sockets. -Windows therefore requires bundled mode; an arbitrary system libev build does -not necessarily use the same descriptor convention. +On Windows, `LibuvWindows.cmake` applies a checked, idempotent fix to extracted +libuv 1.52.1 CPU-info code: a mutable string temporary replaces an incompatible +`const char **` output argument rejected by GCC 14+. The archive is unchanged. + +Libuv replaces libev. Its Windows socket polling uses AFD readiness requests +completed through IOCP; macOS uses kqueue. The internal `ss_event` layer combines +read/write watchers for each descriptor and owns libuv handle lifetime separately +from connection objects. c-ares stays integrated into that loop for asynchronous +A/AAAA resolution, custom DNS servers and cancellation. Native platform tests +exercise the backend, concurrent watchers, delayed DNS completion and shutdown. To update an archive: select a supported upstream release, download its release archive from the upstream source, verify published signatures/checksums where diff --git a/third_party/dist/libev-4.33.tar.gz b/third_party/dist/libev-4.33.tar.gz deleted file mode 100644 index 4cb43d84d..000000000 Binary files a/third_party/dist/libev-4.33.tar.gz and /dev/null differ diff --git a/third_party/dist/libuv-v1.52.1.tar.gz b/third_party/dist/libuv-v1.52.1.tar.gz new file mode 100644 index 000000000..6b28a2730 Binary files /dev/null and b/third_party/dist/libuv-v1.52.1.tar.gz differ diff --git a/third_party/libev_windows.h b/third_party/libev_windows.h deleted file mode 100644 index 67bf6224e..000000000 --- a/third_party/libev_windows.h +++ /dev/null @@ -1,8 +0,0 @@ -/* libev's int watcher descriptors carry Winsock handles in this application. - * Keep the same conversion for its wakeup socket pair and application I/O. - * The default CRT descriptor conversion is incompatible with raw sockets. - */ -#include -#define EV_FD_TO_WIN32_HANDLE(fd) ((SOCKET)(unsigned int)(fd)) -#define EV_WIN32_HANDLE_TO_FD(handle) ((int)(handle)) -#define EV_WIN32_CLOSE_FD(fd) closesocket(EV_FD_TO_WIN32_HANDLE(fd)) diff --git a/third_party/manifest.json b/third_party/manifest.json index c2435a89c..3993d748d 100644 --- a/third_party/manifest.json +++ b/third_party/manifest.json @@ -28,10 +28,10 @@ "sha256": "adbdd8f16149e81ac6078a03aca6fc03b592b89ef7b5ed83841c086191be3349" }, { - "name": "ev", - "version": "4.33", - "url": "https://dist.schmorp.de/libev/libev-4.33.tar.gz", - "archive": "libev-4.33.tar.gz", - "sha256": "507eb7b8d1015fbec5b935f34ebed15bf346bed04a11ab82b8eee848c4205aea" + "name": "uv", + "version": "1.52.1", + "url": "https://dist.libuv.org/dist/v1.52.1/libuv-v1.52.1.tar.gz", + "archive": "libuv-v1.52.1.tar.gz", + "sha256": "66d511b9e6e334c0e62279eb234fbfb2b3110b1479c09b95b44c7afca8cff9e7" } ]