// Copyright (C) 2006-2014 David Sugar, Tycho Softworks.
// Copyright (C) 2015-2020 Cherokees of Idaho.
//
// This file is part of GNU uCommon C++.
//
// GNU uCommon C++ is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU uCommon C++ 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with GNU uCommon C++.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Common socket class and address manipulation.
 * This offers a common socket base class that exposes socket functionality
 * based on what the target platform supports.  Support for multicast, IPV6
 * addressing, and manipulation of cidr policies are all supported here.
 * @file ucommon/socket.h
 */

#ifndef _UCOMMON_SOCKET_H_
#define _UCOMMON_SOCKET_H_

#ifndef _UCOMMON_TIMERS_H_
#include <ucommon/timers.h>
#endif

#ifndef _UCOMMON_LINKED_H_
#include <ucommon/linked.h>
#endif

#ifndef _UCOMMON_STRING_H_
#include <ucommon/string.h>
#endif

#ifndef _UCOMMON_TYPEREF_H_
#include <ucommon/typeref.h>
#endif

extern "C" {
    struct addrinfo;
}

#ifdef  _MSWINDOWS_
#define SHUT_RDWR   SD_BOTH
#define SHUT_WR     SD_SEND
#define SHUT_RD     SD_RECV
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
#else
#include <unistd.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netdb.h>
#endif

#if defined(__ANDROID__)
typedef uint16_t in_port_t;
#endif

#include <errno.h>
#include <stdio.h>

#ifndef IPTOS_LOWDELAY
#define IPTOS_LOWDELAY      0x10
#define IPTOS_THROUGHPUT    0x08
#define IPTOS_RELIABILITY   0x04
#define IPTOS_MINCOST       0x02
#endif

#ifdef  AF_UNSPEC
#define DEFAULT_FAMILY  AF_UNSPEC
#else
#define DEFAULT_FAMILY  AF_INET
#endif

typedef struct sockaddr *sockaddr_t;

typedef struct sockaddr sockaddr_struct;    // older gcc needs...?

/**
 * An object that holds ipv4 or ipv6 binary encoded host addresses.
 */
struct hostaddr_internet
{
    union
    {
        struct in_addr ipv4;
#ifdef  AF_INET6
        struct in6_addr ipv6;
#endif
    };
};

#if defined(AF_INET6) || defined(__CYGWIN__)
/**
 * An object that can hold a ipv4 or ipv6 socket address.  This would be
 * used for tcpip socket connections.  We do not use sockaddr_storage
 * because it is not present in pre ipv6 stacks, and because the storage
 * size also includes the size of the path of a unix domain socket on
 * posix systems.
 */
struct sockaddr_internet
{
    union {
#ifdef  AF_INET6
        struct sockaddr_in6 ipv6;
#endif
        struct sockaddr_in ipv4;
        struct sockaddr address;
    };
};
#else
struct sockaddr_internet
{
    union {
        struct sockaddr_in ipv4;
        struct sockaddr address;
    };
};

struct sockaddr_storage
{
#ifdef  AF_UNIX
    char sa_data[128];
#else
    char sa_data[sizeof(struct sockaddr_in)];
#endif
};
#endif

#ifndef SOCK_DCCP
#define SOCK_DCCP       6
#endif

#ifndef IPPROTO_DCCP
#define IPPROTO_DCCP    23
#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

namespace ucommon {

/**
 * A class to hold internet segment routing rules.  This class can be used
 * to provide a stand-alone representation of a cidr block of internet
 * addresses or chained together into some form of access control list.  The
 * cidr class can hold segments for both IPV4 and IPV6 addresses.  The class
 * accepts cidr's defined as C strings, typically in the form of address/bits
 * or address/submask.  These routines auto-detect ipv4 and ipv6 addresses.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
class __EXPORT cidr : public LinkedObject
{
protected:
    int Family;
    struct hostaddr_internet Netmask, Network;
    char Name[16];

    unsigned mask(const char *cp) const;

    struct hostaddr_internet broadcast(void) const;

    unsigned mask(void) const;

public:
    /**
     * A convenience type for using a pointer to a linked list as a policy chain.
     */
    typedef LinkedObject policy;

    /**
     * Create an uninitialized cidr.
     */
    cidr();

    /**
     * Create an unlinked cidr from a string.  The string is typically in
     * the form base-host-address/range, where range might be a bit count
     * or a network mask.
     * @param string for cidr block.
     */
    cidr(const char *string);

    /**
     * Create an unnamed cidr entry on a specified policy chain.
     * @param policy chain to link cidr to.
     * @param string for cidr block.
     */
    cidr(policy **policy, const char *string);

    /**
     * Create a named cidr entry on a specified policy chain.
     * @param policy chain to link cidr to.
     * @param string for cidr block.
     * @param name of this policy object.
     */
    cidr(policy **policy, const char *string, const char *name);

    /**
     * Construct a copy of an existing cidr.
     * @param existing cidr we copy from.
     */
    cidr(const cidr& existing);

    /**
     * Find the smallest cidr entry in a list that matches the socket address.
     * @param policy chain to search.
     * @param address to search for.
     * @return smallest cidr or NULL if none match.
     */
    static const cidr *find(const policy *policy, const struct sockaddr *address);

    /**
     * Get the largest container cidr entry in a list that matches the
     * socket address.
     * @param policy chain to search.
     * @param address to search for.
     * @return largest cidr or NULL if none match.
     */
    static const cidr *container(const policy *policy, const struct sockaddr *address);

    /**
     * Get the saved name of our cidr.  This is typically used with find
     * when the same policy name might be associated with multiple non-
     * overlapping cidr blocks.  A typical use might to have a cidr
     * block like 127/8 named "localdomain", as well as the ipv6 "::1".
     * @return name of cidr.
     */
    inline const char *getName(void) const {
        return Name;
    }

    /**
     * Get the address family of our cidr block object.
     * @return family of our cidr.
     */
    inline int getFamily(void) const {
        return Family;
    }

    /**
     * Get the network host base address of our cidr block.
     * @return binary network host address.
     */
    inline struct hostaddr_internet getNetwork(void) const {
        return Network;
    }

    /**
     * Get the effective network mask for our cidr block.
     * @return binary network mask for our cidr.
     */
    inline struct hostaddr_internet getNetmask(void) const {
        return Netmask;
    }

    /**
     * Get the broadcast host address represented by our cidr.
     * @return binary broadcast host address.
     */
    inline struct hostaddr_internet getBroadcast(void) const {
        return broadcast();
    }

    /**
     * Get the number of bits in the cidr bitmask.
     * @return bit mask of cidr.
     */
    inline unsigned getMask(void) const {
        return mask();
    }

    /**
     * Set our cidr to a string address.  Replaces prior value.
     * @param string to set for cidr.
     */
    void set(const char *string);

    /**
     * Test if a given socket address falls within this cidr.
     * @param address of socket to test.
     * @return true if address is within cidr.
     */
    bool is_member(const struct sockaddr *address) const;

    /**
     * Test if a given socket address falls within this cidr.
     * @param address of socket to test.
     * @return true if address is within cidr.
     */
    inline bool operator==(const struct sockaddr *address) const {
        return is_member(address);
    }

    /**
     * Test if a given socket address falls outside this cidr.
     * @param address of socket to test.
     * @return true if address is outside cidr.
     */
    inline bool operator!=(const struct sockaddr *address) const {
        return !is_member(address);
    }
};

/**
 * A generic socket base class.  This class can be used directly or as a
 * base class for building network protocol stacks.  This common base tries
 * to handle UDP and TCP sockets, as well as support multicast, IPV4/IPV6
 * addressing, and additional addressing domains (such as Unix domain sockets).
 * @author David Sugar <dyfet@gnutelephony.org>
 */
