Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / lib / mdns / minimal / ResponseSender.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 "ResponseSender.h"
19
20 #include "QueryReplyFilter.h"
21
22 #include <system/SystemClock.h>
23
24 #define RETURN_IF_ERROR(err)                                                                                                       \
25     do                                                                                                                             \
26     {                                                                                                                              \
27         if (err != CHIP_NO_ERROR)                                                                                                  \
28         {                                                                                                                          \
29             return;                                                                                                                \
30         }                                                                                                                          \
31     } while (false)
32
33 namespace mdns {
34 namespace Minimal {
35
36 namespace {
37
38 constexpr uint16_t kMdnsStandardPort = 5353;
39
40 // Restriction for UDP packets:  https://tools.ietf.org/html/rfc1035#section-4.2.1
41 //
42 //    Messages carried by UDP are restricted to 512 bytes (not counting the IP
43 //    or UDP headers).  Longer messages are truncated and the TC bit is set in
44 //    the header.
45 constexpr uint16_t kPacketSizeBytes = 512;
46
47 } // namespace
48 namespace Internal {
49
50 bool ResponseSendingState::SendUnicast() const
51 {
52     return mQuery->RequestedUnicastAnswer() || (mSource->SrcPort != kMdnsStandardPort);
53 }
54
55 bool ResponseSendingState::IncludeQuery() const
56 {
57     return (mSource->SrcPort != kMdnsStandardPort);
58 }
59
60 } // namespace Internal
61
62 CHIP_ERROR ResponseSender::Respond(uint32_t messageId, const QueryData & query, const chip::Inet::IPPacketInfo * querySource)
63 {
64     mSendState.Reset(messageId, query, querySource);
65
66     // Responder has a stateful 'additional replies required' that is used within the response
67     // loop. 'no additionals required' is set at the start and additionals are marked as the query
68     // reply is built.
69     mResponder->ResetAdditionals();
70
71     // send all 'Answer' replies
72     {
73         const uint64_t kTimeNowMs = chip::System::Platform::Layer::GetClock_MonotonicMS();
74
75         QueryReplyFilter queryReplyFilter(query);
76         QueryResponderRecordFilter responseFilter;
77
78         responseFilter.SetReplyFilter(&queryReplyFilter);
79
80         if (!mSendState.SendUnicast())
81         {
82             // According to https://tools.ietf.org/html/rfc6762#section-6  we should multicast at most 1/sec
83             //
84             // TODO: the 'last sent' value does NOT track the interface we used to send, so this may cause
85             //       broadcasts on one interface to throttle broadcasts on another interface.
86             constexpr uint64_t kOneSecondMs = 1000;
87             responseFilter.SetIncludeOnlyMulticastBeforeMS(kTimeNowMs - kOneSecondMs);
88         }
89
90         for (auto it = mResponder->begin(&responseFilter); it != mResponder->end(); it++)
91         {
92             it->responder->AddAllResponses(querySource, this);
93             ReturnErrorOnFailure(mSendState.GetError());
94
95             mResponder->MarkAdditionalRepliesFor(it);
96
97             if (!mSendState.SendUnicast())
98             {
99                 it->lastMulticastTime = kTimeNowMs;
100             }
101         }
102     }
103
104     // send all 'Additional' replies
105     {
106         mSendState.SetResourceType(ResourceType::kAdditional);
107
108         QueryReplyFilter queryReplyFilter(query);
109
110         queryReplyFilter.SetIgnoreNameMatch(true).SetSendingAdditionalItems(true);
111
112         QueryResponderRecordFilter responseFilter;
113         responseFilter
114             .SetReplyFilter(&queryReplyFilter) //
115             .SetIncludeAdditionalRepliesOnly(true);
116
117         for (auto it = mResponder->begin(&responseFilter); it != mResponder->end(); it++)
118         {
119             it->responder->AddAllResponses(querySource, this);
120             ReturnErrorOnFailure(mSendState.GetError());
121         }
122     }
123
124     return FlushReply();
125 }
126
127 CHIP_ERROR ResponseSender::FlushReply()
128 {
129     ReturnErrorCodeIf(!mResponseBuilder.HasPacketBuffer(), CHIP_NO_ERROR); // nothing to flush
130
131     if (mResponseBuilder.HasResponseRecords())
132     {
133
134         if (mSendState.SendUnicast())
135         {
136             ChipLogProgress(Discovery, "Directly sending mDns reply to peer on port %d", mSendState.GetSourcePort());
137             ReturnErrorOnFailure(mServer->DirectSend(mResponseBuilder.ReleasePacket(), mSendState.GetSourceAddress(),
138                                                      mSendState.GetSourcePort(), mSendState.GetSourceInterfaceId()));
139         }
140         else
141         {
142             ChipLogProgress(Discovery, "Broadcasting mDns reply");
143             ReturnErrorOnFailure(
144                 mServer->BroadcastSend(mResponseBuilder.ReleasePacket(), kMdnsStandardPort, mSendState.GetSourceInterfaceId()));
145         }
146     }
147
148     return CHIP_NO_ERROR;
149 }
150
151 CHIP_ERROR ResponseSender::PrepareNewReplyPacket()
152 {
153     chip::System::PacketBufferHandle buffer = chip::System::PacketBufferHandle::New(kPacketSizeBytes);
154     ReturnErrorCodeIf(buffer.IsNull(), CHIP_ERROR_NO_MEMORY);
155
156     mResponseBuilder.Reset(std::move(buffer));
157     mResponseBuilder.Header().SetMessageId(mSendState.GetMessageId());
158
159     if (mSendState.IncludeQuery())
160     {
161         mResponseBuilder.AddQuery(*mSendState.GetQuery());
162     }
163
164     return CHIP_NO_ERROR;
165 }
166
167 void ResponseSender::AddResponse(const ResourceRecord & record)
168 {
169     RETURN_IF_ERROR(mSendState.GetError());
170
171     if (!mResponseBuilder.HasPacketBuffer())
172     {
173         mSendState.SetError(PrepareNewReplyPacket());
174         RETURN_IF_ERROR(mSendState.GetError());
175     }
176
177     if (!mResponseBuilder.Ok())
178     {
179         mSendState.SetError(CHIP_ERROR_INCORRECT_STATE);
180         return;
181     }
182
183     mResponseBuilder.AddRecord(mSendState.GetResourceType(), record);
184
185     // ResponseBuilder AddRecord will only fail if insufficient space is available (or at least this is
186     // the assumption here). It also guarantees that existing data and header are unchanged on
187     // failure, hence we can flush and try again. This allows for split replies.
188     if (!mResponseBuilder.Ok())
189     {
190         mResponseBuilder.Header().SetFlags(mResponseBuilder.Header().GetFlags().SetTruncated(true));
191
192         RETURN_IF_ERROR(mSendState.SetError(FlushReply()));
193         RETURN_IF_ERROR(mSendState.SetError(PrepareNewReplyPacket()));
194
195         mResponseBuilder.AddRecord(mSendState.GetResourceType(), record);
196         if (!mResponseBuilder.Ok())
197         {
198             // Very much unexpected: single record addtion should fit (our records should not be that big).
199             ChipLogError(Discovery, "Failed to add single record to mDNS response.");
200             mSendState.SetError(CHIP_ERROR_INTERNAL);
201         }
202     }
203 }
204
205 } // namespace Minimal
206 } // namespace mdns