Example
Dependencies: FXAS21002 FXOS8700Q
simple-mbed-cloud-client/mbed-cloud-client/mbed-client/mbed-client-classic/source/m2mconnectionhandlerpimpl.cpp
- Committer:
- maygup01
- Date:
- 2019-11-19
- Revision:
- 0:11cc2b7889af
File content as of revision 0:11cc2b7889af:
/* * Copyright (c) 2015 - 2017 ARM Limited. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * 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. */ // fixup the compilation on ARMCC for PRIu32 #define __STDC_FORMAT_MACROS #include <inttypes.h> #include "mbed-client-classic/m2mconnectionhandlerpimpl.h" #include "mbed-client/m2mconnectionobserver.h" #include "mbed-client/m2mconstants.h" #include "mbed-client/m2msecurity.h" #include "mbed-client/m2mconnectionhandler.h" #include "pal.h" #include "eventOS_scheduler.h" #include "eventOS_event_timer.h" #include "mbed-trace/mbed_trace.h" #include <stdlib.h> // free() and malloc() #define TRACE_GROUP "mClt" #ifndef MBED_CONF_MBED_CLIENT_TLS_MAX_RETRY #define MBED_CONF_MBED_CLIENT_TLS_MAX_RETRY 60 #endif #if (PAL_DNS_API_VERSION == 1) && defined(TARGET_LIKE_MBED) #error "For async PAL DNS only API v2 or greater is supported on Mbed." #endif int8_t M2MConnectionHandlerPimpl::_tasklet_id = -1; // This is called from event loop, but as it is static C function, this is just a wrapper // which calls C++ on the instance. extern "C" void eventloop_event_handler(arm_event_s *event) { tr_debug("M2MConnectionHandlerPimpl::eventloop_event_handler %d", event->event_type); if (event->event_type != M2MConnectionHandlerPimpl::ESocketIdle) { if(!event->data_ptr) { tr_error("M2MConnectionHandlerPimpl::eventloop_event_handler event->data_ptr=NULL !!!!"); assert(event->data_ptr); } M2MConnectionHandlerPimpl* instance = (M2MConnectionHandlerPimpl*)event->data_ptr; instance->event_handler(event); } } // event handler that forwards the event according to its type and/or connection state void M2MConnectionHandlerPimpl::event_handler(arm_event_s *event) { switch (event->event_type) { // Event from socket callback method case M2MConnectionHandlerPimpl::ESocketCallback: case M2MConnectionHandlerPimpl::ESocketTimerCallback: // this will enable sending more events during this event processing, but that is less evil than missing one _suppressable_event_in_flight = false; if (_socket_state == M2MConnectionHandlerPimpl::ESocketStateHandshaking) { receive_handshake_handler(); } else if ((_socket_state == M2MConnectionHandlerPimpl::ESocketStateUnsecureConnection) || (_socket_state == M2MConnectionHandlerPimpl::ESocketStateSecureConnection)) { // the connection is established receive_handler(); } else { socket_connect_handler(); } // Receive processing could have changed state, so recheck if ((_socket_state == M2MConnectionHandlerPimpl::ESocketStateUnsecureConnection) || (_socket_state == M2MConnectionHandlerPimpl::ESocketStateSecureConnection)) { // the connection is established send_socket_data(); } break; // Data send request from client side case M2MConnectionHandlerPimpl::ESocketSend: send_socket_data(); break; // DNS resolved successfully case M2MConnectionHandlerPimpl::ESocketDnsResolved: handle_dns_result(true); break; // DNS resolving failed case M2MConnectionHandlerPimpl::ESocketDnsError: handle_dns_result(false); break; // Establish the connection by connecting the socket case M2MConnectionHandlerPimpl::ESocketConnect: socket_connect_handler(); break; case M2MConnectionHandlerPimpl::ESocketClose: close_socket(); break; default: tr_debug("M2MConnectionHandlerPimpl::connection_event_handler: default type: %d", (int)event->event_type); break; } } // This callback is used from PAL sockets, it is called with object instance as argument. // This is received from "some" socket event from "some" socket and the C++ side is responsible // of forwarding it or ignoring the event completely. extern "C" void socket_event_handler(void* arg) { M2MConnectionHandlerPimpl* instance = (M2MConnectionHandlerPimpl*)arg; if (!instance) { tr_error("Invalid callback argument"); return; } instance->send_socket_event(M2MConnectionHandlerPimpl::ESocketCallback); } void M2MConnectionHandlerPimpl::send_socket_event(SocketEvent event_type) { // the socket callback events can safely be suppressed, the receiving end must tolerate that if (event_type == ESocketCallback) { // only the socket connected state supports retries somehow if (_suppressable_event_in_flight == false) { _suppressable_event_in_flight = true; } else { // XXX: DO NOT ADD FOLLOWING LINE TO OFFICIAL GIT, THIS WILL KILL SOME NETWORK STACKS // IF EVENT IS SENT FROM A INTERRUPT CONTEXT // tr_debug("** SKIPPING event"); return; } } if (!send_event(event_type)) { // TODO: give a proper error based on state instead of this _observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true); } } M2MConnectionHandlerPimpl::M2MConnectionHandlerPimpl(M2MConnectionHandler* base, M2MConnectionObserver &observer, M2MConnectionSecurity* sec, M2MInterface::BindingMode mode, M2MInterface::NetworkStack stack) :_base(base), _observer(observer), _security_impl(sec), _security(NULL), _binding_mode(mode), _socket(0), _server_type(M2MConnectionObserver::LWM2MServer), _server_port(0), _listen_port(0), _net_iface(0), #if (PAL_DNS_API_VERSION == 0) || (PAL_DNS_API_VERSION == 1) _socket_address_len(0), #elif (PAL_DNS_API_VERSION == 2) _handler_async_DNS(0), #endif _socket_state(ESocketStateDisconnected), _handshake_retry(0), _suppressable_event_in_flight(false), _secure_connection(false) { #ifndef PAL_NET_TCP_AND_TLS_SUPPORT if (is_tcp_connection()) { tr_error("ConnectionHandler: TCP support not available."); return; } #endif if (PAL_SUCCESS != pal_init()) { tr_error("PAL init failed."); } memset(&_address, 0, sizeof _address); memset((void*)&_socket_address, 0, sizeof _socket_address); memset(&_ipV4Addr, 0, sizeof(palIpV4Addr_t)); memset(&_ipV6Addr, 0, sizeof(palIpV6Addr_t)); ns_list_init(&_linked_list_send_data); eventOS_scheduler_mutex_wait(); if (M2MConnectionHandlerPimpl::_tasklet_id == -1) { M2MConnectionHandlerPimpl::_tasklet_id = eventOS_event_handler_create(&eventloop_event_handler, ESocketIdle); } eventOS_scheduler_mutex_release(); } M2MConnectionHandlerPimpl::~M2MConnectionHandlerPimpl() { tr_debug("~M2MConnectionHandlerPimpl()"); #if (PAL_DNS_API_VERSION == 2) if ( _handler_async_DNS > 0) { pal_cancelAddressInfoAsync(_handler_async_DNS); } #endif close_socket(); delete _security_impl; _security_impl = NULL; pal_destroy(); tr_debug("~M2MConnectionHandlerPimpl() - OUT"); } bool M2MConnectionHandlerPimpl::bind_connection(const uint16_t listen_port) { _listen_port = listen_port; return true; } bool M2MConnectionHandlerPimpl::send_event(SocketEvent event_type) { arm_event_s event = {0}; event.receiver = M2MConnectionHandlerPimpl::_tasklet_id; event.sender = 0; event.event_type = event_type; event.data_ptr = this; event.event_data = 0; event.priority = ARM_LIB_HIGH_PRIORITY_EVENT; return !eventOS_event_send(&event); } // This callback is used from PAL pal_getAddressInfoAsync, #if (PAL_DNS_API_VERSION == 2) extern "C" void address_resolver_cb(const char* url, palSocketAddress_t* address, palStatus_t status, void* callbackArgument) { tr_debug("M2MConnectionHandlerPimpl::address_resolver callback"); M2MConnectionHandlerPimpl* instance = (M2MConnectionHandlerPimpl*)callbackArgument; if (PAL_SUCCESS != status) { tr_error("M2MConnectionHandlerPimpl::address_resolver callback failed with %" PRIx32, status); if (!(instance->send_event(M2MConnectionHandlerPimpl::ESocketDnsError))) { tr_error("M2MConnectionHandlerPimpl::address_resolver callback, error event alloc fail."); } } else { if (!(instance->send_event(M2MConnectionHandlerPimpl::ESocketDnsResolved))) { tr_error("M2MConnectionHandlerPimpl::address_resolver callback, resolved event alloc fail."); } } } #endif bool M2MConnectionHandlerPimpl::address_resolver(void) { palStatus_t status; bool ret = false; #if (PAL_DNS_API_VERSION == 2) tr_debug("M2MConnectionHandlerPimpl::address_resolver:asynchronous DNS"); _handler_async_DNS = 0; status = pal_getAddressInfoAsync(_server_address.c_str(), (palSocketAddress_t*)&_socket_address, &address_resolver_cb, this, &_handler_async_DNS); if (PAL_SUCCESS != status) { tr_error("M2MConnectionHandlerPimpl::address_resolver, pal_getAddressInfoAsync fail. %" PRIx32, status); _observer.socket_error(M2MConnectionHandler::DNS_RESOLVING_ERROR); } else { ret = true; } #else // #if (PAL_DNS_API_VERSION == 0) tr_debug("M2MConnectionHandlerPimpl::address_resolver:synchronous DNS"); status = pal_getAddressInfo(_server_address.c_str(), (palSocketAddress_t*)&_socket_address, &_socket_address_len); if (PAL_SUCCESS != status) { tr_error("M2MConnectionHandlerPimpl::getAddressInfo failed with %" PRIx32, status); if (!send_event(ESocketDnsError)) { tr_error("M2MConnectionHandlerPimpl::address_resolver, error event alloc fail."); } } else { if (!send_event(ESocketDnsResolved)) { tr_error("M2MConnectionHandlerPimpl::address_resolver, resolved event alloc fail."); } else { ret = true; } } #endif return ret; } void M2MConnectionHandlerPimpl::handle_dns_result(bool success) { #if (PAL_DNS_API_VERSION == 2) _handler_async_DNS = 0; #endif if (_socket_state != ESocketStateDNSResolving) { tr_warn("M2MConnectionHandlerPimpl::handle_dns_result() called, not in ESocketStateDNSResolving state!"); return; } if (success) { _socket_state = EsocketStateInitializeConnection; socket_connect_handler(); } else { _observer.socket_error(M2MConnectionHandler::DNS_RESOLVING_ERROR); } } bool M2MConnectionHandlerPimpl::resolve_server_address(const String& server_address, const uint16_t server_port, M2MConnectionObserver::ServerType server_type, const M2MSecurity* security) { #if (PAL_DNS_API_VERSION == 2) if ( _handler_async_DNS > 0) { if (pal_cancelAddressInfoAsync(_handler_async_DNS) != PAL_SUCCESS) { return false; } } #endif _socket_state = ESocketStateDNSResolving; _security = security; int32_t security_instance_id = _security->get_security_instance_id(M2MSecurity::M2MServer); if (server_type == M2MConnectionObserver::Bootstrap) { security_instance_id = _security->get_security_instance_id(M2MSecurity::Bootstrap); } if (_security && security_instance_id >= 0 && (_security->resource_value_int(M2MSecurity::SecurityMode, security_instance_id) == M2MSecurity::Certificate || _security->resource_value_int(M2MSecurity::SecurityMode, security_instance_id) == M2MSecurity::Psk)) { _secure_connection = true; } _server_port = server_port; _server_type = server_type; _server_address = server_address; return address_resolver(); } void M2MConnectionHandlerPimpl::socket_connect_handler() { palStatus_t status; int32_t security_instance_id = _security->get_security_instance_id(M2MSecurity::M2MServer); if (_server_type == M2MConnectionObserver::Bootstrap) { security_instance_id = _security->get_security_instance_id(M2MSecurity::Bootstrap); } tr_debug("M2MConnectionHandlerPimpl::socket_connect_handler - _socket_state = %d", _socket_state); switch (_socket_state) { case ESocketStateCloseBeingCalled: case ESocketStateDNSResolving: case ESocketStateDisconnected: case ESocketStateHandshaking: case ESocketStateUnsecureConnection: case ESocketStateSecureConnection: // Ignore these events break; case EsocketStateInitializeConnection: // Initialize the socket to stable state close_socket(); status = pal_setSockAddrPort((palSocketAddress_t*)&_socket_address, _server_port); if (PAL_SUCCESS != status) { tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - setSockAddrPort err: %" PRIx32, status); } else { tr_debug("address family: %d", (int)_socket_address.addressType); } if (_socket_address.addressType == PAL_AF_INET) { status = pal_getSockAddrIPV4Addr((palSocketAddress_t*)&_socket_address,_ipV4Addr); if (PAL_SUCCESS != status) { tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - sockAddr4, err: %" PRIx32, status); _observer.socket_error(M2MConnectionHandler::DNS_RESOLVING_ERROR); return; } tr_info("M2MConnectionHandlerPimpl::socket_connect_handler - IPv4 Address %d.%d.%d.%d", _ipV4Addr[0], _ipV4Addr[1], _ipV4Addr[2], _ipV4Addr[3]); _address._address = (void*)_ipV4Addr; _address._length = PAL_IPV4_ADDRESS_SIZE; _address._port = _server_port; } else if (_socket_address.addressType == PAL_AF_INET6) { status = pal_getSockAddrIPV6Addr((palSocketAddress_t*)&_socket_address,_ipV6Addr); if (PAL_SUCCESS != status) { tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - sockAddr6, err: %" PRIx32, status); _observer.socket_error(M2MConnectionHandler::DNS_RESOLVING_ERROR); return; } tr_info("M2MConnectionHandlerPimpl::socket_connect_handler - IPv6 Address: %s", mbed_trace_ipv6(_ipV6Addr)); _address._address = (void*)_ipV6Addr; _address._length = PAL_IPV6_ADDRESS_SIZE; _address._port = _server_port; } else { tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - socket config error, stack: %d", (int)_socket_address.addressType); _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT); return; } if (!init_socket()) { tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - socket init error"); // The init_socket() calls the socket_error() -callback directly, so it must not be // done here too. return; } // This state was used to ignore the spurious events _during_ the call of non-blocking pal_connect(). // Now that we just retry connect when it is not yet succeeded anyway this state might be removed completely. _socket_state = ESocketStateConnectBeingCalled; // fall through is intentional case ESocketStateConnectBeingCalled: case ESocketStateConnecting: if (is_tcp_connection()) { #ifdef PAL_NET_TCP_AND_TLS_SUPPORT tr_info("M2MConnectionHandlerPimpl::socket_connect_handler - Using TCP"); status = pal_connect(_socket, (palSocketAddress_t*)&_socket_address, sizeof(_socket_address)); if ((status == PAL_ERR_SOCKET_IN_PROGRES) || (status == PAL_ERR_SOCKET_WOULD_BLOCK)) { // In this case the connect is done asynchronously, and the pal_socketMiniSelect() // will be used to detect the end of connect. tr_debug("M2MConnectionHandlerPimpl::socket_connect_handler - pal_connect(): %" PRIx32 ", async connect started", status); // we need to wait for the event _socket_state = ESocketStateConnecting; break; } else if (status == PAL_SUCCESS || status == PAL_ERR_SOCKET_ALREADY_CONNECTED) { tr_debug("M2MConnectionHandlerPimpl::socket_connect_handler - pal_connect(): success"); _socket_state = ESocketStateConnected; } else { tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - pal_connect(): failed: %" PRIx32, status); close_socket(); _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT); return; } #else tr_error("socket_connect_handler() - TCP not configured" #endif //PAL_NET_TCP_AND_TLS_SUPPORT } else { tr_info("M2MConnectionHandlerPimpl::socket_connect_handler - Using UDP"); _socket_state = ESocketStateConnected; } // fall through is a normal flow in case the UDP was used or pal_connect() happened to return immediately with PAL_SUCCESS case ESocketStateConnected: if (_security && security_instance_id >= 0) { if (_secure_connection) { if ( _security_impl != NULL ) { _security_impl->reset(); int ret_code = _security_impl->init(_security, security_instance_id); if (ret_code == M2MConnectionHandler::ERROR_NONE) { // Initiate handshake. Perhaps there could be a separate event type for this? _socket_state = ESocketStateHandshaking; send_socket_event(ESocketCallback); } else { tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - init failed"); close_socket(); if (ret_code == M2MConnectionHandler::FAILED_TO_READ_CREDENTIALS) { _observer.socket_error(M2MConnectionHandler::FAILED_TO_READ_CREDENTIALS, false); } else { _observer.socket_error(M2MConnectionHandler::SSL_CONNECTION_ERROR, true); } return; } } else { tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - sec is null"); close_socket(); _observer.socket_error(M2MConnectionHandler::SSL_CONNECTION_ERROR, true); return; } } } if (_socket_state != ESocketStateHandshaking) { _socket_state = ESocketStateUnsecureConnection; _observer.address_ready(_address, _server_type, _address._port); } break; } } bool M2MConnectionHandlerPimpl::send_data(uint8_t *data, uint16_t data_len, sn_nsdl_addr_s *address) { if (address == NULL || data == NULL || !data_len || _socket_state < ESocketStateUnsecureConnection) { tr_warn("M2MConnectionHandlerPimpl::send_data() - too early"); return false; } send_data_queue_s* out_data = (send_data_queue_s*)malloc(sizeof(send_data_queue_s)); if (!out_data) { return false; } memset(out_data, 0, sizeof(send_data_queue_s)); uint8_t offset = 0; #ifdef PAL_NET_TCP_AND_TLS_SUPPORT if (is_tcp_connection() && !_secure_connection ) { offset = 4; } #endif out_data->data = (uint8_t*)malloc(data_len + offset); if (!out_data->data) { free(out_data); return false; } // TCP non-secure // We need to "shim" the length in front #ifdef PAL_NET_TCP_AND_TLS_SUPPORT if (is_tcp_connection() && !_secure_connection ) { out_data->data[0] = 0; out_data->data[1] = 0; out_data->data[2] = (data_len >> 8 ) & 0xff; out_data->data[3] = data_len & 0xff; } #endif //PAL_NET_TCP_AND_TLS_SUPPORT memcpy(out_data->data + offset, data, data_len); out_data->data_len = data_len + offset; claim_mutex(); ns_list_add_to_end(&_linked_list_send_data, out_data); release_mutex(); if (!send_event(ESocketSend)) { // Event push failed, free the buffer claim_mutex(); ns_list_remove(&_linked_list_send_data, out_data); release_mutex(); free(out_data->data); free(out_data); return false; } return true; } void M2MConnectionHandlerPimpl::send_socket_data() { tr_debug("M2MConnectionHandlerPimpl::send_socket_data()"); int bytes_sent = 0; bool success = true; send_data_queue_s* out_data = get_item_from_list(); if (!out_data) { return; } if (!out_data->data || !out_data->data_len || _socket_state < ESocketStateUnsecureConnection) { tr_warn("M2MConnectionHandlerPimpl::send_socket_data() - too early"); add_item_to_list(out_data); return; } // Loop until all the data is sent for (; out_data->offset < out_data->data_len; out_data->offset += bytes_sent) { // Secure send if (_socket_state == ESocketStateSecureConnection) { // TODO! Change the send_message API to take bytes_sent as a out param like the pal send API's. while ((bytes_sent = _security_impl->send_message(out_data->data + out_data->offset, out_data->data_len - out_data->offset)) <= 0) { if (bytes_sent == M2MConnectionHandler::CONNECTION_ERROR_WANTS_WRITE) { // Return and wait the next event add_item_to_list(out_data); return; } if (bytes_sent != M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ) { tr_error("M2MConnectionHandlerPimpl::send_socket_data() - secure, failed %d", bytes_sent); success = false; break; } } if (!success) { break; } } // Unsecure send else { bytes_sent = 0; palStatus_t ret; if (is_tcp_connection()) { #ifdef PAL_NET_TCP_AND_TLS_SUPPORT ret = pal_send(_socket, out_data->data + out_data->offset, out_data->data_len - out_data->offset, (size_t*)&bytes_sent); #endif } else { ret = pal_sendTo(_socket, out_data->data + out_data->offset, out_data->data_len - out_data->offset, (palSocketAddress_t*)&_socket_address, sizeof(_socket_address), (size_t*)&bytes_sent); } if (ret == PAL_ERR_SOCKET_WOULD_BLOCK) { // Return and wait next event add_item_to_list(out_data); return; } if (ret < 0) { tr_error("M2MConnectionHandlerPimpl::send_socket_data() - unsecure failed %" PRIx32, ret); success = false; break; } } } free(out_data->data); free(out_data); if (!success) { if (bytes_sent == M2MConnectionHandler::SSL_PEER_CLOSE_NOTIFY) { _observer.socket_error(M2MConnectionHandler::SSL_PEER_CLOSE_NOTIFY, true); } else if (bytes_sent == M2MConnectionHandler::MEMORY_ALLOCATION_FAILED) { tr_error("M2MConnectionHandlerPimpl::send_socket_data() - memory allocation failed!"); _handshake_retry = 0; _observer.socket_error(M2MConnectionHandler::MEMORY_ALLOCATION_FAILED, false); } else { tr_error("M2MConnectionHandlerPimpl::send_socket_data() - SOCKET_SEND_ERROR"); _observer.socket_error(M2MConnectionHandler::SOCKET_SEND_ERROR, true); } close_socket(); } else { _observer.data_sent(); } } bool M2MConnectionHandlerPimpl::start_listening_for_data() { return true; } void M2MConnectionHandlerPimpl::stop_listening() { // Do not call close_socket() directly here. // This can be called from multiple locations. send_socket_event(ESocketClose); } void M2MConnectionHandlerPimpl::handle_connection_error(int error) { tr_error("M2MConnectionHandlerPimpl::handle_connection_error - error %d", error); _observer.socket_error(error); } void M2MConnectionHandlerPimpl::set_platform_network_handler(void *handler) { tr_debug("M2MConnectionHandlerPimpl::set_platform_network_handler"); if (PAL_SUCCESS != pal_registerNetworkInterface(handler, &_net_iface)) { tr_error("M2MConnectionHandlerPimpl::set_platform_network_handler - Interface registration failed."); } tr_debug("M2MConnectionHandlerPimpl::set_platform_network_handler - index = %d", _net_iface); } void M2MConnectionHandlerPimpl::receive_handshake_handler() { int return_value; tr_debug("M2MConnectionHandlerPimpl::receive_handshake_handler()"); // assert(_socket_state == ESocketStateHandshaking); return_value = _security_impl->connect(_base); if (return_value == M2MConnectionHandler::ERROR_NONE) { _handshake_retry = 0; _socket_state = ESocketStateSecureConnection; _observer.address_ready(_address, _server_type, _server_port); } else if (return_value == M2MConnectionHandler::SSL_PEER_CLOSE_NOTIFY) { _handshake_retry = 0; _observer.socket_error(M2MConnectionHandler::SSL_PEER_CLOSE_NOTIFY, true); close_socket(); } else if (return_value == M2MConnectionHandler::MEMORY_ALLOCATION_FAILED) { tr_error("M2MConnectionHandlerPimpl::receive_handshake_handler() - memory allocation failed"); _handshake_retry = 0; _observer.socket_error(M2MConnectionHandler::MEMORY_ALLOCATION_FAILED, false); close_socket(); } else if (return_value != M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ) { tr_error("M2MConnectionHandlerPimpl::receive_handshake_handler() - SSL_HANDSHAKE_ERROR"); _handshake_retry = 0; _observer.socket_error(M2MConnectionHandler::SSL_HANDSHAKE_ERROR, true); close_socket(); } else { // Comes here only if error is M2MConnectionHandler::ERROR_GENERIC if (_handshake_retry++ > MBED_CONF_MBED_CLIENT_TLS_MAX_RETRY) { tr_error("M2MConnectionHandlerPimpl::receive_handshake_handler() - Max TLS retry fail"); _handshake_retry = 0; _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT, true); close_socket(); } eventOS_event_timer_cancel(ESocketTimerCallback, M2MConnectionHandlerPimpl::_tasklet_id); // There is required to set event.data_ptr for eventloop_event_handler. arm_event_s event = {0}; event.receiver = M2MConnectionHandlerPimpl::_tasklet_id; event.sender = 0; event.event_id = ESocketTimerCallback; event.event_type = ESocketTimerCallback; event.data_ptr = this; event.event_data = 0; event.priority = ARM_LIB_HIGH_PRIORITY_EVENT; eventOS_event_timer_request_in(&event, eventOS_event_timer_ms_to_ticks(1000)); } } bool M2MConnectionHandlerPimpl::is_handshake_ongoing() const { return (_socket_state == ESocketStateHandshaking); } void M2MConnectionHandlerPimpl::receive_handler() { // assert(_socket_state > ESocketStateHandshaking); if (_socket_state == ESocketStateSecureConnection) { int rcv_size; unsigned char recv_buffer[BUFFER_LENGTH]; // we need to read as much as there is data available as the events may or may not be suppressed do { tr_debug("M2MConnectionHandlerPimpl::receive_handler().."); rcv_size = _security_impl->read(recv_buffer, sizeof(recv_buffer)); tr_debug("M2MConnectionHandlerPimpl::receive_handler() res: %d", rcv_size); if (rcv_size > 0) { _observer.data_available((uint8_t*)recv_buffer, rcv_size, _address); } else if (M2MConnectionHandler::SSL_PEER_CLOSE_NOTIFY == rcv_size) { tr_error("M2MConnectionHandlerPimpl::receive_handler() - peer close notify!"); _observer.socket_error(M2MConnectionHandler::SSL_PEER_CLOSE_NOTIFY, true); return; } else if (M2MConnectionHandler::MEMORY_ALLOCATION_FAILED == rcv_size) { tr_error("M2MConnectionHandlerPimpl::receive_handler() - memory allocation failed!"); _observer.socket_error(M2MConnectionHandler::MEMORY_ALLOCATION_FAILED, false); close_socket(); return; } else if (M2MConnectionHandler::ERROR_GENERIC == rcv_size) { tr_error("M2MConnectionHandlerPimpl::receive_handler() - secure ERROR_GENERIC"); _observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true); close_socket(); return; } } while (rcv_size > 0 && _socket_state == ESocketStateSecureConnection); } else { size_t recv; palStatus_t status; unsigned char recv_buffer[BUFFER_LENGTH]; do { if (is_tcp_connection()) { #ifdef PAL_NET_TCP_AND_TLS_SUPPORT status = pal_recv(_socket, recv_buffer, sizeof(recv_buffer), &recv); #endif //PAL_NET_TCP_AND_TLS_SUPPORT } else { status = pal_receiveFrom(_socket, recv_buffer, sizeof(recv_buffer), NULL, NULL, &recv); } if (status == PAL_ERR_SOCKET_WOULD_BLOCK) { return; } else if (status != PAL_SUCCESS) { tr_error("M2MConnectionHandlerPimpl::receive_handler() - SOCKET_READ_ERROR %" PRIx32, status); _observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true); close_socket(); return; } tr_debug("M2MConnectionHandlerPimpl::receive_handler() - data received, len: %zu", recv); if (!is_tcp_connection()) { // Observer for UDP plain mode _observer.data_available((uint8_t*)recv_buffer, recv, _address); } else { #ifdef PAL_NET_TCP_AND_TLS_SUPPORT if ( recv < 4 ) { tr_error("M2MConnectionHandlerPimpl::receive_handler() - TCP SOCKET_READ_ERROR"); _observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true); close_socket(); return; } //We need to "shim" out the length from the front uint32_t len = (recv_buffer[0] << 24 & 0xFF000000) + (recv_buffer[1] << 16 & 0xFF0000); len += (recv_buffer[2] << 8 & 0xFF00) + (recv_buffer[3] & 0xFF); if (len > 0 && len <= recv - 4) { // Observer for TCP plain mode _observer.data_available(recv_buffer + 4, len, _address); } #endif //PAL_NET_TCP_AND_TLS_SUPPORT } } while (recv > 0 && _socket_state == ESocketStateUnsecureConnection); } } void M2MConnectionHandlerPimpl::claim_mutex() { eventOS_scheduler_mutex_wait(); } void M2MConnectionHandlerPimpl::release_mutex() { eventOS_scheduler_mutex_release(); } bool M2MConnectionHandlerPimpl::init_socket() { palSocketType_t socket_type = PAL_SOCK_DGRAM; palStatus_t status; palSocketAddress_t bind_address; palIpV4Addr_t interface_address4; palIpV6Addr_t interface_address6; memset(&bind_address, 0, sizeof(palSocketAddress_t)); memset(&interface_address4, 0, sizeof(interface_address4)); memset(&interface_address6, 0, sizeof(interface_address6)); if (is_tcp_connection()) { #ifdef PAL_NET_TCP_AND_TLS_SUPPORT socket_type = PAL_SOCK_STREAM; #else // Somebody has built code without TCP support but tries to use it. // Perhaps a "assert(false)" would be sufficient. tr_error("M2MConnectionHandlerPimpl::init_socket() - TCP config error"); _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT); return; #endif //PAL_NET_TCP_AND_TLS_SUPPORT } status = pal_asynchronousSocketWithArgument((palSocketDomain_t)_socket_address.addressType, socket_type, true, _net_iface, &socket_event_handler, this, &_socket); if (PAL_SUCCESS != status) { tr_error("M2MConnectionHandlerPimpl::init_socket() - socket create error : %" PRIx32, status); _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT); return false; } if (_socket_address.addressType == PAL_AF_INET) { status = pal_setSockAddrIPV4Addr(&bind_address, interface_address4); } else if (_socket_address.addressType == PAL_AF_INET6) { status = pal_setSockAddrIPV6Addr(&bind_address, interface_address6); } else { tr_warn("M2MConnectionHandlerPimpl::init_socket() - stack type: %d", (int)_socket_address.addressType); } if (PAL_SUCCESS != status) { tr_error("M2MConnectionHandlerPimpl::init_socket - setSockAddrIPV err: %" PRIx32, status); return false; } status = pal_setSockAddrPort(&bind_address, _listen_port); if (PAL_SUCCESS != status) { tr_error("M2MConnectionHandlerPimpl::init_socket - setSockAddrPort err: %" PRIx32, status); return false; } pal_bind(_socket, &bind_address, sizeof(bind_address)); _security_impl->set_socket(_socket, (palSocketAddress_t*)&_socket_address); return true; } bool M2MConnectionHandlerPimpl::is_tcp_connection() const { return ( _binding_mode == M2MInterface::TCP || _binding_mode == M2MInterface::TCP_QUEUE ); } void M2MConnectionHandlerPimpl::close_socket() { _suppressable_event_in_flight = false; if (_socket) { // At least on mbed-os the pal_close() will perform callbacks even during it // is called, which we will ignore when this state is set. _socket_state = ESocketStateCloseBeingCalled; pal_close(&_socket); _socket = 0; } // make sure the socket connection statemachine is reset too. _socket_state = ESocketStateDisconnected; if (_security_impl) { _security_impl->reset(); } claim_mutex(); /*ns_list_foreach_safe(M2MConnectionHandlerPimpl::send_data_queue_s, tmp, &_linked_list_send_data) { ns_list_remove(&_linked_list_send_data, tmp); free(tmp->data); free(tmp); }*/ // Workaround for IAR compilation issue. ns_list_foreach does not compile with IAR. // Error[Pe144]: a value of type "void *" cannot be used to initialize an entity of type "M2MConnectionHandlerPimpl::send_data_queue *" while (!ns_list_is_empty(&_linked_list_send_data)) { send_data_queue_s* data = (send_data_queue_s*)ns_list_get_first(&_linked_list_send_data); ns_list_remove(&_linked_list_send_data, data); free(data->data); free(data); } release_mutex(); } M2MConnectionHandlerPimpl::send_data_queue_s* M2MConnectionHandlerPimpl::get_item_from_list() { claim_mutex(); send_data_queue_s* out_data = (send_data_queue_s*)ns_list_get_first(&_linked_list_send_data); if (out_data) { ns_list_remove(&_linked_list_send_data, out_data); } release_mutex(); return out_data; } void M2MConnectionHandlerPimpl::add_item_to_list(M2MConnectionHandlerPimpl::send_data_queue_s *data) { claim_mutex(); ns_list_add_to_start(&_linked_list_send_data, data); release_mutex(); } void M2MConnectionHandlerPimpl::force_close() { close_socket(); } void M2MConnectionHandlerPimpl::unregister_network_handler() { pal_unregisterNetworkInterface(_net_iface); }