/*
	Copyright (C) 2004 Christopher E. Miller
	
	This software is provided 'as-is', without any express or implied
	warranty.  In no event will the authors be held liable for any damages
	arising from the use of this software.
	
	Permission is granted to anyone to use this software for any purpose,
	including commercial applications, and to alter it and redistribute it
	freely, subject to the following restrictions:
	
	1. The origin of this software must not be misrepresented; you must not
	   claim that you wrote the original software. If you use this software
	   in a product, an acknowledgment in the product documentation would be
	   appreciated but is not required.
	2. Altered source versions must be plainly marked as such, and must not
	   be misrepresented as being the original software.
	3. This notice may not be removed or altered from any source
	   distribution.
*/

/* NOTE: This file has been patched from the original DMD distribution to
   work with the GDC compiler.

   Modified by David Friedman, September 2004
*/

// socket.d 1.2
// Apr 2004

module std.socket;


version(linux)
{
	version = BsdSockets;
}
version(Unix)
{
    version = BsdSockets;
}

version(Win32)
{
	typedef uint socket_t = ~0u;
}
else version(BsdSockets)
{
	typedef int socket_t = -1;
}
else
{
	static assert(0); // No socket support yet
}
const socket_t INVALID_SOCKET = socket_t.init;
const int SOCKET_ERROR = -1;


private:

import std.string, std.stdint, std.c.stdlib;


version(Win32)
{
	import std.c.windows.windows;
	
	
	extern(Windows)
	{
		const int WSADESCRIPTION_LEN = 256;
		const int WSASYS_STATUS_LEN = 128;
		
		struct WSADATA
		{
			WORD wVersion;
			WORD wHighVersion;
			char szDescription[WSADESCRIPTION_LEN+1];
			char szSystemStatus[WSASYS_STATUS_LEN+1];
			ushort iMaxSockets;
			ushort iMaxUdpDg;
			char* lpVendorInfo;
		}
		alias WSADATA* LPWSADATA;
		
		
		const int IOCPARM_MASK =  0x7f;
		const int IOC_IN =        cast(int)0x80000000;
		const int FIONBIO =       cast(int)(IOC_IN | ((uint.sizeof & IOCPARM_MASK) << 16) | (102 << 8) | 126);
		const int SOL_SOCKET =    0xFFFF;
		const int SO_TYPE =       0x1008;
		
		
		int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
		int WSACleanup();
		socket_t socket(int af, int type, int protocol);
		int ioctlsocket(socket_t s, int cmd, uint* argp);
		int getsockopt(socket_t s, int level, int optname, char* optval, int* optlen);
		uint inet_addr(char* cp);
		int bind(socket_t s, sockaddr* name, int namelen);
		int connect(socket_t s, sockaddr* name, int namelen);
		int listen(socket_t s, int backlog);
		socket_t accept(socket_t s, sockaddr* addr, int* addrlen);
		int closesocket(socket_t s);
		int shutdown(socket_t s, int how);
		int getpeername(socket_t s, sockaddr* name, int* namelen);
		int getsockname(socket_t s, sockaddr* name, int* namelen);
		int send(socket_t s, void* buf, int len, int flags);
		int sendto(socket_t s, void* buf, int len, int flags, sockaddr* to, int tolen);
		int recv(socket_t s, void* buf, int len, int flags);
		int recvfrom(socket_t s, void* buf, int len, int flags, sockaddr* from, int* fromlen);
		int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, timeval* timeout);
		//int __WSAFDIsSet(socket_t s, fd_set* fds);
		int getsockopt(socket_t s, int level, int optname, void* optval, int* optlen);
		int setsockopt(socket_t s, int level, int optname, void* optval, int optlen);
		char* inet_ntoa(uint ina);
		hostent* gethostbyname(char* name);
		hostent* gethostbyaddr(void* addr, int len, int type);
		
		
		const int WSAEWOULDBLOCK =  10035;
		const int WSAEINTR =        10004;
		
		int WSAGetLastError();
	}
}
else version(BsdSockets)
{
    version (GNU) {
	private import std.c.unix;
    }
	extern(C)
	{
	    version (GNU) {
		// nothing
	    } else {
		const int F_GETFL =       3;
		const int F_SETFL =       4;
		const int O_NONBLOCK =    0x4000;
		const int SOL_SOCKET =    0xFFFF;
		const int SO_TYPE =       0x1008;
	    }
		
		
		socket_t socket(int af, int type, int protocol);
		int fcntl(socket_t s, int f, ...);
		int getsockopt(socket_t s, int level, int optname, char* optval, int* optlen);
		uint inet_addr(char* cp);
		int bind(socket_t s, sockaddr* name, int namelen);
		int connect(socket_t s, sockaddr* name, int namelen);
		int listen(socket_t s, int backlog);
		socket_t accept(socket_t s, sockaddr* addr, int* addrlen);
		int close(socket_t s);
		int shutdown(socket_t s, int how);
		int getpeername(socket_t s, sockaddr* name, int* namelen);
		int getsockname(socket_t s, sockaddr* name, int* namelen);
		int send(socket_t s, void* buf, int len, int flags);
		int sendto(socket_t s, void* buf, int len, int flags, sockaddr* to, int tolen);
		int recv(socket_t s, void* buf, int len, int flags);
		int recvfrom(socket_t s, void* buf, int len, int flags, sockaddr* from, int* fromlen);
		int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, timeval* timeout);
		int getsockopt(socket_t s, int level, int optname, void* optval, int* optlen);
		int setsockopt(socket_t s, int level, int optname, void* optval, int optlen);
		char* inet_ntoa(uint ina);
		hostent* gethostbyname(char* name);
		hostent* gethostbyaddr(void* addr, int len, int type);
		
		
		version (GNU) {
		    // nothing
		} else {
		    const int EINTR =           4;
		    version(linux)
		    {
			    const int EINPROGRESS =  115; //EWOULDBLOCK
			    
			    
			    import std.c.linux.linux; //for getErrno
		    }
		    else
		    {
			    static assert(0);
		    }
		}
	}
}


