• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 22618661228

02 Mar 2026 09:33PM UTC coverage: 42.258% (-34.4%) from 76.611%
22618661228

push

github

victorjulien
github-actions: bump actions/download-artifact from 7.0.0 to 8.0.0

Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/37930b1c2...70fc10c6e)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

91511 of 216553 relevant lines covered (42.26%)

3416852.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.74
/rust/src/dns/parser.rs
1
/* Copyright (C) 2017 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
//! Nom parsers for DNS.
19

20
use crate::detect::EnumString;
21
use crate::dns::dns::*;
22
use nom8::combinator::rest;
23
use nom8::error::ErrorKind;
24
use nom8::multi::{count, length_data};
25
use nom8::number::streaming::{be_u16, be_u32, be_u8};
26
use nom8::{error_position, Err, IResult, Parser};
27

28
// Set a maximum assembled hostname length of 1025, this value was
29
// chosen as its what DNSMasq uses, a popular DNS server, even if most
30
// tooling limits names to 256 chars without special options.
31
static MAX_NAME_LEN: usize = 1025;
32

33
/// Parse a DNS name.
34
///
35
/// Names are parsed with the following restrictions:
36
///
37
/// - Only 255 segments will be processed, if more the parser may
38
///   error out. This is also our safeguard against an infinite loop. If
39
///   a pointer had been followed a truncated name will be
40
///   returned. However if pointer has been processed we error out as we
41
///   don't know where the next data point starts without more
42
///   iterations.
43
///
44
/// - The maximum name parsed in representation format is MAX_NAME_LEN
45
///   characters. Once larger, the truncated name will be returned with
46
///   a flag specifying the name was truncated. Note that parsing
47
///   continues if no pointer has been used as we still need to find the
48
///   start of the next protocol unit.
49
///
50
/// As some error in parsing the name are recoverable, a DNSName
51
/// object is returned with flags signifying a recoverable
52
/// error. These errors include:
53
///
54
/// - infinite loop: as we know the end of the name in the input
55
///   stream, we can return what we've parsed with the remain data.
56
///
57
/// - maximum number of segments/labels parsed
58
///
59
/// - truncation of name when too long
60
///
61
/// Parameters:
62
///   start: the start of the name
63
///   message: the complete message that start is a part of with the DNS header
64
fn dns_parse_name<'b>(
9,550✔
65
    start: &'b [u8], message: &'b [u8], parse_flags: &mut DNSNameFlags,
9,550✔
66
) -> IResult<&'b [u8], DNSName> {
9,550✔
67
    let mut pos = start;
9,550✔
68
    let mut pivot = start;
9,550✔
69
    let mut name: Vec<u8> = Vec::with_capacity(32);
9,550✔
70
    let mut count = 0;
9,550✔
71
    let mut flags = DNSNameFlags::default();
9,550✔
72

73
    loop {
74
        if pos.is_empty() {
48,148✔
75
            break;
×
76
        }
48,148✔
77

48,148✔
78
        let len = pos[0];
48,148✔
79

48,148✔
80
        if len == 0x00 {
48,148✔
81
            pos = &pos[1..];
9,549✔
82
            break;
9,549✔
83
        } else if len & 0b1100_0000 == 0 {
38,599✔
84
            let (rem, label) = length_data(be_u8).parse(pos)?;
31,389✔
85
            if !flags.contains(DNSNameFlags::TRUNCATED) {
31,389✔
86
                if !name.is_empty() {
31,389✔
87
                    name.push(b'.');
22,040✔
88
                }
22,040✔
89
                name.extend(label);
31,389✔
90
            }
×
91
            pos = rem;
31,389✔
92
        } else if len & 0b1100_0000 == 0b1100_0000 {
7,210✔
93
            let (rem, leader) = be_u16(pos)?;
7,210✔
94
            let offset = usize::from(leader) & 0x3fff;
7,210✔
95
            if offset > message.len() {
7,210✔
96
                return Err(Err::Error(error_position!(pos, ErrorKind::OctDigit)));
1✔
97
            }
7,209✔
98

7,209✔
99
            if &message[offset..] == pos {
7,209✔
100
                // Self reference, immedate infinite loop.
101
                flags.insert(DNSNameFlags::INFINITE_LOOP);
×
102

×
103
                // If we have followed a pointer, we can just break as
×
104
                // we've already found the end of the input. But if we
×
105
                // have not followed a pointer yet return a parse
×
106
                // error.
×
107
                if pivot != start {
×
108
                    break;
×
109
                }
×
110
                return Err(Err::Error(error_position!(pos, ErrorKind::OctDigit)));
×
111
            }
7,209✔
112

7,209✔
113
            pos = &message[offset..];
7,209✔
114
            if pivot == start {
7,209✔
115
                pivot = rem;
5,159✔
116
            }
5,159✔
117
        } else {
118
            return Err(Err::Error(error_position!(pos, ErrorKind::OctDigit)));
×
119
        }
120

121
        // Return error if we've looped a certain number of times.
122
        count += 1;
38,598✔
123

38,598✔
124
        if count > 255 {
38,598✔
125
            flags.insert(DNSNameFlags::LABEL_LIMIT);
×
126

×
127
            // Our segment limit has been reached, if we have hit a
×
128
            // pointer we can just return the truncated name. If we
×
129
            // have not hit a pointer, we need to bail with an error.
×
130
            if pivot != start {
×
131
                flags.insert(DNSNameFlags::TRUNCATED);
×
132
                break;
×
133
            }
×
134
            return Err(Err::Error(error_position!(pos, ErrorKind::OctDigit)));
×
135
        }
38,598✔
136

38,598✔
137
        if name.len() > MAX_NAME_LEN {
38,598✔
138
            name.truncate(MAX_NAME_LEN);
×
139
            flags.insert(DNSNameFlags::TRUNCATED);
×
140

×
141
            // If we have pivoted due to a pointer we know where the
×
142
            // end of the data is, so we can break early. Otherwise
×
143
            // we'll keep parsing in hopes to find the end of the name
×
144
            // so parsing can continue.
×
145
            if pivot != start {
×
146
                break;
×
147
            }
×
148
        }
38,598✔
149
    }
150

151
    parse_flags.insert(flags);
9,549✔
152

9,549✔
153
    // If we followed a pointer we return the position after the first
9,549✔
154
    // pointer followed. Is there a better way to see if these slices
9,549✔
155
    // diverged from each other?  A straight up comparison would
9,549✔
156
    // actually check the contents.
9,549✔
157
    if pivot != start {
9,549✔
158
        Ok((pivot, DNSName { value: name, flags }))
5,159✔
159
    } else {
160
        Ok((pos, DNSName { value: name, flags }))
4,390✔
161
    }
162
}
9,550✔
163

164
/// Parse answer entries.
165
///
166
/// In keeping with the C implementation, answer values that can
167
/// contain multiple answers get expanded into their own answer
168
/// records. An example of this is a TXT record with multiple strings
169
/// in it - each string will be expanded to its own answer record.
170
///
171
/// This function could be a made a whole lot simpler if we logged a
172
/// multi-string TXT entry as a single quote string, similar to the
173
/// output of dig. Something to consider for a future version.
174
fn dns_parse_answer<'a>(
11,070✔
175
    slice: &'a [u8], message: &'a [u8], count: usize, flags: &mut DNSNameFlags,
11,070✔
176
) -> IResult<&'a [u8], Vec<DNSAnswerEntry>> {
11,070✔
177
    let mut answers = Vec::new();
11,070✔
178
    let mut input = slice;
11,070✔
179

180
    struct Answer<'a> {
181
        name: DNSName,
182
        rrtype: u16,
183
        rrclass: u16,
184
        ttl: u32,
185
        data: &'a [u8],
186
    }
187

188
    fn subparser<'a>(
3,870✔
189
        i: &'a [u8], message: &'a [u8], flags: &mut DNSNameFlags,
3,870✔
190
    ) -> IResult<&'a [u8], Answer<'a>> {
3,870✔
191
        let (i, name) = dns_parse_name(i, message, flags)?;
3,870✔
192
        let (i, rrtype) = be_u16(i)?;
3,870✔
193
        let (i, rrclass) = be_u16(i)?;
3,870✔
194
        let (i, ttl) = be_u32(i)?;
3,870✔
195
        let (i, data) = length_data(be_u16).parse(i)?;
3,870✔
196
        let answer = Answer {
3,868✔
197
            name,
3,868✔
198
            rrtype,
3,868✔
199
            rrclass,
3,868✔
200
            ttl,
3,868✔
201
            data,
3,868✔
202
        };
3,868✔
203
        Ok((i, answer))
3,868✔
204
    }
3,870✔
205

206
    for _ in 0..count {
11,070✔
207
        match subparser(input, message, flags) {
3,870✔
208
            Ok((rem, val)) => {
3,868✔
209
                // edge case for additional section of type=OPT
3,868✔
210
                // with empty data (data length = 0x0000)
3,868✔
211
                if val.data.is_empty() && val.rrtype == DNSRecordType::OPT as u16 {
3,868✔
212
                    answers.push(DNSAnswerEntry {
87✔
213
                        name: val.name.clone(),
87✔
214
                        rrtype: val.rrtype,
87✔
215
                        rrclass: val.rrclass,
87✔
216
                        ttl: val.ttl,
87✔
217
                        data: DNSRData::OPT(Vec::new()),
87✔
218
                    });
87✔
219
                    input = rem;
87✔
220
                    continue;
87✔
221
                }
3,781✔
222
                let (_, rdata) = dns_parse_rdata(val.data, message, val.rrtype, flags)?;
3,781✔
223
                answers.push(DNSAnswerEntry {
3,781✔
224
                    name: val.name.clone(),
3,781✔
225
                    rrtype: val.rrtype,
3,781✔
226
                    rrclass: val.rrclass,
3,781✔
227
                    ttl: val.ttl,
3,781✔
228
                    data: rdata,
3,781✔
229
                });
3,781✔
230
                input = rem;
3,781✔
231
            }
232
            Err(e) => {
2✔
233
                return Err(e);
2✔
234
            }
235
        }
236
    }
237

238
    return Ok((input, answers));
11,068✔
239
}
11,070✔
240

241
/// Parse a single DNS query.
242
///
243
/// Arguments are suitable for using with call!:
244
///
245
///    call!(complete_dns_message_buffer)
246
fn dns_parse_query<'a>(
3,760✔
247
    input: &'a [u8], message: &'a [u8], flags: &mut DNSNameFlags,
3,760✔
248
) -> IResult<&'a [u8], DNSQueryEntry> {
3,760✔
249
    let i = input;
3,760✔
250
    let (i, name) = dns_parse_name(i, message, flags)?;
3,760✔
251
    let (i, rrtype) = be_u16(i)?;
3,759✔
252
    let (i, rrclass) = be_u16(i)?;
3,759✔
253
    Ok((
3,759✔
254
        i,
3,759✔
255
        DNSQueryEntry {
3,759✔
256
            name,
3,759✔
257
            rrtype,
3,759✔
258
            rrclass,
3,759✔
259
        },
3,759✔
260
    ))
3,759✔
261
}
3,760✔
262

263
fn dns_parse_rdata_a(input: &[u8]) -> IResult<&[u8], DNSRData> {
1,568✔
264
    rest(input).map(|(input, data)| (input, DNSRData::A(data.to_vec())))
1,568✔
265
}
1,568✔
266

267
fn dns_parse_rdata_aaaa(input: &[u8]) -> IResult<&[u8], DNSRData> {
181✔
268
    rest(input).map(|(input, data)| (input, DNSRData::AAAA(data.to_vec())))
181✔
269
}
181✔
270

271
fn dns_parse_rdata_cname<'a>(
399✔
272
    input: &'a [u8], message: &'a [u8], flags: &mut DNSNameFlags,
399✔
273
) -> IResult<&'a [u8], DNSRData> {
399✔
274
    dns_parse_name(input, message, flags).map(|(input, name)| (input, DNSRData::CNAME(name)))
399✔
275
}
399✔
276

277
fn dns_parse_rdata_ns<'a>(
1,148✔
278
    input: &'a [u8], message: &'a [u8], flags: &mut DNSNameFlags,
1,148✔
279
) -> IResult<&'a [u8], DNSRData> {
1,148✔
280
    dns_parse_name(input, message, flags).map(|(input, name)| (input, DNSRData::NS(name)))
1,148✔
281
}
1,148✔
282

283
fn dns_parse_rdata_ptr<'a>(
129✔
284
    input: &'a [u8], message: &'a [u8], flags: &mut DNSNameFlags,
129✔
285
) -> IResult<&'a [u8], DNSRData> {
129✔
286
    dns_parse_name(input, message, flags).map(|(input, name)| (input, DNSRData::PTR(name)))
129✔
287
}
129✔
288

289
fn dns_parse_rdata_soa<'a>(
106✔
290
    input: &'a [u8], message: &'a [u8], flags: &mut DNSNameFlags,
106✔
291
) -> IResult<&'a [u8], DNSRData> {
106✔
292
    let i = input;
106✔
293
    let (i, mname) = dns_parse_name(i, message, flags)?;
106✔
294
    let (i, rname) = dns_parse_name(i, message, flags)?;
106✔
295
    let (i, serial) = be_u32(i)?;
106✔
296
    let (i, refresh) = be_u32(i)?;
106✔
297
    let (i, retry) = be_u32(i)?;
106✔
298
    let (i, expire) = be_u32(i)?;
106✔
299
    let (i, minimum) = be_u32(i)?;
106✔
300
    Ok((
106✔
301
        i,
106✔
302
        DNSRData::SOA(DNSRDataSOA {
106✔
303
            mname,
106✔
304
            rname,
106✔
305
            serial,
106✔
306
            refresh,
106✔
307
            retry,
106✔
308
            expire,
106✔
309
            minimum,
106✔
310
        }),
106✔
311
    ))
106✔
312
}
106✔
313

314
fn dns_parse_rdata_mx<'a>(
20✔
315
    input: &'a [u8], message: &'a [u8], flags: &mut DNSNameFlags,
20✔
316
) -> IResult<&'a [u8], DNSRData> {
20✔
317
    // For MX we skip over the preference field before
318
    // parsing out the name.
319
    let (i, _) = be_u16(input)?;
20✔
320
    let (i, name) = dns_parse_name(i, message, flags)?;
20✔
321
    Ok((i, DNSRData::MX(name)))
20✔
322
}
20✔
323

324
fn dns_parse_rdata_srv<'a>(
12✔
325
    input: &'a [u8], message: &'a [u8], flags: &mut DNSNameFlags,
12✔
326
) -> IResult<&'a [u8], DNSRData> {
12✔
327
    let i = input;
12✔
328
    let (i, priority) = be_u16(i)?;
12✔
329
    let (i, weight) = be_u16(i)?;
12✔
330
    let (i, port) = be_u16(i)?;
12✔
331
    let (i, target) = dns_parse_name(i, message, flags)?;
12✔
332
    Ok((
12✔
333
        i,
12✔
334
        DNSRData::SRV(DNSRDataSRV {
12✔
335
            priority,
12✔
336
            weight,
12✔
337
            port,
12✔
338
            target,
12✔
339
        }),
12✔
340
    ))
12✔
341
}
12✔
342

343
fn dns_parse_rdata_txt(input: &[u8]) -> IResult<&[u8], DNSRData> {
94✔
344
    let mut txt_strings = Vec::new();
94✔
345
    let mut i = input;
94✔
346

347
    while !i.is_empty() {
325✔
348
        let (j, txt) = length_data(be_u8).parse(i)?;
231✔
349
        txt_strings.push(txt.to_vec());
231✔
350
        i = j;
231✔
351
    }
352

353
    Ok((i, DNSRData::TXT(txt_strings)))
94✔
354
}
94✔
355

356
fn dns_parse_rdata_null(input: &[u8]) -> IResult<&[u8], DNSRData> {
4✔
357
    rest(input).map(|(input, data)| (input, DNSRData::NULL(data.to_vec())))
4✔
358
}
4✔
359

360
fn dns_parse_rdata_sshfp(input: &[u8]) -> IResult<&[u8], DNSRData> {
4✔
361
    let i = input;
4✔
362
    let (i, algo) = be_u8(i)?;
4✔
363
    let (i, fp_type) = be_u8(i)?;
4✔
364
    let fingerprint = i;
4✔
365
    Ok((
4✔
366
        &[],
4✔
367
        DNSRData::SSHFP(DNSRDataSSHFP {
4✔
368
            algo,
4✔
369
            fp_type,
4✔
370
            fingerprint: fingerprint.to_vec(),
4✔
371
        }),
4✔
372
    ))
4✔
373
}
4✔
374

375
fn dns_parse_rdata_opt(input: &[u8]) -> IResult<&[u8], DNSRData> {
108✔
376
    let mut dns_rdata_opt_vec = Vec::new();
108✔
377
    let mut i = input;
108✔
378

379
    while !i.is_empty() {
216✔
380
        let (j, code) = be_u16(i)?;
108✔
381
        let (j, data) = length_data(be_u16).parse(j)?;
108✔
382
        i = j;
108✔
383
        dns_rdata_opt_vec.push(DNSRDataOPT {
108✔
384
            code,
108✔
385
            data: data.to_vec(),
108✔
386
        });
108✔
387
    }
388
    Ok((i, DNSRData::OPT(dns_rdata_opt_vec)))
108✔
389
}
108✔
390

391
fn dns_parse_rdata_unknown(input: &[u8]) -> IResult<&[u8], DNSRData> {
8✔
392
    rest(input).map(|(input, data)| (input, DNSRData::Unknown(data.to_vec())))
8✔
393
}
8✔
394

395
fn dns_parse_rdata<'a>(
3,781✔
396
    input: &'a [u8], message: &'a [u8], rrtype: u16, flags: &mut DNSNameFlags,
3,781✔
397
) -> IResult<&'a [u8], DNSRData> {
3,781✔
398
    match DNSRecordType::from_u(rrtype) {
3,781✔
399
        Some(DNSRecordType::A) => dns_parse_rdata_a(input),
1,568✔
400
        Some(DNSRecordType::AAAA) => dns_parse_rdata_aaaa(input),
181✔
401
        Some(DNSRecordType::CNAME) => dns_parse_rdata_cname(input, message, flags),
399✔
402
        Some(DNSRecordType::PTR) => dns_parse_rdata_ptr(input, message, flags),
129✔
403
        Some(DNSRecordType::SOA) => dns_parse_rdata_soa(input, message, flags),
106✔
404
        Some(DNSRecordType::MX) => dns_parse_rdata_mx(input, message, flags),
20✔
405
        Some(DNSRecordType::NS) => dns_parse_rdata_ns(input, message, flags),
1,148✔
406
        Some(DNSRecordType::TXT) => dns_parse_rdata_txt(input),
94✔
407
        Some(DNSRecordType::NULL) => dns_parse_rdata_null(input),
4✔
408
        Some(DNSRecordType::SSHFP) => dns_parse_rdata_sshfp(input),
4✔
409
        Some(DNSRecordType::SRV) => dns_parse_rdata_srv(input, message, flags),
12✔
410
        Some(DNSRecordType::OPT) => dns_parse_rdata_opt(input),
108✔
411
        _ => dns_parse_rdata_unknown(input),
8✔
412
    }
413
}
3,781✔
414

415
// Parse a DNS header.
416
pub fn dns_parse_header(i: &[u8]) -> IResult<&[u8], DNSHeader> {
3,692✔
417
    let (i, tx_id) = be_u16(i)?;
3,692✔
418
    let (i, flags) = be_u16(i)?;
3,692✔
419
    let (i, questions) = be_u16(i)?;
3,692✔
420
    let (i, answer_rr) = be_u16(i)?;
3,692✔
421
    let (i, authority_rr) = be_u16(i)?;
3,692✔
422
    let (i, additional_rr) = be_u16(i)?;
3,692✔
423
    Ok((
3,692✔
424
        i,
3,692✔
425
        DNSHeader {
3,692✔
426
            tx_id,
3,692✔
427
            flags,
3,692✔
428
            questions,
3,692✔
429
            answer_rr,
3,692✔
430
            authority_rr,
3,692✔
431
            additional_rr,
3,692✔
432
        },
3,692✔
433
    ))
3,692✔
434
}
3,692✔
435

436
pub fn dns_parse_body<'a>(
3,691✔
437
    i: &'a [u8], message: &'a [u8], header: DNSHeader,
3,691✔
438
) -> IResult<&'a [u8], (DNSMessage, DNSNameFlags)> {
3,691✔
439
    let mut flags = DNSNameFlags::default();
3,691✔
440
    let (i, queries) = count(
3,691✔
441
        |b| dns_parse_query(b, message, &mut flags),
3,760✔
442
        header.questions as usize,
3,691✔
443
    ).parse(i)?;
3,691✔
444
    let (i, answers) = dns_parse_answer(i, message, header.answer_rr as usize, &mut flags)?;
3,690✔
445

446
    let mut invalid_authorities = false;
3,690✔
447
    let mut authorities = Vec::new();
3,690✔
448
    let mut i_next = i;
3,690✔
449
    let authorities_parsed = dns_parse_answer(i, message, header.authority_rr as usize, &mut flags);
3,690✔
450
    if let Ok((i, authorities_ok)) = authorities_parsed {
3,690✔
451
        authorities = authorities_ok;
3,690✔
452
        i_next = i;
3,690✔
453
    } else {
3,690✔
454
        invalid_authorities = true;
×
455
    }
×
456

457
    let mut invalid_additionals = false;
3,690✔
458
    let mut additionals = Vec::new();
3,690✔
459
    if !invalid_authorities {
3,690✔
460
        let additionals_parsed =
3,690✔
461
            dns_parse_answer(i_next, message, header.additional_rr as usize, &mut flags);
3,690✔
462
        if let Ok((i, additionals_ok)) = additionals_parsed {
3,690✔
463
            additionals = additionals_ok;
3,688✔
464
            i_next = i;
3,688✔
465
        } else {
3,688✔
466
            invalid_additionals = true;
2✔
467
        }
2✔
468
    }
×
469
    Ok((
3,690✔
470
        i_next,
3,690✔
471
        (
3,690✔
472
            DNSMessage {
3,690✔
473
                header,
3,690✔
474
                queries,
3,690✔
475
                answers,
3,690✔
476
                authorities,
3,690✔
477
                invalid_authorities,
3,690✔
478
                additionals,
3,690✔
479
                invalid_additionals,
3,690✔
480
            },
3,690✔
481
            flags,
3,690✔
482
        ),
3,690✔
483
    ))
3,690✔
484
}
3,691✔
485

486
#[cfg(test)]
487
mod tests {
488

489
    use crate::dns::dns::{DNSAnswerEntry, DNSHeader};
490
    use crate::dns::parser::*;
491

492
    /// Parse a simple name with no pointers.
493
    #[test]
494
    fn test_dns_parse_name() {
495
        let buf: &[u8] = &[
496
            0x09, 0x63, /* .......c */
