diff --git a/components/coap/CMakeLists.txt b/components/coap/CMakeLists.txt index 01cd61e68..5c3fcd783 100644 --- a/components/coap/CMakeLists.txt +++ b/components/coap/CMakeLists.txt @@ -1,12 +1,14 @@ -set(COMPONENT_ADD_INCLUDEDIRS port/include port/include/coap libcoap/include libcoap/include/coap) +set(COMPONENT_ADD_INCLUDEDIRS port/include port/include/coap libcoap/include libcoap/include/coap2) set(COMPONENT_SRCS "libcoap/src/address.c" "libcoap/src/async.c" "libcoap/src/block.c" + "libcoap/src/coap_event.c" + "libcoap/src/coap_hashkey.c" + "libcoap/src/coap_session.c" "libcoap/src/coap_time.c" - "libcoap/src/debug.c" + "libcoap/src/coap_debug.c" "libcoap/src/encode.c" - "libcoap/src/hashkey.c" "libcoap/src/mem.c" "libcoap/src/net.c" "libcoap/src/option.c" @@ -15,7 +17,8 @@ set(COMPONENT_SRCS "libcoap/src/address.c" "libcoap/src/str.c" "libcoap/src/subscribe.c" "libcoap/src/uri.c" - "port/coap_io_socket.c") + "libcoap/src/coap_notls.c" + "port/coap_io.c") set(COMPONENT_REQUIRES lwip) @@ -26,16 +29,3 @@ register_component() # TODO: find a way to move this to a port header target_compile_definitions(${COMPONENT_TARGET} PUBLIC WITH_POSIX) -set_source_files_properties( - libcoap/src/debug.c - libcoap/src/pdu.c - PROPERTIES COMPILE_FLAGS - -Wno-write-strings) - -if(GCC_NOT_5_2_0) - # Temporary suppress "fallthrough" warnings until they are fixed in libcoap repo - set_source_files_properties( - libcoap/src/option.c - PROPERTIES COMPILE_FLAGS - -Wno-implicit-fallthrough) -endif() \ No newline at end of file diff --git a/components/coap/component.mk b/components/coap/component.mk index bd7b0e0a6..b5a50afeb 100644 --- a/components/coap/component.mk +++ b/components/coap/component.mk @@ -2,17 +2,11 @@ # Component Makefile # -COMPONENT_ADD_INCLUDEDIRS := port/include port/include/coap libcoap/include libcoap/include/coap +COMPONENT_ADD_INCLUDEDIRS := port/include port/include/coap libcoap/include libcoap/include/coap2 -COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_time.o libcoap/src/debug.o libcoap/src/encode.o libcoap/src/hashkey.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o port/coap_io_socket.o +COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_event.o libcoap/src/coap_hashkey.o libcoap/src/coap_session.o libcoap/src/coap_time.o libcoap/src/coap_debug.o libcoap/src/encode.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o libcoap/src/coap_notls.o port/coap_io.o COMPONENT_SRCDIRS := libcoap/src libcoap port COMPONENT_SUBMODULES += libcoap -libcoap/src/debug.o: CFLAGS += -Wno-write-strings -libcoap/src/pdu.o: CFLAGS += -Wno-write-strings -ifeq ($(GCC_NOT_5_2_0), 1) -# Temporary suppress "fallthrough" warnings until they are fixed in libcoap repo -libcoap/src/option.o: CFLAGS += -Wno-implicit-fallthrough -endif diff --git a/components/coap/libcoap b/components/coap/libcoap index 6468887a1..cfec0d072 160000 --- a/components/coap/libcoap +++ b/components/coap/libcoap @@ -1 +1 @@ -Subproject commit 6468887a12666f88b8704d797fc176cd4f40ee4c +Subproject commit cfec0d072c5b99ed3e54828ca50ea2f6b91e1f50 diff --git a/components/coap/port/coap_io.c b/components/coap/port/coap_io.c new file mode 100644 index 000000000..58391add6 --- /dev/null +++ b/components/coap/port/coap_io.c @@ -0,0 +1,1422 @@ +/* coap_io.c -- Default network I/O functions for libcoap + * + * Copyright (C) 2012,2014,2016-2019 Olaf Bergmann and others + * + * This file is part of the CoAP library libcoap. Please see + * README for terms of use. + */ + +#include "coap_config.h" + +#ifdef HAVE_STDIO_H +# include +#endif + +#ifdef HAVE_SYS_SELECT_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +# define OPTVAL_T(t) (t) +# define OPTVAL_GT(t) (t) +#endif +#ifdef HAVE_SYS_IOCTL_H + #include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_WS2TCPIP_H +#include +# define OPTVAL_T(t) (const char*)(t) +# define OPTVAL_GT(t) (char*)(t) +# undef CMSG_DATA +# define CMSG_DATA WSA_CMSG_DATA +#endif +#ifdef HAVE_SYS_UIO_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#include + +#ifdef WITH_CONTIKI +# include "uip.h" +#endif + +#include "libcoap.h" +#include "coap_debug.h" +#include "mem.h" +#include "net.h" +#include "coap_io.h" +#include "pdu.h" +#include "utlist.h" +#include "resource.h" + +#if !defined(WITH_CONTIKI) + /* define generic PKTINFO for IPv4 */ +#if defined(IP_PKTINFO) +# define GEN_IP_PKTINFO IP_PKTINFO +#elif defined(IP_RECVDSTADDR) +# define GEN_IP_PKTINFO IP_RECVDSTADDR +#else +# error "Need IP_PKTINFO or IP_RECVDSTADDR to request ancillary data from OS." +#endif /* IP_PKTINFO */ + +/* define generic KTINFO for IPv6 */ +#ifdef IPV6_RECVPKTINFO +# define GEN_IPV6_PKTINFO IPV6_RECVPKTINFO +#elif defined(IPV6_PKTINFO) +# define GEN_IPV6_PKTINFO IPV6_PKTINFO +#else +# error "Need IPV6_PKTINFO or IPV6_RECVPKTINFO to request ancillary data from OS." +#endif /* IPV6_RECVPKTINFO */ +#endif + +void coap_free_endpoint(coap_endpoint_t *ep); + +#ifdef WITH_CONTIKI +static int ep_initialized = 0; + +struct coap_endpoint_t * + coap_malloc_endpoint() { + static struct coap_endpoint_t ep; + + if (ep_initialized) { + return NULL; + } else { + ep_initialized = 1; + return &ep; + } +} + +void +coap_mfree_endpoint(struct coap_endpoint_t *ep) { + ep_initialized = 0; + coap_session_mfree(&ep->hello); +} + +int +coap_socket_bind_udp(coap_socket_t *sock, + const coap_address_t *listen_addr, + coap_address_t *bound_addr) { + sock->conn = udp_new(NULL, 0, NULL); + + if (!sock->conn) { + coap_log(LOG_WARNING, "coap_socket_bind_udp"); + return 0; + } + + coap_address_init(bound_addr); + uip_ipaddr_copy(&bound_addr->addr, &listen_addr->addr); + bound_addr->port = listen_addr->port; + udp_bind((struct uip_udp_conn *)sock->conn, bound_addr->port); + return 1; +} + +int +coap_socket_connect_udp(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) { + return 0; +} + +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) { + return 0; +} + +int +coap_socket_connect_tcp2(coap_socket_t *sock, + coap_address_t *local_addr, + coap_address_t *remote_addr) { + return 0; +} + +int +coap_socket_bind_tcp(coap_socket_t *sock, + const coap_address_t *listen_addr, + coap_address_t *bound_addr) { + 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) { + return 0; +} + +ssize_t +coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) { + return -1; +} + +ssize_t +coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) { + return -1; +} + +void coap_socket_close(coap_socket_t *sock) { + if (sock->conn) + uip_udp_remove((struct uip_udp_conn *)sock->conn); + sock->flags = COAP_SOCKET_EMPTY; +} + +#else + +static const char *coap_socket_format_errno( int error ); + +struct coap_endpoint_t * + coap_malloc_endpoint(void) { + return (struct coap_endpoint_t *)coap_malloc_type(COAP_ENDPOINT, sizeof(struct coap_endpoint_t)); +} + +void +coap_mfree_endpoint(struct coap_endpoint_t *ep) { + coap_session_mfree(&ep->hello); + coap_free_type(COAP_ENDPOINT, ep); +} + +int +coap_socket_bind_udp(coap_socket_t *sock, + const coap_address_t *listen_addr, + coap_address_t *bound_addr) { + int on = 1, off = 0; +#ifdef _WIN32 + u_long u_on = 1; +#endif + + sock->fd = socket(listen_addr->addr.sa.sa_family, SOCK_DGRAM, 0); + + if (sock->fd == COAP_INVALID_SOCKET) { + coap_log(LOG_WARNING, + "coap_socket_bind_udp: 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(LOG_WARNING, + "coap_socket_bind_udp: ioctl FIONBIO: %s\n", coap_socket_strerror()); + } + + if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR) + coap_log(LOG_WARNING, + "coap_socket_bind_udp: setsockopt SO_REUSEADDR: %s\n", + coap_socket_strerror()); + + switch (listen_addr->addr.sa.sa_family) { + case AF_INET: + if (setsockopt(sock->fd, IPPROTO_IP, GEN_IP_PKTINFO, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR) + coap_log(LOG_ALERT, + "coap_socket_bind_udp: setsockopt IP_PKTINFO: %s\n", + coap_socket_strerror()); + break; + 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(LOG_ALERT, + "coap_socket_bind_udp: setsockopt IPV6_V6ONLY: %s\n", + coap_socket_strerror()); + if (setsockopt(sock->fd, IPPROTO_IPV6, GEN_IPV6_PKTINFO, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR) + coap_log(LOG_ALERT, + "coap_socket_bind_udp: setsockopt IPV6_PKTINFO: %s\n", + coap_socket_strerror()); + setsockopt(sock->fd, IPPROTO_IP, GEN_IP_PKTINFO, OPTVAL_T(&on), sizeof(on)); /* ignore error, because the likely cause is that IPv4 is disabled at the os level */ + break; + default: + coap_log(LOG_ALERT, "coap_socket_bind_udp: unsupported sa_family\n"); + break; + } + + if (bind(sock->fd, &listen_addr->addr.sa, listen_addr->size) == COAP_SOCKET_ERROR) { + coap_log(LOG_WARNING, "coap_socket_bind_udp: 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(LOG_WARNING, + "coap_socket_bind_udp: getsockname: %s\n", + coap_socket_strerror()); + goto error; + } + + return 1; + +error: + coap_socket_close(sock); + return 0; +} + +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, off = 0; +#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(LOG_WARNING, + "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(LOG_WARNING, + "coap_socket_connect_tcp1: ioctl FIONBIO: %s\n", + coap_socket_strerror()); + } + + switch (server->addr.sa.sa_family) { + case AF_INET: + if (connect_addr.addr.sin.sin_port == 0) + connect_addr.addr.sin.sin_port = htons(default_port); + break; + 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(LOG_WARNING, + "coap_socket_connect_tcp1: setsockopt IPV6_V6ONLY: %s\n", + coap_socket_strerror()); + break; + default: + coap_log(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(LOG_WARNING, + "coap_socket_connect_tcp1: setsockopt SO_REUSEADDR: %s\n", + coap_socket_strerror()); + if (bind(sock->fd, &local_if->addr.sa, local_if->size) == COAP_SOCKET_ERROR) { + coap_log(LOG_WARNING, "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(LOG_WARNING, "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(LOG_WARNING, "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(LOG_WARNING, "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(LOG_WARNING, "coap_socket_finish_connect_tcp: getsockopt: %s\n", + coap_socket_strerror()); + } + + if (error) { + coap_log(LOG_WARNING, + "coap_socket_finish_connect_tcp: 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(LOG_WARNING, "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(LOG_WARNING, "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, off = 0; +#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(LOG_WARNING, "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(LOG_WARNING, "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(LOG_WARNING, + "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(LOG_WARNING, + "coap_socket_bind_tcp: setsockopt SO_REUSEADDR: %s\n", + coap_socket_strerror()); + + switch (listen_addr->addr.sa.sa_family) { + case AF_INET: + break; + 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(LOG_ALERT, + "coap_socket_bind_tcp: setsockopt IPV6_V6ONLY: %s\n", + coap_socket_strerror()); + break; + default: + coap_log(LOG_ALERT, "coap_socket_bind_tcp: unsupported sa_family\n"); + } + + if (bind(sock->fd, &listen_addr->addr.sa, listen_addr->size) == COAP_SOCKET_ERROR) { + coap_log(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(LOG_WARNING, "coap_socket_bind_tcp: getsockname: %s\n", + coap_socket_strerror()); + goto error; + } + + if (listen(sock->fd, 5) == COAP_SOCKET_ERROR) { + coap_log(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) { +#ifdef _WIN32 + u_long u_on = 1; +#else + int on = 1; +#endif + + 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(LOG_WARNING, "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(LOG_WARNING, "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(LOG_WARNING, "coap_socket_accept_tcp: ioctl FIONBIO: %s\n", + coap_socket_strerror()); + } + + return 1; +} + +int +coap_socket_connect_udp(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, off = 0; +#ifdef _WIN32 + u_long u_on = 1; +#endif + coap_address_t connect_addr; + int is_mcast = coap_is_mcast(server); + coap_address_copy(&connect_addr, server); + + sock->flags &= ~(COAP_SOCKET_CONNECTED | COAP_SOCKET_MULTICAST); + sock->fd = socket(connect_addr.addr.sa.sa_family, SOCK_DGRAM, 0); + + if (sock->fd == COAP_INVALID_SOCKET) { + coap_log(LOG_WARNING, "coap_socket_connect_udp: 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(LOG_WARNING, "coap_socket_connect_udp: ioctl FIONBIO: %s\n", + coap_socket_strerror()); + } + + switch (connect_addr.addr.sa.sa_family) { + case AF_INET: + if (connect_addr.addr.sin.sin_port == 0) + connect_addr.addr.sin.sin_port = htons(default_port); + break; + 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(LOG_WARNING, + "coap_socket_connect_udp: setsockopt IPV6_V6ONLY: %s\n", + coap_socket_strerror()); + break; + default: + coap_log(LOG_ALERT, "coap_socket_connect_udp: unsupported sa_family\n"); + break; + } + + if (local_if && local_if->addr.sa.sa_family) { + if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR) + coap_log(LOG_WARNING, + "coap_socket_connect_udp: setsockopt SO_REUSEADDR: %s\n", + coap_socket_strerror()); + if (bind(sock->fd, &local_if->addr.sa, local_if->size) == COAP_SOCKET_ERROR) { + coap_log(LOG_WARNING, "coap_socket_connect_udp: bind: %s\n", + coap_socket_strerror()); + goto error; + } + } + + /* special treatment for sockets that are used for multicast communication */ + if (is_mcast) { + if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) { + coap_log(LOG_WARNING, + "coap_socket_connect_udp: getsockname for multicast socket: %s\n", + coap_socket_strerror()); + } + coap_address_copy(remote_addr, &connect_addr); + sock->flags |= COAP_SOCKET_MULTICAST; + return 1; + } + + if (connect(sock->fd, &connect_addr.addr.sa, connect_addr.size) == COAP_SOCKET_ERROR) { + coap_log(LOG_WARNING, "coap_socket_connect_udp: connect: %s\n", + coap_socket_strerror()); + goto error; + } + + if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) { + coap_log(LOG_WARNING, "coap_socket_connect_udp: getsockname: %s\n", + coap_socket_strerror()); + } + + if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) { + coap_log(LOG_WARNING, "coap_socket_connect_udp: getpeername: %s\n", + coap_socket_strerror()); + } + + sock->flags |= COAP_SOCKET_CONNECTED; + return 1; + +error: + coap_socket_close(sock); + return 0; +} + +void coap_socket_close(coap_socket_t *sock) { + if (sock->fd != COAP_INVALID_SOCKET) { + coap_closesocket(sock->fd); + sock->fd = COAP_INVALID_SOCKET; + } + sock->flags = COAP_SOCKET_EMPTY; +} + +ssize_t +coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) { + ssize_t r; + + sock->flags &= ~(COAP_SOCKET_WANT_WRITE | COAP_SOCKET_CAN_WRITE); +#ifdef _WIN32 + r = send(sock->fd, (const char *)data, (int)data_len, 0); +#else + r = send(sock->fd, data, data_len, 0); +#endif + if (r == COAP_SOCKET_ERROR) { +#ifdef _WIN32 + if (WSAGetLastError() == WSAEWOULDBLOCK) { +#elif EAGAIN != EWOULDBLOCK + if (errno==EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { +#else + if (errno==EAGAIN || errno == EINTR) { +#endif + sock->flags |= COAP_SOCKET_WANT_WRITE; + return 0; + } + coap_log(LOG_WARNING, "coap_socket_write: send: %s\n", + coap_socket_strerror()); + return -1; + } + if (r < (ssize_t)data_len) + sock->flags |= COAP_SOCKET_WANT_WRITE; + return r; +} + +ssize_t +coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) { + ssize_t r; +#ifdef _WIN32 + int error; +#endif + +#ifdef _WIN32 + r = recv(sock->fd, (char *)data, (int)data_len, 0); +#else + r = recv(sock->fd, data, data_len, 0); +#endif + if (r == 0) { + /* graceful shutdown */ + sock->flags &= ~COAP_SOCKET_CAN_READ; + return -1; + } else if (r == COAP_SOCKET_ERROR) { + sock->flags &= ~COAP_SOCKET_CAN_READ; +#ifdef _WIN32 + error = WSAGetLastError(); + if (error == WSAEWOULDBLOCK) { +#elif EAGAIN != EWOULDBLOCK + if (errno==EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { +#else + if (errno==EAGAIN || errno == EINTR) { +#endif + return 0; + } +#ifdef _WIN32 + if (error != WSAECONNRESET) +#else + if (errno != ECONNRESET) +#endif + coap_log(LOG_WARNING, "coap_socket_read: recv: %s\n", + coap_socket_strerror()); + return -1; + } + if (r < (ssize_t)data_len) + sock->flags &= ~COAP_SOCKET_CAN_READ; + return r; +} + +#endif /* WITH_CONTIKI */ + +#if (!defined(WITH_CONTIKI)) != ( defined(HAVE_NETINET_IN_H) || defined(HAVE_WS2TCPIP_H) ) +/* define struct in6_pktinfo and struct in_pktinfo if not available + FIXME: check with configure +*/ +struct in6_pktinfo { + struct in6_addr ipi6_addr; /* src/dst IPv6 address */ + unsigned int ipi6_ifindex; /* send/recv interface index */ +}; + +struct in_pktinfo { + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; +}; +#endif + +#if !defined(WITH_CONTIKI) && !defined(SOL_IP) +/* Solaris expects level IPPROTO_IP for ancillary data. */ +#define SOL_IP IPPROTO_IP +#endif + +#ifdef __GNUC__ +#define UNUSED_PARAM __attribute__ ((unused)) +#else /* not a GCC */ +#define UNUSED_PARAM +#endif /* GCC */ + +#if defined(_WIN32) +#include +static __declspec(thread) LPFN_WSARECVMSG lpWSARecvMsg = NULL; +/* Map struct WSABUF fields to their posix counterpart */ +#define msghdr _WSAMSG +#define msg_name name +#define msg_namelen namelen +#define msg_iov lpBuffers +#define msg_iovlen dwBufferCount +#define msg_control Control.buf +#define msg_controllen Control.len +#define iovec _WSABUF +#define iov_base buf +#define iov_len len +#define iov_len_t u_long +#undef CMSG_DATA +#define CMSG_DATA WSA_CMSG_DATA +#define ipi_spec_dst ipi_addr +#else +#define iov_len_t size_t +#endif + +ssize_t +coap_network_send(coap_socket_t *sock, const coap_session_t *session, const uint8_t *data, size_t datalen) { + ssize_t bytes_written = 0; + + if (!coap_debug_send_packet()) { + bytes_written = (ssize_t)datalen; +#ifndef WITH_CONTIKI + } else if (sock->flags & COAP_SOCKET_CONNECTED) { +#ifdef _WIN32 + bytes_written = send(sock->fd, (const char *)data, (int)datalen, 0); +#else + bytes_written = send(sock->fd, data, datalen, 0); +#endif +#endif + } else { +#ifndef WITH_CONTIKI +#ifdef _WIN32 + DWORD dwNumberOfBytesSent = 0; + int r; +#endif +#ifndef COAP_BAD_RECVMSG + /* a buffer large enough to hold all packet info types, ipv6 is the largest */ + char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + struct msghdr mhdr; + struct iovec iov[1]; + const void *addr = &session->remote_addr.addr; + + assert(session); + + memcpy (&iov[0].iov_base, &data, sizeof (iov[0].iov_base)); + iov[0].iov_len = (iov_len_t)datalen; + + memset(buf, 0, sizeof (buf)); + + memset(&mhdr, 0, sizeof(struct msghdr)); + memcpy (&mhdr.msg_name, &addr, sizeof (mhdr.msg_name)); + mhdr.msg_namelen = session->remote_addr.size; + + mhdr.msg_iov = iov; + mhdr.msg_iovlen = 1; + + if (!coap_address_isany(&session->local_addr) && !coap_is_mcast(&session->local_addr)) switch (session->local_addr.addr.sa.sa_family) { + case AF_INET6: + { + struct cmsghdr *cmsg; + + if (IN6_IS_ADDR_V4MAPPED(&session->local_addr.addr.sin6.sin6_addr)) { +#if defined(IP_PKTINFO) + struct in_pktinfo *pktinfo; + mhdr.msg_control = buf; + mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); + + cmsg = CMSG_FIRSTHDR(&mhdr); + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + + pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); + + pktinfo->ipi_ifindex = session->ifindex; + memcpy(&pktinfo->ipi_spec_dst, session->local_addr.addr.sin6.sin6_addr.s6_addr + 12, sizeof(pktinfo->ipi_spec_dst)); +#elif defined(IP_SENDSRCADDR) + mhdr.msg_control = buf; + mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); + + cmsg = CMSG_FIRSTHDR(&mhdr); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + + memcpy(CMSG_DATA(cmsg), session->local_addr.addr.sin6.sin6_addr.s6_addr + 12, sizeof(struct in_addr)); +#endif /* IP_PKTINFO */ + } else { + struct in6_pktinfo *pktinfo; + mhdr.msg_control = buf; + mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); + + cmsg = CMSG_FIRSTHDR(&mhdr); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + + pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); + + pktinfo->ipi6_ifindex = session->ifindex; + memcpy(&pktinfo->ipi6_addr, &session->local_addr.addr.sin6.sin6_addr, sizeof(pktinfo->ipi6_addr)); + } + break; + } + case AF_INET: + { +#if defined(IP_PKTINFO) + struct cmsghdr *cmsg; + struct in_pktinfo *pktinfo; + + mhdr.msg_control = buf; + mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); + + cmsg = CMSG_FIRSTHDR(&mhdr); + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + + pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); + + pktinfo->ipi_ifindex = session->ifindex; + memcpy(&pktinfo->ipi_spec_dst, &session->local_addr.addr.sin.sin_addr, sizeof(pktinfo->ipi_spec_dst)); +#elif defined(IP_SENDSRCADDR) + struct cmsghdr *cmsg; + mhdr.msg_control = buf; + mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); + + cmsg = CMSG_FIRSTHDR(&mhdr); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + + memcpy(CMSG_DATA(cmsg), &session->local_addr.addr.sin.sin_addr, sizeof(struct in_addr)); +#endif /* IP_PKTINFO */ + break; + } + default: + /* error */ + coap_log(LOG_WARNING, "protocol not supported\n"); + bytes_written = -1; + } +#endif /* ! COAP_BAD_RECVMSG */ + +#ifdef _WIN32 + r = WSASendMsg(sock->fd, &mhdr, 0 /*dwFlags*/, &dwNumberOfBytesSent, NULL /*lpOverlapped*/, NULL /*lpCompletionRoutine*/); + if (r == 0) + bytes_written = (ssize_t)dwNumberOfBytesSent; + else + bytes_written = -1; +#else +#ifndef COAP_BAD_RECVMSG + bytes_written = sendmsg(sock->fd, &mhdr, 0); +#else /* COAP_BAD_RECVMSG */ + bytes_written = sendto(sock->fd, data, datalen, 0, &session->remote_addr.addr.sa, session->remote_addr.size); +#endif /* COAP_BAD_RECVMSG */ +#endif +#else /* WITH_CONTIKI */ + /* FIXME: untested */ + /* FIXME: is there a way to check if send was successful? */ + (void)datalen; + (void)data; + uip_udp_packet_sendto((struct uip_udp_conn *)sock->conn, data, datalen, + &session->remote_addr.addr, session->remote_addr.port); + bytes_written = datalen; +#endif /* WITH_CONTIKI */ + } + + if (bytes_written < 0) + coap_log(LOG_CRIT, "coap_network_send: %s\n", coap_socket_strerror()); + + return bytes_written; +} + +#define SIN6(A) ((struct sockaddr_in6 *)(A)) + +void +coap_packet_get_memmapped(coap_packet_t *packet, unsigned char **address, size_t *length) { + *address = packet->payload; + *length = packet->length; +} + +void coap_packet_set_addr(coap_packet_t *packet, const coap_address_t *src, const coap_address_t *dst) { + coap_address_copy(&packet->src, src); + coap_address_copy(&packet->dst, dst); +} + +ssize_t +coap_network_read(coap_socket_t *sock, coap_packet_t *packet) { + ssize_t len = -1; + + assert(sock); + assert(packet); + + if ((sock->flags & COAP_SOCKET_CAN_READ) == 0) { + return -1; + } else { + /* clear has-data flag */ + sock->flags &= ~COAP_SOCKET_CAN_READ; + } + +#ifndef WITH_CONTIKI + if (sock->flags & COAP_SOCKET_CONNECTED) { +#ifdef _WIN32 + len = recv(sock->fd, (char *)packet->payload, COAP_RXBUFFER_SIZE, 0); +#else + len = recv(sock->fd, packet->payload, COAP_RXBUFFER_SIZE, 0); +#endif + if (len < 0) { +#ifdef _WIN32 + if (WSAGetLastError() == WSAECONNRESET) { +#else + if (errno == ECONNREFUSED) { +#endif + /* client-side ICMP destination unreachable, ignore it */ + coap_log(LOG_WARNING, "coap_network_read: unreachable\n"); + return -2; + } + coap_log(LOG_WARNING, "coap_network_read: %s\n", coap_socket_strerror()); + goto error; + } else if (len > 0) { + packet->length = (size_t)len; + } + } else { +#endif /* WITH_CONTIKI */ +#if defined(_WIN32) + DWORD dwNumberOfBytesRecvd = 0; + int r; +#endif +#if !defined(WITH_CONTIKI) +#ifndef COAP_BAD_RECVMSG + /* a buffer large enough to hold all packet info types, ipv6 is the largest */ + char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + struct msghdr mhdr; + struct iovec iov[1]; + + iov[0].iov_base = packet->payload; + iov[0].iov_len = (iov_len_t)COAP_RXBUFFER_SIZE; + + memset(&mhdr, 0, sizeof(struct msghdr)); + + mhdr.msg_name = (struct sockaddr*)&packet->src.addr; + mhdr.msg_namelen = sizeof(packet->src.addr); + + mhdr.msg_iov = iov; + mhdr.msg_iovlen = 1; + + mhdr.msg_control = buf; + mhdr.msg_controllen = sizeof(buf); + +#if defined(_WIN32) + if (!lpWSARecvMsg) { + GUID wsaid = WSAID_WSARECVMSG; + DWORD cbBytesReturned = 0; + if (WSAIoctl(sock->fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &wsaid, sizeof(wsaid), &lpWSARecvMsg, sizeof(lpWSARecvMsg), &cbBytesReturned, NULL, NULL) != 0) { + coap_log(LOG_WARNING, "coap_network_read: no WSARecvMsg\n"); + return -1; + } + } + r = lpWSARecvMsg(sock->fd, &mhdr, &dwNumberOfBytesRecvd, NULL /* LPWSAOVERLAPPED */, NULL /* LPWSAOVERLAPPED_COMPLETION_ROUTINE */); + if (r == 0) + len = (ssize_t)dwNumberOfBytesRecvd; +#else + len = recvmsg(sock->fd, &mhdr, 0); +#endif + +#else /* COAP_BAD_RECVMSG */ + packet->src.size = packet->src.size; + len = recvfrom(sock->fd, packet->payload, COAP_RXBUFFER_SIZE, 0, &packet->src.addr.sa, &packet->src.size); +#endif /* COAP_BAD_RECVMSG */ + + if (len < 0) { +#ifdef _WIN32 + if (WSAGetLastError() == WSAECONNRESET) { +#else + if (errno == ECONNREFUSED) { +#endif + /* server-side ICMP destination unreachable, ignore it. The destination address is in msg_name. */ + return 0; + } + coap_log(LOG_WARNING, "coap_network_read: %s\n", coap_socket_strerror()); + goto error; + } else { +#ifndef COAP_BAD_RECVMSG + struct cmsghdr *cmsg; + + packet->src.size = mhdr.msg_namelen; + packet->length = (size_t)len; + + /* Walk through ancillary data records until the local interface + * is found where the data was received. */ + for (cmsg = CMSG_FIRSTHDR(&mhdr); cmsg; cmsg = CMSG_NXTHDR(&mhdr, cmsg)) { + + /* get the local interface for IPv6 */ + if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + union { + uint8_t *c; + struct in6_pktinfo *p; + } u; + u.c = CMSG_DATA(cmsg); + packet->ifindex = (int)(u.p->ipi6_ifindex); + memcpy(&packet->dst.addr.sin6.sin6_addr, &u.p->ipi6_addr, sizeof(struct in6_addr)); + break; + } + + /* local interface for IPv4 */ +#if defined(IP_PKTINFO) + if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO) { + union { + uint8_t *c; + struct in_pktinfo *p; + } u; + u.c = CMSG_DATA(cmsg); + packet->ifindex = u.p->ipi_ifindex; + if (packet->dst.addr.sa.sa_family == AF_INET6) { + memset(packet->dst.addr.sin6.sin6_addr.s6_addr, 0, 10); + packet->dst.addr.sin6.sin6_addr.s6_addr[10] = 0xff; + packet->dst.addr.sin6.sin6_addr.s6_addr[11] = 0xff; + memcpy(packet->dst.addr.sin6.sin6_addr.s6_addr + 12, &u.p->ipi_addr, sizeof(struct in_addr)); + } else { + memcpy(&packet->dst.addr.sin.sin_addr, &u.p->ipi_addr, sizeof(struct in_addr)); + } + break; + } +#elif defined(IP_RECVDSTADDR) + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { + packet->ifindex = 0; + memcpy(&packet->dst.addr.sin.sin_addr, CMSG_DATA(cmsg), sizeof(struct in_addr)); + break; + } +#endif /* IP_PKTINFO */ + } +#else /* COAP_BAD_RECVMSG */ + packet->length = (size_t)len; + packet->ifindex = 0; + if (getsockname(sock->fd, &packet->dst.addr.sa, &packet->dst.size) < 0) { + coap_log(LOG_DEBUG, "Cannot determine local port\n"); + goto error; + } +#endif /* COAP_BAD_RECVMSG */ + } +#endif /* !defined(WITH_CONTIKI) */ +#ifdef WITH_CONTIKI + /* FIXME: untested, make this work */ +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLIPH_LEN]) + + if (uip_newdata()) { + uip_ipaddr_copy(&packet->src.addr, &UIP_IP_BUF->srcipaddr); + packet->src.port = UIP_UDP_BUF->srcport; + uip_ipaddr_copy(&(packet)->dst.addr, &UIP_IP_BUF->destipaddr); + packet->dst.port = UIP_UDP_BUF->destport; + + len = uip_datalen(); + + if (len > COAP_RXBUFFER_SIZE) { + /* FIXME: we might want to send back a response */ + coap_log(LOG_WARNING, "discarded oversized packet\n"); + return -1; + } + + ((char *)uip_appdata)[len] = 0; +#ifndef NDEBUG + if (LOG_DEBUG <= coap_get_log_level()) { +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 40 +#endif + unsigned char addr_str[INET6_ADDRSTRLEN + 8]; + + if (coap_print_addr(&packet->src, addr_str, INET6_ADDRSTRLEN + 8)) { + coap_log(LOG_DEBUG, "received %zd bytes from %s\n", len, addr_str); + } + } +#endif /* NDEBUG */ + + packet->length = len; + memcpy(&packet->payload, uip_appdata, len); + } + +#undef UIP_IP_BUF +#undef UIP_UDP_BUF +#endif /* WITH_CONTIKI */ +#ifndef WITH_CONTIKI + } +#endif /* WITH_CONTIKI */ + + if (len >= 0) + return len; +#if !defined(WITH_CONTIKI) +error: +#endif + return -1; +} + +#if !defined(WITH_CONTIKI) + +unsigned int +coap_write(coap_context_t *ctx, + coap_socket_t *sockets[], + unsigned int max_sockets, + unsigned int *num_sockets, + coap_tick_t now) +{ + coap_queue_t *nextpdu; + coap_endpoint_t *ep; + coap_session_t *s; + coap_tick_t session_timeout; + coap_tick_t timeout = 0; + coap_session_t *tmp; + + *num_sockets = 0; + + /* Check to see if we need to send off any Observe requests */ + coap_check_notify(ctx); + + if (ctx->session_timeout > 0) + session_timeout = ctx->session_timeout * COAP_TICKS_PER_SECOND; + else + session_timeout = COAP_DEFAULT_SESSION_TIMEOUT * COAP_TICKS_PER_SECOND; + + LL_FOREACH(ctx->endpoint, ep) { + if (ep->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_ACCEPT)) { + if (*num_sockets < max_sockets) + sockets[(*num_sockets)++] = &ep->sock; + } + LL_FOREACH_SAFE(ep->sessions, s, tmp) { + if (s->type == COAP_SESSION_TYPE_SERVER && s->ref == 0 && + s->delayqueue == NULL && + (s->last_rx_tx + session_timeout <= now || + s->state == COAP_SESSION_STATE_NONE)) { + coap_session_free(s); + } else { + if (s->type == COAP_SESSION_TYPE_SERVER && s->ref == 0 && s->delayqueue == NULL) { + coap_tick_t s_timeout = (s->last_rx_tx + session_timeout) - now; + if (timeout == 0 || s_timeout < timeout) + timeout = s_timeout; + } + if (s->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_WRITE)) { + if (*num_sockets < max_sockets) + sockets[(*num_sockets)++] = &s->sock; + } + } + } + } + LL_FOREACH_SAFE(ctx->sessions, s, tmp) { + if ( + s->type == COAP_SESSION_TYPE_CLIENT + && COAP_PROTO_RELIABLE(s->proto) + && s->state == COAP_SESSION_STATE_ESTABLISHED + && ctx->ping_timeout > 0 + ) { + coap_tick_t s_timeout; + if (s->last_rx_tx + ctx->ping_timeout * COAP_TICKS_PER_SECOND <= now) { + if ((s->last_ping > 0 && s->last_pong < s->last_ping) + || coap_session_send_ping(s) == COAP_INVALID_TID) + { + /* Make sure the session object is not deleted in the callback */ + coap_session_reference(s); + coap_session_disconnected(s, COAP_NACK_NOT_DELIVERABLE); + coap_session_release(s); + continue; + } + s->last_rx_tx = now; + s->last_ping = now; + } + s_timeout = (s->last_rx_tx + ctx->ping_timeout * COAP_TICKS_PER_SECOND) - now; + if (timeout == 0 || s_timeout < timeout) + timeout = s_timeout; + } + + if ( + s->type == COAP_SESSION_TYPE_CLIENT + && COAP_PROTO_RELIABLE(s->proto) + && s->state == COAP_SESSION_STATE_CSM + && ctx->csm_timeout > 0 + ) { + coap_tick_t s_timeout; + if (s->csm_tx == 0) { + s->csm_tx = now; + } else if (s->csm_tx + ctx->csm_timeout * COAP_TICKS_PER_SECOND <= now) { + /* Make sure the session object is not deleted in the callback */ + coap_session_reference(s); + coap_session_disconnected(s, COAP_NACK_NOT_DELIVERABLE); + coap_session_release(s); + continue; + } + s_timeout = (s->csm_tx + ctx->csm_timeout * COAP_TICKS_PER_SECOND) - now; + if (timeout == 0 || s_timeout < timeout) + timeout = s_timeout; + } + + if (s->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_CONNECT)) { + if (*num_sockets < max_sockets) + sockets[(*num_sockets)++] = &s->sock; + } + } + + nextpdu = coap_peek_next(ctx); + + while (nextpdu && now >= ctx->sendqueue_basetime && nextpdu->t <= now - ctx->sendqueue_basetime) { + coap_retransmit(ctx, coap_pop_next(ctx)); + nextpdu = coap_peek_next(ctx); + } + + if (nextpdu && (timeout == 0 || nextpdu->t - ( now - ctx->sendqueue_basetime ) < timeout)) + timeout = nextpdu->t - (now - ctx->sendqueue_basetime); + + if (ctx->dtls_context) { + if (coap_dtls_is_context_timeout()) { + coap_tick_t tls_timeout = coap_dtls_get_context_timeout(ctx->dtls_context); + if (tls_timeout > 0) { + if (tls_timeout < now + COAP_TICKS_PER_SECOND / 10) + tls_timeout = now + COAP_TICKS_PER_SECOND / 10; + coap_log(LOG_DEBUG, "** DTLS global timeout set to %dms\n", + (int)((tls_timeout - now) * 1000 / COAP_TICKS_PER_SECOND)); + if (timeout == 0 || tls_timeout - now < timeout) + timeout = tls_timeout - now; + } + } else { + LL_FOREACH(ctx->endpoint, ep) { + if (ep->proto == COAP_PROTO_DTLS) { + LL_FOREACH(ep->sessions, s) { + if (s->proto == COAP_PROTO_DTLS && s->tls) { + coap_tick_t tls_timeout = coap_dtls_get_timeout(s); + while (tls_timeout > 0 && tls_timeout <= now) { + coap_log(LOG_DEBUG, "** %s: DTLS retransmit timeout\n", + coap_session_str(s)); + coap_dtls_handle_timeout(s); + if (s->tls) + tls_timeout = coap_dtls_get_timeout(s); + else { + tls_timeout = 0; + timeout = 1; + } + } + if (tls_timeout > 0 && (timeout == 0 || tls_timeout - now < timeout)) + timeout = tls_timeout - now; + } + } + } + } + LL_FOREACH(ctx->sessions, s) { + if (s->proto == COAP_PROTO_DTLS && s->tls) { + coap_tick_t tls_timeout = coap_dtls_get_timeout(s); + while (tls_timeout > 0 && tls_timeout <= now) { + coap_log(LOG_DEBUG, "** %s: DTLS retransmit timeout\n", coap_session_str(s)); + coap_dtls_handle_timeout(s); + if (s->tls) + tls_timeout = coap_dtls_get_timeout(s); + else { + tls_timeout = 0; + timeout = 1; + } + } + if (tls_timeout > 0 && (timeout == 0 || tls_timeout - now < timeout)) + timeout = tls_timeout - now; + } + } + } + } + + return (unsigned int)((timeout * 1000 + COAP_TICKS_PER_SECOND - 1) / COAP_TICKS_PER_SECOND); +} + +int +coap_run_once(coap_context_t *ctx, unsigned timeout_ms) { + fd_set readfds, writefds, exceptfds; + coap_fd_t nfds = 0; + struct timeval tv; + coap_tick_t before, now; + int result; + coap_socket_t *sockets[64]; + unsigned int num_sockets = 0, i, timeout; + + coap_ticks(&before); + + timeout = coap_write(ctx, sockets, (unsigned int)(sizeof(sockets) / sizeof(sockets[0])), &num_sockets, before); + if (timeout == 0 || timeout_ms < timeout) + timeout = timeout_ms; + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + for (i = 0; i < num_sockets; i++) { + if (sockets[i]->fd + 1 > nfds) + nfds = sockets[i]->fd + 1; + if (sockets[i]->flags & COAP_SOCKET_WANT_READ) + FD_SET(sockets[i]->fd, &readfds); + if (sockets[i]->flags & COAP_SOCKET_WANT_WRITE) + FD_SET(sockets[i]->fd, &writefds); + if (sockets[i]->flags & COAP_SOCKET_WANT_ACCEPT) + FD_SET(sockets[i]->fd, &readfds); + if (sockets[i]->flags & COAP_SOCKET_WANT_CONNECT) { + FD_SET(sockets[i]->fd, &writefds); + FD_SET(sockets[i]->fd, &exceptfds); + } + } + + if ( timeout > 0 ) { + tv.tv_usec = (timeout % 1000) * 1000; + tv.tv_sec = (long)(timeout / 1000); + } + + result = select(nfds, &readfds, &writefds, &exceptfds, timeout > 0 ? &tv : NULL); + + if (result < 0) { /* error */ +#ifdef _WIN32 + if (WSAGetLastError() != WSAEINVAL) { /* May happen because of ICMP */ +#else + if (errno != EINTR) { +#endif + coap_log(LOG_DEBUG, "%s", coap_socket_strerror()); + return -1; + } + } + + if (result > 0) { + for (i = 0; i < num_sockets; i++) { + if ((sockets[i]->flags & COAP_SOCKET_WANT_READ) && FD_ISSET(sockets[i]->fd, &readfds)) + sockets[i]->flags |= COAP_SOCKET_CAN_READ; + if ((sockets[i]->flags & COAP_SOCKET_WANT_ACCEPT) && FD_ISSET(sockets[i]->fd, &readfds)) + sockets[i]->flags |= COAP_SOCKET_CAN_ACCEPT; + if ((sockets[i]->flags & COAP_SOCKET_WANT_WRITE) && FD_ISSET(sockets[i]->fd, &writefds)) + sockets[i]->flags |= COAP_SOCKET_CAN_WRITE; + if ((sockets[i]->flags & COAP_SOCKET_WANT_CONNECT) && (FD_ISSET(sockets[i]->fd, &writefds) || FD_ISSET(sockets[i]->fd, &exceptfds))) + sockets[i]->flags |= COAP_SOCKET_CAN_CONNECT; + } + } + + coap_ticks(&now); + coap_read(ctx, now); + + return (int)(((now - before) * 1000) / COAP_TICKS_PER_SECOND); +} + +#else +int coap_run_once(coap_context_t *ctx, unsigned int timeout_ms) { + return -1; +} + +unsigned int +coap_write(coap_context_t *ctx, + coap_socket_t *sockets[], + unsigned int max_sockets, + unsigned int *num_sockets, + coap_tick_t now) +{ + *num_sockets = 0; + return 0; +} +#endif + +#ifdef _WIN32 +static const char *coap_socket_format_errno(int error) { + static char szError[256]; + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD)error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)szError, (DWORD)sizeof(szError), NULL) == 0) + strcpy(szError, "Unknown error"); + return szError; +} + +const char *coap_socket_strerror(void) { + return coap_socket_format_errno(WSAGetLastError()); +} +#else +#ifndef WITH_CONTIKI +static const char *coap_socket_format_errno(int error) { + return strerror(error); +} +#endif /* WITH_CONTIKI */ + +const char *coap_socket_strerror(void) { + return strerror(errno); +} +#endif + +ssize_t +coap_socket_send(coap_socket_t *sock, coap_session_t *session, + const uint8_t *data, size_t data_len) { + return session->context->network_send(sock, session, data, data_len); +} + +#undef SIN6 diff --git a/components/coap/port/coap_io_socket.c b/components/coap/port/coap_io_socket.c deleted file mode 100644 index 4c3f85b45..000000000 --- a/components/coap/port/coap_io_socket.c +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Network function implementation with socket for ESP32 platform. - * - * Uses libcoap software implementation for failover when concurrent - * network operations are in use. - * - * coap_io.h -- Default network I/O functions for libcoap - * - * Copyright (C) 2012,2014 Olaf Bergmann - * - * Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD - * - * This file is part of the CoAP library libcoap. Please see - * README for terms of use. - */ - -#include "coap_config.h" - -#ifdef HAVE_STDIO_H -# include -#endif - -#ifdef HAVE_SYS_SELECT_H -# include -#endif -#ifdef HAVE_SYS_SOCKET_H -# include -#endif -#ifdef HAVE_NETINET_IN_H -# include -#endif -#ifdef HAVE_SYS_UIO_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif -#include - -#ifdef WITH_CONTIKI -# include "uip.h" -#endif - -#include "pdu.h" -#include "debug.h" -#include "mem.h" -#include "coap_io.h" - -#ifdef WITH_POSIX -/* define generic PKTINFO for IPv4 */ -#if defined(IP_PKTINFO) -# define GEN_IP_PKTINFO IP_PKTINFO -#elif defined(IP_RECVDSTADDR) -# define GEN_IP_PKTINFO IP_RECVDSTADDR -#else -# error "Need IP_PKTINFO or IP_RECVDSTADDR to request ancillary data from OS." -#endif /* IP_PKTINFO */ - -/* define generic KTINFO for IPv6 */ -#ifdef IPV6_RECVPKTINFO -# define GEN_IPV6_PKTINFO IPV6_RECVPKTINFO -#elif defined(IPV6_PKTINFO) -# define GEN_IPV6_PKTINFO IPV6_PKTINFO -#else -# error "Need IPV6_PKTINFO or IPV6_RECVPKTINFO to request ancillary data from OS." -#endif /* IPV6_RECVPKTINFO */ - -struct coap_packet_t { - coap_if_handle_t hnd; /**< the interface handle */ - coap_address_t src; /**< the packet's source address */ - coap_address_t dst; /**< the packet's destination address */ - const coap_endpoint_t *interface; - - int ifindex; - void *session; /**< opaque session data */ - - size_t length; /**< length of payload */ - unsigned char payload[]; /**< payload */ -}; -#endif - -#ifdef CUSTOM_COAP_NETWORK_ENDPOINT - -#ifdef WITH_CONTIKI -static int ep_initialized = 0; - -static inline struct coap_endpoint_t * -coap_malloc_contiki_endpoint() { - static struct coap_endpoint_t ep; - - if (ep_initialized) { - return NULL; - } else { - ep_initialized = 1; - return &ep; - } -} - -static inline void -coap_free_contiki_endpoint(struct coap_endpoint_t *ep) { - ep_initialized = 0; -} - -coap_endpoint_t * -coap_new_endpoint(const coap_address_t *addr, int flags) { - struct coap_endpoint_t *ep = coap_malloc_contiki_endpoint(); - - if (ep) { - memset(ep, 0, sizeof(struct coap_endpoint_t)); - ep->handle.conn = udp_new(NULL, 0, NULL); - - if (!ep->handle.conn) { - coap_free_endpoint(ep); - return NULL; - } - - coap_address_init(&ep->addr); - uip_ipaddr_copy(&ep->addr.addr, &addr->addr); - ep->addr.port = addr->port; - udp_bind((struct uip_udp_conn *)ep->handle.conn, addr->port); - } - return ep; -} - -void -coap_free_endpoint(coap_endpoint_t *ep) { - if (ep) { - if (ep->handle.conn) { - uip_udp_remove((struct uip_udp_conn *)ep->handle.conn); - } - coap_free_contiki_endpoint(ep); - } -} - -#else /* WITH_CONTIKI */ -static inline struct coap_endpoint_t * -coap_malloc_posix_endpoint(void) { - return (struct coap_endpoint_t *)coap_malloc(sizeof(struct coap_endpoint_t)); -} - -static inline void -coap_free_posix_endpoint(struct coap_endpoint_t *ep) { - coap_free(ep); -} - -coap_endpoint_t * -coap_new_endpoint(const coap_address_t *addr, int flags) { - int sockfd = socket(addr->addr.sa.sa_family, SOCK_DGRAM, 0); - int on = 1; - struct coap_endpoint_t *ep; - - if (sockfd < 0) { - coap_log(LOG_WARNING, "coap_new_endpoint: socket"); - return NULL; - } - - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) - coap_log(LOG_WARNING, "coap_new_endpoint: setsockopt SO_REUSEADDR"); - - if (bind(sockfd, &addr->addr.sa, addr->size) < 0) { - coap_log(LOG_WARNING, "coap_new_endpoint: bind"); - close (sockfd); - return NULL; - } - - ep = coap_malloc_posix_endpoint(); - if (!ep) { - coap_log(LOG_WARNING, "coap_new_endpoint: malloc"); - close(sockfd); - return NULL; - } - - memset(ep, 0, sizeof(struct coap_endpoint_t)); - ep->handle.fd = sockfd; - ep->flags = flags; - - ep->addr.size = addr->size; - if (getsockname(sockfd, &ep->addr.addr.sa, &ep->addr.size) < 0) { - coap_log(LOG_WARNING, "coap_new_endpoint: cannot determine local address"); - close (sockfd); - return NULL; - } - -#ifndef NDEBUG - if (LOG_DEBUG <= coap_get_log_level()) { -#ifndef INET6_ADDRSTRLEN -#define INET6_ADDRSTRLEN 40 -#endif - unsigned char addr_str[INET6_ADDRSTRLEN+8]; - - if (coap_print_addr(&ep->addr, addr_str, INET6_ADDRSTRLEN+8)) { - debug("created %sendpoint %s\n", - ep->flags & COAP_ENDPOINT_DTLS ? "DTLS " : "", - addr_str); - } - } -#endif /* NDEBUG */ - - return (coap_endpoint_t *)ep; -} - -void -coap_free_endpoint(coap_endpoint_t *ep) { - if(ep) { - if (ep->handle.fd >= 0) - close(ep->handle.fd); - coap_free_posix_endpoint((struct coap_endpoint_t *)ep); - } -} - -#endif /* WITH_CONTIKI */ -#endif /* CUSTOM_COAP_NETWORK_ENDPOINT */ - -#ifdef CUSTOM_COAP_NETWORK_SEND - -#if defined(WITH_POSIX) != defined(HAVE_NETINET_IN_H) -/* define struct in6_pktinfo and struct in_pktinfo if not available - FIXME: check with configure -*/ -struct in6_pktinfo { - struct in6_addr ipi6_addr; /* src/dst IPv6 address */ - unsigned int ipi6_ifindex; /* send/recv interface index */ -}; - -struct in_pktinfo { - int ipi_ifindex; - struct in_addr ipi_spec_dst; - struct in_addr ipi_addr; -}; -#endif - -#if defined(WITH_POSIX) && !defined(SOL_IP) -/* Solaris expects level IPPROTO_IP for ancillary data. */ -#define SOL_IP IPPROTO_IP -#endif - -#ifdef __GNUC__ -#define UNUSED_PARAM __attribute__ ((unused)) -#else /* not a GCC */ -#define UNUSED_PARAM -#endif /* GCC */ - -ssize_t -coap_network_send(struct coap_context_t *context UNUSED_PARAM, - const coap_endpoint_t *local_interface, - const coap_address_t *dst, - unsigned char *data, - size_t datalen) { - - struct coap_endpoint_t *ep = - (struct coap_endpoint_t *)local_interface; - -#ifndef WITH_CONTIKI - return sendto(ep->handle.fd, data, datalen, 0, (struct sockaddr*)&dst->addr.sa, sizeof(struct sockaddr)); -#else /* WITH_CONTIKI */ - /* FIXME: untested */ - /* FIXME: is there a way to check if send was successful? */ - uip_udp_packet_sendto((struct uip_udp_conn *)ep->handle.conn, data, datalen, - &dst->addr, dst->port); - return datalen; -#endif /* WITH_CONTIKI */ -} - -#endif /* CUSTOM_COAP_NETWORK_SEND */ - -#ifdef CUSTOM_COAP_NETWORK_READ - -#define SIN6(A) ((struct sockaddr_in6 *)(A)) - -#ifdef WITH_POSIX -static coap_packet_t * -coap_malloc_packet(void) { - coap_packet_t *packet; - const size_t need = sizeof(coap_packet_t) + COAP_MAX_PDU_SIZE; - - packet = (coap_packet_t *)coap_malloc(need); - if (packet) { - memset(packet, 0, need); - } - return packet; -} - -void -coap_free_packet(coap_packet_t *packet) { - coap_free(packet); -} -#endif /* WITH_POSIX */ -#ifdef WITH_CONTIKI -static inline coap_packet_t * -coap_malloc_packet(void) { - return (coap_packet_t *)coap_malloc_type(COAP_PACKET, 0); -} - -void -coap_free_packet(coap_packet_t *packet) { - coap_free_type(COAP_PACKET, packet); -} -#endif /* WITH_CONTIKI */ - -static inline size_t -coap_get_max_packetlength(const coap_packet_t *packet UNUSED_PARAM) { - return COAP_MAX_PDU_SIZE; -} - -void -coap_packet_populate_endpoint(coap_packet_t *packet, coap_endpoint_t *target) -{ - target->handle = packet->interface->handle; - memcpy(&target->addr, &packet->dst, sizeof(target->addr)); - target->ifindex = packet->ifindex; - target->flags = 0; /* FIXME */ -} -void -coap_packet_copy_source(coap_packet_t *packet, coap_address_t *target) -{ - memcpy(target, &packet->src, sizeof(coap_address_t)); -} -void -coap_packet_get_memmapped(coap_packet_t *packet, unsigned char **address, size_t *length) -{ - *address = packet->payload; - *length = packet->length; -} - -/** - * Checks if a message with destination address @p dst matches the - * local interface with address @p local. This function returns @c 1 - * if @p dst is a valid match, and @c 0 otherwise. - */ -static inline int -is_local_if(const coap_address_t *local, const coap_address_t *dst) { - return coap_address_isany(local) || coap_address_equals(dst, local) || - coap_is_mcast(dst); -} - -ssize_t -coap_network_read(coap_endpoint_t *ep, coap_packet_t **packet) { - ssize_t len = -1; - -#ifdef WITH_POSIX - #define SOC_APPDATA_LEN 1460 - char *soc_appdata = NULL; - struct sockaddr_in soc_srcipaddr; - socklen_t soc_srcsize = sizeof(struct sockaddr_in); -#endif /* WITH_POSIX */ - - assert(ep); - assert(packet); - - *packet = coap_malloc_packet(); - - if (!*packet) { - warn("coap_network_read: insufficient memory, drop packet\n"); - return -1; - } - - coap_address_init(&(*packet)->dst); /* the local interface address */ - coap_address_init(&(*packet)->src); /* the remote peer */ - -#ifdef WITH_POSIX - soc_appdata = coap_malloc(SOC_APPDATA_LEN); - if (soc_appdata){ - len = recvfrom(ep->handle.fd, soc_appdata, SOC_APPDATA_LEN, 0, (struct sockaddr *)&soc_srcipaddr, (socklen_t *)&soc_srcsize); - - if (len < 0){ - coap_log(LOG_WARNING, "coap_network_read: %s\n", strerror(errno)); - goto error; - } else { - /* use getsockname() to get the local port */ - (*packet)->dst.size = sizeof((*packet)->dst.addr); - if (getsockname(ep->handle.fd, &(*packet)->dst.addr.sa, &(*packet)->dst.size) < 0) { - coap_log(LOG_DEBUG, "cannot determine local port\n"); - goto error; - } - - /* local interface for IPv4 */ - (*packet)->src.size = sizeof((*packet)->src.addr.sa); - memcpy(&((*packet)->src.addr.sa), &soc_srcipaddr, (*packet)->src.size); - - if (len > coap_get_max_packetlength(*packet)) { - /* FIXME: we might want to send back a response */ - warn("discarded oversized packet\n"); - goto error; - } - - if (!is_local_if(&ep->addr, &(*packet)->dst)) { - coap_log(LOG_DEBUG, "packet received on wrong interface, dropped\n"); - printf("error 3\n"); - goto error; - } - - (*packet)->length = len; - - memcpy(&(*packet)->payload, soc_appdata, len); - } - - coap_free(soc_appdata); - soc_appdata = NULL; - } else { - goto error; - } -#endif /* WITH_POSIX */ -#ifdef WITH_CONTIKI - /* FIXME: untested, make this work */ -#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) -#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLIPH_LEN]) - - if(uip_newdata()) { - uip_ipaddr_copy(&(*packet)->src.addr, &UIP_IP_BUF->srcipaddr); - (*packet)->src.port = UIP_UDP_BUF->srcport; - uip_ipaddr_copy(&(*packet)->dst.addr, &UIP_IP_BUF->destipaddr); - (*packet)->dst.port = UIP_UDP_BUF->destport; - - if (!is_local_if(&ep->addr, &(*packet)->dst)) { - coap_log(LOG_DEBUG, "packet received on wrong interface, dropped\n"); - goto error; - } - - len = uip_datalen(); - - if (len > coap_get_max_packetlength(*packet)) { - /* FIXME: we might want to send back a response */ - warn("discarded oversized packet\n"); - return -1; - } - - ((char *)uip_appdata)[len] = 0; -#ifndef NDEBUG - if (LOG_DEBUG <= coap_get_log_level()) { -#ifndef INET6_ADDRSTRLEN -#define INET6_ADDRSTRLEN 40 -#endif - unsigned char addr_str[INET6_ADDRSTRLEN+8]; - - if (coap_print_addr(&(*packet)->src, addr_str, INET6_ADDRSTRLEN+8)) { - debug("received %zd bytes from %s\n", len, addr_str); - } - } -#endif /* NDEBUG */ - - (*packet)->length = len; - memcpy(&(*packet)->payload, uip_appdata, len); - } - -#undef UIP_IP_BUF -#undef UIP_UDP_BUF -#endif /* WITH_CONTIKI */ -#ifdef WITH_LWIP -#error "coap_network_read() not implemented on this platform" -#endif - - (*packet)->interface = ep; - - return len; - error: -#ifdef WITH_POSIX - if (soc_appdata) - coap_free(soc_appdata); - soc_appdata = NULL; -#endif - coap_free_packet(*packet); - *packet = NULL; - return -1; -} - -#undef SIN6 - -#endif /* CUSTOM_COAP_NETWORK_READ */ diff --git a/components/coap/port/include/coap/coap.h b/components/coap/port/include/coap/coap.h index cbdc9dfc8..f048ca857 100644 --- a/components/coap/port/include/coap/coap.h +++ b/components/coap/port/include/coap/coap.h @@ -17,19 +17,21 @@ #ifndef _COAP_H_ #define _COAP_H_ -#include "libcoap.h" - #ifdef __cplusplus extern "C" { #endif +#include "libcoap.h" + #include "address.h" #include "async.h" #include "bits.h" #include "block.h" +#include "coap_dtls.h" +#include "coap_event.h" #include "coap_io.h" #include "coap_time.h" -#include "debug.h" +#include "coap_debug.h" #include "encode.h" #include "mem.h" #include "net.h" @@ -40,8 +42,6 @@ extern "C" { #include "str.h" #include "subscribe.h" #include "uri.h" -#include "uthash.h" -#include "utlist.h" #ifdef __cplusplus } diff --git a/components/coap/port/include/coap_config_posix.h b/components/coap/port/include/coap_config_posix.h index a77e97f07..d675b7a58 100644 --- a/components/coap/port/include/coap_config_posix.h +++ b/components/coap/port/include/coap_config_posix.h @@ -25,6 +25,7 @@ #define HAVE_SYS_SOCKET_H #define HAVE_MALLOC #define HAVE_ARPA_INET_H +#define HAVE_TIME_H #define IP_PKTINFO IP_MULTICAST_IF #define IPV6_PKTINFO IPV6_V6ONLY @@ -32,10 +33,7 @@ #define PACKAGE_NAME "libcoap-posix" #define PACKAGE_VERSION "?" -#define CUSTOM_COAP_NETWORK_ENDPOINT -#define CUSTOM_COAP_NETWORK_SEND -#define CUSTOM_COAP_NETWORK_READ - -#endif +#define COAP_BAD_RECVMSG +#endif /* WITH_POSIX */ #endif /* COAP_CONFIG_POSIX_H_ */ diff --git a/examples/protocols/coap_client/README.md b/examples/protocols/coap_client/README.md index 81b589d64..d058e3c31 100644 --- a/examples/protocols/coap_client/README.md +++ b/examples/protocols/coap_client/README.md @@ -37,7 +37,8 @@ make -j4 flash monitor See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. ## Example Output -Prerequisite: we startup a CoAP server on coap server example. +Prerequisite: we startup a CoAP server on coap server example, +or use the default of coap://californium.eclipse.org. and you could receive data from CoAP server if succeed, such as the following log: @@ -54,12 +55,26 @@ I (1692) wifi: pm start, type: 1 I (2582) event: sta ip: 192.168.3.89, mask: 255.255.255.0, gw: 192.168.3.1 I (2582) CoAP_client: Connected to AP -I (2582) CoAP_client: DNS lookup succeeded. IP=192.168.3.84 -Received: Hello World! -E (8102) CoAP_client: select timeout -E (13102) CoAP_client: select timeout +I (2582) CoAP_client: DNS lookup succeeded. IP=104.196.15.150 +Received: +************************************************************ +CoAP RFC 7252 Cf 2.0.0-SNAPSHOT +************************************************************ +This server is using the Eclipse Californium (Cf) CoAP framework +published under EPL+EDL: http://www.eclipse.org/californium/ + +(c) 2014, 2015, 2016 Institute for Pervasive Computing, ETH Zurich and others +************************************************************ ... ``` +## libcoap Documentation +This can be found at https://libcoap.net/doc/reference/4.2.0/ + ## Troubleshooting -* Please make sure Target Url includes valid `host`, `port`, `path`, and begins with `coap://` +* Please make sure Target Url includes valid `host`, optional `port`, optional `path`, and begins +with `coap://` or `coap+tcp://` for a coap server that supports TCP +(not all do including coap+tcp://californium.eclipse.org). + +* libcoap logging can be increased by changing `#define COAP_LOGGING_LEVEL 0` +to `#define COAP_LOGGING_LEVEL 9` diff --git a/examples/protocols/coap_client/main/coap_client_example_main.c b/examples/protocols/coap_client/main/coap_client_example_main.c index e2d1b9684..76810a7fb 100644 --- a/examples/protocols/coap_client/main/coap_client_example_main.c +++ b/examples/protocols/coap_client/main/coap_client_example_main.c @@ -33,7 +33,9 @@ #define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD #define COAP_DEFAULT_TIME_SEC 5 -#define COAP_DEFAULT_TIME_USEC 0 + +/* Set this to 9 to get verbose logging from within libcoap */ +#define COAP_LOGGING_LEVEL 0 /* The examples use uri "coap://californium.eclipse.org" that you can set via 'make menuconfig'. @@ -52,17 +54,88 @@ const static int CONNECTED_BIT = BIT0; const static char *TAG = "CoAP_client"; -static void message_handler(struct coap_context_t *ctx, const coap_endpoint_t *local_interface, const coap_address_t *remote, +static int resp_wait = 1; +static coap_optlist_t *optlist = NULL; +static int wait_ms; + +static void message_handler(coap_context_t *ctx, coap_session_t *session, coap_pdu_t *sent, coap_pdu_t *received, const coap_tid_t id) { unsigned char* data = NULL; size_t data_len; - if (COAP_RESPONSE_CLASS(received->hdr->code) == 2) { - if (coap_get_data(received, &data_len, &data)) { - printf("Received: %s\n", data); + coap_pdu_t *pdu = NULL; + coap_opt_t *block_opt; + coap_opt_iterator_t opt_iter; + unsigned char buf[4]; + coap_optlist_t *option; + coap_tid_t tid; + + if (COAP_RESPONSE_CLASS(received->code) == 2) { + /* Need to see if blocked response */ + block_opt = coap_check_option(received, COAP_OPTION_BLOCK2, &opt_iter); + if (block_opt) { + uint16_t blktype = opt_iter.type; + + if (coap_opt_block_num(block_opt) == 0) { + printf("Received:\n"); + } + if (coap_get_data(received, &data_len, &data)) { + printf("%.*s", (int)data_len, data); + } + if (COAP_OPT_BLOCK_MORE(block_opt)) { + /* more bit is set */ + + /* create pdu with request for next block */ + pdu = coap_new_pdu(session); + if (!pdu) { + ESP_LOGE(TAG, "coap_new_pdu() failed"); + goto clean_up; + } + pdu->type = COAP_MESSAGE_CON; + pdu->tid = coap_new_message_id(session); + pdu->code = COAP_REQUEST_GET; + + /* add URI components from optlist */ + for (option = optlist; option; option = option->next ) { + switch (option->number) { + case COAP_OPTION_URI_HOST : + case COAP_OPTION_URI_PORT : + case COAP_OPTION_URI_PATH : + case COAP_OPTION_URI_QUERY : + coap_add_option(pdu, option->number, option->length, + option->data); + break; + default: + ; /* skip other options */ + } + } + + /* finally add updated block option from response, clear M bit */ + /* blocknr = (blocknr & 0xfffffff7) + 0x10; */ + coap_add_option(pdu, + blktype, + coap_encode_var_safe(buf, sizeof(buf), + ((coap_opt_block_num(block_opt) + 1) << 4) | + COAP_OPT_BLOCK_SZX(block_opt)), buf); + + tid = coap_send(session, pdu); + + if (tid != COAP_INVALID_TID) { + resp_wait = 1; + wait_ms = COAP_DEFAULT_TIME_SEC * 1000; + return; + } + } + printf("\n"); + } else { + if (coap_get_data(received, &data_len, &data)) { + printf("Received: %.*s\n", (int)data_len, data); + } } } +clean_up: + resp_wait = 0; } static void coap_example_task(void *p) @@ -70,17 +143,22 @@ static void coap_example_task(void *p) struct hostent *hp; struct ip4_addr *ip4_addr; - coap_context_t* ctx = NULL; coap_address_t dst_addr, src_addr; static coap_uri_t uri; - fd_set readfds; - struct timeval tv; - int flags, result; - coap_pdu_t* request = NULL; const char* server_uri = COAP_DEFAULT_DEMO_URI; - uint8_t get_method = 1; char* phostname = NULL; + + coap_set_log_level(COAP_LOGGING_LEVEL); while (1) { +#define BUFSIZE 40 + unsigned char _buf[BUFSIZE]; + unsigned char *buf; + size_t buflen; + int res; + coap_context_t *ctx = NULL; + coap_session_t *session = NULL; + coap_pdu_t *request = NULL; + /* Wait for the callback to set the CONNECTED_BIT in the event group. */ @@ -88,11 +166,18 @@ static void coap_example_task(void *p) false, true, portMAX_DELAY); ESP_LOGI(TAG, "Connected to AP"); + optlist = NULL; if (coap_split_uri((const uint8_t *)server_uri, strlen(server_uri), &uri) == -1) { ESP_LOGE(TAG, "CoAP server uri error"); break; } + if ((uri.scheme==COAP_URI_SCHEME_COAPS && !coap_dtls_is_supported()) || + (uri.scheme==COAP_URI_SCHEME_COAPS_TCP && !coap_tls_is_supported())) { + ESP_LOGE(TAG, "CoAP server uri scheme error"); + break; + } + phostname = (char *)calloc(1, uri.host.length + 1); if (phostname == NULL) { @@ -107,6 +192,7 @@ static void coap_example_task(void *p) if (hp == NULL) { ESP_LOGE(TAG, "DNS lookup failed"); vTaskDelay(1000 / portTICK_PERIOD_MS); + free(phostname); continue; } @@ -121,46 +207,94 @@ static void coap_example_task(void *p) src_addr.addr.sin.sin_port = htons(0); src_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; - ctx = coap_new_context(&src_addr); - if (ctx) { - coap_address_init(&dst_addr); - dst_addr.addr.sin.sin_family = AF_INET; - dst_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); - dst_addr.addr.sin.sin_addr.s_addr = ip4_addr->addr; + if (uri.path.length) { + buflen = BUFSIZE; + buf = _buf; + res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen); - request = coap_new_pdu(); - if (request){ - request->hdr->type = COAP_MESSAGE_CON; - request->hdr->id = coap_new_message_id(ctx); - request->hdr->code = get_method; - coap_add_option(request, COAP_OPTION_URI_PATH, uri.path.length, uri.path.s); + while (res--) { + coap_insert_optlist(&optlist, + coap_new_optlist(COAP_OPTION_URI_PATH, + coap_opt_length(buf), + coap_opt_value(buf))); - coap_register_response_handler(ctx, message_handler); - coap_send_confirmed(ctx, ctx->endpoint, &dst_addr, request); - - flags = fcntl(ctx->sockfd, F_GETFL, 0); - fcntl(ctx->sockfd, F_SETFL, flags|O_NONBLOCK); - - tv.tv_usec = COAP_DEFAULT_TIME_USEC; - tv.tv_sec = COAP_DEFAULT_TIME_SEC; - - for(;;) { - FD_ZERO(&readfds); - FD_CLR( ctx->sockfd, &readfds ); - FD_SET( ctx->sockfd, &readfds ); - result = select( ctx->sockfd+1, &readfds, 0, 0, &tv ); - if (result > 0) { - if (FD_ISSET( ctx->sockfd, &readfds )) - coap_read(ctx); - } else if (result < 0) { - break; - } else { - ESP_LOGE(TAG, "select timeout"); - } - } + buf += coap_opt_size(buf); } - coap_free_context(ctx); } + + if (uri.query.length) { + buflen = BUFSIZE; + buf = _buf; + res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen); + + while (res--) { + coap_insert_optlist(&optlist, + coap_new_optlist(COAP_OPTION_URI_QUERY, + coap_opt_length(buf), + coap_opt_value(buf))); + + buf += coap_opt_size(buf); + } + } + + ctx = coap_new_context(NULL); + if (!ctx) { + ESP_LOGE(TAG, "coap_new_context() failed"); + goto clean_up; + } + + coap_address_init(&dst_addr); + dst_addr.addr.sin.sin_family = AF_INET; + dst_addr.addr.sin.sin_port = htons(uri.port); + dst_addr.addr.sin.sin_addr.s_addr = ip4_addr->addr; + + session = coap_new_client_session(ctx, &src_addr, &dst_addr, + uri.scheme==COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP : + uri.scheme==COAP_URI_SCHEME_COAPS_TCP ? COAP_PROTO_TLS : + uri.scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_UDP); + if (!session) { + ESP_LOGE(TAG, "coap_new_client_session() failed"); + goto clean_up; + } + + coap_register_response_handler(ctx, message_handler); + + request = coap_new_pdu(session); + if (!request) { + ESP_LOGE(TAG, "coap_new_pdu() failed"); + goto clean_up; + } + request->type = COAP_MESSAGE_CON; + request->tid = coap_new_message_id(session); + request->code = COAP_REQUEST_GET; + coap_add_optlist_pdu(request, &optlist); + + resp_wait = 1; + coap_send(session, request); + + wait_ms = COAP_DEFAULT_TIME_SEC * 1000; + + while (resp_wait) { + int result = coap_run_once(ctx, wait_ms > 1000 ? 1000 : wait_ms); + if (result >= 0) { + if (result >= wait_ms) { + ESP_LOGE(TAG, "select timeout"); + break; + } else { + wait_ms -= result; + } + } + } +clean_up: + if (optlist) { + coap_delete_optlist(optlist); + optlist = NULL; + } + if (session) coap_session_release(session); + if (ctx) coap_free_context(ctx); + coap_cleanup(); + /* Only send the request off once */ + break; } vTaskDelete(NULL); @@ -210,5 +344,5 @@ void app_main(void) { ESP_ERROR_CHECK( nvs_flash_init() ); wifi_conn_init(); - xTaskCreate(coap_example_task, "coap", 2048, NULL, 5, NULL); + xTaskCreate(coap_example_task, "coap", 10240, NULL, 5, NULL); } diff --git a/examples/protocols/coap_server/README.md b/examples/protocols/coap_server/README.md index 560873635..44366936d 100644 --- a/examples/protocols/coap_server/README.md +++ b/examples/protocols/coap_server/README.md @@ -51,13 +51,18 @@ I (1692) wifi: pm start, type: 1 I (2622) event: sta ip: 192.168.3.84, mask: 255.255.255.0, gw: 192.168.3.1 I (2622) CoAP_server: Connected to AP -E (7622) CoAP_server: select timeout -E (12622) CoAP_server: select timeout -E (17622) CoAP_server: select timeout ... ``` -if a CoAP client query `/Espressif` resource, CoAP server would return `"Hello World!"` +if a CoAP client query `/Espressif` resource, CoAP server would return `"no data"` +until a CoAP client does a PUT with some data. + +## libcoap Documentation +This can be found at https://libcoap.net/doc/reference/4.2.0/ ## Troubleshooting -* Please make sure CoAP client fetchs data under path: `/Espressif` +* Please make sure CoAP client fetchs or puts data under path: `/Espressif` or +fetches `/.well-known/core` + +* libcoap logging can be increased by changing `#define COAP_LOGGING_LEVEL 0` +to `#define COAP_LOGGING_LEVEL 9` diff --git a/examples/protocols/coap_server/main/coap_server_example_main.c b/examples/protocols/coap_server/main/coap_server_example_main.c index 0d2ce0a28..507a815c2 100644 --- a/examples/protocols/coap_server/main/coap_server_example_main.c +++ b/examples/protocols/coap_server/main/coap_server_example_main.c @@ -31,8 +31,8 @@ #define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID #define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD -#define COAP_DEFAULT_TIME_SEC 5 -#define COAP_DEFAULT_TIME_USEC 0 +/* Set this to 9 to get verbose logging from within libcoap */ +#define COAP_LOGGING_LEVEL 0 static EventGroupHandle_t wifi_event_group; @@ -42,53 +42,86 @@ static EventGroupHandle_t wifi_event_group; const static int CONNECTED_BIT = BIT0; const static char *TAG = "CoAP_server"; - -static coap_async_state_t *async = NULL; - -static void -send_async_response(coap_context_t *ctx, const coap_endpoint_t *local_if) -{ - coap_pdu_t *response; - unsigned char buf[3]; - const char* response_data = "Hello World!"; - response = coap_pdu_init(async->flags & COAP_MESSAGE_CON, COAP_RESPONSE_CODE(205), 0, COAP_MAX_PDU_SIZE); - response->hdr->id = coap_new_message_id(ctx); - if (async->tokenlen) - coap_add_token(response, async->tokenlen, async->token); - coap_add_option(response, COAP_OPTION_CONTENT_TYPE, coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf); - coap_add_data (response, strlen(response_data), (unsigned char *)response_data); - - if (coap_send(ctx, local_if, &async->peer, response) == COAP_INVALID_TID) { - - } - coap_delete_pdu(response); - coap_async_state_t *tmp; - coap_remove_async(ctx, async->id, &tmp); - coap_free_async(async); - async = NULL; -} +static char espressif_data[100]; +static int espressif_data_len = 0; /* * The resource handler */ static void -async_handler(coap_context_t *ctx, struct coap_resource_t *resource, - const coap_endpoint_t *local_interface, coap_address_t *peer, - coap_pdu_t *request, str *token, coap_pdu_t *response) +hnd_espressif_get(coap_context_t *ctx, coap_resource_t *resource, + coap_session_t *session, + coap_pdu_t *request, coap_binary_t *token, + coap_string_t *query, coap_pdu_t *response) { - async = coap_register_async(ctx, peer, request, COAP_ASYNC_SEPARATE | COAP_ASYNC_CONFIRM, (void*)"no data"); + coap_add_data_blocked_response(resource, session, request, response, token, + COAP_MEDIATYPE_TEXT_PLAIN, 0, + (size_t)espressif_data_len, + (const u_char *)espressif_data); +} + +static void +hnd_espressif_put(coap_context_t *ctx, + coap_resource_t *resource, + coap_session_t *session, + coap_pdu_t *request, + coap_binary_t *token, + coap_string_t *query, + coap_pdu_t *response) +{ + size_t size; + unsigned char *data; + + coap_resource_notify_observers(resource, NULL); + + if (strcmp (espressif_data, "no data") == 0) { + response->code = COAP_RESPONSE_CODE(201); + } + else { + response->code = COAP_RESPONSE_CODE(204); + } + + /* coap_get_data() sets size to 0 on error */ + (void)coap_get_data(request, &size, &data); + + if (size == 0) { /* re-init */ + snprintf(espressif_data, sizeof(espressif_data), "no data"); + espressif_data_len = strlen(espressif_data); + } else { + espressif_data_len = size > sizeof (espressif_data) ? sizeof (espressif_data) : size; + memcpy (espressif_data, data, espressif_data_len); + } +} + +static void +hnd_espressif_delete(coap_context_t *ctx, + coap_resource_t *resource, + coap_session_t *session, + coap_pdu_t *request, + coap_binary_t *token, + coap_string_t *query, + coap_pdu_t *response) +{ + coap_resource_notify_observers(resource, NULL); + snprintf(espressif_data, sizeof(espressif_data), "no data"); + espressif_data_len = strlen(espressif_data); + response->code = COAP_RESPONSE_CODE(202); } static void coap_example_thread(void *p) { - coap_context_t* ctx = NULL; + coap_context_t *ctx = NULL; coap_address_t serv_addr; - coap_resource_t* resource = NULL; - fd_set readfds; - struct timeval tv; - int flags = 0; + coap_resource_t *resource = NULL; + snprintf(espressif_data, sizeof(espressif_data), "no data"); + espressif_data_len = strlen(espressif_data); + coap_set_log_level(COAP_LOGGING_LEVEL); while (1) { + coap_endpoint_t *ep_udp = NULL; + coap_endpoint_t *ep_tcp = NULL; + unsigned wait_ms; + /* Wait for the callback to set the CONNECTED_BIT in the event group. */ @@ -101,43 +134,49 @@ static void coap_example_thread(void *p) serv_addr.addr.sin.sin_family = AF_INET; serv_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; serv_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); - ctx = coap_new_context(&serv_addr); - if (ctx) { - flags = fcntl(ctx->sockfd, F_GETFL, 0); - fcntl(ctx->sockfd, F_SETFL, flags|O_NONBLOCK); - tv.tv_usec = COAP_DEFAULT_TIME_USEC; - tv.tv_sec = COAP_DEFAULT_TIME_SEC; - /* Initialize the resource */ - resource = coap_resource_init((unsigned char *)"Espressif", 9, 0); - if (resource){ - coap_register_handler(resource, COAP_REQUEST_GET, async_handler); - coap_add_resource(ctx, resource); - /*For incoming connections*/ - for (;;) { - FD_ZERO(&readfds); - FD_CLR( ctx->sockfd, &readfds); - FD_SET( ctx->sockfd, &readfds); + ctx = coap_new_context(NULL); + if (!ctx) { + continue; + } + ep_udp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP); + if (!ep_udp) { + goto clean_up; + } + ep_tcp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP); + if (!ep_tcp) { + goto clean_up; + } + resource = coap_resource_init(coap_make_str_const("Espressif"), 0); + if (!resource) { + goto clean_up; + } + coap_register_handler(resource, COAP_REQUEST_GET, hnd_espressif_get); + coap_register_handler(resource, COAP_REQUEST_PUT, hnd_espressif_put); + coap_register_handler(resource, COAP_REQUEST_DELETE, hnd_espressif_delete); + /* We possibly want to Observe the GETs */ + coap_resource_set_get_observable(resource, 1); + coap_add_resource(ctx, resource); - int result = select( ctx->sockfd+1, &readfds, 0, 0, &tv ); - if (result > 0){ - if (FD_ISSET( ctx->sockfd, &readfds )) - coap_read(ctx); - } else if (result < 0){ - break; - } else { - ESP_LOGE(TAG, "select timeout"); - } + wait_ms = COAP_RESOURCE_CHECK_TIME * 1000; - if (async) { - send_async_response(ctx, ctx->endpoint); - } - } + while (1) { + int result = coap_run_once(ctx, wait_ms); + if (result < 0) { + break; + } else if (result && (unsigned)result < wait_ms) { + /* decrement if there is a result wait time returned */ + wait_ms -= result; + } + if (result) { + /* result must have been >= wait_ms, so reset wait_ms */ + wait_ms = COAP_RESOURCE_CHECK_TIME * 1000; } - - coap_free_context(ctx); } } +clean_up: + coap_free_context(ctx); + coap_cleanup(); vTaskDelete(NULL); } @@ -187,5 +226,5 @@ void app_main(void) ESP_ERROR_CHECK( nvs_flash_init() ); wifi_conn_init(); - xTaskCreate(coap_example_thread, "coap", 2048, NULL, 5, NULL); + xTaskCreate(coap_example_thread, "coap", 10240, NULL, 5, NULL); }