version (GNU) {
    // already have one
} else {
//transparent
struct fd_set
{
}
}


struct sockaddr
{
    version(BsdSockets_salen) {
	ubyte  sa_len;
	ubyte  sa_family;
    } else {
	ushort sa_family;               
    }
	char[14] sa_data = [0];             
}


struct hostent
{
	char* h_name;
	char** h_aliases;
	version(Win32)
	{
		short h_addrtype;
		short h_length;
	}
	else version(BsdSockets)
	{
		int h_addrtype;
		int h_length;
	}
	char** h_addr_list;
	
	
	char* h_addr()
	{
		return h_addr_list[0];
	}
}


version(BigEndian)
{
	uint16_t htons(uint16_t x)
	{
		return x;
	}
	
	
	uint32_t htonl(uint32_t x)
	{
		return x;
	}
}
else version(LittleEndian)
{
	import std.intrinsic;
	
	
	uint16_t htons(uint16_t x)
	{
		return (x >> 8) | (x << 8);
	}


	uint32_t htonl(uint32_t x)
	{
		return bswap(x);
	}
}
else
{
	static assert(0);
}


uint16_t ntohs(uint16_t x)
{
	return htons(x);
}


uint32_t ntohl(uint32_t x)
{
	return htonl(x);
}


public:
class SocketException: Exception
{
	this(char[] msg)
	{
		super(msg);
	}
}


static this()
{
	version(Win32)
	{
		WSADATA wd;
		if(WSAStartup(0x0101, &wd))
			throw new SocketException("Unable to initialize socket library.");
	}
}


static ~this()
{
	version(Win32)
	{
		WSACleanup();
	}
}


version(Win32)
{
	enum AddressFamily: int
	{
		UNSPEC =     0,
		UNIX =       1,
		INET =       2,
		IPX =        6,
		APPLETALK =  16,
		//INET6 =      ? // Need Windows XP ?
	}
}
else version(BsdSockets)
{
	enum AddressFamily: int
	{
		UNSPEC =     0,
		UNIX =       1,
		INET =       2,
		IPX =        4,
		APPLETALK =  5,
		//INET6 =      10,
	}
}


enum SocketType: int
{
	STREAM =     1,
	DGRAM =      2,
	RAW =        3,
	RDM =        4,
	SEQPACKET =  5,
}


enum ProtocolType: int
{
	IP =    0,
	ICMP =  1,
	IGMP =  2,
	GGP =   3,
	TCP =   6,
	PUP =   12,
	UDP =   17,
	IDP =   22,
}


