From 86fc5768b7e8ee72483519d62d5e5693d8efe8d2 Mon Sep 17 00:00:00 2001 From: Joonghwan Lee Date: Tue, 15 Nov 2016 17:25:49 +0900 Subject: [PATCH] [IOT-1548] Fix to transfer a large size of data on CoAPs over TCP Fixed sending/receiving for large size of data which is bigger than 16kbytes on TLS. https://jira.iotivity.org/browse/IOT-1548 *Notice : This patchset necessarily requires https://gerrit.iotivity.org/gerrit/#/c/14325/. Change-Id: I1921d1d2d9d18acb921f093136457120ac862a2c Signed-off-by: Joonghwan Lee Reviewed-on: https://gerrit.iotivity.org/gerrit/14351 Reviewed-by: Uze Choi Tested-by: jenkins-iotivity Reviewed-by: Phil Coval Reviewed-by: Randeep Singh Reviewed-on: https://gerrit.iotivity.org/gerrit/14827 --- resource/csdk/connectivity/inc/catcpadapter.h | 4 +- resource/csdk/connectivity/inc/catcpinterface.h | 18 ++ .../src/adapter_util/ca_adapter_net_ssl.c | 55 ++++-- .../connectivity/src/tcp_adapter/catcpadapter.c | 45 +++++ .../connectivity/src/tcp_adapter/catcpserver.c | 202 ++++++++++++++++++++- 5 files changed, 300 insertions(+), 24 deletions(-) diff --git a/resource/csdk/connectivity/inc/catcpadapter.h b/resource/csdk/connectivity/inc/catcpadapter.h index f258711..c719214 100644 --- a/resource/csdk/connectivity/inc/catcpadapter.h +++ b/resource/csdk/connectivity/inc/catcpadapter.h @@ -53,7 +53,9 @@ typedef struct int fd; /**< file descriptor info */ unsigned char* data; /**< received data from remote device */ size_t len; /**< received data length */ - size_t totalLen; /**< total data length required to receive */ + size_t totalLen; /**< total coap data length required to receive */ + unsigned char tlsdata[18437]; /**< tls data(rfc5246: TLSCiphertext max (2^14+2048+5)) */ + size_t tlsLen; /**< received tls data length */ CAProtocol_t protocol; /**< application-level protocol */ } CATCPSessionInfo_t; diff --git a/resource/csdk/connectivity/inc/catcpinterface.h b/resource/csdk/connectivity/inc/catcpinterface.h index 3d48bc4..dbae713 100644 --- a/resource/csdk/connectivity/inc/catcpinterface.h +++ b/resource/csdk/connectivity/inc/catcpinterface.h @@ -232,6 +232,24 @@ CAResult_t CASearchAndDeleteTCPSession(const CAEndpoint_t *endpoint); */ size_t CACheckPayloadLengthFromHeader(const void *data, size_t dlen); +/** + * Construct CoAP header and payload from buffer + * + * @param[in] svritem - used socket, buffer, current received message length and protocol + * @param[in/out] data - data buffer, this value is updated as data is copied to svritem + * @param[in/out] dataLength - length of data, this value decreased as data is copied to svritem + * @return - CA_STATUS_OK or appropriate error code + */ +CAResult_t CAConstructCoAP(CATCPSessionInfo_t *svritem, unsigned char **data, + size_t *dataLength); + +/** + * Clean socket state data + * + * @param[in/out] svritem - socket state data + */ +void CACleanData(CATCPSessionInfo_t *svritem); + #ifdef __cplusplus } #endif diff --git a/resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c b/resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c index c28a4a6..a755ce0 100644 --- a/resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c +++ b/resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c @@ -38,6 +38,7 @@ #include "mbedtls/ctr_drbg.h" #include "mbedtls/pkcs12.h" #include "mbedtls/ssl_internal.h" +#include "mbedtls/net.h" #ifdef __WITH_DTLS__ #include "mbedtls/timing.h" #include "mbedtls/ssl_cookie.h" @@ -1536,15 +1537,29 @@ CAResult_t CAencryptSsl(const CAEndpoint_t *endpoint, if (MBEDTLS_SSL_HANDSHAKE_OVER == tep->ssl.state) { - ret = mbedtls_ssl_write(&tep->ssl, (unsigned char *) data, dataLen); + unsigned char *dataBuf = (unsigned char *)data; + size_t written = 0; - if(ret < 0) + do { - OIC_LOG_V(ERROR, NET_SSL_TAG, "mbedTLS write returned %d", ret); - RemovePeerFromList(&tep->sep.endpoint); - oc_mutex_unlock(g_sslContextMutex); - return CA_STATUS_FAILED; - } + ret = mbedtls_ssl_write(&tep->ssl, dataBuf, dataLen - written); + if (ret < 0) + { + if (MBEDTLS_ERR_SSL_WANT_WRITE != ret) + { + OIC_LOG_V(ERROR, NET_SSL_TAG, "mbedTLS write failed! returned 0x%x", -ret); + RemovePeerFromList(&tep->sep.endpoint); + oc_mutex_unlock(g_sslContextMutex); + return CA_STATUS_FAILED; + } + continue; + } + OIC_LOG_V(DEBUG, NET_SSL_TAG, "mbedTLS write returned with sent bytes[%d]", ret); + + dataBuf += ret; + written += ret; + } while (dataLen > written); + } else { @@ -1581,16 +1596,27 @@ static void SendCacheMessages(SslEndPoint_t * tep) SslCacheMessage_t * msg = (SslCacheMessage_t *) u_arraylist_get(tep->cacheList, listIndex); if (NULL != msg && NULL != msg->data && 0 != msg->len) { + unsigned char *dataBuf = (unsigned char *)msg->data; + size_t written = 0; + do { - ret = mbedtls_ssl_write(&tep->ssl, (unsigned char *) msg->data, msg->len); - } - while(MBEDTLS_ERR_SSL_WANT_WRITE == ret); + ret = mbedtls_ssl_write(&tep->ssl, dataBuf, msg->len - written); + if (ret < 0) + { + if (MBEDTLS_ERR_SSL_WANT_WRITE != ret) + { + OIC_LOG_V(ERROR, NET_SSL_TAG, "mbedTLS write failed! returned -0x%x", -ret); + break; + } + continue; + } + OIC_LOG_V(DEBUG, NET_SSL_TAG, "mbedTLS write returned with sent bytes[%d]", ret); + + dataBuf += ret; + written += ret; + } while (msg->len > written); - if(ret < 0) - { - OIC_LOG_V(ERROR, NET_SSL_TAG,"mbedTLS write returned %d", ret ); - } if (u_arraylist_remove(tep->cacheList, listIndex)) { DeleteCacheMessage(msg); @@ -1725,6 +1751,7 @@ CAResult_t CAdecryptSsl(const CASecureEndpoint_t *sep, uint8_t *data, uint32_t d if (NULL != uuidPos) { memcpy(uuid, (char*) uuidPos + sizeof(UUID_PREFIX) - 1, UUID_LENGTH * 2 + 4); + OIC_LOG_V(DEBUG, NET_SSL_TAG, "certificate uuid string: %s" , uuid); ret = OCConvertStringToUuid(uuid, peer->sep.identity.id); SSL_CHECK_FAIL(peer, ret, "Failed to convert subject", 1, CA_STATUS_FAILED, MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT); diff --git a/resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c b/resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c index 15bdf59..aa5a7d3 100644 --- a/resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c +++ b/resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c @@ -163,10 +163,55 @@ void CATCPPacketReceivedCB(const CASecureEndpoint_t *sep, const void *data, OIC_LOG_V(DEBUG, TAG, "Address: %s, port:%d", sep->endpoint.addr, sep->endpoint.port); +#ifdef SINGLE_THREAD if (g_networkPacketCallback) { g_networkPacketCallback(sep, data, dataLength); } +#else + unsigned char *buffer = (unsigned char*)data; + size_t bufferLen = dataLength; + size_t index = 0; + + //get remote device information from file descriptor. + CATCPSessionInfo_t *svritem = CAGetTCPSessionInfoFromEndpoint(&sep->endpoint, &index); + if (!svritem) + { + OIC_LOG(ERROR, TAG, "there is no connection information in list"); + return; + } + if (UNKNOWN == svritem->protocol) + { + OIC_LOG(ERROR, TAG, "invalid protocol type"); + return; + } + + //totalLen filled only when header fully read and parsed + while (0 != bufferLen) + { + CAResult_t res = CAConstructCoAP(svritem, &buffer, &bufferLen); + if (CA_STATUS_OK != res) + { + OIC_LOG_V(ERROR, TAG, "CAConstructCoAP return error : %d", res); + return; + } + + //when successfully read all required data - pass them to upper layer. + if (svritem->len == svritem->totalLen) + { + if (g_networkPacketCallback) + { + g_networkPacketCallback(sep, svritem->data, svritem->totalLen); + } + CACleanData(svritem); + } + else + { + OIC_LOG_V(DEBUG, TAG, "%u bytes required for complete CoAP", + svritem->totalLen - svritem->len); + } + } +#endif } #ifdef __WITH_TLS__ diff --git a/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c b/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c index 76527b0..b8b4fa0 100644 --- a/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c +++ b/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c @@ -400,19 +400,136 @@ static bool CAIsTlsMessage(const unsigned char* data, size_t length) * * @param[in/out] item - socket state data */ -static void CACleanData(CATCPSessionInfo_t *svritem) +void CACleanData(CATCPSessionInfo_t *svritem) { if (svritem) { OICFree(svritem->data); svritem->data = NULL; svritem->len = 0; + svritem->tlsLen = 0; svritem->totalLen = 0; svritem->protocol = UNKNOWN; } } /** + * Construct CoAP header and payload from buffer + * + * @param[in] svritem - used socket, buffer, current received message length and protocol + * @param[in/out] data - data buffer, this value is updated as data is copied to svritem + * @param[in/out] dataLength - length of data, this value decreased as data is copied to svritem + * @return - CA_STATUS_OK or appropriate error code + */ +CAResult_t CAConstructCoAP(CATCPSessionInfo_t *svritem, unsigned char **data, + size_t *dataLength) +{ + OIC_LOG_V(DEBUG, TAG, "In %s", __func__); + + if (NULL == svritem || NULL == data || NULL == dataLength) + { + OIC_LOG(ERROR, TAG, "Invalid input parameter(NULL)"); + return CA_STATUS_INVALID_PARAM; + } + + unsigned char *inBuffer = *data; + size_t inLen = *dataLength; + OIC_LOG_V(DEBUG, TAG, "before-datalength : %u", *dataLength); + + if (NULL == svritem->data && inLen > 0) + { + // allocate memory for message header (CoAP header size because it is bigger) + svritem->data = (unsigned char *) OICCalloc(1, COAP_MAX_HEADER_SIZE); + if (NULL == svritem->data) + { + OIC_LOG(ERROR, TAG, "OICCalloc - out of memory"); + return CA_MEMORY_ALLOC_FAILED; + } + + // copy 1 byte to parse coap header length + memcpy(svritem->data, inBuffer, 1); + svritem->len = 1; + inBuffer++; + inLen--; + } + + //if not enough data received - read them on next CAFillHeader() call + if (0 == inLen) + { + return CA_STATUS_OK; + } + + //if enough data received - parse header + svritem->protocol = COAP; + + //seems CoAP data received. read full coap header. + coap_transport_t transport = coap_get_tcp_header_type_from_initbyte(svritem->data[0] >> 4); + size_t headerLen = coap_get_tcp_header_length_for_transport(transport); + size_t copyLen = 0; + + // HEADER + if (svritem->len < headerLen) + { + copyLen = headerLen - svritem->len; + if (inLen < copyLen) + { + copyLen = inLen; + } + + //read required bytes to have full CoAP header + memcpy(svritem->data + svritem->len, inBuffer, copyLen); + svritem->len += copyLen; + inBuffer += copyLen; + inLen -= copyLen; + + //if not enough data received - read them on next CAFillHeader() call + if (svritem->len < headerLen) + { + *data = inBuffer; + *dataLength = inLen; + OIC_LOG(DEBUG, TAG, "CoAP header received partially. Wait for rest header data"); + return CA_STATUS_OK; + } + + //calculate CoAP message length + svritem->totalLen = CAGetTotalLengthFromHeader(svritem->data); + + // allocate required memory + unsigned char *buffer = OICRealloc(svritem->data, svritem->totalLen); + if (NULL == buffer) + { + OIC_LOG(ERROR, TAG, "OICRealloc - out of memory"); + return CA_MEMORY_ALLOC_FAILED; + } + svritem->data = buffer; + } + + // PAYLOAD + if (inLen > 0) + { + // read required bytes to have full CoAP payload + copyLen = svritem->totalLen - svritem->len; + if (inLen < copyLen) + { + copyLen = inLen; + } + + //read required bytes to have full CoAP header + memcpy(svritem->data + svritem->len, inBuffer, copyLen); + svritem->len += copyLen; + inBuffer += copyLen; + inLen -= copyLen; + } + + *data = inBuffer; + *dataLength = inLen; + + OIC_LOG_V(DEBUG, TAG, "after-datalength : %u", *dataLength); + OIC_LOG_V(DEBUG, TAG, "Out %s", __func__); + return CA_STATUS_OK; +} + +/** * Read message header from socket item->fd * * @param[in/out] item - used socket, buffer, current received message length and protocol @@ -580,20 +697,87 @@ static void CAReceiveMessage(int fd) return; } - //totalLen filled only when header fully read and parsed - if (0 == svritem->totalLen) + // read data + int len = 0; + + if (svritem->sep.endpoint.flags & CA_SECURE) { - res = CAReadHeader(svritem); + svritem->protocol = TLS; + +#ifdef __WITH_TLS__ + size_t nbRead = 0; + size_t tlsLength = 0; + + if (TLS_HEADER_SIZE > svritem->tlsLen) + { + nbRead = TLS_HEADER_SIZE - svritem->tlsLen; + } + else + { + //[3][4] bytes in tls header are tls payload length + tlsLength = TLS_HEADER_SIZE + + (size_t)((svritem->tlsdata[3] << 8) | svritem->tlsdata[4]); + OIC_LOG_V(DEBUG, TAG, "toal tls length = %u", tlsLength); + if (tlsLength > sizeof(svritem->tlsdata)) + { + OIC_LOG_V(ERROR, TAG, "toal tls length is too big (buffer size : %u)", + sizeof(svritem->tlsdata)); + return CA_STATUS_FAILED; + } + nbRead = tlsLength - svritem->tlsLen; + } + + len = recv(fd, svritem->tlsdata + svritem->tlsLen, nbRead, 0); + if (len < 0) + { + OIC_LOG_V(ERROR, TAG, "recv failed %s", strerror(errno)); + res = CA_RECEIVE_FAILED; + } + else if (0 == len) + { + OIC_LOG(INFO, TAG, "Received disconnect from peer. Close connection"); + res = CA_DESTINATION_DISCONNECTED; + } + else + { + svritem->tlsLen += len; + OIC_LOG_V(DEBUG, TAG, "nb_read : %u bytes , recv() : %d bytes, svritem->tlsLen : %u bytes", + nbRead, len, svritem->tlsLen); + if (tlsLength > 0 && tlsLength == svritem->tlsLen) + { + //when successfully read data - pass them to callback. + res = CAdecryptSsl(&svritem->sep, (uint8_t *)svritem->tlsdata, svritem->tlsLen); + svritem->tlsLen = 0; + OIC_LOG_V(DEBUG, TAG, "%s: CAdecryptSsl returned %d", __func__, res); + } + } +#endif + } else { - res = CAReadPayload(svritem); + unsigned char buffer[65535] = {0,}; // 65535 is the maximum size of ip packet + svritem->protocol = COAP; - //when successfully read all required data - pass them to upper layer. - if (CA_STATUS_OK == res && svritem->len == svritem->totalLen) + len = recv(fd, buffer, sizeof(buffer), 0); + if (len < 0) { - CAExecuteRequest(svritem); - CACleanData(svritem); + OIC_LOG_V(ERROR, TAG, "recv failed %s", strerror(errno)); + res = CA_RECEIVE_FAILED; + } + else if (0 == len) + { + OIC_LOG(INFO, TAG, "Received disconnect from peer. Close connection"); + res = CA_DESTINATION_DISCONNECTED; + } + else + { + OIC_LOG_V(DEBUG, TAG, "recv() : %d bytes", len); + //when successfully read data - pass them to callback. + if (g_packetReceivedCallback) + { + g_packetReceivedCallback(&svritem->sep, buffer, len); + } } } -- 2.7.4