diff --git a/.clang-tidy b/.clang-tidy index 6c2d98e8..b98f3bb8 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -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 34056199..45305415 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 04c15070..763a8b1e 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 @@ -59,6 +61,7 @@ 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" @@ -95,6 +98,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 +136,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 +177,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 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9af7418d..3bfcfd5c 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 76188be4..e6661179 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 234467d9..b3e0ba3d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ 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-libev 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, @@ -93,7 +93,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 | @@ -196,7 +196,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 +275,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 3dd28b45..70ce18d0 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 00000000..f60dd303 --- /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 4db28a13..e70fd895 100644 --- a/cmake/InstallPackage.cmake +++ b/cmake/InstallPackage.cmake @@ -5,23 +5,23 @@ 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) @@ -32,10 +32,14 @@ if(SS_BUILD_STATIC_LIBRARY) list(APPEND private_links "\${_ss_libdir}/shadowsocks-libev/$") string(APPEND SS_PC_PRIVATE " \${libdir}/shadowsocks-libev/$") 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,6 +47,10 @@ 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}") string(APPEND private_content "get_filename_component(_ss_libdir \"\${CMAKE_CURRENT_LIST_DIR}/${libdir_from_config}\" ABSOLUTE)\n") diff --git a/cmake/config.h.cmake b/cmake/config.h.cmake index de889cc8..bf19aa55 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 2c6d2496..1f25c230 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/debian/control b/debian/control index 30317194..afccd13e 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/docker/static/Dockerfile b/docker/static/Dockerfile index 76fdaf45..69e5c2c9 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/docs/modernization.md b/docs/modernization.md index 4a9aaad7..ae52bcaf 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 @@ -64,6 +64,22 @@ 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,21 @@ 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. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 32685e04..28ba0fa5 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} diff --git a/src/local.c b/src/local.c index 94f0402f..4b006a34 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 @@ not_bypass: 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 @@ not_bypass: 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 @@ not_bypass: 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 1cc45721..5de6261e 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 bb2c2f68..162c8162 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 @@ ERROR_MSG: } 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 11e6c728..5d58fd10 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 44f0cb41..4b75b58c 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 445d4018..a8cb880d 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 6f1b223c..6b498903 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 2544d65e..a03bf9cf 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 9c42d2f7..5c6a7bf8 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 events = (read ? SS_READ : 0) | (write ? SS_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; + 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 e211abc6..326eca0d 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 fd7c05f9..57d85a8a 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 7cd5b4e6..8e0762f6 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 00000000..b0470f2d --- /dev/null +++ b/src/ss_event.c @@ -0,0 +1,265 @@ +#include "ss_event.h" +#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 00000000..47ba9081 --- /dev/null +++ b/src/ss_event.h @@ -0,0 +1,57 @@ +/* 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 +#include +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); +uv_loop_t *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 bd5470bc..60d91758 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 871e8b6b..76e0b394 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 25263333..62417391 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 @@ CLEAN_UP: } 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 0048a631..e020ec99 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/tests/CMakeLists.txt b/tests/CMakeLists.txt index 80fbf2ea..64d4423e 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 00000000..ebce559b --- /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 3ff4d93b..f47af3a0 100644 --- a/tests/check_static_runtime.py +++ b/tests/check_static_runtime.py @@ -8,7 +8,7 @@ from pathlib import Path 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/test_event.c b/tests/test_event.c new file mode 100644 index 00000000..c5bfa016 --- /dev/null +++ b/tests/test_event.c @@ -0,0 +1,82 @@ +#include "test_helpers.h" +#include "ss_event.h" +#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 b47000de..763ca968 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 87b2d9b2..743811e5 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 00000000..1d5c2cd2 --- /dev/null +++ b/tests/test_resolv_async.c @@ -0,0 +1,139 @@ +/* Local delayed DNS server proves A/AAAA/NXDOMAIN completion without blocking. */ +#include "test_helpers.h" +#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 9c2413dd..7e18a29f 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -63,17 +63,16 @@ 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) +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) +foreach(name sodium uv mbedtls) install(FILES "${${name}_SOURCE}/LICENSE" DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-libev/${name}") endforeach() install(FILES "${cares_SOURCE}/LICENSE.md" DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/shadowsocks-libev/cares") diff --git a/third_party/README.md b/third_party/README.md index 41f6725e..f844eaea 100644 --- a/third_party/README.md +++ b/third_party/README.md @@ -10,22 +10,24 @@ 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. +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 4cb43d84..00000000 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 00000000..6b28a273 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 67bf6224..00000000 --- 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 c2435a89..3993d748 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" } ]