├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── doc ├── CMakeLists.txt ├── Doxyfile.in ├── index.md └── overrides.css ├── examples ├── CMakeLists.txt ├── browser │ ├── CMakeLists.txt │ ├── browser.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── servicemodel.cpp │ └── servicemodel.h └── provider │ ├── CMakeLists.txt │ ├── mainwindow.cpp │ ├── mainwindow.h │ └── provider.cpp ├── src ├── CMakeLists.txt ├── include │ └── qmdnsengine │ │ ├── abstractserver.h │ │ ├── bitmap.h │ │ ├── browser.h │ │ ├── cache.h │ │ ├── dns.h │ │ ├── hostname.h │ │ ├── mdns.h │ │ ├── message.h │ │ ├── prober.h │ │ ├── provider.h │ │ ├── query.h │ │ ├── record.h │ │ ├── resolver.h │ │ ├── server.h │ │ └── service.h ├── qmdnsengine_export.h.in ├── resource.rc.in └── src │ ├── abstractserver.cpp │ ├── bitmap.cpp │ ├── bitmap_p.h │ ├── browser.cpp │ ├── browser_p.h │ ├── cache.cpp │ ├── cache_p.h │ ├── dns.cpp │ ├── hostname.cpp │ ├── hostname_p.h │ ├── mdns.cpp │ ├── message.cpp │ ├── message_p.h │ ├── prober.cpp │ ├── prober_p.h │ ├── provider.cpp │ ├── provider_p.h │ ├── query.cpp │ ├── query_p.h │ ├── record.cpp │ ├── record_p.h │ ├── resolver.cpp │ ├── resolver_p.h │ ├── server.cpp │ ├── server_p.h │ ├── service.cpp │ └── service_p.h └── tests ├── CMakeLists.txt ├── TestBrowser.cpp ├── TestCache.cpp ├── TestDns.cpp ├── TestHostname.cpp ├── TestProber.cpp ├── TestProvider.cpp ├── TestResolver.cpp └── common ├── CMakeLists.txt ├── testserver.cpp ├── testserver.h ├── util.cpp └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | /CMakeLists.txt.user 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(qmdnsengine) 3 | 4 | set(PROJECT_NAME "QMdnsEngine") 5 | set(PROJECT_DESCRIPTION "Multicast DNS library for Qt applications") 6 | set(PROJECT_AUTHOR "Nathan Osman") 7 | set(PROJECT_URL "https://github.com/nitroshare/qmdnsengine") 8 | 9 | set(PROJECT_VERSION_MAJOR 0) 10 | set(PROJECT_VERSION_MINOR 2) 11 | set(PROJECT_VERSION_PATCH 1) 12 | set(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}) 13 | 14 | # Build a shared library by default 15 | option(BUILD_SHARED_LIBS "Build QMdnsEngine as a shared library" ON) 16 | 17 | set(BIN_INSTALL_DIR bin CACHE STRING "Binary installation directory relative to the install prefix") 18 | set(LIB_INSTALL_DIR lib CACHE STRING "Library installation directory relative to the install prefix") 19 | set(INCLUDE_INSTALL_DIR include CACHE STRING "Header installation directory relative to the install prefix") 20 | 21 | set(DOC_INSTALL_DIR share/doc/qmdnsengine CACHE STRING "Documentation installation directory relative to the install prefix") 22 | set(EXAMPLES_INSTALL_DIR "${LIB_INSTALL_DIR}/qmdnsengine/examples" CACHE STRING "Examples installation directory relative to the install prefix") 23 | 24 | if (CMAKE_PREFIX_PATH) 25 | message( STATUS "QMdnsEngine: Use CMAKE_PREFIX_PATH to locate Qt version to be used: ${CMAKE_PREFIX_PATH}" ) 26 | endif() 27 | 28 | find_package(QT NAMES Qt6 Qt5 COMPONENTS Network REQUIRED) 29 | message( STATUS "QMdnsEngine: Found Qt Version: ${QT_VERSION}" ) 30 | 31 | if (${QT_VERSION_MAJOR} GREATER_EQUAL 6 ) 32 | SET(QT_MIN_VERSION "6.0.0") 33 | else() 34 | SET(QT_MIN_VERSION "5.7.0") 35 | endif() 36 | 37 | if ( "${QT_VERSION}" VERSION_LESS "${QT_MIN_VERSION}" ) 38 | message( FATAL_ERROR "Your Qt version is to old! Minimum required ${QT_MIN_VERSION}" ) 39 | endif() 40 | 41 | find_package(Qt${QT_VERSION_MAJOR} ${QT_VERSION} COMPONENTS Network REQUIRED) 42 | 43 | message( STATUS "QMdnsEngine: Qt version used: ${QT_VERSION}" ) 44 | 45 | set(CMAKE_AUTOMOC ON) 46 | 47 | # Add compilation flags for warnings 48 | if (MSVC) 49 | add_compile_options(/W4) 50 | else() 51 | add_compile_options(-Wall -Wextra -pedantic) 52 | endif() 53 | 54 | add_subdirectory(src) 55 | 56 | option(BUILD_DOC "Build Doxygen documentation" OFF) 57 | if(BUILD_DOC) 58 | find_package(Doxygen REQUIRED) 59 | add_subdirectory(doc) 60 | endif() 61 | 62 | option(BUILD_EXAMPLES "Build example applications" OFF) 63 | if(BUILD_EXAMPLES) 64 | find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED) 65 | add_subdirectory(examples) 66 | endif() 67 | 68 | option(BUILD_TESTS "Build test suite" OFF) 69 | if(BUILD_TESTS) 70 | find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Test REQUIRED) 71 | enable_testing() 72 | add_subdirectory(tests) 73 | endif() 74 | 75 | set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}") 76 | set(CPACK_PACKAGE_VENDOR "${PROJECT_AUTHOR}") 77 | set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) 78 | set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) 79 | set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) 80 | 81 | set(CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "Documentation") 82 | set(CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION "Documentation generated for the library") 83 | set(CPACK_COMPONENT_EXAMPLES_DISPLAY_NAME "Examples") 84 | set(CPACK_COMPONENT_EXAMPLES_DESCRIPTION "Sample applications using the library") 85 | 86 | include(CPack) 87 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Nathan Osman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## QMdnsEngine 2 | 3 | [![Build Status](https://ci.quickmediasolutions.com/job/qmdnsengine/badge/icon)](https://ci.quickmediasolutions.com/job/qmdnsengine/) 4 | [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://opensource.org/licenses/MIT) 5 | 6 | This library provides an implementation of multicast DNS as per [RFC 6762](https://tools.ietf.org/html/rfc6762). 7 | 8 | ### Documentation 9 | 10 | To learn more about building and using the library, please visit this page: 11 | 12 | https://ci.quickmediasolutions.com/job/qmdnsengine-documentation/doxygen/ 13 | -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | configure_file(Doxyfile.in "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile") 2 | 3 | add_custom_target(doc ALL 4 | "${DOXYGEN_EXECUTABLE}" \"${CMAKE_CURRENT_BINARY_DIR}/Doxyfile\" 5 | ) 6 | 7 | install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html" 8 | DESTINATION "${DOC_INSTALL_DIR}" 9 | COMPONENT documentation 10 | ) 11 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | QMdnsEngine provides an implementation of multicast DNS as per RFC 6762. 2 | 3 | Some of QMdnsEngine's features include: 4 | 5 | - Supports Windows, macOS, Linux, and most other platforms supported by Qt 6 | - Requires only QtCore and QtNetwork - no other dependencies 7 | - Includes an exhaustive set of unit tests 8 | 9 | ## Build Requirements 10 | 11 | QMdnsEngine requires the following in order to build the library: 12 | 13 | - CMake 3.2+ 14 | - Qt 5.4+ 15 | - C++ compiler with C++11 support 16 | 17 | ## Build Instructions 18 | 19 | QMdnsEngine uses CMake for building the library. The options shown below allow the build to be customized: 20 | 21 | - Installation: 22 | - `BIN_INSTALL_DIR` - binary installation directory relative to the install prefix 23 | - `LIB_INSTALL_DIR` - library installation directory relative to the install prefix 24 | - `INCLUDE_INSTALL_DIR` - header installation directory relative to the install prefix 25 | - Customization: 26 | - `BUILD_DOC` - build the documentation for the library with Doxygen 27 | - `BUILD_EXAMPLES` - build example applications that use the library 28 | - `BUILD_TESTS` - build the test suite 29 | 30 | ## Basic Provider Usage 31 | 32 | To provide a service on the local network, begin by creating a [Server](@ref QMdnsEngine::Server), a [Hostname](@ref QMdnsEngine::Hostname), and a [Provider](@ref QMdnsEngine::Provider): 33 | 34 | @code 35 | QMdnsEngine::Server server; 36 | QMdnsEngine::Hostname hostname(&server); 37 | QMdnsEngine::Provider provider(&server, &hostname); 38 | @endcode 39 | 40 | The server sends and receives raw DNS packets. The hostname finds a unique hostname that is not in use to identify the device. Lastly, the provider manages the records for a service. 41 | 42 | The next step is to create the service and update the provider: 43 | 44 | @code 45 | QMdnsEngine::Service service; 46 | service.setType("_http._tcp.local."); 47 | service.setName("My Service"); 48 | service.setPort(1234); 49 | provider.update(service); 50 | @endcode 51 | 52 | That's it! As long as the provider remains in scope, the service will be available on the local network and other devices will be able to find it. 53 | 54 | ## Basic Browser Usage 55 | 56 | To find services on the local network, begin by creating a [Server](@ref QMdnsEngine::Server) and a [Browser](@ref QMdnsEngine::Browser): 57 | 58 | @code 59 | QMdnsEngine::Server server; 60 | QMdnsEngine::Cache cache; 61 | QMdnsEngine::Browser browser(&server, "_http._tcp.local.", &cache); 62 | @endcode 63 | 64 | The cache is optional but helps save time later when resolving services. The browser is provided with a service type which is used to filter services. 65 | 66 | To receive a notification when services are added, connect to the [Browser::serviceAdded()](@ref QMdnsEngine::Browser::serviceAdded) signal: 67 | 68 | @code 69 | QObject::connect(&browser, &QMdnsEngine::Browser::serviceAdded, 70 | [](const QMdnsEngine::Service &service) { 71 | qDebug() << service.name() << "discovered!"; 72 | } 73 | ); 74 | @endcode 75 | 76 | To resolve the service, use a [Resolver](@ref QMdnsEngine::Resolver): 77 | 78 | @code 79 | QMdnsEngine::Resolver resolver(&server, service.hostname(), &cache); 80 | QObject::connect(&resolver, &QMdnsEngine::Resolver::resolved, 81 | [](const QHostAddress &address) { 82 | qDebug() << "resolved to" << address; 83 | } 84 | ); 85 | @endcode 86 | 87 | Note that [Resolver::resolved()](@ref QMdnsEngine::Resolver::resolved) may be emitted once for each address provided by the service. 88 | -------------------------------------------------------------------------------- /doc/overrides.css: -------------------------------------------------------------------------------- 1 | #titlearea { 2 | margin: 10px 0; 3 | } 4 | #projectalign { 5 | padding-left: 0 !important; 6 | } 7 | .fragment { 8 | padding: 4px !important; 9 | } 10 | @media (min-width: 992px) { 11 | #top, .header, .contents, .footer { 12 | margin: auto !important; 13 | max-width: 900px; 14 | } 15 | #titlearea { 16 | border-bottom: none; 17 | } 18 | #main-nav, #navrow1 { 19 | border-radius: 4px 4px 0 0; 20 | border-top: 1px solid #c4cfe5; 21 | position: relative; 22 | } 23 | #main-nav, #navrow1, .header, .nav-path, .contents { 24 | border-left: 1px solid #c4cfe5; 25 | border-right: 1px solid #c4cfe5; 26 | box-sizing: border-box; 27 | } 28 | .contents { 29 | padding: 14px; 30 | } 31 | hr.footer { 32 | border-top: 1px solid #c4cfe5; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(EXAMPLES 2 | browser 3 | provider 4 | ) 5 | 6 | foreach(EXAMPLE ${EXAMPLES}) 7 | set(EXAMPLE_DIR "${EXAMPLES_INSTALL_DIR}/${EXAMPLE}") 8 | add_subdirectory(${EXAMPLE}) 9 | install(DIRECTORY ${EXAMPLE} 10 | DESTINATION "${EXAMPLES_INSTALL_DIR}" 11 | COMPONENT examples 12 | ) 13 | endforeach() 14 | -------------------------------------------------------------------------------- /examples/browser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SRC 2 | browser.cpp 3 | mainwindow.cpp 4 | servicemodel.cpp 5 | ) 6 | 7 | add_executable(browser WIN32 ${SRC}) 8 | 9 | set_target_properties(browser PROPERTIES 10 | CXX_STANDARD 11 11 | ) 12 | 13 | target_link_libraries(browser qmdnsengine Qt${QT_VERSION_MAJOR}::Widgets) 14 | 15 | install(TARGETS browser 16 | RUNTIME DESTINATION "${EXAMPLE_DIR}" 17 | COMPONENT examples 18 | ) 19 | -------------------------------------------------------------------------------- /examples/browser/browser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | #include "mainwindow.h" 28 | 29 | int main(int argc, char *argv[]) 30 | { 31 | QApplication app(argc, argv); 32 | 33 | MainWindow mainWindow; 34 | mainWindow.show(); 35 | 36 | return app.exec(); 37 | } 38 | -------------------------------------------------------------------------------- /examples/browser/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | 40 | #include "mainwindow.h" 41 | #include "servicemodel.h" 42 | 43 | Q_DECLARE_METATYPE(QMdnsEngine::Service) 44 | 45 | MainWindow::MainWindow() 46 | : mServiceModel(nullptr), 47 | mResolver(nullptr) 48 | { 49 | setWindowTitle(tr("mDNS Browser")); 50 | resize(640, 480); 51 | 52 | mServiceType = new QLineEdit(tr("_http._tcp.local.")); 53 | mStartStop = new QPushButton(tr("Browse")); 54 | mServices = new QListView; 55 | mAddresses = new QListWidget; 56 | mAttributes = new QTableWidget; 57 | mAttributes->setSelectionBehavior(QAbstractItemView::SelectRows); 58 | 59 | QVBoxLayout *rootLayout = new QVBoxLayout; 60 | QWidget *widget = new QWidget; 61 | widget->setLayout(rootLayout); 62 | setCentralWidget(widget); 63 | 64 | QCheckBox *any = new QCheckBox(tr("Any")); 65 | 66 | QHBoxLayout *typeLayout = new QHBoxLayout; 67 | typeLayout->addWidget(mServiceType, 1); 68 | typeLayout->addWidget(any); 69 | typeLayout->addWidget(mStartStop); 70 | rootLayout->addLayout(typeLayout); 71 | 72 | QSplitter *vSplitter = new QSplitter; 73 | vSplitter->setOrientation(Qt::Vertical); 74 | vSplitter->addWidget(mAddresses); 75 | vSplitter->addWidget(mAttributes); 76 | 77 | QSplitter *hSplitter = new QSplitter; 78 | hSplitter->addWidget(mServices); 79 | hSplitter->addWidget(vSplitter); 80 | 81 | QHBoxLayout *servicesLayout = new QHBoxLayout; 82 | servicesLayout->addWidget(hSplitter); 83 | rootLayout->addLayout(servicesLayout); 84 | 85 | connect(any, &QCheckBox::toggled, this, &MainWindow::onToggled); 86 | connect(mStartStop, &QPushButton::clicked, this, &MainWindow::onClicked); 87 | } 88 | 89 | void MainWindow::onToggled(bool checked) 90 | { 91 | if (checked) { 92 | mServiceType->setText(QMdnsEngine::MdnsBrowseType); 93 | } 94 | mServiceType->setEnabled(!checked); 95 | } 96 | 97 | void MainWindow::onClicked() 98 | { 99 | if (mServiceModel) { 100 | mServices->setModel(nullptr); 101 | delete mServiceModel; 102 | mAttributes->clear(); 103 | mAttributes->setColumnCount(0); 104 | } 105 | 106 | mServiceModel = new ServiceModel(&mServer, mServiceType->text().toUtf8()); 107 | mServices->setModel(mServiceModel); 108 | 109 | connect(mServices->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::onSelectionChanged); 110 | } 111 | 112 | void MainWindow::onSelectionChanged(const QItemSelection &selected, const QItemSelection &) 113 | { 114 | mAddresses->clear(); 115 | mAttributes->clear(); 116 | mAttributes->setColumnCount(0); 117 | 118 | if (mResolver) { 119 | delete mResolver; 120 | mResolver = nullptr; 121 | } 122 | 123 | if (selected.count()) { 124 | auto service = mServiceModel->data(selected.at(0).topLeft(), Qt::UserRole).value(); 125 | 126 | // Show TXT values 127 | auto attributes = service.attributes(); 128 | mAttributes->setRowCount(attributes.keys().count()); 129 | mAttributes->setColumnCount(2); 130 | mAttributes->setHorizontalHeaderLabels({tr("Key"), tr("Value")}); 131 | mAttributes->horizontalHeader()->setStretchLastSection(true); 132 | mAttributes->verticalHeader()->setVisible(false); 133 | int row = 0; 134 | for (auto i = attributes.constBegin(); i != attributes.constEnd(); ++i, ++row) { 135 | mAttributes->setItem(row, 0, new QTableWidgetItem(QString(i.key()))); 136 | mAttributes->setItem(row, 1, new QTableWidgetItem(QString(i.value()))); 137 | } 138 | 139 | // Resolve the address 140 | mResolver = new QMdnsEngine::Resolver(&mServer, service.hostname(), nullptr, this); 141 | connect(mResolver, &QMdnsEngine::Resolver::resolved, [this](const QHostAddress &address) { 142 | mAddresses->addItem(address.toString()); 143 | }); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /examples/browser/mainwindow.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef MAINWINDOW_H 26 | #define MAINWINDOW_H 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | class QItemSelection; 34 | class QLineEdit; 35 | class QListView; 36 | class QListWidget; 37 | class QPushButton; 38 | class QTableWidget; 39 | 40 | class ServiceModel; 41 | 42 | class MainWindow : public QMainWindow 43 | { 44 | Q_OBJECT 45 | 46 | public: 47 | 48 | MainWindow(); 49 | 50 | private Q_SLOTS: 51 | 52 | void onToggled(bool checked); 53 | void onClicked(); 54 | void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); 55 | 56 | private: 57 | 58 | QMdnsEngine::Server mServer; 59 | ServiceModel *mServiceModel; 60 | 61 | QLineEdit *mServiceType; 62 | QPushButton *mStartStop; 63 | QListView *mServices; 64 | QListWidget *mAddresses; 65 | QTableWidget *mAttributes; 66 | 67 | QMdnsEngine::Resolver *mResolver; 68 | }; 69 | 70 | #endif // MAINWINDOW_H 71 | -------------------------------------------------------------------------------- /examples/browser/servicemodel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include "servicemodel.h" 26 | 27 | Q_DECLARE_METATYPE(QMdnsEngine::Service) 28 | 29 | ServiceModel::ServiceModel(QMdnsEngine::Server *server, const QByteArray &type) 30 | : mBrowser(server, type, &cache) 31 | { 32 | connect(&mBrowser, &QMdnsEngine::Browser::serviceAdded, this, &ServiceModel::onServiceAdded); 33 | connect(&mBrowser, &QMdnsEngine::Browser::serviceUpdated, this, &ServiceModel::onServiceUpdated); 34 | connect(&mBrowser, &QMdnsEngine::Browser::serviceRemoved, this, &ServiceModel::onServiceRemoved); 35 | } 36 | 37 | int ServiceModel::rowCount(const QModelIndex &) const 38 | { 39 | return mServices.count(); 40 | } 41 | 42 | QVariant ServiceModel::data(const QModelIndex &index, int role) const 43 | { 44 | // Ensure the index points to a valid row 45 | if (!index.isValid() || index.row() < 0 || index.row() >= mServices.count()) { 46 | return QVariant(); 47 | } 48 | 49 | QMdnsEngine::Service service = mServices.at(index.row()); 50 | 51 | switch (role) { 52 | case Qt::DisplayRole: 53 | return QString("%1 (%2)") 54 | .arg(QString(service.name())) 55 | .arg(QString(service.type())); 56 | case Qt::UserRole: 57 | return QVariant::fromValue(service); 58 | } 59 | 60 | return QVariant(); 61 | } 62 | 63 | void ServiceModel::onServiceAdded(const QMdnsEngine::Service &service) 64 | { 65 | beginInsertRows(QModelIndex(), mServices.count(), mServices.count()); 66 | mServices.append(service); 67 | endInsertRows(); 68 | } 69 | 70 | void ServiceModel::onServiceUpdated(const QMdnsEngine::Service &service) 71 | { 72 | int i = findService(service.name()); 73 | if (i != -1) { 74 | mServices.replace(i, service); 75 | emit dataChanged(index(i), index(i)); 76 | } 77 | } 78 | 79 | void ServiceModel::onServiceRemoved(const QMdnsEngine::Service &service) 80 | { 81 | int i = findService(service.name()); 82 | if (i != -1) { 83 | beginRemoveRows(QModelIndex(), i, i); 84 | mServices.removeAt(i); 85 | endRemoveRows(); 86 | } 87 | } 88 | 89 | int ServiceModel::findService(const QByteArray &name) 90 | { 91 | for (auto i = mServices.constBegin(); i != mServices.constEnd(); ++i) { 92 | if ((*i).name() == name) { 93 | return i - mServices.constBegin(); 94 | } 95 | } 96 | return -1; 97 | } 98 | -------------------------------------------------------------------------------- /examples/browser/servicemodel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef SERVICEMODEL_H 26 | #define SERVICEMODEL_H 27 | 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | class ServiceModel : public QAbstractListModel 37 | { 38 | Q_OBJECT 39 | 40 | public: 41 | 42 | ServiceModel(QMdnsEngine::Server *server, const QByteArray &type); 43 | 44 | virtual int rowCount(const QModelIndex &parent) const; 45 | virtual QVariant data(const QModelIndex &index, int role) const; 46 | 47 | private Q_SLOTS: 48 | 49 | void onServiceAdded(const QMdnsEngine::Service &service); 50 | void onServiceUpdated(const QMdnsEngine::Service &service); 51 | void onServiceRemoved(const QMdnsEngine::Service &service); 52 | 53 | private: 54 | 55 | int findService(const QByteArray &name); 56 | 57 | QMdnsEngine::Cache cache; 58 | QMdnsEngine::Browser mBrowser; 59 | QList mServices; 60 | }; 61 | 62 | #endif // SERVICEMODEL_H 63 | -------------------------------------------------------------------------------- /examples/provider/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SRC 2 | mainwindow.cpp 3 | provider.cpp 4 | ) 5 | 6 | add_executable(provider WIN32 ${SRC}) 7 | 8 | target_link_libraries(provider qmdnsengine Qt${QT_VERSION_MAJOR}::Widgets) 9 | 10 | install(TARGETS provider 11 | RUNTIME DESTINATION "${EXAMPLE_DIR}" 12 | COMPONENT examples 13 | ) 14 | -------------------------------------------------------------------------------- /examples/provider/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include "mainwindow.h" 40 | 41 | MainWindow::MainWindow() 42 | : mHostname(&mServer), 43 | mProvider(0), 44 | mServiceName(new QLineEdit(tr("Test Service"))), 45 | mServiceType(new QLineEdit(tr("_test._tcp.local."))), 46 | mServicePort(new QLineEdit("1234")), 47 | mButton(new QPushButton(buttonCaption())), 48 | mShowQueries(new QCheckBox(tr("Show queries"))), 49 | mLog(new QTextEdit(tr("Initializing application"))) 50 | { 51 | setWindowTitle(tr("mDNS Provider")); 52 | resize(640, 480); 53 | 54 | mServicePort->setValidator(new QIntValidator(1, 65535, this)); 55 | mLog->setReadOnly(true); 56 | 57 | // Create the root layout 58 | QVBoxLayout *rootLayout = new QVBoxLayout; 59 | QWidget *widget = new QWidget; 60 | widget->setLayout(rootLayout); 61 | setCentralWidget(widget); 62 | 63 | // Create the horizontal layout for the grid and buttons 64 | QHBoxLayout *upperLayout = new QHBoxLayout; 65 | rootLayout->addLayout(upperLayout); 66 | 67 | // Create the grid layout for the line edits 68 | QGridLayout *gridLayout = new QGridLayout; 69 | gridLayout->addWidget(new QLabel(tr("Service name:")), 0, 0); 70 | gridLayout->addWidget(mServiceName, 0, 1); 71 | gridLayout->addWidget(new QLabel(tr("Service type:")), 1, 0); 72 | gridLayout->addWidget(mServiceType, 1, 1); 73 | gridLayout->addWidget(new QLabel(tr("Service port:")), 2, 0); 74 | gridLayout->addWidget(mServicePort, 2, 1); 75 | upperLayout->addLayout(gridLayout); 76 | 77 | // Create the layout for the buttons 78 | QVBoxLayout *buttonLayout = new QVBoxLayout; 79 | buttonLayout->addWidget(mButton); 80 | buttonLayout->addWidget(mShowQueries); 81 | buttonLayout->addWidget(new QWidget, 1); 82 | upperLayout->addLayout(buttonLayout); 83 | 84 | // Add the log 85 | rootLayout->addWidget(mLog, 1); 86 | 87 | connect(mButton, &QPushButton::clicked, this, &MainWindow::onClicked); 88 | connect(&mHostname, &QMdnsEngine::Hostname::hostnameChanged, this, &MainWindow::onHostnameChanged); 89 | connect(&mServer, &QMdnsEngine::Server::messageReceived, this, &MainWindow::onMessageReceived); 90 | } 91 | 92 | void MainWindow::onClicked() 93 | { 94 | if (mProvider) { 95 | mLog->append(tr("Destroying provider")); 96 | delete mProvider; 97 | mProvider = 0; 98 | } else { 99 | mLog->append(tr("Creating provider")); 100 | QMdnsEngine::Service service; 101 | service.setName(mServiceName->text().toUtf8()); 102 | service.setType(mServiceType->text().toUtf8()); 103 | service.setPort(mServicePort->text().toUShort()); 104 | mProvider = new QMdnsEngine::Provider(&mServer, &mHostname, this); 105 | mProvider->update(service); 106 | } 107 | mButton->setText(buttonCaption()); 108 | } 109 | 110 | void MainWindow::onHostnameChanged(const QByteArray &hostname) 111 | { 112 | mLog->append(tr("Hostname changed to %1").arg(QString(hostname))); 113 | } 114 | 115 | void MainWindow::onMessageReceived(const QMdnsEngine::Message &message) 116 | { 117 | if (!mShowQueries->isChecked()) { 118 | return; 119 | } 120 | const auto queries = message.queries(); 121 | for (const QMdnsEngine::Query &query : queries) { 122 | mLog->append( 123 | tr("[%1] %2") 124 | .arg(QMdnsEngine::typeName(query.type())) 125 | .arg(QString(query.name())) 126 | ); 127 | } 128 | } 129 | 130 | QString MainWindow::buttonCaption() const 131 | { 132 | return mProvider ? tr("Stop") : tr("Start"); 133 | } 134 | -------------------------------------------------------------------------------- /examples/provider/mainwindow.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef MAINWINDOW_H 26 | #define MAINWINDOW_H 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | class QCheckBox; 36 | class QPushButton; 37 | class QLineEdit; 38 | class QTextEdit; 39 | 40 | class MainWindow : public QMainWindow 41 | { 42 | Q_OBJECT 43 | 44 | public: 45 | 46 | MainWindow(); 47 | 48 | private Q_SLOTS: 49 | 50 | void onClicked(); 51 | void onHostnameChanged(const QByteArray &hostname); 52 | void onMessageReceived(const QMdnsEngine::Message &message); 53 | 54 | private: 55 | 56 | QString buttonCaption() const; 57 | 58 | QMdnsEngine::Server mServer; 59 | QMdnsEngine::Hostname mHostname; 60 | QMdnsEngine::Provider *mProvider; 61 | 62 | QLineEdit *mServiceName; 63 | QLineEdit *mServiceType; 64 | QLineEdit *mServicePort; 65 | 66 | QPushButton *mButton; 67 | QCheckBox *mShowQueries; 68 | 69 | QTextEdit *mLog; 70 | }; 71 | 72 | #endif // MAINWINDOW_H 73 | -------------------------------------------------------------------------------- /examples/provider/provider.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | #include "mainwindow.h" 28 | 29 | int main(int argc, char *argv[]) 30 | { 31 | QApplication app(argc, argv); 32 | 33 | MainWindow mainWindow; 34 | mainWindow.show(); 35 | 36 | return app.exec(); 37 | } 38 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | configure_file(qmdnsengine_export.h.in "${CMAKE_CURRENT_BINARY_DIR}/qmdnsengine_export.h") 2 | 3 | set(HEADERS 4 | include/qmdnsengine/abstractserver.h 5 | include/qmdnsengine/bitmap.h 6 | include/qmdnsengine/browser.h 7 | include/qmdnsengine/cache.h 8 | include/qmdnsengine/dns.h 9 | include/qmdnsengine/hostname.h 10 | include/qmdnsengine/mdns.h 11 | include/qmdnsengine/message.h 12 | include/qmdnsengine/prober.h 13 | include/qmdnsengine/provider.h 14 | include/qmdnsengine/query.h 15 | include/qmdnsengine/record.h 16 | include/qmdnsengine/resolver.h 17 | include/qmdnsengine/server.h 18 | include/qmdnsengine/service.h 19 | "${CMAKE_CURRENT_BINARY_DIR}/qmdnsengine_export.h" 20 | ) 21 | 22 | set(SRC 23 | src/abstractserver.cpp 24 | src/bitmap.cpp 25 | src/browser.cpp 26 | src/cache.cpp 27 | src/dns.cpp 28 | src/hostname.cpp 29 | src/mdns.cpp 30 | src/message.cpp 31 | src/prober.cpp 32 | src/provider.cpp 33 | src/query.cpp 34 | src/record.cpp 35 | src/resolver.cpp 36 | src/server.cpp 37 | src/service.cpp 38 | ) 39 | 40 | if(WIN32) 41 | configure_file(resource.rc.in "${CMAKE_CURRENT_BINARY_DIR}/resource.rc") 42 | set(SRC ${SRC} "${CMAKE_CURRENT_BINARY_DIR}/resource.rc") 43 | endif() 44 | 45 | add_library(qmdnsengine ${HEADERS} ${SRC}) 46 | 47 | if ( "${QT_VERSION}" VERSION_GREATER_EQUAL "6.6.0" ) 48 | message( STATUS "QMdnsEngine: Enable C++17 for Qt 6.6 and newer" ) 49 | set_target_properties(qmdnsengine PROPERTIES 50 | CXX_STANDARD 17 51 | ) 52 | else() 53 | set_target_properties(qmdnsengine PROPERTIES 54 | CXX_STANDARD 11 55 | ) 56 | endif() 57 | 58 | set_target_properties(qmdnsengine PROPERTIES 59 | CXX_STANDARD_REQUIRED ON 60 | DEFINE_SYMBOL QT_NO_SIGNALS_SLOTS_KEYWORDS 61 | DEFINE_SYMBOL QT_NO_FOREACH 62 | DEFINE_SYMBOL QMDNSENGINE_LIBRARY 63 | PUBLIC_HEADER "${HEADERS}" 64 | VERSION ${PROJECT_VERSION} 65 | SOVERSION ${PROJECT_VERSION_MAJOR} 66 | ) 67 | 68 | target_include_directories(qmdnsengine PUBLIC 69 | "$" 70 | "$" 71 | "$" 72 | ) 73 | 74 | target_link_libraries(qmdnsengine Qt${QT_VERSION_MAJOR}::Network) 75 | 76 | install(TARGETS qmdnsengine 77 | EXPORT qmdnsengine-export 78 | RUNTIME DESTINATION "${BIN_INSTALL_DIR}" 79 | LIBRARY DESTINATION "${LIB_INSTALL_DIR}" 80 | ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" 81 | PUBLIC_HEADER DESTINATION "${INCLUDE_INSTALL_DIR}/qmdnsengine" 82 | ) 83 | 84 | install(EXPORT qmdnsengine-export 85 | FILE qmdnsengineConfig.cmake 86 | DESTINATION "${LIB_INSTALL_DIR}/cmake/qmdnsengine" 87 | ) 88 | 89 | include(CMakePackageConfigHelpers) 90 | 91 | write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/qmdnsengineConfigVersion.cmake" 92 | VERSION ${PROJECT_VERSION} 93 | COMPATIBILITY SameMajorVersion 94 | ) 95 | 96 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qmdnsengineConfigVersion.cmake" 97 | DESTINATION "${LIB_INSTALL_DIR}/cmake/qmdnsengine" 98 | ) 99 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/abstractserver.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_ABSTRACTSERVER_H 26 | #define QMDNSENGINE_ABSTRACTSERVER_H 27 | 28 | #include 29 | 30 | #include "qmdnsengine_export.h" 31 | 32 | namespace QMdnsEngine 33 | { 34 | 35 | class Message; 36 | 37 | /** 38 | * @brief Base class for sending and receiving DNS messages 39 | * 40 | * Many of the other classes in this library require the ability to send and 41 | * receive DNS messages. By having them use this base class, they become far 42 | * easier to test. Any class derived from this one that implements the pure 43 | * virtual methods can be used for sending and receiving DNS messages. 44 | */ 45 | class QMDNSENGINE_EXPORT AbstractServer : public QObject 46 | { 47 | Q_OBJECT 48 | 49 | public: 50 | 51 | /** 52 | * @brief Abstract constructor 53 | */ 54 | explicit AbstractServer(QObject *parent = 0); 55 | 56 | /** 57 | * @brief Send a message to its provided destination 58 | * 59 | * The message should be sent over the IP protocol specified in the 60 | * message and to the target address and port specified in the message. 61 | */ 62 | virtual void sendMessage(const Message &message) = 0; 63 | 64 | /** 65 | * @brief Send a message to the multicast address on each interface 66 | * 67 | * The message should be sent over both IPv4 and IPv6 on all interfaces. 68 | */ 69 | virtual void sendMessageToAll(const Message &message) = 0; 70 | 71 | Q_SIGNALS: 72 | 73 | /** 74 | * @brief Indicate that a DNS message was received 75 | * @param message newly received message 76 | */ 77 | void messageReceived(const Message &message); 78 | 79 | /** 80 | * @brief Indicate that an error has occurred 81 | * @param message brief description of the error 82 | */ 83 | void error(const QString &message); 84 | }; 85 | 86 | } 87 | 88 | #endif // QMDNSENGINE_ABSTRACTSERVER_H 89 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/bitmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_BITMAP_H 26 | #define QMDNSENGINE_BITMAP_H 27 | 28 | #include "qmdnsengine_export.h" 29 | 30 | namespace QMdnsEngine 31 | { 32 | 33 | class QMDNSENGINE_EXPORT BitmapPrivate; 34 | 35 | /** 36 | * @brief 256-bit bitmap 37 | * 38 | * Bitmaps are used in QMdnsEngine::NSEC records to indicate which records are 39 | * available. Bitmaps in mDNS records use only the first block (block 0). 40 | */ 41 | class QMDNSENGINE_EXPORT Bitmap 42 | { 43 | public: 44 | 45 | /** 46 | * @brief Create an empty bitmap 47 | */ 48 | Bitmap(); 49 | 50 | /** 51 | * @brief Create a copy of an existing bitmap 52 | */ 53 | Bitmap(const Bitmap &other); 54 | 55 | /** 56 | * @brief Assignment operator 57 | */ 58 | Bitmap &operator=(const Bitmap &other); 59 | 60 | /** 61 | * @brief Equality operator 62 | */ 63 | bool operator==(const Bitmap &other); 64 | 65 | /** 66 | * @brief Destroy the bitmap 67 | */ 68 | virtual ~Bitmap(); 69 | 70 | /** 71 | * @brief Retrieve the length of the block in bytes 72 | * 73 | * This method indicates how many bytes are pointed to by the data() 74 | * method. 75 | */ 76 | quint8 length() const; 77 | 78 | /** 79 | * @brief Retrieve a pointer to the underlying data in the bitmap 80 | * 81 | * Use the length() method to determine how many bytes contain valid data. 82 | */ 83 | const quint8 *data() const; 84 | 85 | /** 86 | * @brief Set the data to be stored in the bitmap 87 | * 88 | * The length parameter indicates how many bytes of data are valid. The 89 | * actual bytes are copied to the bitmap. 90 | */ 91 | void setData(quint8 length, const quint8 *data); 92 | 93 | private: 94 | 95 | BitmapPrivate *const d; 96 | }; 97 | 98 | } 99 | 100 | #endif // QMDNSENGINE_BITMAP_H 101 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/browser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_BROWSER_H 26 | #define QMDNSENGINE_BROWSER_H 27 | 28 | #include 29 | #include 30 | 31 | #include "qmdnsengine_export.h" 32 | 33 | namespace QMdnsEngine 34 | { 35 | 36 | class AbstractServer; 37 | class Cache; 38 | class Service; 39 | 40 | class QMDNSENGINE_EXPORT BrowserPrivate; 41 | 42 | /** 43 | * @brief %Browser for local services 44 | * 45 | * This class provides a simple way to discover services on the local network. 46 | * A cache may be provided in the constructor to store records for future 47 | * queries. 48 | * 49 | * To browse for services of any type: 50 | * 51 | * @code 52 | * QMdnsEngine::Browser browser(&server, QMdnsEngine::MdnsBrowseType); 53 | * @endcode 54 | * 55 | * To browse for services of a specific type: 56 | * 57 | * @code 58 | * QMdnsEngine::Browser browser(&server, "_http._tcp.local."); 59 | * @endcode 60 | * 61 | * When a service is found, the serviceAdded() signal is emitted: 62 | * 63 | * @code 64 | * connect(&browser, &QMdnsEngine::Browser::serviceAdded, [](const QMdnsEngine::Service &service) { 65 | * qDebug() << "Service added:" << service.name(); 66 | * }); 67 | * @endcode 68 | * 69 | * The serviceUpdated() and serviceRemoved() signals are emitted when services 70 | * are updated (their properties change) or are removed, respectively. 71 | */ 72 | class QMDNSENGINE_EXPORT Browser : public QObject 73 | { 74 | Q_OBJECT 75 | 76 | public: 77 | 78 | /** 79 | * @brief Create a new browser instance 80 | * @param server server to use for receiving and sending mDNS messages 81 | * @param type service type to browse for 82 | * @param cache DNS cache to use or null to create one 83 | * @param parent QObject 84 | */ 85 | Browser(AbstractServer *server, const QByteArray &type, Cache *cache = 0, QObject *parent = 0); 86 | 87 | Q_SIGNALS: 88 | 89 | /** 90 | * @brief Indicate that a new service has been added 91 | * 92 | * This signal is emitted when the PTR and SRV records for a service are 93 | * received. If TXT records are received later, the serviceUpdated() 94 | * signal will be emitted. 95 | */ 96 | void serviceAdded(const Service &service); 97 | 98 | /** 99 | * @brief Indicate that the specified service was updated 100 | * 101 | * This signal is emitted when the SRV record for a service (identified by 102 | * its name and type) or a TXT record has changed. 103 | */ 104 | void serviceUpdated(const Service &service); 105 | 106 | /** 107 | * @brief Indicate that the specified service was removed 108 | * 109 | * This signal is emitted when an essential record (PTR or SRV) is 110 | * expiring from the cache. This will also occur when an updated PTR or 111 | * SRV record is received with a TTL of 0. 112 | */ 113 | void serviceRemoved(const Service &service); 114 | 115 | private: 116 | 117 | BrowserPrivate *const d; 118 | }; 119 | 120 | } 121 | 122 | #endif // QMDNSENGINE_BROWSER_H 123 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/cache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_CACHE_H 26 | #define QMDNSENGINE_CACHE_H 27 | 28 | #include 29 | #include 30 | 31 | #include "qmdnsengine_export.h" 32 | 33 | namespace QMdnsEngine 34 | { 35 | 36 | class Record; 37 | 38 | class QMDNSENGINE_EXPORT CachePrivate; 39 | 40 | /** 41 | * @brief %Cache for DNS records 42 | * 43 | * Records are added to the cache using the addRecord() method which are then 44 | * stored in the cache until they are considered to have expired, at which 45 | * point they are purged. The shouldQuery() signal is used to indicate when a 46 | * record is approaching expiry and the recordExpired() signal indicates when 47 | * a record has expired (at which point it is removed). 48 | * 49 | * The cache can be queried to retrieve one or more records matching a given 50 | * type. For example, to retrieve all TXT records that match a given name: 51 | * 52 | * @code 53 | * Cache cache; 54 | * 55 | * QList records; 56 | * cache.lookupRecords("My Service._http._tcp.local.", QMdnsEngine::TXT, records); 57 | * 58 | * for (const QMdnsEngine::Record &record : records) { 59 | * qDebug() << "Record:" << record.name(); 60 | * } 61 | * @endcode 62 | * 63 | * Alternatively, lookupRecord() can be used to find a single record. 64 | */ 65 | class QMDNSENGINE_EXPORT Cache : public QObject 66 | { 67 | Q_OBJECT 68 | 69 | public: 70 | 71 | /** 72 | * @brief Create an empty cache. 73 | */ 74 | explicit Cache(QObject *parent = 0); 75 | 76 | /** 77 | * @brief Add a record to the cache 78 | * @param record add this record to the cache 79 | * 80 | * The TTL for the record will be added to the current time to calculate 81 | * when the record expires. Existing records of the same name and type 82 | * will be replaced, resetting their expiration. 83 | */ 84 | void addRecord(const Record &record); 85 | 86 | /** 87 | * @brief Retrieve a single record from the cache 88 | * @param name name of record to retrieve or null for any 89 | * @param type type of record to retrieve or ANY for all types 90 | * @param record storage for the record retrieved 91 | * @return true if a record was retrieved 92 | * 93 | * Some record types allow multiple records to be stored with identical 94 | * names and types. This method will only retrieve the first matching 95 | * record. Use lookupRecords() to obtain all of the records. 96 | */ 97 | bool lookupRecord(const QByteArray &name, quint16 type, Record &record) const; 98 | 99 | /** 100 | * @brief Retrieve multiple records from the cache 101 | * @param name name of records to retrieve or null for any 102 | * @param type type of records to retrieve or ANY for all types 103 | * @param records storage for the records retrieved 104 | * @return true if records were retrieved 105 | */ 106 | bool lookupRecords(const QByteArray &name, quint16 type, QList &records) const; 107 | 108 | Q_SIGNALS: 109 | 110 | /** 111 | * @brief Indicate that a record will expire soon and a new query should be issued 112 | * @param record reference to the record that will soon expire 113 | * 114 | * This signal is emitted when a record reaches approximately 50%, 85%, 115 | * 90%, and 95% of its lifetime. 116 | */ 117 | void shouldQuery(const Record &record); 118 | 119 | /** 120 | * @brief Indicate that the specified record expired 121 | * @param record reference to the record that has expired 122 | */ 123 | void recordExpired(const Record &record); 124 | 125 | private: 126 | 127 | CachePrivate *const d; 128 | }; 129 | 130 | } 131 | 132 | #endif // QMDNSENGINE_CACHE_H 133 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/dns.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_DNS_H 26 | #define QMDNSENGINE_DNS_H 27 | 28 | #include 29 | #include 30 | 31 | #include "qmdnsengine_export.h" 32 | 33 | namespace QMdnsEngine 34 | { 35 | 36 | class Message; 37 | class Record; 38 | 39 | enum { 40 | /// IPv4 address record 41 | A = 1, 42 | /// IPv6 address record 43 | AAAA = 28, 44 | /// Wildcard for cache lookups 45 | ANY = 255, 46 | /// List of records 47 | NSEC = 47, 48 | /// Pointer to hostname 49 | PTR = 12, 50 | /// %Service information 51 | SRV = 33, 52 | /// Arbitrary metadata 53 | TXT = 16 54 | }; 55 | 56 | /** 57 | * @brief Parse a name from a raw DNS packet 58 | * @param packet raw DNS packet data 59 | * @param offset offset into the packet where the name begins 60 | * @param name reference to QByteArray to store the name in 61 | * @return true if no errors occurred 62 | * 63 | * The offset will be incremented by the number of bytes read. Name 64 | * compression requires access to the contents of the packet. 65 | */ 66 | QMDNSENGINE_EXPORT bool parseName(const QByteArray &packet, quint16 &offset, QByteArray &name); 67 | 68 | /** 69 | * @brief Write a name to a raw DNS packet 70 | * @param packet raw DNS packet to write to 71 | * @param offset offset to update with the number of bytes written 72 | * @param name name to write to the packet 73 | * @param nameMap map of names already written to their offsets 74 | * 75 | * The offset will be incremented by the number of bytes read. The name map 76 | * will be updated with offsets of any names written so that it can be passed 77 | * to future invocations of this function. 78 | */ 79 | QMDNSENGINE_EXPORT void writeName(QByteArray &packet, quint16 &offset, const QByteArray &name, QMap &nameMap); 80 | 81 | /** 82 | * @brief Parse a record from a raw DNS packet 83 | * @param packet raw DNS packet data 84 | * @param offset offset into the packet where the record begins 85 | * @param record reference to Record to populate 86 | * @return true if no errors occurred 87 | */ 88 | QMDNSENGINE_EXPORT bool parseRecord(const QByteArray &packet, quint16 &offset, Record &record); 89 | 90 | /** 91 | * @brief Write a record to a raw DNS packet 92 | * @param packet raw DNS packet to write to 93 | * @param offset offset to update with the number of bytes written 94 | * @param record record to write to the packet 95 | * @param nameMap map of names already written to their offsets 96 | */ 97 | QMDNSENGINE_EXPORT void writeRecord(QByteArray &packet, quint16 &offset, Record &record, QMap &nameMap); 98 | 99 | /** 100 | * @brief Populate a Message with data from a raw DNS packet 101 | * @param packet raw DNS packet data 102 | * @param message reference to Message to populate 103 | * @return true if no errors occurred 104 | */ 105 | QMDNSENGINE_EXPORT bool fromPacket(const QByteArray &packet, Message &message); 106 | 107 | /** 108 | * @brief Create a raw DNS packet from a Message 109 | * @param message Message to create the packet from 110 | * @param packet storage for raw DNS packet 111 | */ 112 | QMDNSENGINE_EXPORT void toPacket(const Message &message, QByteArray &packet); 113 | 114 | /** 115 | * @brief Retrieve the string representation of a DNS type 116 | * @param type integer type 117 | * @return human-readable name for the type 118 | */ 119 | QMDNSENGINE_EXPORT QString typeName(quint16 type); 120 | 121 | } 122 | 123 | #endif // QMDNSENGINE_DNS_H 124 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/hostname.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_HOSTNAME_H 26 | #define QMDNSENGINE_HOSTNAME_H 27 | 28 | #include 29 | 30 | #include "qmdnsengine_export.h" 31 | 32 | namespace QMdnsEngine 33 | { 34 | 35 | class AbstractServer; 36 | 37 | class QMDNSENGINE_EXPORT HostnamePrivate; 38 | 39 | /** 40 | * @brief %Hostname reserved for exclusive use 41 | * 42 | * In order to provide services on the local network, a unique hostname must 43 | * be used. This class asserts a hostname (by first confirming that it is not 44 | * in use) and then responds to A and AAAA queries for the hostname. 45 | * 46 | * @code 47 | * QMdnsEngine::Hostname hostname(&server); 48 | * 49 | * connect(&hostname, &QMdnsEngine::Hostname::hostnameChanged, [](const QByteArray &hostname) { 50 | * qDebug() << "New hostname:" << hostname; 51 | * }); 52 | * @endcode 53 | */ 54 | class QMDNSENGINE_EXPORT Hostname : public QObject 55 | { 56 | Q_OBJECT 57 | 58 | public: 59 | 60 | /** 61 | * @brief Create a new hostname 62 | */ 63 | Hostname(AbstractServer *server, QObject *parent = 0); 64 | 65 | /** 66 | * @brief Determine if a hostname has been registered 67 | * 68 | * A hostname is not considered registered until a probe for the desired 69 | * name has been completed and no matching records were received. 70 | */ 71 | bool isRegistered() const; 72 | 73 | /** 74 | * @brief Retrieve the current hostname 75 | * 76 | * This value is only valid when isRegistered() returns true. 77 | */ 78 | QByteArray hostname() const; 79 | 80 | Q_SIGNALS: 81 | 82 | /** 83 | * @brief Indicate that the current hostname has changed 84 | * @param hostname new hostname 85 | */ 86 | void hostnameChanged(const QByteArray &hostname); 87 | 88 | private: 89 | 90 | HostnamePrivate *const d; 91 | }; 92 | 93 | } 94 | 95 | #endif // QMDNSENGINE_HOSTNAME_H 96 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/mdns.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_MDNS_H 26 | #define QMDNSENGINE_MDNS_H 27 | 28 | #include 29 | #include 30 | 31 | #include "qmdnsengine_export.h" 32 | 33 | namespace QMdnsEngine 34 | { 35 | 36 | /** 37 | * @brief Standard port for mDNS 38 | */ 39 | QMDNSENGINE_EXPORT extern const quint16 MdnsPort; 40 | 41 | /** 42 | * @brief Standard IPv4 address for mDNS 43 | */ 44 | QMDNSENGINE_EXPORT extern const QHostAddress MdnsIpv4Address; 45 | 46 | /** 47 | * @brief Standard IPv6 address for mDNS 48 | */ 49 | QMDNSENGINE_EXPORT extern const QHostAddress MdnsIpv6Address; 50 | 51 | /** 52 | * @brief Service type for browsing service types 53 | */ 54 | QMDNSENGINE_EXPORT extern const QByteArray MdnsBrowseType; 55 | 56 | } 57 | 58 | #endif // QMDNSENGINE_MDNS_H 59 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/message.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_MESSAGE_H 26 | #define QMDNSENGINE_MESSAGE_H 27 | 28 | #include 29 | #include 30 | 31 | #include "qmdnsengine_export.h" 32 | 33 | namespace QMdnsEngine 34 | { 35 | 36 | class Query; 37 | class Record; 38 | 39 | class QMDNSENGINE_EXPORT MessagePrivate; 40 | 41 | /** 42 | * @brief DNS message 43 | * 44 | * A DNS message consists of a header and zero or more queries and records. 45 | * Instances of this class are created and initialized by 46 | * [AbstractServer](@ref QMdnsEngine::AbstractServer) when messages are 47 | * received from the network. 48 | * 49 | * If a message is being constructed in reply to one received from the 50 | * network, the reply() method can be used to simplify initialization: 51 | * 52 | * @code 53 | * connect(&server, &QMdnsEngine::Server::messageReceived, [](const QMdnsEngine::Message &message) { 54 | * QMdnsEngine::Message reply; 55 | * reply.reply(message); 56 | * server.sendMessage(reply); 57 | * }); 58 | * @endcode 59 | */ 60 | class QMDNSENGINE_EXPORT Message 61 | { 62 | public: 63 | 64 | /** 65 | * @brief Create an empty message 66 | */ 67 | Message(); 68 | 69 | /** 70 | * @brief Create a copy of an existing message 71 | */ 72 | Message(const Message &other); 73 | 74 | /** 75 | * @brief Assignment operator 76 | */ 77 | Message &operator=(const Message &other); 78 | 79 | /** 80 | * @brief Destroy the message 81 | */ 82 | virtual ~Message(); 83 | 84 | /** 85 | * @brief Retrieve the address for the message 86 | * 87 | * When receiving messages, this is the address that the message was 88 | * received from. 89 | */ 90 | QHostAddress address() const; 91 | 92 | /** 93 | * @brief Set the address for the message 94 | * 95 | * When sending messages, this is the address that the message will be 96 | * sent to. QMdnsEngine::MdnsIpv4Address and QMdnsEngine::MdnsIpv6Address 97 | * can be used for mDNS messages. 98 | */ 99 | void setAddress(const QHostAddress &address); 100 | 101 | /** 102 | * @brief Retrieve the port for the message 103 | * 104 | * When receiving messages, this is the port that the message was received 105 | * from. For traditional queries, this will be an ephemeral port. For mDNS 106 | * queries, this will always equal QMdnsEngine::MdnsPort. 107 | */ 108 | quint16 port() const; 109 | 110 | /** 111 | * @brief Set the port for the message 112 | * 113 | * When sending messages, this is the port that the message will be sent 114 | * to. This should be set to QMdnsEngine::MdnsPort unless the message is a 115 | * reply to a traditional DNS query. 116 | */ 117 | void setPort(quint16 port); 118 | 119 | /** 120 | * @brief Retrieve the transaction ID for the message 121 | * 122 | * This is always set to 1 for mDNS messages. Traditional DNS queries may 123 | * set this to an arbitrary integer. 124 | */ 125 | quint16 transactionId() const; 126 | 127 | /** 128 | * @brief Set the transaction ID for the message 129 | * 130 | * The default transaction ID is 0. This value should not be changed 131 | * unless responding to a traditional DNS query. 132 | */ 133 | void setTransactionId(quint16 transactionId); 134 | 135 | /** 136 | * @brief Determine if the message is a response 137 | */ 138 | bool isResponse() const; 139 | 140 | /** 141 | * @brief Set whether the message is a response 142 | */ 143 | void setResponse(bool isResponse); 144 | 145 | /** 146 | * @brief Determine if the message is truncated 147 | */ 148 | bool isTruncated() const; 149 | 150 | /** 151 | * @brief Set whether the message is truncated 152 | */ 153 | void setTruncated(bool isTruncated); 154 | 155 | /** 156 | * @brief Retrieve a list of queries in the message 157 | */ 158 | QList queries() const; 159 | 160 | /** 161 | * @brief Add a query to the message 162 | */ 163 | void addQuery(const Query &query); 164 | 165 | /** 166 | * @brief Retrieve a list of records in the message 167 | */ 168 | QList records() const; 169 | 170 | /** 171 | * @brief Add a record to the message 172 | */ 173 | void addRecord(const Record &record); 174 | 175 | /** 176 | * @brief Reply to another message 177 | * 178 | * The message will be correctly initialized to respond to the other 179 | * message. This includes setting the target address, port, and 180 | * transaction ID. 181 | */ 182 | void reply(const Message &other); 183 | 184 | private: 185 | 186 | MessagePrivate *const d; 187 | }; 188 | 189 | } 190 | 191 | #endif // QMDNSENGINE_MESSAGE_H 192 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/prober.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_PROBER_H 26 | #define QMDNSENGINE_PROBER_H 27 | 28 | #include 29 | 30 | #include "qmdnsengine_export.h" 31 | 32 | namespace QMdnsEngine 33 | { 34 | 35 | class AbstractServer; 36 | class Record; 37 | 38 | class QMDNSENGINE_EXPORT ProberPrivate; 39 | 40 | /** 41 | * @brief %Prober to confirm that a record is unique 42 | * 43 | * Before responding to queries for a record, its uniqueness on the network 44 | * must be confirmed. This class takes care of probing for existing records 45 | * that match and adjusts the record's name until a unique one is found. 46 | * 47 | * For example, to probe for a SRV record: 48 | * 49 | * @code 50 | * QMdnsEngine::Record record; 51 | * record.setName("My Service._http._tcp.local."); 52 | * record.setType(QMdnsEngine::SRV); 53 | * record.setPort(1234); 54 | * record.setTarget(hostname.hostname()); 55 | * 56 | * QMdnsEngine::Prober prober(&server, record); 57 | * connect(&prober, &QMdnsEngine::Prober::nameConfirmed, [](const QByteArray &name) { 58 | * qDebug() << "Name confirmed:" << name; 59 | * }); 60 | * @endcode 61 | */ 62 | class QMDNSENGINE_EXPORT Prober : public QObject 63 | { 64 | Q_OBJECT 65 | 66 | public: 67 | 68 | /** 69 | * @brief Create a new prober 70 | */ 71 | Prober(AbstractServer *server, const Record &record, QObject *parent = 0); 72 | 73 | Q_SIGNALS: 74 | 75 | /** 76 | * @brief Indicate that the name has been confirmed unique 77 | * @param name that was confirmed to be unique 78 | */ 79 | void nameConfirmed(const QByteArray &name); 80 | 81 | private: 82 | 83 | ProberPrivate *const d; 84 | }; 85 | 86 | } 87 | 88 | #endif // QMDNSENGINE_PROBER_H 89 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/provider.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_PROVIDER_H 26 | #define QMDNSENGINE_PROVIDER_H 27 | 28 | #include 29 | 30 | #include "qmdnsengine_export.h" 31 | 32 | namespace QMdnsEngine 33 | { 34 | 35 | class AbstractServer; 36 | class Hostname; 37 | class Service; 38 | 39 | class QMDNSENGINE_EXPORT ProviderPrivate; 40 | 41 | /** 42 | * @brief %Provider for a single mDNS service 43 | * 44 | * This class provide a [Service](@ref QMdnsEngine::Service) on the local 45 | * network by responding to the appropriate DNS queries. A hostname is 46 | * required for creating the SRV record. 47 | * 48 | * The provider needs to be given a reference to the service through the 49 | * update() method so that it can construct DNS records: 50 | * 51 | * @code 52 | * QMdnsEngine::Service service; 53 | * service.setType("_http._tcp.local."); 54 | * service.setName("My Service"); 55 | * service.setPort(1234); 56 | * 57 | * QMdnsEngine::Provider provider; 58 | * provider.update(service); 59 | * @endcode 60 | * 61 | * This method can also be used to update the provider's records. 62 | */ 63 | class QMDNSENGINE_EXPORT Provider : public QObject 64 | { 65 | Q_OBJECT 66 | 67 | public: 68 | 69 | /** 70 | * @brief Create a new service provider 71 | */ 72 | Provider(AbstractServer *server, Hostname *hostname, QObject *parent = 0); 73 | 74 | /** 75 | * @brief Update the service with the provided information 76 | * @param service updated service description 77 | * 78 | * This class will not respond to any DNS queries until the hostname is 79 | * confirmed and this method is called. 80 | */ 81 | void update(const Service &service); 82 | 83 | private: 84 | 85 | ProviderPrivate *const d; 86 | }; 87 | 88 | } 89 | 90 | #endif // QMDNSENGINE_PROVIDER_H 91 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/query.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_QUERY_H 26 | #define QMDNSENGINE_QUERY_H 27 | 28 | #include 29 | 30 | #include "qmdnsengine_export.h" 31 | 32 | namespace QMdnsEngine 33 | { 34 | 35 | class QMDNSENGINE_EXPORT QueryPrivate; 36 | 37 | /** 38 | * @brief DNS query 39 | * 40 | * This class represents a query for a DNS record. For example, to query for 41 | * the IPv4 address of a local host: 42 | * 43 | * @code 44 | * QMdnsEngine::Query query; 45 | * query.setName("myserver.local."); 46 | * query.setType(QMdnsEngine::A); 47 | * 48 | * message.addQuery(query); 49 | * @endcode 50 | */ 51 | class QMDNSENGINE_EXPORT Query 52 | { 53 | public: 54 | 55 | /** 56 | * @brief Create an empty query 57 | */ 58 | Query(); 59 | 60 | /** 61 | * @brief Create a copy of an existing query 62 | */ 63 | Query(const Query &other); 64 | 65 | /** 66 | * @brief Assignment operator 67 | */ 68 | Query &operator=(const Query &other); 69 | 70 | /** 71 | * @brief Destroy the query 72 | */ 73 | virtual ~Query(); 74 | 75 | /** 76 | * @brief Retrieve the name being queried 77 | */ 78 | QByteArray name() const; 79 | 80 | /** 81 | * @brief Set the name to query 82 | */ 83 | void setName(const QByteArray &name); 84 | 85 | /** 86 | * @brief Retrieve the type of record being queried 87 | */ 88 | quint16 type() const; 89 | 90 | /** 91 | * @brief Set the type of record to query 92 | * 93 | * Constants, such as QMdnsEngine::SRV are provided for convenience. 94 | */ 95 | void setType(quint16 type); 96 | 97 | /** 98 | * @brief Determine if a unicast response is desired 99 | */ 100 | bool unicastResponse() const; 101 | 102 | /** 103 | * @brief Set whether a unicast response is desired 104 | */ 105 | void setUnicastResponse(bool unicastResponse); 106 | 107 | private: 108 | 109 | QueryPrivate *const d; 110 | }; 111 | 112 | QMDNSENGINE_EXPORT QDebug operator<<(QDebug dbg, const Query &query); 113 | 114 | } 115 | 116 | #endif // QMDNSENGINE_QUERY_H 117 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/record.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_RECORD_H 26 | #define QMDNSENGINE_RECORD_H 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | #include "qmdnsengine_export.h" 35 | 36 | namespace QMdnsEngine 37 | { 38 | 39 | class QMDNSENGINE_EXPORT RecordPrivate; 40 | 41 | /** 42 | * @brief DNS record 43 | * 44 | * This class maintains information for an individual record. Not all record 45 | * types use every field. 46 | * 47 | * For example, to create a TXT record: 48 | * 49 | * @code 50 | * QMdnsEngine::Record record; 51 | * record.setName("My Service._http._tcp.local."); 52 | * record.setType(QMdnsEngine::TXT); 53 | * record.addAttribute("a", "value1"); 54 | * record.addAttribute("b", "value2"); 55 | * 56 | * message.addRecord(record); 57 | * @endcode 58 | */ 59 | class QMDNSENGINE_EXPORT Record 60 | { 61 | public: 62 | 63 | /** 64 | * @brief Create an uninitialized record 65 | */ 66 | Record(); 67 | 68 | /** 69 | * @brief Create a copy of an existing record 70 | */ 71 | Record(const Record &other); 72 | 73 | /** 74 | * @brief Assignment operator 75 | */ 76 | Record &operator=(const Record &other); 77 | 78 | /** 79 | * @brief Equality operator 80 | */ 81 | bool operator==(const Record &other) const; 82 | 83 | /** 84 | * @brief Inequality operator 85 | */ 86 | bool operator!=(const Record &other) const; 87 | 88 | /** 89 | * @brief Destroy the record 90 | */ 91 | virtual ~Record(); 92 | 93 | /** 94 | * @brief Retrieve the name of the record 95 | */ 96 | QByteArray name() const; 97 | 98 | /** 99 | * @brief Set the name of the record 100 | */ 101 | void setName(const QByteArray &name); 102 | 103 | /** 104 | * @brief Retrieve the type of the record 105 | */ 106 | quint16 type() const; 107 | 108 | /** 109 | * @brief Set the type of the record 110 | * 111 | * For convenience, constants for types used by mDNS, such as 112 | * QMdnsEngine::A or QMdnsEngine::PTR, may be used here. 113 | */ 114 | void setType(quint16 type); 115 | 116 | /** 117 | * @brief Determine whether to replace or append to existing records 118 | * 119 | * If true, this record replaces all previous records of the same name and 120 | * type rather than appending to them. 121 | */ 122 | bool flushCache() const; 123 | 124 | /** 125 | * @brief Set whether to replace or append to existing records 126 | */ 127 | void setFlushCache(bool flushCache); 128 | 129 | /** 130 | * @brief Retrieve the TTL (time to live) for the record 131 | */ 132 | quint32 ttl() const; 133 | 134 | /** 135 | * @brief Set the TTL (time to live) for the record 136 | */ 137 | void setTtl(quint32 ttl); 138 | 139 | /** 140 | * @brief Retrieve the address for the record 141 | * 142 | * This field is used by QMdnsEngine::A and QMdnsEngine::AAAA records. 143 | */ 144 | QHostAddress address() const; 145 | 146 | /** 147 | * @brief Set the address for the record 148 | */ 149 | void setAddress(const QHostAddress &address); 150 | 151 | /** 152 | * @brief Retrieve the target for the record 153 | * 154 | * This field is used by QMdnsEngine::PTR and QMdnsEngine::SRV records. 155 | */ 156 | QByteArray target() const; 157 | 158 | /** 159 | * @brief Set the target for the record 160 | */ 161 | void setTarget(const QByteArray &target); 162 | 163 | /** 164 | * @brief Retrieve the next domain name 165 | * 166 | * This field is used by QMdnsEngine::NSEC records. 167 | */ 168 | QByteArray nextDomainName() const; 169 | 170 | /** 171 | * @brief Set the next domain name 172 | */ 173 | void setNextDomainName(const QByteArray &nextDomainName); 174 | 175 | /** 176 | * @brief Retrieve the priority for the record 177 | * 178 | * This field is used by QMdnsEngine::SRV records. 179 | */ 180 | quint16 priority() const; 181 | 182 | /** 183 | * @brief Set the priority for the record 184 | * 185 | * Unless more than one QMdnsEngine::SRV record is being sent, this field 186 | * should be set to 0. 187 | */ 188 | void setPriority(quint16 priority); 189 | 190 | /** 191 | * @brief Retrieve the weight of the record 192 | * 193 | * This field is used by QMdnsEngine::SRV records. 194 | */ 195 | quint16 weight() const; 196 | 197 | /** 198 | * @brief Set the weight of the record 199 | * 200 | * Unless more than one QMdnsEngine::SRV record is being sent, this field 201 | * should be set to 0. 202 | */ 203 | void setWeight(quint16 weight); 204 | 205 | /** 206 | * @brief Retrieve the port for the record 207 | * 208 | * This field is used by QMdnsEngine::SRV records. 209 | */ 210 | quint16 port() const; 211 | 212 | /** 213 | * @brief Set the port for the record 214 | */ 215 | void setPort(quint16 port); 216 | 217 | /** 218 | * @brief Retrieve attributes for the record 219 | * 220 | * This field is used by QMdnsEngine::TXT records. 221 | */ 222 | QMap attributes() const; 223 | 224 | /** 225 | * @brief Set attributes for the record 226 | */ 227 | void setAttributes(const QMap &attributes); 228 | 229 | /** 230 | * @brief Add an attribute to the record 231 | */ 232 | void addAttribute(const QByteArray &key, const QByteArray &value); 233 | 234 | /** 235 | * @brief Retrieve the bitmap for the record 236 | * 237 | * This field is used by QMdnsEngine::NSEC records. 238 | */ 239 | Bitmap bitmap() const; 240 | 241 | /** 242 | * @brief Set the bitmap for the record 243 | */ 244 | void setBitmap(const Bitmap &bitmap); 245 | 246 | private: 247 | 248 | RecordPrivate *const d; 249 | }; 250 | 251 | QMDNSENGINE_EXPORT QDebug operator<<(QDebug dbg, const Record &record); 252 | 253 | } 254 | 255 | #endif // QMDNSENGINE_RECORD_H 256 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/resolver.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_RESOLVER_H 26 | #define QMDNSENGINE_RESOLVER_H 27 | 28 | #include 29 | #include 30 | 31 | #include "qmdnsengine_export.h" 32 | 33 | namespace QMdnsEngine 34 | { 35 | 36 | class AbstractServer; 37 | class Cache; 38 | 39 | class QMDNSENGINE_EXPORT ResolverPrivate; 40 | 41 | /** 42 | * @brief %Resolver for services 43 | * 44 | * When [Browser](@ref QMdnsEngine::Browser) indicates that a new service has 45 | * been found, it becomes necessary to resolve the service in order to connect 46 | * to it. This class serves that role. A [Cache](@ref QMdnsEngine::Cache) can 47 | * optionally be provided to speed up the resolving process. 48 | * 49 | * For example, assuming that `record` is a SRV record: 50 | * 51 | * @code 52 | * QMdnsEngine::Resolver resolver(&server, record.target()); 53 | * connect(&resolver, &QMdnsEngine::Resolver::resolved, [](const QHostAddress &address) { 54 | * qDebug() << "Address:" << address; 55 | * }); 56 | * @endcode 57 | */ 58 | class QMDNSENGINE_EXPORT Resolver : public QObject 59 | { 60 | Q_OBJECT 61 | 62 | public: 63 | 64 | /** 65 | * @brief Create a new resolver 66 | */ 67 | Resolver(AbstractServer *server, const QByteArray &name, Cache *cache = 0, QObject *parent = 0); 68 | 69 | Q_SIGNALS: 70 | 71 | /** 72 | * @brief Indicate that the host resolved to an address 73 | * @param address service address 74 | * 75 | * This signal will be emitted once for each resolved address. For 76 | * example, if a host provides both A and AAAA records, this signal will 77 | * be emitted twice. 78 | */ 79 | void resolved(const QHostAddress &address); 80 | 81 | private: 82 | 83 | ResolverPrivate *const d; 84 | }; 85 | 86 | } 87 | 88 | #endif // QMDNSENGINE_RESOLVER_H 89 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_SERVER_H 26 | #define QMDNSENGINE_SERVER_H 27 | 28 | #include 29 | 30 | #include "qmdnsengine_export.h" 31 | 32 | namespace QMdnsEngine 33 | { 34 | 35 | class Message; 36 | 37 | class QMDNSENGINE_EXPORT ServerPrivate; 38 | 39 | /** 40 | * @brief mDNS server 41 | * 42 | * This class provides an implementation of 43 | * [AbstractServer](@ref QMdnsEngine::AbstractServer) that uses all available 44 | * local network adapters to send and receive mDNS messages. 45 | * 46 | * The class takes care of watching for the addition and removal of network 47 | * interfaces, automatically joining multicast groups when new interfaces are 48 | * available. 49 | */ 50 | class QMDNSENGINE_EXPORT Server : public AbstractServer 51 | { 52 | Q_OBJECT 53 | 54 | public: 55 | 56 | /** 57 | * @brief Create a new server 58 | */ 59 | explicit Server(QObject *parent = 0); 60 | 61 | /** 62 | * @brief Implementation of AbstractServer::sendMessage() 63 | */ 64 | virtual void sendMessage(const Message &message); 65 | 66 | /** 67 | * @brief Implementation of AbstractServer::sendMessageToAll() 68 | */ 69 | virtual void sendMessageToAll(const Message &message); 70 | 71 | private: 72 | 73 | ServerPrivate *const d; 74 | }; 75 | 76 | } 77 | 78 | #endif // QMDNSENGINE_SERVER_H 79 | -------------------------------------------------------------------------------- /src/include/qmdnsengine/service.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_SERVICE_H 26 | #define QMDNSENGINE_SERVICE_H 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "qmdnsengine_export.h" 35 | 36 | namespace QMdnsEngine 37 | { 38 | 39 | class QMDNSENGINE_EXPORT ServicePrivate; 40 | 41 | /** 42 | * @brief %Service available on the local network 43 | * 44 | * This class contains the descriptive information necessary to represent an 45 | * individual service made available to the local network. Instances are 46 | * provided by [Browser](@ref QMdnsEngine::Browser) as services are 47 | * discovered. Instances must be created and passed to 48 | * [Provider::update()](@ref QMdnsEngine::Provider::update) to provide a 49 | * service. 50 | */ 51 | class QMDNSENGINE_EXPORT Service 52 | { 53 | public: 54 | 55 | /** 56 | * @brief Create an uninitialized service 57 | */ 58 | Service(); 59 | 60 | /** 61 | * @brief Create a copy of an existing service 62 | */ 63 | Service(const Service &other); 64 | 65 | /** 66 | * @brief Assignment operator 67 | */ 68 | Service &operator=(const Service &other); 69 | 70 | /** 71 | * @brief Equality operator 72 | */ 73 | bool operator==(const Service &other) const; 74 | 75 | /** 76 | * @brief Inequality operator 77 | */ 78 | bool operator!=(const Service &other) const; 79 | 80 | /** 81 | * @brief Destroy the service 82 | */ 83 | virtual ~Service(); 84 | 85 | /** 86 | * @brief Retrieve the service type 87 | */ 88 | QByteArray type() const; 89 | 90 | /** 91 | * @brief Set the service type 92 | * 93 | * For example, an HTTP service might use "_http._tcp". 94 | */ 95 | void setType(const QByteArray &type); 96 | 97 | /** 98 | * @brief Retrieve the service name 99 | */ 100 | QByteArray name() const; 101 | 102 | /** 103 | * @brief Set the service name 104 | * 105 | * This is combined with the service type and domain to form the FQDN for 106 | * the service. 107 | */ 108 | void setName(const QByteArray &name); 109 | 110 | /** 111 | * @brief Retrieve the hostname of the device providing the service 112 | */ 113 | QByteArray hostname() const; 114 | 115 | /** 116 | * @brief Set the hostname of the device providing the service 117 | */ 118 | void setHostname(const QByteArray &hostname); 119 | 120 | /** 121 | * @brief Retrieve the service port 122 | */ 123 | quint16 port() const; 124 | 125 | /** 126 | * @brief Set the service port 127 | */ 128 | void setPort(quint16 port); 129 | 130 | /** 131 | * @brief Retrieve the attributes for the service 132 | * 133 | * Boolean attributes will have null values (invoking QByteArray::isNull() 134 | * on the value will return true). 135 | */ 136 | QMap attributes() const; 137 | 138 | /** 139 | * @brief Set the attributes for the service 140 | */ 141 | void setAttributes(const QMap &attributes); 142 | 143 | /** 144 | * @brief Add an attribute to the service 145 | */ 146 | void addAttribute(const QByteArray &key, const QByteArray &value); 147 | 148 | private: 149 | 150 | ServicePrivate *const d; 151 | }; 152 | 153 | QMDNSENGINE_EXPORT QDebug operator<<(QDebug debug, const Service &service); 154 | 155 | } 156 | 157 | #endif // QMDNSENGINE_SERVICE_H 158 | -------------------------------------------------------------------------------- /src/qmdnsengine_export.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_EXPORT_H 26 | #define QMDNSENGINE_EXPORT_H 27 | 28 | #include 29 | 30 | #cmakedefine BUILD_SHARED_LIBS 31 | 32 | #if defined(BUILD_SHARED_LIBS) 33 | # if defined(QMDNSENGINE_LIBRARY) 34 | # define QMDNSENGINE_EXPORT Q_DECL_EXPORT 35 | # else 36 | # define QMDNSENGINE_EXPORT Q_DECL_IMPORT 37 | # endif 38 | #else 39 | # define QMDNSENGINE_EXPORT 40 | #endif 41 | 42 | #endif // QMDNSENGINE_EXPORT_H 43 | -------------------------------------------------------------------------------- /src/resource.rc.in: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | VS_VERSION_INFO VERSIONINFO 4 | FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,0 5 | PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,0 6 | { 7 | BLOCK "StringFileInfo" 8 | { 9 | BLOCK "040904b0" 10 | { 11 | VALUE "CompanyName", "@PROJECT_AUTHOR@\0" 12 | VALUE "FileDescription", "@PROJECT_DESCRIPTION@\0" 13 | VALUE "FileVersion", "@PROJECT_VERSION@\0" 14 | VALUE "InternalName", "@PROJECT_NAME@\0" 15 | VALUE "LegalCopyright", "Copyright (c) 2018 @PROJECT_AUTHOR@\0" 16 | VALUE "OriginalFilename", "@OUTPUT_NAME@.dll\0" 17 | VALUE "ProductName", "@PROJECT_NAME@\0" 18 | VALUE "ProductVersion", "@PROJECT_VERSION@\0" 19 | } 20 | } 21 | BLOCK "VarFileInfo" 22 | { 23 | VALUE "Translation", 0x409, 1252 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/src/abstractserver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | using namespace QMdnsEngine; 28 | 29 | AbstractServer::AbstractServer(QObject *parent) 30 | : QObject(parent) 31 | { 32 | } 33 | -------------------------------------------------------------------------------- /src/src/bitmap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | #include "bitmap_p.h" 28 | 29 | using namespace QMdnsEngine; 30 | 31 | BitmapPrivate::BitmapPrivate() 32 | : length(0), 33 | data(nullptr) 34 | { 35 | } 36 | 37 | BitmapPrivate::~BitmapPrivate() 38 | { 39 | free(); 40 | } 41 | 42 | void BitmapPrivate::free() 43 | { 44 | if (data) { 45 | delete[] data; 46 | } 47 | } 48 | 49 | void BitmapPrivate::fromData(quint8 newLength, const quint8 *newData) 50 | { 51 | data = new quint8[newLength]; 52 | for (int i = 0; i < newLength; ++i) { 53 | data[i] = newData[i]; 54 | } 55 | length = newLength; 56 | } 57 | 58 | Bitmap::Bitmap() 59 | : d(new BitmapPrivate) 60 | { 61 | } 62 | 63 | Bitmap::Bitmap(const Bitmap &other) 64 | : d(new BitmapPrivate) 65 | { 66 | d->fromData(other.d->length, other.d->data); 67 | } 68 | 69 | Bitmap &Bitmap::operator=(const Bitmap &other) 70 | { 71 | d->free(); 72 | d->fromData(other.d->length, other.d->data); 73 | return *this; 74 | } 75 | 76 | bool Bitmap::operator==(const Bitmap &other) 77 | { 78 | if (d->length != other.d->length) { 79 | return false; 80 | } 81 | for (int i = 0; i < d->length; ++i) { 82 | if (d->data[i] != other.d->data[i]) { 83 | return false; 84 | } 85 | } 86 | return true; 87 | } 88 | 89 | Bitmap::~Bitmap() 90 | { 91 | delete d; 92 | } 93 | 94 | quint8 Bitmap::length() const 95 | { 96 | return d->length; 97 | } 98 | 99 | const quint8 *Bitmap::data() const 100 | { 101 | return d->data; 102 | } 103 | 104 | void Bitmap::setData(quint8 length, const quint8 *data) 105 | { 106 | d->free(); 107 | d->fromData(length, data); 108 | } 109 | -------------------------------------------------------------------------------- /src/src/bitmap_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_BITMAP_P_H 26 | #define QMDNSENGINE_BITMAP_P_H 27 | 28 | #include 29 | 30 | namespace QMdnsEngine 31 | { 32 | 33 | class BitmapPrivate 34 | { 35 | public: 36 | 37 | BitmapPrivate(); 38 | virtual ~BitmapPrivate(); 39 | 40 | void free(); 41 | void fromData(quint8 newLength, const quint8 *newData); 42 | 43 | quint8 length; 44 | quint8 *data; 45 | }; 46 | 47 | } 48 | 49 | #endif // QMDNSENGINE_BITMAP_P_H 50 | -------------------------------------------------------------------------------- /src/src/browser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "browser_p.h" 37 | 38 | using namespace QMdnsEngine; 39 | 40 | BrowserPrivate::BrowserPrivate(Browser *browser, AbstractServer *server, const QByteArray &type, Cache *existingCache) 41 | : QObject(browser), 42 | server(server), 43 | type(type), 44 | cache(existingCache ? existingCache : new Cache(this)), 45 | q(browser) 46 | { 47 | connect(server, &AbstractServer::messageReceived, this, &BrowserPrivate::onMessageReceived); 48 | connect(cache, &Cache::shouldQuery, this, &BrowserPrivate::onShouldQuery); 49 | connect(cache, &Cache::recordExpired, this, &BrowserPrivate::onRecordExpired); 50 | connect(&queryTimer, &QTimer::timeout, this, &BrowserPrivate::onQueryTimeout); 51 | connect(&serviceTimer, &QTimer::timeout, this, &BrowserPrivate::onServiceTimeout); 52 | 53 | queryTimer.setInterval(60 * 1000); 54 | queryTimer.setSingleShot(true); 55 | 56 | serviceTimer.setInterval(100); 57 | serviceTimer.setSingleShot(true); 58 | 59 | // Immediately begin browsing for services 60 | onQueryTimeout(); 61 | } 62 | 63 | // TODO: multiple SRV records not supported 64 | 65 | bool BrowserPrivate::updateService(const QByteArray &fqName) 66 | { 67 | // Split the FQDN into service name and type 68 | int index = fqName.indexOf('.'); 69 | QByteArray serviceName = fqName.left(index); 70 | QByteArray serviceType = fqName.mid(index + 1); 71 | 72 | // Immediately return if a PTR record does not exist 73 | Record ptrRecord; 74 | if (!cache->lookupRecord(serviceType, PTR, ptrRecord)) { 75 | return false; 76 | } 77 | 78 | // If a SRV record is missing, query for it (by returning true) 79 | Record srvRecord; 80 | if (!cache->lookupRecord(fqName, SRV, srvRecord)) { 81 | return true; 82 | } 83 | 84 | Service service; 85 | service.setName(serviceName); 86 | service.setType(serviceType); 87 | service.setHostname(srvRecord.target()); 88 | service.setPort(srvRecord.port()); 89 | 90 | // If TXT records are available for the service, add their values 91 | QList txtRecords; 92 | if (cache->lookupRecords(fqName, TXT, txtRecords)) { 93 | QMap attributes; 94 | #if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) 95 | for (const Record &record : std::as_const(txtRecords)) { 96 | #else 97 | for (const Record &record : qAsConst(txtRecords)) { 98 | #endif 99 | for (auto i = record.attributes().constBegin(); 100 | i != record.attributes().constEnd(); ++i) { 101 | attributes.insert(i.key(), i.value()); 102 | } 103 | } 104 | service.setAttributes(attributes); 105 | } 106 | 107 | // If the service existed, this is an update; otherwise it is a new 108 | // addition; emit the appropriate signal 109 | if (!services.contains(fqName)) { 110 | emit q->serviceAdded(service); 111 | } else if(services.value(fqName) != service) { 112 | emit q->serviceUpdated(service); 113 | } 114 | 115 | services.insert(fqName, service); 116 | hostnames.insert(service.hostname()); 117 | 118 | return false; 119 | } 120 | 121 | void BrowserPrivate::onMessageReceived(const Message &message) 122 | { 123 | if (!message.isResponse()) { 124 | return; 125 | } 126 | 127 | const bool any = type == MdnsBrowseType; 128 | 129 | // Use a set to track all services that are updated in the message to 130 | // prevent unnecessary queries for SRV and TXT records 131 | QSet updateNames; 132 | const auto records = message.records(); 133 | for (const Record &record : records) { 134 | bool cacheRecord = false; 135 | 136 | switch (record.type()) { 137 | case PTR: 138 | if (any && record.name() == MdnsBrowseType) { 139 | ptrTargets.insert(record.target()); 140 | serviceTimer.start(); 141 | cacheRecord = true; 142 | } else if (any || record.name() == type) { 143 | updateNames.insert(record.target()); 144 | cacheRecord = true; 145 | } 146 | break; 147 | case SRV: 148 | case TXT: 149 | if (any || record.name().endsWith("." + type)) { 150 | updateNames.insert(record.name()); 151 | cacheRecord = true; 152 | } 153 | break; 154 | } 155 | if (cacheRecord) { 156 | cache->addRecord(record); 157 | } 158 | } 159 | 160 | // For each of the services marked to be updated, perform the update and 161 | // make a list of all missing SRV records 162 | QSet queryNames; 163 | #if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) 164 | for (const QByteArray &name : std::as_const(updateNames)) { 165 | #else 166 | for (const QByteArray &name : qAsConst(updateNames)) { 167 | #endif 168 | if (updateService(name)) { 169 | queryNames.insert(name); 170 | } 171 | } 172 | 173 | // Cache A / AAAA records after services are processed to ensure hostnames are known 174 | for (const Record &record : records) { 175 | bool cacheRecord = false; 176 | 177 | switch (record.type()) { 178 | case A: 179 | case AAAA: 180 | cacheRecord = hostnames.contains(record.name()); 181 | break; 182 | } 183 | if (cacheRecord) { 184 | cache->addRecord(record); 185 | } 186 | } 187 | 188 | // Build and send a query for all of the SRV and TXT records 189 | if (queryNames.count()) { 190 | Message queryMessage; 191 | #if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) 192 | for (const QByteArray &name : std::as_const(queryNames)) { 193 | #else 194 | for (const QByteArray &name : qAsConst(queryNames)) { 195 | #endif 196 | Query query; 197 | query.setName(name); 198 | query.setType(SRV); 199 | queryMessage.addQuery(query); 200 | query.setType(TXT); 201 | queryMessage.addQuery(query); 202 | } 203 | server->sendMessageToAll(queryMessage); 204 | } 205 | } 206 | 207 | void BrowserPrivate::onShouldQuery(const Record &record) 208 | { 209 | // Assume that all messages in the cache are still in use (by the browser) 210 | // and attempt to renew them immediately 211 | 212 | Query query; 213 | query.setName(record.name()); 214 | query.setType(record.type()); 215 | Message message; 216 | message.addQuery(query); 217 | server->sendMessageToAll(message); 218 | } 219 | 220 | void BrowserPrivate::onRecordExpired(const Record &record) 221 | { 222 | // If the SRV record has expired for a service, then it must be 223 | // removed - TXT records on the other hand, cause an update 224 | 225 | QByteArray serviceName; 226 | switch (record.type()) { 227 | case SRV: 228 | serviceName = record.name(); 229 | break; 230 | case TXT: 231 | updateService(record.name()); 232 | return; 233 | default: 234 | return; 235 | } 236 | Service service = services.value(serviceName); 237 | if (!service.name().isNull()) { 238 | emit q->serviceRemoved(service); 239 | services.remove(serviceName); 240 | updateHostnames(); 241 | } 242 | } 243 | 244 | void BrowserPrivate::onQueryTimeout() 245 | { 246 | Query query; 247 | query.setName(type); 248 | query.setType(PTR); 249 | Message message; 250 | message.addQuery(query); 251 | 252 | // TODO: including too many records could cause problems 253 | 254 | // Include PTR records for the target that are already known 255 | QList records; 256 | if (cache->lookupRecords(query.name(), PTR, records)) { 257 | #if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) 258 | for (const Record &record : std::as_const(records)) { 259 | #else 260 | for (const Record &record : qAsConst(records)) { 261 | #endif 262 | message.addRecord(record); 263 | } 264 | } 265 | 266 | server->sendMessageToAll(message); 267 | queryTimer.start(); 268 | } 269 | 270 | void BrowserPrivate::onServiceTimeout() 271 | { 272 | if (ptrTargets.count()) { 273 | Message message; 274 | #if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) 275 | for (const QByteArray &target : std::as_const(ptrTargets)) { 276 | #else 277 | for (const QByteArray &target : qAsConst(ptrTargets)) { 278 | #endif 279 | // Add a query for PTR records 280 | Query query; 281 | query.setName(target); 282 | query.setType(PTR); 283 | message.addQuery(query); 284 | 285 | // Include PTR records for the target that are already known 286 | QList records; 287 | if (cache->lookupRecords(target, PTR, records)) { 288 | #if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) 289 | for (const Record &record : std::as_const(records)) { 290 | #else 291 | for (const Record &record : qAsConst(records)) { 292 | #endif 293 | message.addRecord(record); 294 | } 295 | } 296 | } 297 | 298 | server->sendMessageToAll(message); 299 | ptrTargets.clear(); 300 | } 301 | } 302 | 303 | void BrowserPrivate::updateHostnames() 304 | { 305 | hostnames.clear(); 306 | 307 | for (const auto& service : services) { 308 | hostnames.insert(service.hostname()); 309 | } 310 | } 311 | 312 | Browser::Browser(AbstractServer *server, const QByteArray &type, Cache *cache, QObject *parent) 313 | : QObject(parent), 314 | d(new BrowserPrivate(this, server, type, cache)) 315 | { 316 | } 317 | -------------------------------------------------------------------------------- /src/src/browser_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_BROWSER_P_H 26 | #define QMDNSENGINE_BROWSER_P_H 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | 36 | namespace QMdnsEngine 37 | { 38 | 39 | class AbstractServer; 40 | class Browser; 41 | class Cache; 42 | class Message; 43 | class Record; 44 | 45 | class BrowserPrivate : public QObject 46 | { 47 | Q_OBJECT 48 | 49 | public: 50 | 51 | explicit BrowserPrivate(Browser *browser, AbstractServer *server, const QByteArray &type, Cache *existingCache); 52 | 53 | bool updateService(const QByteArray &fqName); 54 | 55 | AbstractServer *server; 56 | QByteArray type; 57 | 58 | Cache *cache; 59 | QSet ptrTargets; 60 | QMap services; 61 | QSet hostnames; 62 | 63 | QTimer queryTimer; 64 | QTimer serviceTimer; 65 | 66 | private Q_SLOTS: 67 | 68 | void onMessageReceived(const Message &message); 69 | void onShouldQuery(const Record &record); 70 | void onRecordExpired(const Record &record); 71 | 72 | void onQueryTimeout(); 73 | void onServiceTimeout(); 74 | 75 | private: 76 | void updateHostnames(); 77 | 78 | Browser *const q; 79 | }; 80 | 81 | } 82 | 83 | #endif // QMDNSENGINE_BROWSER_P_H 84 | -------------------------------------------------------------------------------- /src/src/cache.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #if(QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) 27 | #include 28 | #define USE_QRANDOMGENERATOR 29 | #endif 30 | 31 | #include 32 | #include 33 | 34 | #include "cache_p.h" 35 | 36 | using namespace QMdnsEngine; 37 | 38 | CachePrivate::CachePrivate(Cache *cache) 39 | : QObject(cache), 40 | q(cache) 41 | { 42 | connect(&timer, &QTimer::timeout, this, &CachePrivate::onTimeout); 43 | 44 | timer.setSingleShot(true); 45 | } 46 | 47 | void CachePrivate::onTimeout() 48 | { 49 | // Loop through all of the records in the cache, emitting the appropriate 50 | // signal when a trigger has passed, determining when the next trigger 51 | // will occur, and removing records that have expired 52 | QDateTime now = QDateTime::currentDateTime(); 53 | QDateTime newNextTrigger; 54 | 55 | for (auto i = entries.begin(); i != entries.end();) { 56 | 57 | // Loop through the triggers and remove ones that have already 58 | // passed 59 | bool shouldQuery = false; 60 | for (auto j = i->triggers.begin(); j != i->triggers.end();) { 61 | if ((*j) <= now) { 62 | shouldQuery = true; 63 | j = i->triggers.erase(j); 64 | } else { 65 | break; 66 | } 67 | } 68 | 69 | // If triggers remain, determine the next earliest one; if none 70 | // remain, the record has expired and should be removed 71 | if (i->triggers.length()) { 72 | if (newNextTrigger.isNull() || i->triggers.at(0) < newNextTrigger) { 73 | newNextTrigger = i->triggers.at(0); 74 | } 75 | if (shouldQuery) { 76 | emit q->shouldQuery(i->record); 77 | } 78 | ++i; 79 | } else { 80 | emit q->recordExpired(i->record); 81 | i = entries.erase(i); 82 | } 83 | } 84 | 85 | // If newNextTrigger contains a value, it will be the time for the next 86 | // trigger and the timer should be started again 87 | nextTrigger = newNextTrigger; 88 | if (!nextTrigger.isNull()) { 89 | timer.start(now.msecsTo(nextTrigger)); 90 | } 91 | } 92 | 93 | Cache::Cache(QObject *parent) 94 | : QObject(parent), 95 | d(new CachePrivate(this)) 96 | { 97 | } 98 | 99 | void Cache::addRecord(const Record &record) 100 | { 101 | // If a record exists that matches, remove it from the cache; if the TTL 102 | // is nonzero, it will be added back to the cache with updated times 103 | for (auto i = d->entries.begin(); i != d->entries.end();) { 104 | if ((record.flushCache() && 105 | (*i).record.name() == record.name() && 106 | (*i).record.type() == record.type()) || 107 | (*i).record == record) { 108 | 109 | // If the TTL is set to 0, indicate that the record was removed 110 | if (record.ttl() == 0) { 111 | emit recordExpired((*i).record); 112 | } 113 | 114 | i = d->entries.erase(i); 115 | 116 | // No need to continue further if the TTL was set to 0 117 | if (record.ttl() == 0) { 118 | return; 119 | } 120 | } else { 121 | ++i; 122 | } 123 | } 124 | 125 | // Use the current time to calculate the triggers and add a random offset 126 | QDateTime now = QDateTime::currentDateTime(); 127 | #ifdef USE_QRANDOMGENERATOR 128 | qint64 random = QRandomGenerator::global()->bounded(20); 129 | #else 130 | qint64 random = qrand() % 20; 131 | #endif 132 | 133 | QList triggers{ 134 | now.addMSecs(record.ttl() * 500 + random), // 50% 135 | now.addMSecs(record.ttl() * 850 + random), // 85% 136 | now.addMSecs(record.ttl() * 900 + random), // 90% 137 | now.addMSecs(record.ttl() * 950 + random), // 95% 138 | now.addSecs(record.ttl()) 139 | }; 140 | 141 | // Append the record and its triggers 142 | d->entries.append({record, triggers}); 143 | 144 | // Check if the new record's first trigger is earlier than the next 145 | // scheduled trigger; if so, restart the timer 146 | if (d->nextTrigger.isNull() || triggers.at(0) < d->nextTrigger) { 147 | d->nextTrigger = triggers.at(0); 148 | d->timer.start(now.msecsTo(d->nextTrigger)); 149 | } 150 | } 151 | 152 | bool Cache::lookupRecord(const QByteArray &name, quint16 type, Record &record) const 153 | { 154 | QList records; 155 | if (lookupRecords(name, type, records)) { 156 | record = records.at(0); 157 | return true; 158 | } 159 | return false; 160 | } 161 | 162 | bool Cache::lookupRecords(const QByteArray &name, quint16 type, QList &records) const 163 | { 164 | bool recordsAdded = false; 165 | for (const CachePrivate::Entry &entry : d->entries) { 166 | if ((name.isNull() || entry.record.name() == name) && 167 | (type == ANY || entry.record.type() == type)) { 168 | records.append(entry.record); 169 | recordsAdded = true; 170 | } 171 | } 172 | return recordsAdded; 173 | } 174 | -------------------------------------------------------------------------------- /src/src/cache_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_CACHE_P_H 26 | #define QMDNSENGINE_CACHE_P_H 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | namespace QMdnsEngine 36 | { 37 | 38 | class Cache; 39 | 40 | class CachePrivate : public QObject 41 | { 42 | Q_OBJECT 43 | 44 | public: 45 | 46 | struct Entry 47 | { 48 | Record record; 49 | QList triggers; 50 | }; 51 | 52 | CachePrivate(Cache *cache); 53 | 54 | QTimer timer; 55 | QList entries; 56 | QDateTime nextTrigger; 57 | 58 | private Q_SLOTS: 59 | 60 | void onTimeout(); 61 | 62 | private: 63 | 64 | Cache *const q; 65 | }; 66 | 67 | } 68 | 69 | #endif // QMDNSENGINE_CACHE_P_H 70 | -------------------------------------------------------------------------------- /src/src/hostname.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "hostname_p.h" 38 | 39 | using namespace QMdnsEngine; 40 | 41 | HostnamePrivate::HostnamePrivate(Hostname *hostname, AbstractServer *server) 42 | : QObject(hostname), 43 | server(server), 44 | q(hostname) 45 | { 46 | connect(server, &AbstractServer::messageReceived, this, &HostnamePrivate::onMessageReceived); 47 | connect(®istrationTimer, &QTimer::timeout, this, &HostnamePrivate::onRegistrationTimeout); 48 | connect(&rebroadcastTimer, &QTimer::timeout, this, &HostnamePrivate::onRebroadcastTimeout); 49 | 50 | registrationTimer.setInterval(2 * 1000); 51 | registrationTimer.setSingleShot(true); 52 | 53 | rebroadcastTimer.setInterval(30 * 60 * 1000); 54 | rebroadcastTimer.setSingleShot(true); 55 | 56 | // Immediately assert the hostname 57 | onRebroadcastTimeout(); 58 | } 59 | 60 | void HostnamePrivate::assertHostname() 61 | { 62 | // Begin with the local hostname and replace any "." with "-" (I'm looking 63 | // at you, macOS) 64 | QByteArray localHostname = QHostInfo::localHostName().toUtf8(); 65 | localHostname = localHostname.replace('.', '-'); 66 | 67 | // If the suffix > 1, then append a "-2", "-3", etc. to the hostname to 68 | // aid in finding one that is unique and not in use 69 | hostname = (hostnameSuffix == 1 ? localHostname: 70 | localHostname + "-" + QByteArray::number(hostnameSuffix)) + ".local."; 71 | 72 | // Compose a query for A and AAAA records matching the hostname 73 | Query ipv4Query; 74 | ipv4Query.setName(hostname); 75 | ipv4Query.setType(A); 76 | Query ipv6Query; 77 | ipv6Query.setName(hostname); 78 | ipv6Query.setType(AAAA); 79 | Message message; 80 | message.addQuery(ipv4Query); 81 | message.addQuery(ipv6Query); 82 | 83 | server->sendMessageToAll(message); 84 | 85 | // If no reply is received after two seconds, the hostname is available 86 | registrationTimer.start(); 87 | } 88 | 89 | bool HostnamePrivate::generateRecord(const QHostAddress &srcAddress, quint16 type, Record &record) 90 | { 91 | // Attempt to find the interface that corresponds with the provided 92 | // address and determine this device's address from the interface 93 | 94 | const auto interfaces = QNetworkInterface::allInterfaces(); 95 | for (const QNetworkInterface &networkInterface : interfaces) { 96 | const auto entries = networkInterface.addressEntries(); 97 | for (const QNetworkAddressEntry &entry : entries) { 98 | if (srcAddress.isInSubnet(entry.ip(), entry.prefixLength())) { 99 | for (const QNetworkAddressEntry &newEntry : entries) { 100 | QHostAddress address = newEntry.ip(); 101 | if ((address.protocol() == QAbstractSocket::IPv4Protocol && type == A) || 102 | (address.protocol() == QAbstractSocket::IPv6Protocol && type == AAAA)) { 103 | record.setName(hostname); 104 | record.setType(type); 105 | record.setAddress(address); 106 | return true; 107 | } 108 | } 109 | } 110 | } 111 | } 112 | return false; 113 | } 114 | 115 | void HostnamePrivate::onMessageReceived(const Message &message) 116 | { 117 | if (message.isResponse()) { 118 | if (hostnameRegistered) { 119 | return; 120 | } 121 | const auto records = message.records(); 122 | for (const Record &record : records) { 123 | if ((record.type() == A || record.type() == AAAA) && record.name() == hostname) { 124 | ++hostnameSuffix; 125 | assertHostname(); 126 | } 127 | } 128 | } else { 129 | if (!hostnameRegistered) { 130 | return; 131 | } 132 | Message reply; 133 | reply.reply(message); 134 | const auto queries = message.queries(); 135 | for (const Query &query : queries) { 136 | if ((query.type() == A || query.type() == AAAA) && query.name() == hostname) { 137 | Record record; 138 | if (generateRecord(message.address(), query.type(), record)) { 139 | reply.addRecord(record); 140 | } 141 | } 142 | } 143 | if (reply.records().count()) { 144 | server->sendMessage(reply); 145 | } 146 | } 147 | } 148 | 149 | void HostnamePrivate::onRegistrationTimeout() 150 | { 151 | hostnameRegistered = true; 152 | if (hostname != hostnamePrev) { 153 | emit q->hostnameChanged(hostname); 154 | } 155 | 156 | // Re-assert the hostname in half an hour 157 | rebroadcastTimer.start(); 158 | } 159 | 160 | void HostnamePrivate::onRebroadcastTimeout() 161 | { 162 | hostnamePrev = hostname; 163 | hostnameRegistered = false; 164 | hostnameSuffix = 1; 165 | 166 | assertHostname(); 167 | } 168 | 169 | Hostname::Hostname(AbstractServer *server, QObject *parent) 170 | : QObject(parent), 171 | d(new HostnamePrivate(this, server)) 172 | { 173 | } 174 | 175 | bool Hostname::isRegistered() const 176 | { 177 | return d->hostnameRegistered; 178 | } 179 | 180 | QByteArray Hostname::hostname() const 181 | { 182 | return d->hostname; 183 | } 184 | -------------------------------------------------------------------------------- /src/src/hostname_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_HOSTNAME_P_H 26 | #define QMDNSENGINE_HOSTNAME_P_H 27 | 28 | #include 29 | #include 30 | 31 | class QHostAddress; 32 | 33 | namespace QMdnsEngine 34 | { 35 | 36 | class AbstractServer; 37 | class Hostname; 38 | class Message; 39 | class Record; 40 | 41 | class HostnamePrivate : public QObject 42 | { 43 | Q_OBJECT 44 | 45 | public: 46 | 47 | HostnamePrivate(Hostname *hostname, AbstractServer *server); 48 | 49 | void assertHostname(); 50 | bool generateRecord(const QHostAddress &srcAddress, quint16 type, Record &record); 51 | 52 | AbstractServer *server; 53 | 54 | QByteArray hostnamePrev; 55 | QByteArray hostname; 56 | bool hostnameRegistered; 57 | int hostnameSuffix; 58 | 59 | QTimer registrationTimer; 60 | QTimer rebroadcastTimer; 61 | 62 | private Q_SLOTS: 63 | 64 | void onMessageReceived(const Message &message); 65 | void onRegistrationTimeout(); 66 | void onRebroadcastTimeout(); 67 | 68 | private: 69 | 70 | Hostname *const q; 71 | }; 72 | 73 | } 74 | 75 | #endif // QMDNSENGINE_HOSTNAME_P_H 76 | -------------------------------------------------------------------------------- /src/src/mdns.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | namespace QMdnsEngine 28 | { 29 | 30 | const quint16 MdnsPort = 5353; 31 | const QHostAddress MdnsIpv4Address("224.0.0.251"); 32 | const QHostAddress MdnsIpv6Address("ff02::fb"); 33 | const QByteArray MdnsBrowseType("_services._dns-sd._udp.local."); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/src/message.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "message_p.h" 31 | 32 | using namespace QMdnsEngine; 33 | 34 | MessagePrivate::MessagePrivate() 35 | : port(0), 36 | transactionId(0), 37 | isResponse(false), 38 | isTruncated(false) 39 | { 40 | } 41 | 42 | Message::Message() 43 | : d(new MessagePrivate) 44 | { 45 | } 46 | 47 | Message::Message(const Message &other) 48 | : d(new MessagePrivate) 49 | { 50 | *this = other; 51 | } 52 | 53 | Message &Message::operator=(const Message &other) 54 | { 55 | *d = *other.d; 56 | return *this; 57 | } 58 | 59 | Message::~Message() 60 | { 61 | delete d; 62 | } 63 | 64 | QHostAddress Message::address() const 65 | { 66 | return d->address; 67 | } 68 | 69 | void Message::setAddress(const QHostAddress &address) 70 | { 71 | d->address = address; 72 | } 73 | 74 | quint16 Message::port() const 75 | { 76 | return d->port; 77 | } 78 | 79 | void Message::setPort(quint16 port) 80 | { 81 | d->port = port; 82 | } 83 | 84 | quint16 Message::transactionId() const 85 | { 86 | return d->transactionId; 87 | } 88 | 89 | void Message::setTransactionId(quint16 transactionId) 90 | { 91 | d->transactionId = transactionId; 92 | } 93 | 94 | bool Message::isResponse() const 95 | { 96 | return d->isResponse; 97 | } 98 | 99 | void Message::setResponse(bool isResponse) 100 | { 101 | d->isResponse = isResponse; 102 | } 103 | 104 | bool Message::isTruncated() const 105 | { 106 | return d->isTruncated; 107 | } 108 | 109 | void Message::setTruncated(bool isTruncated) 110 | { 111 | d->isTruncated = isTruncated; 112 | } 113 | 114 | QList Message::queries() const 115 | { 116 | return d->queries; 117 | } 118 | 119 | void Message::addQuery(const Query &query) 120 | { 121 | d->queries.append(query); 122 | } 123 | 124 | QList Message::records() const 125 | { 126 | return d->records; 127 | } 128 | 129 | void Message::addRecord(const Record &record) 130 | { 131 | d->records.append(record); 132 | } 133 | 134 | void Message::reply(const Message &other) 135 | { 136 | if (other.port() == MdnsPort) { 137 | if (other.address().protocol() == QAbstractSocket::IPv4Protocol) { 138 | setAddress(MdnsIpv4Address); 139 | } else { 140 | setAddress(MdnsIpv6Address); 141 | } 142 | } else { 143 | setAddress(other.address()); 144 | } 145 | setPort(other.port()); 146 | setTransactionId(other.transactionId()); 147 | setResponse(true); 148 | } 149 | -------------------------------------------------------------------------------- /src/src/message_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_MESSAGE_P_H 26 | #define QMDNSENGINE_MESSAGE_P_H 27 | 28 | #include 29 | #include 30 | 31 | namespace QMdnsEngine 32 | { 33 | 34 | class Query; 35 | class Record; 36 | 37 | class MessagePrivate 38 | { 39 | public: 40 | 41 | MessagePrivate(); 42 | 43 | QHostAddress address; 44 | quint16 port; 45 | quint16 transactionId; 46 | bool isResponse; 47 | bool isTruncated; 48 | QList queries; 49 | QList records; 50 | }; 51 | 52 | } 53 | 54 | #endif // QMDNSENGINE_MESSAGE_P_H 55 | -------------------------------------------------------------------------------- /src/src/prober.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "prober_p.h" 32 | 33 | using namespace QMdnsEngine; 34 | 35 | ProberPrivate::ProberPrivate(Prober *prober, AbstractServer *server, const Record &record) 36 | : QObject(prober), 37 | server(server), 38 | confirmed(false), 39 | proposedRecord(record), 40 | suffix(1), 41 | q(prober) 42 | { 43 | // All records should contain at least one "." 44 | int index = record.name().indexOf('.'); 45 | name = record.name().left(index); 46 | type = record.name().mid(index); 47 | 48 | connect(server, &AbstractServer::messageReceived, this, &ProberPrivate::onMessageReceived); 49 | connect(&timer, &QTimer::timeout, this, &ProberPrivate::onTimeout); 50 | 51 | timer.setSingleShot(true); 52 | 53 | assertRecord(); 54 | } 55 | 56 | void ProberPrivate::assertRecord() 57 | { 58 | // Use the current suffix to set the name of the proposed record 59 | QString tmpName = suffix == 1 60 | ? QString("%1%2").arg(name, type.constData()) 61 | : QString("%1-%2%3").arg(name.constData(), QByteArray::number(suffix), type); 62 | 63 | proposedRecord.setName(tmpName.toUtf8()); 64 | 65 | // Broadcast a query for the proposed name (using an ANY query) and 66 | // include the proposed record in the query 67 | Query query; 68 | query.setName(proposedRecord.name()); 69 | query.setType(ANY); 70 | Message message; 71 | message.addQuery(query); 72 | message.addRecord(proposedRecord); 73 | server->sendMessageToAll(message); 74 | 75 | // Wait two seconds to confirm it is unique 76 | timer.stop(); 77 | timer.start(2 * 1000); 78 | } 79 | 80 | void ProberPrivate::onMessageReceived(const Message &message) 81 | { 82 | // If the response matches the proposed record, increment the suffix and 83 | // try with the new name 84 | 85 | if (confirmed || !message.isResponse()) { 86 | return; 87 | } 88 | const auto records = message.records(); 89 | for (const Record &record : records) { 90 | if (record.name() == proposedRecord.name() && record.type() == proposedRecord.type()) { 91 | ++suffix; 92 | assertRecord(); 93 | } 94 | } 95 | } 96 | 97 | void ProberPrivate::onTimeout() 98 | { 99 | confirmed = true; 100 | emit q->nameConfirmed(proposedRecord.name()); 101 | } 102 | 103 | Prober::Prober(AbstractServer *server, const Record &record, QObject *parent) 104 | : QObject(parent), 105 | d(new ProberPrivate(this, server, record)) 106 | { 107 | } 108 | -------------------------------------------------------------------------------- /src/src/prober_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_PROBER_P_H 26 | #define QMDNSENGINE_PROBER_P_H 27 | 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | namespace QMdnsEngine 34 | { 35 | 36 | class AbstractServer; 37 | class Message; 38 | class Prober; 39 | 40 | class ProberPrivate : public QObject 41 | { 42 | Q_OBJECT 43 | 44 | public: 45 | 46 | ProberPrivate(Prober *prober, AbstractServer *server, const Record &record); 47 | 48 | void assertRecord(); 49 | 50 | AbstractServer *server; 51 | QTimer timer; 52 | 53 | bool confirmed; 54 | 55 | Record proposedRecord; 56 | QByteArray name; 57 | QByteArray type; 58 | int suffix; 59 | 60 | private Q_SLOTS: 61 | 62 | void onMessageReceived(const Message &message); 63 | void onTimeout(); 64 | 65 | private: 66 | 67 | Prober *const q; 68 | }; 69 | 70 | } 71 | 72 | #endif // QMDNSENGINE_PROBER_P_H 73 | -------------------------------------------------------------------------------- /src/src/provider.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "provider_p.h" 35 | 36 | using namespace QMdnsEngine; 37 | 38 | ProviderPrivate::ProviderPrivate(QObject *parent, AbstractServer *server, Hostname *hostname) 39 | : QObject(parent), 40 | server(server), 41 | hostname(hostname), 42 | prober(nullptr), 43 | initialized(false), 44 | confirmed(false) 45 | { 46 | connect(server, &AbstractServer::messageReceived, this, &ProviderPrivate::onMessageReceived); 47 | connect(hostname, &Hostname::hostnameChanged, this, &ProviderPrivate::onHostnameChanged); 48 | 49 | browsePtrProposed.setName(MdnsBrowseType); 50 | browsePtrProposed.setType(PTR); 51 | ptrProposed.setType(PTR); 52 | srvProposed.setType(SRV); 53 | txtProposed.setType(TXT); 54 | } 55 | 56 | ProviderPrivate::~ProviderPrivate() 57 | { 58 | if (confirmed) { 59 | farewell(); 60 | } 61 | } 62 | 63 | void ProviderPrivate::announce() 64 | { 65 | // Broadcast a message with each of the records 66 | 67 | Message message; 68 | message.setResponse(true); 69 | message.addRecord(ptrRecord); 70 | message.addRecord(srvRecord); 71 | message.addRecord(txtRecord); 72 | server->sendMessageToAll(message); 73 | } 74 | 75 | void ProviderPrivate::confirm() 76 | { 77 | // Confirm that the desired name is unique through probing 78 | 79 | if (prober) { 80 | delete prober; 81 | } 82 | prober = new Prober(server, srvProposed, this); 83 | connect(prober, &Prober::nameConfirmed, [this](const QByteArray &name) { 84 | 85 | // If existing records were confirmed, indicate that they are no 86 | // longer valid 87 | if (confirmed) { 88 | farewell(); 89 | } else { 90 | confirmed = true; 91 | } 92 | 93 | // Update the proposed records 94 | ptrProposed.setTarget(name); 95 | srvProposed.setName(name); 96 | txtProposed.setName(name); 97 | 98 | // Publish the proposed records and announce them 99 | publish(); 100 | 101 | delete prober; 102 | prober = nullptr; 103 | }); 104 | } 105 | 106 | void ProviderPrivate::farewell() 107 | { 108 | // Send a message indicating that the existing records are no longer valid 109 | // by setting their TTL to 0 110 | 111 | ptrRecord.setTtl(0); 112 | srvRecord.setTtl(0); 113 | txtRecord.setTtl(0); 114 | announce(); 115 | } 116 | 117 | void ProviderPrivate::publish() 118 | { 119 | // Copy the proposed records over and announce them 120 | 121 | browsePtrRecord = browsePtrProposed; 122 | ptrRecord = ptrProposed; 123 | srvRecord = srvProposed; 124 | txtRecord = txtProposed; 125 | announce(); 126 | } 127 | 128 | void ProviderPrivate::onMessageReceived(const Message &message) 129 | { 130 | if (!confirmed || message.isResponse()) { 131 | return; 132 | } 133 | 134 | bool sendBrowsePtr = false; 135 | bool sendPtr = false; 136 | bool sendSrv = false; 137 | bool sendTxt = false; 138 | 139 | // Determine which records to send based on the queries 140 | const auto queries = message.queries(); 141 | for (const Query &query : queries) { 142 | if (query.type() == PTR && query.name() == MdnsBrowseType) { 143 | sendBrowsePtr = true; 144 | } else if (query.type() == PTR && query.name() == ptrRecord.name()) { 145 | sendPtr = true; 146 | } else if (query.type() == SRV && query.name() == srvRecord.name()) { 147 | sendSrv = true; 148 | } else if (query.type() == TXT && query.name() == txtRecord.name()) { 149 | sendTxt = true; 150 | } 151 | } 152 | 153 | // Remove records to send if they are already known 154 | const auto records = message.records(); 155 | for (const Record &record : records) { 156 | if (record == ptrRecord) { 157 | sendPtr = false; 158 | } else if (record == srvRecord) { 159 | sendSrv = false; 160 | } else if (record == txtRecord) { 161 | sendTxt = false; 162 | } 163 | } 164 | 165 | // Include the SRV and TXT if the PTR is being sent 166 | if (sendPtr) { 167 | sendSrv = sendTxt = true; 168 | } 169 | 170 | // If any records should be sent, compose a message reply 171 | if (sendBrowsePtr || sendPtr || sendSrv || sendTxt) { 172 | Message reply; 173 | reply.reply(message); 174 | if (sendBrowsePtr) { 175 | reply.addRecord(browsePtrRecord); 176 | } 177 | if (sendPtr) { 178 | reply.addRecord(ptrRecord); 179 | } 180 | if (sendSrv) { 181 | reply.addRecord(srvRecord); 182 | } 183 | if (sendTxt) { 184 | reply.addRecord(txtRecord); 185 | } 186 | server->sendMessage(reply); 187 | } 188 | } 189 | 190 | void ProviderPrivate::onHostnameChanged(const QByteArray &newHostname) 191 | { 192 | // Update the proposed SRV record 193 | srvProposed.setTarget(newHostname); 194 | 195 | // If initialized, confirm the record 196 | if (initialized) { 197 | confirm(); 198 | } 199 | } 200 | 201 | Provider::Provider(AbstractServer *server, Hostname *hostname, QObject *parent) 202 | : QObject(parent), 203 | d(new ProviderPrivate(this, server, hostname)) 204 | { 205 | } 206 | 207 | void Provider::update(const Service &service) 208 | { 209 | d->initialized = true; 210 | 211 | // Clean the service name 212 | QByteArray serviceName = service.name(); 213 | serviceName = serviceName.replace('.', '-'); 214 | 215 | // Update the proposed records 216 | QByteArray fqName = serviceName + "." + service.type(); 217 | d->browsePtrProposed.setTarget(service.type()); 218 | d->ptrProposed.setName(service.type()); 219 | d->ptrProposed.setTarget(fqName); 220 | d->srvProposed.setName(fqName); 221 | d->srvProposed.setPort(service.port()); 222 | d->srvProposed.setTarget(d->hostname->hostname()); 223 | d->txtProposed.setName(fqName); 224 | d->txtProposed.setAttributes(service.attributes()); 225 | 226 | // Assuming a valid hostname exists, check to see if the new service uses 227 | // a different name - if so, it must first be confirmed 228 | if (d->hostname->isRegistered()) { 229 | if (!d->confirmed || fqName != d->srvRecord.name()) { 230 | d->confirm(); 231 | } else { 232 | d->publish(); 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/src/provider_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_PROVIDER_P_H 26 | #define QMDNSENGINE_PROVIDER_P_H 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | namespace QMdnsEngine 34 | { 35 | 36 | class AbstractServer; 37 | class Hostname; 38 | class Message; 39 | class Prober; 40 | 41 | class ProviderPrivate : public QObject 42 | { 43 | Q_OBJECT 44 | 45 | public: 46 | 47 | ProviderPrivate(QObject *parent, AbstractServer *server, Hostname *hostname); 48 | virtual ~ProviderPrivate(); 49 | 50 | void announce(); 51 | void confirm(); 52 | void farewell(); 53 | void publish(); 54 | 55 | AbstractServer *server; 56 | Hostname *hostname; 57 | Prober *prober; 58 | 59 | Service service; 60 | bool initialized; 61 | bool confirmed; 62 | 63 | Record browsePtrRecord; 64 | Record ptrRecord; 65 | Record srvRecord; 66 | Record txtRecord; 67 | 68 | Record browsePtrProposed; 69 | Record ptrProposed; 70 | Record srvProposed; 71 | Record txtProposed; 72 | 73 | private Q_SLOTS: 74 | 75 | void onMessageReceived(const Message &message); 76 | void onHostnameChanged(const QByteArray &hostname); 77 | }; 78 | 79 | } 80 | 81 | #endif // QMDNSENGINE_PROVIDER_P_H 82 | -------------------------------------------------------------------------------- /src/src/query.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #include "query_p.h" 31 | 32 | using namespace QMdnsEngine; 33 | 34 | QueryPrivate::QueryPrivate() 35 | : type(0), 36 | unicastResponse(false) 37 | { 38 | } 39 | 40 | Query::Query() 41 | : d(new QueryPrivate) 42 | { 43 | } 44 | 45 | Query::Query(const Query &other) 46 | : d(new QueryPrivate) 47 | { 48 | *this = other; 49 | } 50 | 51 | Query &Query::operator=(const Query &other) 52 | { 53 | *d = *other.d; 54 | return *this; 55 | } 56 | 57 | Query::~Query() 58 | { 59 | delete d; 60 | } 61 | 62 | QByteArray Query::name() const 63 | { 64 | return d->name; 65 | } 66 | 67 | void Query::setName(const QByteArray &name) 68 | { 69 | d->name = name; 70 | } 71 | 72 | quint16 Query::type() const 73 | { 74 | return d->type; 75 | } 76 | 77 | void Query::setType(quint16 type) 78 | { 79 | d->type = type; 80 | } 81 | 82 | bool Query::unicastResponse() const 83 | { 84 | return d->unicastResponse; 85 | } 86 | 87 | void Query::setUnicastResponse(bool unicastResponse) 88 | { 89 | d->unicastResponse = unicastResponse; 90 | } 91 | 92 | QDebug QMdnsEngine::operator<<(QDebug dbg, const Query &query) 93 | { 94 | QDebugStateSaver saver(dbg); 95 | Q_UNUSED(saver); 96 | 97 | dbg.noquote().nospace() << "Query(" << typeName(query.type()) << " " << query.name() << ")"; 98 | 99 | return dbg; 100 | } 101 | -------------------------------------------------------------------------------- /src/src/query_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_QUERY_P_H 26 | #define QMDNSENGINE_QUERY_P_H 27 | 28 | #include 29 | 30 | namespace QMdnsEngine 31 | { 32 | 33 | class QueryPrivate 34 | { 35 | public: 36 | 37 | QueryPrivate(); 38 | 39 | QByteArray name; 40 | quint16 type; 41 | bool unicastResponse; 42 | }; 43 | 44 | } 45 | 46 | #endif // QMDNSENGINE_QUERY_P_H 47 | -------------------------------------------------------------------------------- /src/src/record.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #include "record_p.h" 31 | 32 | using namespace QMdnsEngine; 33 | 34 | RecordPrivate::RecordPrivate() 35 | : type(0), 36 | flushCache(false), 37 | ttl(3600), 38 | priority(0), 39 | weight(0), 40 | port(0) 41 | { 42 | } 43 | 44 | Record::Record() 45 | : d(new RecordPrivate) 46 | { 47 | } 48 | 49 | Record::Record(const Record &other) 50 | : d(new RecordPrivate) 51 | { 52 | *this = other; 53 | } 54 | 55 | Record &Record::operator=(const Record &other) 56 | { 57 | *d = *other.d; 58 | return *this; 59 | } 60 | 61 | bool Record::operator==(const Record &other) const 62 | { 63 | return d->name == other.d->name && 64 | d->type == other.d->type && 65 | d->address == other.d->address && 66 | d->target == other.d->target && 67 | d->nextDomainName == other.d->nextDomainName && 68 | d->priority == other.d->priority && 69 | d->weight == other.d->weight && 70 | d->port == other.d->port && 71 | d->attributes == other.d->attributes && 72 | d->bitmap == other.d->bitmap; 73 | } 74 | 75 | bool Record::operator!=(const Record &other) const 76 | { 77 | return !(*this == other); 78 | } 79 | 80 | Record::~Record() 81 | { 82 | delete d; 83 | } 84 | 85 | QByteArray Record::name() const 86 | { 87 | return d->name; 88 | } 89 | 90 | void Record::setName(const QByteArray &name) 91 | { 92 | d->name = name; 93 | } 94 | 95 | quint16 Record::type() const 96 | { 97 | return d->type; 98 | } 99 | 100 | void Record::setType(quint16 type) 101 | { 102 | d->type = type; 103 | } 104 | 105 | bool Record::flushCache() const 106 | { 107 | return d->flushCache; 108 | } 109 | 110 | void Record::setFlushCache(bool flushCache) 111 | { 112 | d->flushCache = flushCache; 113 | } 114 | 115 | quint32 Record::ttl() const 116 | { 117 | return d->ttl; 118 | } 119 | 120 | void Record::setTtl(quint32 ttl) 121 | { 122 | d->ttl = ttl; 123 | } 124 | 125 | QHostAddress Record::address() const 126 | { 127 | return d->address; 128 | } 129 | 130 | void Record::setAddress(const QHostAddress &address) 131 | { 132 | d->address = address; 133 | } 134 | 135 | QByteArray Record::target() const 136 | { 137 | return d->target; 138 | } 139 | 140 | void Record::setTarget(const QByteArray &target) 141 | { 142 | d->target = target; 143 | } 144 | 145 | QByteArray Record::nextDomainName() const 146 | { 147 | return d->nextDomainName; 148 | } 149 | 150 | void Record::setNextDomainName(const QByteArray &nextDomainName) 151 | { 152 | d->nextDomainName = nextDomainName; 153 | } 154 | 155 | quint16 Record::priority() const 156 | { 157 | return d->priority; 158 | } 159 | 160 | void Record::setPriority(quint16 priority) 161 | { 162 | d->priority = priority; 163 | } 164 | 165 | quint16 Record::weight() const 166 | { 167 | return d->weight; 168 | } 169 | 170 | void Record::setWeight(quint16 weight) 171 | { 172 | d->weight = weight; 173 | } 174 | 175 | quint16 Record::port() const 176 | { 177 | return d->port; 178 | } 179 | 180 | void Record::setPort(quint16 port) 181 | { 182 | d->port = port; 183 | } 184 | 185 | QMap Record::attributes() const 186 | { 187 | return d->attributes; 188 | } 189 | 190 | void Record::setAttributes(const QMap &attributes) 191 | { 192 | d->attributes = attributes; 193 | } 194 | 195 | void Record::addAttribute(const QByteArray &key, const QByteArray &value) 196 | { 197 | d->attributes.insert(key, value); 198 | } 199 | 200 | Bitmap Record::bitmap() const 201 | { 202 | return d->bitmap; 203 | } 204 | 205 | void Record::setBitmap(const Bitmap &bitmap) 206 | { 207 | d->bitmap = bitmap; 208 | } 209 | 210 | QDebug QMdnsEngine::operator<<(QDebug dbg, const Record &record) 211 | { 212 | QDebugStateSaver saver(dbg); 213 | Q_UNUSED(saver); 214 | 215 | dbg.noquote().nospace() << "Record(" << typeName(record.type()) << " " << record.name() << ")"; 216 | 217 | return dbg; 218 | } 219 | -------------------------------------------------------------------------------- /src/src/record_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_RECORD_P_H 26 | #define QMDNSENGINE_RECORD_P_H 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | namespace QMdnsEngine { 35 | 36 | class RecordPrivate 37 | { 38 | public: 39 | 40 | RecordPrivate(); 41 | 42 | QByteArray name; 43 | quint16 type; 44 | bool flushCache; 45 | quint32 ttl; 46 | 47 | QHostAddress address; 48 | QByteArray target; 49 | QByteArray nextDomainName; 50 | quint16 priority; 51 | quint16 weight; 52 | quint16 port; 53 | QMap attributes; 54 | Bitmap bitmap; 55 | }; 56 | 57 | } 58 | 59 | #endif // QMDNSENGINE_RECORD_P_H 60 | -------------------------------------------------------------------------------- /src/src/resolver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "resolver_p.h" 36 | 37 | using namespace QMdnsEngine; 38 | 39 | ResolverPrivate::ResolverPrivate(Resolver *resolver, AbstractServer *server, const QByteArray &name, Cache *cache) 40 | : QObject(resolver), 41 | server(server), 42 | name(name), 43 | cache(cache ? cache : new Cache(this)), 44 | q(resolver) 45 | { 46 | connect(server, &AbstractServer::messageReceived, this, &ResolverPrivate::onMessageReceived); 47 | connect(&timer, &QTimer::timeout, this, &ResolverPrivate::onTimeout); 48 | 49 | // Query for new records 50 | query(); 51 | 52 | // Pull the existing records from the cache 53 | timer.setSingleShot(true); 54 | timer.start(0); 55 | } 56 | 57 | QList ResolverPrivate::existing() const 58 | { 59 | QList records; 60 | cache->lookupRecords(name, A, records); 61 | cache->lookupRecords(name, AAAA, records); 62 | return records; 63 | } 64 | 65 | void ResolverPrivate::query() const 66 | { 67 | Message message; 68 | 69 | // Add a query for A and AAAA records 70 | Query query; 71 | query.setName(name); 72 | query.setType(A); 73 | message.addQuery(query); 74 | query.setType(AAAA); 75 | message.addQuery(query); 76 | 77 | // Add existing (known) records to the query 78 | const auto records = existing(); 79 | for (const Record &record : records) { 80 | message.addRecord(record); 81 | } 82 | 83 | // Send the query 84 | server->sendMessageToAll(message); 85 | } 86 | 87 | void ResolverPrivate::onMessageReceived(const Message &message) 88 | { 89 | if (!message.isResponse()) { 90 | return; 91 | } 92 | const auto records = message.records(); 93 | for (const Record &record : records) { 94 | if (record.name() == name && (record.type() == A || record.type() == AAAA)) { 95 | cache->addRecord(record); 96 | if (!addresses.contains(record.address())) { 97 | emit q->resolved(record.address()); 98 | addresses.insert(record.address()); 99 | } 100 | } 101 | } 102 | } 103 | 104 | void ResolverPrivate::onTimeout() 105 | { 106 | const auto records = existing(); 107 | for (const Record &record : records) { 108 | emit q->resolved(record.address()); 109 | } 110 | } 111 | 112 | Resolver::Resolver(AbstractServer *server, const QByteArray &name, Cache *cache, QObject *parent) 113 | : QObject(parent), 114 | d(new ResolverPrivate(this, server, name, cache)) 115 | { 116 | } 117 | -------------------------------------------------------------------------------- /src/src/resolver_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_RESOLVER_P_H 26 | #define QMDNSENGINE_RESOLVER_P_H 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace QMdnsEngine 34 | { 35 | 36 | class AbstractServer; 37 | class Cache; 38 | class Message; 39 | class Record; 40 | class Resolver; 41 | 42 | class ResolverPrivate : public QObject 43 | { 44 | Q_OBJECT 45 | 46 | public: 47 | 48 | explicit ResolverPrivate(Resolver *resolver, AbstractServer *server, const QByteArray &name, Cache *cache); 49 | 50 | QList existing() const; 51 | void query() const; 52 | 53 | AbstractServer *server; 54 | QByteArray name; 55 | Cache *cache; 56 | QSet addresses; 57 | QTimer timer; 58 | 59 | private Q_SLOTS: 60 | 61 | void onMessageReceived(const Message &message); 62 | void onTimeout(); 63 | 64 | private: 65 | 66 | Resolver *const q; 67 | }; 68 | 69 | } 70 | 71 | #endif // QMDNSENGINE_RESOLVER_P_H 72 | -------------------------------------------------------------------------------- /src/src/server.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | #ifdef Q_OS_UNIX 28 | # include 29 | # include 30 | # include 31 | #endif 32 | 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "server_p.h" 42 | 43 | using namespace QMdnsEngine; 44 | 45 | ServerPrivate::ServerPrivate(Server *server) 46 | : QObject(server), 47 | q(server) 48 | { 49 | connect(&timer, &QTimer::timeout, this, &ServerPrivate::onTimeout); 50 | connect(&ipv4Socket, &QUdpSocket::readyRead, this, &ServerPrivate::onReadyRead); 51 | connect(&ipv6Socket, &QUdpSocket::readyRead, this, &ServerPrivate::onReadyRead); 52 | 53 | timer.setInterval(60 * 1000); 54 | timer.setSingleShot(true); 55 | onTimeout(); 56 | } 57 | 58 | bool ServerPrivate::bindSocket(QUdpSocket &socket, const QHostAddress &address) 59 | { 60 | // Exit early if the socket is already bound 61 | if (socket.state() == QAbstractSocket::BoundState) { 62 | return true; 63 | } 64 | 65 | // I cannot find the correct combination of flags that allows the socket 66 | // to bind properly on Linux, so on that platform, we must manually create 67 | // the socket and initialize the QUdpSocket with it 68 | 69 | #ifdef Q_OS_UNIX 70 | if (!socket.bind(address, MdnsPort, QAbstractSocket::ShareAddress)) { 71 | int arg = 1; 72 | if (setsockopt(socket.socketDescriptor(), SOL_SOCKET, SO_REUSEADDR, 73 | reinterpret_cast(&arg), sizeof(int))) { 74 | emit q->error(strerror(errno)); 75 | return false; 76 | } 77 | #endif 78 | if (!socket.bind(address, MdnsPort, QAbstractSocket::ReuseAddressHint)) { 79 | emit q->error(socket.errorString()); 80 | return false; 81 | } 82 | #ifdef Q_OS_UNIX 83 | } 84 | #endif 85 | 86 | return true; 87 | } 88 | 89 | void ServerPrivate::onTimeout() 90 | { 91 | // A timer is used to run a set of operations once per minute; first, the 92 | // two sockets are bound - if this fails, another attempt is made once per 93 | // timeout; secondly, all network interfaces are enumerated; if the 94 | // interface supports multicast, the socket will join the mDNS multicast 95 | // groups 96 | 97 | bool ipv4Bound = bindSocket(ipv4Socket, QHostAddress::AnyIPv4); 98 | bool ipv6Bound = bindSocket(ipv6Socket, QHostAddress::AnyIPv6); 99 | 100 | if (ipv4Bound || ipv6Bound) { 101 | const auto interfaces = QNetworkInterface::allInterfaces(); 102 | for (const QNetworkInterface &networkInterface : interfaces) { 103 | if (networkInterface.flags() & QNetworkInterface::CanMulticast) { 104 | if (ipv4Bound) { 105 | ipv4Socket.joinMulticastGroup(MdnsIpv4Address, networkInterface); 106 | } 107 | if (ipv6Bound) { 108 | ipv6Socket.joinMulticastGroup(MdnsIpv6Address, networkInterface); 109 | } 110 | } 111 | } 112 | } 113 | 114 | timer.start(); 115 | } 116 | 117 | void ServerPrivate::onReadyRead() 118 | { 119 | // Read the packet from the socket 120 | QUdpSocket *socket = qobject_cast(sender()); 121 | QByteArray packet; 122 | packet.resize(socket->pendingDatagramSize()); 123 | QHostAddress address; 124 | quint16 port; 125 | socket->readDatagram(packet.data(), packet.size(), &address, &port); 126 | 127 | // Attempt to decode the packet 128 | Message message; 129 | if (fromPacket(packet, message)) { 130 | message.setAddress(address); 131 | message.setPort(port); 132 | emit q->messageReceived(message); 133 | } 134 | } 135 | 136 | Server::Server(QObject *parent) 137 | : AbstractServer(parent), 138 | d(new ServerPrivate(this)) 139 | { 140 | } 141 | 142 | void Server::sendMessage(const Message &message) 143 | { 144 | QByteArray packet; 145 | toPacket(message, packet); 146 | if (message.address().protocol() == QAbstractSocket::IPv4Protocol) { 147 | d->ipv4Socket.writeDatagram(packet, message.address(), message.port()); 148 | } else { 149 | d->ipv6Socket.writeDatagram(packet, message.address(), message.port()); 150 | } 151 | } 152 | 153 | void Server::sendMessageToAll(const Message &message) 154 | { 155 | QByteArray packet; 156 | toPacket(message, packet); 157 | d->ipv4Socket.writeDatagram(packet, MdnsIpv4Address, MdnsPort); 158 | d->ipv6Socket.writeDatagram(packet, MdnsIpv6Address, MdnsPort); 159 | } 160 | -------------------------------------------------------------------------------- /src/src/server_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_SERVER_P_H 26 | #define QMDNSENGINE_SERVER_P_H 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | class QHostAddress; 33 | 34 | namespace QMdnsEngine 35 | { 36 | 37 | class Server; 38 | 39 | class ServerPrivate : public QObject 40 | { 41 | Q_OBJECT 42 | 43 | public: 44 | 45 | explicit ServerPrivate(Server *server); 46 | 47 | bool bindSocket(QUdpSocket &socket, const QHostAddress &address); 48 | 49 | QTimer timer; 50 | QUdpSocket ipv4Socket; 51 | QUdpSocket ipv6Socket; 52 | 53 | private Q_SLOTS: 54 | 55 | void onTimeout(); 56 | void onReadyRead(); 57 | 58 | private: 59 | 60 | Server *const q; 61 | }; 62 | 63 | } 64 | 65 | #endif // QMDNSENGINE_SERVER_P_H 66 | -------------------------------------------------------------------------------- /src/src/service.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | #include "service_p.h" 28 | 29 | using namespace QMdnsEngine; 30 | 31 | ServicePrivate::ServicePrivate() 32 | { 33 | } 34 | 35 | Service::Service() 36 | : d(new ServicePrivate) 37 | { 38 | } 39 | 40 | Service::Service(const Service &other) 41 | : d(new ServicePrivate) 42 | { 43 | *this = other; 44 | } 45 | 46 | Service &Service::operator=(const Service &other) 47 | { 48 | *d = *other.d; 49 | return *this; 50 | } 51 | 52 | bool Service::operator==(const Service &other) const 53 | { 54 | return d->type == other.d->type && 55 | d->name == other.d->name && 56 | d->port == other.d->port && 57 | d->attributes == other.d->attributes; 58 | } 59 | 60 | bool Service::operator!=(const Service &other) const 61 | { 62 | return !(*this == other); 63 | } 64 | 65 | Service::~Service() 66 | { 67 | delete d; 68 | } 69 | 70 | QByteArray Service::type() const 71 | { 72 | return d->type; 73 | } 74 | 75 | void Service::setType(const QByteArray &type) 76 | { 77 | d->type = type; 78 | } 79 | 80 | QByteArray Service::name() const 81 | { 82 | return d->name; 83 | } 84 | 85 | void Service::setName(const QByteArray &name) 86 | { 87 | d->name = name; 88 | } 89 | 90 | QByteArray Service::hostname() const 91 | { 92 | return d->hostname; 93 | } 94 | 95 | void Service::setHostname(const QByteArray &hostname) 96 | { 97 | d->hostname = hostname; 98 | } 99 | 100 | quint16 Service::port() const 101 | { 102 | return d->port; 103 | } 104 | 105 | void Service::setPort(quint16 port) 106 | { 107 | d->port = port; 108 | } 109 | 110 | QMap Service::attributes() const 111 | { 112 | return d->attributes; 113 | } 114 | 115 | void Service::setAttributes(const QMap &attributes) 116 | { 117 | d->attributes = attributes; 118 | } 119 | 120 | void Service::addAttribute(const QByteArray &key, const QByteArray &value) 121 | { 122 | d->attributes.insert(key, value); 123 | } 124 | 125 | QDebug QMdnsEngine::operator<<(QDebug debug, const Service &service) 126 | { 127 | QDebugStateSaver saver(debug); 128 | Q_UNUSED(saver); 129 | 130 | debug.noquote().nospace() 131 | << "Service(name: " << service.name() 132 | << ", type: " << service.type() 133 | << ", hostname: " << service.hostname() 134 | << ", port: " << service.port() 135 | << ", attributes: " << service.attributes() 136 | << ")"; 137 | 138 | return debug; 139 | } 140 | -------------------------------------------------------------------------------- /src/src/service_p.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef QMDNSENGINE_SERVICE_P_H 26 | #define QMDNSENGINE_SERVICE_P_H 27 | 28 | #include 29 | #include 30 | 31 | namespace QMdnsEngine 32 | { 33 | 34 | class ServicePrivate 35 | { 36 | public: 37 | 38 | ServicePrivate(); 39 | 40 | QByteArray type; 41 | QByteArray name; 42 | QByteArray hostname; 43 | quint16 port; 44 | QMap attributes; 45 | }; 46 | 47 | } 48 | 49 | #endif // QMDNSENGINE_SERVICE_P_H 50 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(common) 2 | 3 | set(TESTS 4 | TestBrowser 5 | TestCache 6 | TestDns 7 | TestHostname 8 | TestProber 9 | TestProvider 10 | TestResolver 11 | ) 12 | 13 | foreach(_test ${TESTS}) 14 | add_executable(${_test} ${_test}.cpp) 15 | set_target_properties(${_test} PROPERTIES 16 | CXX_STANDARD 11 17 | CXX_STANDARD_REQUIRED ON 18 | ) 19 | target_include_directories(${_test} PUBLIC "${CMAKE_CURRENT_BINARY_DIR}") 20 | target_link_libraries(${_test} qmdnsengine Qt${QT_VERSION_MAJOR}::Test common) 21 | add_test(NAME ${_test} 22 | COMMAND ${_test} 23 | ) 24 | endforeach() 25 | 26 | # On Windows, the tests will not run without the DLL located in the current 27 | # directory - a target must be used to copy it here once built 28 | if(WIN32) 29 | add_custom_target(qmdnsengine-copy ALL 30 | "${CMAKE_COMMAND}" -E copy_if_different \"$\" \"${CMAKE_CURRENT_BINARY_DIR}\" 31 | DEPENDS qmdnsengine 32 | ) 33 | endif() 34 | -------------------------------------------------------------------------------- /tests/TestBrowser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "common/testserver.h" 37 | #include "common/util.h" 38 | 39 | Q_DECLARE_METATYPE(QMdnsEngine::Service) 40 | 41 | const QByteArray Name = "Test"; 42 | const QByteArray Type = "_test._tcp.local."; 43 | const QByteArray Fqdn = Name + "." + Type; 44 | const QByteArray Target = "Test.local."; 45 | const quint16 Port = 1234; 46 | const QByteArray Key = "key"; 47 | const QByteArray Value = "value"; 48 | 49 | class TestBrowser : public QObject 50 | { 51 | Q_OBJECT 52 | 53 | private Q_SLOTS: 54 | 55 | void initTestCase(); 56 | void testBrowser(); 57 | void testBrowsePtr(); 58 | }; 59 | 60 | void TestBrowser::initTestCase() 61 | { 62 | qRegisterMetaType("Service"); 63 | } 64 | 65 | void TestBrowser::testBrowser() 66 | { 67 | TestServer server; 68 | QMdnsEngine::Browser browser(&server, Type); 69 | 70 | QSignalSpy serviceAddedSpy(&browser, SIGNAL(serviceAdded(Service))); 71 | QSignalSpy serviceUpdatedSpy(&browser, SIGNAL(serviceUpdated(Service))); 72 | QSignalSpy serviceRemovedSpy(&browser, SIGNAL(serviceRemoved(Service))); 73 | 74 | // Wait for the PTR query 75 | QTRY_VERIFY(queryReceived(&server, Type, QMdnsEngine::PTR)); 76 | server.clearReceivedMessages(); 77 | 78 | // Transmit the PTR record 79 | { 80 | QMdnsEngine::Record record; 81 | record.setName(Type); 82 | record.setType(QMdnsEngine::PTR); 83 | record.setTarget(Fqdn); 84 | QMdnsEngine::Message message; 85 | message.setResponse(true); 86 | message.addRecord(record); 87 | server.deliverMessage(message); 88 | } 89 | 90 | // Wait for a SRV query 91 | QTRY_VERIFY(queryReceived(&server, Fqdn, QMdnsEngine::SRV)); 92 | server.clearReceivedMessages(); 93 | 94 | // Nothing should have been added yet 95 | QCOMPARE(serviceAddedSpy.count(), 0); 96 | 97 | // Transmit the SRV record 98 | { 99 | QMdnsEngine::Record record; 100 | record.setName(Fqdn); 101 | record.setType(QMdnsEngine::SRV); 102 | record.setTarget(Target); 103 | record.setPort(Port); 104 | QMdnsEngine::Message message; 105 | message.setResponse(true); 106 | message.addRecord(record); 107 | server.deliverMessage(message); 108 | } 109 | 110 | // The serviceAdded signal should have been emitted 111 | QCOMPARE(serviceAddedSpy.count(), 1); 112 | 113 | // Transmit a TXT record 114 | { 115 | QMdnsEngine::Record record; 116 | record.setName(Fqdn); 117 | record.setType(QMdnsEngine::TXT); 118 | record.setAttributes({{Key, Value}}); 119 | QMdnsEngine::Message message; 120 | message.setResponse(true); 121 | message.addRecord(record); 122 | server.deliverMessage(message); 123 | } 124 | 125 | // The serviceAdded signal should NOT have been emitted and the 126 | // serviceUpdated signal should have been 127 | QCOMPARE(serviceAddedSpy.count(), 1); 128 | QCOMPARE(serviceUpdatedSpy.count(), 1); 129 | 130 | // Remove the SRV record 131 | { 132 | QMdnsEngine::Record record; 133 | record.setName(Fqdn); 134 | record.setType(QMdnsEngine::SRV); 135 | record.setTarget(Target); 136 | record.setPort(Port); 137 | record.setTtl(0); 138 | QMdnsEngine::Message message; 139 | message.setResponse(true); 140 | message.addRecord(record); 141 | server.deliverMessage(message); 142 | } 143 | 144 | // The serviceRemoved signal should have been emitted 145 | QCOMPARE(serviceRemovedSpy.count(), 1); 146 | } 147 | 148 | void TestBrowser::testBrowsePtr() 149 | { 150 | TestServer server; 151 | QMdnsEngine::Browser browser(&server, QMdnsEngine::MdnsBrowseType); 152 | Q_UNUSED(browser); 153 | 154 | // Wait for a query for service types 155 | QTRY_VERIFY(queryReceived(&server, QMdnsEngine::MdnsBrowseType, QMdnsEngine::PTR)); 156 | 157 | // Send a PTR and SRV record 158 | QMdnsEngine::Record record; 159 | record.setName(QMdnsEngine::MdnsBrowseType); 160 | record.setType(QMdnsEngine::PTR); 161 | record.setTarget(Type); 162 | QMdnsEngine::Message message; 163 | message.setResponse(true); 164 | message.addRecord(record); 165 | server.deliverMessage(message); 166 | 167 | // Wait for the query for records of the specified type 168 | QTRY_VERIFY(queryReceived(&server, Type, QMdnsEngine::PTR)); 169 | } 170 | 171 | QTEST_MAIN(TestBrowser) 172 | #include "TestBrowser.moc" 173 | -------------------------------------------------------------------------------- /tests/TestCache.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | Q_DECLARE_METATYPE(QMdnsEngine::Record) 34 | 35 | const QByteArray Name = "Test"; 36 | const quint16 Type = QMdnsEngine::TXT; 37 | 38 | class TestCache : public QObject 39 | { 40 | Q_OBJECT 41 | 42 | public: 43 | 44 | TestCache() : mCounter(0) {} 45 | 46 | private Q_SLOTS: 47 | 48 | void initTestCase(); 49 | void testExpiry(); 50 | void testRemoval(); 51 | void testCacheFlush(); 52 | 53 | private: 54 | 55 | QMdnsEngine::Record createRecord(); 56 | 57 | int mCounter; 58 | }; 59 | 60 | void TestCache::initTestCase() 61 | { 62 | qRegisterMetaType("Record"); 63 | } 64 | 65 | void TestCache::testExpiry() 66 | { 67 | QMdnsEngine::Cache cache; 68 | cache.addRecord(createRecord()); 69 | 70 | QSignalSpy shouldQuerySpy(&cache, SIGNAL(shouldQuery(Record))); 71 | QSignalSpy recordExpiredSpy(&cache, SIGNAL(recordExpired(Record))); 72 | 73 | // The record should be in the cache 74 | QMdnsEngine::Record record; 75 | QVERIFY(cache.lookupRecord(Name, Type, record)); 76 | 77 | // After entering the event loop, the record should be purged when its TTL 78 | // expires in 1s 79 | QTRY_VERIFY(!cache.lookupRecord(Name, Type, record)); 80 | 81 | // Ensure that the shouldQuery() signal was emitted at least once and the 82 | // recordExpired() signal was eventually emitted as well 83 | QVERIFY(shouldQuerySpy.count() > 0); 84 | QCOMPARE(recordExpiredSpy.count(), 1); 85 | } 86 | 87 | void TestCache::testRemoval() 88 | { 89 | QMdnsEngine::Cache cache; 90 | QMdnsEngine::Record record = createRecord(); 91 | cache.addRecord(record); 92 | 93 | QSignalSpy recordExpiredSpy(&cache, SIGNAL(recordExpired(Record))); 94 | 95 | // Purge the record from the cache by setting its TTL to 0 96 | record.setTtl(0); 97 | cache.addRecord(record); 98 | 99 | // Verify that the record is gone 100 | QVERIFY(!cache.lookupRecord(Name, Type, record)); 101 | QCOMPARE(recordExpiredSpy.count(), 1); 102 | } 103 | 104 | void TestCache::testCacheFlush() 105 | { 106 | QMdnsEngine::Cache cache; 107 | for (int i = 0; i < 2; ++i) { 108 | cache.addRecord(createRecord()); 109 | } 110 | 111 | QList records; 112 | QVERIFY(cache.lookupRecords(Name, Type, records)); 113 | QCOMPARE(records.length(), 2); 114 | 115 | // Insert a new record with the cache clear bit set 116 | QMdnsEngine::Record record = createRecord(); 117 | record.setFlushCache(true); 118 | cache.addRecord(record); 119 | 120 | // Confirm that only a single record exists 121 | records.clear(); 122 | QVERIFY(cache.lookupRecords(Name, Type, records)); 123 | QCOMPARE(records.length(), 1); 124 | } 125 | 126 | QMdnsEngine::Record TestCache::createRecord() 127 | { 128 | QMdnsEngine::Record record; 129 | record.setName(Name); 130 | record.setType(Type); 131 | record.setTtl(1); 132 | record.addAttribute("key", QByteArray::number(mCounter++)); 133 | return record; 134 | } 135 | 136 | QTEST_MAIN(TestCache) 137 | #include "TestCache.moc" 138 | -------------------------------------------------------------------------------- /tests/TestHostname.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "common/testserver.h" 37 | 38 | const quint16 Port = 1234; 39 | 40 | class TestHostname : public QObject 41 | { 42 | Q_OBJECT 43 | 44 | private Q_SLOTS: 45 | 46 | void testAcquire(); 47 | void testAnswer(); 48 | }; 49 | 50 | void TestHostname::testAcquire() 51 | { 52 | TestServer server; 53 | QMdnsEngine::Hostname hostname(&server); 54 | QSignalSpy hostnameChangedSpy(&hostname, SIGNAL(hostnameChanged(QByteArray))); 55 | 56 | // Wait for the first query 57 | QTRY_VERIFY(server.receivedMessages().count() > 0); 58 | QMdnsEngine::Message probe = server.receivedMessages().at(0); 59 | QVERIFY(probe.queries().count() > 0); 60 | 61 | // Immediately indicate that name is taken 62 | QMdnsEngine::Record record; 63 | record.setName(probe.queries().at(0).name()); 64 | record.setType(QMdnsEngine::A); 65 | record.setAddress(QHostAddress("127.0.0.1")); 66 | QMdnsEngine::Message message; 67 | message.addRecord(record); 68 | server.deliverMessage(message); 69 | 70 | // Wait for another attempt and verify the name is different 71 | QTRY_VERIFY(hostname.isRegistered()); 72 | QCOMPARE(hostname.hostname(), probe.queries().at(0).name()); 73 | QCOMPARE(hostnameChangedSpy.count(), 1); 74 | } 75 | 76 | void TestHostname::testAnswer() 77 | { 78 | if (!QNetworkInterface::allAddresses().count()) { 79 | QSKIP("no addresses available"); 80 | } 81 | QHostAddress address = QNetworkInterface::allAddresses().at(0); 82 | 83 | TestServer server; 84 | QMdnsEngine::Hostname hostname(&server); 85 | 86 | // Wait for the hostname to be acquired 87 | QTRY_VERIFY(hostname.isRegistered()); 88 | server.clearReceivedMessages(); 89 | 90 | // Build a query for the host appearing to come from the first address 91 | QMdnsEngine::Query query; 92 | query.setName(hostname.hostname()); 93 | query.setType(QMdnsEngine::A); 94 | QMdnsEngine::Message message; 95 | message.setAddress(address); 96 | message.setPort(Port); 97 | message.addQuery(query); 98 | server.deliverMessage(message); 99 | 100 | // Ensure a valid reply was received 101 | QTRY_VERIFY(server.receivedMessages().count() > 0); 102 | QMdnsEngine::Message reply = server.receivedMessages().at(0); 103 | QCOMPARE(reply.address(), address); 104 | QCOMPARE(reply.port(), Port); 105 | QVERIFY(reply.records().count() > 0); 106 | } 107 | 108 | QTEST_MAIN(TestHostname) 109 | #include "TestHostname.moc" 110 | -------------------------------------------------------------------------------- /tests/TestProber.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "common/testserver.h" 34 | 35 | const QByteArray Name = "Test._http._tcp.local."; 36 | const quint16 Type = QMdnsEngine::SRV; 37 | 38 | class TestProber : public QObject 39 | { 40 | Q_OBJECT 41 | 42 | private Q_SLOTS: 43 | 44 | void testProbe(); 45 | }; 46 | 47 | void TestProber::testProbe() 48 | { 49 | QMdnsEngine::Record record; 50 | record.setName(Name); 51 | record.setType(Type); 52 | 53 | TestServer server; 54 | QMdnsEngine::Prober prober(&server, record); 55 | QSignalSpy nameConfirmedSpy(&prober, SIGNAL(nameConfirmed(QByteArray))); 56 | 57 | // Return an existing record to signal a conflict 58 | QMdnsEngine::Message message; 59 | message.setResponse(true); 60 | message.addRecord(record); 61 | server.deliverMessage(message); 62 | 63 | // Wait for the replacement name to be confirmed 64 | QTRY_COMPARE(nameConfirmedSpy.count(), 1); 65 | QVERIFY(nameConfirmedSpy.at(0).at(0).toByteArray() != Name); 66 | } 67 | 68 | QTEST_MAIN(TestProber) 69 | #include "TestProber.moc" 70 | -------------------------------------------------------------------------------- /tests/TestProvider.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "common/testserver.h" 34 | 35 | const QByteArray Name = "Test"; 36 | const QByteArray Type = "_test._tcp.local."; 37 | const QByteArray Fqdn = Name + "." + Type; 38 | const quint16 Port = 1234; 39 | const QByteArray Key = "key"; 40 | const QByteArray Value = "value"; 41 | 42 | class TestProvider : public QObject 43 | { 44 | Q_OBJECT 45 | 46 | private Q_SLOTS: 47 | 48 | void testProvider(); 49 | }; 50 | 51 | void TestProvider::testProvider() 52 | { 53 | TestServer server; 54 | QMdnsEngine::Hostname hostname(&server); 55 | QMdnsEngine::Provider provider(&server, &hostname); 56 | 57 | // Create a service description and provide it 58 | QMdnsEngine::Service service; 59 | service.setName(Name); 60 | service.setType(Type); 61 | service.setPort(Port); 62 | service.setAttributes({{Key, Value}}); 63 | provider.update(service); 64 | 65 | // Wait for the three records to be announced (3 on each protocol) 66 | QMdnsEngine::Record record; 67 | QTRY_VERIFY(server.cache()->lookupRecord(Type, QMdnsEngine::PTR, record)); 68 | QCOMPARE(record.target(), Fqdn); 69 | QTRY_VERIFY(server.cache()->lookupRecord(Fqdn, QMdnsEngine::SRV, record)); 70 | QCOMPARE(record.target(), hostname.hostname()); 71 | QCOMPARE(record.port(), Port); 72 | QTRY_VERIFY(server.cache()->lookupRecord(Fqdn, QMdnsEngine::TXT, record)); 73 | QCOMPARE(record.attributes(), service.attributes()); 74 | } 75 | 76 | QTEST_MAIN(TestProvider) 77 | #include "TestProvider.moc" 78 | -------------------------------------------------------------------------------- /tests/TestResolver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "common/testserver.h" 35 | #include "common/util.h" 36 | 37 | Q_DECLARE_METATYPE(QHostAddress) 38 | 39 | const QByteArray Name = "test.localhost."; 40 | const QHostAddress Address("127.0.0.1"); 41 | 42 | class TestResolver : public QObject 43 | { 44 | Q_OBJECT 45 | 46 | private Q_SLOTS: 47 | 48 | void initTestCase(); 49 | void testResolver(); 50 | }; 51 | 52 | void TestResolver::initTestCase() 53 | { 54 | qRegisterMetaType("QHostAddress"); 55 | } 56 | 57 | void TestResolver::testResolver() 58 | { 59 | TestServer server; 60 | QMdnsEngine::Resolver resolver(&server, Name); 61 | QSignalSpy resolvedSpy(&resolver, SIGNAL(resolved(QHostAddress))); 62 | 63 | // Ensure two queries were dispatched 64 | QTRY_VERIFY(queryReceived(&server, Name, QMdnsEngine::A)); 65 | QVERIFY(queryReceived(&server, Name, QMdnsEngine::AAAA)); 66 | 67 | // Send a record with an address 68 | QMdnsEngine::Record record; 69 | record.setName(Name); 70 | record.setType(QMdnsEngine::A); 71 | record.setAddress(Address); 72 | QMdnsEngine::Message message; 73 | message.setResponse(true); 74 | message.addRecord(record); 75 | message.addRecord(record); 76 | server.deliverMessage(message); 77 | 78 | // Ensure resolved() was emitted with the right address 79 | QTRY_COMPARE(resolvedSpy.count(), 1); 80 | QCOMPARE(resolvedSpy.at(0).at(0).value(), Address); 81 | } 82 | 83 | QTEST_MAIN(TestResolver) 84 | #include "TestResolver.moc" 85 | -------------------------------------------------------------------------------- /tests/common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SRC 2 | testserver.cpp 3 | util.cpp 4 | ) 5 | 6 | add_library(common STATIC ${SRC}) 7 | set_target_properties(common PROPERTIES 8 | CXX_STANDARD 11 9 | CXX_STANDARD_REQUIRED ON 10 | ) 11 | target_link_libraries(common qmdnsengine) 12 | -------------------------------------------------------------------------------- /tests/common/testserver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #include "testserver.h" 29 | 30 | void TestServer::sendMessage(const QMdnsEngine::Message &message) 31 | { 32 | saveMessage(message); 33 | } 34 | 35 | void TestServer::sendMessageToAll(const QMdnsEngine::Message &message) 36 | { 37 | QMdnsEngine::Message ipv4Message = message; 38 | ipv4Message.setAddress(QMdnsEngine::MdnsIpv4Address); 39 | ipv4Message.setPort(QMdnsEngine::MdnsPort); 40 | saveMessage(ipv4Message); 41 | QMdnsEngine::Message ipv6Message = message; 42 | ipv4Message.setAddress(QMdnsEngine::MdnsIpv6Address); 43 | ipv4Message.setPort(QMdnsEngine::MdnsPort); 44 | saveMessage(ipv6Message); 45 | } 46 | 47 | void TestServer::deliverMessage(const QMdnsEngine::Message &message) 48 | { 49 | emit messageReceived(message); 50 | } 51 | 52 | QList TestServer::receivedMessages() const 53 | { 54 | return mMessages; 55 | } 56 | 57 | void TestServer::clearReceivedMessages() 58 | { 59 | mMessages.clear(); 60 | } 61 | 62 | const QMdnsEngine::Cache *TestServer::cache() const 63 | { 64 | return &mCache; 65 | } 66 | 67 | void TestServer::saveMessage(const QMdnsEngine::Message &message) 68 | { 69 | mMessages.append(message); 70 | if (message.isResponse()) { 71 | const auto records = message.records(); 72 | for (const QMdnsEngine::Record &record : records) { 73 | mCache.addRecord(record); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/common/testserver.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef COMMON_TESTSERVER_H 26 | #define COMMON_TESTSERVER_H 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | /** 35 | * @brief Test server that stores sent messages and enables manual message delivery 36 | */ 37 | class TestServer : public QMdnsEngine::AbstractServer 38 | { 39 | Q_OBJECT 40 | 41 | public: 42 | 43 | virtual void sendMessage(const QMdnsEngine::Message &message); 44 | virtual void sendMessageToAll(const QMdnsEngine::Message &message); 45 | 46 | void deliverMessage(const QMdnsEngine::Message &message); 47 | 48 | QList receivedMessages() const; 49 | void clearReceivedMessages(); 50 | 51 | const QMdnsEngine::Cache *cache() const; 52 | 53 | private: 54 | 55 | void saveMessage(const QMdnsEngine::Message &message); 56 | 57 | QList mMessages; 58 | QMdnsEngine::Cache mCache; 59 | }; 60 | 61 | #endif // COMMON_TESTSERVER_H 62 | -------------------------------------------------------------------------------- /tests/common/util.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #include "util.h" 29 | 30 | bool queryReceived(TestServer *server, const QByteArray &name, quint16 type) 31 | { 32 | const auto messages = server->receivedMessages(); 33 | for (const QMdnsEngine::Message &message : messages) { 34 | if (!message.isResponse()) { 35 | const auto queries = message.queries(); 36 | for (const QMdnsEngine::Query &query : queries) { 37 | if (query.name() == name && query.type() == type) { 38 | return true; 39 | } 40 | } 41 | } 42 | } 43 | return false; 44 | } 45 | -------------------------------------------------------------------------------- /tests/common/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Nathan Osman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef COMMON_UTIL_H 26 | #define COMMON_UTIL_H 27 | 28 | #include 29 | 30 | #include "testserver.h" 31 | 32 | bool queryReceived(TestServer *server, const QByteArray &name, quint16 type); 33 | 34 | #endif // COMMON_UTIL_H 35 | --------------------------------------------------------------------------------