From 16c124d34e43282da6c552739211f8d8aca04791 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 15 Apr 2013 00:59:58 -0500 Subject: Rewrote modules/CMakeLists.txt and do not build the 'extras' modules, if users want them built they should copy or symlink them out of extras --- modules/CMakeLists.txt | 342 +++++------ modules/commands/os_dns.cpp | 2 +- modules/dns.h | 178 ++++++ modules/extra/CMakeLists.txt | 3 + modules/extra/dns.h | 178 ------ modules/extra/m_dns.cpp | 1021 -------------------------------- modules/extra/webcpanel/CMakeLists.txt | 1 + modules/m_dns.cpp | 1021 ++++++++++++++++++++++++++++++++ modules/m_dnsbl.cpp | 2 +- 9 files changed, 1349 insertions(+), 1399 deletions(-) create mode 100644 modules/dns.h create mode 100644 modules/extra/CMakeLists.txt delete mode 100644 modules/extra/dns.h delete mode 100644 modules/extra/m_dns.cpp create mode 100644 modules/m_dns.cpp (limited to 'modules') diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 1ec4e1087..bce14a6ff 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -1,217 +1,163 @@ -add_subdirectory("third/language") - -# Get a list of ALL files and directories within the current directory -file(GLOB MODULES_FOLDERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*") -remove_item_from_list(MODULES_FOLDERS "CMakeFiles") -append_to_list(MODULES_FOLDERS ".") - # If using Windows, add the MODULE_COMPILE define if(WIN32) add_definitions(-DMODULE_COMPILE) endif(WIN32) -# Iterate through the directories -foreach(MODULE_FOLDER ${MODULES_FOLDERS}) - if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_FOLDER}") - # Get a list of all .cpp files in this directory - file(GLOB MODULES_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${MODULE_FOLDER}/*.cpp") - sort_list(MODULES_SRCS) - - # Set all the files to use C++ as well as set their compile flags (use the module-specific compile flags, though) - set_source_files_properties(${MODULES_SRCS} PROPERTIES LANGUAGE CXX COMPILE_FLAGS "${CXXFLAGS}") - - # Create an empty list to store extra include directories - set(EXTRA_INCLUDES) - - set(MODULE TRUE) - - # Get the length of the folder name - string(LENGTH ${MODULE_FOLDER} FOLDER_LEN) - # Add one (the /) - math(EXPR FOLDER_LEN "${FOLDER_LEN} + 1") - - # Iterate through all the source files - foreach(SRC ${MODULES_SRCS}) - # Get the length of the new source file - string(LENGTH ${SRC} SRC_LEN) - # Set FILE_LEN to the length of the source file minus folder length - math(EXPR FILE_LEN "${SRC_LEN} - ${FOLDER_LEN}") - # Get the real name of the source file now - string(SUBSTRING ${SRC} ${FOLDER_LEN} ${FILE_LEN} SRC_REALNAME) - # Convert the real source file extension to have a .so extension - string(REGEX REPLACE "\\.cpp$" ".so" SO ${SRC_REALNAME}) - # Reset skip_depends - set(SKIP_DEPENDS) - # Temporary variable for the current source's include directories - set(TEMP_INCLUDES) - # Calculate the header file dependencies for the given source file - calculate_depends(${SRC} SKIP_DEPENDS MODULE TEMP_INCLUDES) - # If there were some extra include directories, add them to the list - if(TEMP_INCLUDES) - append_to_list(EXTRA_INCLUDES ${TEMP_INCLUDES}) - endif(TEMP_INCLUDES) - # Reset linker flags - set(TEMP_LDFLAGS) - # Reset extra dependencies - set(TEMP_DEPENDENCIES) - # Reset skip_libraries - set(SKIP_LIBRARIES) - # Calculate the library dependencies for the given source file - calculate_libraries(${SRC} SKIP_LIBRARIES MODULE TEMP_LDFLAGS TEMP_DEPENDENCIES) - if(NOT SKIP_DEPENDS AND NOT SKIP_LIBRARIES) - # Reset has_function - set(HAS_FUNCTION) - # Check the function dependencies for the given source file - check_functions(${SRC} HAS_FUNCTION) - # Only continue if this module has all of the required functions - if(HAS_FUNCTION) - # For Visual Studio only, include win32_memory static library, required to override Visual Studio's overrides of the new/delete operators - if(MSVC) - set(WIN32_MEMORY win32_memory) - else(MSVC) - set(WIN32_MEMORY) - endif(MSVC) - # Generate the module and set it's linker flags, also set it to depend on the main Anope executable to be built beforehand - add_library(${SO} MODULE ${SRC}) - # Windows requires this because it's weird - if(WIN32) - set(WIN32_NO_LIBS "/nodefaultlib:\"libcmt.lib\"") - else(WIN32) - set(WIN32_NO_LIBS) - endif(WIN32) - set_target_properties(${SO} PROPERTIES LINKER_LANGUAGE CXX PREFIX "" SUFFIX "" LINK_FLAGS "${TEMP_LDFLAGS} ${WIN32_NO_LIBS}" INSTALL_RPATH_USE_LINK_PATH ON BUILD_WITH_INSTALL_RPATH ON) - add_dependencies(${SO} ${PROGRAM_NAME}) - if(GETTEXT_FOUND) - add_dependencies(${SO} module_language) - endif(GETTEXT_FOUND) - target_link_libraries(${SO} ${TEMP_DEPENDENCIES}) - # For Windows only, have the module link to the export library of Anope as well as wsock32 and Ws2_32 libraries (most of the modules probably don't need this, but this is to be on the safe side), also set it's version - if(WIN32) - target_link_libraries(${SO} ${PROGRAM_NAME} wsock32 Ws2_32 ${WIN32_MEMORY}) - set_target_properties(${PROGRAM_NAME} PROPERTIES VERSION "${VERSION_DOTTED}") - else(WIN32) - if(APPLE) - target_link_libraries(${SO} ${PROGRAM_NAME}) - endif(APPLE) - endif(WIN32) - # Set the module to be installed to the module directory under the data directory - install(TARGETS ${SO} - DESTINATION ${LIB_DIR}/modules - ) - endif(HAS_FUNCTION) - endif(NOT SKIP_DEPENDS AND NOT SKIP_LIBRARIES) - endforeach(SRC) - - if(NOT MODULE_FOLDER STREQUAL ".") - # Get a list of ALL files and directories within this modules directory - file(GLOB SUBMODULE_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${MODULE_FOLDER}/*") - remove_item_from_list(SUBMODULE_DIRS "CMakeFiles") - remove_item_from_list(SUBMODULE_DIRS "third/language") - - foreach(SUBDIR ${SUBMODULE_DIRS}) - if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}") - file(GLOB_RECURSE MODULES_SUBDIR_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${SUBDIR}/*.cpp") - sort_list(MODULES_SUBDIR_SRCS) - - # Set all the files to use C++ as well as set their compile flags (use the module-specific compile flags, though) - set_source_files_properties(${MODULES_SUBDIR_SRCS} PROPERTIES LANGUAGE CXX COMPILE_FLAGS "${CXXFLAGS}") - - # Get the length of this subdir - string(LENGTH ${SUBDIR} SUBDIR_LEN) - # Calculate the length of the folder - math(EXPR FILE_LEN "${SUBDIR_LEN} - ${FOLDER_LEN}") - # Extract this subfolders name to use to generate the .so file - string(SUBSTRING ${SUBDIR} ${FOLDER_LEN} ${FILE_LEN} SUBDIR_REALNAME) - # Add .so to the end of the directory name, this will be the module's name - set(SO "${SUBDIR_REALNAME}.so") - - # Temporary linker flags for this subdirectory - set(SUBDIR_LDFLAGS "${LDFLAGS}") - # Temporary extra dependencies for this subdirectory - set(SUBDIR_EXTRA_DEPENDS) - # Reset skip_depends - set(SKIP_DEPENDS) - # Reset skip_libraries - set(SKIP_LIBRARIES) +function(build_modules SRC) + if(NOT ${SRC} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR} AND EXISTS "${SRC}/CMakeLists.txt") + add_subdirectory("${SRC}") + else(NOT ${SRC} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR} AND EXISTS "${SRC}/CMakeLists.txt") + file(GLOB MODULES_SRCS "${SRC}/*") + foreach(MODULE_SRC ${MODULES_SRCS}) + if(IS_DIRECTORY "${MODULE_SRC}") + build_modules("${MODULE_SRC}") + else(IS_DIRECTORY "${MODULE_SRC}") + string(REGEX MATCH "\\.cpp$" CPP ${MODULE_SRC}) + if(CPP) + file(RELATIVE_PATH FNAME ${SRC} ${MODULE_SRC}) + # Convert the real source file extension to have a .so extension + string(REGEX REPLACE "\\.cpp$" ".so" SO ${FNAME}) + # Temporary variable for the current source's include directories + set(TEMP_INCLUDES) + # Calculate the header file dependencies for the given source file + calculate_depends(${MODULE_SRC} TEMP_INCLUDES) + # If there were some extra include directories, add them to the list + if(TEMP_INCLUDES) + append_to_list(EXTRA_INCLUDES ${TEMP_INCLUDES}) + endif(TEMP_INCLUDES) + + # Reset linker flags + set(TEMP_LDFLAGS) + # Reset extra dependencies + set(TEMP_DEPENDENCIES) + # Calculate the library dependencies for the given source file + calculate_libraries(${MODULE_SRC} TEMP_LDFLAGS TEMP_DEPENDENCIES) # Reset has_function - set(HAS_FUNCTION TRUE) - - # Iterate through the source files in the subdirectory - foreach(SRC ${MODULES_SUBDIR_SRCS}) - if(NOT SKIP_DEPENDS AND NOT SKIP_LIBRARIES AND HAS_FUNCTION) - # Temporary variable for the current source's include directories - set(TEMP_INCLUDES) - # Calculate the header file dependencies for the given source file - calculate_depends(${SRC} SKIP_DEPENDS MODULE TEMP_INCLUDES) - # If there were some extra include directories, add them to the list - if(TEMP_INCLUDES) - append_to_list(EXTRA_INCLUDES ${TEMP_INCLUDES}) - endif(TEMP_INCLUDES) - # Reset linker flags - set(TEMP_LDFLAGS) - # Reset extra dependencies - set(TEMP_DEPENDENCIES) - # Calculate the library dependencies for the given source file - calculate_libraries(${SRC} SKIP_LIBRARIES MODULE TEMP_LDFLAGS TEMP_DEPENDENCIES) - # Check the function dependencies for the given source file - check_functions(${SRC} HAS_FUNCTION) - - # Append this source file's linker flags to the subdirectoy's linker flags, if there are any to append - if(TEMP_DEPENDENCIES) - append_to_list(SUBDIR_EXTRA_DEPENDS ${TEMP_DEPDENCIES}) - endif(TEMP_DEPENDENCIES) - endif(NOT SKIP_DEPENDS AND NOT SKIP_LIBRARIES AND HAS_FUNCTION) - endforeach(SRC) - - # Continue if library and function requirements are met - if(NOT SKIP_DEPENDS AND NOT SKIP_LIBRARIES AND HAS_FUNCTION) - # Remove duplicates from the linker flags - if(SUBDIR_LDFLAGS) - remove_list_duplicates(SUBDIR_LDFLAGS) - endif(SUBDIR_LDFLAGS) - # Remove duplicates from the extra dependencies - if(SUBDIR_EXTRA_DEPENDS) - remove_list_duplicates(SUBDIR_EXTRA_DEPENDS) - endif(SUBDIR_EXTRA_DEPENDS) - + set(HAS_FUNCTION) + # Check the function dependencies for the given source file + check_functions(${MODULE_SRC} HAS_FUNCTION) + # Only continue if this module has all of the required functions + if(HAS_FUNCTION) # For Visual Studio only, include win32_memory static library, required to override Visual Studio's overrides of the new/delete operators if(MSVC) set(WIN32_MEMORY win32_memory) else(MSVC) set(WIN32_MEMORY) endif(MSVC) - - # Generate the module and set it's linker flags, also set it to depend on the main Anope executable to be built beforehand - add_library(${SO} MODULE ${MODULES_SUBDIR_SRCS}) - set_target_properties(${SO} PROPERTIES LINKER_LANGUAGE CXX PREFIX "" SUFFIX "" LINK_FLAGS "${SUBDIR_LDFLAGS}" INSTALL_RPATH_USE_LINK_PATH ON BUILD_WITH_INSTALL_RPATH ON) + # Generate the module and set its linker flags, also set it to depend on the main Anope executable to be built beforehand + add_library(${SO} MODULE ${MODULE_SRC}) + # Windows requires this because it's weird + if(WIN32) + set(WIN32_NO_LIBS "/nodefaultlib:\"libcmt.lib\"") + else(WIN32) + set(WIN32_NO_LIBS) + endif(WIN32) + set_target_properties(${SO} PROPERTIES LINKER_LANGUAGE CXX PREFIX "" SUFFIX "" LINK_FLAGS "${TEMP_LDFLAGS} ${WIN32_NO_LIBS}" INSTALL_RPATH_USE_LINK_PATH ON BUILD_WITH_INSTALL_RPATH ON) add_dependencies(${SO} ${PROGRAM_NAME}) - target_link_libraries(${SO} ${PROGRAM_NAME}) if(GETTEXT_FOUND) add_dependencies(${SO} module_language) endif(GETTEXT_FOUND) - # For Windows only, have the module link to the export library of Anope as well as wsock32 and Ws2_32 libraries (most of the modules probably don't need this, but this is to be on the safe side), also set it's version + target_link_libraries(${SO} ${TEMP_DEPENDENCIES}) + # For Windows only, have the module link to the export library of Anope as well as wsock32 and Ws2_32 libraries (most of the modules probably don't need this, but this is to be on the safe side), also set its version if(WIN32) - target_link_libraries(${SO} ${PROGRAM_NAME} wsock32 Ws2_32 ${WIN32_MEMORY} ${SUBDIR_EXTRA_DEPENDS}) - set_target_properties(${PROGRAM_NAME} PROPERTIES VERSION "${VERSION_DOTTED}") + target_link_libraries(${SO} ${PROGRAM_NAME} wsock32 Ws2_32 ${WIN32_MEMORY}) + set_target_properties(${PROGRAM_NAME} PROPERTIES VERSION "${VERSION_DOTTED}") + else(WIN32) + if(APPLE) + target_link_libraries(${SO} ${PROGRAM_NAME}) + endif(APPLE) endif(WIN32) # Set the module to be installed to the module directory under the data directory - install(TARGETS ${SO} - DESTINATION ${LIB_DIR}/modules - ) - endif(NOT SKIP_DEPENDS AND NOT SKIP_LIBRARIES AND HAS_FUNCTION) - - # Run the directories CMakeLists.txt if there is one - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/CMakeLists.txt") - add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}") - endif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/CMakeLists.txt") - endif(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}") - endforeach(SUBDIR) - endif(NOT MODULE_FOLDER STREQUAL ".") - endif(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_FOLDER}") -endforeach(MODULE_FOLDER) - -# If there were extra include directories, remove the duplicates and add the directories to the include path -if(EXTRA_INCLUDES) - remove_list_duplicates(EXTRA_INCLUDES) - include_directories(${EXTRA_INCLUDES}) -endif(EXTRA_INCLUDES) + install(TARGETS ${SO} DESTINATION ${LIB_DIR}/modules) + endif(HAS_FUNCTION) + endif(CPP) + endif(IS_DIRECTORY "${MODULE_SRC}") + endforeach(MODULE_SRC ${MODULES_SRCS}) + endif(NOT ${SRC} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR} AND EXISTS "${SRC}/CMakeLists.txt") +endfunction(build_modules) + +function(build_subdir) + file(GLOB_RECURSE MODULES_SUBDIR_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cpp") + sort_list(MODULES_SUBDIR_SRCS) + + GET_FILENAME_COMPONENT(FOLDER_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) + set(SO "${FOLDER_NAME}.so") + + # Set all the files to use C++ as well as set their compile flags (use the module-specific compile flags, though) + set_source_files_properties(${MODULES_SUBDIR_SRCS} PROPERTIES LANGUAGE CXX COMPILE_FLAGS "${CXXFLAGS}") + + set(HAS_FUNCTION TRUE) + + # Iterate through the source files in the subdirectory + foreach(SRC ${MODULES_SUBDIR_SRCS}) + if(HAS_FUNCTION) + # Temporary variable for the current source's include directories + set(TEMP_INCLUDES) + # Calculate the header file dependencies for the given source file + calculate_depends(${SRC} TEMP_INCLUDES) + # If there were some extra include directories, add them to the list + if(TEMP_INCLUDES) + include_directories(${TEMP_INCLUDES}) + endif(TEMP_INCLUDES) + + # Reset linker flags + set(TEMP_LDFLAGS) + # Reset extra dependencies + set(TEMP_DEPENDENCIES) + # Calculate the library dependencies for the given source file + calculate_libraries(${SRC} SKIP_LIBRARIES MODULE TEMP_LDFLAGS TEMP_DEPENDENCIES) + # Check the function dependencies for the given source file + check_functions(${SRC} HAS_FUNCTION) + + # Append this source file's linker flags to the subdirectoy's linker flags, if there are any to append + if(TEMP_DEPENDENCIES) + append_to_list(SUBDIR_EXTRA_DEPENDS ${TEMP_DEPDENCIES}) + endif(TEMP_DEPENDENCIES) + endif(HAS_FUNCTION) + endforeach(SRC ${MODULES_SUBDIR_SRCS}) + + # Continue if library and function requirements are met + if(HAS_FUNCTION) + # Remove duplicates from the linker flags + if(SUBDIR_LDFLAGS) + remove_list_duplicates(SUBDIR_LDFLAGS) + endif(SUBDIR_LDFLAGS) + + # Remove duplicates from the extra dependencies + if(SUBDIR_EXTRA_DEPENDS) + remove_list_duplicates(SUBDIR_EXTRA_DEPENDS) + endif(SUBDIR_EXTRA_DEPENDS) + + # For Visual Studio only, include win32_memory static library, required to override Visual Studio's overrides of the new/delete operators + if(MSVC) + set(WIN32_MEMORY win32_memory) + else(MSVC) + set(WIN32_MEMORY) + endif(MSVC) + + # Generate the module and set it's linker flags, also set it to depend on the main Anope executable to be built beforehand + add_library(${SO} MODULE ${MODULES_SUBDIR_SRCS}) + set_target_properties(${SO} PROPERTIES LINKER_LANGUAGE CXX PREFIX "" SUFFIX "" LINK_FLAGS "${SUBDIR_LDFLAGS}" INSTALL_RPATH_USE_LINK_PATH ON BUILD_WITH_INSTALL_RPATH ON) + add_dependencies(${SO} ${PROGRAM_NAME}) + if(GETTEXT_FOUND) + add_dependencies(${SO} module_language) + endif(GETTEXT_FOUND) + target_link_libraries(${SO} ${SUBDIR_EXTRA_DEPENDS}) + # For Windows only, have the module link to the export library of Anope as well as wsock32 and Ws2_32 libraries (most of the modules probably don't need this, but this is to be on the safe side), also set it's version + if(WIN32) + target_link_libraries(${SO} ${PROGRAM_NAME} wsock32 Ws2_32 ${WIN32_MEMORY}) + set_target_properties(${PROGRAM_NAME} PROPERTIES VERSION "${VERSION_DOTTED}") + else(WIN32) + if(APPLE) + target_link_libraries(${SO} ${PROGRAM_NAME}) + endif(APPLE) + endif(WIN32) + + # Set the module to be installed to the module directory under the data directory + install(TARGETS ${SO} DESTINATION ${LIB_DIR}/modules) + + endif(HAS_FUNCTION) +endfunction(build_subdir) + +build_modules(${CMAKE_CURRENT_SOURCE_DIR}) + diff --git a/modules/commands/os_dns.cpp b/modules/commands/os_dns.cpp index 04971e89a..346ff4727 100644 --- a/modules/commands/os_dns.cpp +++ b/modules/commands/os_dns.cpp @@ -6,7 +6,7 @@ */ #include "module.h" -#include "../extra/dns.h" +#include "../dns.h" static ServiceReference dnsmanager("DNS::Manager", "dns/manager"); diff --git a/modules/dns.h b/modules/dns.h new file mode 100644 index 000000000..469bb0e82 --- /dev/null +++ b/modules/dns.h @@ -0,0 +1,178 @@ +/* + * + * (C) 2003-2013 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + * + */ + +#ifndef DNS_H +#define DNS_H + +namespace DNS +{ + /** Valid query types + */ + enum QueryType + { + /* Nothing */ + QUERY_NONE, + /* A simple A lookup */ + QUERY_A = 1, + /* An authoritative name server */ + QUERY_NS = 2, + /* A CNAME lookup */ + QUERY_CNAME = 5, + /* Start of a zone of authority */ + QUERY_SOA = 6, + /* Reverse DNS lookup */ + QUERY_PTR = 12, + /* IPv6 AAAA lookup */ + QUERY_AAAA = 28, + /* Zone transfer */ + QUERY_AXFR = 252 + }; + + /** Flags that can be AND'd into DNSPacket::flags to receive certain values + */ + enum + { + QUERYFLAGS_QR = 0x8000, + QUERYFLAGS_OPCODE = 0x7800, + QUERYFLAGS_AA = 0x400, + QUERYFLAGS_TC = 0x200, + QUERYFLAGS_RD = 0x100, + QUERYFLAGS_RA = 0x80, + QUERYFLAGS_Z = 0x70, + QUERYFLAGS_RCODE = 0xF + }; + + enum Error + { + ERROR_NONE, + ERROR_UNKNOWN, + ERROR_UNLOADED, + ERROR_TIMEDOUT, + ERROR_NOT_AN_ANSWER, + ERROR_NONSTANDARD_QUERY, + ERROR_FORMAT_ERROR, + ERROR_SERVER_FAILURE, + ERROR_DOMAIN_NOT_FOUND, + ERROR_NOT_IMPLEMENTED, + ERROR_REFUSED, + ERROR_NO_RECORDS, + ERROR_INVALIDTYPE + }; + + struct Question + { + Anope::string name; + QueryType type; + unsigned short qclass; + + Question() : type(QUERY_NONE), qclass(0) { } + Question(const Anope::string &n, QueryType t, unsigned short c = 1) : name(n), type(t), qclass(c) { } + }; + + struct ResourceRecord : public Question + { + unsigned int ttl; + Anope::string rdata; + time_t created; + + ResourceRecord(const Anope::string &n, QueryType t, unsigned short c = 1) : Question(n, t, c), ttl(0), created(Anope::CurTime) { } + ResourceRecord(const Question &q) : Question(q), ttl(0), created(Anope::CurTime) { } + }; + + struct Query + { + std::vector questions; + std::vector answers, authorities, additional; + Error error; + + Query() : error(ERROR_NONE) { } + Query(const Question &q) : error(ERROR_NONE) { questions.push_back(q); } + }; + + class Packet : public Query + { + public: + static const int POINTER = 0xC0; + static const int LABEL = 0x3F; + static const int HEADER_LENGTH = 12; + + virtual ~Packet() { } + }; + + class ReplySocket; + class Request; + + /** DNS manager + */ + class Manager : public Service + { + public: + Manager(Module *creator) : Service(creator, "DNS::Manager", "dns/manager") { } + virtual ~Manager() { } + + virtual void Process(Request *req) = 0; + virtual void RemoveRequest(Request *req) = 0; + + virtual bool HandlePacket(ReplySocket *s, const unsigned char *const data, int len, sockaddrs *from) = 0; + + virtual void UpdateSerial() = 0; + virtual uint32_t GetSerial() const = 0; + }; + + /** A DNS query. + */ + class Request : public Timer, public Question + { + Manager *manager; + public: + /* Use result cache if available */ + bool use_cache; + /* Request id */ + unsigned short id; + /* Creator of this request */ + Module *creator; + + Request(Manager *mgr, Module *c, const Anope::string &addr, QueryType qt, bool cache = false) : Timer(0), Question(addr, qt), manager(mgr), + use_cache(cache), id(0), creator(c) { } + + virtual ~Request() + { + manager->RemoveRequest(this); + } + + /** Called when this request succeeds + * @param r The query sent back from the nameserver + */ + virtual void OnLookupComplete(const Query *r) = 0; + + /** Called when this request fails or times out. + * @param r The query sent back from the nameserver, check the error code. + */ + virtual void OnError(const Query *r) { } + + /** Used to time out the query, xalls OnError and lets the TimerManager + * delete this request. + */ + void Tick(time_t) anope_override + { + Log(LOG_DEBUG_2) << "Resolver: timeout for query " << this->name; + Query rr(*this); + rr.error = ERROR_TIMEDOUT; + this->OnError(&rr); + } + }; + +} // namespace DNS + +#endif // DNS_H + + diff --git a/modules/extra/CMakeLists.txt b/modules/extra/CMakeLists.txt new file mode 100644 index 000000000..6c50f8605 --- /dev/null +++ b/modules/extra/CMakeLists.txt @@ -0,0 +1,3 @@ +# This file is here to prevent the building of the extras directory. +# Users who want to build the contents of this directory should copy +# or symlink files out of it. diff --git a/modules/extra/dns.h b/modules/extra/dns.h deleted file mode 100644 index 469bb0e82..000000000 --- a/modules/extra/dns.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * - * (C) 2003-2013 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - * - * Based on the original code of Epona by Lara. - * Based on the original code of Services by Andy Church. - * - */ - -#ifndef DNS_H -#define DNS_H - -namespace DNS -{ - /** Valid query types - */ - enum QueryType - { - /* Nothing */ - QUERY_NONE, - /* A simple A lookup */ - QUERY_A = 1, - /* An authoritative name server */ - QUERY_NS = 2, - /* A CNAME lookup */ - QUERY_CNAME = 5, - /* Start of a zone of authority */ - QUERY_SOA = 6, - /* Reverse DNS lookup */ - QUERY_PTR = 12, - /* IPv6 AAAA lookup */ - QUERY_AAAA = 28, - /* Zone transfer */ - QUERY_AXFR = 252 - }; - - /** Flags that can be AND'd into DNSPacket::flags to receive certain values - */ - enum - { - QUERYFLAGS_QR = 0x8000, - QUERYFLAGS_OPCODE = 0x7800, - QUERYFLAGS_AA = 0x400, - QUERYFLAGS_TC = 0x200, - QUERYFLAGS_RD = 0x100, - QUERYFLAGS_RA = 0x80, - QUERYFLAGS_Z = 0x70, - QUERYFLAGS_RCODE = 0xF - }; - - enum Error - { - ERROR_NONE, - ERROR_UNKNOWN, - ERROR_UNLOADED, - ERROR_TIMEDOUT, - ERROR_NOT_AN_ANSWER, - ERROR_NONSTANDARD_QUERY, - ERROR_FORMAT_ERROR, - ERROR_SERVER_FAILURE, - ERROR_DOMAIN_NOT_FOUND, - ERROR_NOT_IMPLEMENTED, - ERROR_REFUSED, - ERROR_NO_RECORDS, - ERROR_INVALIDTYPE - }; - - struct Question - { - Anope::string name; - QueryType type; - unsigned short qclass; - - Question() : type(QUERY_NONE), qclass(0) { } - Question(const Anope::string &n, QueryType t, unsigned short c = 1) : name(n), type(t), qclass(c) { } - }; - - struct ResourceRecord : public Question - { - unsigned int ttl; - Anope::string rdata; - time_t created; - - ResourceRecord(const Anope::string &n, QueryType t, unsigned short c = 1) : Question(n, t, c), ttl(0), created(Anope::CurTime) { } - ResourceRecord(const Question &q) : Question(q), ttl(0), created(Anope::CurTime) { } - }; - - struct Query - { - std::vector questions; - std::vector answers, authorities, additional; - Error error; - - Query() : error(ERROR_NONE) { } - Query(const Question &q) : error(ERROR_NONE) { questions.push_back(q); } - }; - - class Packet : public Query - { - public: - static const int POINTER = 0xC0; - static const int LABEL = 0x3F; - static const int HEADER_LENGTH = 12; - - virtual ~Packet() { } - }; - - class ReplySocket; - class Request; - - /** DNS manager - */ - class Manager : public Service - { - public: - Manager(Module *creator) : Service(creator, "DNS::Manager", "dns/manager") { } - virtual ~Manager() { } - - virtual void Process(Request *req) = 0; - virtual void RemoveRequest(Request *req) = 0; - - virtual bool HandlePacket(ReplySocket *s, const unsigned char *const data, int len, sockaddrs *from) = 0; - - virtual void UpdateSerial() = 0; - virtual uint32_t GetSerial() const = 0; - }; - - /** A DNS query. - */ - class Request : public Timer, public Question - { - Manager *manager; - public: - /* Use result cache if available */ - bool use_cache; - /* Request id */ - unsigned short id; - /* Creator of this request */ - Module *creator; - - Request(Manager *mgr, Module *c, const Anope::string &addr, QueryType qt, bool cache = false) : Timer(0), Question(addr, qt), manager(mgr), - use_cache(cache), id(0), creator(c) { } - - virtual ~Request() - { - manager->RemoveRequest(this); - } - - /** Called when this request succeeds - * @param r The query sent back from the nameserver - */ - virtual void OnLookupComplete(const Query *r) = 0; - - /** Called when this request fails or times out. - * @param r The query sent back from the nameserver, check the error code. - */ - virtual void OnError(const Query *r) { } - - /** Used to time out the query, xalls OnError and lets the TimerManager - * delete this request. - */ - void Tick(time_t) anope_override - { - Log(LOG_DEBUG_2) << "Resolver: timeout for query " << this->name; - Query rr(*this); - rr.error = ERROR_TIMEDOUT; - this->OnError(&rr); - } - }; - -} // namespace DNS - -#endif // DNS_H - - diff --git a/modules/extra/m_dns.cpp b/modules/extra/m_dns.cpp deleted file mode 100644 index e8f3bd758..000000000 --- a/modules/extra/m_dns.cpp +++ /dev/null @@ -1,1021 +0,0 @@ -/* - * - * (C) 2003-2013 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - * - * Based on the original code of Epona by Lara. - * Based on the original code of Services by Andy Church. - * - */ - -#include "module.h" -#include "dns.h" - -using namespace DNS; - -namespace -{ - Anope::string admin, nameservers; - int refresh; - time_t timeout; -} - -/** A full packet sent or recieved to/from the nameserver - */ -class MyPacket : public Packet -{ - void PackName(unsigned char *output, unsigned short output_size, unsigned short &pos, const Anope::string &name) - { - if (name.length() + 2 > output_size) - throw SocketException("Unable to pack name"); - - Log(LOG_DEBUG_2) << "Resolver: PackName packing " << name; - - sepstream sep(name, '.'); - Anope::string token; - - while (sep.GetToken(token)) - { - output[pos++] = token.length(); - memcpy(&output[pos], token.c_str(), token.length()); - pos += token.length(); - } - - output[pos++] = 0; - } - - Anope::string UnpackName(const unsigned char *input, unsigned short input_size, unsigned short &pos) - { - Anope::string name; - unsigned short pos_ptr = pos, lowest_ptr = input_size; - bool compressed = false; - - if (pos_ptr >= input_size) - throw SocketException("Unable to unpack name - no input"); - - while (input[pos_ptr] > 0) - { - unsigned short offset = input[pos_ptr]; - - if (offset & POINTER) - { - if ((offset & POINTER) != POINTER) - throw SocketException("Unable to unpack name - bogus compression header"); - if (pos_ptr + 1 >= input_size) - throw SocketException("Unable to unpack name - bogus compression header"); - - /* Place pos at the second byte of the first (farthest) compression pointer */ - if (compressed == false) - { - ++pos; - compressed = true; - } - - pos_ptr = (offset & LABEL) << 8 | input[pos_ptr + 1]; - - /* Pointers can only go back */ - if (pos_ptr >= lowest_ptr) - throw SocketException("Unable to unpack name - bogus compression pointer"); - lowest_ptr = pos_ptr; - } - else - { - if (pos_ptr + offset + 1 >= input_size) - throw SocketException("Unable to unpack name - offset too large"); - if (!name.empty()) - name += "."; - for (unsigned i = 1; i <= offset; ++i) - name += input[pos_ptr + i]; - - pos_ptr += offset + 1; - if (compressed == false) - /* Move up pos */ - pos = pos_ptr; - } - } - - /* +1 pos either to one byte after the compression pointer or one byte after the ending \0 */ - ++pos; - - Log(LOG_DEBUG_2) << "Resolver: UnpackName successfully unpacked " << name; - - return name; - } - - Question UnpackQuestion(const unsigned char *input, unsigned short input_size, unsigned short &pos) - { - Question question; - - question.name = this->UnpackName(input, input_size, pos); - - if (pos + 4 > input_size) - throw SocketException("Unable to unpack question"); - - question.type = static_cast(input[pos] << 8 | input[pos + 1]); - pos += 2; - - question.qclass = input[pos] << 8 | input[pos + 1]; - pos += 2; - - return question; - } - - ResourceRecord UnpackResourceRecord(const unsigned char *input, unsigned short input_size, unsigned short &pos) - { - ResourceRecord record = static_cast(this->UnpackQuestion(input, input_size, pos)); - - if (pos + 6 > input_size) - throw SocketException("Unable to unpack resource record"); - - record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3]; - pos += 4; - - //record.rdlength = input[pos] << 8 | input[pos + 1]; - pos += 2; - - switch (record.type) - { - case QUERY_A: - { - if (pos + 4 > input_size) - throw SocketException("Unable to unpack resource record"); - - in_addr a; - a.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16) | (input[pos + 3] << 24); - pos += 4; - - sockaddrs addrs; - addrs.ntop(AF_INET, &a); - - record.rdata = addrs.addr(); - break; - } - case QUERY_AAAA: - { - if (pos + 16 > input_size) - throw SocketException("Unable to unpack resource record"); - - in6_addr a; - for (int j = 0; j < 16; ++j) - a.s6_addr[j] = input[pos + j]; - pos += 16; - - sockaddrs addrs; - addrs.ntop(AF_INET6, &a); - - record.rdata = addrs.addr(); - break; - } - case QUERY_CNAME: - case QUERY_PTR: - { - record.rdata = this->UnpackName(input, input_size, pos); - break; - } - default: - break; - } - - Log(LOG_DEBUG_2) << "Resolver: " << record.name << " -> " << record.rdata; - - return record; - } - - public: - Manager *manager; - /* Source or destination of the packet */ - sockaddrs addr; - /* ID for this packet */ - unsigned short id; - /* Flags on the packet */ - unsigned short flags; - - MyPacket(Manager *m, sockaddrs *a) : manager(m), id(0), flags(0) - { - if (a) - addr = *a; - } - - void Fill(const unsigned char *input, const unsigned short len) - { - if (len < HEADER_LENGTH) - throw SocketException("Unable to fill packet"); - - unsigned short packet_pos = 0; - - this->id = (input[packet_pos] << 8) | input[packet_pos + 1]; - packet_pos += 2; - - this->flags = (input[packet_pos] << 8) | input[packet_pos + 1]; - packet_pos += 2; - - unsigned short qdcount = (input[packet_pos] << 8) | input[packet_pos + 1]; - packet_pos += 2; - - unsigned short ancount = (input[packet_pos] << 8) | input[packet_pos + 1]; - packet_pos += 2; - - unsigned short nscount = (input[packet_pos] << 8) | input[packet_pos + 1]; - packet_pos += 2; - - unsigned short arcount = (input[packet_pos] << 8) | input[packet_pos + 1]; - packet_pos += 2; - - Log(LOG_DEBUG_2) << "Resolver: qdcount: " << qdcount << " ancount: " << ancount << " nscount: " << nscount << " arcount: " << arcount; - - for (unsigned i = 0; i < qdcount; ++i) - this->questions.push_back(this->UnpackQuestion(input, len, packet_pos)); - - for (unsigned i = 0; i < ancount; ++i) - this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos)); - - for (unsigned i = 0; i < nscount; ++i) - this->authorities.push_back(this->UnpackResourceRecord(input, len, packet_pos)); - - for (unsigned i = 0; i < arcount; ++i) - this->additional.push_back(this->UnpackResourceRecord(input, len, packet_pos)); - } - - unsigned short Pack(unsigned char *output, unsigned short output_size) - { - if (output_size < HEADER_LENGTH) - throw SocketException("Unable to pack packet"); - - unsigned short pos = 0; - - output[pos++] = this->id >> 8; - output[pos++] = this->id & 0xFF; - output[pos++] = this->flags >> 8; - output[pos++] = this->flags & 0xFF; - output[pos++] = this->questions.size() >> 8; - output[pos++] = this->questions.size() & 0xFF; - output[pos++] = this->answers.size() >> 8; - output[pos++] = this->answers.size() & 0xFF; - output[pos++] = this->authorities.size() >> 8; - output[pos++] = this->authorities.size() & 0xFF; - output[pos++] = this->additional.size() >> 8; - output[pos++] = this->additional.size() & 0xFF; - - for (unsigned i = 0; i < this->questions.size(); ++i) - { - Question &q = this->questions[i]; - - if (q.type == QUERY_PTR) - { - sockaddrs ip(q.name); - - if (q.name.find(':') != Anope::string::npos) - { - static const char *const hex = "0123456789abcdef"; - char reverse_ip[128]; - unsigned reverse_ip_count = 0; - for (int j = 15; j >= 0; --j) - { - reverse_ip[reverse_ip_count++] = hex[ip.sa6.sin6_addr.s6_addr[j] & 0xF]; - reverse_ip[reverse_ip_count++] = '.'; - reverse_ip[reverse_ip_count++] = hex[ip.sa6.sin6_addr.s6_addr[j] >> 4]; - reverse_ip[reverse_ip_count++] = '.'; - } - reverse_ip[reverse_ip_count++] = 0; - - q.name = reverse_ip; - q.name += "ip6.arpa"; - } - else - { - unsigned long forward = ip.sa4.sin_addr.s_addr; - in_addr reverse; - reverse.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24; - - ip.ntop(AF_INET, &reverse); - - q.name = ip.addr() + ".in-addr.arpa"; - } - } - - this->PackName(output, output_size, pos, q.name); - - if (pos + 4 >= output_size) - throw SocketException("Unable to pack packet"); - - short s = htons(q.type); - memcpy(&output[pos], &s, 2); - pos += 2; - - s = htons(q.qclass); - memcpy(&output[pos], &s, 2); - pos += 2; - } - - std::vector types[] = { this->answers, this->authorities, this->additional }; - for (int i = 0; i < 3; ++i) - for (unsigned j = 0; j < types[i].size(); ++j) - { - ResourceRecord &rr = types[i][j]; - - this->PackName(output, output_size, pos, rr.name); - - if (pos + 8 >= output_size) - throw SocketException("Unable to pack packet"); - - short s = htons(rr.type); - memcpy(&output[pos], &s, 2); - pos += 2; - - s = htons(rr.qclass); - memcpy(&output[pos], &s, 2); - pos += 2; - - long l = htonl(rr.ttl); - memcpy(&output[pos], &l, 4); - pos += 4; - - switch (rr.type) - { - case QUERY_A: - { - if (pos + 6 > output_size) - throw SocketException("Unable to pack packet"); - - sockaddrs a(rr.rdata); - - s = htons(4); - memcpy(&output[pos], &s, 2); - pos += 2; - - memcpy(&output[pos], &a.sa4.sin_addr, 4); - pos += 4; - break; - } - case QUERY_AAAA: - { - if (pos + 18 > output_size) - throw SocketException("Unable to pack packet"); - - sockaddrs a(rr.rdata); - - s = htons(16); - memcpy(&output[pos], &s, 2); - pos += 2; - - memcpy(&output[pos], &a.sa6.sin6_addr, 16); - pos += 16; - break; - } - case QUERY_NS: - case QUERY_CNAME: - case QUERY_PTR: - { - if (pos + 2 >= output_size) - throw SocketException("Unable to pack packet"); - - unsigned short packet_pos_save = pos; - pos += 2; - - this->PackName(output, output_size, pos, rr.rdata); - - s = htons(pos - packet_pos_save - 2); - memcpy(&output[packet_pos_save], &s, 2); - break; - } - case QUERY_SOA: - { - if (pos + 2 >= output_size) - throw SocketException("Unable to pack packet"); - - unsigned short packet_pos_save = pos; - pos += 2; - - std::vector ns; - spacesepstream(nameservers).GetTokens(ns); - this->PackName(output, output_size, pos, !ns.empty() ? ns[0] : ""); - this->PackName(output, output_size, pos, admin.replace_all_cs('@', '.')); - - if (pos + 20 >= output_size) - throw SocketException("Unable to pack SOA"); - - l = htonl(manager->GetSerial()); - memcpy(&output[pos], &l, 4); - pos += 4; - - l = htonl(refresh); // Refresh - memcpy(&output[pos], &l, 4); - pos += 4; - - l = htonl(refresh); // Retry - memcpy(&output[pos], &l, 4); - pos += 4; - - l = htonl(604800); // Expire - memcpy(&output[pos], &l, 4); - pos += 4; - - l = htonl(0); // Minimum - memcpy(&output[pos], &l, 4); - pos += 4; - - s = htons(pos - packet_pos_save - 2); - memcpy(&output[packet_pos_save], &s, 2); - - break; - } - default: - break; - } - } - - return pos; - } -}; - -namespace DNS -{ - class ReplySocket : public virtual Socket - { - public: - virtual ~ReplySocket() { } - virtual void Reply(MyPacket *p) = 0; - }; -} - -/* Listens for TCP requests */ -class TCPSocket : public ListenSocket -{ - /* A TCP client */ - class Client : public ClientSocket, public Timer, public ReplySocket - { - Manager *manager; - TCPSocket *tcpsock; - MyPacket *packet; - unsigned char packet_buffer[524]; - int length; - - public: - Client(Manager *m, TCPSocket *l, int fd, const sockaddrs &addr) : Socket(fd, l->IsIPv6()), ClientSocket(l, addr), Timer(5), - manager(m), tcpsock(l), packet(NULL), length(0) - { - Log(LOG_DEBUG_2) << "Resolver: New client from " << addr.addr(); - } - - ~Client() - { - Log(LOG_DEBUG_2) << "Resolver: Exiting client from " << clientaddr.addr(); - delete packet; - } - - /* Times out after a few seconds */ - void Tick(time_t) anope_override { } - - void Reply(MyPacket *p) anope_override - { - delete packet; - packet = p; - SocketEngine::Change(this, true, SF_WRITABLE); - } - - bool ProcessRead() anope_override - { - Log(LOG_DEBUG_2) << "Resolver: Reading from DNS TCP socket"; - - int i = recv(this->GetFD(), reinterpret_cast(packet_buffer) + length, sizeof(packet_buffer) - length, 0); - if (i <= 0) - return false; - - length += i; - - unsigned short want_len = packet_buffer[0] << 8 | packet_buffer[1]; - if (length >= want_len + 2) - { - int len = length - 2; - length -= want_len + 2; - return this->manager->HandlePacket(this, packet_buffer + 2, len, NULL); - } - return true; - } - - bool ProcessWrite() anope_override - { - Log(LOG_DEBUG_2) << "Resolver: Writing to DNS TCP socket"; - - if (packet != NULL) - { - try - { - unsigned char buffer[65535]; - unsigned short len = packet->Pack(buffer + 2, sizeof(buffer) - 2); - - short s = htons(len); - memcpy(buffer, &s, 2); - len += 2; - - send(this->GetFD(), reinterpret_cast(buffer), len, 0); - } - catch (const SocketException &) { } - - delete packet; - packet = NULL; - } - - SocketEngine::Change(this, false, SF_WRITABLE); - return true; /* Do not return false here, bind is unhappy we close the connection so soon after sending */ - } - }; - - Manager *manager; - - public: - TCPSocket(Manager *m, const Anope::string &ip, int port) : Socket(-1, ip.find(':') != Anope::string::npos), ListenSocket(ip, port, ip.find(':') != Anope::string::npos), manager(m) { } - - ClientSocket *OnAccept(int fd, const sockaddrs &addr) anope_override - { - return new Client(this->manager, this, fd, addr); - } -}; - -/* Listens for UDP requests */ -class UDPSocket : public ReplySocket -{ - Manager *manager; - std::deque packets; - - public: - UDPSocket(Manager *m, const Anope::string &ip, int port) : Socket(-1, ip.find(':') != Anope::string::npos, SOCK_DGRAM), manager(m) { } - - ~UDPSocket() - { - for (unsigned i = 0; i < packets.size(); ++i) - delete packets[i]; - } - - void Reply(MyPacket *p) anope_override - { - packets.push_back(p); - SocketEngine::Change(this, true, SF_WRITABLE); - } - - std::deque& GetPackets() { return packets; } - - bool ProcessRead() anope_override - { - Log(LOG_DEBUG_2) << "Resolver: Reading from DNS UDP socket"; - - unsigned char packet_buffer[524]; - sockaddrs from_server; - socklen_t x = sizeof(from_server); - int length = recvfrom(this->GetFD(), reinterpret_cast(&packet_buffer), sizeof(packet_buffer), 0, &from_server.sa, &x); - return this->manager->HandlePacket(this, packet_buffer, length, &from_server); - } - - bool ProcessWrite() anope_override - { - Log(LOG_DEBUG_2) << "Resolver: Writing to DNS UDP socket"; - - MyPacket *r = !packets.empty() ? packets.front() : NULL; - if (r != NULL) - { - try - { - unsigned char buffer[524]; - unsigned short len = r->Pack(buffer, sizeof(buffer)); - - sendto(this->GetFD(), reinterpret_cast(buffer), len, 0, &r->addr.sa, r->addr.size()); - } - catch (const SocketException &) { } - - delete r; - packets.pop_front(); - } - - if (packets.empty()) - SocketEngine::Change(this, false, SF_WRITABLE); - - return true; - } -}; - -class MyManager : public Manager, public Timer -{ - uint32_t serial; - - typedef std::multimap cache_map; - cache_map cache; - - TCPSocket *tcpsock; - UDPSocket *udpsock; - - bool listen; - sockaddrs addrs; - public: - std::map requests; - - MyManager(Module *creator) : Manager(creator), Timer(300, Anope::CurTime, true), serial(Anope::CurTime), tcpsock(NULL), udpsock(NULL), listen(false) - { - } - - ~MyManager() - { - delete udpsock; - delete tcpsock; - - for (std::map::iterator it = this->requests.begin(), it_end = this->requests.end(); it != it_end;) - { - Request *request = it->second; - ++it; - - Query rr(*request); - rr.error = ERROR_UNKNOWN; - request->OnError(&rr); - - delete request; - } - this->requests.clear(); - - this->cache.clear(); - } - - void SetIPPort(const Anope::string &nameserver, const Anope::string &ip, unsigned short port) - { - delete udpsock; - delete tcpsock; - - udpsock = NULL; - tcpsock = NULL; - - try - { - this->addrs.pton(nameserver.find(':') != Anope::string::npos ? AF_INET6 : AF_INET, nameserver, 53); - - udpsock = new UDPSocket(this, ip, port); - - if (!ip.empty()) - { - udpsock->Bind(ip, port); - tcpsock = new TCPSocket(this, ip, port); - listen = true; - } - } - catch (const SocketException &ex) - { - Log() << "Unable to bind dns to " << ip << ":" << port << ": " << ex.GetReason(); - } - } - - void Process(Request *req) anope_override - { - Log(LOG_DEBUG_2) << "Resolver: Processing request to lookup " << req->name << ", of type " << req->type; - - if (req->use_cache && this->CheckCache(req)) - { - Log(LOG_DEBUG_2) << "Resolver: Using cached result"; - delete req; - return; - } - - if (!this->udpsock) - throw SocketException("No dns socket"); - - if (this->udpsock->GetPackets().size() == 65535) - throw SocketException("DNS queue full"); - - do - { - static unsigned short cur_id = rand(); - cur_id = req->id = (cur_id + 1) & 0xFFFF; - } - while (!req->id || this->requests.count(req->id)); - - this->requests[req->id] = req; - - req->SetSecs(timeout); - - MyPacket *p = new MyPacket(this, &this->addrs); - p->flags = QUERYFLAGS_RD; - - p->id = req->id; - p->questions.push_back(*req); - this->udpsock->Reply(p); - } - - void RemoveRequest(Request *req) anope_override - { - this->requests.erase(req->id); - } - - bool HandlePacket(ReplySocket *s, const unsigned char *const packet_buffer, int length, sockaddrs *from) anope_override - { - if (length < Packet::HEADER_LENGTH) - return true; - - MyPacket recv_packet(this, from); - - try - { - recv_packet.Fill(packet_buffer, length); - } - catch (const SocketException &ex) - { - Log(LOG_DEBUG_2) << ex.GetReason(); - return true; - } - - if (!(recv_packet.flags & QUERYFLAGS_QR)) - { - if (!listen) - return true; - else if (recv_packet.questions.empty()) - { - Log(LOG_DEBUG_2) << "Resolver: Received a question with no questions?"; - return true; - } - - MyPacket *packet = new MyPacket(recv_packet); - packet->flags |= QUERYFLAGS_QR; /* This is a reponse */ - packet->flags |= QUERYFLAGS_AA; /* And we are authoritative */ - - packet->answers.clear(); - packet->authorities.clear(); - packet->additional.clear(); - - for (unsigned i = 0; i < recv_packet.questions.size(); ++i) - { - const Question& q = recv_packet.questions[i]; - - if (q.type == QUERY_AXFR || q.type == QUERY_SOA) - { - ResourceRecord rr(q.name, QUERY_SOA); - packet->answers.push_back(rr); - - if (q.type == QUERY_AXFR) - { - Anope::string token; - spacesepstream sep(nameservers); - while (sep.GetToken(token)) - { - ResourceRecord rr2(q.name, QUERY_NS); - rr2.rdata = token; - packet->answers.push_back(rr2); - } - } - break; - } - } - - FOREACH_MOD(I_OnDnsRequest, OnDnsRequest(recv_packet, packet)); - - for (unsigned i = 0; i < recv_packet.questions.size(); ++i) - { - const Question& q = recv_packet.questions[i]; - - if (q.type == QUERY_AXFR) - { - ResourceRecord rr(q.name, QUERY_SOA); - packet->answers.push_back(rr); - break; - } - } - - s->Reply(packet); - return true; - } - - if (from == NULL) - { - Log(LOG_DEBUG_2) << "Resolver: Received an answer over TCP. This is not supported."; - return true; - } - else if (this->addrs != *from) - { - Log(LOG_DEBUG_2) << "Resolver: Received an answer from the wrong nameserver, Bad NAT or DNS forging attempt? '" << this->addrs.addr() << "' != '" << from->addr() << "'"; - return true; - } - - std::map::iterator it = this->requests.find(recv_packet.id); - if (it == this->requests.end()) - { - Log(LOG_DEBUG_2) << "Resolver: Received an answer for something we didn't request"; - return true; - } - Request *request = it->second; - - if (recv_packet.flags & QUERYFLAGS_OPCODE) - { - Log(LOG_DEBUG_2) << "Resolver: Received a nonstandard query"; - recv_packet.error = ERROR_NONSTANDARD_QUERY; - request->OnError(&recv_packet); - } - else if (recv_packet.flags & QUERYFLAGS_RCODE) - { - Error error = ERROR_UNKNOWN; - - switch (recv_packet.flags & QUERYFLAGS_RCODE) - { - case 1: - Log(LOG_DEBUG_2) << "Resolver: format error"; - error = ERROR_FORMAT_ERROR; - break; - case 2: - Log(LOG_DEBUG_2) << "Resolver: server error"; - error = ERROR_SERVER_FAILURE; - break; - case 3: - Log(LOG_DEBUG_2) << "Resolver: domain not found"; - error = ERROR_DOMAIN_NOT_FOUND; - break; - case 4: - Log(LOG_DEBUG_2) << "Resolver: not implemented"; - error = ERROR_NOT_IMPLEMENTED; - break; - case 5: - Log(LOG_DEBUG_2) << "Resolver: refused"; - error = ERROR_REFUSED; - break; - default: - break; - } - - recv_packet.error = error; - request->OnError(&recv_packet); - } - else if (recv_packet.answers.empty()) - { - Log(LOG_DEBUG_2) << "Resolver: No resource records returned"; - recv_packet.error = ERROR_NO_RECORDS; - request->OnError(&recv_packet); - } - else - { - Log(LOG_DEBUG_2) << "Resolver: Lookup complete for " << request->name; - request->OnLookupComplete(&recv_packet); - this->AddCache(recv_packet); - } - - delete request; - return true; - } - - void UpdateSerial() anope_override - { - serial = Anope::CurTime; - } - - uint32_t GetSerial() const anope_override - { - return serial; - } - - /** Add a record to the dns cache - * @param r The record - */ - void AddCache(Query &r) - { - for (unsigned i = 0; i < r.answers.size(); ++i) - { - ResourceRecord &rr = r.answers[i]; - Log(LOG_DEBUG_3) << "Resolver cache: added cache for " << rr.name << " -> " << rr.rdata << ", ttl: " << rr.ttl; - this->cache.insert(std::make_pair(rr.name, rr)); - } - } - - void Tick(time_t now) anope_override - { - Log(LOG_DEBUG_2) << "Resolver: Purging DNS cache"; - - for (cache_map::iterator it = this->cache.begin(), it_next; it != this->cache.end(); it = it_next) - { - ResourceRecord &req = it->second; - it_next = it; - ++it_next; - - if (req.created + static_cast(req.ttl) < now) - this->cache.erase(it); - } - } - - private: - /** Check the DNS cache to see if request can be handled by a cached result - * @return true if a cached result was found. - */ - bool CheckCache(Request *request) - { - cache_map::iterator it = this->cache.find(request->name); - if (it != this->cache.end()) - { - Query record(*request); - - for (cache_map::iterator it_end = this->cache.upper_bound(request->name); it != it_end; ++it) - { - ResourceRecord &rec = it->second; - if (rec.created + static_cast(rec.ttl) >= Anope::CurTime) - record.answers.push_back(rec); - } - - if (!record.answers.empty()) - { - Log(LOG_DEBUG_3) << "Resolver: Using cached result for " << request->name; - request->OnLookupComplete(&record); - return true; - } - } - - return false; - } - -}; - -class ModuleDNS : public Module -{ - MyManager manager; - - Anope::string nameserver; - Anope::string ip; - int port; - - public: - ModuleDNS(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), manager(this) - { - - Implementation i[] = { I_OnReload, I_OnModuleUnload }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - } - - void OnReload(ServerConfig *conf, ConfigReader &reader) anope_override - { - - nameserver = reader.ReadValue("dns", "nameserver", "127.0.0.1", 0); - timeout = Anope::DoTime(reader.ReadValue("dns", "timeout", "5", 0)); - ip = reader.ReadValue("dns", "ip", "0.0.0.0", 0); - port = reader.ReadInteger("dns", "port", "53", 0, false); - admin = reader.ReadValue("dns", "admin", "admin@example.com", 0); - nameservers = reader.ReadValue("dns", "nameservers", "ns1.example.com", 0); - refresh = reader.ReadInteger("dns", "refresh", "3600", 0, false); - - if (Anope::IsFile(nameserver)) - { - std::ifstream f(nameserver.c_str()); - bool success = false; - - if (f.is_open()) - { - for (Anope::string server; std::getline(f, server.str());) - { - if (server.find("nameserver") == 0) - { - size_t i = server.find_first_of("123456789"); - if (i != Anope::string::npos) - { - if (server.substr(i).is_pos_number_only()) - { - nameserver = server.substr(i); - Log(LOG_DEBUG) << "Nameserver set to " << nameserver; - success = true; - break; - } - } - } - } - - f.close(); - } - - if (!success) - { - Log() << "Unable to find nameserver, defaulting to 127.0.0.1"; - nameserver = "127.0.0.1"; - } - } - - try - { - this->manager.SetIPPort(nameserver, ip, port); - } - catch (const SocketException &ex) - { - throw ModuleException(ex.GetReason()); - } - } - - void OnModuleUnload(User *u, Module *m) anope_override - { - for (std::map::iterator it = this->manager.requests.begin(), it_end = this->manager.requests.end(); it != it_end;) - { - unsigned short id = it->first; - Request *req = it->second; - ++it; - - if (req->creator == m) - { - Query rr(*req); - rr.error = ERROR_UNLOADED; - req->OnError(&rr); - - delete req; - this->manager.requests.erase(id); - } - } - } -}; - -MODULE_INIT(ModuleDNS) - diff --git a/modules/extra/webcpanel/CMakeLists.txt b/modules/extra/webcpanel/CMakeLists.txt index 4437156ad..247973f61 100644 --- a/modules/extra/webcpanel/CMakeLists.txt +++ b/modules/extra/webcpanel/CMakeLists.txt @@ -1,3 +1,4 @@ +build_subdir(${CMAKE_CURRENT_SOURCE_DIR}) install(DIRECTORY templates DESTINATION "${DB_DIR}/modules/webcpanel" diff --git a/modules/m_dns.cpp b/modules/m_dns.cpp new file mode 100644 index 000000000..e8f3bd758 --- /dev/null +++ b/modules/m_dns.cpp @@ -0,0 +1,1021 @@ +/* + * + * (C) 2003-2013 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + * + */ + +#include "module.h" +#include "dns.h" + +using namespace DNS; + +namespace +{ + Anope::string admin, nameservers; + int refresh; + time_t timeout; +} + +/** A full packet sent or recieved to/from the nameserver + */ +class MyPacket : public Packet +{ + void PackName(unsigned char *output, unsigned short output_size, unsigned short &pos, const Anope::string &name) + { + if (name.length() + 2 > output_size) + throw SocketException("Unable to pack name"); + + Log(LOG_DEBUG_2) << "Resolver: PackName packing " << name; + + sepstream sep(name, '.'); + Anope::string token; + + while (sep.GetToken(token)) + { + output[pos++] = token.length(); + memcpy(&output[pos], token.c_str(), token.length()); + pos += token.length(); + } + + output[pos++] = 0; + } + + Anope::string UnpackName(const unsigned char *input, unsigned short input_size, unsigned short &pos) + { + Anope::string name; + unsigned short pos_ptr = pos, lowest_ptr = input_size; + bool compressed = false; + + if (pos_ptr >= input_size) + throw SocketException("Unable to unpack name - no input"); + + while (input[pos_ptr] > 0) + { + unsigned short offset = input[pos_ptr]; + + if (offset & POINTER) + { + if ((offset & POINTER) != POINTER) + throw SocketException("Unable to unpack name - bogus compression header"); + if (pos_ptr + 1 >= input_size) + throw SocketException("Unable to unpack name - bogus compression header"); + + /* Place pos at the second byte of the first (farthest) compression pointer */ + if (compressed == false) + { + ++pos; + compressed = true; + } + + pos_ptr = (offset & LABEL) << 8 | input[pos_ptr + 1]; + + /* Pointers can only go back */ + if (pos_ptr >= lowest_ptr) + throw SocketException("Unable to unpack name - bogus compression pointer"); + lowest_ptr = pos_ptr; + } + else + { + if (pos_ptr + offset + 1 >= input_size) + throw SocketException("Unable to unpack name - offset too large"); + if (!name.empty()) + name += "."; + for (unsigned i = 1; i <= offset; ++i) + name += input[pos_ptr + i]; + + pos_ptr += offset + 1; + if (compressed == false) + /* Move up pos */ + pos = pos_ptr; + } + } + + /* +1 pos either to one byte after the compression pointer or one byte after the ending \0 */ + ++pos; + + Log(LOG_DEBUG_2) << "Resolver: UnpackName successfully unpacked " << name; + + return name; + } + + Question UnpackQuestion(const unsigned char *input, unsigned short input_size, unsigned short &pos) + { + Question question; + + question.name = this->UnpackName(input, input_size, pos); + + if (pos + 4 > input_size) + throw SocketException("Unable to unpack question"); + + question.type = static_cast(input[pos] << 8 | input[pos + 1]); + pos += 2; + + question.qclass = input[pos] << 8 | input[pos + 1]; + pos += 2; + + return question; + } + + ResourceRecord UnpackResourceRecord(const unsigned char *input, unsigned short input_size, unsigned short &pos) + { + ResourceRecord record = static_cast(this->UnpackQuestion(input, input_size, pos)); + + if (pos + 6 > input_size) + throw SocketException("Unable to unpack resource record"); + + record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3]; + pos += 4; + + //record.rdlength = input[pos] << 8 | input[pos + 1]; + pos += 2; + + switch (record.type) + { + case QUERY_A: + { + if (pos + 4 > input_size) + throw SocketException("Unable to unpack resource record"); + + in_addr a; + a.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16) | (input[pos + 3] << 24); + pos += 4; + + sockaddrs addrs; + addrs.ntop(AF_INET, &a); + + record.rdata = addrs.addr(); + break; + } + case QUERY_AAAA: + { + if (pos + 16 > input_size) + throw SocketException("Unable to unpack resource record"); + + in6_addr a; + for (int j = 0; j < 16; ++j) + a.s6_addr[j] = input[pos + j]; + pos += 16; + + sockaddrs addrs; + addrs.ntop(AF_INET6, &a); + + record.rdata = addrs.addr(); + break; + } + case QUERY_CNAME: + case QUERY_PTR: + { + record.rdata = this->UnpackName(input, input_size, pos); + break; + } + default: + break; + } + + Log(LOG_DEBUG_2) << "Resolver: " << record.name << " -> " << record.rdata; + + return record; + } + + public: + Manager *manager; + /* Source or destination of the packet */ + sockaddrs addr; + /* ID for this packet */ + unsigned short id; + /* Flags on the packet */ + unsigned short flags; + + MyPacket(Manager *m, sockaddrs *a) : manager(m), id(0), flags(0) + { + if (a) + addr = *a; + } + + void Fill(const unsigned char *input, const unsigned short len) + { + if (len < HEADER_LENGTH) + throw SocketException("Unable to fill packet"); + + unsigned short packet_pos = 0; + + this->id = (input[packet_pos] << 8) | input[packet_pos + 1]; + packet_pos += 2; + + this->flags = (input[packet_pos] << 8) | input[packet_pos + 1]; + packet_pos += 2; + + unsigned short qdcount = (input[packet_pos] << 8) | input[packet_pos + 1]; + packet_pos += 2; + + unsigned short ancount = (input[packet_pos] << 8) | input[packet_pos + 1]; + packet_pos += 2; + + unsigned short nscount = (input[packet_pos] << 8) | input[packet_pos + 1]; + packet_pos += 2; + + unsigned short arcount = (input[packet_pos] << 8) | input[packet_pos + 1]; + packet_pos += 2; + + Log(LOG_DEBUG_2) << "Resolver: qdcount: " << qdcount << " ancount: " << ancount << " nscount: " << nscount << " arcount: " << arcount; + + for (unsigned i = 0; i < qdcount; ++i) + this->questions.push_back(this->UnpackQuestion(input, len, packet_pos)); + + for (unsigned i = 0; i < ancount; ++i) + this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos)); + + for (unsigned i = 0; i < nscount; ++i) + this->authorities.push_back(this->UnpackResourceRecord(input, len, packet_pos)); + + for (unsigned i = 0; i < arcount; ++i) + this->additional.push_back(this->UnpackResourceRecord(input, len, packet_pos)); + } + + unsigned short Pack(unsigned char *output, unsigned short output_size) + { + if (output_size < HEADER_LENGTH) + throw SocketException("Unable to pack packet"); + + unsigned short pos = 0; + + output[pos++] = this->id >> 8; + output[pos++] = this->id & 0xFF; + output[pos++] = this->flags >> 8; + output[pos++] = this->flags & 0xFF; + output[pos++] = this->questions.size() >> 8; + output[pos++] = this->questions.size() & 0xFF; + output[pos++] = this->answers.size() >> 8; + output[pos++] = this->answers.size() & 0xFF; + output[pos++] = this->authorities.size() >> 8; + output[pos++] = this->authorities.size() & 0xFF; + output[pos++] = this->additional.size() >> 8; + output[pos++] = this->additional.size() & 0xFF; + + for (unsigned i = 0; i < this->questions.size(); ++i) + { + Question &q = this->questions[i]; + + if (q.type == QUERY_PTR) + { + sockaddrs ip(q.name); + + if (q.name.find(':') != Anope::string::npos) + { + static const char *const hex = "0123456789abcdef"; + char reverse_ip[128]; + unsigned reverse_ip_count = 0; + for (int j = 15; j >= 0; --j) + { + reverse_ip[reverse_ip_count++] = hex[ip.sa6.sin6_addr.s6_addr[j] & 0xF]; + reverse_ip[reverse_ip_count++] = '.'; + reverse_ip[reverse_ip_count++] = hex[ip.sa6.sin6_addr.s6_addr[j] >> 4]; + reverse_ip[reverse_ip_count++] = '.'; + } + reverse_ip[reverse_ip_count++] = 0; + + q.name = reverse_ip; + q.name += "ip6.arpa"; + } + else + { + unsigned long forward = ip.sa4.sin_addr.s_addr; + in_addr reverse; + reverse.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24; + + ip.ntop(AF_INET, &reverse); + + q.name = ip.addr() + ".in-addr.arpa"; + } + } + + this->PackName(output, output_size, pos, q.name); + + if (pos + 4 >= output_size) + throw SocketException("Unable to pack packet"); + + short s = htons(q.type); + memcpy(&output[pos], &s, 2); + pos += 2; + + s = htons(q.qclass); + memcpy(&output[pos], &s, 2); + pos += 2; + } + + std::vector types[] = { this->answers, this->authorities, this->additional }; + for (int i = 0; i < 3; ++i) + for (unsigned j = 0; j < types[i].size(); ++j) + { + ResourceRecord &rr = types[i][j]; + + this->PackName(output, output_size, pos, rr.name); + + if (pos + 8 >= output_size) + throw SocketException("Unable to pack packet"); + + short s = htons(rr.type); + memcpy(&output[pos], &s, 2); + pos += 2; + + s = htons(rr.qclass); + memcpy(&output[pos], &s, 2); + pos += 2; + + long l = htonl(rr.ttl); + memcpy(&output[pos], &l, 4); + pos += 4; + + switch (rr.type) + { + case QUERY_A: + { + if (pos + 6 > output_size) + throw SocketException("Unable to pack packet"); + + sockaddrs a(rr.rdata); + + s = htons(4); + memcpy(&output[pos], &s, 2); + pos += 2; + + memcpy(&output[pos], &a.sa4.sin_addr, 4); + pos += 4; + break; + } + case QUERY_AAAA: + { + if (pos + 18 > output_size) + throw SocketException("Unable to pack packet"); + + sockaddrs a(rr.rdata); + + s = htons(16); + memcpy(&output[pos], &s, 2); + pos += 2; + + memcpy(&output[pos], &a.sa6.sin6_addr, 16); + pos += 16; + break; + } + case QUERY_NS: + case QUERY_CNAME: + case QUERY_PTR: + { + if (pos + 2 >= output_size) + throw SocketException("Unable to pack packet"