Coverage Report

Created: 2026-07-10 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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