mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 12:52:44 +01:00
c787638696
AwaLWM2M needs to be polled regularly to check for incoming data. Since RIOT only supports timeout at the GNRC sock layer while the network abstraction for RIOT in AwaLWM2M uses the posix layer, this causes RIOT to be blocked waiting for data that never arrive. This commit implements only the SO_RCVTIMEO option in setsockopt to allow users to set a receive timeout for a socket at the posix layer. Signed-off-by: Francois Berder <francois.berder@imgtec.com>
998 lines
26 KiB
C
998 lines
26 KiB
C
/*
|
|
* Copyright (C) 2015 Freie Universität Berlin
|
|
* Copyright (C) 2015 INRIA
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @{
|
|
* @file
|
|
* @brief Providing implementation for POSIX socket wrapper.
|
|
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
|
* @author Oliver Hahm <oliver.hahm@inria.fr>
|
|
* @todo
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
|
|
#include "bitfield.h"
|
|
#include "fd.h"
|
|
#include "mutex.h"
|
|
#include "net/ipv4/addr.h"
|
|
#include "net/ipv6/addr.h"
|
|
#include "random.h"
|
|
|
|
#include "sys/socket.h"
|
|
#include "netinet/in.h"
|
|
|
|
#include "net/sock/ip.h"
|
|
#include "net/sock/udp.h"
|
|
#include "net/sock/tcp.h"
|
|
|
|
/* enough to create sockets both with socket() and accept() */
|
|
#define _ACTUAL_SOCKET_POOL_SIZE (SOCKET_POOL_SIZE + \
|
|
(SOCKET_POOL_SIZE * SOCKET_TCP_QUEUE_SIZE))
|
|
|
|
/**
|
|
* @brief Unitfied connection type.
|
|
*/
|
|
typedef union {
|
|
/* is not supposed to be used, this is only for the case that no
|
|
* sock module was added (maybe useful for UNIX sockets?) */
|
|
/* cppcheck-suppress unusedStructMember */
|
|
int undef;
|
|
#ifdef MODULE_SOCK_IP
|
|
sock_ip_t raw; /**< raw IP sock */
|
|
#endif /* MODULE_SOCK_IP */
|
|
#ifdef MODULE_SOCK_TCP
|
|
union {
|
|
sock_tcp_t sock; /**< TCP sock */
|
|
sock_tcp_queue_t queue; /**< TCP queue */
|
|
} tcp; /**< both TCP types */
|
|
#endif /* MODULE_SOCK_TCP */
|
|
#ifdef MODULE_SOCK_UDP
|
|
sock_udp_t udp; /**< UDP sock */
|
|
#endif /* MODULE_SOCK_UDP */
|
|
} socket_sock_t;
|
|
|
|
typedef struct {
|
|
int fd;
|
|
sa_family_t domain;
|
|
int type;
|
|
int protocol;
|
|
bool bound;
|
|
#ifdef POSIX_SETSOCKOPT
|
|
uint32_t recv_timeout;
|
|
#endif
|
|
socket_sock_t *sock;
|
|
#ifdef MODULE_SOCK_TCP
|
|
sock_tcp_t *queue_array;
|
|
unsigned queue_array_len;
|
|
#endif
|
|
sock_tcp_ep_t local; /* to store bind before connect/listen */
|
|
} socket_t;
|
|
|
|
static socket_t _socket_pool[_ACTUAL_SOCKET_POOL_SIZE];
|
|
static socket_sock_t _sock_pool[SOCKET_POOL_SIZE];
|
|
#ifdef MODULE_SOCK_TCP
|
|
static sock_tcp_t _tcp_sock_pool[SOCKET_POOL_SIZE][SOCKET_TCP_QUEUE_SIZE];
|
|
#endif
|
|
BITFIELD(_sock_pool_used, SOCKET_POOL_SIZE);
|
|
static mutex_t _socket_pool_mutex = MUTEX_INIT;
|
|
|
|
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
|
|
const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
|
|
|
|
static socket_t *_get_free_socket(void)
|
|
{
|
|
for (int i = 0; i < _ACTUAL_SOCKET_POOL_SIZE; i++) {
|
|
if (_socket_pool[i].domain == AF_UNSPEC) {
|
|
return &_socket_pool[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static socket_sock_t *_get_free_sock(void)
|
|
{
|
|
int i = bf_get_unset(_sock_pool_used, SOCKET_POOL_SIZE);
|
|
if (i < 0) {
|
|
return NULL;
|
|
}
|
|
return &_sock_pool[i];
|
|
}
|
|
|
|
static socket_t *_get_socket(int fd)
|
|
{
|
|
for (int i = 0; i < _ACTUAL_SOCKET_POOL_SIZE; i++) {
|
|
if (_socket_pool[i].fd == fd) {
|
|
return &_socket_pool[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int _get_sock_idx(socket_sock_t *sock)
|
|
{
|
|
if ((sock < &_sock_pool[0]) || (sock > &_sock_pool[SOCKET_POOL_SIZE - 1])) {
|
|
return -1;
|
|
}
|
|
return sock - &_sock_pool[0];
|
|
}
|
|
|
|
static inline int _choose_ipproto(int type, int protocol)
|
|
{
|
|
switch (type) {
|
|
#ifdef MODULE_SOCK_TCP
|
|
case SOCK_STREAM:
|
|
if ((protocol == 0) || (protocol == IPPROTO_TCP)) {
|
|
return protocol;
|
|
}
|
|
else {
|
|
errno = EPROTOTYPE;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_UDP
|
|
case SOCK_DGRAM:
|
|
if ((protocol == 0) || (protocol == IPPROTO_UDP)) {
|
|
return protocol;
|
|
}
|
|
else {
|
|
errno = EPROTOTYPE;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_IP
|
|
case SOCK_RAW:
|
|
return protocol;
|
|
#endif
|
|
default:
|
|
(void)protocol;
|
|
break;
|
|
}
|
|
errno = EPROTONOSUPPORT;
|
|
return -1;
|
|
}
|
|
|
|
static inline socklen_t _addr_truncate(struct sockaddr *out, socklen_t out_len,
|
|
struct sockaddr_storage *in,
|
|
socklen_t target_size) {
|
|
out_len = (out_len < target_size) ? out_len : target_size;
|
|
memcpy(out, in, out_len);
|
|
return out_len;
|
|
}
|
|
|
|
static int _ep_to_sockaddr(const struct _sock_tl_ep *ep,
|
|
struct sockaddr_storage *out)
|
|
{
|
|
assert((ep->family == AF_INET) || (ep->family == AF_INET6));
|
|
switch (ep->family) {
|
|
case AF_INET: {
|
|
struct sockaddr_in *in_addr = (struct sockaddr_in *)out;
|
|
|
|
in_addr->sin_family = AF_INET;
|
|
in_addr->sin_addr.s_addr = ep->addr.ipv4_u32;
|
|
in_addr->sin_port = htons(ep->port);
|
|
return sizeof(struct sockaddr_in);
|
|
}
|
|
#ifdef SOCK_HAS_IPV6
|
|
case AF_INET6: {
|
|
struct sockaddr_in6 *in6_addr = (struct sockaddr_in6 *)out;
|
|
|
|
in6_addr->sin6_family = AF_INET6;
|
|
memcpy(&in6_addr->sin6_addr, &ep->addr.ipv6, sizeof(ep->addr.ipv6));
|
|
in6_addr->sin6_port = htons(ep->port);
|
|
return sizeof(struct sockaddr_in6);
|
|
}
|
|
#endif
|
|
default:
|
|
/* should not happen */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int _sockaddr_to_ep(const struct sockaddr *address, socklen_t address_len,
|
|
struct _sock_tl_ep *out)
|
|
{
|
|
switch (address->sa_family) {
|
|
case AF_INET:
|
|
if (address_len < sizeof(struct sockaddr_in)) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
struct sockaddr_in *in_addr = (struct sockaddr_in *)address;
|
|
out->family = AF_INET;
|
|
out->addr.ipv4_u32 = in_addr->sin_addr.s_addr;
|
|
out->port = ntohs(in_addr->sin_port);
|
|
break;
|
|
#ifdef SOCK_HAS_IPV6
|
|
case AF_INET6:
|
|
if (address_len < sizeof(struct sockaddr_in6)) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
struct sockaddr_in6 *in6_addr = (struct sockaddr_in6 *)address;
|
|
out->family = AF_INET6;
|
|
memcpy(&out->addr.ipv6, &in6_addr->sin6_addr, sizeof(out->addr.ipv6));
|
|
out->port = ntohs(in6_addr->sin6_port);
|
|
break;
|
|
#endif
|
|
default:
|
|
errno = EAFNOSUPPORT;
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int socket_close(int socket)
|
|
{
|
|
socket_t *s;
|
|
int res = 0;
|
|
|
|
assert(((unsigned)socket) < _ACTUAL_SOCKET_POOL_SIZE);
|
|
s = &_socket_pool[socket];
|
|
assert((s->domain == AF_INET) || (s->domain == AF_INET6));
|
|
mutex_lock(&_socket_pool_mutex);
|
|
if (s->sock != NULL) {
|
|
int idx = _get_sock_idx(s->sock);
|
|
switch (s->type) {
|
|
#ifdef MODULE_SOCK_UDP
|
|
case SOCK_DGRAM:
|
|
sock_udp_close(&s->sock->udp);
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_IP
|
|
case SOCK_RAW:
|
|
sock_ip_close(&s->sock->raw);
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_TCP
|
|
case SOCK_STREAM:
|
|
if (s->queue_array == 0) {
|
|
sock_tcp_disconnect(&s->sock->tcp.sock);
|
|
}
|
|
else {
|
|
sock_tcp_stop_listen(&s->sock->tcp.queue);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
errno = EOPNOTSUPP;
|
|
res = -1;
|
|
break;
|
|
}
|
|
if (idx >= 0) {
|
|
bf_unset(_sock_pool_used, idx);
|
|
}
|
|
}
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
s->sock = NULL;
|
|
s->domain = AF_UNSPEC;
|
|
return res;
|
|
}
|
|
|
|
static ssize_t socket_read(int socket, void *buf, size_t n)
|
|
{
|
|
return recv(socket, buf, n, 0);
|
|
}
|
|
|
|
static ssize_t socket_write(int socket, const void *buf, size_t n)
|
|
{
|
|
return send(socket, buf, n, 0);
|
|
}
|
|
|
|
int socket(int domain, int type, int protocol)
|
|
{
|
|
int res = 0;
|
|
socket_t *s;
|
|
|
|
mutex_lock(&_socket_pool_mutex);
|
|
s = _get_free_socket();
|
|
if (s == NULL) {
|
|
errno = ENFILE;
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
return -1;
|
|
}
|
|
switch (domain) {
|
|
case AF_INET:
|
|
#ifdef SOCK_HAS_IPV6
|
|
case AF_INET6:
|
|
#endif
|
|
{
|
|
int fd = fd_new(s - _socket_pool, socket_read, socket_write,
|
|
socket_close);
|
|
|
|
if (fd < 0) {
|
|
errno = ENFILE;
|
|
res = -1;
|
|
break;
|
|
}
|
|
else {
|
|
s->fd = res = fd;
|
|
}
|
|
s->domain = domain;
|
|
s->type = type;
|
|
if ((s->protocol = _choose_ipproto(type, protocol)) < 0) {
|
|
res = -1;
|
|
break;
|
|
}
|
|
s->bound = false;
|
|
s->sock = NULL;
|
|
#ifdef POSIX_SETSOCKOPT
|
|
s->recv_timeout = SOCK_NO_TIMEOUT;
|
|
#endif
|
|
#ifdef MODULE_SOCK_TCP
|
|
if (type == SOCK_STREAM) {
|
|
s->queue_array = NULL;
|
|
s->queue_array_len = 0;
|
|
memset(&s->local, 0, sizeof(sock_tcp_ep_t));
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
default:
|
|
(void)type;
|
|
(void)protocol;
|
|
errno = EAFNOSUPPORT;
|
|
res = -1;
|
|
}
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
return res;
|
|
}
|
|
|
|
|
|
int accept(int socket, struct sockaddr *restrict address,
|
|
socklen_t *restrict address_len)
|
|
{
|
|
#ifdef MODULE_SOCK_TCP
|
|
sock_tcp_t *sock = NULL;
|
|
socket_t *s, *new_s = NULL;
|
|
int res = 0;
|
|
|
|
mutex_lock(&_socket_pool_mutex);
|
|
s = _get_socket(socket);
|
|
if (s == NULL) {
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
errno = ENOTSOCK;
|
|
return -1;
|
|
}
|
|
if (s->sock == NULL) {
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
#ifdef POSIX_SETSOCKOPT
|
|
const uint32_t recv_timeout = s->recv_timeout;
|
|
#else
|
|
const uint32_t recv_timeout = SOCK_NO_TIMEOUT;
|
|
#endif
|
|
|
|
switch (s->type) {
|
|
case SOCK_STREAM:
|
|
new_s = _get_free_socket();
|
|
sock = (sock_tcp_t *)new_s->sock;
|
|
if (new_s == NULL) {
|
|
errno = ENFILE;
|
|
res = -1;
|
|
break;
|
|
}
|
|
if ((res = sock_tcp_accept(&s->sock->tcp.queue, &sock,
|
|
recv_timeout)) < 0) {
|
|
errno = -res;
|
|
res = -1;
|
|
break;
|
|
}
|
|
else {
|
|
if ((address != NULL) && (address_len != NULL)) {
|
|
sock_tcp_ep_t ep;
|
|
struct sockaddr_storage sa;
|
|
socklen_t sa_len;
|
|
|
|
if ((res = sock_tcp_get_remote(sock, &ep)) < 0) {
|
|
errno = -res;
|
|
res = -1;
|
|
break;
|
|
}
|
|
sa.ss_family = s->domain;
|
|
sa_len = _ep_to_sockaddr(&ep, &sa);
|
|
*address_len = _addr_truncate(address, *address_len, &sa,
|
|
sa_len);
|
|
|
|
}
|
|
int fd = fd_new(new_s - _socket_pool, socket_read, socket_write,
|
|
socket_close);
|
|
if (fd < 0) {
|
|
errno = ENFILE;
|
|
res = -1;
|
|
break;
|
|
}
|
|
else {
|
|
new_s->fd = res = fd;
|
|
}
|
|
new_s->domain = s->domain;
|
|
new_s->type = s->type;
|
|
new_s->protocol = s->protocol;
|
|
new_s->bound = true;
|
|
new_s->queue_array = NULL;
|
|
new_s->queue_array_len = 0;
|
|
memset(&s->local, 0, sizeof(sock_tcp_ep_t));
|
|
}
|
|
break;
|
|
default:
|
|
errno = EOPNOTSUPP;
|
|
res = -1;
|
|
break;
|
|
}
|
|
if ((res < 0) && (sock != NULL)) {
|
|
sock_tcp_disconnect(sock);
|
|
}
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
return res;
|
|
#else
|
|
(void)socket;
|
|
(void)address;
|
|
(void)address_len;
|
|
errno = -EOPNOTSUPP;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int bind(int socket, const struct sockaddr *address, socklen_t address_len)
|
|
{
|
|
socket_t *s;
|
|
int res = 0;
|
|
|
|
/* only store bind data, real bind happens in _bind_connect/listen */
|
|
mutex_lock(&_socket_pool_mutex);
|
|
s = _get_socket(socket);
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
if (s == NULL) {
|
|
errno = ENOTSOCK;
|
|
return -1;
|
|
}
|
|
if (s->bound) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (address->sa_family != s->domain) {
|
|
errno = EAFNOSUPPORT;
|
|
return -1;
|
|
}
|
|
switch (s->type) {
|
|
#ifdef MODULE_SOCK_IP
|
|
case SOCK_RAW:
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_TCP
|
|
case SOCK_STREAM:
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_UDP
|
|
case SOCK_DGRAM:
|
|
break;
|
|
#endif
|
|
default:
|
|
(void)res;
|
|
errno = EOPNOTSUPP;
|
|
return -1;
|
|
}
|
|
if (_sockaddr_to_ep(address, address_len, &s->local) < 0) {
|
|
return -1;
|
|
}
|
|
s->bound = true;
|
|
return 0;
|
|
}
|
|
|
|
static int _bind_connect(socket_t *s, const struct sockaddr *address,
|
|
socklen_t address_len)
|
|
{
|
|
struct _sock_tl_ep r, *remote = NULL, *local = NULL;
|
|
int res;
|
|
socket_sock_t *sock;
|
|
|
|
assert((s != NULL) && ((address == NULL) || (address_len > 0)));
|
|
if (address != NULL) {
|
|
if (address->sa_family != s->domain) {
|
|
errno = EAFNOSUPPORT;
|
|
return -1;
|
|
}
|
|
if (_sockaddr_to_ep(address, address_len, &r) < 0) {
|
|
return -1;
|
|
}
|
|
remote = &r;
|
|
}
|
|
if (s->bound) {
|
|
local = &s->local;
|
|
}
|
|
mutex_lock(&_socket_pool_mutex);
|
|
sock = _get_free_sock();
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
if (sock == NULL) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
switch (s->type) {
|
|
#ifdef MODULE_SOCK_IP
|
|
case SOCK_RAW:
|
|
/* TODO apply flags if possible */
|
|
res = sock_ip_create(&sock->raw, (sock_ip_ep_t *)local,
|
|
(sock_ip_ep_t *)remote, s->protocol, 0);
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_TCP
|
|
case SOCK_STREAM:
|
|
/* TODO apply flags if possible */
|
|
assert(remote != NULL);
|
|
res = sock_tcp_connect(&sock->tcp.sock, remote,
|
|
(local == NULL) ? 0 : local->port, 0);
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_UDP
|
|
case SOCK_DGRAM:
|
|
/* TODO apply flags if possible */
|
|
res = sock_udp_create(&sock->udp, local, remote, 0);
|
|
break;
|
|
#endif
|
|
default:
|
|
(void)local;
|
|
(void)remote;
|
|
res = -EOPNOTSUPP;
|
|
break;
|
|
}
|
|
if (res < 0) {
|
|
errno = -res;
|
|
/* free sock again */
|
|
mutex_lock(&_socket_pool_mutex);
|
|
bf_unset(_sock_pool_used, _get_sock_idx(sock));
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
return -1;
|
|
}
|
|
s->sock = sock;
|
|
return 0;
|
|
}
|
|
|
|
int connect(int socket, const struct sockaddr *address, socklen_t address_len)
|
|
{
|
|
socket_t *s;
|
|
|
|
mutex_lock(&_socket_pool_mutex);
|
|
s = _get_socket(socket);
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
if (s == NULL) {
|
|
errno = ENOTSOCK;
|
|
return -1;
|
|
}
|
|
if (s->sock != NULL) {
|
|
#ifdef MODULE_SOCK_TCP
|
|
if (s->queue_array != NULL) {
|
|
errno = EOPNOTSUPP;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
errno = EISCONN;
|
|
}
|
|
return -1;
|
|
}
|
|
return _bind_connect(s, address, address_len);
|
|
}
|
|
|
|
static int _getpeername(socket_t *s, struct sockaddr *__restrict address,
|
|
socklen_t *__restrict address_len)
|
|
{
|
|
struct _sock_tl_ep ep;
|
|
int res = 0;
|
|
|
|
if (s->sock == NULL) {
|
|
errno = ENOTCONN;
|
|
return -1;
|
|
}
|
|
switch (s->type) {
|
|
#ifdef MODULE_SOCK_IP
|
|
case SOCK_RAW:
|
|
res = sock_ip_get_remote(&s->sock->raw, (sock_ip_ep_t *)&ep);
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_TCP
|
|
case SOCK_STREAM:
|
|
if (s->queue_array == NULL) {
|
|
res = sock_tcp_get_remote(&s->sock->tcp.sock, &ep);
|
|
}
|
|
else {
|
|
res = -ENOTCONN;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_UDP
|
|
case SOCK_DGRAM:
|
|
res = sock_udp_get_remote(&s->sock->udp, &ep);
|
|
break;
|
|
#endif
|
|
default:
|
|
res = -EOPNOTSUPP;
|
|
break;
|
|
}
|
|
if (res < 0) {
|
|
errno = -res;
|
|
res = -1;
|
|
}
|
|
else {
|
|
struct sockaddr_storage sa;
|
|
socklen_t sa_len = _ep_to_sockaddr(&ep, &sa);
|
|
*address_len = _addr_truncate(address, *address_len, &sa,
|
|
sa_len);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int getpeername(int socket, struct sockaddr *__restrict address,
|
|
socklen_t *__restrict address_len)
|
|
{
|
|
socket_t *s;
|
|
|
|
mutex_lock(&_socket_pool_mutex);
|
|
s = _get_socket(socket);
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
if (s == NULL) {
|
|
errno = ENOTSOCK;
|
|
return -1;
|
|
}
|
|
return _getpeername(s, address, address_len);
|
|
}
|
|
|
|
int getsockname(int socket, struct sockaddr *__restrict address,
|
|
socklen_t *__restrict address_len)
|
|
{
|
|
socket_t *s;
|
|
struct sockaddr_storage sa;
|
|
socklen_t sa_len;
|
|
int res = 0;
|
|
|
|
mutex_lock(&_socket_pool_mutex);
|
|
s = _get_socket(socket);
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
if (s == NULL) {
|
|
errno = ENOTSOCK;
|
|
return -1;
|
|
}
|
|
if (s->sock == NULL) {
|
|
sa_len = _ep_to_sockaddr(&s->local, &sa);
|
|
}
|
|
else {
|
|
struct _sock_tl_ep ep;
|
|
switch (s->type) {
|
|
#ifdef MODULE_SOCK_IP
|
|
case SOCK_RAW:
|
|
res = sock_ip_get_local(&s->sock->raw, (sock_ip_ep_t *)&ep);
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_TCP
|
|
case SOCK_STREAM:
|
|
if (s->queue_array == NULL) {
|
|
res = sock_tcp_get_local(&s->sock->tcp.sock, &ep);
|
|
}
|
|
else {
|
|
res = sock_tcp_queue_get_local(&s->sock->tcp.queue, &ep);
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_UDP
|
|
case SOCK_DGRAM:
|
|
res = sock_udp_get_local(&s->sock->udp, &ep);
|
|
break;
|
|
#endif
|
|
default:
|
|
res = -EOPNOTSUPP;
|
|
break;
|
|
}
|
|
sa_len = _ep_to_sockaddr(&ep, &sa);
|
|
}
|
|
if (res < 0) {
|
|
errno = -res;
|
|
res = -1;
|
|
}
|
|
else {
|
|
*address_len = _addr_truncate(address, *address_len, &sa,
|
|
sa_len);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int listen(int socket, int backlog)
|
|
{
|
|
#ifdef MODULE_SOCK_TCP
|
|
socket_t *s;
|
|
socket_sock_t *sock;
|
|
int res = 0;
|
|
|
|
mutex_lock(&_socket_pool_mutex);
|
|
s = _get_socket(socket);
|
|
if (s == NULL) {
|
|
errno = ENOTSOCK;
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
return -1;
|
|
}
|
|
if (s->sock != NULL) {
|
|
/* or this socket is already connected, this is an error */
|
|
if (s->queue_array == NULL) {
|
|
errno = EINVAL;
|
|
res = -1;
|
|
}
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
return res;
|
|
}
|
|
sock = _get_free_sock();
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
if (sock == NULL) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
s->queue_array = _tcp_sock_pool[_get_sock_idx(sock)];
|
|
s->queue_array_len = (backlog < SOCKET_TCP_QUEUE_SIZE) ? backlog :
|
|
SOCKET_TCP_QUEUE_SIZE;
|
|
switch (s->type) {
|
|
case SOCK_STREAM:
|
|
if (s->bound) {
|
|
/* TODO apply flags if possible */
|
|
res = sock_tcp_listen(&sock->tcp.queue, &s->local,
|
|
s->queue_array, s->queue_array_len, 0);
|
|
}
|
|
else {
|
|
res = -EDESTADDRREQ;
|
|
}
|
|
break;
|
|
default:
|
|
res = -EOPNOTSUPP;
|
|
break;
|
|
}
|
|
if (res == 0) {
|
|
s->sock = sock;
|
|
}
|
|
else {
|
|
errno = -res;
|
|
res = -1;
|
|
mutex_lock(&_socket_pool_mutex);
|
|
bf_unset(_sock_pool_used, _get_sock_idx(sock));
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
}
|
|
return res;
|
|
#else
|
|
(void)socket;
|
|
(void)backlog;
|
|
errno = EOPNOTSUPP;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags,
|
|
struct sockaddr *restrict address,
|
|
socklen_t *restrict address_len)
|
|
{
|
|
socket_t *s;
|
|
int res = 0;
|
|
struct _sock_tl_ep ep = { .port = 0 };
|
|
|
|
(void)flags;
|
|
mutex_lock(&_socket_pool_mutex);
|
|
s = _get_socket(socket);
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
if (s == NULL) {
|
|
errno = ENOTSOCK;
|
|
return -1;
|
|
}
|
|
if (s->sock == NULL) { /* socket is not connected */
|
|
#ifdef MODULE_SOCK_TCP
|
|
if (s->type == SOCK_STREAM) {
|
|
errno = ENOTCONN;
|
|
return -1;
|
|
}
|
|
#endif
|
|
/* bind implicitly */
|
|
if ((res = _bind_connect(s, NULL, 0)) < 0) {
|
|
return res;
|
|
}
|
|
}
|
|
|
|
#ifdef POSIX_SETSOCKOPT
|
|
const uint32_t recv_timeout = s->recv_timeout;
|
|
#else
|
|
const uint32_t recv_timeout = SOCK_NO_TIMEOUT;
|
|
#endif
|
|
|
|
switch (s->type) {
|
|
#ifdef MODULE_SOCK_IP
|
|
case SOCK_RAW:
|
|
if ((res = sock_ip_recv(&s->sock->raw, buffer, length, recv_timeout
|
|
(sock_ip_ep_t *)&ep)) < 0) {
|
|
errno = -res;
|
|
res = -1;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_TCP
|
|
case SOCK_STREAM:
|
|
if ((res = sock_tcp_read(&s->sock->tcp.sock, buffer, length,
|
|
recv_timeout)) < 0) {
|
|
errno = -res;
|
|
res = -1;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_UDP
|
|
case SOCK_DGRAM:
|
|
if ((res = sock_udp_recv(&s->sock->udp, buffer, length, recv_timeout,
|
|
&ep)) < 0) {
|
|
errno = -res;
|
|
res = -1;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
errno = EOPNOTSUPP;
|
|
res = -1;
|
|
break;
|
|
}
|
|
if ((res >= 0) && (address != NULL) && (address_len != 0)) {
|
|
switch (s->type) {
|
|
#ifdef MODULE_SOCK_TCP
|
|
case SOCK_STREAM:
|
|
res = _getpeername(s, address, address_len);
|
|
break;
|
|
#endif
|
|
default: {
|
|
struct sockaddr_storage sa;
|
|
socklen_t sa_len;
|
|
|
|
sa_len = _ep_to_sockaddr(&ep, &sa);
|
|
*address_len = _addr_truncate(address, *address_len, &sa,
|
|
sa_len);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
ssize_t sendto(int socket, const void *buffer, size_t length, int flags,
|
|
const struct sockaddr *address, socklen_t address_len)
|
|
{
|
|
socket_t *s;
|
|
int res = 0;
|
|
#if defined(MODULE_SOCK_IP) || defined(MODULE_SOCK_UDP)
|
|
struct _sock_tl_ep ep = { .port = 0 };
|
|
#endif
|
|
|
|
(void)flags;
|
|
mutex_lock(&_socket_pool_mutex);
|
|
s = _get_socket(socket);
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
if (s == NULL) {
|
|
errno = ENOTSOCK;
|
|
return -1;
|
|
}
|
|
if (s->sock == NULL) { /* socket is not connected */
|
|
#ifdef MODULE_SOCK_TCP
|
|
if (s->type == SOCK_STREAM) {
|
|
errno = ENOTCONN;
|
|
return -1;
|
|
}
|
|
#endif
|
|
/* bind implicitly */
|
|
if ((res = _bind_connect(s, NULL, 0)) < 0) {
|
|
return res;
|
|
}
|
|
}
|
|
#if defined(MODULE_SOCK_IP) || defined(MODULE_SOCK_UDP)
|
|
if ((res = _sockaddr_to_ep(address, address_len, &ep)) < 0)
|
|
return res;
|
|
#endif
|
|
switch (s->type) {
|
|
#ifdef MODULE_SOCK_IP
|
|
case SOCK_RAW:
|
|
if ((res = sock_ip_send(&s->sock->raw, buffer, length,
|
|
s->protocol, (sock_ip_ep_t *)&ep)) < 0) {
|
|
errno = -res;
|
|
res = -1;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_TCP
|
|
case SOCK_STREAM:
|
|
if (address == NULL) {
|
|
(void)address_len;
|
|
if ((res = sock_tcp_write(&s->sock->tcp.sock, buffer, length)) < 0) {
|
|
errno = -res;
|
|
res = -1;
|
|
}
|
|
}
|
|
else {
|
|
res = -1;
|
|
errno = EISCONN;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_SOCK_UDP
|
|
case SOCK_DGRAM:
|
|
if ((res = sock_udp_send(&s->sock->udp, buffer, length, &ep)) < 0) {
|
|
errno = -res;
|
|
res = -1;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
res = -1;
|
|
errno = EOPNOTSUPP;
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* This is a partial implementation of setsockopt for changing the receive
|
|
* timeout value of a socket.
|
|
*/
|
|
int setsockopt(int socket, int level, int option_name, const void *option_value,
|
|
socklen_t option_len)
|
|
{
|
|
#ifdef POSIX_SETSOCKOPT
|
|
socket_t *s;
|
|
struct timeval *tv;
|
|
const uint32_t max_timeout_secs = UINT32_MAX / (1000 * 1000);
|
|
|
|
if (level != SOL_SOCKET
|
|
|| option_name != SO_RCVTIMEO) {
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
if (option_value == NULL
|
|
|| option_len != sizeof(struct timeval)) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
mutex_lock(&_socket_pool_mutex);
|
|
s = _get_socket(socket);
|
|
mutex_unlock(&_socket_pool_mutex);
|
|
if (s == NULL) {
|
|
errno = ENOTSOCK;
|
|
return -1;
|
|
}
|
|
|
|
tv = (struct timeval *) option_value;
|
|
|
|
if (tv->tv_sec < 0 || tv->tv_usec < 0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if ((uint32_t)tv->tv_sec > max_timeout_secs
|
|
|| ((uint32_t)tv->tv_sec == max_timeout_secs && (uint32_t)tv->tv_usec > UINT32_MAX - max_timeout_secs * 1000 * 1000)) {
|
|
errno = EDOM;
|
|
return -1;
|
|
}
|
|
|
|
s->recv_timeout = tv->tv_sec * 1000 * 1000 + tv->tv_usec;
|
|
return 0;
|
|
#else
|
|
(void)socket;
|
|
(void)level;
|
|
(void)option_name;
|
|
(void)option_value;
|
|
(void)option_len;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @}
|
|
*/
|