mbed-os/features/FEATURE_CLIENT/mbed-client-classic/source/m2mconnectionhandlerpimpl.cpp

589 lines
20 KiB
C++

/*
* 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 "NetworkInterface.h"
#include "UDPSocket.h"
#include "TCPSocket.h"
#include "eventOS_event.h"
#include "eventOS_scheduler.h"
#include "mbed-trace/mbed_trace.h"
#define TRACE_GROUP "mClt"
#ifdef MBED_CONF_MBED_CLIENT_EVENT_LOOP_SIZE
#define MBED_CLIENT_EVENT_LOOP_SIZE MBED_CONF_MBED_CLIENT_EVENT_LOOP_SIZE
#else
#define MBED_CLIENT_EVENT_LOOP_SIZE 1024
#endif
int8_t M2MConnectionHandlerPimpl::_tasklet_id = -1;
static MemoryPool<M2MConnectionHandlerPimpl::TaskIdentifier, MBED_CLIENT_EVENT_LOOP_SIZE/64> memory_pool;
extern "C" void connection_tasklet_event_handler(arm_event_s *event)
{
tr_debug("M2MConnectionHandlerPimpl::connection_tasklet_event_handler");
M2MConnectionHandlerPimpl::TaskIdentifier *task_id = (M2MConnectionHandlerPimpl::TaskIdentifier*)event->data_ptr;
M2MConnectionHandlerPimpl* pimpl = (M2MConnectionHandlerPimpl*)task_id->pimpl;
if(pimpl) {
eventOS_scheduler_set_active_tasklet(pimpl->connection_tasklet_handler());
}
switch (event->event_type) {
case M2MConnectionHandlerPimpl::ESocketIdle:
tr_debug("Connection Tasklet Generated");
break;
case M2MConnectionHandlerPimpl::ESocketReadytoRead:
tr_debug("connection_tasklet_event_handler - ESocketReadytoRead");
if(pimpl) {
if(pimpl->is_handshake_ongoing()) {
pimpl->receive_handshake_handler();
} else {
pimpl->receive_handler();
}
}
break;
case M2MConnectionHandlerPimpl::ESocketDnsHandler:
tr_debug("connection_tasklet_event_handler - ESocketDnsHandler");
if(pimpl) {
pimpl->dns_handler();
}
break;
case M2MConnectionHandlerPimpl::ESocketSend:
tr_debug("connection_tasklet_event_handler - ESocketSend");
if(pimpl) {
pimpl->send_socket_data((uint8_t*)task_id->data_ptr,(uint16_t)event->event_data);
if (task_id->data_ptr) {
free(task_id->data_ptr);
}
}
break;
default:
break;
}
if (task_id) {
memory_pool.free(task_id);
}
}
M2MConnectionHandlerPimpl::M2MConnectionHandlerPimpl(M2MConnectionHandler* base, M2MConnectionObserver &observer,
M2MConnectionSecurity* sec,
M2MInterface::BindingMode mode,
M2MInterface::NetworkStack stack)
:_base(base),
_observer(observer),
_security_impl(sec),
_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),
_socket_address(0)
{
memset(&_address_buffer, 0, sizeof _address_buffer);
memset(&_address, 0, sizeof _address);
_address._address = _address_buffer;
if (_network_stack != M2MInterface::LwIP_IPv4) {
tr_error("ConnectionHandler: Unsupported network stack, only IPv4 is currently supported");
}
_running = true;
tr_debug("M2MConnectionHandlerPimpl::M2MConnectionHandlerPimpl() - Initializing thread");
eventOS_scheduler_mutex_wait();
if (M2MConnectionHandlerPimpl::_tasklet_id == -1) {
M2MConnectionHandlerPimpl::_tasklet_id = eventOS_event_handler_create(&connection_tasklet_event_handler, ESocketIdle);
}
eventOS_scheduler_mutex_release();
}
M2MConnectionHandlerPimpl::~M2MConnectionHandlerPimpl()
{
tr_debug("M2MConnectionHandlerPimpl::~M2MConnectionHandlerPimpl()");
if(_socket_address) {
delete _socket_address;
_socket_address = NULL;
}
if (_socket) {
delete _socket;
_socket = 0;
}
_net_iface = 0;
delete _security_impl;
tr_debug("M2MConnectionHandlerPimpl::~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)
{
tr_debug("M2MConnectionHandlerPimpl::resolve_server_address()");
if (!_net_iface) {
return false;
}
_security = security;
_server_port = server_port;
_server_type = server_type;
_server_address = server_address;
TaskIdentifier* task = memory_pool.alloc();
if (!task) {
return false;
}
task->pimpl = this;
arm_event_s event;
event.receiver = M2MConnectionHandlerPimpl::_tasklet_id;
event.sender = 0;
event.event_type = ESocketDnsHandler;
event.data_ptr = task;
event.priority = ARM_LIB_HIGH_PRIORITY_EVENT;
return eventOS_event_send(&event) == 0 ? true : false;
}
void M2MConnectionHandlerPimpl::dns_handler()
{
tr_debug("M2MConnectionHandlerPimpl::dns_handler()");
if(_socket_address) {
delete _socket_address;
_socket_address = NULL;
}
_socket_address = new SocketAddress(_net_iface,_server_address.c_str(), _server_port);
if(*_socket_address) {
_address._address = (void*)_socket_address->get_ip_address();
tr_debug("IP Address %s",_socket_address->get_ip_address());
tr_debug("Port %d",_socket_address->get_port());
_address._length = strlen((char*)_address._address);
_address._port = _socket_address->get_port();
_address._stack = _network_stack;
} else {
_observer.socket_error(M2MConnectionHandler::DNS_RESOLVING_ERROR, true);
close_socket();
return;
}
close_socket();
init_socket();
if(is_tcp_connection()) {
tr_debug("M2MConnectionHandlerPimpl::resolve_server_address - Using TCP");
if (((TCPSocket*)_socket)->connect(*_socket_address) < 0) {
_observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
return;
}
}
_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("M2MConnectionHandlerPimpl::resolve_server_address - connect DTLS");
if(_security_impl->start_connecting_non_blocking(_base) < 0 ){
tr_debug("M2MConnectionHandlerPimpl::dns_handler - handshake failed");
_is_handshaking = false;
_observer.socket_error(M2MConnectionHandler::SSL_CONNECTION_ERROR);
close_socket();
return;
}
} else {
tr_error("M2MConnectionHandlerPimpl::resolve_server_address - init failed");
_observer.socket_error(M2MConnectionHandler::SSL_CONNECTION_ERROR, false);
close_socket();
return;
}
} else {
tr_error("M2MConnectionHandlerPimpl::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);
}
}
void M2MConnectionHandlerPimpl::send_handler()
{
tr_debug("M2MConnectionHandlerPimpl::send_handler()");
_observer.data_sent();
}
bool M2MConnectionHandlerPimpl::send_data(uint8_t *data,
uint16_t data_len,
sn_nsdl_addr_s *address)
{
tr_debug("M2MConnectionHandlerPimpl::send_data()");
if (address == NULL || data == NULL) {
return false;
}
uint8_t *buffer = (uint8_t*)malloc(data_len);
if(!buffer) {
return false;
}
TaskIdentifier* task = memory_pool.alloc();
if (!task) {
free(buffer);
return false;
}
task->pimpl = this;
memcpy(buffer, data, data_len);
task->data_ptr = buffer;
arm_event_s event;
event.receiver = M2MConnectionHandlerPimpl::_tasklet_id;
event.sender = 0;
event.event_type = ESocketSend;
event.data_ptr = task;
event.event_data = data_len;
event.priority = ARM_LIB_HIGH_PRIORITY_EVENT;
return eventOS_event_send(&event) == 0 ? true : false;
}
void M2MConnectionHandlerPimpl::send_socket_data(uint8_t *data,
uint16_t data_len)
{
bool success = false;
if( _use_secure_connection ){
if( _security_impl->send_message(data, data_len) > 0){
success = true;
}
} else {
int32_t ret = -1;
if(is_tcp_connection()){
//We need to "shim" the length in front
uint16_t d_len = data_len+4;
uint8_t* d = (uint8_t*)malloc(data_len+4);
d[0] = (data_len >> 24 )& 0xff;
d[1] = (data_len >> 16 )& 0xff;
d[2] = (data_len >> 8 )& 0xff;
d[3] = data_len & 0xff;
memmove(d+4, data, data_len);
ret = ((TCPSocket*)_socket)->send(d,d_len);
free(d);
}else {
ret = ((UDPSocket*)_socket)->sendto(*_socket_address,data, data_len);
}
if (ret > 0) {
success = true;
}
}
if (!success) {
_observer.socket_error(M2MConnectionHandler::SOCKET_SEND_ERROR, true);
close_socket();
}
}
int8_t M2MConnectionHandlerPimpl::connection_tasklet_handler()
{
return M2MConnectionHandlerPimpl::_tasklet_id;
}
void M2MConnectionHandlerPimpl::socket_event()
{
TaskIdentifier* task = memory_pool.alloc();
if (!task) {
_observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true);
return;
}
task->pimpl = this;
arm_event_s event;
event.receiver = M2MConnectionHandlerPimpl::_tasklet_id;
event.sender = 0;
event.event_type = ESocketReadytoRead;
event.data_ptr = task;
event.priority = ARM_LIB_HIGH_PRIORITY_EVENT;
int8_t error = eventOS_event_send(&event);
if(error != 0) {
_observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true);
}
}
bool M2MConnectionHandlerPimpl::start_listening_for_data()
{
tr_debug("M2MConnectionHandlerPimpl::start_listening_for_data()");
// Boolean return required for other platforms,
// not needed in mbed OS Socket.
_listening = true;
_running = true;
return _listening;
}
void M2MConnectionHandlerPimpl::stop_listening()
{
tr_debug("M2MConnectionHandlerPimpl::stop_listening()");
_listening = false;
if(_security_impl) {
_security_impl->reset();
}
}
int M2MConnectionHandlerPimpl::send_to_socket(const unsigned char *buf, size_t len)
{
tr_debug("M2MConnectionHandlerPimpl::send_to_socket len - %d", len);
int size = -1;
if(is_tcp_connection()) {
size = ((TCPSocket*)_socket)->send(buf,len);
} else {
size = ((UDPSocket*)_socket)->sendto(*_socket_address,buf,len);
}
tr_debug("M2MConnectionHandlerPimpl::send_to_socket size - %d", size);
if(NSAPI_ERROR_WOULD_BLOCK == size){
if(_is_handshaking) {
return M2MConnectionHandler::CONNECTION_ERROR_WANTS_WRITE;
} else {
return len;
}
}else if(size < 0){
return -1;
}else{
if(!_is_handshaking) {
_observer.data_sent();
}
return size;
}
}
int M2MConnectionHandlerPimpl::receive_from_socket(unsigned char *buf, size_t len)
{
tr_debug("M2MConnectionHandlerPimpl::receive_from_socket");
int recv = -1;
if(is_tcp_connection()) {
recv = ((TCPSocket*)_socket)->recv(buf, len);
} else {
recv = ((UDPSocket*)_socket)->recvfrom(NULL,buf, len);
}
tr_debug("M2MConnectionHandlerPimpl::receive_from_socket recv size %d", recv);
if(NSAPI_ERROR_WOULD_BLOCK == recv){
return M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ;
}else if(recv < 0){
return -1;
}else{
return recv;
}
}
void M2MConnectionHandlerPimpl::handle_connection_error(int error)
{
tr_debug("M2MConnectionHandlerPimpl::handle_connection_error");
_observer.socket_error(error);
}
void M2MConnectionHandlerPimpl::set_platform_network_handler(void *handler)
{
tr_debug("M2MConnectionHandlerPimpl::set_platform_network_handler");
_net_iface = (NetworkInterface*)handler;
}
void M2MConnectionHandlerPimpl::receive_handshake_handler()
{
tr_debug("M2MConnectionHandlerPimpl::receive_handshake_handler()");
if( _is_handshaking ){
int ret = _security_impl->continue_connecting();
tr_debug("M2MConnectionHandlerPimpl::receive_handshake_handler() - ret %d", ret);
if( ret == M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ ){ //We wait for next readable event
tr_debug("M2MConnectionHandlerPimpl::receive_handshake_handler() - 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("M2MConnectionHandlerPimpl::receive_handler()");
memset(_recv_buffer, 0, 1024);
size_t receive_length = sizeof(_recv_buffer);
if(_listening) {
if( _use_secure_connection ){
int rcv_size = _security_impl->read(_recv_buffer, receive_length);
if(rcv_size >= 0){
_observer.data_available((uint8_t*)_recv_buffer,
rcv_size, _address);
} else if (M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ != rcv_size) {
_observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true);
close_socket();
return;
}
}else{
int recv = -1;
if(is_tcp_connection()){
recv = ((TCPSocket*)_socket)->recv(_recv_buffer, receive_length);
}else{
recv = ((UDPSocket*)_socket)->recvfrom(NULL,_recv_buffer, receive_length);
}
if (recv > 0) {
// Send data for processing.
if(is_tcp_connection()){
//We need to "shim" out the length from the front
if( receive_length > 4 ){
uint64_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) {
uint8_t* buf = (uint8_t*)malloc(len);
if(buf) {
memmove(buf, _recv_buffer+4, len);
// Observer for TCP plain mode
_observer.data_available(buf,len,_address);
free(buf);
}
}
}else{
_observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true);
close_socket();
}
} else { // Observer for UDP plain mode
tr_debug("M2MConnectionHandlerPimpl::receive_handler - data received %d", recv);
_observer.data_available((uint8_t*)_recv_buffer,
recv, _address);
}
} else if(NSAPI_ERROR_WOULD_BLOCK != recv) {
// Socket error in receiving
_observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true);
close_socket();
}
}
}
}
void M2MConnectionHandlerPimpl::claim_mutex()
{
eventOS_scheduler_mutex_wait();
}
void M2MConnectionHandlerPimpl::release_mutex()
{
eventOS_scheduler_mutex_release();
}
void M2MConnectionHandlerPimpl::init_socket()
{
tr_debug("M2MConnectionHandlerPimpl::init_socket - IN");
_is_handshaking = false;
_running = true;
if(is_tcp_connection()) {
tr_debug("M2MConnectionHandlerPimpl::init_socket - Using TCP");
_socket = new TCPSocket(_net_iface);
if(_socket) {
_socket->attach(this, &M2MConnectionHandlerPimpl::socket_event);
} else {
_observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
return;
}
} else {
tr_debug("M2MConnectionHandlerPimpl::init_socket - Using UDP - port %d", _listen_port);
_socket = new UDPSocket(_net_iface);
if(_socket) {
_socket->bind(_listen_port);
_socket->attach(this, &M2MConnectionHandlerPimpl::socket_event);
} else {
_observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
return;
}
}
_socket->set_blocking(false);
tr_debug("M2MConnectionHandlerPimpl::init_socket - OUT");
}
bool M2MConnectionHandlerPimpl::is_tcp_connection()
{
return _binding_mode == M2MInterface::TCP ||
_binding_mode == M2MInterface::TCP_QUEUE ? true : false;
}
void M2MConnectionHandlerPimpl::close_socket()
{
tr_debug("M2MConnectionHandlerPimpl::close_socket() - IN");
if(_socket) {
_running = false;
_socket->close();
delete _socket;
_socket = NULL;
}
tr_debug("M2MConnectionHandlerPimpl::close_socket() - OUT");
}
void M2MConnectionHandlerPimpl::enable_keepalive()
{
#if MBED_CLIENT_TCP_KEEPALIVE_TIME
if(is_tcp_connection()) {
int keepalive = MBED_CLIENT_TCP_KEEPALIVE_TIME;
int enable = 1;
tr_debug("M2MConnectionHandlerPimpl::resolve_hostname - keepalive %d s\n", keepalive);
if(_socket->setsockopt(1,NSAPI_KEEPALIVE,&enable,sizeof(enable)) != 0) {
tr_error("M2MConnectionHandlerPimpl::enable_keepalive - setsockopt fail to Set Keepalive\n");
}
if(_socket->setsockopt(1,NSAPI_KEEPINTVL,&keepalive,sizeof(keepalive)) != 0) {
tr_error("M2MConnectionHandlerPimpl::enable_keepalive - setsockopt fail to Set Keepalive TimeInterval\n");
}
if(_socket->setsockopt(1,NSAPI_KEEPIDLE,&keepalive,sizeof(keepalive)) != 0) {
tr_error("M2MConnectionHandlerPimpl::enable_keepalive - setsockopt fail to Set Keepalive Time\n");
}
}
#endif
}