497
            0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2d, 0x63, 0x66, /* lient-cf */
498
            0x07, 0x64, 0x72, 0x6f, 0x70, 0x62, 0x6f, 0x78, /* .dropbox */
499
            0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, /* .com.... */
500
        ];
501
        let expected_remainder: &[u8] = &[0x00, 0x01, 0x00];
502
        let mut flags = DNSNameFlags::default();
503
        let (remainder, name) = dns_parse_name(buf, buf, &mut flags).unwrap();
504
        assert_eq!("client-cf.dropbox.com".as_bytes(), &name.value[..]);
505
        assert_eq!(remainder, expected_remainder);
506
    }
507

508
    /// Test parsing a name with pointers.
509
    #[test]
510
    fn test_dns_parse_name_with_pointer() {
511
        let buf: &[u8] = &[
512
            0xd8, 0xcb, 0x8a, 0xed, 0xa1, 0x46, 0x00, 0x15, /* 0   - .....F.. */
513
            0x17, 0x0d, 0x06, 0xf7, 0x08, 0x00, 0x45, 0x00, /* 8   - ......E. */
514
            0x00, 0x7b, 0x71, 0x6e, 0x00, 0x00, 0x39, 0x11, /* 16  - .{qn..9. */
515
            0xf4, 0xd9, 0x08, 0x08, 0x08, 0x08, 0x0a, 0x10, /* 24  - ........ */
516
            0x01, 0x0b, 0x00, 0x35, 0xe1, 0x8e, 0x00, 0x67, /* 32  - ...5...g */
517
            0x60, 0x00, 0xef, 0x08, 0x81, 0x80, 0x00, 0x01, /* 40  - `....... */
518
            0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77, /* 48  - .......w */
519
            0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63, /* 56  - ww.suric */
520
            0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03, /* 64  - ata-ids. */
521
            0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, /* 72  - org..... */
522
            0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, /* 80  - ........ */
523
            0x0e, 0x0f, 0x00, 0x02, 0xc0, 0x10, 0xc0, 0x10, /* 88  - ........ */
524
            0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x2b, /* 96  - .......+ */
525
            0x00, 0x04, 0xc0, 0x00, 0x4e, 0x19, 0xc0, 0x10, /* 104 - ....N... */
526
            0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x2b, /* 112 - .......+ */
527
            0x00, 0x04, 0xc0, 0x00, 0x4e, 0x18, 0x00, 0x00, /* 120 - ....N... */
528
            0x29, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 128 - )....... */
529
            0x00, /* 136 - . */
530
        ];
