/*
	Copyright (C) 2004-2005 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.
	
	socket.d 1.3
	Jan 2005
	
	Thanks to Benjamin Herr for his assistance.
*/

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

   Modified by David Friedman, April 2005
*/

module std.socket;

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


version(Unix)
{
	version = BsdSockets;
}
    
version(Win32)
{
	private import std.c.windows.windows, std.c.windows.winsock;
	private alias std.c.windows.winsock.timeval _ctimeval;
	
	typedef SOCKET socket_t = INVALID_SOCKET;
	private const int _SOCKET_ERROR = SOCKET_ERROR;
	
	
	private int _lasterr()
	{
		return WSAGetLastError();
	}
}
else version(BsdSockets)
{
	version (Unix)
	{
		private import std.c.unix.unix;
		private alias std.c.unix.unix.timeval _ctimeval;
	}
	
	typedef int32_t socket_t = -1;
	private const int _SOCKET_ERROR = -1;
	
	
	private int _lasterr()
	{
		return getErrno();
	}
}
else
{
	static assert(0); // No socket support yet.
}


class SocketException: Exception
{
	int errorCode; // Platform-specific error code.
	
	
	this(char[] msg, int err = 0)
	{
		errorCode = err;
		
		version(Unix)
		{
			if(errorCode > 0)
			{
				char* cs;
				size_t len;
				
				cs = strerror(errorCode);
				len = strlen(cs);
				
				if(cs[len - 1] == '\n')
					len--;
				if(cs[len - 1] == '\r')
					len--;
				msg = msg ~ ": " ~ cs[0 .. len];
			}
		}
		
		super(msg);
	}
}


static this()
{
	version(Win32)
	{
		WSADATA wd;
		
		// Winsock will still load if an older version is present.
		// The version is just a request.
		int val;
		val = WSAStartup(0x2020, &wd);
		if(val) // Request Winsock 2.2 for IPv6.
			throw new SocketException("Unable to initialize socket library", val);
	}
}


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


enum AddressFamily: int
{
	UNSPEC =     AF_UNSPEC,
	UNIX =       AF_UNIX,
	INET =       AF_INET,
	IPX =        AF_IPX,
	APPLETALK =  AF_APPLETALK,
	INET6 =      AF_INET6,
}


enum SocketType: int
{
	STREAM =     SOCK_STREAM,
	DGRAM =      SOCK_DGRAM,
	RAW =        SOCK_RAW,
	RDM =        SOCK_RDM,
	SEQPACKET =  SOCK_SEQPACKET,
}


enum ProtocolType: int
{
	IP =    IPPROTO_IP,
	ICMP =  IPPROTO_ICMP,
	IGMP =  IPPROTO_IGMP,
	GGP =   IPPROTO_GGP,
	TCP =   IPPROTO_TCP,
	PUP =   IPPROTO_PUP,
	UDP =   IPPROTO_UDP,
	IDP =   IPPROTO_IDP,
	IPV6 =  IPPROTO_IPV6,
}


class Protocol
{
	ProtocolType type;
	char[] name;
	char[][] aliases;
	
	
	void populate(protoent* proto)
	{
		type = cast(ProtocolType)proto.p_proto;
		name = std.string.toString(proto.p_name).dup;
		
		int i;
		for(i = 0;; i++)
		{
			if(!proto.p_aliases[i])
				break;
		}
		
		if(i)
		{
			aliases = new char[][i];
			for(i = 0; i != aliases.length; i++)
			{
				aliases[i] = std.string.toString(proto.p_aliases[i]).dup;
			}
		}
		else
		{
			aliases = null;
		}
	}
	
	
	bit getProtocolByName(char[] name)
	{
		protoent* proto;
		proto = getprotobyname(toStringz(name));
		if(!proto)
			return false;
		populate(proto);
		return true;
	}
	
	
	// Same as getprotobynumber().
	bit getProtocolByType(ProtocolType type)
	{
		protoent* proto;
		proto = getprotobynumber(type);
		if(!proto)
			return false;
		populate(proto);
		return true;
	}
}


unittest
{
	Protocol proto = new Protocol;
	assert(proto.getProtocolByType(ProtocolType.TCP));
	printf("About protocol TCP:\n\tName: %.*s\n", proto.name);
	foreach(char[] s; proto.aliases)
	{
		printf("\tAlias: %.*s\n", s);
	}
}