class __EXPORT Socket
{
protected:
    socket_t so;
    int ioerr;
    timeout_t iowait;

public:
    // temporary splints...
    typedef struct hostaddr_internet host_t;
    typedef cidr cidr_t;

    /**
     * Get an address list directly.  This is used internally by some derived
     * socket types when generic address lists would be invalid.
     * @param host name in the form address or "address:port"
     * @param service id or port to use if not specified in host string.
     * @param type of service to get.
     * @param protocol of service to get.
     */
    static struct addrinfo *query(const char *host, const char *service, int type = SOCK_STREAM, int protocol = 0);

    /**
     * Release an address list directly.  This is used internally by some
     * derived socket types which do not use generic address lists.
     * @param list of addresses.
     */
    static void release(struct addrinfo *list);

    /**
     * A generic socket address class.  This class uses the addrinfo list
     * to store socket multiple addresses in a protocol and family
     * independent manner.  Hence, this address class can be used for ipv4
     * and ipv6 sockets, for assigning connections to multiple hosts, etc.
     * The address class will call the resolver when passed host names.
     * @author David Sugar <dyfet@gnutelephony.org>
     */
    class __EXPORT address
    {
    protected:
        struct addrinfo *list;

    public:
        /**
         * Construct a socket address.  This is used to get an address to
         * bind a socket interface to.  The address can be specified as the
         * ip address of the interface or as a "hostname".  If a hostname
         * is used, then family should be specified for clarity.
         * @param family of socket address.  Needed when host names are used.
         * @param address or hostname.
         * @param type of socket (stream, dgram, etc).
         * @param protocol number of socket.
         */
        address(int family, const char *address, int type = SOCK_STREAM, int protocol = 0);

        /**
         * Construct a socket address for an existing socket.  This can be the
         * name of a host or to perform a lookup in a domain for a service.
         * Family can be used to restrict the set of results returned, however
         * since things like connecto() already filter by family and create
         * will use family from the addrinfo, in most cases AF_UNSPEC can be
         * used.  This may be depreciated in favor of the constructor that
         * matches a set() method.
         * @param family of hosts to filter by or AF_UNSPEC.
         * @param hostname or ip address.  The socket family is used for hostnames.
         * @param service port or name we are referencing or NULL.
         */
        address(int family, const char *hostname, const char *service = NULL);

        /**
         * Construct a socket address list for a service.
         * @param host address for service.
         * @param service name or port number.
         * @param type of service, stream, dgram, etc.
         */
        address(const char *host, const char *service, int type = SOCK_STREAM);

        /**
         * Construct a socket address from host and service.  This is primarily
         * used to construct a list of potential service connections by pure
         * port numbers or for host lookup only.
         * @param hostname or address to use.
         * @param service port or 0.
         */
        address(const char *hostname, in_port_t port = 0);

        /**
         * Construct a socket address from an IPv4 address and a port number.
         */
        address(const in_addr& address, in_port_t port = 0);

        /**
         * Construct a socket address from an IPv6 address and a port number.
         */
        address(const in6_addr& address, in_port_t port = 0);

        /**
         * Construct a socket address from a sockaddr object.
         */
        address(const sockaddr& address) : list(NULL) {
            insert(&address);
        }

        /**
         * Construct a socket address from an addrinfo structure.
         */
        address(const addrinfo* address) : list(NULL) {
            insert(address);
        }

        /**
         * Construct an empty address.
         */
        address();

        /**
         * Copy constructor.
         * @param reference to object to copy from.
         */
        address(const address& reference);

        /**
         * Assignment operator.
         * @param reference to object to copy from.
         */
        address& operator=(const address& rhs);

        /**
         * Destroy address.  Deallocate addrinfo structure.
         */
        ~address();

        /**
         * Compare two address lists.
         * @return true if the two lists are the same (same addresses
         * in the same order).
         */
        bool operator==(const address& other) const;

        inline bool operator!=(const address& other) const {
            return !(*this==other);
        }

        inline bool equals(const address& other) const {
            return *this == other;
        }

        /**
         * Get the first socket address in our address list.
         * @return first socket address or NULL if none.
         */
        const struct sockaddr *get(void) const;

        struct sockaddr *modify(void);

        inline const struct sockaddr *getAddr(void) const {
            return get();
        }

        inline const struct sockaddr *operator()(void) const {
            return get();
        }

        /**
         * Get the first socket address by casted reference.
         * @return first socket address we resolved or NULL if none.
         */
        inline operator struct sockaddr *() {
            return modify();
        }

        /**
         * Get the first socket address of specified family from our list.
         * @param family to seek.
         * @return first socket address of family or NULL if none.
         */
        const struct sockaddr *get(int family) const;

        struct sockaddr *modify(int family);

        inline const struct sockaddr *operator()(int family) const {
            return get(family);
        }

        inline operator struct sockaddr_in *() {
            return (struct sockaddr_in *)modify(AF_INET);
        }

#ifdef  AF_INET6
        inline operator struct sockaddr_in6 *() {
            return (struct sockaddr_in6 *)modify(AF_INET6);
        }
#endif

        /**
         * Get the family of the first member in a list of services.
         * @return family of first socket address or 0 if none.
         */
        int family(void) const;

        /**
         * Get the address size of the first address.
         * @return size in bytes of first socket address or 0 if none.
         */
        inline size_t getLength(void) const {
            return len(get());
        }

        /**
         * Get the port of the first address .
         * @return port of first socket address or 0 if none.
         */
        inline in_port_t getPort(void) const {
            return getPort(get());
        }

        /**
         * Set the port of all addresses in the list.
         * @param port the port to set.
         */
        void setPort(in_port_t port);

        /**
         * Returns a copy of this address list with
         * the specified port set.
         */
        address withPort(in_port_t port) const;

        /**
         * Find a specific socket address in our address list.
         * @return matching address from list or NULL if not found.
         */
        struct sockaddr *find(const struct sockaddr *addr) const;

        /**
         * Get the full socket address list from the object.
         * @return addrinfo list we resolved or NULL if none.
         */
        inline struct addrinfo *getList(void) const {
            return list;
        }

        /**
         * Get the full socket address list by casted reference.
         * @return addrinfo list we resolved or NULL if none.
         */
        inline operator struct addrinfo *() const {
            return list;
        }

        /**
         * Return the full socket address list by pointer reference.
         * @return addrinfo list we resolved or NULL if none.
         */
        inline struct addrinfo *operator*() const {
            return list;
        }

        /**
         * Print the first socket address as a human-readable string to the
         * provided buffer and returns the printed string length.
         * @param src Address to print.
         * @param dst Destination buffer to print the socket address on.
         * @param dst_sz Size of the provided buffer. Strongly recommended to be
         *     at least INET6_ADDRSTRLEN (or INET_ADDRSTRLEN if compiled without IPv6).
         * @param port If true, print port number with address.
         *     If true, ipv6_brackets will also be forced to true.
         * @param ipv6_brackets If true, force printing IPv6 brackets. Ignored if address is an IPv4.
         * @return length (in bytes) of the printed string, excluding trailing zero.
         */
        size_t print(char* dst, size_t dst_sz, bool port=false, bool force_brackets=false) const {
            return print(get(), dst, dst_sz, port, force_brackets);
        }

        /**
         * Test if the address list is valid.
         * @return true if we have an address list.
         */
        inline operator bool() const {
            return list != nullptr;
        }

        inline bool is_valid() const {
            return list != nullptr;
        }

        inline bool isValid() const {
            return list != nullptr;
        }

        /**
         * Test if we have no address list.
         * @return true if we have no address list.
         */
        inline bool operator!() const {
            return list == nullptr;
        }

