Loading...
Searching...
No Matches
coap_tcp.c
Go to the documentation of this file.
/*
* coap_tcp.c -- TCP functions for libcoap
*
* Copyright (C) 2019-2024 Olaf Bergmann <bergmann@tzi.org> and others
*
* SPDX-License-Identifier: BSD-2-Clause
*
* This file is part of the CoAP library libcoap. Please see README for terms
* of use.
*/
#include "coap3/coap_libcoap_build.h"
int
coap_tcp_is_supported(void) {
return !COAP_DISABLE_TCP;
}
#if !COAP_DISABLE_TCP && !defined(WITH_LWIP) && !defined(WITH_CONTIKI) && !defined(RIOT_VERSION)
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
# define OPTVAL_T(t) (t)
# define OPTVAL_GT(t) (t)
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
# define OPTVAL_T(t) (const char*)(t)
# define OPTVAL_GT(t) (char*)(t)
# undef CMSG_DATA
# define CMSG_DATA WSA_CMSG_DATA
#endif
int
coap_socket_connect_tcp1(coap_socket_t *sock,
const coap_address_t *local_if,
const coap_address_t *server,
int default_port,
coap_address_t *local_addr,
coap_address_t *remote_addr) {
int on = 1;
#if COAP_IPV6_SUPPORT
int off = 0;
#endif /* COAP_IPV6_SUPPORT */
#ifdef _WIN32
u_long u_on = 1;
#endif
coap_address_t connect_addr;
coap_address_copy(&connect_addr, server);
sock->flags &= ~COAP_SOCKET_CONNECTED;
sock->fd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0);
if (sock->fd == COAP_INVALID_SOCKET) {
coap_log_warn("coap_socket_connect_tcp1: socket: %s\n",
coap_socket_strerror());
goto error;
}
#ifdef _WIN32
if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
#else
if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
#endif
coap_log_warn("coap_socket_connect_tcp1: ioctl FIONBIO: %s\n",
coap_socket_strerror());
}
switch (server->addr.sa.sa_family) {
#if COAP_IPV4_SUPPORT
case AF_INET:
if (connect_addr.addr.sin.sin_port == 0)
connect_addr.addr.sin.sin_port = htons(default_port);
break;
#endif /* COAP_IPV4_SUPPORT */
#if COAP_IPV6_SUPPORT
case AF_INET6:
if (connect_addr.addr.sin6.sin6_port == 0)
connect_addr.addr.sin6.sin6_port = htons(default_port);
/* Configure the socket as dual-stacked */
if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off),
sizeof(off)) == COAP_SOCKET_ERROR)
coap_log_warn("coap_socket_connect_tcp1: setsockopt IPV6_V6ONLY: %s\n",
coap_socket_strerror());
break;
#endif /* COAP_IPV6_SUPPORT */
#if COAP_AF_UNIX_SUPPORT
case AF_UNIX:
break;
#endif /* COAP_AF_UNIX_SUPPORT */
default:
coap_log_alert("coap_socket_connect_tcp1: unsupported sa_family\n");
break;
}
if (local_if && local_if->addr.sa.sa_family) {
coap_address_copy(local_addr, local_if);
if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR)
coap_log_warn("coap_socket_connect_tcp1: setsockopt SO_REUSEADDR: %s\n",
coap_socket_strerror());
if (bind(sock->fd, &local_if->addr.sa,
#if COAP_IPV4_SUPPORT
local_if->addr.sa.sa_family == AF_INET ?
(socklen_t)sizeof(struct sockaddr_in) :
#endif /* COAP_IPV4_SUPPORT */
(socklen_t)local_if->size) == COAP_SOCKET_ERROR) {
coap_log_warn("coap_socket_connect_tcp1: bind: %s\n",
coap_socket_strerror());
goto error;
}
} else {
local_addr->addr.sa.sa_family = server->addr.sa.sa_family;
}
if (connect(sock->fd, &connect_addr.addr.sa, connect_addr.size) == COAP_SOCKET_ERROR) {
#ifdef _WIN32
if (WSAGetLastError() == WSAEWOULDBLOCK) {
#else
if (errno == EINPROGRESS) {
#endif
/*
* COAP_SOCKET_CONNECTED needs to be set here as there will be reads/writes
* by underlying TLS libraries during connect() and we do not want to
* assert() in coap_read_session() or coap_write_session() when called by coap_read()
*/
sock->flags |= COAP_SOCKET_WANT_CONNECT | COAP_SOCKET_CONNECTED;
return 1;
}
coap_log_warn("coap_socket_connect_tcp1: connect: %s\n",
coap_socket_strerror());
goto error;
}
if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) {
coap_log_warn("coap_socket_connect_tcp1: getsockname: %s\n",
coap_socket_strerror());
}
if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) {
coap_log_warn("coap_socket_connect_tcp1: getpeername: %s\n",
coap_socket_strerror());
}
sock->flags |= COAP_SOCKET_CONNECTED;
return 1;
error:
coap_socket_close(sock);
return 0;
}
int
coap_socket_connect_tcp2(coap_socket_t *sock,
coap_address_t *local_addr,
coap_address_t *remote_addr) {
int error = 0;
#ifdef _WIN32
int optlen = (int)sizeof(error);
#else
socklen_t optlen = (socklen_t)sizeof(error);
#endif
sock->flags &= ~(COAP_SOCKET_WANT_CONNECT | COAP_SOCKET_CAN_CONNECT);
if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, OPTVAL_GT(&error),
&optlen) == COAP_SOCKET_ERROR) {
coap_log_warn("coap_socket_connect_tcp2: getsockopt: %s\n",
coap_socket_strerror());
}
if (error) {
coap_log_warn("coap_socket_connect_tcp2: connect failed: %s\n",
coap_socket_format_errno(error));
coap_socket_close(sock);
return 0;
}
if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) {
coap_log_warn("coap_socket_connect_tcp: getsockname: %s\n",
coap_socket_strerror());
}
if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) {
coap_log_warn("coap_socket_connect_tcp: getpeername: %s\n",
coap_socket_strerror());
}
return 1;
}
int
coap_socket_bind_tcp(coap_socket_t *sock,
const coap_address_t *listen_addr,
coap_address_t *bound_addr) {
int on = 1;
#if COAP_IPV6_SUPPORT
int off = 0;
#endif /* COAP_IPV6_SUPPORT */
#ifdef _WIN32
u_long u_on = 1;
#endif
sock->fd = socket(listen_addr->addr.sa.sa_family, SOCK_STREAM, 0);
if (sock->fd == COAP_INVALID_SOCKET) {
coap_log_warn("coap_socket_bind_tcp: socket: %s\n",
coap_socket_strerror());
goto error;
}
#ifdef _WIN32
if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
#else
if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
#endif
coap_log_warn("coap_socket_bind_tcp: ioctl FIONBIO: %s\n",
coap_socket_strerror());
}
if (setsockopt(sock->fd, SOL_SOCKET, SO_KEEPALIVE, OPTVAL_T(&on),
sizeof(on)) == COAP_SOCKET_ERROR)
coap_log_warn("coap_socket_bind_tcp: setsockopt SO_KEEPALIVE: %s\n",
coap_socket_strerror());
if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on),
sizeof(on)) == COAP_SOCKET_ERROR)
coap_log_warn("coap_socket_bind_tcp: setsockopt SO_REUSEADDR: %s\n",
coap_socket_strerror());
switch (listen_addr->addr.sa.sa_family) {
#if COAP_IPV4_SUPPORT
case AF_INET:
break;
#endif /* COAP_IPV4_SUPPORT */
#if COAP_IPV6_SUPPORT
case AF_INET6:
/* Configure the socket as dual-stacked */
if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off),
sizeof(off)) == COAP_SOCKET_ERROR)
coap_log_alert("coap_socket_bind_tcp: setsockopt IPV6_V6ONLY: %s\n",
coap_socket_strerror());
break;
#endif /* COAP_IPV6_SUPPORT */
#if COAP_AF_UNIX_SUPPORT
case AF_UNIX:
break;
#endif /* COAP_AF_UNIX_SUPPORT */
default:
coap_log_alert("coap_socket_bind_tcp: unsupported sa_family\n");
}
if (bind(sock->fd, &listen_addr->addr.sa,
#if COAP_IPV4_SUPPORT
listen_addr->addr.sa.sa_family == AF_INET ?
(socklen_t)sizeof(struct sockaddr_in) :
#endif /* COAP_IPV4_SUPPORT */
(socklen_t)listen_addr->size) == COAP_SOCKET_ERROR) {
coap_log_alert("coap_socket_bind_tcp: bind: %s\n",
coap_socket_strerror());
goto error;
}
bound_addr->size = (socklen_t)sizeof(*bound_addr);
if (getsockname(sock->fd, &bound_addr->addr.sa, &bound_addr->size) < 0) {
coap_log_warn("coap_socket_bind_tcp: getsockname: %s\n",
coap_socket_strerror());
goto error;
}
if (listen(sock->fd, 5) == COAP_SOCKET_ERROR) {
coap_log_alert("coap_socket_bind_tcp: listen: %s\n",
coap_socket_strerror());
goto error;
}
return 1;
error:
coap_socket_close(sock);
return 0;
}
int
coap_socket_accept_tcp(coap_socket_t *server,
coap_socket_t *new_client,
coap_address_t *local_addr,
coap_address_t *remote_addr,
void *extra) {
#ifdef _WIN32
u_long u_on = 1;
#else
int on = 1;
#endif
(void)extra;
server->flags &= ~COAP_SOCKET_CAN_ACCEPT;
new_client->fd = accept(server->fd, &remote_addr->addr.sa,
&remote_addr->size);
if (new_client->fd == COAP_INVALID_SOCKET) {
coap_log_warn("coap_socket_accept_tcp: accept: %s\n",
coap_socket_strerror());
return 0;
}
if (getsockname(new_client->fd, &local_addr->addr.sa, &local_addr->size) < 0)
coap_log_warn("coap_socket_accept_tcp: getsockname: %s\n",
coap_socket_strerror());
#ifdef _WIN32
if (ioctlsocket(new_client->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
#else
if (ioctl(new_client->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
#endif
coap_log_warn("coap_socket_accept_tcp: ioctl FIONBIO: %s\n",
coap_socket_strerror());
}
return 1;
}
#endif /* !COAP_DISABLE_TCP */
coap_address_copy
void coap_address_copy(coap_address_t *dst, const coap_address_t *src)
Definition coap_address.c:806
COAP_IPV4_SUPPORT
#define COAP_IPV4_SUPPORT
Definition coap_internal.h:70
coap_socket_format_errno
const char * coap_socket_format_errno(int error)
Definition coap_io.c:1896
coap_socket_close
void coap_socket_close(coap_socket_t *sock)
Function interface to close off a socket.
Definition coap_io.c:375
coap_socket_strerror
const char * coap_socket_strerror(void)
Definition coap_io.c:1900
COAP_SOCKET_ERROR
#define COAP_SOCKET_ERROR
Definition coap_io.h:49
COAP_INVALID_SOCKET
#define COAP_INVALID_SOCKET
Definition coap_io.h:50
COAP_SOCKET_CAN_ACCEPT
#define COAP_SOCKET_CAN_ACCEPT
non blocking server socket can now accept without blocking
Definition coap_io_internal.h:84
COAP_SOCKET_CAN_CONNECT
#define COAP_SOCKET_CAN_CONNECT
non blocking client socket can now connect without blocking
Definition coap_io_internal.h:85
COAP_SOCKET_WANT_CONNECT
#define COAP_SOCKET_WANT_CONNECT
non blocking client socket is waiting for connect
Definition coap_io_internal.h:81
COAP_SOCKET_CONNECTED
#define COAP_SOCKET_CONNECTED
the socket is connected
Definition coap_io_internal.h:77
coap_libcoap_build.h
Library specific build wrapper for coap_internal.h.
coap_log_alert
#define coap_log_alert(...)
Definition coap_debug.h:84
coap_log_warn
#define coap_log_warn(...)
Definition coap_debug.h:102
coap_tcp_is_supported
int coap_tcp_is_supported(void)
Check whether TCP is available.
Definition coap_tcp.c:20
coap_socket_bind_tcp
int coap_socket_bind_tcp(coap_socket_t *sock, const coap_address_t *listen_addr, coap_address_t *bound_addr)
Create a new TCP socket and then listen for new incoming TCP sessions.
Definition coap_tcp.c:200
coap_socket_connect_tcp1
int coap_socket_connect_tcp1(coap_socket_t *sock, const coap_address_t *local_if, const coap_address_t *server, int default_port, coap_address_t *local_addr, coap_address_t *remote_addr)
Create a new TCP socket and initiate the connection.
Definition coap_tcp.c:43
coap_socket_accept_tcp
int coap_socket_accept_tcp(coap_socket_t *server, coap_socket_t *new_client, coap_address_t *local_addr, coap_address_t *remote_addr, void *extra)
Accept a new incoming TCP session.
Definition coap_tcp.c:291
coap_socket_connect_tcp2
int coap_socket_connect_tcp2(coap_socket_t *sock, coap_address_t *local_addr, coap_address_t *remote_addr)
Complete the TCP Connection.
Definition coap_tcp.c:161
coap_address_t
Multi-purpose address abstraction.
Definition coap_address.h:148
coap_address_t::size
socklen_t size
size of addr
Definition coap_address.h:149
coap_address_t::sin
struct sockaddr_in sin
Definition coap_address.h:152
coap_address_t::sin6
struct sockaddr_in6 sin6
Definition coap_address.h:153
coap_address_t::sa
struct sockaddr sa
Definition coap_address.h:151
coap_address_t::addr
union coap_address_t::@046063134121062142011205144005042234315245237012 addr
coap_socket_t
Definition coap_io_internal.h:38
coap_socket_t::fd
coap_fd_t fd
Definition coap_io_internal.h:57
coap_socket_t::flags
coap_socket_flags_t flags
1 or more of COAP_SOCKET* flag values
Definition coap_io_internal.h:59