531

532
        // The DNS payload starts at offset 42.
533
        let message = &buf[42..];
534

535
        // The name at offset 54 is the complete name.
536
        let start1 = &buf[54..];
537
        let mut flags = DNSNameFlags::default();
538
        let res1 = dns_parse_name(start1, message, &mut flags);
539
        assert_eq!(
540
            res1,
541
            Ok((
542
                &start1[22..],
543
                DNSName {
544
                    value: "www.suricata-ids.org".as_bytes().to_vec(),
545
                    flags: DNSNameFlags::default(),
546
                }
547
            ))
548
        );
549

550
        // The second name starts at offset 80, but is just a pointer
551
        // to the first.
552
        let start2 = &buf[80..];
553
        let mut flags = DNSNameFlags::default();
554
        let res2 = dns_parse_name(start2, message, &mut flags);
555
        assert_eq!(
556
            res2,
557
            Ok((
558
                &start2[2..],
559
                DNSName {
560
                    value: "www.suricata-ids.org".as_bytes().to_vec(),
561
                    flags: DNSNameFlags::default()
562
                }
563
            ))
564
        );
565

566
        // The third name starts at offset 94, but is a pointer to a
567
        // portion of the first.
568
        let start3 = &buf[94..];
569
        let mut flags = DNSNameFlags::default();