class Service
{
	char[] name;
	char[][] aliases;
	ushort port;
	char[] protocolName;
	
	
	void populate(servent* serv)
	{
		name = std.string.toString(serv.s_name).dup;
		port = ntohs(serv.s_port);
		protocolName = std.string.toString(serv.s_proto).dup;
		
		int i;
		for(i = 0;; i++)
		{
			if(!serv.s_aliases[i])
				break;
		}
		
		if(i)
		{
			aliases = new char[][i];
			for(i = 0; i != aliases.length; i++)
			{
				aliases[i] = std.string.toString(serv.s_aliases[i]).dup;
			}
		}
		else
		{
			aliases = null;
		}
	}
	
	
	bit getServiceByName(char[] name, char[] protocolName)
	{
		servent* serv;
		serv = getservbyname(toStringz(name), toStringz(protocolName));
		if(!serv)
			return false;
		populate(serv);
		return true;
	}
	
	
	// Any protocol name will be matched.
	bit getServiceByName(char[] name)
	{
		servent* serv;
		serv = getservbyname(toStringz(name), null);
		if(!serv)
			return false;
		populate(serv);
		return true;
	}
	
	
	bit getServiceByPort(ushort port, char[] protocolName)
	{
		servent* serv;
		serv = getservbyport(port, toStringz(protocolName));
		if(!serv)
			return false;
		populate(serv);
		return true;
	}
	
	
	// Any protocol name will be matched.
	bit getServiceByPort(ushort port)
	{
		servent* serv;
		serv = getservbyport(port, null);
		if(!serv)
			return false;
		populate(serv);
		return true;
	}
}


unittest
{
	Service serv = new Service;
	if(serv.getServiceByName("epmap", "tcp"))
	{
		printf("About service epmap:\n\tService: %.*s\n\tPort: %d\n\tProtocol: %.*s\n",
			serv.name, serv.port, serv.protocolName);
		foreach(char[] s; serv.aliases)
		{
			printf("\tAlias: %.*s\n", s);
		}
	}
	else
	{
		printf("No service for epmap.\n");
	}
}


class HostException: Exception
{
	int errorCode;
	
	
	this(char[] msg, int err = 0)
	{
		errorCode = err;
		super(msg);
	}
}


