Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2021, Sergey Bugaev <bugaevc@serenityos.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include "Name.h"
9#include <AK/Random.h>
10#include <AK/Stream.h>
11#include <AK/Vector.h>
12#include <ctype.h>
13
14namespace DNS {
15
16Name::Name(DeprecatedString const& name)
17{
18 if (name.ends_with('.'))
19 m_name = name.substring(0, name.length() - 1);
20 else
21 m_name = name;
22}
23
24Name Name::parse(u8 const* data, size_t& offset, size_t max_offset, size_t recursion_level)
25{
26 if (recursion_level > 4)
27 return {};
28
29 StringBuilder builder;
30 while (true) {
31 if (offset >= max_offset)
32 return {};
33 u8 b = data[offset++];
34 if (b == '\0') {
35 // This terminates the name.
36 return builder.to_deprecated_string();
37 } else if ((b & 0xc0) == 0xc0) {
38 // The two bytes tell us the offset when to continue from.
39 if (offset >= max_offset)
40 return {};
41 size_t dummy = (b & 0x3f) << 8 | data[offset++];
42 auto rest_of_name = parse(data, dummy, max_offset, recursion_level + 1);
43 builder.append(rest_of_name.as_string());
44 return builder.to_deprecated_string();
45 } else {
46 // This is the length of a part.
47 if (offset + b >= max_offset)
48 return {};
49 builder.append((char const*)&data[offset], (size_t)b);
50 builder.append('.');
51 offset += b;
52 }
53 }
54}
55
56size_t Name::serialized_size() const
57{
58 if (m_name.is_empty())
59 return 1;
60 return m_name.length() + 2;
61}
62
63void Name::randomize_case()
64{
65 StringBuilder builder;
66 for (char c : m_name) {
67 // Randomize the 0x20 bit in every ASCII character.
68 if (isalpha(c)) {
69 if (get_random_uniform(2))
70 c |= 0x20;
71 else
72 c &= ~0x20;
73 }
74 builder.append(c);
75 }
76 m_name = builder.to_deprecated_string();
77}
78
79ErrorOr<void> Name::write_to_stream(Stream& stream) const
80{
81 auto parts = as_string().split_view('.');
82 for (auto& part : parts) {
83 TRY(stream.write_value<u8>(part.length()));
84 TRY(stream.write_until_depleted(part.bytes()));
85 }
86 TRY(stream.write_value('\0'));
87 return {};
88}
89
90unsigned Name::Traits::hash(Name const& name)
91{
92 return CaseInsensitiveStringTraits::hash(name.as_string());
93}
94
95bool Name::Traits::equals(Name const& a, Name const& b)
96{
97 return CaseInsensitiveStringTraits::equals(a.as_string(), b.as_string());
98}
99
100}