添加广播MDNS服务

This commit is contained in:
钟富强 2024-08-19 09:39:32 +08:00
parent 1f7bc017ca
commit 0a5b0db9a5
192 changed files with 22181 additions and 616 deletions

View File

@ -42,6 +42,22 @@
</ClCompile>
<ClCompile Include="SourceCode\Media\VideoDecoder\RingBuffer.cpp" />
<ClCompile Include="SourceCode\Network\ClientHandler.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\abstractserver.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\bitmap.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\browser.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\cache.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\dns.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\hostname.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\mdns.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\message.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\prober.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\provider.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\query.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\record.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\resolver.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\server.cpp" />
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\service.cpp" />
<ClCompile Include="SourceCode\Network\mdns\servicemodel.cpp" />
<ClCompile Include="SourceCode\ParseDataHandler\parseData.cpp" />
<ClCompile Include="SourceCode\RecvDataHandler\RecvDataHandler.cpp" />
<ClCompile Include="SourceCode\Widget\MainWidget.cpp">
@ -82,10 +98,41 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="SourceCode\Json\readJsonFile.h" />
<QtMoc Include="SourceCode\LicenseGenerate\LicenseConfirmWindow.h" />
<ClInclude Include="SourceCode\LicenseGenerate\LicenseGenerate.h" />
<ClInclude Include="SourceCode\LicenseGenerate\p_code.h" />
<ClInclude Include="SourceCode\Media\Media.h" />
<QtMoc Include="SourceCode\Network\LicenseConfirmWindow.h" />
<QtMoc Include="SourceCode\Network\ImageEnrollWindow.h" />
<QtMoc Include="SourceCode\Network\PasswordEnrollWindow.h" />
<QtMoc Include="SourceCode\Network\mdns\servicemodel.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\abstractserver.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\bitmap.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\browser.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\cache.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\dns.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\hostname.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\mdns.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\message.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\prober.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\provider.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\qmdnsengine_export.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\query.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\record.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\resolver.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\server.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\service.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\src\bitmap_p.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\browser_p.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\cache_p.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\hostname_p.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\src\message_p.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\prober_p.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\provider_p.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\src\query_p.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\src\record_p.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\resolver_p.h" />
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\server_p.h" />
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\src\service_p.h" />
<ClInclude Include="SourceCode\RecvDataHandler\MsgTpye.h" />
<QtMoc Include="SourceCode\Widget\FocusWindow.h" />
<QtMoc Include="SourceCode\Media\VideoDecoder\FFmpegDecoder.h" />

View File

@ -32,6 +32,54 @@
<ClCompile Include="SourceCode\LicenseGenerate\LicenseGenerate.cpp">
<Filter>LicenseGenerate</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\servicemodel.cpp">
<Filter>Network\mdns</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\abstractserver.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\bitmap.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\browser.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\cache.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\dns.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\hostname.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\mdns.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\message.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\prober.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\provider.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\query.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\record.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\resolver.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\server.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
<ClCompile Include="SourceCode\Network\mdns\qmdnsengine\src\service.cpp">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<QtMoc Include="SourceCode\HotPlugListener.h">
@ -55,9 +103,63 @@
<QtMoc Include="SourceCode\Network\DelUserWindows.h">
<Filter>Network</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\LicenseConfirmWindow.h">
<QtMoc Include="SourceCode\LicenseGenerate\LicenseConfirmWindow.h">
<Filter>LicenseGenerate</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\ImageEnrollWindow.h">
<Filter>Network</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\PasswordEnrollWindow.h">
<Filter>Network</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\servicemodel.h">
<Filter>Network\mdns</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\abstractserver.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\browser.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\cache.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\hostname.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\prober.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\provider.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\resolver.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\include\server.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\browser_p.h">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\cache_p.h">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\hostname_p.h">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\prober_p.h">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\provider_p.h">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\resolver_p.h">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</QtMoc>
<QtMoc Include="SourceCode\Network\mdns\qmdnsengine\src\server_p.h">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<None Include=".editorconfig" />
@ -135,6 +237,18 @@
<Filter Include="LicenseGenerate">
<UniqueIdentifier>{463f4c83-c88f-4815-b160-19430dd30eda}</UniqueIdentifier>
</Filter>
<Filter Include="Network\mdns">
<UniqueIdentifier>{3570ab1e-6ede-402b-8dd0-1c53afff0bd8}</UniqueIdentifier>
</Filter>
<Filter Include="Network\mdns\qmdnsengine">
<UniqueIdentifier>{d437b89e-76f2-495a-9434-e6ab8479c960}</UniqueIdentifier>
</Filter>
<Filter Include="Network\mdns\qmdnsengine\include">
<UniqueIdentifier>{7f4c4097-0c9f-4b37-967c-5efae6108a91}</UniqueIdentifier>
</Filter>
<Filter Include="Network\mdns\qmdnsengine\src">
<UniqueIdentifier>{e8857e15-2519-4a60-9fd5-eff901a76ba6}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="SourceCode\Media\Media.h">
@ -161,5 +275,44 @@
<ClInclude Include="SourceCode\LicenseGenerate\LicenseGenerate.h">
<Filter>LicenseGenerate</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\bitmap.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\dns.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\mdns.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\message.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\query.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\record.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\service.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\src\bitmap_p.h">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\src\message_p.h">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\src\query_p.h">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\src\record_p.h">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\src\service_p.h">
<Filter>Network\mdns\qmdnsengine\src</Filter>
</ClInclude>
<ClInclude Include="SourceCode\Network\mdns\qmdnsengine\include\qmdnsengine_export.h">
<Filter>Network\mdns\qmdnsengine\include</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -0,0 +1,8 @@
[
{
"cmd": "V851_SPK_TEST",
"val": 0,
"lable": "V851²⊔SPK",
"timeout": 2000
}
]

View File

@ -0,0 +1,118 @@
[
{
"cmd": "V851_SPK_TEST",
"val": 0,
"lable": "V851测试SPK",
"timeout": 2000
},
{
"cmd": "806_SPK_TEST",
"val": 0,
"lable": "806测试SPK",
"timeout": 2000
},
{
"cmd": "MIC_TEST",
"val": 0,
"lable": "MIC测试",
"timeout": 2000
},
{
"cmd": "UNLOCK_LOCK_TEST",
"val": 0,
"lable": "开锁测试",
"timeout": 2000
},
{
"cmd": "UNLOCK_LOCK_TEST",
"val": 1,
"lable": "关锁测试",
"timeout": 2000
},
{
"cmd": "BUTTON_TEST",
"val": 0,
"lable": "开锁按键测试",
"timeout": 2000
},
{
"cmd": "BUTTON_TEST",
"val": 1,
"lable": "关锁按键测试",
"timeout": 2000
},
{
"cmd": "BUTTON_TEST",
"val": 2,
"lable": "猫眼按键测试",
"timeout": 2000
},
{
"cmd": "BACKLIGHT_TEST",
"val": 0,
"lable": "后屏背光测试",
"timeout": 2000
},
{
"cmd": "TOUCH_TEST",
"x1": 100,
"y1": 150,
"r1": 50,
"x2": 620,
"y2": 150,
"r2": 50,
"x3": 360,
"y3": 640,
"r3": 50,
"x4": 100,
"y4": 1130,
"r4": 50,
"x5": 620,
"y5": 1130,
"r5": 50,
"lable": "后触摸屏测试",
"timeout": 2000
},
{
"cmd": "START_CAT_EYE_TEST",
"val": 0,
"lable": "开猫眼测试",
"timeout": 2000
},
{
"cmd": "UART_TEST",
"val": 0,
"lable": "串口测试",
"timeout": 2000
},
{
"cmd": "VIDEO_TEST",
"val": 0,
"lable": "开启视频",
"timeout": 2000
},
{
"cmd": "VIDEO_TEST",
"val": 1,
"lable": "关闭视频",
"timeout": 2000
},
{
"cmd": "WIFI_SIGNAL_TEST",
"val": 0,
"lable": "wifi信号测试",
"timeout": 2000
},
{
"cmd": "ENTER_CONFIG_NET",
"val": 0,
"lable": "进入配网测试",
"timeout": 2000
},
{
"cmd": "NETWORK_TEST",
"val": 0,
"lable": "联网测试",
"timeout": 2000
}
]

View File

@ -1,79 +1,118 @@
[
{
"msdID": "text",
"val": "text",
"lable": "<22>״<EFBFBD><D7B4><EFBFBD><EFBFBD>"
},
{
"cmd": "text",
"val": "text",
"lable": "NFC测试",
"cmd": "V851_SPK_TEST",
"val": 0,
"lable": "V851测试SPK",
"timeout": 2000
},
{
"cmd": "text",
"val": "text",
"lable": "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ע<EFBFBD><EFBFBD>",
"cmd": "806_SPK_TEST",
"val": 0,
"lable": "806测试SPK",
"timeout": 2000
},
{
"cmd": "text",
"val": "text",
"lable": "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʶ<EFBFBD><EFBFBD>",
"cmd": "MIC_TEST",
"val": 0,
"lable": "MIC测试",
"timeout": 2000
},
{
"cmd": "text",
"val": "text",
"lable": "<EFBFBD>ƾ<EFBFBD><EFBFBD><EFBFBD>ע<EFBFBD><EFBFBD>",
"cmd": "UNLOCK_LOCK_TEST",
"val": 0,
"lable": "开锁测试",
"timeout": 2000
},
{
"cmd": "text",
"val": "text",
"lable": "<EFBFBD>ƾ<EFBFBD><EFBFBD><EFBFBD>ʶ<EFBFBD><EFBFBD>",
"cmd": "UNLOCK_LOCK_TEST",
"val": 1,
"lable": "关锁测试",
"timeout": 2000
},
{
"cmd": "text",
"val": "text",
"lable": "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>",
"cmd": "BUTTON_TEST",
"val": 0,
"lable": "开锁按键测试",
"timeout": 2000
},
{
"cmd": "text",
"val": "text",
"lable": "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>",
"cmd": "BUTTON_TEST",
"val": 1,
"lable": "关锁按键测试",
"timeout": 2000
},
{
"cmd": "text",
"val": "text",
"lable": "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>",
"cmd": "BUTTON_TEST",
"val": 2,
"lable": "猫眼按键测试",
"timeout": 2000
},
{
"cmd": "text",
"val": "text",
"lable": "MIC<EFBFBD><EFBFBD><EFBFBD><EFBFBD>",
"cmd": "BACKLIGHT_TEST",
"val": 0,
"lable": "后屏背光测试",
"timeout": 2000
},
{
"cmd": "text",
"val": "text",
"lable": "SPK<50><4B><EFBFBD><EFBFBD>",
"cmd": "TOUCH_TEST",
"x1": 100,
"y1": 150,
"r1": 50,
"x2": 620,
"y2": 150,
"r2": 50,
"x3": 360,
"y3": 640,
"r3": 50,
"x4": 100,
"y4": 1130,
"r4": 50,
"x5": 620,
"y5": 1130,
"r5": 50,
"lable": "后触摸屏测试",
"timeout": 2000
},
{
"cmd": "text",
"val": "text",
"lable": "<22><>è<EFBFBD><C3A8>",
"cmd": "START_CAT_EYE_TEST",
"val": 0,
"lable": "开猫眼测试",
"timeout": 2000
},
{
"cmd": "set_volume",
"val": 50,
"lable": "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>",
"cmd": "UART_TEST",
"val": 0,
"lable": "串口测试",
"timeout": 2000
},
{
"cmd": "VIDEO_TEST",
"val": 0,
"lable": "开启视频",
"timeout": 2000
},
{
"cmd": "VIDEO_TEST",
"val": 1,
"lable": "关闭视频",
"timeout": 2000
},
{
"cmd": "WIFI_SIGNAL_TEST",
"val": 0,
"lable": "wifi信号测试",
"timeout": 2000
},
{
"cmd": "ENTER_CONFIG_NET",
"val": 0,
"lable": "进入配网测试",
"timeout": 2000
},
{
"cmd": "NETWORK_TEST",
"val": 0,
"lable": "联网测试",
"timeout": 2000
}
]

View File

@ -1,12 +1,12 @@
[
{
"cmd": "GET_BACK_SW_VERSION",
"cmd": "GET_BACK_V851_VERSION",
"val": 0,
"lable": "后板V851版本:",
"timeout": 2000
},
{
"cmd": "GET_BACK_SW_VERSION",
"cmd": "GET_BACK_806_VERSION",
"val": 0,
"lable": "后板806版本:",
"timeout": 2000
@ -24,9 +24,9 @@
"timeout": 2000
},
{
"cmd": "GET_BACK_UID",
"cmd": "GET_BACK_UUID",
"val": 0,
"lable": "UID:",
"lable": "UUID:",
"timeout": 2000
}
]

View File

@ -61,26 +61,40 @@
},
{
"cmd": "IMG_ENROLL",
"val": 0,
"val": "",
"lable": "图片注册",
"timeout": 2000
},
{
"cmd": "GET_IMG",
"val": 0,
"lable": "取图(L)",
"lable": "左边镜头取图",
"timeout": 2000
},
{
"cmd": "GET_IMG",
"val": 1,
"lable": "取图(R)",
"lable": "右边镜头取图",
"timeout": 2000
},
{
"cmd": "TOUCH_TEST",
"val": 0,
"lable": "触摸屏测试",
"x1": 100,
"y1": 150,
"r1": 50,
"x2": 620,
"y2": 150,
"r2": 50,
"x3": 360,
"y3": 640,
"r3": 50,
"x4": 100,
"y4": 1130,
"r4": 50,
"x5": 620,
"y5": 1130,
"r5": 50,
"lable": "前触摸屏测试",
"timeout": 2000
},
{

View File

@ -55,7 +55,7 @@
},
{
"cmd": "PASSWD_ENROLL",
"val": 0,
"val": "123456",
"lable": "密码注册",
"timeout": 2000
},

View File

@ -51,6 +51,18 @@ QJsonArray readJson_frontLicense() {
return readJsonArrayFromFile("./SourceCode/Json/JsonFile/frontBoardLicense.json");
}
QJsonArray readJson_backBoardOneClickTest() {
return readJsonArrayFromFile("./SourceCode/Json/JsonFile/backBoardOneClickTest.json");
}
QJsonArray readJson_backBoardTest() {
return readJsonArrayFromFile("./SourceCode/Json/JsonFile/backBoardTest.json");
}
QJsonArray readJson_backBoardFuncConfig() {
return readJsonArrayFromFile("./SourceCode/Json/JsonFile/backBoardFuncConfig.json");
}
QJsonArray readJson_backDevInfo() {
return readJsonArrayFromFile("./SourceCode/Json/JsonFile/backDevInfo.json");
}

View File

@ -12,6 +12,10 @@ QJsonArray readJson_frontBoardTest();
QJsonArray readJson_frontBoardFuncConfig();
QJsonArray readJson_frontDevInfo();
QJsonArray readJson_frontLicense();
QJsonArray readJson_backBoardOneClickTest();
QJsonArray readJson_backBoardTest();
QJsonArray readJson_backBoardFuncConfig();
QJsonArray readJson_backDevInfo();
QJsonArray readJson_testConfig();

View File

@ -1,17 +1,11 @@
// LicenseGenerate.cpp
#include "LicenseGenerate.h"
#define PIX_HARDWARE_INFO_BYTES 32
#define PIX_LICENCE_BYTES 128
typedef const char* (*pix_license_generate_version_func)();
typedef int (*pix_license_generate_func)(const unsigned char*, int, unsigned char*, int);
void licenseGenerate()
bool licenseGenerate(const unsigned char* hardware_info, unsigned char* license_info)
{
unsigned char hardware_info[PIX_HARDWARE_INFO_BYTES] = { };
unsigned char license_info[PIX_LICENCE_BYTES] = { 0 };
// 获取当前路径
wchar_t currentPath[MAX_PATH];
GetCurrentDirectoryW(MAX_PATH, currentPath);
@ -25,8 +19,7 @@ void licenseGenerate()
HINSTANCE hDLL = LoadLibraryW(dllPath.c_str());
if (hDLL == NULL) {
std::cerr << "Failed to load DLL. Error code: " << GetLastError() << std::endl;
FreeLibrary(hDLL);
return;
return false;
}
pix_license_generate_version_func pix_license_generate_version =
(pix_license_generate_version_func)GetProcAddress(hDLL, "pix_license_generate_version");
@ -35,23 +28,25 @@ void licenseGenerate()
if (pix_license_generate_version == NULL || pix_license_generate == NULL) {
std::cerr << "Failed to find one or more functions." << std::endl;
FreeLibrary(hDLL);
return;
return false;
}
printf("pix_license_generate_version is %s\n",
pix_license_generate_version());
printf("Hardware info:");
for (int j = 0; j < PIX_HARDWARE_INFO_BYTES; ++j) {
hardware_info[j] = j;
//hardware_info[j] = j;
printf("0x%02x, ", hardware_info[j]);
}
printf("\n");
// return 限制不调用 pix_license_generate, 防止减少license个数
return;
return false;
int ret = pix_license_generate(hardware_info, PIX_HARDWARE_INFO_BYTES,
license_info, PIX_LICENCE_BYTES);
if (ret != SDK_CODE_OK) {
printf("Fail to generate license with %d\n", ret);
FreeLibrary(hDLL);
return false;
}
else {
printf("License is\n");
@ -62,7 +57,6 @@ void licenseGenerate()
}
FreeLibrary(hDLL);
return;
return true;
}

View File

@ -10,6 +10,9 @@
#include <stdio.h>
#include "p_code.h"
void licenseGenerate();
#define PIX_HARDWARE_INFO_BYTES 32
#define PIX_LICENCE_BYTES 128
bool licenseGenerate(const unsigned char* hardware_info, unsigned char* license_info);
#endif

View File

