FRDM K64F Metronome

Revision:
0:a2cb7295a1f7
diff -r 000000000000 -r a2cb7295a1f7 mbed-client/mbed-client-classic/source/m2mconnectionhandlerpimpl.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-client/mbed-client-classic/source/m2mconnectionhandlerpimpl.cpp	Sun May 14 18:35:07 2017 +0000
@@ -0,0 +1,640 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+#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 "pal_rtos.h"
+#include "pal_errors.h"
+#include "pal_macros.h"
+#include "pal_network.h"
+
+#include "eventOS_scheduler.h"
+#include "eventOS_event.h"
+
+#include "mbed-trace/mbed_trace.h"
+#include "mbed.h"
+
+#define TRACE_GROUP "mClt"
+
+int8_t M2MConnectionHandlerPimpl::_tasklet_id = -1;
+
+static M2MConnectionHandlerPimpl *connection_handler = NULL;
+
+extern "C" void connection_event_handler(arm_event_s *event)
+{
+    if(!connection_handler){
+        return;
+    }
+
+    switch(event->event_type){
+        case M2MConnectionHandlerPimpl::ESocketReadytoRead:
+            connection_handler->receive_handler();
+            break;
+
+        case M2MConnectionHandlerPimpl::ESocketSend:
+            connection_handler->send_socket_data((uint8_t*)event->data_ptr, event->event_data);
+            free(event->data_ptr);
+            break;
+
+        case M2MConnectionHandlerPimpl::ESocketDnsHandler:
+            connection_handler->dns_handler();
+            break;
+
+        default:
+            break;
+    }
+}
+
+void M2MConnectionHandlerPimpl::send_receive_event(void)
+{
+    arm_event_s event;
+    event.receiver = M2MConnectionHandlerPimpl::_tasklet_id;
+    event.sender = 0;
+    event.event_type = ESocketReadytoRead;
+    event.data_ptr = NULL;
+    event.priority = ARM_LIB_HIGH_PRIORITY_EVENT;
+    eventOS_event_send(&event);
+}
+
+extern "C" void socket_event_handler(void)
+{
+    if(!connection_handler) {
+        return;
+    }
+    connection_handler->send_receive_event();
+}
+
+M2MConnectionHandlerPimpl::M2MConnectionHandlerPimpl(M2MConnectionHandler* base, M2MConnectionObserver &observer,
+                                                     M2MConnectionSecurity* sec,
+                                                     M2MInterface::BindingMode mode,
+                                                     M2MInterface::NetworkStack stack)
+:_base(base),
+ _observer(observer),
+ _security_impl(sec),
+ _security(NULL),
+ _use_secure_connection(false),
+ _binding_mode(mode),
+ _network_stack(stack),
+ _socket(0),
+ _is_handshaking(false),
+ _listening(true),
+ _server_type(M2MConnectionObserver::LWM2MServer),
+ _server_port(0),
+ _listen_port(0),
+ _running(false),
+ _net_iface(0)
+{
+#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(&_socket_address, 0, sizeof _socket_address);
+    connection_handler = this;
+    eventOS_scheduler_mutex_wait();
+    if (M2MConnectionHandlerPimpl::_tasklet_id == -1) {
+        M2MConnectionHandlerPimpl::_tasklet_id = eventOS_event_handler_create(&connection_event_handler, ESocketIdle);
+    }
+    eventOS_scheduler_mutex_release();
+}
+
+M2MConnectionHandlerPimpl::~M2MConnectionHandlerPimpl()
+{
+    tr_debug("~M2MConnectionHandlerPimpl()");
+    stop_listening();
+
+    close_socket();
+
+    delete _security_impl;
+    tr_debug("~M2MConnectionHandlerPimpl() - OUT");
+}
+
+bool M2MConnectionHandlerPimpl::bind_connection(const uint16_t listen_port)
+{
+    _listen_port = listen_port;
+    return true;
+}
+
+bool M2MConnectionHandlerPimpl::resolve_server_address(const String& server_address,
+                                                       const uint16_t server_port,
+                                                       M2MConnectionObserver::ServerType server_type,
+                                                       const M2MSecurity* security)
+{
+    arm_event_s event;
+
+    tr_debug("resolve_server_address()");
+
+    _security = security;
+    _server_port = server_port;
+    _server_type = server_type;
+    _server_address = server_address;
+
+    event.receiver = M2MConnectionHandlerPimpl::_tasklet_id;
+    event.sender = 0;
+    event.event_type = ESocketDnsHandler;
+    event.data_ptr = NULL;
+    event.priority = ARM_LIB_HIGH_PRIORITY_EVENT;
+
+    return !eventOS_event_send(&event);
+}
+
+void M2MConnectionHandlerPimpl::dns_handler()
+{
+    palStatus_t status;
+    uint32_t interface_count;
+    status = pal_getNumberOfNetInterfaces(&interface_count);
+    if(PAL_SUCCESS != status ) {
+        _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
+        return;
+    }
+    if(interface_count <= 0) {
+        _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
+        return;
+    }
+
+    palSocketLength_t _socket_address_len;
+
+    if(PAL_SUCCESS != pal_getAddressInfo(_server_address.c_str(), &_socket_address, &_socket_address_len)){
+        _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
+        return;
+    }
+    pal_setSockAddrPort(&_socket_address, _server_port);
+
+    if(_network_stack == M2MInterface::LwIP_IPv4 ||
+       _network_stack == M2MInterface::ATWINC_IPv4){
+        if(PAL_SUCCESS != pal_getSockAddrIPV4Addr(&_socket_address,_ipV4Addr)){
+            _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
+            return;
+        }
+
+        tr_debug("IP Address %s",tr_array(_ipV4Addr, 4));
+
+        _address._address = (void*)_ipV4Addr;
+        _address._length = PAL_IPV4_ADDRESS_SIZE;
+        _address._port = _server_port;
+        _address._stack = _network_stack;
+    }
+    else if(_network_stack == M2MInterface::LwIP_IPv6 ||
+            _network_stack == M2MInterface::Nanostack_IPv6){
+        if(PAL_SUCCESS != pal_getSockAddrIPV6Addr(&_socket_address,_ipV6Addr)){
+            _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
+            return;
+        }
+
+        tr_debug("IP Address %s",tr_array(_ipV6Addr,sizeof(_ipV6Addr)));
+
+        _address._address = (void*)_ipV6Addr;
+        _address._length = PAL_IPV6_ADDRESS_SIZE;
+        _address._port = _server_port;
+        _address._stack = _network_stack;
+    }
+    else {
+        tr_error("socket config error, %d", (int)_network_stack);
+        _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
+        return;
+    }
+
+    close_socket();
+    if(!init_socket()) {
+        _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
+        return;
+    }
+
+    if(is_tcp_connection()) {
+#ifdef PAL_NET_TCP_AND_TLS_SUPPORT
+       tr_debug("resolve_server_address - Using TCP");
+        if (pal_connect(_socket, &_socket_address, sizeof(_socket_address)) != PAL_SUCCESS) {
+            _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
+            return;
+        }
+#endif //PAL_NET_TCP_AND_TLS_SUPPORT
+    }
+
+    _running = true;
+
+    if (_security) {
+        if (_security->resource_value_int(M2MSecurity::SecurityMode) == M2MSecurity::Certificate ||
+            _security->resource_value_int(M2MSecurity::SecurityMode) == M2MSecurity::Psk) {
+            if( _security_impl != NULL ){
+                _security_impl->reset();
+                if (_security_impl->init(_security) == 0) {
+                    _is_handshaking = true;
+                    tr_debug("resolve_server_address - connect DTLS");
+                    if(_security_impl->start_connecting_non_blocking(_base) < 0 ){
+                        tr_debug("dns_handler - handshake failed");
+                        _is_handshaking = false;
+                        _observer.socket_error(M2MConnectionHandler::SSL_CONNECTION_ERROR);
+                        close_socket();
+                        return;
+                    }
+                } else {
+                    tr_error("resolve_server_address - init failed");
+                    _observer.socket_error(M2MConnectionHandler::SSL_CONNECTION_ERROR, false);
+                    close_socket();
+                    return;
+                }
+            } else {
+                tr_error("dns_handler - sec is null");
+                _observer.socket_error(M2MConnectionHandler::SSL_CONNECTION_ERROR, false);
+                close_socket();
+                return;
+            }
+        }
+    }
+    if(!_is_handshaking) {
+        enable_keepalive();
+        _observer.address_ready(_address,
+                                _server_type,
+                                _address._port);
+    }
+}
+
+bool M2MConnectionHandlerPimpl::send_data(uint8_t *data,
+                                          uint16_t data_len,
+                                          sn_nsdl_addr_s *address)
+{
+    arm_event_s event;
+
+    tr_debug("send_data()");
+    if (address == NULL || data == NULL || !data_len || !_running) {
+        return false;
+    }
+
+    event.data_ptr = (uint8_t*)malloc(data_len);
+    if(!event.data_ptr) {
+        return false;
+    }
+    memcpy(event.data_ptr, data, data_len);
+
+    event.receiver = M2MConnectionHandlerPimpl::_tasklet_id;
+    event.sender = 0;
+    event.event_type = ESocketSend;
+    event.event_data = data_len;
+    event.priority = ARM_LIB_HIGH_PRIORITY_EVENT;
+
+    if (eventOS_event_send(&event) != 0) {
+        // Event push failed, free the buffer
+        free(event.data_ptr);
+        return false;
+    }
+
+    return true;
+}
+
+void M2MConnectionHandlerPimpl::send_socket_data(uint8_t *data, uint16_t data_len)
+{
+    size_t sent_len;
+    bool success = false;
+    palStatus_t ret = PAL_ERR_GENERIC_FAILURE;
+
+    if(!data || ! data_len || !_running) {
+        return;
+    }
+
+    tr_debug("send_handler()");
+
+    if( _use_secure_connection ){
+        if( _security_impl->send_message(data, data_len) > 0){
+            success = true;
+        }
+    } else {
+        if(is_tcp_connection()){
+#ifdef PAL_NET_TCP_AND_TLS_SUPPORT
+            //We need to "shim" the length in front
+            uint8_t* d = (uint8_t*)malloc(data_len+4);
+            if(d){
+                d[0] = 0;
+                d[1] = 0;
+                d[2] = (data_len >> 8 )& 0xff;
+                d[3] = data_len & 0xff;
+                memcpy(d + 4, data, data_len);
+                ret = pal_send(_socket, d, data_len+4, &sent_len);
+                free(d);
+            }
+#endif //PAL_NET_TCP_AND_TLS_SUPPORT
+        } else {
+            ret = pal_sendTo(_socket, data, data_len, &_socket_address, sizeof(_socket_address), &sent_len);
+        }
+        if (ret == PAL_SUCCESS) {
+            success = true;
+        }
+    }
+
+    if (!success) {
+        _observer.socket_error(M2MConnectionHandler::SOCKET_SEND_ERROR, true);
+        close_socket();
+    }
+    else{
+        _observer.data_sent();
+    }
+}
+
+bool M2MConnectionHandlerPimpl::start_listening_for_data()
+{
+    tr_debug("start_listening_for_data()");
+    _listening = true;
+    return true;
+}
+
+void M2MConnectionHandlerPimpl::stop_listening()
+{
+    tr_debug("stop_listening()");
+    _listening = false;
+
+    if(_security_impl) {
+        _security_impl->reset();
+    }
+}
+
+int M2MConnectionHandlerPimpl::send_to_socket(const unsigned char *buf, size_t len)
+{
+    size_t sent_len = 0;
+    palStatus_t status = PAL_ERR_GENERIC_FAILURE;
+
+    if(!_running) {
+        return (-1);
+    }
+
+    tr_debug("send_to_socket len - %d", len);
+
+    if(is_tcp_connection()) {
+#ifdef PAL_NET_TCP_AND_TLS_SUPPORT
+        status = pal_send(_socket, buf, len, &sent_len);
+#endif //PAL_NET_TCP_AND_TLS_SUPPORT
+    } else {
+        status = pal_sendTo(_socket, buf, len, &_socket_address, sizeof(_socket_address), &sent_len);
+    }
+
+    if(status == PAL_SUCCESS){
+        return sent_len;
+    }
+
+    return (-1);
+}
+
+int M2MConnectionHandlerPimpl::receive_from_socket(unsigned char *buf, size_t len)
+{
+    size_t recv_len;
+    palStatus_t status = PAL_ERR_GENERIC_FAILURE;
+    tr_debug("receive_from_socket");
+
+    if(!_running) {
+        return (-1);
+    }
+
+    if(is_tcp_connection()) {
+#ifdef PAL_NET_TCP_AND_TLS_SUPPORT
+        status = pal_recv(_socket, buf, len, &recv_len);
+#endif //PAL_NET_TCP_AND_TLS_SUPPORT
+    } else {
+        status = pal_receiveFrom(_socket, buf, len, NULL, NULL, &recv_len);
+    }
+
+    if(status == PAL_SUCCESS){
+        return recv_len;
+    }
+    else if(status == PAL_ERR_SOCKET_WOULD_BLOCK || status == (-65536)){
+        return M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ;
+    }
+    else {
+        tr_info("PAL Socket returned: %d", status);
+    }
+
+    return (-1);
+}
+
+void M2MConnectionHandlerPimpl::handle_connection_error(int error)
+{
+    tr_debug("handle_connection_error");
+    _observer.socket_error(error);
+}
+
+void M2MConnectionHandlerPimpl::set_platform_network_handler(void *handler)
+{
+    tr_debug("set_platform_network_handler");
+    if(PAL_SUCCESS != pal_registerNetworkInterface(handler, &_net_iface)) {
+        tr_error("Interface registration failed.");
+    }
+}
+
+void M2MConnectionHandlerPimpl::receive_handshake_handler()
+{
+    tr_debug("receive_handshake_handler()");
+    if( _is_handshaking ){
+        int ret = _security_impl->continue_connecting();
+        tr_debug("ret %d", ret);
+        if( ret == M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ ){ //We wait for next readable event
+            tr_debug("We wait for next readable event");
+            return;
+        } else if( ret == 0 ){
+            _is_handshaking = false;
+            _use_secure_connection = true;
+            enable_keepalive();
+            _observer.address_ready(_address,
+                                    _server_type,
+                                    _server_port);
+        } else if( ret < 0 ){
+            _is_handshaking = false;
+            _observer.socket_error(M2MConnectionHandler::SSL_CONNECTION_ERROR, true);
+            close_socket();
+        }
+    }
+}
+
+bool M2MConnectionHandlerPimpl::is_handshake_ongoing()
+{
+    return _is_handshaking;
+}
+
+void M2MConnectionHandlerPimpl::receive_handler()
+{
+    tr_debug("receive_handler()");
+    if(_is_handshaking){
+        receive_handshake_handler();
+        return;
+    }
+
+    if(!_listening || !_running) {
+        return;
+    }
+
+    if( _use_secure_connection ){
+        int rcv_size;
+        do{
+            rcv_size = _security_impl->read(_recv_buffer, sizeof(_recv_buffer));
+            if(rcv_size > 0){
+                _observer.data_available((uint8_t*)_recv_buffer,
+                                         rcv_size, _address);
+            } else if (M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ != rcv_size && rcv_size < 0) {
+                _observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true);
+                close_socket();
+                return;
+            }
+        } while(M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ != rcv_size);
+    } else{
+        size_t recv;
+        palStatus_t status;
+        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) {
+                _observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true);
+                close_socket();
+                return;
+            }
+
+            tr_debug("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 ){
+                    _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(status != PAL_ERR_SOCKET_WOULD_BLOCK);
+    }
+}
+
+void M2MConnectionHandlerPimpl::claim_mutex()
+{
+    eventOS_scheduler_mutex_wait();
+}
+
+void M2MConnectionHandlerPimpl::release_mutex()
+{
+    eventOS_scheduler_mutex_release();
+}
+
+static palNetInterfaceInfo_t interface_info;
+static palIpV4Addr_t interface_address4 = {0,0,0,0};
+static palIpV6Addr_t interface_address6 = {0};
+
+bool M2MConnectionHandlerPimpl::init_socket()
+{
+    tr_debug("init_socket - IN");
+    _is_handshaking = false;
+    _running = true;
+    palSocketType_t socket_type = PAL_SOCK_DGRAM;
+    palStatus_t status;
+    palSocketDomain_t domain;
+    palSocketAddress_t bind_address;
+
+    if(is_tcp_connection()) {
+#ifdef PAL_NET_TCP_AND_TLS_SUPPORT
+        socket_type = PAL_SOCK_STREAM;
+#else
+        _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
+        return;
+#endif //PAL_NET_TCP_AND_TLS_SUPPORT
+    }
+
+    if(_network_stack == M2MInterface::LwIP_IPv4){
+        domain = PAL_AF_INET;
+    } else if(_network_stack == M2MInterface::LwIP_IPv6){
+        domain = PAL_AF_INET6;
+    } else {
+        domain = PAL_AF_UNSPEC;
+    }
+
+    uint32_t interface_count;
+    pal_getNumberOfNetInterfaces(&interface_count);
+    tr_debug("Interface count: %d",interface_count);
+    pal_getNetInterfaceInfo(_net_iface, &interface_info);
+    tr_debug("Interface name: %s",interface_info.interfaceName);
+    tr_debug("Interface no: %d", _net_iface);
+
+    tr_debug("init_socket - port %d", _listen_port);
+
+    status = pal_asynchronousSocket(domain, socket_type, 1, _net_iface, &socket_event_handler, &_socket);
+
+    if(PAL_SUCCESS != status) {
+        _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
+        return false;
+    }
+
+    if(_network_stack == M2MInterface::LwIP_IPv4){
+        pal_setSockAddrIPV4Addr(&bind_address, interface_address4);
+    } else if(_network_stack == M2MInterface::LwIP_IPv6){
+        pal_setSockAddrIPV6Addr(&bind_address, interface_address6);
+    }
+
+    pal_setSockAddrPort(&bind_address, _listen_port);
+    pal_bind(_socket, &bind_address, sizeof(bind_address));
+
+    tr_debug("init_socket - OUT");
+    return true;
+}
+
+bool M2MConnectionHandlerPimpl::is_tcp_connection()
+{
+    return ( _binding_mode == M2MInterface::TCP ||
+             _binding_mode == M2MInterface::TCP_QUEUE );
+}
+
+void M2MConnectionHandlerPimpl::close_socket()
+{
+    tr_debug("close_socket() - IN");
+    if(_running) {
+       _running = false;
+       pal_close(&_socket);
+    }
+    tr_debug("close_socket() - OUT");
+}
+
+void M2MConnectionHandlerPimpl::enable_keepalive()
+{
+#if MBED_CLIENT_TCP_KEEPALIVE_TIME
+#ifdef PAL_NET_TCP_AND_TLS_SUPPORT
+    if(is_tcp_connection()) {
+        int enable = 1;
+        pal_setSocketOptions(_socket, PAL_SO_KEEPALIVE, &enable, sizeof(enable));
+    }
+#endif
+#endif
+}