#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/select.h>
#include <sys/ioctl.h>
+#include <sys/poll.h>
#include <stdio.h>
#include <unistd.h>
-#include <sys/types.h>
#include <fcntl.h>
-#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>
#include <errno.h>
-#include <sys/poll.h>
#ifndef WITH_ARDUINO
#include <sys/socket.h>
#define TCP_MAX_HEADER_LEN 6
/**
- * Accept server file descriptor.
- */
-static int g_acceptServerFD = -1;
-
-/**
* Mutex to synchronize device object list.
*/
static ca_mutex g_mutexObjectList = NULL;
static void CATCPDestroyMutex();
static CAResult_t CATCPCreateCond();
static void CATCPDestroyCond();
-static CAResult_t CACreateAcceptSocket();
-static void CAAcceptConnection();
+static int CACreateAcceptSocket(int family, CASocket_t *sock);
+static void CAAcceptConnection(CATransportFlags_t flag, CASocket_t *sock);
static void CAFindReadyMessage();
static void CASelectReturned(fd_set *readFds, int ret);
static void CAReceiveMessage(int fd);
FD_ZERO(&readFds);
- if (-1 != g_acceptServerFD)
+ if (-1 != caglobals.tcp.ipv4.fd)
+ {
+ FD_SET(caglobals.tcp.ipv4.fd, &readFds);
+ }
+ if (-1 != caglobals.tcp.ipv6.fd)
{
- FD_SET(g_acceptServerFD, &readFds);
+ FD_SET(caglobals.tcp.ipv6.fd, &readFds);
}
if (-1 != caglobals.tcp.shutdownFds[0])
{
static void CASelectReturned(fd_set *readFds, int ret)
{
- (void)ret;
+ VERIFY_NON_NULL_VOID(readFds, TAG, "readFds is NULL");
- if (g_acceptServerFD != -1 && FD_ISSET(g_acceptServerFD, readFds))
+ if (caglobals.tcp.ipv4.fd != -1 && FD_ISSET(caglobals.tcp.ipv4.fd, readFds))
+ {
+ CAAcceptConnection(CA_IPV4, &caglobals.tcp.ipv4);
+ return;
+ }
+ else if (caglobals.tcp.ipv6.fd != -1 && FD_ISSET(caglobals.tcp.ipv6.fd, readFds))
{
- CAAcceptConnection();
+ CAAcceptConnection(CA_IPV6, &caglobals.tcp.ipv6);
return;
}
else if (-1 != caglobals.tcp.connectionFds[0] &&
}
}
-static void CAAcceptConnection()
+static void CAAcceptConnection(CATransportFlags_t flag, CASocket_t *sock)
{
+ VERIFY_NON_NULL_VOID(sock, TAG, "sock is NULL");
+
struct sockaddr_storage clientaddr;
socklen_t clientlen = sizeof (struct sockaddr_in);
- int sockfd = accept(g_acceptServerFD, (struct sockaddr *)&clientaddr,
- &clientlen);
+ int sockfd = accept(sock->fd, (struct sockaddr *)&clientaddr, &clientlen);
if (-1 != sockfd)
{
CATCPSessionInfo_t *svritem =
}
svritem->fd = sockfd;
+ svritem->sep.endpoint.flags = flag;
CAConvertAddrToName((struct sockaddr_storage *)&clientaddr, clientlen,
(char *) &svritem->sep.endpoint.addr, &svritem->sep.endpoint.port);
}
}
+static CAResult_t CATCPConvertNameToAddr(int family, const char *host, uint16_t port,
+ struct sockaddr_storage *sockaddr)
+{
+ struct addrinfo *addrs = NULL;
+ struct addrinfo hints = { .ai_family = family,
+ .ai_protocol = IPPROTO_TCP,
+ .ai_socktype = SOCK_STREAM,
+ .ai_flags = AI_NUMERICHOST };
+
+ int r = getaddrinfo(host, NULL, &hints, &addrs);
+ if (r)
+ {
+ if (EAI_SYSTEM == r)
+ {
+ OIC_LOG_V(ERROR, TAG, "getaddrinfo failed: errno %s", strerror(errno));
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "getaddrinfo failed: %s", gai_strerror(r));
+ }
+ freeaddrinfo(addrs);
+ return CA_STATUS_FAILED;
+ }
+ // assumption: in this case, getaddrinfo will only return one addrinfo
+ // or first is the one we want.
+ if (addrs[0].ai_family == AF_INET6)
+ {
+ memcpy(sockaddr, addrs[0].ai_addr, sizeof (struct sockaddr_in6));
+ ((struct sockaddr_in6 *)sockaddr)->sin6_port = htons(port);
+ }
+ else
+ {
+ memcpy(sockaddr, addrs[0].ai_addr, sizeof (struct sockaddr_in));
+ ((struct sockaddr_in *)sockaddr)->sin_port = htons(port);
+ }
+ freeaddrinfo(addrs);
+ return CA_STATUS_OK;
+}
+
static int CATCPCreateSocket(int family, CATCPSessionInfo_t *svritem)
{
- // create tcp socket
+ // #1. create tcp socket.
int fd = socket(family, SOCK_STREAM, IPPROTO_TCP);
if (-1 == fd)
{
OIC_LOG_V(ERROR, TAG, "create socket failed: %s", strerror(errno));
- goto exit;
+ return -1;
}
+ // #2. convert address from string to binary.
struct sockaddr_storage sa = { .ss_family = family };
- CAConvertNameToAddr(svritem->sep.endpoint.addr, svritem->sep.endpoint.port, &sa);
- socklen_t socklen = sizeof (struct sockaddr_in);
+ CAResult_t res = CATCPConvertNameToAddr(family, svritem->sep.endpoint.addr,
+ svritem->sep.endpoint.port, &sa);
+ if (CA_STATUS_OK != res)
+ {
+ close(fd);
+ return -1;
+ }
- // connect to TCP server
- int ret = connect(fd, (struct sockaddr *)&sa, socklen);
- if (0 == ret)
+ // #3. set socket length.
+ socklen_t socklen;
+ if (sa.ss_family == AF_INET6)
{
- OIC_LOG(DEBUG, TAG, "connect socket success");
- CAWakeUpForReadFdsUpdate(svritem->sep.endpoint.addr);
+ struct sockaddr_in6 *sock6 = (struct sockaddr_in6 *)&sa;
+ if (!sock6->sin6_scope_id)
+ {
+ sock6->sin6_scope_id = svritem->sep.endpoint.interface;
+ }
+ socklen = sizeof(struct sockaddr_in6);
}
else
{
- OIC_LOG(ERROR, TAG, "failed to connect socket");
- goto exit;
+ socklen = sizeof(struct sockaddr_in);
}
- return fd;
-
-exit:
- if (fd >= 0)
+ // #4. connect to remote server device.
+ if (connect(fd, (struct sockaddr *)&sa, socklen) < 0)
{
+ OIC_LOG_V(ERROR, TAG, "failed to connect socket, %s", strerror(errno));
close(fd);
+ return -1;
}
- return -1;
+
+ OIC_LOG(DEBUG, TAG, "connect socket success");
+ CAWakeUpForReadFdsUpdate(svritem->sep.endpoint.addr);
+ return fd;
}
-static CAResult_t CACreateAcceptSocket()
+static int CACreateAcceptSocket(int family, CASocket_t *sock)
{
- if (g_acceptServerFD != -1)
+ VERIFY_NON_NULL_RET(sock, TAG, "sock", -1);
+
+ if (sock->fd != -1)
{
OIC_LOG(DEBUG, TAG, "accept socket created already");
- return CA_STATUS_OK;
+ return sock->fd;
}
- int reuse = 1;
- struct sockaddr_in server = { .sin_addr.s_addr = INADDR_ANY,
- .sin_family = AF_INET,
- .sin_port = htons(SERVER_PORT),
- .sin_zero = { 0 } };
+ socklen_t socklen;
+ struct sockaddr_storage server = { .ss_family = family };
- g_acceptServerFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (g_acceptServerFD < 0)
+ int fd = socket(family, SOCK_STREAM, IPPROTO_TCP);
+ if (fd < 0)
{
OIC_LOG(ERROR, TAG, "Failed to create socket");
goto exit;
}
- if (-1 == setsockopt(g_acceptServerFD, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)))
+ if (family == AF_INET6)
+ {
+ // the socket is re‐stricted to sending and receiving IPv6 packets only.
+ int on = 1;
+ if (-1 == setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)))
+ {
+ OIC_LOG_V(ERROR, TAG, "IPV6_V6ONLY failed: %s", strerror(errno));
+ goto exit;
+ }
+ ((struct sockaddr_in6 *)&server)->sin6_port = htons(sock->port);
+ socklen = sizeof (struct sockaddr_in6);
+ }
+ else
+ {
+ ((struct sockaddr_in *)&server)->sin_port = htons(sock->port);
+ socklen = sizeof (struct sockaddr_in);
+ }
+
+ int reuse = 1;
+ if (-1 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)))
{
OIC_LOG(ERROR, TAG, "setsockopt SO_REUSEADDR");
goto exit;
}
- int serverlen = sizeof(server);
- if (-1 == bind(g_acceptServerFD, (struct sockaddr *)&server, serverlen))
+ if (-1 == bind(fd, (struct sockaddr *)&server, socklen))
{
- OIC_LOG(ERROR, TAG, "bind() error");
+ OIC_LOG_V(ERROR, TAG, "bind socket failed: %s", strerror(errno));
goto exit;
}
- if (listen(g_acceptServerFD, caglobals.tcp.listenBacklog) != 0)
+ if (listen(fd, caglobals.tcp.listenBacklog) != 0)
{
OIC_LOG(ERROR, TAG, "listen() error");
goto exit;
}
- CHECKFD(g_acceptServerFD);
+ if (!sock->port) // return the assigned port
+ {
+ if (-1 == getsockname(fd, (struct sockaddr *)&server, &socklen))
+ {
+ OIC_LOG_V(ERROR, TAG, "getsockname failed: %s", strerror(errno));
+ goto exit;
+ }
+ sock->port = ntohs(family == AF_INET6 ?
+ ((struct sockaddr_in6 *)&server)->sin6_port :
+ ((struct sockaddr_in *)&server)->sin_port);
+ }
- return CA_STATUS_OK;
+ return fd;
exit:
- if (g_acceptServerFD >= 0)
+ if (fd >= 0)
{
- close(g_acceptServerFD);
- g_acceptServerFD = -1;
+ close(fd);
}
- return CA_STATUS_FAILED;
+ return -1;
}
static void CAInitializePipe(int *fds)
{
caglobals.tcp.ipv4tcpenabled = true; // only needed to run CA tests
}
+ if (!caglobals.tcp.ipv6tcpenabled)
+ {
+ caglobals.tcp.ipv6tcpenabled = true; // only needed to run CA tests
+ }
CAResult_t res = CATCPCreateMutex();
if (CA_STATUS_OK == res)
if (caglobals.server)
{
- res = CACreateAcceptSocket();
- if (CA_STATUS_OK != res)
- {
- OIC_LOG(ERROR, TAG, "failed to create accept socket");
- return res;
- }
+ caglobals.tcp.ipv4.fd = CACreateAcceptSocket(AF_INET, &caglobals.tcp.ipv4);
+ CHECKFD(caglobals.tcp.ipv4.fd);
+ caglobals.tcp.ipv6.fd = CACreateAcceptSocket(AF_INET6, &caglobals.tcp.ipv6);
+ CHECKFD(caglobals.tcp.ipv6.fd);
+
+ OIC_LOG_V(DEBUG, TAG, "IPv4 socket fd=%d, port=%d",
+ caglobals.tcp.ipv4.fd, caglobals.tcp.ipv4.port);
+ OIC_LOG_V(DEBUG, TAG, "IPv6 socket fd=%d, port=%d",
+ caglobals.tcp.ipv6.fd, caglobals.tcp.ipv6.port);
}
// create pipe for fast shutdown
// mutex unlock
ca_mutex_unlock(g_mutexObjectList);
- if (-1 != g_acceptServerFD)
+ if (-1 != caglobals.tcp.ipv4.fd)
+ {
+ close(caglobals.tcp.ipv4.fd);
+ caglobals.tcp.ipv4.fd = -1;
+ }
+
+ if (-1 != caglobals.tcp.ipv6.fd)
{
- close(g_acceptServerFD);
- g_acceptServerFD = -1;
+ close(caglobals.tcp.ipv6.fd);
+ caglobals.tcp.ipv6.fd = -1;
}
CATCPDisconnectAll();
return payloadLen;
}
-static void sendData(const CAEndpoint_t *endpoint,
- const void *data, size_t dlen)
+static void sendData(const CAEndpoint_t *endpoint, const void *data,
+ size_t dlen, const char *fam)
{
// #1. get TCP Server object from list
size_t index = 0;
remainLen -= len;
} while (remainLen > 0);
- OIC_LOG_V(INFO, TAG, "unicast ipv4tcp sendTo is successful: %zu bytes", dlen);
+ OIC_LOG_V(INFO, TAG, "unicast %stcp sendTo is successful: %zu bytes", fam, dlen);
}
void CATCPSendData(CAEndpoint_t *endpoint, const void *data, uint32_t datalen,
if (!isMulticast)
{
- if (caglobals.tcp.ipv4tcpenabled && (endpoint->adapter & CA_ADAPTER_TCP))
+ if (caglobals.tcp.ipv6tcpenabled && (endpoint->flags & CA_IPV6))
{
- sendData(endpoint, data, datalen);
+ sendData(endpoint, data, datalen, "ipv6");
+ }
+ if (caglobals.tcp.ipv4tcpenabled && (endpoint->flags & CA_IPV4))
+ {
+ sendData(endpoint, data, datalen, "ipv4");
}
}
}
}
memcpy(svritem->sep.endpoint.addr, endpoint->addr, sizeof(svritem->sep.endpoint.addr));
svritem->sep.endpoint.port = endpoint->port;
+ svritem->sep.endpoint.flags = endpoint->flags;
+ svritem->sep.endpoint.interface = endpoint->interface;
// #2. create the socket and connect to TCP server
- if (caglobals.tcp.ipv4tcpenabled)
+ int family = (svritem->sep.endpoint.flags & CA_IPV6) ? AF_INET6 : AF_INET;
+ int fd = CATCPCreateSocket(family, svritem);
+ if (-1 == fd)
+ {
+ OICFree(svritem);
+ return NULL;
+ }
+
+ // #3. add TCP connection info to list
+ svritem->fd = fd;
+ ca_mutex_lock(g_mutexObjectList);
+ if (caglobals.tcp.svrlist)
{
- int fd = CATCPCreateSocket(AF_INET, svritem);
- if (-1 == fd)
+ bool res = u_arraylist_add(caglobals.tcp.svrlist, svritem);
+ if (!res)
{
+ OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
+ close(svritem->fd);
OICFree(svritem);
+ ca_mutex_unlock(g_mutexObjectList);
return NULL;
}
+ }
+ ca_mutex_unlock(g_mutexObjectList);
- // #3. add TCP connection info to list
- svritem->fd = fd;
- ca_mutex_lock(g_mutexObjectList);
- if (caglobals.tcp.svrlist)
- {
- bool res = u_arraylist_add(caglobals.tcp.svrlist, svritem);
- if (!res)
- {
- OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
- close(svritem->fd);
- OICFree(svritem);
- ca_mutex_unlock(g_mutexObjectList);
- return NULL;
- }
- }
- ca_mutex_unlock(g_mutexObjectList);
-
- CHECKFD(fd);
+ CHECKFD(fd);
- // pass the connection information to RI for keepalive.
- if (g_keepaliveCallback)
- {
- g_keepaliveCallback(svritem->sep.endpoint.addr, svritem->sep.endpoint.port, true);
- }
+ // pass the connection information to RI for keepalive.
+ if (g_keepaliveCallback)
+ {
+ g_keepaliveCallback(svritem->sep.endpoint.addr, svritem->sep.endpoint.port, true);
}
return svritem;
ca_mutex_unlock(g_mutexObjectList);
}
-CATCPSessionInfo_t *CAGetTCPSessionInfoFromEndpoint(const CAEndpoint_t *endpoint,
- size_t *index)
+CATCPSessionInfo_t *CAGetTCPSessionInfoFromEndpoint(const CAEndpoint_t *endpoint, size_t *index)
{
VERIFY_NON_NULL_RET(endpoint, TAG, "endpoint is NULL", NULL);
VERIFY_NON_NULL_RET(index, TAG, "index is NULL", NULL);
if (!strncmp(svritem->sep.endpoint.addr, endpoint->addr,
sizeof(svritem->sep.endpoint.addr))
- && (svritem->sep.endpoint.port == endpoint->port))
+ && (svritem->sep.endpoint.port == endpoint->port)
+ && (svritem->sep.endpoint.flags & endpoint->flags))
{
*index = i;
return svritem;