class AddressException: Exception
{
	this(char[] msg)
	{
		super(msg);
	}
}


abstract class Address
{
	protected sockaddr* name();
	protected int nameLen();
	AddressFamily addressFamily();
	char[] toString();
}


class UnknownAddress: Address
{
	protected:
	sockaddr sa;
	
	
	sockaddr* name()
	{
		return &sa;
	}
	
	
	int nameLen()
	{
		return sa.sizeof;
	}
	
	
	public:
	AddressFamily addressFamily()
	{
		return cast(AddressFamily)sa.sa_family;
	}
	
	
	char[] toString()
	{
		return "Unknown";
	}
}


class HostException: Exception
{
	this(char[] msg)
	{
		super(msg);
	}
}


class InternetHost
{
	char[] name;
	char[][] aliases;
	uint[] addrList;
	
	
	protected void validHostent(hostent* he)
	{
		if(he.h_addrtype != cast(int)AddressFamily.INET || he.h_length != 4)
			throw new HostException("Address family mismatch.");
	}
	
	
	void populate(hostent* he)
	{
		int i;
		char* p;
		
		name = std.string.toString(he.h_name);
		
		for(i = 0;; i++)
		{
			p = he.h_aliases[i];
			if(!p)
				break;
		}
		
		if(i)
		{
			aliases = new char[][i];
			for(i = 0; i != aliases.length; i++)
			{
				aliases[i] = std.string.toString(he.h_aliases[i]);
			}
		}
		else
		{
			aliases = null;
		}
		
		for(i = 0;; i++)
		{
			p = he.h_addr_list[i];
			if(!p)
				break;
		}
		
		if(i)
		{
			addrList = new uint[i];
			for(i = 0; i != addrList.length; i++)
			{
				addrList[i] = ntohl(*(cast(uint*)he.h_addr_list[i]));
			}
		}
		else
		{
			addrList = null;
		}
	}
	
	
	bit getHostByName(char[] name)
	{
		hostent* he = gethostbyname(toStringz(name));
		if(!he)
			return false;
		validHostent(he);
		populate(he);
		return true;
	}
	
	
	bit getHostByAddr(uint addr)
	{
		uint x = htonl(addr);
		hostent* he = gethostbyaddr(&x, 4, cast(int)AddressFamily.INET);
		if(!he)
			return false;
		validHostent(he);
		populate(he);
		return true;
	}
	
	
	//shortcut
	bit getHostByAddr(char[] addr)
	{
		uint x = inet_addr(std.string.toStringz(addr));
		hostent* he = gethostbyaddr(&x, 4, cast(int)AddressFamily.INET);
		if(!he)
			return false;
		validHostent(he);
		populate(he);
		return true;
	}
}


unittest
{
	InternetHost ih = new InternetHost;
	assert(ih.getHostByName("www.digitalmars.com"));
	printf("addrList.length = %d\n", ih.addrList.length);
	assert(ih.addrList.length);
	InternetAddress ia = new InternetAddress(ih.addrList[0], InternetAddress.PORT_ANY);
	printf("IP address = %.*s\nname = %.*s\n", ia.toAddrString(), ih.name);
	foreach(int i, char[] s; ih.aliases)
	{
		printf("aliases[%d] = %.*s\n", i, s);
	}
	
	printf("---\n");
	
	assert(ih.getHostByAddr(ih.addrList[0]));
	printf("name = %.*s\n", ih.name);
	foreach(int i, char[] s; ih.aliases)
	{
		printf("aliases[%d] = %.*s\n", i, s);
	}
}


class InternetAddress: Address
{
	protected:
	struct sockaddr_in
	{
	    version( BsdSockets_salen ) {
		ubyte sin_len = sockaddr_in.sizeof;
		ubyte sin_family = cast(ushort)AddressFamily.INET;
	    } else {
		ushort sin_family = cast(ushort)AddressFamily.INET;
	    }
		ushort sin_port;
		uint sin_addr; //in_addr
		char[8] sin_zero = [0];
	}
	sockaddr_in sin;


