// Copyright (C) 1999-2005 Open Source Telecom Corporation.
// Copyright (C) 2006-2009 David Sugar, Tycho Softworks,
// Copyright (C) 2009 Leandro Melo de Sales <leandroal@gmail.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
// This exception applies only to the code released under the name GNU
// Common C++.  If you copy code from other releases into a copy of GNU
// Common C++, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU Common C++, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//

/**
 * @file socket.h
 * @short Network addresses and sockets related classes.
 **/

#ifndef	CCXX_SOCKET_H_
#define	CCXX_SOCKET_H_

#ifndef	CCXX_ADDRESS_H_
#include <cc++/address.h>
#endif

#if defined(WIN32) && !defined(__CYGWIN32__)
#include <io.h>
#define	_IOLEN64	(unsigned)
#define	_IORET64	(int)
#define TIMEOUT_INF ~((timeout_t) 0)
typedef int socklen_t;
#else
#define INVALID_SOCKET	-1
typedef int SOCKET;
#endif

#ifndef	_IOLEN64
#define	_IOLEN64
#endif

#ifndef	_IORET64
#define	_IORET64
#endif

#ifndef	MSG_DONTWAIT
#define	MSG_DONTWAIT	0
#endif

#ifndef	MSG_NOSIGNAL
#define	MSG_NOSIGNAL	0
#endif

#ifndef SOCK_DCCP
#define SOCK_DCCP       6
#endif
#ifndef IPPROTO_DCCP
#define IPPROTO_DCCP    33
#endif
#ifndef SOL_DCCP
#define SOL_DCCP       269
#endif
#define DCCP_SOCKOPT_AVAILABLE_CCIDS    12
#define DCCP_SOCKOPT_CCID               13
#define DCCP_SOCKOPT_TX_CCID            14
#define DCCP_SOCKOPT_RX_CCID            15

#ifdef	CCXX_NAMESPACES
namespace ost {
#endif

/**
 * Transport Protocol Ports.
 */
typedef unsigned short tpport_t;

/**
 * The Socket is used as the base for all Internet protocol services
 * under Common C++.  A socket is a system resource (or winsock descriptor)
 * that occupies a specific port address (and may be bound to a specific
 * network interface) on the local machine.  The socket may also be
 * directly connected to a specific socket on a remote internet host.
 *
 * This base class is not directly used, but is
 * provided to offer properties common to other Common C++ socket classes,
 * including the socket exception model and the ability to set socket
 * properties such as QoS, "sockopts" properties like Dont-Route
 * and Keep-Alive, etc.
 *
 *
 * @author David Sugar <dyfet@ostel.com>
 * @short base class of all sockets.
 */
class __EXPORT Socket
{
public:
	enum Family {
#ifdef	CCXX_IPV6
		IPV6 = AF_INET6,
#endif
		IPV4 = AF_INET
	};

	typedef enum Family Family;

	enum Error {
		errSuccess = 0,
		errCreateFailed,
		errCopyFailed,
		errInput,
		errInputInterrupt,
		errResourceFailure,
		errOutput,
		errOutputInterrupt,
		errNotConnected,
		errConnectRefused,
		errConnectRejected,
		errConnectTimeout,
		errConnectFailed,
		errConnectInvalid,
		errConnectBusy,
		errConnectNoRoute,
		errBindingFailed,
		errBroadcastDenied,
		errRoutingDenied,
		errKeepaliveDenied,
		errServiceDenied,
		errServiceUnavailable,
		errMulticastDisabled,
		errTimeout,
		errNoDelay,
		errExtended,
		errLookupFail,
		errSearchErr,
		errInvalidValue
	};

	typedef enum Error Error;

	enum Tos {
		tosLowDelay = 0,
		tosThroughput,
		tosReliability,
		tosMinCost,
		tosInvalid
	};
	typedef enum Tos Tos;

	enum Pending {
		pendingInput,
		pendingOutput,
		pendingError
	};
	typedef enum Pending Pending;

protected:
	enum State {
		INITIAL,
		AVAILABLE,
		BOUND,
		CONNECTED,
		CONNECTING,
		STREAM
	};
	typedef enum State State;

private:
	// used by exception handlers....
	mutable Error errid;
	mutable const char *errstr;
	mutable long syserr;

	void setSocket(void);
	friend SOCKET dupSocket(SOCKET s,Socket::State state);

protected:
	static Mutex mutex;

	mutable struct {
		bool thrown: 1;
		bool broadcast: 1;
		bool route: 1;
		bool keepalive: 1;
		bool loopback: 1;
		bool multicast: 1;
		bool completion: 1;
		bool linger: 1;
		unsigned ttl: 8;
	} flags;

	/**
	 * the actual socket descriptor, in Windows, unlike posix it
	 * *cannot* be used as an file descriptor
	 * that way madness lies -- jfc
	 */
	SOCKET volatile so;
	State volatile state;

	/**
	 * This service is used to throw all socket errors which usually
	 * occur during the socket constructor.
	 *
	 * @param error defined socket error id.
	 * @param err string or message to pass.
	 * @param systemError the system error# that caused the error
	 */
	Error error(Error error, const char *err = NULL, long systemError = 0) const;

	/**
	 * This service is used to throw application defined socket errors
	 * where the application specific error code is a string.
	 *
	 * @param err string or message to pass.
	 */
	inline void error(const char *err) const
		{error(errExtended, err);};

	/**
	 * This service is used to turn the error handler on or off for
	 * "throwing" exceptions by manipulating the thrown flag.
	 *
	 * @param enable true to enable handler.
	 */
	inline void setError(bool enable)
		{flags.thrown = !enable;};

	/**
	 * Used as the default destructor for ending a socket.  This
	 * will cleanly terminate the socket connection.  It is provided
	 * for use in derived virtual destructors.
	 */
	void endSocket(void);

	/**
	 * Used as a common handler for connection failure processing.
	 *
	 * @return correct failure code to apply.
	 */
	Error connectError(void);

	/**
	 * Set the send limit.
	 */
	Error sendLimit(int limit = 2048);

	/**
	 * Set thr receive limit.
	 */
	Error receiveLimit(int limit = 1);

	/**
	 * Set the send timeout for sending raw network data.
	 *
	 * @return errSuccess if set.
	 * @param timer value in millisec.
	 */
	Error sendTimeout(timeout_t timer);

	/**
	 * Receive timeout for receiving raw network data.
	 *
	 * @return errSuccess if set.
	 * @param timer value in milliseconds.
	 */
	Error receiveTimeout(timeout_t timer);

	/**
	 * Set the protocol stack network kernel send buffer size
	 * associated with the socket.
	 *
	 * @return errSuccess on success, or error.
	 * @param size of buffer in bytes.
	 */
	Error sendBuffer(unsigned size);

	/**
	 * Set the protocol stack network kernel receive buffer size
	 * associated with the socket.
	 *
	 * @return errSuccess on success, or error.
	 * @param size of buffer in bytes.
	 */
	Error receiveBuffer(unsigned size);

	/**
	 * Set the total protocol stack network kernel buffer size
	 * for both send and receive together.
	 *
	 * @return errSuccess on success
	 * @param size of buffer.
	 */
	Error bufferSize(unsigned size);

	/**
	 * Set the subnet broadcast flag for the socket.  This enables
	 * sending to a subnet and may require special image privileges
	 * depending on the operating system.
	 *
	 * @return 0 (errSuccess) on success, else error code.
	 * @param enable when set to true.
	 */
	Error setBroadcast(bool enable);

	/**
	 * Setting multicast binds the multicast interface used for
	 * the socket to the interface the socket itself has been
	 * implicitly bound to.  It is also used as a check flag
	 * to make sure multicast is enabled before multicast
	 * operations are used.
	 *
	 * @return 0 (errSuccess) on success, else error code.
	 * @param enable when set to true.
	 * @param family of protocol.
	 */
	Error setMulticastByFamily(bool enable, Family family = IPV4);

