diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 73957a23f..05ec00229 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -25,6 +25,7 @@ set(BUILD_WITH_SYSTEMD ON CACHE BOOL "Build with systemd") add_subdirectory(coost) add_subdirectory(zrpc) +add_subdirectory(QtZeroConf) if (CMAKE_SYSTEM MATCHES "Windows") diff --git a/3rdparty/QtZeroConf/.clang-format b/3rdparty/QtZeroConf/.clang-format new file mode 100644 index 000000000..0c0c2f2c4 --- /dev/null +++ b/3rdparty/QtZeroConf/.clang-format @@ -0,0 +1,117 @@ +--- +Language: Cpp +AlignConsecutiveMacros: AcrossEmptyLinesAndComments +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true +LambdaBodyIndentation: OuterScope +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Custom #Linux +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 180 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 1 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false +#SpaceBeforeAssignmentOperators: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: AlignWithSpaces +... + diff --git a/3rdparty/QtZeroConf/.gitignore b/3rdparty/QtZeroConf/.gitignore new file mode 100644 index 000000000..3f1b1d2b9 --- /dev/null +++ b/3rdparty/QtZeroConf/.gitignore @@ -0,0 +1,3 @@ +*.user +*.o +moc_* diff --git a/3rdparty/QtZeroConf/CMakeLists.txt b/3rdparty/QtZeroConf/CMakeLists.txt new file mode 100644 index 000000000..666091dc9 --- /dev/null +++ b/3rdparty/QtZeroConf/CMakeLists.txt @@ -0,0 +1,179 @@ +cmake_minimum_required(VERSION 3.4) +project(QtZeroConf VERSION 0.1.0) + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Network) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Network) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(PUBLIC_HEADERS + qzeroconfservice.h + qzeroconfglobal.h + qzeroconf.h +) +add_library(QtZeroConf + ${PUBLIC_HEADERS} + qzeroconfservice.cpp +) + +include(GNUInstallDirs) + +if(BUILD_SHARED_LIBS) + target_compile_definitions(QtZeroConf PRIVATE QT_BUILD_ZEROCONF_LIB) + set_target_properties(QtZeroConf PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION 0) +else() + target_compile_definitions(QtZeroConf PUBLIC QZEROCONF_STATIC) +endif() + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + find_library(avahi-client-lib avahi-client REQUIRED) + find_library(avahi-common-lib avahi-common REQUIRED) + find_path(avahi-client-includes avahi-client/client.h REQUIRED) + find_path(avahi-common-includes avahi-common/defs.h REQUIRED) + target_sources(QtZeroConf PRIVATE + avahi-qt/qt-watch_p.h + avahi-qt/qt-watch.cpp + avahiclient.cpp + ) + target_include_directories(QtZeroConf PRIVATE ${avahi-client-includes} ${avahi-common-includes}) + list(APPEND ${PUBLIC_HEADERS} + avahi-qt/qt-watch.h + ) + target_link_libraries(QtZeroConf PRIVATE ${avahi-client-lib} ${avahi-common-lib}) +endif() + +if(APPLE) + target_sources(QtZeroConf PRIVATE + bonjour_p.h + bonjour.cpp + ) + find_library(CoreServices CoreServices) + target_link_libraries(QtZeroConf PUBLIC ${CoreServices}) +endif() + +target_include_directories(QtZeroConf PUBLIC + $ + $ +) +target_link_libraries(QtZeroConf PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network) + +if(WIN32) + target_sources(QtZeroConf PRIVATE + bonjour_p.h + bonjour.cpp + bonjour-sdk/dnssd_clientlib.c + bonjour-sdk/dnssd_clientstub.c + bonjour-sdk/dnssd_ipc.c + ) + target_link_libraries(QtZeroConf PUBLIC ws2_32) + if(MSVC) + target_link_libraries(QtZeroConf PUBLIC "legacy_stdio_definitions.lib") + endif() + target_compile_definitions(QtZeroConf PRIVATE + -DWIN32 + -D_USRDLL + -DMDNS_DEBUGMSGS=0 + -DWIN32_LEAN_AND_MEAN + -DUSE_TCP_LOOPBACK + -D_NO_CRT_STDIO_INLINE + -D_CRT_SECURE_NO_DEPRECATE + -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1 + -DNOT_HAVE_SA_LEN) + target_include_directories(QtZeroConf PRIVATE "${CMAKE_CURRENT_LIST_DIR}/bonjour-sdk") +endif() + +if(ANDROID_AVAHI) + set(ACM "${CMAKE_CURRENT_LIST_DIR}/avahi-common") + set(ACR "${CMAKE_CURRENT_LIST_DIR}/avahi-core") + target_sources(QtZeroConf PRIVATE + qzeroconf.h + avahi-qt/qt-watch.h + avahi-qt/qt-watch_p.h + avahicore.cpp + avahi-qt/qt-watch.cpp + ${ACM}/address.c + ${ACM}/alternative.c + ${ACM}/domain.c + ${ACM}/error.c + ${ACM}/i18n.c + ${ACM}/malloc.c + ${ACM}/rlist.c + ${ACM}/simple-watch.c + ${ACM}/strlst.c + ${ACM}/thread-watch.c + ${ACM}/timeval.c + ${ACM}/utf8.c + ${ACR}/addr-util.c + ${ACR}/announce.c + ${ACR}/browse.c + ${ACR}/browse-dns-server.c + ${ACR}/browse-domain.c + ${ACR}/browse-service.c + ${ACR}/browse-service-type.c + ${ACR}/cache.c + ${ACR}/dns.c + ${ACR}/domain-util.c + ${ACR}/entry.c + ${ACR}/fdutil.c + ${ACR}/hashmap.c + ${ACR}/iface.c + ${ACR}/iface-linux.c + ${ACR}/log.c + ${ACR}/multicast-lookup.c + ${ACR}/netlink.c + ${ACR}/prioq.c + ${ACR}/probe-sched.c + ${ACR}/querier.c + ${ACR}/query-sched.c + ${ACR}/resolve-address.c + ${ACR}/resolve-host-name.c + ${ACR}/resolve-service.c + ${ACR}/response-sched.c + ${ACR}/rr.c + ${ACR}/rrlist.c + ${ACR}/server.c + ${ACR}/socket.c + ${ACR}/timeeventq.c + ${ACR}/util.c + ${ACR}/wide-area.c + ) + list(APPEND ${PUBLIC_HEADERS} + avahi-qt/qt-watch.h + ) + target_compile_definitions(QtZeroConf PRIVATE HAVE_STRLCPY GETTEXT_PACKAGE HAVE_NETLINK) +elseif(ANDROID) + target_sources(QtZeroConf PRIVATE + qzeroconf.h + androidnsd_p.h + androidnsd.cpp + ) + find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui) + target_link_libraries(QtZeroConf PUBLIC Qt${QT_VERSION_MAJOR}::Gui) + if (QT_VERSION_MAJOR EQUAL 5) + find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS AndroidExtras) + target_link_libraries(QtZeroConf PUBLIC Qt${QT_VERSION_MAJOR}::AndroidExtras) + endif () +endif() + +# install +set(INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE STRING "Installation directory for cmake config files") +set_target_properties(QtZeroConf PROPERTIES PUBLIC_HEADER + "${PUBLIC_HEADERS}" +) +install(TARGETS QtZeroConf + EXPORT QtZeroConfConfig + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} +) +export(TARGETS QtZeroConf + FILE ${CMAKE_CURRENT_BINARY_DIR}/QtZeroConfConfig.cmake +) +install(EXPORT QtZeroConfConfig + DESTINATION ${INSTALL_CMAKEDIR} +) + +set(BUILD_EXAMPLE false) +if(BUILD_EXAMPLE) + add_subdirectory(example) +endif() diff --git a/3rdparty/QtZeroConf/LICENSE b/3rdparty/QtZeroConf/LICENSE new file mode 100644 index 000000000..aed671a0c --- /dev/null +++ b/3rdparty/QtZeroConf/LICENSE @@ -0,0 +1,173 @@ + GNU LESSER GENERAL PUBLIC LICENSE + + The Qt Toolkit is Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). + Contact: http://www.qt-project.org/legal + + You may use, distribute and copy the Qt GUI Toolkit under the terms of + GNU Lesser General Public License version 3, which is displayed below. + +------------------------------------------------------------------------- + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright © 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies of this +licensedocument, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + +0. Additional Definitions. + + As used herein, “this License” refers to version 3 of the GNU Lesser +General Public License, and the “GNU GPL” refers to version 3 of the +GNU General Public License. + + “The Library” refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An “Application” is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A “Combined Work” is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the “Linked +Version”. + + The “Minimal Corresponding Source” for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The “Corresponding Application Code” for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort + to ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + +3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this + license document. + +4. Combined Works. + + You may convey a Combined Work under terms of your choice that, taken +together, effectively do not restrict modification of the portions of +the Library contained in the Combined Work and reverse engineering for +debugging such modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this + license document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of + this License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with + the Library. A suitable mechanism is one that (a) uses at run + time a copy of the Library already present on the user's + computer system, and (b) will operate properly with a modified + version of the Library that is interface-compatible with the + Linked Version. + + e) Provide Installation Information, but only if you would + otherwise be required to provide such information under section 6 + of the GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the Application + with a modified version of the Linked Version. (If you use option + 4d0, the Installation Information must accompany the Minimal + Corresponding Source and Corresponding Application Code. If you + use option 4d1, you must provide the Installation Information in + the manner specified by section 6 of the GNU GPL for conveying + Corresponding Source.) + +5. Combined Libraries. + + You may place library facilities that are a work based on the Library +side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities, conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of + it is a work based on the Library, and explaining where to find + the accompanying uncombined form of the same work. + +6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +as you received it specifies that a certain numbered version of the +GNU Lesser General Public License “or any later version” applies to +it, you have the option of following the terms and conditions either +of that published version or of any later version published by the +Free Software Foundation. If the Library as you received it does not +specify a version number of the GNU Lesser General Public License, +you may choose any version of the GNU Lesser General Public License +ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the Library. + diff --git a/3rdparty/QtZeroConf/QZeroConfNsdManager.java b/3rdparty/QtZeroConf/QZeroConfNsdManager.java new file mode 100644 index 000000000..2c419df7b --- /dev/null +++ b/3rdparty/QtZeroConf/QZeroConfNsdManager.java @@ -0,0 +1,217 @@ +/************************************************************************************************** +--------------------------------------------------------------------------------------------------- + Copyright (C) 2021 Jonathan Bagg + This file is part of QtZeroConf. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Jonathan Bagg nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------------------------------- + Project name : QtZeroConf + File name : QZeroConfNsdManager.java + Created : 10 Spetember 2021 + Author(s) : Michael Zanetti +--------------------------------------------------------------------------------------------------- + NsdManager wrapper for use on Android devices +--------------------------------------------------------------------------------------------------- +**************************************************************************************************/ + +package qtzeroconf; + +import java.util.Map; +import java.util.ArrayList; + +import android.util.Log; + +import android.content.Context; + +import android.net.nsd.NsdServiceInfo; +import android.net.nsd.NsdManager; + +public class QZeroConfNsdManager { + + public static native void onServiceResolvedJNI(long id, String name, String type, String hostname, String address, int port, Map txtRecords); + public static native void onServiceRemovedJNI(long id, String name); + public static native void onBrowserStateChangedJNI(long id, boolean running, boolean error); + public static native void onPublisherStateChangedJNI(long id, boolean running, boolean error); + public static native void onServiceNameChangedJNI(long id, String newName); + + private static String TAG = "QZeroConfNsdManager"; + private long id; + private Context context; + private NsdManager nsdManager; + private NsdManager.DiscoveryListener discoveryListener; + private NsdManager.RegistrationListener registrationListener; + private String registrationName; // The original service name that was given for registration, it might change on collisions + + // There can only be one resolver at a time per application, we'll need to queue the resolving + static private ArrayList resolverQueue = new ArrayList(); + static private NsdServiceInfo pendingResolve = null; + + public QZeroConfNsdManager(long id, Context context) { + super(); + this.id = id; + this.context = context; + + nsdManager = (NsdManager)context.getSystemService(Context.NSD_SERVICE); + discoveryListener = initializeDiscoveryListener(); + registrationListener = initializeRegistrationListener(); + } + + public void registerService(String name, String type, int port, Map txtRecords) { + registrationName = name; + + NsdServiceInfo serviceInfo = new NsdServiceInfo(); + serviceInfo.setServiceName(name); + serviceInfo.setServiceType(type); + serviceInfo.setPort(port); + for (Map.Entry entry: txtRecords.entrySet()) { + serviceInfo.setAttribute(entry.getKey(), entry.getValue()); + } + + try { + nsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Error registering service: " + e.toString()); + onPublisherStateChangedJNI(id, false, true); + } + } + + public void unregisterService() { + nsdManager.unregisterService(registrationListener); + } + + public void discoverServices(String serviceType) { + nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryListener); + } + + public void stopServiceDiscovery() { + nsdManager.stopServiceDiscovery(discoveryListener); + } + + private NsdManager.DiscoveryListener initializeDiscoveryListener() { + return new NsdManager.DiscoveryListener() { + + @Override + public void onDiscoveryStarted(String regType) { + QZeroConfNsdManager.onBrowserStateChangedJNI(id, true, false); + } + + @Override + public void onServiceFound(NsdServiceInfo service) { + enqueueResolver(service); + } + + @Override + public void onServiceLost(NsdServiceInfo serviceInfo) { + QZeroConfNsdManager.onServiceRemovedJNI(id, serviceInfo.getServiceName()); + } + + @Override + public void onDiscoveryStopped(String serviceType) { + QZeroConfNsdManager.onBrowserStateChangedJNI(id, false, false); + } + + @Override + public void onStartDiscoveryFailed(String serviceType, int errorCode) { + QZeroConfNsdManager.onBrowserStateChangedJNI(id, false, true); + } + + @Override + public void onStopDiscoveryFailed(String serviceType, int errorCode) { + QZeroConfNsdManager.onBrowserStateChangedJNI(id, false, true); + } + }; + } + + private NsdManager.ResolveListener initializeResolveListener() { + return new NsdManager.ResolveListener() { + + @Override + public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { + Log.w(TAG, "Resolving failed for: " + serviceInfo.getServiceName() + " " + serviceInfo.getServiceType() + ": " + errorCode); + if (errorCode == NsdManager.FAILURE_ALREADY_ACTIVE) { + enqueueResolver(pendingResolve); + } + pendingResolve = null; + processResolverQueue(); + } + + @Override + public void onServiceResolved(NsdServiceInfo serviceInfo) { + QZeroConfNsdManager.onServiceResolvedJNI(id, + serviceInfo.getServiceName(), + serviceInfo.getServiceType(), + serviceInfo.getHost().getHostName(), + serviceInfo.getHost().getHostAddress(), + serviceInfo.getPort(), + serviceInfo.getAttributes() + ); + pendingResolve = null; + processResolverQueue(); + } + }; + } + + public NsdManager.RegistrationListener initializeRegistrationListener() { + return new NsdManager.RegistrationListener() { + + @Override + public void onServiceRegistered(NsdServiceInfo serviceInfo) { + QZeroConfNsdManager.onPublisherStateChangedJNI(id, true, false); + if (!serviceInfo.getServiceName().equals(registrationName)) { + onServiceNameChangedJNI(id, serviceInfo.getServiceName()); + } + } + + @Override + public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { + QZeroConfNsdManager.onPublisherStateChangedJNI(id, false, true); + } + + @Override + public void onServiceUnregistered(NsdServiceInfo arg0) { + QZeroConfNsdManager.onPublisherStateChangedJNI(id, false, false); + } + + @Override + public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { + QZeroConfNsdManager.onPublisherStateChangedJNI(id, false, true); + } + }; + } + + private void enqueueResolver(NsdServiceInfo serviceInfo) { + resolverQueue.add(serviceInfo); + processResolverQueue(); + } + + private void processResolverQueue() { + if (resolverQueue.isEmpty()) { + return; + } + if (pendingResolve != null) { + return; + } + pendingResolve = resolverQueue.get(0); + resolverQueue.remove(0); + nsdManager.resolveService(pendingResolve, initializeResolveListener()); + } +} diff --git a/3rdparty/QtZeroConf/README.md b/3rdparty/QtZeroConf/README.md new file mode 100644 index 000000000..e959c781c --- /dev/null +++ b/3rdparty/QtZeroConf/README.md @@ -0,0 +1,166 @@ + +QZeroConf is a Qt wrapper class for ZeroConf libraries across various platforms. + +* Windows (requires iTunes or [Apple Print Services](https://support.apple.com/kb/DL999)) +* Mac +* Linux +* Android +* iOS + +QZeroConf wraps avahi-client on Linux, Network Discovery Service (java) on Android, and dnssd on Mac, iOS and Windows. + +### Building + +QZeroConf can be built directly into your project if your project is [LGPL3](http://www.gnu.org/licenses/lgpl-3.0.en.html) compatible. If your project is closed source, you can build QZeroConf as a dynamic library and link against it. + +#### Building into your project + +1. Clone or download QZeroConf. If you download, unzip. +2. Copy the qtzeroconf directory to be under your project's directory. +3. Include the qtzeroconf.pri file in your projects .pro file + + include(qtzeroconf/qtzeroconf.pri) + +4. Add QZEROCONF_STATIC define in your projects .pro file + + DEFINES= QZEROCONF_STATIC + +#### Compiling as a dynamic library + +1. Clone or download QZeroConf. If you download, unzip. +2. Enter the qtzeroconf directory, run qmake and then make. + +#### Building with CMake +Use `BUILD_SHARED_LIBS` to control whether QZeroConf should be built as static (`-DBUILD_SHARED_LIBS=OFF`) or as shared (`-DBUILD_SHARED_LIBS=ON`) library. +The default is `OFF`. + +You can also build the included example project by setting `BUILD_EXAMPLE` to `ON`. +The default for this is `OFF` + +#### Android + +Prior to Android api 30, QtZeroConf used AvaliCore. AvaliCore no longer works >= api 30 as bind() to netlink sockets was disabled in Android. QtZeroConf now uses the Android java Network Discovery Services. NDS is slightly buggy, but more or less gets the job done. A common issue with NDS is that if the app is in sleep mode and a service is removed on another device, the app does not get notified the service was removed when it wakes back up. ANDROID_PACKAGE_SOURCE_DIR must be added to your app's .pro file. + +``` +ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android +``` +QZeroConfNsdManager.java must then be copied or linked to $$PWD/android/src/QZeroConfNsdManager.java Notice the extra src/ ...gradle expects this. + +### API + +#### Service Publishing + +(See the example included with the source) + +1) Include header + +```c++ +#include "qzeroconf.h" +``` +2) Create an instance of QZeroConf + +```c++ +QZeroConf zeroConf; +``` +It is recommend, but not required, that you connect a slot to QZeroConf's error() signal and servicePublished() signal. + +3) If you want to add one or more txt records to the service, call +```c++ +zeroConf.addServiceTxtRecord("name", "value"); +``` +or +```c++ +zeroConf.addServiceTxtRecord("nameOnly"); +``` +before calling startServicePublish() + +4) Call startServicePublish() with the name, type, domain and port of your service. + +```c++ +zeroConf.startServicePublish("Test", "_test._tcp", "local", 12345); +``` +QZeroConf will emit servicePublished() if successful, or the error() signal if registration fails. + +Service publishing can be stopped by calling stopServicePublish(). +Only one service can be published per instance of QZeroConf. + +#### Service Discovery + +(See the example included with the source) + +1) Include header + +```c++ +#include "qzeroconf.h" +``` +2) Create an instance of QZeroConf + +```c++ +QZeroConf zeroConf; +``` +It is recommend, but not required, that you connect a slot to QZeroConf's error() signal. + +3) Connect a slot to QZeroConf's serviceAdded() signal. When serviceAdded() is emitted, it passes the QZeroConfService recently discovered. QZeroConfServices are [shared objects](http://doc.qt.io/qt-5/implicit-sharing.html). They are safe to use between threads. + +4) Optionally connect a slot to QZeroConf's serviceRemoved() signal to received status when the service is unpublished. ServiceRemoved() passes the QZeroConfService being removed. + +5) Call startBrowser() with the type of the service to browse for and optionally the protocol to use. + +```c++ +startBrowser("_test._tcp"); +``` +If you are browsing for services published using both ipv4 and ipv6 ( QAbstractSocket::AnyIPProtocol) you should also connect a slot to QzeroConf's serviceUpdated() signal. When the IP address of the first protocol is resolved, serviceAdded() is emitted, when the IP address of the second protocol is resolved, serviceUpdated() is emitted. + +Only one browser can be in use per instance of QzeroConf. + +**Txt records** are placed into a QMap called txt within the discovered service. For example, the value of txt record "Qt=The Best!" can be retrieved with the code... + +```c++ +qDebug() << zcs->txt["Qt"]; +``` +**QML** + +QZeroConf can be used in QML applications + + +### Build Dependencies + +Qt5 or Qt6 + +On Linux, libavahi-client-dev and libavahi-common-dev + +### Distribution Requirements + + +Distributing Software / Apps that use QtZeroConf must follow the requirements of the LGPLv3. Some of my interpretations of the LGPLv3 + + +* LGPLv3 text or a link to the LGPLv3 text must be distributed with the binary. My preferred way to do this is in the App's "About" section. +* An offer of source code with a link to QtZeroConf must be distributed with the binary. My preferred way to do this is in the App's "About" section +* For Android and iOS apps only, instructions on how to re-link or re-package the App or a link to instructions must be distributed with the binary. My preferred way to do this is in the App's "About" section. + + +All of the above must be shown to the user at least once, separate from the EULA, with a method for the user to acknowledge they have seen it (an ok button). Ideally all of the above is also listed in the description of the App on the App Store. + +### Apple App Store deployment + +Publishing closed source Apps that use a LGPLv3 library in the Apple App Store must provide a method for the end user to 1. update the library in the app and 2. run the new version of the app with the updated library. Qt on iOS further complicates this by using static linking. Closed source Apps on iOS using QtZeroConf must provide the apps object files along with clear step by step instructions on how to re-link the app with a new / different version of QtZeroConf (obligation 1). iOS end uses can run the re-linked App on their device by creating a free iOS developer account and use the time limited signing on that account for their device. (obligation 2) I consider this an poor way to meet obligation 2, but as long as Apple has this mechanism, obligation 2 is meet. I will not pursue copyright infringement as long as the individual / organization is meeting obligation 1 and 2 and the Distribution Requirements above. + +### iOS device sleep + +When iOS puts the device to sleep, it breaks the DNS-SD browser and service publisher. The only way around this is to call stopServicePublish() and stopBrowser() when the application state changes to Qt::ApplicationSuspended (sleep) and then call startPublish() and startBrowser() when the application state changes to Qt::ApplicationActive (wake). See appStateChanged() in example. + +### iOS 14 and up + +iOS 14 and up requires apps to have permissions to access the local network. See [this video](https://developer.apple.com/videos/play/wwdc2020/10110/) Two keys must be added to the info.plist.... + +```xml +1. NSLocalNetworkUsageDescription + This app will need access to the local network for Discovery services. +2. NSBonjourServices + + _myservice1._tcp + _myservice2._tcp + +``` + diff --git a/3rdparty/QtZeroConf/androidnsd.cpp b/3rdparty/QtZeroConf/androidnsd.cpp new file mode 100644 index 000000000..69de66b7c --- /dev/null +++ b/3rdparty/QtZeroConf/androidnsd.cpp @@ -0,0 +1,364 @@ +/************************************************************************************************** +--------------------------------------------------------------------------------------------------- + Copyright (C) 2015-2021 Jonathan Bagg + This file is part of QtZeroConf. + + QtZeroConf is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QtZeroConf is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with QtZeroConf. If not, see . +--------------------------------------------------------------------------------------------------- + Project name : QtZeroConf + File name : androidnsd.cpp + Created : 10 Spetember 2021 + Author(s) : Michael Zanetti +--------------------------------------------------------------------------------------------------- + NsdManager wrapper for use on Android devices +--------------------------------------------------------------------------------------------------- +**************************************************************************************************/ +#include +#include +#include "androidnsd_p.h" +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +using QAndroidJniEnvironment = QJniEnvironment; +#endif + +Q_DECLARE_METATYPE(QHostAddress) + +static QMutex s_instancesMutex; +static QList s_instances; + + +QZeroConfPrivate::QZeroConfPrivate(QZeroConf *parent) +{ + qRegisterMetaType(); + qRegisterMetaType("TxtRecordMap"); + + pub = parent; + + QAndroidJniEnvironment env; + + JNINativeMethod methods[] { + { "onServiceResolvedJNI", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/util/Map;)V", (void*)QZeroConfPrivate::onServiceResolvedJNI }, + { "onServiceRemovedJNI", "(JLjava/lang/String;)V", (void*)QZeroConfPrivate::onServiceRemovedJNI }, + { "onBrowserStateChangedJNI", "(JZZ)V", (void*)QZeroConfPrivate::onBrowserStateChangedJNI }, + { "onPublisherStateChangedJNI", "(JZZ)V", (void*)QZeroConfPrivate::onPublisherStateChangedJNI }, + { "onServiceNameChangedJNI", "(JLjava/lang/String;)V", (void*)QZeroConfPrivate::onServiceNameChangedJNI } + }; + + // There seems to be no straight forward way to match the "thiz" pointer from JNI calls to our pointer of the Java class + // Passing "this" as ID down to Java so we can access "this" in callbacks. + // Note: needs to be quint64 as uintptr_t might be 32 or 64 bit depending on the system, while Java expects a jlong which is always 64 bit. +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + nsdManager = QAndroidJniObject("qtzeroconf/QZeroConfNsdManager", "(JLandroid/content/Context;)V", reinterpret_cast(this), QtAndroid::androidActivity().object()); +#else + nsdManager = QAndroidJniObject("qtzeroconf/QZeroConfNsdManager", "(JLandroid/content/Context;)V", reinterpret_cast(this), QNativeInterface::QAndroidApplication::context()); +#endif + if (nsdManager.isValid()) { + jclass objectClass = env->GetObjectClass(nsdManager.object()); + env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0])); + env->DeleteLocalRef(objectClass); + } + + QMutexLocker locker(&s_instancesMutex); + s_instances.append(this); +} + +QZeroConfPrivate::~QZeroConfPrivate() +{ + QMutexLocker locker(&s_instancesMutex); + s_instances.removeAll(this); +} + +// In order to not having to pay attention to only use thread safe methods on the java side, we're only running +// Java calls on the Android thread. +// To make sure the Java object is not going out of scope and being garbage collected when the QZeroConf object +// is deleted before the worker thread actually starts, keep a new QAndroidJniObject to nsdManager +// which will increase the ref counter in the JVM. +void QZeroConfPrivate::startServicePublish(const char *name, const char *type, quint16 port) +{ + QAndroidJniObject ref(nsdManager); + publishName = name; + publishType = type; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QtAndroid::runOnAndroidThread([=](){ +#else + QNativeInterface::QAndroidApplication::runOnAndroidMainThread([=]() { +#endif + QAndroidJniObject txtMap("java/util/HashMap"); + foreach (const QByteArray &key, txtRecords.keys()) { + txtMap.callObjectMethod("put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", + QAndroidJniObject::fromString(key).object(), + QAndroidJniObject::fromString(txtRecords.value(key)).object()); + } + + ref.callMethod("registerService", "(Ljava/lang/String;Ljava/lang/String;ILjava/util/Map;)V", + QAndroidJniObject::fromString(publishName).object(), + QAndroidJniObject::fromString(publishType).object(), + port, + txtMap.object()); + }); +} + +void QZeroConfPrivate::stopServicePublish() +{ + QAndroidJniObject ref(nsdManager); + // If Android is on it's way to suspend when stopServicePublish() is called, we need to call nsd.unregisterService() synchronously + // to force it to run before the device goes to sleep. If instead it is scheduled to run in the Android thread, it will not run + // until the device is woken back up. + if (qGuiApp->applicationState() == Qt::ApplicationSuspended) { + ref.callMethod("unregisterService"); + } else { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QtAndroid::runOnAndroidThread([ref]() { +#else + QNativeInterface::QAndroidApplication::runOnAndroidMainThread([ref]() { +#endif + ref.callMethod("unregisterService"); + }); + } +} + +void QZeroConfPrivate::startBrowser(QString type, QAbstractSocket::NetworkLayerProtocol protocol) +{ + Q_UNUSED(protocol) + QAndroidJniObject ref(nsdManager); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QtAndroid::runOnAndroidThread([ref, type]() { +#else + QNativeInterface::QAndroidApplication::runOnAndroidMainThread([ref, type]() { +#endif + ref.callMethod("discoverServices", "(Ljava/lang/String;)V", QAndroidJniObject::fromString(type).object()); + }); +} + +void QZeroConfPrivate::stopBrowser() +{ + QAndroidJniObject ref(nsdManager); + // If Android is on it's way to suspend when stopBrowser() is called, we need to call nsd.stopServiceDiscovery() synchronously + // to force it to run before the device goes to sleep. + if (qGuiApp->applicationState() == Qt::ApplicationSuspended) { + ref.callMethod("stopServiceDiscovery"); + } else { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QtAndroid::runOnAndroidThread([ref]() { +#else + QNativeInterface::QAndroidApplication::runOnAndroidMainThread([ref]() { +#endif + ref.callMethod("stopServiceDiscovery"); + }); + } +} + +// Callbacks will come in from the android thread. So we're never accessing any of our members directly but instead +// propagate callbacks through Qt::QueuedConnection invokes into the Qt thread. Be sure to check if the instance is still +// alive by checking s_instances while holding the mutex before scheduling the invokation. +void QZeroConfPrivate::onServiceResolvedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jstring name, jstring type, jstring hostname, jstring address, jint port, jobject txtRecords) +{ + QMap txtMap; + QAndroidJniObject txt(txtRecords); + QAndroidJniObject txtKeys = txt.callObjectMethod("keySet", "()Ljava/util/Set;").callObjectMethod("toArray", "()[Ljava/lang/Object;"); + + QAndroidJniEnvironment env; + for (int i = 0; i < txt.callMethod("size"); i++) { + QAndroidJniObject key = QAndroidJniObject(env->GetObjectArrayElement(txtKeys.object(), i)); + QAndroidJniObject valueObj = txt.callObjectMethod("get", "(Ljava/lang/Object;)Ljava/lang/Object;", key.object()); + if (valueObj.isValid()) { + jboolean isCopy; + jbyte* b = env->GetByteArrayElements(valueObj.object(), &isCopy); + QByteArray value((char *)b, env->GetArrayLength(valueObj.object())); + env->ReleaseByteArrayElements(valueObj.object(), b, JNI_ABORT); + txtMap.insert(key.toString().toUtf8(), value); + } else { + txtMap.insert(key.toString().toUtf8(), QByteArray()); + } + } + + QZeroConfPrivate *ref = reinterpret_cast(id); + QMutexLocker locker(&s_instancesMutex); + if (!s_instances.contains(ref)) { + return; + } + QMetaObject::invokeMethod(ref, "onServiceResolved", Qt::QueuedConnection, + Q_ARG(QString, QAndroidJniObject(name).toString()), + Q_ARG(QString, QAndroidJniObject(type).toString()), + Q_ARG(QString, QAndroidJniObject(hostname).toString()), + Q_ARG(QHostAddress, QHostAddress(QAndroidJniObject(address).toString())), + Q_ARG(int, port), + Q_ARG(TxtRecordMap, txtMap) + ); + +} + +void QZeroConfPrivate::onServiceRemovedJNI(JNIEnv */*env*/, jobject /*this*/, jlong id, jstring name) +{ + QZeroConfPrivate *ref = reinterpret_cast(id); + QMutexLocker locker(&s_instancesMutex); + if (!s_instances.contains(ref)) { + return; + } + QMetaObject::invokeMethod(ref, "onServiceRemoved", Qt::QueuedConnection, Q_ARG(QString, QAndroidJniObject(name).toString())); +} + + +void QZeroConfPrivate::onBrowserStateChangedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jboolean running, jboolean error) +{ + QZeroConfPrivate *ref = reinterpret_cast(id); + QMutexLocker locker(&s_instancesMutex); + if (!s_instances.contains(ref)) { + return; + } + QMetaObject::invokeMethod(ref, "onBrowserStateChanged", Qt::QueuedConnection, Q_ARG(bool, running), Q_ARG(bool, error)); +} + +void QZeroConfPrivate::onPublisherStateChangedJNI(JNIEnv */*env*/, jobject /*this*/, jlong id, jboolean running, jboolean error) +{ + QZeroConfPrivate *ref = reinterpret_cast(id); + QMutexLocker locker(&s_instancesMutex); + if (!s_instances.contains(ref)) { + return; + } + QMetaObject::invokeMethod(ref, "onPublisherStateChanged", Qt::QueuedConnection, Q_ARG(bool, running), Q_ARG(bool, error)); +} + +void QZeroConfPrivate::onServiceNameChangedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jstring newName) +{ + QZeroConfPrivate *ref = reinterpret_cast(id); + QMutexLocker locker(&s_instancesMutex); + if (!s_instances.contains(ref)) { + return; + } + QMetaObject::invokeMethod(ref, "onServiceNameChanged", Qt::QueuedConnection, Q_ARG(QString, QAndroidJniObject(newName).toString())); +} + +void QZeroConfPrivate::onServiceResolved(const QString &name, const QString &type, const QString &hostname, const QHostAddress &address, int port, const TxtRecordMap &txtRecords) +{ + QZeroConfService zcs; + bool newRecord = false; + if (pub->services.contains(name)) { + zcs = pub->services.value(name); + } else { + zcs = QZeroConfService(new QZeroConfServiceData); + newRecord = true; + } + + zcs->m_name = name; + zcs->m_type = type; + // A previous implementation (based on avahi) returned service type as "_http._tcp" but Android API return "._http._tcp" + // Stripping leading dot for backwards compatibility. FIXME: Still not in line with bonjour, which adds a trailing dot. + zcs->m_type.remove(QRegularExpression("^.")); + zcs->m_host = hostname; + zcs->m_port = port; + zcs->m_ip = address; + zcs->m_txt = txtRecords; + + // Those are not available on Androids NsdManager + // zcs->m_domain = domain; + // zcs->m_interfaceIndex = interface; + + if (newRecord) { + pub->services.insert(name, zcs); + emit pub->serviceAdded(zcs); + } else { + emit pub->serviceUpdated(zcs); + } +} + +void QZeroConfPrivate::onServiceRemoved(const QString &name) +{ + if (pub->services.contains(name)) { + QZeroConfService service = pub->services.take(name); + emit pub->serviceRemoved(service); + } +} + +void QZeroConfPrivate::onBrowserStateChanged(bool running, bool error) +{ + browserExists = running; + if (error) { + emit pub->error(QZeroConf::browserFailed); + } +} + +void QZeroConfPrivate::onPublisherStateChanged(bool running, bool error) +{ + publisherExists = running; + if (running) { + emit pub->servicePublished(); + } + if (error) { + emit pub->error(QZeroConf::serviceRegistrationFailed); + } +} + +void QZeroConfPrivate::onServiceNameChanged(const QString &newName) +{ + emit pub->serviceNameChanged(newName); +} + + +QZeroConf::QZeroConf(QObject *parent) : QObject(parent) +{ + pri = new QZeroConfPrivate(this); + qRegisterMetaType("QZeroConfService"); +} + +QZeroConf::~QZeroConf() +{ + delete pri; +} + +void QZeroConf::startServicePublish(const char *name, const char *type, const char *domain, quint16 port, quint32 interface) +{ + Q_UNUSED(domain) // Not supported on Android API + Q_UNUSED(interface) // Not supported on Android API + pri->startServicePublish(name, type, port); +} + +void QZeroConf::stopServicePublish(void) +{ + pri->stopServicePublish(); +} + +bool QZeroConf::publishExists(void) +{ + return pri->publisherExists; +} + +void QZeroConf::addServiceTxtRecord(QString nameOnly) +{ + pri->txtRecords.insert(nameOnly.toUtf8(), QByteArray()); +} + +void QZeroConf::addServiceTxtRecord(QString name, QString value) +{ + pri->txtRecords.insert(name.toUtf8(), value.toUtf8()); +} + +void QZeroConf::clearServiceTxtRecords() +{ + pri->txtRecords.clear(); +} + +void QZeroConf::startBrowser(QString type, QAbstractSocket::NetworkLayerProtocol protocol) +{ + pri->startBrowser(type, protocol); +} + +void QZeroConf::stopBrowser(void) +{ + pri->stopBrowser(); +} + +bool QZeroConf::browserExists(void) +{ + return pri->browserExists; +} diff --git a/3rdparty/QtZeroConf/androidnsd_p.h b/3rdparty/QtZeroConf/androidnsd_p.h new file mode 100644 index 000000000..916110c8f --- /dev/null +++ b/3rdparty/QtZeroConf/androidnsd_p.h @@ -0,0 +1,73 @@ +/************************************************************************************************** +--------------------------------------------------------------------------------------------------- + Copyright (C) 2015-2021 Jonathan Bagg + This file is part of QtZeroConf. + + QtZeroConf is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QtZeroConf is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with QtZeroConf. If not, see . +--------------------------------------------------------------------------------------------------- + Project name : QtZeroConf + File name : androidnsd_p.h + Created : 10 Spetember 2021 + Author(s) : Michael Zanetti +--------------------------------------------------------------------------------------------------- + NsdManager wrapper for use on Android devices +--------------------------------------------------------------------------------------------------- +**************************************************************************************************/ +#include "qzeroconf.h" +#include + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +#include +#include +#else +#include +using QAndroidJniObject = QJniObject; +#endif + +class QZeroConfPrivate: QObject +{ + Q_OBJECT +public: + typedef QMap TxtRecordMap; + + QZeroConfPrivate(QZeroConf *parent); + ~QZeroConfPrivate(); + void startServicePublish(const char *name, const char *type, quint16 port); + void stopServicePublish(); + void startBrowser(QString type, QAbstractSocket::NetworkLayerProtocol protocol); + void stopBrowser(); + static void onServiceResolvedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jstring name, jstring type, jstring hostname, jstring address, jint port, jobject txtRecords); + static void onServiceRemovedJNI(JNIEnv */*env*/, jobject /*this*/, jlong id, jstring name); + static void onBrowserStateChangedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jboolean running, jboolean error); + static void onPublisherStateChangedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jboolean running, jboolean error); + static void onServiceNameChangedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jstring newName); + + QZeroConf *pub; + QAndroidJniObject nsdManager; + + bool browserExists = false; + bool publisherExists = false; + QMap txtRecords; + QString publishName; + QString publishType; + + +private slots: + void onServiceResolved(const QString &name, const QString &type, const QString &hostname, const QHostAddress &address, int port, const TxtRecordMap &txtRecords); + void onServiceRemoved(const QString &name); + void onBrowserStateChanged(bool running, bool error); + void onPublisherStateChanged(bool running, bool error); + void onServiceNameChanged(const QString &newName); +}; diff --git a/3rdparty/QtZeroConf/avahi-common/address.c b/3rdparty/QtZeroConf/avahi-common/address.c new file mode 100644 index 000000000..e8f61489f --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/address.c @@ -0,0 +1,155 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "address.h" +#include "malloc.h" + +static size_t address_get_size(const AvahiAddress *a) { + assert(a); + + if (a->proto == AVAHI_PROTO_INET) + return 4; + else if (a->proto == AVAHI_PROTO_INET6) + return 16; + + return 0; +} + +int avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b) { + assert(a); + assert(b); + + if (a->proto != b->proto) + return -1; + + return memcmp(a->data.data, b->data.data, address_get_size(a)); +} + +char *avahi_address_snprint(char *s, size_t length, const AvahiAddress *a) { + assert(s); + assert(length); + assert(a); + + if (!(inet_ntop(avahi_proto_to_af(a->proto), a->data.data, s, length))) + return NULL; + + return s; +} + +char* avahi_reverse_lookup_name(const AvahiAddress *a, char *ret_s, size_t length) { + assert(ret_s); + assert(length > 0); + assert(a); + + if (a->proto == AVAHI_PROTO_INET) { + uint32_t n = ntohl(a->data.ipv4.address); + snprintf( + ret_s, length, + "%u.%u.%u.%u.in-addr.arpa", + n & 0xFF, (n >> 8) & 0xFF, (n >> 16) & 0xFF, n >> 24); + } else { + assert(a->proto == AVAHI_PROTO_INET6); + + snprintf( + ret_s, length, + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa", + a->data.ipv6.address[15] & 0xF, a->data.ipv6.address[15] >> 4, + a->data.ipv6.address[14] & 0xF, a->data.ipv6.address[14] >> 4, + a->data.ipv6.address[13] & 0xF, a->data.ipv6.address[13] >> 4, + a->data.ipv6.address[12] & 0xF, a->data.ipv6.address[12] >> 4, + a->data.ipv6.address[11] & 0xF, a->data.ipv6.address[11] >> 4, + a->data.ipv6.address[10] & 0xF, a->data.ipv6.address[10] >> 4, + a->data.ipv6.address[ 9] & 0xF, a->data.ipv6.address[ 9] >> 4, + a->data.ipv6.address[ 8] & 0xF, a->data.ipv6.address[ 8] >> 4, + a->data.ipv6.address[ 7] & 0xF, a->data.ipv6.address[ 7] >> 4, + a->data.ipv6.address[ 6] & 0xF, a->data.ipv6.address[ 6] >> 4, + a->data.ipv6.address[ 5] & 0xF, a->data.ipv6.address[ 5] >> 4, + a->data.ipv6.address[ 4] & 0xF, a->data.ipv6.address[ 4] >> 4, + a->data.ipv6.address[ 3] & 0xF, a->data.ipv6.address[ 3] >> 4, + a->data.ipv6.address[ 2] & 0xF, a->data.ipv6.address[ 2] >> 4, + a->data.ipv6.address[ 1] & 0xF, a->data.ipv6.address[ 1] >> 4, + a->data.ipv6.address[ 0] & 0xF, a->data.ipv6.address[ 0] >> 4); + } + + return ret_s; +} + +AvahiAddress *avahi_address_parse(const char *s, AvahiProtocol proto, AvahiAddress *ret_addr) { + assert(ret_addr); + assert(s); + + if (proto == AVAHI_PROTO_UNSPEC) { + if (inet_pton(AF_INET, s, ret_addr->data.data) <= 0) { + if (inet_pton(AF_INET6, s, ret_addr->data.data) <= 0) + return NULL; + else + ret_addr->proto = AVAHI_PROTO_INET6; + } else + ret_addr->proto = AVAHI_PROTO_INET; + } else { + if (inet_pton(avahi_proto_to_af(proto), s, ret_addr->data.data) <= 0) + return NULL; + + ret_addr->proto = proto; + } + + return ret_addr; +} + +int avahi_proto_to_af(AvahiProtocol proto) { + if (proto == AVAHI_PROTO_INET) + return AF_INET; + if (proto == AVAHI_PROTO_INET6) + return AF_INET6; + + assert(proto == AVAHI_PROTO_UNSPEC); + return AF_UNSPEC; +} + +AvahiProtocol avahi_af_to_proto(int af) { + if (af == AF_INET) + return AVAHI_PROTO_INET; + if (af == AF_INET6) + return AVAHI_PROTO_INET6; + + assert(af == AF_UNSPEC); + return AVAHI_PROTO_UNSPEC; +} + +const char* avahi_proto_to_string(AvahiProtocol proto) { + if (proto == AVAHI_PROTO_INET) + return "IPv4"; + if (proto == AVAHI_PROTO_INET6) + return "IPv6"; + + assert(proto == AVAHI_PROTO_UNSPEC); + return "UNSPEC"; +} diff --git a/3rdparty/QtZeroConf/avahi-common/address.h b/3rdparty/QtZeroConf/avahi-common/address.h new file mode 100644 index 000000000..a14104fad --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/address.h @@ -0,0 +1,119 @@ +#ifndef fooaddresshfoo +#define fooaddresshfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file address.h Definitions and functions to manipulate IP addresses. */ + +#include +#include + +#include + +AVAHI_C_DECL_BEGIN + +/** Protocol family specification, takes the values AVAHI_PROTO_INET, AVAHI_PROTO_INET6, AVAHI_PROTO_UNSPEC */ +typedef int AvahiProtocol; + +/** Numeric network interface index. Takes OS dependent values and the special constant AVAHI_IF_UNSPEC */ +typedef int AvahiIfIndex; + +/** Values for AvahiProtocol */ +enum { + AVAHI_PROTO_INET = 0, /**< IPv4 */ + AVAHI_PROTO_INET6 = 1, /**< IPv6 */ + AVAHI_PROTO_UNSPEC = -1 /**< Unspecified/all protocol(s) */ +}; + +/** Special values for AvahiIfIndex */ +enum { + AVAHI_IF_UNSPEC = -1 /**< Unspecified/all interface(s) */ +}; + +/** Maximum size of an address in string form */ +#define AVAHI_ADDRESS_STR_MAX 40 /* IPv6 Max = 4*8 + 7 + 1 for NUL */ + +/** Return TRUE if the specified interface index is valid */ +#define AVAHI_IF_VALID(ifindex) (((ifindex) >= 0) || ((ifindex) == AVAHI_IF_UNSPEC)) + +/** Return TRUE if the specified protocol is valid */ +#define AVAHI_PROTO_VALID(protocol) (((protocol) == AVAHI_PROTO_INET) || ((protocol) == AVAHI_PROTO_INET6) || ((protocol) == AVAHI_PROTO_UNSPEC)) + +/** An IPv4 address */ +typedef struct AvahiIPv4Address { + uint32_t address; /**< Address data in network byte order. */ +} AvahiIPv4Address; + +/** An IPv6 address */ +typedef struct AvahiIPv6Address { + uint8_t address[16]; /**< Address data */ +} AvahiIPv6Address; + +/** Protocol (address family) independent address structure */ +typedef struct AvahiAddress { + AvahiProtocol proto; /**< Address family */ + + union { + AvahiIPv6Address ipv6; /**< Address when IPv6 */ + AvahiIPv4Address ipv4; /**< Address when IPv4 */ + uint8_t data[1]; /**< Type-independent data field */ + } data; +} AvahiAddress; + +/** @{ \name Comparison */ + +/** Compare two addresses. Returns 0 when equal, a negative value when a < b, a positive value when a > b. */ +int avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b); + +/** @} */ + +/** @{ \name String conversion */ + +/** Convert the specified address *a to a human readable character string, use AVAHI_ADDRESS_STR_MAX to allocate an array of the right size */ +char *avahi_address_snprint(char *ret_s, size_t length, const AvahiAddress *a); + +/** Convert the specified human readable character string to an + * address structure. Set af to AVAHI_UNSPEC for automatic address + * family detection. */ +AvahiAddress *avahi_address_parse(const char *s, AvahiProtocol af, AvahiAddress *ret_addr); + +/** @} */ + +/** \cond fulldocs */ +/** Generate the DNS reverse lookup name for an IPv4 or IPv6 address. */ +char* avahi_reverse_lookup_name(const AvahiAddress *a, char *ret_s, size_t length); +/** \endcond */ + +/** @{ \name Protocol/address family handling */ + +/** Map AVAHI_PROTO_xxx constants to Unix AF_xxx constants */ +int avahi_proto_to_af(AvahiProtocol proto); + +/** Map Unix AF_xxx constants to AVAHI_PROTO_xxx constants */ +AvahiProtocol avahi_af_to_proto(int af); + +/** Return a textual representation of the specified protocol number. i.e. "IPv4", "IPv6" or "UNSPEC" */ +const char* avahi_proto_to_string(AvahiProtocol proto); + +/** @} */ + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/alternative.c b/3rdparty/QtZeroConf/avahi-common/alternative.c new file mode 100644 index 000000000..b3d39f0ed --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/alternative.c @@ -0,0 +1,182 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "alternative.h" +#include "malloc.h" +#include "domain.h" +#include "utf8.h" + +static void drop_incomplete_utf8(char *c) { + char *e; + + e = strchr(c, 0) - 1; + + while (e >= c) { + + if (avahi_utf8_valid(c)) + break; + + assert(*e & 128); + *e = 0; + + e--; + } +} + +char *avahi_alternative_host_name(const char *s) { + const char *e; + char *r; + + assert(s); + + if (!avahi_is_valid_host_name(s)) + return NULL; + + if ((e = strrchr(s, '-'))) { + const char *p; + + e++; + + for (p = e; *p; p++) + if (!isdigit(*p)) { + e = NULL; + break; + } + + if (e && (*e == '0' || *e == 0)) + e = NULL; + } + + if (e) { + char *c, *m; + size_t l; + int n; + + n = atoi(e)+1; + if (!(m = avahi_strdup_printf("%i", n))) + return NULL; + + l = e-s-1; + + if (l >= AVAHI_LABEL_MAX-1-strlen(m)-1) + l = AVAHI_LABEL_MAX-1-strlen(m)-1; + + if (!(c = avahi_strndup(s, l))) { + avahi_free(m); + return NULL; + } + + drop_incomplete_utf8(c); + + r = avahi_strdup_printf("%s-%s", c, m); + avahi_free(c); + avahi_free(m); + + } else { + char *c; + + if (!(c = avahi_strndup(s, AVAHI_LABEL_MAX-1-2))) + return NULL; + + drop_incomplete_utf8(c); + + r = avahi_strdup_printf("%s-2", c); + avahi_free(c); + } + + assert(avahi_is_valid_host_name(r)); + + return r; +} + +char *avahi_alternative_service_name(const char *s) { + const char *e; + char *r; + + assert(s); + + if (!avahi_is_valid_service_name(s)) + return NULL; + + if ((e = strstr(s, " #"))) { + const char *n, *p; + e += 2; + + while ((n = strstr(e, " #"))) + e = n + 2; + + for (p = e; *p; p++) + if (!isdigit(*p)) { + e = NULL; + break; + } + + if (e && (*e == '0' || *e == 0)) + e = NULL; + } + + if (e) { + char *c, *m; + size_t l; + int n; + + n = atoi(e)+1; + if (!(m = avahi_strdup_printf("%i", n))) + return NULL; + + l = e-s-2; + + if (l >= AVAHI_LABEL_MAX-1-strlen(m)-2) + l = AVAHI_LABEL_MAX-1-strlen(m)-2; + + if (!(c = avahi_strndup(s, l))) { + avahi_free(m); + return NULL; + } + + drop_incomplete_utf8(c); + + r = avahi_strdup_printf("%s #%s", c, m); + avahi_free(c); + avahi_free(m); + } else { + char *c; + + if (!(c = avahi_strndup(s, AVAHI_LABEL_MAX-1-3))) + return NULL; + + drop_incomplete_utf8(c); + + r = avahi_strdup_printf("%s #2", c); + avahi_free(c); + } + + assert(avahi_is_valid_service_name(r)); + + return r; +} diff --git a/3rdparty/QtZeroConf/avahi-common/alternative.h b/3rdparty/QtZeroConf/avahi-common/alternative.h new file mode 100644 index 000000000..9b044de58 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/alternative.h @@ -0,0 +1,43 @@ +#ifndef fooalternativehfoo +#define fooalternativehfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file alternative.h Functions to find alternative names for hosts and services in the case of name collision */ + +#include + +AVAHI_C_DECL_BEGIN + +/** Find an alternative for the specified host name. If called with an + * original host name, "-2" is appended, afterwards the number is + * increased on each call. (i.e. "foo" becomes "foo-2" becomes "foo-3" + * and so on.) avahi_free() the result. */ +char *avahi_alternative_host_name(const char *s); + +/** Find an alternative for the specified service name. If called with + * an original service name, " #2" is appended. Afterwards the number + * is increased on each call (i.e. "foo" becomes "foo #2" becomes "foo + * #3" and so on.) avahi_free() the result. */ +char *avahi_alternative_service_name(const char *s); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/cdecl.h b/3rdparty/QtZeroConf/avahi-common/cdecl.h new file mode 100644 index 000000000..aef6aba0e --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/cdecl.h @@ -0,0 +1,38 @@ +#ifndef foocdeclhfoo +#define foocdeclhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file cdecl.h C++ compatibility */ +#ifdef __cplusplus +/** If using C++ this macro enables C mode, otherwise does nothing */ +#define AVAHI_C_DECL_BEGIN extern "C" { +/** If using C++ this macro switches back to C++ mode, otherwise does nothing */ +#define AVAHI_C_DECL_END } + +#else +/** If using C++ this macro enables C mode, otherwise does nothing */ +#define AVAHI_C_DECL_BEGIN +/** If using C++ this macro switches back to C++ mode, otherwise does nothing */ +#define AVAHI_C_DECL_END + +#endif + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/defs.h b/3rdparty/QtZeroConf/avahi-common/defs.h new file mode 100644 index 000000000..bb73a9dfd --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/defs.h @@ -0,0 +1,356 @@ +#ifndef foodefshfoo +#define foodefshfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file defs.h Some common definitions */ + +#include + +/** \mainpage + * + * \section choose_api Choosing an API + * + * Avahi provides three programming APIs for integration of + * mDNS/DNS-SD features into your C progams: + * + * \li avahi-core: an API for embedding a complete mDNS/DNS-SD stack + * into your software. This is intended for developers of embedded + * appliances only. We dissuade from using this API in normal desktop + * applications since it is not a good idea to run multiple mDNS + * stacks simultaneously on the same host. + * \li the D-Bus API: an extensive D-Bus interface for browsing and + * registering mDNS/DNS-SD services using avahi-daemon. We recommend + * using this API for software written in any language other than + * C (e.g. Python). + * \li avahi-client: a simplifying C wrapper around the D-Bus API. We + * recommend using this API in C or C++ progams. The D-Bus internals + * are hidden completely. + * \li avahi-gobject: an object-oriented C wrapper based on + * GLib's GObject. We recommd using this API for GNOME/Gtk programs. + * + * All three APIs are very similar, however avahi-core is the most powerful. + * + * In addition to the three APIs described above Avahi supports two + * compatibility libraries: + * + * \li avahi-compat-libdns_sd: the original Bonjour API as documented + * in the header file "dns_sd.h" by Apple Computer, Inc. + * + * \li avahi-compat-howl: the HOWL API as released with HOWL 0.9.8 by + * Porchdog Software. + * + * Please note that these compatibility layers are incomplete and + * generally a waste of resources. We strongly encourage everyone to + * use our native APIs for newly written programs and to port older + * programs to avahi-client! + * + * The native APIs (avahi-client and avahi-core) can be integrated + * into external event loops. We provide adapters for the following + * event loop implementations: + * + * \li avahi-glib: The GLIB main loop as used by GTk+/GNOME + * + * \li avahi-qt: The Qt main loop as used by Qt/KDE + * + * Finally, we provide a high-level Gtk+ GUI dialog called + * avahi-ui for user-friendly browsing for services. + * + * The doxygen-generated API documentation covers avahi-client + * (including its auxiliary APIs), the event loop adapters and + * avahi-ui. For the other APIs please consult the original + * documentation (for the compatibility APIs) or the header files. + * + * Please note that the doxygen-generated API documentation of the + * native Avahi API is not complete. A few definitions that are part + * of the Avahi API have been removed from this documentation, either + * because they are only relevant in a very few low-level applications + * or because they are considered obsolete. Please consult the C header + * files for all definitions that are part of the Avahi API. Please + * note that these hidden definitions are considered part of the Avahi + * API and will stay available in the API in the future. + * + * \section error_reporting Error Reporting + * + * Some notes on the Avahi error handling: + * + * - Error codes are negative integers and defined as AVAHI_ERR_xx + * - If a function returns some kind of non-negative integer value on + * success, a failure is indicated by returning the error code + * directly. + * - If a function returns a pointer of some kind on success, a + * failure is indicated by returning NULL + * - The last error number may be retrieved by calling + * avahi_client_errno() + * - Just like the libc errno variable the Avahi errno is NOT reset to + * AVAHI_OK if a function call succeeds. + * - You may convert a numeric error code into a human readable string + * using avahi_strerror() + * - The constructor function avahi_client_new() returns the error + * code in a call-by-reference argument + * + * \section event_loop Event Loop Abstraction + * + * Avahi uses a simple event loop abstraction layer. A table AvahiPoll + * which contains function pointers for user defined timeout and I/O + * condition event source implementations needs to be passed to + * avahi_client_new(). An adapter for this abstraction layer is + * available for the GLib main loop in the object AvahiGLibPoll. A + * simple stand-alone implementation is available under the name + * AvahiSimplePoll. An adpater for the Qt main loop is available from + * avahi_qt_poll_get(). + * + * \section good_publish How to Register Services + * + * - Subscribe to server state changes. Pass a callback function + * pointer to avahi_client_new(). It will be called + * whenever the server state changes. + * - Only register your services when the server is in state + * AVAHI_SERVER_RUNNING. If you register your services in other server + * states they might not be accessible since the local host name might not necessarily + * be established. + * - Remove your services when the server enters + * AVAHI_SERVER_COLLISION or AVAHI_SERVER_REGISTERING state. Your + * services may not be reachable anymore since the local host name is + * no longer established or is currently in the process of being + * established. + * - When registering services, use the following algorithm: + * - Create a new entry group (i.e. avahi_entry_group_new()) + * - Add your service(s)/additional RRs/subtypes (e.g. avahi_entry_group_add_service()) + * - Commit the entry group (i.e. avahi_entry_group_commit()) + * - Subscribe to entry group state changes. + * - If the entry group enters AVAHI_ENTRY_GROUP_COLLISION state the + * services of the entry group are automatically removed from the + * server. You may immediately add your services back to the entry + * group (but with new names, perhaps using + * avahi_alternative_service_name()) and commit again. Please do not + * free the entry group and create a new one. This would inhibit some + * traffic limiting algorithms in mDNS. + * - When you need to modify your services (i.e. change the TXT data + * or the port number), use the AVAHI_PUBLISH_UPDATE flag. Please do + * not free the entry group and create a new one. This would inhibit + * some traffic limiting algorithms in mDNS. When changing just the + * TXT data avahi_entry_group_update_txt() is a shortcut for + * AVAHI_PUBLISH_UPDATE. Please note that you cannot use + * AVAHI_PUBLISH_UPDATE when changing the service name! Renaming a + * DNS-SD service is identical to deleting and creating a new one, and + * that's exactly what you should do in that case. First call + * avahi_entry_group_reset() to remove it and then read it normally. + * + * \section good_browse How to Browse for Services + * + * - For normal applications you need to call avahi_service_browser_new() + * for the service type you want to browse for. Use + * avahi_service_resolver_new() to acquire service data for a service + * name. + * - You can use avahi_domain_browser_new() to get a list of announced + * browsing domains. Please note that not all domains whith services + * on the LAN are mandatorily announced. + * - There is no need to subscribe to server state changes. + * + * \section daemon_dies How to Write a Client That Can Deal with Daemon Restarts + * + * With Avahi it is possible to write client applications that can + * deal with Avahi daemon restarts. To accomplish that make sure to + * pass AVAHI_CLIENT_NO_FAIL to avahi_client_new()'s flags + * parameter. That way avahi_client_new() will succeed even when the + * daemon is not running. In that case the object will enter + * AVAHI_CLIENT_CONNECTING state. As soon as the daemon becomes + * available the object will enter one of the AVAHI_CLIENT_S_xxx + * states. Make sure to not create browsers or entry groups before the + * client object has entered one of those states. As usual you will be + * informed about state changes with the callback function supplied to + * avahi_client_new(). If the client is forced to disconnect from the + * server it will enter AVAHI_CLIENT_FAILURE state with + * avahi_client_errno() == AVAHI_ERR_DISCONNECTED. Free the + * AvahiClient object in that case (and all its associated objects + * such as entry groups and browser objects prior to that) and + * reconnect to the server anew - again with passing + * AVAHI_CLIENT_NO_FAIL to avahi_client_new(). + * + * We encourage implementing this in all software where service + * discovery is not an integral part of application. e.g. use it in + * all kinds of background daemons, but not necessarily in software + * like iChat compatible IM software. + * + * For now AVAHI_CLIENT_NO_FAIL cannot deal with D-Bus daemon restarts. + * + * \section domains How to Deal Properly with Browsing Domains + * + * Due to the introduction of wide-area DNS-SD the correct handling of + * domains becomes more important for Avahi enabled applications. All + * applications that offer the user a list of services discovered with + * Avahi should offer some kind of editable drop down box where the + * user can either enter his own domain or select one of those offered + * by AvahiDomainBrowser. The default domain to browse should be the + * one returned by avahi_client_get_domain_name(). The list of domains + * returned by AvahiDomainBrowser is assembled by the browsing domains + * configured in the daemon's configuration file, the domains + * announced inside the default domain, the domains set with the + * environment variable $AVAHI_BROWSE_DOMAINS (colon-seperated) on the + * client side and the domains set in the XDG configuration file + * ~/.config/avahi/browse-domains on the client side (seperated by + * newlines). File managers offering some kind of "Network + * Neighborhood" folder should show the entries of the default domain + * right inside that and offer subfolders for the browsing domains + * returned by AvahiDomainBrowser. + */ + +AVAHI_C_DECL_BEGIN + +/** @{ \name States */ + +/** States of a server object */ +typedef enum { + AVAHI_SERVER_INVALID, /**< Invalid state (initial) */ + AVAHI_SERVER_REGISTERING, /**< Host RRs are being registered */ + AVAHI_SERVER_RUNNING, /**< All host RRs have been established */ + AVAHI_SERVER_COLLISION, /**< There is a collision with a host RR. All host RRs have been withdrawn, the user should set a new host name via avahi_server_set_host_name() */ + AVAHI_SERVER_FAILURE /**< Some fatal failure happened, the server is unable to proceed */ +} AvahiServerState; + +/** States of an entry group object */ +typedef enum { + AVAHI_ENTRY_GROUP_UNCOMMITED, /**< The group has not yet been commited, the user must still call avahi_entry_group_commit() */ + AVAHI_ENTRY_GROUP_REGISTERING, /**< The entries of the group are currently being registered */ + AVAHI_ENTRY_GROUP_ESTABLISHED, /**< The entries have successfully been established */ + AVAHI_ENTRY_GROUP_COLLISION, /**< A name collision for one of the entries in the group has been detected, the entries have been withdrawn */ + AVAHI_ENTRY_GROUP_FAILURE /**< Some kind of failure happened, the entries have been withdrawn */ +} AvahiEntryGroupState; + +/** @} */ + +/** @{ \name Flags */ + +/** Some flags for publishing functions */ +typedef enum { + AVAHI_PUBLISH_UNIQUE = 1, /**< For raw records: The RRset is intended to be unique */ + AVAHI_PUBLISH_NO_PROBE = 2, /**< For raw records: Though the RRset is intended to be unique no probes shall be sent */ + AVAHI_PUBLISH_NO_ANNOUNCE = 4, /**< For raw records: Do not announce this RR to other hosts */ + AVAHI_PUBLISH_ALLOW_MULTIPLE = 8, /**< For raw records: Allow multiple local records of this type, even if they are intended to be unique */ +/** \cond fulldocs */ + AVAHI_PUBLISH_NO_REVERSE = 16, /**< For address records: don't create a reverse (PTR) entry */ + AVAHI_PUBLISH_NO_COOKIE = 32, /**< For service records: do not implicitly add the local service cookie to TXT data */ +/** \endcond */ + AVAHI_PUBLISH_UPDATE = 64, /**< Update existing records instead of adding new ones */ +/** \cond fulldocs */ + AVAHI_PUBLISH_USE_WIDE_AREA = 128, /**< Register the record using wide area DNS (i.e. unicast DNS update) */ + AVAHI_PUBLISH_USE_MULTICAST = 256 /**< Register the record using multicast DNS */ +/** \endcond */ +} AvahiPublishFlags; + +/** Some flags for lookup functions */ +typedef enum { +/** \cond fulldocs */ + AVAHI_LOOKUP_USE_WIDE_AREA = 1, /**< Force lookup via wide area DNS */ + AVAHI_LOOKUP_USE_MULTICAST = 2, /**< Force lookup via multicast DNS */ +/** \endcond */ + AVAHI_LOOKUP_NO_TXT = 4, /**< When doing service resolving, don't lookup TXT record */ + AVAHI_LOOKUP_NO_ADDRESS = 8 /**< When doing service resolving, don't lookup A/AAAA record */ +} AvahiLookupFlags; + +/** Some flags for lookup callback functions */ +typedef enum { + AVAHI_LOOKUP_RESULT_CACHED = 1, /**< This response originates from the cache */ + AVAHI_LOOKUP_RESULT_WIDE_AREA = 2, /**< This response originates from wide area DNS */ + AVAHI_LOOKUP_RESULT_MULTICAST = 4, /**< This response originates from multicast DNS */ + AVAHI_LOOKUP_RESULT_LOCAL = 8, /**< This record/service resides on and was announced by the local host. Only available in service and record browsers and only on AVAHI_BROWSER_NEW. */ + AVAHI_LOOKUP_RESULT_OUR_OWN = 16, /**< This service belongs to the same local client as the browser object. Only available in avahi-client, and only for service browsers and only on AVAHI_BROWSER_NEW. */ + AVAHI_LOOKUP_RESULT_STATIC = 32 /**< The returned data has been defined statically by some configuration option */ +} AvahiLookupResultFlags; + +/** @} */ + +/** @{ \name Events */ + +/** Type of callback event when browsing */ +typedef enum { + AVAHI_BROWSER_NEW, /**< The object is new on the network */ + AVAHI_BROWSER_REMOVE, /**< The object has been removed from the network */ + AVAHI_BROWSER_CACHE_EXHAUSTED, /**< One-time event, to notify the user that all entries from the caches have been sent */ + AVAHI_BROWSER_ALL_FOR_NOW, /**< One-time event, to notify the user that more records will probably not show up in the near future, i.e. all cache entries have been read and all static servers been queried */ + AVAHI_BROWSER_FAILURE /**< Browsing failed due to some reason which can be retrieved using avahi_server_errno()/avahi_client_errno() */ +} AvahiBrowserEvent; + +/** Type of callback event when resolving */ +typedef enum { + AVAHI_RESOLVER_FOUND, /**< RR found, resolving successful */ + AVAHI_RESOLVER_FAILURE /**< Resolving failed due to some reason which can be retrieved using avahi_server_errno()/avahi_client_errno() */ +} AvahiResolverEvent; + +/** @} */ + +/** @{ \name Other definitions */ + +/** The type of domain to browse for */ +typedef enum { + AVAHI_DOMAIN_BROWSER_BROWSE, /**< Browse for a list of available browsing domains */ + AVAHI_DOMAIN_BROWSER_BROWSE_DEFAULT, /**< Browse for the default browsing domain */ + AVAHI_DOMAIN_BROWSER_REGISTER, /**< Browse for a list of available registering domains */ + AVAHI_DOMAIN_BROWSER_REGISTER_DEFAULT, /**< Browse for the default registering domain */ + AVAHI_DOMAIN_BROWSER_BROWSE_LEGACY, /**< Legacy browse domain - see DNS-SD spec for more information */ + AVAHI_DOMAIN_BROWSER_MAX +} AvahiDomainBrowserType; + +/** @} */ + +/** \cond fulldocs */ +/** For every service a special TXT item is implicitly added, which + * contains a random cookie which is private to the local daemon. This + * can be used by clients to determine if two services on two + * different subnets are effectively the same. */ +#define AVAHI_SERVICE_COOKIE "org.freedesktop.Avahi.cookie" + +/** In invalid cookie as special value */ +#define AVAHI_SERVICE_COOKIE_INVALID (0) +/** \endcond fulldocs */ + +/** @{ \name DNS RR definitions */ + +/** DNS record types, see RFC 1035 */ +enum { + AVAHI_DNS_TYPE_A = 0x01, + AVAHI_DNS_TYPE_NS = 0x02, + AVAHI_DNS_TYPE_CNAME = 0x05, + AVAHI_DNS_TYPE_SOA = 0x06, + AVAHI_DNS_TYPE_PTR = 0x0C, + AVAHI_DNS_TYPE_HINFO = 0x0D, + AVAHI_DNS_TYPE_MX = 0x0F, + AVAHI_DNS_TYPE_TXT = 0x10, + AVAHI_DNS_TYPE_AAAA = 0x1C, + AVAHI_DNS_TYPE_SRV = 0x21 +}; + +/** DNS record classes, see RFC 1035 */ +enum { + AVAHI_DNS_CLASS_IN = 0x01 /**< Probably the only class we will ever use */ +}; + +/** @} */ + +/** The default TTL for RRs which contain a host name of some kind. */ +#define AVAHI_DEFAULT_TTL_HOST_NAME (120) + +/** The default TTL for all other records. */ +#define AVAHI_DEFAULT_TTL (75*60) + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/domain.c b/3rdparty/QtZeroConf/avahi-common/domain.c new file mode 100644 index 000000000..3b1ab6834 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/domain.c @@ -0,0 +1,609 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "domain.h" +#include "malloc.h" +#include "error.h" +#include "address.h" +#include "utf8.h" + +/* Read the first label from string *name, unescape "\" and write it to dest */ +char *avahi_unescape_label(const char **name, char *dest, size_t size) { + unsigned i = 0; + char *d; + + assert(dest); + assert(size > 0); + assert(name); + + d = dest; + + for (;;) { + if (i >= size) + return NULL; + + if (**name == '.') { + (*name)++; + break; + } + + if (**name == 0) + break; + + if (**name == '\\') { + /* Escaped character */ + + (*name) ++; + + if (**name == 0) + /* Ending NUL */ + return NULL; + + else if (**name == '\\' || **name == '.') { + /* Escaped backslash or dot */ + *(d++) = *((*name) ++); + i++; + } else if (isdigit(**name)) { + int n; + + /* Escaped literal ASCII character */ + + if (!isdigit(*(*name+1)) || !isdigit(*(*name+2))) + return NULL; + + n = ((uint8_t) (**name - '0') * 100) + ((uint8_t) (*(*name+1) - '0') * 10) + ((uint8_t) (*(*name +2) - '0')); + + if (n > 255 || n == 0) + return NULL; + + *(d++) = (char) n; + i++; + + (*name) += 3; + } else + return NULL; + + } else { + + /* Normal character */ + + *(d++) = *((*name) ++); + i++; + } + } + + assert(i < size); + + *d = 0; + + if (!avahi_utf8_valid(dest)) + return NULL; + + return dest; +} + +/* Escape "\" and ".", append \0 */ +char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size) { + char *r; + + assert(src); + assert(ret_name); + assert(*ret_name); + assert(ret_size); + assert(*ret_size > 0); + + r = *ret_name; + + while (src_length > 0) { + if (*src == '.' || *src == '\\') { + + /* Dot or backslash */ + + if (*ret_size < 3) + return NULL; + + *((*ret_name) ++) = '\\'; + *((*ret_name) ++) = *src; + (*ret_size) -= 2; + + } else if ( + *src == '_' || + *src == '-' || + (*src >= '0' && *src <= '9') || + (*src >= 'a' && *src <= 'z') || + (*src >= 'A' && *src <= 'Z')) { + + /* Proper character */ + + if (*ret_size < 2) + return NULL; + + *((*ret_name)++) = *src; + (*ret_size) --; + + } else { + + /* Everything else */ + + if (*ret_size < 5) + return NULL; + + *((*ret_name) ++) = '\\'; + *((*ret_name) ++) = '0' + (char) ((uint8_t) *src / 100); + *((*ret_name) ++) = '0' + (char) (((uint8_t) *src / 10) % 10); + *((*ret_name) ++) = '0' + (char) ((uint8_t) *src % 10); + + (*ret_size) -= 4; + } + + src_length --; + src++; + } + + **ret_name = 0; + + return r; +} + +char *avahi_normalize_name(const char *s, char *ret_s, size_t size) { + int empty = 1; + char *r; + + assert(s); + assert(ret_s); + assert(size > 0); + + r = ret_s; + *ret_s = 0; + + while (*s) { + char label[AVAHI_LABEL_MAX]; + + if (!(avahi_unescape_label(&s, label, sizeof(label)))) + return NULL; + + if (label[0] == 0) { + + if (*s == 0 && empty) + return ret_s; + + return NULL; + } + + if (!empty) { + if (size < 1) + return NULL; + + *(r++) = '.'; + size--; + + } else + empty = 0; + + avahi_escape_label(label, strlen(label), &r, &size); + } + + return ret_s; +} + +char *avahi_normalize_name_strdup(const char *s) { + char t[AVAHI_DOMAIN_NAME_MAX]; + assert(s); + + if (!(avahi_normalize_name(s, t, sizeof(t)))) + return NULL; + + return avahi_strdup(t); +} + +int avahi_domain_equal(const char *a, const char *b) { + assert(a); + assert(b); + + if (a == b) + return 1; + + for (;;) { + char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *r; + + r = avahi_unescape_label(&a, ca, sizeof(ca)); + assert(r); + r = avahi_unescape_label(&b, cb, sizeof(cb)); + assert(r); + + if (strcasecmp(ca, cb)) + return 0; + + if (!*a && !*b) + return 1; + } + + return 1; +} + +int avahi_is_valid_service_type_generic(const char *t) { + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) + return 0; + + do { + char label[AVAHI_LABEL_MAX]; + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; + + if (strlen(label) <= 2 || label[0] != '_') + return 0; + + } while (*t); + + return 1; +} + +int avahi_is_valid_service_type_strict(const char *t) { + char label[AVAHI_LABEL_MAX]; + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) + return 0; + + /* Application name */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; + + if (strlen(label) <= 2 || label[0] != '_') + return 0; + + if (!*t) + return 0; + + /* _tcp or _udp boilerplate */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; + + if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp")) + return 0; + + if (*t) + return 0; + + return 1; +} + +const char *avahi_get_type_from_subtype(const char *t) { + char label[AVAHI_LABEL_MAX]; + const char *ret; + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) + return NULL; + + /* Subtype name */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return NULL; + + if (strlen(label) <= 2 || label[0] != '_') + return NULL; + + if (!*t) + return NULL; + + /* String "_sub" */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return NULL; + + if (strcasecmp(label, "_sub")) + return NULL; + + if (!*t) + return NULL; + + ret = t; + + /* Application name */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return NULL; + + if (strlen(label) <= 2 || label[0] != '_') + return NULL; + + if (!*t) + return NULL; + + /* _tcp or _udp boilerplate */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return NULL; + + if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp")) + return NULL; + + if (*t) + return NULL; + + return ret; +} + +int avahi_is_valid_service_subtype(const char *t) { + assert(t); + + return !!avahi_get_type_from_subtype(t); +} + +int avahi_is_valid_domain_name(const char *t) { + int is_first = 1; + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX) + return 0; + + do { + char label[AVAHI_LABEL_MAX]; + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; + + /* Explicitly allow the root domain name */ + if (is_first && label[0] == 0 && *t == 0) + return 1; + + is_first = 0; + + if (label[0] == 0) + return 0; + + } while (*t); + + return 1; +} + +int avahi_is_valid_service_name(const char *t) { + assert(t); + + if (strlen(t) >= AVAHI_LABEL_MAX || !*t) + return 0; + + return 1; +} + +int avahi_is_valid_host_name(const char *t) { + char label[AVAHI_LABEL_MAX]; + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) + return 0; + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; + + if (strlen(label) < 1) + return 0; + + if (*t) + return 0; + + return 1; +} + +unsigned avahi_domain_hash(const char *s) { + unsigned hash = 0; + + while (*s) { + char c[AVAHI_LABEL_MAX], *p, *r; + + r = avahi_unescape_label(&s, c, sizeof(c)); + assert(r); + + for (p = c; *p; p++) + hash = 31 * hash + tolower(*p); + } + + return hash; +} + +int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain) { + char escaped_name[AVAHI_LABEL_MAX*4]; + char normalized_type[AVAHI_DOMAIN_NAME_MAX]; + char normalized_domain[AVAHI_DOMAIN_NAME_MAX]; + + assert(p); + + /* Validity checks */ + + if ((name && !avahi_is_valid_service_name(name))) + return AVAHI_ERR_INVALID_SERVICE_NAME; + + if (!avahi_is_valid_service_type_generic(type)) + return AVAHI_ERR_INVALID_SERVICE_TYPE; + + if (!avahi_is_valid_domain_name(domain)) + return AVAHI_ERR_INVALID_DOMAIN_NAME; + + /* Preparation */ + + if (name) { + size_t l = sizeof(escaped_name); + char *e = escaped_name, *r; + r = avahi_escape_label(name, strlen(name), &e, &l); + assert(r); + } + + if (!(avahi_normalize_name(type, normalized_type, sizeof(normalized_type)))) + return AVAHI_ERR_INVALID_SERVICE_TYPE; + + if (!(avahi_normalize_name(domain, normalized_domain, sizeof(normalized_domain)))) + return AVAHI_ERR_INVALID_DOMAIN_NAME; + + /* Concatenation */ + + snprintf(p, size, "%s%s%s.%s", name ? escaped_name : "", name ? "." : "", normalized_type, normalized_domain); + + return AVAHI_OK; +} + +#ifndef HAVE_STRLCPY + +static size_t strlcpy(char *dest, const char *src, size_t n) { + assert(dest); + assert(src); + + if (n > 0) { + strncpy(dest, src, n-1); + dest[n-1] = 0; + } + + return strlen(src); +} + +#endif + +int avahi_service_name_split(const char *p, char *name, size_t name_size, char *type, size_t type_size, char *domain, size_t domain_size) { + enum { + NAME, + TYPE, + DOMAIN + } state; + int type_empty = 1, domain_empty = 1; + + assert(p); + assert(type); + assert(type_size > 0); + assert(domain); + assert(domain_size > 0); + + if (name) { + assert(name_size > 0); + *name = 0; + state = NAME; + } else + state = TYPE; + + *type = *domain = 0; + + while (*p) { + char buf[64]; + + if (!(avahi_unescape_label(&p, buf, sizeof(buf)))) + return -1; + + switch (state) { + case NAME: + strlcpy(name, buf, name_size); + state = TYPE; + break; + + case TYPE: + + if (buf[0] == '_') { + + if (!type_empty) { + if (!type_size) + return AVAHI_ERR_NO_MEMORY; + + *(type++) = '.'; + type_size --; + + } else + type_empty = 0; + + if (!(avahi_escape_label(buf, strlen(buf), &type, &type_size))) + return AVAHI_ERR_NO_MEMORY; + + break; + } + + state = DOMAIN; + /* fall through */ + + case DOMAIN: + + if (!domain_empty) { + if (!domain_size) + return AVAHI_ERR_NO_MEMORY; + + *(domain++) = '.'; + domain_size --; + } else + domain_empty = 0; + + if (!(avahi_escape_label(buf, strlen(buf), &domain, &domain_size))) + return AVAHI_ERR_NO_MEMORY; + + break; + } + } + + return 0; +} + +int avahi_is_valid_fqdn(const char *t) { + char label[AVAHI_LABEL_MAX]; + char normalized[AVAHI_DOMAIN_NAME_MAX]; + const char *k = t; + AvahiAddress a; + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX) + return 0; + + if (!avahi_is_valid_domain_name(t)) + return 0; + + /* Check if there are at least two labels*/ + if (!(avahi_unescape_label(&k, label, sizeof(label)))) + return 0; + + if (label[0] == 0 || !k) + return 0; + + if (!(avahi_unescape_label(&k, label, sizeof(label)))) + return 0; + + if (label[0] == 0 || !k) + return 0; + + /* Make sure that the name is not an IP address */ + if (!(avahi_normalize_name(t, normalized, sizeof(normalized)))) + return 0; + + if (avahi_address_parse(normalized, AVAHI_PROTO_UNSPEC, &a)) + return 0; + + return 1; +} diff --git a/3rdparty/QtZeroConf/avahi-common/domain.h b/3rdparty/QtZeroConf/avahi-common/domain.h new file mode 100644 index 000000000..c3995d304 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/domain.h @@ -0,0 +1,129 @@ +#ifndef foodomainhfoo +#define foodomainhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file domain.h Domain name handling functions */ + +#include +#include + +#include + +AVAHI_C_DECL_BEGIN + +/** The maximum length of a a fully escaped domain name C string. This + * is calculated like this: RFC1034 mandates maximum length of FQDNs + * is 255. The maximum label length is 63. To minimize the number of + * (non-escaped) dots, we comprise our maximum-length domain name of + * four labels á 63 characters plus three inner dots. Escaping the + * four labels quadruples their length at maximum. An escaped domain + * name has the therefore the maximum length of 63*4*4+3=1011. A + * trailing NUL and perhaps two unnecessary dots leading and trailing + * the string brings us to 1014. */ +#define AVAHI_DOMAIN_NAME_MAX 1014 + +/** Maximum size of an unescaped label */ +#define AVAHI_LABEL_MAX 64 + +/** @{ \name Normalization */ + +/** Normalize a domain name into canonical form. This drops trailing + * dots and removes useless backslash escapes. */ +char *avahi_normalize_name(const char *s, char *ret_s, size_t size); + +/** Normalize a domain name into canonical form. This drops trailing + * dots and removes useless backslash escapes. avahi_free() the + * result! */ +char *avahi_normalize_name_strdup(const char *s); + +/** @} */ + +/** @{ \name Comparison */ + +/** Return 1 when the specified domain names are equal, 0 otherwise */ +int avahi_domain_equal(const char *a, const char *b); + +/** Return some kind of hash value for the domain, useful for using domains as hash table keys. */ +unsigned avahi_domain_hash(const char *name); + +/** @} */ + +/** @{ \name Escaping */ + +/** Read the first label from the textual domain name *name, unescape + * it and write it to dest, *name is changed to point to the next label*/ +char *avahi_unescape_label(const char **name, char *dest, size_t size); + +/** Escape the domain name in *src and write it to *ret_name */ +char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size); + +/** @} */ + +/** @{ \name Validity Checks */ + +/** Return 1 when the specified string contains a valid generic DNS-SD + * service type (i.e. a series of words starting with "_"), 0 + * otherwise */ +int avahi_is_valid_service_type_generic(const char *t); + +/** Return 1 when the specified string contains a valid strict DNS-SD + * service type (i.e. consisting of only two words, the latter being + * either _udp or _tcp), 0 otherwise */ +int avahi_is_valid_service_type_strict(const char *t); + +/** Return 1 when the specified string contains a valid DNS-SD service + * subtype, 0 otherwise */ +int avahi_is_valid_service_subtype(const char *t); + +/** Return 1 when the specified string contains a valid domain name, 0 otherwise */ +int avahi_is_valid_domain_name(const char *t); + +/** Return 1 when the specified string contains a valid DNS-SD service name, 0 otherwise */ +int avahi_is_valid_service_name(const char *t); + +/** Return 1 when the specified string contains a valid non-FQDN host name (i.e. without dots), 0 otherwise */ +int avahi_is_valid_host_name(const char *t); + +/** Return 1 when the specified string contains a valid FQDN host name (i.e. with more than one label and non-numerical), 0 otherwise. \since 0.6.9 */ +int avahi_is_valid_fqdn(const char *t); + +/** @} */ + +/** @{ \name DNS-SD service name handling */ + +/** Construct a valid complete DNS-SD service name from a name, a type and a domain */ +int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain); + +/** Split a full service name into name, type and domain */ +int avahi_service_name_split(const char *p, char *name, size_t name_size, char *type, size_t type_size, char *domain, size_t domain_size); + +/** @} */ + +/** @{ \name DNS-SD Subtype handling */ + +/** Return a pointer to the type section of a subtype i.e. _foo._sub._bar._tcp => _bar._tcp */ +const char *avahi_get_type_from_subtype(const char *t); + +/** @} */ + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/error.c b/3rdparty/QtZeroConf/avahi-common/error.c new file mode 100644 index 000000000..63c50331a --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/error.c @@ -0,0 +1,97 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. + ***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "error.h" +#include "i18n.h" + +const char *avahi_strerror(int error) { + + const char * const msg[- AVAHI_ERR_MAX] = { + N_("OK"), + N_("Operation failed"), + N_("Bad state"), + N_("Invalid host name"), + N_("Invalid domain name"), + N_("No suitable network protocol available"), + N_("Invalid DNS TTL"), + N_("Resource record key is pattern"), + N_("Local name collision"), + N_("Invalid record"), + + N_("Invalid service name"), + N_("Invalid service type"), + N_("Invalid port number"), + N_("Invalid record key"), + N_("Invalid address"), + N_("Timeout reached"), + N_("Too many clients"), + N_("Too many objects"), + N_("Too many entries"), + N_("OS Error"), + + N_("Access denied"), + N_("Invalid operation"), + N_("An unexpected D-Bus error occurred"), + N_("Daemon connection failed"), + N_("Memory exhausted"), + N_("The object passed in was not valid"), + N_("Daemon not running"), + N_("Invalid interface index"), + N_("Invalid protocol specification"), + N_("Invalid flags"), + + N_("Not found"), + N_("Invalid configuration"), + N_("Version mismatch"), + N_("Invalid service subtype"), + N_("Invalid packet"), + N_("Invalid DNS return code"), + N_("DNS failure: FORMERR"), + N_("DNS failure: SERVFAIL"), + N_("DNS failure: NXDOMAIN"), + N_("DNS failure: NOTIMP"), + + N_("DNS failure: REFUSED"), + N_("DNS failure: YXDOMAIN"), + N_("DNS failure: YXRRSET"), + N_("DNS failure: NXRRSET"), + N_("DNS failure: NOTAUTH"), + N_("DNS failure: NOTZONE"), + N_("Invalid RDATA"), + N_("Invalid DNS type"), + N_("Invalid DNS class"), + N_("Not supported"), + + N_("Not permitted"), + N_("Invalid argument"), + N_("Is empty"), + N_("The requested operation is invalid because redundant") + }; + + avahi_init_i18n(); + + if (-error < 0 || -error >= -AVAHI_ERR_MAX) + return _("Invalid Error Code"); + + return _(msg[-error]); +} diff --git a/3rdparty/QtZeroConf/avahi-common/error.h b/3rdparty/QtZeroConf/avahi-common/error.h new file mode 100644 index 000000000..94511c92e --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/error.h @@ -0,0 +1,107 @@ +#ifndef fooerrorhfoo +#define fooerrorhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file error.h Error codes and auxiliary functions */ + +#include + +AVAHI_C_DECL_BEGIN + +/** Error codes used by avahi */ +enum { + AVAHI_OK = 0, /**< OK */ + AVAHI_ERR_FAILURE = -1, /**< Generic error code */ + AVAHI_ERR_BAD_STATE = -2, /**< Object was in a bad state */ + AVAHI_ERR_INVALID_HOST_NAME = -3, /**< Invalid host name */ + AVAHI_ERR_INVALID_DOMAIN_NAME = -4, /**< Invalid domain name */ + AVAHI_ERR_NO_NETWORK = -5, /**< No suitable network protocol available */ + AVAHI_ERR_INVALID_TTL = -6, /**< Invalid DNS TTL */ + AVAHI_ERR_IS_PATTERN = -7, /**< RR key is pattern */ + AVAHI_ERR_COLLISION = -8, /**< Name collision */ + AVAHI_ERR_INVALID_RECORD = -9, /**< Invalid RR */ + + AVAHI_ERR_INVALID_SERVICE_NAME = -10, /**< Invalid service name */ + AVAHI_ERR_INVALID_SERVICE_TYPE = -11, /**< Invalid service type */ + AVAHI_ERR_INVALID_PORT = -12, /**< Invalid port number */ + AVAHI_ERR_INVALID_KEY = -13, /**< Invalid key */ + AVAHI_ERR_INVALID_ADDRESS = -14, /**< Invalid address */ + AVAHI_ERR_TIMEOUT = -15, /**< Timeout reached */ + AVAHI_ERR_TOO_MANY_CLIENTS = -16, /**< Too many clients */ + AVAHI_ERR_TOO_MANY_OBJECTS = -17, /**< Too many objects */ + AVAHI_ERR_TOO_MANY_ENTRIES = -18, /**< Too many entries */ + AVAHI_ERR_OS = -19, /**< OS error */ + + AVAHI_ERR_ACCESS_DENIED = -20, /**< Access denied */ + AVAHI_ERR_INVALID_OPERATION = -21, /**< Invalid operation */ + AVAHI_ERR_DBUS_ERROR = -22, /**< An unexpected D-Bus error occurred */ + AVAHI_ERR_DISCONNECTED = -23, /**< Daemon connection failed */ + AVAHI_ERR_NO_MEMORY = -24, /**< Memory exhausted */ + AVAHI_ERR_INVALID_OBJECT = -25, /**< The object passed to this function was invalid */ + AVAHI_ERR_NO_DAEMON = -26, /**< Daemon not running */ + AVAHI_ERR_INVALID_INTERFACE = -27, /**< Invalid interface */ + AVAHI_ERR_INVALID_PROTOCOL = -28, /**< Invalid protocol */ + AVAHI_ERR_INVALID_FLAGS = -29, /**< Invalid flags */ + + AVAHI_ERR_NOT_FOUND = -30, /**< Not found */ + AVAHI_ERR_INVALID_CONFIG = -31, /**< Configuration error */ + AVAHI_ERR_VERSION_MISMATCH = -32, /**< Verson mismatch */ + AVAHI_ERR_INVALID_SERVICE_SUBTYPE = -33, /**< Invalid service subtype */ + AVAHI_ERR_INVALID_PACKET = -34, /**< Invalid packet */ + AVAHI_ERR_INVALID_DNS_ERROR = -35, /**< Invlaid DNS return code */ + AVAHI_ERR_DNS_FORMERR = -36, /**< DNS Error: Form error */ + AVAHI_ERR_DNS_SERVFAIL = -37, /**< DNS Error: Server Failure */ + AVAHI_ERR_DNS_NXDOMAIN = -38, /**< DNS Error: No such domain */ + AVAHI_ERR_DNS_NOTIMP = -39, /**< DNS Error: Not implemented */ + + AVAHI_ERR_DNS_REFUSED = -40, /**< DNS Error: Operation refused */ + AVAHI_ERR_DNS_YXDOMAIN = -41, + AVAHI_ERR_DNS_YXRRSET = -42, + AVAHI_ERR_DNS_NXRRSET = -43, + AVAHI_ERR_DNS_NOTAUTH = -44, /**< DNS Error: Not authorized */ + AVAHI_ERR_DNS_NOTZONE = -45, + AVAHI_ERR_INVALID_RDATA = -46, /**< Invalid RDATA */ + AVAHI_ERR_INVALID_DNS_CLASS = -47, /**< Invalid DNS class */ + AVAHI_ERR_INVALID_DNS_TYPE = -48, /**< Invalid DNS type */ + AVAHI_ERR_NOT_SUPPORTED = -49, /**< Not supported */ + + AVAHI_ERR_NOT_PERMITTED = -50, /**< Operation not permitted */ + AVAHI_ERR_INVALID_ARGUMENT = -51, /**< Invalid argument */ + AVAHI_ERR_IS_EMPTY = -52, /**< Is empty */ + AVAHI_ERR_NO_CHANGE = -53, /**< The requested operation is invalid because it is redundant */ + + /**** + **** IF YOU ADD A NEW ERROR CODE HERE, PLEASE DON'T FORGET TO ADD + **** IT TO THE STRING ARRAY IN avahi_strerror() IN error.c AND + **** TO THE ARRAY IN dbus.c AND FINALLY TO dbus.h! + **** + **** Also remember to update the MAX value below. + ****/ + + AVAHI_ERR_MAX = -54 +}; + +/** Return a human readable error string for the specified error code */ +const char *avahi_strerror(int error); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/gccmacro.h b/3rdparty/QtZeroConf/avahi-common/gccmacro.h new file mode 100644 index 000000000..4c97111aa --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/gccmacro.h @@ -0,0 +1,74 @@ +#ifndef foogccmacrohfoo +#define foogccmacrohfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file gccmacro.h Defines some macros for GCC extensions */ + +#include + +AVAHI_C_DECL_BEGIN + +#if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) +#define AVAHI_GCC_ALLOC_SIZE(x) __attribute__ ((__alloc_size__(x))) +#define AVAHI_GCC_ALLOC_SIZE2(x,y) __attribute__ ((__alloc_size__(x,y))) +#else +/** Macro for usage of GCC's alloc_size attribute */ +#define AVAHI_GCC_ALLOC_SIZE(x) +#define AVAHI_GCC_ALLOC_SIZE2(x,y) +#endif + +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define AVAHI_GCC_SENTINEL __attribute__ ((sentinel)) +#else +/** Macro for usage of GCC's sentinel compilation warnings */ +#define AVAHI_GCC_SENTINEL +#endif + +#ifdef __GNUC__ +#define AVAHI_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) +#else +/** Macro for usage of GCC's printf compilation warnings */ +#define AVAHI_GCC_PRINTF_ATTR(a,b) +#endif + +/** Same as AVAHI_GCC_PRINTF_ATTR but hard coded to arguments 1 and 2 */ +#define AVAHI_GCC_PRINTF_ATTR12 AVAHI_GCC_PRINTF_ATTR(1,2) + +/** Same as AVAHI_GCC_PRINTF_ATTR but hard coded to arguments 2 and 3 */ +#define AVAHI_GCC_PRINTF_ATTR23 AVAHI_GCC_PRINTF_ATTR(2,3) + +#ifdef __GNUC__ +#define AVAHI_GCC_NORETURN __attribute__((noreturn)) +#else +/** Macro for no-return functions */ +#define AVAHI_GCC_NORETURN +#endif + +#ifdef __GNUC__ +#define AVAHI_GCC_UNUSED __attribute__ ((unused)) +#else +/** Macro for not used parameter */ +#define AVAHI_GCC_UNUSED +#endif + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/i18n.c b/3rdparty/QtZeroConf/avahi-common/i18n.c new file mode 100644 index 000000000..51c93e4e6 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/i18n.c @@ -0,0 +1,38 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. + ***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "i18n.h" + +void avahi_init_i18n(void) { + + /* Not really thread safe, but this doesn't matter much since + * bindtextdomain is supposed to be idempotent anyway. */ + + static int done = 0; + + if (!done) { + bindtextdomain(GETTEXT_PACKAGE, AVAHI_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + done = 1; + } +} diff --git a/3rdparty/QtZeroConf/avahi-common/i18n.h b/3rdparty/QtZeroConf/avahi-common/i18n.h new file mode 100644 index 000000000..2a613e58f --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/i18n.h @@ -0,0 +1,53 @@ +#ifndef fooi18nhfoo +#define fooi18nhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#if !defined(GETTEXT_PACKAGE) +#error "Something is very wrong here, config.h needs to be included first" +#endif + +#ifdef ENABLE_NLS + +#include + +#define _(String) dgettext(GETTEXT_PACKAGE, String) +#ifdef gettext_noop +#define N_(String) gettext_noop(String) +#else +#define N_(String) (String) +#endif + +#else /* NLS is disabled */ + +#define _(String) (String) +#define N_(String) (String) +#define textdomain(String) (String) +#define gettext(String) (String) +#define dgettext(Domain,String) (String) +#define dcgettext(Domain,String,Type) (String) +#define bindtextdomain(Domain,Directory) (Domain) +#define bind_textdomain_codeset(Domain,Codeset) (Codeset) + +#endif /* ENABLE_NLS */ + +void avahi_init_i18n(void); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/llist.h b/3rdparty/QtZeroConf/avahi-common/llist.h new file mode 100644 index 000000000..e37056d98 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/llist.h @@ -0,0 +1,75 @@ +#ifndef foollistfoo +#define foollistfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file llist.h A simple macro based linked list implementation */ + +#include + +#include + +AVAHI_C_DECL_BEGIN + +/** The head of the linked list. Use this in the structure that shall + * contain the head of the linked list */ +#define AVAHI_LLIST_HEAD(t,name) t *name + +/** The pointers in the linked list's items. Use this in the item structure */ +#define AVAHI_LLIST_FIELDS(t,name) t *name##_next, *name##_prev + +/** Initialize the list's head */ +#define AVAHI_LLIST_HEAD_INIT(t,head) do { (head) = NULL; } while(0) + +/** Initialize a list item */ +#define AVAHI_LLIST_INIT(t,name,item) do { \ + t *_item = (item); \ + assert(_item); \ + _item->name##_prev = _item->name##_next = NULL; \ + } while(0) + +/** Prepend an item to the list */ +#define AVAHI_LLIST_PREPEND(t,name,head,item) do { \ + t **_head = &(head), *_item = (item); \ + assert(_item); \ + if ((_item->name##_next = *_head)) \ + _item->name##_next->name##_prev = _item; \ + _item->name##_prev = NULL; \ + *_head = _item; \ + } while (0) + +/** Remove an item from the list */ +#define AVAHI_LLIST_REMOVE(t,name,head,item) do { \ + t **_head = &(head), *_item = (item); \ + assert(_item); \ + if (_item->name##_next) \ + _item->name##_next->name##_prev = _item->name##_prev; \ + if (_item->name##_prev) \ + _item->name##_prev->name##_next = _item->name##_next; \ + else {\ + assert(*_head == _item); \ + *_head = _item->name##_next; \ + } \ + _item->name##_next = _item->name##_prev = NULL; \ + } while(0) + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/malloc.c b/3rdparty/QtZeroConf/avahi-common/malloc.c new file mode 100644 index 000000000..23b13a939 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/malloc.c @@ -0,0 +1,257 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "malloc.h" + +#ifndef va_copy +#ifdef __va_copy +#define va_copy(DEST,SRC) __va_copy((DEST),(SRC)) +#else +#define va_copy(DEST,SRC) memcpy(&(DEST), &(SRC), sizeof(va_list)) +#endif +#endif + +static const AvahiAllocator *allocator = NULL; + +static void oom(void) AVAHI_GCC_NORETURN; + +static void oom(void) { + + static const char msg[] = "Out of memory, aborting ...\n"; + const char *n = msg; + + while (strlen(n) > 0) { + ssize_t r; + + if ((r = write(2, n, strlen(n))) < 0) + break; + + n += r; + } + + abort(); +} + +/* Default implementation for avahi_malloc() */ +static void* xmalloc(size_t size) { + void *p; + + if (size == 0) + return NULL; + + if (!(p = malloc(size))) + oom(); + + return p; +} + +/* Default implementation for avahi_realloc() */ +static void *xrealloc(void *p, size_t size) { + + if (size == 0) { + free(p); + return NULL; + } + + if (!(p = realloc(p, size))) + oom(); + + return p; +} + +/* Default implementation for avahi_calloc() */ +static void *xcalloc(size_t nmemb, size_t size) { + void *p; + + if (size == 0 || nmemb == 0) + return NULL; + + if (!(p = calloc(nmemb, size))) + oom(); + + return p; +} + +void *avahi_malloc(size_t size) { + + if (size <= 0) + return NULL; + + if (!allocator) + return xmalloc(size); + + assert(allocator->malloc); + return allocator->malloc(size); +} + +void *avahi_malloc0(size_t size) { + void *p; + + if (size <= 0) + return NULL; + + if (!allocator) + return xcalloc(1, size); + + if (allocator->calloc) + return allocator->calloc(1, size); + + assert(allocator->malloc); + if ((p = allocator->malloc(size))) + memset(p, 0, size); + + return p; +} + +void avahi_free(void *p) { + + if (!p) + return; + + if (!allocator) { + free(p); + return; + } + + assert(allocator->free); + allocator->free(p); +} + +void *avahi_realloc(void *p, size_t size) { + + if (size == 0) { + avahi_free(p); + return NULL; + } + + if (!allocator) + return xrealloc(p, size); + + assert(allocator->realloc); + return allocator->realloc(p, size); +} + +char *avahi_strdup(const char *s) { + char *r; + size_t size; + + if (!s) + return NULL; + + size = strlen(s); + if (!(r = avahi_malloc(size+1))) + return NULL; + + memcpy(r, s, size+1); + return r; +} + +char *avahi_strndup(const char *s, size_t max) { + char *r; + size_t size; + const char *p; + + if (!s) + return NULL; + + for (p = s, size = 0; + size < max && *p; + p++, size++); + + if (!(r = avahi_new(char, size+1))) + return NULL; + + memcpy(r, s, size); + r[size] = 0; + return r; +} + +/* Change the allocator */ +void avahi_set_allocator(const AvahiAllocator *a) { + allocator = a; +} + +char *avahi_strdup_vprintf(const char *fmt, va_list ap) { + size_t len = 80; + char *buf; + + assert(fmt); + + if (!(buf = avahi_malloc(len))) + return NULL; + + for (;;) { + int n; + char *nbuf; + va_list ap2; + + va_copy (ap2, ap); + n = vsnprintf(buf, len, fmt, ap2); + va_end (ap2); + + if (n >= 0 && n < (int) len) + return buf; + + if (n >= 0) + len = n+1; + else + len *= 2; + + if (!(nbuf = avahi_realloc(buf, len))) { + avahi_free(buf); + return NULL; + } + + buf = nbuf; + } +} + +char *avahi_strdup_printf(const char *fmt, ... ) { + char *s; + va_list ap; + + assert(fmt); + + va_start(ap, fmt); + s = avahi_strdup_vprintf(fmt, ap); + va_end(ap); + + return s; +} + +void *avahi_memdup(const void *s, size_t l) { + void *p; + assert(s); + + if (!(p = avahi_malloc(l))) + return NULL; + + memcpy(p, s, l); + return p; +} diff --git a/3rdparty/QtZeroConf/avahi-common/malloc.h b/3rdparty/QtZeroConf/avahi-common/malloc.h new file mode 100644 index 000000000..ffaac3f3c --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/malloc.h @@ -0,0 +1,96 @@ +#ifndef foomallochfoo +#define foomallochfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file malloc.h Memory allocation */ + +#include +#include +#include +#include + +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Allocate some memory, just like the libc malloc() */ +void *avahi_malloc(size_t size) AVAHI_GCC_ALLOC_SIZE(1); + +/** Similar to avahi_malloc() but set the memory to zero */ +void *avahi_malloc0(size_t size) AVAHI_GCC_ALLOC_SIZE(1); + +/** Free some memory */ +void avahi_free(void *p); + +/** Similar to libc's realloc() */ +void *avahi_realloc(void *p, size_t size) AVAHI_GCC_ALLOC_SIZE(2); + +/** Internal helper for avahi_new() */ +static inline void* AVAHI_GCC_ALLOC_SIZE2(1,2) avahi_new_internal(unsigned n, size_t k) { + assert(n < INT_MAX/k); + return avahi_malloc(n*k); +} + +/** Allocate n new structures of the specified type. */ +#define avahi_new(type, n) ((type*) avahi_new_internal((n), sizeof(type))) + +/** Internal helper for avahi_new0() */ +static inline void* AVAHI_GCC_ALLOC_SIZE2(1,2) avahi_new0_internal(unsigned n, size_t k) { + assert(n < INT_MAX/k); + return avahi_malloc0(n*k); +} + +/** Same as avahi_new() but set the memory to zero */ +#define avahi_new0(type, n) ((type*) avahi_new0_internal((n), sizeof(type))) + +/** Just like libc's strdup() */ +char *avahi_strdup(const char *s); + +/** Just like libc's strndup() */ +char *avahi_strndup(const char *s, size_t l); + +/** Duplicate the given memory block into a new one allocated with avahi_malloc() */ +void *avahi_memdup(const void *s, size_t l) AVAHI_GCC_ALLOC_SIZE(2); + +/** Wraps allocator functions */ +typedef struct AvahiAllocator { + void* (*malloc)(size_t size) AVAHI_GCC_ALLOC_SIZE(1); + void (*free)(void *p); + void* (*realloc)(void *p, size_t size) AVAHI_GCC_ALLOC_SIZE(2); + void* (*calloc)(size_t nmemb, size_t size) AVAHI_GCC_ALLOC_SIZE2(1,2); /**< May be NULL */ +} AvahiAllocator; + +/** Change the allocator. May be NULL to return to default (libc) + * allocators. The structure is not copied! */ +void avahi_set_allocator(const AvahiAllocator *a); + +/** Like sprintf() but store the result in a freshly allocated buffer. Free this with avahi_free() */ +char *avahi_strdup_printf(const char *fmt, ... ) AVAHI_GCC_PRINTF_ATTR12; + +/** \cond fulldocs */ +/** Same as avahi_strdup_printf() but take a va_list instead of varargs */ +char *avahi_strdup_vprintf(const char *fmt, va_list ap); +/** \endcond */ + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/rlist.c b/3rdparty/QtZeroConf/avahi-common/rlist.c new file mode 100644 index 000000000..17dcb24a4 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/rlist.c @@ -0,0 +1,62 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "rlist.h" +#include "malloc.h" + +AvahiRList* avahi_rlist_prepend(AvahiRList *r, void *data) { + AvahiRList *n; + + if (!(n = avahi_new(AvahiRList, 1))) + return NULL; + + n->data = data; + + AVAHI_LLIST_PREPEND(AvahiRList, rlist, r, n); + return r; +} + +AvahiRList* avahi_rlist_remove(AvahiRList *r, void *data) { + AvahiRList *n; + + for (n = r; n; n = n->rlist_next) + + if (n->data == data) { + AVAHI_LLIST_REMOVE(AvahiRList, rlist, r, n); + avahi_free(n); + break; + } + + return r; +} + +AvahiRList* avahi_rlist_remove_by_link(AvahiRList *r, AvahiRList *n) { + assert(n); + + AVAHI_LLIST_REMOVE(AvahiRList, rlist, r, n); + avahi_free(n); + + return r; +} diff --git a/3rdparty/QtZeroConf/avahi-common/rlist.h b/3rdparty/QtZeroConf/avahi-common/rlist.h new file mode 100644 index 000000000..9bcc1d589 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/rlist.h @@ -0,0 +1,49 @@ +#ifndef foorlistfoo +#define foorlistfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file rlist.h A simple linked list implementation */ + +#include "llist.h" + +AVAHI_C_DECL_BEGIN + +/** A doubly linked list type */ +typedef struct AvahiRList AvahiRList; + +/** A doubly linked list type */ +struct AvahiRList { + AVAHI_LLIST_FIELDS(AvahiRList, rlist); + void *data; +}; + +/** Prepend a new item to the beginning of the list and return the new beginning */ +AvahiRList* avahi_rlist_prepend(AvahiRList *r, void *data); + +/** Remove the first occurence of the specified item from the list and return the new beginning */ +AvahiRList* avahi_rlist_remove(AvahiRList *r, void *data); + +/** Remove the specified item from the list and return the new beginning */ +AvahiRList* avahi_rlist_remove_by_link(AvahiRList *r, AvahiRList *n); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/simple-watch.c b/3rdparty/QtZeroConf/avahi-common/simple-watch.c new file mode 100644 index 000000000..08d8090c7 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/simple-watch.c @@ -0,0 +1,649 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "llist.h" +#include "malloc.h" +#include "timeval.h" +#include "simple-watch.h" + +struct AvahiWatch { + AvahiSimplePoll *simple_poll; + int dead; + + int idx; + struct pollfd pollfd; + + AvahiWatchCallback callback; + void *userdata; + + AVAHI_LLIST_FIELDS(AvahiWatch, watches); +}; + +struct AvahiTimeout { + AvahiSimplePoll *simple_poll; + int dead; + + int enabled; + struct timeval expiry; + + AvahiTimeoutCallback callback; + void *userdata; + + AVAHI_LLIST_FIELDS(AvahiTimeout, timeouts); +}; + +struct AvahiSimplePoll { + AvahiPoll api; + AvahiPollFunc poll_func; + void *poll_func_userdata; + + struct pollfd* pollfds; + int n_pollfds, max_pollfds, rebuild_pollfds; + + int watch_req_cleanup, timeout_req_cleanup; + int quit; + int events_valid; + + int n_watches; + AVAHI_LLIST_HEAD(AvahiWatch, watches); + AVAHI_LLIST_HEAD(AvahiTimeout, timeouts); + + int wakeup_pipe[2]; + int wakeup_issued; + + int prepared_timeout; + + enum { + STATE_INIT, + STATE_PREPARING, + STATE_PREPARED, + STATE_RUNNING, + STATE_RAN, + STATE_DISPATCHING, + STATE_DISPATCHED, + STATE_QUIT, + STATE_FAILURE + } state; +}; + +void avahi_simple_poll_wakeup(AvahiSimplePoll *s) { + char c = 'W'; + assert(s); + + write(s->wakeup_pipe[1], &c, sizeof(c)); + s->wakeup_issued = 1; +} + +static void clear_wakeup(AvahiSimplePoll *s) { + char c[10]; /* Read ten at a time */ + + if (!s->wakeup_issued) + return; + + s->wakeup_issued = 0; + + for(;;) + if (read(s->wakeup_pipe[0], &c, sizeof(c)) != sizeof(c)) + break; +} + +static int set_nonblock(int fd) { + int n; + + assert(fd >= 0); + + if ((n = fcntl(fd, F_GETFL)) < 0) + return -1; + + if (n & O_NONBLOCK) + return 0; + + return fcntl(fd, F_SETFL, n|O_NONBLOCK); +} + +static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata) { + AvahiWatch *w; + AvahiSimplePoll *s; + + assert(api); + assert(fd >= 0); + assert(callback); + + s = api->userdata; + assert(s); + + if (!(w = avahi_new(AvahiWatch, 1))) + return NULL; + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(s); + + w->simple_poll = s; + w->dead = 0; + + w->pollfd.fd = fd; + w->pollfd.events = event; + w->pollfd.revents = 0; + + w->callback = callback; + w->userdata = userdata; + + w->idx = -1; + s->rebuild_pollfds = 1; + + AVAHI_LLIST_PREPEND(AvahiWatch, watches, s->watches, w); + s->n_watches++; + + return w; +} + +static void watch_update(AvahiWatch *w, AvahiWatchEvent events) { + assert(w); + assert(!w->dead); + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(w->simple_poll); + + w->pollfd.events = events; + + if (w->idx != -1) { + assert(w->simple_poll); + w->simple_poll->pollfds[w->idx] = w->pollfd; + } else + w->simple_poll->rebuild_pollfds = 1; +} + +static AvahiWatchEvent watch_get_events(AvahiWatch *w) { + assert(w); + assert(!w->dead); + + if (w->idx != -1 && w->simple_poll->events_valid) + return w->simple_poll->pollfds[w->idx].revents; + + return 0; +} + +static void remove_pollfd(AvahiWatch *w) { + assert(w); + + if (w->idx == -1) + return; + + w->simple_poll->rebuild_pollfds = 1; +} + +static void watch_free(AvahiWatch *w) { + assert(w); + + assert(!w->dead); + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(w->simple_poll); + + remove_pollfd(w); + + w->dead = 1; + w->simple_poll->n_watches --; + w->simple_poll->watch_req_cleanup = 1; +} + +static void destroy_watch(AvahiWatch *w) { + assert(w); + + remove_pollfd(w); + AVAHI_LLIST_REMOVE(AvahiWatch, watches, w->simple_poll->watches, w); + + if (!w->dead) + w->simple_poll->n_watches --; + + avahi_free(w); +} + +static void cleanup_watches(AvahiSimplePoll *s, int all) { + AvahiWatch *w, *next; + assert(s); + + for (w = s->watches; w; w = next) { + next = w->watches_next; + + if (all || w->dead) + destroy_watch(w); + } + + s->timeout_req_cleanup = 0; +} + +static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) { + AvahiTimeout *t; + AvahiSimplePoll *s; + + assert(api); + assert(callback); + + s = api->userdata; + assert(s); + + if (!(t = avahi_new(AvahiTimeout, 1))) + return NULL; + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(s); + + t->simple_poll = s; + t->dead = 0; + + if ((t->enabled = !!tv)) + t->expiry = *tv; + + t->callback = callback; + t->userdata = userdata; + + AVAHI_LLIST_PREPEND(AvahiTimeout, timeouts, s->timeouts, t); + return t; +} + +static void timeout_update(AvahiTimeout *t, const struct timeval *tv) { + assert(t); + assert(!t->dead); + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(t->simple_poll); + + if ((t->enabled = !!tv)) + t->expiry = *tv; +} + +static void timeout_free(AvahiTimeout *t) { + assert(t); + assert(!t->dead); + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(t->simple_poll); + + t->dead = 1; + t->simple_poll->timeout_req_cleanup = 1; +} + + +static void destroy_timeout(AvahiTimeout *t) { + assert(t); + + AVAHI_LLIST_REMOVE(AvahiTimeout, timeouts, t->simple_poll->timeouts, t); + + avahi_free(t); +} + +static void cleanup_timeouts(AvahiSimplePoll *s, int all) { + AvahiTimeout *t, *next; + assert(s); + + for (t = s->timeouts; t; t = next) { + next = t->timeouts_next; + + if (all || t->dead) + destroy_timeout(t); + } + + s->timeout_req_cleanup = 0; +} + +AvahiSimplePoll *avahi_simple_poll_new(void) { + AvahiSimplePoll *s; + + if (!(s = avahi_new(AvahiSimplePoll, 1))) + return NULL; + + if (pipe(s->wakeup_pipe) < 0) { + avahi_free(s); + return NULL; + } + + set_nonblock(s->wakeup_pipe[0]); + set_nonblock(s->wakeup_pipe[1]); + + s->api.userdata = s; + + s->api.watch_new = watch_new; + s->api.watch_free = watch_free; + s->api.watch_update = watch_update; + s->api.watch_get_events = watch_get_events; + + s->api.timeout_new = timeout_new; + s->api.timeout_free = timeout_free; + s->api.timeout_update = timeout_update; + + s->pollfds = NULL; + s->max_pollfds = s->n_pollfds = 0; + s->rebuild_pollfds = 1; + s->quit = 0; + s->n_watches = 0; + s->events_valid = 0; + + s->watch_req_cleanup = 0; + s->timeout_req_cleanup = 0; + + s->prepared_timeout = 0; + + s->state = STATE_INIT; + + s->wakeup_issued = 0; + + avahi_simple_poll_set_func(s, NULL, NULL); + + AVAHI_LLIST_HEAD_INIT(AvahiWatch, s->watches); + AVAHI_LLIST_HEAD_INIT(AvahiTimeout, s->timeouts); + + return s; +} + +void avahi_simple_poll_free(AvahiSimplePoll *s) { + assert(s); + + cleanup_timeouts(s, 1); + cleanup_watches(s, 1); + assert(s->n_watches == 0); + + avahi_free(s->pollfds); + + if (s->wakeup_pipe[0] >= 0) + close(s->wakeup_pipe[0]); + + if (s->wakeup_pipe[1] >= 0) + close(s->wakeup_pipe[1]); + + avahi_free(s); +} + +static int rebuild(AvahiSimplePoll *s) { + AvahiWatch *w; + int idx; + + assert(s); + + if (s->n_watches+1 > s->max_pollfds) { + struct pollfd *n; + + s->max_pollfds = s->n_watches + 10; + + if (!(n = avahi_realloc(s->pollfds, sizeof(struct pollfd) * s->max_pollfds))) + return -1; + + s->pollfds = n; + } + + + s->pollfds[0].fd = s->wakeup_pipe[0]; + s->pollfds[0].events = POLLIN; + s->pollfds[0].revents = 0; + + idx = 1; + + for (w = s->watches; w; w = w->watches_next) { + + if(w->dead) + continue; + + assert(w->idx < s->max_pollfds); + s->pollfds[w->idx = idx++] = w->pollfd; + } + + s->n_pollfds = idx; + s->events_valid = 0; + s->rebuild_pollfds = 0; + + return 0; +} + +static AvahiTimeout* find_next_timeout(AvahiSimplePoll *s) { + AvahiTimeout *t, *n = NULL; + assert(s); + + for (t = s->timeouts; t; t = t->timeouts_next) { + + if (t->dead || !t->enabled) + continue; + + if (!n || avahi_timeval_compare(&t->expiry, &n->expiry) < 0) + n = t; + } + + return n; +} + +static void timeout_callback(AvahiTimeout *t) { + assert(t); + assert(!t->dead); + assert(t->enabled); + + t->enabled = 0; + t->callback(t, t->userdata); +} + +int avahi_simple_poll_prepare(AvahiSimplePoll *s, int timeout) { + AvahiTimeout *next_timeout; + + assert(s); + assert(s->state == STATE_INIT || s->state == STATE_DISPATCHED || s->state == STATE_FAILURE); + s->state = STATE_PREPARING; + + /* Clear pending wakeup requests */ + clear_wakeup(s); + + /* Cleanup things first */ + if (s->watch_req_cleanup) + cleanup_watches(s, 0); + + if (s->timeout_req_cleanup) + cleanup_timeouts(s, 0); + + /* Check whether a quit was requested */ + if (s->quit) { + s->state = STATE_QUIT; + return 1; + } + + /* Do we need to rebuild our array of pollfds? */ + if (s->rebuild_pollfds) + if (rebuild(s) < 0) { + s->state = STATE_FAILURE; + return -1; + } + + /* Calculate the wakeup time */ + if ((next_timeout = find_next_timeout(s))) { + struct timeval now; + int t; + AvahiUsec usec; + + if (next_timeout->expiry.tv_sec == 0 && + next_timeout->expiry.tv_usec == 0) { + + /* Just a shortcut so that we don't need to call gettimeofday() */ + timeout = 0; + goto finish; + } + + gettimeofday(&now, NULL); + usec = avahi_timeval_diff(&next_timeout->expiry, &now); + + if (usec <= 0) { + /* Timeout elapsed */ + + timeout = 0; + goto finish; + } + + /* Calculate sleep time. We add 1ms because otherwise we'd + * wake up too early most of the time */ + t = (int) (usec / 1000) + 1; + + if (timeout < 0 || timeout > t) + timeout = t; + } + +finish: + s->prepared_timeout = timeout; + s->state = STATE_PREPARED; + return 0; +} + +int avahi_simple_poll_run(AvahiSimplePoll *s) { + assert(s); + assert(s->state == STATE_PREPARED || s->state == STATE_FAILURE); + + s->state = STATE_RUNNING; + + for (;;) { + errno = 0; + + if (s->poll_func(s->pollfds, s->n_pollfds, s->prepared_timeout, s->poll_func_userdata) < 0) { + + if (errno == EINTR) + continue; + + s->state = STATE_FAILURE; + return -1; + } + + break; + } + + /* The poll events are now valid again */ + s->events_valid = 1; + + /* Update state */ + s->state = STATE_RAN; + return 0; +} + +int avahi_simple_poll_dispatch(AvahiSimplePoll *s) { + AvahiTimeout *next_timeout; + AvahiWatch *w; + + assert(s); + assert(s->state == STATE_RAN); + s->state = STATE_DISPATCHING; + + /* We execute only on callback in every iteration */ + + /* Check whether the wakeup time has been reached now */ + if ((next_timeout = find_next_timeout(s))) { + + if (next_timeout->expiry.tv_sec == 0 && next_timeout->expiry.tv_usec == 0) { + + /* Just a shortcut so that we don't need to call gettimeofday() */ + timeout_callback(next_timeout); + goto finish; + } + + if (avahi_age(&next_timeout->expiry) >= 0) { + + /* Timeout elapsed */ + timeout_callback(next_timeout); + goto finish; + } + } + + /* Look for some kind of I/O event */ + for (w = s->watches; w; w = w->watches_next) { + + if (w->dead) + continue; + + assert(w->idx >= 0); + assert(w->idx < s->n_pollfds); + + if (s->pollfds[w->idx].revents != 0) { + w->callback(w, w->pollfd.fd, s->pollfds[w->idx].revents, w->userdata); + goto finish; + } + } + +finish: + + s->state = STATE_DISPATCHED; + return 0; +} + +int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) { + int r; + + if ((r = avahi_simple_poll_prepare(s, timeout)) != 0) + return r; + + if ((r = avahi_simple_poll_run(s)) != 0) + return r; + + if ((r = avahi_simple_poll_dispatch(s)) != 0) + return r; + + return 0; +} + +void avahi_simple_poll_quit(AvahiSimplePoll *s) { + assert(s); + + s->quit = 1; + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(s); +} + +const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) { + assert(s); + + return &s->api; +} + +static int system_poll(struct pollfd *ufds, unsigned int nfds, int timeout, AVAHI_GCC_UNUSED void *userdata) { + return poll(ufds, nfds, timeout); +} + +void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata) { + assert(s); + + s->poll_func = func ? func : system_poll; + s->poll_func_userdata = func ? userdata : NULL; + + /* If there is a background thread running the poll() for us, tell it to exit the poll() */ + avahi_simple_poll_wakeup(s); +} + +int avahi_simple_poll_loop(AvahiSimplePoll *s) { + int r; + + assert(s); + + for (;;) + if ((r = avahi_simple_poll_iterate(s, -1)) != 0) + if (r >= 0 || errno != EINTR) + return r; +} diff --git a/3rdparty/QtZeroConf/avahi-common/simple-watch.h b/3rdparty/QtZeroConf/avahi-common/simple-watch.h new file mode 100644 index 000000000..db8712285 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/simple-watch.h @@ -0,0 +1,85 @@ +#ifndef foosimplewatchhfoo +#define foosimplewatchhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file simple-watch.h Simple poll() based main loop implementation */ + +#include +#include +#include + +AVAHI_C_DECL_BEGIN + +/** A main loop object. Main loops of this type aren't very flexible + * since they only support a single wakeup type. Nevertheless it + * should suffice for small test and example applications. */ +typedef struct AvahiSimplePoll AvahiSimplePoll; + +/** Create a new main loop object */ +AvahiSimplePoll *avahi_simple_poll_new(void); + +/** Free a main loop object */ +void avahi_simple_poll_free(AvahiSimplePoll *s); + +/** Return the abstracted poll API object for this main loop + * object. The is will return the same pointer each time it is + * called. */ +const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s); + +/** Run a single main loop iteration of this main loop. If sleep_time +is < 0 this will block until any of the registered events happens, +then it will execute the attached callback function. If sleep_time is +0 the routine just checks if any event is pending. If yes the attached +callback function is called, otherwise the function returns +immediately. If sleep_time > 0 the function will block for at most the +specified time in msecs. Returns -1 on error, 0 on success and 1 if a +quit request has been scheduled. Usually this function should be called +in a loop until it returns a non-zero value*/ +int avahi_simple_poll_iterate(AvahiSimplePoll *s, int sleep_time); + +/** Request that the main loop quits. If this is called the next + call to avahi_simple_poll_iterate() will return 1 */ +void avahi_simple_poll_quit(AvahiSimplePoll *s); + +/** Prototype for a poll() type function */ +typedef int (*AvahiPollFunc)(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata); + +/** Replace the internally used poll() function. By default the system's poll() will be used */ +void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata); + +/** The first stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ +int avahi_simple_poll_prepare(AvahiSimplePoll *s, int timeout); + +/** The second stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ +int avahi_simple_poll_run(AvahiSimplePoll *s); + +/** The third and final stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ +int avahi_simple_poll_dispatch(AvahiSimplePoll *s); + +/** Call avahi_simple_poll_iterate() in a loop and return if it returns non-zero */ +int avahi_simple_poll_loop(AvahiSimplePoll *s); + +/** Wakeup the main loop. (for threaded environments) */ +void avahi_simple_poll_wakeup(AvahiSimplePoll *s); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/strlst.c b/3rdparty/QtZeroConf/avahi-common/strlst.c new file mode 100644 index 000000000..b861cf82e --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/strlst.c @@ -0,0 +1,505 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "strlst.h" +#include "malloc.h" +#include "defs.h" + +AvahiStringList*avahi_string_list_add_anonymous(AvahiStringList *l, size_t size) { + AvahiStringList *n; + + if (!(n = avahi_malloc(sizeof(AvahiStringList) + size))) + return NULL; + + n->next = l; + n->size = size; + + /* NUL terminate strings, just to make sure */ + n->text[size] = 0; + + return n; +} + +AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const uint8_t*text, size_t size) { + AvahiStringList *n; + + assert(size == 0 || text); + + if (!(n = avahi_string_list_add_anonymous(l, size))) + return NULL; + + if (size > 0) + memcpy(n->text, text, size); + + return n; +} + +AvahiStringList *avahi_string_list_add(AvahiStringList *l, const char *text) { + assert(text); + + return avahi_string_list_add_arbitrary(l, (const uint8_t*) text, strlen(text)); +} + +int avahi_string_list_parse(const void* data, size_t size, AvahiStringList **ret) { + const uint8_t *c; + AvahiStringList *r = NULL; + + assert(data); + assert(ret); + + c = data; + while (size > 0) { + size_t k; + + k = *(c++); + size--; + + if (k > size) + goto fail; /* Overflow */ + + if (k > 0) { /* Ignore empty strings */ + AvahiStringList *n; + + if (!(n = avahi_string_list_add_arbitrary(r, c, k))) + goto fail; /* OOM */ + + r = n; + } + + c += k; + size -= k; + } + + *ret = r; + + return 0; + +fail: + avahi_string_list_free(r); + return -1; +} + +void avahi_string_list_free(AvahiStringList *l) { + AvahiStringList *n; + + while (l) { + n = l->next; + avahi_free(l); + l = n; + } +} + +AvahiStringList* avahi_string_list_reverse(AvahiStringList *l) { + AvahiStringList *r = NULL, *n; + + while (l) { + n = l->next; + l->next = r; + r = l; + l = n; + } + + return r; +} + +char* avahi_string_list_to_string(AvahiStringList *l) { + AvahiStringList *n; + size_t s = 0; + char *t, *e; + + for (n = l; n; n = n->next) { + if (n != l) + s ++; + + s += n->size+2; + } + + if (!(t = e = avahi_new(char, s+1))) + return NULL; + + l = avahi_string_list_reverse(l); + + for (n = l; n; n = n->next) { + if (n != l) + *(e++) = ' '; + + *(e++) = '"'; + strncpy(e, (char*) n->text, n->size); + e[n->size] = 0; + e = strchr(e, 0); + *(e++) = '"'; + + assert(e); + } + + l = avahi_string_list_reverse(l); + + *e = 0; + + return t; +} + +size_t avahi_string_list_serialize(AvahiStringList *l, void *data, size_t size) { + size_t used = 0; + + if (data) { + AvahiStringList *n; + uint8_t *c; + + l = avahi_string_list_reverse(l); + c = data; + + for (n = l; size > 1 && n; n = n->next) { + size_t k; + + if ((k = n->size) == 0) + /* Skip empty strings */ + continue; + + if (k > 255) + /* Truncate strings at 255 characters */ + k = 255; + + if (k > size-1) + /* Make sure this string fits in */ + k = size-1; + + *(c++) = (uint8_t) k; + memcpy(c, n->text, k); + c += k; + + used += 1 + k; + size -= 1 + k; + } + + l = avahi_string_list_reverse(l); + + if (used == 0 && size > 0) { + + /* Empty lists are treated specially. To comply with + * section 6.1 of the DNS-SD spec, we return a single + * empty string (i.e. a NUL byte)*/ + + *(uint8_t*) data = 0; + used = 1; + } + + } else { + AvahiStringList *n; + + for (n = l; n; n = n->next) { + size_t k; + + if ((k = n->size) == 0) + continue; + + if (k > 255) + k = 255; + + used += 1+k; + } + + if (used == 0) + used = 1; + } + + return used; +} + +int avahi_string_list_equal(const AvahiStringList *a, const AvahiStringList *b) { + + for (;;) { + if (!a && !b) + return 1; + + if (!a || !b) + return 0; + + if (a->size != b->size) + return 0; + + if (a->size != 0 && memcmp(a->text, b->text, a->size) != 0) + return 0; + + a = a->next; + b = b->next; + } +} + +AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) { + va_list va; + + va_start(va, r); + r = avahi_string_list_add_many_va(r, va); + va_end(va); + + return r; +} + +AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va) { + const char *txt; + + while ((txt = va_arg(va, const char*))) + r = avahi_string_list_add(r, txt); + + return r; +} + +AvahiStringList *avahi_string_list_new(const char *txt, ...) { + va_list va; + AvahiStringList *r = NULL; + + if (txt) { + r = avahi_string_list_add(r, txt); + + va_start(va, txt); + r = avahi_string_list_add_many_va(r, va); + va_end(va); + } + + return r; +} + +AvahiStringList *avahi_string_list_new_va(va_list va) { + return avahi_string_list_add_many_va(NULL, va); +} + +AvahiStringList *avahi_string_list_copy(const AvahiStringList *l) { + AvahiStringList *r = NULL; + + for (; l; l = l->next) + if (!(r = avahi_string_list_add_arbitrary(r, l->text, l->size))) { + avahi_string_list_free(r); + return NULL; + } + + return avahi_string_list_reverse(r); +} + +AvahiStringList *avahi_string_list_new_from_array(const char *array[], int length) { + AvahiStringList *r = NULL; + int i; + + assert(array); + + for (i = 0; length >= 0 ? i < length : !!array[i]; i++) + r = avahi_string_list_add(r, array[i]); + + return r; +} + +unsigned avahi_string_list_length(const AvahiStringList *l) { + unsigned n = 0; + + for (; l; l = l->next) + n++; + + return n; +} + +AvahiStringList *avahi_string_list_add_vprintf(AvahiStringList *l, const char *format, va_list va) { + size_t len = 80; + AvahiStringList *r; + + assert(format); + + if (!(r = avahi_malloc(sizeof(AvahiStringList) + len))) + return NULL; + + for (;;) { + int n; + AvahiStringList *nr; + va_list va2; + + va_copy(va2, va); + n = vsnprintf((char*) r->text, len, format, va2); + va_end(va2); + + if (n >= 0 && n < (int) len) + break; + + if (n >= 0) + len = n+1; + else + len *= 2; + + if (!(nr = avahi_realloc(r, sizeof(AvahiStringList) + len))) { + avahi_free(r); + return NULL; + } + + r = nr; + } + + r->next = l; + r->size = strlen((char*) r->text); + + return r; +} + +AvahiStringList *avahi_string_list_add_printf(AvahiStringList *l, const char *format, ...) { + va_list va; + + assert(format); + + va_start(va, format); + l = avahi_string_list_add_vprintf(l, format, va); + va_end(va); + + return l; +} + +AvahiStringList *avahi_string_list_find(AvahiStringList *l, const char *key) { + size_t n; + + assert(key); + n = strlen(key); + + for (; l; l = l->next) { + if (strcasecmp((char*) l->text, key) == 0) + return l; + + if (strncasecmp((char*) l->text, key, n) == 0 && l->text[n] == '=') + return l; + } + + return NULL; +} + +AvahiStringList *avahi_string_list_add_pair(AvahiStringList *l, const char *key, const char *value) { + assert(key); + + if (value) + return avahi_string_list_add_printf(l, "%s=%s", key, value); + else + return avahi_string_list_add(l, key); +} + +AvahiStringList *avahi_string_list_add_pair_arbitrary(AvahiStringList *l, const char *key, const uint8_t *value, size_t size) { + size_t n; + assert(key); + + if (!value) + return avahi_string_list_add(l, key); + + n = strlen(key); + + if (!(l = avahi_string_list_add_anonymous(l, n + 1 + size))) + return NULL; + + memcpy(l->text, key, n); + l->text[n] = '='; + memcpy(l->text + n + 1, value, size); + + return l; +} + +int avahi_string_list_get_pair(AvahiStringList *l, char **key, char **value, size_t *size) { + char *e; + + assert(l); + + if (!(e = memchr(l->text, '=', l->size))) { + + if (key) + if (!(*key = avahi_strdup((char*) l->text))) + return -1; + + if (value) + *value = NULL; + + if (size) + *size = 0; + + } else { + size_t n; + + if (key) + if (!(*key = avahi_strndup((char*) l->text, e - (char *) l->text))) + return -1; + + e++; /* Advance after '=' */ + + n = l->size - (e - (char*) l->text); + + if (value) { + + if (!(*value = avahi_memdup(e, n+1))) { + if (key) + avahi_free(*key); + return -1; + } + + (*value)[n] = 0; + } + + if (size) + *size = n; + } + + return 0; +} + +AvahiStringList *avahi_string_list_get_next(AvahiStringList *l) { + assert(l); + return l->next; +} + +uint8_t *avahi_string_list_get_text(AvahiStringList *l) { + assert(l); + return l->text; +} + +size_t avahi_string_list_get_size(AvahiStringList *l) { + assert(l); + return l->size; +} + +uint32_t avahi_string_list_get_service_cookie(AvahiStringList *l) { + AvahiStringList *f; + char *value = NULL, *end = NULL; + uint32_t ret; + + if (!(f = avahi_string_list_find(l, AVAHI_SERVICE_COOKIE))) + return AVAHI_SERVICE_COOKIE_INVALID; + + if (avahi_string_list_get_pair(f, NULL, &value, NULL) < 0 || !value) + return AVAHI_SERVICE_COOKIE_INVALID; + + ret = (uint32_t) strtoll(value, &end, 0); + + if (*value && end && *end != 0) { + avahi_free(value); + return AVAHI_SERVICE_COOKIE_INVALID; + } + + avahi_free(value); + + return ret; +} diff --git a/3rdparty/QtZeroConf/avahi-common/strlst.h b/3rdparty/QtZeroConf/avahi-common/strlst.h new file mode 100644 index 000000000..94adcea8a --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/strlst.h @@ -0,0 +1,180 @@ +#ifndef footxtlisthfoo +#define footxtlisthfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file strlst.h Implementation of a data type to store lists of strings */ + +#include +#include +#include + +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Linked list of strings that can contain any number of binary + * characters, including NUL bytes. An empty list is created by + * assigning a NULL to a pointer to AvahiStringList. The string list + * is stored in reverse order, so that appending to the string list is + * effectively a prepending to the linked list. This object is used + * primarily for storing DNS TXT record data. */ +typedef struct AvahiStringList { + struct AvahiStringList *next; /**< Pointer to the next linked list element */ + size_t size; /**< Size of text[] */ + uint8_t text[1]; /**< Character data */ +} AvahiStringList; + +/** @{ \name Construction and destruction */ + +/** Create a new string list by taking a variable list of NUL + * terminated strings. The strings are copied using g_strdup(). The + * argument list must be terminated by a NULL pointer. */ +AvahiStringList *avahi_string_list_new(const char *txt, ...) AVAHI_GCC_SENTINEL; + +/** \cond fulldocs */ +/** Same as avahi_string_list_new() but pass a va_list structure */ +AvahiStringList *avahi_string_list_new_va(va_list va); +/** \endcond */ + +/** Create a new string list from a string array. The strings are + * copied using g_strdup(). length should contain the length of the + * array, or -1 if the array is NULL terminated*/ +AvahiStringList *avahi_string_list_new_from_array(const char **array, int length); + +/** Free a string list */ +void avahi_string_list_free(AvahiStringList *l); + +/** @} */ + +/** @{ \name Adding strings */ + +/** Append a NUL terminated string to the specified string list. The + * passed string is copied using g_strdup(). Returns the new list + * start. */ +AvahiStringList *avahi_string_list_add(AvahiStringList *l, const char *text); + +/** Append a new NUL terminated formatted string to the specified string list */ +AvahiStringList *avahi_string_list_add_printf(AvahiStringList *l, const char *format, ...) AVAHI_GCC_PRINTF_ATTR23; + +/** \cond fulldocs */ +/** Append a new NUL terminated formatted string to the specified string list */ +AvahiStringList *avahi_string_list_add_vprintf(AvahiStringList *l, const char *format, va_list va); +/** \endcond */ + +/** Append an arbitrary length byte string to the list. Returns the + * new list start. */ +AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const uint8_t *text, size_t size); + +/** Append a new entry to the string list. The string is not filled +with data. The caller should fill in string data afterwards by writing +it to l->text, where l is the pointer returned by this function. This +function exists solely to optimize a few operations where otherwise +superfluous string copying would be necessary. */ +AvahiStringList*avahi_string_list_add_anonymous(AvahiStringList *l, size_t size); + +/** Same as avahi_string_list_add(), but takes a variable number of + * NUL terminated strings. The argument list must be terminated by a + * NULL pointer. Returns the new list start. */ +AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) AVAHI_GCC_SENTINEL; + +/** \cond fulldocs */ +/** Same as avahi_string_list_add_many(), but use a va_list + * structure. Returns the new list start. */ +AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va); +/** \endcond */ + +/** @} */ + +/** @{ \name String list operations */ + +/** Convert the string list object to a single character string, + * seperated by spaces and enclosed in "". avahi_free() the result! This + * function doesn't work well with strings that contain NUL bytes. */ +char* avahi_string_list_to_string(AvahiStringList *l); + +/** \cond fulldocs */ +/** Serialize the string list object in a way that is compatible with + * the storing of DNS TXT records. Strings longer than 255 bytes are truncated. */ +size_t avahi_string_list_serialize(AvahiStringList *l, void * data, size_t size); + +/** Inverse of avahi_string_list_serialize() */ +int avahi_string_list_parse(const void *data, size_t size, AvahiStringList **ret); +/** \endcond */ + +/** Compare to string lists */ +int avahi_string_list_equal(const AvahiStringList *a, const AvahiStringList *b); + +/** Copy a string list */ +AvahiStringList *avahi_string_list_copy(const AvahiStringList *l); + +/** Reverse the string list. */ +AvahiStringList* avahi_string_list_reverse(AvahiStringList *l); + +/** Return the number of elements in the string list */ +unsigned avahi_string_list_length(const AvahiStringList *l); + +/** @} */ + +/** @{ \name Accessing items */ + +/** Returns the next item in the string list */ +AvahiStringList *avahi_string_list_get_next(AvahiStringList *l); + +/** Returns the text for the current item */ +uint8_t *avahi_string_list_get_text(AvahiStringList *l); + +/** Returns the size of the current text */ +size_t avahi_string_list_get_size(AvahiStringList *l); + +/** @} */ + +/** @{ \name DNS-SD TXT pair handling */ + +/** Find the string list entry for the given DNS-SD TXT key */ +AvahiStringList *avahi_string_list_find(AvahiStringList *l, const char *key); + +/** Return the DNS-SD TXT key and value for the specified string list + * item. If size is not NULL it will be filled with the length of + * value. (for strings containing NUL bytes). If the entry doesn't + * contain a value *value will be set to NULL. You need to + * avahi_free() the strings returned in *key and *value. */ +int avahi_string_list_get_pair(AvahiStringList *l, char **key, char **value, size_t *size); + +/** Add a new DNS-SD TXT key value pair to the string list. value may + * be NULL in case you want to specify a key without a value */ +AvahiStringList *avahi_string_list_add_pair(AvahiStringList *l, const char *key, const char *value); + +/** Same as avahi_string_list_add_pair() but allow strings containing NUL bytes in *value. */ +AvahiStringList *avahi_string_list_add_pair_arbitrary(AvahiStringList *l, const char *key, const uint8_t *value, size_t size); + +/** @} */ + +/** \cond fulldocs */ +/** Try to find a magic service cookie in the specified DNS-SD string + * list. Or return AVAHI_SERVICE_COOKIE_INVALID if none is found. */ +uint32_t avahi_string_list_get_service_cookie(AvahiStringList *l); +/** \endcond */ + +AVAHI_C_DECL_END + +#endif + diff --git a/3rdparty/QtZeroConf/avahi-common/thread-watch.c b/3rdparty/QtZeroConf/avahi-common/thread-watch.c new file mode 100644 index 000000000..ecb202bdf --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/thread-watch.c @@ -0,0 +1,186 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "llist.h" +#include "malloc.h" +#include "timeval.h" +#include "simple-watch.h" +#include "thread-watch.h" + +struct AvahiThreadedPoll { + AvahiSimplePoll *simple_poll; + pthread_t thread_id; + pthread_mutex_t mutex; + int thread_running; + int retval; +}; + +static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) { + pthread_mutex_t *mutex = userdata; + int r; + + /* Before entering poll() we unlock the mutex, so that + * avahi_simple_poll_quit() can succeed from another thread. */ + + pthread_mutex_unlock(mutex); + r = poll(ufds, nfds, timeout); + pthread_mutex_lock(mutex); + + return r; +} + +static void* thread(void *userdata){ + AvahiThreadedPoll *p = userdata; + sigset_t mask; + + /* Make sure that signals are delivered to the main thread */ + sigfillset(&mask); + pthread_sigmask(SIG_BLOCK, &mask, NULL); + + pthread_mutex_lock(&p->mutex); + p->retval = avahi_simple_poll_loop(p->simple_poll); + pthread_mutex_unlock(&p->mutex); + + return NULL; +} + +AvahiThreadedPoll *avahi_threaded_poll_new(void) { + AvahiThreadedPoll *p; + + if (!(p = avahi_new(AvahiThreadedPoll, 1))) + goto fail; /* OOM */ + + if (!(p->simple_poll = avahi_simple_poll_new())) + goto fail; + + pthread_mutex_init(&p->mutex, NULL); + + avahi_simple_poll_set_func(p->simple_poll, poll_func, &p->mutex); + + p->thread_running = 0; + + return p; + +fail: + if (p) { + if (p->simple_poll) { + avahi_simple_poll_free(p->simple_poll); + pthread_mutex_destroy(&p->mutex); + } + + avahi_free(p); + } + + return NULL; +} + +void avahi_threaded_poll_free(AvahiThreadedPoll *p) { + assert(p); + + /* Make sure that this function is not called from the helper thread */ + assert(!p->thread_running || !pthread_equal(pthread_self(), p->thread_id)); + + if (p->thread_running) + avahi_threaded_poll_stop(p); + + if (p->simple_poll) + avahi_simple_poll_free(p->simple_poll); + + pthread_mutex_destroy(&p->mutex); + avahi_free(p); +} + +const AvahiPoll* avahi_threaded_poll_get(AvahiThreadedPoll *p) { + assert(p); + + return avahi_simple_poll_get(p->simple_poll); +} + +int avahi_threaded_poll_start(AvahiThreadedPoll *p) { + assert(p); + + assert(!p->thread_running); + + if (pthread_create(&p->thread_id, NULL, thread, p) < 0) + return -1; + + p->thread_running = 1; + + return 0; +} + +int avahi_threaded_poll_stop(AvahiThreadedPoll *p) { + assert(p); + + if (!p->thread_running) + return -1; + + /* Make sure that this function is not called from the helper thread */ + assert(!pthread_equal(pthread_self(), p->thread_id)); + + pthread_mutex_lock(&p->mutex); + avahi_simple_poll_quit(p->simple_poll); + pthread_mutex_unlock(&p->mutex); + + pthread_join(p->thread_id, NULL); + p->thread_running = 0; + + return p->retval; +} + +void avahi_threaded_poll_quit(AvahiThreadedPoll *p) { + assert(p); + + /* Make sure that this function is called from the helper thread */ + assert(pthread_equal(pthread_self(), p->thread_id)); + + avahi_simple_poll_quit(p->simple_poll); +} + +void avahi_threaded_poll_lock(AvahiThreadedPoll *p) { + assert(p); + + /* Make sure that this function is not called from the helper thread */ + assert(!p->thread_running || !pthread_equal(pthread_self(), p->thread_id)); + + pthread_mutex_lock(&p->mutex); +} + +void avahi_threaded_poll_unlock(AvahiThreadedPoll *p) { + assert(p); + + /* Make sure that this function is not called from the helper thread */ + assert(!p->thread_running || !pthread_equal(pthread_self(), p->thread_id)); + + pthread_mutex_unlock(&p->mutex); +} diff --git a/3rdparty/QtZeroConf/avahi-common/thread-watch.h b/3rdparty/QtZeroConf/avahi-common/thread-watch.h new file mode 100644 index 000000000..1b44ccb09 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/thread-watch.h @@ -0,0 +1,80 @@ +#ifndef foothreadedwatchhfoo +#define foothreadedwatchhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file thread-watch.h Threaded poll() based main loop implementation */ + +#include +#include +#include + +AVAHI_C_DECL_BEGIN + +/** A main loop object that runs an AvahiSimplePoll in its own thread. \since 0.6.4 */ +typedef struct AvahiThreadedPoll AvahiThreadedPoll; + +/** Create a new event loop object. This will allocate the internal + * AvahiSimplePoll, but will not start the helper thread. \since 0.6.4 */ +AvahiThreadedPoll *avahi_threaded_poll_new(void); + +/** Free an event loop object. This will stop the associated event loop + * thread (if it is running). \since 0.6.4 */ +void avahi_threaded_poll_free(AvahiThreadedPoll *p); + +/** Return the abstracted poll API object for this event loop + * object. The will return the same pointer each time it is + * called. \since 0.6.4 */ +const AvahiPoll* avahi_threaded_poll_get(AvahiThreadedPoll *p); + +/** Start the event loop helper thread. After the thread has started + * you must make sure to access the event loop object + * (AvahiThreadedPoll, AvahiPoll and all its associated objects) + * synchronized, i.e. with proper locking. You may want to use + * avahi_threaded_poll_lock()/avahi_threaded_poll_unlock() for this, + * which will lock the the entire event loop. Please note that event + * loop callback functions are called from the event loop helper thread + * with that lock held, i.e. avahi_threaded_poll_lock() calls are not + * required from event callbacks. \since 0.6.4 */ +int avahi_threaded_poll_start(AvahiThreadedPoll *p); + +/** Request that the event loop quits and the associated thread + stops. Call this from outside the helper thread if you want to shut + it down. \since 0.6.4 */ +int avahi_threaded_poll_stop(AvahiThreadedPoll *p); + +/** Request that the event loop quits and the associated thread + stops. Call this from inside the helper thread if you want to shut it + down. \since 0.6.4 */ +void avahi_threaded_poll_quit(AvahiThreadedPoll *p); + +/** Lock the main loop object. Use this if you want to access the event + * loop objects (such as creating a new event source) from anything + * else but the event loop helper thread, i.e. from anything else but event + * loop callbacks \since 0.6.4 */ +void avahi_threaded_poll_lock(AvahiThreadedPoll *p); + +/** Unlock the event loop object, use this as counterpart to + * avahi_threaded_poll_lock() \since 0.6.4 */ +void avahi_threaded_poll_unlock(AvahiThreadedPoll *p); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/timeval.c b/3rdparty/QtZeroConf/avahi-common/timeval.c new file mode 100644 index 000000000..cdb0f099e --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/timeval.c @@ -0,0 +1,123 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "timeval.h" + +int avahi_timeval_compare(const struct timeval *a, const struct timeval *b) { + assert(a); + assert(b); + + if (a->tv_sec < b->tv_sec) + return -1; + + if (a->tv_sec > b->tv_sec) + return 1; + + if (a->tv_usec < b->tv_usec) + return -1; + + if (a->tv_usec > b->tv_usec) + return 1; + + return 0; +} + +AvahiUsec avahi_timeval_diff(const struct timeval *a, const struct timeval *b) { + assert(a); + assert(b); + + if (avahi_timeval_compare(a, b) < 0) + return - avahi_timeval_diff(b, a); + + return ((AvahiUsec) a->tv_sec - b->tv_sec)*1000000 + a->tv_usec - b->tv_usec; +} + +struct timeval* avahi_timeval_add(struct timeval *a, AvahiUsec usec) { + AvahiUsec u; + assert(a); + + u = usec + a->tv_usec; + + if (u < 0) { + a->tv_usec = (long) (1000000 + (u % 1000000)); + a->tv_sec += (long) (-1 + (u / 1000000)); + } else { + a->tv_usec = (long) (u % 1000000); + a->tv_sec += (long) (u / 1000000); + } + + return a; +} + +AvahiUsec avahi_age(const struct timeval *a) { + struct timeval now; + + assert(a); + + gettimeofday(&now, NULL); + + return avahi_timeval_diff(&now, a); +} + +struct timeval *avahi_elapse_time(struct timeval *tv, unsigned msec, unsigned jitter) { + assert(tv); + + gettimeofday(tv, NULL); + + if (msec) + avahi_timeval_add(tv, (AvahiUsec) msec*1000); + + if (jitter) { + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + static int last_rand; + static time_t timestamp = 0; + + time_t now; + int r; + + now = time(NULL); + + pthread_mutex_lock(&mutex); + if (now >= timestamp + 10) { + timestamp = now; + last_rand = rand(); + } + + r = last_rand; + + pthread_mutex_unlock(&mutex); + + /* We use the same jitter for 10 seconds. That way our + * time events elapse in bursts which has the advantage that + * packet data can be aggregated better */ + + avahi_timeval_add(tv, (AvahiUsec) (jitter*1000.0*r/(RAND_MAX+1.0))); + } + + return tv; +} + diff --git a/3rdparty/QtZeroConf/avahi-common/timeval.h b/3rdparty/QtZeroConf/avahi-common/timeval.h new file mode 100644 index 000000000..6470f015b --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/timeval.h @@ -0,0 +1,54 @@ +#ifndef footimevalhfoo +#define footimevalhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file timeval.h Functions to facilitate timeval handling */ + +#include +#include + +#include + +AVAHI_C_DECL_BEGIN + +/** A numeric data type for storing microsecond values. (signed 64bit integer) */ +typedef int64_t AvahiUsec; + +/** Compare two timeval structures and return a negative value when a < b, 0 when a == b and a positive value otherwise */ +int avahi_timeval_compare(const struct timeval *a, const struct timeval *b); + +/** Calculate the difference between two timeval structures as microsecond value */ +AvahiUsec avahi_timeval_diff(const struct timeval *a, const struct timeval *b); + +/** Add a number of microseconds to the specified timeval structure and return it. *a is modified. */ +struct timeval* avahi_timeval_add(struct timeval *a, AvahiUsec usec); + +/** Return the difference between the current time and *a. Positive if *a was earlier */ +AvahiUsec avahi_age(const struct timeval *a); + +/** Fill *tv with the current time plus "ms" milliseconds plus an + * extra jitter of "j" milliseconds. Pass 0 for j if you don't want + * the jitter */ +struct timeval *avahi_elapse_time(struct timeval *tv, unsigned ms, unsigned j); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/utf8.c b/3rdparty/QtZeroConf/avahi-common/utf8.c new file mode 100644 index 000000000..ab10ba06a --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/utf8.c @@ -0,0 +1,110 @@ +/* This file is based on the GLIB utf8 validation functions. The + * original license text follows. */ + +/* gutf8.c - Operations on UTF-8 strings. + * + * Copyright (C) 1999 Tom Tromey + * Copyright (C) 2000 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "utf8.h" + +#define UNICODE_VALID(Char) \ + ((Char) < 0x110000 && \ + (((Char) & 0xFFFFF800) != 0xD800) && \ + ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ + ((Char) & 0xFFFE) != 0xFFFE) + + +#define CONTINUATION_CHAR \ + do { \ + if ((*(const unsigned char *)p & 0xc0) != 0x80) /* 10xxxxxx */ \ + goto error; \ + val <<= 6; \ + val |= (*(const unsigned char *)p) & 0x3f; \ + } while(0) + + +const char * +avahi_utf8_valid (const char *str) + +{ + unsigned val = 0; + unsigned min = 0; + const char *p; + + for (p = str; *p; p++) + { + if (*(const unsigned char *)p < 128) + /* done */; + else + { + if ((*(const unsigned char *)p & 0xe0) == 0xc0) /* 110xxxxx */ + { + if ( ((*(const unsigned char *)p & 0x1e) == 0)) + goto error; + p++; + if ( ((*(const unsigned char *)p & 0xc0) != 0x80)) /* 10xxxxxx */ + goto error; + } + else + { + if ((*(const unsigned char *)p & 0xf0) == 0xe0) /* 1110xxxx */ + { + min = (1 << 11); + val = *(const unsigned char *)p & 0x0f; + goto TWO_REMAINING; + } + else if ((*(const unsigned char *)p & 0xf8) == 0xf0) /* 11110xxx */ + { + min = (1 << 16); + val = *(const unsigned char *)p & 0x07; + } + else + goto error; + + p++; + CONTINUATION_CHAR; + TWO_REMAINING: + p++; + CONTINUATION_CHAR; + p++; + CONTINUATION_CHAR; + + if ( (val < min)) + goto error; + + if ( (!UNICODE_VALID(val))) + goto error; + } + + continue; + + error: + return NULL; + } + } + + return str; +} diff --git a/3rdparty/QtZeroConf/avahi-common/utf8.h b/3rdparty/QtZeroConf/avahi-common/utf8.h new file mode 100644 index 000000000..dc1ce4bc9 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/utf8.h @@ -0,0 +1,33 @@ +#ifndef fooutf8hfoo +#define fooutf8hfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include + +AVAHI_C_DECL_BEGIN + +const char *avahi_utf8_valid(const char *str); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-common/watch.h b/3rdparty/QtZeroConf/avahi-common/watch.h new file mode 100644 index 000000000..eea12ec49 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-common/watch.h @@ -0,0 +1,97 @@ +#ifndef foowatchhfoo +#define foowatchhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file watch.h Simplistic main loop abstraction */ + +#include +#include + +#include + +AVAHI_C_DECL_BEGIN + +/** An I/O watch object */ +typedef struct AvahiWatch AvahiWatch; + +/** A timeout watch object */ +typedef struct AvahiTimeout AvahiTimeout; + +/** An event polling abstraction object */ +typedef struct AvahiPoll AvahiPoll; + +/** Type of watch events */ +typedef enum { + AVAHI_WATCH_IN = POLLIN, /**< Input event */ + AVAHI_WATCH_OUT = POLLOUT, /**< Output event */ + AVAHI_WATCH_ERR = POLLERR, /**< Error event */ + AVAHI_WATCH_HUP = POLLHUP /**< Hangup event */ +} AvahiWatchEvent; + +/** Called whenever an I/O event happens on an I/O watch */ +typedef void (*AvahiWatchCallback)(AvahiWatch *w, int fd, AvahiWatchEvent event, void *userdata); + +/** Called when the timeout is reached */ +typedef void (*AvahiTimeoutCallback)(AvahiTimeout *t, void *userdata); + +/** Defines an abstracted event polling API. This may be used to + connect Avahi to other main loops. This is loosely based on Unix + poll(2). A consumer will call watch_new() for all file descriptors it + wants to listen for events on. In addition he can call timeout_new() + to define time based events .*/ +struct AvahiPoll { + + /** Some abstract user data usable by the provider of the API */ + void* userdata; + + /** Create a new watch for the specified file descriptor and for + * the specified events. The API will call the callback function + * whenever any of the events happens. */ + AvahiWatch* (*watch_new)(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata); + + /** Update the events to wait for. It is safe to call this function from an AvahiWatchCallback */ + void (*watch_update)(AvahiWatch *w, AvahiWatchEvent event); + + /** Return the events that happened. It is safe to call this function from an AvahiWatchCallback */ + AvahiWatchEvent (*watch_get_events)(AvahiWatch *w); + + /** Free a watch. It is safe to call this function from an AvahiWatchCallback */ + void (*watch_free)(AvahiWatch *w); + + /** Set a wakeup time for the polling loop. The API will call the + callback function when the absolute time *tv is reached. If tv is + NULL, the timeout is disabled. After the timeout expired the + callback function will be called and the timeout is disabled. You + can reenable it by calling timeout_update() */ + AvahiTimeout* (*timeout_new)(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata); + + /** Update the absolute expiration time for a timeout, If tv is + * NULL, the timeout is disabled. It is safe to call this function from an AvahiTimeoutCallback */ + void (*timeout_update)(AvahiTimeout *, const struct timeval *tv); + + /** Free a timeout. It is safe to call this function from an AvahiTimeoutCallback */ + void (*timeout_free)(AvahiTimeout *t); +}; + +AVAHI_C_DECL_END + +#endif + diff --git a/3rdparty/QtZeroConf/avahi-core/addr-util.c b/3rdparty/QtZeroConf/avahi-core/addr-util.c new file mode 100644 index 000000000..979b1b774 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/addr-util.c @@ -0,0 +1,94 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "addr-util.h" + +AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr) { + assert(sa); + assert(ret_addr); + + assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); + + ret_addr->proto = avahi_af_to_proto(sa->sa_family); + + if (sa->sa_family == AF_INET) + memcpy(&ret_addr->data.ipv4, &((const struct sockaddr_in*) sa)->sin_addr, sizeof(ret_addr->data.ipv4)); + else + memcpy(&ret_addr->data.ipv6, &((const struct sockaddr_in6*) sa)->sin6_addr, sizeof(ret_addr->data.ipv6)); + + return ret_addr; +} + +uint16_t avahi_port_from_sockaddr(const struct sockaddr* sa) { + assert(sa); + + assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); + + if (sa->sa_family == AF_INET) + return ntohs(((const struct sockaddr_in*) sa)->sin_port); + else + return ntohs(((const struct sockaddr_in6*) sa)->sin6_port); +} + +int avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a) { + + static const uint8_t ipv4_in_ipv6[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF + }; + + assert(a); + + if (a->proto != AVAHI_PROTO_INET6) + return 0; + + return memcmp(a->data.ipv6.address, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0; +} + +#define IPV4LL_NETWORK 0xA9FE0000L +#define IPV4LL_NETMASK 0xFFFF0000L +#define IPV6LL_NETWORK 0xFE80 +#define IPV6LL_NETMASK 0xFFC0 + +int avahi_address_is_link_local(const AvahiAddress *a) { + assert(a); + + if (a->proto == AVAHI_PROTO_INET) { + uint32_t n = ntohl(a->data.ipv4.address); + return (n & IPV4LL_NETMASK) == IPV4LL_NETWORK; + } + else if (a->proto == AVAHI_PROTO_INET6) { + unsigned n = (a->data.ipv6.address[0] << 8) | (a->data.ipv6.address[1] << 0); + return (n & IPV6LL_NETMASK) == IPV6LL_NETWORK; + } + + return 0; +} diff --git a/3rdparty/QtZeroConf/avahi-core/addr-util.h b/3rdparty/QtZeroConf/avahi-core/addr-util.h new file mode 100644 index 000000000..66a9422ac --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/addr-util.h @@ -0,0 +1,47 @@ +#ifndef fooaddrutilhfoo +#define fooaddrutilhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Make an address structture of a sockaddr structure */ +AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr); + +/** Return the port number of a sockaddr structure (either IPv4 or IPv6) */ +uint16_t avahi_port_from_sockaddr(const struct sockaddr* sa); + +/** Check whether the specified IPv6 address is in fact an + * encapsulated IPv4 address, returns 1 if yes, 0 otherwise */ +int avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a); + +/** Check whether the specified address is a link-local IPv4 or IPv6 address; + * returns 1 if yes, 0 otherwise */ +int avahi_address_is_link_local(const AvahiAddress *a); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/announce.c b/3rdparty/QtZeroConf/avahi-core/announce.c new file mode 100644 index 000000000..ccdbf15ad --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/announce.c @@ -0,0 +1,524 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include "announce.h" +#include "log.h" +#include "rr-util.h" + +#define AVAHI_ANNOUNCEMENT_JITTER_MSEC 250 +#define AVAHI_PROBE_JITTER_MSEC 250 +#define AVAHI_PROBE_INTERVAL_MSEC 250 + +static void remove_announcer(AvahiServer *s, AvahiAnnouncer *a) { + assert(s); + assert(a); + + if (a->time_event) + avahi_time_event_free(a->time_event); + + AVAHI_LLIST_REMOVE(AvahiAnnouncer, by_interface, a->interface->announcers, a); + AVAHI_LLIST_REMOVE(AvahiAnnouncer, by_entry, a->entry->announcers, a); + + avahi_free(a); +} + +static void elapse_announce(AvahiTimeEvent *e, void *userdata); + +static void set_timeout(AvahiAnnouncer *a, const struct timeval *tv) { + assert(a); + + if (!tv) { + if (a->time_event) { + avahi_time_event_free(a->time_event); + a->time_event = NULL; + } + } else { + + if (a->time_event) + avahi_time_event_update(a->time_event, tv); + else + a->time_event = avahi_time_event_new(a->server->time_event_queue, tv, elapse_announce, a); + } +} + +static void next_state(AvahiAnnouncer *a); + +void avahi_s_entry_group_check_probed(AvahiSEntryGroup *g, int immediately) { + AvahiEntry *e; + assert(g); + assert(!g->dead); + + /* Check whether all group members have been probed */ + + if (g->state != AVAHI_ENTRY_GROUP_REGISTERING || g->n_probing > 0) + return; + + avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_ESTABLISHED); + + if (g->dead) + return; + + for (e = g->entries; e; e = e->by_group_next) { + AvahiAnnouncer *a; + + for (a = e->announcers; a; a = a->by_entry_next) { + + if (a->state != AVAHI_WAITING) + continue; + + a->state = AVAHI_ANNOUNCING; + + if (immediately) { + /* Shortcut */ + + a->n_iteration = 1; + next_state(a); + } else { + struct timeval tv; + a->n_iteration = 0; + avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC); + set_timeout(a, &tv); + } + } + } +} + +static void next_state(AvahiAnnouncer *a) { + assert(a); + + if (a->state == AVAHI_WAITING) { + + assert(a->entry->group); + + avahi_s_entry_group_check_probed(a->entry->group, 1); + + } else if (a->state == AVAHI_PROBING) { + + if (a->n_iteration >= 4) { + /* Probing done */ + + if (a->entry->group) { + assert(a->entry->group->n_probing); + a->entry->group->n_probing--; + } + + if (a->entry->group && a->entry->group->state == AVAHI_ENTRY_GROUP_REGISTERING) + a->state = AVAHI_WAITING; + else { + a->state = AVAHI_ANNOUNCING; + a->n_iteration = 1; + } + + set_timeout(a, NULL); + next_state(a); + } else { + struct timeval tv; + + avahi_interface_post_probe(a->interface, a->entry->record, 0); + + avahi_elapse_time(&tv, AVAHI_PROBE_INTERVAL_MSEC, 0); + set_timeout(a, &tv); + + a->n_iteration++; + } + + } else if (a->state == AVAHI_ANNOUNCING) { + + if (a->entry->flags & AVAHI_PUBLISH_UNIQUE) + /* Send the whole rrset at once */ + avahi_server_prepare_matching_responses(a->server, a->interface, a->entry->record->key, 0); + else + avahi_server_prepare_response(a->server, a->interface, a->entry, 0, 0); + + avahi_server_generate_response(a->server, a->interface, NULL, NULL, 0, 0, 0); + + if (++a->n_iteration >= 4) { + /* Announcing done */ + + a->state = AVAHI_ESTABLISHED; + + set_timeout(a, NULL); + } else { + struct timeval tv; + avahi_elapse_time(&tv, a->sec_delay*1000, AVAHI_ANNOUNCEMENT_JITTER_MSEC); + + if (a->n_iteration < 10) + a->sec_delay *= 2; + + set_timeout(a, &tv); + } + } +} + +static void elapse_announce(AvahiTimeEvent *e, void *userdata) { + assert(e); + + next_state(userdata); +} + +static AvahiAnnouncer *get_announcer(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { + AvahiAnnouncer *a; + + assert(s); + assert(e); + assert(i); + + for (a = e->announcers; a; a = a->by_entry_next) + if (a->interface == i) + return a; + + return NULL; +} + +static void go_to_initial_state(AvahiAnnouncer *a) { + AvahiEntry *e; + struct timeval tv; + + assert(a); + e = a->entry; + + if ((e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE)) + a->state = AVAHI_PROBING; + else if (!(e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)) { + + if (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED) + a->state = AVAHI_ANNOUNCING; + else + a->state = AVAHI_WAITING; + + } else + a->state = AVAHI_ESTABLISHED; + + a->n_iteration = 1; + a->sec_delay = 1; + + if (a->state == AVAHI_PROBING && e->group) + e->group->n_probing++; + + if (a->state == AVAHI_PROBING) + set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC)); + else if (a->state == AVAHI_ANNOUNCING) + set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC)); + else + set_timeout(a, NULL); +} + +static void new_announcer(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) { + AvahiAnnouncer *a; + + assert(s); + assert(i); + assert(e); + assert(!e->dead); + + if (!avahi_interface_match(i, e->interface, e->protocol) || !i->announcing || !avahi_entry_is_commited(e)) + return; + + /* We don't want duplicate announcers */ + if (get_announcer(s, e, i)) + return; + + if ((!(a = avahi_new(AvahiAnnouncer, 1)))) { + avahi_log_error(__FILE__": Out of memory."); + return; + } + + a->server = s; + a->interface = i; + a->entry = e; + a->time_event = NULL; + + AVAHI_LLIST_PREPEND(AvahiAnnouncer, by_interface, i->announcers, a); + AVAHI_LLIST_PREPEND(AvahiAnnouncer, by_entry, e->announcers, a); + + go_to_initial_state(a); +} + +void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) { + AvahiEntry *e; + + assert(s); + assert(i); + + if (!i->announcing) + return; + + for (e = s->entries; e; e = e->entries_next) + if (!e->dead) + new_announcer(s, i, e); +} + +static void announce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + AvahiEntry *e = userdata; + + assert(m); + assert(i); + assert(e); + assert(!e->dead); + + new_announcer(m->server, i, e); +} + +void avahi_announce_entry(AvahiServer *s, AvahiEntry *e) { + assert(s); + assert(e); + assert(!e->dead); + + avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e); +} + +void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g) { + AvahiEntry *e; + + assert(s); + assert(g); + + for (e = g->entries; e; e = e->by_group_next) + if (!e->dead) + avahi_announce_entry(s, e); +} + +int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { + AvahiAnnouncer *a; + + assert(s); + assert(e); + assert(i); + assert(!e->dead); + + if (!(a = get_announcer(s, e, i))) + return 0; + + return + a->state == AVAHI_ANNOUNCING || + a->state == AVAHI_ESTABLISHED || + (a->state == AVAHI_WAITING && !(e->flags & AVAHI_PUBLISH_UNIQUE)); +} + +int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { + AvahiAnnouncer *a; + + assert(s); + assert(e); + assert(i); + assert(!e->dead); + + if (!(a = get_announcer(s, e, i))) + return 0; + + return + a->state == AVAHI_PROBING || + (a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE)); +} + +void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { + AvahiAnnouncer *a; + + assert(s); + assert(e); + assert(i); + + if (!(a = get_announcer(s, e, i))) + return; + + if (a->state == AVAHI_PROBING && a->entry->group) + a->entry->group->n_probing--; + + go_to_initial_state(a); +} + +static AvahiRecord *make_goodbye_record(AvahiRecord *r) { + AvahiRecord *g; + + assert(r); + + if (!(g = avahi_record_copy(r))) + return NULL; /* OOM */ + + assert(g->ref == 1); + g->ttl = 0; + + return g; +} + +static int is_duplicate_entry(AvahiServer *s, AvahiEntry *e) { + AvahiEntry *i; + + assert(s); + assert(e); + + for (i = avahi_hashmap_lookup(s->entries_by_key, e->record->key); i; i = i->by_key_next) { + + if ((i == e) || (i->dead)) + continue; + + if (!avahi_record_equal_no_ttl(i->record, e->record)) + continue; + + return 1; + } + + return 0; +} + +static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + AvahiEntry *e = userdata; + AvahiRecord *g; + + assert(m); + assert(i); + assert(e); + assert(!e->dead); + + if (!avahi_interface_match(i, e->interface, e->protocol)) + return; + + if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE) + return; + + if (!avahi_entry_is_registered(m->server, e, i)) + return; + + if (is_duplicate_entry(m->server, e)) + return; + + if (!(g = make_goodbye_record(e->record))) + return; /* OOM */ + + avahi_interface_post_response(i, g, e->flags & AVAHI_PUBLISH_UNIQUE, NULL, 1); + avahi_record_unref(g); +} + +static void reannounce(AvahiAnnouncer *a) { + AvahiEntry *e; + struct timeval tv; + + assert(a); + e = a->entry; + + /* If the group this entry belongs to is not even commited, there's nothing to reannounce */ + if (e->group && (e->group->state == AVAHI_ENTRY_GROUP_UNCOMMITED || e->group->state == AVAHI_ENTRY_GROUP_COLLISION)) + return; + + /* Because we might change state we decrease the probing counter first */ + if (a->state == AVAHI_PROBING && a->entry->group) + a->entry->group->n_probing--; + + if (a->state == AVAHI_PROBING || + (a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE))) + + /* We were probing or waiting after probe, so we restart probing from the beginning here */ + + a->state = AVAHI_PROBING; + else if (a->state == AVAHI_WAITING) + + /* We were waiting, but were not probing before, so we continue waiting */ + a->state = AVAHI_WAITING; + + else if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE) + + /* No announcer needed */ + a->state = AVAHI_ESTABLISHED; + + else { + + /* Ok, let's restart announcing */ + a->state = AVAHI_ANNOUNCING; + } + + /* Now let's increase the probing counter again */ + if (a->state == AVAHI_PROBING && e->group) + e->group->n_probing++; + + a->n_iteration = 1; + a->sec_delay = 1; + + if (a->state == AVAHI_PROBING) + set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC)); + else if (a->state == AVAHI_ANNOUNCING) + set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC)); + else + set_timeout(a, NULL); +} + + +static void reannounce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + AvahiEntry *e = userdata; + AvahiAnnouncer *a; + + assert(m); + assert(i); + assert(e); + assert(!e->dead); + + if (!(a = get_announcer(m->server, e, i))) + return; + + reannounce(a); +} + +void avahi_reannounce_entry(AvahiServer *s, AvahiEntry *e) { + + assert(s); + assert(e); + assert(!e->dead); + + avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, reannounce_walk_callback, e); +} + +void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye, int remove) { + assert(s); + assert(i); + + if (send_goodbye) + if (i->announcing) { + AvahiEntry *e; + + for (e = s->entries; e; e = e->entries_next) + if (!e->dead) + send_goodbye_callback(s->monitor, i, e); + } + + if (remove) + while (i->announcers) + remove_announcer(s, i->announcers); +} + +void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int send_goodbye, int remove) { + assert(s); + assert(e); + + if (send_goodbye) + if (!e->dead) + avahi_interface_monitor_walk(s->monitor, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, send_goodbye_callback, e); + + if (remove) + while (e->announcers) + remove_announcer(s, e->announcers); +} + diff --git a/3rdparty/QtZeroConf/avahi-core/announce.h b/3rdparty/QtZeroConf/avahi-core/announce.h new file mode 100644 index 000000000..fd23d8be2 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/announce.h @@ -0,0 +1,69 @@ +#ifndef fooannouncehfoo +#define fooannouncehfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct AvahiAnnouncer AvahiAnnouncer; + +#include +#include "iface.h" +#include "internal.h" +#include "timeeventq.h" +#include "publish.h" + +typedef enum { + AVAHI_PROBING, /* probing phase */ + AVAHI_WAITING, /* wait for other records in group */ + AVAHI_ANNOUNCING, /* announcing phase */ + AVAHI_ESTABLISHED /* we'e established */ +} AvahiAnnouncerState; + +struct AvahiAnnouncer { + AvahiServer *server; + AvahiInterface *interface; + AvahiEntry *entry; + + AvahiTimeEvent *time_event; + + AvahiAnnouncerState state; + unsigned n_iteration; + unsigned sec_delay; + + AVAHI_LLIST_FIELDS(AvahiAnnouncer, by_interface); + AVAHI_LLIST_FIELDS(AvahiAnnouncer, by_entry); +}; + +void avahi_announce_interface(AvahiServer *s, AvahiInterface *i); +void avahi_announce_entry(AvahiServer *s, AvahiEntry *e); +void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g); + +void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); + +void avahi_s_entry_group_check_probed(AvahiSEntryGroup *g, int immediately); + +int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); +int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); + +void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye, int rem); +void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int send_goodbye, int rem); + +void avahi_reannounce_entry(AvahiServer *s, AvahiEntry *e); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/browse-dns-server.c b/3rdparty/QtZeroConf/avahi-core/browse-dns-server.c new file mode 100644 index 000000000..a51c38fc0 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/browse-dns-server.c @@ -0,0 +1,322 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include "browse.h" +#include "log.h" +#include "rr.h" + +typedef struct AvahiDNSServerInfo AvahiDNSServerInfo; + +struct AvahiDNSServerInfo { + AvahiSDNSServerBrowser *browser; + + AvahiIfIndex interface; + AvahiProtocol protocol; + AvahiRecord *srv_record; + AvahiSHostNameResolver *host_name_resolver; + AvahiAddress address; + AvahiLookupResultFlags flags; + + AVAHI_LLIST_FIELDS(AvahiDNSServerInfo, info); +}; + +struct AvahiSDNSServerBrowser { + AvahiServer *server; + + AvahiSRecordBrowser *record_browser; + AvahiSDNSServerBrowserCallback callback; + void* userdata; + AvahiProtocol aprotocol; + AvahiLookupFlags user_flags; + + unsigned n_info; + + AVAHI_LLIST_FIELDS(AvahiSDNSServerBrowser, browser); + AVAHI_LLIST_HEAD(AvahiDNSServerInfo, info); +}; + +static AvahiDNSServerInfo* get_server_info(AvahiSDNSServerBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r) { + AvahiDNSServerInfo *i; + + assert(b); + assert(r); + + for (i = b->info; i; i = i->info_next) + if (i->interface == interface && + i->protocol == protocol && + avahi_record_equal_no_ttl(r, i->srv_record)) + return i; + + return NULL; +} + +static void server_info_free(AvahiSDNSServerBrowser *b, AvahiDNSServerInfo *i) { + assert(b); + assert(i); + + avahi_record_unref(i->srv_record); + if (i->host_name_resolver) + avahi_s_host_name_resolver_free(i->host_name_resolver); + + AVAHI_LLIST_REMOVE(AvahiDNSServerInfo, info, b->info, i); + + assert(b->n_info >= 1); + b->n_info--; + + avahi_free(i); +} + +static void host_name_resolver_callback( + AvahiSHostNameResolver *r, + AVAHI_GCC_UNUSED AvahiIfIndex interface, + AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiResolverEvent event, + const char *host_name, + const AvahiAddress *a, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiDNSServerInfo *i = userdata; + + assert(r); + assert(host_name); + assert(i); + + switch (event) { + case AVAHI_RESOLVER_FOUND: { + i->address = *a; + + i->browser->callback( + i->browser, + i->interface, + i->protocol, + AVAHI_BROWSER_NEW, + i->srv_record->data.srv.name, + &i->address, + i->srv_record->data.srv.port, + i->flags | flags, + i->browser->userdata); + + break; + } + + case AVAHI_RESOLVER_FAILURE: + /* Ignore */ + break; + } + + avahi_s_host_name_resolver_free(i->host_name_resolver); + i->host_name_resolver = NULL; +} + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSDNSServerBrowser *b = userdata; + + assert(rr); + assert(b); + + /* Filter flags */ + flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; + + switch (event) { + case AVAHI_BROWSER_NEW: { + AvahiDNSServerInfo *i; + + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_SRV); + + if (get_server_info(b, interface, protocol, record)) + return; + + if (b->n_info >= 10) + return; + + if (!(i = avahi_new(AvahiDNSServerInfo, 1))) + return; /* OOM */ + + i->browser = b; + i->interface = interface; + i->protocol = protocol; + i->srv_record = avahi_record_ref(record); + i->host_name_resolver = avahi_s_host_name_resolver_new( + b->server, + interface, protocol, + record->data.srv.name, + b->aprotocol, + b->user_flags, + host_name_resolver_callback, i); + i->flags = flags; + + AVAHI_LLIST_PREPEND(AvahiDNSServerInfo, info, b->info, i); + + b->n_info++; + break; + } + + case AVAHI_BROWSER_REMOVE: { + AvahiDNSServerInfo *i; + + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_SRV); + + if (!(i = get_server_info(b, interface, protocol, record))) + return; + + if (!i->host_name_resolver) + b->callback( + b, + interface, + protocol, + event, + i->srv_record->data.srv.name, + &i->address, + i->srv_record->data.srv.port, + i->flags | flags, + b->userdata); + + server_info_free(b, i); + break; + } + + case AVAHI_BROWSER_FAILURE: + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + + b->callback( + b, + interface, + protocol, + event, + NULL, + NULL, + 0, + flags, + b->userdata); + + break; + } +} + +AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDNSServerType type, + AvahiProtocol aprotocol, + AvahiLookupFlags flags, + AvahiSDNSServerBrowserCallback callback, + void* userdata) { + + static const char * const type_table[AVAHI_DNS_SERVER_MAX] = { + "_domain._udp", + "_dns-update._udp" + }; + + AvahiSDNSServerBrowser *b; + AvahiKey *k = NULL; + char n[AVAHI_DOMAIN_NAME_MAX]; + int r; + + assert(server); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DNS_SERVER_MAX, AVAHI_ERR_INVALID_FLAGS); + + if (!domain) + domain = server->domain_name; + + if ((r = avahi_service_name_join(n, sizeof(n), NULL, type_table[type], domain)) < 0) { + avahi_server_set_errno(server, r); + return NULL; + } + + if (!(b = avahi_new(AvahiSDNSServerBrowser, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + b->server = server; + b->callback = callback; + b->userdata = userdata; + b->aprotocol = aprotocol; + b->n_info = 0; + b->user_flags = flags; + + AVAHI_LLIST_HEAD_INIT(AvahiDNSServerInfo, b->info); + AVAHI_LLIST_PREPEND(AvahiSDNSServerBrowser, browser, server->dns_server_browsers, b); + + if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) + goto fail; + + avahi_key_unref(k); + + return b; + +fail: + + if (k) + avahi_key_unref(k); + + avahi_s_dns_server_browser_free(b); + return NULL; +} + +void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b) { + assert(b); + + while (b->info) + server_info_free(b, b->info); + + AVAHI_LLIST_REMOVE(AvahiSDNSServerBrowser, browser, b->server->dns_server_browsers, b); + + if (b->record_browser) + avahi_s_record_browser_free(b->record_browser); + + avahi_free(b); +} + diff --git a/3rdparty/QtZeroConf/avahi-core/browse-domain.c b/3rdparty/QtZeroConf/avahi-core/browse-domain.c new file mode 100644 index 000000000..9705b2a52 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/browse-domain.c @@ -0,0 +1,235 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include "browse.h" +#include "log.h" + +struct AvahiSDomainBrowser { + int ref; + + AvahiServer *server; + + AvahiSRecordBrowser *record_browser; + + AvahiDomainBrowserType type; + AvahiSDomainBrowserCallback callback; + void* userdata; + + AvahiTimeEvent *defer_event; + + int all_for_now_scheduled; + + AVAHI_LLIST_FIELDS(AvahiSDomainBrowser, browser); +}; + +static void inc_ref(AvahiSDomainBrowser *b) { + assert(b); + assert(b->ref >= 1); + + b->ref++; +} + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSDomainBrowser *b = userdata; + char *n = NULL; + + assert(rr); + assert(b); + + if (event == AVAHI_BROWSER_ALL_FOR_NOW && + b->defer_event) { + + b->all_for_now_scheduled = 1; + return; + } + + /* Filter flags */ + flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; + + if (record) { + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + n = record->data.ptr.name; + + if (b->type == AVAHI_DOMAIN_BROWSER_BROWSE) { + AvahiStringList *l; + + /* Filter out entries defined statically */ + + for (l = b->server->config.browse_domains; l; l = l->next) + if (avahi_domain_equal((char*) l->text, n)) + return; + } + + } + + b->callback(b, interface, protocol, event, n, flags, b->userdata); +} + +static void defer_callback(AvahiTimeEvent *e, void *userdata) { + AvahiSDomainBrowser *b = userdata; + AvahiStringList *l; + + assert(e); + assert(b); + + assert(b->type == AVAHI_DOMAIN_BROWSER_BROWSE); + + avahi_time_event_free(b->defer_event); + b->defer_event = NULL; + + /* Increase ref counter */ + inc_ref(b); + + for (l = b->server->config.browse_domains; l; l = l->next) { + + /* Check whether this object still exists outside our own + * stack frame */ + if (b->ref <= 1) + break; + + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_NEW, (char*) l->text, AVAHI_LOOKUP_RESULT_STATIC, b->userdata); + } + + if (b->ref > 1) { + /* If the ALL_FOR_NOW event has already been scheduled, execute it now */ + + if (b->all_for_now_scheduled) + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_ALL_FOR_NOW, NULL, 0, b->userdata); + } + + /* Decrease ref counter */ + avahi_s_domain_browser_free(b); +} + +AvahiSDomainBrowser *avahi_s_domain_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDomainBrowserType type, + AvahiLookupFlags flags, + AvahiSDomainBrowserCallback callback, + void* userdata) { + + static const char * const type_table[AVAHI_DOMAIN_BROWSER_MAX] = { + "b", + "db", + "r", + "dr", + "lb" + }; + + AvahiSDomainBrowser *b; + AvahiKey *k = NULL; + char n[AVAHI_DOMAIN_NAME_MAX]; + int r; + + assert(server); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DOMAIN_BROWSER_MAX, AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + + if (!domain) + domain = server->domain_name; + + if ((r = avahi_service_name_join(n, sizeof(n), type_table[type], "_dns-sd._udp", domain)) < 0) { + avahi_server_set_errno(server, r); + return NULL; + } + + if (!(b = avahi_new(AvahiSDomainBrowser, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + b->ref = 1; + b->server = server; + b->callback = callback; + b->userdata = userdata; + b->record_browser = NULL; + b->type = type; + b->all_for_now_scheduled = 0; + b->defer_event = NULL; + + AVAHI_LLIST_PREPEND(AvahiSDomainBrowser, browser, server->domain_browsers, b); + + if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) + goto fail; + + avahi_key_unref(k); + + if (type == AVAHI_DOMAIN_BROWSER_BROWSE && b->server->config.browse_domains) + b->defer_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b); + + return b; + +fail: + + if (k) + avahi_key_unref(k); + + avahi_s_domain_browser_free(b); + + return NULL; +} + +void avahi_s_domain_browser_free(AvahiSDomainBrowser *b) { + assert(b); + + assert(b->ref >= 1); + if (--b->ref > 0) + return; + + AVAHI_LLIST_REMOVE(AvahiSDomainBrowser, browser, b->server->domain_browsers, b); + + if (b->record_browser) + avahi_s_record_browser_free(b->record_browser); + + if (b->defer_event) + avahi_time_event_free(b->defer_event); + + avahi_free(b); +} diff --git a/3rdparty/QtZeroConf/avahi-core/browse-service-type.c b/3rdparty/QtZeroConf/avahi-core/browse-service-type.c new file mode 100644 index 000000000..6fff07165 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/browse-service-type.c @@ -0,0 +1,157 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include "browse.h" +#include "log.h" + +struct AvahiSServiceTypeBrowser { + AvahiServer *server; + char *domain_name; + + AvahiSRecordBrowser *record_browser; + + AvahiSServiceTypeBrowserCallback callback; + void* userdata; + + AVAHI_LLIST_FIELDS(AvahiSServiceTypeBrowser, browser); +}; + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSServiceTypeBrowser *b = userdata; + + assert(rr); + assert(b); + + /* Filter flags */ + flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; + + if (record) { + char type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX]; + + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + + if (avahi_service_name_split(record->data.ptr.name, NULL, 0, type, sizeof(type), domain, sizeof(domain)) < 0) { + avahi_log_warn("Invalid service type '%s'", record->key->name); + return; + } + + b->callback(b, interface, protocol, event, type, domain, flags, b->userdata); + } else + b->callback(b, interface, protocol, event, NULL, b->domain_name, flags, b->userdata); +} + +AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiLookupFlags flags, + AvahiSServiceTypeBrowserCallback callback, + void* userdata) { + + AvahiSServiceTypeBrowser *b; + AvahiKey *k = NULL; + char n[AVAHI_DOMAIN_NAME_MAX]; + int r; + + assert(server); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + + if (!domain) + domain = server->domain_name; + + if ((r = avahi_service_name_join(n, sizeof(n), NULL, "_services._dns-sd._udp", domain)) < 0) { + avahi_server_set_errno(server, r); + return NULL; + } + + if (!(b = avahi_new(AvahiSServiceTypeBrowser, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + b->server = server; + b->callback = callback; + b->userdata = userdata; + b->record_browser = NULL; + + AVAHI_LLIST_PREPEND(AvahiSServiceTypeBrowser, browser, server->service_type_browsers, b); + + if (!(b->domain_name = avahi_normalize_name_strdup(domain))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) + goto fail; + + avahi_key_unref(k); + + return b; + +fail: + if (k) + avahi_key_unref(k); + + avahi_s_service_type_browser_free(b); + + return NULL; +} + +void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b) { + assert(b); + + AVAHI_LLIST_REMOVE(AvahiSServiceTypeBrowser, browser, b->server->service_type_browsers, b); + + if (b->record_browser) + avahi_s_record_browser_free(b->record_browser); + + avahi_free(b->domain_name); + avahi_free(b); +} + + diff --git a/3rdparty/QtZeroConf/avahi-core/browse-service.c b/3rdparty/QtZeroConf/avahi-core/browse-service.c new file mode 100644 index 000000000..dde36bc74 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/browse-service.c @@ -0,0 +1,167 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include "browse.h" +#include "log.h" + +struct AvahiSServiceBrowser { + AvahiServer *server; + char *domain_name; + char *service_type; + + AvahiSRecordBrowser *record_browser; + + AvahiSServiceBrowserCallback callback; + void* userdata; + + AVAHI_LLIST_FIELDS(AvahiSServiceBrowser, browser); +}; + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSServiceBrowser *b = userdata; + + assert(rr); + assert(b); + + /* Filter flags */ + flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; + + if (record) { + char service[AVAHI_LABEL_MAX], type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX]; + + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + + if (event == AVAHI_BROWSER_NEW && avahi_server_is_service_local(b->server, interface, protocol, record->data.ptr.name)) + flags |= AVAHI_LOOKUP_RESULT_LOCAL; + + if (avahi_service_name_split(record->data.ptr.name, service, sizeof(service), type, sizeof(type), domain, sizeof(domain)) < 0) { + avahi_log_warn("Failed to split '%s'", record->key->name); + return; + } + + b->callback(b, interface, protocol, event, service, type, domain, flags, b->userdata); + + } else + b->callback(b, interface, protocol, event, NULL, b->service_type, b->domain_name, flags, b->userdata); + +} + +AvahiSServiceBrowser *avahi_s_service_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *service_type, + const char *domain, + AvahiLookupFlags flags, + AvahiSServiceBrowserCallback callback, + void* userdata) { + + AvahiSServiceBrowser *b; + AvahiKey *k = NULL; + char n[AVAHI_DOMAIN_NAME_MAX]; + int r; + + assert(server); + assert(callback); + assert(service_type); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_generic(service_type), AVAHI_ERR_INVALID_SERVICE_TYPE); + + if (!domain) + domain = server->domain_name; + + if ((r = avahi_service_name_join(n, sizeof(n), NULL, service_type, domain)) < 0) { + avahi_server_set_errno(server, r); + return NULL; + } + + if (!(b = avahi_new(AvahiSServiceBrowser, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + b->server = server; + b->domain_name = b->service_type = NULL; + b->callback = callback; + b->userdata = userdata; + b->record_browser = NULL; + + AVAHI_LLIST_PREPEND(AvahiSServiceBrowser, browser, server->service_browsers, b); + + if (!(b->domain_name = avahi_normalize_name_strdup(domain)) || + !(b->service_type = avahi_normalize_name_strdup(service_type))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) + goto fail; + + avahi_key_unref(k); + + return b; + +fail: + + if (k) + avahi_key_unref(k); + + avahi_s_service_browser_free(b); + return NULL; +} + +void avahi_s_service_browser_free(AvahiSServiceBrowser *b) { + assert(b); + + AVAHI_LLIST_REMOVE(AvahiSServiceBrowser, browser, b->server->service_browsers, b); + + if (b->record_browser) + avahi_s_record_browser_free(b->record_browser); + + avahi_free(b->domain_name); + avahi_free(b->service_type); + avahi_free(b); +} diff --git a/3rdparty/QtZeroConf/avahi-core/browse.c b/3rdparty/QtZeroConf/avahi-core/browse.c new file mode 100644 index 000000000..eabd7eaaa --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/browse.c @@ -0,0 +1,613 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include "browse.h" +#include "log.h" +#include "querier.h" +#include "domain-util.h" +#include "rr-util.h" + +#define AVAHI_LOOKUPS_PER_BROWSER_MAX 15 + +struct AvahiSRBLookup { + AvahiSRecordBrowser *record_browser; + + unsigned ref; + + AvahiIfIndex interface; + AvahiProtocol protocol; + AvahiLookupFlags flags; + + AvahiKey *key; + + AvahiWideAreaLookup *wide_area; + AvahiMulticastLookup *multicast; + + AvahiRList *cname_lookups; + + AVAHI_LLIST_FIELDS(AvahiSRBLookup, lookups); +}; + +static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r); +static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r); + +static void transport_flags_from_domain(AvahiServer *s, AvahiLookupFlags *flags, const char *domain) { + assert(flags); + assert(domain); + + assert(!((*flags & AVAHI_LOOKUP_USE_MULTICAST) && (*flags & AVAHI_LOOKUP_USE_WIDE_AREA))); + + if (*flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA)) + return; + + if (!s->wide_area_lookup_engine || + !avahi_wide_area_has_servers(s->wide_area_lookup_engine) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6)) + *flags |= AVAHI_LOOKUP_USE_MULTICAST; + else + *flags |= AVAHI_LOOKUP_USE_WIDE_AREA; +} + +static AvahiSRBLookup* lookup_new( + AvahiSRecordBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiLookupFlags flags, + AvahiKey *key) { + + AvahiSRBLookup *l; + + assert(b); + assert(AVAHI_IF_VALID(interface)); + assert(AVAHI_PROTO_VALID(protocol)); + + if (b->n_lookups >= AVAHI_LOOKUPS_PER_BROWSER_MAX) + /* We don't like cyclic CNAMEs */ + return NULL; + + if (!(l = avahi_new(AvahiSRBLookup, 1))) + return NULL; + + l->ref = 1; + l->record_browser = b; + l->interface = interface; + l->protocol = protocol; + l->key = avahi_key_ref(key); + l->wide_area = NULL; + l->multicast = NULL; + l->cname_lookups = NULL; + l->flags = flags; + + transport_flags_from_domain(b->server, &l->flags, key->name); + + AVAHI_LLIST_PREPEND(AvahiSRBLookup, lookups, b->lookups, l); + + b->n_lookups ++; + + return l; +} + +static void lookup_unref(AvahiSRBLookup *l) { + assert(l); + assert(l->ref >= 1); + + if (--l->ref >= 1) + return; + + AVAHI_LLIST_REMOVE(AvahiSRBLookup, lookups, l->record_browser->lookups, l); + l->record_browser->n_lookups --; + + if (l->wide_area) { + avahi_wide_area_lookup_free(l->wide_area); + l->wide_area = NULL; + } + + if (l->multicast) { + avahi_multicast_lookup_free(l->multicast); + l->multicast = NULL; + } + + while (l->cname_lookups) { + lookup_unref(l->cname_lookups->data); + l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, l->cname_lookups); + } + + avahi_key_unref(l->key); + avahi_free(l); +} + +static AvahiSRBLookup* lookup_ref(AvahiSRBLookup *l) { + assert(l); + assert(l->ref >= 1); + + l->ref++; + return l; +} + +static AvahiSRBLookup *lookup_find( + AvahiSRecordBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiLookupFlags flags, + AvahiKey *key) { + + AvahiSRBLookup *l; + + assert(b); + + for (l = b->lookups; l; l = l->lookups_next) { + + if ((l->interface == AVAHI_IF_UNSPEC || l->interface == interface) && + (l->interface == AVAHI_PROTO_UNSPEC || l->protocol == protocol) && + l->flags == flags && + avahi_key_equal(l->key, key)) + + return l; + } + + return NULL; +} + +static void browser_cancel(AvahiSRecordBrowser *b) { + assert(b); + + if (b->root_lookup) { + lookup_unref(b->root_lookup); + b->root_lookup = NULL; + } + + if (b->defer_time_event) { + avahi_time_event_free(b->defer_time_event); + b->defer_time_event = NULL; + } +} + +static void lookup_wide_area_callback( + AvahiWideAreaLookupEngine *e, + AvahiBrowserEvent event, + AvahiLookupResultFlags flags, + AvahiRecord *r, + void *userdata) { + + AvahiSRBLookup *l = userdata; + AvahiSRecordBrowser *b; + + assert(e); + assert(l); + assert(l->ref >= 1); + + b = l->record_browser; + + if (b->dead) + return; + + lookup_ref(l); + + switch (event) { + case AVAHI_BROWSER_NEW: + assert(r); + + if (r->key->clazz == AVAHI_DNS_CLASS_IN && + r->key->type == AVAHI_DNS_TYPE_CNAME) + /* It's a CNAME record, so let's follow it. We only follow it on wide area DNS! */ + lookup_handle_cname(l, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_USE_WIDE_AREA, r); + else { + /* It's a normal record, so let's call the user callback */ + assert(avahi_key_equal(r->key, l->key)); + + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, r, flags, b->userdata); + } + break; + + case AVAHI_BROWSER_REMOVE: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + /* Not defined for wide area DNS */ + abort(); + + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_FAILURE: + + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata); + break; + } + + lookup_unref(l); + +} + +static void lookup_multicast_callback( + AvahiMulticastLookupEngine *e, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiLookupResultFlags flags, + AvahiRecord *r, + void *userdata) { + + AvahiSRBLookup *l = userdata; + AvahiSRecordBrowser *b; + + assert(e); + assert(l); + + b = l->record_browser; + + if (b->dead) + return; + + lookup_ref(l); + + switch (event) { + case AVAHI_BROWSER_NEW: + assert(r); + + if (r->key->clazz == AVAHI_DNS_CLASS_IN && + r->key->type == AVAHI_DNS_TYPE_CNAME) + /* It's a CNAME record, so let's follow it. We allow browsing on both multicast and wide area. */ + lookup_handle_cname(l, interface, protocol, b->flags, r); + else { + /* It's a normal record, so let's call the user callback */ + + if (avahi_server_is_record_local(b->server, interface, protocol, r)) + flags |= AVAHI_LOOKUP_RESULT_LOCAL; + + b->callback(b, interface, protocol, event, r, flags, b->userdata); + } + break; + + case AVAHI_BROWSER_REMOVE: + assert(r); + + if (r->key->clazz == AVAHI_DNS_CLASS_IN && + r->key->type == AVAHI_DNS_TYPE_CNAME) + /* It's a CNAME record, so let's drop that query! */ + lookup_drop_cname(l, interface, protocol, 0, r); + else { + /* It's a normal record, so let's call the user callback */ + assert(avahi_key_equal(b->key, l->key)); + + b->callback(b, interface, protocol, event, r, flags, b->userdata); + } + break; + + case AVAHI_BROWSER_ALL_FOR_NOW: + + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata); + break; + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_FAILURE: + /* Not defined for multicast DNS */ + abort(); + + } + + lookup_unref(l); +} + +static int lookup_start(AvahiSRBLookup *l) { + assert(l); + + assert(!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) != !(l->flags & AVAHI_LOOKUP_USE_MULTICAST)); + assert(!l->wide_area && !l->multicast); + + if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) { + + if (!(l->wide_area = avahi_wide_area_lookup_new(l->record_browser->server->wide_area_lookup_engine, l->key, lookup_wide_area_callback, l))) + return -1; + + } else { + assert(l->flags & AVAHI_LOOKUP_USE_MULTICAST); + + if (!(l->multicast = avahi_multicast_lookup_new(l->record_browser->server->multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l))) + return -1; + } + + return 0; +} + +static int lookup_scan_cache(AvahiSRBLookup *l) { + int n = 0; + + assert(l); + + assert(!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) != !(l->flags & AVAHI_LOOKUP_USE_MULTICAST)); + + + if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) { + n = (int) avahi_wide_area_scan_cache(l->record_browser->server->wide_area_lookup_engine, l->key, lookup_wide_area_callback, l); + + } else { + assert(l->flags & AVAHI_LOOKUP_USE_MULTICAST); + n = (int) avahi_multicast_lookup_engine_scan_cache(l->record_browser->server->multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l); + } + + return n; +} + +static AvahiSRBLookup* lookup_add(AvahiSRecordBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiKey *key) { + AvahiSRBLookup *l; + + assert(b); + assert(!b->dead); + + if ((l = lookup_find(b, interface, protocol, flags, key))) + return lookup_ref(l); + + if (!(l = lookup_new(b, interface, protocol, flags, key))) + return NULL; + + return l; +} + +static int lookup_go(AvahiSRBLookup *l) { + int n = 0; + assert(l); + + if (l->record_browser->dead) + return 0; + + lookup_ref(l); + + /* Browse the cache for the root request */ + n = lookup_scan_cache(l); + + /* Start the lookup */ + if (!l->record_browser->dead && l->ref > 1) { + + if ((l->flags & AVAHI_LOOKUP_USE_MULTICAST) || n == 0) + /* We do no start a query if the cache contained entries and we're on wide area */ + + if (lookup_start(l) < 0) + n = -1; + } + + lookup_unref(l); + + return n; +} + +static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) { + AvahiKey *k; + AvahiSRBLookup *n; + + assert(l); + assert(r); + + assert(r->key->clazz == AVAHI_DNS_CLASS_IN); + assert(r->key->type == AVAHI_DNS_TYPE_CNAME); + + k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type); + n = lookup_add(l->record_browser, interface, protocol, flags, k); + avahi_key_unref(k); + + if (!n) { + avahi_log_debug(__FILE__": Failed to create SRBLookup."); + return; + } + + l->cname_lookups = avahi_rlist_prepend(l->cname_lookups, lookup_ref(n)); + + lookup_go(n); + lookup_unref(n); +} + +static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) { + AvahiKey *k; + AvahiSRBLookup *n = NULL; + AvahiRList *rl; + + assert(r->key->clazz == AVAHI_DNS_CLASS_IN); + assert(r->key->type == AVAHI_DNS_TYPE_CNAME); + + k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type); + + for (rl = l->cname_lookups; rl; rl = rl->rlist_next) { + n = rl->data; + + assert(n); + + if ((n->interface == AVAHI_IF_UNSPEC || n->interface == interface) && + (n->interface == AVAHI_PROTO_UNSPEC || n->protocol == protocol) && + n->flags == flags && + avahi_key_equal(n->key, k)) + break; + } + + avahi_key_unref(k); + + if (rl) { + l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, rl); + lookup_unref(n); + } +} + +static void defer_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) { + AvahiSRecordBrowser *b = userdata; + int n; + + assert(b); + assert(!b->dead); + + /* Remove the defer timeout */ + if (b->defer_time_event) { + avahi_time_event_free(b->defer_time_event); + b->defer_time_event = NULL; + } + + /* Create initial query */ + assert(!b->root_lookup); + b->root_lookup = lookup_add(b, b->interface, b->protocol, b->flags, b->key); + assert(b->root_lookup); + + n = lookup_go(b->root_lookup); + + if (b->dead) + return; + + if (n < 0) { + /* sending of the initial query failed */ + + avahi_server_set_errno(b->server, AVAHI_ERR_FAILURE); + + b->callback( + b, b->interface, b->protocol, AVAHI_BROWSER_FAILURE, NULL, + b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : AVAHI_LOOKUP_RESULT_MULTICAST, + b->userdata); + + browser_cancel(b); + return; + } + + /* Tell the client that we're done with the cache */ + b->callback( + b, b->interface, b->protocol, AVAHI_BROWSER_CACHE_EXHAUSTED, NULL, + b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : AVAHI_LOOKUP_RESULT_MULTICAST, + b->userdata); + + if (!b->dead && b->root_lookup && b->root_lookup->flags & AVAHI_LOOKUP_USE_WIDE_AREA && n > 0) { + + /* If we do wide area lookups and the the cache contained + * entries, we assume that it is complete, and tell the user + * so by firing ALL_FOR_NOW. */ + + b->callback(b, b->interface, b->protocol, AVAHI_BROWSER_ALL_FOR_NOW, NULL, AVAHI_LOOKUP_RESULT_WIDE_AREA, b->userdata); + } +} + +void avahi_s_record_browser_restart(AvahiSRecordBrowser *b) { + assert(b); + assert(!b->dead); + + browser_cancel(b); + + /* Request a new iteration of the cache scanning */ + if (!b->defer_time_event) { + b->defer_time_event = avahi_time_event_new(b->server->time_event_queue, NULL, defer_callback, b); + assert(b->defer_time_event); + } +} + +AvahiSRecordBrowser *avahi_s_record_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiKey *key, + AvahiLookupFlags flags, + AvahiSRecordBrowserCallback callback, + void* userdata) { + + AvahiSRecordBrowser *b; + + assert(server); + assert(key); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !avahi_key_is_pattern(key), AVAHI_ERR_IS_PATTERN); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_key_is_valid(key), AVAHI_ERR_INVALID_KEY); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !(flags & AVAHI_LOOKUP_USE_WIDE_AREA) || !(flags & AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + + if (!(b = avahi_new(AvahiSRecordBrowser, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + b->dead = 0; + b->server = server; + b->interface = interface; + b->protocol = protocol; + b->key = avahi_key_ref(key); + b->flags = flags; + b->callback = callback; + b->userdata = userdata; + b->n_lookups = 0; + AVAHI_LLIST_HEAD_INIT(AvahiSRBLookup, b->lookups); + b->root_lookup = NULL; + + AVAHI_LLIST_PREPEND(AvahiSRecordBrowser, browser, server->record_browsers, b); + + /* The currently cached entries are scanned a bit later, and than we will start querying, too */ + b->defer_time_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b); + assert(b->defer_time_event); + + return b; +} + +void avahi_s_record_browser_free(AvahiSRecordBrowser *b) { + assert(b); + assert(!b->dead); + + b->dead = 1; + b->server->need_browser_cleanup = 1; + + browser_cancel(b); +} + +void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b) { + assert(b); + + browser_cancel(b); + + AVAHI_LLIST_REMOVE(AvahiSRecordBrowser, browser, b->server->record_browsers, b); + + avahi_key_unref(b->key); + + avahi_free(b); +} + +void avahi_browser_cleanup(AvahiServer *server) { + AvahiSRecordBrowser *b; + AvahiSRecordBrowser *n; + + assert(server); + + while (server->need_browser_cleanup) { + server->need_browser_cleanup = 0; + + for (b = server->record_browsers; b; b = n) { + n = b->browser_next; + + if (b->dead) + avahi_s_record_browser_destroy(b); + } + } + + if (server->wide_area_lookup_engine) + avahi_wide_area_cleanup(server->wide_area_lookup_engine); + avahi_multicast_lookup_engine_cleanup(server->multicast_lookup_engine); +} + diff --git a/3rdparty/QtZeroConf/avahi-core/browse.h b/3rdparty/QtZeroConf/avahi-core/browse.h new file mode 100644 index 000000000..a0dc20762 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/browse.h @@ -0,0 +1,60 @@ +#ifndef foobrowsehfoo +#define foobrowsehfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "core.h" +#include "timeeventq.h" +#include "internal.h" +#include "dns.h" +#include "lookup.h" + +typedef struct AvahiSRBLookup AvahiSRBLookup; + +struct AvahiSRecordBrowser { + AVAHI_LLIST_FIELDS(AvahiSRecordBrowser, browser); + int dead; + AvahiServer *server; + + AvahiKey *key; + AvahiIfIndex interface; + AvahiProtocol protocol; + AvahiLookupFlags flags; + + AvahiTimeEvent *defer_time_event; + + AvahiSRecordBrowserCallback callback; + void* userdata; + + /* Lookup data */ + AVAHI_LLIST_HEAD(AvahiSRBLookup, lookups); + unsigned n_lookups; + + AvahiSRBLookup *root_lookup; +}; + +void avahi_browser_cleanup(AvahiServer *server); + +void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b); +void avahi_s_record_browser_restart(AvahiSRecordBrowser *b); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/cache.c b/3rdparty/QtZeroConf/avahi-core/cache.c new file mode 100644 index 000000000..454aac53a --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/cache.c @@ -0,0 +1,513 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include + +#include "cache.h" +#include "log.h" +#include "rr-util.h" + +static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) { + AvahiCacheEntry *t; + + assert(c); + assert(e); + +/* avahi_log_debug("removing from cache: %p %p", c, e); */ + + /* Remove from hash table */ + t = avahi_hashmap_lookup(c->hashmap, e->record->key); + AVAHI_LLIST_REMOVE(AvahiCacheEntry, by_key, t, e); + if (t) + avahi_hashmap_replace(c->hashmap, t->record->key, t); + else + avahi_hashmap_remove(c->hashmap, e->record->key); + + /* Remove from linked list */ + AVAHI_LLIST_REMOVE(AvahiCacheEntry, entry, c->entries, e); + + if (e->time_event) + avahi_time_event_free(e->time_event); + + avahi_multicast_lookup_engine_notify(c->server->multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_REMOVE); + + avahi_record_unref(e->record); + + avahi_free(e); + + assert(c->n_entries >= 1); + --c->n_entries; +} + +AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *iface) { + AvahiCache *c; + assert(server); + + if (!(c = avahi_new(AvahiCache, 1))) { + avahi_log_error(__FILE__": Out of memory."); + return NULL; /* OOM */ + } + + c->server = server; + c->interface = iface; + + if (!(c->hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL))) { + avahi_log_error(__FILE__": Out of memory."); + avahi_free(c); + return NULL; /* OOM */ + } + + AVAHI_LLIST_HEAD_INIT(AvahiCacheEntry, c->entries); + c->n_entries = 0; + + c->last_rand_timestamp = 0; + + return c; +} + +void avahi_cache_free(AvahiCache *c) { + assert(c); + + while (c->entries) + remove_entry(c, c->entries); + assert(c->n_entries == 0); + + avahi_hashmap_free(c->hashmap); + + avahi_free(c); +} + +static AvahiCacheEntry *lookup_key(AvahiCache *c, AvahiKey *k) { + assert(c); + assert(k); + + assert(!avahi_key_is_pattern(k)); + + return avahi_hashmap_lookup(c->hashmap, k); +} + +void* avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, void* userdata) { + void* ret; + + assert(c); + assert(pattern); + assert(cb); + + if (avahi_key_is_pattern(pattern)) { + AvahiCacheEntry *e, *n; + + for (e = c->entries; e; e = n) { + n = e->entry_next; + + if (avahi_key_pattern_match(pattern, e->record->key)) + if ((ret = cb(c, pattern, e, userdata))) + return ret; + } + + } else { + AvahiCacheEntry *e, *n; + + for (e = lookup_key(c, pattern); e; e = n) { + n = e->by_key_next; + + if ((ret = cb(c, pattern, e, userdata))) + return ret; + } + } + + return NULL; +} + +static void* lookup_record_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) { + assert(c); + assert(pattern); + assert(e); + + if (avahi_record_equal_no_ttl(e->record, userdata)) + return e; + + return NULL; +} + +static AvahiCacheEntry *lookup_record(AvahiCache *c, AvahiRecord *r) { + assert(c); + assert(r); + + return avahi_cache_walk(c, r->key, lookup_record_callback, r); +} + +static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, unsigned percent); + +static void elapse_func(AvahiTimeEvent *t, void *userdata) { + AvahiCacheEntry *e = userdata; +/* char *txt; */ + unsigned percent = 0; + + assert(t); + assert(e); + +/* txt = avahi_record_to_string(e->record); */ + + switch (e->state) { + + case AVAHI_CACHE_EXPIRY_FINAL: + case AVAHI_CACHE_POOF_FINAL: + case AVAHI_CACHE_GOODBYE_FINAL: + case AVAHI_CACHE_REPLACE_FINAL: + + remove_entry(e->cache, e); + + e = NULL; +/* avahi_log_debug("Removing entry from cache due to expiration (%s)", txt); */ + break; + + case AVAHI_CACHE_VALID: + case AVAHI_CACHE_POOF: + e->state = AVAHI_CACHE_EXPIRY1; + percent = 85; + break; + + case AVAHI_CACHE_EXPIRY1: + e->state = AVAHI_CACHE_EXPIRY2; + percent = 90; + break; + case AVAHI_CACHE_EXPIRY2: + e->state = AVAHI_CACHE_EXPIRY3; + percent = 95; + break; + + case AVAHI_CACHE_EXPIRY3: + e->state = AVAHI_CACHE_EXPIRY_FINAL; + percent = 100; + break; + } + + if (e) { + + assert(percent > 0); + + /* Request a cache update if we are subscribed to this entry */ + if (avahi_querier_shall_refresh_cache(e->cache->interface, e->record->key)) + avahi_interface_post_query(e->cache->interface, e->record->key, 0, NULL); + + /* Check again later */ + next_expiry(e->cache, e, percent); + + } + +/* avahi_free(txt); */ +} + +static void update_time_event(AvahiCache *c, AvahiCacheEntry *e) { + assert(c); + assert(e); + + if (e->time_event) + avahi_time_event_update(e->time_event, &e->expiry); + else + e->time_event = avahi_time_event_new(c->server->time_event_queue, &e->expiry, elapse_func, e); +} + +static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, unsigned percent) { + AvahiUsec usec, left, right; + time_t now; + + assert(c); + assert(e); + assert(percent > 0 && percent <= 100); + + usec = (AvahiUsec) e->record->ttl * 10000; + + left = usec * percent; + right = usec * (percent+2); /* 2% jitter */ + + now = time(NULL); + + if (now >= c->last_rand_timestamp + 10) { + c->last_rand = rand(); + c->last_rand_timestamp = now; + } + + usec = left + (AvahiUsec) ((double) (right-left) * c->last_rand / (RAND_MAX+1.0)); + + e->expiry = e->timestamp; + avahi_timeval_add(&e->expiry, usec); + +/* g_message("wake up in +%lu seconds", e->expiry.tv_sec - e->timestamp.tv_sec); */ + + update_time_event(c, e); +} + +static void expire_in_one_second(AvahiCache *c, AvahiCacheEntry *e, AvahiCacheEntryState state) { + assert(c); + assert(e); + + e->state = state; + gettimeofday(&e->expiry, NULL); + avahi_timeval_add(&e->expiry, 1000000); /* 1s */ + update_time_event(c, e); +} + +void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const AvahiAddress *a) { +/* char *txt; */ + + assert(c); + assert(r && r->ref >= 1); + +/* txt = avahi_record_to_string(r); */ + + if (r->ttl == 0) { + /* This is a goodbye request */ + + AvahiCacheEntry *e; + + if ((e = lookup_record(c, r))) + expire_in_one_second(c, e, AVAHI_CACHE_GOODBYE_FINAL); + + } else { + AvahiCacheEntry *e = NULL, *first; + struct timeval now; + + gettimeofday(&now, NULL); + + /* This is an update request */ + + if ((first = lookup_key(c, r->key))) { + + if (cache_flush) { + + /* For unique entries drop all entries older than one second */ + for (e = first; e; e = e->by_key_next) { + AvahiUsec t; + + t = avahi_timeval_diff(&now, &e->timestamp); + + if (t > 1000000) + expire_in_one_second(c, e, AVAHI_CACHE_REPLACE_FINAL); + } + } + + /* Look for exactly the same entry */ + for (e = first; e; e = e->by_key_next) + if (avahi_record_equal_no_ttl(e->record, r)) + break; + } + + if (e) { + +/* avahi_log_debug("found matching cache entry"); */ + + /* We need to update the hash table key if we replace the + * record */ + if (e->by_key_prev == NULL) + avahi_hashmap_replace(c->hashmap, r->key, e); + + /* Update the record */ + avahi_record_unref(e->record); + e->record = avahi_record_ref(r); + +/* avahi_log_debug("cache: updating %s", txt); */ + + } else { + /* No entry found, therefore we create a new one */ + +/* avahi_log_debug("cache: couldn't find matching cache entry for %s", txt); */ + + if (c->n_entries >= c->server->config.n_cache_entries_max) + return; + + if (!(e = avahi_new(AvahiCacheEntry, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return; + } + + e->cache = c; + e->time_event = NULL; + e->record = avahi_record_ref(r); + + /* Append to hash table */ + AVAHI_LLIST_PREPEND(AvahiCacheEntry, by_key, first, e); + avahi_hashmap_replace(c->hashmap, e->record->key, first); + + /* Append to linked list */ + AVAHI_LLIST_PREPEND(AvahiCacheEntry, entry, c->entries, e); + + c->n_entries++; + + /* Notify subscribers */ + avahi_multicast_lookup_engine_notify(c->server->multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_NEW); + } + + e->origin = *a; + e->timestamp = now; + next_expiry(c, e, 80); + e->state = AVAHI_CACHE_VALID; + e->cache_flush = cache_flush; + } + +/* avahi_free(txt); */ +} + +struct dump_data { + AvahiDumpCallback callback; + void* userdata; +}; + +static void dump_callback(void* key, void* data, void* userdata) { + AvahiCacheEntry *e = data; + AvahiKey *k = key; + struct dump_data *dump_data = userdata; + + assert(k); + assert(e); + assert(data); + + for (; e; e = e->by_key_next) { + char *t; + + if (!(t = avahi_record_to_string(e->record))) + continue; /* OOM */ + + dump_data->callback(t, dump_data->userdata); + avahi_free(t); + } +} + +int avahi_cache_dump(AvahiCache *c, AvahiDumpCallback callback, void* userdata) { + struct dump_data data; + + assert(c); + assert(callback); + + callback(";;; CACHE DUMP FOLLOWS ;;;", userdata); + + data.callback = callback; + data.userdata = userdata; + + avahi_hashmap_foreach(c->hashmap, dump_callback, &data); + + return 0; +} + +int avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e) { + struct timeval now; + unsigned age; + + assert(c); + assert(e); + + gettimeofday(&now, NULL); + + age = (unsigned) (avahi_timeval_diff(&now, &e->timestamp)/1000000); + +/* avahi_log_debug("age: %lli, ttl/2: %u", age, e->record->ttl); */ + + return age >= e->record->ttl/2; +} + +void avahi_cache_flush(AvahiCache *c) { + assert(c); + + while (c->entries) + remove_entry(c, c->entries); +} + +/*** Passive observation of failure ***/ + +static void* start_poof_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) { + AvahiAddress *a = userdata; + struct timeval now; + + assert(c); + assert(pattern); + assert(e); + assert(a); + + gettimeofday(&now, NULL); + + switch (e->state) { + case AVAHI_CACHE_VALID: + + /* The entry was perfectly valid till, now, so let's enter + * POOF mode */ + + e->state = AVAHI_CACHE_POOF; + e->poof_address = *a; + e->poof_timestamp = now; + e->poof_num = 0; + + break; + + case AVAHI_CACHE_POOF: + if (avahi_timeval_diff(&now, &e->poof_timestamp) < 1000000) + break; + + e->poof_timestamp = now; + e->poof_address = *a; + e->poof_num ++; + + /* This is the 4th time we got no response, so let's + * fucking remove this entry. */ + if (e->poof_num > 3) + expire_in_one_second(c, e, AVAHI_CACHE_POOF_FINAL); + break; + + default: + ; + } + + return NULL; +} + +void avahi_cache_start_poof(AvahiCache *c, AvahiKey *key, const AvahiAddress *a) { + assert(c); + assert(key); + + avahi_cache_walk(c, key, start_poof_callback, (void*) a); +} + +void avahi_cache_stop_poof(AvahiCache *c, AvahiRecord *record, const AvahiAddress *a) { + AvahiCacheEntry *e; + + assert(c); + assert(record); + assert(a); + + if (!(e = lookup_record(c, record))) + return; + + /* This function is called for each response suppression + record. If the matching cache entry is in POOF state and the + query address is the same, we put it back into valid mode */ + + if (e->state == AVAHI_CACHE_POOF || e->state == AVAHI_CACHE_POOF_FINAL) + if (avahi_address_cmp(a, &e->poof_address) == 0) { + e->state = AVAHI_CACHE_VALID; + next_expiry(c, e, 80); + } +} diff --git a/3rdparty/QtZeroConf/avahi-core/cache.h b/3rdparty/QtZeroConf/avahi-core/cache.h new file mode 100644 index 000000000..49ba9b9c4 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/cache.h @@ -0,0 +1,101 @@ +#ifndef foocachehfoo +#define foocachehfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct AvahiCache AvahiCache; + +#include +#include "prioq.h" +#include "internal.h" +#include "timeeventq.h" +#include "hashmap.h" + +typedef enum { + AVAHI_CACHE_VALID, + AVAHI_CACHE_EXPIRY1, + AVAHI_CACHE_EXPIRY2, + AVAHI_CACHE_EXPIRY3, + AVAHI_CACHE_EXPIRY_FINAL, + AVAHI_CACHE_POOF, /* Passive observation of failure */ + AVAHI_CACHE_POOF_FINAL, + AVAHI_CACHE_GOODBYE_FINAL, + AVAHI_CACHE_REPLACE_FINAL +} AvahiCacheEntryState; + +typedef struct AvahiCacheEntry AvahiCacheEntry; + +struct AvahiCacheEntry { + AvahiCache *cache; + AvahiRecord *record; + struct timeval timestamp; + struct timeval poof_timestamp; + struct timeval expiry; + int cache_flush; + int poof_num; + + AvahiAddress origin; + + AvahiCacheEntryState state; + AvahiTimeEvent *time_event; + + AvahiAddress poof_address; + + AVAHI_LLIST_FIELDS(AvahiCacheEntry, by_key); + AVAHI_LLIST_FIELDS(AvahiCacheEntry, entry); +}; + +struct AvahiCache { + AvahiServer *server; + + AvahiInterface *interface; + + AvahiHashmap *hashmap; + + AVAHI_LLIST_HEAD(AvahiCacheEntry, entries); + + unsigned n_entries; + + int last_rand; + time_t last_rand_timestamp; +}; + +AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *interface); +void avahi_cache_free(AvahiCache *c); + +void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const AvahiAddress *a); + +int avahi_cache_dump(AvahiCache *c, AvahiDumpCallback callback, void* userdata); + +typedef void* AvahiCacheWalkCallback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata); +void* avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, void* userdata); + +int avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e); + +/** Start the "Passive observation of Failure" algorithm for all + * records of the specified key. The specified address is */ +void avahi_cache_start_poof(AvahiCache *c, AvahiKey *key, const AvahiAddress *a); + +/* Stop a previously started POOF algorithm for a record. (Used for response suppresions records */ +void avahi_cache_stop_poof(AvahiCache *c, AvahiRecord *record, const AvahiAddress *a); + +void avahi_cache_flush(AvahiCache *c); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/core.h b/3rdparty/QtZeroConf/avahi-core/core.h new file mode 100644 index 000000000..3fba55e6b --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/core.h @@ -0,0 +1,165 @@ +#ifndef foocorehfoo +#define foocorehfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file core.h The Avahi Multicast DNS and DNS Service Discovery implementation. */ + +/** An mDNS responder object */ +typedef struct AvahiServer AvahiServer; + +#include +#include +#include +#include +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Maximum number of defined DNS servers for wide area DNS */ +#define AVAHI_WIDE_AREA_SERVERS_MAX 4 + +/** Prototype for callback functions which are called whenever the state of an AvahiServer object changes */ +typedef void (*AvahiServerCallback) (AvahiServer *s, AvahiServerState state, void* userdata); + +/** Stores configuration options for a server instance */ +typedef struct AvahiServerConfig { + char *host_name; /**< Default host name. If left empty defaults to the result of gethostname(2) of the libc */ + char *domain_name; /**< Default domain name. If left empty defaults to .local */ + int use_ipv4; /**< Enable IPv4 support */ + int use_ipv6; /**< Enable IPv6 support */ + AvahiStringList *allow_interfaces;/**< Allow specific interface to be used for Avahi */ + AvahiStringList *deny_interfaces; /**< Deny specific interfaces to be used for Avahi */ + int publish_hinfo; /**< Register a HINFO record for the host containing the local OS and CPU type */ + int publish_addresses; /**< Register A, AAAA and PTR records for all local IP addresses */ + int publish_workstation; /**< Register a _workstation._tcp service */ + int publish_domain; /**< Announce the local domain for browsing */ + int check_response_ttl; /**< If enabled the server ignores all incoming responses with IP TTL != 255. Newer versions of the RFC do no longer contain this check, so it is disabled by default. */ + int use_iff_running; /**< Require IFF_RUNNING on local network interfaces. This is the official way to check for link beat. Unfortunately this doesn't work with all drivers. So bettere leave this off. */ + int enable_reflector; /**< Reflect incoming mDNS traffic to all local networks. This allows mDNS based network browsing beyond ethernet borders */ + int reflect_ipv; /**< if enable_reflector is 1, enable/disable reflecting between IPv4 and IPv6 */ + int add_service_cookie; /**< Add magic service cookie to all locally generated records implicitly */ + int enable_wide_area; /**< Enable wide area support */ + AvahiAddress wide_area_servers[AVAHI_WIDE_AREA_SERVERS_MAX]; /** Unicast DNS server to use for wide area lookup */ + unsigned n_wide_area_servers; /**< Number of servers in wide_area_servers[] */ + int disallow_other_stacks; /**< Make sure that only one mDNS responder is run at the same time on the local machine. If this is enable Avahi will not set SO_REUSADDR on its sockets, effectively preventing other stacks from running on the local machine */ + AvahiStringList *browse_domains; /**< Additional browsing domains */ + int disable_publishing; /**< Disable publishing of any record */ + int allow_point_to_point; /**< Enable publishing on POINTOPOINT interfaces */ + int publish_a_on_ipv6; /**< Publish an IPv4 A RR on IPv6 sockets */ + int publish_aaaa_on_ipv4; /**< Publish an IPv6 AAAA RR on IPv4 sockets */ + unsigned n_cache_entries_max; /**< Maximum number of cache entries per interface */ + AvahiUsec ratelimit_interval; /**< If non-zero, rate-limiting interval parameter. */ + unsigned ratelimit_burst; /**< If ratelimit_interval is non-zero, rate-limiting burst parameter. */ +} AvahiServerConfig; + +/** Allocate a new mDNS responder object. */ +AvahiServer *avahi_server_new( + const AvahiPoll *api, /**< The main loop adapter */ + const AvahiServerConfig *sc, /**< If non-NULL a pointer to a configuration structure for the server. The server makes an internal deep copy of this structure, so you may free it using avahi_server_config_done() immediately after calling this function. */ + AvahiServerCallback callback, /**< A callback which is called whenever the state of the server changes */ + void* userdata, /**< An opaque pointer which is passed to the callback function */ + int *error); + +/** Free an mDNS responder object */ +void avahi_server_free(AvahiServer* s); + +/** Fill in default values for a server configuration structure. If you + * make use of an AvahiServerConfig structure be sure to initialize + * it with this function for the sake of upwards library + * compatibility. This call may allocate strings on the heap. To + * release this memory make sure to call + * avahi_server_config_done(). If you want to replace any strings in + * the structure be sure to free the strings filled in by this + * function with avahi_free() first and allocate the replacements with + * g_malloc() (or g_strdup()).*/ +AvahiServerConfig* avahi_server_config_init( + AvahiServerConfig *c /**< A structure which shall be filled in */ ); + +/** Make a deep copy of the configuration structure *c to *ret. */ +AvahiServerConfig* avahi_server_config_copy( + AvahiServerConfig *ret /**< destination */, + const AvahiServerConfig *c /**< source */); + +/** Free the data in a server configuration structure. */ +void avahi_server_config_free(AvahiServerConfig *c); + +/** Return the currently chosen domain name of the server object. The + * return value points to an internally allocated string. Be sure to + * make a copy of the string before calling any other library + * functions. */ +const char* avahi_server_get_domain_name(AvahiServer *s); + +/** Return the currently chosen host name. The return value points to a internally allocated string. */ +const char* avahi_server_get_host_name(AvahiServer *s); + +/** Return the currently chosen host name as a FQDN ("fully qualified + * domain name", i.e. the concatenation of the host and domain + * name). The return value points to a internally allocated string. */ +const char* avahi_server_get_host_name_fqdn(AvahiServer *s); + +/** Change the host name of a running mDNS responder. This will drop +all automicatilly generated RRs and readd them with the new +name. Since the responder has to probe for the new RRs this function +takes some time to take effect altough it returns immediately. This +function is intended to be called when a host name conflict is +reported using AvahiServerCallback. The caller should readd all user +defined RRs too since they otherwise continue to point to the outdated +host name..*/ +int avahi_server_set_host_name(AvahiServer *s, const char *host_name); + +/** Change the domain name of a running mDNS responder. The same rules + * as with avahi_server_set_host_name() apply. */ +int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name); + +/** Return the opaque user data pointer attached to a server object */ +void* avahi_server_get_data(AvahiServer *s); + +/** Change the opaque user data pointer attached to a server object */ +void avahi_server_set_data(AvahiServer *s, void* userdata); + +/** Return the current state of the server object */ +AvahiServerState avahi_server_get_state(AvahiServer *s); + +/** Callback prototype for avahi_server_dump() */ +typedef void (*AvahiDumpCallback)(const char *text, void* userdata); + +/** Dump the current server status by calling "callback" for each line. */ +int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata); + +/** Return the last error code */ +int avahi_server_errno(AvahiServer *s); + +/** Return the local service cookie */ +uint32_t avahi_server_get_local_service_cookie(AvahiServer *s); + +/** Set the wide area DNS servers */ +int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n); + +/** Set the browsing domains */ +int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains); + +/** Return the current configuration of the server \since 0.6.17 */ +const AvahiServerConfig* avahi_server_get_config(AvahiServer *s); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/dns-srv-rr.h b/3rdparty/QtZeroConf/avahi-core/dns-srv-rr.h new file mode 100644 index 000000000..fdd901061 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/dns-srv-rr.h @@ -0,0 +1,87 @@ +#ifndef foodnssrvhfoo +#define foodnssrvhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file avahi-core/dns-srv-rr.h Functions for announcing and browsing for unicast DNS servers via mDNS */ + +/** A domain service browser object. Use this to browse for + * conventional unicast DNS servers which may be used to resolve + * conventional domain names */ +typedef struct AvahiSDNSServerBrowser AvahiSDNSServerBrowser; + +#include +#include +#include +#include + +AVAHI_C_DECL_BEGIN + +/** The type of DNS server */ +typedef enum { + AVAHI_DNS_SERVER_RESOLVE, /**< Unicast DNS servers for normal resolves (_domain._udp)*/ + AVAHI_DNS_SERVER_UPDATE, /**< Unicast DNS servers for updates (_dns-update._udp)*/ + AVAHI_DNS_SERVER_MAX +} AvahiDNSServerType; + +/** Publish the specified unicast DNS server address via mDNS. You may + * browse for records create this way wit + * avahi_s_dns_server_browser_new(). */ +int avahi_server_add_dns_server_address( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *domain, + AvahiDNSServerType type, + const AvahiAddress *address, + uint16_t port /** should be 53 */); + +/** Callback prototype for AvahiSDNSServerBrowser events */ +typedef void (*AvahiSDNSServerBrowserCallback)( + AvahiSDNSServerBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *host_name, /**< Host name of the DNS server, probably useless */ + const AvahiAddress *a, /**< Address of the DNS server */ + uint16_t port, /**< Port number of the DNS servers, probably 53 */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSDNSServerBrowser object */ +AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDNSServerType type, + AvahiProtocol aprotocol, /**< Address protocol for the DNS server */ + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSDNSServerBrowserCallback callback, + void* userdata); + +/** Free an AvahiSDNSServerBrowser object */ +void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/dns.c b/3rdparty/QtZeroConf/avahi-core/dns.c new file mode 100644 index 000000000..7c38f4249 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/dns.c @@ -0,0 +1,897 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "dns.h" +#include "log.h" + +AvahiDnsPacket* avahi_dns_packet_new(unsigned mtu) { + AvahiDnsPacket *p; + size_t max_size; + + if (mtu <= 0) + max_size = AVAHI_DNS_PACKET_SIZE_MAX; + else if (mtu >= AVAHI_DNS_PACKET_EXTRA_SIZE) + max_size = mtu - AVAHI_DNS_PACKET_EXTRA_SIZE; + else + max_size = 0; + + if (max_size < AVAHI_DNS_PACKET_HEADER_SIZE) + max_size = AVAHI_DNS_PACKET_HEADER_SIZE; + + if (!(p = avahi_malloc(sizeof(AvahiDnsPacket) + max_size))) + return p; + + p->size = p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE; + p->max_size = max_size; + p->res_size = 0; + p->name_table = NULL; + p->data = NULL; + + memset(AVAHI_DNS_PACKET_DATA(p), 0, p->size); + return p; +} + +AvahiDnsPacket* avahi_dns_packet_new_query(unsigned mtu) { + AvahiDnsPacket *p; + + if (!(p = avahi_dns_packet_new(mtu))) + return NULL; + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + return p; +} + +AvahiDnsPacket* avahi_dns_packet_new_response(unsigned mtu, int aa) { + AvahiDnsPacket *p; + + if (!(p = avahi_dns_packet_new(mtu))) + return NULL; + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(1, 0, aa, 0, 0, 0, 0, 0, 0, 0)); + return p; +} + +AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, unsigned mtu, int copy_queries, int aa) { + AvahiDnsPacket *r; + assert(p); + + if (!(r = avahi_dns_packet_new_response(mtu, aa))) + return NULL; + + if (copy_queries) { + unsigned saved_rindex; + uint32_t n; + + saved_rindex = p->rindex; + p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE; + + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n--) { + AvahiKey *k; + int unicast_response; + + if ((k = avahi_dns_packet_consume_key(p, &unicast_response))) { + avahi_dns_packet_append_key(r, k, unicast_response); + avahi_key_unref(k); + } + } + + p->rindex = saved_rindex; + + avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_QDCOUNT, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT)); + } + + avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_ID, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)); + + avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_FLAGS, + (avahi_dns_packet_get_field(r, AVAHI_DNS_FIELD_FLAGS) & ~AVAHI_DNS_FLAG_OPCODE) | + (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_OPCODE)); + + return r; +} + + +void avahi_dns_packet_free(AvahiDnsPacket *p) { + assert(p); + + if (p->name_table) + avahi_hashmap_free(p->name_table); + + avahi_free(p); +} + +void avahi_dns_packet_set_field(AvahiDnsPacket *p, unsigned idx, uint16_t v) { + assert(p); + assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE); + + ((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx] = htons(v); +} + +uint16_t avahi_dns_packet_get_field(AvahiDnsPacket *p, unsigned idx) { + assert(p); + assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE); + + return ntohs(((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx]); +} + +void avahi_dns_packet_inc_field(AvahiDnsPacket *p, unsigned idx) { + assert(p); + assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE); + + avahi_dns_packet_set_field(p, idx, avahi_dns_packet_get_field(p, idx) + 1); +} + + +static void name_table_cleanup(void *key, void *value, void *user_data) { + AvahiDnsPacket *p = user_data; + + if ((uint8_t*) value >= AVAHI_DNS_PACKET_DATA(p) + p->size) + avahi_hashmap_remove(p->name_table, key); +} + +void avahi_dns_packet_cleanup_name_table(AvahiDnsPacket *p) { + if (p->name_table) + avahi_hashmap_foreach(p->name_table, name_table_cleanup, p); +} + +uint8_t* avahi_dns_packet_append_name(AvahiDnsPacket *p, const char *name) { + uint8_t *d, *saved_ptr = NULL; + size_t saved_size; + + assert(p); + assert(name); + + saved_size = p->size; + saved_ptr = avahi_dns_packet_extend(p, 0); + + while (*name) { + uint8_t* prev; + const char *pname; + char label[64], *u; + + /* Check whether we can compress this name. */ + + if (p->name_table && (prev = avahi_hashmap_lookup(p->name_table, name))) { + unsigned idx; + + assert(prev >= AVAHI_DNS_PACKET_DATA(p)); + idx = (unsigned) (prev - AVAHI_DNS_PACKET_DATA(p)); + + assert(idx < p->size); + + if (idx < 0x4000) { + uint8_t *t; + if (!(t = (uint8_t*) avahi_dns_packet_extend(p, sizeof(uint16_t)))) + return NULL; + + t[0] = (uint8_t) ((0xC000 | idx) >> 8); + t[1] = (uint8_t) idx; + return saved_ptr; + } + } + + pname = name; + + if (!(avahi_unescape_label(&name, label, sizeof(label)))) + goto fail; + + if (!(d = avahi_dns_packet_append_string(p, label))) + goto fail; + + if (!p->name_table) + /* This works only for normalized domain names */ + p->name_table = avahi_hashmap_new(avahi_string_hash, avahi_string_equal, avahi_free, NULL); + + if (!(u = avahi_strdup(pname))) + avahi_log_error("avahi_strdup() failed."); + else + avahi_hashmap_insert(p->name_table, u, d); + } + + if (!(d = avahi_dns_packet_extend(p, 1))) + goto fail; + + *d = 0; + + return saved_ptr; + +fail: + p->size = saved_size; + avahi_dns_packet_cleanup_name_table(p); + + return NULL; +} + +uint8_t* avahi_dns_packet_append_uint16(AvahiDnsPacket *p, uint16_t v) { + uint8_t *d; + assert(p); + + if (!(d = avahi_dns_packet_extend(p, sizeof(uint16_t)))) + return NULL; + + d[0] = (uint8_t) (v >> 8); + d[1] = (uint8_t) v; + return d; +} + +uint8_t *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, uint32_t v) { + uint8_t *d; + assert(p); + + if (!(d = avahi_dns_packet_extend(p, sizeof(uint32_t)))) + return NULL; + + d[0] = (uint8_t) (v >> 24); + d[1] = (uint8_t) (v >> 16); + d[2] = (uint8_t) (v >> 8); + d[3] = (uint8_t) v; + + return d; +} + +uint8_t *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, const void *b, size_t l) { + uint8_t* d; + + assert(p); + assert(b); + assert(l); + + if (!(d = avahi_dns_packet_extend(p, l))) + return NULL; + + memcpy(d, b, l); + return d; +} + +uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s) { + uint8_t* d; + size_t k; + + assert(p); + assert(s); + + if ((k = strlen(s)) >= 255) + k = 255; + + if (!(d = avahi_dns_packet_extend(p, k+1))) + return NULL; + + *d = (uint8_t) k; + memcpy(d+1, s, k); + + return d; +} + +uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l) { + uint8_t *d; + + assert(p); + + if (p->size+l > p->max_size) + return NULL; + + d = AVAHI_DNS_PACKET_DATA(p) + p->size; + p->size += l; + + return d; +} + +int avahi_dns_packet_check_valid(AvahiDnsPacket *p) { + uint16_t flags; + assert(p); + + if (p->size < AVAHI_DNS_PACKET_HEADER_SIZE) + return -1; + + flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); + + if (flags & AVAHI_DNS_FLAG_OPCODE) + return -1; + + return 0; +} + +int avahi_dns_packet_check_valid_multicast(AvahiDnsPacket *p) { + uint16_t flags; + assert(p); + + if (avahi_dns_packet_check_valid(p) < 0) + return -1; + + flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); + + if (flags & AVAHI_DNS_FLAG_RCODE) + return -1; + + return 0; +} + +int avahi_dns_packet_is_query(AvahiDnsPacket *p) { + assert(p); + + return !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_QR); +} + +static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_t l) { + int ret = 0; + int compressed = 0; + int first_label = 1; + unsigned label_ptr; + int i; + assert(p && ret_name && l); + + for (i = 0; i < AVAHI_DNS_LABELS_MAX; i++) { + uint8_t n; + + if (idx+1 > p->size) + return -1; + + n = AVAHI_DNS_PACKET_DATA(p)[idx]; + + if (!n) { + idx++; + if (!compressed) + ret++; + + if (l < 1) + return -1; + *ret_name = 0; + + return ret; + + } else if (n <= 63) { + /* Uncompressed label */ + idx++; + if (!compressed) + ret++; + + if (idx + n > p->size) + return -1; + + if ((size_t) n + 1 > l) + return -1; + + if (!first_label) { + *(ret_name++) = '.'; + l--; + } else + first_label = 0; + + if (!(avahi_escape_label((char*) AVAHI_DNS_PACKET_DATA(p) + idx, n, &ret_name, &l))) + return -1; + + idx += n; + + if (!compressed) + ret += n; + } else if ((n & 0xC0) == 0xC0) { + /* Compressed label */ + + if (idx+2 > p->size) + return -1; + + label_ptr = ((unsigned) (AVAHI_DNS_PACKET_DATA(p)[idx] & ~0xC0)) << 8 | AVAHI_DNS_PACKET_DATA(p)[idx+1]; + + if ((label_ptr < AVAHI_DNS_PACKET_HEADER_SIZE) || (label_ptr >= idx)) + return -1; + + idx = label_ptr; + + if (!compressed) + ret += 2; + + compressed = 1; + } else + return -1; + } + + return -1; +} + +int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l) { + int r; + + if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0) + return -1; + + p->rindex += r; + return 0; +} + +int avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, uint16_t *ret_v) { + uint8_t *d; + + assert(p); + assert(ret_v); + + if (p->rindex + sizeof(uint16_t) > p->size) + return -1; + + d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex); + *ret_v = (d[0] << 8) | d[1]; + p->rindex += sizeof(uint16_t); + + return 0; +} + +int avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, uint32_t *ret_v) { + uint8_t* d; + + assert(p); + assert(ret_v); + + if (p->rindex + sizeof(uint32_t) > p->size) + return -1; + + d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex); + *ret_v = (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3]; + p->rindex += sizeof(uint32_t); + + return 0; +} + +int avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, void * ret_data, size_t l) { + assert(p); + assert(ret_data); + assert(l > 0); + + if (p->rindex + l > p->size) + return -1; + + memcpy(ret_data, AVAHI_DNS_PACKET_DATA(p) + p->rindex, l); + p->rindex += l; + + return 0; +} + +int avahi_dns_packet_consume_string(AvahiDnsPacket *p, char *ret_string, size_t l) { + size_t k; + + assert(p); + assert(ret_string); + assert(l > 0); + + if (p->rindex >= p->size) + return -1; + + k = AVAHI_DNS_PACKET_DATA(p)[p->rindex]; + + if (p->rindex+1+k > p->size) + return -1; + + if (l > k+1) + l = k+1; + + memcpy(ret_string, AVAHI_DNS_PACKET_DATA(p)+p->rindex+1, l-1); + ret_string[l-1] = 0; + + p->rindex += 1+k; + + return 0; +} + +const void* avahi_dns_packet_get_rptr(AvahiDnsPacket *p) { + assert(p); + + if (p->rindex > p->size) + return NULL; + + return AVAHI_DNS_PACKET_DATA(p) + p->rindex; +} + +int avahi_dns_packet_skip(AvahiDnsPacket *p, size_t length) { + assert(p); + + if (p->rindex + length > p->size) + return -1; + + p->rindex += length; + return 0; +} + +static int parse_rdata(AvahiDnsPacket *p, AvahiRecord *r, uint16_t rdlength) { + char buf[AVAHI_DOMAIN_NAME_MAX]; + const void* start; + + assert(p); + assert(r); + + start = avahi_dns_packet_get_rptr(p); + + switch (r->key->type) { + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + + if (avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) + return -1; + + r->data.ptr.name = avahi_strdup(buf); + break; + + + case AVAHI_DNS_TYPE_SRV: + + if (avahi_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 || + avahi_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 || + avahi_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 || + avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) + return -1; + + r->data.srv.name = avahi_strdup(buf); + break; + + case AVAHI_DNS_TYPE_HINFO: + + if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) + return -1; + + r->data.hinfo.cpu = avahi_strdup(buf); + + if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) + return -1; + + r->data.hinfo.os = avahi_strdup(buf); + break; + + case AVAHI_DNS_TYPE_TXT: + + if (rdlength > 0) { + if (avahi_string_list_parse(avahi_dns_packet_get_rptr(p), rdlength, &r->data.txt.string_list) < 0) + return -1; + + if (avahi_dns_packet_skip(p, rdlength) < 0) + return -1; + } else + r->data.txt.string_list = NULL; + + break; + + case AVAHI_DNS_TYPE_A: + +/* avahi_log_debug("A"); */ + + if (avahi_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(AvahiIPv4Address)) < 0) + return -1; + + break; + + case AVAHI_DNS_TYPE_AAAA: + +/* avahi_log_debug("aaaa"); */ + + if (avahi_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(AvahiIPv6Address)) < 0) + return -1; + + break; + + default: + +/* avahi_log_debug("generic"); */ + + if (rdlength > 0) { + + r->data.generic.data = avahi_memdup(avahi_dns_packet_get_rptr(p), rdlength); + r->data.generic.size = rdlength; + + if (avahi_dns_packet_skip(p, rdlength) < 0) + return -1; + } + + break; + } + + /* Check if we read enough data */ + if ((const uint8_t*) avahi_dns_packet_get_rptr(p) - (const uint8_t*) start != rdlength) + return -1; + + return 0; +} + +AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_flush) { + char name[AVAHI_DOMAIN_NAME_MAX]; + uint16_t type, class; + uint32_t ttl; + uint16_t rdlength; + AvahiRecord *r = NULL; + + assert(p); + + if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || + avahi_dns_packet_consume_uint16(p, &type) < 0 || + avahi_dns_packet_consume_uint16(p, &class) < 0 || + avahi_dns_packet_consume_uint32(p, &ttl) < 0 || + avahi_dns_packet_consume_uint16(p, &rdlength) < 0 || + p->rindex + rdlength > p->size) + goto fail; + + if (ret_cache_flush) + *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH); + class &= ~AVAHI_DNS_CACHE_FLUSH; + + if (!(r = avahi_record_new_full(name, class, type, ttl))) + goto fail; + + if (parse_rdata(p, r, rdlength) < 0) + goto fail; + + if (!avahi_record_is_valid(r)) + goto fail; + + return r; + +fail: + if (r) + avahi_record_unref(r); + + return NULL; +} + +AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_response) { + char name[256]; + uint16_t type, class; + AvahiKey *k; + + assert(p); + + if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || + avahi_dns_packet_consume_uint16(p, &type) < 0 || + avahi_dns_packet_consume_uint16(p, &class) < 0) + return NULL; + + if (ret_unicast_response) + *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE); + + class &= ~AVAHI_DNS_UNICAST_RESPONSE; + + if (!(k = avahi_key_new(name, class, type))) + return NULL; + + if (!avahi_key_is_valid(k)) { + avahi_key_unref(k); + return NULL; + } + + return k; +} + +uint8_t* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, int unicast_response) { + uint8_t *t; + size_t size; + + assert(p); + assert(k); + + size = p->size; + + if (!(t = avahi_dns_packet_append_name(p, k->name)) || + !avahi_dns_packet_append_uint16(p, k->type) || + !avahi_dns_packet_append_uint16(p, k->clazz | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) { + p->size = size; + avahi_dns_packet_cleanup_name_table(p); + + return NULL; + } + + return t; +} + +static int append_rdata(AvahiDnsPacket *p, AvahiRecord *r) { + assert(p); + assert(r); + + switch (r->key->type) { + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + + if (!(avahi_dns_packet_append_name(p, r->data.ptr.name))) + return -1; + + break; + + case AVAHI_DNS_TYPE_SRV: + + if (!avahi_dns_packet_append_uint16(p, r->data.srv.priority) || + !avahi_dns_packet_append_uint16(p, r->data.srv.weight) || + !avahi_dns_packet_append_uint16(p, r->data.srv.port) || + !avahi_dns_packet_append_name(p, r->data.srv.name)) + return -1; + + break; + + case AVAHI_DNS_TYPE_HINFO: + if (!avahi_dns_packet_append_string(p, r->data.hinfo.cpu) || + !avahi_dns_packet_append_string(p, r->data.hinfo.os)) + return -1; + + break; + + case AVAHI_DNS_TYPE_TXT: { + + uint8_t *data; + size_t n; + + n = avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); + + if (!(data = avahi_dns_packet_extend(p, n))) + return -1; + + avahi_string_list_serialize(r->data.txt.string_list, data, n); + break; + } + + + case AVAHI_DNS_TYPE_A: + + if (!avahi_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address))) + return -1; + + break; + + case AVAHI_DNS_TYPE_AAAA: + + if (!avahi_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address))) + return -1; + + break; + + default: + + if (r->data.generic.size) + if (!avahi_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size)) + return -1; + + break; + } + + return 0; +} + + +uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int cache_flush, unsigned max_ttl) { + uint8_t *t, *l, *start; + size_t size; + + assert(p); + assert(r); + + size = p->size; + + if (!(t = avahi_dns_packet_append_name(p, r->key->name)) || + !avahi_dns_packet_append_uint16(p, r->key->type) || + !avahi_dns_packet_append_uint16(p, cache_flush ? (r->key->clazz | AVAHI_DNS_CACHE_FLUSH) : (r->key->clazz &~ AVAHI_DNS_CACHE_FLUSH)) || + !avahi_dns_packet_append_uint32(p, (max_ttl && r->ttl > max_ttl) ? max_ttl : r->ttl) || + !(l = avahi_dns_packet_append_uint16(p, 0))) + goto fail; + + start = avahi_dns_packet_extend(p, 0); + + if (append_rdata(p, r) < 0) + goto fail; + + size = avahi_dns_packet_extend(p, 0) - start; + assert(size <= AVAHI_DNS_RDATA_MAX); + +/* avahi_log_debug("appended %u", size); */ + + l[0] = (uint8_t) ((uint16_t) size >> 8); + l[1] = (uint8_t) ((uint16_t) size); + + return t; + + +fail: + p->size = size; + avahi_dns_packet_cleanup_name_table(p); + + return NULL; +} + +int avahi_dns_packet_is_empty(AvahiDnsPacket *p) { + assert(p); + + return p->size <= AVAHI_DNS_PACKET_HEADER_SIZE; +} + +size_t avahi_dns_packet_space(AvahiDnsPacket *p) { + assert(p); + + assert(p->size <= p->max_size); + + return p->max_size - p->size; +} + +size_t avahi_dns_packet_reserve_size(AvahiDnsPacket *p, size_t res_size) { + assert(p); + + assert(p->size + p->res_size <= p->max_size); + + if ((p->size + p->res_size + res_size) <= p->max_size) + p->res_size += res_size; + + return p->res_size; +} + +size_t avahi_dns_packet_reserved_space(AvahiDnsPacket *p) { + assert(p); + + assert(p->size + p->res_size <= p->max_size); + + return p->max_size - p->size - p->res_size; +} + +int avahi_rdata_parse(AvahiRecord *record, const void* rdata, size_t size) { + int ret; + AvahiDnsPacket p; + + assert(record); + assert(rdata); + + p.data = (void*) rdata; + p.max_size = p.size = size; + p.rindex = 0; + p.name_table = NULL; + + ret = parse_rdata(&p, record, size); + + assert(!p.name_table); + + return ret; +} + +size_t avahi_rdata_serialize(AvahiRecord *record, void *rdata, size_t max_size) { + int ret; + AvahiDnsPacket p; + + assert(record); + assert(rdata); + assert(max_size > 0); + + p.data = (void*) rdata; + p.max_size = max_size; + p.size = p.rindex = 0; + p.name_table = NULL; + + ret = append_rdata(&p, record); + + if (p.name_table) + avahi_hashmap_free(p.name_table); + + if (ret < 0) + return (size_t) -1; + + return p.size; +} diff --git a/3rdparty/QtZeroConf/avahi-core/dns.h b/3rdparty/QtZeroConf/avahi-core/dns.h new file mode 100644 index 000000000..13b1ac29a --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/dns.h @@ -0,0 +1,113 @@ +#ifndef foodnshfoo +#define foodnshfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "rr.h" +#include "hashmap.h" + +#define AVAHI_DNS_PACKET_HEADER_SIZE 12 +#define AVAHI_DNS_PACKET_EXTRA_SIZE 48 +#define AVAHI_DNS_LABELS_MAX 127 +#define AVAHI_DNS_RDATA_MAX 0xFFFF +#define AVAHI_DNS_PACKET_SIZE_MAX (AVAHI_DNS_PACKET_HEADER_SIZE + 256 + 2 + 2 + 4 + 2 + AVAHI_DNS_RDATA_MAX) + +typedef struct AvahiDnsPacket { + size_t size, rindex, max_size, res_size; + AvahiHashmap *name_table; /* for name compression */ + uint8_t *data; +} AvahiDnsPacket; + +#define AVAHI_DNS_PACKET_DATA(p) ((p)->data ? (p)->data : ((uint8_t*) p) + sizeof(AvahiDnsPacket)) + +AvahiDnsPacket* avahi_dns_packet_new(unsigned mtu); +AvahiDnsPacket* avahi_dns_packet_new_query(unsigned mtu); +AvahiDnsPacket* avahi_dns_packet_new_response(unsigned mtu, int aa); + +AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, unsigned mtu, int copy_queries, int aa); + +void avahi_dns_packet_free(AvahiDnsPacket *p); +void avahi_dns_packet_set_field(AvahiDnsPacket *p, unsigned idx, uint16_t v); +uint16_t avahi_dns_packet_get_field(AvahiDnsPacket *p, unsigned idx); +void avahi_dns_packet_inc_field(AvahiDnsPacket *p, unsigned idx); + +uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l); + +void avahi_dns_packet_cleanup_name_table(AvahiDnsPacket *p); + +uint8_t *avahi_dns_packet_append_uint16(AvahiDnsPacket *p, uint16_t v); +uint8_t *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, uint32_t v); +uint8_t *avahi_dns_packet_append_name(AvahiDnsPacket *p, const char *name); +uint8_t *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, const void *d, size_t l); +uint8_t* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, int unicast_response); +uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int cache_flush, unsigned max_ttl); +uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s); + +int avahi_dns_packet_is_query(AvahiDnsPacket *p); +int avahi_dns_packet_check_valid(AvahiDnsPacket *p); +int avahi_dns_packet_check_valid_multicast(AvahiDnsPacket *p); + +int avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, uint16_t *ret_v); +int avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, uint32_t *ret_v); +int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l); +int avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, void* ret_data, size_t l); +AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_response); +AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_flush); +int avahi_dns_packet_consume_string(AvahiDnsPacket *p, char *ret_string, size_t l); + +const void* avahi_dns_packet_get_rptr(AvahiDnsPacket *p); + +int avahi_dns_packet_skip(AvahiDnsPacket *p, size_t length); + +int avahi_dns_packet_is_empty(AvahiDnsPacket *p); +size_t avahi_dns_packet_space(AvahiDnsPacket *p); +size_t avahi_dns_packet_reserve_size(AvahiDnsPacket *p, size_t res_size); +size_t avahi_dns_packet_reserved_space(AvahiDnsPacket *p); + +#define AVAHI_DNS_FIELD_ID 0 +#define AVAHI_DNS_FIELD_FLAGS 1 +#define AVAHI_DNS_FIELD_QDCOUNT 2 +#define AVAHI_DNS_FIELD_ANCOUNT 3 +#define AVAHI_DNS_FIELD_NSCOUNT 4 +#define AVAHI_DNS_FIELD_ARCOUNT 5 + +#define AVAHI_DNS_FLAG_QR (1 << 15) +#define AVAHI_DNS_FLAG_OPCODE (15 << 11) +#define AVAHI_DNS_FLAG_RCODE (15) +#define AVAHI_DNS_FLAG_TC (1 << 9) +#define AVAHI_DNS_FLAG_AA (1 << 10) + +#define AVAHI_DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \ + (((uint16_t) !!qr << 15) | \ + ((uint16_t) (opcode & 15) << 11) | \ + ((uint16_t) !!aa << 10) | \ + ((uint16_t) !!tc << 9) | \ + ((uint16_t) !!rd << 8) | \ + ((uint16_t) !!ra << 7) | \ + ((uint16_t) !!ad << 5) | \ + ((uint16_t) !!cd << 4) | \ + ((uint16_t) (rcode & 15))) + +#define AVAHI_MDNS_SUFFIX_LOCAL "local" +#define AVAHI_MDNS_SUFFIX_ADDR_IPV4 "254.169.in-addr.arpa" +#define AVAHI_MDNS_SUFFIX_ADDR_IPV6 "0.8.e.f.ip6.arpa" + +#endif + diff --git a/3rdparty/QtZeroConf/avahi-core/domain-util.c b/3rdparty/QtZeroConf/avahi-core/domain-util.c new file mode 100644 index 000000000..6eed27f21 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/domain-util.c @@ -0,0 +1,188 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "log.h" +#include "domain-util.h" +#include "util.h" + +static void strip_bad_chars(char *s) { + char *p, *d; + + s[strcspn(s, ".")] = 0; + + for (p = s, d = s; *p; p++) + if ((*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z') || + (*p >= '0' && *p <= '9') || + *p == '-') + *(d++) = *p; + + *d = 0; +} + +#ifdef __linux__ +static int load_lsb_distrib_id(char *ret_s, size_t size) { + FILE *f; + + assert(ret_s); + assert(size > 0); + + if (!(f = fopen("/etc/lsb-release", "r"))) + return -1; + + while (!feof(f)) { + char ln[256], *p; + + if (!fgets(ln, sizeof(ln), f)) + break; + + if (strncmp(ln, "DISTRIB_ID=", 11)) + continue; + + p = ln + 11; + p += strspn(p, "\""); + p[strcspn(p, "\"")] = 0; + + snprintf(ret_s, size, "%s", p); + + fclose(f); + return 0; + } + + fclose(f); + return -1; +} +#endif + +char *avahi_get_host_name(char *ret_s, size_t size) { + assert(ret_s); + assert(size > 0); + + if (gethostname(ret_s, size) >= 0) { + ret_s[size-1] = 0; + strip_bad_chars(ret_s); + } else + *ret_s = 0; + + if (strcmp(ret_s, "localhost") == 0 || strncmp(ret_s, "localhost.", 10) == 0) { + *ret_s = 0; + avahi_log_warn("System host name is set to 'localhost'. This is not a suitable mDNS host name, looking for alternatives."); + } + + if (*ret_s == 0) { + /* No hostname was set, so let's take the OS name */ + +#ifdef __linux__ + + /* Try LSB distribution name first */ + if (load_lsb_distrib_id(ret_s, size) >= 0) { + strip_bad_chars(ret_s); + avahi_strdown(ret_s); + } + + if (*ret_s == 0) +#endif + + { + /* Try uname() second */ + struct utsname utsname; + + if (uname(&utsname) >= 0) { + snprintf(ret_s, size, "%s", utsname.sysname); + strip_bad_chars(ret_s); + avahi_strdown(ret_s); + } + + /* Give up */ + if (*ret_s == 0) + snprintf(ret_s, size, "unnamed"); + } + } + + if (size >= AVAHI_LABEL_MAX) + ret_s[AVAHI_LABEL_MAX-1] = 0; + + return ret_s; +} + +char *avahi_get_host_name_strdup(void) { + char t[AVAHI_DOMAIN_NAME_MAX]; + + if (!(avahi_get_host_name(t, sizeof(t)))) + return NULL; + + return avahi_strdup(t); +} + +int avahi_binary_domain_cmp(const char *a, const char *b) { + assert(a); + assert(b); + + if (a == b) + return 0; + + for (;;) { + char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *p; + int r; + + p = avahi_unescape_label(&a, ca, sizeof(ca)); + assert(p); + p = avahi_unescape_label(&b, cb, sizeof(cb)); + assert(p); + + if ((r = strcmp(ca, cb))) + return r; + + if (!*a && !*b) + return 0; + } +} + +int avahi_domain_ends_with(const char *domain, const char *suffix) { + assert(domain); + assert(suffix); + + for (;;) { + char dummy[AVAHI_LABEL_MAX], *r; + + if (*domain == 0) + return 0; + + if (avahi_domain_equal(domain, suffix)) + return 1; + + r = avahi_unescape_label(&domain, dummy, sizeof(dummy)); + assert(r); + } +} + diff --git a/3rdparty/QtZeroConf/avahi-core/domain-util.h b/3rdparty/QtZeroConf/avahi-core/domain-util.h new file mode 100644 index 000000000..082fde65c --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/domain-util.h @@ -0,0 +1,45 @@ +#ifndef foodomainutilhfoo +#define foodomainutilhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Return the local host name. */ +char *avahi_get_host_name(char *ret_s, size_t size); + +/** Return the local host name. avahi_free() the result! */ +char *avahi_get_host_name_strdup(void); + +/** Do a binary comparison of to specified domain names, return -1, 0, or 1, depending on the order. */ +int avahi_binary_domain_cmp(const char *a, const char *b); + +/** Returns 1 if the the end labels of domain are eqal to suffix */ +int avahi_domain_ends_with(const char *domain, const char *suffix); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/entry.c b/3rdparty/QtZeroConf/avahi-core/entry.c new file mode 100644 index 000000000..0d862133d --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/entry.c @@ -0,0 +1,1233 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "internal.h" +#include "iface.h" +#include "socket.h" +#include "browse.h" +#include "log.h" +#include "util.h" +#include "dns-srv-rr.h" +#include "rr-util.h" +#include "domain-util.h" + +static void transport_flags_from_domain(AvahiServer *s, AvahiPublishFlags *flags, const char *domain) { + assert(flags); + assert(domain); + + assert(!((*flags & AVAHI_PUBLISH_USE_MULTICAST) && (*flags & AVAHI_PUBLISH_USE_WIDE_AREA))); + + if (*flags & (AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA)) + return; + + if (!s->wide_area_lookup_engine || + !avahi_wide_area_has_servers(s->wide_area_lookup_engine) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6)) + *flags |= AVAHI_PUBLISH_USE_MULTICAST; + else + *flags |= AVAHI_PUBLISH_USE_WIDE_AREA; +} + +void avahi_entry_free(AvahiServer*s, AvahiEntry *e) { + AvahiEntry *t; + + assert(s); + assert(e); + + avahi_goodbye_entry(s, e, 1, 1); + + /* Remove from linked list */ + AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e); + + /* Remove from hash table indexed by name */ + t = avahi_hashmap_lookup(s->entries_by_key, e->record->key); + AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e); + if (t) + avahi_hashmap_replace(s->entries_by_key, t->record->key, t); + else + avahi_hashmap_remove(s->entries_by_key, e->record->key); + + /* Remove from associated group */ + if (e->group) + AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e); + + avahi_record_unref(e->record); + avahi_free(e); +} + +void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g) { + assert(s); + assert(g); + + while (g->entries) + avahi_entry_free(s, g->entries); + + if (g->register_time_event) + avahi_time_event_free(g->register_time_event); + + AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g); + avahi_free(g); +} + +void avahi_cleanup_dead_entries(AvahiServer *s) { + assert(s); + + if (s->need_group_cleanup) { + AvahiSEntryGroup *g, *next; + + for (g = s->groups; g; g = next) { + next = g->groups_next; + + if (g->dead) + avahi_entry_group_free(s, g); + } + + s->need_group_cleanup = 0; + } + + if (s->need_entry_cleanup) { + AvahiEntry *e, *next; + + for (e = s->entries; e; e = next) { + next = e->entries_next; + + if (e->dead) + avahi_entry_free(s, e); + } + + s->need_entry_cleanup = 0; + } + + if (s->need_browser_cleanup) + avahi_browser_cleanup(s); + + if (s->cleanup_time_event) { + avahi_time_event_free(s->cleanup_time_event); + s->cleanup_time_event = NULL; + } +} + +static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiPublishFlags flags) { + AvahiEntry *e; + + assert(s); + assert(r); + + for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) { + if (e->dead) + continue; + + if (!(flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_UNIQUE)) + continue; + + if ((flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) && (e->flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) ) + continue; + + if (avahi_record_equal_no_ttl(r, e->record)) { + /* The records are the same, not a conflict in any case */ + continue; + } + + if ((interface <= 0 || + e->interface <= 0 || + e->interface == interface) && + (protocol == AVAHI_PROTO_UNSPEC || + e->protocol == AVAHI_PROTO_UNSPEC || + e->protocol == protocol)) + + return -1; + } + + return 0; +} + +static AvahiEntry * server_add_internal( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + AvahiRecord *r) { + + AvahiEntry *e; + + assert(s); + assert(r); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, s->state != AVAHI_SERVER_FAILURE && s->state != AVAHI_SERVER_INVALID, AVAHI_ERR_BAD_STATE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID( + flags, + AVAHI_PUBLISH_NO_ANNOUNCE| + AVAHI_PUBLISH_NO_PROBE| + AVAHI_PUBLISH_UNIQUE| + AVAHI_PUBLISH_ALLOW_MULTIPLE| + AVAHI_PUBLISH_UPDATE| + AVAHI_PUBLISH_USE_WIDE_AREA| + AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(r->key->name), AVAHI_ERR_INVALID_HOST_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->ttl != 0, AVAHI_ERR_INVALID_TTL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !avahi_key_is_pattern(r->key), AVAHI_ERR_IS_PATTERN); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_record_is_valid(r), AVAHI_ERR_INVALID_RECORD); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->key->clazz == AVAHI_DNS_CLASS_IN, AVAHI_ERR_INVALID_DNS_CLASS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, + (r->key->type != 0) && + (r->key->type != AVAHI_DNS_TYPE_ANY) && + (r->key->type != AVAHI_DNS_TYPE_OPT) && + (r->key->type != AVAHI_DNS_TYPE_TKEY) && + (r->key->type != AVAHI_DNS_TYPE_TSIG) && + (r->key->type != AVAHI_DNS_TYPE_IXFR) && + (r->key->type != AVAHI_DNS_TYPE_AXFR), AVAHI_ERR_INVALID_DNS_TYPE); + + transport_flags_from_domain(s, &flags, r->key->name); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !s->config.disable_publishing, AVAHI_ERR_NOT_PERMITTED); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, + !g || + (g->state != AVAHI_ENTRY_GROUP_ESTABLISHED && g->state != AVAHI_ENTRY_GROUP_REGISTERING) || + (flags & AVAHI_PUBLISH_UPDATE), AVAHI_ERR_BAD_STATE); + + if (flags & AVAHI_PUBLISH_UPDATE) { + AvahiRecord *old_record; + int is_first = 1; + + /* Update and existing record */ + + /* Find the first matching entry */ + for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) { + if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol) + break; + + is_first = 0; + } + + /* Hmm, nothing found? */ + if (!e) { + avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND); + return NULL; + } + + /* Update the entry */ + old_record = e->record; + e->record = avahi_record_ref(r); + e->flags = flags; + + /* Announce our changes when needed */ + if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) { + + /* Remove the old entry from all caches, if needed */ + if (!(e->flags & AVAHI_PUBLISH_UNIQUE)) + avahi_goodbye_entry(s, e, 1, 0); + + /* Reannounce our updated entry */ + avahi_reannounce_entry(s, e); + } + + /* If we were the first entry in the list, we need to update the key */ + if (is_first) + avahi_hashmap_replace(s->entries_by_key, e->record->key, e); + + avahi_record_unref(old_record); + + } else { + AvahiEntry *t; + + /* Add a new record */ + + if (check_record_conflict(s, interface, protocol, r, flags) < 0) { + avahi_server_set_errno(s, AVAHI_ERR_COLLISION); + return NULL; + } + + if (!(e = avahi_new(AvahiEntry, 1))) { + avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + e->server = s; + e->record = avahi_record_ref(r); + e->group = g; + e->interface = interface; + e->protocol = protocol; + e->flags = flags; + e->dead = 0; + + AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->announcers); + + AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e); + + /* Insert into hash table indexed by name */ + t = avahi_hashmap_lookup(s->entries_by_key, e->record->key); + AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e); + avahi_hashmap_replace(s->entries_by_key, e->record->key, t); + + /* Insert into group list */ + if (g) + AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); + + avahi_announce_entry(s, e); + } + + return e; +} + +int avahi_server_add( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + AvahiRecord *r) { + + if (!server_add_internal(s, g, interface, protocol, flags, r)) + return avahi_server_errno(s); + + return AVAHI_OK; +} + +const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) { + AvahiEntry **e = (AvahiEntry**) state; + assert(s); + assert(e); + + if (!*e) + *e = g ? g->entries : s->entries; + + while (*e && (*e)->dead) + *e = g ? (*e)->by_group_next : (*e)->entries_next; + + if (!*e) + return NULL; + + return avahi_record_ref((*e)->record); +} + +int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) { + AvahiEntry *e; + + assert(s); + assert(callback); + + callback(";;; ZONE DUMP FOLLOWS ;;;", userdata); + + for (e = s->entries; e; e = e->entries_next) { + char *t; + char ln[256]; + + if (e->dead) + continue; + + if (!(t = avahi_record_to_string(e->record))) + return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + + snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol); + avahi_free(t); + + callback(ln, userdata); + } + + avahi_dump_caches(s->monitor, callback, userdata); + + if (s->wide_area_lookup_engine) + avahi_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata); + return AVAHI_OK; +} + +static AvahiEntry *server_add_ptr_internal( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + uint32_t ttl, + const char *name, + const char *dest) { + + AvahiRecord *r; + AvahiEntry *e; + + assert(s); + assert(dest); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(dest), AVAHI_ERR_INVALID_HOST_NAME); + + if (!name) + name = s->host_name_fqdn; + + if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl))) { + avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + r->data.ptr.name = avahi_normalize_name_strdup(dest); + e = server_add_internal(s, g, interface, protocol, flags, r); + avahi_record_unref(r); + return e; +} + +int avahi_server_add_ptr( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + uint32_t ttl, + const char *name, + const char *dest) { + + AvahiEntry *e; + + assert(s); + + if (!(e = server_add_ptr_internal(s, g, interface, protocol, flags, ttl, name, dest))) + return avahi_server_errno(s); + + return AVAHI_OK; +} + +int avahi_server_add_address( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + AvahiAddress *a) { + + char n[AVAHI_DOMAIN_NAME_MAX]; + int ret = AVAHI_OK; + AvahiEntry *entry = NULL, *reverse = NULL; + AvahiRecord *r; + + assert(s); + assert(a); + + AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(a->proto), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, + AVAHI_PUBLISH_NO_REVERSE| + AVAHI_PUBLISH_NO_ANNOUNCE| + AVAHI_PUBLISH_NO_PROBE| + AVAHI_PUBLISH_UPDATE| + AVAHI_PUBLISH_USE_WIDE_AREA| + AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY(s, !name || avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME); + + /* Prepare the host naem */ + + if (!name) + name = s->host_name_fqdn; + else { + AVAHI_ASSERT_TRUE(avahi_normalize_name(name, n, sizeof(n))); + name = n; + } + + transport_flags_from_domain(s, &flags, name); + AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + + /* Create the A/AAAA record */ + + if (a->proto == AVAHI_PROTO_INET) { + + if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) { + ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + goto finish; + } + + r->data.a.address = a->data.ipv4; + + } else { + assert(a->proto == AVAHI_PROTO_INET6); + + if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) { + ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + goto finish; + } + + r->data.aaaa.address = a->data.ipv6; + } + + entry = server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r); + avahi_record_unref(r); + + if (!entry) { + ret = avahi_server_errno(s); + goto finish; + } + + /* Create the reverse lookup entry */ + + if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) { + char reverse_n[AVAHI_DOMAIN_NAME_MAX]; + avahi_reverse_lookup_name(a, reverse_n, sizeof(reverse_n)); + + if (!(reverse = server_add_ptr_internal(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse_n, name))) { + ret = avahi_server_errno(s); + goto finish; + } + } + +finish: + + if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) { + if (entry) + avahi_entry_free(s, entry); + if (reverse) + avahi_entry_free(s, reverse); + } + + return ret; +} + +static AvahiEntry *server_add_txt_strlst_nocopy( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + uint32_t ttl, + const char *name, + AvahiStringList *strlst) { + + AvahiRecord *r; + AvahiEntry *e; + + assert(s); + + if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) { + avahi_string_list_free(strlst); + avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + r->data.txt.string_list = strlst; + e = server_add_internal(s, g, interface, protocol, flags, r); + avahi_record_unref(r); + + return e; +} + +static AvahiStringList *add_magic_cookie( + AvahiServer *s, + AvahiStringList *strlst) { + + assert(s); + + if (!s->config.add_service_cookie) + return strlst; + + if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE)) + /* This string list already contains a magic cookie */ + return strlst; + + return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie); +} + +static int server_add_service_strlst_nocopy( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + const char *host, + uint16_t port, + AvahiStringList *strlst) { + + char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL; + AvahiRecord *r = NULL; + int ret = AVAHI_OK; + AvahiEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL; + + assert(s); + assert(type); + assert(name); + + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, + AVAHI_PUBLISH_NO_COOKIE| + AVAHI_PUBLISH_UPDATE| + AVAHI_PUBLISH_USE_WIDE_AREA| + AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_fqdn(host), AVAHI_ERR_INVALID_HOST_NAME); + + if (!domain) + domain = s->domain_name; + + if (!host) + host = s->host_name_fqdn; + + transport_flags_from_domain(s, &flags, domain); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + + if (!(h = avahi_normalize_name_strdup(host))) { + ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 || + (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 || + (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) { + avahi_server_set_errno(s, ret); + goto fail; + } + + /* Add service enumeration PTR record */ + + if (!(ptr_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name))) { + ret = avahi_server_errno(s); + goto fail; + } + + /* Add SRV record */ + + if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) { + ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + goto fail; + } + + r->data.srv.priority = 0; + r->data.srv.weight = 0; + r->data.srv.port = port; + r->data.srv.name = h; + h = NULL; + srv_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, r); + avahi_record_unref(r); + + if (!srv_entry) { + ret = avahi_server_errno(s); + goto fail; + } + + /* Add TXT record */ + + if (!(flags & AVAHI_PUBLISH_NO_COOKIE)) + strlst = add_magic_cookie(s, strlst); + + txt_entry = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst); + strlst = NULL; + + if (!txt_entry) { + ret = avahi_server_errno(s); + goto fail; + } + + /* Add service type enumeration record */ + + if (!(enum_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name))) { + ret = avahi_server_errno(s); + goto fail; + } + +fail: + if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) { + if (srv_entry) + avahi_entry_free(s, srv_entry); + if (txt_entry) + avahi_entry_free(s, txt_entry); + if (ptr_entry) + avahi_entry_free(s, ptr_entry); + if (enum_entry) + avahi_entry_free(s, enum_entry); + } + + avahi_string_list_free(strlst); + avahi_free(h); + + return ret; +} + +int avahi_server_add_service_strlst( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + const char *host, + uint16_t port, + AvahiStringList *strlst) { + + assert(s); + assert(type); + assert(name); + + return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_copy(strlst)); +} + +int avahi_server_add_service( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + const char *host, + uint16_t port, + ... ){ + + va_list va; + int ret; + + va_start(va, port); + ret = server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_new_va(va)); + va_end(va); + + return ret; +} + +static int server_update_service_txt_strlst_nocopy( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + AvahiStringList *strlst) { + + char svc_name[AVAHI_DOMAIN_NAME_MAX]; + int ret = AVAHI_OK; + AvahiEntry *e; + + assert(s); + assert(type); + assert(name); + + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, + AVAHI_PUBLISH_NO_COOKIE| + AVAHI_PUBLISH_USE_WIDE_AREA| + AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + + if (!domain) + domain = s->domain_name; + + transport_flags_from_domain(s, &flags, domain); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + + if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) { + avahi_server_set_errno(s, ret); + goto fail; + } + + /* Add TXT record */ + if (!(flags & AVAHI_PUBLISH_NO_COOKIE)) + strlst = add_magic_cookie(s, strlst); + + e = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_UPDATE, AVAHI_DEFAULT_TTL, svc_name, strlst); + strlst = NULL; + + if (!e) + ret = avahi_server_errno(s); + +fail: + + avahi_string_list_free(strlst); + + return ret; +} + +int avahi_server_update_service_txt_strlst( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + AvahiStringList *strlst) { + + return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_copy(strlst)); +} + +/** Update the TXT record for a service with the NULL termonate list of strings */ +int avahi_server_update_service_txt( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + ...) { + + va_list va; + int ret; + + va_start(va, domain); + ret = server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_new_va(va)); + va_end(va); + + return ret; +} + +int avahi_server_add_service_subtype( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + const char *subtype) { + + int ret = AVAHI_OK; + char svc_name[AVAHI_DOMAIN_NAME_MAX], ptr_name[AVAHI_DOMAIN_NAME_MAX]; + + assert(name); + assert(type); + assert(subtype); + + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_subtype(subtype), AVAHI_ERR_INVALID_SERVICE_SUBTYPE); + + if (!domain) + domain = s->domain_name; + + transport_flags_from_domain(s, &flags, domain); + AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + + if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 || + (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) { + avahi_server_set_errno(s, ret); + goto fail; + } + + if ((ret = avahi_server_add_ptr(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0) + goto fail; + +fail: + + return ret; +} + +static void hexstring(char *s, size_t sl, const void *p, size_t pl) { + static const char hex[] = "0123456789abcdef"; + int b = 0; + const uint8_t *k = p; + + while (sl > 1 && pl > 0) { + *(s++) = hex[(b ? *k : *k >> 4) & 0xF]; + + if (b) { + k++; + pl--; + } + + b = !b; + + sl--; + } + + if (sl > 0) + *s = 0; +} + +static AvahiEntry *server_add_dns_server_name( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *domain, + AvahiDNSServerType type, + const char *name, + uint16_t port /** should be 53 */) { + + AvahiEntry *e; + char t[AVAHI_DOMAIN_NAME_MAX], normalized_d[AVAHI_DOMAIN_NAME_MAX], *n; + + AvahiRecord *r; + + assert(s); + assert(name); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_WIDE_AREA|AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, port != 0, AVAHI_ERR_INVALID_PORT); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + + if (!domain) + domain = s->domain_name; + + transport_flags_from_domain(s, &flags, domain); + AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + + if (!(n = avahi_normalize_name_strdup(name))) { + avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + AVAHI_ASSERT_TRUE(avahi_normalize_name(domain, normalized_d, sizeof(normalized_d))); + + snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", normalized_d); + + if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) { + avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + avahi_free(n); + return NULL; + } + + r->data.srv.priority = 0; + r->data.srv.weight = 0; + r->data.srv.port = port; + r->data.srv.name = n; + e = server_add_internal(s, g, interface, protocol, 0, r); + avahi_record_unref(r); + + return e; +} + +int avahi_server_add_dns_server_address( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *domain, + AvahiDNSServerType type, + const AvahiAddress *address, + uint16_t port /** should be 53 */) { + + AvahiRecord *r; + char n[64], h[64]; + AvahiEntry *a_entry, *s_entry; + + assert(s); + assert(address); + + AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(address->proto), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS); + AVAHI_CHECK_VALIDITY(s, port != 0, AVAHI_ERR_INVALID_PORT); + AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + + if (!domain) + domain = s->domain_name; + + transport_flags_from_domain(s, &flags, domain); + AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); + + if (address->proto == AVAHI_PROTO_INET) { + hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address)); + snprintf(n, sizeof(n), "ip-%s.%s", h, domain); + r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME); + r->data.a.address = address->data.ipv4; + } else { + hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address)); + snprintf(n, sizeof(n), "ip6-%s.%s", h, domain); + r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME); + r->data.aaaa.address = address->data.ipv6; + } + + if (!r) + return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + + a_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r); + avahi_record_unref(r); + + if (!a_entry) + return avahi_server_errno(s); + + if (!(s_entry = server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port))) { + if (!(flags & AVAHI_PUBLISH_UPDATE)) + avahi_entry_free(s, a_entry); + return avahi_server_errno(s); + } + + return AVAHI_OK; +} + +void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) { + assert(g); + + if (g->state == state) + return; + + assert(state <= AVAHI_ENTRY_GROUP_COLLISION); + + if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED) { + + /* If the entry group was established for a time longer then + * 5s, reset the establishment trial counter */ + + if (avahi_age(&g->established_at) > 5000000) + g->n_register_try = 0; + } else if (g->state == AVAHI_ENTRY_GROUP_REGISTERING) { + if (g->register_time_event) { + avahi_time_event_free(g->register_time_event); + g->register_time_event = NULL; + } + } + + if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) + + /* If the entry group is now established, remember the time + * this happened */ + + gettimeofday(&g->established_at, NULL); + + g->state = state; + + if (g->callback) + g->callback(g->server, g, state, g->userdata); +} + +AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) { + AvahiSEntryGroup *g; + + assert(s); + + if (!(g = avahi_new(AvahiSEntryGroup, 1))) { + avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + g->server = s; + g->callback = callback; + g->userdata = userdata; + g->dead = 0; + g->state = AVAHI_ENTRY_GROUP_UNCOMMITED; + g->n_probing = 0; + g->n_register_try = 0; + g->register_time_event = NULL; + g->register_time.tv_sec = 0; + g->register_time.tv_usec = 0; + AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries); + + AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g); + return g; +} + +static void cleanup_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) { + AvahiServer *s = userdata; + + assert(s); + + avahi_cleanup_dead_entries(s); +} + +static void schedule_cleanup(AvahiServer *s) { + struct timeval tv; + + assert(s); + + if (!s->cleanup_time_event) + s->cleanup_time_event = avahi_time_event_new(s->time_event_queue, avahi_elapse_time(&tv, 1000, 0), &cleanup_time_event_callback, s); +} + +void avahi_s_entry_group_free(AvahiSEntryGroup *g) { + AvahiEntry *e; + + assert(g); + assert(g->server); + + for (e = g->entries; e; e = e->by_group_next) { + if (!e->dead) { + avahi_goodbye_entry(g->server, e, 1, 1); + e->dead = 1; + } + } + + if (g->register_time_event) { + avahi_time_event_free(g->register_time_event); + g->register_time_event = NULL; + } + + g->dead = 1; + + g->server->need_group_cleanup = 1; + g->server->need_entry_cleanup = 1; + + schedule_cleanup(g->server); +} + +static void entry_group_commit_real(AvahiSEntryGroup *g) { + assert(g); + + gettimeofday(&g->register_time, NULL); + + avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING); + + if (g->dead) + return; + + avahi_announce_group(g->server, g); + avahi_s_entry_group_check_probed(g, 0); +} + +static void entry_group_register_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) { + AvahiSEntryGroup *g = userdata; + assert(g); + + avahi_time_event_free(g->register_time_event); + g->register_time_event = NULL; + + /* Holdoff time passed, so let's start probing */ + entry_group_commit_real(g); +} + +int avahi_s_entry_group_commit(AvahiSEntryGroup *g) { + struct timeval now; + + assert(g); + assert(!g->dead); + + if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION) + return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE); + + if (avahi_s_entry_group_is_empty(g)) + return avahi_server_set_errno(g->server, AVAHI_ERR_IS_EMPTY); + + g->n_register_try++; + + avahi_timeval_add(&g->register_time, + 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ? + AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT : + AVAHI_RR_HOLDOFF_MSEC)); + + gettimeofday(&now, NULL); + + if (avahi_timeval_compare(&g->register_time, &now) <= 0) { + + /* Holdoff time passed, so let's start probing */ + entry_group_commit_real(g); + } else { + + /* Holdoff time has not yet passed, so let's wait */ + assert(!g->register_time_event); + g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g); + + avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING); + } + + return AVAHI_OK; +} + +void avahi_s_entry_group_reset(AvahiSEntryGroup *g) { + AvahiEntry *e; + assert(g); + + for (e = g->entries; e; e = e->by_group_next) { + if (!e->dead) { + avahi_goodbye_entry(g->server, e, 1, 1); + e->dead = 1; + } + } + g->server->need_entry_cleanup = 1; + + g->n_probing = 0; + + avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED); + + schedule_cleanup(g->server); +} + +int avahi_entry_is_commited(AvahiEntry *e) { + assert(e); + assert(!e->dead); + + return !e->group || + e->group->state == AVAHI_ENTRY_GROUP_REGISTERING || + e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED; +} + +AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) { + assert(g); + assert(!g->dead); + + return g->state; +} + +void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) { + assert(g); + + g->userdata = userdata; +} + +void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) { + assert(g); + + return g->userdata; +} + +int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) { + AvahiEntry *e; + assert(g); + + /* Look for an entry that is not dead */ + for (e = g->entries; e; e = e->by_group_next) + if (!e->dead) + return 0; + + return 1; +} diff --git a/3rdparty/QtZeroConf/avahi-core/fdutil.c b/3rdparty/QtZeroConf/avahi-core/fdutil.c new file mode 100644 index 000000000..c294754ad --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/fdutil.c @@ -0,0 +1,72 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "fdutil.h" + +int avahi_set_cloexec(int fd) { + int n; + + assert(fd >= 0); + + if ((n = fcntl(fd, F_GETFD)) < 0) + return -1; + + if (n & FD_CLOEXEC) + return 0; + + return fcntl(fd, F_SETFD, n|FD_CLOEXEC); +} + +int avahi_set_nonblock(int fd) { + int n; + + assert(fd >= 0); + + if ((n = fcntl(fd, F_GETFL)) < 0) + return -1; + + if (n & O_NONBLOCK) + return 0; + + return fcntl(fd, F_SETFL, n|O_NONBLOCK); +} + +int avahi_wait_for_write(int fd) { + fd_set fds; + int r; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + if ((r = select(fd+1, NULL, &fds, NULL, NULL)) < 0) + return -1; + + assert(r > 0); + + return 0; +} diff --git a/3rdparty/QtZeroConf/avahi-core/fdutil.h b/3rdparty/QtZeroConf/avahi-core/fdutil.h new file mode 100644 index 000000000..68607f8ee --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/fdutil.h @@ -0,0 +1,33 @@ +#ifndef foofdutilhfoo +#define foofdutilhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +AVAHI_C_DECL_BEGIN + +int avahi_set_cloexec(int fd); +int avahi_set_nonblock(int fd); +int avahi_wait_for_write(int fd); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/hashmap.c b/3rdparty/QtZeroConf/avahi-core/hashmap.c new file mode 100644 index 000000000..9b55bd316 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/hashmap.c @@ -0,0 +1,248 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#include + +#include "hashmap.h" +#include "util.h" + +#define HASH_MAP_SIZE 123 + +typedef struct Entry Entry; +struct Entry { + AvahiHashmap *hashmap; + void *key; + void *value; + + AVAHI_LLIST_FIELDS(Entry, bucket); + AVAHI_LLIST_FIELDS(Entry, entries); +}; + +struct AvahiHashmap { + AvahiHashFunc hash_func; + AvahiEqualFunc equal_func; + AvahiFreeFunc key_free_func, value_free_func; + + Entry *entries[HASH_MAP_SIZE]; + AVAHI_LLIST_HEAD(Entry, entries_list); +}; + +static Entry* entry_get(AvahiHashmap *m, const void *key) { + unsigned idx; + Entry *e; + + idx = m->hash_func(key) % HASH_MAP_SIZE; + + for (e = m->entries[idx]; e; e = e->bucket_next) + if (m->equal_func(key, e->key)) + return e; + + return NULL; +} + +static void entry_free(AvahiHashmap *m, Entry *e, int stolen) { + unsigned idx; + assert(m); + assert(e); + + idx = m->hash_func(e->key) % HASH_MAP_SIZE; + + AVAHI_LLIST_REMOVE(Entry, bucket, m->entries[idx], e); + AVAHI_LLIST_REMOVE(Entry, entries, m->entries_list, e); + + if (m->key_free_func) + m->key_free_func(e->key); + if (m->value_free_func && !stolen) + m->value_free_func(e->value); + + avahi_free(e); +} + +AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_func, AvahiFreeFunc key_free_func, AvahiFreeFunc value_free_func) { + AvahiHashmap *m; + + assert(hash_func); + assert(equal_func); + + if (!(m = avahi_new0(AvahiHashmap, 1))) + return NULL; + + m->hash_func = hash_func; + m->equal_func = equal_func; + m->key_free_func = key_free_func; + m->value_free_func = value_free_func; + + AVAHI_LLIST_HEAD_INIT(Entry, m->entries_list); + + return m; +} + +void avahi_hashmap_free(AvahiHashmap *m) { + assert(m); + + while (m->entries_list) + entry_free(m, m->entries_list, 0); + + avahi_free(m); +} + +void* avahi_hashmap_lookup(AvahiHashmap *m, const void *key) { + Entry *e; + + assert(m); + + if (!(e = entry_get(m, key))) + return NULL; + + return e->value; +} + +int avahi_hashmap_insert(AvahiHashmap *m, void *key, void *value) { + unsigned idx; + Entry *e; + + assert(m); + + if ((e = entry_get(m, key))) { + if (m->key_free_func) + m->key_free_func(key); + if (m->value_free_func) + m->value_free_func(value); + + return 1; + } + + if (!(e = avahi_new(Entry, 1))) + return -1; + + e->hashmap = m; + e->key = key; + e->value = value; + + AVAHI_LLIST_PREPEND(Entry, entries, m->entries_list, e); + + idx = m->hash_func(key) % HASH_MAP_SIZE; + AVAHI_LLIST_PREPEND(Entry, bucket, m->entries[idx], e); + + return 0; +} + + +int avahi_hashmap_replace(AvahiHashmap *m, void *key, void *value) { + unsigned idx; + Entry *e; + + assert(m); + + if ((e = entry_get(m, key))) { + if (m->key_free_func) + m->key_free_func(e->key); + if (m->value_free_func) + m->value_free_func(e->value); + + e->key = key; + e->value = value; + + return 1; + } + + if (!(e = avahi_new(Entry, 1))) + return -1; + + e->hashmap = m; + e->key = key; + e->value = value; + + AVAHI_LLIST_PREPEND(Entry, entries, m->entries_list, e); + + idx = m->hash_func(key) % HASH_MAP_SIZE; + AVAHI_LLIST_PREPEND(Entry, bucket, m->entries[idx], e); + + return 0; +} + +void avahi_hashmap_remove(AvahiHashmap *m, const void *key) { + Entry *e; + + assert(m); + + if (!(e = entry_get(m, key))) + return; + + entry_free(m, e, 0); +} + +void avahi_hashmap_foreach(AvahiHashmap *m, AvahiHashmapForeachCallback callback, void *userdata) { + Entry *e, *next; + assert(m); + assert(callback); + + for (e = m->entries_list; e; e = next) { + next = e->entries_next; + + callback(e->key, e->value, userdata); + } +} + +unsigned avahi_string_hash(const void *data) { + const char *p = data; + unsigned hash = 0; + + assert(p); + + for (; *p; p++) + hash = 31 * hash + *p; + + return hash; +} + +int avahi_string_equal(const void *a, const void *b) { + const char *p = a, *q = b; + + assert(p); + assert(q); + + return strcmp(p, q) == 0; +} + +unsigned avahi_int_hash(const void *data) { + const int *i = data; + + assert(i); + + return (unsigned) *i; +} + +int avahi_int_equal(const void *a, const void *b) { + const int *_a = a, *_b = b; + + assert(_a); + assert(_b); + + return *_a == *_b; +} diff --git a/3rdparty/QtZeroConf/avahi-core/hashmap.h b/3rdparty/QtZeroConf/avahi-core/hashmap.h new file mode 100644 index 000000000..9d7e81fc2 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/hashmap.h @@ -0,0 +1,53 @@ +#ifndef foohashmaphfoo +#define foohashmaphfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +AVAHI_C_DECL_BEGIN + +typedef struct AvahiHashmap AvahiHashmap; + +typedef unsigned (*AvahiHashFunc)(const void *data); +typedef int (*AvahiEqualFunc)(const void *a, const void *b); +typedef void (*AvahiFreeFunc)(void *p); + +AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_func, AvahiFreeFunc key_free_func, AvahiFreeFunc value_free_func); + +void avahi_hashmap_free(AvahiHashmap *m); +void* avahi_hashmap_lookup(AvahiHashmap *m, const void *key); +int avahi_hashmap_insert(AvahiHashmap *m, void *key, void *value); +int avahi_hashmap_replace(AvahiHashmap *m, void *key, void *value); +void avahi_hashmap_remove(AvahiHashmap *m, const void *key); + +typedef void (*AvahiHashmapForeachCallback)(void *key, void *value, void *userdata); + +void avahi_hashmap_foreach(AvahiHashmap *m, AvahiHashmapForeachCallback callback, void *userdata); + +unsigned avahi_string_hash(const void *data); +int avahi_string_equal(const void *a, const void *b); + +unsigned avahi_int_hash(const void *data); +int avahi_int_equal(const void *a, const void *b); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/iface-linux.c b/3rdparty/QtZeroConf/avahi-core/iface-linux.c new file mode 100644 index 000000000..c6c5f77ca --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/iface-linux.c @@ -0,0 +1,391 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include + +#include "log.h" +#include "iface.h" +#include "iface-linux.h" + +#ifndef IFLA_RTA +#include +#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#endif + +#ifndef IFA_RTA +#include +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#endif + +static int netlink_list_items(AvahiNetlink *nl, uint16_t type, unsigned *ret_seq) { + struct nlmsghdr *n; + struct rtgenmsg *gen; + uint8_t req[1024]; + + /* Issue a wild dump NETLINK request */ + + memset(&req, 0, sizeof(req)); + n = (struct nlmsghdr*) req; + n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); + n->nlmsg_type = type; + n->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + n->nlmsg_pid = 0; + + gen = NLMSG_DATA(n); + memset(gen, 0, sizeof(struct rtgenmsg)); + gen->rtgen_family = AF_UNSPEC; + + return avahi_netlink_send(nl, n, ret_seq); +} + +static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdata) { + AvahiInterfaceMonitor *m = userdata; + + /* This routine is called for every RTNETLINK response packet */ + + assert(m); + assert(n); + assert(m->osdep.netlink == nl); + + if (n->nlmsg_type == RTM_NEWLINK) { + + /* A new interface appeared or an existing one has been modified */ + + struct ifinfomsg *ifinfomsg = NLMSG_DATA(n); + AvahiHwInterface *hw; + struct rtattr *a = NULL; + size_t l; + + /* A (superfluous?) sanity check */ + if (ifinfomsg->ifi_family != AF_UNSPEC) + return; + + /* Check whether there already is an AvahiHwInterface object + * for this link, so that we can update its data. Note that + * Netlink sends us an RTM_NEWLINK not only when a new + * interface appears, but when it changes, too */ + + if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index))) + + /* No object found, so let's create a new + * one. avahi_hw_interface_new() will call + * avahi_interface_new() internally twice for IPv4 and + * IPv6, so there is no need for us to do that + * ourselves */ + if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifinfomsg->ifi_index))) + return; /* OOM */ + + /* Check whether the flags of this interface are OK for us */ + hw->flags_ok = + (ifinfomsg->ifi_flags & IFF_UP) && + (!m->server->config.use_iff_running || (ifinfomsg->ifi_flags & IFF_RUNNING)) && + !(ifinfomsg->ifi_flags & IFF_LOOPBACK) && + (ifinfomsg->ifi_flags & IFF_MULTICAST) && + (m->server->config.allow_point_to_point || !(ifinfomsg->ifi_flags & IFF_POINTOPOINT)); + + /* Handle interface attributes */ + l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg)); + a = IFLA_RTA(ifinfomsg); + + while (RTA_OK(a, l)) { + switch(a->rta_type) { + case IFLA_IFNAME: + + /* Fill in interface name */ + avahi_free(hw->name); + hw->name = avahi_strndup(RTA_DATA(a), RTA_PAYLOAD(a)); + break; + + case IFLA_MTU: + + /* Fill in MTU */ + assert(RTA_PAYLOAD(a) == sizeof(unsigned int)); + hw->mtu = *((unsigned int*) RTA_DATA(a)); + break; + + case IFLA_ADDRESS: + + /* Fill in hardware (MAC) address */ + hw->mac_address_size = RTA_PAYLOAD(a); + if (hw->mac_address_size > AVAHI_MAC_ADDRESS_MAX) + hw->mac_address_size = AVAHI_MAC_ADDRESS_MAX; + + memcpy(hw->mac_address, RTA_DATA(a), hw->mac_address_size); + break; + + default: + ; + } + + a = RTA_NEXT(a, l); + } + + /* Check whether this interface is now "relevant" for us. If + * it is Avahi will start to announce its records on this + * interface and send out queries for subscribed records on + * it */ + avahi_hw_interface_check_relevant(hw); + + /* Update any associated RRs of this interface. (i.e. the + * _workstation._tcp record containing the MAC address) */ + avahi_hw_interface_update_rrs(hw, 0); + + } else if (n->nlmsg_type == RTM_DELLINK) { + + /* An interface has been removed */ + + struct ifinfomsg *ifinfomsg = NLMSG_DATA(n); + AvahiHwInterface *hw; + + /* A (superfluous?) sanity check */ + if (ifinfomsg->ifi_family != AF_UNSPEC) + return; + + /* Get a reference to our AvahiHwInterface object of this interface */ + if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifinfomsg->ifi_index))) + return; + + /* Free our object */ + avahi_hw_interface_free(hw, 0); + + } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) { + + /* An address has been added, modified or removed */ + + struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n); + AvahiInterface *i; + struct rtattr *a = NULL; + size_t l; + AvahiAddress raddr, rlocal, *r; + int raddr_valid = 0, rlocal_valid = 0; + + /* We are only interested in IPv4 and IPv6 */ + if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6) + return; + + /* Try to get a reference to our AvahiInterface object for the + * interface this address is assigned to. If ther is no object + * for this interface, we ignore this address. */ + if (!(i = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifaddrmsg->ifa_index, avahi_af_to_proto(ifaddrmsg->ifa_family)))) + return; + + /* Fill in address family for our new address */ + rlocal.proto = raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family); + + l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg)); + a = IFA_RTA(ifaddrmsg); + + while (RTA_OK(a, l)) { + + switch(a->rta_type) { + + case IFA_LOCAL: + + if ((rlocal.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) || + (rlocal.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4)) + return; + + memcpy(rlocal.data.data, RTA_DATA(a), RTA_PAYLOAD(a)); + rlocal_valid = 1; + + break; + + case IFA_ADDRESS: + + /* Fill in local address data. Usually this is + * preferable over IFA_ADDRESS if both are set, + * since this refers to the local address of a PPP + * link while IFA_ADDRESS refers to the other + * end. */ + + if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) || + (raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4)) + return; + + memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a)); + raddr_valid = 1; + + break; + + default: + ; + } + + a = RTA_NEXT(a, l); + } + + /* If there was no adress attached to this message, let's quit. */ + if (rlocal_valid) + r = &rlocal; + else if (raddr_valid) + r = &raddr; + else + return; + + if (n->nlmsg_type == RTM_NEWADDR) { + AvahiInterfaceAddress *addr; + + /* This address is new or has been modified, so let's get an object for it */ + if (!(addr = avahi_interface_monitor_get_address(m, i, r))) + + /* Mmm, no object existing yet, so let's create a new one */ + if (!(addr = avahi_interface_address_new(m, i, r, ifaddrmsg->ifa_prefixlen))) + return; /* OOM */ + + /* Update the scope field for the address */ + addr->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE; + addr->deprecated = !!(ifaddrmsg->ifa_flags & IFA_F_DEPRECATED); + } else { + AvahiInterfaceAddress *addr; + assert(n->nlmsg_type == RTM_DELADDR); + + /* Try to get a reference to our AvahiInterfaceAddress object for this address */ + if (!(addr = avahi_interface_monitor_get_address(m, i, r))) + return; + + /* And free it */ + avahi_interface_address_free(addr); + } + + /* Avahi only considers interfaces with at least one address + * attached relevant. Since we migh have added or removed an + * address, let's have it check again whether the interface is + * now relevant */ + avahi_interface_check_relevant(i); + + /* Update any associated RRs, like A or AAAA for our new/removed address */ + avahi_interface_update_rrs(i, 0); + + } else if (n->nlmsg_type == NLMSG_DONE) { + + /* This wild dump request ended, so let's see what we do next */ + + if (m->osdep.list == LIST_IFACE) { + + /* Mmmm, interfaces have been wild dumped already, so + * let's go on with wild dumping the addresses */ + + if (netlink_list_items(m->osdep.netlink, RTM_GETADDR, &m->osdep.query_addr_seq) < 0) { + avahi_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno)); + m->osdep.list = LIST_DONE; + } else + + /* Update state information */ + m->osdep.list = LIST_ADDR; + + } else + /* We're done. Tell avahi_interface_monitor_sync() to finish. */ + m->osdep.list = LIST_DONE; + + if (m->osdep.list == LIST_DONE) { + + /* Only after this boolean variable has been set, Avahi + * will start to announce or browse on all interfaces. It + * is originaly set to 0, which means that relevancy + * checks and RR updates are disabled during the wild + * dumps. */ + m->list_complete = 1; + + /* So let's check if any interfaces are relevant now */ + avahi_interface_monitor_check_relevant(m); + + /* And update all RRs attached to any interface */ + avahi_interface_monitor_update_rrs(m, 0); + + /* Tell the user that the wild dump is complete */ + avahi_log_info("Network interface enumeration completed."); + } + + } else if (n->nlmsg_type == NLMSG_ERROR && + (n->nlmsg_seq == m->osdep.query_link_seq || n->nlmsg_seq == m->osdep.query_addr_seq)) { + struct nlmsgerr *e = NLMSG_DATA (n); + + /* Some kind of error happened. Let's just tell the user and + * ignore it otherwise */ + + if (e->error) + avahi_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error)); + } +} + +int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) { + assert(m); + + /* Initialize our own data */ + + m->osdep.netlink = NULL; + m->osdep.query_addr_seq = m->osdep.query_link_seq = 0; + + /* Create a netlink object for us. It abstracts some things and + * makes netlink easier to use. It will attach to the main loop + * for us and call netlink_callback() whenever an event + * happens. */ + if (!(m->osdep.netlink = avahi_netlink_new(m->server->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m))) + goto fail; + + /* Set the initial state. */ + m->osdep.list = LIST_IFACE; + + /* Start the wild dump for the interfaces */ + if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0) + goto fail; + + return 0; + +fail: + + if (m->osdep.netlink) { + avahi_netlink_free(m->osdep.netlink); + m->osdep.netlink = NULL; + } + + return -1; +} + +void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) { + assert(m); + + if (m->osdep.netlink) { + avahi_netlink_free(m->osdep.netlink); + m->osdep.netlink = NULL; + } +} + +void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { + assert(m); + + /* Let's handle netlink events until we are done with wild + * dumping */ + + while (!m->list_complete) + if (!avahi_netlink_work(m->osdep.netlink, 1) == 0) + break; + + /* At this point Avahi knows about all local interfaces and + * addresses in existance. */ +} diff --git a/3rdparty/QtZeroConf/avahi-core/iface-linux.h b/3rdparty/QtZeroConf/avahi-core/iface-linux.h new file mode 100644 index 000000000..677f86dd7 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/iface-linux.h @@ -0,0 +1,40 @@ +#ifndef fooifacelinuxhfoo +#define fooifacelinuxhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep; + +#include "netlink.h" + +struct AvahiInterfaceMonitorOSDep { + AvahiNetlink *netlink; + + unsigned query_addr_seq, query_link_seq; + + enum { + LIST_IFACE, + LIST_ADDR, + LIST_DONE + } list; +}; + + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/iface-pfroute.h b/3rdparty/QtZeroConf/avahi-core/iface-pfroute.h new file mode 100644 index 000000000..3766cb06c --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/iface-pfroute.h @@ -0,0 +1,37 @@ +#ifndef fooifacepfroutehfoo +#define fooifacepfroutehfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ +#include + +typedef struct AvahiPfRoute AvahiPfRoute; +struct AvahiPfRoute { + int fd; + AvahiWatch *watch; + AvahiInterfaceMonitor *m; +}; + +typedef struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep; + +struct AvahiInterfaceMonitorOSDep { + AvahiPfRoute *pfroute; +}; + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/iface.c b/3rdparty/QtZeroConf/avahi-core/iface.c new file mode 100644 index 000000000..39a860ae7 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/iface.c @@ -0,0 +1,865 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "iface.h" +#include "dns.h" +#include "socket.h" +#include "announce.h" +#include "util.h" +#include "log.h" +#include "multicast-lookup.h" +#include "querier.h" + +void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs) { + AvahiInterfaceMonitor *m; + + assert(a); + m = a->monitor; + + if (m->list_complete && + avahi_interface_address_is_relevant(a) && + avahi_interface_is_relevant(a->interface) && + !remove_rrs && + m->server->config.publish_addresses && + (m->server->state == AVAHI_SERVER_RUNNING || + m->server->state == AVAHI_SERVER_REGISTERING)) { + + /* Fill the entry group */ + if (!a->entry_group) + a->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL); + + if (!a->entry_group) /* OOM */ + return; + + if (avahi_s_entry_group_is_empty(a->entry_group)) { + char t[AVAHI_ADDRESS_STR_MAX]; + AvahiProtocol p; + + p = (a->interface->protocol == AVAHI_PROTO_INET && m->server->config.publish_a_on_ipv6) || + (a->interface->protocol == AVAHI_PROTO_INET6 && m->server->config.publish_aaaa_on_ipv4) ? AVAHI_PROTO_UNSPEC : a->interface->protocol; + + avahi_address_snprint(t, sizeof(t), &a->address); + avahi_log_info("Registering new address record for %s on %s.%s.", t, a->interface->hardware->name, p == AVAHI_PROTO_UNSPEC ? "*" : avahi_proto_to_string(p)); + + if (avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, p, 0, NULL, &a->address) < 0) { + avahi_log_warn(__FILE__": avahi_server_add_address() failed: %s", avahi_strerror(m->server->error)); + avahi_s_entry_group_free(a->entry_group); + a->entry_group = NULL; + return; + } + + avahi_s_entry_group_commit(a->entry_group); + } + } else { + + /* Clear the entry group */ + + if (a->entry_group && !avahi_s_entry_group_is_empty(a->entry_group)) { + char t[AVAHI_ADDRESS_STR_MAX]; + avahi_address_snprint(t, sizeof(t), &a->address); + + avahi_log_info("Withdrawing address record for %s on %s.", t, a->interface->hardware->name); + + if (avahi_s_entry_group_get_state(a->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING && + m->server->state == AVAHI_SERVER_REGISTERING) + avahi_server_decrease_host_rr_pending(m->server); + + avahi_s_entry_group_reset(a->entry_group); + } + } +} + +void avahi_interface_update_rrs(AvahiInterface *i, int remove_rrs) { + AvahiInterfaceAddress *a; + + assert(i); + + for (a = i->addresses; a; a = a->address_next) + avahi_interface_address_update_rrs(a, remove_rrs); +} + +void avahi_hw_interface_update_rrs(AvahiHwInterface *hw, int remove_rrs) { + AvahiInterface *i; + AvahiInterfaceMonitor *m; + + assert(hw); + m = hw->monitor; + + for (i = hw->interfaces; i; i = i->by_hardware_next) + avahi_interface_update_rrs(i, remove_rrs); + + if (m->list_complete && + !remove_rrs && + m->server->config.publish_workstation && + (m->server->state == AVAHI_SERVER_RUNNING)) { + + if (!hw->entry_group) + hw->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL); + + if (!hw->entry_group) + return; /* OOM */ + + if (avahi_s_entry_group_is_empty(hw->entry_group)) { + char name[AVAHI_LABEL_MAX], unescaped[AVAHI_LABEL_MAX], mac[256]; + const char *p = m->server->host_name; + + avahi_unescape_label(&p, unescaped, sizeof(unescaped)); + avahi_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size); + snprintf(name, sizeof(name), "%s [%s]", unescaped, mac); + + if (avahi_server_add_service(m->server, hw->entry_group, hw->index, AVAHI_PROTO_UNSPEC, 0, name, "_workstation._tcp", NULL, NULL, 9, NULL) < 0) { + avahi_log_warn(__FILE__": avahi_server_add_service() failed: %s", avahi_strerror(m->server->error)); + avahi_s_entry_group_free(hw->entry_group); + hw->entry_group = NULL; + } else + avahi_s_entry_group_commit(hw->entry_group); + } + + } else { + + if (hw->entry_group && !avahi_s_entry_group_is_empty(hw->entry_group)) { + + avahi_log_info("Withdrawing workstation service for %s.", hw->name); + + if (avahi_s_entry_group_get_state(hw->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING && + m->server->state == AVAHI_SERVER_REGISTERING) + avahi_server_decrease_host_rr_pending(m->server); + + avahi_s_entry_group_reset(hw->entry_group); + } + } +} + +void avahi_interface_monitor_update_rrs(AvahiInterfaceMonitor *m, int remove_rrs) { + AvahiHwInterface *hw; + + assert(m); + + for (hw = m->hw_interfaces; hw; hw = hw->hardware_next) + avahi_hw_interface_update_rrs(hw, remove_rrs); +} + +static int interface_mdns_mcast_join(AvahiInterface *i, int join) { + char at[AVAHI_ADDRESS_STR_MAX]; + int r; + assert(i); + + if (!!join == !!i->mcast_joined) + return 0; + + if ((i->protocol == AVAHI_PROTO_INET6 && i->monitor->server->fd_ipv6 < 0) || + (i->protocol == AVAHI_PROTO_INET && i->monitor->server->fd_ipv4 < 0)) + return -1; + + if (join) { + AvahiInterfaceAddress *a; + + /* Look if there's an address with global scope */ + for (a = i->addresses; a; a = a->address_next) + if (a->global_scope) + break; + + /* No address with a global scope has been found, so let's use + * any. */ + if (!a) + a = i->addresses; + + /* Hmm, there is no address available. */ + if (!a) + return -1; + + i->local_mcast_address = a->address; + } + + avahi_log_info("%s mDNS multicast group on interface %s.%s with address %s.", + join ? "Joining" : "Leaving", + i->hardware->name, + avahi_proto_to_string(i->protocol), + avahi_address_snprint(at, sizeof(at), &i->local_mcast_address)); + + if (i->protocol == AVAHI_PROTO_INET6) + r = avahi_mdns_mcast_join_ipv6(i->monitor->server->fd_ipv6, &i->local_mcast_address.data.ipv6, i->hardware->index, join); + else { + assert(i->protocol == AVAHI_PROTO_INET); + + r = avahi_mdns_mcast_join_ipv4(i->monitor->server->fd_ipv4, &i->local_mcast_address.data.ipv4, i->hardware->index, join); + } + + if (r < 0) + i->mcast_joined = 0; + else + i->mcast_joined = join; + + return 0; +} + +static int interface_mdns_mcast_rejoin(AvahiInterface *i) { + AvahiInterfaceAddress *a, *usable = NULL, *found = NULL; + assert(i); + + if (!i->mcast_joined) + return 0; + + /* Check whether old address we joined with is still available. If + * not, rejoin using an other address. */ + + for (a = i->addresses; a; a = a->address_next) { + if (a->global_scope && !usable) + usable = a; + + if (avahi_address_cmp(&a->address, &i->local_mcast_address) == 0) { + + if (a->global_scope) + /* No action necessary: the address still exists and + * has global scope. */ + return 0; + + found = a; + } + } + + if (found && !usable) + /* No action necessary: the address still exists and no better one has been found */ + return 0; + + interface_mdns_mcast_join(i, 0); + return interface_mdns_mcast_join(i, 1); +} + +void avahi_interface_address_free(AvahiInterfaceAddress *a) { + assert(a); + assert(a->interface); + + avahi_interface_address_update_rrs(a, 1); + AVAHI_LLIST_REMOVE(AvahiInterfaceAddress, address, a->interface->addresses, a); + + if (a->entry_group) + avahi_s_entry_group_free(a->entry_group); + + interface_mdns_mcast_rejoin(a->interface); + + avahi_free(a); +} + +void avahi_interface_free(AvahiInterface *i, int send_goodbye) { + assert(i); + + /* Handle goodbyes and remove announcers */ + avahi_goodbye_interface(i->monitor->server, i, send_goodbye, 1); + avahi_response_scheduler_force(i->response_scheduler); + assert(!i->announcers); + + if (i->mcast_joined) + interface_mdns_mcast_join(i, 0); + + /* Remove queriers */ + avahi_querier_free_all(i); + avahi_hashmap_free(i->queriers_by_key); + + /* Remove local RRs */ + avahi_interface_update_rrs(i, 1); + + while (i->addresses) + avahi_interface_address_free(i->addresses); + + avahi_response_scheduler_free(i->response_scheduler); + avahi_query_scheduler_free(i->query_scheduler); + avahi_probe_scheduler_free(i->probe_scheduler); + avahi_cache_free(i->cache); + + AVAHI_LLIST_REMOVE(AvahiInterface, interface, i->monitor->interfaces, i); + AVAHI_LLIST_REMOVE(AvahiInterface, by_hardware, i->hardware->interfaces, i); + + avahi_free(i); +} + +void avahi_hw_interface_free(AvahiHwInterface *hw, int send_goodbye) { + assert(hw); + + avahi_hw_interface_update_rrs(hw, 1); + + while (hw->interfaces) + avahi_interface_free(hw->interfaces, send_goodbye); + + if (hw->entry_group) + avahi_s_entry_group_free(hw->entry_group); + + AVAHI_LLIST_REMOVE(AvahiHwInterface, hardware, hw->monitor->hw_interfaces, hw); + avahi_hashmap_remove(hw->monitor->hashmap, &hw->index); + + avahi_free(hw->name); + avahi_free(hw); +} + +AvahiInterface* avahi_interface_new(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiProtocol protocol) { + AvahiInterface *i; + + assert(m); + assert(hw); + assert(AVAHI_PROTO_VALID(protocol)); + + if (!(i = avahi_new(AvahiInterface, 1))) + goto fail; /* OOM */ + + i->monitor = m; + i->hardware = hw; + i->protocol = protocol; + i->announcing = 0; + i->mcast_joined = 0; + + AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses); + AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, i->announcers); + + AVAHI_LLIST_HEAD_INIT(AvahiQuerier, i->queriers); + i->queriers_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); + + i->cache = avahi_cache_new(m->server, i); + i->response_scheduler = avahi_response_scheduler_new(i); + i->query_scheduler = avahi_query_scheduler_new(i); + i->probe_scheduler = avahi_probe_scheduler_new(i); + + if (!i->cache || !i->response_scheduler || !i->query_scheduler || !i->probe_scheduler) + goto fail; /* OOM */ + + AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i); + AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i); + + return i; + +fail: + + if (i) { + if (i->cache) + avahi_cache_free(i->cache); + if (i->response_scheduler) + avahi_response_scheduler_free(i->response_scheduler); + if (i->query_scheduler) + avahi_query_scheduler_free(i->query_scheduler); + if (i->probe_scheduler) + avahi_probe_scheduler_free(i->probe_scheduler); + } + + return NULL; +} + +AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex idx) { + AvahiHwInterface *hw; + + assert(m); + assert(AVAHI_IF_VALID(idx)); + + if (!(hw = avahi_new(AvahiHwInterface, 1))) + return NULL; + + hw->monitor = m; + hw->name = NULL; + hw->flags_ok = 0; + hw->mtu = 1500; + hw->index = idx; + hw->mac_address_size = 0; + hw->entry_group = NULL; + hw->ratelimit_begin.tv_sec = 0; + hw->ratelimit_begin.tv_usec = 0; + hw->ratelimit_counter = 0; + + AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces); + AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw); + + avahi_hashmap_insert(m->hashmap, &hw->index, hw); + + if (m->server->fd_ipv4 >= 0 || m->server->config.publish_a_on_ipv6) + avahi_interface_new(m, hw, AVAHI_PROTO_INET); + if (m->server->fd_ipv6 >= 0 || m->server->config.publish_aaaa_on_ipv4) + avahi_interface_new(m, hw, AVAHI_PROTO_INET6); + + return hw; +} + +AvahiInterfaceAddress *avahi_interface_address_new(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *addr, unsigned prefix_len) { + AvahiInterfaceAddress *a; + + assert(m); + assert(i); + + if (!(a = avahi_new(AvahiInterfaceAddress, 1))) + return NULL; + + a->interface = i; + a->monitor = m; + a->address = *addr; + a->prefix_len = prefix_len; + a->global_scope = 0; + a->deprecated = 0; + a->entry_group = NULL; + + AVAHI_LLIST_PREPEND(AvahiInterfaceAddress, address, i->addresses, a); + + return a; +} + +void avahi_interface_check_relevant(AvahiInterface *i) { + int b; + AvahiInterfaceMonitor *m; + + assert(i); + m = i->monitor; + + b = avahi_interface_is_relevant(i); + + if (m->list_complete && b && !i->announcing) { + interface_mdns_mcast_join(i, 1); + + if (i->mcast_joined) { + avahi_log_info("New relevant interface %s.%s for mDNS.", i->hardware->name, avahi_proto_to_string(i->protocol)); + + i->announcing = 1; + avahi_announce_interface(m->server, i); + avahi_multicast_lookup_engine_new_interface(m->server->multicast_lookup_engine, i); + } + + } else if (!b && i->announcing) { + avahi_log_info("Interface %s.%s no longer relevant for mDNS.", i->hardware->name, avahi_proto_to_string(i->protocol)); + + interface_mdns_mcast_join(i, 0); + + avahi_goodbye_interface(m->server, i, 0, 1); + avahi_querier_free_all(i); + + avahi_response_scheduler_clear(i->response_scheduler); + avahi_query_scheduler_clear(i->query_scheduler); + avahi_probe_scheduler_clear(i->probe_scheduler); + avahi_cache_flush(i->cache); + + i->announcing = 0; + + } else + interface_mdns_mcast_rejoin(i); +} + +void avahi_hw_interface_check_relevant(AvahiHwInterface *hw) { + AvahiInterface *i; + + assert(hw); + + for (i = hw->interfaces; i; i = i->by_hardware_next) + avahi_interface_check_relevant(i); +} + +void avahi_interface_monitor_check_relevant(AvahiInterfaceMonitor *m) { + AvahiInterface *i; + + assert(m); + + for (i = m->interfaces; i; i = i->interface_next) + avahi_interface_check_relevant(i); +} + +AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *s) { + AvahiInterfaceMonitor *m = NULL; + + if (!(m = avahi_new0(AvahiInterfaceMonitor, 1))) + return NULL; /* OOM */ + + m->server = s; + m->list_complete = 0; + m->hashmap = avahi_hashmap_new(avahi_int_hash, avahi_int_equal, NULL, NULL); + + AVAHI_LLIST_HEAD_INIT(AvahiInterface, m->interfaces); + AVAHI_LLIST_HEAD_INIT(AvahiHwInterface, m->hw_interfaces); + + if (avahi_interface_monitor_init_osdep(m) < 0) + goto fail; + + return m; + +fail: + avahi_interface_monitor_free(m); + return NULL; +} + +void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) { + assert(m); + + while (m->hw_interfaces) + avahi_hw_interface_free(m->hw_interfaces, 1); + + assert(!m->interfaces); + + avahi_interface_monitor_free_osdep(m); + + if (m->hashmap) + avahi_hashmap_free(m->hashmap); + + avahi_free(m); +} + + +AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol) { + AvahiHwInterface *hw; + AvahiInterface *i; + + assert(m); + assert(idx >= 0); + assert(protocol != AVAHI_PROTO_UNSPEC); + + if (!(hw = avahi_interface_monitor_get_hw_interface(m, idx))) + return NULL; + + for (i = hw->interfaces; i; i = i->by_hardware_next) + if (i->protocol == protocol) + return i; + + return NULL; +} + +AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx) { + assert(m); + assert(idx >= 0); + + return avahi_hashmap_lookup(m->hashmap, &idx); +} + +AvahiInterfaceAddress* avahi_interface_monitor_get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr) { + AvahiInterfaceAddress *ia; + + assert(m); + assert(i); + assert(raddr); + + for (ia = i->addresses; ia; ia = ia->address_next) + if (avahi_address_cmp(&ia->address, raddr) == 0) + return ia; + + return NULL; +} + +void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port) { + assert(i); + assert(p); + + if (!i->announcing) + return; + + assert(!a || a->proto == i->protocol); + + if (i->monitor->server->config.ratelimit_interval > 0) { + struct timeval now, end; + + gettimeofday(&now, NULL); + + end = i->hardware->ratelimit_begin; + avahi_timeval_add(&end, i->monitor->server->config.ratelimit_interval); + + if (i->hardware->ratelimit_begin.tv_sec <= 0 || + avahi_timeval_compare(&end, &now) < 0) { + + i->hardware->ratelimit_begin = now; + i->hardware->ratelimit_counter = 0; + } + + if (i->hardware->ratelimit_counter > i->monitor->server->config.ratelimit_burst) + return; + + i->hardware->ratelimit_counter++; + } + + if (i->protocol == AVAHI_PROTO_INET && i->monitor->server->fd_ipv4 >= 0) + avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv4 : NULL, a ? &a->data.ipv4 : NULL, port); + else if (i->protocol == AVAHI_PROTO_INET6 && i->monitor->server->fd_ipv6 >= 0) + avahi_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv6 : NULL, a ? &a->data.ipv6 : NULL, port); +} + +void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) { + assert(i); + assert(p); + + avahi_interface_send_packet_unicast(i, p, NULL, 0); +} + +int avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, int immediately, unsigned *ret_id) { + assert(i); + assert(key); + + if (!i->announcing) + return 0; + + return avahi_query_scheduler_post(i->query_scheduler, key, immediately, ret_id); +} + +int avahi_interface_withraw_query(AvahiInterface *i, unsigned id) { + + return avahi_query_scheduler_withdraw_by_id(i->query_scheduler, id); +} + +int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) { + assert(i); + assert(record); + + if (!i->announcing) + return 0; + + return avahi_response_scheduler_post(i->response_scheduler, record, flush_cache, querier, immediately); +} + +int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *record, int immediately) { + assert(i); + assert(record); + + if (!i->announcing) + return 0; + + return avahi_probe_scheduler_post(i->probe_scheduler, record, immediately); +} + +int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata) { + AvahiInterface *i; + assert(m); + + for (i = m->interfaces; i; i = i->interface_next) { + if (avahi_interface_is_relevant(i)) { + char ln[256]; + snprintf(ln, sizeof(ln), ";;; INTERFACE %s.%s ;;;", i->hardware->name, avahi_proto_to_string(i->protocol)); + callback(ln, userdata); + if (avahi_cache_dump(i->cache, callback, userdata) < 0) + return -1; + } + } + + return 0; +} + +static int avahi_interface_is_relevant_internal(AvahiInterface *i) { + AvahiInterfaceAddress *a; + + assert(i); + + if (!i->hardware->flags_ok) + return 0; + + for (a = i->addresses; a; a = a->address_next) + if (avahi_interface_address_is_relevant(a)) + return 1; + + return 0; +} + +int avahi_interface_is_relevant(AvahiInterface *i) { + AvahiStringList *l; + assert(i); + + for (l = i->monitor->server->config.deny_interfaces; l; l = l->next) + if (strcasecmp((char*) l->text, i->hardware->name) == 0) + return 0; + + if (i->monitor->server->config.allow_interfaces) { + + for (l = i->monitor->server->config.allow_interfaces; l; l = l->next) + if (strcasecmp((char*) l->text, i->hardware->name) == 0) + goto good; + + return 0; + } + +good: + return avahi_interface_is_relevant_internal(i); +} + +int avahi_interface_address_is_relevant(AvahiInterfaceAddress *a) { + AvahiInterfaceAddress *b; + assert(a); + + /* Publish public and non-deprecated IP addresses */ + if (a->global_scope && !a->deprecated) + return 1; + + /* Publish link-local and deprecated IP addresses only if they are + * the only ones on the link */ + for (b = a->interface->addresses; b; b = b->address_next) { + if (b == a) + continue; + + if (b->global_scope && !b->deprecated) + return 0; + } + + return 1; +} + +int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol) { + assert(i); + + if (idx != AVAHI_IF_UNSPEC && idx != i->hardware->index) + return 0; + + if (protocol != AVAHI_PROTO_UNSPEC && protocol != i->protocol) + return 0; + + return 1; +} + +void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex interface, AvahiProtocol protocol, AvahiInterfaceMonitorWalkCallback callback, void* userdata) { + assert(m); + assert(callback); + + if (interface != AVAHI_IF_UNSPEC) { + if (protocol != AVAHI_PROTO_UNSPEC) { + AvahiInterface *i; + + if ((i = avahi_interface_monitor_get_interface(m, interface, protocol))) + callback(m, i, userdata); + + } else { + AvahiHwInterface *hw; + AvahiInterface *i; + + if ((hw = avahi_interface_monitor_get_hw_interface(m, interface))) + for (i = hw->interfaces; i; i = i->by_hardware_next) + if (avahi_interface_match(i, interface, protocol)) + callback(m, i, userdata); + } + + } else { + AvahiInterface *i; + + for (i = m->interfaces; i; i = i->interface_next) + if (avahi_interface_match(i, interface, protocol)) + callback(m, i, userdata); + } +} + + +int avahi_address_is_local(AvahiInterfaceMonitor *m, const AvahiAddress *a) { + AvahiInterface *i; + AvahiInterfaceAddress *ia; + assert(m); + assert(a); + + for (i = m->interfaces; i; i = i->interface_next) + for (ia = i->addresses; ia; ia = ia->address_next) + if (avahi_address_cmp(a, &ia->address) == 0) + return 1; + + return 0; +} + +int avahi_interface_address_on_link(AvahiInterface *i, const AvahiAddress *a) { + AvahiInterfaceAddress *ia; + + assert(i); + assert(a); + + if (a->proto != i->protocol) + return 0; + + for (ia = i->addresses; ia; ia = ia->address_next) { + + if (a->proto == AVAHI_PROTO_INET) { + uint32_t m; + + m = ~(((uint32_t) -1) >> ia->prefix_len); + + if ((ntohl(a->data.ipv4.address) & m) == (ntohl(ia->address.data.ipv4.address) & m)) + return 1; + } else { + unsigned j; + unsigned char pl; + assert(a->proto == AVAHI_PROTO_INET6); + + pl = ia->prefix_len; + + for (j = 0; j < 16; j++) { + uint8_t m; + + if (pl == 0) + return 1; + + if (pl >= 8) { + m = 0xFF; + pl -= 8; + } else { + m = ~(0xFF >> pl); + pl = 0; + } + + if ((a->data.ipv6.address[j] & m) != (ia->address.data.ipv6.address[j] & m)) + break; + } + } + } + + return 0; +} + +int avahi_interface_has_address(AvahiInterfaceMonitor *m, AvahiIfIndex iface, const AvahiAddress *a) { + AvahiInterface *i; + AvahiInterfaceAddress *j; + + assert(m); + assert(iface != AVAHI_IF_UNSPEC); + assert(a); + + if (!(i = avahi_interface_monitor_get_interface(m, iface, a->proto))) + return 0; + + for (j = i->addresses; j; j = j->address_next) + if (avahi_address_cmp(a, &j->address) == 0) + return 1; + + return 0; +} + +AvahiIfIndex avahi_find_interface_for_address(AvahiInterfaceMonitor *m, const AvahiAddress *a) { + AvahiInterface *i; + assert(m); + + /* Some stupid OS don't support passing the interface index when a + * packet is received. We have to work around that limitation by + * looking for an interface that has the incoming address + * attached. This is sometimes ambiguous, but we have to live with + * it. */ + + for (i = m->interfaces; i; i = i->interface_next) { + AvahiInterfaceAddress *ai; + + if (i->protocol != a->proto) + continue; + + for (ai = i->addresses; ai; ai = ai->address_next) + if (avahi_address_cmp(a, &ai->address) == 0) + return i->hardware->index; + } + + return AVAHI_IF_UNSPEC; +} diff --git a/3rdparty/QtZeroConf/avahi-core/iface.h b/3rdparty/QtZeroConf/avahi-core/iface.h new file mode 100644 index 000000000..c3f24af05 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/iface.h @@ -0,0 +1,195 @@ +#ifndef fooifacehfoo +#define fooifacehfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct AvahiInterfaceMonitor AvahiInterfaceMonitor; +typedef struct AvahiInterfaceAddress AvahiInterfaceAddress; +typedef struct AvahiInterface AvahiInterface; +typedef struct AvahiHwInterface AvahiHwInterface; + +#include +#include + +#include "internal.h" +#include "cache.h" +#include "response-sched.h" +#include "query-sched.h" +#include "probe-sched.h" +#include "dns.h" +#include "announce.h" +#include "browse.h" +#include "querier.h" + +#ifdef HAVE_NETLINK +#include "iface-linux.h" +#elif defined(HAVE_PF_ROUTE) +#include "iface-pfroute.h" +#else +typedef struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep; +struct AvahiInterfaceMonitorOSDep { + + unsigned query_addr_seq, query_link_seq; + + enum { + LIST_IFACE, + LIST_ADDR, + LIST_DONE + } list; +}; +#endif + +#define AVAHI_MAC_ADDRESS_MAX 32 + +struct AvahiInterfaceMonitor { + AvahiServer *server; + AvahiHashmap *hashmap; + + AVAHI_LLIST_HEAD(AvahiInterface, interfaces); + AVAHI_LLIST_HEAD(AvahiHwInterface, hw_interfaces); + + int list_complete; + AvahiInterfaceMonitorOSDep osdep; +}; + +struct AvahiHwInterface { + AvahiInterfaceMonitor *monitor; + + AVAHI_LLIST_FIELDS(AvahiHwInterface, hardware); + + char *name; + AvahiIfIndex index; + int flags_ok; + + unsigned mtu; + + uint8_t mac_address[AVAHI_MAC_ADDRESS_MAX]; + size_t mac_address_size; + + AvahiSEntryGroup *entry_group; + + /* Packet rate limiting */ + struct timeval ratelimit_begin; + unsigned ratelimit_counter; + + AVAHI_LLIST_HEAD(AvahiInterface, interfaces); +}; + +struct AvahiInterface { + AvahiInterfaceMonitor *monitor; + AvahiHwInterface *hardware; + + AVAHI_LLIST_FIELDS(AvahiInterface, interface); + AVAHI_LLIST_FIELDS(AvahiInterface, by_hardware); + + AvahiProtocol protocol; + int announcing; + AvahiAddress local_mcast_address; + int mcast_joined; + + AvahiCache *cache; + + AvahiQueryScheduler *query_scheduler; + AvahiResponseScheduler * response_scheduler; + AvahiProbeScheduler *probe_scheduler; + + AVAHI_LLIST_HEAD(AvahiInterfaceAddress, addresses); + AVAHI_LLIST_HEAD(AvahiAnnouncer, announcers); + + AvahiHashmap *queriers_by_key; + AVAHI_LLIST_HEAD(AvahiQuerier, queriers); +}; + +struct AvahiInterfaceAddress { + AvahiInterfaceMonitor *monitor; + AvahiInterface *interface; + + AVAHI_LLIST_FIELDS(AvahiInterfaceAddress, address); + + AvahiAddress address; + unsigned prefix_len; + + int global_scope; + int deprecated; + + AvahiSEntryGroup *entry_group; +}; + +AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *server); +void avahi_interface_monitor_free(AvahiInterfaceMonitor *m); + +int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m); +void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m); +void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m); + +typedef void (*AvahiInterfaceMonitorWalkCallback)(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata); +void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol, AvahiInterfaceMonitorWalkCallback callback, void* userdata); +int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata); + +void avahi_interface_monitor_update_rrs(AvahiInterfaceMonitor *m, int remove_rrs); +int avahi_address_is_local(AvahiInterfaceMonitor *m, const AvahiAddress *a); +void avahi_interface_monitor_check_relevant(AvahiInterfaceMonitor *m); + +/* AvahiHwInterface */ + +AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex idx); +void avahi_hw_interface_free(AvahiHwInterface *hw, int send_goodbye); + +void avahi_hw_interface_update_rrs(AvahiHwInterface *hw, int remove_rrs); +void avahi_hw_interface_check_relevant(AvahiHwInterface *hw); + +AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, int idx); + +/* AvahiInterface */ + +AvahiInterface* avahi_interface_new(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiProtocol protocol); +void avahi_interface_free(AvahiInterface *i, int send_goodbye); + +void avahi_interface_update_rrs(AvahiInterface *i, int remove_rrs); +void avahi_interface_check_relevant(AvahiInterface *i); +int avahi_interface_is_relevant(AvahiInterface *i); + +void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p); +void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port); + +int avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, int immediately, unsigned *ret_id); +int avahi_interface_withraw_query(AvahiInterface *i, unsigned id); +int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately); +int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *p, int immediately); + +int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol); +int avahi_interface_address_on_link(AvahiInterface *i, const AvahiAddress *a); +int avahi_interface_has_address(AvahiInterfaceMonitor *m, AvahiIfIndex iface, const AvahiAddress *a); + +AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol); + +/* AvahiInterfaceAddress */ + +AvahiInterfaceAddress *avahi_interface_address_new(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *addr, unsigned prefix_len); +void avahi_interface_address_free(AvahiInterfaceAddress *a); + +void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs); +int avahi_interface_address_is_relevant(AvahiInterfaceAddress *a); + +AvahiInterfaceAddress* avahi_interface_monitor_get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr); + +AvahiIfIndex avahi_find_interface_for_address(AvahiInterfaceMonitor *m, const AvahiAddress *a); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/internal.h b/3rdparty/QtZeroConf/avahi-core/internal.h new file mode 100644 index 000000000..b8f930269 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/internal.h @@ -0,0 +1,227 @@ +#ifndef foointernalhfoo +#define foointernalhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** A locally registered DNS resource record */ +typedef struct AvahiEntry AvahiEntry; + +#include +#include +#include + +#include "core.h" +#include "iface.h" +#include "prioq.h" +#include "timeeventq.h" +#include "announce.h" +#include "browse.h" +#include "dns.h" +#include "rrlist.h" +#include "hashmap.h" +#include "wide-area.h" +#include "multicast-lookup.h" +#include "dns-srv-rr.h" + +#define AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX 100 + +#define AVAHI_FLAGS_VALID(flags, max) (!((flags) & ~(max))) + +#define AVAHI_RR_HOLDOFF_MSEC 1000 +#define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 20000 +#define AVAHI_RR_RATE_LIMIT_COUNT 15 + +typedef struct AvahiLegacyUnicastReflectSlot AvahiLegacyUnicastReflectSlot; + +struct AvahiLegacyUnicastReflectSlot { + AvahiServer *server; + + uint16_t id, original_id; + AvahiAddress address; + uint16_t port; + int interface; + struct timeval elapse_time; + AvahiTimeEvent *time_event; +}; + +struct AvahiEntry { + AvahiServer *server; + AvahiSEntryGroup *group; + + int dead; + + AvahiPublishFlags flags; + AvahiRecord *record; + AvahiIfIndex interface; + AvahiProtocol protocol; + + AVAHI_LLIST_FIELDS(AvahiEntry, entries); + AVAHI_LLIST_FIELDS(AvahiEntry, by_key); + AVAHI_LLIST_FIELDS(AvahiEntry, by_group); + + AVAHI_LLIST_HEAD(AvahiAnnouncer, announcers); +}; + +struct AvahiSEntryGroup { + AvahiServer *server; + int dead; + + AvahiEntryGroupState state; + void* userdata; + AvahiSEntryGroupCallback callback; + + unsigned n_probing; + + unsigned n_register_try; + struct timeval register_time; + AvahiTimeEvent *register_time_event; + + struct timeval established_at; + + AVAHI_LLIST_FIELDS(AvahiSEntryGroup, groups); + AVAHI_LLIST_HEAD(AvahiEntry, entries); +}; + +struct AvahiServer { + const AvahiPoll *poll_api; + + AvahiInterfaceMonitor *monitor; + AvahiServerConfig config; + + AVAHI_LLIST_HEAD(AvahiEntry, entries); + AvahiHashmap *entries_by_key; + + AVAHI_LLIST_HEAD(AvahiSEntryGroup, groups); + + AVAHI_LLIST_HEAD(AvahiSRecordBrowser, record_browsers); + AvahiHashmap *record_browser_hashmap; + AVAHI_LLIST_HEAD(AvahiSHostNameResolver, host_name_resolvers); + AVAHI_LLIST_HEAD(AvahiSAddressResolver, address_resolvers); + AVAHI_LLIST_HEAD(AvahiSDomainBrowser, domain_browsers); + AVAHI_LLIST_HEAD(AvahiSServiceTypeBrowser, service_type_browsers); + AVAHI_LLIST_HEAD(AvahiSServiceBrowser, service_browsers); + AVAHI_LLIST_HEAD(AvahiSServiceResolver, service_resolvers); + AVAHI_LLIST_HEAD(AvahiSDNSServerBrowser, dns_server_browsers); + + int need_entry_cleanup, need_group_cleanup, need_browser_cleanup; + + /* Used for scheduling RR cleanup */ + AvahiTimeEvent *cleanup_time_event; + + AvahiTimeEventQueue *time_event_queue; + + char *host_name, *host_name_fqdn, *domain_name; + + int fd_ipv4, fd_ipv6, + /* The following two sockets two are used for reflection only */ + fd_legacy_unicast_ipv4, fd_legacy_unicast_ipv6; + + AvahiWatch *watch_ipv4, *watch_ipv6, + *watch_legacy_unicast_ipv4, *watch_legacy_unicast_ipv6; + + AvahiServerState state; + AvahiServerCallback callback; + void* userdata; + + AvahiSEntryGroup *hinfo_entry_group; + AvahiSEntryGroup *browse_domain_entry_group; + unsigned n_host_rr_pending; + + /* Used for assembling responses */ + AvahiRecordList *record_list; + + /* Used for reflection of legacy unicast packets */ + AvahiLegacyUnicastReflectSlot **legacy_unicast_reflect_slots; + uint16_t legacy_unicast_reflect_id; + + /* The last error code */ + int error; + + /* The local service cookie */ + uint32_t local_service_cookie; + + AvahiMulticastLookupEngine *multicast_lookup_engine; + AvahiWideAreaLookupEngine *wide_area_lookup_engine; +}; + +void avahi_entry_free(AvahiServer*s, AvahiEntry *e); +void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g); + +void avahi_cleanup_dead_entries(AvahiServer *s); + +void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary); +void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response); +void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int is_probe); + +void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state); + +int avahi_entry_is_commited(AvahiEntry *e); + +void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata); + +void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata); + +void avahi_server_decrease_host_rr_pending(AvahiServer *s); + +int avahi_server_set_errno(AvahiServer *s, int error); + +int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name); +int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record); + +int avahi_server_add_ptr( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + uint32_t ttl, + const char *name, + const char *dest); + +#define AVAHI_CHECK_VALIDITY(server, expression, error) { \ + if (!(expression)) \ + return avahi_server_set_errno((server), (error)); \ +} + +#define AVAHI_CHECK_VALIDITY_RETURN_NULL(server, expression, error) { \ + if (!(expression)) { \ + avahi_server_set_errno((server), (error)); \ + return NULL; \ + } \ +} + +#define AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(server, expression, error) {\ + if (!(expression)) { \ + ret = avahi_server_set_errno((server), (error)); \ + goto fail; \ + } \ +} + +#define AVAHI_ASSERT_TRUE(expression) { \ + int __tmp = !!(expression); \ + assert(__tmp); \ +} + +#define AVAHI_ASSERT_SUCCESS(expression) { \ + int __tmp = (expression); \ + assert(__tmp == 0); \ +} + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/log.c b/3rdparty/QtZeroConf/avahi-core/log.c new file mode 100644 index 000000000..d1107657f --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/log.c @@ -0,0 +1,86 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "log.h" + +static AvahiLogFunction log_function = NULL; + +void avahi_set_log_function(AvahiLogFunction function) { + log_function = function; +} + +void avahi_log_ap(AvahiLogLevel level, const char*format, va_list ap) { + char txt[256]; + + vsnprintf(txt, sizeof(txt), format, ap); + + if (log_function) + log_function(level, txt); + else + fprintf(stderr, "%s\n", txt); +} + +void avahi_log(AvahiLogLevel level, const char*format, ...) { + va_list ap; + va_start(ap, format); + avahi_log_ap(level, format, ap); + va_end(ap); +} + +void avahi_log_error(const char*format, ...) { + va_list ap; + va_start(ap, format); + avahi_log_ap(AVAHI_LOG_ERROR, format, ap); + va_end(ap); +} + +void avahi_log_warn(const char*format, ...) { + va_list ap; + va_start(ap, format); + avahi_log_ap(AVAHI_LOG_WARN, format, ap); + va_end(ap); +} + +void avahi_log_notice(const char*format, ...) { + va_list ap; + va_start(ap, format); + avahi_log_ap(AVAHI_LOG_NOTICE, format, ap); + va_end(ap); +} + +void avahi_log_info(const char*format, ...) { + va_list ap; + va_start(ap, format); + avahi_log_ap(AVAHI_LOG_INFO, format, ap); + va_end(ap); +} + +void avahi_log_debug(const char*format, ...) { + va_list ap; + va_start(ap, format); + avahi_log_ap(AVAHI_LOG_DEBUG, format, ap); + va_end(ap); +} diff --git a/3rdparty/QtZeroConf/avahi-core/log.h b/3rdparty/QtZeroConf/avahi-core/log.h new file mode 100644 index 000000000..878f07a46 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/log.h @@ -0,0 +1,73 @@ +#ifndef foologhfoo +#define foologhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include + +/** \file log.h Extensible logging subsystem */ + +AVAHI_C_DECL_BEGIN + +/** Log level for avahi_log_xxx() */ +typedef enum { + AVAHI_LOG_ERROR = 0, /**< Error messages */ + AVAHI_LOG_WARN = 1, /**< Warning messages */ + AVAHI_LOG_NOTICE = 2, /**< Notice messages */ + AVAHI_LOG_INFO = 3, /**< Info messages */ + AVAHI_LOG_DEBUG = 4, /**< Debug messages */ + AVAHI_LOG_LEVEL_MAX +} AvahiLogLevel; + +/** Prototype for a user supplied log function */ +typedef void (*AvahiLogFunction)(AvahiLogLevel level, const char *txt); + +/** Set a user supplied log function, replacing the default which + * prints to log messages unconditionally to STDERR. Pass NULL for + * resetting to the default log function */ +void avahi_set_log_function(AvahiLogFunction function); + +/** Issue a log message using a va_list object */ +void avahi_log_ap(AvahiLogLevel level, const char *format, va_list ap); + +/** Issue a log message by passing a log level and a format string */ +void avahi_log(AvahiLogLevel level, const char*format, ...) AVAHI_GCC_PRINTF_ATTR23; + +/** Shortcut for avahi_log(AVAHI_LOG_ERROR, ...) */ +void avahi_log_error(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; + +/** Shortcut for avahi_log(AVAHI_LOG_WARN, ...) */ +void avahi_log_warn(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; + +/** Shortcut for avahi_log(AVAHI_LOG_NOTICE, ...) */ +void avahi_log_notice(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; + +/** Shortcut for avahi_log(AVAHI_LOG_INFO, ...) */ +void avahi_log_info(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; + +/** Shortcut for avahi_log(AVAHI_LOG_DEBUG, ...) */ +void avahi_log_debug(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12; + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/lookup.h b/3rdparty/QtZeroConf/avahi-core/lookup.h new file mode 100644 index 000000000..06dec6ee2 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/lookup.h @@ -0,0 +1,235 @@ +#ifndef foolookuphfoo +#define foolookuphfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file avahi-core/lookup.h Functions for browsing/resolving services and other RRs */ + +/** \example core-browse-services.c Example how to browse for DNS-SD + * services using an embedded mDNS stack. */ + +/** A browsing object for arbitrary RRs */ +typedef struct AvahiSRecordBrowser AvahiSRecordBrowser; + +/** A host name to IP adddress resolver object */ +typedef struct AvahiSHostNameResolver AvahiSHostNameResolver; + +/** An IP address to host name resolver object ("reverse lookup") */ +typedef struct AvahiSAddressResolver AvahiSAddressResolver; + +/** A local domain browsing object. May be used to enumerate domains used on the local LAN */ +typedef struct AvahiSDomainBrowser AvahiSDomainBrowser; + +/** A DNS-SD service type browsing object. May be used to enumerate the service types of all available services on the local LAN */ +typedef struct AvahiSServiceTypeBrowser AvahiSServiceTypeBrowser; + +/** A DNS-SD service browser. Use this to enumerate available services of a certain kind on the local LAN. Use AvahiSServiceResolver to get specific service data like address and port for a service. */ +typedef struct AvahiSServiceBrowser AvahiSServiceBrowser; + +/** A DNS-SD service resolver. Use this to retrieve addres, port and TXT data for a DNS-SD service */ +typedef struct AvahiSServiceResolver AvahiSServiceResolver; + +#include +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Callback prototype for AvahiSRecordBrowser events */ +typedef void (*AvahiSRecordBrowserCallback)( + AvahiSRecordBrowser *b, /**< The AvahiSRecordBrowser object that is emitting this callback */ + AvahiIfIndex interface, /**< Logical OS network interface number the record was found on */ + AvahiProtocol protocol, /**< Protocol number the record was found. */ + AvahiBrowserEvent event, /**< Browsing event, either AVAHI_BROWSER_NEW or AVAHI_BROWSER_REMOVE */ + AvahiRecord *record, /**< The record that was found */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata /**< Arbitrary user data passed to avahi_s_record_browser_new() */ ); + +/** Create a new browsing object for arbitrary RRs */ +AvahiSRecordBrowser *avahi_s_record_browser_new( + AvahiServer *server, /**< The server object to which attach this query */ + AvahiIfIndex interface, /**< Logical OS interface number where to look for the records, or AVAHI_IF_UNSPEC to look on interfaces */ + AvahiProtocol protocol, /**< Protocol number to use when looking for the record, or AVAHI_PROTO_UNSPEC to look on all protocols */ + AvahiKey *key, /**< The search key */ + AvahiLookupFlags flags, /**< Lookup flags. Must have set either AVAHI_LOOKUP_FORCE_WIDE_AREA or AVAHI_LOOKUP_FORCE_MULTICAST, since domain based detection is not available here. */ + AvahiSRecordBrowserCallback callback, /**< The callback to call on browsing events */ + void* userdata /**< Arbitrary use suppliable data which is passed to the callback */); + +/** Free an AvahiSRecordBrowser object */ +void avahi_s_record_browser_free(AvahiSRecordBrowser *b); + +/** Callback prototype for AvahiSHostNameResolver events */ +typedef void (*AvahiSHostNameResolverCallback)( + AvahiSHostNameResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, /**< Resolving event */ + const char *host_name, /**< Host name which should be resolved. May differ in case from the query */ + const AvahiAddress *a, /**< The address, or NULL if the host name couldn't be resolved. */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create an AvahiSHostNameResolver object for resolving a host name to an adddress. See AvahiSRecordBrowser for more info on the paramters. */ +AvahiSHostNameResolver *avahi_s_host_name_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *host_name, /**< The host name to look for */ + AvahiProtocol aprotocol, /**< The address family of the desired address or AVAHI_PROTO_UNSPEC if doesn't matter. */ + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSHostNameResolverCallback calback, + void* userdata); + +/** Free a AvahiSHostNameResolver object */ +void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r); + +/** Callback prototype for AvahiSAddressResolver events */ +typedef void (*AvahiSAddressResolverCallback)( + AvahiSAddressResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const AvahiAddress *a, + const char *host_name, /**< A host name for the specified address, if one was found, i.e. event == AVAHI_RESOLVER_FOUND */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create an AvahiSAddressResolver object. See AvahiSRecordBrowser for more info on the paramters. */ +AvahiSAddressResolver *avahi_s_address_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const AvahiAddress *address, + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSAddressResolverCallback calback, + void* userdata); + +/** Free an AvahiSAddressResolver object */ +void avahi_s_address_resolver_free(AvahiSAddressResolver *r); + +/** Callback prototype for AvahiSDomainBrowser events */ +typedef void (*AvahiSDomainBrowserCallback)( + AvahiSDomainBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *domain, + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSDomainBrowser object */ +AvahiSDomainBrowser *avahi_s_domain_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDomainBrowserType type, + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSDomainBrowserCallback callback, + void* userdata); + +/** Free an AvahiSDomainBrowser object */ +void avahi_s_domain_browser_free(AvahiSDomainBrowser *b); + +/** Callback prototype for AvahiSServiceTypeBrowser events */ +typedef void (*AvahiSServiceTypeBrowserCallback)( + AvahiSServiceTypeBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSServiceTypeBrowser object. */ +AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSServiceTypeBrowserCallback callback, + void* userdata); + +/** Free an AvahiSServiceTypeBrowser object */ +void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b); + +/** Callback prototype for AvahiSServiceBrowser events */ +typedef void (*AvahiSServiceBrowserCallback)( + AvahiSServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name /**< Service name, e.g. "Lennart's Files" */, + const char *type /**< DNS-SD type, e.g. "_http._tcp" */, + const char *domain /**< Domain of this service, e.g. "local" */, + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSServiceBrowser object. */ +AvahiSServiceBrowser *avahi_s_service_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *service_type /** DNS-SD service type, e.g. "_http._tcp" */, + const char *domain, + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSServiceBrowserCallback callback, + void* userdata); + +/** Free an AvahiSServiceBrowser object */ +void avahi_s_service_browser_free(AvahiSServiceBrowser *b); + +/** Callback prototype for AvahiSServiceResolver events */ +typedef void (*AvahiSServiceResolverCallback)( + AvahiSServiceResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, /**< Is AVAHI_RESOLVER_FOUND when the service was resolved successfully, and everytime it changes. Is AVAHI_RESOLVER_TIMOUT when the service failed to resolve or disappeared. */ + const char *name, /**< Service name */ + const char *type, /**< Service Type */ + const char *domain, + const char *host_name, /**< Host name of the service */ + const AvahiAddress *a, /**< The resolved host name */ + uint16_t port, /**< Service name */ + AvahiStringList *txt, /**< TXT record data */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSServiceResolver object. The specified callback function will be called with the resolved service data. */ +AvahiSServiceResolver *avahi_s_service_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain, + AvahiProtocol aprotocol, /**< Address family of the desired service address. Use AVAHI_PROTO_UNSPEC if you don't care */ + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSServiceResolverCallback calback, + void* userdata); + +/** Free an AvahiSServiceResolver object */ +void avahi_s_service_resolver_free(AvahiSServiceResolver *r); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/multicast-lookup.c b/3rdparty/QtZeroConf/avahi-core/multicast-lookup.c new file mode 100644 index 000000000..75988bfcd --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/multicast-lookup.c @@ -0,0 +1,350 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include "internal.h" +#include "browse.h" +#include "socket.h" +#include "log.h" +#include "hashmap.h" +#include "multicast-lookup.h" +#include "rr-util.h" + +struct AvahiMulticastLookup { + AvahiMulticastLookupEngine *engine; + int dead; + + AvahiKey *key, *cname_key; + + AvahiMulticastLookupCallback callback; + void *userdata; + + AvahiIfIndex interface; + AvahiProtocol protocol; + + int queriers_added; + + AvahiTimeEvent *all_for_now_event; + + AVAHI_LLIST_FIELDS(AvahiMulticastLookup, lookups); + AVAHI_LLIST_FIELDS(AvahiMulticastLookup, by_key); +}; + +struct AvahiMulticastLookupEngine { + AvahiServer *server; + + /* Lookups */ + AVAHI_LLIST_HEAD(AvahiMulticastLookup, lookups); + AvahiHashmap *lookups_by_key; + + int cleanup_dead; +}; + +static void all_for_now_callback(AvahiTimeEvent *e, void* userdata) { + AvahiMulticastLookup *l = userdata; + + assert(e); + assert(l); + + avahi_time_event_free(l->all_for_now_event); + l->all_for_now_event = NULL; + + l->callback(l->engine, l->interface, l->protocol, AVAHI_BROWSER_ALL_FOR_NOW, AVAHI_LOOKUP_RESULT_MULTICAST, NULL, l->userdata); +} + +AvahiMulticastLookup *avahi_multicast_lookup_new( + AvahiMulticastLookupEngine *e, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiKey *key, + AvahiMulticastLookupCallback callback, + void *userdata) { + + AvahiMulticastLookup *l, *t; + struct timeval tv; + + assert(e); + assert(AVAHI_IF_VALID(interface)); + assert(AVAHI_PROTO_VALID(protocol)); + assert(key); + assert(callback); + + l = avahi_new(AvahiMulticastLookup, 1); + l->engine = e; + l->dead = 0; + l->key = avahi_key_ref(key); + l->cname_key = avahi_key_new_cname(l->key); + l->callback = callback; + l->userdata = userdata; + l->interface = interface; + l->protocol = protocol; + l->all_for_now_event = NULL; + l->queriers_added = 0; + + t = avahi_hashmap_lookup(e->lookups_by_key, l->key); + AVAHI_LLIST_PREPEND(AvahiMulticastLookup, by_key, t, l); + avahi_hashmap_replace(e->lookups_by_key, avahi_key_ref(l->key), t); + + AVAHI_LLIST_PREPEND(AvahiMulticastLookup, lookups, e->lookups, l); + + avahi_querier_add_for_all(e->server, interface, protocol, l->key, &tv); + l->queriers_added = 1; + + /* Add a second */ + avahi_timeval_add(&tv, 1000000); + + /* Issue the ALL_FOR_NOW event one second after the querier was initially created */ + l->all_for_now_event = avahi_time_event_new(e->server->time_event_queue, &tv, all_for_now_callback, l); + + return l; +} + +static void lookup_stop(AvahiMulticastLookup *l) { + assert(l); + + l->callback = NULL; + + if (l->queriers_added) { + avahi_querier_remove_for_all(l->engine->server, l->interface, l->protocol, l->key); + l->queriers_added = 0; + } + + if (l->all_for_now_event) { + avahi_time_event_free(l->all_for_now_event); + l->all_for_now_event = NULL; + } +} + +static void lookup_destroy(AvahiMulticastLookup *l) { + AvahiMulticastLookup *t; + assert(l); + + lookup_stop(l); + + t = avahi_hashmap_lookup(l->engine->lookups_by_key, l->key); + AVAHI_LLIST_REMOVE(AvahiMulticastLookup, by_key, t, l); + if (t) + avahi_hashmap_replace(l->engine->lookups_by_key, avahi_key_ref(l->key), t); + else + avahi_hashmap_remove(l->engine->lookups_by_key, l->key); + + AVAHI_LLIST_REMOVE(AvahiMulticastLookup, lookups, l->engine->lookups, l); + + if (l->key) + avahi_key_unref(l->key); + + if (l->cname_key) + avahi_key_unref(l->cname_key); + + avahi_free(l); +} + +void avahi_multicast_lookup_free(AvahiMulticastLookup *l) { + assert(l); + + if (l->dead) + return; + + l->dead = 1; + l->engine->cleanup_dead = 1; + lookup_stop(l); +} + +void avahi_multicast_lookup_engine_cleanup(AvahiMulticastLookupEngine *e) { + AvahiMulticastLookup *l, *n; + assert(e); + + while (e->cleanup_dead) { + e->cleanup_dead = 0; + + for (l = e->lookups; l; l = n) { + n = l->lookups_next; + + if (l->dead) + lookup_destroy(l); + } + } +} + +struct cbdata { + AvahiMulticastLookupEngine *engine; + AvahiMulticastLookupCallback callback; + void *userdata; + AvahiKey *key, *cname_key; + AvahiInterface *interface; + unsigned n_found; +}; + +static void* scan_cache_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { + struct cbdata *cbdata = userdata; + + assert(c); + assert(pattern); + assert(e); + assert(cbdata); + + cbdata->callback( + cbdata->engine, + cbdata->interface->hardware->index, + cbdata->interface->protocol, + AVAHI_BROWSER_NEW, + AVAHI_LOOKUP_RESULT_CACHED|AVAHI_LOOKUP_RESULT_MULTICAST, + e->record, + cbdata->userdata); + + cbdata->n_found ++; + + return NULL; +} + +static void scan_interface_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + struct cbdata *cbdata = userdata; + + assert(m); + assert(i); + assert(cbdata); + + cbdata->interface = i; + + avahi_cache_walk(i->cache, cbdata->key, scan_cache_callback, cbdata); + + if (cbdata->cname_key) + avahi_cache_walk(i->cache, cbdata->cname_key, scan_cache_callback, cbdata); + + cbdata->interface = NULL; +} + +unsigned avahi_multicast_lookup_engine_scan_cache( + AvahiMulticastLookupEngine *e, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiKey *key, + AvahiMulticastLookupCallback callback, + void *userdata) { + + struct cbdata cbdata; + + assert(e); + assert(key); + assert(callback); + + assert(AVAHI_IF_VALID(interface)); + assert(AVAHI_PROTO_VALID(protocol)); + + cbdata.engine = e; + cbdata.key = key; + cbdata.cname_key = avahi_key_new_cname(key); + cbdata.callback = callback; + cbdata.userdata = userdata; + cbdata.interface = NULL; + cbdata.n_found = 0; + + avahi_interface_monitor_walk(e->server->monitor, interface, protocol, scan_interface_callback, &cbdata); + + if (cbdata.cname_key) + avahi_key_unref(cbdata.cname_key); + + return cbdata.n_found; +} + +void avahi_multicast_lookup_engine_new_interface(AvahiMulticastLookupEngine *e, AvahiInterface *i) { + AvahiMulticastLookup *l; + + assert(e); + assert(i); + + for (l = e->lookups; l; l = l->lookups_next) { + + if (l->dead || !l->callback) + continue; + + if (l->queriers_added && avahi_interface_match(i, l->interface, l->protocol)) + avahi_querier_add(i, l->key, NULL); + } +} + +void avahi_multicast_lookup_engine_notify(AvahiMulticastLookupEngine *e, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event) { + AvahiMulticastLookup *l; + + assert(e); + assert(record); + assert(i); + + for (l = avahi_hashmap_lookup(e->lookups_by_key, record->key); l; l = l->by_key_next) { + if (l->dead || !l->callback) + continue; + + if (avahi_interface_match(i, l->interface, l->protocol)) + l->callback(e, i->hardware->index, i->protocol, event, AVAHI_LOOKUP_RESULT_MULTICAST, record, l->userdata); + } + + + if (record->key->clazz == AVAHI_DNS_CLASS_IN && record->key->type == AVAHI_DNS_TYPE_CNAME) { + /* It's a CNAME record, so we have to scan the all lookups to see if one matches */ + + for (l = e->lookups; l; l = l->lookups_next) { + AvahiKey *key; + + if (l->dead || !l->callback) + continue; + + if ((key = avahi_key_new_cname(l->key))) { + if (avahi_key_equal(record->key, key)) + l->callback(e, i->hardware->index, i->protocol, event, AVAHI_LOOKUP_RESULT_MULTICAST, record, l->userdata); + + avahi_key_unref(key); + } + } + } +} + +AvahiMulticastLookupEngine *avahi_multicast_lookup_engine_new(AvahiServer *s) { + AvahiMulticastLookupEngine *e; + + assert(s); + + e = avahi_new(AvahiMulticastLookupEngine, 1); + e->server = s; + e->cleanup_dead = 0; + + /* Initialize lookup list */ + e->lookups_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL); + AVAHI_LLIST_HEAD_INIT(AvahiWideAreaLookup, e->lookups); + + return e; +} + +void avahi_multicast_lookup_engine_free(AvahiMulticastLookupEngine *e) { + assert(e); + + while (e->lookups) + lookup_destroy(e->lookups); + + avahi_hashmap_free(e->lookups_by_key); + avahi_free(e); +} + diff --git a/3rdparty/QtZeroConf/avahi-core/multicast-lookup.h b/3rdparty/QtZeroConf/avahi-core/multicast-lookup.h new file mode 100644 index 000000000..270766647 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/multicast-lookup.h @@ -0,0 +1,51 @@ +#ifndef foomulticastlookuphfoo +#define foomulticastlookuphfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "lookup.h" +#include "browse.h" + +typedef struct AvahiMulticastLookupEngine AvahiMulticastLookupEngine; +typedef struct AvahiMulticastLookup AvahiMulticastLookup; + +typedef void (*AvahiMulticastLookupCallback)( + AvahiMulticastLookupEngine *e, + AvahiIfIndex idx, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiLookupResultFlags flags, + AvahiRecord *r, + void *userdata); + +AvahiMulticastLookupEngine *avahi_multicast_lookup_engine_new(AvahiServer *s); +void avahi_multicast_lookup_engine_free(AvahiMulticastLookupEngine *e); + +unsigned avahi_multicast_lookup_engine_scan_cache(AvahiMulticastLookupEngine *e, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, AvahiMulticastLookupCallback callback, void *userdata); +void avahi_multicast_lookup_engine_new_interface(AvahiMulticastLookupEngine *e, AvahiInterface *i); +void avahi_multicast_lookup_engine_cleanup(AvahiMulticastLookupEngine *e); +void avahi_multicast_lookup_engine_notify(AvahiMulticastLookupEngine *e, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event); + +AvahiMulticastLookup *avahi_multicast_lookup_new(AvahiMulticastLookupEngine *e, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, AvahiMulticastLookupCallback callback, void *userdata); +void avahi_multicast_lookup_free(AvahiMulticastLookup *q); + + +#endif + diff --git a/3rdparty/QtZeroConf/avahi-core/netlink.c b/3rdparty/QtZeroConf/avahi-core/netlink.c new file mode 100644 index 000000000..42433d437 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/netlink.c @@ -0,0 +1,209 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +//#include + +#include +#include "netlink.h" +#include "log.h" + +struct AvahiNetlink { + int fd; + unsigned seq; + AvahiNetlinkCallback callback; + void* userdata; + uint8_t* buffer; + size_t buffer_length; + + const AvahiPoll *poll_api; + AvahiWatch *watch; +}; + +int avahi_netlink_work(AvahiNetlink *nl, int block) { + ssize_t bytes; + struct msghdr smsg; + struct cmsghdr *cmsg; + struct ucred *cred; + struct iovec iov; + struct nlmsghdr *p; + char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; + + assert(nl); + + iov.iov_base = nl->buffer; + iov.iov_len = nl->buffer_length; + + smsg.msg_name = NULL; + smsg.msg_namelen = 0; + smsg.msg_iov = &iov; + smsg.msg_iovlen = 1; + smsg.msg_control = cred_msg; + smsg.msg_controllen = sizeof(cred_msg); + smsg.msg_flags = (block ? 0 : MSG_DONTWAIT); + + if ((bytes = recvmsg(nl->fd, &smsg, 0)) < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + avahi_log_error(__FILE__": recvmsg() failed: %s", strerror(errno)); + return -1; + } + + cmsg = CMSG_FIRSTHDR(&smsg); + + if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) { + avahi_log_warn("No sender credentials received, ignoring data."); + return -1; + } + + cred = (struct ucred*) CMSG_DATA(cmsg); + + if (cred->uid != 0) + return -1; + + p = (struct nlmsghdr *) nl->buffer; + + assert(nl->callback); + + for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) { + if (!NLMSG_OK(p, (size_t) bytes)) { + avahi_log_warn(__FILE__": packet truncated"); + return -1; + } + + nl->callback(nl, p, nl->userdata); + } + + return 0; +} + +static void socket_event(AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event, void *userdata) { + AvahiNetlink *nl = userdata; + + assert(w); + assert(nl); + assert(fd == nl->fd); + + avahi_netlink_work(nl, 0); +} + +AvahiNetlink *avahi_netlink_new(const AvahiPoll *poll_api, uint32_t groups, void (*cb) (AvahiNetlink *nl, struct nlmsghdr *n, void* userdata), void* userdata) { + int fd = -1; + const int on = 1; + struct sockaddr_nl addr; + AvahiNetlink *nl = NULL; + + assert(poll_api); + assert(cb); + + if ((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { + avahi_log_error(__FILE__": socket(PF_NETLINK): %s", strerror(errno)); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_groups = groups; + addr.nl_pid = getpid(); + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + avahi_log_error(__FILE__": bind(): %s", strerror(errno)); + goto fail; + } + + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { + avahi_log_error(__FILE__": SO_PASSCRED: %s", strerror(errno)); + goto fail; + } + + if (!(nl = avahi_new(AvahiNetlink, 1))) { + avahi_log_error(__FILE__": avahi_new() failed."); + goto fail; + } + + nl->poll_api = poll_api; + nl->fd = fd; + nl->seq = 0; + nl->callback = cb; + nl->userdata = userdata; + + if (!(nl->buffer = avahi_new(uint8_t, nl->buffer_length = 64*1024))) { + avahi_log_error(__FILE__": avahi_new() failed."); + goto fail; + } + + if (!(nl->watch = poll_api->watch_new(poll_api, fd, AVAHI_WATCH_IN, socket_event, nl))) { + avahi_log_error(__FILE__": Failed to create watch."); + goto fail; + } + + return nl; + +fail: + + if (fd >= 0) + close(fd); + + if (nl) { + avahi_free(nl->buffer); + avahi_free(nl); + } + + return NULL; +} + +void avahi_netlink_free(AvahiNetlink *nl) { + assert(nl); + + if (nl->watch) + nl->poll_api->watch_free(nl->watch); + + if (nl->fd >= 0) + close(nl->fd); + + avahi_free(nl->buffer); + avahi_free(nl); +} + +int avahi_netlink_send(AvahiNetlink *nl, struct nlmsghdr *m, unsigned *ret_seq) { + assert(nl); + assert(m); + + m->nlmsg_seq = nl->seq++; + m->nlmsg_flags |= NLM_F_ACK; + + if (send(nl->fd, m, m->nlmsg_len, 0) < 0) { + avahi_log_error(__FILE__": send(): %s", strerror(errno)); + return -1; + } + + if (ret_seq) + *ret_seq = m->nlmsg_seq; + + return 0; +} diff --git a/3rdparty/QtZeroConf/avahi-core/netlink.h b/3rdparty/QtZeroConf/avahi-core/netlink.h new file mode 100644 index 000000000..9c6eb281d --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/netlink.h @@ -0,0 +1,41 @@ +#ifndef foonetlinkhfoo +#define foonetlinkhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include + +#include +#include + +#include + +typedef struct AvahiNetlink AvahiNetlink; + +typedef void (*AvahiNetlinkCallback)(AvahiNetlink *n, struct nlmsghdr *m, void* userdata); + +AvahiNetlink *avahi_netlink_new(const AvahiPoll *poll_api, uint32_t groups, AvahiNetlinkCallback callback, void* userdata); +void avahi_netlink_free(AvahiNetlink *n); +int avahi_netlink_send(AvahiNetlink *n, struct nlmsghdr *m, unsigned *ret_seq); +int avahi_netlink_work(AvahiNetlink *n, int block); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/prioq.c b/3rdparty/QtZeroConf/avahi-core/prioq.c new file mode 100644 index 000000000..28b50189d --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/prioq.c @@ -0,0 +1,388 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include "prioq.h" + +AvahiPrioQueue* avahi_prio_queue_new(AvahiPQCompareFunc compare) { + AvahiPrioQueue *q; + assert(compare); + + if (!(q = avahi_new(AvahiPrioQueue, 1))) + return NULL; /* OOM */ + + q->root = q->last = NULL; + q->n_nodes = 0; + q->compare = compare; + + return q; +} + +void avahi_prio_queue_free(AvahiPrioQueue *q) { + assert(q); + + while (q->last) + avahi_prio_queue_remove(q, q->last); + + assert(!q->n_nodes); + avahi_free(q); +} + +static AvahiPrioQueueNode* get_node_at_xy(AvahiPrioQueue *q, unsigned x, unsigned y) { + unsigned r; + AvahiPrioQueueNode *n; + assert(q); + + n = q->root; + assert(n); + + for (r = 0; r < y; r++) { + assert(n); + + if ((x >> (y-r-1)) & 1) + n = n->right; + else + n = n->left; + } + + assert(n->x == x); + assert(n->y == y); + + return n; +} + +static void exchange_nodes(AvahiPrioQueue *q, AvahiPrioQueueNode *a, AvahiPrioQueueNode *b) { + AvahiPrioQueueNode *l, *r, *p, *ap, *an, *bp, *bn; + unsigned t; + assert(q); + assert(a); + assert(b); + assert(a != b); + + /* Swap positions */ + t = a->x; a->x = b->x; b->x = t; + t = a->y; a->y = b->y; b->y = t; + + if (a->parent == b) { + /* B is parent of A */ + + p = b->parent; + b->parent = a; + + if ((a->parent = p)) { + if (a->parent->left == b) + a->parent->left = a; + else + a->parent->right = a; + } else + q->root = a; + + if (b->left == a) { + if ((b->left = a->left)) + b->left->parent = b; + a->left = b; + + r = a->right; + if ((a->right = b->right)) + a->right->parent = a; + if ((b->right = r)) + b->right->parent = b; + + } else { + if ((b->right = a->right)) + b->right->parent = b; + a->right = b; + + l = a->left; + if ((a->left = b->left)) + a->left->parent = a; + if ((b->left = l)) + b->left->parent = b; + } + } else if (b->parent == a) { + /* A ist parent of B */ + + p = a->parent; + a->parent = b; + + if ((b->parent = p)) { + if (b->parent->left == a) + b->parent->left = b; + else + b->parent->right = b; + } else + q->root = b; + + if (a->left == b) { + if ((a->left = b->left)) + a->left->parent = a; + b->left = a; + + r = a->right; + if ((a->right = b->right)) + a->right->parent = a; + if ((b->right = r)) + b->right->parent = b; + } else { + if ((a->right = b->right)) + a->right->parent = a; + b->right = a; + + l = a->left; + if ((a->left = b->left)) + a->left->parent = a; + if ((b->left = l)) + b->left->parent = b; + } + } else { + AvahiPrioQueueNode *apl = NULL, *bpl = NULL; + + /* Swap parents */ + ap = a->parent; + bp = b->parent; + + if (ap) + apl = ap->left; + if (bp) + bpl = bp->left; + + if ((a->parent = bp)) { + if (bpl == b) + bp->left = a; + else + bp->right = a; + } else + q->root = a; + + if ((b->parent = ap)) { + if (apl == a) + ap->left = b; + else + ap->right = b; + } else + q->root = b; + + /* Swap children */ + l = a->left; + r = a->right; + + if ((a->left = b->left)) + a->left->parent = a; + + if ((b->left = l)) + b->left->parent = b; + + if ((a->right = b->right)) + a->right->parent = a; + + if ((b->right = r)) + b->right->parent = b; + } + + /* Swap siblings */ + ap = a->prev; an = a->next; + bp = b->prev; bn = b->next; + + if (a->next == b) { + /* A is predecessor of B */ + a->prev = b; + b->next = a; + + if ((a->next = bn)) + a->next->prev = a; + else + q->last = a; + + if ((b->prev = ap)) + b->prev->next = b; + + } else if (b->next == a) { + /* B is predecessor of A */ + a->next = b; + b->prev = a; + + if ((a->prev = bp)) + a->prev->next = a; + + if ((b->next = an)) + b->next->prev = b; + else + q->last = b; + + } else { + /* A is no neighbour of B */ + + if ((a->prev = bp)) + a->prev->next = a; + + if ((a->next = bn)) + a->next->prev = a; + else + q->last = a; + + if ((b->prev = ap)) + b->prev->next = b; + + if ((b->next = an)) + b->next->prev = b; + else + q->last = b; + } +} + +/* Move a node to the correct position */ +void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n) { + assert(q); + assert(n); + assert(n->queue == q); + + /* Move up until the position is OK */ + while (n->parent && q->compare(n->parent->data, n->data) > 0) + exchange_nodes(q, n, n->parent); + + /* Move down until the position is OK */ + for (;;) { + AvahiPrioQueueNode *min; + + if (!(min = n->left)) { + /* No children */ + assert(!n->right); + break; + } + + if (n->right && q->compare(n->right->data, min->data) < 0) + min = n->right; + + /* min now contains the smaller one of our two children */ + + if (q->compare(n->data, min->data) <= 0) + /* Order OK */ + break; + + exchange_nodes(q, n, min); + } +} + +AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, void* data) { + AvahiPrioQueueNode *n; + assert(q); + + if (!(n = avahi_new(AvahiPrioQueueNode, 1))) + return NULL; /* OOM */ + + n->queue = q; + n->data = data; + + if (q->last) { + assert(q->root); + assert(q->n_nodes); + + n->y = q->last->y; + n->x = q->last->x+1; + + if (n->x >= ((unsigned) 1 << n->y)) { + n->x = 0; + n->y++; + } + + q->last->next = n; + n->prev = q->last; + + assert(n->y > 0); + n->parent = get_node_at_xy(q, n->x/2, n->y-1); + + if (n->x & 1) + n->parent->right = n; + else + n->parent->left = n; + } else { + assert(!q->root); + assert(!q->n_nodes); + + n->y = n->x = 0; + q->root = n; + n->prev = n->parent = NULL; + } + + n->next = n->left = n->right = NULL; + q->last = n; + q->n_nodes++; + + avahi_prio_queue_shuffle(q, n); + + return n; +} + +void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n) { + assert(q); + assert(n); + assert(q == n->queue); + + if (n != q->last) { + AvahiPrioQueueNode *replacement = q->last; + exchange_nodes(q, replacement, n); + avahi_prio_queue_remove(q, n); + avahi_prio_queue_shuffle(q, replacement); + return; + } + + assert(n == q->last); + assert(!n->next); + assert(!n->left); + assert(!n->right); + + q->last = n->prev; + + if (n->prev) { + n->prev->next = NULL; + assert(n->parent); + } else + assert(!n->parent); + + if (n->parent) { + assert(n->prev); + if (n->parent->left == n) { + assert(n->parent->right == NULL); + n->parent->left = NULL; + } else { + assert(n->parent->right == n); + assert(n->parent->left != NULL); + n->parent->right = NULL; + } + } else { + assert(q->root == n); + assert(!n->prev); + assert(q->n_nodes == 1); + q->root = NULL; + } + + avahi_free(n); + + assert(q->n_nodes > 0); + q->n_nodes--; +} + diff --git a/3rdparty/QtZeroConf/avahi-core/prioq.h b/3rdparty/QtZeroConf/avahi-core/prioq.h new file mode 100644 index 000000000..b3d31eb58 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/prioq.h @@ -0,0 +1,49 @@ +#ifndef fooprioqhfoo +#define fooprioqhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct AvahiPrioQueue AvahiPrioQueue; +typedef struct AvahiPrioQueueNode AvahiPrioQueueNode; + +typedef int (*AvahiPQCompareFunc)(const void* a, const void* b); + +struct AvahiPrioQueue { + AvahiPrioQueueNode *root, *last; + unsigned n_nodes; + AvahiPQCompareFunc compare; +}; + +struct AvahiPrioQueueNode { + AvahiPrioQueue *queue; + void* data; + unsigned x, y; + AvahiPrioQueueNode *left, *right, *parent, *next, *prev; +}; + +AvahiPrioQueue* avahi_prio_queue_new(AvahiPQCompareFunc compare); +void avahi_prio_queue_free(AvahiPrioQueue *q); + +AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, void* data); +void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n); + +void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/probe-sched.c b/3rdparty/QtZeroConf/avahi-core/probe-sched.c new file mode 100644 index 000000000..2e22338df --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/probe-sched.c @@ -0,0 +1,403 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include "probe-sched.h" +#include "log.h" +#include "rr-util.h" + +#define AVAHI_PROBE_HISTORY_MSEC 150 +#define AVAHI_PROBE_DEFER_MSEC 50 + +typedef struct AvahiProbeJob AvahiProbeJob; + +struct AvahiProbeJob { + AvahiProbeScheduler *scheduler; + AvahiTimeEvent *time_event; + + int chosen; /* Use for packet assembling */ + int done; + struct timeval delivery; + + AvahiRecord *record; + + AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs); +}; + +struct AvahiProbeScheduler { + AvahiInterface *interface; + AvahiTimeEventQueue *time_event_queue; + + AVAHI_LLIST_HEAD(AvahiProbeJob, jobs); + AVAHI_LLIST_HEAD(AvahiProbeJob, history); +}; + +static AvahiProbeJob* job_new(AvahiProbeScheduler *s, AvahiRecord *record, int done) { + AvahiProbeJob *pj; + + assert(s); + assert(record); + + if (!(pj = avahi_new(AvahiProbeJob, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; /* OOM */ + } + + pj->scheduler = s; + pj->record = avahi_record_ref(record); + pj->time_event = NULL; + pj->chosen = 0; + + if ((pj->done = done)) + AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj); + else + AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->jobs, pj); + + return pj; +} + +static void job_free(AvahiProbeScheduler *s, AvahiProbeJob *pj) { + assert(pj); + + if (pj->time_event) + avahi_time_event_free(pj->time_event); + + if (pj->done) + AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->history, pj); + else + AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj); + + avahi_record_unref(pj->record); + avahi_free(pj); +} + +static void elapse_callback(AvahiTimeEvent *e, void* data); + +static void job_set_elapse_time(AvahiProbeScheduler *s, AvahiProbeJob *pj, unsigned msec, unsigned jitter) { + struct timeval tv; + + assert(s); + assert(pj); + + avahi_elapse_time(&tv, msec, jitter); + + if (pj->time_event) + avahi_time_event_update(pj->time_event, &tv); + else + pj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, pj); +} + +static void job_mark_done(AvahiProbeScheduler *s, AvahiProbeJob *pj) { + assert(s); + assert(pj); + + assert(!pj->done); + + AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj); + AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj); + + pj->done = 1; + + job_set_elapse_time(s, pj, AVAHI_PROBE_HISTORY_MSEC, 0); + gettimeofday(&pj->delivery, NULL); +} + +AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i) { + AvahiProbeScheduler *s; + + assert(i); + + if (!(s = avahi_new(AvahiProbeScheduler, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; + } + + s->interface = i; + s->time_event_queue = i->monitor->server->time_event_queue; + + AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->jobs); + AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->history); + + return s; +} + +void avahi_probe_scheduler_free(AvahiProbeScheduler *s) { + assert(s); + + avahi_probe_scheduler_clear(s); + avahi_free(s); +} + +void avahi_probe_scheduler_clear(AvahiProbeScheduler *s) { + assert(s); + + while (s->jobs) + job_free(s, s->jobs); + while (s->history) + job_free(s, s->history); +} + +static int packet_add_probe_query(AvahiProbeScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) { + size_t size; + AvahiKey *k; + int b; + + assert(s); + assert(p); + assert(pj); + + assert(!pj->chosen); + + /* Estimate the size for this record */ + size = + avahi_key_get_estimate_size(pj->record->key) + + avahi_record_get_estimate_size(pj->record); + + /* Too large */ + if (size > avahi_dns_packet_reserved_space(p)) + return 0; + + /* Create the probe query */ + if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY))) + return 0; /* OOM */ + + b = !!avahi_dns_packet_append_key(p, k, 0); + assert(b); + + /* reserve size for record data */ + avahi_dns_packet_reserve_size(p, avahi_record_get_estimate_size(pj->record)); + + /* Mark this job for addition to the packet */ + pj->chosen = 1; + + /* Scan for more jobs whith matching key pattern */ + for (pj = s->jobs; pj; pj = pj->jobs_next) { + if (pj->chosen) + continue; + + /* Does the record match the probe? */ + if (k->clazz != pj->record->key->clazz || !avahi_domain_equal(k->name, pj->record->key->name)) + continue; + + /* This job wouldn't fit in */ + if (avahi_record_get_estimate_size(pj->record) > avahi_dns_packet_reserved_space(p)) + break; + + /* reserve size for record data */ + avahi_dns_packet_reserve_size(p, avahi_record_get_estimate_size(pj->record)); + + /* Mark this job for addition to the packet */ + pj->chosen = 1; + } + + avahi_key_unref(k); + + return 1; +} + +static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) { + AvahiProbeJob *pj = data, *next; + AvahiProbeScheduler *s; + AvahiDnsPacket *p; + unsigned n; + + assert(pj); + s = pj->scheduler; + + if (pj->done) { + /* Lets remove it from the history */ + job_free(s, pj); + return; + } + + if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu))) + return; /* OOM */ + n = 1; + + /* Add the import probe */ + if (!packet_add_probe_query(s, p, pj)) { + size_t size; + AvahiKey *k; + int b; + + avahi_dns_packet_free(p); + + /* The probe didn't fit in the package, so let's allocate a larger one */ + + size = + avahi_key_get_estimate_size(pj->record->key) + + avahi_record_get_estimate_size(pj->record) + + AVAHI_DNS_PACKET_HEADER_SIZE; + + if (!(p = avahi_dns_packet_new_query(size + AVAHI_DNS_PACKET_EXTRA_SIZE))) + return; /* OOM */ + + if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY))) { + avahi_dns_packet_free(p); + return; /* OOM */ + } + + b = avahi_dns_packet_append_key(p, k, 0) && avahi_dns_packet_append_record(p, pj->record, 0, 0); + avahi_key_unref(k); + + if (b) { + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, 1); + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, 1); + avahi_interface_send_packet(s->interface, p); + } else + avahi_log_warn("Probe record too large, cannot send"); + + avahi_dns_packet_free(p); + job_mark_done(s, pj); + + return; + } + + /* Try to fill up packet with more probes, if available */ + for (pj = s->jobs; pj; pj = pj->jobs_next) { + + if (pj->chosen) + continue; + + if (!packet_add_probe_query(s, p, pj)) + break; + + n++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n); + + n = 0; + + /* Now add the chosen records to the authorative section */ + for (pj = s->jobs; pj; pj = next) { + + next = pj->jobs_next; + + if (!pj->chosen) + continue; + + if (!avahi_dns_packet_append_record(p, pj->record, 0, 0)) { +/* avahi_log_warn("Bad probe size estimate!"); */ + + /* Unmark all following jobs */ + for (; pj; pj = pj->jobs_next) + pj->chosen = 0; + + break; + } + + job_mark_done(s, pj); + + n ++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n); + + /* Send it now */ + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); +} + +static AvahiProbeJob* find_scheduled_job(AvahiProbeScheduler *s, AvahiRecord *record) { + AvahiProbeJob *pj; + + assert(s); + assert(record); + + for (pj = s->jobs; pj; pj = pj->jobs_next) { + assert(!pj->done); + + if (avahi_record_equal_no_ttl(pj->record, record)) + return pj; + } + + return NULL; +} + +static AvahiProbeJob* find_history_job(AvahiProbeScheduler *s, AvahiRecord *record) { + AvahiProbeJob *pj; + + assert(s); + assert(record); + + for (pj = s->history; pj; pj = pj->jobs_next) { + assert(pj->done); + + if (avahi_record_equal_no_ttl(pj->record, record)) { + /* Check whether this entry is outdated */ + + if (avahi_age(&pj->delivery) > AVAHI_PROBE_HISTORY_MSEC*1000) { + /* it is outdated, so let's remove it */ + job_free(s, pj); + return NULL; + } + + return pj; + } + } + + return NULL; +} + +int avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, int immediately) { + AvahiProbeJob *pj; + struct timeval tv; + + assert(s); + assert(record); + assert(!avahi_key_is_pattern(record->key)); + + if ((pj = find_history_job(s, record))) + return 0; + + avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0); + + if ((pj = find_scheduled_job(s, record))) { + + if (avahi_timeval_compare(&tv, &pj->delivery) < 0) { + /* If the new entry should be scheduled earlier, update the old entry */ + pj->delivery = tv; + avahi_time_event_update(pj->time_event, &pj->delivery); + } + + return 1; + } else { + /* Create a new job and schedule it */ + if (!(pj = job_new(s, record, 0))) + return 0; /* OOM */ + + pj->delivery = tv; + pj->time_event = avahi_time_event_new(s->time_event_queue, &pj->delivery, elapse_callback, pj); + + +/* avahi_log_debug("Accepted new probe job."); */ + + return 1; + } +} diff --git a/3rdparty/QtZeroConf/avahi-core/probe-sched.h b/3rdparty/QtZeroConf/avahi-core/probe-sched.h new file mode 100644 index 000000000..e47de415c --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/probe-sched.h @@ -0,0 +1,34 @@ +#ifndef fooprobeschedhfoo +#define fooprobeschedhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct AvahiProbeScheduler AvahiProbeScheduler; + +#include +#include "iface.h" + +AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i); +void avahi_probe_scheduler_free(AvahiProbeScheduler *s); +void avahi_probe_scheduler_clear(AvahiProbeScheduler *s); + +int avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, int immediately); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/publish.h b/3rdparty/QtZeroConf/avahi-core/publish.h new file mode 100644 index 000000000..90797de57 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/publish.h @@ -0,0 +1,175 @@ +#ifndef foopublishhfoo +#define foopublishhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file core/publish.h Functions for publising local services and RRs */ + +/** \example core-publish-service.c Example how to register a DNS-SD + * service using an embedded mDNS stack. It behaves like a network + * printer registering both an IPP and a BSD LPR service. */ + +/** A group of locally registered DNS RRs */ +typedef struct AvahiSEntryGroup AvahiSEntryGroup; + +#include +#include + +AVAHI_C_DECL_BEGIN + +/** Prototype for callback functions which are called whenever the state of an AvahiSEntryGroup object changes */ +typedef void (*AvahiSEntryGroupCallback) (AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void* userdata); + +/** Iterate through all local entries of the server. (when g is NULL) + * or of a specified entry group. At the first call state should point + * to a NULL initialized void pointer, That pointer is used to track + * the current iteration. It is not safe to call any other + * avahi_server_xxx() function during the iteration. If the last entry + * has been read, NULL is returned. */ +const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state); + +/** Create a new entry group. The specified callback function is + * called whenever the state of the group changes. Use entry group + * objects to keep track of you RRs. Add new RRs to a group using + * avahi_server_add_xxx(). Make sure to call avahi_s_entry_group_commit() + * to start the registration process for your RRs */ +AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata); + +/** Free an entry group. All RRs assigned to the group are removed from the server */ +void avahi_s_entry_group_free(AvahiSEntryGroup *g); + +/** Commit an entry group. This starts the probing and registration process for all RRs in the group */ +int avahi_s_entry_group_commit(AvahiSEntryGroup *g); + +/** Remove all entries from the entry group and reset the state to AVAHI_ENTRY_GROUP_UNCOMMITED. */ +void avahi_s_entry_group_reset(AvahiSEntryGroup *g); + +/** Return 1 if the entry group is empty, i.e. has no records attached. */ +int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g); + +/** Return the current state of the specified entry group */ +AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g); + +/** Change the opaque user data pointer attached to an entry group object */ +void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata); + +/** Return the opaque user data pointer currently set for the entry group object */ +void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g); + +/** Add a new resource record to the server. Returns 0 on success, negative otherwise. */ +int avahi_server_add( + AvahiServer *s, /**< The server object to add this record to */ + AvahiSEntryGroup *g, /**< An entry group object if this new record shall be attached to one, or NULL. If you plan to remove the record sometime later you a required to pass an entry group object here. */ + AvahiIfIndex interface, /**< A numeric index of a network interface to attach this record to, or AVAHI_IF_UNSPEC to attach this record to all interfaces */ + AvahiProtocol protocol, /**< A protocol family to attach this record to. One of the AVAHI_PROTO_xxx constants. Use AVAHI_PROTO_UNSPEC to make this record available on all protocols (wich means on both IPv4 and IPv6). */ + AvahiPublishFlags flags, /**< Special flags for this record */ + AvahiRecord *r /**< The record to add. This function increases the reference counter of this object. */); + +/** Add an IP address mapping to the server. This will add both the + * host-name-to-address and the reverse mapping to the server. See + * avahi_server_add() for more information. If adding one of the RRs + * fails, the function returns with an error, but it is not defined if + * the other RR is deleted from the server or not. Therefore, you have + * to free the AvahiSEntryGroup and create a new one before + * proceeding. */ +int avahi_server_add_address( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + AvahiAddress *a); + +/** Add an DNS-SD service to the Server. This will add all required + * RRs to the server. See avahi_server_add() for more information. If + * adding one of the RRs fails, the function returns with an error, + * but it is not defined if the other RR is deleted from the server or + * not. Therefore, you have to free the AvahiSEntryGroup and create a + * new one before proceeding. */ +int avahi_server_add_service( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, /**< Service name, e.g. "Lennart's Files" */ + const char *type, /**< DNS-SD type, e.g. "_http._tcp" */ + const char *domain, + const char *host, /**< Host name where this servcie resides, or NULL if on the local host */ + uint16_t port, /**< Port number of the service */ + ... /**< Text records, terminated by NULL */) AVAHI_GCC_SENTINEL; + +/** Mostly identical to avahi_server_add_service(), but takes an AvahiStringList object for the TXT records. The AvahiStringList object is copied. */ +int avahi_server_add_service_strlst( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + const char *host, + uint16_t port, + AvahiStringList *strlst); + +/** Add a subtype for an already existing service */ +int avahi_server_add_service_subtype( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, /**< Specify the name of main service you already added here */ + const char *type, /**< Specify the main type of the service you already added here */ + const char *domain, /**< Specify the main type of the service you already added here */ + const char *subtype /**< The new subtype for the specified service */ ); + +/** Update the TXT record for a service with the data from the specified string list */ +int avahi_server_update_service_txt_strlst( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + AvahiStringList *strlst); + +/** Update the TXT record for a service with the NULL termonate list of strings */ +int avahi_server_update_service_txt( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + ...) AVAHI_GCC_SENTINEL; + +/** Check if there is a service locally defined and return the entry group it is attached to. Returns NULL if the service isn't local*/ +int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/querier.c b/3rdparty/QtZeroConf/avahi-core/querier.c new file mode 100644 index 000000000..d9dc1fb0f --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/querier.c @@ -0,0 +1,268 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include + +#include "querier.h" +#include "log.h" + +struct AvahiQuerier { + AvahiInterface *interface; + + AvahiKey *key; + int n_used; + + unsigned sec_delay; + + AvahiTimeEvent *time_event; + + struct timeval creation_time; + + unsigned post_id; + int post_id_valid; + + AVAHI_LLIST_FIELDS(AvahiQuerier, queriers); +}; + +void avahi_querier_free(AvahiQuerier *q) { + assert(q); + + AVAHI_LLIST_REMOVE(AvahiQuerier, queriers, q->interface->queriers, q); + avahi_hashmap_remove(q->interface->queriers_by_key, q->key); + + avahi_key_unref(q->key); + avahi_time_event_free(q->time_event); + + avahi_free(q); +} + +static void querier_elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) { + AvahiQuerier *q = userdata; + struct timeval tv; + + assert(q); + + if (q->n_used <= 0) { + + /* We are not referenced by anyone anymore, so let's free + * ourselves. We should not send out any further queries from + * this querier object anymore. */ + + avahi_querier_free(q); + return; + } + + if (avahi_interface_post_query(q->interface, q->key, 0, &q->post_id)) { + + /* The queue accepted our query. We store the query id here, + * that allows us to drop the query at a later point if the + * query is very short-lived. */ + + q->post_id_valid = 1; + } + + q->sec_delay *= 2; + + if (q->sec_delay >= 60*60) /* 1h */ + q->sec_delay = 60*60; + + avahi_elapse_time(&tv, q->sec_delay*1000, 0); + avahi_time_event_update(q->time_event, &tv); +} + +void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_ctime) { + AvahiQuerier *q; + struct timeval tv; + + assert(i); + assert(key); + + if ((q = avahi_hashmap_lookup(i->queriers_by_key, key))) { + + /* Someone is already browsing for records of this RR key */ + q->n_used++; + + /* Return the creation time. This is used for generating the + * ALL_FOR_NOW event one second after the querier was + * initially created. */ + if (ret_ctime) + *ret_ctime = q->creation_time; + return; + } + + /* No one is browsing for this RR key, so we add a new querier */ + if (!(q = avahi_new(AvahiQuerier, 1))) + return; /* OOM */ + + q->key = avahi_key_ref(key); + q->interface = i; + q->n_used = 1; + q->sec_delay = 1; + q->post_id_valid = 0; + gettimeofday(&q->creation_time, NULL); + + /* Do the initial query */ + if (avahi_interface_post_query(i, key, 0, &q->post_id)) + q->post_id_valid = 1; + + /* Schedule next queries */ + q->time_event = avahi_time_event_new(i->monitor->server->time_event_queue, avahi_elapse_time(&tv, q->sec_delay*1000, 0), querier_elapse_callback, q); + + AVAHI_LLIST_PREPEND(AvahiQuerier, queriers, i->queriers, q); + avahi_hashmap_insert(i->queriers_by_key, q->key, q); + + /* Return the creation time. This is used for generating the + * ALL_FOR_NOW event one second after the querier was initially + * created. */ + if (ret_ctime) + *ret_ctime = q->creation_time; +} + +void avahi_querier_remove(AvahiInterface *i, AvahiKey *key) { + AvahiQuerier *q; + + /* There was no querier for this RR key, or it wasn't referenced + * by anyone. */ + if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key)) || q->n_used <= 0) + return; + + if ((--q->n_used) <= 0) { + + /* Nobody references us anymore. */ + + if (q->post_id_valid && avahi_interface_withraw_query(i, q->post_id)) { + + /* We succeeded in withdrawing our query from the queue, + * so let's drop dead. */ + + avahi_querier_free(q); + } + + /* If we failed to withdraw our query from the queue, we stay + * alive, in case someone else might recycle our querier at a + * later point. We are freed at our next expiry, in case + * nobody recycled us. */ + } +} + +static void remove_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + assert(m); + assert(i); + assert(userdata); + + if (i->announcing) + avahi_querier_remove(i, (AvahiKey*) userdata); +} + +void avahi_querier_remove_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key) { + assert(s); + assert(key); + + avahi_interface_monitor_walk(s->monitor, idx, protocol, remove_querier_callback, key); +} + +struct cbdata { + AvahiKey *key; + struct timeval *ret_ctime; +}; + +static void add_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + struct cbdata *cbdata = userdata; + + assert(m); + assert(i); + assert(cbdata); + + if (i->announcing) { + struct timeval tv; + avahi_querier_add(i, cbdata->key, &tv); + + if (cbdata->ret_ctime && avahi_timeval_compare(&tv, cbdata->ret_ctime) > 0) + *cbdata->ret_ctime = tv; + } +} + +void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, struct timeval *ret_ctime) { + struct cbdata cbdata; + + assert(s); + assert(key); + + cbdata.key = key; + cbdata.ret_ctime = ret_ctime; + + if (ret_ctime) + ret_ctime->tv_sec = ret_ctime->tv_usec = 0; + + avahi_interface_monitor_walk(s->monitor, idx, protocol, add_querier_callback, &cbdata); +} + +int avahi_querier_shall_refresh_cache(AvahiInterface *i, AvahiKey *key) { + AvahiQuerier *q; + + assert(i); + assert(key); + + /* Called by the cache maintainer */ + + if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key))) + /* This key is currently not subscribed at all, so no cache + * refresh is needed */ + return 0; + + if (q->n_used <= 0) { + + /* If this is an entry nobody references right now, don't + * consider it "existing". */ + + /* Remove this querier since it is referenced by nobody + * and the cached data will soon be out of date */ + avahi_querier_free(q); + + /* Tell the cache that no refresh is needed */ + return 0; + + } else { + struct timeval tv; + + /* We can defer our query a little, since the cache will now + * issue a refresh query anyway. */ + avahi_elapse_time(&tv, q->sec_delay*1000, 0); + avahi_time_event_update(q->time_event, &tv); + + /* Tell the cache that a refresh should be issued */ + return 1; + } +} + +void avahi_querier_free_all(AvahiInterface *i) { + assert(i); + + while (i->queriers) + avahi_querier_free(i->queriers); +} diff --git a/3rdparty/QtZeroConf/avahi-core/querier.h b/3rdparty/QtZeroConf/avahi-core/querier.h new file mode 100644 index 000000000..6a32a3b2f --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/querier.h @@ -0,0 +1,48 @@ +#ifndef fooquerierhfoo +#define fooquerierhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct AvahiQuerier AvahiQuerier; + +#include "iface.h" + +/** Add querier for the specified key to the specified interface */ +void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_ctime); + +/** Remove a querier for the specified key from the specified interface */ +void avahi_querier_remove(AvahiInterface *i, AvahiKey *key); + +/** Add a querier for the specified key on all interfaces that mach */ +void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, struct timeval *ret_ctime); + +/** Remove a querier for the specified key on all interfaces that mach */ +void avahi_querier_remove_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key); + +/** Free all queriers */ +void avahi_querier_free(AvahiQuerier *q); + +/** Free all queriers on the specified interface */ +void avahi_querier_free_all(AvahiInterface *i); + +/** Return 1 if there is a querier for the specified key on the specified interface */ +int avahi_querier_shall_refresh_cache(AvahiInterface *i, AvahiKey *key); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/query-sched.c b/3rdparty/QtZeroConf/avahi-core/query-sched.c new file mode 100644 index 000000000..ff833f97b --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/query-sched.c @@ -0,0 +1,450 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include "query-sched.h" +#include "log.h" + +#define AVAHI_QUERY_HISTORY_MSEC 100 +#define AVAHI_QUERY_DEFER_MSEC 100 + +typedef struct AvahiQueryJob AvahiQueryJob; +typedef struct AvahiKnownAnswer AvahiKnownAnswer; + +struct AvahiQueryJob { + unsigned id; + int n_posted; + + AvahiQueryScheduler *scheduler; + AvahiTimeEvent *time_event; + + int done; + struct timeval delivery; + + AvahiKey *key; + + /* Jobs are stored in a simple linked list. It might turn out in + * the future that this list grows too long and we must switch to + * some other kind of data structure. This needs further + * investigation. I expect the list to be very short (< 20 + * entries) most of the time, but this might be a wrong + * assumption, especially on setups where traffic reflection is + * involved. */ + + AVAHI_LLIST_FIELDS(AvahiQueryJob, jobs); +}; + +struct AvahiKnownAnswer { + AvahiQueryScheduler *scheduler; + AvahiRecord *record; + + AVAHI_LLIST_FIELDS(AvahiKnownAnswer, known_answer); +}; + +struct AvahiQueryScheduler { + AvahiInterface *interface; + AvahiTimeEventQueue *time_event_queue; + + unsigned next_id; + + AVAHI_LLIST_HEAD(AvahiQueryJob, jobs); + AVAHI_LLIST_HEAD(AvahiQueryJob, history); + AVAHI_LLIST_HEAD(AvahiKnownAnswer, known_answers); +}; + +static AvahiQueryJob* job_new(AvahiQueryScheduler *s, AvahiKey *key, int done) { + AvahiQueryJob *qj; + + assert(s); + assert(key); + + if (!(qj = avahi_new(AvahiQueryJob, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; + } + + qj->scheduler = s; + qj->key = avahi_key_ref(key); + qj->time_event = NULL; + qj->n_posted = 1; + qj->id = s->next_id++; + + if ((qj->done = done)) + AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj); + else + AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->jobs, qj); + + return qj; +} + +static void job_free(AvahiQueryScheduler *s, AvahiQueryJob *qj) { + assert(s); + assert(qj); + + if (qj->time_event) + avahi_time_event_free(qj->time_event); + + if (qj->done) + AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->history, qj); + else + AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->jobs, qj); + + avahi_key_unref(qj->key); + avahi_free(qj); +} + +static void elapse_callback(AvahiTimeEvent *e, void* data); + +static void job_set_elapse_time(AvahiQueryScheduler *s, AvahiQueryJob *qj, unsigned msec, unsigned jitter) { + struct timeval tv; + + assert(s); + assert(qj); + + avahi_elapse_time(&tv, msec, jitter); + + if (qj->time_event) + avahi_time_event_update(qj->time_event, &tv); + else + qj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, qj); +} + +static void job_mark_done(AvahiQueryScheduler *s, AvahiQueryJob *qj) { + assert(s); + assert(qj); + + assert(!qj->done); + + AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->jobs, qj); + AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj); + + qj->done = 1; + + job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0); + gettimeofday(&qj->delivery, NULL); +} + +AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i) { + AvahiQueryScheduler *s; + assert(i); + + if (!(s = avahi_new(AvahiQueryScheduler, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; /* OOM */ + } + + s->interface = i; + s->time_event_queue = i->monitor->server->time_event_queue; + s->next_id = 0; + + AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->jobs); + AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->history); + AVAHI_LLIST_HEAD_INIT(AvahiKnownAnswer, s->known_answers); + + return s; +} + +void avahi_query_scheduler_free(AvahiQueryScheduler *s) { + assert(s); + + assert(!s->known_answers); + avahi_query_scheduler_clear(s); + avahi_free(s); +} + +void avahi_query_scheduler_clear(AvahiQueryScheduler *s) { + assert(s); + + while (s->jobs) + job_free(s, s->jobs); + while (s->history) + job_free(s, s->history); +} + +static void* known_answer_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { + AvahiQueryScheduler *s = userdata; + AvahiKnownAnswer *ka; + + assert(c); + assert(pattern); + assert(e); + assert(s); + + if (avahi_cache_entry_half_ttl(c, e)) + return NULL; + + if (!(ka = avahi_new0(AvahiKnownAnswer, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; + } + + ka->scheduler = s; + ka->record = avahi_record_ref(e->record); + + AVAHI_LLIST_PREPEND(AvahiKnownAnswer, known_answer, s->known_answers, ka); + return NULL; +} + +static int packet_add_query_job(AvahiQueryScheduler *s, AvahiDnsPacket *p, AvahiQueryJob *qj) { + assert(s); + assert(p); + assert(qj); + + if (!avahi_dns_packet_append_key(p, qj->key, 0)) + return 0; + + /* Add all matching known answers to the list */ + avahi_cache_walk(s->interface->cache, qj->key, known_answer_walk_callback, s); + + job_mark_done(s, qj); + + return 1; +} + +static void append_known_answers_and_send(AvahiQueryScheduler *s, AvahiDnsPacket *p) { + AvahiKnownAnswer *ka; + unsigned n; + assert(s); + assert(p); + + n = 0; + + while ((ka = s->known_answers)) { + int too_large = 0; + + while (!avahi_dns_packet_append_record(p, ka->record, 0, 0)) { + + if (avahi_dns_packet_is_empty(p)) { + /* The record is too large to fit into one packet, so + there's no point in sending it. Better is letting + the owner of the record send it as a response. This + has the advantage of a cache refresh. */ + + too_large = 1; + break; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) | AVAHI_DNS_FLAG_TC); + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); + + p = avahi_dns_packet_new_query(s->interface->hardware->mtu); + n = 0; + } + + AVAHI_LLIST_REMOVE(AvahiKnownAnswer, known_answer, s->known_answers, ka); + avahi_record_unref(ka->record); + avahi_free(ka); + + if (!too_large) + n++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); +} + +static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) { + AvahiQueryJob *qj = data; + AvahiQueryScheduler *s; + AvahiDnsPacket *p; + unsigned n; + int b; + + assert(qj); + s = qj->scheduler; + + if (qj->done) { + /* Lets remove it from the history */ + job_free(s, qj); + return; + } + + assert(!s->known_answers); + + if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu))) + return; /* OOM */ + + b = packet_add_query_job(s, p, qj); + assert(b); /* An query must always fit in */ + n = 1; + + /* Try to fill up packet with more queries, if available */ + while (s->jobs) { + + if (!packet_add_query_job(s, p, s->jobs)) + break; + + n++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n); + + /* Now add known answers */ + append_known_answers_and_send(s, p); +} + +static AvahiQueryJob* find_scheduled_job(AvahiQueryScheduler *s, AvahiKey *key) { + AvahiQueryJob *qj; + + assert(s); + assert(key); + + for (qj = s->jobs; qj; qj = qj->jobs_next) { + assert(!qj->done); + + if (avahi_key_equal(qj->key, key)) + return qj; + } + + return NULL; +} + +static AvahiQueryJob* find_history_job(AvahiQueryScheduler *s, AvahiKey *key) { + AvahiQueryJob *qj; + + assert(s); + assert(key); + + for (qj = s->history; qj; qj = qj->jobs_next) { + assert(qj->done); + + if (avahi_key_equal(qj->key, key)) { + /* Check whether this entry is outdated */ + + if (avahi_age(&qj->delivery) > AVAHI_QUERY_HISTORY_MSEC*1000) { + /* it is outdated, so let's remove it */ + job_free(s, qj); + return NULL; + } + + return qj; + } + } + + return NULL; +} + +int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id) { + struct timeval tv; + AvahiQueryJob *qj; + + assert(s); + assert(key); + + if ((qj = find_history_job(s, key))) + return 0; + + avahi_elapse_time(&tv, immediately ? 0 : AVAHI_QUERY_DEFER_MSEC, 0); + + if ((qj = find_scheduled_job(s, key))) { + /* Duplicate questions suppression */ + + if (avahi_timeval_compare(&tv, &qj->delivery) < 0) { + /* If the new entry should be scheduled earlier, + * update the old entry */ + qj->delivery = tv; + avahi_time_event_update(qj->time_event, &qj->delivery); + } + + qj->n_posted++; + + } else { + + if (!(qj = job_new(s, key, 0))) + return 0; /* OOM */ + + qj->delivery = tv; + qj->time_event = avahi_time_event_new(s->time_event_queue, &qj->delivery, elapse_callback, qj); + } + + if (ret_id) + *ret_id = qj->id; + + return 1; +} + +void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key) { + AvahiQueryJob *qj; + + assert(s); + assert(key); + + /* This function is called whenever an incoming query was + * received. We drop scheduled queries that match. The keyword is + * "DUPLICATE QUESTION SUPPRESION". */ + + if ((qj = find_scheduled_job(s, key))) { + job_mark_done(s, qj); + return; + } + + /* Look if there's a history job for this key. If there is, just + * update the elapse time */ + if (!(qj = find_history_job(s, key))) + if (!(qj = job_new(s, key, 1))) + return; /* OOM */ + + gettimeofday(&qj->delivery, NULL); + job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0); +} + +int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id) { + AvahiQueryJob *qj; + + assert(s); + + /* Very short lived queries can withdraw an already scheduled item + * from the queue using this function, simply by passing the id + * returned by avahi_query_scheduler_post(). */ + + for (qj = s->jobs; qj; qj = qj->jobs_next) { + assert(!qj->done); + + if (qj->id == id) { + /* Entry found */ + + assert(qj->n_posted >= 1); + + if (--qj->n_posted <= 0) { + + /* We withdraw this job only if the calling object was + * the only remaining poster. (Usually this is the + * case since there should exist only one querier per + * key, but there are exceptions, notably reflected + * traffic.) */ + + job_free(s, qj); + return 1; + } + } + } + + return 0; +} diff --git a/3rdparty/QtZeroConf/avahi-core/query-sched.h b/3rdparty/QtZeroConf/avahi-core/query-sched.h new file mode 100644 index 000000000..b45520c96 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/query-sched.h @@ -0,0 +1,36 @@ +#ifndef fooqueryschedhfoo +#define fooqueryschedhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct AvahiQueryScheduler AvahiQueryScheduler; + +#include +#include "iface.h" + +AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i); +void avahi_query_scheduler_free(AvahiQueryScheduler *s); +void avahi_query_scheduler_clear(AvahiQueryScheduler *s); + +int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id); +int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id); +void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/resolve-address.c b/3rdparty/QtZeroConf/avahi-core/resolve-address.c new file mode 100644 index 000000000..dd4adbc45 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/resolve-address.c @@ -0,0 +1,268 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include + +#include "browse.h" + +#define TIMEOUT_MSEC 5000 + +struct AvahiSAddressResolver { + AvahiServer *server; + AvahiAddress address; + + AvahiSRecordBrowser *record_browser; + + AvahiSAddressResolverCallback callback; + void* userdata; + + AvahiRecord *ptr_record; + AvahiIfIndex interface; + AvahiProtocol protocol; + AvahiLookupResultFlags flags; + + int retry_with_multicast; + AvahiKey *key; + + AvahiTimeEvent *time_event; + + AVAHI_LLIST_FIELDS(AvahiSAddressResolver, resolver); +}; + +static void finish(AvahiSAddressResolver *r, AvahiResolverEvent event) { + assert(r); + + if (r->time_event) { + avahi_time_event_free(r->time_event); + r->time_event = NULL; + } + + switch (event) { + case AVAHI_RESOLVER_FAILURE: + r->callback(r, r->interface, r->protocol, event, &r->address, NULL, r->flags, r->userdata); + break; + + case AVAHI_RESOLVER_FOUND: + assert(r->ptr_record); + r->callback(r, r->interface, r->protocol, event, &r->address, r->ptr_record->data.ptr.name, r->flags, r->userdata); + break; + } +} + +static void time_event_callback(AvahiTimeEvent *e, void *userdata) { + AvahiSAddressResolver *r = userdata; + + assert(e); + assert(r); + + avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT); + finish(r, AVAHI_RESOLVER_FAILURE); +} + +static void start_timeout(AvahiSAddressResolver *r) { + struct timeval tv; + assert(r); + + if (r->time_event) + return; + + avahi_elapse_time(&tv, TIMEOUT_MSEC, 0); + r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); +} + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSAddressResolver *r = userdata; + + assert(rr); + assert(r); + + switch (event) { + case AVAHI_BROWSER_NEW: + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + + if (r->interface > 0 && interface != r->interface) + return; + + if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) + return; + + if (r->interface <= 0) + r->interface = interface; + + if (r->protocol == AVAHI_PROTO_UNSPEC) + r->protocol = protocol; + + if (!r->ptr_record) { + r->ptr_record = avahi_record_ref(record); + r->flags = flags; + + finish(r, AVAHI_RESOLVER_FOUND); + } + break; + + case AVAHI_BROWSER_REMOVE: + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + + if (r->ptr_record && avahi_record_equal_no_ttl(record, r->ptr_record)) { + avahi_record_unref(r->ptr_record); + r->ptr_record = NULL; + r->flags = flags; + + /** Look for a replacement */ + avahi_s_record_browser_restart(r->record_browser); + start_timeout(r); + } + + break; + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_ALL_FOR_NOW: + break; + + case AVAHI_BROWSER_FAILURE: + + if (r->retry_with_multicast) { + r->retry_with_multicast = 0; + + avahi_s_record_browser_free(r->record_browser); + r->record_browser = avahi_s_record_browser_new(r->server, r->interface, r->protocol, r->key, AVAHI_LOOKUP_USE_MULTICAST, record_browser_callback, r); + + if (r->record_browser) { + start_timeout(r); + break; + } + } + + r->flags = flags; + finish(r, AVAHI_RESOLVER_FAILURE); + break; + } +} + +AvahiSAddressResolver *avahi_s_address_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const AvahiAddress *address, + AvahiLookupFlags flags, + AvahiSAddressResolverCallback callback, + void* userdata) { + + AvahiSAddressResolver *r; + AvahiKey *k; + char n[AVAHI_DOMAIN_NAME_MAX]; + + assert(server); + assert(address); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, address->proto == AVAHI_PROTO_INET || address->proto == AVAHI_PROTO_INET6, AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + + avahi_reverse_lookup_name(address, n, sizeof(n)); + + if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + if (!(r = avahi_new(AvahiSAddressResolver, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + avahi_key_unref(k); + return NULL; + } + + r->server = server; + r->address = *address; + r->callback = callback; + r->userdata = userdata; + r->ptr_record = NULL; + r->interface = interface; + r->protocol = protocol; + r->flags = 0; + r->retry_with_multicast = 0; + r->key = k; + + r->record_browser = NULL; + AVAHI_LLIST_PREPEND(AvahiSAddressResolver, resolver, server->address_resolvers, r); + + r->time_event = NULL; + + if (!(flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA))) { + + if (!server->wide_area_lookup_engine || !avahi_wide_area_has_servers(server->wide_area_lookup_engine)) + flags |= AVAHI_LOOKUP_USE_MULTICAST; + else { + flags |= AVAHI_LOOKUP_USE_WIDE_AREA; + r->retry_with_multicast = 1; + } + } + + r->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); + + if (!r->record_browser) { + avahi_s_address_resolver_free(r); + return NULL; + } + + start_timeout(r); + + return r; +} + +void avahi_s_address_resolver_free(AvahiSAddressResolver *r) { + assert(r); + + AVAHI_LLIST_REMOVE(AvahiSAddressResolver, resolver, r->server->address_resolvers, r); + + if (r->record_browser) + avahi_s_record_browser_free(r->record_browser); + + if (r->time_event) + avahi_time_event_free(r->time_event); + + if (r->ptr_record) + avahi_record_unref(r->ptr_record); + + if (r->key) + avahi_key_unref(r->key); + + avahi_free(r); +} diff --git a/3rdparty/QtZeroConf/avahi-core/resolve-host-name.c b/3rdparty/QtZeroConf/avahi-core/resolve-host-name.c new file mode 100644 index 000000000..08f209b62 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/resolve-host-name.c @@ -0,0 +1,297 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include + +#include "browse.h" +#include "log.h" + +#define TIMEOUT_MSEC 5000 + +struct AvahiSHostNameResolver { + AvahiServer *server; + char *host_name; + + AvahiSRecordBrowser *record_browser_a; + AvahiSRecordBrowser *record_browser_aaaa; + + AvahiSHostNameResolverCallback callback; + void* userdata; + + AvahiRecord *address_record; + AvahiIfIndex interface; + AvahiProtocol protocol; + AvahiLookupResultFlags flags; + + AvahiTimeEvent *time_event; + + AVAHI_LLIST_FIELDS(AvahiSHostNameResolver, resolver); +}; + +static void finish(AvahiSHostNameResolver *r, AvahiResolverEvent event) { + assert(r); + + if (r->time_event) { + avahi_time_event_free(r->time_event); + r->time_event = NULL; + } + + switch (event) { + case AVAHI_RESOLVER_FOUND: { + AvahiAddress a; + + assert(r->address_record); + + switch (r->address_record->key->type) { + case AVAHI_DNS_TYPE_A: + a.proto = AVAHI_PROTO_INET; + a.data.ipv4 = r->address_record->data.a.address; + break; + + case AVAHI_DNS_TYPE_AAAA: + a.proto = AVAHI_PROTO_INET6; + a.data.ipv6 = r->address_record->data.aaaa.address; + break; + + default: + abort(); + } + + r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_FOUND, r->address_record->key->name, &a, r->flags, r->userdata); + break; + + } + + case AVAHI_RESOLVER_FAILURE: + + r->callback(r, r->interface, r->protocol, event, r->host_name, NULL, r->flags, r->userdata); + break; + } +} + +static void time_event_callback(AvahiTimeEvent *e, void *userdata) { + AvahiSHostNameResolver *r = userdata; + + assert(e); + assert(r); + + avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT); + finish(r, AVAHI_RESOLVER_FAILURE); +} + +static void start_timeout(AvahiSHostNameResolver *r) { + struct timeval tv; + assert(r); + + if (r->time_event) + return; + + avahi_elapse_time(&tv, TIMEOUT_MSEC, 0); + + r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); +} + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSHostNameResolver *r = userdata; + + assert(rr); + assert(r); + + + switch (event) { + case AVAHI_BROWSER_NEW: + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA); + + if (r->interface > 0 && interface != r->interface) + return; + + if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) + return; + + if (r->interface <= 0) + r->interface = interface; + + if (r->protocol == AVAHI_PROTO_UNSPEC) + r->protocol = protocol; + + if (!r->address_record) { + r->address_record = avahi_record_ref(record); + r->flags = flags; + + finish(r, AVAHI_RESOLVER_FOUND); + } + + break; + + case AVAHI_BROWSER_REMOVE: + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA); + + if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) { + avahi_record_unref(r->address_record); + r->address_record = NULL; + + r->flags = flags; + + + /** Look for a replacement */ + if (r->record_browser_aaaa) + avahi_s_record_browser_restart(r->record_browser_aaaa); + if (r->record_browser_a) + avahi_s_record_browser_restart(r->record_browser_a); + + start_timeout(r); + } + + break; + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_ALL_FOR_NOW: + /* Ignore */ + break; + + case AVAHI_BROWSER_FAILURE: + + /* Stop browsers */ + + if (r->record_browser_aaaa) + avahi_s_record_browser_free(r->record_browser_aaaa); + if (r->record_browser_a) + avahi_s_record_browser_free(r->record_browser_a); + + r->record_browser_a = r->record_browser_aaaa = NULL; + r->flags = flags; + + finish(r, AVAHI_RESOLVER_FAILURE); + break; + } +} + +AvahiSHostNameResolver *avahi_s_host_name_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *host_name, + AvahiProtocol aprotocol, + AvahiLookupFlags flags, + AvahiSHostNameResolverCallback callback, + void* userdata) { + + AvahiSHostNameResolver *r; + AvahiKey *k; + + assert(server); + assert(host_name); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_fqdn(host_name), AVAHI_ERR_INVALID_HOST_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + + if (!(r = avahi_new(AvahiSHostNameResolver, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + r->server = server; + r->host_name = avahi_normalize_name_strdup(host_name); + r->callback = callback; + r->userdata = userdata; + r->address_record = NULL; + r->interface = interface; + r->protocol = protocol; + r->flags = 0; + + r->record_browser_a = r->record_browser_aaaa = NULL; + + r->time_event = NULL; + + AVAHI_LLIST_PREPEND(AvahiSHostNameResolver, resolver, server->host_name_resolvers, r); + + r->record_browser_aaaa = r->record_browser_a = NULL; + + if (aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_UNSPEC) { + k = avahi_key_new(host_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A); + r->record_browser_a = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); + avahi_key_unref(k); + + if (!r->record_browser_a) + goto fail; + } + + if (aprotocol == AVAHI_PROTO_INET6 || aprotocol == AVAHI_PROTO_UNSPEC) { + k = avahi_key_new(host_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA); + r->record_browser_aaaa = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); + avahi_key_unref(k); + + if (!r->record_browser_aaaa) + goto fail; + } + + assert(r->record_browser_aaaa || r->record_browser_a); + + start_timeout(r); + + return r; + +fail: + avahi_s_host_name_resolver_free(r); + return NULL; +} + +void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r) { + assert(r); + + AVAHI_LLIST_REMOVE(AvahiSHostNameResolver, resolver, r->server->host_name_resolvers, r); + + if (r->record_browser_a) + avahi_s_record_browser_free(r->record_browser_a); + + if (r->record_browser_aaaa) + avahi_s_record_browser_free(r->record_browser_aaaa); + + if (r->time_event) + avahi_time_event_free(r->time_event); + + if (r->address_record) + avahi_record_unref(r->address_record); + + avahi_free(r->host_name); + avahi_free(r); +} diff --git a/3rdparty/QtZeroConf/avahi-core/resolve-service.c b/3rdparty/QtZeroConf/avahi-core/resolve-service.c new file mode 100644 index 000000000..3377a509c --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/resolve-service.c @@ -0,0 +1,489 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include "browse.h" +#include "log.h" + +#define TIMEOUT_MSEC 5000 + +struct AvahiSServiceResolver { + AvahiServer *server; + char *service_name; + char *service_type; + char *domain_name; + AvahiProtocol address_protocol; + + AvahiIfIndex interface; + AvahiProtocol protocol; + + AvahiSRecordBrowser *record_browser_srv; + AvahiSRecordBrowser *record_browser_txt; + AvahiSRecordBrowser *record_browser_a; + AvahiSRecordBrowser *record_browser_aaaa; + + AvahiRecord *srv_record, *txt_record, *address_record; + AvahiLookupResultFlags srv_flags, txt_flags, address_flags; + + AvahiSServiceResolverCallback callback; + void* userdata; + AvahiLookupFlags user_flags; + + AvahiTimeEvent *time_event; + + AVAHI_LLIST_FIELDS(AvahiSServiceResolver, resolver); +}; + +static void finish(AvahiSServiceResolver *r, AvahiResolverEvent event) { + AvahiLookupResultFlags flags; + + assert(r); + + if (r->time_event) { + avahi_time_event_free(r->time_event); + r->time_event = NULL; + } + + flags = + r->txt_flags | + r->srv_flags | + r->address_flags; + + switch (event) { + case AVAHI_RESOLVER_FAILURE: + + r->callback( + r, + r->interface, + r->protocol, + event, + r->service_name, + r->service_type, + r->domain_name, + NULL, + NULL, + 0, + NULL, + flags, + r->userdata); + + break; + + case AVAHI_RESOLVER_FOUND: { + AvahiAddress a; + + assert(event == AVAHI_RESOLVER_FOUND); + + assert(r->srv_record); + + if (r->address_record) { + switch (r->address_record->key->type) { + case AVAHI_DNS_TYPE_A: + a.proto = AVAHI_PROTO_INET; + a.data.ipv4 = r->address_record->data.a.address; + break; + + case AVAHI_DNS_TYPE_AAAA: + a.proto = AVAHI_PROTO_INET6; + a.data.ipv6 = r->address_record->data.aaaa.address; + break; + + default: + assert(0); + } + } + + r->callback( + r, + r->interface, + r->protocol, + event, + r->service_name, + r->service_type, + r->domain_name, + r->srv_record->data.srv.name, + r->address_record ? &a : NULL, + r->srv_record->data.srv.port, + r->txt_record ? r->txt_record->data.txt.string_list : NULL, + flags, + r->userdata); + + break; + } + } +} + +static void time_event_callback(AvahiTimeEvent *e, void *userdata) { + AvahiSServiceResolver *r = userdata; + + assert(e); + assert(r); + + avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT); + finish(r, AVAHI_RESOLVER_FAILURE); +} + +static void start_timeout(AvahiSServiceResolver *r) { + struct timeval tv; + assert(r); + + if (r->time_event) + return; + + avahi_elapse_time(&tv, TIMEOUT_MSEC, 0); + + r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); +} + +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + + AvahiSServiceResolver *r = userdata; + + assert(rr); + assert(r); + + if (rr == r->record_browser_aaaa || rr == r->record_browser_a) + r->address_flags = flags; + else if (rr == r->record_browser_srv) + r->srv_flags = flags; + else if (rr == r->record_browser_txt) + r->txt_flags = flags; + + switch (event) { + + case AVAHI_BROWSER_NEW: { + int changed = 0; + assert(record); + + if (r->interface > 0 && interface > 0 && interface != r->interface) + return; + + if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) + return; + + if (r->interface <= 0) + r->interface = interface; + + if (r->protocol == AVAHI_PROTO_UNSPEC) + r->protocol = protocol; + + switch (record->key->type) { + case AVAHI_DNS_TYPE_SRV: + if (!r->srv_record) { + r->srv_record = avahi_record_ref(record); + changed = 1; + + if (r->record_browser_a) { + avahi_s_record_browser_free(r->record_browser_a); + r->record_browser_a = NULL; + } + + if (r->record_browser_aaaa) { + avahi_s_record_browser_free(r->record_browser_aaaa); + r->record_browser_aaaa = NULL; + } + + if (!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)) { + + if (r->address_protocol == AVAHI_PROTO_INET || r->address_protocol == AVAHI_PROTO_UNSPEC) { + AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A); + r->record_browser_a = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); + avahi_key_unref(k); + } + + if (r->address_protocol == AVAHI_PROTO_INET6 || r->address_protocol == AVAHI_PROTO_UNSPEC) { + AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA); + r->record_browser_aaaa = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); + avahi_key_unref(k); + } + } + } + break; + + case AVAHI_DNS_TYPE_TXT: + + assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT)); + + if (!r->txt_record) { + r->txt_record = avahi_record_ref(record); + changed = 1; + } + break; + + case AVAHI_DNS_TYPE_A: + case AVAHI_DNS_TYPE_AAAA: + + assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)); + + if (!r->address_record) { + r->address_record = avahi_record_ref(record); + changed = 1; + } + break; + + default: + abort(); + } + + + if (changed && + r->srv_record && + (r->txt_record || (r->user_flags & AVAHI_LOOKUP_NO_TXT)) && + (r->address_record || (r->user_flags & AVAHI_LOOKUP_NO_ADDRESS))) + finish(r, AVAHI_RESOLVER_FOUND); + + break; + + } + + case AVAHI_BROWSER_REMOVE: + + assert(record); + + switch (record->key->type) { + case AVAHI_DNS_TYPE_SRV: + + if (r->srv_record && avahi_record_equal_no_ttl(record, r->srv_record)) { + avahi_record_unref(r->srv_record); + r->srv_record = NULL; + + if (r->record_browser_a) { + avahi_s_record_browser_free(r->record_browser_a); + r->record_browser_a = NULL; + } + + if (r->record_browser_aaaa) { + avahi_s_record_browser_free(r->record_browser_aaaa); + r->record_browser_aaaa = NULL; + } + + /** Look for a replacement */ + avahi_s_record_browser_restart(r->record_browser_srv); + start_timeout(r); + } + + break; + + case AVAHI_DNS_TYPE_TXT: + + assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT)); + + if (r->txt_record && avahi_record_equal_no_ttl(record, r->txt_record)) { + avahi_record_unref(r->txt_record); + r->txt_record = NULL; + + /** Look for a replacement */ + avahi_s_record_browser_restart(r->record_browser_txt); + start_timeout(r); + } + break; + + case AVAHI_DNS_TYPE_A: + case AVAHI_DNS_TYPE_AAAA: + + assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)); + + if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) { + avahi_record_unref(r->address_record); + r->address_record = NULL; + + /** Look for a replacement */ + if (r->record_browser_aaaa) + avahi_s_record_browser_restart(r->record_browser_aaaa); + if (r->record_browser_a) + avahi_s_record_browser_restart(r->record_browser_a); + start_timeout(r); + } + break; + + default: + abort(); + } + + break; + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_ALL_FOR_NOW: + break; + + case AVAHI_BROWSER_FAILURE: + + if (rr == r->record_browser_a && r->record_browser_aaaa) { + /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */ + avahi_s_record_browser_free(r->record_browser_a); + r->record_browser_a = NULL; + break; + } + + if (rr == r->record_browser_aaaa && r->record_browser_a) { + /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */ + avahi_s_record_browser_free(r->record_browser_aaaa); + r->record_browser_aaaa = NULL; + break; + } + + /* Hmm, everything's lost, tell the user */ + + if (r->record_browser_srv) + avahi_s_record_browser_free(r->record_browser_srv); + if (r->record_browser_txt) + avahi_s_record_browser_free(r->record_browser_txt); + if (r->record_browser_a) + avahi_s_record_browser_free(r->record_browser_a); + if (r->record_browser_aaaa) + avahi_s_record_browser_free(r->record_browser_aaaa); + + r->record_browser_srv = r->record_browser_txt = r->record_browser_a = r->record_browser_aaaa = NULL; + + finish(r, AVAHI_RESOLVER_FAILURE); + break; + } +} + +AvahiSServiceResolver *avahi_s_service_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain, + AvahiProtocol aprotocol, + AvahiLookupFlags flags, + AvahiSServiceResolverCallback callback, + void* userdata) { + + AvahiSServiceResolver *r; + AvahiKey *k; + char n[AVAHI_DOMAIN_NAME_MAX]; + int ret; + + assert(server); + assert(type); + assert(callback); + + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !name || avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); + AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), AVAHI_ERR_INVALID_FLAGS); + + if (!domain) + domain = server->domain_name; + + if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain)) < 0) { + avahi_server_set_errno(server, ret); + return NULL; + } + + if (!(r = avahi_new(AvahiSServiceResolver, 1))) { + avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); + return NULL; + } + + r->server = server; + r->service_name = avahi_strdup(name); + r->service_type = avahi_normalize_name_strdup(type); + r->domain_name = avahi_normalize_name_strdup(domain); + r->callback = callback; + r->userdata = userdata; + r->address_protocol = aprotocol; + r->srv_record = r->txt_record = r->address_record = NULL; + r->srv_flags = r->txt_flags = r->address_flags = 0; + r->interface = interface; + r->protocol = protocol; + r->user_flags = flags; + r->record_browser_a = r->record_browser_aaaa = r->record_browser_srv = r->record_browser_txt = NULL; + r->time_event = NULL; + AVAHI_LLIST_PREPEND(AvahiSServiceResolver, resolver, server->service_resolvers, r); + + k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV); + r->record_browser_srv = avahi_s_record_browser_new(server, interface, protocol, k, flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); + avahi_key_unref(k); + + if (!r->record_browser_srv) { + avahi_s_service_resolver_free(r); + return NULL; + } + + if (!(flags & AVAHI_LOOKUP_NO_TXT)) { + k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT); + r->record_browser_txt = avahi_s_record_browser_new(server, interface, protocol, k, flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); + avahi_key_unref(k); + + if (!r->record_browser_txt) { + avahi_s_service_resolver_free(r); + return NULL; + } + } + + start_timeout(r); + + return r; +} + +void avahi_s_service_resolver_free(AvahiSServiceResolver *r) { + assert(r); + + AVAHI_LLIST_REMOVE(AvahiSServiceResolver, resolver, r->server->service_resolvers, r); + + if (r->time_event) + avahi_time_event_free(r->time_event); + + if (r->record_browser_srv) + avahi_s_record_browser_free(r->record_browser_srv); + if (r->record_browser_txt) + avahi_s_record_browser_free(r->record_browser_txt); + if (r->record_browser_a) + avahi_s_record_browser_free(r->record_browser_a); + if (r->record_browser_aaaa) + avahi_s_record_browser_free(r->record_browser_aaaa); + + if (r->srv_record) + avahi_record_unref(r->srv_record); + if (r->txt_record) + avahi_record_unref(r->txt_record); + if (r->address_record) + avahi_record_unref(r->address_record); + + avahi_free(r->service_name); + avahi_free(r->service_type); + avahi_free(r->domain_name); + avahi_free(r); +} diff --git a/3rdparty/QtZeroConf/avahi-core/response-sched.c b/3rdparty/QtZeroConf/avahi-core/response-sched.c new file mode 100644 index 000000000..abac0a1e5 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/response-sched.c @@ -0,0 +1,511 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include "response-sched.h" +#include "log.h" +#include "rr-util.h" + +/* Local packets are supressed this long after sending them */ +#define AVAHI_RESPONSE_HISTORY_MSEC 500 + +/* Local packets are deferred this long before sending them */ +#define AVAHI_RESPONSE_DEFER_MSEC 20 + +/* Additional jitter for deferred packets */ +#define AVAHI_RESPONSE_JITTER_MSEC 100 + +/* Remote packets can suppress local traffic as long as this value */ +#define AVAHI_RESPONSE_SUPPRESS_MSEC 700 + +typedef struct AvahiResponseJob AvahiResponseJob; + +typedef enum { + AVAHI_SCHEDULED, + AVAHI_DONE, + AVAHI_SUPPRESSED +} AvahiResponseJobState; + +struct AvahiResponseJob { + AvahiResponseScheduler *scheduler; + AvahiTimeEvent *time_event; + + AvahiResponseJobState state; + struct timeval delivery; + + AvahiRecord *record; + int flush_cache; + AvahiAddress querier; + int querier_valid; + + AVAHI_LLIST_FIELDS(AvahiResponseJob, jobs); +}; + +struct AvahiResponseScheduler { + AvahiInterface *interface; + AvahiTimeEventQueue *time_event_queue; + + AVAHI_LLIST_HEAD(AvahiResponseJob, jobs); + AVAHI_LLIST_HEAD(AvahiResponseJob, history); + AVAHI_LLIST_HEAD(AvahiResponseJob, suppressed); +}; + +static AvahiResponseJob* job_new(AvahiResponseScheduler *s, AvahiRecord *record, AvahiResponseJobState state) { + AvahiResponseJob *rj; + + assert(s); + assert(record); + + if (!(rj = avahi_new(AvahiResponseJob, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; + } + + rj->scheduler = s; + rj->record = avahi_record_ref(record); + rj->time_event = NULL; + rj->flush_cache = 0; + rj->querier_valid = 0; + + if ((rj->state = state) == AVAHI_SCHEDULED) + AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->jobs, rj); + else if (rj->state == AVAHI_DONE) + AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->history, rj); + else /* rj->state == AVAHI_SUPPRESSED */ + AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->suppressed, rj); + + return rj; +} + +static void job_free(AvahiResponseScheduler *s, AvahiResponseJob *rj) { + assert(s); + assert(rj); + + if (rj->time_event) + avahi_time_event_free(rj->time_event); + + if (rj->state == AVAHI_SCHEDULED) + AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj); + else if (rj->state == AVAHI_DONE) + AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->history, rj); + else /* rj->state == AVAHI_SUPPRESSED */ + AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->suppressed, rj); + + avahi_record_unref(rj->record); + avahi_free(rj); +} + +static void elapse_callback(AvahiTimeEvent *e, void* data); + +static void job_set_elapse_time(AvahiResponseScheduler *s, AvahiResponseJob *rj, unsigned msec, unsigned jitter) { + struct timeval tv; + + assert(s); + assert(rj); + + avahi_elapse_time(&tv, msec, jitter); + + if (rj->time_event) + avahi_time_event_update(rj->time_event, &tv); + else + rj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, rj); +} + +static void job_mark_done(AvahiResponseScheduler *s, AvahiResponseJob *rj) { + assert(s); + assert(rj); + + assert(rj->state == AVAHI_SCHEDULED); + + AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj); + AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->history, rj); + + rj->state = AVAHI_DONE; + + job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0); + + gettimeofday(&rj->delivery, NULL); +} + +AvahiResponseScheduler *avahi_response_scheduler_new(AvahiInterface *i) { + AvahiResponseScheduler *s; + assert(i); + + if (!(s = avahi_new(AvahiResponseScheduler, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; + } + + s->interface = i; + s->time_event_queue = i->monitor->server->time_event_queue; + + AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->jobs); + AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->history); + AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->suppressed); + + return s; +} + +void avahi_response_scheduler_free(AvahiResponseScheduler *s) { + assert(s); + + avahi_response_scheduler_clear(s); + avahi_free(s); +} + +void avahi_response_scheduler_clear(AvahiResponseScheduler *s) { + assert(s); + + while (s->jobs) + job_free(s, s->jobs); + while (s->history) + job_free(s, s->history); + while (s->suppressed) + job_free(s, s->suppressed); +} + +static void enumerate_aux_records_callback(AVAHI_GCC_UNUSED AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) { + AvahiResponseJob *rj = userdata; + + assert(r); + assert(rj); + + avahi_response_scheduler_post(rj->scheduler, r, flush_cache, rj->querier_valid ? &rj->querier : NULL, 0); +} + +static int packet_add_response_job(AvahiResponseScheduler *s, AvahiDnsPacket *p, AvahiResponseJob *rj) { + assert(s); + assert(p); + assert(rj); + + /* Try to add this record to the packet */ + if (!avahi_dns_packet_append_record(p, rj->record, rj->flush_cache, 0)) + return 0; + + /* Ok, this record will definitely be sent, so schedule the + * auxilliary packets, too */ + avahi_server_enumerate_aux_records(s->interface->monitor->server, s->interface, rj->record, enumerate_aux_records_callback, rj); + job_mark_done(s, rj); + + return 1; +} + +static void send_response_packet(AvahiResponseScheduler *s, AvahiResponseJob *rj) { + AvahiDnsPacket *p; + unsigned n; + + assert(s); + assert(rj); + + if (!(p = avahi_dns_packet_new_response(s->interface->hardware->mtu, 1))) + return; /* OOM */ + n = 1; + + /* Put it in the packet. */ + if (packet_add_response_job(s, p, rj)) { + + /* Try to fill up packet with more responses, if available */ + while (s->jobs) { + + if (!packet_add_response_job(s, p, s->jobs)) + break; + + n++; + } + + } else { + size_t size; + + avahi_dns_packet_free(p); + + /* OK, the packet was too small, so create one that fits */ + size = avahi_record_get_estimate_size(rj->record) + AVAHI_DNS_PACKET_HEADER_SIZE; + + if (!(p = avahi_dns_packet_new_response(size + AVAHI_DNS_PACKET_EXTRA_SIZE, 1))) + return; /* OOM */ + + if (!packet_add_response_job(s, p, rj)) { + avahi_dns_packet_free(p); + + avahi_log_warn("Record too large, cannot send"); + job_mark_done(s, rj); + return; + } + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); +} + +static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) { + AvahiResponseJob *rj = data; + + assert(rj); + + if (rj->state == AVAHI_DONE || rj->state == AVAHI_SUPPRESSED) + job_free(rj->scheduler, rj); /* Lets drop this entry */ + else + send_response_packet(rj->scheduler, rj); +} + +static AvahiResponseJob* find_scheduled_job(AvahiResponseScheduler *s, AvahiRecord *record) { + AvahiResponseJob *rj; + + assert(s); + assert(record); + + for (rj = s->jobs; rj; rj = rj->jobs_next) { + assert(rj->state == AVAHI_SCHEDULED); + + if (avahi_record_equal_no_ttl(rj->record, record)) + return rj; + } + + return NULL; +} + +static AvahiResponseJob* find_history_job(AvahiResponseScheduler *s, AvahiRecord *record) { + AvahiResponseJob *rj; + + assert(s); + assert(record); + + for (rj = s->history; rj; rj = rj->jobs_next) { + assert(rj->state == AVAHI_DONE); + + if (avahi_record_equal_no_ttl(rj->record, record)) { + /* Check whether this entry is outdated */ + +/* avahi_log_debug("history age: %u", (unsigned) (avahi_age(&rj->delivery)/1000)); */ + + if (avahi_age(&rj->delivery)/1000 > AVAHI_RESPONSE_HISTORY_MSEC) { + /* it is outdated, so let's remove it */ + job_free(s, rj); + return NULL; + } + + return rj; + } + } + + return NULL; +} + +static AvahiResponseJob* find_suppressed_job(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) { + AvahiResponseJob *rj; + + assert(s); + assert(record); + assert(querier); + + for (rj = s->suppressed; rj; rj = rj->jobs_next) { + assert(rj->state == AVAHI_SUPPRESSED); + assert(rj->querier_valid); + + if (avahi_record_equal_no_ttl(rj->record, record) && + avahi_address_cmp(&rj->querier, querier) == 0) { + /* Check whether this entry is outdated */ + + if (avahi_age(&rj->delivery) > AVAHI_RESPONSE_SUPPRESS_MSEC*1000) { + /* it is outdated, so let's remove it */ + job_free(s, rj); + return NULL; + } + + return rj; + } + } + + return NULL; +} + +int avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) { + AvahiResponseJob *rj; + struct timeval tv; +/* char *t; */ + + assert(s); + assert(record); + + assert(!avahi_key_is_pattern(record->key)); + +/* t = avahi_record_to_string(record); */ +/* avahi_log_debug("post %i %s", immediately, t); */ +/* avahi_free(t); */ + + /* Check whether this response is suppressed */ + if (querier && + (rj = find_suppressed_job(s, record, querier)) && + avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && + rj->record->ttl >= record->ttl/2) { + +/* avahi_log_debug("Response suppressed by known answer suppression."); */ + return 0; + } + + /* Check if we already sent this response recently */ + if ((rj = find_history_job(s, record))) { + + if (avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && + rj->record->ttl >= record->ttl/2 && + (rj->flush_cache || !flush_cache)) { +/* avahi_log_debug("Response suppressed by local duplicate suppression (history)"); */ + return 0; + } + + /* Outdated ... */ + job_free(s, rj); + } + + avahi_elapse_time(&tv, immediately ? 0 : AVAHI_RESPONSE_DEFER_MSEC, immediately ? 0 : AVAHI_RESPONSE_JITTER_MSEC); + + if ((rj = find_scheduled_job(s, record))) { +/* avahi_log_debug("Response suppressed by local duplicate suppression (scheduled)"); */ + + /* Update a little ... */ + + /* Update the time if the new is prior to the old */ + if (avahi_timeval_compare(&tv, &rj->delivery) < 0) { + rj->delivery = tv; + avahi_time_event_update(rj->time_event, &rj->delivery); + } + + /* Update the flush cache bit */ + if (flush_cache) + rj->flush_cache = 1; + + /* Update the querier field */ + if (!querier || (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) != 0)) + rj->querier_valid = 0; + + /* Update record data (just for the TTL) */ + avahi_record_unref(rj->record); + rj->record = avahi_record_ref(record); + + return 1; + } else { +/* avahi_log_debug("Accepted new response job."); */ + + /* Create a new job and schedule it */ + if (!(rj = job_new(s, record, AVAHI_SCHEDULED))) + return 0; /* OOM */ + + rj->delivery = tv; + rj->time_event = avahi_time_event_new(s->time_event_queue, &rj->delivery, elapse_callback, rj); + rj->flush_cache = flush_cache; + + if ((rj->querier_valid = !!querier)) + rj->querier = *querier; + + return 1; + } +} + +void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache) { + AvahiResponseJob *rj; + assert(s); + + /* This function is called whenever an incoming response was + * receieved. We drop scheduled responses which match here. The + * keyword is "DUPLICATE ANSWER SUPPRESION". */ + + if ((rj = find_scheduled_job(s, record))) { + + if ((!rj->flush_cache || flush_cache) && /* flush cache bit was set correctly */ + avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */ + record->ttl >= rj->record->ttl/2) { /* sensible TTL */ + + /* A matching entry was found, so let's mark it done */ +/* avahi_log_debug("Response suppressed by distributed duplicate suppression"); */ + job_mark_done(s, rj); + } + + return; + } + + if ((rj = find_history_job(s, record))) { + /* Found a history job, let's update it */ + avahi_record_unref(rj->record); + rj->record = avahi_record_ref(record); + } else + /* Found no existing history job, so let's create a new one */ + if (!(rj = job_new(s, record, AVAHI_DONE))) + return; /* OOM */ + + rj->flush_cache = flush_cache; + rj->querier_valid = 0; + + gettimeofday(&rj->delivery, NULL); + job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0); +} + +void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) { + AvahiResponseJob *rj; + + assert(s); + assert(record); + assert(querier); + + if ((rj = find_scheduled_job(s, record))) { + + if (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) == 0 && /* same originator */ + avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */ + record->ttl >= rj->record->ttl/2) { /* sensible TTL */ + + /* A matching entry was found, so let's drop it */ +/* avahi_log_debug("Known answer suppression active!"); */ + job_free(s, rj); + } + } + + if ((rj = find_suppressed_job(s, record, querier))) { + + /* Let's update the old entry */ + avahi_record_unref(rj->record); + rj->record = avahi_record_ref(record); + + } else { + + /* Create a new entry */ + if (!(rj = job_new(s, record, AVAHI_SUPPRESSED))) + return; /* OOM */ + rj->querier_valid = 1; + rj->querier = *querier; + } + + gettimeofday(&rj->delivery, NULL); + job_set_elapse_time(s, rj, AVAHI_RESPONSE_SUPPRESS_MSEC, 0); +} + +void avahi_response_scheduler_force(AvahiResponseScheduler *s) { + assert(s); + + /* Send all scheduled responses immediately */ + while (s->jobs) + send_response_packet(s, s->jobs); +} diff --git a/3rdparty/QtZeroConf/avahi-core/response-sched.h b/3rdparty/QtZeroConf/avahi-core/response-sched.h new file mode 100644 index 000000000..548839e83 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/response-sched.h @@ -0,0 +1,37 @@ +#ifndef fooresponseschedhfoo +#define fooresponseschedhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct AvahiResponseScheduler AvahiResponseScheduler; + +#include +#include "iface.h" + +AvahiResponseScheduler *avahi_response_scheduler_new(AvahiInterface *i); +void avahi_response_scheduler_free(AvahiResponseScheduler *s); +void avahi_response_scheduler_clear(AvahiResponseScheduler *s); +void avahi_response_scheduler_force(AvahiResponseScheduler *s); + +int avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately); +void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache); +void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/rr-util.h b/3rdparty/QtZeroConf/avahi-core/rr-util.h new file mode 100644 index 000000000..0eebc0017 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/rr-util.h @@ -0,0 +1,62 @@ +#ifndef foorrutilhfoo +#define foorrutilhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "rr.h" + +AVAHI_C_DECL_BEGIN + +/** Creaze new AvahiKey object based on an existing key but replaceing the type by CNAME */ +AvahiKey *avahi_key_new_cname(AvahiKey *key); + +/** Match a key to a key pattern. The pattern has a type of +AVAHI_DNS_CLASS_ANY, the classes are taken to be equal. Same for the +type. If the pattern has neither class nor type with ANY constants, +this function is identical to avahi_key_equal(). In contrast to +avahi_equal() this function is not commutative. */ +int avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k); + +/** Check whether a key is a pattern key, i.e. the class/type has a + * value of AVAHI_DNS_CLASS_ANY/AVAHI_DNS_TYPE_ANY */ +int avahi_key_is_pattern(const AvahiKey *k); + +/** Returns a maximum estimate for the space that is needed to store + * this key in a DNS packet. */ +size_t avahi_key_get_estimate_size(AvahiKey *k); + +/** Returns a maximum estimate for the space that is needed to store + * the record in a DNS packet. */ +size_t avahi_record_get_estimate_size(AvahiRecord *r); + +/** Do a mDNS spec conforming lexicographical comparison of the two + * records. Return a negative value if a < b, a positive if a > b, + * zero if equal. */ +int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b); + +/** Return 1 if the specified record is an mDNS goodbye record. i.e. TTL is zero. */ +int avahi_record_is_goodbye(AvahiRecord *r); + +/** Make a deep copy of an AvahiRecord object */ +AvahiRecord *avahi_record_copy(AvahiRecord *r); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/rr.c b/3rdparty/QtZeroConf/avahi-core/rr.c new file mode 100644 index 000000000..7fa0beebf --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/rr.c @@ -0,0 +1,733 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "rr.h" +#include "log.h" +#include "util.h" +#include "hashmap.h" +#include "domain-util.h" +#include "rr-util.h" +#include "addr-util.h" + +AvahiKey *avahi_key_new(const char *name, uint16_t class, uint16_t type) { + AvahiKey *k; + assert(name); + + if (!(k = avahi_new(AvahiKey, 1))) { + avahi_log_error("avahi_new() failed."); + return NULL; + } + + if (!(k->name = avahi_normalize_name_strdup(name))) { + avahi_log_error("avahi_normalize_name() failed."); + avahi_free(k); + return NULL; + } + + k->ref = 1; + k->clazz = class; + k->type = type; + + return k; +} + +AvahiKey *avahi_key_new_cname(AvahiKey *key) { + assert(key); + + if (key->clazz != AVAHI_DNS_CLASS_IN) + return NULL; + + if (key->type == AVAHI_DNS_TYPE_CNAME) + return NULL; + + return avahi_key_new(key->name, key->clazz, AVAHI_DNS_TYPE_CNAME); +} + +AvahiKey *avahi_key_ref(AvahiKey *k) { + assert(k); + assert(k->ref >= 1); + + k->ref++; + + return k; +} + +void avahi_key_unref(AvahiKey *k) { + assert(k); + assert(k->ref >= 1); + + if ((--k->ref) <= 0) { + avahi_free(k->name); + avahi_free(k); + } +} + +AvahiRecord *avahi_record_new(AvahiKey *k, uint32_t ttl) { + AvahiRecord *r; + + assert(k); + + if (!(r = avahi_new(AvahiRecord, 1))) { + avahi_log_error("avahi_new() failed."); + return NULL; + } + + r->ref = 1; + r->key = avahi_key_ref(k); + + memset(&r->data, 0, sizeof(r->data)); + + r->ttl = ttl != (uint32_t) -1 ? ttl : AVAHI_DEFAULT_TTL; + + return r; +} + +AvahiRecord *avahi_record_new_full(const char *name, uint16_t class, uint16_t type, uint32_t ttl) { + AvahiRecord *r; + AvahiKey *k; + + assert(name); + + if (!(k = avahi_key_new(name, class, type))) { + avahi_log_error("avahi_key_new() failed."); + return NULL; + } + + r = avahi_record_new(k, ttl); + avahi_key_unref(k); + + if (!r) { + avahi_log_error("avahi_record_new() failed."); + return NULL; + } + + return r; +} + +AvahiRecord *avahi_record_ref(AvahiRecord *r) { + assert(r); + assert(r->ref >= 1); + + r->ref++; + return r; +} + +void avahi_record_unref(AvahiRecord *r) { + assert(r); + assert(r->ref >= 1); + + if ((--r->ref) <= 0) { + switch (r->key->type) { + + case AVAHI_DNS_TYPE_SRV: + avahi_free(r->data.srv.name); + break; + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + avahi_free(r->data.ptr.name); + break; + + case AVAHI_DNS_TYPE_HINFO: + avahi_free(r->data.hinfo.cpu); + avahi_free(r->data.hinfo.os); + break; + + case AVAHI_DNS_TYPE_TXT: + avahi_string_list_free(r->data.txt.string_list); + break; + + case AVAHI_DNS_TYPE_A: + case AVAHI_DNS_TYPE_AAAA: + break; + + default: + avahi_free(r->data.generic.data); + } + + avahi_key_unref(r->key); + avahi_free(r); + } +} + +const char *avahi_dns_class_to_string(uint16_t class) { + if (class & AVAHI_DNS_CACHE_FLUSH) + return "FLUSH"; + + switch (class) { + case AVAHI_DNS_CLASS_IN: + return "IN"; + case AVAHI_DNS_CLASS_ANY: + return "ANY"; + default: + return NULL; + } +} + +const char *avahi_dns_type_to_string(uint16_t type) { + switch (type) { + case AVAHI_DNS_TYPE_CNAME: + return "CNAME"; + case AVAHI_DNS_TYPE_A: + return "A"; + case AVAHI_DNS_TYPE_AAAA: + return "AAAA"; + case AVAHI_DNS_TYPE_PTR: + return "PTR"; + case AVAHI_DNS_TYPE_HINFO: + return "HINFO"; + case AVAHI_DNS_TYPE_TXT: + return "TXT"; + case AVAHI_DNS_TYPE_SRV: + return "SRV"; + case AVAHI_DNS_TYPE_ANY: + return "ANY"; + case AVAHI_DNS_TYPE_SOA: + return "SOA"; + case AVAHI_DNS_TYPE_NS: + return "NS"; + default: + return NULL; + } +} + +char *avahi_key_to_string(const AvahiKey *k) { + char class[16], type[16]; + const char *c, *t; + + assert(k); + assert(k->ref >= 1); + + /* According to RFC3597 */ + + if (!(c = avahi_dns_class_to_string(k->clazz))) { + snprintf(class, sizeof(class), "CLASS%u", k->clazz); + c = class; + } + + if (!(t = avahi_dns_type_to_string(k->type))) { + snprintf(type, sizeof(type), "TYPE%u", k->type); + t = type; + } + + return avahi_strdup_printf("%s\t%s\t%s", k->name, c, t); +} + +char *avahi_record_to_string(const AvahiRecord *r) { + char *p, *s; + char buf[1024], *t = NULL, *d = NULL; + + assert(r); + assert(r->ref >= 1); + + switch (r->key->type) { + case AVAHI_DNS_TYPE_A: + inet_ntop(AF_INET, &r->data.a.address.address, t = buf, sizeof(buf)); + break; + + case AVAHI_DNS_TYPE_AAAA: + inet_ntop(AF_INET6, &r->data.aaaa.address.address, t = buf, sizeof(buf)); + break; + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + + t = r->data.ptr.name; + break; + + case AVAHI_DNS_TYPE_TXT: + t = d = avahi_string_list_to_string(r->data.txt.string_list); + break; + + case AVAHI_DNS_TYPE_HINFO: + + snprintf(t = buf, sizeof(buf), "\"%s\" \"%s\"", r->data.hinfo.cpu, r->data.hinfo.os); + break; + + case AVAHI_DNS_TYPE_SRV: + + snprintf(t = buf, sizeof(buf), "%u %u %u %s", + r->data.srv.priority, + r->data.srv.weight, + r->data.srv.port, + r->data.srv.name); + + break; + + default: { + + uint8_t *c; + uint16_t n; + int i; + char *e; + + /* According to RFC3597 */ + + snprintf(t = buf, sizeof(buf), "\\# %u", r->data.generic.size); + + e = strchr(t, 0); + + for (c = r->data.generic.data, n = r->data.generic.size, i = 0; + n > 0 && i < 20; + c ++, n --, i++) { + + sprintf(e, " %02X", *c); + e = strchr(e, 0); + } + + break; + } + } + + p = avahi_key_to_string(r->key); + s = avahi_strdup_printf("%s %s ; ttl=%u", p, t, r->ttl); + avahi_free(p); + avahi_free(d); + + return s; +} + +int avahi_key_equal(const AvahiKey *a, const AvahiKey *b) { + assert(a); + assert(b); + + if (a == b) + return 1; + + return avahi_domain_equal(a->name, b->name) && + a->type == b->type && + a->clazz == b->clazz; +} + +int avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) { + assert(pattern); + assert(k); + + assert(!avahi_key_is_pattern(k)); + + if (pattern == k) + return 1; + + return avahi_domain_equal(pattern->name, k->name) && + (pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) && + (pattern->clazz == k->clazz || pattern->clazz == AVAHI_DNS_CLASS_ANY); +} + +int avahi_key_is_pattern(const AvahiKey *k) { + assert(k); + + return + k->type == AVAHI_DNS_TYPE_ANY || + k->clazz == AVAHI_DNS_CLASS_ANY; +} + +unsigned avahi_key_hash(const AvahiKey *k) { + assert(k); + + return + avahi_domain_hash(k->name) + + k->type + + k->clazz; +} + +static int rdata_equal(const AvahiRecord *a, const AvahiRecord *b) { + assert(a); + assert(b); + assert(a->key->type == b->key->type); + + switch (a->key->type) { + case AVAHI_DNS_TYPE_SRV: + return + a->data.srv.priority == b->data.srv.priority && + a->data.srv.weight == b->data.srv.weight && + a->data.srv.port == b->data.srv.port && + avahi_domain_equal(a->data.srv.name, b->data.srv.name); + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + return avahi_domain_equal(a->data.ptr.name, b->data.ptr.name); + + case AVAHI_DNS_TYPE_HINFO: + return + !strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu) && + !strcmp(a->data.hinfo.os, b->data.hinfo.os); + + case AVAHI_DNS_TYPE_TXT: + return avahi_string_list_equal(a->data.txt.string_list, b->data.txt.string_list); + + case AVAHI_DNS_TYPE_A: + return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)) == 0; + + case AVAHI_DNS_TYPE_AAAA: + return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)) == 0; + + default: + return a->data.generic.size == b->data.generic.size && + (a->data.generic.size == 0 || memcmp(a->data.generic.data, b->data.generic.data, a->data.generic.size) == 0); + } + +} + +int avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b) { + assert(a); + assert(b); + + if (a == b) + return 1; + + return + avahi_key_equal(a->key, b->key) && + rdata_equal(a, b); +} + + +AvahiRecord *avahi_record_copy(AvahiRecord *r) { + AvahiRecord *copy; + + if (!(copy = avahi_new(AvahiRecord, 1))) { + avahi_log_error("avahi_new() failed."); + return NULL; + } + + copy->ref = 1; + copy->key = avahi_key_ref(r->key); + copy->ttl = r->ttl; + + switch (r->key->type) { + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + if (!(copy->data.ptr.name = avahi_strdup(r->data.ptr.name))) + goto fail; + break; + + case AVAHI_DNS_TYPE_SRV: + copy->data.srv.priority = r->data.srv.priority; + copy->data.srv.weight = r->data.srv.weight; + copy->data.srv.port = r->data.srv.port; + if (!(copy->data.srv.name = avahi_strdup(r->data.srv.name))) + goto fail; + break; + + case AVAHI_DNS_TYPE_HINFO: + if (!(copy->data.hinfo.os = avahi_strdup(r->data.hinfo.os))) + goto fail; + + if (!(copy->data.hinfo.cpu = avahi_strdup(r->data.hinfo.cpu))) { + avahi_free(r->data.hinfo.os); + goto fail; + } + break; + + case AVAHI_DNS_TYPE_TXT: + copy->data.txt.string_list = avahi_string_list_copy(r->data.txt.string_list); + break; + + case AVAHI_DNS_TYPE_A: + copy->data.a.address = r->data.a.address; + break; + + case AVAHI_DNS_TYPE_AAAA: + copy->data.aaaa.address = r->data.aaaa.address; + break; + + default: + if (!(copy->data.generic.data = avahi_memdup(r->data.generic.data, r->data.generic.size))) + goto fail; + copy->data.generic.size = r->data.generic.size; + break; + + } + + return copy; + +fail: + avahi_log_error("Failed to allocate memory"); + + avahi_key_unref(copy->key); + avahi_free(copy); + + return NULL; +} + + +size_t avahi_key_get_estimate_size(AvahiKey *k) { + assert(k); + + return strlen(k->name)+1+4; +} + +size_t avahi_record_get_estimate_size(AvahiRecord *r) { + size_t n; + assert(r); + + n = avahi_key_get_estimate_size(r->key) + 4 + 2; + + switch (r->key->type) { + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + n += strlen(r->data.ptr.name) + 1; + break; + + case AVAHI_DNS_TYPE_SRV: + n += 6 + strlen(r->data.srv.name) + 1; + break; + + case AVAHI_DNS_TYPE_HINFO: + n += strlen(r->data.hinfo.os) + 1 + strlen(r->data.hinfo.cpu) + 1; + break; + + case AVAHI_DNS_TYPE_TXT: + n += avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); + break; + + case AVAHI_DNS_TYPE_A: + n += sizeof(AvahiIPv4Address); + break; + + case AVAHI_DNS_TYPE_AAAA: + n += sizeof(AvahiIPv6Address); + break; + + default: + n += r->data.generic.size; + } + + return n; +} + +static int lexicographical_memcmp(const void* a, size_t al, const void* b, size_t bl) { + size_t c; + int ret; + + assert(a); + assert(b); + + c = al < bl ? al : bl; + if ((ret = memcmp(a, b, c))) + return ret; + + if (al == bl) + return 0; + else + return al == c ? 1 : -1; +} + +static int uint16_cmp(uint16_t a, uint16_t b) { + return a == b ? 0 : (a < b ? -1 : 1); +} + +int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) { + int r; +/* char *t1, *t2; */ + + assert(a); + assert(b); + +/* t1 = avahi_record_to_string(a); */ +/* t2 = avahi_record_to_string(b); */ +/* g_message("lexicocmp: %s %s", t1, t2); */ +/* avahi_free(t1); */ +/* avahi_free(t2); */ + + if (a == b) + return 0; + + if ((r = uint16_cmp(a->key->clazz, b->key->clazz)) || + (r = uint16_cmp(a->key->type, b->key->type))) + return r; + + switch (a->key->type) { + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + return avahi_binary_domain_cmp(a->data.ptr.name, b->data.ptr.name); + + case AVAHI_DNS_TYPE_SRV: { + if ((r = uint16_cmp(a->data.srv.priority, b->data.srv.priority)) == 0 && + (r = uint16_cmp(a->data.srv.weight, b->data.srv.weight)) == 0 && + (r = uint16_cmp(a->data.srv.port, b->data.srv.port)) == 0) + r = avahi_binary_domain_cmp(a->data.srv.name, b->data.srv.name); + + return r; + } + + case AVAHI_DNS_TYPE_HINFO: { + + if ((r = strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu)) || + (r = strcmp(a->data.hinfo.os, b->data.hinfo.os))) + return r; + + return 0; + + } + + case AVAHI_DNS_TYPE_TXT: { + + uint8_t *ma = NULL, *mb = NULL; + size_t asize, bsize; + + asize = avahi_string_list_serialize(a->data.txt.string_list, NULL, 0); + bsize = avahi_string_list_serialize(b->data.txt.string_list, NULL, 0); + + if (asize > 0 && !(ma = avahi_new(uint8_t, asize))) + goto fail; + + if (bsize > 0 && !(mb = avahi_new(uint8_t, bsize))) { + avahi_free(ma); + goto fail; + } + + avahi_string_list_serialize(a->data.txt.string_list, ma, asize); + avahi_string_list_serialize(b->data.txt.string_list, mb, bsize); + + if (asize && bsize) + r = lexicographical_memcmp(ma, asize, mb, bsize); + else if (asize && !bsize) + r = 1; + else if (!asize && bsize) + r = -1; + else + r = 0; + + avahi_free(ma); + avahi_free(mb); + + return r; + } + + case AVAHI_DNS_TYPE_A: + return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)); + + case AVAHI_DNS_TYPE_AAAA: + return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)); + + default: + return lexicographical_memcmp(a->data.generic.data, a->data.generic.size, + b->data.generic.data, b->data.generic.size); + } + + +fail: + avahi_log_error(__FILE__": Out of memory"); + return -1; /* or whatever ... */ +} + +int avahi_record_is_goodbye(AvahiRecord *r) { + assert(r); + + return r->ttl == 0; +} + +int avahi_key_is_valid(AvahiKey *k) { + assert(k); + + if (!avahi_is_valid_domain_name(k->name)) + return 0; + + return 1; +} + +int avahi_record_is_valid(AvahiRecord *r) { + assert(r); + + if (!avahi_key_is_valid(r->key)) + return 0; + + switch (r->key->type) { + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + case AVAHI_DNS_TYPE_NS: + return avahi_is_valid_domain_name(r->data.ptr.name); + + case AVAHI_DNS_TYPE_SRV: + return avahi_is_valid_domain_name(r->data.srv.name); + + case AVAHI_DNS_TYPE_HINFO: + return + strlen(r->data.hinfo.os) <= 255 && + strlen(r->data.hinfo.cpu) <= 255; + + case AVAHI_DNS_TYPE_TXT: { + + AvahiStringList *strlst; + + for (strlst = r->data.txt.string_list; strlst; strlst = strlst->next) + if (strlst->size > 255 || strlst->size <= 0) + return 0; + + return 1; + } + } + + return 1; +} + +static AvahiAddress *get_address(const AvahiRecord *r, AvahiAddress *a) { + assert(r); + + switch (r->key->type) { + case AVAHI_DNS_TYPE_A: + a->proto = AVAHI_PROTO_INET; + a->data.ipv4 = r->data.a.address; + break; + + case AVAHI_DNS_TYPE_AAAA: + a->proto = AVAHI_PROTO_INET6; + a->data.ipv6 = r->data.aaaa.address; + break; + + default: + return NULL; + } + + return a; +} + +int avahi_record_is_link_local_address(const AvahiRecord *r) { + AvahiAddress a; + + assert(r); + + if (!get_address(r, &a)) + return 0; + + return avahi_address_is_link_local(&a); +} diff --git a/3rdparty/QtZeroConf/avahi-core/rr.h b/3rdparty/QtZeroConf/avahi-core/rr.h new file mode 100644 index 000000000..794b83fb1 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/rr.h @@ -0,0 +1,175 @@ +#ifndef foorrhfoo +#define foorrhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file rr.h Functions and definitions for manipulating DNS resource record (RR) data. */ + +#include +#include + +#include +#include +#include + +AVAHI_C_DECL_BEGIN + +/** DNS record types, see RFC 1035, in addition to those defined in defs.h */ +enum { + AVAHI_DNS_TYPE_ANY = 0xFF, /**< Special query type for requesting all records */ + AVAHI_DNS_TYPE_OPT = 41, /**< EDNS0 option */ + AVAHI_DNS_TYPE_TKEY = 249, + AVAHI_DNS_TYPE_TSIG = 250, + AVAHI_DNS_TYPE_IXFR = 251, + AVAHI_DNS_TYPE_AXFR = 252 +}; + +/** DNS record classes, see RFC 1035, in addition to those defined in defs.h */ +enum { + AVAHI_DNS_CLASS_ANY = 0xFF, /**< Special query type for requesting all records */ + AVAHI_DNS_CACHE_FLUSH = 0x8000, /**< Not really a class but a bit which may be set in response packets, see mDNS spec for more information */ + AVAHI_DNS_UNICAST_RESPONSE = 0x8000 /**< Not really a class but a bit which may be set in query packets, see mDNS spec for more information */ +}; + +/** Encapsulates a DNS query key consisting of class, type and + name. Use avahi_key_ref()/avahi_key_unref() for manipulating the + reference counter. The structure is intended to be treated as "immutable", no + changes should be imposed after creation */ +typedef struct AvahiKey { + int ref; /**< Reference counter */ + char *name; /**< Record name */ + uint16_t clazz; /**< Record class, one of the AVAHI_DNS_CLASS_xxx constants */ + uint16_t type; /**< Record type, one of the AVAHI_DNS_TYPE_xxx constants */ +} AvahiKey; + +/** Encapsulates a DNS resource record. The structure is intended to + * be treated as "immutable", no changes should be imposed after + * creation. */ +typedef struct AvahiRecord { + int ref; /**< Reference counter */ + AvahiKey *key; /**< Reference to the query key of this record */ + + uint32_t ttl; /**< DNS TTL of this record */ + + union { + + struct { + void* data; + uint16_t size; + } generic; /**< Generic record data for unknown types */ + + struct { + uint16_t priority; + uint16_t weight; + uint16_t port; + char *name; + } srv; /**< Data for SRV records */ + + struct { + char *name; + } ptr, ns, cname; /**< Data for PTR, NS and CNAME records */ + + struct { + char *cpu; + char *os; + } hinfo; /**< Data for HINFO records */ + + struct { + AvahiStringList *string_list; + } txt; /**< Data for TXT records */ + + struct { + AvahiIPv4Address address; + } a; /**< Data for A records */ + + struct { + AvahiIPv6Address address; + } aaaa; /**< Data for AAAA records */ + + } data; /**< Record data */ + +} AvahiRecord; + +/** Create a new AvahiKey object. The reference counter will be set to 1. */ +AvahiKey *avahi_key_new(const char *name, uint16_t clazz, uint16_t type); + +/** Increase the reference counter of an AvahiKey object by one */ +AvahiKey *avahi_key_ref(AvahiKey *k); + +/** Decrease the reference counter of an AvahiKey object by one */ +void avahi_key_unref(AvahiKey *k); + +/** Check whether two AvahiKey object contain the same + * data. AVAHI_DNS_CLASS_ANY/AVAHI_DNS_TYPE_ANY are treated like any + * other class/type. */ +int avahi_key_equal(const AvahiKey *a, const AvahiKey *b); + +/** Return a numeric hash value for a key for usage in hash tables. */ +unsigned avahi_key_hash(const AvahiKey *k); + +/** Create a new record object. Record data should be filled in right after creation. The reference counter is set to 1. */ +AvahiRecord *avahi_record_new(AvahiKey *k, uint32_t ttl); + +/** Create a new record object. Record data should be filled in right after creation. The reference counter is set to 1. */ +AvahiRecord *avahi_record_new_full(const char *name, uint16_t clazz, uint16_t type, uint32_t ttl); + +/** Increase the reference counter of an AvahiRecord by one. */ +AvahiRecord *avahi_record_ref(AvahiRecord *r); + +/** Decrease the reference counter of an AvahiRecord by one. */ +void avahi_record_unref(AvahiRecord *r); + +/** Return a textual representation of the specified DNS class. The + * returned pointer points to a read only internal string. */ +const char *avahi_dns_class_to_string(uint16_t clazz); + +/** Return a textual representation of the specified DNS class. The + * returned pointer points to a read only internal string. */ +const char *avahi_dns_type_to_string(uint16_t type); + +/** Create a textual representation of the specified key. avahi_free() the + * result! */ +char *avahi_key_to_string(const AvahiKey *k); + +/** Create a textual representation of the specified record, similar + * in style to BIND zone file data. avahi_free() the result! */ +char *avahi_record_to_string(const AvahiRecord *r); + +/** Check whether two records are equal (regardless of the TTL */ +int avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b); + +/** Check whether the specified key is valid */ +int avahi_key_is_valid(AvahiKey *k); + +/** Check whether the specified record is valid */ +int avahi_record_is_valid(AvahiRecord *r); + +/** Parse a binary rdata object and fill it into *record. This function is actually implemented in dns.c */ +int avahi_rdata_parse(AvahiRecord *record, const void* rdata, size_t size); + +/** Serialize an AvahiRecord object into binary rdata. This function is actually implemented in dns.c */ +size_t avahi_rdata_serialize(AvahiRecord *record, void *rdata, size_t max_size); + +/** Return TRUE if the AvahiRecord object is a link-local A or AAAA address */ +int avahi_record_is_link_local_address(const AvahiRecord *r); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/rrlist.c b/3rdparty/QtZeroConf/avahi-core/rrlist.c new file mode 100644 index 000000000..0a794abaf --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/rrlist.c @@ -0,0 +1,188 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include + +#include "rrlist.h" +#include "log.h" + +typedef struct AvahiRecordListItem AvahiRecordListItem; + +struct AvahiRecordListItem { + int read; + AvahiRecord *record; + int unicast_response; + int flush_cache; + int auxiliary; + AVAHI_LLIST_FIELDS(AvahiRecordListItem, items); +}; + +struct AvahiRecordList { + AVAHI_LLIST_HEAD(AvahiRecordListItem, read); + AVAHI_LLIST_HEAD(AvahiRecordListItem, unread); + + int all_flush_cache; +}; + +AvahiRecordList *avahi_record_list_new(void) { + AvahiRecordList *l; + + if (!(l = avahi_new(AvahiRecordList, 1))) { + avahi_log_error("avahi_new() failed."); + return NULL; + } + + AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->read); + AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->unread); + + l->all_flush_cache = 1; + return l; +} + +void avahi_record_list_free(AvahiRecordList *l) { + assert(l); + + avahi_record_list_flush(l); + avahi_free(l); +} + +static void item_free(AvahiRecordList *l, AvahiRecordListItem *i) { + assert(l); + assert(i); + + if (i->read) + AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->read, i); + else + AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i); + + avahi_record_unref(i->record); + avahi_free(i); +} + +void avahi_record_list_flush(AvahiRecordList *l) { + assert(l); + + while (l->read) + item_free(l, l->read); + while (l->unread) + item_free(l, l->unread); + + l->all_flush_cache = 1; +} + +AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary) { + AvahiRecord *r; + AvahiRecordListItem *i; + + if (!(i = l->unread)) + return NULL; + + assert(!i->read); + + r = avahi_record_ref(i->record); + if (ret_unicast_response) + *ret_unicast_response = i->unicast_response; + if (ret_flush_cache) + *ret_flush_cache = i->flush_cache; + if (ret_auxiliary) + *ret_auxiliary = i->auxiliary; + + AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i); + AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->read, i); + + i->read = 1; + + return r; +} + +static AvahiRecordListItem *get(AvahiRecordList *l, AvahiRecord *r) { + AvahiRecordListItem *i; + + assert(l); + assert(r); + + for (i = l->read; i; i = i->items_next) + if (avahi_record_equal_no_ttl(i->record, r)) + return i; + + for (i = l->unread; i; i = i->items_next) + if (avahi_record_equal_no_ttl(i->record, r)) + return i; + + return NULL; +} + +void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, int unicast_response, int auxiliary) { + AvahiRecordListItem *i; + + assert(l); + assert(r); + + if (get(l, r)) + return; + + if (!(i = avahi_new(AvahiRecordListItem, 1))) { + avahi_log_error("avahi_new() failed."); + return; + } + + i->unicast_response = unicast_response; + i->flush_cache = flush_cache; + i->auxiliary = auxiliary; + i->record = avahi_record_ref(r); + i->read = 0; + + l->all_flush_cache = l->all_flush_cache && flush_cache; + + AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->unread, i); +} + +void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r) { + AvahiRecordListItem *i; + + assert(l); + assert(r); + + if (!(i = get(l, r))) + return; + + item_free(l, i); +} + +int avahi_record_list_is_empty(AvahiRecordList *l) { + assert(l); + + return !l->unread && !l->read; +} + +int avahi_record_list_all_flush_cache(AvahiRecordList *l) { + assert(l); + + /* Return TRUE if all entries in this list have flush_cache set */ + + return l->all_flush_cache; +} diff --git a/3rdparty/QtZeroConf/avahi-core/rrlist.h b/3rdparty/QtZeroConf/avahi-core/rrlist.h new file mode 100644 index 000000000..c7b5aba18 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/rrlist.h @@ -0,0 +1,40 @@ +#ifndef foorrlisthfoo +#define foorrlisthfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + + +#include "rr.h" + +typedef struct AvahiRecordList AvahiRecordList; + +AvahiRecordList *avahi_record_list_new(void); +void avahi_record_list_free(AvahiRecordList *l); +void avahi_record_list_flush(AvahiRecordList *l); + +AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary); +void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, int unicast_response, int auxiliary); +void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r); + +int avahi_record_list_all_flush_cache(AvahiRecordList *l); + +int avahi_record_list_is_empty(AvahiRecordList *l); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/server.c b/3rdparty/QtZeroConf/avahi-core/server.c new file mode 100644 index 000000000..a2cb19a83 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/server.c @@ -0,0 +1,1805 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "internal.h" +#include "iface.h" +#include "socket.h" +#include "browse.h" +#include "log.h" +#include "util.h" +#include "dns-srv-rr.h" +#include "addr-util.h" +#include "domain-util.h" +#include "rr-util.h" + +#define AVAHI_DEFAULT_CACHE_ENTRIES_MAX 4096 + +static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) { + assert(s); + assert(i); + assert(name); + assert(callback); + + if (type == AVAHI_DNS_TYPE_ANY) { + AvahiEntry *e; + + for (e = s->entries; e; e = e->entries_next) + if (!e->dead && + avahi_entry_is_registered(s, e, i) && + e->record->key->clazz == AVAHI_DNS_CLASS_IN && + avahi_domain_equal(name, e->record->key->name)) + callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata); + + } else { + AvahiEntry *e; + AvahiKey *k; + + if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type))) + return; /** OOM */ + + for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next) + if (!e->dead && avahi_entry_is_registered(s, e, i)) + callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata); + + avahi_key_unref(k); + } +} + +void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) { + assert(s); + assert(i); + assert(r); + assert(callback); + + /* Call the specified callback far all records referenced by the one specified in *r */ + + if (r->key->clazz == AVAHI_DNS_CLASS_IN) { + if (r->key->type == AVAHI_DNS_TYPE_PTR) { + enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata); + enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata); + } else if (r->key->type == AVAHI_DNS_TYPE_SRV) { + enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata); + enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata); + } else if (r->key->type == AVAHI_DNS_TYPE_CNAME) + enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata); + } +} + +void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) { + assert(s); + assert(i); + assert(e); + + avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary); +} + +void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) { + assert(s); + assert(i); + assert(k); + + /* Push all records that match the specified key to the record list */ + + if (avahi_key_is_pattern(k)) { + AvahiEntry *e; + + /* Handle ANY query */ + + for (e = s->entries; e; e = e->entries_next) + if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i)) + avahi_server_prepare_response(s, i, e, unicast_response, 0); + + } else { + AvahiEntry *e; + + /* Handle all other queries */ + + for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next) + if (!e->dead && avahi_entry_is_registered(s, e, i)) + avahi_server_prepare_response(s, i, e, unicast_response, 0); + } + + /* Look for CNAME records */ + + if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY) + && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) { + + AvahiKey *cname_key; + + if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME))) + return; + + avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response); + avahi_key_unref(cname_key); + } +} + +static void withdraw_entry(AvahiServer *s, AvahiEntry *e) { + assert(s); + assert(e); + + /* Withdraw the specified entry, and if is part of an entry group, + * put that into COLLISION state */ + + if (e->dead) + return; + + if (e->group) { + AvahiEntry *k; + + for (k = e->group->entries; k; k = k->by_group_next) + if (!k->dead) { + avahi_goodbye_entry(s, k, 0, 1); + k->dead = 1; + } + + e->group->n_probing = 0; + + avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION); + } else { + avahi_goodbye_entry(s, e, 0, 1); + e->dead = 1; + } + + s->need_entry_cleanup = 1; +} + +static void withdraw_rrset(AvahiServer *s, AvahiKey *key) { + AvahiEntry *e; + + assert(s); + assert(key); + + /* Withdraw an entry RRSset */ + + for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next) + withdraw_entry(s, e); +} + +static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) { + AvahiEntry *e, *n; + int ours = 0, won = 0, lost = 0; + + assert(s); + assert(record); + assert(i); + + /* Handle incoming probes and check if they conflict our own probes */ + + for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) { + int cmp; + n = e->by_key_next; + + if (e->dead) + continue; + + if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) { + ours = 1; + break; + } else { + + if (avahi_entry_is_probing(s, e, i)) { + if (cmp > 0) + won = 1; + else /* cmp < 0 */ + lost = 1; + } + } + } + + if (!ours) { + char *t = avahi_record_to_string(record); + + if (won) + avahi_log_debug("Received conflicting probe [%s]. Local host won.", t); + else if (lost) { + avahi_log_debug("Received conflicting probe [%s]. Local host lost. Withdrawing.", t); + withdraw_rrset(s, record->key); + } + + avahi_free(t); + } +} + +static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique) { + int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0; + AvahiEntry *e, *n, *conflicting_entry = NULL; + + assert(s); + assert(i); + assert(record); + + /* Check whether an incoming record conflicts with one of our own */ + + for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) { + n = e->by_key_next; + + if (e->dead) + continue; + + /* Check if the incoming is a goodbye record */ + if (avahi_record_is_goodbye(record)) { + + if (avahi_record_equal_no_ttl(e->record, record)) { + char *t; + + /* Refresh */ + t = avahi_record_to_string(record); + avahi_log_debug("Received goodbye record for one of our records [%s]. Refreshing.", t); + avahi_server_prepare_matching_responses(s, i, e->record->key, 0); + + valid = 0; + avahi_free(t); + break; + } + + /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */ + continue; + } + + if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique) + continue; + + /* Either our entry or the other is intended to be unique, so let's check */ + + if (avahi_record_equal_no_ttl(e->record, record)) { + ours = 1; /* We have an identical record, so this is no conflict */ + + /* Check wheter there is a TTL conflict */ + if (record->ttl <= e->record->ttl/2 && + avahi_entry_is_registered(s, e, i)) { + char *t; + /* Refresh */ + t = avahi_record_to_string(record); + + avahi_log_debug("Received record with bad TTL [%s]. Refreshing.", t); + avahi_server_prepare_matching_responses(s, i, e->record->key, 0); + valid = 0; + + avahi_free(t); + } + + /* There's no need to check the other entries of this RRset */ + break; + + } else { + + if (avahi_entry_is_registered(s, e, i)) { + + /* A conflict => we have to return to probe mode */ + conflict = 1; + conflicting_entry = e; + + } else if (avahi_entry_is_probing(s, e, i)) { + + /* We are currently registering a matching record, but + * someone else already claimed it, so let's + * withdraw */ + conflict = 1; + withdraw_immediately = 1; + } + } + } + + if (!ours && conflict) { + char *t; + + valid = 0; + + t = avahi_record_to_string(record); + + if (withdraw_immediately) { + avahi_log_debug("Received conflicting record [%s] with local record to be. Withdrawing.", t); + withdraw_rrset(s, record->key); + } else { + assert(conflicting_entry); + avahi_log_debug("Received conflicting record [%s]. Resetting our record.", t); + avahi_entry_return_to_initial_state(s, conflicting_entry, i); + + /* Local unique records are returned to probing + * state. Local shared records are reannounced. */ + } + + avahi_free(t); + } + + return valid; +} + +static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) { + int *unicast_response = userdata; + + assert(s); + assert(r); + assert(unicast_response); + + avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1); +} + +static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) { + assert(s); + assert(r); + + avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response); +} + +void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) { + + assert(s); + assert(i); + assert(!legacy_unicast || (a && port > 0 && p)); + + if (legacy_unicast) { + AvahiDnsPacket *reply; + AvahiRecord *r; + + if (!(reply = avahi_dns_packet_new_reply(p, 512 + AVAHI_DNS_PACKET_EXTRA_SIZE /* unicast DNS maximum packet size is 512 */ , 1, 1))) + return; /* OOM */ + + while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) { + + append_aux_records_to_list(s, i, r, 0); + + if (avahi_dns_packet_append_record(reply, r, 0, 10)) + avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT); + else { + char *t = avahi_record_to_string(r); + avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t); + avahi_free(t); + } + + avahi_record_unref(r); + } + + if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0) + avahi_interface_send_packet_unicast(i, reply, a, port); + + avahi_dns_packet_free(reply); + + } else { + int unicast_response, flush_cache, auxiliary; + AvahiDnsPacket *reply = NULL; + AvahiRecord *r; + + /* In case the query packet was truncated never respond + immediately, because known answer suppression records might be + contained in later packets */ + int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC); + + while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) { + + int im = immediately; + + /* Only send the response immediately if it contains a + * unique entry AND it is not in reply to a truncated + * packet AND it is not an auxiliary record AND all other + * responses for this record are unique too. */ + + if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->record_list)) + im = 1; + + if (!avahi_interface_post_response(i, r, flush_cache, a, im) && unicast_response) { + + /* Due to some reasons the record has not been scheduled. + * The client requested an unicast response in that + * case. Therefore we prepare such a response */ + + append_aux_records_to_list(s, i, r, unicast_response); + + for (;;) { + + if (!reply) { + assert(p); + + if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0))) + break; /* OOM */ + } + + if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) { + + /* Appending this record succeeded, so incremeant + * the specific header field, and return to the caller */ + + avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT); + break; + } + + if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) { + size_t size; + + /* The record is too large for one packet, so create a larger packet */ + + avahi_dns_packet_free(reply); + size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE; + + if (!(reply = avahi_dns_packet_new_reply(p, size + AVAHI_DNS_PACKET_EXTRA_SIZE, 0, 1))) + break; /* OOM */ + + if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) { + + /* Appending this record succeeded, so incremeant + * the specific header field, and return to the caller */ + + avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT); + break; + + } else { + + /* We completely fucked up, there's + * nothing we can do. The RR just doesn't + * fit in. Let's ignore it. */ + + char *t; + avahi_dns_packet_free(reply); + reply = NULL; + t = avahi_record_to_string(r); + avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t); + avahi_free(t); + break; + } + } + + /* Appending the record didn't succeeed, so let's send this packet, and create a new one */ + avahi_interface_send_packet_unicast(i, reply, a, port); + avahi_dns_packet_free(reply); + reply = NULL; + } + } + + avahi_record_unref(r); + } + + if (reply) { + if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0) + avahi_interface_send_packet_unicast(i, reply, a, port); + avahi_dns_packet_free(reply); + } + } + + avahi_record_list_flush(s->record_list); +} + +static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) { + AvahiInterface *j; + + assert(s); + assert(i); + assert(r); + + if (!s->config.enable_reflector) + return; + + for (j = s->monitor->interfaces; j; j = j->interface_next) + if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) + avahi_interface_post_response(j, r, flush_cache, NULL, 1); +} + +static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { + AvahiServer *s = userdata; + AvahiRecord* r; + + assert(c); + assert(pattern); + assert(e); + assert(s); + + /* Don't reflect cache entry with ipv6 link-local addresses. */ + r = e->record; + if ((r->key->type == AVAHI_DNS_TYPE_AAAA) && + (r->data.aaaa.address.address[0] == 0xFE) && + (r->data.aaaa.address.address[1] == 0x80)) + return NULL; + + avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0); + return NULL; +} + +static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) { + AvahiInterface *j; + + assert(s); + assert(i); + assert(k); + + if (!s->config.enable_reflector) + return; + + for (j = s->monitor->interfaces; j; j = j->interface_next) + if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) { + /* Post the query to other networks */ + avahi_interface_post_query(j, k, 1, NULL); + + /* Reply from caches of other network. This is needed to + * "work around" known answer suppression. */ + + avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s); + } +} + +static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) { + AvahiInterface *j; + + assert(s); + assert(i); + assert(r); + + if (!s->config.enable_reflector) + return; + + for (j = s->monitor->interfaces; j; j = j->interface_next) + if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) + avahi_interface_post_probe(j, r, 1); +} + +static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) { + size_t n; + int is_probe; + + assert(s); + assert(p); + assert(i); + assert(a); + + assert(avahi_record_list_is_empty(s->record_list)); + + is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0; + + /* Handle the questions */ + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) { + AvahiKey *key; + int unicast_response = 0; + + if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) { + avahi_log_debug(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)"); + goto fail; + } + + if (!legacy_unicast && !from_local_iface) { + reflect_query(s, i, key); + if (!unicast_response) + avahi_cache_start_poof(i->cache, key, a); + } + + if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 && + !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC)) + /* Allow our own queries to be suppressed by incoming + * queries only when they do not include known answers */ + avahi_query_scheduler_incoming(i->query_scheduler, key); + + avahi_server_prepare_matching_responses(s, i, key, unicast_response); + avahi_key_unref(key); + } + + if (!legacy_unicast) { + + /* Known Answer Suppression */ + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) { + AvahiRecord *record; + int unique = 0; + + if (!(record = avahi_dns_packet_consume_record(p, &unique))) { + avahi_log_debug(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)"); + goto fail; + } + + avahi_response_scheduler_suppress(i->response_scheduler, record, a); + avahi_record_list_drop(s->record_list, record); + avahi_cache_stop_poof(i->cache, record, a); + + avahi_record_unref(record); + } + + /* Probe record */ + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) { + AvahiRecord *record; + int unique = 0; + + if (!(record = avahi_dns_packet_consume_record(p, &unique))) { + avahi_log_debug(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)"); + goto fail; + } + + if (!avahi_key_is_pattern(record->key)) { + if (!from_local_iface) + reflect_probe(s, i, record); + incoming_probe(s, record, i); + } + + avahi_record_unref(record); + } + } + + if (!avahi_record_list_is_empty(s->record_list)) + avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe); + + return; + +fail: + avahi_record_list_flush(s->record_list); +} + +static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) { + unsigned n; + + assert(s); + assert(p); + assert(i); + assert(a); + + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) + + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) { + AvahiRecord *record; + int cache_flush = 0; + + if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) { + avahi_log_debug(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)"); + break; + } + + if (!avahi_key_is_pattern(record->key)) { + + if (handle_conflict(s, i, record, cache_flush)) { + if (!from_local_iface && !avahi_record_is_link_local_address(record)) + reflect_response(s, i, record, cache_flush); + avahi_cache_update(i->cache, record, cache_flush, a); + avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush); + } + } + + avahi_record_unref(record); + } + + /* If the incoming response contained a conflicting record, some + records have been scheduled for sending. We need to flush them + here. */ + if (!avahi_record_list_is_empty(s->record_list)) + avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1); +} + +static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) { + unsigned n, idx = (unsigned) -1; + AvahiLegacyUnicastReflectSlot *slot; + + assert(s); + + if (!s->legacy_unicast_reflect_slots) + s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX); + + for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) { + idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; + + if (!s->legacy_unicast_reflect_slots[idx]) + break; + } + + if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx]) + return NULL; + + if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1))) + return NULL; /* OOM */ + + s->legacy_unicast_reflect_slots[idx] = slot; + slot->id = s->legacy_unicast_reflect_id++; + slot->server = s; + + return slot; +} + +static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) { + unsigned idx; + + assert(s); + assert(slot); + + idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; + + assert(s->legacy_unicast_reflect_slots[idx] == slot); + + avahi_time_event_free(slot->time_event); + + avahi_free(slot); + s->legacy_unicast_reflect_slots[idx] = NULL; +} + +static void free_slots(AvahiServer *s) { + unsigned idx; + assert(s); + + if (!s->legacy_unicast_reflect_slots) + return; + + for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++) + if (s->legacy_unicast_reflect_slots[idx]) + deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]); + + avahi_free(s->legacy_unicast_reflect_slots); + s->legacy_unicast_reflect_slots = NULL; +} + +static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) { + unsigned idx; + + assert(s); + + if (!s->legacy_unicast_reflect_slots) + return NULL; + + idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; + + if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id) + return NULL; + + return s->legacy_unicast_reflect_slots[idx]; +} + +static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) { + AvahiLegacyUnicastReflectSlot *slot = userdata; + + assert(e); + assert(slot); + assert(slot->time_event == e); + + deallocate_slot(slot->server, slot); +} + +static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) { + AvahiLegacyUnicastReflectSlot *slot; + AvahiInterface *j; + + assert(s); + assert(p); + assert(i); + assert(a); + assert(port > 0); + assert(i->protocol == a->proto); + + if (!s->config.enable_reflector) + return; + + /* Reflecting legacy unicast queries is a little more complicated + than reflecting normal queries, since we must route the + responses back to the right client. Therefore we must store + some information for finding the right client contact data for + response packets. In contrast to normal queries legacy + unicast query and response packets are reflected untouched and + are not reassembled into larger packets */ + + if (!(slot = allocate_slot(s))) { + /* No slot available, we drop this legacy unicast query */ + avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet."); + return; + } + + slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID); + slot->address = *a; + slot->port = port; + slot->interface = i->hardware->index; + + avahi_elapse_time(&slot->elapse_time, 2000, 0); + slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot); + + /* Patch the packet with our new locally generatet id */ + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id); + + for (j = s->monitor->interfaces; j; j = j->interface_next) + if (j->announcing && + j != i && + (s->config.reflect_ipv || j->protocol == i->protocol)) { + + if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) { + avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0); + } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) + avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0); + } + + /* Reset the id */ + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id); +} + +static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) { + assert(s); + assert(address); + assert(port > 0); + + if (!s->config.enable_reflector) + return 0; + + if (!avahi_address_is_local(s->monitor, address)) + return 0; + + if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) { + struct sockaddr_in lsa; + socklen_t l = sizeof(lsa); + + if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0) + avahi_log_warn("getsockname(): %s", strerror(errno)); + else + return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port; + + } + + if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) { + struct sockaddr_in6 lsa; + socklen_t l = sizeof(lsa); + + if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0) + avahi_log_warn("getsockname(): %s", strerror(errno)); + else + return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port; + } + + return 0; +} + +static int is_mdns_mcast_address(const AvahiAddress *a) { + AvahiAddress b; + assert(a); + + avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b); + return avahi_address_cmp(a, &b) == 0; +} + +static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) { + assert(s); + assert(iface != AVAHI_IF_UNSPEC); + assert(a); + + /* If it isn't the MDNS port it can't be generated by us */ + if (port != AVAHI_MDNS_PORT) + return 0; + + return avahi_interface_has_address(s->monitor, iface, a); +} + +static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) { + AvahiInterface *i; + int from_local_iface = 0; + + assert(s); + assert(p); + assert(src_address); + assert(dst_address); + assert(iface > 0); + assert(src_address->proto == dst_address->proto); + + if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) || + !i->announcing) { + avahi_log_debug("Received packet from invalid interface."); + return; + } + + if (port <= 0) { + /* This fixes RHBZ #475394 */ + avahi_log_debug("Received packet from invalid source port %u.", (unsigned) port); + return; + } + + if (avahi_address_is_ipv4_in_ipv6(src_address)) + /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */ + return; + + if (originates_from_local_legacy_unicast_socket(s, src_address, port)) + /* This originates from our local reflector, so let's ignore it */ + return; + + /* We don't want to reflect local traffic, so we check if this packet is generated locally. */ + if (s->config.enable_reflector) + from_local_iface = originates_from_local_iface(s, iface, src_address, port); + + if (avahi_dns_packet_check_valid_multicast(p) < 0) { + avahi_log_debug("Received invalid packet."); + return; + } + + if (avahi_dns_packet_is_query(p)) { + int legacy_unicast = 0; + + /* For queries EDNS0 might allow ARCOUNT != 0. We ignore the + * AR section completely here, so far. Until the day we add + * EDNS0 support. */ + + if (port != AVAHI_MDNS_PORT) { + /* Legacy Unicast */ + + if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 || + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) { + avahi_log_debug("Invalid legacy unicast query packet."); + return; + } + + legacy_unicast = 1; + } + + if (legacy_unicast) + reflect_legacy_unicast_query_packet(s, p, i, src_address, port); + + handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface); + + } else { + char t[AVAHI_ADDRESS_STR_MAX]; + + if (port != AVAHI_MDNS_PORT) { + avahi_log_debug("Received response from host %s with invalid source port %u on interface '%s.%i'", avahi_address_snprint(t, sizeof(t), src_address), port, i->hardware->name, i->protocol); + return; + } + + if (ttl != 255 && s->config.check_response_ttl) { + avahi_log_debug("Received response from host %s with invalid TTL %u on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), ttl, i->hardware->name, i->protocol); + return; + } + + if (!is_mdns_mcast_address(dst_address) && + !avahi_interface_address_on_link(i, src_address)) { + + avahi_log_debug("Received non-local response from host %s on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), i->hardware->name, i->protocol); + return; + } + + if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 || + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 || + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) { + + avahi_log_debug("Invalid response packet from host %s.", avahi_address_snprint(t, sizeof(t), src_address)); + return; + } + + handle_response_packet(s, p, i, src_address, from_local_iface); + } +} + +static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) { + AvahiInterface *j; + AvahiLegacyUnicastReflectSlot *slot; + + assert(s); + assert(p); + + if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) { + avahi_log_debug("Received invalid packet."); + return; + } + + if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) { + avahi_log_debug("Received legacy unicast response with unknown id"); + return; + } + + if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) || + !j->announcing) + return; + + /* Patch the original ID into this response */ + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id); + + /* Forward the response to the correct client */ + avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port); + + /* Undo changes to packet */ + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id); +} + +static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) { + AvahiServer *s = userdata; + AvahiAddress dest, src; + AvahiDnsPacket *p = NULL; + AvahiIfIndex iface; + uint16_t port; + uint8_t ttl; + + assert(w); + assert(fd >= 0); + assert(events & AVAHI_WATCH_IN); + + if (fd == s->fd_ipv4) { + dest.proto = src.proto = AVAHI_PROTO_INET; + p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl); + } else { + assert(fd == s->fd_ipv6); + dest.proto = src.proto = AVAHI_PROTO_INET6; + p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl); + } + + if (p) { + if (iface == AVAHI_IF_UNSPEC) + iface = avahi_find_interface_for_address(s->monitor, &dest); + + if (iface != AVAHI_IF_UNSPEC) + dispatch_packet(s, p, &src, port, &dest, iface, ttl); + else + avahi_log_error("Incoming packet received on address that isn't local."); + + avahi_dns_packet_free(p); + + avahi_cleanup_dead_entries(s); + } +} + +static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) { + AvahiServer *s = userdata; + AvahiDnsPacket *p = NULL; + + assert(w); + assert(fd >= 0); + assert(events & AVAHI_WATCH_IN); + + if (fd == s->fd_legacy_unicast_ipv4) + p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL); + else { + assert(fd == s->fd_legacy_unicast_ipv6); + p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL); + } + + if (p) { + dispatch_legacy_unicast_packet(s, p); + avahi_dns_packet_free(p); + + avahi_cleanup_dead_entries(s); + } +} + +static void server_set_state(AvahiServer *s, AvahiServerState state) { + assert(s); + + if (s->state == state) + return; + + s->state = state; + + avahi_interface_monitor_update_rrs(s->monitor, 0); + + if (s->callback) + s->callback(s, state, s->userdata); +} + +static void withdraw_host_rrs(AvahiServer *s) { + assert(s); + + if (s->hinfo_entry_group) + avahi_s_entry_group_reset(s->hinfo_entry_group); + + if (s->browse_domain_entry_group) + avahi_s_entry_group_reset(s->browse_domain_entry_group); + + avahi_interface_monitor_update_rrs(s->monitor, 1); + s->n_host_rr_pending = 0; +} + +void avahi_server_decrease_host_rr_pending(AvahiServer *s) { + assert(s); + + assert(s->n_host_rr_pending > 0); + + if (--s->n_host_rr_pending == 0) + server_set_state(s, AVAHI_SERVER_RUNNING); +} + +void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { + assert(s); + assert(g); + + if (state == AVAHI_ENTRY_GROUP_REGISTERING && + s->state == AVAHI_SERVER_REGISTERING) + s->n_host_rr_pending ++; + + else if (state == AVAHI_ENTRY_GROUP_COLLISION && + (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) { + withdraw_host_rrs(s); + server_set_state(s, AVAHI_SERVER_COLLISION); + + } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED && + s->state == AVAHI_SERVER_REGISTERING) + avahi_server_decrease_host_rr_pending(s); +} + +static void register_hinfo(AvahiServer *s) { + struct utsname utsname; + AvahiRecord *r; + + assert(s); + + if (!s->config.publish_hinfo) + return; + + if (s->hinfo_entry_group) + assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group)); + else + s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL); + + if (!s->hinfo_entry_group) { + avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error)); + return; + } + + /* Fill in HINFO rr */ + if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) { + + if (uname(&utsname) < 0) + avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno)); + else { + + r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine)); + r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname)); + + avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os); + + if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) { + avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error)); + return; + } + } + + avahi_record_unref(r); + } + + if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0) + avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error)); + +} + +static void register_localhost(AvahiServer *s) { + AvahiAddress a; + assert(s); + + /* Add localhost entries */ + avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a); + avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a); + + avahi_address_parse("::1", AVAHI_PROTO_INET6, &a); + avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a); +} + +static void register_browse_domain(AvahiServer *s) { + assert(s); + + if (!s->config.publish_domain) + return; + + if (avahi_domain_equal(s->domain_name, "local")) + return; + + if (s->browse_domain_entry_group) + assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group)); + else + s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL); + + if (!s->browse_domain_entry_group) { + avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error)); + return; + } + + if (avahi_server_add_ptr(s, s->browse_domain_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, AVAHI_DEFAULT_TTL, "b._dns-sd._udp.local", s->domain_name) < 0) { + avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error)); + return; + } + + if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0) + avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error)); +} + +static void register_stuff(AvahiServer *s) { + assert(s); + + server_set_state(s, AVAHI_SERVER_REGISTERING); + s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */ + + register_hinfo(s); + register_browse_domain(s); + avahi_interface_monitor_update_rrs(s->monitor, 0); + + assert(s->n_host_rr_pending > 0); + s->n_host_rr_pending --; + + if (s->n_host_rr_pending == 0) + server_set_state(s, AVAHI_SERVER_RUNNING); +} + +static void update_fqdn(AvahiServer *s) { + char *n; + + assert(s); + assert(s->host_name); + assert(s->domain_name); + + if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name))) + return; /* OOM */ + + avahi_free(s->host_name_fqdn); + s->host_name_fqdn = n; +} + +int avahi_server_set_host_name(AvahiServer *s, const char *host_name) { + char *hn = NULL; + assert(s); + + AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME); + + if (!host_name) + hn = avahi_get_host_name_strdup(); + else + hn = avahi_normalize_name_strdup(host_name); + + hn[strcspn(hn, ".")] = 0; + + if (avahi_domain_equal(s->host_name, hn) && s->state != AVAHI_SERVER_COLLISION) { + avahi_free(hn); + return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE); + } + + withdraw_host_rrs(s); + + avahi_free(s->host_name); + s->host_name = hn; + + update_fqdn(s); + + register_stuff(s); + return AVAHI_OK; +} + +int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) { + char *dn = NULL; + assert(s); + + AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME); + + if (!domain_name) + dn = avahi_strdup("local"); + else + dn = avahi_normalize_name_strdup(domain_name); + + if (avahi_domain_equal(s->domain_name, domain_name)) { + avahi_free(dn); + return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE); + } + + withdraw_host_rrs(s); + + avahi_free(s->domain_name); + s->domain_name = dn; + update_fqdn(s); + + register_stuff(s); + + avahi_free(dn); + return AVAHI_OK; +} + +static int valid_server_config(const AvahiServerConfig *sc) { + AvahiStringList *l; + + assert(sc); + + if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX) + return AVAHI_ERR_INVALID_CONFIG; + + if (sc->host_name && !avahi_is_valid_host_name(sc->host_name)) + return AVAHI_ERR_INVALID_HOST_NAME; + + if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name)) + return AVAHI_ERR_INVALID_DOMAIN_NAME; + + for (l = sc->browse_domains; l; l = l->next) + if (!avahi_is_valid_domain_name((char*) l->text)) + return AVAHI_ERR_INVALID_DOMAIN_NAME; + + return AVAHI_OK; +} + +static int setup_sockets(AvahiServer *s) { + assert(s); + + s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1; + s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1; + + if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) + return AVAHI_ERR_NO_NETWORK; + + if (s->fd_ipv4 < 0 && s->config.use_ipv4) + avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode"); + else if (s->fd_ipv6 < 0 && s->config.use_ipv6) + avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode"); + + s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1; + s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1; + + s->watch_ipv4 = + s->watch_ipv6 = + s->watch_legacy_unicast_ipv4 = + s->watch_legacy_unicast_ipv6 = NULL; + + if (s->fd_ipv4 >= 0) + s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s); + if (s->fd_ipv6 >= 0) + s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s); + + if (s->fd_legacy_unicast_ipv4 >= 0) + s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, legacy_unicast_socket_event, s); + if (s->fd_legacy_unicast_ipv6 >= 0) + s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, legacy_unicast_socket_event, s); + + return 0; +} + +AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) { + AvahiServer *s; + int e; + + if (sc && (e = valid_server_config(sc)) < 0) { + if (error) + *error = e; + return NULL; + } + + if (!(s = avahi_new(AvahiServer, 1))) { + if (error) + *error = AVAHI_ERR_NO_MEMORY; + + return NULL; + } + + s->poll_api = poll_api; + + if (sc) + avahi_server_config_copy(&s->config, sc); + else + avahi_server_config_init(&s->config); + + if ((e = setup_sockets(s)) < 0) { + if (error) + *error = e; + + avahi_server_config_free(&s->config); + avahi_free(s); + + return NULL; + } + + s->n_host_rr_pending = 0; + s->need_entry_cleanup = 0; + s->need_group_cleanup = 0; + s->need_browser_cleanup = 0; + s->cleanup_time_event = NULL; + s->hinfo_entry_group = NULL; + s->browse_domain_entry_group = NULL; + s->error = AVAHI_OK; + s->state = AVAHI_SERVER_INVALID; + + s->callback = callback; + s->userdata = userdata; + + s->time_event_queue = avahi_time_event_queue_new(poll_api); + + s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); + AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries); + AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups); + + s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); + AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers); + AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers); + AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers); + AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers); + AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers); + AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers); + AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers); + AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers); + + s->legacy_unicast_reflect_slots = NULL; + s->legacy_unicast_reflect_id = 0; + + s->record_list = avahi_record_list_new(); + + /* Get host name */ + s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup(); + s->host_name[strcspn(s->host_name, ".")] = 0; + s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local"); + s->host_name_fqdn = NULL; + update_fqdn(s); + + do { + s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand(); + } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID); + + if (s->config.enable_wide_area) { + s->wide_area_lookup_engine = avahi_wide_area_engine_new(s); + avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers); + } else + s->wide_area_lookup_engine = NULL; + + s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s); + + s->monitor = avahi_interface_monitor_new(s); + avahi_interface_monitor_sync(s->monitor); + + register_localhost(s); + register_stuff(s); + + return s; +} + +void avahi_server_free(AvahiServer* s) { + assert(s); + + /* Remove all browsers */ + + while (s->dns_server_browsers) + avahi_s_dns_server_browser_free(s->dns_server_browsers); + while (s->host_name_resolvers) + avahi_s_host_name_resolver_free(s->host_name_resolvers); + while (s->address_resolvers) + avahi_s_address_resolver_free(s->address_resolvers); + while (s->domain_browsers) + avahi_s_domain_browser_free(s->domain_browsers); + while (s->service_type_browsers) + avahi_s_service_type_browser_free(s->service_type_browsers); + while (s->service_browsers) + avahi_s_service_browser_free(s->service_browsers); + while (s->service_resolvers) + avahi_s_service_resolver_free(s->service_resolvers); + while (s->record_browsers) + avahi_s_record_browser_destroy(s->record_browsers); + + /* Remove all locally rgeistered stuff */ + + while(s->entries) + avahi_entry_free(s, s->entries); + + avahi_interface_monitor_free(s->monitor); + + while (s->groups) + avahi_entry_group_free(s, s->groups); + + free_slots(s); + + avahi_hashmap_free(s->entries_by_key); + avahi_record_list_free(s->record_list); + avahi_hashmap_free(s->record_browser_hashmap); + + if (s->wide_area_lookup_engine) + avahi_wide_area_engine_free(s->wide_area_lookup_engine); + avahi_multicast_lookup_engine_free(s->multicast_lookup_engine); + + if (s->cleanup_time_event) + avahi_time_event_free(s->cleanup_time_event); + + avahi_time_event_queue_free(s->time_event_queue); + + /* Free watches */ + + if (s->watch_ipv4) + s->poll_api->watch_free(s->watch_ipv4); + if (s->watch_ipv6) + s->poll_api->watch_free(s->watch_ipv6); + + if (s->watch_legacy_unicast_ipv4) + s->poll_api->watch_free(s->watch_legacy_unicast_ipv4); + if (s->watch_legacy_unicast_ipv6) + s->poll_api->watch_free(s->watch_legacy_unicast_ipv6); + + /* Free sockets */ + + if (s->fd_ipv4 >= 0) + close(s->fd_ipv4); + if (s->fd_ipv6 >= 0) + close(s->fd_ipv6); + + if (s->fd_legacy_unicast_ipv4 >= 0) + close(s->fd_legacy_unicast_ipv4); + if (s->fd_legacy_unicast_ipv6 >= 0) + close(s->fd_legacy_unicast_ipv6); + + /* Free other stuff */ + + avahi_free(s->host_name); + avahi_free(s->domain_name); + avahi_free(s->host_name_fqdn); + + avahi_server_config_free(&s->config); + + avahi_free(s); +} + +const char* avahi_server_get_domain_name(AvahiServer *s) { + assert(s); + + return s->domain_name; +} + +const char* avahi_server_get_host_name(AvahiServer *s) { + assert(s); + + return s->host_name; +} + +const char* avahi_server_get_host_name_fqdn(AvahiServer *s) { + assert(s); + + return s->host_name_fqdn; +} + +void* avahi_server_get_data(AvahiServer *s) { + assert(s); + + return s->userdata; +} + +void avahi_server_set_data(AvahiServer *s, void* userdata) { + assert(s); + + s->userdata = userdata; +} + +AvahiServerState avahi_server_get_state(AvahiServer *s) { + assert(s); + + return s->state; +} + +AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) { + assert(c); + + memset(c, 0, sizeof(AvahiServerConfig)); + c->use_ipv6 = 1; + c->use_ipv4 = 1; + c->allow_interfaces = NULL; + c->deny_interfaces = NULL; + c->host_name = NULL; + c->domain_name = NULL; + c->check_response_ttl = 0; + c->publish_hinfo = 0; + c->publish_addresses = 1; + c->publish_workstation = 0; + c->publish_domain = 1; + c->use_iff_running = 0; + c->enable_reflector = 0; + c->reflect_ipv = 0; + c->add_service_cookie = 0; + c->enable_wide_area = 0; + c->n_wide_area_servers = 0; + c->disallow_other_stacks = 0; + c->browse_domains = NULL; + c->disable_publishing = 0; + c->allow_point_to_point = 0; + c->publish_aaaa_on_ipv4 = 1; + c->publish_a_on_ipv6 = 0; + c->n_cache_entries_max = AVAHI_DEFAULT_CACHE_ENTRIES_MAX; + c->ratelimit_interval = 0; + c->ratelimit_burst = 0; + + return c; +} + +void avahi_server_config_free(AvahiServerConfig *c) { + assert(c); + + avahi_free(c->host_name); + avahi_free(c->domain_name); + avahi_string_list_free(c->browse_domains); + avahi_string_list_free(c->allow_interfaces); + avahi_string_list_free(c->deny_interfaces); +} + +AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) { + char *d = NULL, *h = NULL; + AvahiStringList *browse = NULL, *allow = NULL, *deny = NULL; + assert(ret); + assert(c); + + if (c->host_name) + if (!(h = avahi_strdup(c->host_name))) + return NULL; + + if (c->domain_name) + if (!(d = avahi_strdup(c->domain_name))) { + avahi_free(h); + return NULL; + } + + if (!(browse = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) { + avahi_free(h); + avahi_free(d); + return NULL; + } + + if (!(allow = avahi_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) { + avahi_string_list_free(browse); + avahi_free(h); + avahi_free(d); + return NULL; + } + + if (!(deny = avahi_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) { + avahi_string_list_free(allow); + avahi_string_list_free(browse); + avahi_free(h); + avahi_free(d); + return NULL; + } + + *ret = *c; + ret->host_name = h; + ret->domain_name = d; + ret->browse_domains = browse; + ret->allow_interfaces = allow; + ret->deny_interfaces = deny; + + return ret; +} + +int avahi_server_errno(AvahiServer *s) { + assert(s); + + return s->error; +} + +/* Just for internal use */ +int avahi_server_set_errno(AvahiServer *s, int error) { + assert(s); + + return s->error = error; +} + +uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) { + assert(s); + + return s->local_service_cookie; +} + +static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) { + AvahiEntry *e; + + assert(s); + assert(key); + + for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next) + + if ((e->interface == interface || e->interface <= 0 || interface <= 0) && + (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) && + (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING)) + + return e; + + return NULL; +} + +int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group) { + AvahiKey *key = NULL; + AvahiEntry *e; + int ret; + char n[AVAHI_DOMAIN_NAME_MAX]; + + assert(s); + assert(name); + assert(type); + assert(ret_group); + + AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); + AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); + AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); + AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); + + if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0)) + return avahi_server_set_errno(s, ret); + + if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) + return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); + + e = find_entry(s, interface, protocol, key); + avahi_key_unref(key); + + if (e) { + *ret_group = e->group; + return AVAHI_OK; + } + + return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND); +} + +int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) { + AvahiKey *key = NULL; + AvahiEntry *e; + + assert(s); + assert(name); + + if (!s->host_name_fqdn) + return 0; + + if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) + return 0; + + e = find_entry(s, interface, protocol, key); + avahi_key_unref(key); + + if (!e) + return 0; + + return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name); +} + +int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) { + AvahiEntry *e; + + assert(s); + assert(record); + + for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next) + + if ((e->interface == interface || e->interface <= 0 || interface <= 0) && + (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) && + (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) && + avahi_record_equal_no_ttl(record, e->record)) + return 1; + + return 0; +} + +/** Set the wide area DNS servers */ +int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) { + assert(s); + + if (!s->wide_area_lookup_engine) + return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG); + + avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n); + return AVAHI_OK; +} + +const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) { + assert(s); + + return &s->config; +} + +/** Set the browsing domains */ +int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) { + AvahiStringList *l; + + assert(s); + + for (l = domains; l; l = l->next) + if (!avahi_is_valid_domain_name((char*) l->text)) + return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME); + + avahi_string_list_free(s->config.browse_domains); + s->config.browse_domains = avahi_string_list_copy(domains); + + return AVAHI_OK; +} diff --git a/3rdparty/QtZeroConf/avahi-core/socket.c b/3rdparty/QtZeroConf/avahi-core/socket.c new file mode 100644 index 000000000..5a00300a5 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/socket.c @@ -0,0 +1,994 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include + +#ifdef IP_RECVIF +#include +#endif + +#include "dns.h" +#include "fdutil.h" +#include "socket.h" +#include "log.h" +#include "addr-util.h" + +/* this is a portability hack */ +#ifndef IPV6_ADD_MEMBERSHIP +#ifdef IPV6_JOIN_GROUP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif +#endif + +#ifndef IPV6_DROP_MEMBERSHIP +#ifdef IPV6_LEAVE_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif +#endif + +static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) { + assert(ret_sa); + + memset(ret_sa, 0, sizeof(struct sockaddr_in)); + ret_sa->sin_family = AF_INET; + ret_sa->sin_port = htons(AVAHI_MDNS_PORT); + inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &ret_sa->sin_addr); +} + +static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) { + assert(ret_sa); + + memset(ret_sa, 0, sizeof(struct sockaddr_in6)); + ret_sa->sin6_family = AF_INET6; + ret_sa->sin6_port = htons(AVAHI_MDNS_PORT); + inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &ret_sa->sin6_addr); +} + +static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, uint16_t port) { + assert(ret_sa); + assert(a); + assert(port > 0); + + memset(ret_sa, 0, sizeof(struct sockaddr_in)); + ret_sa->sin_family = AF_INET; + ret_sa->sin_port = htons(port); + memcpy(&ret_sa->sin_addr, a, sizeof(AvahiIPv4Address)); +} + +static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, uint16_t port) { + assert(ret_sa); + assert(a); + assert(port > 0); + + memset(ret_sa, 0, sizeof(struct sockaddr_in6)); + ret_sa->sin6_family = AF_INET6; + ret_sa->sin6_port = htons(port); + memcpy(&ret_sa->sin6_addr, a, sizeof(AvahiIPv6Address)); +} + +int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *a, int idx, int join) { +#ifdef HAVE_STRUCT_IP_MREQN + struct ip_mreqn mreq; +#else + struct ip_mreq mreq; +#endif + struct sockaddr_in sa; + + assert(fd >= 0); + assert(idx >= 0); + assert(a); + + memset(&mreq, 0, sizeof(mreq)); +#ifdef HAVE_STRUCT_IP_MREQN + mreq.imr_ifindex = idx; + mreq.imr_address.s_addr = a->address; +#else + mreq.imr_interface.s_addr = a->address; +#endif + mdns_mcast_group_ipv4(&sa); + mreq.imr_multiaddr = sa.sin_addr; + + /* Some network drivers have issues with dropping membership of + * mcast groups when the iface is down, but don't allow rejoining + * when it comes back up. This is an ugly workaround */ + if (join) + setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); + + if (setsockopt(fd, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { + avahi_log_warn("%s failed: %s", join ? "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP", strerror(errno)); + return -1; + } + + return 0; +} + +int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *a, int idx, int join) { + struct ipv6_mreq mreq6; + struct sockaddr_in6 sa6; + + assert(fd >= 0); + assert(idx >= 0); + assert(a); + + memset(&mreq6, 0, sizeof(mreq6)); + mdns_mcast_group_ipv6 (&sa6); + mreq6.ipv6mr_multiaddr = sa6.sin6_addr; + mreq6.ipv6mr_interface = idx; + + if (join) + setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)); + + if (setsockopt(fd, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { + avahi_log_warn("%s failed: %s", join ? "IPV6_ADD_MEMBERSHIP" : "IPV6_DROP_MEMBERSHIP", strerror(errno)); + return -1; + } + + return 0; +} + +static int reuseaddr(int fd) { + int yes; + + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { + avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno)); + return -1; + } + +#ifdef SO_REUSEPORT + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) { + avahi_log_warn("SO_REUSEPORT failed: %s", strerror(errno)); + if (errno != ENOPROTOOPT) + return -1; + } +#endif + + return 0; +} + +static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) { + + assert(fd >= 0); + assert(sa); + assert(l > 0); + + if (bind(fd, sa, l) < 0) { + + if (errno != EADDRINUSE) { + avahi_log_warn("bind() failed: %s", strerror(errno)); + return -1; + } + + avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***", + sa->sa_family == AF_INET ? "IPv4" : "IPv6"); + + /* Try again, this time with SO_REUSEADDR set */ + if (reuseaddr(fd) < 0) + return -1; + + if (bind(fd, sa, l) < 0) { + avahi_log_warn("bind() failed: %s", strerror(errno)); + return -1; + } + } else { + + /* We enable SO_REUSEADDR afterwards, to make sure that the + * user may run other mDNS implementations if he really + * wants. */ + + if (reuseaddr(fd) < 0) + return -1; + } + + return 0; +} + +static int ipv4_pktinfo(int fd) { + int yes; + +#ifdef IP_PKTINFO + yes = 1; + if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno)); + return -1; + } +#else + +#ifdef IP_RECVINTERFACE + yes = 1; + if (setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno)); + return -1; + } +#elif defined(IP_RECVIF) + yes = 1; + if (setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IP_RECVIF failed: %s", strerror(errno)); + return -1; + } +#endif + +#ifdef IP_RECVDSTADDR + yes = 1; + if (setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno)); + return -1; + } +#endif + +#endif /* IP_PKTINFO */ + +#ifdef IP_RECVTTL + yes = 1; + if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno)); + return -1; + } +#endif + + return 0; +} + +static int ipv6_pktinfo(int fd) { + int yes; + +#ifdef IPV6_RECVPKTINFO + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno)); + return -1; + } +#elif defined(IPV6_PKTINFO) + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno)); + return -1; + } +#endif + +#ifdef IPV6_RECVHOPS + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno)); + return -1; + } +#elif defined(IPV6_RECVHOPLIMIT) + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno)); + return -1; + } +#elif defined(IPV6_HOPLIMIT) + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno)); + return -1; + } +#endif + + return 0; +} + +int avahi_open_socket_ipv4(int no_reuse) { + struct sockaddr_in local; + int fd = -1, r, ittl; + uint8_t ttl, cyes; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + avahi_log_warn("socket() failed: %s", strerror(errno)); + goto fail; + } + + ttl = 255; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { + avahi_log_warn("IP_MULTICAST_TTL failed: %s", strerror(errno)); + goto fail; + } + + ittl = 255; + if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) < 0) { + avahi_log_warn("IP_TTL failed: %s", strerror(errno)); + goto fail; + } + + cyes = 1; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &cyes, sizeof(cyes)) < 0) { + avahi_log_warn("IP_MULTICAST_LOOP failed: %s", strerror(errno)); + goto fail; + } + + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + local.sin_port = htons(AVAHI_MDNS_PORT); + + if (no_reuse) + r = bind(fd, (struct sockaddr*) &local, sizeof(local)); + else + r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)); + + if (r < 0) + goto fail; + + if (ipv4_pktinfo (fd) < 0) + goto fail; + + if (avahi_set_cloexec(fd) < 0) { + avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); + goto fail; + } + + if (avahi_set_nonblock(fd) < 0) { + avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); + goto fail; + } + + return fd; + +fail: + if (fd >= 0) + close(fd); + + return -1; +} + +int avahi_open_socket_ipv6(int no_reuse) { + struct sockaddr_in6 sa, local; + int fd = -1, yes, r; + int ttl; + + mdns_mcast_group_ipv6(&sa); + + if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + avahi_log_warn("socket() failed: %s", strerror(errno)); + goto fail; + } + + ttl = 255; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) { + avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s", strerror(errno)); + goto fail; + } + + ttl = 255; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) { + avahi_log_warn("IPV6_UNICAST_HOPS failed: %s", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s", strerror(errno)); + goto fail; + } + + memset(&local, 0, sizeof(local)); + local.sin6_family = AF_INET6; + local.sin6_port = htons(AVAHI_MDNS_PORT); + + if (no_reuse) + r = bind(fd, (struct sockaddr*) &local, sizeof(local)); + else + r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)); + + if (r < 0) + goto fail; + + if (ipv6_pktinfo(fd) < 0) + goto fail; + + if (avahi_set_cloexec(fd) < 0) { + avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); + goto fail; + } + + if (avahi_set_nonblock(fd) < 0) { + avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); + goto fail; + } + + return fd; + +fail: + if (fd >= 0) + close(fd); + + return -1; +} + +static int sendmsg_loop(int fd, struct msghdr *msg, int flags) { + assert(fd >= 0); + assert(msg); + + for (;;) { + + if (sendmsg(fd, msg, flags) >= 0) + break; + + if (errno == EINTR) + continue; + + if (errno != EAGAIN) { + char where[64]; + struct sockaddr_in *sin = msg->msg_name; + + inet_ntop(sin->sin_family, &sin->sin_addr, where, sizeof(where)); + avahi_log_debug("sendmsg() to %s failed: %s", where, strerror(errno)); + return -1; + } + + if (avahi_wait_for_write(fd) < 0) + return -1; + } + + return 0; +} + +int avahi_send_dns_packet_ipv4( + int fd, + AvahiIfIndex interface, + AvahiDnsPacket *p, + const AvahiIPv4Address *src_address, + const AvahiIPv4Address *dst_address, + uint16_t dst_port) { + + struct sockaddr_in sa; + struct msghdr msg; + struct iovec io; +#ifdef IP_PKTINFO + struct cmsghdr *cmsg; + size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1]; +#elif !defined(IP_MULTICAST_IF) && defined(IP_SENDSRCADDR) + struct cmsghdr *cmsg; + size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_addr)) / sizeof(size_t)) + 1]; +#endif + + assert(fd >= 0); + assert(p); + assert(avahi_dns_packet_check_valid(p) >= 0); + assert(!dst_address || dst_port > 0); + + if (!dst_address) + mdns_mcast_group_ipv4(&sa); + else + ipv4_address_to_sockaddr(&sa, dst_address, dst_port); + + memset(&io, 0, sizeof(io)); + io.iov_base = AVAHI_DNS_PACKET_DATA(p); + io.iov_len = p->size; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sa; + msg.msg_namelen = sizeof(sa); + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + +#ifdef IP_PKTINFO + if (interface > 0 || src_address) { + struct in_pktinfo *pkti; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + msg.msg_control = cmsg_data; + msg.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = msg.msg_controllen; + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + + pkti = (struct in_pktinfo*) CMSG_DATA(cmsg); + + if (interface > 0) + pkti->ipi_ifindex = interface; + + if (src_address) + pkti->ipi_spec_dst.s_addr = src_address->address; + } +#elif defined(IP_MULTICAST_IF) + if (src_address) { + struct in_addr any = { INADDR_ANY }; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, src_address ? &src_address->address : &any, sizeof(struct in_addr)) < 0) { + avahi_log_warn("IP_MULTICAST_IF failed: %s", strerror(errno)); + return -1; + } + } +#elif defined(IP_SENDSRCADDR) + if (src_address) { + struct in_addr *addr; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + msg.msg_control = cmsg_data; + msg.msg_controllen = CMSG_LEN(sizeof(struct in_addr)); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = msg.msg_controllen; + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + + addr = (struct in_addr *)CMSG_DATA(cmsg); + addr->s_addr = src_address->address; + } +#elif defined(__GNUC__) +#warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_MULTICAST_IF is not available" +#endif + + return sendmsg_loop(fd, &msg, 0); +} + +int avahi_send_dns_packet_ipv6( + int fd, + AvahiIfIndex interface, + AvahiDnsPacket *p, + const AvahiIPv6Address *src_address, + const AvahiIPv6Address *dst_address, + uint16_t dst_port) { + + struct sockaddr_in6 sa; + struct msghdr msg; + struct iovec io; + struct cmsghdr *cmsg; + size_t cmsg_data[(CMSG_SPACE(sizeof(struct in6_pktinfo))/sizeof(size_t)) + 1]; + + assert(fd >= 0); + assert(p); + assert(avahi_dns_packet_check_valid(p) >= 0); + assert(!dst_address || dst_port > 0); + + if (!dst_address) + mdns_mcast_group_ipv6(&sa); + else + ipv6_address_to_sockaddr(&sa, dst_address, dst_port); + + memset(&io, 0, sizeof(io)); + io.iov_base = AVAHI_DNS_PACKET_DATA(p); + io.iov_len = p->size; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sa; + msg.msg_namelen = sizeof(sa); + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + + if (interface > 0 || src_address) { + struct in6_pktinfo *pkti; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + msg.msg_control = cmsg_data; + msg.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo)); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = msg.msg_controllen; + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + + pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg); + + if (interface > 0) + pkti->ipi6_ifindex = interface; + + if (src_address) + memcpy(&pkti->ipi6_addr, src_address->address, sizeof(src_address->address)); + } else { + msg.msg_control = NULL; + msg.msg_controllen = 0; + } + + return sendmsg_loop(fd, &msg, 0); +} + +AvahiDnsPacket *avahi_recv_dns_packet_ipv4( + int fd, + AvahiIPv4Address *ret_src_address, + uint16_t *ret_src_port, + AvahiIPv4Address *ret_dst_address, + AvahiIfIndex *ret_iface, + uint8_t *ret_ttl) { + + AvahiDnsPacket *p= NULL; + struct msghdr msg; + struct iovec io; + size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */ + ssize_t l; + struct cmsghdr *cmsg; + int found_addr = 0; + int ms; + struct sockaddr_in sa; + + assert(fd >= 0); + + if (ioctl(fd, FIONREAD, &ms) < 0) { + avahi_log_warn("ioctl(): %s", strerror(errno)); + goto fail; + } + + if (ms < 0) { + avahi_log_warn("FIONREAD returned negative value."); + goto fail; + } + + p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE); + + io.iov_base = AVAHI_DNS_PACKET_DATA(p); + io.iov_len = p->max_size; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sa; + msg.msg_namelen = sizeof(sa); + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = aux; + msg.msg_controllen = sizeof(aux); + msg.msg_flags = 0; + + if ((l = recvmsg(fd, &msg, 0)) < 0) { + /* Linux returns EAGAIN when an invalid IP packet has been + received. We suppress warnings in this case because this might + create quite a bit of log traffic on machines with unstable + links. (See #60) */ + + if (errno != EAGAIN) + avahi_log_warn("recvmsg(): %s", strerror(errno)); + + goto fail; + } + + /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So + * fail after having read them. */ + if (!ms) + goto fail; + + if (sa.sin_addr.s_addr == INADDR_ANY) + /* Linux 2.4 behaves very strangely sometimes! */ + goto fail; + + assert(!(msg.msg_flags & MSG_CTRUNC)); + assert(!(msg.msg_flags & MSG_TRUNC)); + + p->size = (size_t) l; + + if (ret_src_port) + *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa); + + if (ret_src_address) { + AvahiAddress a; + avahi_address_from_sockaddr((struct sockaddr*) &sa, &a); + *ret_src_address = a.data.ipv4; + } + + if (ret_ttl) + *ret_ttl = 255; + + if (ret_iface) + *ret_iface = AVAHI_IF_UNSPEC; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + + if (cmsg->cmsg_level == IPPROTO_IP) { + + switch (cmsg->cmsg_type) { +#ifdef IP_RECVTTL + case IP_RECVTTL: +#endif + case IP_TTL: + if (ret_ttl) + *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); + + break; + +#ifdef IP_PKTINFO + case IP_PKTINFO: { + struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); + + if (ret_iface && i->ipi_ifindex > 0) + *ret_iface = (int) i->ipi_ifindex; + + if (ret_dst_address) + ret_dst_address->address = i->ipi_addr.s_addr; + + found_addr = 1; + + break; + } +#endif + +#ifdef IP_RECVIF + case IP_RECVIF: { + struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg); + + if (ret_iface) { +#ifdef __sun + if (*(uint_t*) sdl > 0) + *ret_iface = *(uint_t*) sdl; +#else + + if (sdl->sdl_index > 0) + *ret_iface = (int) sdl->sdl_index; +#endif + } + + break; + } +#endif + +#ifdef IP_RECVDSTADDR + case IP_RECVDSTADDR: + if (ret_dst_address) + memcpy(&ret_dst_address->address, CMSG_DATA (cmsg), 4); + + found_addr = 1; + break; +#endif + + default: + avahi_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type); + break; + } + } + } + + assert(found_addr); + + return p; + +fail: + if (p) + avahi_dns_packet_free(p); + + return NULL; +} + +AvahiDnsPacket *avahi_recv_dns_packet_ipv6( + int fd, + AvahiIPv6Address *ret_src_address, + uint16_t *ret_src_port, + AvahiIPv6Address *ret_dst_address, + AvahiIfIndex *ret_iface, + uint8_t *ret_ttl) { + + AvahiDnsPacket *p = NULL; + struct msghdr msg; + struct iovec io; + size_t aux[1024 / sizeof(size_t)]; + ssize_t l; + int ms; + struct cmsghdr *cmsg; + int found_ttl = 0, found_iface = 0; + struct sockaddr_in6 sa; + + assert(fd >= 0); + + if (ioctl(fd, FIONREAD, &ms) < 0) { + avahi_log_warn("ioctl(): %s", strerror(errno)); + goto fail; + } + + if (ms < 0) { + avahi_log_warn("FIONREAD returned negative value."); + goto fail; + } + + p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE); + + io.iov_base = AVAHI_DNS_PACKET_DATA(p); + io.iov_len = p->max_size; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (struct sockaddr*) &sa; + msg.msg_namelen = sizeof(sa); + + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = aux; + msg.msg_controllen = sizeof(aux); + msg.msg_flags = 0; + + if ((l = recvmsg(fd, &msg, 0)) < 0) { + /* Linux returns EAGAIN when an invalid IP packet has been + received. We suppress warnings in this case because this might + create quite a bit of log traffic on machines with unstable + links. (See #60) */ + + if (errno != EAGAIN) + avahi_log_warn("recvmsg(): %s", strerror(errno)); + + goto fail; + } + + /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So + * fail after having read them. */ + if (!ms) + goto fail; + + assert(!(msg.msg_flags & MSG_CTRUNC)); + assert(!(msg.msg_flags & MSG_TRUNC)); + + p->size = (size_t) l; + + if (ret_src_port) + *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa); + + if (ret_src_address) { + AvahiAddress a; + avahi_address_from_sockaddr((struct sockaddr*) &sa, &a); + *ret_src_address = a.data.ipv6; + } + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + + if (cmsg->cmsg_level == IPPROTO_IPV6) { + + switch (cmsg->cmsg_type) { + + case IPV6_HOPLIMIT: + + if (ret_ttl) + *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); + + found_ttl = 1; + + break; + + case IPV6_PKTINFO: { + struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); + + if (ret_iface && i->ipi6_ifindex > 0) + *ret_iface = i->ipi6_ifindex; + + if (ret_dst_address) + memcpy(ret_dst_address->address, i->ipi6_addr.s6_addr, 16); + + found_iface = 1; + break; + } + + default: + avahi_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type); + break; + } + } + } + + assert(found_iface); + assert(found_ttl); + + return p; + +fail: + if (p) + avahi_dns_packet_free(p); + + return NULL; +} + +int avahi_open_unicast_socket_ipv4(void) { + struct sockaddr_in local; + int fd = -1; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + avahi_log_warn("socket() failed: %s", strerror(errno)); + goto fail; + } + + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + + if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { + avahi_log_warn("bind() failed: %s", strerror(errno)); + goto fail; + } + + if (ipv4_pktinfo(fd) < 0) { + goto fail; + } + + if (avahi_set_cloexec(fd) < 0) { + avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); + goto fail; + } + + if (avahi_set_nonblock(fd) < 0) { + avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); + goto fail; + } + + return fd; + +fail: + if (fd >= 0) + close(fd); + + return -1; +} + +int avahi_open_unicast_socket_ipv6(void) { + struct sockaddr_in6 local; + int fd = -1, yes; + + if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + avahi_log_warn("socket() failed: %s", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) { + avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno)); + goto fail; + } + + memset(&local, 0, sizeof(local)); + local.sin6_family = AF_INET6; + + if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { + avahi_log_warn("bind() failed: %s", strerror(errno)); + goto fail; + } + + if (ipv6_pktinfo(fd) < 0) + goto fail; + + if (avahi_set_cloexec(fd) < 0) { + avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); + goto fail; + } + + if (avahi_set_nonblock(fd) < 0) { + avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); + goto fail; + } + + return fd; + +fail: + if (fd >= 0) + close(fd); + + return -1; +} diff --git a/3rdparty/QtZeroConf/avahi-core/socket.h b/3rdparty/QtZeroConf/avahi-core/socket.h new file mode 100644 index 000000000..92f12d741 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/socket.h @@ -0,0 +1,47 @@ +#ifndef foosockethfoo +#define foosockethfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "dns.h" + +#define AVAHI_MDNS_PORT 5353 +#define AVAHI_DNS_PORT 53 +#define AVAHI_IPV4_MCAST_GROUP "224.0.0.251" +#define AVAHI_IPV6_MCAST_GROUP "ff02::fb" + +int avahi_open_socket_ipv4(int no_reuse); +int avahi_open_socket_ipv6(int no_reuse); + +int avahi_open_unicast_socket_ipv4(void); +int avahi_open_unicast_socket_ipv6(void); + +int avahi_send_dns_packet_ipv4(int fd, AvahiIfIndex iface, AvahiDnsPacket *p, const AvahiIPv4Address *src_address, const AvahiIPv4Address *dst_address, uint16_t dst_port); +int avahi_send_dns_packet_ipv6(int fd, AvahiIfIndex iface, AvahiDnsPacket *p, const AvahiIPv6Address *src_address, const AvahiIPv6Address *dst_address, uint16_t dst_port); + +AvahiDnsPacket *avahi_recv_dns_packet_ipv4(int fd, AvahiIPv4Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv4Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl); +AvahiDnsPacket *avahi_recv_dns_packet_ipv6(int fd, AvahiIPv6Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv6Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl); + +int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *local_address, int iface, int join); +int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *local_address, int iface, int join); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/timeeventq.c b/3rdparty/QtZeroConf/avahi-core/timeeventq.c new file mode 100644 index 000000000..2799bf242 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/timeeventq.c @@ -0,0 +1,225 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include + +#include "timeeventq.h" +#include "log.h" + +struct AvahiTimeEvent { + AvahiTimeEventQueue *queue; + AvahiPrioQueueNode *node; + struct timeval expiry; + struct timeval last_run; + AvahiTimeEventCallback callback; + void* userdata; +}; + +struct AvahiTimeEventQueue { + const AvahiPoll *poll_api; + AvahiPrioQueue *prioq; + AvahiTimeout *timeout; +}; + +static int compare(const void* _a, const void* _b) { + const AvahiTimeEvent *a = _a, *b = _b; + int ret; + + if ((ret = avahi_timeval_compare(&a->expiry, &b->expiry)) != 0) + return ret; + + /* If both exevents are scheduled for the same time, put the entry + * that has been run earlier the last time first. */ + return avahi_timeval_compare(&a->last_run, &b->last_run); +} + +static AvahiTimeEvent* time_event_queue_root(AvahiTimeEventQueue *q) { + assert(q); + + return q->prioq->root ? q->prioq->root->data : NULL; +} + +static void update_timeout(AvahiTimeEventQueue *q) { + AvahiTimeEvent *e; + assert(q); + + if ((e = time_event_queue_root(q))) + q->poll_api->timeout_update(q->timeout, &e->expiry); + else + q->poll_api->timeout_update(q->timeout, NULL); +} + +static void expiration_event(AVAHI_GCC_UNUSED AvahiTimeout *timeout, void *userdata) { + AvahiTimeEventQueue *q = userdata; + AvahiTimeEvent *e; + + if ((e = time_event_queue_root(q))) { + struct timeval now; + + gettimeofday(&now, NULL); + + /* Check if expired */ + if (avahi_timeval_compare(&now, &e->expiry) >= 0) { + + /* Make sure to move the entry away from the front */ + e->last_run = now; + avahi_prio_queue_shuffle(q->prioq, e->node); + + /* Run it */ + assert(e->callback); + e->callback(e, e->userdata); + + update_timeout(q); + return; + } + } + + avahi_log_debug(__FILE__": Strange, expiration_event() called, but nothing really happened."); + update_timeout(q); +} + +static void fix_expiry_time(AvahiTimeEvent *e) { + struct timeval now; + assert(e); + + return; /*** DO WE REALLY NEED THIS? ***/ + + gettimeofday(&now, NULL); + + if (avahi_timeval_compare(&now, &e->expiry) > 0) + e->expiry = now; +} + +AvahiTimeEventQueue* avahi_time_event_queue_new(const AvahiPoll *poll_api) { + AvahiTimeEventQueue *q; + + if (!(q = avahi_new(AvahiTimeEventQueue, 1))) { + avahi_log_error(__FILE__": Out of memory"); + goto oom; + } + + q->poll_api = poll_api; + + if (!(q->prioq = avahi_prio_queue_new(compare))) + goto oom; + + if (!(q->timeout = poll_api->timeout_new(poll_api, NULL, expiration_event, q))) + goto oom; + + return q; + +oom: + + if (q) { + avahi_free(q); + + if (q->prioq) + avahi_prio_queue_free(q->prioq); + } + + return NULL; +} + +void avahi_time_event_queue_free(AvahiTimeEventQueue *q) { + AvahiTimeEvent *e; + + assert(q); + + while ((e = time_event_queue_root(q))) + avahi_time_event_free(e); + avahi_prio_queue_free(q->prioq); + + q->poll_api->timeout_free(q->timeout); + + avahi_free(q); +} + +AvahiTimeEvent* avahi_time_event_new( + AvahiTimeEventQueue *q, + const struct timeval *timeval, + AvahiTimeEventCallback callback, + void* userdata) { + + AvahiTimeEvent *e; + + assert(q); + assert(callback); + assert(userdata); + + if (!(e = avahi_new(AvahiTimeEvent, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; /* OOM */ + } + + e->queue = q; + e->callback = callback; + e->userdata = userdata; + + if (timeval) + e->expiry = *timeval; + else { + e->expiry.tv_sec = 0; + e->expiry.tv_usec = 0; + } + + fix_expiry_time(e); + + e->last_run.tv_sec = 0; + e->last_run.tv_usec = 0; + + if (!(e->node = avahi_prio_queue_put(q->prioq, e))) { + avahi_free(e); + return NULL; + } + + update_timeout(q); + return e; +} + +void avahi_time_event_free(AvahiTimeEvent *e) { + AvahiTimeEventQueue *q; + assert(e); + + q = e->queue; + + avahi_prio_queue_remove(q->prioq, e->node); + avahi_free(e); + + update_timeout(q); +} + +void avahi_time_event_update(AvahiTimeEvent *e, const struct timeval *timeval) { + assert(e); + assert(timeval); + + e->expiry = *timeval; + fix_expiry_time(e); + avahi_prio_queue_shuffle(e->queue->prioq, e->node); + + update_timeout(e->queue); +} + diff --git a/3rdparty/QtZeroConf/avahi-core/timeeventq.h b/3rdparty/QtZeroConf/avahi-core/timeeventq.h new file mode 100644 index 000000000..a695b6aaf --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/timeeventq.h @@ -0,0 +1,46 @@ +#ifndef footimeeventqhfoo +#define footimeeventqhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +typedef struct AvahiTimeEventQueue AvahiTimeEventQueue; +typedef struct AvahiTimeEvent AvahiTimeEvent; + +#include + +#include "prioq.h" + +typedef void (*AvahiTimeEventCallback)(AvahiTimeEvent *e, void* userdata); + +AvahiTimeEventQueue* avahi_time_event_queue_new(const AvahiPoll *poll_api); +void avahi_time_event_queue_free(AvahiTimeEventQueue *q); + +AvahiTimeEvent* avahi_time_event_new( + AvahiTimeEventQueue *q, + const struct timeval *timeval, + AvahiTimeEventCallback callback, + void* userdata); + +void avahi_time_event_free(AvahiTimeEvent *e); +void avahi_time_event_update(AvahiTimeEvent *e, const struct timeval *timeval); + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/util.c b/3rdparty/QtZeroConf/avahi-core/util.c new file mode 100644 index 000000000..21ef94c4f --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/util.c @@ -0,0 +1,120 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include "util.h" + +void avahi_hexdump(const void* p, size_t size) { + const uint8_t *c = p; + assert(p); + + printf("Dumping %lu bytes from %p:\n", (unsigned long) size, p); + + while (size > 0) { + unsigned i; + + for (i = 0; i < 16; i++) { + if (i < size) + printf("%02x ", c[i]); + else + printf(" "); + } + + for (i = 0; i < 16; i++) { + if (i < size) + printf("%c", c[i] >= 32 && c[i] < 127 ? c[i] : '.'); + else + printf(" "); + } + + printf("\n"); + + c += 16; + + if (size <= 16) + break; + + size -= 16; + } +} + +char *avahi_format_mac_address(char *r, size_t l, const uint8_t* mac, size_t size) { + char *t = r; + unsigned i; + static const char hex[] = "0123456789abcdef"; + + assert(r); + assert(l > 0); + assert(mac); + + if (size <= 0) { + *r = 0; + return r; + } + + for (i = 0; i < size; i++) { + if (l < 3) + break; + + *(t++) = hex[*mac >> 4]; + *(t++) = hex[*mac & 0xF]; + *(t++) = ':'; + + l -= 3; + + mac++; + } + + if (t > r) + *(t-1) = 0; + else + *r = 0; + + return r; +} + +char *avahi_strup(char *s) { + char *c; + assert(s); + + for (c = s; *c; c++) + *c = (char) toupper(*c); + + return s; +} + +char *avahi_strdown(char *s) { + char *c; + assert(s); + + for (c = s; *c; c++) + *c = (char) tolower(*c); + + return s; +} diff --git a/3rdparty/QtZeroConf/avahi-core/util.h b/3rdparty/QtZeroConf/avahi-core/util.h new file mode 100644 index 000000000..e13c334ff --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/util.h @@ -0,0 +1,41 @@ +#ifndef fooutilhfoo +#define fooutilhfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include + +AVAHI_C_DECL_BEGIN + +void avahi_hexdump(const void *p, size_t size); + +char *avahi_format_mac_address(char *t, size_t l, const uint8_t* mac, size_t size); + +/** Change every character in the string to upper case (ASCII), return a pointer to the string */ +char *avahi_strup(char *s); + +/** Change every character in the string to lower case (ASCII), return a pointer to the string */ +char *avahi_strdown(char *s); + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-core/wide-area.c b/3rdparty/QtZeroConf/avahi-core/wide-area.c new file mode 100644 index 000000000..d5e64e57d --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/wide-area.c @@ -0,0 +1,723 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "internal.h" +#include "browse.h" +#include "socket.h" +#include "log.h" +#include "hashmap.h" +#include "wide-area.h" +#include "addr-util.h" +#include "rr-util.h" + +#define CACHE_ENTRIES_MAX 500 + +typedef struct AvahiWideAreaCacheEntry AvahiWideAreaCacheEntry; + +struct AvahiWideAreaCacheEntry { + AvahiWideAreaLookupEngine *engine; + + AvahiRecord *record; + struct timeval timestamp; + struct timeval expiry; + + AvahiTimeEvent *time_event; + + AVAHI_LLIST_FIELDS(AvahiWideAreaCacheEntry, by_key); + AVAHI_LLIST_FIELDS(AvahiWideAreaCacheEntry, cache); +}; + +struct AvahiWideAreaLookup { + AvahiWideAreaLookupEngine *engine; + int dead; + + uint32_t id; /* effectively just an uint16_t, but we need it as an index for a hash table */ + AvahiTimeEvent *time_event; + + AvahiKey *key, *cname_key; + + int n_send; + AvahiDnsPacket *packet; + + AvahiWideAreaLookupCallback callback; + void *userdata; + + AvahiAddress dns_server_used; + + AVAHI_LLIST_FIELDS(AvahiWideAreaLookup, lookups); + AVAHI_LLIST_FIELDS(AvahiWideAreaLookup, by_key); +}; + +struct AvahiWideAreaLookupEngine { + AvahiServer *server; + + int fd_ipv4, fd_ipv6; + AvahiWatch *watch_ipv4, *watch_ipv6; + + uint16_t next_id; + + /* Cache */ + AVAHI_LLIST_HEAD(AvahiWideAreaCacheEntry, cache); + AvahiHashmap *cache_by_key; + unsigned cache_n_entries; + + /* Lookups */ + AVAHI_LLIST_HEAD(AvahiWideAreaLookup, lookups); + AvahiHashmap *lookups_by_id; + AvahiHashmap *lookups_by_key; + + int cleanup_dead; + + AvahiAddress dns_servers[AVAHI_WIDE_AREA_SERVERS_MAX]; + unsigned n_dns_servers; + unsigned current_dns_server; +}; + +static AvahiWideAreaLookup* find_lookup(AvahiWideAreaLookupEngine *e, uint16_t id) { + AvahiWideAreaLookup *l; + int i = (int) id; + + assert(e); + + if (!(l = avahi_hashmap_lookup(e->lookups_by_id, &i))) + return NULL; + + assert(l->id == id); + + if (l->dead) + return NULL; + + return l; +} + +static int send_to_dns_server(AvahiWideAreaLookup *l, AvahiDnsPacket *p) { + AvahiAddress *a; + + assert(l); + assert(p); + + if (l->engine->n_dns_servers <= 0) + return -1; + + assert(l->engine->current_dns_server < l->engine->n_dns_servers); + + a = &l->engine->dns_servers[l->engine->current_dns_server]; + l->dns_server_used = *a; + + if (a->proto == AVAHI_PROTO_INET) { + + if (l->engine->fd_ipv4 < 0) + return -1; + + return avahi_send_dns_packet_ipv4(l->engine->fd_ipv4, AVAHI_IF_UNSPEC, p, NULL, &a->data.ipv4, AVAHI_DNS_PORT); + + } else { + assert(a->proto == AVAHI_PROTO_INET6); + + if (l->engine->fd_ipv6 < 0) + return -1; + + return avahi_send_dns_packet_ipv6(l->engine->fd_ipv6, AVAHI_IF_UNSPEC, p, NULL, &a->data.ipv6, AVAHI_DNS_PORT); + } +} + +static void next_dns_server(AvahiWideAreaLookupEngine *e) { + assert(e); + + e->current_dns_server++; + + if (e->current_dns_server >= e->n_dns_servers) + e->current_dns_server = 0; +} + +static void lookup_stop(AvahiWideAreaLookup *l) { + assert(l); + + l->callback = NULL; + + if (l->time_event) { + avahi_time_event_free(l->time_event); + l->time_event = NULL; + } +} + +static void sender_timeout_callback(AvahiTimeEvent *e, void *userdata) { + AvahiWideAreaLookup *l = userdata; + struct timeval tv; + + assert(l); + + /* Try another DNS server after three retries */ + if (l->n_send >= 3 && avahi_address_cmp(&l->engine->dns_servers[l->engine->current_dns_server], &l->dns_server_used) == 0) { + next_dns_server(l->engine); + + if (avahi_address_cmp(&l->engine->dns_servers[l->engine->current_dns_server], &l->dns_server_used) == 0) + /* There is no other DNS server, fail */ + l->n_send = 1000; + } + + if (l->n_send >= 6) { + avahi_log_warn(__FILE__": Query timed out."); + avahi_server_set_errno(l->engine->server, AVAHI_ERR_TIMEOUT); + l->callback(l->engine, AVAHI_BROWSER_FAILURE, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata); + lookup_stop(l); + return; + } + + assert(l->packet); + send_to_dns_server(l, l->packet); + l->n_send++; + + avahi_time_event_update(e, avahi_elapse_time(&tv, 1000, 0)); +} + +AvahiWideAreaLookup *avahi_wide_area_lookup_new( + AvahiWideAreaLookupEngine *e, + AvahiKey *key, + AvahiWideAreaLookupCallback callback, + void *userdata) { + + struct timeval tv; + AvahiWideAreaLookup *l, *t; + uint8_t *p; + + assert(e); + assert(key); + assert(callback); + assert(userdata); + + l = avahi_new(AvahiWideAreaLookup, 1); + l->engine = e; + l->dead = 0; + l->key = avahi_key_ref(key); + l->cname_key = avahi_key_new_cname(l->key); + l->callback = callback; + l->userdata = userdata; + + /* If more than 65K wide area quries are issued simultaneously, + * this will break. This should be limited by some higher level */ + + for (;; e->next_id++) + if (!find_lookup(e, e->next_id)) + break; /* This ID is not yet used. */ + + l->id = e->next_id++; + + /* We keep the packet around in case we need to repeat our query */ + l->packet = avahi_dns_packet_new(0); + + avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_ID, (uint16_t) l->id); + avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 1, 0, 0, 0, 0, 0)); + + p = avahi_dns_packet_append_key(l->packet, key, 0); + assert(p); + + avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_QDCOUNT, 1); + + if (send_to_dns_server(l, l->packet) < 0) { + avahi_log_error(__FILE__": Failed to send packet."); + avahi_dns_packet_free(l->packet); + avahi_key_unref(l->key); + if (l->cname_key) + avahi_key_unref(l->cname_key); + avahi_free(l); + return NULL; + } + + l->n_send = 1; + + l->time_event = avahi_time_event_new(e->server->time_event_queue, avahi_elapse_time(&tv, 500, 0), sender_timeout_callback, l); + + avahi_hashmap_insert(e->lookups_by_id, &l->id, l); + + t = avahi_hashmap_lookup(e->lookups_by_key, l->key); + AVAHI_LLIST_PREPEND(AvahiWideAreaLookup, by_key, t, l); + avahi_hashmap_replace(e->lookups_by_key, avahi_key_ref(l->key), t); + + AVAHI_LLIST_PREPEND(AvahiWideAreaLookup, lookups, e->lookups, l); + + return l; +} + +static void lookup_destroy(AvahiWideAreaLookup *l) { + AvahiWideAreaLookup *t; + assert(l); + + lookup_stop(l); + + t = avahi_hashmap_lookup(l->engine->lookups_by_key, l->key); + AVAHI_LLIST_REMOVE(AvahiWideAreaLookup, by_key, t, l); + if (t) + avahi_hashmap_replace(l->engine->lookups_by_key, avahi_key_ref(l->key), t); + else + avahi_hashmap_remove(l->engine->lookups_by_key, l->key); + + AVAHI_LLIST_REMOVE(AvahiWideAreaLookup, lookups, l->engine->lookups, l); + + avahi_hashmap_remove(l->engine->lookups_by_id, &l->id); + avahi_dns_packet_free(l->packet); + + if (l->key) + avahi_key_unref(l->key); + + if (l->cname_key) + avahi_key_unref(l->cname_key); + + avahi_free(l); +} + +void avahi_wide_area_lookup_free(AvahiWideAreaLookup *l) { + assert(l); + + if (l->dead) + return; + + l->dead = 1; + l->engine->cleanup_dead = 1; + lookup_stop(l); +} + +void avahi_wide_area_cleanup(AvahiWideAreaLookupEngine *e) { + AvahiWideAreaLookup *l, *n; + assert(e); + + while (e->cleanup_dead) { + e->cleanup_dead = 0; + + for (l = e->lookups; l; l = n) { + n = l->lookups_next; + + if (l->dead) + lookup_destroy(l); + } + } +} + +static void cache_entry_free(AvahiWideAreaCacheEntry *c) { + AvahiWideAreaCacheEntry *t; + assert(c); + + if (c->time_event) + avahi_time_event_free(c->time_event); + + AVAHI_LLIST_REMOVE(AvahiWideAreaCacheEntry, cache, c->engine->cache, c); + + t = avahi_hashmap_lookup(c->engine->cache_by_key, c->record->key); + AVAHI_LLIST_REMOVE(AvahiWideAreaCacheEntry, by_key, t, c); + if (t) + avahi_hashmap_replace(c->engine->cache_by_key, avahi_key_ref(c->record->key), t); + else + avahi_hashmap_remove(c->engine->cache_by_key, c->record->key); + + c->engine->cache_n_entries --; + + avahi_record_unref(c->record); + avahi_free(c); +} + +static void expiry_event(AvahiTimeEvent *te, void *userdata) { + AvahiWideAreaCacheEntry *e = userdata; + + assert(te); + assert(e); + + cache_entry_free(e); +} + +static AvahiWideAreaCacheEntry* find_record_in_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { + AvahiWideAreaCacheEntry *c; + + assert(e); + assert(r); + + for (c = avahi_hashmap_lookup(e->cache_by_key, r->key); c; c = c->by_key_next) + if (avahi_record_equal_no_ttl(r, c->record)) + return c; + + return NULL; +} + +static void run_callbacks(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { + AvahiWideAreaLookup *l; + + assert(e); + assert(r); + + for (l = avahi_hashmap_lookup(e->lookups_by_key, r->key); l; l = l->by_key_next) { + if (l->dead || !l->callback) + continue; + + l->callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_RESULT_WIDE_AREA, r, l->userdata); + } + + if (r->key->clazz == AVAHI_DNS_CLASS_IN && r->key->type == AVAHI_DNS_TYPE_CNAME) { + /* It's a CNAME record, so we have to scan the all lookups to see if one matches */ + + for (l = e->lookups; l; l = l->lookups_next) { + AvahiKey *key; + + if (l->dead || !l->callback) + continue; + + if ((key = avahi_key_new_cname(l->key))) { + if (avahi_key_equal(r->key, key)) + l->callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_RESULT_WIDE_AREA, r, l->userdata); + + avahi_key_unref(key); + } + } + } +} + +static void add_to_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { + AvahiWideAreaCacheEntry *c; + int is_new; + + assert(e); + assert(r); + + if ((c = find_record_in_cache(e, r))) { + is_new = 0; + + /* Update the existing entry */ + avahi_record_unref(c->record); + } else { + AvahiWideAreaCacheEntry *t; + + is_new = 1; + + /* Enforce cache size */ + if (e->cache_n_entries >= CACHE_ENTRIES_MAX) + /* Eventually we should improve the caching algorithm here */ + goto finish; + + c = avahi_new(AvahiWideAreaCacheEntry, 1); + c->engine = e; + c->time_event = NULL; + + AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, cache, e->cache, c); + + /* Add the new entry to the cache entry hash table */ + t = avahi_hashmap_lookup(e->cache_by_key, r->key); + AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, by_key, t, c); + avahi_hashmap_replace(e->cache_by_key, avahi_key_ref(r->key), t); + + e->cache_n_entries ++; + } + + c->record = avahi_record_ref(r); + + gettimeofday(&c->timestamp, NULL); + c->expiry = c->timestamp; + avahi_timeval_add(&c->expiry, r->ttl * 1000000); + + if (c->time_event) + avahi_time_event_update(c->time_event, &c->expiry); + else + c->time_event = avahi_time_event_new(e->server->time_event_queue, &c->expiry, expiry_event, c); + +finish: + + if (is_new) + run_callbacks(e, r); +} + +static int map_dns_error(uint16_t error) { + static const int table[16] = { + AVAHI_OK, + AVAHI_ERR_DNS_FORMERR, + AVAHI_ERR_DNS_SERVFAIL, + AVAHI_ERR_DNS_NXDOMAIN, + AVAHI_ERR_DNS_NOTIMP, + AVAHI_ERR_DNS_REFUSED, + AVAHI_ERR_DNS_YXDOMAIN, + AVAHI_ERR_DNS_YXRRSET, + AVAHI_ERR_DNS_NXRRSET, + AVAHI_ERR_DNS_NOTAUTH, + AVAHI_ERR_DNS_NOTZONE, + AVAHI_ERR_INVALID_DNS_ERROR, + AVAHI_ERR_INVALID_DNS_ERROR, + AVAHI_ERR_INVALID_DNS_ERROR, + AVAHI_ERR_INVALID_DNS_ERROR, + AVAHI_ERR_INVALID_DNS_ERROR + }; + + assert(error <= 15); + + return table[error]; +} + +static void handle_packet(AvahiWideAreaLookupEngine *e, AvahiDnsPacket *p) { + AvahiWideAreaLookup *l = NULL; + int i, r; + + AvahiBrowserEvent final_event = AVAHI_BROWSER_ALL_FOR_NOW; + + assert(e); + assert(p); + + /* Some superficial validity tests */ + if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) { + avahi_log_warn(__FILE__": Ignoring invalid response for wide area datagram."); + goto finish; + } + + /* Look for the lookup that issued this query */ + if (!(l = find_lookup(e, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID))) || l->dead) + goto finish; + + /* Check whether this a packet indicating a failure */ + if ((r = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & 15) != 0 || + avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0) { + + avahi_server_set_errno(e->server, r == 0 ? AVAHI_ERR_NOT_FOUND : map_dns_error(r)); + /* Tell the user about the failure */ + final_event = AVAHI_BROWSER_FAILURE; + + /* We go on here, since some of the records contained in the + reply might be interesting in some way */ + } + + /* Skip over the question */ + for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); i > 0; i--) { + AvahiKey *k; + + if (!(k = avahi_dns_packet_consume_key(p, NULL))) { + avahi_log_warn(__FILE__": Wide area response packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)"); + avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET); + final_event = AVAHI_BROWSER_FAILURE; + goto finish; + } + + avahi_key_unref(k); + } + + /* Process responses */ + for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) + + (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) + + (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); i > 0; i--) { + + AvahiRecord *rr; + + if (!(rr = avahi_dns_packet_consume_record(p, NULL))) { + avahi_log_warn(__FILE__": Wide area response packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)"); + avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET); + final_event = AVAHI_BROWSER_FAILURE; + goto finish; + } + + add_to_cache(e, rr); + avahi_record_unref(rr); + } + +finish: + + if (l && !l->dead) { + if (l->callback) + l->callback(e, final_event, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata); + + lookup_stop(l); + } +} + +static void socket_event(AVAHI_GCC_UNUSED AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent events, void *userdata) { + AvahiWideAreaLookupEngine *e = userdata; + AvahiDnsPacket *p = NULL; + + if (fd == e->fd_ipv4) + p = avahi_recv_dns_packet_ipv4(e->fd_ipv4, NULL, NULL, NULL, NULL, NULL); + else { + assert(fd == e->fd_ipv6); + p = avahi_recv_dns_packet_ipv6(e->fd_ipv6, NULL, NULL, NULL, NULL, NULL); + } + + if (p) { + handle_packet(e, p); + avahi_dns_packet_free(p); + } +} + +AvahiWideAreaLookupEngine *avahi_wide_area_engine_new(AvahiServer *s) { + AvahiWideAreaLookupEngine *e; + + assert(s); + + e = avahi_new(AvahiWideAreaLookupEngine, 1); + e->server = s; + e->cleanup_dead = 0; + + /* Create sockets */ + e->fd_ipv4 = s->config.use_ipv4 ? avahi_open_unicast_socket_ipv4() : -1; + e->fd_ipv6 = s->config.use_ipv6 ? avahi_open_unicast_socket_ipv6() : -1; + + if (e->fd_ipv4 < 0 && e->fd_ipv6 < 0) { + avahi_log_error(__FILE__": Failed to create wide area sockets: %s", strerror(errno)); + + if (e->fd_ipv6 >= 0) + close(e->fd_ipv6); + + if (e->fd_ipv4 >= 0) + close(e->fd_ipv4); + + avahi_free(e); + return NULL; + } + + /* Create watches */ + + e->watch_ipv4 = e->watch_ipv6 = NULL; + + if (e->fd_ipv4 >= 0) + e->watch_ipv4 = s->poll_api->watch_new(e->server->poll_api, e->fd_ipv4, AVAHI_WATCH_IN, socket_event, e); + if (e->fd_ipv6 >= 0) + e->watch_ipv6 = s->poll_api->watch_new(e->server->poll_api, e->fd_ipv6, AVAHI_WATCH_IN, socket_event, e); + + e->n_dns_servers = e->current_dns_server = 0; + e->next_id = (uint16_t) rand(); + + /* Initialize cache */ + AVAHI_LLIST_HEAD_INIT(AvahiWideAreaCacheEntry, e->cache); + e->cache_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL); + e->cache_n_entries = 0; + + /* Initialize lookup list */ + e->lookups_by_id = avahi_hashmap_new((AvahiHashFunc) avahi_int_hash, (AvahiEqualFunc) avahi_int_equal, NULL, NULL); + e->lookups_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL); + AVAHI_LLIST_HEAD_INIT(AvahiWideAreaLookup, e->lookups); + + return e; +} + +void avahi_wide_area_engine_free(AvahiWideAreaLookupEngine *e) { + assert(e); + + avahi_wide_area_clear_cache(e); + + while (e->lookups) + lookup_destroy(e->lookups); + + avahi_hashmap_free(e->cache_by_key); + avahi_hashmap_free(e->lookups_by_id); + avahi_hashmap_free(e->lookups_by_key); + + if (e->watch_ipv4) + e->server->poll_api->watch_free(e->watch_ipv4); + + if (e->watch_ipv6) + e->server->poll_api->watch_free(e->watch_ipv6); + + if (e->fd_ipv6 >= 0) + close(e->fd_ipv6); + + if (e->fd_ipv4 >= 0) + close(e->fd_ipv4); + + avahi_free(e); +} + +void avahi_wide_area_clear_cache(AvahiWideAreaLookupEngine *e) { + assert(e); + + while (e->cache) + cache_entry_free(e->cache); + + assert(e->cache_n_entries == 0); +} + +void avahi_wide_area_set_servers(AvahiWideAreaLookupEngine *e, const AvahiAddress *a, unsigned n) { + assert(e); + + if (a) { + for (e->n_dns_servers = 0; n > 0 && e->n_dns_servers < AVAHI_WIDE_AREA_SERVERS_MAX; a++, n--) + if ((a->proto == AVAHI_PROTO_INET && e->fd_ipv4 >= 0) || (a->proto == AVAHI_PROTO_INET6 && e->fd_ipv6 >= 0)) + e->dns_servers[e->n_dns_servers++] = *a; + } else { + assert(n == 0); + e->n_dns_servers = 0; + } + + e->current_dns_server = 0; + + avahi_wide_area_clear_cache(e); +} + +void avahi_wide_area_cache_dump(AvahiWideAreaLookupEngine *e, AvahiDumpCallback callback, void* userdata) { + AvahiWideAreaCacheEntry *c; + + assert(e); + assert(callback); + + callback(";; WIDE AREA CACHE ;;; ", userdata); + + for (c = e->cache; c; c = c->cache_next) { + char *t = avahi_record_to_string(c->record); + callback(t, userdata); + avahi_free(t); + } +} + +unsigned avahi_wide_area_scan_cache(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata) { + AvahiWideAreaCacheEntry *c; + AvahiKey *cname_key; + unsigned n = 0; + + assert(e); + assert(key); + assert(callback); + + for (c = avahi_hashmap_lookup(e->cache_by_key, key); c; c = c->by_key_next) { + callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_RESULT_WIDE_AREA|AVAHI_LOOKUP_RESULT_CACHED, c->record, userdata); + n++; + } + + if ((cname_key = avahi_key_new_cname(key))) { + + for (c = avahi_hashmap_lookup(e->cache_by_key, cname_key); c; c = c->by_key_next) { + callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_RESULT_WIDE_AREA|AVAHI_LOOKUP_RESULT_CACHED, c->record, userdata); + n++; + } + + avahi_key_unref(cname_key); + } + + return n; +} + +int avahi_wide_area_has_servers(AvahiWideAreaLookupEngine *e) { + assert(e); + + return e->n_dns_servers > 0; +} + + + diff --git a/3rdparty/QtZeroConf/avahi-core/wide-area.h b/3rdparty/QtZeroConf/avahi-core/wide-area.h new file mode 100644 index 000000000..b1dc570f9 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-core/wide-area.h @@ -0,0 +1,52 @@ +#ifndef foowideareahfoo +#define foowideareahfoo + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "lookup.h" +#include "browse.h" + +typedef struct AvahiWideAreaLookupEngine AvahiWideAreaLookupEngine; +typedef struct AvahiWideAreaLookup AvahiWideAreaLookup; + +typedef void (*AvahiWideAreaLookupCallback)( + AvahiWideAreaLookupEngine *e, + AvahiBrowserEvent event, + AvahiLookupResultFlags flags, + AvahiRecord *r, + void *userdata); + +AvahiWideAreaLookupEngine *avahi_wide_area_engine_new(AvahiServer *s); +void avahi_wide_area_engine_free(AvahiWideAreaLookupEngine *e); + +unsigned avahi_wide_area_scan_cache(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata); +void avahi_wide_area_cache_dump(AvahiWideAreaLookupEngine *e, AvahiDumpCallback callback, void* userdata); +void avahi_wide_area_set_servers(AvahiWideAreaLookupEngine *e, const AvahiAddress *a, unsigned n); +void avahi_wide_area_clear_cache(AvahiWideAreaLookupEngine *e); +void avahi_wide_area_cleanup(AvahiWideAreaLookupEngine *e); +int avahi_wide_area_has_servers(AvahiWideAreaLookupEngine *e); + +AvahiWideAreaLookup *avahi_wide_area_lookup_new(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata); +void avahi_wide_area_lookup_free(AvahiWideAreaLookup *q); + + + +#endif + diff --git a/3rdparty/QtZeroConf/avahi-qt/qt-watch.cpp b/3rdparty/QtZeroConf/avahi-qt/qt-watch.cpp new file mode 100644 index 000000000..13e459f9d --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-qt/qt-watch.cpp @@ -0,0 +1,133 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ +#include +#include +#include "qt-watch.h" +#include "qt-watch_p.h" + +AvahiWatch::AvahiWatch(int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void* userdata) : + m_in(0), m_out(0), m_callback(callback), m_fd(fd), m_userdata(userdata), m_incallback(false) +{ + setWatchedEvents(event); +} + +void AvahiWatch::gotIn() +{ + m_lastEvent = AVAHI_WATCH_IN; + m_incallback=true; + m_callback(this,m_fd,m_lastEvent,m_userdata); + m_incallback=false; +} + +void AvahiWatch::gotOut() +{ + m_lastEvent = AVAHI_WATCH_IN; + m_incallback=true; + m_callback(this,m_fd,m_lastEvent,m_userdata); + m_incallback=false; +} + +void AvahiWatch::setWatchedEvents(AvahiWatchEvent event) +{ + if (!(event & AVAHI_WATCH_IN)) { delete m_in; m_in=0; } + if (!(event & AVAHI_WATCH_OUT)) { delete m_out; m_out=0; } + if (event & AVAHI_WATCH_IN) { + m_in = new QSocketNotifier(m_fd,QSocketNotifier::Read, this); + connect(m_in,SIGNAL(activated(int)),SLOT(gotIn())); + } + if (event & AVAHI_WATCH_OUT) { + m_out = new QSocketNotifier(m_fd,QSocketNotifier::Write, this); + connect(m_out,SIGNAL(activated(int)),SLOT(gotOut())); + } +} + +AvahiTimeout::AvahiTimeout(const struct timeval* tv, AvahiTimeoutCallback callback, void *userdata) : + m_callback(callback), m_userdata(userdata) +{ + connect(&m_timer, SIGNAL(timeout()), this, SLOT(timeout())); + m_timer.setSingleShot(true); + update(tv); +} + +void AvahiTimeout::update(const struct timeval *tv) +{ + m_timer.stop(); + if (tv) { + AvahiUsec u = avahi_age(tv)/1000; + m_timer.start( (u>0) ? 0 : -u); + } +} + +void AvahiTimeout::timeout() +{ + m_callback(this,m_userdata); +} + +static AvahiWatch* q_watch_new(const AvahiPoll *, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, + void *userdata) +{ + return new AvahiWatch(fd, event, callback, userdata); +} + +static void q_watch_update(AvahiWatch *w, AvahiWatchEvent events) +{ + w->setWatchedEvents(events); +} + +static AvahiWatchEvent q_watch_get_events(AvahiWatch *w) +{ + return w->getEvents(); +} + +static void q_watch_free(AvahiWatch *w) +{ + delete w; +} + +static AvahiTimeout* q_timeout_new(const AvahiPoll *, const struct timeval *tv, AvahiTimeoutCallback callback, + void *userdata) +{ + return new AvahiTimeout(tv, callback, userdata); +} + +static void q_timeout_update(AvahiTimeout *t, const struct timeval *tv) +{ + t->update(tv); +} + +static void q_timeout_free(AvahiTimeout *t) +{ + delete t; +} + +const AvahiPoll* avahi_qt_poll_get(void) +{ + static const AvahiPoll qt_poll = { + NULL, + q_watch_new, + q_watch_update, + q_watch_get_events, + q_watch_free, + q_timeout_new, + q_timeout_update, + q_timeout_free + }; + + return &qt_poll; +} diff --git a/3rdparty/QtZeroConf/avahi-qt/qt-watch.h b/3rdparty/QtZeroConf/avahi-qt/qt-watch.h new file mode 100644 index 000000000..cd7130dd6 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-qt/qt-watch.h @@ -0,0 +1,38 @@ +#ifndef QAVAHI_H +#define QAVAHI_H + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file qt-watch.h Qt main loop adapter */ + +#include + +AVAHI_C_DECL_BEGIN + +/** Setup abstract poll structure for integration with Qt main loop */ +const AvahiPoll* avahi_qt_poll_get(void) +#ifdef HAVE_VISIBILITY_HIDDEN +__attribute__ ((visibility("default"))) +#endif +; + +AVAHI_C_DECL_END + +#endif diff --git a/3rdparty/QtZeroConf/avahi-qt/qt-watch_p.h b/3rdparty/QtZeroConf/avahi-qt/qt-watch_p.h new file mode 100644 index 000000000..8988422d8 --- /dev/null +++ b/3rdparty/QtZeroConf/avahi-qt/qt-watch_p.h @@ -0,0 +1,71 @@ +#ifndef QAVAHIP_H +#define QAVAHIP_H + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file qt-watch_p.h Qt main loop adapter */ + +#include +#include +#include + +class AvahiWatch : public QObject +{ + Q_OBJECT +public: + AvahiWatch(int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void* userdata); + ~AvahiWatch() {} + AvahiWatchEvent getEvents() const { return m_incallback ? m_lastEvent : (AvahiWatchEvent)0; } + void setWatchedEvents(AvahiWatchEvent event); + +private slots: + void gotIn(); + void gotOut(); + +private: + QSocketNotifier* m_in; + QSocketNotifier* m_out; + //FIXME: ERR and HUP? + AvahiWatchCallback m_callback; + AvahiWatchEvent m_lastEvent; + int m_fd; + void* m_userdata; + bool m_incallback; +}; + +class AvahiTimeout : public QObject +{ + Q_OBJECT + +public: + AvahiTimeout(const struct timeval* tv, AvahiTimeoutCallback callback, void* userdata); + ~AvahiTimeout() {} + void update(const struct timeval* tv); + +private slots: + void timeout(); + +private: + QTimer m_timer; + AvahiTimeoutCallback m_callback; + void* m_userdata; +}; + +#endif diff --git a/3rdparty/QtZeroConf/avahiclient.cpp b/3rdparty/QtZeroConf/avahiclient.cpp new file mode 100644 index 000000000..1d5743d9e --- /dev/null +++ b/3rdparty/QtZeroConf/avahiclient.cpp @@ -0,0 +1,339 @@ +/************************************************************************************************** +--------------------------------------------------------------------------------------------------- + Copyright (C) 2015 Jonathan Bagg + This file is part of QtZeroConf. + + QtZeroConf is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QtZeroConf is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with QtZeroConf. If not, see . +--------------------------------------------------------------------------------------------------- + Project name : QtZeroConf + File name : avahiclient.cpp + Created : 20 July 2015 + Author(s) : Jonathan Bagg +--------------------------------------------------------------------------------------------------- + Avahi-client wrapper for use in Desktop Linux systems +--------------------------------------------------------------------------------------------------- +**************************************************************************************************/ +//#include // +#include "avahi-qt/qt-watch.h" // fixme don't depend on avahi-qt until it supports Qt5 +#include +#include +#include +#include +#include "qzeroconf.h" + +class QZeroConfPrivate +{ +public: + QZeroConfPrivate(QZeroConf *parent) + { + qint32 error; + + txt = NULL; + pub = parent; + group = NULL; + browser = NULL; + poll = avahi_qt_poll_get(); + if (!poll) { + return; + } + client = avahi_client_new(poll, AVAHI_CLIENT_IGNORE_USER_CONFIG, NULL, this, &error); + if (!client) { + return; + } + } + + static void groupCallback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) + { + QZeroConfPrivate *ref = static_cast(userdata); + switch (state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED: + emit ref->pub->servicePublished(); + break; + case AVAHI_ENTRY_GROUP_COLLISION: + avahi_entry_group_free(g); + ref->group = NULL; + emit ref->pub->error(QZeroConf::serviceNameCollision); + break; + case AVAHI_ENTRY_GROUP_FAILURE: + avahi_entry_group_free(g); + ref->group = NULL; + emit ref->pub->error(QZeroConf::serviceRegistrationFailed); + break; + case AVAHI_ENTRY_GROUP_UNCOMMITED: break; + case AVAHI_ENTRY_GROUP_REGISTERING: break; + } + } + + static void browseCallback(AvahiServiceBrowser *, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + void* userdata) + { + QString key = name + QString::number(interface); + QZeroConfPrivate *ref = static_cast(userdata); + + QZeroConfService zcs; + + switch (event) { + case AVAHI_BROWSER_FAILURE: + ref->broswerCleanUp(); + emit ref->pub->error(QZeroConf::browserFailed); + break; + case AVAHI_BROWSER_NEW: + if (!ref->resolvers.contains(key)) + ref->resolvers.insert(key, avahi_service_resolver_new(ref->client, interface, protocol, name, type, domain, ref->aProtocol, AVAHI_LOOKUP_USE_MULTICAST, resolveCallback, ref)); + break; + case AVAHI_BROWSER_REMOVE: + if (!ref->resolvers.contains(key)) + return; + avahi_service_resolver_free(ref->resolvers[key]); + ref->resolvers.remove(key); + + if (!ref->pub->services.contains(key)) + return; + + zcs = ref->pub->services[key]; + ref->pub->services.remove(key); + emit ref->pub->serviceRemoved(zcs); + break; + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + break; + } + } + + static void resolveCallback( + AVAHI_GCC_UNUSED AvahiServiceResolver *r, + AVAHI_GCC_UNUSED AvahiIfIndex interface, + AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags, + AVAHI_GCC_UNUSED void* userdata) + { + bool newRecord = 0; + QZeroConfService zcs; + QZeroConfPrivate *ref = static_cast(userdata); + + QString key = name + QString::number(interface); + if (event == AVAHI_RESOLVER_FOUND) { + if (ref->pub->services.contains(key)){ + zcs = ref->pub->services[key]; + while (txt) // get txt records + { + QByteArray avahiText((const char *)txt->text, txt->size); + const ssize_t pos = avahiText.indexOf('='); + if (pos < 0) + zcs->m_txt[avahiText] = ""; + else + zcs->m_txt[avahiText.left(pos)] = avahiText.mid(pos + 1, -1); + txt = txt->next; + } + } + else { + zcs = QZeroConfService(new QZeroConfServiceData); + newRecord = 1; + zcs->m_name = name; + zcs->m_type = type; + zcs->m_domain = domain; + zcs->m_host = host_name; + zcs->m_interfaceIndex = interface; + zcs->m_port = port; + while (txt) // get txt records + { + QByteArray avahiText((const char *)txt->text, txt->size); + const ssize_t pos = avahiText.indexOf('='); + if (pos < 0) + zcs->m_txt[avahiText] = ""; + else + zcs->m_txt[avahiText.left(pos)] = avahiText.mid(pos + 1, -1); + txt = txt->next; + } + ref->pub->services.insert(key, zcs); + } + + char a[AVAHI_ADDRESS_STR_MAX]; + avahi_address_snprint(a, sizeof(a), address); + QHostAddress addr(a); + zcs->setIp(addr); + + if (newRecord) + emit ref->pub->serviceAdded(zcs); + else + emit ref->pub->serviceUpdated(zcs); + } + else if (ref->pub->services.contains(key)) { // delete service if exists and unable to resolve + zcs = ref->pub->services[key]; + ref->pub->services.remove(key); + emit ref->pub->serviceRemoved(zcs); + // don't delete the resolver here...we need to keep it around so Avahi will keep updating....might be able to resolve the service in the future + } + } + + void broswerCleanUp(void) + { + if (!browser) + return; + avahi_service_browser_free(browser); + browser = NULL; + + QMap::iterator i; + for (i = pub->services.begin(); i != pub->services.end(); i++) { + emit pub->serviceRemoved(i.value()); + } + pub->services.clear(); + + QMap::iterator r; + for (r = resolvers.begin(); r != resolvers.end(); r++) + avahi_service_resolver_free(*r); + resolvers.clear(); + } + + QZeroConf *pub; + const AvahiPoll *poll; + AvahiClient *client; + AvahiEntryGroup *group; + AvahiServiceBrowser *browser; + AvahiProtocol aProtocol; + QMap resolvers; + AvahiStringList *txt; +}; + + +QZeroConf::QZeroConf(QObject *parent) : QObject (parent) +{ + pri = new QZeroConfPrivate(this); + qRegisterMetaType("QZeroConfService"); +} + +QZeroConf::~QZeroConf() +{ + avahi_string_list_free(pri->txt); + pri->broswerCleanUp(); + if (pri->client) + avahi_client_free(pri->client); + delete pri; +} + +void QZeroConf::startServicePublish(const char *name, const char *type, const char *domain, quint16 port, quint32 interface) +{ + if (!pri->client || pri->group) { // check client is ok (avahi daemon is running) and group is not already configured + emit error(QZeroConf::serviceRegistrationFailed); + return; + } + if (interface <= 0) { + interface = AVAHI_IF_UNSPEC; + } + + pri->group = avahi_entry_group_new(pri->client, QZeroConfPrivate::groupCallback, pri); + + int ret = avahi_entry_group_add_service_strlst(pri->group, interface, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UPDATE, name, type, domain, NULL, port, pri->txt); + if (ret < 0) { + avahi_entry_group_free(pri->group); + pri->group = NULL; + emit error(QZeroConf::serviceRegistrationFailed); + return; + } + + ret = avahi_entry_group_commit(pri->group); + if (ret < 0) { + pri->group = NULL; + avahi_entry_group_free(pri->group); + emit error(QZeroConf::serviceRegistrationFailed); + } + + if (!pri->group) + emit error(QZeroConf::serviceRegistrationFailed); +} + +void QZeroConf::stopServicePublish(void) +{ + if (pri->group) { + avahi_entry_group_free(pri->group); + pri->group = NULL; + } +} + +bool QZeroConf::publishExists(void) +{ + if (pri->group) + return true; + else + return false; +} + +// http://www.zeroconf.org/rendezvous/txtrecords.html + +void QZeroConf::addServiceTxtRecord(QString nameOnly) +{ + pri->txt = avahi_string_list_add(pri->txt, nameOnly.toUtf8()); +} + +void QZeroConf::addServiceTxtRecord(QString name, QString value) +{ + name.append("="); + name.append(value); + addServiceTxtRecord(name); +} + +void QZeroConf::clearServiceTxtRecords() +{ + avahi_string_list_free(pri->txt); + pri->txt = NULL; +} + +void QZeroConf::startBrowser(QString type, QAbstractSocket::NetworkLayerProtocol protocol) +{ + if (!pri->client || pri->browser) { // check client is ok (avahi daemon is running) and browser is not already started + emit error(QZeroConf::browserFailed); + return; + } + + switch (protocol) { + case QAbstractSocket::IPv4Protocol: pri->aProtocol = AVAHI_PROTO_INET; break; + case QAbstractSocket::IPv6Protocol: pri->aProtocol = AVAHI_PROTO_INET6; break; + default: + qDebug("QZeroConf::startBrowser() - unsupported protocol, using IPv4"); + pri->aProtocol = AVAHI_PROTO_INET; + break; + }; + + pri->browser = avahi_service_browser_new(pri->client, AVAHI_IF_UNSPEC, pri->aProtocol, type.toUtf8(), NULL, AVAHI_LOOKUP_USE_MULTICAST, QZeroConfPrivate::browseCallback, pri); + if (!pri->browser) + emit error(QZeroConf::browserFailed); +} + +void QZeroConf::stopBrowser(void) +{ + pri->broswerCleanUp(); +} + +bool QZeroConf::browserExists(void) +{ + if (pri->browser) + return true; + else + return false; +} diff --git a/3rdparty/QtZeroConf/avahicore.cpp b/3rdparty/QtZeroConf/avahicore.cpp new file mode 100644 index 000000000..8fe08c24c --- /dev/null +++ b/3rdparty/QtZeroConf/avahicore.cpp @@ -0,0 +1,380 @@ +/************************************************************************************************** +--------------------------------------------------------------------------------------------------- + Copyright (C) 2015 Jonathan Bagg + This file is part of QtZeroConf. + + QtZeroConf is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QtZeroConf is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with QtZeroConf. If not, see . +--------------------------------------------------------------------------------------------------- + Project name : QtZeroConf + File name : avahicore.cpp + Created : 9 September 2015 + Author(s) : Jonathan Bagg +--------------------------------------------------------------------------------------------------- + Avahi-core wrapper for use in embedded Linux systems (Android) +--------------------------------------------------------------------------------------------------- +**************************************************************************************************/ +//#include // +#include "avahi-qt/qt-watch.h" // fixme don't depend on avahi-qt until it supports Qt5 +#include +#include +#include +#include +#include +#include "qzeroconf.h" + +class QZeroConfPrivate +{ +public: + QZeroConfPrivate(QZeroConf *parent) + { + qint32 error; + + pub = parent; + group = NULL; + browser = NULL; + txt = NULL; + ready = 0; + registerWaiting = 0; + + poll = avahi_qt_poll_get(); + if (!poll) { + return; + } + + avahi_server_config_init(&config); + config.publish_workstation = 0; + + if (!referenceCount) { + server = avahi_server_new(poll, &config, serverCallback, this, &error); + } + referenceCount++; + if (!server) { + return; + } + } + + static void serverCallback(AvahiServer *, AvahiServerState state, AVAHI_GCC_UNUSED void * userdata) + { + QZeroConfPrivate *ref = static_cast(userdata); + switch (state) { + case AVAHI_SERVER_RUNNING: + ref->ready = 1; + if (ref->registerWaiting) { + ref->registerWaiting = 0; + ref->registerService(ref->name.toUtf8(), ref->type.toUtf8(), ref->domain.toUtf8(), ref->port, 0); + } + break; + case AVAHI_SERVER_COLLISION: + break; + case AVAHI_SERVER_REGISTERING: break; + case AVAHI_SERVER_FAILURE: + break; + case AVAHI_SERVER_INVALID: break; + } + } + + static void groupCallback(AvahiServer *, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) + { + QZeroConfPrivate *ref = static_cast(userdata); + switch (state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED: + emit ref->pub->servicePublished(); + break; + case AVAHI_ENTRY_GROUP_COLLISION: + avahi_s_entry_group_free(g); + ref->group = NULL; + emit ref->pub->error(QZeroConf::serviceNameCollision); + break; + case AVAHI_ENTRY_GROUP_FAILURE : + avahi_s_entry_group_free(g); + ref->group = NULL; + emit ref->pub->error(QZeroConf::serviceRegistrationFailed); + break; + case AVAHI_ENTRY_GROUP_UNCOMMITED: break; + case AVAHI_ENTRY_GROUP_REGISTERING: break; + } + } + + static void browseCallback( + AvahiSServiceBrowser *, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + void* userdata) + { + QString key = name + QString::number(interface); + QZeroConfPrivate *ref = static_cast(userdata); + QZeroConfService zcs; + + switch (event) { + case AVAHI_BROWSER_FAILURE: + ref->broswerCleanUp(); + emit ref->pub->error(QZeroConf::browserFailed); + break; + case AVAHI_BROWSER_NEW: + if (!ref->resolvers.contains(key)) + ref->resolvers.insert(key, avahi_s_service_resolver_new(ref->server, interface, protocol, name, type, domain, ref->aProtocol, AVAHI_LOOKUP_USE_MULTICAST, resolveCallback, ref)); + break; + case AVAHI_BROWSER_REMOVE: + if (!ref->resolvers.contains(key)) + return; + avahi_s_service_resolver_free(ref->resolvers[key]); + ref->resolvers.remove(key); + + if (!ref->pub->services.contains(key)) + return; + zcs = ref->pub->services[key]; + ref->pub->services.remove(key); + emit ref->pub->serviceRemoved(zcs); + break; + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + break; + } + } + + static void resolveCallback( + AVAHI_GCC_UNUSED AvahiSServiceResolver *r, + AVAHI_GCC_UNUSED AvahiIfIndex interface, + AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags, + AVAHI_GCC_UNUSED void* userdata) + { + bool newRecord = 0; + QZeroConfService zcs; + QZeroConfPrivate *ref = static_cast(userdata); + + QString key = name + QString::number(interface); + if (event == AVAHI_RESOLVER_FOUND) { + if (ref->pub->services.contains(key)) + zcs = ref->pub->services[key]; + else { + zcs = QZeroConfService(new QZeroConfServiceData); + newRecord = 1; + zcs->m_name = name; + zcs->m_type = type; + zcs->m_domain = domain; + zcs->m_host = host_name; + zcs->m_interfaceIndex = interface; + zcs->m_port = port; + while (txt) // get txt records + { + QByteArray avahiText((const char *)txt->text, txt->size); + const int pos = avahiText.indexOf('='); + if (pos < 0) + zcs->m_txt[avahiText] = ""; + else + zcs->m_txt[avahiText.left(pos)] = avahiText.mid(pos + 1, -1); + txt = txt->next; + } + ref->pub->services.insert(key, zcs); + } + + char a[AVAHI_ADDRESS_STR_MAX]; + avahi_address_snprint(a, sizeof(a), address); + QHostAddress addr(a); + zcs->setIp(addr); + + if (newRecord) + emit ref->pub->serviceAdded(zcs); + else + emit ref->pub->serviceUpdated(zcs); + } + else if (ref->pub->services.contains(key)) { // delete service if exists and unable to resolve + zcs = ref->pub->services[key]; + ref->pub->services.remove(key); + emit ref->pub->serviceRemoved(zcs); + // don't delete the resolver here...we need to keep it around so Avahi will keep updating....might be able to resolve the service in the future + } + } + + void broswerCleanUp(void) + { + if (!browser) + return; + avahi_s_service_browser_free(browser); + browser = NULL; + + QMap::iterator i; + for (i = pub->services.begin(); i != pub->services.end(); i++) { + emit pub->serviceRemoved(i.value()); + } + pub->services.clear(); + + QMap::iterator r; + for (r = resolvers.begin(); r != resolvers.end(); r++) + avahi_s_service_resolver_free(*r); + resolvers.clear(); + } + + void registerService(const char *name, const char *type, const char *domain, quint16 port, quint32 interface) + { + qint32 ret; + group = avahi_s_entry_group_new(server, QZeroConfPrivate::groupCallback, this); + if (!group) { + pub->emit error(QZeroConf::serviceRegistrationFailed); + return; + } + + if (interface <= 0) { + interface = AVAHI_IF_UNSPEC; + } + + ret = avahi_server_add_service_strlst(server, group, interface, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UPDATE, name, type, domain, NULL, port, txt); + if (ret < 0) { + avahi_s_entry_group_free(group); + group = NULL; + pub->emit error(QZeroConf::serviceRegistrationFailed); + return; + } + + ret = avahi_s_entry_group_commit(group); + if (ret < 0) { + avahi_s_entry_group_free(group); + group = NULL; + pub->emit error(QZeroConf::serviceRegistrationFailed); + } + } + + QZeroConf *pub; + const AvahiPoll *poll; + static AvahiServer *server; + static quint32 referenceCount; + AvahiServerConfig config; + AvahiSEntryGroup *group; + AvahiSServiceBrowser *browser; + AvahiProtocol aProtocol; + QMap resolvers; + AvahiStringList *txt; + bool ready, registerWaiting; + QString name, type, domain; + qint32 port; +}; + +AvahiServer* QZeroConfPrivate::server = nullptr; +quint32 QZeroConfPrivate::referenceCount = 0; + +QZeroConf::QZeroConf(QObject *parent) : QObject (parent) +{ + pri = new QZeroConfPrivate(this); + qRegisterMetaType("QZeroConfService"); +} + +QZeroConf::~QZeroConf() +{ + avahi_string_list_free(pri->txt); + pri->broswerCleanUp(); + avahi_server_config_free(&pri->config); + pri->referenceCount--; + if (!pri->referenceCount) + avahi_server_free(pri->server); + delete pri; +} + +void QZeroConf::startServicePublish(const char *name, const char *type, const char *domain, quint16 port, quint32 interface) +{ + if (pri->group) { + emit error(QZeroConf::serviceRegistrationFailed); + return; + } + if (pri->ready) + pri->registerService(name, type, domain, port, interface); + else { + pri->registerWaiting = 1; + pri->name = name; + pri->type = type; + pri->domain = domain; + pri->port = port; + } +} + +void QZeroConf::stopServicePublish(void) +{ + if (pri->group) { + avahi_s_entry_group_free(pri->group); + pri->group = NULL; + } +} + +bool QZeroConf::publishExists(void) +{ + if (pri->group) + return true; + else + return false; +} + +// http://www.zeroconf.org/rendezvous/txtrecords.html + +void QZeroConf::addServiceTxtRecord(QString nameOnly) +{ + pri->txt = avahi_string_list_add(pri->txt, nameOnly.toUtf8()); +} + +void QZeroConf::addServiceTxtRecord(QString name, QString value) +{ + name.append("="); + name.append(value); + addServiceTxtRecord(name); +} + +void QZeroConf::clearServiceTxtRecords() +{ + avahi_string_list_free(pri->txt); + pri->txt = NULL; +} + +void QZeroConf::startBrowser(QString type, QAbstractSocket::NetworkLayerProtocol protocol) +{ + if (pri->browser) + emit error(QZeroConf::browserFailed); + + switch (protocol) { + case QAbstractSocket::IPv4Protocol: pri->aProtocol = AVAHI_PROTO_INET; break; + case QAbstractSocket::IPv6Protocol: pri->aProtocol = AVAHI_PROTO_INET6; break; + default: + qDebug("QZeroConf::startBrowser() - unsupported protocol, using IPv4"); + pri->aProtocol = AVAHI_PROTO_INET; + break; + }; + + pri->browser = avahi_s_service_browser_new(pri->server, AVAHI_IF_UNSPEC, pri->aProtocol, type.toUtf8(), NULL, AVAHI_LOOKUP_USE_MULTICAST, QZeroConfPrivate::browseCallback, pri); + if (!pri->browser) + emit error(QZeroConf::browserFailed); +} + +void QZeroConf::stopBrowser(void) +{ + pri->broswerCleanUp(); +} + +bool QZeroConf::browserExists(void) +{ + if (pri->browser) + return true; + else + return false; +} diff --git a/3rdparty/QtZeroConf/bonjour-sdk/CommonServices.h b/3rdparty/QtZeroConf/bonjour-sdk/CommonServices.h new file mode 100644 index 000000000..c6887982b --- /dev/null +++ b/3rdparty/QtZeroConf/bonjour-sdk/CommonServices.h @@ -0,0 +1,1551 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @header CommonServices + + Common Services for Mac OS X, Linux, Palm, VxWorks, Windows, and Windows CE. + */ + +#ifndef __COMMON_SERVICES__ +#define __COMMON_SERVICES__ + +#ifdef __cplusplus +extern "C" { +#endif + +#if 0 +#pragma mark == Target == +#endif + +//=========================================================================================================================== +// Target +//=========================================================================================================================== + +// Macintosh + +#if ( !defined( TARGET_OS_MAC ) ) + #if ( ( macintosh || __MACH__ ) && !KERNEL ) +// ConditionalMacros.h in CoreServices will define this TARGET_* flag. + #else + #define TARGET_OS_MAC 0 + #endif +#endif + +#if ( !defined( TARGET_API_MAC_OSX_KERNEL ) ) + #if ( __MACH__ && KERNEL ) + #define TARGET_API_MAC_OSX_KERNEL 1 + #else + #define TARGET_API_MAC_OSX_KERNEL 0 + #endif +#endif + +// FreeBSD + +#if ( !defined( TARGET_OS_FREEBSD ) ) + #if ( defined( __FreeBSD__ ) ) + #define TARGET_OS_FREEBSD 1 + #else + #define TARGET_OS_FREEBSD 0 + #endif +#endif + +// Linux + +#if ( !defined( TARGET_OS_LINUX ) ) + #if ( defined( __linux__ ) ) + #define TARGET_OS_LINUX 1 + #else + #define TARGET_OS_LINUX 0 + #endif +#endif + +// Solaris + +#if ( !defined( TARGET_OS_SOLARIS ) ) + #if ( defined(solaris) || (defined(__SVR4) && defined(sun)) ) + #define TARGET_OS_SOLARIS 1 + #else + #define TARGET_OS_SOLARIS 0 + #endif +#endif + +// Palm + +#if ( !defined( TARGET_OS_PALM ) ) + #if ( defined( __PALMOS_TRAPS__ ) || defined( __PALMOS_ARMLET__ ) ) + #define TARGET_OS_PALM 1 + #else + #define TARGET_OS_PALM 0 + #endif +#endif + +// VxWorks + +#if ( !defined( TARGET_OS_VXWORKS ) ) + +// No predefined macro for VxWorks so just assume VxWorks if nothing else is set. + + #if ( !macintosh && !__MACH__ && !defined( __FreeBSD__ ) && !defined( __linux__ ) && !defined ( __SVR4 ) && !defined ( __sun ) && !defined( __PALMOS_TRAPS__ ) && !defined( __PALMOS_ARMLET__ ) && !defined( _WIN32 ) ) + #define TARGET_OS_VXWORKS 1 + #else + #define TARGET_OS_VXWORKS 0 + #endif +#endif + +// Windows + +#if ( !defined( TARGET_OS_WIN32 ) ) + #if ( macintosh || __MACH__ ) +// ConditionalMacros.h in CoreServices will define this TARGET_* flag. + #else + #if ( defined( _WIN32 ) ) + #define TARGET_OS_WIN32 1 + #else + #define TARGET_OS_WIN32 0 + #endif + #endif +#endif + +// Windows CE + +#if ( !defined( TARGET_OS_WINDOWS_CE ) ) + #if ( defined( _WIN32_WCE ) ) + #define TARGET_OS_WINDOWS_CE 1 + #else + #define TARGET_OS_WINDOWS_CE 0 + #endif +#endif + +#if 0 +#pragma mark == Includes == +#endif + +//=========================================================================================================================== +// Includes +//=========================================================================================================================== + +#if ( !KERNEL ) + #if defined(WIN32) && !defined(_WSPIAPI_COUNTOF) + #define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) + #endif + #include +#endif + +#if ( ( macintosh || __MACH__ ) && !KERNEL ) + + #if ( defined( __MWERKS__ ) ) + #if ( __option( c9x ) ) + #include + #endif + #else + #include + #endif + + #include + + #if ( __MACH__ ) + +// Mac OS X + + #include + #include + #include + #include + #include + #include + #include + #include + + #else + +// Classic Mac OS + + #include + #include + + #endif + +#elif ( KERNEL ) + +// Mac OS X Kernel + + #include + + #include + #include + +#elif ( TARGET_OS_FREEBSD ) + +// FreeBSD + #include + #include + #include + #include + #include + +#elif ( TARGET_OS_LINUX ) + +// Linux + + #include + #include + +#elif ( TARGET_OS_SOLARIS ) + +// Solaris + + #include + + #include + #include + + #if ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) ) + #define TARGET_RT_LITTLE_ENDIAN 1 + #endif + #if ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) ) + #define TARGET_RT_BIG_ENDIAN 1 + #endif + +#elif ( TARGET_OS_PALM ) + +// Palm (no special includes yet). + +#elif ( TARGET_OS_VXWORKS ) + +// VxWorks + + #include "vxWorks.h" + +#elif ( TARGET_OS_WIN32 ) + +// Windows + + #if ( !defined( WIN32_WINDOWS ) ) + #define WIN32_WINDOWS 0x0401 + #endif + + #if ( !defined( _WIN32_WINDOWS ) ) + #define _WIN32_WINDOWS 0x0401 + #endif + + #if ( !defined( WIN32_LEAN_AND_MEAN ) ) + #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces. + #endif + + #if ( defined( __MWERKS__ ) ) + + #if ( __option( c9x ) ) + #include + #endif + + #include + + #elif ( defined( _MSC_VER ) ) + + #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros. + #pragma warning( disable:4706 ) // Disable "assignment within conditional expression" for Microsoft headers. + + #endif + + #include + #include + #include + + #if ( defined( _MSC_VER ) ) + #pragma warning( default:4706 ) + #endif + +#else + #error unknown OS - update this file to support your OS +#endif + +#if ( !defined( TARGET_BUILD_MAIN ) ) + #if ( !TARGET_OS_VXWORKS ) + #define TARGET_BUILD_MAIN 1 + #endif +#endif + +#if ( __GNUC__ || !TARGET_OS_VXWORKS ) + #define TARGET_LANGUAGE_C_LIKE 1 +#else + #define TARGET_LANGUAGE_C_LIKE 0 +#endif + +#if 0 +#pragma mark == CPU == +#endif + +//=========================================================================================================================== +// CPU +//=========================================================================================================================== + +// PowerPC + +#if ( !defined( TARGET_CPU_PPC ) ) + #if ( defined( __ppc__ ) || defined( __PPC__ ) || defined( powerpc ) || defined( ppc ) || defined( _M_MPPC ) ) + #define TARGET_CPU_PPC 1 + #else + #define TARGET_CPU_PPC 0 + #endif +#endif + +// x86 + +#if ( !defined( TARGET_CPU_X86 ) ) + #if ( __INTEL__ || defined( __i386__ ) || defined( i386 ) || defined( intel ) || defined( _M_IX86 ) ) + #define TARGET_CPU_X86 1 + #else + #define TARGET_CPU_X86 0 + #endif +#endif + +// MIPS + +#if ( !defined( TARGET_CPU_MIPS ) ) + #if ( __MIPS__ || defined( MIPS32 ) || defined( R3000 ) || defined( R4000 ) || defined( R4650 ) || defined( _M_MRX000 ) ) + #define TARGET_CPU_MIPS 1 + #else + #define TARGET_CPU_MIPS 0 + #endif +#endif + +#if ( !defined( TARGET_CPU_PPC ) && !defined( TARGET_CPU_X86 ) && !defined( TARGET_CPU_MIPS ) ) + #error unknown CPU - update this file to support your CPU +#endif + +#if 0 +#pragma mark == Byte Order == +#endif + +//=========================================================================================================================== +// Byte Order +//=========================================================================================================================== + +// TARGET_RT_LITTLE_ENDIAN + +#if ( !defined( TARGET_RT_LITTLE_ENDIAN ) ) + #if ( MIPSEL || IL_LITTLE_ENDIAN || defined( __LITTLE_ENDIAN__ ) || \ + ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) ) || \ + ( defined( _BYTE_ORDER ) && defined( _LITTLE_ENDIAN ) && ( _BYTE_ORDER == _LITTLE_ENDIAN ) ) || \ + ( defined( __BYTE_ORDER ) && defined( __LITTLE_ENDIAN ) && ( __BYTE_ORDER == __LITTLE_ENDIAN ) ) || \ + TARGET_CPU_X86 || ( defined( TARGET_RT_BIG_ENDIAN ) && !TARGET_RT_BIG_ENDIAN ) ) + #define TARGET_RT_LITTLE_ENDIAN 1 + #else + #define TARGET_RT_LITTLE_ENDIAN 0 + #endif +#endif + +// TARGET_RT_BIG_ENDIAN + +#if ( !defined( TARGET_RT_BIG_ENDIAN ) ) + #if ( MIPSEB || IL_BIG_ENDIAN || defined( __BIG_ENDIAN__ ) || \ + ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) ) || \ + ( defined( _BYTE_ORDER ) && defined( _BIG_ENDIAN ) && ( _BYTE_ORDER == _BIG_ENDIAN ) ) || \ + ( defined( __BYTE_ORDER ) && defined( __BIG_ENDIAN ) && ( __BYTE_ORDER == __BIG_ENDIAN ) ) || \ + ( defined( TARGET_RT_LITTLE_ENDIAN ) && !TARGET_RT_LITTLE_ENDIAN ) ) + #define TARGET_RT_BIG_ENDIAN 1 + #else + #define TARGET_RT_BIG_ENDIAN 0 + #endif +#endif + +#if ( defined( TARGET_RT_LITTLE_ENDIAN ) && !defined( TARGET_RT_BIG_ENDIAN ) ) + #if ( TARGET_RT_LITTLE_ENDIAN ) + #define TARGET_RT_BIG_ENDIAN 0 + #else + #define TARGET_RT_BIG_ENDIAN 1 + #endif +#endif + +#if ( defined( TARGET_RT_BIG_ENDIAN ) && !defined( TARGET_RT_LITTLE_ENDIAN ) ) + #if ( TARGET_RT_BIG_ENDIAN ) + #define TARGET_RT_LITTLE_ENDIAN 0 + #else + #define TARGET_RT_LITTLE_ENDIAN 1 + #endif +#endif + +#if ( !defined( TARGET_RT_LITTLE_ENDIAN ) || !defined( TARGET_RT_BIG_ENDIAN ) ) + #error unknown byte order - update this file to support your byte order +#endif + +// TARGET_RT_BYTE_ORDER + +#if ( !defined( TARGET_RT_BYTE_ORDER_BIG_ENDIAN ) ) + #define TARGET_RT_BYTE_ORDER_BIG_ENDIAN 1234 +#endif + +#if ( !defined( TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN ) ) + #define TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN 4321 +#endif + +#if ( !defined( TARGET_RT_BYTE_ORDER ) ) + #if ( TARGET_RT_LITTLE_ENDIAN ) + #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN + #else + #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_BIG_ENDIAN + #endif +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#if ( !TARGET_OS_MAC ) + #define CR '\r' +#endif + +#define LF '\n' +#define CRSTR "\r" +#define LFSTR "\n" +#define CRLF "\r\n" +#define CRCR "\r\r" + +#if 0 +#pragma mark == Compatibility == +#endif + +//=========================================================================================================================== +// Compatibility +//=========================================================================================================================== + +// Macros to allow the same code to work on Windows and other sockets API-compatible platforms. + +#if ( TARGET_OS_WIN32 ) + #define close_compat( X ) closesocket( X ) + #define errno_compat() (int) GetLastError() + #define set_errno_compat( X ) SetLastError( X ) + #define EWOULDBLOCK_compat WSAEWOULDBLOCK + #define ETIMEDOUT_compat WSAETIMEDOUT + #define ENOTCONN_compat WSAENOTCONN + #define IsValidSocket( X ) ( ( X ) != INVALID_SOCKET ) + #define kInvalidSocketRef INVALID_SOCKET + #if ( TARGET_LANGUAGE_C_LIKE ) +typedef SOCKET SocketRef; + #endif +#else + #define close_compat( X ) close( X ) + #define errno_compat() errno + #define set_errno_compat( X ) do { errno = ( X ); } while( 0 ) + #define EWOULDBLOCK_compat EWOULDBLOCK + #define ETIMEDOUT_compat ETIMEDOUT + #define ENOTCONN_compat ENOTCONN + #define IsValidSocket( X ) ( ( X ) >= 0 ) + #define kInvalidSocketRef -1 + #if ( TARGET_LANGUAGE_C_LIKE ) +typedef int SocketRef; + #endif +#endif + +// socklen_t is not defined on the following platforms so emulate it if not defined: +// +// - Pre-Panther Mac OS X. Panther defines SO_NOADDRERR so trigger off that. +// - Windows SDK prior to 2003. 2003+ SDK's define EAI_AGAIN so trigger off that. +// - VxWorks + +#if ( TARGET_LANGUAGE_C_LIKE ) + #if ( ( TARGET_OS_MAC && !defined( SO_NOADDRERR ) ) || ( TARGET_OS_WIN32 && !defined( EAI_AGAIN ) ) || TARGET_OS_VXWORKS ) +typedef int socklen_t; + #endif +#endif + +// ssize_t is not defined on the following platforms so emulate it if not defined: +// +// - Mac OS X when not building with BSD headers +// - Windows + +#if ( TARGET_LANGUAGE_C_LIKE ) + #if ( !defined(_SSIZE_T) && ( TARGET_OS_WIN32 || !defined( _BSD_SSIZE_T_DEFINED_ ) ) && !TARGET_OS_FREEBSD && !TARGET_OS_LINUX && !TARGET_OS_VXWORKS && !TARGET_OS_MAC) +typedef int ssize_t; + #endif +#endif + +// sockaddr_storage is not supported on non-IPv6 machines so alias it to an IPv4-compatible structure. + +#if ( TARGET_LANGUAGE_C_LIKE ) + #if ( !defined( AF_INET6 ) ) + #define sockaddr_storage sockaddr_in + #define ss_family sin_family + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined SOCKADDR_IS_IP_LOOPBACK + + @abstract Determines if a sockaddr is an IPv4 or IPv6 loopback address (if IPv6 is supported). + */ + +#if ( defined( AF_INET6 ) ) + #define SOCKADDR_IS_IP_LOOPBACK( SA ) \ + ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ + ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \ + : ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET6 ) \ + ? IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) \ + : 0 +#else + #define SOCKADDR_IS_IP_LOOPBACK( SA ) \ + ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ + ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \ + : 0 +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined SOCKADDR_IS_IP_LINK_LOCAL + + @abstract Determines if a sockaddr is an IPv4 or IPv6 link-local address (if IPv6 is supported). + */ + +#if ( defined( AF_INET6 ) ) + #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \ + ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ + ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \ + ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \ + : IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) ) +#else + #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \ + ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ + ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \ + ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \ + : 0 ) +#endif + +// _beginthreadex and _endthreadex are not supported on Windows CE 2.1 or later (the C runtime issues with leaking +// resources have apparently been resolved and they seem to have just ripped out support for the API) so map it to +// CreateThread on Windows CE. + +#if ( TARGET_OS_WINDOWS_CE ) + #define _beginthreadex_compat( SECURITY_PTR, STACK_SIZE, START_ADDRESS, ARG_LIST, FLAGS, THREAD_ID_PTR ) \ + (uintptr_t) CreateThread( SECURITY_PTR, STACK_SIZE, (LPTHREAD_START_ROUTINE) START_ADDRESS, ARG_LIST, FLAGS, \ + (LPDWORD) THREAD_ID_PTR ) + + #define _endthreadex_compat( RESULT ) ExitThread( (DWORD) RESULT ) +#elif ( TARGET_OS_WIN32 ) + #define _beginthreadex_compat _beginthreadex + #define _endthreadex_compat _endthreadex +#endif + +// The C99 "inline" keyword is not supported by Microsoft compilers, but they do support __inline so map it when needed. + +#if ( defined( _MSC_VER ) ) + #define inline_compat __inline +#else + #define inline_compat inline +#endif + +// Calling conventions + +#if ( !defined( CALLBACK_COMPAT ) ) + #if ( TARGET_OS_WIN32 || TARGET_OS_WINDOWS_CE ) + #define CALLBACK_COMPAT CALLBACK + #else + #define CALLBACK_COMPAT + #endif +#endif + +#if 0 +#pragma mark == Macros == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kSizeCString + + @abstract A meta-value to pass to supported routines to indicate the size should be calculated with strlen. + */ + +#define kSizeCString ( (size_t) -1 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined sizeof_array + + @abstract Determines the number of elements in an array. + */ + +#define sizeof_array( X ) ( sizeof( X ) / sizeof( X[ 0 ] ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined sizeof_element + + @abstract Determines the size of an array element. + */ + +#define sizeof_element( X ) sizeof( X[ 0 ] ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined sizeof_string + + @abstract Determines the size of a constant C string, excluding the null terminator. + */ + +#define sizeof_string( X ) ( sizeof( ( X ) ) - 1 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined sizeof_field + + @abstract Determines the size of a field of a type. + */ + +#define sizeof_field( TYPE, FIELD ) sizeof( ( ( (TYPE *) 0 )->FIELD ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RoundUp + + @abstract Rounds X up to a multiple of Y. + */ + +#define RoundUp( X, Y ) ( ( X ) + ( ( Y ) -( ( X ) % ( Y ) ) ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function IsAligned + + @abstract Returns non-zero if X is aligned to a Y byte boundary and 0 if not. Y must be a power of 2. + */ + +#define IsAligned( X, Y ) ( ( ( X ) &( ( Y ) -1 ) ) == 0 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function IsFieldAligned + + @abstract Returns non-zero if FIELD of type TYPE is aligned to a Y byte boundary and 0 if not. Y must be a power of 2. + */ + +#define IsFieldAligned( X, TYPE, FIELD, Y ) IsAligned( ( (uintptr_t)( X ) ) + offsetof( TYPE, FIELD ), ( Y ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function AlignDown + + @abstract Aligns X down to a Y byte boundary. Y must be a power of 2. + */ + +#define AlignDown( X, Y ) ( ( X ) &~( ( Y ) -1 ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function AlignUp + + @abstract Aligns X up to a Y byte boundary. Y must be a power of 2. + */ + +#define AlignUp( X, Y ) ( ( ( X ) + ( ( Y ) -1 ) ) & ~( ( Y ) -1 ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function Min + + @abstract Returns the lesser of X and Y. + */ + +#if ( !defined( Min ) ) + #define Min( X, Y ) ( ( ( X ) < ( Y ) ) ? ( X ) : ( Y ) ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function Max + + @abstract Returns the greater of X and Y. + */ + +#if ( !defined( Max ) ) + #define Max( X, Y ) ( ( ( X ) > ( Y ) ) ? ( X ) : ( Y ) ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function InsertBits + + @abstract Inserts BITS (both 0 and 1 bits) into X, controlled by MASK and SHIFT, and returns the result. + + @discussion + + MASK is the bitmask of the bits in the final position. + SHIFT is the number of bits to shift left for 1 to reach the first bit position of MASK. + + For example, if you wanted to insert 0x3 into the leftmost 4 bits of a 32-bit value: + + InsertBits( 0, 0x3, 0xF0000000U, 28 ) == 0x30000000 + */ + +#define InsertBits( X, BITS, MASK, SHIFT ) ( ( ( X ) &~( MASK ) ) | ( ( ( BITS ) << ( SHIFT ) ) & ( MASK ) ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function ExtractBits + + @abstract Extracts bits from X, controlled by MASK and SHIFT, and returns the result. + + @discussion + + MASK is the bitmask of the bits in the final position. + SHIFT is the number of bits to shift right to right justify MASK. + + For example, if you had a 32-bit value (e.g. 0x30000000) wanted the left-most 4 bits (e.g. 3 in this example): + + ExtractBits( 0x30000000U, 0xF0000000U, 28 ) == 0x3 + */ + +#define ExtractBits( X, MASK, SHIFT ) ( ( ( X ) >> ( SHIFT ) ) & ( ( MASK ) >> ( SHIFT ) ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function Stringify + + @abstract Stringify's an expression. + + @discussion + + Stringify macros to process raw text passed via -D options to C string constants. The double-wrapping is necessary + because the C preprocessor doesn't perform its normal argument expansion pre-scan with stringified macros so the + -D macro needs to be expanded once via the wrapper macro then stringified so the raw text is stringified. Otherwise, + the replacement value would be used instead of the symbolic name (only for preprocessor symbols like #defines). + + For example: + + #define kMyConstant 1 + + printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant" + printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "1" + + Non-preprocessor symbols do not have this issue. For example: + + enum + { + kMyConstant = 1 + }; + + printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant" + printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "kMyConstant" + + See for more info on C preprocessor pre-scanning. + */ + +#define Stringify( X ) # X +#define StringifyExpansion( X ) Stringify( X ) + +#if 0 +#pragma mark == Types == +#endif + +#if ( TARGET_LANGUAGE_C_LIKE ) +//=========================================================================================================================== +// Standard Types +//=========================================================================================================================== + +#if ( defined( __GNUC__ ) ) + #include +#endif + +#if ( !defined( INT8_MIN ) ) + + #define INT8_MIN SCHAR_MIN + + #if ( defined( _MSC_VER ) ) + +// C99 stdint.h not supported in VC++/VS.NET yet. + +typedef INT8 int8_t; +typedef UINT8 uint8_t; +typedef INT16 int16_t; +typedef UINT16 uint16_t; +typedef INT32 int32_t; +typedef UINT32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + + #elif ( TARGET_OS_VXWORKS && ( TORNADO_VERSION < 220 ) ) +typedef long long int64_t; +typedef unsigned long long uint64_t; + #endif + +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; + +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + + #if ( !defined( _MSC_VER ) || TARGET_OS_WINDOWS_CE ) +typedef long int intptr_t; +typedef unsigned long int uintptr_t; + #endif + +#endif + +// Macros for minimum-width integer constants + +#if ( !defined( INT8_C ) ) + #define INT8_C( value ) value +#endif + +#if ( !defined( INT16_C ) ) + #define INT16_C( value ) value +#endif + +#if ( !defined( INT32_C ) ) + #define INT32_C( value ) value ## L +#endif + +#if ( !defined( INT64_C ) ) + #if ( defined( _MSC_VER ) ) + #define INT64_C( value ) value ## i64 + #else + #define INT64_C( value ) value ## LL + #endif +#endif + +#if ( !defined( UINT8_C ) ) + #define UINT8_C( value ) value ## U +#endif + +#if ( !defined( UINT16_C ) ) + #define UINT16_C( value ) value ## U +#endif + +#if ( !defined( UINT32_C ) ) + #define UINT32_C( value ) value ## UL +#endif + +#if ( !defined( UINT64_C ) ) + #if ( defined( _MSC_VER ) ) + #define UINT64_C( value ) value ## UI64 + #else + #define UINT64_C( value ) value ## ULL + #endif +#endif + +// Limits + +#if( !defined( UINT32_MAX ) ) + #define UINT32_MAX UINT32_C( 4294967295 ) +#endif + +#if 0 +#pragma mark == bool == +#endif + +//=========================================================================================================================== +// Boolean Constants and Types +//=========================================================================================================================== + +// C++ defines bool, true, and false. Metrowerks allows this to be controlled by the "bool" option though. +// C99 defines __bool_true_false_are_defined when bool, true, and false are defined. +// MacTypes.h defines true and false (Mac builds only). +// +// Note: The Metrowerks has to be in its own block because Microsoft Visual Studio .NET does not completely +// short-circuit and gets confused by the option( bool ) portion of the conditional. + +#if ( defined( __MWERKS__ ) ) + +// Note: The following test is done on separate lines because CodeWarrior doesn't like it all on one line. + + #if ( !__bool_true_false_are_defined && ( !defined( __cplusplus ) || !__option( bool ) ) ) + #define COMMON_SERVICES_NEEDS_BOOL 1 + #else + #define COMMON_SERVICES_NEEDS_BOOL 0 + #endif + +// Workaround when building with CodeWarrior, but using the Apple stdbool.h header, which uses _Bool. + + #if ( __bool_true_false_are_defined && !defined( __cplusplus ) && !__option( c9x ) ) + #define _Bool int + #endif + +// Workaround when building with CodeWarrior for C++ with bool disabled and using the Apple stdbool.h header, +// which defines true and false to map to C++ true and false (which are not enabled). Serenity Now! + + #if ( __bool_true_false_are_defined && defined( __cplusplus ) && !__option( bool ) ) + #define true 1 + #define false 0 + #endif +#else + #if ( !defined( __cplusplus ) && !__bool_true_false_are_defined ) + #define COMMON_SERVICES_NEEDS_BOOL 1 + #else + #define COMMON_SERVICES_NEEDS_BOOL 0 + #endif +#endif + +#if ( COMMON_SERVICES_NEEDS_BOOL ) + +typedef int bool; + + #define bool bool + + #if ( !defined( __MACTYPES__ ) && !defined( true ) && !defined( false ) ) + #define true 1 + #define false 0 + #endif + + #define __bool_true_false_are_defined 1 +#endif + +// IOKit IOTypes.h typedef's bool if TYPE_BOOL is not defined so define it here to prevent redefinition by IOTypes.h. + +#if ( TARGET_API_MAC_OSX_KERNEL ) + #define TYPE_BOOL 1 +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef CStr255 + + @abstract 255 character null-terminated (C-style) string. + */ + +#if ( TARGET_LANGUAGE_C_LIKE ) +typedef char CStr255[ 256 ]; +#endif + +#endif // TARGET_LANGUAGE_C_LIKE + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined TYPE_LONGLONG_NATIVE + + @abstract Defines whether long long (or its equivalent) is natively supported or requires special libraries. + */ + +#if ( !defined( TYPE_LONGLONG_NATIVE ) ) + #if ( !TARGET_OS_VXWORKS ) + #define TYPE_LONGLONG_NATIVE 1 + #else + #define TYPE_LONGLONG_NATIVE 0 + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined long_long_compat + + @abstract Compatibility type to map to the closest thing to long long and unsigned long long. + + @discussion + + Neither long long nor unsigned long long are supported by Microsoft compilers, but they do support proprietary + "__int64" and "unsigned __int64" equivalents so map to those types if the real long long is not supported. + */ + +#if ( TARGET_LANGUAGE_C_LIKE ) + #if ( TARGET_OS_WIN32 ) +typedef __int64 long_long_compat; +typedef unsigned __int64 unsigned_long_long_compat; + #else +typedef signed long long long_long_compat; +typedef unsigned long long unsigned_long_long_compat; + #endif +#endif + +#if 0 +#pragma mark == Errors == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum OSStatus + + @abstract Status Code + + @constant kNoErr 0 No error occurred. + @constant kInProgressErr 1 Operation in progress. + @constant kUnknownErr -6700 Unknown error occurred. + @constant kOptionErr -6701 Option was not acceptable. + @constant kSelectorErr -6702 Selector passed in is invalid or unknown. + @constant kExecutionStateErr -6703 Call made in the wrong execution state (e.g. called at interrupt time). + @constant kPathErr -6704 Path is invalid, too long, or otherwise not usable. + @constant kParamErr -6705 Parameter is incorrect, missing, or not appropriate. + @constant kParamCountErr -6706 Incorrect or unsupported number of parameters. + @constant kCommandErr -6707 Command invalid or not supported. + @constant kIDErr -6708 Unknown, invalid, or inappropriate identifier. + @constant kStateErr -6709 Not in appropriate state to perform operation. + @constant kRangeErr -6710 Index is out of range or not valid. + @constant kRequestErr -6711 Request was improperly formed or not appropriate. + @constant kResponseErr -6712 Response was incorrect or out of sequence. + @constant kChecksumErr -6713 Checksum does not match the actual data. + @constant kNotHandledErr -6714 Operation was not handled (or not handled completely). + @constant kVersionErr -6715 Version is not incorrect or not compatibile. + @constant kSignatureErr -6716 Signature did not match what was expected. + @constant kFormatErr -6717 Unknown, invalid, or inappropriate file/data format. + @constant kNotInitializedErr -6718 Action request before needed services were initialized. + @constant kAlreadyInitializedErr -6719 Attempt made to initialize when already initialized. + @constant kNotInUseErr -6720 Object not in use (e.g. cannot abort if not already in use). + @constant kInUseErr -6721 Object is in use (e.g. cannot reuse active param blocks). + @constant kTimeoutErr -6722 Timeout occurred. + @constant kCanceledErr -6723 Operation canceled (successful cancel). + @constant kAlreadyCanceledErr -6724 Operation has already been canceled. + @constant kCannotCancelErr -6725 Operation could not be canceled (maybe already done or invalid). + @constant kDeletedErr -6726 Object has already been deleted. + @constant kNotFoundErr -6727 Something was not found. + @constant kNoMemoryErr -6728 Not enough memory was available to perform the operation. + @constant kNoResourcesErr -6729 Resources unavailable to perform the operation. + @constant kDuplicateErr -6730 Duplicate found or something is a duplicate. + @constant kImmutableErr -6731 Entity is not changeable. + @constant kUnsupportedDataErr -6732 Data is unknown or not supported. + @constant kIntegrityErr -6733 Data is corrupt. + @constant kIncompatibleErr -6734 Data is not compatible or it is in an incompatible format. + @constant kUnsupportedErr -6735 Feature or option is not supported. + @constant kUnexpectedErr -6736 Error occurred that was not expected. + @constant kValueErr -6737 Value is not appropriate. + @constant kNotReadableErr -6738 Could not read or reading is not allowed. + @constant kNotWritableErr -6739 Could not write or writing is not allowed. + @constant kBadReferenceErr -6740 An invalid or inappropriate reference was specified. + @constant kFlagErr -6741 An invalid, inappropriate, or unsupported flag was specified. + @constant kMalformedErr -6742 Something was not formed correctly. + @constant kSizeErr -6743 Size was too big, too small, or not appropriate. + @constant kNameErr -6744 Name was not correct, allowed, or appropriate. + @constant kNotReadyErr -6745 Device or service is not ready. + @constant kReadErr -6746 Could not read. + @constant kWriteErr -6747 Could not write. + @constant kMismatchErr -6748 Something does not match. + @constant kDateErr -6749 Date is invalid or out-of-range. + @constant kUnderrunErr -6750 Less data than expected. + @constant kOverrunErr -6751 More data than expected. + @constant kEndingErr -6752 Connection, session, or something is ending. + @constant kConnectionErr -6753 Connection failed or could not be established. + @constant kAuthenticationErr -6754 Authentication failed or is not supported. + @constant kOpenErr -6755 Could not open file, pipe, device, etc. + @constant kTypeErr -6756 Incorrect or incompatible type (e.g. file, data, etc.). + @constant kSkipErr -6757 Items should be or was skipped. + @constant kNoAckErr -6758 No acknowledge. + @constant kCollisionErr -6759 Collision occurred (e.g. two on bus at same time). + @constant kBackoffErr -6760 Backoff in progress and operation intentionally failed. + @constant kNoAddressAckErr -6761 No acknowledge of address. + @constant kBusyErr -6762 Cannot perform because something is busy. + @constant kNoSpaceErr -6763 Not enough space to perform operation. + */ + +#if ( TARGET_LANGUAGE_C_LIKE ) + #if ( !TARGET_OS_MAC && !TARGET_API_MAC_OSX_KERNEL ) +typedef int32_t OSStatus; + #endif +#endif + +#define kNoErr 0 +#define kInProgressErr 1 + +// Generic error codes are in the range -6700 to -6779. + +#define kGenericErrorBase -6700 // Starting error code for all generic errors. + +#define kUnknownErr -6700 +#define kOptionErr -6701 +#define kSelectorErr -6702 +#define kExecutionStateErr -6703 +#define kPathErr -6704 +#define kParamErr -6705 +#define kParamCountErr -6706 +#define kCommandErr -6707 +#define kIDErr -6708 +#define kStateErr -6709 +#define kRangeErr -6710 +#define kRequestErr -6711 +#define kResponseErr -6712 +#define kChecksumErr -6713 +#define kNotHandledErr -6714 +#define kVersionErr -6715 +#define kSignatureErr -6716 +#define kFormatErr -6717 +#define kNotInitializedErr -6718 +#define kAlreadyInitializedErr -6719 +#define kNotInUseErr -6720 +#define kInUseErr -6721 +#define kTimeoutErr -6722 +#define kCanceledErr -6723 +#define kAlreadyCanceledErr -6724 +#define kCannotCancelErr -6725 +#define kDeletedErr -6726 +#define kNotFoundErr -6727 +#define kNoMemoryErr -6728 +#define kNoResourcesErr -6729 +#define kDuplicateErr -6730 +#define kImmutableErr -6731 +#define kUnsupportedDataErr -6732 +#define kIntegrityErr -6733 +#define kIncompatibleErr -6734 +#define kUnsupportedErr -6735 +#define kUnexpectedErr -6736 +#define kValueErr -6737 +#define kNotReadableErr -6738 +#define kNotWritableErr -6739 +#define kBadReferenceErr -6740 +#define kFlagErr -6741 +#define kMalformedErr -6742 +#define kSizeErr -6743 +#define kNameErr -6744 +#define kNotReadyErr -6745 +#define kReadErr -6746 +#define kWriteErr -6747 +#define kMismatchErr -6748 +#define kDateErr -6749 +#define kUnderrunErr -6750 +#define kOverrunErr -6751 +#define kEndingErr -6752 +#define kConnectionErr -6753 +#define kAuthenticationErr -6754 +#define kOpenErr -6755 +#define kTypeErr -6756 +#define kSkipErr -6757 +#define kNoAckErr -6758 +#define kCollisionErr -6759 +#define kBackoffErr -6760 +#define kNoAddressAckErr -6761 +#define kBusyErr -6762 +#define kNoSpaceErr -6763 + +#define kGenericErrorEnd -6779 // Last generic error code (inclusive) + +#if 0 +#pragma mark == Mac Compatibility == +#endif + +//=========================================================================================================================== +// Mac Compatibility +//=========================================================================================================================== + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum Duration + + @abstract Type used to specify a duration of time. + + @constant kDurationImmediate Indicates no delay/wait time. + @constant kDurationMicrosecond Microsecond units. + @constant kDurationMillisecond Millisecond units. + @constant kDurationSecond Second units. + @constant kDurationMinute Minute units. + @constant kDurationHour Hour units. + @constant kDurationDay Day units. + @constant kDurationForever Infinite period of time (no timeout). + + @discussion + + Duration values are intended to be multiplied by the specific interval to achieve an actual duration. For example, + to wait for 5 seconds you would use "5 * kDurationSecond". + */ + +#if ( TARGET_LANGUAGE_C_LIKE ) + #if ( !TARGET_OS_MAC ) +typedef int32_t Duration; + #endif +#endif + +#define kDurationImmediate 0L +#define kDurationMicrosecond -1L +#define kDurationMillisecond 1L +#define kDurationSecond ( 1000L * kDurationMillisecond ) +#define kDurationMinute ( 60L * kDurationSecond ) +#define kDurationHour ( 60L * kDurationMinute ) +#define kDurationDay ( 24L * kDurationHour ) +#define kDurationForever 0x7FFFFFFFL + +// Seconds <-> Minutes <-> Hours <-> Days <-> Weeks <-> Months <-> Years conversions + +#define kNanosecondsPerMicrosecond 1000 +#define kNanosecondsPerMillisecond 1000000 +#define kNanosecondsPerSecond 1000000000 +#define kMicrosecondsPerSecond 1000000 +#define kMicrosecondsPerMillisecond 1000 +#define kMillisecondsPerSecond 1000 +#define kSecondsPerMinute 60 +#define kSecondsPerHour ( 60 * 60 ) // 3600 +#define kSecondsPerDay ( 60 * 60 * 24 ) // 86400 +#define kSecondsPerWeek ( 60 * 60 * 24 * 7 ) // 604800 +#define kMinutesPerHour 60 +#define kMinutesPerDay ( 60 * 24 ) // 1440 +#define kHoursPerDay 24 +#define kDaysPerWeek 7 +#define kWeeksPerYear 52 +#define kMonthsPerYear 12 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined VersionStages + + @abstract NumVersion-style version stages. + */ + +#define kVersionStageDevelopment 0x20 +#define kVersionStageAlpha 0x40 +#define kVersionStageBeta 0x60 +#define kVersionStageFinal 0x80 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function NumVersionBuild + + @abstract Builds a 32-bit Mac-style NumVersion value (e.g. NumVersionBuild( 1, 2, 3, kVersionStageBeta, 4 ) -> 1.2.3b4). + */ + +#define NumVersionBuild( MAJOR, MINOR, BUGFIX, STAGE, REV ) \ + ( ( ( ( MAJOR ) & 0xFF ) << 24 ) | \ + ( ( ( MINOR ) & 0x0F ) << 20 ) | \ + ( ( ( BUGFIX ) & 0x0F ) << 16 ) | \ + ( ( ( STAGE ) & 0xFF ) << 8 ) | \ + ( ( ( REV ) & 0xFF ) ) ) + +#define NumVersionExtractMajor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 24 ) & 0xFF ) ) +#define NumVersionExtractMinorAndBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0xFF ) ) +#define NumVersionExtractMinor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 20 ) & 0x0F ) ) +#define NumVersionExtractBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0x0F ) ) +#define NumVersionExtractStage( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 8 ) & 0xFF ) ) +#define NumVersionExtractRevision( VERSION ) ( (uint8_t)( ( VERSION ) & 0xFF ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function NumVersionCompare + + @abstract Compares two NumVersion values and returns the following values: + + left < right -> -1 + left > right -> 1 + left = right -> 0 + */ + +#if ( TARGET_LANGUAGE_C_LIKE ) +int NumVersionCompare( uint32_t inLeft, uint32_t inRight ); +#endif + +#if 0 +#pragma mark == Binary Constants == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined binary_4 + + @abstract Macro to generate an 4-bit constant using binary notation (e.g. binary_4( 1010 ) == 0xA). + */ + +#define binary_4( a ) binary_4_hex_wrap( hex_digit4( a ) ) +#define binary_4_hex_wrap( a ) binary_4_hex( a ) +#define binary_4_hex( a ) ( 0x ## a ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined binary_8 + + @abstract Macro to generate an 8-bit constant using binary notation (e.g. binary_8( 01111011 ) == 0x7B). + */ + +#define binary_8( a ) binary_8_hex_wrap( hex_digit8( a ) ) +#define binary_8_hex_wrap( a ) binary_8_hex( a ) +#define binary_8_hex( a ) ( 0x ## a ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined binary_16 + + @abstract Macro to generate an 16-bit constant using binary notation (e.g. binary_16( 01111011, 01111011 ) == 0x7B7B). + */ + +#define binary_16( a, b ) binary_16_hex_wrap( hex_digit8( a ), hex_digit8( b ) ) +#define binary_16_hex_wrap( a, b ) binary_16_hex( a, b ) +#define binary_16_hex( a, b ) ( 0x ## a ## b ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined binary_32 + + @abstract Macro to generate an 32-bit constant using binary notation + (e.g. binary_32( 01111011, 01111011, 01111011, 01111011 ) == 0x7B7B7B7B). + */ + +#define binary_32( a, b, c, d ) binary_32_hex_wrap( hex_digit8( a ), hex_digit8( b ), hex_digit8( c ), hex_digit8( d ) ) +#define binary_32_hex_wrap( a, b, c, d ) binary_32_hex( a, b, c, d ) +#define binary_32_hex( a, b, c, d ) ( 0x ## a ## b ## c ## d ) + +// Binary Constant Helpers + +#define hex_digit8( a ) HEX_DIGIT_ ## a +#define hex_digit4( a ) HEX_DIGIT_ ## 0000 ## a + +#define HEX_DIGIT_00000000 00 +#define HEX_DIGIT_00000001 01 +#define HEX_DIGIT_00000010 02 +#define HEX_DIGIT_00000011 03 +#define HEX_DIGIT_00000100 04 +#define HEX_DIGIT_00000101 05 +#define HEX_DIGIT_00000110 06 +#define HEX_DIGIT_00000111 07 +#define HEX_DIGIT_00001000 08 +#define HEX_DIGIT_00001001 09 +#define HEX_DIGIT_00001010 0A +#define HEX_DIGIT_00001011 0B +#define HEX_DIGIT_00001100 0C +#define HEX_DIGIT_00001101 0D +#define HEX_DIGIT_00001110 0E +#define HEX_DIGIT_00001111 0F +#define HEX_DIGIT_00010000 10 +#define HEX_DIGIT_00010001 11 +#define HEX_DIGIT_00010010 12 +#define HEX_DIGIT_00010011 13 +#define HEX_DIGIT_00010100 14 +#define HEX_DIGIT_00010101 15 +#define HEX_DIGIT_00010110 16 +#define HEX_DIGIT_00010111 17 +#define HEX_DIGIT_00011000 18 +#define HEX_DIGIT_00011001 19 +#define HEX_DIGIT_00011010 1A +#define HEX_DIGIT_00011011 1B +#define HEX_DIGIT_00011100 1C +#define HEX_DIGIT_00011101 1D +#define HEX_DIGIT_00011110 1E +#define HEX_DIGIT_00011111 1F +#define HEX_DIGIT_00100000 20 +#define HEX_DIGIT_00100001 21 +#define HEX_DIGIT_00100010 22 +#define HEX_DIGIT_00100011 23 +#define HEX_DIGIT_00100100 24 +#define HEX_DIGIT_00100101 25 +#define HEX_DIGIT_00100110 26 +#define HEX_DIGIT_00100111 27 +#define HEX_DIGIT_00101000 28 +#define HEX_DIGIT_00101001 29 +#define HEX_DIGIT_00101010 2A +#define HEX_DIGIT_00101011 2B +#define HEX_DIGIT_00101100 2C +#define HEX_DIGIT_00101101 2D +#define HEX_DIGIT_00101110 2E +#define HEX_DIGIT_00101111 2F +#define HEX_DIGIT_00110000 30 +#define HEX_DIGIT_00110001 31 +#define HEX_DIGIT_00110010 32 +#define HEX_DIGIT_00110011 33 +#define HEX_DIGIT_00110100 34 +#define HEX_DIGIT_00110101 35 +#define HEX_DIGIT_00110110 36 +#define HEX_DIGIT_00110111 37 +#define HEX_DIGIT_00111000 38 +#define HEX_DIGIT_00111001 39 +#define HEX_DIGIT_00111010 3A +#define HEX_DIGIT_00111011 3B +#define HEX_DIGIT_00111100 3C +#define HEX_DIGIT_00111101 3D +#define HEX_DIGIT_00111110 3E +#define HEX_DIGIT_00111111 3F +#define HEX_DIGIT_01000000 40 +#define HEX_DIGIT_01000001 41 +#define HEX_DIGIT_01000010 42 +#define HEX_DIGIT_01000011 43 +#define HEX_DIGIT_01000100 44 +#define HEX_DIGIT_01000101 45 +#define HEX_DIGIT_01000110 46 +#define HEX_DIGIT_01000111 47 +#define HEX_DIGIT_01001000 48 +#define HEX_DIGIT_01001001 49 +#define HEX_DIGIT_01001010 4A +#define HEX_DIGIT_01001011 4B +#define HEX_DIGIT_01001100 4C +#define HEX_DIGIT_01001101 4D +#define HEX_DIGIT_01001110 4E +#define HEX_DIGIT_01001111 4F +#define HEX_DIGIT_01010000 50 +#define HEX_DIGIT_01010001 51 +#define HEX_DIGIT_01010010 52 +#define HEX_DIGIT_01010011 53 +#define HEX_DIGIT_01010100 54 +#define HEX_DIGIT_01010101 55 +#define HEX_DIGIT_01010110 56 +#define HEX_DIGIT_01010111 57 +#define HEX_DIGIT_01011000 58 +#define HEX_DIGIT_01011001 59 +#define HEX_DIGIT_01011010 5A +#define HEX_DIGIT_01011011 5B +#define HEX_DIGIT_01011100 5C +#define HEX_DIGIT_01011101 5D +#define HEX_DIGIT_01011110 5E +#define HEX_DIGIT_01011111 5F +#define HEX_DIGIT_01100000 60 +#define HEX_DIGIT_01100001 61 +#define HEX_DIGIT_01100010 62 +#define HEX_DIGIT_01100011 63 +#define HEX_DIGIT_01100100 64 +#define HEX_DIGIT_01100101 65 +#define HEX_DIGIT_01100110 66 +#define HEX_DIGIT_01100111 67 +#define HEX_DIGIT_01101000 68 +#define HEX_DIGIT_01101001 69 +#define HEX_DIGIT_01101010 6A +#define HEX_DIGIT_01101011 6B +#define HEX_DIGIT_01101100 6C +#define HEX_DIGIT_01101101 6D +#define HEX_DIGIT_01101110 6E +#define HEX_DIGIT_01101111 6F +#define HEX_DIGIT_01110000 70 +#define HEX_DIGIT_01110001 71 +#define HEX_DIGIT_01110010 72 +#define HEX_DIGIT_01110011 73 +#define HEX_DIGIT_01110100 74 +#define HEX_DIGIT_01110101 75 +#define HEX_DIGIT_01110110 76 +#define HEX_DIGIT_01110111 77 +#define HEX_DIGIT_01111000 78 +#define HEX_DIGIT_01111001 79 +#define HEX_DIGIT_01111010 7A +#define HEX_DIGIT_01111011 7B +#define HEX_DIGIT_01111100 7C +#define HEX_DIGIT_01111101 7D +#define HEX_DIGIT_01111110 7E +#define HEX_DIGIT_01111111 7F +#define HEX_DIGIT_10000000 80 +#define HEX_DIGIT_10000001 81 +#define HEX_DIGIT_10000010 82 +#define HEX_DIGIT_10000011 83 +#define HEX_DIGIT_10000100 84 +#define HEX_DIGIT_10000101 85 +#define HEX_DIGIT_10000110 86 +#define HEX_DIGIT_10000111 87 +#define HEX_DIGIT_10001000 88 +#define HEX_DIGIT_10001001 89 +#define HEX_DIGIT_10001010 8A +#define HEX_DIGIT_10001011 8B +#define HEX_DIGIT_10001100 8C +#define HEX_DIGIT_10001101 8D +#define HEX_DIGIT_10001110 8E +#define HEX_DIGIT_10001111 8F +#define HEX_DIGIT_10010000 90 +#define HEX_DIGIT_10010001 91 +#define HEX_DIGIT_10010010 92 +#define HEX_DIGIT_10010011 93 +#define HEX_DIGIT_10010100 94 +#define HEX_DIGIT_10010101 95 +#define HEX_DIGIT_10010110 96 +#define HEX_DIGIT_10010111 97 +#define HEX_DIGIT_10011000 98 +#define HEX_DIGIT_10011001 99 +#define HEX_DIGIT_10011010 9A +#define HEX_DIGIT_10011011 9B +#define HEX_DIGIT_10011100 9C +#define HEX_DIGIT_10011101 9D +#define HEX_DIGIT_10011110 9E +#define HEX_DIGIT_10011111 9F +#define HEX_DIGIT_10100000 A0 +#define HEX_DIGIT_10100001 A1 +#define HEX_DIGIT_10100010 A2 +#define HEX_DIGIT_10100011 A3 +#define HEX_DIGIT_10100100 A4 +#define HEX_DIGIT_10100101 A5 +#define HEX_DIGIT_10100110 A6 +#define HEX_DIGIT_10100111 A7 +#define HEX_DIGIT_10101000 A8 +#define HEX_DIGIT_10101001 A9 +#define HEX_DIGIT_10101010 AA +#define HEX_DIGIT_10101011 AB +#define HEX_DIGIT_10101100 AC +#define HEX_DIGIT_10101101 AD +#define HEX_DIGIT_10101110 AE +#define HEX_DIGIT_10101111 AF +#define HEX_DIGIT_10110000 B0 +#define HEX_DIGIT_10110001 B1 +#define HEX_DIGIT_10110010 B2 +#define HEX_DIGIT_10110011 B3 +#define HEX_DIGIT_10110100 B4 +#define HEX_DIGIT_10110101 B5 +#define HEX_DIGIT_10110110 B6 +#define HEX_DIGIT_10110111 B7 +#define HEX_DIGIT_10111000 B8 +#define HEX_DIGIT_10111001 B9 +#define HEX_DIGIT_10111010 BA +#define HEX_DIGIT_10111011 BB +#define HEX_DIGIT_10111100 BC +#define HEX_DIGIT_10111101 BD +#define HEX_DIGIT_10111110 BE +#define HEX_DIGIT_10111111 BF +#define HEX_DIGIT_11000000 C0 +#define HEX_DIGIT_11000001 C1 +#define HEX_DIGIT_11000010 C2 +#define HEX_DIGIT_11000011 C3 +#define HEX_DIGIT_11000100 C4 +#define HEX_DIGIT_11000101 C5 +#define HEX_DIGIT_11000110 C6 +#define HEX_DIGIT_11000111 C7 +#define HEX_DIGIT_11001000 C8 +#define HEX_DIGIT_11001001 C9 +#define HEX_DIGIT_11001010 CA +#define HEX_DIGIT_11001011 CB +#define HEX_DIGIT_11001100 CC +#define HEX_DIGIT_11001101 CD +#define HEX_DIGIT_11001110 CE +#define HEX_DIGIT_11001111 CF +#define HEX_DIGIT_11010000 D0 +#define HEX_DIGIT_11010001 D1 +#define HEX_DIGIT_11010010 D2 +#define HEX_DIGIT_11010011 D3 +#define HEX_DIGIT_11010100 D4 +#define HEX_DIGIT_11010101 D5 +#define HEX_DIGIT_11010110 D6 +#define HEX_DIGIT_11010111 D7 +#define HEX_DIGIT_11011000 D8 +#define HEX_DIGIT_11011001 D9 +#define HEX_DIGIT_11011010 DA +#define HEX_DIGIT_11011011 DB +#define HEX_DIGIT_11011100 DC +#define HEX_DIGIT_11011101 DD +#define HEX_DIGIT_11011110 DE +#define HEX_DIGIT_11011111 DF +#define HEX_DIGIT_11100000 E0 +#define HEX_DIGIT_11100001 E1 +#define HEX_DIGIT_11100010 E2 +#define HEX_DIGIT_11100011 E3 +#define HEX_DIGIT_11100100 E4 +#define HEX_DIGIT_11100101 E5 +#define HEX_DIGIT_11100110 E6 +#define HEX_DIGIT_11100111 E7 +#define HEX_DIGIT_11101000 E8 +#define HEX_DIGIT_11101001 E9 +#define HEX_DIGIT_11101010 EA +#define HEX_DIGIT_11101011 EB +#define HEX_DIGIT_11101100 EC +#define HEX_DIGIT_11101101 ED +#define HEX_DIGIT_11101110 EE +#define HEX_DIGIT_11101111 EF +#define HEX_DIGIT_11110000 F0 +#define HEX_DIGIT_11110001 F1 +#define HEX_DIGIT_11110010 F2 +#define HEX_DIGIT_11110011 F3 +#define HEX_DIGIT_11110100 F4 +#define HEX_DIGIT_11110101 F5 +#define HEX_DIGIT_11110110 F6 +#define HEX_DIGIT_11110111 F7 +#define HEX_DIGIT_11111000 F8 +#define HEX_DIGIT_11111001 F9 +#define HEX_DIGIT_11111010 FA +#define HEX_DIGIT_11111011 FB +#define HEX_DIGIT_11111100 FC +#define HEX_DIGIT_11111101 FD +#define HEX_DIGIT_11111110 FE +#define HEX_DIGIT_11111111 FF + +#if 0 +#pragma mark == Debugging == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function CommonServicesTest + + @abstract Unit test. + */ + +#if ( DEBUG ) + #if ( TARGET_LANGUAGE_C_LIKE ) +OSStatus CommonServicesTest( void ); + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __COMMON_SERVICES__ diff --git a/3rdparty/QtZeroConf/bonjour-sdk/DebugServices.h b/3rdparty/QtZeroConf/bonjour-sdk/DebugServices.h new file mode 100644 index 000000000..108f7f5f9 --- /dev/null +++ b/3rdparty/QtZeroConf/bonjour-sdk/DebugServices.h @@ -0,0 +1,1607 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @header DebugServices + + Debugging Library + */ + +#ifndef __DEBUG_SERVICES__ +#define __DEBUG_SERVICES__ + +#include + +#include "CommonServices.h" + +#if ( TARGET_OS_VXWORKS ) + #include "logLib.h" +#endif + +#if 0 +#pragma mark == Settings == +#endif + +//=========================================================================================================================== +// Settings +//=========================================================================================================================== + +// General + +#if ( !defined( DEBUG ) ) + #define DEBUG 0 +#endif + +#if ( defined( NDEBUG ) && DEBUG ) + #error NDEBUG defined and DEBUG is also enabled...they need to be in-sync +#endif + +// AssertMacros.h/Debugging.h overrides. + +#if ( !defined( DEBUG_OVERRIDE_APPLE_MACROS ) ) + #define DEBUG_OVERRIDE_APPLE_MACROS 1 +#endif + +// Routine name. Uses ISO __func__ where possible. Otherwise, uses the best thing that is available (if anything). + +#if ( defined( __MWERKS__ ) || ( __GNUC__ > 2 ) || ( ( __GNUC__ == 2 ) && ( __GNUC_MINOR__ >= 9 ) ) ) + #define __ROUTINE__ __func__ +#elif ( defined( __GNUC__ ) ) + #define __ROUTINE__ __PRETTY_FUNCTION__ +#elif ( defined( _MSC_VER ) && !defined( _WIN32_WCE ) ) + #define __ROUTINE__ __FUNCTION__ +#else + #define __ROUTINE__ "" +#endif + +// Variable argument macro support. Use ANSI C99 __VA_ARGS__ where possible. Otherwise, use the next best thing. + +#if ( defined( __GNUC__ ) ) + #if ( ( __GNUC__ > 3 ) || ( ( __GNUC__ == 3 ) && ( __GNUC_MINOR__ >= 3) ) ) + #define DEBUG_C99_VA_ARGS 1 + #define DEBUG_GNU_VA_ARGS 0 + #else + #define DEBUG_C99_VA_ARGS 0 + #define DEBUG_GNU_VA_ARGS 1 + #endif +#elif ( defined( __MWERKS__ ) ) + #define DEBUG_C99_VA_ARGS 1 + #define DEBUG_GNU_VA_ARGS 0 +#else + #define DEBUG_C99_VA_ARGS 0 + #define DEBUG_GNU_VA_ARGS 0 +#endif + +#if 0 +#pragma mark == Output == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_FPRINTF_ENABLED + + @abstract Enables ANSI C fprintf output. + */ + +#if ( !defined( DEBUG_FPRINTF_ENABLED ) ) + #if ( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE ) + #define DEBUG_FPRINTF_ENABLED 1 + #else + #define DEBUG_FPRINTF_ENABLED 0 + #endif +#else + #if ( TARGET_API_MAC_OSX_KERNEL || TARGET_OS_WINDOWS_CE ) + #error fprintf enabled, but not supported on Mac OS X kernel or Windows CE + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_MAC_OS_X_IOLOG_ENABLED + + @abstract Enables IOLog (Mac OS X Kernel) output. + */ + +#if ( !defined( DEBUG_MAC_OS_X_IOLOG_ENABLED ) ) + #define DEBUG_MAC_OS_X_IOLOG_ENABLED TARGET_API_MAC_OSX_KERNEL +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_KPRINTF_ENABLED + + @abstract Enables kprintf (Mac OS X Kernel) output. + */ + +#if ( !defined( DEBUG_KPRINTF_ENABLED ) ) + #define DEBUG_KPRINTF_ENABLED TARGET_API_MAC_OSX_KERNEL +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_IDEBUG_ENABLED + + @abstract Enables iDebug (Mac OS X user and Kernel) output. + + @discussion + + For Mac OS X kernel development, iDebug is enabled by default because we can dynamically check for the presence + of iDebug via some exported IOKit symbols. Mac OS X app usage doesn't allow dynamic detection because it relies + on statically linking to the iDebugServices.cp file so for Mac OS X app usage, you have to manually enable iDebug. + */ + +#if ( !defined( DEBUG_IDEBUG_ENABLED ) ) + #define DEBUG_IDEBUG_ENABLED TARGET_API_MAC_OSX_KERNEL +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_CORE_SERVICE_ASSERTS_ENABLED + + @abstract Controls whether Core Services assert handling is enabled. Enabling requires CoreServices framework. + */ + +#if ( !defined( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) ) + #if ( defined( __DEBUGGING__ ) ) + #define DEBUG_CORE_SERVICE_ASSERTS_ENABLED 1 + #else + #define DEBUG_CORE_SERVICE_ASSERTS_ENABLED 0 + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DebugOutputType + + @abstract Type of debug output (i.e. where the output goes). + */ + +typedef uint32_t DebugOutputType; + +#define kDebugOutputTypeNone 0x6E6F6E65U // 'none' - no params +#define kDebugOutputTypeCustom 0x63757374U // 'cust' - 1st param = function ptr, 2nd param = context +#define kDebugOutputTypeFPrintF 0x66707269U // 'fpri' - 1st param = DebugOutputTypeFlags [, 2nd param = filename] +#define kDebugOutputTypeiDebug 0x69646267U // 'idbg' - no params +#define kDebugOutputTypeKPrintF 0x6B707266U // 'kprf' - no params +#define kDebugOutputTypeMacOSXIOLog 0x696C6F67U // 'ilog' - no params +#define kDebugOutputTypeMacOSXLog 0x786C6F67U // 'xlog' - no params +#define kDebugOutputTypeWindowsDebugger 0x77696E64U // 'wind' - no params +#define kDebugOutputTypeWindowsEventLog 0x7765766CU // 'wevl' - 1st param = C-string name, 2nd param = HMODULE or NULL. + +// Console meta output kind - Any kind of Console output (in horizontal order of preference): +// +// Mac OS X = ANSI printf (viewable in Console.app) +// Mac OS X Kernel = IOLog (/var/log/system.log) or kprintf (serial). +// Windows = ANSI printf (Console window) or OutputDebugString (debugger). +// Other = ANSI printf (viewer varies). + +#define kDebugOutputTypeMetaConsole 0x434F4E53U // 'CONS' - no params + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DebugOutputTypeFlags + + @abstract Flags controlling how the output type is configured. + + @constant kDebugOutputTypeFlagsTypeMask Bit mask for the output type (e.g. stdout, stderr, file, etc.). + @constant kDebugOutputTypeFlagsStdOut fprintf should go to stdout. + @constant kDebugOutputTypeFlagsStdErr fprintf should go to stderr. + @constant kDebugOutputTypeFlagsFile fprintf should go to a specific file (filename passed as va_arg). + */ + +typedef unsigned int DebugOutputTypeFlags; + +#define kDebugOutputTypeFlagsTypeMask 0xF +#define kDebugOutputTypeFlagsStdOut 1 +#define kDebugOutputTypeFlagsStdErr 2 +#define kDebugOutputTypeFlagsFile 10 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DebugOutputFunctionPtr + + @abstract Function ptr for a custom callback to print debug output. + */ + +typedef void ( *DebugOutputFunctionPtr )( char *inData, size_t inSize, void *inContext ); + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#if 0 +#pragma mark == Flags == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DebugFlags + + @abstract Flags controlling how output is printed. + */ + +typedef uint32_t DebugFlags; + +#define kDebugFlagsNone 0 +#define kDebugFlagsNoAddress ( 1 << 0 ) +#define kDebugFlagsNoOffset ( 1 << 1 ) +#define kDebugFlags32BitOffset ( 1 << 2 ) +#define kDebugFlagsNoASCII ( 1 << 3 ) +#define kDebugFlagsNoNewLine ( 1 << 4 ) +#define kDebugFlags8BitSeparator ( 1 << 5 ) +#define kDebugFlags16BitSeparator ( 1 << 6 ) +#define kDebugFlagsNo32BitSeparator ( 1 << 7 ) +#define kDebugFlagsNo16ByteHexPad ( 1 << 8 ) +#define kDebugFlagsNoByteCount ( 1 << 9 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DebugTaskLevelFlags + + @abstract Flags indicating the task level. + */ + +enum +{ + kDebugInterruptLevelShift = 0, + kDebugInterruptLevelMask = 0x00000007, + kDebugInVBLTaskMask = 0x00000010, + kDebugInDeferredTaskMask = 0x00000020, + kDebugInSecondaryInterruptHandlerMask = 0x00000040, + kDebugPageFaultFatalMask = 0x00000100, // There should be a "kPageFaultFatalMask" in Debugging.h. + kDebugMPTaskLevelMask = 0x00000200, // There should be a "kMPTaskLevelMask" in Debugging.h. + kDebugInterruptDepthShift = 16, + kDebugInterruptDepthMask = 0x00FF0000 +}; + +#define DebugExtractTaskLevelInterruptLevel( LEVEL ) \ + ( ( ( LEVEL ) &kDebugInterruptLevelMask ) >> kDebugInterruptLevelShift ) + +#define DebugExtractTaskLevelInterruptDepth( LEVEL ) \ + ( ( ( LEVEL ) &kDebugInterruptDepthMask ) >> kDebugInterruptDepthShift ) + +#if 0 +#pragma mark == Levels == +#endif + +//=========================================================================================================================== +// Constants & Types - Levels +//=========================================================================================================================== + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DebugLevel + + @abstract Level used to control debug logging. + */ + +typedef int32_t DebugLevel; + +// Levels + +#define kDebugLevelMask 0x0000FFFF +#define kDebugLevelChatty 100 +#define kDebugLevelVerbose 500 +#define kDebugLevelTrace 800 +#define kDebugLevelInfo 1000 +#define kDebugLevelNotice 3000 +#define kDebugLevelWarning 5000 +#define kDebugLevelAssert 6000 +#define kDebugLevelRequire 7000 +#define kDebugLevelError 8000 +#define kDebugLevelCritical 9000 +#define kDebugLevelAlert 10000 +#define kDebugLevelEmergency 11000 +#define kDebugLevelTragic 12000 +#define kDebugLevelMax 0x0000FFFF + +// Level Flags + +#define kDebugLevelFlagMask 0xFFFF0000 +#define kDebugLevelFlagStackTrace 0x00010000 +#define kDebugLevelFlagDebugBreak 0x00020000 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef LogLevel + + @abstract Level used to control which events are logged. + */ + +typedef int32_t LogLevel; + +#define kLogLevelUninitialized -1L +#define kLogLevelAll 0L +#define kLogLevelChatty 100L +#define kLogLevelVerbose 500L +#define kLogLevelTrace 800L +#define kLogLevelInfo 1000L +#define kLogLevelNotice 3000L +#define kLogLevelWarning 4000L +#define kLogLevelAssert 6000L +#define kLogLevelRequire 7000L +#define kLogLevelError 8000L +#define kLogLevelCritical 9000L +#define kLogLevelAlert 10000L +#define kLogLevelEmergency 11000L +#define kLogLevelTragic 12000L +#define kLogLevelOff 0x0000FFFEL + +#if 0 +#pragma mark == Properties == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DebugPropertyTag + + @abstract Tag for properties. + */ + +typedef uint32_t DebugPropertyTag; + +#define kDebugPropertyTagPrintLevelMin 0x6D696E70U // 'minp' Get: 1st param = DebugLevel * + // Set: 1st param = DebugLevel + +#define kDebugPropertyTagPrintLevel kDebugPropertyTagPrintLevelMin + +#define kDebugPropertyTagPrintLevelMax 0x706D786CU // 'maxp' Get: 1st param = DebugLevel * + // Set: 1st param = DebugLevel + +#define kDebugPropertyTagBreakLevel 0x62726B6CU // 'brkl' Get: 1st param = DebugLevel * + // Set: 1st param = DebugLevel +#if 0 +#pragma mark == General macros == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_UNUSED + + @abstract Macro to mark a paramter as unused to avoid unused parameter warnings. + + @discussion + + There is no universally supported pragma/attribute for indicating a variable is unused. DEBUG_UNUSED lets us + indicate a variable is unused in a manner that is supported by most compilers. + */ + +#define DEBUG_UNUSED( X ) (void)( X ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_USE_ONLY + + @abstract Macro to mark a variable as used only when debugging is enabled. + + @discussion + + Variables are sometimes needed only for debugging. When debugging is turned off, these debug-only variables generate + compiler warnings about unused variables. To eliminate these warnings, use these macros to indicate variables that + are only used for debugging. + */ + +#if ( DEBUG ) + #define DEBUG_USE_ONLY( X ) +#else + #define DEBUG_USE_ONLY( X ) (void)( X ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_LOCAL + + @abstract Macros to make variables and functions static when debugging is off, but extern when debugging is on. + + @discussion + + Rather than using "static" directly, using this macros allows you to access these variables external while + debugging without being penalized for production builds. + */ + +#if ( DEBUG ) + #define DEBUG_LOCAL +#else + #define DEBUG_LOCAL static +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_STATIC + + @abstract Macros to make variables and functions static when debugging is off, but extern when debugging is on. + + @discussion + + Rather than using "static" directly, using this macros allows you to access these variables external while + debugging without being penalized for production builds. + */ + +#if ( DEBUG ) + #define DEBUG_STATIC +#else + #define DEBUG_STATIC static +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_EXPORT + + @abstract Macros to export variables. + + @discussion + + "__private_extern__" is a hack for IOKit to allow symbols to be exported from compilation units, but + // not exported outside a driver (IOKit uses a lame global namespace for symbols). This still does not + // solve the problem of multiple drivers in the same dependency chain since they share symbols. + */ + +#if ( TARGET_API_MAC_OSX_KERNEL ) + #define DEBUG_EXPORT __private_extern__ +#else + #define DEBUG_EXPORT extern +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined debug_add + + @abstract Macro to add (or subtract if negative) a value when debugging is on. Does nothing if debugging is off. + */ + +#if ( DEBUG ) + #define debug_add( A, B ) ( A ) += ( B ) +#else + #define debug_add( A, B ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined debug_perform + + @abstract Macro to perform something in debug-only builds. + */ + +#if ( DEBUG ) + #define debug_perform( X ) do { X; } while( 0 ) +#else + #define debug_perform( X ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function translate_errno + + @abstract Returns 0 if the test success. If the test fails, returns errno if non-zero and othewise the alternate error. + */ + +#define translate_errno( TEST, ERRNO, ALTERNATE_ERROR ) ( ( TEST ) ? 0 : ( ERRNO ) ? ( ERRNO ) : ( ALTERNATE_ERROR ) ) + +#if 0 +#pragma mark == Compile Time macros == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_compile_time + + @abstract Performs a compile-time check of something such as the size of an int. + + @discussion + + This declares an array with a size that is determined by a compile-time expression. If the expression evaluates + to 0, the array has a size of -1, which is illegal and generates a compile-time error. + + For example: + + check_compile_time( sizeof( int ) == 4 ); + + Note: This only works with compile-time expressions. + Note: This only works in places where extern declarations are allowed (e.g. global scope). + + References: + + + + + Note: The following macros differ from the macros on the www.jaggersoft.com web site because those versions do not + work with GCC due to GCC allow a zero-length array. Using a -1 condition turned out to be more portable. + */ + +#define check_compile_time( X ) extern int debug_compile_time_name[ ( X ) ? 1 : -1 ] + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_compile_time_code + + @abstract Perform a compile-time check, suitable for placement in code, of something such as the size of an int. + + @discussion + + This creates a switch statement with an existing case for 0 and an additional case using the result of a + compile-time expression. A switch statement cannot have two case labels with the same constant so if the + compile-time expression evaluates to 0, it is illegal and generates a compile-time error. If the compile-time + expression does not evaluate to 0, the resulting value is used as the case label and it compiles without error. + + For example: + + check_compile_time_code( sizeof( int ) == 4 ); + + Note: This only works with compile-time expressions. + Note: This does not work in a global scope so it must be inside a function. + + References: + + + + */ + +#define check_compile_time_code( X ) switch( 0 ) { case 0: case X:; } + +#if 0 +#pragma mark == check macros == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check + + @abstract Check that an expression is true (non-zero). + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method. + + Code inside check() statements is not compiled into production builds. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef check +#endif +#if ( !defined( check ) ) + #if ( DEBUG ) + #define check( X ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + } while( 0 ) + #else + #define check( X ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_string + + @abstract Check that an expression is true (non-zero) with an explanation. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method. + + Code inside check_string() statements is not compiled into production builds. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef check_string +#endif +#if ( !defined( check_string ) ) + #if ( DEBUG ) + #define check_string( X, STR ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + \ + } while( 0 ) + #else + #define check_string( X, STR ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_noerr + + @abstract Check that an error code is noErr (0). + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method. + + Code inside check_noerr() statements is not compiled into production builds. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef check_noerr +#endif +#if ( !defined( check_noerr ) ) + #if ( DEBUG ) + #define check_noerr( ERR ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + \ + } while( 0 ) + #else + #define check_noerr( ERR ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_noerr_string + + @abstract Check that an error code is noErr (0) with an explanation. + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method. + + Code inside check_noerr_string() statements is not compiled into production builds. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef check_noerr_string +#endif +#if ( !defined( check_noerr_string ) ) + #if ( DEBUG ) + #define check_noerr_string( ERR, STR ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + \ + } while( 0 ) + #else + #define check_noerr_string( ERR, STR ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_translated_errno + + @abstract Check a condition and prints errno (if non-zero) to the log. + + @discussion + + Code inside check_translated_errno() statements is not compiled into production builds. + */ + +#if ( !defined( check_translated_errno ) ) + #if ( DEBUG ) + #define check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) \ + do \ + { \ + if( !( TEST ) ) \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERRNO ); \ + localErr = ( localErr != 0 ) ? localErr : (int_least32_t)( ALTERNATE_ERROR ); \ + debug_print_assert( localErr, # TEST, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + \ + } while( 0 ) + #else + #define check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_ptr_overlap + + @abstract Checks that two ptrs do not overlap. + */ + +#define check_ptr_overlap( P1, P1_SIZE, P2, P2_SIZE ) \ + do \ + { \ + check( !( ( (uintptr_t)( P1 ) >= (uintptr_t)( P2 ) ) && \ + ( (uintptr_t)( P1 ) < ( ( (uintptr_t)( P2 ) ) + ( P2_SIZE ) ) ) ) ); \ + check( !( ( (uintptr_t)( P2 ) >= (uintptr_t)( P1 ) ) && \ + ( (uintptr_t)( P2 ) < ( ( (uintptr_t)( P1 ) ) + ( P1_SIZE ) ) ) ) ); \ + \ + } while( 0 ) + +#if 0 +#pragma mark == require macros == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require + + @abstract Requires that an expression evaluate to true. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require +#endif +#if ( !defined( require ) ) + #define require( X, LABEL ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_string + + @abstract Requires that an expression evaluate to true with an explanation. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method then jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_string +#endif +#if ( !defined( require_string ) ) + #define require_string( X, LABEL, STR ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_quiet + + @abstract Requires that an expression evaluate to true. + + @discussion + + If expression evalulates to false, this jumps to a label. No debugging information is printed. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_quiet +#endif +#if ( !defined( require_quiet ) ) + #define require_quiet( X, LABEL ) \ + do \ + { \ + if( !( X ) ) \ + { \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr + + @abstract Require that an error code is noErr (0). + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr +#endif +#if ( !defined( require_noerr ) ) + #define require_noerr( ERR, LABEL ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_string + + @abstract Require that an error code is noErr (0). + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.), and a custom explanation string using the default debugging output method using the + default debugging output method then jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_string +#endif +#if ( !defined( require_noerr_string ) ) + #define require_noerr_string( ERR, LABEL, STR ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_action_string + + @abstract Require that an error code is noErr (0). + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.), and a custom explanation string using the default debugging output method using the + default debugging output method then executes an action and jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_action_string +#endif +#if ( !defined( require_noerr_action_string ) ) + #define require_noerr_action_string( ERR, LABEL, ACTION, STR ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_quiet + + @abstract Require that an error code is noErr (0). + + @discussion + + If the error code is non-0, this jumps to a label. No debugging information is printed. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_quiet +#endif +#if ( !defined( require_noerr_quiet ) ) + #define require_noerr_quiet( ERR, LABEL ) \ + do \ + { \ + if( ( ERR ) != 0 ) \ + { \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_action + + @abstract Require that an error code is noErr (0) with an action to execute otherwise. + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then executes an action and jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_action +#endif +#if ( !defined( require_noerr_action ) ) + #define require_noerr_action( ERR, LABEL, ACTION ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_action_quiet + + @abstract Require that an error code is noErr (0) with an action to execute otherwise. + + @discussion + + If the error code is non-0, this executes an action and jumps to a label. No debugging information is printed. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_action_quiet +#endif +#if ( !defined( require_noerr_action_quiet ) ) + #define require_noerr_action_quiet( ERR, LABEL, ACTION ) \ + do \ + { \ + if( ( ERR ) != 0 ) \ + { \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_action + + @abstract Requires that an expression evaluate to true with an action to execute otherwise. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then executes an action and jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_action +#endif +#if ( !defined( require_action ) ) + #define require_action( X, LABEL, ACTION ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_action_quiet + + @abstract Requires that an expression evaluate to true with an action to execute otherwise. + + @discussion + + If expression evalulates to false, this executes an action and jumps to a label. No debugging information is printed. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_action_quiet +#endif +#if ( !defined( require_action_quiet ) ) + #define require_action_quiet( X, LABEL, ACTION ) \ + do \ + { \ + if( !( X ) ) \ + { \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_action_string + + @abstract Requires that an expression evaluate to true with an explanation and action to execute otherwise. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method then executes an + action and jumps to a label. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_action_string +#endif +#if ( !defined( require_action_string ) ) + #define require_action_string( X, LABEL, ACTION, STR ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) + +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_throw + + @abstract Requires that an expression evaluates to true or an exception is thrown. + + @discussion + + If the expression evaluates to false, this prints debugging information (actual expression string, file, + line number, function name, etc.) using the default debugging output method then throws an exception. + */ + +#if ( defined( __cplusplus ) ) + #define require_throw( X ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, # X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + throw kUnknownErr; \ + } \ + \ + } while( 0 ) +#endif + +#if 0 +#pragma mark == Design-By-Contract macros == +#endif + +//=========================================================================================================================== +// Design-By-Contract macros +//=========================================================================================================================== + +#define ensure( X ) check( X ) +#define ensure_string( X, STR ) check_string( X, STR ) +#define ensure_noerr( ERR ) check_noerr( ERR ) +#define ensure_noerr_string( ERR, STR ) check_noerr_string( ERR, STR ) +#define ensure_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) + +// Note: Design-By-Contract "require" macros are already defined elsewhere. + +#if 0 +#pragma mark == Expect macros == +#endif + +//=========================================================================================================================== +// Expect macros +//=========================================================================================================================== + +// Expect macros allow code to include runtime checking of things that should not happen in shipping code (e.g. internal +// programmer errors, such as a NULL parameter where it is not allowed). Once the code has been verified to work correctly +// without asserting, the DEBUG_EXPECT_VERIFIED conditional can be set to eliminate the error checking entirely. It can +// also be useful to measure the cost of error checking code by profiling with it enable and with it disabled. + +#if ( DEBUG_EXPECT_VERIFIED ) + #define require_expect + #define require_string_expect + #define require_quiet_expect + #define require_noerr_expect + #define require_noerr_string_expect + #define require_noerr_action_string_expect + #define require_noerr_quiet_expect + #define require_noerr_action_expect + #define require_noerr_action_quiet_expect + #define require_action_expect + #define require_action_quiet_expect + #define require_action_string_expect +#else + #define require_expect require + #define require_string_expect require_string + #define require_quiet_expect require_quiet + #define require_noerr_expect require_noerr + #define require_noerr_string_expect require_noerr_string + #define require_noerr_action_string_expect require_noerr_action_string + #define require_noerr_quiet_expect require_noerr_quiet + #define require_noerr_action_expect require_noerr_action + #define require_noerr_action_quiet_expect require_noerr_action_quiet + #define require_action_expect require_action + #define require_action_quiet_expect require_action_quiet + #define require_action_string_expect require_action_string +#endif + +#if 0 +#pragma mark == Output macros == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined debug_string + + @abstract Prints a debugging C string. + */ + +#if ( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef debug_string +#endif +#if ( !defined( debug_string ) ) + #if ( DEBUG ) + #define debug_string( STR ) \ + do \ + { \ + debug_print_assert( 0, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + \ + } while( 0 ) + #else + #define debug_string( STR ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined debug_print_assert + + @abstract Prints an assertion. + */ + +#if ( DEBUG ) + #define debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) \ + DebugPrintAssert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) +#else + #define debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined dlog + + @abstract Prints a debug-only message. + */ + +#if ( DEBUG ) + #if ( DEBUG_C99_VA_ARGS ) + #define dlog(... ) DebugPrintF( __VA_ARGS__ ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define dlog( ARGS... ) DebugPrintF( ## ARGS ) + #else + #define dlog DebugPrintF + #endif +#else + #if ( DEBUG_C99_VA_ARGS ) + #define dlog(... ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define dlog( ARGS... ) + #else + #define dlog while( 0 ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined dlogv + + @abstract Prints a debug-only message. + */ + +#if ( DEBUG ) + #define dlogv( LEVEL, FORMAT, LIST ) DebugPrintFVAList( ( LEVEL ), ( FORMAT ), ( LIST ) ) +#else + #define dlogv( LEVEL, FORMAT, LIST ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined dlogmem + + @abstract Prints a debug-only dump of memory. + */ + +#if ( DEBUG ) + #define dlogmem( LEVEL, PTR, SIZE ) \ + DebugHexDump( ( LEVEL ), 0, NULL, 0, 0, NULL, 0, ( PTR ), ( PTR ), ( SIZE ), kDebugFlagsNone, NULL, 0 ) +#else + #define dlogmem( LEVEL, PTR, SIZE ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DebugNSLog + + @abstract Debug-only macro for the Cocoa NSLog function. + */ + +#if ( DEBUG ) + #if ( DEBUG_C99_VA_ARGS ) + #define DebugNSLog(... ) NSLog( __VA_ARGS__ ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define DebugNSLog( ARGS... ) NSLog( ## ARGS ) + #else + #define DebugNSLog NSLog + #endif +#else + #if ( DEBUG_C99_VA_ARGS ) + #define DebugNSLog(... ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define DebugNSLog( ARGS... ) + #else + #define DebugNSLog while( 0 ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DebugLogMsg + + @abstract Debug-only macro for the VxWorks logMsg function. + */ + +#if ( TARGET_OS_VXWORKS ) + #if ( DEBUG ) + #define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 ) \ + do \ + { \ + if( ( inLevel >= gDebugPrintLevelMin ) || ( inLevel <= gDebugPrintLevelMax ) ) \ + { \ + logMsg( ( FORMAT ), ( P1 ), ( P2 ), ( P3 ), ( P4 ), ( P5 ), ( P6 ) ); \ + } \ + \ + } while( 0 ) + #else + #define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 ) + #endif +#else + #define DebugLogMsg dlog +#endif + +#if 0 +#pragma mark == Routines - General == +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugInitialize + + @abstract Initializes the debugging library for a specific kind of output. + + @param inType + @param varArg Variable number parameters, controlled by the "inType" parameter. + */ + +#if ( DEBUG ) +DEBUG_EXPORT OSStatus DebugInitialize( DebugOutputType inType, ... ); +#endif + +#if ( DEBUG ) + #if ( DEBUG_C99_VA_ARGS ) + #define debug_initialize(... ) DebugInitialize( __VA_ARGS__ ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define debug_initialize( ARGS... ) DebugInitialize( ## ARGS ) + #else + #define debug_initialize DebugInitialize + #endif +#else + #if ( DEBUG_C99_VA_ARGS ) + #define debug_initialize(... ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define debug_initialize( ARGS... ) + #else + #define debug_initialize while( 0 ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugFinalize + + @abstract Releases any resources used by the debugging library + */ + +#if ( DEBUG ) +DEBUG_EXPORT void DebugFinalize( void ); +#endif + +#if ( DEBUG ) + #define debug_terminate() DebugFinalize() +#else + #define debug_terminate() +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugGetProperty + + @abstract Gets the specified property from the debugging library. + */ + +#if ( DEBUG ) +DEBUG_EXPORT OSStatus DebugGetProperty( DebugPropertyTag inTag, ... ); +#endif + +#if ( DEBUG ) + #if ( DEBUG_C99_VA_ARGS ) + #define debug_get_property(... ) DebugGetProperty( __VA_ARGS__ ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define debug_get_property( ARGS... ) DebugGetProperty( ## ARGS ) + #else + #define debug_get_property DebugGetProperty + #endif +#else + #if ( DEBUG_C99_VA_ARGS ) + #define debug_get_property(... ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define debug_get_property( ARGS... ) + #else + #define debug_get_property while( 0 ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugSetProperty + + @abstract Sets the specified property from the debugging library. + */ + +#if ( DEBUG ) +DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... ); +#endif + +#if ( DEBUG ) + #if ( DEBUG_C99_VA_ARGS ) + #define debug_set_property(... ) DebugSetProperty( __VA_ARGS__ ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define debug_set_property( ARGS... ) DebugSetProperty( ## ARGS ) + #else + #define debug_set_property DebugSetProperty + #endif +#else + #if ( DEBUG_C99_VA_ARGS ) + #define debug_set_property(... ) + #elif ( DEBUG_GNU_VA_ARGS ) + #define debug_set_property( ARGS... ) + #else + #define debug_set_property while( 0 ) + #endif +#endif + +#if 0 +#pragma mark == Routines - Debugging Output == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugPrintF + + @abstract Prints a debug message with printf-style formatting. + + @param inLevel Error that generated this assert or noErr. + + @param inFormatString + C string containing assertion text. + + @param VAR_ARG + Variable number of arguments depending on the format string. + + @result Number of bytes printed or -1 on error. + */ + +#if ( DEBUG ) +DEBUG_EXPORT size_t DebugPrintF( DebugLevel inLevel, const char *inFormat, ... ); +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugPrintFVAList + + @abstract va_list version of DebugPrintF. See DebugPrintF for more info. + */ + +#if ( DEBUG ) +DEBUG_EXPORT size_t DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs ); +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugPrintAssert + + @abstract Prints a message describing the reason the (e.g. an assert failed), an optional error message, + an optional source filename, an optional source line number. + + @param inErrorCode Error that generated this assert or noErr. + @param inAssertString C string containing assertion text. + @param inMessage C string containing a message about the assert. + @param inFileName C string containing path of file where the error occurred. + @param inLineNumber Line number in source file where the error occurred. + @param inFunction C string containing name of function where assert occurred. + + @discussion + + Example output: + + [ASSERT] assert: "dataPtr != NULL" allocate memory for object failed + [ASSERT] where: "MyFile.c", line 123, ("MyFunction") + + OR + + [ASSERT] error: -6728 (kNoMemoryErr) + [ASSERT] where: "MyFile.c", line 123, ("MyFunction") + */ + +#if ( DEBUG ) +DEBUG_EXPORT void +DebugPrintAssert( + int_least32_t inErrorCode, + const char * inAssertString, + const char * inMessage, + const char * inFilename, + int_least32_t inLineNumber, + const char * inFunction ); +#endif + +#if 0 +#pragma mark == Routines - Utilities == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugSNPrintF + + @abstract Debugging versions of standard C snprintf with extra features. + + @param sbuffer Buffer to receive result. Null terminated unless the buffer size is 0. + @param buflen Size of the buffer including space for the null terminator. + @param fmt printf-style format string. + @param VAR_ARG Variable number of arguments depending on the format string. + + @result Number of characters written (minus the null terminator). + + @discussion + + Extra features over the standard C snprintf: +
+        64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb).
+        %@   - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription.
+        %a   - Network Address: %.4a=IPv4, %.6a=Ethernet, %.8a Fibre Channel, %.16a=IPv6. Arg=ptr to network address.
+        %#a  - IPv4 or IPv6 mDNSAddr. Arg=ptr to mDNSAddr.
+        %##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr.
+        %b   - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc.
+        %C   - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode.
+        %H   - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+        %#H  - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+        %m   - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code arg=the same as %d, %x, etc.
+        %#s  - Pascal-style length-prefixed string. Arg=ptr to string.
+        %##s - DNS label-sequence name. Arg=ptr to name.
+        %S   - UTF-16 string, 0x0000 terminated. Host order if no BOM. Precision is UTF-16 count. Precision includes BOM.
+        %#S  - Big Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
+        %##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
+        %U   - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID.
+    
+ */ + +#if ( DEBUG ) +DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...); +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugSNPrintFVAList + + @abstract va_list version of DebugSNPrintF. See DebugSNPrintF for more info. + */ + +#if ( DEBUG ) +DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg); +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugGetErrorString + + @abstract Gets an error string from an error code. + + @param inStatus Error code to get the string for. + @param inBuffer Optional buffer to copy the string to for non-static strings. May be null. + @param inBufferSize Size of optional buffer. May be 0. + + @result C string containing error string for the error code. Guaranteed to be a valid, static string. If a + buffer is supplied, the return value will always be a pointer to the supplied buffer, which will + contain the best available description of the error code. If a buffer is not supplied, the return + value will be the best available description of the error code that can be represented as a static + string. This allows code that cannot use a temporary buffer to hold the result to still get a useful + error string in most cases, but also allows code that can use a temporary buffer to get the best + available description. + */ + +#if ( DEBUG ) +DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize ); +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugHexDump + + @abstract Hex dumps data to a string or to the output device. + */ + +#if ( DEBUG ) +DEBUG_EXPORT size_t +DebugHexDump( + DebugLevel inLevel, + int inIndent, + const char * inLabel, + size_t inLabelSize, + int inLabelMinWidth, + const char * inType, + size_t inTypeSize, + const void * inDataStart, + const void * inData, + size_t inDataSize, + DebugFlags inFlags, + char * outBuffer, + size_t inBufferSize ); +#endif + +#if ( DEBUG ) + #define dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE ) \ + DebugHexDump( ( LEVEL ), (INDENT), ( LABEL ), ( LABEL_SIZE ), ( LABEL_MIN_SIZE ), ( TYPE ), ( TYPE_SIZE ), \ + ( DATA_START ), ( DATA ), ( DATA_SIZE ), ( FLAGS ), ( BUFFER ), ( BUFFER_SIZE ) ) +#else + #define dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugTaskLevel + + @abstract Returns the current task level. + + @result Current task level + + @discussion + + Bit masks to isolate portions of the result (note that some masks may also need bit shifts to right justify): +
+        kDebugInterruptLevelMask				- Indicates the current interrupt level (> 0 means interrupt time).
+        kDebugInVBLTaskMask						- Indicates if a VBL task is currently being executed.
+        kDebugInDeferredTaskMask				- Indicates if a Deferred Task is currently being executed.
+        kDebugInSecondaryInterruptHandlerMask	- Indicates if a Secondary Interrupt Handler is currently being executed.
+        kDebugPageFaultFatalMask				- Indicates if it is unsafe to cause a page fault (worse than interrupt time).
+        kDebugMPTaskLevelMask					- Indicates if being called from an MP task.
+        kDebugInterruptDepthMask				- 0 means task level, 1 means in interrupt, > 1 means in nested interrupt.
+    
+ + Helpers: +
+        DebugExtractTaskLevelInterruptDepth()   - Macro to extract interrupt depth from task level value.
+    
+ */ + +#if ( DEBUG ) +DEBUG_EXPORT uint32_t DebugTaskLevel( void ); +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugServicesTest + + @abstract Unit test. + */ + +#if ( DEBUG ) +DEBUG_EXPORT OSStatus DebugServicesTest( void ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __DEBUG_SERVICES__ diff --git a/3rdparty/QtZeroConf/bonjour-sdk/dns_sd.h b/3rdparty/QtZeroConf/bonjour-sdk/dns_sd.h new file mode 100755 index 000000000..a1f5a573f --- /dev/null +++ b/3rdparty/QtZeroConf/bonjour-sdk/dns_sd.h @@ -0,0 +1,2728 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/*! @header DNS Service Discovery + * + * @discussion This section describes the functions, callbacks, and data structures + * that make up the DNS Service Discovery API. + * + * The DNS Service Discovery API is part of Bonjour, Apple's implementation + * of zero-configuration networking (ZEROCONF). + * + * Bonjour allows you to register a network service, such as a + * printer or file server, so that it can be found by name or browsed + * for by service type and domain. Using Bonjour, applications can + * discover what services are available on the network, along with + * all the information -- such as name, IP address, and port -- + * necessary to access a particular service. + * + * In effect, Bonjour combines the functions of a local DNS server and + * AppleTalk. Bonjour allows applications to provide user-friendly printer + * and server browsing, among other things, over standard IP networks. + * This behavior is a result of combining protocols such as multicast and + * DNS to add new functionality to the network (such as multicast DNS). + * + * Bonjour gives applications easy access to services over local IP + * networks without requiring the service or the application to support + * an AppleTalk or a Netbeui stack, and without requiring a DNS server + * for the local network. + */ + +/* _DNS_SD_H contains the API version number for this header file + * The API version defined in this header file symbol allows for compile-time + * checking, so that C code building with earlier versions of the header file + * can avoid compile errors trying to use functions that aren't even defined + * in those earlier versions. Similar checks may also be performed at run-time: + * => weak linking -- to avoid link failures if run with an earlier + * version of the library that's missing some desired symbol, or + * => DNSServiceGetProperty(DaemonVersion) -- to verify whether the running daemon + * ("system service" on Windows) meets some required minimum functionality level. + */ + +#ifndef _DNS_SD_H +#define _DNS_SD_H 8800035 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Set to 1 if libdispatch is supported + * Note: May also be set by project and/or Makefile + */ +#if defined(__APPLE__) +#define _DNS_SD_LIBDISPATCH 1 +#else +#define _DNS_SD_LIBDISPATCH 0 +#endif + +/* standard calling convention under Win32 is __stdcall */ +/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */ +/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */ +#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64) +#define DNSSD_API __stdcall +#else +#define DNSSD_API +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 4)) +#define DNSSD_EXPORT __attribute__((visibility("default"))) +#else +#define DNSSD_EXPORT +#endif + +#if defined(_WIN32) +#include +typedef SOCKET dnssd_sock_t; +#else +typedef int dnssd_sock_t; +#endif + +/* stdint.h does not exist on FreeBSD 4.x; its types are defined in sys/types.h instead */ +#if defined(__FreeBSD__) && (__FreeBSD__ < 5) +#include + +/* Likewise, on Sun, standard integer types are in sys/types.h */ +#elif defined(__sun__) +#include + +/* EFI does not have stdint.h, or anything else equivalent */ +#elif defined(EFI32) || defined(EFI64) || defined(EFIX64) +#include "Tiano.h" +#if !defined(_STDINT_H_) +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; +#endif +/* Windows has its own differences */ +#elif defined(_WIN32) +#include +#define _UNUSED +#ifndef _MSL_STDINT_H +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; +#endif + +/* All other Posix platforms use stdint.h */ +#else +#include +#endif + +#if _DNS_SD_LIBDISPATCH +#include +#endif + +/* DNSServiceRef, DNSRecordRef + * + * Opaque internal data types. + * Note: client is responsible for serializing access to these structures if + * they are shared between concurrent threads. + */ + +typedef struct _DNSServiceRef_t *DNSServiceRef; +typedef struct _DNSRecordRef_t *DNSRecordRef; + +struct sockaddr; + +/*! @enum General flags + * Most DNS-SD API functions and callbacks include a DNSServiceFlags parameter. + * As a general rule, any given bit in the 32-bit flags field has a specific fixed meaning, + * regardless of the function or callback being used. For any given function or callback, + * typically only a subset of the possible flags are meaningful, and all others should be zero. + * The discussion section for each API call describes which flags are valid for that call + * and callback. In some cases, for a particular call, it may be that no flags are currently + * defined, in which case the DNSServiceFlags parameter exists purely to allow future expansion. + * In all cases, developers should expect that in future releases, it is possible that new flag + * values will be defined, and write code with this in mind. For example, code that tests + * if (flags == kDNSServiceFlagsAdd) ... + * will fail if, in a future release, another bit in the 32-bit flags field is also set. + * The reliable way to test whether a particular bit is set is not with an equality test, + * but with a bitwise mask: + * if (flags & kDNSServiceFlagsAdd) ... + * With the exception of kDNSServiceFlagsValidate, each flag can be valid(be set) + * EITHER only as an input to one of the DNSService*() APIs OR only as an output + * (provide status) through any of the callbacks used. For example, kDNSServiceFlagsAdd + * can be set only as an output in the callback, whereas the kDNSServiceFlagsIncludeP2P + * can be set only as an input to the DNSService*() APIs. See comments on kDNSServiceFlagsValidate + * defined in enum below. + */ +enum +{ + kDNSServiceFlagsMoreComing = 0x1, + /* MoreComing indicates to a callback that at least one more result is + * queued and will be delivered following immediately after this one. + * When the MoreComing flag is set, applications should not immediately + * update their UI, because this can result in a great deal of ugly flickering + * on the screen, and can waste a great deal of CPU time repeatedly updating + * the screen with content that is then immediately erased, over and over. + * Applications should wait until MoreComing is not set, and then + * update their UI when no more changes are imminent. + * When MoreComing is not set, that doesn't mean there will be no more + * answers EVER, just that there are no more answers immediately + * available right now at this instant. If more answers become available + * in the future they will be delivered as usual. + */ + + kDNSServiceFlagsAutoTrigger = 0x1, + /* Valid for browses using kDNSServiceInterfaceIndexAny. + * Will auto trigger the browse over AWDL as well once the service is discoveryed + * over BLE. + * This flag is an input value to DNSServiceBrowse(), which is why we can + * use the same value as kDNSServiceFlagsMoreComing, which is an output flag + * for various client callbacks. + */ + + kDNSServiceFlagsAdd = 0x2, + kDNSServiceFlagsDefault = 0x4, + /* Flags for domain enumeration and browse/query reply callbacks. + * "Default" applies only to enumeration and is only valid in + * conjunction with "Add". An enumeration callback with the "Add" + * flag NOT set indicates a "Remove", i.e. the domain is no longer + * valid. + */ + + kDNSServiceFlagsNoAutoRename = 0x8, + /* Flag for specifying renaming behavior on name conflict when registering + * non-shared records. By default, name conflicts are automatically handled + * by renaming the service. NoAutoRename overrides this behavior - with this + * flag set, name conflicts will result in a callback. The NoAutorename flag + * is only valid if a name is explicitly specified when registering a service + * (i.e. the default name is not used.) + */ + + kDNSServiceFlagsShared = 0x10, + kDNSServiceFlagsUnique = 0x20, + /* Flag for registering individual records on a connected + * DNSServiceRef. Shared indicates that there may be multiple records + * with this name on the network (e.g. PTR records). Unique indicates that the + * record's name is to be unique on the network (e.g. SRV records). + */ + + kDNSServiceFlagsBrowseDomains = 0x40, + kDNSServiceFlagsRegistrationDomains = 0x80, + /* Flags for specifying domain enumeration type in DNSServiceEnumerateDomains. + * BrowseDomains enumerates domains recommended for browsing, RegistrationDomains + * enumerates domains recommended for registration. + */ + + kDNSServiceFlagsLongLivedQuery = 0x100, + /* Flag for creating a long-lived unicast query for the DNSServiceQueryRecord call. */ + + kDNSServiceFlagsAllowRemoteQuery = 0x200, + /* Flag for creating a record for which we will answer remote queries + * (queries from hosts more than one hop away; hosts not directly connected to the local link). + */ + + kDNSServiceFlagsForceMulticast = 0x400, + /* Flag for signifying that a query or registration should be performed exclusively via multicast + * DNS, even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS. + */ + + kDNSServiceFlagsForce = 0x800, // This flag is deprecated. + + kDNSServiceFlagsKnownUnique = 0x800, + /* + * Client guarantees that record names are unique, so we can skip sending out initial + * probe messages. Standard name conflict resolution is still done if a conflict is discovered. + * Currently only valid for a DNSServiceRegister call. + */ + + kDNSServiceFlagsReturnIntermediates = 0x1000, + /* Flag for returning intermediate results. + * For example, if a query results in an authoritative NXDomain (name does not exist) + * then that result is returned to the client. However the query is not implicitly + * cancelled -- it remains active and if the answer subsequently changes + * (e.g. because a VPN tunnel is subsequently established) then that positive + * result will still be returned to the client. + * Similarly, if a query results in a CNAME record, then in addition to following + * the CNAME referral, the intermediate CNAME result is also returned to the client. + * When this flag is not set, NXDomain errors are not returned, and CNAME records + * are followed silently without informing the client of the intermediate steps. + * (In earlier builds this flag was briefly calledkDNSServiceFlagsReturnCNAME) + */ + + kDNSServiceFlagsNonBrowsable = 0x2000, + /* A service registered with the NonBrowsable flag set can be resolved using + * DNSServiceResolve(), but will not be discoverable using DNSServiceBrowse(). + * This is for cases where the name is actually a GUID; it is found by other means; + * there is no end-user benefit to browsing to find a long list of opaque GUIDs. + * Using the NonBrowsable flag creates SRV+TXT without the cost of also advertising + * an associated PTR record. + */ + + kDNSServiceFlagsShareConnection = 0x4000, + /* For efficiency, clients that perform many concurrent operations may want to use a + * single Unix Domain Socket connection with the background daemon, instead of having a + * separate connection for each independent operation. To use this mode, clients first + * call DNSServiceCreateConnection(&MainRef) to initialize the main DNSServiceRef. + * For each subsequent operation that is to share that same connection, the client copies + * the MainRef, and then passes the address of that copy, setting the ShareConnection flag + * to tell the library that this DNSServiceRef is not a typical uninitialized DNSServiceRef; + * it's a copy of an existing DNSServiceRef whose connection information should be reused. + * + * For example: + * + * DNSServiceErrorType error; + * DNSServiceRef MainRef; + * error = DNSServiceCreateConnection(&MainRef); + * if (error) ... + * DNSServiceRef BrowseRef = MainRef; // Important: COPY the primary DNSServiceRef first... + * error = DNSServiceBrowse(&BrowseRef, kDNSServiceFlagsShareConnection, ...); // then use the copy + * if (error) ... + * ... + * DNSServiceRefDeallocate(BrowseRef); // Terminate the browse operation + * DNSServiceRefDeallocate(MainRef); // Terminate the shared connection + * Also see Point 4.(Don't Double-Deallocate if the MainRef has been Deallocated) in Notes below: + * + * Notes: + * + * 1. Collective kDNSServiceFlagsMoreComing flag + * When callbacks are invoked using a shared DNSServiceRef, the + * kDNSServiceFlagsMoreComing flag applies collectively to *all* active + * operations sharing the same parent DNSServiceRef. If the MoreComing flag is + * set it means that there are more results queued on this parent DNSServiceRef, + * but not necessarily more results for this particular callback function. + * The implication of this for client programmers is that when a callback + * is invoked with the MoreComing flag set, the code should update its + * internal data structures with the new result, and set a variable indicating + * that its UI needs to be updated. Then, later when a callback is eventually + * invoked with the MoreComing flag not set, the code should update *all* + * stale UI elements related to that shared parent DNSServiceRef that need + * updating, not just the UI elements related to the particular callback + * that happened to be the last one to be invoked. + * + * 2. Canceling operations and kDNSServiceFlagsMoreComing + * Whenever you cancel any operation for which you had deferred UI updates + * waiting because of a kDNSServiceFlagsMoreComing flag, you should perform + * those deferred UI updates. This is because, after cancelling the operation, + * you can no longer wait for a callback *without* MoreComing set, to tell + * you do perform your deferred UI updates (the operation has been canceled, + * so there will be no more callbacks). An implication of the collective + * kDNSServiceFlagsMoreComing flag for shared connections is that this + * guideline applies more broadly -- any time you cancel an operation on + * a shared connection, you should perform all deferred UI updates for all + * operations sharing that connection. This is because the MoreComing flag + * might have been referring to events coming for the operation you canceled, + * which will now not be coming because the operation has been canceled. + * + * 3. Only share DNSServiceRef's created with DNSServiceCreateConnection + * Calling DNSServiceCreateConnection(&ref) creates a special shareable DNSServiceRef. + * DNSServiceRef's created by other calls like DNSServiceBrowse() or DNSServiceResolve() + * cannot be shared by copying them and using kDNSServiceFlagsShareConnection. + * + * 4. Don't Double-Deallocate if the MainRef has been Deallocated + * Calling DNSServiceRefDeallocate(ref) for a particular operation's DNSServiceRef terminates + * just that operation. Calling DNSServiceRefDeallocate(ref) for the main shared DNSServiceRef + * (the parent DNSServiceRef, originally created by DNSServiceCreateConnection(&ref)) + * automatically terminates the shared connection and all operations that were still using it. + * After doing this, DO NOT then attempt to deallocate any remaining subordinate DNSServiceRef's. + * The memory used by those subordinate DNSServiceRef's has already been freed, so any attempt + * to do a DNSServiceRefDeallocate (or any other operation) on them will result in accesses + * to freed memory, leading to crashes or other equally undesirable results. + * + * 5. Thread Safety + * The dns_sd.h API does not presuppose any particular threading model, and consequently + * does no locking internally (which would require linking with a specific threading library). + * If the client concurrently, from multiple threads (or contexts), calls API routines using + * the same DNSServiceRef, it is the client's responsibility to provide mutual exclusion for + * that DNSServiceRef. + + * For example, use of DNSServiceRefDeallocate requires caution. A common mistake is as follows: + * Thread B calls DNSServiceRefDeallocate to deallocate sdRef while Thread A is processing events + * using sdRef. Doing this will lead to intermittent crashes on thread A if the sdRef is used after + * it was deallocated. + + * A telltale sign of this crash type is to see DNSServiceProcessResult on the stack preceding the + * actual crash location. + + * To state this more explicitly, mDNSResponder does not queue DNSServiceRefDeallocate so + * that it occurs discretely before or after an event is handled. + */ + + kDNSServiceFlagsSuppressUnusable = 0x8000, + /* + * This flag is meaningful only in DNSServiceQueryRecord which suppresses unusable queries on the + * wire. If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name) + * but this host has no routable IPv6 address, then the call will not try to look up IPv6 addresses + * for "hostname", since any addresses it found would be unlikely to be of any use anyway. Similarly, + * if this host has no routable IPv4 address, the call will not try to look up IPv4 addresses for + * "hostname". + */ + + kDNSServiceFlagsTimeout = 0x10000, + /* + * When kDNServiceFlagsTimeout is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, the query is + * stopped after a certain number of seconds have elapsed. The time at which the query will be stopped + * is determined by the system and cannot be configured by the user. The query will be stopped irrespective + * of whether a response was given earlier or not. When the query is stopped, the callback will be called + * with an error code of kDNSServiceErr_Timeout and a NULL sockaddr will be returned for DNSServiceGetAddrInfo + * and zero length rdata will be returned for DNSServiceQueryRecord. + */ + + kDNSServiceFlagsIncludeP2P = 0x20000, + /* + * Include P2P interfaces when kDNSServiceInterfaceIndexAny is specified. + * By default, specifying kDNSServiceInterfaceIndexAny does not include P2P interfaces. + */ + + kDNSServiceFlagsWakeOnResolve = 0x40000, + /* + * This flag is meaningful only in DNSServiceResolve. When set, it tries to send a magic packet + * to wake up the client. + */ + + kDNSServiceFlagsBackgroundTrafficClass = 0x80000, + /* + * This flag is meaningful for Unicast DNS queries. When set, it uses the background traffic + * class for packets that service the request. + */ + + kDNSServiceFlagsIncludeAWDL = 0x100000, + /* + * Include AWDL interface when kDNSServiceInterfaceIndexAny is specified. + */ + + kDNSServiceFlagsValidate = 0x200000, + /* + * This flag is meaningful in DNSServiceGetAddrInfo and DNSServiceQueryRecord. This is the ONLY flag to be valid + * as an input to the APIs and also an output through the callbacks in the APIs. + * + * When this flag is passed to DNSServiceQueryRecord and DNSServiceGetAddrInfo to resolve unicast names, + * the response will be validated using DNSSEC. The validation results are delivered using the flags field in + * the callback and kDNSServiceFlagsValidate is marked in the flags to indicate that DNSSEC status is also available. + * When the callback is called to deliver the query results, the validation results may or may not be available. + * If it is not delivered along with the results, the validation status is delivered when the validation completes. + * + * When the validation results are delivered in the callback, it is indicated by marking the flags with + * kDNSServiceFlagsValidate and kDNSServiceFlagsAdd along with the DNSSEC status flags (described below) and a NULL + * sockaddr will be returned for DNSServiceGetAddrInfo and zero length rdata will be returned for DNSServiceQueryRecord. + * DNSSEC validation results are for the whole RRSet and not just individual records delivered in the callback. When + * kDNSServiceFlagsAdd is not set in the flags, applications should implicitly assume that the DNSSEC status of the + * RRSet that has been delivered up until that point is not valid anymore, till another callback is called with + * kDNSServiceFlagsAdd and kDNSServiceFlagsValidate. + * + * The following four flags indicate the status of the DNSSEC validation and marked in the flags field of the callback. + * When any of the four flags is set, kDNSServiceFlagsValidate will also be set. To check the validation status, the + * other applicable output flags should be masked. See kDNSServiceOutputFlags below. + */ + + kDNSServiceFlagsSecure = 0x200010, + /* + * The response has been validated by verifying all the signatures in the response and was able to + * build a successful authentication chain starting from a known trust anchor. + */ + + kDNSServiceFlagsInsecure = 0x200020, + /* + * A chain of trust cannot be built starting from a known trust anchor to the response. + */ + + kDNSServiceFlagsBogus = 0x200040, + /* + * If the response cannot be verified to be secure due to expired signatures, missing signatures etc., + * then the results are considered to be bogus. + */ + + kDNSServiceFlagsIndeterminate = 0x200080, + /* + * There is no valid trust anchor that can be used to determine whether a response is secure or not. + */ + + kDNSServiceFlagsUnicastResponse = 0x400000, + /* + * Request unicast response to query. + */ + kDNSServiceFlagsValidateOptional = 0x800000, + + /* + * This flag is identical to kDNSServiceFlagsValidate except for the case where the response + * cannot be validated. If this flag is set in DNSServiceQueryRecord or DNSServiceGetAddrInfo, + * the DNSSEC records will be requested for validation. If they cannot be received for some reason + * during the validation (e.g., zone is not signed, zone is signed but cannot be traced back to + * root, recursive server does not understand DNSSEC etc.), then this will fallback to the default + * behavior where the validation will not be performed and no DNSSEC results will be provided. + * + * If the zone is signed and there is a valid path to a known trust anchor configured in the system + * and the application requires DNSSEC validation irrespective of the DNSSEC awareness in the current + * network, then this option MUST not be used. This is only intended to be used during the transition + * period where the different nodes participating in the DNS resolution may not understand DNSSEC or + * managed properly (e.g. missing DS record) but still want to be able to resolve DNS successfully. + */ + + kDNSServiceFlagsWakeOnlyService = 0x1000000, + /* + * This flag is meaningful only in DNSServiceRegister. When set, the service will not be registered + * with sleep proxy server during sleep. + */ + + kDNSServiceFlagsThresholdOne = 0x2000000, + kDNSServiceFlagsThresholdFinder = 0x4000000, + kDNSServiceFlagsThresholdReached = kDNSServiceFlagsThresholdOne, + /* + * kDNSServiceFlagsThresholdOne is meaningful only in DNSServiceBrowse. When set, + * the system will stop issuing browse queries on the network once the number + * of answers returned is one or more. It will issue queries on the network + * again if the number of answers drops to zero. + * This flag is for Apple internal use only. Third party developers + * should not rely on this behavior being supported in any given software release. + * + * kDNSServiceFlagsThresholdFinder is meaningful only in DNSServiceBrowse. When set, + * the system will stop issuing browse queries on the network once the number + * of answers has reached the threshold set for Finder. + * It will issue queries on the network again if the number of answers drops below + * this threshold. + * This flag is for Apple internal use only. Third party developers + * should not rely on this behavior being supported in any given software release. + * + * When kDNSServiceFlagsThresholdReached is set in the client callback add or remove event, + * it indicates that the browse answer threshold has been reached and no + * browse requests will be generated on the network until the number of answers falls + * below the threshold value. Add and remove events can still occur based + * on incoming Bonjour traffic observed by the system. + * The set of services return to the client is not guaranteed to represent the + * entire set of services present on the network once the threshold has been reached. + * + * Note, while kDNSServiceFlagsThresholdReached and kDNSServiceFlagsThresholdOne + * have the same value, there isn't a conflict because kDNSServiceFlagsThresholdReached + * is only set in the callbacks and kDNSServiceFlagsThresholdOne is only set on + * input to a DNSServiceBrowse call. + */ + kDNSServiceFlagsPrivateOne = 0x8000000, + /* + * This flag is private and should not be used. + */ + + kDNSServiceFlagsPrivateTwo = 0x10000000, + /* + * This flag is private and should not be used. + */ + + kDNSServiceFlagsPrivateThree = 0x20000000, + /* + * This flag is private and should not be used. + */ + + kDNSServiceFlagsPrivateFour = 0x40000000, + /* + * This flag is private and should not be used. + */ + + kDNSServiceFlagsAllowExpiredAnswers = 0x80000000, + /* + * When kDNSServiceFlagsAllowExpiredAnswers is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, + * if there are matching expired records still in the cache, then they are immediately returned to the + * client, and in parallel a network query for that name is issued. All returned records from the query will + * remain in the cache after expiration. + */ + + kDNSServiceFlagsExpiredAnswer = 0x80000000 + /* + * When kDNSServiceFlagsAllowExpiredAnswers is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, + * an expired answer will have this flag set. + */ + +}; + +#define kDNSServiceOutputFlags (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional | kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault) + /* All the output flags excluding the DNSSEC Status flags. Typically used to check DNSSEC Status */ + +/* Possible protocol values */ +enum +{ + /* for DNSServiceGetAddrInfo() */ + kDNSServiceProtocol_IPv4 = 0x01, + kDNSServiceProtocol_IPv6 = 0x02, + /* 0x04 and 0x08 reserved for future internetwork protocols */ + + /* for DNSServiceNATPortMappingCreate() */ + kDNSServiceProtocol_UDP = 0x10, + kDNSServiceProtocol_TCP = 0x20 + /* 0x40 and 0x80 reserved for future transport protocols, e.g. SCTP [RFC 2960] + * or DCCP [RFC 4340]. If future NAT gateways are created that support port + * mappings for these protocols, new constants will be defined here. + */ +}; + +/* + * The values for DNS Classes and Types are listed in RFC 1035, and are available + * on every OS in its DNS header file. Unfortunately every OS does not have the + * same header file containing DNS Class and Type constants, and the names of + * the constants are not consistent. For example, BIND 8 uses "T_A", + * BIND 9 uses "ns_t_a", Windows uses "DNS_TYPE_A", etc. + * For this reason, these constants are also listed here, so that code using + * the DNS-SD programming APIs can use these constants, so that the same code + * can compile on all our supported platforms. + */ + +enum +{ + kDNSServiceClass_IN = 1 /* Internet */ +}; + +enum +{ + kDNSServiceType_A = 1, /* Host address. */ + kDNSServiceType_NS = 2, /* Authoritative server. */ + kDNSServiceType_MD = 3, /* Mail destination. */ + kDNSServiceType_MF = 4, /* Mail forwarder. */ + kDNSServiceType_CNAME = 5, /* Canonical name. */ + kDNSServiceType_SOA = 6, /* Start of authority zone. */ + kDNSServiceType_MB = 7, /* Mailbox domain name. */ + kDNSServiceType_MG = 8, /* Mail group member. */ + kDNSServiceType_MR = 9, /* Mail rename name. */ + kDNSServiceType_NULL = 10, /* Null resource record. */ + kDNSServiceType_WKS = 11, /* Well known service. */ + kDNSServiceType_PTR = 12, /* Domain name pointer. */ + kDNSServiceType_HINFO = 13, /* Host information. */ + kDNSServiceType_MINFO = 14, /* Mailbox information. */ + kDNSServiceType_MX = 15, /* Mail routing information. */ + kDNSServiceType_TXT = 16, /* One or more text strings (NOT "zero or more..."). */ + kDNSServiceType_RP = 17, /* Responsible person. */ + kDNSServiceType_AFSDB = 18, /* AFS cell database. */ + kDNSServiceType_X25 = 19, /* X_25 calling address. */ + kDNSServiceType_ISDN = 20, /* ISDN calling address. */ + kDNSServiceType_RT = 21, /* Router. */ + kDNSServiceType_NSAP = 22, /* NSAP address. */ + kDNSServiceType_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */ + kDNSServiceType_SIG = 24, /* Security signature. */ + kDNSServiceType_KEY = 25, /* Security key. */ + kDNSServiceType_PX = 26, /* X.400 mail mapping. */ + kDNSServiceType_GPOS = 27, /* Geographical position (withdrawn). */ + kDNSServiceType_AAAA = 28, /* IPv6 Address. */ + kDNSServiceType_LOC = 29, /* Location Information. */ + kDNSServiceType_NXT = 30, /* Next domain (security). */ + kDNSServiceType_EID = 31, /* Endpoint identifier. */ + kDNSServiceType_NIMLOC = 32, /* Nimrod Locator. */ + kDNSServiceType_SRV = 33, /* Server Selection. */ + kDNSServiceType_ATMA = 34, /* ATM Address */ + kDNSServiceType_NAPTR = 35, /* Naming Authority PoinTeR */ + kDNSServiceType_KX = 36, /* Key Exchange */ + kDNSServiceType_CERT = 37, /* Certification record */ + kDNSServiceType_A6 = 38, /* IPv6 Address (deprecated) */ + kDNSServiceType_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ + kDNSServiceType_SINK = 40, /* Kitchen sink (experimental) */ + kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */ + kDNSServiceType_APL = 42, /* Address Prefix List */ + kDNSServiceType_DS = 43, /* Delegation Signer */ + kDNSServiceType_SSHFP = 44, /* SSH Key Fingerprint */ + kDNSServiceType_IPSECKEY = 45, /* IPSECKEY */ + kDNSServiceType_RRSIG = 46, /* RRSIG */ + kDNSServiceType_NSEC = 47, /* Denial of Existence */ + kDNSServiceType_DNSKEY = 48, /* DNSKEY */ + kDNSServiceType_DHCID = 49, /* DHCP Client Identifier */ + kDNSServiceType_NSEC3 = 50, /* Hashed Authenticated Denial of Existence */ + kDNSServiceType_NSEC3PARAM = 51, /* Hashed Authenticated Denial of Existence */ + + kDNSServiceType_HIP = 55, /* Host Identity Protocol */ + + kDNSServiceType_SPF = 99, /* Sender Policy Framework for E-Mail */ + kDNSServiceType_UINFO = 100, /* IANA-Reserved */ + kDNSServiceType_UID = 101, /* IANA-Reserved */ + kDNSServiceType_GID = 102, /* IANA-Reserved */ + kDNSServiceType_UNSPEC = 103, /* IANA-Reserved */ + + kDNSServiceType_TKEY = 249, /* Transaction key */ + kDNSServiceType_TSIG = 250, /* Transaction signature. */ + kDNSServiceType_IXFR = 251, /* Incremental zone transfer. */ + kDNSServiceType_AXFR = 252, /* Transfer zone of authority. */ + kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */ + kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */ + kDNSServiceType_ANY = 255 /* Wildcard match. */ +}; + +/* possible error code values */ +enum +{ + kDNSServiceErr_NoError = 0, + kDNSServiceErr_Unknown = -65537, /* 0xFFFE FFFF */ + kDNSServiceErr_NoSuchName = -65538, + kDNSServiceErr_NoMemory = -65539, + kDNSServiceErr_BadParam = -65540, + kDNSServiceErr_BadReference = -65541, + kDNSServiceErr_BadState = -65542, + kDNSServiceErr_BadFlags = -65543, + kDNSServiceErr_Unsupported = -65544, + kDNSServiceErr_NotInitialized = -65545, + kDNSServiceErr_AlreadyRegistered = -65547, + kDNSServiceErr_NameConflict = -65548, + kDNSServiceErr_Invalid = -65549, + kDNSServiceErr_Firewall = -65550, + kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */ + kDNSServiceErr_BadInterfaceIndex = -65552, + kDNSServiceErr_Refused = -65553, + kDNSServiceErr_NoSuchRecord = -65554, + kDNSServiceErr_NoAuth = -65555, + kDNSServiceErr_NoSuchKey = -65556, + kDNSServiceErr_NATTraversal = -65557, + kDNSServiceErr_DoubleNAT = -65558, + kDNSServiceErr_BadTime = -65559, /* Codes up to here existed in Tiger */ + kDNSServiceErr_BadSig = -65560, + kDNSServiceErr_BadKey = -65561, + kDNSServiceErr_Transient = -65562, + kDNSServiceErr_ServiceNotRunning = -65563, /* Background daemon not running */ + kDNSServiceErr_NATPortMappingUnsupported = -65564, /* NAT doesn't support PCP, NAT-PMP or UPnP */ + kDNSServiceErr_NATPortMappingDisabled = -65565, /* NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator */ + kDNSServiceErr_NoRouter = -65566, /* No router currently configured (probably no network connectivity) */ + kDNSServiceErr_PollingMode = -65567, + kDNSServiceErr_Timeout = -65568 + + /* mDNS Error codes are in the range + * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ +}; + +/* Maximum length, in bytes, of a service name represented as a */ +/* literal C-String, including the terminating NULL at the end. */ + +#define kDNSServiceMaxServiceName 64 + +/* Maximum length, in bytes, of a domain name represented as an *escaped* C-String */ +/* including the final trailing dot, and the C-String terminating NULL at the end. */ + +#define kDNSServiceMaxDomainName 1009 + +/* + * Notes on DNS Name Escaping + * -- or -- + * "Why is kDNSServiceMaxDomainName 1009, when the maximum legal domain name is 256 bytes?" + * + * All strings used in the DNS-SD APIs are UTF-8 strings. + * Apart from the exceptions noted below, the APIs expect the strings to be properly escaped, using the + * conventional DNS escaping rules, as used by the traditional DNS res_query() API, as described below: + * + * Generally all UTF-8 characters (which includes all US ASCII characters) represent themselves, + * with two exceptions, the dot ('.') character, which is the label separator, + * and the backslash ('\') character, which is the escape character. + * The escape character ('\') is interpreted as described below: + * + * '\ddd', where ddd is a three-digit decimal value from 000 to 255, + * represents a single literal byte with that value. Any byte value may be + * represented in '\ddd' format, even characters that don't strictly need to be escaped. + * For example, the ASCII code for 'w' is 119, and therefore '\119' is equivalent to 'w'. + * Thus the command "ping '\119\119\119.apple.com'" is the equivalent to the command "ping 'www.apple.com'". + * Nonprinting ASCII characters in the range 0-31 are often represented this way. + * In particular, the ASCII NUL character (0) cannot appear in a C string because C uses it as the + * string terminator character, so ASCII NUL in a domain name has to be represented in a C string as '\000'. + * Other characters like space (ASCII code 32) are sometimes represented as '\032' + * in contexts where having an actual space character in a C string would be inconvenient. + * + * Otherwise, for all cases where a '\' is followed by anything other than a three-digit decimal value + * from 000 to 255, the character sequence '\x' represents a single literal occurrence of character 'x'. + * This is legal for any character, so, for example, '\w' is equivalent to 'w'. + * Thus the command "ping '\w\w\w.apple.com'" is the equivalent to the command "ping 'www.apple.com'". + * However, this encoding is most useful when representing the characters '.' and '\', + * which otherwise would have special meaning in DNS name strings. + * This means that the following encodings are particularly common: + * '\\' represents a single literal '\' in the name + * '\.' represents a single literal '.' in the name + * + * A lone escape character ('\') appearing at the end of a string is not allowed, since it is + * followed by neither a three-digit decimal value from 000 to 255 nor a single character. + * If a lone escape character ('\') does appear as the last character of a string, it is silently ignored. + * + * The exceptions, that do not use escaping, are the routines where the full + * DNS name of a resource is broken, for convenience, into servicename/regtype/domain. + * In these routines, the "servicename" is NOT escaped. It does not need to be, since + * it is, by definition, just a single literal string. Any characters in that string + * represent exactly what they are. The "regtype" portion is, technically speaking, + * escaped, but since legal regtypes are only allowed to contain US ASCII letters, + * digits, and hyphens, there is nothing to escape, so the issue is moot. + * The "domain" portion is also escaped, though most domains in use on the public + * Internet today, like regtypes, don't contain any characters that need to be escaped. + * As DNS-SD becomes more popular, rich-text domains for service discovery will + * become common, so software should be written to cope with domains with escaping. + * + * The servicename may be up to 63 bytes of UTF-8 text (not counting the C-String + * terminating NULL at the end). The regtype is of the form _service._tcp or + * _service._udp, where the "service" part is 1-15 characters, which may be + * letters, digits, or hyphens. The domain part of the three-part name may be + * any legal domain, providing that the resulting servicename+regtype+domain + * name does not exceed 256 bytes. + * + * For most software, these issues are transparent. When browsing, the discovered + * servicenames should simply be displayed as-is. When resolving, the discovered + * servicename/regtype/domain are simply passed unchanged to DNSServiceResolve(). + * When a DNSServiceResolve() succeeds, the returned fullname is already in + * the correct format to pass to standard system DNS APIs such as res_query(). + * For converting from servicename/regtype/domain to a single properly-escaped + * full DNS name, the helper function DNSServiceConstructFullName() is provided. + * + * The following (highly contrived) example illustrates the escaping process. + * Suppose you have a service called "Dr. Smith\Dr. Johnson", of type "_ftp._tcp" + * in subdomain "4th. Floor" of subdomain "Building 2" of domain "apple.com." + * The full (escaped) DNS name of this service's SRV record would be: + * Dr\.\032Smith\\Dr\.\032Johnson._ftp._tcp.4th\.\032Floor.Building\0322.apple.com. + */ + + +/* + * Constants for specifying an interface index + * + * Specific interface indexes are identified via a 32-bit unsigned integer returned + * by the if_nametoindex() family of calls. + * + * If the client passes 0 for interface index, that means "do the right thing", + * which (at present) means, "if the name is in an mDNS local multicast domain + * (e.g. 'local.', '254.169.in-addr.arpa.', '{8,9,A,B}.E.F.ip6.arpa.') then multicast + * on all applicable interfaces, otherwise send via unicast to the appropriate + * DNS server." Normally, most clients will use 0 for interface index to + * automatically get the default sensible behaviour. + * + * If the client passes a positive interface index, then that indicates to do the + * operation only on that one specified interface. + * + * If the client passes kDNSServiceInterfaceIndexLocalOnly when registering + * a service, then that service will be found *only* by other local clients + * on the same machine that are browsing using kDNSServiceInterfaceIndexLocalOnly + * or kDNSServiceInterfaceIndexAny. + * If a client has a 'private' service, accessible only to other processes + * running on the same machine, this allows the client to advertise that service + * in a way such that it does not inadvertently appear in service lists on + * all the other machines on the network. + * + * If the client passes kDNSServiceInterfaceIndexLocalOnly when querying or + * browsing, then the LocalOnly authoritative records and /etc/hosts caches + * are searched and will find *all* records registered or configured on that + * same local machine. + * + * If interested in getting negative answers to local questions while querying + * or browsing, then set both the kDNSServiceInterfaceIndexLocalOnly and the + * kDNSServiceFlagsReturnIntermediates flags. If no local answers exist at this + * moment in time, then the reply will return an immediate negative answer. If + * local records are subsequently created that answer the question, then those + * answers will be delivered, for as long as the question is still active. + * + * If the kDNSServiceFlagsTimeout and kDNSServiceInterfaceIndexLocalOnly flags + * are set simultaneously when either DNSServiceQueryRecord or DNSServiceGetAddrInfo + * is called then both flags take effect. However, if DNSServiceQueryRecord is called + * with both the kDNSServiceFlagsSuppressUnusable and kDNSServiceInterfaceIndexLocalOnly + * flags set, then the kDNSServiceFlagsSuppressUnusable flag is ignored. + * + * Clients explicitly wishing to discover *only* LocalOnly services during a + * browse may do this, without flags, by inspecting the interfaceIndex of each + * service reported to a DNSServiceBrowseReply() callback function, and + * discarding those answers where the interface index is not set to + * kDNSServiceInterfaceIndexLocalOnly. + * + * kDNSServiceInterfaceIndexP2P is meaningful only in Browse, QueryRecord, Register, + * and Resolve operations. It should not be used in other DNSService APIs. + * + * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceBrowse or + * DNSServiceQueryRecord, it restricts the operation to P2P. + * + * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceRegister, it is + * mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P + * set. + * + * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceResolve, it is + * mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P + * set, because resolving a P2P service may create and/or enable an interface whose + * index is not known a priori. The resolve callback will indicate the index of the + * interface via which the service can be accessed. + * + * If applications pass kDNSServiceInterfaceIndexAny to DNSServiceBrowse + * or DNSServiceQueryRecord, they must set the kDNSServiceFlagsIncludeP2P flag + * to include P2P. In this case, if a service instance or the record being queried + * is found over P2P, the resulting ADD event will indicate kDNSServiceInterfaceIndexP2P + * as the interface index. + */ + +#define kDNSServiceInterfaceIndexAny 0 +#define kDNSServiceInterfaceIndexLocalOnly ((uint32_t)-1) +#define kDNSServiceInterfaceIndexUnicast ((uint32_t)-2) +#define kDNSServiceInterfaceIndexP2P ((uint32_t)-3) +#define kDNSServiceInterfaceIndexBLE ((uint32_t)-4) + +typedef uint32_t DNSServiceFlags; +typedef uint32_t DNSServiceProtocol; +typedef int32_t DNSServiceErrorType; + + +/********************************************************************************************* +* +* Version checking +* +*********************************************************************************************/ + +/* DNSServiceGetProperty() Parameters: + * + * property: The requested property. + * Currently the only property defined is kDNSServiceProperty_DaemonVersion. + * + * result: Place to store result. + * For retrieving DaemonVersion, this should be the address of a uint32_t. + * + * size: Pointer to uint32_t containing size of the result location. + * For retrieving DaemonVersion, this should be sizeof(uint32_t). + * On return the uint32_t is updated to the size of the data returned. + * For DaemonVersion, the returned size is always sizeof(uint32_t), but + * future properties could be defined which return variable-sized results. + * + * return value: Returns kDNSServiceErr_NoError on success, or kDNSServiceErr_ServiceNotRunning + * if the daemon (or "system service" on Windows) is not running. + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceGetProperty +( + const char *property, /* Requested property (i.e. kDNSServiceProperty_DaemonVersion) */ + void *result, /* Pointer to place to store result */ + uint32_t *size /* size of result location */ +); + +/* + * When requesting kDNSServiceProperty_DaemonVersion, the result pointer must point + * to a 32-bit unsigned integer, and the size parameter must be set to sizeof(uint32_t). + * + * On return, the 32-bit unsigned integer contains the API version number + * + * For example, Mac OS X 10.4.9 has API version 1080400. + * This allows applications to do simple greater-than and less-than comparisons: + * e.g. an application that requires at least API version 1080400 can check: + * if (version >= 1080400) ... + * + * Example usage: + * uint32_t version; + * uint32_t size = sizeof(version); + * DNSServiceErrorType err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &version, &size); + * if (!err) printf("DNS_SD API version is %d.%d\n", version / 10000, version / 100 % 100); + */ + +#define kDNSServiceProperty_DaemonVersion "DaemonVersion" + +/********************************************************************************************* +* +* Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions +* +*********************************************************************************************/ + +/* DNSServiceRefSockFD() + * + * Access underlying Unix domain socket for an initialized DNSServiceRef. + * The DNS Service Discovery implementation uses this socket to communicate between the client and + * the daemon. The application MUST NOT directly read from or write to this socket. + * Access to the socket is provided so that it can be used as a kqueue event source, a CFRunLoop + * event source, in a select() loop, etc. When the underlying event management subsystem (kqueue/ + * select/CFRunLoop etc.) indicates to the client that data is available for reading on the + * socket, the client should call DNSServiceProcessResult(), which will extract the daemon's + * reply from the socket, and pass it to the appropriate application callback. By using a run + * loop or select(), results from the daemon can be processed asynchronously. Alternatively, + * a client can choose to fork a thread and have it loop calling "DNSServiceProcessResult(ref);" + * If DNSServiceProcessResult() is called when no data is available for reading on the socket, it + * will block until data does become available, and then process the data and return to the caller. + * The application is responsible for checking the return value of DNSServiceProcessResult() + * to determine if the socket is valid and if it should continue to process data on the socket. + * When data arrives on the socket, the client is responsible for calling DNSServiceProcessResult(ref) + * in a timely fashion -- if the client allows a large backlog of data to build up the daemon + * may terminate the connection. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls. + * + * return value: The DNSServiceRef's underlying socket descriptor, or -1 on + * error. + */ + +DNSSD_EXPORT +dnssd_sock_t DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef); + + +/* DNSServiceProcessResult() + * + * Read a reply from the daemon, calling the appropriate application callback. This call will + * block until the daemon's response is received. Use DNSServiceRefSockFD() in + * conjunction with a run loop or select() to determine the presence of a response from the + * server before calling this function to process the reply without blocking. Call this function + * at any point if it is acceptable to block until the daemon's response arrives. Note that the + * client is responsible for ensuring that DNSServiceProcessResult() is called whenever there is + * a reply from the daemon - the daemon may terminate its connection with a client that does not + * process the daemon's responses. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls + * that take a callback parameter. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns + * an error code indicating the specific failure that occurred. + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); + + +/* DNSServiceRefDeallocate() + * + * Terminate a connection with the daemon and free memory associated with the DNSServiceRef. + * Any services or records registered with this DNSServiceRef will be deregistered. Any + * Browse, Resolve, or Query operations called with this reference will be terminated. + * + * Note: If the reference's underlying socket is used in a run loop or select() call, it should + * be removed BEFORE DNSServiceRefDeallocate() is called, as this function closes the reference's + * socket. + * + * Note: If the reference was initialized with DNSServiceCreateConnection(), any DNSRecordRefs + * created via this reference will be invalidated by this call - the resource records are + * deregistered, and their DNSRecordRefs may not be used in subsequent functions. Similarly, + * if the reference was initialized with DNSServiceRegister, and an extra resource record was + * added to the service via DNSServiceAddRecord(), the DNSRecordRef created by the Add() call + * is invalidated when this function is called - the DNSRecordRef may not be used in subsequent + * functions. + * + * Note: This call is to be used only with the DNSServiceRef defined by this API. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls. + * + */ + +DNSSD_EXPORT +void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); + + +/********************************************************************************************* +* +* Domain Enumeration +* +*********************************************************************************************/ + +/* DNSServiceEnumerateDomains() + * + * Asynchronously enumerate domains available for browsing and registration. + * + * The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains + * are to be found. + * + * Note that the names returned are (like all of DNS-SD) UTF-8 strings, + * and are escaped using standard DNS escaping rules. + * (See "Notes on DNS Name Escaping" earlier in this file for more details.) + * A graphical browser displaying a hierarchical tree-structured view should cut + * the names at the bare dots to yield individual labels, then de-escape each + * label according to the escaping rules, and then display the resulting UTF-8 text. + * + * DNSServiceDomainEnumReply Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceEnumerateDomains(). + * + * flags: Possible values are: + * kDNSServiceFlagsMoreComing + * kDNSServiceFlagsAdd + * kDNSServiceFlagsDefault + * + * interfaceIndex: Specifies the interface on which the domain exists. (The index for a given + * interface is determined via the if_nametoindex() family of calls.) + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise indicates + * the failure that occurred (other parameters are undefined if errorCode is nonzero). + * + * replyDomain: The name of the domain. + * + * context: The context pointer passed to DNSServiceEnumerateDomains. + * + */ + +typedef void (DNSSD_API *DNSServiceDomainEnumReply) +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *replyDomain, + void *context +); + + +/* DNSServiceEnumerateDomains() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the enumeration operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Possible values are: + * kDNSServiceFlagsBrowseDomains to enumerate domains recommended for browsing. + * kDNSServiceFlagsRegistrationDomains to enumerate domains recommended + * for registration. + * + * interfaceIndex: If non-zero, specifies the interface on which to look for domains. + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to enumerate domains on + * all interfaces. See "Constants for specifying an interface index" for more details. + * + * callBack: The function to be called when a domain is found or the call asynchronously + * fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is not invoked and the DNSServiceRef + * is not initialized). + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callBack, + void *context /* may be NULL */ +); + + +/********************************************************************************************* +* +* Service Registration +* +*********************************************************************************************/ + +/* Register a service that is discovered via Browse() and Resolve() calls. + * + * DNSServiceRegisterReply() Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceRegister(). + * + * flags: When a name is successfully registered, the callback will be + * invoked with the kDNSServiceFlagsAdd flag set. When Wide-Area + * DNS-SD is in use, it is possible for a single service to get + * more than one success callback (e.g. one in the "local" multicast + * DNS domain, and another in a wide-area unicast DNS domain). + * If a successfully-registered name later suffers a name conflict + * or similar problem and has to be deregistered, the callback will + * be invoked with the kDNSServiceFlagsAdd flag not set. The callback + * is *not* invoked in the case where the caller explicitly terminates + * the service registration by calling DNSServiceRefDeallocate(ref); + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred (including name conflicts, + * if the kDNSServiceFlagsNoAutoRename flag was used when registering.) + * Other parameters are undefined if errorCode is nonzero. + * + * name: The service name registered (if the application did not specify a name in + * DNSServiceRegister(), this indicates what name was automatically chosen). + * + * regtype: The type of service registered, as it was passed to the callout. + * + * domain: The domain on which the service was registered (if the application did not + * specify a domain in DNSServiceRegister(), this indicates the default domain + * on which the service was registered). + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceRegisterReply) +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char *name, + const char *regtype, + const char *domain, + void *context +); + + +/* DNSServiceRegister() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the registration will remain active indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Indicates the renaming behavior on name conflict (most applications + * will pass 0). See flag definitions above for details. + * + * interfaceIndex: If non-zero, specifies the interface on which to register the service + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to register on all + * available interfaces. See "Constants for specifying an interface index" for more details. + * + * name: If non-NULL, specifies the service name to be registered. + * Most applications will not specify a name, in which case the computer + * name is used (this name is communicated to the client via the callback). + * If a name is specified, it must be 1-63 bytes of UTF-8 text. + * If the name is longer than 63 bytes it will be automatically truncated + * to a legal length, unless the NoAutoRename flag is set, + * in which case kDNSServiceErr_BadParam will be returned. + * + * regtype: The service type followed by the protocol, separated by a dot + * (e.g. "_ftp._tcp"). The service type must be an underscore, followed + * by 1-15 characters, which may be letters, digits, or hyphens. + * The transport protocol must be "_tcp" or "_udp". New service types + * should be registered at . + * + * Additional subtypes of the primary service type (where a service + * type has defined subtypes) follow the primary service type in a + * comma-separated list, with no additional spaces, e.g. + * "_primarytype._tcp,_subtype1,_subtype2,_subtype3" + * Subtypes provide a mechanism for filtered browsing: A client browsing + * for "_primarytype._tcp" will discover all instances of this type; + * a client browsing for "_primarytype._tcp,_subtype2" will discover only + * those instances that were registered with "_subtype2" in their list of + * registered subtypes. + * + * The subtype mechanism can be illustrated with some examples using the + * dns-sd command-line tool: + * + * % dns-sd -R Simple _test._tcp "" 1001 & + * % dns-sd -R Better _test._tcp,HasFeatureA "" 1002 & + * % dns-sd -R Best _test._tcp,HasFeatureA,HasFeatureB "" 1003 & + * + * Now: + * % dns-sd -B _test._tcp # will find all three services + * % dns-sd -B _test._tcp,HasFeatureA # finds "Better" and "Best" + * % dns-sd -B _test._tcp,HasFeatureB # finds only "Best" + * + * Subtype labels may be up to 63 bytes long, and may contain any eight- + * bit byte values, including zero bytes. However, due to the nature of + * using a C-string-based API, conventional DNS escaping must be used for + * dots ('.'), commas (','), backslashes ('\') and zero bytes, as shown below: + * + * % dns-sd -R Test '_test._tcp,s\.one,s\,two,s\\three,s\000four' local 123 + * + * When a service is registered, all the clients browsing for the registered + * type ("regtype") will discover it. If the discovery should be + * restricted to a smaller set of well known peers, the service can be + * registered with additional data (group identifier) that is known + * only to a smaller set of peers. The group identifier should follow primary + * service type using a colon (":") as a delimeter. If subtypes are also present, + * it should be given before the subtype as shown below. + * + * % dns-sd -R _test1 _http._tcp:mygroup1 local 1001 + * % dns-sd -R _test2 _http._tcp:mygroup2 local 1001 + * % dns-sd -R _test3 _http._tcp:mygroup3,HasFeatureA local 1001 + * + * Now: + * % dns-sd -B _http._tcp:"mygroup1" # will discover only test1 + * % dns-sd -B _http._tcp:"mygroup2" # will discover only test2 + * % dns-sd -B _http._tcp:"mygroup3",HasFeatureA # will discover only test3 + * + * By specifying the group information, only the members of that group are + * discovered. + * + * The group identifier itself is not sent in clear. Only a hash of the group + * identifier is sent and the clients discover them anonymously. The group identifier + * may be up to 256 bytes long and may contain any eight bit values except comma which + * should be escaped. + * + * domain: If non-NULL, specifies the domain on which to advertise the service. + * Most applications will not specify a domain, instead automatically + * registering in the default domain(s). + * + * host: If non-NULL, specifies the SRV target host name. Most applications + * will not specify a host, instead automatically using the machine's + * default host name(s). Note that specifying a non-NULL host does NOT + * create an address record for that host - the application is responsible + * for ensuring that the appropriate address record exists, or creating it + * via DNSServiceRegisterRecord(). + * + * port: The port, in network byte order, on which the service accepts connections. + * Pass 0 for a "placeholder" service (i.e. a service that will not be discovered + * by browsing, but will cause a name conflict if another client tries to + * register that same name). Most clients will not use placeholder services. + * + * txtLen: The length of the txtRecord, in bytes. Must be zero if the txtRecord is NULL. + * + * txtRecord: The TXT record rdata. A non-NULL txtRecord MUST be a properly formatted DNS + * TXT record, i.e. ... + * Passing NULL for the txtRecord is allowed as a synonym for txtLen=1, txtRecord="", + * i.e. it creates a TXT record of length one containing a single empty string. + * RFC 1035 doesn't allow a TXT record to contain *zero* strings, so a single empty + * string is the smallest legal DNS TXT record. + * As with the other parameters, the DNSServiceRegister call copies the txtRecord + * data; e.g. if you allocated the storage for the txtRecord parameter with malloc() + * then you can safely free that memory right after the DNSServiceRegister call returns. + * + * callBack: The function to be called when the registration completes or asynchronously + * fails. The client MAY pass NULL for the callback - The client will NOT be notified + * of the default values picked on its behalf, and the client will NOT be notified of any + * asynchronous errors (e.g. out of memory errors, etc.) that may prevent the registration + * of the service. The client may NOT pass the NoAutoRename flag if the callback is NULL. + * The client may still deregister the service at any time via DNSServiceRefDeallocate(). + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized). + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceRegister +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, /* may be NULL */ + const char *regtype, + const char *domain, /* may be NULL */ + const char *host, /* may be NULL */ + uint16_t port, /* In network byte order */ + uint16_t txtLen, + const void *txtRecord, /* may be NULL */ + DNSServiceRegisterReply callBack, /* may be NULL */ + void *context /* may be NULL */ +); + + +/* DNSServiceAddRecord() + * + * Add a record to a registered service. The name of the record will be the same as the + * registered service's name. + * The record can later be updated or deregistered by passing the RecordRef initialized + * by this function to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * + * Note that the DNSServiceAddRecord/UpdateRecord/RemoveRecord are *NOT* thread-safe + * with respect to a single DNSServiceRef. If you plan to have multiple threads + * in your program simultaneously add, update, or remove records from the same + * DNSServiceRef, then it's the caller's responsibility to use a mutex lock + * or take similar appropriate precautions to serialize those calls. + * + * Parameters; + * + * sdRef: A DNSServiceRef initialized by DNSServiceRegister(). + * + * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this + * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * If the above DNSServiceRef is passed to DNSServiceRefDeallocate(), RecordRef is also + * invalidated and may not be used further. + * + * flags: Currently ignored, reserved for future use. + * + * rrtype: The type of the record (e.g. kDNSServiceType_TXT, kDNSServiceType_SRV, etc) + * + * rdlen: The length, in bytes, of the rdata. + * + * rdata: The raw rdata to be contained in the added resource record. + * + * ttl: The time to live of the resource record, in seconds. + * Most clients should pass 0 to indicate that the system should + * select a sensible default value. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred (the RecordRef is not initialized). + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceAddRecord +( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, + const void *rdata, + uint32_t ttl +); + + +/* DNSServiceUpdateRecord + * + * Update a registered resource record. The record must either be: + * - The primary txt record of a service registered via DNSServiceRegister() + * - A record added to a registered service via DNSServiceAddRecord() + * - An individual record registered by DNSServiceRegisterRecord() + * + * Parameters: + * + * sdRef: A DNSServiceRef that was initialized by DNSServiceRegister() + * or DNSServiceCreateConnection(). + * + * RecordRef: A DNSRecordRef initialized by DNSServiceAddRecord, or NULL to update the + * service's primary txt record. + * + * flags: Currently ignored, reserved for future use. + * + * rdlen: The length, in bytes, of the new rdata. + * + * rdata: The new rdata to be contained in the updated resource record. + * + * ttl: The time to live of the updated resource record, in seconds. + * Most clients should pass 0 to indicate that the system should + * select a sensible default value. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred. + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord +( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, /* may be NULL */ + DNSServiceFlags flags, + uint16_t rdlen, + const void *rdata, + uint32_t ttl +); + + +/* DNSServiceRemoveRecord + * + * Remove a record previously added to a service record set via DNSServiceAddRecord(), or deregister + * a record registered individually via DNSServiceRegisterRecord(). + * + * Parameters: + * + * sdRef: A DNSServiceRef initialized by DNSServiceRegister() (if the + * record being removed was registered via DNSServiceAddRecord()) or by + * DNSServiceCreateConnection() (if the record being removed was registered via + * DNSServiceRegisterRecord()). + * + * recordRef: A DNSRecordRef initialized by a successful call to DNSServiceAddRecord() + * or DNSServiceRegisterRecord(). + * + * flags: Currently ignored, reserved for future use. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred. + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord +( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags +); + + +/********************************************************************************************* +* +* Service Discovery +* +*********************************************************************************************/ + +/* Browse for instances of a service. + * + * DNSServiceBrowseReply() Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceBrowse(). + * + * flags: Possible values are kDNSServiceFlagsMoreComing and kDNSServiceFlagsAdd. + * See flag definitions for details. + * + * interfaceIndex: The interface on which the service is advertised. This index should + * be passed to DNSServiceResolve() when resolving the service. + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * the errorCode is nonzero. + * + * serviceName: The discovered service name. This name should be displayed to the user, + * and stored for subsequent use in the DNSServiceResolve() call. + * + * regtype: The service type, which is usually (but not always) the same as was passed + * to DNSServiceBrowse(). One case where the discovered service type may + * not be the same as the requested service type is when using subtypes: + * The client may want to browse for only those ftp servers that allow + * anonymous connections. The client will pass the string "_ftp._tcp,_anon" + * to DNSServiceBrowse(), but the type of the service that's discovered + * is simply "_ftp._tcp". The regtype for each discovered service instance + * should be stored along with the name, so that it can be passed to + * DNSServiceResolve() when the service is later resolved. + * + * domain: The domain of the discovered service instance. This may or may not be the + * same as the domain that was passed to DNSServiceBrowse(). The domain for each + * discovered service instance should be stored along with the name, so that + * it can be passed to DNSServiceResolve() when the service is later resolved. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceBrowseReply) +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *serviceName, + const char *regtype, + const char *replyDomain, + void *context +); + + +/* DNSServiceBrowse() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the browse operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Currently ignored, reserved for future use. + * + * interfaceIndex: If non-zero, specifies the interface on which to browse for services + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to browse on all available + * interfaces. See "Constants for specifying an interface index" for more details. + * + * regtype: The service type being browsed for followed by the protocol, separated by a + * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + * A client may optionally specify a single subtype to perform filtered browsing: + * e.g. browsing for "_primarytype._tcp,_subtype" will discover only those + * instances of "_primarytype._tcp" that were registered specifying "_subtype" + * in their list of registered subtypes. Additionally, a group identifier may + * also be specified before the subtype e.g., _primarytype._tcp:GroupID, which + * will discover only the members that register the service with GroupID. See + * DNSServiceRegister for more details. + * + * domain: If non-NULL, specifies the domain on which to browse for services. + * Most applications will not specify a domain, instead browsing on the + * default domain(s). + * + * callBack: The function to be called when an instance of the service being browsed for + * is found, or if the call asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is not invoked and the DNSServiceRef + * is not initialized). + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceBrowse +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *regtype, + const char *domain, /* may be NULL */ + DNSServiceBrowseReply callBack, + void *context /* may be NULL */ +); + + +/* DNSServiceResolve() + * + * Resolve a service name discovered via DNSServiceBrowse() to a target host name, port number, and + * txt record. + * + * Note: Applications should NOT use DNSServiceResolve() solely for txt record monitoring - use + * DNSServiceQueryRecord() instead, as it is more efficient for this task. + * + * Note: When the desired results have been returned, the client MUST terminate the resolve by calling + * DNSServiceRefDeallocate(). + * + * Note: DNSServiceResolve() behaves correctly for typical services that have a single SRV record + * and a single TXT record. To resolve non-standard services with multiple SRV or TXT records, + * DNSServiceQueryRecord() should be used. + * + * DNSServiceResolveReply Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceResolve(). + * + * flags: Possible values: kDNSServiceFlagsMoreComing + * + * interfaceIndex: The interface on which the service was resolved. + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * the errorCode is nonzero. + * + * fullname: The full service domain name, in the form ... + * (This name is escaped following standard DNS rules, making it suitable for + * passing to standard system DNS APIs such as res_query(), or to the + * special-purpose functions included in this API that take fullname parameters. + * See "Notes on DNS Name Escaping" earlier in this file for more details.) + * + * hosttarget: The target hostname of the machine providing the service. This name can + * be passed to functions like gethostbyname() to identify the host's IP address. + * + * port: The port, in network byte order, on which connections are accepted for this service. + * + * txtLen: The length of the txt record, in bytes. + * + * txtRecord: The service's primary txt record, in standard txt record format. + * + * context: The context pointer that was passed to the callout. + * + * NOTE: In earlier versions of this header file, the txtRecord parameter was declared "const char *" + * This is incorrect, since it contains length bytes which are values in the range 0 to 255, not -128 to +127. + * Depending on your compiler settings, this change may cause signed/unsigned mismatch warnings. + * These should be fixed by updating your own callback function definition to match the corrected + * function signature using "const unsigned char *txtRecord". Making this change may also fix inadvertent + * bugs in your callback function, where it could have incorrectly interpreted a length byte with value 250 + * as being -6 instead, with various bad consequences ranging from incorrect operation to software crashes. + * If you need to maintain portable code that will compile cleanly with both the old and new versions of + * this header file, you should update your callback function definition to use the correct unsigned value, + * and then in the place where you pass your callback function to DNSServiceResolve(), use a cast to eliminate + * the compiler warning, e.g.: + * DNSServiceResolve(sd, flags, index, name, regtype, domain, (DNSServiceResolveReply)MyCallback, context); + * This will ensure that your code compiles cleanly without warnings (and more importantly, works correctly) + * with both the old header and with the new corrected version. + * + */ + +typedef void (DNSSD_API *DNSServiceResolveReply) +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *fullname, + const char *hosttarget, + uint16_t port, /* In network byte order */ + uint16_t txtLen, + const unsigned char *txtRecord, + void *context +); + + +/* DNSServiceResolve() Parameters + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the resolve operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Specifying kDNSServiceFlagsForceMulticast will cause query to be + * performed with a link-local mDNS query, even if the name is an + * apparently non-local name (i.e. a name not ending in ".local.") + * + * interfaceIndex: The interface on which to resolve the service. If this resolve call is + * as a result of a currently active DNSServiceBrowse() operation, then the + * interfaceIndex should be the index reported in the DNSServiceBrowseReply + * callback. If this resolve call is using information previously saved + * (e.g. in a preference file) for later use, then use interfaceIndex 0, because + * the desired service may now be reachable via a different physical interface. + * See "Constants for specifying an interface index" for more details. + * + * name: The name of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. + * + * regtype: The type of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. + * + * domain: The domain of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized). + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceResolve +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + DNSServiceResolveReply callBack, + void *context /* may be NULL */ +); + + +/********************************************************************************************* +* +* Querying Individual Specific Records +* +*********************************************************************************************/ + +/* DNSServiceQueryRecord + * + * Query for an arbitrary DNS record. + * + * DNSServiceQueryRecordReply() Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceQueryRecord(). + * + * flags: Possible values are kDNSServiceFlagsMoreComing and + * kDNSServiceFlagsAdd. The Add flag is NOT set for PTR records + * with a ttl of 0, i.e. "Remove" events. + * + * interfaceIndex: The interface on which the query was resolved (the index for a given + * interface is determined via the if_nametoindex() family of calls). + * See "Constants for specifying an interface index" for more details. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * errorCode is nonzero. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * rdlen: The length, in bytes, of the resource record rdata. + * + * rdata: The raw rdata of the resource record. + * + * ttl: If the client wishes to cache the result for performance reasons, + * the TTL indicates how long the client may legitimately hold onto + * this result, in seconds. After the TTL expires, the client should + * consider the result no longer valid, and if it requires this data + * again, it should be re-fetched with a new query. Of course, this + * only applies to clients that cancel the asynchronous operation when + * they get a result. Clients that leave the asynchronous operation + * running can safely assume that the data remains valid until they + * get another callback telling them otherwise. The ttl value is not + * updated when the daemon answers from the cache, hence relying on + * the accuracy of the ttl value is not recommended. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceQueryRecordReply) +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + void *context +); + + +/* DNSServiceQueryRecord() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the query operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: kDNSServiceFlagsForceMulticast or kDNSServiceFlagsLongLivedQuery. + * Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast + * query to a unicast DNS server that implements the protocol. This flag + * has no effect on link-local multicast queries. + * + * interfaceIndex: If non-zero, specifies the interface on which to issue the query + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the name to be queried for on all + * interfaces. See "Constants for specifying an interface index" for more details. + * + * fullname: The full domain name of the resource record to be queried for. + * + * rrtype: The numerical type of the resource record to be queried for + * (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized). + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceQueryRecord +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + DNSServiceQueryRecordReply callBack, + void *context /* may be NULL */ +); + + +/********************************************************************************************* +* +* Unified lookup of both IPv4 and IPv6 addresses for a fully qualified hostname +* +*********************************************************************************************/ + +/* DNSServiceGetAddrInfo + * + * Queries for the IP address of a hostname by using either Multicast or Unicast DNS. + * + * DNSServiceGetAddrInfoReply() parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceGetAddrInfo(). + * + * flags: Possible values are kDNSServiceFlagsMoreComing and + * kDNSServiceFlagsAdd. + * + * interfaceIndex: The interface to which the answers pertain. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred. Other parameters are + * undefined if errorCode is nonzero. + * + * hostname: The fully qualified domain name of the host to be queried for. + * + * address: IPv4 or IPv6 address. + * + * ttl: If the client wishes to cache the result for performance reasons, + * the TTL indicates how long the client may legitimately hold onto + * this result, in seconds. After the TTL expires, the client should + * consider the result no longer valid, and if it requires this data + * again, it should be re-fetched with a new query. Of course, this + * only applies to clients that cancel the asynchronous operation when + * they get a result. Clients that leave the asynchronous operation + * running can safely assume that the data remains valid until they + * get another callback telling them otherwise. The ttl value is not + * updated when the daemon answers from the cache, hence relying on + * the accuracy of the ttl value is not recommended. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceGetAddrInfoReply) +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *hostname, + const struct sockaddr *address, + uint32_t ttl, + void *context +); + + +/* DNSServiceGetAddrInfo() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds then it + * initializes the DNSServiceRef, returns kDNSServiceErr_NoError, and the query + * begins and will last indefinitely until the client terminates the query + * by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: kDNSServiceFlagsForceMulticast + * + * interfaceIndex: The interface on which to issue the query. Passing 0 causes the query to be + * sent on all active interfaces via Multicast or the primary interface via Unicast. + * + * protocol: Pass in kDNSServiceProtocol_IPv4 to look up IPv4 addresses, or kDNSServiceProtocol_IPv6 + * to look up IPv6 addresses, or both to look up both kinds. If neither flag is + * set, the system will apply an intelligent heuristic, which is (currently) + * that it will attempt to look up both, except: + * + * * If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name) + * but this host has no routable IPv6 address, then the call will not try to + * look up IPv6 addresses for "hostname", since any addresses it found would be + * unlikely to be of any use anyway. Similarly, if this host has no routable + * IPv4 address, the call will not try to look up IPv4 addresses for "hostname". + * + * hostname: The fully qualified domain name of the host to be queried for. + * + * callBack: The function to be called when the query succeeds or fails asynchronously. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred. + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceProtocol protocol, + const char *hostname, + DNSServiceGetAddrInfoReply callBack, + void *context /* may be NULL */ +); + + +/********************************************************************************************* +* +* Special Purpose Calls: +* DNSServiceCreateConnection(), DNSServiceRegisterRecord(), DNSServiceReconfirmRecord() +* (most applications will not use these) +* +*********************************************************************************************/ + +/* DNSServiceCreateConnection() + * + * Create a connection to the daemon allowing efficient registration of + * multiple individual records. + * + * Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating + * the reference (via DNSServiceRefDeallocate()) severs the + * connection and deregisters all records registered on this connection. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns + * an error code indicating the specific failure that occurred (in which + * case the DNSServiceRef is not initialized). + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); + +/* DNSServiceRegisterRecord + * + * Register an individual resource record on a connected DNSServiceRef. + * + * Note that name conflicts occurring for records registered via this call must be handled + * by the client in the callback. + * + * DNSServiceRegisterRecordReply() parameters: + * + * sdRef: The connected DNSServiceRef initialized by + * DNSServiceCreateConnection(). + * + * RecordRef: The DNSRecordRef initialized by DNSServiceRegisterRecord(). If the above + * DNSServiceRef is passed to DNSServiceRefDeallocate(), this DNSRecordRef is + * invalidated, and may not be used further. + * + * flags: Currently unused, reserved for future use. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred (including name conflicts.) + * Other parameters are undefined if errorCode is nonzero. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceRegisterRecordReply) +( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + void *context +); + + +/* DNSServiceRegisterRecord() Parameters: + * + * sdRef: A DNSServiceRef initialized by DNSServiceCreateConnection(). + * + * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this + * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * (To deregister ALL records registered on a single connected DNSServiceRef + * and deallocate each of their corresponding DNSServiceRecordRefs, call + * DNSServiceRefDeallocate()). + * + * flags: Possible values are kDNSServiceFlagsShared or kDNSServiceFlagsUnique + * (see flag type definitions for details). + * + * interfaceIndex: If non-zero, specifies the interface on which to register the record + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the record to be registered on all interfaces. + * See "Constants for specifying an interface index" for more details. + * + * fullname: The full domain name of the resource record. + * + * rrtype: The numerical type of the resource record (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN) + * + * rdlen: Length, in bytes, of the rdata. + * + * rdata: A pointer to the raw rdata, as it is to appear in the DNS record. + * + * ttl: The time to live of the resource record, in seconds. + * Most clients should pass 0 to indicate that the system should + * select a sensible default value. + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails (e.g. because of a name conflict.) + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSRecordRef is + * not initialized). + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord +( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + DNSServiceRegisterRecordReply callBack, + void *context /* may be NULL */ +); + + +/* DNSServiceReconfirmRecord + * + * Instruct the daemon to verify the validity of a resource record that appears + * to be out of date (e.g. because TCP connection to a service's target failed.) + * Causes the record to be flushed from the daemon's cache (as well as all other + * daemons' caches on the network) if the record is determined to be invalid. + * Use this routine conservatively. Reconfirming a record necessarily consumes + * network bandwidth, so this should not be done indiscriminately. + * + * Parameters: + * + * flags: Not currently used. + * + * interfaceIndex: Specifies the interface of the record in question. + * The caller must specify the interface. + * This API (by design) causes increased network traffic, so it requires + * the caller to be precise about which record should be reconfirmed. + * It is not possible to pass zero for the interface index to perform + * a "wildcard" reconfirmation, where *all* matching records are reconfirmed. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * rdlen: The length, in bytes, of the resource record rdata. + * + * rdata: The raw rdata of the resource record. + * + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord +( + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata +); + + +/********************************************************************************************* +* +* NAT Port Mapping +* +*********************************************************************************************/ + +/* DNSServiceNATPortMappingCreate + * + * Request a port mapping in the NAT gateway, which maps a port on the local machine + * to an external port on the NAT. The NAT should support either PCP, NAT-PMP or the + * UPnP/IGD protocol for this API to create a successful mapping. Note that this API + * currently supports IPv4 addresses/mappings only. If the NAT gateway supports PCP and + * returns an IPv6 address (incorrectly, since this API specifically requests IPv4 + * addresses), the DNSServiceNATPortMappingReply callback will be invoked with errorCode + * kDNSServiceErr_NATPortMappingUnsupported. + * + * The port mapping will be renewed indefinitely until the client process exits, or + * explicitly terminates the port mapping request by calling DNSServiceRefDeallocate(). + * The client callback will be invoked, informing the client of the NAT gateway's + * external IP address and the external port that has been allocated for this client. + * The client should then record this external IP address and port using whatever + * directory service mechanism it is using to enable peers to connect to it. + * (Clients advertising services using Wide-Area DNS-SD DO NOT need to use this API + * -- when a client calls DNSServiceRegister() NAT mappings are automatically created + * and the external IP address and port for the service are recorded in the global DNS. + * Only clients using some directory mechanism other than Wide-Area DNS-SD need to use + * this API to explicitly map their own ports.) + * + * It's possible that the client callback could be called multiple times, for example + * if the NAT gateway's IP address changes, or if a configuration change results in a + * different external port being mapped for this client. Over the lifetime of any long-lived + * port mapping, the client should be prepared to handle these notifications of changes + * in the environment, and should update its recorded address and/or port as appropriate. + * + * NOTE: There are two unusual aspects of how the DNSServiceNATPortMappingCreate API works, + * which were intentionally designed to help simplify client code: + * + * 1. It's not an error to request a NAT mapping when the machine is not behind a NAT gateway. + * In other NAT mapping APIs, if you request a NAT mapping and the machine is not behind a NAT + * gateway, then the API returns an error code -- it can't get you a NAT mapping if there's no + * NAT gateway. The DNSServiceNATPortMappingCreate API takes a different view. Working out + * whether or not you need a NAT mapping can be tricky and non-obvious, particularly on + * a machine with multiple active network interfaces. Rather than make every client recreate + * this logic for deciding whether a NAT mapping is required, the PortMapping API does that + * work for you. If the client calls the PortMapping API when the machine already has a + * routable public IP address, then instead of complaining about it and giving an error, + * the PortMapping API just invokes your callback, giving the machine's public address + * and your own port number. This means you don't need to write code to work out whether + * your client needs to call the PortMapping API -- just call it anyway, and if it wasn't + * necessary, no harm is done: + * + * - If the machine already has a routable public IP address, then your callback + * will just be invoked giving your own address and port. + * - If a NAT mapping is required and obtained, then your callback will be invoked + * giving you the external address and port. + * - If a NAT mapping is required but not obtained from the local NAT gateway, + * or the machine has no network connectivity, then your callback will be + * invoked giving zero address and port. + * + * 2. In other NAT mapping APIs, if a laptop computer is put to sleep and woken up on a new + * network, it's the client's job to notice this, and work out whether a NAT mapping + * is required on the new network, and make a new NAT mapping request if necessary. + * The DNSServiceNATPortMappingCreate API does this for you, automatically. + * The client just needs to make one call to the PortMapping API, and its callback will + * be invoked any time the mapping state changes. This property complements point (1) above. + * If the client didn't make a NAT mapping request just because it determined that one was + * not required at that particular moment in time, the client would then have to monitor + * for network state changes to determine if a NAT port mapping later became necessary. + * By unconditionally making a NAT mapping request, even when a NAT mapping not to be + * necessary, the PortMapping API will then begin monitoring network state changes on behalf of + * the client, and if a NAT mapping later becomes necessary, it will automatically create a NAT + * mapping and inform the client with a new callback giving the new address and port information. + * + * DNSServiceNATPortMappingReply() parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceNATPortMappingCreate(). + * + * flags: Currently unused, reserved for future use. + * + * interfaceIndex: The interface through which the NAT gateway is reached. + * + * errorCode: Will be kDNSServiceErr_NoError on success. + * Will be kDNSServiceErr_DoubleNAT when the NAT gateway is itself behind one or + * more layers of NAT, in which case the other parameters have the defined values. + * For other failures, will indicate the failure that occurred, and the other + * parameters are undefined. + * + * externalAddress: Four byte IPv4 address in network byte order. + * + * protocol: Will be kDNSServiceProtocol_UDP or kDNSServiceProtocol_TCP or both. + * + * internalPort: The port on the local machine that was mapped. + * + * externalPort: The actual external port in the NAT gateway that was mapped. + * This is likely to be different than the requested external port. + * + * ttl: The lifetime of the NAT port mapping created on the gateway. + * This controls how quickly stale mappings will be garbage-collected + * if the client machine crashes, suffers a power failure, is disconnected + * from the network, or suffers some other unfortunate demise which + * causes it to vanish without explicitly removing its NAT port mapping. + * It's possible that the ttl value will differ from the requested ttl value. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceNATPortMappingReply) +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + uint32_t externalAddress, /* four byte IPv4 address in network byte order */ + DNSServiceProtocol protocol, + uint16_t internalPort, /* In network byte order */ + uint16_t externalPort, /* In network byte order and may be different than the requested port */ + uint32_t ttl, /* may be different than the requested ttl */ + void *context +); + + +/* DNSServiceNATPortMappingCreate() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds then it + * initializes the DNSServiceRef, returns kDNSServiceErr_NoError, and the nat + * port mapping will last indefinitely until the client terminates the port + * mapping request by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Currently ignored, reserved for future use. + * + * interfaceIndex: The interface on which to create port mappings in a NAT gateway. Passing 0 causes + * the port mapping request to be sent on the primary interface. + * + * protocol: To request a port mapping, pass in kDNSServiceProtocol_UDP, or kDNSServiceProtocol_TCP, + * or (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP) to map both. + * The local listening port number must also be specified in the internalPort parameter. + * To just discover the NAT gateway's external IP address, pass zero for protocol, + * internalPort, externalPort and ttl. + * + * internalPort: The port number in network byte order on the local machine which is listening for packets. + * + * externalPort: The requested external port in network byte order in the NAT gateway that you would + * like to map to the internal port. Pass 0 if you don't care which external port is chosen for you. + * + * ttl: The requested renewal period of the NAT port mapping, in seconds. + * If the client machine crashes, suffers a power failure, is disconnected from + * the network, or suffers some other unfortunate demise which causes it to vanish + * unexpectedly without explicitly removing its NAT port mappings, then the NAT gateway + * will garbage-collect old stale NAT port mappings when their lifetime expires. + * Requesting a short TTL causes such orphaned mappings to be garbage-collected + * more promptly, but consumes system resources and network bandwidth with + * frequent renewal packets to keep the mapping from expiring. + * Requesting a long TTL is more efficient on the network, but in the event of the + * client vanishing, stale NAT port mappings will not be garbage-collected as quickly. + * Most clients should pass 0 to use a system-wide default value. + * + * callBack: The function to be called when the port mapping request succeeds or fails asynchronously. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred. + * + * If you don't actually want a port mapped, and are just calling the API + * because you want to find out the NAT's external IP address (e.g. for UI + * display) then pass zero for protocol, internalPort, externalPort and ttl. + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceProtocol protocol, /* TCP and/or UDP */ + uint16_t internalPort, /* network byte order */ + uint16_t externalPort, /* network byte order */ + uint32_t ttl, /* time to live in seconds */ + DNSServiceNATPortMappingReply callBack, + void *context /* may be NULL */ +); + + +/********************************************************************************************* +* +* General Utility Functions +* +*********************************************************************************************/ + +/* DNSServiceConstructFullName() + * + * Concatenate a three-part domain name (as returned by the above callbacks) into a + * properly-escaped full domain name. Note that callbacks in the above functions ALREADY ESCAPE + * strings where necessary. + * + * Parameters: + * + * fullName: A pointer to a buffer that where the resulting full domain name is to be written. + * The buffer must be kDNSServiceMaxDomainName (1009) bytes in length to + * accommodate the longest legal domain name without buffer overrun. + * + * service: The service name - any dots or backslashes must NOT be escaped. + * May be NULL (to construct a PTR record name, e.g. + * "_ftp._tcp.apple.com."). + * + * regtype: The service type followed by the protocol, separated by a dot + * (e.g. "_ftp._tcp"). + * + * domain: The domain name, e.g. "apple.com.". Literal dots or backslashes, + * if any, must be escaped, e.g. "1st\. Floor.apple.com." + * + * return value: Returns kDNSServiceErr_NoError (0) on success, kDNSServiceErr_BadParam on error. + * + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceConstructFullName +( + char * const fullName, + const char * const service, /* may be NULL */ + const char * const regtype, + const char * const domain +); + + +/********************************************************************************************* +* +* TXT Record Construction Functions +* +*********************************************************************************************/ + +/* + * A typical calling sequence for TXT record construction is something like: + * + * Client allocates storage for TXTRecord data (e.g. declare buffer on the stack) + * TXTRecordCreate(); + * TXTRecordSetValue(); + * TXTRecordSetValue(); + * TXTRecordSetValue(); + * ... + * DNSServiceRegister( ... TXTRecordGetLength(), TXTRecordGetBytesPtr() ... ); + * TXTRecordDeallocate(); + * Explicitly deallocate storage for TXTRecord data (if not allocated on the stack) + */ + + +/* TXTRecordRef + * + * Opaque internal data type. + * Note: Represents a DNS-SD TXT record. + */ + +typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef; + + +/* TXTRecordCreate() + * + * Creates a new empty TXTRecordRef referencing the specified storage. + * + * If the buffer parameter is NULL, or the specified storage size is not + * large enough to hold a key subsequently added using TXTRecordSetValue(), + * then additional memory will be added as needed using malloc(). + * + * On some platforms, when memory is low, malloc() may fail. In this + * case, TXTRecordSetValue() will return kDNSServiceErr_NoMemory, and this + * error condition will need to be handled as appropriate by the caller. + * + * You can avoid the need to handle this error condition if you ensure + * that the storage you initially provide is large enough to hold all + * the key/value pairs that are to be added to the record. + * The caller can precompute the exact length required for all of the + * key/value pairs to be added, or simply provide a fixed-sized buffer + * known in advance to be large enough. + * A no-value (key-only) key requires (1 + key length) bytes. + * A key with empty value requires (1 + key length + 1) bytes. + * A key with non-empty value requires (1 + key length + 1 + value length). + * For most applications, DNS-SD TXT records are generally + * less than 100 bytes, so in most cases a simple fixed-sized + * 256-byte buffer will be more than sufficient. + * Recommended size limits for DNS-SD TXT Records are discussed in RFC 6763 + * + * + * Note: When passing parameters to and from these TXT record APIs, + * the key name does not include the '=' character. The '=' character + * is the separator between the key and value in the on-the-wire + * packet format; it is not part of either the key or the value. + * + * txtRecord: A pointer to an uninitialized TXTRecordRef. + * + * bufferLen: The size of the storage provided in the "buffer" parameter. + * + * buffer: Optional caller-supplied storage used to hold the TXTRecord data. + * This storage must remain valid for as long as + * the TXTRecordRef. + */ + +DNSSD_EXPORT +void DNSSD_API TXTRecordCreate +( + TXTRecordRef *txtRecord, + uint16_t bufferLen, + void *buffer +); + + +/* TXTRecordDeallocate() + * + * Releases any resources allocated in the course of preparing a TXT Record + * using TXTRecordCreate()/TXTRecordSetValue()/TXTRecordRemoveValue(). + * Ownership of the buffer provided in TXTRecordCreate() returns to the client. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + */ + +DNSSD_EXPORT +void DNSSD_API TXTRecordDeallocate +( + TXTRecordRef *txtRecord +); + + +/* TXTRecordSetValue() + * + * Adds a key (optionally with value) to a TXTRecordRef. If the "key" already + * exists in the TXTRecordRef, then the current value will be replaced with + * the new value. + * Keys may exist in four states with respect to a given TXT record: + * - Absent (key does not appear at all) + * - Present with no value ("key" appears alone) + * - Present with empty value ("key=" appears in TXT record) + * - Present with non-empty value ("key=value" appears in TXT record) + * For more details refer to "Data Syntax for DNS-SD TXT Records" in RFC 6763 + * + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * key: A null-terminated string which only contains printable ASCII + * values (0x20-0x7E), excluding '=' (0x3D). Keys should be + * 9 characters or fewer (not counting the terminating null). + * + * valueSize: The size of the value. + * + * value: Any binary value. For values that represent + * textual data, UTF-8 is STRONGLY recommended. + * For values that represent textual data, valueSize + * should NOT include the terminating null (if any) + * at the end of the string. + * If NULL, then "key" will be added with no value. + * If non-NULL but valueSize is zero, then "key=" will be + * added with empty value. + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_Invalid if the "key" string contains + * illegal characters. + * Returns kDNSServiceErr_NoMemory if adding this key would + * exceed the available storage. + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API TXTRecordSetValue +( + TXTRecordRef *txtRecord, + const char *key, + uint8_t valueSize, /* may be zero */ + const void *value /* may be NULL */ +); + + +/* TXTRecordRemoveValue() + * + * Removes a key from a TXTRecordRef. The "key" must be an + * ASCII string which exists in the TXTRecordRef. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * key: A key name which exists in the TXTRecordRef. + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_NoSuchKey if the "key" does not + * exist in the TXTRecordRef. + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API TXTRecordRemoveValue +( + TXTRecordRef *txtRecord, + const char *key +); + + +/* TXTRecordGetLength() + * + * Allows you to determine the length of the raw bytes within a TXTRecordRef. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * return value: Returns the size of the raw bytes inside a TXTRecordRef + * which you can pass directly to DNSServiceRegister() or + * to DNSServiceUpdateRecord(). + * Returns 0 if the TXTRecordRef is empty. + */ + +DNSSD_EXPORT +uint16_t DNSSD_API TXTRecordGetLength +( + const TXTRecordRef *txtRecord +); + + +/* TXTRecordGetBytesPtr() + * + * Allows you to retrieve a pointer to the raw bytes within a TXTRecordRef. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * return value: Returns a pointer to the raw bytes inside the TXTRecordRef + * which you can pass directly to DNSServiceRegister() or + * to DNSServiceUpdateRecord(). + */ + +DNSSD_EXPORT +const void * DNSSD_API TXTRecordGetBytesPtr +( + const TXTRecordRef *txtRecord +); + + +/********************************************************************************************* +* +* TXT Record Parsing Functions +* +*********************************************************************************************/ + +/* + * A typical calling sequence for TXT record parsing is something like: + * + * Receive TXT record data in DNSServiceResolve() callback + * if (TXTRecordContainsKey(txtLen, txtRecord, "key")) then do something + * val1ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key1", &len1); + * val2ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key2", &len2); + * ... + * memcpy(myval1, val1ptr, len1); + * memcpy(myval2, val2ptr, len2); + * ... + * return; + * + * If you wish to retain the values after return from the DNSServiceResolve() + * callback, then you need to copy the data to your own storage using memcpy() + * or similar, as shown in the example above. + * + * If for some reason you need to parse a TXT record you built yourself + * using the TXT record construction functions above, then you can do + * that using TXTRecordGetLength and TXTRecordGetBytesPtr calls: + * TXTRecordGetValue(TXTRecordGetLength(x), TXTRecordGetBytesPtr(x), key, &len); + * + * Most applications only fetch keys they know about from a TXT record and + * ignore the rest. + * However, some debugging tools wish to fetch and display all keys. + * To do that, use the TXTRecordGetCount() and TXTRecordGetItemAtIndex() calls. + */ + +/* TXTRecordContainsKey() + * + * Allows you to determine if a given TXT Record contains a specified key. + * + * txtLen: The size of the received TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * key: A null-terminated ASCII string containing the key name. + * + * return value: Returns 1 if the TXT Record contains the specified key. + * Otherwise, it returns 0. + */ + +DNSSD_EXPORT +int DNSSD_API TXTRecordContainsKey +( + uint16_t txtLen, + const void *txtRecord, + const char *key +); + + +/* TXTRecordGetValuePtr() + * + * Allows you to retrieve the value for a given key from a TXT Record. + * + * txtLen: The size of the received TXT Record + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * key: A null-terminated ASCII string containing the key name. + * + * valueLen: On output, will be set to the size of the "value" data. + * + * return value: Returns NULL if the key does not exist in this TXT record, + * or exists with no value (to differentiate between + * these two cases use TXTRecordContainsKey()). + * Returns pointer to location within TXT Record bytes + * if the key exists with empty or non-empty value. + * For empty value, valueLen will be zero. + * For non-empty value, valueLen will be length of value data. + */ + +DNSSD_EXPORT +const void * DNSSD_API TXTRecordGetValuePtr +( + uint16_t txtLen, + const void *txtRecord, + const char *key, + uint8_t *valueLen +); + + +/* TXTRecordGetCount() + * + * Returns the number of keys stored in the TXT Record. The count + * can be used with TXTRecordGetItemAtIndex() to iterate through the keys. + * + * txtLen: The size of the received TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * return value: Returns the total number of keys in the TXT Record. + * + */ + +DNSSD_EXPORT +uint16_t DNSSD_API TXTRecordGetCount +( + uint16_t txtLen, + const void *txtRecord +); + + +/* TXTRecordGetItemAtIndex() + * + * Allows you to retrieve a key name and value pointer, given an index into + * a TXT Record. Legal index values range from zero to TXTRecordGetCount()-1. + * It's also possible to iterate through keys in a TXT record by simply + * calling TXTRecordGetItemAtIndex() repeatedly, beginning with index zero + * and increasing until TXTRecordGetItemAtIndex() returns kDNSServiceErr_Invalid. + * + * On return: + * For keys with no value, *value is set to NULL and *valueLen is zero. + * For keys with empty value, *value is non-NULL and *valueLen is zero. + * For keys with non-empty value, *value is non-NULL and *valueLen is non-zero. + * + * txtLen: The size of the received TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * itemIndex: An index into the TXT Record. + * + * keyBufLen: The size of the string buffer being supplied. + * + * key: A string buffer used to store the key name. + * On return, the buffer contains a null-terminated C string + * giving the key name. DNS-SD TXT keys are usually + * 9 characters or fewer. To hold the maximum possible + * key name, the buffer should be 256 bytes long. + * + * valueLen: On output, will be set to the size of the "value" data. + * + * value: On output, *value is set to point to location within TXT + * Record bytes that holds the value data. + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_NoMemory if keyBufLen is too short. + * Returns kDNSServiceErr_Invalid if index is greater than + * TXTRecordGetCount()-1. + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex +( + uint16_t txtLen, + const void *txtRecord, + uint16_t itemIndex, + uint16_t keyBufLen, + char *key, + uint8_t *valueLen, + const void **value +); + +#if _DNS_SD_LIBDISPATCH +/* + * DNSServiceSetDispatchQueue + * + * Allows you to schedule a DNSServiceRef on a serial dispatch queue for receiving asynchronous + * callbacks. It's the clients responsibility to ensure that the provided dispatch queue is running. + * + * A typical application that uses CFRunLoopRun or dispatch_main on its main thread will + * usually schedule DNSServiceRefs on its main queue (which is always a serial queue) + * using "DNSServiceSetDispatchQueue(sdref, dispatch_get_main_queue());" + * + * If there is any error during the processing of events, the application callback will + * be called with an error code. For shared connections, each subordinate DNSServiceRef + * will get its own error callback. Currently these error callbacks only happen + * if the daemon is manually terminated or crashes, and the error + * code in this case is kDNSServiceErr_ServiceNotRunning. The application must call + * DNSServiceRefDeallocate to free the DNSServiceRef when it gets such an error code. + * These error callbacks are rare and should not normally happen on customer machines, + * but application code should be written defensively to handle such error callbacks + * gracefully if they occur. + * + * After using DNSServiceSetDispatchQueue on a DNSServiceRef, calling DNSServiceProcessResult + * on the same DNSServiceRef will result in undefined behavior and should be avoided. + * + * Once the application successfully schedules a DNSServiceRef on a serial dispatch queue using + * DNSServiceSetDispatchQueue, it cannot remove the DNSServiceRef from the dispatch queue, or use + * DNSServiceSetDispatchQueue a second time to schedule the DNSServiceRef onto a different serial dispatch + * queue. Once scheduled onto a dispatch queue a DNSServiceRef will deliver events to that queue until + * the application no longer requires that operation and terminates it using DNSServiceRefDeallocate. + * + * service: DNSServiceRef that was allocated and returned to the application, when the + * application calls one of the DNSService API. + * + * queue: dispatch queue where the application callback will be scheduled + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_NoMemory if it cannot create a dispatch source + * Returns kDNSServiceErr_BadParam if the service param is invalid or the + * queue param is invalid + */ + +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue +( + DNSServiceRef service, + dispatch_queue_t queue +); +#endif //_DNS_SD_LIBDISPATCH + +#if !defined(_WIN32) +typedef void (DNSSD_API *DNSServiceSleepKeepaliveReply) +( + DNSServiceRef sdRef, + DNSServiceErrorType errorCode, + void *context +); +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + int fd, + unsigned int timeout, + DNSServiceSleepKeepaliveReply callBack, + void *context +); +#endif + +/* Some C compiler cleverness. We can make the compiler check certain things for us, + * and report errors at compile-time if anything is wrong. The usual way to do this would + * be to use a run-time "if" statement or the conventional run-time "assert" mechanism, but + * then you don't find out what's wrong until you run the software. This way, if the assertion + * condition is false, the array size is negative, and the complier complains immediately. + */ + +struct CompileTimeAssertionChecks_DNS_SD +{ + char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _DNS_SD_H */ diff --git a/3rdparty/QtZeroConf/bonjour-sdk/dnssd_clientlib.c b/3rdparty/QtZeroConf/bonjour-sdk/dnssd_clientlib.c new file mode 100644 index 000000000..2a1f5ed4d --- /dev/null +++ b/3rdparty/QtZeroConf/bonjour-sdk/dnssd_clientlib.c @@ -0,0 +1,370 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2004-2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "dns_sd.h" + +#if defined(_WIN32) +// disable warning "conversion from to uint16_t" +#pragma warning(disable:4244) +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +/********************************************************************************************* +* +* Supporting Functions +* +*********************************************************************************************/ + +#define mDNSIsDigit(X) ((X) >= '0' && (X) <= '9') + +// DomainEndsInDot returns 1 if name ends with a dot, 0 otherwise +// (DNSServiceConstructFullName depends this returning 1 for true, rather than any non-zero value meaning true) + +static int DomainEndsInDot(const char *dom) +{ + while (dom[0] && dom[1]) + { + if (dom[0] == '\\') // advance past escaped byte sequence + { + if (mDNSIsDigit(dom[1]) && mDNSIsDigit(dom[2]) && mDNSIsDigit(dom[3])) + dom += 4; // If "\ddd" then skip four + else dom += 2; // else if "\x" then skip two + } + else dom++; // else goto next character + } + return (dom[0] == '.'); +} + +static uint8_t *InternalTXTRecordSearch +( + uint16_t txtLen, + const void *txtRecord, + const char *key, + unsigned long *keylen +) +{ + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + *keylen = (unsigned long) strlen(key); + while (p= lim) goto fail; + *fn++ = '\\'; + *fn++ = '0' + (c / 100); + *fn++ = '0' + (c / 10) % 10; + c = '0' + (c ) % 10; + } + else if (c == '.' || (c == '\\')) // Escape dot and backslash literals + { + if (fn+2 >= lim) goto fail; + *fn++ = '\\'; + } + else + if (fn+1 >= lim) goto fail; + *fn++ = (char)c; + } + *fn++ = '.'; + } + + while (*r) if (fn+1 >= lim) goto fail;else *fn++ = *r++; + if (!DomainEndsInDot(regtype)) { if (fn+1 >= lim) goto fail;else *fn++ = '.';} + + while (*d) if (fn+1 >= lim) goto fail;else *fn++ = *d++; + if (!DomainEndsInDot(domain)) { if (fn+1 >= lim) goto fail;else *fn++ = '.';} + + *fn = '\0'; + return kDNSServiceErr_NoError; + +fail: + *fn = '\0'; + return kDNSServiceErr_BadParam; +} + +/********************************************************************************************* +* +* TXT Record Construction Functions +* +*********************************************************************************************/ + +typedef struct _TXTRecordRefRealType +{ + uint8_t *buffer; // Pointer to data + uint16_t buflen; // Length of buffer + uint16_t datalen; // Length currently in use + uint16_t malloced; // Non-zero if buffer was allocated via malloc() +} TXTRecordRefRealType; + +#define txtRec ((TXTRecordRefRealType*)txtRecord) + +// The opaque storage defined in the public dns_sd.h header is 16 bytes; +// make sure we don't exceed that. +struct CompileTimeAssertionCheck_dnssd_clientlib +{ + char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1]; +}; + +void DNSSD_API TXTRecordCreate +( + TXTRecordRef *txtRecord, + uint16_t bufferLen, + void *buffer +) +{ + txtRec->buffer = buffer; + txtRec->buflen = buffer ? bufferLen : (uint16_t)0; + txtRec->datalen = 0; + txtRec->malloced = 0; +} + +void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtRecord) +{ + if (txtRec->malloced) free(txtRec->buffer); +} + +DNSServiceErrorType DNSSD_API TXTRecordSetValue +( + TXTRecordRef *txtRecord, + const char *key, + uint8_t valueSize, + const void *value +) +{ + uint8_t *start, *p; + const char *k; + unsigned long keysize, keyvalsize; + + for (k = key; *k; k++) if (*k < 0x20 || *k > 0x7E || *k == '=') return(kDNSServiceErr_Invalid); + keysize = (unsigned long)(k - key); + keyvalsize = 1 + keysize + (value ? (1 + valueSize) : 0); + if (keysize < 1 || keyvalsize > 255) return(kDNSServiceErr_Invalid); + (void)TXTRecordRemoveValue(txtRecord, key); + if (txtRec->datalen + keyvalsize > txtRec->buflen) + { + unsigned char *newbuf; + unsigned long newlen = txtRec->datalen + keyvalsize; + if (newlen > 0xFFFF) return(kDNSServiceErr_Invalid); + newbuf = malloc((size_t)newlen); + if (!newbuf) return(kDNSServiceErr_NoMemory); + memcpy(newbuf, txtRec->buffer, txtRec->datalen); + if (txtRec->malloced) free(txtRec->buffer); + txtRec->buffer = newbuf; + txtRec->buflen = (uint16_t)(newlen); + txtRec->malloced = 1; + } + start = txtRec->buffer + txtRec->datalen; + p = start + 1; + memcpy(p, key, keysize); + p += keysize; + if (value) + { + *p++ = '='; + memcpy(p, value, valueSize); + p += valueSize; + } + *start = (uint8_t)(p - start - 1); + txtRec->datalen += p - start; + return(kDNSServiceErr_NoError); +} + +DNSServiceErrorType DNSSD_API TXTRecordRemoveValue +( + TXTRecordRef *txtRecord, + const char *key +) +{ + unsigned long keylen, itemlen, remainder; + uint8_t *item = InternalTXTRecordSearch(txtRec->datalen, txtRec->buffer, key, &keylen); + if (!item) return(kDNSServiceErr_NoSuchKey); + itemlen = (unsigned long)(1 + item[0]); + remainder = (unsigned long)((txtRec->buffer + txtRec->datalen) - (item + itemlen)); + // Use memmove because memcpy behaviour is undefined for overlapping regions + memmove(item, item + itemlen, remainder); + txtRec->datalen -= itemlen; + return(kDNSServiceErr_NoError); +} + +uint16_t DNSSD_API TXTRecordGetLength (const TXTRecordRef *txtRecord) { return(txtRec->datalen); } +const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); } + +/********************************************************************************************* +* +* TXT Record Parsing Functions +* +*********************************************************************************************/ + +int DNSSD_API TXTRecordContainsKey +( + uint16_t txtLen, + const void *txtRecord, + const char *key +) +{ + unsigned long keylen; + return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0); +} + +const void * DNSSD_API TXTRecordGetValuePtr +( + uint16_t txtLen, + const void *txtRecord, + const char *key, + uint8_t *valueLen +) +{ + unsigned long keylen; + uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen); + if (!item || item[0] <= keylen) return(NULL); // If key not found, or found with no value, return NULL + *valueLen = (uint8_t)(item[0] - (keylen + 1)); + return (item + 1 + keylen + 1); +} + +uint16_t DNSSD_API TXTRecordGetCount +( + uint16_t txtLen, + const void *txtRecord +) +{ + uint16_t count = 0; + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + while (pe) ? (uint16_t)0 : count); +} + +DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex +( + uint16_t txtLen, + const void *txtRecord, + uint16_t itemIndex, + uint16_t keyBufLen, + char *key, + uint8_t *valueLen, + const void **value +) +{ + uint16_t count = 0; + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + while (p= keyBufLen) return(kDNSServiceErr_NoMemory); + memcpy(key, x, len); + key[len] = 0; + if (x+len +#include + +#include "dnssd_ipc.h" + +#if APPLE_OSX_mDNSResponder +#include +#include +#include +#include "dns_sd_internal.h" +#endif + +#if defined(_WIN32) + + #define _SSIZE_T + #include + #include + #include + #include + #include + #include + #include + + #define sockaddr_mdns sockaddr_in + #define AF_MDNS AF_INET + +// Disable warning: "'type cast' : from data pointer 'void *' to function pointer" + #pragma warning(disable:4055) + +// Disable warning: "nonstandard extension, function/data pointer conversion in expression" + #pragma warning(disable:4152) + +extern BOOL IsSystemServiceDisabled(); + + #define sleep(X) Sleep((X) * 1000) + +static int g_initWinsock = 0; + #define LOG_WARNING kDebugLevelWarning + #define LOG_INFO kDebugLevelInfo +static void syslog( int priority, const char * message, ...) +{ + va_list args; + int len; + char * buffer; + DWORD err = WSAGetLastError(); + (void) priority; + va_start( args, message ); + len = _vscprintf( message, args ) + 1; + buffer = malloc( len * sizeof(char) ); + if ( buffer ) { vsnprintf( buffer, len, message, args ); OutputDebugString( buffer ); free( buffer ); } + WSASetLastError( err ); +} +#else + + #include // For O_RDWR etc. + #include + #include + #include + + #define sockaddr_mdns sockaddr_un + #define AF_MDNS AF_LOCAL + +#endif + +// Specifies how many times we'll try and connect to the server. + +#define DNSSD_CLIENT_MAXTRIES 4 + +// Uncomment the line below to use the old error return mechanism of creating a temporary named socket (e.g. in /var/tmp) +//#define USE_NAMED_ERROR_RETURN_SOCKET 1 + +// If the UDS client has not received a response from the daemon in 60 secs, it is unlikely to get one +// Note: Timeout of 3 secs should be sufficient in normal scenarios, but 60 secs is chosen as a safeguard since +// some clients may come up before mDNSResponder itself after a BOOT and on rare ocassions IOPM/Keychain/D2D calls +// in mDNSResponder's INIT may take a much longer time to return +#define DNSSD_CLIENT_TIMEOUT 60 + +#ifndef CTL_PATH_PREFIX +#define CTL_PATH_PREFIX "/var/tmp/dnssd_result_socket." +#endif + +typedef struct +{ + ipc_msg_hdr ipc_hdr; + DNSServiceFlags cb_flags; + uint32_t cb_interface; + DNSServiceErrorType cb_err; +} CallbackHeader; + +typedef struct _DNSServiceRef_t DNSServiceOp; +typedef struct _DNSRecordRef_t DNSRecord; + +#if !defined(_WIN32) +typedef struct +{ + void *AppCallback; // Client callback function and context + void *AppContext; +} SleepKAContext; +#endif + +// client stub callback to process message from server and deliver results to client application +typedef void (*ProcessReplyFn)(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *msg, const char *const end); + +#define ValidatorBits 0x12345678 +#define DNSServiceRefValid(X) (dnssd_SocketValid((X)->sockfd) && (((X)->sockfd ^ (X)->validator) == ValidatorBits)) + +// When using kDNSServiceFlagsShareConnection, there is one primary _DNSServiceOp_t, and zero or more subordinates +// For the primary, the 'next' field points to the first subordinate, and its 'next' field points to the next, and so on. +// For the primary, the 'primary' field is NULL; for subordinates the 'primary' field points back to the associated primary +// +// _DNS_SD_LIBDISPATCH is defined where libdispatch/GCD is available. This does not mean that the application will use the +// DNSServiceSetDispatchQueue API. Hence any new code guarded with _DNS_SD_LIBDISPATCH should still be backwards compatible. +struct _DNSServiceRef_t +{ + DNSServiceOp *next; // For shared connection + DNSServiceOp *primary; // For shared connection + dnssd_sock_t sockfd; // Connected socket between client and daemon + dnssd_sock_t validator; // Used to detect memory corruption, double disposals, etc. + client_context_t uid; // For shared connection requests, each subordinate DNSServiceRef has its own ID, + // unique within the scope of the same shared parent DNSServiceRef + uint32_t op; // request_op_t or reply_op_t + uint32_t max_index; // Largest assigned record index - 0 if no additional records registered + uint32_t logcounter; // Counter used to control number of syslog messages we write + int *moreptr; // Set while DNSServiceProcessResult working on this particular DNSServiceRef + ProcessReplyFn ProcessReply; // Function pointer to the code to handle received messages + void *AppCallback; // Client callback function and context + void *AppContext; + DNSRecord *rec; +#if _DNS_SD_LIBDISPATCH + dispatch_source_t disp_source; + dispatch_queue_t disp_queue; +#endif + void *kacontext; +}; + +struct _DNSRecordRef_t +{ + DNSRecord *recnext; + void *AppContext; + DNSServiceRegisterRecordReply AppCallback; + DNSRecordRef recref; + uint32_t record_index; // index is unique to the ServiceDiscoveryRef + client_context_t uid; // For demultiplexing multiple DNSServiceRegisterRecord calls + DNSServiceOp *sdr; +}; + +#if !defined(USE_TCP_LOOPBACK) +static void SetUDSPath(struct sockaddr_un *saddr, const char *path) +{ + size_t pathLen; + + pathLen = strlen(path); + if (pathLen < sizeof(saddr->sun_path)) + memcpy(saddr->sun_path, path, pathLen + 1); + else + saddr->sun_path[0] = '\0'; +} +#endif + +// Write len bytes. Return 0 on success, -1 on error +static int write_all(dnssd_sock_t sd, char *buf, size_t len) +{ + // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. + //if (send(sd, buf, len, MSG_WAITALL) != len) return -1; + while (len) + { + ssize_t num_written = send(sd, buf, (long)len, 0); + if (num_written < 0 || (size_t)num_written > len) + { + // Check whether socket has gone defunct, + // otherwise, an error here indicates some OS bug + // or that the mDNSResponder daemon crashed (which should never happen). +#if !defined(__ppc__) && defined(SO_ISDEFUNCT) + int defunct = 0; + socklen_t dlen = sizeof (defunct); + if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0) + syslog(LOG_WARNING, "dnssd_clientstub write_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + if (!defunct) + syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd, + (long)num_written, (long)len, + (num_written < 0) ? dnssd_errno : 0, + (num_written < 0) ? dnssd_strerror(dnssd_errno) : ""); + else + syslog(LOG_INFO, "dnssd_clientstub write_all(%d) DEFUNCT", sd); +#else + syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd, + (long)num_written, (long)len, + (num_written < 0) ? dnssd_errno : 0, + (num_written < 0) ? dnssd_strerror(dnssd_errno) : ""); +#endif + return -1; + } + buf += num_written; + len -= num_written; + } + return 0; +} + +enum { read_all_success = 0, read_all_fail = -1, read_all_wouldblock = -2 }; + +// Read len bytes. Return 0 on success, read_all_fail on error, or read_all_wouldblock for +static int read_all(dnssd_sock_t sd, char *buf, int len) +{ + // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. + //if (recv(sd, buf, len, MSG_WAITALL) != len) return -1; + + while (len) + { + ssize_t num_read = recv(sd, buf, len, 0); + // It is valid to get an interrupted system call error e.g., somebody attaching + // in a debugger, retry without failing + if ((num_read < 0) && (errno == EINTR)) + { + syslog(LOG_INFO, "dnssd_clientstub read_all: EINTR continue"); + continue; + } + if ((num_read == 0) || (num_read < 0) || (num_read > len)) + { + int printWarn = 0; + int defunct = 0; + + // Check whether socket has gone defunct, + // otherwise, an error here indicates some OS bug + // or that the mDNSResponder daemon crashed (which should never happen). +#if defined(WIN32) + // Suppress logs for "A non-blocking socket operation + // could not be completed immediately" + if (WSAGetLastError() != WSAEWOULDBLOCK) + printWarn = 1; +#endif +#if !defined(__ppc__) && defined(SO_ISDEFUNCT) + { + socklen_t dlen = sizeof (defunct); + if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0) + syslog(LOG_WARNING, "dnssd_clientstub read_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + } + if (!defunct) + printWarn = 1; +#endif + if (printWarn) + syslog(LOG_WARNING, "dnssd_clientstub read_all(%d) failed %ld/%ld %d %s", sd, + (long)num_read, (long)len, + (num_read < 0) ? dnssd_errno : 0, + (num_read < 0) ? dnssd_strerror(dnssd_errno) : ""); + else if (defunct) + syslog(LOG_INFO, "dnssd_clientstub read_all(%d) DEFUNCT", sd); + return (num_read < 0 && dnssd_errno == dnssd_EWOULDBLOCK) ? read_all_wouldblock : read_all_fail; + } + buf += num_read; + len -= num_read; + } + return read_all_success; +} + +// Returns 1 if more bytes remain to be read on socket descriptor sd, 0 otherwise +static int more_bytes(dnssd_sock_t sd) +{ + struct timeval tv = { 0, 0 }; + fd_set readfds; + fd_set *fs; + int ret; + +#if defined(_WIN32) + fs = &readfds; + FD_ZERO(fs); + FD_SET(sd, fs); + ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv); +#else + if (sd < FD_SETSIZE) + { + fs = &readfds; + FD_ZERO(fs); + } + else + { + // Compute the number of integers needed for storing "sd". Internally fd_set is stored + // as an array of ints with one bit for each fd and hence we need to compute + // the number of ints needed rather than the number of bytes. If "sd" is 32, we need + // two ints and not just one. + int nfdbits = sizeof (int) * 8; + int nints = (sd/nfdbits) + 1; + fs = (fd_set *)calloc(nints, (size_t)sizeof(int)); + if (fs == NULL) + { + syslog(LOG_WARNING, "dnssd_clientstub more_bytes: malloc failed"); + return 0; + } + } + FD_SET(sd, fs); + ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv); + if (fs != &readfds) + free(fs); +#endif + return (ret > 0); +} + +// set_waitlimit() implements a timeout using select. It is called from deliver_request() before recv() OR accept() +// to ensure the UDS clients are not blocked in these system calls indefinitely. +// Note: Ideally one should never be blocked here, because it indicates either mDNSResponder daemon is not yet up/hung/ +// superbusy/crashed or some other OS bug. For eg: On Windows which suffers from 3rd party software +// (primarily 3rd party firewall software) interfering with proper functioning of the TCP protocol stack it is possible +// the next operation on this socket(recv/accept) is blocked since we depend on TCP to communicate with the system service. +static int set_waitlimit(dnssd_sock_t sock, int timeout) +{ + int gDaemonErr = kDNSServiceErr_NoError; + + // To prevent stack corruption since select does not work with timeout if fds > FD_SETSIZE(1024) + if (!gDaemonErr && sock < FD_SETSIZE) + { + struct timeval tv; + fd_set set; + + FD_ZERO(&set); + FD_SET(sock, &set); + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (!select((int)(sock + 1), &set, NULL, NULL, &tv)) + { + // Ideally one should never hit this case: See comments before set_waitlimit() + syslog(LOG_WARNING, "dnssd_clientstub set_waitlimit:_daemon timed out (%d secs) without any response: Socket %d", timeout, sock); + gDaemonErr = kDNSServiceErr_Timeout; + } + } + return gDaemonErr; +} + +/* create_hdr + * + * allocate and initialize an ipc message header. Value of len should initially be the + * length of the data, and is set to the value of the data plus the header. data_start + * is set to point to the beginning of the data section. SeparateReturnSocket should be + * non-zero for calls that can't receive an immediate error return value on their primary + * socket, and therefore require a separate return path for the error code result. + * if zero, the path to a control socket is appended at the beginning of the message buffer. + * data_start is set past this string. + */ +static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int SeparateReturnSocket, DNSServiceOp *ref) +{ + char *msg = NULL; + ipc_msg_hdr *hdr; + int datalen; +#if !defined(USE_TCP_LOOPBACK) + char ctrl_path[64] = ""; // "/var/tmp/dnssd_result_socket.xxxxxxxxxx-xxx-xxxxxx" +#endif + + if (SeparateReturnSocket) + { +#if defined(USE_TCP_LOOPBACK) + *len += 2; // Allocate space for two-byte port number +#elif defined(USE_NAMED_ERROR_RETURN_SOCKET) + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) + { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: gettimeofday failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); return NULL; } + snprintf(ctrl_path, sizeof(ctrl_path), "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(), + (unsigned long)(tv.tv_sec & 0xFFF), (unsigned long)(tv.tv_usec)); + *len += strlen(ctrl_path) + 1; +#else + *len += 1; // Allocate space for single zero byte (empty C string) +#endif + } + + datalen = (int) *len; + *len += sizeof(ipc_msg_hdr); + + // Write message to buffer + msg = malloc(*len); + if (!msg) { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: malloc failed"); return NULL; } + + memset(msg, 0, *len); + hdr = (ipc_msg_hdr *)msg; + hdr->version = VERSION; + hdr->datalen = datalen; + hdr->ipc_flags = 0; + hdr->op = op; + hdr->client_context = ref->uid; + hdr->reg_index = 0; + *data_start = msg + sizeof(ipc_msg_hdr); +#if defined(USE_TCP_LOOPBACK) + // Put dummy data in for the port, since we don't know what it is yet. + // The data will get filled in before we send the message. This happens in deliver_request(). + if (SeparateReturnSocket) put_uint16(0, data_start); +#else + if (SeparateReturnSocket) put_string(ctrl_path, data_start); +#endif + return hdr; +} + +static void FreeDNSRecords(DNSServiceOp *sdRef) +{ + DNSRecord *rec = sdRef->rec; + while (rec) + { + DNSRecord *next = rec->recnext; + free(rec); + rec = next; + } +} + +static void FreeDNSServiceOp(DNSServiceOp *x) +{ + // We don't use our DNSServiceRefValid macro here because if we're cleaning up after a socket() call failed + // then sockfd could legitimately contain a failing value (e.g. dnssd_InvalidSocket) + if ((x->sockfd ^ x->validator) != ValidatorBits) + { + static DNSServiceOp *op_were_not_going_to_free_but_we_need_to_fool_the_analyzer; + syslog(LOG_WARNING, "dnssd_clientstub attempt to dispose invalid DNSServiceRef %p %08X %08X", x, x->sockfd, x->validator); + op_were_not_going_to_free_but_we_need_to_fool_the_analyzer = x; + } + else + { + x->next = NULL; + x->primary = NULL; + x->sockfd = dnssd_InvalidSocket; + x->validator = 0xDDDDDDDD; + x->op = request_op_none; + x->max_index = 0; + x->logcounter = 0; + x->moreptr = NULL; + x->ProcessReply = NULL; + x->AppCallback = NULL; + x->AppContext = NULL; +#if _DNS_SD_LIBDISPATCH + if (x->disp_source) dispatch_release(x->disp_source); + x->disp_source = NULL; + x->disp_queue = NULL; +#endif + // DNSRecords may have been added to subordinate sdRef e.g., DNSServiceRegister/DNSServiceAddRecord + // or on the main sdRef e.g., DNSServiceCreateConnection/DNSServiceRegisterRecord. + // DNSRecords may have been freed if the application called DNSRemoveRecord. + FreeDNSRecords(x); + if (x->kacontext) + { + free(x->kacontext); + x->kacontext = NULL; + } + free(x); + } +} + +// Return a connected service ref (deallocate with DNSServiceRefDeallocate) +static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags flags, uint32_t op, ProcessReplyFn ProcessReply, void *AppCallback, void *AppContext) +{ + int NumTries = 0; + + dnssd_sockaddr_t saddr; + DNSServiceOp *sdr; + + if (!ref) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSService operation with NULL DNSServiceRef"); + return kDNSServiceErr_BadParam; + } + + if (flags & kDNSServiceFlagsShareConnection) + { + if (!*ref) + { + syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with NULL DNSServiceRef"); + return kDNSServiceErr_BadParam; + } + if (!DNSServiceRefValid(*ref) || ((*ref)->op != connection_request && (*ref)->op != connection_delegate_request) || (*ref)->primary) + { + syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with invalid DNSServiceRef %p %08X %08X op %d", + (*ref), (*ref)->sockfd, (*ref)->validator, (*ref)->op); + *ref = NULL; + return kDNSServiceErr_BadReference; + } + } + + #if defined(_WIN32) + if (!g_initWinsock) + { + WSADATA wsaData; + g_initWinsock = 1; + if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { *ref = NULL; return kDNSServiceErr_ServiceNotRunning; } + } + // If the system service is disabled, we only want to try to connect once + //if (IsSystemServiceDisabled()) + // NumTries = DNSSD_CLIENT_MAXTRIES; + #endif + + sdr = malloc(sizeof(DNSServiceOp)); + if (!sdr) + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: malloc failed"); + *ref = NULL; + return kDNSServiceErr_NoMemory; + } + sdr->next = NULL; + sdr->primary = NULL; + sdr->sockfd = dnssd_InvalidSocket; + sdr->validator = sdr->sockfd ^ ValidatorBits; + sdr->op = op; + sdr->max_index = 0; + sdr->logcounter = 0; + sdr->moreptr = NULL; + sdr->uid.u32[0] = 0; + sdr->uid.u32[1] = 0; + sdr->ProcessReply = ProcessReply; + sdr->AppCallback = AppCallback; + sdr->AppContext = AppContext; + sdr->rec = NULL; +#if _DNS_SD_LIBDISPATCH + sdr->disp_source = NULL; + sdr->disp_queue = NULL; +#endif + sdr->kacontext = NULL; + + if (flags & kDNSServiceFlagsShareConnection) + { + DNSServiceOp **p = &(*ref)->next; // Append ourselves to end of primary's list + while (*p) + p = &(*p)->next; + *p = sdr; + // Preincrement counter before we use it -- it helps with debugging if we know the all-zeroes ID should never appear + if (++(*ref)->uid.u32[0] == 0) + ++(*ref)->uid.u32[1]; // In parent DNSServiceOp increment UID counter + sdr->primary = *ref; // Set our primary pointer + sdr->sockfd = (*ref)->sockfd; // Inherit primary's socket + sdr->validator = (*ref)->validator; + sdr->uid = (*ref)->uid; + //printf("ConnectToServer sharing socket %d\n", sdr->sockfd); + } + else + { + #ifdef SO_NOSIGPIPE + const unsigned long optval = 1; + #endif + #ifndef USE_TCP_LOOPBACK + char* uds_serverpath = getenv(MDNS_UDS_SERVERPATH_ENVVAR); + if (uds_serverpath == NULL) + uds_serverpath = MDNS_UDS_SERVERPATH; + else if (strlen(uds_serverpath) >= MAX_CTLPATH) + { + uds_serverpath = MDNS_UDS_SERVERPATH; + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: using default path since env len is invalid"); + } + #endif + *ref = NULL; + sdr->sockfd = socket(AF_DNSSD, SOCK_STREAM, 0); + sdr->validator = sdr->sockfd ^ ValidatorBits; + if (!dnssd_SocketValid(sdr->sockfd)) + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: socket failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + FreeDNSServiceOp(sdr); + return kDNSServiceErr_NoMemory; + } + #ifdef SO_NOSIGPIPE + // Some environments (e.g. OS X) support turning off SIGPIPE for a socket + if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0) + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_NOSIGPIPE failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + #endif + #if defined(USE_TCP_LOOPBACK) + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + saddr.sin_port = htons(MDNS_TCP_SERVERPORT); + #else + saddr.sun_family = AF_LOCAL; + SetUDSPath(&saddr, uds_serverpath); + #if !defined(__ppc__) && defined(SO_DEFUNCTOK) + { + int defunct = 1; + if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0) + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + } + #endif + #endif + + while (1) + { + int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr)); + if (!err) + break; // If we succeeded, return sdr + // If we failed, then it may be because the daemon is still launching. + // This can happen for processes that launch early in the boot process, while the + // daemon is still coming up. Rather than fail here, we wait 1 sec and try again. + // If, after DNSSD_CLIENT_MAXTRIES, we still can't connect to the daemon, + // then we give up and return a failure code. + if (++NumTries < DNSSD_CLIENT_MAXTRIES) + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect()-> No of tries: %d", NumTries); + sleep(1); // Sleep a bit, then try again + } + else + { + #if !defined(USE_TCP_LOOPBACK) + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect() failed path:%s Socket:%d Err:%d Errno:%d %s", + uds_serverpath, sdr->sockfd, err, dnssd_errno, dnssd_strerror(dnssd_errno)); + #endif + dnssd_close(sdr->sockfd); + FreeDNSServiceOp(sdr); + return kDNSServiceErr_ServiceNotRunning; + } + } + //printf("ConnectToServer opened socket %d\n", sdr->sockfd); + } + + *ref = sdr; + return kDNSServiceErr_NoError; +} + +#define deliver_request_bailout(MSG) \ + do { syslog(LOG_WARNING, "dnssd_clientstub deliver_request: %s failed %d (%s)", (MSG), dnssd_errno, dnssd_strerror(dnssd_errno)); goto cleanup; } while(0) + +static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) +{ + uint32_t datalen; + dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket; + DNSServiceErrorType err = kDNSServiceErr_Unknown; // Default for the "goto cleanup" cases + int MakeSeparateReturnSocket; + #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET) + char *data; + #endif + + if (!hdr) + { + syslog(LOG_WARNING, "dnssd_clientstub deliver_request: !hdr"); + return kDNSServiceErr_Unknown; + } + + datalen = hdr->datalen; // We take a copy here because we're going to convert hdr->datalen to network byte order + #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET) + data = (char *)hdr + sizeof(ipc_msg_hdr); + #endif + + // Note: need to check hdr->op, not sdr->op. + // hdr->op contains the code for the specific operation we're currently doing, whereas sdr->op + // contains the original parent DNSServiceOp (e.g. for an add_record_request, hdr->op will be + // add_record_request but the parent sdr->op will be connection_request or reg_service_request) + MakeSeparateReturnSocket = (sdr->primary || + hdr->op == reg_record_request || hdr->op == add_record_request || hdr->op == update_record_request || hdr->op == remove_record_request); + + if (!DNSServiceRefValid(sdr)) + { + if (hdr) + free(hdr); + syslog(LOG_WARNING, "dnssd_clientstub deliver_request: invalid DNSServiceRef %p %08X %08X", sdr, sdr->sockfd, sdr->validator); + return kDNSServiceErr_BadReference; + } + + if (MakeSeparateReturnSocket) + { + #if defined(USE_TCP_LOOPBACK) + { + union { uint16_t s; u_char b[2]; } port; + dnssd_sockaddr_t caddr; + dnssd_socklen_t len = (dnssd_socklen_t) sizeof(caddr); + listenfd = socket(AF_DNSSD, SOCK_STREAM, 0); + if (!dnssd_SocketValid(listenfd)) deliver_request_bailout("TCP socket"); + + caddr.sin_family = AF_INET; + caddr.sin_port = 0; + caddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + if (bind(listenfd, (struct sockaddr*) &caddr, sizeof(caddr)) < 0) deliver_request_bailout("TCP bind"); + if (getsockname(listenfd, (struct sockaddr*) &caddr, &len) < 0) deliver_request_bailout("TCP getsockname"); + if (listen(listenfd, 1) < 0) deliver_request_bailout("TCP listen"); + port.s = caddr.sin_port; + data[0] = port.b[0]; // don't switch the byte order, as the + data[1] = port.b[1]; // daemon expects it in network byte order + } + #elif defined(USE_NAMED_ERROR_RETURN_SOCKET) + { + mode_t mask; + int bindresult; + dnssd_sockaddr_t caddr; + listenfd = socket(AF_DNSSD, SOCK_STREAM, 0); + if (!dnssd_SocketValid(listenfd)) deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET socket"); + + caddr.sun_family = AF_LOCAL; + // According to Stevens (section 3.2), there is no portable way to + // determine whether sa_len is defined on a particular platform. + #ifndef NOT_HAVE_SA_LEN + caddr.sun_len = sizeof(struct sockaddr_un); + #endif + SetUDSPath(&caddr, data); + mask = umask(0); + bindresult = bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr)); + umask(mask); + if (bindresult < 0) deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET bind"); + if (listen(listenfd, 1) < 0) deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET listen"); + } + #else + { + dnssd_sock_t sp[2]; + if (socketpair(AF_DNSSD, SOCK_STREAM, 0, sp) < 0) deliver_request_bailout("socketpair"); + else + { + errsd = sp[0]; // We'll read our four-byte error code from sp[0] + listenfd = sp[1]; // We'll send sp[1] to the daemon + #if !defined(__ppc__) && defined(SO_DEFUNCTOK) + { + int defunct = 1; + if (setsockopt(errsd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0) + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + } + #endif + } + } + #endif + } + +#if !defined(USE_TCP_LOOPBACK) && !defined(USE_NAMED_ERROR_RETURN_SOCKET) + // If we're going to make a separate error return socket, and pass it to the daemon + // using sendmsg, then we'll hold back one data byte to go with it. + // On some versions of Unix (including Leopard) sending a control message without + // any associated data does not work reliably -- e.g. one particular issue we ran + // into is that if the receiving program is in a kqueue loop waiting to be notified + // of the received message, it doesn't get woken up when the control message arrives. + if (MakeSeparateReturnSocket || sdr->op == send_bpf) + datalen--; // Okay to use sdr->op when checking for op == send_bpf +#endif + + // At this point, our listening socket is set up and waiting, if necessary, for the daemon to connect back to + ConvertHeaderBytes(hdr); + //syslog(LOG_WARNING, "dnssd_clientstub deliver_request writing %lu bytes", (unsigned long)(datalen + sizeof(ipc_msg_hdr))); + //if (MakeSeparateReturnSocket) syslog(LOG_WARNING, "dnssd_clientstub deliver_request name is %s", data); +#if TEST_SENDING_ONE_BYTE_AT_A_TIME + unsigned int i; + for (i=0; isockfd, ((char *)hdr)+i, 1) < 0) + { syslog(LOG_WARNING, "write_all (byte %u) failed", i); goto cleanup; } + usleep(10000); + } +#else + if (write_all(sdr->sockfd, (char *)hdr, datalen + sizeof(ipc_msg_hdr)) < 0) + { + // write_all already prints an error message if there is an error writing to + // the socket except for DEFUNCT. Logging here is unnecessary and also wrong + // in the case of DEFUNCT sockets + syslog(LOG_INFO, "dnssd_clientstub deliver_request ERROR: write_all(%d, %lu bytes) failed", + sdr->sockfd, (unsigned long)(datalen + sizeof(ipc_msg_hdr))); + goto cleanup; + } +#endif + + if (!MakeSeparateReturnSocket) + errsd = sdr->sockfd; + if (MakeSeparateReturnSocket || sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf + { +#if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET) + // At this point we may wait in accept for a few milliseconds waiting for the daemon to connect back to us, + // but that's okay -- the daemon should not take more than a few milliseconds to respond. + // set_waitlimit() ensures we do not block indefinitely just in case something is wrong + dnssd_sockaddr_t daddr; + dnssd_socklen_t len = sizeof(daddr); + if ((err = set_waitlimit(listenfd, DNSSD_CLIENT_TIMEOUT)) != kDNSServiceErr_NoError) + goto cleanup; + errsd = accept(listenfd, (struct sockaddr *)&daddr, &len); + if (!dnssd_SocketValid(errsd)) + deliver_request_bailout("accept"); +#else + + struct iovec vec = { ((char *)hdr) + sizeof(ipc_msg_hdr) + datalen, 1 }; // Send the last byte along with the SCM_RIGHTS + struct msghdr msg; + struct cmsghdr *cmsg; + char cbuf[CMSG_SPACE(4 * sizeof(dnssd_sock_t))]; + + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + if (MakeSeparateReturnSocket || sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf + { + if (sdr->op == send_bpf) + { + int i; + char p[12]; // Room for "/dev/bpf999" with terminating null + for (i=0; i<100; i++) + { + snprintf(p, sizeof(p), "/dev/bpf%d", i); + listenfd = open(p, O_RDWR, 0); + //if (dnssd_SocketValid(listenfd)) syslog(LOG_WARNING, "Sending fd %d for %s", listenfd, p); + if (!dnssd_SocketValid(listenfd) && dnssd_errno != EBUSY) + syslog(LOG_WARNING, "Error opening %s %d (%s)", p, dnssd_errno, dnssd_strerror(dnssd_errno)); + if (dnssd_SocketValid(listenfd) || dnssd_errno != EBUSY) break; + } + } + msg.msg_control = cbuf; + msg.msg_controllen = CMSG_LEN(sizeof(dnssd_sock_t)); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(dnssd_sock_t)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *((dnssd_sock_t *)CMSG_DATA(cmsg)) = listenfd; + } + +#if TEST_KQUEUE_CONTROL_MESSAGE_BUG + sleep(1); +#endif + +#if DEBUG_64BIT_SCM_RIGHTS + syslog(LOG_WARNING, "dnssd_clientstub sendmsg read sd=%d write sd=%d %ld %ld %ld/%ld/%ld/%ld", + errsd, listenfd, sizeof(dnssd_sock_t), sizeof(void*), + sizeof(struct cmsghdr) + sizeof(dnssd_sock_t), + CMSG_LEN(sizeof(dnssd_sock_t)), (long)CMSG_SPACE(sizeof(dnssd_sock_t)), + (long)((char*)CMSG_DATA(cmsg) + 4 - cbuf)); +#endif // DEBUG_64BIT_SCM_RIGHTS + + if (sendmsg(sdr->sockfd, &msg, 0) < 0) + { + syslog(LOG_WARNING, "dnssd_clientstub deliver_request ERROR: sendmsg failed read sd=%d write sd=%d errno %d (%s)", + errsd, listenfd, dnssd_errno, dnssd_strerror(dnssd_errno)); + err = kDNSServiceErr_Incompatible; + goto cleanup; + } + +#if DEBUG_64BIT_SCM_RIGHTS + syslog(LOG_WARNING, "dnssd_clientstub sendmsg read sd=%d write sd=%d okay", errsd, listenfd); +#endif // DEBUG_64BIT_SCM_RIGHTS + +#endif + // Close our end of the socketpair *before* calling read_all() to get the four-byte error code. + // Otherwise, if the daemon closes our socket (or crashes), we will have to wait for a timeout + // in read_all() because the socket is not closed (we still have an open reference to it) + // Note: listenfd is overwritten in the case of send_bpf above and that will be closed here + // for send_bpf operation. + dnssd_close(listenfd); + listenfd = dnssd_InvalidSocket; // Make sure we don't close it a second time in the cleanup handling below + } + + // At this point we may wait in read_all for a few milliseconds waiting for the daemon to send us the error code, + // but that's okay -- the daemon should not take more than a few milliseconds to respond. + // set_waitlimit() ensures we do not block indefinitely just in case something is wrong + if (sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf + err = kDNSServiceErr_NoError; + else if ((err = set_waitlimit(errsd, DNSSD_CLIENT_TIMEOUT)) == kDNSServiceErr_NoError) + { + if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0) + err = kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us + else + err = ntohl(err); + } + //syslog(LOG_WARNING, "dnssd_clientstub deliver_request: retrieved error code %d", err); + +cleanup: + if (MakeSeparateReturnSocket) + { + if (dnssd_SocketValid(listenfd)) dnssd_close(listenfd); + if (dnssd_SocketValid(errsd)) dnssd_close(errsd); +#if defined(USE_NAMED_ERROR_RETURN_SOCKET) + // syslog(LOG_WARNING, "dnssd_clientstub deliver_request: removing UDS: %s", data); + if (unlink(data) != 0) + syslog(LOG_WARNING, "dnssd_clientstub WARNING: unlink(\"%s\") failed errno %d (%s)", data, dnssd_errno, dnssd_strerror(dnssd_errno)); + // else syslog(LOG_WARNING, "dnssd_clientstub deliver_request: removed UDS: %s", data); +#endif + } + + free(hdr); + return err; +} + +dnssd_sock_t DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) +{ + if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with NULL DNSServiceRef"); return dnssd_InvalidSocket; } + + if (!DNSServiceRefValid(sdRef)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with invalid DNSServiceRef %p %08X %08X", + sdRef, sdRef->sockfd, sdRef->validator); + return dnssd_InvalidSocket; + } + + if (sdRef->primary) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD undefined for kDNSServiceFlagsShareConnection subordinate DNSServiceRef %p", sdRef); + return dnssd_InvalidSocket; + } + + return sdRef->sockfd; +} + +#if _DNS_SD_LIBDISPATCH +static void CallbackWithError(DNSServiceRef sdRef, DNSServiceErrorType error) +{ + DNSServiceOp *sdr = sdRef; + DNSServiceOp *sdrNext; + DNSRecord *rec; + DNSRecord *recnext; + int morebytes; + + while (sdr) + { + // We can't touch the sdr after the callback as it can be deallocated in the callback + sdrNext = sdr->next; + morebytes = 1; + sdr->moreptr = &morebytes; + switch (sdr->op) + { + case resolve_request: + if (sdr->AppCallback) ((DNSServiceResolveReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL, sdr->AppContext); + break; + case query_request: + if (sdr->AppCallback) ((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL, 0, sdr->AppContext); + break; + case addrinfo_request: + if (sdr->AppCallback) ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, NULL, 0, sdr->AppContext); + break; + case browse_request: + if (sdr->AppCallback) ((DNSServiceBrowseReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, NULL, sdr->AppContext); + break; + case reg_service_request: + if (sdr->AppCallback) ((DNSServiceRegisterReply) sdr->AppCallback)(sdr, 0, error, NULL, 0, NULL, sdr->AppContext); + break; + case enumeration_request: + if (sdr->AppCallback) ((DNSServiceDomainEnumReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, sdr->AppContext); + break; + case connection_request: + case connection_delegate_request: + // This means Register Record, walk the list of DNSRecords to do the callback + rec = sdr->rec; + while (rec) + { + recnext = rec->recnext; + if (rec->AppCallback) ((DNSServiceRegisterRecordReply)rec->AppCallback)(sdr, 0, 0, error, rec->AppContext); + // The Callback can call DNSServiceRefDeallocate which in turn frees sdr and all the records. + // Detect that and return early + if (!morebytes) {syslog(LOG_WARNING, "dnssdclientstub:Record: CallbackwithError morebytes zero"); return;} + rec = recnext; + } + break; + case port_mapping_request: + if (sdr->AppCallback) ((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, 0, 0, error, 0, 0, 0, 0, 0, sdr->AppContext); + break; + default: + syslog(LOG_WARNING, "dnssd_clientstub CallbackWithError called with bad op %d", sdr->op); + } + // If DNSServiceRefDeallocate was called in the callback, morebytes will be zero. As the sdRef + // (and its subordinates) have been freed, we should not proceed further. Note that when we + // call the callback with a subordinate sdRef the application can call DNSServiceRefDeallocate + // on the main sdRef and DNSServiceRefDeallocate handles this case by walking all the sdRefs and + // clears the moreptr so that we can terminate here. + // + // If DNSServiceRefDeallocate was not called in the callback, then set moreptr to NULL so that + // we don't access the stack variable after we return from this function. + if (!morebytes) {syslog(LOG_WARNING, "dnssdclientstub:sdRef: CallbackwithError morebytes zero sdr %p", sdr); return;} + else {sdr->moreptr = NULL;} + sdr = sdrNext; + } +} +#endif // _DNS_SD_LIBDISPATCH + +// Handle reply from server, calling application client callback. If there is no reply +// from the daemon on the socket contained in sdRef, the call will block. +DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) +{ + int morebytes = 0; + + if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } + + if (!DNSServiceRefValid(sdRef)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator); + return kDNSServiceErr_BadReference; + } + + if (sdRef->primary) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult undefined for kDNSServiceFlagsShareConnection subordinate DNSServiceRef %p", sdRef); + return kDNSServiceErr_BadReference; + } + + if (!sdRef->ProcessReply) + { + static int num_logs = 0; + if (num_logs < 10) syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with DNSServiceRef with no ProcessReply function"); + if (num_logs < 1000) num_logs++;else sleep(1); + return kDNSServiceErr_BadReference; + } + + do + { + CallbackHeader cbh; + char *data; + + // return NoError on EWOULDBLOCK. This will handle the case + // where a non-blocking socket is told there is data, but it was a false positive. + // On error, read_all will write a message to syslog for us, so don't need to duplicate that here + // Note: If we want to properly support using non-blocking sockets in the future + int result = read_all(sdRef->sockfd, (void *)&cbh.ipc_hdr, sizeof(cbh.ipc_hdr)); + if (result == read_all_fail) + { + // Set the ProcessReply to NULL before callback as the sdRef can get deallocated + // in the callback. + sdRef->ProcessReply = NULL; +#if _DNS_SD_LIBDISPATCH + // Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult + // is not called by the application and hence need to communicate the error. Cancel the + // source so that we don't get any more events + // Note: read_all fails if we could not read from the daemon which can happen if the + // daemon dies or the file descriptor is disconnected (defunct). + if (sdRef->disp_source) + { + dispatch_source_cancel(sdRef->disp_source); + dispatch_release(sdRef->disp_source); + sdRef->disp_source = NULL; + CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning); + } +#endif + // Don't touch sdRef anymore as it might have been deallocated + return kDNSServiceErr_ServiceNotRunning; + } + else if (result == read_all_wouldblock) + { + if (morebytes && sdRef->logcounter < 100) + { + sdRef->logcounter++; + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult error: select indicated data was waiting but read_all returned EWOULDBLOCK"); + } + return kDNSServiceErr_NoError; + } + + ConvertHeaderBytes(&cbh.ipc_hdr); + if (cbh.ipc_hdr.version != VERSION) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult daemon version %d does not match client version %d", cbh.ipc_hdr.version, VERSION); + sdRef->ProcessReply = NULL; + return kDNSServiceErr_Incompatible; + } + + data = malloc(cbh.ipc_hdr.datalen); + if (!data) return kDNSServiceErr_NoMemory; + if (read_all(sdRef->sockfd, data, cbh.ipc_hdr.datalen) < 0) // On error, read_all will write a message to syslog for us + { + // Set the ProcessReply to NULL before callback as the sdRef can get deallocated + // in the callback. + sdRef->ProcessReply = NULL; +#if _DNS_SD_LIBDISPATCH + // Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult + // is not called by the application and hence need to communicate the error. Cancel the + // source so that we don't get any more events + if (sdRef->disp_source) + { + dispatch_source_cancel(sdRef->disp_source); + dispatch_release(sdRef->disp_source); + sdRef->disp_source = NULL; + CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning); + } +#endif + // Don't touch sdRef anymore as it might have been deallocated + free(data); + return kDNSServiceErr_ServiceNotRunning; + } + else + { + const char *ptr = data; + cbh.cb_flags = get_flags (&ptr, data + cbh.ipc_hdr.datalen); + cbh.cb_interface = get_uint32 (&ptr, data + cbh.ipc_hdr.datalen); + cbh.cb_err = get_error_code(&ptr, data + cbh.ipc_hdr.datalen); + + // CAUTION: We have to handle the case where the client calls DNSServiceRefDeallocate from within the callback function. + // To do this we set moreptr to point to morebytes. If the client does call DNSServiceRefDeallocate(), + // then that routine will clear morebytes for us, and cause us to exit our loop. + morebytes = more_bytes(sdRef->sockfd); + if (morebytes) + { + cbh.cb_flags |= kDNSServiceFlagsMoreComing; + sdRef->moreptr = &morebytes; + } + if (ptr) sdRef->ProcessReply(sdRef, &cbh, ptr, data + cbh.ipc_hdr.datalen); + // Careful code here: + // If morebytes is non-zero, that means we set sdRef->moreptr above, and the operation was not + // cancelled out from under us, so now we need to clear sdRef->moreptr so we don't leave a stray + // dangling pointer pointing to a long-gone stack variable. + // If morebytes is zero, then one of two thing happened: + // (a) morebytes was 0 above, so we didn't set sdRef->moreptr, so we don't need to clear it + // (b) morebytes was 1 above, and we set sdRef->moreptr, but the operation was cancelled (with DNSServiceRefDeallocate()), + // so we MUST NOT try to dereference our stale sdRef pointer. + if (morebytes) sdRef->moreptr = NULL; + } + free(data); + } while (morebytes); + + return kDNSServiceErr_NoError; +} + +void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef) +{ + if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefDeallocate called with NULL DNSServiceRef"); return; } + + if (!DNSServiceRefValid(sdRef)) // Also verifies dnssd_SocketValid(sdRef->sockfd) for us too + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefDeallocate called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator); + return; + } + + // If we're in the middle of a DNSServiceProcessResult() invocation for this DNSServiceRef, clear its morebytes flag to break it out of its while loop + if (sdRef->moreptr) *(sdRef->moreptr) = 0; + + if (sdRef->primary) // If this is a subordinate DNSServiceOp, just send a 'stop' command + { + DNSServiceOp **p = &sdRef->primary->next; + while (*p && *p != sdRef) p = &(*p)->next; + if (*p) + { + char *ptr; + size_t len = 0; + ipc_msg_hdr *hdr = create_hdr(cancel_request, &len, &ptr, 0, sdRef); + if (hdr) + { + ConvertHeaderBytes(hdr); + write_all(sdRef->sockfd, (char *)hdr, len); + free(hdr); + } + *p = sdRef->next; + FreeDNSServiceOp(sdRef); + } + } + else // else, make sure to terminate all subordinates as well + { +#if _DNS_SD_LIBDISPATCH + // The cancel handler will close the fd if a dispatch source has been set + if (sdRef->disp_source) + { + // By setting the ProcessReply to NULL, we make sure that we never call + // the application callbacks ever, after returning from this function. We + // assume that DNSServiceRefDeallocate is called from the serial queue + // that was passed to DNSServiceSetDispatchQueue. Hence, dispatch_source_cancel + // should cancel all the blocks on the queue and hence there should be no more + // callbacks when we return from this function. Setting ProcessReply to NULL + // provides extra protection. + sdRef->ProcessReply = NULL; + shutdown(sdRef->sockfd, SHUT_WR); + dispatch_source_cancel(sdRef->disp_source); + dispatch_release(sdRef->disp_source); + sdRef->disp_source = NULL; + } + // if disp_queue is set, it means it used the DNSServiceSetDispatchQueue API. In that case, + // when the source was cancelled, the fd was closed in the handler. Currently the source + // is cancelled only when the mDNSResponder daemon dies + else if (!sdRef->disp_queue) dnssd_close(sdRef->sockfd); +#else + dnssd_close(sdRef->sockfd); +#endif + // Free DNSRecords added in DNSRegisterRecord if they have not + // been freed in DNSRemoveRecord + while (sdRef) + { + DNSServiceOp *p = sdRef; + sdRef = sdRef->next; + // When there is an error reading from the daemon e.g., bad fd, CallbackWithError + // is called which sets moreptr. It might set the moreptr on a subordinate sdRef + // but the application might call DNSServiceRefDeallocate with the main sdRef from + // the callback. Hence, when we loop through the subordinate sdRefs, we need + // to clear the moreptr so that CallbackWithError can terminate itself instead of + // walking through the freed sdRefs. + if (p->moreptr) *(p->moreptr) = 0; + FreeDNSServiceOp(p); + } + } +} + +DNSServiceErrorType DNSSD_API DNSServiceGetProperty(const char *property, void *result, uint32_t *size) +{ + DNSServiceErrorType err; + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceOp *tmp; + uint32_t actualsize; + + if (!property || !result || !size) + return kDNSServiceErr_BadParam; + + len = strlen(property) + 1; + err = ConnectToServer(&tmp, 0, getproperty_request, NULL, NULL, NULL); + if (err) return err; + + hdr = create_hdr(getproperty_request, &len, &ptr, 0, tmp); + if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; } + + put_string(property, &ptr); + err = deliver_request(hdr, tmp); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(tmp); return err; } + + if (read_all(tmp->sockfd, (char*)&actualsize, (int)sizeof(actualsize)) < 0) + { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; } + + actualsize = ntohl(actualsize); + if (read_all(tmp->sockfd, (char*)result, actualsize < *size ? actualsize : *size) < 0) + { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; } + DNSServiceRefDeallocate(tmp); + + // Swap version result back to local process byte order + if (!strcmp(property, kDNSServiceProperty_DaemonVersion) && *size >= 4) + *(uint32_t*)result = ntohl(*(uint32_t*)result); + + *size = actualsize; + return kDNSServiceErr_NoError; +} + +DNSServiceErrorType DNSSD_API DNSServiceGetPID(const uint16_t srcport, int32_t *pid) +{ + char *ptr; + ipc_msg_hdr *hdr; + DNSServiceOp *tmp = NULL; + size_t len = sizeof(int32_t); + + DNSServiceErrorType err = ConnectToServer(&tmp, 0, getpid_request, NULL, NULL, NULL); + if (err) return err; + + hdr = create_hdr(getpid_request, &len, &ptr, 0, tmp); + if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; } + + put_uint16(srcport, &ptr); + err = deliver_request(hdr, tmp); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(tmp); return err; } + + if (read_all(tmp->sockfd, (char*)pid, sizeof(int32_t)) < 0) + { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; } + + DNSServiceRefDeallocate(tmp); + return kDNSServiceErr_NoError; +} + +static void handle_resolve_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *end) +{ + char fullname[kDNSServiceMaxDomainName]; + char target[kDNSServiceMaxDomainName]; + uint16_t txtlen; + union { uint16_t s; u_char b[2]; } port; + unsigned char *txtrecord; + + get_string(&data, end, fullname, kDNSServiceMaxDomainName); + get_string(&data, end, target, kDNSServiceMaxDomainName); + if (!data || data + 2 > end) goto fail; + + port.b[0] = *data++; + port.b[1] = *data++; + txtlen = get_uint16(&data, end); + txtrecord = (unsigned char *)get_rdata(&data, end, txtlen); + + if (!data) goto fail; + ((DNSServiceResolveReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, fullname, target, port.s, txtlen, txtrecord, sdr->AppContext); + return; + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function +fail: + syslog(LOG_WARNING, "dnssd_clientstub handle_resolve_response: error reading result from daemon"); +} + +#if TARGET_OS_EMBEDDED + +static int32_t libSystemVersion = 0; + +// Return true if the iOS application linked against a version of libsystem where P2P +// interfaces were included by default when using kDNSServiceInterfaceIndexAny. +// Using 160.0.0 == 0xa00000 as the version threshold. +static int includeP2PWithIndexAny() +{ + if (libSystemVersion == 0) + libSystemVersion = NSVersionOfLinkTimeLibrary("System"); + + if (libSystemVersion < 0xa00000) + return 1; + else + return 0; +} + +#else // TARGET_OS_EMBEDDED + +// always return false for non iOS platforms +static int includeP2PWithIndexAny() +{ + return 0; +} + +#endif // TARGET_OS_EMBEDDED + +DNSServiceErrorType DNSSD_API DNSServiceResolve +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + DNSServiceResolveReply callBack, + void *context +) +{ + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceErrorType err; + + if (!sdRef || !name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam; + + // Need a real InterfaceID for WakeOnResolve + if ((flags & kDNSServiceFlagsWakeOnResolve) != 0 && + ((interfaceIndex == kDNSServiceInterfaceIndexAny) || + (interfaceIndex == kDNSServiceInterfaceIndexLocalOnly) || + (interfaceIndex == kDNSServiceInterfaceIndexUnicast) || + (interfaceIndex == kDNSServiceInterfaceIndexP2P) || + (interfaceIndex == kDNSServiceInterfaceIndexBLE))) + { + return kDNSServiceErr_BadParam; + } + + if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) + flags |= kDNSServiceFlagsIncludeP2P; + + err = ConnectToServer(sdRef, flags, resolve_request, handle_resolve_response, callBack, context); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL + + // Calculate total message length + len = sizeof(flags); + len += sizeof(interfaceIndex); + len += strlen(name) + 1; + len += strlen(regtype) + 1; + len += strlen(domain) + 1; + + hdr = create_hdr(resolve_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + + put_flags(flags, &ptr); + put_uint32(interfaceIndex, &ptr); + put_string(name, &ptr); + put_string(regtype, &ptr); + put_string(domain, &ptr); + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } + return err; +} + +static void handle_query_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) +{ + uint32_t ttl; + char name[kDNSServiceMaxDomainName]; + uint16_t rrtype, rrclass, rdlen; + const char *rdata; + + get_string(&data, end, name, kDNSServiceMaxDomainName); + rrtype = get_uint16(&data, end); + rrclass = get_uint16(&data, end); + rdlen = get_uint16(&data, end); + rdata = get_rdata(&data, end, rdlen); + ttl = get_uint32(&data, end); + + if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_query_response: error reading result from daemon"); + else ((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, name, rrtype, rrclass, rdlen, rdata, ttl, sdr->AppContext); + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function +} + +DNSServiceErrorType DNSSD_API DNSServiceQueryRecord +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + uint16_t rrtype, + uint16_t rrclass, + DNSServiceQueryRecordReply callBack, + void *context +) +{ + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceErrorType err; + + // NULL name handled below. + if (!sdRef || !callBack) return kDNSServiceErr_BadParam; + + if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) + flags |= kDNSServiceFlagsIncludeP2P; + + err = ConnectToServer(sdRef, flags, query_request, handle_query_response, callBack, context); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL + + if (!name) name = "\0"; + + // Calculate total message length + len = sizeof(flags); + len += sizeof(uint32_t); // interfaceIndex + len += strlen(name) + 1; + len += 2 * sizeof(uint16_t); // rrtype, rrclass + + hdr = create_hdr(query_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + + put_flags(flags, &ptr); + put_uint32(interfaceIndex, &ptr); + put_string(name, &ptr); + put_uint16(rrtype, &ptr); + put_uint16(rrclass, &ptr); + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } + return err; +} + +static void handle_addrinfo_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) +{ + char hostname[kDNSServiceMaxDomainName]; + uint16_t rrtype, rrclass, rdlen; + const char *rdata; + uint32_t ttl; + + get_string(&data, end, hostname, kDNSServiceMaxDomainName); + rrtype = get_uint16(&data, end); + rrclass = get_uint16(&data, end); + rdlen = get_uint16(&data, end); + rdata = get_rdata (&data, end, rdlen); + ttl = get_uint32(&data, end); + (void)rrclass; // Unused + + // We only generate client callbacks for A and AAAA results (including NXDOMAIN results for + // those types, if the client has requested those with the kDNSServiceFlagsReturnIntermediates). + // Other result types, specifically CNAME referrals, are not communicated to the client, because + // the DNSServiceGetAddrInfoReply interface doesn't have any meaningful way to communiate CNAME referrals. + if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_addrinfo_response: error reading result from daemon"); + else if (rrtype == kDNSServiceType_A || rrtype == kDNSServiceType_AAAA) + { + struct sockaddr_in sa4; + struct sockaddr_in6 sa6; + const struct sockaddr *const sa = (rrtype == kDNSServiceType_A) ? (struct sockaddr*)&sa4 : (struct sockaddr*)&sa6; + if (rrtype == kDNSServiceType_A) + { + memset(&sa4, 0, sizeof(sa4)); + #ifndef NOT_HAVE_SA_LEN + sa4.sin_len = sizeof(struct sockaddr_in); + #endif + sa4.sin_family = AF_INET; + // sin_port = 0; + if (!cbh->cb_err) memcpy(&sa4.sin_addr, rdata, rdlen); + } + else + { + memset(&sa6, 0, sizeof(sa6)); + #ifndef NOT_HAVE_SA_LEN + sa6.sin6_len = sizeof(struct sockaddr_in6); + #endif + sa6.sin6_family = AF_INET6; + // sin6_port = 0; + // sin6_flowinfo = 0; + // sin6_scope_id = 0; + if (!cbh->cb_err) + { + memcpy(&sa6.sin6_addr, rdata, rdlen); + if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr)) sa6.sin6_scope_id = cbh->cb_interface; + } + } + // Validation results are always delivered separately from the actual results of the + // DNSServiceGetAddrInfo. Set the "addr" to NULL as per the documentation. + // + // Note: If we deliver validation results along with the "addr" in the future, we need + // a way to differentiate the negative response from validation-only response as both + // has zero address. + if (!(cbh->cb_flags & kDNSServiceFlagsValidate)) + ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, sdr->AppContext); + else + ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, NULL, 0, sdr->AppContext); + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function + } +} + +DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + uint32_t protocol, + const char *hostname, + DNSServiceGetAddrInfoReply callBack, + void *context /* may be NULL */ +) +{ + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceErrorType err; + + if (!sdRef || !hostname || !callBack) return kDNSServiceErr_BadParam; + + err = ConnectToServer(sdRef, flags, addrinfo_request, handle_addrinfo_response, callBack, context); + if (err) + { + return err; // On error ConnectToServer leaves *sdRef set to NULL + } + + // Calculate total message length + len = sizeof(flags); + len += sizeof(uint32_t); // interfaceIndex + len += sizeof(uint32_t); // protocol + len += strlen(hostname) + 1; + + hdr = create_hdr(addrinfo_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + + put_flags(flags, &ptr); + put_uint32(interfaceIndex, &ptr); + put_uint32(protocol, &ptr); + put_string(hostname, &ptr); + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } + return err; +} + +static void handle_browse_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) +{ + char replyName[256], replyType[kDNSServiceMaxDomainName], replyDomain[kDNSServiceMaxDomainName]; + get_string(&data, end, replyName, 256); + get_string(&data, end, replyType, kDNSServiceMaxDomainName); + get_string(&data, end, replyDomain, kDNSServiceMaxDomainName); + if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_browse_response: error reading result from daemon"); + else ((DNSServiceBrowseReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, replyName, replyType, replyDomain, sdr->AppContext); + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function +} + +DNSServiceErrorType DNSSD_API DNSServiceBrowse +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *regtype, + const char *domain, + DNSServiceBrowseReply callBack, + void *context +) +{ + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceErrorType err; + + // NULL domain handled below + if (!sdRef || !regtype || !callBack) return kDNSServiceErr_BadParam; + + if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) + flags |= kDNSServiceFlagsIncludeP2P; + + err = ConnectToServer(sdRef, flags, browse_request, handle_browse_response, callBack, context); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL + + if (!domain) domain = ""; + len = sizeof(flags); + len += sizeof(interfaceIndex); + len += strlen(regtype) + 1; + len += strlen(domain) + 1; + + hdr = create_hdr(browse_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + + put_flags(flags, &ptr); + put_uint32(interfaceIndex, &ptr); + put_string(regtype, &ptr); + put_string(domain, &ptr); + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } + return err; +} + +DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain) +{ + DNSServiceErrorType err; + DNSServiceOp *tmp; + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + + if (!domain) return kDNSServiceErr_BadParam; + len = sizeof(flags) + strlen(domain) + 1; + + err = ConnectToServer(&tmp, 0, setdomain_request, NULL, NULL, NULL); + if (err) return err; + + hdr = create_hdr(setdomain_request, &len, &ptr, 0, tmp); + if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; } + + put_flags(flags, &ptr); + put_string(domain, &ptr); + err = deliver_request(hdr, tmp); // Will free hdr for us + DNSServiceRefDeallocate(tmp); + return err; +} + +static void handle_regservice_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) +{ + char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName]; + get_string(&data, end, name, 256); + get_string(&data, end, regtype, kDNSServiceMaxDomainName); + get_string(&data, end, domain, kDNSServiceMaxDomainName); + if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_regservice_response: error reading result from daemon"); + else ((DNSServiceRegisterReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_err, name, regtype, domain, sdr->AppContext); + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function +} + +DNSServiceErrorType DNSSD_API DNSServiceRegister +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + const char *host, + uint16_t PortInNetworkByteOrder, + uint16_t txtLen, + const void *txtRecord, + DNSServiceRegisterReply callBack, + void *context +) +{ + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceErrorType err; + union { uint16_t s; u_char b[2]; } port = { PortInNetworkByteOrder }; + + if (!sdRef || !regtype) return kDNSServiceErr_BadParam; + if (!name) name = ""; + if (!domain) domain = ""; + if (!host) host = ""; + if (!txtRecord) txtRecord = (void*)""; + + // No callback must have auto-rename + if (!callBack && (flags & kDNSServiceFlagsNoAutoRename)) return kDNSServiceErr_BadParam; + + if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) + flags |= kDNSServiceFlagsIncludeP2P; + + err = ConnectToServer(sdRef, flags, reg_service_request, callBack ? handle_regservice_response : NULL, callBack, context); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); // interfaceIndex + len += strlen(name) + strlen(regtype) + strlen(domain) + strlen(host) + 4; + len += 2 * sizeof(uint16_t); // port, txtLen + len += txtLen; + + hdr = create_hdr(reg_service_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + if (!callBack) hdr->ipc_flags |= IPC_FLAGS_NOREPLY; + + put_flags(flags, &ptr); + put_uint32(interfaceIndex, &ptr); + put_string(name, &ptr); + put_string(regtype, &ptr); + put_string(domain, &ptr); + put_string(host, &ptr); + *ptr++ = port.b[0]; + *ptr++ = port.b[1]; + put_uint16(txtLen, &ptr); + put_rdata(txtLen, txtRecord, &ptr); + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } + return err; +} + +static void handle_enumeration_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) +{ + char domain[kDNSServiceMaxDomainName]; + get_string(&data, end, domain, kDNSServiceMaxDomainName); + if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_enumeration_response: error reading result from daemon"); + else ((DNSServiceDomainEnumReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, domain, sdr->AppContext); + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function +} + +DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callBack, + void *context +) +{ + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceErrorType err; + int f1; + int f2; + + if (!sdRef || !callBack) return kDNSServiceErr_BadParam; + + f1 = (flags & kDNSServiceFlagsBrowseDomains) != 0; + f2 = (flags & kDNSServiceFlagsRegistrationDomains) != 0; + if (f1 + f2 != 1) return kDNSServiceErr_BadParam; + + err = ConnectToServer(sdRef, flags, enumeration_request, handle_enumeration_response, callBack, context); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); + + hdr = create_hdr(enumeration_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + + put_flags(flags, &ptr); + put_uint32(interfaceIndex, &ptr); + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } + return err; +} + +static void ConnectionResponse(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *const data, const char *const end) +{ + (void)data; // Unused + + //printf("ConnectionResponse got %d\n", cbh->ipc_hdr.op); + if (cbh->ipc_hdr.op != reg_record_reply_op) + { + // When using kDNSServiceFlagsShareConnection, need to search the list of associated DNSServiceOps + // to find the one this response is intended for, and then call through to its ProcessReply handler. + // We start with our first subordinate DNSServiceRef -- don't want to accidentally match the parent DNSServiceRef. + DNSServiceOp *op = sdr->next; + while (op && (op->uid.u32[0] != cbh->ipc_hdr.client_context.u32[0] || op->uid.u32[1] != cbh->ipc_hdr.client_context.u32[1])) + op = op->next; + // Note: We may sometimes not find a matching DNSServiceOp, in the case where the client has + // cancelled the subordinate DNSServiceOp, but there are still messages in the pipeline from the daemon + if (op && op->ProcessReply) op->ProcessReply(op, cbh, data, end); + // WARNING: Don't touch op or sdr after this -- client may have called DNSServiceRefDeallocate + return; + } + else + { + DNSRecordRef rec; + for (rec = sdr->rec; rec; rec = rec->recnext) + { + if (rec->uid.u32[0] == cbh->ipc_hdr.client_context.u32[0] && rec->uid.u32[1] == cbh->ipc_hdr.client_context.u32[1]) + break; + } + // The record might have been freed already and hence not an + // error if the record is not found. + if (!rec) + { + syslog(LOG_INFO, "ConnectionResponse: Record not found"); + return; + } + if (rec->sdr != sdr) + { + syslog(LOG_WARNING, "ConnectionResponse: Record sdr mismatch: rec %p sdr %p", rec->sdr, sdr); + return; + } + + if (sdr->op == connection_request || sdr->op == connection_delegate_request) + { + rec->AppCallback(rec->sdr, rec, cbh->cb_flags, cbh->cb_err, rec->AppContext); + } + else + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectionResponse: sdr->op != connection_request"); + rec->AppCallback(rec->sdr, rec, 0, kDNSServiceErr_Unknown, rec->AppContext); + } + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function + } +} + +DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef) +{ + DNSServiceErrorType err; + char *ptr; + size_t len = 0; + ipc_msg_hdr *hdr; + + if (!sdRef) return kDNSServiceErr_BadParam; + err = ConnectToServer(sdRef, 0, connection_request, ConnectionResponse, NULL, NULL); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL + + hdr = create_hdr(connection_request, &len, &ptr, 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } + return err; +} + +#if APPLE_OSX_mDNSResponder && !TARGET_IPHONE_SIMULATOR +DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid) +{ + char *ptr; + size_t len = 0; + ipc_msg_hdr *hdr; + + if (!sdRef) return kDNSServiceErr_BadParam; + DNSServiceErrorType err = ConnectToServer(sdRef, 0, connection_delegate_request, ConnectionResponse, NULL, NULL); + if (err) + { + return err; // On error ConnectToServer leaves *sdRef set to NULL + } + + // Only one of the two options can be set. If pid is zero, uuid is used. + // If both are specified only pid will be used. We send across the pid + // so that the daemon knows what to read from the socket. + + len += sizeof(int32_t); + + hdr = create_hdr(connection_delegate_request, &len, &ptr, 0, *sdRef); + if (!hdr) + { + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; + return kDNSServiceErr_NoMemory; + } + + if (pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED, &pid, sizeof(pid)) == -1) + { + syslog(LOG_WARNING, "dnssdclientstub: Could not setsockopt() for PID[%d], no entitlements or process(pid) invalid errno:%d (%s)", pid, errno, strerror(errno)); + // Free the hdr in case we return before calling deliver_request() + if (hdr) + free(hdr); + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; + return kDNSServiceErr_NoAuth; + } + + if (!pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED_UUID, uuid, sizeof(uuid_t)) == -1) + { + syslog(LOG_WARNING, "dnssdclientstub: Could not setsockopt() for UUID, no entitlements or process(uuid) invalid errno:%d (%s) ", errno, strerror(errno)); + // Free the hdr in case we return before calling deliver_request() + if (hdr) + free(hdr); + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; + return kDNSServiceErr_NoAuth; + } + + put_uint32(pid, &ptr); + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) + { + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; + } + return err; +} +#elif TARGET_IPHONE_SIMULATOR // This hack is for Simulator platform only +DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid) +{ + (void) pid; + (void) uuid; + return DNSServiceCreateConnection(sdRef); +} +#endif + +DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord +( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + DNSServiceRegisterRecordReply callBack, + void *context +) +{ + char *ptr; + size_t len; + ipc_msg_hdr *hdr = NULL; + DNSRecordRef rref = NULL; + DNSRecord **p; + int f1 = (flags & kDNSServiceFlagsShared) != 0; + int f2 = (flags & kDNSServiceFlagsUnique) != 0; + if (f1 + f2 != 1) return kDNSServiceErr_BadParam; + + if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) + flags |= kDNSServiceFlagsIncludeP2P; + + if (!sdRef || !RecordRef || !fullname || (!rdata && rdlen) || !callBack) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with NULL parameter"); + return kDNSServiceErr_BadParam; + } + + if (!DNSServiceRefValid(sdRef)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator); + return kDNSServiceErr_BadReference; + } + + if (sdRef->op != connection_request && sdRef->op != connection_delegate_request) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with non-DNSServiceCreateConnection DNSServiceRef %p %d", sdRef, sdRef->op); + return kDNSServiceErr_BadReference; + } + + *RecordRef = NULL; + + len = sizeof(DNSServiceFlags); + len += 2 * sizeof(uint32_t); // interfaceIndex, ttl + len += 3 * sizeof(uint16_t); // rrtype, rrclass, rdlen + len += strlen(fullname) + 1; + len += rdlen; + + // Bump up the uid. Normally for shared operations (kDNSServiceFlagsShareConnection), this + // is done in ConnectToServer. For DNSServiceRegisterRecord, ConnectToServer has already + // been called. As multiple DNSServiceRegisterRecords can be multiplexed over a single + // connection, we need a way to demultiplex the response so that the callback corresponding + // to the right DNSServiceRegisterRecord instance can be called. Use the same mechanism that + // is used by kDNSServiceFlagsShareConnection. create_hdr copies the uid value to ipc + // hdr->client_context which will be returned in the ipc response. + if (++sdRef->uid.u32[0] == 0) + ++sdRef->uid.u32[1]; + hdr = create_hdr(reg_record_request, &len, &ptr, 1, sdRef); + if (!hdr) return kDNSServiceErr_NoMemory; + + put_flags(flags, &ptr); + put_uint32(interfaceIndex, &ptr); + put_string(fullname, &ptr); + put_uint16(rrtype, &ptr); + put_uint16(rrclass, &ptr); + put_uint16(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + put_uint32(ttl, &ptr); + + rref = malloc(sizeof(DNSRecord)); + if (!rref) { free(hdr); return kDNSServiceErr_NoMemory; } + rref->AppContext = context; + rref->AppCallback = callBack; + rref->record_index = sdRef->max_index++; + rref->sdr = sdRef; + rref->recnext = NULL; + *RecordRef = rref; + // Remember the uid that we are sending across so that we can match + // when the response comes back. + rref->uid = sdRef->uid; + hdr->reg_index = rref->record_index; + + p = &(sdRef)->rec; + while (*p) p = &(*p)->recnext; + *p = rref; + + return deliver_request(hdr, sdRef); // Will free hdr for us +} + +// sdRef returned by DNSServiceRegister() +DNSServiceErrorType DNSSD_API DNSServiceAddRecord +( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, + const void *rdata, + uint32_t ttl +) +{ + ipc_msg_hdr *hdr; + size_t len = 0; + char *ptr; + DNSRecordRef rref; + DNSRecord **p; + + if (!sdRef || !RecordRef || (!rdata && rdlen)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL parameter"); + return kDNSServiceErr_BadParam; + } + if (sdRef->op != reg_service_request) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with non-DNSServiceRegister DNSServiceRef %p %d", sdRef, sdRef->op); + return kDNSServiceErr_BadReference; + } + + if (!DNSServiceRefValid(sdRef)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator); + return kDNSServiceErr_BadReference; + } + + *RecordRef = NULL; + + len += 2 * sizeof(uint16_t); // rrtype, rdlen + len += rdlen; + len += sizeof(uint32_t); + len += sizeof(DNSServiceFlags); + + hdr = create_hdr(add_record_request, &len, &ptr, 1, sdRef); + if (!hdr) return kDNSServiceErr_NoMemory; + put_flags(flags, &ptr); + put_uint16(rrtype, &ptr); + put_uint16(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + put_uint32(ttl, &ptr); + + rref = malloc(sizeof(DNSRecord)); + if (!rref) { free(hdr); return kDNSServiceErr_NoMemory; } + rref->AppContext = NULL; + rref->AppCallback = NULL; + rref->record_index = sdRef->max_index++; + rref->sdr = sdRef; + rref->recnext = NULL; + *RecordRef = rref; + hdr->reg_index = rref->record_index; + + p = &(sdRef)->rec; + while (*p) p = &(*p)->recnext; + *p = rref; + + return deliver_request(hdr, sdRef); // Will free hdr for us +} + +// DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord +DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord +( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags, + uint16_t rdlen, + const void *rdata, + uint32_t ttl +) +{ + ipc_msg_hdr *hdr; + size_t len = 0; + char *ptr; + + if (!sdRef || (!rdata && rdlen)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with NULL parameter"); + return kDNSServiceErr_BadParam; + } + + if (!DNSServiceRefValid(sdRef)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator); + return kDNSServiceErr_BadReference; + } + + // Note: RecordRef is allowed to be NULL + + len += sizeof(uint16_t); + len += rdlen; + len += sizeof(uint32_t); + len += sizeof(DNSServiceFlags); + + hdr = create_hdr(update_record_request, &len, &ptr, 1, sdRef); + if (!hdr) return kDNSServiceErr_NoMemory; + hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX; + put_flags(flags, &ptr); + put_uint16(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + put_uint32(ttl, &ptr); + return deliver_request(hdr, sdRef); // Will free hdr for us +} + +DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord +( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags +) +{ + ipc_msg_hdr *hdr; + size_t len = 0; + char *ptr; + DNSServiceErrorType err; + + if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } + if (!RecordRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with NULL DNSRecordRef"); return kDNSServiceErr_BadParam; } + if (!sdRef->max_index) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with bad DNSServiceRef"); return kDNSServiceErr_BadReference; } + + if (!DNSServiceRefValid(sdRef)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator); + return kDNSServiceErr_BadReference; + } + + len += sizeof(flags); + hdr = create_hdr(remove_record_request, &len, &ptr, 1, sdRef); + if (!hdr) return kDNSServiceErr_NoMemory; + hdr->reg_index = RecordRef->record_index; + put_flags(flags, &ptr); + err = deliver_request(hdr, sdRef); // Will free hdr for us + if (!err) + { + // This RecordRef could have been allocated in DNSServiceRegisterRecord or DNSServiceAddRecord. + // If so, delink from the list before freeing + DNSRecord **p = &sdRef->rec; + while (*p && *p != RecordRef) p = &(*p)->recnext; + if (*p) *p = RecordRef->recnext; + free(RecordRef); + } + return err; +} + +DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord +( + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata +) +{ + DNSServiceErrorType err; + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceOp *tmp = NULL; + + if (!fullname || (!rdata && rdlen)) return kDNSServiceErr_BadParam; + + err = ConnectToServer(&tmp, flags, reconfirm_record_request, NULL, NULL, NULL); + if (err) return err; + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); + len += strlen(fullname) + 1; + len += 3 * sizeof(uint16_t); + len += rdlen; + hdr = create_hdr(reconfirm_record_request, &len, &ptr, 0, tmp); + if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; } + + put_flags(flags, &ptr); + put_uint32(interfaceIndex, &ptr); + put_string(fullname, &ptr); + put_uint16(rrtype, &ptr); + put_uint16(rrclass, &ptr); + put_uint16(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + + err = deliver_request(hdr, tmp); // Will free hdr for us + DNSServiceRefDeallocate(tmp); + return err; +} + + +static void handle_port_mapping_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) +{ + union { uint32_t l; u_char b[4]; } addr; + uint8_t protocol; + union { uint16_t s; u_char b[2]; } internalPort; + union { uint16_t s; u_char b[2]; } externalPort; + uint32_t ttl; + + if (!data || data + 13 > end) goto fail; + + addr.b[0] = *data++; + addr.b[1] = *data++; + addr.b[2] = *data++; + addr.b[3] = *data++; + protocol = *data++; + internalPort.b[0] = *data++; + internalPort.b[1] = *data++; + externalPort.b[0] = *data++; + externalPort.b[1] = *data++; + ttl = get_uint32(&data, end); + if (!data) goto fail; + + ((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, addr.l, protocol, internalPort.s, externalPort.s, ttl, sdr->AppContext); + return; + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function + + fail : + syslog(LOG_WARNING, "dnssd_clientstub handle_port_mapping_response: error reading result from daemon"); +} + +DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + uint32_t protocol, /* TCP and/or UDP */ + uint16_t internalPortInNetworkByteOrder, + uint16_t externalPortInNetworkByteOrder, + uint32_t ttl, /* time to live in seconds */ + DNSServiceNATPortMappingReply callBack, + void *context /* may be NULL */ +) +{ + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + union { uint16_t s; u_char b[2]; } internalPort = { internalPortInNetworkByteOrder }; + union { uint16_t s; u_char b[2]; } externalPort = { externalPortInNetworkByteOrder }; + + DNSServiceErrorType err = ConnectToServer(sdRef, flags, port_mapping_request, handle_port_mapping_response, callBack, context); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL + + len = sizeof(flags); + len += sizeof(interfaceIndex); + len += sizeof(protocol); + len += sizeof(internalPort); + len += sizeof(externalPort); + len += sizeof(ttl); + + hdr = create_hdr(port_mapping_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + + put_flags(flags, &ptr); + put_uint32(interfaceIndex, &ptr); + put_uint32(protocol, &ptr); + *ptr++ = internalPort.b[0]; + *ptr++ = internalPort.b[1]; + *ptr++ = externalPort.b[0]; + *ptr++ = externalPort.b[1]; + put_uint32(ttl, &ptr); + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } + return err; +} + +#if _DNS_SD_LIBDISPATCH +DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue +( + DNSServiceRef service, + dispatch_queue_t queue +) +{ + int dnssd_fd = DNSServiceRefSockFD(service); + if (dnssd_fd == dnssd_InvalidSocket) return kDNSServiceErr_BadParam; + if (!queue) + { + syslog(LOG_WARNING, "dnssd_clientstub: DNSServiceSetDispatchQueue dispatch queue NULL"); + return kDNSServiceErr_BadParam; + } + if (service->disp_queue) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSetDispatchQueue dispatch queue set already"); + return kDNSServiceErr_BadParam; + } + if (service->disp_source) + { + syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch source set already"); + return kDNSServiceErr_BadParam; + } + service->disp_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, dnssd_fd, 0, queue); + if (!service->disp_source) + { + syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch_source_create failed"); + return kDNSServiceErr_NoMemory; + } + service->disp_queue = queue; + dispatch_source_set_event_handler(service->disp_source, ^{DNSServiceProcessResult(service);}); + dispatch_source_set_cancel_handler(service->disp_source, ^{dnssd_close(dnssd_fd);}); + dispatch_resume(service->disp_source); + return kDNSServiceErr_NoError; +} +#endif // _DNS_SD_LIBDISPATCH + +#if !defined(_WIN32) + +static void DNSSD_API SleepKeepaliveCallback(DNSServiceRef sdRef, DNSRecordRef rec, const DNSServiceFlags flags, + DNSServiceErrorType errorCode, void *context) +{ + SleepKAContext *ka = (SleepKAContext *)context; + (void)rec; // Unused + (void)flags; // Unused + + if (sdRef->kacontext != context) + syslog(LOG_WARNING, "SleepKeepaliveCallback context mismatch"); + + if (ka->AppCallback) + ((DNSServiceSleepKeepaliveReply)ka->AppCallback)(sdRef, errorCode, ka->AppContext); +} + +DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + int fd, + unsigned int timeout, + DNSServiceSleepKeepaliveReply callBack, + void *context +) +{ + char source_str[INET6_ADDRSTRLEN]; + char target_str[INET6_ADDRSTRLEN]; + struct sockaddr_storage lss; + struct sockaddr_storage rss; + socklen_t len1, len2; + unsigned int len, proxyreclen; + char buf[256]; + DNSServiceErrorType err; + DNSRecordRef record = NULL; + char name[10]; + char recname[128]; + SleepKAContext *ka; + unsigned int i, unique; + + + (void) flags; //unused + if (!timeout) return kDNSServiceErr_BadParam; + + + len1 = sizeof(lss); + if (getsockname(fd, (struct sockaddr *)&lss, &len1) < 0) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive: getsockname %d\n", errno); + return kDNSServiceErr_BadParam; + } + + len2 = sizeof(rss); + if (getpeername(fd, (struct sockaddr *)&rss, &len2) < 0) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive: getpeername %d\n", errno); + return kDNSServiceErr_BadParam; + } + + if (len1 != len2) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive local/remote info not same"); + return kDNSServiceErr_Unknown; + } + + unique = 0; + if (lss.ss_family == AF_INET) + { + struct sockaddr_in *sl = (struct sockaddr_in *)&lss; + struct sockaddr_in *sr = (struct sockaddr_in *)&rss; + unsigned char *ptr = (unsigned char *)&sl->sin_addr; + + if (!inet_ntop(AF_INET, (const void *)&sr->sin_addr, target_str, sizeof (target_str))) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive remote info failed %d", errno); + return kDNSServiceErr_Unknown; + } + if (!inet_ntop(AF_INET, (const void *)&sl->sin_addr, source_str, sizeof (source_str))) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive local info failed %d", errno); + return kDNSServiceErr_Unknown; + } + // Sum of all bytes in the local address and port should result in a unique + // number in the local network + for (i = 0; i < sizeof(struct in_addr); i++) + unique += ptr[i]; + unique += sl->sin_port; + len = snprintf(buf+1, sizeof(buf) - 1, "t=%u h=%s d=%s l=%u r=%u", timeout, source_str, target_str, ntohs(sl->sin_port), ntohs(sr->sin_port)); + } + else + { + struct sockaddr_in6 *sl6 = (struct sockaddr_in6 *)&lss; + struct sockaddr_in6 *sr6 = (struct sockaddr_in6 *)&rss; + unsigned char *ptr = (unsigned char *)&sl6->sin6_addr; + + if (!inet_ntop(AF_INET6, (const void *)&sr6->sin6_addr, target_str, sizeof (target_str))) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive remote6 info failed %d", errno); + return kDNSServiceErr_Unknown; + } + if (!inet_ntop(AF_INET6, (const void *)&sl6->sin6_addr, source_str, sizeof (source_str))) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive local6 info failed %d", errno); + return kDNSServiceErr_Unknown; + } + for (i = 0; i < sizeof(struct in6_addr); i++) + unique += ptr[i]; + unique += sl6->sin6_port; + len = snprintf(buf+1, sizeof(buf) - 1, "t=%u H=%s D=%s l=%u r=%u", timeout, source_str, target_str, ntohs(sl6->sin6_port), ntohs(sr6->sin6_port)); + } + + if (len >= (sizeof(buf) - 1)) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit local/remote info"); + return kDNSServiceErr_Unknown; + } + // Include the NULL byte also in the first byte. The total length of the record includes the + // first byte also. + buf[0] = len + 1; + proxyreclen = len + 2; + + len = snprintf(name, sizeof(name), "%u", unique); + if (len >= sizeof(name)) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit unique"); + return kDNSServiceErr_Unknown; + } + + len = snprintf(recname, sizeof(recname), "%s.%s", name, "_keepalive._dns-sd._udp.local"); + if (len >= sizeof(recname)) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit name"); + return kDNSServiceErr_Unknown; + } + + ka = malloc(sizeof(SleepKAContext)); + if (!ka) return kDNSServiceErr_NoMemory; + ka->AppCallback = callBack; + ka->AppContext = context; + + err = DNSServiceCreateConnection(sdRef); + if (err) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive cannot create connection"); + free(ka); + return err; + } + + // we don't care about the "record". When sdRef gets deallocated later, it will be freed too + err = DNSServiceRegisterRecord(*sdRef, &record, kDNSServiceFlagsUnique, 0, recname, + kDNSServiceType_NULL, kDNSServiceClass_IN, proxyreclen, buf, kDNSServiceInterfaceIndexAny, SleepKeepaliveCallback, ka); + if (err) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive cannot create connection"); + free(ka); + return err; + } + (*sdRef)->kacontext = ka; + return kDNSServiceErr_NoError; +} +#endif diff --git a/3rdparty/QtZeroConf/bonjour-sdk/dnssd_ipc.c b/3rdparty/QtZeroConf/bonjour-sdk/dnssd_ipc.c new file mode 100644 index 000000000..0fd75824f --- /dev/null +++ b/3rdparty/QtZeroConf/bonjour-sdk/dnssd_ipc.c @@ -0,0 +1,161 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dnssd_ipc.h" + +#if defined(_WIN32) + +char *win32_strerror(int inErrorCode) +{ + static char buffer[1024]; + DWORD n; + memset(buffer, 0, sizeof(buffer)); + n = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + (DWORD) inErrorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buffer, + sizeof(buffer), + NULL); + if (n > 0) + { + // Remove any trailing CR's or LF's since some messages have them. + while ((n > 0) && isspace(((unsigned char *) buffer)[n - 1])) + buffer[--n] = '\0'; + } + return buffer; +} + +#endif + +void put_uint32(const uint32_t l, char **ptr) +{ + (*ptr)[0] = (char)((l >> 24) & 0xFF); + (*ptr)[1] = (char)((l >> 16) & 0xFF); + (*ptr)[2] = (char)((l >> 8) & 0xFF); + (*ptr)[3] = (char)((l ) & 0xFF); + *ptr += sizeof(uint32_t); +} + +uint32_t get_uint32(const char **ptr, const char *end) +{ + if (!*ptr || *ptr + sizeof(uint32_t) > end) + { + *ptr = NULL; + return(0); + } + else + { + uint8_t *p = (uint8_t*) *ptr; + *ptr += sizeof(uint32_t); + return((uint32_t) ((uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | p[3])); + } +} + +void put_uint16(uint16_t s, char **ptr) +{ + (*ptr)[0] = (char)((s >> 8) & 0xFF); + (*ptr)[1] = (char)((s ) & 0xFF); + *ptr += sizeof(uint16_t); +} + +uint16_t get_uint16(const char **ptr, const char *end) +{ + if (!*ptr || *ptr + sizeof(uint16_t) > end) + { + *ptr = NULL; + return(0); + } + else + { + uint8_t *p = (uint8_t*) *ptr; + *ptr += sizeof(uint16_t); + return((uint16_t) ((uint16_t)p[0] << 8 | p[1])); + } +} + +int put_string(const char *str, char **ptr) +{ + if (!str) str = ""; + strcpy(*ptr, str); + *ptr += strlen(str) + 1; + return 0; +} + +int get_string(const char **ptr, const char *const end, char *buffer, int buflen) +{ + if (!*ptr) + { + *buffer = 0; + return(-1); + } + else + { + char *lim = buffer + buflen; // Calculate limit + while (*ptr < end && buffer < lim) + { + char c = *buffer++ = *(*ptr)++; + if (c == 0) return(0); // Success + } + if (buffer == lim) buffer--; + *buffer = 0; // Failed, so terminate string, + *ptr = NULL; // clear pointer, + return(-1); // and return failure indication + } +} + +void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr) +{ + memcpy(*ptr, rdata, rdlen); + *ptr += rdlen; +} + +const char *get_rdata(const char **ptr, const char *end, int rdlen) +{ + if (!*ptr || *ptr + rdlen > end) + { + *ptr = NULL; + return(0); + } + else + { + const char *rd = *ptr; + *ptr += rdlen; + return rd; + } +} + +void ConvertHeaderBytes(ipc_msg_hdr *hdr) +{ + hdr->version = htonl(hdr->version); + hdr->datalen = htonl(hdr->datalen); + hdr->ipc_flags = htonl(hdr->ipc_flags); + hdr->op = htonl(hdr->op ); + hdr->reg_index = htonl(hdr->reg_index); +} diff --git a/3rdparty/QtZeroConf/bonjour-sdk/dnssd_ipc.h b/3rdparty/QtZeroConf/bonjour-sdk/dnssd_ipc.h new file mode 100644 index 000000000..96466a944 --- /dev/null +++ b/3rdparty/QtZeroConf/bonjour-sdk/dnssd_ipc.h @@ -0,0 +1,220 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DNSSD_IPC_H +#define DNSSD_IPC_H + +#include "dns_sd.h" + +// +// Common cross platform services +// +#if defined(WIN32) +# include +# define dnssd_InvalidSocket INVALID_SOCKET +# define dnssd_SocketValid(s) ((s) != INVALID_SOCKET) +# define dnssd_EWOULDBLOCK WSAEWOULDBLOCK +# define dnssd_EINTR WSAEINTR +# define dnssd_ECONNRESET WSAECONNRESET +# define dnssd_socklen_t int +# define dnssd_close(sock) closesocket(sock) +# define dnssd_errno WSAGetLastError() +# define dnssd_strerror(X) win32_strerror(X) +# define ssize_t int +# define getpid _getpid +# define unlink _unlink +extern char *win32_strerror(int inErrorCode); +#else +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# define dnssd_InvalidSocket -1 +# define dnssd_SocketValid(s) ((s) >= 0) +# define dnssd_EWOULDBLOCK EWOULDBLOCK +# define dnssd_EINTR EINTR +# define dnssd_ECONNRESET ECONNRESET +# define dnssd_EPIPE EPIPE +# define dnssd_socklen_t unsigned int +# define dnssd_close(sock) close(sock) +# define dnssd_errno errno +# define dnssd_strerror(X) strerror(X) +#endif + +#if defined(USE_TCP_LOOPBACK) +# define AF_DNSSD AF_INET +# define MDNS_TCP_SERVERADDR "127.0.0.1" +# define MDNS_TCP_SERVERPORT 5354 +# define LISTENQ 5 +# define dnssd_sockaddr_t struct sockaddr_in +#else +# define AF_DNSSD AF_LOCAL +# ifndef MDNS_UDS_SERVERPATH +# define MDNS_UDS_SERVERPATH "/var/run/mDNSResponder" +# endif +# define MDNS_UDS_SERVERPATH_ENVVAR "DNSSD_UDS_PATH" +# define LISTENQ 100 +// longest legal control path length +# define MAX_CTLPATH (sizeof(((struct sockaddr_un*)0)->sun_path)) +# define dnssd_sockaddr_t struct sockaddr_un +#endif + +// Compatibility workaround +#ifndef AF_LOCAL +#define AF_LOCAL AF_UNIX +#endif + +// General UDS constants +#define TXT_RECORD_INDEX ((uint32_t)(-1)) // record index for default text record + +// IPC data encoding constants and types +#define VERSION 1 +#define IPC_FLAGS_NOREPLY 1 // set flag if no asynchronous replies are to be sent to client + +// Structure packing macro. If we're not using GNUC, it's not fatal. Most compilers naturally pack the on-the-wire +// structures correctly anyway, so a plain "struct" is usually fine. In the event that structures are not packed +// correctly, our compile-time assertion checks will catch it and prevent inadvertent generation of non-working code. +#ifndef packedstruct + #if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) + #define packedstruct struct __attribute__((__packed__)) + #define packedunion union __attribute__((__packed__)) + #else + #define packedstruct struct + #define packedunion union + #endif +#endif + +typedef enum +{ + request_op_none = 0, // No request yet received on this connection + connection_request = 1, // connected socket via DNSServiceConnect() + reg_record_request, // reg/remove record only valid for connected sockets + remove_record_request, + enumeration_request, + reg_service_request, + browse_request, + resolve_request, + query_request, + reconfirm_record_request, + add_record_request, + update_record_request, + setdomain_request, // Up to here is in Tiger and B4W 1.0.3 + getproperty_request, // New in B4W 1.0.4 + port_mapping_request, // New in Leopard and B4W 2.0 + addrinfo_request, + send_bpf, // New in SL + getpid_request, + release_request, + connection_delegate_request, + + cancel_request = 63 +} request_op_t; + +typedef enum +{ + enumeration_reply_op = 64, + reg_service_reply_op, + browse_reply_op, + resolve_reply_op, + query_reply_op, + reg_record_reply_op, // Up to here is in Tiger and B4W 1.0.3 + getproperty_reply_op, // New in B4W 1.0.4 + port_mapping_reply_op, // New in Leopard and B4W 2.0 + addrinfo_reply_op +} reply_op_t; + +#if defined(_WIN64) +# pragma pack(push,4) +#endif + +// Define context object big enough to hold a 64-bit pointer, +// to accomodate 64-bit clients communicating with 32-bit daemon. +// There's no reason for the daemon to ever be a 64-bit process, but its clients might be +typedef packedunion +{ + void *context; + uint32_t u32[2]; +} client_context_t; + +typedef packedstruct +{ + uint32_t version; + uint32_t datalen; + uint32_t ipc_flags; + uint32_t op; // request_op_t or reply_op_t + client_context_t client_context; // context passed from client, returned by server in corresponding reply + uint32_t reg_index; // identifier for a record registered via DNSServiceRegisterRecord() on a + // socket connected by DNSServiceCreateConnection(). Must be unique in the scope of the connection, such that and + // index/socket pair uniquely identifies a record. (Used to select records for removal by DNSServiceRemoveRecord()) +} ipc_msg_hdr; + +#if defined(_WIN64) +# pragma pack(pop) +#endif + +// routines to write to and extract data from message buffers. +// caller responsible for bounds checking. +// ptr is the address of the pointer to the start of the field. +// it is advanced to point to the next field, or the end of the message + +void put_uint32(const uint32_t l, char **ptr); +uint32_t get_uint32(const char **ptr, const char *end); + +void put_uint16(uint16_t s, char **ptr); +uint16_t get_uint16(const char **ptr, const char *end); + +#define put_flags put_uint32 +#define get_flags get_uint32 + +#define put_error_code put_uint32 +#define get_error_code get_uint32 + +int put_string(const char *str, char **ptr); +int get_string(const char **ptr, const char *const end, char *buffer, int buflen); + +void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr); +const char *get_rdata(const char **ptr, const char *end, int rdlen); // return value is rdata pointed to by *ptr - +// rdata is not copied from buffer. + +void ConvertHeaderBytes(ipc_msg_hdr *hdr); + +struct CompileTimeAssertionChecks_dnssd_ipc +{ + // Check that the compiler generated our on-the-wire packet format structure definitions + // properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries. + char assert0[(sizeof(client_context_t) == 8) ? 1 : -1]; + char assert1[(sizeof(ipc_msg_hdr) == 28) ? 1 : -1]; +}; + +#endif // DNSSD_IPC_H diff --git a/3rdparty/QtZeroConf/bonjour.cpp b/3rdparty/QtZeroConf/bonjour.cpp new file mode 100644 index 000000000..2698af00b --- /dev/null +++ b/3rdparty/QtZeroConf/bonjour.cpp @@ -0,0 +1,384 @@ +/************************************************************************************************** +--------------------------------------------------------------------------------------------------- + Copyright (C) 2015 Jonathan Bagg + This file is part of QtZeroConf. + + QtZeroConf is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QtZeroConf is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with QtZeroConf. If not, see . +--------------------------------------------------------------------------------------------------- + Project name : QtZeroConf + File name : bonjour.cpp + Created : 20 July 2015 + Author(s) : Jonathan Bagg +--------------------------------------------------------------------------------------------------- + Wrapper for Apple's Bonjour library for use on Windows, MACs and iOS +--------------------------------------------------------------------------------------------------- +**************************************************************************************************/ +#include "qzeroconf.h" +#include "bonjour_p.h" + + + +void Resolver::resolverReady() +{ + DNSServiceErrorType err = DNSServiceProcessResult(DNSresolverRef); + if (err != kDNSServiceErr_NoError) + cleanUp(); +} + +void Resolver::addressReady() +{ + DNSServiceErrorType err = DNSServiceProcessResult(DNSaddressRef); + if (err != kDNSServiceErr_NoError) + cleanUp(); +} + +void Resolver::cleanUp() +{ + DNSServiceRefDeallocate(DNSresolverRef); + DNSServiceRefDeallocate(DNSaddressRef); + // the QSocketNotifiers resolverNotifier and addressNotifier get deleted when the QSharedPointer gets deleted along with the Resolver. No need to clear them here. + QString key = zcs->name() + QString::number(zcs->interfaceIndex()); + ref->resolvers.remove(key); + delete this; +} + +QZeroConfPrivate::QZeroConfPrivate(QZeroConf *parent) +{ + pub = parent; +} + +void QZeroConfPrivate::bsRead() +{ + DNSServiceErrorType err = DNSServiceProcessResult(dnssRef); + if (err != kDNSServiceErr_NoError) { + cleanUp(dnssRef); + emit pub->error(QZeroConf::serviceRegistrationFailed); + } +} + +void QZeroConfPrivate::browserRead() +{ + DNSServiceErrorType err = DNSServiceProcessResult(browser); + if (err != kDNSServiceErr_NoError) { + cleanUp(browser); + emit pub->error(QZeroConf::browserFailed); + } +} + +void QZeroConfPrivate::resolve(QZeroConfService zcs) +{ + DNSServiceErrorType err; + Resolver *resolver = new Resolver; + QString key = zcs->name() + QString::number(zcs->interfaceIndex()); + resolvers.insert(key, resolver); + resolver->ref = this; + resolver->zcs = zcs; + + err = DNSServiceResolve(&resolver->DNSresolverRef, kDNSServiceFlagsTimeout, zcs->interfaceIndex(), zcs->name().toUtf8(), zcs->type().toUtf8(), zcs->domain().toUtf8(), static_cast(resolverCallback), resolver); + if (err == kDNSServiceErr_NoError) { + int sockfd = DNSServiceRefSockFD(resolver->DNSresolverRef); + if (sockfd == -1) { + resolver->cleanUp(); + } + else { + resolver->resolverNotifier = QSharedPointer::create(sockfd, QSocketNotifier::Read); + connect(resolver->resolverNotifier.data(), &QSocketNotifier::activated, resolver, &Resolver::resolverReady); + } + } + else { + resolver->cleanUp(); + } +} + +void DNSSD_API QZeroConfPrivate::registerCallback(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *, const char *, const char *, void *userdata) +{ + QZeroConfPrivate *ref = static_cast(userdata); + + if (errorCode == kDNSServiceErr_NoError) { + emit ref->pub->servicePublished(); + } + else { + ref->cleanUp(ref->dnssRef); + emit ref->pub->error(QZeroConf::serviceRegistrationFailed); + } +} + +void DNSSD_API QZeroConfPrivate::browseCallback(DNSServiceRef, DNSServiceFlags flags, + quint32 interfaceIndex, DNSServiceErrorType err, const char *name, + const char *type, const char *domain, void *userdata) +{ + QString key; + QZeroConfService zcs; + QZeroConfPrivate *ref = static_cast(userdata); + + //qDebug() << name; + if (err == kDNSServiceErr_NoError) { + key = name + QString::number(interfaceIndex); + if (flags & kDNSServiceFlagsAdd) { + if (!ref->pub->services.contains(key)) { + zcs = QZeroConfService::create(); + zcs->m_name = name; + zcs->m_type = type; + zcs->m_domain = domain; + zcs->m_interfaceIndex = interfaceIndex; + ref->resolve(zcs); + } + } + else if (ref->pub->services.contains(key)) { + zcs = ref->pub->services[key]; + ref->pub->services.remove(key); + if (ref->resolvers.contains(key)) + ref->resolvers[key]->cleanUp(); + emit ref->pub->serviceRemoved(zcs); + } + } + else { + ref->cleanUp(ref->browser); + emit ref->pub->error(QZeroConf::browserFailed); + } +} + +void DNSSD_API QZeroConfPrivate::resolverCallback(DNSServiceRef, DNSServiceFlags, + quint32 interfaceIndex, DNSServiceErrorType err, const char *, + const char *hostName, quint16 port, quint16 txtLen, + const unsigned char *txtRecord, void *userdata) +{ + Resolver *resolver = static_cast(userdata); + + if (err != kDNSServiceErr_NoError) { + resolver->cleanUp(); + return; + } + + uchar recLen; + while (txtLen > 0) // add txt records + { + recLen = txtRecord[0]; + txtRecord++; + QByteArray avahiText(reinterpret_cast(txtRecord), recLen); + const int pos = avahiText.indexOf('='); + if (pos < 0) + resolver->zcs->m_txt[avahiText] = ""; + else + resolver->zcs->m_txt[avahiText.left(pos)] = avahiText.mid(pos + 1, -1); + + txtLen-= recLen + 1; + txtRecord+= recLen; + } + resolver->zcs->m_host = hostName; + resolver->zcs->m_port = qFromBigEndian(port); + + if (resolver->DNSaddressRef) { + DNSServiceRefDeallocate(resolver->DNSaddressRef); + resolver->DNSaddressRef = nullptr; + } + err = DNSServiceGetAddrInfo(&resolver->DNSaddressRef, kDNSServiceFlagsForceMulticast, interfaceIndex, resolver->ref->protocol, hostName, static_cast(addressReply), resolver); + if (err == kDNSServiceErr_NoError) { + int sockfd = DNSServiceRefSockFD(resolver->DNSaddressRef); + if (sockfd == -1) { + resolver->cleanUp(); + } + else { + // Fix "multiple socket notifiers for same socket" warning + resolver->addressNotifier.clear(); + resolver->addressNotifier = QSharedPointer::create(sockfd, QSocketNotifier::Read); + connect(resolver->addressNotifier.data(), &QSocketNotifier::activated, resolver, &Resolver::addressReady); + } + } + else { + resolver->cleanUp(); + } +} + +void DNSSD_API QZeroConfPrivate::addressReply(DNSServiceRef sdRef, + DNSServiceFlags flags, quint32 interfaceIndex, + DNSServiceErrorType err, const char *hostName, + const struct sockaddr* address, quint32 ttl, void *userdata) +{ + Q_UNUSED(interfaceIndex) + Q_UNUSED(sdRef) + Q_UNUSED(ttl) + Q_UNUSED(hostName) + + Resolver *resolver = static_cast(userdata); + + if (err == kDNSServiceErr_NoError) { + if ((flags & kDNSServiceFlagsAdd) != 0) { + QHostAddress hAddress(address); + resolver->zcs->setIp(hAddress); + + QString key = resolver->zcs->name() + QString::number(interfaceIndex); + if (!resolver->ref->pub->services.contains(key)) { + resolver->ref->pub->services.insert(key, resolver->zcs); + emit resolver->ref->pub->serviceAdded(resolver->zcs); + } + else + emit resolver->ref->pub->serviceUpdated(resolver->zcs); + + } + } + else + resolver->cleanUp(); +} + +void QZeroConfPrivate::cleanUp(DNSServiceRef toClean) +{ + if (!toClean) + return; + else if (toClean == browser) { + browser = nullptr; + browserNotifier.clear(); + QMap::iterator i; + for (i = pub->services.begin(); i != pub->services.end(); i++) { + QString key = (*i)->name() + QString::number((*i)->interfaceIndex()); + resolvers[key]->cleanUp(); + emit pub->serviceRemoved(*i); + } + pub->services.clear(); + } + else if (toClean == dnssRef) { + dnssRef = nullptr; + serviceNotifier.clear(); + } + + DNSServiceRefDeallocate(toClean); +} + +QZeroConf::QZeroConf(QObject *parent) : QObject (parent) +{ + pri = new QZeroConfPrivate(this); + qRegisterMetaType("QZeroConfService"); +} + +QZeroConf::~QZeroConf() +{ + pri->cleanUp(pri->dnssRef); + pri->cleanUp(pri->browser); + delete pri; +} + +void QZeroConf::startServicePublish(const char *name, const char *type, const char *domain, quint16 port, quint32 interface) +{ + DNSServiceErrorType err; + + if (pri->dnssRef) { + emit error(QZeroConf::serviceRegistrationFailed); + return; + } + + err = DNSServiceRegister(&pri->dnssRef, 0, interface, + name, + type, + domain, + nullptr, + qFromBigEndian(port), + static_cast(pri->txt.size()), pri->txt.data(), + static_cast(QZeroConfPrivate::registerCallback), pri); + + if (err == kDNSServiceErr_NoError) { + int sockfd = DNSServiceRefSockFD(pri->dnssRef); + if (sockfd == -1) { + pri->cleanUp(pri->dnssRef); + emit error(QZeroConf::serviceRegistrationFailed); + } + else { + pri->serviceNotifier = QSharedPointer::create(sockfd, QSocketNotifier::Read, this); + connect(pri->serviceNotifier.data(), &QSocketNotifier::activated, pri, &QZeroConfPrivate::bsRead); + } + } + else { + pri->cleanUp(pri->dnssRef); + emit error(QZeroConf::serviceRegistrationFailed); + } +} + +void QZeroConf::stopServicePublish(void) +{ + pri->cleanUp(pri->dnssRef); +} + +bool QZeroConf::publishExists(void) +{ + if (pri->dnssRef) + return true; + else + return false; +} + +void QZeroConf::addServiceTxtRecord(QString nameOnly) +{ + pri->txt.append(static_cast(nameOnly.size())); + pri->txt.append(nameOnly.toUtf8()); +} + +void QZeroConf::addServiceTxtRecord(QString name, QString value) +{ + name.append("="); + name.append(value); + addServiceTxtRecord(name); +} + +void QZeroConf::clearServiceTxtRecords() +{ + pri->txt.clear(); +} + +void QZeroConf::startBrowser(QString type, QAbstractSocket::NetworkLayerProtocol protocol) +{ + DNSServiceErrorType err; + + if (pri->browser) { + emit error(QZeroConf::browserFailed); + return; + } + + switch (protocol) { + case QAbstractSocket::IPv4Protocol: pri->protocol = kDNSServiceProtocol_IPv4; break; + case QAbstractSocket::IPv6Protocol: pri->protocol = kDNSServiceProtocol_IPv6; break; + default: + qDebug("QZeroConf::startBrowser() - unsupported protocol, using IPv4"); + pri->protocol = kDNSServiceProtocol_IPv4; + break; + } + + err = DNSServiceBrowse(&pri->browser, 0, 0, type.toUtf8(), nullptr, static_cast(QZeroConfPrivate::browseCallback), pri); + if (err == kDNSServiceErr_NoError) { + int sockfd = DNSServiceRefSockFD(pri->browser); + if (sockfd == -1) { + pri->cleanUp(pri->browser); + emit error(QZeroConf::browserFailed); + } + else { + pri->browserNotifier = QSharedPointer::create(sockfd, QSocketNotifier::Read, this); + connect(pri->browserNotifier.data(), &QSocketNotifier::activated, pri, &QZeroConfPrivate::browserRead); + } + } + else { + pri->cleanUp(pri->browser); + emit error(QZeroConf::browserFailed); + } +} + +void QZeroConf::stopBrowser(void) +{ + pri->cleanUp(pri->browser); +} + +bool QZeroConf::browserExists(void) +{ + if (pri->browser) + return true; + else + return false; +} diff --git a/3rdparty/QtZeroConf/bonjour_p.h b/3rdparty/QtZeroConf/bonjour_p.h new file mode 100644 index 000000000..65083168e --- /dev/null +++ b/3rdparty/QtZeroConf/bonjour_p.h @@ -0,0 +1,93 @@ +/************************************************************************************************** +--------------------------------------------------------------------------------------------------- + Copyright (C) 2015 Jonathan Bagg + This file is part of QtZeroConf. + + QtZeroConf is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QtZeroConf is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with QtZeroConf. If not, see . +--------------------------------------------------------------------------------------------------- + Project name : QtZeroConf + File name : bonjour_p.h + Created : 22 July 2015 + Author(s) : Jonathan Bagg +--------------------------------------------------------------------------------------------------- + Part of wrapper for Apple's Bonjour library for use on Windows, MACs and iOS. Needed for slots. +--------------------------------------------------------------------------------------------------- +**************************************************************************************************/ +#ifndef QZEROCONFPRIVATE_H_ +#define QZEROCONFPRIVATE_H_ + +#include +#include +#include +#include +#include "qzeroconf.h" +#include + +#ifndef kDNSServiceFlagsTimeout // earlier versions of dns_sd.h don't define this constant + #define kDNSServiceFlagsTimeout 0x10000 +#endif + +class Resolver : public QObject +{ + Q_OBJECT +public: + void cleanUp(); + QZeroConfService zcs; + QZeroConfPrivate *ref = nullptr; + DNSServiceRef DNSresolverRef = nullptr; + DNSServiceRef DNSaddressRef = nullptr; + QSharedPointer resolverNotifier; + QSharedPointer addressNotifier; + +public slots: + void resolverReady(); + void addressReady(); +}; + +class QZeroConfPrivate : public QObject +{ + Q_OBJECT + +public: + QZeroConfPrivate(QZeroConf *parent); + void cleanUp(DNSServiceRef ref); + void resolve(QZeroConfService); + + static void DNSSD_API registerCallback(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *, + const char *, const char *, void *userdata); + + static void DNSSD_API browseCallback(DNSServiceRef, DNSServiceFlags flags,quint32, DNSServiceErrorType err, const char *name, + const char *type, const char *domain, void *userdata); + + static void DNSSD_API resolverCallback(DNSServiceRef, DNSServiceFlags, quint32, DNSServiceErrorType err, const char *, + const char *hostName, quint16 port, quint16 txtLen, const unsigned char *txtRecord, void *userdata); + + static void DNSSD_API addressReply(DNSServiceRef sdRef, DNSServiceFlags flags, quint32 interfaceIndex, + DNSServiceErrorType err, const char *hostName, const struct sockaddr* address, quint32 ttl, void *userdata); + + QZeroConf *pub; + DNSServiceRef dnssRef = nullptr; + DNSServiceRef browser = nullptr; + DNSServiceProtocol protocol; + QSharedPointer serviceNotifier; + QSharedPointer browserNotifier; + QByteArray txt; + QHash resolvers; + +public slots: + void bsRead(); + void browserRead(); +}; + +#endif // QZEROCONFPRIVATE_H_ diff --git a/3rdparty/QtZeroConf/example/CMakeLists.txt b/3rdparty/QtZeroConf/example/CMakeLists.txt new file mode 100644 index 000000000..f48d3fca6 --- /dev/null +++ b/3rdparty/QtZeroConf/example/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.4) +project(QtZeroConfExample) + +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Gui Widgets REQUIRED) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +# Find QtZeroConf library if we're building standalone +if(NOT TARGET QtZeroConf) + find_package(QtZeroConf CONFIG REQUIRED) +endif() + +add_executable(QtZeroConfExample + window.h + window.cpp + main.cpp +) + +target_link_libraries(QtZeroConfExample + QtZeroConf + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Widgets +) diff --git a/3rdparty/QtZeroConf/example/android/src/QZeroConfNsdManager.java b/3rdparty/QtZeroConf/example/android/src/QZeroConfNsdManager.java new file mode 120000 index 000000000..c901ec5c0 --- /dev/null +++ b/3rdparty/QtZeroConf/example/android/src/QZeroConfNsdManager.java @@ -0,0 +1 @@ +/home/ljc/project/dde-cooperation/dde-cooperation/3rdparty/QtZeroConf/example/android/src/QZeroConfNsdManager.java \ No newline at end of file diff --git a/3rdparty/QtZeroConf/example/main.cpp b/3rdparty/QtZeroConf/example/main.cpp new file mode 100644 index 000000000..1f0e45c59 --- /dev/null +++ b/3rdparty/QtZeroConf/example/main.cpp @@ -0,0 +1,41 @@ +/************************************************************************************************** +--------------------------------------------------------------------------------------------------- + Copyright (C) 2015 Jonathan Bagg + This file is part of QtZeroConf. + + QtZeroConf is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QtZeroConf is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with QtZeroConf. If not, see . +--------------------------------------------------------------------------------------------------- + Project name : QtZeroConf Example + File name : main.cpp + Created : 3 November 2015 + Author(s) : Jonathan Bagg +--------------------------------------------------------------------------------------------------- + Example app to demonstrate service publishing and service discovery +--------------------------------------------------------------------------------------------------- +**************************************************************************************************/ +#include +#include "window.h" + + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + app.setApplicationName("QtZeroConf Example"); + app.setApplicationVersion("0.1"); + + mainWindow window; + + return app.exec(); +} diff --git a/3rdparty/QtZeroConf/example/test_app.pro b/3rdparty/QtZeroConf/example/test_app.pro new file mode 100644 index 000000000..5b884b829 --- /dev/null +++ b/3rdparty/QtZeroConf/example/test_app.pro @@ -0,0 +1,10 @@ +QT+= core gui widgets network +TARGET = test_app +HEADERS= window.h +SOURCES= main.cpp window.cpp +DEFINES= QZEROCONF_STATIC + +include(../qtzeroconf.pri) +INCLUDEPATH+=../ + +android: ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android diff --git a/3rdparty/QtZeroConf/example/window.cpp b/3rdparty/QtZeroConf/example/window.cpp new file mode 100644 index 000000000..a2bb3f1c1 --- /dev/null +++ b/3rdparty/QtZeroConf/example/window.cpp @@ -0,0 +1,190 @@ +/************************************************************************************************** +--------------------------------------------------------------------------------------------------- + Copyright (C) 2015 Jonathan Bagg + This file is part of QtZeroConf. + + QtZeroConf is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QtZeroConf is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with QtZeroConf. If not, see . +--------------------------------------------------------------------------------------------------- + Project name : QtZeroConf Example + File name : window.cpp + Created : 3 November 2015 + Author(s) : Jonathan Bagg +--------------------------------------------------------------------------------------------------- + Example app to demonstrate service publishing and service discovery +--------------------------------------------------------------------------------------------------- +**************************************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include "window.h" + +#ifdef Q_OS_IOS + #define OS_NAME "iOS" +#elif defined(Q_OS_MAC) + #define OS_NAME "Mac" +#elif defined(Q_OS_ANDROID) + #define OS_NAME "Android" +#elif defined(Q_OS_LINUX) + #define OS_NAME "Linux" +#elif defined(Q_OS_WIN) + #define OS_NAME "Windows" +#elif defined(Q_OS_FREEBSD) + #define OS_NAME "FreeBSD" +#else + #define OS_NAME "Some OS" +#endif + +mainWindow::mainWindow() +{ + publishEnabled = 0; + buildGUI(); + + connect(&zeroConf, &QZeroConf::serviceAdded, this, &mainWindow::addService); + connect(&zeroConf, &QZeroConf::serviceRemoved, this, &mainWindow::removeService); + connect(&zeroConf, &QZeroConf::serviceUpdated, this, &mainWindow::updateService); + connect(qGuiApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(appStateChanged(Qt::ApplicationState))); + + publishEnabled = 1; +} + +void mainWindow::buildGUI() +{ + QPushButton *button; + QVBoxLayout *layout = new QVBoxLayout; + + button = new QPushButton(tr(" Enable Publish ")); + button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + layout->addWidget(button); + layout->setAlignment(button, Qt::AlignHCenter); + connect(button, &QPushButton::clicked, this, &mainWindow::startPublishClicked); + + button = new QPushButton(tr(" Disable Publish ")); + button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + layout->addWidget(button); + layout->setAlignment(button, Qt::AlignHCenter); + connect(button, &QPushButton::clicked, this, &mainWindow::stopPublishClicked); + + table.verticalHeader()->hide(); + table.horizontalHeader()->hide(); + table.setColumnCount(2); + layout->addWidget(&table); + //table.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + QWidget *widget = new QWidget; + widget->setLayout(layout); + setCentralWidget(widget); + show(); +} + +QString mainWindow::buildName(void) +{ + QString name; + + QList list = QNetworkInterface::allInterfaces(); // now you have interfaces list + + name = list.last().hardwareAddress(); + name.remove(":"); + name.remove(0, 6); + name+= ')'; + name.prepend("Qt ZeroConf Test - " OS_NAME " ("); + return name; +} + +void mainWindow::appStateChanged(Qt::ApplicationState state) +{ + if (state == Qt::ApplicationSuspended) { + zeroConf.stopServicePublish(); + zeroConf.stopBrowser(); + } + else if (state == Qt::ApplicationActive) { + if (publishEnabled && !zeroConf.publishExists()) + startPublish(); + if (!zeroConf.browserExists()) + zeroConf.startBrowser("_cooperation._udp"); + } +} + +// ---------- Service Publish ---------- + +void mainWindow::startPublish() +{ + zeroConf.clearServiceTxtRecords(); + zeroConf.addServiceTxtRecord("Qt", "the best!"); + zeroConf.addServiceTxtRecord("ZeroConf is nice too"); + zeroConf.startServicePublish(buildName().toUtf8(), "_cooperation._udp", "local", 11437); +} + +void mainWindow::startPublishClicked() +{ + if (publishEnabled) + return; + publishEnabled = 1; + startPublish(); +} + +void mainWindow::stopPublishClicked() +{ + if (!publishEnabled) + return; + publishEnabled = 0; + zeroConf.stopServicePublish(); +} + +// ---------- Discovery Callbacks ---------- + +void mainWindow::addService(QZeroConfService zcs) +{ + qint32 row; + QTableWidgetItem *cell; + qDebug() << "Added service: " << zcs; + + row = table.rowCount(); + table.insertRow(row); + cell = new QTableWidgetItem(zcs->name()); + table.setItem(row, 0, cell); + cell = new QTableWidgetItem(zcs->ip().toString()); + table.setItem(row, 1, cell); + table.resizeColumnsToContents(); + #if !(defined(Q_OS_IOS) || defined(Q_OS_ANDROID)) + setFixedSize(table.horizontalHeader()->length() + 60, table.verticalHeader()->length() + 100); + #endif +} + +void mainWindow::removeService(QZeroConfService zcs) +{ + qint32 i, row; + QTableWidgetItem *nameItem, *ipItem; + qDebug() << "Removed service: " << zcs; + + QList search = table.findItems(zcs->name(), Qt::MatchExactly); + for (i=0; irow(); + ipItem = table.item(row, 1); + if (table.item(row, 1)->text() == zcs->ip().toString()) { // match ip address in case of dual homed systems + delete nameItem; + delete ipItem; + table.removeRow(row); + } + } +} + +void mainWindow::updateService(QZeroConfService zcs) +{ + qDebug() << "Service updated: " << zcs; +} diff --git a/3rdparty/QtZeroConf/example/window.h b/3rdparty/QtZeroConf/example/window.h new file mode 100644 index 000000000..e6fcc574c --- /dev/null +++ b/3rdparty/QtZeroConf/example/window.h @@ -0,0 +1,58 @@ +/************************************************************************************************** +--------------------------------------------------------------------------------------------------- + Copyright (C) 2015 Jonathan Bagg + This file is part of QtZeroConf. + + QtZeroConf is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QtZeroConf is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with QtZeroConf. If not, see . +--------------------------------------------------------------------------------------------------- + Project name : QtZeroConf Example + File name : window.h + Created : 3 November 2015 + Author(s) : Jonathan Bagg +--------------------------------------------------------------------------------------------------- + Example app to demonstrate service publishing and service discovery +--------------------------------------------------------------------------------------------------- +**************************************************************************************************/ +#ifndef WINDOW_H_ +#define WINDOW_H_ + +#include +#include +#include "qzeroconf.h" + +class mainWindow : public QMainWindow +{ + Q_OBJECT + +public: + mainWindow(); + +private: + void buildGUI(); + void startPublish(); + QString buildName(void); + QTableWidget table; + QZeroConf zeroConf; + bool publishEnabled; + +private slots: + void appStateChanged(Qt::ApplicationState state); + void startPublishClicked(); + void stopPublishClicked(); + void addService(QZeroConfService item); + void removeService(QZeroConfService item); + void updateService(QZeroConfService zcs); +}; + +#endif /* WINDOW_H_ */ diff --git a/3rdparty/QtZeroConf/qtzeroconf.pri b/3rdparty/QtZeroConf/qtzeroconf.pri new file mode 100644 index 000000000..d84497e95 --- /dev/null +++ b/3rdparty/QtZeroConf/qtzeroconf.pri @@ -0,0 +1,136 @@ + +# for Qt4 on Linux +lessThan(QT_MAJOR_VERSION, 5) { + HEADERS+= $$PWD/qzeroconf.h $$PWD/avahi-qt/qt-watch.h $$PWD/avahi-qt/qt-watch_p.h + SOURCES+= $$PWD/avahiclient.cpp $$PWD/avahi-qt/qt-watch.cpp + LIBS+= -lavahi-client -lavahi-common + QMAKE_CXXFLAGS+= -I$$PWD +} + +# below for >= Qt5 +linux:!android:!ubports { + HEADERS+= $$PWD/qzeroconf.h $$PWD/avahi-qt/qt-watch.h $$PWD/avahi-qt/qt-watch_p.h + SOURCES+= $$PWD/avahiclient.cpp $$PWD/avahi-qt/qt-watch.cpp + LIBS+= -lavahi-client -lavahi-common + QMAKE_CXXFLAGS+= -I$$PWD +} + +freebsd { + HEADERS+= $$PWD/qzeroconf.h $$PWD/avahi-qt/qt-watch.h $$PWD/avahi-qt/qt-watch_p.h + SOURCES+= $$PWD/avahiclient.cpp $$PWD/avahi-qt/qt-watch.cpp + LIBS+= -lavahi-client -lavahi-common + QMAKE_CXXFLAGS+= -I$$PWD +} + +win32 { + DEFINES+= WIN32 + DEFINES+= NDEBUG + DEFINES+= _WINDOWS + DEFINES+= _USRDLL + DEFINES+= MDNS_DEBUGMSGS=0 + DEFINES+= WIN32_LEAN_AND_MEAN + DEFINES+= USE_TCP_LOOPBACK + DEFINES+= _CRT_SECURE_NO_DEPRECATE + DEFINES+= _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1 + DEFINES+= NOT_HAVE_SA_LEN + QMAKE_CFLAGS+= -I$$PWD/bonjour-sdk + QMAKE_CXXFLAGS+= -I$$PWD/bonjour-sdk -I$$PWD + HEADERS+= $$PWD/qzeroconf.h $$PWD/bonjour_p.h + SOURCES+= $$PWD/bonjour.cpp + SOURCES+= $$PWD/bonjour-sdk/dnssd_clientlib.c + SOURCES+= $$PWD/bonjour-sdk/dnssd_clientstub.c + SOURCES+= $$PWD/bonjour-sdk/dnssd_ipc.c + LIBS+= -lws2_32 -lwsock32 +} + +macx { + HEADERS+= $$PWD/qzeroconf.h $$PWD/bonjour_p.h + SOURCES+= $$PWD/bonjour.cpp + QMAKE_LFLAGS += -F /System/Library/Frameworks/CoreServices.framework/ + LIBS+= -framework CoreServices + QMAKE_CXXFLAGS+= -I$$PWD +} + +ios { + HEADERS+= $$PWD/qzeroconf.h $$PWD/bonjour_p.h + SOURCES+= $$PWD/bonjour.cpp + QMAKE_CXXFLAGS+= -I$$PWD +} + +ubports: { + QMAKE_CXXFLAGS+= -I$$PWD + QMAKE_CFLAGS+= -I$$PWD + ACM = $$PWD/avahi-common + ACR = $$PWD/avahi-core + HEADERS+= $$PWD/qzeroconf.h $$PWD/avahi-qt/qt-watch.h $$PWD/avahi-qt/qt-watch_p.h + SOURCES+= $$PWD/avahicore.cpp $$PWD/avahi-qt/qt-watch.cpp + # avahi-common + android: { + DEFINES+= HAVE_STRLCPY GETTEXT_PACKAGE + } + ubports: { + DEFINES+= _GNU_SOURCE GETTEXT_PACKAGE + } + SOURCES+= $$ACM/address.c + SOURCES+= $$ACM/alternative.c + SOURCES+= $$ACM/domain.c + SOURCES+= $$ACM/error.c + SOURCES+= $$ACM/i18n.c + SOURCES+= $$ACM/malloc.c + SOURCES+= $$ACM/rlist.c + SOURCES+= $$ACM/simple-watch.c + SOURCES+= $$ACM/strlst.c + SOURCES+= $$ACM/thread-watch.c + SOURCES+= $$ACM/timeval.c + SOURCES+= $$ACM/utf8.c + # avahi-core + DEFINES+= HAVE_NETLINK + SOURCES+= $$ACR/addr-util.c + SOURCES+= $$ACR/announce.c + SOURCES+= $$ACR/browse.c + SOURCES+= $$ACR/browse-dns-server.c + SOURCES+= $$ACR/browse-domain.c + SOURCES+= $$ACR/browse-service.c + SOURCES+= $$ACR/browse-service-type.c + SOURCES+= $$ACR/cache.c + SOURCES+= $$ACR/dns.c + SOURCES+= $$ACR/domain-util.c + SOURCES+= $$ACR/entry.c + SOURCES+= $$ACR/fdutil.c + SOURCES+= $$ACR/hashmap.c + SOURCES+= $$ACR/iface.c + SOURCES+= $$ACR/iface-linux.c + SOURCES+= $$ACR/log.c + SOURCES+= $$ACR/multicast-lookup.c + SOURCES+= $$ACR/netlink.c + SOURCES+= $$ACR/prioq.c + SOURCES+= $$ACR/probe-sched.c + SOURCES+= $$ACR/querier.c + SOURCES+= $$ACR/query-sched.c + SOURCES+= $$ACR/resolve-address.c + SOURCES+= $$ACR/resolve-host-name.c + SOURCES+= $$ACR/resolve-service.c + SOURCES+= $$ACR/response-sched.c + SOURCES+= $$ACR/rr.c + SOURCES+= $$ACR/rrlist.c + SOURCES+= $$ACR/server.c + SOURCES+= $$ACR/socket.c + SOURCES+= $$ACR/timeeventq.c + SOURCES+= $$ACR/util.c + SOURCES+= $$ACR/wide-area.c + #avahi-core/iface-none.c avahi-core/iface-pfroute.c avahi-core/avahi-reflector.c +} + +android: { + lessThan(QT_MAJOR_VERSION, 6) { + QT += androidextras + } + QT += gui + HEADERS += $$PWD/qzeroconf.h $$PWD/androidnsd_p.h + SOURCES += $$PWD/androidnsd.cpp + DISTFILES += $$PWD/QZeroConfNsdManager.java +} + +HEADERS+= $$PWD/qzeroconfservice.h $$PWD/qzeroconfglobal.h + +SOURCES+= $$PWD/qzeroconfservice.cpp diff --git a/3rdparty/QtZeroConf/qtzeroconf.pro b/3rdparty/QtZeroConf/qtzeroconf.pro new file mode 100644 index 000000000..44fa9f288 --- /dev/null +++ b/3rdparty/QtZeroConf/qtzeroconf.pro @@ -0,0 +1,29 @@ +QT = core network + +include($$PWD/qtzeroconf.pri) + +#VERSION = 1.1 + +TEMPLATE = lib +TARGET = $$qtLibraryTarget(QtZeroConf$$QT_LIBINFIX) +CONFIG += module create_prl +DEFINES+= QT_BUILD_ZEROCONF_LIB +mac:QMAKE_FRAMEWORK_BUNDLE_NAME = $$TARGET + +headersDataFiles.files = $$PWD/qzeroconf.h $$PWD/qzeroconfservice.h $$PWD/qzeroconfglobal.h + +# install to Qt installation directory if no PREFIX specified +_PREFIX = $$PREFIX +isEmpty(_PREFIX) { + INSTALL_HEADER_PATH = $$[QT_INSTALL_HEADERS]/QtZeroConf/ + INSTALL_LIB_PATH = $$[QT_INSTALL_LIBS] +} else { + + INSTALL_HEADER_PATH = $$PREFIX/include/QtZeroConf/ + INSTALL_LIB_PATH = $$PREFIX/lib +} +message(install to: $$INSTALL_LIB_PATH) +headersDataFiles.path = $$INSTALL_HEADER_PATH +target.path = $$INSTALL_LIB_PATH + +INSTALLS+= target headersDataFiles diff --git a/3rdparty/QtZeroConf/qzeroconf.h b/3rdparty/QtZeroConf/qzeroconf.h new file mode 100644 index 000000000..b3842a20c --- /dev/null +++ b/3rdparty/QtZeroConf/qzeroconf.h @@ -0,0 +1,90 @@ +/************************************************************************************************** +--------------------------------------------------------------------------------------------------- + Copyright (C) 2015 Jonathan Bagg + This file is part of QtZeroConf. + + QtZeroConf is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QtZeroConf is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with QtZeroConf. If not, see . +--------------------------------------------------------------------------------------------------- + Project name : QtZeroConf + File name : qzeroconf.h + Created : 20 July 2015 + Author(s) : Jonathan Bagg +--------------------------------------------------------------------------------------------------- + QtZeroConf class definition +--------------------------------------------------------------------------------------------------- +**************************************************************************************************/ +#ifndef QZEROCONF_H_ +#define QZEROCONF_H_ + +#include +#include +#include +#include "qzeroconfglobal.h" +#include "qzeroconfservice.h" + +class QZeroConfPrivate; + +class Q_ZEROCONF_EXPORT QZeroConf : public QObject +{ + Q_OBJECT + +friend class QZeroConfPrivate; + +public: + enum error_t { + noError = 0, + serviceRegistrationFailed = -1, + serviceNameCollision = -2, + browserFailed = -3, + }; + QZeroConf(QObject *parent = Q_NULLPTR); + ~QZeroConf(); + void startServicePublish(const char *name, const char *type, const char *domain, quint16 port, quint32 interface = 0); + void stopServicePublish(void); + bool publishExists(void); + inline void startBrowser(QString type) + { + startBrowser(type, QAbstractSocket::IPv4Protocol); + } + void startBrowser(QString type, QAbstractSocket::NetworkLayerProtocol protocol); + void stopBrowser(void); + bool browserExists(void); + void addServiceTxtRecord(QString nameOnly); + void addServiceTxtRecord(QString name, QString value); + void clearServiceTxtRecords(); + + QMap getServices() const + { + return services; + } + +Q_SIGNALS: + void servicePublished(void); + void serviceNameChanged(const QString &newName); + void error(QZeroConf::error_t); + void serviceAdded(QZeroConfService); + void serviceUpdated(QZeroConfService); + void serviceRemoved(QZeroConfService); + +private: + QZeroConfPrivate *pri; + QMap services; + + + +}; + +#endif // QZEROCONF_H_ + + diff --git a/3rdparty/QtZeroConf/qzeroconfglobal.h b/3rdparty/QtZeroConf/qzeroconfglobal.h new file mode 100644 index 000000000..1f1a18309 --- /dev/null +++ b/3rdparty/QtZeroConf/qzeroconfglobal.h @@ -0,0 +1,42 @@ +/************************************************************************************************** +--------------------------------------------------------------------------------------------------- + Copyright (C) 2017 Jonathan Bagg + This file is part of QtZeroConf. + + QtZeroConf is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QtZeroConf is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with QtZeroConf. If not, see . +--------------------------------------------------------------------------------------------------- + Project name : QtZeroConf + File name : qzeroconfglobal.h + Created : 31 Oct 2017 + Author(s) : Jonathan Bagg +--------------------------------------------------------------------------------------------------- + Sets up Q_DECL_EXPORT / IMPORT for QZeroConf and QZeroConfService classes +--------------------------------------------------------------------------------------------------- +**************************************************************************************************/ +#ifndef QZEROCONFGLOBAL_H +#define QZEROCONFGLOBAL_H + +#include + +#if (!defined(QT_STATIC) && !defined(QZEROCONF_STATIC)) +# ifdef QT_BUILD_ZEROCONF_LIB +# define Q_ZEROCONF_EXPORT Q_DECL_EXPORT +# else +# define Q_ZEROCONF_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_ZEROCONF_EXPORT +#endif + +#endif // QZEROCONFGLOBAL_H diff --git a/3rdparty/QtZeroConf/qzeroconfservice.cpp b/3rdparty/QtZeroConf/qzeroconfservice.cpp new file mode 100644 index 000000000..99904d518 --- /dev/null +++ b/3rdparty/QtZeroConf/qzeroconfservice.cpp @@ -0,0 +1,10 @@ +#include +#include "qzeroconfservice.h" + +QDebug operator<<(QDebug debug, const QZeroConfService &service) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "Zeroconf Service: " + service->name() + " @ " + service->host() + " ("+ service->ip().toString() + ":" + QString::number( service->port()) + ")"; + return debug.maybeSpace(); +} + diff --git a/3rdparty/QtZeroConf/qzeroconfservice.h b/3rdparty/QtZeroConf/qzeroconfservice.h new file mode 100644 index 000000000..65fb874b8 --- /dev/null +++ b/3rdparty/QtZeroConf/qzeroconfservice.h @@ -0,0 +1,58 @@ +#ifndef QZEROCONFSERVICE_H +#define QZEROCONFSERVICE_H + +#include +#include +#include +#include "qzeroconfglobal.h" + +class QZeroConfPrivate; + +class Q_ZEROCONF_EXPORT QZeroConfServiceData +{ + Q_GADGET + Q_PROPERTY( QString name READ name ) + Q_PROPERTY( QString type READ type ) + Q_PROPERTY( QString domain READ domain ) + Q_PROPERTY( QString host READ host ) + +friend class QZeroConfPrivate; + +public: + inline QString name() const {return m_name;} + inline QString type() const {return m_type;} + inline QString domain() const {return m_domain;} + inline QString host() const {return m_host;} + QHostAddress ip() + { + QMutexLocker locker(&m_lock); + return m_ip; + } + inline quint32 interfaceIndex() const {return m_interfaceIndex;} + quint16 port() const {return m_port;} + QMap txt() const {return m_txt;} + +private: + void setIp(QHostAddress &ip) + { + QMutexLocker locker(&m_lock); + m_ip = ip; + } + QString m_name; + QString m_type; + QString m_domain; + QString m_host; + QHostAddress m_ip; + quint32 m_interfaceIndex; + quint16 m_port; + QMap m_txt; + QMutex m_lock; +}; + +typedef QSharedPointer QZeroConfService; + +Q_DECLARE_METATYPE(QZeroConfService) + +QDebug Q_ZEROCONF_EXPORT operator<<(QDebug debug, const QZeroConfService &service); + +#endif // QZEROCONFSERVICE_H diff --git a/src/plugins/cooperation/core/CMakeLists.txt b/src/plugins/cooperation/core/CMakeLists.txt index 075fa1651..2a97c4b58 100644 --- a/src/plugins/cooperation/core/CMakeLists.txt +++ b/src/plugins/cooperation/core/CMakeLists.txt @@ -37,6 +37,8 @@ FILE(GLOB PLUGIN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/utils/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/maincontroller/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/maincontroller/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/discover/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/discover/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/*.json" ) @@ -95,6 +97,7 @@ target_link_libraries(${PROJECT_NAME} Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network co + QtZeroConf ) # 平台相关的库,添加到这里 diff --git a/src/plugins/cooperation/core/cooperationcoreplugin.cpp b/src/plugins/cooperation/core/cooperationcoreplugin.cpp index 2b655ff89..2787bb8d1 100644 --- a/src/plugins/cooperation/core/cooperationcoreplugin.cpp +++ b/src/plugins/cooperation/core/cooperationcoreplugin.cpp @@ -20,6 +20,7 @@ # include "base/reportlog/reportlogmanager.h" # include # include +# include DWIDGET_USE_NAMESPACE #endif @@ -68,6 +69,8 @@ bool CooperaionCorePlugin::start() #ifndef linux CooperationProxy::instance(); +#else + DiscoverController::instance(); #endif return true; diff --git a/src/plugins/cooperation/core/discover/discovercontroller.cpp b/src/plugins/cooperation/core/discover/discovercontroller.cpp new file mode 100644 index 000000000..916577cbb --- /dev/null +++ b/src/plugins/cooperation/core/discover/discovercontroller.cpp @@ -0,0 +1,377 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "discovercontroller.h" +#include "discovercontroller_p.h" +#include "utils/cooperationutil.h" +#include "co/log.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace cooperation_core; + +DiscoverControllerPrivate::DiscoverControllerPrivate(DiscoverController *qq) + : q(qq) +{ +} + +DiscoverController::DiscoverController(QObject *parent) + : QObject(parent), + d(new DiscoverControllerPrivate(this)) +{ + if (isZeroConfDaemonActive()) { + initZeroConf(); + return; + } + + openZeroConfDaemonDailog(); + // QTimer *timer = new QTimer(this); + // connect(timer, &QTimer::timeout, this, [timer, this] { + // if (isZeroConfDaemonActive()) { + // initZeroConf(); + // timer->stop(); + // } + // }); + // timer->start(5000); + + // initSearchDeivce(); +} + +DiscoverController::~DiscoverController() +{ +} + +void DiscoverController::initZeroConf() +{ + d->zeroConf = new QZeroConf(); + d->zeroconfname = QSysInfo::machineUniqueId(); + + publish(); + // initConnect(); + // connect signal should before browser + // if (!d->zeroConf->browserExists()) + // d->zeroConf->startBrowser(UNI_CHANNEL); +} + +void DiscoverController::initConnect() +{ +// connect(CooperationUtil::instance(), &CooperationUtil::onlineStateChanged, this, [this](const QString &validIP) { +// if (validIP.isEmpty()) +// return; +// updatePublish(); +// refresh(); +// }); +#ifdef linux + connect(DConfigManager::instance(), &DConfigManager::valueChanged, this, &DiscoverController::onDConfigValueChanged); +#endif + connect(ConfigManager::instance(), &ConfigManager::appAttributeChanged, this, &DiscoverController::onAppAttributeChanged); + connect(d->zeroConf, &QZeroConf::serviceAdded, this, &DiscoverController::addService); + connect(d->zeroConf, &QZeroConf::serviceRemoved, this, &DiscoverController::removeService); + connect(d->zeroConf, &QZeroConf::serviceUpdated, this, &DiscoverController::updateService); +} + +void DiscoverController::initSearchDeivce() +{ + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, [this] { + if (!d->searchDevice) + return; + + // bool res = NetworkUtil::instance()->searchDevice(d->searchDevice->ipAddress()); + // if (res) + // return; + + Q_EMIT deviceOffline(d->searchDevice->ipAddress()); + d->onlineDeviceList.removeOne(d->searchDevice); + d->searchDevice = nullptr; + }); + timer->start(10000); +} + +bool DiscoverController::isVaildDevice(const DeviceInfoPointer info) +{ + if (!info || info->ipAddress().isEmpty() || !info->ipAddress().startsWith(d->ipfilter)) + return false; + else + return true; +} + +DeviceInfoPointer DiscoverController::parseDeviceJson(const QString &info) +{ + QJsonParseError error; + auto doc = QJsonDocument::fromJson(info.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) { + ELOG << "parse device info error"; + return nullptr; + } + + auto map = doc.toVariant().toMap(); + auto devInfo = DeviceInfo::fromVariantMap(map); + devInfo->setConnectStatus(DeviceInfo::Connectable); + + return devInfo; +} + +DeviceInfoPointer DiscoverController::parseDeviceService(QZeroConfService zcs) +{ + QVariantMap infomap; + for (const auto &key : zcs->txt().keys()) + infomap.insert(key, QString::fromUtf8(QByteArray::fromBase64(zcs->txt().value(key)))); + + auto devInfo = DeviceInfo::fromVariantMap(infomap); + if (!isVaildDevice(devInfo)) + return nullptr; + return devInfo; +} + +QList DiscoverController::getOnlineDeviceList() const +{ + return d->onlineDeviceList; +} + +bool DiscoverController::openZeroConfDaemonDailog() +{ +#ifdef __linux__ + CooperationDialog dlg; + dlg.setIcon(QIcon::fromTheme("dialog-warning")); + dlg.addButton(tr("Confirm"), true, CooperationDialog::ButtonRecommend); + dlg.addButton(tr("Close"), false, CooperationDialog::ButtonWarning); + + // dlg.setTitle(tr("Please click to confirm to enable the LAN discovery service!")); + // dlg.setMessage(tr("Unable to discover and be discovered by other devices when LAN discovery service is not turned on")); + + dlg.setTitle(tr("Please click to confirm to enable the LAN discovery service!")); + dlg.setMessage(tr("If you want discovery through the mdns service, you need to enable the avahi service")); + + int code = dlg.exec(); + if (code == 0) + QProcess::startDetached("systemctl start avahi-daemon.service"); + return true; +//#else +// int choice = QMessageBox::warning(nullptr, tr("Please click to confirm to enable the LAN discovery service!"), +// tr("Unable to discover and be discovered by other devices when LAN discovery service is not turned on" +// "Right click on Windows Start menu ->Computer Management ->Services and Applications ->Services to enable Bonjour service"), +// QMessageBox::Ok); +// QDesktopServices::openUrl(QUrl("services.msc")); +// return false; +#endif +} + +bool DiscoverController::isZeroConfDaemonActive() +{ +#ifdef __linux__ + QProcess process; + process.start("systemctl", QStringList() << "is-active" + << "avahi-daemon.service"); + process.waitForFinished(); + + QString output = process.readAllStandardOutput(); + QString error = process.readAllStandardError(); + + if (process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0) { + if (output.contains("active")) { + LOG << "Avahi service is running"; + return true; + } else { + WLOG << "Avahi service is not running"; + return false; + } + } else { + //ELOG << "Error: " << error.toStdString(); + return false; + } + +#else + QProcess process; + process.start("sc query \"Bonjour Service\""); + process.waitForFinished(); + QByteArray output = process.readAllStandardOutput(); + QString res = QTextCodec::codecForName("GBK")->toUnicode(output); + + if (res.contains("RUNNING")) + return true; + else + return false; +#endif +} + +DeviceInfoPointer DiscoverController::findDeviceByIP(const QString &ip) +{ + for (int i = 0; i < d->onlineDeviceList.size(); ++i) { + auto info = d->onlineDeviceList[i]; + if (info->ipAddress() == ip) + return info; + } + return nullptr; +} + +DeviceInfoPointer DiscoverController::selfInfo() +{ + return DeviceInfo::fromVariantMap(CooperationUtil::deviceInfo()); +} + +void DiscoverController::updateDeviceState(const DeviceInfoPointer info) +{ + Q_EMIT deviceOnline({ info }); +} + +void DiscoverController::onDConfigValueChanged(const QString &config, const QString &key) +{ + Q_UNUSED(key); + if (config != kDefaultCfgPath) + return; + + updatePublish(); +} + +void DiscoverController::onAppAttributeChanged(const QString &group, const QString &key, const QVariant &value) +{ + if (group != AppSettings::GenericGroup) + return; + + // if (key == AppSettings::StoragePathKey) + // CooperationUtil::instance()->setStorageConfig(value.toString()); + + updatePublish(); +} + +void DiscoverController::addService(QZeroConfService zcs) +{ + auto devInfo = parseDeviceService(zcs); + + if (!devInfo) + return; + + d->onlineDeviceList.append(devInfo); + Q_EMIT deviceOnline({ devInfo }); +} + +void DiscoverController::updateService(QZeroConfService zcs) +{ + auto devInfo = parseDeviceService(zcs); + + if (!devInfo) + return; + + auto oldinfo = findDeviceByIP(devInfo->ipAddress()); + if (oldinfo) + d->onlineDeviceList.removeOne(oldinfo); + d->onlineDeviceList.append(devInfo); + Q_EMIT deviceOnline({ devInfo }); +} + +void DiscoverController::removeService(QZeroConfService zcs) +{ + auto devInfo = parseDeviceService(zcs); + + if (!devInfo) + return; + + auto oldinfo = findDeviceByIP(devInfo->ipAddress()); + if (oldinfo) + d->onlineDeviceList.removeOne(oldinfo); + Q_EMIT deviceOffline(devInfo->ipAddress()); +} + +DiscoverController *DiscoverController::instance() +{ + static DiscoverController ins; + return &ins; +} + +void DiscoverController::publish() +{ + d->zeroConf->clearServiceTxtRecords(); + + QVariantMap deviceInfo = CooperationUtil::deviceInfo(); + //设置为局域网不发现 + if (deviceInfo.value(AppSettings::DiscoveryModeKey) == 1) { + unpublish(); + return; + } + + QString selfIP = deviceInfo.value("IPAddress").toString(); + d->ipfilter = selfIP.lastIndexOf(".") != -1 ? selfIP.left(selfIP.lastIndexOf(".")) : ""; + + LOG << "publish:-------------------------------------------"; + for (const auto &key : deviceInfo.keys()) + d->zeroConf->addServiceTxtRecord(key, deviceInfo.value(key).toString().toUtf8().toBase64()); + + d->zeroConf->startServicePublish(d->zeroconfname.toUtf8(), "_cooperationv20._udp", "local", 0); +} + +void DiscoverController::unpublish() +{ + d->zeroConf->stopServicePublish(); +} + +void DiscoverController::updatePublish() +{ + if (!d->zeroConf) + return; + + unpublish(); + publish(); +} + +void DiscoverController::refresh() +{ + if (!d->zeroConf) + return; + + d->onlineDeviceList.clear(); + auto allServices = d->zeroConf->getServices(); + + for (const auto &key : allServices.keys()) { + QZeroConfService zcs = allServices.value(key); + auto devInfo = parseDeviceService(zcs); + + if (devInfo) + d->onlineDeviceList.append(devInfo); + } + if (d->searchDevice) + d->onlineDeviceList.append(d->searchDevice); + + Q_EMIT deviceOnline({ d->onlineDeviceList }); + bool hasFound = d->onlineDeviceList.isEmpty(); + Q_EMIT discoveryFinished(hasFound); +} + +void DiscoverController::addSearchDeivce(const QString &info) +{ + auto devInfo = parseDeviceJson(info); + if (!devInfo) { + Q_EMIT discoveryFinished(false); + return; + } + d->searchDevice = devInfo; + + auto oldinfo = findDeviceByIP(devInfo->ipAddress()); + if (oldinfo) + d->onlineDeviceList.removeOne(oldinfo); + d->onlineDeviceList.append(d->searchDevice); + + if (devInfo->isValid()) + Q_EMIT deviceOnline({ d->searchDevice }); +} + +void DiscoverController::startDiscover() +{ + if (!d->zeroConf) + return; + + Q_EMIT startDiscoveryDevice(); + + // 延迟1s,为了展示发现界面 + QTimer::singleShot(1000, this, &DiscoverController::refresh); +} diff --git a/src/plugins/cooperation/core/discover/discovercontroller.h b/src/plugins/cooperation/core/discover/discovercontroller.h new file mode 100644 index 000000000..6d6861e00 --- /dev/null +++ b/src/plugins/cooperation/core/discover/discovercontroller.h @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef DISCOVERYCONTROLLER_H +#define DISCOVERYCONTROLLER_H + +#include "global_defines.h" +#include "info/deviceinfo.h" +#include +#include "qzeroconf.h" + +namespace cooperation_core { +class DiscoverControllerPrivate; +class DiscoverController : public QObject +{ + Q_OBJECT +public: + static DiscoverController *instance(); + + void publish(); + void unpublish(); + void updatePublish(); + void refresh(); + void startDiscover(); + + QList getOnlineDeviceList() const; + DeviceInfoPointer findDeviceByIP(const QString &ip); + static DeviceInfoPointer selfInfo(); + + void updateDeviceState(const DeviceInfoPointer info); + + static bool isZeroConfDaemonActive(); + static bool openZeroConfDaemonDailog(); + +Q_SIGNALS: + void deviceOnline(const QList &infoList); + void deviceOffline(const QString &ip); + void startDiscoveryDevice(); + void discoveryFinished(bool hasFound); + +private Q_SLOTS: + void addService(QZeroConfService zcs); + void removeService(QZeroConfService zcs); + void updateService(QZeroConfService zcs); + + void onDConfigValueChanged(const QString &config, const QString &key); + void onAppAttributeChanged(const QString &group, const QString &key, const QVariant &value); + + void addSearchDeivce(const QString &info); + +private: + explicit DiscoverController(QObject *parent = nullptr); + ~DiscoverController(); + + void initZeroConf(); + void initConnect(); + void initSearchDeivce(); + bool isVaildDevice(const DeviceInfoPointer info); + DeviceInfoPointer parseDeviceJson(const QString &info); + DeviceInfoPointer parseDeviceService(QZeroConfService zcs); + +private: + QSharedPointer d { nullptr }; +}; + +} // namespace cooperation_core + +#endif // DISCOVERYCONTROLLER_H diff --git a/src/plugins/cooperation/core/discover/discovercontroller_p.h b/src/plugins/cooperation/core/discover/discovercontroller_p.h new file mode 100644 index 000000000..6c9a0235a --- /dev/null +++ b/src/plugins/cooperation/core/discover/discovercontroller_p.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef DISCOVERYCONTROLLER_P_H +#define DISCOVERYCONTROLLER_P_H + +#include "discovercontroller.h" +#include "qzeroconf.h" + +namespace cooperation_core { + +class DiscoverControllerPrivate +{ + friend class DiscoverController; + +public: + explicit DiscoverControllerPrivate(DiscoverController *qq); + +private: + DiscoverController *q; + QZeroConf *zeroConf = { nullptr }; + QList onlineDeviceList; + //过滤非同子网段 + QString ipfilter; + //发现服务名,需要为局域网唯一 + QString zeroconfname; + + DeviceInfoPointer searchDevice; +}; + +} // namespace cooperation_core + +#endif // DISCOVERYCONTROLLER_P_H diff --git a/translations/dde-cooperation/dde-cooperation.ts b/translations/dde-cooperation/dde-cooperation.ts index a9811c914..32fbed49e 100644 --- a/translations/dde-cooperation/dde-cooperation.ts +++ b/translations/dde-cooperation/dde-cooperation.ts @@ -17,7 +17,7 @@ cooperation_core::BottomLabel - + Local IP: %1 @@ -322,45 +322,68 @@ + + cooperation_core::DiscoverController + + + Confirm + + + + + Close + + + + + Please click to confirm to enable the LAN discovery service! + + + + + If you want discovery through the mdns service, you need to enable the avahi service + + + cooperation_core::FirstTipWidget - + First step - + The opposite end opens the application and connects to the same network - + Second step - + Third step - + to send the file - + to connect to the peer device - + Enter the peer IP in the search box - + Click @@ -387,7 +410,7 @@ cooperation_core::LookingForDeviceWidget - + Looking for devices @@ -421,7 +444,7 @@ cooperation_core::NoNetworkWidget - + Please connect to the network @@ -429,32 +452,32 @@ cooperation_core::NoResultTipWidget - + 1. Enable cross-end collaborative applications. Applications on the UOS can be downloaded from the App Store, and applications on the Windows side can be downloaded from: - + 2. On the same LAN as the device - + 3. File Manager-Settings-File Drop-Allow the following users to drop files to me -"Everyone on the same LAN" - + 3. Settings-Basic Settings-Discovery Mode-"Allow everyone in the same LAN" - + 4. Try entering the target device IP in the top search box - + Unable to find collaborative device? @@ -462,7 +485,7 @@ cooperation_core::NoResultWidget - + No device found @@ -692,13 +715,13 @@ cooperation_core::WorkspaceWidgetPrivate - - + + Please enter the device ip/name of the collaborator - + Nearby Device diff --git a/translations/dde-cooperation/dde-cooperation_ug.ts b/translations/dde-cooperation/dde-cooperation_ug.ts index 8f7954f06..7c23fecd0 100644 --- a/translations/dde-cooperation/dde-cooperation_ug.ts +++ b/translations/dde-cooperation/dde-cooperation_ug.ts @@ -17,7 +17,7 @@ cooperation_core::BottomLabel - + Local IP: %1 IP ئادرېس: %1 @@ -322,45 +322,68 @@ توردا يوق + + cooperation_core::DiscoverController + + + Confirm + + + + + Close + تاقاش + + + + Please click to confirm to enable the LAN discovery service! + + + + + If you want discovery through the mdns service, you need to enable the avahi service + + + cooperation_core::FirstTipWidget - + First step - + The opposite end opens the application and connects to the same network - + Second step - + Third step - + to send the file - + to connect to the peer device - + Enter the peer IP in the search box - + Click @@ -387,7 +410,7 @@ cooperation_core::LookingForDeviceWidget - + Looking for devices ئۈسكۈنە ئىزدەۋاتىدۇ @@ -421,7 +444,7 @@ cooperation_core::NoNetworkWidget - + Please connect to the network تورغا ئۇلاڭ @@ -429,32 +452,32 @@ cooperation_core::NoResultTipWidget - + 1. Enable cross-end collaborative applications. Applications on the UOS can be downloaded from the App Store, and applications on the Windows side can be downloaded from: 1. تېرمىنال ھالقىپ ھەمكارلىشىش دېتالىنى ئېچىڭ، UOS تېرمىنالىدىكى ئەپنى ئەپ بازىرىدىن چۈشۈرگىلى بولىدۇ، Windows تېرمىنالىنى چۈشۈرۈش ئادرېسى: - + 2. On the same LAN as the device 2. بۇ ئۈسكۈنە بىلەن ئوخشاش دائىرىلىك تورغا ئۇلاڭ - + 3. File Manager-Settings-File Drop-Allow the following users to drop files to me -"Everyone on the same LAN" 3. ھۆججەت باشقۇرغۇچ-تەڭشەك-ھۆججەت يوللاش-تۆۋەندىكى ئىشلەتكۈچىلەرنىڭ ماڭا ھۆججەت يوللىشىغا يول قويۇش-«ئوخشاش دائىرىلىك توردىكى بارلىق كىشىلەر» - + 3. Settings-Basic Settings-Discovery Mode-"Allow everyone in the same LAN" 3. تەڭشەك-ئاساسىي تەڭشەك-بايقىغىلى بولىدىغانلىرى-«ئوخشاش دائىرىلىك توردىكى بارلىق كىشىگە رۇخسەت قىلىش» - + 4. Try entering the target device IP in the top search box 4. ئۈستىدىكى ئىزدەش رامكىسىغا نىشان ئۈسكۈنىنىڭ IP سىنى كىرگۈزۈپ سىناپ بېقىڭ - + Unable to find collaborative device? ئۈسكۈنىنى تاپالمىدىمۇ؟ @@ -462,7 +485,7 @@ cooperation_core::NoResultWidget - + No device found ھېچقانداق ئۈسكۈنە بايقالمىدى @@ -692,13 +715,13 @@ cooperation_core::WorkspaceWidgetPrivate - - + + Please enter the device ip/name of the collaborator ھەمكارلاشقۇچى ئوبيېكت ئۈسكۈنىنىڭ نامى ياكى IP ئادرېسىنى كىرگۈزۈڭ - + Nearby Device ئەتراپتىكى ئۈسكۈنە diff --git a/translations/dde-cooperation/dde-cooperation_zh_CN.ts b/translations/dde-cooperation/dde-cooperation_zh_CN.ts index b46ec8b5d..08e50b2f8 100644 --- a/translations/dde-cooperation/dde-cooperation_zh_CN.ts +++ b/translations/dde-cooperation/dde-cooperation_zh_CN.ts @@ -17,7 +17,7 @@ cooperation_core::BottomLabel - + Local IP: %1 本机IP:%1 @@ -322,45 +322,68 @@ 离线 + + cooperation_core::DiscoverController + + + Confirm + 确认 + + + + Close + 关闭 + + + + Please click to confirm to enable the LAN discovery service! + 请点击确认开启局域网发现服务! + + + + If you want discovery through the mdns service, you need to enable the avahi service + 如果您希望通过mdns服务发现,则需要开启avahi服务进程 + + cooperation_core::FirstTipWidget - + First step 第1步 - + The opposite end opens the application and connects to the same network 对端开启应用,并连接同一网络 - + Second step 第2步 - + Third step 第3步 - + to send the file 发送文件 - + to connect to the peer device 连接对端设备 - + Enter the peer IP in the search box 在搜索框输入对端IP - + Click 点击 @@ -387,7 +410,7 @@ cooperation_core::LookingForDeviceWidget - + Looking for devices 正在寻找设备 @@ -421,7 +444,7 @@ cooperation_core::NoNetworkWidget - + Please connect to the network 请连接网络 @@ -429,32 +452,32 @@ cooperation_core::NoResultTipWidget - + 1. Enable cross-end collaborative applications. Applications on the UOS can be downloaded from the App Store, and applications on the Windows side can be downloaded from: 1. 开启跨端协同应用,UOS端应用可在应用商店下载,Windows端下载地址: - + 2. On the same LAN as the device 2. 与本设备在同一局域网 - + 3. File Manager-Settings-File Drop-Allow the following users to drop files to me -"Everyone on the same LAN" 3. 文件管理器-设置-文件投送-允许以下用户向我投送文件-“同一局域网下的所有人” - + 3. Settings-Basic Settings-Discovery Mode-"Allow everyone in the same LAN" 3. 设置-基础设置-可被发现-“允许同一局域网下的所有人” - + 4. Try entering the target device IP in the top search box 4. 尝试在顶部搜索框输入目标设备IP - + Unable to find collaborative device? 找不到协同设备? @@ -462,7 +485,7 @@ cooperation_core::NoResultWidget - + No device found 未搜寻到任何设备 @@ -692,13 +715,13 @@ cooperation_core::WorkspaceWidgetPrivate - - + + Please enter the device ip/name of the collaborator 请输入协同对象的设备IP/昵称 - + Nearby Device 附近的设备 diff --git a/translations/dde-cooperation/dde-cooperation_zh_HK.ts b/translations/dde-cooperation/dde-cooperation_zh_HK.ts index 3ecb2da44..e9be2c6a6 100644 --- a/translations/dde-cooperation/dde-cooperation_zh_HK.ts +++ b/translations/dde-cooperation/dde-cooperation_zh_HK.ts @@ -17,7 +17,7 @@ cooperation_core::BottomLabel - + Local IP: %1 本機IP:%1 @@ -322,45 +322,68 @@ 離線 + + cooperation_core::DiscoverController + + + Confirm + + + + + Close + 關閉 + + + + Please click to confirm to enable the LAN discovery service! + + + + + If you want discovery through the mdns service, you need to enable the avahi service + + + cooperation_core::FirstTipWidget - + First step 第1步 - + The opposite end opens the application and connects to the same network 对端开启应用,并连接同一网络 - + Second step 第2步 - + Third step 第3步 - + to send the file 发送文件 - + to connect to the peer device 连接对端设备 - + Enter the peer IP in the search box 在搜索框输入对端IP - + Click 点击 @@ -387,7 +410,7 @@ cooperation_core::LookingForDeviceWidget - + Looking for devices 正在尋找設備 @@ -421,7 +444,7 @@ cooperation_core::NoNetworkWidget - + Please connect to the network 請連接網絡 @@ -429,32 +452,32 @@ cooperation_core::NoResultTipWidget - + 1. Enable cross-end collaborative applications. Applications on the UOS can be downloaded from the App Store, and applications on the Windows side can be downloaded from: 1. 開啟跨端協同應用,UOS端應用可在應用商店下載,Windows端下載地址: - + 2. On the same LAN as the device 2. 與本設備在同一局域網 - + 3. File Manager-Settings-File Drop-Allow the following users to drop files to me -"Everyone on the same LAN" 3. 設置-基礎設置-可被發現-“允許同一局域網下的所有人” - + 3. Settings-Basic Settings-Discovery Mode-"Allow everyone in the same LAN" 3. 設置-基礎設置-可被發現-“允許同一局域網下的所有人” - + 4. Try entering the target device IP in the top search box 4. 嘗試在頂部搜索框輸入目標設備IP - + Unable to find collaborative device? 找不到協同設備? @@ -462,7 +485,7 @@ cooperation_core::NoResultWidget - + No device found 未搜尋到任何設備 @@ -692,13 +715,13 @@ cooperation_core::WorkspaceWidgetPrivate - - + + Please enter the device ip/name of the collaborator 請輸入協同對象的設備IP/暱稱 - + Nearby Device 附近的設備 diff --git a/translations/dde-cooperation/dde-cooperation_zh_TW.ts b/translations/dde-cooperation/dde-cooperation_zh_TW.ts index 309f1b79f..e89311b4c 100644 --- a/translations/dde-cooperation/dde-cooperation_zh_TW.ts +++ b/translations/dde-cooperation/dde-cooperation_zh_TW.ts @@ -17,7 +17,7 @@ cooperation_core::BottomLabel - + Local IP: %1 本機IP:%1 @@ -322,45 +322,68 @@ 離線 + + cooperation_core::DiscoverController + + + Confirm + + + + + Close + 關閉 + + + + Please click to confirm to enable the LAN discovery service! + + + + + If you want discovery through the mdns service, you need to enable the avahi service + + + cooperation_core::FirstTipWidget - + First step 第1步 - + The opposite end opens the application and connects to the same network 对端开启应用,并连接同一网络 - + Second step 第2步 - + Third step 第3步 - + to send the file 发送文件 - + to connect to the peer device 连接对端设备 - + Enter the peer IP in the search box 在搜索框输入对端IP - + Click 点击 @@ -387,7 +410,7 @@ cooperation_core::LookingForDeviceWidget - + Looking for devices 正在尋找裝置 @@ -421,7 +444,7 @@ cooperation_core::NoNetworkWidget - + Please connect to the network 請連接網路 @@ -429,32 +452,32 @@ cooperation_core::NoResultTipWidget - + 1. Enable cross-end collaborative applications. Applications on the UOS can be downloaded from the App Store, and applications on the Windows side can be downloaded from: 1. 開啟跨端協同應用,UOS端應用可在應用商店下載,Windows端下載網址: - + 2. On the same LAN as the device 2. 與本裝置在同一區域網路 - + 3. File Manager-Settings-File Drop-Allow the following users to drop files to me -"Everyone on the same LAN" 3. 檔案管理器-設定-文件投送-允許以下使用者向我投送文件-“同一區域網路下的所有人” - + 3. Settings-Basic Settings-Discovery Mode-"Allow everyone in the same LAN" 3. 設定-基礎設定-可被發現-“允許同一區域網路下的所有人” - + 4. Try entering the target device IP in the top search box 4. 嘗試在頂部搜尋框輸入目標裝置IP - + Unable to find collaborative device? 找不到協同裝置? @@ -462,7 +485,7 @@ cooperation_core::NoResultWidget - + No device found 未搜尋到任何裝置 @@ -692,13 +715,13 @@ cooperation_core::WorkspaceWidgetPrivate - - + + Please enter the device ip/name of the collaborator 請輸入協同物件的裝置IP/暱稱 - + Nearby Device 附近的裝置