	sockaddr* name()
	{
		return cast(sockaddr*)&sin;
	}
	
	
	int nameLen()
	{
		return sin.sizeof;
	}
	
	
	this()
	{
	}
	
	
	public:
	const uint ADDR_ANY = 0;
	const uint ADDR_NONE = cast(uint)-1;
	const ushort PORT_ANY = 0;
	
	
	AddressFamily addressFamily()
	{
		return AddressFamily.INET;
	}
	
	
	ushort port()
	{
		return ntohs(sin.sin_port);
	}
	
	
	uint addr()
	{
		return ntohl(sin.sin_addr);
	}
	
	
	//-port- can be PORT_ANY
	//-addr- is an IP address or host name
	this(char[] addr, ushort port)
	{
		uint uiaddr = parse(addr);
		if(ADDR_NONE == uiaddr)
		{
			InternetHost ih = new InternetHost;
			if(!ih.getHostByName(addr))
				throw new AddressException("Invalid internet address.");
			uiaddr = ih.addrList[0];
		}
		sin.sin_addr = htonl(uiaddr);
		sin.sin_port = htons(port);
	}
	
	
	this(uint addr, ushort port)
	{
		sin.sin_addr = htonl(addr);
		sin.sin_port = htons(port);
	}
	
	
	this(ushort port)
	{
		sin.sin_addr = 0; //any, "0.0.0.0"
		sin.sin_port = htons(port);
	}
	
	
	char[] toAddrString()
	{
		return std.string.toString(inet_ntoa(sin.sin_addr)).dup;
	}
	
	
	char[] toPortString()
	{
		return std.string.toString(port());
	}
	
	
	char[] toString()
	{
		return toAddrString() ~ ":" ~ toPortString();
	}
	
	
	//-addr- is an IP address in the format "a.b.c.d"
	//returns ADDR_NONE on failure
	static uint parse(char[] addr)
	{
		return ntohl(inet_addr(std.string.toStringz(addr)));
	}
}


unittest
{
	InternetAddress ia = new InternetAddress("63.105.9.61", 80);
	assert(ia.toString() == "63.105.9.61:80");
}


class SocketAcceptException: SocketException
{
	this(char[] msg)
	{
		super(msg);
	}
}


enum SocketShutdown: int
{
	RECEIVE =  0,
	SEND =     1,
	BOTH =     2,
}


enum SocketFlags: int
{
	NONE =           0,
	OOB =            0x1, //out of band
	PEEK =           0x02, //only for receiving
	DONTROUTE =      0x04, //only for sending
}



struct timeval
{
	// D interface
	version (GNU) {
	    // typeof(std.c.uni...) doesn't work..
	    alias std.c.unix.timeval __unix_timeval;
	    typeof(__unix_timeval.tv_sec) seconds;
	    typeof(__unix_timeval.tv_usec) microseconds;
	} else {
	    int seconds;
	    int microseconds;
	}
	
	// C interface
	deprecated
	{
		alias seconds tv_sec;
		alias microseconds tv_usec;
	}
}


