blob: d40fb48e59c36e3622eb11e1d3017555f25b4f0e [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "discovery/dnssd/public/dns_sd_txt_record.h"
#include <cctype>
#include <utility>
namespace openscreen {
namespace discovery {
// static
bool DnsSdTxtRecord::IsValidTxtValue(const std::string& key,
const std::vector<uint8_t>& value) {
// The max length of any individual TXT record is 255 bytes.
if (key.size() + value.size() + 1 /* for equals */ > 255) {
return false;
}
return IsKeyValid(key);
}
// static
bool DnsSdTxtRecord::IsValidTxtValue(const std::string& key, uint8_t value) {
return IsValidTxtValue(key, std::vector<uint8_t>{value});
}
// static
bool DnsSdTxtRecord::IsValidTxtValue(const std::string& key,
const std::string& value) {
return IsValidTxtValue(key, std::vector<uint8_t>(value.begin(), value.end()));
}
Error DnsSdTxtRecord::SetValue(const std::string& key,
std::vector<uint8_t> value) {
if (!IsValidTxtValue(key, value)) {
return Error::Code::kParameterInvalid;
}
key_value_txt_[key] = std::move(value);
ClearFlag(key);
return Error::None();
}
Error DnsSdTxtRecord::SetValue(const std::string& key,
const std::string& value) {
return SetValue(key, std::vector<uint8_t>(value.begin(), value.end()));
}
Error DnsSdTxtRecord::SetFlag(const std::string& key, bool value) {
if (!IsKeyValid(key)) {
return Error::Code::kParameterInvalid;
}
if (value) {
boolean_txt_.insert(key);
} else {
ClearFlag(key);
}
ClearValue(key);
return Error::None();
}
ErrorOr<DnsSdTxtRecord::ValueRef> DnsSdTxtRecord::GetValue(
const std::string& key) const {
if (!IsKeyValid(key)) {
return Error::Code::kParameterInvalid;
}
auto it = key_value_txt_.find(key);
if (it != key_value_txt_.end()) {
return std::cref(it->second);
}
return Error::Code::kItemNotFound;
}
ErrorOr<bool> DnsSdTxtRecord::GetFlag(const std::string& key) const {
if (!IsKeyValid(key)) {
return Error::Code::kParameterInvalid;
}
return boolean_txt_.find(key) != boolean_txt_.end();
}
Error DnsSdTxtRecord::ClearValue(const std::string& key) {
if (!IsKeyValid(key)) {
return Error::Code::kParameterInvalid;
}
key_value_txt_.erase(key);
return Error::None();
}
Error DnsSdTxtRecord::ClearFlag(const std::string& key) {
if (!IsKeyValid(key)) {
return Error::Code::kParameterInvalid;
}
boolean_txt_.erase(key);
return Error::None();
}
// static
bool DnsSdTxtRecord::IsKeyValid(const std::string& key) {
// The max length of any individual TXT record is 255 bytes.
if (key.size() > 255) {
return false;
}
// The length of a key must be at least 1.
if (key.size() < 1) {
return false;
}
// Keys must contain only valid characters.
for (char key_char : key) {
if (key_char < char{0x20} || key_char > char{0x7E} || key_char == '=') {
return false;
}
}
return true;
}
std::vector<std::vector<uint8_t>> DnsSdTxtRecord::GetData() const {
std::vector<std::vector<uint8_t>> data;
for (const auto& pair : key_value_txt_) {
data.emplace_back();
std::vector<uint8_t>* new_entry = &data.back();
new_entry->reserve(pair.first.size() + 1 + pair.second.size());
new_entry->insert(new_entry->end(), pair.first.begin(), pair.first.end());
new_entry->push_back('=');
new_entry->insert(new_entry->end(), pair.second.begin(), pair.second.end());
}
for (const auto& flag : boolean_txt_) {
data.emplace_back();
std::vector<uint8_t>* new_entry = &data.back();
new_entry->insert(new_entry->end(), flag.begin(), flag.end());
}
return data;
}
bool DnsSdTxtRecord::CaseInsensitiveComparison::operator()(
const std::string& lhs,
const std::string& rhs) const {
if (lhs.size() != rhs.size()) {
return lhs < rhs;
}
for (size_t i = 0; i < lhs.size(); i++) {
int lhs_char = tolower(lhs[i]);
int rhs_char = tolower(rhs[i]);
if (lhs_char != rhs_char) {
return lhs_char < rhs_char;
}
}
return false;
}
} // namespace discovery
} // namespace openscreen