	/**
	 * Set the multicast loopback flag for the socket.  Loopback
	 * enables a socket to hear what it is sending.
	 *
	 * @return 0 (errSuccess) on success, else error code.
	 * @param enable when set to true.
	 * @param family of protocol.
	 */
	Error setLoopbackByFamily(bool enable, Family family = IPV4);

	/**
	 * Set the multicast time to live for a multicast socket.
	 *
	 * @return 0 (errSuccess) on success, else error code.
	 * @param ttl time to live.
	 * @param fam family of protocol.
	 */
	Error setTimeToLiveByFamily(unsigned char ttl, Family fam = IPV4);

	/**
	 * Join a multicast group.
	 *
	 * @return 0 (errSuccess) on success, else error code.
	 * @param ia address of multicast group to join.
	 */
	Error join(const IPV4Multicast &ia);
#ifdef	CCXX_IPV6
	Error join(const IPV6Multicast &ia);
#endif

	/**
	 * Drop membership from a multicast group.
	 *
	 * @return 0 (errSuccess) on success, else error code.
	 * @param ia address of multicast group to drop.
	 */
	Error drop(const IPV4Multicast &ia);
#ifdef	CCXX_IPV6
	Error drop(const IPV6Multicast &ia);
#endif

	/**
	 * Set the socket routing to indicate if outgoing messages
	 * should bypass normal routing (set false).
	 *
	 * @return 0 on success.
	 * @param enable normal routing when set to true.
	 */
	Error setRouting(bool enable);


	/**
	 * Enable/disable delaying packets (Nagle algorithm)
	 *
	 * @return 0 on success.
	 * @param enable disable Nagle algorithm when set to true.
	 */
	Error setNoDelay(bool enable);

	/**
	 * An unconnected socket may be created directly on the local
	 * machine.  Sockets can occupy both the internet domain (AF_INET)
	 * and UNIX socket domain (AF_UNIX) under unix.  The socket type
	 * (SOCK_STREAM, SOCK_DGRAM) and protocol may also be specified.
	 * If the socket cannot be created, an exception is thrown.
	 *
	 * @param domain socket domain to use.
	 * @param type base type and protocol family of the socket.
	 * @param protocol specific protocol to apply.
	 */
	Socket(int domain, int type, int protocol = 0);

	/**
	 * A socket object may be created from a file descriptor when that
	 * descriptor was created either through a socket() or accept()
	 * call.  This constructor is mostly for internal use.
	 *
	 * @param fd file descriptor of an already existing socket.
	 */
	Socket(SOCKET fd);

	/**
	 * Create an inactive socket object for base constructors.
	 */
	Socket();

	/**
	 * A socket can also be constructed from an already existing
	 * Socket object. On POSIX systems, the socket file descriptor
	 * is dup()'d. On Win32, DuplicateHandle() is used.
	 *
	 * @param source of existing socket to clone.
	 */
	Socket(const Socket &source);

	/**
	 * Process a logical input line from a socket descriptor
	 * directly.
	 *
	 * @param buf pointer to string.
	 * @param len maximum length to read.
	 * @param timeout for pending data in milliseconds.
	 * @return number of bytes actually read.
	 */
	ssize_t readLine(char *buf, size_t len, timeout_t timeout = 0);

	/**
	 * Read in a block of len bytes with specific separator.  Can
	 * be zero, or any other char.  If \\n or \\r, it's treated just
	 * like a readLine().  Otherwise it looks for the separator.
	 *
	 * @param buf pointer to byte allocation.
	 * @param len maximum length to read.
	 * @param separator separator for a particular ASCII character
	 * @param t timeout for pending data in milliseconds.
	 * @return number of bytes actually read.
	 */
	virtual ssize_t readData(void * buf,size_t len,char separator=0,timeout_t t=0);

	/**
	 * Write a block of len bytes to socket.
	 *
	 * @param buf pointer to byte allocation.
	 * @param len maximum length to write.
	 * @param t timeout for pending data in milliseconds.
	 * @return number of bytes actually written.
	 */
	virtual ssize_t writeData(const void* buf,size_t len,timeout_t t=0);

public:
	/**
	 * The socket base class may be "thrown" as a result of an
	 * error, and the "catcher" may then choose to destroy the
	 * object.  By assuring the socket base class is a virtual
	 * destructor, we can assure the full object is properly
	 * terminated.
	 */
	virtual ~Socket();

	/**
	 * See if a specific protocol family is available in the
	 * current runtime environment.
	 *
	 * @return true if family available.
	 */
	static bool check(Family fam);

	/**
	 * Sockets may also be duplicated by the assignment operator.
	 */
	Socket &operator=(const Socket &from);

	/**
	 * May be used to examine the origin of data waiting in the
	 * socket receive queue.  This can tell a TCP server where pending
	 * "connect" requests are coming from, or a UDP socket where it's
	 * next packet arrived from.
	 *
	 * @param port ptr to port number of sender.
	 * @return host address, test with "isInetAddress()".
	 */
	IPV4Host getIPV4Sender(tpport_t *port = NULL) const;

	inline IPV4Host getSender(tpport_t *port = NULL) const
		{return getIPV4Sender(port);}

#ifdef	CCXX_IPV6
	IPV6Host getIPV6Sender(tpport_t *port = NULL) const;
#endif

	/**
	 * Get the host address and port of the socket this socket
	 * is connected to.  If the socket is currently not in a
	 * connected state, then a host address of 0.0.0.0 is
	 * returned.
	 *
	 * @param port ptr to port number of remote socket.
	 * @return host address of remote socket.
	 */
	IPV4Host getIPV4Peer(tpport_t *port = NULL) const;

	inline IPV4Host getPeer(tpport_t *port = NULL) const
		{return getIPV4Peer(port);}

#ifdef	CCXX_IPV6
	IPV6Host getIPV6Peer(tpport_t *port = NULL) const;
#endif

	/**
	 * Get the local address and port number this socket is
	 * currently bound to.
	 *
	 * @param port ptr to port number on local host.
	 * @return host address of interface this socket is bound to.
	 */
	IPV4Host getIPV4Local(tpport_t *port = NULL) const;

	inline IPV4Host getLocal(tpport_t *port = NULL) const
		{return getIPV4Local(port);}

#ifdef	CCXX_IPV6
	IPV6Host getIPV6Local(tpport_t *port = NULL) const;
#endif

	/**
	 * Perform NAT table lookup for this socket.
	 * Used to allow an application to know the original ip:port
	 * pair the the client "thinks" it is connecting to. Used mostly
	 * to transparently impersonate a remote server/service.
	 *
	 * On error, 0.0.0.0:0 is returned and one of the following error codes
	 * is set: errServiceUnavailable - if nat is not supported on the
	 * current platform or if it was not compiled; errLookupFail - if the
	 * nat syscall failed for some reason (extended error code);
	 * errSearchErr - if the socket does not have nat information (i.e.
	 * is not nated).
	 *
	 * NAT lookup is supported on NetFilter for ipv4 and ipv6 (Linux),
	 * IPFilter for ipv4 (Solaris, *BSD except OpenBSD, HP-UX, etc.) and
	 * Packet Filter for ipv4 and ipv6 (OpenBSD).
	 * When using IPFilter or Packet Filter, the first NAT lookup must be
	 * performed as root (the NAT device is read only for root and is opened
	 * once, unless an error occurs). Permissions on the nat device may be
	 * changed to solve this.
	 *
	 * \warning When using IPFilter and Packet Filter, application data model
	 * must be the same as the running kernel (32/64 bits).
	 *
	 * @param port ptr to NATed port number on local host.
	 * @return NATed host address that this socket is related to.
	 */
	IPV4Host getIPV4NAT(tpport_t *port = NULL) const;

	inline IPV4Host getNAT(tpport_t *port) const
		{return getIPV4NAT(port);}

#ifdef	CCXX_IPV6
	IPV6Host getIPV6NAT(tpport_t *port = NULL) const;
#endif

