pax_global_header00006660000000000000000000000064132272304170014513gustar00rootroot0000000000000052 comment=7fe78cdae4d23a6100cfe67580baad3a634da719 libevhtp-1.2.16/000077500000000000000000000000001322723041700134175ustar00rootroot00000000000000libevhtp-1.2.16/.github/000077500000000000000000000000001322723041700147575ustar00rootroot00000000000000libevhtp-1.2.16/.github/issue_template.md000066400000000000000000000002601322723041700203220ustar00rootroot00000000000000# Details # Steps or code to reproduce the problem. ## Example code (if applicable) ```C #include int main(int argc, char ** argv) { return 0; } ``` # Version libevhtp-1.2.16/.gitignore000066400000000000000000000003531322723041700154100ustar00rootroot00000000000000# cmake manages these; they shouldn't go in version control # # # they aren't going into version control, but shouldn't be # completely ignored. I'm removing the mods here. # docs/ html/ build/* !build/.gitkeep cmake-build-debug .idea libevhtp-1.2.16/.travis.yml000066400000000000000000000004731322723041700155340ustar00rootroot00000000000000language: c compiler: - gcc - clang sudo: required before_install: - sudo apt-get update -qq - sudo apt-get install libevent-dev libonig-dev script: - cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. && make examples notifications: - irc "irc.oftc.net#libevhtp" libevhtp-1.2.16/CMakeLists.txt000066400000000000000000000232171322723041700161640ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(libevhtp) set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) include (version) include (colors) include (options) include (CheckFunctionExists) include (CheckIncludeFiles) include (CheckTypeSize) include (CheckCCompilerFlag) include (TestBigEndian) add_definitions ("-DPROJECT_VERSION=${PROJECT_VERSION} -Wall") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -ggdb3") SET(CMAKE_BUILD_TYPE Release CACHE STRING "default to Release") CHECK_FUNCTION_EXISTS(strndup HAVE_STRNDUP) CHECK_FUNCTION_EXISTS(strnlen HAVE_STRNLEN) if (EVHTP_THR_SHARED_PIPE) add_definitions(-DEVTHR_SHARED_PIPE) endif() check_include_files (stdlib.h HAVE_STDLIB_H) check_include_files (string.h HAVE_STRING_H) check_include_files (stdint.h HAVE_STDINT_H) check_include_files (errno.h HAVE_ERRNO_H) check_include_files (strings.h HAVE_STRINGS_H) check_include_files (inttypes.h HAVE_INTTYPES_H) check_include_files (limits.h HAVE_LIMITS_H) check_include_files (unistd.h HAVE_UNISTD_H) check_include_files (stdarg.h HAVE_STDARG_PROTOTYPES) check_include_files (sys/tree.h HAVE_SYS_TREE) check_include_files (sys/queue.h HAVE_SYS_QUEUE) check_include_files (sys/un.h HAVE_SYS_UN) CHECK_TYPE_SIZE("int" SIZEOF_INT) CHECK_TYPE_SIZE("long" SIZEOF_LONG) CHECK_TYPE_SIZE("short" SIZEOF_SHORT) TEST_BIG_ENDIAN(HOST_BIG_ENDIAN) check_c_compiler_flag(-fvisibility=hidden EVHTP_HAS_VISIBILITY_HIDDEN) check_c_compiler_flag(-std=c99 EVHTP_HAS_C99) check_c_compiler_flag(-fsanitize=address EVHTP_HAS_SANITIZE_ADDRESS) check_c_compiler_flag(-fsanitize=leak EVHTP_HAS_SANITIZE_LEAK) # NOTE in as of ubuntu 14, this is enabled by default, so if you see it fail # the check, don't freak out. check_c_compiler_flag(-fstack-protector-strong EVHTP_HAS_STACK_PROTECTOR) if (EVHTP_HAS_SANITIZE_ADDRESS) set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address") endif() if (EVHTP_HAS_SANITIZE_LEAK) set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=leak") endif() if (EVHTP_HAS_STACK_PROTECTOR) set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fstack-protector-strong") endif() if (EVHTP_HAS_C99) add_definitions(-DEVHTP_HAS_C99) endif() if (EVHTP_HAS_VISIBILITY_HIDDEN) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") endif() if (NOT HAVE_SYS_TREE) configure_file ( ${PROJECT_SOURCE_DIR}/compat/sys/tree.h.in ${PROJECT_BINARY_DIR}/compat/sys/tree.h) include_directories(${PROJECT_BINARY_DIR}/compat) endif() if (NOT HAVE_SYS_QUEUE) configure_file ( ${PROJECT_SOURCE_DIR}/compat/sys/queue.h.in ${PROJECT_BINARY_DIR}/compat/sys/queue.h) include_directories(${PROJECT_BINARY_DIR}/compat) endif() if (NOT HAVE_STRNDUP) add_definitions(-DNO_STRNDUP) endif() if (NOT HAVE_STRNLEN) add_definitions(-DNO_STRNLEN) endif() if (NOT HAVE_SYS_UN) add_definitions(-DNO_SYS_UN) endif(NOT HAVE_SYS_UN) if (HOST_BIG_ENDIAN) add_definitions(-DHOST_BIG_ENDIAN) endif() # Test 32/64 bits if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") add_definitions(-DEVHTP_SYS_ARCH=64) elseif("${CMAKE_SIZEOF_VOID_P}" EQUAL "4") add_definitions(-DEVHTP_SYS_ARCH=32) else() message(ERROR "Unable to determine architecture") endif() if (EVHTP_USE_DEFER_ACCEPT) add_definitions(-DUSE_DEFER_ACCEPT) endif(EVHTP_USE_DEFER_ACCEPT) set (CMAKE_INCLUDE_CURRENT_DIR ON) find_package(LibEvent REQUIRED) if (NOT EVHTP_DISABLE_REGEX) find_package(Oniguruma) if (NOT ONIGURUMA_FOUND) message(STATUS "${BoldRed}${CMAKE_BUILD_TYPE}ONIGURUMA NOT FOUND, DISABLING REGEX SUPPORT${ColourReset}") set(EVHTP_DISABLE_REGEX ON) endif() endif() if (NOT EVHTP_DISABLE_SSL) find_package(OpenSSL) endif() if (OPENSSL_FOUND) if (APPLE) # darwin based hosts have deprecated normal openssl functions, which is # annoying to see warnings, for now, just ignore them. add_definitions(-Wno-deprecated-declarations) endif() endif() if (EVHTP_USE_JEMALLOC) set(LIBEVHTP_EXTERNAL_LIBS ${LIBEVHTP_EXTERNAL_LIBS} jemalloc) endif() if (EVHTP_USE_TCMALLOC) set(LIBEVHTP_EXTERNAL_LIBS ${LIBEVHTP_EXTERNAL_LIBS} tcmalloc) endif() if (WIN32) add_definitions (-DWIN32 -march=i486) find_library (LIB_WS32 ws2_32) set (SYS_LIBS ${SYS_LIBS} ${LIB_WS32}) endif () if (EVHTP_BUILD_SHARED) set (EVHTP_LIBTYPE SHARED) else() set (EVHTP_LIBTYPE STATIC) endif() configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/include/evhtp/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/evhtp/config.h) include_directories( ${PROJECT_BINARY_DIR}/include ${PROJECT_SOURCE_DIR}/include ${OPENSSL_INCLUDE_DIRS} ${ONIGURUMA_INCLUDE_DIRS} ${LIBEVENT_INCLUDE_DIRS} ) set(LIBEVHTP_EXTERNAL_LIBS ${LIBEVENT_LIBRARIES} ${ONIGURUMA_LIBRARIES} ${OPENSSL_LIBRARIES} ) set (LIBEVHTP_SOURCE_FILES evhtp.c numtoa.c parser.c) if (NOT EVHTP_DISABLE_SSL) list (APPEND LIBEVHTP_SOURCE_FILES sslutils.c) endif() if (NOT EVHTP_DISABLE_EVTHR) list (APPEND LIBEVHTP_SOURCE_FILES thread.c) list (APPEND LIBEVHTP_EXTERNAL_LIBS pthread) endif() add_library (evhtp ${EVHTP_LIBTYPE} ${LIBEVHTP_SOURCE_FILES}) target_link_libraries (evhtp ${LIBEVHTP_EXTERNAL_LIBS}) if (EVHTP_BUILD_SHARED) set_target_properties(evhtp PROPERTIES VERSION "${PROJECT_VERSION}" SOVERSION 0 OUTPUT_NAME "evhtp") endif() add_subdirectory(examples) if (NOT LIB_INSTALL_DIR) set (LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib") endif (NOT LIB_INSTALL_DIR) if(NOT INCLUDE_INSTALL_DIR) set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include) endif() install (TARGETS evhtp DESTINATION ${LIB_INSTALL_DIR}) install ( FILES ${PROJECT_SOURCE_DIR}/include/evhtp/evhtp.h ${PROJECT_SOURCE_DIR}/include/evhtp/parser.h ${PROJECT_BINARY_DIR}/include/evhtp/config.h DESTINATION ${INCLUDE_INSTALL_DIR}/evhtp) install ( FILES ${PROJECT_SOURCE_DIR}/include/evhtp.h DESTINATION ${INCLUDE_INSTALL_DIR}) if (NOT EVHTP_DISABLE_SSL) install ( FILES ${PROJECT_SOURCE_DIR}/include/evhtp/sslutils.h DESTINATION ${INCLUDE_INSTALL_DIR}/evhtp) endif() if (NOT EVHTP_DISABLE_EVTHR) install ( FILES ${PROJECT_SOURCE_DIR}/include/evhtp/thread.h DESTINATION ${INCLUDE_INSTALL_DIR}/evhtp) endif() if (NOT HAVE_SYS_QUEUE) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/compat/sys/queue.h DESTINATION ${INCLUDE_INSTALL_DIR}/evhtp/sys) endif() IF (WIN32) install (FILES compat/sys/queue.h DESTINATION ${INCLUDE_INSTALL_DIR}/sys) ENDIF (WIN32) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/evhtp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/evhtp.pc @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/evhtp.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig") # add_definitions() calls do not show up in the C_FLAGS var # it is instead a property of COMPILE_DEFINITIONS. # # so we fetch the property into the variable CDEFS, iterate over each entry, # append it to a list, and finally replace ";" separators to -D to mimic a CFLAG get_property(CDEFS DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY COMPILE_DEFINITIONS) foreach(CDEF ${CDEFS}) list(APPEND CFLAG_LIST ${CDEF}) endforeach() string(REPLACE ";" " -D" CFLAG_DEFS "${CFLAG_LIST}") message("") message(STATUS "${BoldBlue}EVHTP_VERSION${ColourReset} : ${BoldGreen} ${PROJECT_VERSION}${ColourReset}") message(STATUS "${BoldBlue}EVHTP_DISABLE_SSL${ColourReset} : ${BoldGreen} ${EVHTP_DISABLE_SSL}${ColourReset}") message(STATUS "${BoldBlue}EVHTP_DISABLE_EVTHR${ColourReset} : ${BoldGreen} ${EVHTP_DISABLE_EVTHR}${ColourReset}") message(STATUS "${BoldBlue}EVHTP_DISABLE_REGEX${ColourReset} : ${BoldGreen} ${EVHTP_DISABLE_REGEX}${ColourReset}") message(STATUS "${BoldBlue}EVHTP_BUILD_SHARED${ColourReset} : ${BoldGreen} ${EVHTP_BUILD_SHARED}${ColourReset}") message(STATUS "${BoldBlue}EVHTP_USE_JEMALLOC${ColourReset} : ${BoldGreen} ${EVHTP_USE_JEMALLOC}${ColourReset}") message(STATUS "${BoldBlue}EVHTP_USE_TCMALLOC${ColourReset} : ${BoldGreen} ${EVHTP_USE_TCMALLOC}${ColourReset}") message("") message(STATUS "${Blue}CMAKE_BUILD_TYPE${ColourReset} : " ${BoldRed}${CMAKE_BUILD_TYPE}${ColourReset}) message(STATUS "${Blue}CMAKE_INSTALL_PREFIX${ColourReset} : " ${BoldMagenta}${CMAKE_INSTALL_PREFIX}${ColourReset}) message(STATUS "${Blue}CMAKE_BINARY_DIR${ColourReset} : " ${CMAKE_BINARY_DIR}) message(STATUS "${Blue}CMAKE_CURRENT_BINARY_DIR${ColourReset} : " ${CMAKE_CURRENT_BINARY_DIR}) message(STATUS "${Blue}CMAKE_CURRENT_SOURCE_DIR${ColourReset} : " ${CMAKE_CURRENT_SOURCE_DIR}) message(STATUS "${Blue}PROJECT_BINARY_DIR${ColourReset} : " ${PROJECT_BINARY_DIR}) message(STATUS "${Blue}PROJECT_SOURCE_DIR${ColourReset} : " ${PROJECT_SOURCE_DIR}) message(STATUS "${Blue}CMAKE_MODULE_PATH${ColourReset} : " ${CMAKE_MODULE_PATH}) message(STATUS "${Blue}CMAKE_SYSTEM_NAME${ColourReset} : " ${CMAKE_SYSTEM_NAME}) message(STATUS "${Blue}CMAKE_SYSTEM_VERSION${ColourReset} : " ${CMAKE_SYSTEM_VERSION}) message(STATUS "${Blue}CMAKE_C_COMPILER${ColourReset} : " ${CMAKE_C_COMPILER}) message(STATUS "${Blue}CMAKE_AR${ColourReset} : " ${CMAKE_AR}) message(STATUS "${Blue}CMAKE_RANLIB${ColourReset} : " ${CMAKE_RANLIB}) message(STATUS "${Blue}CFLAGS${ColourReset} : -D${CFLAG_DEFS} ${CMAKE_C_FLAGS}") if (CMAKE_BUILD_TYPE MATCHES Debug) message(" ${CMAKE_C_FLAGS_DEBUG}") else(CMAKE_BUILD_TYPE MATCHES Release) message(" ${CMAKE_C_FLAGS_RELEASE}") endif() message("") include(packaging) libevhtp-1.2.16/ChangeLog000066400000000000000000001341721322723041700152010ustar00rootroot00000000000000v1.2.16 o Added various SSL utility functions (ab190e1 Nathan French) o Fix compilation with shared libraries (6075565 Vincent Bernat) o Extensive example of streaming data without hogging memory (8f50b83 Nathan French) o Added EVHTP_FLAG_ENABLE_ALL to enable all socket flags (756a7e2 Nathan French) o only warn if setsockopt fails on EOPNOTSUPP (e5a3bdf Nathan French) o sslutil API updates [documentation / parsers / x-hdr helpers] (22207ad Nathan French) o OpenSSL 1.1.0 updates (load_*/_init/_add*) (3819073 Nathan French) o example_https_server now uses htp_sslutil_verify2opts (086afd1 Nathan French) o added example_https_client.c (933febf Nathan French) o [#69] Fix potential out of bound write to p->buf (8b68657 Nathan French) o [#72] Fix for oob read from htparser_get_strerror (75574ba Nathan French) o [#70] Do not disable EV_WRITE when pausing requests; The assumption that libevent would automatically start transferring pending data was wrong. (4cb782db77 Ultima1252) v1.2.15 o deprecated unset_hook and set_hook / cleanup (f1d2bd1 Nathan French) o updated travis configuration (45003e1 Nathan French) o Latest revision to support multiple SSL versions (4e353ba Tony Lambiris) o remove silly comment (d3da401 Nathan French) o check for errors in SSL RAND_(poll|bytes) calls (018dec8 Nathan French) o Added new virtualhost examples and functions (3467382 Nathan French) o added example request pausing app (282a1c9 Nathan French) o Added a more extensive SSL sandbox. (1e0c241 Nathan French) o Add examples/https/README (6ecf7e7 Nathan French) o exit failure in example_https when SSL is disabled (0848e08 Nathan French) o [#26] Use SSL_CTX_use_certificate_chain_file (4c4eb3a Nathan French) v1.2.14 - !!!! SECURITY UPDATE !!!! o Added doxygen tags. Updated Doxyfile to include more sources and headers. (50ab41d Dan Henderson) o SSL logging on handshake errors (0fff6bc Nathan French) o Remove built-in Oniguruma, now rely on system only (a31601c Nathan French) o FindLibevent now displays all deps (02a9e6c Nathan French) * THIS IS PRIMARILY A SECURITY UPDATE: Oniguruma (the regex library used by libevhtp) was packaged with the source; this was dumb. There were several CVE's recently published that made evhtp insecure. NOTE TO USERS: Libevhtp will no longer ship Oniguruma with the source. Instead, the build process will attempt and find a system-installed version instead. There is a big red warning if it is not found. A big thanks must go to @flokli (github) for pointing this out to me. v1.2.13 o Rename test->test_general (test is reservet target name in cmake) (d18c6d0 Vladimir Romanov) o Fix CMake files (a2fcb3d Vladimir Romanov) o Fix compilation (74537f5 Vladimir Romanov) o [#32] fix crash in evhtp_unbind_socket when evlistener fails (8149a84 Nathan French) o [#31] use CURRENT_SOURCE_DIR instead of SOURCE_DIR in cmake (aa8a989 Nathan French) o Added EVHTP_DEBUG option (with new logging) (f20e5cf Nathan French) o minor updates for pull #33 (ccf5ce4 Nathan French) o Make github stop bitching about newlines. (2c98506 Nathan French) o transparent reference counting API (6f4dc08 Nathan French) o Added some debug logs (0a79919 Nathan French) o use evhtp/onig (76ad658 Nathan French) o [#38] some extra debug logging (9db2231 Nathan French) o [#38] I'm an idiot, fix for parser_init. (7c13a57 Nathan French) o proper format str for logging in parser (de287b0 Nathan French) o Fix typo in `evthr_new()` function name (3cfeb03 Vincent Bernat) o Use evhtp/onig when system version isn't found. (a59b625 Nathan French) o fix build on SunOS (0c54892 Sebastian Wiedenroth) o Parser cleanup. (db040e9 Nathan French) o add onigposix to build dir evhtp if sys not found (7b9d1a2 Nathan French) o Ignore me, just formatting. (d911f58 Nathan French) o Update LICENSE file (new authors / dates), no other changes (12ed7e8 Nathan French) o fixes #43 (no need to check for -ldl or -lrt (bb0630c Nathan French) o Likely defect fix for issue 42. Check connection->type to ensure that it is not evhtp_type_client. Also checked for c->htp to not be null in a couple of ssl related functions. (77a0752 Dan Henderson) o Bugfixes and cleanup for htp__connection_writecb_ (440e5b9 Nathan French) o Update issues template. (960a788 Nathan French) o [Bugfix #51] htp__callback_find_ length check fix (44ddf9a Nathan French) o Better error handling for connection_ssl_new (369fe77 Nathan French) o connection_ssl_new log errors to stderr (a1f6926 Nathan French) o Add evhtp_query_free for test_query (b150ebe Nathan French) o formatting (2307737 Nathan French) o Add RPM to package generation (8479c02 Tony Lambiris) o replace cruft in evhtp_unset_all_hooks with for loop (4e62d69 Nathan French) o [#55] More flexible package generation (4c5dbe2 Nathan French) o added colors.cmake for pretty printing (bec3451 Nathan French) v1.2.12 o remove evhtp_heap, never used (d132721 Nathan French) o remove evhtp_json, never used (aec4b3c Nathan French) o [Issue#20] Include pathing (80a69cc Nathan French) o Fix deprecated usage (f4c42c7 Roman Gershman) o Create issue_template.md (8d1a95f Nathan French) o [Issue#25] Return NULL if callbacks == NULL for get_cb (cfcdba4 Nathan French) o adding more null checks. I wish __attr__(nonull) was portable (9b552b7 Nathan French) o adding some struct accessor defines to make life easier (613c5bf Nathan French) o I think I worked out most of the potential derefs (acb42cf Nathan French) o Fix potential null deref in htp__strndup_ (60b85ca Nathan French) o Fix dead assignments in parser.c (e0a48e1 Nathan French) o Eat our own dogfood (use_thread_wexit()) (ea37928 Nathan French) o No use for strlen() in a loop (6833080 Nathan French) o use PROJECT_BINARY_DIR for sys/ compat headers (012341c Nathan French) o return int for htp__path_new_ (bf59eb1 Nathan French) o more return normalization (91008a6 Nathan French) o allocation functions set *out to NULL (0305c10 Nathan French) o do not return on error from requie_uri (ddc2ce6 Nathan French) o added user-defined custom memory functions (73b54c6 Nathan French) o for some reason, htparser_init was being called manually with type_request?? (a153e5f Nathan French) o Add htp__strndup_ memfn wrapper (763168c Nathan French) o add a callback member in evhtp_ssl_cfg_t for customized decrypt privfile. fixes #16 (b3a4d42 h00360646) o Internalize some structs / deprecate evhtp_set_hook() (50ab327 Nathan French) o remove cruft (1b1a037 Nathan French) o add include directory for compat/sys headers (948c547 Nathan French) o export flag functions (3467cbb Nathan French) o formatting (4ec8dd3 Nathan French) o (evhtp_send_reply): Grab reference to bufferevent during write. (a976a2f Marcus Sundberg) o add thread exit callback for cleaning (0c7d9c4 jgli) o fix memory leak (a6b00cc jgli) o fix thread exit callback type (c8978b6 jgli) o Updates for threading functionality. (b634002 Mark Ellzey) o Added evhtp_accept_socket (a497a14 Mark Ellzey) o Forgot to export evhtp_accept_socke. (c94cb5b Mark Ellzey) o Formatting. (4a78297 Mark Ellzey) o Maybe I should spell rite. (2114210 Mark Ellzey) o Fix cmake compilation issue in centos7 (dfc8c2b kaustubh-d) o Remove double-free when SSL is used. (ee32b2a Jacob Marble) o Add CPack commands to build a debian package. (0c4a8ec Tom Pusateri) o [docs] added some doxygen groups (8a247f1 Mark Ellzey) o check res for bufferevent_socket_connect (#136) (70b68d4 mthomas) o Regression from commit 67ed0bc (c96c51e Ultima1252) o Added build/* to gitignore (c64f1dc Mark Ellzey) o Updating license to include Marcus Sundberg (801c52f Mark Ellzey) o [htparse] fix up some stuff to make coverity happy (abc7eb4 Mark Ellzey) o More coverity fixes (7d3cc52 Mark Ellzey) o check for sockopt returns (534bb48 Mark Ellzey) o master travis updates (2c6bb88 Mark Ellzey) o added testbigendian module for old cmake (fb6a866 Mark Ellzey) o Added initial evhtp_json API (6e48770 Mark Ellzey) o LICENSE update for ripping liblz json api (fb473ef Mark Ellzey) o Removing the SIGNED.md file (outdated anyway) (d4bcfa8 Mark Ellzey) o Update README (6ef073e Mark Ellzey) o updates / formatting / renames (926e355 Nathan French) o static funcs from now on will just return int (5112b6d Nathan French) o fix htp__use_threads_ call error (2ed2f7f weijiazhen) o FIX : Socket leakage on error #6 (d13b72b Nathan French) o Issue#6: make evhtp_accept_socket conform to api (d0347dc Nathan French) o Establish conformity through flags. (58da6dd Nathan French) o request flags (71341d9 Nathan French) o EVHTP_CONN flags (087e9a7 Nathan French) o Flag ops (and related functions) / cleanup (0abc96f Nathan French) o Add flags accessor (e0f04aa Nathan French) v1.2.11 o Grab reference to bufferevent during write. (a976a2f Marcus Sundberg) o add thread exit callback for cleaning (0c7d9c4 jgli) o fix memory leak (a6b00cc jgli) o fix thread exit callback type (c8978b6 jgli) o Updates for threading functionality. (b634002 Mark Ellzey) o Added evhtp_accept_socket (a497a14 Mark Ellzey) o Forgot to export evhtp_accept_socke. (c94cb5b Mark Ellzey) o Formatting. (4a78297 Mark Ellzey) o Maybe I should spell rite. (2114210 Mark Ellzey) o Fix cmake compilation issue in centos7 (dfc8c2b kaustubh-d) o Remove double-free when SSL is used. (ee32b2a Jacob Marble) o Add CPack commands to build a debian package. (0c4a8ec Tom Pusateri) o [docs] added some doxygen groups (8a247f1 Mark Ellzey) o check res for bufferevent_socket_connect (#136) (70b68d4 mthomas) o Regression from commit 67ed0bc (c96c51e Ultima1252) o Added build/* to gitignore (c64f1dc Mark Ellzey) o Updating license to include Marcus Sundberg (801c52f Mark Ellzey) o [htparse] fix up some stuff to make coverity happy (abc7eb4 Mark Ellzey) o Update README.markdown (3058a6e Mark Ellzey) o fixes and travis integration (ad92289 Mark Ellzey) o check for sockopt returns (534bb48 Mark Ellzey) o more travis updates (a9b4d38 Mark Ellzey) o travis (12a91ab Mark Ellzey) o Update README.markdown (b595a0d Mark Ellzey) o added initial lightweight heap allocator (6bf121b Mark Ellzey) o added initial lightweight heap allocator (5f634b6 Mark Ellzey) o Added initial evhtp_json API (6e48770 Mark Ellzey) o Removing the SIGNED.md file (outdated anyway) (d4bcfa8 Mark Ellzey) o updates / formatting / renames (926e355 Nathan French) o static funcs from now on will just return int (5112b6d Nathan French) o fix htp__use_threads_ call error (2ed2f7f weijiazhen) o FIX : Socket leakage on error #6 (d13b72b Nathan French) o Issue#6: make evhtp_accept_socket conform to api (d0347dc Nathan French) o Establish conformity through flags. (58da6dd Nathan French) o request flags (71341d9 Nathan French) o EVHTP_CONN flags (087e9a7 Nathan French) o Flag ops (and related functions) / cleanup (0abc96f Nathan French) o Add flags accessor (e0f04aa Nathan French) o Added missing extern "C"'s to public headers (9a0d250 Mark Ellzey) o CMakeLists cleanup, and pretty display (fc0f5da Mark Ellzey) o Oops, added back find_package() for libevent (9cdae63 Mark Ellzey) o Does image linking work here? (c10690b Mark Ellzey) o [htparse] have each state consume as needed (c34dba5 Mark Ellzey) o [htparse] formatting (4adcc67 Mark Ellzey) o Cleaning up things a bit (e7268d2 Mark Ellzey) o updated (718de1e Mark Ellzey) o Install into /include/evhtp/*.h (7065156 Mark Ellzey) o Optimizations / assertions / more safe_Free conversions (511fb19 Mark Ellzey) o Fix test_query tests (2d4c22f Mark Ellzey) o evthr initial shared pipe prototype (72f01f5 Mark Ellzey) o [evthr] shared pipe updates (0251481 Mark Ellzey) o Formatting. (f67f958 Mark Ellzey) o Added EVHTP_THR_SHARED_PIPE option (default ON) (d160452 Mark Ellzey) o formatting, fixes, assertions (b1cef41 Mark Ellzey) o Issue161 : deal with http 1.0 stuff (67ed0bc Mark Ellzey) o update test_basic (3ea2eba Mark Ellzey) o use evhtp_safe_free for authority (50dffb6 Mark Ellzey) o [htparse] keep-alive A_case fix (910137f Mark Ellzey) o sockflags, and more pipeline fixses (9b69ee7 Mark Ellzey) o Might want to make that listener nonblocking (d34a1d0 Mark Ellzey) o Coreent socket() assertion (d2263e0 Mark Ellzey) o htparse optimizations, backlog flag for test.c (9f5a38e Mark Ellzey) o scratch buffers / added test_perf for graphing (95e9ff3 Mark Ellzey) o rm'd ratelimit, added ability to use je/tc malloc (934cf5a Mark Ellzey) o Updating layouts - added lambda-pp code (8aef49d Mark Ellzey) o Cleanup time! (43005ad Mark Ellzey) o formatting (542a701 Mark Ellzey) o default response cb needs to set 0 len (2f1ecab Mark Ellzey) o use elif defined JE... (2682dca Mark Ellzey) o Added evhtp_get_cb (see full commit msg) (cdf5291 Mark Ellzey) o Various fixes / added SOVERSION'ing (73d7ee5 Mark Ellzey) o Move around tc/jemalloc ifdefs, btw tcmalloc is awful. (e456fe0 Mark Ellzey) o build: install evhtp.pc in /usr/lib/pkgconfig (0400ce0 Vincent Bernat) o jfkdlsa (fabe244 Mark Ellzey) o doxygen mod for evhtp.h (3d0e615 Mark Ellzey) o Default to EVHTP_PARSE_QUERY_FLAG_LENIENT for query parsing. (a462fc5 Mark Ellzey Thomas) o (evhtp_make_request): Add request->buffer_out as body if populated. (8bc4ab3 akalend) o Added setsockopt for ipv6 to only use ipv6 null check for ssl via akalend (acfc9dd Mark Ellzey Thomas) o modify str_to_uint64: uint64_t check can never be greater than UINT64_MAX (7d6135c xuhao) o use EVHTP_DISABLE_SSL instead of USE_SSL in header (c884191 Mark Ellzey) o export evhtp_connection_ssl_new via @rosenk, thanks! (c2168be Mark Ellzey) v1.2.10 o client ssl connection added. This requires a SSL_CTX and must be set by the user (439431a StunMan) o Only export public symbols. (cf66c7c Mark Ellzey) o Export evhtp_connection_set_ratelimit (df2fbd6 Mark Ellzey) o Uncomplexify evthr - huge performance boost. (8a6873e Mark Ellzey) o Do backlogs matter for evthr? I am thinking not. (76b4a96 Mark Ellzey) o Remove all the stupid backlog stuff. (cb4e280 Mark Ellzey) o Proposed changes for request pause/resume (pipelined) (6cd8946 Mark Ellzey) o Remove dead code from evthr (3d51c76 Mark Ellzey) o Remove dead declarations in evthr.h (72488a8 Mark Ellzey) o Be more consistent and slightly more lenient when handling GET params (86ba10b TJ Koblentz) o Empty query args processed with a val of NULL, extended the test_query code (bc897d2 Mark Ellzey) o add connection connected status for client connection (0e839f0 zhangjing) o Added on_event hook / cleanup (22c0fac Mark Ellzey) o Fixed bug with calling user-defined event callback :) (246e33d Mark Ellzey) o Add a pkg-config .pc file (6e85a9b Mark Ellzey) o TestBigEndian, and add big endian versions of _str*cmp macros (736bc80 Mark Ellzey) o ignore build directory (submodule doesnt become dirty on build) (1aceedb TJ Koblentz) o missing SSL guard + test compilation problems (-Wunused-but-set-variable) (787eeb9 TJ Koblentz) o Fix some empty GET param cases (with tests) (e282b6f TJ Koblentz) o Formatting cleanup (89f11cd Mark Ellzey) o Cleanup and fixes (61c7f4f Mark Ellzey) o (evhtp_free): Free ssl_ctx if used. (9318571 Marcus Sundberg) o Properly handle errors when allocating connections and requests. (ab2f567 Marcus Sundberg) o Prevent double free of request. (ec445f9 Martin Hedenfalk) o Stop parsing when we have got a complete response/request. (279a7d3 Marcus Sundberg) o (evhtp_connection_free): Call hook before freeing request. (c093ff9 Marcus Sundberg) o Properly parse CONNECT request line. (76f2830 Marcus Sundberg) o Support IPv6 literals in CONNECT authority string. (378b790 Marcus Sundberg) o Separate fragment string from query string according to RFC 3986. (a9c0679 Marcus Sundberg) o (evhtp_parse_query): Remove strange handling of '?' and '/'. (3d96f5e Marcus Sundberg) o Fix parse errors on trailing whitespace in Content-Length header. (b67068c Marcus Sundberg) o (evhtp_parse_query): Advance state to s_query_key after start. (a535258 Marcus Sundberg) o Add hooks for host and port, and fill in authority struct. (1be3a0f Marcus Sundberg) o (_evhtp_path_new): If len is 0 we set full to "/", just like path. (7d277f8 Marcus Sundberg) o Do not use different API/ABI when regexps are disabled. (39fcb28 Marcus Sundberg) o Fix warnings in test.c when EVHTP_DISABLE_REGEX is defined. (05b01cf Marcus Sundberg) o Add keepalive flag to connection. (74031a7 Marcus Sundberg) o Add evhtp_hook_on_conn_error hook for connection errors. (6c3ed3d Marcus Sundberg) o Added the function evhtp_connection_new_dns(). (47eecd0 Jan Edhner) o (evhtp_connection_new_dns): Handle errors. (b13994b Marcus Sundberg) o (evhtp_connection_new): Handle IPv6 addresses. (ac97672 Marcus Sundberg) o (_evhtp_create_headers): Use evbuffer_expand() to reserve space. (c4ed326 Marcus Sundberg) o Use malloc() instead of calloc() for buffers we will immediately fill. (3906a65 Marcus Sundberg) o added padding for all structs containing bitfields (a2ebece Mark Ellzey) o Update evthr.c (1d492cc romange) o Cleanup, use EVHTP_DISABLE_SSL for client (c32562a Mark Ellzey) o Various fixes, see full commit message (d8a4935 Mark Ellzey) o Added evhp_set_flags along with some documentation (27b5e8a Mark Ellzey) o Symbol exports moved into headers, more documentation (352aebe Mark Ellzey) o If available, use c99 to our advantage (read commit msg) (8a44e6f Mark Ellzey) o Remove duplicate evbuffer_new for buffer_in (5284ce3 Mark Ellzey) o Fix for client connect double-free condition (5267ed2 Mark Ellzey) o Don't set conn->bev to NULL if error (ce2197e Mark Ellzey) o Use new and faster wildcard matching function. (4047f1e Mark Ellzey) o Integer to string optimizations. (4189bfa Mark Ellzey) o Export the numtoa functions. (037c766 Mark Ellzey) o Disable unused tailq for client requests (for future use in pipelined) (3516276 Mark Ellzey) o More conversions from free to safe_free (05fd68c Mark Ellzey) v1.2.9 o Accept upper-case "Chunked" in addition to "chunked" for transfer encoding. (07a9322 Tim Burks) o [htparse] Added length checks for various header values (42050e8 Mark Ellzey) o Fix up some compiler warnings. (37e0c4e Mark Ellzey) o Use snprintf for resp hdrs, fallback to evbuffer_add_printf (c27ac5a Mark Ellzey) o status_code_to_str no longer uses RB lookups (d143a85 Mark Ellzey) o Don't call bufferevent_set_timeouts read/write NULL (f372d9c Mark Ellzey) o add 'libdir' support (3b7e250 Christian) o Add INCLUDE_INSTALL_DIR support (19cab6f Mark Ellzey) o Add back in tree.h (0f69673 Mark Ellzey) o Fix a segfault in regex handling (d1a4240 f69m) o Formatting cleanup (c0c2055 Mark Ellzey) o No need for sys/tree include (0d3619f Mark Ellzey) v1.2.8 o There's no libdl on FreeBSD. (a683a1f Adrian Chadd) o FreeBSD handles the POSIX_C_SOURCE definition very strictly - strncmp() and a lot of other stuff isn't defined in the 2001 standards. Thus, the functions aren't declared when you include the library headers. (0354a67 Adrian Chadd) v1.2.7 o Fix double free for evthr at shutdown (a2c2826 Azat Khuzhin) o Workaround for odd recv()'s being done when not requested. (2abd591 Mark Ellzey) o Only return from BEV_EOF|READ if errno is EAGAIN (00eef42 Mark Ellzey) o Add connection-specific rate-limiting function. (32f4115 Mark Ellzey) o Fix compilation without threads support (104eafd Janusz Dziemidowicz) o Fix compilation without regex support (a71c736 Janusz Dziemidowicz) o Fix crash when unable to bind socket with TCP deferred accept (60f9d71 Janusz Dziemidowicz) o Add missing call to SSL_CTX_free() in evhtp_free(). (891d0cf Janusz Dziemidowicz) o Malloc with 1 instead of sizeof(char) to avoid warnings (de445f7 Mark Ellzey) o Don't check for bevssl if EVHTP_DISABLE_SSL enabled (9369afc Mark Ellzey) o Use SSL_OP_NO_COMPRESSION if possible (2b714c5 Janusz Dziemidowicz) o Support for DH parameters file (3d22d56 Janusz Dziemidowicz) o Use proper #ifndef for ECDH (OPENSSL_NO_ECDH instead of OPENSSL_NO_EC) (35c21c4 Janusz Dziemidowicz) o Build evhtp-config.h to make things easier to link (77552e4 Mark Ellzey) o Undef optional flags as to not screw things up (374a269 Mark Ellzey) o Disable OpenSSL support if libevent_ssl is not found (820d314 Mark Ellzey) o Clean up unused variables in onig (2d38112 Mark Ellzey) v1.2.6 o Add connection-specific rate-limiting function. (d0caf82 Mark Ellzey) o Fix double free for evthr at shutdown (a514e80 Azat Khuzhin) v1.2.5 o Port to MinGW (b0bd094 poet) o Only check for 100-continue is enabled for servers, not clients. (0cea847 Mark Ellzey) o Only decrement the thread backlog if it is a server. (ceb1e60 Mark Ellzey) o Use MSYS Makefiles instead of MinGW Makefiles (fd6ae5d poetwang) v1.2.4 o run on_headers_begin if request type is response (this happens when a 100 continue type thing happens). (c3775e0 Mark Ellzey) v1.2.3 o Added evhtp_disable_100_continue() which informs evhtp to not send a 100 continue response. (1d0eb84 Mark Ellzey) v1.2.1 o Add signal.h as reported by gnehzuil (1ac8706 Mark Ellzey) o evthr_*set_backlog for SO_RCVBUF sockopts (93f35d9 Mark Ellzey) o SO_RCVBUF should be multiple of sizeof(evthr_cmd_t) (32bd829 Mark Ellzey) o Install regex header if enabled (6d61db4 Mark Ellzey) o Fix missing symbol SIGTERM by including signal.h (c449ad8 Dennis van Dok) o Examples will only built if "make examples" is run. (2abe24f Mark Ellzey) o Fixed enum typedef issue reported by scott-- (fee4166 Mark Ellzey) o Use system onig if avail, install includes in include/evthr (56067a9 Mark Ellzey) o Added evhtp_request_status() to get client response code. (9008a0f Mark Ellzey) o Removed unused code. (a7dabaa Mark Ellzey) v1.2.0 o Initial client API (0bd23c9 Mark Ellzey) o updating test_client.c (b53f25f Mark Ellzey) o Added sample chunk callbacks for client (849bddb Mark Ellzey) o Updating test_client.c (081bcfe Mark Ellzey) o Merge from master fixups (97bf2d1 Aaron Stone) o Add test_proxy: example code for using evhtp_client functionality to implement an HTTP reverse proxy. (73fdd0c Aaron Stone) o Add evhtp_kvs_add_kvs / evhtp_headers_add_headers to copy headers from source to dest. (b0aefd4 Aaron Stone) o Update test_proxy: pause the frontend request while processing the backend request; pass headers through with evhtp_headers_add_he o Accept requests like :// That is, without an extra / after the host. Consider the path as o Enable search engine for Doxygen. (fa4c9cc Mark Ellzey) o Fixed bug in body-based query checking. (8635b2b Mark Ellzey) o use EVHTP_DISABLE_SSL instead of USE_SSL (5052107 Mark Ellzey) o Fix some generic warnings in test_proxy.c (de206a8 Mark Ellzey) o Add documentation on fallthrough state for method proto. (c1f5216 Mark Ellzey) o (htparse): Properly propagate errors from hook_on_hdrs_begin_run() (1bb641f Marcus Sundberg) o (htparse): Use correct arguments to hook_path_run(). (8aaa419 Marcus Sundberg) o (htparse): Use correct length parameter when calling hooks. (51f78e4 Marcus Sundberg) o (htparse): Handle query parameters according to RFC 3986. (0e7bdcd Marcus Sundberg) o (htparse): Properly handle abolute-URI with empty path. (657fe3b Marcus Sundberg) o (htparse): Use correct arguments to hook_port_run(). (1f60434 Marcus Sundberg) o (htparse): Handle literal IPv6 addresses in URIs. (b495e81 Marcus Sundberg) o (htparse): Fix parsing of ftp and nfs schemes. (9f53ee5 Marcus Sundberg) o (htparse): Avoid memset()ing entire parse buffer on init. (6f1b93f Marcus Sundberg) o (htparse): Add support for CONNECT and PATCH methods. (622256b Marcus Sundberg) o Changed host_ipv6_done state to host_done (93e4f0f Mark Ellzey) o Fixes for multiple requests, new htparse tests (59de92a Mark Ellzey) o evhtp_free now frees all associated callbacks. (34b4261 Mark Ellzey) o Error checking for header_val_add() (c8165a0 Mark Ellzey) o Fix memory leak on hdr_val (a3ccc1e Mark Ellzey) o Added htparser test for empty header keys. (d5c640a Mark Ellzey) o parser fixes / deal with paused responses better. (9d8e243 Mark Ellzey) o Request pausing + threading fixes. (0e774db Mark Ellzey) o Fix format warnings for test_client (680bce6 Mark Ellzey) o rm cruft that got merged in that shouldn't have. (4289914 Mark Ellzey) o Apprently rm'd some critical things, added them back in. (9052e56 Mark Ellzey) o Added -DEVHTP_BUILD_SHARED:STRING=[OFF:ON] (4da6965 Mark Ellzey) o Removed old contrib crap. (cb62ca0 Mark Ellzey) o Fixing the -DEVHTP_BUILD_SHARED:STRING=ON option to only build static-builds or dynamic-builds when set. Avoid mixing and also the o Fix for issues/69 for evthr memset() (f471298 Mark Ellzey) o Browsers can send more trailing data after the application/x-www-form-urlencoded content type, for example, which charset is used. o get_content_len now returns non-modified content_len var (bb6cdac Mark Ellzey) o Added htparser_get_content_pending for fetching bytes left to process. (28710f8 Mark Ellzey) v1.1.7 o NULL check for evhtp_kvs_for_each (37f3864 Mark Ellzey) o Parse body as the query args if content-type is url-encoded. (ee25209 Mark Ellzey) o Added a write hook. (c32c0ed Mark Ellzey) v1.1.6 o Added __cplusplus into evthr (31e3973 Mark Ellzey) o Use evhtp_hook_generic_cb function pointer prototype when setting a hook (59f01f3 Gian-Paolo Musumeci) o ISO C fixes. (d1627f7 Mark Ellzey) o ISO C fixes. (5be2662 Mark Ellzey) o Renaming evhtp_hook_generic_cb to evhtp_hook, more ISO compliance. (60b3502 Mark Ellzey) o Removing strict flags from BaseConfig. (bdc6265 Mark Ellzey) v1.1.5 o Don't treat non hex chars as errors. (d8c584d Mark Ellzey) o If non hex, replace idx-1 with % (2783983 Mark Ellzey) o Fix off-by-one in parse_query. (86881c1 Mark Ellzey) o Do not use dynamically allocated timevals in evhtp_t. (3398442 Marcus Sundberg) o Only initialize status_code_tree once. (da64395 Marcus Sundberg) o Add evhtp_free() matching evhtp_new(). (60b141a Marcus Sundberg) o (evhtp_callback_free): Free hooks as well, if set. (6fff598 Marcus Sundberg) o (evhtp_callback_free): Free regexp pointer, not only regexp. (ab9adc6 Marcus Sundberg) o (evthr_pool_stop): Do not read queue entry after freeing. (f7cc5f2 Marcus Sundberg) o Use TAILQ_FOREACH_SAFE() in evhthr_pool_free() as well. (5c2adcc Marcus Sundberg) o (evhtp_free): Stop and free thread pool, if started. (f13c207 Marcus Sundberg) o Use libevent signal handling instead of signal() in test.c (e0b68b4 Marcus Sundberg) o Properly deallocate resources in test programs. (ea1bd57 Marcus Sundberg) o Add test_vhost to .gitignore (2a596c2 Marcus Sundberg) o Fix issue where SSL is not disabled if not found. (6f25292 Mark Ellzey) o Forgot LIBEVENT_OPENSSL_LIBRARY (5a933c7 Mark Ellzey) o add upport multiline header values. (325c5c7 Mark Ellzey) v1.1.4 o Fixed potential deadlock - reported by zhangpeihao (36ee722 Mark Ellzey) o Removing conflicting code from master branch. (51feaa9 Mark Ellzey) o Assure SSL is disabled if not found. (d4f3f35 Mark Ellzey) v1.1.3 o Added evhtp_unbind_socket() (9c9b508 Mark Ellzey) o Fixed issue with query argument decoding. (8ff9cf3 Mark Ellzey) o Fix issue 53, error on LF without CR's (d51b526 Mark Ellzey) v1.1.2 o Manual verification modes for SNI vhosts. (5f9b3a5 Mark Ellzey) v1.1.1 o Added max-body functionality. (7f0052a Mark Ellzey) o Added evhtp_set_max_keepalive_requests() (503ef28 Mark Ellzey) o inherit parent settings when adding vhosts. (c66161b Mark Ellzey) o Check for NULL before request status switch/case (f943a41 Mark Ellzey) o call tlsext_servername_callback in bind_sockaddr (d1b5433 Mark Ellzey) o Swap callback finder for path+file first. (2c30003 Mark Ellzey) o Disable BEV_WRITE when htparser_run() is active (414814a Mark Ellzey) v1.1.0 o Add ability for rules to be ordered. (ad8c468 Mark Ellzey) o Cleanup the old rule code. (fbfda73 Mark Ellzey) o Fixed missing variable in callback_find (4eb5e87 Mark Ellzey) v1.0.1 o Fixes to make coverity happy. (6776d85 Mark Ellzey) o Add support for empty header values. (7ec160f Mark Ellzey) v1.0.0 o Added some function documentation. (adc3ac6 Mark Ellzey) o Check for null deref on request check. (a722fc4 Mark Ellzey) o Fix for state set override for headers. (87fb859 Mark Ellzey) o No need for null checks before free(). (7783ca2 Mark Ellzey) o Bumping this up to 1.0.0 since it's been in production for a while. v0.4.17 o Added cmake option for disabling regex. (5ab2e4b Mark Ellzey) o Cleanup compiler warnings. (770a344 Mark Ellzey) o Fixed EVHTP_DISABLE_SSL compile. (e088f2b Mark Ellzey) v0.4.16 o Use POSIX 200112 for *NIX based builds. (09dbb0f Mark Ellzey) o Drain before letting ownership change. (cc50f84 Mark Ellzey) o jfkdal (5817bd3 Mark Ellzey) o Document evhtp_set_timeouts(). (acc3ed2 Mark Ellzey) v0.4.15 o Override global timeouts on a per-connection basis. (156f5ef Mark Ellzey) o Use evhtp_connection_set_timeouts in accept. (8eb5aa9 Mark Ellzey) o Remove use for alloca.h (339f9f2 Mark Ellzey) o Added a max-backlog for evthr/pools (e8ac1f9 Mark Ellzey) o Added evthr max backlog options. (a44d1c0 Mark Ellzey) o Initial commit of vhost feature. (8fb43e0 Mark Ellzey) o SSL SNI support. (4dcc68e Mark Ellzey) o Added test_vhost.c (c5a698e Mark Ellzey) o _evhtp_ssl_servername now uses wildcard matching. (00d0488 Mark Ellzey) o Added vhost aliases functionality. (bf5766f Mark Ellzey) o Readd inc/dec counter for evthr. (022ecf3 Mark Ellzey) o Make evhtp_request_free public. (9046466 Mark Ellzey) o Added a EVHTP_RES_USER evhtp_res type. (6b41cee Mark Ellzey) o Add support for ECDH ciphers (via cfg->named_curve string) (131cae3 b) o finding cbs/hook additions, hostname parse hooks (361a068 Mark Ellzey) o Optimizations. (caddf10 Mark Ellzey) o hook_on_hdrs_complete runs before last LF (2f77428 Mark Ellzey) v0.4.14 o Added example and documentation on proper thread-safe design with evhtp. (154017e Mark Ellzey) o Added glob/wildcard callback patterns. (d33a416 Mark Ellzey) o Fixed issue with path matching. (d0648ff Mark Ellzey) v0.4.13 o Decrement pending count when defer fails. (4e99de9 Mark Ellzey) o Updating documentation to say there is not much documentation. (184f33e Mark Ellzey) v0.4.11 o Added #ifndef EVHTP_DISABLE_EVTHR flags for compilation withotu threading support (d5bcc33 Thibaut Le Guilly) o Modify evthr_lock() macro previous patch for less ifdefs. (c800fe9 Mark Ellzey) o Took out more disable thread ifdefs. (b5de21e Mark Ellzey) o Remove use of bev_flush for evhtp_send_reply_end (9f2f31f Mark Ellzey) o Modify evthr_lock() macro previous patch for less ifdefs. (aa24e6a Mark Ellzey) o Took out more disable thread ifdefs. (accbe88 Mark Ellzey) o Bugfixes, additions, cleanup, see full commit msg. (e6155d8 Mark Ellzey) o Added evhtp_set_bev_flags() (a342903 Mark Ellzey) o Prep release v0.4.12 (acda3c8 Mark Ellzey) v0.4.10 o Add compile time EVHTP_DISABLE_REGEX option. (eab0d0f Andy Hochhaus) o Use evbuffer_add in lieu of add_reference for short writes. (2df5fbd Joe Nardone) o Added take_ownership functionality which frees resources but gives the underlying bufferevent to the caller. (75050f8 Mark Ellzey) o Release v0.4.11 (5d6339f Mark Ellzey) v0.4.9 o Fixed memleak with evthr_free() (10ad15a Mark Ellzey) o Correct SSL_Shutdown() usage. (f5f97ee Mark Ellzey) o Added total bytes read function in htparser. (4a9eefb Mark Ellzey) o Added rate-limiting option in test.c (b9e10c1 Mark Ellzey) o Don't add aux headers if content-type is multipart. (6be91ca Mark Ellzey) o Removed silly compile-time flags for OSX (0dd14a9 Mark Ellzey) o Ignore deprecated ssl warnings on OSX until further notice. (0aa4fb6 Mark Ellzey) o Prep release v0.4.10 (60d0f25 Mark Ellzey) v0.4.8 o Fix for non-system strndup. (d7486b4 Mark Ellzey) o Prepping release v0.4.9 (4ef6362 Mark Ellzey) v0.4.7 o Place _evhtp_run_pre_accept() into _evhtp_connection_accept() (e45adbd Mark Ellzey) o pre_accept_cb argument changes. (c2fbb86 Mark Ellzey) o Fixed test.c for pre_accept changes. (671a911 Mark Ellzey) o testing client ssl socket error log for debugging. (265437b Mark Ellzey) o Remove some extra logging. (dd45fb8 Mark Ellzey) o SSL client error handling debugging. (5c26eaf Mark Ellzey) o err (8f5013e Mark Ellzey) o removed some dbg msgs (84bec53 Mark Ellzey) o adding error to connection on eventcb (0c9690c Mark Ellzey) o Remove debug abort. (ca8089f Mark Ellzey) o Remove debug msg. (9a41148 Mark Ellzey) o Prepping release v0.4.8 (c140e2f Mark Ellzey) v0.4.6 o Fix to be able to set a verification "mode" to the SSL_CTX_set_verify() function without having to set a custom (*verify_callback). (f3c3f37 Oscar Koeroo) o Allow SSL_CTX_set_timeout value to be passed in via config. (4f775bd Stephen Cox) o Fixed issue with _evhtp_request_parser_path with no matched callbacks where the end offset was never being set. (Reported by snnn119@gmail.com) (0d20de9 Mark Ellzey) o evhtp_parse_query() is no longer limited to 1024 byte key/val (a8179a2 Mark Ellzey) o set request status to PAUSE if evhtp_request_pause() is called manually. (4d64111 Mark Ellzey) o Prepping release v0.4.7 (12d7cc4 Mark Ellzey) v0.4.5 o Don't treat EOF eventcb flags for ssl enabled connections as errors. (49c98b1 Mark Ellzey) o Add HTTP/1.1 chunked encoding interface. (69a29d3 Andy Hochhaus) o Added test-case for chunking API usage. (939517a Mark Ellzey) o Chunk API modifications, formatting cleanup. (b805dbd Mark Ellzey) o Added SSL_CTX_set_timeout() for openssl >= 1.0 (e6fa029 Mark Ellzey) o Prepping release 0.4.6 (81c493d Mark Ellzey) v0.4.3 o Added api docs (57370c1 Mark Ellzey) o Added various accessor functions, see full commitlog (16d3fdc Mark Ellzey) o Added evhtp_request_get_connection() (db1f023 Mark Ellzey) o Moved callback locking as optional via evhtp_use_callback_locks() (7a028eb Mark Ellzey) o Removing API docs. Generate yourself! (5bc036d Mark Ellzey) o added local PF_UNIX socket listen support (f6ef167 Mark Ellzey) o Adding sys/un.h checks. (6ec70e5 Mark Ellzey) o Added evhtp_bind_sockaddr() (8f38b01 Mark Ellzey) o Add a .gitignore file (09ac468 Nick Mathewson) o Stop using the deprecated event.h; use event2/event.h instead (c60715d Nick Mathewson) o Set -fno-strict-aliasing using gcc (4a43c0b Mark Ellzey) o Picked DISABLE_SSL fixes from nmathewson into develop (b2bfb2f Mark Ellzey) o Detect non-ascii hosts and refuse to build on them (b0c2267 Nick Mathewson) o Eliminate use of ctype.h (7d0084e Nick Mathewson) o Add checks for sys/queue.h and use compat when missing (f05b54a Mark Ellzey) o compat queue.h BSD-only move. (2b49db6 Mark Ellzey) o Removed README and added changelog generator. (219f2eb Mark Ellzey) o Prepping release v0.4.4 (d335798 Mark Ellzey) o ChangeLog for v0.4.4 (c5c6d19 Mark Ellzey) o Fixing size_t printf format issues. (c75bc5b Mark Ellzey) o Added support for the -C option in the test.c The internals were there, it just covers the getops. (e3636d5 Oscar Koeroo) o The struct evhtp_connection_s has a member ssl_ctx of type evhtp_ssl_t (a typedef of an SSL*) which by name is confusing with respect to the evhtp_ssl_ctx_t (a typedef of an SSL_CTX*). The member of struct evhtp_connection_s is now rename from ssl_ctx to ssl. (ca42b4d Oscar Koeroo) o Initial markdown-based API documentation. (c16b551 Mark Ellzey) o Documentation updates. (f097558 Mark Ellzey) o Documentation updates (98c8ff0 Mark Ellzey) o Added evhtp_unescape_string() to unescape query type strings. (d75904f Mark Ellzey) o added on_headers_start hook (before header parsing, post requestline parsing). (7076b8e Mark Ellzey) o Added htparser_set_(major|minor). fixed edgecase where major/minor is not yet set. (137aa19 Mark Ellzey) o Fix C++/clang++ build. (af2a0dd Andy Hochhaus) o Added #ifndef _GNU_SOURCE before setting it again. (f8a2308 Mark Ellzey) o Changes to DISABLE to EVHTP_DISABLE, also fixed enum hook missing from last merge. (0ab23de Mark Ellzey) o inline enum's should not be static. (5efd199 Mark Ellzey) o Set libevent as a required dependency (d08f4fd Mark Ellzey) o Prepping release v0.4.5 (a3731d7 Mark Ellzey) v0.4.2 o Moving libhtparse to just htparse (bf2e43a Mark Ellzey) o Thread-safe add/remove callback additions. (d916366 Mark Ellzey) o strn* compat functions set to static (cab9503 Mark Ellzey) o Fixed issue with OPTIONS requests. (a3487fd Mark Ellzey) o arg parsing fixes (8627d0d Mark Ellzey) o Added a basic example (c06ef72 Mark Ellzey) o Added IPv6 listener support. (c1482a2 Mark Ellzey) o Prepping release v0.4.3 (952baa9 Mark Ellzey) v0.4.1 o Removing tabs from ChangeLog (3f6f220 Mark Ellzey) o Add checks for sys/tree.h and compat when missing (40c87e5 Jason L. Shiffer) o Fix strdup build warnings/errors on OSX (84e17c1 Jason L. Shiffer) o SSL Threading changes (2631e7f Mark Ellzey) o Increasing the parser stack size. (b0df5a8 Mark Ellzey) o Added chunk hooking stuff. (2bcba66 Mark Ellzey) o jfdsla (1fa36b9 Mark Ellzey) o x509 updates (089bd8a Mark Ellzey) o Revert "x509 updates" (2cc9195 Mark Ellzey) o Add checks for sys/tree.h and compat when missing (8fa7e11 Jason L. Shiffer) o Fix strdup build warnings/errors on OSX (95cbcf3 Jason L. Shiffer) o Reworked SSL Thread initialization (38aee17 Mark Ellzey) o Increased default stack limit in htparse (48f338a Mark Ellzey) o chunk hooks, proper status code strings. (dbca386 Mark Ellzey) o Moved ./libhtparse to ./htparse (44c77d3 Mark Ellzey) o Prepping release v0.4.2 (815b023 Mark Ellzey) v0.4.0 o Deal with 100 return status with responses correctly (bb86d09 Mark Ellzey) o more 100 fixes (3ef168a Mark Ellzey) o hert pup (a637672 Mark Ellzey) o updates (83abb12 Mark Ellzey) o Adding struct sockaddr to connection_t (45c30c7 Mark Ellzey) o Swapping around SSL init globals (3327f44 Mark Ellzey) o Adding -lpthread for thread enabled configuration (80fff45 Mark Ellzey) o Added include_dir for libevent pathing (4c6dbc2 Mark Ellzey) o turn off compression by default (6244108 Mark Ellzey) o Cleanup (275329a Mark Ellzey) o static again (8796e10 Mark Ellzey) o testing timeouts (ac70f3c Mark Ellzey) o fix (1aa2d79 Mark Ellzey) o more timeout tests (7f29ff6 Mark Ellzey) o SSL 1.x optimizations / timeouts (b693c7f Mark Ellzey) o Adding LICENSE (c1ba152 Mark Ellzey) o blerp (1623480 Mark Ellzey) o hrm (964aab3 Mark Ellzey) o added backlog arg to evhtp_bind_socket() (584e73e Mark Ellzey) o Adding backlog to bufferevent (c740b0c Mark Ellzey) o blerp (7c8fe61 Mark Ellzey) o htparser fix (c76e3ea Mark Ellzey) o Re-added user-set timeouts (ec246ff Mark Ellzey) o Changing static len checks with sizeof() (7c3a79f Mark Ellzey) o Fixed over/underflow condition in str_to_uint64 (c72deee Mark Ellzey) o Fixed issue with state transition into read_body (0fc1897 Mark Ellzey) o added static rt (1910c19 Mark Ellzey) o Fixed incorrect integer conversion which misses zero edge case. (c562ede Mark Ellzey) o Added correct status code string definitions. (a5bc03d Mark Ellzey) o Include RT and DL if avail when linking test. SSL needs them. (29e35fb Mark Ellzey) o default cb now returns 404 (fd4e3cb Mark Ellzey) o Prepping release v0.4.1 (246a5da Mark Ellzey) v0.3.7 o linking ChangeLog to README (4da3a26 Mark Ellzey) o updated htparse (1706b82 Mark Ellzey) o removing cruft (ac3b4f4 Mark Ellzey) o killkillkill (45d0dfa Mark Ellzey) o More logical structure (3ee2531 Mark Ellzey) o cleanup (f7e3af2 Mark Ellzey) o blerp (2833315 Mark Ellzey) o getting better (6daf3ca Mark Ellzey) o Request pipeline now functional. (99554e3 Mark Ellzey) o derpityderp (1bd1b4e Mark Ellzey) o cruft (45e5a33 Mark Ellzey) o Fixed an issue with the body parser callback (f130965 Mark Ellzey) o Major cleanup / re-factor (0752eef Mark Ellzey) o fixups (0cc0980 Mark Ellzey) o Added some more documentation (56c08d6 Mark Ellzey) o documentation updates (3715c81 Mark Ellzey) o Added better functionality, more docs - see full commit log (8a8e555 Mark Ellzey) o Added Basic reply functions (b5434de Mark Ellzey) o More updates - perf updates - bug fixes (0b050cc Mark Ellzey) o updating ssl and test.c (37191f9 Mark Ellzey) o bugfix in kv_add (3a18380 Mark Ellzey) o pausing / fixes / request and connection fini hooks (3e98351 Mark Ellzey) o fixed all the pause issues.. (658650e Mark Ellzey) o threading fixes (97f18c5 Mark Ellzey) o some optimizations (6a747c6 Mark Ellzey) o blerp (f04c39a Mark Ellzey) o Added CA Path option for ssl_cfg. (Thanks Oscar Koeroo) (8ace875 Mark Ellzey) o Added x509_verify_cb, max_verify_depth, verify_peer and store_flags option to the struct evhtp_ssl_cfg_s. And also added HTTP return code 418 (e92e882 Oscar Koeroo) o Adding dummy callbacks and values to the test.c program. (e703100 Oscar Koeroo) o max_verify_depth -> verify_depth (556d722 Mark Ellzey) o OSX Compat / fixes (e844f9a Mark Ellzey) o cleanup (f7f25ef Mark Ellzey) o docs, cleanup (022d424 Mark Ellzey) o created verify and verify depth callbacks types (instead of using void *) (8a2f6d8 Mark Ellzey) o cleanup (aee4707 Mark Ellzey) o HTTP response parsing in libhtparser (6855a08 Mark Ellzey) o fix with evthr (d4a63d8 Mark Ellzey) o Added htparser_get_status (dae487a Mark Ellzey) o fixes (7c6f5ab Mark Ellzey) o send_reply start/body/end (33bfade Mark Ellzey) o thread initialization functionality (362f902 Mark Ellzey) o Content-Length duplicate header fix (8f74408 Mark Ellzey) o Added chunk_complete and chunks_complete callback hooks (f391855 Mark Ellzey) o Added some documentation (7853017 Mark Ellzey) o fixed issue with bufferevent SSL events (a34b413 Mark Ellzey) o on_new_chunk bugfix (de39114 Mark Ellzey) o Additions for HTTP/1.1 / other additions / fixes (f865ff4 Mark Ellzey) o Fixed conditional bug (c5c4aa4 Mark Ellzey) o fixed some bugs dealing with parsing and schemes (258cbec Mark Ellzey) o SSL shutdown / bugfixes / see commit log (c850692 Mark Ellzey) o Making static (add8058 Mark Ellzey) o linking ChangeLog to README (61866b8 Mark Ellzey) o fixes, docs, features: (see full commit log) (01e61e2 Mark Ellzey) o SSL updates (see full commit log) (b3cfb24 Mark Ellzey) o bugfix in kv_add (ed5699e Mark Ellzey) o Hook macros, fixes, additions (see full commit log) (ef3fee3 Mark Ellzey) o Added CA Path option for ssl_cfg. (Thanks Oscar Koeroo) (7b79640 Mark Ellzey) o SSL verification configuration options (62c07ff Oscar Koeroo) o OSX Compatability fixes (955133d Mark Ellzey) o Created verify and verify depth callbacks types (instead of using void *) (a2a0f09 Mark Ellzey) o HTTP response parsing in libhtparser (29ac2e7 Mark Ellzey) o evthr bugfix in evthr_new() args (17aede7 Mark Ellzey) o Added htparser_get_status to libhtparse (7b16bd1 Mark Ellzey) o Added streaming reply functionality (f0a8ed3 Mark Ellzey) o thread initialization functionality (78a6863 Mark Ellzey) o Content-Length duplicate header fix (b7d283f Mark Ellzey) o Additional libevhtparse chunk-specific hooks and documentation. (c0c6e59 Mark Ellzey) o fixed issue with bufferevent SSL events (fbe3c8b Mark Ellzey) o libhtparse on_new_chunk bugfix (d352dd1 Mark Ellzey) o Additions for HTTP/1.1 / other additions / fixes (c2883eb Mark Ellzey) o Fixed conditional bug for chunked responses (b159c32 Mark Ellzey) o libhtparse fixes when dealing with requests with schema data. (efdc171 Mark Ellzey) o SSL shutdown / bugfixes / see commit log (482d0bd Mark Ellzey) o Modified so that libevhtp creates a static library instead of shared. (ba170f1 Mark Ellzey) o Rebase fix for htparser_init() (ce446b0 Mark Ellzey) o Updating for release 0.4.0 (872c243 Mark Ellzey) v0.3.6 o Matched callback hooking (5b385a6 Mark Ellzey) o Drop connection with invalid requests. (e4cd611 Mark Ellzey) o added some more requests accessors (d9cf7d5 Mark Ellzey) o added a finished hook (c04e61b Mark Ellzey) o added evhtp_request_set_cbargs() (28a22dd Mark Ellzey) o Bunch of stuff - see full commit message. (ae49ecf Mark Ellzey) o If compiling as debug, http-parser will be pre-processed then compiled. (easier to debug the shitty and unnecessary macro-based function prototypes). (c378db3 Mark Ellzey) o Mods to pass -Wextra (2b44c46 Mark Ellzey) o More -Wextra mods (f2d6c9e Mark Ellzey) o a bit broken (17d1572 Mark Ellzey) o adding gitignore (b8eba44 Mark Ellzey) o Removing dep for http_parser over to my libhtparse codebase (11b484e Mark Ellzey) o Adding libhtparse.... (dd61c20 Mark Ellzey) o Prepping release v0.3.7 (64db298 Mark Ellzey) v0.3.5 o Added various SSL information accessors (8db938e Mark Ellzey) o evhtp_hdr functions / default 404 cb / fixes (37dbe4f Mark Ellzey) o Prepping the removal of submodules (354e71d Mark Ellzey) o No more submodules (1e0ec98 Mark Ellzey) o updating release_prep (5a5e495 Mark Ellzey) o Prepping release v0.3.6 (e582c7c Mark Ellzey) o Updated ChangeLog (03a8536 Mark Ellzey) v0.3.4 o Added evhtp_set_regex_cb for matching URI with a regex. (99e8e64 Mark Ellzey) o Adding oniguruma submodule (30adea5 Mark Ellzey) o .. (2161264 Mark Ellzey) o Switched over to oniguruma for regex (b80820b Mark Ellzey) o updates (f2bb622 Mark Ellzey) o httparser updates (1befd5c Mark Ellzey) o make install rules, cleanup of dependencies (68cb269 Mark Ellzey) o cmake onig test compile (75ede66 Mark Ellzey) o Added find_callbacks_woffsets (0174055 Mark Ellzey) o evhtp_request_t is now private. (c2cadcd Mark Ellzey) o added various request accessors (6964387 Mark Ellzey) o Even more evhtp_request_t accessors. (8c45457 Mark Ellzey) o Better error / response handling. (6e869fc Mark Ellzey) o Prepping release v0.3.5 (6a84efe Mark Ellzey) v0.3.3 o SSL and other various changes (see commit log) (76dc58d Mark Ellzey) o Added ChangeLog (33bab50 Mark Ellzey) o Updated version information. (df80701 Mark Ellzey) o Added contrib section with misc patches. (0b2bb36 Mark Ellzey) o And place it in the right directory :) (e337cb7 Mark Ellzey) o Fixing up problems with the conflict resolution (6a606a4 Mark Ellzey) v0.3.2 o initial SSL support, junk is brizzoke (fbbc2fa Mark Ellzey) o cleanup (57e309c Mark Ellzey) o not working as intended, REBASE THIS JUNK (f645813 Mark Ellzey) o SSL session caching. (590e226 Mark Ellzey) o Adding a builtin cache (0e1d01e Mark Ellzey) o Added SSL thread-safe functionality. (a9db78b Mark Ellzey) o cleanup (6f71526 Mark Ellzey) o Properly expire cache entries. (65f017b Mark Ellzey) o Cleanup (edd1b44 Mark Ellzey) o Prepping v0.3.3 (67307f1 Mark Ellzey) v0.3.1 o Modifying to use bufferevents (0c4b22e Mark Ellzey) o Converting back to bevents after perf issue solved (6771104 Mark Ellzey) o Prepping release 0.3.2 (758bb16 Mark Ellzey) v0.3.0 o Optional evthr support (5392bc9 Mark Ellzey) o Prep v0.3.1 (8a6c836 Mark Ellzey) o Prep release 0.3.1 (33f1a82 Mark Ellzey) libevhtp-1.2.16/Doxyfile000066400000000000000000000123641322723041700151330ustar00rootroot00000000000000OUTPUT_DIRECTORY = ./html/docs/ DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = Libevhtp PROJECT_NUMBER = 1.2.13 CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = YES MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES TYPEDEF_HIDES_STRUCT = YES EXTRACT_ALL = YES EXTRACT_PRIVATE = NO EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = NO FILE_VERSION_FILTER = QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = INPUT = evhtp.c parser.c thread.c log.h refcount.h ./include/evhtp.h ./include/internal.h ./include/numtoa.h ./include/evhtp/evhtp.h include/evhtp/config.h include/evhtp/parser.h INPUT_ENCODING = UTF-8 RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = */.git/* .*.d EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = YES ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project HTML_DYNAMIC_SECTIONS = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = GENERATE_MAN = YES MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES INCLUDE_PATH = ./ ./include ./include/evhtp INCLUDE_FILE_PATTERNS = *.h PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = YES CALLER_GRAPH = YES GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = YES DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES SEARCHENGINE = NO DISABLE_INDEX = YES GENERATE_TREEVIEW = YES libevhtp-1.2.16/LICENSE000066400000000000000000000035131322723041700144260ustar00rootroot00000000000000Libevhtp is available for use under the following license, commonly known as the 3-clause (or "modified") BSD license: ============================== Copyright (c) 2010-2017 Nathan French, Dan Henderson, Marcus Sundberg Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ============================== Portions of Libevhtp are based on works by others, also made available by them under the three-clause BSD license above. The functions include: evhtp.c: htp__glob_match_(): Copyright (c) 2006-2009, Salvatore Sanfilippo refcount.h: Copyright (c) 2016-2017, Mark Ellzey libevhtp-1.2.16/README.markdown000066400000000000000000000137521322723041700161300ustar00rootroot00000000000000| ![LOGO](http://i.imgur.com/uBd4iIz.png) |

Libevhtp

| | :------------- | -------------: | [![Build Status](https://travis-ci.org/criticalstack/libevhtp.svg?branch=develop)](https://travis-ci.org/criticalstack/libevhtp) Coverity Scan Build Status This document describes details on using the evhtp API. This document is probably not very awesome, it's best to look at test.c to see advanced usage. # Prebuilt Packages [![Package Versions](https://repology.org/badge/vertical-allrepos/libevhtp.svg)](https://repology.org/metapackage/libevhtp) ## Required Dependencies * [gcc](http://gcc.gnu.org/) * [Libevent2](http://libevent.org) ## Optional Dependencies * [OpenSSL](http://openssl.org) * pthreads * [onig (regex)](https://github.com/kkos/oniguruma) ## Building * cd build * cmake .. * make * make examples ## Overview *** Libevhtp was created as a replacement API for Libevent's current HTTP API. The reality of libevent's http interface is that it was created as a JIT server, meaning the developer never thought of it being used for creating a full-fledged HTTP service. Infact I am under the impression that the libevent http API was designed almost as an example of what you can do with libevent. It's not Apache in a box, but more and more developers are attempting to use it as so. ### Libevent's HTTP pitfalls *** * It was not designed to be a fully functional HTTP server. * The code is messy, abstractions are almost non-existent, and feature-creep has made long-term maintainability very hard. * The parsing code is slow and requires data to be buffered before a full parse can be completed. This results in extranious memory usage and lots of string comparison functions. * There is no method for a user to access various parts of the request processing cycle. For example if the "Content-Length" header has a value of 50000, your callback is not executed until all 50000 bytes have been read. * Setting callback URI's do exact matches; meaning if you set a callback for "/foo/", requests for "/foo/bar/" are ignored. * Creating an HTTPS server is hard, it requires a bunch of work to be done on the underlying bufferevents. * As far as I know, streaming data back to a client is hard, if not impossible without messing with underlying bufferevents. * It's confusing to work with, this is probably due to the lack of proper documentation. Libevhtp attempts to address these problems along with a wide variety of cool mechanisms allowing a developer to have complete control over your server operations. This is not to say the API cannot be used in a very simplistic manner - a developer can easily create a backwards compatible version of libevent's HTTP server to libevhtp. ### A bit about the architecture of libevhtp *** #### Bootstrapping 1. Create a parent evhtp_t structure. 2. Assign callbacks to the parent for specific URIs or posix-regex based URI's 3. Optionally assign per-connection hooks (see hooks) to the callbacks. 4. Optionally assign pre-accept and post-accept callbacks for incoming connections. 5. Optionally enable built-in threadpool for connection handling (lock-free, and non-blocking). 6. Optionally morph your server to HTTPS. 7. Start the evhtp listener. #### Request handling. 1. Optionally deal with pre-accept and post-accept callbacks if they exist, allowing for a connection to be rejected if the function deems it as unacceptable. 2. Optionally assign per-request hooks (see hooks) for a request (the most optimal place for setting these hooks is on a post-accept callback). 3. Deal with either per-connection or per-request hook callbacks if they exist. 4. Once the request has been fully processed, inform evhtp to send a reply. ##### A very basic example with no optional conditions. #include #include void testcb(evhtp_request_t * req, void * a) { evbuffer_add_reference(req->buffer_out, "foobar", 6, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } int main(int argc, char ** argv) { evbase_t * evbase = event_base_new(); evhtp_t * htp = evhtp_new(evbase, NULL); evhtp_set_cb(htp, "/test", testcb, NULL); evhtp_bind_socket(htp, "0.0.0.0", 8080, 1024); event_base_loop(evbase, 0); return 0; } ## Is evhtp thread-safe? For simple usage with evhtp_use_threads(), yes. But for more extreme cases: sorta, you are bound to the thread mechanisms of libevent itself. But with proper design around libevhtp, thread issues can be out-of-sight, out-of-mind. What do you mean by this "proper design" statement? Refer to the code in ./examples/thread_design.c. The comments go into great detail of the hows and whys for proper design using libevhtp's threading model. This example uses redis, mainly because most people who have asked me "is evhtp thread-safe" were attempting to *other things* before sending a response to a request. And on more than one occasion, those *other things* were communicating with redis. ## For Windows MinGW cmake -G "MSYS Makefiles" -DCMAKE_INCLUDE_PATH=/mingw/include -DCMAKE_LIBRARY_PATH=/mingw/lib -DCMAKE_INSTALL_PREFIX=/mingw . make ## Performance stuff While we never documented any benchmark publically, the popular open source project [ZIMG](http://zimg.buaa.us) did a bit of that for us.The ZIMG team decided to move away from NGINX to libevhtp for their software, and the results were pretty outstanding. Here is a graph showing their application under very high load ![ZIMG GRAPH](/zimg_vs_nginx.png) The X-axis is the number of connections, while the Y-axis is requests per second. You can read the whole article here: [Architecture Design of an Image Server](http://zimg.buaa.us/documents/Architecture_Design_of_Image_Server/) Slightly outdated (Now faster!) ![HI NGINX](http://i.imgur.com/kiSkSLH.png) Coverity Scan Build Status libevhtp-1.2.16/build/000077500000000000000000000000001322723041700145165ustar00rootroot00000000000000libevhtp-1.2.16/build/.gitkeep000066400000000000000000000000001322723041700161350ustar00rootroot00000000000000libevhtp-1.2.16/cmake/000077500000000000000000000000001322723041700144775ustar00rootroot00000000000000libevhtp-1.2.16/cmake/FindLibEvent.cmake000066400000000000000000000020651322723041700200150ustar00rootroot00000000000000# - Try to find the LibEvent config processing library # Once done this will define # # LIBEVENT_FOUND - System has LibEvent # LIBEVENT_INCLUDE_DIR - the LibEvent include directory # LIBEVENT_LIBRARIES 0 The libraries needed to use LibEvent find_path (LIBEVENT_INCLUDE_DIR NAMES event.h) find_library (LIBEVENT_LIBRARY NAMES event) find_library (LIBEVENT_CORE NAMES event_core) find_library (LIBEVENT_EXTRA NAMES event_extra) if (NOT EVHTP_DISABLE_EVTHR) find_library (LIBEVENT_THREAD NAMES event_pthreads) endif() if (NOT EVHTP_DISABLE_SSL) find_library (LIBEVENT_SSL NAMES event_openssl) endif() include (FindPackageHandleStandardArgs) set (LIBEVENT_INCLUDE_DIRS ${LIBEVENT_INCLUDE_DIR}) set (LIBEVENT_LIBRARIES ${LIBEVENT_LIBRARY} ${LIBEVENT_SSL} ${LIBEVENT_CORE} ${LIBEVENT_EXTRA} ${LIBEVENT_THREAD} ${LIBEVENT_EXTRA}) find_package_handle_standard_args (LIBEVENT DEFAULT_MSG LIBEVENT_LIBRARIES LIBEVENT_INCLUDE_DIR) mark_as_advanced(LIBEVENT_INCLUDE_DIRS LIBEVENT_LIBRARIES) libevhtp-1.2.16/cmake/FindOniguruma.cmake000066400000000000000000000027271322723041700202600ustar00rootroot00000000000000# Copyright (C) 2007-2009 LuaDist. # Created by Peter Kapec # Redistribution and use of this file is allowed according to the terms of the MIT license. # For details see the COPYRIGHT file distributed with LuaDist. # Note: # Searching headers and libraries is very simple and is NOT as powerful as scripts # distributed with CMake, because LuaDist defines directories to search for. # Everyone is encouraged to contact the author with improvements. Maybe this file # becomes part of CMake distribution sometimes. # - Find oniguruma # Find the native ONIGURUMA headers and libraries. # # ONIGURUMA_INCLUDE_DIRS - where to find oniguruma.h, etc. # ONIGURUMA_LIBRARIES - List of libraries when using onig. # ONIGURUMA_FOUND - True if oniguruma found. # Look for the header file. FIND_PATH(ONIGURUMA_INCLUDE_DIR NAMES oniguruma.h) # Look for the library. FIND_LIBRARY(ONIGURUMA_LIBRARY NAMES onig) # Handle the QUIETLY and REQUIRED arguments and set ONIGURUMA_FOUND to TRUE if all listed variables are TRUE. INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(ONIGURUMA DEFAULT_MSG ONIGURUMA_LIBRARY ONIGURUMA_INCLUDE_DIR) # Copy the results to the output variables. IF(ONIGURUMA_FOUND) SET(ONIGURUMA_LIBRARIES ${ONIGURUMA_LIBRARY}) SET(ONIGURUMA_INCLUDE_DIRS ${ONIGURUMA_INCLUDE_DIR}) ELSE(ONIGURUMA_FOUND) SET(ONIGURUMA_LIBRARIES) SET(ONIGURUMA_INCLUDE_DIRS) ENDIF(ONIGURUMA_FOUND) MARK_AS_ADVANCED(ONIGURUMA_INCLUDE_DIRS ONIGURUMA_LIBRARIES) libevhtp-1.2.16/cmake/TestBigEndian.cmake000066400000000000000000000110401322723041700201550ustar00rootroot00000000000000#.rst: # TestBigEndian # ------------- # # Define macro to determine endian type # # Check if the system is big endian or little endian # # :: # # TEST_BIG_ENDIAN(VARIABLE) # VARIABLE - variable to store the result to #============================================================================= # Copyright 2002-2009 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 distribute this file outside of CMake, substitute the full # License text for the above reference.) macro(TEST_BIG_ENDIAN VARIABLE) if(NOT DEFINED HAVE_${VARIABLE}) message(STATUS "Check if the system is big endian") message(STATUS "Searching 16 bit integer") include(CheckTypeSize) CHECK_TYPE_SIZE("unsigned short" CMAKE_SIZEOF_UNSIGNED_SHORT) if(CMAKE_SIZEOF_UNSIGNED_SHORT EQUAL 2) message(STATUS "Using unsigned short") set(CMAKE_16BIT_TYPE "unsigned short") else() CHECK_TYPE_SIZE("unsigned int" CMAKE_SIZEOF_UNSIGNED_INT) if(CMAKE_SIZEOF_UNSIGNED_INT) message(STATUS "Using unsigned int") set(CMAKE_16BIT_TYPE "unsigned int") else() CHECK_TYPE_SIZE("unsigned long" CMAKE_SIZEOF_UNSIGNED_LONG) if(CMAKE_SIZEOF_UNSIGNED_LONG) message(STATUS "Using unsigned long") set(CMAKE_16BIT_TYPE "unsigned long") else() message(FATAL_ERROR "no suitable type found") endif() endif() endif() configure_file("${CMAKE_ROOT}/Modules/TestEndianess.c.in" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/TestEndianess.c" @ONLY) file(READ "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/TestEndianess.c" TEST_ENDIANESS_FILE_CONTENT) try_compile(HAVE_${VARIABLE} "${CMAKE_BINARY_DIR}" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/TestEndianess.c" OUTPUT_VARIABLE OUTPUT COPY_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestEndianess.bin" ) if(HAVE_${VARIABLE}) file(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestEndianess.bin" CMAKE_TEST_ENDIANESS_STRINGS_LE LIMIT_COUNT 1 REGEX "THIS IS LITTLE ENDIAN") file(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestEndianess.bin" CMAKE_TEST_ENDIANESS_STRINGS_BE LIMIT_COUNT 1 REGEX "THIS IS BIG ENDIAN") # on mac, if there are universal binaries built both will be true # return the result depending on the machine on which cmake runs if(CMAKE_TEST_ENDIANESS_STRINGS_BE AND CMAKE_TEST_ENDIANESS_STRINGS_LE) if(CMAKE_SYSTEM_PROCESSOR MATCHES powerpc) set(CMAKE_TEST_ENDIANESS_STRINGS_BE TRUE) set(CMAKE_TEST_ENDIANESS_STRINGS_LE FALSE) else() set(CMAKE_TEST_ENDIANESS_STRINGS_BE FALSE) set(CMAKE_TEST_ENDIANESS_STRINGS_LE TRUE) endif() message(STATUS "TEST_BIG_ENDIAN found different results, consider setting CMAKE_OSX_ARCHITECTURES or CMAKE_TRY_COMPILE_OSX_ARCHITECTURES to one or no architecture !") endif() if(CMAKE_TEST_ENDIANESS_STRINGS_LE) set(${VARIABLE} 0 CACHE INTERNAL "Result of TEST_BIG_ENDIAN" FORCE) message(STATUS "Check if the system is big endian - little endian") endif() if(CMAKE_TEST_ENDIANESS_STRINGS_BE) set(${VARIABLE} 1 CACHE INTERNAL "Result of TEST_BIG_ENDIAN" FORCE) message(STATUS "Check if the system is big endian - big endian") endif() if(NOT CMAKE_TEST_ENDIANESS_STRINGS_BE AND NOT CMAKE_TEST_ENDIANESS_STRINGS_LE) message(SEND_ERROR "TEST_BIG_ENDIAN found no result!") endif() file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "Determining if the system is big endian passed with the following output:\n${OUTPUT}\nTestEndianess.c:\n${TEST_ENDIANESS_FILE_CONTENT}\n\n") else() message(STATUS "Check if the system is big endian - failed") file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Determining if the system is big endian failed with the following output:\n${OUTPUT}\nTestEndianess.c:\n${TEST_ENDIANESS_FILE_CONTENT}\n\n") set(${VARIABLE}) endif() endif() endmacro() libevhtp-1.2.16/cmake/colors.cmake000066400000000000000000000011331322723041700170000ustar00rootroot00000000000000if(NOT WIN32) string(ASCII 27 Esc) set(ColourReset "${Esc}[m") set(ColourBold "${Esc}[1m") set(Red "${Esc}[31m") set(Green "${Esc}[32m") set(Yellow "${Esc}[33m") set(Blue "${Esc}[34m") set(Magenta "${Esc}[35m") set(Cyan "${Esc}[36m") set(White "${Esc}[37m") set(BoldRed "${Esc}[1;31m") set(BoldGreen "${Esc}[1;32m") set(BoldYellow "${Esc}[1;33m") set(BoldBlue "${Esc}[1;34m") set(BoldMagenta "${Esc}[1;35m") set(BoldCyan "${Esc}[1;36m") set(BoldWhite "${Esc}[1;37m") endif() libevhtp-1.2.16/cmake/options.cmake000066400000000000000000000014201322723041700171710ustar00rootroot00000000000000# -DEVHTP_DISABLE_SSL:STRING=ON option (EVHTP_DISABLE_SSL "Disable ssl support" OFF) # -DEVHTP_DISABLE_EVTHR:STRING=ON option (EVHTP_DISABLE_EVTHR "Disable evthread support" OFF) # -DEVHTP_DISABLE_REGEX:STRING=ON option (EVHTP_DISABLE_REGEX "Disable regex support" OFF) # -DEVHTP_BUILD_SHARED:STRING=ON option (EVHTP_BUILD_SHARED "Build shared library too" OFF) # -DEVHTP_DEBUG:STRING=ON option (EVHTP_DEBUG "Enable verbose debug logging" OFF) # can be overwritten by new set_alloc functions option (EVHTP_USE_JEMALLOC "Enable jemalloc allocator" OFF) option (EVHTP_USE_TCMALLOC "Enable tcmalloc allocator" OFF) # disable ability to wrap memory functions option (EVHTP_DISABLE_MEMFUNCTIONS "Disable custom allocators" OFF) libevhtp-1.2.16/cmake/packaging.cmake000066400000000000000000000051431322723041700174300ustar00rootroot00000000000000 # ${PROJECT_MAJOR_VERSION}.${PROJECT_MINOR_VERSION}.${PROJECT_PATCH_VERSION} set (CPACK_GENERATOR "TGZ;TZ") set (CPACK_SET_DESTDIR ON) set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Embedded Libevent based HTTP API") set (CPACK_PACKAGE_DESCRIPTION "Very fast HTTP server API, developed as a very flexible and replacement for Libevent's evhttp API. Create a multi-threaded, SSL aware, secure, HTTP server using only a minimal amount of code.") set (CPACK_PACKAGE_NAME "libevhtp") set (CPACK_PACKAGE_CONTACT "nate@cl0d.com") set (CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_MAJOR_VERSION}) set (CPACK_PACKAGE_VERSION_MINOR ${PROJECT_MINOR_VERSION}) set (CPACK_PACKAGE_VERSION_PATCH ${PROJECT_PATCH_VERSION}) if(X86) set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "i386") set (CPACK_RPM_PACKAGE_ARCHITECTURE "i686") elseif(X86_64) set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") set (CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64") elseif(ARM) set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "armhf") set (CPACK_RPM_PACKAGE_ARCHITECTURE "armhf") elseif(AARCH64) set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "arm64") set (CPACK_RPM_PACKAGE_ARCHITECTURE "aarch64") elseif(PPC64LE) set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "ppc64el") set (CPACK_RPM_PACKAGE_ARCHITECTURE "ppc64le") else() set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) set (CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) endif() if(CPACK_GENERATOR STREQUAL "DEB") set (LIBEVHTP_PACKAGE_ARCH_SUFFIX ${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}) elseif (CPACK_GENERATOR STREQUAL "RPM") set (LIBEVHTP_PACKAGE_ARCH_SUFFIX ${CPACK_RPM_PACKAGE_ARCHITECTURE}) else() set (LIBEVHTP_PACKAGE_ARCH_SUFFIX ${CMAKE_SYSTEM_PROCESSOR}) endif() set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}-${LIBEVHTP_PACKAGE_ARCH_SUFFIX}") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}-${LIBEVHTP_PACKAGE_ARCH_SUFFIX}") # RPM STUFF set(CPACK_RPM_COMPONENT_INSTALL TRUE) set(CPACK_RPM_PACKAGE_SUMMARY ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}) set(CPACK_RPM_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION}) set(CPACK_RPM_PACKAGE_URL "http://github.com/criticalstack/libevhtp") set(CPACK_RPM_PACKAGE_LICENSE "BSD") # DEB STUFF set(CPACK_DEB_COMPONENT_INSTALL TRUE) set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") set(CPACK_DEBIAN_PACKAGE_SECTION "universe/libdevel") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/criticalstack/libevhtp") include (CPack) libevhtp-1.2.16/cmake/version.cmake000066400000000000000000000003021322723041700171610ustar00rootroot00000000000000set (PROJECT_MAJOR_VERSION 1) set (PROJECT_MINOR_VERSION 2) set (PROJECT_PATCH_VERSION 16) set (PROJECT_VERSION "${PROJECT_MAJOR_VERSION}.${PROJECT_MINOR_VERSION}.${PROJECT_PATCH_VERSION}") libevhtp-1.2.16/compat/000077500000000000000000000000001322723041700147025ustar00rootroot00000000000000libevhtp-1.2.16/compat/sys/000077500000000000000000000000001322723041700155205ustar00rootroot00000000000000libevhtp-1.2.16/compat/sys/queue.h.in000066400000000000000000000404421322723041700174260ustar00rootroot00000000000000/* $OpenBSD: queue.h,v 1.16 2000/09/07 19:47:59 art Exp $ */ /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues, and circular queues. * * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * A circle queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the list. * A circle queue may be traversed in either direction, but has a more * complex end of list detection. * * For details on the use of these macros, see the queue(3) manual page. */ /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #ifndef _WIN32 #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } #endif /* * Singly-linked List access methods. */ #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_END(head) NULL #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_FOREACH(var, head, field) \ for((var) = SLIST_FIRST(head); \ (var) != SLIST_END(head); \ (var) = SLIST_NEXT(var, field)) /* * Singly-linked List functions. */ #define SLIST_INIT(head) { \ SLIST_FIRST(head) = SLIST_END(head); \ } #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (0) /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List access methods */ #define LIST_FIRST(head) ((head)->lh_first) #define LIST_END(head) NULL #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_FOREACH(var, head, field) \ for((var) = LIST_FIRST(head); \ (var)!= LIST_END(head); \ (var) = LIST_NEXT(var, field)) /* * List functions. */ #define LIST_INIT(head) do { \ LIST_FIRST(head) = LIST_END(head); \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (0) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ } while (0) #define LIST_REPLACE(elm, elm2, field) do { \ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ (elm2)->field.le_next->field.le_prev = \ &(elm2)->field.le_next; \ (elm2)->field.le_prev = (elm)->field.le_prev; \ *(elm2)->field.le_prev = (elm2); \ } while (0) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue access methods. */ #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_END(head) NULL #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) #define SIMPLEQ_FOREACH(var, head, field) \ for((var) = SIMPLEQ_FIRST(head); \ (var) != SIMPLEQ_END(head); \ (var) = SIMPLEQ_NEXT(var, field)) /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (0) #define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \ if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * tail queue access methods */ #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_END(head) NULL #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) /* XXX */ #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_EMPTY(head) \ (TAILQ_FIRST(head) == TAILQ_END(head)) #define TAILQ_FOREACH(var, head, field) \ for((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head); \ (var) = TAILQ_NEXT(var, field)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV(var, headname, field)) /* * Tail queue functions. */ #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ } while (0) #define TAILQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ (elm2)->field.tqe_next->field.tqe_prev = \ &(elm2)->field.tqe_next; \ else \ (head)->tqh_last = &(elm2)->field.tqe_next; \ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ *(elm2)->field.tqe_prev = (elm2); \ } while (0) /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) \ struct name { \ struct type *cqh_first; /* first element */ \ struct type *cqh_last; /* last element */ \ } #define CIRCLEQ_HEAD_INITIALIZER(head) \ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } #define CIRCLEQ_ENTRY(type) \ struct { \ struct type *cqe_next; /* next element */ \ struct type *cqe_prev; /* previous element */ \ } /* * Circular queue access methods */ #define CIRCLEQ_FIRST(head) ((head)->cqh_first) #define CIRCLEQ_LAST(head) ((head)->cqh_last) #define CIRCLEQ_END(head) ((void *)(head)) #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) #define CIRCLEQ_EMPTY(head) \ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) #define CIRCLEQ_FOREACH(var, head, field) \ for((var) = CIRCLEQ_FIRST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_NEXT(var, field)) #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ for((var) = CIRCLEQ_LAST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_PREV(var, field)) /* * Circular queue functions. */ #define CIRCLEQ_INIT(head) do { \ (head)->cqh_first = CIRCLEQ_END(head); \ (head)->cqh_last = CIRCLEQ_END(head); \ } while (0) #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ (listelm)->field.cqe_next = (elm); \ } while (0) #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ (listelm)->field.cqe_prev = (elm); \ } while (0) #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ (elm)->field.cqe_next = (head)->cqh_first; \ (elm)->field.cqe_prev = CIRCLEQ_END(head); \ if ((head)->cqh_last == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (head)->cqh_first->field.cqe_prev = (elm); \ (head)->cqh_first = (elm); \ } while (0) #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.cqe_next = CIRCLEQ_END(head); \ (elm)->field.cqe_prev = (head)->cqh_last; \ if ((head)->cqh_first == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (head)->cqh_last->field.cqe_next = (elm); \ (head)->cqh_last = (elm); \ } while (0) #define CIRCLEQ_REMOVE(head, elm, field) do { \ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm)->field.cqe_prev; \ else \ (elm)->field.cqe_next->field.cqe_prev = \ (elm)->field.cqe_prev; \ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm)->field.cqe_next; \ else \ (elm)->field.cqe_prev->field.cqe_next = \ (elm)->field.cqe_next; \ } while (0) #define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ CIRCLEQ_END(head)) \ (head).cqh_last = (elm2); \ else \ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ CIRCLEQ_END(head)) \ (head).cqh_first = (elm2); \ else \ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ } while (0) #endif /* !_SYS_QUEUE_H_ */ libevhtp-1.2.16/compat/sys/tree.h.in000066400000000000000000002207201322723041700172400ustar00rootroot00000000000000/* * OPENBSD ORIGINAL: sys/sys/tree.h */ /* * $OpenBSD: tree.h,v 1.12 2009/03/02 09:42:55 mikeb Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* * SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (0) /* * Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name ## _SPLAY(struct name *, struct type *); \ void name ## _SPLAY_MINMAX(struct name *, int); \ struct type *name ## _SPLAY_INSERT(struct name *, struct type *); \ struct type *name ## _SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __inline struct type * \ name ## _SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ return (NULL); } \ name ## _SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ return (head->sph_root); } \ return (NULL); \ } \ \ static __inline struct type * \ name ## _SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name ## _SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else{ \ elm = NULL; } \ return (elm); \ } \ \ static __inline struct type * \ name ## _SPLAY_MIN_MAX(struct name *head, int val) \ { \ name ## _SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* * Main splay operation. Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name ## _SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name ## _SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if (__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else{ \ return ((head)->sph_root); } \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name ## _SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) { \ return (NULL); } \ name ## _SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ name ## _SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name ## _SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root))) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) { \ break; } \ if ((cmp)(elm, __tmp) < 0) { \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ break; } \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) { \ break; } \ if ((cmp)(elm, __tmp) > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL) { \ break; } \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name ## _SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) { \ break; } \ if (__comp < 0) { \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ break; } \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) { \ break; } \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL) { \ break; } \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name ## _SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name ## _SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name ## _SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name ## _SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name ## _SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name ## _SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* * Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) do {} while (0) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) { \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); } \ else{ \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); } \ } else{ \ (head)->rbh_root = (tmp); } \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) { \ RB_AUGMENT(RB_PARENT(tmp, field)); } \ } while (0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) { \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); } \ else{ \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); } \ } else{ \ (head)->rbh_root = (tmp); } \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) { \ RB_AUGMENT(RB_PARENT(tmp, field)); } \ } while (0) /* * Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, ) #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ attr void name ## _RB_INSERT_COLOR(struct name *, struct type *); \ attr void name ## _RB_REMOVE_COLOR(struct name *, struct type *, struct type *); \ attr struct type *name ## _RB_REMOVE(struct name *, struct type *); \ attr struct type *name ## _RB_INSERT(struct name *, struct type *); \ attr struct type *name ## _RB_FIND(struct name *, struct type *); \ attr struct type *name ## _RB_NFIND(struct name *, struct type *); \ attr struct type *name ## _RB_NEXT(struct type *); \ attr struct type *name ## _RB_PREV(struct type *); \ attr struct type *name ## _RB_MINMAX(struct name *, int); \ \ /* * Main rb operation. Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, ) #define RB_GENERATE_STATIC(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ attr void \ name ## _RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field); \ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field); \ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field); \ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field); \ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ attr void \ name ## _RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field); \ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) { \ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field))) { \ RB_COLOR(oleft, field) = RB_BLACK; } \ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field); \ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) { \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK; } \ RB_ROTATE_LEFT(head, parent, tmp, field); \ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field); \ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) { \ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field))) { \ RB_COLOR(oright, field) = RB_BLACK; } \ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field); \ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) { \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK; } \ RB_ROTATE_RIGHT(head, parent, tmp, field); \ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) { \ RB_COLOR(elm, field) = RB_BLACK; } \ } \ \ attr struct type * \ name ## _RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) { \ child = RB_RIGHT(elm, field); } \ else if (RB_RIGHT(elm, field) == NULL) { \ child = RB_LEFT(elm, field); } \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field))) { \ elm = left; } \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) { \ RB_PARENT(child, field) = parent; } \ if (parent) { \ if (RB_LEFT(parent, field) == elm) { \ RB_LEFT(parent, field) = child; } \ else{ \ RB_RIGHT(parent, field) = child; } \ RB_AUGMENT(parent); \ } else{ \ RB_ROOT(head) = child; } \ if (RB_PARENT(elm, field) == old) { \ parent = elm; } \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old) { \ RB_LEFT(RB_PARENT(old, field), field) = elm; } \ else{ \ RB_RIGHT(RB_PARENT(old, field), field) = elm; } \ RB_AUGMENT(RB_PARENT(old, field)); \ } else{ \ RB_ROOT(head) = elm; } \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) { \ RB_PARENT(RB_RIGHT(old, field), field) = elm; } \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field))); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) { \ RB_PARENT(child, field) = parent; } \ if (parent) { \ if (RB_LEFT(parent, field) == elm) { \ RB_LEFT(parent, field) = child; } \ else{ \ RB_RIGHT(parent, field) = child; } \ RB_AUGMENT(parent); \ } else{ \ RB_ROOT(head) = child; } \ color: \ if (color == RB_BLACK) { \ name ## _RB_REMOVE_COLOR(head, parent, child); } \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ attr struct type * \ name ## _RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) { \ tmp = RB_LEFT(tmp, field); } \ else if (comp > 0) { \ tmp = RB_RIGHT(tmp, field); } \ else{ \ return (tmp); } \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) { \ RB_LEFT(parent, field) = elm; } \ else{ \ RB_RIGHT(parent, field) = elm; } \ RB_AUGMENT(parent); \ } else{ \ RB_ROOT(head) = elm; } \ name ## _RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ attr struct type * \ name ## _RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ tmp = RB_LEFT(tmp, field); } \ else if (comp > 0) { \ tmp = RB_RIGHT(tmp, field); } \ else{ \ return (tmp); } \ } \ return (NULL); \ } \ \ /* Finds the first node greater than or equal to the search key */ \ attr struct type * \ name ## _RB_NFIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *res = NULL; \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ res = tmp; \ tmp = RB_LEFT(tmp, field); \ } \ else if (comp > 0) { \ tmp = RB_RIGHT(tmp, field); } \ else{ \ return (tmp); } \ } \ return (res); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name ## _RB_NEXT(struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); } \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) { \ elm = RB_PARENT(elm, field); } \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) { \ elm = RB_PARENT(elm, field); } \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name ## _RB_PREV(struct type *elm) \ { \ if (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); \ while (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); } \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) { \ elm = RB_PARENT(elm, field); } \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) { \ elm = RB_PARENT(elm, field); } \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name ## _RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) { \ tmp = RB_LEFT(tmp, field); } \ else{ \ tmp = RB_RIGHT(tmp, field); } \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name ## _RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name ## _RB_REMOVE(x, y) #define RB_FIND(name, x, y) name ## _RB_FIND(x, y) #define RB_NFIND(name, x, y) name ## _RB_NFIND(x, y) #define RB_NEXT(name, x, y) name ## _RB_NEXT(y) #define RB_PREV(name, x, y) name ## _RB_PREV(y) #define RB_MIN(name, x) name ## _RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name ## _RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name ## _RB_NEXT(x)) #define RB_FOREACH_REVERSE(x, name, head) \ for ((x) = RB_MAX(name, head); \ (x) != NULL; \ (x) = name ## _RB_PREV(x)) #endif /* _SYS_TREE_H_ */ libevhtp-1.2.16/evhtp.c000066400000000000000000004162441322723041700147240ustar00rootroot00000000000000/** * @file evhtp.c * * @brief implementation file for libevhtp. */ #include #include #include #include #include #include #include #ifndef WIN32 #include #include #include #include #else #define WINVER 0x0501 #include #include #endif #ifndef NO_SYS_UN #include #endif #include #include #include "internal.h" #include "numtoa.h" #include "evhtp/evhtp.h" #include "log.h" /** * @brief structure containing a single callback and configuration * * The definition structure which is used within the evhtp_callbacks_t * structure. This holds information about what should execute for either * a single or regex path. * * For example, if you registered a callback to be executed on a request * for "/herp/derp", your defined callback will be executed. * * Optionally you can set callback-specific hooks just like per-connection * hooks using the same rules. * */ struct evhtp_callback_s { evhtp_callback_type type; /**< the type of callback (regex|path) */ evhtp_callback_cb cb; /**< the actual callback function */ void * cbarg; /**< user-defind arguments passed to the cb */ evhtp_hooks_t * hooks; /**< per-callback hooks */ size_t len; union { char * path; char * glob; #ifndef EVHTP_DISABLE_REGEX regex_t * regex; #endif } val; TAILQ_ENTRY(evhtp_callback_s) next; }; TAILQ_HEAD(evhtp_callbacks_s, evhtp_callback_s); #define SET_BIT(VAR, FLAG) VAR |= FLAG #define UNSET_BIT(VAR, FLAG) VAR &= ~FLAG #define HTP_FLAG_ON(PRE, FLAG) SET_BIT(PRE->flags, FLAG) #define HTP_FLAG_OFF(PRE, FLAG) UNSET_BIT(PRE->flags, FLAG) #define HOOK_AVAIL(var, hook_name) (var->hooks && var->hooks->hook_name) #define HOOK_FUNC(var, hook_name) (var->hooks->hook_name) #define HOOK_ARGS(var, hook_name) var->hooks->hook_name ## _arg #define HOOK_REQUEST_RUN(request, hook_name, ...) do { \ if (HOOK_AVAIL(request, hook_name)) \ { \ return HOOK_FUNC(request, hook_name) (request, __VA_ARGS__, \ HOOK_ARGS(request, hook_name)); \ } \ \ if (request->conn && HOOK_AVAIL(request->conn, hook_name)) \ { \ return HOOK_FUNC(request->conn, hook_name) (request, __VA_ARGS__, \ HOOK_ARGS(request->conn, hook_name)); \ } \ } while (0) #define HOOK_REQUEST_RUN_NARGS(__request, hook_name) do { \ if (HOOK_AVAIL(__request, hook_name)) \ { \ return HOOK_FUNC(__request, hook_name) (__request, \ HOOK_ARGS(__request, hook_name)); \ } \ \ if (__request->conn && HOOK_AVAIL(__request->conn, hook_name)) \ { \ return HOOK_FUNC(__request->conn, hook_name) (request, \ HOOK_ARGS(__request->conn, hook_name)); \ } \ } while (0); #ifndef EVHTP_DISABLE_EVTHR /** * @brief Helper macro to lock htp structure * * @param h htp structure */ #define htp__lock_(h) do { \ if (h->lock) \ { \ pthread_mutex_lock(h->lock); \ } \ } while (0) /** * @brief Helper macro to unlock htp lock * * @param h htp structure */ #define htp__unlock_(h) do { \ if (h->lock) \ { \ pthread_mutex_unlock(h->lock); \ } \ } while (0) #else #define htp__lock_(h) do { \ } while (0) #define htp__unlock_(h) do { \ } while (0) #endif #ifndef TAILQ_FOREACH_SAFE #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = TAILQ_FIRST((head)); \ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ (var) = (tvar)) #endif /* rc == request->conn. Just little things to make life easier */ #define rc_scratch conn->scratch_buf #define rc_parser conn->parser /* ch_ == conn->hooks->on_... */ #define ch_fini_arg hooks->on_connection_fini_arg #define ch_fini hooks->on_connection_fini /* cr_ == conn->request */ #define cr_status request->status /* rh_ == request->hooks->on_ */ #define rh_err hooks->on_error #define rh_err_arg hooks->on_error_arg #ifndef EVHTP_DISABLE_MEMFUNCTIONS static void * (*malloc_)(size_t sz) = malloc; static void * (* realloc_)(void * d, size_t sz) = realloc; static void (* free_)(void * d) = free; /** * @brief Wrapper for malloc so that a different malloc can be used * if desired. * * @see evhtp_set_mem_functions * * @param size size_t of memory to be allocated * * @return void * to malloc'd memory or NULL if fail */ static void * htp__malloc_(size_t size) { return malloc_(size); } /** * @brief Wrapper for realloc so that a different realloc can be used * if desired. * * @see evhtp_set_mem_functions * * @param ptr current memory ptr * @param size size_t of memory to be allocated * * @return void * to newly realloc'd memory or NULL if fail */ static void * htp__realloc_(void * ptr, size_t size) { return realloc_(ptr, size); } /** * @brief Wrapper for free so that a different free can be used * if desired. * * @see evhtp_set_mem_functions * * @param ptr pointer to memory to be freed. * */ static void htp__free_(void * ptr) { return free_(ptr); } /** * @brief Wrapper for calloc so that a different calloc can be used * if desired. * * @see evhtp_set_mem_functions * * @param nmemb number of members (as a size_t) * @param size size of member blocks (as a size_t) * * @return void * to new memory block */ static void * htp__calloc_(size_t nmemb, size_t size) { if (malloc_ != malloc) { size_t len = nmemb * size; void * p; if ((p = malloc_(len)) == NULL) { return NULL; } memset(p, 0, len); return p; } return calloc(nmemb, size); } /** * @brief implementation of strdup function. * * @param str - null terminated string. * * @return duplicate of string or NULL if fail * */ static char * htp__strdup_(const char * str) { if (malloc_ != malloc) { size_t len; void * p; len = strlen(str); if ((p = malloc_(len + 1)) == NULL) { return NULL; } memcpy(p, str, len + 1); return p; } return strdup(str); } /** * @brief implementation of strndup function. * * @param str - null terminated string. * @param len - size_t length off string * * @return duplicate of string or NULL if fail * */ static char * htp__strndup_(const char * str, size_t len) { if (malloc_ != malloc) { char * p; if ((p = malloc_(len + 1)) != NULL) { memcpy(p, str, len + 1); } else { return NULL; } p[len] = '\0'; return p; } return strndup(str, len); } #else #define htp__malloc_(sz) malloc(sz) #define htp__calloc_(n, sz) calloc(n, sz) #define htp__strdup_(s) strdup(s) #define htp__strndup_(n, sz) strndup(n, sz) #define htp__realloc_(p, sz) realloc(p, sz) #define htp__free_(p) free(p) #endif void evhtp_set_mem_functions(void *(*mallocfn_)(size_t len), void *(*reallocfn_)(void * p, size_t sz), void (*freefn_)(void * p)) { #ifndef EVHTP_DISABLE_MEMFUNCTIONS malloc_ = mallocfn_; realloc_ = reallocfn_; free_ = freefn_; return event_set_mem_functions(malloc_, realloc_, free_); #endif } /** * @brief returns string status code from enum code * * @param code as evhtp_res enum * * @return string corresponding to code, else UNKNOWN */ static const char * status_code_to_str(evhtp_res code) { switch (code) { case EVHTP_RES_200: return "OK"; case EVHTP_RES_300: return "Redirect"; case EVHTP_RES_400: return "Bad Request"; case EVHTP_RES_NOTFOUND: return "Not Found"; case EVHTP_RES_SERVERR: return "Internal Server Error"; case EVHTP_RES_CONTINUE: return "Continue"; case EVHTP_RES_FORBIDDEN: return "Forbidden"; case EVHTP_RES_SWITCH_PROTO: return "Switching Protocols"; case EVHTP_RES_MOVEDPERM: return "Moved Permanently"; case EVHTP_RES_PROCESSING: return "Processing"; case EVHTP_RES_URI_TOOLONG: return "URI Too Long"; case EVHTP_RES_CREATED: return "Created"; case EVHTP_RES_ACCEPTED: return "Accepted"; case EVHTP_RES_NAUTHINFO: return "No Auth Info"; case EVHTP_RES_NOCONTENT: return "No Content"; case EVHTP_RES_RSTCONTENT: return "Reset Content"; case EVHTP_RES_PARTIAL: return "Partial Content"; case EVHTP_RES_MSTATUS: return "Multi-Status"; case EVHTP_RES_IMUSED: return "IM Used"; case EVHTP_RES_FOUND: return "Found"; case EVHTP_RES_SEEOTHER: return "See Other"; case EVHTP_RES_NOTMOD: return "Not Modified"; case EVHTP_RES_USEPROXY: return "Use Proxy"; case EVHTP_RES_SWITCHPROXY: return "Switch Proxy"; case EVHTP_RES_TMPREDIR: return "Temporary Redirect"; case EVHTP_RES_UNAUTH: return "Unauthorized"; case EVHTP_RES_PAYREQ: return "Payment Required"; case EVHTP_RES_METHNALLOWED: return "Not Allowed"; case EVHTP_RES_NACCEPTABLE: return "Not Acceptable"; case EVHTP_RES_PROXYAUTHREQ: return "Proxy Authentication Required"; case EVHTP_RES_TIMEOUT: return "Request Timeout"; case EVHTP_RES_CONFLICT: return "Conflict"; case EVHTP_RES_GONE: return "Gone"; case EVHTP_RES_LENREQ: return "Length Required"; case EVHTP_RES_PRECONDFAIL: return "Precondition Failed"; case EVHTP_RES_ENTOOLARGE: return "Entity Too Large"; case EVHTP_RES_URITOOLARGE: return "Request-URI Too Long"; case EVHTP_RES_UNSUPPORTED: return "Unsupported Media Type"; case EVHTP_RES_RANGENOTSC: return "Requested Range Not Satisfiable"; case EVHTP_RES_EXPECTFAIL: return "Expectation Failed"; case EVHTP_RES_IAMATEAPOT: return "I'm a teapot"; case EVHTP_RES_NOTIMPL: return "Not Implemented"; case EVHTP_RES_BADGATEWAY: return "Bad Gateway"; case EVHTP_RES_SERVUNAVAIL: return "Service Unavailable"; case EVHTP_RES_GWTIMEOUT: return "Gateway Timeout"; case EVHTP_RES_VERNSUPPORT: return "HTTP Version Not Supported"; case EVHTP_RES_BWEXEED: return "Bandwidth Limit Exceeded"; } /* switch */ return "UNKNOWN"; } /* status_code_to_str */ #ifndef EVHTP_DISABLE_SSL static int session_id_context = 1; #ifndef EVHTP_DISABLE_EVTHR static int ssl_num_locks; static evhtp_mutex_t * ssl_locks; static int ssl_locks_initialized = 0; #endif #endif /* * COMPAT FUNCTIONS */ #ifdef NO_STRNLEN /** * @brief Implementation of strnlen function if none exists. * * @param s - null terminated character string * @param maxlen - maximum length of string * * @return length of string * */ static size_t strnlen(const char * s, size_t maxlen) { const char * e; size_t n; for (e = s, n = 0; *e && n < maxlen; e++, n++) { ; } return n; } #endif #ifdef NO_STRNDUP /** * @brief Implementation of strndup if none exists. * * @param s - const char * to null terminated string * @param n - size_t maximum legnth of string * * @return length limited string duplicate or NULL if fail * */ static char * strndup(const char * s, size_t n) { size_t len = strnlen(s, n); char * ret; if (len < n) { return htp__strdup_(s); } if ((ret = htp__malloc_(n + 1)) == NULL) { return NULL; } ret[n] = '\0'; memcpy(ret, s, n); return ret; } #endif /* * PRIVATE FUNCTIONS */ /** * * @brief helper macro to determine if http version is HTTP/1.0 * * @param major the major version number * @param minor the minor version number * * @return 1 if HTTP/1.0, else 0 */ #define htp__is_http_11_(_major, _minor) \ (_major >= 1 && _minor >= 1) /** * @brief helper function to determine if http version is HTTP/1.1 * * @param major the major version number * @param minor the minor version number * * @return 1 if HTTP/1.1, else 0 */ #define htp__is_http_10_(_major, _minor) \ (_major >= 1 && _minor <= 0) /** * @brief returns the HTTP protocol version * * @param major the major version number * @param minor the minor version number * * @return EVHTP_PROTO_10 if HTTP/1.0, EVHTP_PROTO_11 if HTTP/1.1, otherwise * EVHTP_PROTO_INVALID */ static inline evhtp_proto htp__protocol_(const char major, const char minor) { if (htp__is_http_10_(major, minor)) { return EVHTP_PROTO_10; } if (htp__is_http_11_(major, minor)) { return EVHTP_PROTO_11; } return EVHTP_PROTO_INVALID; } /** * @brief runs the user-defined on_path hook for a request * * @param request the request structure * @param path the path structure * * @return EVHTP_RES_OK on success, otherwise something else. */ static inline evhtp_res htp__hook_path_(evhtp_request_t * request, evhtp_path_t * path) { HOOK_REQUEST_RUN(request, on_path, path); return EVHTP_RES_OK; } /** * @brief runs the user-defined on_header hook for a request * * once a full key: value header has been parsed, this will call the hook * * @param request the request strucutre * @param header the header structure * * @return EVHTP_RES_OK on success, otherwise something else. */ static inline evhtp_res htp__hook_header_(evhtp_request_t * request, evhtp_header_t * header) { HOOK_REQUEST_RUN(request, on_header, header); return EVHTP_RES_OK; } /** * @brief runs the user-defined on_Headers hook for a request after all headers * have been parsed. * * @param request the request structure * @param headers the headers tailq structure * * @return EVHTP_RES_OK on success, otherwise something else. */ static inline evhtp_res htp__hook_headers_(evhtp_request_t * request, evhtp_headers_t * headers) { HOOK_REQUEST_RUN(request, on_headers, headers); return EVHTP_RES_OK; } /** * @brief runs the user-defined on_body hook for requests containing a body. * the data is stored in the request->buffer_in so the user may either * leave it, or drain upon being called. * * @param request the request strucutre * @param buf a evbuffer containing body data * * @return EVHTP_RES_OK on success, otherwise something else. */ static inline evhtp_res htp__hook_body_(evhtp_request_t * request, struct evbuffer * buf) { if (request == NULL) { return 500; } HOOK_REQUEST_RUN(request, on_read, buf); return EVHTP_RES_OK; } /** * @brief runs the user-defined hook called just prior to a request been * free()'d * * @param request therequest structure * * @return EVHTP_RES_OK on success, otherwise treated as an error */ static inline evhtp_res htp__hook_request_fini_(evhtp_request_t * request) { if (request == NULL) { return 500; } HOOK_REQUEST_RUN_NARGS(request, on_request_fini); return EVHTP_RES_OK; } /** * @brief Runs the user defined request hook * * @param request * @param len * @return */ static inline evhtp_res htp__hook_chunk_new_(evhtp_request_t * request, uint64_t len) { HOOK_REQUEST_RUN(request, on_new_chunk, len); return EVHTP_RES_OK; } /** * @brief Runs the user defined on_chunk_fini hook * * @param request * @return */ static inline evhtp_res htp__hook_chunk_fini_(evhtp_request_t * request) { HOOK_REQUEST_RUN_NARGS(request, on_chunk_fini); return EVHTP_RES_OK; } /** * @brief Runs the user defined on chunk_finis hook * * @param request * @return */ static inline evhtp_res htp__hook_chunks_fini_(evhtp_request_t * request) { HOOK_REQUEST_RUN_NARGS(request, on_chunks_fini); return EVHTP_RES_OK; } /** * @brief Runs the user defined on_headers_start hook * * @param request * @return */ static inline evhtp_res htp__hook_headers_start_(evhtp_request_t * request) { HOOK_REQUEST_RUN_NARGS(request, on_headers_start); return EVHTP_RES_OK; } /** * @brief runs the user-definedhook called just prior to a connection being * closed * * @param connection the connection structure * * @return EVHTP_RES_OK on success, but pretty much ignored in any case. */ static inline evhtp_res htp__hook_connection_fini_(evhtp_connection_t * connection) { if (evhtp_unlikely(connection == NULL)) { return 500; } if (connection->hooks != NULL && connection->ch_fini != NULL) { return (connection->ch_fini)(connection, connection->ch_fini_arg); } return EVHTP_RES_OK; } /** * @brief runs the user-defined hook when a connection error occurs * * @param request the request structure * @param errtype the error that ocurred */ static inline void htp__hook_error_(evhtp_request_t * request, evhtp_error_flags errtype) { if (request && request->hooks && request->rh_err) { (*request->rh_err)(request, errtype, request->rh_err_arg); } } /** * @brief runs the user-defined hook when a connection error occurs * * @param connection the connection structure * @param errtype the error that ocurred */ static inline evhtp_res htp__hook_connection_error_(evhtp_connection_t * connection, evhtp_error_flags errtype) { if (connection == NULL) { return EVHTP_RES_FATAL; } if (connection->request != NULL) { htp__hook_error_(connection->request, errtype); } return EVHTP_RES_OK; } /** * @brief Runs the user defined hostname processing hook * * @param r * @param hostname * @return */ static inline evhtp_res htp__hook_hostname_(evhtp_request_t * r, const char * hostname) { HOOK_REQUEST_RUN(r, on_hostname, hostname); return EVHTP_RES_OK; } /** * @brief Runs the user defined on_write hook * * @param connection * @return */ static inline evhtp_res htp__hook_connection_write_(evhtp_connection_t * connection) { if (connection->hooks && connection->hooks->on_write) { return (connection->hooks->on_write)(connection, connection->hooks->on_write_arg); } return EVHTP_RES_OK; } /** * @brief glob/wildcard type pattern matching. * * Note: This code was derived from redis's (v2.6) stringmatchlen() function. * * @param pattern * @param string * * @return */ static int htp__glob_match_(const char * pattern, size_t plen, const char * string, size_t str_len) { while (plen) { switch (pattern[0]) { case '*': while (pattern[1] == '*') { pattern++; plen--; } if (plen == 1) { return 1; /* match */ } while (str_len) { if (htp__glob_match_(pattern + 1, plen - 1, string, str_len)) { return 1; /* match */ } string++; str_len--; } return 0; /* no match */ default: if (pattern[0] != string[0]) { return 0; /* no match */ } string++; str_len--; break; } /* switch */ pattern++; plen--; if (str_len == 0) { while (*pattern == '*') { pattern++; plen--; } break; } } if (plen == 0 && str_len == 0) { return 1; } return 0; } /* htp__glob_match_ */ /** * @brief Locates a given callback offsets performs a regex pattern match * * @param [IN] cbs ptr to evhtp_callbacks_t structure * @param [IN] path * @param [OUT] start_offset * @param [OUT] end_offset * @return */ static evhtp_callback_t * htp__callback_find_(evhtp_callbacks_t * cbs, const char * path, unsigned int * start_offset, unsigned int * end_offset) { size_t path_len; #ifndef EVHTP_DISABLE_REGEX regmatch_t pmatch[28]; #endif evhtp_callback_t * callback; if (evhtp_unlikely(cbs == NULL)) { return NULL; } path_len = strlen(path); TAILQ_FOREACH(callback, cbs, next) { switch (callback->type) { case evhtp_callback_type_hash: if (strncmp(path, callback->val.path, callback->len) == 0) { *start_offset = 0; *end_offset = path_len; return callback; } break; #ifndef EVHTP_DISABLE_REGEX case evhtp_callback_type_regex: if (regexec(callback->val.regex, path, callback->val.regex->re_nsub + 1, pmatch, 0) == 0) { *start_offset = pmatch[callback->val.regex->re_nsub].rm_so; *end_offset = pmatch[callback->val.regex->re_nsub].rm_eo; return callback; } break; #endif case evhtp_callback_type_glob: { size_t glob_len = strlen(callback->val.glob); if (htp__glob_match_(callback->val.glob, glob_len, path, path_len) == 1) { *start_offset = 0; *end_offset = path_len; return callback; } } default: break; } /* switch */ } return NULL; } /* htp__callback_find_ */ /** * @brief parses the path and file from an input buffer * * @details in order to properly create a structure that can match * both a path and a file, this will parse a string into * what it considers a path, and a file. * * @details if for example the input was "/a/b/c", the parser will * consider "/a/b/" as the path, and "c" as the file. * * @param the unallocated destination buffer. * @param data raw input data (assumes a /path/[file] structure) * @param len length of the input data * * @return 0 on success, -1 on error. */ static int htp__path_new_(evhtp_path_t ** out, const char * data, size_t len) { evhtp_path_t * req_path; const char * data_end = (const char *)(data + len); char * path = NULL; char * file = NULL; req_path = htp__calloc_(1, sizeof(*req_path)); evhtp_alloc_assert(req_path); *out = NULL; if (evhtp_unlikely(len == 0)) { /* * odd situation here, no preceding "/", so just assume the path is "/" */ path = htp__strdup_("/"); evhtp_alloc_assert(path); } else if (*data != '/') { /* request like GET stupid HTTP/1.0, treat stupid as the file, and * assume the path is "/" */ path = htp__strdup_("/"); evhtp_alloc_assert(path); file = htp__strndup_(data, len); evhtp_alloc_assert(file); } else { if (data[len - 1] != '/') { /* * the last character in data is assumed to be a file, not the end of path * loop through the input data backwards until we find a "/" */ size_t i; for (i = (len - 1); i != 0; i--) { if (data[i] == '/') { /* * we have found a "/" representing the start of the file, * and the end of the path */ size_t path_len; size_t file_len; path_len = (size_t)(&data[i] - data) + 1; file_len = (size_t)(data_end - &data[i + 1]); /* check for overflow */ if ((const char *)(data + path_len) > data_end) { evhtp_safe_free(req_path, htp__free_); return -1; } /* check for overflow */ if ((const char *)(&data[i + 1] + file_len) > data_end) { evhtp_safe_free(req_path, htp__free_); return -1; } path = htp__strndup_(data, path_len); evhtp_alloc_assert(path); file = htp__strndup_(&data[i + 1], file_len); evhtp_alloc_assert(file); break; } } if (i == 0 && data[i] == '/' && !file && !path) { /* drops here if the request is something like GET /foo */ path = htp__strdup_("/"); evhtp_alloc_assert(path); if (len > 1) { file = htp__strndup_((const char *)(data + 1), len); evhtp_alloc_assert(file); } } } else { /* the last character is a "/", thus the request is just a path */ path = htp__strndup_(data, len); evhtp_alloc_assert(path); } } if (len != 0) { req_path->full = htp__strndup_(data, len); } else { req_path->full = htp__strdup_("/"); } evhtp_alloc_assert(req_path->full); req_path->path = path; req_path->file = file; *out = req_path; return 0; } /* htp__path_new_ */ /** * @brief Correctly frees the evhtp_path_t ptr that is passed in. * @param path */ static void htp__path_free_(evhtp_path_t * path) { if (evhtp_unlikely(path == NULL)) { return; } evhtp_safe_free(path->full, htp__free_); evhtp_safe_free(path->path, htp__free_); evhtp_safe_free(path->file, htp__free_); evhtp_safe_free(path->match_start, htp__free_); evhtp_safe_free(path->match_end, htp__free_); evhtp_safe_free(path, htp__free_); } /** * @brief create an authority structure * * @return 0 on success, -1 on error */ static int htp__authority_new_(evhtp_authority_t ** out) { evhtp_authority_t * authority; if (evhtp_unlikely(out == NULL)) { return -1; } *out = htp__calloc_(1, sizeof(*authority)); return (*out != NULL) ? 0 : -1; } /** * @brief frees an authority structure * * @param authority evhtp_authority_t */ static void htp__authority_free_(evhtp_authority_t * authority) { if (authority == NULL) { return; } evhtp_safe_free(authority->username, htp__free_); evhtp_safe_free(authority->password, htp__free_); evhtp_safe_free(authority->hostname, htp__free_); evhtp_safe_free(authority, htp__free_); } /** * @brief frees an overlay URI structure * * @param uri evhtp_uri_t */ static void htp__uri_free_(evhtp_uri_t * uri) { if (evhtp_unlikely(uri == NULL)) { return; } evhtp_safe_free(uri->query, evhtp_query_free); evhtp_safe_free(uri->path, htp__path_free_); evhtp_safe_free(uri->authority, htp__authority_free_); evhtp_safe_free(uri->fragment, htp__free_); evhtp_safe_free(uri->query_raw, htp__free_); evhtp_safe_free(uri, htp__free_); } /** * @brief create an overlay URI structure * * @return 0 on success, -1 on error. */ static int htp__uri_new_(evhtp_uri_t ** out) { evhtp_uri_t * uri; *out = NULL; if ((uri = htp__calloc_(1, sizeof(*uri))) == NULL) { return -1; } uri->authority = NULL; if (htp__authority_new_(&uri->authority) == -1) { evhtp_safe_free(uri, htp__uri_free_); return -1; } *out = uri; return 0; } /** * @brief frees all data in an evhtp_request_t along with calling finished hooks * * @param request the request structure */ static void htp__request_free_(evhtp_request_t * request) { if (evhtp_unlikely(request == NULL)) { return; } htp__hook_request_fini_(request); evhtp_safe_free(request->uri, htp__uri_free_); evhtp_safe_free(request->headers_in, evhtp_kvs_free); evhtp_safe_free(request->headers_out, evhtp_kvs_free); if (request->conn && request->conn->request == request) { request->conn->request = NULL; } if (request->buffer_in != NULL) { evhtp_safe_free(request->buffer_in, evbuffer_free); } if (request->buffer_out != NULL) { evhtp_safe_free(request->buffer_out, evbuffer_free); } evhtp_safe_free(request->hooks, htp__free_); evhtp_safe_free(request, htp__free_); } /** * @brief Creates a new evhtp_request_t * * @param c * * @return evhtp_request_t structure on success, otherwise NULL */ static evhtp_request_t * htp__request_new_(evhtp_connection_t * c) { evhtp_request_t * req; uint8_t error; if (evhtp_unlikely(!(req = htp__calloc_(sizeof(*req), 1)))) { return NULL; } error = 1; req->conn = c; req->htp = c ? c->htp : NULL; req->status = EVHTP_RES_OK; do { if (evhtp_unlikely(!(req->buffer_in = evbuffer_new()))) { break; } if (evhtp_unlikely(!(req->buffer_out = evbuffer_new()))) { break; } if (evhtp_unlikely(!(req->headers_in = htp__malloc_(sizeof(evhtp_headers_t))))) { break; } if (evhtp_unlikely(!(req->headers_out = htp__malloc_(sizeof(evhtp_headers_t))))) { break; } TAILQ_INIT(req->headers_in); TAILQ_INIT(req->headers_out); error = 0; } while (0); if (error == 0) { return req; } evhtp_safe_free(req, htp__request_free_); return req; } /* htp__request_new_ */ /** * @brief Starts the parser for the connection associated with the parser struct * * @param p * @return 0 on success, -1 on fail */ static int htp__request_parse_start_(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); if (evhtp_unlikely(c->type == evhtp_type_client)) { return 0; } if (c->flags & EVHTP_CONN_FLAG_PAUSED) { return -1; } if (c->request) { if (c->request->flags & EVHTP_REQ_FLAG_FINISHED) { htp__request_free_(c->request); } else { return -1; } } if (((c->request = htp__request_new_(c))) == NULL) { return -1; } return 0; } /** * @brief parses http request arguments * * @see htparser_get_userdata * * @param p * @param data * @param len * @return 0 on success, -1 on failure (sets connection cr_status as well) */ static int htp__request_parse_args_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); evhtp_uri_t * uri = c->request->uri; const char * fragment; int ignore_fragment; if (c->type == evhtp_type_client) { /* as a client, technically we should never get here, but just in case * we return a 0 to the parser to continue. */ return 0; } /* if the parser flags has the IGNORE_FRAGMENTS bit set, skip * the fragment parsing */ ignore_fragment = (c->htp->parser_flags & EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS); if (!ignore_fragment && (fragment = memchr(data, '#', len))) { /* Separate fragment from query according to RFC 3986. * * XXX: not happy about using strchr stuff, maybe this functionality * is more apt as part of evhtp_parse_query() */ ptrdiff_t frag_offset; frag_offset = fragment - data; if (frag_offset < len) { size_t fraglen; /* Skip '#'. */ fragment += 1; frag_offset += 1; fraglen = len - frag_offset; uri->fragment = htp__malloc_(fraglen + 1); evhtp_alloc_assert(uri->fragment); memcpy(uri->fragment, fragment, fraglen); uri->fragment[fraglen] = '\0'; len -= fraglen + 1; /* Skip '#' + fragment string. */ } } uri->query = evhtp_parse_query_wflags(data, len, c->htp->parser_flags); if (evhtp_unlikely(!uri->query)) { c->cr_status = EVHTP_RES_ERROR; return -1; } uri->query_raw = htp__malloc_(len + 1); evhtp_alloc_assert(uri->query_raw); memcpy(uri->query_raw, data, len); uri->query_raw[len] = '\0'; return 0; } /* htp__request_parse_args_ */ static int htp__request_parse_headers_start_(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); if ((c->cr_status = htp__hook_headers_start_(c->request)) != EVHTP_RES_OK) { return -1; } return 0; } static int htp__request_parse_header_key_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); char * key_s; evhtp_header_t * hdr; key_s = htp__malloc_(len + 1); evhtp_alloc_assert(key_s); key_s[len] = '\0'; memcpy(key_s, data, len); if ((hdr = evhtp_header_key_add(c->request->headers_in, key_s, 0)) == NULL) { c->cr_status = EVHTP_RES_FATAL; return -1; } hdr->k_heaped = 1; return 0; } static int htp__request_parse_header_val_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); char * val_s; evhtp_header_t * header; val_s = htp__malloc_(len + 1); evhtp_alloc_assert(val_s); val_s[len] = '\0'; memcpy(val_s, data, len); if ((header = evhtp_header_val_add(c->request->headers_in, val_s, 0)) == NULL) { evhtp_safe_free(val_s, htp__free_); c->cr_status = EVHTP_RES_FATAL; return -1; } header->v_heaped = 1; if ((c->cr_status = htp__hook_header_(c->request, header)) != EVHTP_RES_OK) { return -1; } return 0; } static inline evhtp_t * htp__request_find_vhost_(evhtp_t * evhtp, const char * name) { evhtp_t * evhtp_vhost; evhtp_alias_t * evhtp_alias; TAILQ_FOREACH(evhtp_vhost, &evhtp->vhosts, next_vhost) { if (evhtp_unlikely(evhtp_vhost->server_name == NULL)) { continue; } if (htp__glob_match_(evhtp_vhost->server_name, strlen(evhtp_vhost->server_name), name, strlen(name)) == 1) { return evhtp_vhost; } TAILQ_FOREACH(evhtp_alias, &evhtp_vhost->aliases, next) { if (evhtp_alias->alias == NULL) { continue; } if (htp__glob_match_(evhtp_alias->alias, strlen(evhtp_alias->alias), name, strlen(name)) == 1) { return evhtp_vhost; } } } return NULL; } static inline int htp__request_set_callbacks_(evhtp_request_t * request) { evhtp_t * evhtp; evhtp_connection_t * conn; evhtp_uri_t * uri; evhtp_path_t * path; evhtp_hooks_t * hooks; evhtp_callback_t * callback; evhtp_callback_cb cb; void * cbarg; if (request == NULL) { return -1; } if ((evhtp = request->htp) == NULL) { return -1; } if ((conn = request->conn) == NULL) { return -1; } if ((uri = request->uri) == NULL) { return -1; } if ((path = uri->path) == NULL) { return -1; } hooks = NULL; callback = NULL; cb = NULL; cbarg = NULL; if ((callback = htp__callback_find_(evhtp->callbacks, path->full, &path->matched_soff, &path->matched_eoff))) { /* matched a callback using both path and file (/a/b/c/d) */ cb = callback->cb; cbarg = callback->cbarg; hooks = callback->hooks; } else if ((callback = htp__callback_find_(evhtp->callbacks, path->path, &path->matched_soff, &path->matched_eoff))) { /* matched a callback using *just* the path (/a/b/c/) */ cb = callback->cb; cbarg = callback->cbarg; hooks = callback->hooks; } else { /* no callbacks found for either case, use defaults */ cb = evhtp->defaults.cb; cbarg = evhtp->defaults.cbarg; path->matched_soff = 0; path->matched_eoff = (unsigned int)strlen(path->full); } if (path->match_start == NULL) { path->match_start = htp__calloc_(strlen(path->full) + 1, 1); evhtp_alloc_assert(path->match_start); } if (path->match_end == NULL) { path->match_end = htp__calloc_(strlen(path->full) + 1, 1); evhtp_alloc_assert(path->match_end); } if (path->matched_soff != UINT_MAX /*ONIG_REGION_NOTPOS*/) { if (path->matched_eoff - path->matched_soff) { memcpy(path->match_start, (void *)(path->full + path->matched_soff), path->matched_eoff - path->matched_soff); } else { memcpy(path->match_start, (void *)(path->full + path->matched_soff), strlen((const char *)(path->full + path->matched_soff))); } memcpy(path->match_end, (void *)(path->full + path->matched_eoff), strlen(path->full) - path->matched_eoff); } if (hooks != NULL) { if (request->hooks == NULL) { request->hooks = htp__malloc_(sizeof(evhtp_hooks_t)); evhtp_alloc_assert(request->hooks); } memcpy(request->hooks, hooks, sizeof(evhtp_hooks_t)); } request->cb = cb; request->cbarg = cbarg; return 0; } /* htp__request_set_callbacks_ */ static int htp__request_parse_hostname_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); evhtp_t * evhtp; evhtp_t * evhtp_vhost; #ifndef EVHTP_DISABLE_SSL if ((c->flags & EVHTP_CONN_FLAG_VHOST_VIA_SNI) && c->ssl != NULL) { /* use the SNI set hostname instead of the header hostname */ const char * host; host = SSL_get_servername(c->ssl, TLSEXT_NAMETYPE_host_name); if ((c->cr_status = htp__hook_hostname_(c->request, host)) != EVHTP_RES_OK) { return -1; } return 0; } #endif evhtp = c->htp; /* since this is called after htp__request_parse_path_(), which already * setup callbacks for the URI, we must now attempt to find callbacks which * are specific to this host. */ htp__lock_(evhtp); { if ((evhtp_vhost = htp__request_find_vhost_(evhtp, data))) { htp__lock_(evhtp_vhost); { /* if we found a match for the host, we must set the htp * variables for both the connection and the request. */ c->htp = evhtp_vhost; c->request->htp = evhtp_vhost; htp__request_set_callbacks_(c->request); } htp__unlock_(evhtp_vhost); } } htp__unlock_(evhtp); if ((c->cr_status = htp__hook_hostname_(c->request, data)) != EVHTP_RES_OK) { return -1; } return 0; } /* htp__request_parse_hostname_ */ static int htp__require_uri_(evhtp_connection_t * c) { if (c != NULL && c->request != NULL) { if (c->request->uri == NULL) { return htp__uri_new_(&c->request->uri); } return 0; } return -1; } static int htp__request_parse_host_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c; evhtp_authority_t * authority; if (evhtp_unlikely(p == NULL)) { return -1; } c = htparser_get_userdata(p); /* all null checks are done in require_uri_, * no need to check twice */ if (htp__require_uri_(c) == -1) { return -1; } authority = c->request->uri->authority; authority->hostname = htp__malloc_(len + 1); evhtp_alloc_assert(authority->hostname); if (authority->hostname == NULL) { c->cr_status = EVHTP_RES_FATAL; return -1; } memcpy(authority->hostname, data, len); authority->hostname[len] = '\0'; return 0; } static int htp__request_parse_port_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); evhtp_authority_t * authority; char * endptr; unsigned long port; if (htp__require_uri_(c) == -1) { return -1; } authority = c->request->uri->authority; port = strtoul(data, &endptr, 10); if (endptr - data != len || port > 65535) { c->cr_status = EVHTP_RES_FATAL; return -1; } authority->port = port; return 0; } static int htp__request_parse_path_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); evhtp_path_t * path; if (evhtp_unlikely(p == NULL || c == NULL)) { return -1; } if (htp__require_uri_(c) == -1) { return -1; } if (htp__path_new_(&path, data, len) == -1) { c->cr_status = EVHTP_RES_FATAL; return -1; } c->request->uri->path = path; c->request->uri->scheme = htparser_get_scheme(p); c->request->method = htparser_get_method(p); htp__lock_(c->htp); { htp__request_set_callbacks_(c->request); } htp__unlock_(c->htp); if ((c->cr_status = htp__hook_path_(c->request, path)) != EVHTP_RES_OK) { return -1; } return 0; } /* htp__request_parse_path_ */ static int htp__request_parse_headers_(htparser * p) { evhtp_connection_t * c; if ((c = htparser_get_userdata(p)) == NULL) { return -1; } /* XXX proto should be set with htparsers on_hdrs_begin hook */ if (htparser_should_keep_alive(p) == 1) { c->request->flags |= EVHTP_REQ_FLAG_KEEPALIVE; } c->request->proto = htp__protocol_(htparser_get_major(p), htparser_get_minor(p)); c->cr_status = htp__hook_headers_(c->request, c->request->headers_in); if (c->cr_status != EVHTP_RES_OK) { return -1; } if (c->type == evhtp_type_server && c->htp->flags & EVHTP_FLAG_ENABLE_100_CONT) { /* only send a 100 continue response if it hasn't been disabled via * evhtp_disable_100_continue. */ if (!evhtp_header_find(c->request->headers_in, "Expect")) { return 0; } evbuffer_add_printf(bufferevent_get_output(c->bev), "HTTP/%c.%c 100 Continue\r\n\r\n", evhtp_modp_uchartoa(htparser_get_major(p)), evhtp_modp_uchartoa(htparser_get_minor(p))); } return 0; } static int htp__request_parse_body_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); struct evbuffer * buf; int res = 0; if (c->max_body_size > 0 && c->body_bytes_read + len >= c->max_body_size) { c->flags |= EVHTP_CONN_FLAG_ERROR; c->cr_status = EVHTP_RES_DATA_TOO_LONG; return -1; } if ((buf = c->scratch_buf) == NULL) { return -1; } evbuffer_add(buf, data, len); if ((c->cr_status = htp__hook_body_(c->request, buf)) != EVHTP_RES_OK) { res = -1; } if (evbuffer_get_length(buf)) { evbuffer_add_buffer(c->request->buffer_in, buf); } evbuffer_drain(buf, -1); c->body_bytes_read += len; return res; } static int htp__request_parse_chunk_new_(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); if ((c->cr_status = htp__hook_chunk_new_(c->request, htparser_get_content_length(p))) != EVHTP_RES_OK) { return -1; } return 0; } static int htp__request_parse_chunk_fini_(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); if ((c->cr_status = htp__hook_chunk_fini_(c->request)) != EVHTP_RES_OK) { return -1; } return 0; } static int htp__request_parse_chunks_fini_(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); if ((c->cr_status = htp__hook_chunks_fini_(c->request)) != EVHTP_RES_OK) { return -1; } return 0; } /** * @brief determines if the request body contains the query arguments. * if the query is NULL and the content length of the body has never * been drained, and the content-type is x-www-form-urlencoded, the * function returns 1 * * @param req * * @return 1 if evhtp can use the body as the query arguments, 0 otherwise. */ static int htp__should_parse_query_body_(evhtp_request_t * req) { const char * content_type; if (req == NULL) { return 0; } if (req->uri == NULL || req->uri->query != NULL) { return 0; } if (evhtp_request_content_len(req) == 0) { return 0; } if (evhtp_request_content_len(req) != evbuffer_get_length(req->buffer_in)) { return 0; } content_type = evhtp_kv_find(req->headers_in, "content-type"); if (content_type == NULL) { return 0; } if (strncasecmp(content_type, "application/x-www-form-urlencoded", 33)) { return 0; } return 1; } static int htp__request_parse_fini_(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); if (c->flags & EVHTP_CONN_FLAG_PAUSED) { return -1; } /* check to see if we should use the body of the request as the query * arguments. * * htp__should_parse_query_body_ does all the proper null checks. */ if (htp__should_parse_query_body_(c->request) == 1) { const char * body; size_t body_len; evhtp_uri_t * uri; struct evbuffer * buf_in; uri = c->request->uri; buf_in = c->request->buffer_in; body_len = evbuffer_get_length(buf_in); body = (const char *)evbuffer_pullup(buf_in, body_len); uri->query_raw = htp__calloc_(body_len + 1, 1); evhtp_alloc_assert(uri->query_raw); memcpy(uri->query_raw, body, body_len); uri->query = evhtp_parse_query(body, body_len); } /* * XXX c->request should never be NULL, but we have found some path of * execution where this actually happens. We will check for now, but the bug * path needs to be tracked down. * */ if (c->request && c->request->cb) { (c->request->cb)(c->request, c->request->cbarg); } if (c->flags & EVHTP_CONN_FLAG_PAUSED) { return -1; } return 0; } /* htp__request_parse_fini_ */ static int htp__create_headers_(evhtp_header_t * header, void * arg) { struct evbuffer * buf = arg; evbuffer_expand(buf, header->klen + 2 + header->vlen + 2); evbuffer_add(buf, header->key, header->klen); evbuffer_add(buf, ": ", 2); evbuffer_add(buf, header->val, header->vlen); evbuffer_add(buf, "\r\n", 2); return 0; } static struct evbuffer * htp__create_reply_(evhtp_request_t * request, evhtp_res code) { struct evbuffer * buf; const char * content_type; char res_buf[2048]; int sres; size_t out_len; unsigned char major; unsigned char minor; char out_buf[64]; evhtp_assert(request && request->headers_out && request->buffer_out && request->conn && request->rc_parser); content_type = evhtp_header_find(request->headers_out, "Content-Type"); out_len = evbuffer_get_length(request->buffer_out); if ((buf = request->rc_scratch) == NULL) { request->rc_scratch = evbuffer_new(); evhtp_alloc_assert(request->rc_scratch); } evbuffer_drain(buf, -1); if (htparser_get_multipart(request->rc_parser) == 1) { goto check_proto; } if (out_len && !(request->flags & EVHTP_REQ_FLAG_CHUNKED)) { /* add extra headers (like content-length/type) if not already present */ if (!evhtp_header_find(request->headers_out, "Content-Length")) { /* convert the buffer_out length to a string and set * and add the new Content-Length header. */ evhtp_modp_sizetoa(out_len, out_buf); evhtp_headers_add_header(request->headers_out, evhtp_header_new("Content-Length", out_buf, 0, 1)); } } check_proto: /* add the proper keep-alive type headers based on http version */ switch (request->proto) { case EVHTP_PROTO_11: if (!(request->flags & EVHTP_REQ_FLAG_KEEPALIVE)) { /* protocol is HTTP/1.1 but client wanted to close */ evhtp_headers_add_header(request->headers_out, evhtp_header_new("Connection", "close", 0, 0)); } if (!evhtp_header_find(request->headers_out, "Content-Length")) { evhtp_headers_add_header(request->headers_out, evhtp_header_new("Content-Length", "0", 0, 0)); } break; case EVHTP_PROTO_10: if (request->flags & EVHTP_REQ_FLAG_KEEPALIVE) { /* protocol is HTTP/1.0 and clients wants to keep established */ evhtp_headers_add_header(request->headers_out, evhtp_header_new("Connection", "keep-alive", 0, 0)); } break; default: /* this sometimes happens when a response is made but paused before * the method has been parsed */ htparser_set_major(request->rc_parser, 1); htparser_set_minor(request->rc_parser, 0); break; } /* switch */ if (!content_type) { evhtp_headers_add_header(request->headers_out, evhtp_header_new("Content-Type", "text/plain", 0, 0)); } /* attempt to add the status line into a temporary buffer and then use * evbuffer_add(). Using plain old snprintf() will be faster than * evbuffer_add_printf(). If the snprintf() fails, which it rarely should, * we fallback to using evbuffer_add_printf(). */ major = evhtp_modp_uchartoa(htparser_get_major(request->rc_parser)); minor = evhtp_modp_uchartoa(htparser_get_minor(request->rc_parser)); evhtp_modp_u32toa((uint32_t)code, out_buf); sres = snprintf(res_buf, sizeof(res_buf), "HTTP/%c.%c %s %s\r\n", major, minor, out_buf, status_code_to_str(code)); if (sres >= sizeof(res_buf) || sres < 0) { /* failed to fit the whole thing in the res_buf, so just fallback to * using evbuffer_add_printf(). */ evbuffer_add_printf(buf, "HTTP/%c.%c %d %s\r\n", major, minor, code, status_code_to_str(code)); } else { /* copy the res_buf using evbuffer_add() instead of add_printf() */ evbuffer_add(buf, res_buf, sres); } evhtp_headers_for_each(request->headers_out, htp__create_headers_, buf); evbuffer_add(buf, "\r\n", 2); if (evbuffer_get_length(request->buffer_out)) { evbuffer_add_buffer(buf, request->buffer_out); } return buf; } /* htp__create_reply_ */ /** * @brief callback definitions for request processing from libhtparse */ static htparse_hooks request_psets = { .on_msg_begin = htp__request_parse_start_, .method = NULL, .scheme = NULL, .host = htp__request_parse_host_, .port = htp__request_parse_port_, .path = htp__request_parse_path_, .args = htp__request_parse_args_, .uri = NULL, .on_hdrs_begin = htp__request_parse_headers_start_, .hdr_key = htp__request_parse_header_key_, .hdr_val = htp__request_parse_header_val_, .hostname = htp__request_parse_hostname_, .on_hdrs_complete = htp__request_parse_headers_, .on_new_chunk = htp__request_parse_chunk_new_, .on_chunk_complete = htp__request_parse_chunk_fini_, .on_chunks_complete = htp__request_parse_chunks_fini_, .body = htp__request_parse_body_, .on_msg_complete = htp__request_parse_fini_ }; static void htp__connection_readcb_(struct bufferevent * bev, void * arg) { evhtp_connection_t * c = arg; void * buf; size_t nread; size_t avail; if (evhtp_unlikely(bev == NULL)) { return; } avail = evbuffer_get_length(bufferevent_get_input(bev)); if (evhtp_unlikely(avail == 0)) { return; } if (c->request) { c->cr_status = EVHTP_RES_OK; } if (c->flags & EVHTP_CONN_FLAG_PAUSED) { return; } buf = evbuffer_pullup(bufferevent_get_input(bev), avail); evhtp_assert(buf != NULL); evhtp_assert(c->parser != NULL); nread = htparser_run(c->parser, &request_psets, (const char *)buf, avail); log_debug("nread = %zu", nread); if (!(c->flags & EVHTP_CONN_FLAG_OWNER)) { /* * someone has taken the ownership of this connection, we still need to * drain the input buffer that had been read up to this point. */ log_debug("EVHTP_CONN_FLAG_OWNER set, removing contexts"); evbuffer_drain(bufferevent_get_input(bev), nread); evhtp_connection_free(c); return; } if (c->request) { switch (c->cr_status) { case EVHTP_RES_DATA_TOO_LONG: htp__hook_connection_error_(c, -1); evhtp_connection_free(c); return; default: break; } } evbuffer_drain(bufferevent_get_input(bev), nread); if (c->request && c->cr_status == EVHTP_RES_PAUSE) { log_debug("Pausing connection"); evhtp_request_pause(c->request); } else if (htparser_get_error(c->parser) != htparse_error_none) { log_debug("error %d, freeing connection", htparser_get_error(c->parser)); evhtp_connection_free(c); } else if (nread < avail) { /* we still have more data to read (piped request probably) */ log_debug("Reading more data via resumption"); evhtp_connection_resume(c); } } /* htp__connection_readcb_ */ static void htp__connection_writecb_(struct bufferevent * bev, void * arg) { evhtp_connection_t * conn; uint64_t keepalive_max; const char * errstr; evhtp_assert(bev != NULL); if (evhtp_unlikely(arg == NULL)) { log_error("No data associated with the bufferevent %p", bev); bufferevent_free(bev); return; } errstr = NULL; conn = (evhtp_connection_t *)arg; do { if (evhtp_unlikely(conn->request == NULL)) { errstr = "no request associated with connection"; break; } if (evhtp_unlikely(conn->parser == NULL)) { errstr = "no parser registered with connection"; break; } if (evhtp_likely(conn->type == evhtp_type_server)) { if (evhtp_unlikely(conn->htp == NULL)) { errstr = "no context associated with the server-connection"; break; } keepalive_max = conn->htp->max_keepalive_requests; } else { keepalive_max = 0; } } while (0); if (evhtp_unlikely(errstr != NULL)) { log_error("shutting down connection: %s", errstr); evhtp_safe_free(conn, evhtp_connection_free); return; } /* connection is in a paused state, no further processing yet */ if ((conn->flags & EVHTP_CONN_FLAG_PAUSED)) { return; } /* run user-hook for on_write callback before further analysis */ htp__hook_connection_write_(conn); if (conn->flags & EVHTP_CONN_FLAG_WAITING) { HTP_FLAG_OFF(conn, EVHTP_CONN_FLAG_WAITING); bufferevent_enable(bev, EV_READ); if (evbuffer_get_length(bufferevent_get_input(bev))) { htp__connection_readcb_(bev, arg); } return; } /* if the connection is not finished, OR there is data ready to output * (can only happen if a user-defined connection_write hook added data * manually, since this is called only when all data has been flushed) * just return and wait. */ if (!(conn->request->flags & EVHTP_REQ_FLAG_FINISHED) || evbuffer_get_length(bufferevent_get_output(bev))) { return; } /* * if there is a set maximum number of keepalive requests configured, check * to make sure we are not over it. If we have gone over the max we set the * keepalive bit to 0, thus closing the connection. */ if (keepalive_max > 0) { conn->num_requests += 1; if (conn->num_requests >= keepalive_max) { HTP_FLAG_OFF(conn->request, EVHTP_REQ_FLAG_KEEPALIVE); } } if (conn->request->flags & EVHTP_REQ_FLAG_KEEPALIVE) { htp_type type; /* free up the current request, set it to NULL, making * way for the next request. */ evhtp_safe_free(conn->request, htp__request_free_); /* since the request is keep-alive, assure that the connection * is aware of the same. */ HTP_FLAG_ON(conn, EVHTP_CONN_FLAG_KEEPALIVE); conn->body_bytes_read = 0; if (conn->type == evhtp_type_server) { if (conn->htp->parent != NULL && !(conn->flags & EVHTP_CONN_FLAG_VHOST_VIA_SNI)) { /* this request was served by a virtual host evhtp_t structure * which was *NOT* found via SSL SNI lookup. In this case we want to * reset our connections evhtp_t structure back to the original so * that subsequent requests can have a different 'Host' header. */ conn->htp = conn->htp->parent; } } switch (conn->type) { case evhtp_type_client: type = htp_type_response; break; case evhtp_type_server: type = htp_type_request; break; default: log_error("Unknown connection type"); evhtp_safe_free(conn, evhtp_connection_free); return; } htparser_init(conn->parser, type); htparser_set_userdata(conn->parser, conn); return; } else { evhtp_safe_free(conn, evhtp_connection_free); return; } return; } /* htp__connection_writecb_ */ static void htp__connection_eventcb_(struct bufferevent * bev, short events, void * arg) { evhtp_connection_t * c = arg; log_debug("%p %p eventcb %s%s%s%s", arg, (void *)bev, events & BEV_EVENT_CONNECTED ? "connected" : "", events & BEV_EVENT_ERROR ? "error" : "", events & BEV_EVENT_TIMEOUT ? "timeout" : "", events & BEV_EVENT_EOF ? "eof" : ""); if (c->hooks && c->hooks->on_event) { (c->hooks->on_event)(c, events, c->hooks->on_event_arg); } if ((events & BEV_EVENT_CONNECTED)) { log_debug("CONNECTED"); if (evhtp_likely(c->type == evhtp_type_client)) { HTP_FLAG_ON(c, EVHTP_CONN_FLAG_CONNECTED); bufferevent_setcb(bev, htp__connection_readcb_, htp__connection_writecb_, htp__connection_eventcb_, c); } return; } #ifndef EVHTP_DISABLE_SSL if (c->ssl && !(events & BEV_EVENT_EOF)) { #ifdef EVHTP_DEBUG unsigned long sslerr; while ((sslerr = bufferevent_get_openssl_error(bev))) { log_error("SSL ERROR %lu:%i:%s:%i:%s:%i:%s", sslerr, ERR_GET_REASON(sslerr), ERR_reason_error_string(sslerr), ERR_GET_LIB(sslerr), ERR_lib_error_string(sslerr), ERR_GET_FUNC(sslerr), ERR_func_error_string(sslerr)); } #endif /* XXX need to do better error handling for SSL specific errors */ HTP_FLAG_ON(c, EVHTP_CONN_FLAG_ERROR); if (c->request) { HTP_FLAG_ON(c->request, EVHTP_REQ_FLAG_ERROR); } } #endif if (events == (BEV_EVENT_EOF | BEV_EVENT_READING)) { if (errno == EAGAIN) { /* libevent will sometimes recv again when it's not actually ready, * this results in a 0 return value, and errno will be set to EAGAIN * (try again). This does not mean there is a hard socket error, but * simply needs to be read again. * * but libevent will disable the read side of the bufferevent * anyway, so we must re-enable it. */ bufferevent_enable(bev, EV_READ); errno = 0; return; } } /* set the error mask */ HTP_FLAG_ON(c, EVHTP_CONN_FLAG_ERROR); /* unset connected flag */ HTP_FLAG_OFF(c, EVHTP_CONN_FLAG_CONNECTED); htp__hook_connection_error_(c, events); if (c->flags & EVHTP_CONN_FLAG_PAUSED) { /* we are currently paused, so we don't want to free just yet, let's * wait till the next loop. */ HTP_FLAG_ON(c, EVHTP_CONN_FLAG_FREE_CONN); } else { evhtp_connection_free((evhtp_connection_t *)arg); } } /* htp__connection_eventcb_ */ static void htp__connection_resumecb_(int fd, short events, void * arg) { evhtp_connection_t * c = arg; /* unset the pause flag */ HTP_FLAG_OFF(c, EVHTP_CONN_FLAG_PAUSED); if (c->request) { c->cr_status = EVHTP_RES_OK; } if (c->flags & EVHTP_CONN_FLAG_FREE_CONN) { evhtp_connection_free(c); return; } /* XXX this is a hack to show a potential fix for issues/86, the main indea * is that you call resume AFTER you have sent the reply (not BEFORE). * * When it has been decided this is a proper fix, the pause bit should be * changed to a state-type flag. */ if (evbuffer_get_length(bufferevent_get_output(c->bev))) { HTP_FLAG_ON(c, EVHTP_CONN_FLAG_WAITING); bufferevent_enable(c->bev, EV_WRITE); } else { bufferevent_enable(c->bev, EV_READ | EV_WRITE); htp__connection_readcb_(c->bev, c); } } static int htp__run_pre_accept_(evhtp_t * htp, evhtp_connection_t * conn) { void * args; evhtp_res res; if (evhtp_likely(htp->defaults.pre_accept == NULL)) { return 0; } args = htp->defaults.pre_accept_cbarg; res = htp->defaults.pre_accept(conn, args); if (res != EVHTP_RES_OK) { return -1; } return 0; } static int htp__connection_accept_(struct event_base * evbase, evhtp_connection_t * connection) { struct timeval * c_recv_timeo; struct timeval * c_send_timeo; if (htp__run_pre_accept_(connection->htp, connection) < 0) { evutil_closesocket(connection->sock); return -1; } #ifndef EVHTP_DISABLE_SSL if (connection->htp->ssl_ctx != NULL) { connection->ssl = SSL_new(connection->htp->ssl_ctx); connection->bev = bufferevent_openssl_socket_new(evbase, connection->sock, connection->ssl, BUFFEREVENT_SSL_ACCEPTING, connection->htp->bev_flags); SSL_set_app_data(connection->ssl, connection); goto end; } #endif connection->bev = bufferevent_socket_new(evbase, connection->sock, connection->htp->bev_flags); log_debug("enter sock=%d\n", connection->sock); #ifndef EVHTP_DISABLE_SSL end: #endif if (connection->recv_timeo.tv_sec || connection->recv_timeo.tv_usec) { c_recv_timeo = &connection->recv_timeo; } else if (connection->htp->recv_timeo.tv_sec || connection->htp->recv_timeo.tv_usec) { c_recv_timeo = &connection->htp->recv_timeo; } else { c_recv_timeo = NULL; } if (connection->send_timeo.tv_sec || connection->send_timeo.tv_usec) { c_send_timeo = &connection->send_timeo; } else if (connection->htp->send_timeo.tv_sec || connection->htp->send_timeo.tv_usec) { c_send_timeo = &connection->htp->send_timeo; } else { c_send_timeo = NULL; } evhtp_connection_set_timeouts(connection, c_recv_timeo, c_send_timeo); connection->resume_ev = event_new(evbase, -1, EV_READ | EV_PERSIST, htp__connection_resumecb_, connection); event_add(connection->resume_ev, NULL); bufferevent_enable(connection->bev, EV_READ); bufferevent_setcb(connection->bev, htp__connection_readcb_, htp__connection_writecb_, htp__connection_eventcb_, connection); return 0; } /* htp__connection_accept_ */ static void htp__default_request_cb_(evhtp_request_t * request, void * arg) { evhtp_headers_add_header(request->headers_out, evhtp_header_new("Content-Length", "0", 0, 0)); evhtp_send_reply(request, EVHTP_RES_NOTFOUND); } static evhtp_connection_t * htp__connection_new_(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) { evhtp_connection_t * connection; htp_type ptype; switch (type) { case evhtp_type_client: ptype = htp_type_response; break; case evhtp_type_server: ptype = htp_type_request; break; default: return NULL; } connection = htp__calloc_(sizeof(evhtp_connection_t), 1); evhtp_alloc_assert(connection); connection->scratch_buf = evbuffer_new(); evhtp_alloc_assert(connection->scratch_buf); connection->flags = EVHTP_CONN_FLAG_OWNER; connection->sock = sock; connection->htp = htp; connection->type = type; connection->parser = htparser_new(); evhtp_alloc_assert(connection->parser); htparser_init(connection->parser, ptype); htparser_set_userdata(connection->parser, connection); return connection; } /* htp__connection_new_ */ #ifdef LIBEVENT_HAS_SHUTDOWN #ifndef EVHTP_DISABLE_SSL static void htp__shutdown_eventcb_(struct bufferevent * bev, short events, void * arg) { } #endif #endif static int htp__run_post_accept_(evhtp_t * htp, evhtp_connection_t * connection) { void * args; evhtp_res res; if (evhtp_likely(htp->defaults.post_accept == NULL)) { return 0; } args = htp->defaults.post_accept_cbarg; res = htp->defaults.post_accept(connection, args); if (res != EVHTP_RES_OK) { return -1; } return 0; } #ifndef EVHTP_DISABLE_EVTHR static void htp__run_in_thread_(evthr_t * thr, void * arg, void * shared) { evhtp_t * htp = shared; evhtp_connection_t * connection = arg; connection->evbase = evthr_get_base(thr); connection->thread = thr; if (htp__connection_accept_(connection->evbase, connection) < 0) { evhtp_connection_free(connection); return; } if (htp__run_post_accept_(htp, connection) < 0) { evhtp_connection_free(connection); return; } } #endif static void htp__accept_cb_(struct evconnlistener * serv, int fd, struct sockaddr * s, int sl, void * arg) { evhtp_t * htp = arg; evhtp_connection_t * connection; evhtp_assert(htp && serv && serv && s); connection = htp__connection_new_(htp, fd, evhtp_type_server); if (evhtp_unlikely(connection == NULL)) { return; } log_debug("fd = %d, conn = %p", fd, connection); connection->saddr = htp__malloc_(sl); evhtp_alloc_assert(connection->saddr); memcpy(connection->saddr, s, sl); #ifndef EVHTP_DISABLE_EVTHR if (htp->thr_pool != NULL) { if (evthr_pool_defer(htp->thr_pool, htp__run_in_thread_, connection) != EVTHR_RES_OK) { evutil_closesocket(connection->sock); evhtp_connection_free(connection); return; } return; } #endif connection->evbase = htp->evbase; if (htp__connection_accept_(htp->evbase, connection) == -1) { evhtp_connection_free(connection); return; } if (htp__run_post_accept_(htp, connection) == -1) { evhtp_connection_free(connection); return; } } /* htp__accept_cb_ */ #ifndef EVHTP_DISABLE_SSL #ifndef EVHTP_DISABLE_EVTHR static unsigned long htp__ssl_get_thread_id_(void) { #ifndef WIN32 return (unsigned long)pthread_self(); #else return (unsigned long)(pthread_self().p); #endif } static void htp__ssl_thread_lock_(int mode, int type, const char * file, int line) { if (type < ssl_num_locks) { if (mode & CRYPTO_LOCK) { pthread_mutex_lock(&(ssl_locks[type])); } else { pthread_mutex_unlock(&(ssl_locks[type])); } } } #endif static void htp__ssl_delete_scache_ent_(evhtp_ssl_ctx_t * ctx, evhtp_ssl_sess_t * sess) { evhtp_t * htp; evhtp_ssl_cfg_t * cfg; evhtp_ssl_data_t * sid; unsigned int slen; htp = (evhtp_t *)SSL_CTX_get_app_data(ctx); cfg = htp->ssl_cfg; sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen); if (cfg->scache_del) { (cfg->scache_del)(htp, sid, slen); } } static int htp__ssl_add_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_sess_t * sess) { evhtp_connection_t * connection; evhtp_ssl_cfg_t * cfg; evhtp_ssl_data_t * sid; int slen; connection = (evhtp_connection_t *)SSL_get_app_data(ssl); if (connection->htp == NULL) { return 0; /* We cannot get the ssl_cfg */ } cfg = connection->htp->ssl_cfg; sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen); SSL_set_timeout(sess, cfg->scache_timeout); if (cfg->scache_add) { return (cfg->scache_add)(connection, sid, slen, sess); } return 0; } static evhtp_ssl_sess_t * htp__ssl_get_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_data_t * sid, int sid_len, int * copy) { evhtp_connection_t * connection; evhtp_ssl_cfg_t * cfg; evhtp_ssl_sess_t * sess; connection = (evhtp_connection_t * )SSL_get_app_data(ssl); if (connection->htp == NULL) { return NULL; /* We have no way of getting ssl_cfg */ } cfg = connection->htp->ssl_cfg; sess = NULL; if (cfg->scache_get) { sess = (cfg->scache_get)(connection, sid, sid_len); } *copy = 0; return sess; } static int htp__ssl_servername_(evhtp_ssl_t * ssl, int * unused, void * arg) { const char * sname; evhtp_connection_t * connection; evhtp_t * evhtp; evhtp_t * evhtp_vhost; if (evhtp_unlikely(ssl == NULL)) { return SSL_TLSEXT_ERR_NOACK; } if (!(sname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) { return SSL_TLSEXT_ERR_NOACK; } if (!(connection = SSL_get_app_data(ssl))) { return SSL_TLSEXT_ERR_NOACK; } if (!(evhtp = connection->htp)) { return SSL_TLSEXT_ERR_NOACK; } if ((evhtp_vhost = htp__request_find_vhost_(evhtp, sname))) { SSL_CTX * ctx = SSL_get_SSL_CTX(ssl); connection->htp = evhtp_vhost; HTP_FLAG_ON(connection, EVHTP_CONN_FLAG_VHOST_VIA_SNI); SSL_set_SSL_CTX(ssl, evhtp_vhost->ssl_ctx); SSL_set_options(ssl, SSL_CTX_get_options(ctx)); if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) || (SSL_num_renegotiations(ssl) == 0)) { SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx), SSL_CTX_get_verify_callback(ctx)); } return SSL_TLSEXT_ERR_OK; } return SSL_TLSEXT_ERR_NOACK; } /* htp__ssl_servername_ */ #endif /* * PUBLIC FUNCTIONS */ htp_method evhtp_request_get_method(evhtp_request_t * r) { evhtp_assert(r != NULL); evhtp_assert(r->conn != NULL); evhtp_assert(r->conn->parser != NULL); return htparser_get_method(r->conn->parser); } void evhtp_connection_pause(evhtp_connection_t * c) { evhtp_assert(c != NULL); HTP_FLAG_ON(c, EVHTP_CONN_FLAG_PAUSED); bufferevent_disable(c->bev, EV_READ); return; } void evhtp_connection_resume(evhtp_connection_t * c) { evhtp_assert(c != NULL); HTP_FLAG_OFF(c, EVHTP_CONN_FLAG_PAUSED); event_active(c->resume_ev, EV_WRITE, 1); return; } void evhtp_request_pause(evhtp_request_t * request) { evhtp_assert(request != NULL); request->status = EVHTP_RES_PAUSE; evhtp_connection_pause(request->conn); } void evhtp_request_resume(evhtp_request_t * request) { evhtp_assert(request != NULL); evhtp_connection_resume(request->conn); } evhtp_header_t * evhtp_header_key_add(evhtp_headers_t * headers, const char * key, char key_alloc) { evhtp_header_t * header; if (!(header = evhtp_header_new(key, NULL, key_alloc, 0))) { return NULL; } evhtp_headers_add_header(headers, header); return header; } evhtp_header_t * evhtp_header_val_add(evhtp_headers_t * headers, const char * val, char val_alloc) { evhtp_header_t * header; if (!headers || !val) { return NULL; } if (!(header = TAILQ_LAST(headers, evhtp_headers_s))) { return NULL; } if (header->val != NULL) { return NULL; } header->vlen = strlen(val); if (val_alloc == 1) { header->val = htp__malloc_(header->vlen + 1); evhtp_alloc_assert(header->val); header->val[header->vlen] = '\0'; memcpy(header->val, val, header->vlen); } else { header->val = (char *)val; } header->v_heaped = val_alloc; return header; } evhtp_kvs_t * evhtp_kvs_new(void) { evhtp_kvs_t * kvs; kvs = htp__malloc_(sizeof(evhtp_kvs_t)); evhtp_alloc_assert(kvs); TAILQ_INIT(kvs); return kvs; } evhtp_kv_t * evhtp_kv_new(const char * key, const char * val, char key_alloc, char val_alloc) { evhtp_kv_t * kv; kv = htp__malloc_(sizeof(evhtp_kv_t)); evhtp_alloc_assert(kv); kv->k_heaped = key_alloc; kv->v_heaped = val_alloc; kv->klen = 0; kv->vlen = 0; kv->key = NULL; kv->val = NULL; if (key != NULL) { kv->klen = strlen(key); if (key_alloc == 1) { char * s; if (!(s = htp__malloc_(kv->klen + 1))) { evhtp_safe_free(kv, htp__free_); return NULL; } memcpy(s, key, kv->klen); s[kv->klen] = '\0'; kv->key = s; } else { kv->key = (char *)key; } } if (val != NULL) { kv->vlen = strlen(val); if (val_alloc == 1) { char * s = htp__malloc_(kv->vlen + 1); s[kv->vlen] = '\0'; memcpy(s, val, kv->vlen); kv->val = s; } else { kv->val = (char *)val; } } return kv; } /* evhtp_kv_new */ void evhtp_kv_free(evhtp_kv_t * kv) { if (evhtp_unlikely(kv == NULL)) { return; } if (kv->k_heaped) { evhtp_safe_free(kv->key, htp__free_); } if (kv->v_heaped) { evhtp_safe_free(kv->val, htp__free_); } evhtp_safe_free(kv, htp__free_); } void evhtp_kv_rm_and_free(evhtp_kvs_t * kvs, evhtp_kv_t * kv) { if (evhtp_unlikely(kvs == NULL || kv == NULL)) { return; } TAILQ_REMOVE(kvs, kv, next); evhtp_kv_free(kv); } void evhtp_kvs_free(evhtp_kvs_t * kvs) { evhtp_kv_t * kv; evhtp_kv_t * save; if (evhtp_unlikely(kvs == NULL)) { return; } kv = NULL; save = NULL; for (kv = TAILQ_FIRST(kvs); kv != NULL; kv = save) { save = TAILQ_NEXT(kv, next); TAILQ_REMOVE(kvs, kv, next); evhtp_safe_free(kv, evhtp_kv_free); } evhtp_safe_free(kvs, htp__free_); } int evhtp_kvs_for_each(evhtp_kvs_t * kvs, evhtp_kvs_iterator cb, void * arg) { evhtp_kv_t * kv; if (kvs == NULL || cb == NULL) { return -1; } TAILQ_FOREACH(kv, kvs, next) { int res; if ((res = cb(kv, arg))) { return res; } } return 0; } const char * evhtp_kv_find(evhtp_kvs_t * kvs, const char * key) { evhtp_kv_t * kv; if (evhtp_unlikely(kvs == NULL || key == NULL)) { return NULL; } TAILQ_FOREACH(kv, kvs, next) { if (strcasecmp(kv->key, key) == 0) { return kv->val; } } return NULL; } evhtp_kv_t * evhtp_kvs_find_kv(evhtp_kvs_t * kvs, const char * key) { evhtp_kv_t * kv; if (evhtp_unlikely(kvs == NULL || key == NULL)) { return NULL; } TAILQ_FOREACH(kv, kvs, next) { if (strcasecmp(kv->key, key) == 0) { return kv; } } return NULL; } void evhtp_kvs_add_kv(evhtp_kvs_t * kvs, evhtp_kv_t * kv) { if (evhtp_unlikely(kvs == NULL || kv == NULL)) { return; } TAILQ_INSERT_TAIL(kvs, kv, next); } void evhtp_kvs_add_kvs(evhtp_kvs_t * dst, evhtp_kvs_t * src) { if (dst == NULL || src == NULL) { return; } evhtp_kv_t * kv; TAILQ_FOREACH(kv, src, next) { evhtp_kvs_add_kv(dst, evhtp_kv_new(kv->key, kv->val, kv->k_heaped, kv->v_heaped)); } } typedef enum { s_query_start = 0, s_query_separator, s_query_key, s_query_val, s_query_key_hex_1, s_query_key_hex_2, s_query_val_hex_1, s_query_val_hex_2, s_query_done } query_parser_state; static inline int evhtp_is_hex_query_char(unsigned char ch) { switch (ch) { case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return 1; default: return 0; } /* switch */ } enum unscape_state { unscape_state_start = 0, unscape_state_hex1, unscape_state_hex2 }; int evhtp_unescape_string(unsigned char ** out, unsigned char * str, size_t str_len) { unsigned char * optr; unsigned char * sptr; unsigned char d; unsigned char ch; unsigned char c; size_t i; enum unscape_state state; state = unscape_state_start; optr = *out; sptr = str; d = 0; *out = NULL; for (i = 0; i < str_len; i++) { ch = *sptr++; switch (state) { case unscape_state_start: if (ch == '%') { state = unscape_state_hex1; break; } *optr++ = ch; break; case unscape_state_hex1: if (ch >= '0' && ch <= '9') { d = (unsigned char)(ch - '0'); state = unscape_state_hex2; break; } c = (unsigned char)(ch | 0x20); if (c >= 'a' && c <= 'f') { d = (unsigned char)(c - 'a' + 10); state = unscape_state_hex2; break; } state = unscape_state_start; *optr++ = ch; break; case unscape_state_hex2: state = unscape_state_start; if (ch >= '0' && ch <= '9') { ch = (unsigned char)((d << 4) + ch - '0'); *optr++ = ch; break; } c = (unsigned char)(ch | 0x20); if (c >= 'a' && c <= 'f') { ch = (unsigned char)((d << 4) + c - 'a' + 10); *optr++ = ch; break; } break; } /* switch */ } return 0; } /* evhtp_unescape_string */ evhtp_query_t * evhtp_parse_query_wflags(const char * query, const size_t len, const int flags) { evhtp_query_t * query_args; query_parser_state state; size_t key_idx; size_t val_idx; unsigned char ch; size_t i; if (len > (SIZE_MAX - (len + 2))) { return NULL; } query_args = evhtp_query_new(); state = s_query_start; key_idx = 0; val_idx = 0; #ifdef EVHTP_HAS_C99 char key_buf[len + 1]; char val_buf[len + 1]; #else char * key_buf; char * val_buf; key_buf = htp__malloc_(len + 1); evhtp_alloc_assert(key_buf); val_buf = htp__malloc_(len + 1); evhtp_alloc_assert(val_buf); #endif for (i = 0; i < len; i++) { ch = query[i]; if (key_idx >= len || val_idx >= len) { goto error; } switch (state) { case s_query_start: key_idx = 0; val_idx = 0; key_buf[0] = '\0'; val_buf[0] = '\0'; state = s_query_key; /* Fall through. */ case s_query_key: switch (ch) { case '=': state = s_query_val; break; case '%': key_buf[key_idx++] = ch; key_buf[key_idx] = '\0'; if (!(flags & EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX)) { state = s_query_key_hex_1; } break; case ';': if (!(flags & EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP)) { key_buf[key_idx++] = ch; key_buf[key_idx] = '\0'; break; } /* otherwise we fallthrough */ case '&': /* in this state, we have a NULL value */ if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS)) { goto error; } /* insert the key with value of NULL and set the * state back to parsing s_query_key. */ evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 1)); key_idx = 0; val_idx = 0; key_buf[0] = '\0'; val_buf[0] = '\0'; state = s_query_key; break; default: key_buf[key_idx++] = ch; key_buf[key_idx] = '\0'; break; } /* switch */ break; case s_query_key_hex_1: if (!evhtp_is_hex_query_char(ch)) { /* not hex, so we treat as a normal key */ if ((key_idx + 2) >= len) { /* we need to insert \%, but not enough space */ goto error; } key_buf[key_idx - 1] = '%'; key_buf[key_idx++] = ch; key_buf[key_idx] = '\0'; state = s_query_key; break; } key_buf[key_idx++] = ch; key_buf[key_idx] = '\0'; state = s_query_key_hex_2; break; case s_query_key_hex_2: if (!evhtp_is_hex_query_char(ch)) { goto error; } key_buf[key_idx++] = ch; key_buf[key_idx] = '\0'; state = s_query_key; break; case s_query_val: switch (ch) { case ';': if (!(flags & EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP)) { val_buf[val_idx++] = ch; val_buf[val_idx] = '\0'; break; } case '&': evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1)); key_idx = 0; val_idx = 0; key_buf[0] = '\0'; val_buf[0] = '\0'; state = s_query_key; break; case '%': val_buf[val_idx++] = ch; val_buf[val_idx] = '\0'; if (!(flags & EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX)) { state = s_query_val_hex_1; } break; default: val_buf[val_idx++] = ch; val_buf[val_idx] = '\0'; break; } /* switch */ break; case s_query_val_hex_1: if (!evhtp_is_hex_query_char(ch)) { /* not really a hex val */ if ((val_idx + 2) >= len) { /* we need to insert \%, but not enough space */ goto error; } if (val_idx == 0) { goto error; } val_buf[val_idx - 1] = '%'; val_buf[val_idx++] = ch; val_buf[val_idx] = '\0'; state = s_query_val; break; } val_buf[val_idx++] = ch; val_buf[val_idx] = '\0'; state = s_query_val_hex_2; break; case s_query_val_hex_2: if (!evhtp_is_hex_query_char(ch)) { goto error; } val_buf[val_idx++] = ch; val_buf[val_idx] = '\0'; state = s_query_val; break; default: /* bad state */ goto error; } /* switch */ } if (key_idx) { do { if (val_idx) { evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1)); break; } if (state >= s_query_val) { if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS)) { goto error; } evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, "", 1, 1)); break; } if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS)) { goto error; } evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 0)); } while (0); } #ifndef EVHTP_HAS_C99 evhtp_safe_free(key_buf, htp__free_); evhtp_safe_free(val_buf, htp__free_); #endif return query_args; error: #ifndef EVHTP_HAS_C99 evhtp_safe_free(key_buf, htp__free_); evhtp_safe_free(val_buf, htp__free_); #endif evhtp_query_free(query_args); return NULL; } /* evhtp_parse_query */ evhtp_query_t * evhtp_parse_query(const char * query, size_t len) { return evhtp_parse_query_wflags(query, len, EVHTP_PARSE_QUERY_FLAG_STRICT); } void evhtp_send_reply_start(evhtp_request_t * request, evhtp_res code) { evhtp_connection_t * c; struct evbuffer * reply_buf; c = evhtp_request_get_connection(request); if (!(reply_buf = htp__create_reply_(request, code))) { evhtp_connection_free(c); return; } bufferevent_write_buffer(c->bev, reply_buf); evbuffer_drain(reply_buf, -1); } void evhtp_send_reply_body(evhtp_request_t * request, struct evbuffer * buf) { evhtp_connection_t * c; c = request->conn; bufferevent_write_buffer(c->bev, buf); } void evhtp_send_reply_end(evhtp_request_t * request) { HTP_FLAG_ON(request, EVHTP_REQ_FLAG_FINISHED); } void evhtp_send_reply(evhtp_request_t * request, evhtp_res code) { evhtp_connection_t * c; struct evbuffer * reply_buf; struct bufferevent * bev; c = request->conn; HTP_FLAG_ON(request, EVHTP_REQ_FLAG_FINISHED); if (!(reply_buf = htp__create_reply_(request, code))) { evhtp_connection_free(request->conn); return; } bev = c->bev; bufferevent_lock(bev); { bufferevent_write_buffer(bev, reply_buf); } bufferevent_unlock(bev); evbuffer_drain(reply_buf, -1); } int evhtp_response_needs_body(const evhtp_res code, const htp_method method) { return code != EVHTP_RES_NOCONTENT && code != EVHTP_RES_NOTMOD && (code < 100 || code >= 200) && method != htp_method_HEAD; } void evhtp_send_reply_chunk_start(evhtp_request_t * request, evhtp_res code) { evhtp_header_t * content_len; if (evhtp_response_needs_body(code, request->method)) { content_len = evhtp_headers_find_header(request->headers_out, "Content-Length"); switch (request->proto) { case EVHTP_PROTO_11: /* * prefer HTTP/1.1 chunked encoding to closing the connection; * note RFC 2616 section 4.4 forbids it with Content-Length: * and it's not necessary then anyway. */ evhtp_kv_rm_and_free(request->headers_out, content_len); HTP_FLAG_ON(request, EVHTP_REQ_FLAG_CHUNKED); break; case EVHTP_PROTO_10: /* * HTTP/1.0 can be chunked as long as the Content-Length header * is set to 0 */ evhtp_kv_rm_and_free(request->headers_out, content_len); HTP_FLAG_ON(request, EVHTP_REQ_FLAG_CHUNKED); break; default: HTP_FLAG_OFF(request, EVHTP_REQ_FLAG_CHUNKED); break; } /* switch */ } else { HTP_FLAG_OFF(request, EVHTP_REQ_FLAG_CHUNKED); } if (request->flags & EVHTP_REQ_FLAG_CHUNKED) { evhtp_headers_add_header(request->headers_out, evhtp_header_new("Transfer-Encoding", "chunked", 0, 0)); /* * if data already exists on the output buffer, we automagically convert * it to the first chunk. */ if (evbuffer_get_length(request->buffer_out) > 0) { char lstr[128]; int sres; sres = snprintf(lstr, sizeof(lstr), "%x\r\n", (unsigned)evbuffer_get_length(request->buffer_out)); if (sres >= sizeof(lstr) || sres < 0) { /* overflow condition, shouldn't ever get here, but lets * terminate the connection asap */ goto end; } evbuffer_prepend(request->buffer_out, lstr, strlen(lstr)); evbuffer_add(request->buffer_out, "\r\n", 2); } } end: evhtp_send_reply_start(request, code); } /* evhtp_send_reply_chunk_start */ void evhtp_send_reply_chunk(evhtp_request_t * request, struct evbuffer * buf) { struct evbuffer * output; if (evbuffer_get_length(buf) == 0) { return; } output = bufferevent_get_output(request->conn->bev); if (request->flags & EVHTP_REQ_FLAG_CHUNKED) { evbuffer_add_printf(output, "%x\r\n", (unsigned)evbuffer_get_length(buf)); } evhtp_send_reply_body(request, buf); if (request->flags & EVHTP_REQ_FLAG_CHUNKED) { evbuffer_add(output, "\r\n", 2); } bufferevent_flush(request->conn->bev, EV_WRITE, BEV_FLUSH); } void evhtp_send_reply_chunk_end(evhtp_request_t * request) { if (request->flags & EVHTP_REQ_FLAG_CHUNKED) { evbuffer_add(bufferevent_get_output(evhtp_request_get_bev(request)), "0\r\n\r\n", 5); } evhtp_send_reply_end(request); } void evhtp_unbind_socket(evhtp_t * htp) { if (htp == NULL || htp->server == NULL) { return; } evhtp_safe_free(htp->server, evconnlistener_free); } int evhtp_accept_socket(evhtp_t * htp, evutil_socket_t sock, int backlog) { int on = 1; int err = 1; if (htp == NULL || sock == -1) { return -1; } do { #if defined SO_REUSEPORT if (htp->flags & EVHTP_FLAG_ENABLE_REUSEPORT) { if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) == -1) { if (errno != EOPNOTSUPP) { break; } log_warn("SO_REUSEPORT not supported for this socket.. Skipping"); } } #endif #if defined TCP_NODELAY if (htp->flags & EVHTP_FLAG_ENABLE_NODELAY) { if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)) == -1) { if (errno != EOPNOTSUPP) { break; } log_warn("TCP_NODELAY not supported for this socket.. Skipping"); } } #endif #if defined TCP_DEFER_ACCEPT if (htp->flags & EVHTP_FLAG_ENABLE_DEFER_ACCEPT) { if (setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, (void *)&on, sizeof(on)) == -1) { if (errno != EOPNOTSUPP) { break; } log_warn("TCP_DEFER_ACCEPT not supported for this socket.. Skipping"); } } #endif htp->server = evconnlistener_new(htp->evbase, htp__accept_cb_, htp, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, backlog, sock); if (htp->server == NULL) { break; } #ifndef EVHTP_DISABLE_SSL if (htp->ssl_ctx != NULL) { /* if ssl is enabled and we have virtual hosts, set our servername * callback. We do this here because we want to make sure that this gets * set after all potential virtualhosts have been set, not just after * ssl_init. */ if (TAILQ_FIRST(&htp->vhosts) != NULL) { SSL_CTX_set_tlsext_servername_callback(htp->ssl_ctx, htp__ssl_servername_); } } #endif err = 0; } while (0); if (err == 1) { if (htp->server != NULL) { evhtp_safe_free(htp->server, evconnlistener_free); } return -1; } return 0; } /* evhtp_accept_socket */ int evhtp_bind_sockaddr(evhtp_t * htp, struct sockaddr * sa, size_t sin_len, int backlog) { evutil_socket_t fd = -1; int on = 1; int error = 1; if (htp == NULL) { return -1; } /* XXX: API's should not set signals */ #ifndef WIN32 signal(SIGPIPE, SIG_IGN); #endif do { if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { return -1; } evutil_make_socket_closeonexec(fd); evutil_make_socket_nonblocking(fd); if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)) == -1) { break; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) == -1) { break; } if (sa->sa_family == AF_INET6) { if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) { break; } } if (bind(fd, sa, sin_len) == -1) { break; } error = 0; } while (0); if (error == 1) { if (fd != -1) { evutil_closesocket(fd); } return -1; } if (evhtp_accept_socket(htp, fd, backlog) == -1) { /* accept_socket() does not close the descriptor * on error, but this function does. */ evutil_closesocket(fd); return -1; } return 0; } /* evhtp_bind_sockaddr */ int evhtp_bind_socket(evhtp_t * htp, const char * baddr, uint16_t port, int backlog) { #ifndef NO_SYS_UN struct sockaddr_un sockun = { 0 }; #endif struct sockaddr * sa; struct sockaddr_in6 sin6 = { 0 }; struct sockaddr_in sin = { 0 }; size_t sin_len; if (!strncmp(baddr, "ipv6:", 5)) { baddr += 5; sin_len = sizeof(struct sockaddr_in6); sin6.sin6_port = htons(port); sin6.sin6_family = AF_INET6; evutil_inet_pton(AF_INET6, baddr, &sin6.sin6_addr); sa = (struct sockaddr *)&sin6; } else if (!strncmp(baddr, "unix:", 5)) { #ifndef NO_SYS_UN baddr += 5; if (strlen(baddr) >= sizeof(sockun.sun_path)) { return -1; } sin_len = sizeof(struct sockaddr_un); sockun.sun_family = AF_UNIX; strncpy(sockun.sun_path, baddr, strlen(baddr)); sa = (struct sockaddr *)&sockun; #else return -1; #endif } else { if (!strncmp(baddr, "ipv4:", 5)) { baddr += 5; } sin_len = sizeof(struct sockaddr_in); sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = inet_addr(baddr); sa = (struct sockaddr *)&sin; } return evhtp_bind_sockaddr(htp, sa, sin_len, backlog); } /* evhtp_bind_socket */ void evhtp_callbacks_free(evhtp_callbacks_t * callbacks) { evhtp_callback_t * callback; evhtp_callback_t * tmp; if (callbacks == NULL) { return; } TAILQ_FOREACH_SAFE(callback, callbacks, next, tmp) { TAILQ_REMOVE(callbacks, callback, next); evhtp_safe_free(callback, evhtp_callback_free); } evhtp_safe_free(callbacks, htp__free_); } evhtp_callback_t * evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_cb cb, void * arg) { evhtp_callback_t * hcb; hcb = htp__calloc_(sizeof(evhtp_callback_t), 1); evhtp_alloc_assert(hcb); hcb->type = type; hcb->cb = cb; hcb->cbarg = arg; hcb->len = strlen(path); switch (type) { case evhtp_callback_type_hash: hcb->val.path = htp__strdup_(path); evhtp_alloc_assert(hcb->val.path); break; #ifndef EVHTP_DISABLE_REGEX case evhtp_callback_type_regex: hcb->val.regex = htp__malloc_(sizeof(regex_t)); evhtp_alloc_assert(hcb->val.regex); if (regcomp(hcb->val.regex, (char *)path, REG_EXTENDED) != 0) { evhtp_safe_free(hcb->val.regex, htp__free_); evhtp_safe_free(hcb, htp__free_); return NULL; } break; #endif case evhtp_callback_type_glob: hcb->val.glob = htp__strdup_(path); evhtp_alloc_assert(hcb->val.glob); break; default: evhtp_safe_free(hcb, htp__free_); return NULL; } /* switch */ return hcb; } /* evhtp_callback_new */ void evhtp_callback_free(evhtp_callback_t * callback) { if (callback == NULL) { return; } switch (callback->type) { case evhtp_callback_type_hash: evhtp_safe_free(callback->val.path, htp__free_); break; case evhtp_callback_type_glob: evhtp_safe_free(callback->val.glob, htp__free_); break; #ifndef EVHTP_DISABLE_REGEX case evhtp_callback_type_regex: regfree(callback->val.regex); evhtp_safe_free(callback->val.regex, htp__free_); break; #endif } if (callback->hooks) { evhtp_safe_free(callback->hooks, htp__free_); } evhtp_safe_free(callback, htp__free_); return; } int evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb) { TAILQ_INSERT_TAIL(cbs, cb, next); return 0; } static int htp__set_hook_(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg) { if (*hooks == NULL) { if (!(*hooks = htp__calloc_(sizeof(evhtp_hooks_t), 1))) { return -1; } } switch (type) { case evhtp_hook_on_headers_start: (*hooks)->on_headers_start = (evhtp_hook_headers_start_cb)cb; (*hooks)->on_headers_start_arg = arg; break; case evhtp_hook_on_header: (*hooks)->on_header = (evhtp_hook_header_cb)cb; (*hooks)->on_header_arg = arg; break; case evhtp_hook_on_headers: (*hooks)->on_headers = (evhtp_hook_headers_cb)cb; (*hooks)->on_headers_arg = arg; break; case evhtp_hook_on_path: (*hooks)->on_path = (evhtp_hook_path_cb)cb; (*hooks)->on_path_arg = arg; break; case evhtp_hook_on_read: (*hooks)->on_read = (evhtp_hook_read_cb)cb; (*hooks)->on_read_arg = arg; break; case evhtp_hook_on_request_fini: (*hooks)->on_request_fini = (evhtp_hook_request_fini_cb)cb; (*hooks)->on_request_fini_arg = arg; break; case evhtp_hook_on_connection_fini: (*hooks)->on_connection_fini = (evhtp_hook_connection_fini_cb)cb; (*hooks)->on_connection_fini_arg = arg; break; case evhtp_hook_on_conn_error: (*hooks)->on_connection_error = (evhtp_hook_conn_err_cb)cb; (*hooks)->on_connection_error_arg = arg; break; case evhtp_hook_on_error: (*hooks)->on_error = (evhtp_hook_err_cb)cb; (*hooks)->on_error_arg = arg; break; case evhtp_hook_on_new_chunk: (*hooks)->on_new_chunk = (evhtp_hook_chunk_new_cb)cb; (*hooks)->on_new_chunk_arg = arg; break; case evhtp_hook_on_chunk_complete: (*hooks)->on_chunk_fini = (evhtp_hook_chunk_fini_cb)cb; (*hooks)->on_chunk_fini_arg = arg; break; case evhtp_hook_on_chunks_complete: (*hooks)->on_chunks_fini = (evhtp_hook_chunks_fini_cb)cb; (*hooks)->on_chunks_fini_arg = arg; break; case evhtp_hook_on_hostname: (*hooks)->on_hostname = (evhtp_hook_hostname_cb)cb; (*hooks)->on_hostname_arg = arg; break; case evhtp_hook_on_write: (*hooks)->on_write = (evhtp_hook_write_cb)cb; (*hooks)->on_write_arg = arg; break; case evhtp_hook_on_event: (*hooks)->on_event = (evhtp_hook_event_cb)cb; (*hooks)->on_event_arg = arg; break; default: return -1; } /* switch */ return 0; } /* htp__set_hook_ */ static int htp__unset_hook_(evhtp_hooks_t ** hooks, evhtp_hook_type type) { return htp__set_hook_(hooks, type, NULL, NULL); } int evhtp_callback_unset_hook(evhtp_callback_t * callback, evhtp_hook_type type) { return htp__unset_hook_(&callback->hooks, type); } int evhtp_request_unset_hook(evhtp_request_t * req, evhtp_hook_type type) { return htp__unset_hook_(&req->hooks, type); } int evhtp_connection_unset_hook(evhtp_connection_t * conn, evhtp_hook_type type) { return htp__unset_hook_(&conn->hooks, type); } int evhtp_callback_set_hook(evhtp_callback_t * callback, evhtp_hook_type type, evhtp_hook cb, void * arg) { return htp__set_hook_(&callback->hooks, type, cb, arg); } int evhtp_request_set_hook(evhtp_request_t * req, evhtp_hook_type type, evhtp_hook cb, void * arg) { return htp__set_hook_(&req->hooks, type, cb, arg); } int evhtp_connection_set_hook(evhtp_connection_t * conn, evhtp_hook_type type, evhtp_hook cb, void * arg) { return htp__set_hook_(&conn->hooks, type, cb, arg); } int evhtp_unset_all_hooks(evhtp_hooks_t ** hooks) { int i; struct { enum evhtp_hook_type type; } hooklist_[] = { { evhtp_hook_on_header }, { evhtp_hook_on_headers }, { evhtp_hook_on_path }, { evhtp_hook_on_read }, { evhtp_hook_on_request_fini }, { evhtp_hook_on_connection_fini }, { evhtp_hook_on_new_chunk }, { evhtp_hook_on_chunk_complete }, { evhtp_hook_on_chunks_complete }, { evhtp_hook_on_headers_start }, { evhtp_hook_on_error }, { evhtp_hook_on_hostname }, { evhtp_hook_on_write }, { evhtp_hook_on_event }, { evhtp_hook_on_conn_error }, { -1 } }; if (hooks == NULL) { return -1; } for (i = 0; hooklist_[i].type != -1; i++) { if (htp__unset_hook_(hooks, hooklist_[i].type) == -1) { return -1; } } return 0; } evhtp_hooks_t * evhtp_connection_get_hooks(evhtp_connection_t * c) { if (evhtp_unlikely(c == NULL)) { return NULL; } return c->hooks; } /** * @brief returns request hooks * * @param r * @return */ evhtp_hooks_t * evhtp_request_get_hooks(evhtp_request_t * r) { if (evhtp_unlikely(r == NULL)) { return NULL; } return r->hooks; } /** * @brief returns callback hooks * * @param cb * @return */ evhtp_hooks_t * evhtp_callback_get_hooks(evhtp_callback_t * cb) { return cb->hooks; } evhtp_callback_t * evhtp_set_cb(evhtp_t * htp, const char * path, evhtp_callback_cb cb, void * arg) { evhtp_callback_t * hcb; htp__lock_(htp); if (htp->callbacks == NULL) { if (!(htp->callbacks = htp__calloc_(sizeof(evhtp_callbacks_t), 1))) { htp__unlock_(htp); return NULL; } TAILQ_INIT(htp->callbacks); } if (!(hcb = evhtp_callback_new(path, evhtp_callback_type_hash, cb, arg))) { htp__unlock_(htp); return NULL; } if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) { evhtp_safe_free(hcb, evhtp_callback_free); htp__unlock_(htp); return NULL; } htp__unlock_(htp); return hcb; } evhtp_callback_t * evhtp_get_cb(evhtp_t * htp, const char * path) { evhtp_callback_t * callback; evhtp_assert(htp != NULL); if (evhtp_unlikely(htp->callbacks == NULL)) { return NULL; } TAILQ_FOREACH(callback, htp->callbacks, next) { if (strcmp(callback->val.path, path) == 0) { return callback; } } return NULL; } #ifndef EVHTP_DISABLE_EVTHR static void htp__thread_init_(evthr_t * thr, void * arg) { evhtp_t * htp = (evhtp_t *)arg; if (htp->thread_init_cb) { htp->thread_init_cb(htp, thr, htp->thread_cbarg); } } static void htp__thread_exit_(evthr_t * thr, void * arg) { evhtp_t * htp = (evhtp_t *)arg; if (htp->thread_exit_cb) { htp->thread_exit_cb(htp, thr, htp->thread_cbarg); } } static int htp__use_threads_(evhtp_t * htp, evhtp_thread_init_cb init_cb, evhtp_thread_exit_cb exit_cb, int nthreads, void * arg) { if (htp == NULL) { return -1; } htp->thread_cbarg = arg; htp->thread_init_cb = init_cb; htp->thread_exit_cb = exit_cb; #ifndef EVHTP_DISABLE_SSL evhtp_ssl_use_threads(); #endif if (!(htp->thr_pool = evthr_pool_wexit_new(nthreads, htp__thread_init_, htp__thread_exit_, htp))) { return -1; } evthr_pool_start(htp->thr_pool); return 0; } int evhtp_use_threads(evhtp_t * htp, evhtp_thread_init_cb init_cb, int nthreads, void * arg) { return htp__use_threads_(htp, init_cb, NULL, nthreads, arg); } int evhtp_use_threads_wexit(evhtp_t * htp, evhtp_thread_init_cb init_cb, evhtp_thread_exit_cb exit_cb, int nthreads, void * arg) { return htp__use_threads_(htp, init_cb, exit_cb, nthreads, arg); } #endif #ifndef EVHTP_DISABLE_EVTHR int evhtp_use_callback_locks(evhtp_t * htp) { if (htp == NULL) { return -1; } if (!(htp->lock = htp__malloc_(sizeof(pthread_mutex_t)))) { return -1; } return pthread_mutex_init(htp->lock, NULL); } #endif #ifndef EVHTP_DISABLE_REGEX evhtp_callback_t * evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg) { evhtp_callback_t * hcb; htp__lock_(htp); if (htp->callbacks == NULL) { if (!(htp->callbacks = htp__calloc_(sizeof(evhtp_callbacks_t), 1))) { htp__unlock_(htp); return NULL; } TAILQ_INIT(htp->callbacks); } if (!(hcb = evhtp_callback_new(pattern, evhtp_callback_type_regex, cb, arg))) { htp__unlock_(htp); return NULL; } if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) { evhtp_safe_free(hcb, evhtp_callback_free); htp__unlock_(htp); return NULL; } htp__unlock_(htp); return hcb; } #endif evhtp_callback_t * evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg) { evhtp_callback_t * hcb; htp__lock_(htp); if (htp->callbacks == NULL) { if (!(htp->callbacks = htp__calloc_(sizeof(evhtp_callbacks_t), 1))) { htp__unlock_(htp); return NULL; } TAILQ_INIT(htp->callbacks); } if (!(hcb = evhtp_callback_new(pattern, evhtp_callback_type_glob, cb, arg))) { htp__unlock_(htp); return NULL; } if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) { evhtp_safe_free(hcb, evhtp_callback_free); htp__unlock_(htp); return NULL; } htp__unlock_(htp); return hcb; } void evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg) { htp->defaults.cb = cb; htp->defaults.cbarg = arg; } void evhtp_set_pre_accept_cb(evhtp_t * htp, evhtp_pre_accept_cb cb, void * arg) { htp->defaults.pre_accept = cb; htp->defaults.pre_accept_cbarg = arg; } void evhtp_set_post_accept_cb(evhtp_t * htp, evhtp_post_accept_cb cb, void * arg) { htp->defaults.post_accept = cb; htp->defaults.post_accept_cbarg = arg; } #ifndef EVHTP_DISABLE_SSL #ifndef EVHTP_DISABLE_EVTHR int evhtp_ssl_use_threads(void) { int i; if (ssl_locks_initialized == 1) { return 0; } ssl_locks_initialized = 1; ssl_num_locks = CRYPTO_num_locks(); if ((ssl_locks = htp__calloc_(ssl_num_locks, sizeof(evhtp_mutex_t))) == NULL) { return -1; } for (i = 0; i < ssl_num_locks; i++) { pthread_mutex_init(&(ssl_locks[i]), NULL); } CRYPTO_set_id_callback(htp__ssl_get_thread_id_); CRYPTO_set_locking_callback(htp__ssl_thread_lock_); return 0; } #endif int evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) { long cache_mode; unsigned char c; if (cfg == NULL || htp == NULL || cfg->pemfile == NULL) { return -1; } #if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_library_init(); ERR_load_crypto_strings(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); #else /* unnecessary in OpenSSL 1.1.0 */ /* * if (OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, NULL) == 0) { * log_error("OPENSSL_init_ssl"); * return -1; * } * * if (OPENSSL_init_crypto( * OPENSSL_INIT_ADD_ALL_CIPHERS | * OPENSSL_INIT_ADD_ALL_DIGESTS | * OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) { * log_error("OPENSSL_init_crypto"); * return -1; * } */ #endif if (RAND_poll() != 1) { log_error("RAND_poll"); return -1; } if (RAND_bytes(&c, 1) != 1) { log_error("RAND_bytes"); return -1; } #if OPENSSL_VERSION_NUMBER < 0x10000000L STACK_OF(SSL_COMP) * comp_methods = SSL_COMP_get_compression_methods(); sk_SSL_COMP_zero(comp_methods); #endif htp->ssl_cfg = cfg; #if OPENSSL_VERSION_NUMBER < 0x10100000L htp->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); #else htp->ssl_ctx = SSL_CTX_new(TLS_server_method()); #endif evhtp_alloc_assert(htp->ssl_ctx); #if OPENSSL_VERSION_NUMBER >= 0x10000000L SSL_CTX_set_options(htp->ssl_ctx, SSL_MODE_RELEASE_BUFFERS | SSL_OP_NO_COMPRESSION); SSL_CTX_set_timeout(htp->ssl_ctx, cfg->ssl_ctx_timeout); #endif SSL_CTX_set_options(htp->ssl_ctx, cfg->ssl_opts); #ifndef OPENSSL_NO_ECDH if (cfg->named_curve != NULL) { EC_KEY * ecdh = NULL; int nid = 0; nid = OBJ_sn2nid(cfg->named_curve); if (nid == 0) { log_error("ECDH initialization failed: unknown curve %s", cfg->named_curve); } ecdh = EC_KEY_new_by_curve_name(nid); if (ecdh == NULL) { log_error("ECDH initialization failed for curve %s", cfg->named_curve); } SSL_CTX_set_tmp_ecdh(htp->ssl_ctx, ecdh); EC_KEY_free(ecdh); } #endif /* OPENSSL_NO_ECDH */ #ifndef OPENSSL_NO_DH if (cfg->dhparams != NULL) { FILE * fh; DH * dh; fh = fopen(cfg->dhparams, "r"); if (fh != NULL) { dh = PEM_read_DHparams(fh, NULL, NULL, NULL); if (dh != NULL) { SSL_CTX_set_tmp_dh(htp->ssl_ctx, dh); DH_free(dh); } else { log_error("DH initialization failed: unable to parse file %s", cfg->dhparams); } fclose(fh); } else { log_error("DH initialization failed: unable to open file %s", cfg->dhparams); } } #endif /* OPENSSL_NO_DH */ if (cfg->ciphers != NULL) { if (SSL_CTX_set_cipher_list(htp->ssl_ctx, cfg->ciphers) == 0) { log_error("set_cipher_list"); return -1; } } SSL_CTX_load_verify_locations(htp->ssl_ctx, cfg->cafile, cfg->capath); X509_STORE_set_flags(SSL_CTX_get_cert_store(htp->ssl_ctx), cfg->store_flags); SSL_CTX_set_verify(htp->ssl_ctx, cfg->verify_peer, cfg->x509_verify_cb); if (cfg->x509_chk_issued_cb != NULL) { #if OPENSSL_VERSION_NUMBER < 0x10100000L htp->ssl_ctx->cert_store->check_issued = cfg->x509_chk_issued_cb; #else X509_STORE_set_check_issued(SSL_CTX_get_cert_store(htp->ssl_ctx), cfg->x509_chk_issued_cb); #endif } if (cfg->verify_depth) { SSL_CTX_set_verify_depth(htp->ssl_ctx, cfg->verify_depth); } switch (cfg->scache_type) { case evhtp_ssl_scache_type_disabled: cache_mode = SSL_SESS_CACHE_OFF; break; default: cache_mode = SSL_SESS_CACHE_SERVER; break; } /* switch */ SSL_CTX_use_certificate_chain_file(htp->ssl_ctx, cfg->pemfile); char * const key = cfg->privfile ? cfg->privfile : cfg->pemfile; if (cfg->decrypt_cb != NULL) { EVP_PKEY * pkey = cfg->decrypt_cb(key); if (pkey == NULL) { return -1; } SSL_CTX_use_PrivateKey(htp->ssl_ctx, pkey); /*cleanup */ EVP_PKEY_free(pkey); } else { SSL_CTX_use_PrivateKey_file(htp->ssl_ctx, key, SSL_FILETYPE_PEM); } SSL_CTX_set_session_id_context(htp->ssl_ctx, (void *)&session_id_context, sizeof(session_id_context)); SSL_CTX_set_app_data(htp->ssl_ctx, htp); SSL_CTX_set_session_cache_mode(htp->ssl_ctx, cache_mode); if (cache_mode != SSL_SESS_CACHE_OFF) { SSL_CTX_sess_set_cache_size(htp->ssl_ctx, cfg->scache_size ? cfg->scache_size : 1024); if (cfg->scache_type == evhtp_ssl_scache_type_builtin || cfg->scache_type == evhtp_ssl_scache_type_user) { SSL_CTX_sess_set_new_cb(htp->ssl_ctx, htp__ssl_add_scache_ent_); SSL_CTX_sess_set_get_cb(htp->ssl_ctx, htp__ssl_get_scache_ent_); SSL_CTX_sess_set_remove_cb(htp->ssl_ctx, htp__ssl_delete_scache_ent_); if (cfg->scache_init) { cfg->args = (cfg->scache_init)(htp); } } } return 0; } /* evhtp_use_ssl */ #endif struct bufferevent * evhtp_connection_get_bev(evhtp_connection_t * connection) { return connection->bev; } struct bufferevent * evhtp_connection_take_ownership(evhtp_connection_t * connection) { struct bufferevent * bev = evhtp_connection_get_bev(connection); if (connection->hooks) { evhtp_unset_all_hooks(&connection->hooks); } if (connection->request && connection->request->hooks) { evhtp_unset_all_hooks(&connection->request->hooks); } evhtp_connection_set_bev(connection, NULL); /* relinquish ownership of this connection, unset * the ownership flag. */ HTP_FLAG_OFF(connection, EVHTP_CONN_FLAG_OWNER); bufferevent_disable(bev, EV_READ); bufferevent_setcb(bev, NULL, NULL, NULL, NULL); return bev; } struct bufferevent * evhtp_request_get_bev(evhtp_request_t * request) { return evhtp_connection_get_bev(request->conn); } struct bufferevent * evhtp_request_take_ownership(evhtp_request_t * request) { return evhtp_connection_take_ownership(request->conn); } void evhtp_connection_set_bev(evhtp_connection_t * conn, struct bufferevent * bev) { conn->bev = bev; } void evhtp_request_set_bev(evhtp_request_t * request, struct bufferevent * bev) { evhtp_connection_set_bev(request->conn, bev); } void evhtp_request_set_keepalive(evhtp_request_t * request, int val) { if (val) { HTP_FLAG_ON(request, EVHTP_REQ_FLAG_KEEPALIVE); } } evhtp_connection_t * evhtp_request_get_connection(evhtp_request_t * request) { return request->conn; } evhtp_proto evhtp_request_get_proto(evhtp_request_t * request) { return request->proto; } inline void evhtp_connection_set_timeouts(evhtp_connection_t * c, const struct timeval * rtimeo, const struct timeval * wtimeo) { if (evhtp_unlikely(c == NULL)) { return; } bufferevent_set_timeouts(c->bev, rtimeo, wtimeo); } void evhtp_connection_set_max_body_size(evhtp_connection_t * c, uint64_t len) { if (len == 0) { c->max_body_size = c->htp->max_body_size; } else { c->max_body_size = len; } } void evhtp_request_set_max_body_size(evhtp_request_t * req, uint64_t len) { evhtp_connection_set_max_body_size(req->conn, len); } void evhtp_connection_free(evhtp_connection_t * connection) { if (evhtp_unlikely(connection == NULL)) { return; } htp__hook_connection_fini_(connection); evhtp_safe_free(connection->request, htp__request_free_); evhtp_safe_free(connection->parser, htp__free_); evhtp_safe_free(connection->hooks, htp__free_); evhtp_safe_free(connection->saddr, htp__free_); evhtp_safe_free(connection->scratch_buf, evbuffer_free); if (connection->resume_ev) { evhtp_safe_free(connection->resume_ev, event_free); } if (connection->bev) { #ifdef LIBEVENT_HAS_SHUTDOWN bufferevent_shutdown(connection->bev, htp__shutdown_eventcb_); #else #ifndef EVHTP_DISABLE_SSL if (connection->ssl != NULL) { SSL_set_shutdown(connection->ssl, SSL_RECEIVED_SHUTDOWN); SSL_shutdown(connection->ssl); } #endif bufferevent_free(connection->bev); #endif } evhtp_safe_free(connection, htp__free_); } /* evhtp_connection_free */ void evhtp_request_free(evhtp_request_t * request) { htp__request_free_(request); } void evhtp_set_timeouts(evhtp_t * htp, const struct timeval * r_timeo, const struct timeval * w_timeo) { if (r_timeo != NULL) { htp->recv_timeo = *r_timeo; } if (w_timeo != NULL) { htp->send_timeo = *w_timeo; } } void evhtp_set_max_keepalive_requests(evhtp_t * htp, uint64_t num) { htp->max_keepalive_requests = num; } void evhtp_set_bev_flags(evhtp_t * htp, int flags) { htp->bev_flags = flags; } void evhtp_set_max_body_size(evhtp_t * htp, uint64_t len) { htp->max_body_size = len; } void evhtp_disable_100_continue(evhtp_t * htp) { HTP_FLAG_OFF(htp, EVHTP_FLAG_ENABLE_100_CONT); } void evhtp_set_parser_flags(evhtp_t * htp, int flags) { htp->parser_flags = flags; } #define HTP_FLAG_FNGEN(NAME, TYPE) void \ evhtp ## NAME ## _enable_flag(TYPE v, int flag) { \ HTP_FLAG_ON(v, flag); \ } \ \ void \ evhtp ## NAME ## _disable_flag(TYPE v, int flag) { \ HTP_FLAG_OFF(v, flag); \ } \ \ int \ evhtp ## NAME ## _get_flags(TYPE v) { \ if (v) \ { \ return v->flags; \ } \ return -1; \ } HTP_FLAG_FNGEN(, evhtp_t *); HTP_FLAG_FNGEN(_connection, evhtp_connection_t *); HTP_FLAG_FNGEN(_request, evhtp_request_t *); int evhtp_add_alias(evhtp_t * evhtp, const char * name) { evhtp_alias_t * alias; if (evhtp_unlikely(evhtp == NULL || name == NULL)) { return -1; } if (!(alias = htp__calloc_(sizeof(evhtp_alias_t), 1))) { return -1; } log_debug("Adding %s to aliases", name); alias->alias = htp__strdup_(name); evhtp_alloc_assert(alias->alias); TAILQ_INSERT_TAIL(&evhtp->aliases, alias, next); return 0; } int evhtp_add_aliases(evhtp_t * htp, const char * name, ...) { va_list argp; size_t len; if (evhtp_add_alias(htp, name) == -1) { return -1; } va_start(argp, name); { const char * p; while ((p = va_arg(argp, const char *)) != NULL) { if (evhtp_add_alias(htp, p) == -1) { log_error("Unable to add %s alias", p); return -1; } } } va_end(argp); return 0; } int evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * vhost) { if (evhtp == NULL || name == NULL || vhost == NULL) { return -1; } if (TAILQ_FIRST(&vhost->vhosts) != NULL) { /* vhosts cannot have secondary vhosts defined */ return -1; } if (!(vhost->server_name = htp__strdup_(name))) { return -1; } /* set the parent of this vhost so when the request has been completely * serviced, the vhost can be reset to the original evhtp structure. * * This allows for a keep-alive connection to make multiple requests with * different Host: values. */ vhost->parent = evhtp; /* inherit various flags from the parent evhtp structure */ vhost->bev_flags = evhtp->bev_flags; vhost->max_body_size = evhtp->max_body_size; vhost->max_keepalive_requests = evhtp->max_keepalive_requests; vhost->recv_timeo = evhtp->recv_timeo; vhost->send_timeo = evhtp->send_timeo; TAILQ_INSERT_TAIL(&evhtp->vhosts, vhost, next_vhost); return 0; } /** * @brief Allocates new evhtp_t structure * * @param [OUT] out - double ptr to evhtp_t structure. * @param [IN] evbase - event_base structure * @param [IN] arg - anonymous argument * * @return 0 on success, -1 on failure */ static int evhtp__new_(evhtp_t ** out, struct event_base * evbase, void * arg) { evhtp_t * htp; if (evhtp_unlikely(evbase == NULL)) { return -1; } *out = NULL; if ((htp = htp__calloc_(1, sizeof(*htp))) == NULL) { return -1; } htp->arg = arg; htp->evbase = evbase; htp->flags = EVHTP_FLAG_DEFAULTS; htp->bev_flags = BEV_OPT_CLOSE_ON_FREE; /* default to lenient argument parsing */ htp->parser_flags = EVHTP_PARSE_QUERY_FLAG_DEFAULT; TAILQ_INIT(&htp->vhosts); TAILQ_INIT(&htp->aliases); /* note that we pass the htp context to the callback, * not the user supplied arguments. That is stored * within the context itself. */ evhtp_set_gencb(htp, htp__default_request_cb_, (void *)htp); *out = htp; return 0; } evhtp_t * evhtp_new(struct event_base * evbase, void * arg) { evhtp_t * htp; if (evhtp__new_(&htp, evbase, arg) == -1) { return NULL; } return htp; } void evhtp_free(evhtp_t * evhtp) { evhtp_alias_t * evhtp_alias, * tmp; if (evhtp == NULL) { return; } #ifndef EVHTP_DISABLE_EVTHR if (evhtp->thr_pool) { evthr_pool_stop(evhtp->thr_pool); evthr_pool_free(evhtp->thr_pool); } #endif #ifndef EVHTP_DISABLE_SSL if (evhtp->ssl_ctx) { evhtp_safe_free(evhtp->ssl_ctx, SSL_CTX_free); } #endif if (evhtp->server_name) { evhtp_safe_free(evhtp->server_name, htp__free_); } if (evhtp->callbacks) { evhtp_safe_free(evhtp->callbacks, evhtp_callbacks_free); } TAILQ_FOREACH_SAFE(evhtp_alias, &evhtp->aliases, next, tmp) { if (evhtp_alias->alias != NULL) { evhtp_safe_free(evhtp_alias->alias, htp__free_); } TAILQ_REMOVE(&evhtp->aliases, evhtp_alias, next); evhtp_safe_free(evhtp_alias, htp__free_); } evhtp_safe_free(evhtp, htp__free_); } /* evhtp_free */ /***************************************************************** * client request functions * *****************************************************************/ evhtp_connection_t * evhtp_connection_new(struct event_base * evbase, const char * addr, uint16_t port) { return evhtp_connection_new_dns(evbase, NULL, addr, port); } evhtp_connection_t * evhtp_connection_new_dns(struct event_base * evbase, struct evdns_base * dns_base, const char * addr, uint16_t port) { evhtp_connection_t * conn; int err; log_debug("Enter"); evhtp_assert(evbase != NULL); if (!(conn = htp__connection_new_(NULL, -1, evhtp_type_client))) { return NULL; } conn->evbase = evbase; conn->bev = bufferevent_socket_new(evbase, -1, BEV_OPT_CLOSE_ON_FREE); if (conn->bev == NULL) { evhtp_connection_free(conn); return NULL; } bufferevent_enable(conn->bev, EV_READ); bufferevent_setcb(conn->bev, NULL, NULL, htp__connection_eventcb_, conn); if (dns_base != NULL) { err = bufferevent_socket_connect_hostname(conn->bev, dns_base, AF_UNSPEC, addr, port); } else { struct sockaddr_in sin4; struct sockaddr_in6 sin6; struct sockaddr * sin; int salen; if (inet_pton(AF_INET, addr, &sin4.sin_addr)) { sin4.sin_family = AF_INET; sin4.sin_port = htons(port); sin = (struct sockaddr *)&sin4; salen = sizeof(sin4); } else if (inet_pton(AF_INET6, addr, &sin6.sin6_addr)) { sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(port); sin = (struct sockaddr *)&sin6; salen = sizeof(sin6); } else { /* Not a valid IP. */ evhtp_connection_free(conn); return NULL; } err = bufferevent_socket_connect(conn->bev, sin, salen); } /* not needed since any of the bufferevent errors will go straight to * the eventcb */ if (err) { return NULL; } return conn; } /* evhtp_connection_new_dns */ #ifndef EVHTP_DISABLE_SSL #define ssl_sk_new_ bufferevent_openssl_socket_new #define ssl_sk_connect_ bufferevent_socket_connect evhtp_connection_t * evhtp_connection_ssl_new(struct event_base * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t * ctx) { evhtp_connection_t * conn; struct sockaddr_in sin; const char * errstr; if (evbase == NULL) { return NULL; } if (!(conn = htp__connection_new_(NULL, -1, evhtp_type_client))) { return NULL; } conn->evbase = evbase; errstr = NULL; do { if ((conn->ssl = SSL_new(ctx)) == NULL) { errstr = "unable to allocate SSL context"; break; } if ((conn->bev = ssl_sk_new_(evbase, -1, conn->ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE)) == NULL) { errstr = "unable to allocate bev context"; break; } if (bufferevent_enable(conn->bev, EV_READ) == -1) { errstr = "unable to enable reading"; break; } bufferevent_setcb(conn->bev, NULL, NULL, htp__connection_eventcb_, conn); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(addr); sin.sin_port = htons(port); if (ssl_sk_connect_(conn->bev, (struct sockaddr *)&sin, sizeof(sin)) == -1) { errstr = "sk_connect_ failure"; break; } } while (0); if (errstr != NULL) { log_error("%s", errstr); evhtp_safe_free(conn, evhtp_connection_free); return NULL; } return conn; } /* evhtp_connection_ssl_new */ #endif evhtp_request_t * evhtp_request_new(evhtp_callback_cb cb, void * arg) { evhtp_request_t * r; r = htp__request_new_(NULL); evhtp_alloc_assert(r); r->cb = cb; r->cbarg = arg; r->proto = EVHTP_PROTO_11; return r; } int evhtp_make_request(evhtp_connection_t * c, evhtp_request_t * r, htp_method meth, const char * uri) { struct evbuffer * obuf; char * proto; obuf = bufferevent_get_output(c->bev); r->conn = c; c->request = r; switch (r->proto) { case EVHTP_PROTO_10: proto = "1.0"; break; case EVHTP_PROTO_11: default: proto = "1.1"; break; } evbuffer_add_printf(obuf, "%s %s HTTP/%s\r\n", htparser_get_methodstr_m(meth), uri, proto); evhtp_headers_for_each(r->headers_out, htp__create_headers_, obuf); evbuffer_add_reference(obuf, "\r\n", 2, NULL, NULL); if (evbuffer_get_length(r->buffer_out)) { evbuffer_add_buffer(obuf, r->buffer_out); } return 0; } unsigned int evhtp_request_status(evhtp_request_t * r) { return htparser_get_status(r->conn->parser); } libevhtp-1.2.16/evhtp.pc.in000066400000000000000000000004451322723041700155010ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ libdir=@LIB_INSTALL_DIR@ includedir=@INCLUDE_INSTALL_DIR@/evhtp Name: libevhtp Description: A more flexible replacement for libevent's httpd API Version: @PROJECT_VERSION@ Libs: -L${libdir} -levhtp Libs.private: @LIBEVHTP_EXTERNAL_LIBS@ Cflags: -I${includedir} libevhtp-1.2.16/examples/000077500000000000000000000000001322723041700152355ustar00rootroot00000000000000libevhtp-1.2.16/examples/CMakeLists.txt000066400000000000000000000044611322723041700200020ustar00rootroot00000000000000add_custom_target(examples) add_executable(test_extensive EXCLUDE_FROM_ALL test.c) add_executable(test_basic EXCLUDE_FROM_ALL test_basic.c) add_executable(test_vhost EXCLUDE_FROM_ALL test_vhost.c) add_executable(test_client EXCLUDE_FROM_ALL test_client.c) add_executable(test_query EXCLUDE_FROM_ALL test_query.c) add_executable(test_perf EXCLUDE_FROM_ALL test_perf.c) add_executable(example_vhost EXCLUDE_FROM_ALL example_vhost.c) add_executable(example_pause EXCLUDE_FROM_ALL example_pause.c) add_executable(example_chunked EXCLUDE_FROM_ALL example_chunked.c) if (NOT EVHTP_DISABLE_EVTHR) add_executable(test_proxy EXCLUDE_FROM_ALL test_proxy.c) target_link_libraries(test_proxy evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) add_dependencies(examples test_proxy) endif() target_link_libraries(test_extensive evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_basic evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_vhost evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_client evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_query evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_perf evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(example_vhost evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(example_pause evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(example_chunked evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) if (NOT EVHTP_DISABLE_SSL) file (COPY https/etc/ca.cnf https/etc/client1.cnf https/etc/client2.cnf https/etc/server.cnf DESTINATION https/etc/) configure_file(https/bin/generate.sh.in https/bin/generate.sh @ONLY) add_executable(example_https_server EXCLUDE_FROM_ALL https/example_https_server.c) target_link_libraries(example_https_server evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) add_executable(example_https_client EXCLUDE_FROM_ALL https/example_https_client.c) target_link_libraries(example_https_client evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) add_dependencies(examples example_https_server example_https_client) endif() add_dependencies(examples example_chunked example_pause example_vhost test_extensive test_basic test_vhost test_client test_query test_perf) libevhtp-1.2.16/examples/eutils.h000066400000000000000000000017421322723041700167170ustar00rootroot00000000000000#pragma once static void * mm__dup_(const void * src, size_t size) { void * mem = malloc(size); return mem ? memcpy(mem, src, size) : NULL; } #define mm__alloc_(type, ...) \ (type *)mm__dup_((type[]) {__VA_ARGS__ }, sizeof(type)) #define bind__sock_port0_(HTP) ({ \ struct sockaddr_in sin; \ socklen_t len = len = sizeof(struct sockaddr); \ uint16_t port; \ \ evhtp_bind_socket(HTP, "127.0.0.1", 0, 128); \ \ getsockname( \ evconnlistener_get_fd(HTP->server), \ (struct sockaddr *)&sin, &len); \ \ port = ntohs(sin.sin_port); \ port; \ }) libevhtp-1.2.16/examples/example_chunked.c000066400000000000000000000104451322723041700205410ustar00rootroot00000000000000#include #include #include #include #include #include "../log.h" #include "./eutils.h" #include "internal.h" #include "evhtp/evhtp.h" struct reply_ { evhtp_request_t * request; FILE * file_desc; struct evbuffer * buffer; }; /* This function is called each time the client has been sent * all outstanding data. We use this to send the next part of * the file in a chunk at 128 byte increments. * * When there is no more data to be read from the file, this * will send the final chunked reply and free our struct reply_. */ static evhtp_res http__send_chunk_(evhtp_connection_t * conn, void * arg) { struct reply_ * reply = (struct reply_ *)arg; char buf[128]; size_t bytes_read; /* try to read 128 bytes from the file pointer */ bytes_read = fread(buf, 1, sizeof(buf), reply->file_desc); log_info("Sending %zu bytes", bytes_read); if (bytes_read > 0) { /* add our data we read from the file into our reply buffer */ evbuffer_add(reply->buffer, buf, bytes_read); /* send the reply buffer as a http chunked message */ evhtp_send_reply_chunk(reply->request, reply->buffer); /* we can now drain our reply buffer as to not be a resource * hog. */ evbuffer_drain(reply->buffer, bytes_read); } /* check if we have read everything from the file */ if (feof(reply->file_desc)) { log_info("Sending last chunk"); /* now that we have read everything from the file, we must * first unset our on_write hook, then inform evhtp to send * this message as the final chunk. */ evhtp_connection_unset_hook(conn, evhtp_hook_on_write); evhtp_send_reply_chunk_end(reply->request); /* we can now free up our little reply_ structure */ { fclose(reply->file_desc); evbuffer_free(reply->buffer); free(reply); } } return EVHTP_RES_OK; } /* This function is called when a request has been fully received. * * This function assumes the `arg` value is the filename that was * passed via `evhtp_set_gencb` in `main`. * * 1. open the file * 2. create a `struct reply_` * 3. create an evbuffer that we will write into. * 4. set a hook to call the function `http__send_chunk_` each * time all data has been sent from the previous write call. * 5. start the chunked stream via `evhtp_send_reply_chunk_start` */ static void http__callback_(evhtp_request_t * req, void * arg) { const char * filename = arg; FILE * file_desc; struct reply_ * reply; evhtp_assert(arg != NULL); /* open up the file as passed to us via evhtp_set_gencb */ file_desc = fopen((const char *)arg, "r"); evhtp_assert(file_desc != NULL); /* create our little internal reply structure which will * be used by `http__send_chunk_` */ reply = mm__alloc_(struct reply_, { req, file_desc, evbuffer_new() }); /* here we set a connection hook of the type `evhtp_hook_on_write` * * this will execute the function `http__send_chunk_` each time * all data has been written to the client. */ evhtp_connection_set_hook(req->conn, evhtp_hook_on_write, http__send_chunk_, reply); /* we do not have to start sending data from the file from here - * this function will write data to the client, thus when finished, * will call our `http__send_chunk_` callback. */ evhtp_send_reply_chunk_start(req, EVHTP_RES_OK); } int main(int argc, char ** argv) { evhtp_t * htp; struct event_base * evbase; if (argc < 2) { printf("Usage: %s \n", argv[0]); exit(EXIT_FAILURE); } evbase = event_base_new(); evhtp_alloc_assert(evbase); htp = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp); /* here we set our default request response callback, the argument * that is passed will be the filename we want to stream to the * client in chunked form. */ evhtp_set_gencb(htp, http__callback_, strdup(argv[1])); log_info("curl http://127.0.0.1:%d/", bind__sock_port0_(htp)); event_base_loop(evbase, 0); return 0; } libevhtp-1.2.16/examples/example_pause.c000066400000000000000000000064041322723041700202350ustar00rootroot00000000000000/* * Quick example of how to pause a request, and in this case, simply * set a timer to emit the response 10 seconds later. * * This is a good way to long running tasks before responding (thus * not blocking any other processing). */ #include #include #include #include #include #include "../log.h" #include "internal.h" #include "evhtp/evhtp.h" #include "./eutils.h" struct paused_request_ { struct event * _timeoutev; struct timeval _timeout; evhtp_request_t * _request; }; /* once 10 seconds has passed, this function is called, it will * resume the request, and send the final response back to the * client. */ static void http_resume__callback_(int sock, short events, void * arg) { struct paused_request_ * preq; evhtp_request_t * req; evhtp_assert(arg != NULL); preq = (struct paused_request_ *)arg; req = preq->_request; evhtp_assert(req != NULL); event_free(preq->_timeoutev); free(preq); /* add the current time to our output buffer to the client */ evbuffer_add_printf(req->buffer_out, "time end %ld\n", time(NULL)); /* inform the evhtp API to resume this connection request */ evhtp_request_resume(req); /* finally send the response to the client, YAY! */ evhtp_send_reply(req, EVHTP_RES_OK); } /* this is our default callback, it is the one who sets up the evtimer * that triggers the response after 10 seconds. */ static void http_pause__callback_(evhtp_request_t * req, void * arg) { struct timeval * tv = (struct timeval *)arg; struct paused_request_ * preq; /* allocate a little structure that holds our evtimer and the * pending request, se the timeout to 10 seconds. */ preq = malloc(sizeof(*preq)); evhtp_alloc_assert(preq); preq->_request = req; preq->_timeout.tv_sec = tv->tv_sec; preq->_timeout.tv_usec = tv->tv_usec; /* when 10 seconds is up, the function http_resume__callback_ will * be called, this function will actually send the response. */ preq->_timeoutev = evtimer_new(req->htp->evbase, http_resume__callback_, preq); evhtp_alloc_assert(preq->_timeoutev); /* just for debugging, add the time the request was first seen */ evbuffer_add_printf(req->buffer_out, "time start %ld\n", time(NULL)); /* add the timer to the event loop */ evtimer_add(preq->_timeoutev, &preq->_timeout); /* notify the evhtp API to "pause" this request (meaning it will no * longer do any work on this connection until it is "resumed". */ evhtp_request_pause(req); } int main(int argc, char ** argv) { evhtp_t * htp; struct event_base * evbase; struct timeval timeo = { 10, 0 }; evbase = event_base_new(); evhtp_alloc_assert(evbase); htp = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp); /* we just set the default callback for any requests to * the function that pauses the session, sets a timer, * and 10 seconds later, sends the response. */ evhtp_set_gencb(htp, http_pause__callback_, &timeo); log_info("response delayed for 10s: " "curl http://127.0.0.1:%d/", bind__sock_port0_(htp)); return event_base_loop(evbase, 0); } libevhtp-1.2.16/examples/example_vhost.c000066400000000000000000000104701322723041700202610ustar00rootroot00000000000000#include #include #include #include #include #include "internal.h" #include "../log.h" #include "evhtp/evhtp.h" #include "./eutils.h" #define make_response(cb) do { \ evbuffer_add_printf(req->buffer_out, \ "%s = host:%s, arg:%s\n", cb, \ evhtp_header_find(req->headers_in, "Host"), \ (char *)arg); \ } while (0) static void vhost_1__callback_(evhtp_request_t * req, void * arg) { /* these should be our callbacks for our evhtp.io hosts */ make_response("vhost_1__callback_"); evhtp_send_reply(req, EVHTP_RES_OK); } static void vhost_2__callback_(evhtp_request_t * req, void * arg) { /* these should be our callbacks for our google hosts */ make_response("vhost_2__callback_"); evhtp_send_reply(req, EVHTP_RES_OK); } int main(int argc, char ** argv) { struct event_base * evbase; evhtp_t * htp; evhtp_t * htp_vhost_1; evhtp_t * htp_vhost_2; evbase = event_base_new(); evhtp_alloc_assert(evbase); /* allocate our main evhtp structure which vhosts are * nested under */ htp = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp); /* create a evhtp structure for vhost_1 specific hostnames, * this will match hostnames for 'evhtp.io' * */ htp_vhost_1 = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp_vhost_1); /* running a get on /vhost for htp_vhost_1 will be different * from htp_vhost_2 due to the hostname we set below. */ evhtp_set_cb(htp_vhost_1, "/vhost", vhost_1__callback_, "evhtp.io domains"); /* create a evhtp structure for vhost_2 specific hostnames, * this will match hostnames for 'google.com' */ htp_vhost_2 = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp_vhost_2); /* running a get on /vhost for http_vhost_2 will be different * from the http_vhost_1 due to the hostname we set below. */ evhtp_set_cb(htp_vhost_2, "/vhost", vhost_2__callback_, "google.com domains"); /* if Host: evhtp.io is present, the callbacks fro htp_vhost_1 are * used. We do this by adding the vhost_1 evhtp to the main htp ctx. */ evhtp_add_vhost(htp, "evhtp.io", htp_vhost_1); /* now lets set some virtual host aliases to evhtp.io */ evhtp_add_aliases(htp_vhost_1, "www.evhtp.io", "web.evhtp.io", NULL); /* If Host: google.com is present, the callbacks for htp_vhost_2 are * used instead. This must be attached to the main htp context. */ evhtp_add_vhost(htp, "google.com", htp_vhost_2); /* now add some virtual host aliases for google.com */ evhtp_add_aliases(htp_vhost_2, "www.google.com", "web.google.com", "inbox.google.com", NULL); /* we can also append a single alias to vhost_2 like this */ evhtp_add_alias(htp_vhost_2, "gmail.google.com"); { uint16_t port = bind__sock_port0_(htp); log_info("[[ try the following commands and you should see 'evhtp.io domains' ]]"); log_info("====================================================================="); log_info("curl -H'Host: evhtp.io' http://127.0.0.1:%d/vhost", port); log_info("curl -H'Host: www.evhtp.io' http://127.0.0.1:%d/vhost", port); log_info("curl -H'Host: web.evhtp.io' http://127.0.0.1:%d/vhost", port); log_info("========================================================================"); log_info("[[ try the following commands and you should see 'google.com domains' ]]"); log_info("========================================================================"); log_info("curl -H'Host: google.com' http://127.0.0.1:%d/vhost", port); log_info("curl -H'Host: www.google.com' http://127.0.0.1:%d/vhost", port); log_info("curl -H'Host: web.google.com' http://127.0.0.1:%d/vhost", port); log_info("curl -H'Host: inbox.google.com' http://127.0.0.1:%d/vhost", port); log_info("curl -H'Host: gmail.google.com' http://127.0.0.1:%d/vhost", port); } return event_base_loop(evbase, 0); } /* main */ libevhtp-1.2.16/examples/https/000077500000000000000000000000001322723041700163775ustar00rootroot00000000000000libevhtp-1.2.16/examples/https/README.md000066400000000000000000000070141322723041700176600ustar00rootroot00000000000000After running `make examples`, if SSL is enabled, you can quickly test HTTPS, with optional client-based certificate authentication using the following process within the build directory: ``` # do all the stupid ssl generation ./examples/https/bin/generate.sh # Test without client auth # Run the server ./examples/example_https_server \ -cert examples/https/server-crt.pem \ -key examples/https/server-key.pem \ -verify-client off # Make a request curl -vk https://localhost:4443/ # Test WITH client auth ./examples/example_https_server \ -cert examples/https/server-crt.pem \ -key examples/https/server-key.pem \ -ca examples/https/ca-crt.pem \ -verify-client on \ -verify-depth 2 # Make a request with the client key curl -kv \ --key examples/https/client1-key.pem \ --cert examples/https/client1-crt.pem \ https://localhost:4443/ ``` The output (with client-certs) should look like: ``` < HTTP/1.1 200 OK < X-SSL-Subject: /C=US/ST=MA/L=Boston/O=Critical Stack/OU=evhtp/CN=client1/emailAddress=nate@cl0d.com < X-SSL-Issuer: /C=US/ST=MA/L=Boston/O=Critical Stack/OU=evhtp/CN=ca/emailAddress=nate@cl0d.com < X-SSL-Notbefore: Dec 7 16:10:34 2017 GMT < X-SSL-Notafter: Sep 1 16:10:34 2020 GMT < X-SSL-Serial: 57459A54BD08848C6D1546C2733EAD8A03553670 < X-SSL-Cipher: ECDHE-RSA-AES256-GCM-SHA384 < X-SSL-Sha1: 7A:68:47:CD:79:18:FF:DA:65:BC:67:6B:C2:5D:F3:66:9A:4A:64:7A < X-SSL-Certificate: -----BEGIN CERTIFICATE----- < MIIFkDCCA3igAwIBAgIUV0WaVL0IhIxtFUbCcz6tigNVNnAwDQYJKoZIhvcNAQEL < BQAwfzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1BMQ8wDQYDVQQHDAZCb3N0b24x < FzAVBgNVBAoMDkNyaXRpY2FsIFN0YWNrMQ4wDAYDVQQLDAVldmh0cDELMAkGA1UE < AwwCY2ExHDAaBgkqhkiG9w0BCQEWDW5hdGVAY2wwZC5jb20wHhcNMTcxMjA3MTYx < MDM0WhcNMjAwOTAxMTYxMDM0WjCBhDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1B < MQ8wDQYDVQQHDAZCb3N0b24xFzAVBgNVBAoMDkNyaXRpY2FsIFN0YWNrMQ4wDAYD < VQQLDAVldmh0cDEQMA4GA1UEAwwHY2xpZW50MTEcMBoGCSqGSIb3DQEJARYNbmF0 < ZUBjbDBkLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALVoTyUm < 62PqJ9RHkNewV+0Dn6AvTVYXQRIejORB75e1OklAp3LGw+Nlc1iP5/MjKzTtMpxk < kLDTDDhiX1mC2j9BDYOC6gpWEVosyU+fXaQvCxWKy4BASPUk7toLwgHxv855TTjV < 2pe6VtAsImCT6sUGrKDnywAFvsBriXnzbTllm4gl7oPi8TrVZhk475JjEKgGKzsS < wtpbxNUqiTXe5lQ/jU6oCCMWG1VWgPLLTIZElhp/TPqLO976DutPXuCBy56NoPMy < DRm7YarhmG1vQNFdeJmC8/xdnKCbQpVhR9sF13tNIc+9QlTKNwzn375MK9e+xjXO < nc8RuqRLeJcMrs5bx5Mtd28F+yNA0riGGtp72vse95bG0PoVUhAqzog4iceHtt6M < 4jyyBhICHYKhkrAYoy3lQdfckiiu5GjZ4rPAq+PP4XPgcXYeqxph6OA+IJwUcWjK < KjfQHvKJbIfo1ILQ4wzGxJ2KAE7F8CBrgokhYmshOMpDx6C4RPwifbtN2GcJN3Vc < kaKGwE72PExFcCLKXwJvXTz+4P87JywCCtYXbUXlgn6rMe4JSRn1NZF00nkeV5bD < AwCCoqSR5Qg9VUyhZKMF3zyQjHKS07SRDyl2vKLFUxnIu+6y4FxvnQqQOdDoz0BG < Uf/s2KNRWK3w3i7hDz0mAQpXyeqmGilT4NZhAgMBAAEwDQYJKoZIhvcNAQELBQAD < ggIBAIypf+0b+5xDRZ4IkcnlbUemZ0xt14UIw4N1Dr2kqp94gu4Z1nkLvYpLg61W < sy3vJLDKc1kSPZG5sPEj/W9zophaSQzL8P/yLHQ3psk2+ie/XDmpmvMsvsVExgut < lOExwMVfp+dIJ1cVfK5i8oMIE0IBbtdAIE/tzV+zzHpvAdA9KDcydW4oF+FLRLmQ < +qfKnK1BkxWqQayNmsbVN63ao8i/4OKD5VtKGPC5RdsEURIDc579lFKACpUnQGaJ < EKX/dKNiqoJSqOEDSsCN6jSJ7uTr5do+7xydqOcTQ+gI3FQsC1NjseqRRU0Q4HVL < 95crEmqxlOxOjcrQK6U36HyKfn7EJ1B6/SJM8U9abOKBRUQjgLlrC6GaA/rToHmX < mlkqw2nKTnZhvIGmi0UjwtOD8rQnGahnq+jwoQV6Ag2YbSfeygvajJvdLBjEBYm+ < 5F6nQgv3JR9iJoS9AxcCEURH7jIAfdbYT6RBT3VARZyPcPtLSMFLLcXh0Z/Egifi < f+xTIL7mCgdW2Jp5s8cNjhrWk6dJVaXwwA6MNSfDeWeu7uHRm3Ir0Jwoe5I2pENm < mKueI6EhIKc6tdQWS6t+ZM0IJsVvhh4s0FqeUFYCP7RxG+P4u5wZxHdjbfUUJ8zA < xHMrDvO8p6dwRUDAPkOqCPpdGmBky/ukBXNi2u0OOJ+wUgoA < -----END CERTIFICATE----- < Content-Length: 0 < Content-Type: text/plain < ``` libevhtp-1.2.16/examples/https/bin/000077500000000000000000000000001322723041700171475ustar00rootroot00000000000000libevhtp-1.2.16/examples/https/bin/generate.sh.in000077500000000000000000000037571322723041700217210ustar00rootroot00000000000000#!/usr/bin/env bash CONFIG_DIR="@PROJECT_BINARY_DIR@/examples/https" # Create new CA openssl req -new -x509 -days 9999 \ -config "$CONFIG_DIR/etc/ca.cnf" \ -keyout "$CONFIG_DIR/ca-key.pem" \ -out "$CONFIG_DIR/ca-crt.pem" # Generate private key for server openssl genrsa -out "$CONFIG_DIR/server-key.pem" 4096 # Generate cert signing request openssl req -new \ -config "$CONFIG_DIR/etc/server.cnf" \ -key "$CONFIG_DIR/server-key.pem" \ -out "$CONFIG_DIR/server-csr.pem" # Sign the request openssl x509 -req \ -extfile "$CONFIG_DIR/etc/server.cnf" \ -days 999 \ -passin "pass:password" \ -in "$CONFIG_DIR/server-csr.pem" \ -CA "$CONFIG_DIR/ca-crt.pem" \ -CAkey "$CONFIG_DIR/ca-key.pem" \ -CAcreateserial \ -out "$CONFIG_DIR/server-crt.pem" # Generate a few client certs openssl genrsa -out "$CONFIG_DIR/client1-key.pem" 4096 openssl genrsa -out "$CONFIG_DIR/client2-key.pem" 4096 # create two cert sign requests openssl req -new -config "$CONFIG_DIR/etc/client1.cnf" -key $CONFIG_DIR/client1-key.pem -out $CONFIG_DIR/client1-csr.pem openssl req -new -config $CONFIG_DIR/etc/client2.cnf -key $CONFIG_DIR/client2-key.pem -out $CONFIG_DIR/client2-csr.pem # sign the above client certs openssl x509 -req \ -extfile $CONFIG_DIR/etc/client1.cnf \ -days 999 \ -passin "pass:password" \ -in $CONFIG_DIR/client1-csr.pem \ -CA $CONFIG_DIR/ca-crt.pem \ -CAkey $CONFIG_DIR/ca-key.pem \ -CAcreateserial \ -out $CONFIG_DIR/client1-crt.pem openssl x509 -req \ -extfile $CONFIG_DIR/etc/client2.cnf \ -days 999 \ -passin "pass:password" \ -in $CONFIG_DIR/client2-csr.pem \ -CA $CONFIG_DIR/ca-crt.pem \ -CAkey $CONFIG_DIR/ca-key.pem \ -CAcreateserial \ -out $CONFIG_DIR/client2-crt.pem libevhtp-1.2.16/examples/https/etc/000077500000000000000000000000001322723041700171525ustar00rootroot00000000000000libevhtp-1.2.16/examples/https/etc/ca.cnf000066400000000000000000000013231322723041700202240ustar00rootroot00000000000000[ ca ] default_ca = CA_default [ CA_default ] serial = ca-serial crl = ca-crl.pem database = ca-database.txt name_opt = CA_default cert_opt = CA_default default_crl_days = 9999 default_md = md5 [ req ] default_bits = 4096 days = 9999 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no output_password = password [ req_distinguished_name ] C = US ST = MA L = Boston O = Critical Stack OU = evhtp CN = ca emailAddress = nate@cl0d.com [ req_attributes ] challengePassword = test libevhtp-1.2.16/examples/https/etc/client1.cnf000066400000000000000000000012421322723041700212000ustar00rootroot00000000000000[ req ] default_bits = 4096 days = 9999 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no x509_extensions = v3_ca [ req_distinguished_name ] C = US ST = MA L = Boston O = Critical Stack OU = evhtp CN = client1 emailAddress = nate@cl0d.com [ req_attributes ] challengePassword = password [ v3_ca ] authorityInfoAccess = @issuer_info [ issuer_info ] OCSP;URI.0 = http://ocsp.example.com/ caIssuers;URI.0 = http://example.com/ca.cert libevhtp-1.2.16/examples/https/etc/client2.cnf000066400000000000000000000012421322723041700212010ustar00rootroot00000000000000[ req ] default_bits = 4096 days = 9999 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no x509_extensions = v3_ca [ req_distinguished_name ] C = US ST = MA L = Boston O = Critical Stack OU = evhtp CN = client2 emailAddress = nate@cl0d.com [ req_attributes ] challengePassword = password [ v3_ca ] authorityInfoAccess = @issuer_info [ issuer_info ] OCSP;URI.0 = http://ocsp.example.com/ caIssuers;URI.0 = http://example.com/ca.cert libevhtp-1.2.16/examples/https/etc/server.cnf000066400000000000000000000012441322723041700211510ustar00rootroot00000000000000[ req ] default_bits = 4096 days = 9999 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no x509_extensions = v3_ca [ req_distinguished_name ] C = US ST = MA L = Boston O = Critical Stack OU = evhtp CN = localhost emailAddress = nate@cl0d.com [ req_attributes ] challengePassword = password [ v3_ca ] authorityInfoAccess = @issuer_info [ issuer_info ] OCSP;URI.0 = http://ocsp.example.com/ caIssuers;URI.0 = http://example.com/ca.cert libevhtp-1.2.16/examples/https/example_https_client.c000066400000000000000000000107121322723041700227570ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "../log.h" #include "internal.h" #include "evhtp/evhtp.h" #ifndef EVHTP_DISABLE_SSL #include "evhtp/sslutils.h" static int print_header_(evhtp_header_t * header, void * arg) { fprintf(stderr, "%s: %s\n", header->key, header->val); return 0; } static void https_resp_(evhtp_request_t * req, void * arg) { evhtp_headers_for_each(req->headers_in, print_header_, NULL); if (evbuffer_get_length(req->buffer_in)) { fprintf(stderr, "got: %.*s\n", (int)evbuffer_get_length(req->buffer_in), evbuffer_pullup(req->buffer_in, -1)); } /* since we only made one request, we break the event loop */ event_base_loopbreak((struct event_base *)arg); } enum { OPTARG_CERT = 1000, OPTARG_KEY, OPTARG_ADDR, OPTARG_PORT, OPTARG_SNI }; #endif int main(int argc, char ** argv) { #ifndef EVHTP_DISABLE_SSL struct event_base * evbase; evhtp_connection_t * conn; evhtp_request_t * req; evhtp_ssl_ctx_t * ctx; char * addr = NULL; uint16_t port = 4443; char * key = NULL; char * crt = NULL; int opt = 0; int long_index = 0; int res; static struct option long_options[] = { { "cert", required_argument, 0, OPTARG_CERT }, { "key", required_argument, 0, OPTARG_KEY }, { "addr", required_argument, 0, OPTARG_ADDR }, { "port", required_argument, 0, OPTARG_PORT }, { "help", no_argument, 0, 'h' }, { NULL, 0, 0, 0 } }; while ((opt = getopt_long_only(argc, argv, "", long_options, &long_index)) != -1) { switch (opt) { case 'h': printf("Usage: %s\n" " -key \n" " -cert \n" " -addr \n" " -port \n", argv[0]); return 0; case OPTARG_CERT: crt = strdup(optarg); break; case OPTARG_KEY: key = strdup(optarg); break; case OPTARG_ADDR: addr = strdup(optarg); break; case OPTARG_PORT: port = atoi(optarg); break; } /* switch */ } evbase = event_base_new(); evhtp_alloc_assert(evbase); #if OPENSSL_VERSION_NUMBER < 0x10100000L ctx = SSL_CTX_new(SSLv23_client_method()); #else ctx = SSL_CTX_new(TLS_client_method()); #endif evhtp_assert(ctx != NULL); if (key) { /* client private key file defined, so use it */ res = SSL_CTX_use_PrivateKey_file( ctx, key, SSL_FILETYPE_PEM); if (res == 0) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } } if (crt) { /* client cert key file defined, use it */ res = SSL_CTX_use_certificate_file( ctx, crt, SSL_FILETYPE_PEM); if (res == 0) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } } /* create a new connection to the server */ conn = evhtp_connection_ssl_new(evbase, addr ? : "127.0.0.1", port, ctx); evhtp_assert(conn != NULL); /* when the request has been completed, call https_resp_ */ req = evhtp_request_new(https_resp_, evbase); evhtp_assert(req != NULL); /* make a request context, 'GET / HTTP/1.1' */ res = evhtp_make_request(conn, req, htp_method_GET, "/"); evhtp_assert(res == 0); /* the loop will make the request and call https_resp_ * when complete. */ event_base_loop(evbase, 0); /* free up all the resources */ { SSL_CTX_free(ctx); evhtp_connection_free(conn); evhtp_request_free(req); event_base_free(evbase); free(crt); free(key); free(addr); } return 0; #else log_error("Not compiled with SSL support, go away"); return EXIT_FAILURE; #endif } /* main */ libevhtp-1.2.16/examples/https/example_https_server.c000066400000000000000000000225021322723041700230070ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "../log.h" #include "internal.h" #include "evhtp/evhtp.h" #ifndef EVHTP_DISABLE_SSL #include "evhtp/sslutils.h" static void http__callback_(evhtp_request_t * req, void * arg) { evhtp_connection_t * conn; evhtp_assert(req != NULL); conn = evhtp_request_get_connection(req); evhtp_assert(conn != NULL); htp_sslutil_add_xheaders( req->headers_out, conn->ssl, HTP_SSLUTILS_XHDR_ALL); return evhtp_send_reply(req, EVHTP_RES_OK); } static int ssl__x509_verify_(int ok, X509_STORE_CTX * store) { char buf[256]; X509 * err_cert; int err; int depth; SSL * ssl; evhtp_connection_t * connection; evhtp_ssl_cfg_t * ssl_cfg; err_cert = X509_STORE_CTX_get_current_cert(store); err = X509_STORE_CTX_get_error(store); depth = X509_STORE_CTX_get_error_depth(store); ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()); X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); connection = SSL_get_app_data(ssl); ssl_cfg = connection->htp->ssl_cfg; if (depth > ssl_cfg->verify_depth) { ok = 0; err = X509_V_ERR_CERT_CHAIN_TOO_LONG; X509_STORE_CTX_set_error(store, err); } if (!ok) { log_error("SSL: verify error:num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf); } return ok; } enum { OPTARG_CERT = 1000, OPTARG_KEY, OPTARG_CA, OPTARG_CAPATH, OPTARG_CIPHERS, OPTARG_VERIFY_PEER, OPTARG_VERIFY_DEPTH, OPTARG_ENABLE_CACHE, OPTARG_CACHE_TIMEOUT, OPTARG_CACHE_SIZE, OPTARG_CTX_TIMEOUT, OPTARG_ENABLE_PROTOCOL, OPTARG_DISABLE_PROTOCOL }; static const char * help = "Usage %s [opts] :\n" " -cert : Server PEM-encoded X.509 Certificate file\n" " -key : Server PEM-encoded Private Key file\n" " -ca : File of PEM-encoded Server CA Certificates\n" " -capath : Directory of PEM-encoded CA Certificates for Client Auth\n" " -ciphers : Accepted SSL Ciphers\n" " -verify-client (on | off | optional)\n" " Enables verification of client certificates. \n" " on : the client has to present a valid cert \n" " off : no client cert is required at all \n" " optional : the client may present a valid cert \n" " -verify-depth : Maximum depth of CA Certificates in Client Certificate verification\n" " -enable-protocol

: Enable one of the following protocols: SSLv2, SSLv3, TLSv1, or ALL\n" " -disable-protocol

: Disable one of the following protocols: SSLv2, SSLv3, TLSv1, or ALL\n" " -ctx-timeout : SSL Session Timeout (SSL >= 1.0)\n"; evhtp_ssl_cfg_t * parse__ssl_opts_(int argc, char ** argv) { int opt = 0; int long_index = 0; int ssl_verify_mode = 0; struct stat f_stat; evhtp_ssl_cfg_t * ssl_config = calloc(1, sizeof(evhtp_ssl_cfg_t)); ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1; static struct option long_options[] = { { "cert", required_argument, 0, OPTARG_CERT }, { "key", required_argument, 0, OPTARG_KEY }, { "ca", required_argument, 0, OPTARG_CA }, { "capath", required_argument, 0, OPTARG_CAPATH }, { "ciphers", required_argument, 0, OPTARG_CIPHERS }, { "verify-client", required_argument, 0, OPTARG_VERIFY_PEER }, { "verify-depth", required_argument, 0, OPTARG_VERIFY_DEPTH }, { "enable-cache", no_argument, 0, OPTARG_ENABLE_CACHE }, { "cache-timeout", required_argument, 0, OPTARG_CACHE_TIMEOUT }, { "cache-size", required_argument, 0, OPTARG_CACHE_SIZE }, { "enable-protocol", required_argument, 0, OPTARG_ENABLE_PROTOCOL }, { "disable-protocol", required_argument, 0, OPTARG_DISABLE_PROTOCOL }, { "ctx-timeout", required_argument, 0, OPTARG_CTX_TIMEOUT }, { "help", no_argument, 0, 'h' }, { NULL, 0, 0, 0 } }; while ((opt = getopt_long_only(argc, argv, "", long_options, &long_index)) != -1) { switch (opt) { case 'h': printf(help, argv[0]); exit(EXIT_FAILURE); case OPTARG_CERT: ssl_config->pemfile = strdup(optarg); break; case OPTARG_KEY: ssl_config->privfile = strdup(optarg); break; case OPTARG_CA: ssl_config->cafile = strdup(optarg); break; case OPTARG_CAPATH: ssl_config->capath = strdup(optarg); break; case OPTARG_CIPHERS: ssl_config->ciphers = strdup(optarg); break; case OPTARG_VERIFY_DEPTH: ssl_config->verify_depth = atoi(optarg); break; case OPTARG_VERIFY_PEER: ssl_verify_mode = htp_sslutil_verify2opts(optarg); break; case OPTARG_ENABLE_CACHE: ssl_config->scache_type = evhtp_ssl_scache_type_internal; break; case OPTARG_CACHE_TIMEOUT: ssl_config->scache_timeout = atoi(optarg); break; case OPTARG_CACHE_SIZE: ssl_config->scache_size = atoi(optarg); break; case OPTARG_CTX_TIMEOUT: ssl_config->ssl_ctx_timeout = atoi(optarg); break; case OPTARG_ENABLE_PROTOCOL: if (!strcasecmp(optarg, "SSLv2")) { ssl_config->ssl_opts &= ~SSL_OP_NO_SSLv2; } else if (!strcasecmp(optarg, "SSLv3")) { ssl_config->ssl_opts &= ~SSL_OP_NO_SSLv3; } else if (!strcasecmp(optarg, "TLSv1")) { ssl_config->ssl_opts &= ~SSL_OP_NO_TLSv1; } else if (!strcasecmp(optarg, "ALL")) { ssl_config->ssl_opts = 0; } break; case OPTARG_DISABLE_PROTOCOL: if (!strcasecmp(optarg, "SSLv2")) { ssl_config->ssl_opts |= SSL_OP_NO_SSLv2; } else if (!strcasecmp(optarg, "SSLv3")) { ssl_config->ssl_opts |= SSL_OP_NO_SSLv3; } else if (!strcasecmp(optarg, "TLSv1")) { ssl_config->ssl_opts |= SSL_OP_NO_TLSv1; } else if (!strcasecmp(optarg, "ALL")) { ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1; } break; default: break; } /* switch */ } if (ssl_verify_mode != 0) { ssl_config->verify_peer = ssl_verify_mode; ssl_config->x509_verify_cb = ssl__x509_verify_; } if (ssl_config->pemfile) { if (stat(ssl_config->pemfile, &f_stat) != 0) { log_error("Cannot load SSL cert '%s' (%s)", ssl_config->pemfile, strerror(errno)); exit(EXIT_FAILURE); } } if (ssl_config->privfile) { if (stat(ssl_config->privfile, &f_stat) != 0) { log_error("Cannot load SSL key '%s' (%s)", ssl_config->privfile, strerror(errno)); exit(EXIT_FAILURE); } } if (ssl_config->cafile) { if (stat(ssl_config->cafile, &f_stat) != 0) { log_error("Cannot find SSL CA File '%s' (%s)", ssl_config->cafile, strerror(errno)); exit(EXIT_FAILURE); } } if (ssl_config->capath) { if (stat(ssl_config->capath, &f_stat) != 0) { log_error("Cannot find SSL CA PATH '%s' (%s)", ssl_config->capath, strerror(errno)); exit(EXIT_FAILURE); } } return ssl_config; } /* parse__ssl_opts_ */ #endif int main(int argc, char ** argv) { #ifndef EVHTP_DISABLE_SSL evhtp_t * htp; struct event_base * evbase; evbase = event_base_new(); evhtp_alloc_assert(evbase); htp = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp); evhtp_ssl_init(htp, parse__ssl_opts_(argc, argv)); evhtp_set_gencb(htp, http__callback_, NULL); evhtp_bind_socket(htp, "127.0.0.1", 4443, 128); { struct sockaddr_in sin; socklen_t len = sizeof(struct sockaddr); uint16_t port; getsockname( evconnlistener_get_fd(htp->server), (struct sockaddr *)&sin, &len); port = ntohs(sin.sin_port); log_info("curl https://127.0.0.1:%d/", port); } event_base_loop(evbase, 0); return 0; #else log_error("Not compiled with SSL support, go away"); return EXIT_FAILURE; #endif } libevhtp-1.2.16/examples/test.c000066400000000000000000000456211322723041700163700ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "internal.h" #include "evhtp/evhtp.h" #ifndef EVHTP_DISABLE_EVTHR int use_threads = 0; int num_threads = 0; #endif char * bind_addr = "0.0.0.0"; uint16_t bind_port = 8081; char * ext_body = NULL; char * ssl_pem = NULL; char * ssl_ca = NULL; char * ssl_capath = NULL; size_t bw_limit = 0; uint64_t max_keepalives = 0; int backlog = 1024; struct pauser { event_t * timer_ev; evhtp_request_t * request; struct timeval * tv; }; /* pause testing */ static void resume_request_timer(evutil_socket_t sock, short which, void * arg) { struct pauser * pause = (struct pauser *)arg; printf("resume_request_timer(%p) timer_ev = %p\n", pause->request->conn, pause->timer_ev); fflush(stdout); evhtp_request_resume(pause->request); } static evhtp_res pause_cb(evhtp_request_t * request, evhtp_header_t * header, void * arg) { struct pauser * pause = (struct pauser *)arg; int s = rand() % 1000000; printf("pause_cb(%p) pause == %p, timer_ev = %p\n", request->conn, pause, pause->timer_ev); printf("pause_cb(%p) k=%s, v=%s timer_ev = %p\n", request->conn, header->key, header->val, pause->timer_ev); printf("pause_cb(%p) setting to %ld usec sleep timer_ev = %p\n", request->conn, (long int)s, pause->timer_ev); pause->tv->tv_sec = 0; pause->tv->tv_usec = s; if (evtimer_pending(pause->timer_ev, NULL)) { evtimer_del(pause->timer_ev); } evtimer_add(pause->timer_ev, pause->tv); return EVHTP_RES_PAUSE; } static evhtp_res pause_connection_fini(evhtp_connection_t * connection, void * arg) { printf("pause_connection_fini(%p)\n", connection); return EVHTP_RES_OK; } static evhtp_res pause_request_fini(evhtp_request_t * request, void * arg) { struct pauser * pause = (struct pauser *)arg; printf("pause_request_fini() req=%p, c=%p\n", request, request->conn); event_free(pause->timer_ev); free(pause->tv); free(pause); return EVHTP_RES_OK; } static evhtp_res pause_init_cb(evhtp_request_t * req, evhtp_path_t * path, void * arg) { evbase_t * evbase = req->conn->evbase; struct pauser * pause = calloc(sizeof(struct pauser), 1); pause->tv = calloc(sizeof(struct timeval), 1); pause->timer_ev = evtimer_new(evbase, resume_request_timer, pause); pause->request = req; evhtp_request_set_hook(req, evhtp_hook_on_header, pause_cb, pause); evhtp_request_set_hook(req, evhtp_hook_on_request_fini, pause_request_fini, pause); evhtp_connection_set_hook(req->conn, evhtp_hook_on_connection_fini, pause_connection_fini, NULL); return EVHTP_RES_OK; } static void test_pause_cb(evhtp_request_t * request, void * arg) { printf("test_pause_cb(%p)\n", request->conn); evhtp_send_reply(request, EVHTP_RES_OK); } static void _owned_readcb(evbev_t * bev, void * arg) { /* echo the input back to the client */ bufferevent_write_buffer(bev, bufferevent_get_input(bev)); } static void _owned_eventcb(evbev_t * bev, short events, void * arg) { bufferevent_free(bev); } static void test_ownership(evhtp_request_t * request, void * arg) { evhtp_connection_t * conn = evhtp_request_get_connection(request); evbev_t * bev = evhtp_connection_take_ownership(conn); bufferevent_enable(bev, EV_READ); bufferevent_setcb(bev, _owned_readcb, NULL, _owned_eventcb, NULL); } #ifndef EVHTP_DISABLE_REGEX static void test_regex(evhtp_request_t * req, void * arg) { evbuffer_add_printf(req->buffer_out, "rest_regex start = '%s', end = '%s\n", req->uri->path->match_start, req->uri->path->match_end); evhtp_send_reply(req, EVHTP_RES_OK); } static void dynamic_cb(evhtp_request_t * r, void * arg) { const char * name = arg; evbuffer_add_printf(r->buffer_out, "dynamic_cb = %s\n", name); evhtp_send_reply(r, EVHTP_RES_OK); } static void create_callback(evhtp_request_t * r, void * arg) { char * uri; char * nuri; size_t urilen; uri = r->uri->path->match_start; urilen = strlen(uri); if (urilen == 0) { return evhtp_send_reply(r, EVHTP_RES_BADREQ); } nuri = calloc(urilen + 2, 1); snprintf(nuri, urilen + 2, "/%s", uri); evhtp_set_cb(r->htp, nuri, dynamic_cb, nuri); evhtp_send_reply(r, EVHTP_RES_OK); } #endif static void test_foo_cb(evhtp_request_t * req, void * arg ) { evbuffer_add_reference(req->buffer_out, "test_foo_cb\n", 12, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } static void test_500_cb(evhtp_request_t * req, void * arg ) { evbuffer_add_reference(req->buffer_out, "test_500_cb\n", 12, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_SERVERR); } static void test_max_body(evhtp_request_t * req, void * arg) { evbuffer_add_reference(req->buffer_out, "test_max_body\n", 14, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } const char * chunk_strings[] = { "I give you the light of Eärendil,\n", "our most beloved star.\n", "May it be a light for you in dark places,\n", "when all other lights go out.\n", NULL }; static void test_chunking(evhtp_request_t * req, void * arg) { const char * chunk_str; evbuf_t * buf; int i = 0; buf = evbuffer_new(); evhtp_send_reply_chunk_start(req, EVHTP_RES_OK); while ((chunk_str = chunk_strings[i++]) != NULL) { evbuffer_add(buf, chunk_str, strlen(chunk_str)); evhtp_send_reply_chunk(req, buf); evbuffer_drain(buf, -1); } evhtp_send_reply_chunk_end(req); evbuffer_free(buf); } static void test_bar_cb(evhtp_request_t * req, void * arg) { evhtp_send_reply(req, EVHTP_RES_OK); } static void test_glob_cb(evhtp_request_t * req, void * arg) { evbuffer_add(req->buffer_out, "test_glob_cb\n", 13); evhtp_send_reply(req, EVHTP_RES_OK); } static void test_default_cb(evhtp_request_t * req, void * arg) { evbuffer_add_reference(req->buffer_out, "test_default_cb\n", 16, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } static evhtp_res print_kv(evhtp_request_t * req, evhtp_header_t * hdr, void * arg) { evbuffer_add_printf(req->buffer_out, "print_kv() key = '%s', val = '%s'\n", hdr->key, hdr->val); return EVHTP_RES_OK; } static int output_header(evhtp_header_t * header, void * arg) { evbuf_t * buf = arg; evbuffer_add_printf(buf, "print_kvs() key = '%s', val = '%s'\n", header->key, header->val); return 0; } static evhtp_res print_kvs(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg ) { evhtp_headers_for_each(hdrs, output_header, req->buffer_out); return EVHTP_RES_OK; } static evhtp_res print_path(evhtp_request_t * req, evhtp_path_t * path, void * arg) { if (ext_body) { evbuffer_add_printf(req->buffer_out, "ext_body: '%s'\n", ext_body); } evbuffer_add_printf(req->buffer_out, "print_path() full = '%s'\n" " path = '%s'\n" " file = '%s'\n" " match start = '%s'\n" " match_end = '%s'\n" " methno = '%d'\n", path->full, path->path, path->file, path->match_start, path->match_end, evhtp_request_get_method(req)); return EVHTP_RES_OK; } static evhtp_res print_data(evhtp_request_t * req, evbuf_t * buf, void * arg) { #ifndef NDEBUG evbuffer_add_printf(req->buffer_out, "got %zu bytes of data\n", evbuffer_get_length(buf)); /* printf("%.*s", (int)evbuffer_get_length(buf), (char *)evbuffer_pullup(buf, evbuffer_get_length(buf))); */ #endif evbuffer_drain(buf, -1); return EVHTP_RES_OK; } static evhtp_res print_new_chunk_len(evhtp_request_t * req, uint64_t len, void * arg) { evbuffer_add_printf(req->buffer_out, "started new chunk, %" PRId64 "u bytes\n", len); return EVHTP_RES_OK; } static evhtp_res print_chunk_complete(evhtp_request_t * req, void * arg) { evbuffer_add_printf(req->buffer_out, "ended a single chunk\n"); return EVHTP_RES_OK; } static evhtp_res print_chunks_complete(evhtp_request_t * req, void * arg) { evbuffer_add_printf(req->buffer_out, "all chunks read\n"); return EVHTP_RES_OK; } #ifndef EVHTP_DISABLE_REGEX static evhtp_res test_regex_hdrs_cb(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg ) { return EVHTP_RES_OK; } #endif static evhtp_res set_max_body(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg) { evhtp_request_set_max_body_size(req, 1024); return EVHTP_RES_OK; } static evhtp_res test_pre_accept(evhtp_connection_t * c, void * arg) { uint16_t port = *(uint16_t *)arg; if (port > 10000) { return EVHTP_RES_ERROR; } return EVHTP_RES_OK; } static evhtp_res test_fini(evhtp_request_t * r, void * arg) { struct ev_token_bucket_cfg * tcfg = arg; if (tcfg) { ev_token_bucket_cfg_free(tcfg); } return EVHTP_RES_OK; } #if 0 static evhtp_res print_hostname(evhtp_request_t * r, const char * host, void * arg) { printf("%s\n", host); return EVHTP_RES_OK; } #endif static evhtp_res set_my_connection_handlers(evhtp_connection_t * conn, void * arg) { struct timeval tick; struct ev_token_bucket_cfg * tcfg = NULL; evhtp_connection_set_hook(conn, evhtp_hook_on_header, print_kv, "foo"); evhtp_connection_set_hook(conn, evhtp_hook_on_headers, print_kvs, "bar"); evhtp_connection_set_hook(conn, evhtp_hook_on_path, print_path, "baz"); evhtp_connection_set_hook(conn, evhtp_hook_on_read, print_data, "derp"); evhtp_connection_set_hook(conn, evhtp_hook_on_new_chunk, print_new_chunk_len, NULL); evhtp_connection_set_hook(conn, evhtp_hook_on_chunk_complete, print_chunk_complete, NULL); evhtp_connection_set_hook(conn, evhtp_hook_on_chunks_complete, print_chunks_complete, NULL); if (bw_limit > 0) { tick.tv_sec = 0; tick.tv_usec = 500 * 100; tcfg = ev_token_bucket_cfg_new(bw_limit, bw_limit, bw_limit, bw_limit, &tick); bufferevent_set_rate_limit(conn->bev, tcfg); } evhtp_connection_set_hook(conn, evhtp_hook_on_request_fini, test_fini, tcfg); return EVHTP_RES_OK; } #ifndef EVHTP_DISABLE_SSL static int dummy_ssl_verify_callback(int ok, X509_STORE_CTX * x509_store) { return 1; } static int dummy_check_issued_cb(X509_STORE_CTX * ctx, X509 * x, X509 * issuer) { return 1; } #endif const char * optstr = "htn:a:p:r:s:c:C:l:N:m:b:"; const char * help = "Options: \n" " -h : This help text\n" #ifndef EVHTP_DISABLE_EVTHR " -t : Run requests in a thread (default: off)\n" " -n : Number of threads (default: 0 if -t is off, 4 if -t is on)\n" #endif #ifndef EVHTP_DISABLE_SSL " -s : Enable SSL and PEM (default: NULL)\n" " -c : CA cert file (default: NULL)\n" " -C : CA Path (default: NULL)\n" " (to test: \n" " openssl genrsa -out pkey 2048\n" " openssl req -new -key pkey -out cert.req\n" " openssl x509 -req -days 365 -in cert.req -signkey pkey -out cert\n" " cat pkey cert > test_cert\n" " then use -s test_cert\n" #endif " -l : Max bandwidth (in bytes) (default: NULL)\n" " -r : Document root (default: .)\n" " -N : Add this string to body. (default: NULL)\n" " -a : Bind Address (default: 0.0.0.0)\n" " -p : Bind Port (default: 8081)\n" " -m : Max keepalive requests (default: 0)\n"; int parse_args(int argc, char ** argv) { extern char * optarg; extern int optind; extern int opterr; extern int optopt; int c; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { case 'h': printf("Usage: %s [opts]\n%s", argv[0], help); return -1; case 'N': ext_body = strdup(optarg); break; case 'a': bind_addr = strdup(optarg); break; case 'p': bind_port = atoi(optarg); break; #ifndef EVHTP_DISABLE_EVTHR case 't': use_threads = 1; break; case 'n': num_threads = atoi(optarg); break; #endif #ifndef EVHTP_DISABLE_SSL case 's': ssl_pem = strdup(optarg); break; case 'c': ssl_ca = strdup(optarg); break; case 'C': ssl_capath = strdup(optarg); break; #endif case 'l': bw_limit = atoll(optarg); break; case 'm': max_keepalives = atoll(optarg); break; case 'b': backlog = atoll(optarg); break; default: printf("Unknown opt %s\n", optarg); return -1; } /* switch */ } #ifndef EVHTP_DISABLE_EVTHR if (use_threads && num_threads == 0) { num_threads = 4; } #endif return 0; } /* parse_args */ static void sigint(int sig, short why, void * data) { event_base_loopexit(data, NULL); } int main(int argc, char ** argv) { struct event * ev_sigint; evbase_t * evbase = NULL; evhtp_t * htp = NULL; evhtp_callback_t * cb_1 = NULL; evhtp_callback_t * cb_2 = NULL; evhtp_callback_t * cb_3 = NULL; evhtp_callback_t * cb_4 = NULL; evhtp_callback_t * cb_5 = NULL; #ifndef EVHTP_DISABLE_REGEX evhtp_callback_t * cb_6 = NULL; #endif evhtp_callback_t * cb_7 = NULL; #ifndef EVHTP_DISABLE_REGEX evhtp_callback_t * cb_8 = NULL; #endif evhtp_callback_t * cb_9 = NULL; evhtp_callback_t * cb_10 = NULL; evhtp_callback_t * cb_11 = NULL; evhtp_callback_t * cb_12 = NULL; if (parse_args(argc, argv) < 0) { exit(1); } srand((unsigned)time(NULL)); evbase = event_base_new(); htp = evhtp_new(evbase, NULL); evhtp_enable_flag(htp, EVHTP_FLAG_ENABLE_REUSEPORT); evhtp_set_parser_flags(htp, EVHTP_PARSE_QUERY_FLAG_LENIENT); evhtp_set_max_keepalive_requests(htp, max_keepalives); cb_1 = evhtp_set_cb(htp, "/ref", test_default_cb, "fjdkls"); evhtp_assert(cb_1 != NULL); cb_2 = evhtp_set_cb(htp, "/foo", test_foo_cb, "bar"); evhtp_assert(cb_2 != NULL); cb_3 = evhtp_set_cb(htp, "/foo/", test_foo_cb, "bar"); evhtp_assert(cb_3 != NULL); cb_4 = evhtp_set_cb(htp, "/bar", test_bar_cb, "baz"); evhtp_assert(cb_4 != NULL); cb_5 = evhtp_set_cb(htp, "/500", test_500_cb, "500"); evhtp_assert(cb_5 != NULL); #ifndef EVHTP_DISABLE_REGEX cb_6 = evhtp_set_regex_cb(htp, "^(/anything/).*", test_regex, NULL); evhtp_assert(cb_6 != NULL); #endif cb_7 = evhtp_set_cb(htp, "/pause", test_pause_cb, NULL); evhtp_assert(cb_7 != NULL); #ifndef EVHTP_DISABLE_REGEX cb_8 = evhtp_set_regex_cb(htp, "^/create/(.*)", create_callback, NULL); evhtp_assert(cb_8 != NULL); #endif cb_9 = evhtp_set_glob_cb(htp, "*/glob/*", test_glob_cb, NULL); evhtp_assert(cb_9 != NULL); cb_10 = evhtp_set_cb(htp, "/max_body_size", test_max_body, NULL); evhtp_assert(cb_10 != NULL); /* set a callback to test out chunking API */ cb_11 = evhtp_set_cb(htp, "/chunkme", test_chunking, NULL); evhtp_assert(cb_11 != NULL); /* set a callback which takes ownership of the underlying bufferevent and * just starts echoing things */ cb_12 = evhtp_set_cb(htp, "/ownme", test_ownership, NULL); evhtp_assert(cb_12 != NULL); /* set a callback to pause on each header for cb_7 */ evhtp_callback_set_hook(cb_7, evhtp_hook_on_path, pause_init_cb, NULL); /* set a callback to set hooks specifically for the cb_6 callback */ #ifndef EVHTP_DISABLE_REGEX evhtp_callback_set_hook(cb_6, evhtp_hook_on_headers, test_regex_hdrs_cb, NULL); #endif evhtp_callback_set_hook(cb_10, evhtp_hook_on_headers, set_max_body, NULL); /* set a default request handler */ evhtp_set_gencb(htp, test_default_cb, "foobarbaz"); /* set a callback invoked before a connection is accepted */ evhtp_set_pre_accept_cb(htp, test_pre_accept, &bind_port); /* set a callback to set per-connection hooks (via a post_accept cb) */ evhtp_set_post_accept_cb(htp, set_my_connection_handlers, NULL); #ifndef EVHTP_DISABLE_SSL if (ssl_pem != NULL) { evhtp_ssl_cfg_t scfg = { .pemfile = ssl_pem, .privfile = ssl_pem, .cafile = ssl_ca, .capath = ssl_capath, .ciphers = "RC4+RSA:HIGH:+MEDIUM:+LOW", .ssl_opts = SSL_OP_NO_SSLv2, .ssl_ctx_timeout = 60 * 60 * 48, .verify_peer = SSL_VERIFY_PEER, .verify_depth = 42, .x509_verify_cb = dummy_ssl_verify_callback, .x509_chk_issued_cb = dummy_check_issued_cb, .scache_type = evhtp_ssl_scache_type_internal, .scache_size = 1024, .scache_timeout = 1024, .scache_init = NULL, .scache_add = NULL, .scache_get = NULL, .scache_del = NULL, }; evhtp_ssl_init(htp, &scfg); #ifndef EVHTP_DISABLE_EVTHR if (use_threads) { #define OPENSSL_THREAD_DEFINES #include #if defined(OPENSSL_THREADS) #else fprintf(stderr, "Your version of OpenSSL does not support threading!\n"); exit(-1); #endif } #endif } #endif #ifndef EVHTP_DISABLE_EVTHR if (use_threads) { evhtp_use_threads_wexit(htp, NULL, NULL, num_threads, NULL); } #endif if (evhtp_bind_socket(htp, bind_addr, bind_port, backlog) < 0) { fprintf(stderr, "Could not bind socket: %s\n", strerror(errno)); exit(-1); } ev_sigint = evsignal_new(evbase, SIGINT, sigint, evbase); evsignal_add(ev_sigint, NULL); event_base_loop(evbase, 0); event_free(ev_sigint); evhtp_unbind_socket(htp); evhtp_free(htp); event_base_free(evbase); return 0; } /* main */ libevhtp-1.2.16/examples/test_basic.c000066400000000000000000000025021322723041700175200ustar00rootroot00000000000000#include #include #include #include #include #include void testcb(evhtp_request_t * req, void * a) { const char * str = a; evbuffer_add(req->buffer_out, str, strlen(str)); evhtp_send_reply(req, EVHTP_RES_OK); } void issue161cb(evhtp_request_t * req, void * a) { struct evbuffer * b = evbuffer_new(); if (evhtp_request_get_proto(req) == EVHTP_PROTO_10) { evhtp_request_set_keepalive(req, 0); } evhtp_send_reply_start(req, EVHTP_RES_OK); evbuffer_add(b, "foo", 3); evhtp_send_reply_body(req, b); evbuffer_add(b, "bar\n\n", 5); evhtp_send_reply_body(req, b); evhtp_send_reply_end(req); evbuffer_free(b); } int main(int argc, char ** argv) { evbase_t * evbase = event_base_new(); evhtp_t * htp = evhtp_new(evbase, NULL); evhtp_set_cb(htp, "/simple/", testcb, "simple"); evhtp_set_cb(htp, "/1/ping", testcb, "one"); evhtp_set_cb(htp, "/1/ping.json", testcb, "two"); evhtp_set_cb(htp, "/issue161", issue161cb, NULL); #ifndef EVHTP_DISABLE_EVTHR evhtp_use_threads_wexit(htp, NULL, NULL, 8, NULL); #endif evhtp_bind_socket(htp, "0.0.0.0", 8081, 2048); event_base_loop(evbase, 0); evhtp_unbind_socket(htp); evhtp_free(htp); event_base_free(evbase); return 0; } libevhtp-1.2.16/examples/test_client.c000066400000000000000000000040131322723041700177140ustar00rootroot00000000000000#include #include #include #include #include #include #include static void request_cb(evhtp_request_t * req, void * arg) { printf("hi %zu\n", evbuffer_get_length(req->buffer_in)); } static evhtp_res print_data(evhtp_request_t * req, evbuf_t * buf, void * arg) { printf("Got %zu bytes\n", evbuffer_get_length(buf)); return EVHTP_RES_OK; } static evhtp_res print_new_chunk_len(evhtp_request_t * req, uint64_t len, void * arg) { printf("started new chunk, %" PRIu64 " bytes\n", len); return EVHTP_RES_OK; } static evhtp_res print_chunk_complete(evhtp_request_t * req, void * arg) { printf("ended a single chunk\n"); return EVHTP_RES_OK; } static evhtp_res print_chunks_complete(evhtp_request_t * req, void * arg) { printf("all chunks read\n"); return EVHTP_RES_OK; } int main(int argc, char ** argv) { evbase_t * evbase; evhtp_connection_t * conn; evhtp_request_t * request; evbase = event_base_new(); conn = evhtp_connection_new(evbase, "75.126.169.52", 80); request = evhtp_request_new(request_cb, evbase); evhtp_request_set_hook(request, evhtp_hook_on_read, print_data, evbase); evhtp_request_set_hook(request, evhtp_hook_on_new_chunk, print_new_chunk_len, NULL); evhtp_request_set_hook(request, evhtp_hook_on_chunk_complete, print_chunk_complete, NULL); evhtp_request_set_hook(request, evhtp_hook_on_chunks_complete, print_chunks_complete, NULL); evhtp_headers_add_header(request->headers_out, evhtp_header_new("Host", "ieatfood.net", 0, 0)); evhtp_headers_add_header(request->headers_out, evhtp_header_new("User-Agent", "libevhtp", 0, 0)); evhtp_headers_add_header(request->headers_out, evhtp_header_new("Connection", "close", 0, 0)); evhtp_make_request(conn, request, htp_method_GET, "/"); event_base_loop(evbase, 0); event_base_free(evbase); return 0; } libevhtp-1.2.16/examples/test_htparse.c000066400000000000000000000272721322723041700201200ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "htparse.h" #define ADD_DATA_BUF(buf, name, data, len) do { \ strcat(buf, name ": '"); \ strncat(buf, data, len); \ strcat(buf, "'\n"); \ } while (0) struct testobj { char * name; char * data; htp_type type; }; static int _msg_begin(htparser * p) { char * buf = (char *)htparser_get_userdata(p); strcat(buf, "START\n"); return 0; } static int _method(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); if (htparser_get_method(p) == htp_method_UNKNOWN) { ADD_DATA_BUF(buf, "METHOD_UNKNOWN", b, s); return htp_method_UNKNOWN; } else { ADD_DATA_BUF(buf, "METHOD", b, s); } return 0; } static int _scheme(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "SCHEME", b, s); return 0; } static int _host(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "HOST", b, s); return 0; } static int _port(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "PORT", b, s); return 0; } static int _path(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "PATH", b, s); return 0; } static int _args(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "ARGS", b, s); return 0; } static int _uri(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "URI", b, s); return 0; } static int _hdrs_begin(htparser * p) { char * buf = (char *)htparser_get_userdata(p); strcat(buf, "HDRS_BEGIN\n"); return 0; } static int _hdr_key(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "HDR_KEY", b, s); return 0; } static int _hdr_val(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "HDR_VAL", b, s); return 0; } static int _hostname(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "HOSTNAME", b, s); return 0; } static int _hdrs_complete(htparser * p) { char * buf = (char *)htparser_get_userdata(p); strcat(buf, "HDRS_COMPLETE\n"); return 0; } static int _new_chunk(htparser * p) { char * buf = (char *)htparser_get_userdata(p); char tbuf[1024] = { 0 }; sprintf(tbuf, "NEW_CHUNK: %" PRIu64 "\n", htparser_get_content_length(p)); strcat(buf, tbuf); return 0; } static int _chunk_complete(htparser * p) { char * buf = (char *)htparser_get_userdata(p); strcat(buf, "END_CHUNK\n"); return 0; } static int _chunks_complete(htparser * p) { char * buf = (char *)htparser_get_userdata(p); strcat(buf, "END_CHUNKS\n"); return 0; } static int _body(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "BODY", b, s); return 0; } static int _msg_complete(htparser * p) { char * buf = (char *)htparser_get_userdata(p); strcat(buf, "MSG_COMPLETE\n"); return 0; } htparse_hooks hooks = { .on_msg_begin = _msg_begin, .method = _method, .scheme = _scheme, .host = _host, .port = _port, .path = _path, .args = _args, .uri = _uri, .on_hdrs_begin = _hdrs_begin, .hdr_key = _hdr_key, .hdr_val = _hdr_val, .hostname = _hostname, .on_hdrs_complete = _hdrs_complete, .on_new_chunk = _new_chunk, .on_chunk_complete = _chunk_complete, .on_chunks_complete = _chunks_complete, .body = _body, .on_msg_complete = _msg_complete }; struct testobj t1 = { .name = "small GET request", .type = htp_type_request, .data = "GET / HTTP/1.0\r\n\r\n" }; struct testobj t2 = { .name = "GET request with arguments", .type = htp_type_request, .data = "GET /test?a=b&c=d HTTP/1.1\r\n\r\n" }; struct testobj t3 = { .name = "POST request with 4 bytes of data", .type = htp_type_request, .data = "POST /foo/bar HTTP/1.0\r\n" "Content-Length: 4\r\n\r\n" "abcd" }; struct testobj t4 = { .name = "Simple POST with chunked data", .type = htp_type_request, .data = "POST /test/ HTTP/1.1\r\n" "Transfer-Encoding: chunked\r\n\r\n" "1e\r\nall your base are belong to us\r\n" "0\r\n" "\r\n" }; struct testobj t5 = { .name = "POST request with multiple chunks", .type = htp_type_request, .data = "POST /test/ HTTP/1.1\r\n" "Connection: Keep-Alive\r\n" "Transfer-Encoding: chunked\r\n\r\n" "23\r\n" "This is the data in the first chunk" "\r\n" "1A\r\n" "and this is the second one" "\r\n" "3\r\n" "foo\r\n" "6\r\n" "barbaz\r\n" "0\r\n\r\n" }; struct testobj t6 = { .name = "GET request with a host header", .type = htp_type_request, .data = "GET /test/ HTTP/1.0\r\n" "Host: ieatfood.net\r\n\r\n" }; struct testobj t7 = { .name = "GET request with an empty header value", .type = htp_type_request, .data = "GET /test/ HTTP/1.0\r\n" "Header1: value1\r\n" "Header2: \r\n" "Header3: value3\r\n\r\n" }; struct testobj t8 = { .name = "GET request with a multi-line header value", .type = htp_type_request, .data = "GET /test/ HTTP/1.1\r\n" "Header1: value1\r\n" "Header2: val\r\n" "\tue\r\n" "\t2\r\n" "Header3: value3\r\n\r\n" }; struct testobj t9 = { .name = "[FAILURE TEST] GET REQUEST with LF instead of CRLF on header value", .type = htp_type_request, .data = "GET /test/ HTTP/1.1\r\n" "Header: value\n\n" }; struct testobj t10 = { .name = "[FAILURE TEST] GET request with invalid protocol", .type = htp_type_request, .data = "GET /test/ fdasfs\r\n\r\n" }; struct testobj t11 = { .name = "[FALURE TEST] POST request with invalid chunk length", .type = htp_type_request, .data = "POST /test/ HTTP/1.1\r\n" "Transfer-Encoding: chunked\r\n\r\n" "3\r\n" "foo" "\r\n" "A\r\n" "foobar\r\n" "3\r\n" "baz\r\n" "0\r\n\r\n" }; struct testobj t12 = { .name = "Simple GET on a FTP scheme", .type = htp_type_request, .data = "GET ftp://test.com/foo/bar HTTP/1.1\r\n\r\n" }; struct testobj t13 = { .name = "Multiple GET requests in HTTP/1.1 request", .type = htp_type_request, .data = "GET /request1 HTTP/1.1\r\n\r\n" "GET /request2 HTTP/1.0\r\n" "Connection: close\r\n\r\n" }; struct testobj t14 = { .name = "[FAILURE TEST] invalid request type", .type = htp_type_request, .data = "DERP /test HTTP/1.1\r\n\r\n" }; struct testobj t15 = { .name = "http SCHEME request with port / args / headers", .type = htp_type_request, .data = "GET http://ieatfood.net:80/index.html?foo=bar&baz=buz HTTP/1.0\r\n" "Host: ieatfood.net\r\n" "Header: value\r\n\r\n" }; struct testobj t16 = { .name = "GET request which should run all callbacks minus scheme stuff, this includes multiple requests", .type = htp_type_request, .data = "GET /test1?a=b&c=d&e=f HTTP/1.1\r\n" "Content-Length: 6\r\n\r\n" "foobar" "GET /test2 HTTP/1.1\r\n" "Header: test2\r\n\r\n" "POST /test/ HTTP/1.1\r\n" "Transfer-Encoding: chunked\r\n\r\n" "23\r\n" "This is the data in the first chunk" "\r\n" "1A\r\n" "and this is the second one" "\r\n" "3\r\n" "foo\r\n" "6\r\n" "barbaz\r\n" "0\r\n\r\n" "GET /test/ HTTP/1.1\r\n" "Host: ieatfood.net\r\n" "Connection: close\r\n\r\n" }; struct testobj t17 = { .name = "Like the last test, but with scheme requests", .type = htp_type_request, .data = "GET http://ieatfood.net/test1?a=b&c=d&e=f HTTP/1.1\r\n" "Content-Length: 6\r\n\r\n" "foobar" "GET https://ieatfood.net:443/test2 HTTP/1.1\r\n" "Header: test2\r\n\r\n" "POST /test/ HTTP/1.1\r\n" "Transfer-Encoding: chunked\r\n\r\n" "23\r\n" "This is the data in the first chunk" "\r\n" "1A\r\n" "and this is the second one" "\r\n" "3\r\n" "foo\r\n" "6\r\n" "barbaz\r\n" "0\r\n\r\n" "GET ftp://ackers.net:21/test/ HTTP/1.1\r\n" "Host: ackers.net\r\n" "Connection: close\r\n\r\n" }; struct testobj t18 = { .name = "scheme request with empty path", .type = htp_type_request, .data = "GET http://ackers.net HTTP/1.0\r\n\r\n" }; struct testobj t19 = { .name = "basic HTTP RESPONSE", .type = htp_type_response, .data = "HTTP/1.1 200 OK\r\n\r\n" }; struct testobj t20 = { .name = "HTTP RESPONSE with body", .type = htp_type_response, .data = "HTTP/1.1 200 OK\r\n" "Content-Length: 6\r\n\r\n" "foobar" }; struct testobj t21 = { .name = "HTTP RESPONSE with chunked data", .type = htp_type_response, .data = "HTTP/1.1 200 OK\r\n" "Transfer-Encoding: chunked\r\n\r\n" "23\r\n" "This is the data in the first chunk" "\r\n" "1A\r\n" "and this is the second one" "\r\n" "3\r\n" "foo\r\n" "6\r\n" "barbaz\r\n" "0\r\n\r\n" }; struct testobj t22 = { .name = "Header key with no value", .type = htp_type_request, .data = "GET / HTTP/1.1\r\n" "Accept\r\n\r\n" }; static int _run_test(htparser * p, struct testobj * obj) { size_t data_sz; size_t parsed_sz; char result_buf[5000] = { 0 }; htparser_init(p, obj->type); htparser_set_userdata(p, result_buf); data_sz = strlen(obj->data); parsed_sz = htparser_run(p, &hooks, obj->data, data_sz); strcat(result_buf, "ERROR_STR: "); strcat(result_buf, htparser_get_strerror(p)); strcat(result_buf, "\n"); printf("%s\n", obj->name); printf("-----------------\n"); printf("%s", result_buf); printf("\n"); return 0; } int main(int argc, char **argv) { htparser * parser; parser = htparser_new(); assert(parser != NULL); _run_test(parser, &t1); _run_test(parser, &t2); _run_test(parser, &t3); _run_test(parser, &t4); _run_test(parser, &t5); _run_test(parser, &t6); _run_test(parser, &t7); _run_test(parser, &t8); _run_test(parser, &t9); _run_test(parser, &t10); _run_test(parser, &t11); _run_test(parser, &t12); _run_test(parser, &t13); _run_test(parser, &t14); _run_test(parser, &t15); _run_test(parser, &t16); _run_test(parser, &t17); _run_test(parser, &t18); _run_test(parser, &t19); _run_test(parser, &t20); _run_test(parser, &t21); _run_test(parser, &t22); return 0; } libevhtp-1.2.16/examples/test_perf.c000066400000000000000000000072371322723041700174050ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "internal.h" #include "evhtp/evhtp.h" static int num_threads = 0; static char * baddr = "127.0.0.1"; static uint16_t bport = 8081; static int backlog = 1024; static int nodelay = 0; static int defer_accept = 0; static int reuse_port = 0; static size_t payload_sz = 100; static void response_cb(evhtp_request_t * r, void * a) { evbuffer_add_reference(r->buffer_out, (const char *)a, payload_sz, NULL, NULL); evhtp_send_reply(r, EVHTP_RES_OK); } int main(int argc, char ** argv) { extern char * optarg; extern int optind; extern int opterr; extern int optopt; int c; while ((c = getopt(argc, argv, "t:a:p:b:ndrs:")) != -1) { switch (c) { case 't': num_threads = atoi(optarg); break; case 'a': baddr = strdup(optarg); break; case 'p': bport = atoi(optarg); break; case 'b': backlog = atoll(optarg); case 'n': nodelay = 1; break; case 'd': defer_accept = 1; break; case 'r': reuse_port = 1; break; case 's': payload_sz = atoll(optarg); break; default: fprintf(stdout, "Usage: %s [flags]\n", argv[0]); fprintf(stdout, " -t : number of worker threads [Default: %d]\n", num_threads); fprintf(stdout, " -a : bind address [Default: %s]\n", baddr); fprintf(stdout, " -p : bind port [Default: %d]\n", bport); fprintf(stdout, " -b : listen backlog [Default: %d]\n", backlog); fprintf(stdout, " -s : size of the response [Default: %zu]\n", payload_sz); fprintf(stdout, " -n : disable nagle (nodelay) [Default: %s]\n", nodelay ? "true" : "false"); fprintf(stdout, " -d : enable deferred accept [Default: %s]\n", defer_accept ? "true" : "false"); fprintf(stdout, " -r : enable linux reuseport [Default: %s]\n", reuse_port ? "true" : "false"); exit(EXIT_FAILURE); } /* switch */ } { struct event_base * evbase; evhtp_t * htp; char payload[payload_sz]; evbase = event_base_new(); evhtp_alloc_assert(evbase); htp = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp); evhtp_set_parser_flags(htp, EVHTP_PARSE_QUERY_FLAG_LENIENT); if (nodelay) { evhtp_enable_flag(htp, EVHTP_FLAG_ENABLE_NODELAY); } if (defer_accept) { evhtp_enable_flag(htp, EVHTP_FLAG_ENABLE_DEFER_ACCEPT); } if (reuse_port) { evhtp_enable_flag(htp, EVHTP_FLAG_ENABLE_REUSEPORT); } memset(payload, 0x42, payload_sz); evhtp_assert(evhtp_set_cb(htp, "/data", response_cb, payload)); #ifndef EVHTP_DISABLE_EVTHR if (num_threads > 0) { evhtp_assert(evhtp_use_threads_wexit(htp, NULL, NULL, num_threads, NULL) != -1); } #endif evhtp_errno_assert(evhtp_bind_socket(htp, baddr, bport, backlog) >= 0); event_base_loop(evbase, 0); } return 0; } /* main */ libevhtp-1.2.16/examples/test_proxy.c000066400000000000000000000067451322723041700176350ustar00rootroot00000000000000#include #include #include #include #include #include #include #include int make_request(evbase_t * evbase, evthr_t * evthr, const char * const host, const short port, const char * const path, evhtp_headers_t * headers, evhtp_callback_cb cb, void * arg) { evhtp_connection_t * conn; evhtp_request_t * request; conn = evhtp_connection_new(evbase, host, port); conn->thread = evthr; request = evhtp_request_new(cb, arg); evhtp_headers_add_header(request->headers_out, evhtp_header_new("Host", "localhost", 0, 0)); evhtp_headers_add_header(request->headers_out, evhtp_header_new("User-Agent", "libevhtp", 0, 0)); evhtp_headers_add_header(request->headers_out, evhtp_header_new("Connection", "close", 0, 0)); evhtp_headers_add_headers(request->headers_out, headers); printf("Making backend request...\n"); evhtp_make_request(conn, request, htp_method_GET, path); printf("Ok.\n"); return 0; } static void backend_cb(evhtp_request_t * backend_req, void * arg) { evhtp_request_t * frontend_req = (evhtp_request_t *)arg; evbuffer_prepend_buffer(frontend_req->buffer_out, backend_req->buffer_in); evhtp_headers_add_headers(frontend_req->headers_out, backend_req->headers_in); /* * char body[1024] = { '\0' }; * ev_ssize_t len = evbuffer_copyout(frontend_req->buffer_out, body, sizeof(body)); * printf("Backend %zu: %s\n", len, body); */ evhtp_send_reply(frontend_req, EVHTP_RES_OK); evhtp_request_resume(frontend_req); } static void frontend_cb(evhtp_request_t * req, void * arg) { int * aux; int thr; aux = (int *)evthr_get_aux(req->conn->thread); thr = *aux; printf(" Received frontend request on thread %d... ", thr); /* Pause the frontend request while we run the backend requests. */ evhtp_request_pause(req); make_request(evthr_get_base(req->conn->thread), req->conn->thread, "127.0.0.1", 80, req->uri->path->full, req->headers_in, backend_cb, req); printf("Ok.\n"); } /* Terminate gracefully on SIGTERM */ void sigterm_cb(int fd, short event, void * arg) { evbase_t * evbase = (evbase_t *)arg; struct timeval tv = { .tv_usec = 100000, .tv_sec = 0 }; /* 100 ms */ event_base_loopexit(evbase, &tv); } void init_thread_cb(evhtp_t * htp, evthr_t * thr, void * arg) { static int aux = 0; printf("Spinning up a thread: %d\n", ++aux); evthr_set_aux(thr, &aux); } int main(int argc, char ** argv) { struct event *ev_sigterm; evbase_t * evbase = event_base_new(); evhtp_t * evhtp = evhtp_new(evbase, NULL); evhtp_set_gencb(evhtp, frontend_cb, NULL); #if 0 #ifndef EVHTP_DISABLE_SSL evhtp_ssl_cfg_t scfg1 = { 0 }; scfg1.pemfile = "./server.pem"; scfg1.privfile = "./server.pem"; evhtp_ssl_init(evhtp, &scfg1); #endif #endif evhtp_use_threads_wexit(evhtp, init_thread_cb, NULL, 8, NULL); #ifndef WIN32 ev_sigterm = evsignal_new(evbase, SIGTERM, sigterm_cb, evbase); evsignal_add(ev_sigterm, NULL); #endif evhtp_bind_socket(evhtp, "0.0.0.0", 8081, 1024); event_base_loop(evbase, 0); printf("Clean exit\n"); return 0; } libevhtp-1.2.16/examples/test_query.c000066400000000000000000000166111322723041700176120ustar00rootroot00000000000000#include #include #include #include "evhtp.h" struct test { const char * raw_query; int exp_error; struct expected { char * key; char * val; } exp[10]; /* avoid flexible array member: limit expectations per raw_query */ }; static int test_cmp(evhtp_query_t * query, evhtp_kv_t * kvobj, const char * valstr, struct expected * exp) { if (!query || !kvobj) { return -1; } if (exp->val == NULL) { if (kvobj->val || valstr) { return -1; } return 0; } if (strcmp(kvobj->val, exp->val)) { printf("\n"); printf(" expected: '%s'\n", exp->val); printf(" actual: '%s'\n", kvobj->val); return -1; } if (strcmp(valstr, exp->val)) { return -1; } return 0; } /* evhtp_kvs_iterator */ int kvs_print(evhtp_kv_t * kvobj, void * arg) { int * key_idx = arg; if (*key_idx) { printf(", "); } printf("\"%s\": %s%s%s", kvobj->key, kvobj->val ? "\"" : "", kvobj->val, kvobj->val ? "\"" : ""); *key_idx += 1; return 0; } static int query_test(const char * raw_query, int exp_error, struct expected exp[], int flags) { evhtp_query_t * query; struct expected * check; int key_idx = 0; int idx = 0; int num_errors = 0; /* print whether error is expected or not */ printf("%-7s ", exp_error ? "(error)" : ""); query = evhtp_parse_query_wflags(raw_query, strlen(raw_query), flags); if (!query) { printf(""); return exp_error == 0; } printf("{"); evhtp_kvs_for_each(query, kvs_print, &key_idx); /* TODO check for keys in query but not in exp */ printf("}"); while (1) { evhtp_kv_t * kvobj = NULL; const char * valstr = NULL; check = &exp[idx++]; if (check == NULL || check->key == NULL) { break; } kvobj = evhtp_kvs_find_kv(query, check->key); valstr = evhtp_kv_find(query, check->key); if (test_cmp(query, kvobj, valstr, check) == -1) { num_errors += 1; printf(" "); } } if (exp_error) { return -1; } evhtp_query_free(query); return num_errors; } /* query_test */ struct test base_tests[] = { { "a=b;key&c=val", 0, { { "a", "b;key" }, { "c", "val" }, { NULL, NULL }, } }, { "a=b;key=val", 0, { { "a", "b;key=val" }, { NULL, NULL }, } }, { "a;b=val", 0, { { "a;b", "val" }, { NULL, NULL }, } }, { "end_empty_string=", 1 }, { "end_null", 1 }, { "hexa=some%20&hexb=bla%0", 0, { { "hexa", "some%20" }, { "hexb", "bla%0" }, { NULL, NULL }, } }, { "hexa=some%20;hexb=bla", 0, { { "hexa", "some%20;hexb=bla" }, { NULL, NULL }, } }, { "hexa%z=some", 0, { { "hexa%z", "some" }, { NULL, NULL }, } }, { "aaa=some\%az", 1 }, }; struct test ignore_hex_tests[] = { { "hexa=some%20&hexb=bla%0&hexc=%", 0, { { "hexa", "some%20" }, { "hexb", "bla%0" }, { "hexc", "%" }, { NULL, NULL }, } }, { "hexa%z=some", 0, { { "hexa%z", "some" }, { NULL, NULL }, } }, { "aaa=some%zz", 0, { { "aaa", "some%zz" }, { NULL, NULL }, } }, }; struct test allow_empty_tests[] = { { "end_empty_string=", 0, { { "end_empty_string", "" }, { NULL, NULL }, } }, }; struct test allow_null_tests[] = { { "end_null", 0, { { "end_null", NULL }, { NULL, NULL }, } }, }; struct test treat_semicolon_as_sep_tests[] = { { "a=b;key=val", 0, { { "a", "b" }, { "key", "val" }, { NULL, NULL }, } }, { "a;b=val", 1 }, }; struct test lenient_tests[] = { { "a=b;key&c=val", 0, { { "a", "b" }, { "key", NULL }, { "c", "val" }, { NULL, NULL }, } }, { "a=b;key=val", 0, { { "a", "b" }, { "key", "val" }, { NULL, NULL }, } }, { "end_empty_string=", 0, { { "end_empty_string", "" }, { NULL, NULL }, } }, { "end_null", 0, { { "end_null", NULL }, { NULL, NULL }, } }, { "hexa=some\%a;hexb=bl%0&hexc=\%az", 0, { { "hexa", "some\%a" }, { "hexb", "bl%0" }, { "hexc", "\%az" }, { NULL, NULL }, } }, }; static void test(const char * raw_query, int exp_error, struct expected exp[], int flags) { printf(" %-30s ", raw_query); printf("\r %s\n", query_test(raw_query, exp_error, exp, flags) ? "ERROR" : "OK"); } #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) int main(int argc, char ** argv) { int i; #define PARSE_QUERY_TEST(tests, flags) do { \ printf("- " # tests "\n"); \ for (i = 0; i < ARRAY_SIZE(tests); i++) { \ test((tests)[i].raw_query, (tests)[i].exp_error, (tests)[i].exp, flags); \ } \ } while (0) PARSE_QUERY_TEST(base_tests, EVHTP_PARSE_QUERY_FLAG_STRICT); PARSE_QUERY_TEST(ignore_hex_tests, EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX); PARSE_QUERY_TEST(allow_empty_tests, EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS); PARSE_QUERY_TEST(allow_null_tests, EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS); PARSE_QUERY_TEST(treat_semicolon_as_sep_tests, EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP); PARSE_QUERY_TEST(lenient_tests, EVHTP_PARSE_QUERY_FLAG_LENIENT); return 0; } libevhtp-1.2.16/examples/test_vhost.c000066400000000000000000000024451322723041700176100ustar00rootroot00000000000000#include #include #include #include #include #include void testcb(evhtp_request_t * req, void * a) { evbuffer_add_reference(req->buffer_out, "foobar", 6, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } int main(int argc, char ** argv) { evbase_t * evbase = event_base_new(); evhtp_t * evhtp = evhtp_new(evbase, NULL); evhtp_t * v1 = evhtp_new(evbase, NULL); evhtp_t * v2 = evhtp_new(evbase, NULL); evhtp_set_cb(v1, "/host1", NULL, "host1.com"); evhtp_set_cb(v2, "/localhost", testcb, "localhost"); evhtp_add_vhost(evhtp, "host1.com", v1); evhtp_add_vhost(evhtp, "localhost", v2); evhtp_add_alias(v2, "127.0.0.1"); evhtp_add_alias(v2, "localhost"); evhtp_add_alias(v2, "localhost:8081"); #if 0 scfg1.pemfile = "./server.pem"; scfg1.privfile = "./server.pem"; scfg2.pemfile = "./server1.pem"; scfg2.pemfile = "./server1.pem"; evhtp_ssl_init(evhtp, &scfg1); evhtp_ssl_init(v1, &scfg2); evhtp_ssl_init(v2, &scfg2); #endif evhtp_bind_socket(evhtp, "0.0.0.0", 8081, 1024); event_base_loop(evbase, 0); evhtp_unbind_socket(evhtp); evhtp_free(v2); evhtp_free(v1); evhtp_free(evhtp); event_base_free(evbase); return 0; } libevhtp-1.2.16/examples/thread_design.c000066400000000000000000000241441322723041700202060ustar00rootroot00000000000000/* * How to exploit the wonders of libevhtp's threading model to avoid using * libevent's locking API. * * In this example we use Redis's Async API (Libhiredis) store and retr the following * information for a request: * * Total requests seen. * Total requests seen by the requestors IP address. * All of the source ports seen used by the requestors IP address. * * We do this all using libevhtp's builtin thread-pool model, without the use of * mutexes or evthread_use_pthreads() type stuff. * * The technique is simple: * 1. Create your evhtp_t structure, assign callbacks like usual. * 2. Call evhtp_use_threads() with a thread init callback. * 3. Each time a thread starts, the thread init callback you defined will be * called with information about that thread. * * First a bit of information about how evhtp does threading: * libevhtp uses the evthr library, which works more like a threaded * co-routine than a threadpool. Each evthr in a pool has its own unique * event_base (and each evthr runs its own event_base_loop()). Under the * hood when libevhtp sends a request to a thread, it calls * "evthr_pool_defer(pool, _run_connection_in_thread, ...). * * The evthr library then finds a thread inside the pool with the lowest backlog, * sends a packet over that threads socketpair containing information about what * function to execute. It uses socketpairs because they can be treated as * an event, thus able to be processed in a threads own unique * event_base_loop(). * * Knowing that, a connection in evhtp is never associated with the initial * event_base that was passed to evhtp_new(), but instead the connection * uses the evthr's unique event_base. This is what makes libevhtp's * safe from thread-related race conditions. * * 4. Use the thread init callback as a place to put event type things on the * threads event_base() instead of using the global one. * * In this code, that function is app_init_thread(). When this function is * called, the first argument is the evthr_t of the thread that just * started. This function uses "evthr_get_base(thread)" to get the * event_base associated with this specific thread. * * Using that event_base, the function will start up an async redis * connection. This redis connection is now tied to that thread, and can be * used on a threaded request without locking (remember that your request * has the same event_base as the thread it was executed in). * * We allocate a dummy structure "struct app" and then call * "evthr_set_aux(thread, app)". This function sets some aux data which can * be fetched at any point using evthr_get_aux(thread). We use this later on * inside process_request() * * This part is the secret to evhtp threading success. * * 5. When a request has been fully processed, it will call the function * "app_process_request()". Note here that the "arg" argument is NULL since no * arguments were passed to evhtp_set_gencb(). * * Since we want to do a bunch of redis stuff before sending a reply to the * client, we must fetch the "struct app" data we allocated and set for the * thread associated with this request (struct app * app = * evthr_get_aux(thread);). * * struct app has our thread-specific redis connection ctx, so using that * redisAsyncCommand() is called a bunch of times to queue up the commands * which will be run. * * The last part of this technique is to call the function * "evhtp_request_pause()". This essentially tells evhtp to flip the * read-side of the connections file-descriptor OFF (This avoids potential * situations where a client disconnected before all of the redis commands * executed). * * 6. Each redis command is executed in order, and each callback will write to * the requests output_buffer with relevant information from the result. * * 7. The last redis callback executed here is "redis_get_srcport_cb". It is * the job os this function to call evhtp_send_reply() and then * evhtp_request_resume(). * * Using this design in conjunction with libevhtp makes the world an easier * place to code. * * Compile: gcc thread_design.c -o thread_design -levhtp -levent -lhiredis * */ #include #include #include #include #include #include #include #include #include struct app_parent { evhtp_t * evhtp; evbase_t * evbase; char * redis_host; uint16_t redis_port; }; struct app { struct app_parent * parent; evbase_t * evbase; redisAsyncContext * redis; }; static evthr_t * get_request_thr(evhtp_request_t * request) { evhtp_connection_t * htpconn; evthr_t * thread; htpconn = evhtp_request_get_connection(request); thread = htpconn->thread; return thread; } void redis_global_incr_cb(redisAsyncContext * redis, void * redis_reply, void * arg) { redisReply * reply = redis_reply; evhtp_request_t * request = arg; printf("global_incr_cb(%p)\n", request); if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) { evbuffer_add_printf(request->buffer_out, "redis_global_incr_cb() failed\n"); return; } evbuffer_add_printf(request->buffer_out, "Total requests = %lld\n", reply->integer); } void redis_srcaddr_incr_cb(redisAsyncContext * redis, void * redis_reply, void * arg) { redisReply * reply = redis_reply; evhtp_request_t * request = arg; printf("incr_cb(%p)\n", request); if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) { evbuffer_add_printf(request->buffer_out, "redis_srcaddr_incr_cb() failed\n"); return; } evbuffer_add_printf(request->buffer_out, "Requests from this source IP = %lld\n", reply->integer); } void redis_set_srcport_cb(redisAsyncContext * redis, void * redis_reply, void * arg) { redisReply * reply = redis_reply; evhtp_request_t * request = arg; printf("set_srcport_cb(%p)\n", request); if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) { evbuffer_add_printf(request->buffer_out, "redis_set_srcport_cb() failed\n"); return; } if (!reply->integer) { evbuffer_add_printf(request->buffer_out, "This source port has been seen already.\n"); } else { evbuffer_add_printf(request->buffer_out, "This source port has never been seen.\n"); } } void redis_get_srcport_cb(redisAsyncContext * redis, void * redis_reply, void * arg) { redisReply * reply = redis_reply; evhtp_request_t * request = arg; int i; printf("get_srcport_cb(%p)\n", request); if (reply == NULL || reply->type != REDIS_REPLY_ARRAY) { evbuffer_add_printf(request->buffer_out, "redis_get_srcport_cb() failed.\n"); return; } evbuffer_add_printf(request->buffer_out, "source ports which have been seen for your ip:\n"); for (i = 0; i < reply->elements; i++) { redisReply * elem = reply->element[i]; evbuffer_add_printf(request->buffer_out, "%s ", elem->str); } evbuffer_add(request->buffer_out, "\n", 1); /* final callback for redis, so send the response */ evhtp_send_reply(request, EVHTP_RES_OK); evhtp_request_resume(request); } void app_process_request(evhtp_request_t * request, void * arg) { struct sockaddr_in * sin; struct app_parent * app_parent; struct app * app; evthr_t * thread; evhtp_connection_t * conn; char tmp[1024]; printf("process_request(%p)\n", request); thread = get_request_thr(request); conn = evhtp_request_get_connection(request); app = (struct app *)evthr_get_aux(thread); sin = (struct sockaddr_in *)conn->saddr; evutil_inet_ntop(AF_INET, &sin->sin_addr, tmp, sizeof(tmp)); /* increment a global counter of hits on redis */ redisAsyncCommand(app->redis, redis_global_incr_cb, (void *)request, "INCR requests:total"); /* increment a counter for hits from this source address on redis */ redisAsyncCommand(app->redis, redis_srcaddr_incr_cb, (void *)request, "INCR requests:ip:%s", tmp); /* add the source port of this request to a source-specific set */ redisAsyncCommand(app->redis, redis_set_srcport_cb, (void *)request, "SADD requests:ip:%s:ports %d", tmp, ntohs(sin->sin_port)); /* get all of the ports this source address has used */ redisAsyncCommand(app->redis, redis_get_srcport_cb, (void *)request, "SMEMBERS requests:ip:%s:ports", tmp); /* pause the request processing */ evhtp_request_pause(request); } void app_init_thread(evhtp_t * htp, evthr_t * thread, void * arg) { struct app_parent * app_parent; struct app * app; app_parent = (struct app_parent *)arg; app = calloc(sizeof(struct app), 1); app->parent = app_parent; app->evbase = evthr_get_base(thread); app->redis = redisAsyncConnect(app_parent->redis_host, app_parent->redis_port); redisLibeventAttach(app->redis, app->evbase); evthr_set_aux(thread, app); } int main(int argc, char ** argv) { evbase_t * evbase; evhtp_t * evhtp; struct app_parent * app_p; evbase = event_base_new(); evhtp = evhtp_new(evbase, NULL); app_p = calloc(sizeof(struct app_parent), 1); app_p->evhtp = evhtp; app_p->evbase = evbase; app_p->redis_host = "127.0.0.1"; app_p->redis_port = 6379; evhtp_set_gencb(evhtp, app_process_request, NULL); evhtp_use_threads(evhtp, app_init_thread, 4, app_p); evhtp_bind_socket(evhtp, "127.0.0.1", 9090, 1024); event_base_loop(evbase, 0); return 0; } libevhtp-1.2.16/examples/v6_v4.c000066400000000000000000000022171322723041700163470ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "evhtp-internal.h" #include "evhtp.h" static void _req_cb(evhtp_request_t * req, void * arg) { const char * ver = (const char *)arg; evbuffer_add(req->buffer_out, ver, strlen(ver)); evhtp_send_reply(req, EVHTP_RES_OK); } int main(int argc, char ** argv) { struct event_base * evbase; evhtp_t * htp_v6; evhtp_t * htp_v4; int rc; evbase = event_base_new(); evhtp_alloc_assert(evbase); htp_v6 = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp_v6); htp_v4 = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp_v4); evhtp_set_gencb(htp_v6, _req_cb, (void *)"ipv6"); evhtp_set_gencb(htp_v4, _req_cb, (void *)"ipv4"); rc = evhtp_bind_socket(htp_v6, "ipv6:::/128", 9090, 1024); evhtp_errno_assert(rc != -1); rc = evhtp_bind_socket(htp_v4, "ipv4:0.0.0.0", 9090, 1024); evhtp_errno_assert(rc != -1); event_base_loop(evbase, 0); return 0; } /* main */libevhtp-1.2.16/include/000077500000000000000000000000001322723041700150425ustar00rootroot00000000000000libevhtp-1.2.16/include/evhtp.h000066400000000000000000000001621322723041700163400ustar00rootroot00000000000000/** * @file evhtp.h * */ #ifndef __EVHTP_BASE_H__ #define __EVHTP_BASE_H__ #include #endif libevhtp-1.2.16/include/evhtp/000077500000000000000000000000001322723041700161705ustar00rootroot00000000000000libevhtp-1.2.16/include/evhtp/config.h.in000066400000000000000000000037431322723041700202220ustar00rootroot00000000000000#ifndef __EVHTP_CONFIG_H__ #define __EVHTP_CONFIG_H__ #ifdef __cplusplus extern "C" { #endif #ifndef EVHTP_EXPORT # if (defined __GNUC__ && __GNUC__ >= 4) || defined __INTEL_COMPILER || defined __clang__ # define EVHTP_EXPORT __attribute__ ((visibility("default"))) # else # define EVHTP_EXPORT # endif #endif #if defined(_MSC_VER) && _MSC_VER >= 1500 /* MSVC 2008 */ # define DEPRECATED(message) __declspec(deprecated(message)) #elif defined(__clang__) && defined(__has_feature) # if __has_feature(attribute_deprecated_with_message) # define DEPRECATED(message) __attribute__ ((deprecated(message))) # endif # elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) # define DEPRECATED(message) __attribute__ ((deprecated(message))) # elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) # define DEPRECATED(message) __attribute__((__deprecated__)) # else # define DEPRECATED(message) #endif #undef EVHTP_DISABLE_EVTHR #undef EVHTP_DISABLE_REGEX #undef EVHTP_DISABLE_SSL #undef EVHTP_DISABLE_EVTHR #undef EVHTP_DEBUG #cmakedefine EVHTP_DISABLE_EVTHR #cmakedefine EVHTP_DISABLE_REGEX #cmakedefine EVHTP_DISABLE_SSL #cmakedefine EVHTP_DISABLE_EVTHR #cmakedefine EVHTP_USE_TCMALLOC #cmakedefine EVHTP_USE_JEMALLOC #cmakedefine EVHTP_USE_TCMALLOC #cmakedefine EVHTP_DEBUG #cmakedefine EVHTP_DISABLE_MEMFUNCTIONS #ifndef EVHTP_DISABLE_REGEX #include #endif #ifdef EVHTP_USE_TCMALLOC #include #define malloc(size) tc_malloc(size) #define calloc(count, size) tc_calloc(count, size) #define realloc(ptr, size) tc_realloc(ptr, size) #define free(ptr) tc_free(ptr) #endif #ifdef EVHTP_USE_JEMALLOC #define JEMALLOC_NO_DEMANGLE #include #define malloc(size) je_malloc(size) #define calloc(count, size) je_calloc(count, size) #define realloc(ptr, size) je_realloc(ptr, size) #define free(ptr) je_free(ptr) #endif #ifdef __cplusplus } #endif #endif libevhtp-1.2.16/include/evhtp/evhtp.h000066400000000000000000001343311322723041700174740ustar00rootroot00000000000000/** * @file evhtp.h */ #include #ifndef __EVHTP__H__ #define __EVHTP__H__ /** @file */ #ifndef EVHTP_DISABLE_EVTHR #include #endif #include #ifndef EVHTP_DISABLE_REGEX #include #endif #include #include #include #include #include #ifndef EVHTP_DISABLE_SSL #include #include #include #include #endif #ifdef __cplusplus extern "C" { #endif struct evhtp_callback_s; struct evhtp_callbacks_s; #ifndef EVHTP_DISABLE_SSL typedef SSL_SESSION evhtp_ssl_sess_t; typedef SSL evhtp_ssl_t; typedef SSL_CTX evhtp_ssl_ctx_t; typedef X509 evhtp_x509_t; typedef X509_STORE_CTX evhtp_x509_store_ctx_t; #if OPENSSL_VERSION_NUMBER < 0x10100000L typedef unsigned char evhtp_ssl_data_t; #else typedef const unsigned char evhtp_ssl_data_t; #endif #else typedef void evhtp_ssl_sess_t; typedef void evhtp_ssl_t; typedef void evhtp_ssl_ctx_t; typedef void evhtp_x509_t; typedef void evhtp_x509_store_ctx_t; #endif typedef struct evbuffer evbuf_t; typedef struct event event_t; typedef struct evconnlistener evserv_t; typedef struct bufferevent evbev_t; #ifdef EVHTP_DISABLE_EVTHR typedef struct event_base evbase_t; typedef void evthr_t; typedef void evthr_pool_t; typedef void evhtp_mutex_t; #else typedef pthread_mutex_t evhtp_mutex_t; #endif typedef struct evhtp_s evhtp_t; typedef struct evhtp_defaults_s evhtp_defaults_t; typedef struct evhtp_callbacks_s evhtp_callbacks_t; typedef struct evhtp_callback_s evhtp_callback_t; typedef struct evhtp_defaults_s evhtp_defaults_5; typedef struct evhtp_kv_s evhtp_kv_t; typedef struct evhtp_kvs_s evhtp_kvs_t; typedef struct evhtp_uri_s evhtp_uri_t; typedef struct evhtp_path_s evhtp_path_t; typedef struct evhtp_authority_s evhtp_authority_t; typedef struct evhtp_request_s evhtp_request_t; typedef struct evhtp_hooks_s evhtp_hooks_t; typedef struct evhtp_connection_s evhtp_connection_t; typedef struct evhtp_ssl_cfg_s evhtp_ssl_cfg_t; typedef struct evhtp_alias_s evhtp_alias_t; typedef uint16_t evhtp_res; typedef uint8_t evhtp_error_flags; #define evhtp_header_s evhtp_kv_s #define evhtp_headers_s evhtp_kvs_s #define evhtp_query_s evhtp_kvs_s #define evhtp_header_t evhtp_kv_t #define evhtp_headers_t evhtp_kvs_t #define evhtp_query_t evhtp_kvs_t enum evhtp_ssl_scache_type { evhtp_ssl_scache_type_disabled = 0, evhtp_ssl_scache_type_internal, evhtp_ssl_scache_type_user, evhtp_ssl_scache_type_builtin }; /** * @brief types associated with where a developer can hook into * during the request processing cycle. */ enum evhtp_hook_type { evhtp_hook_on_header, /**< type which defines to hook after one header has been parsed */ evhtp_hook_on_headers, /**< type which defines to hook after all headers have been parsed */ evhtp_hook_on_path, /**< type which defines to hook once a path has been parsed */ evhtp_hook_on_read, /**< type which defines to hook whenever the parser recieves data in a body */ evhtp_hook_on_request_fini, /**< type which defines to hook before the request is free'd */ evhtp_hook_on_connection_fini, evhtp_hook_on_new_chunk, evhtp_hook_on_chunk_complete, evhtp_hook_on_chunks_complete, evhtp_hook_on_headers_start, evhtp_hook_on_error, /**< type which defines to hook whenever an error occurs */ evhtp_hook_on_hostname, evhtp_hook_on_write, evhtp_hook_on_event, evhtp_hook_on_conn_error, /**< type which defines to hook whenever a connection error occurs */ }; enum evhtp_callback_type { evhtp_callback_type_hash, evhtp_callback_type_glob, #ifndef EVHTP_DISABLE_REGEX evhtp_callback_type_regex, #endif }; enum evhtp_proto { EVHTP_PROTO_INVALID, EVHTP_PROTO_10, EVHTP_PROTO_11 }; enum evhtp_type { evhtp_type_client, evhtp_type_server }; typedef enum evhtp_hook_type evhtp_hook_type; typedef enum evhtp_callback_type evhtp_callback_type; typedef enum evhtp_proto evhtp_proto; typedef enum evhtp_ssl_scache_type evhtp_ssl_scache_type; typedef enum evhtp_type evhtp_type; typedef void (* evhtp_thread_init_cb)(evhtp_t * htp, evthr_t * thr, void * arg); typedef void (* evhtp_thread_exit_cb)(evhtp_t * htp, evthr_t * thr, void * arg); typedef void (* evhtp_callback_cb)(evhtp_request_t * req, void * arg); typedef void (* evhtp_hook_err_cb)(evhtp_request_t * req, evhtp_error_flags errtype, void * arg); typedef void (* evhtp_hook_event_cb)(evhtp_connection_t * conn, short events, void * arg); /* Generic hook for passing ISO tests */ typedef evhtp_res (* evhtp_hook)(); typedef evhtp_res (* evhtp_hook_conn_err_cb)(evhtp_connection_t * connection, evhtp_error_flags errtype, void * arg); typedef evhtp_res (* evhtp_pre_accept_cb)(evhtp_connection_t * conn, void * arg); typedef evhtp_res (* evhtp_post_accept_cb)(evhtp_connection_t * conn, void * arg); typedef evhtp_res (* evhtp_hook_header_cb)(evhtp_request_t * req, evhtp_header_t * hdr, void * arg); typedef evhtp_res (* evhtp_hook_headers_cb)(evhtp_request_t * req, evhtp_headers_t * hdr, void * arg); typedef evhtp_res (* evhtp_hook_path_cb)(evhtp_request_t * req, evhtp_path_t * path, void * arg); typedef evhtp_res (* evhtp_hook_read_cb)(evhtp_request_t * req, struct evbuffer * buf, void * arg); typedef evhtp_res (* evhtp_hook_request_fini_cb)(evhtp_request_t * req, void * arg); typedef evhtp_res (* evhtp_hook_connection_fini_cb)(evhtp_connection_t * connection, void * arg); typedef evhtp_res (* evhtp_hook_chunk_new_cb)(evhtp_request_t * r, uint64_t len, void * arg); typedef evhtp_res (* evhtp_hook_chunk_fini_cb)(evhtp_request_t * r, void * arg); typedef evhtp_res (* evhtp_hook_chunks_fini_cb)(evhtp_request_t * r, void * arg); typedef evhtp_res (* evhtp_hook_headers_start_cb)(evhtp_request_t * r, void * arg); typedef evhtp_res (* evhtp_hook_hostname_cb)(evhtp_request_t * r, const char * hostname, void * arg); typedef evhtp_res (* evhtp_hook_write_cb)(evhtp_connection_t * conn, void * arg); typedef int (* evhtp_kvs_iterator)(evhtp_kv_t * kv, void * arg); typedef int (* evhtp_headers_iterator)(evhtp_header_t * header, void * arg); #ifndef EVHTP_DISABLE_SSL typedef int (* evhtp_ssl_verify_cb)(int pre_verify, evhtp_x509_store_ctx_t * ctx); typedef int (* evhtp_ssl_chk_issued_cb)(evhtp_x509_store_ctx_t * ctx, evhtp_x509_t * x, evhtp_x509_t * issuer); typedef EVP_PKEY * (* evhtp_ssl_decrypt_cb)(char * privfile); typedef int (* evhtp_ssl_scache_add)(evhtp_connection_t * connection, evhtp_ssl_data_t * sid, int sid_len, evhtp_ssl_sess_t * sess); typedef void (* evhtp_ssl_scache_del)(evhtp_t * htp, evhtp_ssl_data_t * sid, int sid_len); typedef evhtp_ssl_sess_t * (* evhtp_ssl_scache_get)(evhtp_connection_t * connection, evhtp_ssl_data_t * sid, int sid_len); typedef void * (* evhtp_ssl_scache_init)(evhtp_t *); #endif #define EVHTP_VERSION "1.2.16" #define EVHTP_VERSION_MAJOR 1 #define EVHTP_VERSION_MINOR 2 #define EVHTP_VERSION_PATCH 15 #define evhtp_headers_iterator evhtp_kvs_iterator #define EVHTP_RES_ERROR 0 #define EVHTP_RES_PAUSE 1 #define EVHTP_RES_FATAL 2 #define EVHTP_RES_USER 3 #define EVHTP_RES_DATA_TOO_LONG 4 #define EVHTP_RES_OK 200 #ifndef DOXYGEN_SHOULD_SKIP_THIS #define EVHTP_RES_100 100 #define EVHTP_RES_CONTINUE 100 #define EVHTP_RES_SWITCH_PROTO 101 #define EVHTP_RES_PROCESSING 102 #define EVHTP_RES_URI_TOOLONG 122 #define EVHTP_RES_200 200 #define EVHTP_RES_CREATED 201 #define EVHTP_RES_ACCEPTED 202 #define EVHTP_RES_NAUTHINFO 203 #define EVHTP_RES_NOCONTENT 204 #define EVHTP_RES_RSTCONTENT 205 #define EVHTP_RES_PARTIAL 206 #define EVHTP_RES_MSTATUS 207 #define EVHTP_RES_IMUSED 226 #define EVHTP_RES_300 300 #define EVHTP_RES_MCHOICE 300 #define EVHTP_RES_MOVEDPERM 301 #define EVHTP_RES_FOUND 302 #define EVHTP_RES_SEEOTHER 303 #define EVHTP_RES_NOTMOD 304 #define EVHTP_RES_USEPROXY 305 #define EVHTP_RES_SWITCHPROXY 306 #define EVHTP_RES_TMPREDIR 307 #define EVHTP_RES_400 400 #define EVHTP_RES_BADREQ 400 #define EVHTP_RES_UNAUTH 401 #define EVHTP_RES_PAYREQ 402 #define EVHTP_RES_FORBIDDEN 403 #define EVHTP_RES_NOTFOUND 404 #define EVHTP_RES_METHNALLOWED 405 #define EVHTP_RES_NACCEPTABLE 406 #define EVHTP_RES_PROXYAUTHREQ 407 #define EVHTP_RES_TIMEOUT 408 #define EVHTP_RES_CONFLICT 409 #define EVHTP_RES_GONE 410 #define EVHTP_RES_LENREQ 411 #define EVHTP_RES_PRECONDFAIL 412 #define EVHTP_RES_ENTOOLARGE 413 #define EVHTP_RES_URITOOLARGE 414 #define EVHTP_RES_UNSUPPORTED 415 #define EVHTP_RES_RANGENOTSC 416 #define EVHTP_RES_EXPECTFAIL 417 #define EVHTP_RES_IAMATEAPOT 418 #define EVHTP_RES_500 500 #define EVHTP_RES_SERVERR 500 #define EVHTP_RES_NOTIMPL 501 #define EVHTP_RES_BADGATEWAY 502 #define EVHTP_RES_SERVUNAVAIL 503 #define EVHTP_RES_GWTIMEOUT 504 #define EVHTP_RES_VERNSUPPORT 505 #define EVHTP_RES_BWEXEED 509 #endif struct evhtp_defaults_s { evhtp_callback_cb cb; evhtp_pre_accept_cb pre_accept; evhtp_post_accept_cb post_accept; void * cbarg; void * pre_accept_cbarg; void * post_accept_cbarg; }; struct evhtp_alias_s { char * alias; TAILQ_ENTRY(evhtp_alias_s) next; }; /** * @ingroup evhtp_core * @brief main structure containing all configuration information */ struct evhtp_s { evhtp_t * parent; /**< only when this is a vhost */ struct event_base * evbase; /**< the initialized event_base */ struct evconnlistener * server; /**< the libevent listener struct */ char * server_name; /**< the name included in Host: responses */ void * arg; /**< user-defined evhtp_t specific arguments */ int bev_flags; /**< bufferevent flags to use on bufferevent_*_socket_new() */ uint64_t max_body_size; uint64_t max_keepalive_requests; #define EVHTP_FLAG_ENABLE_100_CONT (1 << 1) #define EVHTP_FLAG_ENABLE_REUSEPORT (1 << 2) #define EVHTP_FLAG_ENABLE_NODELAY (1 << 3) #define EVHTP_FLAG_ENABLE_DEFER_ACCEPT (1 << 4) #define EVHTP_FLAG_DEFAULTS EVHTP_FLAG_ENABLE_100_CONT #define EVHTP_FLAG_ENABLE_ALL EVHTP_FLAG_ENABLE_100_CONT \ | EVHTP_FLAG_ENABLE_REUSEPORT \ | EVHTP_FLAG_ENABLE_NODELAY \ | EVHTP_FLAG_ENABLE_DEFER_ACCEPT uint16_t flags; /**< the base flags set for this context, see: EVHTP_FLAG_* */ uint16_t parser_flags; /**< default query flags to alter 'strictness' (see EVHTP_PARSE_QUERY_FLAG_*) */ #ifndef EVHTP_DISABLE_SSL evhtp_ssl_ctx_t * ssl_ctx; /**< if ssl enabled, this is the servers CTX */ evhtp_ssl_cfg_t * ssl_cfg; #endif #ifndef EVHTP_DISABLE_EVTHR evthr_pool_t * thr_pool; /**< connection threadpool */ pthread_mutex_t * lock; /**< parent lock for add/del cbs in threads */ evhtp_thread_init_cb thread_init_cb; evhtp_thread_exit_cb thread_exit_cb; /* keep backwards compat because I'm dumb and didn't * make these structs private */ #define thread_init_cbarg thread_cbarg void * thread_cbarg; #endif evhtp_callbacks_t * callbacks; evhtp_defaults_t defaults; struct timeval recv_timeo; struct timeval send_timeo; TAILQ_HEAD(, evhtp_alias_s) aliases; TAILQ_HEAD(, evhtp_s) vhosts; TAILQ_ENTRY(evhtp_s) next_vhost; }; /** * @brief a generic key/value structure */ struct evhtp_kv_s { char * key; char * val; size_t klen; size_t vlen; char k_heaped; /**< set to 1 if the key can be free()'d */ char v_heaped; /**< set to 1 if the val can be free()'d */ TAILQ_ENTRY(evhtp_kv_s) next; }; TAILQ_HEAD(evhtp_kvs_s, evhtp_kv_s); /** * @brief a generic container representing an entire URI strucutre */ struct evhtp_uri_s { evhtp_authority_t * authority; evhtp_path_t * path; unsigned char * fragment; /**< data after '#' in uri */ unsigned char * query_raw; /**< the unparsed query arguments */ evhtp_query_t * query; /**< list of k/v for query arguments */ htp_scheme scheme; /**< set if a scheme is found */ }; /** * @brief structure which represents authority information in a URI */ struct evhtp_authority_s { char * username; /**< the username in URI (scheme://USER:.. */ char * password; /**< the password in URI (scheme://...:PASS.. */ char * hostname; /**< hostname if present in URI */ uint16_t port; /**< port if present in URI */ }; /** * @brief structure which represents a URI path and or file */ struct evhtp_path_s { char * full; /**< the full path+file (/a/b/c.html) */ char * path; /**< the path (/a/b/) */ char * file; /**< the filename if present (c.html) */ char * match_start; char * match_end; unsigned int matched_soff; /**< offset of where the uri starts * mainly used for regex matching */ unsigned int matched_eoff; /**< offset of where the uri ends * mainly used for regex matching */ }; /** * @brief a structure containing all information for a http request. */ struct evhtp_request_s { evhtp_t * htp; /**< the parent evhtp_t structure */ evhtp_connection_t * conn; /**< the associated connection */ evhtp_hooks_t * hooks; /**< request specific hooks */ evhtp_uri_t * uri; /**< request URI information */ struct evbuffer * buffer_in; /**< buffer containing data from client */ struct evbuffer * buffer_out; /**< buffer containing data to client */ evhtp_headers_t * headers_in; /**< headers from client */ evhtp_headers_t * headers_out; /**< headers to client */ evhtp_proto proto; /**< HTTP protocol used */ htp_method method; /**< HTTP method used */ evhtp_res status; /**< The HTTP response code or other error conditions */ #define EVHTP_REQ_FLAG_KEEPALIVE (1 << 1) #define EVHTP_REQ_FLAG_FINISHED (1 << 2) #define EVHTP_REQ_FLAG_CHUNKED (1 << 3) #define EVHTP_REQ_FLAG_ERROR (1 << 4) uint16_t flags; evhtp_callback_cb cb; /**< the function to call when fully processed */ void * cbarg; /**< argument which is passed to the cb function */ TAILQ_ENTRY(evhtp_request_s) next; }; #define evhtp_request_content_len(r) htparser_get_content_length(r->conn->parser) struct evhtp_connection_s { evhtp_t * htp; struct event_base * evbase; struct bufferevent * bev; #ifndef EVHTP_DISABLE_EVTHR evthr_t * thread; #endif #ifndef EVHTP_DISABLE_SSL evhtp_ssl_t * ssl; #endif evhtp_hooks_t * hooks; htparser * parser; struct event * resume_ev; struct sockaddr * saddr; struct timeval recv_timeo; /**< conn read timeouts (overrides global) */ struct timeval send_timeo; /**< conn write timeouts (overrides global) */ evutil_socket_t sock; evhtp_request_t * request; /**< the request currently being processed */ uint64_t max_body_size; uint64_t body_bytes_read; uint64_t num_requests; evhtp_type type; /**< server or client */ #define EVHTP_CONN_FLAG_ERROR (1 << 1) #define EVHTP_CONN_FLAG_OWNER (1 << 2) /**< set to 1 if this structure owns the bufferevent */ #define EVHTP_CONN_FLAG_VHOST_VIA_SNI (1 << 3) /**< set to 1 if the vhost was found via SSL SNI */ #define EVHTP_CONN_FLAG_PAUSED (1 << 4) /**< this connection has been marked as paused */ #define EVHTP_CONN_FLAG_CONNECTED (1 << 5) /**< client specific - set after successful connection */ #define EVHTP_CONN_FLAG_WAITING (1 << 6) /**< used to make sure resuming happens AFTER sending a reply */ #define EVHTP_CONN_FLAG_FREE_CONN (1 << 7) #define EVHTP_CONN_FLAG_KEEPALIVE (1 << 8) /**< set to 1 after the first request has been processed and the connection is kept open */ uint16_t flags; struct evbuffer * scratch_buf; /**< always zero'd out after used */ #ifdef EVHTP_FUTURE_USE TAILQ_HEAD(, evhtp_request_s) pending; /**< client pending data */ #endif }; struct evhtp_hooks_s { evhtp_hook_headers_start_cb on_headers_start; evhtp_hook_header_cb on_header; evhtp_hook_headers_cb on_headers; evhtp_hook_path_cb on_path; evhtp_hook_read_cb on_read; evhtp_hook_request_fini_cb on_request_fini; evhtp_hook_connection_fini_cb on_connection_fini; evhtp_hook_conn_err_cb on_connection_error; evhtp_hook_err_cb on_error; evhtp_hook_chunk_new_cb on_new_chunk; evhtp_hook_chunk_fini_cb on_chunk_fini; evhtp_hook_chunks_fini_cb on_chunks_fini; evhtp_hook_hostname_cb on_hostname; evhtp_hook_write_cb on_write; evhtp_hook_event_cb on_event; void * on_headers_start_arg; void * on_header_arg; void * on_headers_arg; void * on_path_arg; void * on_read_arg; void * on_request_fini_arg; void * on_connection_fini_arg; void * on_connection_error_arg; void * on_error_arg; void * on_new_chunk_arg; void * on_chunk_fini_arg; void * on_chunks_fini_arg; void * on_hostname_arg; void * on_write_arg; void * on_event_arg; }; #ifndef EVHTP_DISABLE_SSL struct evhtp_ssl_cfg_s { char * pemfile; char * privfile; char * cafile; char * capath; char * ciphers; char * named_curve; char * dhparams; long ssl_opts; long ssl_ctx_timeout; int verify_peer; int verify_depth; evhtp_ssl_verify_cb x509_verify_cb; evhtp_ssl_chk_issued_cb x509_chk_issued_cb; evhtp_ssl_decrypt_cb decrypt_cb; long store_flags; evhtp_ssl_scache_type scache_type; long scache_timeout; long scache_size; evhtp_ssl_scache_init scache_init; evhtp_ssl_scache_add scache_add; evhtp_ssl_scache_get scache_get; evhtp_ssl_scache_del scache_del; void * args; }; #endif EVHTP_EXPORT void evhtp_set_mem_functions(void *(*malloc_)(size_t), void *(*realloc_)(void *, size_t), void (* free_)(void *)); /** * @defgroup evhtp_core Core evhtp functions * @{ */ /** * @brief creates a new evhtp_t instance * * @param evbase the initialized event base * @param arg user-defined argument which is evhtp_t specific * * @return a new evhtp_t structure or NULL on error */ EVHTP_EXPORT evhtp_t * evhtp_new(struct event_base * evbase, void * arg); EVHTP_EXPORT void evhtp_enable_flag(evhtp_t *, int); EVHTP_EXPORT void evhtp_connection_enable_flag(evhtp_connection_t *, int); EVHTP_EXPORT void evhtp_request_enable_flag(evhtp_request_t *, int); EVHTP_EXPORT int evhtp_get_flags(evhtp_t *); EVHTP_EXPORT int evhtp_connection_get_flags(evhtp_connection_t *); EVHTP_EXPORT int evhtp_request_get_flags(evhtp_request_t *); EVHTP_EXPORT void evhtp_disable_flag(evhtp_t *, int); EVHTP_EXPORT void evhtp_connection_disable_flag(evhtp_connection_t *, int); EVHTP_EXPORT void evhtp_request_disable_flag(evhtp_request_t *, int); /** * @brief Frees evhtp_t structure; will stop and free threads associated * with the structure, and free the ssl context as well (if applicable). * * @param evhtp - ptr to evhtp_t structure * */ EVHTP_EXPORT void evhtp_free(evhtp_t * evhtp); /** @} */ /** * @brief set a read/write timeout on all things evhtp_t. When the timeout * expires your error hook will be called with the libevent supplied event * flags. * * @param htp the base evhtp_t struct * @param r read-timeout in timeval * @param w write-timeout in timeval. */ EVHTP_EXPORT void evhtp_set_timeouts(evhtp_t * htp, const struct timeval * r, const struct timeval * w); /** * @brief during the request processing cycle, these flags will be used to * for query argument parsing. i.e., what to parse and not to parse. * * SEE: EVHTP_PARSE_QUERY_* stuff. * * For example, if you do not wish for the streaming parser attempting the act * of fragment parsing: * evhtp_set_parser_flags(htp, EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS); * * @param htp * @param flags */ EVHTP_EXPORT void evhtp_set_parser_flags(evhtp_t * htp, int flags); /** * @brief bufferevent flags which will be used for bev sockets. * * @param htp * @param flags */ EVHTP_EXPORT void evhtp_set_bev_flags(evhtp_t * htp, int flags); #ifndef EVHTP_DISABLE_SSL EVHTP_EXPORT int evhtp_ssl_use_threads(void); EVHTP_EXPORT int evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * ssl_cfg); #endif /** * @brief when a client sends an Expect: 100-continue, if this is function is * called, evhtp will not send a HTTP/x.x continue response. * * @param htp */ EVHTP_EXPORT void evhtp_disable_100_continue(evhtp_t * htp) DEPRECATED("evhtp_disable_100 will soon be deprecated, use htp->flags instead"); /** * @brief creates a lock around callbacks and hooks, allowing for threaded * applications to add/remove/modify hooks & callbacks in a thread-safe manner. * * @param htp * * @return 0 on success, -1 on error */ EVHTP_EXPORT int evhtp_use_callback_locks(evhtp_t * htp); /** * @brief sets a callback which is called if no other callbacks are matched * * @param htp the initialized evhtp_t * @param cb the function to be executed * @param arg user-defined argument passed to the callback */ EVHTP_EXPORT void evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg); /** * @brief call a user-defined function before the connection is accepted. * * @param htp * @param evhtp_pre_accept_cb * @param arg * * @return */ EVHTP_EXPORT void evhtp_set_pre_accept_cb(evhtp_t * htp, evhtp_pre_accept_cb, void * arg); /** * @brief call a user-defined function right after a connection is accepted. * * @param htp * @param evhtp_post_accept_cb * @param arg * * @return */ EVHTP_EXPORT void evhtp_set_post_accept_cb(evhtp_t * htp, evhtp_post_accept_cb, void * arg); /** * @brief sets a callback to be executed on a specific path * * @param htp the initialized evhtp_t * @param path the path to match * @param cb the function to be executed * @param arg user-defined argument passed to the callback * * @return evhtp_callback_t * on success, NULL on error. */ EVHTP_EXPORT evhtp_callback_t * evhtp_set_cb(evhtp_t * htp, const char * path, evhtp_callback_cb cb, void * arg); /** * @brief sets a callback to be executed based on a regex pattern * * @param htp the initialized evhtp_t * @param pattern a POSIX compat regular expression * @param cb the function to be executed * @param arg user-defined argument passed to the callback * * @return evhtp_callback_t * on success, NULL on error */ #ifndef EVHTP_DISABLE_REGEX EVHTP_EXPORT evhtp_callback_t * evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg); #endif /** * @brief sets a callback to to be executed on simple glob/wildcard patterns * this is useful if the app does not care about what was matched, but * just that it matched. This is technically faster than regex. * * @param htp * @param pattern wildcard pattern, the '*' can be set at either or both the front or end. * @param cb * @param arg * * @return */ EVHTP_EXPORT evhtp_callback_t * evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg); /** * @brief attempts to find the callback matching the exact string 'needle'. This is useful * in cases where we want to get the original handle, but is not in scope. * * with pattern based callbacks, this does not attempt to find a callback that would * match the string if the pattern matcher was executed. * * Meaning: * evhtp_set_glob_cb(htp, "/foo/bar*", ....); * * Calling * evhtp_get_cb(htp, "/foo/bar/baz"); * * Will return NULL since it's not the exact pattern set * * Calling * evhtp_get_cb(htp, "/foo/bar*"); * * Is the correct usage. * * @param htp * @param needle * * @return NULL if callback is not not found */ EVHTP_EXPORT evhtp_callback_t * evhtp_get_cb(evhtp_t * htp, const char * needle); /** * @brief sets a callback hook for either a connection or a path/regex . * * A user may set a variety of hooks either per-connection, or per-callback. * This allows the developer to hook into various parts of the request processing * cycle. * * a per-connection hook can be set at any time, but it is recommended to set these * during either a pre-accept phase, or post-accept phase. This allows a developer * to set hooks before any other hooks are called. * * a per-callback hook works differently. In this mode a developer can setup a set * of hooks prior to starting the event loop for specific callbacks. For example * if you wanted to hook something ONLY for a callback set by evhtp_set_cb or * evhtp_set_regex_cb this is the method of doing so. * * per-callback example: * * evhtp_callback_t * cb = evhtp_set_regex_cb(htp, "/anything/(.*)", default_cb, NULL); * * evhtp_set_hook(&cb->hooks, evhtp_hook_on_headers, anything_headers_cb, NULL); * * evhtp_set_hook(&cb->hooks, evhtp_hook_on_fini, anything_fini_cb, NULL); * * With the above example, once libevhtp has determined that it has a user-defined * callback for /anything/.*; anything_headers_cb will be executed after all headers * have been parsed, and anything_fini_cb will be executed before the request is * free()'d. * * The same logic applies to per-connection hooks, but it should be noted that if * a per-callback hook is set, the per-connection hook will be ignored. * * @param hooks double pointer to the evhtp_hooks_t structure * @param type the hook type * @param cb the callback to be executed. * @param arg optional argument which is passed when the callback is executed * * @return 0 on success, -1 on error (if hooks is NULL, it is allocated) */ EVHTP_EXPORT int evhtp_connection_set_hook(evhtp_connection_t * c, evhtp_hook_type type, evhtp_hook cb, void * arg); EVHTP_EXPORT int evhtp_request_set_hook(evhtp_request_t * r, evhtp_hook_type type, evhtp_hook cb, void * arg); EVHTP_EXPORT int evhtp_callback_set_hook(evhtp_callback_t * cb, evhtp_hook_type type, evhtp_hook hookcb, void * arg); EVHTP_EXPORT evhtp_hooks_t * evhtp_connection_get_hooks(evhtp_connection_t * c); EVHTP_EXPORT evhtp_hooks_t * evhtp_request_get_hooks(evhtp_request_t * r); EVHTP_EXPORT evhtp_hooks_t * evhtp_callback_get_hooks(evhtp_callback_t * cb); /** * @brief removes all hooks. * * @param hooks * * @return */ EVHTP_EXPORT int evhtp_unset_all_hooks(evhtp_hooks_t ** hooks); EVHTP_EXPORT int evhtp_request_unset_hook(evhtp_request_t * req, evhtp_hook_type type); EVHTP_EXPORT int evhtp_connection_unset_hook(evhtp_connection_t * conn, evhtp_hook_type type); EVHTP_EXPORT int evhtp_callback_unset_hook(evhtp_callback_t * callback, evhtp_hook_type type); /** * @brief bind to a socket, optionally with specific protocol support * formatting. The addr can be defined as one of the following: * ipv6: for binding to an IPv6 address. * unix: for binding to a unix named socket * ipv4: for binding to an ipv4 address * Otherwise the addr is assumed to be ipv4. * * @param htp * @param addr * @param port * @param backlog * * @return */ EVHTP_EXPORT int evhtp_bind_socket(evhtp_t * htp, const char * addr, uint16_t port, int backlog); /** * @brief stops the listening socket. * * @param htp */ EVHTP_EXPORT void evhtp_unbind_socket(evhtp_t * htp); /** * @brief create the listener plus setup various options with an already-bound * socket. * * @note Since the file descriptor is passed to the function, it will not * attempt to close it if an error occurs. * * @param htp * @param sock * @param backlog * * @return 0 on success, -1 on error (check errno) */ EVHTP_EXPORT int evhtp_accept_socket(evhtp_t * htp, evutil_socket_t sock, int backlog); /** * @brief bind to an already allocated sockaddr. * @see evhtp_bind_socket * * @param htp - ptr to evhtp_t structure * @param sa - ptr to sockaddr structure * @param sin_len - size of sockaddr structure * @param backlog - backlog flag * * @return 0 on success, -1 on fail */ EVHTP_EXPORT int evhtp_bind_sockaddr(evhtp_t * htp, struct sockaddr *, size_t sin_len, int backlog); /** * @brief Enable thread-pool support for an evhtp_t context. Connectios are * distributed across 'nthreads'. An optional "on-start" callback can * be set which allows you to manipulate the thread-specific inforation * (such as the thread-specific event_base). * * @param htp * @param init_cb * @param exit_cb * @param nthreads * @param arg * * @return */ EVHTP_EXPORT int evhtp_use_threads(evhtp_t *, evhtp_thread_init_cb, int nthreads, void *) DEPRECATED("will take on the syntax of evhtp_use_threads_wexit"); /** * @brief Temporary function which will be renamed evhtp_use_threads in the * future. evhtp_use_threads() has been noted as deprecated for now */ EVHTP_EXPORT int evhtp_use_threads_wexit(evhtp_t *, evhtp_thread_init_cb, evhtp_thread_exit_cb, int nthreads, void * arg); /** * @brief generates all the right information for a reply to be sent to the client * * @param request * @param code HTTP return status code */ EVHTP_EXPORT void evhtp_send_reply(evhtp_request_t * request, evhtp_res code); /* The following three functions allow for the user to do what evhtp_send_reply does at its core * but for the weak of heart. */ EVHTP_EXPORT void evhtp_send_reply_start(evhtp_request_t * request, evhtp_res code); EVHTP_EXPORT void evhtp_send_reply_body(evhtp_request_t * request, struct evbuffer * buf); EVHTP_EXPORT void evhtp_send_reply_end(evhtp_request_t * request); /** * @brief Determine if a response should have a body. * Follows the rules in RFC 2616 section 4.3. * @return 1 if the response MUST have a body; 0 if the response MUST NOT have * a body. */ EVHTP_EXPORT int evhtp_response_needs_body(const evhtp_res code, const htp_method method); /** * @brief start a chunked response. If data already exists on the output buffer, * this will be converted to the first chunk. * * @param request * @param code */ EVHTP_EXPORT void evhtp_send_reply_chunk_start(evhtp_request_t * request, evhtp_res code); /** * @brief send a chunk reply. * * @param request * @param buf */ EVHTP_EXPORT void evhtp_send_reply_chunk(evhtp_request_t * request, struct evbuffer * buf); /** * @brief call when all chunks have been sent and you wish to send the last * bits. This will add the last 0CRLFCRCL and call send_reply_end(). * * @param request */ EVHTP_EXPORT void evhtp_send_reply_chunk_end(evhtp_request_t * request); /** * @brief creates a new evhtp_callback_t structure. * * All callbacks are stored in this structure * which define what the final function to be * called after all parsing is done. A callback * can be either a static string or a regular * expression. * * @param path can either be a static path (/path/to/resource/) or * a POSIX compatible regular expression (^/resource/(.*)) * @param type informs the function what type of of information is * is contained within the path argument. This can either be * callback_type_path, or callback_type_regex. * @param cb the callback function to be invoked * @param arg optional argument which is passed when the callback is executed. * * @return 0 on success, -1 on error. */ EVHTP_EXPORT evhtp_callback_t * evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_cb cb, void * arg); /** * @brief safely frees callback structure memory and internals * * @see evhtp_safe_free * * * @param callback - callback to be freed * */ EVHTP_EXPORT void evhtp_callback_free(evhtp_callback_t * callback); /** * @brief Adds a evhtp_callback_t to the evhtp_callbacks_t list * * @param cbs an allocated evhtp_callbacks_t structure * @param cb an initialized evhtp_callback_t structure * * @return 0 on success, -1 on error */ EVHTP_EXPORT int evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb); /** * @brief add an evhtp_t structure (with its own callbacks) to a base evhtp_t * structure for virtual hosts. It should be noted that if you enable SSL * on the base evhtp_t and your version of OpenSSL supports SNI, the SNI * hostname will always take precedence over the Host header value. * * @param evhtp * @param name * @param vhost * * @return */ EVHTP_EXPORT int evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * vhost); /** * @brief Add an alias hostname for a virtual-host specific evhtp_t. This avoids * having multiple evhtp_t virtual hosts with the same callback for the same * vhost. * * @param evhtp * @param name * * @return */ EVHTP_EXPORT int evhtp_add_alias(evhtp_t * evhtp, const char * name); /** * @brief set a variable number of aliases in one call * @reference evhtp_add_alias * @note last argument must be NULL terminated * * @param evhtp * @param name * @param ... * * @return 0 on success, -1 on error */ EVHTP_EXPORT int evhtp_add_aliases(evhtp_t * evhtp, const char * name, ...); /** * @brief Allocates a new key/value structure. * * @param key null terminated string * @param val null terminated string * @param kalloc if set to 1, the key will be copied, if 0 no copy is done. * @param valloc if set to 1, the val will be copied, if 0 no copy is done. * * @return evhtp_kv_t * on success, NULL on error. */ EVHTP_EXPORT evhtp_kv_t * evhtp_kv_new(const char * key, const char * val, char kalloc, char valloc); /** * @brief creates an empty list of key/values * * @return */ EVHTP_EXPORT evhtp_kvs_t * evhtp_kvs_new(void); /** * @brief frees resources allocated for a single key/value * * @param kv */ EVHTP_EXPORT void evhtp_kv_free(evhtp_kv_t * kv); /** * @brief frees a the list of key/values, and all underlying entries * * @param kvs */ EVHTP_EXPORT void evhtp_kvs_free(evhtp_kvs_t * kvs); /** * @brief free's resources associated with 'kv' if ONLY found within the key/value list * * @param kvs * @param kv */ EVHTP_EXPORT void evhtp_kv_rm_and_free(evhtp_kvs_t * kvs, evhtp_kv_t * kv); /** * @brief find the string value of 'key' from the key/value list 'kvs' * * @param kvs * @param key * * @return NULL if not found */ EVHTP_EXPORT const char * evhtp_kv_find(evhtp_kvs_t * kvs, const char * key); /** * @brief find the evhtp_kv_t reference 'key' from the k/val list 'kvs' * * @param kvs * @param key * * @return */ EVHTP_EXPORT evhtp_kv_t * evhtp_kvs_find_kv(evhtp_kvs_t * kvs, const char * key); /** * @brief appends a key/val structure to a evhtp_kvs_t tailq * * @param kvs an evhtp_kvs_t structure * @param kv an evhtp_kv_t structure */ EVHTP_EXPORT void evhtp_kvs_add_kv(evhtp_kvs_t * kvs, evhtp_kv_t * kv); /** * @brief appends all key/val structures from src tailq onto dst tailq * * @param dst an evhtp_kvs_t structure * @param src an evhtp_kvs_t structure */ EVHTP_EXPORT void evhtp_kvs_add_kvs(evhtp_kvs_t * dst, evhtp_kvs_t * src); /** * @brief callback iterator which executes 'cb' for every entry in 'kvs' * * @param kvs * @param cb * @param arg * * @return */ EVHTP_EXPORT int evhtp_kvs_for_each(evhtp_kvs_t * kvs, evhtp_kvs_iterator cb, void * arg); #define EVHTP_PARSE_QUERY_FLAG_STRICT 0 #define EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX (1 << 0) #define EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS (1 << 1) #define EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS (1 << 2) #define EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP (1 << 3) #define EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS (1 << 4) #define EVHTP_PARSE_QUERY_FLAG_LENIENT \ EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX \ | EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS \ | EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS \ | EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP #define EVHTP_PARSE_QUERY_FLAG_DEFAULT EVHTP_PARSE_QUERY_FLAG_LENIENT /** * @brief Parses the query portion of the uri into a set of key/values * * Parses query arguments like "?herp=&foo=bar;blah=baz&a=%3" * * @param query data containing the uri query arguments * @param len size of the data * @param flags parse query flags to alter 'strictness' (see EVHTP_PARSE_QUERY_FLAG_*) * * @return evhtp_query_t * on success, NULL on error */ EVHTP_EXPORT evhtp_query_t * evhtp_parse_query_wflags(const char * query, size_t len, int flags); /** * @brief Parses the query portion of the uri into a set of key/values in a * strict manner * * Parses query arguments like "?herp=derp&foo=bar&blah=baz" * * @param query data containing the uri query arguments * @param len size of the data * * @return evhtp_query_t * on success, NULL on error */ EVHTP_EXPORT evhtp_query_t * evhtp_parse_query(const char * query, size_t len); /** * @brief Unescapes strings like '%7B1,%202,%203%7D' would become '{1, 2, 3}' * * @param out double pointer where output is stored. This is allocated by the user. * @param str the string to unescape * @param str_len the length of the string to unescape * * @return 0 on success, -1 on error */ EVHTP_EXPORT int evhtp_unescape_string(unsigned char ** out, unsigned char * str, size_t str_len); /** * @brief creates a new evhtp_header_t key/val structure * * @param key a null terminated string * @param val a null terminated string * @param kalloc if 1, key will be copied, otherwise no copy performed * @param valloc if 1, val will be copied, otehrwise no copy performed * * @return evhtp_header_t * or NULL on error */ EVHTP_EXPORT evhtp_header_t * evhtp_header_new(const char * key, const char * val, char kalloc, char valloc); /** * @brief creates a new evhtp_header_t, sets only the key, and adds to the * evhtp_headers TAILQ * * @param headers the evhtp_headers_t TAILQ (evhtp_kv_t) * @param key a null terminated string * @param kalloc if 1 the string will be copied, otherwise assigned * * @return an evhtp_header_t pointer or NULL on error */ EVHTP_EXPORT evhtp_header_t * evhtp_header_key_add(evhtp_headers_t * headers, const char * key, char kalloc); /** * @brief finds the last header in the headers tailq and adds the value * * @param headers the evhtp_headers_t TAILQ (evhtp_kv_t) * @param val a null terminated string * @param valloc if 1 the string will be copied, otherwise assigned * * @return an evhtp_header_t pointer or NULL on error */ EVHTP_EXPORT evhtp_header_t * evhtp_header_val_add(evhtp_headers_t * headers, const char * val, char valloc); /** * @brief adds an evhtp_header_t to the end of the evhtp_headers_t tailq * * @param headers * @param header */ EVHTP_EXPORT void evhtp_headers_add_header(evhtp_headers_t * headers, evhtp_header_t * header); /** * @brief finds the value of a key in a evhtp_headers_t structure * * @param headers the evhtp_headers_t tailq * @param key the key to find * * @return the value of the header key if found, NULL if not found. */ EVHTP_EXPORT const char * evhtp_header_find(evhtp_headers_t * headers, const char * key); #define evhtp_header_find evhtp_kv_find #define evhtp_headers_find_header evhtp_kvs_find_kv #define evhtp_headers_for_each evhtp_kvs_for_each #define evhtp_header_new evhtp_kv_new #define evhtp_header_free evhtp_kv_free #define evhtp_headers_new evhtp_kvs_new #define evhtp_headers_free evhtp_kvs_free #define evhtp_header_rm_and_free evhtp_kv_rm_and_free #define evhtp_headers_add_header evhtp_kvs_add_kv #define evhtp_headers_add_headers evhtp_kvs_add_kvs #define evhtp_query_new evhtp_kvs_new #define evhtp_query_free evhtp_kvs_free /** * @brief returns the htp_method enum version of the request method. * * @param r * * @return htp_method enum */ EVHTP_EXPORT htp_method evhtp_request_get_method(evhtp_request_t * r); EVHTP_EXPORT evhtp_proto evhtp_request_get_proto(evhtp_request_t * r); /* the following functions all do the same thing, pause and the processing */ /** * @brief pauses a connection (disables reading) * * @param c a evhtp_connection_t * structure */ EVHTP_EXPORT void evhtp_connection_pause(evhtp_connection_t * connection); /** * @brief resumes a connection (enables reading) and activates resume event. * * @param c */ EVHTP_EXPORT void evhtp_connection_resume(evhtp_connection_t * connection); /** * @brief Wrapper around evhtp_connection_pause * * @see evhtp_connection_pause * * @param request */ EVHTP_EXPORT void evhtp_request_pause(evhtp_request_t * request); /** * @brief Wrapper around evhtp_connection_resume * * @see evhtp_connection_resume * * @param request */ EVHTP_EXPORT void evhtp_request_resume(evhtp_request_t * request); /** * @brief returns the underlying evhtp_connection_t structure from a request * * @param request * * @return evhtp_connection_t on success, otherwise NULL */ EVHTP_EXPORT evhtp_connection_t * evhtp_request_get_connection(evhtp_request_t * request); /** * @brief Sets the connections underlying bufferevent * * @param conn * @param bev */ EVHTP_EXPORT void evhtp_connection_set_bev(evhtp_connection_t * conn, struct bufferevent * bev); /** * @brief sets the underlying bufferevent for a evhtp_request * * @param request * @param bev */ EVHTP_EXPORT void evhtp_request_set_bev(evhtp_request_t * request, struct bufferevent * bev); /** * @brief returns the underlying connections bufferevent * * @param conn * * @return bufferevent on success, otherwise NULL */ EVHTP_EXPORT struct bufferevent * evhtp_connection_get_bev(evhtp_connection_t * conn); /** * @brief sets a connection-specific read/write timeout which overrides the * global read/write settings. * * @param conn * @param r timeval for read * @param w timeval for write */ EVHTP_EXPORT void evhtp_connection_set_timeouts(evhtp_connection_t * conn, const struct timeval * r, const struct timeval * w); /** * @brief returns the underlying requests bufferevent * * @param request * * @return bufferevent on success, otherwise NULL */ EVHTP_EXPORT struct bufferevent * evhtp_request_get_bev(evhtp_request_t * request); /** * @brief let a user take ownership of the underlying bufferevent and free * all other underlying resources. * * Warning: this will free all evhtp_connection/request structures, remove all * associated hooks and reset the bufferevent to defaults, i.e., disable * EV_READ, and set all callbacks to NULL. * * @param connection * * @return underlying connections bufferevent. */ EVHTP_EXPORT struct bufferevent * evhtp_connection_take_ownership(evhtp_connection_t * connection); /** * @brief free's all connection related resources, this will also call your * request fini hook and request fini hook. * * @param connection */ EVHTP_EXPORT void evhtp_connection_free(evhtp_connection_t * connection); EVHTP_EXPORT void evhtp_request_free(evhtp_request_t * request); /** * @brief set a max body size to accept for an incoming request, this will * default to unlimited. * * @param htp * @param len */ EVHTP_EXPORT void evhtp_set_max_body_size(evhtp_t * htp, uint64_t len); /** * @brief set a max body size for a specific connection, this will default to * the size set by evhtp_set_max_body_size * * @param conn * @param len */ EVHTP_EXPORT void evhtp_connection_set_max_body_size(evhtp_connection_t * conn, uint64_t len); /** * @brief just calls evhtp_connection_set_max_body_size for the request. * * @param request * @param len */ EVHTP_EXPORT void evhtp_request_set_max_body_size(evhtp_request_t * request, uint64_t len); EVHTP_EXPORT void evhtp_request_set_keepalive(evhtp_request_t * request, int val); /** * @brief sets a maximum number of requests that a single connection can make. * * @param htp * @param num */ EVHTP_EXPORT void evhtp_set_max_keepalive_requests(evhtp_t * htp, uint64_t num); /***************************************************************** * client request functions * *****************************************************************/ /** * @brief allocate a new connection */ EVHTP_EXPORT evhtp_connection_t * evhtp_connection_new_dns( struct event_base * evbase, struct evdns_base * dns_base, const char * addr, uint16_t port); /** * @brief allocate a new connection */ EVHTP_EXPORT evhtp_connection_t * evhtp_connection_new(struct event_base * evbase, const char * addr, uint16_t port); #ifndef EVHTP_DISABLE_SSL EVHTP_EXPORT evhtp_connection_t * evhtp_connection_ssl_new( struct event_base * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t * ctx); #endif /** * @brief allocate a new request */ EVHTP_EXPORT evhtp_request_t * evhtp_request_new(evhtp_callback_cb cb, void * arg); /** * @brief make a client request */ EVHTP_EXPORT int evhtp_make_request(evhtp_connection_t * c, evhtp_request_t * r, htp_method meth, const char * uri); EVHTP_EXPORT unsigned int evhtp_request_status(evhtp_request_t *); #ifdef __cplusplus } #endif #endif /* __EVHTP__H__ */ libevhtp-1.2.16/include/evhtp/parser.h000066400000000000000000000100641322723041700176360ustar00rootroot00000000000000/** * @file parser.h */ #ifndef __HTPARSE_H__ #define __HTPARSE_H__ #include #include #ifdef __cplusplus extern "C" { #endif struct htparser; enum htp_type { htp_type_request = 0, htp_type_response }; enum htp_scheme { htp_scheme_none = 0, htp_scheme_ftp, htp_scheme_http, htp_scheme_https, htp_scheme_nfs, htp_scheme_unknown }; enum htp_method { htp_method_GET = 0, htp_method_HEAD, htp_method_POST, htp_method_PUT, htp_method_DELETE, htp_method_MKCOL, htp_method_COPY, htp_method_MOVE, htp_method_OPTIONS, htp_method_PROPFIND, htp_method_PROPPATCH, htp_method_LOCK, htp_method_UNLOCK, htp_method_TRACE, htp_method_CONNECT, /* RFC 2616 */ htp_method_PATCH, /* RFC 5789 */ htp_method_UNKNOWN, }; enum htpparse_error { htparse_error_none = 0, htparse_error_too_big, htparse_error_inval_method, htparse_error_inval_reqline, htparse_error_inval_schema, htparse_error_inval_proto, htparse_error_inval_ver, htparse_error_inval_hdr, htparse_error_inval_chunk_sz, htparse_error_inval_chunk, htparse_error_inval_state, htparse_error_user, htparse_error_status, htparse_error_generic }; typedef struct htparser htparser; typedef struct htparse_hooks htparse_hooks; typedef enum htp_scheme htp_scheme; typedef enum htp_method htp_method; typedef enum htp_type htp_type; typedef enum htpparse_error htpparse_error; typedef int (* htparse_hook)(htparser *); typedef int (* htparse_data_hook)(htparser *, const char *, size_t); struct htparse_hooks { htparse_hook on_msg_begin; htparse_data_hook method; htparse_data_hook scheme; /* called if scheme is found */ htparse_data_hook host; /* called if a host was in the request scheme */ htparse_data_hook port; /* called if a port was in the request scheme */ htparse_data_hook path; /* only the path of the uri */ htparse_data_hook args; /* only the arguments of the uri */ htparse_data_hook uri; /* the entire uri including path/args */ htparse_hook on_hdrs_begin; htparse_data_hook hdr_key; htparse_data_hook hdr_val; htparse_data_hook hostname; htparse_hook on_hdrs_complete; htparse_hook on_new_chunk; /* called after parsed chunk octet */ htparse_hook on_chunk_complete; /* called after single parsed chunk */ htparse_hook on_chunks_complete; /* called after all parsed chunks processed */ htparse_data_hook body; htparse_hook on_msg_complete; }; EVHTP_EXPORT size_t htparser_run(htparser *, htparse_hooks *, const char *, size_t); EVHTP_EXPORT int htparser_should_keep_alive(htparser * p); EVHTP_EXPORT htp_scheme htparser_get_scheme(htparser *); EVHTP_EXPORT htp_method htparser_get_method(htparser *); EVHTP_EXPORT const char * htparser_get_methodstr(htparser *); EVHTP_EXPORT const char * htparser_get_methodstr_m(htp_method); EVHTP_EXPORT void htparser_set_major(htparser *, unsigned char); EVHTP_EXPORT void htparser_set_minor(htparser *, unsigned char); EVHTP_EXPORT unsigned char htparser_get_major(htparser *); EVHTP_EXPORT unsigned char htparser_get_minor(htparser *); EVHTP_EXPORT unsigned char htparser_get_multipart(htparser *); EVHTP_EXPORT unsigned int htparser_get_status(htparser *); EVHTP_EXPORT uint64_t htparser_get_content_length(htparser *); EVHTP_EXPORT uint64_t htparser_get_content_pending(htparser *); EVHTP_EXPORT uint64_t htparser_get_total_bytes_read(htparser *); EVHTP_EXPORT htpparse_error htparser_get_error(htparser *); EVHTP_EXPORT const char * htparser_get_strerror(htparser *); EVHTP_EXPORT void * htparser_get_userdata(htparser *); EVHTP_EXPORT void htparser_set_userdata(htparser *, void *); EVHTP_EXPORT void htparser_init(htparser *, htp_type); EVHTP_EXPORT htparser * htparser_new(void); #ifdef __cplusplus } #endif #endif libevhtp-1.2.16/include/evhtp/sslutils.h000066400000000000000000000107061322723041700202270ustar00rootroot00000000000000/** * @file sslutils.h */ #ifndef __EVHTP_SSLUTILS_H__ #define __EVHTP_SSLUTILS_H__ #include #ifdef __cplusplus extern "C" { #endif /** * @defgroup htp_sslutils SSL utility functions */ /** * @brief converts the client certificate DNAME information (CN=, OU=.....) * @ingroup htp_sslutils * * @param ssl the client SSL context * * @return heap allocated str representation, NULL on error. */ EVHTP_EXPORT unsigned char * htp_sslutil_subject_tostr(evhtp_ssl_t * ssl); /** * @brief converts the DN (issuer of cert from the client) * @ingroup htp_sslutils * * @param ssl client SSL context * * @return heap allocated str representation, NULL on error */ EVHTP_EXPORT unsigned char * htp_sslutil_issuer_tostr(evhtp_ssl_t * ssl); /** * @brief converts the `notbefore` date of the cert from the client * @ingroup htp_sslutils * * @param ssl client SSL context * * @return heap allocated str (YYMMDDhhmmss) of the notbefore, NULL on error. */ EVHTP_EXPORT unsigned char * htp_sslutil_notbefore_tostr(evhtp_ssl_t * ssl); /** * @brief converts the `notafter` date of the cert from the client * @ingroup htp_sslutils * * @param ssl ssl client SSL context * * @return heap allocated str (YYMMDDhhmmss) of notafter, NULL on error. */ EVHTP_EXPORT unsigned char * htp_sslutil_notafter_tostr(evhtp_ssl_t * ssl); /** * @brief converts the SHA1 digest in str from the client * @ingroup htp_sslutils * * @param ssl SSL context from client * * @return NULL on error */ EVHTP_EXPORT unsigned char * htp_sslutil_sha1_tostr(evhtp_ssl_t * ssl); /** * @brief convert serial number to string * @ingroup htp_sslutils * * @param ssl SSL context from client * * @return NULL on error */ EVHTP_EXPORT unsigned char * htp_sslutil_serial_tostr(evhtp_ssl_t * ssl); /** * @brief convert the used for this SSL context * @ingroup htp_sslutils * * @param ssl SSL context * * @return heap allocated cipher str, NULL on error */ EVHTP_EXPORT unsigned char * htp_sslutil_cipher_tostr(evhtp_ssl_t * ssl); /** * @brief convert the client cert into a multiline string * @ingroup htp_sslutils * * @param ssl client SSL context * * @return heap allocated string, NULL on error */ EVHTP_EXPORT unsigned char * htp_sslutil_cert_tostr(evhtp_ssl_t * ssl); /** * @brief convert X509 extentions to string * @ingroup htp_sslutils * * @param ssl SSL context * @param oid * * @return */ EVHTP_EXPORT unsigned char * htp_sslutil_x509_ext_tostr(evhtp_ssl_t * ssl, const char * oid); /** * @brief convert a string to the proper verify opts * @ingroup htp_sslutils * * @param opts_str ("on" / "optional" / "off" ) * where: * "on" : client must present a valid cert (otherwise rejected) * "off" : no client cert required at all * "optional" : client MAY present a valid certificate (but not rejected) * * @note if `opts_str` is NULL, defaults to "off" * * @return OR'd mask SSL_VERIFY_* flags, -1 on error */ EVHTP_EXPORT int htp_sslutil_verify2opts(const char * opts_str); /* * @ingroup htp_sslutils * @ { */ #define HTP_SSLUTILS_XHDR_SUBJ (1 << 0) #define HTP_SSLUTILS_XHDR_ISSR (1 << 1) #define HTP_SSLUTILS_XHDR_NBFR (1 << 2) #define HTP_SSLUTILS_XHDR_NAFR (1 << 3) #define HTP_SSLUTILS_XHDR_SERL (1 << 4) #define HTP_SSLUTILS_XHDR_SHA1 (1 << 5) #define HTP_SSLUTILS_XHDR_CERT (1 << 6) #define HTP_SSLUTILS_XHDR_CIPH (1 << 7) #define HTP_SSLUTILS_XHDR_ALL \ HTP_SSLUTILS_XHDR_SUBJ \ | HTP_SSLUTILS_XHDR_ISSR \ | HTP_SSLUTILS_XHDR_NBFR \ | HTP_SSLUTILS_XHDR_NAFR \ | HTP_SSLUTILS_XHDR_SERL \ | HTP_SSLUTILS_XHDR_SHA1 \ | HTP_SSLUTILS_XHDR_CERT \ | HTP_SSLUTILS_XHDR_CIPH /** @} */ /** * @brief add SSL-type X-Header flags to an evhtp_headers_t context * @ingroup htp_sslutils * * @param hdrs headers structure to append into * @param ssl the SSL context * HTP_SSLUTILS_XHDR_SUBJ: `X-SSL-Subject` * HTP_SSLUTILS_XHDR_ISSR: `X-SSL-Issuer` * HTP_SSLUTILS_XHDR_NBFR: `X-SSL-Notbefore` * HTP_SSLUTILS_XHDR_NAFR: `X-SSL-Notafter` * HTP_SSLUTILS_XHDR_SERL: `X-SSL-Serial` * HTP_SSLUTILS_XHDR_CIPH: `X-SSL-Cipher` * HTP_SSLUTILS_XHDR_CERT: `X-SSL-Certificate` * HTP_SSLUTILS_XHDR_SHA1: `X-SSL-SHA1` * * @param flags flags (See XHDR defines above) * * @return 0 on success, -1 on error */ EVHTP_EXPORT int htp_sslutil_add_xheaders(evhtp_headers_t * hdrs, evhtp_ssl_t * ssl, short flags); #ifdef __cplusplus } #endif #endif libevhtp-1.2.16/include/evhtp/thread.h000066400000000000000000000036611322723041700176160ustar00rootroot00000000000000/** * @file thread.h */ #ifndef __EVTHR_H__ #define __EVTHR_H__ #include #include #include #ifdef __cplusplus extern "C" { #endif enum evthr_res { EVTHR_RES_OK = 0, EVTHR_RES_BACKLOG, EVTHR_RES_RETRY, EVTHR_RES_NOCB, EVTHR_RES_FATAL }; struct evthr_pool; struct evthr; typedef struct event_base evbase_t; typedef struct event ev_t; typedef struct evthr_pool evthr_pool_t; typedef struct evthr evthr_t; typedef enum evthr_res evthr_res; typedef void (* evthr_cb)(evthr_t * thr, void * cmd_arg, void * shared); typedef void (* evthr_init_cb)(evthr_t * thr, void * shared); typedef void (* evthr_exit_cb)(evthr_t * thr, void * shared); EVHTP_EXPORT evthr_t * evthr_new(evthr_init_cb, void *) DEPRECATED("will take on the syntax of evthr_wexit_new"); EVHTP_EXPORT evbase_t * evthr_get_base(evthr_t * thr); EVHTP_EXPORT void evthr_set_aux(evthr_t * thr, void * aux); EVHTP_EXPORT void * evthr_get_aux(evthr_t * thr); EVHTP_EXPORT int evthr_start(evthr_t * evthr); EVHTP_EXPORT evthr_res evthr_stop(evthr_t * evthr); EVHTP_EXPORT evthr_res evthr_defer(evthr_t * evthr, evthr_cb cb, void *); EVHTP_EXPORT void evthr_free(evthr_t * evthr); EVHTP_EXPORT evthr_pool_t * evthr_pool_new(int nthreads, evthr_init_cb, void *) DEPRECATED("will take on the syntax of evthr_pool_wexit_new"); EVHTP_EXPORT int evthr_pool_start(evthr_pool_t * pool); EVHTP_EXPORT evthr_res evthr_pool_stop(evthr_pool_t * pool); EVHTP_EXPORT evthr_res evthr_pool_defer(evthr_pool_t * pool, evthr_cb cb, void * arg); EVHTP_EXPORT void evthr_pool_free(evthr_pool_t * pool); EVHTP_EXPORT evthr_t * evthr_wexit_new(evthr_init_cb, evthr_exit_cb, void * shared); EVHTP_EXPORT evthr_pool_t * evthr_pool_wexit_new(int nthreads, evthr_init_cb, evthr_exit_cb, void *); #ifdef __cplusplus } #endif #endif /* __EVTHR_H__ */ libevhtp-1.2.16/include/internal.h000066400000000000000000000057421322723041700170370ustar00rootroot00000000000000/** * @file internal.h */ #ifndef __EVHTP_INTERNAL_H__ #define __EVHTP_INTERNAL_H__ #ifdef __cplusplus extern "C" { #endif #if defined __GNUC__ || defined __llvm__ # define evhtp_likely(x) __builtin_expect(!!(x), 1) # define evhtp_unlikely(x) __builtin_expect(!!(x), 0) #else # define evhtp_likely(x) (x) # define evhtp_unlikely(x) (x) #endif #ifndef TAILQ_FOREACH_SAFE #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = TAILQ_FIRST((head)); \ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ (var) = (tvar)) #endif #define evhtp_safe_free(_var, _freefn) do { \ _freefn((_var)); \ (_var) = NULL; \ } while (0) #define evhtp_assert(x) \ do { \ if (evhtp_unlikely(!(x))) { \ fprintf(stderr, "Assertion failed: %s (%s:%s:%d)\n", # x, \ __func__, __FILE__, __LINE__); \ fflush(stderr); \ abort(); \ } \ } while (0) #define evhtp_alloc_assert(x) \ do { \ if (evhtp_unlikely(!x)) { \ fprintf(stderr, "Out of memory (%s:%s:%d)\n", \ __func__, __FILE__, __LINE__); \ fflush(stderr); \ abort(); \ } \ } while (0) #define evhtp_assert_fmt(x, fmt, ...) \ do { \ if (evhtp_unlikely(!(x))) { \ fprintf(stderr, "Assertion failed: %s (%s:%s:%d) " fmt "\n", \ # x, __func__, __FILE__, __LINE__, __VA_ARGS__); \ fflush(stderr); \ abort(); \ } \ } while (0) #define evhtp_errno_assert(x) \ do { \ if (evhtp_unlikely(!(x))) { \ fprintf(stderr, "%s [%d] (%s:%s:%d)\n", \ strerror(errno), errno, \ __func__, __FILE__, __LINE__); \ fflush(stderr); \ abort(); \ } \ } while (0) #ifdef __cplusplus } #endif #endif libevhtp-1.2.16/include/numtoa.h000066400000000000000000000016561322723041700165260ustar00rootroot00000000000000#ifndef __EVHTP_NUMTOA_H__ #define __EVHTP_NUMTOA_H__ #ifdef __cplusplus extern "C" { #endif #include "evhtp/config.h" /** * @brief based on the system architecture, convert a size_t * number to a string. * * @param value the input value * @param str The output buffer, should be 24 chars or more. * * @return */ EVHTP_EXPORT size_t evhtp_modp_sizetoa(size_t value, char * str); /** * @brief converts uint32_t value to string * * @param value input value * @param str output buffer, should be 16 chars or more * * @return */ EVHTP_EXPORT size_t evhtp_modp_u32toa(uint32_t value, char * str); /** * @brief convert uint64_t value to a string * * @param value input value * @param str output buffer, should be 24 chars or more * * @return */ EVHTP_EXPORT size_t evhtp_modp_u64toa(uint64_t value, char * str); #define evhtp_modp_uchartoa(_val) (unsigned char)('0' + _val) #ifdef __cplusplus } #endif #endif libevhtp-1.2.16/log.h000066400000000000000000000042401322723041700143510ustar00rootroot00000000000000#pragma once #include #include #include #define __FILENAME__ \ (strrchr(__FILE__, '/') ? \ strrchr(__FILE__, '/') + 1 : __FILE__) #define clean_errno() \ (errno == 0 ? "None" : strerror(errno)) #define __log_debug_color(X) "[\x1b[1;36m" X "\x1b[0;39m]" #define __log_error_color(X) "[\x1b[1;31m" X "\x1b[0;39m]" #define __log_warn_color(X) "[\x1b[1;33m" X "\x1b[0;39m]" #define __log_info_color(X) "[\x1b[32m" X "\x1b[0;39m]" #define __log_func_color(X) "\x1b[33m" X "\x1b[39m" #define __log_args_color(X) "\x1b[94m" X "\x1b[39m" #define __log_errno_color(X) "\x1b[35m" X "\x1b[39m" #if !defined(EVHTP_DEBUG) /* compile with all debug messages removed */ #define log_debug(M, ...) #else #define log_debug(M, ...) \ fprintf(stderr, __log_debug_color("DEBUG") " " \ __log_func_color("%s:%-9d") \ __log_args_color(M) \ "\n", \ __FILENAME__, __LINE__, ## __VA_ARGS__) #endif #define log_error(M, ...) \ fprintf(stderr, __log_error_color("ERROR") " " \ __log_func_color("%s:%-9d") \ __log_args_color(M) \ " :: " \ __log_errno_color("(errno: %s)") \ "\n", \ __FILENAME__, __LINE__, ## __VA_ARGS__, clean_errno()) #define log_warn(M, ...) \ fprintf(stderr, __log_warn_color("WARN") " " \ __log_func_color("%s:%-9d") \ __log_args_color(M) \ " :: " \ __log_errno_color("(errno: %s)") \ "\n", \ __FILENAME__, __LINE__, ## __VA_ARGS__, clean_errno()) #define log_info(M, ...) \ fprintf(stderr, __log_info_color("INFO") " " \ __log_func_color("%4s:%-9d") \ __log_args_color(M) "\n", \ __FILENAME__, __LINE__, ## __VA_ARGS__) libevhtp-1.2.16/numtoa.c000066400000000000000000000025561322723041700150760ustar00rootroot00000000000000/* * DERIVED FROM the stringencoders library's modp_numtoa * * Copyright ; 2007, Nick Galbreath -- nickg [at] client9 [dot] com * All rights reserved. * http://code.google.com/p/stringencoders/ * Released under the MIT license. * */ #include #include #include #include #include "internal.h" #include "numtoa.h" static inline void strreverse(char * begin, char * end) { char aux; while (end > begin) { aux = *end, *end-- = *begin, *begin++ = aux; } } size_t evhtp_modp_u64toa(uint64_t value, char * str) { char * wstr = str; /* Conversion. Number is reversed. */ do { *wstr++ = (char)(48 + (value % 10)); } while (value /= 10); *wstr = '\0'; /* Reverse string */ strreverse(str, wstr - 1); return (size_t)(wstr - str); } size_t evhtp_modp_u32toa(uint32_t value, char * str) { char * wstr = str; /* Conversion. Number is reversed. */ do { *wstr++ = (char)(48 + (value % 10)); } while (value /= 10); *wstr = '\0'; /* Reverse string */ strreverse(str, wstr - 1); return (size_t)(wstr - str); } inline size_t evhtp_modp_sizetoa(size_t value, char * str) { #if EVHTP_SYS_ARCH == 64 return evhtp_modp_u64toa(value, str); #elif EVHTP_SYS_ARCH == 32 return evhtp_modp_u32toa(value, str); #else #warn "UNKNOWN ARCH" #endif } libevhtp-1.2.16/parser.c000066400000000000000000002065361322723041700150730ustar00rootroot00000000000000#include #include #include #include "internal.h" #include "evhtp/parser.h" #include "evhtp/config.h" #include "log.h" #if '\n' != '\x0a' || 'A' != 65 #error "You have somehow found a non-ASCII host. We can't build here." #endif #define PARSER_STACK_MAX 8192 #define LF (unsigned char)10 #define CR (unsigned char)13 #define CRLF "\x0d\x0a" enum eval_hdr_val { eval_hdr_val_none = 0, eval_hdr_val_connection, eval_hdr_val_proxy_connection, eval_hdr_val_content_length, eval_hdr_val_transfer_encoding, eval_hdr_val_hostname, eval_hdr_val_content_type }; enum parser_flags { parser_flag_chunked = (1 << 0), parser_flag_connection_keep_alive = (1 << 1), parser_flag_connection_close = (1 << 2), parser_flag_trailing = (1 << 3), }; enum parser_state { s_start = 0, s_method, s_spaces_before_uri, s_schema, s_schema_slash, s_schema_slash_slash, s_host, s_host_ipv6, s_host_done, s_port, s_after_slash_in_uri, s_check_uri, s_uri, s_http_09, s_http_H, s_http_HT, s_http_HTT, s_http_HTTP, s_first_major_digit, s_major_digit, s_first_minor_digit, s_minor_digit, s_spaces_after_digit, s_almost_done, s_done, s_hdrline_start, s_hdrline_hdr_almost_done, s_hdrline_hdr_done, s_hdrline_hdr_key, s_hdrline_hdr_space_before_val, s_hdrline_hdr_val, s_hdrline_almost_done, s_hdrline_done, s_body_read, s_chunk_size_start, s_chunk_size, s_chunk_size_almost_done, s_chunk_data, s_chunk_data_almost_done, s_chunk_data_done, s_status, s_space_after_status, s_status_text }; typedef enum eval_hdr_val eval_hdr_val; typedef enum parser_flags parser_flags; typedef enum parser_state parser_state; struct htparser { htpparse_error error; parser_state state; parser_flags flags; eval_hdr_val heval; htp_type type; htp_scheme scheme; htp_method method; unsigned char multipart; unsigned char major; unsigned char minor; uint64_t content_len; /* this gets decremented as data passes through */ uint64_t orig_content_len; /* this contains the original length of the body */ uint64_t bytes_read; uint64_t total_bytes_read; unsigned int status; /* only for responses */ unsigned int status_count; /* only for responses */ char * scheme_offset; char * host_offset; char * port_offset; char * path_offset; char * args_offset; void * userdata; size_t buf_idx; /* Must be last since htparser_init memsets up to the offset of this buffer */ char buf[PARSER_STACK_MAX]; }; #ifdef EVHTP_DEBUG static void log_htparser__s_(struct htparser * p) { log_debug( "struct htparser {\n" " htpparse_error = %d\n" " parser_state = %d\n" " parser_flags = %d\n" " eval_hdr_val = %d\n" " htp_type = %d\n" " htp_scheme = %d\n" " htp_method = %d\n" " multipart = %c\n" " major = %c\n" " minor = %c\n" " content_len = %zu\n" " orig_clen = %zu\n" " bytes_read = %zu\n" " total_read = %zu\n" " status = %d\n" " status_count = %d\n" " scheme_offset = %s\n" " host_offset = %s\n" " port_offset = %s\n" " path_offset = %s\n" " args_offset = %s\n" " userdata = %p\n" " buf_idx = %zu\n" " buf = %s\n" "};", p->error, p->state, p->flags, p->heval, p->type, p->scheme, p->method, p->multipart, p->major, p->minor, p->content_len, p->orig_content_len, p->bytes_read, p->total_bytes_read, p->status, p->status_count, p->scheme_offset, p->host_offset, p->port_offset, p->path_offset, p->args_offset, p->userdata, p->buf_idx, p->buf); } /* log_htparser__s_ */ #else #define log_htparser__s_(p) #endif static uint32_t usual[] = { 0xffffdbfe, 0x7fff37d6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; static int8_t unhex[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; static const char * errstr_map[] = { "htparse_error_none", "htparse_error_too_big", "htparse_error_invalid_method", "htparse_error_invalid_requestline", "htparse_error_invalid_schema", "htparse_error_invalid_protocol", "htparse_error_invalid_version", "htparse_error_invalid_header", "htparse_error_invalid_chunk_size", "htparse_error_invalid_chunk", "htparse_error_invalid_state", "htparse_error_user", "htparse_error_unknown" }; static const char * method_strmap[] = { "GET", "HEAD", "POST", "PUT", "DELETE", "MKCOL", "COPY", "MOVE", "OPTIONS", "PROPFIND", "PROPATCH", "LOCK", "UNLOCK", "TRACE", "CONNECT", "PATCH", }; #define _MIN_READ(a, b) ((a) < (b) ? (a) : (b)) #ifndef HOST_BIG_ENDIAN /* Little-endian cmp macros */ #define _str3_cmp(m, c0, c1, c2, c3) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) #define _str3Ocmp(m, c0, c1, c2, c3) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) #define _str4cmp(m, c0, c1, c2, c3) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) #define _str5cmp(m, c0, c1, c2, c3, c4) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ && m[4] == c4 #define _str6cmp(m, c0, c1, c2, c3, c4, c5) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ && (((uint32_t *)m)[1] & 0xffff) == ((c5 << 8) | c4) #define _str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ && ((uint32_t *)m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) #define _str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ && ((uint32_t *)m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) #define _str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ && ((uint32_t *)m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) \ && m[8] == c8 #else /* Big endian cmp macros */ #define _str3_cmp(m, c0, c1, c2, c3) \ m[0] == c0 && m[1] == c1 && m[2] == c2 #define _str3Ocmp(m, c0, c1, c2, c3) \ m[0] == c0 && m[2] == c2 && m[3] == c3 #define _str4cmp(m, c0, c1, c2, c3) \ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 #define _str5cmp(m, c0, c1, c2, c3, c4) \ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4 #define _str6cmp(m, c0, c1, c2, c3, c4, c5) \ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \ && m[4] == c4 && m[5] == c5 #define _str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \ && m[4] == c4 && m[5] == c5 && m[6] == c6 #define _str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \ && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 #define _str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \ && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8 #endif #define __HTPARSE_GENHOOK(__n) \ static inline int hook_ ## __n ## _run(htparser * p, htparse_hooks * hooks) { \ log_debug("enter"); \ if (hooks && (hooks)->__n) \ { \ return (hooks)->__n(p); \ } \ \ return 0; \ } #define __HTPARSE_GENDHOOK(__n) \ static inline int hook_ ## __n ## _run(htparser * p, \ htparse_hooks * hooks, \ const char * s, size_t l) { \ log_debug("enter"); \ if (hooks && (hooks)->__n) \ { \ return (hooks)->__n(p, s, l); \ } \ \ return 0; \ } __HTPARSE_GENHOOK(on_msg_begin) __HTPARSE_GENHOOK(on_hdrs_begin) __HTPARSE_GENHOOK(on_hdrs_complete) __HTPARSE_GENHOOK(on_new_chunk) __HTPARSE_GENHOOK(on_chunk_complete) __HTPARSE_GENHOOK(on_chunks_complete) __HTPARSE_GENHOOK(on_msg_complete) __HTPARSE_GENDHOOK(method) __HTPARSE_GENDHOOK(scheme) __HTPARSE_GENDHOOK(host) __HTPARSE_GENDHOOK(port) __HTPARSE_GENDHOOK(path) __HTPARSE_GENDHOOK(args) __HTPARSE_GENDHOOK(uri) __HTPARSE_GENDHOOK(hdr_key) __HTPARSE_GENDHOOK(hdr_val) __HTPARSE_GENDHOOK(body) __HTPARSE_GENDHOOK(hostname) static inline uint64_t str_to_uint64(char * str, size_t n, int * err) { uint64_t value; /* Trim whitespace after value. */ while (n && isblank(str[n - 1])) { n--; } if (n > 20) { /* 18446744073709551615 is 20 bytes */ *err = 1; return 0; } for (value = 0; n--; str++) { uint64_t check; if (*str < '0' || *str > '9') { *err = 1; return 0; } check = value * 10 + (*str - '0'); if ((value && check <= value)) { *err = 1; return 0; } value = check; } return value; } static inline ssize_t _str_to_ssize_t(char * str, size_t n) { ssize_t value; if (n == 0) { return -1; } for (value = 0; n--; str++) { if (*str < '0' || *str > '9') { return -1; } value = value * 10 + (*str - '0'); #if 0 if (value > INTMAX_MAX) { return -1; } #endif } return value; } htpparse_error htparser_get_error(htparser * p) { return p->error; } const char * htparser_get_strerror(htparser * p) { htpparse_error e = htparser_get_error(p); if (e > htparse_error_generic) { return "htparse_no_such_error"; } return errstr_map[e]; } unsigned int htparser_get_status(htparser * p) { return p->status; } int htparser_should_keep_alive(htparser * p) { if (p->major > 0 && p->minor > 0) { if (p->flags & parser_flag_connection_close) { return 0; } else { return 1; } } else { if (p->flags & parser_flag_connection_keep_alive) { return 1; } else { return 0; } } return 0; } htp_scheme htparser_get_scheme(htparser * p) { return p->scheme; } htp_method htparser_get_method(htparser * p) { return p->method; } const char * htparser_get_methodstr_m(htp_method meth) { if (meth >= htp_method_UNKNOWN) { return NULL; } return method_strmap[meth]; } const char * htparser_get_methodstr(htparser * p) { return htparser_get_methodstr_m(p->method); } void htparser_set_major(htparser * p, unsigned char major) { p->major = major; } void htparser_set_minor(htparser * p, unsigned char minor) { p->minor = minor; } unsigned char htparser_get_major(htparser * p) { return p->major; } unsigned char htparser_get_minor(htparser * p) { return p->minor; } unsigned char htparser_get_multipart(htparser * p) { return p->multipart; } void * htparser_get_userdata(htparser * p) { return p->userdata; } void htparser_set_userdata(htparser * p, void * ud) { p->userdata = ud; } uint64_t htparser_get_content_pending(htparser * p) { return p->content_len; } uint64_t htparser_get_content_length(htparser * p) { return p->orig_content_len; } uint64_t htparser_get_bytes_read(htparser * p) { return p->bytes_read; } uint64_t htparser_get_total_bytes_read(htparser * p) { return p->total_bytes_read; } void htparser_init(htparser * p, htp_type type) { /* Do not memset entire string buffer. */ memset(p, 0, offsetof(htparser, buf)); p->buf[0] = '\0'; p->state = s_start; p->error = htparse_error_none; p->method = htp_method_UNKNOWN; p->type = type; } htparser * htparser_new(void) { return malloc(sizeof(htparser)); } static int is_host_char(unsigned char ch) { char c = (unsigned char)(ch | 0x20); if (c >= 'a' && c <= 'z') { return 1; } if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') { return 1; } return 0; } static htp_method get_method(const char * m, const size_t sz) { switch (sz) { case 3: if (_str3_cmp(m, 'G', 'E', 'T', '\0')) { return htp_method_GET; } if (_str3_cmp(m, 'P', 'U', 'T', '\0')) { return htp_method_PUT; } break; case 4: if (m[1] == 'O') { if (_str3Ocmp(m, 'P', 'O', 'S', 'T')) { return htp_method_POST; } if (_str3Ocmp(m, 'C', 'O', 'P', 'Y')) { return htp_method_COPY; } if (_str3Ocmp(m, 'M', 'O', 'V', 'E')) { return htp_method_MOVE; } if (_str3Ocmp(m, 'L', 'O', 'C', 'K')) { return htp_method_LOCK; } } else { if (_str4cmp(m, 'H', 'E', 'A', 'D')) { return htp_method_HEAD; } } break; case 5: if (_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) { return htp_method_MKCOL; } if (_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) { return htp_method_TRACE; } if (_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) { return htp_method_PATCH; } break; case 6: if (_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) { return htp_method_DELETE; } if (_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) { return htp_method_UNLOCK; } break; case 7: if (_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', '\0')) { return htp_method_OPTIONS; } if (_str7_cmp(m, 'C', 'O', 'N', 'N', 'E', 'C', 'T', '\0')) { return htp_method_CONNECT; } break; case 8: if (_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D')) { return htp_method_PROPFIND; } break; case 9: if (_str9cmp(m, 'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H')) { return htp_method_PROPPATCH; } break; } /* switch */ return htp_method_UNKNOWN; } /* get_method */ #define HTP_SET_BUF(CH) do { \ if (evhtp_likely((p->buf_idx + 1) < PARSER_STACK_MAX)) { \ p->buf[p->buf_idx++] = CH; \ p->buf[p->buf_idx] = '\0'; \ } else { \ p->error = htparse_error_too_big; \ return i + 1; \ } \ } while (0) size_t htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) { unsigned char ch; char c; size_t i; log_debug("enter"); log_debug("p == %p", p); p->error = htparse_error_none; p->bytes_read = 0; for (i = 0; i < len; i++) { int res; int err; ch = data[i]; log_debug("[%p] data[%zu] = %c (%x)", p, i, isprint(ch) ? ch : ' ', ch); p->total_bytes_read += 1; p->bytes_read += 1; switch (p->state) { case s_start: log_debug("[%p] s_start", p); if (ch == CR || ch == LF) { break; } if ((ch < 'A' || ch > 'Z') && ch != '_') { p->error = htparse_error_inval_reqline; log_debug("s_start invalid fist char '%c'", ch); log_htparser__s_(p); return i + 1; } p->flags = 0; p->error = htparse_error_none; p->method = htp_method_UNKNOWN; p->multipart = 0; p->major = 0; p->minor = 0; p->content_len = 0; p->orig_content_len = 0; p->status = 0; p->status_count = 0; p->scheme_offset = NULL; p->host_offset = NULL; p->port_offset = NULL; p->path_offset = NULL; p->args_offset = NULL; res = hook_on_msg_begin_run(p, hooks); HTP_SET_BUF(ch); if (evhtp_likely(p->type == htp_type_request)) { p->state = s_method; } else if (p->type == htp_type_response && ch == 'H') { p->state = s_http_H; } else { log_debug("not type of request or response?"); log_htparser__s_(p); p->error = htparse_error_inval_reqline; return i + 1; } if (res) { p->error = htparse_error_user; return i + 1; } break; case s_method: log_debug("[%p] s_method", p); do { if (ch == ' ') { p->method = get_method(p->buf, p->buf_idx); res = hook_method_run(p, hooks, p->buf, p->buf_idx); p->buf_idx = 0; p->state = s_spaces_before_uri; if (res) { p->error = htparse_error_user; return i + 1; } break; } else { if ((ch < 'A' || ch > 'Z') && ch != '_') { p->error = htparse_error_inval_method; return i + 1; } HTP_SET_BUF(ch); } ch = data[++i]; } while (i < len); break; case s_spaces_before_uri: log_debug("[%p] s_spaces_before_uri", p); /* CONNECT is special - RFC 2817 section 5.2: * The Request-URI portion of the Request-Line is * always an 'authority' as defined by URI Generic * Syntax [2], which is to say the host name and port * number destination of the requested connection * separated by a colon */ if (p->method == htp_method_CONNECT) { switch (ch) { case ' ': break; case '[': /* Literal IPv6 address start. */ HTP_SET_BUF(ch); p->host_offset = &p->buf[p->buf_idx]; p->state = s_host_ipv6; break; default: if (!is_host_char(ch)) { p->error = htparse_error_inval_reqline; log_htparser__s_(p); return i + 1; } p->host_offset = &p->buf[p->buf_idx]; HTP_SET_BUF(ch); p->state = s_host; break; } /* switch */ break; } switch (ch) { case ' ': break; case '/': p->path_offset = &p->buf[p->buf_idx]; HTP_SET_BUF(ch); p->state = s_after_slash_in_uri; break; default: c = (unsigned char)(ch | 0x20); if (c >= 'a' && c <= 'z') { p->scheme_offset = &p->buf[p->buf_idx]; HTP_SET_BUF(ch); p->state = s_schema; break; } p->error = htparse_error_inval_reqline; log_htparser__s_(p); return i + 1; } /* switch */ break; case s_schema: log_debug("[%p] s_schema", p); c = (unsigned char)(ch | 0x20); if (c >= 'a' && c <= 'z') { HTP_SET_BUF(ch); break; } switch (ch) { case ':': p->scheme = htp_scheme_unknown; switch (p->buf_idx) { case 3: if (_str3_cmp(p->scheme_offset, 'f', 't', 'p', '\0')) { p->scheme = htp_scheme_ftp; break; } if (_str3_cmp(p->scheme_offset, 'n', 'f', 's', '\0')) { p->scheme = htp_scheme_nfs; break; } break; case 4: if (_str4cmp(p->scheme_offset, 'h', 't', 't', 'p')) { p->scheme = htp_scheme_http; break; } break; case 5: if (_str5cmp(p->scheme_offset, 'h', 't', 't', 'p', 's')) { p->scheme = htp_scheme_https; break; } break; } /* switch */ res = hook_scheme_run(p, hooks, p->scheme_offset, (&p->buf[p->buf_idx] - p->scheme_offset)); HTP_SET_BUF(ch); p->state = s_schema_slash; if (res) { p->error = htparse_error_user; return i + 1; } break; default: p->error = htparse_error_inval_schema; return i + 1; } /* switch */ break; case s_schema_slash: log_debug("[%p] s_schema_slash", p); switch (ch) { case '/': HTP_SET_BUF(ch); p->state = s_schema_slash_slash; break; default: p->error = htparse_error_inval_schema; return i + 1; } break; case s_schema_slash_slash: log_debug("[%p] s_schema_slash_slash", p); switch (ch) { case '/': HTP_SET_BUF(ch); p->host_offset = &p->buf[p->buf_idx]; p->state = s_host; break; default: p->error = htparse_error_inval_schema; return i + 1; } break; case s_host: if (ch == '[') { /* Literal IPv6 address start. */ HTP_SET_BUF(ch); p->host_offset = &p->buf[p->buf_idx]; p->state = s_host_ipv6; break; } if (is_host_char(ch)) { HTP_SET_BUF(ch); break; } res = hook_host_run(p, hooks, p->host_offset, (&p->buf[p->buf_idx] - p->host_offset)); if (res) { p->error = htparse_error_user; return i + 1; } /* successfully parsed a NON-IPV6 hostname, knowing this, the * current character in 'ch' is actually the next state, so we * we fall through to avoid another loop. */ case s_host_done: res = 0; switch (ch) { case ':': HTP_SET_BUF(ch); p->port_offset = &p->buf[p->buf_idx]; p->state = s_port; break; case ' ': /* this technically should never happen, but we should * check anyway */ if (i == 0) { p->error = htparse_error_inval_state; return i + 1; } i--; ch = '/'; /* to accept requests like :// * we fallthrough to the next case. */ case '/': p->path_offset = &p->buf[p->buf_idx]; HTP_SET_BUF(ch); p->state = s_after_slash_in_uri; break; default: p->error = htparse_error_inval_schema; return i + 1; } /* switch */ if (res) { p->error = htparse_error_user; return i + 1; } break; case s_host_ipv6: c = (unsigned char)(ch | 0x20); if ((c >= 'a' && c <= 'f') || (ch >= '0' && ch <= '9') || ch == ':' || ch == '.') { HTP_SET_BUF(ch); break; } switch (ch) { case ']': res = hook_host_run(p, hooks, p->host_offset, (&p->buf[p->buf_idx] - p->host_offset)); if (res) { p->error = htparse_error_user; return i + 1; } HTP_SET_BUF(ch); p->state = s_host_done; break; default: p->error = htparse_error_inval_schema; return i + 1; } /* switch */ break; case s_port: if (ch >= '0' && ch <= '9') { HTP_SET_BUF(ch); break; } res = hook_port_run(p, hooks, p->port_offset, (&p->buf[p->buf_idx] - p->port_offset)); switch (ch) { case ' ': /* this technically should never happen, but we should * check anyway */ if (i == 0) { p->error = htparse_error_inval_state; return i + 1; } i--; ch = '/'; /* to accept requests like :// * we fallthrough to the next case. */ case '/': HTP_SET_BUF(ch); p->path_offset = &p->buf[p->buf_idx - 1]; p->state = s_after_slash_in_uri; break; default: p->error = htparse_error_inval_reqline; log_debug("[s_port] inval_reqline"); log_htparser__s_(p); return i + 1; } /* switch */ if (res) { p->error = htparse_error_user; return i + 1; } break; case s_after_slash_in_uri: log_debug("[%p] s_after_slash_in_uri", p); res = 0; if (usual[ch >> 5] & (1 << (ch & 0x1f))) { HTP_SET_BUF(ch); p->state = s_check_uri; break; } switch (ch) { case ' ': { int r1 = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); int r2 = hook_uri_run(p, hooks, p->buf, p->buf_idx); p->state = s_http_09; p->buf_idx = 0; if (r1 || r2) { res = 1; } } break; case CR: p->minor = 9; p->state = s_almost_done; break; case LF: p->minor = 9; p->state = s_hdrline_start; break; case '.': case '%': case '/': case '#': HTP_SET_BUF(ch); p->state = s_uri; break; case '?': res = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); HTP_SET_BUF(ch); p->args_offset = &p->buf[p->buf_idx]; p->state = s_uri; break; default: HTP_SET_BUF(ch); p->state = s_check_uri; break; } /* switch */ if (res) { p->error = htparse_error_user; return i + 1; } break; case s_check_uri: res = 0; do { log_debug("[%p] s_check_uri", p); if (usual[ch >> 5] & (1 << (ch & 0x1f))) { HTP_SET_BUF(ch); } else { break; } ch = data[++i]; } while (i < len); switch (ch) { case ' ': { int r1 = 0; int r2 = 0; if (p->args_offset) { r1 = hook_args_run(p, hooks, p->args_offset, (&p->buf[p->buf_idx] - p->args_offset)); } else { r1 = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); } r2 = hook_uri_run(p, hooks, p->buf, p->buf_idx); p->buf_idx = 0; p->state = s_http_09; if (r1 || r2) { res = 1; } } break; case '/': HTP_SET_BUF(ch); p->state = s_after_slash_in_uri; break; case CR: p->minor = 9; p->buf_idx = 0; p->state = s_almost_done; break; case LF: p->minor = 9; p->buf_idx = 0; p->state = s_hdrline_start; break; case '?': res = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); HTP_SET_BUF(ch); p->args_offset = &p->buf[p->buf_idx]; p->state = s_uri; break; default: HTP_SET_BUF(ch); p->state = s_uri; break; } /* switch */ if (res) { p->error = htparse_error_user; return i + 1; } break; case s_uri: log_debug("[%p] s_uri", p); res = 0; do { if (usual[ch >> 5] & (1 << (ch & 0x1f))) { HTP_SET_BUF(ch); } else { break; } ch = data[++i]; } while (i < len); switch (ch) { case ' ': { int r1 = 0; int r2 = 0; if (p->args_offset) { r1 = hook_args_run(p, hooks, p->args_offset, (&p->buf[p->buf_idx] - p->args_offset)); } else { r1 = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); } p->buf_idx = 0; p->state = s_http_09; if (r1 || r2) { res = 1; } } break; case CR: p->minor = 9; p->buf_idx = 0; p->state = s_almost_done; break; case LF: p->minor = 9; p->buf_idx = 0; p->state = s_hdrline_start; break; case '?': /* RFC 3986 section 3.4: * The query component is indicated by the * first question mark ("?") character and * terminated by a number sign ("#") character * or by the end of the URI. */ if (!p->args_offset) { res = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); HTP_SET_BUF(ch); p->args_offset = &p->buf[p->buf_idx]; break; } /* Fall through. */ default: HTP_SET_BUF(ch); break; } /* switch */ if (res) { p->error = htparse_error_user; return i + 1; } break; case s_http_09: log_debug("[%p] s_http_09", p); switch (ch) { case ' ': break; case CR: p->minor = 9; p->buf_idx = 0; p->state = s_almost_done; break; case LF: p->minor = 9; p->buf_idx = 0; p->state = s_hdrline_start; break; case 'H': p->buf_idx = 0; p->state = s_http_H; break; default: p->error = htparse_error_inval_proto; return i + 1; } /* switch */ break; case s_http_H: log_debug("[%p] s_http_H", p); switch (ch) { case 'T': p->state = s_http_HT; break; default: p->error = htparse_error_inval_proto; return i + 1; } break; case s_http_HT: switch (ch) { case 'T': p->state = s_http_HTT; break; default: p->error = htparse_error_inval_proto; return i + 1; } break; case s_http_HTT: switch (ch) { case 'P': p->state = s_http_HTTP; break; default: p->error = htparse_error_inval_proto; return i + 1; } break; case s_http_HTTP: switch (ch) { case '/': p->state = s_first_major_digit; break; default: p->error = htparse_error_inval_proto; return i + 1; } break; case s_first_major_digit: if (ch < '1' || ch > '9') { p->error = htparse_error_inval_ver; return i + 1; } p->major = ch - '0'; p->state = s_major_digit; break; case s_major_digit: if (ch == '.') { p->state = s_first_minor_digit; break; } if (ch < '0' || ch > '9') { p->error = htparse_error_inval_ver; return i + 1; } p->major = p->major * 10 + ch - '0'; break; case s_first_minor_digit: if (ch < '0' || ch > '9') { p->error = htparse_error_inval_ver; return i + 1; } p->minor = ch - '0'; p->state = s_minor_digit; break; case s_minor_digit: switch (ch) { case ' ': if (evhtp_likely(p->type == htp_type_request)) { p->state = s_spaces_after_digit; } else if (p->type == htp_type_response) { p->state = s_status; } break; case CR: p->state = s_almost_done; break; case LF: /* LF without a CR? error.... */ p->error = htparse_error_inval_reqline; log_debug("[s_minor_digit] LF without CR!"); log_htparser__s_(p); return i + 1; default: if (ch < '0' || ch > '9') { p->error = htparse_error_inval_ver; return i + 1; } p->minor = p->minor * 10 + ch - '0'; break; } /* switch */ break; case s_status: /* http response status code */ if (ch == ' ') { if (p->status) { p->state = s_status_text; } break; } if (ch < '0' || ch > '9') { p->error = htparse_error_status; return i + 1; } p->status = p->status * 10 + ch - '0'; if (++p->status_count == 3) { p->state = s_space_after_status; } break; case s_space_after_status: switch (ch) { case ' ': p->state = s_status_text; break; case CR: p->state = s_almost_done; break; case LF: p->state = s_hdrline_start; break; default: p->error = htparse_error_generic; return i + 1; } break; case s_status_text: switch (ch) { case CR: p->state = s_almost_done; break; case LF: p->state = s_hdrline_start; break; default: break; } break; case s_spaces_after_digit: switch (ch) { case ' ': break; case CR: p->state = s_almost_done; break; case LF: p->state = s_hdrline_start; break; default: p->error = htparse_error_inval_ver; return i + 1; } break; case s_almost_done: switch (ch) { case LF: if (p->type == htp_type_response && p->status >= 100 && p->status < 200) { res = hook_on_hdrs_begin_run(p, hooks); if (res) { p->error = htparse_error_user; return i + 1; } p->status = 0; p->status_count = 0; p->state = s_start; break; } p->state = s_done; res = hook_on_hdrs_begin_run(p, hooks); if (res) { p->error = htparse_error_user; return i + 1; } break; default: p->error = htparse_error_inval_reqline; log_htparser__s_(p); return i + 1; } /* switch */ break; case s_done: switch (ch) { case CR: p->state = s_hdrline_almost_done; break; case LF: return i + 1; default: goto hdrline_start; } break; hdrline_start: case s_hdrline_start: log_debug("[%p] s_hdrline_start", p); p->buf_idx = 0; switch (ch) { case CR: p->state = s_hdrline_hdr_almost_done; break; case LF: p->state = s_hdrline_hdr_done; break; default: HTP_SET_BUF(ch); p->state = s_hdrline_hdr_key; break; } break; case s_hdrline_hdr_key: log_debug("[%p] s_hdrline_hdr_key", p); do { if (evhtp_unlikely(ch == ':')) { res = hook_hdr_key_run(p, hooks, p->buf, p->buf_idx); /* figure out if the value of this header is valueable */ p->heval = eval_hdr_val_none; switch (p->buf_idx + 1) { case 5: if (!strcasecmp(p->buf, "host")) { p->heval = eval_hdr_val_hostname; } break; case 11: if (!strcasecmp(p->buf, "connection")) { p->heval = eval_hdr_val_connection; } break; case 13: if (!strcasecmp(p->buf, "content-type")) { p->heval = eval_hdr_val_content_type; } break; case 15: if (!strcasecmp(p->buf, "content-length")) { p->heval = eval_hdr_val_content_length; } break; case 17: if (!strcasecmp(p->buf, "proxy-connection")) { p->heval = eval_hdr_val_proxy_connection; } break; case 18: if (!strcasecmp(p->buf, "transfer-encoding")) { p->heval = eval_hdr_val_transfer_encoding; } break; } /* switch */ p->buf_idx = 0; p->state = s_hdrline_hdr_space_before_val; if (res) { p->error = htparse_error_user; return i + 1; } break; } switch (ch) { case CR: p->state = s_hdrline_hdr_almost_done; break; case LF: p->state = s_hdrline_hdr_done; break; default: HTP_SET_BUF(ch); break; } if (p->state != s_hdrline_hdr_key) { break; } ch = data[++i]; } while (i < len); break; case s_hdrline_hdr_space_before_val: log_debug("[%p] s_hdrline_hdr_space_before_val", p); switch (ch) { case ' ': break; case CR: /* * we have an empty header value here, so we set the buf * to empty, set the state to hdrline_hdr_val, and * decrement the start byte counter. */ HTP_SET_BUF(' '); p->state = s_hdrline_hdr_val; /* * make sure the next pass comes back to this CR byte, * so it matches in s_hdrline_hdr_val. */ i--; break; case LF: /* never got a CR for an empty header, this is an * invalid state. */ p->error = htparse_error_inval_hdr; return i + 1; default: HTP_SET_BUF(ch); p->state = s_hdrline_hdr_val; break; } /* switch */ break; case s_hdrline_hdr_val: err = 0; do { log_debug("[%p] s_hdrline_hdr_val", p); if (ch == CR) { switch (p->heval) { case eval_hdr_val_none: break; case eval_hdr_val_hostname: if (hook_hostname_run(p, hooks, p->buf, p->buf_idx)) { p->state = s_hdrline_hdr_almost_done; p->error = htparse_error_user; return i + 1; } break; case eval_hdr_val_content_length: p->content_len = str_to_uint64(p->buf, p->buf_idx, &err); p->orig_content_len = p->content_len; log_debug("[%p] s_hdrline_hdr_val content-lenth = %zu", p, p->content_len); if (err == 1) { p->error = htparse_error_too_big; return i + 1; } break; case eval_hdr_val_connection: switch (p->buf[0]) { char A_case; char C_case; const char * S_buf; case 'K': case 'k': if (p->buf_idx != 10) { break; } A_case = (p->buf[5] == 'A') ? 'A' : 'a'; S_buf = (const char *)(p->buf + 1); if (_str9cmp(S_buf, 'e', 'e', 'p', '-', A_case, 'l', 'i', 'v', 'e')) { p->flags |= parser_flag_connection_keep_alive; } break; case 'c': case 'C': if (p->buf_idx != 5) { break; } C_case = (p->buf[0] == 'C') ? 'C' : 'c'; S_buf = (const char *)p->buf; if (_str5cmp(S_buf, C_case, 'l', 'o', 's', 'e')) { p->flags |= parser_flag_connection_close; } break; } /* switch */ break; case eval_hdr_val_transfer_encoding: if (p->buf_idx != 7) { break; } switch (p->buf[0]) { const char * S_buf; case 'c': case 'C': if (p->buf_idx != 7) { break; } S_buf = (const char *)(p->buf + 1); if (_str6cmp(S_buf, 'h', 'u', 'n', 'k', 'e', 'd')) { p->flags |= parser_flag_chunked; } break; } break; case eval_hdr_val_content_type: if (p->buf_idx != 9) { break; } switch (p->buf[0]) { const char * S_buf; case 'm': case 'M': S_buf = (const char *)(p->buf + 1); if (_str8cmp(S_buf, 'u', 'l', 't', 'i', 'p', 'a', 'r', 't')) { p->multipart = 1; } break; } break; case eval_hdr_val_proxy_connection: default: break; } /* switch */ p->state = s_hdrline_hdr_almost_done; break; } switch (ch) { case LF: /* LF before CR? invalid */ p->error = htparse_error_inval_hdr; return i + 1; default: HTP_SET_BUF(ch); break; } /* switch */ if (p->state != s_hdrline_hdr_val) { break; } ch = data[++i]; } while (i < len); break; case s_hdrline_hdr_almost_done: log_debug("[%p] s_hdrline_hdr_almost_done", p); res = 0; switch (ch) { case LF: if (p->flags & parser_flag_trailing) { res = hook_on_msg_complete_run(p, hooks); p->state = s_start; break; } p->state = s_hdrline_hdr_done; break; default: p->error = htparse_error_inval_hdr; return i + 1; } if (res) { p->error = htparse_error_user; return i + 1; } break; case s_hdrline_hdr_done: log_debug("[%p] s_hdrline_hdr_done", p); switch (ch) { case CR: res = hook_hdr_val_run(p, hooks, p->buf, p->buf_idx); p->state = s_hdrline_almost_done; if (res) { p->error = htparse_error_user; return i + 1; } break; case LF: /* got LFLF? is this valid? */ p->error = htparse_error_inval_hdr; return i + 1; case '\t': /* this is a multiline header value, we must go back to * reading as a header value */ p->state = s_hdrline_hdr_val; break; default: res = hook_hdr_val_run(p, hooks, p->buf, p->buf_idx); p->buf_idx = 0; HTP_SET_BUF(ch); p->state = s_hdrline_hdr_key; if (res) { p->error = htparse_error_user; return i + 1; } break; } /* switch */ break; case s_hdrline_almost_done: log_debug("[%p] s_hdrline_almost_done", p); switch (ch) { case LF: res = hook_on_hdrs_complete_run(p, hooks); if (res != 0) { p->error = htparse_error_user; return i + 1; } p->buf_idx = 0; if (p->flags & parser_flag_trailing) { res = hook_on_msg_complete_run(p, hooks); p->state = s_start; } else if (p->flags & parser_flag_chunked) { p->state = s_chunk_size_start; } else if (p->content_len > 0) { p->state = s_body_read; } else if (p->content_len == 0) { res = hook_on_msg_complete_run(p, hooks); p->state = s_start; } else { p->state = s_hdrline_done; } if (res != 0) { p->error = htparse_error_user; return i + 1; } break; default: p->error = htparse_error_inval_hdr; return i + 1; } /* switch */ if (res != 0) { p->error = htparse_error_user; return i + 1; } break; case s_hdrline_done: log_debug("[%p] s_hdrline_done", p); res = 0; if (p->flags & parser_flag_trailing) { res = hook_on_msg_complete_run(p, hooks); p->state = s_start; } else if (p->flags & parser_flag_chunked) { p->state = s_chunk_size_start; i--; } else if (p->content_len > 0) { p->state = s_body_read; i--; } else if (p->content_len == 0) { res = hook_on_msg_complete_run(p, hooks); p->state = s_start; } if (res) { p->error = htparse_error_user; return i + 1; } break; case s_chunk_size_start: c = unhex[(unsigned char)ch]; if (c == -1) { p->error = htparse_error_inval_chunk_sz; return i + 1; } p->content_len = c; p->state = s_chunk_size; break; case s_chunk_size: if (ch == CR) { p->state = s_chunk_size_almost_done; break; } c = unhex[(unsigned char)ch]; if (c == -1) { p->error = htparse_error_inval_chunk_sz; return i + 1; } p->content_len *= 16; p->content_len += c; break; case s_chunk_size_almost_done: if (ch != LF) { p->error = htparse_error_inval_chunk_sz; return i + 1; } p->orig_content_len = p->content_len; if (p->content_len == 0) { res = hook_on_chunks_complete_run(p, hooks); p->flags |= parser_flag_trailing; p->state = s_hdrline_start; } else { res = hook_on_new_chunk_run(p, hooks); p->state = s_chunk_data; } if (res) { p->error = htparse_error_user; return i + 1; } break; case s_chunk_data: res = 0; { const char * pp = &data[i]; const char * pe = (const char *)(data + len); size_t to_read = _MIN_READ(pe - pp, p->content_len); if (to_read > 0) { res = hook_body_run(p, hooks, pp, to_read); i += to_read - 1; } if (to_read == p->content_len) { p->state = s_chunk_data_almost_done; } p->content_len -= to_read; } if (res) { p->error = htparse_error_user; return i + 1; } break; case s_chunk_data_almost_done: if (ch != CR) { p->error = htparse_error_inval_chunk; return i + 1; } p->state = s_chunk_data_done; break; case s_chunk_data_done: if (ch != LF) { p->error = htparse_error_inval_chunk; return i + 1; } p->orig_content_len = 0; p->state = s_chunk_size_start; if (hook_on_chunk_complete_run(p, hooks)) { p->error = htparse_error_user; return i + 1; } break; case s_body_read: res = 0; { const char * pp = &data[i]; const char * pe = (const char *)(data + len); size_t to_read = _MIN_READ(pe - pp, p->content_len); if (to_read > 0) { res = hook_body_run(p, hooks, pp, to_read); i += to_read - 1; p->content_len -= to_read; } if (p->content_len == 0) { res = hook_on_msg_complete_run(p, hooks); p->state = s_start; } if (res) { p->error = htparse_error_user; return i + 1; } } break; default: log_debug("[%p] This is a silly state....", p); p->error = htparse_error_inval_state; return i + 1; } /* switch */ /* If we successfully completed a request/response we return * to caller, and leave it up to him to call us again if * parsing should continue. */ if (p->state == s_start) { return i + 1; } } /* switch */ return i; } /* htparser_run */ libevhtp-1.2.16/refcount.h000066400000000000000000000034321322723041700154170ustar00rootroot00000000000000#pragma once #include static void (* REF_free)(void *) = free; static void * (* REF_realloc)(void *, size_t) = realloc; static void * (* REF_malloc)(size_t) = malloc; struct refcount_ { pthread_mutex_t mux; unsigned int count; char data[]; }; #define ref_upcast(DAT) \ (struct refcount_ *)((char *)(DAT - offsetof(struct refcount_, data))) #define ref_barrier(CODE) \ pthread_mutex_lock(&refc->mux); \ CODE \ pthread_mutex_unlock(&refc->mux) static inline void ref_init_functions(void *(*mallocf)(size_t), void * (*callocf)(size_t, size_t), void *(*reallocf)(void *, size_t), void (* freef)(void *)) { REF_malloc = mallocf; REF_realloc = reallocf; REF_free = freef; } static unsigned int ref_inc(void * buf) { struct refcount_ * refc = ref_upcast(buf); unsigned int refs; ref_barrier({ refs = ++refc->count; }); return refs; } static unsigned int ref_dec(void * buf) { struct refcount_ * refc = ref_upcast(buf); unsigned int refs; ref_barrier({ refc->count -= 1; refs = refc->count; }); return refs; } static inline void * ref_malloc(size_t size) { struct refcount_ * refc; pthread_mutexattr_t attr; refc = REF_malloc(sizeof(*refc) + size); refc->count = 1; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&refc->mux, &attr); return refc->data; } static void ref_free(void * buf) { struct refcount_ * refc = ref_upcast(buf); ref_barrier({ if (--refc->count == 0) { pthread_mutex_unlock(&refc->mux); return REF_free(refc); } }); } libevhtp-1.2.16/sslutils.c000066400000000000000000000263621322723041700154560ustar00rootroot00000000000000#include #include #include #include #include #include #include "evhtp/config.h" #include "evhtp/evhtp.h" #include "evhtp/sslutils.h" #include "internal.h" unsigned char * htp_sslutil_subject_tostr(evhtp_ssl_t * ssl) { unsigned char * subj_str; char * p; X509 * cert; X509_NAME * name; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(name = X509_get_subject_name(cert))) { X509_free(cert); return NULL; } if (!(p = X509_NAME_oneline(name, NULL, 0))) { X509_free(cert); return NULL; } subj_str = strdup(p); OPENSSL_free(p); X509_free(cert); return subj_str; } unsigned char * htp_sslutil_issuer_tostr(evhtp_ssl_t * ssl) { X509 * cert; X509_NAME * name; char * p; unsigned char * issr_str; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(name = X509_get_issuer_name(cert))) { X509_free(cert); return NULL; } if (!(p = X509_NAME_oneline(name, NULL, 0))) { X509_free(cert); return NULL; } issr_str = strdup(p); OPENSSL_free(p); X509_free(cert); return issr_str; } unsigned char * htp_sslutil_notbefore_tostr(evhtp_ssl_t * ssl) { BIO * bio; X509 * cert; ASN1_TIME * time; size_t len; unsigned char * time_str; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(time = X509_get_notBefore(cert))) { X509_free(cert); return NULL; } if (!(bio = BIO_new(BIO_s_mem()))) { X509_free(cert); return NULL; } if (!ASN1_TIME_print(bio, time)) { BIO_free(bio); X509_free(cert); return NULL; } if ((len = BIO_pending(bio)) == 0) { BIO_free(bio); X509_free(cert); return NULL; } if (!(time_str = calloc(len + 1, 1))) { return NULL; } BIO_read(bio, time_str, len); BIO_free(bio); X509_free(cert); return time_str; } /* htp_sslutil_notbefore_tostr */ unsigned char * htp_sslutil_notafter_tostr(evhtp_ssl_t * ssl) { BIO * bio; X509 * cert; ASN1_TIME * time; size_t len; unsigned char * time_str; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(time = X509_get_notAfter(cert))) { X509_free(cert); return NULL; } if (!(bio = BIO_new(BIO_s_mem()))) { X509_free(cert); return NULL; } if (!ASN1_TIME_print(bio, time)) { BIO_free(bio); X509_free(cert); return NULL; } if ((len = BIO_pending(bio)) == 0) { BIO_free(bio); X509_free(cert); return NULL; } if (!(time_str = calloc(len + 1, 1))) { return NULL; } BIO_read(bio, time_str, len); BIO_free(bio); X509_free(cert); return time_str; } /* htp_sslutil_notafter_tostr */ unsigned char * htp_sslutil_sha1_tostr(evhtp_ssl_t * ssl) { const EVP_MD * md_alg; X509 * cert; unsigned int n; unsigned char md[EVP_MAX_MD_SIZE]; unsigned char * buf = NULL; size_t offset; size_t nsz; int sz; int i; if (!ssl) { return NULL; } md_alg = EVP_sha1(); if (!md_alg) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } n = 0; if (!X509_digest(cert, md_alg, md, &n)) { return NULL; } nsz = 3 * n + 1; buf = (unsigned char *)calloc(nsz, 1); if (buf) { offset = 0; for (i = 0; i < n; i++) { sz = snprintf(buf + offset, nsz - offset, "%02X%c", md[i], (i + 1 == n) ? 0 : ':'); offset += sz; if (sz < 0 || offset >= nsz) { free(buf); buf = NULL; break; } } } X509_free(cert); return buf; } /* htp_sslutil_sha1_tostr */ unsigned char * htp_sslutil_serial_tostr(evhtp_ssl_t * ssl) { BIO * bio; X509 * cert; size_t len; unsigned char * ser_str; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(bio = BIO_new(BIO_s_mem()))) { X509_free(cert); return NULL; } i2a_ASN1_INTEGER(bio, X509_get_serialNumber(cert)); if ((len = BIO_pending(bio)) == 0) { BIO_free(bio); X509_free(cert); return NULL; } if (!(ser_str = calloc(len + 1, 1))) { return NULL; } BIO_read(bio, ser_str, len); X509_free(cert); BIO_free(bio); return ser_str; } /* htp_sslutil_serial_tostr */ unsigned char * htp_sslutil_cipher_tostr(evhtp_ssl_t * ssl) { const SSL_CIPHER * cipher; const char * p; unsigned char * cipher_str; if (!ssl) { return NULL; } if (!(cipher = SSL_get_current_cipher(ssl))) { return NULL; } if (!(p = SSL_CIPHER_get_name(cipher))) { return NULL; } cipher_str = strdup(p); return cipher_str; } unsigned char * htp_sslutil_cert_tostr(evhtp_ssl_t * ssl) { X509 * cert; BIO * bio; unsigned char * raw_cert_str; unsigned char * cert_str; unsigned char * p; size_t raw_cert_len; size_t cert_len; int i; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(bio = BIO_new(BIO_s_mem()))) { X509_free(cert); return NULL; } if (!PEM_write_bio_X509(bio, cert)) { BIO_free(bio); X509_free(cert); return NULL; } raw_cert_len = BIO_pending(bio); raw_cert_str = calloc(raw_cert_len + 1, 1); BIO_read(bio, raw_cert_str, raw_cert_len); cert_len = raw_cert_len - 1; for (i = 0; i < raw_cert_len - 1; i++) { if (raw_cert_str[i] == '\n') { /* * \n's will be converted to \r\n\t, so we must reserve * enough space for that much data. */ cert_len += 2; } } /* 2 extra chars, one for possible last char (if not '\n'), and one for NULL terminator */ cert_str = calloc(cert_len + 2, 1); p = cert_str; for (i = 0; i < raw_cert_len - 1; i++) { if (raw_cert_str[i] == '\n') { *p++ = '\r'; *p++ = '\n'; *p++ = '\t'; } else { *p++ = raw_cert_str[i]; } } /* Don't assume last character is '\n' */ if (raw_cert_str[i] != '\n') { *p++ = raw_cert_str[i]; } BIO_free(bio); X509_free(cert); free(raw_cert_str); return cert_str; } /* htp_sslutil_cert_tostr */ unsigned char * htp_sslutil_x509_ext_tostr(evhtp_ssl_t * ssl, const char * oid) { unsigned char * ext_str; X509 * cert; ASN1_OBJECT * oid_obj; int oid_pos; X509_EXTENSION * ext; ASN1_OCTET_STRING * octet; const unsigned char * octet_data; long xlen; int xtag; int xclass; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(oid_obj = OBJ_txt2obj(oid, 1))) { X509_free(cert); return NULL; } ext_str = NULL; oid_pos = X509_get_ext_by_OBJ(cert, oid_obj, -1); if (!(ext = X509_get_ext(cert, oid_pos))) { ASN1_OBJECT_free(oid_obj); X509_free(cert); return NULL; } if (!(octet = X509_EXTENSION_get_data(ext))) { ASN1_OBJECT_free(oid_obj); X509_free(cert); return NULL; } octet_data = octet->data; if (ASN1_get_object(&octet_data, &xlen, &xtag, &xclass, octet->length)) { ASN1_OBJECT_free(oid_obj); X509_free(cert); return NULL; } /* We're only supporting string data. Could optionally add support * for encoded binary data */ if (xlen > 0 && xtag == 0x0C && octet->type == V_ASN1_OCTET_STRING) { ext_str = strndup(octet_data, xlen); } ASN1_OBJECT_free(oid_obj); X509_free(cert); return ext_str; } /* htp_sslutil_x509_ext_tostr */ int htp_sslutil_verify2opts(const char * opts_str) { if (!opts_str || !strcasecmp(opts_str, "off")) { return SSL_VERIFY_NONE; } if (!strcasecmp(opts_str, "optional")) { return SSL_VERIFY_PEER; } if (!strcasecmp(opts_str, "on")) { return SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; } return -1; } int htp_sslutil_add_xheaders(evhtp_headers_t * hdrs, evhtp_ssl_t * ssl, short flags) { int i; if (!hdrs || !ssl) { return -1; } struct { const char * hdr_str; short flag; unsigned char * (* convfn)(evhtp_ssl_t *); } ssl_x_hdrs_[] = { { "X-SSL-Subject", HTP_SSLUTILS_XHDR_SUBJ, htp_sslutil_subject_tostr }, { "X-SSL-Issuer", HTP_SSLUTILS_XHDR_ISSR, htp_sslutil_issuer_tostr }, { "X-SSL-Notbefore", HTP_SSLUTILS_XHDR_NBFR, htp_sslutil_notbefore_tostr }, { "X-SSL-Notafter", HTP_SSLUTILS_XHDR_NAFR, htp_sslutil_notafter_tostr }, { "X-SSL-Serial", HTP_SSLUTILS_XHDR_SERL, htp_sslutil_serial_tostr }, { "X-SSL-Cipher", HTP_SSLUTILS_XHDR_CIPH, htp_sslutil_cipher_tostr }, { "X-SSL-Certificate", HTP_SSLUTILS_XHDR_CERT, htp_sslutil_cert_tostr }, { "X-SSL-SHA1", HTP_SSLUTILS_XHDR_SHA1, htp_sslutil_sha1_tostr }, { NULL, 0, NULL } }; /* remove all of the current x- headers if present */ evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Subject")); evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Issuer")); evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Notbefore")); evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Notafter")); evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Serial")); evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Cipher")); evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Certificate")); if (flags == 0) { return 0; } /* iterate over our ssl_x_hdrs_ struct array, compare the flags, * and if a xhdr flag is set, run the proper *_tostr function and * append it to the `hdrs` passed to this function. */ for (i = 0; ssl_x_hdrs_[i].hdr_str; i++) { char * o_str = NULL; if (flags & ssl_x_hdrs_[i].flag) { if ((o_str = (ssl_x_hdrs_[i].convfn)(ssl))) { evhtp_headers_add_header( hdrs, evhtp_header_new(ssl_x_hdrs_[i].hdr_str, o_str, 0, 1)); evhtp_safe_free(o_str, free); } } } return 0; } /* htp_sslutil_add_xheaders */ libevhtp-1.2.16/thread.c000066400000000000000000000211751322723041700150400ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #ifndef WIN32 #include #endif #include #include #include #include #include "internal.h" #include "evhtp/thread.h" typedef struct evthr_cmd evthr_cmd_t; typedef struct evthr_pool_slist evthr_pool_slist_t; struct evthr_cmd { uint8_t stop; void * args; evthr_cb cb; } __attribute__((packed)); TAILQ_HEAD(evthr_pool_slist, evthr); struct evthr_pool { #ifdef EVTHR_SHARED_PIPE int rdr; int wdr; #endif int nthreads; evthr_pool_slist_t threads; }; struct evthr { int rdr; int wdr; char err; ev_t * event; evbase_t * evbase; pthread_mutex_t lock; pthread_t * thr; evthr_init_cb init_cb; evthr_init_cb exit_cb; void * arg; void * aux; #ifdef EVTHR_SHARED_PIPE int pool_rdr; struct event * shared_pool_ev; #endif TAILQ_ENTRY(evthr) next; }; #define _evthr_read(thr, cmd, sock) \ (recv(sock, cmd, sizeof(evthr_cmd_t), 0) == sizeof(evthr_cmd_t)) ? 1 : 0 static void _evthr_read_cmd(evutil_socket_t sock, short which, void * args) { evthr_t * thread; evthr_cmd_t cmd; int stopped; if (!(thread = (evthr_t *)args)) { return; } stopped = 0; if (evhtp_likely(_evthr_read(thread, &cmd, sock) == 1)) { stopped = cmd.stop; if (evhtp_likely(cmd.cb != NULL)) { (cmd.cb)(thread, cmd.args, thread->arg); } } if (evhtp_unlikely(stopped == 1)) { event_base_loopbreak(thread->evbase); } return; } /* _evthr_read_cmd */ static void * _evthr_loop(void * args) { evthr_t * thread; if (!(thread = (evthr_t *)args)) { return NULL; } if (thread == NULL || thread->thr == NULL) { pthread_exit(NULL); } thread->evbase = event_base_new(); thread->event = event_new(thread->evbase, thread->rdr, EV_READ | EV_PERSIST, _evthr_read_cmd, args); event_add(thread->event, NULL); #ifdef EVTHR_SHARED_PIPE if (thread->pool_rdr > 0) { thread->shared_pool_ev = event_new(thread->evbase, thread->pool_rdr, EV_READ | EV_PERSIST, _evthr_read_cmd, args); event_add(thread->shared_pool_ev, NULL); } #endif pthread_mutex_lock(&thread->lock); if (thread->init_cb != NULL) { (thread->init_cb)(thread, thread->arg); } pthread_mutex_unlock(&thread->lock); event_base_loop(thread->evbase, 0); pthread_mutex_lock(&thread->lock); if (thread->exit_cb != NULL) { (thread->exit_cb)(thread, thread->arg); } pthread_mutex_unlock(&thread->lock); if (thread->err == 1) { fprintf(stderr, "FATAL ERROR!\n"); } pthread_exit(NULL); } /* _evthr_loop */ evthr_res evthr_defer(evthr_t * thread, evthr_cb cb, void * arg) { evthr_cmd_t cmd = { .cb = cb, .args = arg, .stop = 0 }; if (send(thread->wdr, &cmd, sizeof(cmd), 0) <= 0) { return EVTHR_RES_RETRY; } return EVTHR_RES_OK; } evthr_res evthr_stop(evthr_t * thread) { evthr_cmd_t cmd = { .cb = NULL, .args = NULL, .stop = 1 }; if (send(thread->wdr, &cmd, sizeof(evthr_cmd_t), 0) < 0) { return EVTHR_RES_RETRY; } pthread_join(*thread->thr, NULL); return EVTHR_RES_OK; } evbase_t * evthr_get_base(evthr_t * thr) { return thr ? thr->evbase : NULL; } void evthr_set_aux(evthr_t * thr, void * aux) { if (thr) { thr->aux = aux; } } void * evthr_get_aux(evthr_t * thr) { return thr ? thr->aux : NULL; } int evthr_set_initcb(evthr_t * thr, evthr_init_cb cb) { if (thr == NULL) { return -1; } thr->init_cb = cb; return 01; } int evthr_set_exitcb(evthr_t * thr, evthr_exit_cb cb) { if (thr == NULL) { return -1; } thr->exit_cb = cb; return 0; } static evthr_t * _evthr_new(evthr_init_cb init_cb, evthr_exit_cb exit_cb, void * args) { evthr_t * thread; int fds[2]; if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) { return NULL; } evutil_make_socket_nonblocking(fds[0]); evutil_make_socket_nonblocking(fds[1]); if (!(thread = calloc(sizeof(evthr_t), 1))) { return NULL; } thread->thr = malloc(sizeof(pthread_t)); thread->arg = args; thread->rdr = fds[0]; thread->wdr = fds[1]; evthr_set_initcb(thread, init_cb); evthr_set_exitcb(thread, exit_cb); if (pthread_mutex_init(&thread->lock, NULL)) { evthr_free(thread); return NULL; } return thread; } /* evthr_new */ evthr_t * evthr_new(evthr_init_cb init_cb, void * args) { return _evthr_new(init_cb, NULL, args); } evthr_t * evthr_wexit_new(evthr_init_cb init_cb, evthr_exit_cb exit_cb, void * args) { return _evthr_new(init_cb, exit_cb, args); } int evthr_start(evthr_t * thread) { if (thread == NULL || thread->thr == NULL) { return -1; } if (pthread_create(thread->thr, NULL, _evthr_loop, (void *)thread)) { return -1; } return 0; } void evthr_free(evthr_t * thread) { if (thread == NULL) { return; } if (thread->rdr > 0) { close(thread->rdr); } if (thread->wdr > 0) { close(thread->wdr); } if (thread->thr) { free(thread->thr); } if (thread->event) { event_free(thread->event); } if (thread->evbase) { event_base_free(thread->evbase); } free(thread); } /* evthr_free */ void evthr_pool_free(evthr_pool_t * pool) { evthr_t * thread; evthr_t * save; if (pool == NULL) { return; } TAILQ_FOREACH_SAFE(thread, &pool->threads, next, save) { TAILQ_REMOVE(&pool->threads, thread, next); evthr_free(thread); } free(pool); } evthr_res evthr_pool_stop(evthr_pool_t * pool) { evthr_t * thr; evthr_t * save; if (pool == NULL) { return EVTHR_RES_FATAL; } TAILQ_FOREACH_SAFE(thr, &pool->threads, next, save) { evthr_stop(thr); } return EVTHR_RES_OK; } evthr_res evthr_pool_defer(evthr_pool_t * pool, evthr_cb cb, void * arg) { #ifdef EVTHR_SHARED_PIPE evthr_cmd_t cmd = { .cb = cb, .args = arg, .stop = 0 }; if (evhtp_unlikely(send(pool->wdr, &cmd, sizeof(cmd), 0) == -1)) { return EVTHR_RES_RETRY; } return EVTHR_RES_OK; #else evthr_t * thr = NULL; if (pool == NULL) { return EVTHR_RES_FATAL; } if (cb == NULL) { return EVTHR_RES_NOCB; } thr = TAILQ_FIRST(&pool->threads); TAILQ_REMOVE(&pool->threads, thr, next); TAILQ_INSERT_TAIL(&pool->threads, thr, next); return evthr_defer(thr, cb, arg); #endif } /* evthr_pool_defer */ static evthr_pool_t * _evthr_pool_new(int nthreads, evthr_init_cb init_cb, evthr_exit_cb exit_cb, void * shared) { evthr_pool_t * pool; int i; #ifdef EVTHR_SHARED_PIPE int fds[2]; #endif if (nthreads == 0) { return NULL; } if (!(pool = calloc(sizeof(evthr_pool_t), 1))) { return NULL; } pool->nthreads = nthreads; TAILQ_INIT(&pool->threads); #ifdef EVTHR_SHARED_PIPE if (evutil_socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == -1) { return NULL; } evutil_make_socket_nonblocking(fds[0]); evutil_make_socket_nonblocking(fds[1]); pool->rdr = fds[0]; pool->wdr = fds[1]; #endif for (i = 0; i < nthreads; i++) { evthr_t * thread; if (!(thread = evthr_wexit_new(init_cb, exit_cb, shared))) { evthr_pool_free(pool); return NULL; } #ifdef EVTHR_SHARED_PIPE thread->pool_rdr = fds[0]; #endif TAILQ_INSERT_TAIL(&pool->threads, thread, next); } return pool; } /* _evthr_pool_new */ evthr_pool_t * evthr_pool_new(int nthreads, evthr_init_cb init_cb, void * shared) { return _evthr_pool_new(nthreads, init_cb, NULL, shared); } evthr_pool_t * evthr_pool_wexit_new(int nthreads, evthr_init_cb init_cb, evthr_exit_cb exit_cb, void * shared) { return _evthr_pool_new(nthreads, init_cb, exit_cb, shared); } int evthr_pool_start(evthr_pool_t * pool) { evthr_t * evthr = NULL; if (pool == NULL) { return -1; } TAILQ_FOREACH(evthr, &pool->threads, next) { if (evthr_start(evthr) < 0) { return -1; } usleep(5000); } return 0; } libevhtp-1.2.16/zimg_vs_nginx.png000066400000000000000000002106231322723041700170120ustar00rootroot00000000000000PNG  IHDR[$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYsgR@IDATxTҗ; %hlMD]1kTT+v]Ƃ!Q# t{ew333ΛsY6t{ih @ @%BB 9ٟg=lٲ"Y%rgn@ @#*ڵDy=C @ @ lݺY]߫Aֶm[w@ @MB-Wq@ @@@8rn@ @KaG @ '0/q @ |/ @ @%Na^ȹ! @ @`/^A @J¼đsC@ @^,8 @ 8y# @ {Yp@ @(qG !@ @{  @ P%B @@e @ @ K97 @ %0˂#@ @@@8rn@ @KaG @ '0/q @ |/ @ @%Na^ȹ! @ @`/^A @J¼đsC@H7K^a+=rrr [\{myh@(oyb@eիmv '~ZhaOy= BS-\ѣ}G "RBkcu%T/^! X^%K^kW\qE{b o{mٲ%ޭ?r1oɆ;ZΝū]R%U]ƏoW^O_7]vO?4OSLڵk_|W_E^yȑv뭷Zvns-?\ժU.r#ǎ=X:uj*nA "YÆ k/߿:E  @ ȋz˝Osb+ލ[nmYYYkxU^;w\ 6tHFȫ?bꫯx/iժUu7l/;c't5k$SDׯwjҤ}ֳgϤMbŊn4RfxU&On_|URōX4rYgY5}_|ю<Ȩ}?]W_u}Q{G}t" O5kքhzgSԀ"H0@(ydJ%{@H ƍ;y0+҉Ы* >P8q 7,򤴼 mܸq?{lw,צM<\g'o?۶mK?_PPc T^ׯo˖-38ò[W8&6dիM4< $xG5YejWĎB>w/bMr(:#) ^agĉxF}w@KӚHQ{~=裑%Oj"~_&}]{뭷L!a PzǞ;C@ H(Hzɀ.,N.i]B c庿$(5R/}4٠Ȇsj7on;wt+xE/7qDgu4(i} ~M^uo 賎g̘ߺA@(%7@([nuBd̙N܄W6m}7{|))|ݷ+O_,PVZNegNB"Z!| V豼 oShWub}Pu4! YvinA vRq]}%f9mG%(j!ӬICf 6PprDB./@Nn]߅x4*RGxpv(W&dtM JJ=w PhZ;J_{BU*wq!a!Q'iwqQCE^.g%YY7yԋ2]K4yqx%…XAu]"P VeiֻCɓgI@+*{, / ֭ U /:@ fM(Bp|o.&k4i~u-{xC@ ǍZJkZ/ƎERX)\ׯU(ۼy7ZbGb:ښkWȴĊ%bf*#=Z/yy}8)W>xS@yUGKIv[$Q" Y~k#QSBMQ7%I}yx9y•o֬Y+i֝+A= 9?1@Niu4YFJyC@ ʞB4@$HIVq4ZY8 >Bsf\#R7Kh-^&~Xj1c K/c% YmI6eJIk|f,a ノ4To4iM&U?%0xH&bdwZ3=xUve?C\ߑxںM\|L42M&{:lʃtjSx=ɓcUw^V}W _PNQeW"T]@@'Ί҂ħSJLP@/ sC5s\ 5о}?~u=b]s@%Ba^"  Pr.ʋIjs,2dKZk>d%Һ|c9[|p Sdy}(A[I`J)z r=o:+Q<.Aۻwo*]^kPb[뮻Ej+$\{=}(ᥙi?R@y&0/ϣϳCF`ҥ6tPn(T{+ڛ .VdH,kͭz뭡DO/vqUH+ۻ ^=.Vo^sqI|˫8b)U&ky%NV-`7UʲbJhY>}~,1otTA5 ^TJu%bZV<^Fy YWR~^(!Z8wxXh*9k=%LmE@IaB 2N@8 DMI۽ޛǣ')}dL=K+sT9y5I.J'r{kz}WԶ2ʴ6/۷oOxYs֚{Ąk8g8jIF*?n7-^c/.ӽ+-[QS}L-諎ړ8WyR<@@'όL@poeSqGDm:Ua W讶7)UYrEI^Uo O2vMo3Yke WRBM c/bhj )yǍ¤ZMaɚZ.A W^yy.a.ŬޭO4cAO rF ?O=<\d?)##2 oe&/B߽iK2Ky]K, c4i}_6o B y"V1$L:+7\\I5MVI =DXҩ[9_­V5x$_'hs~p O&l,SR> s4gBU,MH dQGp+>R#ܫHZ¡NU!b[dNv* zWԅ~/Z#~d`zѿ5#! yz1ygcg $ID?>0@&O\0ug`*rz$ $.$E X +1Wr*==nzFXbZKًwy?|>;Ex׿BwʳqKëc=&,t_"0&jO9h{b$+O)*ÇsTxdgq`XE\#_>&Ҷ@ LCpM}@@ ̋ΐ ynӎq+qSL(<-[&P#yVC9Mr-y7tZ/o24/o&Q4Y#?S4Q !o7Ѿ\IkAb~& J_ @e R#0pP]qF%jԩ>[n+10ydN/ItM.X _G:\¿n֖Sze>T ׺`ev_ي4%#NJ_@e 'l2WYTL9$ZPXuWhBt%=e|2״vWe+IHw%ܓ'U}n+3y]5Ql>}dD?kk4&uh$%.%䨕#oj?AQPҸgX|׾jh3rׅP"(,]{채ZRrȔ!rL'g J¼dys7@y\|N7/~=Ͻ-ǴA] CN bX[3o\Q޵'D駟> %>zW_}e\ >"Dދ~*SHyoDAwOW"j&23-!@ yQ@JHDQ-o)Kwy'$Zte>k,E }e9%7gr]rS΀X&a=*:bqWz]vh\~A3|h9e^kBJW -1-[ {v CMhHBo7_^}]y|}I9|xVPTM$wwA ?M:ﺠ߅)S_\ @!-@H<\rIzYL6qnnرNxy~0+YȮ<{vGk{:u-UkT(Զn|a޵<@KSO=0MD#á5E-I{ `?.a|aHWD~r,mOnJ2N!~nDyn[2o}U-{dD?3܊^Ph*-DSGiJn׿EHq;649D @ 5H+"0` p r[ji[Wg=^:_7]´>YKImyБ-ik-yeb d^=U 4faz{p_|1?!.Jȥ2ԏO>UW?%Mi]L,a"W[aHkCY_~e'YF>"l 5[=֬Yc\$sh~;ϑ뼕x.lRpBs9ZۊZQJ* j(=;묳B^s]NH#>C@|lW! 'kWb/ lB%uE  @kol>\!#Fp%d/򐨗ؕ/ȼx' ĻT.Qw1ǸnQ-!K/uM\Iь5WZ|]5=<0+c+@}wyM6- xy奎J6t2yN&Lj+AgEd(塇r^gҥ[+W@*M>Br䝲?{v &.};M-gMiY&% J¼tsW@HRM枅) ZWKGkSBG-Nd}D>Z^ yO|ČԋVF! T!LaքIJ)IB["P< Ѥv%%[,ry\sM(_^u J¼tsW@x#2_E~s}]_Vfe>>ʮ?}iE&"O7 P  V^rX<)[ϡlZP]y5&ދ2!qO|'N+IC.ODĥ?\u$$4qRy K@),Y`bΝ;wHV8Zzނ&"tO୾2UFB[\E3K'O&S$0YgD~oƌ㼻>Dc㯇+L=;_e4QmvI:V41>軭DkJܨXkYĺil4艈t>(Abāķ9iG&/8jDLW_=?>c PJVC  @ S07C:7p1H_9*@SQ1w/{ Yn |C _@8ʩNX,O:@.7 F)^xj ,@77p rcn  Žs@$ Vn0 D[D rh׾3=s㏾Zn, 7h>ո;ty[nL+آ, ) B؉ 7^& cLc#>OD%`čM s D+ Z#7v~߻["ٝ~$Dn>.YI/׶Lܝ&B=@l+e -75/9MP> "\פMgD,UHZN&=MF QpB X)^!Q 2]+$W6H`Dړ7HCb8!b[1J>-QcnFrq]Zb*؟PoWI^[ydGĽ$ A7aQg`-}n|@=E $jA&AȽ/|)EQd&T^  I(@y ,ỹ݅ڛ4rh  &JkW뎵OW}MkǏgVV28m*Ւ>V_8NV+&Z_nkJz~ek,mZD.rsՖxJW^y}ߴ;5YහKEjCY}^єXN)2,>AHGU3|pum0<G>A@ DgXvb=ZFv% Bi.nY%6S"5dhm%' @O \W3 nOdm_$+yPݖ&@*ECBXs'@l 2{= "um!dv[)9He:\%9KیҪt yiP8 tΔe|8x<@Ҝi>@t @6y_ @Ҝ<A @@&0/A @@@=@ @(e{|y:@ @Hs4 @ @e¼l/O@ @iNaD @ @l@ @ @ | @ m=< @ 9y݃ @ Ma^Ǘ @ 4'0O{ @ P t @ i>@t @6y_ @Ҝ<A @@&0/A @@@=@ @(e{|y:@ @Hs4 @ @e¼l/O@ @iNRqo˖-6}tYu)-6mdǏjժYϞ=-;;;j95k{uZn\qf\ @ @x̏>hӧuVZws=֭kGqr!Vvm6lXr0vXkڴu;8kӦu͖,Ylq| @  _~>s'<]8p=s6x`:uM8>`6myVN{m̙3_~*[m @ PR*7ol_G{ٲe /)bC5+}ȑVjU{'BUyS{v' ..2={3ƕ-6C @ PR[o 6=crKk}ְaC;O>+WZ LetB K+]^tGKXkWXk @җ@]!=+R&b~}7lZlƌ|^]WѣG{챵kڱN.^]+6ݴ-rъ5k @ %pYg1kzVVL+|}ukc^e%~Sk֬ ~VNt]dѦk88՘3g[Cdxjw @@zhԨQzt^K) +>pkc\~䑦md \;VNtއGIQm6_ $CB @ rM߶mfW]uLx 벍7+sNwNS!U9Ym5@ @@ (\[-X `wuWSLq=&wy$o2y?yƍ<2 /vmrh3~C @@֭[]O?~;mu&a~2&L;)?i$nݺeee\G8t  @ Pʮ0vG$eK,qSO՛Kcԩ6n8@MǫTb'p͛7Ǝ*uogy;_m! @ @H|؎-m6mf= @2:@ @Na#H!@ @ 3z< @ d:y  @ &0 @ > @2:@ @Na#H!@ @ 3z< @ d:y  @ &0 @ > @2:@ @Na#H!@ @ 3z< @ d:y  @ &0 @ > @2:@ @Na#H!@ @ 3z< @ d:y  @ &0 @ > @2:@ @Na#H!@ @ 3z< @ d:y  @ &0 @ > @2J*Ν;;vtIb _$Ge-Z۽{ /؞={ywwCthy* @ PLR㏛oisεO?ڵkg>}ߺuu͝/ ӦM#<ԩco͟?t3gZ~,'''s"|ԩ6qD;ꫯ>T.6C8 @ #xoFkРAm۶Cډ'hs ]Af쬳s.#c ޫWPG%K牶'.ᭉXƺy@ @ (7Z4](R&Ǎ璿ITGnwmGuT}UXѭKWC=n&g}\Ynݺy)ܚ5k ,[~}WFeUGH`? |i嫡,Z~G[Z] @ 5j@ʄ{gÇ7%wShyǎm.ӺO.\h u/gyyܺp]jU_-ϻk,^Ym&SYRHR?jԨaZWi6lp$$Ad>C @@&B8d۸q۷n!o+M5Hh(LE3m׵+r2WNeg  @ PR&}gmZ]6ukY]d>Ca7n؝Wևīlrmߏc@ @@qH0Uz.]GeEg;vpxV U4i;ףG~Xe)A2m)' @ @D %||K<۳gOr:K//ݻD '`ͳcǺs%W"3<ӝ:SݻubԩtJʦuɴ @ @$*ٳ|;tR{Jwo:ܮ];=λ.WVm^#Gڥ^ĩіlJwg{[zr-nmUW]围D U @ PR":t]tE6kxCgKh~7l١tio)!.ox-H[Ct6 do;޻wo1bu!T66C8 @ #aÆ x/߿ӧHR5%\kҤI(kz7md/ڵk[M{3ūN˖-s}N7h|a'OlFrlVԁ @ P6 Hjc1i[2?VZ֩ShS(ZV'6 @ @ UR-U@ @@y#0/o#B @@Z@p@ @(omy^@ @H+)MVOV:i۲`U[aY* @ @ ~Nbڊ|͵,~GYoflٹ1_N~_?fm}엶ZĽ>& vTvzURqq @@TTbvٍ͍=Yljî6A'zj п6,BZ?Xu3m\ 7T){Ϟ\`b*(Ec9@ @ c)Mj<8Wr*ڷz"X|eޚT+ 4θf}msI^x&Å @@"P*2ukTqۑ}%=Z7$'6eSu+'/Oj9ۣybnXu_3;Vi?*YŊl7Kl֝1G^ 9\$>C @MaM]ζ5.ZgY^b +[jlk6Bk3xVagržݶϬ/o'>'*լ2?$zth݈zpQp @@>|HJDvvY=lβM`ke5{qDk}m7l tl[NV'hWhf5Öޒ0$% @@9%Pqurrrl۶hѢb(^|͙3zmIߤ~ֶY-kӤhPשfukVq¼r q۫Pq5?w{m7rĭyqϮ]~[RՎU -x)["\vlϾ]Hbƍ6lUmg ަ~R ק/}k+ @ !ɷjŗZGX#~kY+&;֭o.Ī/ewӮ<ծ^%&Zmyx'u C @  TtZ&񊫬#[_fKٷM9#lT3>9e7&U @yY$%;'kF2{MB[+^֢ar}rG?d߽q @ r2бƾ7Zz*[l?Yc zۙZAT/A/_~c@ @e¼qbX]{~˭aV{*ڵyyIx me$ c! @ xku:uCau:vJlڐlЛCj.iT @yy==Z뮷f%v-?&^y%{$K; @ Ga^<'n[?5k&U777ז̦_;q76.$K !@ @a^FϘU:d;ɧq[$@ٱmjq vm cE4T @ ^5iݛJjYA> M;֭=`߾>uoS?6la=7\T= C @Hgt4@v.A\Ͷmyl[쌅ٕԷի$]96)6z҄P @@:@yߔ  _aU%֣U~]_cS݄6'w_,H!@ +yL++˚v})k}bժI|ON]v>˭Uj 9G.OA@ @Ha}X9kگe=۵ym}%O۟[lL89ܻБL^t  @@&@g⨥q6hh_m{vtO/_f{Ү]9nz[۾3' @ N4e/ڵۺ^we7km5cM֟خj{CeMfGyթS~m?>3gδ~YN^aUm:AD^tFkvoCX㖟4gIq뮸@ @$dV;vXؿӧOfkРAxvyO?-[̅ȑ#CW^m-[޽{ۗ_~.a?e%%]Oǽ8J}˖-bŊ|گ>j{xc'ݷmg&5mdSluvøծlH銎" @@zh޼[fWeb>\68%$R5k*W޻^sO?]?;0'W\DjBSNq\^tGKxѢEΫ~ Ь~֨kqVlI1˯ڸnsu2˪g @:+n2wCYƍܡK,lΞ\{ " @ TH0ҶiݻwwY[n풢͚5]V7r?!BصU-l?w4'P=aVc}iϕyOWk @RA wJ˔i]~?%0aC~KKt+{rףGW8 Mj{N uӺv򜷭{;n4 @ PR",XJ|7߸Dm~˳SO=Օ{饗L۽M:Ք=J$Nﰆ}&i^HϔvK9 K!@ @!Pqu+UE{>k\^O?ԶlbW?.B'Z;c\ue&>v81OBӛ7onegg;]w]r%ɊxϚ̵˗ݻ{dR69Y+ZCm;]6::o~ RQ12B_muNj4 @D@9ƏwIGg 6Վ;Sy:t=ՈK_|V1MtA[o;.:b{cƌ.fΜ<88[oE{q0yd5j{^K+$BTS͑ \lΎMZ5su\eD)15i$5_|W(>iN }ųh3 0/P]_6',u7yjw-?bAvھkj @".SU[uܹ@QYYYD*)\ATXhuIiV!% 0 k~nܢk6`M_>nY.B @H@%і(4#Xulڐ,gc{>ڠ[̲J%ռAuߣ?5"=&3.@ @ cV\@߳rg kxso>`h%`(5[ڟI6&cW= l @@#Ǽ y{wCw7ö\Y C|a5n˪`ou*١]'>uO!@ @|@/7O_=Hnmˏ?=WN]6GٞKnM{t++7l;v*Vf4 @Ҕv֡U~jYYQr @LaΣCRNR͚;;_~ YۛNm ,@yuvEv,-?py czJluw0l@ P%˛ UXoo>5ݲ_[[ P9JBۉ{uIkx{ ԫYy;7mժT7D\ @@@8rn*TW\eUֵGQ`nXe:Jdi`KV`8ChSmNjuWuw-;5|n}2.)}5峺6X)6m m'Q-a-ym۱;շ/ror',v]:XТR,:@,GͣF'ēyq}gw|1kfr kv۸6'߯0"Cݏ~s@SkT'Zp϶LlnMƽ9++Z4ɳ޺IM  @¼Xhhror:6mm}{;q~ˍLkQVڧHOUͱ5V]6=q/*4-i]֧˫ k8Nb  @¼ 4Y0={Y!wmƍq+l]Ԧ~p˪S21 ׽{)!ZqTXZ%kl8P+]7e{/)SA^U\-:nxԮ@e%Ǖn*#D! @u,wCw7ö\9Mb3~:^vEr =Ouw ??dɶ&n7ի'J3o,'l)3{Ir,_ͽ+B] tsd O2`ٚ&?f- DSX)!$L _ 7AA-[wK_o[~1-hZ|Jrkz:= 2s,ĶՊY^viX[)Lb]¹4L6Wתg]b݉ޠ5:HX| FŴ#mvs߶Oe>#Kȅ[\mb&MC .y\<\,6hh=&^}m]8.>m-ZXþ-W\qoZT脱n+9(ɄiZ]]/=-Pwb= .l kyFGq"?/~ J.', &[/sSSϲY$.Š@e INv\);*¼xj noMr۵iS'ݳǦ}l6f QC݃iEYn*nYj/5knujT%ԽX_zk[s* -yZnkص[iYEB(!aR0p_@ ,Xy|P7kڏ7ݚXjh'ҷeA Bok,͚Ynn.Vj>}Xzɭ-+Pp]/X׭^l\$˪Ƀ]1VPN^K6ئ `K7+]LuofmLn~ҕيm?l2AR+Q wE|o?6.vPFEn<7דcÖ)>-36= 3lnۭ%6|U6[;Z*{ uGl/J`_}򕖮 h-)r /rMt֭)0h{E W|E'AB\f 9 ŽEKy@+ۧ.M^bo?SX6[A  3ikhub[ȸ}8kp=+bxߊj{լj5-ca+P5!2r?KlUvE|a3Ufz=9jwZ,< D莑_.Ў +h[?JINAVGw9?˥877^|t_Wcy[0dϤѢJ`d[.//>om:'nD/%Zr)'huҵ[mB]ˣ\LI>Ľ\@]e_qDCھo܌UժQ ;O+ߣHrC@(N}2i[Rv_?N?%@ Uc a]; 3y:}K/AYfl͋۷bՃmԚ/n9.f6 6+mx/? nI8Ӟdܹ uFSK$Լ4wQpfسͱ#z4c5AP VkۤkLYEʳԎ2b;=j6W`),±lgBOL!0ϔiAbj[l╗ێuy֤g7mf;vr@@I fꋃ?@̗5۹+|̽$EXTL0y۴$zL0m}oB׋gGAa75T$- (%g7vNxxȕ;ֶi-;:px ʳ M 0i\n{;SlGoȰoKroт:WCէ,XIuBPD i2X[b4L1[6lhn&ٱ#fiSozګ6j&r 7KpxezC.Mgsgod CS2$@O[:lS'։sָW]lQK lagIsL6;D33D }ncD|4tekwK%Mڵx;?c5A}7gJ&hm}g˲:TݳR>MjNUi! ٻ𨊵ozo!@%4. W(b ( bPl+^ैTZi Cz/߼M{v7nrM}UDFΟ= UkU ePv PӥEbJ?{U `=T pe#8&s^IYa+=QPx{mU# Ԇ\D= ܽ U;ȯn`l g9? l $[$|Y(K[._9Ae|nOfn e7(ګNn~t:,2%%Z\l yկOAwtT7)՘ݕDDپ%nlW>*)V_5xn r=(j)vdt;v9/cVʐj%ppkծ,' pF槰ܕoG -y^??D.˦ww0{_w oԼq U7f@`^{=BCV=íjk$b϶a򕔖GoU5QmC /q8_#|镇гˌR_ݥ 7_tKq A)#q]5T `] iδ!ڲM9k7yVO7#(M׆^Qaf- /=o/CИ 0w۫/Dt1 ߾|"RGN8/ =Mba1o61F<5J Vzyz9|EF=ӣw7?'?d=Xi'лI9Dqcjs\ZuB9@S( ;/\PTlwR_y7]sZe$ 4ͽG^ 33Uӓ:|4UMs9H1ԯ]7stksmP'7v6#:kC;^MNYyhO*:2C5W zt;fn/e/qsopķżm[j8Y}i_O~z! T@J$zN=Iq^eGv@6 t87ˌb~8;>?$-Q?c4^4 >5q@xx4U)܂bi]6vU|y '<֟剡G)܃S  8m32$f]UhJF}i}x4GPtzK:&FYbq/L$'`H_$@@`n^O@9qtmcE5e 44lS:>Ayd?InbH.#G7?_xO>Yn[8Ț5kd nj#sO]dx|| J .4qeuc4blJL<999YKib+LC[תV!ߚJu_yUb' @ap9%pMԀ7jԨҕ|p.W(MP8܋ֹ>|REP>}hǎ${m\CF7_?ۅ)Фn}s1U:1w6ŴnKb @UÆ VH %0u\ UN: iy& 4ҡmڴS\Ν;'߻t"ߵ۷osKJJnS c>RՋ/I9*a^R5Y$zˍ)bTՙ *@ `@hhPX`W_+1bD+@|ΝB)sӧ dn osspSSS.uԑu.Ř6eE~MV:B-r-*a4~o6:eHTe+j#zZf@ @,[NT?2Q6ԩSڵk+{9Yʕ+iÆ t 9/\ˮ*sQ˦qwcڔ԰§YtdkT T+}֥V} @ %+}i>dk e8)Sh4|ϙ3GVL\222{_ qޮVq jv3 lA3,:;\nɤxR}U @Q r^8WKuE1駟Վ?.9q7\pDE2̝իS 6!ע%<2j=s?( @-0dmwqpi&jVd6䋀C @6"y[hAD17M626sۜPP-o((7M[p&:̘En~/O+ @ `u ̭~8!P[ iλzw%byP˪ @5㎳B,QkRC'gLT@ @@`n>K䩧 ;/%N|ss E@ @z̭g3Abxi-@IDAT8hKİTZRb.*@ @@`ngh9eu #%a? @ `%Vi `i'ggjnl`=T @,/8&MfGy雯) C@ @-!`u{zR:~j= @,+ܲh5"ۤ)E9gͤUa' @ `9斳EQ:]R_2x tbTn.*@ @@`n~Sx?5wדߛI% @ `^Dk9cϝ7X @  07'ZM y}"2xmI{__0X @  07%Z 8Qwwںt}fP @0s8ؼXFm >C C@ @̫o `7^닞zͥtz<ʼxQvB @@`^}CoݚLLNNN]G3S~rj= @'z~8v)ڳ5{9מ&  @@W GA>2<8}d_JsfoG @/hv#rrg7{q#P @0]f8#LQoE~͛_vЕ2X @ p8g0= 3xoW}O ;1X @ opX dzgFҩ5MŹC@ @@ 6ƍ[wHjJ޷WsC6$Vɿe+ފ|GoG @`/j@˯RJKJ+~6y"׬9q޲ =5Yԅ @@@`^+3n w/%$j&z+ϧ3K9{@@@ݿEKrSv @Ry|i Dxr(םM[NʗrWp9]͚ @pxq@W^dud0įv#]]'Vޒ#99"A @BjN"@n?m:^0Ry9\ u}fy^WokB& h݆:wF՞N@ `iFcoo0s6e^8/Eˈ9GcdFvkVQN6:q\!!n25 @%[BmBD6~) eP=CEn秤P{3+ @ `IEpP C[̉_.+PmwN$c5|dF @skj\p`-OeiQ!e^T.XϹ~ݬI{v#07( @ 5sB8dm ZAe7\%樟/ yxUY#Sy9  @U>9\7PrzŋTV}/--{(bê @,ܖ @3,ګwݖPի7˥&!ko" @ppq{;ggiDHyQzo#Y*u܃@ ز-_ gݺ׼^RÞo@ [@`nOPX^ < @*^H gOջM;} 32T`' @lU>\  o@Mj~R﫸?C @.cEBz͓0I @T>8\6jyi'OPQVVmcB 8sx;"BﭖQʟ@ تs[}2.@@XޕioH޳[G| @]50r O}vW̤;w޽{)77WiKv>+j`ٴǏSO{8 @ %0?{,ݛ4iB= 0"Đ &Piii(MGEtR^ߟ-ZT~WjՊ,o߾=]~\]KY Uk)),pv:@ `f ̗.]J)))dڽ{7}L~}Go&NHt)(4i}NS@@_._L_|P߾}X,Kw@ g߷׶.W@ f 'OL'Oqg}VqF%J .vQΝi͚5A˖-`?KdX}?0EFFҘ1cW^ .Ю]d]K|lNвi7|n\ @ aFUA7t;իW˞Çk񇐐ӧرdo;׍"]8a܋C-ѦM&''mVi?6@Upq%נ *uK"(?'Y~l @S.lJNƥe˖Νt٦|hӦLKJJ͛4h eqhS6l/x?oCUhCܹ6х2 @ <԰azX`W_[1b^eNVԩSGnJMM%g6XMٰ /^sx _D +'TZ ̠NbtSNw@D 44Aa_ud>*)0pnO^(Fx^ܭ\%ڔ 5mڴ0~ܕ/*U@t7+)_UxyT q@ P+WkJlfy4N9v \222[{ȟ9һtXM]6@xM81d]_I޻>l @K{RlJ6_ږ.ɉ^ ڀ3¨K36o\&6 dvQ@sQzõCҸK}>|lS nGm)ϥRfv]JȤѵlzV;Y?sג2ri'kM6Q۶mNSYfáC*;ze8%};6,f@Ū U(Z$VKy#!@]vt%lʱ!cB)0K`)✝w:}{1}ժUTZt@珻Ӑ!CҥKrmZMI*g6˝?@) nk( @eP^A=]rk=y/4lذJNSٳgk~}ײw3{zKjܧO.wiӚΝvAN-j_~ԭ[*]V^4bj~|r9sb R(W7n8=5kRE[r'ǎ#UDaP(-)#St5:uַ!@M E)+Ц˘qvv/E}r,wҶjU6xy睲w]iqkŋ/RoeW_>LN"YWV8{m*(~)hCm>#1ЄA9۷/q[nQll,U -&ضuchIClNpu @L͙=J.OiR)(Y~M6M=k֕\86ν\Ο?/ZݢE MP^s`nŬYMQZko]m7'ʲh+,Ѧ6!˦خRV/'1]o @$Ч}= Qtn˥abri=ۖXrg,OCn׮M2E٬_nPPX1\Y)-[_UNqoiŜdܖK涌kG c'rl] AHC۷׹!@- D5 $~siAwF"9HIaroPKۤ@ `/ ..%MtZI޳Gm7A @̭NB50癣@ @@ރ+,hvl,\ !@ Pk'-*֦ Q٤Uc' @)ܚ8 `лճ'#;UN@ `sP #"'YW.SvtGT@ G@`OZ*@QT>y/a' @[')K}8;[i\ @jt[лzϼx@ XKq@AA6Jήꃝ @@`ngОّ N @ PIy%lE sSST`' @,-h1 hZKKK zy @J̭@5#`p8>,V3Og @P+xRp3g C;n  @>sUBUW5{tiI ۧw?v@ @--!04=i/C@ Z,?|:j@eNESaFFm}B ؘs{ @^ odކK)~ @@`nI] ،{w̵B @jqwR['NPQvj @%[BmB6'Ӹ1y7{]%EEήW; @,)ܒh)^b @ dgϞ,O:E%bZSʕ+i֭f]mpq(̯Ao7\r[; @JK =8Q''gr  'rSOnݺtI +?ɓ'ӑ#GsJ>,5oޜ.\Vծ!0Dž#۴yիG :)),C)o}tF@ `_Ϗ~-ffZ9'տ?5{9rvwMݼy\]])11y/ʝTcUJpp0qP߰aên`(>\ ` Cٓ ;%& @ Kv]QO9n2<;o+hٲer=[SyM@ ΞwO]N @@pkW MV&8@ -\>#OiݺuZϞ=饗^xHxet?d{JgyFd9o/{9['jZ  J U`' @֪,qNs"w^VZј1cX￯7(yRÃΟ?lR}%K`+FEEQ۶m]8kQNN1 y!hӖ<{"1皣@ k cm'A=־Y\w_% Ϙ1iڴi'V,-tC.< ])|M6&wnb SoݺUn3Q~NJ[.W?Z8 ߆ՓRx8{Hzc @@@T;s-ksΝȫn=\Od$ s0[oV޽;EDDa<~"##ƮSYw_4gW=N Ԥ@JE&Q'&  @deZ|s~5zGi۶m\TÊmv~q)..Cy:WOuDb`juj}(;K MV'Έs*OOOjDwNϯvک C%cwr.ƶY!#~ΦJ5JC2*U@<ԚÇ#UG( 8@xxUwhwƁ8jc1K`c'-'4hI-P̟;wN ݾ}mb;~[-?'>@lWNV6T?ӎڙI^XHnU@ETAib R  @QxD//υ0K`nʅs sN'GTvөQF)SW,Yg055`]Nyυ1\iSV4W:|x*@6JE; *➟O55=&,*YWI>󶢌t*+]eKtw_~o䬕PEW]l ؃*z`>uTxx޽{\nÆ x^2^_/{6m];)b1!r@ .`k)SȤm>gd&v!zmtѫ,DY۹p]oorչXfF P+8;Z`w/5{ntZdu73/EKM \ K u[ E~(V$ji 5|r/{ْE @{駉Ǐݜ. Kdo<vQs]bLm3 P;F.bb=)kª / 졼V#OY6mP=(ȣQ  nM.J́4u?trd^Kǎ{f;mݺuGvcT; P{\ԗ:]R}zock*ˣ Tj<1-"UW}l @ jJ]ΌZJn뮻任2d\mu9܆7DGGӁdR6WnJA:О=U9yhtd"]o=sp~`stŔo @V,=999_ ~!]V͛7'^{۶m_5'LyǏu}Q6m5nX.Ifze8@B ]B%b]%;列wFvkY3ln7g[O="-Sq>F)RHß @.P#!p+Bvn+[Bם&5[:w,9s|HVWͽ5Q[Π˗d=4]߭lj_mOӹZu @')n@ή'#'iwB{nYV|4oˑySjמ~ xw\{y-\ON\}[&F po9F!}Z @s d?[7O>GXdI3`\#m&Ns🤫?(2BԒyE~|E";gsHLm"yh^R{?D0.rT,!!N|)ߪu] -h*Tjg#['O۶WWz+aobz%Kqz|{?NuC-JE$˿Zxkۚ[yy&7/<48w7@s6 C ^CLH /c׭?oRs=.^DW=5w}Z>]̹;_ z9Jョ('[}I+!@ 0@:BIJt4F>͞}^q7PMTUø&OAZfy]sƝp-|[yYP h 0G@{:b}6 ɪ[jc0ߟ<5>:b7m hM-iY~~jΧR煋@ c+z@իʁ9q]xy7j#tC o~-q#܄siB/"P @1̍QB@V <+=DZ 9xlri3[z."AVfE[]s;͛O.^>=@ 0ÇK#8udN =DH 7`FuRի|;9s:C @jt^~E$*ˢTfR`Dg_@QoIT5ث|8 {@`n/O Ԩ_/+ 89JWe2T1T܃+=z=C{|.feV& I{v7~5 @45@*ܽ;K㔡<&OH,:T*UU͟Z;!@ks]CK[߲C~rƻT\Pja}(#Ta' @O2 `>wPԛo .{۸|'GK 8sx @I`듈*W, U~@jA" * @a}ZoRcJIP @ 0@O͞y`k%%t情tauQ @`w@Vh4tE>>Kٔ~@c 0w狻 h1h3ə32p+ @+a-n jR ^{ ^BQN6xw^3X @pL\qW `m^@w4x%t|[`.*@ x @ViQoL::TO7 E@Kc=O  ؘ.ikrE9s:  @GyyָS@!g0=kd_*  8sx @qWc0x/lө`]T @3@P3+N;uJs^Z\l.*@ `!@ )ߩ0=`}T @\mpu %h0*ɦ+P":p<3xWhDF W=!vB `1E 4yi*Φ?@qAɗDb9x0rvsu(A `V@) @ xaP¯;M=RbKJJ;oop * E^]Ļx{;!@jp@ڼ:R}:݂[_11m\}|*̻Oٻoٻ @ Pcb^xԔ7t >1Ϻ|I ]ͷr.ہ<{C@.џ0ZՍO{OJgŽ%T(^U)Q1?*8lJM=\  @1s6?n=Rm1% ֯FA:;njq `|fS!^II(R^x^J2xw5|t(598=  @B@kђUAzY$O?s6vSS>w_H\[Ң))Y#& X?\ ._:Zs^qOeD0/<ƌ1trKTuB9k}A M%̔<Œ*;w&///uϟ?Oj׮EFFg6 ; @$D!!9Yz\{^YYQ_",f3@*f /^HG~yZn]Sat?<}wTRR"iĉ߿_()[R Md65 @yǼzYSR(+K.V}]l/ te ;gS4> @Zf 9~iƌzA74yd9r$_u4iem\N:Ep1,?;iԷo_:+qmvBjӸ|{MAasWUJZP>(_ QRL!O"w1$ɫQ PSf ̍xew cPԩ#\g]xz\,Ѧl؄_|||tfOOOpܟ ͢* @͞KbXe 'asڻSN/(B11ExX ow: EY'eue>+Ws=.juڦl@ `~L*˴Ve~ ?We3EyϨ\9DL]{|/W$C uj2: ggqխja@xG4"?d5+k]Ȥ8@ſĨD{n7#&/6@UsNEV.ߕ!]arJwUmS pY[}:;CnUE)rà p6ҵukMR,r,NΜNi V ̛5k& :Tɒ\Htz|0kmʆ  @v)ܭ;ud)u`+knпʊэyE^_oQҞfCK/R}fmAp= L@IDAT4[jj29www!C%߯xsNsDa@ )o̧#&g#kXF'C-@/HҠA*V~~>\>,{mFxI.9:g&?qmٲx)1Ccƌ5É7\|r5˓Y*!8(AٵkWy:` @ jGu:u[ǏSQvQgʍA v_&UQ8B EO'SBgRBʈjuu/J;5oAajU pNmwZhQCX_ׯu֭J7}\v/vC_5=r|رn:$p;΁+2.vqQHm< ͚5KLg6˝?;vn*˥UA Ay'4<;>:Tr5rh7: ^ă|HhNکh:E"Ol3Rk41j2)76d@̤O?o|Yr}elZ>MM^/:""B{sϖhIL؀,T XA ~V P$zs^yh:Yj4tΧU#/_wܯo_vdnH_l pځmʿyCA9_'3s=K@  ,[܄yю)ˡ9S7/oj$j7m2gu2mpLDu@ `ڽ\m.]ϕt >ޢCE@ " jvaWzR@TQJAAa,j8r[n?&UGIn!@# @ PUy~Mh<_clbUCb6G Lw~xi5cKaV&^8μlƶzlQ->\ @V([|k\y>n<<^Qg-/iBrKVxu:/ZLOä뉿&z_['Wp< @`nӏ@UDN|@^u}#Ghs9g7}5+rhޚBiiFCk;ltC h,Zwxy)r˨P 8ˀ'4yp $$$ŋ:^^^k-A `_g_j9J(;\#=nzʺ\)ƫn=rru5 KV.^dTP+4{99z@܄E.Œ ʼw("]#`AhԠAEaƊ"N& (*4Rwm7og{}og?w̹\8W"հ5pl_L}7{k}_ICJ5*锓ee\iҤLvʙ%A -WQ+4@DJv2v<^sqJYf Mr?WyZJ}:Rv%qchg\Ӛ5IRACed*e瞑uN5ַJ$n @%@1/O`gQCIB Tʶ3=Ne36Vh&==Mm{_ԅYYN_$1P_Ȗ d5'ʀuӦʶŋrW֬d v&nmϏ?=^cCQ??;ѼtR}{iTg n%^ ƍu[>H+D@'*IB ?G0>Զ+dP?N)y0Avm[oOӬ"Z-7Sv_eBà+$;']+3?4JZ^- ?xߺT[,rTJ>JL\MkMvוּ]!:R5u;vq:?=?Tb\=n#Xk7 "֧ZN ybD$ @t;[s]n>ZFrȟIeUS^nY@ZW4w Lk]U[7+or]v?RN_ei4.@9ߺ]K%b^AC1 d@NI?:S\YEN\*94 ѭfVҠ{2oܸwa+ʵ*ju}U~K[Jc]ͩSvҴ {V<,\J|.o"A  WP+4@2Kծh"ucgT6ZVss?U7{_ڏHJN|;/&iNܰ{wzRsT8j]U~bEe{IzKy$xżlC^)Y] @ %P);[=9lXgKmRZmGE'vpY_4?orwI} ?nGd @*JrA <uuĽԪ>kO/cg $f}UT|J~g'jFz<4:ҿN_+?YSl'byQ@@ T'>rOHǕ*εڈd]w\תoYw.;7m}cI]>:&mϿ-}']u[ 2D@ @1O!aQT@ %K;x2iܸLY;m|z%|⛥^n>jYƿJEP]w%.*"R.w m}ז-2?^#T( Mf @Nac{X޾My-޾dI7gW y꛼ǨѲ,tRv6Q~@ޢ/w ޷%l$$b^  @Jo^Zmf|>.^]ޑPòRusM۶muA҂cIJ @  Zum]dW&wlb|'CG%gwO޺պTF jpj @iK ϵr>zo>GLj.#Zh>v}_E۳c|2cJHKCN @(H ZV+NqgUb})Pۻ}:7,7dG:"b)qP@ L'qV7>M~FI"srrå9 'l;G>rl?Ot@ RP#% @ D\oOEwϷKZ|u禍ysRy*IB dϵZSZM!|_{Avn$?x?UK3y jC u~`\o 7y24m&ݭ[zJU+}#B<* @(@HQJ/.Ҫ&?l:jդeKV%V-'D%(y1`8 @ ZK|iXyE5M_v}Ra"Xn]]4C=TW^l\i' @ @pSO)S*zʕeΝRRعSJ~dձcҢEرݻw_,/ݻ2dH,f @ $}ҹsgD"}^}|JٳOu믿.-qɼyW^gϞ?Os9%믿ϭ/c9F:ycʒf"~@ @Hg̵.͚5;j3Fn*S5ݒx@&O,'p\)gy/׵lRx 9qi @ @ BQKΆO0A:uSʽkTW\gU1xsF/~ ywe͚5ҰaCiK?۶m˷޻dڵҥKeƍa!@ h޼UbQ !bnq{U]~;رl2=w\ݽ{XƝ4i,_=woUu0qNq @]۪U"d" WU+٢zϞ=eذaҺuk'[z DeeeInnnq4hh\F4]2.tw}|RvB9@ D@ƍQJ*\1eɒ%RV-7+G3<#?嫯r½eժU+R(z\k()MӠqT~IE,ß5kJ۶m ]i&wLwoP$@ @@Fp\LJom[Ǝ,k͛7GVjg\ZmנqkԨ~{4XIӻo@ @@ T*t_~Vmpt5֤Iwޒx[Rio@ @@2 DB1߱c<"Kg̘Q3gtǺv۵k羋3꺏,iʔ @ $P|޽EVo;~QGUi& .SF!s=o5 һeڴi(+/K.A@ @*@1W_urE;:\sMW\q{gСCe}u.^y3vZ?wzCG- =!ih!r~̓ôi/N: < +@(\CjՊDǵc*ORԯ_Х+QF ]P~þ? my1laO[F;@ѐcrrr~!oj]͛DsNiذa8@ @HQ5jz3@u kҤIa~C @N csU3 A9s;ֵkB8@ @H&>}Ե״mVZh̼BM;;;[[YZj+M7$sΕ~Zԩ;vZnh~vZNwlCG- =!ih!r~Yj3Jە A@SO>]Tncǎ%{?"-ثW/Q+C}.W|#V62 @ !e=zVxHXey2qD4hSč1n*Ç(@ @@(\9sdӦM#-[Pd@ @'qWyuP @ @hc @ @s?@ @H$%Y@ @(~( @ $k-I<IvϞ=2ydc}ٲe+[n J&Mk׮r&բ+d„ u\Vo޼vir'Jvvv u. D e- r-곊~QihłrxZ1|֭7!lV7￿}LՍU16lW@;{m&MdC;?/K[nf7v%sN3w\3j(>> 00&l*ϰ@[4y= m1 }R@@+'D~d7!b$^h"7W. 0-Zo .m㏥^2|7OoQ(mD~@E}/ *퀶ݖx(@broٳgDuﶟЯ_?Y|k?}2SrrrJo܋ɒiӦoDʌ((~gCTf"mw%}6 $ i&V\ _~~TԮ]W|? ~,Y"u񓴯80 ߶@[Ay6/=ByF=#>Z//ݕ@c^₊?̚5uyӦḾXb;vllzl۶\ve./XtBa rAA@h %'5П!:r+ !P~K+bcǎ%{?<@C`ԩnGԩt]ի'"XƍeƌpB|ĉҫWdU5zKn&cVZlڴI.]*SLno߾k5XWdz9.m1 Lߏż^1>H[#@[T<7#e=z&2HڕLg4hf}תUX?ƾ% :S㏛>:_ҥ{|+O!a`+0ہ 9 e_4CG?hJ~1 PE`\$R hڵkennݺҨQ#iҤXR6l` *$JC4~ 9/"bL&iG ~Ɯi'^*+VȄ dbKnnс͛idggeto;k׮bWNH˖-]?Q(rAދgCr&-@'b J,P/vz뭲wޘtн^PrncA|<`XrYYYր6A]~## e@"02!l9}ͳ! C4@YAʀz"L%A%1bOKhtEPCb5f*1;5ִiSs'ǎ3&k׮b/߶m\_|x96:խN:O6\rUW]e.Bӻw1}5dP֪U˕SN7W^y+vmg`rrr{d.ˀA^-ghgCGma?xF~' B 1Kc0G .MRV!TիdYƍeƌpB|ĉέV9祠7~K/g{ҪU+[tmڴI^f2esfRyZ6Δ?SϋU [.]wXe").3f~B1|_qrG:āˀh^;vȳ!%" M[-zυG|C KKl`#Wn6An ;߷Τ{vHI)Δ?裏Ηo|9Rj___SȰė03s]6o<%utʕ.3/zԓo|Q(r0y¾’C"φ^9*UWmĿ+!y"x ]VV^fuqFI&bWHIt^OJaÆ a\!GATL $~gr޲QhQ(}h(@ѣodggG>EkbgPy.t t~ڵkƣtp}\Zև[HEHdŊneݺubgֽ݀ynn;2&#v믋Z#Z]]9!-[LF.(9 79c_ATm1mѽ0A[yD]nn@Q"{͝wi쬽ηlM .>|޽{ 7`}lyufh9r]3*n)Y`/!._Zի;;t.(9ch0N) eO_@[tK@B◲ yB,C8=mڴq{u/*z?oѢnߺ_x|i?WEK/5 0͚5s狛?/K[nFMz>w\3j(gW-Z0֠2d׾aÆ||3i$ӿW֡C;?a9L[43ֈ`v2|R) 8&%Uc2;,\|.M͸q]:mn7SGŲ{g\\T(N1?ꨣ{xk!T. ."KMwn',5bADrA  2D z&9_ATm1m1.\yr7۷)jMeZuv1#tnì{9S9/)ۮd y'FtA2ӗ 4;0O6-51ݿ'Y|ۇ'8@Ce@Eڢ À~gCTGbmQe@@P̃`Z~Ψښ5kD}Wy̱|w⤓NrFtASCl v3V\cl?V:HY=,YU_2hbsT$*e@+rA4@H(<&*@{~d'_˚h`yNؙaczR =s΅Wmq_}W:^R?no'|+WlƌS(_|-B{`ر.Mݷ`z۶ms iAjf֬Y%&|~a9VM[4ΞE}m=cÿU!|=7!*;vXһwo[Bt#d…5Vq%K.Q*X3ְW%{-Gyd얶[T>D~uo4`:tM[ͨqAݏXK/p]ߪU+[WlڴI.]*v{G+XcmAdҘ:u[F:u$ݻw;81ck#v G&N((_`kBa9乁-󺶰$ 9l@y6xcUD8t}t~ :xAEѻtbI+?1ji=od>%I_wuΔ늄>͞з{ ^}7#_ ]we@ƭڠ-.1H@[-z,"A@ |C1OdXk3Z>z謼kԨQ>CtɄjP߸qԭ[ߤI뒙u,(9 oAr-jHb4b쥉(sQ!P-7?蠃:c͞|gm'[`*NHI ^d<^ nLWZV Xn Ɏ!>Y yv U8%[MvmҶm[9~&YuȼyD]X:w@tkM6m\9;v;;`].V;my 2?҂ @ +ӻ'.~6lX`SOɿ/{k_P?>/- ?;sqn4nVVx2|p.O[]YRPKjA4jjժtsQ5ʹS Emw}#W=m O<_Z{|yG}ٝw" #00ooegcU5)o d-ǎT^ *GusQgg+cY[ν( 4hP$u`D_< 6}^C { _2nr|q>U2 \HヶAMݧ*U^ P4nN[ Xea](ѕ %LL/=@Pwqۥ셒|Wr~UIҐ \G^{Ag$=Pw*A)83C:)skHO3ZAR7v)yo48b'c)^z:;}W5k:L5\c/J`C\NpǵAgfT]"n.BUvI1nTP_4n8_I*}20`i x}B~K,,/zuW̳ÞcǎL>wu?LK(gq/n]I>}s,#װ~NZe1}7oSAwؗgWcO/*rc>%Ծ}{\t.׍%.],Bj*b|NvڹZ#G\ f IDATCP{;m49s[!jKw -d=hشiS *#5TT={KʮЃ }~̾XTJg:v S쀥/ZdRɕa̘1riV!vKYb2ޅ$+'-d\z-_h]Y!ۥAe_l:j{A˦&/eS!A5FFރv5YwN g^w-CV 5j,ΉNl=\{JyA"5mRm؁A; Z_m[UuP#^+dpwǂ~1z 7,}%%K_puIUXTP+ZtZ֗SO=U}݂QS bԉG/ŋUA~}=eGbv ))-}{O?uu/JޞrfgKJL.rr쌥{WUv}Ѡʊ]=!v{3fg˔G"PCxv KW^q/`mNDڕ!e\teVctHנJ8R ŧUߪ曱/UTTA)ڥKTQ yKy2`UȀgdb,/jFa g"\ "7u Zj`R%6n*^{u?ܧ؈v`Хk=8ޖT/[d}㷌jBA;[nOzY%.9`  h~^W߂zר͘ ]die&6@}Pk~qcH}ۙ@SˌuY!NTsv)m߸0B*"Xh*g❬=Zc?j`N} T+֧z~崫%ZKHe/{smg%loWQemA O-{ 5/{ٵ?P0$Z U~T|*cwSmDc$=0`i  XUy,3`~1^@y+t܎-})v> OZwu -"u {˷*.ۺ\uTd=y„ EnнYZyI)]>ߒo׭AnP{^~₝:-)VV)ZG쌱n#`gݶmЭ/rKRU }ΤKw_b]m^,vmQ#hr:luߺnm5ad]Jgw@nϿnòn5 փvP}B)o@r 2D_ >y5zh̓?5 &/EW}TZ5kF%eL$F;+K5Q^@h!5Rg$SuF /,:V0ʠ:"B"@tQۢ=jUDɖCizFDyC @e$J+btIR7Y"V(9sF[L>A3[@f >_,־F?ȓ@DyBQ @ @+BG}$O=OƎlN)@<D!@ s !'k7kMw\[UFԏfQ(rA4`#u*7VSbUui(CX/z-9蠃|[D@PÖCJ E3̠3QovL Q`2 3}_-j{u5zoˀcqV"ҁ@R 2i$4f#- @N@}D?ҹsg&W_}uHJ\%T_]tu]?L/)hT(9@ܽ}f͚%{lcǎ˯~+o#(f̣&@(@@gov6l8߬@b/_W.--$ Q`2 [A}B6mD?裲k׮"$u+8ג~aq8(d &A[t 3gt3ժU+S #kam۶Ĥ-[&ח5k' e@"0:9sc=7nݺd˖-2p@YtL2x@}=Zb @2@bRn T @&b @2yFC @@PÖC @@F@1hSy@ @y @ @h(-~*@ @a@1[@ @M