        /**
         * Test if the first socket address is ADDR_ANY:
         *   0.0.0.0 or ::0
         * @return true if the address is one of the above.
         */
        inline bool is_any() const {
            return isAny(get());
        }

        inline bool isAny() const {
            return isAny(get());
        }

        /**
         * Clear the address list and set the first address to be the
         * ADDR_ANY of the current family, or of the specified family
         * (if set).
         * @param family: address family to set.
         */
        void setAny(int family = AF_UNSPEC);

        /**
         * Test if the first socket address is ADDR_LOOPBACK:
         *   127.0.0.1 or ::1
         * @return true if the address is one of the above.
         */
        inline bool is_loopback() const {
            return isLoopback(get());
        }

        inline bool isLoopback() const {
            return isLoopback(get());
        }

        /**
         * Clear the address list and set the first address to be the
         * ADDR_LOOPBACK of the current family, or of the specified family
         * (if set).
         * @param family: address family to set.
         */
        void setLoopback(int family = AF_UNSPEC);

        /**
         * Clear current object.
         */
        void clear(void);

        /**
         * Set the host addresses to form a new list.
         * @param hostname or address to resolve.
         * @param service name or port number, or NULL if not used.
         * @param type of socket (stream or dgram) to filter list by.
         */
        void set(const char *hostname, const char *service = NULL, int type = SOCK_STREAM);

        /**
         * Append additional host addresses to our list.
         * @param hostname or address to resolve.
         * @param service name or port number, or NULL if not used.
         * @param type of socket (stream or dgram).
         */
        void add(const char *hostname, const char *service = NULL, int type = SOCK_STREAM);

        /**
         * Set an entry for host binding.
         * @param family of socket address.  Needed when hostnames are used.
         * @param address or hostname.
         * @param type of socket (stream, dgram, etc).
         * @param protocol number of socket.
         */
        void set(int family, const char *address, int type = SOCK_STREAM, int protocol = 0);

        /**
         * Add an individual socket address to our address list.
         * @param address to add.
         */
        void add(sockaddr *address);

        /**
         * Insert unique members from another socket address list to ours.
         * @param address list to insert into list.
         * @return count of addresses added.
         */
        unsigned insert(const struct addrinfo *address);

        /**
         * Remove members from another socket address list from ours.
         * @param address list to remove from list.
         * @return count of addresses removed.
         */
        unsigned remove(const struct addrinfo *address);

        /**
         * Remove an individual socket address from our address list.
         * @param address to remove.
         * @return true if found and removed, false if not found.
         */
        bool remove(const struct sockaddr *address);

        /**
         * Insert an individual socket address to our address list only if
         * unique.
         * @param address to insert into list.
         * @return true if inserted, false if duplicate.
         */
        bool insert(const struct sockaddr *address);

        /**
         * Copy an existing addrinfo into our object.  This is also used
         * to support the copy constructor.
         * @param address list to copy from.
         */
        void copy(const struct addrinfo *address);

        /**
         * Set an individual socket address for our address list.
         * @param address to add.
         */
        void set(struct sockaddr *address);

        /**
         * Set a socket address from host and service.
         * @param hostname or address to use.
         * @param service port or 0.
         */
        void set(const char *hostname, in_port_t service = 0);

        /**
         * Returns the size of the socket address according to the family.
         * @return size in bytes of the valid part of the socket address.
         */
        static size_t getLength(const struct sockaddr *address) {
            return len(address);
        }

        /**
         * Returns the port of the socket address.
         * @return port associated to the socket address.
         */
        inline static in_port_t getPort(const struct sockaddr *address) {
            return Socket::port(address);
        }

        /**
         * Set the port of the socket address.
         * @param address to edit.
         * @param port to associate to the socket address.
         */
        static void setPort(struct sockaddr *address, in_port_t port);

        /**
         * Test if the socket address is ADDR_ANY:
         *   0.0.0.0 or ::0
         * @return true if the address is one of the above.
         */
        static bool isAny(const struct sockaddr *address);

        /**
         * Set the socket address to ADDR_ANY:
         *   0.0.0.0 or ::0
         */
        static void setAny(struct sockaddr *sa);

        /**
         * Get a ADDR_ANY socket address of the given family.
         */
        static sockaddr_storage any(int family);

        /**
         * Test if the socket address is ADDR_LOOPBACK:
         *   127.0.0.1 or ::1
         * @return true if the address is one of the above.
         */
        static bool isLoopback(const struct sockaddr *address);

        /**
         * Set the socket address to ADDR_LOOPBACK:
         *   127.0.0.1 or ::1
         * depending on the family of the pointed address.
         */
        static void setLoopback(struct sockaddr *sa);

        /**
         * Get a ADDR_LOOPBACK socket address of the given family.
         */
        static sockaddr_storage loopback(int family);

        /**
         * Duplicate a socket address.
         * @param address to duplicate.
         * @return duplicate address object.
         */
        static struct sockaddr *dup(struct sockaddr *address);

        /**
         * Convert address object into ipv4 address.
         * @param address to convert.
         * @return new ipv4 address or NULL if not ipv4.
         */
        static struct sockaddr_in *ipv4(struct sockaddr *address);

#ifdef  AF_INET6
        /**
         * Convert address object into ipv6 address.
         * @param address to convert.
         * @return new ipv6 address or NULL if not ipv6.
         */
        static struct sockaddr_in6 *ipv6(struct sockaddr *address);
#endif

        /**
         * Print socket address as a human-readable string to the provided
         * buffer and returns the printed string length.
         * @param src Address to print.
         * @param dst Destination buffer to print the socket address on.
         * @param dst_sz Size of the provided buffer. Strongly recommended to be
         *     at least INET6_ADDRSTRLEN (or INET_ADDRSTRLEN if compiled without IPv6).
         * @param port If true, print port number with address.
         *     If true, ipv6_brackets will also be forced to true.
         * @param ipv6_brackets If true, force printing IPv6 brackets. Ignored if address is an IPv4.
         * @return length (in bytes) of the printed string, excluding trailing zero.
         */
        static size_t print(const struct sockaddr *src, char* dst, size_t dst_sz, bool port=false, bool ipv6_brackets=false);
    };

    friend class address;

    /**
     * Create a socket object for use.
     */
    Socket();

    /**
     * Create socket as duped handle of existing socket.
     * @param existing socket to dup.
     */
    Socket(const Socket& existing);

    /**
     * Create socket from existing socket descriptor.
     * @param socket descriptor to use.
     */
    Socket(socket_t socket);

    /**
     * Create and connect a socket to an address from an address list.  The
     * type of socket created is based on the type we are connecting to.
     * @param address list to connect with.
     */
    Socket(const struct addrinfo *address);

    /**
     * Create an unbound socket of a specific type.
     * @param family of our new socket.
     * @param type (stream, udp, etc) of our new socket.
     * @param protocol number of our new socket.'
     */
    Socket(int family, int type, int protocol = 0);

    /**
     * Create a bound socket.  If one wishes to listen for connections on
     * a protocol, then ListenSocket should be used instead.
     * @param address to bind or "*" for all.
     * @param port number of service to bind.
     * @param family to bind as.
     * @param type of socket to bind (stream, udp, etc).
     * @param protocol of socket to bind.
     */
    Socket(const char *address, const char *port, int family = AF_UNSPEC, int type = 0, int protocol = 0);

    /**
     * Shutdown, close, and destroy socket.
     */
    virtual ~Socket();

    /**
     * Cancel pending i/o by shutting down the socket.
     */
    void cancel(void);

    /**
     * Cancel pending i/o by shutting down the socket.
     * @param socket to shutdown.
     */
    static void cancel(socket_t socket);

    /**
     * Shutdown and close the socket.
     */
    void release(void);

    /**
     * Get error code.
     */
    inline int err(void) const {
        return ioerr;
    }