@ -1,255 +1,3 @@
// FFmpegDecoder.cpp
//#include "FFmpegDecoder.h"
//
//FFmpegDecoder::FFmpegDecoder(QObject* parent) :
// QThread(parent),
// videoLabel(nullptr),
// abort(false),
// restart(false),
// formatContext(nullptr),
// codecContext(nullptr),
// frame(nullptr),
// packet(nullptr),
// swsContext(nullptr),
// videoStreamIndex(-1) // 初始化成员变量
//{
// av_log_set_level(AV_LOG_QUIET); // 设置日志级别为安静模式
// avformat_network_init(); // 初始化网络
//}
//
//FFmpegDecoder::~FFmpegDecoder()
//{
// mutex.lock();
// abort = true;
// condition.wakeOne();
// mutex.unlock();
// wait();
// if (codecContext) {
// avcodec_free_context(&codecContext);
// }
// if (frame) {
// av_frame_free(&frame);
// }
// if (packet) {
// av_packet_free(&packet);
// }
// if (swsContext) {
// sws_freeContext(swsContext);
// }
// if (formatContext) {
// avformat_close_input(&formatContext);
// }
// avformat_network_deinit(); // 反初始化网络
//}
//
//void FFmpegDecoder::initialize()
//{
// // 初始化FFmpeg库
// avformat_network_init();
//}
//
//void FFmpegDecoder::decodeFile(const QString& filePath, QLabel* videoLabel)
//{
// QMutexLocker locker(&mutex);
// this->filePath = filePath;
// this->videoLabel = videoLabel;
// if (!isRunning()) {
// //start(LowPriority);
// start(NormalPriority);
// }
// restart = true;
// condition.wakeOne();
//}
//
//void FFmpegDecoder::run()
//{
// QFile file(filePath);
// qint64 fileSize = 0;
//
// for (;;) {
// mutex.lock();
// while (!restart && !abort) {
// condition.wait(&mutex);
// }
// if (abort) {
// mutex.unlock();
// break;
// }
// restart = false;
// QLabel* currentVideoLabel = videoLabel;
// QSize labelSize = currentVideoLabel->size();
// mutex.unlock();
// qDebug() << "Video label size: Width =" << labelSize.width() << ", Height =" << labelSize.height();
//
// if (!file.open(QIODevice::ReadOnly)) {
// qWarning() << "Failed to open file:" << filePath;
// continue;
// }
// // 从上次处理的位置开始读取
// file.seek(fileSize);
//
// formatContext = nullptr;
// codecContext = nullptr;
// frame = nullptr;
// packet = nullptr;
//
// // 通过 FFmpeg 初始化格式上下文和解码器
// if (avformat_open_input(&formatContext, filePath.toStdString().c_str(), nullptr, nullptr) != 0) {
// qWarning() << "Failed to open file with FFmpeg:" << filePath;
// file.close();
// continue;
// }
//
// if (avformat_find_stream_info(formatContext, nullptr) < 0) {
// qWarning() << "Failed to retrieve stream info";
// avformat_close_input(&formatContext);
// file.close();
// continue;
// }
//
// videoStreamIndex = -1;
// for (unsigned int i = 0; i < formatContext->nb_streams; ++i) {
// if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
// videoStreamIndex = i;
// break;
// }
// }
// if (videoStreamIndex == -1) {
// qWarning() << "No video stream found";
// avformat_close_input(&formatContext);
// file.close();
// continue;
// }
//
// AVCodecParameters* codecParameters = formatContext->streams[videoStreamIndex]->codecpar;
// const AVCodec* codec = avcodec_find_decoder(codecParameters->codec_id);
// if (!codec) {
// qWarning() << "Unsupported codec";
// avformat_close_input(&formatContext);
// file.close();
// continue;
// }
//
// codecContext = avcodec_alloc_context3(codec);
// if (!codecContext) {
// qWarning() << "Failed to allocate codec context";
// avformat_close_input(&formatContext);
// file.close();
// continue;
// }
//
// if (avcodec_parameters_to_context(codecContext, codecParameters) < 0) {
// qWarning() << "Failed to copy codec parameters to context";
// avcodec_free_context(&codecContext);
// avformat_close_input(&formatContext);
// file.close();
// continue;
// }
//
// if (avcodec_open2(codecContext, codec, nullptr) < 0) {
// qWarning() << "Failed to open codec";
// avcodec_free_context(&codecContext);
// avformat_close_input(&formatContext);
// file.close();
// continue;
// }
//
// frame = av_frame_alloc();
// packet = av_packet_alloc();
//
// // 主解码循环
// while (!abort) {
// qint64 currentFileSize = file.size();
// if (currentFileSize > fileSize) {
// fileSize = currentFileSize;
// file.seek(fileSize); // 设置文件读取位置到末尾
//
// // 读取并处理数据包
// while (av_read_frame(formatContext, packet) >= 0) {
// if (packet->stream_index == videoStreamIndex) {
// int ret = avcodec_send_packet(codecContext, packet);
// if (ret < 0) {
// qWarning() << "Error sending packet for decoding";
// av_packet_unref(packet);
// continue;
// }
// while (ret >= 0) {
// ret = avcodec_receive_frame(codecContext, frame);
// if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
// qWarning() << "----------- break";
// av_packet_unref(packet);
// continue;
// //break;
// }
// else if (ret < 0) {
// qWarning() << "Error during decoding";
// break;
// }
//
// /*mutex.lock();
// QSize labelSize = currentVideoLabel->size();
// mutex.unlock();*/
// //qDebug() << "Video label size: Width =" << labelSize.width() << ", Height =" << labelSize.height();
// QImage img = avFrameToQImage(frame);
// QImage scaledImage = img.scaled(labelSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
// currentVideoLabel->setPixmap(QPixmap::fromImage(scaledImage));
// //currentVideoLabel->setPixmap(QPixmap::fromImage(img));
// QThread::msleep(10); // Simulate 25 FPS frame rate
// }
// }
// av_packet_unref(packet);
// }
// }
//
// mutex.lock();
// if (restart) {
// restart = false;
// mutex.unlock();
// break;
// }
// mutex.unlock();
// }
//
// avcodec_free_context(&codecContext);
// avformat_close_input(&formatContext);
// av_frame_free(&frame);
// av_packet_free(&packet);
// file.close();
// sws_freeContext(swsContext);
//
// mutex.lock();
// if (!restart) {
// condition.wait(&mutex);
// }
// mutex.unlock();
// }
//}
//
//
//QImage FFmpegDecoder::avFrameToQImage(AVFrame* frame)
//{
// int width = frame->width;
// int height = frame->height;
// AVPixelFormat pixFmt = (AVPixelFormat)frame->format;
//
// SwsContext* swsCtx = sws_getContext(width, height, pixFmt, width, height, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr);
// if (!swsCtx) {
// qWarning() << "Failed to initialize the conversion context";
// return QImage();
// }
//
// QImage img(width, height, QImage::Format_RGB888);
// uint8_t* dest[4] = { img.bits(), nullptr, nullptr, nullptr };
// int destLinesize[4] = { img.bytesPerLine(), 0, 0, 0 };
//
// sws_scale(swsCtx, frame->data, frame->linesize, 0, height, dest, destLinesize);
// sws_freeContext(swsCtx);
//
// return img;
//}
//
//
// FFmpegDecoder.cpp
#include "FFmpegDecoder.h"
@ -267,7 +15,7 @@ FFmpegDecoder::FFmpegDecoder(QObject* parent) :
{
av_log_set_level(AV_LOG_QUIET); // 设置日志级别为安静模式
avformat_network_init(); // 初始化网络
qDebug() << "FFmpegDecoder created";
//qDebug() << "FFmpegDecoder created";
}
FFmpegDecoder::~FFmpegDecoder()
@ -285,7 +33,6 @@ FFmpegDecoder::~FFmpegDecoder()
void FFmpegDecoder::initialize()
{
qDebug() << "Initializing FFmpeg library";
// 初始化FFmpeg库
avformat_network_init();
}
@ -328,15 +75,12 @@ void FFmpegDecoder::run()
qWarning() << "Failed to open file:" << filePath;
continue;
}
if (!initializeFFmpeg(filePath)) {
qDebug() << "Failed to initialize FFmpeg for file:" << filePath;
cleanup();
file.close();
continue;
}
// 主解码循环
while (!abort) {
qint64 currentFileSize = file.size();
if (currentFileSize > fileSize) {

View File

@ -1,6 +1,8 @@
// ClientHandler.cpp
#include "ClientHandler.h"
#include "LicenseConfirmWindow.h"
#include "../LicenseGenerate/LicenseConfirmWindow.h"
#include "ImageEnrollWindow.h"
#include "PasswordEnrollWindow.h"
ClientHandler::ClientHandler(QTcpSocket* socket, QJsonArray frontBoardOneClickTest, QJsonArray frontBoardTest, QJsonArray frontBoardFuncConfig,
QJsonArray frontBoardDevInfoJson, QJsonArray frontBoardLicenseJson, QJsonArray backBoardDevInfoJson,
@ -14,14 +16,13 @@ ClientHandler::ClientHandler(QTcpSocket* socket, QJsonArray frontBoardOneClickTe
isManualSend(false), isSingleSend(false), isClickedSend(false), size(0),
isFirstDataReceived(true), processDataFunction(nullptr),
isDataStuck(false), dataProcessingActive(false), isRecvVideoData(false),
currentFrontBoardIndex(0), // 初始化为0
currentFrontBoardIndex(0),
currentBackBoardIndex(0)
{
connect(socket, &QTcpSocket::readyRead, this, &ClientHandler::onDataReceived);
connect(socket, &QTcpSocket::disconnected, this, &ClientHandler::onDisconnected);
qint64 bufferSize = socket->socketOption(QAbstractSocket::ReceiveBufferSizeSocketOption).toLongLong();
qDebug() << "------Receive buffer size:" << bufferSize;
/*connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error),
this, &ClientHandler::onSocketError);*/
@ -88,14 +89,14 @@ QString ClientHandler::getCurrentFuncItemData() const
QString ClientHandler::getCurrentItemLable() const
{
QString lable = currentItem.value("lable").toString();
qDebug() << "Getting current item lable:" << lable;
//qDebug() << "Getting current item lable:" << lable;
return lable; // 返回当前项的 "data" 字段
}
QString ClientHandler::getCurrentFuncItemLable() const
{
QString lable = currentFuncItem.value("lable").toString();
qDebug() << "Getting current funcItem lable:" << lable;
//qDebug() << "Getting current funcItem lable:" << lable;
return lable; // 返回当前项的 "data" 字段
}
@ -131,8 +132,11 @@ void ClientHandler::sendJsonItem(const QJsonArray& jsonArray, int itemIndex, con
QMutexLocker locker(&mutex);
isSingleSend = true;
isClickedSend = true;
if (itemType == "getVideo") {
isRecvVideoData = 1;
if (itemType == "handleVideo") {
if(itemIndex == 0)
isRecvVideoData = 1;
else
isRecvVideoData = 0;
startReadVideoDataTimer();
}
qDebug() << "itemIndex" << itemIndex;
@ -177,15 +181,16 @@ void ClientHandler::sendGetDevInfoItem(int itemIndex)
}
// 发送取图按键
void ClientHandler::sendGetPicItem(int itemIndex)
void ClientHandler::sendGetPicItem(int itemIndex, int lastClickedGetVideoCamIndex)
{
sendJsonItem(getPicJson, itemIndex, "", "getPic");
sendJsonItem(getPicJson, itemIndex, QString::number(lastClickedGetVideoCamIndex), "getPic");
}
// 发送拉视频按键
void ClientHandler::sendGetVideoItem(int itemIndex, int video_flag)
{
sendJsonItem(getVideoJson, itemIndex, "", "getVideo");
qDebug() << "sendGetVideoItem itemIndex:" << itemIndex;
sendJsonItem(getVideoJson, itemIndex, "", "handleVideo");
}
// 发送License处理按键
@ -195,16 +200,12 @@ void ClientHandler::sendLicenseItem(int itemIndex)
qDebug() << "Invalid itemIndex";
return;
}
QJsonObject item = frontBoardLicenseJson[itemIndex].toObject();
QString label = item["lable"].toString();
if (label == "write_license" || label == "get_license") {
if (label == "write_license") {
LicenseConfirmWindow dialog("你确定要发送此授权项吗?");
int result = dialog.exec();
if (result == QDialog::Accepted) {
sendJsonItem(frontBoardLicenseJson, itemIndex, "", "License");
if (dialog.exec() == QDialog::Accepted) {
sendJsonItem(frontBoardLicenseJson, itemIndex, "", "License");
}
}
else {
@ -222,7 +223,43 @@ void ClientHandler::sendFuncItem(int itemIndex, const QString text)
// 发送单独一个测试配置 JSON 项目
void ClientHandler::sendItem(int itemIndex)
{
sendJsonItem(frontBoardTest, itemIndex, "", "test");
QString text = "";
QJsonObject currentItem = frontBoardTest[itemIndex].toObject();
if (currentItem.contains("cmd") && currentItem["cmd"].toString() == "IMG_ENROLL") {
ImageEnrollWindow dialog;
if (dialog.exec() == QDialog::Accepted) {
text = dialog.getFilePath();
QByteArray imageData = dialog.getImageData();
if (!imageData.isEmpty()) {
text = QString::fromUtf8(imageData);
}
}
else {
return;
}
}
else if (currentItem.contains("cmd") && currentItem["cmd"].toString() == "DEL_USER") {
DelUserWindow dialog;
if (dialog.exec() == QDialog::Accepted) {
QString userInput = dialog.getUserInput();
if (!userInput.isEmpty() && currentItem.contains("val")) {
text = userInput;
}
}
else {
return;
}
}
else if (currentItem.contains("cmd") && currentItem["cmd"].toString() == "PASSWD_ENROLL") {
PasswordEnrollWindow dialog;
if (dialog.exec() == QDialog::Accepted) {
text = dialog.getPassword();
}
else {
return;
}
}
sendJsonItem(frontBoardTest, itemIndex, text, "test");
}
void ClientHandler::sendDevInfoJsonItem(const QJsonObject& jsonItem, int itemIndex)
@ -406,6 +443,10 @@ void ClientHandler::setThreadPriority(QThread::Priority priority) {
void ClientHandler::onDataReceived()
{
// 接收其他数据 添加区分 视频与其他数据 的标志位
qDebug() << "isRecvVideoData:" << isRecvVideoData;
qDebug() << "isPowerOnSend:" << isPowerOnSend;
qDebug() << "isClickedSend:" << isClickedSend;
qDebug() << "isSingleSend:" << isSingleSend;
if (!isRecvVideoData &&
(isPowerOnSend || isClickedSend || (isSingleSend && (currentItemIndex < frontBoardTest.size())))) {
QByteArray allData;
@ -425,7 +466,8 @@ void ClientHandler::onDataReceived()
emit startTimeout(0);
}
if (!allData.isEmpty()) {
emit dataReceived(getClientAddress(), allData, 0xFF, currentItemIndex, currentFuncItemIndex, "", "");
//emit dataReceived(getClientAddress(), allData, 0xFF, currentItemIndex, currentFuncItemIndex, "", "");
emit dataReceived(getClientAddress(), allData, 0xFF, currentItemIndex, currentFuncItemIndex, getCurrentItemLable(), "");
if (!isSingleSend && !isPowerOnSend) {
currentItemIndex ++;
itemsProcessedCount ++;
@ -458,10 +500,13 @@ void ClientHandler::onDataReceived()
//qDebug() << "" << __FUNCTION__ << "------> itemsProcessedCount " << itemsProcessedCount;
}
// 接收视频流数据 isRecvVideoData 置 0
else if (!dataProcessingActive) {
else if (isRecvVideoData && (!dataProcessingActive)) {
dataProcessingActive = true;
QTimer::singleShot(0, this, &ClientHandler::processPendingData);
}
else {
socket->readAll();
}
}
void ClientHandler::processPendingData()

View File

@ -50,7 +50,7 @@ public:
// 发送获取设备信息按键
void sendGetDevInfoItem(int itemIndex);
// 发送取图按键
void sendGetPicItem(int itemIndex);
void sendGetPicItem(int itemIndex, int lastClickedGetVideoCamIndex);
// 发送拉视频流按键
void sendGetVideoItem(int itemIndex, int video_flag);
// 发送License处理按键

View File

@ -0,0 +1,80 @@
#ifndef IMAGEENROLLWINDOW_H
#define IMAGEENROLLWINDOW_H
#include <QDialog>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFileDialog>
#include <QLabel>
class ImageEnrollWindow : public QDialog
{
Q_OBJECT
public:
explicit ImageEnrollWindow(QWidget* parent = nullptr)
: QDialog(parent)
{
QVBoxLayout* mainLayout = new QVBoxLayout(this);
QLabel* label = new QLabel("请选择注册的图片:", this);
mainLayout->addWidget(label);
QHBoxLayout* inputLayout = new QHBoxLayout;
filePathLineEdit = new QLineEdit(this);
filePathLineEdit->setReadOnly(true);
filePathLineEdit->setPlaceholderText("请选择PNG图片路径...");
QPushButton* browseButton = new QPushButton("选择图片路径", this);
inputLayout->addWidget(filePathLineEdit);
inputLayout->addWidget(browseButton);
mainLayout->addLayout(inputLayout);
QHBoxLayout* buttonLayout = new QHBoxLayout;
QPushButton* confirmButton = new QPushButton("确认", this);
QPushButton* cancelButton = new QPushButton("取消", this);
buttonLayout->addWidget(confirmButton);
buttonLayout->addWidget(cancelButton);
mainLayout->addLayout(buttonLayout);
setWindowTitle("选择图片");
resize(460, 110); // 设置对话框的大小
connect(browseButton, &QPushButton::clicked, this, &ImageEnrollWindow::onBrowseButtonClicked);
connect(confirmButton, &QPushButton::clicked, this, &QDialog::accept);
connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject);
}
QString getFilePath() const {
return filePathLineEdit->text();
}
QByteArray getImageData() const {
QString filePath = filePathLineEdit->text();
if (!filePath.isEmpty()) {
QFile file(filePath);
if (file.open(QIODevice::ReadOnly)) {
QByteArray imageData = file.readAll();
return imageData.toBase64();
}
}
return QByteArray();
}
private slots:
void onBrowseButtonClicked() {
QString filePath = QFileDialog::getOpenFileName(this, tr("选择图片"), "", tr("Images (*.png *.xpm *.jpg *.bmp);;All Files (*)"));
if (!filePath.isEmpty()) {
filePathLineEdit->setText(filePath);
}
}
private:
QLineEdit* filePathLineEdit;
};
#endif // IMAGEENROLLWINDOW_H

View File

@ -0,0 +1,48 @@
// PasswordEnrollWindow.h
#ifndef PASSWORDENROLLWINDOW_H
#define PASSWORDENROLLWINDOW_H
#include <QDialog>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
class PasswordEnrollWindow : public QDialog
{
Q_OBJECT
public:
explicit PasswordEnrollWindow(QWidget* parent = nullptr) : QDialog(parent) {
QVBoxLayout* layout = new QVBoxLayout(this);
QLabel* label = new QLabel("请输入要注册的密码:", this);
layout->addWidget(label);
passwordLineEdit = new QLineEdit(this);
layout->addWidget(passwordLineEdit);
QHBoxLayout* buttonLayout = new QHBoxLayout;
QPushButton* okButton = new QPushButton("确认", this);
QPushButton* cancelButton = new QPushButton("取消", this);
buttonLayout->addWidget(okButton);
buttonLayout->addWidget(cancelButton);
layout->addLayout(buttonLayout);
setWindowTitle("输入密码");
resize(300, 110); // 设置对话框的大小
connect(okButton, &QPushButton::clicked, this, &QDialog::accept);
connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject);
}
QString getPassword() const {
return passwordLineEdit->text();
}
private:
QLineEdit* passwordLineEdit;
};
#endif // PASSWORDENROLLWINDOW_H

View File

@ -0,0 +1,88 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_ABSTRACTSERVER_H
#define QMDNSENGINE_ABSTRACTSERVER_H
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class Message;
/**
* @brief Base class for sending and receiving DNS messages
*
* Many of the other classes in this library require the ability to send and
* receive DNS messages. By having them use this base class, they become far
* easier to test. Any class derived from this one that implements the pure
* virtual methods can be used for sending and receiving DNS messages.
*/
class QMDNSENGINE_EXPORT AbstractServer : public QObject
{
Q_OBJECT
public:
/**
* @brief Abstract constructor
*/
explicit AbstractServer(QObject *parent = 0);
/**
* @brief Send a message to its provided destination
*
* The message should be sent over the IP protocol specified in the
* message and to the target address and port specified in the message.
*/
virtual void sendMessage(const Message &message) = 0;
/**
* @brief Send a message to the multicast address on each interface
*
* The message should be sent over both IPv4 and IPv6 on all interfaces.
*/
virtual void sendMessageToAll(const Message &message) = 0;
Q_SIGNALS:
/**
* @brief Indicate that a DNS message was received
* @param message newly received message
*/
void messageReceived(const Message &message);
/**
* @brief Indicate that an error has occurred
* @param message brief description of the error
*/
void error(const QString &message);
};
}
#endif // QMDNSENGINE_ABSTRACTSERVER_H

View File

@ -0,0 +1,100 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_BITMAP_H
#define QMDNSENGINE_BITMAP_H
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class QMDNSENGINE_EXPORT BitmapPrivate;
/**
* @brief 256-bit bitmap
*
* Bitmaps are used in QMdnsEngine::NSEC records to indicate which records are
* available. Bitmaps in mDNS records use only the first block (block 0).
*/
class QMDNSENGINE_EXPORT Bitmap
{
public:
/**
* @brief Create an empty bitmap
*/
Bitmap();
/**
* @brief Create a copy of an existing bitmap
*/
Bitmap(const Bitmap &other);
/**
* @brief Assignment operator
*/
Bitmap &operator=(const Bitmap &other);
/**
* @brief Equality operator
*/
bool operator==(const Bitmap &other);
/**
* @brief Destroy the bitmap
*/
virtual ~Bitmap();
/**
* @brief Retrieve the length of the block in bytes
*
* This method indicates how many bytes are pointed to by the data()
* method.
*/
quint8 length() const;
/**
* @brief Retrieve a pointer to the underlying data in the bitmap
*
* Use the length() method to determine how many bytes contain valid data.
*/
const quint8 *data() const;
/**
* @brief Set the data to be stored in the bitmap
*
* The length parameter indicates how many bytes of data are valid. The
* actual bytes are copied to the bitmap.
*/
void setData(quint8 length, const quint8 *data);
private:
BitmapPrivate *const d;
};
}
#endif // QMDNSENGINE_BITMAP_H

View File

@ -0,0 +1,122 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_BROWSER_H
#define QMDNSENGINE_BROWSER_H
#include <QByteArray>
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class AbstractServer;
class Cache;
class Service;
class QMDNSENGINE_EXPORT BrowserPrivate;
/**
* @brief %Browser for local services
*
* This class provides a simple way to discover services on the local network.
* A cache may be provided in the constructor to store records for future
* queries.
*
* To browse for services of any type:
*
* @code
* QMdnsEngine::Browser browser(&server, QMdnsEngine::MdnsBrowseType);
* @endcode
*
* To browse for services of a specific type:
*
* @code
* QMdnsEngine::Browser browser(&server, "_http._tcp.local.");
* @endcode
*
* When a service is found, the serviceAdded() signal is emitted:
*
* @code
* connect(&browser, &QMdnsEngine::Browser::serviceAdded, [](const QMdnsEngine::Service &service) {
* qDebug() << "Service added:" << service.name();
* });
* @endcode
*
* The serviceUpdated() and serviceRemoved() signals are emitted when services
* are updated (their properties change) or are removed, respectively.
*/
class QMDNSENGINE_EXPORT Browser : public QObject
{
Q_OBJECT
public:
/**
* @brief Create a new browser instance
* @param server server to use for receiving and sending mDNS messages
* @param type service type to browse for
* @param cache DNS cache to use or null to create one
* @param parent QObject
*/
Browser(AbstractServer *server, const QByteArray &type, Cache *cache = 0, QObject *parent = 0);
Q_SIGNALS:
/**
* @brief Indicate that a new service has been added
*
* This signal is emitted when the PTR and SRV records for a service are
* received. If TXT records are received later, the serviceUpdated()
* signal will be emitted.
*/
void serviceAdded(const Service &service);
/**
* @brief Indicate that the specified service was updated
*
* This signal is emitted when the SRV record for a service (identified by
* its name and type) or a TXT record has changed.
*/
void serviceUpdated(const Service &service);
/**
* @brief Indicate that the specified service was removed
*
* This signal is emitted when an essential record (PTR or SRV) is
* expiring from the cache. This will also occur when an updated PTR or
* SRV record is received with a TTL of 0.
*/
void serviceRemoved(const Service &service);
private:
BrowserPrivate *const d;
};
}
#endif // QMDNSENGINE_BROWSER_H

View File

@ -0,0 +1,132 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_CACHE_H
#define QMDNSENGINE_CACHE_H
#include <QList>
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class Record;
class QMDNSENGINE_EXPORT CachePrivate;
/**
* @brief %Cache for DNS records
*
* Records are added to the cache using the addRecord() method which are then
* stored in the cache until they are considered to have expired, at which
* point they are purged. The shouldQuery() signal is used to indicate when a
* record is approaching expiry and the recordExpired() signal indicates when
* a record has expired (at which point it is removed).
*
* The cache can be queried to retrieve one or more records matching a given
* type. For example, to retrieve all TXT records that match a given name:
*
* @code
* Cache cache;
*
* QList<QMdnsEngine::Record> records;
* cache.lookupRecords("My Service._http._tcp.local.", QMdnsEngine::TXT, records);
*
* for (const QMdnsEngine::Record &record : records) {
* qDebug() << "Record:" << record.name();
* }
* @endcode
*
* Alternatively, lookupRecord() can be used to find a single record.
*/
class QMDNSENGINE_EXPORT Cache : public QObject
{
Q_OBJECT
public:
/**
* @brief Create an empty cache.
*/
explicit Cache(QObject *parent = 0);
/**
* @brief Add a record to the cache
* @param record add this record to the cache
*
* The TTL for the record will be added to the current time to calculate
* when the record expires. Existing records of the same name and type
* will be replaced, resetting their expiration.
*/
void addRecord(const Record &record);
/**
* @brief Retrieve a single record from the cache
* @param name name of record to retrieve or null for any
* @param type type of record to retrieve or ANY for all types
* @param record storage for the record retrieved
* @return true if a record was retrieved
*
* Some record types allow multiple records to be stored with identical
* names and types. This method will only retrieve the first matching
* record. Use lookupRecords() to obtain all of the records.
*/
bool lookupRecord(const QByteArray &name, quint16 type, Record &record) const;
/**
* @brief Retrieve multiple records from the cache
* @param name name of records to retrieve or null for any
* @param type type of records to retrieve or ANY for all types
* @param records storage for the records retrieved
* @return true if records were retrieved
*/
bool lookupRecords(const QByteArray &name, quint16 type, QList<Record> &records) const;
Q_SIGNALS:
/**
* @brief Indicate that a record will expire soon and a new query should be issued
* @param record reference to the record that will soon expire
*
* This signal is emitted when a record reaches approximately 50%, 85%,
* 90%, and 95% of its lifetime.
*/
void shouldQuery(const Record &record);
/**
* @brief Indicate that the specified record expired
* @param record reference to the record that has expired
*/
void recordExpired(const Record &record);
private:
CachePrivate *const d;
};
}
#endif // QMDNSENGINE_CACHE_H

View File

@ -0,0 +1,123 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_DNS_H
#define QMDNSENGINE_DNS_H
#include <QByteArray>
#include <QMap>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class Message;
class Record;
enum {
/// IPv4 address record
A = 1,
/// IPv6 address record
AAAA = 28,
/// Wildcard for cache lookups
ANY = 255,
/// List of records
NSEC = 47,
/// Pointer to hostname
PTR = 12,
/// %Service information
SRV = 33,
/// Arbitrary metadata
TXT = 16
};
/**
* @brief Parse a name from a raw DNS packet
* @param packet raw DNS packet data
* @param offset offset into the packet where the name begins
* @param name reference to QByteArray to store the name in
* @return true if no errors occurred
*
* The offset will be incremented by the number of bytes read. Name
* compression requires access to the contents of the packet.
*/
QMDNSENGINE_EXPORT bool parseName(const QByteArray &packet, quint16 &offset, QByteArray &name);
/**
* @brief Write a name to a raw DNS packet
* @param packet raw DNS packet to write to
* @param offset offset to update with the number of bytes written
* @param name name to write to the packet
* @param nameMap map of names already written to their offsets
*
* The offset will be incremented by the number of bytes read. The name map
* will be updated with offsets of any names written so that it can be passed
* to future invocations of this function.
*/
QMDNSENGINE_EXPORT void writeName(QByteArray &packet, quint16 &offset, const QByteArray &name, QMap<QByteArray, quint16> &nameMap);
/**
* @brief Parse a record from a raw DNS packet
* @param packet raw DNS packet data
* @param offset offset into the packet where the record begins
* @param record reference to Record to populate
* @return true if no errors occurred
*/
QMDNSENGINE_EXPORT bool parseRecord(const QByteArray &packet, quint16 &offset, Record &record);
/**
* @brief Write a record to a raw DNS packet
* @param packet raw DNS packet to write to
* @param offset offset to update with the number of bytes written
* @param record record to write to the packet
* @param nameMap map of names already written to their offsets
*/
QMDNSENGINE_EXPORT void writeRecord(QByteArray &packet, quint16 &offset, Record &record, QMap<QByteArray, quint16> &nameMap);
/**
* @brief Populate a Message with data from a raw DNS packet
* @param packet raw DNS packet data
* @param message reference to Message to populate
* @return true if no errors occurred
*/
QMDNSENGINE_EXPORT bool fromPacket(const QByteArray &packet, Message &message);
/**
* @brief Create a raw DNS packet from a Message
* @param message Message to create the packet from
* @param packet storage for raw DNS packet
*/
QMDNSENGINE_EXPORT void toPacket(const Message &message, QByteArray &packet);
/**
* @brief Retrieve the string representation of a DNS type
* @param type integer type
* @return human-readable name for the type
*/
QMDNSENGINE_EXPORT QString typeName(quint16 type);
}
#endif // QMDNSENGINE_DNS_H

View File

@ -0,0 +1,95 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_HOSTNAME_H
#define QMDNSENGINE_HOSTNAME_H
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class AbstractServer;
class QMDNSENGINE_EXPORT HostnamePrivate;
/**
* @brief %Hostname reserved for exclusive use
*
* In order to provide services on the local network, a unique hostname must
* be used. This class asserts a hostname (by first confirming that it is not
* in use) and then responds to A and AAAA queries for the hostname.
*
* @code
* QMdnsEngine::Hostname hostname(&server);
*
* connect(&hostname, &QMdnsEngine::Hostname::hostnameChanged, [](const QByteArray &hostname) {
* qDebug() << "New hostname:" << hostname;
* });
* @endcode
*/
class QMDNSENGINE_EXPORT Hostname : public QObject
{
Q_OBJECT
public:
/**
* @brief Create a new hostname
*/
Hostname(AbstractServer *server, QObject *parent = 0);
/**
* @brief Determine if a hostname has been registered
*
* A hostname is not considered registered until a probe for the desired
* name has been completed and no matching records were received.
*/
bool isRegistered() const;
/**
* @brief Retrieve the current hostname
*
* This value is only valid when isRegistered() returns true.
*/
QByteArray hostname() const;
Q_SIGNALS:
/**
* @brief Indicate that the current hostname has changed
* @param hostname new hostname
*/
void hostnameChanged(const QByteArray &hostname);
private:
HostnamePrivate *const d;
};
}
#endif // QMDNSENGINE_HOSTNAME_H

View File

@ -0,0 +1,58 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_MDNS_H
#define QMDNSENGINE_MDNS_H
#include <QByteArray>
#include <QHostAddress>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
/**
* @brief Standard port for mDNS
*/
QMDNSENGINE_EXPORT extern const quint16 MdnsPort;
/**
* @brief Standard IPv4 address for mDNS
*/
QMDNSENGINE_EXPORT extern const QHostAddress MdnsIpv4Address;
/**
* @brief Standard IPv6 address for mDNS
*/
QMDNSENGINE_EXPORT extern const QHostAddress MdnsIpv6Address;
/**
* @brief Service type for browsing service types
*/
QMDNSENGINE_EXPORT extern const QByteArray MdnsBrowseType;
}
#endif // QMDNSENGINE_MDNS_H

View File

@ -0,0 +1,191 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_MESSAGE_H
#define QMDNSENGINE_MESSAGE_H
#include <QHostAddress>
#include <QList>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class Query;
class Record;
class QMDNSENGINE_EXPORT MessagePrivate;
/**
* @brief DNS message
*
* A DNS message consists of a header and zero or more queries and records.
* Instances of this class are created and initialized by
* [AbstractServer](@ref QMdnsEngine::AbstractServer) when messages are
* received from the network.
*
* If a message is being constructed in reply to one received from the
* network, the reply() method can be used to simplify initialization:
*
* @code
* connect(&server, &QMdnsEngine::Server::messageReceived, [](const QMdnsEngine::Message &message) {
* QMdnsEngine::Message reply;
* reply.reply(message);
* server.sendMessage(reply);
* });
* @endcode
*/
class QMDNSENGINE_EXPORT Message
{
public:
/**
* @brief Create an empty message
*/
Message();
/**
* @brief Create a copy of an existing message
*/
Message(const Message &other);
/**
* @brief Assignment operator
*/
Message &operator=(const Message &other);
/**
* @brief Destroy the message
*/
virtual ~Message();
/**
* @brief Retrieve the address for the message
*
* When receiving messages, this is the address that the message was
* received from.
*/
QHostAddress address() const;
/**
* @brief Set the address for the message
*
* When sending messages, this is the address that the message will be
* sent to. QMdnsEngine::MdnsIpv4Address and QMdnsEngine::MdnsIpv6Address
* can be used for mDNS messages.
*/
void setAddress(const QHostAddress &address);
/**
* @brief Retrieve the port for the message
*
* When receiving messages, this is the port that the message was received
* from. For traditional queries, this will be an ephemeral port. For mDNS
* queries, this will always equal QMdnsEngine::MdnsPort.
*/
quint16 port() const;
/**
* @brief Set the port for the message
*
* When sending messages, this is the port that the message will be sent
* to. This should be set to QMdnsEngine::MdnsPort unless the message is a
* reply to a traditional DNS query.
*/
void setPort(quint16 port);
/**
* @brief Retrieve the transaction ID for the message
*
* This is always set to 1 for mDNS messages. Traditional DNS queries may
* set this to an arbitrary integer.
*/
quint16 transactionId() const;
/**
* @brief Set the transaction ID for the message
*
* The default transaction ID is 0. This value should not be changed
* unless responding to a traditional DNS query.
*/
void setTransactionId(quint16 transactionId);
/**
* @brief Determine if the message is a response
*/
bool isResponse() const;
/**
* @brief Set whether the message is a response
*/
void setResponse(bool isResponse);
/**
* @brief Determine if the message is truncated
*/
bool isTruncated() const;
/**
* @brief Set whether the message is truncated
*/
void setTruncated(bool isTruncated);
/**
* @brief Retrieve a list of queries in the message
*/
QList<Query> queries() const;
/**
* @brief Add a query to the message
*/
void addQuery(const Query &query);
/**
* @brief Retrieve a list of records in the message
*/
QList<Record> records() const;
/**
* @brief Add a record to the message
*/
void addRecord(const Record &record);
/**
* @brief Reply to another message
*
* The message will be correctly initialized to respond to the other
* message. This includes setting the target address, port, and
* transaction ID.
*/
void reply(const Message &other);
private:
MessagePrivate *const d;
};
}
#endif // QMDNSENGINE_MESSAGE_H

View File

@ -0,0 +1,88 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_PROBER_H
#define QMDNSENGINE_PROBER_H
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class AbstractServer;
class Record;
class QMDNSENGINE_EXPORT ProberPrivate;
/**
* @brief %Prober to confirm that a record is unique
*
* Before responding to queries for a record, its uniqueness on the network
* must be confirmed. This class takes care of probing for existing records
* that match and adjusts the record's name until a unique one is found.
*
* For example, to probe for a SRV record:
*
* @code
* QMdnsEngine::Record record;
* record.setName("My Service._http._tcp.local.");
* record.setType(QMdnsEngine::SRV);
* record.setPort(1234);
* record.setTarget(hostname.hostname());
*
* QMdnsEngine::Prober prober(&server, record);
* connect(&prober, &QMdnsEngine::Prober::nameConfirmed, [](const QByteArray &name) {
* qDebug() << "Name confirmed:" << name;
* });
* @endcode
*/
class QMDNSENGINE_EXPORT Prober : public QObject
{
Q_OBJECT
public:
/**
* @brief Create a new prober
*/
Prober(AbstractServer *server, const Record &record, QObject *parent = 0);
Q_SIGNALS:
/**
* @brief Indicate that the name has been confirmed unique
* @param name that was confirmed to be unique
*/
void nameConfirmed(const QByteArray &name);
private:
ProberPrivate *const d;
};
}
#endif // QMDNSENGINE_PROBER_H

