#include "matador/net/socket.hpp" #include "matador/net/socket_stream.hpp" #include "matador/net/os.hpp" #include "matador/net/error.hpp" #include "matador/net/ip.hpp" #include namespace matador { #define throw_logic_error(msg) \ do { \ std::stringstream str; \ str << msg; \ throw std::logic_error(str.str()); \ } while(false); template < class P > socket_base

::socket_base(const protocol_type &protocol) { open(protocol); } template < class P > socket_base

::socket_base(const peer_type &peer) { open(peer.protocol()); } template < class P > socket_type socket_base

::open(const protocol_type &protocol) { return open(protocol.family(), protocol.type(), protocol.protocol()); } template < class P > void socket_base

::close() { if (sock_ <= 0) { return; } //os::shutdown(sock_, os::shutdown_type::READ_WRITE); os::close(sock_); sock_ = 0; } template < class P > bool socket_base

::is_open() const { return sock_ > 0; } template < class P > socket_type socket_base

::release() { auto tmp_fd = sock_; sock_ = 0; return tmp_fd; } template < class P > bool socket_base

::connect(const typename protocol_type::peer &p) { return ::connect(sock_, p.data(), static_cast(p.size())) == 0; } template < class P > void socket_base

::non_blocking(bool nb) { #ifdef WIN32 unsigned long nonblock = nb ? 0 : 1; // fcntl doesn't do the right thing, but the similar ioctl does // warning: is that still true? and does it the right thing for // set blocking as well? if (ioctlsocket(sock_, FIONBIO, &nonblock) != 0) { throw std::logic_error("ioctlsocket: couldn't set flags"); } is_nonblocking_ = nb; #else int val = fcntl(sock_, F_GETFL, 0); if (val < 0) { throw std::logic_error("fcntl: couldn't get flags"); } int flag = (nb ? O_NONBLOCK : 0); if (fcntl(sock_, F_SETFL, val | flag) < 0) { std::string err(strerror(errno)); throw std::logic_error("fcntl: couldn't set flags (" + err + ")"); } #endif } template < class P > bool socket_base

::non_blocking() const { #ifdef _WIN32 return is_nonblocking_; #else int val = fcntl(sock_, F_GETFL, 0); if (val < 0) { std::string err(strerror(errno)); throw std::logic_error("fcntl: couldn't get flags (" + err + ")"); } return (val & O_NONBLOCK) > 0; #endif } template < class P > void socket_base

::cloexec(bool nb) { #ifdef WIN32 unsigned long cloexec = 1; // fcntl doesn't do the right thing, but the simular ioctl does // warning: is that still true? and does it the right thing for // set blocking as well? if (ioctlsocket(sock_, FIONBIO, &cloexec) != 0) { throw std::logic_error("ioctlsocket: couldn't set flags"); } #else int val = fcntl(sock_, F_GETFL, 0); if (val < 0) { throw std::logic_error("fcntl: couldn't get flags"); } int flag = (nb ? FD_CLOEXEC : 0); if (fcntl(sock_, F_SETFL, val | flag) < 0) { std::string err(strerror(errno)); throw std::logic_error("fcntl: couldn't set flags (" + err + ")"); } #endif } template < class P > bool socket_base

::cloexec() const { int val = fcntl(sock_, F_GETFL, 0); if (val < 0) { std::string err(strerror(errno)); throw std::logic_error("fcntl: couldn't get flags (" + err + ")"); } return (val & FD_CLOEXEC) > 0; } template < class P > bool socket_base

::options(int name, bool value) { const char flag = static_cast(value ? 1 : 0); return setsockopt(sock_, IPPROTO_TCP, name, &flag, sizeof(flag)) == 0; } template < class P > socket_type socket_base

::id() const { return sock_; } template < class P > void socket_base

::assign(socket_type sock) { if (is_open()) { throw std::logic_error("couldn't assign: socket already opened"); } struct sockaddr_in addr{}; socklen_t addr_size = sizeof(struct sockaddr_in); if (getpeername(sock, (struct sockaddr *)&addr, &addr_size) == 0) { //char *clientip = new char[20]; char s[INET6_ADDRSTRLEN]; os::inet_ntop(addr.sin_family, &addr.sin_addr, s, sizeof s); #ifdef _WIN32 // strcpy_s(clientip, 20, s); #else // strcpy(clientip, s); #endif } sock_ = sock; } template < class P > socket_type socket_base

::open(int family, int type, int protocol) { sock_ = ::socket(family, type, protocol); return sock_; } template < class P > socket_type connect(socket_stream

&stream, const char* hostname, unsigned short port) { char portstr[6]; sprintf(portstr, "%d", port); // const char* portname = "daytime"; struct addrinfo hints = {}; memset(&hints,0,sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; struct addrinfo* res = nullptr; struct addrinfo* head = nullptr; int err = getaddrinfo(hostname, portstr, &hints, &res); if (err != 0) { detail::throw_logic_error_with_gai_errno("failed to resolve local socket address: %s", err); } head = res; socket_type conn_fd = 0; socket_type ret = 0; do { conn_fd = ::socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (!is_valid_socket(conn_fd)) { // error, try next one continue; } ret = ::connect(conn_fd, res->ai_addr, res->ai_addrlen); if (ret == 0) { // success stream.assign(conn_fd); break; // } else { // throw_logic_error("couldn't execute: " << strerror(errno)); } // bind error, close and try next one os::shutdown(conn_fd, os::shutdown_type::READ_WRITE); } while ( (res = res->ai_next) != nullptr); if (res == nullptr) { throw_logic_error("couldn't execute to " << hostname << ":" << port); } freeaddrinfo(head); return ret; } template < class P > int connect(socket_stream

&stream, peer_base

endpoint) { auto pt = endpoint.protocol(); auto fd = ::socket(pt.family(), pt.type(), pt.protocol()); if (!is_valid_socket(fd)) { return static_cast(fd); } auto ret = ::connect(fd, endpoint.data(), static_cast(endpoint.size())); if (ret == 0) { stream.assign(fd); } else { os::shutdown(fd, os::shutdown_type::READ_WRITE); } return ret; } }