--- /dev/null
+
+/*] Copyright (c) 2009-2010, Charles McGarvey [**************************
+**] All rights reserved.
+*
+* vi:ts=4 sw=4 tw=75
+*
+* Distributable under the terms and conditions of the 2-clause BSD license;
+* see the file COPYING for a complete text of the license.
+*
+**************************************************************************/
+
+#ifndef _MOOF_SOCKET_HH_
+#define _MOOF_SOCKET_HH_
+
+/**
+ * \file socket.hh
+ * Network-related classes, including a reinterpreted sockets API.
+ */
+
+#include <algorithm>
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#error No alternative to fcntl implemented yet.
+#endif
+
+#if defined(_WIN32)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <wspiapi.h>
+#define SHUT_RD SD_RECEIVE
+#define SHUT_WR SD_SEND
+#define SHUT_RDWR SD_BOTH
+#else
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#endif
+
+#include <moof/log.hh>
+#include <moof/packet.hh>
+#include <moof/thread.hh>
+
+
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0
+#endif
+
+#ifndef AI_V4MAPPED
+#define AI_V4MAPPED 0
+#endif
+
+
+namespace moof {
+
+
+/**
+ * The socket class represents a connection or between this node and a
+ * remote node.
+ */
+class socket
+{
+public:
+
+ /**
+ * A class to represent the address of a remote host, including the
+ * type of service and socket communication.
+ */
+ class address
+ {
+ public:
+
+ /**
+ * Construct an unspecified address.
+ */
+ address() :
+ size_(0),
+ type_(0)
+ {
+ addr_.sa.sa_family = AF_UNSPEC;
+ addr_.in.sin_port = 0;
+ }
+
+ /**
+ * Construct an address with a specified host. The address can be
+ * used to connect to a host.
+ * \param service The service name or port number.
+ * \param name The numeric IP address of the host.
+ * \param type The type of socket; either SOCK_STREAM or
+ * SOCK_DGRAM.
+ * \param family The family; can be AF_INET or AF_INET6.
+ */
+ address(const std::string& service,
+ const std::string& name,
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC)
+ {
+ init(service, name, type, family);
+ }
+
+ /**
+ * Construct an address without a specified host. The address can
+ * be used to accept on a local port.
+ * \param service The service name or port number.
+ * \param type The type of socket; either SOCK_STREAM or
+ * SOCK_DGRAM.
+ * \param family The family; can be AF_INET or AF_INET6.
+ */
+ explicit address(const std::string& service,
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC)
+ {
+ init(service, type, family);
+ }
+
+ /**
+ * Construct an address from the information in an addrinfo
+ * structure.
+ * \param addr The addrinfo structure.
+ */
+ address(const struct addrinfo* addr) :
+ size_(addr->ai_addrlen),
+ type_(addr->ai_socktype)
+ {
+ memcpy(&addr_.sa, addr->ai_addr, addr->ai_addrlen);
+ get_name_and_service(name_, service_);
+ }
+
+ /**
+ * Construct an address from a sockaddr structure.
+ * \param addr The sockaddr structure.
+ * \param size The size of the sockaddr structure.
+ * \param type The type of socket; either SOCK_STREAM or
+ * SOCK_DGRAM.
+ */
+ address(const struct sockaddr* addr,
+ size_t size,
+ int type = SOCK_STREAM) :
+ size_(size),
+ type_(type)
+ {
+ memcpy(&addr_.sa, addr, size);
+ get_name_and_service(name_, service_);
+ }
+
+
+ /**
+ * Get an IPv4 broadcast address.
+ * \param service The service name or port number.
+ * \return The socket address.
+ */
+ static address broadcast(const std::string& service)
+ {
+ std::istringstream stream(service);
+ unsigned short port;
+ stream >> port;
+
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = INADDR_BROADCAST;
+ memset(&addr.sin_zero, 0, sizeof(addr.sin_zero));
+ return address((struct sockaddr*)&addr, sizeof(addr),
+ SOCK_DGRAM);
+ }
+
+
+ /**
+ * Initialize the address with a specified host. The address can
+ * be used to connect to a host.
+ * \param service The service name or port number.
+ * \param name The numeric IP address of the host.
+ * \param type The type of socket; either SOCK_STREAM or
+ * SOCK_DGRAM.
+ * \param family The family; can be AF_INET or AF_INET6.
+ */
+ void init(const std::string& service,
+ const std::string& name,
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC)
+ {
+ const int flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_V4MAPPED;
+ struct addrinfo* addr = resolve(service.c_str(), name.c_str(),
+ type, family,
+ flags);
+ if (addr)
+ {
+ size_ = addr->ai_addrlen;
+ type_ = addr->ai_socktype;
+ memcpy(&addr_.sa, addr->ai_addr, addr->ai_addrlen);
+
+ service_ = service;
+ name_ = name;
+
+ freeaddrinfo(addr);
+ }
+ else
+ {
+ type_ = 0;
+ size_ = 0;
+ addr_.sa.sa_family = AF_UNSPEC;
+ addr_.in.sin_port = 0;
+ }
+ }
+
+ /**
+ * Initialize the address without a specified host. The address
+ * can be used to accept on a local port.
+ * \param service The service name or port number.
+ * \param type The type of socket; either SOCK_STREAM or
+ * SOCK_DGRAM.
+ * \param family The family; can be AF_INET or AF_INET6.
+ */
+ void init(const std::string& service,
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC)
+ {
+ struct addrinfo* addr = resolve(service.c_str(), 0,
+ type, family,
+ AI_PASSIVE);
+ if (addr)
+ {
+ size_ = addr->ai_addrlen;
+ type_ = addr->ai_socktype;
+ memcpy(&addr_.sa, addr->ai_addr, addr->ai_addrlen);
+
+ service_ = service;
+ get_name(name_);
+
+ freeaddrinfo(addr);
+ }
+ else
+ {
+ type_ = 0;
+ size_ = 0;
+ addr_.sa.sa_family = AF_UNSPEC;
+ addr_.in.sin_port = 0;
+ }
+ }
+
+
+ /**
+ * Get the name of the service. This could also be a port number
+ * if there is no service name associated with the number.
+ * \return The service.
+ */
+ const std::string& service() const
+ {
+ return service_;
+ }
+
+ /**
+ * Get the name of the host. This may be the host used to
+ * construct the address, or a resolved numeric host if none was
+ * used.
+ * \return The host.
+ */
+ const std::string& name() const
+ {
+ return name_;
+ }
+
+ /**
+ * Get the port number of the address service.
+ * \return Port number.
+ */
+ unsigned short port() const
+ {
+ return ntohs(addr_.in.sin_port);
+ }
+
+ /**
+ * Get the type of socket associated with the service of this
+ * address.
+ * \return Socket type; either SOCK_STREAM or SOCK_DGRAM.
+ */
+ int type() const
+ {
+ return type_;
+ }
+
+ /**
+ * Get the family of the protocol associated with the address.
+ * \return Protocol family; either AF_INET, AF_INET6, or AF_UNSPEC.
+ */
+ int family() const
+ {
+ return addr_.sa.sa_family;
+ }
+
+
+ /**
+ * Get the sockaddr structure of the address.
+ * \return The sockaddr structure.
+ */
+ const struct sockaddr* sockaddr() const
+ {
+ return size_ != 0 ? &addr_.sa : 0;
+ }
+
+ /**
+ * Get the size of the sockaddr structure of the address.
+ * \return The size of the sockaddr structure.
+ */
+ size_t size() const
+ {
+ return size_;
+ }
+
+
+ /**
+ * Get a list of addresses resolved to by the given search
+ * criteria. This can be used to perform lookups for name
+ * resolution, so this method may take some time to return. Use
+ * the ResolveTask class to resolve addresses asynchronously.
+ * \param service The service name or port number.
+ * \param name The name of the local or remote host.
+ * \param type The type of socket; either SOCK_STREAM or
+ * SOCK_DGRAM.
+ * \param family The family; can be AF_INET or AF_INET6.
+ * \param resolved The list to be filled with addresses.
+ * \return 0 on success, -1 on error.
+ */
+ static int resolve(const std::string& service,
+ const std::string& name,
+ int type,
+ int family,
+ std::vector<address>& resolved)
+ {
+ struct addrinfo* list = resolve(service.c_str(), name.c_str(),
+ type, family,
+ AI_ADDRCONFIG | AI_V4MAPPED);
+ int result = collect_addresses(list, resolved);
+ freeaddrinfo(list);
+ return result;
+ }
+
+ /**
+ * Get a list of addresses resolved to by the given search
+ * criteria. The addresses will be suitable for accepting on a
+ * local port. \param service The service name or port number.
+ * \param type The type of socket; either SOCK_STREAM or
+ * SOCK_DGRAM.
+ * \param family The family; can be AF_INET or AF_INET6.
+ * \param resolved The list to be filled with addresses.
+ * \return 0 on success, -1 on error.
+ */
+ static int resolve(const std::string& service,
+ int type,
+ int family,
+ std::vector<address>& resolved)
+ {
+ struct addrinfo* list = resolve(service.c_str(), 0,
+ type, family,
+ AI_PASSIVE);
+ int result = collect_addresses(list, resolved);
+ freeaddrinfo(list);
+ return result;
+ }
+
+
+ /**
+ * Resolve the hostname of the address. The default behavior is to
+ * avoid a reverse lookup by giving the numeric address. You can
+ * change that behavior with the getnameinfo flags.
+ * \param name The place to store the hostname or IP address.
+ * \param flags The getnameinfo flags.
+ */
+ void get_name(std::string& name, int flags = NI_NUMERICHOST)
+ {
+ char node[256] = {'\0'};
+ int result = getnameinfo(&addr_.sa, size_,
+ node, sizeof(node),
+ 0, 0,
+ flags);
+ if (result == 0) name.assign(node);
+ }
+
+ /**
+ * Resolve the service name of the address.
+ * \param service The place to store the service name or port
+ * number.
+ * \param flags The getnameinfo flags.
+ */
+ void get_service(std::string& service, int flags)
+ {
+ flags |= type_ == SOCK_DGRAM ? NI_DGRAM : 0;
+
+ char serv[64] = {'\0'};
+ int result = getnameinfo(&addr_.sa, size_,
+ 0, 0,
+ serv, sizeof(serv),
+ flags);
+ if (result == 0) service.assign(serv);
+ }
+
+ /**
+ * Resolve the service and hostname of the address. The default
+ * behavior is to avoid a reverse lookup by giving the numeric
+ * address. You can change that behavior with the getnameinfo
+ * flags.
+ * \param name The place to store the hostname or IP address.
+ * \param service The place to store the service name or port
+ * number.
+ * \param flags The getnameinfo flags.
+ */
+ void get_name_and_service(std::string& name,
+ std::string& service,
+ int flags = NI_NUMERICHOST)
+ {
+ flags |= type_ == SOCK_DGRAM ? NI_DGRAM : 0;
+
+ char serv[64] = {'\0'};
+ char node[256] = {'\0'};
+ int result = getnameinfo(&addr_.sa, size_,
+ node, sizeof(node),
+ serv, sizeof(serv),
+ flags);
+ if (result == 0)
+ {
+ service.assign(serv);
+ name.assign(node);
+ }
+ }
+
+
+ private:
+
+ static struct addrinfo* resolve(const char* service,
+ const char* node,
+ int type,
+ int family,
+ int flags)
+ {
+ ASSERT(type == SOCK_STREAM || type == SOCK_DGRAM);
+ ASSERT(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC);
+
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = type;
+ hints.ai_flags = flags;
+
+ struct addrinfo* addr;
+ int status = getaddrinfo(node, service, &hints, &addr);
+
+ if (status == 0)
+ {
+ return addr;
+ }
+ else
+ {
+ log_warning(gai_strerror(status));
+ return 0;
+ }
+ }
+
+ static int collect_addresses(struct addrinfo* addresses,
+ std::vector<address>& resolved)
+ {
+ if (addresses)
+ {
+ resolved.clear();
+
+ for (struct addrinfo* addr = addresses;
+ addr != 0;
+ addr = addr->ai_next)
+ {
+ resolved.push_back(address(addr));
+ }
+
+ return 0;
+ }
+ else return -1;
+ }
+
+
+ union
+ {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_storage storage;
+ } addr_;
+ size_t size_;
+ int type_;
+
+ std::string name_;
+ std::string service_;
+ };
+
+
+private:
+
+ struct impl
+ {
+ socket::address address;
+ int fd;
+ bool is_connected;
+
+ impl() :
+ fd(-1),
+ is_connected(false) {}
+
+ impl(const socket::address& address, int flags = 0) :
+ address(address),
+ fd(::socket(address.family(), address.type(), flags)),
+ is_connected(false) {}
+ } impl_;
+
+
+public:
+
+ /**
+ * Construct a socket with no associated peer.
+ */
+ socket() {}
+
+ /**
+ * Construct a socket with an address.
+ * \param address The address.
+ * \param flags The socket options.
+ */
+ socket(const address& address, int flags = 0) :
+ impl_(address, flags) {}
+
+ /**
+ * Construct a socket with a specified host. The socket can be used to
+ * connect to a host.
+ * \param service The service name or port number.
+ * \param name The numeric IP address of the host.
+ * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
+ * \param family The family; can be AF_INET or AF_INET6.
+ * \param flags The socket options.
+ */
+ socket(const std::string& service,
+ const std::string& name,
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC,
+ int flags = 0) :
+ impl_(address(service, name, type, family), flags) {}
+
+ /**
+ * Construct a socket without a specified host. The socket can be used
+ * to accept sockets on a local port.
+ * \param service The service name or port number.
+ * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
+ * \param family The family; can be AF_INET or AF_INET6.
+ * \param flags The socket options.
+ */
+ explicit socket(const std::string& service,
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC,
+ int flags = 0) :
+ impl_(address(service, type, family), flags) {}
+
+
+ /**
+ * Deconstruct the socket, closing it.
+ */
+ ~socket()
+ {
+ close();
+ }
+
+
+ /**
+ * Get whether or not the socket is connected.
+ * \return True if the socket is connected, false otherwise.
+ */
+ bool is_connected() const
+ {
+ return impl_.is_connected;
+ }
+
+ /**
+ * Get the address associated with the socket.
+ */
+ const address& peer_address() const
+ {
+ return impl_.address;
+ }
+
+
+ /**
+ * Connect the socket to its peer.
+ * \return 0 on success, -1 on failure.
+ */
+ int connect()
+ {
+ int result = ::connect(impl_.fd,
+ impl_.address.sockaddr(),
+ impl_.address.size());
+ impl_.is_connected = result != -1;
+ return result;
+ }
+
+ /**
+ * Disconnect a connected socket from its peer.
+ * \param flags Specify the socket directions to close.
+ * \return 0 on success, -1 on failure.
+ */
+ int disconnect(int flags = SHUT_RDWR)
+ {
+ return shutdown(impl_.fd, flags);
+ }
+
+
+ /**
+ * Bind the socket to interface and port number specified in the
+ * address.
+ * \return 0 on success, -1 on failure.
+ */
+ int bind()
+ {
+ return ::bind(impl_.fd,
+ impl_.address.sockaddr(),
+ impl_.address.size());
+ }
+
+ /**
+ * Listen on the socket for incoming connections. This is only useful
+ * for sockets of type SOCK_STREAM.
+ * \param backlog The number of unaccepted connections to queue.
+ * \return 0 on success, -1 on failure.
+ */
+ int listen(int backlog = SOMAXCONN)
+ {
+ return ::listen(impl_.fd, backlog > 0 ? backlog : SOMAXCONN);
+ }
+
+ /**
+ * Accept a new connection on the socket. This is only useful for
+ * sockets of type SOCK_STREAM.
+ * \param socket Set to the new socket on return.
+ * \return 0 on success, -1 on failure.
+ */
+ int accept(socket& socket)
+ {
+ moof::socket temp = moof::socket(impl_.fd);
+ if (temp.impl_.fd != -1)
+ {
+ socket = temp;
+ return socket.impl_.fd;
+ }
+ return -1;
+ }
+
+
+ /**
+ * Set an integer socket option.
+ * \param option The option to set.
+ * \param value The new value.
+ * \param level The layer to handle the option.
+ * \return 0 on success, -1 on failure.
+ */
+ template <class T>
+ int set(int option, const T& value, int level = SOL_SOCKET)
+ {
+#if defined(_WIN32)
+ return setsockopt(impl_.fd,
+ level,
+ option,
+ reinterpret_cast<const char*>(&value),
+ sizeof(value));
+#else
+ return setsockopt(impl_.fd, level, option, &value, sizeof(value));
+#endif
+ }
+
+ /**
+ * Set a string socket option.
+ * \param option The option to set.
+ * \param value The new value.
+ * \param level The layer to handle the option.
+ * \return 0 on success, -1 on failure.
+ */
+ int set(int option, const std::string& value, int level = SOL_SOCKET)
+ {
+ return setsockopt(impl_.fd, level, option,
+ value.data(), value.length());
+ }
+
+ /**
+ * Get an integer socket option.
+ * \param option The option to set.
+ * \param value The new value.
+ * \param level The layer to handle the option.
+ * \return 0 on success, -1 on failure.
+ */
+ template <class T>
+ int get(int option, T& value, int level = SOL_SOCKET) const
+ {
+ int size = sizeof(value);
+ return getsockopt(impl_.fd, level, option, &value, &size);
+ }
+
+ /**
+ * Get a string socket option.
+ * \param option The option to set.
+ * \param value The new value.
+ * \param level The layer to handle the option.
+ * \return 0 on success, -1 on failure.
+ */
+ int get(int option, std::string& value, int level = SOL_SOCKET) const
+ {
+ char str[256] = {'\0'};
+ socklen_t size = sizeof(str);
+
+#if defined(_WIN32)
+ int result = getsockopt(impl_.fd,
+ level,
+ option,
+ reinterpret_cast<char*>(&str),
+ &size);
+#else
+ int result = getsockopt(impl_.fd, level, option, &str, &size);
+#endif
+ value.assign(str, size);
+ return result;
+ }
+
+
+ /**
+ * Set the socket IO mode to either blocking or non-blocking.
+ * \param is_blocking True if the socket should block, false otherwise.
+ */
+ void is_blocking(bool is_blocking)
+ {
+#ifdef HAVE_FCNTL
+ int flags = fcntl(impl_.fd, F_GETFL);
+ flags = is_blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
+ fcntl(impl_.fd, F_SETFL, flags);
+#elif defined(_WIN32)
+ u_long value = is_blocking;
+ ioctlsocket(impl_.fd, FIONBIO, &value);
+#endif
+ }
+
+ /**
+ * Get whether or not the socket is blocking or non-blocking. If the
+ * IO mode can't be determined, this method will assume the socket is
+ * a blocking socket.
+ * \return True if the socket blocks, false otherwise.
+ */
+ bool is_blocking() const
+ {
+#ifdef HAVE_FCNTL
+ int flags = fcntl(impl_.fd, F_GETFL);
+ return !(flags & O_NONBLOCK);
+#endif
+ return true;
+ }
+
+
+ /**
+ * Write some bytes to the socket. Use this for connected sockets.
+ * \param bytes The bytes.
+ * \param size The number of bytes.
+ * \param flags The send options.
+ * \return The number of bytes written.
+ */
+ ssize_t write(const void* bytes, size_t size, int flags = 0)
+ {
+#if defined(_WIN32)
+ return send(impl_.fd,
+ reinterpret_cast<const char *>(bytes), size,
+ flags);
+#else
+ return send(impl_.fd, bytes, size, flags);
+#endif
+ }
+
+ /**
+ * Write some bytes to the socket using the given address. Use this
+ * for unconnected sockets.
+ * \param bytes The bytes.
+ * \param size The number of bytes.
+ * \param address The address to send to.
+ * \param flags The send options.
+ * \return The number of bytes written.
+ */
+ ssize_t write(const void* bytes,
+ size_t size,
+ const address& address,
+ int flags = 0)
+ {
+#if defined(_WIN32)
+ return sendto(impl_.fd,
+ reinterpret_cast<const char*>(bytes), size,
+ flags,
+ address.sockaddr(), address.size());
+#else
+ return sendto(impl_.fd, bytes, size, flags,
+ address.sockaddr(), address.size());
+#endif
+ }
+
+ /**
+ * Write a packet to the socket. Use this for connected sockets.
+ * \param packet The packet.
+ * \param flags The send options.
+ * \return The number of bytes written.
+ */
+ ssize_t write(const packet& packet, int flags = 0)
+ {
+ return write(packet.bytes(), packet.size(), flags);
+ }
+
+ /**
+ * Write a packet to the socket using the given address. Use this for
+ * unconnected sockets.
+ * \param packet The packet.
+ * \param address The address to send to.
+ * \param flags The send options.
+ * \return The number of bytes written.
+ */
+ ssize_t write(const packet& packet,
+ const address& address,
+ int flags = 0)
+ {
+ return write(packet.bytes(), packet.size(), address, flags);
+ }
+
+
+ /**
+ * Read some bytes from the socket. Use this for connected sockets.
+ * \param bytes The buffer to store the bytes.
+ * \param size The size of the buffer.
+ * \param flags The recv options.
+ * \return The number of bytes read.
+ */
+ ssize_t read(void* bytes, size_t size, int flags = 0)
+ {
+#if defined(_WIN32)
+ ssize_t result = recv(impl_.fd,
+ reinterpret_cast<char*>(bytes), size,
+ flags);
+#else
+ ssize_t result = recv(impl_.fd, bytes, size, flags);
+#endif
+ if (result == 0) impl_.is_connected = false;
+ return result;
+ }
+
+ /**
+ * Read some bytes from the socket using the given address. Use this
+ * for unconnected sockets.
+ * \param bytes The buffer to store the bytes.
+ * \param size The size of the buffer.
+ * \param address The address to read from.
+ * \param flags The recv options.
+ * \return The number of bytes read.
+ */
+ ssize_t read(void* bytes,
+ size_t size,
+ socket::address& address,
+ int flags = 0)
+ {
+ union
+ {
+ struct sockaddr sa;
+ struct sockaddr_storage storage;
+ } addr;
+ socklen_t length = sizeof(addr);
+
+#if defined(_WIN32)
+ ssize_t result = recvfrom(impl_.fd,
+ reinterpret_cast<char*>(bytes), size,
+ flags,
+ &addr.sa, &length);
+#else
+ ssize_t result = recvfrom(impl_.fd, bytes, size, flags,
+ &addr.sa, &length);
+#endif
+ if (result != -1)
+ {
+ address = socket::address(&addr.sa, length,
+ impl_.address.type());
+ }
+ else if (result == 0)
+ {
+ impl_.is_connected = false;
+ }
+ return result;
+ }
+
+ /**
+ * Read a packet from the socket. Use this for connected sockets.
+ * \param packet Set to the packet read on return.
+ * \param flags The recv options.
+ * \return The number of bytes read.
+ */
+ ssize_t read(packet& packet, int flags = 0)
+ {
+ char buffer[65536];
+ ssize_t result = read(buffer, sizeof(buffer), flags);
+ if (result != -1) packet = moof::packet(buffer, result);
+ return result;
+ }
+
+ /**
+ * Read a packet from the socket using the given address. Use this for
+ * unconnected sockets.
+ * \param packet Set to the packet read on return.
+ * \param address The address to read from.
+ * \param flags The recv options.
+ * \return The number of bytes read.
+ */
+ ssize_t read(packet& packet, address& address, int flags = 0)
+ {
+ char buffer[65536];
+ ssize_t result = read(buffer, sizeof(buffer), address, flags);
+ if (result != -1) packet = moof::packet(buffer, result);
+ return result;
+ }
+
+
+ // The rest of this junk is used to implement the "move" semantics
+ // correctly, since it makes no sense for socket objects to be copied.
+
+ socket(socket& move) :
+ impl_(move.impl_)
+ {
+ move.impl_.fd = -1;
+ move.impl_.is_connected = false;
+ }
+
+ socket(impl move) :
+ impl_(move) {}
+
+ socket& operator=(socket& move)
+ {
+ close();
+ impl_ = move.impl_;
+ move.impl_.fd = -1;
+ move.impl_.is_connected = false;
+ return *this;
+ }
+
+ socket& operator=(impl move)
+ {
+ close();
+ impl_ = move;
+ return *this;
+ }
+
+ operator impl()
+ {
+ impl impl(impl_);
+ impl_.fd = -1;
+ impl_.is_connected = false;
+ return impl;
+ }
+
+
+private:
+
+ socket(int fd)
+ {
+ // for accepting a socket from fd
+ union
+ {
+ struct sockaddr sa;
+ struct sockaddr_storage storage;
+ } addr;
+ socklen_t length = sizeof(addr);
+
+ impl_.fd = ::accept(fd, &addr.sa, &length);
+ if (impl_.fd != -1)
+ {
+ impl_.is_connected = true;
+ impl_.address = address(&addr.sa, length);
+ }
+ }
+
+ void close()
+ {
+#if defined(_WIN32)
+ if (impl_.fd != -1) closesocket(impl_.fd);
+#else
+ if (impl_.fd != -1) ::close(impl_.fd);
+#endif
+ }
+};
+
+
+class socket_multiplexer
+{
+public:
+
+ typedef boost::function<int(socket_multiplexer&,
+ packet&,
+ const socket::address&)> function;
+
+ explicit socket_multiplexer(moof::socket sock) :
+ socket_(sock) {}
+
+
+ void socket(moof::socket sock)
+ {
+ mutex::scoped_lock lock(mutex_);
+ socket_ = sock;
+ }
+
+ moof::socket& socket()
+ {
+ return socket_;
+ }
+
+
+ std::vector<function>& protocols()
+ {
+ return protocols_;
+ }
+
+
+ void update(scalar t, scalar dt)
+ {
+ socket::address address;
+ packet packet;
+ ssize_t bytes = socket_.read(packet, address);
+
+ if (bytes > 0)
+ {
+ std::vector<function>::iterator it;
+ for (it = protocols_.begin(); it < protocols_.end(); ++it)
+ {
+ packet.revert();
+ if ((*it)(*this, packet, address)) break;
+ }
+ }
+ }
+
+
+ int background()
+ {
+ return 0;
+ }
+
+
+private:
+
+ moof::socket socket_;
+ std::vector<function> protocols_;
+ mutex mutex_;
+};
+
+
+/**
+ * An asynchronous task to resolve addresses.
+ */
+class resolver_task : public threaded_task
+{
+public:
+
+ /**
+ * Construct a resolver task from a service and hostname.
+ * \param service Server name or port number.
+ * \param name The hostname or numeric address.
+ * \param type The type of communication.
+ * \param family The requested protocol family.
+ */
+ resolver_task(const std::string& service,
+ const std::string& name,
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC) :
+ is_done_(false)
+ {
+ function_ = boost::bind(&resolver_task::resolve,
+ this, service, name, type, family);
+ }
+
+
+ /**
+ * Get whether or not the task is done.
+ * \return True if the task has finished, false otherwise.
+ */
+ bool is_done() const
+ {
+ return is_done_;
+ }
+
+ /**
+ * Start the task. This does nothing if the task was already run or is
+ * currently running.
+ */
+ void run()
+ {
+ if (!is_done() && !thread_.is_valid())
+ {
+ thread_ = thread::detach(function_);
+ }
+ }
+
+
+ /**
+ * Get the addresses resolved. This is filled and safe to access after
+ * the task finishes.
+ * \return List of addresses.
+ * \see is_done()
+ */
+ const std::vector<socket::address>& addresses() const
+ {
+ return address_list_;
+ }
+
+
+private:
+
+ int resolve(const std::string& service,
+ const std::string& name,
+ int type,
+ int family)
+ {
+ int status = socket::address::resolve(service, name,
+ type, family,
+ address_list_);
+ is_done_ = true;
+ return status;
+ }
+
+
+ std::vector<socket::address> address_list_;
+ bool is_done_;
+ thread::function function_;
+};
+
+
+/**
+ * Insert a string representation of a socket address into a stream.
+ * \param stream The output stream.
+ * \param addr The socket address.
+ * \return The stream.
+ */
+std::ostream& operator << (std::ostream& stream, const socket::address& addr)
+{
+ stream << addr.name() << ":" << addr.service();
+ return stream;
+}
+
+/**
+ * Insert a string representation of a socket into a stream.
+ * \param stream The output stream.
+ * \param addr The socket.
+ * \return The stream.
+ */
+std::ostream& operator << (std::ostream& stream, const socket& sock)
+{
+ stream << sock.peer_address();
+ return stream;
+}
+
+
+} // namespace moof
+
+#endif // _MOOF_SOCKET_HH_
+