3 * Copyright (c) 2020-2021 Project CHIP Authors
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include <inet/InetInterface.h>
23 #include <inet/UDPEndPoint.h>
24 #include <mdns/minimal/QueryBuilder.h>
25 #include <mdns/minimal/Server.h>
26 #include <mdns/minimal/core/QName.h>
27 #include <platform/CHIPDeviceLayer.h>
28 #include <support/CHIPArgParser.hpp>
29 #include <support/CHIPMem.h>
30 #include <system/SystemPacketBuffer.h>
31 #include <system/SystemTimer.h>
33 #include "AllInterfaceListener.h"
34 #include "PacketReporter.h"
42 bool enableIpV4 = false;
43 bool unicastAnswers = true;
44 uint32_t runtimeMs = 500;
45 uint16_t querySendPort = 5353;
46 uint16_t listenPort = 5388;
47 const char * query = "_services._dns-sd._udp.local";
48 mdns::Minimal::QType type = mdns::Minimal::QType::ANY;
51 constexpr uint32_t kTestMessageId = 0x1234;
52 constexpr size_t kMdnsMaxPacketSize = 1'024;
54 using namespace chip::ArgParser;
56 constexpr uint16_t kOptionEnableIpV4 = '4';
57 constexpr uint16_t kOptionQuery = 'q';
58 constexpr uint16_t kOptionType = 't';
60 // non-ascii options have no short option version
61 constexpr uint16_t kOptionListenPort = 0x100;
62 constexpr uint16_t kOptionQueryPort = 0x101;
63 constexpr uint16_t kOptionRuntimeMs = 0x102;
64 constexpr uint16_t kOptionMulticastReplies = 0x103;
66 bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue)
70 case kOptionEnableIpV4:
71 gOptions.enableIpV4 = true;
74 case kOptionListenPort:
75 if (!ParseInt(aValue, gOptions.listenPort))
77 PrintArgError("%s: invalid value for listen port: %s\n", aProgram, aValue);
82 gOptions.query = aValue;
85 if (strcasecmp(aValue, "ANY") == 0)
87 gOptions.type = mdns::Minimal::QType::ANY;
89 else if (strcasecmp(aValue, "A") == 0)
91 gOptions.type = mdns::Minimal::QType::A;
93 else if (strcasecmp(aValue, "AAAA") == 0)
95 gOptions.type = mdns::Minimal::QType::AAAA;
97 else if (strcasecmp(aValue, "PTR") == 0)
99 gOptions.type = mdns::Minimal::QType::PTR;
101 else if (strcasecmp(aValue, "TXT") == 0)
103 gOptions.type = mdns::Minimal::QType::TXT;
105 else if (strcasecmp(aValue, "SRV") == 0)
107 gOptions.type = mdns::Minimal::QType::SRV;
109 else if (strcasecmp(aValue, "CNAME") == 0)
111 gOptions.type = mdns::Minimal::QType::CNAME;
115 PrintArgError("%s: invalid value for query type: %s\n", aProgram, aValue);
120 case kOptionQueryPort:
121 if (!ParseInt(aValue, gOptions.querySendPort))
123 PrintArgError("%s: invalid value for query send port: %s\n", aProgram, aValue);
128 case kOptionRuntimeMs:
129 if (!ParseInt(aValue, gOptions.runtimeMs))
131 PrintArgError("%s: invalid value for runtime ms: %s\n", aProgram, aValue);
136 case kOptionMulticastReplies:
137 gOptions.unicastAnswers = false;
141 PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", aProgram, aName);
146 OptionDef cmdLineOptionsDef[] = {
147 { "listen-port", kArgumentRequired, kOptionListenPort },
148 { "enable-ip-v4", kNoArgument, kOptionEnableIpV4 },
149 { "query", kArgumentRequired, kOptionQuery },
150 { "type", kArgumentRequired, kOptionType },
151 { "query-port", kArgumentRequired, kOptionQueryPort },
152 { "timeout-ms", kArgumentRequired, kOptionRuntimeMs },
153 { "multicast-reply", kNoArgument, kOptionMulticastReplies },
157 OptionSet cmdLineOptions = { HandleOptions, cmdLineOptionsDef, "PROGRAM OPTIONS",
158 " --listen-port <number>\n"
159 " The port number to listen on\n"
162 " enable listening on IPv4\n"
165 " The query to send\n"
168 " The query type to ask for (ANY/PTR/A/AAAA/SRV/TXT)\n"
170 " On what port to multicast the query\n"
172 " How long to wait for replies\n"
173 " --multicast-reply\n"
174 " Do not request unicast replies\n"
177 HelpOptions helpOptions("minimal-mdns-client", "Usage: minimal-mdns-client [options]", "1.0");
179 OptionSet * allOptions[] = { &cmdLineOptions, &helpOptions, nullptr };
181 class ReportDelegate : public mdns::Minimal::ServerDelegate
184 void OnQuery(const mdns::Minimal::BytesRange & data, const chip::Inet::IPPacketInfo * info) override
187 info->SrcAddress.ToString(addr, sizeof(addr));
189 printf("QUERY from: %-15s on port %d, via interface %d\n", addr, info->SrcPort, info->Interface);
190 Report("QUERY: ", data);
193 void OnResponse(const mdns::Minimal::BytesRange & data, const chip::Inet::IPPacketInfo * info) override
196 info->SrcAddress.ToString(addr, sizeof(addr));
198 printf("RESPONSE from: %-15s on port %d, via interface %d\n", addr, info->SrcPort, info->Interface);
199 Report("RESPONSE: ", data);
203 void Report(const char * prefix, const mdns::Minimal::BytesRange & data)
205 MdnsExample::PacketReporter reporter(prefix, data);
206 if (!mdns::Minimal::ParsePacket(data, &reporter))
208 printf("INVALID PACKET!!!!!!\n");
216 void Split(const char * query)
221 const char * dot = nullptr;
222 while (nullptr != (dot = strchr(query, '.')))
224 mStorage.push_back(std::string(query, dot));
228 mStorage.push_back(query);
230 for (unsigned i = 0; i < mStorage.size(); i++)
232 mParts.push_back(mStorage[i].c_str());
236 mdns::Minimal::Query MdnsQuery() const
238 mdns::Minimal::FullQName qName;
240 qName.nameCount = mParts.size();
241 qName.names = mParts.data();
243 return mdns::Minimal::Query(qName);
247 std::vector<mdns::Minimal::QNamePart> mParts;
248 std::vector<std::string> mStorage;
251 void BroadcastPacket(mdns::Minimal::ServerBase * server)
253 System::PacketBufferHandle buffer = System::PacketBufferHandle::New(kMdnsMaxPacketSize);
256 printf("Buffer allocation failure.");
262 query.Split(gOptions.query);
264 mdns::Minimal::QueryBuilder builder(std::move(buffer));
266 builder.Header().SetMessageId(kTestMessageId);
267 builder.AddQuery(query
269 .SetClass(mdns::Minimal::QClass::IN) //
270 .SetType(gOptions.type) //
271 .SetAnswerViaUnicast(gOptions.unicastAnswers) //
276 printf("Failed to build the question");
280 if (server->BroadcastSend(builder.ReleasePacket(), gOptions.querySendPort) != CHIP_NO_ERROR)
282 printf("Error sending\n");
289 int main(int argc, char ** args)
291 if (Platform::MemoryInit() != CHIP_NO_ERROR)
293 printf("FAILED to initialize memory");
297 if (DeviceLayer::PlatformMgr().InitChipStack() != CHIP_NO_ERROR)
299 printf("FAILED to initialize chip stack");
303 if (!chip::ArgParser::ParseArgs(args[0], argc, args, allOptions))
308 printf("Running...\n");
310 mdns::Minimal::Server<10> mdnsServer;
311 ReportDelegate reporter;
313 mdnsServer.SetDelegate(&reporter);
316 MdnsExample::AllInterfaces allInterfaces(gOptions.enableIpV4);
318 if (mdnsServer.Listen(&chip::DeviceLayer::InetLayer, &allInterfaces, gOptions.listenPort) != CHIP_NO_ERROR)
320 printf("Server failed to listen on all interfaces\n");
325 BroadcastPacket(&mdnsServer);
327 System::Timer * timer = nullptr;
329 if (DeviceLayer::SystemLayer.NewTimer(timer) == CHIP_NO_ERROR)
332 gOptions.runtimeMs, [](System::Layer *, void *, System::Error err) { DeviceLayer::PlatformMgr().Shutdown(); }, nullptr);
337 printf("Failed to create the shutdown timer. Kill with ^C.\n");
340 DeviceLayer::PlatformMgr().RunEventLoop();