View File

@ -0,0 +1,90 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_PROVIDER_H
#define QMDNSENGINE_PROVIDER_H
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class AbstractServer;
class Hostname;
class Service;
class QMDNSENGINE_EXPORT ProviderPrivate;
/**
* @brief %Provider for a single mDNS service
*
* This class provide a [Service](@ref QMdnsEngine::Service) on the local
* network by responding to the appropriate DNS queries. A hostname is
* required for creating the SRV record.
*
* The provider needs to be given a reference to the service through the
* update() method so that it can construct DNS records:
*
* @code
* QMdnsEngine::Service service;
* service.setType("_http._tcp.local.");
* service.setName("My Service");
* service.setPort(1234);
*
* QMdnsEngine::Provider provider;
* provider.update(service);
* @endcode
*
* This method can also be used to update the provider's records.
*/
class QMDNSENGINE_EXPORT Provider : public QObject
{
Q_OBJECT
public:
/**
* @brief Create a new service provider
*/
Provider(AbstractServer *server, Hostname *hostname, QObject *parent = 0);
/**
* @brief Update the service with the provided information
* @param service updated service description
*
* This class will not respond to any DNS queries until the hostname is
* confirmed and this method is called.
*/
void update(const Service &service);
private:
ProviderPrivate *const d;
};
}
#endif // QMDNSENGINE_PROVIDER_H

View File

@ -0,0 +1,40 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_EXPORT_H
#define QMDNSENGINE_EXPORT_H
#include <QtCore/qglobal.h>
#if defined(BUILD_SHARED_LIBS)
# if defined(QMDNSENGINE_LIBRARY)
# define QMDNSENGINE_EXPORT Q_DECL_EXPORT
# else
# define QMDNSENGINE_EXPORT Q_DECL_IMPORT
# endif
#else
# define QMDNSENGINE_EXPORT
#endif
#endif // QMDNSENGINE_EXPORT_H

View File

@ -0,0 +1,116 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_QUERY_H
#define QMDNSENGINE_QUERY_H
#include <QByteArray>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class QMDNSENGINE_EXPORT QueryPrivate;
/**
* @brief DNS query
*
* This class represents a query for a DNS record. For example, to query for
* the IPv4 address of a local host:
*
* @code
* QMdnsEngine::Query query;
* query.setName("myserver.local.");
* query.setType(QMdnsEngine::A);
*
* message.addQuery(query);
* @endcode
*/
class QMDNSENGINE_EXPORT Query
{
public:
/**
* @brief Create an empty query
*/
Query();
/**
* @brief Create a copy of an existing query
*/
Query(const Query &other);
/**
* @brief Assignment operator
*/
Query &operator=(const Query &other);
/**
* @brief Destroy the query
*/
virtual ~Query();
/**
* @brief Retrieve the name being queried
*/
QByteArray name() const;
/**
* @brief Set the name to query
*/
void setName(const QByteArray &name);
/**
* @brief Retrieve the type of record being queried
*/
quint16 type() const;
/**
* @brief Set the type of record to query
*
* Constants, such as QMdnsEngine::SRV are provided for convenience.
*/
void setType(quint16 type);
/**
* @brief Determine if a unicast response is desired
*/
bool unicastResponse() const;
/**
* @brief Set whether a unicast response is desired
*/
void setUnicastResponse(bool unicastResponse);
private:
QueryPrivate *const d;
};
QMDNSENGINE_EXPORT QDebug operator<<(QDebug dbg, const Query &query);
}
#endif // QMDNSENGINE_QUERY_H

View File

@ -0,0 +1,255 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_RECORD_H
#define QMDNSENGINE_RECORD_H
#include <QByteArray>
#include <QHostAddress>
#include <QMap>
#include "bitmap.h"
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class QMDNSENGINE_EXPORT RecordPrivate;
/**
* @brief DNS record
*
* This class maintains information for an individual record. Not all record
* types use every field.
*
* For example, to create a TXT record:
*
* @code
* QMdnsEngine::Record record;
* record.setName("My Service._http._tcp.local.");
* record.setType(QMdnsEngine::TXT);
* record.addAttribute("a", "value1");
* record.addAttribute("b", "value2");
*
* message.addRecord(record);
* @endcode
*/
class QMDNSENGINE_EXPORT Record
{
public:
/**
* @brief Create an uninitialized record
*/
Record();
/**
* @brief Create a copy of an existing record
*/
Record(const Record &other);
/**
* @brief Assignment operator
*/
Record &operator=(const Record &other);
/**
* @brief Equality operator
*/
bool operator==(const Record &other) const;
/**
* @brief Inequality operator
*/
bool operator!=(const Record &other) const;
/**
* @brief Destroy the record
*/
virtual ~Record();
/**
* @brief Retrieve the name of the record
*/
QByteArray name() const;
/**
* @brief Set the name of the record
*/
void setName(const QByteArray &name);
/**
* @brief Retrieve the type of the record
*/
quint16 type() const;
/**
* @brief Set the type of the record
*
* For convenience, constants for types used by mDNS, such as
* QMdnsEngine::A or QMdnsEngine::PTR, may be used here.
*/
void setType(quint16 type);
/**
* @brief Determine whether to replace or append to existing records
*
* If true, this record replaces all previous records of the same name and
* type rather than appending to them.
*/
bool flushCache() const;
/**
* @brief Set whether to replace or append to existing records
*/
void setFlushCache(bool flushCache);
/**
* @brief Retrieve the TTL (time to live) for the record
*/
quint32 ttl() const;
/**
* @brief Set the TTL (time to live) for the record
*/
void setTtl(quint32 ttl);
/**
* @brief Retrieve the address for the record
*
* This field is used by QMdnsEngine::A and QMdnsEngine::AAAA records.
*/
QHostAddress address() const;
/**
* @brief Set the address for the record
*/
void setAddress(const QHostAddress &address);
/**
* @brief Retrieve the target for the record
*
* This field is used by QMdnsEngine::PTR and QMdnsEngine::SRV records.
*/
QByteArray target() const;
/**
* @brief Set the target for the record
*/
void setTarget(const QByteArray &target);
/**
* @brief Retrieve the next domain name
*
* This field is used by QMdnsEngine::NSEC records.
*/
QByteArray nextDomainName() const;
/**
* @brief Set the next domain name
*/
void setNextDomainName(const QByteArray &nextDomainName);
/**
* @brief Retrieve the priority for the record
*
* This field is used by QMdnsEngine::SRV records.
*/
quint16 priority() const;
/**
* @brief Set the priority for the record
*
* Unless more than one QMdnsEngine::SRV record is being sent, this field
* should be set to 0.
*/
void setPriority(quint16 priority);
/**
* @brief Retrieve the weight of the record
*
* This field is used by QMdnsEngine::SRV records.
*/
quint16 weight() const;
/**
* @brief Set the weight of the record
*
* Unless more than one QMdnsEngine::SRV record is being sent, this field
* should be set to 0.
*/
void setWeight(quint16 weight);
/**
* @brief Retrieve the port for the record
*
* This field is used by QMdnsEngine::SRV records.
*/
quint16 port() const;
/**
* @brief Set the port for the record
*/
void setPort(quint16 port);
/**
* @brief Retrieve attributes for the record
*
* This field is used by QMdnsEngine::TXT records.
*/
QMap<QByteArray, QByteArray> attributes() const;
/**
* @brief Set attributes for the record
*/
void setAttributes(const QMap<QByteArray, QByteArray> &attributes);
/**
* @brief Add an attribute to the record
*/
void addAttribute(const QByteArray &key, const QByteArray &value);
/**
* @brief Retrieve the bitmap for the record
*
* This field is used by QMdnsEngine::NSEC records.
*/
Bitmap bitmap() const;
/**
* @brief Set the bitmap for the record
*/
void setBitmap(const Bitmap &bitmap);
private:
RecordPrivate *const d;
};
QMDNSENGINE_EXPORT QDebug operator<<(QDebug dbg, const Record &record);
}
#endif // QMDNSENGINE_RECORD_H

View File

@ -0,0 +1,88 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_RESOLVER_H
#define QMDNSENGINE_RESOLVER_H
#include <QHostAddress>
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class AbstractServer;
class Cache;
class QMDNSENGINE_EXPORT ResolverPrivate;
/**
* @brief %Resolver for services
*
* When [Browser](@ref QMdnsEngine::Browser) indicates that a new service has
* been found, it becomes necessary to resolve the service in order to connect
* to it. This class serves that role. A [Cache](@ref QMdnsEngine::Cache) can
* optionally be provided to speed up the resolving process.
*
* For example, assuming that `record` is a SRV record:
*
* @code
* QMdnsEngine::Resolver resolver(&server, record.target());
* connect(&resolver, &QMdnsEngine::Resolver::resolved, [](const QHostAddress &address) {
* qDebug() << "Address:" << address;
* });
* @endcode
*/
class QMDNSENGINE_EXPORT Resolver : public QObject
{
Q_OBJECT
public:
/**
* @brief Create a new resolver
*/
Resolver(AbstractServer *server, const QByteArray &name, Cache *cache = 0, QObject *parent = 0);
Q_SIGNALS:
/**
* @brief Indicate that the host resolved to an address
* @param address service address
*
* This signal will be emitted once for each resolved address. For
* example, if a host provides both A and AAAA records, this signal will
* be emitted twice.
*/
void resolved(const QHostAddress &address);
private:
ResolverPrivate *const d;
};
}
#endif // QMDNSENGINE_RESOLVER_H

View File

@ -0,0 +1,78 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_SERVER_H
#define QMDNSENGINE_SERVER_H
#include "abstractserver.h"
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class Message;
class QMDNSENGINE_EXPORT ServerPrivate;
/**
* @brief mDNS server
*
* This class provides an implementation of
* [AbstractServer](@ref QMdnsEngine::AbstractServer) that uses all available
* local network adapters to send and receive mDNS messages.
*
* The class takes care of watching for the addition and removal of network
* interfaces, automatically joining multicast groups when new interfaces are
* available.
*/
class QMDNSENGINE_EXPORT Server : public AbstractServer
{
Q_OBJECT
public:
/**
* @brief Create a new server
*/
explicit Server(QObject *parent = 0);
/**
* @brief Implementation of AbstractServer::sendMessage()
*/
virtual void sendMessage(const Message &message);
/**
* @brief Implementation of AbstractServer::sendMessageToAll()
*/
virtual void sendMessageToAll(const Message &message);
private:
ServerPrivate *const d;
};
}
#endif // QMDNSENGINE_SERVER_H

View File

@ -0,0 +1,156 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_SERVICE_H
#define QMDNSENGINE_SERVICE_H
#include <QByteArray>
#include <QHostAddress>
#include <QList>
#include <QMap>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class QMDNSENGINE_EXPORT ServicePrivate;
/**
* @brief %Service available on the local network
*
* This class contains the descriptive information necessary to represent an
* individual service made available to the local network. Instances are
* provided by [Browser](@ref QMdnsEngine::Browser) as services are
* discovered. Instances must be created and passed to
* [Provider::update()](@ref QMdnsEngine::Provider::update) to provide a
* service.
*/
class QMDNSENGINE_EXPORT Service
{
public:
/**
* @brief Create an uninitialized service
*/
Service();
/**
* @brief Create a copy of an existing service
*/
Service(const Service &other);
/**
* @brief Assignment operator
*/
Service &operator=(const Service &other);
/**
* @brief Equality operator
*/
bool operator==(const Service &other) const;
/**
* @brief Inequality operator
*/
bool operator!=(const Service &other) const;
/**
* @brief Destroy the service
*/
virtual ~Service();
/**
* @brief Retrieve the service type
*/
QByteArray type() const;
/**
* @brief Set the service type
*
* For example, an HTTP service might use "_http._tcp".
*/
void setType(const QByteArray &type);
/**
* @brief Retrieve the service name
*/
QByteArray name() const;
/**
* @brief Set the service name
*
* This is combined with the service type and domain to form the FQDN for
* the service.
*/
void setName(const QByteArray &name);
/**
* @brief Retrieve the hostname of the device providing the service
*/
QByteArray hostname() const;
/**
* @brief Set the hostname of the device providing the service
*/
void setHostname(const QByteArray &hostname);
/**
* @brief Retrieve the service port
*/
quint16 port() const;
/**
* @brief Set the service port
*/
void setPort(quint16 port);
/**
* @brief Retrieve the attributes for the service
*
* Boolean attributes will have null values (invoking QByteArray::isNull()
* on the value will return true).
*/
QMap<QByteArray, QByteArray> attributes() const;
/**
* @brief Set the attributes for the service
*/
void setAttributes(const QMap<QByteArray, QByteArray> &attributes);
/**
* @brief Add an attribute to the service
*/
void addAttribute(const QByteArray &key, const QByteArray &value);
private:
ServicePrivate *const d;
};
QMDNSENGINE_EXPORT QDebug operator<<(QDebug debug, const Service &service);
}
#endif // QMDNSENGINE_SERVICE_H

View File

@ -0,0 +1,40 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_EXPORT_H
#define QMDNSENGINE_EXPORT_H
#include <QtCore/qglobal.h>
#if defined(BUILD_SHARED_LIBS)
# if defined(QMDNSENGINE_LIBRARY)
# define QMDNSENGINE_EXPORT Q_DECL_EXPORT
# else
# define QMDNSENGINE_EXPORT Q_DECL_IMPORT
# endif
#else
# define QMDNSENGINE_EXPORT
#endif
#endif // QMDNSENGINE_EXPORT_H

View File

@ -0,0 +1,32 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include "../include/abstractserver.h"
using namespace QMdnsEngine;
AbstractServer::AbstractServer(QObject *parent)
: QObject(parent)
{
}

View File

@ -0,0 +1,108 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include "../include/bitmap.h"
#include "bitmap_p.h"
using namespace QMdnsEngine;
BitmapPrivate::BitmapPrivate()
: length(0),
data(nullptr)
{
}
BitmapPrivate::~BitmapPrivate()
{
free();
}
void BitmapPrivate::free()
{
if (data) {
delete[] data;
}
}
void BitmapPrivate::fromData(quint8 newLength, const quint8 *newData)
{
data = new quint8[newLength];
for (int i = 0; i < newLength; ++i) {
data[i] = newData[i];
}
length = newLength;
}
Bitmap::Bitmap()
: d(new BitmapPrivate)
{
}
Bitmap::Bitmap(const Bitmap &other)
: d(new BitmapPrivate)
{
d->fromData(other.d->length, other.d->data);
}
Bitmap &Bitmap::operator=(const Bitmap &other)
{
d->free();
d->fromData(other.d->length, other.d->data);
return *this;
}
bool Bitmap::operator==(const Bitmap &other)
{
if (d->length != other.d->length) {
return false;
}
for (int i = 0; i < d->length; ++i) {
if (d->data[i] != other.d->data[i]) {
return false;
}
}
return true;
}
Bitmap::~Bitmap()
{
delete d;
}
quint8 Bitmap::length() const
{
return d->length;
}
const quint8 *Bitmap::data() const
{
return d->data;
}
void Bitmap::setData(quint8 length, const quint8 *data)
{
d->free();
d->fromData(length, data);
}

View File

@ -0,0 +1,49 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_BITMAP_P_H
#define QMDNSENGINE_BITMAP_P_H
#include <QtGlobal>
namespace QMdnsEngine
{
class BitmapPrivate
{
public:
BitmapPrivate();
virtual ~BitmapPrivate();
void free();
void fromData(quint8 newLength, const quint8 *newData);
quint8 length;
quint8 *data;
};
}
#endif // QMDNSENGINE_BITMAP_P_H

View File