    /**
     * See the number of bytes in the receive queue.
     * @param value to test for.
     * @return true if at least that many bytes waiting in receive queue.
     */
    bool is_pending(unsigned value);

    /**
     * Test if socket is connected.
     * @return true if connected.
     */
    bool connected(void) const;

    /**
     * Test for pending input data.  This function can wait up to a specified
     * timeout for data to appear.
     * @param timeout or 0 if none.
     * @return true if input data waiting.
     */
    bool wait(timeout_t timeout = 0) const;

    /**
     * Set nodelay option for tcp socket.
     * @return 0 if successful, -1 on error.
     */
    inline int nodelay(void) const {
        return nodelay(so);
    }

    /**
     * Test for pending input data.  This function can wait up to a specified
     * timeout for data to appear.
     * @param socket to test.
     * @param timeout or 0 if none.
     * @return true if input data waiting.
     */
    static bool wait(socket_t socket, timeout_t timeout = 0);

    /**
     * Test for output data sent.  This function can wait up to a specified
     * timeout for data to appear sent.
     * @param timeout or 0 if none.
     * @return false if cannot send more output/out of buffer space.
     */
    bool waitSending(timeout_t timeout = 0) const;

    /**
     * Get the number of bytes of data in the socket receive buffer.
     * @return bytes pending.
     */
    inline unsigned pending(void) const {
        return pending(so);
    }

    /**
     * Set socket for unicast mode broadcasts.
     * @param enable broadcasting if true.
     * @return 0 on success, -1 if error.
     */
    inline int broadcast(bool enable) {
        return broadcast(so, enable);
    }

    /**
     * Set socket for keepalive packets.
     * @param enable keep-alive if true.
     * @return 0 on success, -1 if error.
     */
    inline int keepalive(bool enable) {
        return keepalive(so, enable);
    }

    /**
     * Set socket blocking I/O mode.
     * @param enable true for blocking I/O.
     * @return 0 on success, -1 if error.
     */
    inline int blocking(bool enable) {
        return blocking(so, enable);
    }

    /**
     * Set multicast mode and multicast broadcast range.
     * @param ttl to set for multicast socket or 0 to disable multicast.
     * @return 0 on success, -1 if error.
     */
    inline int multicast(unsigned ttl = 1) {
        return multicast(so, ttl);
    }

    /**
     * Set loopback to read multicast packets we broadcast.
     * @param enable true to loopback, false to ignore.
     * @return 0 on success, -1 if error.
     */
    inline int loopback(bool enable) {
        return loopback(so, enable);
    }

    /**
     * Get socket error code.
     * @return socket error code.
     */
    inline int getError(void) const {
        return error(so);
    }

    /**
     * Set the time to live before packets expire.
     * @param time to live to set.
     * @return 0 on success, -1 on error.
     */
    inline int ttl(uint8_t time) {
        return ttl(so, time);
    }

    /**
     * Set the size of the socket send buffer.
     * @param size of send buffer to set.
     * @return 0 on success, -1 on error.
     */
    inline int sendsize(unsigned size) {
        return sendsize(so, size);
    }

    /**
     * Set the size to wait before sending.
     * @param size of send wait buffer to set.
     * @return 0 on success, -1 on error.
     */
    inline int sendwait(unsigned size) {
        return sendwait(so, size);
    }

    /**
     * Set the size of the socket receive buffer.
     * @param size of recv buffer to set.
     * @return 0 on success, -1 on error.
     */
    inline int recvsize(unsigned size) {
        return recvsize(so, size);
    }

    /**
     * Get the type of a socket.
     * @param socket descriptor.
     * @return socket type.
     */
    static int type(const socket_t socket);

    /**
     * Set segment size and get MTU.
     * @param socket to modify.
     * @param size of segment or zero to not set.
     * @return mtu size of socket.
     */
    static unsigned segsize(socket_t socket, unsigned size = 0);

    /**
     * Set congestion control id.
     * @param socket to modify.
     * @param ccid value to set.
     * @return true if success, false if not dccp or not supported ccid used.
     */
    static bool ccid(socket_t socket, uint8_t id);

    /**
     * Get the type of a socket.
     * @return socket type.
     */
    inline int type(void) const {
        return type(so);
    }

    /**
     * Set segment size and get mtu of a socket.
     * @param size of segment or 0 to leave unchanged.
     * @return mtu size.
     */
    inline unsigned segsize(unsigned size) {
        return segsize(so, size);
    }

    /**
     * Set ccid of dccp socket.
     * @param ccid to set.
     * @return true if success, false if not dccp or not supported ccid used.
     */
    inline bool ccid(uint8_t id) {
        return ccid(so, id);
    }

    /**
     * Set the type of service field of outgoing packets.  Some useful
     * values include IPTOS_LOWDELAY to minimize delay for interactive
     * traffic, IPTOS_THROUGHPUT to optimize throughput, OPTOS_RELIABILITY
     * to optimize for reliability, and IPTOS_MINCOST for low speed use.
     * @param type of service value.
     * @return 0 on success or -1 on error.
     */
    inline int tos(int type) {
        return tos(so, type);
    }

    /**
     * Set packet priority, 0 to 6 unless privileged.  Should be set before
     * type-of-service.
     * @param scheduling priority for packet scheduling.
     * @return 0 on success, -1 on error.
     */
    inline int priority(int scheduling) {
        return priority(so, scheduling);
    }

    /**
     * Shutdown the socket communication channel.
     */
    inline void shutdown(void) {
        ::shutdown(so, SHUT_RDWR);
    }

    /**
     * Connect our socket to a remote host from an address list.
     * For TCP (and DCCP) sockets, the entire list may be tried.  For UDP,
     * connect is only a state and the first valid entry in the list is used.
     * @param list of addresses to connect to.
     * @return 0 on success or error.
     */
    int connectto(struct addrinfo *list);

    /**
     * Disconnect a connected socket.  Depending on the implementation, this
     * might be done by connecting to AF_UNSPEC, connecting to a 0 address,
     * or connecting to self.
     * @return 0 on success or error.
     */
    int disconnect(void);

    /**
     * Join socket to multicast group.
     * @param list of groups to join.
     * @return 0 on success, -1 on error.
     */
    int join(const struct addrinfo *list, const int ifindex = 0);

    /**
     * Drop socket from multicast group.
     * @param list of groups to drop.
     * @return 0 on success, -1 on error.
     */
    int drop(const struct addrinfo *list, const int ifindex = 0);

    /**
     * Socket i/o timer setting.
     * @param timeout to wait, inf for blocking, 0 pure non-blocking.
     * @return 0 on success or error code.
     */
    int wait(timeout_t timeout = Timer::inf);

    /**
     * Peek at data waiting in the socket receive buffer.
     * @param data pointer to save data in.
     * @param number of bytes to peek.
     * @return number of bytes actually read, or 0 if no data waiting.
     */
    size_t peek(void *data, size_t number) const;

    /**
     * Read data from the socket receive buffer.  This will be used in abi 4.
     * @param data pointer to save data in.
     * @param number of bytes to read.
     * @param address of peer data was received from.
     * @return number of bytes actually read, 0 if none, -1 if error.
     */
    size_t readfrom(void *data, size_t number, struct sockaddr_storage *address = NULL);

    /**
     * Write data to the socket send buffer.  This will be used in abi 4.
     * @param data pointer to write data from.
     * @param number of bytes to write.
     * @param address of peer to send data to if not connected.
     * @return number of bytes actually sent, 0 if none, -1 if error.
     */
    size_t writeto(const void *data, size_t number, const struct sockaddr *address = NULL);