//a set of sockets for Socket.select()
class SocketSet
{
	private:
	uint nbytes; //Win32: excludes uint.sizeof "count"
	byte* buf;
	
	
	version(Win32)
	{
		uint count()
		{
			return *(cast(uint*)buf);
		}
		
		
		void count(int setter)
		{
			*(cast(uint*)buf) = setter;
		}
		
		
		socket_t* first()
		{
			return cast(socket_t*)(buf + uint.sizeof);
		}
	}
	else version(linux)
	{
		import std.intrinsic;
		
		
		uint nfdbits;
		
		
		uint fdelt(socket_t s)
		{
			return cast(uint)s / nfdbits;
		}
		
		
		uint fdmask(socket_t s)
		{
			return 1 << cast(uint)s % nfdbits;
		}
		
		
		uint* first()
		{
			return cast(uint*)buf;
		}
	}
	
	
	public:
	this(uint max)
	{
		version(Win32)
		{
			nbytes = max * socket_t.sizeof;
			buf = new byte[nbytes + uint.sizeof];
			count = 0;
		}
		else version(linux)
		{
			if(max <= 32)
				nbytes = 32 * uint.sizeof;
			else
				nbytes = max * uint.sizeof;
			buf = new byte[nbytes];
			nfdbits = nbytes * 8;
			//clear(); //new initializes to 0
		}
		else version(GNU)
		{
		    assert(max <= FD_SETSIZE);
		    nbytes = fd_set.sizeof;
		    buf = new byte[nbytes];
		}
		else
		{
			static assert(0);
		}
	}
	
	
	this()
	{
		version(Win32)
		{
			this(64);
		}
		else version(linux)
		{
			this(32);
		}
		else version(GNU)
		{
			this(FD_SETSIZE);
		}
		else
		{
			static assert(0);
		}
	}
	
	
	void reset()
	{
		version(Win32)
		{
			count = 0;
		}
		else version(linux)
		{
			buf[0 .. nbytes] = 0;
		}
		else version(GNU)
		{
		    FD_ZERO(*cast(fd_set*)buf);
		}
		else
		{
			static assert(0);
		}
	}
	
	
	void add(socket_t s)
	in
	{
		version(Win32)
		{
			assert(count < max); //added too many sockets; specify a higher max in the constructor
		}
	}
	body
	{
		version(Win32)
		{
			uint c = count;
			first[c] = s;
			count = c + 1;
		}
		else version(linux)
		{
			bts(cast(uint*)&first[fdelt(s)], cast(uint)s % nfdbits);
		}
		else version(GNU)
		{
			FD_SET(s, *cast(fd_set*)buf);
		}
		else
		{
			static assert(0);
		}
	}
	
	
	void add(Socket s)
	{
		add(s.sock);
	}
	
	
	void remove(socket_t s)
	{
		version(Win32)
		{
			uint c = count;
			socket_t* start = first;
			socket_t* stop = start + c;
			
			for(; start != stop; start++)
			{
				if(*start == s)
					goto found;
			}
			return; //not found
			
			found:
			for(++start; start != stop; start++)
			{
				*(start - 1) = *start;
			}
			
			count = c - 1;
		}
		else version(linux)
		{
			btr(cast(uint*)&first[fdelt(s)], cast(uint)s % nfdbits);
		}
		else version(GNU)
		{
			FD_CLR(s, *cast(fd_set*)buf);
		}
		else
		{
			static assert(0);
		}
	}
	
	
	void remove(Socket s)
	{
		remove(s.sock);
	}
	
	
	int isSet(socket_t s)
	{
		version(Win32)
		{
			socket_t* start = first;
			socket_t* stop = start + count;
			
			for(; start != stop; start++)
			{
				if(*start == s)
					return true;
			}
			return false;
		}
		else version(linux)
		{
			return bt(cast(uint*)&first[fdelt(s)], cast(uint)s % nfdbits);
		}
		else version(GNU)
		{
			return FD_ISSET(s, *cast(fd_set*)buf);
		}
		else
		{
			static assert(0);
		}
	}
	
	
	int isSet(Socket s)
	{
		return isSet(s.sock);
	}
	
	
	uint max() //max sockets that can be added, like FD_SETSIZE
	{
	    version(GNU)
	    {
		return FD_SETSIZE;
	    }
	    else
	    {
		return nbytes / socket_t.sizeof;
	    }
	}
	
	
	fd_set* toFd_set()
	{
		return cast(fd_set*)buf;
	}
}


version (GNU) {
    enum SocketOptionLevel: int
    {
	SOCKET = SOL_SOCKET,
	IP =      0,
	TCP =     6,
	UDP =     17,
    }
}
else {
    enum SocketOptionLevel: int
    {
	    SOCKET =  0xFFFF, //different source 1
	    IP =      0,
	    TCP =     6,
	    UDP =     17,
    }
}


struct linger
{
	// D interface
	version (GNU) {
	    // typeof(std.c.uni...) doesn't work..
	    alias std.c.unix.linger __unix_linger;
	    typeof(__unix_linger.l_onoff) on;
	    typeof(__unix_linger.l_linger) time;
	} else {    
	    ushort on;
	    ushort time;
	}
	
	// C interface
	deprecated
	{
		alias on l_onoff;
		alias time l_linger;
	}
}


