diff options
Diffstat (limited to 'test/unit/api/test_sockets.c')
-rw-r--r-- | test/unit/api/test_sockets.c | 852 |
1 files changed, 852 insertions, 0 deletions
diff --git a/test/unit/api/test_sockets.c b/test/unit/api/test_sockets.c new file mode 100644 index 00000000000..3faa5ff9f18 --- /dev/null +++ b/test/unit/api/test_sockets.c @@ -0,0 +1,852 @@ +#include "test_sockets.h" + +#include "lwip/mem.h" +#include "lwip/opt.h" +#include "lwip/sockets.h" +#include "lwip/priv/sockets_priv.h" +#include "lwip/stats.h" + +#include "lwip/tcpip.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/api.h" + + +static int +test_sockets_get_used_count(void) +{ + int used = 0; + int i; + + for (i = 0; i < NUM_SOCKETS; i++) { + struct lwip_sock* s = lwip_socket_dbg_get_socket(i); + if (s != NULL) { + if (s->fd_used) { + used++; + } + } + } + return used; +} + + +/* Setups/teardown functions */ + +static void +sockets_setup(void) +{ + /* expect full free heap */ + lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT)); +} + +static void +sockets_teardown(void) +{ + fail_unless(test_sockets_get_used_count() == 0); + /* poll until all memory is released... */ + tcpip_thread_poll_one(); + while (tcp_tw_pcbs) { + tcp_abort(tcp_tw_pcbs); + tcpip_thread_poll_one(); + } + tcpip_thread_poll_one(); + /* ensure full free heap */ + lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT)); +} + +#ifndef NUM_SOCKETS +#define NUM_SOCKETS MEMP_NUM_NETCONN +#endif + +#if LWIP_SOCKET +static int +test_sockets_alloc_socket_nonblocking(int domain, int type) +{ + int s = lwip_socket(domain, type, 0); + if (s >= 0) { + int ret = lwip_fcntl(s, F_SETFL, O_NONBLOCK); + fail_unless(ret == 0); + } + return s; +} + +/* Verify basic sockets functionality + */ +START_TEST(test_sockets_basics) +{ + int s, i, ret; + int s2[NUM_SOCKETS]; + LWIP_UNUSED_ARG(_i); + + s = lwip_socket(AF_INET, SOCK_STREAM, 0); + fail_unless(s >= 0); + lwip_close(s); + + for (i = 0; i < NUM_SOCKETS; i++) { + s2[i] = lwip_socket(AF_INET, SOCK_STREAM, 0); + fail_unless(s2[i] >= 0); + } + + /* all sockets used, now it should fail */ + s = lwip_socket(AF_INET, SOCK_STREAM, 0); + fail_unless(s == -1); + /* close one socket */ + ret = lwip_close(s2[0]); + fail_unless(ret == 0); + /* now it should succeed */ + s2[0] = lwip_socket(AF_INET, SOCK_STREAM, 0); + fail_unless(s2[0] >= 0); + + /* close all sockets */ + for (i = 0; i < NUM_SOCKETS; i++) { + ret = lwip_close(s2[i]); + fail_unless(ret == 0); + } +} +END_TEST + +static void test_sockets_allfunctions_basic_domain(int domain) +{ + int s, s2, s3, ret; + struct sockaddr_storage addr, addr2; + socklen_t addrlen, addr2len; + char buf[4]; + /* listen socket */ + s = lwip_socket(domain, SOCK_STREAM, 0); + fail_unless(s >= 0); + + ret = lwip_listen(s, 0); + fail_unless(ret == 0); + + addrlen = sizeof(addr); + ret = lwip_getsockname(s, (struct sockaddr*)&addr, &addrlen); + fail_unless(ret == 0); + + s2 = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM); + fail_unless(s2 >= 0); + /* nonblocking connect s2 to s (but use loopback address) */ + if (domain == AF_INET) { +#if LWIP_IPV4 + struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; + addr4->sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK); +#endif + } else { +#if LWIP_IPV6 + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; + struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT; + addr6->sin6_addr = lo6; +#endif + } + ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen); + fail_unless(ret == -1); + fail_unless(errno == EINPROGRESS); + ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen); + fail_unless(ret == -1); + fail_unless(errno == EALREADY); + + while(tcpip_thread_poll_one()); + + s3 = lwip_accept(s, (struct sockaddr*)&addr2, &addr2len); + fail_unless(s3 >= 0); + + ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen); + fail_unless(ret == -1); + fail_unless(errno == EISCONN); + + /* write from server to client */ + ret = lwip_write(s3, "test", 4); + fail_unless(ret == 4); + + ret = lwip_shutdown(s3, SHUT_WR); + fail_unless(ret == 0); + + while(tcpip_thread_poll_one()); + + ret = lwip_recv(s2, buf, 3, MSG_PEEK); + fail_unless(ret == 3); + + ret = lwip_recv(s2, buf, 3, MSG_PEEK); + fail_unless(ret == 3); + + ret = lwip_read(s2, buf, 4); + fail_unless(ret == 4); + + ret = lwip_read(s2, buf, 1); + fail_unless(ret == 0); + + ret = lwip_read(s2, buf, 1); + fail_unless(ret == -1); + + ret = lwip_write(s2, "foo", 3); + fail_unless(ret == 3); + + ret = lwip_close(s2); + fail_unless(ret == 0); + + while(tcpip_thread_poll_one()); + + /* read one byte more than available to check handling FIN */ + ret = lwip_read(s3, buf, 4); + fail_unless(ret == 3); + + ret = lwip_read(s3, buf, 1); + fail_unless(ret == 0); + + ret = lwip_read(s3, buf, 1); + fail_unless(ret == -1); + + while(tcpip_thread_poll_one()); + + ret = lwip_close(s); + fail_unless(ret == 0); + ret = lwip_close(s3); + fail_unless(ret == 0); +} + +/* Try to step through all sockets functions once... + */ +START_TEST(test_sockets_allfunctions_basic) +{ + LWIP_UNUSED_ARG(_i); +#if LWIP_IPV4 + test_sockets_allfunctions_basic_domain(AF_INET); +#endif +#if LWIP_IPV6 + test_sockets_allfunctions_basic_domain(AF_INET6); +#endif +} +END_TEST + +static void test_sockets_init_loopback_addr(int domain, struct sockaddr_storage *addr_st, socklen_t *sz) +{ + memset(addr_st, 0, sizeof(*addr_st)); + switch(domain) { +#if LWIP_IPV6 + case AF_INET6: { + struct sockaddr_in6 *addr = (struct sockaddr_in6*)addr_st; + struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT; + addr->sin6_family = AF_INET6; + addr->sin6_port = 0; /* use ephemeral port */ + addr->sin6_addr = lo6; + *sz = sizeof(*addr); + } + break; +#endif /* LWIP_IPV6 */ +#if LWIP_IPV4 + case AF_INET: { + struct sockaddr_in *addr = (struct sockaddr_in*)addr_st; + addr->sin_family = AF_INET; + addr->sin_port = 0; /* use ephemeral port */ + addr->sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK); + *sz = sizeof(*addr); + } + break; +#endif /* LWIP_IPV4 */ + default: + *sz = 0; + fail(); + break; + } +} + +static void test_sockets_msgapi_update_iovs(struct msghdr *msg, size_t bytes) +{ + msg_iovlen_t i; + + /* note: this modifies the underyling iov_base and iov_len for a partial + read for an individual vector. This updates the msg->msg_iov pointer + to skip fully consumed vectors */ + + /* process fully consumed vectors */ + for (i = 0; i < msg->msg_iovlen; i++) { + if (msg->msg_iov[i].iov_len <= bytes) { + /* reduce bytes by amount of this vector */ + bytes -= msg->msg_iov[i].iov_len; + } else { + break; /* iov not fully consumed */ + } + } + + /* slide down over fully consumed vectors */ + msg->msg_iov = &msg->msg_iov[i]; + msg->msg_iovlen -= i; + + /* update new first vector with any remaining amount */ + msg->msg_iov[0].iov_base = ((u8_t *)msg->msg_iov[0].iov_base + bytes); + msg->msg_iov[0].iov_len -= bytes; +} + +static void test_sockets_msgapi_tcp(int domain) +{ + #define BUF_SZ (TCP_SND_BUF/4) + #define TOTAL_DATA_SZ (BUF_SZ*8) /* ~(TCP_SND_BUF*2) that accounts for integer rounding */ + #define NEED_TRAILER (BUF_SZ % 4 != 0) + int listnr, s1, s2, i, ret, opt; + int bytes_written, bytes_read; + struct sockaddr_storage addr_storage; + socklen_t addr_size; + struct iovec siovs[8]; + struct msghdr smsg; + u8_t * snd_buf; + struct iovec riovs[5]; + struct iovec riovs_tmp[5]; + struct msghdr rmsg; + u8_t * rcv_buf; + int rcv_off; + int rcv_trailer = 0; + u8_t val; + + test_sockets_init_loopback_addr(domain, &addr_storage, &addr_size); + + listnr = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM); + fail_unless(listnr >= 0); + s1 = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM); + fail_unless(s1 >= 0); + + /* setup a listener socket on loopback with ephemeral port */ + ret = lwip_bind(listnr, (struct sockaddr*)&addr_storage, addr_size); + fail_unless(ret == 0); + ret = lwip_listen(listnr, 0); + fail_unless(ret == 0); + + /* update address with ephemeral port */ + ret = lwip_getsockname(listnr, (struct sockaddr*)&addr_storage, &addr_size); + fail_unless(ret == 0); + + /* connect, won't complete until we accept it */ + ret = lwip_connect(s1, (struct sockaddr*)&addr_storage, addr_size); + fail_unless(ret == -1); + fail_unless(errno == EINPROGRESS); + + while (tcpip_thread_poll_one()); + + /* accept, creating the other side of the connection */ + s2 = lwip_accept(listnr, NULL, NULL); + fail_unless(s2 >= 0); + + /* double check s1 is connected */ + ret = lwip_connect(s1, (struct sockaddr*)&addr_storage, addr_size); + fail_unless(ret == -1); + fail_unless(errno == EISCONN); + + /* set s2 to non-blocking, not inherited from listener */ + opt = lwip_fcntl(s2, F_GETFL, 0); + fail_unless(opt == O_RDWR); + opt = O_NONBLOCK; + ret = lwip_fcntl(s2, F_SETFL, opt); + fail_unless(ret == 0); + + /* we are done with listener, close it */ + ret = lwip_close(listnr); + fail_unless(ret == 0); + + /* allocate a buffer for a stream of incrementing hex (0x00..0xFF) which we will use + to create an input vector set that is larger than the TCP's send buffer. This will + force execution of the partial IO vector send case */ + snd_buf = (u8_t*)mem_malloc(BUF_SZ); + val = 0x00; + fail_unless(snd_buf != NULL); + for (i = 0; i < BUF_SZ; i++,val++) { + snd_buf[i] = val; + } + + /* send the buffer 8 times in one message, equating to TOTAL_DATA_SZ */ + for (i = 0; i < 8; i++) { + siovs[i].iov_base = snd_buf; + siovs[i].iov_len = BUF_SZ; + } + + /* allocate a receive buffer, same size as snd_buf for easy verification */ + rcv_buf = (u8_t*)mem_calloc(1, BUF_SZ); + fail_unless(rcv_buf != NULL); + /* split across iovs */ + for (i = 0; i < 4; i++) { + riovs[i].iov_base = &rcv_buf[i*(BUF_SZ/4)]; + riovs[i].iov_len = BUF_SZ/4; + } + /* handling trailing bytes if buffer doesn't evenly divide by 4 */ +#if NEED_TRAILER + if ((BUF_SZ % 4) != 0) { + riovs[5].iov_base = &rcv_buf[4*(BUF_SZ/4)]; + riovs[5].iov_len = BUF_SZ - (4*(BUF_SZ/4)); + rcv_trailer = 1; + } +#endif /* NEED_TRAILER */ + + /* we use a copy of riovs since we'll be modifying base and len during + receiving. This gives us an easy way to reset the iovs for next recvmsg */ + memcpy(riovs_tmp, riovs, sizeof(riovs)); + + memset(&smsg, 0, sizeof(smsg)); + smsg.msg_iov = siovs; + smsg.msg_iovlen = 8; + + memset(&rmsg, 0, sizeof(rmsg)); + rmsg.msg_iov = riovs_tmp; + rmsg.msg_iovlen = (rcv_trailer ? 5 : 4); + + bytes_written = 0; + bytes_read = 0; + rcv_off = 0; + + while (bytes_written < TOTAL_DATA_SZ && (bytes_read < TOTAL_DATA_SZ)) { + /* send data */ + if (bytes_written < TOTAL_DATA_SZ) { + ret = lwip_sendmsg(s1, &smsg, 0); + /* note: since we always receive after sending, there will be open + space in the send buffer */ + fail_unless(ret > 0); + + bytes_written += ret; + if (bytes_written < TOTAL_DATA_SZ) { + test_sockets_msgapi_update_iovs(&smsg, (size_t)ret); + } + } + + while (tcpip_thread_poll_one()); + + /* receive and verify data */ + do { + if (bytes_read < TOTAL_DATA_SZ) { + ret = lwip_recvmsg(s2, &rmsg, 0); + fail_unless(ret > 0 || (ret == -1 && errno == EWOULDBLOCK)); + + if (ret > 0) { + rcv_off += ret; + /* we have received a full buffer */ + if (rcv_off == BUF_SZ) { + /* note: since iovs are just pointers, compare underlying buf */ + fail_unless(!memcmp(snd_buf, rcv_buf, BUF_SZ)); + bytes_read += BUF_SZ; + /* reset receive state for next buffer */ + rcv_off = 0; + memset(rcv_buf, 0, BUF_SZ); + memcpy(riovs_tmp, riovs, sizeof(riovs)); + rmsg.msg_iov = riovs_tmp; + rmsg.msg_iovlen = (rcv_trailer ? 5 : 4); + } else { /* partial read */ + test_sockets_msgapi_update_iovs(&rmsg, (size_t)ret); + } + } + } else { + break; + } + } while(ret > 0); + } + + ret = lwip_close(s1); + fail_unless(ret == 0); + ret = lwip_close(s2); + fail_unless(ret == 0); + mem_free(snd_buf); + mem_free(rcv_buf); +} + +static void test_sockets_msgapi_udp_send_recv_loop(int s, struct msghdr *smsg, struct msghdr *rmsg) +{ + int i, ret; + + /* send/receive our datagram of IO vectors 10 times */ + for (i = 0; i < 10; i++) { + ret = lwip_sendmsg(s, smsg, 0); + fail_unless(ret == 4); + + while (tcpip_thread_poll_one()); + + /* receive the datagram split across 4 buffers */ + ret = lwip_recvmsg(s, rmsg, 0); + fail_unless(ret == 4); + + /* verify data */ + fail_unless(*((u8_t*)rmsg->msg_iov[0].iov_base) == 0xDE); + fail_unless(*((u8_t*)rmsg->msg_iov[1].iov_base) == 0xAD); + fail_unless(*((u8_t*)rmsg->msg_iov[2].iov_base) == 0xBE); + fail_unless(*((u8_t*)rmsg->msg_iov[3].iov_base) == 0xEF); + + /* clear rcv_buf to ensure no data is being skipped */ + *((u8_t*)rmsg->msg_iov[0].iov_base) = 0x00; + *((u8_t*)rmsg->msg_iov[1].iov_base) = 0x00; + *((u8_t*)rmsg->msg_iov[2].iov_base) = 0x00; + *((u8_t*)rmsg->msg_iov[3].iov_base) = 0x00; + } +} + +static void test_sockets_msgapi_udp(int domain) +{ + int s, i, ret; + struct sockaddr_storage addr_storage; + socklen_t addr_size; + struct iovec riovs[4]; + struct msghdr rmsg; + u8_t rcv_buf[4]; + struct iovec siovs[4]; + struct msghdr smsg; + u8_t snd_buf[4] = {0xDE, 0xAD, 0xBE, 0xEF}; + + /* initialize IO vectors with data */ + for (i = 0; i < 4; i++) { + siovs[i].iov_base = &snd_buf[i]; + siovs[i].iov_len = sizeof(u8_t); + riovs[i].iov_base = &rcv_buf[i]; + riovs[i].iov_len = sizeof(u8_t); + } + + test_sockets_init_loopback_addr(domain, &addr_storage, &addr_size); + + s = test_sockets_alloc_socket_nonblocking(domain, SOCK_DGRAM); + fail_unless(s >= 0); + + ret = lwip_bind(s, (struct sockaddr*)&addr_storage, addr_size); + fail_unless(ret == 0); + + /* Update addr with epehermal port */ + ret = lwip_getsockname(s, (struct sockaddr*)&addr_storage, &addr_size); + fail_unless(ret == 0); + switch(domain) { +#if LWIP_IPV6 + case AF_INET6: + fail_unless(addr_size == sizeof(struct sockaddr_in6)); + break; +#endif /* LWIP_IPV6 */ +#if LWIP_IPV4 + case AF_INET: + fail_unless(addr_size == sizeof(struct sockaddr_in)); + break; +#endif /* LWIP_IPV6 */ + default: + fail(); + break; + } + + /* send and receive the datagram in 4 pieces */ + memset(&smsg, 0, sizeof(smsg)); + smsg.msg_iov = siovs; + smsg.msg_iovlen = 4; + memset(&rmsg, 0, sizeof(rmsg)); + rmsg.msg_iov = riovs; + rmsg.msg_iovlen = 4; + + /* perform a sendmsg with remote host (self) */ + smsg.msg_name = &addr_storage; + smsg.msg_namelen = addr_size; + + test_sockets_msgapi_udp_send_recv_loop(s, &smsg, &rmsg); + + /* Connect to self, allowing us to not pass message name */ + ret = lwip_connect(s, (struct sockaddr*)&addr_storage, addr_size); + fail_unless(ret == 0); + + smsg.msg_name = NULL; + smsg.msg_namelen = 0; + + test_sockets_msgapi_udp_send_recv_loop(s, &smsg, &rmsg); + + ret = lwip_close(s); + fail_unless(ret == 0); +} + +#if LWIP_IPV4 +static void test_sockets_msgapi_cmsg(int domain) +{ + int s, ret, enable; + struct sockaddr_storage addr_storage; + socklen_t addr_size; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + struct in_pktinfo *pktinfo; + u8_t rcv_buf[4]; + u8_t snd_buf[4] = {0xDE, 0xAD, 0xBE, 0xEF}; + u8_t cmsg_buf[CMSG_SPACE(sizeof(struct in_pktinfo))]; + + test_sockets_init_loopback_addr(domain, &addr_storage, &addr_size); + + s = test_sockets_alloc_socket_nonblocking(domain, SOCK_DGRAM); + fail_unless(s >= 0); + + ret = lwip_bind(s, (struct sockaddr*)&addr_storage, addr_size); + fail_unless(ret == 0); + + /* Update addr with epehermal port */ + ret = lwip_getsockname(s, (struct sockaddr*)&addr_storage, &addr_size); + fail_unless(ret == 0); + + enable = 1; + ret = lwip_setsockopt(s, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable)); + fail_unless(ret == 0); + + /* Receive full message, including control message */ + iov.iov_base = rcv_buf; + iov.iov_len = sizeof(rcv_buf); + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof(cmsg_buf); + msg.msg_flags = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + + memset(rcv_buf, 0, sizeof(rcv_buf)); + ret = lwip_sendto(s, snd_buf, sizeof(snd_buf), 0, (struct sockaddr*)&addr_storage, addr_size); + fail_unless(ret == sizeof(snd_buf)); + + tcpip_thread_poll_one(); + + ret = lwip_recvmsg(s, &msg, 0); + fail_unless(ret == sizeof(rcv_buf)); + fail_unless(!memcmp(rcv_buf, snd_buf, sizeof(rcv_buf))); + + /* Verify message header */ + cmsg = CMSG_FIRSTHDR(&msg); + fail_unless(cmsg != NULL); + fail_unless(cmsg->cmsg_len > 0); + fail_unless(cmsg->cmsg_level == IPPROTO_IP); + fail_unless(cmsg->cmsg_type == IP_PKTINFO); + + /* Verify message data */ + pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg); + /* We only have loopback interface enabled */ + fail_unless(pktinfo->ipi_ifindex == 1); + fail_unless(pktinfo->ipi_addr.s_addr == PP_HTONL(INADDR_LOOPBACK)); + + /* Verify there are no additional messages */ + cmsg = CMSG_NXTHDR(&msg, cmsg); + fail_unless(cmsg == NULL); + + /* Send datagram again, testing truncation */ + memset(rcv_buf, 0, sizeof(rcv_buf)); + ret = lwip_sendto(s, snd_buf, sizeof(snd_buf), 0, (struct sockaddr*)&addr_storage, addr_size); + fail_unless(ret == sizeof(snd_buf)); + + tcpip_thread_poll_one(); + + msg.msg_controllen = 1; + msg.msg_flags = 0; + ret = lwip_recvmsg(s, &msg, 0); + fail_unless(ret == sizeof(rcv_buf)); + fail_unless(!memcmp(rcv_buf, snd_buf, sizeof(rcv_buf))); + /* Ensure truncation was returned */ + fail_unless(msg.msg_flags & MSG_CTRUNC); + /* Ensure no control messages were returned */ + fail_unless(msg.msg_controllen == 0); + + ret = lwip_close(s); + fail_unless(ret == 0); +} +#endif /* LWIP_IPV4 */ + +START_TEST(test_sockets_msgapis) +{ + LWIP_UNUSED_ARG(_i); +#if LWIP_IPV4 + test_sockets_msgapi_udp(AF_INET); + test_sockets_msgapi_tcp(AF_INET); + test_sockets_msgapi_cmsg(AF_INET); +#endif +#if LWIP_IPV6 + test_sockets_msgapi_udp(AF_INET6); + test_sockets_msgapi_tcp(AF_INET6); +#endif +} +END_TEST + +START_TEST(test_sockets_select) +{ +#if LWIP_SOCKET_SELECT + int s; + int ret; + fd_set readset; + fd_set writeset; + fd_set errset; + struct timeval tv; + + fail_unless(test_sockets_get_used_count() == 0); + + s = lwip_socket(AF_INET, SOCK_STREAM, 0); + fail_unless(s >= 0); + fail_unless(test_sockets_get_used_count() == 0); + + FD_ZERO(&readset); + FD_SET(s, &readset); + FD_ZERO(&writeset); + FD_SET(s, &writeset); + FD_ZERO(&errset); + FD_SET(s, &errset); + + tv.tv_sec = tv.tv_usec = 0; + ret = lwip_select(s + 1, &readset, &writeset, &errset, &tv); + fail_unless(ret == 0); + fail_unless(test_sockets_get_used_count() == 0); + + ret = lwip_close(s); + fail_unless(ret == 0); + +#endif + LWIP_UNUSED_ARG(_i); +} +END_TEST + +START_TEST(test_sockets_recv_after_rst) +{ + int sl, sact; + int spass = -1; + int ret; + struct sockaddr_in sa_listen; + const u16_t port = 1234; + int arg; + const char txbuf[] = "something"; + char rxbuf[16]; + struct lwip_sock *sact_sock; + int err; + LWIP_UNUSED_ARG(_i); + + fail_unless(test_sockets_get_used_count() == 0); + + memset(&sa_listen, 0, sizeof(sa_listen)); + sa_listen.sin_family = AF_INET; + sa_listen.sin_port = PP_HTONS(port); + sa_listen.sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK); + + /* set up the listener */ + sl = lwip_socket(AF_INET, SOCK_STREAM, 0); + fail_unless(sl >= 0); + fail_unless(test_sockets_get_used_count() == 0); + + ret = lwip_bind(sl, (struct sockaddr *)&sa_listen, sizeof(sa_listen)); + fail_unless(ret == 0); + ret = lwip_listen(sl, 0); + fail_unless(ret == 0); + + /* set up the client */ + sact = lwip_socket(AF_INET, SOCK_STREAM, 0); + fail_unless(sact >= 0); + fail_unless(test_sockets_get_used_count() == 0); + /* set the client to nonblocking to simplify this test */ + arg = 1; + ret = lwip_ioctl(sact, FIONBIO, &arg); + fail_unless(ret == 0); + /* connect */ + do { + ret = lwip_connect(sact, (struct sockaddr *)&sa_listen, sizeof(sa_listen)); + err = errno; + fail_unless((ret == 0) || (ret == -1)); + if (ret != 0) { + if (err == EISCONN) { + /* Although this is not valid, use EISCONN as an indicator for successful connection. + This marks us as "connect phase is done". On error, we would either have a different + errno code or "send" fails later... -> good enough for this test. */ + ret = 0; + } else { + fail_unless(err == EINPROGRESS); + if (err != EINPROGRESS) { + goto cleanup; + } + /* we're in progress: little side check: test for EALREADY */ + ret = lwip_connect(sact, (struct sockaddr *)&sa_listen, sizeof(sa_listen)); + err = errno; + fail_unless(ret == -1); + fail_unless(err == EALREADY); + if ((ret != -1) || (err != EALREADY)) { + goto cleanup; + } + } + tcpip_thread_poll_one(); + tcpip_thread_poll_one(); + tcpip_thread_poll_one(); + tcpip_thread_poll_one(); + } + } while (ret != 0); + fail_unless(ret == 0); + + /* accept the server connection part */ + spass = lwip_accept(sl, NULL, NULL); + fail_unless(spass >= 0); + + /* write data from client */ + ret = lwip_send(sact, txbuf, sizeof(txbuf), 0); + fail_unless(ret == sizeof(txbuf)); + + tcpip_thread_poll_one(); + tcpip_thread_poll_one(); + + /* issue RST (This is a HACK, don't try this in your own app!) */ + sact_sock = lwip_socket_dbg_get_socket(sact); + fail_unless(sact_sock != NULL); + if (sact_sock != NULL) { + struct netconn *sact_conn = sact_sock->conn; + fail_unless(sact_conn != NULL); + if (sact_conn != NULL) { + struct tcp_pcb *pcb = sact_conn->pcb.tcp; + fail_unless(pcb != NULL); + if (pcb != NULL) { + tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port); + } + } + } + tcpip_thread_poll_one(); + tcpip_thread_poll_one(); + + /* expect to receive data first */ + ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0); + fail_unless(ret > 0); + tcpip_thread_poll_one(); + tcpip_thread_poll_one(); + + /* expect to receive RST indication */ + ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0); + fail_unless(ret == -1); + err = errno; + fail_unless(err == ECONNRESET); + tcpip_thread_poll_one(); + tcpip_thread_poll_one(); + + /* expect to receive ENOTCONN indication */ + ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0); + fail_unless(ret == -1); + err = errno; + fail_unless(err == ENOTCONN); + tcpip_thread_poll_one(); + tcpip_thread_poll_one(); + + /* expect to receive ENOTCONN indication */ + ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0); + fail_unless(ret == -1); + err = errno; + fail_unless(err == ENOTCONN); + tcpip_thread_poll_one(); + tcpip_thread_poll_one(); + +cleanup: + ret = lwip_close(sl); + fail_unless(ret == 0); + ret = lwip_close(sact); + fail_unless(ret == 0); + if (spass >= 0) { + ret = lwip_close(spass); + fail_unless(ret == 0); + } +} +END_TEST + +/** Create the suite including all tests for this module */ +Suite * +sockets_suite(void) +{ + testfunc tests[] = { + TESTFUNC(test_sockets_basics), + TESTFUNC(test_sockets_allfunctions_basic), + TESTFUNC(test_sockets_msgapis), + TESTFUNC(test_sockets_select), + TESTFUNC(test_sockets_recv_after_rst), + }; + return create_suite("SOCKETS", tests, sizeof(tests)/sizeof(testfunc), sockets_setup, sockets_teardown); +} + +#else /* LWIP_SOCKET */ + +Suite * +sockets_suite(void) +{ + return create_suite("SOCKETS", NULL, 0, NULL, NULL); +} +#endif /* LWIP_SOCKET */ |