Simple interface for Mbed Cloud Client

Dependents:  

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ftcd_comm_socket.cpp Source File

ftcd_comm_socket.cpp

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2016-2017 ARM Ltd.
00003 //
00004 // Licensed under the Apache License, Version 2.0 (the "License");
00005 // you may 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,
00012 // WITHOUT 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 // Note: this macro is needed on armcc to get the the PRI*32 macros
00018 // from inttypes.h in a C++ code.
00019 #ifndef __STDC_FORMAT_MACROS
00020 #define __STDC_FORMAT_MACROS
00021 #endif
00022 
00023 #include <stdlib.h>
00024 #include "pal.h"
00025 #include "pv_log.h"
00026 #include "ftcd_comm_socket.h"
00027 #include "fcc_malloc.h"
00028 
00029 #define NUM_OF_PENDING_CONNECTIONS 1
00030 #define NUM_OF_TRIES_TO_GET_INTERFACE_INFO 5
00031 #define TRACE_GROUP "fcsk"
00032 #define RANDOM_PORT_MIN 1024
00033 #define RANDOM_PORT_MAX 65535
00034 
00035 FtcdCommSocket::FtcdCommSocket(const void *interfaceHandler, ftcd_socket_domain_e domain, const uint16_t port_num, ftcd_comm_network_endianness_e network_endianness, int32_t timeout)
00036     : FtcdCommBase(network_endianness, NULL, false)
00037 {
00038     _interface_handler = interfaceHandler;
00039     _required_domain_type = domain;
00040     _port = port_num;
00041     _rcv_timeout = timeout;
00042     _current_domain_type = FTCD_AF_UNSPEC;
00043     _interface_index = 0;
00044     _net_interface_info = NULL;
00045     _server_socket = NULL;
00046     _client_socket = NULL;
00047 }
00048 
00049 FtcdCommSocket::FtcdCommSocket(const void *interfaceHandler, ftcd_socket_domain_e domain, const uint16_t port_num, ftcd_comm_network_endianness_e network_endianness, const uint8_t *header_token, bool use_signature, int32_t timeout)
00050     : FtcdCommBase(network_endianness,header_token, use_signature)
00051 
00052 {
00053     _interface_handler = interfaceHandler;
00054     _required_domain_type = domain;
00055     _port = port_num;
00056     _rcv_timeout = timeout;
00057     _current_domain_type = FTCD_AF_UNSPEC;
00058     _interface_index = 0;
00059     _net_interface_info = NULL;
00060     _server_socket = NULL;
00061     _client_socket = NULL;
00062 }
00063 
00064 FtcdCommSocket::~FtcdCommSocket()
00065 {
00066 
00067     if (_net_interface_info != NULL) {
00068         fcc_free(_net_interface_info);
00069     }
00070     if (_server_socket != NULL) {
00071         pal_close(&_server_socket);
00072     }
00073     if (_client_socket != NULL) {
00074         pal_close(&_client_socket);
00075     }
00076 }
00077 
00078 
00079 bool FtcdCommSocket::init()
00080 {
00081     int retries = NUM_OF_TRIES_TO_GET_INTERFACE_INFO;
00082     palIpV4Addr_t ip_v4_addr;
00083     char ip_and_port_string[32] = { 0 };
00084     uint32_t index = 0;
00085 
00086     //Call to pal init
00087     palStatus_t result = pal_init();
00088     if (result != PAL_SUCCESS) {
00089         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Error initializing pal");
00090         return false;
00091     }
00092 
00093     // If port is 0, generate random port
00094     // Pal currently does not support binding with port 0.
00095     if (_port == 0) {
00096         srand((unsigned int)pal_osKernelSysTick());
00097         // Generate random port int the range [RANDOM_PORT_MIN, RANDOM_PORT_MAX - 1] including.
00098         _port = (uint16_t)(rand() % (RANDOM_PORT_MAX - RANDOM_PORT_MIN) + RANDOM_PORT_MIN);
00099     }
00100 
00101     //Register connected interface handler
00102     result = pal_registerNetworkInterface((void*)_interface_handler, &_interface_index);
00103     if (result != 0) {
00104         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "\n pal_RegisterNetworkInterface Failed");
00105         return false;
00106     }
00107 
00108     //Allocate memory for interface info
00109     if (_net_interface_info == NULL) {
00110         _net_interface_info = (palNetInterfaceInfo_t *)fcc_malloc(sizeof(palNetInterfaceInfo_t ));
00111         if (_net_interface_info == NULL) {
00112             mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "\n Failed to allocate memory for network interface");
00113             return false;
00114         }
00115     }
00116 
00117     //Try to get interface info
00118     while (retries--) {
00119         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "\n Trying receive interface ...");
00120         result = pal_getNetInterfaceInfo(_interface_index, _net_interface_info);
00121         if (result != 0) {
00122             pal_osDelay(200);
00123         } else {//In case we have interface info we print it
00124             if (_required_domain_type != FTCD_IPV4) {
00125                 mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "\n Illegal domain type");
00126                 break;
00127             }
00128             //Update domain type
00129             _current_domain_type = _required_domain_type;
00130 
00131             result = pal_getSockAddrIPV4Addr(&(_net_interface_info->address), ip_v4_addr);
00132             if (result != PAL_SUCCESS) {
00133                 mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "\n palGetSockAddrIPV4Addr failed");
00134                 break;
00135             }
00136             memset(ip_and_port_string, 0, sizeof(ip_and_port_string));
00137             index = 0;
00138             for (uint32_t i = 0; i < sizeof(palIpV4Addr_t); i++) {
00139                 if (i < sizeof(palIpV4Addr_t) - 1) {
00140                     index += sprintf(&ip_and_port_string[index], "%d.", ip_v4_addr[i]);
00141                 } else {
00142                     index += sprintf(&ip_and_port_string[index], "%d:", ip_v4_addr[i]);
00143                     index += sprintf(&ip_and_port_string[index], "%d\n", _port);
00144                 }
00145             }
00146 
00147             mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "\n Factory Client IP Address and Port :  %s", ip_and_port_string);
00148             //open and listen to socket
00149             if (_listen()) {
00150                 return true;
00151             } else {
00152                 mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Failed to listen to socket");
00153             }
00154 
00155         }
00156 
00157     }
00158 
00159     mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "\n FCC did not succeed receive network interface !!!!!!");
00160     //If we couldn't get interface info free allocated memory
00161     fcc_free(_net_interface_info);
00162     _net_interface_info = NULL;
00163     return false;
00164 
00165 }
00166 
00167 
00168 void FtcdCommSocket::finish(void)
00169 {
00170     if (_server_socket != NULL) {
00171         pal_close(&_server_socket);
00172         _server_socket = NULL;
00173     }
00174     if (_client_socket != NULL) {
00175         pal_close(&_client_socket);
00176         _client_socket = NULL;
00177     }
00178     pal_destroy();
00179 }
00180 
00181 ftcd_comm_status_e FtcdCommSocket::wait_for_message(uint8_t **message_out, uint32_t *message_size_out)
00182 {
00183     int result = PAL_SUCCESS;
00184 
00185     palSocketLength_t addrlen = 0;
00186     palSocketAddress_t address = { 0 };
00187 
00188     // wait to accept connection
00189     result = pal_accept(_server_socket, &address, &addrlen, &_client_socket);
00190     if (result == PAL_ERR_SOCKET_WOULD_BLOCK ) {
00191         return FTCD_COMM_NETWORK_TIMEOUT;
00192     } else if (result != PAL_SUCCESS) {
00193         return FTCD_COMM_NETWORK_CONNECTION_ERROR;
00194     }
00195 
00196     return FtcdCommBase::wait_for_message(message_out, message_size_out);
00197 }
00198 
00199 ftcd_comm_status_e FtcdCommSocket::is_token_detected(void)
00200 {
00201     char c;
00202     int result = PAL_SUCCESS;
00203     size_t idx = 0;
00204 
00205     //read char by char to detect token
00206     while (idx < FTCD_MSG_HEADER_TOKEN_SIZE_BYTES) {
00207         result = _read_from_socket(reinterpret_cast<void*>(&c), 1);
00208         if (result == PAL_ERR_SOCKET_WOULD_BLOCK ) {
00209             return FTCD_COMM_NETWORK_TIMEOUT;
00210         } else if (result != PAL_SUCCESS) {
00211             return FTCD_COMM_NETWORK_CONNECTION_ERROR;
00212         }
00213 
00214         if (c == _header_token[idx]) {
00215             idx++;
00216         } else {
00217             idx = 0;
00218         }
00219     }
00220     return FTCD_COMM_STATUS_SUCCESS;
00221 }
00222 
00223 
00224 uint32_t FtcdCommSocket::read_message_size(void)
00225 {
00226     uint32_t message_size = 0;
00227     ftcd_comm_status_e result = FTCD_COMM_STATUS_SUCCESS;
00228 
00229     result = _read_from_socket(reinterpret_cast<void*>(&message_size), sizeof(message_size));
00230     if (result != FTCD_COMM_STATUS_SUCCESS) {
00231         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Failed reading message size");
00232         return 0;
00233     }
00234 
00235     return message_size;
00236 }
00237 
00238 bool FtcdCommSocket::read_message(uint8_t *message_out, size_t message_size)
00239 {
00240     ftcd_comm_status_e result = FTCD_COMM_STATUS_SUCCESS;
00241 
00242     if (message_out == NULL) {
00243         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Invalid message buffer");
00244         return false;
00245     }
00246 
00247     // Read CBOR message bytes
00248     // We assume that message_size is NOT bigger than INT_MAX
00249     result = _read_from_socket(reinterpret_cast<void*>(message_out), (int)message_size);
00250     if (result != FTCD_COMM_STATUS_SUCCESS) {
00251         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Failed reading message bytes");
00252         return false;
00253     }
00254     return true;
00255 }
00256 
00257 
00258 bool FtcdCommSocket::read_message_signature(uint8_t *sig, size_t sig_size)
00259 {
00260     ftcd_comm_status_e result = FTCD_COMM_STATUS_SUCCESS;
00261 
00262     if (sig == NULL) {
00263         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Invalid sig buffer");
00264         return false;
00265     }
00266 
00267     // Read signature from medium
00268     // We assume that sig_size is NOT bigger than INT_MAX
00269     result = _read_from_socket(reinterpret_cast<void*>(sig), (int)sig_size);
00270     if (result != FTCD_COMM_STATUS_SUCCESS) {
00271         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Failed reading message signature bytes");
00272         return false;
00273     }
00274     return true;
00275 }
00276 
00277 
00278 bool FtcdCommSocket::send(const uint8_t *data, uint32_t data_size)
00279 {
00280     bool success = true;
00281     palStatus_t result = PAL_SUCCESS;
00282     size_t sent_bytes = 0;
00283     size_t remaind_bytes = (size_t)data_size;
00284 
00285     do {
00286         result = pal_send(_client_socket, data, remaind_bytes, &sent_bytes);
00287         if (result != PAL_SUCCESS) {
00288             mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Failed pal_send");
00289             success = false;
00290             break;
00291         }
00292 
00293         if (sent_bytes == 0 || sent_bytes > remaind_bytes) {
00294             mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Sending response message failed");
00295             success = false;
00296             break;
00297         }
00298         remaind_bytes = remaind_bytes - sent_bytes;
00299         data += sent_bytes;
00300 
00301     } while (remaind_bytes != 0);
00302 
00303     return success;
00304 }
00305 
00306 bool FtcdCommSocket::_listen(void)
00307 {
00308     int status;
00309     palStatus_t result = PAL_SUCCESS;
00310     palSocketAddress_t address = { 0 };
00311     palIpV4Addr_t ipv4 = { 0 };
00312     int enable_reuseaddr = 1;
00313 
00314     //Check port number and domain type
00315     if (_port == 0) {
00316         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "\n Wrong port number");
00317         return false;
00318     }
00319 
00320     if (_current_domain_type != FTCD_IPV4) {
00321         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "\n Wrong domain type");
00322         return false;
00323     }
00324 
00325     //Open server and client sockets
00326     result = pal_socket((palSocketDomain_t )_current_domain_type, PAL_SOCK_STREAM_SERVER , false, _interface_index, &_server_socket);
00327     if (result != PAL_SUCCESS) {
00328         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "pal_socket failed");
00329         return false;
00330     }
00331 
00332     result = pal_socket((palSocketDomain_t )_current_domain_type, PAL_SOCK_STREAM, false, _interface_index, &_client_socket);
00333     if (result != PAL_SUCCESS) {
00334         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "pal_socket failed");
00335         return false;
00336     }
00337 
00338     status = pal_setSocketOptions(_server_socket, PAL_SO_REUSEADDR, &enable_reuseaddr, sizeof(enable_reuseaddr));
00339     if (status != 0) {
00340         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Failed to set SO_REUSEADDR (status %d)", status);
00341         return false;
00342     }
00343 
00344     //Get ipv4 format address from interface info structure
00345     status = pal_getSockAddrIPV4Addr(&(_net_interface_info->address), ipv4);
00346     if (status != 0) {
00347         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Cannot palGetSockAddrIPV4Addr (status %d)", status);
00348         return false;
00349     }
00350 
00351     //Set the retrieved address to pal socket address
00352     status = pal_setSockAddrIPV4Addr(&address, ipv4);
00353     if (status != 0) {
00354         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Cannot set socket ipv4 address (status %d)", status);
00355         return false;
00356     }
00357 
00358     //Set current port number to pal socket address
00359     status = pal_setSockAddrPort(&address, _port);
00360     if (status != 0) {
00361         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Cannot set socket port address (status %d)", status);
00362         return false;
00363     }
00364 
00365     //set server socket timeout
00366     if (_rcv_timeout >= 0) {
00367         status = pal_setSocketOptions(_server_socket, PAL_SO_RCVTIMEO , &_rcv_timeout, sizeof(_rcv_timeout));
00368         if (status != 0) {
00369             mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Cannot set server socket timeout (status %d)", status);
00370             return false;
00371         }
00372     }
00373 
00374     status = pal_bind(_server_socket, &address, _net_interface_info->addressSize);
00375     if (status != 0) {
00376         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "pal_bind failed (status %d)", status);
00377         return false;
00378     }
00379 
00380     status = pal_listen(_server_socket, NUM_OF_PENDING_CONNECTIONS);
00381     if (status != 0) {
00382         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "pal_listen failed (status %d)", status);
00383         return false;
00384     }
00385 
00386     mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Factory Client is waiting for incoming connection...");
00387 
00388     return true;
00389 }
00390 
00391 
00392 ftcd_comm_status_e FtcdCommSocket::_read_from_socket(void * data_out, int data_out_size)
00393 {
00394     int status = PAL_SUCCESS;
00395     size_t bytes_received = 0;
00396 
00397     if (data_out == NULL) {
00398         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Invalid message");
00399         return FTCD_COMM_INTERNAL_ERROR;
00400     }
00401 
00402     status = pal_recv(_client_socket, data_out, data_out_size, &bytes_received);
00403     if (status == PAL_ERR_SOCKET_WOULD_BLOCK ) {
00404         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Receive socket timeout, (status = %i)", status);
00405         return FTCD_COMM_NETWORK_TIMEOUT;
00406     } else if (status != PAL_SUCCESS) {
00407         mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Receive socket error, (status = %i)", status);
00408         return FTCD_COMM_NETWORK_CONNECTION_ERROR;
00409     }
00410 
00411     /* FIXME: The socket reads up to MSS (Max Segment Size) which is 1460 bytes.
00412     A bigger message would result in a partial message read. A temporary solution
00413     is to loop through until socket emptiness */
00414 
00415     // The number of bytes we had managed to read so far
00416     size_t bytes_read_successfully = bytes_received;
00417 
00418     // The remaining bytes left to read
00419     size_t remaining_bytes = data_out_size - bytes_received;
00420 
00421     // Loop through until nothing left to read
00422     // remaining_bytes will never be less than 0 since a max of remaining_bytes will be read into bytes_received,
00423     // and we do remaining_bytes -= bytes_received
00424     while (remaining_bytes != 0) {
00425 
00426         //Zero bytes_received before next receive
00427         bytes_received = 0;
00428 
00429         status = pal_recv(_client_socket, (uint8_t *)data_out + bytes_read_successfully, remaining_bytes, &bytes_received);
00430         if (status == PAL_ERR_SOCKET_WOULD_BLOCK ) {
00431             mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Receive socket timeout, (status = %i)", status);
00432             return FTCD_COMM_NETWORK_TIMEOUT;
00433         } else if (status != PAL_SUCCESS) {
00434             mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, "Receive socket error, (status = %i)", status);
00435             return FTCD_COMM_NETWORK_CONNECTION_ERROR;
00436         }
00437         // Should never happen - this means that pal_recv behaved incorrectly.
00438         if (bytes_received > remaining_bytes) {
00439             return FTCD_COMM_INTERNAL_ERROR;
00440         }
00441         remaining_bytes -= bytes_received;
00442         bytes_read_successfully += bytes_received;
00443     }
00444     return FTCD_COMM_STATUS_SUCCESS;
00445 }
00446 
00447