├── src ├── CMakeLists.txt ├── qjdns │ ├── qjdns_sock.h │ ├── CMakeLists.txt │ ├── qjdns_p.h │ ├── qjdns_sock.cpp │ ├── qjdnsshared_p.h │ └── qjdns.cpp └── jdns │ ├── CMakeLists.txt │ ├── jdns_p.h │ ├── jdns_packet.h │ ├── jdns_mdnsd.h │ ├── jdns_sys.c │ └── jdns_packet.c ├── JDnsConfig.cmake.in ├── QJDns-qtConfig.cmake.in ├── jdns.pc.in ├── package.sh ├── qjdns.pc.in ├── .gitignore ├── .travis.yml ├── cmake_uninstall.cmake.in ├── QJDnsConfig.cmake.in ├── jdns.pri ├── TODO ├── COPYING ├── tools └── jdns │ ├── CMakeLists.txt │ ├── main.h │ └── main.cpp ├── include └── jdns │ ├── jdns_export.h │ ├── qjdns.h │ ├── jdns.h │ └── qjdnsshared.h ├── README.md └── CMakeLists.txt /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(jdns) 2 | if(BUILD_QJDNS) 3 | add_subdirectory(qjdns) 4 | endif(BUILD_QJDNS) 5 | -------------------------------------------------------------------------------- /JDnsConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | if(NOT TARGET jdns) 4 | include(${CMAKE_CURRENT_LIST_DIR}/JDnsTargets.cmake) 5 | endif() 6 | 7 | set(JDns_LIBRARY jdns) 8 | -------------------------------------------------------------------------------- /QJDns-qtConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | if(NOT JDns_FOUND) 4 | find_package(JDns "@JDNS_LIB_VERSION_STRING@" REQUIRED) 5 | endif(NOT JDns_FOUND) 6 | 7 | if(NOT TARGET qjdns) 8 | include(${CMAKE_CURRENT_LIST_DIR}/QJDns@QT_MAJ@Targets.cmake) 9 | endif() 10 | 11 | set(QJDns_LIBRARY qjdns) 12 | -------------------------------------------------------------------------------- /jdns.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | libdir=@LIB_INSTALL_DIR_FULL@ 4 | includedir=@INCLUDE_INSTALL_DIR_FULL@/jdns 5 | 6 | Name: jdns 7 | Description: Simple DNS implementation can perform normal DNS queries 8 | Version: @JDNS_LIB_MAJOR_VERSION@.@JDNS_LIB_MINOR_VERSION@.@JDNS_LIB_PATCH_VERSION@ 9 | Libs: -L${libdir} -ljdns 10 | Cflags: -I${includedir} 11 | -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | if [ $# -lt 1 ]; then 5 | echo "usage: $0 [version]" 6 | exit 1 7 | fi 8 | 9 | VERSION=$1 10 | 11 | mkdir -p build/jdns-$VERSION 12 | cp -a .gitignore COPYING README.md TODO CMakeLists.txt cmake_uninstall.cmake.in jdns.* qjdns.* JDns* QJDns* include src tools Doxyfile.in build/jdns-$VERSION 13 | cd build 14 | tar jcvf jdns-$VERSION.tar.bz2 jdns-$VERSION 15 | -------------------------------------------------------------------------------- /qjdns.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | libdir=@LIB_INSTALL_DIR_FULL@ 4 | includedir=@INCLUDE_INSTALL_DIR_FULL@/jdns 5 | 6 | Name: qjdns 7 | Description: Qt bindings for JDNS 8 | Version: @JDNS_LIB_MAJOR_VERSION@.@JDNS_LIB_MINOR_VERSION@.@JDNS_LIB_PATCH_VERSION@ 9 | Requires: jdns @QJDns_QT_PC_VERSION@ 10 | Libs: -L${libdir} -lqjdns@QT_MAJ@ 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | 15 | *.moc 16 | moc_*.c* 17 | qrc_*.cpp 18 | ui_*.h 19 | Makefile* 20 | *-build-* 21 | 22 | # QtCreator 23 | 24 | *.autosave 25 | 26 | # cmake files 27 | /CMakeCache.txt 28 | CMakeFiles 29 | /JDnsConfig.cmake 30 | /JDnsConfigVersion.cmake 31 | /QJDnsConfig.cmake 32 | /QJDnsConfigVersion.cmake 33 | /bin 34 | /lib 35 | /jdns.pc 36 | /qjdns.pc 37 | cmake_install.cmake 38 | __ 39 | cmake_uninstall.cmake 40 | 41 | # backup files 42 | *~ 43 | \#*# 44 | ~* 45 | *.orig 46 | 47 | # KDE Dolphin 48 | .directory 49 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | 4 | language: cpp 5 | 6 | script: cmake -DQT4_BUILD=ON -DCMAKE_BUILD_TYPE=Debug . && make 7 | 8 | compiler: 9 | - clang 10 | - gcc 11 | 12 | before_install: 13 | - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y 14 | - sudo add-apt-repository ppa:kalakris/cmake -y 15 | - sudo apt-get update -qq 16 | - sudo apt-get install -qq cmake 17 | - sudo apt-get install -qq libqt4-dev 18 | - if [ "$CXX" = "clang++" ]; then sudo apt-get install -qq libstdc++-4.8-dev; fi 19 | - if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8; fi 20 | - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi 21 | -------------------------------------------------------------------------------- /cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 2 | message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") 3 | endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 4 | 5 | file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 6 | string(REGEX REPLACE "\n" ";" files "${files}") 7 | foreach(file ${files}) 8 | message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") 9 | exec_program( 10 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 11 | OUTPUT_VARIABLE rm_out 12 | RETURN_VALUE rm_retval 13 | ) 14 | if(NOT "${rm_retval}" STREQUAL 0) 15 | message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") 16 | endif(NOT "${rm_retval}" STREQUAL 0) 17 | endforeach(file) 18 | -------------------------------------------------------------------------------- /QJDnsConfig.cmake.in: -------------------------------------------------------------------------------- 1 | if(Qt5Core_FOUND) 2 | set(SUFFIX qt5) 3 | elseif(Qt4_FOUND) 4 | set(SUFFIX qt4) 5 | else() 6 | message(FATAL_ERROR "Qt4/5 is not found. Need to explicity set Qt first.") 7 | endif() 8 | 9 | if(QJDns_FIND_REQUIRED) 10 | list(APPEND ARGS REQUIRED) 11 | endif() 12 | 13 | if(QJDns_FIND_QUIETLY) 14 | list(APPEND ARGS QUIET) 15 | endif() 16 | 17 | list(APPEND ARGS CONFIG) 18 | 19 | get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../qjdns-${SUFFIX}" ABSOLUTE) 20 | list(APPEND ARGS HINTS ${PACKAGE_PREFIX_DIR}) 21 | 22 | list(APPEND ARGS 23 | NO_DEFAULT_PATH 24 | NO_CMAKE_ENVIRONMENT_PATH 25 | NO_CMAKE_PATH 26 | NO_SYSTEM_ENVIRONMENT_PATH 27 | NO_CMAKE_PACKAGE_REGISTRY 28 | NO_CMAKE_BUILDS_PATH 29 | NO_CMAKE_SYSTEM_PATH 30 | NO_CMAKE_SYSTEM_PACKAGE_REGISTRY 31 | NO_CMAKE_FIND_ROOT_PATH 32 | ) 33 | 34 | find_package(QJDns-${SUFFIX} ${ARGS}) 35 | -------------------------------------------------------------------------------- /jdns.pri: -------------------------------------------------------------------------------- 1 | # qmake project include file 2 | 3 | QT *= network 4 | 5 | DEFINES += JDNS_STATIC 6 | INCLUDEPATH += $$PWD/include/jdns 7 | 8 | windows:{ 9 | LIBS += -lWs2_32 -lAdvapi32 10 | } 11 | unix:{ 12 | #QMAKE_CFLAGS += -pedantic 13 | } 14 | 15 | HEADERS += \ 16 | $$PWD/src/jdns/jdns_packet.h \ 17 | $$PWD/src/jdns/jdns_mdnsd.h \ 18 | $$PWD/src/jdns/jdns_p.h \ 19 | $$PWD/include/jdns/jdns.h \ 20 | $$PWD/src/qjdns/qjdns_sock.h \ 21 | $$PWD/src/qjdns/qjdns_p.h \ 22 | $$PWD/src/qjdns/qjdnsshared_p.h \ 23 | $$PWD/include/jdns/qjdns.h \ 24 | $$PWD/include/jdns/qjdnsshared.h \ 25 | $$PWD/include/jdns/jdns_export.h 26 | 27 | SOURCES += \ 28 | $$PWD/src/jdns/jdns_util.c \ 29 | $$PWD/src/jdns/jdns_packet.c \ 30 | $$PWD/src/jdns/jdns_mdnsd.c \ 31 | $$PWD/src/jdns/jdns_sys.c \ 32 | $$PWD/src/jdns/jdns.c \ 33 | $$PWD/src/qjdns/qjdns_sock.cpp \ 34 | $$PWD/src/qjdns/qjdns.cpp \ 35 | $$PWD/src/qjdns/qjdnsshared.cpp 36 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | (nothing) 2 | 3 | but, this stuff couldn't hurt: 4 | fields that need to be an explicit size should use int16_t, etc 5 | support for other DNS record types (SOA, NSPTR) 6 | detect CNAME loops, rather than looping max times in order to fail 7 | don't follow CNAME for SRV (or so I'm told?) 8 | if it is not possible to implement DNSSEC outside of jdns, then add the 9 | minimal number of hooks to jdns so that it becomes possible 10 | use hash tables to speed up the list lookups 11 | unit tests 12 | qjdns debug lines reworking: 13 | init should emit debugLinesReady, and debug should be available 14 | immediately after call (the emit will break SS/DS, put a note about 15 | this in a comment about init()) 16 | anywhere else, debugLines should conform to SS, but be emitted at the 17 | proper time, not deferred. currently, the debug arrives out of 18 | sequence with the other signals, resulting in strange output in the 19 | commandline tool. 20 | consideration for these changes in jdnsshared 21 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2008 Justin Karneges, Jeremie Miller 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /tools/jdns/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(jdns_tool_MOC_HDRS 2 | main.h 3 | ) 4 | 5 | if(NOT Qt5Core_FOUND) 6 | qt4_wrap_cpp(jdns_tool_MOC_SRCS ${jdns_tool_MOC_HDRS}) 7 | endif() 8 | 9 | set(jdns_tool_SRCS 10 | main.cpp 11 | ) 12 | 13 | add_executable(jdns-tool ${jdns_tool_SRCS} ${jdns_tool_MOC_SRCS} ${jdns_tool_MOC_SRCS}) 14 | 15 | target_link_libraries(jdns-tool jdns qjdns) 16 | 17 | set_target_properties(jdns-tool PROPERTIES 18 | OUTPUT_NAME jdns 19 | ) 20 | 21 | install(TARGETS jdns-tool 22 | LIBRARY DESTINATION ${LIB_INSTALL_DIR} 23 | RUNTIME DESTINATION ${BIN_INSTALL_DIR} 24 | ARCHIVE DESTINATION ${LIB_INSTALL_DIR} 25 | # FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR} 26 | ) 27 | 28 | if(MSVC) 29 | get_target_property(LOCATION jdns-tool LOCATION_DEBUG) 30 | string(REGEX REPLACE "\\.[^.]*$" ".pdb" LOCATION "${LOCATION}") 31 | install(FILES ${LOCATION} DESTINATION ${BIN_INSTALL_DIR} CONFIGURATIONS Debug) 32 | 33 | get_target_property(LOCATION jdns-tool LOCATION_RELWITHDEBINFO) 34 | string(REGEX REPLACE "\\.[^.]*$" ".pdb" LOCATION "${LOCATION}") 35 | install(FILES ${LOCATION} DESTINATION ${BIN_INSTALL_DIR} CONFIGURATIONS RelWithDebInfo) 36 | endif(MSVC) 37 | -------------------------------------------------------------------------------- /src/qjdns/qjdns_sock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005,2006 Justin Karneges 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #ifndef QJDNS_SOCK_H 25 | #define QJDNS_SOCK_H 26 | 27 | bool qjdns_sock_setMulticast4(int s, unsigned long int addr, int *errorCode); 28 | bool qjdns_sock_setMulticast6(int s, unsigned char *addr, int *errorCode); 29 | bool qjdns_sock_setTTL4(int s, int ttl); 30 | bool qjdns_sock_setTTL6(int s, int ttl); 31 | bool qjdns_sock_setIPv6Only(int s); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /include/jdns/jdns_export.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Ivan Romanov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | /** 25 | * @file 26 | * 27 | * Preprocessor magic to allow export of library symbols. 28 | * 29 | * This is strictly internal. 30 | * 31 | * @note You should not include this header directly from an application. You should just use 32 | * `#include ` instead. 33 | */ 34 | 35 | #ifndef JDNS_EXPORT_H 36 | #define JDNS_EXPORT_H 37 | 38 | #ifdef _WIN32 39 | # ifndef JDNS_STATIC 40 | # ifdef JDNS_MAKEDLL 41 | # define JDNS_EXPORT __declspec(dllexport) 42 | # else 43 | # define JDNS_EXPORT __declspec(dllimport) 44 | # endif 45 | # endif 46 | #endif 47 | #ifndef JDNS_EXPORT 48 | # define JDNS_EXPORT 49 | #endif 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /tools/jdns/main.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005 Justin Karneges 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #ifndef MAIN_H 25 | #define MAIN_H 26 | 27 | #include "qjdns.h" 28 | #include 29 | #include 30 | 31 | class App : public QObject 32 | { 33 | Q_OBJECT 34 | public: 35 | bool opt_debug, opt_ipv6, opt_quit; 36 | int quit_time; 37 | QString mode, type, name, ipaddr; 38 | QStringList nslist; 39 | QList pubitems; 40 | QJDns jdns; 41 | int req_id; 42 | 43 | App(); 44 | ~App(); 45 | 46 | public slots: 47 | void start(); 48 | 49 | signals: 50 | void quit(); 51 | 52 | private slots: 53 | void jdns_resultsReady(int id, const QJDns::Response &results); 54 | void jdns_published(int id); 55 | void jdns_error(int id, QJDns::Error e); 56 | void jdns_shutdownFinished(); 57 | void jdns_debugLinesReady(); 58 | void doShutdown(); 59 | }; 60 | 61 | #endif // MAIN_H 62 | -------------------------------------------------------------------------------- /src/jdns/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(jdns_SRCS 2 | jdns.c 3 | jdns_mdnsd.c 4 | jdns_packet.c 5 | jdns_sys.c 6 | jdns_util.c 7 | ) 8 | 9 | 10 | set(jdns_PUBLIC_HEADERS 11 | "${JDNS_INCLUDEDIR}/jdns.h" 12 | "${JDNS_INCLUDEDIR}/jdns_export.h" 13 | ) 14 | 15 | set(jdns_HEADERS 16 | jdns_packet.h 17 | jdns_mdnsd.h 18 | jdns_p.h 19 | ) 20 | 21 | add_library(jdns ${jdns_SRCS} ${jdns_HEADERS} ${jdns_PUBLIC_HEADERS}) 22 | 23 | if(WIN32) 24 | target_link_libraries(jdns Ws2_32 Advapi32) 25 | endif(WIN32) 26 | 27 | if(NOT ANDROID) 28 | set_target_properties(jdns PROPERTIES 29 | VERSION ${JDNS_LIB_MAJOR_VERSION}.${JDNS_LIB_MINOR_VERSION}.${JDNS_LIB_PATCH_VERSION} 30 | SOVERSION ${JDNS_LIB_MAJOR_VERSION} 31 | ) 32 | endif() 33 | set_target_properties(jdns PROPERTIES 34 | DEFINE_SYMBOL JDNS_MAKEDLL 35 | PUBLIC_HEADER "${jdns_PUBLIC_HEADERS}" 36 | # FRAMEWORK ${OSX_FRAMEWORK} 37 | ) 38 | 39 | set(INCLUDES_DESTINATION "") 40 | if("${CMAKE_VERSION}" VERSION_LESS "2.8.12") 41 | if(USE_RELATIVE_PATHS) 42 | target_include_directories(jdns INTERFACE "${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_DIR}/jdns") 43 | else() 44 | target_include_directories(jdns INTERFACE "${INCLUDE_INSTALL_DIR}/jdns") 45 | endif() 46 | else() 47 | set(INCLUDES_DESTINATION INCLUDES DESTINATION "${INCLUDE_INSTALL_DIR}/jdns") 48 | endif() 49 | 50 | install(TARGETS jdns EXPORT jdns-export 51 | LIBRARY DESTINATION ${LIB_INSTALL_DIR} 52 | RUNTIME DESTINATION ${BIN_INSTALL_DIR} 53 | ARCHIVE DESTINATION ${LIB_INSTALL_DIR} 54 | # FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR} 55 | PUBLIC_HEADER DESTINATION "${INCLUDE_INSTALL_DIR}/jdns" ${INCLUDES_DESTINATION} 56 | ) 57 | 58 | if(MSVC) 59 | get_target_property(LOCATION jdns LOCATION_DEBUG) 60 | string(REGEX REPLACE "\\.[^.]*$" ".pdb" LOCATION "${LOCATION}") 61 | install(FILES ${LOCATION} DESTINATION ${LIB_INSTALL_DIR} CONFIGURATIONS Debug) 62 | 63 | get_target_property(LOCATION jdns LOCATION_RELWITHDEBINFO) 64 | string(REGEX REPLACE "\\.[^.]*$" ".pdb" LOCATION "${LOCATION}") 65 | install(FILES ${LOCATION} DESTINATION ${LIB_INSTALL_DIR} CONFIGURATIONS RelWithDebInfo) 66 | endif(MSVC) 67 | -------------------------------------------------------------------------------- /src/qjdns/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(qjdns_MOC_HDRS 2 | "${JDNS_INCLUDEDIR}/qjdns.h" 3 | "${JDNS_INCLUDEDIR}/qjdnsshared.h" 4 | qjdns_p.h 5 | qjdnsshared_p.h 6 | ) 7 | 8 | if(NOT Qt5Core_FOUND) 9 | qt4_wrap_cpp(qjdns_MOC_SRCS ${qjdns_MOC_HDRS}) 10 | endif() 11 | 12 | set(qjdns_SRCS 13 | qjdns.cpp 14 | qjdns_sock.cpp 15 | qjdnsshared.cpp 16 | ) 17 | 18 | set(qjdns_PUBLIC_HEADERS 19 | "${JDNS_INCLUDEDIR}/qjdns.h" 20 | "${JDNS_INCLUDEDIR}/qjdnsshared.h" 21 | ) 22 | 23 | set(qjdns_HEADERS 24 | qjdns_sock.h 25 | ) 26 | 27 | add_library(qjdns ${qjdns_SRCS} ${qjdns_MOC_SRCS} ${qjdns_MOC_HDRS} ${qjdns_PUBLIC_HEADERS}) 28 | 29 | if(QT_MAJ) 30 | set_target_properties(qjdns PROPERTIES OUTPUT_NAME "qjdns${QT_MAJ}") 31 | endif() 32 | 33 | if(Qt5Core_FOUND) 34 | target_link_libraries(qjdns ${Qt5Core_LIBRARIES} ${Qt5Network_LIBRARIES}) 35 | else(Qt5Core_FOUND) 36 | target_link_libraries(qjdns ${QT_LIBRARIES}) 37 | endif(Qt5Core_FOUND) 38 | target_link_libraries(qjdns jdns) 39 | 40 | if(NOT ANDROID) 41 | set_target_properties(qjdns PROPERTIES 42 | VERSION ${JDNS_LIB_MAJOR_VERSION}.${JDNS_LIB_MINOR_VERSION}.${JDNS_LIB_PATCH_VERSION} 43 | SOVERSION ${JDNS_LIB_MAJOR_VERSION} 44 | ) 45 | endif() 46 | set_target_properties(qjdns PROPERTIES 47 | DEFINE_SYMBOL JDNS_MAKEDLL 48 | PUBLIC_HEADER "${qjdns_PUBLIC_HEADERS}" 49 | # FRAMEWORK ${OSX_FRAMEWORK} 50 | ) 51 | 52 | set(INCLUDES_DESTINATION "") 53 | if("${CMAKE_VERSION}" VERSION_LESS "2.8.12") 54 | if(USE_RELATIVE_PATHS) 55 | target_include_directories(qjdns INTERFACE "${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_DIR}/jdns") 56 | else() 57 | target_include_directories(qjdns INTERFACE "${INCLUDE_INSTALL_DIR}/jdns") 58 | endif() 59 | else() 60 | set(INCLUDES_DESTINATION INCLUDES DESTINATION "${INCLUDE_INSTALL_DIR}/jdns") 61 | endif() 62 | 63 | install(TARGETS qjdns EXPORT qjdns-export 64 | LIBRARY DESTINATION ${LIB_INSTALL_DIR} 65 | RUNTIME DESTINATION ${BIN_INSTALL_DIR} 66 | ARCHIVE DESTINATION ${LIB_INSTALL_DIR} 67 | # FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR} 68 | PUBLIC_HEADER DESTINATION "${INCLUDE_INSTALL_DIR}/jdns" ${INCLUDES_DESTINATION} 69 | ) 70 | 71 | if(MSVC) 72 | get_target_property(LOCATION qjdns LOCATION_DEBUG) 73 | string(REGEX REPLACE "\\.[^.]*$" ".pdb" LOCATION "${LOCATION}") 74 | install(FILES ${LOCATION} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin CONFIGURATIONS Debug) 75 | 76 | get_target_property(LOCATION qjdns LOCATION_RELWITHDEBINFO) 77 | string(REGEX REPLACE "\\.[^.]*$" ".pdb" LOCATION "${LOCATION}") 78 | install(FILES ${LOCATION} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin CONFIGURATIONS RelWithDebInfo) 79 | endif(MSVC) 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### JDNS 2 | 3 | Date: October 1st, 2005 4 | Author: Justin Karneges 5 | 6 | JDNS is a simple DNS implementation that can perform normal DNS queries 7 | of any record type (notably SRV), as well as Multicast DNS queries and 8 | advertising. Multicast support is based on Jeremie Miller's "mdnsd" 9 | implementation. 10 | 11 | For maximum flexibility, JDNS is written in C with no direct dependencies, 12 | and is licensed under the MIT license. Your application must supply 13 | functionality to JDNS, such as UDP sending/receiving, via callbacks. 14 | 15 | For Qt users there is a wrapper available called QJDns. jdns.pri can 16 | be used to include everything into a qmake project. CMake-project will build 17 | the sample Qt-based commandline tool 'jdns'. 18 | 19 | #### Features: 20 | * DNS client "stub" resolver 21 | * Can fetch any record type, but provides handy decoding for many 22 | known types: A, AAAA, SRV, MX, TXT, etc. 23 | * Performs retries, caching/expiration, and CNAME following 24 | * Algorithm logic adapted from Q3Dns 25 | * Multicast queries 26 | * Multicast advertising 27 | 28 | #### Why? 29 | * Trolltech is phasing out the Qt DNS implementation, which in Qt 4 has 30 | been relegated to the Qt3Support module. A replacement was desired. 31 | 32 | * While there are many DNS libraries available, at the time of this 33 | writing it was (and still may be) hard to find one that satisfies 34 | three essential conditions: cross-platform friendliness (and this 35 | includes Windows 9x!), the ability to integrate into existing 36 | eventloops, sensible licensing (ie, not GPL). 37 | 38 | #### How to use: 39 | * Prepare callbacks and call jdns_session_new() 40 | * Call jdns_init_unicast() or jdns_init_multicast(), depending on 41 | if you want regular or multicast DNS. If you want both kinds, you 42 | can always make two sessions. 43 | * Make queries and have fun 44 | * Call jdns_step() at the right times to advance JDNS processing 45 | 46 | #### What is left to you: 47 | * The callback functions, obviously. 48 | * Querying for several "qualified" names. Here is what Q3Dns does: 49 | Query for name as provided 50 | Query for name + '.domain' (for every domain the computer is in) 51 | * Detecting for '.local' in a name to be queried, and using that 52 | to decide whether to query via Multicast or normal DNS. 53 | * Recognition of IP addresses. If you want an IP address to resolve 54 | to itself, then do that yourself. Passing an IP address as a DNS 55 | name to JDNS won't work (especially since it wouldn't make any 56 | sense in some contexts, like SRV). 57 | * Recognition of known hosts. If you want this, compare inputs against 58 | jdns_system_dnsparams(). 59 | * For zeroconf/Bonjour, keep in mind that JDNS only provides Multicast 60 | DNS capability. DNS-SD and any higher layers would be your job. 61 | 62 | Using a custom DNS implementation has the drawback that it is difficult 63 | to take advantage of platform-specific features (for example, an OS-wide 64 | DNS cache or LDAP integration). 65 | 66 | An application strategy for normal DNS should probably be: 67 | * If an A or AAAA record is desired, use a native lookup. 68 | * Else, if the platform has advanced DNS features already (ie, 69 | res_query), use those. 70 | * Else, use JDNS. 71 | 72 | However, it may not be a bad idea at first to use JDNS for all occasions, 73 | so that it can be debugged. 74 | 75 | For Multicast DNS, awareness of the platform is doubly important. There 76 | should only be one Multicast DNS "Responder" per computer, and using JDNS 77 | at the same time could result in a conflict. 78 | 79 | An application strategy for Multicast DNS should be: 80 | * If the platform has a Multicast DNS daemon installed already, use 81 | it somehow. 82 | * Else, use JDNS. 83 | 84 | Have fun! 85 | 86 | -------------------------------------------------------------------------------- /src/jdns/jdns_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2008 Justin Karneges 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #ifndef JDNS_P_H 25 | #define JDNS_P_H 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) 34 | # define JDNS_OS_WIN 35 | #else 36 | # define JDNS_OS_UNIX 37 | #endif 38 | 39 | #if defined(__FreeBSD__) || defined(__DragonFly__) 40 | # define JDNS_OS_FREEBSD 41 | #elif defined(__NetBSD__) 42 | # define JDNS_OS_NETBSD 43 | #elif defined(__OpenBSD__) 44 | # define JDNS_OS_OPENBSD 45 | #elif defined(sun) || defined(__sun) 46 | # define JDNS_OS_SOLARIS 47 | #elif defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__)) 48 | # define JDNS_OS_MAC 49 | #endif 50 | 51 | #ifdef JDNS_OS_WIN 52 | # include 53 | #endif 54 | 55 | #ifdef JDNS_OS_UNIX 56 | # include 57 | # include 58 | #endif 59 | 60 | #include "jdns.h" 61 | #include "jdns_packet.h" 62 | 63 | // jdns_util.c 64 | void *jdns_alloc(int size); 65 | void *jdns_realloc(void *p, int size); 66 | void jdns_free(void *p); 67 | char *jdns_strdup(const char *s); 68 | unsigned char *jdns_copy_array(const unsigned char *src, int size); 69 | int jdns_domain_cmp(const unsigned char *a, const unsigned char *b); 70 | 71 | int jdns_sprintf_s(char *str, int n, const char *format, ...); 72 | int jdns_vsprintf_s(char *str, int n, const char *format, va_list ap); 73 | FILE *jdns_fopen(const char *path, const char *mode); 74 | jdns_string_t *jdns_getenv(const char *name); 75 | char *jdns_strcpy(char *dst, const char *src); 76 | 77 | int jdns_string_indexOf(const jdns_string_t *s, unsigned char c, int pos); 78 | jdns_stringlist_t *jdns_string_split(const jdns_string_t *s, unsigned char sep); 79 | 80 | jdns_dnshost_t *jdns_dnshost_new(); 81 | jdns_dnshost_t *jdns_dnshost_copy(const jdns_dnshost_t *a); 82 | void jdns_dnshost_delete(jdns_dnshost_t *a); 83 | jdns_dnshostlist_t *jdns_dnshostlist_new(); 84 | jdns_dnshostlist_t *jdns_dnshostlist_copy(const jdns_dnshostlist_t *a); 85 | void jdns_dnshostlist_delete(jdns_dnshostlist_t *a); 86 | void jdns_dnshostlist_append(jdns_dnshostlist_t *a, const jdns_dnshost_t *host); 87 | 88 | jdns_rr_t *jdns_rr_from_resource(const jdns_packet_resource_t *pr, const jdns_packet_t *ref); 89 | void jdns_response_remove_extra(jdns_response_t *r); 90 | void jdns_response_remove_answer(jdns_response_t *r, int pos); 91 | 92 | #define alloc_type(type) (type *)jdns_alloc(sizeof(type)) 93 | #define _ustrdup(str) (unsigned char *)jdns_strdup((const char *)str) 94 | #define _ustrlen(str) strlen((const char *)str) 95 | #define _ustrcmp(a, b) strcmp((const char *)a, (const char *)b) 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /src/qjdns/qjdns_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2008 Justin Karneges 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #ifndef QJDNS_P_H 25 | #define QJDNS_P_H 26 | 27 | #include "jdns.h" 28 | #include "qjdns.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | class QUdpSocket; 35 | class QTimer; 36 | 37 | class SafeTimer : public QObject 38 | { 39 | Q_OBJECT 40 | public: 41 | SafeTimer(QObject *parent = 0); 42 | ~SafeTimer(); 43 | 44 | int interval() const; 45 | bool isActive() const; 46 | bool isSingleShot() const; 47 | void setInterval(int msec); 48 | void setSingleShot(bool singleShot); 49 | int timerId() const; 50 | 51 | public slots: 52 | void start(int msec); 53 | void start(); 54 | void stop(); 55 | 56 | signals: 57 | void timeout(); 58 | 59 | private: 60 | QTimer *t; 61 | }; 62 | 63 | class QJDns::Private : public QObject 64 | { 65 | Q_OBJECT 66 | public: 67 | class LateError 68 | { 69 | public: 70 | int source_type; // 0 for query, 1 for publish 71 | int id; 72 | Error error; 73 | }; 74 | 75 | class LateResponse 76 | { 77 | public: 78 | int id; 79 | QJDns::Response response; 80 | bool do_cancel; 81 | }; 82 | 83 | QJDns *q; 84 | QJDns::Mode mode; 85 | jdns_session_t *sess; 86 | bool shutting_down; 87 | SafeTimer stepTrigger, debugTrigger; 88 | SafeTimer stepTimeout; 89 | QElapsedTimer clock; 90 | QStringList debug_strings; 91 | bool new_debug_strings; 92 | int next_handle; 93 | bool need_handle; 94 | QHash socketForHandle; 95 | QHash handleForSocket; 96 | int pending; 97 | bool pending_wait; 98 | bool complete_shutdown; 99 | 100 | // pointers that will point to things we are currently signalling 101 | // about. when a query or publish is cancelled, we can use these 102 | // pointers to extract anything we shouldn't signal. 103 | QList *pErrors; 104 | QList *pPublished; 105 | QList *pResponses; 106 | 107 | Private(QJDns *_q); 108 | ~Private(); 109 | void cleanup(); 110 | bool init(QJDns::Mode _mode, const QHostAddress &address); 111 | void setNameServers(const QList &nslist); 112 | void process(); 113 | void processDebug(); 114 | void doNextStep(); 115 | void removeCancelled(int id); 116 | 117 | private slots: 118 | void udp_readyRead(); 119 | void udp_bytesWritten(qint64); 120 | void st_timeout(); 121 | void doNextStepSlot(); 122 | void doDebug(); 123 | 124 | private: 125 | static int cb_time_now(jdns_session_t *, void *app); 126 | static int cb_rand_int(jdns_session_t *, void *); 127 | static void cb_debug_line(jdns_session_t *, void *app, const char *str); 128 | static int cb_udp_bind(jdns_session_t *, void *app, const jdns_address_t *addr, int port, const jdns_address_t *maddr); 129 | static void cb_udp_unbind(jdns_session_t *, void *app, int handle); 130 | static int cb_udp_read(jdns_session_t *, void *app, int handle, jdns_address_t *addr, int *port, unsigned char *buf, int *bufsize); 131 | static int cb_udp_write(jdns_session_t *, void *app, int handle, const jdns_address_t *addr, int port, unsigned char *buf, int bufsize); 132 | }; 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /include/jdns/qjdns.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005,2006 Justin Karneges 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | /** 25 | * @file 26 | * 27 | * The Qt wrapper to jdns. 28 | */ 29 | 30 | #ifndef QJDNS_H 31 | #define QJDNS_H 32 | 33 | #include "jdns_export.h" 34 | #include 35 | #include 36 | 37 | class JDNS_EXPORT QJDns : public QObject 38 | { 39 | Q_OBJECT 40 | public: 41 | enum Mode 42 | { 43 | Unicast, 44 | Multicast 45 | }; 46 | 47 | enum PublishMode 48 | { 49 | Unique, 50 | Shared 51 | }; 52 | 53 | enum Type 54 | { 55 | A = 1, 56 | Aaaa = 28, 57 | Mx = 15, 58 | Srv = 33, 59 | Cname = 5, 60 | Ptr = 12, 61 | Txt = 16, 62 | Hinfo = 13, 63 | Ns = 2, 64 | Any = 255 65 | }; 66 | 67 | enum Error 68 | { 69 | ErrorGeneric, 70 | ErrorNXDomain, ///< query only 71 | ErrorTimeout, ///< query only 72 | ErrorConflict ///< publish only 73 | }; 74 | 75 | class JDNS_EXPORT NameServer 76 | { 77 | public: 78 | QHostAddress address; 79 | int port; 80 | 81 | NameServer(); 82 | }; 83 | 84 | class JDNS_EXPORT DnsHost 85 | { 86 | public: 87 | QByteArray name; 88 | QHostAddress address; 89 | }; 90 | 91 | class JDNS_EXPORT SystemInfo 92 | { 93 | public: 94 | QList nameServers; 95 | QList domains; 96 | QList hosts; 97 | }; 98 | 99 | class JDNS_EXPORT Record 100 | { 101 | public: 102 | QByteArray owner; 103 | int ttl; 104 | int type; 105 | QByteArray rdata; 106 | bool haveKnown; 107 | 108 | // known 109 | QHostAddress address; ///< for A, Aaaa 110 | QByteArray name; ///< for Mx, Srv, Cname, Ptr, Ns 111 | int priority; ///< for Mx, Srv 112 | int weight; ///< for Srv 113 | int port; ///< for Srv 114 | QList texts; ///< for Txt 115 | QByteArray cpu; ///< for Hinfo 116 | QByteArray os; ///< for Hinfo 117 | 118 | Record(); 119 | bool verify() const; 120 | }; 121 | 122 | class JDNS_EXPORT Response 123 | { 124 | public: 125 | QList answerRecords; 126 | QList authorityRecords; 127 | QList additionalRecords; 128 | }; 129 | 130 | QJDns(QObject *parent = 0); 131 | ~QJDns(); 132 | 133 | bool init(Mode mode, const QHostAddress &address); 134 | void shutdown(); 135 | QStringList debugLines(); 136 | 137 | static SystemInfo systemInfo(); 138 | static QHostAddress detectPrimaryMulticast(const QHostAddress &address); 139 | 140 | void setNameServers(const QList &list); 141 | 142 | int queryStart(const QByteArray &name, int type); 143 | void queryCancel(int id); 144 | 145 | // for multicast mode only 146 | int publishStart(PublishMode m, const Record &record); 147 | void publishUpdate(int id, const Record &record); 148 | void publishCancel(int id); 149 | 150 | signals: 151 | void resultsReady(int id, const QJDns::Response &results); 152 | void published(int id); 153 | void error(int id, QJDns::Error e); 154 | void shutdownFinished(); 155 | void debugLinesReady(); 156 | 157 | private: 158 | class Private; 159 | friend class Private; 160 | Private *d; 161 | }; 162 | 163 | #endif 164 | -------------------------------------------------------------------------------- /src/jdns/jdns_packet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 Justin Karneges 3 | * Copyright (C) 2020 Ivan Romanov 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a 6 | * copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included 14 | * in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef JDNS_PACKET_H 26 | #define JDNS_PACKET_H 27 | 28 | #include "jdns.h" 29 | 30 | // -- howto -- 31 | // 32 | // writing packets: 33 | // 1) call jdns_packet_new() 34 | // 2) populate the jdns_packet_t structure, using the functions 35 | // as necessary 36 | // 3) call jdns_packet_export() to populate the raw data of the packet 37 | // 38 | // reading packets: 39 | // 1) call jdns_packet_new() 40 | // 2) call jdns_packet_import() with the raw data 41 | // 3) the jdns_packet_t structure is now populated 42 | // 43 | // IMPORTANT: all names must be valid. that is, ending in a dot character 44 | 45 | int jdns_packet_name_isvalid(const unsigned char *name, int size); // 0 if not valid 46 | 47 | typedef struct jdns_packet_question 48 | { 49 | JDNS_OBJECT 50 | jdns_string_t *qname; 51 | unsigned short int qtype, qclass; 52 | } jdns_packet_question_t; 53 | 54 | jdns_packet_question_t *jdns_packet_question_new(); 55 | jdns_packet_question_t *jdns_packet_question_copy(const jdns_packet_question_t *a); 56 | void jdns_packet_question_delete(jdns_packet_question_t *a); 57 | 58 | typedef struct jdns_packet_write jdns_packet_write_t; 59 | typedef struct jdns_packet jdns_packet_t; 60 | 61 | typedef struct jdns_packet_resource 62 | { 63 | JDNS_OBJECT 64 | jdns_string_t *qname; 65 | unsigned short int qtype, qclass; 66 | unsigned long int ttl; // 31-bit number, top bit always 0 67 | unsigned short int rdlength; 68 | unsigned char *rdata; 69 | 70 | // private 71 | jdns_list_t *writelog; // jdns_packet_write_t 72 | } jdns_packet_resource_t; 73 | 74 | jdns_packet_resource_t *jdns_packet_resource_new(); 75 | jdns_packet_resource_t *jdns_packet_resource_copy(const jdns_packet_resource_t *a); 76 | void jdns_packet_resource_delete(jdns_packet_resource_t *a); 77 | void jdns_packet_resource_add_bytes(jdns_packet_resource_t *a, const unsigned char *data, int size); 78 | void jdns_packet_resource_add_name(jdns_packet_resource_t *a, const jdns_string_t *name); 79 | int jdns_packet_resource_read_name(const jdns_packet_resource_t *a, const jdns_packet_t *p, int *at, jdns_string_t **name); 80 | 81 | struct jdns_packet 82 | { 83 | JDNS_OBJECT 84 | unsigned short int id; 85 | struct 86 | { 87 | unsigned short qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode; 88 | } opts; 89 | 90 | // item counts as specified by the packet. do not use these 91 | // for iteration over the item lists, since they can be wrong 92 | // if the packet is truncated. 93 | int qdcount, ancount, nscount, arcount; 94 | 95 | // value lists 96 | jdns_list_t *questions; // jdns_packet_question_t 97 | jdns_list_t *answerRecords; // jdns_packet_resource_t 98 | jdns_list_t *authorityRecords; // jdns_packet_resource_t 99 | jdns_list_t *additionalRecords; // jdns_packet_resource_t 100 | 101 | // since dns packets are allowed to be truncated, it is possible 102 | // for a packet to not get fully parsed yet still be considered 103 | // successfully parsed. this flag means the packet was fully 104 | // parsed also. 105 | int fully_parsed; 106 | 107 | int raw_size; 108 | unsigned char *raw_data; 109 | }; 110 | 111 | jdns_packet_t *jdns_packet_new(); 112 | jdns_packet_t *jdns_packet_copy(const jdns_packet_t *a); 113 | void jdns_packet_delete(jdns_packet_t *a); 114 | int jdns_packet_import(jdns_packet_t **a, const unsigned char *data, int size); // 0 on fail 115 | int jdns_packet_export(jdns_packet_t *a, int maxsize); // 0 on fail 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /src/jdns/jdns_mdnsd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005 Jeremie Miller 3 | * Copyright (C) 2005,2006 Justin Karneges 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a 6 | * copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included 14 | * in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef JDNS_MDNSD_H 26 | #define JDNS_MDNSD_H 27 | 28 | #include "jdns_p.h" 29 | 30 | struct mytimeval 31 | { 32 | unsigned long int tv_sec; /* seconds */ 33 | unsigned long int tv_usec; /* microseconds */ 34 | }; 35 | 36 | typedef struct mdnsd_struct *mdnsd; // main daemon data 37 | typedef struct mdnsdr_struct *mdnsdr; // record entry 38 | // answer data 39 | typedef struct mdnsda_struct 40 | { 41 | unsigned char *name; 42 | unsigned short int type; 43 | unsigned long int ttl; 44 | unsigned long int real_ttl; 45 | unsigned short int rdlen; 46 | unsigned char *rdata; 47 | unsigned long int ip; // A 48 | unsigned char *rdname; // NS/CNAME/PTR/SRV 49 | struct { unsigned short int priority, weight, port; } srv; // SRV 50 | } *mdnsda; 51 | 52 | /////////// 53 | // Global functions 54 | // 55 | // create a new mdns daemon for the given class of names (usually 1) and maximum frame size 56 | mdnsd mdnsd_new(int qclass, int frame, int port, int (*time_now)(mdnsd d, void *arg), int (*rand_int)(mdnsd d, void *arg), void *arg); 57 | // 58 | // gracefully shutdown the daemon, use mdnsd_out() to get the last packets 59 | void mdnsd_shutdown(mdnsd d); 60 | // 61 | // flush all cached records (network/interface changed) 62 | void mdnsd_flush(mdnsd d); 63 | // 64 | // free given mdnsd (should have used mdnsd_shutdown() first!) 65 | void mdnsd_free(mdnsd d); 66 | // 67 | /////////// 68 | 69 | /////////// 70 | // I/O functions 71 | // 72 | // incoming message from host (to be cached/processed) 73 | void mdnsd_in(mdnsd d, const jdns_packet_t *m, const jdns_response_t *resp, const jdns_address_t *addr, unsigned short int port); 74 | // 75 | // outgoing messge to be delivered to host, returns >0 if one was returned and m/ip/port set 76 | int mdnsd_out(mdnsd d, jdns_packet_t **m, jdns_address_t **addr, unsigned short int *port); 77 | // 78 | // returns the max wait-time until mdnsd_out() needs to be called again 79 | struct mytimeval *mdnsd_sleep(mdnsd d); 80 | // 81 | //////////// 82 | 83 | /////////// 84 | // Q/A functions 85 | // 86 | // register a new query 87 | // answer(record, arg) is called whenever one is found/changes/expires (immediate or anytime after, mdnsda valid until ->ttl==0) 88 | // either answer returns -1, or another mdnsd_query with a NULL answer will remove/unregister this query 89 | void mdnsd_query(mdnsd d, char *host, int type, int (*answer)(mdnsda a, void *arg), void *arg); 90 | // 91 | // returns the first (if last == NULL) or next answer after last from the cache 92 | // mdnsda only valid until an I/O function is called 93 | mdnsda mdnsd_list(mdnsd d, char *host, int type, mdnsda last); 94 | // 95 | /////////// 96 | 97 | /////////// 98 | // Publishing functions 99 | // 100 | // create a new unique record (try mdnsda_list first to make sure it's not used) 101 | // conflict(arg) called at any point when one is detected and unable to recover 102 | // after the first data is set_*(), any future changes effectively expire the old one and attempt to create a new unique record 103 | mdnsdr mdnsd_unique(mdnsd d, char *host, int type, long int ttl, void (*pubresult)(int result, char *host, int type, void *arg), void *arg); 104 | // 105 | // create a new shared record 106 | mdnsdr mdnsd_shared(mdnsd d, char *host, int type, long int ttl); 107 | // 108 | // de-list the given record 109 | void mdnsd_done(mdnsd d, mdnsdr r); 110 | // 111 | // these all set/update the data for the given record, nothing is published until they are called 112 | void mdnsd_set_raw(mdnsd d, mdnsdr r, char *data, int len); 113 | void mdnsd_set_host(mdnsd d, mdnsdr r, char *name); 114 | void mdnsd_set_ip(mdnsd d, mdnsdr r, unsigned long int ip); 115 | void mdnsd_set_srv(mdnsd d, mdnsdr r, int priority, int weight, int port, char *name); 116 | // 117 | /////////// 118 | 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /src/qjdns/qjdns_sock.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005,2006 Justin Karneges 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include "qjdns_sock.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #ifdef Q_OS_WIN 32 | # include 33 | # include 34 | #endif 35 | 36 | #ifdef Q_OS_UNIX 37 | # include 38 | # include 39 | # include 40 | # include 41 | # include 42 | # include 43 | # include 44 | # include 45 | #endif 46 | 47 | #ifndef QT_NO_IPV6 48 | # define HAVE_IPV6 49 | # ifndef s6_addr 50 | # define IPPROTO_IPV6 41 51 | struct in6_addr 52 | { 53 | union 54 | { 55 | unsigned char _S6_u8[16]; 56 | unsigned short _S6_u16[8]; 57 | unsigned long _S6_u32[4]; 58 | } _S6_un; 59 | }; 60 | # define s6_addr _S6_un._S6_u8 61 | # endif 62 | # ifndef IPV6_JOIN_GROUP 63 | # define IPV6_JOIN_GROUP 12 64 | # define IPV6_MULTICAST_HOPS 10 65 | struct ipv6_mreq 66 | { 67 | struct in6_addr ipv6mr_multiaddr; 68 | unsigned int ipv6mr_interface; 69 | }; 70 | # endif 71 | #endif 72 | 73 | static int get_last_error() 74 | { 75 | int x; 76 | #ifdef Q_OS_WIN 77 | x = WSAGetLastError(); 78 | #else 79 | x = errno; 80 | #endif 81 | return x; 82 | } 83 | 84 | bool qjdns_sock_setMulticast4(int s, unsigned long int addr, int *errorCode) 85 | { 86 | int ret; 87 | struct ip_mreq mc; 88 | 89 | memset(&mc, 0, sizeof(mc)); 90 | mc.imr_multiaddr.s_addr = htonl(addr); 91 | mc.imr_interface.s_addr = INADDR_ANY; 92 | 93 | ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&mc, sizeof(mc)); 94 | if(ret != 0) 95 | { 96 | if(errorCode) 97 | *errorCode = get_last_error(); 98 | return false; 99 | } 100 | return true; 101 | } 102 | 103 | bool qjdns_sock_setMulticast6(int s, unsigned char *addr, int *errorCode) 104 | { 105 | #ifdef HAVE_IPV6 106 | int ret; 107 | struct ipv6_mreq mc; 108 | 109 | memset(&mc, 0, sizeof(mc)); 110 | memcpy(mc.ipv6mr_multiaddr.s6_addr, addr, 16); 111 | mc.ipv6mr_interface = 0; 112 | 113 | ret = setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, (const char *)&mc, sizeof(mc)); 114 | if(ret != 0) 115 | { 116 | if(errorCode) 117 | *errorCode = get_last_error(); 118 | return false; 119 | } 120 | return true; 121 | #else 122 | Q_UNUSED(s); 123 | Q_UNUSED(addr); 124 | Q_UNUSED(errorCode); 125 | return false; 126 | #endif 127 | } 128 | 129 | bool qjdns_sock_setTTL4(int s, int ttl) 130 | { 131 | unsigned char cttl; 132 | int ret, ittl; 133 | 134 | cttl = ttl; 135 | ittl = ttl; 136 | 137 | // IP_MULTICAST_TTL might take 1 byte or 4, try both 138 | ret = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&cttl, sizeof(cttl)); 139 | if(ret != 0) 140 | { 141 | ret = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&ittl, sizeof(ittl)); 142 | if(ret != 0) 143 | return false; 144 | } 145 | return true; 146 | } 147 | 148 | bool qjdns_sock_setTTL6(int s, int ttl) 149 | { 150 | #ifdef HAVE_IPV6 151 | unsigned char cttl; 152 | int ret, ittl; 153 | 154 | cttl = ttl; 155 | ittl = ttl; 156 | 157 | // IPV6_MULTICAST_HOPS might take 1 byte or 4, try both 158 | ret = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&cttl, sizeof(cttl)); 159 | if(ret != 0) 160 | { 161 | ret = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&ittl, sizeof(ittl)); 162 | if(ret != 0) 163 | return false; 164 | } 165 | return true; 166 | #else 167 | Q_UNUSED(s); 168 | Q_UNUSED(ttl); 169 | return false; 170 | #endif 171 | } 172 | 173 | bool qjdns_sock_setIPv6Only(int s) 174 | { 175 | #if defined(HAVE_IPV6) && defined(IPV6_V6ONLY) 176 | int x = 1; 177 | if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&x, sizeof(x)) != 0) 178 | return false; 179 | return true; 180 | #else 181 | Q_UNUSED(s); 182 | return false; 183 | #endif 184 | } 185 | -------------------------------------------------------------------------------- /src/qjdns/qjdnsshared_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006-2008 Justin Karneges 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #ifndef QJDNSSHARED_P_H 25 | #define QJDNSSHARED_P_H 26 | 27 | // SafeTimer 28 | #include "qjdns_p.h" 29 | #include "qjdnsshared.h" 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | class JDnsShutdownAgent : public QObject 39 | { 40 | Q_OBJECT 41 | public: 42 | void start(); 43 | 44 | signals: 45 | void started(); 46 | }; 47 | 48 | class JDnsShutdownWorker : public QObject 49 | { 50 | Q_OBJECT 51 | public: 52 | QList list; 53 | 54 | JDnsShutdownWorker(const QList &_list); 55 | 56 | signals: 57 | void finished(); 58 | 59 | private slots: 60 | void jdns_shutdownFinished(); 61 | }; 62 | 63 | class JDnsShutdown : public QThread 64 | { 65 | Q_OBJECT 66 | public: 67 | QMutex m; 68 | QWaitCondition w; 69 | QList list; 70 | JDnsShutdownAgent *agent; 71 | JDnsShutdownWorker *worker; 72 | int phase; 73 | 74 | void waitForShutdown(const QList &_list); 75 | 76 | protected: 77 | virtual void run(); 78 | 79 | private slots: 80 | void agent_started(); 81 | void worker_finished(); 82 | }; 83 | 84 | class QJDnsSharedDebugPrivate : public QObject 85 | { 86 | Q_OBJECT 87 | public: 88 | QJDnsSharedDebug *q; 89 | QMutex m; 90 | QStringList lines; 91 | bool dirty; 92 | 93 | QJDnsSharedDebugPrivate(QJDnsSharedDebug *_q); 94 | void addDebug(const QString &name, const QStringList &_lines); 95 | 96 | private slots: 97 | void doUpdate(); 98 | }; 99 | 100 | //---------------------------------------------------------------------------- 101 | // Handle 102 | //---------------------------------------------------------------------------- 103 | 104 | // QJDns uses integer handle ids, but they are only unique within 105 | // the relevant QJDns instance. Since we want our handles to be 106 | // unique across all instances, we'll make an instance/id pair. 107 | class Handle 108 | { 109 | public: 110 | QJDns *jdns; 111 | int id; 112 | 113 | Handle() : jdns(0), id(-1) 114 | { 115 | } 116 | 117 | Handle(QJDns *_jdns, int _id) : jdns(_jdns), id(_id) 118 | { 119 | } 120 | 121 | bool operator==(const Handle &a) const 122 | { 123 | if(a.jdns == jdns && a.id == id) 124 | return true; 125 | return false; 126 | } 127 | 128 | bool operator!=(const Handle &a) const 129 | { 130 | return !(operator==(a)); 131 | } 132 | }; 133 | 134 | class QJDnsSharedPrivate : public QObject 135 | { 136 | Q_OBJECT 137 | public: 138 | class Instance 139 | { 140 | public: 141 | QJDns *jdns; 142 | QHostAddress addr; 143 | int index; 144 | 145 | Instance() : jdns(0) 146 | , index(0) 147 | { 148 | } 149 | }; 150 | 151 | enum PreprocessMode 152 | { 153 | None, // don't muck with anything 154 | FillInAddress, // for A/AAAA 155 | FillInPtrOwner6, // for PTR, IPv6 156 | FillInPtrOwner4, // for PTR, IPv4 157 | }; 158 | 159 | QJDnsShared *q; 160 | QJDnsShared::Mode mode; 161 | bool shutting_down; 162 | QJDnsSharedDebug *db; 163 | QString dbname; 164 | 165 | QList instances; 166 | QHash instanceForQJDns; 167 | 168 | QSet requests; 169 | QHash requestForHandle; 170 | 171 | QJDnsSharedPrivate(QJDnsShared *_q); 172 | QJDnsSharedRequest *findRequest(QJDns *jdns, int id) const; 173 | void jdns_link(QJDns *jdns); 174 | int getNewIndex() const; 175 | void addDebug(int index, const QString &line); 176 | void doDebug(QJDns *jdns, int index); 177 | PreprocessMode determinePpMode(const QJDns::Record &in); 178 | QJDns::Record manipulateRecord(const QJDns::Record &in, PreprocessMode ppmode, bool *modified = 0); 179 | bool addInterface(const QHostAddress &addr); 180 | void removeInterface(const QHostAddress &addr); 181 | 182 | void queryStart(QJDnsSharedRequest *obj, const QByteArray &name, QJDns::Type qType); 183 | void queryCancel(QJDnsSharedRequest *obj); 184 | void publishStart(QJDnsSharedRequest *obj, QJDns::PublishMode m, const QJDns::Record &record); 185 | void publishUpdate(QJDnsSharedRequest *obj, const QJDns::Record &record); 186 | void publishCancel(QJDnsSharedRequest *obj); 187 | 188 | public slots: 189 | void late_shutdown(); 190 | 191 | private slots: 192 | void jdns_resultsReady(int id, const QJDns::Response &results); 193 | void jdns_published(int id); 194 | void jdns_error(int id, QJDns::Error e); 195 | void jdns_shutdownFinished(); 196 | void jdns_debugLinesReady(); 197 | }; 198 | 199 | class QJDnsSharedRequestPrivate : public QObject 200 | { 201 | Q_OBJECT 202 | public: 203 | QJDnsSharedRequest *q; 204 | QJDnsSharedPrivate *jsp; 205 | 206 | // current action 207 | QJDnsSharedRequest::Type type; 208 | QByteArray name; 209 | QJDns::Type qType; 210 | QJDns::PublishMode pubmode; 211 | QJDnsSharedPrivate::PreprocessMode ppmode; 212 | QJDns::Record pubrecord; 213 | 214 | // a single request might have to perform multiple QJDns operations 215 | QList handles; 216 | 217 | // keep a list of handles that successfully publish 218 | QList published; 219 | 220 | // use to weed out dups for multicast 221 | QList queryCache; 222 | 223 | bool success; 224 | QJDnsSharedRequest::Error error; 225 | QList results; 226 | SafeTimer lateTimer; 227 | QJDnsSharedRequestPrivate(QJDnsSharedRequest *_q); 228 | void resetSession(); 229 | 230 | private slots: 231 | void lateTimer_timeout(); 232 | }; 233 | 234 | #endif 235 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(jdns) 2 | 3 | if(NOT APPLE) 4 | cmake_minimum_required(VERSION 2.8.11) 5 | else() 6 | cmake_minimum_required(VERSION 3.0) 7 | endif() 8 | 9 | cmake_policy(SET CMP0003 NEW) 10 | 11 | # On Windows debug library should have 'd' postfix. 12 | if(WIN32) 13 | set(CMAKE_DEBUG_POSTFIX "d") 14 | elseif(APPLE) 15 | set(CMAKE_DEBUG_POSTFIX "_debug") 16 | endif(WIN32) 17 | 18 | # jdns can be used as bundled in this case need to explicity disable automoc 19 | # to avoid duplicates of mocked files 20 | set(CMAKE_AUTOMOC OFF) 21 | 22 | 23 | # OPTION(OSX_FRAMEWORK "Build a Mac OS X Framework") 24 | # SET(FRAMEWORK_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/Library/Frameworks" 25 | # CACHE PATH "Where to place jdns.framework if OSX_FRAMEWORK is selected") 26 | 27 | option(BUILD_SHARED_LIBS "Build shared library" ON) 28 | option(BUILD_QJDNS "Buid JDNS Qt-wrapper" ON) 29 | option(BUILD_JDNS_TOOL "Build jdns test tool" ON) 30 | 31 | # jdns tool requires qjdns 32 | if(NOT BUILD_QJDNS) 33 | set(BUILD_JDNS_TOOL OFF) 34 | endif(NOT BUILD_QJDNS) 35 | 36 | if(NOT BUILD_SHARED_LIBS) 37 | add_definitions(-DJDNS_STATIC) 38 | endif(NOT BUILD_SHARED_LIBS) 39 | 40 | if(BUILD_QJDNS) 41 | option(QT4_BUILD "Force building with Qt4 even if Qt5 is found") 42 | # On Linux in most cases (or always) can be cointstalled both Qt4 and Qt5. 43 | # Need to provide two versions of qjdns are Qt4 and Qt5-based. 44 | if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 45 | option(MULTI_QT "Consider multiple Qt version on the system (add suffixes)" ON) 46 | else() 47 | option(MULTI_QT "Consider multiple Qt version on the system (add suffixes)" OFF) 48 | endif() 49 | 50 | if(NOT QT4_BUILD) 51 | # Do not link against qtmain on Windows 52 | set(Qt5_NO_LINK_QTMAIN ON) 53 | find_package(Qt5Core QUIET) 54 | find_package(Qt5Network QUIET) 55 | endif(NOT QT4_BUILD) 56 | 57 | if(Qt5Core_FOUND) 58 | message("Qt5 found") 59 | 60 | include_directories(${Qt5Core_INCLUDE_DIRS}) 61 | include_directories(${Qt5Network_INCLUDE_DIRS}) 62 | add_definitions(${Qt5Core_DEFINITIONS}) 63 | add_definitions(${Qt5Network_DEFINITIONS}) 64 | 65 | # Tell CMake to run moc when necessary: 66 | set(CMAKE_AUTOMOC ON) 67 | # As moc files are generated in the binary dir, tell CMake 68 | # to always look for includes there: 69 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 70 | set(QJDns_QT_PC_VERSION "Qt5Core Qt5Network") 71 | set(QT_MAJ "-qt5") 72 | else(Qt5Core_FOUND) 73 | message("Qt5 not found, searching for Qt4") 74 | # Do not link against qtmain on Windows 75 | set(QT4_NO_LINK_QTMAIN ON) 76 | # Find Qt4 77 | find_package(Qt4 REQUIRED QtCore QtNetwork) 78 | 79 | # Include the cmake file needed to use qt4 80 | include(${QT_USE_FILE}) 81 | set(QJDns_QT_PC_VERSION "QtCore QtNetwork") 82 | set(QT_MAJ "-qt4") 83 | endif(Qt5Core_FOUND) 84 | 85 | if(NOT WIN32) 86 | set(QT_DONT_USE_QTGUI TRUE) 87 | endif(NOT WIN32) 88 | 89 | if(NOT MULTI_QT) 90 | set(QT_MAJ "") 91 | endif(NOT MULTI_QT) 92 | endif(BUILD_QJDNS) 93 | 94 | 95 | # put the include dirs which are in the source or build tree 96 | # before all other include dirs, so the headers in the sources 97 | # are prefered over the already installed ones 98 | set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) 99 | 100 | set(JDNS_INCLUDEDIR "${CMAKE_CURRENT_SOURCE_DIR}/include/jdns" ) 101 | 102 | 103 | #add extra search paths for libraries and includes 104 | set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) 105 | set(BIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE STRING "Directory where binary will install") 106 | set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE STRING "Directory where library will install") 107 | set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The directory the headers are installed in") 108 | set(DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/jdns" CACHE PATH "Directory where jdns documentation will install") 109 | 110 | set(LIB_INSTALL_DIR_FULL "${LIB_INSTALL_DIR}") 111 | set(INCLUDE_INSTALL_DIR_FULL "${INCLUDE_INSTALL_DIR}") 112 | 113 | # Normalize paths for comparsion 114 | get_filename_component(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} ABSOLUTE) 115 | get_filename_component(BIN_INSTALL_DIR ${BIN_INSTALL_DIR} ABSOLUTE) 116 | get_filename_component(LIB_INSTALL_DIR ${LIB_INSTALL_DIR} ABSOLUTE) 117 | get_filename_component(INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_DIR} ABSOLUTE) 118 | get_filename_component(DOC_INSTALL_DIR ${DOC_INSTALL_DIR} ABSOLUTE) 119 | 120 | if(WIN32) 121 | # if all paths are subdirs of CMAKE_INSTALL_PREFIX it is possible to use relative paths 122 | set(USE_RELATIVE_PATHS ON) 123 | foreach(PATH ${BIN_INSTALL_DIR} ${LIB_INSTALL_DIR} ${INCLUDE_INSTALL_DIR}) 124 | string(FIND "${PATH}/" "${CMAKE_INSTALL_PREFIX}/" POS) 125 | if(NOT "${POS}" STREQUAL "0") 126 | set(USE_RELATIVE_PATHS OFF) 127 | endif() 128 | endforeach() 129 | else() 130 | # On Mac OS X relative paths lead to relative library id. 131 | # Relative id can't be used to start application linked against such library. 132 | set(USE_RELATIVE_PATHS OFF) 133 | endif() 134 | 135 | if(USE_RELATIVE_PATHS) 136 | file(RELATIVE_PATH BIN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX} ${BIN_INSTALL_DIR}) 137 | file(RELATIVE_PATH LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX} ${LIB_INSTALL_DIR}) 138 | file(RELATIVE_PATH INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX} ${INCLUDE_INSTALL_DIR}) 139 | file(RELATIVE_PATH DOC_INSTALL_DIR ${CMAKE_INSTALL_PREFIX} ${DOC_INSTALL_DIR}) 140 | endif() 141 | 142 | if(NOT MSVC) 143 | set(JDNS_CONFIG_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake/jdns") 144 | set(QJDNS_CONFIG_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake/qjdns${QT_MAJ}") 145 | set(QJDNS_COMMON_CONFIG_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake/qjdns") 146 | else(NOT MSVC) 147 | set(JDNS_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/cmake/") 148 | set(QJDNS_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/cmake/") 149 | set(QJDNS_COMMON_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/cmake/") 150 | endif(NOT MSVC) 151 | 152 | set(JDNS_LIB_MAJOR_VERSION "2") 153 | set(JDNS_LIB_MINOR_VERSION "0") 154 | set(JDNS_LIB_PATCH_VERSION "6") 155 | 156 | set(JDNS_LIB_VERSION_STRING "${JDNS_LIB_MAJOR_VERSION}.${JDNS_LIB_MINOR_VERSION}.${JDNS_LIB_PATCH_VERSION}") 157 | 158 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" ) 159 | # Use the same path for shared and static library 160 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" ) 161 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" ) 162 | 163 | 164 | # pkg-config 165 | if(NOT WIN32) 166 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/jdns.pc.in 167 | ${CMAKE_CURRENT_BINARY_DIR}/jdns.pc 168 | @ONLY) 169 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jdns.pc 170 | DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) 171 | 172 | if(BUILD_QJDNS) 173 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qjdns.pc.in 174 | ${CMAKE_CURRENT_BINARY_DIR}/qjdns${QT_MAJ}.pc 175 | @ONLY) 176 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qjdns${QT_MAJ}.pc 177 | DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) 178 | endif(BUILD_QJDNS) 179 | endif(NOT WIN32) 180 | 181 | include_directories("include/jdns/") 182 | 183 | # Subdirs 184 | add_subdirectory(src) 185 | 186 | if(BUILD_JDNS_TOOL) 187 | add_subdirectory(tools/jdns) 188 | endif(BUILD_JDNS_TOOL) 189 | 190 | configure_file( 191 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" 192 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 193 | IMMEDIATE @ONLY) 194 | 195 | # cmake-modules 196 | include(CMakePackageConfigHelpers) 197 | configure_package_config_file( 198 | JDnsConfig.cmake.in 199 | "${CMAKE_CURRENT_BINARY_DIR}/JDnsConfig.cmake" 200 | INSTALL_DESTINATION ${JDNS_CONFIG_INSTALL_DIR} 201 | PATH_VARS BIN_INSTALL_DIR 202 | LIB_INSTALL_DIR 203 | INCLUDE_INSTALL_DIR) 204 | 205 | write_basic_config_version_file(JDnsConfigVersion.cmake VERSION ${JDNS_LIB_VERSION_STRING} COMPATIBILITY AnyNewerVersion) 206 | 207 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/JDnsConfig.cmake 208 | ${CMAKE_CURRENT_BINARY_DIR}/JDnsConfigVersion.cmake 209 | DESTINATION ${JDNS_CONFIG_INSTALL_DIR}) 210 | 211 | install(EXPORT jdns-export DESTINATION ${JDNS_CONFIG_INSTALL_DIR} FILE JDnsTargets.cmake) 212 | if(BUILD_QJDNS) 213 | install(EXPORT qjdns-export DESTINATION ${QJDNS_CONFIG_INSTALL_DIR} FILE QJDns${QT_MAJ}Targets.cmake) 214 | 215 | # cmake-modules 216 | configure_package_config_file( 217 | QJDns-qtConfig.cmake.in 218 | "${CMAKE_CURRENT_BINARY_DIR}/QJDns${QT_MAJ}Config.cmake" 219 | INSTALL_DESTINATION ${QJDNS_CONFIG_INSTALL_DIR}) 220 | 221 | write_basic_config_version_file(QJDns${QT_MAJ}ConfigVersion.cmake VERSION ${JDNS_LIB_VERSION_STRING} COMPATIBILITY AnyNewerVersion) 222 | 223 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/QJDns${QT_MAJ}Config.cmake 224 | ${CMAKE_CURRENT_BINARY_DIR}/QJDns${QT_MAJ}ConfigVersion.cmake 225 | DESTINATION ${QJDNS_CONFIG_INSTALL_DIR}) 226 | 227 | # Install QJDns config module which will automagically choose correct building of QJDns 228 | if(MULTI_QT) 229 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/QJDnsConfig.cmake.in 230 | ${CMAKE_CURRENT_BINARY_DIR}/QJDnsConfig.cmake 231 | COPYONLY) 232 | 233 | write_basic_config_version_file(QJDnsConfigVersion.cmake VERSION ${JDNS_LIB_VERSION_STRING} COMPATIBILITY AnyNewerVersion) 234 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/QJDnsConfig.cmake 235 | ${CMAKE_CURRENT_BINARY_DIR}/QJDnsConfigVersion.cmake 236 | DESTINATION ${QJDNS_COMMON_CONFIG_INSTALL_DIR}) 237 | endif() 238 | 239 | endif(BUILD_QJDNS) 240 | 241 | add_custom_target(uninstall 242 | "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") 243 | 244 | find_package(Doxygen) 245 | if(DOXYGEN_FOUND) 246 | configure_file(${CMAKE_SOURCE_DIR}/Doxyfile.in ${CMAKE_BINARY_DIR}/Doxyfile @ONLY) 247 | add_custom_target(doc ALL 248 | ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile 249 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 250 | COMMENT "Generating API documentation with Doxygen" VERBATIM) 251 | install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/apidocs/html 252 | DESTINATION ${DOC_INSTALL_DIR}) 253 | endif() 254 | -------------------------------------------------------------------------------- /include/jdns/jdns.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005,2006 Justin Karneges 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | /** 25 | * @file 26 | */ 27 | 28 | #ifndef JDNS_H 29 | #define JDNS_H 30 | 31 | #include "jdns_export.h" 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | typedef void (*jdns_object_dtor_func)(void *); 38 | typedef void *(*jdns_object_cctor_func)(const void *); 39 | 40 | #define JDNS_OBJECT \ 41 | jdns_object_dtor_func dtor; \ 42 | jdns_object_cctor_func cctor; 43 | 44 | #define JDNS_OBJECT_NEW(name) \ 45 | (name##_t *)jdns_object_new(sizeof(name##_t), \ 46 | (jdns_object_dtor_func)name##_delete, \ 47 | (jdns_object_cctor_func)name##_copy); 48 | 49 | typedef struct jdns_object 50 | { 51 | JDNS_OBJECT 52 | } jdns_object_t; 53 | 54 | JDNS_EXPORT void *jdns_object_new(int size, void (*dtor)(void *), 55 | void *(*cctor)(const void *)); 56 | JDNS_EXPORT void *jdns_object_copy(const void *a); 57 | JDNS_EXPORT void jdns_object_delete(void *a); 58 | JDNS_EXPORT void jdns_object_free(void *a); 59 | 60 | #define JDNS_LIST_DECLARE(name) \ 61 | JDNS_OBJECT \ 62 | int count; \ 63 | name##_t **item; 64 | 65 | typedef struct jdns_list 66 | { 67 | JDNS_OBJECT 68 | int count; 69 | void **item; 70 | int valueList; 71 | int autoDelete; 72 | } jdns_list_t; 73 | 74 | JDNS_EXPORT jdns_list_t *jdns_list_new(); 75 | JDNS_EXPORT jdns_list_t *jdns_list_copy(const jdns_list_t *a); 76 | JDNS_EXPORT void jdns_list_delete(jdns_list_t *a); 77 | JDNS_EXPORT void jdns_list_clear(jdns_list_t *a); 78 | JDNS_EXPORT void jdns_list_insert(jdns_list_t *a, void *item, int pos); 79 | JDNS_EXPORT void jdns_list_insert_value(jdns_list_t *a, const void *item, int pos); 80 | JDNS_EXPORT void jdns_list_remove(jdns_list_t *a, void *item); 81 | JDNS_EXPORT void jdns_list_remove_at(jdns_list_t *a, int pos); 82 | 83 | typedef struct jdns_string 84 | { 85 | JDNS_OBJECT 86 | unsigned char *data; 87 | int size; 88 | } jdns_string_t; 89 | 90 | JDNS_EXPORT jdns_string_t *jdns_string_new(); 91 | JDNS_EXPORT jdns_string_t *jdns_string_copy(const jdns_string_t *s); 92 | JDNS_EXPORT void jdns_string_delete(jdns_string_t *s); 93 | JDNS_EXPORT void jdns_string_set(jdns_string_t *s, const unsigned char *str, 94 | int str_len); 95 | JDNS_EXPORT void jdns_string_set_cstr(jdns_string_t *s, const char *str); 96 | 97 | // overlays jdns_list 98 | typedef struct jdns_stringlist 99 | { 100 | JDNS_OBJECT 101 | int count; 102 | jdns_string_t **item; 103 | } jdns_stringlist_t; 104 | 105 | JDNS_EXPORT jdns_stringlist_t *jdns_stringlist_new(); 106 | JDNS_EXPORT jdns_stringlist_t *jdns_stringlist_copy(const jdns_stringlist_t *a); 107 | JDNS_EXPORT void jdns_stringlist_delete(jdns_stringlist_t *a); 108 | JDNS_EXPORT void jdns_stringlist_append(jdns_stringlist_t *a, const jdns_string_t *str); 109 | 110 | typedef struct jdns_address 111 | { 112 | int isIpv6; 113 | union 114 | { 115 | unsigned long int v4; 116 | unsigned char *v6; // 16 bytes 117 | } addr; 118 | char *c_str; 119 | } jdns_address_t; 120 | 121 | JDNS_EXPORT jdns_address_t *jdns_address_new(); 122 | JDNS_EXPORT jdns_address_t *jdns_address_copy(const jdns_address_t *a); 123 | JDNS_EXPORT void jdns_address_delete(jdns_address_t *a); 124 | JDNS_EXPORT void jdns_address_set_ipv4(jdns_address_t *a, unsigned long int ipv4); 125 | JDNS_EXPORT void jdns_address_set_ipv6(jdns_address_t *a, const unsigned char *ipv6); 126 | 127 | /** 128 | * @return 1 if string was ok, else 0. Note: IPv4 addresses only! 129 | */ 130 | JDNS_EXPORT int jdns_address_set_cstr(jdns_address_t *a, const char *str); 131 | 132 | /** 133 | * @return 1 if the same, else 0 134 | */ 135 | JDNS_EXPORT int jdns_address_cmp(const jdns_address_t *a, const jdns_address_t *b); 136 | 137 | // convenient predefined addresses/ports 138 | #define JDNS_UNICAST_PORT 53 139 | #define JDNS_MULTICAST_PORT 5353 140 | JDNS_EXPORT jdns_address_t *jdns_address_multicast4_new(); // 224.0.0.251 141 | JDNS_EXPORT jdns_address_t *jdns_address_multicast6_new(); // FF02::FB 142 | 143 | typedef struct jdns_server 144 | { 145 | unsigned char *name; 146 | /** 147 | * SRV only 148 | */ 149 | int port; 150 | int priority; 151 | 152 | /** 153 | * SRV only 154 | */ 155 | int weight; 156 | } jdns_server_t; 157 | 158 | JDNS_EXPORT jdns_server_t *jdns_server_new(); 159 | JDNS_EXPORT jdns_server_t *jdns_server_copy(const jdns_server_t *s); 160 | JDNS_EXPORT void jdns_server_delete(jdns_server_t *s); 161 | JDNS_EXPORT void jdns_server_set_name(jdns_server_t *s, const unsigned char *name); 162 | 163 | typedef struct jdns_nameserver 164 | { 165 | jdns_address_t *address; 166 | int port; 167 | } jdns_nameserver_t; 168 | 169 | JDNS_EXPORT jdns_nameserver_t *jdns_nameserver_new(); 170 | JDNS_EXPORT jdns_nameserver_t *jdns_nameserver_copy(const jdns_nameserver_t *a); 171 | JDNS_EXPORT void jdns_nameserver_delete(jdns_nameserver_t *a); 172 | JDNS_EXPORT void jdns_nameserver_set(jdns_nameserver_t *a, const jdns_address_t *addr, 173 | int port); 174 | 175 | typedef struct jdns_nameserverlist 176 | { 177 | int count; 178 | jdns_nameserver_t **item; 179 | } jdns_nameserverlist_t; 180 | 181 | JDNS_EXPORT jdns_nameserverlist_t *jdns_nameserverlist_new(); 182 | JDNS_EXPORT jdns_nameserverlist_t *jdns_nameserverlist_copy(const jdns_nameserverlist_t *a); 183 | JDNS_EXPORT void jdns_nameserverlist_delete(jdns_nameserverlist_t *a); 184 | JDNS_EXPORT void jdns_nameserverlist_append(jdns_nameserverlist_t *a, 185 | const jdns_address_t *addr, int port); 186 | 187 | typedef struct jdns_dnshost 188 | { 189 | jdns_string_t *name; 190 | jdns_address_t *address; 191 | } jdns_dnshost_t; 192 | 193 | typedef struct jdns_dnshostlist 194 | { 195 | int count; 196 | jdns_dnshost_t **item; 197 | } jdns_dnshostlist_t; 198 | 199 | typedef struct jdns_dnsparams 200 | { 201 | jdns_nameserverlist_t *nameservers; 202 | jdns_stringlist_t *domains; 203 | jdns_dnshostlist_t *hosts; 204 | } jdns_dnsparams_t; 205 | 206 | JDNS_EXPORT jdns_dnsparams_t *jdns_dnsparams_new(); 207 | JDNS_EXPORT jdns_dnsparams_t *jdns_dnsparams_copy(jdns_dnsparams_t *a); 208 | JDNS_EXPORT void jdns_dnsparams_delete(jdns_dnsparams_t *a); 209 | JDNS_EXPORT void jdns_dnsparams_append_nameserver(jdns_dnsparams_t *a, 210 | const jdns_address_t *addr, int port); 211 | JDNS_EXPORT void jdns_dnsparams_append_domain(jdns_dnsparams_t *a, 212 | const jdns_string_t *domain); 213 | JDNS_EXPORT void jdns_dnsparams_append_host(jdns_dnsparams_t *a, 214 | const jdns_string_t *name, const jdns_address_t *address); 215 | 216 | #define JDNS_RTYPE_A 1 217 | #define JDNS_RTYPE_AAAA 28 218 | #define JDNS_RTYPE_MX 15 219 | #define JDNS_RTYPE_SRV 33 220 | #define JDNS_RTYPE_CNAME 5 221 | #define JDNS_RTYPE_PTR 12 222 | #define JDNS_RTYPE_TXT 16 223 | #define JDNS_RTYPE_HINFO 13 224 | #define JDNS_RTYPE_NS 2 225 | #define JDNS_RTYPE_ANY 255 226 | 227 | typedef struct jdns_rr 228 | { 229 | unsigned char *owner; 230 | int ttl; 231 | int type; 232 | int qclass; 233 | int rdlength; 234 | unsigned char *rdata; 235 | int haveKnown; 236 | 237 | union 238 | { 239 | jdns_address_t *address; /**< for A, AAAA */ 240 | jdns_server_t *server; /**< for MX, SRV */ 241 | unsigned char *name; /**< for CNAME, PTR, NS */ 242 | jdns_stringlist_t *texts; /**< for TXT */ 243 | struct 244 | { 245 | jdns_string_t *cpu; 246 | jdns_string_t *os; 247 | } hinfo; // for HINFO 248 | } data; 249 | } jdns_rr_t; 250 | 251 | JDNS_EXPORT jdns_rr_t *jdns_rr_new(); 252 | JDNS_EXPORT jdns_rr_t *jdns_rr_copy(const jdns_rr_t *r); 253 | JDNS_EXPORT void jdns_rr_delete(jdns_rr_t *r); 254 | JDNS_EXPORT void jdns_rr_set_owner(jdns_rr_t *r, const unsigned char *name); 255 | JDNS_EXPORT void jdns_rr_set_record(jdns_rr_t *r, int type, const unsigned char *rdata, 256 | int rdlength); 257 | JDNS_EXPORT void jdns_rr_set_A(jdns_rr_t *r, const jdns_address_t *address); 258 | JDNS_EXPORT void jdns_rr_set_AAAA(jdns_rr_t *r, const jdns_address_t *address); 259 | JDNS_EXPORT void jdns_rr_set_MX(jdns_rr_t *r, const unsigned char *name, int priority); 260 | JDNS_EXPORT void jdns_rr_set_SRV(jdns_rr_t *r, const unsigned char *name, int port, 261 | int priority, int weight); 262 | JDNS_EXPORT void jdns_rr_set_CNAME(jdns_rr_t *r, const unsigned char *name); 263 | JDNS_EXPORT void jdns_rr_set_PTR(jdns_rr_t *r, const unsigned char *name); 264 | JDNS_EXPORT void jdns_rr_set_TXT(jdns_rr_t *r, const jdns_stringlist_t *texts); 265 | JDNS_EXPORT void jdns_rr_set_HINFO(jdns_rr_t *r, const jdns_string_t *cpu, 266 | const jdns_string_t *os); 267 | JDNS_EXPORT void jdns_rr_set_NS(jdns_rr_t *r, const unsigned char *name); 268 | // note: only works on known types 269 | JDNS_EXPORT int jdns_rr_verify(const jdns_rr_t *r); 270 | 271 | typedef struct jdns_response 272 | { 273 | int answerCount; 274 | jdns_rr_t **answerRecords; 275 | int authorityCount; 276 | jdns_rr_t **authorityRecords; 277 | int additionalCount; 278 | jdns_rr_t **additionalRecords; 279 | } jdns_response_t; 280 | 281 | JDNS_EXPORT jdns_response_t *jdns_response_new(); 282 | JDNS_EXPORT jdns_response_t *jdns_response_copy(const jdns_response_t *r); 283 | JDNS_EXPORT void jdns_response_delete(jdns_response_t *r); 284 | JDNS_EXPORT void jdns_response_append_answer(jdns_response_t *r, const jdns_rr_t *rr); 285 | JDNS_EXPORT void jdns_response_append_authority(jdns_response_t *r, const jdns_rr_t *rr); 286 | JDNS_EXPORT void jdns_response_append_additional(jdns_response_t *r, 287 | const jdns_rr_t *rr); 288 | 289 | #define JDNS_PUBLISH_SHARED 0x0001 290 | #define JDNS_PUBLISH_UNIQUE 0x0002 291 | 292 | #define JDNS_STEP_TIMER 0x0001 293 | #define JDNS_STEP_HANDLE 0x0002 294 | 295 | #define JDNS_EVENT_RESPONSE 0x0001 296 | #define JDNS_EVENT_PUBLISH 0x0002 297 | #define JDNS_EVENT_SHUTDOWN 0x0003 298 | 299 | #define JDNS_STATUS_SUCCESS 0x0001 300 | #define JDNS_STATUS_NXDOMAIN 0x0002 301 | #define JDNS_STATUS_ERROR 0x0003 302 | #define JDNS_STATUS_TIMEOUT 0x0004 303 | #define JDNS_STATUS_CONFLICT 0x0005 304 | 305 | typedef struct jdns_session jdns_session_t; 306 | 307 | typedef struct jdns_callbacks 308 | { 309 | /** 310 | * user-supplied context 311 | */ 312 | void *app; 313 | 314 | /** 315 | * @param s session 316 | * @param app user-supplied context 317 | * @return milliseconds since session started 318 | */ 319 | int (*time_now)(jdns_session_t *s, void *app); 320 | 321 | /** 322 | * @param s session 323 | * @param app user-supplied context 324 | * @return random integer between 0-65535 325 | */ 326 | int (*rand_int)(jdns_session_t *s, void *app); 327 | 328 | /** 329 | * @param s session 330 | * @param app user-supplied context 331 | * @param str a line of debug text 332 | */ 333 | void (*debug_line)(jdns_session_t *s, void *app, const char *str); 334 | 335 | /** 336 | * @note For multicast, the following must be done: 337 | * - use SO_REUSEPORT to share with other mdns programs 338 | * - use IP_ADD_MEMBERSHIP to associate addr and maddr 339 | * - set IP_MULTICAST_TTL to 255 340 | * 341 | * @param s session 342 | * @param app user-supplied context 343 | * @param addr ip address of interface to bind to. 0 for all 344 | * @param port port of interface to bind to. 0 for any 345 | * @param maddr multicast address. 0 if not using multicast 346 | * @return handle (>0) of bound socket, or 0 on error 347 | */ 348 | int (*udp_bind)(jdns_session_t *s, void *app, 349 | const jdns_address_t *addr, int port, 350 | const jdns_address_t *maddr); 351 | 352 | /** 353 | * @param s session 354 | * @param app user-supplied context 355 | * @param handle handle of socket obtained with udp_bind 356 | */ 357 | void (*udp_unbind)(jdns_session_t *s, void *app, int handle); 358 | 359 | /** 360 | * @param s session 361 | * @param app user-supplied context 362 | * @param handle handle of socket obtained with udp_bind 363 | * @param addr store ip address of sender 364 | * @param port store port of sender 365 | * @param buf store packet content 366 | * @param bufsize value contains max size, to be changed to real size 367 | * @return 1 if packet read, 0 if none available 368 | */ 369 | int (*udp_read)(jdns_session_t *s, void *app, int handle, 370 | jdns_address_t *addr, int *port, unsigned char *buf, 371 | int *bufsize); 372 | 373 | /** 374 | * @param s session 375 | * @param app user-supplied context 376 | * @param handle handle of socket obtained with udp_bind 377 | * @param addr ip address of recipient 378 | * @param port port of recipient 379 | * @param buf packet content 380 | * @param bufsize size of packet 381 | * @return 1 if packet taken for writing, 0 if this is a bad time 382 | */ 383 | int (*udp_write)(jdns_session_t *s, void *app, int handle, 384 | const jdns_address_t *addr, int port, unsigned char *buf, 385 | int bufsize); 386 | } jdns_callbacks_t; 387 | 388 | typedef struct jdns_event 389 | { 390 | /** 391 | * JDNS_EVENT 392 | */ 393 | int type; 394 | 395 | /** 396 | * query id or publish id 397 | */ 398 | int id; 399 | 400 | /** 401 | * for query, this can be SUCCESS, NXDOMAIN, ERROR, or TIMEOUT 402 | * for publish, this can be SUCCESS, ERROR, or CONFLICT 403 | */ 404 | int status; 405 | 406 | /** 407 | * for query 408 | */ 409 | jdns_response_t *response; 410 | } jdns_event_t; 411 | 412 | JDNS_EXPORT void jdns_event_delete(jdns_event_t *e); 413 | 414 | /** 415 | * @param callbacks the struct of callbacks 416 | * @return newly allocated session 417 | */ 418 | JDNS_EXPORT jdns_session_t *jdns_session_new(jdns_callbacks_t *callbacks); 419 | 420 | /** 421 | * @param session to free 422 | */ 423 | JDNS_EXPORT void jdns_session_delete(jdns_session_t *s); 424 | 425 | /** 426 | * @param s session 427 | * @param addr ip address of interface to bind to. NULL for all 428 | * @param port port of interface to bind to. 0 for any 429 | * @return 1 on success, 0 on failure 430 | */ 431 | JDNS_EXPORT int jdns_init_unicast(jdns_session_t *s, const jdns_address_t *addr, int port); 432 | 433 | /** 434 | * @param s session 435 | * @param addr ip address of interface to bind to. NULL for all 436 | * @param port port of interface to bind to. 0 for any 437 | * @param maddr multicast address to associate with. Can not be NULL 438 | * @return 1 on success, 0 on failure 439 | */ 440 | JDNS_EXPORT int jdns_init_multicast(jdns_session_t *s, const jdns_address_t *addr, int port, const jdns_address_t *maddr); 441 | 442 | /** 443 | * @param s session 444 | */ 445 | JDNS_EXPORT void jdns_shutdown(jdns_session_t *s); 446 | 447 | /** 448 | * @param s session 449 | * @param nslist list of nameservers 450 | */ 451 | JDNS_EXPORT void jdns_set_nameservers(jdns_session_t *s, const jdns_nameserverlist_t *nslist); 452 | 453 | /** 454 | * @param s session 455 | */ 456 | JDNS_EXPORT void jdns_probe(jdns_session_t *s); 457 | 458 | /** 459 | * @param s session 460 | * @param name the name to look up 461 | * @param rtype the record type 462 | * @return id of this operation 463 | */ 464 | JDNS_EXPORT int jdns_query(jdns_session_t *s, const unsigned char *name, int rtype); 465 | 466 | /** 467 | * @param s session 468 | * @param id the operation id to cancel 469 | */ 470 | JDNS_EXPORT void jdns_cancel_query(jdns_session_t *s, int id); 471 | 472 | /** 473 | * @note Supported record types: A, AAAA, SRV, CNAME, PTR, TXT, and HINFO. 474 | * If the published type is not one of these, raw rdata must be set. 475 | * 476 | * @param s session 477 | * @param mode JDNS_PUBLISH shared or unique 478 | * @param rec the record data 479 | * @return id of this operation 480 | */ 481 | JDNS_EXPORT int jdns_publish(jdns_session_t *s, int mode, const jdns_rr_t *rec); 482 | 483 | /** 484 | * @note update only works on successfully published records, and no event is generated for a 485 | * successful update. 486 | * 487 | * @param s session 488 | * @param id the operation id to update 489 | * @param rec the record data 490 | */ 491 | JDNS_EXPORT void jdns_update_publish(jdns_session_t *s, int id, const jdns_rr_t *rec); 492 | 493 | /** 494 | * @param s session 495 | * @param id the operation id to cancel 496 | */ 497 | JDNS_EXPORT void jdns_cancel_publish(jdns_session_t *s, int id); 498 | 499 | /** 500 | * @param s session 501 | * @return JDNS_STEP flags OR'd together 502 | */ 503 | JDNS_EXPORT int jdns_step(jdns_session_t *s); 504 | 505 | /** 506 | * @param s session 507 | * @return milliseconds until timeout 508 | */ 509 | JDNS_EXPORT int jdns_next_timer(jdns_session_t *s); 510 | 511 | /** 512 | * @param s session 513 | * @param handle handle that is now readable 514 | */ 515 | JDNS_EXPORT void jdns_set_handle_readable(jdns_session_t *s, int handle); 516 | 517 | /** 518 | * @param s session 519 | * @param handle handle that is now writable 520 | */ 521 | JDNS_EXPORT void jdns_set_handle_writable(jdns_session_t *s, int handle); 522 | 523 | /** 524 | * @param s session 525 | * @return newly allocated event, or zero if none are ready 526 | */ 527 | JDNS_EXPORT jdns_event_t *jdns_next_event(jdns_session_t *s); 528 | 529 | /** 530 | * @return newly allocated dnsparams from the system 531 | */ 532 | JDNS_EXPORT jdns_dnsparams_t *jdns_system_dnsparams(); 533 | 534 | /** 535 | * . 536 | * 537 | * Normally, when a unicast query completes or any kind of query or publish 538 | * operation results in an error, the operation is automatically "canceled". 539 | * When id holding is enabled, the operation still stops internally, but the 540 | * id value used by that operation is "held" until the application 541 | * explicitly calls jdns_cancel_query() or jdns_cancel_publish() to release 542 | * it. This allows the application to ensure there is no ambiguity when 543 | * determining which operation a particular event belongs to. It is disabled 544 | * be default so as to not introduce memory leaks in existing applications, 545 | * however new applications really should use it. 546 | * 547 | * @param s session 548 | * @param enabled whether to enable id holding. Default is 0 (disabled) 549 | */ 550 | JDNS_EXPORT void jdns_set_hold_ids_enabled(jdns_session_t *s, int enabled); 551 | 552 | #ifdef __cplusplus 553 | } 554 | #endif 555 | 556 | #endif 557 | -------------------------------------------------------------------------------- /tools/jdns/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005 Justin Karneges 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include "main.h" 25 | #include 26 | #include 27 | #include "qjdns.h" 28 | 29 | QString dataToString(const QByteArray &buf) 30 | { 31 | QString out; 32 | for(int n = 0; n < buf.size(); ++n) 33 | { 34 | unsigned char c = (unsigned char)buf[n]; 35 | if(c == '\\') 36 | out += "\\\\"; 37 | else if(c >= 0x20 && c < 0x7f) 38 | out += c; 39 | else 40 | out += QString().sprintf("\\x%02x", (unsigned int)c); 41 | } 42 | return out; 43 | } 44 | 45 | void print_record(const QJDns::Record &r) 46 | { 47 | switch(r.type) 48 | { 49 | case QJDns::A: 50 | printf(" A: [%s] (ttl=%d)\n", qPrintable(r.address.toString()), r.ttl); 51 | break; 52 | case QJDns::Aaaa: 53 | printf(" AAAA: [%s] (ttl=%d)\n", qPrintable(r.address.toString()), r.ttl); 54 | break; 55 | case QJDns::Mx: 56 | printf(" MX: [%s] priority=%d (ttl=%d)\n", r.name.data(), r.priority, r.ttl); 57 | break; 58 | case QJDns::Srv: 59 | printf(" SRV: [%s] port=%d priority=%d weight=%d (ttl=%d)\n", r.name.data(), r.port, r.priority, r.weight, r.ttl); 60 | break; 61 | case QJDns::Cname: 62 | printf(" CNAME: [%s] (ttl=%d)\n", r.name.data(), r.ttl); 63 | break; 64 | case QJDns::Ptr: 65 | printf(" PTR: [%s] (ttl=%d)\n", r.name.data(), r.ttl); 66 | break; 67 | case QJDns::Txt: 68 | { 69 | printf(" TXT: count=%d (ttl=%d)\n", r.texts.count(), r.ttl); 70 | for(int n = 0; n < r.texts.count(); ++n) 71 | printf(" len=%d [%s]\n", r.texts[n].size(), qPrintable(dataToString(r.texts[n]))); 72 | break; 73 | } 74 | case QJDns::Hinfo: 75 | printf(" HINFO: [%s] [%s] (ttl=%d)\n", r.cpu.data(), r.os.data(), r.ttl); 76 | break; 77 | case QJDns::Ns: 78 | printf(" NS: [%s] (ttl=%d)\n", r.name.data(), r.ttl); 79 | break; 80 | default: 81 | printf(" (Unknown): type=%d, size=%d (ttl=%d)\n", r.type, r.rdata.size(), r.ttl); 82 | break; 83 | } 84 | } 85 | 86 | App::App() 87 | { 88 | connect(&jdns, SIGNAL(resultsReady(int,QJDns::Response)), SLOT(jdns_resultsReady(int,QJDns::Response))); 89 | connect(&jdns, SIGNAL(published(int)), SLOT(jdns_published(int))); 90 | connect(&jdns, SIGNAL(error(int,QJDns::Error)), SLOT(jdns_error(int,QJDns::Error))); 91 | connect(&jdns, SIGNAL(shutdownFinished()), SLOT(jdns_shutdownFinished())); 92 | connect(&jdns, SIGNAL(debugLinesReady()), SLOT(jdns_debugLinesReady())); 93 | } 94 | 95 | App::~App() 96 | { 97 | } 98 | 99 | void App::start() 100 | { 101 | if(mode == "uni") 102 | { 103 | if(!jdns.init(QJDns::Unicast, opt_ipv6 ? QHostAddress::AnyIPv6 : QHostAddress::Any)) 104 | { 105 | jdns_debugLinesReady(); 106 | printf("unable to bind\n"); 107 | emit quit(); 108 | return; 109 | } 110 | 111 | QList addrs; 112 | for(int n = 0; n < nslist.count(); ++n) 113 | { 114 | QJDns::NameServer host; 115 | QString str = nslist[n]; 116 | if(str == "mul") 117 | { 118 | if(opt_ipv6) 119 | host.address = QHostAddress("FF02::FB"); 120 | else 121 | host.address = QHostAddress("224.0.0.251"); 122 | host.port = 5353; 123 | } 124 | else 125 | { 126 | int at = str.indexOf(';'); 127 | if(at != -1) 128 | { 129 | host.address = QHostAddress(str.mid(0, at)); 130 | host.port = str.mid(at + 1).toInt(); 131 | } 132 | else 133 | { 134 | host.address = QHostAddress(str); 135 | } 136 | } 137 | 138 | if(host.address.isNull() || host.port <= 0) 139 | { 140 | printf("bad nameserver: [%s]\n", qPrintable(nslist[n])); 141 | emit quit(); 142 | return; 143 | } 144 | addrs += host; 145 | } 146 | 147 | if(addrs.isEmpty()) 148 | addrs = QJDns::systemInfo().nameServers; 149 | 150 | if(addrs.isEmpty()) 151 | { 152 | printf("no nameservers were detected or specified\n"); 153 | emit quit(); 154 | return; 155 | } 156 | 157 | jdns.setNameServers(addrs); 158 | } 159 | else 160 | { 161 | if(!jdns.init(QJDns::Multicast, opt_ipv6 ? QHostAddress::AnyIPv6 : QHostAddress::Any)) 162 | { 163 | jdns_debugLinesReady(); 164 | printf("unable to bind\n"); 165 | emit quit(); 166 | return; 167 | } 168 | } 169 | 170 | if(mode == "uni" || mode == "mul") 171 | { 172 | int x = QJDns::A; 173 | if(type == "ptr") 174 | x = QJDns::Ptr; 175 | else if(type == "srv") 176 | x = QJDns::Srv; 177 | else if(type == "a") 178 | x = QJDns::A; 179 | else if(type == "aaaa") 180 | x = QJDns::Aaaa; 181 | else if(type == "mx") 182 | x = QJDns::Mx; 183 | else if(type == "txt") 184 | x = QJDns::Txt; 185 | else if(type == "hinfo") 186 | x = QJDns::Hinfo; 187 | else if(type == "cname") 188 | x = QJDns::Cname; 189 | else if(type == "ns") 190 | x = QJDns::Ns; 191 | else if(type == "any") 192 | x = QJDns::Any; 193 | else 194 | { 195 | bool ok; 196 | int y = type.toInt(&ok); 197 | if(ok) 198 | x = y; 199 | } 200 | 201 | req_id = jdns.queryStart(name.toLatin1(), x); 202 | printf("[%d] Querying for [%s] type=%d ...\n", req_id, qPrintable(name), x); 203 | } 204 | else // publish 205 | { 206 | for(int n = 0; n < pubitems.count(); ++n) 207 | { 208 | const QJDns::Record &rr = pubitems[n]; 209 | QJDns::PublishMode m = QJDns::Unique; 210 | if(rr.type == QJDns::Ptr) 211 | m = QJDns::Shared; 212 | int id = jdns.publishStart(m, rr); 213 | printf("[%d] Publishing [%s] type=%d ...\n", id, rr.owner.data(), rr.type); 214 | } 215 | } 216 | 217 | if(opt_quit) 218 | QTimer::singleShot(quit_time * 1000, this, SLOT(doShutdown())); 219 | } 220 | 221 | 222 | void App::jdns_resultsReady(int id, const QJDns::Response &results) 223 | { 224 | printf("[%d] Results\n", id); 225 | for(int n = 0; n < results.answerRecords.count(); ++n) 226 | print_record(results.answerRecords[n]); 227 | 228 | if(mode == "uni") 229 | jdns.shutdown(); 230 | } 231 | 232 | void App::jdns_published(int id) 233 | { 234 | printf("[%d] Published\n", id); 235 | } 236 | 237 | void App::jdns_error(int id, QJDns::Error e) 238 | { 239 | QString str; 240 | if(e == QJDns::ErrorGeneric) 241 | str = "Generic"; 242 | else if(e == QJDns::ErrorNXDomain) 243 | str = "NXDomain"; 244 | else if(e == QJDns::ErrorTimeout) 245 | str = "Timeout"; 246 | else if(e == QJDns::ErrorConflict) 247 | str = "Conflict"; 248 | printf("[%d] Error: %s\n", id, qPrintable(str)); 249 | jdns.shutdown(); 250 | } 251 | 252 | void App::jdns_shutdownFinished() 253 | { 254 | emit quit(); 255 | } 256 | 257 | void App::jdns_debugLinesReady() 258 | { 259 | QStringList lines = jdns.debugLines(); 260 | if(opt_debug) 261 | { 262 | for(int n = 0; n < lines.count(); ++n) 263 | printf("jdns: %s\n", qPrintable(lines[n])); 264 | } 265 | } 266 | 267 | void App::doShutdown() 268 | { 269 | jdns.shutdown(); 270 | } 271 | 272 | void usage() 273 | { 274 | printf("usage: jdns (options) uni [type] [name] (nameserver(;port)|mul ...)\n"); 275 | printf(" jdns (options) mul [type] [name]\n"); 276 | printf(" jdns (options) pub [items ...]\n"); 277 | printf(" jdns sys\n"); 278 | printf("\n"); 279 | printf("options:\n"); 280 | printf(" -d show debug output\n"); 281 | printf(" -6 use ipv6\n"); 282 | printf(" -q x quit x seconds after starting\n"); 283 | printf("\n"); 284 | printf("uni/mul types: a aaaa ptr srv mx txt hinfo cname ns any\n"); 285 | printf("pub items: ptr:name,answer srv:name,answer,port a:name,ipaddr\n"); 286 | printf(" txt:name,str0,...,strn aaaa:name,ipaddr\n"); 287 | printf("\n"); 288 | printf("examples:\n"); 289 | printf(" jdns uni a jabber.org 192.168.0.1\n"); 290 | printf(" jdns uni srv _xmpp-client._tcp.jabber.org 192.168.0.1;53\n"); 291 | printf(" jdns uni 10 user@host._presence._tcp.local mul\n"); 292 | printf(" jdns mul a foobar.local\n"); 293 | printf(" jdns mul ptr _services._dns-sd._udp.local\n"); 294 | printf(" jdns pub a:mybox.local.,192.168.0.55\n"); 295 | printf("\n"); 296 | } 297 | 298 | int main(int argc, char **argv) 299 | { 300 | QCoreApplication app(argc, argv); 301 | 302 | if(argc < 2) 303 | { 304 | usage(); 305 | return 1; 306 | } 307 | 308 | // get args 309 | QStringList args; 310 | for(int n = 1; n < argc; ++n) 311 | args += QString(argv[n]); 312 | 313 | bool opt_debug = false; 314 | bool opt_ipv6 = false; 315 | bool opt_quit = false; 316 | int quit_time = 0; 317 | QString mode, type, name, ipaddr; 318 | QStringList nslist; 319 | QList pubitems; 320 | 321 | // options 322 | for(int n = 0; n < args.count(); ++n) 323 | { 324 | if(args[n].left(1) == "-") 325 | { 326 | if(args[n] == "-d") 327 | opt_debug = true; 328 | else if(args[n] == "-6") 329 | opt_ipv6 = true; 330 | else if(args[n] == "-q") 331 | { 332 | if(n + 1 >= args.count()) 333 | { 334 | printf("need to specify number of seconds\n"); 335 | usage(); 336 | return 1; 337 | } 338 | 339 | int x = args[n + 1].toInt(); 340 | if(x < 1) 341 | x = 30; 342 | 343 | opt_quit = true; 344 | quit_time = x; 345 | 346 | args.removeAt(n + 1); 347 | } 348 | else 349 | { 350 | printf("bad option\n"); 351 | usage(); 352 | return 1; 353 | } 354 | args.removeAt(n); 355 | --n; // adjust position 356 | } 357 | } 358 | 359 | mode = args[0]; 360 | if(mode == "uni" || mode == "mul") 361 | { 362 | if(args.count() < 3) 363 | { 364 | printf("not enough args\n"); 365 | usage(); 366 | return 1; 367 | } 368 | type = args[1]; 369 | name = args[2]; 370 | if(mode == "uni") 371 | { 372 | for(int n = 3; n < args.count(); ++n) 373 | nslist += QString(args[n]); 374 | } 375 | } 376 | else if(mode == "pub") 377 | { 378 | if(args.count() < 2) 379 | { 380 | printf("not enough args\n"); 381 | usage(); 382 | return 1; 383 | } 384 | for(int n = 1; n < args.count(); ++n) 385 | { 386 | QString arg = args[n]; 387 | int at = arg.indexOf(':'); 388 | if(at == -1) 389 | { 390 | printf("missing colon\n"); 391 | usage(); 392 | return 1; 393 | } 394 | QString type = arg.mid(0, at).toLower(); 395 | QString val = arg.mid(at + 1); 396 | if(type == "a") 397 | { 398 | QStringList list = val.split(','); 399 | if(list.count() != 2) 400 | { 401 | printf("bad format for A type\n"); 402 | usage(); 403 | return 1; 404 | } 405 | QHostAddress host(list[1]); 406 | if(host.isNull() || host.protocol() != QAbstractSocket::IPv4Protocol) 407 | { 408 | printf("bad format for A type IP address\n"); 409 | usage(); 410 | return 1; 411 | } 412 | 413 | QJDns::Record rec; 414 | rec.owner = list[0].toLatin1(); 415 | rec.type = QJDns::A; 416 | rec.ttl = 120; 417 | rec.haveKnown = true; 418 | rec.address = host; 419 | pubitems += rec; 420 | } 421 | else if(type == "aaaa") 422 | { 423 | QStringList list = val.split(','); 424 | if(list.count() != 2) 425 | { 426 | printf("bad format for AAAA type\n"); 427 | usage(); 428 | return 1; 429 | } 430 | QHostAddress host(list[1]); 431 | if(host.isNull() || host.protocol() != QAbstractSocket::IPv6Protocol) 432 | { 433 | printf("bad format for AAAA type IP address\n"); 434 | usage(); 435 | return 1; 436 | } 437 | 438 | QJDns::Record rec; 439 | rec.owner = list[0].toLatin1(); 440 | rec.type = QJDns::Aaaa; 441 | rec.ttl = 120; 442 | rec.haveKnown = true; 443 | rec.address = host; 444 | pubitems += rec; 445 | } 446 | else if(type == "srv") 447 | { 448 | QStringList list = val.split(','); 449 | if(list.count() != 3) 450 | { 451 | printf("bad format for SRV type\n"); 452 | usage(); 453 | return 1; 454 | } 455 | 456 | QJDns::Record rec; 457 | rec.owner = list[0].toLatin1(); 458 | rec.type = QJDns::Srv; 459 | rec.ttl = 120; 460 | rec.haveKnown = true; 461 | rec.name = list[1].toLatin1(); 462 | rec.priority = 0; 463 | rec.weight = 0; 464 | rec.port = list[2].toInt(); 465 | pubitems += rec; 466 | } 467 | else if(type == "ptr") 468 | { 469 | QStringList list = val.split(','); 470 | if(list.count() != 2) 471 | { 472 | printf("bad format for PTR type\n"); 473 | usage(); 474 | return 1; 475 | } 476 | 477 | QJDns::Record rec; 478 | rec.owner = list[0].toLatin1(); 479 | rec.type = QJDns::Ptr; 480 | rec.ttl = 120; 481 | rec.haveKnown = true; 482 | rec.name = list[1].toLatin1(); 483 | pubitems += rec; 484 | } 485 | else if(type == "txt") 486 | { 487 | QStringList list = val.split(','); 488 | QList texts; 489 | for(int n = 1; n < list.count(); ++n) 490 | texts += list[n].toLatin1(); 491 | 492 | QJDns::Record rec; 493 | rec.owner = list[0].toLatin1(); 494 | rec.type = QJDns::Txt; 495 | rec.ttl = 120; 496 | rec.haveKnown = true; 497 | rec.texts = texts; 498 | pubitems += rec; 499 | } 500 | else 501 | { 502 | printf("bad record type [%s]\n", qPrintable(type)); 503 | usage(); 504 | return 1; 505 | } 506 | } 507 | } 508 | else if(mode == "sys") 509 | { 510 | QJDns::SystemInfo info = QJDns::systemInfo(); 511 | 512 | printf("DNS System Information\n"); 513 | printf(" Name Servers:\n"); 514 | if(!info.nameServers.isEmpty()) 515 | { 516 | for(int n = 0; n < info.nameServers.count(); ++n) 517 | printf(" %s\n", qPrintable(info.nameServers[n].address.toString())); 518 | } 519 | else 520 | printf(" (None)\n"); 521 | 522 | printf(" Domains:\n"); 523 | if(!info.domains.isEmpty()) 524 | { 525 | for(int n = 0; n < info.domains.count(); ++n) 526 | printf(" [%s]\n", info.domains[n].data()); 527 | } 528 | else 529 | printf(" (None)\n"); 530 | 531 | printf(" Hosts:\n"); 532 | if(!info.hosts.isEmpty()) 533 | { 534 | for(int n = 0; n < info.hosts.count(); ++n) 535 | { 536 | const QJDns::DnsHost &h = info.hosts[n]; 537 | printf(" [%s] -> %s\n", h.name.data(), qPrintable(h.address.toString())); 538 | } 539 | } 540 | else 541 | printf(" (None)\n"); 542 | 543 | QHostAddress addr; 544 | printf("Primary IPv4 Multicast Address: "); 545 | addr = QJDns::detectPrimaryMulticast(QHostAddress::Any); 546 | if(!addr.isNull()) 547 | printf("%s\n", qPrintable(addr.toString())); 548 | else 549 | printf("(None)\n"); 550 | printf("Primary IPv6 Multicast Address: "); 551 | addr = QJDns::detectPrimaryMulticast(QHostAddress::AnyIPv6); 552 | if(!addr.isNull()) 553 | printf("%s\n", qPrintable(addr.toString())); 554 | else 555 | printf("(None)\n"); 556 | 557 | return 0; 558 | } 559 | else 560 | { 561 | usage(); 562 | return 1; 563 | } 564 | 565 | App a; 566 | a.opt_debug = opt_debug; 567 | a.opt_ipv6 = opt_ipv6; 568 | a.opt_quit = opt_quit; 569 | a.quit_time = quit_time; 570 | a.mode = mode; 571 | a.type = type.toLower(); 572 | a.name = name; 573 | a.ipaddr = ipaddr; 574 | a.nslist = nslist; 575 | a.pubitems = pubitems; 576 | QObject::connect(&a, SIGNAL(quit()), &app, SLOT(quit())); 577 | QTimer::singleShot(0, &a, SLOT(start())); 578 | app.exec(); 579 | return 0; 580 | } 581 | -------------------------------------------------------------------------------- /include/jdns/qjdnsshared.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006,2007 Justin Karneges 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | /** 25 | * @file 26 | */ 27 | 28 | #ifndef QJDNSSHARED_H 29 | #define QJDNSSHARED_H 30 | 31 | #include "qjdns.h" 32 | 33 | class QJDnsShared; 34 | class QJDnsSharedPrivate; 35 | class QJDnsSharedRequestPrivate; 36 | class QJDnsSharedDebugPrivate; 37 | 38 | /** 39 | * Collects debugging information from QJDnsShared 40 | * 41 | * @note Iris users should utilize NetNames for DNS capabilities, not QJDnsSharedDebug. 42 | * See the QJDnsShared documentation for more information. 43 | * 44 | * QJDnsSharedDebug is used to collect debugging information from one or many QJDnsShared objects. 45 | * To use it, simply create it and pass it to QJDnsShared::setDebug(). 46 | * 47 | * Example use: 48 | * 49 | * @code 50 | * QJDnsSharedDebug *db = new QJDnsSharedDebug; 51 | * connect(db, SIGNAL(debugLinesReady(QStringList)), 52 | * SLOT(db_debugLinesReady(QStringList))); 53 | * 54 | * QJDnsShared *jdnsShared1 = new QJDnsShared(QJDnsShared::UnicastInternet); 55 | * jdnsShared1->setDebug(db, "U"); 56 | * 57 | * QJDnsShared *jdnsShared2 = new QJDnsShared(QJDnsShared::UnicastLocal); 58 | * jdnsShared2->setDebug(db, "L"); 59 | * ... 60 | * void db_debugLinesReady(const QStringList &lines) 61 | * { 62 | * foreach(QString line, lines) 63 | * printf("%s\n", qPrintable(line)); 64 | * } 65 | * @endcode 66 | * 67 | * QJDnsShared reports debug lines with the name and interface number prepended to each line. 68 | * For example, if there is debug information to report about the second interface added to 69 | * @a jdnsShared2 in the above example, the lines would be prepended with "L1: ". 70 | * 71 | * Do not destroy QJDnsSharedDebug until all of the QJDnsShared objects associated with it have been 72 | * destroyed. 73 | * 74 | * @sa QJDnsShared QJDnsSharedRequest 75 | */ 76 | class JDNS_EXPORT QJDnsSharedDebug : public QObject 77 | { 78 | Q_OBJECT 79 | public: 80 | /** 81 | * Constructs a new object with the given @a parent 82 | */ 83 | QJDnsSharedDebug(QObject *parent = 0); 84 | 85 | /** 86 | * Destroys the object 87 | */ 88 | ~QJDnsSharedDebug(); 89 | 90 | /** 91 | * Read the available debug information 92 | * 93 | * Debug information is reported as a series of lines. The lines are of reasonable length, and 94 | * so if you're storing a backlog of the most recent debug information, it should be safe to 95 | * make the cut-off point based on lines. 96 | * 97 | * @sa readyRead 98 | */ 99 | QStringList readDebugLines(); 100 | 101 | signals: 102 | /** 103 | * Emitted when there is debug information to report 104 | * 105 | * @sa readDebugLines 106 | */ 107 | void readyRead(); 108 | 109 | private: 110 | friend class QJDnsShared; 111 | friend class QJDnsSharedPrivate; 112 | friend class QJDnsSharedDebugPrivate; 113 | QJDnsSharedDebugPrivate *d; 114 | }; 115 | 116 | /** 117 | * Performs a DNS operation using QJDnsShared 118 | * 119 | * @note Iris users should utilize NetNames for DNS capabilities, not QJDnsSharedRequest. 120 | * See the QJDnsShared documentation for more information. 121 | * 122 | * QJDnsSharedRequest is used to perform DNS operations on a QJDnsShared object. Many requests may 123 | * be performed simultaneously, such that a single QJDnsShared object can be "shared" across the 124 | * application. Please see the QJDnsShared documentation for more complete information about how 125 | * the overall system works. 126 | * 127 | * Call query() to perform a query. Call publish() (or publishUpdate()) to make DNS records 128 | * available on the local network (QJDnsShared::Multicast mode only). When the operation has 129 | * something to report, the resultsReady() signal is emitted. Call success() to determine the 130 | * status of the operation. If success() returns false, then the operation has failed and the 131 | * reason for the failure can be determined with error(). If success() returns true, then the 132 | * meaning differs depending on the type of operation being performed: 133 | *
    134 | *
  • 135 | * For QJDnsShared::UnicastInternet and QJDnsShared::UnicastLocal modes, call results() to 136 | * obtain the records obtained by the query. In these modes, resultsReady() is only emitted 137 | * once, at which point the operation is no longer active. 138 | *
  • 139 | *
  • 140 | * For QJDnsShared::Multicast, operations are long-lived. Query operations never timeout, and 141 | * resultsReady() may be emitted multiple times. In order to stop the query, either call 142 | * cancel() or destroy the QJDnsSharedRequest object. Similarly, publishing is long-lived. 143 | * The record stays published as long as the QJDnsSharedRequest has not been cancelled or 144 | * destroyed. 145 | *
  • 146 | *
147 | * 148 | * Here is how you might look up an A record: 149 | * 150 | * @code 151 | * QJDnsSharedRequest *req = new QJDnsSharedRequest(jdnsShared); 152 | * connect(req, SIGNAL(resultsReady()), SLOT(req_resultsReady())); 153 | * req->query("psi-im.org", QJDns::A); 154 | * ... 155 | * void req_resultsReady() 156 | * { 157 | * if(req->success()) 158 | * { 159 | * // print all of the IP addresses obtained 160 | * QList results = req->results(); 161 | * foreach(QJDns::Record r, results) 162 | * { 163 | * if(r.type == QJDns::A) 164 | * printf("%s\n", qPrintable(r.address.toString())); 165 | * } 166 | * } 167 | * else 168 | * printf("Error resolving!\n"); 169 | * } 170 | * @endcode 171 | * 172 | * Here is an example of publishing a record: 173 | * 174 | * @code 175 | * QJDnsSharedRequest *pub = new QJDnsSharedRequest(jdnsShared); 176 | * connect(pub, SIGNAL(resultsReady()), SLOT(pub_resultsReady())); 177 | * 178 | * // let's publish an A record 179 | * QJDns::Record rec; 180 | * rec.owner = "SomeComputer.local."; 181 | * rec.type = QJDns::A; 182 | * rec.ttl = 120; 183 | * rec.haveKnown = true; 184 | * rec.address = QHostAddress("192.168.0.32"); 185 | * 186 | * pub->publish(QJDns::Unique, rec); 187 | * ... 188 | * void pub_resultsReady() 189 | * { 190 | * if(pub->success()) 191 | * printf("Record published\n"); 192 | * else 193 | * printf("Error publishing!\n"); 194 | * } 195 | * @endcode 196 | * 197 | * To update an existing record, use publishUpdate(): 198 | * 199 | * @code 200 | * // the IP address of the host changed, so make a new record 201 | * QJDns::Record rec; 202 | * rec.owner = "SomeComputer.local."; 203 | * rec.type = QJDns::A; 204 | * rec.ttl = 120; 205 | * rec.haveKnown = true; 206 | * rec.address = QHostAddress("192.168.0.64"); 207 | * 208 | * // update it 209 | * pub->publishUpdate(rec); 210 | * @endcode 211 | * 212 | * As a special exception, the address value can be left unspecified for A and Aaaa record types, 213 | * which tells QJDnsShared to substitute the address value with the address of whatever interfaces 214 | * the record gets published on. This is the preferred way to publish the IP address of your own 215 | * machine, and in fact it is the only way to do so if you have multiple interfaces, because there 216 | * will likely be a different IP address value for each interface (the record resolves to a 217 | * different answer depending on which interface a query comes from). 218 | * 219 | * @code 220 | * // let's publish our own A record 221 | * QJDns::Record rec; 222 | * rec.owner = "MyComputer.local."; 223 | * rec.type = QJDns::A; 224 | * rec.ttl = 120; 225 | * rec.haveKnown = true; 226 | * rec.address = QHostAddress(); 227 | * 228 | * pub->publish(QJDns::Unique, rec); 229 | * @endcode 230 | * 231 | * When you want to unpublish, call cancel() or destroy the QJDnsSharedRequest. 232 | * 233 | * @sa QJDnsShared 234 | */ 235 | class JDNS_EXPORT QJDnsSharedRequest : public QObject 236 | { 237 | Q_OBJECT 238 | public: 239 | /** 240 | * Operation type 241 | */ 242 | enum Type 243 | { 244 | Query, ///< Query operation, initiated by query() 245 | Publish ///< Publish operation, initiated by publish() or publishUpdate() 246 | }; 247 | 248 | /** 249 | * Request error 250 | */ 251 | enum Error 252 | { 253 | ErrorNoNet, /**< There are no available network interfaces to operate on. 254 | * This happens if QJDnsShared::addInterface() was not called. */ 255 | ErrorGeneric, /**< Generic error during the operation. */ 256 | ErrorNXDomain, /**< The name looked up does not exist. */ 257 | ErrorTimeout, /**< The operation timed out. */ 258 | ErrorConflict /**< Attempt to publish an already published unique record. */ 259 | }; 260 | 261 | /** 262 | * Constructs a new object with the given @a jdnsShared and @a parent 263 | */ 264 | QJDnsSharedRequest(QJDnsShared *jdnsShared, QObject *parent = 0); 265 | 266 | /** 267 | * Destroys the object 268 | * 269 | * If there is an active operation, it is cancelled. 270 | */ 271 | ~QJDnsSharedRequest(); 272 | 273 | /** 274 | * The type of operation being performed 275 | */ 276 | Type type(); 277 | 278 | /** 279 | * Perform a query operation 280 | */ 281 | void query(const QByteArray &name, int type); 282 | 283 | /** 284 | * Perform a publish operation 285 | */ 286 | void publish(QJDns::PublishMode m, const QJDns::Record &record); 287 | 288 | /** 289 | * Update a record that is currently published 290 | */ 291 | void publishUpdate(const QJDns::Record &record); 292 | 293 | /** 294 | * Cancels the current operation 295 | */ 296 | void cancel(); 297 | 298 | /** 299 | * Indicates whether or not the operation was successful 300 | */ 301 | bool success() const; 302 | 303 | /** 304 | * Returns the reason for error 305 | */ 306 | Error error() const; 307 | 308 | /** 309 | * Returns the results of the operation 310 | */ 311 | QList results() const; 312 | 313 | signals: 314 | /** 315 | * Indicates that the operation has something to report 316 | * 317 | * After receiving this signal, call success() to check on the status of the operation, followed 318 | * by results() or error() as appropriate. 319 | */ 320 | void resultsReady(); 321 | 322 | private: 323 | friend class QJDnsShared; 324 | friend class QJDnsSharedPrivate; 325 | friend class QJDnsSharedRequestPrivate; 326 | QJDnsSharedRequestPrivate *d; 327 | }; 328 | 329 | /** 330 | * Abstraction layer on top of QJDns 331 | * 332 | * @note Iris users should utilize NetNames for DNS capabilities, not QJDnsShared. 333 | * QJDnsShared is provided for non-Iris users (and it is also used internally by NetNames). To use 334 | * QJDnsShared by itself, simply drop the jdnsshared.h and jdnsshared.cpp files, along with JDNS, 335 | * into your project. It is not a full replacement for Qt's Q3Dns, as some tasks are left to you, 336 | * but it covers most of it. 337 | * 338 | * QJDns supports everything a typical application should ever need in DNS. However, it is expected 339 | * that modern applications will need to maintain multiple QJDns instances at the same time, and 340 | * this is where things can get complicated. For example, most applications will want at least two 341 | * QJDns instances: one for IPv4 unicast and one for IPv6 unicast. 342 | * 343 | * A single QJDnsShared object encapsulates multiple instances of QJDns that are related. For 344 | * example, an IPv4 unicast instance and an IPv6 unicast instance could be coupled within 345 | * QJDnsShared. Then, when a unicast operation is performed on the QJDnsShared object, both 346 | * underlying instances will be queried as appropriate. The application would not need to perform 347 | * two resolutions itself, nor deal with any related complexity. 348 | * 349 | * Further, individual operations are performed using a separate class called QJDnsSharedRequest, 350 | * eliminating the need for the application to directly interface with a central QJDns object or 351 | * track integer handles. This makes it easier for individual parts of the application to "share" 352 | * the same instance (or set of instances) of QJDns, hence the name. 353 | * 354 | * QJDnsShared is a thin abstraction. QJDns subtypes (e.g. QJDns::Type, QJDns::Record, etc) are 355 | * still used with QJDnsShared. Because of the duplication of documentation effort between NetNames 356 | * and QJDns, there is no formal documentation for QJDns. Users of QJDnsShared will need to read 357 | * qjdns.h, although a basic explanation of the elements can be found below. 358 | * 359 | * Types: 360 | * 361 | * 362 | * 365 | * 370 | * 371 | * 372 | * 375 | * 388 | * 389 | * 390 | * 398 | * 399 | *
363 | * QJDns::Type 364 | * 366 | * This is a convenience enumeration for common DNS record types. For example: A, Aaaa, Srv, 367 | * etc. The values directly map to the integer values of the DNS protocol (e.g. Srv = 33). 368 | * See qjdns.h for all of the types and values. 369 | *
373 | * QJDns::Record 374 | * 376 | * This class holds a DNS record. The main fields are type (integer type, probably 377 | * something listed in QJDns::Type), rdata (QByteArray of the record value), and 378 | * haveKnown (boolean to indicate if a decoded form of the record value is also 379 | * available). See qjdns.h for the possible known fields. You will most-likely always work 380 | * with known types. Received records that have a type listed in QJDns::Type are guaranteed 381 | * to be known and will provide a decoded value. If you are creating a record for publishing, 382 | * you will need to set owner, ttl, and type. If the type to be 383 | * published is listed in QJDns::Type, then you will need to set haveKnown to true and 384 | * set the known fields as appropriate, otherwise you need to set rdata. You do not 385 | * need to supply an encoded form in rdata for known types, it can be left empty in 386 | * that case. 387 | *
391 | * QJDns::PublishModeThis is for Multicast DNS, and can either be Unique or Shared. 392 | * A shared record can be published by multiple owners (for example, a "_ssh._tcp.local." PTR 393 | * record might resolve to many different SSH services owned by different machines). A unique 394 | * record can only have one owner (for example, a "mycomputer.local." A record would resolve 395 | * to the IP address of the machine that published it). Attempting to publish a record on a 396 | * network where a unique record is already present will result in a conflict error. 397 | *
400 | * 401 | * Functions: 402 | * 403 | * 404 | * 409 | * 410 | *
405 | * QJDns::detectPrimaryMulticast()Detects a multicast interface. Pass 406 | * QHostAddress::Any or QHostAddress::AnyIPv6, depending on which type of interface is 407 | * desired. 408 | *
411 | * 412 | * To use QJDnsShared, first create an instance of it, set it up by calling addInterface() as 413 | * necessary, and then use QJDnsSharedRequest to perform operations on it. 414 | * 415 | * Here is an example of how to create and set up a QJDnsShared object for typical DNS resolution: 416 | * 417 | * @code 418 | * // construct 419 | * QJDnsShared *dns = new QJDnsShared(QJDnsShared::UnicastInternet); 420 | * 421 | * // add IPv4 and IPv6 interfaces 422 | * dns->addInterface(QHostAddress::Any); 423 | * dns->addInterface(QHostAddress::AnyIPv6); 424 | * 425 | * // at this point, the object is ready for operation 426 | * @endcode 427 | * 428 | * Perform a resolution like this: 429 | * 430 | * @code 431 | * QJDnsSharedRequest *req = new QJDnsSharedRequest(dns); 432 | * connect(req, SIGNAL(resultsReady()), SLOT(req_resultsReady())); 433 | * req->query("psi-im.org", QJDns::A); 434 | * ... 435 | * void req_resultsReady() 436 | * { 437 | * if(req->success()) 438 | * { 439 | * // print all of the IP addresses obtained 440 | * QList results = req->results(); 441 | * foreach(QJDns::Record r, results) 442 | * { 443 | * if(r.type == QJDns::A) 444 | * printf("%s\n", qPrintable(r.address.toString()); 445 | * } 446 | * } 447 | * else 448 | * printf("Error resolving!\n"); 449 | * } 450 | * @endcode 451 | * 452 | * It is important to filter the results as shown in the above example. QJDns guarantees at least 453 | * one record in the results will be of the type queried for, but there may also be CNAME records 454 | * present (of course, if the query was for a CNAME type, then the results will only be CNAME 455 | * records). The recommended approach is to simply filter for the record types desired, as shown, 456 | * rather than single out CNAME specifically. 457 | * 458 | * When you are finished with a QJDnsShared object, it should be shut down before deleting: 459 | * 460 | * @code 461 | * connect(dns, SIGNAL(shutdownFinished()), SLOT(dns_shutdownFinished())); 462 | * dns->shutdown(); 463 | * ... 464 | * void dns_shutdownFinished() 465 | * { 466 | * delete dns; 467 | * } 468 | * @endcode 469 | * 470 | * Setting up QJDnsShared for UnicastLocal and Multicast mode is done the same way as with 471 | * UnicastInternet. 472 | * 473 | * For example, here is how Multicast mode could be set up: 474 | * 475 | * @code 476 | * // construct 477 | * QJDnsShared *dns = new QJDnsShared(QJDnsShared::Multicast); 478 | * 479 | * // add IPv4 interface 480 | * QHostAddress addr = QJDns::detectPrimaryMulticast(QHostAddress::Any); 481 | * dns->addInterface(addr); 482 | * 483 | * // at this point, the object is ready for operation 484 | * @endcode 485 | * 486 | * QJDnsShared provides a lot of functionality, but certain aspects of DNS are deemed out of its 487 | * scope. Below are the responsibilities of the user of QJDnsShared, if a more complete DNS 488 | * behavior is desired: 489 | *
    490 | *
  • 491 | * Querying for several "qualified" names. You should first query for the name as provided, and 492 | * if that fails then query for name + ".domain" (for every domain the computer is in). See 493 | * domains(). 494 | *
  • 495 | *
  • 496 | * Detecting for ".local" in the name to be queried, and using that to decide whether to query 497 | * via Multicast/UnicastLocal or UnicastInternet. 498 | *
  • 499 | *
  • 500 | * For zeroconf/Bonjour, keep in mind that QJDnsShared only provides low-level record queries. 501 | * DNS-SD and any higher layers would be your job. 502 | *
  • 503 | *
504 | * 505 | * Using a custom DNS implementation, such as QJDnsShared, has the drawback that it is difficult to 506 | * take advantage of platform-specific features (for example, an OS-wide DNS cache or LDAP 507 | * integration). An application strategy for normal DNS should probably be: 508 | *
    509 | *
  • If an A or AAAA record is desired, use a native lookup.
  • 510 | *
  • Else, if the platform has advanced DNS features already (ie, res_query), use those.
  • 511 | *
  • Else, use QJDnsShared.
  • 512 | *
513 | * 514 | * For Multicast DNS, awareness of the platform is doubly important. There should only be one 515 | * Multicast DNS "Responder" per computer, and using QJDnsShared in Multicast mode at the same time 516 | * could result in a conflict. An application strategy for Multicast DNS should be: 517 | *
    518 | *
  • If the platform has a Multicast DNS daemon installed already, use it somehow.
  • 519 | *
  • Else, use QJDnsShared.
  • 520 | *
521 | * 522 | * @sa QJDnsSharedRequest 523 | */ 524 | class JDNS_EXPORT QJDnsShared : public QObject 525 | { 526 | Q_OBJECT 527 | public: 528 | /** 529 | * The mode to operate in 530 | */ 531 | enum Mode 532 | { 533 | /** 534 | * For regular DNS resolution. In this mode, lookups are performed on all interfaces, and 535 | * the first returned result is used. 536 | */ 537 | UnicastInternet, 538 | 539 | /** 540 | * Perform regular DNS resolution using the Multicast DNS address. This is used to resolve 541 | * large and/or known Multicast DNS names without actually multicasting anything. 542 | */ 543 | UnicastLocal, 544 | 545 | /** 546 | * Multicast DNS querying and publishing. 547 | * 548 | * @note For Multicast mode, QJDnsShared supports up to one interface for each IP version 549 | * (e.g. one IPv4 interface and one IPv6 interface), and expects the default/primary 550 | * multicast interface for that IP version to be used. 551 | */ 552 | Multicast 553 | }; 554 | 555 | /** 556 | * Constructs a new object with the given @a mode and @a parent 557 | */ 558 | QJDnsShared(Mode mode, QObject *parent = 0); 559 | 560 | /** 561 | * Destroys the object 562 | */ 563 | ~QJDnsShared(); 564 | 565 | /** 566 | * Sets the debug object to report to 567 | * 568 | * If a debug object is set using this function, then QJDnsShared will send output text to it, 569 | * prefixing each line with @a name. 570 | */ 571 | void setDebug(QJDnsSharedDebug *db, const QString &name); 572 | 573 | /** 574 | * Adds an interface to operate on 575 | * 576 | * For UnicastInternet and UnicastLocal, these will almost always be QHostAddress::Any or 577 | * QHostAddress::AnyIPv6 (operate on the default interface for IPv4 or IPv6, respectively). 578 | * 579 | * For Multicast, it is expected that the default/primary multicast interface will be used here. 580 | * Do not pass QHostAddress::Any (or AnyIPv6) with Multicast mode. 581 | * 582 | * Returns true if the interface was successfully added, otherwise returns false. 583 | */ 584 | bool addInterface(const QHostAddress &addr); 585 | 586 | /** 587 | * Removes a previously-added interface 588 | */ 589 | void removeInterface(const QHostAddress &addr); 590 | 591 | /** 592 | * Shuts down the object 593 | * 594 | * This operation primarily exists for Multicast mode, so that any published records have a 595 | * chance to be unpublished. If the QJDnsShared object is simply deleted without performing a 596 | * shutdown, then published records will linger on the network until their TTLs expire. 597 | * 598 | * When shutdown is complete, the shutdownFinished() signal will be emitted. 599 | */ 600 | void shutdown(); 601 | 602 | /** 603 | * The domains to search in 604 | * 605 | * You should perform a separate resolution for every domain configured on this machine. 606 | */ 607 | static QList domains(); 608 | 609 | /** 610 | * Performs a blocking shutdown of many QJDnsShared instances 611 | * 612 | * This function is a convenient way to shutdown multiple QJDnsShared instances synchronously. 613 | * The internal shutdown procedure uses no more than a few cycles of the eventloop, so it should 614 | * be safe to call without worry of the application being overly stalled. This function takes 615 | * ownership of the instances passed to it, and will delete them upon completion. 616 | * 617 | * It is worth noting that this function is implemented without the use of a nested eventloop. 618 | * All of the QJDnsShared instances are moved into a temporary thread to perform the shutdown 619 | * procedure, which should not cause any unexpected behavior in the current thread. 620 | * 621 | * @code 622 | * QList list; 623 | * list += jdnsShared_unicast; 624 | * list += jdnsShared_multicast; 625 | * QJDnsShared::waitForShutdown(list); 626 | * 627 | * // collect remaining debug information 628 | * QStringList finalDebugLines = jdnsSharedDebug.readDebugLines(); 629 | * @endcode 630 | */ 631 | static void waitForShutdown(const QList &instances); 632 | 633 | signals: 634 | /** 635 | * Indicates the object has been shut down 636 | */ 637 | void shutdownFinished(); 638 | 639 | private: 640 | friend class QJDnsSharedRequest; 641 | friend class QJDnsSharedPrivate; 642 | QJDnsSharedPrivate *d; 643 | }; 644 | 645 | #endif 646 | -------------------------------------------------------------------------------- /src/jdns/jdns_sys.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2008 Justin Karneges 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | /* 25 | this code probes the system for dns settings. blah. 26 | 27 | q3dns strategies 28 | ---------------- 29 | 30 | windows: 31 | 32 | domain name, name server, "search list" found in windows registry here: 33 | 34 | HKEY_LOCAL_MACHINE 35 | System\CurrentControlSet\Services\Tcpip\Parameters <-- win nt+ 36 | System\CurrentControlSet\Services\VxD\MSTCP <-- win 98 37 | 38 | for domain, try DhcpDomain else Domain 39 | for name servers, try DhcpNameServer, else NameServer 40 | for search list, try SearchList 41 | 42 | iphlpapi.dll : GetNetworkParams(PFIXED_INFO, PULONG); 43 | 44 | info->DomainName 45 | info->DnsServerList (if not null, use it, and loop through ->Next until 46 | null) 47 | no search list 48 | 49 | first try getnetworkparams. if that fails, try the nt regkey then the 98 50 | regkey. it seems that search list can only come from the registry, so 51 | maybe we need to grab that entry even if getnetworkparams works. 52 | 53 | in the case of the registry, the nameserver and searchlist entries are 54 | delimited by spaces on win nt and commas on win 98. probably a good 55 | idea to simplify white space first (chop away space at start and end, 56 | reduce all sections of spaces to one char). also, lowercase the search 57 | list. 58 | 59 | qt doesn't read the hosts file on windows. this might be a good idea, but 60 | probably not worth it. 61 | 62 | unix: 63 | 64 | read /etc/resolv.conf manually: 65 | for each line, split at spaces 66 | if the first item is "nameserver", then there should be an IP address 67 | following it. note: may contain mixed ipv4 and ipv6 addresses 68 | if the first item is "search", all other items are added to the domain 69 | list 70 | if the first item is "domain", then the next item should be added to the 71 | domain list. 72 | do case-insensitive matching for the item types 73 | for search/domain, the items are in the 8-bit system locale 74 | 75 | info can also be fetched using system calls. we use the res_* stuff here. 76 | first we should detect for a "modern res api". this is available from 77 | glibc 2.3 and onward. use the following scheme to check for it: 78 | 79 | #if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) 80 | && (__GLIBC_MINOR__ >= 3))) 81 | // modern res api 82 | #endif 83 | 84 | on mac we should look up res_init in the system library. see: 85 | qt_mac_resolve_sys(RTLD_NEXT, "res_init"); for a hint. 86 | otherwise we can just use res_init() straight. 87 | 88 | under a modern res api, we do: 89 | struct __res_state res; 90 | res_ninit(&res); 91 | otherwise, we simply call res_init(). for the modern api, we use the "res" 92 | struct that we made. otherwise, we use the global "_res" struct. 93 | 94 | read the res struct to obtain the name servers, search list, and domain. 95 | lowercase the search list and domain. 96 | 97 | qt tries the file, and if that fails it tries the syscalls. we may want to 98 | do the syscalls first, or even just do both all the time. 99 | 100 | read /etc/hosts manually: 101 | for each line 102 | if there is a '#' character in the line, remove it and everything to 103 | the right 104 | simplify white space 105 | convert to lowercase 106 | split the line at spaces 107 | first item is the ip address 108 | all remaining items are hostnames 109 | 110 | note: these hosts could also be used for reverse-dns too 111 | note2: Windows has a hosts file as well (like C:\WINDOWS\hosts) 112 | */ 113 | 114 | #include "jdns_p.h" 115 | 116 | #ifdef JDNS_OS_WIN 117 | # include 118 | #endif 119 | 120 | #ifdef JDNS_OS_UNIX 121 | # include 122 | # include 123 | # include 124 | # include 125 | #endif 126 | 127 | // Android uses clipped resolv.h we must provide own stuff 128 | #ifdef ANDROID 129 | #include 130 | 131 | #undef _res 132 | #define MAXNS 3 133 | struct __res_state 134 | { 135 | int nscount; /* number of name servers */ 136 | struct sockaddr_in nsaddr_list[MAXNS]; /* address of name server */ 137 | char defdname[256]; /* default domain (deprecated) */ 138 | union 139 | { 140 | struct 141 | { 142 | u_int16_t nscount; 143 | struct sockaddr_in6 *nsaddrs[MAXNS]; 144 | } _ext; 145 | } _u; 146 | } _res; 147 | #endif 148 | 149 | #define string_indexOf jdns_string_indexOf 150 | #define string_split jdns_string_split 151 | 152 | static int char_isspace(unsigned char c) 153 | { 154 | if(c == ' ' || c == '\t' || c == '\n' || c == '\r') 155 | return 1; 156 | return 0; 157 | } 158 | 159 | static unsigned char *string_getnextword(unsigned char *in, int size, int pos, int *newpos) 160 | { 161 | int n; 162 | int at; 163 | int len; 164 | unsigned char *out; 165 | 166 | at = pos; 167 | 168 | // skip any space at the start 169 | while(at < size && char_isspace(in[at])) 170 | ++at; 171 | 172 | // all space? no word then 173 | if(at >= size) 174 | return 0; 175 | 176 | // skip until a space or end 177 | n = at; 178 | while(n < size && !char_isspace(in[n])) 179 | ++n; 180 | len = n - at; 181 | 182 | // allocate length + zero byte 183 | out = (unsigned char *)jdns_alloc(len + 1); 184 | if(!out) 185 | return 0; 186 | memcpy(out, in + at, len); 187 | out[len] = 0; 188 | *newpos = at + len; 189 | return out; 190 | } 191 | 192 | static jdns_string_t *string_simplify(const jdns_string_t *in) 193 | { 194 | int n; 195 | int pos; 196 | int total; 197 | unsigned char *out; 198 | int outlen; 199 | jdns_string_t *outstr; 200 | jdns_stringlist_t *wordlist; 201 | 202 | // gather words and total of lengths 203 | pos = 0; 204 | total = 0; 205 | wordlist = jdns_stringlist_new(); 206 | while(1) 207 | { 208 | jdns_string_t *word; 209 | unsigned char *str = string_getnextword(in->data, in->size, pos, &pos); 210 | if(!str) 211 | break; 212 | word = jdns_string_new(); 213 | jdns_string_set_cstr(word, (char *)str); 214 | jdns_free(str); 215 | jdns_stringlist_append(wordlist, word); 216 | total += word->size; 217 | jdns_string_delete(word); 218 | } 219 | 220 | if(total == 0) 221 | { 222 | jdns_stringlist_delete(wordlist); 223 | 224 | outstr = jdns_string_new(); 225 | jdns_string_set_cstr(outstr, ""); 226 | return outstr; 227 | } 228 | 229 | // we need to allocate space for total lengths and wordcount-1 spaces 230 | outlen = total + (wordlist->count - 1); 231 | out = (unsigned char *)jdns_alloc(outlen); 232 | 233 | // lay out the words 234 | pos = 0; 235 | for(n = 0; n < wordlist->count; ++n) 236 | { 237 | unsigned char *data = wordlist->item[n]->data; 238 | int size = wordlist->item[n]->size; 239 | memcpy(out + pos, data, size); 240 | pos += size; 241 | 242 | // if this is not the last word, append a space 243 | if(n + 1 < wordlist->count) 244 | out[pos++] = ' '; 245 | } 246 | jdns_stringlist_delete(wordlist); 247 | 248 | outstr = jdns_string_new(); 249 | jdns_string_set(outstr, out, outlen); 250 | jdns_free(out); 251 | return outstr; 252 | } 253 | 254 | static jdns_string_t *string_tolower(const jdns_string_t *in) 255 | { 256 | int n; 257 | jdns_string_t *out = jdns_string_copy(in); 258 | for(n = 0; n < out->size; ++n) 259 | out->data[n] = tolower(out->data[n]); 260 | return out; 261 | } 262 | 263 | static jdns_string_t *file_nextline(FILE *f) 264 | { 265 | int at, size; 266 | unsigned char *buf; 267 | jdns_string_t *str; 268 | 269 | size = 1023; 270 | buf = (unsigned char *)jdns_alloc(size); 271 | at = 0; 272 | while(1) 273 | { 274 | unsigned char c = fgetc(f); 275 | if(feof(f)) 276 | { 277 | if(at > 0) 278 | { 279 | // if we read at least one char, take it as a 280 | // line 281 | break; 282 | } 283 | else 284 | { 285 | jdns_free(buf); 286 | return 0; 287 | } 288 | } 289 | if(c == '\n') 290 | break; 291 | if(c == '\r') 292 | continue; 293 | if(at < 1023) 294 | buf[at++] = c; 295 | } 296 | 297 | str = jdns_string_new(); 298 | jdns_string_set(str, buf, at); 299 | jdns_free(buf); 300 | return str; 301 | } 302 | 303 | static jdns_dnshostlist_t *read_hosts_file(const char *path) 304 | { 305 | jdns_dnshostlist_t *out; 306 | FILE *f; 307 | 308 | out = jdns_dnshostlist_new(); 309 | 310 | f = jdns_fopen(path, "r"); 311 | if(!f) 312 | return out; 313 | while(1) 314 | { 315 | jdns_string_t *line, *simp; 316 | jdns_stringlist_t *parts; 317 | jdns_address_t *addr; 318 | int n; 319 | 320 | line = file_nextline(f); 321 | if(!line) 322 | break; 323 | 324 | // truncate at comment 325 | n = string_indexOf(line, '#', 0); 326 | if(n != -1) 327 | { 328 | line->size = n; 329 | line->data[n] = 0; 330 | } 331 | 332 | simp = string_simplify(line); 333 | jdns_string_delete(line); 334 | 335 | parts = string_split(simp, ' '); 336 | jdns_string_delete(simp); 337 | 338 | if(parts->count < 2) 339 | { 340 | jdns_stringlist_delete(parts); 341 | continue; 342 | } 343 | 344 | addr = jdns_address_new(); 345 | if(!jdns_address_set_cstr(addr, (const char *)parts->item[0]->data)) 346 | { 347 | jdns_address_delete(addr); 348 | jdns_stringlist_delete(parts); 349 | continue; 350 | } 351 | 352 | for(n = 1; n < parts->count; ++n) 353 | { 354 | jdns_dnshost_t *h = jdns_dnshost_new(); 355 | h->name = jdns_string_copy(parts->item[n]); 356 | h->address = jdns_address_copy(addr); 357 | jdns_dnshostlist_append(out, h); 358 | jdns_dnshost_delete(h); 359 | } 360 | 361 | jdns_address_delete(addr); 362 | jdns_stringlist_delete(parts); 363 | } 364 | fclose(f); 365 | return out; 366 | } 367 | 368 | static void apply_hosts_file(jdns_dnsparams_t *a, const char *path) 369 | { 370 | int n; 371 | jdns_dnshostlist_t *list; 372 | 373 | list = read_hosts_file(path); 374 | for(n = 0; n < list->count; ++n) 375 | jdns_dnshostlist_append(a->hosts, list->item[n]); 376 | jdns_dnshostlist_delete(list); 377 | } 378 | 379 | static int dnsparams_have_domain(const jdns_dnsparams_t *a, const jdns_string_t *domain) 380 | { 381 | int n; 382 | for(n = 0; n < a->domains->count; ++n) 383 | { 384 | jdns_string_t *str = a->domains->item[n]; 385 | if(strcmp((const char *)str->data, (const char *)domain->data) == 0) 386 | return 1; 387 | } 388 | return 0; 389 | } 390 | 391 | #ifdef JDNS_OS_WIN 392 | 393 | // from Microsoft IPTypes.h 394 | #ifndef IP_TYPES_INCLUDED 395 | #define MAX_HOSTNAME_LEN 128 396 | #define MAX_DOMAIN_NAME_LEN 128 397 | #define MAX_SCOPE_ID_LEN 256 398 | typedef struct { 399 | char String[4 * 4]; 400 | } IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING; 401 | typedef struct _IP_ADDR_STRING { 402 | struct _IP_ADDR_STRING* Next; 403 | IP_ADDRESS_STRING IpAddress; 404 | IP_MASK_STRING IpMask; 405 | DWORD Context; 406 | } IP_ADDR_STRING, *PIP_ADDR_STRING; 407 | typedef struct { 408 | char HostName[MAX_HOSTNAME_LEN + 4] ; 409 | char DomainName[MAX_DOMAIN_NAME_LEN + 4]; 410 | PIP_ADDR_STRING CurrentDnsServer; 411 | IP_ADDR_STRING DnsServerList; 412 | UINT NodeType; 413 | char ScopeId[MAX_SCOPE_ID_LEN + 4]; 414 | UINT EnableRouting; 415 | UINT EnableProxy; 416 | UINT EnableDns; 417 | } FIXED_INFO, *PFIXED_INFO; 418 | #endif 419 | 420 | typedef DWORD (WINAPI *GetNetworkParamsFunc)(PFIXED_INFO, PULONG); 421 | 422 | static jdns_string_t *reg_readString(HKEY hk, const char *subkey) 423 | { 424 | char *buf; 425 | DWORD bufsize; 426 | int ret; 427 | jdns_string_t *str = 0; 428 | 429 | bufsize = 1024; 430 | buf = (char *)jdns_alloc((int)bufsize); 431 | if(!buf) 432 | return 0; 433 | ret = RegQueryValueExA(hk, subkey, 0, 0, (LPBYTE)buf, &bufsize); 434 | if(ret == ERROR_MORE_DATA) 435 | { 436 | buf = (char *)jdns_realloc(buf, bufsize); 437 | if(!buf) 438 | { 439 | jdns_free(buf); 440 | return 0; 441 | } 442 | ret = RegQueryValueExA(hk, subkey, 0, 0, (LPBYTE)buf, &bufsize); 443 | } 444 | if(ret == ERROR_SUCCESS) 445 | { 446 | str = jdns_string_new(); 447 | jdns_string_set_cstr(str, (char *)buf); 448 | } 449 | jdns_free(buf); 450 | return str; 451 | } 452 | 453 | static jdns_dnsparams_t *dnsparams_get_winreg() 454 | { 455 | int n; 456 | jdns_dnsparams_t *params; 457 | HKEY key; 458 | int ret; 459 | char sep; 460 | jdns_string_t *str_domain, *str_nameserver, *str_searchlist; 461 | jdns_stringlist_t *list_nameserver, *list_searchlist; 462 | 463 | sep = ' '; 464 | ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, 465 | "System\\CurrentControlSet\\Services\\Tcpip\\Parameters", 466 | 0, KEY_READ, &key); 467 | if(ret != ERROR_SUCCESS) 468 | { 469 | sep = ','; 470 | ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, 471 | "System\\CurrentControlSet\\Services\\VxD\\MSTCP", 472 | 0, KEY_READ, &key); 473 | if(ret != ERROR_SUCCESS) 474 | return 0; 475 | } 476 | 477 | str_domain = reg_readString(key, "DhcpDomain"); 478 | if(!str_domain) 479 | str_domain = reg_readString(key, "Domain"); 480 | str_nameserver = reg_readString(key, "DhcpNameServer"); 481 | if(!str_nameserver) 482 | str_nameserver = reg_readString(key, "NameServer"); 483 | str_searchlist = reg_readString(key, "SearchList"); 484 | 485 | RegCloseKey(key); 486 | 487 | list_nameserver = 0; 488 | if(str_nameserver) 489 | { 490 | list_nameserver = string_split(str_nameserver, sep); 491 | jdns_string_delete(str_nameserver); 492 | } 493 | list_searchlist = 0; 494 | if(str_searchlist) 495 | { 496 | // lowercase the string 497 | jdns_string_t *p = string_tolower(str_searchlist); 498 | jdns_string_delete(str_searchlist); 499 | str_searchlist = p; 500 | 501 | list_searchlist = string_split(str_searchlist, sep); 502 | jdns_string_delete(str_searchlist); 503 | } 504 | 505 | params = jdns_dnsparams_new(); 506 | if(list_nameserver) 507 | { 508 | // qt seems to do a strange thing here by running each name 509 | // server address through the q3dns setLabel function, and 510 | // then pulls the result as a list of addresses. i have 511 | // no idea why they do this, or how one IP address would 512 | // turn into anything else, let alone several addresses. 513 | // so, uh, we're not going to do that. 514 | for(n = 0; n < list_nameserver->count; ++n) 515 | { 516 | jdns_address_t *addr = jdns_address_new(); 517 | if(jdns_address_set_cstr(addr, (char *)list_nameserver->item[n]->data)) 518 | jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT); 519 | jdns_address_delete(addr); 520 | } 521 | jdns_stringlist_delete(list_nameserver); 522 | } 523 | if(str_domain) 524 | { 525 | if(str_domain->size > 0) 526 | jdns_dnsparams_append_domain(params, str_domain); 527 | jdns_string_delete(str_domain); 528 | } 529 | if(list_searchlist) 530 | { 531 | for(n = 0; n < list_searchlist->count; ++n) 532 | { 533 | if(list_searchlist->item[n]->size > 0) 534 | jdns_dnsparams_append_domain(params, list_searchlist->item[n]); 535 | } 536 | jdns_stringlist_delete(list_searchlist); 537 | } 538 | 539 | return params; 540 | } 541 | 542 | static jdns_dnsparams_t *dnsparams_get_winsys() 543 | { 544 | jdns_dnsparams_t *params; 545 | GetNetworkParamsFunc myGetNetworkParams; 546 | DWORD ret; 547 | HINSTANCE lib; 548 | jdns_address_t *addr; 549 | jdns_string_t *str; 550 | IP_ADDR_STRING *ipstr; 551 | 552 | lib = LoadLibraryA("iphlpapi"); 553 | if(!lib) 554 | return 0; 555 | 556 | params = 0; 557 | myGetNetworkParams = (GetNetworkParamsFunc)GetProcAddress(lib, "GetNetworkParams"); 558 | if(myGetNetworkParams) 559 | { 560 | ULONG bufsize = 0; 561 | ret = myGetNetworkParams(0, &bufsize); 562 | if(ret == ERROR_BUFFER_OVERFLOW) 563 | { 564 | FIXED_INFO *info = (FIXED_INFO *)jdns_alloc((int)bufsize); 565 | ret = myGetNetworkParams(info, &bufsize); 566 | if(ret == ERROR_SUCCESS) 567 | { 568 | params = jdns_dnsparams_new(); 569 | ipstr = &info->DnsServerList; 570 | while(ipstr) 571 | { 572 | addr = jdns_address_new(); 573 | if(jdns_address_set_cstr(addr, (char *)ipstr->IpAddress.String)) 574 | jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT); 575 | jdns_address_delete(addr); 576 | ipstr = ipstr->Next; 577 | } 578 | str = jdns_string_new(); 579 | jdns_string_set_cstr(str, info->DomainName); 580 | if(str->size > 0) 581 | jdns_dnsparams_append_domain(params, str); 582 | jdns_string_delete(str); 583 | } 584 | jdns_free(info); 585 | } 586 | } 587 | FreeLibrary(lib); 588 | return params; 589 | } 590 | 591 | static void apply_hosts_var_filepath(jdns_dnsparams_t *a, const char *envvar, const char *path) 592 | { 593 | jdns_string_t *e; 594 | char *str; 595 | int elen, plen; 596 | 597 | e = jdns_getenv(envvar); 598 | if(!e) 599 | return; 600 | elen = strlen((char *)e->data); 601 | plen = strlen(path); 602 | str = (char *)jdns_alloc(elen + plen + 1); 603 | memcpy(str, e->data, elen); 604 | jdns_string_delete(e); 605 | 606 | jdns_strcpy(str + elen, path); 607 | apply_hosts_file(a, str); 608 | jdns_free(str); 609 | } 610 | 611 | static void apply_win_hosts_file(jdns_dnsparams_t *a) 612 | { 613 | // windows 64-bit 614 | apply_hosts_var_filepath(a, "SystemRoot", "\\SysWOW64\\drivers\\etc\\hosts"); 615 | 616 | // winnt+ 617 | apply_hosts_var_filepath(a, "SystemRoot", "\\system32\\drivers\\etc\\hosts"); 618 | 619 | // win9x 620 | apply_hosts_var_filepath(a, "WINDIR", "\\hosts"); 621 | } 622 | 623 | static jdns_dnsparams_t *dnsparams_get_win() 624 | { 625 | int n; 626 | jdns_dnsparams_t *sys_params, *reg_params; 627 | 628 | reg_params = dnsparams_get_winreg(); 629 | sys_params = dnsparams_get_winsys(); 630 | 631 | // no sys params? take the reg params then 632 | if(!sys_params) 633 | { 634 | apply_win_hosts_file(reg_params); 635 | return reg_params; 636 | } 637 | 638 | // sys params don't have a search list, so merge the domains from 639 | // the registry if possible 640 | if(reg_params) 641 | { 642 | for(n = 0; n < reg_params->domains->count; ++n) 643 | { 644 | jdns_string_t *reg_str = reg_params->domains->item[n]; 645 | 646 | // don't add dups 647 | if(!dnsparams_have_domain(sys_params, reg_str)) 648 | jdns_dnsparams_append_domain(sys_params, reg_str); 649 | } 650 | jdns_dnsparams_delete(reg_params); 651 | } 652 | apply_win_hosts_file(sys_params); 653 | return sys_params; 654 | } 655 | 656 | #endif 657 | 658 | #ifdef JDNS_OS_UNIX 659 | 660 | static jdns_dnsparams_t *dnsparams_get_unixfiles() 661 | { 662 | FILE *f; 663 | jdns_dnsparams_t *params; 664 | 665 | params = jdns_dnsparams_new(); 666 | 667 | f = jdns_fopen("/etc/resolv.conf", "r"); 668 | if(!f) 669 | return params; 670 | while(1) 671 | { 672 | int n; 673 | jdns_string_t *line, *simp; 674 | jdns_stringlist_t *parts; 675 | 676 | line = file_nextline(f); 677 | if(!line) 678 | break; 679 | 680 | // truncate at comment 681 | n = string_indexOf(line, '#', 0); 682 | if(n != -1) 683 | { 684 | line->size = n; 685 | line->data[n] = 0; 686 | } 687 | 688 | simp = string_simplify(line); 689 | jdns_string_delete(line); 690 | 691 | parts = string_split(simp, ' '); 692 | jdns_string_delete(simp); 693 | 694 | if(parts->count < 2) 695 | { 696 | jdns_stringlist_delete(parts); 697 | continue; 698 | } 699 | 700 | simp = string_tolower(parts->item[0]); 701 | if(strcmp((char *)simp->data, "nameserver") == 0) 702 | { 703 | jdns_address_t *addr = jdns_address_new(); 704 | jdns_address_set_cstr(addr, (const char *)parts->item[1]->data); 705 | jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT); 706 | jdns_address_delete(addr); 707 | } 708 | else if(strcmp((char *)simp->data, "search") == 0) 709 | { 710 | for(n = 1; n < parts->count; ++n) 711 | { 712 | jdns_dnsparams_append_domain(params, parts->item[n]); 713 | } 714 | } 715 | else if(strcmp((char *)simp->data, "domain") == 0) 716 | { 717 | jdns_dnsparams_append_domain(params, parts->item[1]); 718 | } 719 | jdns_string_delete(simp); 720 | 721 | jdns_stringlist_delete(parts); 722 | } 723 | fclose(f); 724 | return params; 725 | } 726 | 727 | #if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3))) 728 | # define JDNS_MODERN_RES_API 729 | #endif 730 | 731 | #ifndef JDNS_MODERN_RES_API 732 | typedef int (*res_init_func)(); 733 | static int my_res_init() 734 | { 735 | #ifdef JDNS_OS_MAC 736 | res_init_func mac_res_init; 737 | 738 | // look up res_init in the system library (qt does this, not sure why) 739 | mac_res_init = (res_init_func)dlsym(RTLD_NEXT, "res_init"); 740 | if(!mac_res_init) 741 | return -1; 742 | return mac_res_init(); 743 | #elif defined ANDROID 744 | memset(&_res, 0, sizeof(_res)); 745 | char prop_value[PROP_VALUE_MAX]; 746 | char prop_name[PROP_NAME_MAX]; 747 | int i = 0; 748 | int readed = 0; 749 | do 750 | { 751 | // net.dns properties starts with 1 752 | sprintf(prop_name, "net.dns%d", i + 1); 753 | readed = __system_property_get(prop_name, prop_value); 754 | if (readed) 755 | { 756 | if (!inet_aton(prop_value, &_res.nsaddr_list[i].sin_addr)) 757 | break; 758 | i++; 759 | } 760 | } while (readed && i < MAXNS); 761 | _res.nscount = i; 762 | 763 | return 0; 764 | #else 765 | return res_init(); 766 | #endif 767 | } 768 | #endif 769 | 770 | // on some platforms, __res_state_ext exists as a struct but it is not 771 | // a define, so the #ifdef doesn't work. as a workaround, we'll explicitly 772 | // specify the platforms that have __res_state_ext 773 | //#ifdef __res_state_ext 774 | #if defined(JDNS_OS_MAC) || defined(JDNS_OS_FREEBSD) || \ 775 | defined(JDNS_OS_NETBSD) || defined (JDNS_OS_SOLARIS) 776 | # define USE_EXTEXT 777 | #endif 778 | #if defined(JDNS_OS_OPENBSD) 779 | # define USE_INDEP_EXT 780 | 781 | #endif 782 | 783 | static jdns_dnsparams_t *dnsparams_get_unixsys() 784 | { 785 | int n; 786 | jdns_dnsparams_t *params; 787 | 788 | #ifdef JDNS_MODERN_RES_API 789 | struct __res_state res; 790 | memset(&res, 0, sizeof(struct __res_state)); 791 | n = res_ninit(&res); 792 | #define RESVAR res 793 | #else 794 | n = my_res_init(); 795 | #define RESVAR _res 796 | #endif 797 | 798 | params = jdns_dnsparams_new(); 799 | 800 | // error initializing? 801 | if(n == -1) 802 | return params; 803 | 804 | #ifdef USE_INDEP_EXT 805 | for(n = 0; n < MAXNS && n < RESVAR.nscount; ++n) 806 | { 807 | struct sockaddr_in *sa = (struct sockaddr_in*)&(_res_ext.nsaddr_list[n]); 808 | jdns_address_t *addr = jdns_address_new(); 809 | if (sa->sin_family = AF_INET6) { 810 | struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; 811 | jdns_address_set_ipv6(addr, sa6->sin6_addr.s6_addr); 812 | } else { 813 | jdns_address_set_ipv4(addr, ntohl(sa->sin_addr.s_addr)); 814 | } 815 | jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT); 816 | jdns_address_delete(addr); 817 | } 818 | #else 819 | // nameservers - ipv6 820 | #ifdef __GLIBC__ 821 | for(n = 0; n < MAXNS; ++n) 822 | #else 823 | for(n = 0; n < MAXNS && n < RESVAR._u._ext.nscount; ++n) 824 | #endif 825 | { 826 | jdns_address_t *addr; 827 | struct sockaddr_in6 *sa6; 828 | 829 | #ifdef USE_EXTEXT 830 | // seems _ext.ext can be null in some cases... 831 | if(RESVAR._u._ext.ext == NULL) 832 | break; 833 | 834 | sa6 = ((struct sockaddr_in6 *)RESVAR._u._ext.ext) + n; 835 | #else 836 | sa6 = RESVAR._u._ext.nsaddrs[n]; 837 | #endif 838 | 839 | if(sa6 == NULL) 840 | continue; 841 | addr = jdns_address_new(); 842 | jdns_address_set_ipv6(addr, sa6->sin6_addr.s6_addr); 843 | jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT); 844 | jdns_address_delete(addr); 845 | } 846 | 847 | // nameservers - ipv4 848 | #ifdef __GLIBC__ 849 | int ns4count = RESVAR.nscount - RESVAR._u._ext.nscount6; 850 | for(n = 0; n < MAXNS && n < ns4count; ++n) 851 | #else 852 | for(n = 0; n < MAXNS && n < RESVAR.nscount; ++n) 853 | #endif 854 | { 855 | jdns_address_t *addr = jdns_address_new(); 856 | jdns_address_set_ipv4(addr, ntohl(RESVAR.nsaddr_list[n].sin_addr.s_addr)); 857 | jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT); 858 | jdns_address_delete(addr); 859 | } 860 | #endif 861 | 862 | // domain name 863 | if(strlen(RESVAR.defdname) > 0) 864 | { 865 | jdns_string_t *str; 866 | jdns_string_t *p; 867 | str = jdns_string_new(); 868 | jdns_string_set_cstr(str, RESVAR.defdname); 869 | p = string_tolower(str); 870 | jdns_string_delete(str); 871 | str = p; 872 | jdns_dnsparams_append_domain(params, str); 873 | jdns_string_delete(str); 874 | } 875 | 876 | // search list 877 | #ifdef MAXDFLSRCH 878 | for(n = 0; n < MAXDFLSRCH && RESVAR.dnsrch[n]; ++n) 879 | { 880 | if(strlen(RESVAR.dnsrch[n]) > 0) 881 | { 882 | jdns_string_t *str; 883 | jdns_string_t *p; 884 | str = jdns_string_new(); 885 | jdns_string_set_cstr(str, RESVAR.dnsrch[n]); 886 | p = string_tolower(str); 887 | jdns_string_delete(str); 888 | str = p; 889 | 890 | // don't add dups 891 | if(!dnsparams_have_domain(params, str)) 892 | jdns_dnsparams_append_domain(params, str); 893 | 894 | jdns_string_delete(str); 895 | } 896 | } 897 | #endif 898 | 899 | return params; 900 | } 901 | 902 | static jdns_dnsparams_t *dnsparams_get_unix() 903 | { 904 | jdns_dnsparams_t *params; 905 | 906 | // prefer system calls over files 907 | params = dnsparams_get_unixsys(); 908 | if(params->nameservers->count == 0) 909 | { 910 | jdns_dnsparams_delete(params); 911 | params = dnsparams_get_unixfiles(); 912 | } 913 | 914 | apply_hosts_file(params, "/etc/hosts"); 915 | 916 | return params; 917 | } 918 | 919 | #endif 920 | 921 | jdns_dnsparams_t *jdns_system_dnsparams() 922 | { 923 | #ifdef JDNS_OS_WIN 924 | return dnsparams_get_win(); 925 | #else 926 | return dnsparams_get_unix(); 927 | #endif 928 | } 929 | -------------------------------------------------------------------------------- /src/qjdns/qjdns.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2008 Justin Karneges 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include "qjdns_p.h" 25 | 26 | #include 27 | #include "qjdns_sock.h" 28 | 29 | // for fprintf 30 | #include 31 | 32 | // safeobj stuff, from qca 33 | 34 | static void releaseAndDeleteLater(QObject *owner, QObject *obj) 35 | { 36 | obj->disconnect(owner); 37 | obj->setParent(0); 38 | obj->deleteLater(); 39 | } 40 | 41 | SafeTimer::SafeTimer(QObject *parent) 42 | : QObject(parent) 43 | { 44 | t = new QTimer(this); 45 | connect(t, SIGNAL(timeout()), SIGNAL(timeout())); 46 | } 47 | 48 | SafeTimer::~SafeTimer() 49 | { 50 | releaseAndDeleteLater(this, t); 51 | } 52 | 53 | int SafeTimer::interval() const 54 | { 55 | return t->interval(); 56 | } 57 | 58 | bool SafeTimer::isActive() const 59 | { 60 | return t->isActive(); 61 | } 62 | 63 | bool SafeTimer::isSingleShot() const 64 | { 65 | return t->isSingleShot(); 66 | } 67 | 68 | void SafeTimer::setInterval(int msec) 69 | { 70 | t->setInterval(msec); 71 | } 72 | 73 | void SafeTimer::setSingleShot(bool singleShot) 74 | { 75 | t->setSingleShot(singleShot); 76 | } 77 | 78 | int SafeTimer::timerId() const 79 | { 80 | return t->timerId(); 81 | } 82 | 83 | void SafeTimer::start(int msec) 84 | { 85 | t->start(msec); 86 | } 87 | 88 | void SafeTimer::start() 89 | 90 | { 91 | t->start(); 92 | } 93 | 94 | void SafeTimer::stop() 95 | { 96 | t->stop(); 97 | } 98 | 99 | static jdns_string_t *qt2str(const QByteArray &in) 100 | { 101 | jdns_string_t *out = jdns_string_new(); 102 | jdns_string_set(out, (const unsigned char *)in.data(), in.size()); 103 | return out; 104 | } 105 | 106 | static QByteArray str2qt(const jdns_string_t *in) 107 | { 108 | return QByteArray((const char *)in->data, in->size); 109 | } 110 | 111 | static void qt2addr_set(jdns_address_t *addr, const QHostAddress &host) 112 | { 113 | if(host.protocol() == QAbstractSocket::IPv6Protocol) 114 | jdns_address_set_ipv6(addr, host.toIPv6Address().c); 115 | else 116 | jdns_address_set_ipv4(addr, host.toIPv4Address()); 117 | } 118 | 119 | static jdns_address_t *qt2addr(const QHostAddress &host) 120 | { 121 | jdns_address_t *addr = jdns_address_new(); 122 | qt2addr_set(addr, host); 123 | return addr; 124 | } 125 | 126 | static QHostAddress addr2qt(const jdns_address_t *addr) 127 | { 128 | if(addr->isIpv6) 129 | return QHostAddress(addr->addr.v6); 130 | else 131 | return QHostAddress(addr->addr.v4); 132 | } 133 | 134 | static QJDns::Record import_record(const jdns_rr_t *in) 135 | { 136 | QJDns::Record out; 137 | 138 | out.owner = QByteArray((const char *)in->owner); 139 | out.ttl = in->ttl; 140 | out.type = in->type; 141 | out.rdata = QByteArray((const char *)in->rdata, in->rdlength); 142 | 143 | // known 144 | if(in->haveKnown) 145 | { 146 | int type = in->type; 147 | 148 | if(type == QJDns::A || type == QJDns::Aaaa) 149 | { 150 | out.haveKnown = true; 151 | out.address = addr2qt(in->data.address); 152 | } 153 | else if(type == QJDns::Mx) 154 | { 155 | out.haveKnown = true; 156 | out.name = QByteArray((const char *)in->data.server->name); 157 | out.priority = in->data.server->priority; 158 | } 159 | else if(type == QJDns::Srv) 160 | { 161 | out.haveKnown = true; 162 | out.name = QByteArray((const char *)in->data.server->name); 163 | out.priority = in->data.server->priority; 164 | out.weight = in->data.server->weight; 165 | out.port = in->data.server->port; 166 | } 167 | else if(type == QJDns::Cname || type == QJDns::Ptr || type == QJDns::Ns) 168 | { 169 | out.haveKnown = true; 170 | out.name = QByteArray((const char *)in->data.name); 171 | } 172 | else if(type == QJDns::Txt) 173 | { 174 | out.haveKnown = true; 175 | out.texts.clear(); 176 | for(int n = 0; n < in->data.texts->count; ++n) 177 | out.texts += str2qt(in->data.texts->item[n]); 178 | } 179 | else if(type == QJDns::Hinfo) 180 | { 181 | out.haveKnown = true; 182 | out.cpu = str2qt(in->data.hinfo.cpu); 183 | out.os = str2qt(in->data.hinfo.os); 184 | } 185 | } 186 | 187 | return out; 188 | } 189 | 190 | static jdns_rr_t *export_record(const QJDns::Record &in) 191 | { 192 | jdns_rr_t *out = jdns_rr_new(); 193 | 194 | jdns_rr_set_owner(out, (const unsigned char *)in.owner.data()); 195 | out->ttl = in.ttl; 196 | 197 | // if we have known, use that 198 | if(in.haveKnown) 199 | { 200 | int type = in.type; 201 | 202 | if(type == QJDns::A) 203 | { 204 | jdns_address_t *addr = qt2addr(in.address); 205 | jdns_rr_set_A(out, addr); 206 | jdns_address_delete(addr); 207 | } 208 | else if(type == QJDns::Aaaa) 209 | { 210 | jdns_address_t *addr = qt2addr(in.address); 211 | jdns_rr_set_AAAA(out, addr); 212 | jdns_address_delete(addr); 213 | } 214 | else if(type == QJDns::Mx) 215 | { 216 | jdns_rr_set_MX(out, (const unsigned char *)in.name.data(), in.priority); 217 | } 218 | else if(type == QJDns::Srv) 219 | { 220 | jdns_rr_set_SRV(out, (const unsigned char *)in.name.data(), in.port, in.priority, in.weight); 221 | } 222 | else if(type == QJDns::Cname) 223 | { 224 | jdns_rr_set_CNAME(out, (const unsigned char *)in.name.data()); 225 | } 226 | else if(type == QJDns::Ptr) 227 | { 228 | jdns_rr_set_PTR(out, (const unsigned char *)in.name.data()); 229 | } 230 | else if(type == QJDns::Txt) 231 | { 232 | jdns_stringlist_t *list = jdns_stringlist_new(); 233 | for(int n = 0; n < in.texts.count(); ++n) 234 | { 235 | jdns_string_t *str = qt2str(in.texts[n]); 236 | jdns_stringlist_append(list, str); 237 | jdns_string_delete(str); 238 | } 239 | jdns_rr_set_TXT(out, list); 240 | jdns_stringlist_delete(list); 241 | } 242 | else if(type == QJDns::Hinfo) 243 | { 244 | jdns_string_t *cpu = qt2str(in.cpu); 245 | jdns_string_t *os = qt2str(in.os); 246 | jdns_rr_set_HINFO(out, cpu, os); 247 | jdns_string_delete(cpu); 248 | jdns_string_delete(os); 249 | } 250 | else if(type == QJDns::Ns) 251 | { 252 | jdns_rr_set_NS(out, (const unsigned char *)in.name.data()); 253 | } 254 | } 255 | else 256 | jdns_rr_set_record(out, in.type, (const unsigned char *)in.rdata.data(), in.rdata.size()); 257 | 258 | return out; 259 | } 260 | 261 | //---------------------------------------------------------------------------- 262 | // QJDns::NameServer 263 | //---------------------------------------------------------------------------- 264 | QJDns::NameServer::NameServer() 265 | { 266 | port = JDNS_UNICAST_PORT; 267 | } 268 | 269 | //---------------------------------------------------------------------------- 270 | // QJDns::Record 271 | //---------------------------------------------------------------------------- 272 | QJDns::Record::Record() 273 | { 274 | ttl = 0; 275 | type = -1; 276 | haveKnown = false; 277 | } 278 | 279 | bool QJDns::Record::verify() const 280 | { 281 | jdns_rr_t *rr = export_record(*this); 282 | int ok = jdns_rr_verify(rr); 283 | jdns_rr_delete(rr); 284 | return (ok ? true : false); 285 | } 286 | 287 | //---------------------------------------------------------------------------- 288 | // QJDns 289 | //---------------------------------------------------------------------------- 290 | static int my_srand_done = 0; 291 | 292 | static void my_srand() 293 | { 294 | if(my_srand_done) 295 | return; 296 | 297 | // lame attempt at randomizing without srand 298 | int count = ::time(NULL) % 128; 299 | for(int n = 0; n < count; ++n) 300 | rand(); 301 | 302 | my_srand_done = 1; 303 | } 304 | 305 | QJDns::Private::Private(QJDns *_q) 306 | : QObject(_q) 307 | , q(_q) 308 | , stepTrigger(this) 309 | , debugTrigger(this) 310 | , stepTimeout(this) 311 | , pErrors(0) 312 | , pPublished(0) 313 | , pResponses(0) 314 | { 315 | sess = 0; 316 | shutting_down = false; 317 | new_debug_strings = false; 318 | pending = 0; 319 | 320 | connect(&stepTrigger, SIGNAL(timeout()), SLOT(doNextStepSlot())); 321 | stepTrigger.setSingleShot(true); 322 | 323 | connect(&debugTrigger, SIGNAL(timeout()), SLOT(doDebug())); 324 | debugTrigger.setSingleShot(true); 325 | 326 | connect(&stepTimeout, SIGNAL(timeout()), SLOT(st_timeout())); 327 | stepTimeout.setSingleShot(true); 328 | 329 | my_srand(); 330 | 331 | clock.start(); 332 | } 333 | 334 | QJDns::Private::~Private() 335 | { 336 | cleanup(); 337 | } 338 | 339 | void QJDns::Private::cleanup() 340 | { 341 | if(sess) 342 | { 343 | jdns_session_delete(sess); 344 | sess = 0; 345 | } 346 | 347 | shutting_down = false; 348 | pending = 0; 349 | 350 | // it is safe to delete the QUdpSocket objects here without 351 | // deleteLater, since this code path never occurs when 352 | // a signal from those objects is on the stack 353 | qDeleteAll(socketForHandle); 354 | socketForHandle.clear(); 355 | handleForSocket.clear(); 356 | 357 | stepTrigger.stop(); 358 | stepTimeout.stop(); 359 | need_handle = 0; 360 | } 361 | 362 | bool QJDns::Private::init(QJDns::Mode _mode, const QHostAddress &address) 363 | { 364 | mode = _mode; 365 | 366 | jdns_callbacks_t callbacks; 367 | callbacks.app = this; 368 | callbacks.time_now = cb_time_now; 369 | callbacks.rand_int = cb_rand_int; 370 | callbacks.debug_line = cb_debug_line; 371 | callbacks.udp_bind = cb_udp_bind; 372 | callbacks.udp_unbind = cb_udp_unbind; 373 | callbacks.udp_read = cb_udp_read; 374 | callbacks.udp_write = cb_udp_write; 375 | sess = jdns_session_new(&callbacks); 376 | jdns_set_hold_ids_enabled(sess, 1); 377 | next_handle = 1; 378 | need_handle = false; 379 | 380 | int ret; 381 | 382 | jdns_address_t *baddr = qt2addr(address); 383 | if(mode == Unicast) 384 | { 385 | ret = jdns_init_unicast(sess, baddr, 0); 386 | } 387 | else 388 | { 389 | jdns_address_t *maddr; 390 | if(address.protocol() == QAbstractSocket::IPv6Protocol) 391 | maddr = jdns_address_multicast6_new(); 392 | else 393 | maddr = jdns_address_multicast4_new(); 394 | ret = jdns_init_multicast(sess, baddr, JDNS_MULTICAST_PORT, maddr); 395 | jdns_address_delete(maddr); 396 | } 397 | jdns_address_delete(baddr); 398 | 399 | if(!ret) 400 | { 401 | jdns_session_delete(sess); 402 | sess = 0; 403 | return false; 404 | } 405 | return true; 406 | } 407 | 408 | void QJDns::Private::setNameServers(const QList &nslist) 409 | { 410 | jdns_nameserverlist_t *addrs = jdns_nameserverlist_new(); 411 | for(int n = 0; n < nslist.count(); ++n) 412 | { 413 | jdns_address_t *addr = qt2addr(nslist[n].address); 414 | jdns_nameserverlist_append(addrs, addr, nslist[n].port); 415 | jdns_address_delete(addr); 416 | } 417 | jdns_set_nameservers(sess, addrs); 418 | jdns_nameserverlist_delete(addrs); 419 | } 420 | 421 | void QJDns::Private::process() 422 | { 423 | if(!stepTrigger.isActive()) 424 | { 425 | stepTimeout.stop(); 426 | stepTrigger.start(); 427 | } 428 | } 429 | 430 | void QJDns::Private::processDebug() 431 | { 432 | new_debug_strings = true; 433 | if(!debugTrigger.isActive()) 434 | debugTrigger.start(); 435 | } 436 | 437 | void QJDns::Private::doNextStep() 438 | { 439 | if(shutting_down && complete_shutdown) 440 | { 441 | cleanup(); 442 | emit q->shutdownFinished(); 443 | return; 444 | } 445 | 446 | QPointer self = this; 447 | 448 | int ret = jdns_step(sess); 449 | 450 | QList errors; 451 | QList published; 452 | QList responses; 453 | bool finish_shutdown = false; 454 | 455 | pErrors = &errors; 456 | pPublished = &published; 457 | pResponses = &responses; 458 | 459 | while(1) 460 | { 461 | jdns_event_t *e = jdns_next_event(sess); 462 | if(!e) 463 | break; 464 | 465 | if(e->type == JDNS_EVENT_SHUTDOWN) 466 | { 467 | finish_shutdown = true; 468 | } 469 | else if(e->type == JDNS_EVENT_PUBLISH) 470 | { 471 | if(e->status != JDNS_STATUS_SUCCESS) 472 | { 473 | QJDns::Error error; 474 | if(e->status == JDNS_STATUS_CONFLICT) 475 | error = QJDns::ErrorConflict; 476 | else 477 | error = QJDns::ErrorGeneric; 478 | LateError le; 479 | le.source_type = 1; 480 | le.id = e->id; 481 | le.error = error; 482 | errors += le; 483 | } 484 | else 485 | { 486 | published += e->id; 487 | } 488 | } 489 | else if(e->type == JDNS_EVENT_RESPONSE) 490 | { 491 | if(e->status != JDNS_STATUS_SUCCESS) 492 | { 493 | QJDns::Error error; 494 | if(e->status == JDNS_STATUS_NXDOMAIN) 495 | error = QJDns::ErrorNXDomain; 496 | else if(e->status == JDNS_STATUS_TIMEOUT) 497 | error = QJDns::ErrorTimeout; 498 | else 499 | error = QJDns::ErrorGeneric; 500 | LateError le; 501 | le.source_type = 0; 502 | le.id = e->id; 503 | le.error = error; 504 | errors += le; 505 | } 506 | else 507 | { 508 | QJDns::Response out_response; 509 | for(int n = 0; n < e->response->answerCount; ++n) 510 | out_response.answerRecords += import_record(e->response->answerRecords[n]); 511 | LateResponse lr; 512 | lr.id = e->id; 513 | lr.response = out_response; 514 | if(mode == Unicast) 515 | lr.do_cancel = true; 516 | else 517 | lr.do_cancel = false; 518 | responses += lr; 519 | } 520 | } 521 | 522 | jdns_event_delete(e); 523 | } 524 | 525 | if(ret & JDNS_STEP_TIMER) 526 | stepTimeout.start(jdns_next_timer(sess)); 527 | else 528 | stepTimeout.stop(); 529 | 530 | need_handle = (ret & JDNS_STEP_HANDLE); 531 | 532 | // read the lists safely enough so that items can be deleted 533 | // behind our back 534 | 535 | while(!errors.isEmpty()) 536 | { 537 | LateError i = errors.takeFirst(); 538 | if(i.source_type == 0) 539 | jdns_cancel_query(sess, i.id); 540 | else 541 | jdns_cancel_publish(sess, i.id); 542 | emit q->error(i.id, i.error); 543 | if(!self) 544 | return; 545 | } 546 | 547 | while(!published.isEmpty()) 548 | { 549 | int i = published.takeFirst(); 550 | emit q->published(i); 551 | if(!self) 552 | return; 553 | } 554 | 555 | while(!responses.isEmpty()) 556 | { 557 | LateResponse i = responses.takeFirst(); 558 | if(i.do_cancel) 559 | jdns_cancel_query(sess, i.id); 560 | emit q->resultsReady(i.id, i.response); 561 | if(!self) 562 | return; 563 | } 564 | 565 | if(finish_shutdown) 566 | { 567 | // if we have pending udp packets to write, stick around 568 | if(pending > 0) 569 | { 570 | pending_wait = true; 571 | } 572 | else 573 | { 574 | complete_shutdown = true; 575 | process(); 576 | } 577 | } 578 | 579 | pErrors = 0; 580 | pPublished = 0; 581 | pResponses = 0; 582 | } 583 | 584 | void QJDns::Private::removeCancelled(int id) 585 | { 586 | if(pErrors) 587 | { 588 | for(int n = 0; n < pErrors->count(); ++n) 589 | { 590 | if(pErrors->at(n).id == id) 591 | { 592 | pErrors->removeAt(n); 593 | --n; // adjust position 594 | } 595 | } 596 | } 597 | 598 | if(pPublished) 599 | { 600 | for(int n = 0; n < pPublished->count(); ++n) 601 | { 602 | if(pPublished->at(n) == id) 603 | { 604 | pPublished->removeAt(n); 605 | --n; // adjust position 606 | } 607 | } 608 | } 609 | 610 | if(pResponses) 611 | { 612 | for(int n = 0; n < pResponses->count(); ++n) 613 | { 614 | if(pResponses->at(n).id == id) 615 | { 616 | pResponses->removeAt(n); 617 | --n; // adjust position 618 | } 619 | } 620 | } 621 | } 622 | 623 | void QJDns::Private::udp_readyRead() 624 | { 625 | QUdpSocket *sock = static_cast(sender()); 626 | int handle = handleForSocket.value(sock); 627 | 628 | if(need_handle) 629 | { 630 | jdns_set_handle_readable(sess, handle); 631 | process(); 632 | } 633 | else 634 | { 635 | // eat packet 636 | QByteArray buf(4096, 0); 637 | QHostAddress from_addr; 638 | quint16 from_port; 639 | sock->readDatagram(buf.data(), buf.size(), &from_addr, &from_port); 640 | } 641 | } 642 | 643 | void QJDns::Private::udp_bytesWritten(qint64) 644 | { 645 | if(pending > 0) 646 | { 647 | --pending; 648 | if(shutting_down && pending_wait && pending == 0) 649 | { 650 | pending_wait = false; 651 | complete_shutdown = true; 652 | process(); 653 | } 654 | } 655 | } 656 | 657 | void QJDns::Private::st_timeout() 658 | { 659 | doNextStep(); 660 | } 661 | 662 | void QJDns::Private::doNextStepSlot() 663 | { 664 | doNextStep(); 665 | } 666 | 667 | void QJDns::Private::doDebug() 668 | { 669 | if(new_debug_strings) 670 | { 671 | new_debug_strings = false; 672 | if(!debug_strings.isEmpty()) 673 | emit q->debugLinesReady(); 674 | } 675 | } 676 | 677 | // jdns callbacks 678 | int QJDns::Private::cb_time_now(jdns_session_t *, void *app) 679 | { 680 | QJDns::Private *self = (QJDns::Private *)app; 681 | 682 | return self->clock.elapsed(); 683 | } 684 | 685 | int QJDns::Private::cb_rand_int(jdns_session_t *, void *) 686 | { 687 | return rand() % 65536; 688 | } 689 | 690 | void QJDns::Private::cb_debug_line(jdns_session_t *, void *app, const char *str) 691 | { 692 | QJDns::Private *self = (QJDns::Private *)app; 693 | 694 | self->debug_strings += QString::fromLatin1(str); 695 | self->processDebug(); 696 | } 697 | 698 | int QJDns::Private::cb_udp_bind(jdns_session_t *, void *app, const jdns_address_t *addr, int port, const jdns_address_t *maddr) 699 | { 700 | QJDns::Private *self = (QJDns::Private *)app; 701 | 702 | // we always pass non-null to jdns_init, so this should be a valid address 703 | QHostAddress host = addr2qt(addr); 704 | 705 | QUdpSocket *sock = new QUdpSocket(self); 706 | self->connect(sock, SIGNAL(readyRead()), SLOT(udp_readyRead())); 707 | 708 | // use queued for bytesWritten, since qt is evil and emits before writeDatagram returns 709 | qRegisterMetaType("qint64"); 710 | self->connect(sock, SIGNAL(bytesWritten(qint64)), SLOT(udp_bytesWritten(qint64)), Qt::QueuedConnection); 711 | 712 | QUdpSocket::BindMode mode; 713 | mode |= QUdpSocket::ShareAddress; 714 | mode |= QUdpSocket::ReuseAddressHint; 715 | if(!sock->bind(host, port, mode)) 716 | { 717 | delete sock; 718 | return 0; 719 | } 720 | 721 | if(maddr) 722 | { 723 | int sd = sock->socketDescriptor(); 724 | bool ok; 725 | int errorCode; 726 | if(maddr->isIpv6) 727 | ok = qjdns_sock_setMulticast6(sd, maddr->addr.v6, &errorCode); 728 | else 729 | ok = qjdns_sock_setMulticast4(sd, maddr->addr.v4, &errorCode); 730 | 731 | if(!ok) 732 | { 733 | delete sock; 734 | 735 | self->debug_strings += QString("failed to setup multicast on the socket (errorCode=%1)").arg(errorCode); 736 | self->processDebug(); 737 | return 0; 738 | } 739 | 740 | if(maddr->isIpv6) 741 | { 742 | qjdns_sock_setTTL6(sd, 255); 743 | qjdns_sock_setIPv6Only(sd); 744 | } 745 | else 746 | qjdns_sock_setTTL4(sd, 255); 747 | } 748 | 749 | int handle = self->next_handle++; 750 | self->socketForHandle.insert(handle, sock); 751 | self->handleForSocket.insert(sock, handle); 752 | return handle; 753 | } 754 | 755 | void QJDns::Private::cb_udp_unbind(jdns_session_t *, void *app, int handle) 756 | { 757 | QJDns::Private *self = (QJDns::Private *)app; 758 | 759 | QUdpSocket *sock = self->socketForHandle.value(handle); 760 | if(!sock) 761 | return; 762 | 763 | self->socketForHandle.remove(handle); 764 | self->handleForSocket.remove(sock); 765 | delete sock; 766 | } 767 | 768 | int QJDns::Private::cb_udp_read(jdns_session_t *, void *app, int handle, jdns_address_t *addr, int *port, unsigned char *buf, int *bufsize) 769 | { 770 | QJDns::Private *self = (QJDns::Private *)app; 771 | 772 | QUdpSocket *sock = self->socketForHandle.value(handle); 773 | if(!sock) 774 | return 0; 775 | 776 | // nothing to read? 777 | if(!sock->hasPendingDatagrams()) 778 | return 0; 779 | 780 | QHostAddress from_addr; 781 | quint16 from_port; 782 | int ret = sock->readDatagram((char *)buf, *bufsize, &from_addr, &from_port); 783 | if(ret == -1) 784 | return 0; 785 | 786 | qt2addr_set(addr, from_addr); 787 | *port = (int)from_port; 788 | *bufsize = ret; 789 | return 1; 790 | } 791 | 792 | int QJDns::Private::cb_udp_write(jdns_session_t *, void *app, int handle, const jdns_address_t *addr, int port, unsigned char *buf, int bufsize) 793 | { 794 | QJDns::Private *self = (QJDns::Private *)app; 795 | 796 | QUdpSocket *sock = self->socketForHandle.value(handle); 797 | if(!sock) 798 | return 0; 799 | 800 | QHostAddress host = addr2qt(addr); 801 | int ret = sock->writeDatagram((const char *)buf, bufsize, host, port); 802 | if(ret == -1) 803 | { 804 | // this can happen if the datagram to send is too big. i'm not sure what else 805 | // may cause this. if we return 0, then jdns may try to resend the packet, 806 | // which might not work if it is too large (causing the same error over and 807 | // over). we'll return success to jdns, so the result is as if the packet 808 | // was dropped. 809 | return 1; 810 | } 811 | 812 | ++self->pending; 813 | return 1; 814 | } 815 | 816 | QJDns::QJDns(QObject *parent) 817 | :QObject(parent) 818 | { 819 | d = new Private(this); 820 | } 821 | 822 | QJDns::~QJDns() 823 | { 824 | delete d; 825 | } 826 | 827 | bool QJDns::init(Mode mode, const QHostAddress &address) 828 | { 829 | return d->init(mode, address); 830 | } 831 | 832 | void QJDns::shutdown() 833 | { 834 | d->shutting_down = true; 835 | d->pending_wait = false; 836 | d->complete_shutdown = false; 837 | jdns_shutdown(d->sess); 838 | d->process(); 839 | } 840 | 841 | QStringList QJDns::debugLines() 842 | { 843 | QStringList tmp = d->debug_strings; 844 | d->debug_strings.clear(); 845 | return tmp; 846 | } 847 | 848 | QJDns::SystemInfo QJDns::systemInfo() 849 | { 850 | SystemInfo out; 851 | jdns_dnsparams_t *params = jdns_system_dnsparams(); 852 | for(int n = 0; n < params->nameservers->count; ++n) 853 | { 854 | NameServer ns; 855 | ns.address = addr2qt(params->nameservers->item[n]->address); 856 | out.nameServers += ns; 857 | } 858 | for(int n = 0; n < params->domains->count; ++n) 859 | out.domains += str2qt(params->domains->item[n]); 860 | for(int n = 0; n < params->hosts->count; ++n) 861 | { 862 | DnsHost h; 863 | h.name = str2qt(params->hosts->item[n]->name); 864 | h.address = addr2qt(params->hosts->item[n]->address); 865 | out.hosts += h; 866 | } 867 | jdns_dnsparams_delete(params); 868 | return out; 869 | } 870 | 871 | #define PROBE_BASE 20000 872 | #define PROBE_RANGE 100 873 | 874 | QHostAddress QJDns::detectPrimaryMulticast(const QHostAddress &address) 875 | { 876 | my_srand(); 877 | 878 | QUdpSocket *sock = new QUdpSocket; 879 | QUdpSocket::BindMode mode; 880 | mode |= QUdpSocket::ShareAddress; 881 | mode |= QUdpSocket::ReuseAddressHint; 882 | int port = -1; 883 | for(int n = 0; n < PROBE_RANGE; ++n) 884 | { 885 | if(sock->bind(address, PROBE_BASE + n, mode)) 886 | { 887 | port = PROBE_BASE + n; 888 | break; 889 | } 890 | } 891 | if(port == -1) 892 | { 893 | delete sock; 894 | return QHostAddress(); 895 | } 896 | 897 | jdns_address_t *a; 898 | if(address.protocol() == QAbstractSocket::IPv6Protocol) 899 | a = jdns_address_multicast6_new(); 900 | else 901 | a = jdns_address_multicast4_new(); 902 | QHostAddress maddr = addr2qt(a); 903 | jdns_address_delete(a); 904 | 905 | if(address.protocol() == QAbstractSocket::IPv6Protocol) 906 | { 907 | int x; 908 | if(!qjdns_sock_setMulticast6(sock->socketDescriptor(), maddr.toIPv6Address().c, &x)) 909 | { 910 | delete sock; 911 | return QHostAddress(); 912 | } 913 | qjdns_sock_setTTL6(sock->socketDescriptor(), 0); 914 | } 915 | else 916 | { 917 | int x; 918 | if(!qjdns_sock_setMulticast4(sock->socketDescriptor(), maddr.toIPv4Address(), &x)) 919 | { 920 | delete sock; 921 | return QHostAddress(); 922 | } 923 | qjdns_sock_setTTL4(sock->socketDescriptor(), 0); 924 | } 925 | 926 | QHostAddress result; 927 | QByteArray out(128, 0); 928 | for(int n = 0; n < out.size(); ++n) 929 | out[n] = rand(); 930 | if(sock->writeDatagram(out.data(), out.size(), maddr, port) == -1) 931 | { 932 | delete sock; 933 | return QHostAddress(); 934 | } 935 | while(1) 936 | { 937 | if(!sock->waitForReadyRead(1000)) 938 | { 939 | fprintf(stderr, "QJDns::detectPrimaryMulticast: timeout while checking %s\n", qPrintable(address.toString())); 940 | delete sock; 941 | return QHostAddress(); 942 | } 943 | QByteArray in(128, 0); 944 | QHostAddress from_addr; 945 | quint16 from_port; 946 | int ret = sock->readDatagram(in.data(), in.size(), &from_addr, &from_port); 947 | if(ret == -1) 948 | { 949 | delete sock; 950 | return QHostAddress(); 951 | } 952 | 953 | if(from_port != port) 954 | continue; 955 | in.resize(ret); 956 | if(in != out) 957 | continue; 958 | 959 | result = from_addr; 960 | break; 961 | } 962 | delete sock; 963 | 964 | return result; 965 | } 966 | 967 | void QJDns::setNameServers(const QList &list) 968 | { 969 | d->setNameServers(list); 970 | } 971 | 972 | int QJDns::queryStart(const QByteArray &name, int type) 973 | { 974 | int id = jdns_query(d->sess, (const unsigned char *)name.data(), type); 975 | d->process(); 976 | return id; 977 | } 978 | 979 | void QJDns::queryCancel(int id) 980 | { 981 | jdns_cancel_query(d->sess, id); 982 | d->removeCancelled(id); 983 | d->process(); 984 | } 985 | 986 | int QJDns::publishStart(PublishMode m, const Record &record) 987 | { 988 | jdns_rr_t *rr = export_record(record); 989 | 990 | int pubmode; 991 | if(m == QJDns::Unique) 992 | pubmode = JDNS_PUBLISH_UNIQUE; 993 | else 994 | pubmode = JDNS_PUBLISH_SHARED; 995 | 996 | int id = jdns_publish(d->sess, pubmode, rr); 997 | jdns_rr_delete(rr); 998 | d->process(); 999 | return id; 1000 | } 1001 | 1002 | void QJDns::publishUpdate(int id, const Record &record) 1003 | { 1004 | jdns_rr_t *rr = export_record(record); 1005 | 1006 | jdns_update_publish(d->sess, id, rr); 1007 | jdns_rr_delete(rr); 1008 | d->process(); 1009 | } 1010 | 1011 | void QJDns::publishCancel(int id) 1012 | { 1013 | jdns_cancel_publish(d->sess, id); 1014 | d->removeCancelled(id); 1015 | d->process(); 1016 | } 1017 | -------------------------------------------------------------------------------- /src/jdns/jdns_packet.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 Justin Karneges 3 | * Copyright (C) 2020 Ivan Romanov 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a 6 | * copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included 14 | * in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | #include "jdns_packet.h" 26 | 27 | #include "jdns_p.h" 28 | 29 | // maximum length of a sublabel 30 | #define MAX_SUBLABEL_LENGTH 63 31 | 32 | // maximum length of a label, including final terminating zero (root sublabel) 33 | // according to RFC 2181, the maximum length is 255, not counting the root 34 | // sublabel. so, with the root sublabel, that means a max length of 256. 35 | #define MAX_LABEL_LENGTH 256 36 | 37 | // jer's endian functions 38 | static unsigned short int net2short(const unsigned char **bufp) 39 | { 40 | unsigned short int i; 41 | i = **bufp; 42 | i <<= 8; 43 | i |= *(*bufp + 1); 44 | *bufp += 2; 45 | return i; 46 | } 47 | 48 | static unsigned long int net2long(const unsigned char **bufp) 49 | { 50 | unsigned long int l; 51 | l = **bufp; 52 | l <<= 8; 53 | l |= *(*bufp + 1); 54 | l <<= 8; 55 | l |= *(*bufp + 2); 56 | l <<= 8; 57 | l |= *(*bufp + 3); 58 | *bufp += 4; 59 | return l; 60 | } 61 | 62 | static void short2net(unsigned short int i, unsigned char **bufp) 63 | { 64 | *(*bufp + 1) = (unsigned char)i; 65 | i >>= 8; 66 | **bufp = (unsigned char)i; 67 | *bufp += 2; 68 | } 69 | 70 | static void long2net(unsigned long int l, unsigned char **bufp) 71 | { 72 | *(*bufp + 3) = (unsigned char)l; 73 | l >>= 8; 74 | *(*bufp + 2) = (unsigned char)l; 75 | l >>= 8; 76 | *(*bufp + 1) = (unsigned char)l; 77 | l >>= 8; 78 | **bufp = (unsigned char)l; 79 | *bufp += 4; 80 | } 81 | 82 | // label stuff 83 | typedef struct jdns_packet_label 84 | { 85 | JDNS_OBJECT 86 | int offset; 87 | jdns_string_t *value; 88 | } jdns_packet_label_t; 89 | 90 | static void jdns_packet_label_delete(jdns_packet_label_t *a); 91 | static jdns_packet_label_t *jdns_packet_label_copy(const jdns_packet_label_t *a); 92 | 93 | static jdns_packet_label_t *jdns_packet_label_new() 94 | { 95 | jdns_packet_label_t *a = JDNS_OBJECT_NEW(jdns_packet_label); 96 | a->offset = 0; 97 | a->value = 0; 98 | return a; 99 | } 100 | 101 | jdns_packet_label_t *jdns_packet_label_copy(const jdns_packet_label_t *a) 102 | { 103 | jdns_packet_label_t *c = jdns_packet_label_new(); 104 | c->offset = a->offset; 105 | if(a->value) 106 | c->value = jdns_string_copy(a->value); 107 | return c; 108 | } 109 | 110 | void jdns_packet_label_delete(jdns_packet_label_t *a) 111 | { 112 | if(!a) 113 | return; 114 | jdns_string_delete(a->value); 115 | jdns_object_free(a); 116 | } 117 | 118 | // gets an offset for decompression. does range and hop count checking also 119 | static int getoffset(const unsigned char *str, int refsize, int *hopsleft) 120 | { 121 | unsigned short int x; 122 | if(*hopsleft <= 0) 123 | return -1; 124 | --(*hopsleft); 125 | x = str[0] & 0x3f; 126 | x <<= 8; 127 | x |= str[1]; 128 | // stay in bounds 129 | if(x >= refsize) 130 | return -1; 131 | return x; 132 | } 133 | 134 | static int readlabel(const unsigned char *in, int insize, const unsigned char *ref, int refsize, int *_at, jdns_string_t **name) 135 | { 136 | int at; 137 | // string format is one character smaller than dns format. e.g.: 138 | // dns: [7] affinix [3] com [0] = 13 bytes 139 | // string: "affinix.com." = 12 bytes 140 | // only exception is '.' itself, but that won't influence the max. 141 | unsigned char out[MAX_LABEL_LENGTH - 1]; 142 | int out_size; 143 | const unsigned char *label, *last; 144 | int hopped_yet; 145 | int hopsleft; 146 | int label_size; 147 | 148 | at = *_at; 149 | 150 | // stay in range 151 | if(at < 0 || at >= insize) 152 | return 0; 153 | 154 | out_size = 0; 155 | label = in + at; 156 | hopped_yet = 0; 157 | last = in + insize; 158 | while(1) 159 | { 160 | // need a byte 161 | if(label + 1 > last) 162 | goto error; 163 | 164 | // we make this a while loop instead of an 'if', in case 165 | // there's a pointer to a pointer. as a precaution, 166 | // we will hop no more than 8 times 167 | hopsleft = 8; 168 | while(*label & 0xc0) 169 | { 170 | int offset; 171 | 172 | // need the next byte, too 173 | if(label + 2 > last) 174 | goto error; 175 | 176 | offset = getoffset(label, refsize, &hopsleft); 177 | if(offset == -1) 178 | goto error; 179 | 180 | label = ref + offset; 181 | if(!hopped_yet) 182 | { 183 | at += 2; 184 | hopped_yet = 1; 185 | last = ref + refsize; 186 | } 187 | 188 | // need a byte 189 | if(label + 1 > last) 190 | goto error; 191 | } 192 | 193 | label_size = *label & 0x3f; 194 | 195 | // null label? then we're done 196 | if(label_size == 0) 197 | { 198 | if(!hopped_yet) 199 | ++at; 200 | break; 201 | } 202 | 203 | // enough source bytes? (length byte + length) 204 | if(label + label_size + 1 > last) 205 | goto error; 206 | 207 | // enough dest bytes? (length + dot) 208 | if(out_size + label_size + 1 > MAX_LABEL_LENGTH - 1) 209 | goto error; 210 | 211 | memcpy(out + out_size, label + 1, label_size); 212 | out_size += label_size; 213 | out[out_size] = '.'; 214 | ++out_size; 215 | 216 | if(!hopped_yet) 217 | at += label_size + 1; 218 | 219 | label += label_size + 1; 220 | } 221 | 222 | *_at = at; 223 | *name = jdns_string_new(); 224 | jdns_string_set(*name, out, out_size); 225 | return 1; 226 | 227 | error: 228 | return 0; 229 | } 230 | 231 | // this function compares labels in label format: 232 | // [length] [value ...] [length] [value ...] [0] 233 | static int matchlabel(const unsigned char *a, int asize, const unsigned char *b, int bsize, const unsigned char *ref, int refsize, int ahopsleft, int bhopsleft) 234 | { 235 | int n, alen, blen, offset; 236 | 237 | // same pointer? 238 | if(a == b) 239 | return 1; 240 | 241 | if(asize < 1 || bsize < 1) 242 | return 0; 243 | 244 | // always ensure we get called without a pointer 245 | if(*a & 0xc0) 246 | { 247 | if(asize < 2) 248 | return 0; 249 | offset = getoffset(a, refsize, &ahopsleft); 250 | if(offset == -1) 251 | return 0; 252 | return matchlabel(ref + offset, refsize - offset, b, bsize, ref, refsize, ahopsleft, bhopsleft); 253 | } 254 | if(*b & 0xc0) 255 | { 256 | if(bsize < 2) 257 | return 0; 258 | offset = getoffset(b, refsize, &bhopsleft); 259 | if(offset == -1) 260 | return 0; 261 | return matchlabel(a, asize, ref + offset, refsize - offset, ref, refsize, ahopsleft, bhopsleft); 262 | } 263 | 264 | alen = *a & 0x3f; 265 | blen = *b & 0x3f; 266 | 267 | // must be same length 268 | if(alen != blen) 269 | return 0; 270 | 271 | // done? 272 | if(alen == 0) 273 | return 1; 274 | 275 | // length byte + length + first byte of next label 276 | if(asize < alen + 2) 277 | return 0; 278 | if(bsize < blen + 2) 279 | return 0; 280 | 281 | // compare the value 282 | for(n = 1; n < alen + 1; ++n) 283 | { 284 | if(a[n] != b[n]) 285 | return 0; 286 | } 287 | 288 | // try next labels 289 | n = alen + 1; 290 | return matchlabel(a + n, asize - n, b + n, bsize - n, ref, refsize, ahopsleft, bhopsleft); 291 | } 292 | 293 | int jdns_packet_name_isvalid(const unsigned char *name, int size) 294 | { 295 | int n, at; 296 | 297 | // at least one byte, no larger than MAX_LABEL_LENGTH - 1 (one byte is 298 | // gained when converting to a label) 299 | if(size < 1 || size > (MAX_LABEL_LENGTH - 1)) 300 | return 0; 301 | 302 | // last byte must be a dot 303 | if(name[size - 1] != '.') 304 | return 0; 305 | 306 | // if name is one character (which must be a dot), return success 307 | if(size == 1) 308 | return 1; 309 | 310 | // first byte can't be a dot if there are characters after 311 | if(size > 1 && name[0] == '.') 312 | return 0; 313 | 314 | // each sublabel must be between 1 and MAX_SUBLABEL_LENGTH in length 315 | at = 0; 316 | while(1) 317 | { 318 | int len; 319 | // search for dot or end 320 | for(n = at; n < size; ++n) 321 | { 322 | if(name[n] == '.') 323 | break; 324 | } 325 | // length of last one is always zero 326 | if(n >= size) 327 | break; 328 | 329 | len = n - at; 330 | if(len < 1 || len > MAX_SUBLABEL_LENGTH) 331 | return 0; 332 | at = n + 1; // skip over the dot 333 | } 334 | 335 | return 1; 336 | } 337 | 338 | // this function assumes label is pointing to a MAX_LABEL_LENGTH byte buffer 339 | static int name_to_label(const jdns_string_t *name, unsigned char *label) 340 | { 341 | int n, i, at; 342 | 343 | if(!jdns_packet_name_isvalid(name->data, name->size)) 344 | return -1; 345 | 346 | if(name->size == 1) 347 | { 348 | label[0] = 0; 349 | return 1; 350 | } 351 | 352 | at = 0; 353 | i = 0; 354 | while(1) 355 | { 356 | int len; 357 | // search for dot or end 358 | for(n = at; n < name->size; ++n) 359 | { 360 | if(name->data[n] == '.') 361 | break; 362 | } 363 | len = n - at; 364 | if(i + (len + 1) > MAX_LABEL_LENGTH) // length byte + length 365 | return 0; 366 | 367 | label[i++] = len; 368 | memcpy(label + i, name->data + at, len); 369 | i += len; 370 | 371 | if(n >= name->size) // end? 372 | break; 373 | at = n + 1; // skip over the dot 374 | } 375 | 376 | return i; 377 | } 378 | 379 | // lookup list is made of jdns_packet_labels 380 | static int writelabel(const jdns_string_t *name, int at, int left, unsigned char **bufp, jdns_list_t *lookup) 381 | { 382 | unsigned char label[MAX_LABEL_LENGTH]; 383 | int n, i, len; 384 | unsigned char *l; 385 | unsigned char *ref; 386 | int refsize; 387 | 388 | len = name_to_label(name, label); 389 | if(len == -1) 390 | return 0; 391 | 392 | ref = *bufp - at; 393 | refsize = at + left; 394 | for(n = 0; label[n]; n += label[n] + 1) 395 | { 396 | for(i = 0; i < lookup->count; ++i) 397 | { 398 | jdns_packet_label_t *pl = (jdns_packet_label_t *)lookup->item[i]; 399 | 400 | if(matchlabel(label + n, len - n, pl->value->data, pl->value->size, ref, refsize, 8, 8)) 401 | { 402 | // set up a pointer right here, overwriting 403 | // the length byte and the first content 404 | // byte of this section within 'label'. 405 | // this is safe, because the length value 406 | // will always be greater than zero, 407 | // ensuring we have two bytes available to 408 | // use. 409 | l = label + n; 410 | short2net((unsigned short int)pl->offset, &l); 411 | label[n] |= 0xc0; 412 | len = n + 2; // cut things short 413 | break; 414 | } 415 | } 416 | if(label[n] & 0xc0) // double loop, so break again 417 | break; 418 | } 419 | 420 | if(left < len) 421 | return 0; 422 | 423 | // copy into buffer, point there now 424 | memcpy(*bufp, label, len); 425 | l = *bufp; 426 | *bufp += len; 427 | 428 | // for each new label, store its location for future compression 429 | for(n = 0; l[n]; n += l[n] + 1) 430 | { 431 | jdns_string_t *str; 432 | jdns_packet_label_t *pl; 433 | if(l[n] & 0xc0) 434 | break; 435 | 436 | pl = jdns_packet_label_new(); 437 | str = jdns_string_new(); 438 | jdns_string_set(str, l + n, len - n); 439 | pl->offset = l + n - ref; 440 | pl->value = str; 441 | jdns_list_insert(lookup, pl, -1); 442 | } 443 | 444 | return 1; 445 | } 446 | 447 | //---------------------------------------------------------------------------- 448 | // jdns_packet_write 449 | //---------------------------------------------------------------------------- 450 | #define JDNS_PACKET_WRITE_RAW 0 451 | #define JDNS_PACKET_WRITE_NAME 1 452 | 453 | struct jdns_packet_write 454 | { 455 | JDNS_OBJECT 456 | int type; 457 | jdns_string_t *value; 458 | }; 459 | 460 | void jdns_packet_write_delete(jdns_packet_write_t *a); 461 | jdns_packet_write_t *jdns_packet_write_copy(const jdns_packet_write_t *a); 462 | 463 | jdns_packet_write_t *jdns_packet_write_new() 464 | { 465 | jdns_packet_write_t *a = JDNS_OBJECT_NEW(jdns_packet_write); 466 | a->type = 0; 467 | a->value = 0; 468 | return a; 469 | } 470 | 471 | jdns_packet_write_t *jdns_packet_write_copy(const jdns_packet_write_t *a) 472 | { 473 | jdns_packet_write_t *c = jdns_packet_write_new(); 474 | c->type = a->type; 475 | if(a->value) 476 | c->value = jdns_string_copy(a->value); 477 | return c; 478 | } 479 | 480 | void jdns_packet_write_delete(jdns_packet_write_t *a) 481 | { 482 | if(!a) 483 | return; 484 | jdns_string_delete(a->value); 485 | jdns_object_free(a); 486 | } 487 | 488 | //---------------------------------------------------------------------------- 489 | // jdns_packet_question 490 | //---------------------------------------------------------------------------- 491 | jdns_packet_question_t *jdns_packet_question_new() 492 | { 493 | jdns_packet_question_t *a = JDNS_OBJECT_NEW(jdns_packet_question); 494 | a->qname = 0; 495 | a->qtype = 0; 496 | a->qclass = 0; 497 | return a; 498 | } 499 | 500 | jdns_packet_question_t *jdns_packet_question_copy(const jdns_packet_question_t *a) 501 | { 502 | jdns_packet_question_t *c = jdns_packet_question_new(); 503 | if(a->qname) 504 | c->qname = jdns_string_copy(a->qname); 505 | c->qtype = a->qtype; 506 | c->qclass = a->qclass; 507 | return c; 508 | } 509 | 510 | void jdns_packet_question_delete(jdns_packet_question_t *a) 511 | { 512 | if(!a) 513 | return; 514 | jdns_string_delete(a->qname); 515 | jdns_object_free(a); 516 | } 517 | 518 | //---------------------------------------------------------------------------- 519 | // jdns_packet_resource 520 | //---------------------------------------------------------------------------- 521 | jdns_packet_resource_t *jdns_packet_resource_new() 522 | { 523 | jdns_packet_resource_t *a = JDNS_OBJECT_NEW(jdns_packet_resource); 524 | a->qname = 0; 525 | a->qtype = 0; 526 | a->qclass = 0; 527 | a->ttl = 0; 528 | a->rdlength = 0; 529 | a->rdata = 0; 530 | 531 | a->writelog = jdns_list_new(); 532 | a->writelog->valueList = 1; 533 | return a; 534 | } 535 | 536 | jdns_packet_resource_t *jdns_packet_resource_copy(const jdns_packet_resource_t *a) 537 | { 538 | jdns_packet_resource_t *c = jdns_packet_resource_new(); 539 | if(a->qname) 540 | c->qname = jdns_string_copy(a->qname); 541 | c->qtype = a->qtype; 542 | c->qclass = a->qclass; 543 | c->ttl = a->ttl; 544 | c->rdlength = a->rdlength; 545 | c->rdata = jdns_copy_array(a->rdata, a->rdlength); 546 | 547 | jdns_list_delete(c->writelog); 548 | c->writelog = jdns_list_copy(a->writelog); 549 | return c; 550 | } 551 | 552 | void jdns_packet_resource_delete(jdns_packet_resource_t *a) 553 | { 554 | if(!a) 555 | return; 556 | jdns_string_delete(a->qname); 557 | if(a->rdata) 558 | jdns_free(a->rdata); 559 | jdns_list_delete(a->writelog); 560 | jdns_object_free(a); 561 | } 562 | 563 | void jdns_packet_resource_add_bytes(jdns_packet_resource_t *a, const unsigned char *data, int size) 564 | { 565 | jdns_packet_write_t *write = jdns_packet_write_new(); 566 | write->type = JDNS_PACKET_WRITE_RAW; 567 | write->value = jdns_string_new(); 568 | jdns_string_set(write->value, data, size); 569 | jdns_list_insert_value(a->writelog, write, -1); 570 | jdns_packet_write_delete(write); 571 | } 572 | 573 | void jdns_packet_resource_add_name(jdns_packet_resource_t *a, const jdns_string_t *name) 574 | { 575 | jdns_packet_write_t *write = jdns_packet_write_new(); 576 | write->type = JDNS_PACKET_WRITE_NAME; 577 | write->value = jdns_string_copy(name); 578 | jdns_list_insert_value(a->writelog, write, -1); 579 | jdns_packet_write_delete(write); 580 | } 581 | 582 | int jdns_packet_resource_read_name(const jdns_packet_resource_t *a, const jdns_packet_t *p, int *at, jdns_string_t **name) 583 | { 584 | return readlabel(a->rdata, a->rdlength, p->raw_data, p->raw_size, at, name); 585 | } 586 | 587 | //---------------------------------------------------------------------------- 588 | // jdns_packet 589 | //---------------------------------------------------------------------------- 590 | 591 | // note: both process_qsection and process_rrsection modify the 'dest' list, 592 | // even if later items cause an error. this turns out to be convenient 593 | // for handling truncated dns packets 594 | 595 | static int process_qsection(jdns_list_t *dest, int count, const unsigned char *data, int size, const unsigned char **bufp) 596 | { 597 | int n; 598 | int offset, at; 599 | jdns_string_t *name = 0; 600 | const unsigned char *buf; 601 | 602 | buf = *bufp; 603 | for(n = 0; n < count; ++n) 604 | { 605 | jdns_packet_question_t *q; 606 | 607 | offset = buf - data; 608 | at = 0; 609 | 610 | if(!readlabel(data + offset, size - offset, data, size, &at, &name)) 611 | goto error; 612 | 613 | offset += at; 614 | 615 | // need 4 more bytes 616 | if(size - offset < 4) 617 | goto error; 618 | 619 | buf = data + offset; 620 | 621 | q = jdns_packet_question_new(); 622 | q->qname = name; 623 | name = 0; 624 | q->qtype = net2short(&buf); 625 | q->qclass = net2short(&buf); 626 | 627 | jdns_list_insert_value(dest, q, -1); 628 | jdns_packet_question_delete(q); 629 | } 630 | 631 | *bufp = buf; 632 | return 1; 633 | 634 | error: 635 | jdns_string_delete(name); 636 | return 0; 637 | } 638 | 639 | static int process_rrsection(jdns_list_t *dest, int count, const unsigned char *data, int size, const unsigned char **bufp) 640 | { 641 | int n; 642 | int offset, at; 643 | jdns_string_t *name = 0; 644 | const unsigned char *buf; 645 | 646 | buf = *bufp; 647 | for(n = 0; n < count; ++n) 648 | { 649 | jdns_packet_resource_t *r; 650 | 651 | offset = buf - data; 652 | at = 0; 653 | 654 | if(!readlabel(data + offset, size - offset, data, size, &at, &name)) 655 | goto error; 656 | 657 | offset += at; 658 | 659 | // need 10 more bytes 660 | if(offset + 10 > size) 661 | goto error; 662 | 663 | buf = data + offset; 664 | 665 | r = jdns_packet_resource_new(); 666 | r->qname = name; 667 | name = 0; 668 | r->qtype = net2short(&buf); 669 | r->qclass = net2short(&buf); 670 | r->ttl = net2long(&buf); 671 | 672 | // per RFC 2181, ttl is supposed to be a 31 bit number. if 673 | // the top bit of the 32 bit field is 1, then entire ttl is 674 | // to be considered 0. 675 | if(r->ttl & 0x80000000) 676 | r->ttl = 0; 677 | 678 | r->rdlength = net2short(&buf); 679 | 680 | offset = buf - data; 681 | 682 | // make sure we have enough for the rdata 683 | if(size - offset < r->rdlength) 684 | { 685 | jdns_packet_resource_delete(r); 686 | goto error; 687 | } 688 | 689 | r->rdata = jdns_copy_array(buf, r->rdlength); 690 | buf += r->rdlength; 691 | 692 | jdns_list_insert_value(dest, r, -1); 693 | jdns_packet_resource_delete(r); 694 | } 695 | 696 | *bufp = buf; 697 | return 1; 698 | 699 | error: 700 | jdns_string_delete(name); 701 | return 0; 702 | } 703 | 704 | static int append_qsection(const jdns_list_t *src, int at, int left, unsigned char **bufp, jdns_list_t *lookup) 705 | { 706 | unsigned char *buf, *start, *last; 707 | int n; 708 | 709 | buf = *bufp; 710 | start = buf - at; 711 | last = buf + left; 712 | for(n = 0; n < src->count; ++n) 713 | { 714 | jdns_packet_question_t *q = (jdns_packet_question_t *)src->item[n]; 715 | 716 | if(!writelabel(q->qname, buf - start, last - buf, &buf, lookup)) 717 | goto error; 718 | 719 | if(buf + 4 > last) 720 | goto error; 721 | 722 | short2net(q->qtype, &buf); 723 | short2net(q->qclass, &buf); 724 | } 725 | 726 | *bufp = buf; 727 | return 1; 728 | 729 | error: 730 | return 0; 731 | } 732 | 733 | static int append_rrsection(const jdns_list_t *src, int at, int left, unsigned char **bufp, jdns_list_t *lookup) 734 | { 735 | unsigned char *buf, *start, *last, *rdlengthp; 736 | int n, i; 737 | 738 | buf = *bufp; 739 | start = buf - at; 740 | last = buf + left; 741 | for(n = 0; n < src->count; ++n) 742 | { 743 | jdns_packet_resource_t *r = (jdns_packet_resource_t *)src->item[n]; 744 | 745 | if(!writelabel(r->qname, buf - start, last - buf, &buf, lookup)) 746 | goto error; 747 | 748 | if(buf + 10 > last) 749 | goto error; 750 | 751 | short2net(r->qtype, &buf); 752 | short2net(r->qclass, &buf); 753 | long2net(r->ttl, &buf); 754 | 755 | // skip over rdlength 756 | rdlengthp = buf; 757 | buf += 2; 758 | 759 | // play write log 760 | for(i = 0; i < r->writelog->count; ++i) 761 | { 762 | jdns_packet_write_t *write = (jdns_packet_write_t *)r->writelog->item[i]; 763 | if(write->type == JDNS_PACKET_WRITE_RAW) 764 | { 765 | if(buf + write->value->size > last) 766 | goto error; 767 | 768 | memcpy(buf, write->value->data, write->value->size); 769 | buf += write->value->size; 770 | } 771 | else // JDNS_PACKET_WRITE_NAME 772 | { 773 | if(!writelabel(write->value, buf - start, last - buf, &buf, lookup)) 774 | goto error; 775 | } 776 | } 777 | 778 | i = buf - rdlengthp; // should be rdata size + 2 779 | short2net((unsigned short int)(i - 2), &rdlengthp); 780 | } 781 | 782 | *bufp = buf; 783 | return 1; 784 | 785 | error: 786 | return 0; 787 | } 788 | 789 | jdns_packet_t *jdns_packet_new() 790 | { 791 | jdns_packet_t *a = JDNS_OBJECT_NEW(jdns_packet); 792 | a->id = 0; 793 | a->opts.qr = 0; 794 | a->opts.opcode = 0; 795 | a->opts.aa = 0; 796 | a->opts.tc = 0; 797 | a->opts.rd = 0; 798 | a->opts.ra = 0; 799 | a->opts.z = 0; 800 | a->opts.ad = 0; 801 | a->opts.cd = 0; 802 | a->opts.rcode = 0; 803 | 804 | a->questions = jdns_list_new(); 805 | a->answerRecords = jdns_list_new(); 806 | a->authorityRecords = jdns_list_new(); 807 | a->additionalRecords = jdns_list_new(); 808 | 809 | a->questions->valueList = 1; 810 | a->answerRecords->valueList = 1; 811 | a->authorityRecords->valueList = 1; 812 | a->additionalRecords->valueList = 1; 813 | 814 | a->fully_parsed = 0; 815 | 816 | a->raw_size = 0; 817 | a->raw_data = 0; 818 | return a; 819 | } 820 | 821 | jdns_packet_t *jdns_packet_copy(const jdns_packet_t *a) 822 | { 823 | jdns_packet_t *c = jdns_packet_new(); 824 | c->id = a->id; 825 | c->opts.qr = a->opts.qr; 826 | c->opts.opcode = a->opts.opcode; 827 | c->opts.aa = a->opts.aa; 828 | c->opts.tc = a->opts.tc; 829 | c->opts.rd = a->opts.rd; 830 | c->opts.ra = a->opts.ra; 831 | c->opts.z = a->opts.z; 832 | c->opts.ad = a->opts.ad; 833 | c->opts.cd = a->opts.cd; 834 | c->opts.rcode = a->opts.rcode; 835 | 836 | jdns_list_delete(c->questions); 837 | jdns_list_delete(c->answerRecords); 838 | jdns_list_delete(c->authorityRecords); 839 | jdns_list_delete(c->additionalRecords); 840 | c->questions = jdns_list_copy(a->questions); 841 | c->answerRecords = jdns_list_copy(a->answerRecords); 842 | c->authorityRecords = jdns_list_copy(a->authorityRecords); 843 | c->additionalRecords = jdns_list_copy(a->additionalRecords); 844 | 845 | c->fully_parsed = a->fully_parsed; 846 | 847 | c->raw_size = a->raw_size; 848 | c->raw_data = jdns_copy_array(a->raw_data, a->raw_size); 849 | 850 | return c; 851 | } 852 | 853 | void jdns_packet_delete(jdns_packet_t *a) 854 | { 855 | if(!a) 856 | return; 857 | jdns_list_delete(a->questions); 858 | jdns_list_delete(a->answerRecords); 859 | jdns_list_delete(a->authorityRecords); 860 | jdns_list_delete(a->additionalRecords); 861 | if(a->raw_data) 862 | jdns_free(a->raw_data); 863 | jdns_object_free(a); 864 | } 865 | 866 | int jdns_packet_import(jdns_packet_t **a, const unsigned char *data, int size) 867 | { 868 | jdns_packet_t *tmp = 0; 869 | const unsigned char *buf; 870 | 871 | // need at least some data 872 | if(!data || size == 0) 873 | return 0; 874 | 875 | // header (id + options + item counts) is 12 bytes 876 | if(size < 12) 877 | goto error; 878 | 879 | tmp = jdns_packet_new(); 880 | buf = data; 881 | 882 | // id 883 | tmp->id = net2short(&buf); 884 | 885 | // options 886 | if(buf[0] & 0x80) // qr is bit 7 887 | tmp->opts.qr = 1; 888 | tmp->opts.opcode = (buf[0] & 0x78) >> 3; // opcode is bits 6,5,4,3 889 | if(buf[0] & 0x04) // aa is bit 2 890 | tmp->opts.aa = 1; 891 | if(buf[0] & 0x02) // tc is bit 1 892 | tmp->opts.tc = 1; 893 | if(buf[0] & 0x01) // rd is bit 0 894 | tmp->opts.rd = 1; 895 | if(buf[1] & 0x80) // ra is bit 7 (second byte) 896 | tmp->opts.ra = 1; 897 | if(buf[1] & 0x40) // z is bits 6 (second byte) 898 | tmp->opts.z = 1; 899 | // See RFC 4035 DNSSEC for AD and CD flags meaning. 900 | // See RFC 6895 for AD and CD flags. 901 | if(buf[1] & 0x20) // ad is bits 5 (second byte) 902 | tmp->opts.ad = 1; 903 | if(buf[1] & 0x10) // cd is bits 4 (second byte) 904 | tmp->opts.cd = 1; 905 | tmp->opts.rcode = buf[1] & 0x0f; // rcode is bits 3,2,1,0 906 | buf += 2; 907 | 908 | // item counts 909 | tmp->qdcount = net2short(&buf); 910 | tmp->ancount = net2short(&buf); 911 | tmp->nscount = net2short(&buf); 912 | tmp->arcount = net2short(&buf); 913 | 914 | // if these fail, we don't count them as errors, since the packet 915 | // might have been truncated 916 | if(!process_qsection(tmp->questions, tmp->qdcount, data, size, &buf)) 917 | goto skip; 918 | if(!process_rrsection(tmp->answerRecords, tmp->ancount, data, size, &buf)) 919 | goto skip; 920 | if(!process_rrsection(tmp->authorityRecords, tmp->nscount, data, size, &buf)) 921 | goto skip; 922 | if(!process_rrsection(tmp->additionalRecords, tmp->arcount, data, size, &buf)) 923 | goto skip; 924 | 925 | tmp->fully_parsed = 1; 926 | 927 | skip: 928 | // keep the raw data for reference during rdata parsing 929 | tmp->raw_size = size; 930 | tmp->raw_data = jdns_copy_array(data, size); 931 | 932 | *a = tmp; 933 | return 1; 934 | 935 | error: 936 | jdns_packet_delete(tmp); 937 | return 0; 938 | } 939 | 940 | int jdns_packet_export(jdns_packet_t *a, int maxsize) 941 | { 942 | unsigned char *block = 0; 943 | unsigned char *buf, *last; 944 | unsigned char c; 945 | int size; 946 | jdns_list_t *lookup = 0; // to hold jdns_packet_label_t 947 | 948 | // clear out any existing raw data before we begin 949 | if(a->raw_data) 950 | { 951 | jdns_free(a->raw_data); 952 | a->raw_data = 0; 953 | a->raw_size = 0; 954 | } 955 | 956 | // preallocate 957 | size = maxsize; 958 | block = (unsigned char *)jdns_alloc(size); 959 | memset(block, 0, size); 960 | 961 | buf = block; 962 | last = block + size; 963 | 964 | if(size < 12) 965 | goto error; 966 | 967 | short2net(a->id, &buf); 968 | if(a->opts.qr) 969 | buf[0] |= 0x80; 970 | c = (unsigned char)a->opts.opcode; 971 | buf[0] |= c << 3; 972 | if(a->opts.aa) 973 | buf[0] |= 0x04; 974 | if(a->opts.tc) 975 | buf[0] |= 0x02; 976 | if(a->opts.rd) 977 | buf[0] |= 0x01; 978 | if(a->opts.ra) 979 | buf[1] |= 0x80; 980 | if (a->opts.z) 981 | buf[1] |= 0x40; 982 | if (a->opts.ad) 983 | buf[1] |= 0x20; 984 | if (a->opts.cd) 985 | buf[1] |= 0x10; 986 | c = (unsigned char)a->opts.rcode; 987 | buf[1] |= c; 988 | buf += 2; 989 | short2net((unsigned short int)a->questions->count, &buf); 990 | short2net((unsigned short int)a->answerRecords->count, &buf); 991 | short2net((unsigned short int)a->authorityRecords->count, &buf); 992 | short2net((unsigned short int)a->additionalRecords->count, &buf); 993 | 994 | // append sections 995 | lookup = jdns_list_new(); 996 | lookup->autoDelete = 1; 997 | 998 | if(!append_qsection(a->questions, buf - block, last - buf, &buf, lookup)) 999 | goto error; 1000 | if(!append_rrsection(a->answerRecords, buf - block, last - buf, &buf, lookup)) 1001 | goto error; 1002 | if(!append_rrsection(a->authorityRecords, buf - block, last - buf, &buf, lookup)) 1003 | goto error; 1004 | if(!append_rrsection(a->additionalRecords, buf - block, last - buf, &buf, lookup)) 1005 | goto error; 1006 | 1007 | // done with all sections 1008 | jdns_list_delete(lookup); 1009 | 1010 | // condense 1011 | size = buf - block; 1012 | block = (unsigned char *)jdns_realloc(block, size); 1013 | 1014 | // finalize 1015 | a->qdcount = a->questions->count; 1016 | a->ancount = a->answerRecords->count; 1017 | a->nscount = a->authorityRecords->count; 1018 | a->arcount = a->additionalRecords->count; 1019 | a->raw_data = block; 1020 | a->raw_size = size; 1021 | 1022 | return 1; 1023 | 1024 | error: 1025 | jdns_list_delete(lookup); 1026 | if(block) 1027 | jdns_free(block); 1028 | return 0; 1029 | } 1030 | --------------------------------------------------------------------------------