| // Copyright 2022 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 "ipcz/node.h" |
| |
| #include <vector> |
| |
| #include "ipcz/ipcz.h" |
| #include "ipcz/node_connector.h" |
| #include "ipcz/node_link.h" |
| #include "ipcz/portal.h" |
| #include "ipcz/router.h" |
| #include "third_party/abseil-cpp/absl/base/macros.h" |
| #include "third_party/abseil-cpp/absl/synchronization/mutex.h" |
| #include "util/log.h" |
| #include "util/ref_counted.h" |
| |
| namespace ipcz { |
| |
| Node::Node(Type type, const IpczDriver& driver, IpczDriverHandle driver_node) |
| : type_(type), driver_(driver), driver_node_(driver_node) { |
| if (type_ == Type::kBroker) { |
| // Only brokers assign their own names. |
| assigned_name_ = GenerateRandomName(); |
| DVLOG(4) << "Created new broker node " << assigned_name_.ToString(); |
| } else { |
| DVLOG(4) << "Created new non-broker node " << this; |
| } |
| } |
| |
| Node::~Node() = default; |
| |
| IpczResult Node::Close() { |
| absl::flat_hash_map<NodeName, Ref<NodeLink>> node_links; |
| { |
| absl::MutexLock lock(&mutex_); |
| node_links_.swap(node_links); |
| broker_link_.reset(); |
| } |
| |
| for (const auto& entry : node_links) { |
| entry.second->Deactivate(); |
| } |
| return IPCZ_RESULT_OK; |
| } |
| |
| void Node::ShutDown() { |
| absl::flat_hash_map<NodeName, Ref<NodeLink>> node_links; |
| { |
| absl::MutexLock lock(&mutex_); |
| std::swap(node_links_, node_links); |
| broker_link_.reset(); |
| } |
| |
| for (const auto& entry : node_links) { |
| entry.second->Deactivate(); |
| } |
| } |
| |
| IpczResult Node::ConnectNode(IpczDriverHandle driver_transport, |
| IpczConnectNodeFlags flags, |
| absl::Span<IpczHandle> initial_portals) { |
| std::vector<Ref<Portal>> portals(initial_portals.size()); |
| for (size_t i = 0; i < initial_portals.size(); ++i) { |
| auto portal = |
| MakeRefCounted<Portal>(WrapRefCounted(this), MakeRefCounted<Router>()); |
| portals[i] = portal; |
| initial_portals[i] = Portal::ReleaseAsHandle(std::move(portal)); |
| } |
| |
| auto transport = MakeRefCounted<DriverTransport>( |
| DriverObject(WrapRefCounted(this), driver_transport)); |
| IpczResult result = NodeConnector::ConnectNode(WrapRefCounted(this), |
| transport, flags, portals); |
| if (result != IPCZ_RESULT_OK) { |
| // On failure the caller retains ownership of `driver_transport`. Release |
| // it here so it doesn't get closed when `transport` is destroyed. |
| transport->Release(); |
| |
| // Wipe out the initial portals we created, since they are invalid and |
| // effectively not returned to the caller on failure. |
| for (Ref<Portal>& portal : portals) { |
| Ref<Portal> doomed_portal = AdoptRef(portal.get()); |
| } |
| return result; |
| } |
| return IPCZ_RESULT_OK; |
| } |
| |
| NodeName Node::GetAssignedName() { |
| absl::MutexLock lock(&mutex_); |
| return assigned_name_; |
| } |
| |
| Ref<NodeLink> Node::GetBrokerLink() { |
| absl::MutexLock lock(&mutex_); |
| return broker_link_; |
| } |
| |
| void Node::SetBrokerLink(Ref<NodeLink> link) { |
| absl::MutexLock lock(&mutex_); |
| ABSL_ASSERT(!broker_link_); |
| broker_link_ = std::move(link); |
| } |
| |
| void Node::SetAssignedName(const NodeName& name) { |
| absl::MutexLock lock(&mutex_); |
| ABSL_ASSERT(!assigned_name_.is_valid()); |
| assigned_name_ = name; |
| } |
| |
| bool Node::AddLink(const NodeName& remote_node_name, Ref<NodeLink> link) { |
| absl::MutexLock lock(&mutex_); |
| auto [it, inserted] = node_links_.insert({remote_node_name, std::move(link)}); |
| return inserted; |
| } |
| |
| NodeName Node::GenerateRandomName() const { |
| NodeName name; |
| IpczResult result = |
| driver_.GenerateRandomBytes(sizeof(name), IPCZ_NO_FLAGS, nullptr, &name); |
| ABSL_ASSERT(result == IPCZ_RESULT_OK); |
| return name; |
| } |
| |
| // static |
| void Node::SimulateDisconnectForTesting(IpczHandle first, IpczHandle second) { |
| Node* node0 = Node::FromHandle(first); |
| Node* node1 = Node::FromHandle(second); |
| |
| node0->SimulateDisconnectForTesting(node1->GetAssignedName()); |
| node1->SimulateDisconnectForTesting(node0->GetAssignedName()); |
| } |
| |
| void Node::SimulateDisconnectForTesting(const NodeName& name) { |
| absl::MutexLock lock(&mutex_); |
| auto it = node_links_.find(name); |
| if (it == node_links_.end()) { |
| return; |
| } |
| |
| Ref<NodeLink> link = std::move(it->second); |
| node_links_.erase(it); |
| |
| if (broker_link_ == link) { |
| broker_link_.reset(); |
| } |
| link->SimulateDisconnectForTesting(); |
| } |
| |
| } // namespace ipcz |