1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "discovery/dnssd/public/dns_sd_txt_record.h"
6
7 #include <cctype>
8 #include <utility>
9
10 namespace openscreen {
11 namespace discovery {
12
13 // static
IsValidTxtValue(const std::string & key,const std::vector<uint8_t> & value)14 bool DnsSdTxtRecord::IsValidTxtValue(const std::string& key,
15 const std::vector<uint8_t>& value) {
16 // The max length of any individual TXT record is 255 bytes.
17 if (key.size() + value.size() + 1 /* for equals */ > 255) {
18 return false;
19 }
20
21 return IsKeyValid(key);
22 }
23
24 // static
IsValidTxtValue(const std::string & key,uint8_t value)25 bool DnsSdTxtRecord::IsValidTxtValue(const std::string& key, uint8_t value) {
26 return IsValidTxtValue(key, std::vector<uint8_t>{value});
27 }
28
29 // static
IsValidTxtValue(const std::string & key,const std::string & value)30 bool DnsSdTxtRecord::IsValidTxtValue(const std::string& key,
31 const std::string& value) {
32 return IsValidTxtValue(key, std::vector<uint8_t>(value.begin(), value.end()));
33 }
34
SetValue(const std::string & key,std::vector<uint8_t> value)35 Error DnsSdTxtRecord::SetValue(const std::string& key,
36 std::vector<uint8_t> value) {
37 if (!IsValidTxtValue(key, value)) {
38 return Error::Code::kParameterInvalid;
39 }
40
41 key_value_txt_[key] = std::move(value);
42 ClearFlag(key);
43 return Error::None();
44 }
45
SetValue(const std::string & key,const std::string & value)46 Error DnsSdTxtRecord::SetValue(const std::string& key,
47 const std::string& value) {
48 return SetValue(key, std::vector<uint8_t>(value.begin(), value.end()));
49 }
50
SetFlag(const std::string & key,bool value)51 Error DnsSdTxtRecord::SetFlag(const std::string& key, bool value) {
52 if (!IsKeyValid(key)) {
53 return Error::Code::kParameterInvalid;
54 }
55
56 if (value) {
57 boolean_txt_.insert(key);
58 } else {
59 ClearFlag(key);
60 }
61 ClearValue(key);
62 return Error::None();
63 }
64
GetValue(const std::string & key) const65 ErrorOr<DnsSdTxtRecord::ValueRef> DnsSdTxtRecord::GetValue(
66 const std::string& key) const {
67 if (!IsKeyValid(key)) {
68 return Error::Code::kParameterInvalid;
69 }
70
71 auto it = key_value_txt_.find(key);
72 if (it != key_value_txt_.end()) {
73 return std::cref(it->second);
74 }
75
76 return Error::Code::kItemNotFound;
77 }
78
GetFlag(const std::string & key) const79 ErrorOr<bool> DnsSdTxtRecord::GetFlag(const std::string& key) const {
80 if (!IsKeyValid(key)) {
81 return Error::Code::kParameterInvalid;
82 }
83
84 return boolean_txt_.find(key) != boolean_txt_.end();
85 }
86
ClearValue(const std::string & key)87 Error DnsSdTxtRecord::ClearValue(const std::string& key) {
88 if (!IsKeyValid(key)) {
89 return Error::Code::kParameterInvalid;
90 }
91
92 key_value_txt_.erase(key);
93 return Error::None();
94 }
95
ClearFlag(const std::string & key)96 Error DnsSdTxtRecord::ClearFlag(const std::string& key) {
97 if (!IsKeyValid(key)) {
98 return Error::Code::kParameterInvalid;
99 }
100
101 boolean_txt_.erase(key);
102 return Error::None();
103 }
104
105 // static
IsKeyValid(const std::string & key)106 bool DnsSdTxtRecord::IsKeyValid(const std::string& key) {
107 // The max length of any individual TXT record is 255 bytes.
108 if (key.size() > 255) {
109 return false;
110 }
111
112 // The length of a key must be at least 1.
113 if (key.size() < 1) {
114 return false;
115 }
116
117 // Keys must contain only valid characters.
118 for (char key_char : key) {
119 if (key_char < char{0x20} || key_char > char{0x7E} || key_char == '=') {
120 return false;
121 }
122 }
123
124 return true;
125 }
126
GetData() const127 std::vector<std::vector<uint8_t>> DnsSdTxtRecord::GetData() const {
128 std::vector<std::vector<uint8_t>> data;
129 for (const auto& pair : key_value_txt_) {
130 data.emplace_back();
131 std::vector<uint8_t>* new_entry = &data.back();
132 new_entry->reserve(pair.first.size() + 1 + pair.second.size());
133 new_entry->insert(new_entry->end(), pair.first.begin(), pair.first.end());
134 new_entry->push_back('=');
135 new_entry->insert(new_entry->end(), pair.second.begin(), pair.second.end());
136 }
137
138 for (const auto& flag : boolean_txt_) {
139 data.emplace_back();
140 std::vector<uint8_t>* new_entry = &data.back();
141 new_entry->insert(new_entry->end(), flag.begin(), flag.end());
142 }
143
144 return data;
145 }
146
operator ()(const std::string & lhs,const std::string & rhs) const147 bool DnsSdTxtRecord::CaseInsensitiveComparison::operator()(
148 const std::string& lhs,
149 const std::string& rhs) const {
150 if (lhs.size() != rhs.size()) {
151 return lhs < rhs;
152 }
153
154 for (size_t i = 0; i < lhs.size(); i++) {
155 int lhs_char = tolower(lhs[i]);
156 int rhs_char = tolower(rhs[i]);
157
158 if (lhs_char != rhs_char) {
159 return lhs_char < rhs_char;
160 }
161 }
162
163 return false;
164 }
165
166 } // namespace discovery
167 } // namespace openscreen
168