    /**
     * Read a newline of text data from the socket and save in NULL terminated
     * string.  This uses an optimized I/O method that takes advantage of
     * socket peeking.  This presumes a connected socket on a streamble
     * protocol.  Because the trailing newline is dropped, the return size
     * may be greater than the string length.  If there was no data read
     * because of eof of data, an error has occured, or timeout without
     * input, then 0 will be returned.
     * @param data to save input line.
     * @param size of input line buffer.
     * @return number of bytes read, 0 if none, err() has error.
     */
    size_t readline(char *data, size_t size);

    /**
     * Print formatted string to socket.
     * @param format string.
     * @return number of bytes sent.
     */
    size_t printf(const char *format, ...) __PRINTF(2,3);

    /**
     * Read a string of input from the socket and strip trailing newline.
     * This uses an optimized I/O method that takes advantage of
     * socket peeking.  This presumes a connected socket on a streamble
     * protocol.  Because the trailing newline is dropped, the return size
     * may be greater than the string length.  If there was no data read
     * because of eof of data, an error has occured, or timeout without
     * input, then 0 will be returned.
     * @param buffer to save input line.
     * @return number of bytes read, 0 if none, err() has error.
     */
    size_t readline(String& buffer);

    stringref_t readline(size_t maxsize);

    /**
     * Read a newline of text data from the socket and save in NULL terminated
     * string.  This uses an optimized I/O method that takes advantage of
     * socket peeking.  As such, it has to be rewritten to be used in a ssl
     * layer socket.
     * @param socket to read from.
     * @param data to save input line.
     * @param size of input line buffer.
     * @param timeout to wait for a complete input line.
     * @return number of bytes read, 0 if none, -1 if error.
     */
    static ssize_t readline(socket_t socket, char *data, size_t size, timeout_t timeout = Timer::inf);

    /**
     * Print formatted string to socket.
     * @param socket to write to.
     * @param format string.
     * @return number of bytes sent, -1 if error.
     */
    static ssize_t printf(socket_t socket, const char *format, ...) __PRINTF(2,3);

    /**
     * Write a null terminated string to the socket.  This exists because
     * we messed up consistency with the original puts() method.  In the
     * future there will be a single puts() that has a NULL default.
     * @param string to write.
     * @return number of bytes sent, 0 if none, -1 if error.
     */
    size_t writes(const char *string);

    /**
     * Test if socket is valid.
     * @return true if valid socket.
     */
    operator bool() const;

    /**
     * Test if socket is invalid.
     * @return true if socket is invalid.
     */
    bool operator!() const;

    /**
     * Assign socket from a socket descriptor.  Release existing socket if
     * one present.
     * @param socket descriptor to assign to object.
     */
    Socket& operator=(socket_t socket);

    /**
     * Get the socket descriptor by casting.
     * @return socket descriptor of object.
     */
    inline operator socket_t() const {
        return so;
    }

    /**
     * Get the socket descriptor by pointer reference.
     * @return socket descriptor of object.
     */
    inline socket_t operator*() const {
        return so;
    }

    /**
     * Get the number of bytes pending in the receive buffer of a socket
     * descriptor.
     * @param socket descriptor.
     * @return number of pending bytes.
     */
    static unsigned pending(socket_t socket);

    /**
     * Set the send size of a socket descriptor.
     * @param socket descriptor.
     * @param size of send buffer to set.
     * @return 0 on success, -1 on error.
     */
    static int sendsize(socket_t socket, unsigned size);

    /**
     * Set the size to wait before sending.
     * @param socket descriptor.
     * @param size of send wait buffer to set.
     * @return 0 on success, -1 on error.
     */
    static int sendwait(socket_t socket, unsigned size);

    /**
     * Set the receive size of a socket descriptor.
     * @param socket descriptor.
     * @param size of receive buffer to set.
     * @return 0 on success, -1 on error.
     */
    static int recvsize(socket_t socket, unsigned size);

    /**
     * Connect socket descriptor to a remote host from an address list.
     * For TCP (and DCCP) sockets, the entire list may be tried.  For UDP,
     * connect is only a state and the first valid entry in the list is used.
     * @param socket descriptor.
     * @param list of addresses to connect to.
     * @return 0 on success, -1 on error.
     */
    static int connectto(socket_t socket, struct addrinfo *list);

    /**
     * Disconnect a connected socket descriptor.
     * @param socket descriptor.
     * @return 0 on success, -1 on error.
     */
    static int disconnect(socket_t socket);

    /**
     * Drop socket descriptor from multicast group.
     * @param socket descriptor.
     * @param list of groups to drop.
     * @return 0 on success, -1 on error.
     */
    static int drop(socket_t socket, const struct addrinfo *list, const int ifindex = 0);

    /**
     * Join socket descriptor to multicast group.
     * @param socket descriptor.
     * @param list of groups to join.
     * @return 0 on success, -1 on error.
     */
    static int join(socket_t socket, const struct addrinfo *list, const int ifindex = 0);

    /**
     * Get socket error code of socket descriptor.
     * @param socket descriptor.
     * @return socket error code.
     */
    static int error(const socket_t socket);

    /**
     * Set multicast mode and multicast broadcast range for socket descriptor.
     * @param socket descriptor.
     * @param ttl to set for multicast socket or 0 to disable multicast.
     * @return 0 if success, -1 if error.
     */
    static int multicast(socket_t socket, unsigned ttl = 1);

    /**
     * Set loopback to read multicast packets socket descriptor broadcasts.
     * @param socket descriptor.
     * @param enable true to loopback, false to ignore.
     * @return 0 if success, -1 if error.
     */
    static int loopback(socket_t socket, bool enable);

    /**
     * Set socket blocking I/O mode of socket descriptor.
     * @param socket descriptor.
     * @param enable true for blocking I/O.
     * @return 0 if success, -1 if error.
     */
    static int blocking(socket_t socket, bool enable);

    /**
     * Set socket for keepalive packets for socket descriptor.
     * @param socket descriptor.
     * @param enable keep-alive if true.
     * @return 0 if success, -1 if error.
     */
    static int keepalive(socket_t socket, bool enable);

    /**
     * Set socket for unicast mode broadcasts on socket descriptor.
     * @param socket descriptor.
     * @param enable broadcasting if true.
     * @return 0 if success, -1 if error.
     */
    static int broadcast(socket_t socket, bool enable);

    /**
     * Set tcp nodelay option on socket descriptor.
     * @param socket descriptor.
     * @return 0 if success, -1 if error.
     */
    static int nodelay(socket_t socket);

    /**
     * Set packet priority of socket descriptor.
     * @param socket descriptor.
     * @param scheduling priority for packet scheduling.
     * @return 0 on success, -1 on error.
     */
    static int priority(socket_t socket, int scheduling);

    /**
     * Set type of service of socket descriptor.
     * @param socket descriptor.
     * @param type of service.
     * @return 0 on success, -1 on error.
     */
    static int tos(socket_t socket, int type);

    /**
     * Set the time to live for the socket descriptor.
     * @param socket descriptor.
     * @param time to live to set.
     * @return 0 on success, -1 on error.
     */
    static int ttl(socket_t socket, uint8_t time);

    /**
     * Get the address family of the socket descriptor.
     * @return address family.
     */
    static int family(socket_t socket);

    /**
     * Get the address family of a socket address object.
     * @param address to examine.
     * @return address family.
     */
    inline static int family(const struct sockaddr_storage& address) {
        return ((const struct sockaddr *)&address)->sa_family;
    }

    /**
     * Get the address family of an internet socket address object.
     * @param address to examine.
     * @return address family.
     */
    inline static int family(const struct sockaddr_internet& address) {
        return address.address.sa_family;
    }

