| // Copyright 2016 The Fuchsia 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 "garnet/bin/netconnector/netconnector_impl.h" |
| |
| #include <iostream> |
| |
| #include <lib/async/cpp/task.h> |
| #include <lib/async/default.h> |
| #include <lib/fit/function.h> |
| #include <zx/time.h> |
| |
| #include "garnet/bin/netconnector/device_service_provider.h" |
| #include "garnet/bin/netconnector/host_name.h" |
| #include "garnet/bin/netconnector/netconnector_params.h" |
| #include "lib/fxl/functional/make_copyable.h" |
| #include "lib/fxl/logging.h" |
| |
| namespace netconnector { |
| |
| // static |
| const inet::IpPort NetConnectorImpl::kPort = inet::IpPort::From_uint16_t(7777); |
| // static |
| const std::string NetConnectorImpl::kFuchsiaServiceName = "_fuchsia._tcp."; |
| // static |
| const std::string NetConnectorImpl::kLocalDeviceName = "local"; |
| |
| NetConnectorImpl::NetConnectorImpl(NetConnectorParams* params, |
| fit::closure quit_callback) |
| : params_(params), |
| quit_callback_(std::move(quit_callback)), |
| startup_context_(component::StartupContext::CreateFromStartupInfo()), |
| // TODO(dalesat): Create a new RespondingServiceHost per user. |
| // Requestors should provide user credentials allowing a ServiceAgent |
| // to obtain a user environment. A RespondingServiceHost should be |
| // created with that environment so that responding services are |
| // launched in the correct environment. |
| responding_service_host_(startup_context_->environment()) { |
| FXL_DCHECK(quit_callback_); |
| |
| if (!params->listen()) { |
| // Start the listener. |
| fuchsia::netconnector::NetConnectorSyncPtr net_connector; |
| startup_context_->ConnectToEnvironmentService(net_connector.NewRequest()); |
| fuchsia::mdns::MdnsServicePtr mdns_service = |
| startup_context_ |
| ->ConnectToEnvironmentService<fuchsia::mdns::MdnsService>(); |
| |
| if (params_->mdns_verbose()) { |
| mdns_service->SetVerbose(true); |
| } |
| |
| if (params_->show_devices()) { |
| uint64_t version; |
| fidl::VectorPtr<fidl::StringPtr> device_names; |
| net_connector->GetKnownDeviceNames( |
| fuchsia::netconnector::kInitialKnownDeviceNames, &version, |
| &device_names); |
| |
| if (device_names->size() == 0) { |
| std::cout << "No remote devices found\n"; |
| } else { |
| for (auto& device_name : *device_names) { |
| std::cout << device_name << "\n"; |
| } |
| } |
| } |
| |
| quit_callback_(); |
| return; |
| } |
| |
| // Running as listener. |
| startup_context_->outgoing().AddPublicService(bindings_.GetHandler(this)); |
| |
| device_names_publisher_.SetCallbackRunner( |
| [this](const GetKnownDeviceNamesCallback& callback, uint64_t version) { |
| fidl::VectorPtr<fidl::StringPtr> device_names = |
| fidl::VectorPtr<fidl::StringPtr>::New(0); |
| |
| for (auto& pair : params_->devices()) { |
| device_names.push_back(pair.first); |
| } |
| |
| callback(version, std::move(device_names)); |
| }); |
| |
| // Register services. |
| for (auto& pair : params->MoveServices()) { |
| responding_service_host_.RegisterSingleton(pair.first, |
| std::move(pair.second)); |
| } |
| |
| StartListener(); |
| } |
| |
| NetConnectorImpl::~NetConnectorImpl() {} |
| |
| void NetConnectorImpl::StartListener() { |
| if (!NetworkIsReady()) { |
| async::PostDelayedTask(async_get_default_dispatcher(), |
| [this]() { StartListener(); }, zx::sec(5)); |
| return; |
| } |
| |
| listener_.Start(kPort, [this](fxl::UniqueFD fd) { |
| AddServiceAgent(ServiceAgent::Create(std::move(fd), this)); |
| }); |
| |
| mdns_service_ = |
| startup_context_ |
| ->ConnectToEnvironmentService<fuchsia::mdns::MdnsService>(); |
| |
| host_name_ = GetHostName(); |
| |
| mdns_service_->PublishServiceInstance( |
| kFuchsiaServiceName, host_name_, kPort.as_uint16_t(), |
| fidl::VectorPtr<fidl::StringPtr>(), |
| [this](fuchsia::mdns::MdnsResult result) { |
| switch (result) { |
| case fuchsia::mdns::MdnsResult::OK: |
| break; |
| case fuchsia::mdns::MdnsResult::INVALID_SERVICE_NAME: |
| FXL_LOG(ERROR) << "mDNS service rejected service name " |
| << kFuchsiaServiceName << "."; |
| break; |
| case fuchsia::mdns::MdnsResult::INVALID_INSTANCE_NAME: |
| FXL_LOG(ERROR) << "mDNS service rejected instance name " |
| << host_name_ << "."; |
| break; |
| case fuchsia::mdns::MdnsResult::ALREADY_PUBLISHED_LOCALLY: |
| FXL_LOG(ERROR) << "mDNS service is already publishing a " |
| << kFuchsiaServiceName << " service instance."; |
| break; |
| case fuchsia::mdns::MdnsResult::ALREADY_PUBLISHED_ON_SUBNET: |
| FXL_LOG(ERROR) << "Another device is already publishing a " |
| << kFuchsiaServiceName |
| << " service instance for this host's name (" |
| << host_name_ << ")."; |
| break; |
| } |
| }); |
| |
| fuchsia::mdns::MdnsServiceSubscriptionPtr subscription; |
| mdns_service_->SubscribeToService(kFuchsiaServiceName, |
| subscription.NewRequest()); |
| |
| mdns_subscriber_.Init( |
| std::move(subscription), |
| [this](const fuchsia::mdns::MdnsServiceInstance* from, |
| const fuchsia::mdns::MdnsServiceInstance* to) { |
| if (from == nullptr && to != nullptr) { |
| if (to->v4_address) { |
| std::cerr << "netconnector: Device '" << to->instance_name |
| << "' discovered at address " |
| << inet::SocketAddress(to->v4_address.get()) << "\n"; |
| params_->RegisterDevice(to->instance_name, |
| inet::IpAddress(&to->v4_address->addr)); |
| device_names_publisher_.SendUpdates(); |
| } else if (to->v6_address) { |
| std::cerr << "netconnector: Device '" << to->instance_name |
| << "' discovered at address " |
| << inet::SocketAddress(to->v6_address.get()) << "\n"; |
| params_->RegisterDevice(to->instance_name, |
| inet::IpAddress(&to->v6_address->addr)); |
| device_names_publisher_.SendUpdates(); |
| } |
| } else if (from != nullptr && to == nullptr) { |
| std::cerr << "netconnector: Device '" << from->instance_name |
| << "' lost\n"; |
| params_->UnregisterDevice(from->instance_name); |
| device_names_publisher_.SendUpdates(); |
| } |
| }); |
| } |
| |
| void NetConnectorImpl::ReleaseDeviceServiceProvider( |
| DeviceServiceProvider* device_service_provider) { |
| size_t removed = device_service_providers_.erase(device_service_provider); |
| FXL_DCHECK(removed == 1); |
| } |
| |
| void NetConnectorImpl::ReleaseRequestorAgent(RequestorAgent* requestor_agent) { |
| size_t removed = requestor_agents_.erase(requestor_agent); |
| FXL_DCHECK(removed == 1); |
| } |
| |
| void NetConnectorImpl::ReleaseServiceAgent(ServiceAgent* service_agent) { |
| size_t removed = service_agents_.erase(service_agent); |
| FXL_DCHECK(removed == 1); |
| } |
| |
| void NetConnectorImpl::GetDeviceServiceProvider( |
| fidl::StringPtr device_name, |
| fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> request) { |
| if (device_name == host_name_ || device_name == kLocalDeviceName) { |
| responding_service_host_.AddBinding(std::move(request)); |
| return; |
| } |
| |
| auto iter = params_->devices().find(device_name); |
| if (iter == params_->devices().end()) { |
| FXL_LOG(ERROR) << "Unrecognized device name " << device_name; |
| return; |
| } |
| |
| AddDeviceServiceProvider(DeviceServiceProvider::Create( |
| device_name, inet::SocketAddress(iter->second, kPort), std::move(request), |
| this)); |
| } |
| |
| void NetConnectorImpl::GetKnownDeviceNames( |
| uint64_t version_last_seen, GetKnownDeviceNamesCallback callback) { |
| device_names_publisher_.Get(version_last_seen, std::move(callback)); |
| } |
| |
| void NetConnectorImpl::RegisterServiceProvider( |
| fidl::StringPtr name, |
| fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> handle) { |
| FXL_LOG(INFO) << "Service '" << name << "' provider registered."; |
| responding_service_host_.RegisterProvider(name, std::move(handle)); |
| } |
| |
| void NetConnectorImpl::AddDeviceServiceProvider( |
| std::unique_ptr<DeviceServiceProvider> device_service_provider) { |
| DeviceServiceProvider* raw_ptr = device_service_provider.get(); |
| device_service_providers_.emplace(raw_ptr, |
| std::move(device_service_provider)); |
| } |
| |
| void NetConnectorImpl::AddRequestorAgent( |
| std::unique_ptr<RequestorAgent> requestor_agent) { |
| RequestorAgent* raw_ptr = requestor_agent.get(); |
| requestor_agents_.emplace(raw_ptr, std::move(requestor_agent)); |
| } |
| |
| void NetConnectorImpl::AddServiceAgent( |
| std::unique_ptr<ServiceAgent> service_agent) { |
| ServiceAgent* raw_ptr = service_agent.get(); |
| service_agents_.emplace(raw_ptr, std::move(service_agent)); |
| } |
| |
| } // namespace netconnector |