--- /dev/null
+#include "scd_socket.h"
+
+#include <sys/socket.h>
+#include <netinet/tcp.h> // for TCP_NODELAY
+#include <cerrno>
+#include <fcntl.h>
+#include <cassert>
+
+#include "scd_logging.h"
+#include "scd_exception.h"
+
+scd_socket::scd_socket()
+{
+ _socket = -1;
+ _is_connecting = false;
+ _is_connected = false;
+}
+
+scd_socket::~scd_socket() {}
+
+bool scd_socket::is_valid() const
+{
+ return (_socket != -1);
+}
+
+bool scd_socket::create()
+{
+ // check if socket already created before
+ if ( is_valid() )
+ {
+ return false;
+ }
+
+ // create TCP socket
+ _socket = socket(PF_INET, SOCK_STREAM, 0);
+
+ assert(is_valid());
+
+ _set_blocking(false);
+
+ _is_connecting = false;
+ _is_connected = false;
+
+ return true;
+
+} // create()
+
+void scd_socket::close()
+{
+ if ( is_valid() )
+ {
+ ::close(_socket);
+ }
+
+ _socket = -1;
+ _is_connecting = false;
+ _is_connected = false;
+}
+
+bool scd_socket::bind(const std::string &loc_name, const uint16_t loc_port)
+{
+ int ret;
+
+ if ( ! is_valid() )
+ {
+ return false;
+ }
+
+ // prepare the local address
+ sockaddr_in loc_addr;
+ if ( !_get_sockaddr(loc_addr, loc_name, loc_port) )
+ {
+ return false;
+ }
+
+ // set socket reusable (to recreate listener without timeout)
+ int on = 1;
+ ret = setsockopt( _socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );
+ if (ret == -1)
+ {
+ throw scd_exception("setsockopt()", errno);
+ }
+
+ ret = ::bind( _socket, reinterpret_cast<struct sockaddr*>(&loc_addr),
+ sizeof( loc_addr ) );
+
+ if ( ret == -1 )
+ {
+ switch(errno)
+ {
+ case EACCES:
+ scd_error("must be root to bind to desired port");
+ break;
+ case EADDRINUSE:
+ scd_error("unable to bind as the address is already in use");
+ break;
+ default:
+ throw scd_exception("bind()", errno);
+ }
+ return false;
+ }
+
+ return true;
+
+} // bind()
+
+
+bool scd_socket::bind(const uint16_t loc_port)
+{
+ return bind("", loc_port);
+} // bind()
+
+
+bool scd_socket::listen()
+{
+ int ret;
+
+ if ( !is_valid() )
+ {
+ return false;
+ }
+
+ ret = ::listen(_socket, SCD_MAXCONN);
+
+ if (ret == -1)
+ {
+ throw scd_exception("listen()", errno);
+ }
+
+ return true;
+} // listen()
+
+bool scd_socket::accept(scd_socket &new_sock)
+{
+ int ret;
+
+ if ( !is_valid() )
+ {
+ return false;
+ }
+
+ ret = ::accept(_socket, NULL, NULL);
+
+ if (ret < 0)
+ {
+ switch (errno)
+ {
+ case EAGAIN:
+ case ECONNABORTED:
+ case EINTR:
+ case EPROTO:
+ return false;
+ break;
+ default:
+ throw scd_exception("accept()", errno);
+ break;
+ }
+ }
+ else
+ {
+ new_sock._is_connected = true;
+ new_sock._socket = ret;
+ new_sock._set_blocking(false);
+ #ifdef TCP_NODELAY
+ new_sock._set_nodelay();
+ #endif
+ }
+
+ return true;
+
+} // accept()
+
+
+bool scd_socket::connect(const std::string &rem_name, const uint16_t rem_port)
+{
+ int ret;
+
+ if ( !is_valid() || _is_connecting || _is_connected )
+ return false;
+
+ sockaddr_in rem_addr;
+ if ( !_get_sockaddr(rem_addr, rem_name, rem_port) )
+ return false;
+
+ ret = ::connect(_socket, reinterpret_cast<const sockaddr*>(&rem_addr),
+ sizeof(rem_addr));
+
+ if (ret == -1)
+ {
+ switch(errno)
+ {
+ case EINPROGRESS:
+ _is_connecting = true;
+ break;
+ case ECONNABORTED:
+ case ECONNREFUSED:
+ case EINTR:
+ case ENETUNREACH:
+ case ETIMEDOUT:
+ break;
+ case EALREADY: // should be prevented by _is_connecting
+ default:
+ throw scd_exception("connect()", errno);
+ break;
+ }
+ }
+ else // ret == 0
+ {
+ _is_connected = true;
+ _is_connecting = false;
+ }
+
+ return true;
+
+} // connect()
+
+bool scd_socket::is_connecting()
+{
+ if ( !is_valid() || !_is_connecting || _is_connected )
+ return false;
+
+ return true;
+
+} // is_connecting()
+
+
+bool scd_socket::is_connected()
+{
+ if ( !is_valid() || _is_connecting || !_is_connected)
+ return false;
+
+ return true;
+
+} // is_connecting()
+
+
+bool scd_socket::connected_event()
+{
+ if (_is_connected)
+ return true;
+
+ if ( !is_valid() )
+ {
+ scd_warn("write event occured on an invalid socket");
+ return false;
+ }
+ else if ( !_is_connecting )
+ {
+ scd_warn("checking connected event while not connecting");
+ return false;
+ }
+
+ // get possible errors from connection attempt
+ int err, ret;
+ socklen_t err_size = sizeof(err);
+ ret = getsockopt(_socket, SOL_SOCKET, SO_ERROR, &err, &err_size);
+
+ if (ret == -1)
+ {
+ throw scd_exception("getsockopt()", errno);
+ }
+
+ // handle errors
+ switch(err)
+ {
+ case 0:
+ // connection established
+ _is_connecting = false;
+ _is_connected = true;
+ #ifdef TCP_NODELAY
+ _set_nodelay();
+ #endif
+ return true;
+ break;
+ case ECONNABORTED:
+ case ECONNREFUSED:
+ case EINTR:
+ case ENETUNREACH:
+ case ETIMEDOUT:
+ // connection attempt failed
+ _is_connecting = false;
+ return false;
+ break;
+ case EINPROGRESS:
+ // should not be possible as we received a completion event
+ throw scd_exception("checking connected event while still connecting");
+ break;
+ case EALREADY: // should be prevented by _is_connecting
+ default:
+ throw scd_exception("connect()", errno);
+ break;
+ }
+
+ // not reached
+} //connected_event()
+
+
+size_t scd_socket::send(const void* buf, size_t len)
+{
+ ssize_t ret;
+
+ if ( !is_valid() || len == 0 || !_is_connected )
+ return 0;
+
+ ret = ::send(_socket, buf, len, MSG_NOSIGNAL);
+
+ if (ret < 0)
+ {
+ switch(errno)
+ {
+ case ECONNRESET:
+ case EPIPE:
+ case ETIMEDOUT:
+ close();
+ break;
+ case EWOULDBLOCK:
+ break;
+ default:
+ throw scd_exception("send()", errno);
+ break;
+ }
+ return 0;
+ }
+ else
+ return ret;
+
+} // send()
+
+size_t scd_socket::recv(void* buf, size_t len)
+{
+ ssize_t ret;
+
+ if ( !is_valid() || len == 0 || !_is_connected)
+ return 0;
+
+ ret = ::recv(_socket, buf, len, MSG_NOSIGNAL);
+
+ if (ret < 0)
+ {
+ switch(errno)
+ {
+ case ETIMEDOUT:
+ close();
+ break;
+ case EAGAIN:
+ case EINTR:
+ break;
+ case ENOTCONN:
+ default:
+ throw scd_exception("recv()", errno);
+ break;
+ }
+ return 0;
+ }
+ else if (ret == 0)
+ {
+ close();
+ return 0;
+ }
+ else
+ return ret;
+
+} // recv()
+
+bool scd_socket::_get_sockaddr(sockaddr_in &addr, const std::string &name,
+ const uint16_t port) const
+{
+ // flush addr
+ memset( &addr, 0, sizeof(addr) );
+
+ /* set content */
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons( port );
+ if ( name.empty() )
+ {
+ // bind to all local addresses
+ addr.sin_addr.s_addr = INADDR_ANY;
+ }
+ else
+ {
+ // bint to specified address only
+ struct hostent* he;
+
+ // resolve name
+ he = gethostbyname2(&name[0], AF_INET);
+ if (he == NULL)
+ return false;
+
+ // copy address (network order)
+ addr.sin_addr.s_addr =
+ reinterpret_cast<struct in_addr*>(he->h_addr_list[0])->s_addr;
+ }
+
+ return true;
+
+} // _get_sockaddr()
+
+
+bool scd_socket::_set_blocking(const bool mode)
+{
+ if ( !is_valid() )
+ {
+ return false;
+ }
+
+ int opts = fcntl( _socket, F_GETFL );
+
+ if ( opts < 0 )
+ {
+ throw scd_exception("fcntl()", errno);
+ }
+
+ if ( !mode )
+ opts = ( opts | O_NONBLOCK );
+ else
+ opts = ( opts & ~O_NONBLOCK );
+
+ fcntl( _socket, F_SETFL,opts );
+
+ return true;
+
+} //_set_blocking()
+
+
+bool scd_socket::_set_nodelay()
+{
+ if ( !is_valid())
+ return false;
+
+ // disable Nagle algorithm
+ int on = 1;
+ int ret = setsockopt( _socket, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on) );
+ if (ret == -1)
+ {
+ throw scd_exception("setsockopt()", errno);
+ }
+
+ return true;
+}