| #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 = 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) |
| { |
| int 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 vecotrs */ |
| |
| /* 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 == 6); |
| 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 */ |