diff --git a/Makefile b/Makefile index aa467f7..29d54de 100644 --- a/Makefile +++ b/Makefile @@ -6,10 +6,10 @@ all: tests tests: bin/tests LLVM_PROFILE_FILE="./bin/tests.profraw" ./bin/tests @llvm-profdata merge -sparse ./bin/tests.profraw -o ./bin/tests.profdata - @llvm-cov report ./bin/tests -instr-profile=./bin/tests.profdata $(wildcard ./tests/*.cpp) include/*.hpp - @llvm-cov report ./bin/tests -instr-profile=./bin/tests.profdata ./tests/*.cpp include/*.hpp | tail -n 1 | tr -s " " | sed -e 's/ /,/g' -- | awk -F "," '{print $$9}' | sed -e 's/^/Untested lines: /g' + @llvm-cov report ./bin/tests -instr-profile=./bin/tests.profdata include/*.hpp + @llvm-cov report ./bin/tests -instr-profile=./bin/tests.profdata include/*.hpp | tail -n 1 | tr -s " " | sed -e 's/ /,/g' -- | awk -F "," '{print $$9}' | sed -e 's/^/Untested lines: /g' -bin/tests: tests.cpp $(wildcard tests/*.cpp) ./tests/test_scaffold.h +bin/tests: tests.cpp $(wildcard tests/*.cpp) $(wildcard include/*.hpp) ./tests/test_scaffold.h @mkdir -p $(@D) $(CXX) $(CXXFLAGS) -Itests -Iinclude tests.cpp -o $@ diff --git a/include/shared_fd.hpp b/include/shared_fd.hpp index 11dc624..213b453 100644 --- a/include/shared_fd.hpp +++ b/include/shared_fd.hpp @@ -5,8 +5,13 @@ #include #include #include +#include +#include #include +#include #include +#include +#include #include namespace gp{ @@ -49,14 +54,15 @@ namespace gp{ sticky_bit = S_ISVTX, }; - enum class net_socket_domain : int { + enum class socket_domain : int { ip4 = AF_INET, - ip6 = AF_INET6 + ip6 = AF_INET6, + unix = AF_UNIX }; - enum class net_socket_protocol : int { - tcp = SOCK_STREAM, - udp = SOCK_DGRAM + enum class socket_protocol : int { + tcp_like = SOCK_STREAM, + udp_like = SOCK_DGRAM }; @@ -74,6 +80,67 @@ namespace gp{ 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 = 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; @@ -109,6 +176,10 @@ namespace gp{ oth.guard=nullptr; } + const int get() const { + return fd; + } + void operator=(const shared_fd& oth) { this->~shared_fd(); @@ -136,18 +207,28 @@ namespace gp{ return ret; } - static shared_fd net_socket(const net_socket_domain& dom, const net_socket_protocol& proto, net_socket_opt_flags& flags) + static shared_fd socket(const socket_domain& dom, const socket_protocol& proto, const net_socket_opt_flags& flags) { - shared_fd ret{::socket((int)dom, (int)proto, (int)flags)}; + shared_fd ret; + + auto res = ::socket((int)dom, (int)proto, (int)flags); + if(res >= 0) + ret = shared_fd{res}; + return ret; } - static shared_fd unix_socket(const net_socket_protocol& proto, const socket_opt_flags flags) { - shared_fd ret{::socket((int)AF_UNIX, (int)proto, (int)flags)}; + 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 net_socket_protocol& proto, const socket_opt_flags flags) { + 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); diff --git a/tests/shared_fd.cpp b/tests/shared_fd.cpp index 13d6b01..1ba3966 100644 --- a/tests/shared_fd.cpp +++ b/tests/shared_fd.cpp @@ -42,6 +42,8 @@ struct manip_test : public test_scaffold { int error = fd.is_valid()?0:1; + error += !(fd.get()>=0); + auto fd_cpy = fd; error += fd_cpy.is_valid()?0:1; @@ -121,4 +123,76 @@ struct rw_err_test : public test_scaffold { } }; -append_test dummy_l6987erd3(new rw_err_test{}); \ No newline at end of file +append_test dummy_l6987erd3(new rw_err_test{}); + + +struct make_address_test : public test_scaffold { + make_address_test() { + name = __FILE__ ":6"; + } + + virtual int run() { + int error = 0; + + gp::address ipv4 = gp::make_ipv4("127.0.0.1", 1234); + auto p = std::get(ipv4); + error += (p.sin_family != AF_INET); + error += (p.sin_addr.s_addr != htonl(0x7F000001)); + error += (p.sin_port != 1234); + + try{ + gp::make_ipv4("not an IP", 1234); + error += 1; + }catch(...){} + + std::string filename = "/tmp/my_socket"; + gp::address unix = gp::make_unix(filename); + auto q = std::get(unix); + error += (q.sun_family != AF_UNIX); + error += strcmp(filename.c_str(), q.sun_path); + + try{ + std::string long_str(1024, 'p'); + gp::make_unix(long_str); + error += 1; + }catch(...){} + + return error; + } +}; + +append_test dummy_l6923ml3(new make_address_test{}); + +struct sockets_test : public test_scaffold { + sockets_test() { + name = __FILE__ ":7"; + } + + virtual int run() { + int error = 0; + + auto v = gp::shared_fd::socket(gp::socket_domain::ip4, gp::socket_protocol::tcp_like, (gp::net_socket_opt_flags)0); + error += !(v.is_valid()); + + v = gp::shared_fd::socket(gp::socket_domain::ip4, gp::socket_protocol::tcp_like, (gp::net_socket_opt_flags)-1); + error += (v.is_valid()); + + auto pair_v = gp::shared_fd::unix_socket_pair(gp::socket_protocol::tcp_like, (gp::net_socket_opt_flags)0); + error += !(pair_v.first.is_valid()); + error += !(pair_v.second.is_valid()); + + pair_v = gp::shared_fd::unix_socket_pair(gp::socket_protocol::tcp_like, (gp::net_socket_opt_flags)-1); + error += pair_v.first.is_valid(); + error += pair_v.second.is_valid(); + + auto u_v = gp::shared_fd::unix_socket(gp::socket_protocol::tcp_like, (gp::socket_opt_flags)0); + error += !(u_v.is_valid()); + + u_v = gp::shared_fd::unix_socket(gp::socket_protocol::tcp_like, (gp::socket_opt_flags)-1); + error += u_v.is_valid(); + + return error; + } +}; + +append_test dummy_r3321443(new sockets_test{}); \ No newline at end of file