query/include/matador/net/socket.tpp

257 lines
6.0 KiB
C++

#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 <cstring>
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<P>::socket_base(const protocol_type &protocol)
{
open(protocol);
}
template < class P >
socket_base<P>::socket_base(const peer_type &peer)
{
open(peer.protocol());
}
template < class P >
socket_type socket_base<P>::open(const protocol_type &protocol)
{
return open(protocol.family(), protocol.type(), protocol.protocol());
}
template < class P >
void socket_base<P>::close()
{
if (sock_ <= 0) {
return;
}
//os::shutdown(sock_, os::shutdown_type::READ_WRITE);
os::close(sock_);
sock_ = 0;
}
template < class P >
bool socket_base<P>::is_open() const
{
return sock_ > 0;
}
template < class P >
socket_type socket_base<P>::release()
{
auto tmp_fd = sock_;
sock_ = 0;
return tmp_fd;
}
template < class P >
bool socket_base<P>::connect(const typename protocol_type::peer &p)
{
return ::connect(sock_, p.data(), static_cast<int>(p.size())) == 0;
}
template < class P >
void socket_base<P>::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<P>::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<P>::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<P>::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<P>::options(int name, bool value)
{
const char flag = static_cast<char>(value ? 1 : 0);
return setsockopt(sock_, IPPROTO_TCP, name, &flag, sizeof(flag)) == 0;
}
template < class P >
socket_type socket_base<P>::id() const
{
return sock_;
}
template < class P >
void socket_base<P>::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<P>::open(int family, int type, int protocol)
{
sock_ = ::socket(family, type, protocol);
return sock_;
}
template < class P >
socket_type connect(socket_stream<P> &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 the 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 the 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<P> &stream, peer_base<P> endpoint)
{
auto pt = endpoint.protocol();
auto fd = ::socket(pt.family(), pt.type(), pt.protocol());
if (!is_valid_socket(fd)) {
return static_cast<int>(fd);
}
auto ret = ::connect(fd, endpoint.data(), static_cast<int>(endpoint.size()));
if (ret == 0) {
stream.assign(fd);
} else {
os::shutdown(fd, os::shutdown_type::READ_WRITE);
}
return ret;
}
}