blob: b21c90de1005cf5c1793d4f7fa53d3453183e11d [file] [log] [blame]
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "connections/implementation/wifi_lan_service_info.h"
#include <inttypes.h>
#include <cstring>
#include <utility>
#include "absl/strings/str_cat.h"
#include "internal/platform/base64_utils.h"
#include "internal/platform/base_input_stream.h"
#include "internal/platform/logging.h"
namespace location {
namespace nearby {
namespace connections {
// These definitions are necessary before C++17.
constexpr absl::string_view WifiLanServiceInfo::kKeyEndpointInfo;
constexpr std::uint32_t WifiLanServiceInfo::kServiceIdHashLength;
constexpr int WifiLanServiceInfo::kMaxEndpointInfoLength;
WifiLanServiceInfo::WifiLanServiceInfo(Version version, Pcp pcp,
absl::string_view endpoint_id,
const ByteArray& service_id_hash,
const ByteArray& endpoint_info,
const ByteArray& uwb_address,
WebRtcState web_rtc_state) {
if (version != Version::kV1 || endpoint_id.empty() ||
endpoint_id.length() != kEndpointIdLength ||
service_id_hash.size() != kServiceIdHashLength ||
endpoint_info.size() > kMaxEndpointInfoLength) {
return;
}
switch (pcp) {
case Pcp::kP2pCluster: // Fall through
case Pcp::kP2pStar: // Fall through
case Pcp::kP2pPointToPoint:
break;
default:
return;
}
version_ = version;
pcp_ = pcp;
service_id_hash_ = service_id_hash;
endpoint_id_ = std::string(endpoint_id);
endpoint_info_ = endpoint_info;
uwb_address_ = uwb_address;
web_rtc_state_ = web_rtc_state;
}
WifiLanServiceInfo::WifiLanServiceInfo(const NsdServiceInfo& nsd_service_info) {
auto txt_endpoint_info_name =
nsd_service_info.GetTxtRecord(std::string(kKeyEndpointInfo));
if (!txt_endpoint_info_name.empty()) {
endpoint_info_ = Base64Utils::Decode(txt_endpoint_info_name);
if (endpoint_info_.size() > kMaxEndpointInfoLength) {
NEARBY_LOG(INFO,
"Cannot deserialize EndpointInfo: expecting endpoint info "
"max %d raw bytes, got %" PRIu64,
kMaxEndpointInfoLength, endpoint_info_.size());
return;
}
}
auto service_info_name = nsd_service_info.GetServiceName();
ByteArray service_info_bytes = Base64Utils::Decode(service_info_name);
if (service_info_bytes.Empty()) {
NEARBY_LOG(
INFO,
"Cannot deserialize WifiLanServiceInfo: failed Base64 decoding of %s",
std::string(service_info_name).c_str());
return;
}
if (service_info_bytes.size() < kMinLanServiceNameLength) {
NEARBY_LOG(INFO,
"Cannot deserialize WifiLanServiceInfo: expecting min %d raw "
"bytes, got %" PRIu64,
kMinLanServiceNameLength, service_info_bytes.size());
return;
}
BaseInputStream base_input_stream{service_info_bytes};
// The first 1 byte is supposed to be the version and pcp.
auto version_and_pcp_byte = static_cast<char>(base_input_stream.ReadUint8());
// The upper 3 bits are supposed to be the version.
version_ =
static_cast<Version>((version_and_pcp_byte & kVersionBitmask) >> 5);
if (version_ != Version::kV1) {
NEARBY_LOG(INFO,
"Cannot deserialize WifiLanServiceInfo: unsupported Version %d",
version_);
return;
}
// The lower 5 bits are supposed to be the Pcp.
pcp_ = static_cast<Pcp>(version_and_pcp_byte & kPcpBitmask);
switch (pcp_) {
case Pcp::kP2pCluster: // Fall through
case Pcp::kP2pStar: // Fall through
case Pcp::kP2pPointToPoint:
break;
default:
NEARBY_LOG(INFO,
"Cannot deserialize WifiLanServiceInfo: unsupported V1 PCP %d",
pcp_);
}
// The next 4 bytes are supposed to be the endpoint_id.
endpoint_id_ = std::string{base_input_stream.ReadBytes(kEndpointIdLength)};
// The next 3 bytes are supposed to be the service_id_hash.
service_id_hash_ = base_input_stream.ReadBytes(kServiceIdHashLength);
// The next 1 byte is supposed to be the length of the uwb_address. If
// available, continues to deserialize UWB address and extra field of WebRtc
// state.
if (base_input_stream.IsAvailable(1)) {
std::uint32_t expected_uwb_address_length = base_input_stream.ReadUint8();
// If the length of uwb_address is not zero, then retrieve it.
if (expected_uwb_address_length != 0) {
uwb_address_ = base_input_stream.ReadBytes(expected_uwb_address_length);
if (uwb_address_.Empty() ||
uwb_address_.size() != expected_uwb_address_length) {
NEARBY_LOG(INFO,
"Cannot deserialize WifiLanServiceInfo: expected "
"uwbAddress size to be %d bytes, got %" PRIu64,
expected_uwb_address_length, uwb_address_.size());
// Clear enpoint_id for validity.
endpoint_id_.clear();
return;
}
}
// The next 1 byte is extra field.
web_rtc_state_ = WebRtcState::kUndefined;
if (base_input_stream.IsAvailable(kExtraFieldLength)) {
auto extra_field = static_cast<char>(base_input_stream.ReadUint8());
web_rtc_state_ = (extra_field & kWebRtcConnectableFlagBitmask) == 1
? WebRtcState::kConnectable
: WebRtcState::kUnconnectable;
}
}
}
WifiLanServiceInfo::operator NsdServiceInfo() const {
if (!IsValid()) {
return {};
}
// The upper 3 bits are the Version.
auto version_and_pcp_byte = static_cast<char>(
(static_cast<uint32_t>(Version::kV1) << 5) & kVersionBitmask);
// The lower 5 bits are the PCP.
version_and_pcp_byte |=
static_cast<char>(static_cast<uint32_t>(pcp_) & kPcpBitmask);
std::string out = absl::StrCat(std::string(1, version_and_pcp_byte),
endpoint_id_, std::string(service_id_hash_));
// The next bytes are UWB address field.
if (!uwb_address_.Empty()) {
absl::StrAppend(&out, std::string(1, uwb_address_.size()));
absl::StrAppend(&out, std::string(uwb_address_));
} else {
// Write UWB address with length 0 to be able to read the next field, which
// needs to be appended.
if (web_rtc_state_ != WebRtcState::kUndefined)
absl::StrAppend(&out, std::string(1, uwb_address_.size()));
}
// The next 1 byte is extra field.
if (web_rtc_state_ != WebRtcState::kUndefined) {
int web_rtc_connectable_flag =
(web_rtc_state_ == WebRtcState::kConnectable) ? 1 : 0;
char field_byte = static_cast<char>(web_rtc_connectable_flag) &
kWebRtcConnectableFlagBitmask;
absl::StrAppend(&out, std::string(1, field_byte));
}
NsdServiceInfo nsd_service_info;
nsd_service_info.SetServiceName(
Base64Utils::Encode(ByteArray{std::move(out)}));
nsd_service_info.SetTxtRecord(std::string(kKeyEndpointInfo),
Base64Utils::Encode(endpoint_info_));
return nsd_service_info;
}
} // namespace connections
} // namespace nearby
} // namespace location