Mercurial > projects > hoofbaby
diff deps/Platinum/ThirdParty/Neptune/Source/System/Bsd/NptBsdSockets.cpp @ 0:3425707ddbf6
Initial import (hopefully this mercurial stuff works...)
author | fraserofthenight |
---|---|
date | Mon, 06 Jul 2009 08:06:28 -0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/deps/Platinum/ThirdParty/Neptune/Source/System/Bsd/NptBsdSockets.cpp Mon Jul 06 08:06:28 2009 -0700 @@ -0,0 +1,1987 @@ +/***************************************************************** +| +| Neptune - Sockets :: BSD/Winsock Implementation +| +| (c) 2001-2008 Gilles Boccon-Gibod +| Author: Gilles Boccon-Gibod (bok@bok.net) +| + ****************************************************************/ + +/*---------------------------------------------------------------------- +| includes ++---------------------------------------------------------------------*/ +#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(__SYMBIAN32__) + +// Win32 includes +#if !defined(__WIN32__) +#define __WIN32__ +#endif +#endif +#if defined(__WIN32__) && !defined(_XBOX) +#define STRICT +#define NPT_WIN32_USE_WINSOCK2 +#ifdef NPT_WIN32_USE_WINSOCK2 +/* it is important to include this in this order, because winsock.h and ws2tcpip.h */ +/* have different definitions for the same preprocessor symbols, such as IP_ADD_MEMBERSHIP */ +#include <winsock2.h> +#include <ws2tcpip.h> +#else +#include <winsock.h> +#endif +#include <windows.h> + +// XBox +#elif defined(_XBOX) +#include <xtl.h> +#include <winsockx.h> + +#elif defined(__TCS__) + +// Trimedia includes +#include <sockets.h> + +#elif defined(__PSP__) + +// PSP includes +#include <psptypes.h> +#include <kernel.h> +#include <pspnet.h> +#include <pspnet_error.h> +#include <pspnet_inet.h> +#include <pspnet_resolver.h> +#include <pspnet_apctl.h> +#include <pspnet_ap_dialog_dummy.h> +#include <errno.h> +#include <wlan.h> +#include <pspnet/sys/socket.h> +#include <pspnet/sys/select.h> +#include <pspnet/netinet/in.h> + +#elif defined(__PPU__) + +// PS3 includes +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/select.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <netex/net.h> +#include <netex/errno.h> + +#else + +// default includes +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#if !defined(__SYMBIAN32__) +#include <netinet/tcp.h> +#endif +#include <netdb.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <signal.h> + +#endif + + +#include "NptConfig.h" +#include "NptTypes.h" +#include "NptStreams.h" +#include "NptThreads.h" +#include "NptSockets.h" +#include "NptUtils.h" +#include "NptConstants.h" + +/*---------------------------------------------------------------------- +| constants ++---------------------------------------------------------------------*/ +const int NPT_TCP_SERVER_SOCKET_DEFAULT_LISTEN_COUNT = 20; + +/*---------------------------------------------------------------------- +| WinSock adaptation layer ++---------------------------------------------------------------------*/ +#if defined(__WIN32__) || defined(_XBOX) +#if defined(_XBOX) +#include "NptXboxNetwork.h" +#define SO_ERROR 0x1007 /* unsupported */ +#else +#include "NptWin32Network.h" +#endif +// force a reference to the initializer so that the linker does not optimize it out +static NPT_WinsockSystem& WinsockInitializer = NPT_WinsockSystem::Initializer; + +#if defined(SetPort) +#undef SetPort +#endif + +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EINPROGRESS WSAEINPROGRESS +#define ECONNREFUSED WSAECONNREFUSED +#define ECONNABORTED WSAECONNABORTED +#define ECONNRESET WSAECONNRESET +#define ETIMEDOUT WSAETIMEDOUT +#define ENETRESET WSAENETRESET +#define EADDRINUSE WSAEADDRINUSE +#define ENETDOWN WSAENETDOWN +#define ENETUNREACH WSAENETUNREACH +#if !defined(EAGAIN) +#define EAGAIN WSAEWOULDBLOCK +#endif + +#if !defined(__MINGW32__) +typedef int ssize_t; +#endif +typedef int socklen_t; +typedef char* SocketBuffer; +typedef const char* SocketConstBuffer; +typedef char* SocketOption; +typedef SOCKET SocketFd; + +#define GetSocketError() WSAGetLastError() +#define NPT_BSD_SOCKET_IS_INVALID(_s) ((_s) == INVALID_SOCKET) +#define NPT_BSD_SOCKET_CALL_FAILED(_e) ((_e) == SOCKET_ERROR) +#define NPT_BSD_SOCKET_SELECT_FAILED(_e) ((_e) == SOCKET_ERROR) + +/*---------------------------------------------------------------------- +| Trimedia adaptation layer ++---------------------------------------------------------------------*/ +#elif defined(__TCS__) // trimedia PSOS w/ Target TCP +typedef void* SocketBuffer; +typedef const void* SocketConstBuffer; +typedef void* SocketOption; +typedef int SocketFd; + +#define GetSocketError() errno +#define NPT_BSD_SOCKET_IS_INVALID(_s) ((_s) < 0) +#define NPT_BSD_SOCKET_CALL_FAILED(_e) ((_e) < 0) +#define NPT_BSD_SOCKET_SELECT_FAILED(_e) ((_e) < 0) + +/*---------------------------------------------------------------------- +| PSP adaptation layer ++---------------------------------------------------------------------*/ +#elif defined(__PSP__) +typedef SceNetInetSocklen_t socklen_t; +#define timeval SceNetInetTimeval +#define inet_addr sceNetInetInetAddr +#define select sceNetInetSelect +#define socket sceNetInetSocket +#define connect sceNetInetConnect +#define bind sceNetInetBind +#define accept sceNetInetAccept +#define getpeername sceNetInetGetpeername +#define getsockopt sceNetInetGetsockopt +#define setsockopt sceNetInetSetsockopt +#define listen sceNetInetListen +#define getsockname sceNetInetGetsockname +#define sockaddr SceNetInetSockaddr +#define sockaddr_in SceNetInetSockaddrIn +#define in_addr SceNetInetInAddr +#define send sceNetInetSend +#define sendto sceNetInetSendto +#define recv sceNetInetRecv +#define recvfrom sceNetInetRecvfrom +#define closesocket sceNetInetClose +#define htonl sceNetHtonl +#define htons sceNetHtons +#define ntohl sceNetNtohl +#define ntohs sceNetNtohs +#define SOL_SOCKET SCE_NET_INET_SOL_SOCKET +#define AF_INET SCE_NET_INET_AF_INET +#define SOCK_STREAM SCE_NET_INET_SOCK_STREAM +#define SOCK_DGRAM SCE_NET_INET_SOCK_DGRAM +#define SO_BROADCAST SCE_NET_INET_SO_BROADCAST +#define SO_ERROR SCE_NET_INET_SO_ERROR +#define IPPROTO_IP SCE_NET_INET_IPPROTO_IP +#define IP_ADD_MEMBERSHIP SCE_NET_INET_IP_ADD_MEMBERSHIP +#define IP_MULTICAST_IF SCE_NET_INET_IP_MULTICAST_IF +#define IP_MULTICAST_TTL SCE_NET_INET_IP_MULTICAST_TTL +#define SO_REUSEADDR SCE_NET_INET_SO_REUSEADDR +#define INADDR_ANY SCE_NET_INET_INADDR_ANY +#define ip_mreq SceNetInetIpMreq +#ifdef fd_set +#undef fd_set +#endif +#define fd_set SceNetInetFdSet +#ifdef FD_ZERO +#undef FD_ZERO +#endif +#define FD_ZERO SceNetInetFD_ZERO +#ifdef FD_SET +#undef FD_SET +#endif +#define FD_SET SceNetInetFD_SET +#ifdef FD_CLR +#undef FD_CLR +#endif +#define FD_CLR SceNetInetFD_CLR +#ifdef FD_ISSET +#undef FD_ISSET +#endif +#define FD_ISSET SceNetInetFD_ISSET + +#define RESOLVER_TIMEOUT (5 * 1000 * 1000) +#define RESOLVER_RETRY 5 + +typedef void* SocketBuffer; +typedef const void* SocketConstBuffer; +typedef void* SocketOption; +typedef int SocketFd; + +#define GetSocketError() sceNetInetGetErrno() +#define NPT_BSD_SOCKET_IS_INVALID(_s) ((_s) < 0) +#define NPT_BSD_SOCKET_CALL_FAILED(_e) ((_e) < 0) +#define NPT_BSD_SOCKET_SELECT_FAILED(_e) ((_e) < 0) + +/*---------------------------------------------------------------------- +| PS3 adaptation layer ++---------------------------------------------------------------------*/ +#elif defined(__PPU__) +#undef EWOULDBLOCK +#undef ECONNREFUSED +#undef ECONNABORTED +#undef ECONNRESET +#undef ETIMEDOUT +#undef ENETRESET +#undef EADDRINUSE +#undef ENETDOWN +#undef ENETUNREACH +#undef EAGAIN +#undef EINTR +#undef EINPROGRESS + +#define EWOULDBLOCK SYS_NET_EWOULDBLOCK +#define ECONNREFUSED SYS_NET_ECONNREFUSED +#define ECONNABORTED SYS_NET_ECONNABORTED +#define ECONNRESET SYS_NET_ECONNRESET +#define ETIMEDOUT SYS_NET_ETIMEDOUT +#define ENETRESET SYS_NET_ENETRESET +#define EADDRINUSE SYS_NET_EADDRINUSE +#define ENETDOWN SYS_NET_ENETDOWN +#define ENETUNREACH SYS_NET_ENETUNREACH +#define EAGAIN SYS_NET_EAGAIN +#define EINTR SYS_NET_EINTR +#define EINPROGRESS SYS_NET_EINPROGRESS + +typedef void* SocketBuffer; +typedef const void* SocketConstBuffer; +typedef void* SocketOption; +typedef int SocketFd; + +#define closesocket socketclose +#define select socketselect + +#define GetSocketError() sys_net_errno +#define NPT_BSD_SOCKET_IS_INVALID(_s) ((_s) < 0) +#define NPT_BSD_SOCKET_CALL_FAILED(_e) ((_e) < 0) +#define NPT_BSD_SOCKET_SELECT_FAILED(_e) ((_e) < 0) + +// network initializer +static struct NPT_Ps3NetworkInitializer { + NPT_Ps3NetworkInitializer() { + sys_net_initialize_network(); + } + ~NPT_Ps3NetworkInitializer() { + sys_net_finalize_network(); + } +} Ps3NetworkInitializer; + +/*---------------------------------------------------------------------- +| Default adaptation layer ++---------------------------------------------------------------------*/ +#else +typedef void* SocketBuffer; +typedef const void* SocketConstBuffer; +typedef void* SocketOption; +typedef int SocketFd; + +#define closesocket close +#define ioctlsocket ioctl + +#define GetSocketError() errno +#define NPT_BSD_SOCKET_IS_INVALID(_s) ((_s) < 0) +#define NPT_BSD_SOCKET_CALL_FAILED(_e) ((_e) < 0) +#define NPT_BSD_SOCKET_SELECT_FAILED(_e) ((_e) < 0) + +#endif + +/*---------------------------------------------------------------------- +| SocketAddressToInetAddress ++---------------------------------------------------------------------*/ +static void +SocketAddressToInetAddress(const NPT_SocketAddress& socket_address, + struct sockaddr_in* inet_address) +{ + // initialize the structure + for (int i=0; i<8; i++) inet_address->sin_zero[i]=0; + + // setup the structure + inet_address->sin_family = AF_INET; + inet_address->sin_port = htons(socket_address.GetPort()); + inet_address->sin_addr.s_addr = htonl(socket_address.GetIpAddress().AsLong()); +} + +/*---------------------------------------------------------------------- +| InetAddressToSocketAddress ++---------------------------------------------------------------------*/ +static void +InetAddressToSocketAddress(const struct sockaddr_in* inet_address, + NPT_SocketAddress& socket_address) +{ + // read the fields + socket_address.SetPort(ntohs(inet_address->sin_port)); + socket_address.SetIpAddress(NPT_IpAddress(ntohl(inet_address->sin_addr.s_addr))); +} + +/*---------------------------------------------------------------------- +| MapErrorCode ++---------------------------------------------------------------------*/ +static NPT_Result +MapErrorCode(int error) +{ + switch (error) { + case ECONNRESET: + case ENETRESET: + return NPT_ERROR_CONNECTION_RESET; + + case ECONNABORTED: + return NPT_ERROR_CONNECTION_ABORTED; + + case ECONNREFUSED: + return NPT_ERROR_CONNECTION_REFUSED; + + case ETIMEDOUT: + return NPT_ERROR_TIMEOUT; + + case EADDRINUSE: + return NPT_ERROR_ADDRESS_IN_USE; + + case ENETDOWN: + return NPT_ERROR_NETWORK_DOWN; + + case ENETUNREACH: + return NPT_ERROR_NETWORK_UNREACHABLE; + + case EINPROGRESS: + case EAGAIN: +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: +#endif + return NPT_ERROR_WOULD_BLOCK; + + default: + return NPT_FAILURE; + } +} + +#if defined(_XBOX) + +struct hostent { + char * h_name; /* official name of host */ + char * * h_aliases; /* alias list */ + short h_addrtype; /* host address type */ + short h_length; /* length of address */ + char * * h_addr_list; /* list of addresses */ +#define h_addr h_addr_list[0] /* address, for backward compat */ +}; + +typedef struct { + struct hostent server; + char name[128]; + char addr[16]; + char* addr_list[4]; +} HostEnt; + +/*---------------------------------------------------------------------- +| gethostbyname ++---------------------------------------------------------------------*/ +static struct hostent* +gethostbyname(const char* name) +{ + struct hostent* host = NULL; + HostEnt* host_entry = new HostEnt; + WSAEVENT hEvent = WSACreateEvent(); + XNDNS* pDns = NULL; + + INT err = XNetDnsLookup(name, hEvent, &pDns); + WaitForSingleObject(hEvent, INFINITE); + if (pDns) { + if (pDns->iStatus == 0) { + strcpy(host_entry->name, name); + host_entry->addr_list[0] = host_entry->addr; + memcpy(host_entry->addr, &(pDns->aina[0].s_addr), 4); + host_entry->server.h_name = host_entry->name; + host_entry->server.h_aliases = 0; + host_entry->server.h_addrtype = AF_INET; + host_entry->server.h_length = 4; + host_entry->server.h_addr_list = new char*[4]; + + host_entry->server.h_addr_list[0] = host_entry->addr_list[0]; + host_entry->server.h_addr_list[1] = 0; + + host = (struct hostent*)host_entry; + } + XNetDnsRelease(pDns); + } + WSACloseEvent(hEvent); + return host; +}; + +#endif // _XBOX + +/*---------------------------------------------------------------------- +| NPT_IpAddress::ResolveName ++---------------------------------------------------------------------*/ +NPT_Result +NPT_IpAddress::ResolveName(const char* name, NPT_Timeout) +{ + // check parameters + if (name == NULL || name[0] == '\0') return NPT_ERROR_HOST_UNKNOWN; + + // handle numerical addrs + NPT_IpAddress numerical_address; + if (NPT_SUCCEEDED(numerical_address.Parse(name))) { + /* the name is a numerical IP addr */ + return Set(numerical_address.AsLong()); + } + +#if defined(__TCS__) + Set(getHostByName(name)); +#elif defined(__PSP__) + int rid; + char buf[1024]; + int buflen = sizeof(buf); + + int ret = sceNetResolverCreate(&rid, buf, buflen); + if(ret < 0){ + return NPT_FAILURE; + } + ret = sceNetResolverStartNtoA(rid, name, &address->sin_addr, + RESOLVER_TIMEOUT, RESOLVER_RETRY); + if(ret < 0){ + return NPT_ERROR_HOST_UNKNOWN; + } + sceNetResolverDelete(rid); +#else + // do a name lookup + struct hostent *host_entry = gethostbyname(name); + if (host_entry == NULL || + host_entry->h_addrtype != AF_INET) { + return NPT_ERROR_HOST_UNKNOWN; + } + NPT_CopyMemory(m_Address, host_entry->h_addr, 4); + +#if defined(_XBOX) + delete host_entry; +#endif + +#endif + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocketFd ++---------------------------------------------------------------------*/ +class NPT_BsdSocketFd +{ +public: + // constructors and destructor + NPT_BsdSocketFd(SocketFd fd) : + m_SocketFd(fd), + m_Blocking(true), + m_ReadTimeout(NPT_TIMEOUT_INFINITE), + m_WriteTimeout(NPT_TIMEOUT_INFINITE), + m_Position(0) { +#if !defined(__WIN32__) && !defined(_XBOX) + pipe(m_AbortPipe); +#endif + } + ~NPT_BsdSocketFd() { +#if !defined(__WIN32__) && !defined(_XBOX) + close(m_AbortPipe[0]); + close(m_AbortPipe[1]); + closesocket(m_SocketFd); +#else + Disconnect(); +#endif + } + + // methods + NPT_Result SetBlockingMode(bool blocking); + NPT_Result WaitUntilReadable(); + NPT_Result WaitUntilWriteable(); + void Disconnect() { + if (!m_SocketFd) return; + +#if !defined(__WIN32__) && !defined(_XBOX) + write(m_AbortPipe[1], "\0", 1); +#else + // no pipe on win32, but closing the socket will + // unblock the socket + SocketFd socketFd = m_SocketFd; + m_SocketFd = 0; + if (socketFd) { + closesocket(socketFd); + } +#endif + } + + // members + SocketFd m_SocketFd; + bool m_Blocking; + NPT_Timeout m_ReadTimeout; + NPT_Timeout m_WriteTimeout; + NPT_Position m_Position; + +private: + // methods + friend class NPT_BsdTcpServerSocket; + NPT_Result WaitForCondition(bool readable, bool writeable, NPT_Timeout timeout); + +public: + // members +#if !defined(__WIN32__) && !defined(_XBOX) + int m_AbortPipe[2]; /* an array to store the file descriptors of the abort pipe. */ +#endif +}; + +typedef NPT_Reference<NPT_BsdSocketFd> NPT_BsdSocketFdReference; + +#if defined(__WIN32__) || defined(__TCS__) || defined(_XBOX) +/*---------------------------------------------------------------------- +| NPT_BsdSocketFd::SetBlockingMode ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocketFd::SetBlockingMode(bool blocking) +{ + unsigned long args = blocking?0:1; + if (ioctlsocket(m_SocketFd, FIONBIO, &args)) { + return NPT_ERROR_SOCKET_CONTROL_FAILED; + } + m_Blocking = blocking; + return NPT_SUCCESS; +} +#elif defined(__PSP__) || defined(__PPU__) +/*---------------------------------------------------------------------- +| NPT_BsdSocketFd::SetBlockingMode ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocketFd::SetBlockingMode(bool blocking) +{ + int args = blocking?0:1; + if (setsockopt(m_SocketFd, SOL_SOCKET, SO_NBIO, &args, sizeof(args))) { + return NPT_ERROR_SOCKET_CONTROL_FAILED; + } + m_Blocking = blocking; + return NPT_SUCCESS; +} +#else +/*---------------------------------------------------------------------- +| NPT_BsdSocketFd::SetBlockingMode ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocketFd::SetBlockingMode(bool blocking) +{ + int flags = fcntl(m_SocketFd, F_GETFL, 0); + if (blocking) { + flags &= ~O_NONBLOCK; + } else { + flags |= O_NONBLOCK; + } + if (fcntl(m_SocketFd, F_SETFL, flags)) { + return NPT_ERROR_SOCKET_CONTROL_FAILED; + } + m_Blocking = blocking; + return NPT_SUCCESS; +} +#endif + +/*---------------------------------------------------------------------- +| NPT_BsdSocketFd::WaitUntilReadable ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocketFd::WaitUntilReadable() +{ + return WaitForCondition(true, false, m_ReadTimeout); +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocketFd::WaitUntilWriteable ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocketFd::WaitUntilWriteable() +{ + return WaitForCondition(false, true, m_WriteTimeout); +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocketFd::WaitForCondition ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocketFd::WaitForCondition(bool wait_for_readable, + bool wait_for_writeable, + NPT_Timeout timeout) +{ + // wait for incoming connection + NPT_Result result = NPT_SUCCESS; + fd_set read_set; + fd_set write_set; + fd_set except_set; + FD_ZERO(&read_set); + if (wait_for_readable) FD_SET(m_SocketFd, &read_set); + FD_ZERO(&write_set); + if (wait_for_writeable) FD_SET(m_SocketFd, &write_set); + FD_ZERO(&except_set); + FD_SET(m_SocketFd, &except_set); + + struct timeval timeout_value; + if (timeout != NPT_TIMEOUT_INFINITE) { + timeout_value.tv_sec = timeout/1000; + timeout_value.tv_usec = 1000*(timeout-1000*(timeout/1000)); + }; + + int nfds = (int)m_SocketFd+1; + +#if !defined(__WIN32__) && !defined(_XBOX) + FD_SET(m_AbortPipe[0], &read_set); + nfds = (m_AbortPipe[0]>(int)m_SocketFd?m_AbortPipe[0]:(int)m_SocketFd)+1; +#endif + + int io_result = select(nfds, + &read_set, &write_set, &except_set, + timeout == NPT_TIMEOUT_INFINITE ? + NULL : &timeout_value); + + if (io_result == 0) { + if (timeout == 0) { + // non-blocking call + result = NPT_ERROR_WOULD_BLOCK; + } else { + // timeout + result = NPT_ERROR_TIMEOUT; + } + } else if (NPT_BSD_SOCKET_SELECT_FAILED(io_result)) { + result = MapErrorCode(GetSocketError()); + } else if ((wait_for_readable && FD_ISSET(m_SocketFd, &read_set)) || + (wait_for_writeable && FD_ISSET(m_SocketFd, &write_set))) { + result = NPT_SUCCESS; + } else if (FD_ISSET(m_SocketFd, &except_set)) { + result = MapErrorCode(GetSocketError()); +#if !defined(__WIN32__) && !defined(_XBOX) + } else if (FD_ISSET(m_AbortPipe[0], &read_set)) { + result = NPT_ERROR_CONNECTION_ABORTED; +#endif + } else { + // should not happen + result = (m_SocketFd == 0)?NPT_ERROR_EOS:NPT_ERROR_INTERNAL; + } + + return result; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocketStream ++---------------------------------------------------------------------*/ +class NPT_BsdSocketStream +{ + public: + // methods + NPT_BsdSocketStream(NPT_BsdSocketFdReference& socket_fd) : + m_SocketFdReference(socket_fd) {} + + // NPT_InputStream and NPT_OutputStream methods + NPT_Result Seek(NPT_Position) { return NPT_ERROR_NOT_SUPPORTED; } + NPT_Result Tell(NPT_Position& where) { + where = 0; + return NPT_SUCCESS; + } + + protected: + // destructor + virtual ~NPT_BsdSocketStream() {} + + // members + NPT_BsdSocketFdReference m_SocketFdReference; +}; + +/*---------------------------------------------------------------------- +| NPT_BsdSocketInputStream ++---------------------------------------------------------------------*/ +class NPT_BsdSocketInputStream : public NPT_InputStream, + private NPT_BsdSocketStream +{ +public: + // constructors and destructor + NPT_BsdSocketInputStream(NPT_BsdSocketFdReference& socket_fd) : + NPT_BsdSocketStream(socket_fd) {} + + // NPT_InputStream methods + NPT_Result Read(void* buffer, + NPT_Size bytes_to_read, + NPT_Size* bytes_read); + NPT_Result Seek(NPT_Position offset) { + return NPT_BsdSocketStream::Seek(offset); } + NPT_Result Tell(NPT_Position& where) { + return NPT_BsdSocketStream::Tell(where); + } + NPT_Result GetSize(NPT_LargeSize& size); + NPT_Result GetAvailable(NPT_LargeSize& available); +}; + +/*---------------------------------------------------------------------- +| NPT_BsdSocketInputStream::Read ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocketInputStream::Read(void* buffer, + NPT_Size bytes_to_read, + NPT_Size* bytes_read) +{ + // if we're blocking, wait until the socket is readable + if (m_SocketFdReference->m_Blocking && + m_SocketFdReference->m_ReadTimeout != NPT_TIMEOUT_INFINITE) { + NPT_Result result = m_SocketFdReference->WaitUntilReadable(); + if (result != NPT_SUCCESS) return result; + } + + // read from the socket + ssize_t nb_read = recv(m_SocketFdReference->m_SocketFd, + (SocketBuffer)buffer, + bytes_to_read, 0); + if (nb_read > 0) { + if (bytes_read) *bytes_read = nb_read; + m_SocketFdReference->m_Position += nb_read; + return NPT_SUCCESS; + } else { + if (bytes_read) *bytes_read = 0; + if (nb_read == 0) { + return NPT_ERROR_EOS; + } else { + return MapErrorCode(GetSocketError()); + } + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocketInputStream::GetSize ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocketInputStream::GetSize(NPT_LargeSize& size) +{ + // generic socket streams have no size + size = 0; + return NPT_ERROR_NOT_SUPPORTED; +} + +#if defined(__PPU__) +/*---------------------------------------------------------------------- +| NPT_BsdSocketInputStream::GetAvailable ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocketInputStream::GetAvailable(NPT_LargeSize&) +{ + return NPT_ERROR_NOT_SUPPORTED; +} +#else +/*---------------------------------------------------------------------- +| NPT_BsdSocketInputStream::GetAvailable ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocketInputStream::GetAvailable(NPT_LargeSize& available) +{ + unsigned long ready = 0; + int io_result = ioctlsocket(m_SocketFdReference->m_SocketFd, FIONREAD, &ready); + if (NPT_BSD_SOCKET_CALL_FAILED(io_result)) { + available = 0; + return NPT_ERROR_SOCKET_CONTROL_FAILED; + } else { + available = ready; + return NPT_SUCCESS; + } +} +#endif + +/*---------------------------------------------------------------------- +| NPT_BsdSocketOutputStream ++---------------------------------------------------------------------*/ +class NPT_BsdSocketOutputStream : public NPT_OutputStream, + private NPT_BsdSocketStream +{ +public: + // constructors and destructor + NPT_BsdSocketOutputStream(NPT_BsdSocketFdReference& socket_fd) : + NPT_BsdSocketStream(socket_fd) {} + + // NPT_OutputStream methods + NPT_Result Write(const void* buffer, + NPT_Size bytes_to_write, + NPT_Size* bytes_written); + NPT_Result Seek(NPT_Position offset) { + return NPT_BsdSocketStream::Seek(offset); } + NPT_Result Tell(NPT_Position& where) { + return NPT_BsdSocketStream::Tell(where); + } + NPT_Result Flush(); +}; + +/*---------------------------------------------------------------------- +| NPT_BsdSocketOutputStream::Write ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocketOutputStream::Write(const void* buffer, + NPT_Size bytes_to_write, + NPT_Size* bytes_written) +{ + // if we're blocking, wait until the socket is writeable + if (m_SocketFdReference->m_Blocking && + m_SocketFdReference->m_WriteTimeout != NPT_TIMEOUT_INFINITE) { + NPT_Result result = m_SocketFdReference->WaitUntilWriteable(); + if (result != NPT_SUCCESS) return result; + } + + int flags = 0; + + // on some BSD implementations, signal we don't want a SIGPIPE + // but instead return EPIPE +#ifdef MSG_NOSIGNAL + flags |= MSG_NOSIGNAL; +#endif + + // write to the socket + ssize_t nb_written = send(m_SocketFdReference->m_SocketFd, + (SocketConstBuffer)buffer, + bytes_to_write, flags); + + if (nb_written > 0) { + if (bytes_written) *bytes_written = nb_written; + m_SocketFdReference->m_Position += nb_written; + return NPT_SUCCESS; + } else { + if (bytes_written) *bytes_written = 0; + if (nb_written == 0) { + return NPT_ERROR_CONNECTION_RESET; + } else { + return MapErrorCode(GetSocketError()); + } + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocketOutputStream::Flush ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocketOutputStream::Flush() +{ + int args = 0; + socklen_t size = sizeof(args); + + // get the value of the nagle algorithm + if (getsockopt(m_SocketFdReference->m_SocketFd, + IPPROTO_TCP, + TCP_NODELAY, + (char*)&args, + &size)) { + return NPT_ERROR_GETSOCKOPT_FAILED; + } + + // return now if nagle was already off + if (args == 1) return NPT_SUCCESS; + + // disable the nagle algorithm + args = 1; + if (setsockopt(m_SocketFdReference->m_SocketFd, + IPPROTO_TCP, + TCP_NODELAY, + (const char*)&args, + sizeof(args))) { + return NPT_ERROR_SETSOCKOPT_FAILED; + } + + // send an empty buffer to flush + char dummy; + send(m_SocketFdReference->m_SocketFd, &dummy, 0, 0); + + // restore the nagle algorithm to its original setting + args = 0; + if (setsockopt(m_SocketFdReference->m_SocketFd, + IPPROTO_TCP, + TCP_NODELAY, + (const char*)&args, + sizeof(args))) { + return NPT_ERROR_SETSOCKOPT_FAILED; + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket ++---------------------------------------------------------------------*/ +class NPT_BsdSocket : public NPT_SocketInterface +{ + public: + // constructors and destructor + NPT_BsdSocket() {} + NPT_BsdSocket(SocketFd fd, bool force_blocking=false); + virtual ~NPT_BsdSocket(); + + // methods + NPT_Result RefreshInfo(); + + // NPT_SocketInterface methods + NPT_Result Bind(const NPT_SocketAddress& address, bool reuse_address = true); + NPT_Result Connect(const NPT_SocketAddress& address, NPT_Timeout timeout); + NPT_Result Disconnect(); + NPT_Result WaitForConnection(NPT_Timeout timeout); + NPT_Result GetInputStream(NPT_InputStreamReference& stream); + NPT_Result GetOutputStream(NPT_OutputStreamReference& stream); + NPT_Result GetInfo(NPT_SocketInfo& info); + NPT_Result SetBlockingMode(bool blocking); + NPT_Result SetReadTimeout(NPT_Timeout timeout); + NPT_Result SetWriteTimeout(NPT_Timeout timeout); + + protected: + // members + NPT_BsdSocketFdReference m_SocketFdReference; + NPT_SocketInfo m_Info; + bool m_Blocking; +}; + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::NPT_BsdSocket ++---------------------------------------------------------------------*/ +NPT_BsdSocket::NPT_BsdSocket(SocketFd fd, bool force_blocking) : + m_SocketFdReference(new NPT_BsdSocketFd(fd)), + m_Blocking(true) +{ + // disable the SIGPIPE signal +#if defined(SO_NOSIGPIPE) + int option = 1; + setsockopt(m_SocketFdReference->m_SocketFd, + SOL_SOCKET, + SO_NOSIGPIPE, + (SocketOption)&option, + sizeof(option)); +#elif defined(SIGPIPE) + signal(SIGPIPE, SIG_IGN); +#endif + + if (force_blocking) m_SocketFdReference->SetBlockingMode(true); + RefreshInfo(); +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::~NPT_BsdSocket ++---------------------------------------------------------------------*/ +NPT_BsdSocket::~NPT_BsdSocket() +{ + // release the socket fd reference + m_SocketFdReference = NULL; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::Bind ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocket::Bind(const NPT_SocketAddress& address, bool reuse_address) +{ + // on non windows, we need to set reuse address no matter what so + // that we can bind on the same port when the socket has closed + // but is still in a timed-out mode +#if !defined(__WIN32__) && !defined(_XBOX) + int option_ra = 1; + setsockopt(m_SocketFdReference->m_SocketFd, + SOL_SOCKET, + SO_REUSEADDR, + (SocketOption)&option_ra, + sizeof(option_ra)); +#endif + + // set socket options + if (reuse_address) { + int option = 1; + // on macosx/linux, we need to use SO_REUSEPORT instead of SO_REUSEADDR +#if defined(SO_REUSEPORT) + setsockopt(m_SocketFdReference->m_SocketFd, + SOL_SOCKET, + SO_REUSEPORT, + (SocketOption)&option, + sizeof(option)); +#else + setsockopt(m_SocketFdReference->m_SocketFd, + SOL_SOCKET, + SO_REUSEADDR, + (SocketOption)&option, + sizeof(option)); +#endif + } + + // convert the address + struct sockaddr_in inet_address; + SocketAddressToInetAddress(address, &inet_address); + +#ifdef _XBOX + if( address.GetIpAddress().AsLong() != NPT_IpAddress::Any.AsLong() ) { + //Xbox can't bind to specific address, defaulting to ANY + SocketAddressToInetAddress(NPT_SocketAddress(NPT_IpAddress::Any, address.GetPort()), &inet_address); + } +#endif + + // bind the socket + if (bind(m_SocketFdReference->m_SocketFd, + (struct sockaddr*)&inet_address, + sizeof(inet_address)) < 0) { + return NPT_ERROR_BIND_FAILED; + } + + // refresh socket info + RefreshInfo(); + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::Connect ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocket::Connect(const NPT_SocketAddress&, NPT_Timeout) +{ + // this is unsupported unless overridden in a derived class + return NPT_ERROR_NOT_SUPPORTED; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::Disconnect ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocket::Disconnect() +{ + // check that we have a socket + if (m_SocketFdReference.IsNull()) return NPT_ERROR_INVALID_STATE; + + m_SocketFdReference->Disconnect(); + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::WaitForConnection ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocket::WaitForConnection(NPT_Timeout) +{ + // this is unsupported unless overridden in a derived class + return NPT_ERROR_NOT_SUPPORTED; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::GetInputStream ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocket::GetInputStream(NPT_InputStreamReference& stream) +{ + // default value + stream = NULL; + + // check that we have a socket + if (m_SocketFdReference.IsNull()) return NPT_ERROR_INVALID_STATE; + + // create a stream + stream = new NPT_BsdSocketInputStream(m_SocketFdReference); + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::GetOutputStream ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocket::GetOutputStream(NPT_OutputStreamReference& stream) +{ + // default value + stream = NULL; + + // check that the file is open + if (m_SocketFdReference.IsNull()) return NPT_ERROR_INVALID_STATE; + + // create a stream + stream = new NPT_BsdSocketOutputStream(m_SocketFdReference); + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::GetInfo ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocket::GetInfo(NPT_SocketInfo& info) +{ + // return the cached info + info = m_Info; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::RefreshInfo ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocket::RefreshInfo() +{ + // check that we have a socket + if (m_SocketFdReference.IsNull()) return NPT_ERROR_INVALID_STATE; + + // get the local socket addr + struct sockaddr_in inet_address; + socklen_t name_length = sizeof(inet_address); + if (getsockname(m_SocketFdReference->m_SocketFd, + (struct sockaddr*)&inet_address, + &name_length) == 0) { + m_Info.local_address.SetIpAddress(ntohl(inet_address.sin_addr.s_addr)); + m_Info.local_address.SetPort(ntohs(inet_address.sin_port)); + } + + // get the peer socket addr + if (getpeername(m_SocketFdReference->m_SocketFd, + (struct sockaddr*)&inet_address, + &name_length) == 0) { + m_Info.remote_address.SetIpAddress(ntohl(inet_address.sin_addr.s_addr)); + m_Info.remote_address.SetPort(ntohs(inet_address.sin_port)); + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::SetBlockingMode ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocket::SetBlockingMode(bool blocking) +{ + if (m_Blocking != blocking) { + m_SocketFdReference->SetBlockingMode(blocking); + m_Blocking = blocking; + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::SetReadTimeout ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocket::SetReadTimeout(NPT_Timeout timeout) +{ + m_SocketFdReference->m_ReadTimeout = timeout; + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdSocket::SetWriteTimeout ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdSocket::SetWriteTimeout(NPT_Timeout timeout) +{ + m_SocketFdReference->m_WriteTimeout = timeout; + setsockopt(m_SocketFdReference->m_SocketFd, + SOL_SOCKET, + SO_SNDTIMEO, + (SocketOption)&timeout, + sizeof(timeout)); + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Socket::~NPT_Socket ++---------------------------------------------------------------------*/ +NPT_Socket::~NPT_Socket() +{ + delete m_SocketDelegate; +} + +/*---------------------------------------------------------------------- +| NPT_BsdUdpSocket ++---------------------------------------------------------------------*/ +class NPT_BsdUdpSocket : public NPT_UdpSocketInterface, + protected NPT_BsdSocket + +{ + public: + // constructor + NPT_BsdUdpSocket(); + virtual ~NPT_BsdUdpSocket() {} + + // NPT_SocketInterface methods + NPT_Result Connect(const NPT_SocketAddress& address, + NPT_Timeout timeout); + + // NPT_UdpSocketInterface methods + NPT_Result Send(const NPT_DataBuffer& packet, + const NPT_SocketAddress* address); + NPT_Result Receive(NPT_DataBuffer& packet, + NPT_SocketAddress* address); + + // friends + friend class NPT_UdpSocket; +}; + +/*---------------------------------------------------------------------- +| NPT_BsdUdpSocket::NPT_BsdUdpSocket ++---------------------------------------------------------------------*/ +NPT_BsdUdpSocket::NPT_BsdUdpSocket() : + NPT_BsdSocket(socket(AF_INET, SOCK_DGRAM, 0)) +{ + // set default socket options + int option = 1; + setsockopt(m_SocketFdReference->m_SocketFd, + SOL_SOCKET, + SO_BROADCAST, + (SocketOption)&option, + sizeof(option)); + +#ifdef _XBOX + // set flag on the socket to allow sending of multicast + if (!NPT_BSD_SOCKET_IS_INVALID(m_SocketFdReference->m_SocketFd)) { + *(DWORD*)((char*)m_SocketFdReference->m_SocketFd+0xc) |= 0x02000000; + } +#endif + +} + +/*---------------------------------------------------------------------- +| NPT_BsdUdpSocket::Connect ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdUdpSocket::Connect(const NPT_SocketAddress& address, + NPT_Timeout /* ignored */) +{ + // setup an address structure + struct sockaddr_in inet_address; + SocketAddressToInetAddress(address, &inet_address); + + // connect so that we can have some addr bound to the socket + int io_result = connect(m_SocketFdReference->m_SocketFd, + (struct sockaddr *)&inet_address, + sizeof(inet_address)); + if (NPT_BSD_SOCKET_CALL_FAILED(io_result)) { + return MapErrorCode(GetSocketError()); + } + + // refresh socket info + RefreshInfo(); + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdUdpSocket::Send ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdUdpSocket::Send(const NPT_DataBuffer& packet, + const NPT_SocketAddress* address) +{ + // get the packet buffer + const NPT_Byte* buffer = packet.GetData(); + ssize_t buffer_length = packet.GetDataSize(); + + // if we're blocking, wait until the socket is writeable + if (m_SocketFdReference->m_Blocking && + m_SocketFdReference->m_WriteTimeout != NPT_TIMEOUT_INFINITE) { + NPT_Result result = m_SocketFdReference->WaitUntilWriteable(); + if (result != NPT_SUCCESS) return result; + } + + // send the packet buffer + int io_result; + if (address) { + // send to the specified address + + // setup an address structure + struct sockaddr_in inet_address; + SocketAddressToInetAddress(*address, &inet_address); + io_result = sendto(m_SocketFdReference->m_SocketFd, + (SocketConstBuffer)buffer, + buffer_length, + 0, + (struct sockaddr *)&inet_address, + sizeof(inet_address)); + } else { + // send to whichever addr the socket is connected + io_result = send(m_SocketFdReference->m_SocketFd, + (SocketConstBuffer)buffer, + buffer_length, + 0); + } + + // check result + if (NPT_BSD_SOCKET_CALL_FAILED(io_result)) { + return MapErrorCode(GetSocketError()); + } + + m_SocketFdReference->m_Position += buffer_length; + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdUdpSocket::Receive ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdUdpSocket::Receive(NPT_DataBuffer& packet, + NPT_SocketAddress* address) +{ + // get the packet buffer + NPT_Byte* buffer = packet.UseData(); + ssize_t buffer_size = packet.GetBufferSize(); + + // check that we have some space to receive + if (buffer_size == 0) { + return NPT_ERROR_INVALID_PARAMETERS; + } + + // if we're blocking, wait until the socket is readable + if (m_SocketFdReference->m_Blocking && + m_SocketFdReference->m_ReadTimeout != NPT_TIMEOUT_INFINITE) { + NPT_Result result = m_SocketFdReference->WaitUntilReadable(); + if (result != NPT_SUCCESS) return result; + } + + // receive a packet + int io_result; + if (address) { + struct sockaddr_in inet_address; + socklen_t inet_address_length = sizeof(inet_address); + io_result = recvfrom(m_SocketFdReference->m_SocketFd, + (SocketBuffer)buffer, + buffer_size, + 0, + (struct sockaddr *)&inet_address, + &inet_address_length); + + // convert the address format + if (!NPT_BSD_SOCKET_CALL_FAILED(io_result)) { + if (inet_address_length == sizeof(inet_address)) { + InetAddressToSocketAddress(&inet_address, *address); + } + } + } else { + io_result = recv(m_SocketFdReference->m_SocketFd, + (SocketBuffer)buffer, + buffer_size, + 0); + } + + // check result + if (NPT_BSD_SOCKET_CALL_FAILED(io_result)) { + packet.SetDataSize(0); + return MapErrorCode(GetSocketError()); + } + + packet.SetDataSize(io_result); + m_SocketFdReference->m_Position += io_result; + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_UdpSocket::NPT_UdpSocket ++---------------------------------------------------------------------*/ +NPT_UdpSocket::NPT_UdpSocket() +{ + NPT_BsdUdpSocket* delegate = new NPT_BsdUdpSocket(); + m_SocketDelegate = delegate; + m_UdpSocketDelegate = delegate; +} + +/*---------------------------------------------------------------------- +| NPT_UdpSocket::NPT_UdpSocket ++---------------------------------------------------------------------*/ +NPT_UdpSocket::NPT_UdpSocket(NPT_UdpSocketInterface* delegate) : + m_UdpSocketDelegate(delegate) +{ +} + +/*---------------------------------------------------------------------- +| NPT_UdpSocket::~NPT_UdpSocket ++---------------------------------------------------------------------*/ +NPT_UdpSocket::~NPT_UdpSocket() +{ + delete m_UdpSocketDelegate; + + // set the delegate pointers to NULL because it is shared by the + // base classes, and we only want to delete the object once + m_UdpSocketDelegate = NULL; + m_SocketDelegate = NULL; +} + +/*---------------------------------------------------------------------- +| NPT_BsdUdpMulticastSocket ++---------------------------------------------------------------------*/ +class NPT_BsdUdpMulticastSocket : public NPT_UdpMulticastSocketInterface, + protected NPT_BsdUdpSocket + +{ + public: + // methods + NPT_BsdUdpMulticastSocket(); + ~NPT_BsdUdpMulticastSocket(); + + // NPT_UdpMulticastSocketInterface methods + NPT_Result JoinGroup(const NPT_IpAddress& group, + const NPT_IpAddress& iface); + NPT_Result LeaveGroup(const NPT_IpAddress& group, + const NPT_IpAddress& iface); + NPT_Result SetTimeToLive(unsigned char ttl); + NPT_Result SetInterface(const NPT_IpAddress& iface); + + // friends + friend class NPT_UdpMulticastSocket; +}; + +/*---------------------------------------------------------------------- +| NPT_BsdUdpMulticastSocket::NPT_BsdUdpMulticastSocket ++---------------------------------------------------------------------*/ +NPT_BsdUdpMulticastSocket::NPT_BsdUdpMulticastSocket() +{ +#ifndef _XBOX + int option = 1; + setsockopt(m_SocketFdReference->m_SocketFd, + IPPROTO_IP, + IP_MULTICAST_LOOP, + (SocketOption)&option, + sizeof(option)); +#endif +} + +/*---------------------------------------------------------------------- +| NPT_BsdUdpMulticastSocket::~NPT_BsdUdpMulticastSocket ++---------------------------------------------------------------------*/ +NPT_BsdUdpMulticastSocket::~NPT_BsdUdpMulticastSocket() +{ +} + +#if defined(_XBOX) +/*---------------------------------------------------------------------- +| NPT_BsdUdpMulticastSocket::JoinGroup ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdUdpMulticastSocket::JoinGroup(const NPT_IpAddress& group, + const NPT_IpAddress& iface) +{ + return NPT_SUCCESS; +} +#else +/*---------------------------------------------------------------------- +| NPT_BsdUdpMulticastSocket::JoinGroup ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdUdpMulticastSocket::JoinGroup(const NPT_IpAddress& group, + const NPT_IpAddress& iface) +{ + struct ip_mreq mreq; + + // set the interface address + mreq.imr_interface.s_addr = htonl(iface.AsLong()); + + // set the group address + mreq.imr_multiaddr.s_addr = htonl(group.AsLong()); + + // set socket option + int io_result = setsockopt(m_SocketFdReference->m_SocketFd, + IPPROTO_IP, IP_ADD_MEMBERSHIP, + (SocketOption)&mreq, sizeof(mreq)); + if (io_result == 0) { + return NPT_SUCCESS; + } else { + return MapErrorCode(GetSocketError()); + } +} +#endif + +#if defined(_XBOX) +/*---------------------------------------------------------------------- +| NPT_BsdUdpMulticastSocket::LeaveGroup ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdUdpMulticastSocket::LeaveGroup(const NPT_IpAddress& group, + const NPT_IpAddress& iface) +{ + return NPT_SUCCESS; +} +#else +/*---------------------------------------------------------------------- +| NPT_BsdUdpMulticastSocket::LeaveGroup ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdUdpMulticastSocket::LeaveGroup(const NPT_IpAddress& group, + const NPT_IpAddress& iface) +{ + struct ip_mreq mreq; + + // set the interface address + mreq.imr_interface.s_addr = htonl(iface.AsLong()); + + // set the group address + mreq.imr_multiaddr.s_addr = htonl(group.AsLong()); + + // set socket option + int io_result = setsockopt(m_SocketFdReference->m_SocketFd, + IPPROTO_IP, IP_DROP_MEMBERSHIP, + (SocketOption)&mreq, sizeof(mreq)); + if (io_result == 0) { + return NPT_SUCCESS; + } else { + return MapErrorCode(GetSocketError()); + } +} +#endif + +#if defined(_XBOX) +/*---------------------------------------------------------------------- +| NPT_BsdUdpMulticastSocket::SetInterface ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdUdpMulticastSocket::SetInterface(const NPT_IpAddress& iface) +{ + return NPT_SUCCESS; +} +#else +/*---------------------------------------------------------------------- +| NPT_BsdUdpMulticastSocket::SetInterface ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdUdpMulticastSocket::SetInterface(const NPT_IpAddress& iface) +{ + struct in_addr iface_addr; + // set the interface address + iface_addr.s_addr = htonl(iface.AsLong()); + + // set socket option + int io_result = setsockopt(m_SocketFdReference->m_SocketFd, + IPPROTO_IP, IP_MULTICAST_IF, + (char*)&iface_addr, sizeof(iface_addr)); + if (io_result == 0) { + return NPT_SUCCESS; + } else { + return MapErrorCode(GetSocketError()); + } +} +#endif + +#if defined(_XBOX) +/*---------------------------------------------------------------------- +| NPT_BsdUdpMulticastSocket::SetTimeToLive ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdUdpMulticastSocket::SetTimeToLive(unsigned char ttl) +{ + return NPT_ERROR_NOT_IMPLEMENTED; +} +#else +/*---------------------------------------------------------------------- +| NPT_BsdUdpMulticastSocket::SetTimeToLive ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdUdpMulticastSocket::SetTimeToLive(unsigned char ttl) +{ + unsigned char ttl_opt = ttl; + + // set socket option + int io_result = setsockopt(m_SocketFdReference->m_SocketFd, + IPPROTO_IP, IP_MULTICAST_TTL, + (SocketOption)&ttl_opt, sizeof(ttl_opt)); + if (io_result == 0) { + return NPT_SUCCESS; + } else { + return MapErrorCode(GetSocketError()); + } +} +#endif + +/*---------------------------------------------------------------------- +| NPT_UdpMulticastSocket::NPT_UdpMulticastSocket ++---------------------------------------------------------------------*/ +NPT_UdpMulticastSocket::NPT_UdpMulticastSocket() : + NPT_UdpSocket(NULL) +{ + NPT_BsdUdpMulticastSocket* delegate = new NPT_BsdUdpMulticastSocket(); + m_SocketDelegate = delegate; + m_UdpSocketDelegate = delegate; + m_UdpMulticastSocketDelegate = delegate; +} + +/*---------------------------------------------------------------------- +| NPT_UdpMulticastSocket::~NPT_UdpMulticastSocket ++---------------------------------------------------------------------*/ +NPT_UdpMulticastSocket::~NPT_UdpMulticastSocket() +{ + delete m_UdpMulticastSocketDelegate; + + // set the delegate pointers to NULL because it is shared by the + // base classes, and we only want to delete the object once + m_SocketDelegate = NULL; + m_UdpSocketDelegate = NULL; + m_UdpMulticastSocketDelegate = NULL; +} + +/*---------------------------------------------------------------------- +| NPT_BsdTcpClientSocket ++---------------------------------------------------------------------*/ +class NPT_BsdTcpClientSocket : protected NPT_BsdSocket +{ + public: + // methods + NPT_BsdTcpClientSocket(); + ~NPT_BsdTcpClientSocket(); + + // NPT_SocketInterface methods + NPT_Result Connect(const NPT_SocketAddress& address, + NPT_Timeout timeout); + NPT_Result WaitForConnection(NPT_Timeout timeout); + +protected: + // methods + NPT_Result DoWaitForConnection(NPT_Timeout timeout); + + // friends + friend class NPT_TcpClientSocket; +}; + +/*---------------------------------------------------------------------- +| NPT_BsdTcpClientSocket::NPT_BsdTcpClientSocket ++---------------------------------------------------------------------*/ +NPT_BsdTcpClientSocket::NPT_BsdTcpClientSocket() : + NPT_BsdSocket(socket(AF_INET, SOCK_STREAM, 0)) +{ +} + +/*---------------------------------------------------------------------- +| NPT_BsdTcpClientSocket::~NPT_BsdTcpClientSocket ++---------------------------------------------------------------------*/ +NPT_BsdTcpClientSocket::~NPT_BsdTcpClientSocket() +{ +} + +/*---------------------------------------------------------------------- +| NPT_BsdTcpClientSocket::Connect ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdTcpClientSocket::Connect(const NPT_SocketAddress& address, + NPT_Timeout timeout) +{ + bool was_blocking = m_Blocking; + + // set the socket to nonblocking so that we can timeout on connect + if (m_Blocking) { + NPT_CHECK(m_SocketFdReference->SetBlockingMode(false)); + } + + // convert the address + struct sockaddr_in inet_address; + SocketAddressToInetAddress(address, &inet_address); + + // initiate connection + int io_result; + io_result = connect(m_SocketFdReference->m_SocketFd, + (struct sockaddr *)&inet_address, + sizeof(inet_address)); + if (io_result == 0) { + // immediate connection + + // put the fd back in its original blocking mode + if (was_blocking) m_SocketFdReference->SetBlockingMode(true); + + // get socket info + RefreshInfo(); + + return NPT_SUCCESS; + } + + NPT_Result result = MapErrorCode(GetSocketError()); + + if (was_blocking) { + // put the fd back in its original blocking mode + m_SocketFdReference->SetBlockingMode(true); + + // wait for the connection to be done + if (result == NPT_ERROR_WOULD_BLOCK) return DoWaitForConnection(timeout); + } + + return result; +} + +/*---------------------------------------------------------------------- +| NPT_BsdTcpClientSocket::WaitForConnection ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdTcpClientSocket::WaitForConnection(NPT_Timeout timeout) +{ + // this function can only be called directly for non-blocking sockets + if (m_Blocking) return NPT_ERROR_INVALID_STATE; + + return DoWaitForConnection(timeout); +} + +/*---------------------------------------------------------------------- +| NPT_BsdTcpClientSocket::DoWaitForConnection ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdTcpClientSocket::DoWaitForConnection(NPT_Timeout timeout) +{ + SocketFd socket_fd = m_SocketFdReference->m_SocketFd; + NPT_Result result = NPT_SUCCESS; + + // wait for connection to succeed or fail + fd_set read_set; + fd_set write_set; + fd_set except_set; + FD_ZERO(&read_set); + FD_SET(socket_fd, &read_set); + FD_ZERO(&write_set); + FD_SET(socket_fd, &write_set); + FD_ZERO(&except_set); + FD_SET(socket_fd, &except_set); + + struct timeval timeout_value; + if (timeout != NPT_TIMEOUT_INFINITE) { + timeout_value.tv_sec = timeout/1000; + timeout_value.tv_usec = 1000*(timeout-1000*(timeout/1000)); + }; + + int nfds = (int)socket_fd+1; + +#if !defined(__WIN32__) && !defined(_XBOX) + FD_SET(m_SocketFdReference->m_AbortPipe[0], &read_set); + nfds = (m_SocketFdReference->m_AbortPipe[0]>(int)socket_fd?m_SocketFdReference->m_AbortPipe[0]:(int)socket_fd)+1; +#endif + + int io_result = select(nfds, + &read_set, &write_set, &except_set, + timeout == NPT_TIMEOUT_INFINITE ? + NULL : &timeout_value); + + if (io_result == 0) { + if (timeout == 0) { + // non-blocking call + return NPT_ERROR_WOULD_BLOCK; + } else { + // timeout + return NPT_ERROR_TIMEOUT; + } + } else if (NPT_BSD_SOCKET_SELECT_FAILED(io_result)) { + return MapErrorCode(GetSocketError()); + } else if (FD_ISSET(socket_fd, &read_set) || + FD_ISSET(socket_fd, &write_set) || + FD_ISSET(socket_fd, &except_set)) { +#if defined(_XBOX) + if (FD_ISSET(socket_fd, &except_set)) return MapErrorCode(GetSocketError()); +#else + // get error status from socket + // (some systems return the error in errno, others + // return it in the buffer passed to getsockopt) + int error = 0; + socklen_t length = sizeof(error); + io_result = getsockopt(socket_fd, + SOL_SOCKET, + SO_ERROR, + (SocketOption)&error, + &length); + if (NPT_BSD_SOCKET_CALL_FAILED(io_result)) { + return MapErrorCode(GetSocketError()); + } else if (error) { + return MapErrorCode(error); + } +#endif +#if !defined(__WIN32__) && !defined(_XBOX) + } else if (FD_ISSET(m_SocketFdReference->m_AbortPipe[0], &read_set)) { + result = NPT_ERROR_CONNECTION_ABORTED; +#endif + } else { + // should not happen + result = (socket_fd == 0)?NPT_ERROR_EOS:NPT_ERROR_INTERNAL; + } + + + // get socket info + RefreshInfo(); + + return result; +} + +/*---------------------------------------------------------------------- +| NPT_TcpClientSocket::NPT_TcpClientSocket ++---------------------------------------------------------------------*/ +NPT_TcpClientSocket::NPT_TcpClientSocket() : + NPT_Socket(new NPT_BsdTcpClientSocket()) +{ +} + +/*---------------------------------------------------------------------- +| NPT_TcpClientSocket::NPT_TcpClientSocket ++---------------------------------------------------------------------*/ +NPT_TcpClientSocket::~NPT_TcpClientSocket() +{ + delete m_SocketDelegate; + + // set the delegate pointer to NULL because it is shared by the + // base classes, and we only want to delete the object once + m_SocketDelegate = NULL; +} + +/*---------------------------------------------------------------------- +| NPT_BsdTcpServerSocket ++---------------------------------------------------------------------*/ +class NPT_BsdTcpServerSocket : public NPT_TcpServerSocketInterface, + protected NPT_BsdSocket + +{ + public: + // methods + NPT_BsdTcpServerSocket(); + ~NPT_BsdTcpServerSocket(); + + // NPT_SocketInterface methods + NPT_Result Bind(const NPT_SocketAddress& address, bool reuse_address = true) { + // inherit + return NPT_BsdSocket::Bind(address, reuse_address); + } + NPT_Result Connect(const NPT_SocketAddress& address, + NPT_Timeout timeout) { + // inherit + return NPT_BsdSocket::Connect(address, timeout); + } + NPT_Result GetInputStream(NPT_InputStreamReference& stream) { + // no stream + stream = NULL; + return NPT_ERROR_NOT_SUPPORTED; + } + NPT_Result GetOutputStream(NPT_OutputStreamReference& stream) { + // no stream + stream = NULL; + return NPT_ERROR_NOT_SUPPORTED; + } + NPT_Result GetInfo(NPT_SocketInfo& info) { + // inherit + return NPT_BsdSocket::GetInfo(info); + } + + // NPT_TcpServerSocket methods + NPT_Result Listen(unsigned int max_clients); + NPT_Result WaitForNewClient(NPT_Socket*& client, NPT_Timeout timeout); + +protected: + // members + unsigned int m_ListenMax; + + // friends + friend class NPT_TcpServerSocket; +}; + +/*---------------------------------------------------------------------- +| NPT_BsdTcpServerSocket::NPT_BsdTcpServerSocket ++---------------------------------------------------------------------*/ +NPT_BsdTcpServerSocket::NPT_BsdTcpServerSocket() : + NPT_BsdSocket(socket(AF_INET, SOCK_STREAM, 0)), + m_ListenMax(0) +{ +} + +/*---------------------------------------------------------------------- +| NPT_BsdTcpServerSocket::~NPT_BsdTcpServerSocket ++---------------------------------------------------------------------*/ +NPT_BsdTcpServerSocket::~NPT_BsdTcpServerSocket() +{ +} + +/*---------------------------------------------------------------------- +| NPT_BsdTcpServerSocket::Listen ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdTcpServerSocket::Listen(unsigned int max_clients) +{ + // listen for connections + if (listen(m_SocketFdReference->m_SocketFd, max_clients) < 0) { + m_ListenMax = 0; + return NPT_ERROR_LISTEN_FAILED; + } + m_ListenMax = max_clients; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BsdTcpServerSocket::WaitForNewClient ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BsdTcpServerSocket::WaitForNewClient(NPT_Socket*& client, + NPT_Timeout timeout) +{ + // default value + client = NULL; + + // check that we are listening for clients + if (m_ListenMax == 0) { + Listen(NPT_TCP_SERVER_SOCKET_DEFAULT_LISTEN_COUNT); + } + + // set the socket to nonblocking so that we can timeout on accept + bool was_blocking = m_Blocking; + if (m_Blocking) { + NPT_CHECK(m_SocketFdReference->SetBlockingMode(false)); + } + + NPT_Result result = m_SocketFdReference->WaitForCondition(true, true, timeout); + if (result != NPT_SUCCESS) return result; + + struct sockaddr_in inet_address; + socklen_t namelen = sizeof(inet_address); + SocketFd socket_fd = accept(m_SocketFdReference->m_SocketFd, (struct sockaddr*)&inet_address, &namelen); + if (NPT_BSD_SOCKET_IS_INVALID(socket_fd)) { + result = MapErrorCode(GetSocketError()); + } else { + client = new NPT_Socket(new NPT_BsdSocket(socket_fd, m_Blocking)); + } + + if (was_blocking) { + // put the fd back in its original blocking mode + m_SocketFdReference->SetBlockingMode(true); + } + + // done + return result; +} + +/*---------------------------------------------------------------------- +| NPT_TcpServerSocket::NPT_TcpServerSocket ++---------------------------------------------------------------------*/ +NPT_TcpServerSocket::NPT_TcpServerSocket() +{ + NPT_BsdTcpServerSocket* delegate = new NPT_BsdTcpServerSocket(); + m_SocketDelegate = delegate; + m_TcpServerSocketDelegate = delegate; +} + +/*---------------------------------------------------------------------- +| NPT_TcpServerSocket::NPT_TcpServerSocket ++---------------------------------------------------------------------*/ +NPT_TcpServerSocket::~NPT_TcpServerSocket() +{ + delete m_TcpServerSocketDelegate; + + // set the delegate pointers to NULL because it is shared by the + // base classes, and we only want to delete the object once + m_SocketDelegate = NULL; + m_TcpServerSocketDelegate = NULL; +}