@ -0,0 +1,291 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include "../include/abstractserver.h"
#include "../include/browser.h"
#include "../include/cache.h"
#include "../include/dns.h"
#include "../include/mdns.h"
#include "../include/message.h"
#include "../include/query.h"
#include "../include/record.h"
#include "browser_p.h"
using namespace QMdnsEngine;
BrowserPrivate::BrowserPrivate(Browser *browser, AbstractServer *server, const QByteArray &type, Cache *existingCache)
: QObject(browser),
server(server),
type(type),
cache(existingCache ? existingCache : new Cache(this)),
q(browser)
{
connect(server, &AbstractServer::messageReceived, this, &BrowserPrivate::onMessageReceived);
connect(cache, &Cache::shouldQuery, this, &BrowserPrivate::onShouldQuery);
connect(cache, &Cache::recordExpired, this, &BrowserPrivate::onRecordExpired);
connect(&queryTimer, &QTimer::timeout, this, &BrowserPrivate::onQueryTimeout);
connect(&serviceTimer, &QTimer::timeout, this, &BrowserPrivate::onServiceTimeout);
queryTimer.setInterval(60 * 1000);
queryTimer.setSingleShot(true);
serviceTimer.setInterval(100);
serviceTimer.setSingleShot(true);
// Immediately begin browsing for services
onQueryTimeout();
}
// TODO: multiple SRV records not supported
bool BrowserPrivate::updateService(const QByteArray &fqName)
{
// Split the FQDN into service name and type
int index = fqName.indexOf('.');
QByteArray serviceName = fqName.left(index);
QByteArray serviceType = fqName.mid(index + 1);
// Immediately return if a PTR record does not exist
Record ptrRecord;
if (!cache->lookupRecord(serviceType, PTR, ptrRecord)) {
return false;
}
// If a SRV record is missing, query for it (by returning true)
Record srvRecord;
if (!cache->lookupRecord(fqName, SRV, srvRecord)) {
return true;
}
Service service;
service.setName(serviceName);
service.setType(serviceType);
service.setHostname(srvRecord.target());
service.setPort(srvRecord.port());
// If TXT records are available for the service, add their values
QList<Record> txtRecords;
if (cache->lookupRecords(fqName, TXT, txtRecords)) {
QMap<QByteArray, QByteArray> attributes;
for (const Record &record : qAsConst(txtRecords)) {
for (auto i = record.attributes().constBegin();
i != record.attributes().constEnd(); ++i) {
attributes.insert(i.key(), i.value());
}
}
service.setAttributes(attributes);
}
// If the service existed, this is an update; otherwise it is a new
// addition; emit the appropriate signal
if (!services.contains(fqName)) {
emit q->serviceAdded(service);
} else if(services.value(fqName) != service) {
emit q->serviceUpdated(service);
}
services.insert(fqName, service);
hostnames.insert(service.hostname());
return false;
}
void BrowserPrivate::onMessageReceived(const Message &message)
{
if (!message.isResponse()) {
return;
}
const bool any = type == MdnsBrowseType;
// Use a set to track all services that are updated in the message to
// prevent unnecessary queries for SRV and TXT records
QSet<QByteArray> updateNames;
const auto records = message.records();
for (const Record &record : records) {
bool cacheRecord = false;
switch (record.type()) {
case PTR:
if (any && record.name() == MdnsBrowseType) {
ptrTargets.insert(record.target());
serviceTimer.start();
cacheRecord = true;
} else if (any || record.name() == type) {
updateNames.insert(record.target());
cacheRecord = true;
}
break;
case SRV:
case TXT:
if (any || record.name().endsWith("." + type)) {
updateNames.insert(record.name());
cacheRecord = true;
}
break;
}
if (cacheRecord) {
cache->addRecord(record);
}
}
// For each of the services marked to be updated, perform the update and
// make a list of all missing SRV records
QSet<QByteArray> queryNames;
for (const QByteArray &name : qAsConst(updateNames)) {
if (updateService(name)) {
queryNames.insert(name);
}
}
// Cache A / AAAA records after services are processed to ensure hostnames are known
for (const Record &record : records) {
bool cacheRecord = false;
switch (record.type()) {
case A:
case AAAA:
cacheRecord = hostnames.contains(record.name());
break;
}
if (cacheRecord) {
cache->addRecord(record);
}
}
// Build and send a query for all of the SRV and TXT records
if (queryNames.count()) {
Message queryMessage;
for (const QByteArray &name : qAsConst(queryNames)) {
Query query;
query.setName(name);
query.setType(SRV);
queryMessage.addQuery(query);
query.setType(TXT);
queryMessage.addQuery(query);
}
server->sendMessageToAll(queryMessage);
}
}
void BrowserPrivate::onShouldQuery(const Record &record)
{
// Assume that all messages in the cache are still in use (by the browser)
// and attempt to renew them immediately
Query query;
query.setName(record.name());
query.setType(record.type());
Message message;
message.addQuery(query);
server->sendMessageToAll(message);
}
void BrowserPrivate::onRecordExpired(const Record &record)
{
// If the SRV record has expired for a service, then it must be
// removed - TXT records on the other hand, cause an update
QByteArray serviceName;
switch (record.type()) {
case SRV:
serviceName = record.name();
break;
case TXT:
updateService(record.name());
return;
default:
return;
}
Service service = services.value(serviceName);
if (!service.name().isNull()) {
emit q->serviceRemoved(service);
services.remove(serviceName);
updateHostnames();
}
}
void BrowserPrivate::onQueryTimeout()
{
Query query;
query.setName(type);
query.setType(PTR);
Message message;
message.addQuery(query);
// TODO: including too many records could cause problems
// Include PTR records for the target that are already known
QList<Record> records;
if (cache->lookupRecords(query.name(), PTR, records)) {
for (const Record &record : qAsConst(records)) {
message.addRecord(record);
}
}
server->sendMessageToAll(message);
queryTimer.start();
}
void BrowserPrivate::onServiceTimeout()
{
if (ptrTargets.count()) {
Message message;
for (const QByteArray &target : qAsConst(ptrTargets)) {
// Add a query for PTR records
Query query;
query.setName(target);
query.setType(PTR);
message.addQuery(query);
// Include PTR records for the target that are already known
QList<Record> records;
if (cache->lookupRecords(target, PTR, records)) {
for (const Record &record : qAsConst(records)) {
message.addRecord(record);
}
}
}
server->sendMessageToAll(message);
ptrTargets.clear();
}
}
void BrowserPrivate::updateHostnames()
{
hostnames.clear();
for (const auto& service : services) {
hostnames.insert(service.hostname());
}
}
Browser::Browser(AbstractServer *server, const QByteArray &type, Cache *cache, QObject *parent)
: QObject(parent),
d(new BrowserPrivate(this, server, type, cache))
{
}

View File

@ -0,0 +1,83 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_BROWSER_P_H
#define QMDNSENGINE_BROWSER_P_H
#include <QByteArray>
#include <QMap>
#include <QObject>
#include <QSet>
#include <QTimer>
#include "../include/service.h"
namespace QMdnsEngine
{
class AbstractServer;
class Browser;
class Cache;
class Message;
class Record;
class BrowserPrivate : public QObject
{
Q_OBJECT
public:
explicit BrowserPrivate(Browser *browser, AbstractServer *server, const QByteArray &type, Cache *existingCache);
bool updateService(const QByteArray &fqName);
AbstractServer *server;
QByteArray type;
Cache *cache;
QSet<QByteArray> ptrTargets;
QMap<QByteArray, Service> services;
QSet<QByteArray> hostnames;
QTimer queryTimer;
QTimer serviceTimer;
private Q_SLOTS:
void onMessageReceived(const Message &message);
void onShouldQuery(const Record &record);
void onRecordExpired(const Record &record);
void onQueryTimeout();
void onServiceTimeout();
private:
void updateHostnames();
Browser *const q;
};
}
#endif // QMDNSENGINE_BROWSER_P_H

View File

@ -0,0 +1,173 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include <QtGlobal>
#if(QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
#include <QRandomGenerator>
#define USE_QRANDOMGENERATOR
#endif
#include "../include/cache.h"
#include "../include/dns.h"
#include "cache_p.h"
using namespace QMdnsEngine;
CachePrivate::CachePrivate(Cache *cache)
: QObject(cache),
q(cache)
{
connect(&timer, &QTimer::timeout, this, &CachePrivate::onTimeout);
timer.setSingleShot(true);
}
void CachePrivate::onTimeout()
{
// Loop through all of the records in the cache, emitting the appropriate
// signal when a trigger has passed, determining when the next trigger
// will occur, and removing records that have expired
QDateTime now = QDateTime::currentDateTime();
QDateTime newNextTrigger;
for (auto i = entries.begin(); i != entries.end();) {
// Loop through the triggers and remove ones that have already
// passed
bool shouldQuery = false;
for (auto j = i->triggers.begin(); j != i->triggers.end();) {
if ((*j) <= now) {
shouldQuery = true;
j = i->triggers.erase(j);
} else {
break;
}
}
// If triggers remain, determine the next earliest one; if none
// remain, the record has expired and should be removed
if (i->triggers.length()) {
if (newNextTrigger.isNull() || i->triggers.at(0) < newNextTrigger) {
newNextTrigger = i->triggers.at(0);
}
if (shouldQuery) {
emit q->shouldQuery(i->record);
}
++i;
} else {
emit q->recordExpired(i->record);
i = entries.erase(i);
}
}
// If newNextTrigger contains a value, it will be the time for the next
// trigger and the timer should be started again
nextTrigger = newNextTrigger;
if (!nextTrigger.isNull()) {
timer.start(now.msecsTo(nextTrigger));
}
}
Cache::Cache(QObject *parent)
: QObject(parent),
d(new CachePrivate(this))
{
}
void Cache::addRecord(const Record &record)
{
// If a record exists that matches, remove it from the cache; if the TTL
// is nonzero, it will be added back to the cache with updated times
for (auto i = d->entries.begin(); i != d->entries.end();) {
if ((record.flushCache() &&
(*i).record.name() == record.name() &&
(*i).record.type() == record.type()) ||
(*i).record == record) {
// If the TTL is set to 0, indicate that the record was removed
if (record.ttl() == 0) {
emit recordExpired((*i).record);
}
i = d->entries.erase(i);
// No need to continue further if the TTL was set to 0
if (record.ttl() == 0) {
return;
}
} else {
++i;
}
}
// Use the current time to calculate the triggers and add a random offset
QDateTime now = QDateTime::currentDateTime();
#ifdef USE_QRANDOMGENERATOR
qint64 random = QRandomGenerator::global()->bounded(20);
#else
qint64 random = qrand() % 20;
#endif
QList<QDateTime> triggers{
now.addMSecs(record.ttl() * 500 + random), // 50%
now.addMSecs(record.ttl() * 850 + random), // 85%
now.addMSecs(record.ttl() * 900 + random), // 90%
now.addMSecs(record.ttl() * 950 + random), // 95%
now.addSecs(record.ttl())
};
// Append the record and its triggers
d->entries.append({record, triggers});
// Check if the new record's first trigger is earlier than the next
// scheduled trigger; if so, restart the timer
if (d->nextTrigger.isNull() || triggers.at(0) < d->nextTrigger) {
d->nextTrigger = triggers.at(0);
d->timer.start(now.msecsTo(d->nextTrigger));
}
}
bool Cache::lookupRecord(const QByteArray &name, quint16 type, Record &record) const
{
QList<Record> records;
if (lookupRecords(name, type, records)) {
record = records.at(0);
return true;
}
return false;
}
bool Cache::lookupRecords(const QByteArray &name, quint16 type, QList<Record> &records) const
{
bool recordsAdded = false;
for (const CachePrivate::Entry &entry : d->entries) {
if ((name.isNull() || entry.record.name() == name) &&
(type == ANY || entry.record.type() == type)) {
records.append(entry.record);
recordsAdded = true;
}
}
return recordsAdded;
}

View File

@ -0,0 +1,69 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_CACHE_P_H
#define QMDNSENGINE_CACHE_P_H
#include <QDateTime>
#include <QList>
#include <QObject>
#include <QTimer>
#include "../include/record.h"
namespace QMdnsEngine
{
class Cache;
class CachePrivate : public QObject
{
Q_OBJECT
public:
struct Entry
{
Record record;
QList<QDateTime> triggers;
};
CachePrivate(Cache *cache);
QTimer timer;
QList<Entry> entries;
QDateTime nextTrigger;
private Q_SLOTS:
void onTimeout();
private:
Cache *const q;
};
}
#endif // QMDNSENGINE_CACHE_P_H

View File

@ -0,0 +1,376 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include <QHostAddress>
#include <QtEndian>
#include "../include/bitmap.h"
#include "../include/dns.h"
#include "../include/message.h"
#include "../include/query.h"
#include "../include/record.h"
namespace QMdnsEngine
{
template<class T>
bool parseInteger(const QByteArray &packet, quint16 &offset, T &value)
{
if (offset + sizeof(T) > static_cast<unsigned int>(packet.length())) {
return false; // out-of-bounds
}
value = qFromBigEndian<T>(reinterpret_cast<const uchar*>(packet.constData() + offset));
offset += sizeof(T);
return true;
}
template<class T>
void writeInteger(QByteArray &packet, quint16 &offset, T value)
{
value = qToBigEndian<T>(value);
packet.append(reinterpret_cast<const char*>(&value), sizeof(T));
offset += sizeof(T);
}
bool parseName(const QByteArray &packet, quint16 &offset, QByteArray &name)
{
quint16 offsetEnd = 0;
quint16 offsetPtr = offset;
forever {
quint8 nBytes;
if (!parseInteger<quint8>(packet, offset, nBytes)) {
return false;
}
if (!nBytes) {
break;
}
switch (nBytes & 0xc0) {
case 0x00:
if (offset + nBytes > packet.length()) {
return false; // length exceeds message
}
name.append(packet.mid(offset, nBytes));
name.append('.');
offset += nBytes;
break;
case 0xc0:
{
quint8 nBytes2;
quint16 newOffset;
if (!parseInteger<quint8>(packet, offset, nBytes2)) {
return false;
}
newOffset = ((nBytes & ~0xc0) << 8) | nBytes2;
if (newOffset >= offsetPtr) {
return false; // prevent infinite loop
}
offsetPtr = newOffset;
if (!offsetEnd) {
offsetEnd = offset;
}
offset = newOffset;
break;
}
default:
return false; // no other types supported
}
}
if (offsetEnd) {
offset = offsetEnd;
}
return true;
}
void writeName(QByteArray &packet, quint16 &offset, const QByteArray &name, QMap<QByteArray, quint16> &nameMap)
{
QByteArray fragment = name;
if (fragment.endsWith('.')) {
fragment.chop(1);
}
while (fragment.length()) {
if (nameMap.contains(fragment)) {
writeInteger<quint16>(packet, offset, nameMap.value(fragment) | 0xc000);
return;
}
nameMap.insert(fragment, offset);
int index = fragment.indexOf('.');
if (index == -1) {
index = fragment.length();
}
writeInteger<quint8>(packet, offset, index);
packet.append(fragment.left(index));
offset += index;
fragment.remove(0, index + 1);
}
writeInteger<quint8>(packet, offset, 0);
}
bool parseRecord(const QByteArray &packet, quint16 &offset, Record &record)
{
QByteArray name;
quint16 type, class_, dataLen;
quint32 ttl;
if (!parseName(packet, offset, name) ||
!parseInteger<quint16>(packet, offset, type) ||
!parseInteger<quint16>(packet, offset, class_) ||
!parseInteger<quint32>(packet, offset, ttl) ||
!parseInteger<quint16>(packet, offset, dataLen)) {
return false;
}
record.setName(name);
record.setType(type);
record.setFlushCache(class_ & 0x8000);
record.setTtl(ttl);
switch (type) {
case A:
{
quint32 ipv4Addr;
if (!parseInteger<quint32>(packet, offset, ipv4Addr)) {
return false;
}
record.setAddress(QHostAddress(ipv4Addr));
break;
}
case AAAA:
{
if (offset + 16 > packet.length()) {
return false;
}
record.setAddress(QHostAddress(
reinterpret_cast<const quint8*>(packet.constData() + offset)
));
offset += 16;
break;
}
case NSEC:
{
QByteArray nextDomainName;
quint8 number;
quint8 length;
if (!parseName(packet, offset, nextDomainName) ||
!parseInteger<quint8>(packet, offset, number) ||
!parseInteger<quint8>(packet, offset, length) ||
number != 0 ||
offset + length > packet.length()) {
return false;
}
Bitmap bitmap;
bitmap.setData(length, reinterpret_cast<const quint8*>(packet.constData() + offset));
record.setNextDomainName(nextDomainName);
record.setBitmap(bitmap);
offset += length;
break;
}
case PTR:
{
QByteArray target;
if (!parseName(packet, offset, target)) {
return false;
}
record.setTarget(target);
break;
}
case SRV:
{
quint16 priority, weight, port;
QByteArray target;
if (!parseInteger<quint16>(packet, offset, priority) ||
!parseInteger<quint16>(packet, offset, weight) ||
!parseInteger<quint16>(packet, offset, port) ||
!parseName(packet, offset, target)) {
return false;
}
record.setPriority(priority);
record.setWeight(weight);
record.setPort(port);
record.setTarget(target);
break;
}
case TXT:
{
quint16 start = offset;
while (offset < start + dataLen) {
quint8 nBytes;
if (!parseInteger<quint8>(packet, offset, nBytes) ||
offset + nBytes > packet.length()) {
return false;
}
if (nBytes == 0) {
break;
}
QByteArray attr(packet.constData() + offset, nBytes);
offset += nBytes;
int splitIndex = attr.indexOf('=');
if (splitIndex == -1) {
record.addAttribute(attr, QByteArray());
} else {
record.addAttribute(attr.left(splitIndex), attr.mid(splitIndex + 1));
}
}
break;
}
default:
offset += dataLen;
break;
}
return true;
}
void writeRecord(QByteArray &packet, quint16 &offset, Record &record, QMap<QByteArray, quint16> &nameMap)
{
writeName(packet, offset, record.name(), nameMap);
writeInteger<quint16>(packet, offset, record.type());
writeInteger<quint16>(packet, offset, record.flushCache() ? 0x8001 : 1);
writeInteger<quint32>(packet, offset, record.ttl());
offset += 2;
QByteArray data;
switch (record.type()) {
case A:
writeInteger<quint32>(data, offset, record.address().toIPv4Address());
break;
case AAAA:
{
Q_IPV6ADDR ipv6Addr = record.address().toIPv6Address();
data.append(reinterpret_cast<const char*>(&ipv6Addr), sizeof(Q_IPV6ADDR));
offset += data.length();
break;
}
case NSEC:
{
quint8 length = record.bitmap().length();
writeName(data, offset, record.nextDomainName(), nameMap);
writeInteger<quint8>(data, offset, 0);
writeInteger<quint8>(data, offset, length);
data.append(reinterpret_cast<const char*>(record.bitmap().data()), length);
offset += length;
break;
}
case PTR:
writeName(data, offset, record.target(), nameMap);
break;
case SRV:
writeInteger<quint16>(data, offset, record.priority());
writeInteger<quint16>(data, offset, record.weight());
writeInteger<quint16>(data, offset, record.port());
writeName(data, offset, record.target(), nameMap);
break;
case TXT:
if (!record.attributes().count()) {
writeInteger<quint8>(data, offset, 0);
break;
}
for (auto i = record.attributes().constBegin(); i != record.attributes().constEnd(); ++i) {
QByteArray entry = i.value().isNull() ? i.key() : i.key() + "=" + i.value();
writeInteger<quint8>(data, offset, entry.length());
data.append(entry);
offset += entry.length();
}
break;
default:
break;
}
offset -= 2;
writeInteger<quint16>(packet, offset, data.length());
packet.append(data);
}
bool fromPacket(const QByteArray &packet, Message &message)
{
quint16 offset = 0;
quint16 transactionId, flags, nQuestion, nAnswer, nAuthority, nAdditional;
if (!parseInteger<quint16>(packet, offset, transactionId) ||
!parseInteger<quint16>(packet, offset, flags) ||
!parseInteger<quint16>(packet, offset, nQuestion) ||
!parseInteger<quint16>(packet, offset, nAnswer) ||
!parseInteger<quint16>(packet, offset, nAuthority) ||
!parseInteger<quint16>(packet, offset, nAdditional)) {
return false;
}
message.setTransactionId(transactionId);
message.setResponse(flags & 0x8400);
message.setTruncated(flags & 0x0200);
for (int i = 0; i < nQuestion; ++i) {
QByteArray name;
quint16 type, class_;
if (!parseName(packet, offset, name) ||
!parseInteger<quint16>(packet, offset, type) ||
!parseInteger<quint16>(packet, offset, class_)) {
return false;
}
Query query;
query.setName(name);
query.setType(type);
query.setUnicastResponse(class_ & 0x8000);
message.addQuery(query);
}
quint16 nRecord = nAnswer + nAuthority + nAdditional;
for (int i = 0; i < nRecord; ++i) {
Record record;
if (!parseRecord(packet, offset, record)) {
return false;
}
message.addRecord(record);
}
return true;
}
void toPacket(const Message &message, QByteArray &packet)
{
quint16 offset = 0;
quint16 flags = (message.isResponse() ? 0x8400 : 0) |
(message.isTruncated() ? 0x200 : 0);
writeInteger<quint16>(packet, offset, message.transactionId());
writeInteger<quint16>(packet, offset, flags);
writeInteger<quint16>(packet, offset, message.queries().length());
writeInteger<quint16>(packet, offset, message.records().length());
writeInteger<quint16>(packet, offset, 0);
writeInteger<quint16>(packet, offset, 0);
QMap<QByteArray, quint16> nameMap;
const auto queries = message.queries();
for (const Query &query : queries) {
writeName(packet, offset, query.name(), nameMap);
writeInteger<quint16>(packet, offset, query.type());
writeInteger<quint16>(packet, offset, query.unicastResponse() ? 0x8001 : 1);
}
const auto records = message.records();
for (Record record : records) {
writeRecord(packet, offset, record, nameMap);
}
}
QString typeName(quint16 type)
{
switch (type) {
case A: return "A";
case AAAA: return "AAAA";
case ANY: return "ANY";
case NSEC: return "NSEC";
case PTR: return "PTR";
case SRV: return "SRV";
case TXT: return "TXT";
default: return "?";
}
}
}

View File

@ -0,0 +1,183 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include <QHostAddress>
#include <QHostInfo>
#include <QNetworkAddressEntry>
#include <QNetworkInterface>
#include "../include/abstractserver.h"
#include "../include/dns.h"
#include "../include/hostname.h"
#include "../include/message.h"
#include "../include/query.h"
#include "../include/record.h"
#include "hostname_p.h"
using namespace QMdnsEngine;
HostnamePrivate::HostnamePrivate(Hostname *hostname, AbstractServer *server)
: QObject(hostname),
server(server),
q(hostname)
{
connect(server, &AbstractServer::messageReceived, this, &HostnamePrivate::onMessageReceived);
connect(&registrationTimer, &QTimer::timeout, this, &HostnamePrivate::onRegistrationTimeout);
connect(&rebroadcastTimer, &QTimer::timeout, this, &HostnamePrivate::onRebroadcastTimeout);
registrationTimer.setInterval(2 * 1000);
registrationTimer.setSingleShot(true);
rebroadcastTimer.setInterval(30 * 60 * 1000);
rebroadcastTimer.setSingleShot(true);
// Immediately assert the hostname
onRebroadcastTimeout();
}
void HostnamePrivate::assertHostname()
{
// Begin with the local hostname and replace any "." with "-" (I'm looking
// at you, macOS)
QByteArray localHostname = QHostInfo::localHostName().toUtf8();
localHostname = localHostname.replace('.', '-');
// If the suffix > 1, then append a "-2", "-3", etc. to the hostname to
// aid in finding one that is unique and not in use
hostname = (hostnameSuffix == 1 ? localHostname:
localHostname + "-" + QByteArray::number(hostnameSuffix)) + ".local.";
// Compose a query for A and AAAA records matching the hostname
Query ipv4Query;
ipv4Query.setName(hostname);
ipv4Query.setType(A);
Query ipv6Query;
ipv6Query.setName(hostname);
ipv6Query.setType(AAAA);
Message message;
message.addQuery(ipv4Query);
message.addQuery(ipv6Query);
server->sendMessageToAll(message);
// If no reply is received after two seconds, the hostname is available
registrationTimer.start();
}
bool HostnamePrivate::generateRecord(const QHostAddress &srcAddress, quint16 type, Record &record)
{
// Attempt to find the interface that corresponds with the provided
// address and determine this device's address from the interface
const auto interfaces = QNetworkInterface::allInterfaces();
for (const QNetworkInterface &networkInterface : interfaces) {
const auto entries = networkInterface.addressEntries();
for (const QNetworkAddressEntry &entry : entries) {
if (srcAddress.isInSubnet(entry.ip(), entry.prefixLength())) {
for (const QNetworkAddressEntry &newEntry : entries) {
QHostAddress address = newEntry.ip();
if ((address.protocol() == QAbstractSocket::IPv4Protocol && type == A) ||
(address.protocol() == QAbstractSocket::IPv6Protocol && type == AAAA)) {
record.setName(hostname);
record.setType(type);
record.setAddress(address);
return true;
}
}
}
}
}
return false;
}
void HostnamePrivate::onMessageReceived(const Message &message)
{
if (message.isResponse()) {
if (hostnameRegistered) {
return;
}
const auto records = message.records();
for (const Record &record : records) {
if ((record.type() == A || record.type() == AAAA) && record.name() == hostname) {
++hostnameSuffix;
assertHostname();
}
}
} else {
if (!hostnameRegistered) {
return;
}
Message reply;
reply.reply(message);
const auto queries = message.queries();
for (const Query &query : queries) {
if ((query.type() == A || query.type() == AAAA) && query.name() == hostname) {
Record record;
if (generateRecord(message.address(), query.type(), record)) {
reply.addRecord(record);
}
}
}
if (reply.records().count()) {
server->sendMessage(reply);
}
}
}
void HostnamePrivate::onRegistrationTimeout()
{
hostnameRegistered = true;
if (hostname != hostnamePrev) {
emit q->hostnameChanged(hostname);
}
// Re-assert the hostname in half an hour
rebroadcastTimer.start();
}
void HostnamePrivate::onRebroadcastTimeout()
{
hostnamePrev = hostname;
hostnameRegistered = false;
hostnameSuffix = 1;
assertHostname();
}
Hostname::Hostname(AbstractServer *server, QObject *parent)
: QObject(parent),
d(new HostnamePrivate(this, server))
{
}
bool Hostname::isRegistered() const
{
return d->hostnameRegistered;
}
QByteArray Hostname::hostname() const
{
return d->hostname;
}

