[IOT-1548] Fix to transfer a large size of data on CoAPs over TCP
authorJoonghwan Lee <jh05.lee@samsung.com>
Tue, 15 Nov 2016 08:25:49 +0000 (17:25 +0900)
committerRandeep Singh <randeep.s@samsung.com>
Mon, 28 Nov 2016 07:05:12 +0000 (07:05 +0000)
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 <jh05.lee@samsung.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/14351
Reviewed-by: Uze Choi <uzchoi@samsung.com>
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: Phil Coval <philippe.coval@osg.samsung.com>
Reviewed-by: Randeep Singh <randeep.s@samsung.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/14827

resource/csdk/connectivity/inc/catcpadapter.h
resource/csdk/connectivity/inc/catcpinterface.h
resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c
resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c
resource/csdk/connectivity/src/tcp_adapter/catcpserver.c

index f258711..c719214 100644 (file)
@@ -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;
 
index 3d48bc4..dbae713 100644 (file)
@@ -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
index c28a4a6..a755ce0 100644 (file)
@@ -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);
index 15bdf59..aa5a7d3 100644 (file)
@@ -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__
index 76527b0..b8b4fa0 100644 (file)
@@ -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);
+            }
         }
     }