	/**
	 * Used to specify blocking mode for the socket.  A socket
	 * can be made non-blocking by setting setCompletion(false)
	 * or set to block on all access with setCompletion(true).
	 * I do not believe this form of non-blocking socket I/O is supported
	 * in winsock, though it provides an alternate asynchronous set of
	 * socket services.
	 *
	 * @param immediate mode specify socket I/O call blocking mode.
	 */
	void setCompletion(bool immediate);

	/**
	 * Enable lingering sockets on close.
	 *
	 * @param linger specify linger enable.
	 */
	Error setLinger(bool linger);

	/**
	 * Set the keep-alive status of this socket and if keep-alive
	 * messages will be sent.
	 *
	 * @return 0 on success.
	 * @param enable keep alive messages.
	 */
	Error setKeepAlive(bool enable);

	/**
	 * Set packet scheduling on platforms which support ip quality
	 * of service conventions.  This effects how packets in the
	 * queue are scheduled through the interface.
	 *
	 * @return 0 on success, error code on failure.
	 * @param service type of service enumerated type.
	 */
	Error setTypeOfService(Tos service);

	/**
	 * Can test to see if this socket is "connected", and hence
	 * whether a "catch" can safely call getPeer().  Of course,
	 * an unconnected socket will return a 0.0.0.0 address from
	 * getPeer() as well.
	 *
	 * @return true when socket is connected to a peer.
	 */
	bool isConnected(void) const;

	/**
	 * Test to see if the socket is at least operating or if it
	 * is mearly initialized.  "initialized" sockets may be the
	 * result of failed constructors.
	 *
	 * @return true if not in initial state.
	 */
	bool isActive(void) const;

	/**
	 * Operator based testing to see if a socket is currently
	 * active.
	 */
	bool operator!() const;

	/**
	 * Return if broadcast has been enabled for the specified
	 * socket.
	 *
	 * @return true if broadcast socket.
	 */
	inline bool isBroadcast(void) const
		{return flags.broadcast;};

	/**
	 * Return if socket routing is enabled.
	 *
	 * @return true if routing enabled.
	 */
	inline bool isRouted(void) const
		{return flags.route;};

	/**
 	 * Often used by a "catch" to fetch the last error of a thrown
	 * socket.
	 *
	 * @return error number of Error error.
 	 */
	inline Error getErrorNumber(void) const {return errid;}

	/**
 	 * Often used by a "catch" to fetch the user set error string
	 * of a thrown socket, but only if EXTENDED error codes are used.
	 *
	 * @return string for error message.
 	 */
	inline const char *getErrorString(void) const {return errstr;}

	inline long getSystemError(void) const {return syserr;}

	const char *getSystemErrorString(void) const;

	/**
	 * Get the status of pending operations.  This can be used to
	 * examine if input or output is waiting, or if an error has
	 * occured on the descriptor.
	 *
	 * @return true if ready, false on timeout.
	 * @param pend ready check to perform.
	 * @param timeout in milliseconds, inf. if not specified.
	 */
	virtual bool isPending(Pending pend, timeout_t timeout = TIMEOUT_INF);
};

/**
 * DCCP sockets are used for stream based connected sessions between two
 * sockets.  Both error recovery and flow control operate transparently
 * for a DCCP socket connection.  The DCCP socket base class is used both
 * for client connections and to bind a DCCP "server" for accepting DCCP 
 * streams.
 *
 * An implicit and unique DCCPSocket object exists in Common C++ to represent
 * a bound DCCP socket acting as a "server" for receiving connection requests.
 * This class is not part of DCCPStream because such objects normally perform
 * no physical I/O (read or write operations) other than to specify a listen
 * backlog queue and perform "accept" operations for pending connections.
 * The Common C++ DCCPSocket offers a Peek method to examine where the next
 * pending connection is coming from, and a Reject method to flush the next
 * request from the queue without having to create a session.
 *
 * The DCCPSocket also supports a "OnAccept" method which can be called when a
 * DCCPStream related object is created from a DCCPSocket.  By creating a
 * DCCPStream from a DCCPSocket, an accept operation automatically occurs, and
 * the DCCPSocket can then still reject the client connection through the
 * return status of it's OnAccept method.
 *
 * @author Leandro Sales <leandroal@gmail.com>
 * @author Heverton Stuart <hevertonsns@gmail.com>
 * @short bound server for DCCP streams and sessions.
 */
class __EXPORT DCCPSocket : public Socket
{
	union {
		struct sockaddr_in ipv4;
#ifdef	CCXX_IPV6
		struct sockaddr_in6 ipv6;
#endif
	}	peer;

	Family family;
	
public:
	/**
	 * A method to call in a derived DCCPSocket class that is acting
	 * as a server when a connection request is being accepted.  The
	 * server can implement protocol specific rules to exclude the
	 * remote socket from being accepted by returning false.  The
	 * Peek method can also be used for this purpose.
	 *
	 * @return true if client should be accepted.
	 * @param ia internet host address of the client.
	 * @param port number of the client.
	 */
	virtual bool onAccept(const IPV4Host &ia, tpport_t port);
#ifdef	CCXX_IPV6
	virtual bool onAccept(const IPV6Host &ia, tpport_t port);
#endif

	/**
	 * A DCCP "server" is created as a DCCP socket that is bound
	 * to a hardware address and port number on the local machine
	 * and that has a backlog queue to listen for remote connection
	 * requests.  If the server cannot be created, an exception is
	 * thrown.
	 *
	 * @param bind local ip address or interface to use.
	 * @param port number to bind socket under.
	 * @param backlog size of connection request queue.
	 */
	DCCPSocket(const IPV4Address &bind, tpport_t port, unsigned backlog = 5);
#ifdef	CCXX_IPV6
	DCCPSocket(const IPV6Address &bind, tpport_t port, unsigned backlog = 5);
#endif

	/**
	 * Create a named dccp socket by service and/or interface id.
	 * For IPV4 we use [host:]svc or [host/]svc for the string.
	 * If we have getaddrinfo, we use that to obtain the addr to
	 * bind for.
	 *
	 * @param name of host interface and service port to bind.
	 * @param backlog size of connection request queue.
	 */
	DCCPSocket(const char *name, Family family = IPV4, unsigned backlog = 5);

	/**
	 * Create an unconnected ephemeral DCCP client socket.
	 */
	DCCPSocket(Family family = IPV4);

	/**
	 * Create a server session by accepting a DCCP Socket.
	 */
	DCCPSocket(DCCPSocket& server, timeout_t timeout = 0);

	/**
	 * Used to reject the next incoming connection request.
     */
	void reject(void);

	/**
	 * Disconnect active dccp connection (client use).
	 */
	void disconnect(void);

	/**
	 * Set CCID DCCP.
	 */
	bool setCCID(uint8 ccid);

    /**
     * Get TX CCID DCCP.
     */
    int getTxCCID();

    /**
     * Get RX CCID DCCP.
     */
    int getRxCCID();

	/**
	 * Create a DCCP client connection to a DCCP socket (on
	 * a remote machine).
	 *
	 * @param host address of remote DCCP server.
	 * @param port number to connect.
	 */
	void connect(const IPV4Host &host, tpport_t port, timeout_t timeout = 0);
#ifdef	CCXX_IPV6
	void connect(const IPV6Host &host, tpport_t port, timeout_t timeout = 0);
#endif

	/**
	 * Connect to a named client.
	 */
	void connect(const char *name);

	/**
	 * Used to wait for pending connection requests.
	 * @return true if data packets available.
	 * @param timeout in milliseconds. TIMEOUT_INF if not specified.
	 */
	inline bool isPendingConnection(timeout_t timeout = TIMEOUT_INF) /* not const -- jfc */
		{return Socket::isPending(Socket::pendingInput, timeout);}

