sandbox / mbed-client-classic

Fork of mbed-client-classic by Christopher Haster

source/m2mconnectionhandlerpimpl.cpp

Committer:
Christopher Haster
Date:
2016-01-22
Revision:
1:2dc916e504c9
Child:
2:c3434146c3d2

File content as of revision 1:2dc916e504c9:

/*
 * 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 "Socket/TCPSocketConnection.h"
#include "Socket/UDPSocket.h"
#include "threadwrapper.h"
#include "mbed_error.h"


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),
 _resolved(true),
 _is_handshaking(false),
 _listening(false),
 _reading_thread(0)
{
    memset(&_address_buffer, 0, sizeof _address_buffer);
    memset(&_address, 0, sizeof _address);
    _address._address = _address_buffer;

    if (_network_stack != M2MInterface::LwIP_IPv4) {
        error("ConnectionHandler: Unsupported network stack, only IPv4 is currently supported");
    }
    
    if (_binding_mode == M2MInterface::TCP ||
        _binding_mode == M2MInterface::TCP_QUEUE) {
        error("ConnectionHandler: Unsupported binding mode, only UDP based modes are currently supported");
    }
}

M2MConnectionHandlerPimpl::~M2MConnectionHandlerPimpl()
{
    stop_listening();
    delete _security_impl;
}

bool M2MConnectionHandlerPimpl::bind_connection(const uint16_t listen_port)
{
    return !(_socket.bind(listen_port) < 0);
}

bool M2MConnectionHandlerPimpl::resolve_server_address(const String& server_address,
                                                       const uint16_t server_port,
                                                       M2MConnectionObserver::ServerType server_type,
                                                       const M2MSecurity* security)
{
    if (_endpoint.set_address(server_address.c_str(), server_port) < 0) {
        return false;
    }

    inet_aton(_endpoint.get_address(), _address._address);
    _address._port = _endpoint.get_port();
    _address._length = 4;
    _address._stack = _network_stack;

    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();
                _security_impl->init(security);
                
                if (_security_impl->connect(_base) < 0) {
                    return false;
                }
                
                _use_secure_connection = true;
            }
        }
    }
    
    _observer.address_ready(_address,
                            server_type,
                            _address._port);
    
    return true;
}

bool M2MConnectionHandlerPimpl::send_data(uint8_t *data,
                                          uint16_t data_len,
                                          sn_nsdl_addr_s *address)
{
    if (address == NULL || data == NULL) {
        return false;
    }
    
    if (_use_secure_connection) {
        if (_security_impl->send_message(data, data_len) < 0) {
            return false;
        }
    } else {
        if (_socket.sendTo(_endpoint, (char*)data, data_len) < 0) {
            return false;
        }
    }
    
    _observer.data_sent();
    return true;
}

bool M2MConnectionHandlerPimpl::start_listening_for_data()
{
    _listening = true;
    _reading_thread = create_thread<M2MConnectionHandlerPimpl,
                                   &M2MConnectionHandlerPimpl::receive_data>(this);
    return true;
}

void M2MConnectionHandlerPimpl::stop_listening()
{
    if (_reading_thread) {
        _listening = false;
        delete _reading_thread;
        _reading_thread = 0;
    }
}

void M2MConnectionHandlerPimpl::receive_data()
{
    while (_listening) {
        if (_use_secure_connection) {
            memset(_receive_buffer, 0, sizeof _receive_buffer);

            int size = _security_impl->read(_receive_buffer, sizeof _receive_buffer);
            if (size > 0) {
                _observer.data_available((uint8_t*)_receive_buffer,
                                         size, _address);
            } else if (size != 0 && size != M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ) {
                _listening = false;
                _observer.socket_error(1);
            }
        } else {
            memset(_receive_buffer, 0, sizeof _receive_buffer);

            int size = _socket.receiveFrom(_endpoint, (char*)_receive_buffer, sizeof _receive_buffer);
            if (size > 0) {
                _observer.data_available((uint8_t*)_receive_buffer,
                                         size, _address);
            } else if (size != 0) {
                _listening = false;
                _observer.socket_error(2);
            }
        }
    }
}


int M2MConnectionHandlerPimpl::send_to_socket(const unsigned char *buf, size_t len)
{
    int size = _socket.sendTo(_endpoint, (char*)buf, len);
    
    if (size == 0) {
        return M2MConnectionHandler::CONNECTION_ERROR_WANTS_WRITE;
    } else {
        return size;
    }
}

int M2MConnectionHandlerPimpl::receive_from_socket(unsigned char *buf, size_t len)
{
    Endpoint recv_endpoint;
    int size = _socket.receiveFrom(recv_endpoint, (char*)buf, len);
    
    if (size == 0) {
        return M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ;
    } else {
        return size;
    }
}

void M2MConnectionHandlerPimpl::handle_connection_error(int /*error*/)
{
    _observer.socket_error(4);
}