570
        let res3 = dns_parse_name(start3, message, &mut flags);
571
        assert_eq!(
572
            res3,
573
            Ok((
574
                &start3[2..],
575
                DNSName {
576
                    value: "suricata-ids.org".as_bytes().to_vec(),
577
                    flags: DNSNameFlags::default()
578
                }
579
            ))
580
        );
581

582
        // The fourth name starts at offset 110, but is a pointer to a
583
        // portion of the first.
584
        let start4 = &buf[110..];
585
        let mut flags = DNSNameFlags::default();
586
        let res4 = dns_parse_name(start4, message, &mut flags);
587
        assert_eq!(
588
            res4,
589
            Ok((
590
                &start4[2..],
591
                DNSName {
592
                    value: "suricata-ids.org".as_bytes().to_vec(),
593
                    flags: DNSNameFlags::default()
594
                }
595
            ))
596
        );
597
    }
598

599
    #[test]
600
    fn test_dns_parse_name_double_pointer() {
601
        let buf: &[u8] = &[
602
            0xd8, 0xcb, 0x8a, 0xed, 0xa1, 0x46, 0x00, 0x15, /* 0:   .....F.. */
603
            0x17, 0x0d, 0x06, 0xf7, 0x08, 0x00, 0x45, 0x00, /* 8:   ......E. */
604
            0x00, 0x66, 0x5e, 0x20, 0x40, 0x00, 0x40, 0x11, /* 16:  .f^ @.@. */
605
            0xc6, 0x3b, 0x0a, 0x10, 0x01, 0x01, 0x0a, 0x10, /* 24:  .;...... */
606
            0x01, 0x0b, 0x00, 0x35, 0xc2, 0x21, 0x00, 0x52, /* 32:  ...5.!.R */
607
            0x35, 0xc5, 0x0d, 0x4f, 0x81, 0x80, 0x00, 0x01, /* 40:  5..O.... */
608
            0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x05, 0x62, /* 48:  .......b */
609
            0x6c, 0x6f, 0x63, 0x6b, 0x07, 0x64, 0x72, 0x6f, /* 56:  lock.dro */
610
            0x70, 0x62, 0x6f, 0x78, 0x03, 0x63, 0x6f, 0x6d, /* 64:  pbox.com */
611
            0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, /* 72:  ........ */
612
            0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, /* 80:  ........ */
613
            0x0b, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x02, /* 88:  ..block. */
614
            0x67, 0x31, 0xc0, 0x12, 0xc0, 0x2f, 0x00, 0x01, /* 96:  g1.../.. */
615
            0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, /* 104: ........ */
616
            0x2d, 0x3a, 0x46, 0x21, /* 112: -:F!     */
617
        ];
618

619
        // The start of the DNS message in the above packet.
620
        let message: &[u8] = &buf[42..];
621

622
        // The start of the name we want to parse, 0xc0 0x2f, a
623
        // pointer to offset 47 in the message (or 89 in the full
624
        // packet).
625
        let start: &[u8] = &buf[100..];
626

627
        let mut flags = DNSNameFlags::default();
628
        let res = dns_parse_name(start, message, &mut flags);
629
        assert_eq!(
630
            res,
631
            Ok((
632
                &start[2..],
633
                DNSName {
634
                    value: "block.g1.dropbox.com".as_bytes().to_vec(),
635
                    flags: DNSNameFlags::default()
636
                }
637
            ))
638
        );
639
    }
