|
|
- #pragma once
- #include <fcntl.h>
- #include <unistd.h>
- #include <sys/mman.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <sys/un.h>
- #include <string>
- #include <sstream>
- #include <atomic>
- #include <array>
- #include <variant>
- #include <cassert>
-
- // 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<sockaddr_in, sockaddr_in6, sockaddr_un>;
-
- template<typename T>
- struct stream_expect{
- const T v;
- stream_expect(T value)
- : v(value)
- {}
- };
-
- template<typename T>
- std::istream& operator>>(std::istream& in, const stream_expect<T>& 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<<addr;
- std::array<uint8_t, 4> address{0};
- int ex;
- din>>ex
- >>stream_expect<char>('.');
- address[0]=ex;
- din>>ex
- >>stream_expect<char>('.');
- address[1]=ex;
- din>>ex
- >>stream_expect<char>('.');
- 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<shared_fd,shared_fd> unix_socket_pair(const socket_protocol& proto, const net_socket_opt_flags flags) {
- std::pair<gp::shared_fd, gp::shared_fd> 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<uint8_t, 256> 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);
- }
- }
- }
- };
- }
|