mirror of
https://github.com/oxen-io/lokinet.git
synced 2024-10-29 11:05:43 +00:00
435 lines
9.9 KiB
C++
435 lines
9.9 KiB
C++
#ifndef EV_WIN32_HPP
|
|
#define EV_WIN32_HPP
|
|
#include <llarp/buffer.h>
|
|
#include <llarp/net.h>
|
|
#include <windows.h>
|
|
#include <cstdio>
|
|
#include <llarp/net.hpp>
|
|
#include "ev.hpp"
|
|
#include "logger.hpp"
|
|
|
|
namespace llarp
|
|
{
|
|
struct udp_listener : public ev_io
|
|
{
|
|
llarp_udp_io* udp;
|
|
|
|
// we receive queued data in the OVERLAPPED data field,
|
|
// much like the pipefds in the UNIX kqueue and loonix
|
|
// epoll handles
|
|
WSAOVERLAPPED portfd;
|
|
size_t iosz;
|
|
|
|
udp_listener(SOCKET fd, llarp_udp_io* u) : ev_io(fd), udp(u)
|
|
{
|
|
memset((void*)&portfd, 0, sizeof(WSAOVERLAPPED));
|
|
};
|
|
|
|
~udp_listener()
|
|
{
|
|
}
|
|
|
|
virtual int
|
|
read(void* buf, size_t sz)
|
|
{
|
|
sockaddr_in6 src;
|
|
socklen_t slen = sizeof(src);
|
|
sockaddr* addr = (sockaddr*)&src;
|
|
unsigned long flags = 0;
|
|
WSABUF wbuf = {sz, static_cast< char* >(buf)};
|
|
// WSARecvFrom
|
|
llarp::LogDebug("read ", sz, " bytes into socket");
|
|
int ret = ::WSARecvFrom(std::get< SOCKET >(fd), &wbuf, 1, nullptr, &flags,
|
|
addr, &slen, &portfd, nullptr);
|
|
// 997 is the error code for queued ops
|
|
int s_errno = ::WSAGetLastError();
|
|
if(ret && s_errno != 997)
|
|
{
|
|
llarp::LogWarn("recv socket error ", s_errno);
|
|
return -1;
|
|
}
|
|
// get the _real_ payload size from tick()
|
|
udp->recvfrom(udp, addr, buf, sz);
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
sendto(const sockaddr* to, const void* data, size_t sz)
|
|
{
|
|
socklen_t slen;
|
|
WSABUF wbuf = {sz, (char*)data};
|
|
switch(to->sa_family)
|
|
{
|
|
case AF_INET:
|
|
slen = sizeof(struct sockaddr_in);
|
|
break;
|
|
case AF_INET6:
|
|
slen = sizeof(struct sockaddr_in6);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
// WSASendTo
|
|
llarp::LogDebug("write ", sz, " bytes into socket");
|
|
ssize_t sent = ::WSASendTo(std::get< SOCKET >(fd), &wbuf, 1, nullptr, 0,
|
|
to, slen, &portfd, nullptr);
|
|
int s_errno = ::WSAGetLastError();
|
|
if(sent && s_errno != 997)
|
|
{
|
|
llarp::LogWarn("send socket error ", s_errno);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
struct tun : public ev_io
|
|
{
|
|
llarp_tun_io* t;
|
|
device* tunif;
|
|
OVERLAPPED* tun_async;
|
|
tun(llarp_tun_io* tio)
|
|
: ev_io(INVALID_HANDLE_VALUE)
|
|
, t(tio)
|
|
, tunif(tuntap_init())
|
|
|
|
{
|
|
|
|
};
|
|
|
|
int
|
|
sendto(const sockaddr* to, const void* data, size_t sz)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
flush_write()
|
|
{
|
|
if(t->before_write)
|
|
{
|
|
t->before_write(t);
|
|
}
|
|
ev_io::flush_write();
|
|
}
|
|
|
|
bool
|
|
do_write(void* data, size_t sz)
|
|
{
|
|
return WriteFile(std::get< HANDLE >(fd), data, sz, nullptr, tun_async);
|
|
}
|
|
|
|
int
|
|
read(void* buf, size_t sz)
|
|
{
|
|
ssize_t ret = tuntap_read(tunif, buf, sz);
|
|
if(ret > 4 && t->recvpkt)
|
|
// should have pktinfo
|
|
t->recvpkt(t, ((byte_t*)buf) + 4, ret - 4);
|
|
return ret;
|
|
}
|
|
|
|
bool
|
|
setup()
|
|
{
|
|
llarp::LogDebug("set ifname to ", t->ifname);
|
|
strncpy(tunif->if_name, t->ifname, sizeof(tunif->if_name));
|
|
|
|
if(tuntap_start(tunif, TUNTAP_MODE_TUNNEL, 0) == -1)
|
|
{
|
|
llarp::LogWarn("failed to start interface");
|
|
return false;
|
|
}
|
|
if(tuntap_set_ip(tunif, t->ifaddr, t->ifaddr, t->netmask) == -1)
|
|
{
|
|
llarp::LogWarn("failed to set ip");
|
|
return false;
|
|
}
|
|
if(tuntap_up(tunif) == -1)
|
|
{
|
|
llarp::LogWarn("failed to put interface up: ", strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
fd = tunif->tun_fd;
|
|
tun_async = &tunif->ovl;
|
|
if(std::get< HANDLE >(fd) == INVALID_HANDLE_VALUE)
|
|
return false;
|
|
|
|
// we're already non-blocking
|
|
return true;
|
|
}
|
|
|
|
~tun()
|
|
{
|
|
}
|
|
};
|
|
}; // namespace llarp
|
|
|
|
struct llarp_win32_loop : public llarp_ev_loop
|
|
{
|
|
HANDLE iocpfd;
|
|
|
|
llarp_win32_loop() : iocpfd(INVALID_HANDLE_VALUE)
|
|
{
|
|
}
|
|
|
|
~llarp_win32_loop()
|
|
{
|
|
if(iocpfd != INVALID_HANDLE_VALUE)
|
|
::CloseHandle(iocpfd);
|
|
}
|
|
|
|
bool
|
|
init()
|
|
{
|
|
if(iocpfd == INVALID_HANDLE_VALUE)
|
|
iocpfd = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0);
|
|
|
|
if(iocpfd == INVALID_HANDLE_VALUE)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// it works! -despair86, 3-Aug-18 @0420
|
|
int
|
|
tick(int ms)
|
|
{
|
|
// The only field we really care about is
|
|
// the listener_id, as it contains the address
|
|
// of the udp_listener instance.
|
|
DWORD iolen = 0;
|
|
// ULONG_PTR is guaranteed to be the same size
|
|
// as an arch-specific pointer value
|
|
ULONG_PTR ev_id = 0;
|
|
WSAOVERLAPPED* qdata = nullptr;
|
|
int idx = 0;
|
|
BOOL result =
|
|
::GetQueuedCompletionStatus(iocpfd, &iolen, &ev_id, &qdata, ms);
|
|
|
|
if(result && qdata)
|
|
{
|
|
llarp::udp_listener* ev = reinterpret_cast< llarp::udp_listener* >(ev_id);
|
|
if(ev && !ev->fd.valueless_by_exception())
|
|
{
|
|
llarp::LogDebug("size: ", iolen, "\tev_id: ", ev_id,
|
|
"\tqdata: ", qdata);
|
|
if(iolen <= sizeof(readbuf))
|
|
ev->read(readbuf, iolen);
|
|
}
|
|
++idx;
|
|
}
|
|
|
|
if(!idx)
|
|
return -1;
|
|
else
|
|
{
|
|
result = idx;
|
|
tick_listeners();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// ok apparently this isn't being used yet...
|
|
int
|
|
run()
|
|
{
|
|
// The only field we really care about is
|
|
// the listener_id, as it contains the address
|
|
// of the udp_listener instance.
|
|
DWORD iolen = 0;
|
|
// ULONG_PTR is guaranteed to be the same size
|
|
// as an arch-specific pointer value
|
|
ULONG_PTR ev_id = 0;
|
|
WSAOVERLAPPED* qdata = nullptr;
|
|
int idx = 0;
|
|
int result =
|
|
::GetQueuedCompletionStatus(iocpfd, &iolen, &ev_id, &qdata, 10);
|
|
|
|
// unlike epoll and kqueue, we only need to run so long as the
|
|
// system call returns TRUE
|
|
if(result)
|
|
{
|
|
llarp::udp_listener* ev = reinterpret_cast< llarp::udp_listener* >(ev_id);
|
|
if(ev && !ev->fd.valueless_by_exception())
|
|
{
|
|
llarp::LogInfo("size: ", iolen, "\tev_id: ", ev_id, "\tqdata: ", qdata);
|
|
if(iolen <= sizeof(readbuf))
|
|
ev->read(readbuf, iolen);
|
|
}
|
|
++idx;
|
|
}
|
|
|
|
if(!idx)
|
|
return -1;
|
|
else
|
|
{
|
|
tick_listeners();
|
|
result = idx;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
SOCKET
|
|
udp_bind(const sockaddr* addr)
|
|
{
|
|
socklen_t slen;
|
|
switch(addr->sa_family)
|
|
{
|
|
case AF_INET:
|
|
slen = sizeof(struct sockaddr_in);
|
|
break;
|
|
case AF_INET6:
|
|
slen = sizeof(struct sockaddr_in6);
|
|
break;
|
|
default:
|
|
return INVALID_SOCKET;
|
|
}
|
|
DWORD on = 1;
|
|
SOCKET fd = ::socket(addr->sa_family, SOCK_DGRAM, 0);
|
|
if(fd == INVALID_SOCKET)
|
|
{
|
|
perror("WSASocket()");
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
if(addr->sa_family == AF_INET6)
|
|
{
|
|
// enable dual stack explicitly
|
|
int dual = 1;
|
|
if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&dual,
|
|
sizeof(dual))
|
|
== -1)
|
|
{
|
|
// failed
|
|
perror("setsockopt()");
|
|
closesocket(fd);
|
|
return INVALID_SOCKET;
|
|
}
|
|
}
|
|
llarp::Addr a(*addr);
|
|
llarp::LogDebug("bind to ", a);
|
|
if(bind(fd, addr, slen) == -1)
|
|
{
|
|
perror("bind()");
|
|
closesocket(fd);
|
|
return INVALID_SOCKET;
|
|
}
|
|
llarp::LogDebug("socket fd is ", fd);
|
|
ioctlsocket(fd, FIONBIO, &on);
|
|
return fd;
|
|
}
|
|
|
|
bool
|
|
close_ev(llarp::ev_io* ev)
|
|
{
|
|
// On Windows, just close the socket to decrease the iocp refcount
|
|
// and stop any pending I/O
|
|
BOOL stopped =
|
|
::CancelIo(reinterpret_cast< HANDLE >(std::get< SOCKET >(ev->fd)));
|
|
return closesocket(std::get< SOCKET >(ev->fd)) == 0 && stopped == TRUE;
|
|
}
|
|
|
|
llarp::ev_io*
|
|
create_udp(llarp_udp_io* l, const sockaddr* src)
|
|
{
|
|
SOCKET fd = udp_bind(src);
|
|
llarp::LogDebug("new socket fd is ", fd);
|
|
if(fd == INVALID_SOCKET)
|
|
return nullptr;
|
|
llarp::udp_listener* listener = new llarp::udp_listener(fd, l);
|
|
l->impl = listener;
|
|
udp_listeners.push_back(l);
|
|
return listener;
|
|
}
|
|
|
|
llarp::ev_io*
|
|
create_tun(llarp_tun_io* tun)
|
|
{
|
|
llarp::tun* t = new llarp::tun(tun);
|
|
if(t->setup())
|
|
return t;
|
|
delete t;
|
|
return nullptr;
|
|
}
|
|
|
|
bool
|
|
add_ev(llarp::ev_io* ev, bool write)
|
|
{
|
|
uint8_t buf[1024];
|
|
llarp::udp_listener* udp = nullptr;
|
|
llarp::tun* t = nullptr;
|
|
ev->listener_id = reinterpret_cast< ULONG_PTR >(ev);
|
|
memset(&buf, 0, 1024);
|
|
|
|
switch(ev->fd.index())
|
|
{
|
|
case 0:
|
|
udp = dynamic_cast< llarp::udp_listener* >(ev);
|
|
if(!::CreateIoCompletionPort((HANDLE)std::get< 0 >(ev->fd), iocpfd,
|
|
ev->listener_id, 0))
|
|
{
|
|
delete ev;
|
|
return false;
|
|
}
|
|
::ReadFile((HANDLE)std::get< 0 >(ev->fd), &buf, 1024, nullptr,
|
|
&udp->portfd);
|
|
break;
|
|
case 1:
|
|
t = dynamic_cast< llarp::tun* >(ev);
|
|
if(!::CreateIoCompletionPort(std::get< 1 >(ev->fd), iocpfd,
|
|
ev->listener_id, 0))
|
|
{
|
|
delete ev;
|
|
return false;
|
|
}
|
|
::ReadFile(std::get< 1 >(ev->fd), &buf, 1024, nullptr, t->tun_async);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
udp_close(llarp_udp_io* l)
|
|
{
|
|
bool ret = false;
|
|
llarp::udp_listener* listener =
|
|
static_cast< llarp::udp_listener* >(l->impl);
|
|
if(listener)
|
|
{
|
|
ret = close_ev(listener);
|
|
l->impl = nullptr;
|
|
delete listener;
|
|
// std::remove_if
|
|
auto itr = udp_listeners.begin();
|
|
while(itr != udp_listeners.end())
|
|
{
|
|
if((*itr) == l)
|
|
itr = udp_listeners.erase(itr);
|
|
else
|
|
++itr;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool
|
|
running() const
|
|
{
|
|
return iocpfd != INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
void
|
|
stop()
|
|
{
|
|
// still does nothing
|
|
}
|
|
};
|
|
|
|
#endif
|