640

641
    #[test]
642
    fn test_dns_parse_request() {
643
        // DNS request from dig-a-www.suricata-ids.org.pcap.
644
        let pkt: &[u8] = &[
645
            0x8d, 0x32, 0x01, 0x20, 0x00, 0x01, /* ...2. .. */
646
            0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77, /* .......w */
647
            0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63, /* ww.suric */
648
            0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03, /* ata-ids. */
649
            0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, /* org..... */
650
            0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00, /* ..)..... */
651
            0x00, 0x00, 0x00, /* ... */
652
        ];
653

654
        let (body, header) = dns_parse_header(pkt).unwrap();
655
        let res = dns_parse_body(body, pkt, header);
656
        let (rem, (request, _flags)) = res.unwrap();
657
        // The request should be fully parsed.
658
        assert!(rem.is_empty());
659

660
        assert_eq!(
661
            request.header,
662
            DNSHeader {
663
                tx_id: 0x8d32,
664
                flags: 0x0120,
665
                questions: 1,
666
                answer_rr: 0,
667
                authority_rr: 0,
668
                additional_rr: 1,
669
            }
670
        );
671

672
        assert_eq!(request.queries.len(), 1);
673

674
        let query = &request.queries[0];
675
        assert_eq!(query.name.value, "www.suricata-ids.org".as_bytes().to_vec());
676
        assert_eq!(query.rrtype, 1);
677
        assert_eq!(query.rrclass, 1);
678

679
        // verify additional section
680
        assert_eq!(request.additionals.len(), 1);
681

682
        let additional = &request.additionals[0];
683
        assert_eq!(
684
            additional,
685
            &DNSAnswerEntry {
686
                name: DNSName {
687
                    value: vec![],
688
                    flags: DNSNameFlags::default()
689
                },
690
                rrtype: DNSRecordType::OPT as u16,
691
                rrclass: 0x1000,             // for OPT this is UDP payload size
692
                ttl: 0,                      // for OPT this is extended RCODE and flags
693
                data: DNSRData::OPT(vec![]), // empty rdata
694
            }
695
        );
696
    }
697

698
    #[test]
