Mbed Cloud example program for workshop in W27 2018.

Dependencies:   MMA7660 LM75B

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers m2mconnectionhandlerpimpl.cpp Source File

m2mconnectionhandlerpimpl.cpp

00001 /*
00002  * Copyright (c) 2015 - 2017 ARM Limited. All rights reserved.
00003  * SPDX-License-Identifier: Apache-2.0
00004  * Licensed under the Apache License, Version 2.0 (the License); you may
00005  * not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  * http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
00012  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 // fixup the compilation on ARMCC for PRIu32
00018 #define __STDC_FORMAT_MACROS
00019 #include <inttypes.h>
00020 
00021 #include "mbed-client-classic/m2mconnectionhandlerpimpl.h"
00022 #include "mbed-client/m2mconnectionobserver.h"
00023 #include "mbed-client/m2mconstants.h"
00024 #include "mbed-client/m2msecurity.h"
00025 #include "mbed-client/m2mconnectionhandler.h"
00026 
00027 #include "pal.h"
00028 
00029 #include "eventOS_scheduler.h"
00030 
00031 #include "eventOS_event_timer.h"
00032 
00033 #include "mbed-trace/mbed_trace.h"
00034 
00035 #include <stdlib.h> // free() and malloc()
00036 
00037 #define TRACE_GROUP "mClt"
00038 
00039 #ifndef MBED_CONF_MBED_CLIENT_TLS_MAX_RETRY
00040 #define MBED_CONF_MBED_CLIENT_TLS_MAX_RETRY 60
00041 #endif
00042 
00043 // TODO: remove this also, as it is platform specific and this default will not work on Linux at all.
00044 #ifndef MBED_CONF_MBED_CLIENT_DNS_THREAD_STACK_SIZE
00045 #define MBED_CONF_MBED_CLIENT_DNS_THREAD_STACK_SIZE 2048
00046 #endif
00047 
00048 
00049 int8_t M2MConnectionHandlerPimpl::_tasklet_id = -1;
00050 
00051 static M2MConnectionHandlerPimpl *connection_handler = NULL;
00052 
00053 // This is called from event loop, but as it is static C function, this is just a wrapper
00054 // which calls C++ on the instance.
00055 extern "C" void eventloop_event_handler(arm_event_s *event)
00056 {
00057     if (!connection_handler) {
00058         return;
00059     }
00060     connection_handler->event_handler(event);
00061 }
00062 
00063 // event handler that forwards the event according to its type and/or connection state
00064 void M2MConnectionHandlerPimpl::event_handler(arm_event_s *event)
00065 {
00066     switch (event->event_type) {
00067 
00068         // Event from socket callback method
00069         case M2MConnectionHandlerPimpl::ESocketCallback:
00070 
00071             // this will enable sending more events during this event processing, but that is less evil than missing one
00072             _suppressable_event_in_flight = false;
00073 
00074             if (_socket_state == M2MConnectionHandlerPimpl::ESocketStateHandshaking) {
00075                 receive_handshake_handler();
00076             } else if ((_socket_state == M2MConnectionHandlerPimpl::ESocketStateUnsecureConnection) ||
00077                        (_socket_state == M2MConnectionHandlerPimpl::ESocketStateSecureConnection)) {
00078                 // the connection is established
00079                 receive_handler();
00080             } else {
00081                 socket_connect_handler();
00082             }
00083 
00084             // Receive processing could have changed state, so recheck
00085             if ((_socket_state == M2MConnectionHandlerPimpl::ESocketStateUnsecureConnection) ||
00086                 (_socket_state == M2MConnectionHandlerPimpl::ESocketStateSecureConnection)) {
00087                 // the connection is established
00088                 send_socket_data();
00089             }
00090             break;
00091 
00092         // Data send request from client side
00093         case M2MConnectionHandlerPimpl::ESocketSend:
00094             send_socket_data();
00095             break;
00096 
00097         // DNS resolved successfully
00098         case M2MConnectionHandlerPimpl::ESocketDnsResolved:
00099             handle_dns_result(true);
00100             break;
00101 
00102             // DNS resolving failed
00103         case M2MConnectionHandlerPimpl::ESocketDnsError:
00104             handle_dns_result(false);
00105             break;
00106 
00107         // Establish the connection by connecting the socket
00108         case M2MConnectionHandlerPimpl::ESocketConnect:
00109             socket_connect_handler();
00110             break;
00111 
00112         case M2MConnectionHandlerPimpl::ESocketClose:
00113             close_socket();
00114             break;
00115 
00116         default:
00117             tr_debug("M2MConnectionHandlerPimpl::connection_event_handler: default type: %d", (int)event->event_type);
00118             break;
00119     }
00120 }
00121 
00122 // This callback is used from PAL sockets, it is called with object instance as argument.
00123 // This is received from "some" socket event from "some" socket and the C++ side is responsible
00124 // of forwarding it or ignoring the event completely.
00125 extern "C" void socket_event_handler(void* arg)
00126 {
00127     M2MConnectionHandlerPimpl* instance = (M2MConnectionHandlerPimpl*)arg;
00128 
00129     if (!instance) {
00130         tr_error("Invalid callback argument");
00131         return;
00132     }
00133 
00134     instance->send_socket_event(M2MConnectionHandlerPimpl::ESocketCallback);
00135 }
00136 
00137 void M2MConnectionHandlerPimpl::send_socket_event(SocketEvent event_type)
00138 {
00139     // the socket callback events can safely be suppressed, the receiving end must tolerate that
00140     if (event_type == ESocketCallback) {
00141         // only the socket connected state supports retries somehow
00142         if (_suppressable_event_in_flight == false) {
00143             _suppressable_event_in_flight = true;
00144         } else {
00145             // XXX: DO NOT ADD FOLLOWING LINE TO OFFICIAL GIT, THIS WILL KILL SOME NETWORK STACKS
00146             // IF EVENT IS SENT FROM A INTERRUPT CONTEXT
00147             // tr_debug("** SKIPPING event");
00148             return;
00149         }
00150     }
00151 
00152     if (!send_event(event_type)) {
00153         // TODO: give a proper error based on state instead of this
00154         _observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true);
00155     }
00156 }
00157 
00158 M2MConnectionHandlerPimpl::M2MConnectionHandlerPimpl(M2MConnectionHandler* base, M2MConnectionObserver &observer,
00159                                                      M2MConnectionSecurity* sec,
00160                                                      M2MInterface::BindingMode mode,
00161                                                      M2MInterface::NetworkStack stack)
00162 :_base(base),
00163  _observer(observer),
00164  _security_impl(sec),
00165  _security(NULL),
00166  _binding_mode(mode),
00167  _socket(0),
00168  _server_type(M2MConnectionObserver::LWM2MServer),
00169  _server_port(0),
00170  _listen_port(0),
00171  _net_iface(0),
00172 #ifdef MBED_CONF_MBED_CLIENT_DNS_USE_THREAD
00173  _dns_thread_id(0),
00174 #endif
00175  _socket_state(ESocketStateDisconnected),
00176  _handshake_retry(0),
00177  _suppressable_event_in_flight(false),
00178  _secure_connection(false)
00179 {
00180 #ifndef PAL_NET_TCP_AND_TLS_SUPPORT
00181     if (is_tcp_connection()) {
00182         tr_error("ConnectionHandler: TCP support not available.");
00183         return;
00184     }
00185 #endif
00186 
00187     if (PAL_SUCCESS != pal_init()) {
00188         tr_error("PAL init failed.");
00189     }
00190 
00191     memset(&_address, 0, sizeof _address);
00192     memset((void*)&_socket_address, 0, sizeof _socket_address);
00193     memset(&_ipV4Addr, 0, sizeof(palIpV4Addr_t));
00194     memset(&_ipV6Addr, 0, sizeof(palIpV6Addr_t));
00195     ns_list_init(&_linked_list_send_data);
00196 
00197     connection_handler = this;
00198     eventOS_scheduler_mutex_wait();
00199     if (M2MConnectionHandlerPimpl::_tasklet_id == -1) {
00200         M2MConnectionHandlerPimpl::_tasklet_id = eventOS_event_handler_create(&eventloop_event_handler, ESocketIdle);
00201     }
00202     eventOS_scheduler_mutex_release();
00203 }
00204 
00205 M2MConnectionHandlerPimpl::~M2MConnectionHandlerPimpl()
00206 {
00207     tr_debug("~M2MConnectionHandlerPimpl()");
00208 
00209     // terminate the DNS thread, if any is used
00210     terminate_dns_thread();
00211 
00212     close_socket();
00213     delete _security_impl;
00214     _security_impl = NULL;
00215     pal_destroy();
00216     tr_debug("~M2MConnectionHandlerPimpl() - OUT");
00217 }
00218 
00219 void M2MConnectionHandlerPimpl::terminate_dns_thread()
00220 {
00221 #ifdef MBED_CONF_MBED_CLIENT_DNS_USE_THREAD
00222     if (_dns_thread_id) {
00223         pal_osThreadTerminate(&_dns_thread_id);
00224         _dns_thread_id = 0;
00225     }
00226 #endif
00227 }
00228 
00229 
00230 bool M2MConnectionHandlerPimpl::bind_connection(const uint16_t listen_port)
00231 {
00232     _listen_port = listen_port;
00233     return true;
00234 }
00235 
00236 bool M2MConnectionHandlerPimpl::send_event(SocketEvent event_type)
00237 {
00238     arm_event_s event = {0};
00239 
00240     event.receiver = M2MConnectionHandlerPimpl::_tasklet_id;
00241     event.sender = 0;
00242     event.event_type = event_type;
00243     event.data_ptr = NULL;
00244     event.event_data = 0;
00245     event.priority = ARM_LIB_HIGH_PRIORITY_EVENT;
00246     return !eventOS_event_send(&event);
00247 }
00248 
00249 extern "C" void dns_thread(void const *connection_handler)
00250 {
00251     ((M2MConnectionHandlerPimpl*)connection_handler)->address_resolver();
00252 
00253     // Sleep until terminated.
00254     for (;;) {
00255         // This trace line is causing unexpected behaviour in Linux.To be investigated!
00256         //tr_debug("M2MConnectionHandlerPimpl::dns_thread() going to sleep..");
00257         pal_osDelay(0xFFFFFFFF);
00258     }
00259 }
00260 
00261 void M2MConnectionHandlerPimpl::address_resolver(void)
00262 {
00263     palStatus_t status;
00264     palSocketLength_t socket_address_len;
00265 
00266     status = pal_getAddressInfo(_server_address.c_str(), (palSocketAddress_t*)&_socket_address, &socket_address_len);
00267     if (PAL_SUCCESS != status) {
00268         tr_error("M2MConnectionHandlerPimpl::getAddressInfo failed with %d", (int)status);
00269         if (!send_event(ESocketDnsError)) {
00270             tr_error("M2MConnectionHandlerPimpl::address_resolver, error event alloc fail.");
00271         }
00272     } else {
00273         if (!send_event(ESocketDnsResolved)) {
00274             tr_error("M2MConnectionHandlerPimpl::address_resolver, resolved event alloc fail.");
00275         }
00276     }
00277 }
00278 
00279 void M2MConnectionHandlerPimpl::handle_dns_result(bool success)
00280 {
00281 
00282     if (_socket_state != ESocketStateDNSResolving) {
00283         tr_warn("M2MConnectionHandlerPimpl::handle_dns_result() called, not in ESocketStateDNSResolving state!");
00284         return;
00285     }
00286 
00287     // DNS thread no-longer needed.
00288     terminate_dns_thread();
00289 
00290     if (success) {
00291         _socket_state = EsocketStateInitializeConnection;
00292         socket_connect_handler();
00293 
00294     } else {
00295         _observer.socket_error(M2MConnectionHandler::DNS_RESOLVING_ERROR);
00296     }
00297 }
00298 
00299 bool M2MConnectionHandlerPimpl::resolve_server_address(const String& server_address,
00300                                                        const uint16_t server_port,
00301                                                        M2MConnectionObserver::ServerType server_type,
00302                                                        const M2MSecurity* security)
00303 {
00304     _socket_state = ESocketStateDNSResolving;
00305     _security = security;
00306 
00307     int32_t security_instance_id = _security->get_security_instance_id(M2MSecurity::M2MServer);
00308     if (server_type == M2MConnectionObserver::Bootstrap) {
00309         security_instance_id = _security->get_security_instance_id(M2MSecurity::Bootstrap);
00310     }
00311 
00312     if (_security &&
00313         security_instance_id >= 0 &&
00314         (_security->resource_value_int(M2MSecurity::SecurityMode, security_instance_id) == M2MSecurity::Certificate ||
00315          _security->resource_value_int(M2MSecurity::SecurityMode, security_instance_id) == M2MSecurity::Psk)) {
00316         _secure_connection = true;
00317     }
00318 
00319     _server_port = server_port;
00320     _server_type = server_type;
00321     _server_address = server_address;
00322 
00323 #ifdef MBED_CONF_MBED_CLIENT_DNS_USE_THREAD
00324 
00325     tr_debug("M2MConnectionHandlerPimpl::resolve_server_address: starting DNS thread");
00326 
00327     // terminate previous thread if it was still there
00328     terminate_dns_thread();
00329 
00330     // Try to create the DNS thread. If it fails, give an error.
00331     if (PAL_SUCCESS != pal_osThreadCreateWithAlloc(dns_thread, this, PAL_osPriorityAboveNormal, MBED_CONF_MBED_CLIENT_DNS_THREAD_STACK_SIZE, NULL, &_dns_thread_id)) {
00332         tr_error("M2MConnectionHandlerPimpl::dns_thread create failed.");
00333         _observer.socket_error(M2MConnectionHandler::DNS_RESOLVING_ERROR);
00334         return false;
00335     }
00336 #else
00337 
00338     tr_debug("M2MConnectionHandlerPimpl::resolve_server_address: synchronous DNS");
00339 
00340     address_resolver();
00341 
00342 #endif
00343     return true;
00344 }
00345 
00346 void M2MConnectionHandlerPimpl::socket_connect_handler()
00347 {
00348     palStatus_t status;
00349     int32_t security_instance_id = _security->get_security_instance_id(M2MSecurity::M2MServer);
00350     if (_server_type == M2MConnectionObserver::Bootstrap) {
00351         security_instance_id = _security->get_security_instance_id(M2MSecurity::Bootstrap);
00352     }
00353 
00354     tr_debug("M2MConnectionHandlerPimpl::socket_connect_handler - _socket_state = %d", _socket_state);
00355 
00356     switch (_socket_state) {
00357         case ESocketStateCloseBeingCalled:
00358         case ESocketStateDNSResolving:
00359         case ESocketStateDisconnected:
00360         case ESocketStateHandshaking:
00361         case ESocketStateUnsecureConnection:
00362         case ESocketStateSecureConnection:
00363             // Ignore these events
00364             break;
00365 
00366         case EsocketStateInitializeConnection:
00367 
00368             // Initialize the socket to stable state
00369             close_socket();
00370 
00371             status = pal_setSockAddrPort((palSocketAddress_t*)&_socket_address, _server_port);
00372 
00373             if (PAL_SUCCESS != status) {
00374                 tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - setSockAddrPort err: %d", (int)status);
00375             } else {
00376                 tr_debug("address family: %d", (int)_socket_address.addressType);
00377             }
00378 
00379             if (_socket_address.addressType == PAL_AF_INET) {
00380                 status = pal_getSockAddrIPV4Addr((palSocketAddress_t*)&_socket_address,_ipV4Addr);
00381                 if (PAL_SUCCESS != status) {
00382                     tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - sockAddr4, err: %d", (int)status);
00383                     _observer.socket_error(M2MConnectionHandler::DNS_RESOLVING_ERROR);
00384                     return;
00385                 }
00386 
00387                 tr_info("M2MConnectionHandlerPimpl::socket_connect_handler - IPv4 Address %d.%d.%d.%d",
00388                         _ipV4Addr[0], _ipV4Addr[1], _ipV4Addr[2], _ipV4Addr[3]);
00389 
00390                 _address._address = (void*)_ipV4Addr;
00391                 _address._length = PAL_IPV4_ADDRESS_SIZE;
00392                 _address._port = _server_port;
00393             } else if (_socket_address.addressType == PAL_AF_INET6 ) {
00394                 status = pal_getSockAddrIPV6Addr((palSocketAddress_t*)&_socket_address,_ipV6Addr);
00395                 if (PAL_SUCCESS != status) {
00396                     tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - sockAddr6, err: %d", (int)status);
00397                     _observer.socket_error(M2MConnectionHandler::DNS_RESOLVING_ERROR);
00398                     return;
00399                 }
00400 
00401                 tr_info("M2MConnectionHandlerPimpl::socket_connect_handler - IPv6 Address: %s", mbed_trace_ipv6(_ipV6Addr));
00402 
00403                 _address._address = (void*)_ipV6Addr;
00404                 _address._length = PAL_IPV6_ADDRESS_SIZE;
00405                 _address._port = _server_port;
00406             } else {
00407                 tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - socket config error, stack: %d", (int)_socket_address.addressType);
00408                 _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
00409                 return;
00410             }
00411 
00412             if (!init_socket()) {
00413                 tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - socket init error");
00414                 // The init_socket() calls the socket_error() -callback directly, so it must not be
00415                 // done here too.
00416                 return;
00417             }
00418 
00419             // This state was used to ignore the spurious events _during_ the call of non-blocking pal_connect().
00420             // Now that we just retry connect when it is not yet succeeded anyway this state might be removed completely.
00421             _socket_state = ESocketStateConnectBeingCalled;
00422 
00423         // fall through is intentional
00424         case ESocketStateConnectBeingCalled:
00425         case ESocketStateConnecting:
00426             if (is_tcp_connection()) {
00427 #ifdef PAL_NET_TCP_AND_TLS_SUPPORT
00428                 tr_info("M2MConnectionHandlerPimpl::socket_connect_handler - Using TCP");
00429 
00430                 status = pal_connect(_socket, (palSocketAddress_t*)&_socket_address, sizeof(_socket_address));
00431 
00432                 if ((status == PAL_ERR_SOCKET_IN_PROGRES ) || (status == PAL_ERR_SOCKET_WOULD_BLOCK )) {
00433                     // In this case the connect is done asynchronously, and the pal_socketMiniSelect()
00434                     // will be used to detect the end of connect.
00435                     tr_debug("M2MConnectionHandlerPimpl::socket_connect_handler - pal_connect(): %d, async connect started", (int)status);
00436                     // we need to wait for the event
00437                     _socket_state = ESocketStateConnecting;
00438                     break;
00439 
00440                 } else if (status == PAL_SUCCESS || status == PAL_ERR_SOCKET_ALREADY_CONNECTED ) {
00441 
00442                     tr_debug("M2MConnectionHandlerPimpl::socket_connect_handler - pal_connect(): success");
00443                     _socket_state = ESocketStateConnected;
00444 
00445                 } else {
00446                     tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - pal_connect(): failed: %d", (int)status);
00447                     close_socket();
00448                     _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
00449                     return;
00450                 }
00451 #else
00452                 tr_error("socket_connect_handler() - TCP not configured"
00453 #endif //PAL_NET_TCP_AND_TLS_SUPPORT
00454             } else {
00455                 tr_info("M2MConnectionHandlerPimpl::socket_connect_handler - Using UDP");
00456                 _socket_state = ESocketStateConnected;
00457             }
00458 
00459         // fall through is a normal flow in case the UDP was used or pal_connect() happened to return immediately with PAL_SUCCESS
00460         case ESocketStateConnected:
00461             if (_security && security_instance_id >= 0) {
00462                 if (_secure_connection) {
00463                     if ( _security_impl != NULL ) {
00464                         _security_impl->reset();
00465 
00466                         if (_security_impl->init(_security, security_instance_id) == 0) {
00467                             // Initiate handshake. Perhaps there could be a separate event type for this?
00468                             _socket_state = ESocketStateHandshaking;
00469                             send_socket_event(ESocketCallback);
00470                         } else {
00471                             tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - init failed");
00472                             close_socket();
00473                             _observer.socket_error(M2MConnectionHandler::SSL_CONNECTION_ERROR, true);
00474                             return;
00475                         }
00476                     } else {
00477                         tr_error("M2MConnectionHandlerPimpl::socket_connect_handler - sec is null");
00478                         close_socket();
00479                         _observer.socket_error(M2MConnectionHandler::SSL_CONNECTION_ERROR, true);
00480                         return;
00481                     }
00482                 }
00483             }
00484             if (_socket_state != ESocketStateHandshaking) {
00485                 _socket_state = ESocketStateUnsecureConnection;
00486                 enable_keepalive();
00487                 _observer.address_ready(_address,
00488                                         _server_type,
00489                                         _address._port);
00490             }
00491             break;
00492 
00493     }
00494 }
00495 
00496 bool M2MConnectionHandlerPimpl::send_data(uint8_t *data,
00497                                           uint16_t data_len,
00498                                           sn_nsdl_addr_s *address)
00499 {
00500     arm_event_s event = {0};
00501 
00502     if (address == NULL || data == NULL || !data_len || _socket_state < ESocketStateUnsecureConnection) {
00503         tr_warn("M2MConnectionHandlerPimpl::send_data() - too early");
00504         return false;
00505     }
00506 
00507     send_data_queue_s* out_data = (send_data_queue_s*)malloc(sizeof(send_data_queue_s));
00508     if (!out_data) {
00509         return false;
00510     }
00511 
00512     memset(out_data, 0, sizeof(send_data_queue_s));
00513 
00514     uint8_t offset = 0;
00515 #ifdef PAL_NET_TCP_AND_TLS_SUPPORT
00516     if (is_tcp_connection() && !_secure_connection ) {
00517         offset = 4;
00518     }
00519 #endif
00520 
00521     out_data->data = (uint8_t*)malloc(data_len + offset);
00522     if (!out_data->data) {
00523         free(out_data);
00524         return false;
00525     }
00526 
00527     // TCP non-secure
00528     // We need to "shim" the length in front
00529 #ifdef PAL_NET_TCP_AND_TLS_SUPPORT
00530     if (is_tcp_connection() && !_secure_connection ) {
00531         out_data->data[0] = 0;
00532         out_data->data[1] = 0;
00533         out_data->data[2] = (data_len >> 8 ) & 0xff;
00534         out_data->data[3] = data_len & 0xff;
00535     }
00536 #endif //PAL_NET_TCP_AND_TLS_SUPPORT
00537 
00538     memcpy(out_data->data + offset, data, data_len);
00539     out_data->data_len = data_len + offset;
00540 
00541     event.receiver = M2MConnectionHandlerPimpl::_tasklet_id;
00542     event.sender = 0;
00543     event.event_type = ESocketSend;
00544     event.priority = ARM_LIB_HIGH_PRIORITY_EVENT;
00545 
00546     claim_mutex();
00547     ns_list_add_to_end(&_linked_list_send_data, out_data);
00548     release_mutex();
00549 
00550     if (eventOS_event_send(&event) != 0) {
00551         // Event push failed, free the buffer
00552         claim_mutex();
00553         ns_list_remove(&_linked_list_send_data, out_data);
00554         release_mutex();
00555         free(out_data->data);
00556         free(out_data);
00557         return false;
00558     }
00559 
00560     return true;
00561 }
00562 
00563 void M2MConnectionHandlerPimpl::send_socket_data()
00564 {
00565     tr_debug("M2MConnectionHandlerPimpl::send_socket_data()");
00566     int bytes_sent = 0;
00567     bool success = true;
00568 
00569     send_data_queue_s* out_data = get_item_from_list();
00570     if (!out_data) {
00571         return;
00572     }
00573 
00574     if (!out_data->data || !out_data->data_len || _socket_state < ESocketStateUnsecureConnection) {
00575         tr_warn("M2MConnectionHandlerPimpl::send_socket_data() - too early");
00576         add_item_to_list(out_data);
00577         return;
00578     }
00579 
00580     // Loop until all the data is sent
00581     for (; out_data->offset < out_data->data_len; out_data->offset += bytes_sent) {
00582         // Secure send
00583         if (_socket_state == ESocketStateSecureConnection) {
00584             // TODO! Change the send_message API to take bytes_sent as a out param like the pal send API's.
00585             while ((bytes_sent = _security_impl->send_message(out_data->data + out_data->offset,
00586                                                             out_data->data_len - out_data->offset)) <= 0) {
00587                 if (bytes_sent == M2MConnectionHandler::CONNECTION_ERROR_WANTS_WRITE) {
00588                     // Return and wait the next event
00589                     add_item_to_list(out_data);
00590                     return;
00591                 }
00592 
00593                 if (bytes_sent != M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ) {
00594                     tr_error("M2MConnectionHandlerPimpl::send_socket_data() - secure, failed %d", bytes_sent);
00595                     success = false;
00596                     break;
00597                 }
00598             }
00599             if (!success) {
00600                 break;
00601             }
00602         }
00603         // Unsecure send
00604         else {
00605             bytes_sent = 0;
00606             palStatus_t ret;
00607             if (is_tcp_connection()) {
00608 #ifdef PAL_NET_TCP_AND_TLS_SUPPORT
00609                 ret = pal_send(_socket,
00610                                out_data->data + out_data->offset,
00611                                out_data->data_len - out_data->offset,
00612                                (size_t*)&bytes_sent);
00613 #endif
00614             } else {
00615                 ret = pal_sendTo(_socket,
00616                                  out_data->data + out_data->offset,
00617                                  out_data->data_len - out_data->offset,
00618                                  (palSocketAddress_t*)&_socket_address,
00619                                  sizeof(_socket_address),
00620                                  (size_t*)&bytes_sent);
00621             }
00622             if (ret == PAL_ERR_SOCKET_WOULD_BLOCK ) {
00623                 // Return and wait next event
00624                 add_item_to_list(out_data);
00625                 return;
00626             }
00627             if (ret < 0) {
00628                 tr_error("M2MConnectionHandlerPimpl::send_socket_data() - unsecure failed %d", (int)ret);
00629                 success = false;
00630                 break;
00631             }
00632         }
00633     }
00634 
00635     free(out_data->data);
00636     free(out_data);
00637 
00638     if (!success) {
00639         if (bytes_sent == M2MConnectionHandler::SSL_PEER_CLOSE_NOTIFY) {
00640             _observer.socket_error(M2MConnectionHandler::SSL_PEER_CLOSED, true);
00641         } else {
00642             tr_error("M2MConnectionHandlerPimpl::send_socket_data() - SOCKET_SEND_ERROR");
00643             _observer.socket_error(M2MConnectionHandler::SOCKET_SEND_ERROR, true);
00644         }
00645         close_socket();
00646     } else {
00647         _observer.data_sent();
00648     }
00649 }
00650 
00651 bool M2MConnectionHandlerPimpl::start_listening_for_data()
00652 {
00653     return true;
00654 }
00655 
00656 void M2MConnectionHandlerPimpl::stop_listening()
00657 {
00658     // Do not call close_socket() directly here.
00659     // This can be called from multiple locations.
00660     send_socket_event(ESocketClose);
00661 }
00662 
00663 void M2MConnectionHandlerPimpl::handle_connection_error(int error)
00664 {
00665     tr_error("M2MConnectionHandlerPimpl::handle_connection_error - error %d", error);
00666     _observer.socket_error(error);
00667 }
00668 
00669 void M2MConnectionHandlerPimpl::set_platform_network_handler(void *handler)
00670 {
00671     tr_debug("M2MConnectionHandlerPimpl::set_platform_network_handler");
00672     if (PAL_SUCCESS != pal_registerNetworkInterface(handler, &_net_iface)) {
00673         tr_error("M2MConnectionHandlerPimpl::set_platform_network_handler - Interface registration failed.");
00674     }
00675 }
00676 
00677 void M2MConnectionHandlerPimpl::receive_handshake_handler()
00678 {
00679     int return_value;
00680     tr_debug("M2MConnectionHandlerPimpl::receive_handshake_handler()");
00681 
00682     // assert(_socket_state == ESocketStateHandshaking);
00683 
00684     return_value = _security_impl->connect(_base);
00685 
00686     if (!return_value) {
00687 
00688         _handshake_retry = 0;
00689         _socket_state = ESocketStateSecureConnection;
00690         enable_keepalive();
00691         _observer.address_ready(_address,
00692                                 _server_type,
00693                                 _server_port);
00694 
00695     } else if (return_value == M2MConnectionHandler::SSL_PEER_CLOSE_NOTIFY) {
00696         _handshake_retry = 0;
00697         _observer.socket_error(M2MConnectionHandler::SSL_PEER_CLOSED, true);
00698         close_socket();
00699 
00700     } else if (return_value != M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ) {
00701 
00702         tr_error("M2MConnectionHandlerPimpl::receive_handshake_handler() - SSL_HANDSHAKE_ERROR");
00703         _handshake_retry = 0;
00704         _observer.socket_error(M2MConnectionHandler::SSL_HANDSHAKE_ERROR, true);
00705         close_socket();
00706 
00707     } else {
00708 
00709         if (_handshake_retry++ > MBED_CONF_MBED_CLIENT_TLS_MAX_RETRY) {
00710 
00711             tr_error("M2MConnectionHandlerPimpl::receive_handshake_handler() - Max TLS retry fail");
00712             _handshake_retry = 0;
00713             _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT, true);
00714             close_socket();
00715 
00716         }
00717         eventOS_event_timer_cancel(ESocketCallback, M2MConnectionHandlerPimpl::_tasklet_id);
00718         eventOS_event_timer_request(ESocketCallback, ESocketCallback, M2MConnectionHandlerPimpl::_tasklet_id, 1000);
00719 
00720     }
00721 }
00722 
00723 bool M2MConnectionHandlerPimpl::is_handshake_ongoing() const
00724 {
00725     return (_socket_state == ESocketStateHandshaking);
00726 }
00727 
00728 void M2MConnectionHandlerPimpl::receive_handler()
00729 {
00730     // assert(_socket_state > ESocketStateHandshaking);
00731 
00732     if (_socket_state == ESocketStateSecureConnection) {
00733 
00734         int rcv_size;
00735         unsigned char recv_buffer[BUFFER_LENGTH];
00736 
00737         // we need to read as much as there is data available as the events may or may not be suppressed
00738         do {
00739             tr_debug("M2MConnectionHandlerPimpl::receive_handler()..");
00740             rcv_size = _security_impl->read(recv_buffer, sizeof(recv_buffer));
00741             tr_debug("M2MConnectionHandlerPimpl::receive_handler() res: %d", rcv_size);
00742             if (rcv_size > 0) {
00743                 _observer.data_available((uint8_t*)recv_buffer,
00744                                          rcv_size, _address);
00745 
00746             } else if (M2MConnectionHandler::SSL_PEER_CLOSE_NOTIFY == rcv_size) {
00747                 _observer.socket_error(M2MConnectionHandler::SSL_PEER_CLOSED, true);
00748                 return;
00749             } else if (M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ != rcv_size && rcv_size < 0) {
00750                 tr_error("M2MConnectionHandlerPimpl::receive_handler() - secure SOCKET_READ_ERROR");
00751                 _observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true);
00752                 close_socket();
00753                 return;
00754             }
00755         } while (rcv_size > 0);
00756 
00757     } else {
00758         size_t recv;
00759         palStatus_t status;
00760         unsigned char recv_buffer[BUFFER_LENGTH];
00761         do {
00762             if (is_tcp_connection()) {
00763 #ifdef PAL_NET_TCP_AND_TLS_SUPPORT
00764                 status = pal_recv(_socket, recv_buffer, sizeof(recv_buffer), &recv);
00765 #endif //PAL_NET_TCP_AND_TLS_SUPPORT
00766             } else {
00767                 status = pal_receiveFrom(_socket, recv_buffer, sizeof(recv_buffer), NULL, NULL, &recv);
00768             }
00769 
00770             if (status == PAL_ERR_SOCKET_WOULD_BLOCK ) {
00771                 return;
00772             } else if (status != PAL_SUCCESS) {
00773                 tr_error("M2MConnectionHandlerPimpl::receive_handler() - SOCKET_READ_ERROR (%d)", (int)status);
00774                 _observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true);
00775                 close_socket();
00776                 return;
00777             }
00778 
00779             tr_debug("M2MConnectionHandlerPimpl::receive_handler() - data received, len: %zu", recv);
00780 
00781             if (!is_tcp_connection()) { // Observer for UDP plain mode
00782                 _observer.data_available((uint8_t*)recv_buffer, recv, _address);
00783             } else {
00784 #ifdef PAL_NET_TCP_AND_TLS_SUPPORT
00785                 if ( recv < 4 ) {
00786                     tr_error("M2MConnectionHandlerPimpl::receive_handler() - TCP SOCKET_READ_ERROR");
00787                     _observer.socket_error(M2MConnectionHandler::SOCKET_READ_ERROR, true);
00788                     close_socket();
00789                     return;
00790                 }
00791 
00792                 //We need to "shim" out the length from the front
00793                 uint32_t len = (recv_buffer[0] << 24 & 0xFF000000) + (recv_buffer[1] << 16 & 0xFF0000);
00794                 len += (recv_buffer[2] << 8 & 0xFF00) + (recv_buffer[3] & 0xFF);
00795                 if (len > 0 && len <= recv - 4) {
00796                     // Observer for TCP plain mode
00797                     _observer.data_available(recv_buffer + 4, len, _address);
00798                 }
00799 #endif //PAL_NET_TCP_AND_TLS_SUPPORT
00800             }
00801         } while (recv > 0);
00802     }
00803 }
00804 
00805 void M2MConnectionHandlerPimpl::claim_mutex()
00806 {
00807     eventOS_scheduler_mutex_wait();
00808 }
00809 
00810 void M2MConnectionHandlerPimpl::release_mutex()
00811 {
00812     eventOS_scheduler_mutex_release();
00813 }
00814 
00815 
00816 bool M2MConnectionHandlerPimpl::init_socket()
00817 {
00818     palSocketType_t  socket_type = PAL_SOCK_DGRAM ;
00819     palStatus_t status;
00820     palSocketAddress_t bind_address;
00821 
00822     palNetInterfaceInfo_t  interface_info;
00823     palIpV4Addr_t interface_address4;
00824     palIpV6Addr_t interface_address6;
00825 
00826     memset(&bind_address, 0, sizeof(palSocketAddress_t));
00827     memset(&interface_address4, 0, sizeof(interface_address4));
00828     memset(&interface_address6, 0, sizeof(interface_address6));
00829 
00830     if (is_tcp_connection()) {
00831 #ifdef PAL_NET_TCP_AND_TLS_SUPPORT
00832         socket_type = PAL_SOCK_STREAM;
00833 #else
00834         // Somebody has built code without TCP support but tries to use it.
00835         // Perhaps a "assert(false)" would be sufficient.
00836         tr_error("M2MConnectionHandlerPimpl::init_socket() - TCP config error");
00837         _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
00838         return;
00839 #endif //PAL_NET_TCP_AND_TLS_SUPPORT
00840     }
00841 
00842     uint32_t interface_count;
00843     pal_getNumberOfNetInterfaces(&interface_count);
00844     pal_getNetInterfaceInfo(_net_iface, &interface_info);
00845 
00846     status = pal_asynchronousSocketWithArgument((palSocketDomain_t )_socket_address.addressType,
00847                                                 socket_type, true, _net_iface, &socket_event_handler,
00848                                                 this, &_socket);
00849 
00850     if (PAL_SUCCESS != status) {
00851         tr_error("M2MConnectionHandlerPimpl::init_socket() - socket create error : %d", (int)status);
00852         _observer.socket_error(M2MConnectionHandler::SOCKET_ABORT);
00853         return false;
00854     }
00855 
00856     if (_socket_address.addressType == PAL_AF_INET) {
00857         status = pal_setSockAddrIPV4Addr(&bind_address, interface_address4);
00858     } else if (_socket_address.addressType == PAL_AF_INET6 ) {
00859         status = pal_setSockAddrIPV6Addr(&bind_address, interface_address6);
00860     } else {
00861         tr_warn("M2MConnectionHandlerPimpl::init_socket() - stack type: %d", (int)_socket_address.addressType);
00862     }
00863     if (PAL_SUCCESS != status) {
00864         tr_error("M2MConnectionHandlerPimpl::init_socket - setSockAddrIPV err: %d", (int)status);
00865         return false;
00866     }
00867     status = pal_setSockAddrPort(&bind_address, _listen_port);
00868     if (PAL_SUCCESS != status) {
00869         tr_error("M2MConnectionHandlerPimpl::init_socket - setSockAddrPort err: %d", (int)status);
00870         return false;
00871     }
00872     pal_bind(_socket, &bind_address, sizeof(bind_address));
00873 
00874     _security_impl->set_socket(_socket, (palSocketAddress_t*)&_socket_address);
00875 
00876     return true;
00877 }
00878 
00879 bool M2MConnectionHandlerPimpl::is_tcp_connection() const
00880 {
00881     return ( _binding_mode == M2MInterface::TCP ||
00882              _binding_mode == M2MInterface::TCP_QUEUE );
00883 }
00884 
00885 void M2MConnectionHandlerPimpl::close_socket()
00886 {
00887     _suppressable_event_in_flight = false;
00888 
00889     terminate_dns_thread();
00890 
00891     if (_socket) {
00892         // At least on mbed-os the pal_close() will perform callbacks even during it
00893         // is called, which we will ignore when this state is set.
00894         _socket_state = ESocketStateCloseBeingCalled;
00895         pal_close(&_socket);
00896         _socket = 0;
00897     }
00898 
00899     // make sure the socket connection statemachine is reset too.
00900     _socket_state = ESocketStateDisconnected;
00901 
00902     if (_security_impl) {
00903         _security_impl->reset();
00904     }
00905 
00906     claim_mutex();
00907     /*ns_list_foreach_safe(M2MConnectionHandlerPimpl::send_data_queue_s, tmp, &_linked_list_send_data) {
00908         ns_list_remove(&_linked_list_send_data, tmp);
00909         free(tmp->data);
00910         free(tmp);
00911     }*/
00912     // Workaround for IAR compilation issue. ns_list_foreach does not compile with IAR.
00913     // Error[Pe144]: a value of type "void *" cannot be used to initialize an entity of type "M2MConnectionHandlerPimpl::send_data_queue *"
00914     while (!ns_list_is_empty(&_linked_list_send_data)) {
00915         send_data_queue_s* data = (send_data_queue_s*)ns_list_get_first(&_linked_list_send_data);
00916         ns_list_remove(&_linked_list_send_data, data);
00917         free(data->data);
00918         free(data);
00919     }
00920     release_mutex();
00921 }
00922 
00923 void M2MConnectionHandlerPimpl::enable_keepalive()
00924 {
00925 #if MBED_CLIENT_TCP_KEEPALIVE_TIME
00926 #ifdef PAL_NET_TCP_AND_TLS_SUPPORT
00927     palStatus_t status;
00928     if (is_tcp_connection()) {
00929         int enable = 1;
00930         status = pal_setSocketOptions(_socket, PAL_SO_KEEPALIVE , &enable, sizeof(enable));
00931         if (PAL_SUCCESS != status) {
00932             tr_error("M2MConnectionHandlerPimpl::enable_keepalive - PAL_SO_KEEPALIVE err: %d", (int)status);
00933         }
00934 
00935         int idle_period = MBED_CLIENT_TCP_KEEPALIVE_TIME;
00936         tr_info("M2MConnectionHandlerPimpl::enable_keepalive - PAL_SO_KEEPIDLE %d", idle_period);
00937         status = pal_setSocketOptions(_socket, PAL_SO_KEEPIDLE , &idle_period, sizeof(idle_period));
00938         if (PAL_SUCCESS != status) {
00939             tr_error("M2MConnectionHandlerPimpl::enable_keepalive - PAL_SO_KEEPIDLE err: %d", (int)status);
00940         }
00941 
00942         int intvl = MBED_CLIENT_TCP_KEEPALIVE_INTERVAL;
00943         status = pal_setSocketOptions(_socket, PAL_SO_KEEPINTVL , &intvl, sizeof(intvl));
00944         if (PAL_SUCCESS != status) {
00945             tr_error("M2MConnectionHandlerPimpl::enable_keepalive - PAL_SO_KEEPINTVL err: %d", (int)status);
00946         }
00947     }
00948 #endif
00949 #endif
00950 }
00951 
00952 M2MConnectionHandlerPimpl::send_data_queue_s* M2MConnectionHandlerPimpl::get_item_from_list()
00953 {
00954     claim_mutex();
00955     send_data_queue_s* out_data = (send_data_queue_s*)ns_list_get_first(&_linked_list_send_data);
00956     if (out_data) {
00957         ns_list_remove(&_linked_list_send_data, out_data);
00958     }
00959     release_mutex();
00960     return out_data;
00961 }
00962 
00963 void M2MConnectionHandlerPimpl::add_item_to_list(M2MConnectionHandlerPimpl::send_data_queue_s *data)
00964 {
00965     claim_mutex();
00966     ns_list_add_to_start(&_linked_list_send_data, data);
00967     release_mutex();
00968 }