1586 lines
45 KiB
C
1586 lines
45 KiB
C
/*********************************************************************
|
||
PicoTCP. Copyright (c) 2013 TASS Belgium NV. Some rights reserved.
|
||
See LICENSE and COPYING for usage.
|
||
Do not redistribute without a written permission by the Copyright
|
||
holders.
|
||
|
||
Author: Maxime Vincent, Daniele Lacamera
|
||
*********************************************************************/
|
||
|
||
#include "pico_defines.h"
|
||
#include "pico_config.h" /* for zalloc and free */
|
||
#include "pico_bsd_sockets.h"
|
||
#include "pico_osal.h"
|
||
#ifdef PICO_SUPPORT_SNTP_CLIENT
|
||
#include "pico_sntp_client.h"
|
||
#endif
|
||
|
||
#include <errno.h> /* should be there in C99 */
|
||
|
||
#define SOCK_OPEN 0
|
||
#define SOCK_BOUND 1
|
||
#define SOCK_LISTEN 2
|
||
#define SOCK_CONNECTED 3
|
||
#define SOCK_ERROR 4
|
||
#define SOCK_RESET_BY_PEER 5
|
||
#define SOCK_CLOSED 100
|
||
|
||
#define bsd_dbg(...) do {} while(0)
|
||
#define bsd_dbg_select(...) do {} while(0)
|
||
|
||
/* Global signal sent on any event (for select) */
|
||
void * picoLock = NULL; /* pico stack lock */
|
||
void * pico_signal_select = NULL; /* pico global signal for select */
|
||
void * pico_signal_tick = NULL; /* pico tick signal, e.g. coming from a driver ISR */
|
||
|
||
struct pico_bsd_endpoint {
|
||
struct pico_socket *s;
|
||
int socket_fd;
|
||
int posix_fd; /* TODO: ifdef... */
|
||
uint8_t in_use;
|
||
uint8_t state; /* for pico_state */
|
||
uint8_t nonblocking; /* The non-blocking flag, for non-blocking socket operations */
|
||
uint16_t events; /* events that we filter for */
|
||
uint16_t revents; /* received events */
|
||
uint16_t proto;
|
||
void * mutex_lock; /* mutex for clearing revents */
|
||
void * signal; /* signals new events */
|
||
uint32_t timeout; /* this is used for timeout sockets */
|
||
int error; /* used for SO_ERROR sockopt after connect() */
|
||
};
|
||
|
||
/* MACRO's */
|
||
#define VALIDATE_NULL(param) \
|
||
if(!param) \
|
||
{ \
|
||
return -1; \
|
||
}
|
||
|
||
#define VALIDATE_ONE(param,value) \
|
||
if(param != value) { \
|
||
pico_err = PICO_ERR_EINVAL; \
|
||
errno = pico_err; \
|
||
return -1; \
|
||
}
|
||
|
||
#define VALIDATE_TWO(param,value1,value2) \
|
||
if(param != value1 && param != value2) { \
|
||
pico_err = PICO_ERR_EINVAL; \
|
||
errno = pico_err; \
|
||
return -1; \
|
||
}
|
||
|
||
|
||
/* Private function prototypes */
|
||
static void pico_event_clear(struct pico_bsd_endpoint *ep, uint16_t events);
|
||
static uint16_t pico_bsd_wait(struct pico_bsd_endpoint * ep, int read, int write, int close);
|
||
static void pico_socket_event(uint16_t ev, struct pico_socket *s);
|
||
|
||
|
||
/************************/
|
||
/* Public API functions */
|
||
/************************/
|
||
void pico_bsd_init(void)
|
||
{
|
||
pico_signal_select = pico_signal_init();
|
||
pico_signal_tick = pico_signal_init();
|
||
picoLock = pico_mutex_init();
|
||
}
|
||
|
||
void pico_bsd_deinit(void)
|
||
{
|
||
pico_mutex_deinit(picoLock);
|
||
}
|
||
|
||
/* just ticks the stack twice */
|
||
void pico_bsd_stack_tick(void)
|
||
{
|
||
pico_mutex_lock(picoLock);
|
||
pico_stack_tick();
|
||
pico_stack_tick();
|
||
pico_mutex_unlock(picoLock);
|
||
}
|
||
|
||
/* ticks the stack, but wait for a signal with a timeout (e.g. from the driver interrupt) */
|
||
void pico_bsd_stack_tick_timeout(int timeout_ms)
|
||
{
|
||
pico_signal_wait_timeout(pico_signal_tick, timeout_ms);
|
||
pico_bsd_stack_tick();
|
||
}
|
||
|
||
/** Declarations of helper functions **/
|
||
static struct pico_bsd_endpoint *pico_bsd_create_socket(void);
|
||
static int get_free_sd(struct pico_bsd_endpoint *ep);
|
||
static int new_sd(struct pico_bsd_endpoint *ep);
|
||
static void free_up_ep(struct pico_bsd_endpoint *ep);
|
||
static struct pico_bsd_endpoint *get_endpoint(int sd, int set_err);
|
||
static int bsd_to_pico_addr(union pico_address *addr, struct sockaddr *_saddr, socklen_t socklen);
|
||
static uint16_t bsd_to_pico_port(struct sockaddr *_saddr, socklen_t socklen);
|
||
static int pico_addr_to_bsd(struct sockaddr *_saddr, socklen_t socklen, union pico_address *addr, uint16_t net);
|
||
static int pico_port_to_bsd(struct sockaddr *_saddr, socklen_t socklen, uint16_t port);
|
||
|
||
/** Global Sockets descriptors array **/
|
||
static struct pico_bsd_endpoint **PicoSockets = NULL;
|
||
static int PicoSocket_max = 0;
|
||
|
||
/*** Public socket functions ***/
|
||
|
||
/* Socket interface. */
|
||
int pico_newsocket(int domain, int type, int proto)
|
||
{
|
||
struct pico_bsd_endpoint * ep = NULL;
|
||
(void)proto;
|
||
|
||
#ifdef PICO_SUPPORT_IPV6
|
||
VALIDATE_TWO(domain,AF_INET, AF_INET6);
|
||
#else
|
||
VALIDATE_ONE(domain, AF_INET);
|
||
#endif
|
||
VALIDATE_TWO(type,SOCK_STREAM,SOCK_DGRAM);
|
||
|
||
if (AF_INET6 != PICO_PROTO_IPV6) {
|
||
if (domain == AF_INET6)
|
||
domain = PICO_PROTO_IPV6;
|
||
else
|
||
domain = PICO_PROTO_IPV4;
|
||
}
|
||
|
||
if (SOCK_STREAM != PICO_PROTO_TCP) {
|
||
if (type == SOCK_STREAM)
|
||
type = PICO_PROTO_TCP;
|
||
else
|
||
type = PICO_PROTO_UDP;
|
||
}
|
||
|
||
pico_mutex_lock(picoLock);
|
||
ep = pico_bsd_create_socket();
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
|
||
ep->proto = type;
|
||
|
||
ep->s = pico_socket_open(domain, type,&pico_socket_event);
|
||
if (!ep->s)
|
||
{
|
||
PICO_FREE(ep);
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
|
||
ep->s->priv = ep; /* let priv point to the endpoint struct */
|
||
|
||
/* open picotcp endpoint */
|
||
ep->state = SOCK_OPEN;
|
||
ep->mutex_lock = pico_mutex_init();
|
||
ep->signal = pico_signal_init();
|
||
ep->error = pico_err;
|
||
pico_mutex_unlock(picoLock);
|
||
return ep->socket_fd;
|
||
}
|
||
|
||
|
||
int pico_bind(int sd, struct sockaddr * local_addr, socklen_t socklen)
|
||
{
|
||
union pico_address addr = { .ip4 = { 0 } };
|
||
uint16_t port;
|
||
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
|
||
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
VALIDATE_NULL(local_addr);
|
||
VALIDATE_TWO(socklen, SOCKSIZE, SOCKSIZE6);
|
||
|
||
if (bsd_to_pico_addr(&addr, local_addr, socklen) < 0)
|
||
{
|
||
ep->error = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
port = bsd_to_pico_port(local_addr, socklen);
|
||
|
||
pico_mutex_lock(picoLock);
|
||
if(pico_socket_bind(ep->s, &addr, &port) < 0)
|
||
{
|
||
ep->error = pico_err;
|
||
errno = pico_err;
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
|
||
ep->state = SOCK_BOUND;
|
||
pico_mutex_unlock(picoLock);
|
||
|
||
return 0;
|
||
}
|
||
|
||
int pico_getsockname(int sd, struct sockaddr * local_addr, socklen_t *socklen)
|
||
{
|
||
union pico_address addr;
|
||
uint16_t port, proto;
|
||
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
VALIDATE_NULL(local_addr);
|
||
VALIDATE_NULL(socklen);
|
||
pico_mutex_lock(picoLock);
|
||
if(pico_socket_getname(ep->s, &addr, &port, &proto) < 0)
|
||
{
|
||
ep->error = pico_err;
|
||
errno = pico_err;
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
|
||
if (proto == PICO_PROTO_IPV6)
|
||
*socklen = SOCKSIZE6;
|
||
else
|
||
*socklen = SOCKSIZE;
|
||
|
||
if (pico_addr_to_bsd(local_addr, *socklen, &addr, proto) < 0) {
|
||
ep->error = pico_err;
|
||
errno = pico_err;
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
pico_mutex_unlock(picoLock);
|
||
pico_port_to_bsd(local_addr, *socklen, port);
|
||
ep->error = pico_err;
|
||
return 0;
|
||
}
|
||
|
||
int pico_getpeername(int sd, struct sockaddr * remote_addr, socklen_t *socklen)
|
||
{
|
||
union pico_address addr;
|
||
uint16_t port, proto;
|
||
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
VALIDATE_NULL(remote_addr);
|
||
VALIDATE_NULL(socklen);
|
||
pico_mutex_lock(picoLock);
|
||
if(pico_socket_getpeername(ep->s, &addr, &port, &proto) < 0)
|
||
{
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
|
||
if (proto == PICO_PROTO_IPV6)
|
||
*socklen = SOCKSIZE6;
|
||
else
|
||
*socklen = SOCKSIZE;
|
||
|
||
if (pico_addr_to_bsd(remote_addr, *socklen, &addr, proto) < 0) {
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
pico_mutex_unlock(picoLock);
|
||
pico_port_to_bsd(remote_addr, *socklen, port);
|
||
return 0;
|
||
}
|
||
|
||
|
||
int pico_listen(int sd, int backlog)
|
||
{
|
||
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
|
||
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
VALIDATE_NULL(ep->s);
|
||
VALIDATE_ONE(ep->state, SOCK_BOUND);
|
||
|
||
pico_mutex_lock(picoLock);
|
||
|
||
if(pico_socket_listen(ep->s, backlog) < 0)
|
||
{
|
||
ep->error = pico_err;
|
||
errno = pico_err;
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
ep->state = SOCK_LISTEN;
|
||
|
||
ep->error = pico_err;
|
||
pico_mutex_unlock(picoLock);
|
||
return 0;
|
||
}
|
||
|
||
int pico_connect(int sd, struct sockaddr *_saddr, socklen_t socklen)
|
||
{
|
||
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
|
||
union pico_address addr;
|
||
uint16_t port;
|
||
uint16_t ev = 0;
|
||
int ret;
|
||
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
VALIDATE_NULL(_saddr);
|
||
if (bsd_to_pico_addr(&addr, _saddr, socklen) < 0)
|
||
{
|
||
ep->error = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
port = bsd_to_pico_port(_saddr, socklen);
|
||
pico_mutex_lock(picoLock);
|
||
ret = pico_socket_connect(ep->s, &addr, port);
|
||
pico_mutex_unlock(picoLock);
|
||
if (ret < 0) {
|
||
ep->error = pico_err;
|
||
return -1;
|
||
}
|
||
|
||
if (ep->nonblocking) {
|
||
pico_err = PICO_ERR_EAGAIN;
|
||
ep->error = pico_err;
|
||
} else {
|
||
/* wait for event */
|
||
ev = pico_bsd_wait(ep, 0, 0, 0); /* wait for ERR, FIN and CONN */
|
||
}
|
||
|
||
if(ev & PICO_SOCK_EV_CONN)
|
||
{
|
||
/* clear the EV_CONN event */
|
||
pico_event_clear(ep, PICO_SOCK_EV_CONN);
|
||
ep->error = pico_err;
|
||
return 0;
|
||
} else {
|
||
if (!(ep->nonblocking))
|
||
pico_socket_close(ep->s);
|
||
}
|
||
ep->error = pico_err;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
|
||
int pico_isconnected(int sd) {
|
||
struct pico_bsd_endpoint *ep = NULL;
|
||
int state = 0;
|
||
|
||
ep = get_endpoint(sd, 1);
|
||
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
|
||
pico_mutex_lock(picoLock);
|
||
if(ep->state == SOCK_CONNECTED) {
|
||
state = 1;
|
||
}
|
||
pico_mutex_unlock(picoLock);
|
||
|
||
return state;
|
||
}
|
||
|
||
int pico_accept(int sd, struct sockaddr *_orig, socklen_t *socklen)
|
||
{
|
||
struct pico_bsd_endpoint *ep, * client_ep = NULL;
|
||
uint16_t events;
|
||
union pico_address picoaddr;
|
||
uint16_t port;
|
||
|
||
ep = get_endpoint(sd, 1);
|
||
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
VALIDATE_ONE(ep->state, SOCK_LISTEN);
|
||
|
||
if (ep->nonblocking)
|
||
events = PICO_SOCK_EV_CONN;
|
||
else
|
||
events = pico_bsd_wait(ep, 0, 0, 0); /* Wait for CONN, FIN and ERR */
|
||
|
||
if(events & PICO_SOCK_EV_CONN)
|
||
{
|
||
struct pico_socket *s;
|
||
pico_mutex_lock(picoLock);
|
||
s = pico_socket_accept(ep->s,&picoaddr,&port);
|
||
if (!s)
|
||
{
|
||
ep->error = pico_err;
|
||
errno = pico_err;
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
|
||
/* Create a new client EP, only after the accept returned succesfully */
|
||
client_ep = pico_bsd_create_socket();
|
||
if (!client_ep)
|
||
{
|
||
ep->error = pico_err;
|
||
errno = pico_err;
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
client_ep->s = s;
|
||
client_ep->state = SOCK_OPEN;
|
||
client_ep->mutex_lock = pico_mutex_init();
|
||
client_ep->signal = pico_signal_init();
|
||
|
||
client_ep->s->priv = client_ep;
|
||
pico_event_clear(ep, PICO_SOCK_EV_CONN); /* clear the CONN event the listening socket */
|
||
if (client_ep->s->net->proto_number == PICO_PROTO_IPV4)
|
||
*socklen = SOCKSIZE;
|
||
else
|
||
*socklen = SOCKSIZE6;
|
||
client_ep->state = SOCK_CONNECTED;
|
||
if (pico_addr_to_bsd(_orig, *socklen, &picoaddr, client_ep->s->net->proto_number) < 0) {
|
||
client_ep->in_use = 0;
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
pico_port_to_bsd(_orig, *socklen, port);
|
||
|
||
client_ep->in_use = 1;
|
||
pico_mutex_unlock(picoLock);
|
||
ep->error = pico_err;
|
||
return client_ep->socket_fd;
|
||
}
|
||
client_ep->in_use = 0;
|
||
ep->error = pico_err;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
|
||
int pico_sendto(int sd, void * buf, int len, int flags, struct sockaddr *_dst, socklen_t socklen)
|
||
{
|
||
int retval = 0;
|
||
int tot_len = 0;
|
||
uint16_t port;
|
||
union pico_address picoaddr;
|
||
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
|
||
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
|
||
if (!buf || (len <= 0)) {
|
||
pico_err = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
ep->error = pico_err;
|
||
return -1;
|
||
}
|
||
|
||
while (tot_len < len) {
|
||
/* Write to the pico socket */
|
||
pico_mutex_lock(picoLock);
|
||
if (_dst == NULL) {
|
||
retval = pico_socket_send(ep->s, ((uint8_t *)buf) + tot_len, len - tot_len);
|
||
} else {
|
||
if (bsd_to_pico_addr(&picoaddr, _dst, socklen) < 0) {
|
||
ep->error = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
port = bsd_to_pico_port(_dst, socklen);
|
||
retval = pico_socket_sendto(ep->s, ((uint8_t *)buf) + tot_len, len - tot_len, &picoaddr, port);
|
||
}
|
||
pico_event_clear(ep, PICO_SOCK_EV_WR);
|
||
pico_mutex_unlock(picoLock);
|
||
|
||
/* If sending failed, return an error */
|
||
if (retval < 0)
|
||
{
|
||
ep->error = pico_err;
|
||
errno = pico_err;
|
||
pico_event_clear(ep, PICO_SOCK_EV_WR);
|
||
return -1;
|
||
}
|
||
|
||
if (retval > 0)
|
||
{
|
||
tot_len += retval;
|
||
break;
|
||
}
|
||
|
||
if (ep->nonblocking)
|
||
break;
|
||
|
||
/* If sent bytes (retval) < len-tot_len: socket full, we need to wait for a new WR event */
|
||
if (retval < (len - tot_len))
|
||
{
|
||
uint16_t ev = 0;
|
||
/* wait for a new WR or CLOSE event */
|
||
ev = pico_bsd_wait(ep, 0, 1, 1);
|
||
|
||
if (ev & (PICO_SOCK_EV_ERR | PICO_SOCK_EV_FIN | PICO_SOCK_EV_CLOSE))
|
||
{
|
||
ep->error = pico_err;
|
||
errno = pico_err;
|
||
pico_event_clear(ep, PICO_SOCK_EV_WR);
|
||
/* closing and freeing the socket is done in the event handler */
|
||
return -1;
|
||
}
|
||
}
|
||
tot_len += retval;
|
||
}
|
||
ep->error = pico_err;
|
||
return tot_len;
|
||
}
|
||
|
||
int pico_fcntl(int sd, int cmd, int arg)
|
||
{
|
||
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
|
||
if (!ep) {
|
||
pico_err = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
|
||
if (cmd == F_SETFL) {
|
||
if ((arg & O_NONBLOCK) != 0) {
|
||
ep->nonblocking = 1;
|
||
} else {
|
||
ep->nonblocking = 0;
|
||
}
|
||
ep->error = PICO_ERR_NOERR;
|
||
return 0;
|
||
}
|
||
|
||
if (cmd == F_GETFL) {
|
||
(void)arg; /* F_GETFL: arg is ignored */
|
||
ep->error = PICO_ERR_NOERR;
|
||
if (ep->nonblocking)
|
||
return O_NONBLOCK;
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
if (cmd == F_SETFD) {
|
||
(void)arg;
|
||
ep->error = PICO_ERR_NOERR;
|
||
return 0;
|
||
}
|
||
|
||
|
||
pico_err = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
ep->error = pico_err;
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* RETURN VALUE
|
||
* Upon successful completion, recv_from() shall return the length of the
|
||
* message in bytes. If no messages are available to be received and the
|
||
* peer has performed an orderly shutdown, recv() shall return 0. Otherwise,
|
||
* −1 shall be returned and errno set to indicate the error.
|
||
*/
|
||
int pico_recvfrom(int sd, void * _buf, int len, int flags, struct sockaddr *_addr, socklen_t *socklen)
|
||
{
|
||
int retval = 0;
|
||
int tot_len = 0;
|
||
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
|
||
union pico_address picoaddr;
|
||
uint16_t port;
|
||
unsigned char *buf = (unsigned char *)_buf;
|
||
bsd_dbg("Recvfrom called \n");
|
||
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
|
||
if (ep->state == SOCK_RESET_BY_PEER) {
|
||
/* not much to do here. Peer has nothing to say. */
|
||
return 0;
|
||
}
|
||
|
||
if (!buf || (len <= 0)) {
|
||
pico_err = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
ep->error = pico_err;
|
||
return -1;
|
||
}
|
||
|
||
while (tot_len < len) {
|
||
pico_mutex_lock(picoLock);
|
||
retval = pico_socket_recvfrom(ep->s, buf + tot_len , len - tot_len, &picoaddr, &port);
|
||
pico_mutex_unlock(picoLock);
|
||
bsd_dbg("pico_socket_recvfrom returns %d, first bytes are %c-%c-%c-%c\n", retval, buf[0], buf[1], buf[2], buf[3]);
|
||
|
||
/* pico_socket_recvfrom failed */
|
||
if (retval < 0) {
|
||
/* data was received */
|
||
if (tot_len > 0)
|
||
{
|
||
bsd_dbg("Recvfrom returning %d\n", tot_len);
|
||
ep->error = pico_err;
|
||
return tot_len;
|
||
}
|
||
/* no data was received yet */
|
||
ep->error = pico_err;
|
||
if (pico_err == PICO_ERR_ESHUTDOWN) /* If no messages are available to be received and the peer has performed an orderly shutdown, recvfrom() shall return 0. */
|
||
{
|
||
return 0;
|
||
}
|
||
else /* Otherwise, the function shall return −1 and set errno to indicate the error. */
|
||
{
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/* If received 0 bytes, return -1 or amount of bytes received */
|
||
if (retval == 0) {
|
||
pico_event_clear(ep, PICO_SOCK_EV_RD);
|
||
if (tot_len > 0) {
|
||
bsd_dbg("Recvfrom returning %d\n", tot_len);
|
||
ep->error = pico_err;
|
||
return tot_len;
|
||
}
|
||
}
|
||
|
||
if (retval > 0) {
|
||
if (ep->proto == PICO_PROTO_UDP) {
|
||
if (_addr && socklen > 0)
|
||
{
|
||
if (pico_addr_to_bsd(_addr, *socklen, &picoaddr, ep->s->net->proto_number) < 0) {
|
||
pico_err = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
ep->error = pico_err;
|
||
return -1;
|
||
}
|
||
pico_port_to_bsd(_addr, *socklen, port);
|
||
}
|
||
/* If in a recvfrom call, for UDP we should return immediately after the first dgram */
|
||
ep->error = pico_err;
|
||
return retval + tot_len;
|
||
} else {
|
||
/* TCP: continue until recvfrom = 0, socket buffer empty */
|
||
tot_len += retval;
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (ep->nonblocking)
|
||
{
|
||
if (retval == 0)
|
||
{
|
||
pico_err = PICO_ERR_EAGAIN; /* or EWOULDBLOCK */
|
||
errno = pico_err;
|
||
retval = -1; /* BSD-speak: -1 == 0 bytes received */
|
||
}
|
||
break;
|
||
}
|
||
|
||
/* If recv bytes (retval) < len-tot_len: socket empty, we need to wait for a new RD event */
|
||
if (retval < (len - tot_len))
|
||
{
|
||
uint16_t ev = 0;
|
||
/* wait for a new RD event -- also wait for CLOSE event */
|
||
ev = pico_bsd_wait(ep, 1, 0, 1);
|
||
if (ev & (PICO_SOCK_EV_ERR | PICO_SOCK_EV_FIN | PICO_SOCK_EV_CLOSE))
|
||
{
|
||
/* closing and freeing the socket is done in the event handler */
|
||
pico_event_clear(ep, PICO_SOCK_EV_RD);
|
||
ep->error = pico_err;
|
||
return 0; /* return 0 on a properly closed socket */
|
||
}
|
||
}
|
||
tot_len += retval;
|
||
}
|
||
pico_event_clear(ep, PICO_SOCK_EV_RD); // What if still space available and we clear it here??
|
||
bsd_dbg("Recvfrom returning %d (full block)\n", tot_len);
|
||
ep->error = pico_err;
|
||
|
||
if (tot_len == 0)
|
||
{
|
||
errno = pico_err;
|
||
tot_len = -1; /* BSD-speak: -1 == 0 bytes received */
|
||
}
|
||
return tot_len;
|
||
}
|
||
|
||
int pico_write(int sd, void * buf, int len)
|
||
{
|
||
return pico_sendto(sd, buf, len, 0, NULL, 0);
|
||
}
|
||
|
||
int pico_send(int sd, void * buf, int len, int flags)
|
||
{
|
||
return pico_sendto(sd, buf, len, flags, NULL, 0);
|
||
}
|
||
|
||
int pico_read(int sd, void * buf, int len)
|
||
{
|
||
return pico_recvfrom(sd, buf, len, 0, NULL, 0);
|
||
}
|
||
|
||
int pico_recv(int sd, void * buf, int len, int flags)
|
||
{
|
||
return pico_recvfrom(sd, buf, len, flags, NULL, 0);
|
||
}
|
||
|
||
|
||
|
||
int pico_close(int sd)
|
||
{
|
||
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
|
||
if (ep->s && ep->in_use) /* valid socket, try to close it */
|
||
{
|
||
pico_mutex_lock(picoLock);
|
||
pico_socket_close(ep->s);
|
||
ep->s->priv = NULL;
|
||
pico_mutex_unlock(picoLock);
|
||
}
|
||
ep->in_use = 0;
|
||
ep->error = pico_err;
|
||
return 0;
|
||
}
|
||
|
||
int pico_shutdown(int sd, int how)
|
||
{
|
||
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
|
||
if(ep->s) /* valid socket, try to close it */
|
||
{
|
||
pico_mutex_lock(picoLock);
|
||
pico_socket_shutdown(ep->s, how);
|
||
ep->error = pico_err;
|
||
pico_mutex_unlock(picoLock);
|
||
} else {
|
||
ep->error = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int pico_join_multicast_group(int sd, const char *address, const char *local) {
|
||
|
||
int ret;
|
||
struct pico_ip_mreq mreq={};
|
||
|
||
pico_string_to_ipv4(address, &mreq.mcast_group_addr.ip4.addr);
|
||
pico_string_to_ipv4(local, &mreq.mcast_link_addr.ip4.addr);
|
||
ret = pico_setsockopt(sd, SOL_SOCKET, PICO_IP_ADD_MEMBERSHIP, &mreq, sizeof(struct pico_ip_mreq));
|
||
|
||
return ret;
|
||
}
|
||
|
||
/*** Helper functions ***/
|
||
static int bsd_to_pico_addr(union pico_address *addr, struct sockaddr *_saddr, socklen_t socklen)
|
||
{
|
||
VALIDATE_TWO(socklen, SOCKSIZE, SOCKSIZE6);
|
||
if (socklen == SOCKSIZE6) {
|
||
struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)_saddr;
|
||
memcpy(&addr->ip6.addr, &saddr->sin6_addr.s6_addr, 16);
|
||
saddr->sin6_family = AF_INET6;
|
||
} else {
|
||
struct sockaddr_in *saddr = (struct sockaddr_in *)_saddr;
|
||
addr->ip4.addr = saddr->sin_addr.s_addr;
|
||
saddr->sin_family = AF_INET;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static uint16_t bsd_to_pico_port(struct sockaddr *_saddr, socklen_t socklen)
|
||
{
|
||
VALIDATE_TWO(socklen, SOCKSIZE, SOCKSIZE6);
|
||
if (socklen == SOCKSIZE6) {
|
||
struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)_saddr;
|
||
return saddr->sin6_port;
|
||
} else {
|
||
struct sockaddr_in *saddr = (struct sockaddr_in *)_saddr;
|
||
return saddr->sin_port;
|
||
}
|
||
}
|
||
|
||
static int pico_port_to_bsd(struct sockaddr *_saddr, socklen_t socklen, uint16_t port)
|
||
{
|
||
if (socklen == SOCKSIZE6) {
|
||
struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)_saddr;
|
||
saddr->sin6_port = port;
|
||
return 0;
|
||
} else {
|
||
struct sockaddr_in *saddr = (struct sockaddr_in *)_saddr;
|
||
saddr->sin_port = port;
|
||
return 0;
|
||
}
|
||
pico_err = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
|
||
static int pico_addr_to_bsd(struct sockaddr *_saddr, socklen_t socklen, union pico_address *addr, uint16_t net)
|
||
{
|
||
VALIDATE_TWO(socklen, SOCKSIZE, SOCKSIZE6);
|
||
if ((socklen == SOCKSIZE6) && (net == PICO_PROTO_IPV6)) {
|
||
struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)_saddr;
|
||
memcpy(&saddr->sin6_addr.s6_addr, &addr->ip6.addr, 16);
|
||
saddr->sin6_family = AF_INET6;
|
||
} else if ((socklen == SOCKSIZE) && (net == PICO_PROTO_IPV4)) {
|
||
struct sockaddr_in *saddr = (struct sockaddr_in *)_saddr;
|
||
saddr->sin_addr.s_addr = addr->ip4.addr;
|
||
saddr->sin_family = AF_INET;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static void free_up_ep(struct pico_bsd_endpoint *ep)
|
||
{
|
||
if (ep->signal)
|
||
pico_signal_deinit(ep->signal);
|
||
if (ep->mutex_lock)
|
||
pico_mutex_deinit(ep->mutex_lock);
|
||
PICO_FREE(ep);
|
||
}
|
||
|
||
static int get_free_sd(struct pico_bsd_endpoint *ep)
|
||
{
|
||
int i;
|
||
for (i = 0; i < PicoSocket_max; i++) {
|
||
if (!PicoSockets[i]->in_use) {
|
||
free_up_ep(PicoSockets[i]);
|
||
PicoSockets[i] = ep;
|
||
return i;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/* DLA TODO: make a GC for freeing up the last socket descriptor periodically if not in use */
|
||
|
||
static int new_sd(struct pico_bsd_endpoint *ep)
|
||
{
|
||
int sd = PicoSocket_max;
|
||
struct pico_bsd_endpoint **new;
|
||
new = PICO_ZALLOC(sizeof(void *) * ++PicoSocket_max);
|
||
if (!new) {
|
||
PicoSocket_max--;
|
||
pico_err = PICO_ERR_ENOMEM;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
if (sd > 0) {
|
||
memcpy(new, PicoSockets, sd * sizeof(void *));
|
||
PICO_FREE(PicoSockets);
|
||
}
|
||
PicoSockets = new;
|
||
new[sd] = ep;
|
||
return sd;
|
||
}
|
||
|
||
/* picoLock must be taken already ! */
|
||
static struct pico_bsd_endpoint *pico_bsd_create_socket(void)
|
||
{
|
||
struct pico_bsd_endpoint *ep = PICO_ZALLOC(sizeof(struct pico_bsd_endpoint));
|
||
if (!ep) {
|
||
pico_err = PICO_ERR_ENOMEM;
|
||
errno = pico_err;
|
||
}
|
||
ep->in_use = 1;
|
||
ep->socket_fd = get_free_sd(ep);
|
||
if (ep->socket_fd < 0) {
|
||
ep->socket_fd = new_sd(ep);
|
||
}
|
||
return ep;
|
||
}
|
||
|
||
|
||
#ifndef PICO_EBADFD
|
||
# define PICO_EBADFD 77 /* File descriptor in bad state */
|
||
#endif
|
||
|
||
static struct pico_bsd_endpoint *get_endpoint(int sd, int set_err)
|
||
{
|
||
if ((sd > PicoSocket_max) || (sd < 0) ||
|
||
(PicoSockets[sd]->in_use == 0)) {
|
||
if (set_err)
|
||
{
|
||
pico_err = PICO_EBADFD;
|
||
errno = pico_err;
|
||
}
|
||
return NULL;
|
||
}
|
||
return PicoSockets[sd];
|
||
}
|
||
|
||
/* wait for one of the selected events, return any of those that occurred */
|
||
uint16_t pico_bsd_select(struct pico_bsd_endpoint *ep)
|
||
{
|
||
uint16_t events = ep->events & ep->revents; /* maybe an event we are waiting for, was already queued ? */
|
||
/* wait for one of the selected events... */
|
||
while (!events)
|
||
{
|
||
pico_signal_wait(ep->signal);
|
||
events = (ep->revents & ep->events); /* filter for the events we were waiting for */
|
||
}
|
||
/* the event we were waiting for happened, now report it */
|
||
return events; /* return any event(s) that occurred, that we were waiting for */
|
||
}
|
||
|
||
|
||
/****************************/
|
||
/* Private helper functions */
|
||
/****************************/
|
||
static uint16_t pico_bsd_wait(struct pico_bsd_endpoint * ep, int read, int write, int close)
|
||
{
|
||
pico_mutex_lock(ep->mutex_lock);
|
||
|
||
ep->events = PICO_SOCK_EV_ERR;
|
||
ep->events |= PICO_SOCK_EV_FIN;
|
||
ep->events |= PICO_SOCK_EV_CONN;
|
||
if (close)
|
||
ep->events |= PICO_SOCK_EV_CLOSE;
|
||
if (read)
|
||
ep->events |= PICO_SOCK_EV_RD;
|
||
if (write)
|
||
ep->events |= PICO_SOCK_EV_WR;
|
||
|
||
pico_mutex_unlock(ep->mutex_lock);
|
||
|
||
return pico_bsd_select(ep);
|
||
}
|
||
|
||
|
||
static void pico_event_clear(struct pico_bsd_endpoint *ep, uint16_t events)
|
||
{
|
||
pico_mutex_lock(ep->mutex_lock);
|
||
ep->revents &= ~events; /* clear those events */
|
||
pico_mutex_unlock(ep->mutex_lock);
|
||
}
|
||
|
||
/* NOTE: __NO__ picoLock'ing here !! */
|
||
/* this is called from pico_stack_tick, so picoLock is already locked */
|
||
static void pico_socket_event(uint16_t ev, struct pico_socket *s)
|
||
{
|
||
struct pico_bsd_endpoint * ep = (struct pico_bsd_endpoint *)(s->priv);
|
||
if (!s)
|
||
return;
|
||
if(!ep || !ep->s || !ep->mutex_lock || !ep->signal )
|
||
{
|
||
/* DLA: do not call close upon SOCK_CLOSE, we might still write. */
|
||
if(ev & (PICO_SOCK_EV_FIN | PICO_SOCK_EV_ERR) )
|
||
{
|
||
pico_signal_send(pico_signal_select); /* Signal this event globally (e.g. for select()) */
|
||
pico_socket_close(s);
|
||
}
|
||
|
||
if (ev & PICO_SOCK_EV_CLOSE)
|
||
pico_signal_send(pico_signal_select);
|
||
|
||
/* endpoint not initialized yet! */
|
||
return;
|
||
}
|
||
|
||
if(ep->in_use != 1)
|
||
return;
|
||
|
||
pico_mutex_lock(ep->mutex_lock); /* lock over the complete body is needed,
|
||
as the event might get cleared in another process.. */
|
||
ep->revents |= ev; /* set those events */
|
||
|
||
if(ev & PICO_SOCK_EV_CONN)
|
||
{
|
||
if(ep->state != SOCK_LISTEN)
|
||
{
|
||
ep->state = SOCK_CONNECTED;
|
||
}
|
||
}
|
||
|
||
if(ev & PICO_SOCK_EV_ERR)
|
||
{
|
||
ep->state = SOCK_RESET_BY_PEER;
|
||
}
|
||
|
||
if (ev & PICO_SOCK_EV_CLOSE) {
|
||
ep->state = SOCK_RESET_BY_PEER;
|
||
/* DO NOT close: we might still write! */
|
||
}
|
||
|
||
if (ev & PICO_SOCK_EV_FIN) {
|
||
/* DO NOT set ep->s = NULL, we might still be transmitting stuff! */
|
||
ep->state = SOCK_CLOSED;
|
||
}
|
||
|
||
pico_signal_send(pico_signal_select); /* Signal this event globally (e.g. for select()) */
|
||
pico_signal_send(ep->signal); /* Signal the endpoint that was blocking on this event */
|
||
|
||
pico_mutex_unlock(ep->mutex_lock);
|
||
}
|
||
|
||
|
||
#define DNSQUERY_OK 1
|
||
#define DNSQUERY_FAIL 0xFF
|
||
struct dnsquery_cookie
|
||
{
|
||
struct addrinfo **res;
|
||
void *signal;
|
||
uint8_t block;
|
||
uint8_t revents;
|
||
};
|
||
|
||
static struct dnsquery_cookie *dnsquery_cookie_create(struct addrinfo **res, uint8_t block)
|
||
{
|
||
struct dnsquery_cookie *ck = PICO_ZALLOC(sizeof(struct dnsquery_cookie));
|
||
if (!ck) {
|
||
pico_err = PICO_ERR_ENOMEM;
|
||
errno = pico_err;
|
||
return NULL;
|
||
}
|
||
ck->signal = pico_signal_init();
|
||
ck->res = res;
|
||
ck->block = block;
|
||
return ck;
|
||
}
|
||
|
||
static int dnsquery_cookie_delete(struct dnsquery_cookie *ck)
|
||
{
|
||
if (!ck) {
|
||
pico_err = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
if (ck->signal)
|
||
{
|
||
pico_signal_deinit(ck->signal);
|
||
ck->signal = NULL;
|
||
}
|
||
PICO_FREE(ck);
|
||
return 0;
|
||
}
|
||
|
||
#ifdef PICO_SUPPORT_IPV6
|
||
static void dns_ip6_cb(char *ip, void *arg)
|
||
{
|
||
struct dnsquery_cookie *ck = (struct dnsquery_cookie *)arg;
|
||
struct addrinfo *new;
|
||
|
||
if (ip) {
|
||
new = PICO_ZALLOC(sizeof(struct addrinfo));
|
||
if (!new) {
|
||
ck->revents = DNSQUERY_FAIL;
|
||
if (ck->block)
|
||
pico_signal_send(ck->signal);
|
||
return;
|
||
}
|
||
new->ai_family = AF_INET6;
|
||
new->ai_addr = PICO_ZALLOC(sizeof(struct sockaddr_in6));
|
||
if (!new->ai_addr) {
|
||
PICO_FREE(new);
|
||
ck->revents = DNSQUERY_FAIL;
|
||
if (ck->block)
|
||
pico_signal_send(ck->signal);
|
||
return;
|
||
}
|
||
new->ai_addrlen = sizeof(struct sockaddr_in6);
|
||
pico_string_to_ipv6(ip, (((struct sockaddr_in6*)(new->ai_addr))->sin6_addr.s6_addr));
|
||
((struct sockaddr_in6*)(new->ai_addr))->sin6_family = AF_INET6;
|
||
new->ai_next = *ck->res;
|
||
*ck->res = new;
|
||
ck->revents = DNSQUERY_OK;
|
||
} else {
|
||
/* No ip given, but still callback was called: timeout! */
|
||
ck->revents = DNSQUERY_FAIL;
|
||
}
|
||
|
||
if (ck->block)
|
||
pico_signal_send(ck->signal);
|
||
}
|
||
#endif
|
||
|
||
static void dns_ip4_cb(char *ip, void *arg)
|
||
{
|
||
struct dnsquery_cookie *ck = (struct dnsquery_cookie *)arg;
|
||
struct addrinfo *new;
|
||
if (ip) {
|
||
new = PICO_ZALLOC(sizeof(struct addrinfo));
|
||
if (!new) {
|
||
ck->revents = DNSQUERY_FAIL;
|
||
if (ck->block)
|
||
pico_signal_send(ck->signal);
|
||
return;
|
||
}
|
||
new->ai_family = AF_INET;
|
||
new->ai_addr = PICO_ZALLOC(sizeof(struct sockaddr_in));
|
||
if (!new->ai_addr) {
|
||
PICO_FREE(new);
|
||
ck->revents = DNSQUERY_FAIL;
|
||
if (ck->block)
|
||
pico_signal_send(ck->signal);
|
||
return;
|
||
}
|
||
new->ai_addrlen = sizeof(struct sockaddr_in);
|
||
pico_string_to_ipv4(ip, &(((struct sockaddr_in*)new->ai_addr)->sin_addr.s_addr));
|
||
((struct sockaddr_in*)(new->ai_addr))->sin_family = AF_INET;
|
||
new->ai_next = *ck->res;
|
||
*ck->res = new;
|
||
ck->revents = DNSQUERY_OK;
|
||
} else {
|
||
/* No ip given, but still callback was called: timeout! */
|
||
ck->revents = DNSQUERY_FAIL;
|
||
}
|
||
if (ck->block)
|
||
pico_signal_send(ck->signal);
|
||
}
|
||
|
||
#ifdef PICO_SUPPORT_DNS_CLIENT
|
||
int pico_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res)
|
||
{
|
||
struct dnsquery_cookie *ck4 = NULL;
|
||
struct dnsquery_cookie *ck6 = NULL;
|
||
struct sockaddr_in sa4;
|
||
*res = NULL;
|
||
(void)service;
|
||
bsd_dbg("Called pico_getaddrinfo, looking for %s\n", node);
|
||
|
||
#ifdef PICO_SUPPORT_IPV6
|
||
struct sockaddr_in6 sa6;
|
||
if (pico_string_to_ipv6(node, sa6.sin6_addr.s6_addr) == 0) {
|
||
ck6 = dnsquery_cookie_create(res, 0);
|
||
dns_ip6_cb((char *)node, ck6);
|
||
dnsquery_cookie_delete(ck6);
|
||
return 0;
|
||
}
|
||
#endif
|
||
|
||
if (pico_string_to_ipv4(node, &sa4.sin_addr.s_addr) == 0) {
|
||
ck4 = dnsquery_cookie_create(res, 0);
|
||
dns_ip4_cb((char*)node, ck4);
|
||
dnsquery_cookie_delete(ck4);
|
||
return 0;
|
||
}
|
||
|
||
#ifdef PICO_SUPPORT_IPV6
|
||
{
|
||
if (!hints || (hints->ai_family == AF_INET6)) {
|
||
ck6 = dnsquery_cookie_create(res, 1);
|
||
if (!ck6)
|
||
return -1;
|
||
pico_mutex_lock(picoLock);
|
||
if (pico_dns_client_getaddr6(node, dns_ip6_cb, ck6) < 0)
|
||
{
|
||
bsd_dbg("Error resolving AAAA record %s\n", node);
|
||
dnsquery_cookie_delete(ck6);
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
bsd_dbg("Resolving AAAA record %s\n", node);
|
||
pico_mutex_unlock(picoLock);
|
||
}
|
||
}
|
||
#endif /* PICO_SUPPORT_IPV6 */
|
||
|
||
if (!hints || (hints->ai_family == AF_INET)) {
|
||
ck4 = dnsquery_cookie_create(res, 1);
|
||
pico_mutex_lock(picoLock);
|
||
if (pico_dns_client_getaddr(node, dns_ip4_cb, ck4) < 0)
|
||
{
|
||
bsd_dbg("Error resolving A record %s\n", node);
|
||
dnsquery_cookie_delete(ck4);
|
||
pico_mutex_unlock(picoLock);
|
||
return -1;
|
||
}
|
||
bsd_dbg("Resolving A record %s\n", node);
|
||
pico_mutex_unlock(picoLock);
|
||
}
|
||
|
||
#ifdef PICO_SUPPORT_IPV6
|
||
if (ck6) {
|
||
/* Signal is always sent; either dns resolved, or timeout/failure */
|
||
pico_signal_wait(ck6->signal);
|
||
dnsquery_cookie_delete(ck6);
|
||
}
|
||
#endif /* PICO_SUPPORT_IPV6 */
|
||
|
||
if (ck4) {
|
||
/* Signal is always sent; either dns resolved, or timeout/failure */
|
||
pico_signal_wait(ck4->signal);
|
||
dnsquery_cookie_delete(ck4);
|
||
}
|
||
|
||
if (*res)
|
||
return 0;
|
||
|
||
return -1;
|
||
}
|
||
|
||
void pico_freeaddrinfo(struct addrinfo *res)
|
||
{
|
||
struct addrinfo *cur = res;
|
||
struct addrinfo *nxt;
|
||
while(cur) {
|
||
if (cur->ai_addr)
|
||
PICO_FREE(cur->ai_addr);
|
||
nxt = cur->ai_next;
|
||
PICO_FREE(cur);
|
||
cur = nxt;
|
||
}
|
||
}
|
||
|
||
/* Legacy gethostbyname call implementation */
|
||
static struct hostent PRIV_HOSTENT = { };
|
||
struct hostent *pico_gethostbyname(const char *name)
|
||
{
|
||
struct addrinfo *res;
|
||
struct addrinfo hint = {.ai_family = AF_INET};
|
||
int ret;
|
||
if (!PRIV_HOSTENT.h_addr_list) {
|
||
/* Done only once: reserve space for 2 entries */
|
||
PRIV_HOSTENT.h_addr_list = PICO_ZALLOC(2 * sizeof(void*));
|
||
PRIV_HOSTENT.h_addr_list[1] = NULL;
|
||
}
|
||
ret = pico_getaddrinfo(name, NULL, &hint, &res);
|
||
if (ret == 0) {
|
||
if (PRIV_HOSTENT.h_name != NULL) {
|
||
PICO_FREE(PRIV_HOSTENT.h_name);
|
||
PRIV_HOSTENT.h_name = NULL;
|
||
}
|
||
if (PRIV_HOSTENT.h_addr_list[0] != NULL) {
|
||
PICO_FREE(PRIV_HOSTENT.h_addr_list[0]);
|
||
PRIV_HOSTENT.h_addr_list[0] = NULL;
|
||
}
|
||
PRIV_HOSTENT.h_name = PICO_ZALLOC(strlen(name));
|
||
if (!PRIV_HOSTENT.h_name) {
|
||
pico_freeaddrinfo(res);
|
||
return NULL;
|
||
}
|
||
strcpy(PRIV_HOSTENT.h_name, name);
|
||
PRIV_HOSTENT.h_addrtype = res->ai_addr->sa_family;
|
||
if (PRIV_HOSTENT.h_addrtype == AF_INET) {
|
||
PRIV_HOSTENT.h_length = 4;
|
||
PRIV_HOSTENT.h_addr_list[0] = PICO_ZALLOC(4);
|
||
if (!PRIV_HOSTENT.h_addr_list[0]) {
|
||
pico_freeaddrinfo(res);
|
||
return NULL;
|
||
}
|
||
memcpy (PRIV_HOSTENT.h_addr_list[0], &(((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr), 4);
|
||
} else {
|
||
/* Only IPv4 supported by this ancient call. */
|
||
pico_freeaddrinfo(res);
|
||
return NULL;
|
||
}
|
||
pico_freeaddrinfo(res);
|
||
return &PRIV_HOSTENT;
|
||
}
|
||
return NULL;
|
||
}
|
||
#endif
|
||
|
||
int pico_getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen)
|
||
{
|
||
struct pico_bsd_endpoint *ep = get_endpoint(sockfd, 1);
|
||
int ret;
|
||
bsd_dbg("called getsockopt\n");
|
||
VALIDATE_NULL(ep);
|
||
if (level != SOL_SOCKET) {
|
||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
if (!ep) {
|
||
pico_err = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
if (!optval) {
|
||
pico_err = PICO_ERR_EFAULT;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
|
||
if (optname == SO_ERROR)
|
||
{
|
||
*((int*)optval) = ep->error;
|
||
ep->error = 0;
|
||
return 0;
|
||
}
|
||
|
||
pico_mutex_lock(ep->mutex_lock);
|
||
ret = pico_socket_getoption(ep->s, sockopt_get_name(optname), optval);
|
||
pico_mutex_unlock(ep->mutex_lock);
|
||
return ret;
|
||
|
||
}
|
||
|
||
int pico_setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
|
||
{
|
||
|
||
struct pico_bsd_endpoint *ep = get_endpoint(sockfd, 1);
|
||
int ret;
|
||
VALIDATE_NULL(ep);
|
||
ep->error = PICO_ERR_NOERR;
|
||
bsd_dbg("called setsockopt\n");
|
||
if (level != SOL_SOCKET) {
|
||
pico_err = PICO_ERR_EPROTONOSUPPORT;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
if (!ep) {
|
||
pico_err = PICO_ERR_EINVAL;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
if (!optval) {
|
||
pico_err = PICO_ERR_EFAULT;
|
||
errno = pico_err;
|
||
return -1;
|
||
}
|
||
if ((optname == SO_REUSEADDR) || (optname == SO_REUSEPORT))
|
||
return 0; /* Pretend it was OK. */
|
||
if (optname == SO_ERROR)
|
||
return PICO_ERR_ENOPROTOOPT;
|
||
|
||
pico_mutex_lock(ep->mutex_lock);
|
||
ret = pico_socket_setoption(ep->s, sockopt_get_name(optname), (void *)optval);
|
||
pico_mutex_unlock(ep->mutex_lock);
|
||
return ret;
|
||
}
|
||
#ifdef PICO_SUPPORT_SNTP_CLIENT
|
||
int pico_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||
{
|
||
int ret;
|
||
(void)tz;
|
||
struct pico_timeval ptv;
|
||
|
||
ret= pico_sntp_gettimeofday(&ptv);
|
||
|
||
tv->tv_sec = ptv.tv_sec;
|
||
tv->tv_usec= ptv.tv_msec * 1000; /* pico_timeval uses milliseconds instead of microseconds */
|
||
return ret;
|
||
}
|
||
|
||
/* dummy function */
|
||
int pico_settimeofday(struct timeval *tv, struct timezone *tz)
|
||
{
|
||
(void)tz;
|
||
(void)tv;
|
||
return 0;
|
||
}
|
||
|
||
#else
|
||
|
||
static struct pico_timeval ptv = {0u,0u};
|
||
|
||
int pico_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||
{
|
||
int ret;
|
||
(void)tz;
|
||
|
||
tv->tv_sec = ptv.tv_sec;
|
||
tv->tv_usec= ptv.tv_msec * 1000; /* pico_timeval uses milliseconds instead of microseconds */
|
||
return 0;
|
||
}
|
||
|
||
int pico_settimeofday(struct timeval *tv, struct timezone *tz)
|
||
{
|
||
int ret;
|
||
(void)tz;
|
||
|
||
ptv.tv_sec = tv->tv_sec;
|
||
ptv.tv_msec= tv->tv_usec / 1000; /* pico_timeval uses milliseconds instead of microseconds */
|
||
return 0;
|
||
}
|
||
#endif
|
||
|
||
long XTIME(void) {
|
||
struct timeval t;
|
||
pico_gettimeofday(&t, NULL);
|
||
return (long)t.tv_sec;
|
||
}
|
||
|
||
const char *pico_inet_ntop(int af, const void *src, char *dst, socklen_t size)
|
||
{
|
||
if ((!dst) || (!src))
|
||
return NULL;
|
||
|
||
switch (af)
|
||
{
|
||
case AF_INET:
|
||
if (size < INET_ADDRSTRLEN)
|
||
return NULL;
|
||
pico_ipv4_to_string(dst, *((const uint32_t *)src));
|
||
break;
|
||
#ifdef PICO_SUPPORT_IPV6
|
||
case AF_INET6:
|
||
if (size < INET6_ADDRSTRLEN)
|
||
return NULL;
|
||
pico_ipv6_to_string(dst, ((struct in6_addr *)src)->s6_addr);
|
||
break;
|
||
#endif
|
||
default:
|
||
dst = NULL;
|
||
break;
|
||
}
|
||
return dst;
|
||
}
|
||
|
||
char *pico_inet_ntoa(struct in_addr in)
|
||
{
|
||
static char ipbuf[INET_ADDRSTRLEN];
|
||
pico_ipv4_to_string(ipbuf, (uint32_t)in.s_addr);
|
||
return ipbuf;
|
||
}
|
||
|
||
|
||
|
||
int pico_pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask)
|
||
{
|
||
/*
|
||
* EV_READ: sets the readfds
|
||
* EV_WRITE: sets the writefds
|
||
* EV_CONN: sets the readfds (a.k.a. someone connects to your listening socket)
|
||
* EV_CLOSE: sets the readfds, then next recv() returns 0;
|
||
* EV_FIN: sets the readfds, then next recv() returns 0;
|
||
* EV_ERR: sets the exceptfds
|
||
*/
|
||
|
||
int i = 0; /* socket fds */
|
||
int nfds_out = 0; /* amount of changed sockets */
|
||
(void) sigmask;
|
||
|
||
bsd_dbg_select("=== IN: PICO SELECT === readfds[0]: 0x%x -- writefds[0]: 0x%x\n", readfds?(*(uint8_t *)readfds):0, writefds?(*(uint8_t *)writefds):0);
|
||
|
||
pico_fd_set readfds_out = {};
|
||
pico_fd_set writefds_out = {};
|
||
pico_fd_set exceptfds_out = {};
|
||
/* First, loop over all possible file descriptors, check if one has an event pending that we're waiting for */
|
||
while (nfds_out == 0)
|
||
{
|
||
for (i = 0; i < nfds; i++)
|
||
{
|
||
struct pico_bsd_endpoint *ep = get_endpoint(i, 0);
|
||
bsd_dbg_select("\t~~~ SELECT: fds %d - ep:%p ", i, ep);
|
||
if (ep)
|
||
{
|
||
/* Is this endpoint still valid? */
|
||
if (!ep->in_use)
|
||
{
|
||
bsd_dbg_select(" ep->in_use = 0\n");
|
||
break;
|
||
}
|
||
|
||
/* READ event needed and available? */
|
||
if (readfds && PICO_FD_ISSET(i,readfds) && (ep->revents & (PICO_SOCK_EV_CONN | PICO_SOCK_EV_CLOSE | PICO_SOCK_EV_RD)))
|
||
{
|
||
bsd_dbg_select("- READ_EV - ");
|
||
nfds_out++;
|
||
PICO_FD_SET(i, &readfds_out);
|
||
}
|
||
|
||
/* Force write events on empty udp sockets */
|
||
if ((ep->proto == PICO_PROTO_UDP) && (ep->s->q_out.size < ep->s->q_out.max_size))
|
||
ep->revents |= PICO_SOCK_EV_WR;
|
||
|
||
/* WRITE event needed? and available? */
|
||
if (writefds && PICO_FD_ISSET(i,writefds) && (ep->revents & (PICO_SOCK_EV_WR)))
|
||
{
|
||
bsd_dbg_select("- WRITE_EV - ");
|
||
nfds_out++;
|
||
PICO_FD_SET(i, &writefds_out);
|
||
}
|
||
|
||
/* EXCEPTION event needed and available? */
|
||
if (exceptfds && PICO_FD_ISSET(i,exceptfds) && (ep->revents & (PICO_SOCK_EV_ERR)))
|
||
{
|
||
bsd_dbg_select("- EXCEPT_EV - ");
|
||
nfds_out++;
|
||
PICO_FD_SET(i, &exceptfds_out);
|
||
}
|
||
}
|
||
|
||
if (ep)
|
||
bsd_dbg_select("- s:%p - ev:%x", ep->s, ep->revents);
|
||
bsd_dbg_select("\n");
|
||
}
|
||
|
||
/* If there was a hit, break out of the loop */
|
||
if (nfds_out)
|
||
break;
|
||
|
||
/* If not, wait for a semaphore signaling an event from the stack */
|
||
if (pico_signal_wait_timeout(pico_signal_select, (timeout->tv_sec * 1000) + ((timeout->tv_nsec) / 1000000)) == -1)
|
||
{
|
||
/* On timeout, break out of the loop */
|
||
bsd_dbg_select("\t~~~ SELECT: TIMEOUT\n");
|
||
break;
|
||
} else {
|
||
/* Process the received event -> re-iterate */
|
||
bsd_dbg_select("\t~~~ SELECT: Socket event, re-iterating fds\n");
|
||
}
|
||
}
|
||
|
||
/* Copy back result only if descriptor was valid */
|
||
if (readfds)
|
||
memcpy(readfds, &readfds_out, sizeof(pico_fd_set));
|
||
if (writefds)
|
||
memcpy(writefds, &writefds_out, sizeof(pico_fd_set));
|
||
if (exceptfds)
|
||
memcpy(exceptfds, &exceptfds_out, sizeof(pico_fd_set));
|
||
|
||
bsd_dbg_select("=== OUT: PICO SELECT === fds changed: %d\n", nfds_out);
|
||
|
||
return nfds_out;
|
||
}
|
||
|
||
int pico_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
|
||
{
|
||
struct timespec ts;
|
||
if (timeout) {
|
||
ts.tv_sec = timeout->tv_sec;
|
||
ts.tv_nsec = timeout->tv_usec * 1000;
|
||
return pico_pselect(nfds, readfds, writefds, exceptfds, &ts, NULL);
|
||
} else
|
||
return pico_pselect(nfds, readfds, writefds, exceptfds, NULL, NULL);
|
||
}
|
||
|
||
int pico_ppoll(struct pollfd *pfd, nfds_t npfd, const struct timespec *timeout, const sigset_t *sigmask) {
|
||
int i;
|
||
int ret = 0;
|
||
(void) sigmask;
|
||
|
||
while (ret == 0) {
|
||
for (i = 0; i < npfd; i++) {
|
||
struct pico_bsd_endpoint *ep = get_endpoint(pfd[i].fd, 0);
|
||
pfd[i].revents = 0u;
|
||
|
||
/* Always polled events */
|
||
if (!ep) {
|
||
pfd[i].revents |= POLLNVAL;
|
||
}
|
||
if (!ep->in_use) {
|
||
pfd[i].revents |= POLLNVAL;
|
||
}
|
||
if (ep->revents & (PICO_SOCK_EV_FIN | PICO_SOCK_EV_ERR)) {
|
||
pfd[i].revents |= POLLERR;
|
||
ret++;
|
||
}
|
||
if (ep->revents & PICO_SOCK_EV_CLOSE)
|
||
pfd[i].revents |= POLLHUP; /* XXX: I am sure we mean POLLRDHUP ! see man 2 poll */
|
||
|
||
/* Checking POLLIN */
|
||
if ((pfd[i].events & POLLIN) && (ep->revents & (PICO_SOCK_EV_RD | PICO_SOCK_EV_CONN))) {
|
||
pfd[i].revents |= POLLIN;
|
||
if (pfd[i].events & POLLRDNORM)
|
||
pfd[i].revents |= POLLRDNORM;
|
||
}
|
||
/* Checking POLLOUT */
|
||
if ((pfd[i].events & POLLOUT) && (ep->revents & (PICO_SOCK_EV_WR))) {
|
||
pfd[i].revents |= POLLOUT;
|
||
if (pfd[i].events & POLLWRNORM)
|
||
pfd[i].revents |= POLLWRNORM;
|
||
}
|
||
|
||
if (pfd[i].revents != 0)
|
||
ret++;
|
||
} /* End for loop */
|
||
if ((ret == 0) && timeout && (pico_signal_wait_timeout(pico_signal_select, (timeout->tv_sec * 1000) + ((timeout->tv_nsec) / 1000000)) == -1))
|
||
return 0; /* Timeout */
|
||
} /* End while loop */
|
||
return ret;
|
||
}
|
||
|
||
int pico_poll(struct pollfd *pfd, nfds_t npfd, int timeout)
|
||
{
|
||
struct timespec ts = {0U, 0U};
|
||
if (timeout >= 0) {
|
||
ts.tv_sec = timeout / 1000;
|
||
ts.tv_nsec = (timeout % 1000) * 1000000;
|
||
return pico_ppoll(pfd, npfd, &ts, NULL);
|
||
} else {
|
||
return pico_ppoll(pfd, npfd, NULL, NULL);
|
||
}
|
||
}
|