699
    fn test_dns_parse_request_multi_opt() {
700
        let pkt: &[u8] = &[
701
            0x8d, 0x32, 0x01, 0x20, 0x00, 0x01, /* ...2. .. */
702
            0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77, /* .......w */
703
            0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63, /* ww.suric */
704
            0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03, /* ata-ids. */
705
            0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, /* org..... */
706
            0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00, /* ..)..... */
707
            0x00, 0x00, 0x10, /* total rdata len: 16 */
708
            /* Option Cookie */
709
            0x00, 0x0a, /* Code: 10 */
710
            0x00, 0x08, /* Option len: 8 */
711
            0x7f, 0x86, 0xcf, 0x8b, 0x81, 0xf6, 0xf9, 0x55, /* Option NSID */
712
            0x00, 0x03, /* Code: 3 */
713
            0x00, 0x00, /* option len: 0 */
714
        ];
715

716
        let (body, header) = dns_parse_header(pkt).unwrap();
717
        let res = dns_parse_body(body, pkt, header);
718
        let (rem, (request, _flags)) = res.unwrap();
719

720
        assert!(rem.is_empty());
721
        assert_eq!(
722
            request.header,
723
            DNSHeader {
724
                tx_id: 0x8d32,
725
                flags: 0x0120,
726
                questions: 1,
727
                answer_rr: 0,
728
                authority_rr: 0,
729
                additional_rr: 1,
730
            }
731
        );
732

733
        assert_eq!(request.queries.len(), 1);
734

735
        let query = &request.queries[0];
736
        assert_eq!(query.name.value, "www.suricata-ids.org".as_bytes().to_vec());
737
        assert_eq!(query.rrtype, 1);
738
        assert_eq!(query.rrclass, 1);
739

740
        // verify additional section
741
        assert_eq!(request.additionals.len(), 1);
742

743
        let additional = &request.additionals[0];
744
        assert_eq!(
745
            additional,
746
            &DNSAnswerEntry {
747
                name: DNSName {
748
                    value: vec![],
749
                    flags: DNSNameFlags::default()
750
                },
751
                rrtype: DNSRecordType::OPT as u16,
752
                rrclass: 0x1000, // for OPT this is requestor's UDP payload size
753
                ttl: 0,          // for OPT this is extended RCODE and flags
754
                // verify two options
755
                data: DNSRData::OPT(vec![
756
                    DNSRDataOPT {
757
                        code: 0x000a,
758
                        data: vec![0x7f, 0x86, 0xcf, 0x8b, 0x81, 0xf6, 0xf9, 0x55]
759
                    },
760
                    DNSRDataOPT {
761
                        code: 0x0003,
762
                        data: vec![]
763
                    },
764
                ])
765
            }
766
        );
767
    }
768

769
    /// Parse a DNS response.
770
    fn dns_parse_response(message: &[u8]) -> IResult<&[u8], (DNSMessage, DNSNameFlags)> {
771
        let i = message;
772
        let (i, header) = dns_parse_header(i)?;
773
        dns_parse_body(i, message, header)
774
    }
775

776
    #[test]
777
    fn test_dns_parse_response() {
778
        // DNS response from dig-a-www.suricata-ids.org.pcap.
779
        let pkt: &[u8] = &[
780
            0x8d, 0x32, 0x81, 0xa0, 0x00, 0x01, /* ...2.... */
781
            0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, /* .......w */
782
            0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63, /* ww.suric */
783
            0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03, /* ata-ids. */
784
            0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, /* org..... */
785
            0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, /* ........ */
786
            0x0d, 0xd8, 0x00, 0x12, 0x0c, 0x73, 0x75, 0x72, /* .....sur */
787
            0x69, 0x63, 0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, /* icata-id */
788
            0x73, 0x03, 0x6f, 0x72, 0x67, 0x00, 0xc0, 0x32, /* s.org..2 */
789
            0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4, /* ........ */
790
            0x00, 0x04, 0xc0, 0x00, 0x4e, 0x18, 0xc0, 0x32, /* ....N..2 */
791
            0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4, /* ........ */
792
            0x00, 0x04, 0xc0, 0x00, 0x4e, 0x19, /* ....N. */
793
        ];
794

795
        let (rem, (response, _flags)) = dns_parse_response(pkt).unwrap();
796
        // The response should be full parsed.
797
        assert_eq!(rem.len(), 0);
798

799
        assert_eq!(
800
            response.header,
801
            DNSHeader {
802
                tx_id: 0x8d32,
803
                flags: 0x81a0,
804
                questions: 1,
805
                answer_rr: 3,
806
                authority_rr: 0,
807
                additional_rr: 0,
808
            }
809
        );
810

811
        assert_eq!(response.answers.len(), 3);
812

813
        let answer1 = &response.answers[0];
814
        assert_eq!(
815
            answer1.name.value,
816
            "www.suricata-ids.org".as_bytes().to_vec()
817
        );
818
        assert_eq!(answer1.rrtype, 5);
819
        assert_eq!(answer1.rrclass, 1);
820
        assert_eq!(answer1.ttl, 3544);
821
        assert_eq!(
822
            answer1.data,
823
            DNSRData::CNAME(DNSName {
824
                value: "suricata-ids.org".as_bytes().to_vec(),
825
                flags: DNSNameFlags::default()
826
            })
827
        );
828

829
        let answer2 = &response.answers[1];
830
        assert_eq!(
831
            answer2,
832
            &DNSAnswerEntry {
833
                name: DNSName {
834
                    value: "suricata-ids.org".as_bytes().to_vec(),
835
                    flags: DNSNameFlags::default()
836
                },
837
                rrtype: 1,
838
                rrclass: 1,
839
                ttl: 244,
840
                data: DNSRData::A([192, 0, 78, 24].to_vec()),
841
            }
842
        );
843

844
        let answer3 = &response.answers[2];
845
        assert_eq!(
846
            answer3,
847
            &DNSAnswerEntry {
848
                name: DNSName {
849
                    value: "suricata-ids.org".as_bytes().to_vec(),
850
                    flags: DNSNameFlags::default()
851
                },
852
                rrtype: 1,
853
                rrclass: 1,
854
                ttl: 244,
855
                data: DNSRData::A([192, 0, 78, 25].to_vec()),
856
            }
857
        )
858
    }
859

860
    #[test]
