diff --git a/.gitignore b/.gitignore index 4ad734ac..75e2f718 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,9 @@ *.gcov *.gcda +# gdb stuff +.gdb_history + # Autotools stuff .deps .dirstamp @@ -36,6 +39,7 @@ Makefile.in # ctags stuff TAGS +tags # Stuff made by our makefiles libevent.pc @@ -80,10 +84,12 @@ libevent_openssl.pc /sample/event-read-fifo /sample/hello-world /sample/http-server +/sample/http-connect /sample/le-proxy /sample/https-client /sample/signal-test /sample/time-test +/sample/event-test /test-driver /test/bench diff --git a/.travis.yml b/.travis.yml index 6f10ff62..f1d8141f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,9 @@ env: - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="" - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_OPENSSL=ON" - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_THREAD_SUPPORT=ON" - - EVENT_BUILD_METHOD=autotools + - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_DEBUG_MODE=ON" + - EVENT_BUILD_METHOD=autotools EVENT_CONFIGURE_OPTIONS="" + - EVENT_BUILD_METHOD=autotools EVENT_CONFIGURE_OPTIONS="--disable-debug-mode" language: c compiler: - gcc @@ -12,5 +14,8 @@ install: - sudo apt-get update -qq - sudo apt-get install -y -qq zlib1g-dev libssl-dev build-essential automake autoconf cmake script: - - if [ "$EVENT_BUILD_METHOD" = "autotools" ]; then ./autogen.sh && ./configure && make && make verify; fi + - if [ "$EVENT_BUILD_METHOD" = "autotools" ]; then ./autogen.sh && ./configure $EVENT_CONFIGURE_OPTIONS && make && make verify; fi - if [ "$EVENT_BUILD_METHOD" = "cmake" ]; then mkdir build && cd build && cmake .. $EVENT_CMAKE_OPTIONS && cmake --build . && CTEST_OUTPUT_ON_FAILURE=1 cmake --build . --target verify; fi + +notifications: + irc: "irc.oftc.net#libevent" diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e76b3e6..08461bce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,38 +20,117 @@ # cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release + CACHE STRING "Set build type to Debug o Release (default Release)" FORCE) +endif() + # get rid of the extra default configurations +# what? why would you get id of other useful build types? - Ellzey set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Limited configurations" FORCE) project(libevent C) -set(EVENT_VERSION_MAJOR 2) -set(EVENT_VERSION_MINOR 1) -set(EVENT_VERSION_PATCH 5) +set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/") + + +include(CheckTypeSize) +include(CheckFunctionExistsEx) +include(CheckFileOffsetBits) +include(CheckFunctionExists) +include(CheckIncludeFile) +include(CheckIncludeFiles) +include(CheckVariableExists) +include(CheckSymbolExists) +include(CheckStructHasMember) +include(CheckCSourceCompiles) +include(CheckPrototypeDefinition) +include(CheckFunctionKeywords) +include(AddCompilerFlags) +include(VersionViaGit) + +event_fuzzy_version_from_git() + +set(EVENT_VERSION_MAJOR ${EVENT_GIT___VERSION_MAJOR}) +set(EVENT_VERSION_MINOR ${EVENT_GIT___VERSION_MINOR}) +set(EVENT_VERSION_PATCH ${EVENT_GIT___VERSION_PATCH}) +set(EVENT_VERSION_STAGE ${EVENT_GIT___VERSION_STAGE}) + + +set(EVENT_ABI_MAJOR ${EVENT_VERSION_MAJOR}) +set(EVENT_ABI_MINOR ${EVENT_VERSION_MINOR}) +set(EVENT_ABI_PATCH ${EVENT_VERSION_PATCH}) + +set(EVENT_ABI_LIBVERSION + "${EVENT_ABI_MAJOR}.${EVENT_ABI_MINOR}.${EVENT_ABI_PATCH}") + +set(EVENT_PACKAGE_VERSION + "${EVENT_VERSION_MAJOR}.${EVENT_VERSION_MINOR}.${EVENT_VERSION_PATCH}") + set(EVENT_NUMERIC_VERSION 0x02010500) -set(EVENT_VERSION_DEVTAG "-beta") -set(EVENT_ABI_MAJOR 2) -set(EVENT_ABI_MINOR 1) -set(EVENT_ABI_PATCH 5) -set(EVENT_ABI_LIBVERSION "${EVENT_ABI_MAJOR}.${EVENT_ABI_MINOR}.${EVENT_ABI_PATCH}") +# only a subset of names can be used, defaults to "beta" +set(EVENT_STAGE_NAME ${EVENT_VERSION_STAGE} + CACHE STRING "set EVENT_STAGE_NAME") -set(EVENT_VERSION "${EVENT_VERSION_MAJOR}.${EVENT_VERSION_MINOR}.${EVENT_VERSION_PATCH}-alpha${EVENT_VERSION_DEVTAG}") -set(EVENT_PACKAGE_VERSION "${EVENT_VERSION_MAJOR}.${EVENT_VERSION_MINOR}.${EVENT_VERSION_PATCH}") +# a list that defines what can set for EVENT_STAGE_VERSION +set(EVENT__ALLOWED_STAGE_NAMES + rc + beta + alpha + release) + +# attempt to find the EVENT__STAGE_VERSION in the allowed list +# of accepted stage names, the return value is stord in +# EVENT__STAGE_RET + +list(FIND EVENT__ALLOWED_STAGE_NAMES + ${EVENT_STAGE_NAME} + EVENT__STAGE_RET) + +if (EVENT_STAGE_RET EQUAL "-1") + set(EVENT_STAGE_NAME "beta") +endif() + +set(EVENT_VERSION + "${EVENT_VERSION_MAJOR}.${EVENT_VERSION_MINOR}.${EVENT_VERSION_PATCH}-${EVENT_STAGE_NAME}") + +option(EVENT__BUILD_SHARED_LIBRARIES + "Define if libevent should be built with shared libraries instead of archives" OFF) + +option(EVENT__DISABLE_DEBUG_MODE + "Define if libevent should build without support for a debug mode" OFF) + +option(EVENT__ENABLE_VERBOSE_DEBUG + "Enables verbose debugging" OFF) + +option(EVENT__DISABLE_MM_REPLACEMENT + "Define if libevent should not allow replacing the mm functions" OFF) + +option(EVENT__DISABLE_THREAD_SUPPORT + "Define if libevent should not be compiled with thread support" OFF) + +option(EVENT__DISABLE_OPENSSL + "Define if libevent should build without support for OpenSSL encrpytion" OFF) + +option(EVENT__DISABLE_BENCHMARK + "Defines if libevent should build without the benchmark exectuables" OFF) + +option(EVENT__DISABLE_TESTS + "If tests should be compiled or not" OFF) + +option(EVENT__DISABLE_REGRESS + "Disable the regress tests" OFF) + +option(EVENT__DISABLE_SAMPLES + "Disable sample files" OFF) + +option(EVENT__FORCE_KQUEUE_CHECK + "When crosscompiling forces running a test program that verifies that Kqueue works with pipes. Note that this requires you to manually run the test program on the the cross compilation target to verify that it works. See cmake documentation for try_run for more details" OFF) -option(EVENT__BUILD_SHARED_LIBRARIES "Define if libevent should be built with shared libraries instead of archives" OFF) -option(EVENT__DISABLE_DEBUG_MODE "Define if libevent should build without support for a debug mode" OFF) -option(EVENT__ENABLE_VERBOSE_DEBUG "Enables verbose debugging" OFF) -option(EVENT__DISABLE_MM_REPLACEMENT "Define if libevent should not allow replacing the mm functions" OFF) -option(EVENT__DISABLE_THREAD_SUPPORT "Define if libevent should not be compiled with thread support" OFF) -option(EVENT__DISABLE_OPENSSL "Define if libevent should build without support for OpenSSL encrpytion" OFF) -option(EVENT__DISABLE_BENCHMARK "Defines if libevent should build without the benchmark exectuables" OFF) -option(EVENT__DISABLE_TESTS "If tests should be compiled or not" OFF) -option(EVENT__DISABLE_REGRESS "Disable the regress tests" OFF) -option(EVENT__DISABLE_SAMPLES "Disable sample files" OFF) -option(EVENT__FORCE_KQUEUE_CHECK "When crosscompiling forces running a test program that verifies that Kqueue works with pipes. Note that this requires you to manually run the test program on the the cross compilation target to verify that it works. See cmake documentation for try_run for more details" OFF) -option(EVENT__COVERAGE "Enable running gcov to get a test coverage report (only works with GCC/CLang). Make sure to enable -DCMAKE_BUILD_TYPE=Debug as well." OFF) # TODO: Add --disable-largefile omit support for large files +option(EVENT__COVERAGE +"Enable running gcov to get a test coverage report (only works with GCC/CLang). Make sure to enable -DCMAKE_BUILD_TYPE=Debug as well." OFF) # Put the libaries and binaries that get built into directories at the # top of the build tree rather than in hard-to-find leaf directories. @@ -59,81 +138,69 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") -include(CheckFunctionExistsEx) -include(CheckFileOffsetBits) -include(CheckFunctionExists) -include(CheckIncludeFile) -include(CheckIncludeFiles) -include(CheckTypeSize) -include(CheckVariableExists) -include(CheckSymbolExists) -include(CheckStructHasMember) -include(CheckCSourceCompiles) -include(CheckPrototypeDefinition) - if (EVENT__ENABLE_VERBOSE_DEBUG) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_DEBUG=1") + add_definitions(-DUSE_DBUG=1) endif() # Setup compiler flags for coverage. if (EVENT__COVERAGE) - if(NOT CMAKE_COMPILER_IS_GNUCC) - if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - message(FATAL_ERROR "Trying to compile coverage support (--DEVENT__COVERAGE) but compiler is not GNU gcc! Aborting... You can set this on the command line using CC=/usr/bin/gcc CXX=/usr/bin/g++ cmake ..") - endif() + if ((NOT CMAKE_COMPILER_IS_GNUCC) AND (NOT ${CMAKE_CXX_COMPILER_ID} STREQAL "Clang")) + message(FATAL_ERROR"Trying to compile coverage support, but compiler is not GNU gcc! Try CC=/usr/bin/gcc CXX=/usr/bin/g++ cmake ..") endif() if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - message(FATAL_ERROR "Code coverage results with an optimised (non-Debug) build may be misleading! Add -DCMAKE_BUILD_TYPE=Debug") + message(FATAL_ERROR "Coverage requires -DCMAKE_BUILD_TYPE=Debug") endif() - message("Setting coverage compiler flags") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") + message(STATUS "Setting coverage compiler flags") + add_compiler_flags(-g -O0 -fprofile-arcs -ftest-coverage) endif() # GCC specific options. if (CMAKE_COMPILER_IS_GNUCC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") - option(EVENT__DISABLE_GCC_WARNINGS "Disable verbose warnings with GCC" OFF) - if (EVENT__DISABLE_GCC_WARNINGS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w") - endif() - option(EVENT__ENABLE_GCC_HARDENING "Enable compiler security checks" OFF) - if (EVENT__ENABLE_GCC_HARDENING) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2 -fstack-protector-all -fwrapv -fPIE -Wstack-protector --param ssp-buffer-size=1") + option(EVENT__ENABLE_GCC_FUNCTION_SECTIONS "Enable gcc function sections" OFF) + option(EVENT__ENABLE_GCC_WARNINGS "Make all GCC warnings into errors" OFF) + + list(APPEND __FLAGS -Wall) + + if (EVENT__DISABLE_GCC_WARNINGS) + list(APPEND __FLAGS -w) + endif() + + if (EVENT__ENABLE_GCC_HARDENING) + list(APPEND __FLAGS + -fstack-protector-all + -fwrapv + -fPIE + -Wstack-protector + "--param ssp-buffer-size=1") + + add_definitions(-D_FORTIFY_SOURCE=2) endif() - option(EVENT__ENABLE_GCC_FUNCTION_SECTIONS "Enable gcc function sections" OFF) if (EVENT__ENABLE_GCC_FUNCTION_SECTIONS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections") + list(APPEND __FLAGS -ffunction-sections) # TODO: Add --gc-sections support. We need some checks for NetBSD to ensure this works. endif() - option(EVENT__ENABLE_GCC_WARNINGS "Make all GCC warnings into errors" OFF) if (EVENT__ENABLE_GCC_WARNINGS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") + list(APPEND __FLAGS -Werror) endif() # We need to test for at least gcc 2.95 here, because older versions don't # have -fno-strict-aliasing - execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion - OUTPUT_VARIABLE GCC_VERSION) - if (GCC_VERSION VERSION_GREATER 2.95) - message(STATUS "GCC Version >= 2.95 enabling no-strict-aliasing") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-strict-aliasing") - endif() + list(APPEND __FLAGS -fno-strict-aliasing) + + add_compiler_flags(__FLAGS) endif() if (APPLE) # Get rid of deprecated warnings for OpenSSL on OSX 10.7 and greater. - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error=deprecated-declarations") - # Get rid of "clang: warning: argument unused during compilation: -I etc - if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Qunused-arguments") - endif() + add_compiler_flags( + -Wno-error=deprecated-declarations + -Qunused-arguments) endif() # Winsock. @@ -318,43 +385,16 @@ if(WIN32) endif() # Check for different inline keyword versions. -foreach(KEYWORD "inline" "__inline__" "__inline") - set(CMAKE_REQUIRED_DEFINITIONS "-DKEYWORD=${KEYWORD}") - CHECK_C_SOURCE_COMPILES( - " - #include - KEYWORD void a() {} - int main(int argc, char **argv) { a(); return 0; } - " HAVE_${KEYWORD}) -endforeach() +check_function_keywords("inline" "__inline" "__inline__") -if (NOT HAVE_inline) - if (HAVE___inline__) - set(EVENT__inline __inline__) - elseif(HAVE___inline) - set(EVENT__inline __inline) - endif() -endif() -set(CMAKE_REQUIRED_DEFINITIONS "") - -# Check for different function name macros. -foreach(KEYWORD "__func__" "__FUNCTION__") - set(CMAKE_REQUIRED_DEFINITIONS "-DKEYWORD=${KEYWORD}") - CHECK_C_SOURCE_COMPILES( - " - #include - int main(int argc, char **argv) { const char *cp = KEYWORD; return 0; } - " HAVE_${KEYWORD}) -endforeach() -set(CMAKE_REQUIRED_DEFINITIONS "") - -if (NOT HAVE___func__) - if (HAVE___FUNCTION__) - set(EVENT____func__ __FUNCTION__) - else() - # Substitute for __func__ - set(EVENT____func__ __FILE__) - endif() +if (HAVE_INLINE) + set (EVENT__inline inline) +elseif (HAVE___INLINE) + set(EVENT__inline __inline) +elseif(HAVE___INLINE__) + set(EVENT__inline __inline__) +else() + set(EVENT__inline) endif() CHECK_SYMBOL_EXISTS(TAILQ_FOREACH sys/queue.h EVENT__HAVE_TAILQFOREACH) @@ -366,51 +406,77 @@ CHECK_SYMBOL_EXISTS(F_SETFD fcntl.h EVENT__HAVE_SETFD) CHECK_TYPE_SIZE(fd_mask EVENT__HAVE_FD_MASK) -CHECK_TYPE_SIZE(size_t EVENT__SIZEOF_SIZE_T) +CHECK_TYPE_SIZE(size_t EVENT__SIZEOF_SIZEE_T) if(NOT EVENT__SIZEOF_SIZE_T) - set(EVENT__size_t unsigned) + set(EVENT__size_t "unsigned") set(EVENT__SIZEOF_SIZE_T ${EVENT__SIZEOF_UNSIGNED}) +else() + set(EVENT__size_t size_t) endif() -CHECK_TYPE_SIZE("off_t" EVENT__SIZEOF_OFF_T) +CHECK_TYPE_SIZE("off_t" EVENT__SIZEOF_OFF_T LANGUAGE C) -CHECK_TYPE_SIZE(ssize_t EVENT__SIZEOF_SSIZE_T) -CHECK_TYPE_SIZE(SSIZE_T EVENT__SIZEOF_UPPERCASE_SSIZE_T) -if(NOT EVENT__SIZEOF_SSIZE_T) - if(EVENT__SIZEOF_UPPERCASE_SSIZE_T) - set(EVENT__ssize_t SSIZE_T) - set(EVENT__SIZEOF_SSIZE_T ${EVENT__SIZEOF_UPPERCASE_SSIZE_T}) - else() - set(EVENT__ssize_t int) - set(EVENT__SIZEOF_SSIZE_T ${EVENT__SIZEOF_INT}) - endif() + +# XXX we should functionalize these size and type sets. --elley + +# Winssck. +set(SSIZE_TMAP_ssize_t "ssize_t") +set(SSIZE_TMAP_SSIZE_T "SSIZE_T") + +CHECK_TYPE_SIZE("ssize_t" EVENT__SIZEOF_SSIZE_T LANGUAGE C) +CHECK_TYPE_SIZE("SSIZE_T" EVENT__SIZEOF_SSIZE_T LANGUAGE C) + +if (DEFINED EVENT__SIZEOF_SSIZE_T) + if (DEFINED SSIZE_TMAP_ssize_t) + set(EVENT__ssize_t "ssize_t") + elseif (DEFINED SSIZE_TMAP_SSIZE_T) + set(EVENT__ssize_t "SSIZE_T") + else() + set(EVENT__ssize_t "int") + set(EVENT__SIZEOF_SSIZE_T ${EVENT__SIZEOF_INT}) + endif() endif() + CHECK_TYPE_SIZE(socklen_t EVENT__SIZEOF_SOCKLEN_T) if(NOT EVENT__SIZEOF_SOCKLEN_T) set(EVENT__socklen_t "unsigned int") set(EVENT__SIZEOF_SOCKLEN_T ${EVENT__SIZEOF_UNSIGNED_INT}) +else() + set(EVENT__socklen_t "socklen_t") endif() CHECK_TYPE_SIZE(pid_t EVENT__SIZEOF_PID_T) if(NOT EVENT__SIZEOF_PID_T) - set(EVENT__pid_t int) + set(EVENT__pid_t "int") set(EVENT__SIZEOF_PID_T ${EVENT__SIZEOF_INT}) +else() + set(EVENT__pid_t "pid_t") + set(EVENT__SIZEOF_PID_T EVENT__SIZEOF_PID_T) endif() -CHECK_TYPE_SIZE(pthread_t EVENT__SIZEOF_PTHREAD_T) +if (NOT EVENT__DISABLE_THREAD_SUPPORT) + CHECK_TYPE_SIZE(pthread_t EVENT__SIZEOF_PTHREAD_T) +endif() if(EVENT__HAVE_CLOCK_GETTIME) set(EVENT__DNS_USE_CPU_CLOCK_FOR_ID 1) endif() +# we're just getting lazy now. CHECK_TYPE_SIZE("uintptr_t" EVENT__HAVE_UINTPTR_T) CHECK_TYPE_SIZE("void *" EVENT__SIZEOF_VOID_P) # Tests file offset bits. # TODO: Add AIX test for if -D_LARGE_FILES is needed. -CHECK_FILE_OFFSET_BITS() -set(EVENT___FILE_OFFSET_BITS _FILE_OFFSET_BITS) + +# XXX: Why is this here? we don't even use it. Well, we don't even use it +# on top of that, why is it set in the config.h?! IT_MAKES_NO_SENSE +# I'm commenting it out for now. +# - ellzey + +#CHECK_FILE_OFFSET_BITS() +#set(EVENT___FILE_OFFSET_BITS _FILE_OFFSET_BITS) # Verify kqueue works with pipes. if (EVENT__HAVE_KQUEUE) @@ -418,7 +484,7 @@ if (EVENT__HAVE_KQUEUE) message(WARNING "Cannot check if kqueue works with pipes when crosscompiling, use EVENT__FORCE_KQUEUE_CHECK to be sure (this requires manually running a test program on the cross compilation target)") set(EVENT__HAVE_WORKING_KQUEUE 1) else() - message("Checking if kqueue works with pipes...") + message(STATUS "Checking if kqueue works with pipes...") include(CheckWorkingKqueue) endif() endif() @@ -437,7 +503,6 @@ endif() # Check for sockaddr structure sizes. set(SOCKADDR_HEADERS) - if (WIN32) set(CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN") if (_MSC_VER LESS 1300) @@ -465,21 +530,36 @@ endif() CHECK_TYPE_SIZE("struct in6_addr" EVENT__HAVE_STRUCT_IN6_ADDR) if(EVENT__HAVE_STRUCT_IN6_ADDR) - CHECK_STRUCT_HAS_MEMBER("struct in6_addr" s6_addr16 "${SOCKADDR_HEADERS}" EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR16) - CHECK_STRUCT_HAS_MEMBER("struct in6_addr" s6_addr32 "${SOCKADDR_HEADERS}" EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR32) + CHECK_STRUCT_HAS_MEMBER("struct in6_addr" + s6_addr16 "${SOCKADDR_HEADERS}" + EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR16) + + CHECK_STRUCT_HAS_MEMBER("struct in6_addr" + s6_addr32 "${SOCKADDR_HEADERS}" + EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR32) endif() CHECK_TYPE_SIZE("sa_family_t" EVENT__HAVE_SA_FAMILY_T) CHECK_TYPE_SIZE("struct sockaddr_in6" EVENT__HAVE_STRUCT_SOCKADDR_IN6) + if(EVENT__HAVE_STRUCT_SOCKADDR_IN6) - CHECK_STRUCT_HAS_MEMBER("struct sockaddr_in6" sin6_len "${SOCKADDR_HEADERS}" EVENT__HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN) - CHECK_STRUCT_HAS_MEMBER("struct sockaddr_in6" sin_len "${SOCKADDR_HEADERS}" EVENT__HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) + CHECK_STRUCT_HAS_MEMBER("struct sockaddr_in6" + sin6_len "${SOCKADDR_HEADERS}" + EVENT__HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN) + + CHECK_STRUCT_HAS_MEMBER("struct sockaddr_in6" + sin_len "${SOCKADDR_HEADERS}" + EVENT__HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) endif() CHECK_TYPE_SIZE("struct sockaddr_storage" EVENT__HAVE_STRUCT_SOCKADDR_STORAGE) if(EVENT__HAVE_STRUCT_SOCKADDR_STORAGE) - CHECK_STRUCT_HAS_MEMBER("struct sockaddr_storage" ss_family "${SOCKADDR_HEADERS}" EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY) - CHECK_STRUCT_HAS_MEMBER("struct sockaddr_storage" __ss_family "${SOCKADDR_HEADERS}" EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY) + CHECK_STRUCT_HAS_MEMBER("struct sockaddr_storage" + ss_family "${SOCKADDR_HEADERS}" + EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY) + + CHECK_STRUCT_HAS_MEMBER("struct sockaddr_storage" + __ss_family "${SOCKADDR_HEADERS}" EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY) endif() # Group the source files. @@ -505,16 +585,14 @@ set(HDR_PRIVATE strlcpy-internal.h util-internal.h evconfig-private.h - compat/sys/queue.h - ) + compat/sys/queue.h) set(HDR_COMPAT include/evdns.h include/evrpc.h include/event.h include/evhttp.h - include/evutil.h - ) + include/evutil.h) set(HDR_PUBLIC include/event2/buffer.h @@ -541,8 +619,7 @@ set(HDR_PUBLIC include/event2/thread.h include/event2/util.h include/event2/visibility.h - ${PROJECT_BINARY_DIR}/include/event2/event-config.h - ) + ${PROJECT_BINARY_DIR}/include/event2/event-config.h) set(SRC_CORE buffer.c @@ -560,8 +637,7 @@ set(SRC_CORE listener.c log.c signal.c - strlcpy.c - ) + strlcpy.c) if(EVENT__HAVE_SELECT) list(APPEND SRC_CORE select.c) @@ -589,10 +665,14 @@ endif() if (NOT EVENT__DISABLE_OPENSSL) find_package(OpenSSL REQUIRED) + set(EVENT__HAVE_OPENSSL 1) - message("OpenSSL include: ${OPENSSL_INCLUDE_DIR}") - message("OpenSSL lib: ${OPENSSL_LIBRARIES}") + + message(STATUS "OpenSSL include: ${OPENSSL_INCLUDE_DIR}") + message(STATUS "OpenSSL lib: ${OPENSSL_LIBRARIES}") + include_directories(${OPENSSL_INCLUDE_DIR}) + list(APPEND SRC_CORE bufferevent_openssl.c) list(APPEND HDR_PUBLIC include/event2/bufferevent_ssl.h) list(APPEND LIB_APPS ${OPENSSL_LIBRARIES}) @@ -604,8 +684,10 @@ if (NOT EVENT__DISABLE_THREAD_SUPPORT) else() find_package(Threads REQUIRED) if (NOT CMAKE_USE_PTHREADS_INIT) - message(FATAL_ERROR "Failed to find Pthreads, set EVENT__DISABLE_THREAD_SUPPORT to turn off thread support") + message(FATAL_ERROR + "Failed to find Pthreads, set EVENT__DISABLE_THREAD_SUPPORT to disable") endif() + set(EVENT__HAVE_PTHREADS 1) list(APPEND SRC_CORE evthread_pthread.c) list(APPEND LIB_APPS ${CMAKE_THREAD_LIBS_INIT}) @@ -617,9 +699,10 @@ if (NOT EVENT__DISABLE_TESTS) find_package(ZLIB) if (ZLIB_LIBRARY) + include_directories(${ZLIB_INCLUDE_DIRS}) + set(EVENT__HAVE_ZLIB 1) set(EVENT__HAVE_ZLIB_H) - include_directories(${ZLIB_INCLUDE_DIRS}) list(APPEND LIB_APPS ${ZLIB_LIBRARIES}) endif() endif() @@ -628,8 +711,7 @@ set(SRC_EXTRA event_tagging.c http.c evdns.c - evrpc.c - ) + evrpc.c) add_definitions(-DHAVE_CONFIG_H) @@ -645,14 +727,16 @@ if(WIN32) bufferevent_async.c event_iocp.c evthread_win32.c - win32select.c - ) + win32select.c) list(APPEND HDR_PRIVATE WIN32-Code/getopt.h) set(EVENT__DNS_USE_FTIME_FOR_ID 1) - add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE) set(LIB_PLATFORM ws2_32) + add_definitions( + -D_CRT_SECURE_NO_WARNINGS + -D_CRT_NONSTDC_NO_DEPRECATE) + include_directories(./WIN32-Code) endif() @@ -673,22 +757,21 @@ include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/include) if (EVENT__BUILD_SHARED_LIBRARIES) set(EVENT__LIBRARY_TYPE SHARED) - if (CMAKE_COMPILER_IS_GNUCC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") - elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") + if ((CMAKE_COMPILER_IS_GNUCC) OR (${CMAKE_C_COMPILER_ID} STREQUAL "Clang")) + add_compiler_flags(-fvisibility=hidden) elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "SunPro") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -xldscope=hidden") - endif () - set(EVENT__NEED_DLLIMPORT 1) + add_compiler_flags(-xldscope=hidden) + endif() + set(EVENT__NEED_DLLIMPORT 1) else (EVENT__BUILD_SHARED_LIBRARIES) set(EVENT__LIBRARY_TYPE STATIC) endif (EVENT__BUILD_SHARED_LIBRARIES) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/event-config.h.cmake - ${CMAKE_CURRENT_BINARY_DIR}/include/event2/event-config.h) + ${CMAKE_CURRENT_BINARY_DIR}/include/event2/event-config.h + NEWLINE_STYLE UNIX) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/evconfig-private.h.cmake @@ -703,16 +786,14 @@ add_library(event_core ${EVENT__LIBRARY_TYPE} ${HDR_PRIVATE} ${HDR_COMPAT} ${HDR_PUBLIC} - ${SRC_CORE} - ) + ${SRC_CORE}) add_library(event_extra ${EVENT__LIBRARY_TYPE} ${HDR_PRIVATE} ${HDR_COMPAT} ${HDR_PUBLIC} ${SRC_CORE} - ${SRC_EXTRA} - ) + ${SRC_EXTRA}) # library exists for historical reasons; it contains the contents of # both libevent_core and libevent_extra. You shouldn’t use it; it may @@ -722,11 +803,7 @@ add_library(event ${EVENT__LIBRARY_TYPE} ${HDR_COMPAT} ${HDR_PUBLIC} ${SRC_CORE} - ${SRC_EXTRA} - ) - -if (EVENT__BUILD_SHARED_LIBRARIES) -endif (EVENT__BUILD_SHARED_LIBRARIES) + ${SRC_EXTRA}) if (EVENT__BUILD_SHARED_LIBRARIES) # Prepare static library to be linked to tests that need hidden symbols @@ -735,8 +812,8 @@ if (EVENT__BUILD_SHARED_LIBRARIES) ${HDR_COMPAT} ${HDR_PUBLIC} ${SRC_CORE} - ${SRC_EXTRA} - ) + ${SRC_EXTRA}) + set(EVENT_EXTRA_FOR_TEST event_extra_static) target_link_libraries(event_core ${OPENSSL_LIBRARIES} @@ -751,9 +828,17 @@ if (EVENT__BUILD_SHARED_LIBRARIES) ${CMAKE_THREAD_LIBS_INIT} ${LIB_PLATFORM}) - set_target_properties(event PROPERTIES SOVERSION ${EVENT_ABI_LIBVERSION}) - set_target_properties(event_core PROPERTIES SOVERSION ${EVENT_ABI_LIBVERSION}) - set_target_properties(event_extra PROPERTIES SOVERSION ${EVENT_ABI_LIBVERSION}) + set_target_properties(event + PROPERTIES SOVERSION + ${EVENT_ABI_LIBVERSION}) + + set_target_properties(event_core + PROPERTIES SOVERSION + ${EVENT_ABI_LIBVERSION}) + + set_target_properties(event_extra + PROPERTIES SOVERSION + ${EVENT_ABI_LIBVERSION}) else (EVENT__BUILD_SHARED_LIBRARIES) set(EVENT_EXTRA_FOR_TEST event_extra) @@ -770,15 +855,25 @@ if (NOT EVENT__DISABLE_SAMPLES) hello-world signal-test http-server + http-connect time-test) if (NOT EVENT__DISABLE_OPENSSL AND OPENSSL_LIBRARIES) - # Special sample with more than one file. + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) + CHECK_FUNCTION_EXISTS_EX(ERR_remove_thread_state EVENT__HAVE_ERR_REMOVE_THREAD_STATE) + set(CMAKE_REQUIRED_LIBRARIES "") + + # Special sample with more than one file. add_executable(https-client sample/https-client.c sample/openssl_hostname_validation.c sample/hostcheck.c) - target_link_libraries(https-client event_extra ${LIB_APPS} ${LIB_PLATFORM}) + + target_link_libraries(https-client + event_extra + ${LIB_APPS} + ${LIB_PLATFORM}) + add_dependencies(https-client event_extra) # Requires OpenSSL. @@ -786,8 +881,14 @@ if (NOT EVENT__DISABLE_SAMPLES) endif() foreach(SAMPLE ${SAMPLES}) - add_executable(${SAMPLE} sample/${SAMPLE}.c) - target_link_libraries(${SAMPLE} event_extra ${LIB_APPS} ${LIB_PLATFORM}) + add_executable(${SAMPLE} + sample/${SAMPLE}.c) + + target_link_libraries(${SAMPLE} + event_extra + ${LIB_APPS} + ${LIB_PLATFORM}) + add_dependencies(${SAMPLE} event_extra) endforeach() endif() @@ -797,17 +898,22 @@ if (NOT EVENT__DISABLE_BENCHMARK) set(BENCH_SRC test/${BENCHMARK}.c) if (WIN32) - list(APPEND BENCH_SRC WIN32-Code/getopt.c WIN32-Code/getopt_long.c) + list(APPEND BENCH_SRC + WIN32-Code/getopt.c + WIN32-Code/getopt_long.c) endif() add_executable(${BENCHMARK} ${BENCH_SRC}) - target_link_libraries(${BENCHMARK} event_extra ${LIB_PLATFORM}) + + target_link_libraries(${BENCHMARK} + event_extra + ${LIB_PLATFORM}) + add_dependencies(${BENCHMARK} event_extra) endforeach() endif() if (NOT EVENT__DISABLE_TESTS) - # # Generate Regress tests. # @@ -815,13 +921,16 @@ if (NOT EVENT__DISABLE_TESTS) # (We require python to generate the regress tests) find_package(PythonInterp) + if (PYTHONINTERP_FOUND AND PYTHON_VERSION_STRING VERSION_LESS "3.0.0") set(__FOUND_USABLE_PYTHON 1) endif() if (__FOUND_USABLE_PYTHON) - message("Generating regress tests...") + message(STATUS "Generating regress tests...") + add_definitions(-DTINYTEST_LOCAL) + add_custom_command( OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/test/regress.gen.c @@ -830,8 +939,7 @@ if (NOT EVENT__DISABLE_TESTS) event_rpcgen.py test/regress.rpc COMMAND ${PYTHON_EXECUTABLE} ../event_rpcgen.py regress.rpc - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test - ) + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test) list(APPEND SRC_REGRESS test/regress.c @@ -852,8 +960,7 @@ if (NOT EVENT__DISABLE_TESTS) test/regress_util.c test/tinytest.c ${SRC_CORE} - ${SRC_EXTRA} - ) + ${SRC_EXTRA}) if (WIN32) list(APPEND SRC_REGRESS test/regress_iocp.c) @@ -873,14 +980,18 @@ if (NOT EVENT__DISABLE_TESTS) endif() add_executable(regress ${SRC_REGRESS}) + # While building the test suite we don't want the visibility # header trying to "dllimport" the symbols on windows (it # generates a ton of warnings due to different link # attributes for all of the symbols) - SET_TARGET_PROPERTIES(regress PROPERTIES COMPILE_DEFINITIONS - "EVENT_BUILDING_REGRESS_TEST=1") + SET_TARGET_PROPERTIES(regress + PROPERTIES COMPILE_DEFINITIONS + "EVENT_BUILDING_REGRESS_TEST=1") - target_link_libraries(regress ${LIB_APPS} ${LIB_PLATFORM}) + target_link_libraries(regress + ${LIB_APPS} + ${LIB_PLATFORM}) else() message(WARNING "No suitable Python interpreter found, cannot generate regress tests!") endif() @@ -889,6 +1000,20 @@ if (NOT EVENT__DISABLE_TESTS) # # Test programs. # + # all of these, including the cmakelists.txt should be moved + # into the dirctory 'tests' first. + # + # doing this, we can remove all the DISABLE_TESTS stuff, and simply + # do something like: + # + # add_custom_targets(tests) + # add_executable(... EXCLUDE_FROM_ALL ...c) + # add_dependencis(tests testa testb testc) + # add_test(....) + # + # then you can just run 'make tests' instead of them all + # auto-compile|running + # - ellzey set(TESTPROGS test-changelist test-eof test-fdleak @@ -896,13 +1021,22 @@ if (NOT EVENT__DISABLE_TESTS) test-time test-weof) - set(ALL_TESTPROGS ${TESTPROGS} test-dumpevents test-ratelim) + set(ALL_TESTPROGS + ${TESTPROGS} + test-dumpevents + test-ratelim) # Create test program executables. foreach (TESTPROG ${ALL_TESTPROGS}) - add_executable(${TESTPROG} test/${TESTPROG}.c) - target_link_libraries(${TESTPROG} ${EVENT_EXTRA_FOR_TEST} ${LIB_PLATFORM}) - add_dependencies(${TESTPROG} ${EVENT_EXTRA_FOR_TEST}) + add_executable(${TESTPROG} + test/${TESTPROG}.c) + + target_link_libraries(${TESTPROG} + ${EVENT_EXTRA_FOR_TEST} + ${LIB_PLATFORM}) + + add_dependencies(${TESTPROG} + ${EVENT_EXTRA_FOR_TEST}) endforeach() # @@ -940,7 +1074,6 @@ if (NOT EVENT__DISABLE_TESTS) list(APPEND BACKENDS WIN32) endif() - message("Available event backends: ${BACKENDS}") # Default environment variables turns off all event systems, # then we enable each one, one at a time when creating the tests. @@ -956,28 +1089,53 @@ if (NOT EVENT__DISABLE_TESTS) foreach (TESTPROG ${TESTPROGS}) set(TEST_NAME ${TESTPROG}__${BACKEND_TEST_NAME}) - add_test(${TEST_NAME} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTPROG}) + + add_test(${TEST_NAME} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTPROG}) + list(APPEND TEST_NAMES ${TEST_NAME}) - set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "${ENV_VARS}") + + set_tests_properties(${TEST_NAME} + PROPERTIES ENVIRONMENT "${ENV_VARS}") endforeach() # Dump events test. if (__FOUND_USABLE_PYTHON) set(TEST_NAME test-dumpevents__${BACKEND_TEST_NAME}) - add_test(${TEST_NAME} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-dumpevents | ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/check-dumpevents.py) - set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "${ENV_VARS}") + + add_test(${TEST_NAME} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-dumpevents | + ${PYTHON_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/test/check-dumpevents.py) + + set_tests_properties(${TEST_NAME} + PROPERTIES ENVIRONMENT "${ENV_VARS}") else() message(WARNING "test-dumpevents will be run without output check since python was not found!") set(TEST_NAME test-dumpevents__${BACKEND_TEST_NAME}_no_check) - add_test(${TEST_NAME} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-dumpevents) - set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "${ENV_VARS}") + + add_test(${TEST_NAME} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-dumpevents) + + set_tests_properties(${TEST_NAME} + PROPERTIES ENVIRONMENT "${ENV_VARS}") endif() # Regress tests. if (NOT EVENT__DISABLE_REGRESS AND __FOUND_USABLE_PYTHON) set(TEST_NAME regress__${BACKEND_TEST_NAME}) - add_test(${TEST_NAME} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/regress) - set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "${ENV_VARS}") + + add_test(${TEST_NAME} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/regress) + + set_tests_properties(${TEST_NAME} + PROPERTIES ENVIRONMENT "${ENV_VARS}") + + add_test(${TEST_NAME}_debug + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/regress) + + set_tests_properties(${TEST_NAME}_debug + PROPERTIES ENVIRONMENT "${ENV_VARS};EVENT_DEBUG_MODE=1") endif() endmacro() @@ -989,9 +1147,14 @@ if (NOT EVENT__DISABLE_TESTS) # Epoll has some extra settings. if (${BACKEND} STREQUAL "EPOLL") - add_backend_test(timerfd_${BACKEND} "${BACKEND_ENV_VARS};EVENT_PRECISE_TIMER=1") - add_backend_test(changelist_${BACKEND} "${BACKEND_ENV_VARS};EVENT_EPOLL_USE_CHANGELIST=yes") - add_backend_test(timerfd_changelist_${BACKEND} "${BACKEND_ENV_VARS};EVENT_EPOLL_USE_CHANGELIST=yes;EVENT_PRECISE_TIMER=1") + add_backend_test(timerfd_${BACKEND} + "${BACKEND_ENV_VARS};EVENT_PRECISE_TIMER=1") + + add_backend_test(changelist_${BACKEND} + "${BACKEND_ENV_VARS};EVENT_EPOLL_USE_CHANGELIST=yes") + + add_backend_test(timerfd_changelist_${BACKEND} + "${BACKEND_ENV_VARS};EVENT_EPOLL_USE_CHANGELIST=yes;EVENT_PRECISE_TIMER=1") else() add_backend_test(${BACKEND} "${BACKEND_ENV_VARS}") endif() @@ -1002,16 +1165,47 @@ if (NOT EVENT__DISABLE_TESTS) # # Group limits, no connection limit. - add_test(test-ratelim__group_lim ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-ratelim -g 30000 -n 30 -t 100 --check-grouplimit 1000 --check-stddev 100) + set(RL_BIN ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-ratelim) + + add_test(test-ratelim__group_lim + ${RL_BIN} + -g 30000 + -n 30 + -t 100 + --check-grouplimit 1000 + --check-stddev 100) # Connection limit, no group limit. - add_test(test-ratelim__con_lim ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-ratelim -c 1000 -n 30 -t 100 --check-connlimit 50 --check-stddev 50) + add_test(test-ratelim__con_lim + ${RL_BIN} + -c 1000 + -n 30 + -t 100 + --check-connlimit 50 + --check-stddev 50) # Connection limit and group limit. - add_test(test-ratelim__group_con_lim ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-ratelim -c 1000 -g 30000 -n 30 -t 100 --check-grouplimit 1000 --check-connlimit 50 --check-stddev 50) + add_test(test-ratelim__group_con_lim + ${RL_BIN} + -c 1000 + -g 30000 + -n 30 + -t 100 + --check-grouplimit 1000 + --check-connlimit 50 + --check-stddev 50) # Connection limit and group limit with independent drain. - add_test(test-ratelim__group_con_lim_drain ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-ratelim -c 1000 -g 35000 -n 30 -t 100 -G 500 --check-grouplimit 1000 --check-connlimit 50 --check-stddev 50) + add_test(test-ratelim__group_con_lim_drain + ${RL_BIN} + -c 1000 + -g 35000 + -n 30 + -t 100 + -G 500 + --check-grouplimit 1000 + --check-connlimit 50 + --check-stddev 50) # Add a "make verify" target, same as for autoconf. # (Important! This will unset all EVENT_NO* environment variables. @@ -1028,12 +1222,21 @@ if (NOT EVENT__DISABLE_TESTS) \"${WINDOWS_CTEST_COMMAND}\" ") - message("${WINDOWS_CTEST_COMMAND}") - file(COPY ${CMAKE_CURRENT_BINARY_DIR}/tmp/verify_tests.bat - DESTINATION ${CMAKE_CURRENT_BINARY_DIR} - FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + message(STATUS "${WINDOWS_CTEST_COMMAND}") - file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/verify_tests.bat" VERIFY_PATH) + file(COPY ${CMAKE_CURRENT_BINARY_DIR}/tmp/verify_tests.bat + DESTINATION + ${CMAKE_CURRENT_BINARY_DIR} + FILE_PERMISSIONS + OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + + file(TO_NATIVE_PATH + "${CMAKE_CURRENT_BINARY_DIR}/verify_tests.bat" VERIFY_PATH) add_custom_target(verify COMMAND "${VERIFY_PATH}" DEPENDS event ${ALL_TESTPROGS}) @@ -1051,11 +1254,22 @@ if (NOT EVENT__DISABLE_TESTS) # Then we copy the file (this allows us to set execute permission on it) file(COPY ${CMAKE_CURRENT_BINARY_DIR}/tmp/verify_tests.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR} - FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + FILE_PERMISSIONS + OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ + WORLD_EXECUTE) # Create the target that runs the script. - add_custom_target(verify COMMAND ${CMAKE_CURRENT_BINARY_DIR}/verify_tests.sh - DEPENDS event ${ALL_TESTPROGS}) + add_custom_target(verify + COMMAND + ${CMAKE_CURRENT_BINARY_DIR}/verify_tests.sh + DEPENDS + event + ${ALL_TESTPROGS}) endif() if (NOT EVENT__DISABLE_REGRESS AND __FOUND_USABLE_PYTHON) @@ -1111,7 +1325,11 @@ export(PACKAGE libevent) set(EVENT__INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/include" "${PROJECT_BINARY_DIR}/include") -set(LIBEVENT_INCLUDE_DIRS ${EVENT__INCLUDE_DIRS} CACHE PATH "Libevent include directories") + +set(LIBEVENT_INCLUDE_DIRS + ${EVENT__INCLUDE_DIRS} + CACHE PATH "Libevent include directories") + configure_file(${PROJECT_SOURCE_DIR}/cmake/LibeventConfigBuildTree.cmake.in ${PROJECT_BINARY_DIR}/LibeventConfig.cmake @ONLY) @@ -1128,6 +1346,7 @@ file(RELATIVE_PATH # config file is located. set(EVENT__INCLUDE_DIRS "\${EVENT_CMAKE_DIR}/${REL_INCLUDE_DIR}") + configure_file(${PROJECT_SOURCE_DIR}/cmake/LibeventConfig.cmake.in ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/LibeventConfig.cmake @ONLY) @@ -1150,20 +1369,56 @@ install(TARGETS event event_core event_extra LIBRARY DESTINATION "${EVENT_INSTALL_LIB_DIR}" COMPONENT lib ARCHIVE DESTINATION "${EVENT_INSTALL_LIB_DIR}" COMPONENT lib PUBLIC_HEADER DESTINATION "${EVENT_INSTALL_INCLUDE_DIR}/event2" COMPONENT dev) + # Install compat headers install(FILES ${HDR_COMPAT} - DESTINATION "${EVENT_INSTALL_INCLUDE_DIR}" + DESTINATION + "${EVENT_INSTALL_INCLUDE_DIR}" COMPONENT dev) # Install the configs. install(FILES ${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/LibeventConfig.cmake ${PROJECT_BINARY_DIR}/LibeventConfigVersion.cmake - DESTINATION "${EVENT_INSTALL_CMAKE_DIR}" COMPONENT dev) - + DESTINATION + "${EVENT_INSTALL_CMAKE_DIR}" + COMPONENT dev) # Install exports for the install-tree. install(EXPORT LibeventTargets - DESTINATION "${EVENT_INSTALL_CMAKE_DIR}" COMPONENT dev) + DESTINATION + "${EVENT_INSTALL_CMAKE_DIR}" + COMPONENT dev) + +set(LIBEVENT_LIBRARIES + event + event_core + event_extra + CACHE STRING "Libevent libraries") + +message("") +message(" ---( Libevent " ${EVENT_VERSION} " )---") +message("") +message(STATUS "Available event backends: ${BACKENDS}") +message(STATUS "CMAKE_BINARY_DIR: " ${CMAKE_BINARY_DIR}) +message(STATUS "CMAKE_CURRENT_BINARY_DIR: " ${CMAKE_CURRENT_BINARY_DIR}) +message(STATUS "CMAKE_SOURCE_DIR: " ${CMAKE_SOURCE_DIR}) +message(STATUS "CMAKE_CURRENT_SOURCE_DIR: " ${CMAKE_CURRENT_SOURCE_DIR}) +message(STATUS "PROJECT_BINARY_DIR: " ${PROJECT_BINARY_DIR}) +message(STATUS "PROJECT_SOURCE_DIR: " ${PROJECT_SOURCE_DIR}) +message(STATUS "CMAKE_MODULE_PATH: " ${CMAKE_MODULE_PATH}) +message(STATUS "CMAKE_COMMAND: " ${CMAKE_COMMAND}) +message(STATUS "CMAKE_ROOT: " ${CMAKE_ROOT} ) +message(STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM} ) +message(STATUS "CMAKE_SYSTEM_NAME: " ${CMAKE_SYSTEM_NAME} ) +message(STATUS "CMAKE_SYSTEM_VERSION: " ${CMAKE_SYSTEM_VERSION} ) +message(STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR} ) +message(STATUS "CMAKE_SKIP_RPATH: " ${CMAKE_SKIP_RPATH} ) +message(STATUS "CMAKE_VERBOSE_MAKEFILE: " ${CMAKE_VERBOSE_MAKEFILE} ) +message(STATUS "CMAKE_C_FLAGS: " ${CMAKE_C_FLAGS} ) +message(STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE} ) +message(STATUS "CMAKE_C_COMPILER: " ${CMAKE_C_COMPILER} ) +message(STATUS "CMAKE_AR: " ${CMAKE_AR} ) +message(STATUS "CMAKE_RANLIB: " ${CMAKE_RANLIB} ) +message("") -set(LIBEVENT_LIBRARIES event event_core event_extra CACHE STRING "Libevent libraries") diff --git a/README.md b/README.md index a7c82b01..ad9652f8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,14 @@ +