    /**
     * Get data waiting in receive queue.
     * @param socket to get from.
     * @param buffer to save.
     * @param size of data buffer to request.
     * @param flags for i/o operation (MSG_OOB, MSG_PEEK, etc).
     * @param address of source.
     * @return number of bytes received, -1 if error.
     */
    static ssize_t recvfrom(socket_t socket, void *buffer, size_t size, int flags = 0, struct sockaddr_storage *address = NULL);

    /**
     * Send data on socket.
     * @param socket to send to.
     * @param buffer to send.
     * @param size of data buffer to send.
     * @param flags for i/o operation (MSG_OOB, MSG_PEEK, etc).
     * @param address of destination, NULL if connected.
     * @return number of bytes sent, -1 if error.
     */
    static ssize_t sendto(socket_t socket, const void *buffer, size_t size, int flags = 0, const struct sockaddr *address = NULL);

    /**
     * Send reply on socket.  Used to reply to a recvfrom message.
     * @param socket to send to.
     * @param buffer to send.
     * @param size of data buffer to send.
     * @param flags for i/o operation (MSG_OOB, MSG_PEEK, etc).
     * @param address to reply to.
     * @return number of bytes sent, -1 if error.
     */
    inline static ssize_t replyto(socket_t socket, const void *buffer, size_t size, int flags, const struct sockaddr_storage *address) {
        return sendto(socket, buffer, size, flags, (const struct sockaddr *)address);
    }

    /**
     * Bind the socket descriptor to a known interface and service port.
     * @param socket descriptor to bind.
     * @param address to bind to or "*" for all.
     * @param service port to bind.
     * @param protocol to use or 0 if default.
     * @return 0 on success, -1 if error.
     */
    static int bindto(socket_t socket, const char *address, const char *service, int protocol = 0);

    /**
     * Bind the socket descriptor to a known interface listen on service port.
     * @param socket descriptor to bind.
     * @param address of interface to bind to.
     * @param backlog for service.
     * @return 0 on success, -1 if error.
     */
    static int listento(socket_t socket, const struct sockaddr *address, int backlog = 5);

    /**
     * Bind the socket descriptor to a known interface.
     * @param socket descriptor to bind.
     * @param address of interface to bind to.
     * @return 0 on success, -1 if error.
     */
    static int bindto(socket_t socket, const struct sockaddr *address);

    /**
     * Accept a socket connection from a remote host.
     * @param socket descriptor to accept from.
     * @param address of socket accepting.
     * @return new socket accepted.
     */
    static socket_t acceptfrom(socket_t socket, struct sockaddr_storage *address = NULL);

    /**
     * Create a socket object unbound.
     * @param family of socket.
     * @param type of socket.
     * @param protocol of socket.
     * @return socket descriptor created or INVALID_SOCKET.
     */
    static socket_t create(int family, int type, int protocol);

    /**
     * Create a connected socket.
     * @param address list to connect to.
     * @param type of socket to create.
     * @param protocol of socket.
     * @return socket descriptor created or INVALID_SOCKET.
     */
    static socket_t create(const struct addrinfo *address, int type, int protocol);

    /**
     * Create a bound socket for a service.
     * @param iface to bind.
     * @param service port to bind.
     * @param family to select or AF_UNSPEC
     * @param type of socket to create.
     * @param protocol of socket to create.
     * @return socket descriptor created or INVALID_SOCKET.
     */
    static socket_t create(const char *iface, const char *service, int family = AF_UNSPEC, int type = 0, int protocol = 0);

    /**
     * Create a connected socket for a service.
     * @param address of service for connect.
     * @return socket descriptor.
     */
    static socket_t create(const Socket::address &address);

    /**
     * Release (close) a socket.
     * @param socket to close.
     */
    static void release(socket_t socket);

    /**
     * Lookup and return the host name associated with a socket address.
     * @param address to lookup.
     * @param buffer to save hostname into.
     * @param size of buffer to save hostname into.
     * @return buffer or NULL if lookup fails.
     */
    static char *hostname(const struct sockaddr *address, char *buffer, size_t size);

    /**
     * Create an address info lookup hint based on the family and type
     * properties of a socket descriptor.
     * @param socket descriptor.
     * @param hint buffer.
     * @return hint buffer.
     */
    static struct addrinfo *hinting(socket_t socket, struct addrinfo *hint);

    /**
     * Lookup a host name and service address based on the addressing family
     * and socket type of a socket descriptor.  Store the result in a socket
     * address structure.
     * @param socket descriptor.
     * @param address that is resolved.
     * @param hostname to resolve.
     * @param service port.
     * @return socket address size.
     */
    static socklen_t query(socket_t socket, struct sockaddr_storage *address, const char *hostname, const char *service);

    /**
     * Get the size of a socket address.
     * @param address of socket.
     * @return size to use for this socket address object.
     */
    static socklen_t len(const struct sockaddr *address);

    /**
     * Compare socket addresses.  Test if the address and service matches
     * or if there is no service, then just the host address values.
     * @param address1 to compare.
     * @param address2 to compare.
     * @return true if same family and equal.
     */
    static bool equal(const struct sockaddr *address1, const struct sockaddr *address2);

    /**
     * Copy a socket address.
     * @param target address pointer to copy into.
     * @param origin address pointer to copy from.
     * @return number of bytes copied, 0 if invalid.
     */
    static unsigned copy(struct sockaddr *target, const struct sockaddr *origin);

    /**
     * Store an address into an address object.
     * @param storage for address.
     * @param address to store.
     * @return number of bytes stored.
     */
    static unsigned store(struct sockaddr_storage *storage, const struct sockaddr *address);

    /**
     * Store an address into an internet address object.
     * @param storage for address.
     * @param address to store.
     * @return number of bytes stored.
     */
    static unsigned store(struct sockaddr_internet *storage, const struct sockaddr *address);

    /**
     * Compare socket host addresses.  Test if the host address matches
     * or if there is no service, then just the host address values.
     * @param address1 to compare.
     * @param address2 to compare.
     * @return true if same family and equal.
     */
    static bool eq_host(const struct sockaddr *address1, const struct sockaddr *address2);

    /**
     * Compare socket addresses.  Test if the stored addresses received match.
     * or if there is no service, then just the host address values.
     * @param address1 to compare.
     * @param address2 to compare.
     * @return true if same family and equal.
     */
    inline static bool eq_from(const struct sockaddr_storage *address1, const struct sockaddr_storage *address2) {
        return equal((const struct sockaddr *)address1, (const struct sockaddr *)address2);
    }

    /**
     * Compare socket addresses.  Test if the internet addresses received match.
     * or if there is no service, then just the host address values.
     * @param address1 to compare.
     * @param address2 to compare.
     * @return true if same family and equal.
     */
    inline static bool eq_inet(const struct sockaddr_internet *address1, const struct sockaddr_internet *address2) {
        return equal((const struct sockaddr *)address1, (const struct sockaddr *)address2);
    }

    /**
     * See if both addresses are in the same subnet.  This is only relevant
     * to IPV4 and class domain routing.
     * @param address1 to test.
     * @param address2 to test.
     * @return true if in same subnet.
     */
    static bool eq_subnet(const struct sockaddr *address1, const struct sockaddr *address2);

    /**
     * Get the socket address of the interface needed to reach a destination
     * address.
     * @param address of interface found.
     * @param target address.
     * @param size of interface, 0 used for older code
     * @return 0 on success, -1 on error.
     */
    static int via(struct sockaddr *address, const struct sockaddr *target, socklen_t size = 0);

    /**
     * Get the hostname of a socket address.
     * @param address to lookup.
     * @param buffer to save hostname in.
     * @param size of hostname buffer.
     * @return buffer if found or NULL if not.
     */
    static char *query(const struct sockaddr *address, char *buffer, socklen_t size);

    /**
     * Get the service port of a socket.
     * @param address of socket to examine.
     * @return service port number.
     */
    static in_port_t port(const struct sockaddr *address);