861
    fn test_dns_parse_response_nxdomain_soa() {
862
        // DNS response with an SOA authority record from
863
        // dns-udp-nxdomain-soa.pcap.
864
        let pkt: &[u8] = &[
865
            0x82, 0x95, 0x81, 0x83, 0x00, 0x01, /* j....... */
866
            0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x03, 0x64, /* .......d */
867
            0x6e, 0x65, 0x04, 0x6f, 0x69, 0x73, 0x66, 0x03, /* ne.oisf. */
868
            0x6e, 0x65, 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, /* net..... */
869
            0xc0, 0x10, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, /* ........ */
870
            0x03, 0x83, 0x00, 0x45, 0x06, 0x6e, 0x73, 0x2d, /* ...E.ns- */
871
            0x31, 0x31, 0x30, 0x09, 0x61, 0x77, 0x73, 0x64, /* 110.awsd */
872
            0x6e, 0x73, 0x2d, 0x31, 0x33, 0x03, 0x63, 0x6f, /* ns-13.co */
873
            0x6d, 0x00, 0x11, 0x61, 0x77, 0x73, 0x64, 0x6e, /* m..awsdn */
874
            0x73, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x6d, 0x61, /* s-hostma */
875
            0x73, 0x74, 0x65, 0x72, 0x06, 0x61, 0x6d, 0x61, /* ster.ama */
876
            0x7a, 0x6f, 0x6e, 0xc0, 0x3b, 0x00, 0x00, 0x00, /* zon.;... */
877
            0x01, 0x00, 0x00, 0x1c, 0x20, 0x00, 0x00, 0x03, /* .... ... */
878
            0x84, 0x00, 0x12, 0x75, 0x00, 0x00, 0x01, 0x51, /* ...u...Q */
879
            0x80, 0x00, 0x00, 0x29, 0x02, 0x00, 0x00, 0x00, /* ...).... */
880
            0x00, 0x00, 0x00, 0x00, /* .... */
881
        ];
882

883
        let (rem, (response, _flags)) = dns_parse_response(pkt).unwrap();
884
        // The response should be fully parsed.
885
        assert!(rem.is_empty());
886

887
        assert_eq!(
888
            response.header,
889
            DNSHeader {
890
                tx_id: 0x8295,
891
                flags: 0x8183,
892
                questions: 1,
893
                answer_rr: 0,
894
                authority_rr: 1,
895
                additional_rr: 1,
896
            }
897
        );
898

899
        assert_eq!(response.authorities.len(), 1);
900

901
        let authority = &response.authorities[0];
902
        assert_eq!(authority.name.value, "oisf.net".as_bytes().to_vec());
903
        assert_eq!(authority.rrtype, 6);
904
        assert_eq!(authority.rrclass, 1);
905
        assert_eq!(authority.ttl, 899);
906
        assert_eq!(
907
            authority.data,
908
            DNSRData::SOA(DNSRDataSOA {
909
                mname: DNSName {
910
                    value: "ns-110.awsdns-13.com".as_bytes().to_vec(),
911
                    flags: DNSNameFlags::default()
912
                },
913
                rname: DNSName {
914
                    value: "awsdns-hostmaster.amazon.com".as_bytes().to_vec(),
915
                    flags: DNSNameFlags::default()
916
                },
917
                serial: 1,
918
                refresh: 7200,
919
                retry: 900,
920
                expire: 1209600,
921
                minimum: 86400,
922
            })
923
        );
924

925
        // verify additional section
926
        assert_eq!(response.additionals.len(), 1);
927

928
        let additional = &response.additionals[0];
929
        assert_eq!(
930
            additional,
931
            &DNSAnswerEntry {
932
                name: DNSName {
933
                    value: vec![],
934
                    flags: DNSNameFlags::default()
935
                },
936
                rrtype: DNSRecordType::OPT as u16,
937
                rrclass: 0x0200,             // for OPT this is UDP payload size
938
                ttl: 0,                      // for OPT this is extended RCODE and flags
939
                data: DNSRData::OPT(vec![]), // no rdata
940
            }
941
        );
942
    }
943

944
    #[test]
945
    fn test_dns_parse_response_null() {
946
        // DNS response with a NULL record from
947
        // https://redmine.openinfosecfoundation.org/attachments/2062
948

949
        let pkt: &[u8] = &[
950
            0x12, 0xb0, 0x84, 0x00, 0x00, 0x01, 0x00, 0x01, /* ........ */
951
            0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x61, 0x61, /* .....vaa */
952
            0x61, 0x61, 0x6b, 0x61, 0x72, 0x64, 0x6c, 0x69, /* aakardli */
953
            0x06, 0x70, 0x69, 0x72, 0x61, 0x74, 0x65, 0x03, /* .pirate. */
954
            0x73, 0x65, 0x61, 0x00, 0x00, 0x0a, 0x00, 0x01, /* sea..... */
955
            0xc0, 0x0c, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, /* ........ */
956
            0x00, 0x00, 0x00, 0x09, 0x56, 0x41, 0x43, 0x4b, /* ....VACK */
957
            0x44, 0x03, 0xc5, 0xe9, 0x01, /* D.... */
958
        ];
959

960
        let (rem, (response, _flags)) = dns_parse_response(pkt).unwrap();
961
        // The response should be fully parsed.
962
        assert_eq!(rem.len(), 0);
963

964
        assert_eq!(
965
            response.header,
966
            DNSHeader {
967
                tx_id: 0x12b0,
968
                flags: 0x8400,
969
                questions: 1,
970
                answer_rr: 1,
971
                authority_rr: 0,
972
                additional_rr: 0,
973
            }
974
        );
975

976
        assert_eq!(response.queries.len(), 1);
977
        let query = &response.queries[0];
978
        assert_eq!(
979
            query.name.value,
980
            "vaaaakardli.pirate.sea".as_bytes().to_vec()
981
        );
982
        assert_eq!(query.rrtype, DNSRecordType::NULL as u16);
983
        assert_eq!(query.rrclass, 1);
984

985
        assert_eq!(response.answers.len(), 1);
986

987
        let answer = &response.answers[0];
988
        assert_eq!(
989
            answer.name.value,
990
            "vaaaakardli.pirate.sea".as_bytes().to_vec()
991
        );
992
        assert_eq!(answer.rrtype, DNSRecordType::NULL as u16);
993
        assert_eq!(answer.rrclass, 1);
994
        assert_eq!(answer.ttl, 0);
995
        assert_eq!(
996
            answer.data,
997
            DNSRData::NULL(vec![
998
                0x56, 0x41, 0x43, 0x4b, /* VACK */
999
                0x44, 0x03, 0xc5, 0xe9, 0x01, /* D.... */
1000
            ])
1001
        );
1002
    }
1003

1004
    #[test]