View File

@ -0,0 +1,75 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_HOSTNAME_P_H
#define QMDNSENGINE_HOSTNAME_P_H
#include <QObject>
#include <QTimer>
class QHostAddress;
namespace QMdnsEngine
{
class AbstractServer;
class Hostname;
class Message;
class Record;
class HostnamePrivate : public QObject
{
Q_OBJECT
public:
HostnamePrivate(Hostname *hostname, AbstractServer *server);
void assertHostname();
bool generateRecord(const QHostAddress &srcAddress, quint16 type, Record &record);
AbstractServer *server;
QByteArray hostnamePrev;
QByteArray hostname;
bool hostnameRegistered;
int hostnameSuffix;
QTimer registrationTimer;
QTimer rebroadcastTimer;
private Q_SLOTS:
void onMessageReceived(const Message &message);
void onRegistrationTimeout();
void onRebroadcastTimeout();
private:
Hostname *const q;
};
}
#endif // QMDNSENGINE_HOSTNAME_P_H

View File

@ -0,0 +1,35 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include "../include/mdns.h"
namespace QMdnsEngine
{
const quint16 MdnsPort = 5353;
const QHostAddress MdnsIpv4Address("224.0.0.251");
const QHostAddress MdnsIpv6Address("ff02::fb");
const QByteArray MdnsBrowseType("_services._dns-sd._udp.local.");
}

View File

@ -0,0 +1,148 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include "../include/mdns.h"
#include "../include/message.h"
#include "../include/query.h"
#include "../include/record.h"
#include "message_p.h"
using namespace QMdnsEngine;
MessagePrivate::MessagePrivate()
: port(0),
transactionId(0),
isResponse(false),
isTruncated(false)
{
}
Message::Message()
: d(new MessagePrivate)
{
}
Message::Message(const Message &other)
: d(new MessagePrivate)
{
*this = other;
}
Message &Message::operator=(const Message &other)
{
*d = *other.d;
return *this;
}
Message::~Message()
{
delete d;
}
QHostAddress Message::address() const
{
return d->address;
}
void Message::setAddress(const QHostAddress &address)
{
d->address = address;
}
quint16 Message::port() const
{
return d->port;
}
void Message::setPort(quint16 port)
{
d->port = port;
}
quint16 Message::transactionId() const
{
return d->transactionId;
}
void Message::setTransactionId(quint16 transactionId)
{
d->transactionId = transactionId;
}
bool Message::isResponse() const
{
return d->isResponse;
}
void Message::setResponse(bool isResponse)
{
d->isResponse = isResponse;
}
bool Message::isTruncated() const
{
return d->isTruncated;
}
void Message::setTruncated(bool isTruncated)
{
d->isTruncated = isTruncated;
}
QList<Query> Message::queries() const
{
return d->queries;
}
void Message::addQuery(const Query &query)
{
d->queries.append(query);
}
QList<Record> Message::records() const
{
return d->records;
}
void Message::addRecord(const Record &record)
{
d->records.append(record);
}
void Message::reply(const Message &other)
{
if (other.port() == MdnsPort) {
if (other.address().protocol() == QAbstractSocket::IPv4Protocol) {
setAddress(MdnsIpv4Address);
} else {
setAddress(MdnsIpv6Address);
}
} else {
setAddress(other.address());
}
setPort(other.port());
setTransactionId(other.transactionId());
setResponse(true);
}

View File

@ -0,0 +1,54 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_MESSAGE_P_H
#define QMDNSENGINE_MESSAGE_P_H
#include <QHostAddress>
#include <QList>
namespace QMdnsEngine
{
class Query;
class Record;
class MessagePrivate
{
public:
MessagePrivate();
QHostAddress address;
quint16 port;
quint16 transactionId;
bool isResponse;
bool isTruncated;
QList<Query> queries;
QList<Record> records;
};
}
#endif // QMDNSENGINE_MESSAGE_P_H

View File

@ -0,0 +1,107 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include "../include/abstractserver.h"
#include "../include/dns.h"
#include "../include/message.h"
#include "../include/prober.h"
#include "../include/query.h"
#include "prober_p.h"
using namespace QMdnsEngine;
ProberPrivate::ProberPrivate(Prober *prober, AbstractServer *server, const Record &record)
: QObject(prober),
server(server),
confirmed(false),
proposedRecord(record),
suffix(1),
q(prober)
{
// All records should contain at least one "."
int index = record.name().indexOf('.');
name = record.name().left(index);
type = record.name().mid(index);
connect(server, &AbstractServer::messageReceived, this, &ProberPrivate::onMessageReceived);
connect(&timer, &QTimer::timeout, this, &ProberPrivate::onTimeout);
timer.setSingleShot(true);
assertRecord();
}
void ProberPrivate::assertRecord()
{
// Use the current suffix to set the name of the proposed record
QString tmpName = suffix == 1
? QString("%1%2").arg(name, type.constData())
: QString("%1-%2%3").arg(name.constData(), QByteArray::number(suffix), type);
proposedRecord.setName(tmpName.toUtf8());
// Broadcast a query for the proposed name (using an ANY query) and
// include the proposed record in the query
Query query;
query.setName(proposedRecord.name());
query.setType(ANY);
Message message;
message.addQuery(query);
message.addRecord(proposedRecord);
server->sendMessageToAll(message);
// Wait two seconds to confirm it is unique
timer.stop();
timer.start(2 * 1000);
}
void ProberPrivate::onMessageReceived(const Message &message)
{
// If the response matches the proposed record, increment the suffix and
// try with the new name
if (confirmed || !message.isResponse()) {
return;
}
const auto records = message.records();
for (const Record &record : records) {
if (record.name() == proposedRecord.name() && record.type() == proposedRecord.type()) {
++suffix;
assertRecord();
}
}
}
void ProberPrivate::onTimeout()
{
confirmed = true;
emit q->nameConfirmed(proposedRecord.name());
}
Prober::Prober(AbstractServer *server, const Record &record, QObject *parent)
: QObject(parent),
d(new ProberPrivate(this, server, record))
{
}

View File

@ -0,0 +1,72 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_PROBER_P_H
#define QMDNSENGINE_PROBER_P_H
#include <QObject>
#include <QTimer>
#include "../include/record.h"
namespace QMdnsEngine
{
class AbstractServer;
class Message;
class Prober;
class ProberPrivate : public QObject
{
Q_OBJECT
public:
ProberPrivate(Prober *prober, AbstractServer *server, const Record &record);
void assertRecord();
AbstractServer *server;
QTimer timer;
bool confirmed;
Record proposedRecord;
QByteArray name;
QByteArray type;
int suffix;
private Q_SLOTS:
void onMessageReceived(const Message &message);
void onTimeout();
private:
Prober *const q;
};
}
#endif // QMDNSENGINE_PROBER_P_H

View File

@ -0,0 +1,239 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include "../include/abstractserver.h"
#include "../include/dns.h"
#include "../include/hostname.h"
#include "../include/mdns.h"
#include "../include/message.h"
#include "../include/prober.h"
#include "../include/provider.h"
#include "../include/query.h"
#include "provider_p.h"
using namespace QMdnsEngine;
ProviderPrivate::ProviderPrivate(QObject *parent, AbstractServer *server, Hostname *hostname)
: QObject(parent),
server(server),
hostname(hostname),
prober(nullptr),
initialized(false),
confirmed(false)
{
connect(server, &AbstractServer::messageReceived, this, &ProviderPrivate::onMessageReceived);
connect(hostname, &Hostname::hostnameChanged, this, &ProviderPrivate::onHostnameChanged);
browsePtrProposed.setName(MdnsBrowseType);
browsePtrProposed.setType(PTR);
ptrProposed.setType(PTR);
srvProposed.setType(SRV);
txtProposed.setType(TXT);
}
ProviderPrivate::~ProviderPrivate()
{
if (confirmed) {
farewell();
}
}
void ProviderPrivate::announce()
{
// Broadcast a message with each of the records
Message message;
message.setResponse(true);
message.addRecord(ptrRecord);
message.addRecord(srvRecord);
message.addRecord(txtRecord);
server->sendMessageToAll(message);
}
void ProviderPrivate::confirm()
{
// Confirm that the desired name is unique through probing
if (prober) {
delete prober;
}
prober = new Prober(server, srvProposed, this);
connect(prober, &Prober::nameConfirmed, [this](const QByteArray &name) {
// If existing records were confirmed, indicate that they are no
// longer valid
if (confirmed) {
farewell();
} else {
confirmed = true;
}
// Update the proposed records
ptrProposed.setTarget(name);
srvProposed.setName(name);
txtProposed.setName(name);
// Publish the proposed records and announce them
publish();
delete prober;
prober = nullptr;
});
}
void ProviderPrivate::farewell()
{
// Send a message indicating that the existing records are no longer valid
// by setting their TTL to 0
ptrRecord.setTtl(0);
srvRecord.setTtl(0);
txtRecord.setTtl(0);
announce();
}
void ProviderPrivate::publish()
{
// Copy the proposed records over and announce them
browsePtrRecord = browsePtrProposed;
ptrRecord = ptrProposed;
srvRecord = srvProposed;
txtRecord = txtProposed;
announce();
}
void ProviderPrivate::onMessageReceived(const Message &message)
{
if (!confirmed || message.isResponse()) {
return;
}
bool sendBrowsePtr = false;
bool sendPtr = false;
bool sendSrv = false;
bool sendTxt = false;
// Determine which records to send based on the queries
const auto queries = message.queries();
for (const Query &query : queries) {
if (query.type() == PTR && query.name() == MdnsBrowseType) {
sendBrowsePtr = true;
} else if (query.type() == PTR && query.name() == ptrRecord.name()) {
sendPtr = true;
} else if (query.type() == SRV && query.name() == srvRecord.name()) {
sendSrv = true;
} else if (query.type() == TXT && query.name() == txtRecord.name()) {
sendTxt = true;
}
}
// Remove records to send if they are already known
const auto records = message.records();
for (const Record &record : records) {
if (record == ptrRecord) {
sendPtr = false;
} else if (record == srvRecord) {
sendSrv = false;
} else if (record == txtRecord) {
sendTxt = false;
}
}
// Include the SRV and TXT if the PTR is being sent
if (sendPtr) {
sendSrv = sendTxt = true;
}
// If any records should be sent, compose a message reply
if (sendBrowsePtr || sendPtr || sendSrv || sendTxt) {
Message reply;
reply.reply(message);
if (sendBrowsePtr) {
reply.addRecord(browsePtrRecord);
}
if (sendPtr) {
reply.addRecord(ptrRecord);
}
if (sendSrv) {
reply.addRecord(srvRecord);
}
if (sendTxt) {
reply.addRecord(txtRecord);
}
server->sendMessage(reply);
}
}
void ProviderPrivate::onHostnameChanged(const QByteArray &newHostname)
{
// Update the proposed SRV record
srvProposed.setTarget(newHostname);
// If initialized, confirm the record
if (initialized) {
confirm();
}
}
Provider::Provider(AbstractServer *server, Hostname *hostname, QObject *parent)
: QObject(parent),
d(new ProviderPrivate(this, server, hostname))
{
}
void Provider::update(const Service &service)
{
d->initialized = true;
// Clean the service name
QByteArray serviceName = service.name();
serviceName = serviceName.replace('.', '-');
// Update the proposed records
QByteArray fqName = serviceName + "." + service.type();
d->browsePtrProposed.setTarget(service.type());
d->ptrProposed.setName(service.type());
d->ptrProposed.setTarget(fqName);
d->srvProposed.setName(fqName);
d->srvProposed.setPort(service.port());
d->srvProposed.setTarget(d->hostname->hostname());
d->txtProposed.setName(fqName);
d->txtProposed.setAttributes(service.attributes());
// Assuming a valid hostname exists, check to see if the new service uses
// a different name - if so, it must first be confirmed
if (d->hostname == nullptr) {
qDebug() << "hostname is nullptr";
return;
}
bool registered = d->hostname->isRegistered();
//qDebug() << "Hostname registered:" << registered;
if(registered) {
//if (d->hostname->isRegistered()) {
if (!d->confirmed || fqName != d->srvRecord.name()) {
d->confirm();
} else {
d->publish();
}
}
}

View File

@ -0,0 +1,81 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_PROVIDER_P_H
#define QMDNSENGINE_PROVIDER_P_H
#include <QObject>
#include "../include/record.h"
#include "../include/service.h"
namespace QMdnsEngine
{
class AbstractServer;
class Hostname;
class Message;
class Prober;
class ProviderPrivate : public QObject
{
Q_OBJECT
public:
ProviderPrivate(QObject *parent, AbstractServer *server, Hostname *hostname);
virtual ~ProviderPrivate();
void announce();
void confirm();
void farewell();
void publish();
AbstractServer *server;
Hostname *hostname;
Prober *prober;
Service service;
bool initialized;
bool confirmed;
Record browsePtrRecord;
Record ptrRecord;
Record srvRecord;
Record txtRecord;
Record browsePtrProposed;
Record ptrProposed;
Record srvProposed;
Record txtProposed;
private Q_SLOTS:
void onMessageReceived(const Message &message);
void onHostnameChanged(const QByteArray &hostname);
};
}
#endif // QMDNSENGINE_PROVIDER_P_H

View File

@ -0,0 +1,100 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include <QDebug>
#include "../include/dns.h"
#include "../include/query.h"
#include "query_p.h"
using namespace QMdnsEngine;
QueryPrivate::QueryPrivate()
: type(0),
unicastResponse(false)
{
}
Query::Query()
: d(new QueryPrivate)
{
}
Query::Query(const Query &other)
: d(new QueryPrivate)
{
*this = other;
}
Query &Query::operator=(const Query &other)
{
*d = *other.d;
return *this;
}
Query::~Query()
{
delete d;
}
QByteArray Query::name() const
{
return d->name;
}
void Query::setName(const QByteArray &name)
{
d->name = name;
}
quint16 Query::type() const
{
return d->type;
}
void Query::setType(quint16 type)
{
d->type = type;
}
bool Query::unicastResponse() const
{
return d->unicastResponse;
}
void Query::setUnicastResponse(bool unicastResponse)
{
d->unicastResponse = unicastResponse;
}
QDebug QMdnsEngine::operator<<(QDebug dbg, const Query &query)
{
QDebugStateSaver saver(dbg);
Q_UNUSED(saver);
dbg.noquote().nospace() << "Query(" << typeName(query.type()) << " " << query.name() << ")";
return dbg;
}

View File

@ -0,0 +1,46 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_QUERY_P_H
#define QMDNSENGINE_QUERY_P_H
#include <QByteArray>
namespace QMdnsEngine
{
class QueryPrivate
{
public:
QueryPrivate();
QByteArray name;
quint16 type;
bool unicastResponse;
};
}
#endif // QMDNSENGINE_QUERY_P_H

View File

@ -0,0 +1,218 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include <QDebug>
#include "../include/dns.h"
#include "../include/record.h"
#include "record_p.h"
using namespace QMdnsEngine;
RecordPrivate::RecordPrivate()
: type(0),
flushCache(false),
ttl(3600),
priority(0),
weight(0),
port(0)
{
}
Record::Record()
: d(new RecordPrivate)
{
}
Record::Record(const Record &other)
: d(new RecordPrivate)
{
*this = other;
}
Record &Record::operator=(const Record &other)
{
*d = *other.d;
return *this;
}
bool Record::operator==(const Record &other) const
{
return d->name == other.d->name &&
d->type == other.d->type &&
d->address == other.d->address &&
d->target == other.d->target &&
d->nextDomainName == other.d->nextDomainName &&
d->priority == other.d->priority &&
d->weight == other.d->weight &&
d->port == other.d->port &&
d->attributes == other.d->attributes &&
d->bitmap == other.d->bitmap;
}
bool Record::operator!=(const Record &other) const
{
return !(*this == other);
}
Record::~Record()
{
delete d;
}
QByteArray Record::name() const
{
return d->name;
}
void Record::setName(const QByteArray &name)
{
d->name = name;
}
quint16 Record::type() const
{
return d->type;
}
void Record::setType(quint16 type)
{
d->type = type;
}
bool Record::flushCache() const
{
return d->flushCache;
}
void Record::setFlushCache(bool flushCache)
{
d->flushCache = flushCache;
}
quint32 Record::ttl() const
{
return d->ttl;
}
void Record::setTtl(quint32 ttl)
{
d->ttl = ttl;
}
QHostAddress Record::address() const
{
return d->address;
}
void Record::setAddress(const QHostAddress &address)
{
d->address = address;
}
QByteArray Record::target() const
{
return d->target;
}
void Record::setTarget(const QByteArray &target)
{
d->target = target;
}
QByteArray Record::nextDomainName() const
{
return d->nextDomainName;
}
void Record::setNextDomainName(const QByteArray &nextDomainName)
{
d->nextDomainName = nextDomainName;
}
quint16 Record::priority() const
{
return d->priority;
}
void Record::setPriority(quint16 priority)
{
d->priority = priority;
}
quint16 Record::weight() const
{
return d->weight;
}
void Record::setWeight(quint16 weight)
{
d->weight = weight;
}
quint16 Record::port() const
{
return d->port;
}
void Record::setPort(quint16 port)
{
d->port = port;
}
QMap<QByteArray, QByteArray> Record::attributes() const
{
return d->attributes;
}
void Record::setAttributes(const QMap<QByteArray, QByteArray> &attributes)
{
d->attributes = attributes;
}
void Record::addAttribute(const QByteArray &key, const QByteArray &value)
{
d->attributes.insert(key, value);
}
Bitmap Record::bitmap() const
{
return d->bitmap;
}
void Record::setBitmap(const Bitmap &bitmap)
{
d->bitmap = bitmap;
}
QDebug QMdnsEngine::operator<<(QDebug dbg, const Record &record)
{
QDebugStateSaver saver(dbg);
Q_UNUSED(saver);
dbg.noquote().nospace() << "Record(" << typeName(record.type()) << " " << record.name() << ")";
return dbg;
}

View File

@ -0,0 +1,59 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_RECORD_P_H
#define QMDNSENGINE_RECORD_P_H
#include <QByteArray>
#include <QHostAddress>
#include <QMap>
#include "../include/bitmap.h"
namespace QMdnsEngine {
class RecordPrivate
{
public:
RecordPrivate();
QByteArray name;
quint16 type;
bool flushCache;
quint32 ttl;
QHostAddress address;
QByteArray target;
QByteArray nextDomainName;
quint16 priority;
quint16 weight;
quint16 port;
QMap<QByteArray, QByteArray> attributes;
Bitmap bitmap;
};
}
#endif // QMDNSENGINE_RECORD_P_H

View File

@ -0,0 +1,116 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include <QTimer>
#include "../include/abstractserver.h"
#include "../include/dns.h"
#include "../include/cache.h"
#include "../include/message.h"
#include "../include/query.h"
#include "../include/record.h"
#include "../include/resolver.h"
#include "resolver_p.h"
using namespace QMdnsEngine;
ResolverPrivate::ResolverPrivate(Resolver *resolver, AbstractServer *server, const QByteArray &name, Cache *cache)
: QObject(resolver),
server(server),
name(name),
cache(cache ? cache : new Cache(this)),
q(resolver)
{
connect(server, &AbstractServer::messageReceived, this, &ResolverPrivate::onMessageReceived);
connect(&timer, &QTimer::timeout, this, &ResolverPrivate::onTimeout);
// Query for new records
query();
// Pull the existing records from the cache
timer.setSingleShot(true);
timer.start(0);
}
QList<Record> ResolverPrivate::existing() const
{
QList<Record> records;
cache->lookupRecords(name, A, records);
cache->lookupRecords(name, AAAA, records);
return records;
}
void ResolverPrivate::query() const
{
Message message;
// Add a query for A and AAAA records
Query query;
query.setName(name);
query.setType(A);
message.addQuery(query);
query.setType(AAAA);
message.addQuery(query);
// Add existing (known) records to the query
const auto records = existing();
for (const Record &record : records) {
message.addRecord(record);
}
// Send the query
server->sendMessageToAll(message);
}
void ResolverPrivate::onMessageReceived(const Message &message)
{
if (!message.isResponse()) {
return;
}
const auto records = message.records();
for (const Record &record : records) {
if (record.name() == name && (record.type() == A || record.type() == AAAA)) {
cache->addRecord(record);
if (!addresses.contains(record.address())) {
emit q->resolved(record.address());
addresses.insert(record.address());
}
}
}
}
void ResolverPrivate::onTimeout()
{
const auto records = existing();
for (const Record &record : records) {
emit q->resolved(record.address());
}
}
Resolver::Resolver(AbstractServer *server, const QByteArray &name, Cache *cache, QObject *parent)
: QObject(parent),
d(new ResolverPrivate(this, server, name, cache))
{
}

View File

@ -0,0 +1,71 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_RESOLVER_P_H
#define QMDNSENGINE_RESOLVER_P_H
#include <QHostAddress>
#include <QObject>
#include <QSet>
#include <QTimer>
namespace QMdnsEngine
{
class AbstractServer;
class Cache;
class Message;
class Record;
class Resolver;
class ResolverPrivate : public QObject
{
Q_OBJECT
public:
explicit ResolverPrivate(Resolver *resolver, AbstractServer *server, const QByteArray &name, Cache *cache);
QList<Record> existing() const;
void query() const;
AbstractServer *server;
QByteArray name;
Cache *cache;
QSet<QHostAddress> addresses;
QTimer timer;
private Q_SLOTS:
void onMessageReceived(const Message &message);
void onTimeout();
private:
Resolver *const q;
};
}
#endif // QMDNSENGINE_RESOLVER_P_H

View File