    /**
     * Get the service port of an inet socket.
     * @param address of internet socket to examine.
     * @return service port number.
     */
    inline static in_port_t port(const struct sockaddr_internet *address) {
        return port((const struct sockaddr *)address);
    }

    /**
     * Convert a socket address and service into a hash map index.
     * @param address to convert.
     * @param size of map index.
     * @return key index path.
     */
    static unsigned keyindex(const struct sockaddr *address, unsigned size);

    /**
     * Convert a socket host address into a hash map index.
     * @param address to convert.
     * @param size of map index.
     * @return key index path.
     */
    static unsigned keyhost(const struct sockaddr *address, unsigned size);

    /**
     * Initialize socket subsystem.
     */
    static void init(void);

    /**
     * Set default socket family preference for query options when the
     * socket type is otherwise not specified.
     * @param family to select.
     */
    static void query(int family);

    /**
     * Set the default socket behavior for v6-v4 mapping.  This also
     * effects v6 address lookup as to whether v4 remapped addresses
     * can be used if no v6 address is found.
     * @param enable true to set mapping.  This is default.
     */
    static void v4mapping(bool enable);

    /**
     * Return error code of last socket operation,
     * @return errno style error code.
     */
    static int error(void);

    /**
     * Simple function to validate that a given IP address string is a "zero"
     * address.  Such address strings are used for example in SIP to indicate
     * "hold" by re-inviting peers to a null address.  Supports IPV4 and
     * IPV6 addresses.
     * @param string address to check.
     * @return true if zero/null address.
     */
    static bool is_null(const char *string);

    /**
     * Simple function to validate that a given IP address string is a numeric
     * address.  This can be used to verify an address is not a "host" name.
     * Supports IPV4 and IPV6 address strings.
     * @param string address to check.
     * @return true if zero/null address.
     */
    static bool is_numeric(const char *string);

    /**
     * Get local address to which the socket is bound.  This is defined here
     * because we may re-define the backend linkage for the socks proxy in
     * the future.
     * @param socket descriptor to examine.
     * @param address storage for local address.
     * @return 0 on success, -1 on failure.
     */
    static int local(socket_t socket, struct sockaddr_storage *address);

    /**
     * Get remote address to which the socket is connected.  This is defined
     * here because we may re-define the backend linkage for the socks proxy in
     * the future.
     * @param socket descriptor to examine.
     * @param address storage for remote address.
     * @return 0 on success, -1 on failure.
     */
    static int remote(socket_t socket, struct sockaddr_storage *address);
};

/**
 * A bound socket used to listen for inbound socket connections.  This class
 * is commonly used for TCP and DCCP listener sockets.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
class __EXPORT ListenSocket : protected Socket
{
private:
    __DELETE_COPY(ListenSocket);

public:
    /**
     * Create and bind a listener socket.
     * @param address to bind on or "*" for all.
     * @param service port to bind listener.
     * @param backlog size for buffering pending connections.
     * @param family of socket.
     * @param type of socket.
     * @param protocol for socket if not TCPIP.
     */
    ListenSocket(const char *address, const char *service, unsigned backlog = 5, int family = AF_UNSPEC, int type = 0, int protocol = 0);

    /**
     * Create a listen socket directly.
     * @param address to bind on or "*" for all.
     * @param service port to bind listener.
     * @param backlog size for buffering pending connections.
     * @param family of socket.
     * @param type of socket.
     * @param protocol for socket if not TCPIP.
     * @return bound and listened to socket.
     */
    static socket_t create(const char *address, const char *service, unsigned backlog = 5, int family = AF_UNSPEC, int type = 0, int protocol = 0);

    /**
     * Accept a socket connection.
     * @param address to save peer connecting.
     * @return socket descriptor of connected socket.
     */
    socket_t accept(struct sockaddr_storage *address = NULL) const;

    /**
     * Wait for a pending connection.
     * @param timeout to wait.
     * @return true when acceptable connection is pending.
     */
    inline bool wait(timeout_t timeout = Timer::inf) const {
        return Socket::wait(timeout);
    }

    /**
     * Get the socket descriptor of the listener.
     * @return socket descriptor.
     */
    inline operator socket_t() const {
        return so;
    }

    /**
     * Get the socket descriptor of the listener by pointer reference.
     * @return socket descriptor.
     */
    inline socket_t operator*() const {
        return so;
    }

    /**
     * Get the socket descriptor of the listener.
     * @return socket descriptor.
     */
    inline socket_t getsocket(void) const {
        return so;
    }

    inline socket_t handle(void) const {
        return so;
    }

};

/**
 * A generic tcp server class.  This saves the service id tag so that it
 * can be propagated.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
class __EXPORT TCPServer : public ListenSocket
{
private:
    __DELETE_DEFAULTS(TCPServer);

public:
    /**
     * Create and bind a tcp server.  This mostly is used to preserve the
     * service tag for TCP Socket when derived from a server instance.
     * @param service tag to use.
     * @param address of interface to bind or "*" for all.
     * @param backlog size for pending connections.
     */
    TCPServer(const char *address, const char *service, unsigned backlog = 5);
};

/**
 * Helper class for linked_pointer template.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
class __EXPORT linked_sockaddr_operations
{
protected:
    inline linked_sockaddr_operations() {}

    /**
    * Helper function for linked_pointer<struct sockaddr>.
    */
    const struct addrinfo *_nextaddrinfo(const struct addrinfo *addrinfo) const;

    /**
    * Helper function for linked_pointer<struct sockaddr>.
    */
    const struct sockaddr *_getaddrinfo(const struct addrinfo *addrinfo) const;

    /**
    * Helper function for linked_pointer<struct sockaddr>.
    */
    socket_t _getaddrsock(const struct addrinfo *addrinfo) const;
};

/**
 * Linked pointer for address lists.  This can be used to iterate through
 * the list of a Socket::address object using the linked_pointer method.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
template <>
class linked_pointer<sockaddr_struct> : private linked_sockaddr_operations
{
private:
    const struct addrinfo *ptr;

public:
    inline linked_pointer(const struct addrinfo *list) {
        ptr = list;
    }

    inline linked_pointer(const linked_pointer& copy) {
        ptr = copy.ptr;
    }

    inline linked_pointer() {
        ptr = nullptr;
    }

    inline linked_pointer(Socket::address& list) {
        ptr = list.getList();
    }

    /**
     * Get the full socket address list by casted reference.
     * @return addrinfo list we resolved or NULL if none.
     */
    inline operator const struct sockaddr *() const {
        return _getaddrinfo(ptr);
    }

    /**
     * Return the full socket address list by pointer reference.
     * @return addrinfo list we resolved or NULL if none.
     */
    inline const struct sockaddr *operator*() const {
        return _getaddrinfo(ptr);
    }

    inline operator const struct sockaddr_in *() const {
        return (struct sockaddr_in *)_getaddrinfo(ptr);
    }

    inline const struct sockaddr_in *in(void) const {
        return (struct sockaddr_in *)_getaddrinfo(ptr);
    }

#ifdef  AF_INET6
    inline operator const struct sockaddr_in6 *() const {
        return (struct sockaddr_in6 *)_getaddrinfo(ptr);
    }

    inline const struct sockaddr_in6 *in6(void) const {
        return (struct sockaddr_in6 *)_getaddrinfo(ptr);
    }