1005
    fn test_dns_parse_rdata_sshfp() {
1006
        // Dummy data since we don't have a pcap sample.
1007
        let data: &[u8] = &[
1008
            // algo: DSS
1009
            0x02, // fp_type: SHA-1
1010
            0x01, // fingerprint: 123456789abcdef67890123456789abcdef67890
1011
            0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf6, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
1012
            0x9a, 0xbc, 0xde, 0xf6, 0x78, 0x90,
1013
        ];
1014

1015
        let (rem, rdata) = dns_parse_rdata_sshfp(data).unwrap();
1016
        // The data should be fully parsed.
1017
        assert_eq!(rem.len(), 0);
1018

1019
        if let DNSRData::SSHFP(sshfp) = rdata {
1020
            assert_eq!(sshfp.algo, 2);
1021
            assert_eq!(sshfp.fp_type, 1);
1022
            assert_eq!(sshfp.fingerprint, &data[2..]);
1023
        } else {
1024
            panic!("Expected DNSRData::SSHFP");
1025
        }
1026
    }
1027

1028
    #[test]
1029
    fn test_dns_parse_rdata_srv() {
1030
        /*  ; <<>> DiG 9.11.5-P4-5.1+deb10u2-Debian <<>> _sip._udp.sip.voice.google.com SRV
1031
        ;; global options: +cmd
1032
        ;; Got answer:
1033
        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1524
1034
        ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 3
1035

1036
        [...]
1037

1038
        ;; ANSWER SECTION:
1039
        _sip._udp.sip.voice.google.com.        300 IN        SRV        10 1 5060 sip-anycast-1.voice.google.com.
1040
        _sip._udp.sip.voice.google.com.        300 IN        SRV        20 1 5060 sip-anycast-2.voice.google.com.
1041

1042
        [...]
1043

1044
        ;; Query time: 72 msec
1045
        ;; MSG SIZE  rcvd: 191   */
1046

1047
        let pkt: &[u8] = &[
1048
            0xeb, 0x56, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x5f,
1049
            0x73, 0x69, 0x70, 0x04, 0x5f, 0x75, 0x64, 0x70, 0x03, 0x73, 0x69, 0x70, 0x05, 0x76,
1050
            0x6f, 0x69, 0x63, 0x65, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f,
1051
            0x6d, 0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00,
1052
            0x01, 0x13, 0x00, 0x26, 0x00, 0x14, 0x00, 0x01, 0x13, 0xc4, 0x0d, 0x73, 0x69, 0x70,
1053
            0x2d, 0x61, 0x6e, 0x79, 0x63, 0x61, 0x73, 0x74, 0x2d, 0x32, 0x05, 0x76, 0x6f, 0x69,
1054
            0x63, 0x65, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00,
1055
            0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x01, 0x13, 0x00, 0x26, 0x00, 0x0a,
1056
            0x00, 0x01, 0x13, 0xc4, 0x0d, 0x73, 0x69, 0x70, 0x2d, 0x61, 0x6e, 0x79, 0x63, 0x61,
1057
            0x73, 0x74, 0x2d, 0x31, 0x05, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x06, 0x67, 0x6f, 0x6f,
1058
            0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00,
1059
        ];
1060

1061
        let (rem, (response, _flags)) = dns_parse_response(pkt).unwrap();
1062
        // The data should be fully parsed.
1063
        assert_eq!(rem.len(), 0);
1064

1065
        assert_eq!(response.answers.len(), 2);
1066

1067
        let answer1 = &response.answers[0];
1068
        if let DNSRData::SRV(srv) = &answer1.data {
1069
            assert_eq!(srv.priority, 20);
1070
            assert_eq!(srv.weight, 1);
1071
            assert_eq!(srv.port, 5060);
1072
            assert_eq!(
1073
                srv.target.value,
1074
                "sip-anycast-2.voice.google.com".as_bytes().to_vec()
1075
            );
1076
        } else {
1077
            panic!("Expected DNSRData::SRV");
1078
        }
1079
        let answer2 = &response.answers[1];
1080
        if let DNSRData::SRV(srv) = &answer2.data {
1081
            assert_eq!(srv.priority, 10);
1082
            assert_eq!(srv.weight, 1);
1083
            assert_eq!(srv.port, 5060);
1084
            assert_eq!(
1085
                srv.target.value,
1086
                "sip-anycast-1.voice.google.com".as_bytes().to_vec()
1087
            );
1088
        } else {
1089
            panic!("Expected DNSRData::SRV");
1090
        }
1091
    }
1092

1093
    #[test]
1094
    fn test_dns_parse_name_truncated() {
1095
        // Generate a non-compressed hostname over our maximum of 1024.
1096
        let mut buf: Vec<u8> = vec![];
1097

1098
        for i in 1..18 {
1099
            buf.push(0b0011_1111);
1100
            buf.resize(i * 64, b'a');
1101
        }
1102

1103
        let mut flags = DNSNameFlags::default();
1104
        let (rem, name) = dns_parse_name(&buf, &buf, &mut flags).unwrap();
1105
        assert_eq!(name.value.len(), MAX_NAME_LEN);
1106
        assert!(name.flags.contains(DNSNameFlags::TRUNCATED));
1107
        assert!(rem.is_empty());
1108
    }
1109

1110
    #[test]
1111
    fn test_dns_parse_name_truncated_max_segments_no_pointer() {
1112
        let mut buf: Vec<u8> = vec![];
1113
        for _ in 0..256 {
1114
            buf.push(0b0000_0001);
1115
            buf.push(b'a');
1116
        }
1117

1118
        // This should fail as we've hit the segment limit without a
1119
        // pointer, we'd need to keep parsing more segments to figure
1120
        // out where the next data point lies.
1121
        let mut flags = DNSNameFlags::default();
1122
        assert!(dns_parse_name(&buf, &buf, &mut flags).is_err());
1123
    }
1124

1125
    #[test]
1126
    fn test_dns_parse_name_truncated_max_segments_with_pointer() {
1127
        #[rustfmt::skip]
1128
        let buf: Vec<u8> = vec![
1129
            // "a" at the beginning of the buffer.
1130
            0b0000_0001,
1131
            b'a',
1132

1133
            // Followed by a pointer back to the beginning.
1134
            0b1100_0000,
1135
            0b0000_0000,
1136

1137
            // The start of the name, which is pointer to the beginning of
1138
            // the buffer.
1139
            0b1100_0000,
1140
            0b000_0000
1141
        ];
1142

1143
        let mut flags = DNSNameFlags::default();
1144
        let (_rem, name) = dns_parse_name(&buf[4..], &buf, &mut flags).unwrap();
1145
        assert_eq!(name.value.len(), 255);
1146
        assert!(name.flags.contains(DNSNameFlags::TRUNCATED));
1147
    }
1148

1149
    #[test]
1150
    fn test_dns_parse_name_self_reference() {
1151
        let buf = vec![0b1100_0000, 0b0000_0000];
1152
        let mut flags = DNSNameFlags::default();
1153
        assert!(dns_parse_name(&buf, &buf, &mut flags).is_err());
1154
    }
1155
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc