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.
Diff: mbed-client/mbed-client-classic/source/m2mconnectionhandlerpimpl.cpp
- Revision:
- 0:06ee5f8a484a
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-client/mbed-client-classic/source/m2mconnectionhandlerpimpl.cpp Sat Mar 18 22:37:16 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
+}