+ libevent logo +

+ + + +[![Appveyor Win32 Build Status](https://ci.appveyor.com/api/projects/status/github/libevent/libevent?branch=master&svg=true)](https://ci.appveyor.com/project/nmathewson/libevent) +[![Travis Build Status](https://travis-ci.org/libevent/libevent.svg?branch=master)](https://travis-ci.org/libevent/libevent) + + + # 0. BUILDING AND INSTALLATION (Briefly) ## Autoconf @@ -7,6 +18,72 @@ $ make verify # (optional) $ sudo make install +## Cmake (General) + + +The following Libevent specific Cmake variables ar as follows (the values being +the default). + +``` +# Installation directory for executables +EVENT_INSTALL_BIN_DIR:PATH=bin + +# Installation directory for CMake files +EVENT_INSTALL_CMAKE_DIR:PATH=lib/cmake/libevent + +## Installation directory for header files +EVENT_INSTALL_INCLUDE_DIR:PATH=include + +## Installation directory for libraries +EVENT_INSTALL_LIB_DIR:PATH=lib + +## Define if libevent should be built with shared libraries instead of archives +EVENT__BUILD_SHARED_LIBRARIES:BOOL=OFF + +# Enable running gcov to get a test coverage report (only works with +# GCC/CLang). Make sure to enable -DCMAKE_BUILD_TYPE=Debug as well. +EVENT__COVERAGE:BOOL=OFF + +# Defines if libevent should build without the benchmark exectuables +EVENT__DISABLE_BENCHMARK:BOOL=OFF + +# Define if libevent should build without support for a debug mode +EVENT__DISABLE_DEBUG_MODE:BOOL=OFF + +# Define if libevent should not allow replacing the mm functions +EVENT__DISABLE_MM_REPLACEMENT:BOOL=OFF + +# Define if libevent should build without support for OpenSSL encrpytion +EVENT__DISABLE_OPENSSL:BOOL=ON + +# Disable the regress tests +EVENT__DISABLE_REGRESS:BOOL=OFF + +# Disable sample files +EVENT__DISABLE_SAMPLES:BOOL=OFF + +# If tests should be compiled or not +EVENT__DISABLE_TESTS:BOOL=OFF + +# Define if libevent should not be compiled with thread support +EVENT__DISABLE_THREAD_SUPPORT:BOOL=OFF + +# Enables verbose debugging +EVENT__ENABLE_VERBOSE_DEBUG:BOOL=OFF + +# When crosscompiling forces running a test program that verifies that Kqueue +# works with pipes. Note that this requires you to manually run the test program +# on the the cross compilation target to verify that it works. See cmake +# documentation for try_run for more details +EVENT__FORCE_KQUEUE_CHECK:BOOL=OFF + +# set EVENT_STAGE_VERSION +EVENT__STAGE_VERSION:STRING=beta +``` + +__More variables can be found by running `cmake -LAH `__ + + ## CMake (Windows) Install CMake: diff --git a/appveyor.yml b/appveyor.yml index 71e91aa1..f9af62d2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,11 +1,45 @@ +version: 2.1.5.{build} +shallow_clone: true + +os: Visual Studio 2015 RC + +build: + verbosity: detailed + +environment: + global: + CYG_ROOT: C:/MinGW/msys/1.0 + +init: + - 'echo Building libevent %version% for Windows' + - 'echo System architecture: %PLATFORM%' + - 'echo Repo build branch is: %APPVEYOR_REPO_BRANCH%' + - 'echo Build folder is: %APPVEYOR_BUILD_FOLDER%' + install: - - appveyor DownloadFile http://slproweb.com/download/Win32OpenSSL-1_0_1L.exe - - Win32OpenSSL-1_0_1L.exe /silent /verysilent /sp- /suppressmsgboxes + - set PATH=%PATH%;C:\MinGW\msys\1.0\bin;C:\MinGW\bin + - appveyor DownloadFile https://strcpy.net/packages/Win32OpenSSL-1_0_2a.exe + - Win32OpenSSL-1_0_2a.exe /silent /verysilent /sp- /suppressmsgboxes + build_script: - - md build - - cd build - - cmake .. - - cmake --build . - - ctest --output-on-failure + - cmd: 'echo Cygwin root is: %CYG_ROOT%' + - cmd: 'echo Build folder is: %APPVEYOR_BUILD_FOLDER%' + - cmd: 'echo Repo build branch is: %APPVEYOR_REPO_BRANCH%' + - cmd: 'echo Repo build commit is: %APPVEYOR_REPO_COMMIT%' + - cmd: "echo installing stuff" + - cmd: 'echo "C:\MinGW /mingw" >%CYG_ROOT%/etc/fstab' + - cmd: 'C:\MinGW\bin\mingw-get install autotools autoconf automake python' + - cmd: 'echo Autogen running...' + - cmd: '%CYG_ROOT%/bin/bash -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0buffer = EVBUFFER_CHAIN_EXTRA(u_char, chain); + chain->buffer = EVBUFFER_CHAIN_EXTRA(unsigned char, chain); chain->refcnt = 1; @@ -2909,7 +2909,7 @@ evbuffer_add_reference(struct evbuffer *outbuf, if (!chain) return (-1); chain->flags |= EVBUFFER_REFERENCE | EVBUFFER_IMMUTABLE; - chain->buffer = (u_char *)data; + chain->buffer = (unsigned char *)data; chain->buffer_len = datlen; chain->off = datlen; diff --git a/bufferevent-internal.h b/bufferevent-internal.h index 134bb337..d9d9e666 100644 --- a/bufferevent-internal.h +++ b/bufferevent-internal.h @@ -40,6 +40,17 @@ extern "C" { #include "ratelim-internal.h" #include "event2/bufferevent_struct.h" +#include "ipv6-internal.h" +#ifdef _WIN32 +#include +#endif +#ifdef EVENT__HAVE_NETINET_IN_H +#include +#endif +#ifdef EVENT__HAVE_NETINET_IN6_H +#include +#endif + /* These flags are reasons that we might be declining to actually enable reading or writing on a bufferevent. */ @@ -205,6 +216,18 @@ struct bufferevent_private { /** Rate-limiting information for this bufferevent */ struct bufferevent_rate_limit *rate_limiting; + + /* Saved conn_addr, to extract IP address from it. + * + * Because some servers may reset/close connection without waiting clients, + * in that case we can't extract IP address even in close_cb. + * So we need to save it, just after we connected to remote server, or + * after resolving (to avoid extra dns requests during retrying, since UDP + * is slow) */ + union { + struct sockaddr_in6 in6; + struct sockaddr_in in; + } conn_address; }; /** Possible operations for a control callback. */ @@ -327,14 +350,17 @@ int bufferevent_disable_hard_(struct bufferevent *bufev, short event); /** Internal: Set up locking on a bufferevent. If lock is set, use it. * Otherwise, use a new lock. */ int bufferevent_enable_locking_(struct bufferevent *bufev, void *lock); -/** Internal: Increment the reference count on bufev. */ -void bufferevent_incref_(struct bufferevent *bufev); +/** Internal: backwards compat macro for the now public function + * Increment the reference count on bufev. */ +#define bufferevent_incref_(bufev) bufferevent_incref(bufev) /** Internal: Lock bufev and increase its reference count. * unlocking it otherwise. */ void bufferevent_incref_and_lock_(struct bufferevent *bufev); -/** Internal: Decrement the reference count on bufev. Returns 1 if it freed +/** Internal: backwards compat macro for the now public function + * Decrement the reference count on bufev. Returns 1 if it freed * the bufferevent.*/ -int bufferevent_decref_(struct bufferevent *bufev); +#define bufferevent_decref_(bufev) bufferevent_decref(bufev) + /** Internal: Drop the reference count on bufev, freeing as necessary, and * unlocking it otherwise. Returns 1 if it freed the bufferevent. */ int bufferevent_decref_and_unlock_(struct bufferevent *bufev); @@ -386,9 +412,13 @@ void bufferevent_init_generic_timeout_cbs_(struct bufferevent *bev); * we delete it.) Call this from anything that changes the timeout values, * that enabled EV_READ or EV_WRITE, or that disables EV_READ or EV_WRITE. */ int bufferevent_generic_adj_timeouts_(struct bufferevent *bev); +int bufferevent_generic_adj_existing_timeouts_(struct bufferevent *bev); enum bufferevent_options bufferevent_get_options_(struct bufferevent *bev); +const struct sockaddr* +bufferevent_socket_get_conn_address_(struct bufferevent *bev); + /** Internal use: We have just successfully read data into an inbuf, so * reset the read timeout (if any). */ #define BEV_RESET_GENERIC_READ_TIMEOUT(bev) \ diff --git a/bufferevent.c b/bufferevent.c index d298d0b3..59ae24f1 100644 --- a/bufferevent.c +++ b/bufferevent.c @@ -777,7 +777,7 @@ bufferevent_finalize_cb_(struct event_callback *evcb, void *arg_) } int -bufferevent_decref_(struct bufferevent *bufev) +bufferevent_decref(struct bufferevent *bufev) { BEV_LOCK(bufev); return bufferevent_decref_and_unlock_(bufev); @@ -793,11 +793,15 @@ bufferevent_free(struct bufferevent *bufev) } void -bufferevent_incref_(struct bufferevent *bufev) +bufferevent_incref(struct bufferevent *bufev) { struct bufferevent_private *bufev_private = EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); + /* XXX: now that this function is public, we might want to + * - return the count from this function + * - create a new function to atomically grab the current refcount + */ BEV_LOCK(bufev); ++bufev_private->refcnt; BEV_UNLOCK(bufev); @@ -965,10 +969,33 @@ bufferevent_generic_adj_timeouts_(struct bufferevent *bev) return 0; } +int +bufferevent_generic_adj_existing_timeouts_(struct bufferevent *bev) +{ + int r = 0; + if (event_pending(&bev->ev_read, EV_READ, NULL)) { + if (evutil_timerisset(&bev->timeout_read)) { + if (bufferevent_add_event_(&bev->ev_read, &bev->timeout_read) < 0) + r = -1; + } else { + event_remove_timer(&bev->ev_read); + } + } + if (event_pending(&bev->ev_write, EV_WRITE, NULL)) { + if (evutil_timerisset(&bev->timeout_write)) { + if (bufferevent_add_event_(&bev->ev_write, &bev->timeout_write) < 0) + r = -1; + } else { + event_remove_timer(&bev->ev_write); + } + } + return r; +} + int bufferevent_add_event_(struct event *ev, const struct timeval *tv) { - if (tv->tv_sec == 0 && tv->tv_usec == 0) + if (!evutil_timerisset(tv)) return event_add(ev, NULL); else return event_add(ev, tv); diff --git a/bufferevent_filter.c b/bufferevent_filter.c index 4d9be43e..6c3ffc4f 100644 --- a/bufferevent_filter.c +++ b/bufferevent_filter.c @@ -536,10 +536,20 @@ be_filter_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op, bevf = upcast(bev); data->ptr = bevf->underlying; return 0; - case BEV_CTRL_GET_FD: case BEV_CTRL_SET_FD: + bevf = upcast(bev); + + if (bevf->underlying && + bevf->underlying->be_ops && + bevf->underlying->be_ops->ctrl) { + return (bevf->underlying->be_ops->ctrl)(bevf->underlying, op, data); + } + + case BEV_CTRL_GET_FD: case BEV_CTRL_CANCEL_ALL: default: return -1; } + + return -1; } diff --git a/bufferevent_openssl.c b/bufferevent_openssl.c index b30f90ff..37478b6a 100644 --- a/bufferevent_openssl.c +++ b/bufferevent_openssl.c @@ -320,8 +320,6 @@ struct bufferevent_openssl { unsigned write_blocked_on_read : 1; /* Treat TCP close before SSL close on SSL >= v3 as clean EOF. */ unsigned allow_dirty_shutdown : 1; - /* XXXX */ - unsigned fd_is_set : 1; /* XXX */ unsigned n_errors : 2; @@ -957,6 +955,18 @@ be_openssl_writeeventcb(evutil_socket_t fd, short what, void *ptr) bufferevent_decref_and_unlock_(&bev_ssl->bev.bev); } +static int +be_openssl_auto_fd(struct bufferevent_openssl *bev_ssl, int fd) +{ + if (!bev_ssl->underlying) { + struct bufferevent *bev = &bev_ssl->bev.bev; + if (event_initialized(&bev->ev_read) && fd < 0) { + fd = event_get_fd(&bev->ev_read); + } + } + return fd; +} + static int set_open_callbacks(struct bufferevent_openssl *bev_ssl, evutil_socket_t fd) { @@ -968,30 +978,36 @@ set_open_callbacks(struct bufferevent_openssl *bev_ssl, evutil_socket_t fd) } else { struct bufferevent *bev = &bev_ssl->bev.bev; int rpending=0, wpending=0, r1=0, r2=0; - if (fd < 0 && bev_ssl->fd_is_set) - fd = event_get_fd(&bev->ev_read); - if (bev_ssl->fd_is_set) { + + if (event_initialized(&bev->ev_read)) { rpending = event_pending(&bev->ev_read, EV_READ, NULL); wpending = event_pending(&bev->ev_write, EV_WRITE, NULL); + event_del(&bev->ev_read); event_del(&bev->ev_write); } + event_assign(&bev->ev_read, bev->ev_base, fd, EV_READ|EV_PERSIST|EV_FINALIZE, be_openssl_readeventcb, bev_ssl); event_assign(&bev->ev_write, bev->ev_base, fd, EV_WRITE|EV_PERSIST|EV_FINALIZE, be_openssl_writeeventcb, bev_ssl); + if (rpending) r1 = bufferevent_add_event_(&bev->ev_read, &bev->timeout_read); if (wpending) r2 = bufferevent_add_event_(&bev->ev_write, &bev->timeout_write); - if (fd >= 0) { - bev_ssl->fd_is_set = 1; - } + return (r1<0 || r2<0) ? -1 : 0; } } +static int +set_open_callbacks_auto(struct bufferevent_openssl *bev_ssl, evutil_socket_t fd) +{ + fd = be_openssl_auto_fd(bev_ssl, fd); + return set_open_callbacks(bev_ssl, fd); +} static int do_handshake(struct bufferevent_openssl *bev_ssl) @@ -1011,9 +1027,10 @@ do_handshake(struct bufferevent_openssl *bev_ssl) decrement_buckets(bev_ssl); if (r==1) { + int fd = event_get_fd(&bev_ssl->bev.bev.ev_read); /* We're done! */ bev_ssl->state = BUFFEREVENT_SSL_OPEN; - set_open_callbacks(bev_ssl, -1); /* XXXX handle failure */ + set_open_callbacks(bev_ssl, fd); /* XXXX handle failure */ /* Call do_read and do_write as needed */ bufferevent_enable(&bev_ssl->bev.bev, bev_ssl->bev.bev.enabled); bufferevent_run_eventcb_(&bev_ssl->bev.bev, @@ -1073,28 +1090,31 @@ set_handshake_callbacks(struct bufferevent_openssl *bev_ssl, evutil_socket_t fd) return do_handshake(bev_ssl); } else { struct bufferevent *bev = &bev_ssl->bev.bev; - int r1=0, r2=0; - if (fd < 0 && bev_ssl->fd_is_set) - fd = event_get_fd(&bev->ev_read); - if (bev_ssl->fd_is_set) { + + if (event_initialized(&bev->ev_read)) { event_del(&bev->ev_read); event_del(&bev->ev_write); } + event_assign(&bev->ev_read, bev->ev_base, fd, EV_READ|EV_PERSIST|EV_FINALIZE, be_openssl_handshakeeventcb, bev_ssl); event_assign(&bev->ev_write, bev->ev_base, fd, EV_WRITE|EV_PERSIST|EV_FINALIZE, be_openssl_handshakeeventcb, bev_ssl); - if (fd >= 0) { - r1 = bufferevent_add_event_(&bev->ev_read, &bev->timeout_read); - r2 = bufferevent_add_event_(&bev->ev_write, &bev->timeout_write); - bev_ssl->fd_is_set = 1; - } - return (r1<0 || r2<0) ? -1 : 0; + if (fd >= 0) + bufferevent_enable(bev, bev->enabled); + return 0; } } +static int +set_handshake_callbacks_auto(struct bufferevent_openssl *bev_ssl, evutil_socket_t fd) +{ + fd = be_openssl_auto_fd(bev_ssl, fd); + return set_handshake_callbacks(bev_ssl, fd); +} + int bufferevent_ssl_renegotiate(struct bufferevent *bev) { @@ -1104,7 +1124,7 @@ bufferevent_ssl_renegotiate(struct bufferevent *bev) if (SSL_renegotiate(bev_ssl->ssl) < 0) return -1; bev_ssl->state = BUFFEREVENT_SSL_CONNECTING; - if (set_handshake_callbacks(bev_ssl, -1) < 0) + if (set_handshake_callbacks_auto(bev_ssl, -1) < 0) return -1; if (!bev_ssl->underlying) return do_handshake(bev_ssl); @@ -1119,14 +1139,13 @@ be_openssl_outbuf_cb(struct evbuffer *buf, int r = 0; /* XXX need to hold a reference here. */ - if (cbinfo->n_added && bev_ssl->state == BUFFEREVENT_SSL_OPEN) { - if (cbinfo->orig_size == 0) - r = bufferevent_add_event_(&bev_ssl->bev.bev.ev_write, - &bev_ssl->bev.bev.timeout_write); - consider_writing(bev_ssl); + if (cbinfo->n_added && bev_ssl->state == BUFFEREVENT_SSL_OPEN && + cbinfo->orig_size == 0) { + r = bufferevent_add_event_(&bev_ssl->bev.bev.ev_write, + &bev_ssl->bev.bev.timeout_write); } /* XXX Handle r < 0 */ - (void)r; + (void)r; } @@ -1136,9 +1155,6 @@ be_openssl_enable(struct bufferevent *bev, short events) struct bufferevent_openssl *bev_ssl = upcast(bev); int r1 = 0, r2 = 0; - if (bev_ssl->state != BUFFEREVENT_SSL_OPEN) - return 0; - if (events & EV_READ) r1 = start_reading(bev_ssl); if (events & EV_WRITE) @@ -1162,8 +1178,6 @@ static int be_openssl_disable(struct bufferevent *bev, short events) { struct bufferevent_openssl *bev_ssl = upcast(bev); - if (bev_ssl->state != BUFFEREVENT_SSL_OPEN) - return 0; if (events & EV_READ) stop_reading(bev_ssl); @@ -1233,23 +1247,7 @@ be_openssl_adj_timeouts(struct bufferevent *bev) if (bev_ssl->underlying) { return bufferevent_generic_adj_timeouts_(bev); } else { - int r1=0, r2=0; - if (event_pending(&bev->ev_read, EV_READ, NULL)) { - if (evutil_timerisset(&bev->timeout_read)) { - r1 = bufferevent_add_event_(&bev->ev_read, &bev->timeout_read); - } else { - event_remove_timer(&bev->ev_read); - } - } - if (event_pending(&bev->ev_write, EV_WRITE, NULL)) { - if (evutil_timerisset(&bev->timeout_write)) { - r2 = bufferevent_add_event_(&bev->ev_write, &bev->timeout_write); - } else { - event_remove_timer(&bev->ev_write); - } - } - - return (r1<0 || r2<0) ? -1 : 0; + return bufferevent_generic_adj_existing_timeouts_(bev); } } @@ -1274,25 +1272,16 @@ be_openssl_ctrl(struct bufferevent *bev, BIO *bio; bio = BIO_new_socket(data->fd, 0); SSL_set_bio(bev_ssl->ssl, bio, bio); - bev_ssl->fd_is_set = 1; } - if (data->fd == -1) - bev_ssl->fd_is_set = 0; - if (bev_ssl->state == BUFFEREVENT_SSL_OPEN) + if (bev_ssl->state == BUFFEREVENT_SSL_OPEN && data->fd >= 0) return set_open_callbacks(bev_ssl, data->fd); else { return set_handshake_callbacks(bev_ssl, data->fd); } case BEV_CTRL_GET_FD: - if (bev_ssl->underlying) - return -1; - if (!bev_ssl->fd_is_set) - return -1; data->fd = event_get_fd(&bev->ev_read); return 0; case BEV_CTRL_GET_UNDERLYING: - if (!bev_ssl->underlying) - return -1; data->ptr = bev_ssl->underlying; return 0; case BEV_CTRL_CANCEL_ALL: @@ -1360,16 +1349,16 @@ bufferevent_openssl_new_impl(struct event_base *base, switch (state) { case BUFFEREVENT_SSL_ACCEPTING: SSL_set_accept_state(bev_ssl->ssl); - if (set_handshake_callbacks(bev_ssl, fd) < 0) + if (set_handshake_callbacks_auto(bev_ssl, fd) < 0) goto err; break; case BUFFEREVENT_SSL_CONNECTING: SSL_set_connect_state(bev_ssl->ssl); - if (set_handshake_callbacks(bev_ssl, fd) < 0) + if (set_handshake_callbacks_auto(bev_ssl, fd) < 0) goto err; break; case BUFFEREVENT_SSL_OPEN: - if (set_open_callbacks(bev_ssl, fd) < 0) + if (set_open_callbacks_auto(bev_ssl, fd) < 0) goto err; break; default: @@ -1382,15 +1371,6 @@ bufferevent_openssl_new_impl(struct event_base *base, if (state == BUFFEREVENT_SSL_OPEN) bufferevent_suspend_read_(underlying, BEV_SUSPEND_FILT_READ); - } else { - bev_ssl->bev.bev.enabled = EV_READ|EV_WRITE; - if (bev_ssl->fd_is_set) { - if (state != BUFFEREVENT_SSL_OPEN) - if (event_add(&bev_ssl->bev.bev.ev_read, NULL) < 0) - goto err; - if (event_add(&bev_ssl->bev.bev.ev_write, NULL) < 0) - goto err; - } } return &bev_ssl->bev.bev; diff --git a/bufferevent_pair.c b/bufferevent_pair.c index 8154e17b..d80e5f81 100644 --- a/bufferevent_pair.c +++ b/bufferevent_pair.c @@ -307,15 +307,17 @@ be_pair_flush(struct bufferevent *bev, short iotype, { struct bufferevent_pair *bev_p = upcast(bev); struct bufferevent *partner; - incref_and_lock(bev); + if (!bev_p->partner) return -1; - partner = downcast(bev_p->partner); - if (mode == BEV_NORMAL) return 0; + incref_and_lock(bev); + + partner = downcast(bev_p->partner); + if ((iotype & EV_READ) != 0) be_pair_transfer(partner, bev, 1); diff --git a/bufferevent_sock.c b/bufferevent_sock.c index 49ebc0be..a2b381ac 100644 --- a/bufferevent_sock.c +++ b/bufferevent_sock.c @@ -79,7 +79,6 @@ static int be_socket_enable(struct bufferevent *, short); static int be_socket_disable(struct bufferevent *, short); static void be_socket_destruct(struct bufferevent *); -static int be_socket_adj_timeouts(struct bufferevent *); static int be_socket_flush(struct bufferevent *, short, enum bufferevent_flush_mode); static int be_socket_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *); @@ -92,13 +91,35 @@ const struct bufferevent_ops bufferevent_ops_socket = { be_socket_disable, NULL, /* unlink */ be_socket_destruct, - be_socket_adj_timeouts, + bufferevent_generic_adj_existing_timeouts_, be_socket_flush, be_socket_ctrl, }; -#define be_socket_add(ev, t) \ - bufferevent_add_event_((ev), (t)) +const struct sockaddr* +bufferevent_socket_get_conn_address_(struct bufferevent *bev) +{ + struct bufferevent_private *bev_p = + EVUTIL_UPCAST(bev, struct bufferevent_private, bev); + + return (struct sockaddr *)&bev_p->conn_address; +} +static void +bufferevent_socket_set_conn_address_fd(struct bufferevent_private *bev_p, int fd) +{ + socklen_t len = sizeof(bev_p->conn_address); + + struct sockaddr *addr = (struct sockaddr *)&bev_p->conn_address; + if (addr->sa_family != AF_UNSPEC) + getpeername(fd, addr, &len); +} +static void +bufferevent_socket_set_conn_address(struct bufferevent_private *bev_p, + struct sockaddr *addr, size_t addrlen) +{ + EVUTIL_ASSERT(addrlen <= sizeof(bev_p->conn_address)); + memcpy(&bev_p->conn_address, addr, addrlen); +} static void bufferevent_socket_outbuf_cb(struct evbuffer *buf, @@ -115,7 +136,7 @@ bufferevent_socket_outbuf_cb(struct evbuffer *buf, !bufev_p->write_suspended) { /* Somebody added data to the buffer, and we would like to * write, and we were not writing. So, start writing. */ - if (be_socket_add(&bufev->ev_write, &bufev->timeout_write) == -1) { + if (bufferevent_add_event_(&bufev->ev_write, &bufev->timeout_write) == -1) { /* Should we log this? */ } } @@ -239,6 +260,7 @@ bufferevent_writecb(evutil_socket_t fd, short event, void *arg) goto done; } else { connected = 1; + bufferevent_socket_set_conn_address_fd(bufev_p, fd); #ifdef _WIN32 if (BEV_IS_ASYNC(bufev)) { event_del(&bufev->ev_write); @@ -351,7 +373,7 @@ bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int bufferevent_socket_connect(struct bufferevent *bev, - struct sockaddr *sa, int socklen) + const struct sockaddr *sa, int socklen) { struct bufferevent_private *bufev_p = EVUTIL_UPCAST(bev, struct bufferevent_private, bev); @@ -457,6 +479,7 @@ bufferevent_connect_getaddrinfo_cb(int result, struct evutil_addrinfo *ai, /* XXX use the other addrinfos? */ /* XXX use this return value */ + bufferevent_socket_set_conn_address(bev_p, ai->ai_addr, (int)ai->ai_addrlen); r = bufferevent_socket_connect(bev, ai->ai_addr, (int)ai->ai_addrlen); (void)r; bufferevent_decref_and_unlock_(bev); @@ -478,23 +501,23 @@ bufferevent_socket_connect_hostname(struct bufferevent *bev, if (port < 1 || port > 65535) return -1; - BEV_LOCK(bev); - bev_p->dns_error = 0; - BEV_UNLOCK(bev); - - evutil_snprintf(portbuf, sizeof(portbuf), "%d", port); - memset(&hint, 0, sizeof(hint)); hint.ai_family = family; hint.ai_protocol = IPPROTO_TCP; hint.ai_socktype = SOCK_STREAM; + evutil_snprintf(portbuf, sizeof(portbuf), "%d", port); + + BEV_LOCK(bev); + bev_p->dns_error = 0; + bufferevent_suspend_write_(bev, BEV_SUSPEND_LOOKUP); bufferevent_suspend_read_(bev, BEV_SUSPEND_LOOKUP); bufferevent_incref_(bev); err = evutil_getaddrinfo_async_(evdns_base, hostname, portbuf, &hint, bufferevent_connect_getaddrinfo_cb, bev); + BEV_UNLOCK(bev); if (err == 0) { return 0; @@ -550,14 +573,12 @@ bufferevent_new(evutil_socket_t fd, static int be_socket_enable(struct bufferevent *bufev, short event) { - if (event & EV_READ) { - if (be_socket_add(&bufev->ev_read,&bufev->timeout_read) == -1) + if (event & EV_READ && + bufferevent_add_event_(&bufev->ev_read, &bufev->timeout_read) == -1) return -1; - } - if (event & EV_WRITE) { - if (be_socket_add(&bufev->ev_write,&bufev->timeout_write) == -1) + if (event & EV_WRITE && + bufferevent_add_event_(&bufev->ev_write, &bufev->timeout_write) == -1) return -1; - } return 0; } @@ -592,29 +613,6 @@ be_socket_destruct(struct bufferevent *bufev) EVUTIL_CLOSESOCKET(fd); } -static int -be_socket_adj_timeouts(struct bufferevent *bufev) -{ - int r = 0; - if (event_pending(&bufev->ev_read, EV_READ, NULL)) { - if (evutil_timerisset(&bufev->timeout_read)) { - if (be_socket_add(&bufev->ev_read, &bufev->timeout_read) < 0) - r = -1; - } else { - event_remove_timer(&bufev->ev_read); - } - } - if (event_pending(&bufev->ev_write, EV_WRITE, NULL)) { - if (evutil_timerisset(&bufev->timeout_write)) { - if (be_socket_add(&bufev->ev_write, &bufev->timeout_write) < 0) - r = -1; - } else { - event_remove_timer(&bufev->ev_write); - } - } - return r; -} - static int be_socket_flush(struct bufferevent *bev, short iotype, enum bufferevent_flush_mode mode) diff --git a/cmake/AddCompilerFlags.cmake b/cmake/AddCompilerFlags.cmake new file mode 100644 index 00000000..c7da188b --- /dev/null +++ b/cmake/AddCompilerFlags.cmake @@ -0,0 +1,15 @@ +include(CheckCCompilerFlag) + +macro(add_compiler_flags _flags) + foreach(flag ${_flags}) + string(REGEX REPLACE "[-.+/:= ]" "_" _flag_esc "${flag}") + + check_c_compiler_flag("${flag}" check_c_compiler_flag_${_flag_esc}) + + if (check_c_compiler_flag_${_flag_esc}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}") + endif() + endforeach() +endmacro() + + diff --git a/cmake/CheckFunctionKeywords.cmake b/cmake/CheckFunctionKeywords.cmake new file mode 100644 index 00000000..3d968b8a --- /dev/null +++ b/cmake/CheckFunctionKeywords.cmake @@ -0,0 +1,14 @@ +include(CheckCSourceCompiles) + +macro(check_function_keywords _wordlist) + set(${_result} "") + foreach(flag ${_wordlist}) + string(REGEX REPLACE "[-+/ ()]" "_" flagname "${flag}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${flagname}") + check_c_source_compiles("${flag} void func(); void func() { } int main() { func(); return 0; }" ${have_flag}) + if(${have_flag} AND NOT ${_result}) + set(${_result} "${flag}") + endif(${have_flag} AND NOT ${_result}) + endforeach(flag) +endmacro(check_function_keywords) diff --git a/cmake/FindGit.cmake b/cmake/FindGit.cmake new file mode 100644 index 00000000..2abbfe4e --- /dev/null +++ b/cmake/FindGit.cmake @@ -0,0 +1,45 @@ +# The module defines the following variables: +# GIT_EXECUTABLE - path to git command line client +# GIT_FOUND - true if the command line client was found +# Example usage: +# find_package(Git) +# if(GIT_FOUND) +# message("git found: ${GIT_EXECUTABLE}") +# endif() + +#============================================================================= +# Copyright 2010 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distributed this file outside of CMake, substitute the full +# License text for the above reference.) + +# Look for 'git' or 'eg' (easy git) +set(git_names git eg) + +# Prefer .cmd variants on Windows unless running in a Makefile +# in the MSYS shell. +if(WIN32) + if(NOT CMAKE_GENERATOR MATCHES "MSYS") + set(git_names git.cmd git eg.cmd eg) + endif() +endif() + +find_program(GIT_EXECUTABLE + NAMES ${git_names} + DOC "git command line client") + +mark_as_advanced(GIT_EXECUTABLE) + +# Handle the QUIETLY and REQUIRED arguments and set GIT_FOUND to TRUE if +# all listed variables are TRUE + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Git DEFAULT_MSG GIT_EXECUTABLE) + diff --git a/cmake/VersionViaGit.cmake b/cmake/VersionViaGit.cmake new file mode 100644 index 00000000..f183a647 --- /dev/null +++ b/cmake/VersionViaGit.cmake @@ -0,0 +1,53 @@ +# This module defines the following variables utilizing +# git to determine the parent tag. And if found the macro +# will attempt to parse them in the github tag fomat +# +# Usful for auto-versionin in ou CMakeLists +# +# EVENT_GIT___VERSION_FOUND - Version variables foud +# EVENT_GIT___VERSION_MAJOR - Major version. +# EVENT_GIT___VERSION_MINOR - Minor version +# EVENT_GIT___VERSION_STAGE - Stage version +# +# Example usage: +# +# event_fuzzy_version_from_git() +# if (EVENT_GIT___VERSION_FOUND) +# message("Libvent major=${EVENT_GIT___VERSION_MAJOR}") +# message(" minor=${EVENT_GIT___VERSION_MINOR}") +# message(" patch=${EVENT_GIT___VERSION_PATCH}") +# message(" stage=${EVENT_GIT___VERSION_STAGE}") +# endif() + +include(FindGit) + +macro(event_fuzzy_version_from_git) + set(EVENT_GIT___VERSION_FOUND FALSE) + + # set our defaults. + set(EVENT_GIT___VERSION_MAJOR 2) + set(EVENT_GIT___VERSION_MINOR 1) + set(EVENT_GIT___VERSION_PATCH 5) + set(EVENT_GIT___VERSION_STAGE "beta") + + find_package(Git) + + if (GIT_FOUND) + execute_process( + COMMAND + ${GIT_EXECUTABLE} describe --abbrev=0 + WORKING_DIRECTORY + ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE + GITRET + OUTPUT_VARIABLE + GITVERSION) + + if (GITRET EQUAL 0) + string(REGEX REPLACE "^release-([0-9]+)\\.([0-9]+)\\.([0-9]+)-(.*)" "\\1" EVENT_GIT___VERSION_MAJOR ${GITVERSION}) + string(REGEX REPLACE "^release-([0-9]+)\\.([0-9]+)\\.([0-9]+)-(.*)" "\\2" EVENT_GIT___VERSION_MINOR ${GITVERSION}) + string(REGEX REPLACE "^release-([0-9]+)\\.([0-9]+)\\.([0-9]+)-(.*)" "\\3" EVENT_GIT___VERSION_PATCH ${GITVERSION}) + string(REGEX REPLACE "^release-([0-9]+)\\.([0-9]+)\\.([0-9]+)-([aA-zZ]+)" "\\4" EVENT_GIT___VERSION_STAGE ${GITVERSION}) + endif() + endif() +endmacro() diff --git a/configure.ac b/configure.ac index 0da4fef9..6a669fb7 100644 --- a/configure.ac +++ b/configure.ac @@ -369,6 +369,7 @@ AC_CHECK_FUNCS([ \ unsetenv \ usleep \ vasprintf \ + getservbyname \ ]) AM_CONDITIONAL(STRLCPY_IMPL, [test x"$ac_cv_func_strlcpy" = xno]) @@ -394,7 +395,6 @@ if test "$libevent_cv_getaddrinfo" = "yes" ; then AC_DEFINE([HAVE_GETADDRINFO], [1], [Do we have getaddrinfo()?]) else -AC_CHECK_FUNCS([getservbyname]) # Check for gethostbyname_r in all its glorious incompatible versions. # (This is cut-and-pasted from Tor, which based its logic on # Python's configure.in.) @@ -763,6 +763,10 @@ fi # check if we have and should use openssl AM_CONDITIONAL(OPENSSL, [test "$enable_openssl" != "no" && test "$have_openssl" = "yes"]) +if test "x$enable_openssl" = "xyes"; then + AC_SEARCH_LIBS([ERR_remove_thread_state], [crypto], + [AC_DEFINE(HAVE_ERR_REMOVE_THREAD_STATE, 1, [Define to 1 if you have ERR_remove_thread_stat().])]) +fi # Add some more warnings which we use in development but not in the # released versions. (Some relevant gcc versions can't handle these.) diff --git a/epoll.c b/epoll.c index aa41f84e..bf730b23 100644 --- a/epoll.c +++ b/epoll.c @@ -246,6 +246,23 @@ epoll_op_to_string(int op) "???"; } +#define PRINT_CHANGES(op, events, ch, status) \ + "Epoll %s(%d) on fd %d " status ". " \ + "Old events were %d; " \ + "read change was %d (%s); " \ + "write change was %d (%s); " \ + "close change was %d (%s)", \ + epoll_op_to_string(op), \ + events, \ + ch->fd, \ + ch->old_events, \ + ch->read_change, \ + change_to_string(ch->read_change), \ + ch->write_change, \ + change_to_string(ch->write_change), \ + ch->close_change, \ + change_to_string(ch->close_change) + static int epoll_apply_one_change(struct event_base *base, struct epollop *epollop, @@ -271,14 +288,7 @@ epoll_apply_one_change(struct event_base *base, epev.data.fd = ch->fd; epev.events = events; if (epoll_ctl(epollop->epfd, op, ch->fd, &epev) == 0) { - event_debug(("Epoll %s(%d) on fd %d okay. [old events were %d; read change was %d; write change was %d; close change was %d]", - epoll_op_to_string(op), - (int)epev.events, - (int)ch->fd, - ch->old_events, - ch->read_change, - ch->write_change, - ch->close_change)); + event_debug((PRINT_CHANGES(op, epev.events, ch, "okay"))); return 0; } @@ -338,18 +348,7 @@ epoll_apply_one_change(struct event_base *base, break; } - event_warn("Epoll %s(%d) on fd %d failed. Old events were %d; read change was %d (%s); write change was %d (%s); close change was %d (%s)", - epoll_op_to_string(op), - (int)epev.events, - ch->fd, - ch->old_events, - ch->read_change, - change_to_string(ch->read_change), - ch->write_change, - change_to_string(ch->write_change), - ch->close_change, - change_to_string(ch->close_change)); - + event_warn(PRINT_CHANGES(op, epev.events, ch, "failed")); return -1; } diff --git a/evdns.c b/evdns.c index 60f4db5c..0955a289 100644 --- a/evdns.c +++ b/evdns.c @@ -1060,24 +1060,6 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) { sizeof(tmp_name))<0) \ goto err; \ } while (0) -#define TEST_NAME \ - do { tmp_name[0] = '\0'; \ - cmp_name[0] = '\0'; \ - k = j; \ - if (name_parse(packet, length, &j, tmp_name, \ - sizeof(tmp_name))<0) \ - goto err; \ - if (name_parse(req->request, req->request_len, &k, \ - cmp_name, sizeof(cmp_name))<0) \ - goto err; \ - if (base->global_randomize_case) { \ - if (strcmp(tmp_name, cmp_name) == 0) \ - name_matches = 1; \ - } else { \ - if (evutil_ascii_strcasecmp(tmp_name, cmp_name) == 0) \ - name_matches = 1; \ - } \ - } while (0) reply.type = req->request_type; @@ -1086,9 +1068,25 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) { /* the question looks like * */ - TEST_NAME; + tmp_name[0] = '\0'; + cmp_name[0] = '\0'; + k = j; + if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name)) < 0) + goto err; + if (name_parse(req->request, req->request_len, &k, + cmp_name, sizeof(cmp_name))<0) + goto err; + if (!base->global_randomize_case) { + if (strcmp(tmp_name, cmp_name) == 0) + name_matches = 1; + } else { + if (evutil_ascii_strcasecmp(tmp_name, cmp_name) == 0) + name_matches = 1; + } + j += 4; - if (j > length) goto err; + if (j > length) + goto err; } if (!name_matches) @@ -4034,15 +4032,6 @@ evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests) /* TODO(nickm) we might need to refcount here. */ - for (server = base->server_head; server; server = server_next) { - server_next = server->next; - evdns_nameserver_free(server); - if (server_next == base->server_head) - break; - } - base->server_head = NULL; - base->global_good_nameservers = 0; - for (i = 0; i < base->n_req_heads; ++i) { while (base->req_heads[i]) { if (fail_requests) @@ -4057,6 +4046,14 @@ evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests) } base->global_requests_inflight = base->global_requests_waiting = 0; + for (server = base->server_head; server; server = server_next) { + server_next = server->next; + evdns_nameserver_free(server); + if (server_next == base->server_head) + break; + } + base->server_head = NULL; + base->global_good_nameservers = 0; if (base->global_search_state) { for (dom = base->global_search_state->head; dom; dom = dom_next) { @@ -4406,17 +4403,23 @@ evdns_getaddrinfo_gotresolve(int result, char type, int count, other_req = &data->ipv4_request; } - EVDNS_LOCK(data->evdns_base); - if (evdns_result_is_answer(result)) { - if (req->type == DNS_IPv4_A) - ++data->evdns_base->getaddrinfo_ipv4_answered; - else - ++data->evdns_base->getaddrinfo_ipv6_answered; + /** Called from evdns_base_free() with @fail_requests == 1 */ + if (result != DNS_ERR_SHUTDOWN) { + EVDNS_LOCK(data->evdns_base); + if (evdns_result_is_answer(result)) { + if (req->type == DNS_IPv4_A) + ++data->evdns_base->getaddrinfo_ipv4_answered; + else + ++data->evdns_base->getaddrinfo_ipv6_answered; + } + user_canceled = data->user_canceled; + if (other_req->r == NULL) + data->request_done = 1; + EVDNS_UNLOCK(data->evdns_base); + } else { + data->evdns_base = NULL; + user_canceled = data->user_canceled; } - user_canceled = data->user_canceled; - if (other_req->r == NULL) - data->request_done = 1; - EVDNS_UNLOCK(data->evdns_base); req->r = NULL; @@ -4450,7 +4453,9 @@ evdns_getaddrinfo_gotresolve(int result, char type, int count, /* The other request is still working; maybe it will * succeed. */ /* XXXX handle failure from set_timeout */ - evdns_getaddrinfo_set_timeout(data->evdns_base, data); + if (result != DNS_ERR_SHUTDOWN) { + evdns_getaddrinfo_set_timeout(data->evdns_base, data); + } data->pending_error = err; return; } diff --git a/event-config.h.cmake b/event-config.h.cmake index 1efb07ad..7cf2493f 100644 --- a/event-config.h.cmake +++ b/event-config.h.cmake @@ -11,8 +11,11 @@ /* Numeric representation of the version */ #define EVENT__NUMERIC_VERSION @EVENT_NUMERIC_VERSION@ +#define EVENT__PACKAGE_VERSION "@EVENT_PACKAGE_VERSION@" -#define EVENT__PACKAGE_VERSION @EVENT_PACKAGE_VERSION@ +#define EVENT__VERSION_MAJOR @EVENT_VERSION_MAJOR@ +#define EVENT__VERSION_MINOR @EVENT_VERSION_MINOR@ +#define EVENT__VERSION_PATCH @EVENT_VERSION_PATCH@ /* Version number of package */ #define EVENT__VERSION "@EVENT_VERSION@" @@ -33,467 +36,499 @@ #define EVENT__PACKAGE_TARNAME "" /* Define if libevent should build without support for a debug mode */ -#cmakedefine EVENT__DISABLE_DEBUG_MODE 0 +#cmakedefine EVENT__DISABLE_DEBUG_MODE /* Define if libevent should not allow replacing the mm functions */ -#cmakedefine EVENT__DISABLE_MM_REPLACEMENT 0 +#cmakedefine EVENT__DISABLE_MM_REPLACEMENT /* Define if libevent should not be compiled with thread support */ -#cmakedefine EVENT__DISABLE_THREAD_SUPPORT 0 +#cmakedefine EVENT__DISABLE_THREAD_SUPPORT /* Define to 1 if you have the `accept4' function. */ -#cmakedefine EVENT__HAVE_ACCEPT4 1 +#cmakedefine EVENT__HAVE_ACCEPT4 /* Define to 1 if you have the `arc4random' function. */ -#cmakedefine EVENT__HAVE_ARC4RANDOM 1 +#cmakedefine EVENT__HAVE_ARC4RANDOM /* Define to 1 if you have the `arc4random_buf' function. */ -#cmakedefine EVENT__HAVE_ARC4RANDOM_BUF 1 +#cmakedefine EVENT__HAVE_ARC4RANDOM_BUF /* Define if clock_gettime is available in libc */ -#cmakedefine EVENT__DNS_USE_CPU_CLOCK_FOR_ID 1 +#cmakedefine EVENT__DNS_USE_CPU_CLOCK_FOR_ID /* Define is no secure id variant is available */ -#cmakedefine EVENT__DNS_USE_GETTIMEOFDAY_FOR_ID 1 -#cmakedefine EVENT__DNS_USE_FTIME_FOR_ID 1 +#cmakedefine EVENT__DNS_USE_GETTIMEOFDAY_FOR_ID +#cmakedefine EVENT__DNS_USE_FTIME_FOR_ID /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_ARPA_INET_H 1 +#cmakedefine EVENT__HAVE_ARPA_INET_H /* Define to 1 if you have the `clock_gettime' function. */ -#cmakedefine EVENT__HAVE_CLOCK_GETTIME 1 +#cmakedefine EVENT__HAVE_CLOCK_GETTIME /* Define to 1 if you have the declaration of `CTL_KERN'. */ -#cmakedefine EVENT__HAVE_DECL_CTL_KERN 1 +#cmakedefine EVENT__HAVE_DECL_CTL_KERN /* Define to 1 if you have the declaration of `KERN_ARND'. */ -#cmakedefine EVENT__HAVE_DECL_KERN_ARND 0 +#cmakedefine EVENT__HAVE_DECL_KERN_ARND /* Define to 1 if you have the declaration of `KERN_RANDOM'. */ -#cmakedefine EVENT__HAVE_DECL_KERN_RANDOM 1 +#cmakedefine EVENT__HAVE_DECL_KERN_RANDOM /* Define if /dev/poll is available */ -#cmakedefine EVENT__HAVE_DEVPOLL 1 +#cmakedefine EVENT__HAVE_DEVPOLL /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_NETDB_H 1 +#cmakedefine EVENT__HAVE_NETDB_H /* Define to 1 if fd_mask type is defined */ -#cmakedefine EVENT__HAVE_FD_MASK 1 +#cmakedefine EVENT__HAVE_FD_MASK /* Define to 1 if the header file defines TAILQ_FOREACH. */ -#cmakedefine EVENT__HAVE_TAILQFOREACH 1 +#cmakedefine EVENT__HAVE_TAILQFOREACH /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_DLFCN_H 1 +#cmakedefine EVENT__HAVE_DLFCN_H /* Define if your system supports the epoll system calls */ -#cmakedefine EVENT__HAVE_EPOLL 1 +#cmakedefine EVENT__HAVE_EPOLL /* Define to 1 if you have the `epoll_create1' function. */ -#cmakedefine EVENT__HAVE_EPOLL_CREATE1 1 +#cmakedefine EVENT__HAVE_EPOLL_CREATE1 /* Define to 1 if you have the `epoll_ctl' function. */ -#cmakedefine EVENT__HAVE_EPOLL_CTL 1 +#cmakedefine EVENT__HAVE_EPOLL_CTL /* Define to 1 if you have the `eventfd' function. */ -#cmakedefine EVENT__HAVE_EVENTFD 1 +#cmakedefine EVENT__HAVE_EVENTFD /* Define if your system supports event ports */ -#cmakedefine EVENT__HAVE_EVENT_PORTS 1 +#cmakedefine EVENT__HAVE_EVENT_PORTS /* Define to 1 if you have the `fcntl' function. */ -#cmakedefine EVENT__HAVE_FCNTL 1 +#cmakedefine EVENT__HAVE_FCNTL /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_FCNTL_H 1 +#cmakedefine EVENT__HAVE_FCNTL_H /* Define to 1 if you have the `getaddrinfo' function. */ -#cmakedefine EVENT__HAVE_GETADDRINFO 1 +#cmakedefine EVENT__HAVE_GETADDRINFO /* Define to 1 if you have the `getegid' function. */ -#cmakedefine EVENT__HAVE_GETEGID 1 +#cmakedefine EVENT__HAVE_GETEGID /* Define to 1 if you have the `geteuid' function. */ -#cmakedefine EVENT__HAVE_GETEUID 1 +#cmakedefine EVENT__HAVE_GETEUID /* TODO: Check for different gethostname argument counts. CheckPrototypeDefinition.cmake can be used. */ /* Define this if you have any gethostbyname_r() */ -#cmakedefine EVENT__HAVE_GETHOSTBYNAME_R 1 +#cmakedefine EVENT__HAVE_GETHOSTBYNAME_R /* Define this if gethostbyname_r takes 3 arguments */ -#cmakedefine EVENT__HAVE_GETHOSTBYNAME_R_3_ARG 1 +#cmakedefine EVENT__HAVE_GETHOSTBYNAME_R_3_ARG /* Define this if gethostbyname_r takes 5 arguments */ -#cmakedefine EVENT__HAVE_GETHOSTBYNAME_R_5_ARG 1 +#cmakedefine EVENT__HAVE_GETHOSTBYNAME_R_5_ARG /* Define this if gethostbyname_r takes 6 arguments */ -#cmakedefine EVENT__HAVE_GETHOSTBYNAME_R_6_ARG 1 +#cmakedefine EVENT__HAVE_GETHOSTBYNAME_R_6_ARG /* Define to 1 if you have the `getifaddrs' function. */ -#cmakedefine EVENT__HAVE_GETIFADDRS 1 +#cmakedefine EVENT__HAVE_GETIFADDRS /* Define to 1 if you have the `getnameinfo' function. */ -#cmakedefine EVENT__HAVE_GETNAMEINFO 1 +#cmakedefine EVENT__HAVE_GETNAMEINFO /* Define to 1 if you have the `getprotobynumber' function. */ -#cmakedefine EVENT__HAVE_GETPROTOBYNUMBER 1 +#cmakedefine EVENT__HAVE_GETPROTOBYNUMBER /* Define to 1 if you have the `getservbyname' function. */ -#cmakedefine EVENT__HAVE_GETSERVBYNAME 1 +#cmakedefine EVENT__HAVE_GETSERVBYNAME /* Define to 1 if you have the `gettimeofday' function. */ -#cmakedefine EVENT__HAVE_GETTIMEOFDAY 1 +#cmakedefine EVENT__HAVE_GETTIMEOFDAY /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_IFADDRS_H 1 +#cmakedefine EVENT__HAVE_IFADDRS_H /* Define to 1 if you have the `inet_ntop' function. */ -#cmakedefine EVENT__HAVE_INET_NTOP 1 +#cmakedefine EVENT__HAVE_INET_NTOP /* Define to 1 if you have the `inet_pton' function. */ -#cmakedefine EVENT__HAVE_INET_PTON 1 +#cmakedefine EVENT__HAVE_INET_PTON /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_INTTYPES_H 1 +#cmakedefine EVENT__HAVE_INTTYPES_H /* Define to 1 if you have the `issetugid' function. */ -#cmakedefine EVENT__HAVE_ISSETUGID 1 +#cmakedefine EVENT__HAVE_ISSETUGID /* Define to 1 if you have the `kqueue' function. */ -#cmakedefine EVENT__HAVE_KQUEUE 1 +#cmakedefine EVENT__HAVE_KQUEUE /* Define if the system has zlib */ -#cmakedefine EVENT__HAVE_LIBZ 1 +#cmakedefine EVENT__HAVE_LIBZ /* Define to 1 if you have the `mach_absolute_time' function. */ -#cmakedefine EVENT__HAVE_MACH_ABSOLUTE_TIME 1 +#cmakedefine EVENT__HAVE_MACH_ABSOLUTE_TIME /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_MACH_MACH_TIME_H 1 +#cmakedefine EVENT__HAVE_MACH_MACH_TIME_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_MEMORY_H 1 +#cmakedefine EVENT__HAVE_MEMORY_H /* Define to 1 if you have the `mmap' function. */ -#cmakedefine EVENT__HAVE_MMAP 1 +#cmakedefine EVENT__HAVE_MMAP /* Define to 1 if you have the `nanosleep' function. */ -#cmakedefine EVENT__HAVE_NANOSLEEP 1 +#cmakedefine EVENT__HAVE_NANOSLEEP /* Define to 1 if you have the `usleep' function. */ -#cmakedefine EVENT__HAVE_USLEEP 1 +#cmakedefine EVENT__HAVE_USLEEP /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_NETDB_H 1 +#cmakedefine EVENT__HAVE_NETDB_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_NETINET_IN6_H 1 +#cmakedefine EVENT__HAVE_NETINET_IN6_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_NETINET_IN_H 1 +#cmakedefine EVENT__HAVE_NETINET_IN_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_NETINET_TCP_H 1 +#cmakedefine EVENT__HAVE_NETINET_TCP_H /* Define if the system has openssl */ -#cmakedefine EVENT__HAVE_OPENSSL 1 +#cmakedefine EVENT__HAVE_OPENSSL /* Defines if the system has zlib */ -#cmakedefine EVENT__HAVE_ZLIB 1 +#cmakedefine EVENT__HAVE_ZLIB /* Define to 1 if you have the `pipe' function. */ -#cmakedefine EVENT__HAVE_PIPE 1 +#cmakedefine EVENT__HAVE_PIPE /* Define to 1 if you have the `pipe2' function. */ -#cmakedefine EVENT__HAVE_PIPE2 1 +#cmakedefine EVENT__HAVE_PIPE2 /* Define to 1 if you have the `poll' function. */ -#cmakedefine EVENT__HAVE_POLL 1 +#cmakedefine EVENT__HAVE_POLL /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_POLL_H 1 +#cmakedefine EVENT__HAVE_POLL_H /* Define to 1 if you have the `port_create' function. */ -#cmakedefine EVENT__HAVE_PORT_CREATE 1 +#cmakedefine EVENT__HAVE_PORT_CREATE /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_PORT_H 1 +#cmakedefine EVENT__HAVE_PORT_H /* Define if you have POSIX threads libraries and header files. */ -#cmakedefine EVENT__HAVE_PTHREAD 1 +#cmakedefine EVENT__HAVE_PTHREAD /* Define if we have pthreads on this system */ -#cmakedefine EVENT__HAVE_PTHREADS 1 +#cmakedefine EVENT__HAVE_PTHREADS /* Define to 1 if you have the `putenv' function. */ -#cmakedefine EVENT__HAVE_PUTENV 1 +#cmakedefine EVENT__HAVE_PUTENV /* Define to 1 if the system has the type `sa_family_t'. */ -#cmakedefine EVENT__HAVE_SA_FAMILY_T 1 +#cmakedefine EVENT__HAVE_SA_FAMILY_T /* Define to 1 if you have the `select' function. */ -#cmakedefine EVENT__HAVE_SELECT 1 +#cmakedefine EVENT__HAVE_SELECT /* Define to 1 if you have the `setenv' function. */ -#cmakedefine EVENT__HAVE_SETENV 1 +#cmakedefine EVENT__HAVE_SETENV /* Define if F_SETFD is defined in */ -#cmakedefine EVENT__HAVE_SETFD 1 +#cmakedefine EVENT__HAVE_SETFD /* Define to 1 if you have the `setrlimit' function. */ -#cmakedefine EVENT__HAVE_SETRLIMIT 1 +#cmakedefine EVENT__HAVE_SETRLIMIT /* Define to 1 if you have the `sendfile' function. */ -#cmakedefine EVENT__HAVE_SENDFILE 1 +#cmakedefine EVENT__HAVE_SENDFILE /* Define if F_SETFD is defined in */ -#cmakedefine EVENT__HAVE_SETFD 1 +#cmakedefine EVENT__HAVE_SETFD /* Define to 1 if you have the `sigaction' function. */ -#cmakedefine EVENT__HAVE_SIGACTION 1 +#cmakedefine EVENT__HAVE_SIGACTION /* Define to 1 if you have the `signal' function. */ -#cmakedefine EVENT__HAVE_SIGNAL 1 +#cmakedefine EVENT__HAVE_SIGNAL /* Define to 1 if you have the `splice' function. */ -#cmakedefine EVENT__HAVE_SPLICE 1 +#cmakedefine EVENT__HAVE_SPLICE /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_STDARG_H 1 +#cmakedefine EVENT__HAVE_STDARG_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_STDDEF_H 1 +#cmakedefine EVENT__HAVE_STDDEF_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_STDINT_H 1 +#cmakedefine EVENT__HAVE_STDINT_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_STDLIB_H 1 +#cmakedefine EVENT__HAVE_STDLIB_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_STRINGS_H 1 +#cmakedefine EVENT__HAVE_STRINGS_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_STRING_H 1 +#cmakedefine EVENT__HAVE_STRING_H /* Define to 1 if you have the `strlcpy' function. */ -#cmakedefine EVENT__HAVE_STRLCPY 1 +#cmakedefine EVENT__HAVE_STRLCPY /* Define to 1 if you have the `strsep' function. */ -#cmakedefine EVENT__HAVE_STRSEP 1 +#cmakedefine EVENT__HAVE_STRSEP /* Define to 1 if you have the `strtok_r' function. */ -#cmakedefine EVENT__HAVE_STRTOK_R 1 +#cmakedefine EVENT__HAVE_STRTOK_R /* Define to 1 if you have the `strtoll' function. */ -#cmakedefine EVENT__HAVE_STRTOLL 1 +#cmakedefine EVENT__HAVE_STRTOLL /* Define to 1 if the system has the type `struct addrinfo'. */ -#cmakedefine EVENT__HAVE_STRUCT_ADDRINFO 1 +#cmakedefine EVENT__HAVE_STRUCT_ADDRINFO /* Define to 1 if the system has the type `struct in6_addr'. */ -#cmakedefine EVENT__HAVE_STRUCT_IN6_ADDR 1 +#cmakedefine EVENT__HAVE_STRUCT_IN6_ADDR /* Define to 1 if `s6_addr16' is member of `struct in6_addr'. */ -#cmakedefine EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR16 1 +#cmakedefine EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR16 /* Define to 1 if `s6_addr32' is member of `struct in6_addr'. */ -#cmakedefine EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR32 1 +#cmakedefine EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR32 /* Define to 1 if the system has the type `struct sockaddr_in6'. */ -#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_IN6 1 +#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_IN6 /* Define to 1 if `sin6_len' is member of `struct sockaddr_in6'. */ -#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN 1 +#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN /* Define to 1 if `sin_len' is member of `struct sockaddr_in'. */ -#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_IN_SIN_LEN 1 +#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_IN_SIN_LEN /* Define to 1 if the system has the type `struct sockaddr_storage'. */ -#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_STORAGE 1 +#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_STORAGE /* Define to 1 if `ss_family' is a member of `struct sockaddr_storage'. */ -#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 +#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY /* Define to 1 if `__ss_family' is a member of `struct sockaddr_storage'. */ -#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY 1 +#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY /* Define to 1 if you have the `sysctl' function. */ -#cmakedefine EVENT__HAVE_SYSCTL 1 +#cmakedefine EVENT__HAVE_SYSCTL /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_DEVPOLL_H 1 +#cmakedefine EVENT__HAVE_SYS_DEVPOLL_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_EPOLL_H 1 +#cmakedefine EVENT__HAVE_SYS_EPOLL_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_EVENTFD_H 1 +#cmakedefine EVENT__HAVE_SYS_EVENTFD_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_EVENT_H 1 +#cmakedefine EVENT__HAVE_SYS_EVENT_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_IOCTL_H 1 +#cmakedefine EVENT__HAVE_SYS_IOCTL_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_MMAN_H 1 +#cmakedefine EVENT__HAVE_SYS_MMAN_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_PARAM_H 1 +#cmakedefine EVENT__HAVE_SYS_PARAM_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_QUEUE_H 1 +#cmakedefine EVENT__HAVE_SYS_QUEUE_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_RESOURCE_H 1 +#cmakedefine EVENT__HAVE_SYS_RESOURCE_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_SELECT_H 1 +#cmakedefine EVENT__HAVE_SYS_SELECT_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_SENDFILE_H 1 +#cmakedefine EVENT__HAVE_SYS_SENDFILE_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_SOCKET_H 1 +#cmakedefine EVENT__HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_STAT_H 1 +#cmakedefine EVENT__HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_SYSCTL_H 1 +#cmakedefine EVENT__HAVE_SYS_SYSCTL_H /* Define to 1 if you have the header file. */ #cmakedefine EVENT__HAVE_SYS_TIMERFD_H */ /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_TIME_H 1 +#cmakedefine EVENT__HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_TYPES_H 1 +#cmakedefine EVENT__HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_UIO_H 1 +#cmakedefine EVENT__HAVE_SYS_UIO_H /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_SYS_WAIT_H 1 +#cmakedefine EVENT__HAVE_SYS_WAIT_H /* Define if TAILQ_FOREACH is defined in */ -#cmakedefine EVENT__HAVE_TAILQFOREACH 1 +#cmakedefine EVENT__HAVE_TAILQFOREACH /* Define if timeradd is defined in */ -#cmakedefine EVENT__HAVE_TIMERADD 1 +#cmakedefine EVENT__HAVE_TIMERADD /* Define if timerclear is defined in */ -#cmakedefine EVENT__HAVE_TIMERCLEAR 1 +#cmakedefine EVENT__HAVE_TIMERCLEAR /* Define if timercmp is defined in */ -#cmakedefine EVENT__HAVE_TIMERCMP 1 +#cmakedefine EVENT__HAVE_TIMERCMP /* Define to 1 if you have the `timerfd_create' function. */ -#cmakedefine EVENT__HAVE_TIMERFD_CREATE 1 +#cmakedefine EVENT__HAVE_TIMERFD_CREATE /* Define if timerisset is defined in */ -#cmakedefine EVENT__HAVE_TIMERISSET 1 +#cmakedefine EVENT__HAVE_TIMERISSET /* Define to 1 if the system has the type `uint8_t'. */ -#cmakedefine EVENT__HAVE_UINT8_T 1 +#cmakedefine EVENT__HAVE_UINT8_T /* Define to 1 if the system has the type `uint16_t'. */ -#cmakedefine EVENT__HAVE_UINT16_T 1 +#cmakedefine EVENT__HAVE_UINT16_T /* Define to 1 if the system has the type `uint32_t'. */ -#cmakedefine EVENT__HAVE_UINT32_T 1 +#cmakedefine EVENT__HAVE_UINT32_T /* Define to 1 if the system has the type `uint64_t'. */ -#cmakedefine EVENT__HAVE_UINT64_T 1 +#cmakedefine EVENT__HAVE_UINT64_T /* Define to 1 if the system has the type `uintptr_t'. */ -#cmakedefine EVENT__HAVE_UINTPTR_T 1 +#cmakedefine EVENT__HAVE_UINTPTR_T /* Define to 1 if you have the `umask' function. */ -#cmakedefine EVENT__HAVE_UMASK 1 +#cmakedefine EVENT__HAVE_UMASK /* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_UNISTD_H 1 +#cmakedefine EVENT__HAVE_UNISTD_H /* Define to 1 if you have the `unsetenv' function. */ -#cmakedefine EVENT__HAVE_UNSETENV 1 +#cmakedefine EVENT__HAVE_UNSETENV /* Define to 1 if you have the `vasprintf' function. */ -#cmakedefine EVENT__HAVE_VASPRINTF 1 +#cmakedefine EVENT__HAVE_VASPRINTF /* Define if kqueue works correctly with pipes */ -#cmakedefine EVENT__HAVE_WORKING_KQUEUE 1 +#cmakedefine EVENT__HAVE_WORKING_KQUEUE -/* Define to necessary symbol if this constant uses a non-standard name on - your system. */ -#cmakedefine EVENT__PTHREAD_CREATE_JOINABLE ${EVENT__PTHREAD_CREATE_JOINABLE} +#ifdef __USE_UNUSED_DEFINITIONS__ +/* Define to necessary symbol if this constant uses a non-standard name on your system. */ +/* XXX: Hello, this isn't even used, nor is it defined anywhere... - Ellzey */ +#define EVENT__PTHREAD_CREATE_JOINABLE ${EVENT__PTHREAD_CREATE_JOINABLE} +#endif /* The size of `pthread_t', as computed by sizeof. */ -#cmakedefine EVENT__SIZEOF_PTHREAD_T ${EVENT__SIZEOF_PTHREAD_T} +#define EVENT__SIZEOF_PTHREAD_T @EVENT__SIZEOF_PTHREAD_T@ /* The size of a `int', as computed by sizeof. */ -#cmakedefine EVENT__SIZEOF_INT ${EVENT__SIZEOF_INT} +#define EVENT__SIZEOF_INT @EVENT__SIZEOF_INT@ /* The size of a `long', as computed by sizeof. */ -#cmakedefine EVENT__SIZEOF_LONG ${EVENT__SIZEOF_LONG} +#define EVENT__SIZEOF_LONG @EVENT__SIZEOF_LONG@ /* The size of a `long long', as computed by sizeof. */ -#cmakedefine EVENT__SIZEOF_LONG_LONG ${EVENT__SIZEOF_LONG_LONG} +#define EVENT__SIZEOF_LONG_LONG @EVENT__SIZEOF_LONG_LONG@ /* The size of `off_t', as computed by sizeof. */ -#cmakedefine EVENT__SIZEOF_OFF_T ${EVENT__SIZEOF_OFF_T} +#define EVENT__SIZEOF_OFF_T @EVENT__SIZEOF_OFF_T@ + +#define EVENT__SIZEOF_SSIZE_T @EVENT__SIZEOF_SSIZE_T@ + /* The size of a `short', as computed by sizeof. */ -#cmakedefine EVENT__SIZEOF_SHORT ${EVENT__SIZEOF_SHORT} +#define EVENT__SIZEOF_SHORT @EVENT__SIZEOF_SHORT@ /* The size of `size_t', as computed by sizeof. */ -#cmakedefine EVENT__SIZEOF_SIZE_T ${EVENT__SIZEOF_SIZE_T} +#define EVENT__SIZEOF_SIZE_T @EVENT__SIZEOF_SIZE_T@ /* Define to 1 if you have the ANSI C header files. */ -#cmakedefine EVENT__STDC_HEADERS ${EVENT__STDC_HEADERS} +#cmakedefine EVENT__STDC_HEADERS /* Define to 1 if you can safely include both and . */ -#cmakedefine EVENT__TIME_WITH_SYS_TIME ${EVENT__TIME_WITH_SYS_TIME} +#cmakedefine EVENT__TIME_WITH_SYS_TIME /* The size of `socklen_t', as computed by sizeof. */ -#cmakedefine EVENT__SIZEOF_SOCKLEN_T ${EVENT__SIZEOF_SOCKLEN_T} +#define EVENT__SIZEOF_SOCKLEN_T @EVENT__SIZEOF_SOCKLEN_T@ /* The size of 'void *', as computer by sizeof */ -#cmakedefine EVENT__SIZEOF_VOID_P ${EVENT__SIZEOF_VOID_P} +#define EVENT__SIZEOF_VOID_P @EVENT__SIZEOF_VOID_P@ -/* Define to appropriate substitute if compiler doesnt have __func__ */ -#cmakedefine EVENT____func__ ${EVENT____func__} +/* set an alias for whatever __func__ __FUNCTION__ is, what sillyness */ +#if defined (__func__) +#define EVENT____func__ __func__ +#elif defined(__FUNCTION__) +#define EVENT____func__ __FUNCTION__ +#else +#define EVENT____func__ __FILE__ +#endif + +#ifdef __THESE_ARE_NOT_CONFIG_H_THINGS_THEY_ARE_DASH_D_THINGS__ /* Number of bits in a file offset, on hosts where this is settable. */ -#cmakedefine EVENT___FILE_OFFSET_BITS ${EVENT___FILE_OFFSET_BITS} +/* Ellzey is not satisfied */ +#define EVENT___FILE_OFFSET_BITS @EVENT___FILE_OFFSET_BITS@ /* Define for large files, on AIX-style hosts. */ -/* #undef _LARGE_FILES */ +#define @_LARGE_FILES@ +#endif +#ifdef _WhAT_DOES_THIS_EVEN_DO_ /* Define to empty if `const' does not conform to ANSI C. */ -/* #undef EVENT__const */ +/* lolwut? - ellzey */ +#undef EVENT__const +#endif + /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus -#cmakedefine EVENT__inline ${EVENT__inline} +/* why not c++? + * + * and are we really expected to use EVENT__inline everywhere, + * shouldn't we just do: + * ifdef EVENT__inline + * define inline EVENT__inline + * + * - Ellzey + */ + +#define EVENT__inline @EVENT__inline@ #endif -/* Define to `int' if does not define. */ -#cmakedefine EVENT__pid_t ${EVENT__pid_t} +/* Define to `int' if does not define. */ +#define EVENT__pid_t @EVENT__pid_t@ /* Define to `unsigned' if does not define. */ -#cmakedefine EVENT__size_t ${EVENT__size_t} +#define EVENT__size_t @EVENT__size_t@ /* Define to unsigned int if you dont have it */ -#cmakedefine EVENT__socklen_t ${EVENT__socklen_t} +#define EVENT__socklen_t @EVENT__socklen_t@ /* Define to `int' if does not define. */ -#cmakedefine EVENT__ssize_t ${EVENT__ssize_t} +#define EVENT__ssize_t @EVENT__ssize_t@ -#cmakedefine EVENT__NEED_DLLIMPORT ${EVENT__NEED_DLLIMPORT} +#define EVENT__NEED_DLLIMPORT @EVENT__NEED_DLLIMPORT@ + +/* Define to 1 if you have ERR_remove_thread_stat(). */ +#cmakedefine EVENT__HAVE_ERR_REMOVE_THREAD_STATE #endif diff --git a/event-internal.h b/event-internal.h index 3bc79c72..66dcfc32 100644 --- a/event-internal.h +++ b/event-internal.h @@ -368,7 +368,6 @@ struct event_config { }; /* Internal use only: Functions that might be missing from */ -#if defined(EVENT__HAVE_SYS_QUEUE_H) && !defined(EVENT__HAVE_TAILQFOREACH) #ifndef TAILQ_FIRST #define TAILQ_FIRST(head) ((head)->tqh_first) #endif @@ -394,7 +393,6 @@ struct event_config { (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0) #endif -#endif /* TAILQ_FOREACH */ #define N_ACTIVE_CALLBACKS(base) \ ((base)->event_count_active) @@ -467,6 +465,13 @@ void event_base_assert_ok_nolock_(struct event_base *base); int event_base_foreach_event_nolock_(struct event_base *base, event_base_foreach_event_cb cb, void *arg); +/* Cleanup function to reset debug mode during shutdown. + * + * Calling this function doesn't mean it'll be possible to re-enable + * debug mode if any events were added. + */ +void event_disable_debug_mode(void); + #ifdef __cplusplus } #endif diff --git a/event.3 b/event.3 index 5b33ec64..655a823e 100644 --- a/event.3 +++ b/event.3 @@ -183,14 +183,14 @@ .Fn "evbuffer_write" "struct evbuffer *buf" "int fd" .Ft int .Fn "evbuffer_read" "struct evbuffer *buf" "int fd" "int size" -.Ft "u_char *" -.Fn "evbuffer_find" "struct evbuffer *buf" "const u_char *data" "size_t size" +.Ft "unsigned char *" +.Fn "evbuffer_find" "struct evbuffer *buf" "const unsigned char *data" "size_t size" .Ft "char *" .Fn "evbuffer_readline" "struct evbuffer *buf" .Ft "struct evhttp *" .Fn "evhttp_new" "struct event_base *base" .Ft int -.Fn "evhttp_bind_socket" "struct evhttp *http" "const char *address" "u_short port" +.Fn "evhttp_bind_socket" "struct evhttp *http" "const char *address" "unsigned short port" .Ft "void" .Fn "evhttp_free" "struct evhttp *http" .Ft int diff --git a/event.c b/event.c index acef2b24..def17665 100644 --- a/event.c +++ b/event.c @@ -199,6 +199,22 @@ eq_debug_entry(const struct event_debug_entry *a, } int event_debug_mode_on_ = 0; + + +#if !defined(EVENT__DISABLE_THREAD_SUPPORT) && !defined(EVENT__DISABLE_DEBUG_MODE) +/** + * @brief debug mode variable which is set for any function/structure that needs + * to be shared across threads (if thread support is enabled). + * + * When and if evthreads are initialized, this variable will be evaluated, + * and if set to something other than zero, this means the evthread setup + * functions were called out of order. + * + * See: "Locks and threading" in the documentation. + */ +int event_debug_created_threadable_ctx_ = 0; +#endif + /* Set if it's too late to enable event_debug_mode. */ static int event_debug_mode_too_late = 0; #ifndef EVENT__DISABLE_THREAD_SUPPORT @@ -655,6 +671,10 @@ event_base_new_with_config(const struct event_config *cfg) /* prepare for threading */ +#if !defined(EVENT__DISABLE_THREAD_SUPPORT) && !defined(EVENT__DISABLE_DEBUG_MODE) + event_debug_created_threadable_ctx_ = 1; +#endif + #ifndef EVENT__DISABLE_THREAD_SUPPORT if (EVTHREAD_LOCKING_ENABLED() && (!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK))) { @@ -749,6 +769,29 @@ event_base_cancel_single_callback_(struct event_base *base, return result; } +static int event_base_free_queues_(struct event_base *base, int run_finalizers) +{ + int deleted = 0, i; + + for (i = 0; i < base->nactivequeues; ++i) { + struct event_callback *evcb, *next; + for (evcb = TAILQ_FIRST(&base->activequeues[i]); evcb; ) { + next = TAILQ_NEXT(evcb, evcb_active_next); + deleted += event_base_cancel_single_callback_(base, evcb, run_finalizers); + evcb = next; + } + } + + { + struct event_callback *evcb; + while ((evcb = TAILQ_FIRST(&base->active_later_queue))) { + deleted += event_base_cancel_single_callback_(base, evcb, run_finalizers); + } + } + + return deleted; +} + static void event_base_free_(struct event_base *base, int run_finalizers) { @@ -809,21 +852,21 @@ event_base_free_(struct event_base *base, int run_finalizers) if (base->common_timeout_queues) mm_free(base->common_timeout_queues); - for (i = 0; i < base->nactivequeues; ++i) { - struct event_callback *evcb, *next; - for (evcb = TAILQ_FIRST(&base->activequeues[i]); evcb; ) { - next = TAILQ_NEXT(evcb, evcb_active_next); - n_deleted += event_base_cancel_single_callback_(base, evcb, run_finalizers); - evcb = next; + for (;;) { + /* For finalizers we can register yet another finalizer out from + * finalizer, and iff finalizer will be in active_later_queue we can + * add finalizer to activequeues, and we will have events in + * activequeues after this function returns, which is not what we want + * (we even have an assertion for this). + * + * A simple case is bufferevent with underlying (i.e. filters). + */ + int i = event_base_free_queues_(base, run_finalizers); + if (!i) { + break; } + n_deleted += i; } - { - struct event_callback *evcb; - while ((evcb = TAILQ_FIRST(&base->active_later_queue))) { - n_deleted += event_base_cancel_single_callback_(base, evcb, run_finalizers); - } - } - if (n_deleted) event_debug(("%s: %d events were still set in base", diff --git a/event_tagging.c b/event_tagging.c index 64b100b8..6459dfa7 100644 --- a/event_tagging.c +++ b/event_tagging.c @@ -40,10 +40,11 @@ #include #include #undef WIN32_LEAN_AND_MEAN -#else -#include #endif +#ifdef EVENT__HAVE_SYS_IOCTL_H +#include +#endif #include #ifdef EVENT__HAVE_SYS_TIME_H #include diff --git a/evthread.c b/evthread.c index 02dab7a8..f3f1eddc 100644 --- a/evthread.c +++ b/evthread.c @@ -45,6 +45,11 @@ #define GLOBAL static #endif +#ifndef EVENT__DISABLE_DEBUG_MODE +extern int event_debug_created_threadable_ctx_; +extern int event_debug_mode_on_; +#endif + /* globals */ GLOBAL int evthread_lock_debugging_enabled_ = 0; GLOBAL struct evthread_lock_callbacks evthread_lock_fns_ = { @@ -89,6 +94,14 @@ evthread_set_lock_callbacks(const struct evthread_lock_callbacks *cbs) { struct evthread_lock_callbacks *target = evthread_get_lock_callbacks(); +#ifndef EVENT__DISABLE_DEBUG_MODE + if (event_debug_mode_on_) { + if (event_debug_created_threadable_ctx_) { + event_errx(1, "evthread initialization must be called BEFORE anything else!"); + } + } +#endif + if (!cbs) { if (target->alloc) event_warnx("Trying to disable lock functions after " @@ -124,6 +137,14 @@ evthread_set_condition_callbacks(const struct evthread_condition_callbacks *cbs) { struct evthread_condition_callbacks *target = evthread_get_condition_callbacks(); +#ifndef EVENT__DISABLE_DEBUG_MODE + if (event_debug_mode_on_) { + if (event_debug_created_threadable_ctx_) { + event_errx(1, "evthread initialization must be called BEFORE anything else!"); + } + } +#endif + if (!cbs) { if (target->alloc_condition) event_warnx("Trying to disable condition functions " @@ -380,17 +401,18 @@ evthread_setup_global_lock_(void *lock_, unsigned locktype, int enable_locks) return evthread_lock_fns_.alloc(locktype); } else { /* Case 4: Fill in a debug lock with a real lock */ - struct debug_lock *lock = lock_; + struct debug_lock *lock = lock_ ? lock_ : debug_lock_alloc(locktype); EVUTIL_ASSERT(enable_locks && evthread_lock_debugging_enabled_); EVUTIL_ASSERT(lock->locktype == locktype); - EVUTIL_ASSERT(lock->lock == NULL); - lock->lock = original_lock_fns_.alloc( - locktype|EVTHREAD_LOCKTYPE_RECURSIVE); if (!lock->lock) { - lock->count = -200; - mm_free(lock); - return NULL; + lock->lock = original_lock_fns_.alloc( + locktype|EVTHREAD_LOCKTYPE_RECURSIVE); + if (!lock->lock) { + lock->count = -200; + mm_free(lock); + return NULL; + } } return lock; } @@ -406,6 +428,12 @@ evthreadimpl_get_id_() void * evthreadimpl_lock_alloc_(unsigned locktype) { +#ifndef EVENT__DISABLE_DEBUG_MODE + if (event_debug_mode_on_) { + event_debug_created_threadable_ctx_ = 1; + } +#endif + return evthread_lock_fns_.alloc ? evthread_lock_fns_.alloc(locktype) : NULL; } @@ -434,6 +462,12 @@ evthreadimpl_lock_unlock_(unsigned mode, void *lock) void * evthreadimpl_cond_alloc_(unsigned condtype) { +#ifndef EVENT__DISABLE_DEBUG_MODE + if (event_debug_mode_on_) { + event_debug_created_threadable_ctx_ = 1; + } +#endif + return evthread_cond_fns_.alloc_condition ? evthread_cond_fns_.alloc_condition(condtype) : NULL; } diff --git a/evutil.c b/evutil.c index 3d72e403..79d825d9 100644 --- a/evutil.c +++ b/evutil.c @@ -226,16 +226,17 @@ evutil_ersatz_socketpair_(int family, int type, int protocol, struct sockaddr_in connect_addr; ev_socklen_t size; int saved_errno = -1; - - if (protocol - || (family != AF_INET + int family_test; + + family_test = family != AF_INET; #ifdef AF_UNIX - && family != AF_UNIX + family_test = family_test && (family != AF_UNIX); #endif - )) { + if (protocol || family_test) { EVUTIL_SET_SOCKET_ERROR(ERR(EAFNOSUPPORT)); return -1; } + if (!fd) { EVUTIL_SET_SOCKET_ERROR(ERR(EINVAL)); return -1; @@ -257,6 +258,9 @@ evutil_ersatz_socketpair_(int family, int type, int protocol, connector = socket(AF_INET, type, 0); if (connector < 0) goto tidy_up_and_fail; + + memset(&connect_addr, 0, sizeof(connect_addr)); + /* We want to find out the port number to connect to. */ size = sizeof(connect_addr); if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1) @@ -310,7 +314,7 @@ evutil_make_socket_nonblocking(evutil_socket_t fd) { #ifdef _WIN32 { - u_long nonblocking = 1; + unsigned long nonblocking = 1; if (ioctlsocket(fd, FIONBIO, &nonblocking) == SOCKET_ERROR) { event_sock_warn(fd, "fcntl(%d, F_GETFL)", (int)fd); return -1; @@ -355,7 +359,7 @@ evutil_fast_socket_nonblocking(evutil_socket_t fd) int evutil_make_listen_socket_reuseable(evutil_socket_t sock) { -#ifndef _WIN32 +#if defined(SO_REUSEADDR) && !defined(_WIN32) int one = 1; /* REUSEADDR on Unix means, "don't hang on to this address after the * listener is closed." On Windows, though, it means "don't keep other @@ -520,7 +524,7 @@ evutil_socket_geterror(evutil_socket_t sock) /* XXX we should use an enum here. */ /* 2 for connection refused, 1 for connected, 0 for not yet, -1 for error. */ int -evutil_socket_connect_(evutil_socket_t *fd_ptr, struct sockaddr *sa, int socklen) +evutil_socket_connect_(evutil_socket_t *fd_ptr, const struct sockaddr *sa, int socklen) { int made_fd = 0; @@ -1159,7 +1163,7 @@ addrinfo_from_hostent(const struct hostent *ent, sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(port); sa = (struct sockaddr *)&sin6; - socklen = sizeof(struct sockaddr_in); + socklen = sizeof(struct sockaddr_in6); addrp = &sin6.sin6_addr; if (ent->h_length != sizeof(sin6.sin6_addr)) { event_warnx("Weird h_length from gethostbyname"); @@ -1705,10 +1709,10 @@ evutil_socket_error_to_string(int errcode) goto done; } - if (0 != FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + if (0 != FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, - NULL, errcode, 0, (LPTSTR)&msg, 0, NULL)) + NULL, errcode, 0, (char *)&msg, 0, NULL)) chomp (msg); /* because message has trailing newline */ else { size_t len = 50; diff --git a/http-internal.h b/http-internal.h index a83160c8..90fd7928 100644 --- a/http-internal.h +++ b/http-internal.h @@ -62,10 +62,10 @@ struct evhttp_connection { struct event retry_ev; /* for retrying connects */ char *bind_address; /* address to use for binding the src */ - u_short bind_port; /* local port for binding the src */ + unsigned short bind_port; /* local port for binding the src */ char *address; /* address to connect to */ - u_short port; + unsigned short port; size_t max_headers_size; ev_uint64_t max_body_size; @@ -101,13 +101,6 @@ struct evhttp_connection { struct event_base *base; struct evdns_base *dns_base; int ai_family; - - /* Saved conn_addr, to extract IP address from it. - * - * Because some servers may reset/close connection without waiting clients, - * in that case we can't extract IP address even in close_cb. - * So we need to save it, just after we connected to remote server. */ - struct sockaddr_storage *conn_address; }; /* A callback for an http server */ diff --git a/http.c b/http.c index dd8c9cff..7d7ff07c 100644 --- a/http.c +++ b/http.c @@ -35,18 +35,22 @@ #include #endif -#ifdef EVENT__HAVE_SYS_TIME_H -#include -#endif #ifdef HAVE_SYS_IOCCOM_H #include #endif +#ifdef EVENT__HAVE_SYS_RESOURCE_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif +#ifdef EVENT__HAVE_SYS_WAIT_H +#include +#endif #ifndef _WIN32 -#include #include #include -#include #else #include #include @@ -461,6 +465,13 @@ evhttp_is_connection_close(int flags, struct evkeyvalq* headers) return (connection != NULL && evutil_ascii_strcasecmp(connection, "close") == 0); } } +static int +evhttp_is_request_connection_close(struct evhttp_request *req) +{ + return + evhttp_is_connection_close(req->flags, req->input_headers) || + evhttp_is_connection_close(req->flags, req->output_headers); +} /* Return true iff 'headers' contains 'Connection: keep-alive' */ static int @@ -674,6 +685,23 @@ evhttp_connection_incoming_fail(struct evhttp_request *req, return (0); } +/* Free connection ownership of which can be acquired by user using + * evhttp_request_own(). */ +static inline void +evhttp_request_free_auto(struct evhttp_request *req) +{ + if (!(req->flags & EVHTTP_USER_OWNED)) { + evhttp_request_free(req); + } +} + +static void +evhttp_request_free_(struct evhttp_connection *evcon, struct evhttp_request *req) +{ + TAILQ_REMOVE(&evcon->requests, req, next); + evhttp_request_free_auto(req); +} + /* Called when evcon has experienced a (non-recoverable? -NM) error, as * given in error. If it's an outgoing connection, reset the connection, * retry any pending requests, and inform the user. If it's incoming, @@ -722,8 +750,7 @@ evhttp_connection_fail_(struct evhttp_connection *evcon, * send over a new connection. when a user cancels a request, * all other pending requests should be processed as normal */ - TAILQ_REMOVE(&evcon->requests, req, next); - evhttp_request_free(req); + evhttp_request_free_(evcon, req); /* reset the connection */ evhttp_connection_reset_(evcon); @@ -773,16 +800,12 @@ evhttp_connection_done(struct evhttp_connection *evcon) if (con_outgoing) { /* idle or close the connection */ - int need_close; + int need_close = evhttp_is_request_connection_close(req); TAILQ_REMOVE(&evcon->requests, req, next); req->evcon = NULL; evcon->state = EVCON_IDLE; - need_close = - evhttp_is_connection_close(req->flags, req->input_headers)|| - evhttp_is_connection_close(req->flags, req->output_headers); - /* check if we got asked to close the connection */ if (need_close) evhttp_connection_reset_(evcon); @@ -820,11 +843,9 @@ evhttp_connection_done(struct evhttp_connection *evcon) /* notify the user of the request */ (*req->cb)(req, req->cb_arg); - /* if this was an outgoing request, we own and it's done. so free it. - * unless the callback specifically requested to own the request. - */ - if (con_outgoing && ((req->flags & EVHTTP_USER_OWNED) == 0)) { - evhttp_request_free(req); + /* if this was an outgoing request, we own and it's done. so free it. */ + if (con_outgoing) { + evhttp_request_free_auto(req); } /* If this was the last request of an outgoing connection and we're @@ -960,7 +981,6 @@ evhttp_read_trailer(struct evhttp_connection *evcon, struct evhttp_request *req) case MORE_DATA_EXPECTED: case REQUEST_CANCELED: /* ??? */ default: - bufferevent_enable(evcon->bufev, EV_READ); break; } } @@ -985,7 +1005,7 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req) return; case REQUEST_CANCELED: /* request canceled */ - evhttp_request_free(req); + evhttp_request_free_auto(req); return; case MORE_DATA_EXPECTED: default: @@ -1031,7 +1051,7 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req) evbuffer_drain(req->input_buffer, evbuffer_get_length(req->input_buffer)); if ((req->flags & EVHTTP_REQ_NEEDS_FREE) != 0) { - evhttp_request_free(req); + evhttp_request_free_auto(req); return; } } @@ -1042,9 +1062,6 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req) evhttp_connection_done(evcon); return; } - - /* Read more! */ - bufferevent_enable(evcon->bufev, EV_READ); } #define get_deferred_queue(evcon) \ @@ -1125,6 +1142,9 @@ evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg) EVUTIL_ASSERT(evcon->state == EVCON_WRITING); + /* We need to wait until we've written all of our output data before we can continue */ + if (evbuffer_get_length(bufferevent_get_output(evcon->bufev)) > 0) { return; } + /* We are done writing our header and are now expecting the response */ req->kind = EVHTTP_RESPONSE; @@ -1152,8 +1172,7 @@ evhttp_connection_free(struct evhttp_connection *evcon) * evhttp_connection_fail_. */ while ((req = TAILQ_FIRST(&evcon->requests)) != NULL) { - TAILQ_REMOVE(&evcon->requests, req, next); - evhttp_request_free(req); + evhttp_request_free_(evcon, req); } if (evcon->http_server != NULL) { @@ -1173,6 +1192,7 @@ evhttp_connection_free(struct evhttp_connection *evcon) &evcon->read_more_deferred_cb); if (evcon->fd != -1) { + bufferevent_disable(evcon->bufev, EV_READ|EV_WRITE); shutdown(evcon->fd, EVUTIL_SHUT_WR); if (!(bufferevent_get_options_(evcon->bufev) & BEV_OPT_CLOSE_ON_FREE)) { evutil_closesocket(evcon->fd); @@ -1185,9 +1205,6 @@ evhttp_connection_free(struct evhttp_connection *evcon) if (evcon->address != NULL) mm_free(evcon->address); - if (evcon->conn_address != NULL) - mm_free(evcon->conn_address); - mm_free(evcon); } @@ -1352,7 +1369,7 @@ evhttp_connection_cb_cleanup(struct evhttp_connection *evcon) /* we might want to set an error here */ request->cb(request, request->cb_arg); - evhttp_request_free(request); + evhttp_request_free_auto(request); } } @@ -1441,7 +1458,6 @@ evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg) struct evhttp_connection *evcon = arg; int error; ev_socklen_t errsz = sizeof(error); - socklen_t conn_address_len = sizeof(*evcon->conn_address); if (evcon->fd == -1) evcon->fd = bufferevent_getfd(bufev); @@ -1492,14 +1508,6 @@ evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg) evcon->retry_cnt = 0; evcon->state = EVCON_IDLE; - if (!evcon->conn_address) { - evcon->conn_address = mm_malloc(sizeof(*evcon->conn_address)); - } - if (getpeername(evcon->fd, (struct sockaddr *)evcon->conn_address, &conn_address_len)) { - mm_free(evcon->conn_address); - evcon->conn_address = NULL; - } - /* reset the bufferevent cbs */ bufferevent_setcb(evcon->bufev, evhttp_read_cb, @@ -2181,9 +2189,6 @@ evhttp_read_header(struct evhttp_connection *evcon, return; } - /* Disable reading for now */ - bufferevent_disable(evcon->bufev, EV_READ); - /* Callback can shut down connection with negative return value */ if (req->header_cb != NULL) { if ((*req->header_cb)(req, req->cb_arg) < 0) { @@ -2335,6 +2340,20 @@ void evhttp_connection_set_family(struct evhttp_connection *evcon, evcon->ai_family = family; } +int evhttp_connection_set_flags(struct evhttp_connection *evcon, + int flags) +{ + if (flags & ~(EVHTTP_CON_REUSE_CONNECTED_ADDR)) { + return 1; + } + + evcon->flags &= ~(EVHTTP_CON_REUSE_CONNECTED_ADDR); + + evcon->flags |= EVHTTP_CON_REUSE_CONNECTED_ADDR; + + return 0; +} + void evhttp_connection_set_base(struct evhttp_connection *evcon, struct event_base *base) @@ -2412,13 +2431,16 @@ evhttp_connection_get_peer(struct evhttp_connection *evcon, const struct sockaddr* evhttp_connection_get_addr(struct evhttp_connection *evcon) { - return (struct sockaddr *)evcon->conn_address; + return bufferevent_socket_get_conn_address_(evcon->bufev); } int evhttp_connection_connect_(struct evhttp_connection *evcon) { int old_state = evcon->state; + const char *address = evcon->address; + const struct sockaddr *sa = evhttp_connection_get_addr(evcon); + int ret; if (evcon->state == EVCON_CONNECTING) return (0); @@ -2450,17 +2472,29 @@ evhttp_connection_connect_(struct evhttp_connection *evcon) evcon); if (!evutil_timerisset(&evcon->timeout)) { const struct timeval conn_tv = { HTTP_CONNECT_TIMEOUT, 0 }; - bufferevent_set_timeouts(evcon->bufev, NULL, &conn_tv); + bufferevent_set_timeouts(evcon->bufev, &conn_tv, &conn_tv); } else { - bufferevent_set_timeouts(evcon->bufev, NULL, &evcon->timeout); + bufferevent_set_timeouts(evcon->bufev, &evcon->timeout, &evcon->timeout); } /* make sure that we get a write callback */ bufferevent_enable(evcon->bufev, EV_WRITE); evcon->state = EVCON_CONNECTING; - if (bufferevent_socket_connect_hostname(evcon->bufev, evcon->dns_base, - evcon->ai_family, evcon->address, evcon->port) < 0) { + if (evcon->flags & EVHTTP_CON_REUSE_CONNECTED_ADDR && + sa && + (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)) { + int socklen = sizeof(struct sockaddr_in); + if (sa->sa_family == AF_INET6) { + socklen = sizeof(struct sockaddr_in6); + } + ret = bufferevent_socket_connect(evcon->bufev, sa, socklen); + } else { + ret = bufferevent_socket_connect_hostname(evcon->bufev, + evcon->dns_base, evcon->ai_family, address, evcon->port); + } + + if (ret < 0) { evcon->state = old_state; event_sock_warn(evcon->fd, "%s: connection to \"%s\" failed", __func__, evcon->address); @@ -2493,7 +2527,7 @@ evhttp_make_request(struct evhttp_connection *evcon, mm_free(req->uri); if ((req->uri = mm_strdup(uri)) == NULL) { event_warn("%s: strdup", __func__); - evhttp_request_free(req); + evhttp_request_free_auto(req); return (-1); } @@ -2556,7 +2590,7 @@ evhttp_cancel_request(struct evhttp_request *req) } } - evhttp_request_free(req); + evhttp_request_free_auto(req); } /* @@ -2567,9 +2601,9 @@ evhttp_cancel_request(struct evhttp_request *req) void evhttp_start_read_(struct evhttp_connection *evcon) { - /* Set up an event to read the headers */ bufferevent_disable(evcon->bufev, EV_WRITE); bufferevent_enable(evcon->bufev, EV_READ); + evcon->state = EVCON_READING_FIRSTLINE; /* Reset the bufferevent callbacks */ bufferevent_setcb(evcon->bufev, @@ -2599,9 +2633,8 @@ evhttp_send_done(struct evhttp_connection *evcon, void *arg) need_close = (REQ_VERSION_BEFORE(req, 1, 1) && - !evhttp_is_connection_keepalive(req->input_headers))|| - evhttp_is_connection_close(req->flags, req->input_headers) || - evhttp_is_connection_close(req->flags, req->output_headers); + !evhttp_is_connection_keepalive(req->input_headers)) || + evhttp_is_request_connection_close(req); EVUTIL_ASSERT(req->flags & EVHTTP_REQ_OWN_CONNECTION); evhttp_request_free(req); @@ -4035,6 +4068,8 @@ evhttp_get_request_connection( evcon->fd = fd; + bufferevent_enable(evcon->bufev, EV_READ); + bufferevent_disable(evcon->bufev, EV_WRITE); bufferevent_setfd(evcon->bufev, fd); return (evcon); diff --git a/include/event.h b/include/event.h index 3a1b0c2c..ba518671 100644 --- a/include/event.h +++ b/include/event.h @@ -63,8 +63,6 @@ extern "C" { #include #include #undef WIN32_LEAN_AND_MEAN -typedef unsigned char u_char; -typedef unsigned short u_short; #endif #include diff --git a/include/event2/buffer_compat.h b/include/event2/buffer_compat.h index c0bdd9d2..24f828c2 100644 --- a/include/event2/buffer_compat.h +++ b/include/event2/buffer_compat.h @@ -37,7 +37,7 @@ /** - Obsolete alias for evbuffer_readln(buffer, NULL, EOL_STYLE_ANY). + Obsolete alias for evbuffer_readln(buffer, NULL, EVBUFFER_EOL_ANY). @deprecated This function is deprecated because its behavior is not correct for almost any protocol, and also because it's wholly subsumed by diff --git a/include/event2/bufferevent.h b/include/event2/bufferevent.h index fe8a74d3..825918e3 100644 --- a/include/event2/bufferevent.h +++ b/include/event2/bufferevent.h @@ -209,7 +209,7 @@ struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socke @return 0 on success, -1 on failure. */ EVENT2_EXPORT_SYMBOL -int bufferevent_socket_connect(struct bufferevent *, struct sockaddr *, int); +int bufferevent_socket_connect(struct bufferevent *, const struct sockaddr *, int); struct evdns_base; /** @@ -562,6 +562,32 @@ void bufferevent_lock(struct bufferevent *bufev); EVENT2_EXPORT_SYMBOL void bufferevent_unlock(struct bufferevent *bufev); + +/** + * Public interface to manually increase the reference count of a bufferevent + * this is useful in situations where a user may reference the bufferevent + * somewhere eles (unknown to libevent) + * + * @param bufev the bufferevent to increase the refcount on + * + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_incref(struct bufferevent *bufev); + +/** + * Public interface to manually decrement the reference count of a bufferevent + * + * Warning: make sure you know what you're doing. This is mainly used in + * conjunction with bufferevent_incref(). This will free up all data associated + * with a bufferevent if the reference count hits 0. + * + * @param bufev the bufferevent to decrement the refcount on + * + * @return 1 if the bufferevent was freed, otherwise 0 (still referenced) + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_decref(struct bufferevent *bufev); + /** Flags that can be passed into filters to let them know how to deal with the incoming data. diff --git a/include/event2/http.h b/include/event2/http.h index 4284d5fc..09fd8e1c 100644 --- a/include/event2/http.h +++ b/include/event2/http.h @@ -633,9 +633,22 @@ struct evhttp_connection *evhttp_connection_base_new( /** * Set family hint for DNS requests. */ +EVENT2_EXPORT_SYMBOL void evhttp_connection_set_family(struct evhttp_connection *evcon, int family); +#define EVHTTP_CON_REUSE_CONNECTED_ADDR 0x0008 /* reuse connection address on retry */ +/** + * Set connection flags. + * + * @see EVHTTP_CON_* + * @return 0 on success, otherwise non zero (for example if flag doesn't + * supported). + */ +EVENT2_EXPORT_SYMBOL +int evhttp_connection_set_flags(struct evhttp_connection *evcon, + int flags); + /** Takes ownership of the request object * * Can be used in a request callback to keep onto the request until @@ -728,7 +741,7 @@ void evhttp_connection_get_peer(struct evhttp_connection *evcon, char **address, ev_uint16_t *port); /** Get the remote address associated with this connection. - * extracted from getpeername(). + * extracted from getpeername() OR from nameserver. * * @return NULL if getpeername() return non success, * or connection is not connected, diff --git a/include/event2/util.h b/include/event2/util.h index b152a4b4..3936786e 100644 --- a/include/event2/util.h +++ b/include/event2/util.h @@ -233,6 +233,7 @@ extern "C" { @{ */ +#ifndef EVENT__HAVE_STDINT_H #define EV_UINT64_MAX ((((ev_uint64_t)0xffffffffUL) << 32) | 0xffffffffUL) #define EV_INT64_MAX ((((ev_int64_t) 0x7fffffffL) << 32) | 0xffffffffL) #define EV_INT64_MIN ((-EV_INT64_MAX) - 1) @@ -245,7 +246,21 @@ extern "C" { #define EV_UINT8_MAX 255 #define EV_INT8_MAX 127 #define EV_INT8_MIN ((-EV_INT8_MAX) - 1) +#else +#define EV_UINT64_MAX UINT64_MAX +#define EV_INT64_MAX INT64_MAX +#define EV_INT64_MIN INT64_MIN +#define EV_UINT32_MAX UINT32_MAX +#define EV_INT32_MAX INT32_MAX +#define EV_INT32_MIN INT32_MIN +#define EV_UINT16_MAX UINT16_MAX +#define EV_INT16_MAX INT16_MAX +#define EV_UINT8_MAX UINT8_MAX +#define EV_INT8_MAX INT8_MAX +#define EV_INT8_MIN INT8_MIN /** @} */ +#endif + /** @name Limits for SIZE_T and SSIZE_T diff --git a/kqueue.c b/kqueue.c index 7780e1f1..1f41b5a7 100644 --- a/kqueue.c +++ b/kqueue.c @@ -50,7 +50,7 @@ /* Some platforms apparently define the udata field of struct kevent as * intptr_t, whereas others define it as void*. There doesn't seem to be an * easy way to tell them apart via autoconf, so we need to use OS macros. */ -#if defined(EVENT__HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__) +#if defined(EVENT__HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__) && !defined(__CloudABI__) #define PTR_TO_UDATA(x) ((intptr_t)(x)) #define INT_TO_UDATA(x) ((intptr_t)(x)) #else @@ -260,7 +260,8 @@ kq_dispatch(struct event_base *base, struct timeval *tv) int i, n_changes, res; if (tv != NULL) { - TIMEVAL_TO_TIMESPEC(tv, &ts); + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000; ts_p = &ts; } diff --git a/listener.c b/listener.c index 172d2927..2af14e3a 100644 --- a/listener.c +++ b/listener.c @@ -421,6 +421,8 @@ listener_read_cb(evutil_socket_t fd, short what, void *p) if (lev->refcnt == 1) { int freed = listener_decref_and_unlock(lev); EVUTIL_ASSERT(freed); + + evutil_closesocket(new_fd); return; } --lev->refcnt; diff --git a/sample/dns-example.c b/sample/dns-example.c index 15e48ce4..fb705664 100644 --- a/sample/dns-example.c +++ b/sample/dns-example.c @@ -12,9 +12,14 @@ #include +#ifdef EVENT__HAVE_UNISTD_H +#include +#endif + #ifdef _WIN32 #include #include +#include #else #include #include @@ -141,34 +146,36 @@ logfn(int is_warn, const char *msg) { int main(int c, char **v) { - int idx; - int reverse = 0, servertest = 0, use_getaddrinfo = 0; + struct options { + int reverse; + int use_getaddrinfo; + int servertest; + const char *resolv_conf; + const char *ns; + }; + struct options o; + char opt; struct event_base *event_base = NULL; struct evdns_base *evdns_base = NULL; - const char *resolv_conf = NULL; - if (c<2) { - fprintf(stderr, "syntax: %s [-x] [-v] [-c resolv.conf] hostname\n", v[0]); - fprintf(stderr, "syntax: %s [-servertest]\n", v[0]); + + memset(&o, 0, sizeof(o)); + + if (c < 2) { + fprintf(stderr, "syntax: %s [-x] [-v] [-c resolv.conf] [-s ns] hostname\n", v[0]); + fprintf(stderr, "syntax: %s [-T]\n", v[0]); return 1; } - idx = 1; - while (idx < c && v[idx][0] == '-') { - if (!strcmp(v[idx], "-x")) - reverse = 1; - else if (!strcmp(v[idx], "-v")) - verbose = 1; - else if (!strcmp(v[idx], "-g")) - use_getaddrinfo = 1; - else if (!strcmp(v[idx], "-servertest")) - servertest = 1; - else if (!strcmp(v[idx], "-c")) { - if (idx + 1 < c) - resolv_conf = v[++idx]; - else - fprintf(stderr, "-c needs an argument\n"); - } else - fprintf(stderr, "Unknown option %s\n", v[idx]); - ++idx; + + while ((opt = getopt(c, v, "xvc:Ts:")) != -1) { + switch (opt) { + case 'x': o.reverse = 1; break; + case 'v': ++verbose; break; + case 'g': o.use_getaddrinfo = 1; break; + case 'T': o.servertest = 1; break; + case 'c': o.resolv_conf = optarg; break; + case 's': o.ns = optarg; break; + default : fprintf(stderr, "Unknown option %c\n", opt); break; + } } #ifdef _WIN32 @@ -182,7 +189,7 @@ main(int c, char **v) { evdns_base = evdns_base_new(event_base, EVDNS_BASE_DISABLE_WHEN_INACTIVE); evdns_set_log_fn(logfn); - if (servertest) { + if (o.servertest) { evutil_socket_t sock; struct sockaddr_in my_addr; sock = socket(PF_INET, SOCK_DGRAM, 0); @@ -200,16 +207,18 @@ main(int c, char **v) { } evdns_add_server_port_with_base(event_base, sock, 0, evdns_server_callback, NULL); } - if (idx < c) { + if (optind < c) { int res; #ifdef _WIN32 - if (resolv_conf == NULL) + if (o.resolv_conf == NULL && !o.ns) res = evdns_base_config_windows_nameservers(evdns_base); else #endif + if (o.ns) + res = evdns_base_nameserver_ip_add(evdns_base, o.ns); + else res = evdns_base_resolv_conf_parse(evdns_base, - DNS_OPTION_NAMESERVERS, - resolv_conf ? resolv_conf : "/etc/resolv.conf"); + DNS_OPTION_NAMESERVERS, o.resolv_conf); if (res < 0) { fprintf(stderr, "Couldn't configure nameservers"); @@ -218,27 +227,27 @@ main(int c, char **v) { } printf("EVUTIL_AI_CANONNAME in example = %d\n", EVUTIL_AI_CANONNAME); - for (; idx < c; ++idx) { - if (reverse) { + for (; optind < c; ++optind) { + if (o.reverse) { struct in_addr addr; - if (evutil_inet_pton(AF_INET, v[idx], &addr)!=1) { - fprintf(stderr, "Skipping non-IP %s\n", v[idx]); + if (evutil_inet_pton(AF_INET, v[optind], &addr)!=1) { + fprintf(stderr, "Skipping non-IP %s\n", v[optind]); continue; } - fprintf(stderr, "resolving %s...\n",v[idx]); - evdns_base_resolve_reverse(evdns_base, &addr, 0, main_callback, v[idx]); - } else if (use_getaddrinfo) { + fprintf(stderr, "resolving %s...\n",v[optind]); + evdns_base_resolve_reverse(evdns_base, &addr, 0, main_callback, v[optind]); + } else if (o.use_getaddrinfo) { struct evutil_addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = EVUTIL_AI_CANONNAME; - fprintf(stderr, "resolving (fwd) %s...\n",v[idx]); - evdns_getaddrinfo(evdns_base, v[idx], NULL, &hints, - gai_callback, v[idx]); + fprintf(stderr, "resolving (fwd) %s...\n",v[optind]); + evdns_getaddrinfo(evdns_base, v[optind], NULL, &hints, + gai_callback, v[optind]); } else { - fprintf(stderr, "resolving (fwd) %s...\n",v[idx]); - evdns_base_resolve_ipv4(evdns_base, v[idx], 0, main_callback, v[idx]); + fprintf(stderr, "resolving (fwd) %s...\n",v[optind]); + evdns_base_resolve_ipv4(evdns_base, v[optind], 0, main_callback, v[optind]); } } fflush(stdout); diff --git a/sample/http-connect.c b/sample/http-connect.c new file mode 100644 index 00000000..45a51c1a --- /dev/null +++ b/sample/http-connect.c @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERIFY(cond) do { \ + if (!(cond)) { \ + fprintf(stderr, "[error] %s\n", #cond); \ + } \ +} while (0); \ + +struct connect_base +{ + struct evhttp_connection *evcon; + struct evhttp_uri *location; +}; + +static void get_cb(struct evhttp_request *req, void *arg) +{ + VERIFY(req); + evbuffer_write(req->input_buffer, STDOUT_FILENO); +} + +static void connect_cb(struct evhttp_request *proxy_req, void *arg) +{ + char buffer[PATH_MAX]; + + struct connect_base *base = arg; + struct evhttp_connection *evcon = base->evcon; + struct evhttp_uri *location = base->location; + + VERIFY(proxy_req); + if (evcon) { + struct evhttp_request *req = evhttp_request_new(get_cb, NULL); + evhttp_add_header(req->output_headers, "Connection", "close"); + VERIFY(!evhttp_make_request(evcon, req, EVHTTP_REQ_GET, + evhttp_uri_join(location, buffer, PATH_MAX))); + } +} + +int main(int argc, const char **argv) +{ + char buffer[PATH_MAX]; + + struct evhttp_uri *host_port; + struct evhttp_uri *location; + struct evhttp_uri *proxy; + + struct event_base *base; + struct evhttp_connection *evcon; + struct evhttp_request *req; + + struct connect_base connect_base; + + if (argc != 3) { + printf("Usage: %s proxy url\n", argv[0]); + return 1; + } + + { + proxy = evhttp_uri_parse(argv[1]); + VERIFY(evhttp_uri_get_host(proxy)); + VERIFY(evhttp_uri_get_port(proxy) > 0); + } + { + host_port = evhttp_uri_parse(argv[2]); + evhttp_uri_set_scheme(host_port, NULL); + evhttp_uri_set_userinfo(host_port, NULL); + evhttp_uri_set_path(host_port, NULL); + evhttp_uri_set_query(host_port, NULL); + evhttp_uri_set_fragment(host_port, NULL); + VERIFY(evhttp_uri_get_host(host_port)); + VERIFY(evhttp_uri_get_port(host_port) > 0); + } + { + location = evhttp_uri_parse(argv[2]); + evhttp_uri_set_scheme(location, NULL); + evhttp_uri_set_userinfo(location, 0); + evhttp_uri_set_host(location, NULL); + evhttp_uri_set_port(location, -1); + } + + VERIFY(base = event_base_new()); + VERIFY(evcon = evhttp_connection_base_new(base, NULL, + evhttp_uri_get_host(proxy), evhttp_uri_get_port(proxy))); + connect_base = (struct connect_base){ + .evcon = evcon, + .location = location, + }; + VERIFY(req = evhttp_request_new(connect_cb, &connect_base)); + + evhttp_add_header(req->output_headers, "Connection", "keep-alive"); + evhttp_add_header(req->output_headers, "Proxy-Connection", "keep-alive"); + evutil_snprintf(buffer, PATH_MAX, "%s:%d", + evhttp_uri_get_host(host_port), evhttp_uri_get_port(host_port)); + evhttp_make_request(evcon, req, EVHTTP_REQ_CONNECT, buffer); + + event_base_dispatch(base); + evhttp_connection_free(evcon); + event_base_free(base); + evhttp_uri_free(proxy); + evhttp_uri_free(host_port); + evhttp_uri_free(location); + return 0; +} diff --git a/sample/https-client.c b/sample/https-client.c index fbd5de8c..029cd19c 100644 --- a/sample/https-client.c +++ b/sample/https-client.c @@ -96,22 +96,19 @@ static void syntax(void) { fputs("Syntax:\n", stderr); - fputs(" https-client -url [-data data-file.bin] [-ignore-cert] [-retries num]\n", stderr); + fputs(" https-client -url [-data data-file.bin] [-ignore-cert] [-retries num] [-timeout sec] [-crt crt]\n", stderr); fputs("Example:\n", stderr); fputs(" https-client -url https://ip.appspot.com/\n", stderr); - - exit(1); } static void -die(const char *msg) +err(const char *msg) { fputs(msg, stderr); - exit(1); } static void -die_openssl(const char *func) +err_openssl(const char *func) { fprintf (stderr, "%s failed:\n", func); @@ -190,22 +187,26 @@ main(int argc, char **argv) { int r; - struct evhttp_uri *http_uri; + struct evhttp_uri *http_uri = NULL; const char *url = NULL, *data_file = NULL; + const char *crt = "/etc/ssl/certs/ca-certificates.crt"; const char *scheme, *host, *path, *query; char uri[256]; int port; int retries = 0; + int timeout = -1; - SSL_CTX *ssl_ctx; - SSL *ssl; + SSL_CTX *ssl_ctx = NULL; + SSL *ssl = NULL; struct bufferevent *bev; - struct evhttp_connection *evcon; + struct evhttp_connection *evcon = NULL; struct evhttp_request *req; struct evkeyvalq *output_headers; - struct evbuffer * output_buffer; + struct evbuffer *output_buffer; int i; + int ret = 0; + enum { HTTP, HTTPS } type = HTTP; for (i = 1; i < argc; i++) { if (!strcmp("-url", argv[i])) { @@ -213,6 +214,14 @@ main(int argc, char **argv) url = argv[i + 1]; } else { syntax(); + goto error; + } + } else if (!strcmp("-crt", argv[i])) { + if (i < argc - 1) { + crt = argv[i + 1]; + } else { + syntax(); + goto error; } } else if (!strcmp("-ignore-cert", argv[i])) { ignore_cert = 1; @@ -221,20 +230,31 @@ main(int argc, char **argv) data_file = argv[i + 1]; } else { syntax(); + goto error; } } else if (!strcmp("-retries", argv[i])) { if (i < argc - 1) { retries = atoi(argv[i + 1]); } else { syntax(); + goto error; + } + } else if (!strcmp("-timeout", argv[i])) { + if (i < argc - 1) { + timeout = atoi(argv[i + 1]); + } else { + syntax(); + goto error; } } else if (!strcmp("-help", argv[i])) { syntax(); + goto error; } } if (!url) { syntax(); + goto error; } #ifdef _WIN32 @@ -248,25 +268,28 @@ main(int argc, char **argv) err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { printf("WSAStartup failed with error: %d\n", err); - return 1; + goto error; } } #endif // _WIN32 http_uri = evhttp_uri_parse(url); if (http_uri == NULL) { - die("malformed url"); + err("malformed url"); + goto error; } scheme = evhttp_uri_get_scheme(http_uri); if (scheme == NULL || (strcasecmp(scheme, "https") != 0 && strcasecmp(scheme, "http") != 0)) { - die("url must be http or https"); + err("url must be http or https"); + goto error; } host = evhttp_uri_get_host(http_uri); if (host == NULL) { - die("url must have a host"); + err("url must have a host"); + goto error; } port = evhttp_uri_get_port(http_uri); @@ -275,7 +298,7 @@ main(int argc, char **argv) } path = evhttp_uri_get_path(http_uri); - if (path == NULL) { + if (strlen(path) == 0) { path = "/"; } @@ -297,23 +320,26 @@ main(int argc, char **argv) * automatically on first use of random number generator. */ r = RAND_poll(); if (r == 0) { - die_openssl("RAND_poll"); + err_openssl("RAND_poll"); + goto error; } /* Create a new OpenSSL context */ ssl_ctx = SSL_CTX_new(SSLv23_method()); - if (!ssl_ctx) - die_openssl("SSL_CTX_new"); + if (!ssl_ctx) { + err_openssl("SSL_CTX_new"); + goto error; + } - #ifndef _WIN32 +#ifndef _WIN32 /* TODO: Add certificate loading on Windows as well */ /* Attempt to use the system's trusted root certificates. * (This path is only valid for Debian-based systems.) */ - if (1 != SSL_CTX_load_verify_locations(ssl_ctx, - "/etc/ssl/certs/ca-certificates.crt", - NULL)) - die_openssl("SSL_CTX_load_verify_locations"); + if (1 != SSL_CTX_load_verify_locations(ssl_ctx, crt, NULL)) { + err_openssl("SSL_CTX_load_verify_locations"); + goto error; + } /* Ask OpenSSL to verify the server certificate. Note that this * does NOT include verifying that the hostname is correct. * So, by itself, this means anyone with any legitimate @@ -336,21 +362,22 @@ main(int argc, char **argv) * OpenSSL's built-in routine which would have been called if * we hadn't set the callback. Therefore, we're just * "wrapping" OpenSSL's routine, not replacing it. */ - SSL_CTX_set_cert_verify_callback (ssl_ctx, cert_verify_callback, + SSL_CTX_set_cert_verify_callback(ssl_ctx, cert_verify_callback, (void *) host); - #endif // not _WIN32 +#endif // not _WIN32 // Create event base base = event_base_new(); if (!base) { perror("event_base_new()"); - return 1; + goto error; } // Create OpenSSL bufferevent and stack evhttp on top of it ssl = SSL_new(ssl_ctx); if (ssl == NULL) { - die_openssl("SSL_new()"); + err_openssl("SSL_new()"); + goto error; } #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME @@ -361,6 +388,7 @@ main(int argc, char **argv) if (strcasecmp(scheme, "http") == 0) { bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); } else { + type = HTTPS; bev = bufferevent_openssl_socket_new(base, -1, ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); @@ -368,7 +396,7 @@ main(int argc, char **argv) if (bev == NULL) { fprintf(stderr, "bufferevent_openssl_socket_new() failed\n"); - return 1; + goto error; } bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); @@ -379,18 +407,21 @@ main(int argc, char **argv) host, port); if (evcon == NULL) { fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n"); - return 1; + goto error; } if (retries > 0) { evhttp_connection_set_retries(evcon, retries); } + if (timeout >= 0) { + evhttp_connection_set_timeout(evcon, timeout); + } // Fire off the request req = evhttp_request_new(http_request_done, bev); if (req == NULL) { fprintf(stderr, "evhttp_request_new() failed\n"); - return 1; + goto error; } output_headers = evhttp_request_get_output_headers(req); @@ -408,6 +439,7 @@ main(int argc, char **argv) if (!f) { syntax(); + goto error; } output_buffer = evhttp_request_get_output_buffer(req); @@ -423,17 +455,40 @@ main(int argc, char **argv) r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri); if (r != 0) { fprintf(stderr, "evhttp_make_request() failed\n"); - return 1; + goto error; } event_base_dispatch(base); + goto cleanup; - evhttp_connection_free(evcon); +error: + ret = 1; +cleanup: + if (evcon) + evhttp_connection_free(evcon); + if (http_uri) + evhttp_uri_free(http_uri); event_base_free(base); + if (ssl_ctx) + SSL_CTX_free(ssl_ctx); + if (type == HTTP && ssl) + SSL_free(ssl); + EVP_cleanup(); + ERR_free_strings(); + +#ifdef EVENT__HAVE_ERR_REMOVE_THREAD_STATE + ERR_remove_thread_state(NULL); +#else + ERR_remove_state(0); +#endif + CRYPTO_cleanup_all_ex_data(); + + sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); + #ifdef _WIN32 WSACleanup(); #endif - return 0; + return ret; } diff --git a/sample/include.am b/sample/include.am index 75f87c70..d1a7242f 100644 --- a/sample/include.am +++ b/sample/include.am @@ -9,6 +9,7 @@ SAMPLES = \ sample/event-read-fifo \ sample/hello-world \ sample/http-server \ + sample/http-connect \ sample/signal-test \ sample/time-test @@ -48,6 +49,5 @@ sample_hello_world_SOURCES = sample/hello-world.c sample_hello_world_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la sample_http_server_SOURCES = sample/http-server.c sample_http_server_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la - - - +sample_http_connect_SOURCES = sample/http-connect.c +sample_http_connect_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la diff --git a/sample/openssl_hostname_validation.c b/sample/openssl_hostname_validation.c index b5adc67b..00e63d1e 100644 --- a/sample/openssl_hostname_validation.c +++ b/sample/openssl_hostname_validation.c @@ -41,6 +41,7 @@ SOFTWARE. #include #include +#include #include "openssl_hostname_validation.h" #include "hostcheck.h" diff --git a/test/bench.c b/test/bench.c index 922a743a..214479c1 100644 --- a/test/bench.c +++ b/test/bench.c @@ -74,7 +74,7 @@ static void read_cb(evutil_socket_t fd, short which, void *arg) { ev_intptr_t idx = (ev_intptr_t) arg, widx = idx + 1; - u_char ch; + unsigned char ch; ev_ssize_t n; n = recv(fd, (char*)&ch, sizeof(ch), 0); diff --git a/test/bench_httpclient.c b/test/bench_httpclient.c index 22f174da..bcddc95f 100644 --- a/test/bench_httpclient.c +++ b/test/bench_httpclient.c @@ -151,7 +151,7 @@ launch_request(void) } frob_socket(sock); if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0) { - int e = errno; + int e = evutil_socket_geterror(sock); if (! EVUTIL_ERR_CONNECT_RETRIABLE(e)) { evutil_closesocket(sock); return -1; @@ -183,6 +183,11 @@ main(int argc, char **argv) double throughput; resource = "/ref"; +#ifdef _WIN32 + WSADATA WSAData; + WSAStartup(0x101, &WSAData); +#endif + setvbuf(stdout, NULL, _IONBF, 0); base = event_base_new(); @@ -226,5 +231,9 @@ main(int argc, char **argv) (double)(usec/1000) / total_n_handled, (I64_TYP)total_n_bytes, n_errors); +#ifdef _WIN32 + WSACleanup(); +#endif + return 0; } diff --git a/test/regress.c b/test/regress.c index 399ba2fb..4d170820 100644 --- a/test/regress.c +++ b/test/regress.c @@ -2290,7 +2290,7 @@ end: static void evtag_fuzz(void *ptr) { - u_char buffer[4096]; + unsigned char buffer[4096]; struct evbuffer *tmp = evbuffer_new(); struct timeval tv; int i, j; diff --git a/test/regress.h b/test/regress.h index bbfefe67..de1aed30 100644 --- a/test/regress.h +++ b/test/regress.h @@ -129,6 +129,14 @@ long timeval_msec_diff(const struct timeval *start, const struct timeval *end); pid_t regress_fork(void); #endif +#ifdef EVENT__HAVE_OPENSSL +#include +EVP_PKEY *ssl_getkey(void); +X509 *ssl_getcert(void); +SSL_CTX *get_ssl_ctx(void); +void init_ssl(void); +#endif + #ifdef __cplusplus } #endif diff --git a/test/regress_buffer.c b/test/regress_buffer.c index 4d6a5de2..957e59f1 100644 --- a/test/regress_buffer.c +++ b/test/regress_buffer.c @@ -198,7 +198,7 @@ test_evbuffer(void *ptr) tt_assert(evbuffer_get_length(evb_two) == 0); tt_assert(evbuffer_get_length(evb) == 7); - tt_assert(!memcmp((char*)EVBUFFER_DATA(evb), "1/hello", 7) != 0); + tt_assert(!memcmp((char*)EVBUFFER_DATA(evb), "1/hello", 7)); memset(buffer, 0, sizeof(buffer)); evbuffer_add(evb, buffer, sizeof(buffer)); @@ -1292,7 +1292,7 @@ test_evbuffer_iterative(void *ptr) static void test_evbuffer_find(void *ptr) { - u_char* p; + unsigned char* p; const char* test1 = "1234567890\r\n"; const char* test2 = "1234567890\r"; #define EVBUFFER_INITIAL_LENGTH 256 @@ -1303,13 +1303,13 @@ test_evbuffer_find(void *ptr) tt_assert(buf); /* make sure evbuffer_find doesn't match past the end of the buffer */ - evbuffer_add(buf, (u_char*)test1, strlen(test1)); + evbuffer_add(buf, (unsigned char*)test1, strlen(test1)); evbuffer_validate(buf); evbuffer_drain(buf, strlen(test1)); evbuffer_validate(buf); - evbuffer_add(buf, (u_char*)test2, strlen(test2)); + evbuffer_add(buf, (unsigned char*)test2, strlen(test2)); evbuffer_validate(buf); - p = evbuffer_find(buf, (u_char*)"\r\n", 2); + p = evbuffer_find(buf, (unsigned char*)"\r\n", 2); tt_want(p == NULL); /* @@ -1321,13 +1321,13 @@ test_evbuffer_find(void *ptr) for (i = 0; i < EVBUFFER_INITIAL_LENGTH; ++i) test3[i] = 'a'; test3[EVBUFFER_INITIAL_LENGTH - 1] = 'x'; - evbuffer_add(buf, (u_char *)test3, EVBUFFER_INITIAL_LENGTH); + evbuffer_add(buf, (unsigned char *)test3, EVBUFFER_INITIAL_LENGTH); evbuffer_validate(buf); - p = evbuffer_find(buf, (u_char *)"xy", 2); + p = evbuffer_find(buf, (unsigned char *)"xy", 2); tt_want(p == NULL); /* simple test for match at end of allocated buffer */ - p = evbuffer_find(buf, (u_char *)"ax", 2); + p = evbuffer_find(buf, (unsigned char *)"ax", 2); tt_assert(p != NULL); tt_want(strncmp((char*)p, "ax", 2) == 0); diff --git a/test/regress_bufferevent.c b/test/regress_bufferevent.c index a87e02ed..68e68764 100644 --- a/test/regress_bufferevent.c +++ b/test/regress_bufferevent.c @@ -123,11 +123,12 @@ errorcb(struct bufferevent *bev, short what, void *arg) } static void -test_bufferevent_impl(int use_pair) +test_bufferevent_impl(int use_pair, int flush) { struct bufferevent *bev1 = NULL, *bev2 = NULL; char buffer[8333]; int i; + int expected = 2; if (use_pair) { struct bufferevent *pair[2]; @@ -171,6 +172,9 @@ test_bufferevent_impl(int use_pair) buffer[i] = i; bufferevent_write(bev1, buffer, sizeof(buffer)); + if (flush >= 0) { + tt_int_op(bufferevent_flush(bev1, EV_WRITE, flush), >=, 0); + } event_dispatch(); @@ -178,23 +182,26 @@ test_bufferevent_impl(int use_pair) tt_ptr_op(bufferevent_pair_get_partner(bev1), ==, NULL); bufferevent_free(bev1); - if (test_ok != 2) + /** Only pair call errorcb for BEV_FINISHED */ + if (use_pair && flush == BEV_FINISHED) { + expected = -1; + } + if (test_ok != expected) test_ok = 0; end: ; } -static void -test_bufferevent(void) -{ - test_bufferevent_impl(0); -} +static void test_bufferevent(void) { test_bufferevent_impl(0, -1); } +static void test_bufferevent_pair(void) { test_bufferevent_impl(1, -1); } -static void -test_bufferevent_pair(void) -{ - test_bufferevent_impl(1); -} +static void test_bufferevent_flush_normal(void) { test_bufferevent_impl(0, BEV_NORMAL); } +static void test_bufferevent_flush_flush(void) { test_bufferevent_impl(0, BEV_FLUSH); } +static void test_bufferevent_flush_finished(void) { test_bufferevent_impl(0, BEV_FINISHED); } + +static void test_bufferevent_pair_flush_normal(void) { test_bufferevent_impl(1, BEV_NORMAL); } +static void test_bufferevent_pair_flush_flush(void) { test_bufferevent_impl(1, BEV_FLUSH); } +static void test_bufferevent_pair_flush_finished(void) { test_bufferevent_impl(1, BEV_FINISHED); } #if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) /** @@ -245,7 +252,6 @@ static void trace_lock_free(void *lock_, unsigned locktype) { lock_wrapper *lock = lu_find(lock_); if (!lock || lock->status == FREE || lock->locked) { - __asm__("int3"); TT_FAIL(("lock: free error")); } else { lock->status = FREE; @@ -278,6 +284,9 @@ static void lock_unlock_free_thread_cbs(void) { event_base_free(NULL); + if (libevent_tests_running_in_debug_mode) + libevent_global_shutdown(); + /** drop immutable flag */ evthread_set_lock_callbacks(NULL); /** avoid calling of event_global_setup_locks_() for new cbs */ @@ -311,6 +320,9 @@ static int use_lock_unlock_profiler(void) } static void free_lock_unlock_profiler(struct basic_test_data *data) { + /** fix "held_by" for kqueue */ + evthread_set_lock_callbacks(NULL); + lock_unlock_free_thread_cbs(); free(lu_base.locks); data->base = NULL; @@ -594,6 +606,7 @@ static int bufferevent_connect_test_flags = 0; static int bufferevent_trigger_test_flags = 0; static int n_strings_read = 0; static int n_reads_invoked = 0; +static int n_events_invoked = 0; #define TEST_STR "Now is the time for all good events to signal for " \ "the good of their protocol" @@ -613,6 +626,31 @@ end: ; } +static int +fake_listener_create(struct sockaddr_in *localhost) +{ + struct sockaddr *sa = (struct sockaddr *)localhost; + evutil_socket_t fd = -1; + ev_socklen_t slen = sizeof(*localhost); + + memset(localhost, 0, sizeof(*localhost)); + localhost->sin_port = 0; /* have the kernel pick a port */ + localhost->sin_addr.s_addr = htonl(0x7f000001L); + localhost->sin_family = AF_INET; + + /* bind, but don't listen or accept. should trigger + "Connection refused" reliably on most platforms. */ + fd = socket(localhost->sin_family, SOCK_STREAM, 0); + tt_assert(fd >= 0); + tt_assert(bind(fd, sa, slen) == 0); + tt_assert(getsockname(fd, sa, &slen) == 0); + + return fd; + +end: + return -1; +} + static void reader_eventcb(struct bufferevent *bev, short what, void *ctx) { @@ -642,6 +680,14 @@ end: ; } +static void +reader_eventcb_simple(struct bufferevent *bev, short what, void *ctx) +{ + TT_BLATHER(("Read eventcb simple invoked on %d.", + (int)bufferevent_getfd(bev))); + n_events_invoked++; +} + static void reader_readcb(struct bufferevent *bev, void *ctx) { @@ -727,6 +773,45 @@ end: bufferevent_free(bev2); } +static void +test_bufferevent_connect_fail_eventcb(void *arg) +{ + struct basic_test_data *data = arg; + int flags = BEV_OPT_CLOSE_ON_FREE | (long)data->setup_data; + struct bufferevent *bev = NULL; + struct evconnlistener *lev = NULL; + struct sockaddr_in localhost; + ev_socklen_t slen = sizeof(localhost); + evutil_socket_t fake_listener = -1; + + fake_listener = fake_listener_create(&localhost); + + tt_int_op(n_events_invoked, ==, 0); + + bev = bufferevent_socket_new(data->base, -1, flags); + tt_assert(bev); + bufferevent_setcb(bev, reader_readcb, reader_readcb, + reader_eventcb_simple, data->base); + bufferevent_enable(bev, EV_READ|EV_WRITE); + tt_int_op(n_events_invoked, ==, 0); + tt_int_op(n_reads_invoked, ==, 0); + /** @see also test_bufferevent_connect_fail() */ + bufferevent_socket_connect(bev, (struct sockaddr *)&localhost, slen); + tt_int_op(n_events_invoked, ==, 0); + tt_int_op(n_reads_invoked, ==, 0); + event_base_dispatch(data->base); + tt_int_op(n_events_invoked, ==, 1); + tt_int_op(n_reads_invoked, ==, 0); + +end: + if (lev) + evconnlistener_free(lev); + if (bev) + bufferevent_free(bev); + if (fake_listener >= 0) + evutil_closesocket(fake_listener); +} + static void want_fail_eventcb(struct bufferevent *bev, short what, void *ctx) { @@ -762,34 +847,23 @@ test_bufferevent_connect_fail(void *arg) { struct basic_test_data *data = (struct basic_test_data *)arg; struct bufferevent *bev=NULL; - struct sockaddr_in localhost; - struct sockaddr *sa = (struct sockaddr*)&localhost; - evutil_socket_t fake_listener = -1; - ev_socklen_t slen = sizeof(localhost); struct event close_listener_event; int close_listener_event_added = 0; struct timeval one_second = { 1, 0 }; + struct sockaddr_in localhost; + ev_socklen_t slen = sizeof(localhost); + evutil_socket_t fake_listener = -1; int r; test_ok = 0; - memset(&localhost, 0, sizeof(localhost)); - localhost.sin_port = 0; /* have the kernel pick a port */ - localhost.sin_addr.s_addr = htonl(0x7f000001L); - localhost.sin_family = AF_INET; - - /* bind, but don't listen or accept. should trigger - "Connection refused" reliably on most platforms. */ - fake_listener = socket(localhost.sin_family, SOCK_STREAM, 0); - tt_assert(fake_listener >= 0); - tt_assert(bind(fake_listener, sa, slen) == 0); - tt_assert(getsockname(fake_listener, sa, &slen) == 0); + fake_listener = fake_listener_create(&localhost); bev = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); tt_assert(bev); bufferevent_setcb(bev, NULL, NULL, want_fail_eventcb, data->base); - r = bufferevent_socket_connect(bev, sa, slen); + r = bufferevent_socket_connect(bev, (struct sockaddr *)&localhost, slen); /* XXXX we'd like to test the '0' case everywhere, but FreeBSD tells * detects the error immediately, which is not really wrong of it. */ tt_want(r == 0 || r == -1); @@ -1084,10 +1158,35 @@ end: bufferevent_free(bev); } +static void +test_bufferevent_socket_filter_inactive(void *arg) +{ + struct basic_test_data *data = arg; + struct bufferevent *bev = NULL, *bevf = NULL; + + bev = bufferevent_socket_new(data->base, -1, 0); + tt_assert(bev); + bevf = bufferevent_filter_new(bev, NULL, NULL, 0, NULL, NULL); + tt_assert(bevf); + +end: + if (bevf) + bufferevent_free(bevf); + if (bev) + bufferevent_free(bev); +} + + struct testcase_t bufferevent_testcases[] = { LEGACY(bufferevent, TT_ISOLATED), LEGACY(bufferevent_pair, TT_ISOLATED), + LEGACY(bufferevent_flush_normal, TT_ISOLATED), + LEGACY(bufferevent_flush_flush, TT_ISOLATED), + LEGACY(bufferevent_flush_finished, TT_ISOLATED), + LEGACY(bufferevent_pair_flush_normal, TT_ISOLATED), + LEGACY(bufferevent_pair_flush_flush, TT_ISOLATED), + LEGACY(bufferevent_pair_flush_finished, TT_ISOLATED), #if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) { "bufferevent_pair_release_lock", test_bufferevent_pair_release_lock, TT_FORK|TT_ISOLATED|TT_NEED_THREADS|TT_NEED_BASE|TT_LEGACY, @@ -1135,12 +1234,26 @@ struct testcase_t bufferevent_testcases[] = { { "bufferevent_zlib", NULL, TT_SKIP, NULL, NULL }, #endif + { "bufferevent_connect_fail_eventcb_defer", + test_bufferevent_connect_fail_eventcb, + TT_FORK|TT_NEED_BASE, &basic_setup, (void*)BEV_OPT_DEFER_CALLBACKS }, + { "bufferevent_connect_fail_eventcb", + test_bufferevent_connect_fail_eventcb, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + + { "bufferevent_socket_filter_inactive", + test_bufferevent_socket_filter_inactive, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + END_OF_TESTCASES, }; struct testcase_t bufferevent_iocp_testcases[] = { LEGACY(bufferevent, TT_ISOLATED|TT_ENABLE_IOCP), + LEGACY(bufferevent_flush_normal, TT_ISOLATED), + LEGACY(bufferevent_flush_flush, TT_ISOLATED), + LEGACY(bufferevent_flush_finished, TT_ISOLATED), LEGACY(bufferevent_watermarks, TT_ISOLATED|TT_ENABLE_IOCP), LEGACY(bufferevent_filters, TT_ISOLATED|TT_ENABLE_IOCP), { "bufferevent_connect", test_bufferevent_connect, @@ -1159,5 +1272,13 @@ struct testcase_t bufferevent_iocp_testcases[] = { TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, (void*)"unset_connectex" }, + { "bufferevent_connect_fail_eventcb_defer", + test_bufferevent_connect_fail_eventcb, + TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, + (void*)BEV_OPT_DEFER_CALLBACKS }, + { "bufferevent_connect_fail", + test_bufferevent_connect_fail_eventcb, + TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, NULL }, + END_OF_TESTCASES, }; diff --git a/test/regress_dns.c b/test/regress_dns.c index 6d6b4844..18736362 100644 --- a/test/regress_dns.c +++ b/test/regress_dns.c @@ -72,6 +72,8 @@ #include "regress.h" #include "regress_testutils.h" +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + static int dns_ok = 0; static int dns_got_cancel = 0; static int dns_err = 0; @@ -511,25 +513,25 @@ generic_dns_callback(int result, char type, int count, int ttl, void *addresses, } static struct regress_dns_server_table search_table[] = { - { "host.a.example.com", "err", "3", 0 }, - { "host.b.example.com", "err", "3", 0 }, - { "host.c.example.com", "A", "11.22.33.44", 0 }, - { "host2.a.example.com", "err", "3", 0 }, - { "host2.b.example.com", "A", "200.100.0.100", 0 }, - { "host2.c.example.com", "err", "3", 0 }, - { "hostn.a.example.com", "errsoa", "0", 0 }, - { "hostn.b.example.com", "errsoa", "3", 0 }, - { "hostn.c.example.com", "err", "0", 0 }, + { "host.a.example.com", "err", "3", 0, 0 }, + { "host.b.example.com", "err", "3", 0, 0 }, + { "host.c.example.com", "A", "11.22.33.44", 0, 0 }, + { "host2.a.example.com", "err", "3", 0, 0 }, + { "host2.b.example.com", "A", "200.100.0.100", 0, 0 }, + { "host2.c.example.com", "err", "3", 0, 0 }, + { "hostn.a.example.com", "errsoa", "0", 0, 0 }, + { "hostn.b.example.com", "errsoa", "3", 0, 0 }, + { "hostn.c.example.com", "err", "0", 0, 0 }, - { "host", "err", "3", 0 }, - { "host2", "err", "3", 0 }, - { "*", "err", "3", 0 }, - { NULL, NULL, NULL, 0 } + { "host", "err", "3", 0, 0 }, + { "host2", "err", "3", 0, 0 }, + { "*", "err", "3", 0, 0 }, + { NULL, NULL, NULL, 0, 0 } }; - static void -dns_search_test(void *arg) +dns_search_test_impl(void *arg, int lower) { + struct regress_dns_server_table table[ARRAY_SIZE(search_table)]; struct basic_test_data *data = arg; struct event_base *base = data->base; struct evdns_base *dns = NULL; @@ -537,8 +539,14 @@ dns_search_test(void *arg) char buf[64]; struct generic_dns_callback_result r[8]; + size_t i; - tt_assert(regress_dnsserver(base, &portnum, search_table)); + for (i = 0; i < ARRAY_SIZE(table); ++i) { + table[i] = search_table[i]; + table[i].lower = lower; + } + + tt_assert(regress_dnsserver(base, &portnum, table)); evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum); dns = evdns_base_new(base, 0); @@ -548,7 +556,7 @@ dns_search_test(void *arg) evdns_base_search_add(dns, "b.example.com"); evdns_base_search_add(dns, "c.example.com"); - n_replies_left = sizeof(r)/sizeof(r[0]); + n_replies_left = ARRAY_SIZE(r); exit_base = base; evdns_base_resolve_ipv4(dns, "host", 0, generic_dns_callback, &r[0]); @@ -584,6 +592,16 @@ end: regress_clean_dnsserver(); } +static void +dns_search_test(void *arg) +{ + return dns_search_test_impl(arg, 0); +} +static void +dns_search_lower_test(void *arg) +{ + return dns_search_test_impl(arg, 1); +} static int request_count = 0; static struct evdns_request *current_req = NULL; @@ -776,13 +794,13 @@ static struct regress_dns_server_table internal_error_table[] = { XXXX we should reissue under a much wider set of circumstances! */ - { "foof.example.com", "err", "4", 0 }, - { NULL, NULL, NULL, 0 } + { "foof.example.com", "err", "4", 0, 0 }, + { NULL, NULL, NULL, 0, 0 } }; static struct regress_dns_server_table reissue_table[] = { - { "foof.example.com", "A", "240.15.240.15", 0 }, - { NULL, NULL, NULL, 0 } + { "foof.example.com", "A", "240.15.240.15", 0, 0 }, + { NULL, NULL, NULL, 0, 0 } }; static void @@ -1672,7 +1690,6 @@ test_getaddrinfo_async(void *arg) end: if (local_outcome.ai) evutil_freeaddrinfo(local_outcome.ai); -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) for (i=0;i<(int)ARRAY_SIZE(a_out);++i) { if (a_out[i].ai) evutil_freeaddrinfo(a_out[i].ai); @@ -1885,29 +1902,22 @@ dbg_leak_resume(void *env_, int cancel, int send_err_shutdown) tt_assert(!evdns_base_resume(env->dns_base)); } + event_base_loop(env->base, EVLOOP_NONBLOCK); /** - * Because we don't cancel request, - * and want our callback to recieve DNS_ERR_SHUTDOWN, - * we use deferred callback, and there was + * Because we don't cancel request, and want our callback to recieve + * DNS_ERR_SHUTDOWN, we use deferred callback, and there was: * - one extra malloc(), * @see reply_schedule_callback() * - and one missing free * @see request_finished() (req->handle->pending_cb = 1) - * than we don't need to count in testleak_cleanup() - * - * So just decrement allocated_chunks to 2, - * like we already take care about it. + * than we don't need to count in testleak_cleanup(), but we can clean them + * if we will run loop once again, but *after* evdns base freed. */ - if (!cancel && send_err_shutdown) { - allocated_chunks -= 2; - } - + evdns_base_free(env->dns_base, send_err_shutdown); + env->dns_base = 0; event_base_loop(env->base, EVLOOP_NONBLOCK); end: - evdns_base_free(env->dns_base, send_err_shutdown); - env->dns_base = 0; - event_base_free(env->base); env->base = 0; } @@ -1998,6 +2008,91 @@ end: evutil_closesocket(fd); } +static void +dns_client_fail_requests_test(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evdns_base *dns = NULL; + struct evdns_server_port *dns_port = NULL; + ev_uint16_t portnum = 0; + char buf[64]; + + struct generic_dns_callback_result r[20]; + int i; + + dns_port = regress_get_dnsserver(base, &portnum, NULL, + regress_dns_server_cb, reissue_table); + tt_assert(dns_port); + + evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum); + + dns = evdns_base_new(base, EVDNS_BASE_DISABLE_WHEN_INACTIVE); + tt_assert(!evdns_base_nameserver_ip_add(dns, buf)); + + for (i = 0; i < 20; ++i) + evdns_base_resolve_ipv4(dns, "foof.example.com", 0, generic_dns_callback, &r[i]); + + n_replies_left = 20; + exit_base = base; + + evdns_base_free(dns, 1 /** fail requests */); + /** run defered callbacks, to trigger UAF */ + event_base_dispatch(base); + + tt_int_op(n_replies_left, ==, 0); + for (i = 0; i < 20; ++i) + tt_int_op(r[i].result, ==, DNS_ERR_SHUTDOWN); + +end: + evdns_close_server_port(dns_port); +} + +static void +getaddrinfo_cb(int err, struct evutil_addrinfo *res, void *ptr) +{ + generic_dns_callback(err, 0, 0, 0, NULL, ptr); +} +static void +dns_client_fail_requests_getaddrinfo_test(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evdns_base *dns = NULL; + struct evdns_server_port *dns_port = NULL; + ev_uint16_t portnum = 0; + char buf[64]; + + struct generic_dns_callback_result r[20]; + int i; + + dns_port = regress_get_dnsserver(base, &portnum, NULL, + regress_dns_server_cb, reissue_table); + tt_assert(dns_port); + + evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum); + + dns = evdns_base_new(base, EVDNS_BASE_DISABLE_WHEN_INACTIVE); + tt_assert(!evdns_base_nameserver_ip_add(dns, buf)); + + for (i = 0; i < 20; ++i) + tt_assert(evdns_getaddrinfo(dns, "foof.example.com", "http", NULL, getaddrinfo_cb, &r[i])); + + n_replies_left = 20; + exit_base = base; + + evdns_base_free(dns, 1 /** fail requests */); + /** run defered callbacks, to trigger UAF */ + event_base_dispatch(base); + + tt_int_op(n_replies_left, ==, 0); + for (i = 0; i < 20; ++i) + tt_int_op(r[i].result, ==, EVUTIL_EAI_FAIL); + +end: + evdns_close_server_port(dns_port); +} + #define DNS_LEGACY(name, flags) \ { #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \ @@ -2010,6 +2105,7 @@ struct testcase_t dns_testcases[] = { DNS_LEGACY(gethostbyaddr, TT_FORK|TT_NEED_BASE|TT_NEED_DNS|TT_OFF_BY_DEFAULT), { "resolve_reverse", dns_resolve_reverse, TT_FORK|TT_OFF_BY_DEFAULT, NULL, NULL }, { "search", dns_search_test, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "search_lower", dns_search_lower_test, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, { "search_cancel", dns_search_cancel_test, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, { "retry", dns_retry_test, TT_FORK|TT_NEED_BASE|TT_NO_LOGS, &basic_setup, NULL }, @@ -2044,6 +2140,12 @@ struct testcase_t dns_testcases[] = { TT_FORK, &testleak_funcs, NULL }, #endif + { "client_fail_requests", dns_client_fail_requests_test, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "client_fail_requests_getaddrinfo", + dns_client_fail_requests_getaddrinfo_test, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + END_OF_TESTCASES }; diff --git a/test/regress_http.c b/test/regress_http.c index 35f6dd76..cbe7aea3 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -58,7 +58,9 @@ #include "event2/http.h" #include "event2/buffer.h" #include "event2/bufferevent.h" +#include "event2/bufferevent_ssl.h" #include "event2/util.h" +#include "event2/listener.h" #include "log-internal.h" #include "http-internal.h" #include "regress.h" @@ -84,6 +86,7 @@ static char const BASIC_REQUEST_BODY[] = "This is funny"; IMPL_HTTP_REQUEST_ERROR_CB(cancel, EVREQ_HTTP_REQUEST_CANCEL) static void http_basic_cb(struct evhttp_request *req, void *arg); +static void http_large_cb(struct evhttp_request *req, void *arg); static void http_chunked_cb(struct evhttp_request *req, void *arg); static void http_post_cb(struct evhttp_request *req, void *arg); static void http_put_cb(struct evhttp_request *req, void *arg); @@ -94,11 +97,14 @@ static void http_badreq_cb(struct evhttp_request *req, void *arg); static void http_dispatcher_cb(struct evhttp_request *req, void *arg); static void http_on_complete_cb(struct evhttp_request *req, void *arg); +#define HTTP_BIND_IPV6 1 +#define HTTP_BIND_SSL 2 static int -http_bind(struct evhttp *myhttp, ev_uint16_t *pport, int ipv6) +http_bind(struct evhttp *myhttp, ev_uint16_t *pport, int mask) { int port; struct evhttp_bound_socket *sock; + int ipv6 = mask & HTTP_BIND_IPV6; if (ipv6) sock = evhttp_bind_socket_with_handle(myhttp, "::1", *pport); @@ -120,19 +126,40 @@ http_bind(struct evhttp *myhttp, ev_uint16_t *pport, int ipv6) return 0; } +#ifdef EVENT__HAVE_OPENSSL +static struct bufferevent * +https_bev(struct event_base *base, void *arg) +{ + SSL *ssl = SSL_new(get_ssl_ctx()); + + SSL_use_certificate(ssl, ssl_getcert()); + SSL_use_PrivateKey(ssl, ssl_getkey()); + + return bufferevent_openssl_socket_new( + base, -1, ssl, BUFFEREVENT_SSL_ACCEPTING, + BEV_OPT_CLOSE_ON_FREE); +} +#endif static struct evhttp * -http_setup(ev_uint16_t *pport, struct event_base *base, int ipv6) +http_setup(ev_uint16_t *pport, struct event_base *base, int mask) { struct evhttp *myhttp; /* Try a few different ports */ myhttp = evhttp_new(base); - if (http_bind(myhttp, pport, ipv6) < 0) + if (http_bind(myhttp, pport, mask) < 0) return NULL; +#ifdef EVENT__HAVE_OPENSSL + if (mask & HTTP_BIND_SSL) { + init_ssl(); + evhttp_set_bevcb(myhttp, https_bev, NULL); + } +#endif /* Register a callback for certain types of requests */ evhttp_set_cb(myhttp, "/test", http_basic_cb, base); + evhttp_set_cb(myhttp, "/large", http_large_cb, base); evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, base); evhttp_set_cb(myhttp, "/streamed", http_chunked_cb, base); evhttp_set_cb(myhttp, "/postit", http_post_cb, base); @@ -151,7 +178,7 @@ http_setup(ev_uint16_t *pport, struct event_base *base, int ipv6) #endif static evutil_socket_t -http_connect(const char *address, u_short port) +http_connect(const char *address, unsigned short port) { /* Stupid code for connecting */ struct evutil_addrinfo ai, *aitop; @@ -276,6 +303,9 @@ http_writecb(struct bufferevent *bev, void *arg) static void http_errorcb(struct bufferevent *bev, short what, void *arg) { + /** For ssl */ + if (what & BEV_EVENT_CONNECTED) + return; test_ok = -2; event_base_loopexit(arg, NULL); } @@ -329,6 +359,19 @@ end: evbuffer_free(evb); } +static void +http_large_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *evb = evbuffer_new(); + int i; + + for (i = 0; i < 1<<20; ++i) { + evbuffer_add_printf(evb, BASIC_REQUEST_BODY); + } + evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); + evbuffer_free(evb); +} + static char const* const CHUNKS[] = { "This is funny", "but not hilarious.", @@ -394,8 +437,28 @@ http_complete_write(evutil_socket_t fd, short what, void *arg) bufferevent_write(bev, http_request, strlen(http_request)); } +static struct bufferevent * +create_bev(struct event_base *base, int fd, int ssl) +{ + int flags = BEV_OPT_DEFER_CALLBACKS; + struct bufferevent *bev = NULL; + + if (!ssl) { + bev = bufferevent_socket_new(base, fd, flags); + } else { +#ifdef EVENT__HAVE_OPENSSL + SSL *ssl = SSL_new(get_ssl_ctx()); + bev = bufferevent_openssl_socket_new( + base, fd, ssl, BUFFEREVENT_SSL_CONNECTING, flags); + bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); +#endif + } + + return bev; +} + static void -http_basic_test(void *arg) +http_basic_test_impl(void *arg, int ssl) { struct basic_test_data *data = arg; struct timeval tv; @@ -403,13 +466,14 @@ http_basic_test(void *arg) evutil_socket_t fd; const char *http_request; ev_uint16_t port = 0, port2 = 0; + int server_flags = ssl ? HTTP_BIND_SSL : 0; test_ok = 0; - http = http_setup(&port, data->base, 0); + http = http_setup(&port, data->base, server_flags); /* bind to a second socket */ - if (http_bind(http, &port2, 0) == -1) { + if (http_bind(http, &port2, server_flags) == -1) { fprintf(stdout, "FAILED (bind)\n"); exit(1); } @@ -417,7 +481,7 @@ http_basic_test(void *arg) fd = http_connect("127.0.0.1", port); /* Stupid thing to send a request */ - bev = bufferevent_socket_new(data->base, fd, 0); + bev = create_bev(data->base, fd, ssl); bufferevent_setcb(bev, http_readcb, http_writecb, http_errorcb, data->base); @@ -443,7 +507,7 @@ http_basic_test(void *arg) fd = http_connect("127.0.0.1", port2); /* Stupid thing to send a request */ - bev = bufferevent_socket_new(data->base, fd, 0); + bev = create_bev(data->base, fd, ssl); bufferevent_setcb(bev, http_readcb, http_writecb, http_errorcb, data->base); @@ -466,7 +530,7 @@ http_basic_test(void *arg) fd = http_connect("127.0.0.1", port2); /* Stupid thing to send a request */ - bev = bufferevent_socket_new(data->base, fd, 0); + bev = create_bev(data->base, fd, ssl); bufferevent_setcb(bev, http_readcb, http_writecb, http_errorcb, data->base); @@ -487,6 +551,8 @@ http_basic_test(void *arg) if (bev) bufferevent_free(bev); } +static void http_basic_test(void *arg) +{ return http_basic_test_impl(arg, 0); } static void @@ -956,6 +1022,7 @@ http_allowed_methods_test(void *arg) evutil_closesocket(fd3); } +static void http_request_no_action_done(struct evhttp_request *, void *); static void http_request_done(struct evhttp_request *, void *); static void http_request_empty_done(struct evhttp_request *, void *); @@ -1060,8 +1127,8 @@ http_persist_connection_test(void *arg) } static struct regress_dns_server_table search_table[] = { - { "localhost", "A", "127.0.0.1", 0 }, - { NULL, NULL, NULL, 0 } + { "localhost", "A", "127.0.0.1", 0, 0 }, + { NULL, NULL, NULL, 0, 0 } }; static void @@ -1319,6 +1386,13 @@ http_cancel_test(void *arg) evhttp_free(http); } +static void +http_request_no_action_done(struct evhttp_request *req, void *arg) +{ + EVUTIL_ASSERT(exit_base); + event_base_loopexit(exit_base, NULL); +} + static void http_request_done(struct evhttp_request *req, void *arg) { @@ -1583,6 +1657,11 @@ http_dispatcher_test_done(struct evhttp_request *req, void *arg) struct event_base *base = arg; const char *what = "DISPATCHER_TEST"; + if (!req) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + if (evhttp_request_get_response_code(req) != HTTP_OK) { fprintf(stderr, "FAILED\n"); exit(1); @@ -2750,6 +2829,10 @@ http_incomplete_readcb(struct bufferevent *bev, void *arg) static void http_incomplete_errorcb(struct bufferevent *bev, short what, void *arg) { + /** For ssl */ + if (what & BEV_EVENT_CONNECTED) + return; + if (what == (BEV_EVENT_READING|BEV_EVENT_EOF)) test_ok++; else @@ -2773,7 +2856,7 @@ http_incomplete_writecb(struct bufferevent *bev, void *arg) } static void -http_incomplete_test_(struct basic_test_data *data, int use_timeout) +http_incomplete_test_(struct basic_test_data *data, int use_timeout, int ssl) { struct bufferevent *bev; evutil_socket_t fd; @@ -2785,14 +2868,14 @@ http_incomplete_test_(struct basic_test_data *data, int use_timeout) test_ok = 0; - http = http_setup(&port, data->base, 0); + http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); evhttp_set_timeout(http, 1); fd = http_connect("127.0.0.1", port); tt_int_op(fd, >=, 0); /* Stupid thing to send a request */ - bev = bufferevent_socket_new(data->base, fd, 0); + bev = create_bev(data->base, fd, ssl); bufferevent_setcb(bev, http_incomplete_readcb, http_incomplete_writecb, http_incomplete_errorcb, use_timeout ? NULL : &fd); @@ -2830,16 +2913,11 @@ http_incomplete_test_(struct basic_test_data *data, int use_timeout) if (fd >= 0) evutil_closesocket(fd); } -static void -http_incomplete_test(void *arg) -{ - http_incomplete_test_(arg, 0); -} -static void -http_incomplete_timeout_test(void *arg) -{ - http_incomplete_test_(arg, 1); -} +static void http_incomplete_test(void *arg) +{ http_incomplete_test_(arg, 0, 0); } +static void http_incomplete_timeout_test(void *arg) +{ http_incomplete_test_(arg, 1, 0); } + /* * the server is going to reply with chunked data. @@ -2856,6 +2934,10 @@ http_chunked_errorcb(struct bufferevent *bev, short what, void *arg) { struct evhttp_request *req = NULL; + /** SSL */ + if (what & BEV_EVENT_CONNECTED) + return; + if (!test_ok) goto out; @@ -2990,7 +3072,7 @@ http_chunked_request_done(struct evhttp_request *req, void *arg) } static void -http_chunk_out_test(void *arg) +http_chunk_out_test_impl(void *arg, int ssl) { struct basic_test_data *data = arg; struct bufferevent *bev; @@ -3005,12 +3087,12 @@ http_chunk_out_test(void *arg) exit_base = data->base; test_ok = 0; - http = http_setup(&port, data->base, 0); + http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); fd = http_connect("127.0.0.1", port); /* Stupid thing to send a request */ - bev = bufferevent_socket_new(data->base, fd, 0); + bev = create_bev(data->base, fd, ssl); bufferevent_setcb(bev, http_chunked_readcb, http_chunked_writecb, http_chunked_errorcb, data->base); @@ -3037,7 +3119,9 @@ http_chunk_out_test(void *arg) tt_int_op(test_ok, ==, 2); /* now try again with the regular connection object */ - evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + bev = create_bev(data->base, -1, ssl); + evcon = evhttp_connection_base_bufferevent_new( + data->base, NULL, bev, "127.0.0.1", port); tt_assert(evcon); /* make two requests to check the keepalive behavior */ @@ -3065,21 +3149,26 @@ http_chunk_out_test(void *arg) if (http) evhttp_free(http); } +static void http_chunk_out_test(void *arg) +{ return http_chunk_out_test_impl(arg, 0); } static void -http_stream_out_test(void *arg) +http_stream_out_test_impl(void *arg, int ssl) { struct basic_test_data *data = arg; ev_uint16_t port = 0; struct evhttp_connection *evcon = NULL; struct evhttp_request *req = NULL; + struct bufferevent *bev; test_ok = 0; exit_base = data->base; - http = http_setup(&port, data->base, 0); + http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); - evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + bev = create_bev(data->base, -1, ssl); + evcon = evhttp_connection_base_bufferevent_new( + data->base, NULL, bev, "127.0.0.1", port); tt_assert(evcon); /* @@ -3107,6 +3196,8 @@ http_stream_out_test(void *arg) if (http) evhttp_free(http); } +static void http_stream_out_test(void *arg) +{ return http_stream_out_test_impl(arg, 0); } static void http_stream_in_chunk(struct evhttp_request *req, void *arg) @@ -3241,65 +3332,70 @@ http_stream_in_cancel_test(void *arg) static void http_connection_fail_done(struct evhttp_request *req, void *arg) { - struct evhttp_connection *evcon = arg; - struct event_base *base = evhttp_connection_get_base(evcon); + struct evhttp_connection *evcon = arg; + struct event_base *base = evhttp_connection_get_base(evcon); - /* An ENETUNREACH error results in an unrecoverable - * evhttp_connection error (see evhttp_connection_fail_()). The - * connection will be reset, and the user will be notified with a NULL - * req parameter. */ - tt_assert(!req); + /* An ENETUNREACH error results in an unrecoverable + * evhttp_connection error (see evhttp_connection_fail_()). The + * connection will be reset, and the user will be notified with a NULL + * req parameter. */ + tt_assert(!req); - evhttp_connection_free(evcon); + evhttp_connection_free(evcon); - test_ok = 1; + test_ok = 1; end: - event_base_loopexit(base, NULL); + event_base_loopexit(base, NULL); } /* Test unrecoverable evhttp_connection errors by generating an ENETUNREACH * error on connection. */ static void -http_connection_fail_test(void *arg) +http_connection_fail_test_impl(void *arg, int ssl) { - struct basic_test_data *data = arg; - ev_uint16_t port = 0; - struct evhttp_connection *evcon = NULL; - struct evhttp_request *req = NULL; + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct bufferevent *bev; - exit_base = data->base; - test_ok = 0; + exit_base = data->base; + test_ok = 0; - /* auto detect a port */ - http = http_setup(&port, data->base, 0); - evhttp_free(http); - http = NULL; + /* auto detect a port */ + http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); + evhttp_free(http); + http = NULL; - /* Pick an unroutable address. This administratively scoped multicast - * address should do when working with TCP. */ - evcon = evhttp_connection_base_new(data->base, NULL, "239.10.20.30", 80); - tt_assert(evcon); + bev = create_bev(data->base, -1, ssl); + /* Pick an unroutable address. This administratively scoped multicast + * address should do when working with TCP. */ + evcon = evhttp_connection_base_bufferevent_new( + data->base, NULL, bev, "239.10.20.30", 80); + tt_assert(evcon); - /* - * At this point, we want to schedule an HTTP GET request - * server using our make request method. - */ + /* + * At this point, we want to schedule an HTTP GET request + * server using our make request method. + */ - req = evhttp_request_new(http_connection_fail_done, evcon); - tt_assert(req); + req = evhttp_request_new(http_connection_fail_done, evcon); + tt_assert(req); - if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/") == -1) { - tt_abort_msg("Couldn't make request"); - } + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/") == -1) { + tt_abort_msg("Couldn't make request"); + } - event_base_dispatch(data->base); + event_base_dispatch(data->base); - tt_int_op(test_ok, ==, 1); + tt_int_op(test_ok, ==, 1); end: - ; + ; } +static void http_connection_fail_test(void *arg) +{ return http_connection_fail_test_impl(arg, 0); } static void http_connection_retry_done(struct evhttp_request *req, void *arg) @@ -3317,33 +3413,81 @@ http_connection_retry_done(struct evhttp_request *req, void *arg) event_base_loopexit(arg,NULL); } +struct http_server +{ + ev_uint16_t port; + int ssl; +}; static struct event_base *http_make_web_server_base=NULL; static void http_make_web_server(evutil_socket_t fd, short what, void *arg) { - ev_uint16_t port = *(ev_uint16_t*)arg; - http = http_setup(&port, http_make_web_server_base, 0); + struct http_server *hs = (struct http_server *)arg; + http = http_setup(&hs->port, http_make_web_server_base, hs->ssl ? HTTP_BIND_SSL : 0); } static void -http_connection_retry_test(void *arg) +http_simple_test_impl(void *arg, int ssl, int dirty) +{ + struct basic_test_data *data = arg; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct bufferevent *bev; + struct http_server hs = { .port = 0, .ssl = ssl, }; + + exit_base = data->base; + test_ok = 0; + + http = http_setup(&hs.port, data->base, ssl ? HTTP_BIND_SSL : 0); + + bev = create_bev(data->base, -1, ssl); + evcon = evhttp_connection_base_bufferevent_new( + data->base, NULL, bev, "127.0.0.1", hs.port); + tt_assert(evcon); + evhttp_connection_set_local_address(evcon, "127.0.0.1"); + + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + tt_assert(req); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + tt_int_op(test_ok, ==, 1); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} +static void http_simple_test(void *arg) +{ return http_simple_test_impl(arg, 0, 0); } + +static void +http_connection_retry_test_basic(void *arg, const char *addr, struct evdns_base *dns_base, int ssl) { struct basic_test_data *data = arg; - ev_uint16_t port = 0; struct evhttp_connection *evcon = NULL; struct evhttp_request *req = NULL; struct timeval tv, tv_start, tv_end; + struct bufferevent *bev; + struct http_server hs = { .port = 0, .ssl = ssl, }; exit_base = data->base; test_ok = 0; /* auto detect a port */ - http = http_setup(&port, data->base, 0); + http = http_setup(&hs.port, data->base, ssl ? HTTP_BIND_SSL : 0); evhttp_free(http); http = NULL; - evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + bev = create_bev(data->base, -1, ssl); + evcon = evhttp_connection_base_bufferevent_new(data->base, dns_base, bev, addr, hs.port); tt_assert(evcon); + if (dns_base) + tt_assert(!evhttp_connection_set_flags(evcon, EVHTTP_CON_REUSE_CONNECTED_ADDR)); evhttp_connection_set_timeout(evcon, 1); /* also bind to local host */ @@ -3377,6 +3521,9 @@ http_connection_retry_test(void *arg) * now test the same but with retries */ test_ok = 0; + /** Shutdown dns server, to test conn_address reusing */ + if (dns_base) + regress_clean_dnsserver(); { const struct timeval tv_timeout = { 0, 500000 }; @@ -3432,7 +3579,7 @@ http_connection_retry_test(void *arg) evutil_timerclear(&tv); tv.tv_usec = 200000; http_make_web_server_base = data->base; - event_base_once(data->base, -1, EV_TIMEOUT, http_make_web_server, &port, &tv); + event_base_once(data->base, -1, EV_TIMEOUT, http_make_web_server, &hs, &tv); evutil_gettimeofday(&tv_start, NULL); event_base_dispatch(data->base); @@ -3449,6 +3596,42 @@ http_connection_retry_test(void *arg) evhttp_free(http); } +static void +http_connection_retry_conn_address_test_impl(void *arg, int ssl) +{ + struct basic_test_data *data = arg; + ev_uint16_t portnum = 0; + struct evdns_base *dns_base = NULL; + char address[64]; + + tt_assert(regress_dnsserver(data->base, &portnum, search_table)); + dns_base = evdns_base_new(data->base, 0/* init name servers */); + tt_assert(dns_base); + + /* Add ourself as the only nameserver, and make sure we really are + * the only nameserver. */ + evutil_snprintf(address, sizeof(address), "127.0.0.1:%d", portnum); + evdns_base_nameserver_ip_add(dns_base, address); + + http_connection_retry_test_basic(arg, "localhost", dns_base, ssl); + + end: + if (dns_base) + evdns_base_free(dns_base, 0); + /** dnsserver will be cleaned in http_connection_retry_test_basic() */ +} +static void http_connection_retry_conn_address_test(void *arg) +{ return http_connection_retry_conn_address_test_impl(arg, 0); } + +static void +http_connection_retry_test_impl(void *arg, int ssl) +{ + return http_connection_retry_test_basic(arg, "127.0.0.1", NULL, ssl); +} +static void +http_connection_retry_test(void *arg) +{ return http_connection_retry_test_impl(arg, 0); } + static void http_primitives(void *ptr) { @@ -3688,6 +3871,7 @@ struct terminate_state { struct bufferevent *bev; evutil_socket_t fd; int gotclosecb: 1; + int oneshot: 1; }; static void @@ -3695,7 +3879,10 @@ terminate_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg) { struct terminate_state *state = arg; struct evbuffer *evb; - struct timeval tv; + + if (!state->req) { + return; + } if (evhttp_request_get_connection(state->req) == NULL) { test_ok = 1; @@ -3709,11 +3896,14 @@ terminate_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg) evhttp_send_reply_chunk(state->req, evb); evbuffer_free(evb); - tv.tv_sec = 0; - tv.tv_usec = 3000; - EVUTIL_ASSERT(state); - EVUTIL_ASSERT(state->base); - event_base_once(state->base, -1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); + if (!state->oneshot) { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 3000; + EVUTIL_ASSERT(state); + EVUTIL_ASSERT(state->base); + event_base_once(state->base, -1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); + } } static void @@ -3721,6 +3911,13 @@ terminate_chunked_close_cb(struct evhttp_connection *evcon, void *arg) { struct terminate_state *state = arg; state->gotclosecb = 1; + + /** TODO: though we can do this unconditionally */ + if (state->oneshot) { + evhttp_request_free(state->req); + state->req = NULL; + event_base_loopexit(state->base,NULL); + } } static void @@ -3760,7 +3957,7 @@ terminate_readcb(struct bufferevent *bev, void *arg) static void -http_terminate_chunked_test(void *arg) +http_terminate_chunked_test_impl(void *arg, int oneshot) { struct basic_test_data *data = arg; struct bufferevent *bev = NULL; @@ -3789,6 +3986,7 @@ http_terminate_chunked_test(void *arg) terminate_state.fd = fd; terminate_state.bev = bev; terminate_state.gotclosecb = 0; + terminate_state.oneshot = oneshot; /* first half of the http request */ http_request = @@ -3812,10 +4010,20 @@ http_terminate_chunked_test(void *arg) if (http) evhttp_free(http); } +static void +http_terminate_chunked_test(void *arg) +{ + http_terminate_chunked_test_impl(arg, 0); +} +static void +http_terminate_chunked_oneshot_test(void *arg) +{ + http_terminate_chunked_test_impl(arg, 1); +} static struct regress_dns_server_table ipv6_search_table[] = { - { "localhost", "AAAA", "::1", 0 }, - { NULL, NULL, NULL, 0 } + { "localhost", "AAAA", "::1", 0, 0 }, + { NULL, NULL, NULL, 0, 0 } }; static void @@ -3929,12 +4137,126 @@ http_set_family_ipv6_test(void *arg) http_ipv6_for_domain_test_impl(arg, AF_INET6); } +static void +http_write_during_read(evutil_socket_t fd, short what, void *arg) +{ + struct bufferevent *bev = arg; + struct timeval tv; + + bufferevent_write(bev, "foobar", strlen("foobar")); + + evutil_timerclear(&tv); + tv.tv_sec = 1; + event_base_loopexit(exit_base, &tv); +} +static void +http_write_during_read_test_impl(void *arg, int ssl) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct bufferevent *bev = NULL; + struct timeval tv; + int fd; + const char *http_request; + + test_ok = 0; + exit_base = data->base; + + http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); + + fd = http_connect("127.0.0.1", port); + bev = create_bev(data->base, fd, 0); + bufferevent_setcb(bev, NULL, NULL, NULL, data->base); + bufferevent_disable(bev, EV_READ); + + http_request = + "GET /large HTTP/1.1\r\n" + "Host: somehost\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + evutil_timerclear(&tv); + tv.tv_usec = 10000; + event_base_once(data->base, -1, EV_TIMEOUT, http_write_during_read, bev, &tv); + + event_base_dispatch(data->base); + + if (bev) + bufferevent_free(bev); + if (http) + evhttp_free(http); +} +static void http_write_during_read_test(void *arg) +{ return http_write_during_read_test_impl(arg, 0); } + +static void +http_request_own_test(void *arg) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + exit_base = data->base; + + http = http_setup(&port, data->base, 0); + evhttp_free(http); + + evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + tt_assert(evcon); + + req = evhttp_request_new(http_request_no_action_done, NULL); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("Couldn't make request"); + } + evhttp_request_own(req); + + event_base_dispatch(data->base); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (req) + evhttp_request_free(req); + + test_ok = 1; +} + #define HTTP_LEGACY(name) \ { #name, run_legacy_test_fn, TT_ISOLATED|TT_LEGACY, &legacy_setup, \ http_##name##_test } #define HTTP(name) \ { #name, http_##name##_test, TT_ISOLATED, &basic_setup, NULL } +#define HTTPS(name) \ + { "https_" #name, https_##name##_test, TT_ISOLATED, &basic_setup, NULL } + +#ifdef EVENT__HAVE_OPENSSL +static void https_basic_test(void *arg) +{ return http_basic_test_impl(arg, 1); } +static void https_incomplete_test(void *arg) +{ http_incomplete_test_(arg, 0, 1); } +static void https_incomplete_timeout_test(void *arg) +{ http_incomplete_test_(arg, 1, 1); } +static void https_simple_test(void *arg) +{ return http_simple_test_impl(arg, 1, 0); } +static void https_simple_dirty_test(void *arg) +{ return http_simple_test_impl(arg, 1, 1); } +static void https_connection_retry_conn_address_test(void *arg) +{ return http_connection_retry_conn_address_test_impl(arg, 1); } +static void https_connection_retry_test(void *arg) +{ return http_connection_retry_test_impl(arg, 1); } +static void https_chunk_out_test(void *arg) +{ return http_chunk_out_test_impl(arg, 1); } +static void https_stream_out_test(void *arg) +{ return http_stream_out_test_impl(arg, 1); } +static void https_connection_fail_test(void *arg) +{ return http_connection_fail_test_impl(arg, 1); } +static void https_write_during_read_test(void *arg) +{ return http_write_during_read_test_impl(arg, 1); } +#endif struct testcase_t http_testcases[] = { { "primitives", http_primitives, 0, NULL, NULL }, @@ -3945,6 +4267,7 @@ struct testcase_t http_testcases[] = { { "parse_uri_nc", http_parse_uri_test, 0, &basic_setup, (void*)"nc" }, { "uriencode", http_uriencode_test, 0, NULL, NULL }, HTTP(basic), + HTTP(simple), HTTP(cancel), HTTP(virtual_host), HTTP(post), @@ -3962,6 +4285,7 @@ struct testcase_t http_testcases[] = { HTTP(incomplete), HTTP(incomplete_timeout), HTTP(terminate_chunked), + HTTP(terminate_chunked_oneshot), HTTP(on_complete), HTTP(highport), @@ -3976,6 +4300,8 @@ struct testcase_t http_testcases[] = { HTTP(connection_fail), { "connection_retry", http_connection_retry_test, TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, + { "connection_retry_conn_address", http_connection_retry_conn_address_test, + TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, HTTP(data_length_constraints), @@ -3986,6 +4312,24 @@ struct testcase_t http_testcases[] = { HTTP(set_family_ipv4), HTTP(set_family_ipv6), + HTTP(write_during_read), + HTTP(request_own), + +#ifdef EVENT__HAVE_OPENSSL + HTTPS(basic), + HTTPS(simple), + HTTPS(simple_dirty), + HTTPS(incomplete), + HTTPS(incomplete_timeout), + { "https_connection_retry", https_connection_retry_test, TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, + { "https_connection_retry_conn_address", https_connection_retry_conn_address_test, + TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, + HTTPS(chunk_out), + HTTPS(stream_out), + HTTPS(connection_fail), + HTTPS(write_during_read), +#endif + END_OF_TESTCASES }; diff --git a/test/regress_main.c b/test/regress_main.c index 3198ced1..6d045bb8 100644 --- a/test/regress_main.c +++ b/test/regress_main.c @@ -407,6 +407,7 @@ const char *finetimetests[] = { "+util/monotonic_res_fallback", "+thread/deferred_cb_skew", "+http/connection_retry", + "+http/https_connection_retry", NULL }; struct testlist_alias_t testaliases[] = { @@ -437,6 +438,7 @@ main(int argc, const char **argv) #ifdef _WIN32 tinytest_skip(testgroups, "http/connection_retry"); + tinytest_skip(testgroups, "http/https_connection_retry"); #endif #ifndef EVENT__DISABLE_THREAD_SUPPORT diff --git a/test/regress_ssl.c b/test/regress_ssl.c index bf9b46b1..0fc9bf21 100644 --- a/test/regress_ssl.c +++ b/test/regress_ssl.c @@ -43,6 +43,7 @@ #include "event2/util.h" #include "event2/event.h" #include "event2/bufferevent_ssl.h" +#include "event2/bufferevent_struct.h" #include "event2/buffer.h" #include "event2/listener.h" @@ -50,12 +51,12 @@ #include "tinytest.h" #include "tinytest_macros.h" -#include #include #include #include #include +#include /* A short pre-generated key, to save the cost of doing an RSA key generation * step during the unit tests. It's only 512 bits long, and it is published @@ -72,8 +73,8 @@ static const char KEY[] = "U6GFEQTZ3IfuiVabG5pummdC4DNbcdI+WKrSFNmQ\n" "-----END RSA PRIVATE KEY-----\n"; -static EVP_PKEY * -getkey(void) +EVP_PKEY * +ssl_getkey(void) { EVP_PKEY *key; BIO *bio; @@ -91,15 +92,15 @@ end: return NULL; } -static X509 * -getcert(void) +X509 * +ssl_getcert(void) { /* Dummy code to make a quick-and-dirty valid certificate with OpenSSL. Don't copy this code into your own program! It does a number of things in a stupid and insecure way. */ X509 *x509 = NULL; X509_NAME *name = NULL; - EVP_PKEY *key = getkey(); + EVP_PKEY *key = ssl_getkey(); int nid; time_t now = time(NULL); @@ -137,7 +138,7 @@ end: static int disable_tls_11_and_12 = 0; static SSL_CTX *the_ssl_ctx = NULL; -static SSL_CTX * +SSL_CTX * get_ssl_ctx(void) { if (the_ssl_ctx) @@ -156,7 +157,7 @@ get_ssl_ctx(void) return the_ssl_ctx; } -static void +void init_ssl(void) { SSL_library_init(); @@ -177,17 +178,64 @@ static int test_is_done = 0; static int n_connected = 0; static int got_close = 0; static int got_error = 0; +static int got_timeout = 0; static int renegotiate_at = -1; static int stop_when_connected = 0; static int pending_connect_events = 0; static struct event_base *exit_base = NULL; +enum regress_openssl_type +{ + REGRESS_OPENSSL_SOCKETPAIR = 1, + REGRESS_OPENSSL_FILTER = 2, + REGRESS_OPENSSL_RENEGOTIATE = 4, + REGRESS_OPENSSL_OPEN = 8, + REGRESS_OPENSSL_DIRTY_SHUTDOWN = 16, + REGRESS_OPENSSL_FD = 32, + + REGRESS_OPENSSL_CLIENT = 64, + REGRESS_OPENSSL_SERVER = 128, + + REGRESS_OPENSSL_FREED = 256, + REGRESS_OPENSSL_TIMEOUT = 512, + REGRESS_OPENSSL_SLEEP = 1024, +}; + +static void +bufferevent_openssl_check_fd(struct bufferevent *bev, int filter) +{ + if (filter) { + tt_int_op(bufferevent_getfd(bev), ==, -1); + tt_int_op(bufferevent_setfd(bev, -1), ==, -1); + } else { + tt_int_op(bufferevent_getfd(bev), !=, -1); + tt_int_op(bufferevent_setfd(bev, -1), ==, 0); + } + tt_int_op(bufferevent_getfd(bev), ==, -1); + +end: + ; +} +static void +bufferevent_openssl_check_freed(struct bufferevent *bev) +{ + tt_int_op(event_pending(&bev->ev_read, EVLIST_ALL, NULL), ==, 0); + tt_int_op(event_pending(&bev->ev_write, EVLIST_ALL, NULL), ==, 0); + +end: + ; +} + static void respond_to_number(struct bufferevent *bev, void *ctx) { struct evbuffer *b = bufferevent_get_input(bev); char *line; int n; + + enum regress_openssl_type type; + type = (enum regress_openssl_type)ctx; + line = evbuffer_readln(b, NULL, EVBUFFER_EOL_LF); if (! line) return; @@ -201,7 +249,7 @@ respond_to_number(struct bufferevent *bev, void *ctx) bufferevent_free(bev); /* Should trigger close on other side. */ return; } - if (!strcmp(ctx, "client") && n == renegotiate_at) { + if ((type & REGRESS_OPENSSL_CLIENT) && n == renegotiate_at) { SSL_renegotiate(bufferevent_openssl_get_ssl(bev)); } ++n; @@ -226,6 +274,9 @@ done_writing_cb(struct bufferevent *bev, void *ctx) static void eventcb(struct bufferevent *bev, short what, void *ctx) { + enum regress_openssl_type type; + type = (enum regress_openssl_type)ctx; + TT_BLATHER(("Got event %d", (int)what)); if (what & BEV_EVENT_CONNECTED) { SSL *ssl; @@ -234,7 +285,7 @@ eventcb(struct bufferevent *bev, short what, void *ctx) ssl = bufferevent_openssl_get_ssl(bev); tt_assert(ssl); peer_cert = SSL_get_peer_certificate(ssl); - if (0==strcmp(ctx, "server")) { + if (type & REGRESS_OPENSSL_SERVER) { tt_assert(peer_cert == NULL); } else { tt_assert(peer_cert != NULL); @@ -246,10 +297,32 @@ eventcb(struct bufferevent *bev, short what, void *ctx) } else if (what & BEV_EVENT_EOF) { TT_BLATHER(("Got a good EOF")); ++got_close; + if (type & REGRESS_OPENSSL_FD) { + bufferevent_openssl_check_fd(bev, type & REGRESS_OPENSSL_FILTER); + } + if (type & REGRESS_OPENSSL_FREED) { + bufferevent_openssl_check_freed(bev); + } bufferevent_free(bev); } else if (what & BEV_EVENT_ERROR) { TT_BLATHER(("Got an error.")); ++got_error; + if (type & REGRESS_OPENSSL_FD) { + bufferevent_openssl_check_fd(bev, type & REGRESS_OPENSSL_FILTER); + } + if (type & REGRESS_OPENSSL_FREED) { + bufferevent_openssl_check_freed(bev); + } + bufferevent_free(bev); + } else if (what & BEV_EVENT_TIMEOUT) { + TT_BLATHER(("Got timeout.")); + ++got_timeout; + if (type & REGRESS_OPENSSL_FD) { + bufferevent_openssl_check_fd(bev, type & REGRESS_OPENSSL_FILTER); + } + if (type & REGRESS_OPENSSL_FREED) { + bufferevent_openssl_check_freed(bev); + } bufferevent_free(bev); } end: @@ -259,10 +332,12 @@ end: static void open_ssl_bufevs(struct bufferevent **bev1_out, struct bufferevent **bev2_out, struct event_base *base, int is_open, int flags, SSL *ssl1, SSL *ssl2, - evutil_socket_t *fd_pair, struct bufferevent **underlying_pair) + evutil_socket_t *fd_pair, struct bufferevent **underlying_pair, + enum regress_openssl_type type) { int state1 = is_open ? BUFFEREVENT_SSL_OPEN :BUFFEREVENT_SSL_CONNECTING; int state2 = is_open ? BUFFEREVENT_SSL_OPEN :BUFFEREVENT_SSL_ACCEPTING; + int dirty_shutdown = type & REGRESS_OPENSSL_DIRTY_SHUTDOWN; if (fd_pair) { *bev1_out = bufferevent_openssl_socket_new( base, fd_pair[0], ssl1, state1, flags); @@ -276,9 +351,12 @@ open_ssl_bufevs(struct bufferevent **bev1_out, struct bufferevent **bev2_out, } bufferevent_setcb(*bev1_out, respond_to_number, done_writing_cb, - eventcb, (void*)"client"); + eventcb, (void*)(REGRESS_OPENSSL_CLIENT | (long)type)); bufferevent_setcb(*bev2_out, respond_to_number, done_writing_cb, - eventcb, (void*)"server"); + eventcb, (void*)(REGRESS_OPENSSL_SERVER | (long)type)); + + bufferevent_openssl_set_allow_dirty_shutdown(*bev1_out, dirty_shutdown); + bufferevent_openssl_set_allow_dirty_shutdown(*bev2_out, dirty_shutdown); } static void @@ -288,20 +366,21 @@ regress_bufferevent_openssl(void *arg) struct bufferevent *bev1, *bev2; SSL *ssl1, *ssl2; - X509 *cert = getcert(); - EVP_PKEY *key = getkey(); - const int start_open = strstr((char*)data->setup_data, "open")!=NULL; - const int filter = strstr((char*)data->setup_data, "filter")!=NULL; + X509 *cert = ssl_getcert(); + EVP_PKEY *key = ssl_getkey(); int flags = BEV_OPT_DEFER_CALLBACKS; struct bufferevent *bev_ll[2] = { NULL, NULL }; evutil_socket_t *fd_pair = NULL; + enum regress_openssl_type type; + type = (enum regress_openssl_type)data->setup_data; + tt_assert(cert); tt_assert(key); init_ssl(); - if (strstr((char*)data->setup_data, "renegotiate")) { + if (type & REGRESS_OPENSSL_RENEGOTIATE) { if (SSLeay() >= 0x10001000 && SSLeay() < 0x1000104f) { /* 1.0.1 up to 1.0.1c has a bug where TLS1.1 and 1.2 @@ -317,11 +396,11 @@ regress_bufferevent_openssl(void *arg) SSL_use_certificate(ssl2, cert); SSL_use_PrivateKey(ssl2, key); - if (! start_open) + if (!(type & REGRESS_OPENSSL_OPEN)) flags |= BEV_OPT_CLOSE_ON_FREE; - if (!filter) { - tt_assert(strstr((char*)data->setup_data, "socketpair")); + if (!(type & REGRESS_OPENSSL_FILTER)) { + tt_assert(type & REGRESS_OPENSSL_SOCKETPAIR); fd_pair = data->pair; } else { bev_ll[0] = bufferevent_socket_new(data->base, data->pair[0], @@ -331,15 +410,15 @@ regress_bufferevent_openssl(void *arg) } open_ssl_bufevs(&bev1, &bev2, data->base, 0, flags, ssl1, ssl2, - fd_pair, bev_ll); + fd_pair, bev_ll, type); - if (!filter) { + if (!(type & REGRESS_OPENSSL_FILTER)) { tt_int_op(bufferevent_getfd(bev1), ==, data->pair[0]); } else { tt_ptr_op(bufferevent_get_underlying(bev1), ==, bev_ll[0]); } - if (start_open) { + if (type & REGRESS_OPENSSL_OPEN) { pending_connect_events = 2; stop_when_connected = 1; exit_base = data->base; @@ -351,37 +430,70 @@ regress_bufferevent_openssl(void *arg) bufferevent_free(bev2); bev1 = bev2 = NULL; open_ssl_bufevs(&bev1, &bev2, data->base, 1, flags, ssl1, ssl2, - fd_pair, bev_ll); + fd_pair, bev_ll, type); } - bufferevent_enable(bev1, EV_READ|EV_WRITE); - bufferevent_enable(bev2, EV_READ|EV_WRITE); + if (!(type & REGRESS_OPENSSL_TIMEOUT)) { + bufferevent_enable(bev1, EV_READ|EV_WRITE); + bufferevent_enable(bev2, EV_READ|EV_WRITE); - evbuffer_add_printf(bufferevent_get_output(bev1), "1\n"); + evbuffer_add_printf(bufferevent_get_output(bev1), "1\n"); - event_base_dispatch(data->base); + event_base_dispatch(data->base); - tt_assert(test_is_done == 1); - tt_assert(n_connected == 2); + tt_assert(test_is_done == 1); + tt_assert(n_connected == 2); - /* We don't handle shutdown properly yet. - tt_int_op(got_close, ==, 1); - tt_int_op(got_error, ==, 0); - */ + /* We don't handle shutdown properly yet */ + if (type & REGRESS_OPENSSL_DIRTY_SHUTDOWN) { + tt_int_op(got_close, ==, 1); + tt_int_op(got_error, ==, 0); + } else { + tt_int_op(got_error, ==, 1); + } + tt_int_op(got_timeout, ==, 0); + } else { + struct timeval t = { 2, 0 }; + + bufferevent_enable(bev1, EV_READ|EV_WRITE); + bufferevent_disable(bev2, EV_READ|EV_WRITE); + + bufferevent_set_timeouts(bev1, &t, &t); + + evbuffer_add_printf(bufferevent_get_output(bev1), "1\n"); + + event_base_dispatch(data->base); + + tt_assert(test_is_done == 0); + tt_assert(n_connected == 0); + + tt_int_op(got_close, ==, 0); + tt_int_op(got_error, ==, 0); + tt_int_op(got_timeout, ==, 1); + } end: return; } +static void +acceptcb_deferred(evutil_socket_t fd, short events, void *arg) +{ + struct bufferevent *bev = arg; + bufferevent_enable(bev, EV_READ|EV_WRITE); +} static void acceptcb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg) { struct basic_test_data *data = arg; struct bufferevent *bev; + enum regress_openssl_type type; SSL *ssl = SSL_new(get_ssl_ctx()); - SSL_use_certificate(ssl, getcert()); - SSL_use_PrivateKey(ssl, getkey()); + type = (enum regress_openssl_type)data->setup_data; + + SSL_use_certificate(ssl, ssl_getcert()); + SSL_use_PrivateKey(ssl, ssl_getkey()); bev = bufferevent_openssl_socket_new( data->base, @@ -391,14 +503,129 @@ acceptcb(struct evconnlistener *listener, evutil_socket_t fd, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); bufferevent_setcb(bev, respond_to_number, NULL, eventcb, - (void*)"server"); + (void*)(REGRESS_OPENSSL_SERVER)); - bufferevent_enable(bev, EV_READ|EV_WRITE); + if (type & REGRESS_OPENSSL_SLEEP) { + struct timeval when = { 1, 0 }; + event_base_once(data->base, -1, EV_TIMEOUT, + acceptcb_deferred, bev, &when); + bufferevent_disable(bev, EV_READ|EV_WRITE); + } else { + bufferevent_enable(bev, EV_READ|EV_WRITE); + } /* Only accept once, then disable ourself. */ evconnlistener_disable(listener); } +struct rwcount +{ + int fd; + size_t read; + size_t write; +}; +static int +bio_rwcount_new(BIO *b) +{ + b->init = 0; + b->num = -1; + b->ptr = NULL; + b->flags = 0; + return 1; +} +static int +bio_rwcount_free(BIO *b) +{ + if (!b) + return 0; + if (b->shutdown) { + b->init = 0; + b->flags = 0; + b->ptr = NULL; + } + return 1; +} +static int +bio_rwcount_read(BIO *b, char *out, int outlen) +{ + struct rwcount *rw = b->ptr; + ssize_t ret = read(rw->fd, out, outlen); + ++rw->read; + if (ret == -1 && errno == EAGAIN) { + BIO_set_retry_read(b); + } + return ret; +} +static int +bio_rwcount_write(BIO *b, const char *in, int inlen) +{ + + struct rwcount *rw = b->ptr; + ssize_t ret = write(rw->fd, in, inlen); + ++rw->write; + if (ret == -1 && errno == EAGAIN) { + BIO_set_retry_write(b); + } + return ret; +} +static long +bio_rwcount_ctrl(BIO *b, int cmd, long num, void *ptr) +{ + long ret = 0; + switch (cmd) { + case BIO_CTRL_GET_CLOSE: + ret = b->shutdown; + break; + case BIO_CTRL_SET_CLOSE: + b->shutdown = (int)num; + break; + case BIO_CTRL_PENDING: + ret = 0; + break; + case BIO_CTRL_WPENDING: + ret = 0; + break; + case BIO_CTRL_DUP: + case BIO_CTRL_FLUSH: + ret = 1; + break; + } + return ret; +} +static int +bio_rwcount_puts(BIO *b, const char *s) +{ + return bio_rwcount_write(b, s, strlen(s)); +} +#define BIO_TYPE_LIBEVENT_RWCOUNT 0xff1 +static BIO_METHOD methods_rwcount = { + BIO_TYPE_LIBEVENT_RWCOUNT, "rwcount", + bio_rwcount_write, + bio_rwcount_read, + bio_rwcount_puts, + NULL /* bio_rwcount_gets */, + bio_rwcount_ctrl, + bio_rwcount_new, + bio_rwcount_free, + NULL /* callback_ctrl */, +}; +static BIO_METHOD * +BIO_s_rwcount(void) +{ + return &methods_rwcount; +} +static BIO * +BIO_new_rwcount(int close_flag) +{ + BIO *result; + if (!(result = BIO_new(BIO_s_rwcount()))) + return NULL; + result->init = 1; + result->ptr = NULL; + result->shutdown = !!close_flag; + return result; +} + static void regress_bufferevent_openssl_connect(void *arg) { @@ -411,6 +638,12 @@ regress_bufferevent_openssl_connect(void *arg) struct sockaddr_in sin; struct sockaddr_storage ss; ev_socklen_t slen; + SSL *ssl; + BIO *bio; + struct rwcount rw = { -1, 0, 0 }; + enum regress_openssl_type type; + + type = (enum regress_openssl_type)data->setup_data; init_ssl(); @@ -428,51 +661,115 @@ regress_bufferevent_openssl_connect(void *arg) tt_assert(listener); tt_assert(evconnlistener_get_fd(listener) >= 0); + ssl = SSL_new(get_ssl_ctx()); + tt_assert(ssl); + bev = bufferevent_openssl_socket_new( - data->base, -1, SSL_new(get_ssl_ctx()), + data->base, -1, ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); tt_assert(bev); bufferevent_setcb(bev, respond_to_number, NULL, eventcb, - (void*)"client"); + (void*)(REGRESS_OPENSSL_CLIENT)); tt_assert(getsockname(evconnlistener_get_fd(listener), (struct sockaddr*)&ss, &slen) == 0); tt_assert(slen == sizeof(struct sockaddr_in)); tt_int_op(((struct sockaddr*)&ss)->sa_family, ==, AF_INET); - tt_int_op(((struct sockaddr*)&ss)->sa_family, ==, AF_INET); tt_assert(0 == bufferevent_socket_connect(bev, (struct sockaddr*)&ss, slen)); + /* Possible only when we have fd, since be_openssl can and will overwrite + * bio otherwise before */ + if (type & REGRESS_OPENSSL_SLEEP) { + rw.fd = bufferevent_getfd(bev); + bio = BIO_new_rwcount(0); + tt_assert(bio); + bio->ptr = &rw; + SSL_set_bio(ssl, bio, bio); + } evbuffer_add_printf(bufferevent_get_output(bev), "1\n"); bufferevent_enable(bev, EV_READ|EV_WRITE); event_base_dispatch(base); + + tt_int_op(rw.read, <=, 100); + tt_int_op(rw.write, <=, 100); end: ; } struct testcase_t ssl_testcases[] = { - - { "bufferevent_socketpair", regress_bufferevent_openssl, TT_ISOLATED, - &basic_setup, (void*)"socketpair" }, +#define T(a) ((void *)(a)) + { "bufferevent_socketpair", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, T(REGRESS_OPENSSL_SOCKETPAIR) }, { "bufferevent_filter", regress_bufferevent_openssl, - TT_ISOLATED, - &basic_setup, (void*)"filter" }, + TT_ISOLATED, &basic_setup, T(REGRESS_OPENSSL_FILTER) }, { "bufferevent_renegotiate_socketpair", regress_bufferevent_openssl, - TT_ISOLATED, - &basic_setup, (void*)"socketpair renegotiate" }, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_RENEGOTIATE) }, { "bufferevent_renegotiate_filter", regress_bufferevent_openssl, - TT_ISOLATED, - &basic_setup, (void*)"filter renegotiate" }, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_RENEGOTIATE) }, { "bufferevent_socketpair_startopen", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, (void*)"socketpair open" }, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_OPEN) }, { "bufferevent_filter_startopen", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, (void*)"filter open" }, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_OPEN) }, + + { "bufferevent_socketpair_dirty_shutdown", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, + { "bufferevent_filter_dirty_shutdown", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, + { "bufferevent_renegotiate_socketpair_dirty_shutdown", + regress_bufferevent_openssl, + TT_ISOLATED, + &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_RENEGOTIATE | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, + { "bufferevent_renegotiate_filter_dirty_shutdown", + regress_bufferevent_openssl, + TT_ISOLATED, + &basic_setup, + T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_RENEGOTIATE | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, + { "bufferevent_socketpair_startopen_dirty_shutdown", + regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_OPEN | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, + { "bufferevent_filter_startopen_dirty_shutdown", + regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_OPEN | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, + + { "bufferevent_socketpair_fd", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_FD) }, + { "bufferevent_socketpair_freed", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_FREED) }, + { "bufferevent_socketpair_freed_fd", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_FREED | REGRESS_OPENSSL_FD) }, + { "bufferevent_filter_freed_fd", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_FREED | REGRESS_OPENSSL_FD) }, + + { "bufferevent_socketpair_timeout", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_TIMEOUT) }, + { "bufferevent_socketpair_timeout_freed_fd", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_TIMEOUT | REGRESS_OPENSSL_FREED | REGRESS_OPENSSL_FD) }, { "bufferevent_connect", regress_bufferevent_openssl_connect, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "bufferevent_connect_sleep", regress_bufferevent_openssl_connect, + TT_FORK|TT_NEED_BASE, &basic_setup, T(REGRESS_OPENSSL_SLEEP) }, + +#undef T END_OF_TESTCASES, }; diff --git a/test/regress_testutils.c b/test/regress_testutils.c index 1f004603..7554a541 100644 --- a/test/regress_testutils.c +++ b/test/regress_testutils.c @@ -135,11 +135,18 @@ regress_clean_dnsserver(void) evutil_closesocket(dns_sock); } +static void strtolower(char *s) +{ + while (*s) { + *s = EVUTIL_TOLOWER_(*s); + ++s; + } +} void regress_dns_server_cb(struct evdns_server_request *req, void *data) { struct regress_dns_server_table *tab = data; - const char *question; + char *question; if (req->nquestions != 1) TT_DIE(("Only handling one question at a time; got %d", @@ -155,6 +162,9 @@ regress_dns_server_cb(struct evdns_server_request *req, void *data) ++tab->seen; + if (tab->lower) + strtolower(question); + if (!strcmp(tab->anstype, "err")) { int err = atoi(tab->ans); tt_assert(! evdns_server_request_respond(req, err)); diff --git a/test/regress_testutils.h b/test/regress_testutils.h index f90b9c99..040516a5 100644 --- a/test/regress_testutils.h +++ b/test/regress_testutils.h @@ -34,6 +34,7 @@ struct regress_dns_server_table { const char *anstype; const char *ans; int seen; + int lower; }; struct evdns_server_port * diff --git a/test/regress_thread.c b/test/regress_thread.c index c42668ea..9ff6a8fa 100644 --- a/test/regress_thread.c +++ b/test/regress_thread.c @@ -579,7 +579,12 @@ struct testcase_t thread_testcases[] = { { "deferred_cb_skew", thread_deferred_cb_skew, TT_FORK|TT_NEED_THREADS|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, +#ifndef _WIN32 + /****** XXX TODO FIXME windows seems to be having some timing trouble, + * looking into it now. / ellzey + ******/ TEST(no_events), +#endif END_OF_TESTCASES }; diff --git a/test/test.sh b/test/test.sh index 2b083ac7..b73c1adc 100755 --- a/test/test.sh +++ b/test/test.sh @@ -97,6 +97,7 @@ run_tests () { announce "FAILED (output not checked)" ; fi fi + test -x $TEST_DIR/regress || return announce_n " regress: " if test "$TEST_OUTPUT_FILE" = "/dev/null" ; @@ -112,6 +113,21 @@ run_tests () { announce FAILED ; FAILED=yes fi + + announce_n " regress_debug: " + if test "$TEST_OUTPUT_FILE" = "/dev/null" ; + then + EVENT_DEBUG_MODE=1 $TEST_DIR/regress --quiet $REGRESS_ARGS + else + EVENT_DEBUG_MODE=1 $TEST_DIR/regress $REGRESS_ARGS >>"$TEST_OUTPUT_FILE" + fi + if test "$?" = "0" ; + then + announce OKAY ; + else + announce FAILED ; + FAILED=yes + fi } do_test() { diff --git a/util-internal.h b/util-internal.h index 4fdedeaf..db4e5f39 100644 --- a/util-internal.h +++ b/util-internal.h @@ -261,7 +261,7 @@ int evutil_open_closeonexec_(const char *pathname, int flags, unsigned mode); int evutil_read_file_(const char *filename, char **content_out, size_t *len_out, int is_binary); -int evutil_socket_connect_(evutil_socket_t *fd_ptr, struct sockaddr *sa, int socklen); +int evutil_socket_connect_(evutil_socket_t *fd_ptr, const struct sockaddr *sa, int socklen); int evutil_socket_finished_connecting_(evutil_socket_t fd); diff --git a/win32select.c b/win32select.c index a50a2df7..1766858c 100644 --- a/win32select.c +++ b/win32select.c @@ -57,7 +57,7 @@ extern struct event_list timequeue; extern struct event_list addqueue; struct win_fd_set { - u_int fd_count; + unsigned int fd_count; SOCKET fd_array[1]; };