	/**
	 * Use base socket handler for ending this socket.
	 */
	virtual ~DCCPSocket();
};

/**
 * UDP sockets implement the TCP SOCK_DGRAM UDP protocol.  They can be
 * used to pass unverified messages between hosts, or to broadcast a
 * specific message to an entire subnet.  Please note that Streaming of
 * realtime data commonly use UDPDuplex related classes rather than
 * UDPSocket.
 *
 * In addition to connected TCP sessions, Common C++ supports UDP sockets and
 * these also cover a range of functionality.  Like a TCPSocket, A UDPSocket
 * can be created bound to a specific network interface and/or port address,
 * though this is not required.  UDP sockets also are usually either
 * connected or otherwise "associated" with a specific "peer" UDP socket.
 * Since UDP sockets operate through discreet packets, there are no streaming
 * operators used with UDP sockets.
 *
 * In addition to the UDP "socket" class, there is a "UDPBroadcast" class.
 * The UDPBroadcast is a socket that is set to send messages to a subnet as a
 * whole rather than to an individual peer socket that it may be associated
 * with.
 *
 * UDP sockets are often used for building "realtime" media  streaming
 * protocols and full duplex messaging services.  When used in this manner,
 * typically a pair of UDP sockets are used together; one socket is used to
 * send and the other to receive data with an associated pair of UDP sockets
 * on a "peer" host.  This concept is represented through the Common C++
 * UDPDuplex object, which is a pair of sockets that communicate with another
 * UDPDuplex pair.
 *
 *
 * @author David Sugar <dyfet@ostel.com>
 * @short Unreliable Datagram Protocol sockets.
 */
class __EXPORT UDPSocket : public Socket
{
private:
	inline Error setKeepAlive(bool enable)
		{return Socket::setKeepAlive(enable);};

protected:
#ifdef	CCXX_IPV6
	union {
		struct sockaddr_in6 ipv6;
		struct sockaddr_in ipv4;
	}	peer;
#else
	union {
		struct sockaddr_in ipv4;
	}	peer;
#endif

	Family family;

public:
	/**
	 * Create an unbound UDP socket, mostly for internal use.
	 */
	UDPSocket(Family family = IPV4);

	/**
	 * Create a UDP socket bound by a service name.
	 */
	UDPSocket(const char *name, Family family = IPV4);

	/**
	 * Create a UDP socket and bind it to a specific interface
	 * and port address so that other UDP sockets on remote
	 * machines (or the same host) may find and send UDP messages
	 * to it.  On failure to bind, an exception is thrown.
	 *
	 * @param bind address to bind this socket to.
	 * @param port number to bind this socket to.
	 */
	UDPSocket(const IPV4Address &bind, tpport_t port);
#ifdef	CCXX_IPV6
	UDPSocket(const IPV6Address &bind, tpport_t port);
#endif

	/**
	 * Destroy a UDP socket as a socket.
	 */
	virtual ~UDPSocket();

	/**
	 * Set the loopback.
	 */
	inline Error setLoopback(bool enable)
		{return Socket::setLoopbackByFamily(enable, family);}

	/**
	 * Set the multicast.
	 */
	inline Error setMulticast(bool enable)
		{return Socket::setMulticastByFamily(enable, family);}

	/**
	 * Set time to live.
	 */
	inline Error setTimeToLive(char ttl)
		{return Socket::setTimeToLiveByFamily(ttl, family);}

	/**
	 * set the peer address to send message packets to.  This can be
	 * set before every send() call if nessisary.
	 *
	 * @param host address to send packets to.
	 * @param port number to deliver packets to.
	 */
	void setPeer(const IPV4Host &host, tpport_t port);
	void connect(const IPV4Host &host, tpport_t port);
#ifdef	CCXX_IPV6
	void setPeer(const IPV6Host &host, tpport_t port);
	void connect(const IPV6Host &host, tpport_t port);
#endif

	/**
	 * get the interface index for a named network device
	 *
	 * @param ethX is device name, like "eth0" or "eth1"
	 * @param InterfaceIndex is the index value returned by os
	 * @todo Win32 and ipv6 specific implementation.
	 */
	Socket::Error getInterfaceIndex(const char *ethX,int& InterfaceIndex);

	/**
	 * join a multicast group on a particular interface
	 *
	 * @param ia is the multicast address to use
	 * @param InterfaceIndex is the index value returned by
	 * getInterfaceIndex
	 * @todo Win32 and ipv6 specific implementation.
	 */
	Socket::Error join(const IPV4Multicast &ia,int InterfaceIndex);


	/**
	 * Send a message packet to a peer host.
	 *
	 * @param buf pointer to packet buffer to send.
	 * @param len of packet buffer to send.
	 * @return number of bytes sent.
	 */
	ssize_t send(const void *buf, size_t len);

	/**
	 * Receive a message from any host.
	 *
	 * @param buf pointer to packet buffer to receive.
	 * @param len of packet buffer to receive.
	 * @param reply save sender address for reply if true.
	 * @return number of bytes received.
	 */
	ssize_t receive(void *buf, size_t len, bool reply = false);

	/**
	 * Examine address of sender of next waiting packet.  This also
	 * sets "peer" address to the sender so that the next "send"
	 * message acts as a "reply".  This additional behavior overides
	 * the standard socket getSender behavior.
	 *
	 * @param port pointer to hold port number.
	 */
	IPV4Host getIPV4Peer(tpport_t *port = NULL) const;
	inline IPV4Host getPeer(tpport_t *port = NULL) const
		{return getIPV4Peer(port);}

#ifdef	CCXX_IPV6
	IPV6Host getIPV6Peer(tpport_t *port = NULL) const;
#endif

	/**
	 * Examine contents of next waiting packet.
	 *
	 * @param buf pointer to packet buffer for contents.
	 * @param len of packet buffer.
	 * @return number of bytes examined.
	 */
	inline ssize_t peek(void *buf, size_t len)
		{return _IORET64 ::recv(so, (char *)buf, _IOLEN64 len, MSG_PEEK);};

	/**
	 * Associate socket with a named connection
	 */
	void setPeer(const char *service);
	void connect(const char *service);

	/**
	 * Disassociate this socket from any host connection.  No data
	 * should be read or written until a connection is established.
	 */
	Error disconnect(void);
};


/**
 * Representing a UDP socket used for subnet broadcasts, this class
 * provides an alternate binding and setPeer() capability for UDP
 * sockets.
 *
 * @author David Sugar <dyfet@ostel.com>
 * @short Unreliable Datagram for subnet broadcasts.
 */
class __EXPORT UDPBroadcast : public UDPSocket
{
private:
	void setPeer(const IPV4Host &ia, tpport_t port);

	Error setBroadcast(bool enable)
		{return Socket::setBroadcast(enable);};

public:
	/**
	 * Create and bind a subnet broadcast socket.
	 *
	 * @param ia address to bind socket under locally.
	 * @param port to bind socket under locally.
	 */
	UDPBroadcast(const IPV4Address &ia, tpport_t port);

	/**
	 * Set peer by subnet rather than specific host.
	 *
	 * @param subnet of peer hosts to send to.
	 * @param port number to use.
	 */
	void setPeer(const IPV4Broadcast &subnet, tpport_t port);
};

/**
 * Representing half of a two-way UDP connection, the UDP transmitter
 * can broadcast data to another selected peer host or to an entire
 * subnet.
 *
 * @author David Sugar <dyfet@ostel.com>
 * @short Unreliable Datagram Peer Associations.
 */
class __EXPORT UDPTransmit : protected UDPSocket
{
private:
	/**
	 * Common code for diferent flavours of Connect (host, broadcast,
	 * multicast).
	 *
	 * @param ia network address to associate with
	 * @param port port number to associate with
	 */
	Error cConnect(const IPV4Address &ia, tpport_t port);

protected:
	/**
	 * Create a UDP transmitter.
	 */
	UDPTransmit(Family family = IPV4);