#endif

    /**
     * Get socket as expression operator.
     */
    inline socket_t operator()(void) const {
        return _getaddrsock(ptr);
    }

    /**
     * Test if the address list is valid.
     * @return true if we have an address list.
     */
    inline operator bool() const {
        return ptr != nullptr;
    }

    /**
     * Assign our pointer from an address list.
     * @param pointer of linked list.
     */
    inline linked_pointer& operator=(const struct addrinfo *list) {
        ptr = list;
        return *this;
    }

    /**
     * Assign our pointer from an address list.
     * @param pointer of linked list.
     */
    inline linked_pointer& operator=(Socket::address& list) {
        ptr = list.getList();
        return *this;
    }

    /**
     * Assign our pointer from an address list.
     * @param pointer of linked list.
     */
    inline void set(const struct addrinfo *list) {
        ptr = list;
    }

    /**
     * Assign our pointer from an address list.
     * @param pointer of linked list.
     */
    inline void set(Socket::address& list) {
        ptr = list.getList();
    }


    /**
     * Return member from typed object our pointer references.
     * @return evaluated member of object we point to.
     */
    inline const struct sockaddr* operator->() const {
        return _getaddrinfo(ptr);
    }

    /**
     * Test if we have no address list.
     * @return true if we have no address list.
     */
    inline bool operator!() const {
        return ptr == nullptr;
    }

    inline void next(void) {
        ptr = _nextaddrinfo(ptr);
    }
};

/**
 * A convenience function to convert a socket address list into an addrinfo.
 * @param address list object.
 * @return addrinfo list or NULL if empty.
 */
inline struct addrinfo *addrinfo(Socket::address& address) {
    return address.getList();
}

/**
 * A convenience function to convert a socket address list into a socket
 * address.
 * @param address list object.
 * @return first socket address in list or NULL if empty.
 */
inline const struct sockaddr *addr(Socket::address& address) {
    return address.get();
}

/**
 * Compare two socket addresses to see if equal.  If the port is zero
 * then this is the same as comparing host address alone.
 * @param s1 socket address to compare.
 * @param s2 socket address to compare.
 * @return true if addresses same.
 */
inline bool eq(const struct sockaddr *s1, const struct sockaddr *s2) {
    return Socket::equal(s1, s2);
}

/**
 * Compare two stored socket addresses to see if equal.  If the port is zero
 * then this is the same as comparing host address alone.
 * @param s1 stored socket address to compare.
 * @param s2 stored socket address to compare.
 * @return true if addresses same.
 */
inline bool eq(const struct sockaddr_storage *s1, const struct sockaddr_storage *s2) {
    return Socket::equal((const struct sockaddr *)s1, (const struct sockaddr *)s2);
}

/**
 * Compare two host addresses to see if equal.  The port numbers are
 * ignored.
 * @param s1 socket address to compare.
 * @param s2 socket address to compare.
 * @return true if addresses same.
 */
inline bool eq_host(const struct sockaddr *s1, const struct sockaddr *s2) {
    return Socket::eq_host(s1, s2);
}

inline bool eq_subnet(const struct sockaddr *s1, const struct sockaddr *s2) {
    return Socket::eq_subnet(s1, s2);
}

String str(Socket& so, size_t size);

namespace Type {

    class HostAddress
    {
    private:
        struct hostaddr_internet storage;

    public:
        inline HostAddress() {
            memset(&storage, 0, sizeof(storage));
        }

        inline HostAddress(const HostAddress& copy) {
            memcpy(&storage, &copy.storage, sizeof(storage));
        }

        inline HostAddress(const struct hostaddr_internet *addr) {
            memcpy(&storage, addr, sizeof(struct hostaddr_internet));
        }

        inline HostAddress(const in_addr *addr) {
            memset(&storage, 0, sizeof(storage));
            memcpy(&storage, addr, sizeof(struct in_addr));
        }

#ifdef  AF_INET6
        inline HostAddress(const in6_addr *addr) {
            memset(&storage, 0, sizeof(storage));
            memcpy(&storage, addr, sizeof(struct in6_addr));
        }
#endif

        inline operator const struct hostaddr_internet *() const {
            return &storage;
        }

        inline struct hostaddr_internet *operator*() {
            return &storage;
        }

        inline socklen_t size() {
            return sizeof(storage);
        }

        inline HostAddress& operator=(const HostAddress& copy) {
            memcpy(&storage, &copy.storage, sizeof(storage));
            return *this;
        }

        inline HostAddress& operator=(const struct hostaddr_internet *host) {
            memcpy(&storage, host, sizeof(struct hostaddr_internet));
            return *this;
        }

        inline bool operator==(const HostAddress& check) const {
            return (memcmp(&check.storage, &storage, sizeof(storage)) == 0);
        }

        inline bool operator!=(const HostAddress& check) const {
            return (memcmp(&check.storage, &storage, sizeof(storage)) != 0);
        }

        inline bool operator==(const struct hostaddr_internet *host) const {
            return (memcmp(host, &storage, sizeof(storage)) == 0);
        }

        inline bool operator!=(const struct hostaddr_internet *host) const {
            return (memcmp(host, &storage, sizeof(storage)) != 0);
        }
    };

    class SockAddress
    {
    private:
        struct sockaddr_storage storage;

    public:
        inline SockAddress() {
            memset(&storage, 0, sizeof(storage));
        }

        inline SockAddress(const SockAddress& copy) {
            memcpy(&storage, &copy.storage, sizeof(storage));
        }

        inline SockAddress(const struct sockaddr *addr) {
            Socket::store(&storage, addr);
        }

        inline SockAddress& operator=(const SockAddress& copy) {
            memcpy(&storage, &copy.storage, sizeof(storage));
            return *this;
        }

        inline SockAddress& operator=(const struct sockaddr *addr) {
            Socket::store(&storage, addr);
            return *this;
        }

        inline operator const struct sockaddr *() const {
            return (const struct sockaddr*)&storage;
        }

        inline struct sockaddr *operator*() {
            return (struct sockaddr *)&storage;
        }

        inline const struct sockaddr *get() const {
            return (const struct sockaddr *)&storage;
        }

        inline socklen_t size() {
            return sizeof(storage);
        }

        inline bool operator==(const SockAddress& check) const {
            return Socket::equal(get(), check.get());
        }

        inline bool operator!=(const SockAddress& check) const {
            return !Socket::equal(get(), check.get());
        }

        inline bool operator==(const struct sockaddr *check) const {
            return Socket::equal(get(), check);
        }

        inline bool operator!=(const struct sockaddr *check) const {
            return !Socket::equal(get(), check);
        }
    };

    class InetAddress
    {
    private:
        struct sockaddr_internet storage;

    public:
        inline InetAddress() {
            memset(&storage, 0, sizeof(storage));
        }

        inline InetAddress(const InetAddress& copy) {
            memcpy(&storage, &copy.storage, sizeof(storage));
        }

        inline InetAddress(const struct sockaddr *addr) {
            Socket::store(&storage, addr);
        }

        inline InetAddress& operator=(const InetAddress& copy) {
            memcpy(&storage, &copy.storage, sizeof(storage));
            return *this;
        }

        inline InetAddress& operator=(const struct sockaddr *addr) {
            Socket::store(&storage, addr);
            return *this;
        }

        inline operator const struct sockaddr *() const {
            return (const struct sockaddr*)&storage;
        }

        inline struct sockaddr *operator*() {
            return (struct sockaddr *)&storage;
        }

        inline const struct sockaddr *get() const {
            return (const struct sockaddr *)&storage;
        }

        inline socklen_t size() {
            return sizeof(storage);
        }

        inline bool operator==(const SockAddress& check) const {
            return Socket::equal(get(), check.get());
        }

        inline bool operator!=(const SockAddress& check) const {
            return !Socket::equal(get(), check.get());
        }

        inline bool operator==(const struct sockaddr *check) const {
            return Socket::equal(get(), check);
        }

        inline bool operator!=(const struct sockaddr *check) const {
            return !Socket::equal(get(), check);
        }
    };
}

typedef TCPServer   tcpserv_t;

} // namespace ucommon

#endif
