nodemcu-firmware/app/mbedtls/app/lwIPSocket.c

1033 lines
26 KiB
C

/*
* ESPRSSIF MIT License
*
* Copyright (c) 2016 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
* it is free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include "lwip/netif.h"
#include "lwip/inet.h"
#include "netif/etharp.h"
#include "lwip/tcp.h"
#include "lwip/ip.h"
#include "lwip/init.h"
#include "lwip/tcp_impl.h"
#include "lwip/memp.h"
#include "ets_sys.h"
#include "os_type.h"
#include "lwip/mem.h"
#include "sys/socket.h"
#ifdef MEMLEAK_DEBUG
static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
#endif
/** The global array of available sockets */
static lwIP_sock sockets[NUM_SOCKETS];
/** Table to quickly map an lwIP error (err_t) to a socket error
* by using -err as an index */
static const int err_to_errno_table[] =
{
0, /* ERR_OK 0 No error, everything OK. */
// ENOMEM, /* ERR_MEM -1 Out of memory error. */
// ENOBUFS, /* ERR_BUF -2 Buffer error. */
// EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
// EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
// EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
// EINVAL, /* ERR_VAL -6 Illegal value. */
// EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
// ECONNABORTED, /* ERR_ABRT -8 Connection aborted. */
// ECONNRESET, /* ERR_RST -9 Connection reset. */
// ESHUTDOWN, /* ERR_CLSD -10 Connection closed. */
// ENOTCONN, /* ERR_CONN -11 Not connected. */
// EIO, /* ERR_ARG -12 Illegal argument. */
// EADDRINUSE, /* ERR_USE -13 Address in use. */
-1, /* ERR_IF -14 Low-level netif error */
-1, /* ERR_ISCONN -15 Already connected. */
};
#define ERR_TO_ERRNO_TABLE_SIZE \
(sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0]))
#define err_to_errno(err) \
((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \
err_to_errno_table[-(err)] : EIO)
#ifdef ERRNO
#ifndef set_errno
#define set_errno(err) errno = (err)
#endif
#else /* ERRNO */
#define set_errno(err)
#endif /* ERRNO */
#define sock_set_errno(sk, e) do { \
sk->err = (e); \
set_errno(sk->err); \
} while (0)
static lwIP_sock *get_socket(int s);
static int find_socket(lwIP_netconn *newconn)
{
int i = 0;
lwIP_ASSERT(newconn);
for (i = 0; i < NUM_SOCKETS; ++i)
{
if (sockets[i].conn && sockets[i].conn == newconn)
return i;
}
return -1;
}
static void remove_tcp(lwIP_netconn *conn)
{
struct tcp_pcb *pcb = NULL;
sint8 ret = ERR_OK;
lwIP_REQUIRE_ACTION(conn, exit, ret = ERR_ARG);
lwIP_REQUIRE_ACTION(conn->tcp, exit, ret = ERR_ARG);
pcb = conn->tcp;
tcp_arg(pcb, NULL);
tcp_recv(pcb, NULL);
tcp_sent(pcb, NULL);
tcp_poll(pcb, NULL, 0);
tcp_err(pcb, NULL);
exit:
return;
}
static void free_netconn(lwIP_netconn *netconn)
{
lwIP_ASSERT(netconn);
if (netconn->readbuf)
{
ringbuf_free(&netconn->readbuf);
}
os_free(netconn);
netconn = NULL;
}
static lwIP_netconn *
netconn_alloc(netconn_type type, void *arg)
{
sint8 ret = ERR_OK;
lwIP_netconn *netconn = NULL;
struct tcp_pcb *pcb = arg;
lwIP_REQUIRE_ACTION(pcb, exit, ret = ERR_ARG);
netconn = (lwIP_netconn *)os_zalloc(sizeof(lwIP_netconn));
lwIP_REQUIRE_ACTION(netconn, exit, ret = ERR_MEM);
netconn->readbuf = ringbuf_new(TCP_SND_BUF);
lwIP_REQUIRE_ACTION(netconn->readbuf, exit, ret = ERR_MEM);
system_overclock();
netconn->socket = -1;
exit:
if (ret != ERR_OK)
{
free_netconn(netconn);
}
return netconn;
}
static err_t netconn_getaddr(lwIP_netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
{
sint8 ret = ERR_OK;
lwIP_REQUIRE_ACTION(conn, exit, ret = ERR_ARG);
lwIP_REQUIRE_ACTION(addr, exit, ret = ERR_ARG);
lwIP_REQUIRE_ACTION(port, exit, ret = ERR_ARG);
lwIP_REQUIRE_ACTION(conn->tcp, exit, ret = ERR_ARG);
*port = local==1 ? conn->tcp->local_port : conn->tcp->remote_port;
*addr = local==1 ? conn->tcp->local_ip : conn->tcp->remote_ip;
exit:
return ret;
}
/** Free a socket. The socket's netconn must have been
* delete before!
*
* @param sock the socket to free
* @param is_tcp != 0 for TCP sockets, used to free lastdata
*/
static void free_socket(lwIP_sock *sock)
{
void *lastdata = NULL;
sock->conn = NULL;
sock->recv_data_len = 0;
sock->recv_index = 0;
sock->send_buffer = NULL;
sock->send_index = 0;
}
static err_t recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
lwIP_netconn *newconn = arg;
err = ESP_OK;
lwIP_REQUIRE_ACTION(newconn, exit, err = ESP_ARG);
if (p!= NULL)
{
struct pbuf *pthis = NULL;
if (newconn->readbuf != NULL)
{
for (pthis = p; pthis != NULL; pthis = pthis->next)
{
newconn->state = NETCONN_STATE_READ;
ringbuf_memcpy_into(newconn->readbuf, pthis->payload, pthis->len);
tcp_recved(newconn->tcp, pthis->len);
newconn->state = NETCONN_STATE_ESTABLISHED;
lwIP_EVENT_PARSE(find_socket(newconn), ERR_OK);
}
pbuf_free(p);
}
else
{
tcp_recved(newconn->tcp, p->tot_len);
pbuf_free(p);
err = ERR_MEM;
}
}
else
{
lwIP_EVENT_PARSE(find_socket(newconn), NETCONN_EVENT_CLOSE);
}
exit:
return err;
}
static err_t poll_tcp(void *arg, struct tcp_pcb *pcb)
{
lwIP_netconn *conn = arg;
lwIP_ASSERT(conn);
return ERR_OK;
}
static err_t sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
{
lwIP_netconn *conn = arg;
lwIP_ASSERT(conn);
conn->state = NETCONN_STATE_ESTABLISHED;
lwIP_EVENT_THREAD(find_socket(conn), NETCONN_EVENT_SEND, len);
return ERR_OK;
}
static void err_tcp(void *arg, err_t err)
{
lwIP_netconn *conn = arg;
lwIP_ASSERT(conn);
conn->state = NETCONN_STATE_ERROR;
ESP_LOG("%s %d %p\n",__FILE__, __LINE__, conn->tcp);
switch (conn->tcp->state)
{
case SYN_SENT:
if (conn->tcp->nrtx == TCP_SYNMAXRTX)
{
err = ERR_CONN;
}
break;
case ESTABLISHED:
if (conn->tcp->nrtx == TCP_MAXRTX)
{
err = ERR_TIMEOUT;
}
break;
default:
break;
}
lwIP_EVENT_PARSE(find_socket(conn), err);
return;
}
/**
* TCP callback function if a connection (opened by tcp_connect/do_connect) has
* been established (or reset by the remote host).
*
* @see tcp.h (struct tcp_pcb.connected) for parameters and return values
*/
static err_t do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
lwIP_netconn *conn = arg;
err = ERR_OK;
lwIP_REQUIRE_ACTION(conn, exit, err = ESP_ARG);
conn->state = NETCONN_STATE_ESTABLISHED;
conn->readbuf = ringbuf_new(TCP_SND_BUF);
lwIP_REQUIRE_ACTION(conn->readbuf, exit, err = ESP_MEM);
lwIP_EVENT_PARSE(find_socket(conn), ERR_OK);
exit:
return err;
}
static void setup_tcp(lwIP_netconn *conn)
{
struct tcp_pcb *pcb = NULL;
sint8 ret = ERR_OK;
lwIP_REQUIRE_ACTION(conn, exit, ret = ERR_ARG);
lwIP_REQUIRE_ACTION(conn->tcp, exit, ret = ERR_ARG);
pcb = conn->tcp;
tcp_arg(pcb, conn);
tcp_recv(pcb, recv_tcp);
tcp_sent(pcb, sent_tcp);
tcp_poll(pcb, poll_tcp, 4);
tcp_err(pcb, err_tcp);
exit:
return;
}
static err_t do_accepted(void *arg, struct tcp_pcb *newpcb, err_t err)
{
lwIP_netconn *newconn = NULL;
lwIP_netconn *conn = arg;
err = ERR_OK;
//Avoid two TCP connections coming in simultaneously
struct tcp_pcb *pactive_pcb;
int active_pcb_num=0;
for(pactive_pcb = tcp_active_pcbs; pactive_pcb != NULL; pactive_pcb = pactive_pcb->next){
if (pactive_pcb->state == ESTABLISHED ||pactive_pcb->state == SYN_RCVD){
active_pcb_num++;
if (active_pcb_num > MEMP_NUM_TCP_PCB){
ESP_LOG("%s %d active_pcb_number:%d\n",__FILE__, __LINE__,active_pcb_num);
return ERR_MEM;
}
}
}
lwIP_REQUIRE_ACTION(conn, exit, err = ESP_ARG);
/* We have to set the callback here even though
* the new socket is unknown. conn->socket is marked as -1. */
newconn = netconn_alloc(conn->type, newpcb);
lwIP_REQUIRE_ACTION(conn, exit, err = ERR_MEM);
newconn->tcp = newpcb;
setup_tcp(newconn);
newconn->state = NETCONN_STATE_ESTABLISHED;
conn->acceptmbox = newconn;
lwIP_EVENT_PARSE(find_socket(conn), ERR_OK);
exit:
return err;
}
sint8 netconn_delete(lwIP_netconn *conn)
{
sint8 error = ESP_OK;
lwIP_REQUIRE_ACTION(conn, exit, error = ESP_ARG);
tcp_recv(conn->tcp, NULL);
error = tcp_close(conn->tcp);
if (error != ERR_OK)
{
/* closing failed, try again later */
tcp_recv(conn->tcp, recv_tcp);
}
else
{
/* closing succeeded */
remove_tcp(conn);
}
free_netconn(conn);
exit:
return error;
}
sint8 netconn_bind(lwIP_netconn *conn, ip_addr_t *addr, u16_t port)
{
sint8 error = ESP_OK;
lwIP_REQUIRE_ACTION(conn, exit, error = ESP_ARG);
lwIP_REQUIRE_ACTION(addr, exit, error = ESP_ARG);
lwIP_REQUIRE_ACTION(conn->tcp, exit, error = ESP_ARG);
error = tcp_bind(conn->tcp, addr, port);
exit:
return error;
}
sint8 netconn_connect(lwIP_netconn *conn, ip_addr_t *addr, u16_t port)
{
sint8 error = ESP_OK;
lwIP_REQUIRE_ACTION(conn, exit, error = ESP_ARG);
lwIP_REQUIRE_ACTION(addr, exit, error = ESP_ARG);
lwIP_REQUIRE_ACTION(conn->tcp, exit, error = ESP_ARG);
setup_tcp(conn);
error = tcp_connect(conn->tcp, addr, port, do_connected);
exit:
return error;
}
err_t netconn_accept(lwIP_netconn *conn, lwIP_netconn **new_conn)
{
err_t error = ESP_OK;
lwIP_netconn *newconn = NULL;
lwIP_REQUIRE_ACTION(conn, exit, error = ESP_ARG);
lwIP_REQUIRE_ACTION(new_conn, exit, error = ESP_ARG);
*new_conn = NULL;
newconn = (lwIP_netconn *)conn->acceptmbox;
conn->acceptmbox = NULL;
lwIP_REQUIRE_ACTION(newconn, exit, error = ERR_CLSD);
*new_conn = newconn;
exit:
return error;
}
sint8 netconn_listen(lwIP_netconn *conn)
{
sint8 error = ESP_OK;
struct tcp_pcb *lpcb = NULL;
lwIP_REQUIRE_ACTION(conn, exit, error = ESP_ARG);
lwIP_REQUIRE_ACTION(conn->tcp, exit, error = ESP_ARG);
setup_tcp(conn);
lpcb = conn->tcp;
conn->tcp = tcp_listen(conn->tcp);
if (conn->tcp != NULL)
{
tcp_accept(conn->tcp, do_accepted);
}
else
{
conn->tcp = lpcb;
}
exit:
return error;
}
static int alloc_socket(lwIP_netconn *newconn, int accepted)
{
int i = 0;
lwIP_ASSERT(newconn);
/* allocate a new socket identifier */
for (i = 0; i < NUM_SOCKETS; ++i)
{
/* Protect socket array */
if (!sockets[i].conn)
{
sockets[i].conn = newconn;
return i;
}
}
return -1;
}
static lwIP_sock *get_socket(int s)
{
lwIP_sock *sock = NULL;
if ((s < 0) || (s >= NUM_SOCKETS))
{
return NULL;
}
sock = &sockets[s];
if (!sock->conn)
{
return NULL;
}
return sock;
}
int lwip_socket(int domain, int type, int protocol)
{
lwIP_netconn *conn = NULL;
int i = 0;
switch (type)
{
case SOCK_STREAM:
conn = (lwIP_netconn *)os_zalloc(sizeof(lwIP_netconn));
lwIP_REQUIRE_ACTION(conn, exit, i = ESP_MEM);
conn->tcp = tcp_new();
lwIP_REQUIRE_ACTION(conn->tcp, exit, i = ESP_MEM);
conn->socket = -1;
break;
default:
return -1;
}
i = alloc_socket(conn, 0);
if (i == -1)
{
goto exit;
}
conn->socket = i;
exit:
if (i == -1)
{
if (conn->tcp)
memp_free(MEMP_TCP_PCB,conn->tcp);
if (conn)
os_free(conn);
}
return i;
}
int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
{
lwIP_sock *sock = NULL;
ip_addr_t local_addr;
uint16 local_port = 0;
sint8 err = ERR_OK;
const struct sockaddr_in *name_in = NULL;
sock = get_socket(s);
if (!sock)
{
return -1;
}
/* check size, familiy and alignment of 'name' */
LWIP_ERROR("lwip_bind: invalid address",
((namelen == sizeof(struct sockaddr_in)) && ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)),
return -1;);
name_in = (const struct sockaddr_in *) (void*) name;
inet_addr_to_ipaddr(&local_addr, &name_in->sin_addr);
local_port = name_in->sin_port;
err = netconn_bind(sock->conn, &local_addr, ntohs(local_port));
if (err != ESP_OK)
{
ESP_LOG("%s %d, err=%d\n", __FILE__, __LINE__, err);
return -1;
}
ESP_LOG("%s %d, %d succeeded\n", __FILE__, __LINE__, s);
return ERR_OK;
}
int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
{
lwIP_sock *sock = NULL;
err_t err = ERR_OK;
ip_addr_t remote_addr;
u16_t remote_port = 0;
const struct sockaddr_in *name_in = NULL;
sock = get_socket(s);
if (!sock)
{
return -1;
}
/* check size, familiy and alignment of 'name' */
LWIP_ERROR("lwip_connect: invalid address",
((namelen == sizeof(struct sockaddr_in)) && ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)),
return -1;);
name_in = (const struct sockaddr_in *) (void*) name;
inet_addr_to_ipaddr(&remote_addr, &name_in->sin_addr);
remote_port = name_in->sin_port;
err = netconn_connect(sock->conn, &remote_addr, ntohs(remote_port));
if (err != ERR_OK)
{
ESP_LOG("lwip_connect(%d) failed, err=%d\n", s, err);
return -1;
}
ESP_LOG("lwip_connect(%d) succeeded\n", s);
return ERR_OK;
}
int lwip_fcntl(int s, int cmd, int val)
{
lwIP_sock *sock = get_socket(s);
int ret = -1;
if (!sock || !sock->conn)
{
return -1;
}
switch (cmd)
{
case F_GETFL:
ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
break;
case F_SETFL:
if ((val & ~O_NONBLOCK) == 0)
{
/* only O_NONBLOCK, all other bits are zero */
netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
ret = 0;
}
break;
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
break;
}
return ret;
}
uint32_t lwip_getul(char *str)
{
uint32 ret = 0;
while (isdigit(*str))
{
ret = ret * 10 + *str++ - '0';
}
return ret;
}
int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
{
lwIP_sock *sock = NULL;
err_t err = ERR_OK;
lwIP_netconn *newconn = NULL;
int newsock = -1;
sock = get_socket(s);
if (!sock)
{
return -1;
}
/* wait for a new connection */
err = netconn_accept(sock->conn, &newconn);
lwIP_REQUIRE_NOERROR(err, exit);
newsock = alloc_socket(newconn, 0);
if (newsock == -1)
{
goto exit;
}
newconn->socket = newsock;
exit:
if (newsock == -1)
{
netconn_delete(newconn);
}
return newsock;
}
int lwip_listen(int s, int backlog)
{
lwIP_sock *sock = NULL;
err_t err = ERR_OK;
sock = get_socket(s);
if (!sock)
{
return -1;
}
err = netconn_listen(sock->conn);
if (err != ERR_OK)
{
ESP_LOG("lwip_connect(%d) failed, err=%d\n", s, err);
return -1;
}
ESP_LOG("lwip_connect(%d) succeeded\n", s);
return ERR_OK;
}
int lwip_recvfrom(int s, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
{
lwIP_sock *sock = NULL;
size_t bytes_used = 0;
int is_tcp = 0;
lwIP_ASSERT(mem);
sock = get_socket(s);
if (!sock)
{
return -1;
}
if (sock->conn != NULL)
{
if (sock->conn->state == NETCONN_STATE_ESTABLISHED)
{
bytes_used = ringbuf_bytes_used(sock->conn->readbuf);
if (bytes_used != 0)
{
if (len > bytes_used)
{
len = bytes_used;
}
ringbuf_memcpy_from(mem, sock->conn->readbuf, len);
return len;
}
else
{
return 0;
}
}
else
{
return 0;
}
}
else
{
return -1;
}
}
int lwip_read(int s, void *mem, size_t len)
{
return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
}
int lwip_recv(int s, void *mem, size_t len, int flags)
{
return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
}
int lwip_send(int s, const void *data, size_t size, int flags)
{
lwIP_sock *sock = NULL;
size_t bytes_used = 0;
err_t Err = ERR_OK;
lwIP_ASSERT(data);
sock = get_socket(s);
if (!sock)
{
return -1;
}
if (tcp_sndbuf(sock->conn->tcp) < size)
{
bytes_used = tcp_sndbuf(sock->conn->tcp);
} else{
bytes_used = size;
}
if (bytes_used > 2 * sock->conn->tcp->mss)
{
bytes_used = 2 * sock->conn->tcp->mss;
}
do
{
Err = tcp_write(sock->conn->tcp, data, bytes_used, 1);
if (Err == ERR_MEM)
size /= 2;
}
while (Err == ERR_MEM && size > 1);
if (Err == ERR_OK)
{
Err = tcp_output(sock->conn->tcp);
} else{
size = Err;
}
return size;
}
int lwip_close(int s)
{
lwIP_sock *sock = NULL;
int err = 0;
sock = get_socket(s);
if (!sock)
{
return -1;
}
/*Do not set callback function when tcp->state is LISTEN.
Avoid memory overlap when conn->tcp changes from
struct tcp_bcb to struct tcp_pcb_listen after lwip_listen.*/
if (sock->conn->tcp->state != LISTEN)
{
if (sock->conn->state != NETCONN_STATE_ERROR){
tcp_recv(sock->conn->tcp, NULL);
err = tcp_close(sock->conn->tcp);
if (err != ERR_OK)
{
/* closing failed, try again later */
tcp_recv(sock->conn->tcp, recv_tcp);
return -1;
}
}
/* closing succeeded */
remove_tcp(sock->conn);
} else {
tcp_close(sock->conn->tcp);
}
free_netconn(sock->conn);
free_socket(sock);
return ERR_OK;
}
int lwip_write(int s, const void *data, size_t size)
{
lwIP_sock *sock = NULL;
int is_tcp = 0;
size_t bytes_free = 0;
sock = get_socket(s);
if (!sock)
{
return -1;
}
if (sock->conn != NULL)
{
switch (sock->conn->state)
{
case NETCONN_STATE_ESTABLISHED:
return lwip_send(s, data, size, 0);
default:
return -1;
}
}
else
{
return -1;
}
}
static int
lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
{
lwIP_sock *sock = NULL;
struct sockaddr_in sin;
ip_addr_t naddr;
lwIP_ASSERT(name);
lwIP_ASSERT(namelen);
sock = get_socket(s);
if (!sock)
{
return -1;
}
os_memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
/* get the IP address and port */
netconn_getaddr(sock->conn, &naddr, &sin.sin_port, local);
sin.sin_port = htons(sin.sin_port);
inet_addr_from_ipaddr(&sin.sin_addr, &naddr);
if (*namelen > sizeof(sin))
{
*namelen = sizeof(sin);
}
MEMCPY(name, &sin, *namelen);
return 0;
}
int lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
{
return lwip_getaddrname(s, name, namelen, 0);
}
int lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
{
return lwip_getaddrname(s, name, namelen, 1);
}
int lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
{
lwIP_sock *sock = NULL;
err_t err = ERR_OK;
lwIP_REQUIRE_ACTION(optval, exit, err = ESP_ARG);
lwIP_REQUIRE_ACTION(optlen, exit, err = ESP_ARG);
sock = get_socket(s);
lwIP_REQUIRE_ACTION(sock, exit, err = ESP_MEM);
switch (level)
{
/* Level: SOL_SOCKET */
case SOL_SOCKET:
switch (optname)
{
/* The option flags */
case SO_ACCEPTCONN:
case SO_BROADCAST:
case SO_KEEPALIVE:
#if SO_REUSE
case SO_REUSEADDR:
case SO_REUSEPORT:
#endif /* SO_REUSE */
*(int*)optval = sock->conn->tcp->so_options & optname;
break;
case SO_TYPE:
switch (NETCONNTYPE_GROUP(sock->conn->type))
{
case NETCONN_TCP:
*(int*)optval = SOCK_STREAM;
break;
case NETCONN_UDP:
*(int*)optval = SOCK_DGRAM;
break;
default:
*(int*)optval = sock->conn->type;
break;
}
break;
break;
}
break;
/* Level: IPPROTO_IP */
case IPPROTO_IP:
break;
#if LWIP_TCP
/* Level: IPPROTO_TCP */
case IPPROTO_TCP:
if (*optlen < sizeof(int))
{
err = ESP_ARG;
lwIP_REQUIRE_NOERROR(err, exit);
}
/* If this is no TCP socket, ignore any options. */
if (sock->conn->type != NETCONN_TCP)
{
err = ESP_ARG;
lwIP_REQUIRE_NOERROR(err, exit);
}
switch (optname)
{
case TCP_NODELAY:
case TCP_KEEPALIVE:
*(int*)optval = (int)sock->conn->tcp->keep_idle;
break;
#if LWIP_TCP_KEEPALIVE
case TCP_KEEPIDLE:
*(int*)optval = (int)(sock->conn->tcp->keep_idle/1000);
break;
case TCP_KEEPINTVL:
*(int*)optval = (int)(sock->conn->tcp->keep_intvl/1000);
break;
case TCP_KEEPCNT:
*(int*)optval = (int)sock->conn->tcp->keep_cnt;
#endif /* LWIP_TCP_KEEPALIVE */
break;
default:
err = ESP_ARG;
lwIP_REQUIRE_NOERROR(err, exit);
break;
} /* switch (optname) */
break;
#endif /* LWIP_TCP */
default:
err = ESP_ARG;
lwIP_REQUIRE_NOERROR(err, exit);
break;
}
exit:
return err;
}
int lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
{
lwIP_sock *sock = NULL;
err_t err = ERR_OK;
lwIP_REQUIRE_ACTION(optval, exit, err = ESP_ARG);
sock = get_socket(s);
lwIP_REQUIRE_ACTION(sock, exit, err = ESP_MEM);
lwIP_REQUIRE_ACTION(sock->conn, exit, err = ESP_MEM);
lwIP_REQUIRE_ACTION(sock->conn->tcp, exit, err = ESP_MEM);
switch (level)
{
/* Level: SOL_SOCKET */
case SOL_SOCKET:
switch (optname)
{
case SO_KEEPALIVE:
if (optlen < sizeof(int))
{
err = ESP_ARG;
lwIP_REQUIRE_NOERROR(err, exit);
}
if (*(int*)optval)
{
sock->conn->tcp->so_options |= optname;
}
else
{
sock->conn->tcp->so_options &= ~optname;
}
break;
}
break;
/* Level: IPPROTO_IP */
case IPPROTO_IP:
break;
/* Level: IPPROTO_TCP */
case IPPROTO_TCP:
if (optlen < sizeof(int))
{
err = ESP_ARG;
lwIP_REQUIRE_NOERROR(err, exit);
}
/* If this is no TCP socket, ignore any options. */
if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP)
{
err = ESP_ARG;
lwIP_REQUIRE_NOERROR(err, exit);
}
switch (optname)
{
case TCP_KEEPALIVE:
sock->conn->tcp->keep_idle = (u32_t) (*(int*) optval);
break;
#if LWIP_TCP_KEEPALIVE
case TCP_KEEPIDLE:
sock->conn->tcp->keep_idle = 1000 * (u32_t) (*(int*) optval);
break;
case TCP_KEEPINTVL:
sock->conn->tcp->keep_intvl = 1000 * (u32_t) (*(int*) optval);
break;
case TCP_KEEPCNT:
sock->conn->tcp->keep_cnt = (u32_t) (*(int*) optval);
break;
#endif /* LWIP_TCP_KEEPALIVE */
default:
err = ESP_ARG;
lwIP_REQUIRE_NOERROR(err, exit);
break;
}
break;
/* UNDEFINED LEVEL */
default:
err = ESP_ARG;
lwIP_REQUIRE_NOERROR(err, exit);
break;
}
exit:
return err;
}