	/**
	 * Create a UDP transmitter, bind it to a specific interface
	 * and port address so that other UDP sockets on remote
	 * machines (or the same host) may find and send UDP messages
	 * to it, and associate it with a given port on a peer host.
	 * On failure to bind, an exception is thrown.  This class is
	 * only used to build the UDP Duplex.
	 *
	 * @param bind address to bind this socket to.
	 * @param port number to bind this socket to.
	 */
	UDPTransmit(const IPV4Address &bind, tpport_t port = 5005);
#ifdef	CCXX_IPV6
	UDPTransmit(const IPV6Address &bind, tpport_t port = 5005);
#endif

	/**
	 * Associate this socket with a specified peer host.  The port
	 * number from the constructor will be used.  All UDP packets
	 * will be sent to and received from the specified host.
	 *
	 * @return 0 on success, -1 on error.
	 * @param host address to connect socket to.
	 * @param port to connect socket to.
	 */
	Error connect(const IPV4Host &host, tpport_t port);
#ifdef	CCXX_IPV6
	Error connect(const IPV6Address &host, tpport_t port);
#endif

	/**
	 * Associate this socket with a subnet of peer hosts for
	 * subnet broadcasting.  The server must be able to assert
	 * broadcast permission for the socket.
	 *
	 * @return 0 on success, -1 on error.
	 * @param subnet subnet address to broadcast into.
	 * @param port transport port to broadcast into.
	 */
	Error connect(const IPV4Broadcast &subnet, tpport_t port);

	/**
	 * Associate this socket with a multicast group.
	 *
	 * @return 0 on success, -1 on error.
	 * @param mgroup address of the multicast group to send to.
	 * @param port port number
	 */
	Error connect(const IPV4Multicast &mgroup, tpport_t port);
#ifdef	CCXX_IPV6
	Error connect(const IPV6Multicast &mgroup, tpport_t port);
#endif

	/**
	 * Transmit "send" to use "connected" send rather than sendto.
	 *
	 * @return number of bytes sent.
	 * @param buf address of buffer to send.
	 * @param len of bytes to send.
	 */
	inline ssize_t send(const void *buf, size_t len)
		{return _IORET64 ::send(so, (const char *)buf, _IOLEN64 len, MSG_NOSIGNAL);}

	/**
	 * Stop transmitter.
	 */
	inline void endTransmitter(void)
		{Socket::endSocket();}

	/*
	 * Get transmitter socket.
	 *
	 * @return transmitter.
	 */
	inline SOCKET getTransmitter(void)
		{return so;};

	inline Error setMulticast(bool enable)
		{return Socket::setMulticastByFamily(enable, family);}

	inline Error setTimeToLive(unsigned char ttl)
		{return Socket::setTimeToLiveByFamily(ttl, family);};

public:
	/**
	 * Transmit "send" to use "connected" send rather than sendto.
	 *
	 * @note Windows does not support MSG_DONTWAIT, so it is defined
	 *	 as 0 on that platform.
	 * @return number of bytes sent.
	 * @param buffer address of buffer to send.
	 * @param len of bytes to send.
	 */
	inline ssize_t transmit(const char *buffer, size_t len)
		{return _IORET64 ::send(so, buffer, _IOLEN64 len, MSG_DONTWAIT|MSG_NOSIGNAL);}

	/**
	 * See if output queue is empty for sending more packets.
	 *
	 * @return true if output available.
	 * @param timeout in milliseconds to wait.
	 */
	inline bool isOutputReady(unsigned long timeout = 0l)
		{return Socket::isPending(Socket::pendingOutput, timeout);};


	inline Error setRouting(bool enable)
		{return Socket::setRouting(enable);};

	inline Error setTypeOfService(Tos tos)
		{return Socket::setTypeOfService(tos);};

	inline Error setBroadcast(bool enable)
		{return Socket::setBroadcast(enable);};
};

/**
 * Representing half of a two-way UDP connection, the UDP receiver
 * can receive data from another peer host or subnet.  This class is
 * used exclusivily to derive the UDPDuplex.
 *
 * @author David Sugar <dyfet@ostel.com>
 * @short Unreliable Datagram Peer Associations.
 */
class __EXPORT UDPReceive : protected UDPSocket
{
protected:
	/**
	 * Create a UDP receiver, bind it to a specific interface
	 * and port address so that other UDP sockets on remote
	 * machines (or the same host) may find and send UDP messages
	 * to it, and associate it with a given port on a peer host.
	 * On failure to bind, an exception is thrown.
	 *
	 * @param bind address to bind this socket to.
	 * @param port number to bind this socket to.
	 */
	UDPReceive(const IPV4Address &bind, tpport_t port);
#ifdef	CCXX_IPV6
	UDPReceive(const IPV6Address &bind, tpport_t port);
#endif

	/**
	 * Associate this socket with a specified peer host.  The port
	 * number from the constructor will be used.  All UDP packets
	 * will be sent received from the specified host.
	 *
	 * @return 0 on success, -1 on error.
	 * @param host host network address to connect socket to.
	 * @param port host transport port to connect socket to.
	 */
	Error connect(const IPV4Host &host, tpport_t port);
#ifdef	CCXX_IPV6
	Error connect(const IPV6Host &host, tpport_t port);
#endif

	/**
	 * Check for pending data.
	 *
	 * @return true if data is waiting.
	 * @param timeout in milliseconds.
	 */
	bool isPendingReceive(timeout_t timeout)
		{return Socket::isPending(Socket::pendingInput, timeout);};

	/**
	 * End receiver.
	 */
	inline void endReceiver(void)
		{Socket::endSocket();}

	inline SOCKET getReceiver(void) const
		{return so;};

	inline Error setRouting(bool enable)
		{return Socket::setRouting(enable);}

	inline Error setMulticast(bool enable)
		{return Socket::setMulticastByFamily(enable, family);}

	inline Error join(const IPV4Multicast &ia)
	        {return Socket::join(ia);}

#ifdef	CCXX_IPV6
	inline Error join(const IPV6Multicast &ia)
		{return Socket::join(ia);}
#endif

	inline Error drop(const IPV4Multicast &ia)
	        {return Socket::drop(ia);}

#ifdef	CCXX_IPV6
	inline Error drop(const IPV6Multicast &ia)
		{return Socket::drop(ia);}
#endif

public:
	/**
	 * Receive a data packet from the connected peer host.
	 *
	 * @return num of bytes actually received.
	 * @param buf address of data receive buffer.
	 * @param len size of data receive buffer.
	 */
	inline ssize_t receive(void *buf, size_t len)
		{return _IORET64 ::recv(so, (char *)buf, _IOLEN64 len, 0);};

	/**
	 * See if input queue has data packets available.
	 *
	 * @return true if data packets available.
	 * @param timeout in milliseconds.
	 */
	inline bool isInputReady(timeout_t timeout = TIMEOUT_INF)
		{return Socket::isPending(Socket::pendingInput, timeout);};
};

/**
 * UDP duplex connections impliment a bi-directional point-to-point UDP
 * session between two peer hosts.  Two UDP sockets are typically used
 * on alternating port addresses to assure that sender and receiver
 * data does not collide or echo back.  A UDP Duplex is commonly used
 * for full duplex real-time streaming of UDP data between hosts.
 *
 * @author David Sugar <dyfet@ostel.com>
 * @short Unreliable Datagram Peer Associations.
 */
class __EXPORT UDPDuplex : public UDPTransmit, public UDPReceive
{
public:
	/**
	 * Create a UDP duplex as a pair of UDP simplex objects
	 * bound to alternating and interconnected port addresses.
	 *
	 * @param bind address to bind this socket to.
	 * @param port number to bind sender.
	 */
	UDPDuplex(const IPV4Address &bind, tpport_t port);
#ifdef	CCXX_IPV6
	UDPDuplex(const IPV6Address &bind, tpport_t port);
#endif

	/**
	 * Associate the duplex with a specified peer host. Both
	 * the sender and receiver will be interconnected with
	 * the remote host.
	 *
	 * @return 0 on success, error code on error.
	 * @param host address to connect socket to.
	 * @param port number to connect socket to.
	 */
	Error connect(const IPV4Host &host, tpport_t port);
#ifdef	CCXX_IPV6
	Error connect(const IPV6Host &host, tpport_t port);
#endif