version(Win32)
{
	enum SocketOption: int
	{
		DEBUG =      0x1,
		BROADCAST =  0x20,
		REUSEADDR =  0x4,
		LINGER =     0x80,
		OOBINLINE =  0x100,
		SNDBUF =     0x1001,
		RCVBUF =     0x1002,
		KEEPALIVE =  0x8,
		DONTROUTE =  0x10,
		
		// SocketOptionLevel.TCP:
		TCP_NODELAY = 1,
	}
}
else version(linux)
{
	enum SocketOption: int
	{
		DEBUG =      1,
		BROADCAST =  6,
		REUSEADDR =  2,
		LINGER =     13,
		OOBINLINE =  10,
		SNDBUF =     7,
		RCVBUF =     8,
		ACCEPTCONN = 30,
		DONTROUTE =  5,
		
		// SocketOptionLevel.TCP:
		TCP_NODELAY = 1,
	}
}
else version(GNU)
{
    enum SocketOption: int
    {
	DEBUG = SO_DEBUG,
	ACCEPTCONN = SO_ACCEPTCONN,
	REUSEADDR = SO_REUSEADDR,
	KEEPALIVE = SO_KEEPALIVE,
	DONTROUTE = SO_DONTROUTE,
	BROADCAST = SO_BROADCAST,
	USELOOPBACK = SO_USELOOPBACK,
	LINGER = SO_LINGER,
	OOBINLINE = SO_OOBINLINE,
	/* These are not always present
	BSDCOMPAT = SO_BSDCOMPAT,
	REUSEPORT = SO_REUSEPORT,
	TIMESTAMP = SO_TIMESTAMP,
	*/
	SNDBUF = SO_SNDBUF,
	RCVBUF = SO_RCVBUF,
	SNDLOWAT = SO_SNDLOWAT,
	RCVLOWAT = SO_RCVLOWAT,
	SNDTIMEO = SO_SNDTIMEO,
	RCVTIMEO = SO_RCVTIMEO,
	ERROR = SO_ERROR,
	TYPE = SO_TYPE,

	// SocketOptionLevel.TCP:
	TCP_NODELAY = 1,
    }
}
else
{
	static assert(0);
}


class Socket
{
	private:
	socket_t sock;
	AddressFamily _family;
	
