Files
nearby/internal/platform/implementation/linux/wifi_lan.cc
T
2024-10-26 19:15:12 +05:30

282 lines
9.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2023 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 <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <cerrno>
#include <cstdint>
#include <cstring>
#include <memory>
#include <utility>
#include <sdbus-c++/Error.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/Types.h>
#include "absl/strings/substitute.h"
#include "internal/platform/implementation/linux/avahi.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/wifi_lan.h"
#include "internal/platform/implementation/linux/wifi_lan_server_socket.h"
#include "internal/platform/implementation/linux/wifi_lan_socket.h"
#include "internal/platform/implementation/linux/wifi_medium.h"
#include "internal/platform/implementation/wifi_lan.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
WifiLanMedium::WifiLanMedium(sdbus::IConnection &system_bus)
: system_bus_(system_bus),
network_manager_(std::make_shared<NetworkManager>(system_bus)),
avahi_(std::make_shared<avahi::Server>(system_bus)) {}
bool WifiLanMedium::IsNetworkConnected() const {
auto state = network_manager_->getState();
return state == NetworkManager::kNMStateConnectedLocal ||
state == NetworkManager::kNMStateConnectedSite ||
state == NetworkManager::kNMStateConnectedGlobal;
}
std::optional<std::pair<std::string, std::string>> entry_group_key(
const NsdServiceInfo &nsd_service_info) {
auto name = nsd_service_info.GetServiceName();
if (name.empty()) {
NEARBY_LOGS(ERROR) << __func__ << ": service name cannot be empty";
return std::nullopt;
}
auto type = nsd_service_info.GetServiceType();
if (type.empty()) {
NEARBY_LOGS(ERROR) << __func__ << ": service type cannot be empty";
return std::nullopt;
}
return std::make_pair(std::move(name), std::move(type));
}
bool WifiLanMedium::StartAdvertising(const NsdServiceInfo &nsd_service_info) {
auto key = entry_group_key(nsd_service_info);
if (!key.has_value()) {
return false;
}
{
absl::ReaderMutexLock l(&entry_groups_mutex_);
if (entry_groups_.count(*key) == 1) {
NEARBY_LOGS(ERROR) << __func__
<< ": advertising is already active for this service";
return false;
}
}
auto txt_records_map = nsd_service_info.GetTxtRecords();
std::vector<std::vector<std::uint8_t>> txt_records(txt_records_map.size());
std::size_t i = 0;
for (auto [key, value] : nsd_service_info.GetTxtRecords()) {
std::string entry = absl::Substitute("$0=$1", key, value);
txt_records[i++] = std::vector<std::uint8_t>(entry.begin(), entry.end());
}
sdbus::ObjectPath entry_group_path;
try {
entry_group_path = avahi_->EntryGroupNew();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(avahi_, "EntryGroupNew", e);
return false;
}
auto entry_group =
std::make_unique<avahi::EntryGroup>(system_bus_, entry_group_path);
try {
entry_group->AddService(
-1, // AVAHI_IF_UNSPEC. This will advertise on all possible interfaces
-1, // AVAHI_PROTO_UNSPED
0, nsd_service_info.GetServiceName(), nsd_service_info.GetServiceType(),
std::string(), std::string(), nsd_service_info.GetPort(), txt_records);
entry_group->Commit();
} catch (const sdbus::Error &e) {
NEARBY_LOGS(ERROR) << __func__ << ": Got error '" << e.getName()
<< "' with message '" << e.getMessage()
<< "' while adding service";
return false;
}
absl::MutexLock l(&entry_groups_mutex_);
entry_groups_.insert({*key, std::move(entry_group)});
return true;
}
bool WifiLanMedium::StopAdvertising(const NsdServiceInfo &nsd_service_info) {
auto key = entry_group_key(nsd_service_info);
if (!key.has_value()) {
return false;
}
absl::MutexLock l(&entry_groups_mutex_);
if (entry_groups_.count(*key) == 0) {
NEARBY_LOGS(ERROR) << __func__
<< ": Advertising is already inactive for this service.";
return false;
}
entry_groups_.erase(*key);
return true;
}
bool WifiLanMedium::StartDiscovery(
const std::string &service_type,
api::WifiLanMedium::DiscoveredServiceCallback callback) {
{
// set up and start a new service discovery browser
// using Avahi's D-Bus API if a browser for the
// specified service type doesn't already exist
absl::ReaderMutexLock l(&service_browsers_mutex_);
if (service_browsers_.count(service_type) != 0) {
auto &object = service_browsers_[service_type];
NEARBY_LOGS(ERROR) << __func__ << ": A service browser for service type "
<< service_type << " already exists at "
<< object->getObjectPath();
return false;
}
}
try {
sdbus::ObjectPath browser_object_path =
avahi_->ServiceBrowserPrepare(-1, // AVAHI_IF_UNSPEC
-1, // AVAHI_PROTO_UNSPED
service_type, std::string(), 0);
NEARBY_VLOG(1)
<< __func__
<< ": Created a new org.freedesktop.Avahi.ServiceBrowser object at "
<< browser_object_path;
absl::MutexLock l(&service_browsers_mutex_);
service_browsers_.emplace(
service_type,
std::make_unique<avahi::ServiceBrowser>(
system_bus_, browser_object_path, std::move(callback), avahi_));
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(avahi_, "ServiceBrowserPrepare", e);
return false;
}
service_browsers_mutex_.ReaderLock();
auto &browser = service_browsers_[service_type];
service_browsers_mutex_.ReaderUnlock();
try {
NEARBY_VLOG(1) << __func__ << ": Starting service discovery for "
<< browser->getObjectPath();
browser->Start();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(browser, "Start", e);
return false;
}
return true;
}
bool WifiLanMedium::StopDiscovery(const std::string &service_type) {
absl::MutexLock l(&service_browsers_mutex_);
if (service_browsers_.count(service_type) == 0) {
NEARBY_LOGS(ERROR) << __func__ << ": Service type " << service_type
<< " has not been registered for discovery";
return false;
}
service_browsers_.erase(service_type);
return true;
}
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::ConnectToService(
const std::string &ip_address, int port,
CancellationFlag *cancellation_flag) {
//creates a TCP socket, connects it to a specified
//IP address and port, and then wraps the sockets file descriptor in an
//sdbus::UnixFd object to pass it to another component
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
NEARBY_LOGS(ERROR) << __func__
<< ": Error opening socket: " << std::strerror(errno);
return nullptr;
}
NEARBY_VLOG(1) << __func__ << ": Connecting to " << ip_address << ":"
<< port;
struct sockaddr_in addr;
addr.sin_addr.s_addr = inet_addr(ip_address.c_str());
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
auto ret =
connect(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr));
if (ret < 0) {
NEARBY_LOGS(ERROR) << __func__ << ": Error connecting to socket: "
<< std::strerror(errno);
return nullptr;
}
//creating a unix file descriptor representing the opened socket and passing it on
sdbus::UnixFd fd(sock);
return std::make_unique<WifiLanSocket>(std::move(fd));
}
std::unique_ptr<api::WifiLanServerSocket> WifiLanMedium::ListenForService(
int port) {
auto sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
NEARBY_LOGS(ERROR) << __func__
<< ": Error opening socket: " << std::strerror(errno);
return nullptr;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
auto ret =
bind(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr));
if (ret < 0) {
NEARBY_LOGS(ERROR) << __func__
<< ": Error binding to socket: " << std::strerror(errno);
return nullptr;
}
ret = listen(sock, 0);
if (ret < 0) {
NEARBY_LOGS(ERROR) << __func__ << ": Error listening on socket: "
<< std::strerror(errno);
return nullptr;
}
NEARBY_VLOG(1) << __func__ << "Listening for services on port " << port;
return std::make_unique<WifiLanServerSocket>(sock, network_manager_,
system_bus_);
}
absl::optional<std::pair<std::int32_t, std::int32_t>> GetDynamicPortRange() {
return absl::nullopt;
}
} // namespace linux
} // namespace nearby