	/**
	 * Disassociate this duplex from any host connection.  No data
	 * should be read or written until a connection is established.
	 *
	 * @return 0 on success, error code on error.
	 */
	Error disconnect(void);
};


/**
 * TCP sockets are used for stream based connected sessions between two
 * sockets.  Both error recovery and flow control operate transparently
 * for a TCP socket connection.  The TCP socket base class is primary used
 * to bind a TCP "server" for accepting TCP streams.
 *
 * An implicit and unique TCPSocket object exists in Common C++ to represent
 * a bound TCP socket acting as a "server" for receiving connection requests.
 * This class is not part of TCPStream because such objects normally perform
 * no physical I/O (read or write operations) other than to specify a listen
 * backlog queue and perform "accept" operations for pending connections.
 * The Common C++ TCPSocket offers a Peek method to examine where the next
 * pending connection is coming from, and a Reject method to flush the next
 * request from the queue without having to create a session.
 *
 * The TCPSocket also supports a "OnAccept" method which can be called when a
 * TCPStream related object is created from a TCPSocket.  By creating a
 * TCPStream from a TCPSocket, an accept operation automatically occurs, and
 * the TCPSocket can then still reject the client connection through the
 * return status of it's OnAccept method.
 *
 * @author David Sugar <dyfet@tycho.com>
 * @short bound server for TCP streams and sessions.
 */
class __EXPORT TCPSocket : protected Socket
{
protected:
	int segsize;
	void setSegmentSize(unsigned mss);

public:
	/**
	 * A method to call in a derived TCPSocket class that is acting
	 * as a server when a connection request is being accepted.  The
	 * server can implement protocol specific rules to exclude the
	 * remote socket from being accepted by returning false.  The
	 * Peek method can also be used for this purpose.
	 *
	 * @return true if client should be accepted.
	 * @param ia internet host address of the client.
	 * @param port number of the client.
	 */
	virtual bool onAccept(const IPV4Host &ia, tpport_t port);

	/**
	 * Fetch out the socket.
	 */
	inline SOCKET getSocket(void)
		{return so;};

	/**
	 * Get the buffer size for servers.
	 */
	inline int getSegmentSize(void)
		{return segsize;};

	/**
	 * A TCP "server" is created as a TCP socket that is bound
	 * to a hardware address and port number on the local machine
	 * and that has a backlog queue to listen for remote connection
	 * requests.  If the server cannot be created, an exception is
	 * thrown.
	 *
	 * @param bind local ip address or interface to use.
	 * @param port number to bind socket under.
	 * @param backlog size of connection request queue.
	 * @param mss maximum segment size for accepted streams.
	 */
	TCPSocket(const IPV4Address &bind, tpport_t port, unsigned backlog = 5, unsigned mss = 536);

	/**
	 * Create a named tcp socket by service and/or interface id.
	 * For IPV4 we use [host:]svc or [host/]svc for the string.
	 * If we have getaddrinfo, we use that to obtain the addr to
	 * bind for.
	 *
	 * @param name of host interface and service port to bind.
	 * @param backlog size of connection request queue.
	 * @param mss maximum segment size for streaming buffers.
	 */
	TCPSocket(const char *name, unsigned backlog = 5, unsigned mss = 536);

	/**
	 * Return address and port of next connection request.  This
	 * can be used instead of OnAccept() to pre-evaluate connection
	 * requests.
	 *
	 * @return host requesting a connection.
	 * @param port number of requestor.
	 */
	inline IPV4Host getRequest(tpport_t *port = NULL) const
		{return Socket::getIPV4Sender(port);}

	/**
	 * Used to reject the next incoming connection request.
	 */
	void reject(void);

	/**
	 * Used to get local bound address.
	 */
	inline IPV4Host getLocal(tpport_t *port = NULL) const
		{return Socket::getIPV4Local(port);}

	/**
	 * Used to wait for pending connection requests.
	 * @return true if data packets available.
	 * @param timeout in milliseconds. TIMEOUT_INF if not specified.
	 */
	inline bool isPendingConnection(timeout_t timeout = TIMEOUT_INF) /* not const -- jfc */
		{return Socket::isPending(Socket::pendingInput, timeout);}

	/**
	 * Use base socket handler for ending this socket.
	 */
	virtual ~TCPSocket();
};

#ifdef  CCXX_IPV6
/**
 * TCPV6 sockets are used for stream based connected sessions between two
 * ipv6 sockets.  Both error recovery and flow control operate transparently
 * for a TCP socket connection.  The TCP socket base class is primary used
 * to bind a TCP "server" for accepting TCP streams.
 *
 * An implicit and unique TCPV6Socket object exists in Common C++ to represent
 * a bound ipv6 TCP socket acting as a "server" for receiving connection requests.
 * This class is not part of TCPStream because such objects normally perform
 * no physical I/O (read or write operations) other than to specify a listen
 * backlog queue and perform "accept" operations for pending connections.
 * The Common C++ TCPV6Socket offers a Peek method to examine where the next
 * pending connection is coming from, and a Reject method to flush the next
 * request from the queue without having to create a session.
 *
 * The TCPV6Socket also supports a "OnAccept" method which can be called when a
 * TCPStream related object is created from a TCPSocket.  By creating a
 * TCPStream from a TCPV6Socket, an accept operation automatically occurs, and
 * the TCPV6Socket can then still reject the client connection through the
 * return status of it's OnAccept method.
 *
 * @author David Sugar <dyfet@tycho.com>
 * @short bound server for TCP streams and sessions.
 */
class __EXPORT TCPV6Socket : protected Socket
{
private:
	int segsize;
	void setSegmentSize(unsigned mss);

public:
	/**
	 * A method to call in a derived TCPSocket class that is acting
	 * as a server when a connection request is being accepted.  The
	 * server can implement protocol specific rules to exclude the
	 * remote socket from being accepted by returning false.  The
	 * Peek method can also be used for this purpose.
	 *
	 * @return true if client should be accepted.
	 * @param ia internet host address of the client.
	 * @param port number of the client.
	 */
	virtual bool onAccept(const IPV6Host &ia, tpport_t port);

	/**
	 * Fetch out the socket.
	 */
	inline SOCKET getSocket(void)
		{return so;};

	inline int getSegmentSize(void)
		{return segsize;};

	/**
	 * A TCP "server" is created as a TCP socket that is bound
	 * to a hardware address and port number on the local machine
	 * and that has a backlog queue to listen for remote connection
	 * requests.  If the server cannot be created, an exception is
	 * thrown.
	 *
	 * @param bind local ip address or interface to use.
	 * @param port number to bind socket under.
	 * @param backlog size of connection request queue.
	 * @param mss maximum segment size of streaming buffer.
	 */
	TCPV6Socket(const IPV6Address &bind, tpport_t port, unsigned backlog = 5, unsigned mss = 536);

	/**
	 * Create a TCP server for a named host interface and service
	 * port.  We use [host/]port for specifying the optional host
	 * name and service port since ':' is a valid char for ipv6
	 * addresses.
	 *
	 * @param name of host interface and service to use.
	 * @param backlog size of connection request queue.
	 * @param mss maximum segment size of streaming buffers.
	 */
	TCPV6Socket(const char *name, unsigned backlog = 5, unsigned mss = 536);

	/**
	 * Return address and port of next connection request.  This
	 * can be used instead of OnAccept() to pre-evaluate connection
	 * requests.
	 *
	 * @return host requesting a connection.
	 * @param port number of requestor.
	 */
	inline IPV6Host getRequest(tpport_t *port = NULL) const
		{return Socket::getIPV6Sender(port);}

	/**
	 * Used to reject the next incoming connection request.
	 */
	void reject(void);