@ -0,0 +1,138 @@
#include <QtGlobal>
#ifdef Q_OS_UNIX
# include <cerrno>
# include <cstring>
# include <sys/socket.h>
#endif
#include <QHostAddress>
#include <QNetworkInterface>
#include "../include/dns.h"
#include "../include/mdns.h"
#include "../include/message.h"
#include "../include/server.h"
#include "server_p.h"
using namespace QMdnsEngine;
ServerPrivate::ServerPrivate(Server *server)
: QObject(server),
q(server)
{
connect(&timer, &QTimer::timeout, this, &ServerPrivate::onTimeout);
connect(&ipv4Socket, &QUdpSocket::readyRead, this, &ServerPrivate::onReadyRead);
connect(&ipv6Socket, &QUdpSocket::readyRead, this, &ServerPrivate::onReadyRead);
timer.setInterval(60 * 1000);
timer.setSingleShot(true);
onTimeout();
}
bool ServerPrivate::bindSocket(QUdpSocket &socket, const QHostAddress &address)
{
// Exit early if the socket is already bound
if (socket.state() == QAbstractSocket::BoundState) {
return true;
}
// I cannot find the correct combination of flags that allows the socket
// to bind properly on Linux, so on that platform, we must manually create
// the socket and initialize the QUdpSocket with it
#ifdef Q_OS_UNIX
if (!socket.bind(address, MdnsPort, QAbstractSocket::ShareAddress)) {
int arg = 1;
if (setsockopt(socket.socketDescriptor(), SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<char*>(&arg), sizeof(int))) {
emit q->error(strerror(errno));
return false;
}
#endif
if (!socket.bind(address, MdnsPort, QAbstractSocket::ReuseAddressHint)) {
emit q->error(socket.errorString());
return false;
}
#ifdef Q_OS_UNIX
}
#endif
return true;
}
void ServerPrivate::onTimeout()
{
// A timer is used to run a set of operations once per minute; first, the
// two sockets are bound - if this fails, another attempt is made once per
// timeout; secondly, all network interfaces are enumerated; if the
// interface supports multicast, the socket will join the mDNS multicast
// groups
bool ipv4Bound = bindSocket(ipv4Socket, QHostAddress::AnyIPv4);
bool ipv6Bound = bindSocket(ipv6Socket, QHostAddress::AnyIPv6);
if (ipv4Bound || ipv6Bound) {
const auto interfaces = QNetworkInterface::allInterfaces();
for (const QNetworkInterface &networkInterface : interfaces) {
if (networkInterface.flags() & QNetworkInterface::CanMulticast) {
if (ipv4Bound) {
ipv4Socket.joinMulticastGroup(MdnsIpv4Address, networkInterface);
}
if (ipv6Bound) {
ipv6Socket.joinMulticastGroup(MdnsIpv6Address, networkInterface);
}
}
}
}
timer.start();
}
void ServerPrivate::onReadyRead()
{
// Read the packet from the socket
QUdpSocket *socket = qobject_cast<QUdpSocket*>(sender());
QByteArray packet;
packet.resize(socket->pendingDatagramSize());
QHostAddress address;
quint16 port;
socket->readDatagram(packet.data(), packet.size(), &address, &port);
// Attempt to decode the packet
Message message;
if (fromPacket(packet, message)) {
message.setAddress(address);
message.setPort(port);
emit q->messageReceived(message);
}
}
Server::Server(QObject *parent)
: AbstractServer(parent),
d(new ServerPrivate(this))
{
}
void Server::sendMessage(const Message &message)
{
QByteArray packet;
toPacket(message, packet);
if (message.address().protocol() == QAbstractSocket::IPv4Protocol) {
d->ipv4Socket.writeDatagram(packet, message.address(), message.port());
} else {
d->ipv6Socket.writeDatagram(packet, message.address(), message.port());
}
}
void Server::sendMessageToAll(const Message &message)
{
QByteArray packet;
toPacket(message, packet);
//qDebug() << "MdnsMsg :" << packet.toHex();
qDebug() << "MdnsPort:" << MdnsPort;
qint64 sentBytes = d->ipv4Socket.writeDatagram(packet, MdnsIpv4Address, MdnsPort);
qDebug() << "ipv4Socket sentBytes:" << sentBytes;
sentBytes = d->ipv6Socket.writeDatagram(packet, MdnsIpv6Address, MdnsPort);
qDebug() << "ipv6Socket sentBytes:" << sentBytes;
}

View File

@ -0,0 +1,65 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_SERVER_P_H
#define QMDNSENGINE_SERVER_P_H
#include <QObject>
#include <QTimer>
#include <QUdpSocket>
class QHostAddress;
namespace QMdnsEngine
{
class Server;
class ServerPrivate : public QObject
{
Q_OBJECT
public:
explicit ServerPrivate(Server *server);
bool bindSocket(QUdpSocket &socket, const QHostAddress &address);
QTimer timer;
QUdpSocket ipv4Socket{ this };
QUdpSocket ipv6Socket{ this };
private Q_SLOTS:
void onTimeout();
void onReadyRead();
private:
Server *const q;
};
}
#endif // QMDNSENGINE_SERVER_P_H

View File

@ -0,0 +1,139 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include "../include/service.h"
#include "service_p.h"
using namespace QMdnsEngine;
ServicePrivate::ServicePrivate()
{
}
Service::Service()
: d(new ServicePrivate)
{
}
Service::Service(const Service &other)
: d(new ServicePrivate)
{
*this = other;
}
Service &Service::operator=(const Service &other)
{
*d = *other.d;
return *this;
}
bool Service::operator==(const Service &other) const
{
return d->type == other.d->type &&
d->name == other.d->name &&
d->port == other.d->port &&
d->attributes == other.d->attributes;
}
bool Service::operator!=(const Service &other) const
{
return !(*this == other);
}
Service::~Service()
{
delete d;
}
QByteArray Service::type() const
{
return d->type;
}
void Service::setType(const QByteArray &type)
{
d->type = type;
}
QByteArray Service::name() const
{
return d->name;
}
void Service::setName(const QByteArray &name)
{
d->name = name;
}
QByteArray Service::hostname() const
{
return d->hostname;
}
void Service::setHostname(const QByteArray &hostname)
{
d->hostname = hostname;
}
quint16 Service::port() const
{
return d->port;
}
void Service::setPort(quint16 port)
{
d->port = port;
}
QMap<QByteArray, QByteArray> Service::attributes() const
{
return d->attributes;
}
void Service::setAttributes(const QMap<QByteArray, QByteArray> &attributes)
{
d->attributes = attributes;
}
void Service::addAttribute(const QByteArray &key, const QByteArray &value)
{
d->attributes.insert(key, value);
}
QDebug QMdnsEngine::operator<<(QDebug debug, const Service &service)
{
QDebugStateSaver saver(debug);
Q_UNUSED(saver);
debug.noquote().nospace()
<< "Service(name: " << service.name()
<< ", type: " << service.type()
<< ", hostname: " << service.hostname()
<< ", port: " << service.port()
<< ", attributes: " << service.attributes()
<< ")";
return debug;
}

View File

@ -0,0 +1,49 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_SERVICE_P_H
#define QMDNSENGINE_SERVICE_P_H
#include <QByteArray>
#include <QMap>
namespace QMdnsEngine
{
class ServicePrivate
{
public:
ServicePrivate();
QByteArray type;
QByteArray name;
QByteArray hostname;
quint16 port;
QMap<QByteArray, QByteArray> attributes;
};
}
#endif // QMDNSENGINE_SERVICE_P_H

View File

@ -0,0 +1,36 @@
// #include "servicemodel.cpp"
#include "servicemodel.h"
Q_DECLARE_METATYPE(QMdnsEngine::Service)
ServiceProvider::ServiceProvider(QObject* parent)
: QObject(parent), mHostname(&mServer), mProvider(nullptr)
{
// Initialize the provider when the ServiceProvider is created
}
void ServiceProvider::startServiceBroadcast(const QString& serviceName, const QString& serviceType, quint16 port)
{
if (mProvider) {
delete mProvider;
mProvider = nullptr;
}
// Set up the service with the specified name, type, and port
mService.setName(serviceName.toUtf8());
mService.setType(serviceType.toUtf8());
mService.setPort(port);
// Create a new provider for this service
mProvider = new QMdnsEngine::Provider(&mServer, &mHostname, this);
mProvider->update(mService);
qDebug() << "mDNS service broadcast started:" << serviceName << serviceType << "on port" << port;
}
void ServiceProvider::stopServiceBroadcast()
{
if (mProvider) {
delete mProvider;
mProvider = nullptr;
qDebug() << "mDNS service broadcast stopped.";
}
}

View File

@ -0,0 +1,29 @@
#ifndef SERVICEMODEL_H
#define SERVICEMODEL_H
#include <QObject>
#include "qmdnsengine/include/server.h"
#include "qmdnsengine/include/hostname.h"
#include "qmdnsengine/include/provider.h"
#include "qmdnsengine/include/service.h"
class ServiceProvider : public QObject
{
Q_OBJECT
public:
explicit ServiceProvider(QObject* parent = nullptr);
void startServiceBroadcast(const QString& serviceName, const QString& serviceType, quint16 port);
void stopServiceBroadcast();
private:
QMdnsEngine::Server mServer;
QMdnsEngine::Hostname mHostname;
QMdnsEngine::Provider* mProvider;
QMdnsEngine::Service mService;
};
#endif // SERVICEPROVIDER_H

View File

@ -56,6 +56,20 @@
#define SET_LOG_LEVEL 0x0360
// 前后板设备信息
#define GET_FRONT_V851_VERSION 0x0400
#define GET_FRONT_MCU_VERSION 0x0401
#define GET_FRONT_HW_VERSION 0x0402
#define GET_FRONT_ALGO_VERSION 0x0403
#define GET_FRONT_SN 0x0404
#define GET_FRONT_HW_INFO 0x0420
#define WRITE_FRONT_LICENSE 0x0421
#define GET_BACK_V851_VERSION 0x0450
#define GET_BACK_806_VERSION 0x0451
#define GET_BACK_HW_VERSION 0x0452
#define GET_BACK_SN 0x0453
#define GET_BACK_UID 0x0454
#endif

View File

@ -2,16 +2,20 @@
#include "RecvDataHandler.h"
#include "../Network/ClientHandler.h"
DataHandler::DataHandler(QLabel* leftLens_imageLabel, QLabel* rightLens_imageLabel, QLabel* videoLabel, QObject* parent)
DataHandler::DataHandler(QLabel* leftLens_imageLabel, QLabel* rightLens_imageLabel, QLabel* videoLabel,
QTextEdit* licenseHwInfoEdit, QMap<QString, QLineEdit*>* devInfoLineEdits, QObject* parent)
: QObject(parent),
leftLens_m_imageLabel(leftLens_imageLabel),
rightLens_m_imageLabel(rightLens_imageLabel),
videoLabel(videoLabel),
licenseHwInfoEdit(licenseHwInfoEdit),
devInfoLineEdits(devInfoLineEdits),
ffmpegDecoder(new FFmpegDecoder()), // 初始化FFmpeg解码器
buffer(new QByteArray())
{
ffmpegDecoder->initialize(); // 初始化解码器
ffmpegDecoder->initialize(); // 初始化解码器
clearAllRecvData();
initializeMsgIdToCmdMap();
}
DataHandler::~DataHandler()
@ -26,7 +30,6 @@ DataHandler::~DataHandler()
buffer = nullptr;
}
// 将十六进制字符串转换为 QByteArray
QByteArray DataHandler::hexStringToByteArray(const QString& hexString)
{
QByteArray byteArray;
@ -65,6 +68,19 @@ void DataHandler::showVideo(const QString& client, const QByteArray& valData)
//ffmpegDecoder->decodeFile(filePath_1, videoLabel);
}
void DataHandler::updateLineEdit(int msg_id, const QByteArray& actual_data) {
QString dataStr = QString(actual_data.toHex(' '));
licenseHwInfoEdit->setPlainText(dataStr);
if (msgIdToCmdMap.contains(msg_id)) {
QString cmd = msgIdToCmdMap[msg_id];
if (devInfoLineEdits->contains(cmd)) {
QLineEdit* lineEdit = devInfoLineEdits->value(cmd);
lineEdit->setText(dataStr);
}
}
}
void DataHandler::clearAllRecvData() {
allRecvData = QByteArray();
remain = 0;
@ -93,12 +109,10 @@ void DataHandler::handleData(const QString& client, const QByteArray& recvData,
#endif
//qDebug() << "---Received data size:" << recvData.size();
// 将接收到的数据追加到buffer
buffer->append(recvData);
while (buffer->size() >= 11) { // 至少需要11个字节来解析数据头
// 检查数据头
if (buffer->mid(0, 4) == QByteArray::fromHex("aa55aa55")) {
msg_id = (static_cast<unsigned char>(buffer->at(5)) << 8) |
(static_cast<unsigned char>(buffer->at(4)));
@ -122,7 +136,7 @@ void DataHandler::handleData(const QString& client, const QByteArray& recvData,
// 同一个client仅当 msg_id 不连续为 0x11/0x21 或第一次处理时才执行 emit statusUpdated
if ((msg_id != 0x0011 || clientLastMsgId.value(client, 0) != 0x0011) &&
(msg_id != 0x0021 || clientLastMsgId.value(client, 0) != 0x0021)){
qDebug() << "Emitting statusUpdated for client:" << client << "with msg_id:" << msg_id;
qDebug() << "Emitting statusUpdated for client:" << client << "with msg_id:" << QString::number(msg_id, 16).toUpper();
emit statusUpdated(client, currentRecvItemIndex + 1, currentRecvFuncItemIndex + 1,
true, itemData, funcItemData);
}
@ -139,6 +153,33 @@ void DataHandler::handleData(const QString& client, const QByteArray& recvData,
}
}
void DataHandler::initializeMsgIdToCmdMap() {
msgIdToCmdMap[GET_FRONT_V851_VERSION] = "GET_FRONT_V851_VERSION";
msgIdToCmdMap[GET_FRONT_MCU_VERSION] = "GET_FRONT_MCU_VERSION";
msgIdToCmdMap[GET_FRONT_HW_VERSION] = "GET_FRONT_HW_VERSION";
msgIdToCmdMap[GET_FRONT_ALGO_VERSION] = "GET_FRONT_ALGO_VERSION";
msgIdToCmdMap[GET_FRONT_SN] = "GET_FRONT_SN";
msgIdToCmdMap[GET_BACK_V851_VERSION] = "GET_BACK_V851_VERSION";
msgIdToCmdMap[GET_BACK_806_VERSION] = "GET_BACK_806_VERSION";
msgIdToCmdMap[GET_BACK_HW_VERSION] = "GET_BACK_HW_VERSION";
msgIdToCmdMap[GET_BACK_SN] = "GET_BACK_SN";
msgIdToCmdMap[GET_BACK_UID] = "GET_BACK_UID";
}
void DataHandler::handleCmd(int msg_id, const QString& client, QByteArray actual_data)
{
if (msg_id < 0x0400) {
handleFrontCmd(msg_id, client, actual_data);
}
else if (msg_id < 0x0500) {
handleDevInfo(msg_id, client, actual_data);
}
else if (msg_id < 0x0800) {
handleBackCmd(msg_id, client, actual_data);
}
}
void DataHandler::handleFrontCmd(int msg_id, const QString& client, QByteArray actual_data)
{
switch (msg_id) {
@ -381,26 +422,67 @@ void DataHandler::handleFrontCmd(int msg_id, const QString& client, QByteArray a
{
}
break;
break;
case SET_LOG_LEVEL:
{
}
break;
default:
{}
break;
}
}
void DataHandler::handleDevInfo(int msg_id, const QString& client, QByteArray actual_data)
{
switch (msg_id) {
case GET_FRONT_V851_VERSION:
case GET_FRONT_MCU_VERSION:
case GET_FRONT_HW_VERSION:
case GET_FRONT_ALGO_VERSION:
case GET_FRONT_SN:
case GET_BACK_V851_VERSION:
case GET_BACK_806_VERSION:
case GET_BACK_HW_VERSION:
case GET_BACK_SN:
case GET_BACK_UID:
{
// aa55aa5503041d00000048464d3231305f4b3431343234395f423230323031305f41302e302e38
qDebug() << "GET_DEV_INFO";
QString dataStr = QString::fromUtf8(actual_data);
if (msgIdToCmdMap.contains(msg_id)) {
QString cmd = msgIdToCmdMap[msg_id];
if (devInfoLineEdits->contains(cmd)) {
QLineEdit* lineEdit = devInfoLineEdits->value(cmd);
lineEdit->setText(dataStr);
}
}
//qDebug() << "GET_DEV_INFO msg_id:" << QString::number(msg_id, 16).toUpper();
}
break;
case GET_FRONT_HW_INFO:
{
qDebug() << "GET_FRONT_HW_INFO";
QString dataStr = QString(actual_data.toHex(' '));
QString displayText = "get_hw_info:\n" + dataStr;
licenseHwInfoEdit->setPlainText(displayText);
}
break;
case WRITE_FRONT_LICENSE:
{
qDebug() << "WRITE_FRONT_LICENSE";
}
break;
default:
break;
}
}
void DataHandler::handleBackCmd(int msg_id, const QString& client, QByteArray actual_data)
{
switch (msg_id) {
}
}
void DataHandler::handleCmd(int msg_id, const QString& client, QByteArray actual_data)
{
if (msg_id < 0x0400) {
handleFrontCmd(msg_id, client, actual_data);
}
else if (msg_id < 0x0800) {
handleBackCmd(msg_id, client, actual_data);
}
}

View File

@ -17,6 +17,8 @@
#include <QFile>
#include <QThread>
#include <QElapsedTimer>
#include <QLineEdit>
#include <QTextEdit>
#include "../Media/Media.h"
#include "../ParseDataHandler/msgID.h"
#include "../Media/VideoDecoder/FFmpegDecoder.h"
@ -30,7 +32,8 @@ class DataHandler : public QObject
Q_OBJECT
public:
explicit DataHandler(QLabel* leftLens_imageLabel, QLabel* rightLens_imageLabel, QLabel* videoLabel, QObject* parent = nullptr);
explicit DataHandler(QLabel* leftLens_imageLabel, QLabel* rightLens_imageLabel, QLabel* videoLabel,
QTextEdit* licenseHwInfoEdit, QMap<QString, QLineEdit*>* devInfoLineEdits, QObject* parent = nullptr);
~DataHandler();
public slots:
@ -45,6 +48,7 @@ private:
QLabel* leftLens_m_imageLabel;
QLabel* rightLens_m_imageLabel;
QLabel* videoLabel;
QTextEdit* licenseHwInfoEdit;
QByteArray allRecvData; // 完整的一帧数据
int remain = 0;
int start_run = 0;
@ -52,6 +56,8 @@ private:
FFmpegDecoder* ffmpegDecoder;
QByteArray *buffer;
QHash<QString, unsigned char> clientLastMsgId;
QMap<QString, QLineEdit*>* devInfoLineEdits;
QMap<int, QString> msgIdToCmdMap;
// 如果接收十六进制数据,转为二进制
QByteArray hexStringToByteArray(const QString& hexString);
@ -59,7 +65,10 @@ private:
void clearAllRecvData();
void handleCmd(int msg_id, const QString& client, QByteArray actual_data);
void handleFrontCmd(int msg_id, const QString& client, QByteArray actual_data);
void handleDevInfo(int msg_id, const QString& client, QByteArray actual_data);
void handleBackCmd(int msg_id, const QString& client, QByteArray actual_data);
void initializeMsgIdToCmdMap();
void updateLineEdit(int msg_id, const QByteArray& actual_data);
};
#endif // DATAHANDLER_H

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@
#include <QMutex>
#include <QFileDialog>
#include <QLineEdit>
#include <QTextEdit>
#include <QTabWidget>
#include <QTimer>
#include <QGroupBox>
@ -23,13 +24,25 @@
#include <QDebug>
#include <QHBoxLayout>
#include <QTcpSocket>
#include <QUdpSocket>
#include <QNetworkInterface>
#include <QTextStream>
#include <QScrollArea>
#include <QDateTime>
#include "../Json/readJsonFile.h"
#include "../RecvDataHandler/RecvDataHandler.h"
#include "../LicenseGenerate/LicenseGenerate.h"
#include "../LicenseGenerate/LicenseConfirmWindow.h"
#include "../Network/mdns/servicemodel.h"
#include "../Network/ClientHandler.h"
#include "FocusWindow.h"
// 用于测试 UDP 组播实现 mdns 功能 非标准 mdns
#define TEST_UDP_BROADCAST 0
#define TCP_CONNECT_PORT 12412
class MainWidget : public QWidget
{
Q_OBJECT
@ -81,7 +94,9 @@ private slots:
void onCheckConnectionStatus();
void startClientReadTimer(int clientId);
void stopClientReadTimer(int clientId);
#if TEST_UDP_BROADCAST
void sendMulticastMessage();
#endif
private:
// 读取 test JSON 配置文件
@ -95,62 +110,77 @@ private:
void onDisconnectClient(int clientId);
void setupTimerForThreadPoolInfo();
bool isSendingAll; // 一键功能测试 状态
QTcpServer* server; // TCP 服务器
QJsonArray frontBoardOneClickTest; // 前板一键功能测试 JSON
QJsonArray frontBoardTest; // 前板单项测试 JSON
QJsonArray frontBoardFuncConfig; // 前板功能配置参数 JSON
QJsonArray frontBoardDevInfoJson; // 前板设备信息参数 JSON
QJsonArray frontBoardLicenseJson; // 前板License JSON
QJsonArray backBoardDevInfoJson; // 后板设备信息参数 JSON
QJsonArray testJsonConfig; // 功能测试区 JSON 配置
QJsonArray funcJsonConfig; // 功能配置区 JSON 配置
QJsonArray getDevInfoJson; // 获取设备信息 JSON 配置
QJsonArray getPicJson; // 发送取图指令 JSON 配置
QJsonArray getVideoJson; // 发送拉视频指令 JSON 配置
QVBoxLayout* mainLayout; // 主布局
QListWidget* statusListWidget; // 状态列表
QPushButton* startServerButton; // 开始服务器按键
QPushButton* focusWindowsButton; // 调焦窗口按键
QPushButton* sendAllButton; // 一键发送按键
QThreadPool threadPool; // 线程池
QMutex mutex; // 互斥锁
int nextClientId; // 新增的客户端编号
int connectedClientsCount = 0; // 连接客户端的数量
bool manualSend; // 判断是否是手动触发的发送
bool deviceConnected = false; // 判断是否有设备连接过
bool isSendingAll; // 一键功能测试 状态
QTcpServer* server; // TCP 服务器
//QUdpSocket* udpSocket;
QJsonArray frontBoardOneClickTest; // 前板一键功能测试 JSON
QJsonArray frontBoardTest; // 前板单项测试 JSON
QJsonArray frontBoardFuncConfig; // 前板功能配置参数 JSON
QJsonArray frontBoardDevInfoJson; // 前板设备信息参数 JSON
QJsonArray frontBoardLicenseJson; // 前板License JSON
QJsonArray backBoardOneClickTest; // 后板一键功能测试 JSON
QJsonArray backBoardTest; // 后板单项测试 JSON
QJsonArray backBoardFuncConfig; // 后板功能配置参数 JSON
QJsonArray backBoardDevInfoJson; // 后板设备信息参数 JSON
QJsonArray testJsonConfig; // 功能测试区 JSON 配置
QJsonArray funcJsonConfig; // 功能配置区 JSON 配置
QJsonArray getDevInfoJson; // 获取设备信息 JSON 配置
QJsonArray getPicJson; // 发送取图指令 JSON 配置
QJsonArray getVideoJson; // 发送拉视频指令 JSON 配置
QVBoxLayout* mainLayout; // 主布局
QListWidget* statusListWidget; // 状态列表
QPushButton* startServerButton; // 开始服务器按键
QPushButton* focusWindowsButton; // 调焦窗口按键
QPushButton* sendAllButton; // 一键发送按键
QThreadPool threadPool; // 线程池
QMutex mutex; // 互斥锁
int nextClientId; // 新增的客户端编号
int connectedClientsCount = 0; // 连接客户端的数量
bool manualSend; // 判断是否是手动触发的发送
bool deviceConnected = false; // 判断是否有设备连接过
QVector<QPushButton*> itemButtons; // 项目按键集合
QVector<QPushButton*> funcItemButtons; // 功能配置项目按键集合
QVector<QPushButton*> getPicButtons; // 保存两个取图的摄像头的按键的指针
QVector<QPushButton*> getVideoButtons; // 保存拉视频设备的按键的指针
QList<ClientHandler*> clients; // 客户端处理器集合
QMap<int, QTimer*> clientTimers; // 每个客户端的定时器
QVector<QPushButton*> itemButtons; // 项目按键集合
QVector<QPushButton*> funcItemButtons; // 功能配置项目按键集合
QVector<QPushButton*> getPicButtons; // 保存两个取图的摄像头的按键的指针
QVector<QPushButton*> getVideoButtons; // 保存拉视频设备的按键的指针
QList<ClientHandler*> clients; // 客户端处理器集合
QMap<int, QTimer*> clientTimers; // 每个客户端的定时器
QMap<QString, QLineEdit*> devInfoLineEdits;// msg_id 和对应的 QLineEdit 的映射关系
QCheckBox* saveCheckBox; // 保存文件复选框
QPushButton* selectFileButton; // Save Log 按键
QPushButton* clearLogButton; // clear Log 按键
QLineEdit* filePathLineEdit; // 文件路径显示
QLabel* leftLens_imageLabel; // 左边镜头图像显示
QLabel* rightLens_imageLabel; // 右边镜头图像显示
QLabel* videoLabel; // 视频显示
QLineEdit* funcConfigLineEdit; // 功能配置编辑框
QTabWidget* tabWidget; // 标签页
QWidget* functionTestArea; // 功能测试区
QWidget* functionConfigArea; // 功能配置区
QCheckBox* saveCheckBox; // 保存文件复选框
QPushButton* selectFileButton; // Save Log 按键
QPushButton* clearLogButton; // clear Log 按键
QLineEdit* filePathLineEdit; // 文件路径显示
QLabel* leftLens_imageLabel; // 左边镜头图像显示
QLabel* rightLens_imageLabel; // 右边镜头图像显示
QLabel* videoLabel; // 视频显示
QLineEdit* funcConfigLineEdit; // 功能配置编辑框
QTextEdit* licenseHwInfoEdit; // 获取license的硬件信息
MainWidget* mainWidget;
QTabWidget* tabWidget; // 标签页
QWidget* functionTestArea; // 功能测试区
QWidget* functionConfigArea; // 功能配置区
int lastClickedGetPicCamIndex; // 记录上一次点击取图的摄像头的按键索引
int lastClickedGetPicDevIndex; // 记录上一次点击取图的设备的按键索引
int lastClickedGetVideoCamIndex; // 记录上一次点击拉视频的摄像头的按键索引
int lastClickedGetVideoDevIndex; // 记录上一次点击拉视频的设备的按键索引
int lastClickedGetPicCamIndex; // 记录上一次点击取图的摄像头的按键索引
int lastClickedGetPicDevIndex; // 记录上一次点击取图的设备的按键索引
int lastClickedGetVideoCamIndex; // 记录上一次点击拉视频的摄像头的按键索引
int lastClickedGetVideoDevIndex; // 记录上一次点击拉视频的设备的按键索引
QTimer* threadStatusTimer;
QTimer* connectionStatusCheckTimer; // 添加检查连接状态的定时器
QMap<int, QThread*> clientThreads;
QTimer* threadStatusTimer; // 检查线程状态的定时器
QTimer* connectionStatusCheckTimer; // 检查连接状态的定时器
QMap<int, QThread*> clientThreads;
QMap<int, QTimer*> clientReadTimers;
QMap<int, ClientHandler*> clients_1;
QMap<int, QTimer*> clientReadTimers;
ServiceProvider* mServiceProvider;
QTimer* mdnsTimer;
void startMdnsService();
void stopMdnsService();
#if TEST_UDP_BROADCAST
QUdpSocket* multicastSocket;
QTimer* multicastTimer;
#endif
};
#endif // MAINWIDGET_H

View File

@ -0,0 +1 @@
/CMakeLists.txt.user

View File

@ -0,0 +1,86 @@
cmake_minimum_required(VERSION 3.2.0)
project(qmdnsengine)
set(PROJECT_NAME "QMdnsEngine")
set(PROJECT_DESCRIPTION "Multicast DNS library for Qt applications")
set(PROJECT_AUTHOR "Nathan Osman")
set(PROJECT_URL "https://github.com/nitroshare/qmdnsengine")
set(PROJECT_VERSION_MAJOR 0)
set(PROJECT_VERSION_MINOR 2)
set(PROJECT_VERSION_PATCH 0)
set(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
# Build a shared library by default
option(BUILD_SHARED_LIBS "Build QMdnsEngine as a shared library" ON)
set(BIN_INSTALL_DIR bin CACHE STRING "Binary installation directory relative to the install prefix")
set(LIB_INSTALL_DIR lib CACHE STRING "Library installation directory relative to the install prefix")
set(INCLUDE_INSTALL_DIR include CACHE STRING "Header installation directory relative to the install prefix")
set(DOC_INSTALL_DIR share/doc/qmdnsengine CACHE STRING "Documentation installation directory relative to the install prefix")
set(EXAMPLES_INSTALL_DIR "${LIB_INSTALL_DIR}/qmdnsengine/examples" CACHE STRING "Examples installation directory relative to the install prefix")
if (CMAKE_PREFIX_PATH)
message( STATUS "QMdnsEngine: Use CMAKE_PREFIX_PATH to locate Qt version to be used: ${CMAKE_PREFIX_PATH}" )
endif()
find_package(QT NAMES Qt6 Qt5 COMPONENTS Network REQUIRED)
message( STATUS "QMdnsEngine: Found Qt Version: ${QT_VERSION}" )
if (${QT_VERSION_MAJOR} GREATER_EQUAL 6 )
SET(QT_MIN_VERSION "6.0.0")
else()
SET(QT_MIN_VERSION "5.7.0")
endif()
if ( "${QT_VERSION}" VERSION_LESS "${QT_MIN_VERSION}" )
message( FATAL_ERROR "Your Qt version is to old! Minimum required ${QT_MIN_VERSION}" )
endif()
find_package(Qt${QT_VERSION_MAJOR} ${QT_VERSION} COMPONENTS Network REQUIRED)
message( STATUS "QMdnsEngine: Qt version used: ${QT_VERSION}" )
set(CMAKE_AUTOMOC ON)
# Add compilation flags for warnings
if (MSVC)
add_compile_options(/W4)
else()
add_compile_options(-Wall -Wextra -pedantic)
endif()
add_subdirectory(src)
option(BUILD_DOC "Build Doxygen documentation" OFF)
if(BUILD_DOC)
find_package(Doxygen REQUIRED)
add_subdirectory(doc)
endif()
option(BUILD_EXAMPLES "Build example applications" OFF)
if(BUILD_EXAMPLES)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
add_subdirectory(examples)
endif()
option(BUILD_TESTS "Build test suite" OFF)
if(BUILD_TESTS)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Test REQUIRED)
enable_testing()
add_subdirectory(tests)
endif()
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}")
set(CPACK_PACKAGE_VENDOR "${PROJECT_AUTHOR}")
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
set(CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "Documentation")
set(CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION "Documentation generated for the library")
set(CPACK_COMPONENT_EXAMPLES_DISPLAY_NAME "Examples")
set(CPACK_COMPONENT_EXAMPLES_DESCRIPTION "Sample applications using the library")
include(CPack)

View File

@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2018 Nathan Osman
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:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.

View File

@ -0,0 +1,12 @@
## QMdnsEngine
[![Build Status](https://ci.quickmediasolutions.com/job/qmdnsengine/badge/icon)](https://ci.quickmediasolutions.com/job/qmdnsengine/)
[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://opensource.org/licenses/MIT)
This library provides an implementation of multicast DNS as per [RFC 6762](https://tools.ietf.org/html/rfc6762).
### Documentation
To learn more about building and using the library, please visit this page:
https://ci.quickmediasolutions.com/job/qmdnsengine-documentation/doxygen/

View File

@ -0,0 +1,10 @@
configure_file(Doxyfile.in "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
add_custom_target(doc ALL
"${DOXYGEN_EXECUTABLE}" \"${CMAKE_CURRENT_BINARY_DIR}/Doxyfile\"
)
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html"
DESTINATION "${DOC_INSTALL_DIR}"
COMPONENT documentation
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,87 @@
QMdnsEngine provides an implementation of multicast DNS as per RFC 6762.
Some of QMdnsEngine's features include:
- Supports Windows, macOS, Linux, and most other platforms supported by Qt
- Requires only QtCore and QtNetwork - no other dependencies
- Includes an exhaustive set of unit tests
## Build Requirements
QMdnsEngine requires the following in order to build the library:
- CMake 3.2+
- Qt 5.4+
- C++ compiler with C++11 support
## Build Instructions
QMdnsEngine uses CMake for building the library. The options shown below allow the build to be customized:
- Installation:
- `BIN_INSTALL_DIR` - binary installation directory relative to the install prefix
- `LIB_INSTALL_DIR` - library installation directory relative to the install prefix
- `INCLUDE_INSTALL_DIR` - header installation directory relative to the install prefix
- Customization:
- `BUILD_DOC` - build the documentation for the library with Doxygen
- `BUILD_EXAMPLES` - build example applications that use the library
- `BUILD_TESTS` - build the test suite
## Basic Provider Usage
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):
@code
QMdnsEngine::Server server;
QMdnsEngine::Hostname hostname(&server);
QMdnsEngine::Provider provider(&server, &hostname);
@endcode
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.
The next step is to create the service and update the provider:
@code
QMdnsEngine::Service service;
service.setType("_http._tcp.local.");
service.setName("My Service");
service.setPort(1234);
provider.update(service);
@endcode
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.
## Basic Browser Usage
To find services on the local network, begin by creating a [Server](@ref QMdnsEngine::Server) and a [Browser](@ref QMdnsEngine::Browser):
@code
QMdnsEngine::Server server;
QMdnsEngine::Cache cache;
QMdnsEngine::Browser browser(&server, "_http._tcp.local.", &cache);
@endcode
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.
To receive a notification when services are added, connect to the [Browser::serviceAdded()](@ref QMdnsEngine::Browser::serviceAdded) signal:
@code
QObject::connect(&browser, &QMdnsEngine::Browser::serviceAdded,
[](const QMdnsEngine::Service &service) {
qDebug() << service.name() << "discovered!";
}
);
@endcode
To resolve the service, use a [Resolver](@ref QMdnsEngine::Resolver):
@code
QMdnsEngine::Resolver resolver(&server, service.hostname(), &cache);
QObject::connect(&resolver, &QMdnsEngine::Resolver::resolved,
[](const QHostAddress &address) {
qDebug() << "resolved to" << address;
}
);
@endcode
Note that [Resolver::resolved()](@ref QMdnsEngine::Resolver::resolved) may be emitted once for each address provided by the service.

View File

@ -0,0 +1,34 @@
#titlearea {
margin: 10px 0;
}
#projectalign {
padding-left: 0 !important;
}
.fragment {
padding: 4px !important;
}
@media (min-width: 992px) {
#top, .header, .contents, .footer {
margin: auto !important;
max-width: 900px;
}
#titlearea {
border-bottom: none;
}
#main-nav, #navrow1 {
border-radius: 4px 4px 0 0;
border-top: 1px solid #c4cfe5;
position: relative;
}
#main-nav, #navrow1, .header, .nav-path, .contents {
border-left: 1px solid #c4cfe5;
border-right: 1px solid #c4cfe5;
box-sizing: border-box;
}
.contents {
padding: 14px;
}
hr.footer {
border-top: 1px solid #c4cfe5;
}
}

View File

@ -0,0 +1,13 @@
set(EXAMPLES
browser
provider
)
foreach(EXAMPLE ${EXAMPLES})
set(EXAMPLE_DIR "${EXAMPLES_INSTALL_DIR}/${EXAMPLE}")
add_subdirectory(${EXAMPLE})
install(DIRECTORY ${EXAMPLE}
DESTINATION "${EXAMPLES_INSTALL_DIR}"
COMPONENT examples
)
endforeach()

View File

@ -0,0 +1,18 @@
set(SRC
browser.cpp
mainwindow.cpp
servicemodel.cpp
)
add_executable(browser WIN32 ${SRC})
set_target_properties(browser PROPERTIES
CXX_STANDARD 11
)
target_link_libraries(browser qmdnsengine Qt${QT_VERSION_MAJOR}::Widgets)
install(TARGETS browser
RUNTIME DESTINATION "${EXAMPLE_DIR}"
COMPONENT examples
)

View File

@ -0,0 +1,37 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return app.exec();
}

