mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-06 15:51:25 +00:00
Add missing BSD functions
This commit is contained in:
1 parent
7886a73807
commit
66afa5e551
6 files changed
+646
-39
No files matched your search
+237
-15
@@ -199,8 +199,9 @@ static struct mg_mgr *s_mgr;
|
||||
enum bsd_op {
|
||||
BSD_OP_NONE, BSD_OP_SOCKET, BSD_OP_BIND, BSD_OP_LISTEN, BSD_OP_FCNTL,
|
||||
BSD_OP_ACCEPT, BSD_OP_CONNECT, BSD_OP_RECV, BSD_OP_SEND, BSD_OP_SENDTO,
|
||||
BSD_OP_RECVFROM, BSD_OP_CLOSE, BSD_OP_POLL, BSD_OP_SELECT,
|
||||
BSD_OP_GETHOSTBYNAME,
|
||||
BSD_OP_RECVFROM, BSD_OP_CLOSE, BSD_OP_SHUTDOWN, BSD_OP_SETSOCKOPT,
|
||||
BSD_OP_GETSOCKOPT, BSD_OP_GETSOCKNAME, BSD_OP_GETPEERNAME, BSD_OP_POLL,
|
||||
BSD_OP_SELECT, BSD_OP_GETHOSTBYNAME,
|
||||
};
|
||||
|
||||
// A BSD API request from a user task. Gets sent via the RTOS queue, and
|
||||
@@ -217,6 +218,7 @@ struct mg_bsd_cmd {
|
||||
struct { int fd, backlog; } op_listen;
|
||||
struct { int fd, cmd, arg; } op_fcntl;
|
||||
struct { int fd; } op_close;
|
||||
struct { int fd, how; } op_shutdown;
|
||||
struct { int fd; const struct sockaddr *addr; socklen_t len; } op_bind;
|
||||
struct { int fd; const struct sockaddr *addr; socklen_t len; } op_connect;
|
||||
struct { int fd; struct sockaddr *addr; socklen_t *addrlen; } op_accept;
|
||||
@@ -224,6 +226,9 @@ struct mg_bsd_cmd {
|
||||
struct { int fd; const void *buf; size_t len; int flags; } op_send;
|
||||
struct { int fd; const void *buf; size_t len; int flags; const struct sockaddr *addr; socklen_t addrlen; } op_sendto;
|
||||
struct { int fd; void *buf; size_t len; int flags; struct sockaddr *src; socklen_t *addrlen; } op_recvfrom;
|
||||
struct { int fd, level, optname; const void *optval; socklen_t optlen; } op_setsockopt;
|
||||
struct { int fd, level, optname; void *optval; socklen_t *optlen; } op_getsockopt;
|
||||
struct { int fd; struct sockaddr *addr; socklen_t *addrlen; } op_getname;
|
||||
struct { struct pollfd *fds; nfds_t nfds; uint64_t deadline; } op_poll;
|
||||
struct { int nfds; fd_set *r, *w, *e; uint64_t deadline; } op_select;
|
||||
struct { const char *name; struct hostent *result; } op_gethostbyname;
|
||||
@@ -240,6 +245,7 @@ struct mg_bsd_sock {
|
||||
struct mg_connection *c;
|
||||
struct mg_bsd_sock *listener;
|
||||
bool used, bound, closed, nonblock, is_udp, orphan; // closed: c was closed
|
||||
bool error_pending, is_shut_rd, is_shut_wr;
|
||||
};
|
||||
|
||||
static struct mg_bsd_sock s_socks[MG_BSD_MAX_SOCKETS > 0 ?
|
||||
@@ -260,6 +266,21 @@ static struct mg_bsd_sock *bsd_get(int fd) {
|
||||
return s;
|
||||
}
|
||||
|
||||
static int bsd_addr4(const struct sockaddr *addr, socklen_t len) {
|
||||
if (addr == NULL || len < sizeof(struct sockaddr_in)) return EINVAL;
|
||||
return addr->sa_family == AF_INET ? 0 : EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
static bool bsd_aton4(const char *str, in_addr_t *ip) {
|
||||
struct mg_addr addr;
|
||||
size_t len = strlen(str);
|
||||
|
||||
if (len == 0 || !mg_aton(mg_str_n(str, len), &addr) || addr.is_ip6)
|
||||
return false;
|
||||
memcpy(ip, addr.addr.ip, sizeof(*ip));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bsd_attach(struct mg_bsd_sock *s, struct mg_connection *c) {
|
||||
s->c = c;
|
||||
c->fn = bsd_ev;
|
||||
@@ -345,8 +366,10 @@ static void bsd_ev(struct mg_connection *c, int ev, void *ev_data) {
|
||||
c->is_bsd = 1;
|
||||
c->is_udp = child->is_udp;
|
||||
}
|
||||
} else if (ev == MG_EV_READ && s->is_shut_rd) {
|
||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||
} else if (ev == MG_EV_ERROR) {
|
||||
if (s->err == 0) s->err = EIO;
|
||||
if (s->err == 0) s->err = EIO, s->error_pending = true;
|
||||
#if MG_ENABLE_FREERTOS
|
||||
bsd_finish_waiters(s, true);
|
||||
#endif
|
||||
@@ -456,9 +479,11 @@ static int bsd_ready(struct mg_bsd_sock *s) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ;
|
||||
if (!s->is_udp && !s->c->is_connecting && !s->c->is_closing &&
|
||||
!s->c->is_draining)
|
||||
if (s->is_shut_rd || s->c->recv.len > 0 || s->c->is_draining)
|
||||
ready |= BSD_READ;
|
||||
if (s->is_shut_wr) ready |= BSD_ERROR;
|
||||
if (!s->is_shut_wr && !s->is_udp && !s->c->is_connecting &&
|
||||
!s->c->is_closing && !s->c->is_draining)
|
||||
ready |= BSD_WRITE; // Established TCP socket
|
||||
return ready;
|
||||
}
|
||||
@@ -535,9 +560,13 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_BIND:
|
||||
case BSD_OP_BIND: {
|
||||
int err;
|
||||
|
||||
if ((s = bsd_get(cmd->op_bind.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if ((err = bsd_addr4(cmd->op_bind.addr, cmd->op_bind.len)) != 0) {
|
||||
cmd->result = -1, cmd->err = err;
|
||||
} else {
|
||||
const struct sockaddr_in *sa =
|
||||
(const struct sockaddr_in *) cmd->op_bind.addr;
|
||||
@@ -556,6 +585,7 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case BSD_OP_LISTEN: {
|
||||
char url[32];
|
||||
|
||||
@@ -631,11 +661,17 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
case BSD_OP_CONNECT:
|
||||
case BSD_OP_CONNECT: {
|
||||
int err;
|
||||
|
||||
if ((s = bsd_get(cmd->op_connect.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
return true;
|
||||
}
|
||||
if ((err = bsd_addr4(cmd->op_connect.addr, cmd->op_connect.len)) != 0) {
|
||||
cmd->result = -1, cmd->err = err;
|
||||
return true;
|
||||
}
|
||||
if (!cmd->started && s->c == NULL) {
|
||||
if (s->closed || (c = bsd_new_conn(s)) == NULL) {
|
||||
cmd->result = -1, cmd->err = s->closed ? EIO : ENOMEM;
|
||||
@@ -674,6 +710,7 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
cmd->result = -1, cmd->err = EINPROGRESS;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
case BSD_OP_RECV:
|
||||
if ((s = bsd_get(cmd->op_recv.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
@@ -696,7 +733,7 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
cmd->result = -1, cmd->err = s->err;
|
||||
return true;
|
||||
}
|
||||
if (c->is_draining) { // Orderly shutdown by peer
|
||||
if (s->is_shut_rd || c->is_draining) { // Local or peer shutdown
|
||||
cmd->result = 0;
|
||||
return true;
|
||||
}
|
||||
@@ -710,8 +747,8 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
case BSD_OP_SEND:
|
||||
if ((s = bsd_get(cmd->op_send.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
||||
c->is_draining) {
|
||||
} else if (s->is_shut_wr || (c = s->c) == NULL || s->err != 0 ||
|
||||
c->is_closing || c->is_draining) {
|
||||
cmd->result = -1, cmd->err = EPIPE;
|
||||
} else if (mg_send(c, cmd->op_send.buf, cmd->op_send.len)) {
|
||||
cmd->result = (int) cmd->op_send.len;
|
||||
@@ -725,11 +762,16 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_SENDTO:
|
||||
case BSD_OP_SENDTO: {
|
||||
int err;
|
||||
|
||||
if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
||||
c->is_draining) {
|
||||
} else if ((err = bsd_addr4(cmd->op_sendto.addr,
|
||||
cmd->op_sendto.addrlen)) != 0) {
|
||||
cmd->result = -1, cmd->err = err;
|
||||
} else if (s->is_shut_wr || (c = s->c) == NULL || s->err != 0 ||
|
||||
c->is_closing || c->is_draining) {
|
||||
cmd->result = -1, cmd->err = EPIPE;
|
||||
} else {
|
||||
const struct sockaddr_in *sa =
|
||||
@@ -750,11 +792,16 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case BSD_OP_RECVFROM:
|
||||
if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
return true;
|
||||
}
|
||||
if (s->is_shut_rd) {
|
||||
cmd->result = 0;
|
||||
return true;
|
||||
}
|
||||
if ((c = s->c) == NULL) {
|
||||
cmd->result = s->closed && s->err == 0 ? 0 : -1;
|
||||
cmd->err = s->closed ? s->err : ENOTCONN;
|
||||
@@ -762,6 +809,13 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
}
|
||||
if (c->recv.len > 0) {
|
||||
size_t n = cmd->op_recvfrom.len;
|
||||
|
||||
if (cmd->op_recvfrom.src != NULL &&
|
||||
(cmd->op_recvfrom.addrlen == NULL ||
|
||||
*cmd->op_recvfrom.addrlen < sizeof(struct sockaddr_in))) {
|
||||
cmd->result = -1, cmd->err = EINVAL;
|
||||
return true;
|
||||
}
|
||||
if (c->recv.len < n) n = c->recv.len;
|
||||
memcpy(cmd->op_recvfrom.buf, c->recv.buf, n);
|
||||
if (!(cmd->op_recvfrom.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
||||
@@ -794,6 +848,95 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
cmd->result = 0;
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_SHUTDOWN:
|
||||
if ((s = bsd_get(cmd->op_shutdown.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if (cmd->op_shutdown.how != SHUT_RD &&
|
||||
cmd->op_shutdown.how != SHUT_WR &&
|
||||
cmd->op_shutdown.how != SHUT_RDWR) {
|
||||
cmd->result = -1, cmd->err = EINVAL;
|
||||
} else if ((c = s->c) == NULL || c->is_listening || c->is_closing ||
|
||||
(s->is_udp && !c->is_client)) {
|
||||
cmd->result = -1, cmd->err = ENOTCONN;
|
||||
} else {
|
||||
if (cmd->op_shutdown.how != SHUT_WR) {
|
||||
s->is_shut_rd = true;
|
||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||
}
|
||||
if (cmd->op_shutdown.how != SHUT_RD) s->is_shut_wr = true;
|
||||
cmd->result = 0;
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_SETSOCKOPT:
|
||||
if ((s = bsd_get(cmd->op_setsockopt.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if (cmd->op_setsockopt.optval == NULL ||
|
||||
cmd->op_setsockopt.optlen != sizeof(int)) {
|
||||
cmd->result = -1, cmd->err = EINVAL;
|
||||
} else if ((cmd->op_setsockopt.level == SOL_SOCKET &&
|
||||
(cmd->op_setsockopt.optname == SO_REUSEADDR ||
|
||||
cmd->op_setsockopt.optname == SO_NOSIGPIPE)) ||
|
||||
(!s->is_udp && cmd->op_setsockopt.level == IPPROTO_TCP &&
|
||||
cmd->op_setsockopt.optname == TCP_NODELAY)) {
|
||||
cmd->result = 0;
|
||||
} else {
|
||||
cmd->result = -1, cmd->err = ENOPROTOOPT;
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_GETSOCKOPT:
|
||||
if ((s = bsd_get(cmd->op_getsockopt.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if (cmd->op_getsockopt.level != SOL_SOCKET ||
|
||||
cmd->op_getsockopt.optname != SO_ERROR) {
|
||||
cmd->result = -1, cmd->err = ENOPROTOOPT;
|
||||
} else if (cmd->op_getsockopt.optval == NULL ||
|
||||
cmd->op_getsockopt.optlen == NULL ||
|
||||
*cmd->op_getsockopt.optlen < sizeof(int)) {
|
||||
cmd->result = -1, cmd->err = EINVAL;
|
||||
} else {
|
||||
*(int *) cmd->op_getsockopt.optval =
|
||||
s->error_pending ? s->err : 0;
|
||||
s->error_pending = false;
|
||||
*cmd->op_getsockopt.optlen = sizeof(int);
|
||||
cmd->result = 0;
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_GETSOCKNAME:
|
||||
case BSD_OP_GETPEERNAME:
|
||||
if ((s = bsd_get(cmd->op_getname.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if (cmd->op_getname.addr == NULL ||
|
||||
cmd->op_getname.addrlen == NULL ||
|
||||
*cmd->op_getname.addrlen < sizeof(struct sockaddr_in)) {
|
||||
cmd->result = -1, cmd->err = EINVAL;
|
||||
} else if ((c = s->c) == NULL &&
|
||||
(cmd->op == BSD_OP_GETPEERNAME || s->closed)) {
|
||||
cmd->result = -1, cmd->err = ENOTCONN;
|
||||
} else if (cmd->op == BSD_OP_GETPEERNAME &&
|
||||
(c->is_listening || (s->is_udp && !c->is_client))) {
|
||||
cmd->result = -1, cmd->err = ENOTCONN;
|
||||
} else if (c != NULL &&
|
||||
((cmd->op == BSD_OP_GETSOCKNAME && c->loc.is_ip6) ||
|
||||
(cmd->op == BSD_OP_GETPEERNAME && c->rem.is_ip6))) {
|
||||
cmd->result = -1, cmd->err = EAFNOSUPPORT;
|
||||
} else {
|
||||
struct sockaddr_in *sa = (struct sockaddr_in *) cmd->op_getname.addr;
|
||||
|
||||
if (c == NULL) {
|
||||
*sa = s->addr;
|
||||
sa->sin_family = AF_INET;
|
||||
} else {
|
||||
const struct mg_addr *addr = cmd->op == BSD_OP_GETSOCKNAME
|
||||
? &c->loc
|
||||
: &c->rem;
|
||||
sa->sin_family = AF_INET;
|
||||
sa->sin_port = addr->port;
|
||||
sa->sin_addr.s_addr = addr->addr.ip4;
|
||||
}
|
||||
*cmd->op_getname.addrlen = sizeof(*sa);
|
||||
cmd->result = 0;
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_POLL: {
|
||||
struct pollfd *p;
|
||||
int i, n = 0, ready;
|
||||
@@ -810,7 +953,7 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
p->revents |= POLLIN;
|
||||
if ((p->events & POLLOUT) && (ready & BSD_WRITE))
|
||||
p->revents |= POLLOUT;
|
||||
if (s->err != 0) p->revents |= POLLERR;
|
||||
if (s->err != 0 || s->is_shut_wr) p->revents |= POLLERR;
|
||||
if (s->closed) p->revents |= POLLHUP;
|
||||
}
|
||||
if (p->revents != 0) n++;
|
||||
@@ -1062,6 +1205,55 @@ int close(int fd) {
|
||||
return cmd.result;
|
||||
}
|
||||
|
||||
int shutdown(int fd, int how) {
|
||||
struct mg_bsd_cmd cmd = BSD_CMD_EMPTY;
|
||||
cmd.op = BSD_OP_SHUTDOWN, cmd.op_shutdown.fd = fd,
|
||||
cmd.op_shutdown.how = how;
|
||||
bsd_dispatch(&cmd);
|
||||
errno = cmd.err;
|
||||
return cmd.result;
|
||||
}
|
||||
|
||||
int setsockopt(int fd, int level, int optname, const void *optval,
|
||||
socklen_t optlen) {
|
||||
struct mg_bsd_cmd cmd = BSD_CMD_EMPTY;
|
||||
cmd.op = BSD_OP_SETSOCKOPT, cmd.op_setsockopt.fd = fd,
|
||||
cmd.op_setsockopt.level = level, cmd.op_setsockopt.optname = optname,
|
||||
cmd.op_setsockopt.optval = optval, cmd.op_setsockopt.optlen = optlen;
|
||||
bsd_dispatch(&cmd);
|
||||
errno = cmd.err;
|
||||
return cmd.result;
|
||||
}
|
||||
|
||||
int getsockopt(int fd, int level, int optname, void *optval,
|
||||
socklen_t *optlen) {
|
||||
struct mg_bsd_cmd cmd = BSD_CMD_EMPTY;
|
||||
cmd.op = BSD_OP_GETSOCKOPT, cmd.op_getsockopt.fd = fd,
|
||||
cmd.op_getsockopt.level = level, cmd.op_getsockopt.optname = optname,
|
||||
cmd.op_getsockopt.optval = optval, cmd.op_getsockopt.optlen = optlen;
|
||||
bsd_dispatch(&cmd);
|
||||
errno = cmd.err;
|
||||
return cmd.result;
|
||||
}
|
||||
|
||||
int getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen) {
|
||||
struct mg_bsd_cmd cmd = BSD_CMD_EMPTY;
|
||||
cmd.op = BSD_OP_GETSOCKNAME, cmd.op_getname.fd = fd,
|
||||
cmd.op_getname.addr = addr, cmd.op_getname.addrlen = addrlen;
|
||||
bsd_dispatch(&cmd);
|
||||
errno = cmd.err;
|
||||
return cmd.result;
|
||||
}
|
||||
|
||||
int getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen) {
|
||||
struct mg_bsd_cmd cmd = BSD_CMD_EMPTY;
|
||||
cmd.op = BSD_OP_GETPEERNAME, cmd.op_getname.fd = fd,
|
||||
cmd.op_getname.addr = addr, cmd.op_getname.addrlen = addrlen;
|
||||
bsd_dispatch(&cmd);
|
||||
errno = cmd.err;
|
||||
return cmd.result;
|
||||
}
|
||||
|
||||
int poll(struct pollfd *fds, nfds_t nfds, int timeout) {
|
||||
struct mg_bsd_cmd cmd = BSD_CMD_EMPTY;
|
||||
uint64_t deadline = timeout < 0 ? (uint64_t) ~0
|
||||
@@ -1093,6 +1285,36 @@ struct hostent *gethostbyname(const char *name) {
|
||||
return cmd.op_gethostbyname.result;
|
||||
}
|
||||
|
||||
int inet_pton(int af, const char *src, void *dst) {
|
||||
if (af != AF_INET) {
|
||||
errno = EAFNOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
return bsd_aton4(src, dst) ? 1 : 0;
|
||||
}
|
||||
|
||||
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) {
|
||||
if (af != AF_INET) {
|
||||
errno = EAFNOSUPPORT;
|
||||
return NULL;
|
||||
}
|
||||
if (mg_snprintf(dst, size, "%M", mg_print_ip4, (void *) src) >= size) {
|
||||
errno = ENOSPC;
|
||||
return NULL;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
in_addr_t inet_addr(const char *src) {
|
||||
in_addr_t addr;
|
||||
return bsd_aton4(src, &addr) ? addr : INADDR_NONE;
|
||||
}
|
||||
|
||||
char *inet_ntoa(struct in_addr addr) {
|
||||
static char buf[INET_ADDRSTRLEN];
|
||||
return (char *) inet_ntop(AF_INET, &addr, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
#endif // MG_ENABLE_BSD_SOCKETS
|
||||
|
||||
#if MG_ENABLE_BSD_SOCKETS
|
||||
|
||||
+11
@@ -4895,6 +4895,15 @@ void mg_bsd_poll(struct mg_mgr *);
|
||||
#ifndef EPIPE
|
||||
#define EPIPE 32
|
||||
#endif
|
||||
#ifndef EAFNOSUPPORT
|
||||
#define EAFNOSUPPORT 97
|
||||
#endif
|
||||
#ifndef ENOPROTOOPT
|
||||
#define ENOPROTOOPT 92
|
||||
#endif
|
||||
#ifndef ENOSPC
|
||||
#define ENOSPC 28
|
||||
#endif
|
||||
#ifndef EPROTONOSUPPORT
|
||||
#define EPROTONOSUPPORT 93
|
||||
#endif
|
||||
@@ -4976,7 +4985,9 @@ typedef struct fd_set {
|
||||
#define IPPROTO_UDP 17
|
||||
#define INADDR_ANY 0
|
||||
#define INADDR_LOOPBACK 0x7f000001
|
||||
#define INADDR_NONE ((in_addr_t) -1)
|
||||
#define INET_ADDRSTRLEN 16
|
||||
#define INET6_ADDRSTRLEN 46
|
||||
#define SOL_SOCKET 0xffff
|
||||
#define SO_REUSEADDR 2
|
||||
#define SO_REUSEPORT 15
|
||||
|
||||
@@ -32,8 +32,9 @@ static struct mg_mgr *s_mgr;
|
||||
enum bsd_op {
|
||||
BSD_OP_NONE, BSD_OP_SOCKET, BSD_OP_BIND, BSD_OP_LISTEN, BSD_OP_FCNTL,
|
||||
BSD_OP_ACCEPT, BSD_OP_CONNECT, BSD_OP_RECV, BSD_OP_SEND, BSD_OP_SENDTO,
|
||||
BSD_OP_RECVFROM, BSD_OP_CLOSE, BSD_OP_POLL, BSD_OP_SELECT,
|
||||
BSD_OP_GETHOSTBYNAME,
|
||||
BSD_OP_RECVFROM, BSD_OP_CLOSE, BSD_OP_SHUTDOWN, BSD_OP_SETSOCKOPT,
|
||||
BSD_OP_GETSOCKOPT, BSD_OP_GETSOCKNAME, BSD_OP_GETPEERNAME, BSD_OP_POLL,
|
||||
BSD_OP_SELECT, BSD_OP_GETHOSTBYNAME,
|
||||
};
|
||||
|
||||
// A BSD API request from a user task. Gets sent via the RTOS queue, and
|
||||
@@ -50,6 +51,7 @@ struct mg_bsd_cmd {
|
||||
struct { int fd, backlog; } op_listen;
|
||||
struct { int fd, cmd, arg; } op_fcntl;
|
||||
struct { int fd; } op_close;
|
||||
struct { int fd, how; } op_shutdown;
|
||||
struct { int fd; const struct sockaddr *addr; socklen_t len; } op_bind;
|
||||
struct { int fd; const struct sockaddr *addr; socklen_t len; } op_connect;
|
||||
struct { int fd; struct sockaddr *addr; socklen_t *addrlen; } op_accept;
|
||||
@@ -57,6 +59,9 @@ struct mg_bsd_cmd {
|
||||
struct { int fd; const void *buf; size_t len; int flags; } op_send;
|
||||
struct { int fd; const void *buf; size_t len; int flags; const struct sockaddr *addr; socklen_t addrlen; } op_sendto;
|
||||
struct { int fd; void *buf; size_t len; int flags; struct sockaddr *src; socklen_t *addrlen; } op_recvfrom;
|
||||
struct { int fd, level, optname; const void *optval; socklen_t optlen; } op_setsockopt;
|
||||
struct { int fd, level, optname; void *optval; socklen_t *optlen; } op_getsockopt;
|
||||
struct { int fd; struct sockaddr *addr; socklen_t *addrlen; } op_getname;
|
||||
struct { struct pollfd *fds; nfds_t nfds; uint64_t deadline; } op_poll;
|
||||
struct { int nfds; fd_set *r, *w, *e; uint64_t deadline; } op_select;
|
||||
struct { const char *name; struct hostent *result; } op_gethostbyname;
|
||||
@@ -73,6 +78,7 @@ struct mg_bsd_sock {
|
||||
struct mg_connection *c;
|
||||
struct mg_bsd_sock *listener;
|
||||
bool used, bound, closed, nonblock, is_udp, orphan; // closed: c was closed
|
||||
bool error_pending, is_shut_rd, is_shut_wr;
|
||||
};
|
||||
|
||||
static struct mg_bsd_sock s_socks[MG_BSD_MAX_SOCKETS > 0 ?
|
||||
@@ -93,6 +99,21 @@ static struct mg_bsd_sock *bsd_get(int fd) {
|
||||
return s;
|
||||
}
|
||||
|
||||
static int bsd_addr4(const struct sockaddr *addr, socklen_t len) {
|
||||
if (addr == NULL || len < sizeof(struct sockaddr_in)) return EINVAL;
|
||||
return addr->sa_family == AF_INET ? 0 : EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
static bool bsd_aton4(const char *str, in_addr_t *ip) {
|
||||
struct mg_addr addr;
|
||||
size_t len = strlen(str);
|
||||
|
||||
if (len == 0 || !mg_aton(mg_str_n(str, len), &addr) || addr.is_ip6)
|
||||
return false;
|
||||
memcpy(ip, addr.addr.ip, sizeof(*ip));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bsd_attach(struct mg_bsd_sock *s, struct mg_connection *c) {
|
||||
s->c = c;
|
||||
c->fn = bsd_ev;
|
||||
@@ -178,8 +199,10 @@ static void bsd_ev(struct mg_connection *c, int ev, void *ev_data) {
|
||||
c->is_bsd = 1;
|
||||
c->is_udp = child->is_udp;
|
||||
}
|
||||
} else if (ev == MG_EV_READ && s->is_shut_rd) {
|
||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||
} else if (ev == MG_EV_ERROR) {
|
||||
if (s->err == 0) s->err = EIO;
|
||||
if (s->err == 0) s->err = EIO, s->error_pending = true;
|
||||
#if MG_ENABLE_FREERTOS
|
||||
bsd_finish_waiters(s, true);
|
||||
#endif
|
||||
@@ -289,9 +312,11 @@ static int bsd_ready(struct mg_bsd_sock *s) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ;
|
||||
if (!s->is_udp && !s->c->is_connecting && !s->c->is_closing &&
|
||||
!s->c->is_draining)
|
||||
if (s->is_shut_rd || s->c->recv.len > 0 || s->c->is_draining)
|
||||
ready |= BSD_READ;
|
||||
if (s->is_shut_wr) ready |= BSD_ERROR;
|
||||
if (!s->is_shut_wr && !s->is_udp && !s->c->is_connecting &&
|
||||
!s->c->is_closing && !s->c->is_draining)
|
||||
ready |= BSD_WRITE; // Established TCP socket
|
||||
return ready;
|
||||
}
|
||||
@@ -368,9 +393,13 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_BIND:
|
||||
case BSD_OP_BIND: {
|
||||
int err;
|
||||
|
||||
if ((s = bsd_get(cmd->op_bind.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if ((err = bsd_addr4(cmd->op_bind.addr, cmd->op_bind.len)) != 0) {
|
||||
cmd->result = -1, cmd->err = err;
|
||||
} else {
|
||||
const struct sockaddr_in *sa =
|
||||
(const struct sockaddr_in *) cmd->op_bind.addr;
|
||||
@@ -389,6 +418,7 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case BSD_OP_LISTEN: {
|
||||
char url[32];
|
||||
|
||||
@@ -464,11 +494,17 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
case BSD_OP_CONNECT:
|
||||
case BSD_OP_CONNECT: {
|
||||
int err;
|
||||
|
||||
if ((s = bsd_get(cmd->op_connect.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
return true;
|
||||
}
|
||||
if ((err = bsd_addr4(cmd->op_connect.addr, cmd->op_connect.len)) != 0) {
|
||||
cmd->result = -1, cmd->err = err;
|
||||
return true;
|
||||
}
|
||||
if (!cmd->started && s->c == NULL) {
|
||||
if (s->closed || (c = bsd_new_conn(s)) == NULL) {
|
||||
cmd->result = -1, cmd->err = s->closed ? EIO : ENOMEM;
|
||||
@@ -507,6 +543,7 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
cmd->result = -1, cmd->err = EINPROGRESS;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
case BSD_OP_RECV:
|
||||
if ((s = bsd_get(cmd->op_recv.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
@@ -529,7 +566,7 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
cmd->result = -1, cmd->err = s->err;
|
||||
return true;
|
||||
}
|
||||
if (c->is_draining) { // Orderly shutdown by peer
|
||||
if (s->is_shut_rd || c->is_draining) { // Local or peer shutdown
|
||||
cmd->result = 0;
|
||||
return true;
|
||||
}
|
||||
@@ -543,8 +580,8 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
case BSD_OP_SEND:
|
||||
if ((s = bsd_get(cmd->op_send.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
||||
c->is_draining) {
|
||||
} else if (s->is_shut_wr || (c = s->c) == NULL || s->err != 0 ||
|
||||
c->is_closing || c->is_draining) {
|
||||
cmd->result = -1, cmd->err = EPIPE;
|
||||
} else if (mg_send(c, cmd->op_send.buf, cmd->op_send.len)) {
|
||||
cmd->result = (int) cmd->op_send.len;
|
||||
@@ -558,11 +595,16 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_SENDTO:
|
||||
case BSD_OP_SENDTO: {
|
||||
int err;
|
||||
|
||||
if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
||||
c->is_draining) {
|
||||
} else if ((err = bsd_addr4(cmd->op_sendto.addr,
|
||||
cmd->op_sendto.addrlen)) != 0) {
|
||||
cmd->result = -1, cmd->err = err;
|
||||
} else if (s->is_shut_wr || (c = s->c) == NULL || s->err != 0 ||
|
||||
c->is_closing || c->is_draining) {
|
||||
cmd->result = -1, cmd->err = EPIPE;
|
||||
} else {
|
||||
const struct sockaddr_in *sa =
|
||||
@@ -583,11 +625,16 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case BSD_OP_RECVFROM:
|
||||
if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
return true;
|
||||
}
|
||||
if (s->is_shut_rd) {
|
||||
cmd->result = 0;
|
||||
return true;
|
||||
}
|
||||
if ((c = s->c) == NULL) {
|
||||
cmd->result = s->closed && s->err == 0 ? 0 : -1;
|
||||
cmd->err = s->closed ? s->err : ENOTCONN;
|
||||
@@ -595,6 +642,13 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
}
|
||||
if (c->recv.len > 0) {
|
||||
size_t n = cmd->op_recvfrom.len;
|
||||
|
||||
if (cmd->op_recvfrom.src != NULL &&
|
||||
(cmd->op_recvfrom.addrlen == NULL ||
|
||||
*cmd->op_recvfrom.addrlen < sizeof(struct sockaddr_in))) {
|
||||
cmd->result = -1, cmd->err = EINVAL;
|
||||
return true;
|
||||
}
|
||||
if (c->recv.len < n) n = c->recv.len;
|
||||
memcpy(cmd->op_recvfrom.buf, c->recv.buf, n);
|
||||
if (!(cmd->op_recvfrom.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
||||
@@ -627,6 +681,95 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
cmd->result = 0;
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_SHUTDOWN:
|
||||
if ((s = bsd_get(cmd->op_shutdown.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if (cmd->op_shutdown.how != SHUT_RD &&
|
||||
cmd->op_shutdown.how != SHUT_WR &&
|
||||
cmd->op_shutdown.how != SHUT_RDWR) {
|
||||
cmd->result = -1, cmd->err = EINVAL;
|
||||
} else if ((c = s->c) == NULL || c->is_listening || c->is_closing ||
|
||||
(s->is_udp && !c->is_client)) {
|
||||
cmd->result = -1, cmd->err = ENOTCONN;
|
||||
} else {
|
||||
if (cmd->op_shutdown.how != SHUT_WR) {
|
||||
s->is_shut_rd = true;
|
||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||
}
|
||||
if (cmd->op_shutdown.how != SHUT_RD) s->is_shut_wr = true;
|
||||
cmd->result = 0;
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_SETSOCKOPT:
|
||||
if ((s = bsd_get(cmd->op_setsockopt.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if (cmd->op_setsockopt.optval == NULL ||
|
||||
cmd->op_setsockopt.optlen != sizeof(int)) {
|
||||
cmd->result = -1, cmd->err = EINVAL;
|
||||
} else if ((cmd->op_setsockopt.level == SOL_SOCKET &&
|
||||
(cmd->op_setsockopt.optname == SO_REUSEADDR ||
|
||||
cmd->op_setsockopt.optname == SO_NOSIGPIPE)) ||
|
||||
(!s->is_udp && cmd->op_setsockopt.level == IPPROTO_TCP &&
|
||||
cmd->op_setsockopt.optname == TCP_NODELAY)) {
|
||||
cmd->result = 0;
|
||||
} else {
|
||||
cmd->result = -1, cmd->err = ENOPROTOOPT;
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_GETSOCKOPT:
|
||||
if ((s = bsd_get(cmd->op_getsockopt.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if (cmd->op_getsockopt.level != SOL_SOCKET ||
|
||||
cmd->op_getsockopt.optname != SO_ERROR) {
|
||||
cmd->result = -1, cmd->err = ENOPROTOOPT;
|
||||
} else if (cmd->op_getsockopt.optval == NULL ||
|
||||
cmd->op_getsockopt.optlen == NULL ||
|
||||
*cmd->op_getsockopt.optlen < sizeof(int)) {
|
||||
cmd->result = -1, cmd->err = EINVAL;
|
||||
} else {
|
||||
*(int *) cmd->op_getsockopt.optval =
|
||||
s->error_pending ? s->err : 0;
|
||||
s->error_pending = false;
|
||||
*cmd->op_getsockopt.optlen = sizeof(int);
|
||||
cmd->result = 0;
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_GETSOCKNAME:
|
||||
case BSD_OP_GETPEERNAME:
|
||||
if ((s = bsd_get(cmd->op_getname.fd)) == NULL) {
|
||||
cmd->result = -1, cmd->err = EBADF;
|
||||
} else if (cmd->op_getname.addr == NULL ||
|
||||
cmd->op_getname.addrlen == NULL ||
|
||||
*cmd->op_getname.addrlen < sizeof(struct sockaddr_in)) {
|
||||
cmd->result = -1, cmd->err = EINVAL;
|
||||
} else if ((c = s->c) == NULL &&
|
||||
(cmd->op == BSD_OP_GETPEERNAME || s->closed)) {
|
||||
cmd->result = -1, cmd->err = ENOTCONN;
|
||||
} else if (cmd->op == BSD_OP_GETPEERNAME &&
|
||||
(c->is_listening || (s->is_udp && !c->is_client))) {
|
||||
cmd->result = -1, cmd->err = ENOTCONN;
|
||||
} else if (c != NULL &&
|
||||
((cmd->op == BSD_OP_GETSOCKNAME && c->loc.is_ip6) ||
|
||||
(cmd->op == BSD_OP_GETPEERNAME && c->rem.is_ip6))) {
|
||||
cmd->result = -1, cmd->err = EAFNOSUPPORT;
|
||||
} else {
|
||||
struct sockaddr_in *sa = (struct sockaddr_in *) cmd->op_getname.addr;
|
||||
|
||||
if (c == NULL) {
|
||||
*sa = s->addr;
|
||||
sa->sin_family = AF_INET;
|
||||
} else {
|
||||
const struct mg_addr *addr = cmd->op == BSD_OP_GETSOCKNAME
|
||||
? &c->loc
|
||||
: &c->rem;
|
||||
sa->sin_family = AF_INET;
|
||||
sa->sin_port = addr->port;
|
||||
sa->sin_addr.s_addr = addr->addr.ip4;
|
||||
}
|
||||
*cmd->op_getname.addrlen = sizeof(*sa);
|
||||
cmd->result = 0;
|
||||
}
|
||||
return true;
|
||||
case BSD_OP_POLL: {
|
||||
struct pollfd *p;
|
||||
int i, n = 0, ready;
|
||||
@@ -643,7 +786,7 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
||||
p->revents |= POLLIN;
|
||||
if ((p->events & POLLOUT) && (ready & BSD_WRITE))
|
||||
p->revents |= POLLOUT;
|
||||
if (s->err != 0) p->revents |= POLLERR;
|
||||
if (s->err != 0 || s->is_shut_wr) p->revents |= POLLERR;
|
||||
if (s->closed) p->revents |= POLLHUP;
|
||||
}
|
||||
if (p->revents != 0) n++;
|
||||
@@ -895,6 +1038,55 @@ int close(int fd) {
|
||||
return cmd.result;
|
||||
}
|
||||
|
||||
int shutdown(int fd, int how) {
|
||||
struct mg_bsd_cmd cmd = BSD_CMD_EMPTY;
|
||||
cmd.op = BSD_OP_SHUTDOWN, cmd.op_shutdown.fd = fd,
|
||||
cmd.op_shutdown.how = how;
|
||||
bsd_dispatch(&cmd);
|
||||
errno = cmd.err;
|
||||
return cmd.result;
|
||||
}
|
||||
|
||||
int setsockopt(int fd, int level, int optname, const void *optval,
|
||||
socklen_t optlen) {
|
||||
struct mg_bsd_cmd cmd = BSD_CMD_EMPTY;
|
||||
cmd.op = BSD_OP_SETSOCKOPT, cmd.op_setsockopt.fd = fd,
|
||||
cmd.op_setsockopt.level = level, cmd.op_setsockopt.optname = optname,
|
||||
cmd.op_setsockopt.optval = optval, cmd.op_setsockopt.optlen = optlen;
|
||||
bsd_dispatch(&cmd);
|
||||
errno = cmd.err;
|
||||
return cmd.result;
|
||||
}
|
||||
|
||||
int getsockopt(int fd, int level, int optname, void *optval,
|
||||
socklen_t *optlen) {
|
||||
struct mg_bsd_cmd cmd = BSD_CMD_EMPTY;
|
||||
cmd.op = BSD_OP_GETSOCKOPT, cmd.op_getsockopt.fd = fd,
|
||||
cmd.op_getsockopt.level = level, cmd.op_getsockopt.optname = optname,
|
||||
cmd.op_getsockopt.optval = optval, cmd.op_getsockopt.optlen = optlen;
|
||||
bsd_dispatch(&cmd);
|
||||
errno = cmd.err;
|
||||
return cmd.result;
|
||||
}
|
||||
|
||||
int getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen) {
|
||||
struct mg_bsd_cmd cmd = BSD_CMD_EMPTY;
|
||||
cmd.op = BSD_OP_GETSOCKNAME, cmd.op_getname.fd = fd,
|
||||
cmd.op_getname.addr = addr, cmd.op_getname.addrlen = addrlen;
|
||||
bsd_dispatch(&cmd);
|
||||
errno = cmd.err;
|
||||
return cmd.result;
|
||||
}
|
||||
|
||||
int getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen) {
|
||||
struct mg_bsd_cmd cmd = BSD_CMD_EMPTY;
|
||||
cmd.op = BSD_OP_GETPEERNAME, cmd.op_getname.fd = fd,
|
||||
cmd.op_getname.addr = addr, cmd.op_getname.addrlen = addrlen;
|
||||
bsd_dispatch(&cmd);
|
||||
errno = cmd.err;
|
||||
return cmd.result;
|
||||
}
|
||||
|
||||
int poll(struct pollfd *fds, nfds_t nfds, int timeout) {
|
||||
struct mg_bsd_cmd cmd = BSD_CMD_EMPTY;
|
||||
uint64_t deadline = timeout < 0 ? (uint64_t) ~0
|
||||
@@ -926,6 +1118,36 @@ struct hostent *gethostbyname(const char *name) {
|
||||
return cmd.op_gethostbyname.result;
|
||||
}
|
||||
|
||||
int inet_pton(int af, const char *src, void *dst) {
|
||||
if (af != AF_INET) {
|
||||
errno = EAFNOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
return bsd_aton4(src, dst) ? 1 : 0;
|
||||
}
|
||||
|
||||
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) {
|
||||
if (af != AF_INET) {
|
||||
errno = EAFNOSUPPORT;
|
||||
return NULL;
|
||||
}
|
||||
if (mg_snprintf(dst, size, "%M", mg_print_ip4, (void *) src) >= size) {
|
||||
errno = ENOSPC;
|
||||
return NULL;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
in_addr_t inet_addr(const char *src) {
|
||||
in_addr_t addr;
|
||||
return bsd_aton4(src, &addr) ? addr : INADDR_NONE;
|
||||
}
|
||||
|
||||
char *inet_ntoa(struct in_addr addr) {
|
||||
static char buf[INET_ADDRSTRLEN];
|
||||
return (char *) inet_ntop(AF_INET, &addr, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
#endif // MG_ENABLE_BSD_SOCKETS
|
||||
|
||||
#if MG_ENABLE_BSD_SOCKETS
|
||||
|
||||
@@ -55,6 +55,15 @@ void mg_bsd_poll(struct mg_mgr *);
|
||||
#ifndef EPIPE
|
||||
#define EPIPE 32
|
||||
#endif
|
||||
#ifndef EAFNOSUPPORT
|
||||
#define EAFNOSUPPORT 97
|
||||
#endif
|
||||
#ifndef ENOPROTOOPT
|
||||
#define ENOPROTOOPT 92
|
||||
#endif
|
||||
#ifndef ENOSPC
|
||||
#define ENOSPC 28
|
||||
#endif
|
||||
#ifndef EPROTONOSUPPORT
|
||||
#define EPROTONOSUPPORT 93
|
||||
#endif
|
||||
@@ -136,7 +145,9 @@ typedef struct fd_set {
|
||||
#define IPPROTO_UDP 17
|
||||
#define INADDR_ANY 0
|
||||
#define INADDR_LOOPBACK 0x7f000001
|
||||
#define INADDR_NONE ((in_addr_t) -1)
|
||||
#define INET_ADDRSTRLEN 16
|
||||
#define INET6_ADDRSTRLEN 46
|
||||
#define SOL_SOCKET 0xffff
|
||||
#define SO_REUSEADDR 2
|
||||
#define SO_REUSEPORT 15
|
||||
|
||||
+11
-2
@@ -92,7 +92,7 @@ fail:
|
||||
exit(1);
|
||||
}
|
||||
|
||||
enum wait_mode { WAIT_ABORT, WAIT_DRAIN, WAIT_HOLD, WAIT_CLOSE };
|
||||
enum wait_mode { WAIT_ABORT, WAIT_DRAIN, WAIT_HOLD, WAIT_CLOSE, WAIT_SEND };
|
||||
|
||||
// Wait for output, optionally draining through the final marker.
|
||||
static int wait_data(const char *host, enum wait_mode mode) {
|
||||
@@ -117,6 +117,13 @@ static int wait_data(const char *host, enum wait_mode mode) {
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
if (mode == WAIT_SEND) {
|
||||
usleep(100000); // Let the server shut down its read side
|
||||
n = send(fd, "x", 1, 0);
|
||||
usleep(500000); // Keep peer connected for server SHUT_WR
|
||||
close(fd);
|
||||
return n == 1 ? 0 : 1;
|
||||
}
|
||||
if (mode == WAIT_CLOSE) {
|
||||
n = recv(fd, buf, sizeof(buf), 0);
|
||||
close(fd);
|
||||
@@ -151,10 +158,12 @@ int main(int argc, char *argv[]) {
|
||||
return wait_data(argv[1], WAIT_HOLD);
|
||||
if (argc == 3 && strcmp(argv[2], "expectclose") == 0)
|
||||
return wait_data(argv[1], WAIT_CLOSE);
|
||||
if (argc == 3 && strcmp(argv[2], "sendlater") == 0)
|
||||
return wait_data(argv[1], WAIT_SEND);
|
||||
if (argc == 3) connections = atoi(argv[2]);
|
||||
if ((argc != 2 && argc != 3) || connections < 1 ||
|
||||
connections > NUM_CONNECTIONS) {
|
||||
fprintf(stderr, "usage: %s IP [connections|waitandabort|waitanddrain|hold|expectclose]\n",
|
||||
fprintf(stderr, "usage: %s IP [connections|waitandabort|waitanddrain|hold|expectclose|sendlater]\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
+139
-7
@@ -91,6 +91,22 @@ static void wait_ready(struct mg_mgr *mgr) {
|
||||
MG_INFO(("Interface started, starting main loop"));
|
||||
}
|
||||
|
||||
static void test_inet(void) {
|
||||
struct in_addr addr;
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
char *str;
|
||||
|
||||
ASSERT(inet_pton(AF_INET, "192.0.2.1", &addr) == 1);
|
||||
ASSERT(inet_ntop(AF_INET, &addr, buf, sizeof(buf)) != NULL);
|
||||
ASSERT(strcmp(buf, "192.0.2.1") == 0);
|
||||
str = inet_ntoa(addr);
|
||||
ASSERT(str != NULL);
|
||||
ASSERT(strcmp(str, "192.0.2.1") == 0);
|
||||
ASSERT(inet_addr("192.0.2.1") == addr.s_addr);
|
||||
ASSERT(inet_pton(AF_INET, "not-an-ip", &addr) == 0);
|
||||
ASSERT(inet_pton(AF_INET6, "::1", &addr) < 0 && errno == EAFNOSUPPORT);
|
||||
}
|
||||
|
||||
#if !MG_ENABLE_FREERTOS
|
||||
static bool s_fail_alloc;
|
||||
|
||||
@@ -132,9 +148,10 @@ static volatile bool s_ready_waiting, s_ready_done;
|
||||
static volatile bool s_backlog_ready, s_backlog_full, s_backlog_accept;
|
||||
static volatile bool s_tx_held, s_tx_waits, s_tx_fail, s_tx_error;
|
||||
static volatile bool s_send_waiting;
|
||||
static volatile bool s_send_done, s_send_expect_error;
|
||||
static volatile bool s_send_done, s_send_expect_error, s_send_expect_shutdown;
|
||||
static volatile bool s_recv_start, s_recv_waiting, s_recv_done;
|
||||
static volatile bool s_recv_expect_error;
|
||||
static volatile bool s_shutdown_waiting, s_shutdown_done;
|
||||
static size_t (*s_tx)(const void *, size_t, struct mg_tcpip_if *);
|
||||
|
||||
struct client_data {
|
||||
@@ -152,6 +169,7 @@ static void select_task(void *args);
|
||||
static void readiness_task(void *args);
|
||||
static void backlog_task(void *args);
|
||||
static void tx_gate_task(void *args);
|
||||
static void shutdown_task(void *args);
|
||||
|
||||
struct atask_args {
|
||||
bool listener_nonblock;
|
||||
@@ -300,8 +318,13 @@ static void tx_gate_task(void *args) {
|
||||
// receiver/sender > network task > gate: final send is waiting
|
||||
while (!s_send_waiting) vTaskDelay(1);
|
||||
ASSERT(!s_send_done);
|
||||
if (s_send_expect_error) s_tx_error = s_tx_fail = true;
|
||||
else s_tx_held = false;
|
||||
if (s_send_expect_shutdown) {
|
||||
ASSERT(shutdown((int) (uintptr_t) args, SHUT_WR) == 0);
|
||||
} else if (s_send_expect_error) {
|
||||
s_tx_error = s_tx_fail = true;
|
||||
} else {
|
||||
s_tx_held = false;
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
(void) args;
|
||||
}
|
||||
@@ -342,15 +365,19 @@ static void send_task(void *args) {
|
||||
n = send(fd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
ASSERT(n < 0 && errno == EAGAIN); // Same full c->send: MSG_DONTWAIT
|
||||
|
||||
if (!s_send_expect_error) {
|
||||
if (!s_send_expect_error && !s_send_expect_shutdown) {
|
||||
s_recv_start = true; // O_NONBLOCK is per socket; now start the receiver
|
||||
while (!s_recv_waiting) vTaskDelay(1);
|
||||
}
|
||||
s_send_waiting = true; // Gate task releases TX after this send blocks
|
||||
n = send(fd, &marker, sizeof(marker), 0);
|
||||
if (s_send_expect_error) ASSERT(n < 0 && errno == EPIPE);
|
||||
else ASSERT(n == (ssize_t) sizeof(marker));
|
||||
if (s_send_expect_error || s_send_expect_shutdown) {
|
||||
ASSERT(n < 0 && errno == EPIPE);
|
||||
} else {
|
||||
ASSERT(n == (ssize_t) sizeof(marker));
|
||||
}
|
||||
s_send_done = true;
|
||||
if (s_send_expect_shutdown) close(fd);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
@@ -388,7 +415,7 @@ static void atask(void *args) {
|
||||
ASSERT(result == pdPASS);
|
||||
}
|
||||
if (cfg->gate != NULL) {
|
||||
result = xTaskCreate(cfg->gate, "gate", 256, NULL,
|
||||
result = xTaskCreate(cfg->gate, "gate", 256, (void *) (uintptr_t) fd,
|
||||
configMAX_PRIORITIES - 3, NULL);
|
||||
ASSERT(result == pdPASS);
|
||||
}
|
||||
@@ -514,6 +541,59 @@ static void backlog_task(void *args) {
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void shutdown_task(void *args) {
|
||||
struct sockaddr_in local, peer;
|
||||
struct timeval zero = {0, 0};
|
||||
struct pollfd p;
|
||||
fd_set e;
|
||||
socklen_t len;
|
||||
char buf;
|
||||
int fd, lfd;
|
||||
|
||||
(void) args;
|
||||
lfd = open_listener(5, true);
|
||||
s_shutdown_waiting = true;
|
||||
while ((fd = accept(lfd, NULL, NULL)) < 0) {
|
||||
ASSERT(errno == EAGAIN);
|
||||
vTaskDelay(1);
|
||||
}
|
||||
len = sizeof(local);
|
||||
ASSERT(getsockname(lfd, (struct sockaddr *) &local, &len) == 0);
|
||||
ASSERT(len == sizeof(local) && local.sin_family == AF_INET &&
|
||||
local.sin_port == htons(1234) &&
|
||||
local.sin_addr.s_addr == INADDR_ANY);
|
||||
len = sizeof(peer);
|
||||
ASSERT(getpeername(lfd, (struct sockaddr *) &peer, &len) < 0 &&
|
||||
errno == ENOTCONN);
|
||||
len = sizeof(local);
|
||||
ASSERT(getsockname(fd, (struct sockaddr *) &local, &len) == 0);
|
||||
ASSERT(len == sizeof(local) && local.sin_family == AF_INET &&
|
||||
local.sin_port == htons(1234));
|
||||
len = sizeof(peer);
|
||||
ASSERT(getpeername(fd, (struct sockaddr *) &peer, &len) == 0);
|
||||
ASSERT(len == sizeof(peer) && peer.sin_family == AF_INET &&
|
||||
peer.sin_port != 0);
|
||||
ASSERT(shutdown(lfd, SHUT_RDWR) < 0 && errno == ENOTCONN);
|
||||
ASSERT(shutdown(fd, -1) < 0 && errno == EINVAL);
|
||||
ASSERT(shutdown(fd, SHUT_RD) == 0);
|
||||
ASSERT(recv(fd, &buf, sizeof(buf), 0) == 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(200)); // Delayed client write must be discarded
|
||||
ASSERT(recv(fd, &buf, sizeof(buf), 0) == 0);
|
||||
ASSERT(shutdown(fd, SHUT_WR) == 0);
|
||||
ASSERT(send(fd, &buf, sizeof(buf), 0) < 0 && errno == EPIPE);
|
||||
FD_ZERO(&e);
|
||||
FD_SET(fd, &e);
|
||||
ASSERT(select(fd + 1, NULL, NULL, &e, &zero) == 1);
|
||||
ASSERT(FD_ISSET(fd, &e));
|
||||
p.fd = fd, p.events = POLLOUT, p.revents = 0;
|
||||
ASSERT(poll(&p, 1, 0) == 1);
|
||||
ASSERT(p.revents == POLLERR);
|
||||
ASSERT(close(fd) == 0);
|
||||
ASSERT(close(lfd) == 0);
|
||||
s_shutdown_done = true;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
// task2: tests owner.
|
||||
static void task2(void *args) {
|
||||
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
||||
@@ -555,6 +635,16 @@ static void task2(void *args) {
|
||||
DASHBOARD("select_child_write");
|
||||
DASHBOARD("poll_child_write");
|
||||
|
||||
s_error = false;
|
||||
s_shutdown_waiting = s_shutdown_done = false;
|
||||
ASSERT(xTaskCreate(shutdown_task, "shutdown", 512, NULL,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
while (!s_shutdown_waiting) vTaskDelay(1);
|
||||
run_client(ip, "sendlater");
|
||||
while (!s_shutdown_done) vTaskDelay(1);
|
||||
DASHBOARD("shutdown_read");
|
||||
DASHBOARD("shutdown_write");
|
||||
|
||||
s_error = false;
|
||||
s_backlog_ready = s_backlog_full = s_backlog_accept = false;
|
||||
ASSERT(xTaskCreate(backlog_task, "backlog", 512, NULL,
|
||||
@@ -669,12 +759,27 @@ static void task2(void *args) {
|
||||
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
s_tx_held = s_tx_fail = s_tx_error = s_send_expect_error = false;
|
||||
|
||||
s_error = false;
|
||||
s_wait = s_tx_held = s_tx_waits = s_tx_fail = s_tx_error = false;
|
||||
s_send_waiting = s_send_done = s_send_expect_error = false;
|
||||
s_send_expect_shutdown = true;
|
||||
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_send_error_server,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
run_client(ip, "hold");
|
||||
ASSERT(s_send_done);
|
||||
DASHBOARD("shutdown_send_wakeup");
|
||||
s_wait = true;
|
||||
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
s_tx_held = s_send_expect_shutdown = false;
|
||||
|
||||
mg_free(ip);
|
||||
|
||||
s_error = false;
|
||||
{
|
||||
fd_set e;
|
||||
struct pollfd p;
|
||||
socklen_t len;
|
||||
int err;
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
struct sockaddr_in sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
@@ -694,6 +799,12 @@ static void task2(void *args) {
|
||||
ASSERT(poll(&p, 1, 0) == 1);
|
||||
ASSERT((p.revents & POLLERR) != 0);
|
||||
ASSERT((p.revents & POLLOUT) == 0);
|
||||
len = sizeof(err);
|
||||
ASSERT(getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len) == 0);
|
||||
ASSERT(len == sizeof(err) && err == EIO);
|
||||
len = sizeof(err), err = -1;
|
||||
ASSERT(getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len) == 0);
|
||||
ASSERT(len == sizeof(err) && err == 0);
|
||||
ASSERT(close(fd) == 0);
|
||||
}
|
||||
DASHBOARD("select_error");
|
||||
@@ -943,6 +1054,25 @@ static void nortos_test(struct mg_mgr *mgr) {
|
||||
DASHBOARD("nortos_poll_children");
|
||||
DASHBOARD("nortos_echo");
|
||||
|
||||
lfd = open_listener(5, true);
|
||||
pid = start_client(ip, "hold");
|
||||
fd = nortos_accept(mgr, lfd);
|
||||
ASSERT(shutdown(fd, SHUT_RD) == 0);
|
||||
ASSERT(recv(fd, &buf, sizeof(buf), 0) == 0);
|
||||
ASSERT(shutdown(fd, SHUT_WR) == 0);
|
||||
ASSERT(send(fd, &buf, sizeof(buf), 0) < 0 && errno == EPIPE);
|
||||
FD_ZERO(&e);
|
||||
FD_SET(fd, &e);
|
||||
ASSERT(select(fd + 1, NULL, NULL, &e, &zero) == 1);
|
||||
ASSERT(FD_ISSET(fd, &e));
|
||||
p.fd = fd, p.events = POLLOUT, p.revents = 0;
|
||||
ASSERT(poll(&p, 1, 0) == 1);
|
||||
ASSERT(p.revents == POLLERR);
|
||||
nortos_wait_client(mgr, pid);
|
||||
ASSERT(close(fd) == 0);
|
||||
ASSERT(close(lfd) == 0);
|
||||
DASHBOARD("nortos_shutdown");
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
@@ -983,6 +1113,8 @@ static bool bsd_x_test(struct mg_tcpip_driver *driver, void *driver_data, const
|
||||
|
||||
mg_tcpip_init(&mgr, &mif);
|
||||
ASSERT(mgr.ifp != NULL); // driver init succeeded
|
||||
test_inet();
|
||||
DASHBOARD("inet");
|
||||
MG_INFO(("Init done, starting interface"));
|
||||
usleep(200000); // 200 ms
|
||||
|
||||
|
||||
Reference in new issue
Block a user