Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: FXAS21002 FXOS8700Q
simple-mbed-cloud-client/mbed-cloud-client/mbed-client/mbed-client-classic/source/m2mconnectionhandlerpimpl.cpp
- Committer:
- vithyat
- Date:
- 2019-08-28
- Revision:
- 0:977e87915078
File content as of revision 0:977e87915078:
/*
* 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);
}