blob: 7a99a2ce74f42fa68d2431c0c9be28c920f6b6d0 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <netinet/in.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <thread>
#include "gtest/gtest.h"
#define DEBUG 0
namespace netstack {
const int32_t kTimeout = 10000; // 10 seconds
extern void NotifySuccess(int ntfyfd);
extern void NotifyFail(int ntfyfd);
extern bool WaitSuccess(int ntfyfd, int timeout);
// DatagramSendtoRecvfrom tests if UDP send automatically binds an ephemeral
// port where the receiver can responds to.
void DatagramReadWrite(int recvfd, int ntfyfd) {
struct pollfd fds = {recvfd, POLLIN, 0};
int nfds = poll(&fds, 1, kTimeout);
EXPECT_EQ(1, nfds) << "poll returned: " << nfds << " errno: " << errno;
if (nfds != 1) {
NotifyFail(ntfyfd);
return;
}
char buf[32];
struct sockaddr_in peer;
socklen_t peerlen = sizeof(peer);
int nbytes = recvfrom(recvfd, buf, sizeof(buf), 0,
reinterpret_cast<struct sockaddr*>(&peer), &peerlen);
EXPECT_GE(nbytes, 0) << "recvfrom failed: " << errno;
if (nbytes < 0) {
NotifyFail(ntfyfd);
return;
}
#if DEBUG
char addrstr[INET_ADDRSTRLEN];
printf("peer.sin_addr: %s\n",
inet_ntop(AF_INET, &peer.sin_addr, addrstr, sizeof(addrstr)));
printf("peer.sin_port: %d\n", ntohs(peer.sin_port));
printf("peerlen: %d\n", peerlen);
#endif
nbytes = sendto(recvfd, buf, nbytes, 0,
reinterpret_cast<struct sockaddr*>(&peer), peerlen);
EXPECT_GE(nbytes, 0) << "sendto failed: " << errno;
if (nbytes < 0) {
NotifyFail(ntfyfd);
return;
}
NotifySuccess(ntfyfd);
}
void DatagramSendtoRecvfrom() {
int recvfd = socket(AF_INET, SOCK_DGRAM, 0);
ASSERT_GE(recvfd, 0);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 0;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
int ret = bind(recvfd, reinterpret_cast<const struct sockaddr*>(&addr),
sizeof(addr));
ASSERT_EQ(0, ret) << "bind failed: " << errno;
socklen_t addrlen = sizeof(addr);
ret =
getsockname(recvfd, reinterpret_cast<struct sockaddr*>(&addr), &addrlen);
ASSERT_EQ(0, ret) << "getsockname failed: " << errno;
#if DEBUG
char addrstr[INET_ADDRSTRLEN];
printf("addr.sin_addr: %s\n",
inet_ntop(AF_INET, &addr.sin_addr, addrstr, sizeof(addrstr)));
printf("addr.sin_port: %d\n", ntohs(addr.sin_port));
printf("addrlen: %d\n", addrlen);
#endif
int ntfyfd[2];
ASSERT_EQ(0, pipe(ntfyfd));
std::thread thrd(DatagramReadWrite, recvfd, ntfyfd[1]);
const char* msg = "hello";
int sendfd = socket(AF_INET, SOCK_DGRAM, 0);
ASSERT_GE(sendfd, 0) << "socket failed: " << errno;
ASSERT_EQ((ssize_t)strlen(msg),
sendto(sendfd, msg, strlen(msg), 0,
reinterpret_cast<struct sockaddr*>(&addr), addrlen))
<< "sendto failed: " << errno;
struct pollfd fds = {sendfd, POLLIN, 0};
int nfds = poll(&fds, 1, kTimeout);
ASSERT_EQ(1, nfds) << "poll returned: " << nfds << " errno: " << errno;
char buf[32];
struct sockaddr_in peer;
socklen_t peerlen = sizeof(peer);
ASSERT_EQ((ssize_t)strlen(msg),
recvfrom(sendfd, buf, sizeof(buf), 0,
reinterpret_cast<struct sockaddr*>(&peer), &peerlen))
<< "recvfrom failed: " << errno;
#if DEBUG
printf("peer.sin_addr[2]: %s\n",
inet_ntop(AF_INET, &peer.sin_addr, addrstr, sizeof(addrstr)));
printf("peer.sin_port[2]: %d\n", ntohs(peer.sin_port));
printf("peerlen[2]: %d\n", peerlen);
#endif
ASSERT_EQ(0, close(sendfd));
ASSERT_EQ(true, WaitSuccess(ntfyfd[0], kTimeout));
thrd.join();
EXPECT_EQ(0, close(recvfd));
EXPECT_EQ(0, close(ntfyfd[0]));
EXPECT_EQ(0, close(ntfyfd[1]));
}
// DatagramSendtoRecvfromV6 tests if UDP send automatically binds an ephemeral
// port where the receiver can responds to.
void DatagramReadWriteV6(int recvfd, int ntfyfd) {
struct pollfd fds = {recvfd, POLLIN, 0};
int nfds = poll(&fds, 1, kTimeout);
EXPECT_EQ(1, nfds) << "poll returned: " << nfds << " errno: " << errno;
if (nfds != 1) {
NotifyFail(ntfyfd);
return;
}
char buf[32];
struct sockaddr_in6 peer;
socklen_t peerlen = sizeof(peer);
int nbytes = recvfrom(recvfd, buf, sizeof(buf), 0,
reinterpret_cast<struct sockaddr*>(&peer), &peerlen);
EXPECT_GE(nbytes, 0) << "recvfrom failed: " << errno;
if (nbytes < 0) {
NotifyFail(ntfyfd);
return;
}
#if DEBUG
char addrstr[INET_ADDRSTRLEN];
printf("peer.sin6_addr: %s\n",
inet_ntop(AF_INET6, &peer.sin6_addr, addrstr, sizeof(addrstr)));
printf("peer.sin6_port: %d\n", ntohs(peer.sin6_port));
printf("peerlen: %d\n", peerlen);
#endif
nbytes = sendto(recvfd, buf, nbytes, 0,
reinterpret_cast<struct sockaddr*>(&peer), peerlen);
EXPECT_GE(nbytes, 0) << "sendto failed: " << errno;
if (nbytes < 0) {
NotifyFail(ntfyfd);
return;
}
NotifySuccess(ntfyfd);
}
void DatagramSendtoRecvfromV6() {
int recvfd = socket(AF_INET6, SOCK_DGRAM, 0);
ASSERT_GE(recvfd, 0);
struct sockaddr_in6 addr;
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = 0;
addr.sin6_addr = in6addr_loopback;
int ret = bind(recvfd, reinterpret_cast<const struct sockaddr*>(&addr),
sizeof(addr));
ASSERT_EQ(0, ret) << "bind failed: " << errno;
socklen_t addrlen = sizeof(addr);
ret =
getsockname(recvfd, reinterpret_cast<struct sockaddr*>(&addr), &addrlen);
ASSERT_EQ(0, ret) << "getsockname failed: " << errno;
#if DEBUG
char addrstr[INET_ADDRSTRLEN];
printf("addr.sin6_addr: %s\n",
inet_ntop(AF_INET6, &addr.sin6_addr, addrstr, sizeof(addrstr)));
printf("addr.sin6_port: %d\n", ntohs(addr.sin6_port));
printf("addrlen: %d\n", addrlen);
#endif
int ntfyfd[2];
ASSERT_EQ(0, pipe(ntfyfd));
std::thread thrd(DatagramReadWriteV6, recvfd, ntfyfd[1]);
const char* msg = "hello";
int sendfd = socket(AF_INET6, SOCK_DGRAM, 0);
ASSERT_GE(sendfd, 0) << "socket failed: " << errno;
ASSERT_EQ((ssize_t)strlen(msg),
sendto(sendfd, msg, strlen(msg), 0,
reinterpret_cast<struct sockaddr*>(&addr), addrlen))
<< "sendto failed: " << errno;
struct pollfd fds = {sendfd, POLLIN, 0};
int nfds = poll(&fds, 1, kTimeout);
ASSERT_EQ(1, nfds) << "poll returned: " << nfds << " errno: " << errno;
char buf[32];
struct sockaddr_in6 peer;
socklen_t peerlen = sizeof(peer);
ASSERT_EQ((ssize_t)strlen(msg),
recvfrom(sendfd, buf, sizeof(buf), 0,
reinterpret_cast<struct sockaddr*>(&peer), &peerlen))
<< "recvfrom failed: " << errno;
#if DEBUG
printf("peer.sin6_addr[2]: %s\n",
inet_ntop(AF_INET6, &peer.sin6_addr, addrstr, sizeof(addrstr)));
printf("peer.sin6_port[2]: %d\n", ntohs(peer.sin6_port));
printf("peerlen[2]: %d\n", peerlen);
#endif
ASSERT_EQ(0, close(sendfd));
ASSERT_EQ(true, WaitSuccess(ntfyfd[0], kTimeout));
thrd.join();
EXPECT_EQ(0, close(recvfd));
EXPECT_EQ(0, close(ntfyfd[0]));
EXPECT_EQ(0, close(ntfyfd[1]));
}
} // namespace netstack