View File

@ -0,0 +1,145 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include <QCheckBox>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLineEdit>
#include <QListView>
#include <QListWidget>
#include <QPushButton>
#include <QSplitter>
#include <QTableWidget>
#include <QVBoxLayout>
#include <QWidget>
#include <qmdnsengine/mdns.h>
#include <qmdnsengine/service.h>
#include "mainwindow.h"
#include "servicemodel.h"
Q_DECLARE_METATYPE(QMdnsEngine::Service)
MainWindow::MainWindow()
: mServiceModel(nullptr),
mResolver(nullptr)
{
setWindowTitle(tr("mDNS Browser"));
resize(640, 480);
mServiceType = new QLineEdit(tr("_http._tcp.local."));
mStartStop = new QPushButton(tr("Browse"));
mServices = new QListView;
mAddresses = new QListWidget;
mAttributes = new QTableWidget;
mAttributes->setSelectionBehavior(QAbstractItemView::SelectRows);
QVBoxLayout *rootLayout = new QVBoxLayout;
QWidget *widget = new QWidget;
widget->setLayout(rootLayout);
setCentralWidget(widget);
QCheckBox *any = new QCheckBox(tr("Any"));
QHBoxLayout *typeLayout = new QHBoxLayout;
typeLayout->addWidget(mServiceType, 1);
typeLayout->addWidget(any);
typeLayout->addWidget(mStartStop);
rootLayout->addLayout(typeLayout);
QSplitter *vSplitter = new QSplitter;
vSplitter->setOrientation(Qt::Vertical);
vSplitter->addWidget(mAddresses);
vSplitter->addWidget(mAttributes);
QSplitter *hSplitter = new QSplitter;
hSplitter->addWidget(mServices);
hSplitter->addWidget(vSplitter);
QHBoxLayout *servicesLayout = new QHBoxLayout;
servicesLayout->addWidget(hSplitter);
rootLayout->addLayout(servicesLayout);
connect(any, &QCheckBox::toggled, this, &MainWindow::onToggled);
connect(mStartStop, &QPushButton::clicked, this, &MainWindow::onClicked);
}
void MainWindow::onToggled(bool checked)
{
if (checked) {
mServiceType->setText(QMdnsEngine::MdnsBrowseType);
}
mServiceType->setEnabled(!checked);
}
void MainWindow::onClicked()
{
if (mServiceModel) {
mServices->setModel(nullptr);
delete mServiceModel;
mAttributes->clear();
mAttributes->setColumnCount(0);
}
mServiceModel = new ServiceModel(&mServer, mServiceType->text().toUtf8());
mServices->setModel(mServiceModel);
connect(mServices->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::onSelectionChanged);
}
void MainWindow::onSelectionChanged(const QItemSelection &selected, const QItemSelection &)
{
mAddresses->clear();
mAttributes->clear();
mAttributes->setColumnCount(0);
if (mResolver) {
delete mResolver;
mResolver = nullptr;
}
if (selected.count()) {
auto service = mServiceModel->data(selected.at(0).topLeft(), Qt::UserRole).value<QMdnsEngine::Service>();
// Show TXT values
auto attributes = service.attributes();
mAttributes->setRowCount(attributes.keys().count());
mAttributes->setColumnCount(2);
mAttributes->setHorizontalHeaderLabels({tr("Key"), tr("Value")});
mAttributes->horizontalHeader()->setStretchLastSection(true);
mAttributes->verticalHeader()->setVisible(false);
int row = 0;
for (auto i = attributes.constBegin(); i != attributes.constEnd(); ++i, ++row) {
mAttributes->setItem(row, 0, new QTableWidgetItem(QString(i.key())));
mAttributes->setItem(row, 1, new QTableWidgetItem(QString(i.value())));
}
// Resolve the address
mResolver = new QMdnsEngine::Resolver(&mServer, service.hostname(), nullptr, this);
connect(mResolver, &QMdnsEngine::Resolver::resolved, [this](const QHostAddress &address) {
mAddresses->addItem(address.toString());
});
}
}

View File

@ -0,0 +1,70 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <qmdnsengine/server.h>
#include <qmdnsengine/resolver.h>
class QItemSelection;
class QLineEdit;
class QListView;
class QListWidget;
class QPushButton;
class QTableWidget;
class ServiceModel;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
private Q_SLOTS:
void onToggled(bool checked);
void onClicked();
void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
private:
QMdnsEngine::Server mServer;
ServiceModel *mServiceModel;
QLineEdit *mServiceType;
QPushButton *mStartStop;
QListView *mServices;
QListWidget *mAddresses;
QTableWidget *mAttributes;
QMdnsEngine::Resolver *mResolver;
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,97 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include "servicemodel.h"
Q_DECLARE_METATYPE(QMdnsEngine::Service)
ServiceModel::ServiceModel(QMdnsEngine::Server *server, const QByteArray &type)
: mBrowser(server, type, &cache)
{
connect(&mBrowser, &QMdnsEngine::Browser::serviceAdded, this, &ServiceModel::onServiceAdded);
connect(&mBrowser, &QMdnsEngine::Browser::serviceUpdated, this, &ServiceModel::onServiceUpdated);
connect(&mBrowser, &QMdnsEngine::Browser::serviceRemoved, this, &ServiceModel::onServiceRemoved);
}
int ServiceModel::rowCount(const QModelIndex &) const
{
return mServices.count();
}
QVariant ServiceModel::data(const QModelIndex &index, int role) const
{
// Ensure the index points to a valid row
if (!index.isValid() || index.row() < 0 || index.row() >= mServices.count()) {
return QVariant();
}
QMdnsEngine::Service service = mServices.at(index.row());
switch (role) {
case Qt::DisplayRole:
return QString("%1 (%2)")
.arg(QString(service.name()))
.arg(QString(service.type()));
case Qt::UserRole:
return QVariant::fromValue(service);
}
return QVariant();
}
void ServiceModel::onServiceAdded(const QMdnsEngine::Service &service)
{
beginInsertRows(QModelIndex(), mServices.count(), mServices.count());
mServices.append(service);
endInsertRows();
}
void ServiceModel::onServiceUpdated(const QMdnsEngine::Service &service)
{
int i = findService(service.name());
if (i != -1) {
mServices.replace(i, service);
emit dataChanged(index(i), index(i));
}
}
void ServiceModel::onServiceRemoved(const QMdnsEngine::Service &service)
{
int i = findService(service.name());
if (i != -1) {
beginRemoveRows(QModelIndex(), i, i);
mServices.removeAt(i);
endRemoveRows();
}
}
int ServiceModel::findService(const QByteArray &name)
{
for (auto i = mServices.constBegin(); i != mServices.constEnd(); ++i) {
if ((*i).name() == name) {
return i - mServices.constBegin();
}
}
return -1;
}

View File

@ -0,0 +1,62 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef SERVICEMODEL_H
#define SERVICEMODEL_H
#include <QAbstractListModel>
#include <QList>
#include <qmdnsengine/browser.h>
#include <qmdnsengine/cache.h>
#include <qmdnsengine/service.h>
#include <qmdnsengine/server.h>
class ServiceModel : public QAbstractListModel
{
Q_OBJECT
public:
ServiceModel(QMdnsEngine::Server *server, const QByteArray &type);
virtual int rowCount(const QModelIndex &parent) const;
virtual QVariant data(const QModelIndex &index, int role) const;
private Q_SLOTS:
void onServiceAdded(const QMdnsEngine::Service &service);
void onServiceUpdated(const QMdnsEngine::Service &service);
void onServiceRemoved(const QMdnsEngine::Service &service);
private:
int findService(const QByteArray &name);
QMdnsEngine::Cache cache;
QMdnsEngine::Browser mBrowser;
QList<QMdnsEngine::Service> mServices;
};
#endif // SERVICEMODEL_H

Binary file not shown.

View File

@ -0,0 +1,13 @@
set(SRC
mainwindow.cpp
provider.cpp
)
add_executable(provider WIN32 ${SRC})
target_link_libraries(provider qmdnsengine Qt${QT_VERSION_MAJOR}::Widgets)
install(TARGETS provider
RUNTIME DESTINATION "${EXAMPLE_DIR}"
COMPONENT examples
)

View File

@ -0,0 +1,133 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include <QCheckBox>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QIntValidator>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
#include <qmdnsengine/dns.h>
#include <qmdnsengine/query.h>
#include <qmdnsengine/service.h>
#include "mainwindow.h"
MainWindow::MainWindow()
: mHostname(&mServer),
mProvider(0),
mServiceName(new QLineEdit(tr("Test Service"))),
mServiceType(new QLineEdit(tr("_test._tcp.local."))),
mServicePort(new QLineEdit("1234")),
mButton(new QPushButton(buttonCaption())),
mShowQueries(new QCheckBox(tr("Show queries"))),
mLog(new QTextEdit(tr("Initializing application")))
{
setWindowTitle(tr("mDNS Provider"));
resize(640, 480);
mServicePort->setValidator(new QIntValidator(1, 65535, this));
mLog->setReadOnly(true);
// Create the root layout
QVBoxLayout *rootLayout = new QVBoxLayout;
QWidget *widget = new QWidget;
widget->setLayout(rootLayout);
setCentralWidget(widget);
// Create the horizontal layout for the grid and buttons
QHBoxLayout *upperLayout = new QHBoxLayout;
rootLayout->addLayout(upperLayout);
// Create the grid layout for the line edits
QGridLayout *gridLayout = new QGridLayout;
gridLayout->addWidget(new QLabel(tr("Service name:")), 0, 0);
gridLayout->addWidget(mServiceName, 0, 1);
gridLayout->addWidget(new QLabel(tr("Service type:")), 1, 0);
gridLayout->addWidget(mServiceType, 1, 1);
gridLayout->addWidget(new QLabel(tr("Service port:")), 2, 0);
gridLayout->addWidget(mServicePort, 2, 1);
upperLayout->addLayout(gridLayout);
// Create the layout for the buttons
QVBoxLayout *buttonLayout = new QVBoxLayout;
buttonLayout->addWidget(mButton);
buttonLayout->addWidget(mShowQueries);
buttonLayout->addWidget(new QWidget, 1);
upperLayout->addLayout(buttonLayout);
// Add the log
rootLayout->addWidget(mLog, 1);
connect(mButton, &QPushButton::clicked, this, &MainWindow::onClicked);
connect(&mHostname, &QMdnsEngine::Hostname::hostnameChanged, this, &MainWindow::onHostnameChanged);
connect(&mServer, &QMdnsEngine::Server::messageReceived, this, &MainWindow::onMessageReceived);
}
void MainWindow::onClicked()
{
if (mProvider) {
mLog->append(tr("Destroying provider"));
delete mProvider;
mProvider = 0;
} else {
mLog->append(tr("Creating provider"));
QMdnsEngine::Service service;
service.setName(mServiceName->text().toUtf8());
service.setType(mServiceType->text().toUtf8());
service.setPort(mServicePort->text().toUShort());
mProvider = new QMdnsEngine::Provider(&mServer, &mHostname, this);
mProvider->update(service);
}
mButton->setText(buttonCaption());
}
void MainWindow::onHostnameChanged(const QByteArray &hostname)
{
mLog->append(tr("Hostname changed to %1").arg(QString(hostname)));
}
void MainWindow::onMessageReceived(const QMdnsEngine::Message &message)
{
if (!mShowQueries->isChecked()) {
return;
}
const auto queries = message.queries();
for (const QMdnsEngine::Query &query : queries) {
mLog->append(
tr("[%1] %2")
.arg(QMdnsEngine::typeName(query.type()))
.arg(QString(query.name()))
);
}
}
QString MainWindow::buttonCaption() const
{
return mProvider ? tr("Stop") : tr("Start");
}

