1 /* ****************************************************************j
3 * Copyright 2015 Samsung Electronics All Rights Reserved.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 ******************************************************************/
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/ioctl.h>
26 #include <sys/types.h>
28 #include <sys/select.h>
29 #include <arpa/inet.h>
30 #include <netinet/in.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
41 #include "catcpinterface.h"
43 #include "caadapterutils.h"
45 #include "oic_malloc.h"
46 #include "oic_string.h"
49 * Logging tag for module name.
51 #define TAG "TCP_SERVER"
54 * Server port number for local test.
56 #define SERVER_PORT 8000
59 * Maximum CoAP over TCP header length
60 * to know the total data length.
62 #define TCP_MAX_HEADER_LEN 6
65 * Default Thread Counts in TCP adapter
67 #define CA_TCP_DEFAULT_THREAD_COUNTS 2
70 * Accept server file descriptor.
72 static int g_acceptServerFD = -1;
75 * Mutex to synchronize device object list.
77 static ca_mutex g_mutexObjectList = NULL;
80 * Conditional mutex to synchronize.
82 static ca_cond g_condObjectList = NULL;
85 * Maintains the current running thread counts.
87 static uint32_t g_threadCounts = CA_TCP_DEFAULT_THREAD_COUNTS;
90 * Maintains the callback to be notified when data received from remote device.
92 static CATCPPacketReceivedCallback g_packetReceivedCallback;
95 * Error callback to update error in TCP.
97 static CATCPErrorHandleCallback g_TCPErrorHandler = NULL;
99 static CAResult_t CATCPCreateMutex();
100 static void CATCPDestroyMutex();
101 static CAResult_t CATCPCreateCond();
102 static void CATCPDestroyCond();
103 static void CAAcceptHandler(void *data);
104 static void CAReceiveHandler(void *data);
105 static CAResult_t CAReceiveMessage();
106 static int CASetNonblocking(int fd);
107 static int CATCPCreateSocket(int family, CATCPServerInfo_t *TCPServerInfo);
108 static size_t CAGetTotalLengthFromHeader(const unsigned char *recvBuffer);
109 static void CATCPDisconnectAll();
111 static void CATCPDestroyMutex()
113 if (g_mutexObjectList)
115 ca_mutex_free(g_mutexObjectList);
116 g_mutexObjectList = NULL;
120 static CAResult_t CATCPCreateMutex()
122 if (!g_mutexObjectList)
124 g_mutexObjectList = ca_mutex_new();
125 if (!g_mutexObjectList)
127 OIC_LOG(ERROR, TAG, "Failed to created mutex!");
128 return CA_STATUS_FAILED;
135 static void CATCPDestroyCond()
137 if (g_condObjectList)
139 ca_cond_free(g_condObjectList);
140 g_condObjectList = NULL;
144 static CAResult_t CATCPCreateCond()
146 if (!g_condObjectList)
148 g_condObjectList = ca_cond_new();
149 if (!g_condObjectList)
151 OIC_LOG(ERROR, TAG, "Failed to created cond!");
152 return CA_STATUS_FAILED;
158 static void CATCPDisconnectAll()
160 OIC_LOG(DEBUG, TAG, "IN");
162 ca_mutex_lock(g_mutexObjectList);
163 uint32_t length = u_arraylist_length(caglobals.tcp.svrlist);
165 CATCPServerInfo_t *svritem = NULL;
166 for (size_t i = 0; i < length; i++)
168 svritem = (CATCPServerInfo_t *) u_arraylist_get(caglobals.tcp.svrlist, i);
169 if (svritem && svritem->u4tcp.fd >= 0)
171 shutdown(svritem->u4tcp.fd, SHUT_RDWR);
172 close(svritem->u4tcp.fd);
175 u_arraylist_destroy(caglobals.tcp.svrlist);
177 ca_mutex_unlock(g_mutexObjectList);
179 OIC_LOG(DEBUG, TAG, "OUT");
182 static void CAReceiveHandler(void *data)
185 OIC_LOG(DEBUG, TAG, "IN - CAReceiveHandler");
187 while (!caglobals.tcp.terminate)
192 ca_mutex_lock(g_mutexObjectList);
197 ca_cond_signal(g_condObjectList);
199 ca_mutex_unlock(g_mutexObjectList);
201 OIC_LOG(DEBUG, TAG, "OUT - CAReceiveHandler");
204 static size_t CAGetTotalLengthFromHeader(const unsigned char *recvBuffer)
206 OIC_LOG(DEBUG, TAG, "IN - CAGetTotalLengthFromHeader");
208 coap_transport_type transport = coap_get_tcp_header_type_from_initbyte(
209 ((unsigned char *)recvBuffer)[0] >> 4);
210 size_t optPaylaodLen = coap_get_length_from_header((unsigned char *)recvBuffer,
212 size_t headerLen = coap_get_tcp_header_length((unsigned char *)recvBuffer);
214 OIC_LOG_V(DEBUG, TAG, "option/paylaod length [%d]", optPaylaodLen);
215 OIC_LOG_V(DEBUG, TAG, "header length [%d]", headerLen);
216 OIC_LOG_V(DEBUG, TAG, "total data length [%d]", headerLen + optPaylaodLen);
218 OIC_LOG(DEBUG, TAG, "OUT - CAGetTotalLengthFromHeader");
219 return headerLen + optPaylaodLen;
222 static CAResult_t CAReceiveMessage()
224 uint32_t length = u_arraylist_length(caglobals.tcp.svrlist);
227 unsigned char *recvBuffer = NULL;
228 CATCPServerInfo_t *svritem = NULL;
229 for (i = 0; i < length; i++)
231 svritem = (CATCPServerInfo_t *) u_arraylist_get(caglobals.tcp.svrlist, i);
232 if (svritem->u4tcp.fd < 0)
237 size_t bufSize = TCP_MAX_HEADER_LEN;
238 recvBuffer = (unsigned char *) OICCalloc(1, bufSize);
241 OIC_LOG(ERROR, TAG, "out of memory");
245 bool isHeaderChecked = false;
247 size_t totalReceivedLen = 0;
250 ssize_t recvLen = recv(svritem->u4tcp.fd, recvBuffer + totalReceivedLen,
251 bufSize - totalReceivedLen, 0);
254 if(EWOULDBLOCK != errno)
256 OIC_LOG_V(ERROR, TAG, "Recvfrom failed %s", strerror(errno));
259 // if received data length is zero, we are breaking loop.
260 // because we use non-blocking socket to receive data from remote device.
261 if (!totalReceivedLen)
268 totalReceivedLen += recvLen;
269 if (!isHeaderChecked && totalReceivedLen)
271 coap_transport_type transport = coap_get_tcp_header_type_from_initbyte(
272 ((unsigned char *)recvBuffer)[0] >> 4);
273 size_t headerLen = coap_get_tcp_header_length_for_transport(transport);
274 if (totalReceivedLen >= headerLen)
276 // get actual data length from coap over tcp header
277 totalLen = CAGetTotalLengthFromHeader((unsigned char *) recvBuffer);
279 unsigned char *newBuf = OICRealloc(recvBuffer, bufSize);
282 OIC_LOG(ERROR, TAG, "out of memory");
286 isHeaderChecked = true;
289 if (totalLen == totalReceivedLen)
291 CAEndpoint_t ep = { .adapter = CA_ADAPTER_TCP,
292 .port = svritem->u4tcp.port };
293 strncpy(ep.addr, svritem->addr, sizeof(ep.addr));
295 if (g_packetReceivedCallback)
297 g_packetReceivedCallback(&ep, recvBuffer, totalLen);
299 OIC_LOG_V(DEBUG, TAG, "received data len:%d", totalLen);
302 } while (!totalLen || totalLen > totalReceivedLen);
310 ca_mutex_lock(g_mutexObjectList);
311 close(svritem->u4tcp.fd);
312 u_arraylist_remove(caglobals.tcp.svrlist, i);
313 ca_mutex_unlock(g_mutexObjectList);
315 return CA_STATUS_FAILED;
318 // TODO: resolving duplication.
319 static int CASetNonblocking(int fd)
321 int fl = fcntl(fd, F_GETFL);
324 OIC_LOG_V(ERROR, TAG, "Failed to get existing flags, Error code: %s",
327 else if ((fl & O_NONBLOCK) != O_NONBLOCK)
329 fl = fcntl(fd, F_SETFL, fl | O_NONBLOCK);
332 OIC_LOG_V(ERROR, TAG, "Failed to set non-blocking mode, Error code: %s",
340 static int CATCPCreateSocket(int family, CATCPServerInfo_t *TCPServerInfo)
343 int fd = socket(family, SOCK_STREAM, IPPROTO_TCP);
346 OIC_LOG_V(ERROR, TAG, "create socket failed: %s", strerror(errno));
350 // set non-blocking socket
351 if (-1 == CASetNonblocking(fd))
356 struct sockaddr_storage sa = { .ss_family = family };
357 CAConvertNameToAddr(TCPServerInfo->addr, TCPServerInfo->u4tcp.port, &sa);
358 socklen_t socklen = sizeof (struct sockaddr_in);
360 // connect to TCP server
361 int ret = connect(fd, (struct sockaddr *)&sa, socklen);
364 OIC_LOG(DEBUG, TAG, "connect socket success");
366 else if (EINPROGRESS == errno)
369 socklen_t len = sizeof(error);
370 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
372 OIC_LOG(ERROR, TAG, "getsockopt() error");
378 if (ECONNREFUSED == error)
380 OIC_LOG(ERROR, TAG, "connection refused");
383 OIC_LOG(ERROR, TAG, "failed to connect socket");
386 OIC_LOG(DEBUG, TAG, "connect socket success");
390 OIC_LOG(ERROR, TAG, "failed to connect socket");
404 static void CAAcceptHandler(void *data)
407 OIC_LOG(DEBUG, TAG, "IN - CAAcceptHandler");
410 struct sockaddr_in server = { .sin_addr.s_addr = INADDR_ANY,
411 .sin_family = AF_INET,
412 .sin_port = htons(SERVER_PORT) };
414 g_acceptServerFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
415 if (g_acceptServerFD < 0)
417 OIC_LOG(ERROR, TAG, "Failed to create socket");
421 if (-1 == setsockopt(g_acceptServerFD, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)))
423 OIC_LOG(ERROR, TAG, "setsockopt SO_REUSEADDR");
427 int serverlen = sizeof(server);
428 if (-1 == bind(g_acceptServerFD, (struct sockaddr *)&server, serverlen))
430 OIC_LOG(ERROR, TAG, "bind() error");
434 if (listen(g_acceptServerFD, caglobals.tcp.listenBacklog) != 0)
436 OIC_LOG(ERROR, TAG, "listen() error");
440 struct pollfd acceptServerFD = { .fd = g_acceptServerFD,
443 while (!caglobals.tcp.terminate)
445 int pollState = poll(&acceptServerFD, 1, caglobals.tcp.selectTimeout);
448 OIC_LOG_V(FATAL, TAG, "polling error %s", strerror(errno));
456 if (acceptServerFD.revents & POLLIN)
458 struct sockaddr_storage clientaddr;
459 socklen_t clientlen = sizeof (struct sockaddr_in);
461 int sockfd = accept(g_acceptServerFD, (struct sockaddr *)&clientaddr, &clientlen);
464 CATCPServerInfo_t *svritem = (CATCPServerInfo_t *) OICMalloc(sizeof (*svritem));
467 OIC_LOG(ERROR, TAG, "Out of memory");
472 // set non-blocking socket
473 if (-1 == CASetNonblocking(sockfd))
479 svritem->u4tcp.fd = sockfd;
481 CAConvertAddrToName((struct sockaddr_storage *)&clientaddr,
482 (char *) &svritem->addr, &svritem->u4tcp.port);
484 ca_mutex_lock(g_mutexObjectList);
485 bool res = u_arraylist_add(caglobals.tcp.svrlist, svritem);
488 OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
491 ca_mutex_unlock(g_mutexObjectList);
494 ca_mutex_unlock(g_mutexObjectList);
499 ca_mutex_lock(g_mutexObjectList);
504 ca_cond_signal(g_condObjectList);
506 ca_mutex_unlock(g_mutexObjectList);
508 OIC_LOG(DEBUG, TAG, "OUT - CAAcceptHandler");
511 if (g_acceptServerFD >= 0)
513 close(g_acceptServerFD);
515 ca_mutex_lock(g_mutexObjectList);
519 ca_cond_signal(g_condObjectList);
521 ca_mutex_unlock(g_mutexObjectList);
525 CAResult_t CATCPStartServer(const ca_thread_pool_t threadPool)
527 if (caglobals.tcp.started)
532 if (!caglobals.tcp.ipv4tcpenabled)
534 caglobals.tcp.ipv4tcpenabled = true; // only needed to run CA tests
537 CAResult_t res = CATCPCreateMutex();
538 if (CA_STATUS_OK != res)
540 OIC_LOG(ERROR, TAG, "failed to create mutex");
544 res = CATCPCreateCond();
545 if (CA_STATUS_OK != res)
547 OIC_LOG(ERROR, TAG, "failed to create cond");
551 ca_mutex_lock(g_mutexObjectList);
552 if (!caglobals.tcp.svrlist)
554 caglobals.tcp.svrlist = u_arraylist_create();
556 ca_mutex_unlock(g_mutexObjectList);
558 caglobals.tcp.terminate = false;
560 res = ca_thread_pool_add_task(threadPool, CAAcceptHandler, NULL);
561 if (CA_STATUS_OK != res)
563 OIC_LOG(ERROR, TAG, "thread_pool_add_task failed");
566 OIC_LOG(DEBUG, TAG, "CAAcceptHandler thread started successfully.");
568 res = ca_thread_pool_add_task(threadPool, CAReceiveHandler, NULL);
569 if (CA_STATUS_OK != res)
571 OIC_LOG(ERROR, TAG, "thread_pool_add_task failed");
574 OIC_LOG(DEBUG, TAG, "CAReceiveHandler thread started successfully.");
576 caglobals.tcp.started = true;
581 void CATCPStopServer()
583 OIC_LOG(DEBUG, TAG, "IN");
586 ca_mutex_lock(g_mutexObjectList);
588 // set terminate flag
589 caglobals.tcp.terminate = true;
591 ca_cond_wait(g_condObjectList, g_mutexObjectList);
594 ca_mutex_unlock(g_mutexObjectList);
596 if (-1 != g_acceptServerFD)
598 close(g_acceptServerFD);
599 g_acceptServerFD = -1;
602 CATCPDisconnectAll();
606 OIC_LOG(DEBUG, TAG, "OUT");
609 void CATCPSetPacketReceiveCallback(CATCPPacketReceivedCallback callback)
611 OIC_LOG(DEBUG, TAG, "IN");
613 g_packetReceivedCallback = callback;
615 OIC_LOG(DEBUG, TAG, "OUT");
618 static size_t CACheckPayloadLength(const void *data, size_t dlen)
620 coap_transport_type transport = coap_get_tcp_header_type_from_initbyte(
621 ((unsigned char *)data)[0] >> 4);
623 coap_pdu_t *pdu = coap_new_pdu(transport, dlen);
626 OIC_LOG(ERROR, TAG, "outpdu is null");
630 int ret = coap_pdu_parse((unsigned char *) data, dlen, pdu, transport);
633 OIC_LOG(ERROR, TAG, "pdu parse failed");
634 coap_delete_pdu(pdu);
638 size_t payloadLen = 0;
641 payloadLen = (unsigned char *) pdu->hdr + pdu->length - pdu->data;
648 static void sendData(const CAEndpoint_t *endpoint,
649 const void *data, size_t dlen)
651 // #1. get TCP Server object from list
653 CATCPServerInfo_t *svritem = CAGetTCPServerInfoFromList(endpoint->addr, endpoint->port,
657 // if there is no connection info, connect to TCP Server
658 svritem = CAConnectToTCPServer(endpoint);
661 OIC_LOG(ERROR, TAG, "Failed to create TCP server object");
662 g_TCPErrorHandler(endpoint, data, dlen, CA_SEND_FAILED);
667 // #2. check payload length
668 size_t payloadLen = CACheckPayloadLength(data, dlen);
669 // if payload length is zero, disconnect from TCP server
672 OIC_LOG(DEBUG, TAG, "payload length is zero, disconnect from remote device");
673 CADisconnectFromTCPServer(endpoint);
677 // #3. check connection state
678 if (svritem->u4tcp.fd < 0)
680 // if file descriptor value is wrong, remove TCP Server info from list
681 OIC_LOG(ERROR, TAG, "Failed to connect to TCP server");
682 CADisconnectFromTCPServer(endpoint);
683 g_TCPErrorHandler(endpoint, data, dlen, CA_SEND_FAILED);
687 // #4. send data to TCP Server
688 size_t remainLen = dlen;
691 size_t len = send(svritem->u4tcp.fd, data, remainLen, 0);
694 if (EWOULDBLOCK != errno)
696 OIC_LOG_V(ERROR, TAG, "unicast ipv4tcp sendTo failed: %s", strerror(errno));
697 g_TCPErrorHandler(endpoint, data, dlen, CA_SEND_FAILED);
704 } while (remainLen > 0);
706 OIC_LOG_V(INFO, TAG, "unicast ipv4tcp sendTo is successful: %d bytes", dlen);
709 void CATCPSendData(CAEndpoint_t *endpoint, const void *data, uint32_t datalen,
712 VERIFY_NON_NULL_VOID(endpoint, TAG, "endpoint is NULL");
713 VERIFY_NON_NULL_VOID(data, TAG, "data is NULL");
717 if (caglobals.tcp.ipv4tcpenabled && (endpoint->adapter & CA_ADAPTER_TCP))
719 sendData(endpoint, data, datalen);
724 CAResult_t CAGetTCPInterfaceInformation(CAEndpoint_t **info, uint32_t *size)
726 OIC_LOG(DEBUG, TAG, "IN");
728 VERIFY_NON_NULL(info, TAG, "info is NULL");
729 VERIFY_NON_NULL(size, TAG, "size is NULL");
731 return CA_NOT_SUPPORTED;
734 CATCPServerInfo_t *CAConnectToTCPServer(const CAEndpoint_t *TCPServerInfo)
736 VERIFY_NON_NULL_RET(TCPServerInfo, TAG, "TCPServerInfo is NULL", NULL);
738 // #1. create TCP server object
739 CATCPServerInfo_t *svritem = (CATCPServerInfo_t *) OICMalloc(sizeof (*svritem));
742 OIC_LOG(ERROR, TAG, "Out of memory");
745 memcpy(svritem->addr, TCPServerInfo->addr, sizeof(svritem->addr));
746 svritem->u4tcp.port = TCPServerInfo->port;
748 // #2. create the socket and connect to TCP server
749 if (caglobals.tcp.ipv4tcpenabled)
751 svritem->u4tcp.fd = CATCPCreateSocket(AF_INET, svritem);
752 if (-1 == svritem->u4tcp.fd)
759 // #3. add TCP connection info to list
760 ca_mutex_lock(g_mutexObjectList);
761 if (caglobals.tcp.svrlist)
763 bool res = u_arraylist_add(caglobals.tcp.svrlist, svritem);
766 OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
767 close(svritem->u4tcp.fd);
769 ca_mutex_unlock(g_mutexObjectList);
773 ca_mutex_unlock(g_mutexObjectList);
778 CAResult_t CADisconnectFromTCPServer(const CAEndpoint_t *TCPServerInfo)
780 VERIFY_NON_NULL(TCPServerInfo, TAG, "TCP server info is NULL");
782 // #1. get server info
784 ca_mutex_lock(g_mutexObjectList);
785 CATCPServerInfo_t *svritem = CAGetTCPServerInfoFromList(TCPServerInfo->addr,
790 OIC_LOG(ERROR, TAG, "there is no connection info");
791 ca_mutex_unlock(g_mutexObjectList);
792 return CA_STATUS_FAILED;
795 // #2. close the socket and remove TCP connection info in list
796 if (svritem->u4tcp.fd >= 0)
798 close(svritem->u4tcp.fd);
800 u_arraylist_remove(caglobals.tcp.svrlist, index);
801 ca_mutex_unlock(g_mutexObjectList);
806 CATCPServerInfo_t *CAGetTCPServerInfoFromList(const char *addr, const uint16_t port,
809 VERIFY_NON_NULL_RET(addr, TAG, "addr is NULL", NULL);
810 VERIFY_NON_NULL_RET(index, TAG, "index is NULL", NULL);
812 // get connection info from list
813 uint32_t length = u_arraylist_length(caglobals.tcp.svrlist);
815 for (size_t i = 0; i < length; i++)
817 CATCPServerInfo_t *svritem = (CATCPServerInfo_t *) u_arraylist_get(
818 caglobals.tcp.svrlist, i);
824 if (!strncmp(svritem->addr, addr, sizeof(svritem->addr))
825 && (svritem->u4tcp.port == port))
835 void CATCPSetErrorHandler(CATCPErrorHandleCallback errorHandleCallback)
837 g_TCPErrorHandler = errorHandleCallback;