blob: 81d2b6754b72506b536e485a2784fe316c9079c0 [file] [log] [blame]
Andrei Litvin056d0b32020-11-16 13:58:58 -05001/*
2 *
Kevin Schoedela8681872021-01-28 15:53:13 -05003 * Copyright (c) 2020-2021 Project CHIP Authors
Andrei Litvin056d0b32020-11-16 13:58:58 -05004 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <cstdio>
19#include <memory>
Michael Spang2b6cc312021-04-16 11:03:36 -040020#include <string>
Andrei Litvin00c7f5a2020-11-23 10:43:42 -050021#include <vector>
Andrei Litvin056d0b32020-11-16 13:58:58 -050022
Andrei Litvin00b522f2023-06-13 11:32:16 -040023#include <TracingCommandLineArgument.h>
Andrei Litvin056d0b32020-11-16 13:58:58 -050024#include <inet/InetInterface.h>
25#include <inet/UDPEndPoint.h>
Andrei Litvinc52f7662022-09-12 13:23:45 -040026#include <lib/dnssd/MinimalMdnsServer.h>
Andrei Litvin0bc15782022-08-29 09:28:41 -040027#include <lib/dnssd/minimal_mdns/AddressPolicy.h>
Kamil Kasperczykd9e02a02021-10-12 09:19:23 +020028#include <lib/dnssd/minimal_mdns/QueryBuilder.h>
29#include <lib/dnssd/minimal_mdns/Server.h>
30#include <lib/dnssd/minimal_mdns/core/QName.h>
Zang MingJie53dd5832021-09-03 03:05:16 +080031#include <lib/support/CHIPArgParser.hpp>
32#include <lib/support/CHIPMem.h>
Andrei Litvin056d0b32020-11-16 13:58:58 -050033#include <platform/CHIPDeviceLayer.h>
Andrei Litvin056d0b32020-11-16 13:58:58 -050034#include <system/SystemPacketBuffer.h>
Andrei Litvin056d0b32020-11-16 13:58:58 -050035
Andrei Litvin00c7f5a2020-11-23 10:43:42 -050036#include "PacketReporter.h"
37
Andrei Litvin056d0b32020-11-16 13:58:58 -050038using namespace chip;
39
40namespace {
41
Andrei Litvin00c7f5a2020-11-23 10:43:42 -050042struct Options
43{
Andrei Litvin2f73cb92020-12-07 10:36:50 -050044 bool unicastAnswers = true;
45 uint32_t runtimeMs = 500;
46 uint16_t querySendPort = 5353;
47 uint16_t listenPort = 5388;
48 const char * query = "_services._dns-sd._udp.local";
49 mdns::Minimal::QType type = mdns::Minimal::QType::ANY;
Andrei Litvin00c7f5a2020-11-23 10:43:42 -050050} gOptions;
Andrei Litvin056d0b32020-11-16 13:58:58 -050051
Andrei Litvinab572892022-05-11 14:21:01 -040052constexpr uint32_t kTestMessageId = 0;
Andrei Litvin056d0b32020-11-16 13:58:58 -050053constexpr size_t kMdnsMaxPacketSize = 1'024;
54
Andrei Litvin00c7f5a2020-11-23 10:43:42 -050055using namespace chip::ArgParser;
Andrei Litvin056d0b32020-11-16 13:58:58 -050056
Andrei Litvin0bc15782022-08-29 09:28:41 -040057constexpr uint16_t kOptionQuery = 'q';
58constexpr uint16_t kOptionType = 't';
Andrei Litvin056d0b32020-11-16 13:58:58 -050059
Andrei Litvin00c7f5a2020-11-23 10:43:42 -050060// non-ascii options have no short option version
61constexpr uint16_t kOptionListenPort = 0x100;
62constexpr uint16_t kOptionQueryPort = 0x101;
63constexpr uint16_t kOptionRuntimeMs = 0x102;
64constexpr uint16_t kOptionMulticastReplies = 0x103;
Andrei Litvin00b522f2023-06-13 11:32:16 -040065constexpr uint16_t kOptionTraceTo = 0x104;
Andrei Litvin056d0b32020-11-16 13:58:58 -050066
Andrei Litvind3564842023-06-23 12:30:50 -040067// Only used for argument parsing. Tracing setup owned by the main loop.
68chip::CommandLineApp::TracingSetup * tracing_setup_for_argparse = nullptr;
69
Andrei Litvin58746882021-01-12 11:57:24 -050070bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue)
Andrei Litvin056d0b32020-11-16 13:58:58 -050071{
Andrei Litvin00c7f5a2020-11-23 10:43:42 -050072 switch (aIdentifier)
Andrei Litvin056d0b32020-11-16 13:58:58 -050073 {
Andrei Litvin00c7f5a2020-11-23 10:43:42 -050074 case kOptionListenPort:
75 if (!ParseInt(aValue, gOptions.listenPort))
76 {
77 PrintArgError("%s: invalid value for listen port: %s\n", aProgram, aValue);
78 return false;
79 }
80 return true;
81 case kOptionQuery:
82 gOptions.query = aValue;
83 return true;
Andrei Litvin00b522f2023-06-13 11:32:16 -040084 case kOptionTraceTo:
Andrei Litvind3564842023-06-23 12:30:50 -040085 tracing_setup_for_argparse->EnableTracingFor(aValue);
Andrei Litvin00b522f2023-06-13 11:32:16 -040086 return true;
Andrei Litvin2f73cb92020-12-07 10:36:50 -050087 case kOptionType:
88 if (strcasecmp(aValue, "ANY") == 0)
89 {
90 gOptions.type = mdns::Minimal::QType::ANY;
91 }
92 else if (strcasecmp(aValue, "A") == 0)
93 {
94 gOptions.type = mdns::Minimal::QType::A;
95 }
96 else if (strcasecmp(aValue, "AAAA") == 0)
97 {
98 gOptions.type = mdns::Minimal::QType::AAAA;
99 }
100 else if (strcasecmp(aValue, "PTR") == 0)
101 {
102 gOptions.type = mdns::Minimal::QType::PTR;
103 }
104 else if (strcasecmp(aValue, "TXT") == 0)
105 {
106 gOptions.type = mdns::Minimal::QType::TXT;
107 }
108 else if (strcasecmp(aValue, "SRV") == 0)
109 {
110 gOptions.type = mdns::Minimal::QType::SRV;
111 }
112 else if (strcasecmp(aValue, "CNAME") == 0)
113 {
114 gOptions.type = mdns::Minimal::QType::CNAME;
115 }
116 else
117 {
118 PrintArgError("%s: invalid value for query type: %s\n", aProgram, aValue);
119 return false;
120 }
121 return true;
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500122
123 case kOptionQueryPort:
124 if (!ParseInt(aValue, gOptions.querySendPort))
125 {
126 PrintArgError("%s: invalid value for query send port: %s\n", aProgram, aValue);
127 return false;
128 }
129 return true;
130
131 case kOptionRuntimeMs:
132 if (!ParseInt(aValue, gOptions.runtimeMs))
133 {
134 PrintArgError("%s: invalid value for runtime ms: %s\n", aProgram, aValue);
135 return false;
136 }
137 return true;
138
139 case kOptionMulticastReplies:
140 gOptions.unicastAnswers = false;
141 return true;
142
Andrei Litvin056d0b32020-11-16 13:58:58 -0500143 default:
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500144 PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", aProgram, aName);
145 return false;
Andrei Litvin056d0b32020-11-16 13:58:58 -0500146 }
147}
148
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500149OptionDef cmdLineOptionsDef[] = {
150 { "listen-port", kArgumentRequired, kOptionListenPort },
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500151 { "query", kArgumentRequired, kOptionQuery },
Andrei Litvin2f73cb92020-12-07 10:36:50 -0500152 { "type", kArgumentRequired, kOptionType },
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500153 { "query-port", kArgumentRequired, kOptionQueryPort },
154 { "timeout-ms", kArgumentRequired, kOptionRuntimeMs },
155 { "multicast-reply", kNoArgument, kOptionMulticastReplies },
Andrei Litvin00b522f2023-06-13 11:32:16 -0400156 { "trace-to", kArgumentRequired, kOptionTraceTo },
Michael Spang2b6cc312021-04-16 11:03:36 -0400157 {},
Andrei Litvin056d0b32020-11-16 13:58:58 -0500158};
159
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500160OptionSet cmdLineOptions = { HandleOptions, cmdLineOptionsDef, "PROGRAM OPTIONS",
161 " --listen-port <number>\n"
162 " The port number to listen on\n"
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500163 " -q\n"
164 " --query\n"
165 " The query to send\n"
Andrei Litvin2f73cb92020-12-07 10:36:50 -0500166 " -t\n"
167 " --type\n"
168 " The query type to ask for (ANY/PTR/A/AAAA/SRV/TXT)\n"
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500169 " --query-port\n"
170 " On what port to multicast the query\n"
171 " --timeout-ms\n"
172 " How long to wait for replies\n"
173 " --multicast-reply\n"
174 " Do not request unicast replies\n"
Andrei Litvin00b522f2023-06-13 11:32:16 -0400175 " --trace-to <dest>\n"
176 " trace to the given destination (supported: " SUPPORTED_COMMAND_LINE_TRACING_TARGETS ").\n"
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500177 "\n" };
Andrei Litvin056d0b32020-11-16 13:58:58 -0500178
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500179HelpOptions helpOptions("minimal-mdns-client", "Usage: minimal-mdns-client [options]", "1.0");
180
181OptionSet * allOptions[] = { &cmdLineOptions, &helpOptions, nullptr };
182
183class ReportDelegate : public mdns::Minimal::ServerDelegate
Andrei Litvin056d0b32020-11-16 13:58:58 -0500184{
185public:
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500186 void OnQuery(const mdns::Minimal::BytesRange & data, const chip::Inet::IPPacketInfo * info) override
Andrei Litvin056d0b32020-11-16 13:58:58 -0500187 {
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500188 char addr[32];
189 info->SrcAddress.ToString(addr, sizeof(addr));
190
Andrei Litvin641aacd2021-11-15 08:37:42 -0500191 char ifName[64];
192 VerifyOrDie(info->Interface.GetInterfaceName(ifName, sizeof(ifName)) == CHIP_NO_ERROR);
193
194 printf("QUERY from: %-15s on port %d, via interface %s\n", addr, info->SrcPort, ifName);
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500195 Report("QUERY: ", data);
Andrei Litvin056d0b32020-11-16 13:58:58 -0500196 }
197
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500198 void OnResponse(const mdns::Minimal::BytesRange & data, const chip::Inet::IPPacketInfo * info) override
Andrei Litvin056d0b32020-11-16 13:58:58 -0500199 {
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500200 char addr[32];
201 info->SrcAddress.ToString(addr, sizeof(addr));
Andrei Litvin056d0b32020-11-16 13:58:58 -0500202
Andrei Litvin641aacd2021-11-15 08:37:42 -0500203 char ifName[64];
204 VerifyOrDie(info->Interface.GetInterfaceName(ifName, sizeof(ifName)) == CHIP_NO_ERROR);
205
206 printf("RESPONSE from: %-15s on port %d, via interface %s\n", addr, info->SrcPort, ifName);
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500207 Report("RESPONSE: ", data);
Andrei Litvin056d0b32020-11-16 13:58:58 -0500208 }
209
210private:
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500211 void Report(const char * prefix, const mdns::Minimal::BytesRange & data)
212 {
213 MdnsExample::PacketReporter reporter(prefix, data);
214 if (!mdns::Minimal::ParsePacket(data, &reporter))
215 {
216 printf("INVALID PACKET!!!!!!\n");
217 }
218 }
Andrei Litvin056d0b32020-11-16 13:58:58 -0500219};
220
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500221class QuerySplitter
Andrei Litvin056d0b32020-11-16 13:58:58 -0500222{
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500223public:
224 void Split(const char * query)
225 {
226 mStorage.clear();
227 mParts.clear();
228
229 const char * dot = nullptr;
230 while (nullptr != (dot = strchr(query, '.')))
231 {
232 mStorage.push_back(std::string(query, dot));
233 query = dot + 1;
234 }
235
236 mStorage.push_back(query);
237
238 for (unsigned i = 0; i < mStorage.size(); i++)
239 {
240 mParts.push_back(mStorage[i].c_str());
241 }
242 }
243
Andrei Litvin2f73cb92020-12-07 10:36:50 -0500244 mdns::Minimal::Query MdnsQuery() const
245 {
246 mdns::Minimal::FullQName qName;
247
248 qName.nameCount = mParts.size();
249 qName.names = mParts.data();
250
251 return mdns::Minimal::Query(qName);
252 }
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500253
254private:
255 std::vector<mdns::Minimal::QNamePart> mParts;
256 std::vector<std::string> mStorage;
257};
258
259void BroadcastPacket(mdns::Minimal::ServerBase * server)
260{
Kevin Schoedela8681872021-01-28 15:53:13 -0500261 System::PacketBufferHandle buffer = System::PacketBufferHandle::New(kMdnsMaxPacketSize);
Kevin Schoedel5d905ea2022-01-21 15:03:15 -0500262 VerifyOrDie(!buffer.IsNull());
Andrei Litvin056d0b32020-11-16 13:58:58 -0500263
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500264 QuerySplitter query;
265 query.Split(gOptions.query);
266
Kevin Schoedelf77bebf2021-01-08 12:15:52 -0500267 mdns::Minimal::QueryBuilder builder(std::move(buffer));
Andrei Litvin056d0b32020-11-16 13:58:58 -0500268
269 builder.Header().SetMessageId(kTestMessageId);
Andrei Litvin2f73cb92020-12-07 10:36:50 -0500270 builder.AddQuery(query
271 .MdnsQuery() //
272 .SetClass(mdns::Minimal::QClass::IN) //
273 .SetType(gOptions.type) //
274 .SetAnswerViaUnicast(gOptions.unicastAnswers) //
275 );
Andrei Litvin056d0b32020-11-16 13:58:58 -0500276
277 if (!builder.Ok())
278 {
279 printf("Failed to build the question");
Andrei Litvin056d0b32020-11-16 13:58:58 -0500280 return;
281 }
282
Andrei Litvin8bfdc132021-11-24 13:20:41 -0500283 if (gOptions.unicastAnswers)
Andrei Litvin056d0b32020-11-16 13:58:58 -0500284 {
Andrei Litvin8bfdc132021-11-24 13:20:41 -0500285 if (server->BroadcastUnicastQuery(builder.ReleasePacket(), gOptions.querySendPort) != CHIP_NO_ERROR)
286 {
287 printf("Error sending\n");
288 return;
289 }
290 }
291 else
292 {
293 if (server->BroadcastSend(builder.ReleasePacket(), gOptions.querySendPort) != CHIP_NO_ERROR)
294 {
295 printf("Error sending\n");
296 return;
297 }
Andrei Litvin056d0b32020-11-16 13:58:58 -0500298 }
299}
300
Andrei Litvinfa47b312022-01-11 09:54:45 -0500301mdns::Minimal::Server<20> gMdnsServer;
302
Andrei Litvin056d0b32020-11-16 13:58:58 -0500303} // namespace
304
305int main(int argc, char ** args)
306{
307 if (Platform::MemoryInit() != CHIP_NO_ERROR)
308 {
309 printf("FAILED to initialize memory");
310 return 1;
311 }
312
313 if (DeviceLayer::PlatformMgr().InitChipStack() != CHIP_NO_ERROR)
314 {
315 printf("FAILED to initialize chip stack");
316 return 1;
317 }
318
Andrei Litvind3564842023-06-23 12:30:50 -0400319 chip::CommandLineApp::TracingSetup tracing_setup;
320
321 tracing_setup_for_argparse = &tracing_setup;
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500322 if (!chip::ArgParser::ParseArgs(args[0], argc, args, allOptions))
Andrei Litvin056d0b32020-11-16 13:58:58 -0500323 {
Andrei Litvin056d0b32020-11-16 13:58:58 -0500324 return 1;
325 }
Andrei Litvind3564842023-06-23 12:30:50 -0400326 tracing_setup_for_argparse = nullptr;
Andrei Litvin056d0b32020-11-16 13:58:58 -0500327
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500328 printf("Running...\n");
329
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500330 ReportDelegate reporter;
chrisdecenzo97d9f7e2021-09-14 10:37:29 -0700331 CHIP_ERROR err;
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500332
Andrei Litvinc52f7662022-09-12 13:23:45 -0400333 // This forces the global MDNS instance to be loaded in, effectively setting
334 // built in policies for addresses.
335 (void) chip::Dnssd::GlobalMinimalMdnsServer::Instance();
336
Andrei Litvinfa47b312022-01-11 09:54:45 -0500337 gMdnsServer.SetDelegate(&reporter);
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500338
339 {
Andrei Litvin0bc15782022-08-29 09:28:41 -0400340 auto endpoints = mdns::Minimal::GetAddressPolicy()->GetListenEndpoints();
Andrei Litvinf8043b62021-04-19 12:20:34 -0400341
Andrei Litvin0bc15782022-08-29 09:28:41 -0400342 err = gMdnsServer.Listen(chip::DeviceLayer::UDPEndPointManager(), endpoints.get(), gOptions.listenPort);
Andrei Litvinf8043b62021-04-19 12:20:34 -0400343 if (err != CHIP_NO_ERROR)
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500344 {
Andrei Litvinf8043b62021-04-19 12:20:34 -0400345 printf("Server failed to listen on all interfaces: %s\n", chip::ErrorStr(err));
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500346 return 1;
347 }
348 }
349
Andrei Litvinfa47b312022-01-11 09:54:45 -0500350 BroadcastPacket(&gMdnsServer);
Andrei Litvin00c7f5a2020-11-23 10:43:42 -0500351
chrisdecenzo97d9f7e2021-09-14 10:37:29 -0700352 err = DeviceLayer::SystemLayer().StartTimer(
Andrei Litvin641aacd2021-11-15 08:37:42 -0500353 chip::System::Clock::Milliseconds32(gOptions.runtimeMs),
Kevin Schoedel86eb0402021-09-10 08:37:38 -0400354 [](System::Layer *, void *) {
Andrei Litvinfa47b312022-01-11 09:54:45 -0500355 // Close all sockets BEFORE system layer is shut down, otherwise
356 // attempts to free UDP sockets with system layer down will segfault
357 gMdnsServer.Shutdown();
358
Kevin Schoedel2d103c82021-08-10 22:11:16 -0400359 DeviceLayer::PlatformMgr().StopEventLoopTask();
Kevin Schoedel2d103c82021-08-10 22:11:16 -0400360 },
361 nullptr);
362 if (err != CHIP_NO_ERROR)
Andrei Litvin056d0b32020-11-16 13:58:58 -0500363 {
Kevin Schoedel2d103c82021-08-10 22:11:16 -0400364 printf("Failed to create the shutdown timer. Kill with ^C. %" CHIP_ERROR_FORMAT "\n", err.Format());
Andrei Litvin056d0b32020-11-16 13:58:58 -0500365 }
366
Andrei Litvin056d0b32020-11-16 13:58:58 -0500367 DeviceLayer::PlatformMgr().RunEventLoop();
368
Andrei Litvind3564842023-06-23 12:30:50 -0400369 tracing_setup.StopTracing();
Boris Zbarskya3589672023-04-03 23:02:21 -0400370 DeviceLayer::PlatformMgr().Shutdown();
371 Platform::MemoryShutdown();
372
Andrei Litvin056d0b32020-11-16 13:58:58 -0500373 printf("Done...\n");
374 return 0;
375}