#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include // TODO: Scavenge for useful parts and discard namespace gp{ using open_opt_flags = int; enum class open_options : open_opt_flags { append = O_APPEND, create = O_CREAT, async = O_ASYNC, direct = O_DIRECT, data_sync = O_DSYNC, file_sync = O_SYNC, exclusive = O_EXCL, no_access_time = O_NOATIME, no_follow = O_NOFOLLOW, read = O_RDONLY, write = O_WRONLY, #ifdef O_TEMP temporary = O_TEMP, #endif #ifdef O_TRUC truncate = O_TRUC, #endif }; using open_opt_mode = int; enum class open_modes : open_opt_mode { user_read = S_IRUSR, user_write = S_IWUSR, user_exec = S_IXUSR, group_read = S_IRGRP, group_write = S_IWGRP, group_exec = S_IXGRP, other_read = S_IROTH, other_write = S_IWOTH, other_exec = S_IXOTH, set_uid = S_ISUID, set_gid = S_ISGID, sticky_bit = S_ISVTX, }; enum class socket_domain : int { ip4 = AF_INET, ip6 = AF_INET6, unix = AF_UNIX }; enum class socket_protocol : int { tcp_like = SOCK_STREAM, udp_like = SOCK_DGRAM }; using socket_opt_flags = int; enum class net_socket_opt_flags : socket_opt_flags { non_blocking = SOCK_NONBLOCK, close_on_exec = SOCK_CLOEXEC, }; using socket_opt_flags = int; enum class unix_socket_opt_flags : socket_opt_flags { non_blocking = SOCK_NONBLOCK, close_on_exec = SOCK_CLOEXEC, }; using address = std::variant; template struct stream_expect{ const T v; stream_expect(T value) : v(value) {} }; template std::istream& operator>>(std::istream& in, const stream_expect& expector) { T vin; in >> vin; if(vin != expector.v) throw std::runtime_error("Expector failed"); return in; } address make_ipv4(const std::string_view addr, const uint16_t port) { std::stringstream din; din< address{0}; int ex; din>>ex >>stream_expect('.'); address[0]=ex; din>>ex >>stream_expect('.'); address[1]=ex; din>>ex >>stream_expect('.'); address[2]=ex; din>>ex; address[3]=ex; sockaddr_in ret; in_addr ret_addr; ret_addr.s_addr = *((uint32_t*)&address); ret.sin_family = AF_INET; ret.sin_addr = ret_addr; ret.sin_port = htons(port); return ret; } // TODO: make an IPv6 parser //address make_ipv6(const std::string_view addr, const uint16_t port){} address make_unix(const std::string_view filename) { sockaddr_un ret; if(filename.size()>(sizeof(ret.sun_path)-1)) throw std::runtime_error("Filename too long"); ret.sun_family = AF_UNIX; *std::copy(filename.begin(), filename.end(), ret.sun_path) = '\0'; return ret; } class shared_fd { int fd; std::atomic_int* guard; int last_error; shared_fd(int fd_v) : fd(fd_v) , guard(new std::atomic_int{1}) , last_error(0) {} public: shared_fd() : fd(-1) , guard(nullptr) , last_error(0) {} shared_fd(const shared_fd& oth) : fd(oth.fd) , guard(oth.guard) , last_error(0) { if(guard) guard->fetch_add(1, std::memory_order_acq_rel); } shared_fd(shared_fd&& oth) : last_error(0) { fd=oth.fd; guard=oth.guard; oth.fd=-1; oth.guard=nullptr; } const int get() const { return fd; } void operator=(const shared_fd& oth) { this->~shared_fd(); fd = oth.fd; guard = oth.guard; if(guard) guard->fetch_add(1, std::memory_order_acq_rel); } void operator=(shared_fd&& oth) { std::swap(fd,oth.fd); std::swap(guard,oth.guard); } static shared_fd open(const std::string& filename, const open_opt_flags& flags, const open_opt_mode& mode = 0) { shared_fd ret{::open(filename.c_str(), flags, mode)}; return ret; } static shared_fd create(const std::string& filename, const open_opt_mode& mode) { shared_fd ret{::creat(filename.c_str(), mode)}; return ret; } static shared_fd tcp_socket() { shared_fd ret; auto res = ::socket(AF_INET, SOCK_STREAM, 0); if(res >= 0) { ret = shared_fd{res}; fcntl(res, F_SETFL, O_NONBLOCK); } else ret.last_error = errno; return ret; } static shared_fd socket(const socket_domain& dom, const socket_protocol& proto)//, const net_socket_opt_flags& flags) { shared_fd ret; auto res = ::socket((int)dom, (int)proto, (int)0); if(res >= 0) { ret = shared_fd{res}; } else ret.last_error = errno; return ret; } static shared_fd unix_socket(const socket_protocol& proto, const socket_opt_flags flags) { shared_fd ret; auto res = ::socket((int)AF_UNIX, (int)proto, (int)flags); if(res >= 0) ret = shared_fd{res}; return ret; } static std::pair unix_socket_pair(const socket_protocol& proto, const net_socket_opt_flags flags) { std::pair ret; int fds[2]; auto result = ::socketpair((int)AF_UNIX, (int)proto, (int)flags, fds); if(result != 0) return ret; ret.first = shared_fd(fds[0]); ret.second = shared_fd(fds[1]); return ret; } bool is_valid() const { return guard && (fd>=0); } bool has_failed() const { return last_error; } bool was_connection_refused() const { return last_error==ECONNREFUSED; } bool in_progress() const { return last_error==EINPROGRESS; } bool must_retry() const { return last_error==EAGAIN; } int error() const { return last_error; } void bind(const address& addr) { int ret; std::visit([&](const auto& v){ ret = ::bind(fd, (struct sockaddr*)&v, sizeof(v)); }, addr); if(ret==0) { last_error = 0; return; } else last_error = errno; } void connect(const address& addr) { int ret; std::visit([&](const auto& v){ ret = ::connect(fd, (struct sockaddr*)&v, sizeof(v)); }, addr); if(ret!=0) { last_error = errno; return; } last_error = 0; } void listen(const int& backlog = 16) { int ret = 0; std::visit([&](){ ret = ::listen(fd, backlog); }); if(ret!=0) { last_error = errno; return; } last_error = 0; } shared_fd accept() { int ret = 0; std::array buffer; socklen_t len; ret = ::accept(fd,(sockaddr*)buffer.begin(), &len); if(ret!=0) { last_error = errno; return shared_fd{}; } last_error = 0; return shared_fd{ret}; } std::string_view read(const std::string_view buffer) { int sz = ::read(fd, (void*)(buffer.begin()), buffer.size()); if(sz<0) last_error = errno; else last_error = 0; sz = sz<0?0:sz; return std::string_view(buffer.begin(), sz); } std::string_view write(const std::string_view buffer) { int sz = ::write(fd, (void*)(buffer.begin()), buffer.size()); if(sz<0) last_error = errno; else last_error = 0; sz = sz<0?0:sz; return std::string_view(buffer.begin()+sz, buffer.size()-sz); } ~shared_fd() { if(guard) if(guard->fetch_sub(1, std::memory_order_acq_rel) == 1) { assert(guard->load() == 0); assert(fd >= 0); delete guard; guard = nullptr; if(fd >= 0) { ::close(fd); } } } }; }