query/include/matador/net/socket_acceptor.hpp

358 lines
9.1 KiB
C++

#ifndef MATADOR_SOCKET_ACCEPTOR_HPP
#define MATADOR_SOCKET_ACCEPTOR_HPP
#include "matador/net/socket.hpp"
namespace matador {
/**
* The socket acceptor class provides the
* base functionality of the socket base class
* plus acceptor specific functions like
* listen, bind or accept
*
* @tparam P Socket protocol type
*/
template < class P >
class socket_acceptor : public socket_base<P>
{
public:
typedef socket_base<P> base; /**< Shortcut to base socket type */
typedef socket_stream<P> stream_type; /**< Shortcut to socket stream type */
typedef typename base::protocol_type protocol_type; /**< Shortcut to protocol type */
typedef typename base::peer_type peer_type; /**< Shortcut to peer type */
/**
* Default constructor
*/
socket_acceptor() = default;
/**
* Constructs a socket acceptor for
* the given peer
*
* @param peer Peer to construct an acceptor from
*/
explicit socket_acceptor(peer_type &peer);
/**
* Constructs an acceptor for given hostname and port
*
* @param hostname Hostname of the accepting endpoint
* @param port Portnumber of the accepting endpoint
*/
socket_acceptor(const char* hostname, unsigned short port);
/**
* Creates a listening socket and binds
* the given hostname and port to it
*
* Returns zero (0) on success and -1 on error
* with errno set
*
* @param hostname Hostname to bind
* @param port Portnumber to bind
* @return Returns zero (0) on success.
*/
int bind(const char* hostname, unsigned short port);
/**
* Creates a listening socket and binds
* the given peer endpoint to it
*
* Returns zero (0) on success and -1 on error
* with errno set
*
* @param peer Binds the given peer endpoint to the socket
* @return Returns zero (0) on success.
*/
int bind(peer_type &peer);
/**
* Start listening to the bound endpoint using
* the internally created socket.
*
* Returns zero (0) on success and -1 on error
* with errno set
*
* @param backlog Number of backlog
* @return Returns zero (0) on success.
*/
int listen(int backlog);
/**
* Returns a pointer to the underlying
* concrete internet address of the given
* socket address structure
*
* @param sa Socket address
* @return Pointer to the internet address
*/
void* get_in_addr(struct sockaddr *sa);
/**
* Returns the port number of the given
* socket address structure
*
* @param sa Socket address
* @return The port number
*/
unsigned short get_port(struct sockaddr *sa);
/**
* Get the remote address and port as string
* representation.
*
* @param remote_addr Remote socket address structure
* @return String representation of the remote address
*/
std::string get_remote_address(struct sockaddr_storage &remote_addr);
/**
* Accept a connection and assign the socket descriptor
* to the given socket stream.
*
* Once the descriptor is assigned to the stream it
* can be used to read and write data to it.
*
* @param stream Stream to use after connection was accepted
* @return The fd of the new connection
*/
int accept(stream_type &stream);
/**
* Accept a connection and assign the socket descriptor
* to the given socket stream.
*
* Once the descriptor is assigned to the stream it
* can be used to read and write data to it.
*
* The given peer endpoint is filled with the
* address information of the remote endpoint.
*
* @param stream Stream to use after connection was accepted
* @param endpoint Will be filled with the remote endpoint information
* @return The fd of the new connection
*/
int accept(stream_type &stream, peer_type &endpoint);
/**
* Sets or clears the reuse address flag. If the
* given value is true the flag is set otherwise the
* flag is cleared.
*
* @param reuse Indicates if the reuse address flag should be set
* @return 0 if setting was successful, -1 on error
*/
int reuse_address(bool reuse);
/**
* Returns true if the flag is set otherwise
* false is returned.
*
* @return True if flag is set
*/
bool reuse_address() const;
};
/// @cond MATADOR_DEV
template < class P >
socket_acceptor<P>::socket_acceptor(peer_type &peer)
: socket_base<P>(peer)
{
bind(peer);
}
template < class P >
socket_acceptor<P>::socket_acceptor(const char* hostname, unsigned short port)
{
bind(hostname, port);
}
template < class P >
int socket_acceptor<P>::bind(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_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
struct addrinfo* res = nullptr;
struct addrinfo* head = nullptr;
int err = getaddrinfo(hostname, portstr, &hints, &res);
if (err != 0) {
throw_logic_error("failed to resolve local socket address (error: " << err << ")");
}
head = res;
int listenfd = 0;
int ret = 0;
const int on = 1;
do {
listenfd = ::socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (listenfd < 0) {
// error, try next one
continue;
}
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
throw std::logic_error(strerror(errno));
}
ret = ::bind(listenfd, res->ai_addr, res->ai_addrlen);
if (ret == 0) {
// success
this->assign(listenfd);
break;
} else {
throw_logic_error("couldn't bind to " << hostname << ":" << port << ": " << strerror(errno));
}
// bind error, close and try next one
this->close();
} while ( (res = res->ai_next) != nullptr);
if (res == nullptr) {
throw_logic_error("couldn't bind to " << hostname << ":" << port);
}
freeaddrinfo(head);
return ret;
}
template < class P >
int socket_acceptor<P>::bind(peer_type &peer)
{
socket_type listen_fd = ::socket(peer.protocol().family(), peer.protocol().type(), peer.protocol().protocol());
if (!is_valid_socket(listen_fd)) {
// error, try next one
return static_cast<int>(listen_fd);
}
#ifdef _WIN32
const char on = 1;
#else
const int on = 1;
#endif
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
detail::throw_logic_error_with_errno("setsockopt error: %s", errno);
}
int ret = ::bind(listen_fd, peer.data(), static_cast<int>(peer.size()));
if (ret == 0) {
// success
this->assign(listen_fd);
} else {
detail::throw_logic_error_with_errno("couldn't bind fd: %s", errno);
}
size_t s = peer.size();
ret = getsockname(this->id(), peer.data(), (socklen_t*)&s);
return ret;
}
template < class P >
int socket_acceptor<P>::listen(int backlog)
{
return ::listen(this->id(), backlog);
}
template < class P >
void* socket_acceptor<P>::get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
} else {
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
}
template < class P >
unsigned short socket_acceptor<P>::get_port(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return ntohs(((struct sockaddr_in*)sa)->sin_port);
} else {
return ntohs(((struct sockaddr_in6*)sa)->sin6_port);
}
}
template < class P >
std::string socket_acceptor<P>::get_remote_address(struct sockaddr_storage &remote_addr)
{
char s[INET6_ADDRSTRLEN];
os::inet_ntop(remote_addr.ss_family,
get_in_addr((struct sockaddr *)&remote_addr),
s, sizeof s);
std::stringstream ra;
ra << s << ":" << get_port((struct sockaddr *)&remote_addr);
return ra.str();
}
template < class P >
int socket_acceptor<P>::accept(stream_type &stream)
{
struct sockaddr_storage remote_addr = {};
// address_type remote_addr;
socklen_t addrlen = sizeof(remote_addr);
auto fd = ::accept(this->id(), (struct sockaddr *)&remote_addr, &addrlen);
if (is_valid_socket(fd)) {
stream.assign(fd);
stream.non_blocking(true);
stream.cloexec(true);
// } else {
// detail::throw_logic_error_with_errno("accept failed: %s", errno);
}
return static_cast<int>(fd);
}
template<class P>
int socket_acceptor<P>::accept(stream_type &stream, peer_type &endpoint)
{
auto addr_len = static_cast<socklen_t>(endpoint.size());
auto fd = ::accept(this->id(), endpoint.data(), &addr_len);
if (is_valid_socket(fd)) {
stream.assign(fd);
stream.non_blocking(true);
stream.cloexec(true);
// } else {
// detail::throw_logic_error_with_errno("accept failed: %s", errno);
}
return static_cast<int>(fd);
}
template < class P >
int socket_acceptor<P>::reuse_address(bool reuse)
{
const int option(reuse ? 1 : 0);
return setsockopt(this->id(), SOL_SOCKET, SO_REUSEADDR, (char*)&option, sizeof(option));
}
template < class P >
bool socket_acceptor<P>::reuse_address() const
{
size_t option {};
socklen_t i;
i = sizeof(option);
getsockopt(this->id(), SOL_SOCKET, SO_REUSEADDR, (char*)&option, &i);
return option > 0;
}
/// @endcond
}
#endif //MATADOR_SOCKET_ACCEPTOR_HPP