class InternetHost
{
	char[] name;
	char[][] aliases;
	uint32_t[] addrList;
	
	
	void validHostent(hostent* he)
	{
		if(he.h_addrtype != cast(int)AddressFamily.INET || he.h_length != 4)
			throw new HostException("Address family mismatch", _lasterr());
	}
	
	
	void populate(hostent* he)
	{
		int i;
		char* p;
		
		name = std.string.toString(he.h_name).dup;
		
		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]).dup;
			}
		}
		else
		{
			aliases = null;
		}
		
		for(i = 0;; i++)
		{
			p = he.h_addr_list[i];
			if(!p)
				break;
		}
		
		if(i)
		{
			addrList = new uint32_t[i];
			for(i = 0; i != addrList.length; i++)
			{
				addrList[i] = ntohl(*(cast(uint32_t*)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 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 InternetAddress: Address
{
	protected:
	sockaddr_in sin;


	sockaddr* name()
	{
		return cast(sockaddr*)&sin;
	}
	
	
	int nameLen()
	{
		return sin.sizeof;
	}
	
	
	this()
	{
	}
	
	
	public:
	const uint ADDR_ANY = INADDR_ANY;
	const uint ADDR_NONE = INADDR_NONE;
	const ushort PORT_ANY = 0;
	
	
	AddressFamily addressFamily()
	{
		return cast(AddressFamily)AddressFamily.INET;
	}
	
	
	ushort port()
	{
		return ntohs(sin.sin_port);
	}
	
	
	uint addr()
	{
		return ntohl(sin.sin_addr.s_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");
				throw new AddressException("Unable to resolve host '" ~ addr ~ "'");
			uiaddr = ih.addrList[0];
		}
		sin.sin_addr.s_addr = htonl(uiaddr);
		sin.sin_port = htons(port);
	}
	
	
	this(uint addr, ushort port)
	{
		sin.sin_addr.s_addr = htonl(addr);
		sin.sin_port = htons(port);
	}
	
	
	this(ushort port)
	{
		sin.sin_addr.s_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, int err = 0)
	{
		super(msg, err);
	}
}


enum SocketShutdown: int
{
	RECEIVE =  SD_RECEIVE,
	SEND =     SD_SEND,
	BOTH =     SD_RECEIVE,
}


enum SocketFlags: int
{
	NONE =       0,
	
	OOB =        MSG_OOB, //out of band
	PEEK =       MSG_PEEK, //only for receiving
	DONTROUTE =  MSG_DONTROUTE, //only for sending
}


extern(C) struct timeval
{
	// D interface
	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(BsdSockets)
	{
		int maxfd = -1;
		
		
		socket_t* first()
		{
			return cast(socket_t*)buf;
		}
	}
	
	
	fd_set* _fd_set()
	{
		return cast(fd_set*)buf;
	}
	
	
	public:
	this(uint max)
	{
		version(Win32)
		{
			nbytes = max * socket_t.sizeof;
			buf = new byte[nbytes + uint.sizeof];
			count = 0;
		}
		else version(BsdSockets)
		{
			nbytes = max / NFDBITS * socket_t.sizeof;
			if(max % NFDBITS)
				nbytes += socket_t.sizeof;
			buf = new byte[nbytes]; // new initializes to 0.
		}
	}
	
	
	this()
	{
		this(FD_SETSIZE);
	}
	
	
	void reset()
	{
		version(Win32)
		{
			count = 0;
		}
		else version(BsdSockets)
		{
			maxfd = -1;
			version(GNU)
			{
			    FD_ZERO(_fd_set);
			}
			else
			{
			    buf[0 .. nbytes] = 0;
			}
		}
	}
	
	
	void add(socket_t s)
	in
	{
		// Make sure too many sockets don't get added.
		version(Win32)
		{
			assert(count < max);
		}
		else version(BsdSockets)
		{
			version(GNU)
			{
			    // Tries to account for little and big endian..er needs work
			    // assert((s/NFDBITS+1)*NFDBITS/8 <= nbytes);
			}
			else
			{
			    assert(FDELT(s) < nbytes / socket_t.sizeof);
			}
		}
	}
	body
	{
		FD_SET(s, _fd_set);
		
		version(BsdSockets)
		{
			if(s > maxfd)
				maxfd = s;
		}
	}
	
	
	void add(Socket s)
	{
		add(s.sock);
	}
	
	
	void remove(socket_t s)
	{
		FD_CLR(s, _fd_set);
	}
	
	
	void remove(Socket s)
	{
		remove(s.sock);
	}
	
	
	int isSet(socket_t s)
	{
		return FD_ISSET(s, _fd_set);
	}
	
	
	int isSet(Socket s)
	{
		return isSet(s.sock);
	}
	
	
	// Max sockets that can be added, like FD_SETSIZE.
	uint max()
	{
		version(Win32)
		{
			return nbytes / socket_t.sizeof;
		}
		else version(BsdSockets)
		{
			return nbytes / socket_t.sizeof * NFDBITS;
		}
		else
		{
			static assert(0);
		}
	}
	
	
	fd_set* toFd_set()
	{
		return _fd_set;
	}
	
	
	int selectn()
	{
		version(Win32)
		{
			return 0;
		}
		else version(BsdSockets)
		{
			return maxfd + 1;
		}
	}
}


enum SocketOptionLevel: int
{
	SOCKET =  SOL_SOCKET,
	IP =      ProtocolType.IP,
	ICMP =    ProtocolType.ICMP,
	IGMP =    ProtocolType.IGMP,
	GGP =     ProtocolType.GGP,
	TCP =     ProtocolType.TCP,
	PUP =     ProtocolType.PUP,
	UDP =     ProtocolType.UDP,
	IDP =     ProtocolType.IDP,
	IPV6 =    ProtocolType.IPV6,
}


extern(C) struct linger
{
	version (BsdSockets)
	    version (GNU)
	    {
		private alias std.c.unix.unix.linger __unix_linger;
		static assert(linger.sizeof == __unix_linger.sizeof);
	    }
	// D interface
	version(Win32)
	{
		uint16_t on;
		uint16_t time;
	}
	else version(BsdSockets)
	{
		version (GNU)
		{
		    
		    typeof(__unix_linger.l_onoff) on;
		    typeof(__unix_linger.l_linger) time;
		    
		}
		else
		{
		    int32_t on;
		    int32_t time;
		}
	}
	
	// C interface
	deprecated
	{
		alias on l_onoff;
		alias time l_linger;
	}
}

enum SocketOption: int
{
	DEBUG =                SO_DEBUG,
	BROADCAST =            SO_BROADCAST,
	REUSEADDR =            SO_REUSEADDR,
	LINGER =               SO_LINGER,
	OOBINLINE =            SO_OOBINLINE,
	SNDBUF =               SO_SNDBUF,
	RCVBUF =               SO_RCVBUF,
	DONTROUTE =            SO_DONTROUTE,
	
	// SocketOptionLevel.TCP:
	TCP_NODELAY =          .TCP_NODELAY,
	
	// SocketOptionLevel.IPV6:
	IPV6_UNICAST_HOPS =    .IPV6_UNICAST_HOPS,
	IPV6_MULTICAST_IF =    .IPV6_MULTICAST_IF,
	IPV6_MULTICAST_LOOP =  .IPV6_MULTICAST_LOOP,
	IPV6_JOIN_GROUP =      .IPV6_JOIN_GROUP,
	IPV6_LEAVE_GROUP =     .IPV6_LEAVE_GROUP,
}


class Socket
{
	private:
	socket_t sock;
	AddressFamily _family;
	
	version(Win32)
		bit _blocking = false;
	
	
	// For use with accepting().
	protected this()
	{
	}
	
	
	public:
	this(AddressFamily af, SocketType type, ProtocolType protocol)
	{
		sock = cast(socket_t)socket(af, type, protocol);
		if(sock == socket_t.init)
			throw new SocketException("Unable to create socket", _lasterr());
		_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(AddressFamily af, SocketType type, char[] protocolName)
	{
		protoent* proto;
		proto = getprotobyname(toStringz(protocolName));
		if(!proto)
			throw new SocketException("Unable to find the protocol", _lasterr());
		this(af, type, cast(ProtocolType)proto.p_proto);
	}
	
	
	~this()
	{
		close();
	}
	
	
	// Get underlying socket handle.
	socket_t handle() // getter
	{
		return sock;
	}
	
	
	bit blocking() // getter
	{
		version(Win32)
		{
			return _blocking;
		}
		else version(BsdSockets)
		{
			return !(fcntl(handle, F_GETFL, 0) & O_NONBLOCK);
		}
	}
	
	
	void blocking(bit byes) // setter
	{
		version(Win32)
		{
			uint num = !byes;
			if(_SOCKET_ERROR == ioctlsocket(sock, FIONBIO, &num))
				goto err;
			_blocking = byes;
		}
		else version(BsdSockets)
		{
			int x = fcntl(sock, F_GETFL, 0);
			if(-1 == x)
				goto err;
			if(byes)
				x &= ~O_NONBLOCK;
			else
				x |= O_NONBLOCK;
			if(-1 == fcntl(sock, F_SETFL, x))
				goto err;
		}
		return; // Success.
		
		err:
		throw new SocketException("Unable to set socket blocking", _lasterr());
	}
	
	
	AddressFamily addressFamily() // getter
	{
		return _family;
	}
	
	
	bit isAlive() // getter
	{
		int type, typesize = type.sizeof;
		return !getsockopt(sock, SOL_SOCKET, SO_TYPE, cast(char*)&type, &typesize);
	}
	
	
	void bind(Address addr)
	{
		if(_SOCKET_ERROR == .bind(sock, addr.name(), addr.nameLen()))
			throw new SocketException("Unable to bind socket", _lasterr());
	}
	
	
	void connect(Address to)
	{
		if(_SOCKET_ERROR == .connect(sock, to.name(), to.nameLen()))
		{
			int err;
			err = _lasterr();
			
			if(!blocking)
			{
				version(Win32)
				{
					if(WSAEWOULDBLOCK == err)
						return;
				}
				else version(Unix)
				{
					if(EINPROGRESS == err)
						return;
				}
				else
				{
					static assert(0);
				}
			}
			throw new SocketException("Unable to connect socket", err);
		}
	}
	
	
	//need to bind() first
	void listen(int backlog)
	{
		if(_SOCKET_ERROR == .listen(sock, backlog))
			throw new SocketException("Unable to listen on socket", _lasterr());
	}
	
	
	// Override to use a derived class.
	// The returned socket's handle must not be set.
	protected Socket accepting()
	{
		return new Socket;
	}
	
	
	Socket accept()
	{
		socket_t newsock;
		//newsock = cast(socket_t).accept(sock, null, null); // DMD 0.101 error: found '(' when expecting ';' following 'statement
		alias .accept topaccept;
		newsock = cast(socket_t)topaccept(sock, null, null);
		if(socket_t.init == newsock)
			throw new SocketAcceptException("Unable to accept socket connection", _lasterr());
		
		Socket newSocket;
		try
		{
			newSocket = accepting();
			assert(newSocket.sock == socket_t.init);
			
			newSocket.sock = newsock;
			version(Win32)
				newSocket._blocking = _blocking; //inherits blocking mode
			newSocket._family = _family; //same family
		}
		catch(Object o)
		{
			_close(newsock);
			throw o;
		}
		
		return newSocket;
	}
	
	
	void shutdown(SocketShutdown how)
	{
		.shutdown(sock, cast(int)how);
	}
	
	
	private static void _close(socket_t sock)
	{
		version(Win32)
		{
			.closesocket(sock);
		}
		else version(BsdSockets)
		{
			.close(sock);
		}
	}
	
	
	//calling shutdown() before this is recommended
	//for connection-oriented sockets
	void close()
	{
		_close(sock);
		sock = socket_t.init;
	}
	
	
	private Address newFamilyObject()
	{
		Address result;
		switch(_family)
		{
			case cast(AddressFamily)AddressFamily.INET:
				result = new InternetAddress;
				break;
			
			default:
				result = new UnknownAddress;
		}
		return result;
	}
	
	
	// Returns the local machine's host name. Idea from mango.
	static char[] hostName() // getter
	{
		char[256] result; // Host names are limited to 255 chars.
		if(_SOCKET_ERROR == .gethostname(result, result.length))
			throw new SocketException("Unable to obtain host name", _lasterr());
		return std.string.toString(cast(char*)result).dup;
	}
	
	
	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", _lasterr());
		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", _lasterr());
		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", _lasterr());
		return len;
	}
	
	
	// Common case for integer and boolean options.
	int getOption(SocketOptionLevel level, SocketOption option, out int32_t result)
	{
		return getOption(level, option, (&result)[0 .. 1]);
	}
	
	
	int getOption(SocketOptionLevel level, SocketOption option, out linger result)
	{
		//return getOption(cast(SocketOptionLevel)SocketOptionLevel.SOCKET, SocketOption.LINGER, (&result)[0 .. 1]);
		return getOption(level, option, (&result)[0 .. 1]); 
	}
	
	
	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", _lasterr());
	}
	
	
	// Common case for integer and boolean options.
	void setOption(SocketOptionLevel level, SocketOption option, int32_t value)
	{
		setOption(level, option, (&value)[0 .. 1]);
	}
	
	
	void setOption(SocketOptionLevel level, SocketOption option, linger value)
	{
		//setOption(cast(SocketOptionLevel)SocketOptionLevel.SOCKET, SocketOption.LINGER, (&value)[0 .. 1]);
		setOption(level, option, (&value)[0 .. 1]);
	}
	
	
	//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 !is checkWrite);
			assert(checkRead !is checkError);
		}
		if(checkWrite)
		{
			assert(checkWrite !is checkError);
		}
	}
	body
	{
		fd_set* fr, fw, fe;
		int n = 0;
		
		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
		{
			if(checkRead)
			{
				fr = checkRead.toFd_set();
				n = checkRead.selectn();
			}
			else
			{
				fr = null;
			}
			
			if(checkWrite)
			{
				fw = checkWrite.toFd_set();
				int _n;
				_n = checkWrite.selectn();
				if(_n > n)
					n = _n;
			}
			else
			{
				fw = null;
			}
			
			if(checkError)
			{
				fe = checkError.toFd_set();
				int _n;
				_n = checkError.selectn();
				if(_n > n)
					n = _n;
			}
			else
			{
				fe = null;
			}
		}
		
		int result = .select(n, fr, fw, fe, cast(_ctimeval*)tv);
		
		version(Win32)
		{
			if(_SOCKET_ERROR == result && WSAGetLastError() == WSAEINTR)
				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", _lasterr());
		
		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(AddressFamily family)
	{
		super(family, SocketType.STREAM, ProtocolType.TCP);
	}
	
	
	this()
	{
		this(cast(AddressFamily)AddressFamily.INET);
	}
	
	
	//shortcut
	this(Address connectTo)
	{
		this(connectTo.addressFamily());
		connect(connectTo);
	}
}


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