	/**
	 * Used to get local bound address.
	 */
	inline IPV6Host getLocal(tpport_t *port = NULL) const
		{return Socket::getIPV6Local(port);}

	/**
	 * Used to wait for pending connection requests.
	 * @return true if data packets available.
	 * @param timeout in milliseconds. TIMEOUT_INF if not specified.
	 */
	inline bool isPendingConnection(timeout_t timeout = TIMEOUT_INF) /* not const -- jfc */
		{return Socket::isPending(Socket::pendingInput, timeout);}

	/**
	 * Use base socket handler for ending this socket.
	 */
	virtual ~TCPV6Socket();
};

#endif

/*
:\projects\libraries\cplusplus\commonc++\win32\socket.h(357) : warning C4275: non dll-interface class 'streambuf' used as base for dll-interface class 'TCPStream'
	c:\program files\microsoft visual studio\vc98\include\streamb.h(69) : see declaration of 'streambuf'
c:\projects\libraries\cplusplus\commonc++\win32\socket.h(358) : warning C4275: non dll-interface class 'iostream' used as base for dll-interface class 'TCPStream'
	c:\program files\microsoft visual studio\vc98\include\iostream.h(66) : see declaration of 'iostream'
*/

#ifdef _MSC_VER
#pragma warning(disable:4275) // disable C4275 warning
#endif

/**
 * TCP streams are used to represent TCP client connections to a server
 * by TCP protocol servers for accepting client connections.  The TCP
 * stream is a C++ "stream" class, and can accept streaming of data to
 * and from other C++ objects using the << and >> operators.
 *
 *  TCPStream itself can be formed either by connecting to a bound network
 *  address of a TCP server, or can be created when "accepting" a
 *  network connection from a TCP server.
 *
 * @author David Sugar <dyfet@ostel.com>
 * @short streamable TCP socket connection.
 */
class __EXPORT TCPStream : protected std::streambuf, public Socket, public std::iostream
{
private:
	int doallocate();

	void segmentBuffering(unsigned mss);

	friend TCPStream& crlf(TCPStream&);
	friend TCPStream& lfcr(TCPStream&);

protected:
	timeout_t timeout;
	size_t bufsize;
	Family family;
	char *gbuf, *pbuf;

public:
	/**
	 * The constructor required for building other classes or to
	 * start an unconnected TCPStream for connect.
	 */
	TCPStream(Family family = IPV4, bool throwflag = true, timeout_t to = 0);

	/**
	 * Disconnect the current session and prepare for a new one.
	 */
	void disconnect(void);

	/**
	 * Get protocol segment size.
	 */
	int getSegmentSize(void);

protected:
	/**
	 * Used to allocate the buffer space needed for iostream
	 * operations.  This function is called by the constructor.
	 *
	 * @param size of stream buffers from constructor.
	 */
	void allocate(size_t size);

	/**
	 * Used to terminate the buffer space and cleanup the socket
	 * connection.  This fucntion is called by the destructor.
	 */
	void endStream(void);

	/**
	 * This streambuf method is used to load the input buffer
	 * through the established tcp socket connection.
	 *
	 * @return char from get buffer, EOF if not connected.
	 */
	int underflow();

	/**
	 * This streambuf method is used for doing unbuffered reads
	 * through the establish tcp socket connection when in interactive mode.
	 * Also this method will handle proper use of buffers if not in
	 * interative mode.
	 *
	 * @return char from tcp socket connection, EOF if not connected.
	 */
	int uflow();

	/**
	 * This streambuf method is used to write the output
	 * buffer through the established tcp connection.
	 *
	 * @param ch char to push through.
	 * @return char pushed through.
	 */
	int overflow(int ch);

	/**
	 * Create a TCP stream by connecting to a TCP socket (on
	 * a remote machine).
	 *
	 * @param host address of remote TCP server.
	 * @param port number to connect.
	 * @param mss maximum segment size of streaming buffers.
	 */
	void connect(const IPV4Host &host, tpport_t port, unsigned mss = 536);
#ifdef	CCXX_IPV6
	void connect(const IPV6Host &host, tpport_t port, unsigned mss = 536);
#endif

	/**
	 * Connect a TCP stream to a named destination host and port
	 * number, using getaddrinfo interface if available.
	 *
	 * @param name of host and service to connect
	 * @param mss maximum segment size of stream buffer
	 */
	void connect(const char *name, unsigned mss = 536);

	/**
	 * Used in derived classes to refer to the current object via
	 * it's iostream.  For example, to send a set of characters
	 * in a derived method, one might use *tcp() << "test".
	 *
	 * @return stream pointer of this object.
	 */
	std::iostream *tcp(void)
		{return ((std::iostream *)this);};

public:
	/**
	 * Create a TCP stream by accepting a connection from a bound
	 * TCP socket acting as a server.  This performs an "accept"
	 * call.
	 *
	 * @param server socket listening
	 * @param throwflag flag to throw errors.
	 * @param timeout for all operations.
	 */
	TCPStream(TCPSocket &server, bool throwflag = true, timeout_t timeout = 0);
#ifdef	CCXX_IPV6
	TCPStream(TCPV6Socket &server, bool throwflag = true, timeout_t timeout = 0);
#endif

	/**
	 * Accept a connection from a TCP Server.
	 *
	 * @param server socket listening
	 */
	void connect(TCPSocket &server);
#ifdef	CCXX_IPV6
	void connect(TCPV6Socket &server);
#endif

	/**
	 * Create a TCP stream by connecting to a TCP socket (on
	 * a remote machine).
	 *
	 * @param host address of remote TCP server.
	 * @param port number to connect.
	 * @param mss maximum segment size of streaming buffers.
	 * @param throwflag flag to throw errors.
	 * @param timeout for all operations.
	 */
	TCPStream(const IPV4Host &host, tpport_t port, unsigned mss = 536, bool throwflag = true, timeout_t timeout = 0);
#ifdef	CCXX_IPV6
	TCPStream(const IPV6Host &host, tpport_t port, unsigned mss = 536, bool throwflag = true, timeout_t timeout = 0);
#endif

	/**
	 * Construct a named TCP Socket connected to a remote machine.
	 *
	 * @param name of remote service.
	 * @param family of protocol.
	 * @param mss maximum segment size of streaming buffers.
	 * @param throwflag flag to throw errors.
	 * @param timer for timeout for all operations.
	 */
	TCPStream(const char *name, Family family = IPV4, unsigned mss = 536, bool throwflag = false, timeout_t timer = 0);

	/**
	 * Set the I/O operation timeout for socket I/O operations.
	 *
	 * @param timer to change timeout.
	 */
	inline void setTimeout(timeout_t timer)
		{timeout = timer;};

	/**
	 * A copy constructor creates a new stream buffer.
	 *
	 * @param source reference of stream to copy from.
	 *
	 */
	TCPStream(const TCPStream &source);

	/**
	 * Flush and empty all buffers, and then remove the allocated
	 * buffers.
	 */
	virtual ~TCPStream();

	/**
	 * Flushes the stream input and output buffers, writes
	 * pending output.
	 *
	 * @return 0 on success.
	 */
	int sync(void);

#ifdef	HAVE_SNPRINTF
	/**
	 * Print content into a socket.
	 *
	 * @return count of bytes sent.
	 * @param format string
	 */
	size_t printf(const char *format, ...);
#endif

	/**
	 * Get the status of pending stream data.  This can be used to
	 * examine if input or output is waiting, or if an error or
	 * disconnect has occured on the stream.  If a read buffer
	 * contains data then input is ready and if write buffer
	 * contains data it is first flushed and then checked.
	 */
	bool isPending(Pending pend, timeout_t timeout = TIMEOUT_INF);

	/**
	  * Examine contents of next waiting packet.
	  *
	  * @param buf pointer to packet buffer for contents.
	  * @param len of packet buffer.
	  * @return number of bytes examined.
	  */
	 inline ssize_t peek(void *buf, size_t len)
		 {return _IORET64 ::recv(so, (char *)buf, _IOLEN64 len, MSG_PEEK);};

