blob: 070048538c6f50745d973181681c477241458643 [file] [log] [blame]
/*
*
* Copyright (c) 2020-2024 Project CHIP Authors
* Copyright (c) 2018 Google LLC.
* Copyright (c) 2013-2018 Nest Labs, Inc.
*
* 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
*
* http://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.
*/
/**
* This file implements Inet::UDPEndPoint using Network Framework.
*/
#if !__has_feature(objc_arc)
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
#include <inet/UDPEndPoint.h>
#include <inet/UDPEndPointImplNetworkFramework.h>
#include <lib/support/CHIPMemString.h>
#define INET_PORTSTRLEN 6
#define NETWORK_FRAMEWORK_DEBUG 0
namespace {
#if !NETWORK_FRAMEWORK_DEBUG
void DebugPrintListenerState(nw_listener_state_t state) {};
void DebugPrintConnectionState(nw_connection_state_t state) {};
void DebugPrintConnection(const nw_connection_t aConnection) {};
#else
constexpr const char * kNilConnection = "The connection is nil.";
constexpr const char * kNilPath = "The connection path is nil.";
constexpr const char * kNilPathSourceEndPoint = "The connection path source endpoint is nil.";
constexpr const char * kNilPathDestinationEndPoint = "The connection path destination endpoint is nil.";
constexpr const char * kPathStatusInvalid = "This path is not valid.";
constexpr const char * kPathStatusUnsatisfied = "The path is not available for use.";
constexpr const char * kPathStatusSatisfied = "The path is available to establish connections and send data.";
constexpr const char * kPathStatusSatisfiable = "The path is not currently available, but establishing a new connection may activate the path.";
constexpr const char * kListenerStateInvalid = "Listener: Invalid";
constexpr const char * kListenerStateWaiting = "Listener: Waiting";
constexpr const char * kListenerStateFailed = "Listener: Failed";
constexpr const char * kListenerStateReady = "Listener: Ready";
constexpr const char * kListenerStateCancelled = "Listener: Cancelled";
constexpr const char * kConnectionStateInvalid = "Connection: Invalid";
constexpr const char * kConnectionStateWaiting = "Connection: Waiting";
constexpr const char * kConnectionStatePreparing = "Connection: Preparing";
constexpr const char * kConnectionStateFailed = "Connection: Failed";
constexpr const char * kConnectionStateReady = "Connection: Ready";
constexpr const char * kConnectionStateCancelled = "Connection: Cancelled";
void DebugPrintConnectionState(nw_connection_state_t state)
{
const char * str = nullptr;
switch (state) {
case nw_connection_state_invalid:
str = kConnectionStateInvalid;
break;
case nw_connection_state_preparing:
str = kConnectionStatePreparing;
break;
case nw_connection_state_waiting:
str = kConnectionStateWaiting;
break;
case nw_connection_state_failed:
str = kConnectionStateFailed;
break;
case nw_connection_state_ready:
str = kConnectionStateReady;
break;
case nw_connection_state_cancelled:
str = kConnectionStateCancelled;
break;
default:
chipDie();
}
ChipLogDetail(Inet, "%s", str);
}
void DebugPrintListenerState(nw_listener_state_t state)
{
const char * str = nullptr;
switch (state) {
case nw_listener_state_invalid:
str = kListenerStateInvalid;
break;
case nw_listener_state_waiting:
str = kListenerStateWaiting;
break;
case nw_listener_state_failed:
str = kListenerStateFailed;
break;
case nw_listener_state_ready:
str = kListenerStateReady;
break;
case nw_listener_state_cancelled:
str = kListenerStateCancelled;
break;
default:
chipDie();
}
ChipLogDetail(Inet, "%s", str);
}
void DebugPrintConnectionPathStatus(nw_path_t path)
{
const char * str = nullptr;
__auto_type status = nw_path_get_status(path);
switch (status) {
case nw_path_status_invalid:
str = kPathStatusInvalid;
break;
case nw_path_status_unsatisfied:
str = kPathStatusUnsatisfied;
break;
case nw_path_status_satisfied:
str = kPathStatusSatisfied;
break;
case nw_path_status_satisfiable:
str = kPathStatusSatisfiable;
break;
default:
chipDie();
}
ChipLogError(Inet, "%s", str);
}
void DebugPrintConnection(const nw_connection_t aConnection)
{
VerifyOrReturn(nil != aConnection, ChipLogError(Inet, "%s", kNilConnection));
__auto_type path = nw_connection_copy_current_path(aConnection);
VerifyOrReturn(nil != path, ChipLogError(Inet, "%s", kNilPath));
DebugPrintConnectionPathStatus(path);
__auto_type srcEndPoint = nw_path_copy_effective_local_endpoint(path);
VerifyOrReturn(nil != srcEndPoint, ChipLogError(Inet, "%s", kNilPathSourceEndPoint));
__auto_type dstEndPoint = nw_path_copy_effective_remote_endpoint(path);
VerifyOrReturn(nil != dstEndPoint, ChipLogError(Inet, "%s", kNilPathDestinationEndPoint));
const __auto_type * srcAddress = nw_endpoint_copy_address_string(srcEndPoint);
const __auto_type srcPort = nw_endpoint_get_port(srcEndPoint);
const __auto_type * dstAddress = nw_endpoint_copy_address_string(dstEndPoint);
const __auto_type dstPort = nw_endpoint_get_port(dstEndPoint);
ChipLogError(Inet, "Connection source: %s:%u destination: %s:%u", srcAddress, srcPort, dstAddress, dstPort);
}
#endif
}
namespace chip {
namespace Inet {
CHIP_ERROR UDPEndPointImplNetworkFramework::BindImpl(IPAddressType addressType, const IPAddress & address, uint16_t port,
InterfaceId intfId)
{
#if NETWORK_FRAMEWORK_DEBUG
ChipLogError(Inet, "%s (%p)", __func__, this);
#endif
VerifyOrReturnError(!intfId.IsPresent(), CHIP_ERROR_NOT_IMPLEMENTED);
__auto_type configure_tls = NW_PARAMETERS_DISABLE_PROTOCOL;
__auto_type parameters = nw_parameters_create_secure_udp(configure_tls, NW_PARAMETERS_DEFAULT_CONFIGURATION);
VerifyOrReturnError(nullptr != parameters, CHIP_ERROR_INVALID_ARGUMENT);
// Note: The ConfigureProtocol function uses nw_ip_options_set_version to set the IP version for this endpoint.
//
// This works as expected when the IPAddress is specified. However, when using a wildcard address (chip::Inet::IPAddressType::kAny)
// for an IPv6 socket, the specified IP version (nw_ip_version_6) may be ignored, allowing both IPv4 and IPv6 connections.
ReturnErrorOnFailure(ConfigureProtocol(addressType, parameters));
// Note: Network.framework does not provide an API to set the SO_REUSEPORT socket option.
// This limitation is not an issue when the port is set to 0, as the platform will choose a random port.
//
// However, when both INET_CONFIG_ENABLE_IPV4 and CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY are enabled,
// the system attempts to create two endpoints—one over IPv6 and another over IPv4—both using the same port
// specified by CHIP_UDC_PORT.
//
// This results in a binding failure due to "Address already in use" since both IPv4 and IPv6 endpoints
// try to use the same port.
//
// A potential solution would be to define separate ports for IPv4 and IPv6 (e.g., CHIP_UDC_PORT_IPv4 and CHIP_UDC_PORT_IPv6).
// For now, as a workaround, we set the port to 0 for IPv4 when a specific port is needed, allowing the platform to
// auto-assign an available port and avoid the conflict.
if (IPAddressType::kIPv4 == addressType && port != 0) {
port = 0;
}
__auto_type endpoint = GetEndPoint(addressType, address, port);
VerifyOrReturnError(nullptr != endpoint, CHIP_ERROR_INTERNAL);
nw_parameters_set_local_endpoint(parameters, endpoint);
mConnectionQueue = dispatch_queue_create("inet_dispatch_global", DISPATCH_QUEUE_SERIAL);
VerifyOrReturnError(nullptr != mConnectionQueue, CHIP_ERROR_NO_MEMORY);
mConnectionSemaphore = dispatch_semaphore_create(0);
VerifyOrReturnError(nullptr != mConnectionSemaphore, CHIP_ERROR_NO_MEMORY);
mSendSemaphore = dispatch_semaphore_create(0);
VerifyOrReturnError(nullptr != mSendSemaphore, CHIP_ERROR_NO_MEMORY);
mSystemQueue = static_cast<System::LayerSocketsLoop &>(GetSystemLayer()).GetDispatchQueue();
mAddrType = addressType;
mConnection = nullptr;
mParameters = parameters;
return CHIP_NO_ERROR;
}
CHIP_ERROR UDPEndPointImplNetworkFramework::BindInterfaceImpl(IPAddressType addrType, InterfaceId intfId)
{
return INET_ERROR_UNKNOWN_INTERFACE;
}
InterfaceId UDPEndPointImplNetworkFramework::GetBoundInterface() const
{
return InterfaceId::Null();
}
uint16_t UDPEndPointImplNetworkFramework::GetBoundPort() const
{
__auto_type endpoint = nw_parameters_copy_local_endpoint(mParameters);
return nw_endpoint_get_port(endpoint);
}
CHIP_ERROR UDPEndPointImplNetworkFramework::ListenImpl()
{
return StartListener();
}
CHIP_ERROR UDPEndPointImplNetworkFramework::SendMsgImpl(const IPPacketInfo * pktInfo, System::PacketBufferHandle && msg)
{
#if NETWORK_FRAMEWORK_DEBUG
ChipLogError(Inet, "%s (%p)", __func__, this);
#endif
// Ensure we have an actual message to send.
VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
// Ensure the destination address type is compatible with the endpoint address type.
VerifyOrReturnError(mAddrType == pktInfo->DestAddress.Type(), CHIP_ERROR_INVALID_ARGUMENT);
// For now the entire message must fit within a single buffer.
VerifyOrReturnError(msg->Next() == nullptr, CHIP_ERROR_MESSAGE_TOO_LONG);
ReturnErrorOnFailure(GetConnection(pktInfo));
// Send a message, and wait for it to be dispatched.
__auto_type content = dispatch_data_create(msg->Start(), msg->DataLength(), mSystemQueue, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
// If there is a current message pending and the state of the network connection changes (e.g switch to a
// different network) the connection will enter a nw_connection_state_failed state and the completion handler
// will never be called. In such cases a signal is sent from the connection state change handler to release
// the semaphore. In this case the CHIP_ERROR will not update with the result of the completion handler.
// To make sure our caller knows that sending a message has failed the following code assumes there is an error
// _unless_ the completion handler says otherwise.
__block CHIP_ERROR err = CHIP_ERROR_UNEXPECTED_EVENT;
nw_connection_send(mConnection, content, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true, ^(nw_error_t error) {
if (error) {
err = CHIP_ERROR_POSIX(nw_error_get_error_code(error));
} else {
err = CHIP_NO_ERROR;
}
dispatch_semaphore_signal(mSendSemaphore);
});
dispatch_semaphore_wait(mSendSemaphore, DISPATCH_TIME_FOREVER);
return err;
}
void UDPEndPointImplNetworkFramework::CloseImpl()
{
ReleaseAll();
}
void UDPEndPointImplNetworkFramework::ReleaseAll()
{
OnMessageReceived = nullptr;
OnReceiveError = nullptr;
ReleaseConnection();
ReleaseListener();
mParameters = nullptr;
mConnectionQueue = nullptr;
mConnectionSemaphore = nullptr;
mListenerQueue = nullptr;
mListenerSemaphore = nullptr;
mSendSemaphore = nullptr;
}
void UDPEndPointImplNetworkFramework::Free()
{
Close();
Release();
}
CHIP_ERROR UDPEndPointImplNetworkFramework::SetMulticastLoopback(IPVersion aIPVersion, bool aLoopback)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
#if INET_CONFIG_ENABLE_IPV4
CHIP_ERROR UDPEndPointImplNetworkFramework::IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress,
bool join)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
#endif // INET_CONFIG_ENABLE_IPV4
CHIP_ERROR UDPEndPointImplNetworkFramework::IPv6JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress,
bool join)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
CHIP_ERROR UDPEndPointImplNetworkFramework::ConfigureProtocol(IPAddressType aAddressType, const nw_parameters_t & aParameters)
{
CHIP_ERROR err = CHIP_NO_ERROR;
__auto_type protocolStack = nw_parameters_copy_default_protocol_stack(aParameters);
__auto_type ipOptions = nw_protocol_stack_copy_internet_protocol(protocolStack);
switch (aAddressType) {
case IPAddressType::kIPv6:
nw_ip_options_set_version(ipOptions, nw_ip_version_6);
break;
#if INET_CONFIG_ENABLE_IPV4
case IPAddressType::kIPv4:
nw_ip_options_set_version(ipOptions, nw_ip_version_4);
break;
#endif // INET_CONFIG_ENABLE_IPV4
default:
err = INET_ERROR_WRONG_ADDRESS_TYPE;
break;
}
return err;
}
void UDPEndPointImplNetworkFramework::GetPacketInfo(const nw_connection_t & aConnection, IPPacketInfo & aPacketInfo)
{
nw_path_t path = nw_connection_copy_current_path(aConnection);
nw_endpoint_t dest_endpoint = nw_path_copy_effective_local_endpoint(path);
nw_endpoint_t src_endpoint = nw_path_copy_effective_remote_endpoint(path);
aPacketInfo.Clear();
// TODO Handle return value of IPAddress::GetIPAddressFromSockAdd
const auto * srcAddress = nw_endpoint_get_address(src_endpoint);
IPAddress::GetIPAddressFromSockAddr(*srcAddress, aPacketInfo.SrcAddress);
// TODO Handle return value of IPAddress::GetIPAddressFromSockAdd
const auto * dstAddress = nw_endpoint_get_address(dest_endpoint);
IPAddress::GetIPAddressFromSockAddr(*dstAddress, aPacketInfo.DestAddress);
aPacketInfo.SrcPort = nw_endpoint_get_port(src_endpoint);
aPacketInfo.DestPort = nw_endpoint_get_port(dest_endpoint);
#if NETWORK_FRAMEWORK_DEBUG
char srcAddrStr[IPAddress::kMaxStringLength + 1 /*null terminator */];
char dstAddrStr[IPAddress::kMaxStringLength + 1 /*null terminator */];
aPacketInfo.SrcAddress.ToString(srcAddrStr);
aPacketInfo.DestAddress.ToString(dstAddrStr);
ChipLogError(Inet, "Packet received from %s to %s", srcAddrStr, dstAddrStr);
#endif
}
nw_endpoint_t UDPEndPointImplNetworkFramework::GetEndPoint(const IPAddressType aAddressType,
const IPAddress & aAddress, uint16_t aPort, InterfaceId interfaceIndex)
{
char addrStr[IPAddress::kMaxStringLength + 1 /*%*/ + InterfaceId::kMaxIfNameLength + 1 /*null terminator */];
char portStr[INET_PORTSTRLEN];
// Note: aAddress.ToString will return the IPv6 Any address if the address type is Any, but that's not what
// we want if the local endpoint is IPv4.
#if INET_CONFIG_ENABLE_IPV4
if (aAddressType == IPAddressType::kIPv4 && aAddress.Type() == IPAddressType::kAny) {
const IPAddress anyAddr = IPAddress(aAddress.ToIPv4());
anyAddr.ToString(addrStr);
} else
#endif // INET_CONFIG_ENABLE_IPV4
{
aAddress.ToString(addrStr);
if (interfaceIndex != InterfaceId::Null()) {
char interface[InterfaceId::kMaxIfNameLength + 1] = {}; // +1 to prepend '%'
interface[0] = '%';
interface[1] = 0;
CHIP_ERROR err = interfaceIndex.GetInterfaceName(interface + 1, sizeof(interface) - 1);
if (err != CHIP_NO_ERROR) {
Platform::CopyString(interface, sizeof(interface), "%(err)");
}
strncat(addrStr, interface, sizeof(addrStr) - strlen(addrStr) - 1);
}
}
snprintf(portStr, sizeof(portStr), "%u", aPort);
char * target = addrStr;
#if NETWORK_FRAMEWORK_DEBUG
ChipLogError(Inet, "Create endpoint for ip(%s) port(%s)", target, portStr);
#endif
return nw_endpoint_create_host(target, portStr);
}
CHIP_ERROR UDPEndPointImplNetworkFramework::GetConnection(const IPPacketInfo * aPktInfo)
{
VerifyOrReturnError(nullptr != mParameters, CHIP_ERROR_INCORRECT_STATE);
if (mConnection) {
__auto_type path = nw_connection_copy_current_path(mConnection);
__auto_type remote_endpoint = nw_path_copy_effective_remote_endpoint(path);
// TODO Handle return value of IPAddress::GetIPAddressFromSockAdd
IPAddress remote_address;
IPAddress::GetIPAddressFromSockAddr(*nw_endpoint_get_address(remote_endpoint), remote_address);
const uint16_t remote_port = nw_endpoint_get_port(remote_endpoint);
const bool isDifferentEndPoint = aPktInfo->DestPort != remote_port || aPktInfo->DestAddress != remote_address;
// Return without doing anything if we are not changing our endpoint.
VerifyOrReturnError(isDifferentEndPoint, CHIP_NO_ERROR);
ReturnErrorOnFailure(ReleaseConnection());
}
__auto_type endpoint = GetEndPoint(mAddrType, aPktInfo->DestAddress, aPktInfo->DestPort, aPktInfo->Interface);
VerifyOrReturnError(nullptr != endpoint, CHIP_ERROR_INCORRECT_STATE);
__auto_type connection = nw_connection_create(endpoint, mParameters);
VerifyOrReturnError(nullptr != connection, CHIP_ERROR_INCORRECT_STATE);
return StartConnection(connection);
}
CHIP_ERROR UDPEndPointImplNetworkFramework::StartListener()
{
VerifyOrReturnError(nullptr == mListener, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(nullptr == mListenerSemaphore, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(nullptr == mListenerQueue, CHIP_ERROR_INCORRECT_STATE);
__auto_type listener = nw_listener_create(mParameters);
VerifyOrReturnError(nullptr != listener, CHIP_ERROR_INCORRECT_STATE);
mListenerSemaphore = dispatch_semaphore_create(0);
VerifyOrReturnError(nullptr != mListenerSemaphore, CHIP_ERROR_NO_MEMORY);
mListenerQueue = dispatch_queue_create("inet_dispatch_listener", DISPATCH_QUEUE_CONCURRENT);
VerifyOrReturnError(nullptr != mListenerQueue, CHIP_ERROR_NO_MEMORY);
nw_listener_set_queue(listener, mListenerQueue);
nw_listener_set_new_connection_handler(listener, ^(nw_connection_t connection) {
ReleaseConnection();
StartConnection(connection);
});
__block CHIP_ERROR err = CHIP_NO_ERROR;
nw_listener_set_state_changed_handler(listener, ^(nw_listener_state_t state, nw_error_t error) {
DebugPrintListenerState(state);
switch (state) {
case nw_listener_state_invalid:
err = CHIP_ERROR_INCORRECT_STATE;
nw_listener_cancel(listener);
break;
case nw_listener_state_waiting:
// Nothing to do.
break;
case nw_listener_state_failed:
err = CHIP_ERROR_POSIX(nw_error_get_error_code(error));
ChipLogError(Inet, "Error: %s", chip::ErrorStr(err));
break;
case nw_listener_state_ready:
err = CHIP_NO_ERROR;
dispatch_semaphore_signal(mListenerSemaphore);
break;
case nw_listener_state_cancelled:
if (err == CHIP_NO_ERROR) {
err = CHIP_ERROR_CONNECTION_ABORTED;
}
dispatch_semaphore_signal(mListenerSemaphore);
break;
}
});
nw_listener_start(listener);
dispatch_semaphore_wait(mListenerSemaphore, DISPATCH_TIME_FOREVER);
if (CHIP_NO_ERROR == err) {
mListener = listener;
}
return err;
}
CHIP_ERROR UDPEndPointImplNetworkFramework::StartConnection(nw_connection_t aConnection)
{
__block CHIP_ERROR err = CHIP_NO_ERROR;
nw_connection_set_queue(aConnection, mConnectionQueue);
nw_connection_set_state_changed_handler(aConnection, ^(nw_connection_state_t state, nw_error_t error) {
DebugPrintConnectionState(state);
switch (state) {
case nw_connection_state_invalid:
err = CHIP_ERROR_INCORRECT_STATE;
nw_connection_cancel(aConnection);
break;
case nw_connection_state_preparing:
err = CHIP_ERROR_INCORRECT_STATE;
break;
case nw_connection_state_waiting:
nw_connection_cancel(aConnection);
break;
case nw_connection_state_failed:
err = CHIP_ERROR_POSIX(nw_error_get_error_code(error));
break;
case nw_connection_state_ready:
err = CHIP_NO_ERROR;
dispatch_semaphore_signal(mConnectionSemaphore);
break;
case nw_connection_state_cancelled:
if (err == CHIP_NO_ERROR) {
err = CHIP_ERROR_CONNECTION_ABORTED;
}
dispatch_semaphore_signal(mConnectionSemaphore);
break;
}
});
nw_connection_start(aConnection);
dispatch_semaphore_wait(mConnectionSemaphore, DISPATCH_TIME_FOREVER);
if (CHIP_NO_ERROR == err) {
DebugPrintConnection(aConnection);
mConnection = aConnection;
HandleDataReceived(mConnection);
}
return err;
}
void UDPEndPointImplNetworkFramework::HandleDataReceived(const nw_connection_t & aConnection)
{
nw_connection_receive_completion_t handler = ^(dispatch_data_t content, nw_content_context_t context, bool is_complete, nw_error_t receive_error) {
dispatch_block_t schedule_next_receive = ^{
if (receive_error == nullptr) {
HandleDataReceived(aConnection);
} else if (OnReceiveError != nullptr) {
__auto_type error_domain = nw_error_get_error_domain(receive_error);
errno = nw_error_get_error_code(receive_error);
if (!(error_domain == nw_error_domain_posix && errno == ECANCELED)) {
CHIP_ERROR error = CHIP_ERROR_POSIX(errno);
IPPacketInfo packetInfo;
GetPacketInfo(aConnection, packetInfo);
dispatch_async(mSystemQueue, ^{
OnReceiveError((UDPEndPoint *) this, error, &packetInfo);
});
}
}
};
if (content != nullptr && OnMessageReceived != nullptr) {
size_t count = dispatch_data_get_size(content);
auto packetBufferHandle = System::PacketBufferHandle::New(count);
auto * packetBuffer = std::move(packetBufferHandle).UnsafeRelease();
dispatch_data_apply(content, ^(dispatch_data_t data, size_t offset, const void * buffer, size_t size) {
memmove(packetBuffer->Start() + offset, buffer, size);
return true;
});
packetBuffer->SetDataLength(count);
IPPacketInfo packetInfo;
GetPacketInfo(aConnection, packetInfo);
dispatch_async(mSystemQueue, ^{
auto handle = System::PacketBufferHandle::Adopt(packetBuffer);
OnMessageReceived((UDPEndPoint *) this, std::move(handle), &packetInfo);
});
}
schedule_next_receive();
};
nw_connection_receive_message(aConnection, handler);
}
CHIP_ERROR UDPEndPointImplNetworkFramework::ReleaseListener()
{
VerifyOrReturnError(nullptr != mListener, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(nullptr != mConnectionQueue, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(nullptr != mConnectionSemaphore, CHIP_ERROR_INCORRECT_STATE);
nw_listener_cancel(mListener);
dispatch_semaphore_wait(mListenerSemaphore, DISPATCH_TIME_FOREVER);
mListener = nullptr;
return CHIP_NO_ERROR;
}
CHIP_ERROR UDPEndPointImplNetworkFramework::ReleaseConnection()
{
VerifyOrReturnError(nullptr != mConnection, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(nullptr != mConnectionQueue, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(nullptr != mConnectionSemaphore, CHIP_ERROR_INCORRECT_STATE);
nw_connection_cancel(mConnection);
dispatch_semaphore_wait(mConnectionSemaphore, DISPATCH_TIME_FOREVER);
mConnection = nullptr;
return CHIP_NO_ERROR;
}
} // namespace Inet
} // namespace chip