2015-10-12 00:43:33 +01:00
|
|
|
/*
|
2017-02-15 16:20:46 +00:00
|
|
|
* This file is part of the MicroPython project, http://micropython.org/
|
2015-10-12 00:43:33 +01:00
|
|
|
*
|
|
|
|
* The MIT License (MIT)
|
|
|
|
*
|
|
|
|
* Copyright (c) 2013, 2014 Damien P. George
|
|
|
|
* Copyright (c) 2015 Galen Hazelwood
|
2017-07-29 16:09:33 +01:00
|
|
|
* Copyright (c) 2015-2017 Paul Sokolovsky
|
2015-10-12 00:43:33 +01:00
|
|
|
*
|
|
|
|
* Permission is hereby granted, 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 <string.h>
|
2015-12-31 20:54:28 +00:00
|
|
|
#include <stdio.h>
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
#include "py/objlist.h"
|
|
|
|
#include "py/runtime.h"
|
2015-10-26 21:04:33 +00:00
|
|
|
#include "py/stream.h"
|
2016-05-12 12:49:14 +01:00
|
|
|
#include "py/mperrno.h"
|
2015-11-02 06:46:58 +00:00
|
|
|
#include "py/mphal.h"
|
2015-10-12 00:43:33 +01:00
|
|
|
|
2017-03-31 12:29:39 +01:00
|
|
|
#include "lib/netutils/netutils.h"
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
#include "lwip/init.h"
|
|
|
|
#include "lwip/tcp.h"
|
|
|
|
#include "lwip/udp.h"
|
|
|
|
//#include "lwip/raw.h"
|
|
|
|
#include "lwip/dns.h"
|
2017-07-31 20:38:37 +01:00
|
|
|
#include "lwip/igmp.h"
|
2018-05-17 03:58:34 +01:00
|
|
|
#if LWIP_VERSION_MAJOR < 2
|
|
|
|
#include "lwip/timers.h"
|
|
|
|
#include "lwip/tcp_impl.h"
|
|
|
|
#else
|
|
|
|
#include "lwip/timeouts.h"
|
|
|
|
#include "lwip/priv/tcp_priv.h"
|
|
|
|
#endif
|
2015-10-12 00:43:33 +01:00
|
|
|
|
2016-04-13 23:15:52 +01:00
|
|
|
#if 0 // print debugging info
|
|
|
|
#define DEBUG_printf DEBUG_printf
|
|
|
|
#else // don't print debugging info
|
|
|
|
#define DEBUG_printf(...) (void)0
|
|
|
|
#endif
|
|
|
|
|
2017-07-31 20:38:37 +01:00
|
|
|
// All socket options should be globally distinct,
|
|
|
|
// because we ignore option levels for efficiency.
|
|
|
|
#define IP_ADD_MEMBERSHIP 0x400
|
|
|
|
|
2016-03-25 15:44:24 +00:00
|
|
|
// For compatibilily with older lwIP versions.
|
|
|
|
#ifndef ip_set_option
|
|
|
|
#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt))
|
|
|
|
#endif
|
2016-03-25 18:53:52 +00:00
|
|
|
#ifndef ip_reset_option
|
|
|
|
#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt))
|
|
|
|
#endif
|
2016-03-25 15:44:24 +00:00
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
#ifdef MICROPY_PY_LWIP_SLIP
|
|
|
|
#include "netif/slipif.h"
|
2015-10-26 21:04:33 +00:00
|
|
|
#include "lwip/sio.h"
|
2015-10-12 00:43:33 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef MICROPY_PY_LWIP_SLIP
|
|
|
|
/******************************************************************************/
|
|
|
|
// Slip object for modlwip. Requires a serial driver for the port that supports
|
|
|
|
// the lwip serial callback functions.
|
|
|
|
|
|
|
|
typedef struct _lwip_slip_obj_t {
|
|
|
|
mp_obj_base_t base;
|
|
|
|
struct netif lwip_netif;
|
|
|
|
} lwip_slip_obj_t;
|
|
|
|
|
|
|
|
// Slip object is unique for now. Possibly can fix this later. FIXME
|
|
|
|
STATIC lwip_slip_obj_t lwip_slip_obj;
|
|
|
|
|
|
|
|
// Declare these early.
|
2015-10-26 22:23:07 +00:00
|
|
|
void mod_lwip_register_poll(void (*poll)(void *arg), void *poll_arg);
|
|
|
|
void mod_lwip_deregister_poll(void (*poll)(void *arg), void *poll_arg);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
STATIC void slip_lwip_poll(void *netif) {
|
2015-10-26 22:23:07 +00:00
|
|
|
slipif_poll((struct netif*)netif);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
STATIC const mp_obj_type_t lwip_slip_type;
|
|
|
|
|
2015-10-26 21:04:33 +00:00
|
|
|
// lwIP SLIP callback functions
|
|
|
|
sio_fd_t sio_open(u8_t dvnum) {
|
|
|
|
// We support singleton SLIP interface, so just return any truish value.
|
|
|
|
return (sio_fd_t)1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sio_send(u8_t c, sio_fd_t fd) {
|
|
|
|
mp_obj_type_t *type = mp_obj_get_type(MP_STATE_VM(lwip_slip_stream));
|
|
|
|
int error;
|
|
|
|
type->stream_p->write(MP_STATE_VM(lwip_slip_stream), &c, 1, &error);
|
|
|
|
}
|
|
|
|
|
|
|
|
u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len) {
|
|
|
|
mp_obj_type_t *type = mp_obj_get_type(MP_STATE_VM(lwip_slip_stream));
|
|
|
|
int error;
|
|
|
|
mp_uint_t out_sz = type->stream_p->read(MP_STATE_VM(lwip_slip_stream), data, len, &error);
|
|
|
|
if (out_sz == MP_STREAM_ERROR) {
|
|
|
|
if (mp_is_nonblocking_error(error)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// Can't do much else, can we?
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return out_sz;
|
|
|
|
}
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
// constructor lwip.slip(device=integer, iplocal=string, ipremote=string)
|
2017-01-04 13:10:42 +00:00
|
|
|
STATIC mp_obj_t lwip_slip_make_new(mp_obj_t type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
2015-10-12 00:43:33 +01:00
|
|
|
mp_arg_check_num(n_args, n_kw, 3, 3, false);
|
|
|
|
|
|
|
|
lwip_slip_obj.base.type = &lwip_slip_type;
|
|
|
|
|
2015-10-26 21:04:33 +00:00
|
|
|
MP_STATE_VM(lwip_slip_stream) = args[0];
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
ip_addr_t iplocal, ipremote;
|
|
|
|
if (!ipaddr_aton(mp_obj_str_get_str(args[1]), &iplocal)) {
|
2017-08-09 05:40:45 +01:00
|
|
|
mp_raise_ValueError("not a valid local IP");
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
if (!ipaddr_aton(mp_obj_str_get_str(args[2]), &ipremote)) {
|
2017-08-09 05:40:45 +01:00
|
|
|
mp_raise_ValueError("not a valid remote IP");
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
2015-10-26 22:37:35 +00:00
|
|
|
struct netif *n = &lwip_slip_obj.lwip_netif;
|
2015-10-26 21:04:33 +00:00
|
|
|
if (netif_add(n, &iplocal, IP_ADDR_BROADCAST, &ipremote, NULL, slipif_init, ip_input) == NULL) {
|
2017-08-09 05:40:45 +01:00
|
|
|
mp_raise_ValueError("out of memory");
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
netif_set_up(n);
|
|
|
|
netif_set_default(n);
|
|
|
|
mod_lwip_register_poll(slip_lwip_poll, n);
|
|
|
|
|
|
|
|
return (mp_obj_t)&lwip_slip_obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC mp_obj_t lwip_slip_status(mp_obj_t self_in) {
|
|
|
|
// Null function for now.
|
|
|
|
return mp_const_none;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(lwip_slip_status_obj, lwip_slip_status);
|
|
|
|
|
2017-07-29 16:09:33 +01:00
|
|
|
STATIC const mp_rom_map_elem_t lwip_slip_locals_dict_table[] = {
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&lwip_slip_status_obj) },
|
2015-10-12 00:43:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
STATIC MP_DEFINE_CONST_DICT(lwip_slip_locals_dict, lwip_slip_locals_dict_table);
|
|
|
|
|
|
|
|
STATIC const mp_obj_type_t lwip_slip_type = {
|
|
|
|
{ &mp_type_type },
|
|
|
|
.name = MP_QSTR_slip,
|
|
|
|
.make_new = lwip_slip_make_new,
|
2017-07-29 16:09:33 +01:00
|
|
|
.locals_dict = (mp_obj_dict_t*)&lwip_slip_locals_dict,
|
2015-10-12 00:43:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif // MICROPY_PY_LWIP_SLIP
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
// Table to convert lwIP err_t codes to socket errno codes, from the lwIP
|
|
|
|
// socket API.
|
|
|
|
|
2018-05-17 03:58:34 +01:00
|
|
|
// lwIP 2 changed LWIP_VERSION and it can no longer be used in macros,
|
|
|
|
// so we define our own equivalent version that can.
|
|
|
|
#define LWIP_VERSION_MACRO (LWIP_VERSION_MAJOR << 24 | LWIP_VERSION_MINOR << 16 \
|
|
|
|
| LWIP_VERSION_REVISION << 8 | LWIP_VERSION_RC)
|
|
|
|
|
2015-12-30 14:43:35 +00:00
|
|
|
// Extension to lwIP error codes
|
|
|
|
#define _ERR_BADF -16
|
2016-03-25 18:32:01 +00:00
|
|
|
// TODO: We just know that change happened somewhere between 1.4.0 and 1.4.1,
|
|
|
|
// investigate in more detail.
|
2018-05-17 03:58:34 +01:00
|
|
|
#if LWIP_VERSION_MACRO < 0x01040100
|
2015-10-12 00:43:33 +01:00
|
|
|
static const int error_lookup_table[] = {
|
2016-05-12 12:49:14 +01:00
|
|
|
0, /* ERR_OK 0 No error, everything OK. */
|
|
|
|
MP_ENOMEM, /* ERR_MEM -1 Out of memory error. */
|
|
|
|
MP_ENOBUFS, /* ERR_BUF -2 Buffer error. */
|
|
|
|
MP_EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
|
|
|
|
MP_EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
|
|
|
|
MP_EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
|
|
|
|
MP_EINVAL, /* ERR_VAL -6 Illegal value. */
|
|
|
|
MP_EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
|
|
|
|
|
|
|
|
MP_ECONNABORTED, /* ERR_ABRT -8 Connection aborted. */
|
|
|
|
MP_ECONNRESET, /* ERR_RST -9 Connection reset. */
|
|
|
|
MP_ENOTCONN, /* ERR_CLSD -10 Connection closed. */
|
|
|
|
MP_ENOTCONN, /* ERR_CONN -11 Not connected. */
|
|
|
|
MP_EIO, /* ERR_ARG -12 Illegal argument. */
|
|
|
|
MP_EADDRINUSE, /* ERR_USE -13 Address in use. */
|
|
|
|
-1, /* ERR_IF -14 Low-level netif error */
|
|
|
|
MP_EALREADY, /* ERR_ISCONN -15 Already connected. */
|
|
|
|
MP_EBADF, /* _ERR_BADF -16 Closed socket (null pcb) */
|
2016-03-25 18:32:01 +00:00
|
|
|
};
|
2018-05-17 03:58:34 +01:00
|
|
|
#elif LWIP_VERSION_MACRO < 0x02000000
|
2016-03-25 18:32:01 +00:00
|
|
|
static const int error_lookup_table[] = {
|
2016-05-12 12:49:14 +01:00
|
|
|
0, /* ERR_OK 0 No error, everything OK. */
|
|
|
|
MP_ENOMEM, /* ERR_MEM -1 Out of memory error. */
|
|
|
|
MP_ENOBUFS, /* ERR_BUF -2 Buffer error. */
|
|
|
|
MP_EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
|
|
|
|
MP_EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
|
|
|
|
MP_EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
|
|
|
|
MP_EINVAL, /* ERR_VAL -6 Illegal value. */
|
|
|
|
MP_EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
|
|
|
|
|
|
|
|
MP_EADDRINUSE, /* ERR_USE -8 Address in use. */
|
|
|
|
MP_EALREADY, /* ERR_ISCONN -9 Already connected. */
|
|
|
|
MP_ECONNABORTED, /* ERR_ABRT -10 Connection aborted. */
|
|
|
|
MP_ECONNRESET, /* ERR_RST -11 Connection reset. */
|
|
|
|
MP_ENOTCONN, /* ERR_CLSD -12 Connection closed. */
|
|
|
|
MP_ENOTCONN, /* ERR_CONN -13 Not connected. */
|
|
|
|
MP_EIO, /* ERR_ARG -14 Illegal argument. */
|
|
|
|
-1, /* ERR_IF -15 Low-level netif error */
|
|
|
|
MP_EBADF, /* _ERR_BADF -16 Closed socket (null pcb) */
|
2015-10-12 00:43:33 +01:00
|
|
|
};
|
2018-05-17 03:58:34 +01:00
|
|
|
#else
|
|
|
|
// Matches lwIP 2.0.3
|
|
|
|
#undef _ERR_BADF
|
|
|
|
#define _ERR_BADF -17
|
|
|
|
static const int error_lookup_table[] = {
|
|
|
|
0, /* ERR_OK 0 No error, everything OK */
|
|
|
|
MP_ENOMEM, /* ERR_MEM -1 Out of memory error */
|
|
|
|
MP_ENOBUFS, /* ERR_BUF -2 Buffer error */
|
|
|
|
MP_EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
|
|
|
|
MP_EHOSTUNREACH, /* ERR_RTE -4 Routing problem */
|
|
|
|
MP_EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
|
|
|
|
MP_EINVAL, /* ERR_VAL -6 Illegal value */
|
|
|
|
MP_EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block */
|
|
|
|
MP_EADDRINUSE, /* ERR_USE -8 Address in use */
|
|
|
|
MP_EALREADY, /* ERR_ALREADY -9 Already connecting */
|
|
|
|
MP_EALREADY, /* ERR_ISCONN -10 Conn already established */
|
|
|
|
MP_ENOTCONN, /* ERR_CONN -11 Not connected */
|
|
|
|
-1, /* ERR_IF -12 Low-level netif error */
|
|
|
|
MP_ECONNABORTED, /* ERR_ABRT -13 Connection aborted */
|
|
|
|
MP_ECONNRESET, /* ERR_RST -14 Connection reset */
|
|
|
|
MP_ENOTCONN, /* ERR_CLSD -15 Connection closed */
|
|
|
|
MP_EIO, /* ERR_ARG -16 Illegal argument. */
|
|
|
|
MP_EBADF, /* _ERR_BADF -17 Closed socket (null pcb) */
|
|
|
|
};
|
2016-03-25 18:32:01 +00:00
|
|
|
#endif
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
/*******************************************************************************/
|
|
|
|
// The socket object provided by lwip.socket.
|
|
|
|
|
|
|
|
#define MOD_NETWORK_AF_INET (2)
|
|
|
|
#define MOD_NETWORK_AF_INET6 (10)
|
|
|
|
|
|
|
|
#define MOD_NETWORK_SOCK_STREAM (1)
|
|
|
|
#define MOD_NETWORK_SOCK_DGRAM (2)
|
|
|
|
#define MOD_NETWORK_SOCK_RAW (3)
|
|
|
|
|
|
|
|
typedef struct _lwip_socket_obj_t {
|
|
|
|
mp_obj_base_t base;
|
|
|
|
|
2015-12-29 19:00:52 +00:00
|
|
|
volatile union {
|
2015-11-02 06:46:58 +00:00
|
|
|
struct tcp_pcb *tcp;
|
|
|
|
struct udp_pcb *udp;
|
|
|
|
} pcb;
|
2015-12-29 19:00:52 +00:00
|
|
|
volatile union {
|
2015-11-02 06:46:58 +00:00
|
|
|
struct pbuf *pbuf;
|
2018-11-28 05:46:02 +00:00
|
|
|
struct {
|
|
|
|
uint8_t alloc;
|
|
|
|
uint8_t iget;
|
|
|
|
uint8_t iput;
|
|
|
|
union {
|
|
|
|
struct tcp_pcb *item; // if alloc == 0
|
|
|
|
struct tcp_pcb **array; // if alloc != 0
|
|
|
|
} tcp;
|
|
|
|
} connection;
|
2015-11-02 06:46:58 +00:00
|
|
|
} incoming;
|
2016-04-17 16:06:45 +01:00
|
|
|
mp_obj_t callback;
|
2015-10-12 00:43:33 +01:00
|
|
|
byte peer[4];
|
|
|
|
mp_uint_t peer_port;
|
|
|
|
mp_uint_t timeout;
|
extmod/modlwip: Store a chain of incoming pbufs, instead of only one.
Storing a chain of pbuf was an original design of @pfalcon's lwIP socket
module. The problem with storing just one, like modlwip does is that
"peer closed connection" notification is completely asynchronous and out of
band. So, there may be following sequence of actions:
1. pbuf #1 arrives, and stored in a socket.
2. pbuf #2 arrives, and rejected, which causes lwIP to put it into a
queue to re-deliver later.
3. "Peer closed connection" is signaled, and socket is set at such status.
4. pbuf #1 is processed.
5. There's no stored pbufs in teh socket, and socket status is "peer closed
connection", so EOF is returned to a client.
6. pbuf #2 gets redelivered.
Apparently, there's no easy workaround for this, except to queue all
incoming pbufs in a socket. This may lead to increased memory pressure,
as number of pending packets would be regulated only by TCP/IP flow
control, whereas with previous setup lwIP had a global overlook of number
packets waiting for redelivery and could regulate them centrally.
2016-06-19 13:53:59 +01:00
|
|
|
uint16_t recv_offset;
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
uint8_t domain;
|
|
|
|
uint8_t type;
|
|
|
|
|
2015-12-30 14:26:23 +00:00
|
|
|
#define STATE_NEW 0
|
2018-12-03 07:02:10 +00:00
|
|
|
#define STATE_LISTENING 1
|
|
|
|
#define STATE_CONNECTING 2
|
|
|
|
#define STATE_CONNECTED 3
|
|
|
|
#define STATE_PEER_CLOSED 4
|
2015-12-30 14:26:23 +00:00
|
|
|
// Negative value is lwIP error
|
|
|
|
int8_t state;
|
2015-10-12 00:43:33 +01:00
|
|
|
} lwip_socket_obj_t;
|
|
|
|
|
2015-12-29 18:35:41 +00:00
|
|
|
static inline void poll_sockets(void) {
|
2015-12-28 16:58:14 +00:00
|
|
|
#ifdef MICROPY_EVENT_POLL_HOOK
|
|
|
|
MICROPY_EVENT_POLL_HOOK;
|
|
|
|
#else
|
2015-12-29 18:35:41 +00:00
|
|
|
mp_hal_delay_ms(1);
|
2015-12-28 16:58:14 +00:00
|
|
|
#endif
|
2015-12-29 18:35:41 +00:00
|
|
|
}
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
/*******************************************************************************/
|
|
|
|
// Callback functions for the lwIP raw API.
|
|
|
|
|
2016-04-17 16:06:45 +01:00
|
|
|
static inline void exec_user_callback(lwip_socket_obj_t *socket) {
|
|
|
|
if (socket->callback != MP_OBJ_NULL) {
|
2018-07-08 14:15:44 +01:00
|
|
|
mp_call_function_1_protected(socket->callback, MP_OBJ_FROM_PTR(socket));
|
2016-04-17 16:06:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
// Callback for incoming UDP packets. We simply stash the packet and the source address,
|
|
|
|
// in case we need it for recvfrom.
|
2018-05-17 03:58:34 +01:00
|
|
|
#if LWIP_VERSION_MAJOR < 2
|
|
|
|
STATIC void _lwip_udp_incoming(void *arg, struct udp_pcb *upcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
|
|
|
|
#else
|
|
|
|
STATIC void _lwip_udp_incoming(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
|
|
|
|
#endif
|
|
|
|
{
|
2015-10-26 22:23:07 +00:00
|
|
|
lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
|
2015-10-12 00:43:33 +01:00
|
|
|
|
2015-11-02 06:46:58 +00:00
|
|
|
if (socket->incoming.pbuf != NULL) {
|
2015-10-12 00:43:33 +01:00
|
|
|
// That's why they call it "unreliable". No room in the inn, drop the packet.
|
|
|
|
pbuf_free(p);
|
|
|
|
} else {
|
2015-11-02 06:46:58 +00:00
|
|
|
socket->incoming.pbuf = p;
|
2015-10-12 00:43:33 +01:00
|
|
|
socket->peer_port = (mp_uint_t)port;
|
2015-12-30 14:40:23 +00:00
|
|
|
memcpy(&socket->peer, addr, sizeof(socket->peer));
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Callback for general tcp errors.
|
|
|
|
STATIC void _lwip_tcp_error(void *arg, err_t err) {
|
2015-10-26 22:23:07 +00:00
|
|
|
lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
// Pass the error code back via the connection variable.
|
2015-12-30 14:26:23 +00:00
|
|
|
socket->state = err;
|
2015-10-12 00:43:33 +01:00
|
|
|
// If we got here, the lwIP stack either has deallocated or will deallocate the pcb.
|
2015-11-02 06:46:58 +00:00
|
|
|
socket->pcb.tcp = NULL;
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Callback for tcp connection requests. Error code err is unused. (See tcp.h)
|
|
|
|
STATIC err_t _lwip_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err) {
|
2015-10-26 22:23:07 +00:00
|
|
|
lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
|
2015-10-12 00:43:33 +01:00
|
|
|
|
2015-12-30 14:26:23 +00:00
|
|
|
socket->state = STATE_CONNECTED;
|
2015-10-12 00:43:33 +01:00
|
|
|
return ERR_OK;
|
|
|
|
}
|
|
|
|
|
2016-04-10 23:21:34 +01:00
|
|
|
// By default, a child socket of listen socket is created with recv
|
|
|
|
// handler which discards incoming pbuf's. We don't want to do that,
|
|
|
|
// so set this handler which requests lwIP to keep pbuf's and deliver
|
|
|
|
// them later. We cannot cache pbufs in child socket on Python side,
|
|
|
|
// until it is created in accept().
|
|
|
|
STATIC err_t _lwip_tcp_recv_unaccepted(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
|
|
|
|
return ERR_BUF;
|
|
|
|
}
|
|
|
|
|
2016-05-15 20:14:12 +01:00
|
|
|
// "Poll" (idle) callback to be called ASAP after accept callback
|
|
|
|
// to execute Python callback function, as it can't be executed
|
|
|
|
// from accept callback itself.
|
|
|
|
STATIC err_t _lwip_tcp_accept_finished(void *arg, struct tcp_pcb *pcb)
|
|
|
|
{
|
|
|
|
lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
|
|
|
|
tcp_poll(pcb, NULL, 0);
|
|
|
|
exec_user_callback(socket);
|
|
|
|
return ERR_OK;
|
|
|
|
}
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
// Callback for incoming tcp connections.
|
|
|
|
STATIC err_t _lwip_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
|
2015-10-26 22:23:07 +00:00
|
|
|
lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
|
2016-04-10 23:21:34 +01:00
|
|
|
tcp_recv(newpcb, _lwip_tcp_recv_unaccepted);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
2018-11-28 05:46:02 +00:00
|
|
|
// Search for an empty slot to store the new connection
|
|
|
|
struct tcp_pcb *volatile *tcp_array;
|
|
|
|
if (socket->incoming.connection.alloc == 0) {
|
|
|
|
tcp_array = &socket->incoming.connection.tcp.item;
|
2015-10-12 00:43:33 +01:00
|
|
|
} else {
|
2018-11-28 05:46:02 +00:00
|
|
|
tcp_array = socket->incoming.connection.tcp.array;
|
|
|
|
}
|
|
|
|
if (tcp_array[socket->incoming.connection.iput] == NULL) {
|
|
|
|
// Have an empty slot to store waiting connection
|
|
|
|
tcp_array[socket->incoming.connection.iput] = newpcb;
|
|
|
|
if (++socket->incoming.connection.iput >= socket->incoming.connection.alloc) {
|
|
|
|
socket->incoming.connection.iput = 0;
|
|
|
|
}
|
2016-05-15 20:14:12 +01:00
|
|
|
if (socket->callback != MP_OBJ_NULL) {
|
|
|
|
// Schedule accept callback to be called when lwIP is done
|
|
|
|
// with processing this incoming connection on its side and
|
|
|
|
// is idle.
|
|
|
|
tcp_poll(newpcb, _lwip_tcp_accept_finished, 1);
|
|
|
|
}
|
2015-10-12 00:43:33 +01:00
|
|
|
return ERR_OK;
|
|
|
|
}
|
2018-11-28 05:46:02 +00:00
|
|
|
|
|
|
|
DEBUG_printf("_lwip_tcp_accept: No room to queue pcb waiting for accept\n");
|
|
|
|
return ERR_BUF;
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Callback for inbound tcp packets.
|
|
|
|
STATIC err_t _lwip_tcp_recv(void *arg, struct tcp_pcb *tcpb, struct pbuf *p, err_t err) {
|
2015-10-26 22:23:07 +00:00
|
|
|
lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
if (p == NULL) {
|
|
|
|
// Other side has closed connection.
|
2016-04-13 23:15:52 +01:00
|
|
|
DEBUG_printf("_lwip_tcp_recv[%p]: other side closed connection\n", socket);
|
2015-12-30 14:26:23 +00:00
|
|
|
socket->state = STATE_PEER_CLOSED;
|
2016-04-17 16:06:45 +01:00
|
|
|
exec_user_callback(socket);
|
2015-10-12 00:43:33 +01:00
|
|
|
return ERR_OK;
|
extmod/modlwip: Store a chain of incoming pbufs, instead of only one.
Storing a chain of pbuf was an original design of @pfalcon's lwIP socket
module. The problem with storing just one, like modlwip does is that
"peer closed connection" notification is completely asynchronous and out of
band. So, there may be following sequence of actions:
1. pbuf #1 arrives, and stored in a socket.
2. pbuf #2 arrives, and rejected, which causes lwIP to put it into a
queue to re-deliver later.
3. "Peer closed connection" is signaled, and socket is set at such status.
4. pbuf #1 is processed.
5. There's no stored pbufs in teh socket, and socket status is "peer closed
connection", so EOF is returned to a client.
6. pbuf #2 gets redelivered.
Apparently, there's no easy workaround for this, except to queue all
incoming pbufs in a socket. This may lead to increased memory pressure,
as number of pending packets would be regulated only by TCP/IP flow
control, whereas with previous setup lwIP had a global overlook of number
packets waiting for redelivery and could regulate them centrally.
2016-06-19 13:53:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (socket->incoming.pbuf == NULL) {
|
|
|
|
socket->incoming.pbuf = p;
|
|
|
|
} else {
|
|
|
|
#ifdef SOCKET_SINGLE_PBUF
|
2015-10-12 00:43:33 +01:00
|
|
|
return ERR_BUF;
|
extmod/modlwip: Store a chain of incoming pbufs, instead of only one.
Storing a chain of pbuf was an original design of @pfalcon's lwIP socket
module. The problem with storing just one, like modlwip does is that
"peer closed connection" notification is completely asynchronous and out of
band. So, there may be following sequence of actions:
1. pbuf #1 arrives, and stored in a socket.
2. pbuf #2 arrives, and rejected, which causes lwIP to put it into a
queue to re-deliver later.
3. "Peer closed connection" is signaled, and socket is set at such status.
4. pbuf #1 is processed.
5. There's no stored pbufs in teh socket, and socket status is "peer closed
connection", so EOF is returned to a client.
6. pbuf #2 gets redelivered.
Apparently, there's no easy workaround for this, except to queue all
incoming pbufs in a socket. This may lead to increased memory pressure,
as number of pending packets would be regulated only by TCP/IP flow
control, whereas with previous setup lwIP had a global overlook of number
packets waiting for redelivery and could regulate them centrally.
2016-06-19 13:53:59 +01:00
|
|
|
#else
|
|
|
|
pbuf_cat(socket->incoming.pbuf, p);
|
|
|
|
#endif
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
2016-04-17 16:06:45 +01:00
|
|
|
|
|
|
|
exec_user_callback(socket);
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
return ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************/
|
2017-05-29 08:08:14 +01:00
|
|
|
// Functions for socket send/receive operations. Socket send/recv and friends call
|
2015-10-12 00:43:33 +01:00
|
|
|
// these to do the work.
|
|
|
|
|
|
|
|
// Helper function for send/sendto to handle UDP packets.
|
|
|
|
STATIC mp_uint_t lwip_udp_send(lwip_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) {
|
|
|
|
if (len > 0xffff) {
|
|
|
|
// Any packet that big is probably going to fail the pbuf_alloc anyway, but may as well try
|
|
|
|
len = 0xffff;
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: maybe PBUF_ROM?
|
|
|
|
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
|
|
|
|
if (p == NULL) {
|
2016-05-12 12:49:14 +01:00
|
|
|
*_errno = MP_ENOMEM;
|
2015-10-12 00:43:33 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(p->payload, buf, len);
|
|
|
|
|
|
|
|
err_t err;
|
|
|
|
if (ip == NULL) {
|
2015-11-02 06:46:58 +00:00
|
|
|
err = udp_send(socket->pcb.udp, p);
|
2015-10-12 00:43:33 +01:00
|
|
|
} else {
|
|
|
|
ip_addr_t dest;
|
|
|
|
IP4_ADDR(&dest, ip[0], ip[1], ip[2], ip[3]);
|
2015-11-02 06:46:58 +00:00
|
|
|
err = udp_sendto(socket->pcb.udp, p, &dest, port);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pbuf_free(p);
|
|
|
|
|
2016-04-26 13:19:08 +01:00
|
|
|
// udp_sendto can return 1 on occasion for ESP8266 port. It's not known why
|
|
|
|
// but it seems that the send actually goes through without error in this case.
|
|
|
|
// So we treat such cases as a success until further investigation.
|
|
|
|
if (err != ERR_OK && err != 1) {
|
2015-10-12 00:43:33 +01:00
|
|
|
*_errno = error_lookup_table[-err];
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper function for recv/recvfrom to handle UDP packets
|
|
|
|
STATIC mp_uint_t lwip_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) {
|
|
|
|
|
2015-11-02 06:46:58 +00:00
|
|
|
if (socket->incoming.pbuf == NULL) {
|
2015-10-12 00:43:33 +01:00
|
|
|
if (socket->timeout != -1) {
|
|
|
|
for (mp_uint_t retries = socket->timeout / 100; retries--;) {
|
2015-10-27 20:31:42 +00:00
|
|
|
mp_hal_delay_ms(100);
|
2015-11-02 06:46:58 +00:00
|
|
|
if (socket->incoming.pbuf != NULL) break;
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
2015-11-02 06:46:58 +00:00
|
|
|
if (socket->incoming.pbuf == NULL) {
|
2016-05-12 12:49:14 +01:00
|
|
|
*_errno = MP_ETIMEDOUT;
|
2015-10-12 00:43:33 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
2015-11-02 06:46:58 +00:00
|
|
|
while (socket->incoming.pbuf == NULL) {
|
2015-12-28 16:58:14 +00:00
|
|
|
poll_sockets();
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ip != NULL) {
|
2015-12-30 14:40:23 +00:00
|
|
|
memcpy(ip, &socket->peer, sizeof(socket->peer));
|
2015-10-12 00:43:33 +01:00
|
|
|
*port = socket->peer_port;
|
|
|
|
}
|
|
|
|
|
2015-11-02 06:46:58 +00:00
|
|
|
struct pbuf *p = socket->incoming.pbuf;
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
u16_t result = pbuf_copy_partial(p, buf, ((p->tot_len > len) ? len : p->tot_len), 0);
|
|
|
|
pbuf_free(p);
|
2015-11-02 06:46:58 +00:00
|
|
|
socket->incoming.pbuf = NULL;
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
return (mp_uint_t) result;
|
|
|
|
}
|
|
|
|
|
2016-04-17 00:20:05 +01:00
|
|
|
// For use in stream virtual methods
|
|
|
|
#define STREAM_ERROR_CHECK(socket) \
|
|
|
|
if (socket->state < 0) { \
|
|
|
|
*_errno = error_lookup_table[-socket->state]; \
|
|
|
|
return MP_STREAM_ERROR; \
|
|
|
|
} \
|
|
|
|
assert(socket->pcb.tcp);
|
|
|
|
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
// Helper function for send/sendto to handle TCP packets
|
2015-10-26 22:26:05 +00:00
|
|
|
STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) {
|
2016-04-17 00:20:05 +01:00
|
|
|
// Check for any pending errors
|
|
|
|
STREAM_ERROR_CHECK(socket);
|
|
|
|
|
2015-11-02 06:46:58 +00:00
|
|
|
u16_t available = tcp_sndbuf(socket->pcb.tcp);
|
2016-03-25 16:38:13 +00:00
|
|
|
|
|
|
|
if (available == 0) {
|
|
|
|
// Non-blocking socket
|
|
|
|
if (socket->timeout == 0) {
|
2016-05-12 12:49:14 +01:00
|
|
|
*_errno = MP_EAGAIN;
|
2016-04-17 00:20:05 +01:00
|
|
|
return MP_STREAM_ERROR;
|
2016-03-25 16:38:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mp_uint_t start = mp_hal_ticks_ms();
|
|
|
|
// Assume that STATE_PEER_CLOSED may mean half-closed connection, where peer closed it
|
|
|
|
// sending direction, but not receiving. Consequently, check for both STATE_CONNECTED
|
|
|
|
// and STATE_PEER_CLOSED as normal conditions and still waiting for buffers to be sent.
|
|
|
|
// If peer fully closed socket, we would have socket->state set to ERR_RST (connection
|
|
|
|
// reset) by error callback.
|
|
|
|
// Avoid sending too small packets, so wait until at least 16 bytes available
|
|
|
|
while (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16) {
|
|
|
|
if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) {
|
2016-05-12 12:49:14 +01:00
|
|
|
*_errno = MP_ETIMEDOUT;
|
2016-04-17 00:20:05 +01:00
|
|
|
return MP_STREAM_ERROR;
|
2016-03-25 16:38:13 +00:00
|
|
|
}
|
|
|
|
poll_sockets();
|
|
|
|
}
|
|
|
|
|
2016-04-17 00:20:05 +01:00
|
|
|
// While we waited, something could happen
|
|
|
|
STREAM_ERROR_CHECK(socket);
|
2016-03-25 16:38:13 +00:00
|
|
|
}
|
|
|
|
|
2015-10-26 22:26:05 +00:00
|
|
|
u16_t write_len = MIN(available, len);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
2015-11-02 06:46:58 +00:00
|
|
|
err_t err = tcp_write(socket->pcb.tcp, buf, write_len, TCP_WRITE_FLAG_COPY);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
2017-10-31 02:50:08 +00:00
|
|
|
// If the output buffer is getting full then send the data to the lower layers
|
|
|
|
if (err == ERR_OK && tcp_sndbuf(socket->pcb.tcp) < TCP_SND_BUF / 4) {
|
|
|
|
err = tcp_output(socket->pcb.tcp);
|
|
|
|
}
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
if (err != ERR_OK) {
|
|
|
|
*_errno = error_lookup_table[-err];
|
2016-04-17 00:20:05 +01:00
|
|
|
return MP_STREAM_ERROR;
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
2015-10-26 22:26:05 +00:00
|
|
|
return write_len;
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Helper function for recv/recvfrom to handle TCP packets
|
|
|
|
STATIC mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) {
|
2016-04-17 00:22:26 +01:00
|
|
|
// Check for any pending errors
|
|
|
|
STREAM_ERROR_CHECK(socket);
|
|
|
|
|
2015-11-02 06:46:58 +00:00
|
|
|
if (socket->incoming.pbuf == NULL) {
|
2016-01-02 21:56:27 +00:00
|
|
|
|
|
|
|
// Non-blocking socket
|
|
|
|
if (socket->timeout == 0) {
|
2016-04-13 22:58:51 +01:00
|
|
|
if (socket->state == STATE_PEER_CLOSED) {
|
|
|
|
return 0;
|
|
|
|
}
|
2016-05-12 12:49:14 +01:00
|
|
|
*_errno = MP_EAGAIN;
|
2016-01-02 21:56:27 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-12-29 18:35:41 +00:00
|
|
|
mp_uint_t start = mp_hal_ticks_ms();
|
2015-12-30 19:03:58 +00:00
|
|
|
while (socket->state == STATE_CONNECTED && socket->incoming.pbuf == NULL) {
|
2015-12-29 18:35:41 +00:00
|
|
|
if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) {
|
2016-05-12 12:49:14 +01:00
|
|
|
*_errno = MP_ETIMEDOUT;
|
2015-10-12 00:43:33 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2015-12-29 18:35:41 +00:00
|
|
|
poll_sockets();
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
2016-04-17 00:22:26 +01:00
|
|
|
|
2015-12-30 19:03:58 +00:00
|
|
|
if (socket->state == STATE_PEER_CLOSED) {
|
2015-12-30 22:49:50 +00:00
|
|
|
if (socket->incoming.pbuf == NULL) {
|
|
|
|
// socket closed and no data left in buffer
|
|
|
|
return 0;
|
|
|
|
}
|
2015-12-30 19:03:58 +00:00
|
|
|
} else if (socket->state != STATE_CONNECTED) {
|
2016-02-28 00:14:49 +00:00
|
|
|
assert(socket->state < 0);
|
|
|
|
*_errno = error_lookup_table[-socket->state];
|
2015-12-30 19:03:58 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
2016-04-17 00:22:26 +01:00
|
|
|
assert(socket->pcb.tcp != NULL);
|
|
|
|
|
2015-11-02 06:46:58 +00:00
|
|
|
struct pbuf *p = socket->incoming.pbuf;
|
2015-10-12 00:43:33 +01:00
|
|
|
|
extmod/modlwip: Store a chain of incoming pbufs, instead of only one.
Storing a chain of pbuf was an original design of @pfalcon's lwIP socket
module. The problem with storing just one, like modlwip does is that
"peer closed connection" notification is completely asynchronous and out of
band. So, there may be following sequence of actions:
1. pbuf #1 arrives, and stored in a socket.
2. pbuf #2 arrives, and rejected, which causes lwIP to put it into a
queue to re-deliver later.
3. "Peer closed connection" is signaled, and socket is set at such status.
4. pbuf #1 is processed.
5. There's no stored pbufs in teh socket, and socket status is "peer closed
connection", so EOF is returned to a client.
6. pbuf #2 gets redelivered.
Apparently, there's no easy workaround for this, except to queue all
incoming pbufs in a socket. This may lead to increased memory pressure,
as number of pending packets would be regulated only by TCP/IP flow
control, whereas with previous setup lwIP had a global overlook of number
packets waiting for redelivery and could regulate them centrally.
2016-06-19 13:53:59 +01:00
|
|
|
mp_uint_t remaining = p->len - socket->recv_offset;
|
|
|
|
if (len > remaining) {
|
|
|
|
len = remaining;
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
extmod/modlwip: Store a chain of incoming pbufs, instead of only one.
Storing a chain of pbuf was an original design of @pfalcon's lwIP socket
module. The problem with storing just one, like modlwip does is that
"peer closed connection" notification is completely asynchronous and out of
band. So, there may be following sequence of actions:
1. pbuf #1 arrives, and stored in a socket.
2. pbuf #2 arrives, and rejected, which causes lwIP to put it into a
queue to re-deliver later.
3. "Peer closed connection" is signaled, and socket is set at such status.
4. pbuf #1 is processed.
5. There's no stored pbufs in teh socket, and socket status is "peer closed
connection", so EOF is returned to a client.
6. pbuf #2 gets redelivered.
Apparently, there's no easy workaround for this, except to queue all
incoming pbufs in a socket. This may lead to increased memory pressure,
as number of pending packets would be regulated only by TCP/IP flow
control, whereas with previous setup lwIP had a global overlook of number
packets waiting for redelivery and could regulate them centrally.
2016-06-19 13:53:59 +01:00
|
|
|
memcpy(buf, (byte*)p->payload + socket->recv_offset, len);
|
|
|
|
|
|
|
|
remaining -= len;
|
|
|
|
if (remaining == 0) {
|
|
|
|
socket->incoming.pbuf = p->next;
|
|
|
|
// If we don't ref here, free() will free the entire chain,
|
|
|
|
// if we ref, it does what we need: frees 1st buf, and decrements
|
|
|
|
// next buf's refcount back to 1.
|
|
|
|
pbuf_ref(p->next);
|
2015-10-12 00:43:33 +01:00
|
|
|
pbuf_free(p);
|
extmod/modlwip: Store a chain of incoming pbufs, instead of only one.
Storing a chain of pbuf was an original design of @pfalcon's lwIP socket
module. The problem with storing just one, like modlwip does is that
"peer closed connection" notification is completely asynchronous and out of
band. So, there may be following sequence of actions:
1. pbuf #1 arrives, and stored in a socket.
2. pbuf #2 arrives, and rejected, which causes lwIP to put it into a
queue to re-deliver later.
3. "Peer closed connection" is signaled, and socket is set at such status.
4. pbuf #1 is processed.
5. There's no stored pbufs in teh socket, and socket status is "peer closed
connection", so EOF is returned to a client.
6. pbuf #2 gets redelivered.
Apparently, there's no easy workaround for this, except to queue all
incoming pbufs in a socket. This may lead to increased memory pressure,
as number of pending packets would be regulated only by TCP/IP flow
control, whereas with previous setup lwIP had a global overlook of number
packets waiting for redelivery and could regulate them centrally.
2016-06-19 13:53:59 +01:00
|
|
|
socket->recv_offset = 0;
|
|
|
|
} else {
|
|
|
|
socket->recv_offset += len;
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
extmod/modlwip: Store a chain of incoming pbufs, instead of only one.
Storing a chain of pbuf was an original design of @pfalcon's lwIP socket
module. The problem with storing just one, like modlwip does is that
"peer closed connection" notification is completely asynchronous and out of
band. So, there may be following sequence of actions:
1. pbuf #1 arrives, and stored in a socket.
2. pbuf #2 arrives, and rejected, which causes lwIP to put it into a
queue to re-deliver later.
3. "Peer closed connection" is signaled, and socket is set at such status.
4. pbuf #1 is processed.
5. There's no stored pbufs in teh socket, and socket status is "peer closed
connection", so EOF is returned to a client.
6. pbuf #2 gets redelivered.
Apparently, there's no easy workaround for this, except to queue all
incoming pbufs in a socket. This may lead to increased memory pressure,
as number of pending packets would be regulated only by TCP/IP flow
control, whereas with previous setup lwIP had a global overlook of number
packets waiting for redelivery and could regulate them centrally.
2016-06-19 13:53:59 +01:00
|
|
|
tcp_recved(socket->pcb.tcp, len);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
extmod/modlwip: Store a chain of incoming pbufs, instead of only one.
Storing a chain of pbuf was an original design of @pfalcon's lwIP socket
module. The problem with storing just one, like modlwip does is that
"peer closed connection" notification is completely asynchronous and out of
band. So, there may be following sequence of actions:
1. pbuf #1 arrives, and stored in a socket.
2. pbuf #2 arrives, and rejected, which causes lwIP to put it into a
queue to re-deliver later.
3. "Peer closed connection" is signaled, and socket is set at such status.
4. pbuf #1 is processed.
5. There's no stored pbufs in teh socket, and socket status is "peer closed
connection", so EOF is returned to a client.
6. pbuf #2 gets redelivered.
Apparently, there's no easy workaround for this, except to queue all
incoming pbufs in a socket. This may lead to increased memory pressure,
as number of pending packets would be regulated only by TCP/IP flow
control, whereas with previous setup lwIP had a global overlook of number
packets waiting for redelivery and could regulate them centrally.
2016-06-19 13:53:59 +01:00
|
|
|
return len;
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************/
|
|
|
|
// The socket functions provided by lwip.socket.
|
|
|
|
|
|
|
|
STATIC const mp_obj_type_t lwip_socket_type;
|
|
|
|
|
2015-12-28 23:08:30 +00:00
|
|
|
STATIC void lwip_socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
extmod/modlwip: Store a chain of incoming pbufs, instead of only one.
Storing a chain of pbuf was an original design of @pfalcon's lwIP socket
module. The problem with storing just one, like modlwip does is that
"peer closed connection" notification is completely asynchronous and out of
band. So, there may be following sequence of actions:
1. pbuf #1 arrives, and stored in a socket.
2. pbuf #2 arrives, and rejected, which causes lwIP to put it into a
queue to re-deliver later.
3. "Peer closed connection" is signaled, and socket is set at such status.
4. pbuf #1 is processed.
5. There's no stored pbufs in teh socket, and socket status is "peer closed
connection", so EOF is returned to a client.
6. pbuf #2 gets redelivered.
Apparently, there's no easy workaround for this, except to queue all
incoming pbufs in a socket. This may lead to increased memory pressure,
as number of pending packets would be regulated only by TCP/IP flow
control, whereas with previous setup lwIP had a global overlook of number
packets waiting for redelivery and could regulate them centrally.
2016-06-19 13:53:59 +01:00
|
|
|
mp_printf(print, "<socket state=%d timeout=%d incoming=%p off=%d>", self->state, self->timeout,
|
|
|
|
self->incoming.pbuf, self->recv_offset);
|
2015-12-28 23:08:30 +00:00
|
|
|
}
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
// FIXME: Only supports two arguments at present
|
2017-01-04 13:10:42 +00:00
|
|
|
STATIC mp_obj_t lwip_socket_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
2015-10-12 00:43:33 +01:00
|
|
|
mp_arg_check_num(n_args, n_kw, 0, 4, false);
|
|
|
|
|
|
|
|
lwip_socket_obj_t *socket = m_new_obj_with_finaliser(lwip_socket_obj_t);
|
2018-07-08 14:15:44 +01:00
|
|
|
socket->base.type = &lwip_socket_type;
|
2015-10-12 00:43:33 +01:00
|
|
|
socket->domain = MOD_NETWORK_AF_INET;
|
|
|
|
socket->type = MOD_NETWORK_SOCK_STREAM;
|
2016-04-17 16:06:45 +01:00
|
|
|
socket->callback = MP_OBJ_NULL;
|
2015-10-12 00:43:33 +01:00
|
|
|
if (n_args >= 1) {
|
|
|
|
socket->domain = mp_obj_get_int(args[0]);
|
|
|
|
if (n_args >= 2) {
|
|
|
|
socket->type = mp_obj_get_int(args[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (socket->type) {
|
2018-11-28 05:46:02 +00:00
|
|
|
case MOD_NETWORK_SOCK_STREAM:
|
|
|
|
socket->pcb.tcp = tcp_new();
|
|
|
|
socket->incoming.connection.alloc = 0;
|
|
|
|
socket->incoming.connection.tcp.item = NULL;
|
|
|
|
break;
|
|
|
|
case MOD_NETWORK_SOCK_DGRAM:
|
|
|
|
socket->pcb.udp = udp_new();
|
|
|
|
socket->incoming.pbuf = NULL;
|
|
|
|
break;
|
2015-11-02 06:46:58 +00:00
|
|
|
//case MOD_NETWORK_SOCK_RAW: socket->pcb.raw = raw_new(); break;
|
2016-10-07 03:47:57 +01:00
|
|
|
default: mp_raise_OSError(MP_EINVAL);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
2015-11-02 06:46:58 +00:00
|
|
|
if (socket->pcb.tcp == NULL) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(MP_ENOMEM);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (socket->type) {
|
|
|
|
case MOD_NETWORK_SOCK_STREAM: {
|
|
|
|
// Register the socket object as our callback argument.
|
2015-11-02 06:46:58 +00:00
|
|
|
tcp_arg(socket->pcb.tcp, (void*)socket);
|
2015-10-12 00:43:33 +01:00
|
|
|
// Register our error callback.
|
2015-11-02 06:46:58 +00:00
|
|
|
tcp_err(socket->pcb.tcp, _lwip_tcp_error);
|
2015-10-12 00:43:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MOD_NETWORK_SOCK_DGRAM: {
|
|
|
|
// Register our receive callback now. Since UDP sockets don't require binding or connection
|
|
|
|
// before use, there's no other good time to do it.
|
2015-11-02 06:46:58 +00:00
|
|
|
udp_recv(socket->pcb.udp, _lwip_udp_incoming, (void*)socket);
|
2015-10-12 00:43:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
socket->timeout = -1;
|
2015-12-30 14:26:23 +00:00
|
|
|
socket->state = STATE_NEW;
|
extmod/modlwip: Store a chain of incoming pbufs, instead of only one.
Storing a chain of pbuf was an original design of @pfalcon's lwIP socket
module. The problem with storing just one, like modlwip does is that
"peer closed connection" notification is completely asynchronous and out of
band. So, there may be following sequence of actions:
1. pbuf #1 arrives, and stored in a socket.
2. pbuf #2 arrives, and rejected, which causes lwIP to put it into a
queue to re-deliver later.
3. "Peer closed connection" is signaled, and socket is set at such status.
4. pbuf #1 is processed.
5. There's no stored pbufs in teh socket, and socket status is "peer closed
connection", so EOF is returned to a client.
6. pbuf #2 gets redelivered.
Apparently, there's no easy workaround for this, except to queue all
incoming pbufs in a socket. This may lead to increased memory pressure,
as number of pending packets would be regulated only by TCP/IP flow
control, whereas with previous setup lwIP had a global overlook of number
packets waiting for redelivery and could regulate them centrally.
2016-06-19 13:53:59 +01:00
|
|
|
socket->recv_offset = 0;
|
2018-07-08 14:15:44 +01:00
|
|
|
return MP_OBJ_FROM_PTR(socket);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
STATIC mp_obj_t lwip_socket_bind(mp_obj_t self_in, mp_obj_t addr_in) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
|
|
|
|
mp_uint_t port = netutils_parse_inet_addr(addr_in, ip, NETUTILS_BIG);
|
|
|
|
|
|
|
|
ip_addr_t bind_addr;
|
|
|
|
IP4_ADDR(&bind_addr, ip[0], ip[1], ip[2], ip[3]);
|
|
|
|
|
|
|
|
err_t err = ERR_ARG;
|
|
|
|
switch (socket->type) {
|
|
|
|
case MOD_NETWORK_SOCK_STREAM: {
|
2015-11-02 06:46:58 +00:00
|
|
|
err = tcp_bind(socket->pcb.tcp, &bind_addr, port);
|
2015-10-12 00:43:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MOD_NETWORK_SOCK_DGRAM: {
|
2015-11-02 06:46:58 +00:00
|
|
|
err = udp_bind(socket->pcb.udp, &bind_addr, port);
|
2015-10-12 00:43:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err != ERR_OK) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(error_lookup_table[-err]);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return mp_const_none;
|
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_bind_obj, lwip_socket_bind);
|
|
|
|
|
|
|
|
STATIC mp_obj_t lwip_socket_listen(mp_obj_t self_in, mp_obj_t backlog_in) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2015-10-12 00:43:33 +01:00
|
|
|
mp_int_t backlog = mp_obj_get_int(backlog_in);
|
|
|
|
|
2015-11-02 06:46:58 +00:00
|
|
|
if (socket->pcb.tcp == NULL) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(MP_EBADF);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
if (socket->type != MOD_NETWORK_SOCK_STREAM) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(MP_EOPNOTSUPP);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
2015-11-02 06:46:58 +00:00
|
|
|
struct tcp_pcb *new_pcb = tcp_listen_with_backlog(socket->pcb.tcp, (u8_t)backlog);
|
2015-10-12 00:43:33 +01:00
|
|
|
if (new_pcb == NULL) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(MP_ENOMEM);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
2015-11-02 06:46:58 +00:00
|
|
|
socket->pcb.tcp = new_pcb;
|
2018-11-28 05:46:02 +00:00
|
|
|
|
|
|
|
// Allocate memory for the backlog of connections
|
|
|
|
if (backlog <= 1) {
|
|
|
|
socket->incoming.connection.alloc = 0;
|
|
|
|
socket->incoming.connection.tcp.item = NULL;
|
|
|
|
} else {
|
|
|
|
socket->incoming.connection.alloc = backlog;
|
|
|
|
socket->incoming.connection.tcp.array = m_new0(struct tcp_pcb*, backlog);
|
|
|
|
}
|
|
|
|
socket->incoming.connection.iget = 0;
|
|
|
|
socket->incoming.connection.iput = 0;
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
tcp_accept(new_pcb, _lwip_tcp_accept);
|
|
|
|
|
2018-05-17 14:17:36 +01:00
|
|
|
// Socket is no longer considered "new" for purposes of polling
|
2018-12-03 07:02:10 +00:00
|
|
|
socket->state = STATE_LISTENING;
|
2018-05-17 14:17:36 +01:00
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
return mp_const_none;
|
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_listen_obj, lwip_socket_listen);
|
|
|
|
|
|
|
|
STATIC mp_obj_t lwip_socket_accept(mp_obj_t self_in) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
2015-11-02 06:46:58 +00:00
|
|
|
if (socket->pcb.tcp == NULL) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(MP_EBADF);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
if (socket->type != MOD_NETWORK_SOCK_STREAM) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(MP_EOPNOTSUPP);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
// I need to do this because "tcp_accepted", later, is a macro.
|
2015-11-02 06:46:58 +00:00
|
|
|
struct tcp_pcb *listener = socket->pcb.tcp;
|
2015-10-12 00:43:33 +01:00
|
|
|
if (listener->state != LISTEN) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(MP_EINVAL);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// accept incoming connection
|
2018-11-28 05:46:02 +00:00
|
|
|
struct tcp_pcb *volatile *incoming_connection;
|
|
|
|
if (socket->incoming.connection.alloc == 0) {
|
|
|
|
incoming_connection = &socket->incoming.connection.tcp.item;
|
|
|
|
} else {
|
|
|
|
incoming_connection = &socket->incoming.connection.tcp.array[socket->incoming.connection.iget];
|
|
|
|
}
|
|
|
|
if (*incoming_connection == NULL) {
|
2017-06-04 11:45:37 +01:00
|
|
|
if (socket->timeout == 0) {
|
|
|
|
mp_raise_OSError(MP_EAGAIN);
|
|
|
|
} else if (socket->timeout != -1) {
|
2015-10-12 00:43:33 +01:00
|
|
|
for (mp_uint_t retries = socket->timeout / 100; retries--;) {
|
2015-10-27 20:31:42 +00:00
|
|
|
mp_hal_delay_ms(100);
|
2018-11-28 05:46:02 +00:00
|
|
|
if (*incoming_connection != NULL) break;
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
2018-11-28 05:46:02 +00:00
|
|
|
if (*incoming_connection == NULL) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(MP_ETIMEDOUT);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
} else {
|
2018-11-28 05:46:02 +00:00
|
|
|
while (*incoming_connection == NULL) {
|
2015-12-28 16:58:14 +00:00
|
|
|
poll_sockets();
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// create new socket object
|
|
|
|
lwip_socket_obj_t *socket2 = m_new_obj_with_finaliser(lwip_socket_obj_t);
|
2018-07-08 14:15:44 +01:00
|
|
|
socket2->base.type = &lwip_socket_type;
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
// We get a new pcb handle...
|
2018-11-28 05:46:02 +00:00
|
|
|
socket2->pcb.tcp = *incoming_connection;
|
|
|
|
if (++socket->incoming.connection.iget >= socket->incoming.connection.alloc) {
|
|
|
|
socket->incoming.connection.iget = 0;
|
|
|
|
}
|
|
|
|
*incoming_connection = NULL;
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
// ...and set up the new socket for it.
|
|
|
|
socket2->domain = MOD_NETWORK_AF_INET;
|
|
|
|
socket2->type = MOD_NETWORK_SOCK_STREAM;
|
2015-11-02 06:46:58 +00:00
|
|
|
socket2->incoming.pbuf = NULL;
|
2015-10-12 00:43:33 +01:00
|
|
|
socket2->timeout = socket->timeout;
|
2015-12-30 14:26:23 +00:00
|
|
|
socket2->state = STATE_CONNECTED;
|
extmod/modlwip: Store a chain of incoming pbufs, instead of only one.
Storing a chain of pbuf was an original design of @pfalcon's lwIP socket
module. The problem with storing just one, like modlwip does is that
"peer closed connection" notification is completely asynchronous and out of
band. So, there may be following sequence of actions:
1. pbuf #1 arrives, and stored in a socket.
2. pbuf #2 arrives, and rejected, which causes lwIP to put it into a
queue to re-deliver later.
3. "Peer closed connection" is signaled, and socket is set at such status.
4. pbuf #1 is processed.
5. There's no stored pbufs in teh socket, and socket status is "peer closed
connection", so EOF is returned to a client.
6. pbuf #2 gets redelivered.
Apparently, there's no easy workaround for this, except to queue all
incoming pbufs in a socket. This may lead to increased memory pressure,
as number of pending packets would be regulated only by TCP/IP flow
control, whereas with previous setup lwIP had a global overlook of number
packets waiting for redelivery and could regulate them centrally.
2016-06-19 13:53:59 +01:00
|
|
|
socket2->recv_offset = 0;
|
2016-04-17 16:06:45 +01:00
|
|
|
socket2->callback = MP_OBJ_NULL;
|
2015-11-02 06:46:58 +00:00
|
|
|
tcp_arg(socket2->pcb.tcp, (void*)socket2);
|
|
|
|
tcp_err(socket2->pcb.tcp, _lwip_tcp_error);
|
|
|
|
tcp_recv(socket2->pcb.tcp, _lwip_tcp_recv);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
tcp_accepted(listener);
|
|
|
|
|
|
|
|
// make the return value
|
|
|
|
uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
|
2015-12-30 14:40:23 +00:00
|
|
|
memcpy(ip, &(socket2->pcb.tcp->remote_ip), sizeof(ip));
|
2015-11-02 06:46:58 +00:00
|
|
|
mp_uint_t port = (mp_uint_t)socket2->pcb.tcp->remote_port;
|
2018-07-08 14:15:44 +01:00
|
|
|
mp_obj_tuple_t *client = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
|
|
|
|
client->items[0] = MP_OBJ_FROM_PTR(socket2);
|
2015-10-12 00:43:33 +01:00
|
|
|
client->items[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG);
|
|
|
|
|
2018-07-08 14:15:44 +01:00
|
|
|
return MP_OBJ_FROM_PTR(client);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(lwip_socket_accept_obj, lwip_socket_accept);
|
|
|
|
|
|
|
|
STATIC mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
2015-11-02 06:46:58 +00:00
|
|
|
if (socket->pcb.tcp == NULL) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(MP_EBADF);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// get address
|
|
|
|
uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
|
|
|
|
mp_uint_t port = netutils_parse_inet_addr(addr_in, ip, NETUTILS_BIG);
|
|
|
|
|
|
|
|
ip_addr_t dest;
|
|
|
|
IP4_ADDR(&dest, ip[0], ip[1], ip[2], ip[3]);
|
|
|
|
|
|
|
|
err_t err = ERR_ARG;
|
|
|
|
switch (socket->type) {
|
|
|
|
case MOD_NETWORK_SOCK_STREAM: {
|
2015-12-30 14:26:23 +00:00
|
|
|
if (socket->state != STATE_NEW) {
|
|
|
|
if (socket->state == STATE_CONNECTED) {
|
2017-06-04 10:30:41 +01:00
|
|
|
mp_raise_OSError(MP_EISCONN);
|
2015-10-12 00:43:33 +01:00
|
|
|
} else {
|
2017-06-04 10:30:41 +01:00
|
|
|
mp_raise_OSError(MP_EALREADY);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
}
|
2017-05-29 08:08:14 +01:00
|
|
|
// Register our receive callback.
|
2015-11-02 06:46:58 +00:00
|
|
|
tcp_recv(socket->pcb.tcp, _lwip_tcp_recv);
|
2015-12-30 14:26:23 +00:00
|
|
|
socket->state = STATE_CONNECTING;
|
2015-11-02 06:46:58 +00:00
|
|
|
err = tcp_connect(socket->pcb.tcp, &dest, port, _lwip_tcp_connected);
|
2015-10-12 00:43:33 +01:00
|
|
|
if (err != ERR_OK) {
|
2015-12-30 14:26:23 +00:00
|
|
|
socket->state = STATE_NEW;
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(error_lookup_table[-err]);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
socket->peer_port = (mp_uint_t)port;
|
2015-12-30 14:40:23 +00:00
|
|
|
memcpy(socket->peer, &dest, sizeof(socket->peer));
|
2015-10-12 00:43:33 +01:00
|
|
|
// And now we wait...
|
|
|
|
if (socket->timeout != -1) {
|
|
|
|
for (mp_uint_t retries = socket->timeout / 100; retries--;) {
|
2015-10-27 20:31:42 +00:00
|
|
|
mp_hal_delay_ms(100);
|
2015-12-30 14:26:23 +00:00
|
|
|
if (socket->state != STATE_CONNECTING) break;
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
2015-12-30 14:26:23 +00:00
|
|
|
if (socket->state == STATE_CONNECTING) {
|
2017-06-03 20:32:07 +01:00
|
|
|
mp_raise_OSError(MP_EINPROGRESS);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
} else {
|
2015-12-30 14:26:23 +00:00
|
|
|
while (socket->state == STATE_CONNECTING) {
|
2015-12-28 16:58:14 +00:00
|
|
|
poll_sockets();
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
}
|
2015-12-30 14:26:23 +00:00
|
|
|
if (socket->state == STATE_CONNECTED) {
|
2015-10-12 00:43:33 +01:00
|
|
|
err = ERR_OK;
|
|
|
|
} else {
|
2015-12-30 14:26:23 +00:00
|
|
|
err = socket->state;
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MOD_NETWORK_SOCK_DGRAM: {
|
2015-11-02 06:46:58 +00:00
|
|
|
err = udp_connect(socket->pcb.udp, &dest, port);
|
2015-10-12 00:43:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err != ERR_OK) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(error_lookup_table[-err]);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return mp_const_none;
|
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_connect_obj, lwip_socket_connect);
|
|
|
|
|
2016-02-27 23:13:14 +00:00
|
|
|
STATIC void lwip_socket_check_connected(lwip_socket_obj_t *socket) {
|
2015-11-02 06:46:58 +00:00
|
|
|
if (socket->pcb.tcp == NULL) {
|
2015-10-12 00:43:33 +01:00
|
|
|
// not connected
|
2016-02-27 23:13:14 +00:00
|
|
|
int _errno = error_lookup_table[-socket->state];
|
2015-12-30 14:43:35 +00:00
|
|
|
socket->state = _ERR_BADF;
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(_errno);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
2016-02-27 23:13:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
STATIC mp_obj_t lwip_socket_send(mp_obj_t self_in, mp_obj_t buf_in) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2016-02-27 23:13:14 +00:00
|
|
|
int _errno;
|
|
|
|
|
|
|
|
lwip_socket_check_connected(socket);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
mp_buffer_info_t bufinfo;
|
|
|
|
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
|
|
|
|
|
|
|
|
mp_uint_t ret = 0;
|
|
|
|
switch (socket->type) {
|
|
|
|
case MOD_NETWORK_SOCK_STREAM: {
|
|
|
|
ret = lwip_tcp_send(socket, bufinfo.buf, bufinfo.len, &_errno);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MOD_NETWORK_SOCK_DGRAM: {
|
|
|
|
ret = lwip_udp_send(socket, bufinfo.buf, bufinfo.len, NULL, 0, &_errno);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ret == -1) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(_errno);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return mp_obj_new_int_from_uint(ret);
|
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_send_obj, lwip_socket_send);
|
|
|
|
|
|
|
|
STATIC mp_obj_t lwip_socket_recv(mp_obj_t self_in, mp_obj_t len_in) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2015-10-12 00:43:33 +01:00
|
|
|
int _errno;
|
|
|
|
|
2016-02-27 23:13:14 +00:00
|
|
|
lwip_socket_check_connected(socket);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
mp_int_t len = mp_obj_get_int(len_in);
|
|
|
|
vstr_t vstr;
|
|
|
|
vstr_init_len(&vstr, len);
|
|
|
|
|
|
|
|
mp_uint_t ret = 0;
|
|
|
|
switch (socket->type) {
|
|
|
|
case MOD_NETWORK_SOCK_STREAM: {
|
|
|
|
ret = lwip_tcp_receive(socket, (byte*)vstr.buf, len, &_errno);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MOD_NETWORK_SOCK_DGRAM: {
|
|
|
|
ret = lwip_udp_receive(socket, (byte*)vstr.buf, len, NULL, NULL, &_errno);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ret == -1) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(_errno);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
return mp_const_empty_bytes;
|
|
|
|
}
|
|
|
|
vstr.len = ret;
|
|
|
|
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
|
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_recv_obj, lwip_socket_recv);
|
|
|
|
|
|
|
|
STATIC mp_obj_t lwip_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2015-10-12 00:43:33 +01:00
|
|
|
int _errno;
|
|
|
|
|
2016-02-27 23:13:14 +00:00
|
|
|
lwip_socket_check_connected(socket);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
mp_buffer_info_t bufinfo;
|
|
|
|
mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ);
|
|
|
|
|
|
|
|
uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
|
|
|
|
mp_uint_t port = netutils_parse_inet_addr(addr_in, ip, NETUTILS_BIG);
|
|
|
|
|
|
|
|
mp_uint_t ret = 0;
|
|
|
|
switch (socket->type) {
|
|
|
|
case MOD_NETWORK_SOCK_STREAM: {
|
|
|
|
ret = lwip_tcp_send(socket, bufinfo.buf, bufinfo.len, &_errno);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MOD_NETWORK_SOCK_DGRAM: {
|
|
|
|
ret = lwip_udp_send(socket, bufinfo.buf, bufinfo.len, ip, port, &_errno);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ret == -1) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(_errno);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return mp_obj_new_int_from_uint(ret);
|
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_3(lwip_socket_sendto_obj, lwip_socket_sendto);
|
|
|
|
|
|
|
|
STATIC mp_obj_t lwip_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2015-10-12 00:43:33 +01:00
|
|
|
int _errno;
|
|
|
|
|
2016-02-27 23:13:14 +00:00
|
|
|
lwip_socket_check_connected(socket);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
|
|
|
mp_int_t len = mp_obj_get_int(len_in);
|
|
|
|
vstr_t vstr;
|
|
|
|
vstr_init_len(&vstr, len);
|
|
|
|
byte ip[4];
|
|
|
|
mp_uint_t port;
|
|
|
|
|
|
|
|
mp_uint_t ret = 0;
|
|
|
|
switch (socket->type) {
|
|
|
|
case MOD_NETWORK_SOCK_STREAM: {
|
2015-12-30 14:40:23 +00:00
|
|
|
memcpy(ip, &socket->peer, sizeof(socket->peer));
|
2015-10-12 00:43:33 +01:00
|
|
|
port = (mp_uint_t) socket->peer_port;
|
|
|
|
ret = lwip_tcp_receive(socket, (byte*)vstr.buf, len, &_errno);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MOD_NETWORK_SOCK_DGRAM: {
|
|
|
|
ret = lwip_udp_receive(socket, (byte*)vstr.buf, len, ip, &port, &_errno);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ret == -1) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(_errno);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
mp_obj_t tuple[2];
|
|
|
|
if (ret == 0) {
|
|
|
|
tuple[0] = mp_const_empty_bytes;
|
|
|
|
} else {
|
|
|
|
vstr.len = ret;
|
|
|
|
tuple[0] = mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
|
|
|
|
}
|
|
|
|
tuple[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG);
|
|
|
|
return mp_obj_new_tuple(2, tuple);
|
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_recvfrom_obj, lwip_socket_recvfrom);
|
|
|
|
|
2016-05-03 08:42:47 +01:00
|
|
|
STATIC mp_obj_t lwip_socket_sendall(mp_obj_t self_in, mp_obj_t buf_in) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2016-05-03 08:42:47 +01:00
|
|
|
lwip_socket_check_connected(socket);
|
|
|
|
|
|
|
|
int _errno;
|
|
|
|
mp_buffer_info_t bufinfo;
|
|
|
|
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
|
|
|
|
|
|
|
|
mp_uint_t ret = 0;
|
|
|
|
switch (socket->type) {
|
|
|
|
case MOD_NETWORK_SOCK_STREAM: {
|
|
|
|
if (socket->timeout == 0) {
|
|
|
|
// Behavior of sendall() for non-blocking sockets isn't explicitly specified.
|
|
|
|
// But it's specified that "On error, an exception is raised, there is no
|
|
|
|
// way to determine how much data, if any, was successfully sent." Then, the
|
|
|
|
// most useful behavior is: check whether we will be able to send all of input
|
|
|
|
// data without EAGAIN, and if won't be, raise it without sending any.
|
|
|
|
if (bufinfo.len > tcp_sndbuf(socket->pcb.tcp)) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(MP_EAGAIN);
|
2016-05-03 08:42:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO: In CPython3.5, socket timeout should apply to the
|
|
|
|
// entire sendall() operation, not to individual send() chunks.
|
|
|
|
while (bufinfo.len != 0) {
|
|
|
|
ret = lwip_tcp_send(socket, bufinfo.buf, bufinfo.len, &_errno);
|
|
|
|
if (ret == -1) {
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(_errno);
|
2016-05-03 08:42:47 +01:00
|
|
|
}
|
|
|
|
bufinfo.len -= ret;
|
|
|
|
bufinfo.buf = (char*)bufinfo.buf + ret;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MOD_NETWORK_SOCK_DGRAM:
|
2017-10-24 12:39:36 +01:00
|
|
|
mp_raise_NotImplementedError(NULL);
|
2016-05-03 08:42:47 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mp_const_none;
|
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_sendall_obj, lwip_socket_sendall);
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
STATIC mp_obj_t lwip_socket_settimeout(mp_obj_t self_in, mp_obj_t timeout_in) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2015-10-12 00:43:33 +01:00
|
|
|
mp_uint_t timeout;
|
|
|
|
if (timeout_in == mp_const_none) {
|
|
|
|
timeout = -1;
|
|
|
|
} else {
|
2016-04-26 12:47:24 +01:00
|
|
|
#if MICROPY_PY_BUILTINS_FLOAT
|
2015-10-12 00:43:33 +01:00
|
|
|
timeout = 1000 * mp_obj_get_float(timeout_in);
|
|
|
|
#else
|
|
|
|
timeout = 1000 * mp_obj_get_int(timeout_in);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
socket->timeout = timeout;
|
|
|
|
return mp_const_none;
|
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_settimeout_obj, lwip_socket_settimeout);
|
|
|
|
|
2016-02-27 00:04:36 +00:00
|
|
|
STATIC mp_obj_t lwip_socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2016-02-27 00:04:36 +00:00
|
|
|
bool val = mp_obj_is_true(flag_in);
|
|
|
|
if (val) {
|
|
|
|
socket->timeout = -1;
|
|
|
|
} else {
|
|
|
|
socket->timeout = 0;
|
|
|
|
}
|
|
|
|
return mp_const_none;
|
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_setblocking_obj, lwip_socket_setblocking);
|
|
|
|
|
2017-08-30 01:59:58 +01:00
|
|
|
STATIC mp_obj_t lwip_socket_setsockopt(size_t n_args, const mp_obj_t *args) {
|
2015-12-31 20:54:28 +00:00
|
|
|
(void)n_args; // always 4
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(args[0]);
|
2016-04-17 16:06:45 +01:00
|
|
|
|
|
|
|
int opt = mp_obj_get_int(args[2]);
|
|
|
|
if (opt == 20) {
|
|
|
|
if (args[3] == mp_const_none) {
|
|
|
|
socket->callback = MP_OBJ_NULL;
|
|
|
|
} else {
|
|
|
|
socket->callback = args[3];
|
|
|
|
}
|
|
|
|
return mp_const_none;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (opt) {
|
2017-07-31 20:38:37 +01:00
|
|
|
// level: SOL_SOCKET
|
|
|
|
case SOF_REUSEADDR: {
|
|
|
|
mp_int_t val = mp_obj_get_int(args[3]);
|
2016-03-25 15:44:24 +00:00
|
|
|
// Options are common for UDP and TCP pcb's.
|
2016-03-25 18:53:52 +00:00
|
|
|
if (val) {
|
|
|
|
ip_set_option(socket->pcb.tcp, SOF_REUSEADDR);
|
|
|
|
} else {
|
|
|
|
ip_reset_option(socket->pcb.tcp, SOF_REUSEADDR);
|
|
|
|
}
|
2016-03-25 15:44:24 +00:00
|
|
|
break;
|
2017-07-31 20:38:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// level: IPPROTO_IP
|
|
|
|
case IP_ADD_MEMBERSHIP: {
|
|
|
|
mp_buffer_info_t bufinfo;
|
|
|
|
mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ);
|
|
|
|
if (bufinfo.len != sizeof(ip_addr_t) * 2) {
|
|
|
|
mp_raise_ValueError(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
// POSIX setsockopt has order: group addr, if addr, lwIP has it vice-versa
|
|
|
|
err_t err = igmp_joingroup((ip_addr_t*)bufinfo.buf + 1, bufinfo.buf);
|
|
|
|
if (err != ERR_OK) {
|
|
|
|
mp_raise_OSError(error_lookup_table[-err]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-03-25 15:44:24 +00:00
|
|
|
default:
|
|
|
|
printf("Warning: lwip.setsockopt() not implemented\n");
|
|
|
|
}
|
2015-12-31 20:54:28 +00:00
|
|
|
return mp_const_none;
|
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_setsockopt_obj, 4, 4, lwip_socket_setsockopt);
|
|
|
|
|
2017-08-30 01:59:58 +01:00
|
|
|
STATIC mp_obj_t lwip_socket_makefile(size_t n_args, const mp_obj_t *args) {
|
2016-01-01 05:31:16 +00:00
|
|
|
(void)n_args;
|
|
|
|
return args[0];
|
|
|
|
}
|
|
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_makefile_obj, 1, 3, lwip_socket_makefile);
|
|
|
|
|
2016-01-01 00:36:30 +00:00
|
|
|
STATIC mp_uint_t lwip_socket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2016-01-01 00:36:30 +00:00
|
|
|
|
|
|
|
switch (socket->type) {
|
|
|
|
case MOD_NETWORK_SOCK_STREAM:
|
|
|
|
return lwip_tcp_receive(socket, buf, size, errcode);
|
|
|
|
case MOD_NETWORK_SOCK_DGRAM:
|
|
|
|
return lwip_udp_receive(socket, buf, size, NULL, NULL, errcode);
|
|
|
|
}
|
|
|
|
// Unreachable
|
|
|
|
return MP_STREAM_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-01-02 21:22:37 +00:00
|
|
|
STATIC mp_uint_t lwip_socket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2016-01-02 21:22:37 +00:00
|
|
|
|
|
|
|
switch (socket->type) {
|
|
|
|
case MOD_NETWORK_SOCK_STREAM:
|
|
|
|
return lwip_tcp_send(socket, buf, size, errcode);
|
|
|
|
case MOD_NETWORK_SOCK_DGRAM:
|
|
|
|
return lwip_udp_send(socket, buf, size, NULL, 0, errcode);
|
|
|
|
}
|
|
|
|
// Unreachable
|
|
|
|
return MP_STREAM_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-12-02 05:41:51 +00:00
|
|
|
STATIC mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
|
2018-07-08 14:15:44 +01:00
|
|
|
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
|
2016-12-02 05:41:51 +00:00
|
|
|
mp_uint_t ret;
|
|
|
|
|
|
|
|
if (request == MP_STREAM_POLL) {
|
|
|
|
uintptr_t flags = arg;
|
|
|
|
ret = 0;
|
|
|
|
|
2018-12-03 07:02:10 +00:00
|
|
|
if (flags & MP_STREAM_POLL_RD) {
|
|
|
|
if (socket->state == STATE_LISTENING) {
|
|
|
|
// Listening TCP socket may have one or multiple connections waiting
|
|
|
|
if ((socket->incoming.connection.alloc == 0
|
|
|
|
&& socket->incoming.connection.tcp.item != NULL)
|
|
|
|
|| socket->incoming.connection.tcp.array[socket->incoming.connection.iget] != NULL) {
|
|
|
|
ret |= MP_STREAM_POLL_RD;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Otherwise there is just one slot for incoming data
|
|
|
|
if (socket->incoming.pbuf != NULL) {
|
|
|
|
ret |= MP_STREAM_POLL_RD;
|
|
|
|
}
|
|
|
|
}
|
2016-12-02 05:41:51 +00:00
|
|
|
}
|
|
|
|
|
2019-01-31 00:22:03 +00:00
|
|
|
if (flags & MP_STREAM_POLL_WR) {
|
|
|
|
if (socket->type == MOD_NETWORK_SOCK_DGRAM && socket->pcb.udp != NULL) {
|
|
|
|
// UDP socket is writable
|
|
|
|
ret |= MP_STREAM_POLL_WR;
|
|
|
|
} else if (socket->pcb.tcp != NULL && tcp_sndbuf(socket->pcb.tcp) > 0) {
|
|
|
|
// TCP socket is writable
|
|
|
|
// Note: pcb.tcp==NULL if state<0, and in this case we can't call tcp_sndbuf
|
|
|
|
ret |= MP_STREAM_POLL_WR;
|
|
|
|
}
|
2016-12-02 05:41:51 +00:00
|
|
|
}
|
|
|
|
|
2018-05-17 14:17:36 +01:00
|
|
|
if (socket->state == STATE_NEW) {
|
|
|
|
// New sockets are not connected so set HUP
|
|
|
|
ret |= flags & MP_STREAM_POLL_HUP;
|
|
|
|
} else if (socket->state == STATE_PEER_CLOSED) {
|
2017-05-01 16:20:09 +01:00
|
|
|
// Peer-closed socket is both readable and writable: read will
|
|
|
|
// return EOF, write - error. Without this poll will hang on a
|
|
|
|
// socket which was closed by peer.
|
|
|
|
ret |= flags & (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR);
|
2018-05-04 06:15:04 +01:00
|
|
|
} else if (socket->state == ERR_RST) {
|
|
|
|
// Socket was reset by peer, a write will return an error
|
|
|
|
ret |= flags & (MP_STREAM_POLL_WR | MP_STREAM_POLL_HUP);
|
|
|
|
} else if (socket->state < 0) {
|
|
|
|
// Socket in some other error state, use catch-all ERR flag
|
|
|
|
// TODO: may need to set other return flags here
|
|
|
|
ret |= flags & MP_STREAM_POLL_ERR;
|
2016-12-02 05:41:51 +00:00
|
|
|
}
|
|
|
|
|
2018-03-07 06:48:53 +00:00
|
|
|
} else if (request == MP_STREAM_CLOSE) {
|
|
|
|
bool socket_is_listener = false;
|
|
|
|
|
|
|
|
if (socket->pcb.tcp == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
2018-07-20 03:59:24 +01:00
|
|
|
|
|
|
|
// Deregister callback (pcb.tcp is set to NULL below so must deregister now)
|
|
|
|
tcp_recv(socket->pcb.tcp, NULL);
|
|
|
|
|
2018-03-07 06:48:53 +00:00
|
|
|
switch (socket->type) {
|
|
|
|
case MOD_NETWORK_SOCK_STREAM: {
|
|
|
|
if (socket->pcb.tcp->state == LISTEN) {
|
|
|
|
socket_is_listener = true;
|
|
|
|
}
|
|
|
|
if (tcp_close(socket->pcb.tcp) != ERR_OK) {
|
|
|
|
DEBUG_printf("lwip_close: had to call tcp_abort()\n");
|
|
|
|
tcp_abort(socket->pcb.tcp);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MOD_NETWORK_SOCK_DGRAM: udp_remove(socket->pcb.udp); break;
|
|
|
|
//case MOD_NETWORK_SOCK_RAW: raw_remove(socket->pcb.raw); break;
|
|
|
|
}
|
|
|
|
socket->pcb.tcp = NULL;
|
|
|
|
socket->state = _ERR_BADF;
|
2018-11-28 05:46:02 +00:00
|
|
|
if (!socket_is_listener) {
|
|
|
|
if (socket->incoming.pbuf != NULL) {
|
2018-03-07 06:48:53 +00:00
|
|
|
pbuf_free(socket->incoming.pbuf);
|
2018-11-28 05:46:02 +00:00
|
|
|
socket->incoming.pbuf = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uint8_t alloc = socket->incoming.connection.alloc;
|
|
|
|
struct tcp_pcb *volatile *tcp_array;
|
|
|
|
if (alloc == 0) {
|
|
|
|
tcp_array = &socket->incoming.connection.tcp.item;
|
2018-03-07 06:48:53 +00:00
|
|
|
} else {
|
2018-11-28 05:46:02 +00:00
|
|
|
tcp_array = socket->incoming.connection.tcp.array;
|
|
|
|
}
|
|
|
|
for (uint8_t i = 0; i < alloc; ++i) {
|
2018-07-20 03:59:24 +01:00
|
|
|
// Deregister callback and abort
|
2018-11-28 05:46:02 +00:00
|
|
|
if (tcp_array[i] != NULL) {
|
|
|
|
tcp_poll(tcp_array[i], NULL, 0);
|
|
|
|
tcp_abort(tcp_array[i]);
|
|
|
|
tcp_array[i] = NULL;
|
|
|
|
}
|
2018-03-07 06:48:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
|
2016-12-02 05:41:51 +00:00
|
|
|
} else {
|
|
|
|
*errcode = MP_EINVAL;
|
|
|
|
ret = MP_STREAM_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-07-29 16:09:33 +01:00
|
|
|
STATIC const mp_rom_map_elem_t lwip_socket_locals_dict_table[] = {
|
2018-03-07 06:48:53 +00:00
|
|
|
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
|
2017-07-29 16:09:33 +01:00
|
|
|
{ MP_ROM_QSTR(MP_QSTR_bind), MP_ROM_PTR(&lwip_socket_bind_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_listen), MP_ROM_PTR(&lwip_socket_listen_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_accept), MP_ROM_PTR(&lwip_socket_accept_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&lwip_socket_connect_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&lwip_socket_send_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&lwip_socket_recv_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_sendto), MP_ROM_PTR(&lwip_socket_sendto_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_recvfrom), MP_ROM_PTR(&lwip_socket_recvfrom_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_sendall), MP_ROM_PTR(&lwip_socket_sendall_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_settimeout), MP_ROM_PTR(&lwip_socket_settimeout_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&lwip_socket_setblocking_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_setsockopt), MP_ROM_PTR(&lwip_socket_setsockopt_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_makefile), MP_ROM_PTR(&lwip_socket_makefile_obj) },
|
|
|
|
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
|
2015-10-12 00:43:33 +01:00
|
|
|
};
|
|
|
|
STATIC MP_DEFINE_CONST_DICT(lwip_socket_locals_dict, lwip_socket_locals_dict_table);
|
|
|
|
|
2016-01-01 00:36:30 +00:00
|
|
|
STATIC const mp_stream_p_t lwip_socket_stream_p = {
|
|
|
|
.read = lwip_socket_read,
|
2016-01-02 21:22:37 +00:00
|
|
|
.write = lwip_socket_write,
|
2016-12-02 05:41:51 +00:00
|
|
|
.ioctl = lwip_socket_ioctl,
|
2016-01-01 00:36:30 +00:00
|
|
|
};
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
STATIC const mp_obj_type_t lwip_socket_type = {
|
|
|
|
{ &mp_type_type },
|
|
|
|
.name = MP_QSTR_socket,
|
2015-12-28 23:08:30 +00:00
|
|
|
.print = lwip_socket_print,
|
2015-10-12 00:43:33 +01:00
|
|
|
.make_new = lwip_socket_make_new,
|
2016-06-18 16:19:24 +01:00
|
|
|
.protocol = &lwip_socket_stream_p,
|
2017-07-29 16:09:33 +01:00
|
|
|
.locals_dict = (mp_obj_dict_t*)&lwip_socket_locals_dict,
|
2015-10-12 00:43:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
// Support functions for memory protection. lwIP has its own memory management
|
|
|
|
// routines for its internal structures, and since they might be called in
|
|
|
|
// interrupt handlers, they need some protection.
|
|
|
|
sys_prot_t sys_arch_protect() {
|
|
|
|
return (sys_prot_t)MICROPY_BEGIN_ATOMIC_SECTION();
|
|
|
|
}
|
|
|
|
|
|
|
|
void sys_arch_unprotect(sys_prot_t state) {
|
|
|
|
MICROPY_END_ATOMIC_SECTION((mp_uint_t)state);
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
// Polling callbacks for the interfaces connected to lwIP. Right now it calls
|
|
|
|
// itself a "list" but isn't; we only support a single interface.
|
|
|
|
|
|
|
|
typedef struct nic_poll {
|
|
|
|
void (* poll)(void *arg);
|
|
|
|
void *poll_arg;
|
|
|
|
} nic_poll_t;
|
|
|
|
|
|
|
|
STATIC nic_poll_t lwip_poll_list;
|
|
|
|
|
|
|
|
void mod_lwip_register_poll(void (* poll)(void *arg), void *poll_arg) {
|
|
|
|
lwip_poll_list.poll = poll;
|
|
|
|
lwip_poll_list.poll_arg = poll_arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mod_lwip_deregister_poll(void (* poll)(void *arg), void *poll_arg) {
|
|
|
|
lwip_poll_list.poll = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
// The lwip global functions.
|
|
|
|
|
|
|
|
STATIC mp_obj_t mod_lwip_reset() {
|
|
|
|
lwip_init();
|
|
|
|
lwip_poll_list.poll = NULL;
|
|
|
|
return mp_const_none;
|
|
|
|
}
|
|
|
|
MP_DEFINE_CONST_FUN_OBJ_0(mod_lwip_reset_obj, mod_lwip_reset);
|
|
|
|
|
|
|
|
STATIC mp_obj_t mod_lwip_callback() {
|
|
|
|
if (lwip_poll_list.poll != NULL) {
|
|
|
|
lwip_poll_list.poll(lwip_poll_list.poll_arg);
|
|
|
|
}
|
|
|
|
sys_check_timeouts();
|
|
|
|
return mp_const_none;
|
|
|
|
}
|
|
|
|
MP_DEFINE_CONST_FUN_OBJ_0(mod_lwip_callback_obj, mod_lwip_callback);
|
|
|
|
|
2016-02-18 12:50:29 +00:00
|
|
|
typedef struct _getaddrinfo_state_t {
|
|
|
|
volatile int status;
|
|
|
|
volatile ip_addr_t ipaddr;
|
|
|
|
} getaddrinfo_state_t;
|
|
|
|
|
|
|
|
// Callback for incoming DNS requests.
|
2018-05-17 03:58:34 +01:00
|
|
|
#if LWIP_VERSION_MAJOR < 2
|
|
|
|
STATIC void lwip_getaddrinfo_cb(const char *name, ip_addr_t *ipaddr, void *arg)
|
|
|
|
#else
|
|
|
|
STATIC void lwip_getaddrinfo_cb(const char *name, const ip_addr_t *ipaddr, void *arg)
|
|
|
|
#endif
|
|
|
|
{
|
2016-02-18 12:50:29 +00:00
|
|
|
getaddrinfo_state_t *state = arg;
|
|
|
|
if (ipaddr != NULL) {
|
|
|
|
state->status = 1;
|
|
|
|
state->ipaddr = *ipaddr;
|
|
|
|
} else {
|
|
|
|
// error
|
|
|
|
state->status = -2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
// lwip.getaddrinfo
|
2017-04-29 16:56:39 +01:00
|
|
|
STATIC mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) {
|
|
|
|
mp_obj_t host_in = args[0], port_in = args[1];
|
2017-03-26 09:19:35 +01:00
|
|
|
const char *host = mp_obj_str_get_str(host_in);
|
2015-10-12 00:43:33 +01:00
|
|
|
mp_int_t port = mp_obj_get_int(port_in);
|
|
|
|
|
2018-04-23 07:38:20 +01:00
|
|
|
// If constraints were passed then check they are compatible with the supported params
|
|
|
|
if (n_args > 2) {
|
|
|
|
mp_int_t family = mp_obj_get_int(args[2]);
|
|
|
|
mp_int_t type = 0;
|
|
|
|
mp_int_t proto = 0;
|
|
|
|
mp_int_t flags = 0;
|
|
|
|
if (n_args > 3) {
|
|
|
|
type = mp_obj_get_int(args[3]);
|
|
|
|
if (n_args > 4) {
|
|
|
|
proto = mp_obj_get_int(args[4]);
|
|
|
|
if (n_args > 5) {
|
|
|
|
flags = mp_obj_get_int(args[5]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!((family == 0 || family == MOD_NETWORK_AF_INET)
|
|
|
|
&& (type == 0 || type == MOD_NETWORK_SOCK_STREAM)
|
|
|
|
&& proto == 0
|
|
|
|
&& flags == 0)) {
|
2018-12-21 11:20:55 +00:00
|
|
|
mp_warning(MP_WARN_CAT(RuntimeWarning), "unsupported getaddrinfo constraints");
|
2018-04-23 07:38:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-18 12:50:29 +00:00
|
|
|
getaddrinfo_state_t state;
|
|
|
|
state.status = 0;
|
2015-10-12 00:43:33 +01:00
|
|
|
|
2016-02-18 12:50:29 +00:00
|
|
|
err_t ret = dns_gethostbyname(host, (ip_addr_t*)&state.ipaddr, lwip_getaddrinfo_cb, &state);
|
|
|
|
switch (ret) {
|
|
|
|
case ERR_OK:
|
|
|
|
// cached
|
|
|
|
state.status = 1;
|
|
|
|
break;
|
|
|
|
case ERR_INPROGRESS:
|
|
|
|
while (state.status == 0) {
|
2015-12-28 16:58:14 +00:00
|
|
|
poll_sockets();
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
break;
|
2016-02-18 12:50:29 +00:00
|
|
|
default:
|
|
|
|
state.status = ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state.status < 0) {
|
|
|
|
// TODO: CPython raises gaierror, we raise with native lwIP negative error
|
|
|
|
// values, to differentiate from normal errno's at least in such way.
|
2016-10-07 03:47:57 +01:00
|
|
|
mp_raise_OSError(state.status);
|
2015-10-12 00:43:33 +01:00
|
|
|
}
|
|
|
|
|
2018-07-08 14:15:44 +01:00
|
|
|
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL));
|
2015-10-12 00:43:33 +01:00
|
|
|
tuple->items[0] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_AF_INET);
|
|
|
|
tuple->items[1] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_SOCK_STREAM);
|
|
|
|
tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0);
|
|
|
|
tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_);
|
2016-02-18 12:50:29 +00:00
|
|
|
tuple->items[4] = netutils_format_inet_addr((uint8_t*)&state.ipaddr, port, NETUTILS_BIG);
|
2015-10-12 00:43:33 +01:00
|
|
|
return mp_obj_new_list(1, (mp_obj_t*)&tuple);
|
|
|
|
}
|
2018-05-21 07:46:30 +01:00
|
|
|
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_getaddrinfo_obj, 2, 6, lwip_getaddrinfo);
|
2015-10-12 00:43:33 +01:00
|
|
|
|
2016-04-26 14:30:13 +01:00
|
|
|
// Debug functions
|
|
|
|
|
|
|
|
STATIC mp_obj_t lwip_print_pcbs() {
|
|
|
|
tcp_debug_print_pcbs();
|
|
|
|
return mp_const_none;
|
|
|
|
}
|
|
|
|
MP_DEFINE_CONST_FUN_OBJ_0(lwip_print_pcbs_obj, lwip_print_pcbs);
|
|
|
|
|
2015-10-12 00:43:33 +01:00
|
|
|
#ifdef MICROPY_PY_LWIP
|
|
|
|
|
2017-07-29 16:09:33 +01:00
|
|
|
STATIC const mp_rom_map_elem_t mp_module_lwip_globals_table[] = {
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_lwip) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&mod_lwip_reset_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_callback), MP_ROM_PTR(&mod_lwip_callback_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_getaddrinfo), MP_ROM_PTR(&lwip_getaddrinfo_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_print_pcbs), MP_ROM_PTR(&lwip_print_pcbs_obj) },
|
2015-10-12 00:43:33 +01:00
|
|
|
// objects
|
2017-07-29 16:09:33 +01:00
|
|
|
{ MP_ROM_QSTR(MP_QSTR_socket), MP_ROM_PTR(&lwip_socket_type) },
|
2015-10-12 00:43:33 +01:00
|
|
|
#ifdef MICROPY_PY_LWIP_SLIP
|
2017-07-29 16:09:33 +01:00
|
|
|
{ MP_ROM_QSTR(MP_QSTR_slip), MP_ROM_PTR(&lwip_slip_type) },
|
2015-10-12 00:43:33 +01:00
|
|
|
#endif
|
|
|
|
// class constants
|
2017-07-31 04:00:34 +01:00
|
|
|
{ MP_ROM_QSTR(MP_QSTR_AF_INET), MP_ROM_INT(MOD_NETWORK_AF_INET) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_AF_INET6), MP_ROM_INT(MOD_NETWORK_AF_INET6) },
|
2015-10-12 00:43:33 +01:00
|
|
|
|
2017-07-31 04:00:34 +01:00
|
|
|
{ MP_ROM_QSTR(MP_QSTR_SOCK_STREAM), MP_ROM_INT(MOD_NETWORK_SOCK_STREAM) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_SOCK_DGRAM), MP_ROM_INT(MOD_NETWORK_SOCK_DGRAM) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_SOCK_RAW), MP_ROM_INT(MOD_NETWORK_SOCK_RAW) },
|
2016-03-25 13:06:12 +00:00
|
|
|
|
2017-07-31 04:00:34 +01:00
|
|
|
{ MP_ROM_QSTR(MP_QSTR_SOL_SOCKET), MP_ROM_INT(1) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_SO_REUSEADDR), MP_ROM_INT(SOF_REUSEADDR) },
|
2017-07-31 20:38:37 +01:00
|
|
|
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_IPPROTO_IP), MP_ROM_INT(0) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_IP_ADD_MEMBERSHIP), MP_ROM_INT(IP_ADD_MEMBERSHIP) },
|
2015-10-12 00:43:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
STATIC MP_DEFINE_CONST_DICT(mp_module_lwip_globals, mp_module_lwip_globals_table);
|
|
|
|
|
|
|
|
const mp_obj_module_t mp_module_lwip = {
|
|
|
|
.base = { &mp_type_module },
|
|
|
|
.globals = (mp_obj_dict_t*)&mp_module_lwip_globals,
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif // MICROPY_PY_LWIP
|