Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / examples / minimal-mdns / client.cpp
1 /*
2  *
3  *    Copyright (c) 2020-2021 Project CHIP Authors
4  *
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>
20 #include <vector>
21
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>
32
33 #include "AllInterfaceListener.h"
34 #include "PacketReporter.h"
35
36 using namespace chip;
37
38 namespace {
39
40 struct Options
41 {
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;
49 } gOptions;
50
51 constexpr uint32_t kTestMessageId   = 0x1234;
52 constexpr size_t kMdnsMaxPacketSize = 1'024;
53
54 using namespace chip::ArgParser;
55
56 constexpr uint16_t kOptionEnableIpV4 = '4';
57 constexpr uint16_t kOptionQuery      = 'q';
58 constexpr uint16_t kOptionType       = 't';
59
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;
65
66 bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue)
67 {
68     switch (aIdentifier)
69     {
70     case kOptionEnableIpV4:
71         gOptions.enableIpV4 = true;
72         return true;
73
74     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;
84     case kOptionType:
85         if (strcasecmp(aValue, "ANY") == 0)
86         {
87             gOptions.type = mdns::Minimal::QType::ANY;
88         }
89         else if (strcasecmp(aValue, "A") == 0)
90         {
91             gOptions.type = mdns::Minimal::QType::A;
92         }
93         else if (strcasecmp(aValue, "AAAA") == 0)
94         {
95             gOptions.type = mdns::Minimal::QType::AAAA;
96         }
97         else if (strcasecmp(aValue, "PTR") == 0)
98         {
99             gOptions.type = mdns::Minimal::QType::PTR;
100         }
101         else if (strcasecmp(aValue, "TXT") == 0)
102         {
103             gOptions.type = mdns::Minimal::QType::TXT;
104         }
105         else if (strcasecmp(aValue, "SRV") == 0)
106         {
107             gOptions.type = mdns::Minimal::QType::SRV;
108         }
109         else if (strcasecmp(aValue, "CNAME") == 0)
110         {
111             gOptions.type = mdns::Minimal::QType::CNAME;
112         }
113         else
114         {
115             PrintArgError("%s: invalid value for query type: %s\n", aProgram, aValue);
116             return false;
117         }
118         return true;
119
120     case kOptionQueryPort:
121         if (!ParseInt(aValue, gOptions.querySendPort))
122         {
123             PrintArgError("%s: invalid value for query send port: %s\n", aProgram, aValue);
124             return false;
125         }
126         return true;
127
128     case kOptionRuntimeMs:
129         if (!ParseInt(aValue, gOptions.runtimeMs))
130         {
131             PrintArgError("%s: invalid value for runtime ms: %s\n", aProgram, aValue);
132             return false;
133         }
134         return true;
135
136     case kOptionMulticastReplies:
137         gOptions.unicastAnswers = false;
138         return true;
139
140     default:
141         PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", aProgram, aName);
142         return false;
143     }
144 }
145
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 },
154     nullptr,
155 };
156
157 OptionSet cmdLineOptions = { HandleOptions, cmdLineOptionsDef, "PROGRAM OPTIONS",
158                              "  --listen-port <number>\n"
159                              "        The port number to listen on\n"
160                              "  -4\n"
161                              "  --enable-ip-v4\n"
162                              "        enable listening on IPv4\n"
163                              "  -q\n"
164                              "  --query\n"
165                              "        The query to send\n"
166                              "  -t\n"
167                              "  --type\n"
168                              "        The query type to ask for (ANY/PTR/A/AAAA/SRV/TXT)\n"
169                              "  --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"
175                              "\n" };
176
177 HelpOptions helpOptions("minimal-mdns-client", "Usage: minimal-mdns-client [options]", "1.0");
178
179 OptionSet * allOptions[] = { &cmdLineOptions, &helpOptions, nullptr };
180
181 class ReportDelegate : public mdns::Minimal::ServerDelegate
182 {
183 public:
184     void OnQuery(const mdns::Minimal::BytesRange & data, const chip::Inet::IPPacketInfo * info) override
185     {
186         char addr[32];
187         info->SrcAddress.ToString(addr, sizeof(addr));
188
189         printf("QUERY from: %-15s on port %d, via interface %d\n", addr, info->SrcPort, info->Interface);
190         Report("QUERY: ", data);
191     }
192
193     void OnResponse(const mdns::Minimal::BytesRange & data, const chip::Inet::IPPacketInfo * info) override
194     {
195         char addr[32];
196         info->SrcAddress.ToString(addr, sizeof(addr));
197
198         printf("RESPONSE from: %-15s on port %d, via interface %d\n", addr, info->SrcPort, info->Interface);
199         Report("RESPONSE: ", data);
200     }
201
202 private:
203     void Report(const char * prefix, const mdns::Minimal::BytesRange & data)
204     {
205         MdnsExample::PacketReporter reporter(prefix, data);
206         if (!mdns::Minimal::ParsePacket(data, &reporter))
207         {
208             printf("INVALID PACKET!!!!!!\n");
209         }
210     }
211 };
212
213 class QuerySplitter
214 {
215 public:
216     void Split(const char * query)
217     {
218         mStorage.clear();
219         mParts.clear();
220
221         const char * dot = nullptr;
222         while (nullptr != (dot = strchr(query, '.')))
223         {
224             mStorage.push_back(std::string(query, dot));
225             query = dot + 1;
226         }
227
228         mStorage.push_back(query);
229
230         for (unsigned i = 0; i < mStorage.size(); i++)
231         {
232             mParts.push_back(mStorage[i].c_str());
233         }
234     }
235
236     mdns::Minimal::Query MdnsQuery() const
237     {
238         mdns::Minimal::FullQName qName;
239
240         qName.nameCount = mParts.size();
241         qName.names     = mParts.data();
242
243         return mdns::Minimal::Query(qName);
244     }
245
246 private:
247     std::vector<mdns::Minimal::QNamePart> mParts;
248     std::vector<std::string> mStorage;
249 };
250
251 void BroadcastPacket(mdns::Minimal::ServerBase * server)
252 {
253     System::PacketBufferHandle buffer = System::PacketBufferHandle::New(kMdnsMaxPacketSize);
254     if (buffer.IsNull())
255     {
256         printf("Buffer allocation failure.");
257         abort();
258         return;
259     }
260
261     QuerySplitter query;
262     query.Split(gOptions.query);
263
264     mdns::Minimal::QueryBuilder builder(std::move(buffer));
265
266     builder.Header().SetMessageId(kTestMessageId);
267     builder.AddQuery(query
268                          .MdnsQuery()                                  //
269                          .SetClass(mdns::Minimal::QClass::IN)          //
270                          .SetType(gOptions.type)                       //
271                          .SetAnswerViaUnicast(gOptions.unicastAnswers) //
272     );
273
274     if (!builder.Ok())
275     {
276         printf("Failed to build the question");
277         return;
278     }
279
280     if (server->BroadcastSend(builder.ReleasePacket(), gOptions.querySendPort) != CHIP_NO_ERROR)
281     {
282         printf("Error sending\n");
283         return;
284     }
285 }
286
287 } // namespace
288
289 int main(int argc, char ** args)
290 {
291     if (Platform::MemoryInit() != CHIP_NO_ERROR)
292     {
293         printf("FAILED to initialize memory");
294         return 1;
295     }
296
297     if (DeviceLayer::PlatformMgr().InitChipStack() != CHIP_NO_ERROR)
298     {
299         printf("FAILED to initialize chip stack");
300         return 1;
301     }
302
303     if (!chip::ArgParser::ParseArgs(args[0], argc, args, allOptions))
304     {
305         return 1;
306     }
307
308     printf("Running...\n");
309
310     mdns::Minimal::Server<10> mdnsServer;
311     ReportDelegate reporter;
312
313     mdnsServer.SetDelegate(&reporter);
314
315     {
316         MdnsExample::AllInterfaces allInterfaces(gOptions.enableIpV4);
317
318         if (mdnsServer.Listen(&chip::DeviceLayer::InetLayer, &allInterfaces, gOptions.listenPort) != CHIP_NO_ERROR)
319         {
320             printf("Server failed to listen on all interfaces\n");
321             return 1;
322         }
323     }
324
325     BroadcastPacket(&mdnsServer);
326
327     System::Timer * timer = nullptr;
328
329     if (DeviceLayer::SystemLayer.NewTimer(timer) == CHIP_NO_ERROR)
330     {
331         timer->Start(
332             gOptions.runtimeMs, [](System::Layer *, void *, System::Error err) { DeviceLayer::PlatformMgr().Shutdown(); }, nullptr);
333     }
334     else
335     {
336
337         printf("Failed to create the shutdown timer. Kill with ^C.\n");
338     }
339
340     DeviceLayer::PlatformMgr().RunEventLoop();
341
342     printf("Done...\n");
343     return 0;
344 }