diff --git a/CMakeLists.txt b/CMakeLists.txt index 77a2e83cd..5afe0f49c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -311,7 +311,7 @@ if(UNIX) if (SOLARIS_HAVE_EPOLL) get_filename_component(EV_SRC "llarp/ev/ev_epoll.cpp" ABSOLUTE) else() - get_filename_component(EV_SRC "llarp/ev/ev_poll.cpp" ABSOLUTE) + get_filename_component(EV_SRC "llarp/ev/ev_sun.cpp" ABSOLUTE) endif() else() message(FATAL_ERROR "Your operating system is not supported yet") diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 17ca2bff3..eaf3b83bd 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -78,6 +78,13 @@ win32/win32_upoll.c ) endif(WIN32) +if (SOLARIS) +set(LIB_PLATFORM_SRC +${LIB_PLATFORM_SRC} +ev/upoll_sun.c +) +endif(SOLARIS) + add_library(${PLATFORM_LIB} STATIC ${LIB_PLATFORM_SRC}) target_link_libraries(${PLATFORM_LIB} PUBLIC ${CRYPTOGRAPHY_LIB} ${UTIL_LIB} libutp Threads::Threads) diff --git a/llarp/ev/ev.cpp b/llarp/ev/ev.cpp index f3f5d9f93..5cf9096de 100644 --- a/llarp/ev/ev.cpp +++ b/llarp/ev/ev.cpp @@ -14,6 +14,8 @@ #elif defined(_WIN32) || defined(_WIN64) || defined(__NT__) #define SHUT_RDWR SD_BOTH #include +#elif defined(__sun) && !defined(SOLARIS_HAVE_EPOLL) +#include #else #error No async event loop for your platform, subclass llarp_ev_loop #endif @@ -22,13 +24,15 @@ void llarp_ev_loop_alloc(struct llarp_ev_loop **ev) { -#if __linux__ || __sun__ +#if __linux__ || SOLARIS_HAVE_EPOLL *ev = new llarp_epoll_loop; #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ || (__APPLE__ && __MACH__) *ev = new llarp_kqueue_loop; #elif defined(_WIN32) || defined(_WIN64) || defined(__NT__) *ev = new llarp_win32_loop; +#elif defined(__sun) && !defined(SOLARIS_HAVE_EPOLL) + *ev = new llarp_poll_loop; #else // TODO: fall back to a generic select-based event loop #error no event loop subclass @@ -40,15 +44,16 @@ llarp_ev_loop_alloc(struct llarp_ev_loop **ev) std::unique_ptr< llarp_ev_loop > llarp_make_ev_loop() { -#if __linux__ || __sun__ +#if __linux__ || SOLARIS_HAVE_EPOLL std::unique_ptr< llarp_ev_loop > r = std::make_unique< llarp_epoll_loop >(); #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ || (__APPLE__ && __MACH__) std::unique_ptr< llarp_ev_loop > r = std::make_unique< llarp_kqueue_loop >(); #elif defined(_WIN32) || defined(_WIN64) || defined(__NT__) std::unique_ptr< llarp_ev_loop > r = std::make_unique< llarp_win32_loop >(); +#elif defined(__sun) && !defined(SOLARIS_HAVE_EPOLL) + std::unique_ptr< llarp_ev_loop > r = std::make_unique< llarp_poll_loop >(); #else -// TODO: fall back to a generic select-based event loop #error no event loop subclass #endif r->init(); diff --git a/llarp/ev/ev.hpp b/llarp/ev/ev.hpp index ff54dd683..17f6fb743 100644 --- a/llarp/ev/ev.hpp +++ b/llarp/ev/ev.hpp @@ -55,7 +55,11 @@ static ssize_t IO(std::function< ssize_t(void) > iofunc) { ssize_t ret = iofunc(); - errno = 0; +#ifndef _WIN32 + errno = 0; +#else + WSASetLastError(0); +#endif return ret; } diff --git a/llarp/ev/ev_sun.cpp b/llarp/ev/ev_sun.cpp new file mode 100644 index 000000000..fee05cc35 --- /dev/null +++ b/llarp/ev/ev_sun.cpp @@ -0,0 +1,526 @@ +#include + +namespace llarp +{ + int + tcp_conn::read(byte_t* buf, size_t sz) + { + if(_shouldClose) + return -1; + + ssize_t amount = ::read(fd, buf, sz); + + if(amount > 0) + { + if(tcp.read) + tcp.read(&tcp, llarp_buffer_t(buf, amount)); + } + else if(amount < 0) + { + // error + _shouldClose = true; + errno = 0; + return -1; + } + return 0; + } + + void + tcp_conn::flush_write() + { + connected(); + ev_io::flush_write(); + } + + ssize_t + tcp_conn::do_write(void* buf, size_t sz) + { + if(_shouldClose) + return -1; + // pretty much every UNIX system still extant, _including_ solaris + // (on both sides of the fork) can ignore SIGPIPE....except + // the other vendored systems... -rick + return ::send(fd, buf, sz, MSG_NOSIGNAL); // ignore sigpipe + } + + void + tcp_conn::connect() + { + socklen_t slen = sizeof(sockaddr_in); + if(_addr.ss_family == AF_UNIX) + slen = sizeof(sockaddr_un); + else if(_addr.ss_family == AF_INET6) + slen = sizeof(sockaddr_in6); + int result = ::connect(fd, (const sockaddr*)&_addr, slen); + if(result == 0) + { + llarp::LogDebug("connected immedidately"); + connected(); + } + else if(errno == EINPROGRESS) + { + // in progress + llarp::LogDebug("connect in progress"); + errno = 0; + return; + } + else if(_conn->error) + { + // wtf? + llarp::LogError("error connecting ", strerror(errno)); + _conn->error(_conn); + errno = 0; + } + } + + int + tcp_serv::read(byte_t*, size_t) + { + int new_fd = ::accept(fd, nullptr, nullptr); + if(new_fd == -1) + { + llarp::LogError("failed to accept on ", fd, ":", strerror(errno)); + return -1; + } + // build handler + llarp::tcp_conn* connimpl = new tcp_conn(loop, new_fd); + if(loop->add_ev(connimpl, true)) + { + // call callback + if(tcp->accepted) + tcp->accepted(tcp, &connimpl->tcp); + return 0; + } + // cleanup error + delete connimpl; + return -1; + } + + bool + udp_listener::tick() + { + if(udp->tick) + udp->tick(udp); + return true; + } + + int + udp_listener::read(byte_t* buf, size_t sz) + { + llarp_buffer_t b; + b.base = buf; + b.cur = b.base; + sockaddr_in6 src; + socklen_t slen = sizeof(sockaddr_in6); + sockaddr* addr = (sockaddr*)&src; + ssize_t ret = ::recvfrom(fd, b.base, sz, 0, addr, &slen); + if(ret < 0) + { + errno = 0; + return -1; + } + if(static_cast< size_t >(ret) > sz) + return -1; + b.sz = ret; + udp->recvfrom(udp, addr, ManagedBuffer{b}); + return ret; + } + + int + udp_listener::sendto(const sockaddr* to, const void* data, size_t sz) + { + socklen_t slen; + 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; + } + ssize_t sent = ::sendto(fd, data, sz, SOCK_NONBLOCK, to, slen); + if(sent == -1) + { + llarp::LogWarn(strerror(errno)); + } + return sent; + } + + int + tun::sendto(__attribute__((unused)) const sockaddr* to, + __attribute__((unused)) const void* data, + __attribute__((unused)) size_t sz) + { + return -1; + } + + bool + tun::tick() + { + if(t->tick) + t->tick(t); + flush_write(); + return true; + } + + void + tun::flush_write() + { + if(t->before_write) + t->before_write(t); + ev_io::flush_write(); + } + + int + tun::read(byte_t* buf, size_t sz) + { + ssize_t ret = tuntap_read(tunif, buf, sz); + if(ret > 0 && t->recvpkt) + { + // does not have pktinfo + t->recvpkt(t, llarp_buffer_t(buf, ret)); + } + return ret; + } + + bool + tun::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_up(tunif) == -1) + { + llarp::LogWarn("failed to put interface up: ", strerror(errno)); + return false; + } + if(tuntap_set_ip(tunif, t->ifaddr, t->ifaddr, t->netmask) == -1) + { + llarp::LogWarn("failed to set ip"); + return false; + } + fd = tunif->tun_fd; + if(fd == -1) + return false; + // set non blocking + int flags = fcntl(fd, F_GETFL, 0); + if(flags == -1) + return false; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1; + } + +}; // namespace llarp + +bool +llarp_poll_loop::tcp_connect(struct llarp_tcp_connecter* tcp, + const sockaddr* remoteaddr) +{ + // create socket + int fd = ::socket(remoteaddr->sa_family, SOCK_STREAM, 0); + if(fd == -1) + return false; + // set non blocking + int flags = fcntl(fd, F_GETFL, 0); + if(flags == -1) + { + ::close(fd); + return false; + } + if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) + { + ::close(fd); + return false; + } + llarp::tcp_conn* conn = new llarp::tcp_conn(this, fd, remoteaddr, tcp); + add_ev(conn, true); + conn->connect(); + return true; +} + +llarp::ev_io* +llarp_poll_loop::bind_tcp(llarp_tcp_acceptor* tcp, const sockaddr* bindaddr) +{ + int fd = ::socket(bindaddr->sa_family, SOCK_STREAM, 0); + if(fd == -1) + return nullptr; + socklen_t sz = sizeof(sockaddr_in); + if(bindaddr->sa_family == AF_INET6) + { + sz = sizeof(sockaddr_in6); + } + else if(bindaddr->sa_family == AF_UNIX) + { + sz = sizeof(sockaddr_un); + } + if(::bind(fd, bindaddr, sz) == -1) + { + ::close(fd); + return nullptr; + } + if(::listen(fd, 5) == -1) + { + ::close(fd); + return nullptr; + } + return new llarp::tcp_serv(this, fd, tcp); +} + +bool +llarp_poll_loop::udp_listen(llarp_udp_io* l, const sockaddr* src) +{ + auto ev = create_udp(l, src); + if(ev) + l->fd = ev->fd; + return ev && add_ev(ev, false); +} + +bool +llarp_poll_loop::running() const +{ + return upollfd != nullptr; +} + +bool +llarp_poll_loop::init() +{ + if(!upollfd) + upollfd = upoll_create(1); // why do we return false? (see ev_poll.cpp) + return false; +} + +int +llarp_poll_loop::tick(int ms) +{ + upoll_event_t events[1024]; + int result; + result = upoll_wait(upollfd, events, 1024, ms); + bool didIO = false; + if(result > 0) + { + int idx = 0; + while(idx < result) + { + llarp::ev_io* ev = static_cast< llarp::ev_io* >(events[idx].data.ptr); + if(ev) + { + llarp::LogDebug(idx, " of ", result, " on ", ev->fd, + " events=", std::to_string(events[idx].events)); + if(events[idx].events & UPOLLERR && errno) + { + IO([&]() -> ssize_t { + llarp::LogDebug("upoll error"); + ev->error(); + return 0; + }); + } + else + { + // write THEN READ don't revert me + if(events[idx].events & UPOLLOUT) + { + IO([&]() -> ssize_t { + llarp::LogDebug("upoll out"); + ev->flush_write(); + return 0; + }); + } + if(events[idx].events & UPOLLIN) + { + ssize_t amount = IO([&]() -> ssize_t { + llarp::LogDebug("upoll in"); + return ev->read(readbuf, sizeof(readbuf)); + }); + if(amount > 0) + didIO = true; + } + } + } + ++idx; + } + } + if(result != -1) + tick_listeners(); + /// if we didn't get an io events we sleep to avoid 100% cpu use + if(!didIO) + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + return result; +} + +int +llarp_poll_loop::run() +{ + upoll_event_t events[1024]; + int result; + do + { + result = upoll_wait(upollfd, events, 1024, EV_TICK_INTERVAL); + if(result > 0) + { + int idx = 0; + while(idx < result) + { + llarp::ev_io* ev = static_cast< llarp::ev_io* >(events[idx].data.ptr); + if(ev) + { + if(events[idx].events & UPOLLERR) + { + ev->error(); + } + else + { + if(events[idx].events & UPOLLIN) + { + ev->read(readbuf, sizeof(readbuf)); + } + if(events[idx].events & UPOLLOUT) + { + ev->flush_write(); + } + } + } + ++idx; + } + } + if(result != -1) + tick_listeners(); + } while(upollfd); + return result; +} + +int +llarp_poll_loop::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 -1; + } + int fd = socket(addr->sa_family, SOCK_DGRAM, 0); + if(fd == -1) + { + perror("socket()"); + return -1; + } + + if(addr->sa_family == AF_INET6) + { + // enable dual stack explicitly + int dual = 1; + if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &dual, sizeof(dual)) == -1) + { + // failed + perror("setsockopt()"); + close(fd); + return -1; + } + } + llarp::Addr a(*addr); + llarp::LogDebug("bind to ", a); + if(bind(fd, addr, slen) == -1) + { + perror("bind()"); + close(fd); + return -1; + } + + return fd; +} + +bool +llarp_poll_loop::close_ev(llarp::ev_io* ev) +{ + return upoll_ctl(upollfd, UPOLL_CTL_DEL, ev->fd, nullptr) != -1; +} + +llarp::ev_io* +llarp_poll_loop::create_tun(llarp_tun_io* tun) +{ + llarp::tun* t = new llarp::tun(tun, this); + if(t->setup()) + { + return t; + } + delete t; + return nullptr; +} + +llarp::ev_io* +llarp_poll_loop::create_udp(llarp_udp_io* l, const sockaddr* src) +{ + int fd = udp_bind(src); + if(fd == -1) + return nullptr; + llarp::ev_io* listener = new llarp::udp_listener(fd, l); + l->impl = listener; + return listener; +} + +bool +llarp_poll_loop::add_ev(llarp::ev_io* e, bool write) +{ + upoll_event_t ev; + ev.data.ptr = e; + ev.events = UPOLLIN | UPOLLERR; + if(write) + ev.events |= UPOLLOUT; + if(upoll_ctl(upollfd, UPOLL_CTL_ADD, e->fd, &ev) == -1) + { + delete e; + return false; + } + handlers.emplace_back(e); + return true; +} + +bool +llarp_poll_loop::udp_close(llarp_udp_io* l) +{ + bool ret = false; + llarp::udp_listener* listener = static_cast< llarp::udp_listener* >(l->impl); + if(listener) + { + close_ev(listener); + // remove handler + auto itr = handlers.begin(); + while(itr != handlers.end()) + { + if(itr->get() == listener) + itr = handlers.erase(itr); + else + ++itr; + } + l->impl = nullptr; + ret = true; + } + return ret; +} + +void +llarp_poll_loop::stop() +{ + // close all handlers before closing the upoll fd + auto itr = handlers.begin(); + while(itr != handlers.end()) + { + close_ev(itr->get()); + itr = handlers.erase(itr); + } + + if(upollfd) + upoll_destroy(upollfd); + upollfd = nullptr; +} diff --git a/llarp/ev/ev_sun.hpp b/llarp/ev/ev_sun.hpp new file mode 100644 index 000000000..f4359c228 --- /dev/null +++ b/llarp/ev/ev_sun.hpp @@ -0,0 +1,136 @@ +#ifndef EV_POLL_HPP +#define EV_POLL_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "upoll_sun.h" +#include +#include +#include + +namespace llarp +{ + struct udp_listener : public ev_io + { + llarp_udp_io* udp; + + udp_listener(int fd, llarp_udp_io* u) : ev_io(fd), udp(u){}; + + ~udp_listener() + { + } + + bool + tick(); + + int + read(byte_t* buf, size_t sz); + + int + sendto(const sockaddr* to, const void* data, size_t sz); + }; + + struct tun : public ev_io + { + llarp_tun_io* t; + device* tunif; + tun(llarp_tun_io* tio, llarp_ev_loop* l) + : ev_io(-1, new LossyWriteQueue_t("tun_write_queue", l, l)) + , t(tio) + , tunif(tuntap_init()) + + { + + }; + + int + sendto(const sockaddr* to, const void* data, size_t sz); + + bool + tick(); + + void + flush_write(); + + int + read(byte_t* buf, size_t sz); + + bool + setup(); + + ~tun() + { + if(tunif) + tuntap_destroy(tunif); + } + }; +}; // namespace llarp + +struct llarp_poll_loop : public llarp_ev_loop +{ + upoll_t* upollfd; + + llarp_poll_loop() : upollfd(nullptr) + { + } + + ~llarp_poll_loop() + { + if(upollfd) + upoll_destroy(upollfd); + } + + bool + tcp_connect(struct llarp_tcp_connecter* tcp, const sockaddr* remoteaddr); + + llarp::ev_io* + bind_tcp(llarp_tcp_acceptor* tcp, const sockaddr* bindaddr); + + virtual bool + udp_listen(llarp_udp_io* l, const sockaddr* src); + + bool + running() const; + + bool + init(); + + int + tick(int ms); + + int + run(); + + int + udp_bind(const sockaddr* addr); + + bool + close_ev(llarp::ev_io* ev); + + llarp::ev_io* + create_tun(llarp_tun_io* tun); + + llarp::ev_io* + create_udp(llarp_udp_io* l, const sockaddr* src); + + bool + add_ev(llarp::ev_io* e, bool write); + + bool + udp_close(llarp_udp_io* l); + + void + stop(); +}; + +#endif diff --git a/llarp/ev/ev_win32.cpp b/llarp/ev/ev_win32.cpp index 105e8dcd4..06531442d 100644 --- a/llarp/ev/ev_win32.cpp +++ b/llarp/ev/ev_win32.cpp @@ -464,7 +464,8 @@ llarp_win32_loop::tick(int ms) { upoll_event_t events[1024]; int result; - result = upoll_wait(upollfd, events, 1024, ms); + result = upoll_wait(upollfd, events, 1024, ms); + bool didIO = false; if(result > 0) { int idx = 0; @@ -473,28 +474,46 @@ llarp_win32_loop::tick(int ms) llarp::ev_io* ev = static_cast< llarp::ev_io* >(events[idx].data.ptr); if(ev) { - if(events[idx].events & UPOLLERR) + llarp::LogDebug(idx, " of ", result, " on ", ev->fd, + " events=", std::to_string(events[idx].events)); + if(events[idx].events & UPOLLERR && WSAGetLastError()) { - ev->error(); + IO([&]() -> ssize_t { + llarp::LogDebug("upoll error"); + ev->error(); + return 0; + }); } else { - if(events[idx].events & UPOLLIN) + // write THEN READ don't revert me + if(events[idx].events & UPOLLOUT) { - ev->read(readbuf, sizeof(readbuf)); + IO([&]() -> ssize_t { + llarp::LogDebug("upoll out"); + ev->flush_write(); + return 0; + }); } - if(events[idx].events & UPOLLOUT) + if(events[idx].events & UPOLLIN) { - ev->flush_write(); + ssize_t amount = IO([&]() -> ssize_t { + llarp::LogDebug("upoll in"); + return ev->read(readbuf, sizeof(readbuf)); + }); + if(amount > 0) + didIO = true; } } } ++idx; } } - if(result != -1) tick_listeners(); + /// if we didn't get an io events we sleep to avoid 100% cpu use + if(!didIO) + std::this_thread::sleep_for(std::chrono::milliseconds(5)); return result; } diff --git a/llarp/ev/upoll_sun.c b/llarp/ev/upoll_sun.c new file mode 100644 index 000000000..dba1f6223 --- /dev/null +++ b/llarp/ev/upoll_sun.c @@ -0,0 +1,433 @@ +#include "upoll_sun.h" + +#define uhash_slot(K, S) (((K) ^ (K >> 8)) & (S - 1)) + +static uhash_t* +uhash_create(uint32_t size) +{ + int i; + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + + uhash_t* hash = (uhash_t*)calloc(1, sizeof(uhash_t) + size * sizeof(ulist_t)); + hash->count = 0; + hash->size = size; + hash->items = (ulist_t*)(((char*)hash) + sizeof(uhash_t)); + + for(i = 0; i < size; i++) + { + ulist_init(&hash->items[i]); + } + + return hash; +} + +static void* +uhash_lookup(uhash_t* hash, intptr_t key) +{ + uint32_t slot = uhash_slot(key, hash->size); + ulist_t* q; + ulist_scan(q, &hash->items[slot]) + { + uitem_t* i = ulist_data(q, uitem_t, list); + if(i->key == key) + return i->val; + } + return NULL; +} +static void +uhash_insert(uhash_t* hash, intptr_t key, void* val) +{ + uint32_t slot = uhash_slot(key, hash->size); + + uitem_t* item = (uitem_t*)calloc(1, sizeof(uitem_t)); + ulist_init(&item->list); + item->key = key; + item->val = val; + + ulist_append(&hash->items[slot], &item->list); +} +static int +uhash_delete(uhash_t* hash, intptr_t key) +{ + uint32_t slot = uhash_slot(key, hash->size); + ulist_t* q; + ulist_scan(q, &hash->items[slot]) + { + uitem_t* i = ulist_data(q, uitem_t, list); + if(i->key == key) + { + ulist_remove(q); + free(q); + return 1; + } + } + return 0; +} +static int +uhash_destroy(uhash_t* hash) +{ + int i; + for(i = 0; i < hash->size; i++) + { + while(!ulist_empty(&hash->items[i])) + { + ulist_t* q = ulist_next(&hash->items[i]); + uitem_t* n = ulist_data(q, uitem_t, list); + ulist_remove(q); + free(n); + } + } + return 0; +} + +upoll_t* +upoll_create(uint32_t size) +{ + assert(size > 0); + upoll_t* upq = (upoll_t*)calloc(1, sizeof(upoll_t)); + + ulist_init(&upq->alive); + + upq->table = uhash_create(size); + return upq; +} + +void +upoll_destroy(upoll_t* upq) +{ + assert(upq != NULL); + uhash_destroy(upq->table); + ulist_t* q; + unote_t* n; + while(!ulist_empty(&upq->alive)) + { + q = ulist_next(&upq->alive); + n = ulist_data(n, unote_t, queue); + ulist_remove(q); + free(n); + } + free(upq); +} + +int +upoll_ctl(upoll_t* upq, int op, intptr_t fd, upoll_event_t* event) +{ + if(fd < 0) + return -EBADF; + + unote_t* note = NULL; + switch(op) + { + case UPOLL_CTL_ADD: + { + note = (unote_t*)uhash_lookup(upq->table, fd); + if(!note) + { + note = (unote_t*)calloc(1, sizeof(unote_t)); + note->upoll = upq; + ulist_init(¬e->queue); + note->event = *event; + note->fd = fd; + ulist_append(&upq->alive, ¬e->queue); + uhash_insert(upq->table, fd, (void*)note); + } + break; + } + case UPOLL_CTL_DEL: + { + note = (unote_t*)uhash_lookup(upq->table, fd); + if(!note) + return -ENOENT; + event = ¬e->event; + ulist_remove(¬e->queue); + uhash_delete(upq->table, fd); + free(note); + break; + } + case UPOLL_CTL_MOD: + { + note = (unote_t*)uhash_lookup(upq->table, fd); + if(!note) + return -ENOENT; + note->event = *event; + break; + } + default: + { + return -EINVAL; + } + } + return 0; +} + +int +upoll_wait_poll(upoll_t* upq, upoll_event_t* evs, int nev, int timeout) +{ + /* FD_SETSIZE should be smaller than OPEN_MAX, but OPEN_MAX isn't portable */ + if(nev > FD_SETSIZE) + nev = FD_SETSIZE; + + unote_t* nvec[nev]; + int r, i, nfds = 0; + uint32_t hint; + struct pollfd pfds[nev]; + + unote_t* n = NULL; + ulist_t* s = ulist_mark(&upq->alive); + ulist_t* q = ulist_next(&upq->alive); + + while(q != s && nfds < nev) + { + n = ulist_data(q, unote_t, queue); + q = ulist_next(q); + + ulist_remove(&n->queue); + ulist_insert(&upq->alive, &n->queue); + + nvec[nfds] = n; + pfds[nfds].events = 0; + pfds[nfds].fd = n->fd; + if(n->event.events & UPOLLIN) + { + pfds[nfds].events |= POLLIN; + } + if(n->event.events & UPOLLOUT) + { + pfds[nfds].events |= POLLOUT; + } + nfds++; + } + + r = poll(pfds, nfds, timeout); + if(r < 0) + return -errno; + + int e = 0; + for(i = 0; i < nfds && e < nev; i++) + { + hint = 0; + if(pfds[i].revents) + { + n = nvec[i]; + if(pfds[i].revents & POLLIN) + hint |= UPOLLIN; + if(pfds[i].revents & POLLOUT) + hint |= UPOLLOUT; + if(pfds[i].revents & (POLLERR | POLLNVAL | POLLHUP)) + hint |= (UPOLLERR | UPOLLIN); + + if(hint & UPOLLERR) + hint &= ~UPOLLOUT; + + evs[e].data = n->event.data; + evs[e].events = hint; + ++e; + } + } + + return e; +} + +int +upoll_wait(upoll_t* upq, upoll_event_t* evs, int nev, int timeout) +{ + int r = 0; + r = upoll_wait_poll(upq, evs, nev, timeout); + return r; +} + +intptr_t +usocket(int domain, int type, int proto) +{ + intptr_t fd = (intptr_t)socket(domain, type, proto); + + if(fd < 0) + return -errno; + int rc = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); + if(rc < 0) + return -errno; + + return fd; +} + +int +ubind(intptr_t fd, const char* host, const char* serv) +{ + struct addrinfo* info; + struct addrinfo hint; + memset(&hint, 0, sizeof(hint)); + + int optval = 0; + unsigned int optlen = sizeof(optval); + int rc = getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen); + + hint.ai_family = AF_INET; + hint.ai_socktype = optval; + + rc = getaddrinfo(host, serv, &hint, &info); + + optval = 1; + if(!rc) + { + rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + if(!rc) + rc = bind(fd, info->ai_addr, info->ai_addrlen); + } + + freeaddrinfo(info); + if(rc) + { + return errno; + } + return 0; +} + +int +uconnect(intptr_t fd, const char* host, const char* serv) +{ + struct addrinfo* info; + + struct addrinfo hint; + memset(&hint, 0, sizeof(hint)); + + int optval = 0; + unsigned int optlen; + + int rc = getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen); + + hint.ai_family = AF_INET; + hint.ai_socktype = optval; + + rc = getaddrinfo(host, serv, &hint, &info); + + if(!rc) + { + rc = connect(fd, info->ai_addr, info->ai_addrlen); + } + + freeaddrinfo(info); + + if(rc) + { + if(errno != EINPROGRESS) + return errno; + } + return 0; +} + +int +ulisten(intptr_t sock, int backlog) +{ + return listen(sock, backlog); +} + +intptr_t +uaccept(intptr_t sock) +{ + struct sockaddr addr; + + addr.sa_family = AF_INET; + socklen_t addr_len; + + intptr_t fd = accept(sock, &addr, &addr_len); + if(fd < 0) + return errno; + + return fd; +} + +int +uclose(intptr_t sock) +{ + return close(sock); +} + +int +uread(intptr_t fd, char* buf, size_t len) +{ + return recv(fd, buf, len, 0); +} +int +uwrite(intptr_t fd, const char* buf, size_t len) +{ + return send(fd, buf, len, 0); +} + +/* adapted from (renamed make_overlapped to async for allergy reasons): */ +/* socketpair.c +Copyright 2007, 2010 by Nathan C. Myers +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + The name of the author must not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Changes: + * 2014-02-12: merge David Woodhouse, Ger Hobbelt improvements + * git.infradead.org/users/dwmw2/openconnect.git/commitdiff/bdeefa54 + * github.com/GerHobbelt/selectable-socketpair + * always init the socks[] to -1/INVALID_SOCKET on error, both on Win32/64 + * and UNIX/other platforms + * 2013-07-18: Change to BSD 3-clause license + * 2010-03-31: + * set addr to 127.0.0.1 because win32 getsockname does not always set it. + * 2010-02-25: + * set SO_REUSEADDR option to avoid leaking some windows resource. + * Windows System Error 10049, "Event ID 4226 TCP/IP has reached + * the security limit imposed on the number of concurrent TCP connect + * attempts." Bleah. + * 2007-04-25: + * preserve value of WSAGetLastError() on all error returns. + * 2007-04-22: (Thanks to Matthew Gregan ) + * s/EINVAL/WSAEINVAL/ fix trivial compile failure + * s/socket/WSASocket/ enable creation of sockets suitable as stdin/stdout + * of a child process. + * add argument make_overlapped + */ + +int +usocketpair(intptr_t socks[2], int dummy) +{ + int sovec[2]; + if(socks == 0) + { + errno = EINVAL; + return -1; + } + dummy = socketpair(AF_LOCAL, SOCK_STREAM, 0, sovec); + if(dummy) + { + socks[0] = socks[1] = -1; + } + else + { + socks[0] = sovec[0]; + socks[1] = sovec[1]; + } + return dummy; +} diff --git a/llarp/ev/upoll_sun.h b/llarp/ev/upoll_sun.h new file mode 100644 index 000000000..58425a398 --- /dev/null +++ b/llarp/ev/upoll_sun.h @@ -0,0 +1,178 @@ +#ifndef _UPOLL_H_ +#define _UPOLL_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include + +#define UPOLL_CTL_ADD 1 +#define UPOLL_CTL_DEL 2 +#define UPOLL_CTL_MOD 3 + +#define UPOLLIN 0x01 +#define UPOLLOUT 0x02 +#define UPOLLERR 0x04 +#define UPOLLET 0x08 + + typedef struct upoll upoll_t; + + typedef union upoll_data { + void* ptr; + intptr_t fd; + uint32_t u32; + uint64_t u64; + } upoll_data_t; + + typedef struct upoll_event + { + uint32_t events; + upoll_data_t data; + } upoll_event_t; + + upoll_t* + upoll_create(uint32_t size); + int + upoll_ctl(upoll_t* upq, int op, intptr_t fd, upoll_event_t* event); + int + upoll_wait(upoll_t* upq, upoll_event_t* events, int maxevents, int timeout); + void + upoll_destroy(upoll_t* upq); + + intptr_t + usocket(int domain, int type, int proto); + intptr_t + uaccept(intptr_t sock); + + int + ubind(intptr_t sock, const char* name, const char* serv); + int + ulisten(intptr_t sock, int backlog); + int + uconnect(intptr_t sock, const char* name, const char* serv); + int + uclose(intptr_t sock); + int + uread(intptr_t fd, char* buf, size_t len); + int + uwrite(intptr_t fd, const char* buf, size_t len); + int + usocketpair(intptr_t socks[2], int async); + +#if(defined(__64BIT__) || defined(__x86_64__)) +#define __IS_64BIT__ +#else +#define __IS_32BIT__ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + typedef struct unote unote_t; + typedef struct ulist ulist_t; + typedef struct uitem uitem_t; + typedef struct uhash uhash_t; + + struct ulist + { + ulist_t* next; + ulist_t* prev; + }; + + struct uitem + { + ulist_t list; + intptr_t key; + void* val; + }; + + struct uhash + { + uint16_t count; + uint16_t size; + ulist_t* items; + }; + + struct upoll + { + int fd; /* backend fd (epoll, kqueue) */ + ulist_t alive; /* all notes this queue knows about */ + uhash_t* table; + }; + + struct unote + { + upoll_event_t event; + intptr_t fd; + ulist_t queue; /* handle for the queue's notes */ + upoll_t* upoll; + }; + +#define container_of(ptr, type, member) \ + ((type*)((char*)(ptr)-offsetof(type, member))) + +#define ulist_init(q) \ + (q)->prev = q; \ + (q)->next = q + +#define ulist_head(h) (h)->next +#define ulist_next(q) (q)->next + +#define ulist_tail(h) (h)->prev +#define ulist_prev(q) (q)->prev + +#define ulist_empty(h) (h == (h)->prev) + +#define ulist_append(h, x) \ + (x)->prev = (h)->prev; \ + (x)->prev->next = x; \ + (x)->next = h; \ + (h)->prev = x + +#define ulist_insert(h, x) \ + (x)->next = (h)->next; \ + (x)->next->prev = x; \ + (x)->prev = h; \ + (h)->next = x + +#define ulist_remove(x) \ + (x)->next->prev = (x)->prev; \ + (x)->prev->next = (x)->next; \ + (x)->prev = x; \ + (x)->next = x + +#define ulist_mark(h) (h) + +#define ulist_scan(q, h) \ + for((q) = ulist_head(h); (q) != ulist_mark(h); (q) = ulist_next(q)) + +#define ulist_data(q, type, link) container_of(q, type, link) + +#ifdef __cplusplus +} +#endif + +#endif /* _UPOLL_H_ */ diff --git a/ui-win32/UIMain.cs b/ui-win32/UIMain.cs index 3b22271bf..077414828 100644 --- a/ui-win32/UIMain.cs +++ b/ui-win32/UIMain.cs @@ -2,7 +2,6 @@ using System.Diagnostics; using System.IO; using System.Reflection; -using System.Text; using System.Windows.Forms; namespace network.loki.lokinet.win32.ui