/*] 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_ #include #include #include #include #if defined(_WIN32) #include #include #include #else #include #include #include #include #if HAVE_FCNTL_H #include #else #include #endif #endif #include #include #include #ifndef SO_NONBLOCK #define SO_NONBLOCK 1024 #endif namespace Mf { class SocketAddress { public: SocketAddress() : mSize(0), mType(0) { mAddress.sa.sa_family = AF_UNSPEC; mAddress.v4.sin_port = 0; } SocketAddress(const std::string& service, const std::string& name, int type = SOCK_STREAM, int family = AF_UNSPEC) { init(service, name, type, family); } SocketAddress(const std::string& service, int type = SOCK_STREAM, int family = AF_UNSPEC) { init(service, type, family); } SocketAddress(const struct addrinfo* addr, const std::string& name) { mType = addr->ai_socktype; memcpy(&mAddress.sa, addr->ai_addr, addr->ai_addrlen); mName = name; mSize = addr->ai_addrlen; } SocketAddress(const struct sockaddr* addr, size_t size, int type = SOCK_STREAM) { mType = type; memcpy(&mAddress.sa, addr, size); mSize = size; setNameFromAddress(); } static SocketAddress broadcast(const std::string& service) { return SocketAddress(service, "255.255.255.255", SOCK_DGRAM); } void init(const std::string& service, const std::string& name = "", int type = SOCK_STREAM, int family = AF_UNSPEC) { 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 = AI_PASSIVE; struct addrinfo* addr; int status = getaddrinfo(name.length() > 0 ? name.c_str() : 0, service.c_str(), &hints, &addr); if (status == 0) { mType = addr->ai_socktype; memcpy(&mAddress.sa, addr->ai_addr, addr->ai_addrlen); mSize = addr->ai_addrlen; if (name != "") mName = name; else setNameFromAddress(); freeaddrinfo(addr); } else { Mf::logWarning(gai_strerror(status)); mType = 0; mSize = 0; mAddress.sa.sa_family = AF_UNSPEC; mAddress.v4.sin_port = 0; } } void init(const std::string& service, int type = SOCK_STREAM, int family = AF_UNSPEC) { init(service, "", type, family); } const std::string& name() const { return mName; } void setName(const std::string& name) { mName = name; } unsigned short port() const { return ntohs(mAddress.v4.sin_port); } int type() const { return mType; } int family() const { return mAddress.sa.sa_family; } const struct sockaddr* address() const { return mSize != 0 ? &mAddress.sa : 0; } size_t size() const { return mSize; } static int resolve(const std::string& service, const std::string& name, int type, int family, std::vector& resolved) { ASSERT(type == SOCK_STREAM || type == SOCK_DGRAM); ASSERT(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC); resolved.clear(); struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = type; hints.ai_flags = AI_PASSIVE; struct addrinfo* list; int status = getaddrinfo(name.length() > 0 ? name.c_str() : 0, service.length() > 0 ? service.c_str() : 0, &hints, &list); if (status == 0) { struct addrinfo* addr = list; while (addr != 0) { resolved.push_back(SocketAddress(addr, name)); addr = addr->ai_next; } freeaddrinfo(list); } else { Mf::logWarning(gai_strerror(status)); } return 0; } private: void setNameFromAddress() { #if defined(_WIN32) // inet_ntop was introduced in Vista mName = inet_ntoa(mAddress.v4.sin_addr); #else char name[INET6_ADDRSTRLEN] = {'\0'}; inet_ntop(mAddress.sa.sa_family, &mAddress.sa, name, sizeof(name)); mName = name; #endif } union { sockaddr sa; sockaddr_in v4; sockaddr_in6 v6; sockaddr_storage storage; } mAddress; size_t mSize; std::string mName; int mType; }; class Socket { public: Socket(const SocketAddress& address) : mFd(-1), mAddress(address) { mFd = socket(address.family(), address.type(), 0); } ~Socket() { #if defined(_WIN32) if (mFd != -1) closesocket(mFd); #else if (mFd != -1) close(mFd); #endif } bool isConnected() const { return mFd != -1; } const SocketAddress& address() const { return mAddress; } int connect() { return ::connect(mFd, mAddress.address(), mAddress.size()); } int bind() { return ::bind(mFd, mAddress.address(), mAddress.size()); } int listen(int backlog = SOMAXCONN) { return ::listen(mFd, backlog > 0 ? backlog : SOMAXCONN); } Socket accept() { return Socket(mFd); } int set(int option, int value = 0) { if (option == SO_NONBLOCK) { #ifdef HAVE_FCNTL int flags = fcntl(mFd, F_GETFL); return fcntl(mFd, F_SETFL, (value ? O_NONBLOCK : 0) | flags); #else return ioctl(mFd, FIONBIO, value); #endif } return setsockopt(mFd, SOL_SOCKET, option, &value, sizeof(value)); } int set(int option, const std::string& value) { return setsockopt(mFd, SOL_SOCKET, option, value.c_str(), value.length()); } int get(int option, int& value) { if (option == SO_NONBLOCK) { #ifdef HAVE_FCNTL int flags = fcntl(mFd, F_GETFL); return flags & O_NONBLOCK; #else return ioctl(mFd, FIONBIO, &value); #endif } socklen_t optlen = sizeof(value); return getsockopt(mFd, SOL_SOCKET, option, &value, &optlen); } int get(int option, std::string& value) { char str[64] = {'\0'}; socklen_t optlen = sizeof(str); int result = getsockopt(mFd, SOL_SOCKET, option, &str, &optlen); value = str; return result; } void write(const Packet& packet) { write(mAddress, packet); } void write(const SocketAddress& address, const Packet& packet) { sendto(mFd, packet.bytes(), packet.size(), 0, address.address(), address.size()); } Packet read() { char buffer[1024]; int size = recv(mFd, buffer, sizeof(buffer), 0); return Packet(buffer, size); } Packet read(SocketAddress& address) { union { sockaddr sa; sockaddr_storage storage; } addr; socklen_t length = sizeof(addr); char buffer[1024]; int size = recvfrom(mFd, buffer, sizeof(buffer), 0, &addr.sa, &length); address = SocketAddress(&addr.sa, length, mAddress.type()); return Packet(buffer, size); } private: Socket(int fd) { // for accepting a socket from fd union { sockaddr sa; sockaddr_storage storage; } addr; socklen_t length = sizeof(addr); mFd = ::accept(fd, &addr.sa, &length); mAddress = SocketAddress(&addr.sa, length); } int mFd; SocketAddress mAddress; }; class ResolverTask : public ThreadedTask { public: ResolverTask(const std::string& service, const std::string& name, int type = SOCK_STREAM, int family = AF_UNSPEC) : mIsDone(false) { mFunction = boost::bind(&ResolverTask::resolve, this, service, name, type, family); } bool isDone() const { return mIsDone; } void run() { if (!mThread) mThread = Mf::detachFunction(mFunction); } const std::vector& addresses() const { return mAddressList; } private: int resolve(const std::string& service, const std::string& name, int type, int family) { int status = SocketAddress::resolve(service, name, type, family, mAddressList); mIsDone = true; return status; } std::vector mAddressList; bool mIsDone; Function mFunction; }; } // namespace Mf #endif // _MOOF_SOCKET_HH_