| /* |
| * |
| * Copyright (c) 2020-2021 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. |
| */ |
| |
| #define __APPLE_USE_RFC_3542 |
| #include <inet/UDPEndPoint.h> |
| #include <inet/UDPEndPointImplNetworkFramework.h> |
| |
| #include <inet/IPPacketInfo.h> |
| #include <inet/InetFaultInjection.h> |
| #include <inet/arpa-inet-compatibility.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/SafeInt.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <system/SystemFaultInjection.h> |
| |
| #include <cstring> |
| #include <utility> |
| |
| #define INET_PORTSTRLEN 6 |
| |
| namespace chip { |
| namespace Inet { |
| |
| CHIP_ERROR UDPEndPointImplNetworkFramework::BindImpl(IPAddressType addressType, const IPAddress & address, uint16_t port, |
| InterfaceId intfId) |
| { |
| nw_parameters_configure_protocol_block_t configure_tls; |
| nw_parameters_t parameters; |
| |
| if (intfId.IsPresent()) |
| { |
| return CHIP_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| configure_tls = NW_PARAMETERS_DISABLE_PROTOCOL; |
| parameters = nw_parameters_create_secure_udp(configure_tls, NW_PARAMETERS_DEFAULT_CONFIGURATION); |
| VerifyOrReturnError(parameters != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| ReturnErrorOnFailure(ConfigureProtocol(addressType, parameters)); |
| |
| nw_endpoint_t endpoint = nullptr; |
| CHIP_ERROR res = GetEndPoint(endpoint, addressType, address, port); |
| nw_parameters_set_local_endpoint(parameters, endpoint); |
| nw_release(endpoint); |
| ReturnErrorOnFailure(res); |
| |
| mDispatchQueue = dispatch_queue_create("inet_dispatch_global", DISPATCH_QUEUE_CONCURRENT); |
| VerifyOrReturnError(mDispatchQueue != nullptr, CHIP_ERROR_NO_MEMORY); |
| dispatch_retain(mDispatchQueue); |
| |
| mConnectionSemaphore = dispatch_semaphore_create(0); |
| VerifyOrReturnError(mConnectionSemaphore != nullptr, CHIP_ERROR_NO_MEMORY); |
| dispatch_retain(mConnectionSemaphore); |
| |
| mSendSemaphore = dispatch_semaphore_create(0); |
| VerifyOrReturnError(mSendSemaphore != nullptr, CHIP_ERROR_NO_MEMORY); |
| dispatch_retain(mSendSemaphore); |
| |
| 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 |
| { |
| nw_endpoint_t 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) |
| { |
| dispatch_data_t content; |
| |
| // 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. |
| content = dispatch_data_create(msg->Start(), msg->DataLength(), mDispatchQueue, DISPATCH_DATA_DESTRUCTOR_DEFAULT); |
| |
| // If there is a current message pending and the state of the network connection change (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 caller knows that sending a message has failed the following code consider there is an error |
| // _unless_ the completion handler says otherwise. |
| __block CHIP_ERROR res = CHIP_ERROR_UNEXPECTED_EVENT; |
| nw_connection_send(mConnection, content, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true, ^(nw_error_t error) { |
| if (error) |
| { |
| res = CHIP_ERROR_POSIX(nw_error_get_error_code(error)); |
| } |
| else |
| { |
| res = CHIP_NO_ERROR; |
| } |
| dispatch_semaphore_signal(mSendSemaphore); |
| }); |
| dispatch_release(content); |
| |
| dispatch_semaphore_wait(mSendSemaphore, DISPATCH_TIME_FOREVER); |
| |
| return res; |
| } |
| |
| void UDPEndPointImplNetworkFramework::CloseImpl() |
| { |
| ReleaseAll(); |
| } |
| |
| void UDPEndPointImplNetworkFramework::ReleaseAll() |
| { |
| |
| OnMessageReceived = nullptr; |
| OnReceiveError = nullptr; |
| |
| ReleaseConnection(); |
| ReleaseListener(); |
| |
| if (mParameters) |
| { |
| nw_release(mParameters); |
| mParameters = nullptr; |
| } |
| |
| if (mDispatchQueue) |
| { |
| dispatch_suspend(mDispatchQueue); |
| dispatch_release(mDispatchQueue); |
| mDispatchQueue = nullptr; |
| } |
| |
| if (mConnectionSemaphore) |
| { |
| dispatch_release(mConnectionSemaphore); |
| mConnectionSemaphore = nullptr; |
| } |
| |
| if (mListenerQueue) |
| { |
| dispatch_suspend(mListenerQueue); |
| dispatch_release(mListenerQueue); |
| mListenerQueue = nullptr; |
| } |
| |
| if (mListenerSemaphore) |
| { |
| dispatch_release(mListenerSemaphore); |
| mListenerSemaphore = nullptr; |
| } |
| |
| if (mSendSemaphore) |
| { |
| dispatch_release(mSendSemaphore); |
| 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 res = CHIP_NO_ERROR; |
| |
| nw_protocol_stack_t protocolStack = nw_parameters_copy_default_protocol_stack(aParameters); |
| nw_protocol_options_t 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: |
| res = INET_ERROR_WRONG_ADDRESS_TYPE; |
| break; |
| } |
| nw_release(ipOptions); |
| nw_release(protocolStack); |
| |
| return res; |
| } |
| |
| 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(); |
| aPacketInfo.SrcAddress = IPAddress::FromSockAddr(*nw_endpoint_get_address(src_endpoint)); |
| aPacketInfo.DestAddress = IPAddress::FromSockAddr(*nw_endpoint_get_address(dest_endpoint)); |
| aPacketInfo.SrcPort = nw_endpoint_get_port(src_endpoint); |
| aPacketInfo.DestPort = nw_endpoint_get_port(dest_endpoint); |
| } |
| |
| CHIP_ERROR UDPEndPointImplNetworkFramework::GetEndPoint(nw_endpoint_t & aEndPoint, const IPAddressType aAddressType, |
| const IPAddress & aAddress, uint16_t aPort) |
| { |
| char addrStr[INET6_ADDRSTRLEN]; |
| 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 locale endpoint is IPv4. |
| if (aAddressType == IPAddressType::kIPv4 && aAddress.Type() == IPAddressType::kAny) |
| { |
| const IPAddress anyAddr = IPAddress(aAddress.ToIPv4()); |
| anyAddr.ToString(addrStr, sizeof(addrStr)); |
| } |
| else |
| { |
| aAddress.ToString(addrStr, sizeof(addrStr)); |
| } |
| |
| snprintf(portStr, sizeof(portStr), "%u", aPort); |
| |
| aEndPoint = nw_endpoint_create_host(addrStr, portStr); |
| VerifyOrReturnError(aEndPoint != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR UDPEndPointImplNetworkFramework::GetConnection(const IPPacketInfo * aPktInfo) |
| { |
| VerifyOrReturnError(mParameters != nullptr, CHIP_ERROR_INCORRECT_STATE); |
| |
| nw_endpoint_t endpoint = nullptr; |
| nw_connection_t connection = nullptr; |
| |
| if (mConnection) |
| { |
| nw_path_t path = nw_connection_copy_current_path(mConnection); |
| nw_endpoint_t remote_endpoint = nw_path_copy_effective_remote_endpoint(path); |
| const IPAddress remote_address = IPAddress::FromSockAddr(*nw_endpoint_get_address(remote_endpoint)); |
| const uint16_t remote_port = nw_endpoint_get_port(remote_endpoint); |
| const bool isDifferentEndPoint = aPktInfo->DestPort != remote_port || aPktInfo->DestAddress != remote_address; |
| VerifyOrReturnError(isDifferentEndPoint, CHIP_NO_ERROR); |
| |
| ReturnErrorOnFailure(ReleaseConnection()); |
| } |
| |
| ReturnErrorOnFailure(GetEndPoint(endpoint, mAddrType, aPktInfo->DestAddress, aPktInfo->DestPort)); |
| |
| connection = nw_connection_create(endpoint, mParameters); |
| nw_release(endpoint); |
| |
| VerifyOrReturnError(connection != nullptr, CHIP_ERROR_INCORRECT_STATE); |
| |
| return StartConnection(connection); |
| } |
| |
| CHIP_ERROR UDPEndPointImplNetworkFramework::StartListener() |
| { |
| __block CHIP_ERROR res = CHIP_NO_ERROR; |
| nw_listener_t listener; |
| |
| VerifyOrReturnError(mListener == nullptr, CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrReturnError(mListenerSemaphore == nullptr, CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrReturnError(mListenerQueue == nullptr, CHIP_ERROR_INCORRECT_STATE); |
| |
| listener = nw_listener_create(mParameters); |
| VerifyOrReturnError(listener != nullptr, CHIP_ERROR_INCORRECT_STATE); |
| |
| mListenerSemaphore = dispatch_semaphore_create(0); |
| VerifyOrReturnError(mListenerSemaphore != nullptr, CHIP_ERROR_NO_MEMORY); |
| dispatch_retain(mListenerSemaphore); |
| |
| mListenerQueue = dispatch_queue_create("inet_dispatch_listener", DISPATCH_QUEUE_CONCURRENT); |
| VerifyOrReturnError(mListenerQueue != nullptr, CHIP_ERROR_NO_MEMORY); |
| dispatch_retain(mListenerQueue); |
| |
| nw_listener_set_queue(listener, mListenerQueue); |
| |
| nw_listener_set_new_connection_handler(listener, ^(nw_connection_t connection) { |
| ReleaseConnection(); |
| StartConnection(connection); |
| }); |
| |
| nw_listener_set_state_changed_handler(listener, ^(nw_listener_state_t state, nw_error_t error) { |
| switch (state) |
| { |
| |
| case nw_listener_state_invalid: |
| ChipLogDetail(Inet, "Listener: Invalid"); |
| res = CHIP_ERROR_INCORRECT_STATE; |
| nw_listener_cancel(listener); |
| break; |
| |
| case nw_listener_state_waiting: |
| ChipLogDetail(Inet, "Listener: Waiting"); |
| break; |
| |
| case nw_listener_state_failed: |
| ChipLogDetail(Inet, "Listener: Failed"); |
| res = CHIP_ERROR_POSIX(nw_error_get_error_code(error)); |
| break; |
| |
| case nw_listener_state_ready: |
| ChipLogDetail(Inet, "Listener: Ready"); |
| res = CHIP_NO_ERROR; |
| dispatch_semaphore_signal(mListenerSemaphore); |
| break; |
| |
| case nw_listener_state_cancelled: |
| ChipLogDetail(Inet, "Listener: Cancelled"); |
| if (res == CHIP_NO_ERROR) |
| { |
| res = CHIP_ERROR_CONNECTION_ABORTED; |
| } |
| |
| dispatch_semaphore_signal(mListenerSemaphore); |
| break; |
| } |
| }); |
| |
| nw_listener_start(listener); |
| dispatch_semaphore_wait(mListenerSemaphore, DISPATCH_TIME_FOREVER); |
| ReturnErrorOnFailure(res); |
| |
| mListener = listener; |
| nw_retain(mListener); |
| return res; |
| } |
| |
| CHIP_ERROR UDPEndPointImplNetworkFramework::StartConnection(nw_connection_t & aConnection) |
| { |
| __block CHIP_ERROR res = CHIP_NO_ERROR; |
| |
| nw_connection_set_queue(aConnection, mDispatchQueue); |
| |
| nw_connection_set_state_changed_handler(aConnection, ^(nw_connection_state_t state, nw_error_t error) { |
| switch (state) |
| { |
| |
| case nw_connection_state_invalid: |
| ChipLogDetail(Inet, "Connection: Invalid"); |
| res = CHIP_ERROR_INCORRECT_STATE; |
| nw_connection_cancel(aConnection); |
| break; |
| |
| case nw_connection_state_preparing: |
| ChipLogDetail(Inet, "Connection: Preparing"); |
| res = CHIP_ERROR_INCORRECT_STATE; |
| break; |
| |
| case nw_connection_state_waiting: |
| ChipLogDetail(Inet, "Connection: Waiting"); |
| nw_connection_cancel(aConnection); |
| break; |
| |
| case nw_connection_state_failed: |
| ChipLogDetail(Inet, "Connection: Failed"); |
| res = CHIP_ERROR_POSIX(nw_error_get_error_code(error)); |
| break; |
| |
| case nw_connection_state_ready: |
| ChipLogDetail(Inet, "Connection: Ready"); |
| res = CHIP_NO_ERROR; |
| dispatch_semaphore_signal(mConnectionSemaphore); |
| break; |
| |
| case nw_connection_state_cancelled: |
| ChipLogDetail(Inet, "Connection: Cancelled"); |
| if (res == CHIP_NO_ERROR) |
| { |
| res = CHIP_ERROR_CONNECTION_ABORTED; |
| } |
| |
| dispatch_semaphore_signal(mConnectionSemaphore); |
| break; |
| } |
| }); |
| |
| nw_connection_start(aConnection); |
| dispatch_semaphore_wait(mConnectionSemaphore, DISPATCH_TIME_FOREVER); |
| SuccessOrExit(res); |
| |
| mConnection = aConnection; |
| nw_retain(mConnection); |
| HandleDataReceived(mConnection); |
| |
| return res; |
| } |
| |
| 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) |
| { |
| nw_error_domain_t 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(mDispatchQueue, ^{ |
| OnReceiveError((UDPEndPoint *) this, error, &packetInfo); |
| }); |
| } |
| } |
| }; |
| |
| if (content != nullptr && OnMessageReceived != nullptr) |
| { |
| size_t count = dispatch_data_get_size(content); |
| System::PacketBufferHandle * packetBuffer = System::PacketBufferHandle::New(count); |
| 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(mDispatchQueue, ^{ |
| OnMessageReceived((UDPEndPoint *) this, packetBuffer, &packetInfo); |
| }); |
| } |
| |
| schedule_next_receive(); |
| }; |
| |
| nw_connection_receive_message(aConnection, handler); |
| } |
| |
| CHIP_ERROR UDPEndPointImplNetworkFramework::ReleaseListener() |
| { |
| VerifyOrReturnError(mListener, CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrReturnError(mDispatchQueue, CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrReturnError(mConnectionSemaphore, CHIP_ERROR_INCORRECT_STATE); |
| |
| nw_listener_cancel(mListener); |
| dispatch_semaphore_wait(mListenerSemaphore, DISPATCH_TIME_FOREVER); |
| nw_release(mListener); |
| mListener = nullptr; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR UDPEndPointImplNetworkFramework::ReleaseConnection() |
| { |
| VerifyOrReturnError(mConnection, CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrReturnError(mDispatchQueue, CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrReturnError(mConnectionSemaphore, CHIP_ERROR_INCORRECT_STATE); |
| |
| nw_connection_cancel(mConnection); |
| dispatch_semaphore_wait(mConnectionSemaphore, DISPATCH_TIME_FOREVER); |
| nw_release(mConnection); |
| mConnection = nullptr; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| } // namespace Inet |
| } // namespace chip |