diff --git a/mongoose.c b/mongoose.c index 20426a7a..ef838866 100644 --- a/mongoose.c +++ b/mongoose.c @@ -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 diff --git a/mongoose.h b/mongoose.h index 61bbb632..fb4a9292 100644 --- a/mongoose.h +++ b/mongoose.h @@ -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 diff --git a/src/bsd.c b/src/bsd.c index f301e10d..aeb2e539 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -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 diff --git a/src/bsd.h b/src/bsd.h index 5a01f70c..eaeb9621 100644 --- a/src/bsd.h +++ b/src/bsd.h @@ -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 diff --git a/test/bsd/bsd_client.c b/test/bsd/bsd_client.c index 44ea79e1..57f86974 100644 --- a/test/bsd/bsd_client.c +++ b/test/bsd/bsd_client.c @@ -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; } diff --git a/test/bsd/bsd_x_test.c b/test/bsd/bsd_x_test.c index 7a72f62e..dd8ecf3b 100644 --- a/test/bsd/bsd_x_test.c +++ b/test/bsd/bsd_x_test.c @@ -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