#include "fake_network_stack.hh" #include #include #include #include "../../../toxcore/mem.h" namespace tox::test { static const Network_Funcs kVtable = { .close = [](void *obj, Socket sock) { return static_cast(obj)->close(sock); }, .accept = [](void *obj, Socket sock) { return static_cast(obj)->accept(sock); }, .bind = [](void *obj, Socket sock, const IP_Port *addr) { return static_cast(obj)->bind(sock, addr); }, .listen = [](void *obj, Socket sock, int backlog) { return static_cast(obj)->listen(sock, backlog); }, .connect = [](void *obj, Socket sock, const IP_Port *addr) { return static_cast(obj)->connect(sock, addr); }, .recvbuf = [](void *obj, Socket sock) { return static_cast(obj)->recvbuf(sock); }, .recv = [](void *obj, Socket sock, uint8_t *buf, size_t len) { return static_cast(obj)->recv(sock, buf, len); }, .recvfrom = [](void *obj, Socket sock, uint8_t *buf, size_t len, IP_Port *addr) { return static_cast(obj)->recvfrom(sock, buf, len, addr); }, .send = [](void *obj, Socket sock, const uint8_t *buf, size_t len) { return static_cast(obj)->send(sock, buf, len); }, .sendto = [](void *obj, Socket sock, const uint8_t *buf, size_t len, const IP_Port *addr) { return static_cast(obj)->sendto(sock, buf, len, addr); }, .socket = [](void *obj, int domain, int type, int proto) { return static_cast(obj)->socket(domain, type, proto); }, .socket_nonblock = [](void *obj, Socket sock, bool nonblock) { return static_cast(obj)->socket_nonblock(sock, nonblock); }, .getsockopt = [](void *obj, Socket sock, int level, int optname, void *optval, size_t *optlen) { return static_cast(obj)->getsockopt( sock, level, optname, optval, optlen); }, .setsockopt = [](void *obj, Socket sock, int level, int optname, const void *optval, size_t optlen) { return static_cast(obj)->setsockopt( sock, level, optname, optval, optlen); }, .getaddrinfo = [](void *obj, const Memory *mem, const char *address, int family, int protocol, IP_Port **addrs) { FakeNetworkStack *self = static_cast(obj); if (self->universe().is_verbose()) { std::cerr << "[FakeNetworkStack] getaddrinfo for " << address << std::endl; } if (strcmp(address, "127.0.0.1") == 0 || strcmp(address, "localhost") == 0) { *addrs = static_cast(mem_alloc(mem, sizeof(IP_Port))); memset(&(*addrs)->ip, 0, sizeof(IP)); ip_init(&(*addrs)->ip, false); (*addrs)->ip.ip.v4.uint32 = net_htonl(0x7F000001); (*addrs)->port = 0; return 1; } IP ip; if (addr_parse_ip(address, &ip)) { *addrs = static_cast(mem_alloc(mem, sizeof(IP_Port))); (*addrs)->ip = ip; (*addrs)->port = 0; if (self->universe().is_verbose()) { std::cerr << "[FakeNetworkStack] resolved " << address << std::endl; } return 1; } return 0; }, .freeaddrinfo = [](void *obj, const Memory *mem, IP_Port *addrs) { mem_delete(mem, addrs); return 0; }, }; FakeNetworkStack::FakeNetworkStack(NetworkUniverse &universe, const IP &node_ip) : universe_(universe) , node_ip_(node_ip) { } FakeNetworkStack::~FakeNetworkStack() = default; struct Network FakeNetworkStack::get_c_network() { return Network{&kVtable, this}; } Socket FakeNetworkStack::socket(int domain, int type, int protocol) { std::lock_guard lock(mutex_); int fd = next_fd_++; std::unique_ptr sock; if (type == SOCK_DGRAM) { if (universe_.is_verbose()) { std::cerr << "[FakeNetworkStack] create UDP socket fd=" << fd << std::endl; } sock = std::make_unique(universe_); } else if (type == SOCK_STREAM) { if (universe_.is_verbose()) { std::cerr << "[FakeNetworkStack] create TCP socket fd=" << fd << std::endl; } sock = std::make_unique(universe_); } else { // Unknown type return net_socket_from_native(-1); } sockets_[fd] = std::move(sock); sockets_[fd]->set_ip(node_ip_); return net_socket_from_native(fd); } FakeSocket *FakeNetworkStack::get_sock(Socket sock) { std::lock_guard lock(mutex_); auto it = sockets_.find(net_socket_to_native(sock)); if (it != sockets_.end()) { return it->second.get(); } return nullptr; } int FakeNetworkStack::close(Socket sock) { std::lock_guard lock(mutex_); int fd = net_socket_to_native(sock); auto it = sockets_.find(fd); if (it == sockets_.end()) { errno = EBADF; return -1; } it->second->close(); sockets_.erase(it); return 0; } // Delegate all others int FakeNetworkStack::bind(Socket sock, const IP_Port *addr) { if (auto *s = get_sock(sock)) { int ret = s->bind(addr); if (universe_.is_verbose() && ret == 0) { char ip_str[TOX_INET_ADDRSTRLEN]; ip_parse_addr(&s->ip_address(), ip_str, sizeof(ip_str)); std::cerr << "[FakeNetworkStack] bound socket to " << ip_str << ":" << s->local_port() << std::endl; } return ret; } errno = EBADF; return -1; } int FakeNetworkStack::connect(Socket sock, const IP_Port *addr) { if (auto *s = get_sock(sock)) return s->connect(addr); errno = EBADF; return -1; } int FakeNetworkStack::listen(Socket sock, int backlog) { if (auto *s = get_sock(sock)) return s->listen(backlog); errno = EBADF; return -1; } Socket FakeNetworkStack::accept(Socket sock) { // This requires creating a new FD IP_Port addr; std::unique_ptr new_sock_obj; { auto *s = get_sock(sock); if (!s) { errno = EBADF; return net_socket_from_native(-1); } new_sock_obj = s->accept(&addr); } if (!new_sock_obj) { // errno set by accept return net_socket_from_native(-1); } std::lock_guard lock(mutex_); int fd = next_fd_++; sockets_[fd] = std::move(new_sock_obj); return net_socket_from_native(fd); } int FakeNetworkStack::send(Socket sock, const uint8_t *buf, size_t len) { if (auto *s = get_sock(sock)) return s->send(buf, len); errno = EBADF; return -1; } int FakeNetworkStack::recv(Socket sock, uint8_t *buf, size_t len) { if (auto *s = get_sock(sock)) return s->recv(buf, len); errno = EBADF; return -1; } int FakeNetworkStack::recvbuf(Socket sock) { if (auto *s = get_sock(sock)) return s->recv_buffer_size(); errno = EBADF; return -1; } int FakeNetworkStack::sendto(Socket sock, const uint8_t *buf, size_t len, const IP_Port *addr) { if (auto *s = get_sock(sock)) return s->sendto(buf, len, addr); errno = EBADF; return -1; } int FakeNetworkStack::recvfrom(Socket sock, uint8_t *buf, size_t len, IP_Port *addr) { if (auto *s = get_sock(sock)) return s->recvfrom(buf, len, addr); errno = EBADF; return -1; } int FakeNetworkStack::socket_nonblock(Socket sock, bool nonblock) { if (auto *s = get_sock(sock)) return s->socket_nonblock(nonblock); errno = EBADF; return -1; } int FakeNetworkStack::getsockopt(Socket sock, int level, int optname, void *optval, size_t *optlen) { if (auto *s = get_sock(sock)) return s->getsockopt(level, optname, optval, optlen); errno = EBADF; return -1; } int FakeNetworkStack::setsockopt( Socket sock, int level, int optname, const void *optval, size_t optlen) { if (auto *s = get_sock(sock)) return s->setsockopt(level, optname, optval, optlen); errno = EBADF; return -1; } FakeUdpSocket *FakeNetworkStack::get_udp_socket(Socket sock) { if (auto *s = get_sock(sock)) { if (s->type() == SOCK_DGRAM) { return static_cast(s); } } return nullptr; } std::vector FakeNetworkStack::get_bound_udp_sockets() { std::lock_guard lock(mutex_); std::vector result; for (const auto &pair : sockets_) { FakeSocket *s = pair.second.get(); if (s->type() == SOCK_DGRAM && s->local_port() != 0) { result.push_back(static_cast(s)); } } return result; } } // namespace tox::test