	/**
	 * Return the size of the current stream buffering used.
	 *
	 * @return size of stream buffers.
	 */
	inline size_t getBufferSize(void) const
		{return bufsize;};
};

/**
 * The TCP session is used to primarily to represent a client connection
 * that can be managed on a seperate thread.  The TCP session also supports
 * a non-blocking connection scheme which prevents blocking during the
 * constructor and moving the process of completing a connection into the
 * thread that executes for the session.
 *
 * @author David Sugar <dyfet@ostel.com>
 * @short Threaded streamable socket with non-blocking constructor.
 */
class __EXPORT TCPSession : public Thread, public TCPStream
{
private:
	TCPSession(const TCPSession &rhs); // not defined
protected:
	/**
	 * Normally called during the thread Initial() method by default,
	 * this will wait for the socket connection to complete when
	 * connecting to a remote socket.  One might wish to use
	 * setCompletion() to change the socket back to blocking I/O
	 * calls after the connection completes.  To implement the
	 * session one must create a derived class which implements
	 * run().
	 *
	 * @return 0 if successful, -1 if timed out.
	 * @param timeout to wait for completion in milliseconds.
	 */
	int waitConnection(timeout_t timeout = TIMEOUT_INF);

	/**
	 * The initial method is used to esablish a connection when
	 * delayed completion is used.  This assures the constructor
	 * terminates without having to wait for a connection request
	 * to complete.
	 */
	void initial(void);

public:
	/**
	 * Create a TCP socket that will be connected to a remote TCP
	 * server and that will execute under it's own thread.
	 *
	 * @param host internet address of remote TCP server.
	 * @param port number of remote server.
	 * @param size of streaming buffer.
	 * @param pri execution priority relative to parent.
	 * @param stack allocation needed on some platforms.
	 */
	TCPSession(const IPV4Host &host,
		tpport_t port, size_t size = 536, int pri = 0, size_t stack = 0);
#ifdef	CCXX_IPV6
	TCPSession(const IPV6Host &host,
		tpport_t port, size_t size = 536, int pri = 0, size_t stack = 0);
#endif

	/**
	 * Create a TCP socket from a bound TCP server by accepting a pending
	 * connection from that server and execute a thread for the accepted
	 * connection.
	 *
	 * @param server tcp socket to accept a connection from.
	 * @param pri execution priority relative to parent.
	 * @param stack allocation needed on some platforms.
	 */
	TCPSession(TCPSocket &server, int pri = 0, size_t stack = 0);
#ifdef	CCXX_IPV6
	TCPSession(TCPV6Socket &server, int pri = 0, size_t stack = 0);
#endif

	/**
	 * Make sure destruction happens through a virtual...
	 */
	virtual ~TCPSession();
};

#if defined(WIN32)

/**
 *  class init_WSA used to initalise windows sockets specfifc stuff : there is
 *  an MS - specific init sequence for Winsock 2 this class attempts to
 *  initalise Winsock 2.2 - needed for non - blocking I/O. It will fall back
 *  on 1.2 or lower if 2.0 or higher is not available,  but < 2.0 does not
 *  support non - blocking I/o
 *  TO DO : might be an idea to have a method that reports version of
 *  Winsock in use or a predicate to test if non - blocking is OK -- JFC
 */
class init_WSA
{
public:
	init_WSA();
	~init_WSA();
};

#endif // WIN32

class __EXPORT SimpleTCPStream;

/**
 * @class SimpleTCPStream
 * @brief Simple TCP Stream, to be used with Common C++ Library
 *
 * This source is derived from a proposal made by Ville Vainio
 * (vvainio@tp.spt.fi).
 *
 * @author Mark S. Millard (msm@wizzer.com)
 * @date   2002-08-15
 * Copyright (C) 2002 Wizzer Works.
 **/
class __EXPORT SimpleTCPStream : public Socket
{
private:

	IPV4Host getSender(tpport_t *port) const;

protected:
	/**
	 * The constructor required for "SimpleTCPStream", a more C++ style
	 * version of the SimpleTCPStream class.
	 */
	SimpleTCPStream();

	/**
	 * Used to terminate the buffer space and cleanup the socket
	 * connection.  This fucntion is called by the destructor.
	 */
	void endStream(void);

	/**
	 * Create a TCP stream by connecting to a TCP socket (on
	 * a remote machine).
	 *
	 * @param host address of remote TCP server.
	 * @param port number to connect.
	 * @param size of streaming input and output buffers.
	 */
	void Connect(const IPV4Host &host, tpport_t port, size_t size);


public:
	/**
	 * Create a TCP stream by accepting a connection from a bound
	 * TCP socket acting as a server.  This performs an "accept"
	 * call.
	 *
	 * @param server bound server tcp socket.
	 * @param size of streaming input and output buffers.
	 */
	SimpleTCPStream(TCPSocket &server, size_t size = 512);

	/**
	 * Create a TCP stream by connecting to a TCP socket (on
	 * a remote machine).
	 *
	 * @param host address of remote TCP server.
	 * @param port number to connect.
	 * @param size of streaming input and output buffers.
	 */
	SimpleTCPStream(const IPV4Host &host, tpport_t port, size_t size = 512);

	/**
	 * A copy constructor creates a new stream buffer.
	 *
	 * @param source A reference to the SimpleTCPStream to copy.
	 */
	SimpleTCPStream(const SimpleTCPStream &source);

	/**
	 * Flush and empty all buffers, and then remove the allocated
	 * buffers.
	 */
	virtual ~SimpleTCPStream();

	/**
	 * @brief Get the status of pending stream data.
	 *
	 * This method can be used to examine if input or output is waiting,
	 * or if an error or disconnect has occured on the stream.
	 * If a read buffer contains data then input is ready. If write buffer
	 * contains data, it is first flushed and then checked.
	 *
	 * @param pend Flag indicating means to pend.
	 * @param timeout The length of time to wait.
	 */
	bool isPending(Pending pend, timeout_t timeout = TIMEOUT_INF);

	void flush() {}

	/**
	 * @brief Read bytes into a buffer.
	 *
	 * <long-description>
	 *
	 * @param bytes A pointer to buffer that will contain the bytes read.
	 * @param length The number of bytes to read (exactly).
	 * @param timeout Period to time out, in milleseconds.
	 *
	 * @return The number of bytes actually read, 0 on EOF.
	 */
	ssize_t read(char *bytes, size_t length, timeout_t timeout = 0);

	/**
	 * @brief Write bytes to buffer
	 *
	 * <long-description>
	 *
	 * @param bytes A pointer to a buffer containing the bytes to write.
	 * @param length The number of bytes to write (exactly).
	 * @param timeout Period to time out, in milleseconds.
	 *
	 * @return The number of bytes actually written.
	 */
	ssize_t write(const char *bytes, size_t length, timeout_t timeout = 0);

	/**
	 * @brief Peek at the incoming data.
	 *
	 * The data is copied into the buffer
	 * but is not removed from the input queue. The function then returns
	 * the number of bytes currently pending to receive.
	 *
	 * @param bytes A pointer to buffer that will contain the bytes read.
	 * @param length The number of bytes to read (exactly).
	 * @param timeout Period to time out, in milleseconds.
	 *
	 * @return The number of bytes pending on the input queue, 0 on EOF.
	 */
	ssize_t peek(char *bytes, size_t length, timeout_t timeout = 0);

};

#ifdef	COMMON_STD_EXCEPTION
class __EXPORT SockException : public IOException
{
private:
	Socket::Error _socketError;

public:
	SockException(const String &str, Socket::Error socketError, long systemError = 0) :
		IOException(str, systemError), _socketError(socketError) {};

	inline Socket::Error getSocketError() const
	{ return _socketError; }
};
#endif

#ifdef	CCXX_NAMESPACES
}
#endif

#endif
/** EMACS **
 * Local variables:
 * mode: c++
 * c-basic-offset: 8
 * End:
 */