	version(Win32)
		bit _blocking = false;
	
	
	this(socket_t sock)
	{
		this.sock = sock;
	}
	
	
	public:
	this(AddressFamily af, SocketType type, ProtocolType protocol)
	{
		sock = socket(af, type, protocol);
		if(sock == sock.init)
			throw new SocketException("Unable to create socket.");
		_family = af;
	}
	
	
	// A single protocol exists to support this socket type within the
	// protocol family, so the ProtocolType is assumed.
	this(AddressFamily af, SocketType type)
	{
		this(af, type, cast(ProtocolType)0); // Pseudo protocol number.
	}
	
	
	~this()
	{
		close();
	}
	
	
	//get underlying socket handle
	socket_t handle()
	{
		return sock;
	}
	
	
	override char[] toString()
	{
		return "Socket";
	}
	
	
	//getter
	bit blocking()
	{
		version(Win32)
		{
			return _blocking;
		}
		else version(BsdSockets)
		{
			return !(fcntl(handle, F_GETFL, 0) & O_NONBLOCK);
		}
	}
	
	
	//setter
	void blocking(bit byes)
	{
		version(Win32)
		{
			uint num = !byes;
			if(SOCKET_ERROR == ioctlsocket(sock, FIONBIO, &num))
				goto err;
			_blocking = byes;
		}
		else version(BsdSockets)
		{
			int x = fcntl(handle, F_GETFL, 0);
			if(byes)
				x &= ~O_NONBLOCK;
			else
				x |= O_NONBLOCK;
			if(SOCKET_ERROR == fcntl(sock, F_SETFL, x))
				goto err;
		}
		return; //success
		
		err:
		throw new SocketException("Unable to set socket blocking.");
	}
	
	
	AddressFamily addressFamily()
	{
		return _family;
	}
	
	
	bit isAlive()
	{
		int type, typesize = type.sizeof;
		return !getsockopt(sock, SOL_SOCKET, SO_TYPE, cast(void*)type, &typesize);
	}
	
	
	void bind(Address addr)
	{
		if(SOCKET_ERROR == .bind(sock, addr.name(), addr.nameLen()))
			throw new SocketException("Unable to bind socket.");
	}
	
	
	void connect(Address to)
	{
		if(SOCKET_ERROR == .connect(sock, to.name(), to.nameLen()))
		{
			if(!blocking)
			{
				version(Win32)
				{
					if(WSAEWOULDBLOCK == WSAGetLastError())
						return;
				}
				else version(linux)
				{
					if(EINPROGRESS == getErrno())
						return;
				}
				else version(Unix)
				{
					if(EINPROGRESS == getErrno())
						return;
				}
				else
				{
					static assert(0);
				}
			}
			throw new SocketException("Unable to connect socket.");
		}
	}
	
	
	//need to bind() first
	void listen(int backlog)
	{
		if(SOCKET_ERROR == .listen(sock, backlog))
			throw new SocketException("Unable to listen on socket.");
	}
	
	
	Socket accept()
	{
		socket_t newsock = .accept(sock, null, null);
		if(INVALID_SOCKET == newsock)
			throw new SocketAcceptException("Unable to accept socket connection.");
		Socket newSocket = new Socket(newsock);
		version(Win32)
			newSocket._blocking = _blocking; //inherits blocking mode
		newSocket._family = _family; //same family
		return newSocket;
	}
	
	
	void shutdown(SocketShutdown how)
	{
		.shutdown(sock, cast(int)how);
	}
	
	
	//calling shutdown() before this is recommended
	//for connection-oriented sockets
	void close()
	{
		version(Win32)
		{
			.closesocket(sock);
		}
		else version(BsdSockets)
		{
			.close(sock);
		}
		sock = sock.init;
	}
	
	
	private Address newFamilyObject()
	{
		Address result;
		switch(_family)
		{
			case AddressFamily.INET:
				result = new InternetAddress;
				break;
			
			default:
				result = new UnknownAddress;
		}
		return result;
	}
	
	
	Address remoteAddress()
	{
		Address addr = newFamilyObject();
		int nameLen = addr.nameLen();
		if(SOCKET_ERROR == .getpeername(sock, addr.name(), &nameLen))
			throw new SocketException("Unable to obtain remote socket address.");
		assert(addr.addressFamily() == _family);
		return addr;
	}
	
	
	Address localAddress()
	{
		Address addr = newFamilyObject();
		int nameLen = addr.nameLen();
		if(SOCKET_ERROR == .getsockname(sock, addr.name(), &nameLen))
			throw new SocketException("Unable to obtain local socket address.");
		assert(addr.addressFamily() == _family);
		return addr;
	}
	
	
	const int ERROR = SOCKET_ERROR;
	
	
	//returns number of bytes actually sent, or -1 on error
	int send(void[] buf, SocketFlags flags)
	{
		int sent = .send(sock, buf, buf.length, cast(int)flags);
		return sent;
	}
	
	
	int send(void[] buf)
	{
		return send(buf, SocketFlags.NONE);
	}
	
	
	int sendTo(void[] buf, SocketFlags flags, Address to)
	{
		int sent = .sendto(sock, buf, buf.length, cast(int)flags, to.name(), to.nameLen());
		return sent;
	}
	
	
	int sendTo(void[] buf, Address to)
	{
		return sendTo(buf, SocketFlags.NONE, to);
	}
	
	
	//assumes you connect()ed
	int sendTo(void[] buf, SocketFlags flags)
	{
		int sent = .sendto(sock, buf, buf.length, cast(int)flags, null, 0);
		return sent;
	}
	
	
	//assumes you connect()ed
	int sendTo(void[] buf)
	{
		return sendTo(buf, SocketFlags.NONE);
	}
	
	
	//returns number of bytes actually received, 0 on connection closure, or -1 on error
	int receive(void[] buf, SocketFlags flags)
	{
		if(!buf.length) //return 0 and don't think the connection closed
			return 0;
		int read = .recv(sock, buf, buf.length, cast(int)flags);
		// if(!read) //connection closed
		return read;
	}
	
	
	int receive(void[] buf)
	{
		return receive(buf, SocketFlags.NONE);
	}
	
	
	int receiveFrom(void[] buf, SocketFlags flags, out Address from)
	{
		if(!buf.length) //return 0 and don't think the connection closed
			return 0;
		from = newFamilyObject();
		int nameLen = from.nameLen();
		int read = .recvfrom(sock, buf, buf.length, cast(int)flags, from.name(), &nameLen);
		assert(from.addressFamily() == _family);
		// if(!read) //connection closed
		return read;
	}
	
	
	int receiveFrom(void[] buf, out Address from)
	{
		return receiveFrom(buf, SocketFlags.NONE, from);
	}
	
	
	//assumes you connect()ed
	int receiveFrom(void[] buf, SocketFlags flags)
	{
		if(!buf.length) //return 0 and don't think the connection closed
			return 0;
		int read = .recvfrom(sock, buf, buf.length, cast(int)flags, null, null);
		// if(!read) //connection closed
		return read;
	}
	
	
	//assumes you connect()ed
	int receiveFrom(void[] buf)
	{
		return receiveFrom(buf, SocketFlags.NONE);
	}
	
	
	//returns the length, in bytes, of the actual result - very different from getsockopt()
	int getOption(SocketOptionLevel level, SocketOption option, void[] result)
	{
		int len = result.length;
		if(SOCKET_ERROR == .getsockopt(sock, cast(int)level, cast(int)option, result, &len))
			throw new SocketException("Unable to get socket option.");
		return len;
	}
	
	
	// Common case for integer and boolean options.
	int getOption(SocketOptionLevel level, SocketOption option, out int result)
	{
		return getOption(level, option, (&result)[0 .. int.sizeof]);
	}
	
	
	void setOption(SocketOptionLevel level, SocketOption option, void[] value)
	{
		if(SOCKET_ERROR == .setsockopt(sock, cast(int)level, cast(int)option, value, value.length))
			throw new SocketException("Unable to set socket option.");
	}
	
	
	// Common case for integer and boolean options.
	void setOption(SocketOptionLevel level, SocketOption option, int value)
	{
		setOption(level, option, (&value)[0 .. int.sizeof]);
	}
	
	
	//SocketSet's updated to include only those sockets which an event occured
	//returns the number of events, 0 on timeout, or -1 on interruption
	//for a connect()ing socket, writeability means connected
	//for a listen()ing socket, readability means listening
	//Winsock: possibly internally limited to 64 sockets per set
	static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, timeval* tv)
	in
	{
		//make sure none of the SocketSet's are the same object
		if(checkRead)
		{
			assert(checkRead !== checkWrite);
			assert(checkRead !== checkError);
		}
		if(checkWrite)
		{
			assert(checkWrite !== checkError);
		}
	}
	body
	{
		fd_set* fr, fw, fe;
		
		version(Win32)
		{
			//Windows has a problem with empty fd_set's that aren't null
			fr = (checkRead && checkRead.count()) ? checkRead.toFd_set() : null;
			fw = (checkWrite && checkWrite.count()) ? checkWrite.toFd_set() : null;
			fe = (checkError && checkError.count()) ? checkError.toFd_set() : null;
		}
		else
		{
			fr = checkRead ? checkRead.toFd_set() : null;
			fw = checkWrite ? checkWrite.toFd_set() : null;
			fe = checkError ? checkError.toFd_set() : null;
		}
		
		int result = .select(cast(int)(socket_t.max - 1), fr, fw, fe, tv);
		
		version(Win32)
		{
			if(SOCKET_ERROR == result && WSAGetLastError() == WSAEINTR)
				return -1;
		}
		else version(linux)
		{
			if(SOCKET_ERROR == result && getErrno() == EINTR)
				return -1;
		}
		else version(Unix)
		{
			if(SOCKET_ERROR == result && getErrno() == EINTR)
				return -1;
		}
		else
		{
			static assert(0);
		}
		
		if(SOCKET_ERROR == result)
			throw new SocketException("Socket select error.");
		
		return result;
	}
	
	
	static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, int microseconds)
	{
		timeval tv;
		tv.seconds = 0;
		tv.microseconds = microseconds;
		return select(checkRead, checkWrite, checkError, &tv);
	}
	
	
	//maximum timeout
	static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError)
	{
		return select(checkRead, checkWrite, checkError, null);
	}
	
	
	/+
	bit poll(events)
	{
		int WSAEventSelect(socket_t s, WSAEVENT hEventObject, int lNetworkEvents); // Winsock 2 ?
		int poll(pollfd* fds, int nfds, int timeout); // Unix ?
	}
	+/
}


class TcpSocket: Socket
{
	this()
	{
		super(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP);
	}
	
	
	//shortcut
	this(InternetAddress connectTo)
	{
		this();
		connect(connectTo);
	}
}


class UdpSocket: Socket
{
	this()
	{
		super(AddressFamily.INET, SocketType.DGRAM, ProtocolType.UDP);
	}
}

