/src/connectedhomeip/src/lib/dnssd/minimal_mdns/Parser.cpp
Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Copyright (c) 2020 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 "Parser.h" |
19 | | |
20 | | #include "Query.h" |
21 | | |
22 | | #include <stdio.h> |
23 | | |
24 | | namespace mdns { |
25 | | namespace Minimal { |
26 | | |
27 | | bool QueryData::Parse(const BytesRange & validData, const uint8_t ** start) |
28 | 697 | { |
29 | | // Structure is: |
30 | | // QNAME |
31 | | // TYPE |
32 | | // CLASS (plus a flag for unicast) |
33 | | |
34 | 697 | if (!validData.Contains(*start)) |
35 | 22 | { |
36 | 22 | return false; |
37 | 22 | } |
38 | | |
39 | 675 | const uint8_t * nameEnd = nullptr; |
40 | 675 | { |
41 | 675 | SerializedQNameIterator it(validData, *start); |
42 | 675 | nameEnd = it.FindDataEnd(); |
43 | 675 | } |
44 | 675 | if (nameEnd == nullptr) |
45 | 26 | { |
46 | 26 | return false; |
47 | 26 | } |
48 | | |
49 | 649 | if (!validData.Contains(nameEnd + 3)) |
50 | 17 | { |
51 | 17 | return false; |
52 | 17 | } |
53 | | |
54 | | // TODO: should there be checks for valid mType/class? |
55 | | |
56 | 632 | mType = static_cast<QType>(chip::Encoding::BigEndian::Read16(nameEnd)); |
57 | | |
58 | 632 | uint16_t klass = chip::Encoding::BigEndian::Read16(nameEnd); |
59 | | |
60 | 632 | mAnswerViaUnicast = (klass & kQClassUnicastAnswerFlag) != 0; |
61 | 632 | mClass = static_cast<QClass>(klass & ~kQClassUnicastAnswerFlag); |
62 | 632 | mNameIterator = SerializedQNameIterator(validData, *start); |
63 | | |
64 | 632 | *start = nameEnd; |
65 | | |
66 | 632 | return true; |
67 | 649 | } |
68 | | |
69 | | bool QueryData::Append(HeaderRef & hdr, RecordWriter & out) const |
70 | 0 | { |
71 | 0 | if ((hdr.GetAdditionalCount() != 0) || (hdr.GetAnswerCount() != 0) || (hdr.GetAuthorityCount() != 0)) |
72 | 0 | { |
73 | 0 | return false; |
74 | 0 | } |
75 | | |
76 | 0 | out.WriteQName(GetName()) |
77 | 0 | .Put16(static_cast<uint16_t>(mType)) |
78 | 0 | .Put16(static_cast<uint16_t>(static_cast<uint16_t>(mClass) | (mAnswerViaUnicast ? kQClassUnicastAnswerFlag : 0))); |
79 | |
|
80 | 0 | if (!out.Fit()) |
81 | 0 | { |
82 | 0 | return false; |
83 | 0 | } |
84 | | |
85 | 0 | hdr.SetQueryCount(static_cast<uint16_t>(hdr.GetQueryCount() + 1)); |
86 | 0 | return true; |
87 | 0 | } |
88 | | |
89 | | bool ResourceData::Parse(const BytesRange & validData, const uint8_t ** start) |
90 | 1.92k | { |
91 | | // Structure is: |
92 | | // QNAME |
93 | | // TYPE (16 bit) |
94 | | // CLASS (16 bit) |
95 | | // TTL (32 bit) |
96 | | // RDLENGTH (16 bit) |
97 | | // <DATA> (RDLENGTH bytes) |
98 | 1.92k | if (!validData.Contains(*start)) |
99 | 121 | { |
100 | 121 | return false; |
101 | 121 | } |
102 | | |
103 | 1.80k | const uint8_t * nameEnd = nullptr; |
104 | | |
105 | 1.80k | { |
106 | 1.80k | SerializedQNameIterator it(validData, *start); |
107 | 1.80k | nameEnd = it.FindDataEnd(); |
108 | 1.80k | } |
109 | 1.80k | if (nameEnd == nullptr) |
110 | 14 | { |
111 | 14 | return false; |
112 | 14 | } |
113 | | |
114 | | // need 3*u16 + u32 |
115 | 1.79k | if (!validData.Contains(nameEnd + 9)) |
116 | 25 | { |
117 | 25 | return false; |
118 | 25 | } |
119 | | |
120 | 1.76k | mType = static_cast<QType>(chip::Encoding::BigEndian::Read16(nameEnd)); |
121 | 1.76k | mClass = static_cast<QClass>(chip::Encoding::BigEndian::Read16(nameEnd)); |
122 | 1.76k | mTtl = chip::Encoding::BigEndian::Read32(nameEnd); |
123 | | |
124 | 1.76k | uint16_t dataLen = chip::Encoding::BigEndian::Read16(nameEnd); // resource data |
125 | | |
126 | 1.76k | if (!validData.Contains(nameEnd + dataLen - 1)) |
127 | 16 | { |
128 | 16 | return false; // no space for RDATA |
129 | 16 | } |
130 | 1.75k | mData = BytesRange(nameEnd, nameEnd + dataLen); |
131 | | |
132 | 1.75k | mNameIterator = SerializedQNameIterator(validData, *start); |
133 | | |
134 | 1.75k | *start = nameEnd + dataLen; |
135 | | |
136 | 1.75k | return true; |
137 | 1.76k | } |
138 | | |
139 | | bool ParsePacket(const BytesRange & packetData, ParserDelegate * delegate) |
140 | 364 | { |
141 | 364 | if (packetData.Size() < static_cast<ptrdiff_t>(HeaderRef::kSizeBytes)) |
142 | 31 | { |
143 | 31 | return false; |
144 | 31 | } |
145 | | |
146 | | // header is used as const, so cast is safe |
147 | 333 | ConstHeaderRef header(packetData.Start()); |
148 | | |
149 | 333 | if (!header.GetFlags().IsValidMdns()) |
150 | 43 | { |
151 | 43 | return false; |
152 | 43 | } |
153 | | |
154 | | // Reject packets with unreasonable record counts to prevent CPU exhaustion. |
155 | | // An mDNS packet is at most ~9000 bytes; the smallest record is ~12 bytes, |
156 | | // so 256 is a generous upper bound for any single section. |
157 | 290 | static constexpr uint16_t kMaxRecordCount = 256; |
158 | | |
159 | 290 | if (header.GetQueryCount() > kMaxRecordCount || header.GetAnswerCount() > kMaxRecordCount || |
160 | 267 | header.GetAuthorityCount() > kMaxRecordCount || header.GetAdditionalCount() > kMaxRecordCount) |
161 | 47 | { |
162 | 47 | return false; |
163 | 47 | } |
164 | | |
165 | 243 | delegate->OnHeader(header); |
166 | | |
167 | 243 | const uint8_t * data = packetData.Start() + HeaderRef::kSizeBytes; |
168 | | |
169 | 243 | { |
170 | 243 | QueryData queryData; |
171 | 875 | for (uint16_t i = 0; i < header.GetQueryCount(); i++) |
172 | 697 | { |
173 | 697 | if (!queryData.Parse(packetData, &data)) |
174 | 65 | { |
175 | 65 | return false; |
176 | 65 | } |
177 | | |
178 | 632 | delegate->OnQuery(queryData); |
179 | 632 | } |
180 | 243 | } |
181 | | |
182 | 178 | { |
183 | 178 | ResourceData resourceData; |
184 | 848 | for (uint16_t i = 0; i < header.GetAnswerCount(); i++) |
185 | 751 | { |
186 | 751 | if (!resourceData.Parse(packetData, &data)) |
187 | 81 | { |
188 | 81 | return false; |
189 | 81 | } |
190 | | |
191 | 670 | delegate->OnResource(ResourceType::kAnswer, resourceData); |
192 | 670 | } |
193 | | |
194 | 704 | for (uint16_t i = 0; i < header.GetAuthorityCount(); i++) |
195 | 661 | { |
196 | 661 | if (!resourceData.Parse(packetData, &data)) |
197 | 54 | { |
198 | 54 | return false; |
199 | 54 | } |
200 | | |
201 | 607 | delegate->OnResource(ResourceType::kAuthority, resourceData); |
202 | 607 | } |
203 | | |
204 | 519 | for (uint16_t i = 0; i < header.GetAdditionalCount(); i++) |
205 | 517 | { |
206 | 517 | if (!resourceData.Parse(packetData, &data)) |
207 | 41 | { |
208 | 41 | return false; |
209 | 41 | } |
210 | | |
211 | 476 | delegate->OnResource(ResourceType::kAdditional, resourceData); |
212 | 476 | } |
213 | 43 | } |
214 | | |
215 | 2 | return true; |
216 | 43 | } |
217 | | |
218 | | } // namespace Minimal |
219 | | } // namespace mdns |