View File

@ -0,0 +1,72 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <qmdnsengine/server.h>
#include <qmdnsengine/hostname.h>
#include <qmdnsengine/message.h>
#include <qmdnsengine/provider.h>
class QCheckBox;
class QPushButton;
class QLineEdit;
class QTextEdit;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
private Q_SLOTS:
void onClicked();
void onHostnameChanged(const QByteArray &hostname);
void onMessageReceived(const QMdnsEngine::Message &message);
private:
QString buttonCaption() const;
QMdnsEngine::Server mServer;
QMdnsEngine::Hostname mHostname;
QMdnsEngine::Provider *mProvider;
QLineEdit *mServiceName;
QLineEdit *mServiceType;
QLineEdit *mServicePort;
QPushButton *mButton;
QCheckBox *mShowQueries;
QTextEdit *mLog;
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,37 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return app.exec();
}

View File

@ -0,0 +1,98 @@
configure_file(qmdnsengine_export.h.in "${CMAKE_CURRENT_BINARY_DIR}/qmdnsengine_export.h")
set(HEADERS
include/qmdnsengine/abstractserver.h
include/qmdnsengine/bitmap.h
include/qmdnsengine/browser.h
include/qmdnsengine/cache.h
include/qmdnsengine/dns.h
include/qmdnsengine/hostname.h
include/qmdnsengine/mdns.h
include/qmdnsengine/message.h
include/qmdnsengine/prober.h
include/qmdnsengine/provider.h
include/qmdnsengine/query.h
include/qmdnsengine/record.h
include/qmdnsengine/resolver.h
include/qmdnsengine/server.h
include/qmdnsengine/service.h
"${CMAKE_CURRENT_BINARY_DIR}/qmdnsengine_export.h"
)
set(SRC
src/abstractserver.cpp
src/bitmap.cpp
src/browser.cpp
src/cache.cpp
src/dns.cpp
src/hostname.cpp
src/mdns.cpp
src/message.cpp
src/prober.cpp
src/provider.cpp
src/query.cpp
src/record.cpp
src/resolver.cpp
src/server.cpp
src/service.cpp
)
if(WIN32)
configure_file(resource.rc.in "${CMAKE_CURRENT_BINARY_DIR}/resource.rc")
set(SRC ${SRC} "${CMAKE_CURRENT_BINARY_DIR}/resource.rc")
endif()
add_library(qmdnsengine ${HEADERS} ${SRC})
if ( "${QT_VERSION}" VERSION_GREATER_EQUAL "6.6.0" )
message( STATUS "QMdnsEngine: Enable C++17 for Qt 6.6 and newer" )
set_target_properties(qmdnsengine PROPERTIES
CXX_STANDARD 17
)
else()
set_target_properties(qmdnsengine PROPERTIES
CXX_STANDARD 11
)
endif()
set_target_properties(qmdnsengine PROPERTIES
CXX_STANDARD_REQUIRED ON
DEFINE_SYMBOL QT_NO_SIGNALS_SLOTS_KEYWORDS
DEFINE_SYMBOL QT_NO_FOREACH
DEFINE_SYMBOL QMDNSENGINE_LIBRARY
PUBLIC_HEADER "${HEADERS}"
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
)
target_include_directories(qmdnsengine PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
"$<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>"
)
target_link_libraries(qmdnsengine Qt${QT_VERSION_MAJOR}::Network)
install(TARGETS qmdnsengine
EXPORT qmdnsengine-export
RUNTIME DESTINATION "${BIN_INSTALL_DIR}"
LIBRARY DESTINATION "${LIB_INSTALL_DIR}"
ARCHIVE DESTINATION "${LIB_INSTALL_DIR}"
PUBLIC_HEADER DESTINATION "${INCLUDE_INSTALL_DIR}/qmdnsengine"
)
install(EXPORT qmdnsengine-export
FILE qmdnsengineConfig.cmake
DESTINATION "${LIB_INSTALL_DIR}/cmake/qmdnsengine"
)
include(CMakePackageConfigHelpers)
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/qmdnsengineConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qmdnsengineConfigVersion.cmake"
DESTINATION "${LIB_INSTALL_DIR}/cmake/qmdnsengine"
)

View File

@ -0,0 +1,88 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_ABSTRACTSERVER_H
#define QMDNSENGINE_ABSTRACTSERVER_H
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class Message;
/**
* @brief Base class for sending and receiving DNS messages
*
* Many of the other classes in this library require the ability to send and
* receive DNS messages. By having them use this base class, they become far
* easier to test. Any class derived from this one that implements the pure
* virtual methods can be used for sending and receiving DNS messages.
*/
class QMDNSENGINE_EXPORT AbstractServer : public QObject
{
Q_OBJECT
public:
/**
* @brief Abstract constructor
*/
explicit AbstractServer(QObject *parent = 0);
/**
* @brief Send a message to its provided destination
*
* The message should be sent over the IP protocol specified in the
* message and to the target address and port specified in the message.
*/
virtual void sendMessage(const Message &message) = 0;
/**
* @brief Send a message to the multicast address on each interface
*
* The message should be sent over both IPv4 and IPv6 on all interfaces.
*/
virtual void sendMessageToAll(const Message &message) = 0;
Q_SIGNALS:
/**
* @brief Indicate that a DNS message was received
* @param message newly received message
*/
void messageReceived(const Message &message);
/**
* @brief Indicate that an error has occurred
* @param message brief description of the error
*/
void error(const QString &message);
};
}
#endif // QMDNSENGINE_ABSTRACTSERVER_H

View File

@ -0,0 +1,100 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_BITMAP_H
#define QMDNSENGINE_BITMAP_H
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class QMDNSENGINE_EXPORT BitmapPrivate;
/**
* @brief 256-bit bitmap
*
* Bitmaps are used in QMdnsEngine::NSEC records to indicate which records are
* available. Bitmaps in mDNS records use only the first block (block 0).
*/
class QMDNSENGINE_EXPORT Bitmap
{
public:
/**
* @brief Create an empty bitmap
*/
Bitmap();
/**
* @brief Create a copy of an existing bitmap
*/
Bitmap(const Bitmap &other);
/**
* @brief Assignment operator
*/
Bitmap &operator=(const Bitmap &other);
/**
* @brief Equality operator
*/
bool operator==(const Bitmap &other);
/**
* @brief Destroy the bitmap
*/
virtual ~Bitmap();
/**
* @brief Retrieve the length of the block in bytes
*
* This method indicates how many bytes are pointed to by the data()
* method.
*/
quint8 length() const;
/**
* @brief Retrieve a pointer to the underlying data in the bitmap
*
* Use the length() method to determine how many bytes contain valid data.
*/
const quint8 *data() const;
/**
* @brief Set the data to be stored in the bitmap
*
* The length parameter indicates how many bytes of data are valid. The
* actual bytes are copied to the bitmap.
*/
void setData(quint8 length, const quint8 *data);
private:
BitmapPrivate *const d;
};
}
#endif // QMDNSENGINE_BITMAP_H

View File

@ -0,0 +1,122 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_BROWSER_H
#define QMDNSENGINE_BROWSER_H
#include <QByteArray>
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class AbstractServer;
class Cache;
class Service;
class QMDNSENGINE_EXPORT BrowserPrivate;
/**
* @brief %Browser for local services
*
* This class provides a simple way to discover services on the local network.
* A cache may be provided in the constructor to store records for future
* queries.
*
* To browse for services of any type:
*
* @code
* QMdnsEngine::Browser browser(&server, QMdnsEngine::MdnsBrowseType);
* @endcode
*
* To browse for services of a specific type:
*
* @code
* QMdnsEngine::Browser browser(&server, "_http._tcp.local.");
* @endcode
*
* When a service is found, the serviceAdded() signal is emitted:
*
* @code
* connect(&browser, &QMdnsEngine::Browser::serviceAdded, [](const QMdnsEngine::Service &service) {
* qDebug() << "Service added:" << service.name();
* });
* @endcode
*
* The serviceUpdated() and serviceRemoved() signals are emitted when services
* are updated (their properties change) or are removed, respectively.
*/
class QMDNSENGINE_EXPORT Browser : public QObject
{
Q_OBJECT
public:
/**
* @brief Create a new browser instance
* @param server server to use for receiving and sending mDNS messages
* @param type service type to browse for
* @param cache DNS cache to use or null to create one
* @param parent QObject
*/
Browser(AbstractServer *server, const QByteArray &type, Cache *cache = 0, QObject *parent = 0);
Q_SIGNALS:
/**
* @brief Indicate that a new service has been added
*
* This signal is emitted when the PTR and SRV records for a service are
* received. If TXT records are received later, the serviceUpdated()
* signal will be emitted.
*/
void serviceAdded(const Service &service);
/**
* @brief Indicate that the specified service was updated
*
* This signal is emitted when the SRV record for a service (identified by
* its name and type) or a TXT record has changed.
*/
void serviceUpdated(const Service &service);
/**
* @brief Indicate that the specified service was removed
*
* This signal is emitted when an essential record (PTR or SRV) is
* expiring from the cache. This will also occur when an updated PTR or
* SRV record is received with a TTL of 0.
*/
void serviceRemoved(const Service &service);
private:
BrowserPrivate *const d;
};
}
#endif // QMDNSENGINE_BROWSER_H

View File

@ -0,0 +1,132 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_CACHE_H
#define QMDNSENGINE_CACHE_H
#include <QList>
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class Record;
class QMDNSENGINE_EXPORT CachePrivate;
/**
* @brief %Cache for DNS records
*
* Records are added to the cache using the addRecord() method which are then
* stored in the cache until they are considered to have expired, at which
* point they are purged. The shouldQuery() signal is used to indicate when a
* record is approaching expiry and the recordExpired() signal indicates when
* a record has expired (at which point it is removed).
*
* The cache can be queried to retrieve one or more records matching a given
* type. For example, to retrieve all TXT records that match a given name:
*
* @code
* Cache cache;
*
* QList<QMdnsEngine::Record> records;
* cache.lookupRecords("My Service._http._tcp.local.", QMdnsEngine::TXT, records);
*
* for (const QMdnsEngine::Record &record : records) {
* qDebug() << "Record:" << record.name();
* }
* @endcode
*
* Alternatively, lookupRecord() can be used to find a single record.
*/
class QMDNSENGINE_EXPORT Cache : public QObject
{
Q_OBJECT
public:
/**
* @brief Create an empty cache.
*/
explicit Cache(QObject *parent = 0);
/**
* @brief Add a record to the cache
* @param record add this record to the cache
*
* The TTL for the record will be added to the current time to calculate
* when the record expires. Existing records of the same name and type
* will be replaced, resetting their expiration.
*/
void addRecord(const Record &record);
/**
* @brief Retrieve a single record from the cache
* @param name name of record to retrieve or null for any
* @param type type of record to retrieve or ANY for all types
* @param record storage for the record retrieved
* @return true if a record was retrieved
*
* Some record types allow multiple records to be stored with identical
* names and types. This method will only retrieve the first matching
* record. Use lookupRecords() to obtain all of the records.
*/
bool lookupRecord(const QByteArray &name, quint16 type, Record &record) const;
/**
* @brief Retrieve multiple records from the cache
* @param name name of records to retrieve or null for any
* @param type type of records to retrieve or ANY for all types
* @param records storage for the records retrieved
* @return true if records were retrieved
*/
bool lookupRecords(const QByteArray &name, quint16 type, QList<Record> &records) const;
Q_SIGNALS:
/**
* @brief Indicate that a record will expire soon and a new query should be issued
* @param record reference to the record that will soon expire
*
* This signal is emitted when a record reaches approximately 50%, 85%,
* 90%, and 95% of its lifetime.
*/
void shouldQuery(const Record &record);
/**
* @brief Indicate that the specified record expired
* @param record reference to the record that has expired
*/
void recordExpired(const Record &record);
private:
CachePrivate *const d;
};
}
#endif // QMDNSENGINE_CACHE_H

View File

@ -0,0 +1,123 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_DNS_H
#define QMDNSENGINE_DNS_H
#include <QByteArray>
#include <QMap>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class Message;
class Record;
enum {
/// IPv4 address record
A = 1,
/// IPv6 address record
AAAA = 28,
/// Wildcard for cache lookups
ANY = 255,
/// List of records
NSEC = 47,
/// Pointer to hostname
PTR = 12,
/// %Service information
SRV = 33,
/// Arbitrary metadata
TXT = 16
};
/**
* @brief Parse a name from a raw DNS packet
* @param packet raw DNS packet data
* @param offset offset into the packet where the name begins
* @param name reference to QByteArray to store the name in
* @return true if no errors occurred
*
* The offset will be incremented by the number of bytes read. Name
* compression requires access to the contents of the packet.
*/
QMDNSENGINE_EXPORT bool parseName(const QByteArray &packet, quint16 &offset, QByteArray &name);
/**
* @brief Write a name to a raw DNS packet
* @param packet raw DNS packet to write to
* @param offset offset to update with the number of bytes written
* @param name name to write to the packet
* @param nameMap map of names already written to their offsets
*
* The offset will be incremented by the number of bytes read. The name map
* will be updated with offsets of any names written so that it can be passed
* to future invocations of this function.
*/
QMDNSENGINE_EXPORT void writeName(QByteArray &packet, quint16 &offset, const QByteArray &name, QMap<QByteArray, quint16> &nameMap);
/**
* @brief Parse a record from a raw DNS packet
* @param packet raw DNS packet data
* @param offset offset into the packet where the record begins
* @param record reference to Record to populate
* @return true if no errors occurred
*/
QMDNSENGINE_EXPORT bool parseRecord(const QByteArray &packet, quint16 &offset, Record &record);
/**
* @brief Write a record to a raw DNS packet
* @param packet raw DNS packet to write to
* @param offset offset to update with the number of bytes written
* @param record record to write to the packet
* @param nameMap map of names already written to their offsets
*/
QMDNSENGINE_EXPORT void writeRecord(QByteArray &packet, quint16 &offset, Record &record, QMap<QByteArray, quint16> &nameMap);
/**
* @brief Populate a Message with data from a raw DNS packet
* @param packet raw DNS packet data
* @param message reference to Message to populate
* @return true if no errors occurred
*/
QMDNSENGINE_EXPORT bool fromPacket(const QByteArray &packet, Message &message);
/**
* @brief Create a raw DNS packet from a Message
* @param message Message to create the packet from
* @param packet storage for raw DNS packet
*/
QMDNSENGINE_EXPORT void toPacket(const Message &message, QByteArray &packet);
/**
* @brief Retrieve the string representation of a DNS type
* @param type integer type
* @return human-readable name for the type
*/
QMDNSENGINE_EXPORT QString typeName(quint16 type);
}
#endif // QMDNSENGINE_DNS_H

View File

@ -0,0 +1,95 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_HOSTNAME_H
#define QMDNSENGINE_HOSTNAME_H
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class AbstractServer;
class QMDNSENGINE_EXPORT HostnamePrivate;
/**
* @brief %Hostname reserved for exclusive use
*
* In order to provide services on the local network, a unique hostname must
* be used. This class asserts a hostname (by first confirming that it is not
* in use) and then responds to A and AAAA queries for the hostname.
*
* @code
* QMdnsEngine::Hostname hostname(&server);
*
* connect(&hostname, &QMdnsEngine::Hostname::hostnameChanged, [](const QByteArray &hostname) {
* qDebug() << "New hostname:" << hostname;
* });
* @endcode
*/
class QMDNSENGINE_EXPORT Hostname : public QObject
{
Q_OBJECT
public:
/**
* @brief Create a new hostname
*/
Hostname(AbstractServer *server, QObject *parent = 0);
/**
* @brief Determine if a hostname has been registered
*
* A hostname is not considered registered until a probe for the desired
* name has been completed and no matching records were received.
*/
bool isRegistered() const;
/**
* @brief Retrieve the current hostname
*
* This value is only valid when isRegistered() returns true.
*/
QByteArray hostname() const;
Q_SIGNALS:
/**
* @brief Indicate that the current hostname has changed
* @param hostname new hostname
*/
void hostnameChanged(const QByteArray &hostname);
private:
HostnamePrivate *const d;
};
}
#endif // QMDNSENGINE_HOSTNAME_H

View File

@ -0,0 +1,58 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_MDNS_H
#define QMDNSENGINE_MDNS_H
#include <QByteArray>
#include <QHostAddress>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
/**
* @brief Standard port for mDNS
*/
QMDNSENGINE_EXPORT extern const quint16 MdnsPort;
/**
* @brief Standard IPv4 address for mDNS
*/
QMDNSENGINE_EXPORT extern const QHostAddress MdnsIpv4Address;
/**
* @brief Standard IPv6 address for mDNS
*/
QMDNSENGINE_EXPORT extern const QHostAddress MdnsIpv6Address;
/**
* @brief Service type for browsing service types
*/
QMDNSENGINE_EXPORT extern const QByteArray MdnsBrowseType;
}
#endif // QMDNSENGINE_MDNS_H

View File

@ -0,0 +1,191 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_MESSAGE_H
#define QMDNSENGINE_MESSAGE_H
#include <QHostAddress>
#include <QList>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class Query;
class Record;
class QMDNSENGINE_EXPORT MessagePrivate;
/**
* @brief DNS message
*
* A DNS message consists of a header and zero or more queries and records.
* Instances of this class are created and initialized by
* [AbstractServer](@ref QMdnsEngine::AbstractServer) when messages are
* received from the network.
*
* If a message is being constructed in reply to one received from the
* network, the reply() method can be used to simplify initialization:
*
* @code
* connect(&server, &QMdnsEngine::Server::messageReceived, [](const QMdnsEngine::Message &message) {
* QMdnsEngine::Message reply;
* reply.reply(message);
* server.sendMessage(reply);
* });
* @endcode
*/
class QMDNSENGINE_EXPORT Message
{
public:
/**
* @brief Create an empty message
*/
Message();
/**
* @brief Create a copy of an existing message
*/
Message(const Message &other);
/**
* @brief Assignment operator
*/
Message &operator=(const Message &other);
/**
* @brief Destroy the message
*/
virtual ~Message();
/**
* @brief Retrieve the address for the message
*
* When receiving messages, this is the address that the message was
* received from.
*/
QHostAddress address() const;
/**
* @brief Set the address for the message
*
* When sending messages, this is the address that the message will be
* sent to. QMdnsEngine::MdnsIpv4Address and QMdnsEngine::MdnsIpv6Address
* can be used for mDNS messages.
*/
void setAddress(const QHostAddress &address);
/**
* @brief Retrieve the port for the message
*
* When receiving messages, this is the port that the message was received
* from. For traditional queries, this will be an ephemeral port. For mDNS
* queries, this will always equal QMdnsEngine::MdnsPort.
*/
quint16 port() const;
/**
* @brief Set the port for the message
*
* When sending messages, this is the port that the message will be sent
* to. This should be set to QMdnsEngine::MdnsPort unless the message is a
* reply to a traditional DNS query.
*/
void setPort(quint16 port);
/**
* @brief Retrieve the transaction ID for the message
*
* This is always set to 1 for mDNS messages. Traditional DNS queries may
* set this to an arbitrary integer.
*/
quint16 transactionId() const;
/**
* @brief Set the transaction ID for the message
*
* The default transaction ID is 0. This value should not be changed
* unless responding to a traditional DNS query.
*/
void setTransactionId(quint16 transactionId);
/**
* @brief Determine if the message is a response
*/
bool isResponse() const;
/**
* @brief Set whether the message is a response
*/
void setResponse(bool isResponse);
/**
* @brief Determine if the message is truncated
*/
bool isTruncated() const;
/**
* @brief Set whether the message is truncated
*/
void setTruncated(bool isTruncated);
/**
* @brief Retrieve a list of queries in the message
*/
QList<Query> queries() const;
/**
* @brief Add a query to the message
*/
void addQuery(const Query &query);
/**
* @brief Retrieve a list of records in the message
*/
QList<Record> records() const;
/**
* @brief Add a record to the message
*/
void addRecord(const Record &record);
/**
* @brief Reply to another message
*
* The message will be correctly initialized to respond to the other
* message. This includes setting the target address, port, and
* transaction ID.
*/
void reply(const Message &other);
private:
MessagePrivate *const d;
};
}
#endif // QMDNSENGINE_MESSAGE_H

View File

@ -0,0 +1,88 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_PROBER_H
#define QMDNSENGINE_PROBER_H
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class AbstractServer;
class Record;
class QMDNSENGINE_EXPORT ProberPrivate;
/**
* @brief %Prober to confirm that a record is unique
*
* Before responding to queries for a record, its uniqueness on the network
* must be confirmed. This class takes care of probing for existing records
* that match and adjusts the record's name until a unique one is found.
*
* For example, to probe for a SRV record:
*
* @code
* QMdnsEngine::Record record;
* record.setName("My Service._http._tcp.local.");
* record.setType(QMdnsEngine::SRV);
* record.setPort(1234);
* record.setTarget(hostname.hostname());
*
* QMdnsEngine::Prober prober(&server, record);
* connect(&prober, &QMdnsEngine::Prober::nameConfirmed, [](const QByteArray &name) {
* qDebug() << "Name confirmed:" << name;
* });
* @endcode
*/
class QMDNSENGINE_EXPORT Prober : public QObject
{
Q_OBJECT
public:
/**
* @brief Create a new prober
*/
Prober(AbstractServer *server, const Record &record, QObject *parent = 0);
Q_SIGNALS:
/**
* @brief Indicate that the name has been confirmed unique
* @param name that was confirmed to be unique
*/
void nameConfirmed(const QByteArray &name);
private:
ProberPrivate *const d;
};
}
#endif // QMDNSENGINE_PROBER_H

View File

@ -0,0 +1,90 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Nathan Osman
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/
#ifndef QMDNSENGINE_PROVIDER_H
#define QMDNSENGINE_PROVIDER_H
#include <QObject>
#include "qmdnsengine_export.h"
namespace QMdnsEngine
{
class AbstractServer;
class Hostname;
class Service;
class QMDNSENGINE_EXPORT ProviderPrivate;
/**
* @brief %Provider for a single mDNS service
*
* This class provide a [Service](@ref QMdnsEngine::Service) on the local
* network by responding to the appropriate DNS queries. A hostname is
* required for creating the SRV record.
*
* The provider needs to be given a reference to the service through the
* update() method so that it can construct DNS records:
*
* @code
* QMdnsEngine::Service service;
* service.setType("_http._tcp.local.");
* service.setName("My Service");
* service.setPort(1234);
*
* QMdnsEngine::Provider provider;
* provider.update(service);
* @endcode
*
* This method can also be used to update the provider's records.
*/
class QMDNSENGINE_EXPORT Provider : public QObject
{
Q_OBJECT
public:
/**
* @brief Create a new service provider
*/
Provider(AbstractServer *server, Hostname *hostname, QObject *parent = 0);
/**
* @brief Update the service with the provided information
* @param service updated service description
*
* This class will not respond to any DNS queries until the hostname is
* confirmed and this method is called.
*/
void update(const Service &service);
private:
ProviderPrivate *const d;
};
}
#endif // QMDNSENGINE_PROVIDER_H

Some files were not shown because too many files have changed in this diff Show More