#ifdef UNIX
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <errno.h>
#include <fcntl.h>
#ifdef SOLARIS
#include <sys/sockio.h> // for SIOCGIFADDR ioctl
#include <netdb.h>      // for rest_init()
#endif  // SOLARIS

#ifdef HAVE_IPV6
#ifdef MACOSX
#include <nameser.h>
#endif // MACOSX
#include <resolv.h>
#endif  // HAVE_IPV6
#endif  // UNIX
#ifdef WIN32
#include <winsock2.h>
#include <WS2tcpip.h>  // for extra socket options
#include <Iphlpapi.h>
#include <Iptypes.h>
#endif  // WIN32

// NOTE: This value should be confirmed.  It's the
//       apparently Linux-only approach we currently
//       use to set the IPv6 flow info header fields
//       Other approaches are under investigation
//       (We seem to be only able to affect
//        the "traffic class" bits at this time)
#ifndef IPV6_FLOWINFO_SEND
#define IPV6_FLOWINFO_SEND 33 // from linux/in6.h
#endif // !IPV6_FLOWINFO_SEND

#include "protoSocket.h"
#include "protoDebug.h"

#include <stdio.h>
#include <string.h>

// Hack for using with NRL IPSEC implementation
#ifdef HAVE_NETSEC
#include <net/security.h>
extern void* netsec_request;
extern int netsec_requestlen;
#endif // HAVE_NETSEC

// Use this macro (-DSOCKLEN_T=xxx) in your
// system's Makefile if the type socklen_t is 
// not defined for your system.
// (use "man getsockname" to see what type 
// is required for the last argument)

#ifdef SOCKLEN_T
#define socklen_t SOCKLEN_T
#else
#ifdef WIN32
//#define socklen_t int
#endif // WIN32
#endif // if/else SOCKLEN_T

#ifdef WIN32
const ProtoSocket::Handle ProtoSocket::INVALID_HANDLE = INVALID_SOCKET;
#else
const ProtoSocket::Handle ProtoSocket::INVALID_HANDLE = -1;
#endif  // if/else WIN32

ProtoSocket::ProtoSocket(ProtoSocket::Protocol theProtocol)
    : protocol(theProtocol), state(CLOSED), handle(INVALID_HANDLE),
      address_type(ProtoAddress::IPv4), port(-1), 
#ifdef HAVE_IPv6
      flow_label(0),
#endif // HAVE_IPV6
      notifier(NULL), notify_output(false), 
#ifdef WIN32
      output_ready(false),
#endif // WIN32
      listener(NULL), user_data(NULL)
{
    
}

ProtoSocket::~ProtoSocket()
{
    
    Close();
    if (listener)
    {
        delete listener;
        listener = NULL;  
    }
}


bool ProtoSocket::SetNotifier(ProtoSocket::Notifier* theNotifier)
{
    if (notifier != theNotifier)
    {
        if (IsOpen())
        {
            // 1) Detach old notifier, if any
            if (notifier)
            {
                notifier->UpdateSocketNotification(*this, 0);
#ifdef UNIX
                if (!theNotifier)
                {
                    // Reset socket to "blocking"
                    if(-1 == fcntl(handle, F_SETFL, fcntl(handle, F_GETFL, 0) & ~O_NONBLOCK))
                        DMSG(0, "ProtoSocket::SetNotifier() fcntl(F_SETFL(O_NONBLOCK)) error: %s\n",
                                 GetErrorString());
                }
            }
            else
            {
                // Set socket to "non-blocking"
                if(-1 == fcntl(handle, F_SETFL, fcntl(handle, F_GETFL, 0) | O_NONBLOCK))
                {                
                    DMSG(0, "ProtoSocket::SetNotifier() fcntl(F_SETFL(O_NONBLOCK)) error: %s\n",
                            GetErrorString());
                    return false;
                }
#endif // UNIX
            }   

            // 2) Set and update new notifier (if applicable)
            notifier = theNotifier;
            if (!UpdateNotification())
            {
                notifier = NULL;  
                return false;
            } 
        }
        else
        {
            notifier = theNotifier;
        }
    }
    return true;
}  // end ProtoSocket::SetNotifier()

// WIN32 needs the address type determine IPv6 _or_ IPv4 socket domain
// Note: WIN32 can't do IPv4 on an IPV6 socket!
bool ProtoSocket::Open(UINT16               thePort, 
                       ProtoAddress::Type   addrType,
                       bool                 bindOnOpen)
{
    if (IsOpen()) Close();
    int domain;
#ifdef HAVE_IPV6
    if(addrType == ProtoAddress::IPv6)
    {
        if (!HostIsIPv6Capable())
        {
            if (!SetHostIPv6Capable())
            {
                DMSG(0, "ProtoSocket::Open() system not IPv6 capable?!\n"); 
                return false;  
            }   
        }
        domain = PF_INET6;
        address_type = ProtoAddress::IPv6;
    }
    else
#endif // HAVE_IPV6
    {
        domain = PF_INET;
        address_type = ProtoAddress::IPv4;
    }    
    
    int socketType = (UDP == protocol) ? SOCK_DGRAM : SOCK_STREAM;
#ifdef WIN32
    int family = (ProtoAddress::IPv6 == addrType) ? AF_INET6: AF_INET;
    // Startup WinSock
    if (!ProtoAddress::Win32Startup())
    {
        DMSG(0, "ProtoSocket::Open() WSAStartup() error: %s\n", GetErrorString());
        return false;
    }
 
    // Since we're might want QoS, we need find a QoS-capable UDP service provider
    WSAPROTOCOL_INFO* infoPtr = NULL;
    WSAPROTOCOL_INFO* protocolInfo = NULL;
    DWORD buflen = 0;
    // Query to properly size protocolInfo buffer
    WSAEnumProtocols(NULL, protocolInfo, &buflen);
    if (buflen)
    {
        int protocolType = (UDP == protocol) ? IPPROTO_UDP : IPPROTO_TCP;
        // Enumerate, try to find multipoint _AND_ QOS-capable UDP, and create a socket
        if ((protocolInfo = (WSAPROTOCOL_INFO*) new char[buflen]))
        {
            int count = WSAEnumProtocols(NULL, protocolInfo, &buflen);
            if (SOCKET_ERROR != count)
            {
                for (int i = 0; i < count; i++)
                {
                    switch (protocol)
                    {
                        case UDP:
                            if ((IPPROTO_UDP == protocolInfo[i].iProtocol) &&
                                (0 != (XP1_SUPPORT_MULTIPOINT & protocolInfo[i].dwServiceFlags1)))
                            {
                                if ((NULL == infoPtr) ||
                                    (0 != (XP1_QOS_SUPPORTED & protocolInfo[i].dwServiceFlags1)))
                                {
                                    infoPtr = protocolInfo + i;
                                }
                            }
                            break;
                        case TCP:
                            if ((IPPROTO_TCP == protocolInfo[i].iProtocol) &&
                                (0 != (XP1_QOS_SUPPORTED & protocolInfo[i].dwServiceFlags1)))
                                infoPtr = protocolInfo + i;
                            break;
                        default:
                            break;
                    }
                }  // end for (i=0..count)
            }
            else
            {
                DMSG(0, "ProtoSocket: WSAEnumProtocols() error2!\n");
                ProtoAddress::Win32Cleanup();
                return false;
            }
        }
        else
        {
            DMSG(0, "ProtoSocket: Error allocating memory!\n");
            ProtoAddress::Win32Cleanup();
            return false;
        }        
    }
    else
    {
        DMSG(0, "ProtoSocket::Open() WSAEnumProtocols() error1!\n");            
    }

    // Use WSASocket() to open right kind of socket
    // (Just a regular socket if infoPtr == NULL
    DWORD flags = 0;
#ifndef _WIN32_WCE
    if (UDP == protocol) flags |= (WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF);
#endif // !_WIN32_WCE
    handle = WSASocket(family, socketType, 0, infoPtr, 0, flags);
    if (protocolInfo) delete[] protocolInfo;
    if (INVALID_HANDLE == handle)
    {
        DMSG(0, "ProtoSocket::WSASocket() error: %s\n", GetErrorString());
        return false;
    }
#else
    if (INVALID_HANDLE == (handle = socket(domain, socketType, 0)))
    {
       DMSG(0, "ProtoSocket: socket() error: %s\n", GetErrorString());
       return false;
    }
#endif // if/else WIN32
    state = IDLE;
#ifdef NETSEC
    if (net_security_setrequest(handle, 0, netsec_request, netsec_requestlen))
    {
        DMSG(0, "ProtoSocket: net_security_setrequest() error: %s\n", 
                GetErrorString());
        Close();
        return false;
    }
#endif // NETSEC

#ifdef UNIX
    // Don't pass descriptor to child processes
    if(-1 == fcntl(handle, F_SETFD, FD_CLOEXEC))
        DMSG(0, "ProtoSocket::Open() fcntl(FD_CLOEXEC) warning: %s\n", GetErrorString());
    // Make the socket non-blocking
    if (notifier)
    {
        if(-1 == fcntl(handle, F_SETFL, fcntl(handle, F_GETFL, 0) | O_NONBLOCK))
        {
            DMSG(0, "ProtoSocket::Open() fcntl(F_SETFL(O_NONBLOCK)) error: %s\n",
                    GetErrorString());
            Close();
            return false;
        }
    }
#endif // UNIX
    if (bindOnOpen)
    {
		if (!Bind(thePort))
		{
			Close();
			return false;
		}
    }
    else
    {
        port = -1;
        if (!UpdateNotification())
        {
            DMSG(0, "ProtoSocket::Open() error installing async notification\n");
            Close();
            return false;
        }
    } 
    return true;
}  // end ProtoSocket::Open()

bool ProtoSocket::UpdateNotification()
{    
    if (notifier)
    {
        int notifyFlags = NOTIFY_NONE;
        if (listener)
        {
            switch(protocol)
            {
                case UDP:
                    if (IsBound())
                        notifyFlags = NOTIFY_INPUT;
                    else
                        notifyFlags = NOTIFY_NONE;
                    break;

                case TCP:
                    switch(state)
                    {
                        case CLOSED:
                        case IDLE:
                            notifyFlags = NOTIFY_NONE;
                            break;
                        case CONNECTING:
                            notifyFlags = NOTIFY_OUTPUT;
                            break;
                        case LISTENING:
                            notifyFlags = NOTIFY_INPUT;
                            break;
                        case CONNECTED:
                            notifyFlags = NOTIFY_INPUT;
                            if (notify_output) 
                                notifyFlags |= NOTIFY_OUTPUT;
                            break;  
                    }  // end switch(state)
                    break; 
            }  // end switch(protocol)
        }  // end if(listener)
        return notifier->UpdateSocketNotification(*this, notifyFlags);
    }  
    else
    {
        return true;   
    }
}  // end ProtoSocket::UpdateNotification()

void ProtoSocket::OnNotify(ProtoSocket::Flag theFlag)
{
    ProtoSocket::Event event = INVALID_EVENT;
    if (NOTIFY_INPUT == theFlag)
    {
        switch (state)
        {
            case CLOSED:
                break;
            case IDLE:
                event = RECV;
                break;
            case CONNECTING:
                break;
            case LISTENING:
                // (TBD) check for error
                event = ACCEPT;
                break; 
            case CONNECTED:
                event = RECV;
                break;
        }        
    }
    else if (NOTIFY_OUTPUT == theFlag)
    {
        switch (state)
        {
            case CLOSED:
                break;
            case IDLE:
                event = SEND;
                break;
            case CONNECTING:
            {
#ifdef WIN32
                event = CONNECT;
                state = CONNECTED;
                UpdateNotification();
#else
                int err;
                socklen_t errsize = sizeof(err);
                if (getsockopt(handle, SOL_SOCKET, SO_ERROR, (char*)&err, &errsize))
                {
                    DMSG(0, "ProtoSocket::OnNotify() getsockopt() error: %s\n", GetErrorString());
                    
                } 
                else if (err)
                {
                    DMSG(0, "ProtoSocket::OnNotify() Connect() error: %s\n", GetErrorString());
                    Disconnect();
                    event = DISCONNECT;
                }
                else
                {
                    event = CONNECT;
                    state = CONNECTED;
                    UpdateNotification();
                }
#endif  // if/else WIN32
                break;
            }
            case LISTENING: 
                break;
            case CONNECTED:
                event = SEND;
                break;
        }    
    }
    else  // NOTIFY_NONE  (connection was ended)
    {
        switch(state)
        {
            case CONNECTING:
                //DMSG(0, "ProtoSocket::OnNotify() Connect() error: %s\n", GetErrorString());
            case CONNECTED:
                Disconnect();
                event = DISCONNECT;
                break;
            default:
                break;
        }
    }
    if (listener) listener->on_event(*this, event);
}  // end ProtoSocket::OnNotify()

bool ProtoSocket::Bind(UINT16 thePort, ProtoAddress* localAddress)
{
    if (IsOpen() && (NULL != localAddress))
    {
        if (address_type != localAddress->GetType())
        {
            DMSG(0,"ProtoSocket::Bind() error: address type mismatch\n");
            return false;
        }
    }
    else if (!IsOpen()) 
    {
        ProtoAddress::Type addrType = localAddress ? localAddress->GetType() : ProtoAddress::IPv4;
		if(!Open(thePort, addrType, false))
        {
			DMSG(0,"ProtoSocket::Bind() error opening socket on port %d\n",thePort);
			return false;
		}
	}
#ifdef HAVE_IPV6
    struct sockaddr_storage socketAddr;
    socklen_t addrSize;
    if (ProtoAddress::IPv6 == address_type)
    {
	    addrSize = sizeof(struct sockaddr_in6);
        memset((char*)&socketAddr, 0, sizeof(struct sockaddr_in6));    
        ((struct sockaddr_in6*)&socketAddr)->sin6_family = AF_INET6;
        ((struct sockaddr_in6*)&socketAddr)->sin6_port = htons(thePort);
        ((struct sockaddr_in6*)&socketAddr)->sin6_flowinfo = 0;
        if (localAddress)
            memcpy(&(((struct sockaddr_in6*)&socketAddr)->sin6_addr), localAddress->GetRawHostAddress(), localAddress->GetLength());
	else
            ((struct sockaddr_in6*)&socketAddr)->sin6_addr = in6addr_any;
    }
    else
    {
        addrSize = sizeof(struct sockaddr_in);
        memset((char*)&socketAddr, 0, sizeof(struct sockaddr_in));    
        ((struct sockaddr_in*)&socketAddr)->sin_family = AF_INET;
        ((struct sockaddr_in*)&socketAddr)->sin_port = htons(thePort);
        if (localAddress)
            memcpy(&(((struct sockaddr_in*)&socketAddr)->sin_addr.s_addr), localAddress->GetRawHostAddress(), localAddress->GetLength());
	else
            ((struct sockaddr_in*)&socketAddr)->sin_addr.s_addr = htonl(INADDR_ANY);
    }
#else
    struct sockaddr socketAddr;
    socklen_t addrSize = sizeof(struct sockaddr_in);
    memset((char*)&socketAddr, 0, sizeof(struct sockaddr_in));    
    ((struct sockaddr_in*)&socketAddr)->sin_family = AF_INET;
    ((struct sockaddr_in*)&socketAddr)->sin_port = htons(thePort);
    if(localAddress)
        memcpy(&(((struct sockaddr_in*)&socketAddr)->sin_addr.s_addr), localAddress->GetRawHostAddress(), localAddress->GetLength());
    else
        ((struct sockaddr_in*)&socketAddr)->sin_addr.s_addr = htonl(INADDR_ANY);
#endif  //  if/else HAVE_IPV6
    // Bind the socket to the given port     
    if (bind(handle, (struct sockaddr*)&socketAddr, addrSize) < 0)
    {
       DMSG(0, "ProtoSocket: bind() error: %s\n", GetErrorString());
       return false;
    }

    // Get socket name so we know our port number  
    socklen_t sockLen = addrSize;
    if (getsockname(handle, (struct sockaddr*)&socketAddr, &sockLen) < 0) 
    {    
        DMSG(0, "ProtoSocket: getsockname() error: %s\n", GetErrorString());
        return false;
    }

    switch(((struct sockaddr*)&socketAddr)->sa_family)
    {
        case AF_INET:    
            port = ntohs(((struct sockaddr_in*)&socketAddr)->sin_port);
            break;
#ifdef HAVE_IPV6        
        case AF_INET6:
            port = ntohs(((struct sockaddr_in6*)&socketAddr)->sin6_port);
            break;
#endif // HAVE_IPV6        
        default:
            DMSG(0, "ProtoSocket: getsockname() returned unknown address type: %s\n",
                    GetErrorString());
            return false;
    }
    return UpdateNotification();
}  // end ProtoSocket::Bind()

bool ProtoSocket::Shutdown()
{
    if (IsConnected() && (TCP == protocol))
    {
        bool notifyOutput = notify_output;
        if (notifyOutput)
        {
            notify_output = false;
            UpdateNotification();
        }
#ifdef WIN32
        if (SOCKET_ERROR == shutdown(handle, SD_SEND))
#else
        if (0 != shutdown(handle, 1))
#endif // if/else WIN32/UNIX
        {
            if (notifyOutput)
            {
                notify_output= true;
                UpdateNotification();
            }
            DMSG(0, "ProtoSocket::Shutdown() error: %s\n", GetErrorString());
            return false;
        }
        else
        {
            return true;
        }
    }
    else
    {
        DMSG(0, "ProtoSocket::Shutdown() error: socket not connected\n");
        return false;
    }
}  // end ProtoSocket::Shutdown()

void ProtoSocket::Close()
{
    if (IsOpen()) 
    {
        if (IsConnected()) Disconnect();
        state = CLOSED;
        UpdateNotification();
#ifdef UNIX
        close(handle);
#endif // UNIX
#ifdef WIN32
        if (SOCKET_ERROR == closesocket(handle))
        {
            DMSG(0, "ProtoSocket::Close() warning: closesocket() error: %s\n", GetErrorString());
        }
        ProtoAddress::Win32Cleanup();
        output_ready = false;
#endif // WIN32
        handle = INVALID_HANDLE;
        port = -1;
    }
}  // end Close() 


bool ProtoSocket::Connect(const ProtoAddress& theAddress)
{
    if (IsConnected()) Disconnect();
    if (!IsOpen() && !Open(0, theAddress.GetType()))
    {
        DMSG(0, "ProtoSocket::Connect() error opening socket!\n");
        return false;
    }
#ifdef HAVE_IPV6
    socklen_t addrSize = (ProtoAddress::IPv6 == address_type) ?
                            sizeof(sockaddr_storage) : 
                            sizeof(struct sockaddr_in);
#else
    socklen_t addrSize = sizeof(struct sockaddr_in);
#endif // if/else HAVE_IPV6
    state = CONNECTING;
    if (!UpdateNotification())
    {   
        DMSG(0, "ProtoSocket::Connect() error updating notification\n");
        state = IDLE;
        return false;
    }
#ifdef WIN32
    int result = WSAConnect(handle, &theAddress.GetSockAddr(), addrSize,
                            NULL, NULL, NULL, NULL);
    if (SOCKET_ERROR == result)
    {
        if (WSAEWOULDBLOCK != WSAGetLastError())
        {
            DMSG(0, "ProtoSocket::Connect() WSAConnect() error: (%s)\n", GetErrorString());
            state = IDLE;
            UpdateNotification();
            return false;
        }
    }
#else
    int result = connect(handle, &theAddress.GetSockAddr(), addrSize);
    if (0 != result)
    {
        if (EINPROGRESS != errno)
        {
            DMSG(0, "ProtoSocket::Connect() connect() error: %s\n", GetErrorString());
            state = IDLE;
            UpdateNotification();
            return false;
        }
    }
#endif // if/else WIN32
    else
    {
        state = CONNECTED;
        if (!UpdateNotification())
        {
            DMSG(0, "ProtoSocket::Connect() error updating notification\n");
            state = IDLE;
            return false;
        }
    }
    destination = theAddress;
    return true;
}  // end bool ProtoSocket::Connect()

void ProtoSocket::Disconnect()
{
    if (IsConnected())
    {
        state = IDLE;
        destination.Invalidate();
        UpdateNotification();
        struct sockaddr nullAddr;
        memset(&nullAddr, 0 , sizeof(struct sockaddr));
#ifdef UNIX
        nullAddr.sa_family = AF_UNSPEC;
        if (connect(handle, &nullAddr, sizeof(struct sockaddr)))
        {
            DMSG(12, "ProtoSocket::Disconnect() connect() error: %s)\n", GetErrorString());
            // (TBD) should we Close() and re-Open() the socket here?
        }
#else
        if (SOCKET_ERROR == WSAConnect(handle, &nullAddr, sizeof(struct sockaddr),
                                       NULL, NULL, NULL, NULL))
        {
            DMSG(12, "ProtoSocket::Disconnect() WSAConnect() error: (%s)\n", GetErrorString());
        }
        output_ready = false;
#endif  // WIN32
    }
}  // end ProtoSocket::Disconnect()

bool ProtoSocket::Listen(UINT16 thePort)
{
    if (IsBound())
    {
        if ((0 != thePort) && (thePort != port))
        {
            DMSG(0, "ProtoSocket::Listen() error: socket bound to different port.\n");
            return false;
        }
    }
    else
    {
        if (0 != thePort)
        {
            if (!Bind(thePort))
            {
                DMSG(0, "ProtoSocket::Listen() error binding socket.\n");
                return false; 
            }            
        }
        else
        {
            DMSG(0, "ProtoSocket::Listen() error: no port given.\n");
            return false;   
        }
    } 
    state = LISTENING;
    if (!UpdateNotification())
    {
        state = IDLE;
        DMSG(0, "ProtoSocket::Listen() error updating notification\n");
        return false;
    }
#ifdef WIN32
    if (SOCKET_ERROR == listen(handle, 5))
#else
    if (listen(handle, 5) < 0)
#endif // if/else WIN32/UNIX
    {
        DMSG(0, "ProtoSocket: listen() error: %s\n", GetErrorString());
        return false;
    }
    return true;
}  // end ProtoSocket::Listen()

bool ProtoSocket::Accept(ProtoSocket* newSocket)
{
    ProtoSocket& theSocket = newSocket ? *newSocket : *this;
    // Clone server socket
    if (this != &theSocket) theSocket = *this;
#ifdef HAVE_IPV6
	struct sockaddr_in6 socketAddr;
#else
    struct sockaddr socketAddr;
#endif // if/else HAVE_IPV6
    socklen_t addrLen = sizeof(socketAddr);
#ifdef WIN32
    if (this != &theSocket)
    {
        if (!ProtoAddress::Win32Startup())
        {
            DMSG(0, "ProtoSocket::Accept() WSAStartup() error: %s\n", GetErrorString());
            return false;
        }
    }
    Handle theHandle = WSAAccept(handle, (struct sockaddr*)&socketAddr, &addrLen, NULL, NULL);
#else
    Handle theHandle = accept(handle, (struct sockaddr*)&socketAddr, &addrLen);
    if (notifier)
    {
        if(-1 == fcntl(theHandle, F_SETFL, fcntl(theHandle, F_GETFL, 0) | O_NONBLOCK))
        {
            DMSG(0, "ProtoSocket::Open() fcntl(F_SETFL(O_NONBLOCK)) error: %s\n",
                    GetErrorString());
            Close();
            return false;
        }
    }
#endif // if/else WIN32/UNIX
    if (INVALID_HANDLE == theHandle)
    {
        DMSG(0, "ProtoSocket::Accept() accept() error: %s\n", GetErrorString());
        if (this != &theSocket)
        {
 #ifdef WIN32
            closesocket(theHandle);
            ProtoAddress::Win32Cleanup();
#endif // WIN32
            theSocket.handle = INVALID_HANDLE;
            theSocket.state = CLOSED;
        }
        return false;
    } 
    theSocket.destination.SetSockAddr((struct sockaddr&)socketAddr);
    // Get the socket name so we know our port number
    addrLen = sizeof(socketAddr);
    if (getsockname(theSocket.handle, (struct sockaddr*)&socketAddr, &addrLen) < 0) 
    {    
        DMSG(0, "ProtoSocket::Accept(): getsockname() error\n");
        if (this != &theSocket)
        {
 #ifdef WIN32
            closesocket(theHandle);
            ProtoAddress::Win32Cleanup();
#endif // WIN32
            theSocket.handle = INVALID_HANDLE;
            theSocket.state = CLOSED;
        }
	    return false;
    }
    switch(((struct sockaddr*)&socketAddr)->sa_family)
    {
	    case AF_INET:    
	        theSocket.port = ntohs(((struct sockaddr_in*)&socketAddr)->sin_port);
	        break;
#ifdef HAVE_IPV6	    
	    case AF_INET6:
	        theSocket.port = ntohs(((struct sockaddr_in6*)&socketAddr)->sin6_port);
	        break;
#endif // HAVE_IPV6	    
	    default:
	        DMSG(0, "ProtoSocket: getsockname() returned unknown address type");
            if (this != &theSocket)
            {
 #ifdef WIN32
                closesocket(theHandle);
                ProtoAddress::Win32Cleanup();
#endif // WIN32
                theSocket.handle = INVALID_HANDLE;
                theSocket.state = CLOSED;
            }
	        return false;;
    }  // end switch()
    if (this == &theSocket)
    {  
#ifdef WIN32
        closesocket(theSocket.handle);
#else
        close(theSocket.handle);
#endif // WIN32
    }
    theSocket.handle = theHandle;
    theSocket.state = CONNECTED;
    theSocket.UpdateNotification();
    return true;
}  // end ProtoSocket::Accept()

bool ProtoSocket::Send(const char*         buffer, 
                       unsigned int&       numBytes)
{
    if (IsConnected())
    {
        
#ifdef WIN32
        WSABUF sendBuf;
        sendBuf.buf = (char*)buffer;
        sendBuf.len = numBytes;
        DWORD bytesSent;
        if (SOCKET_ERROR == WSASend(handle, &sendBuf, 1, &bytesSent, 0, NULL, NULL))
        {
            numBytes = bytesSent;
            switch (WSAGetLastError())
            {
                case WSAEINTR:
                    return true;
                case WSAEWOULDBLOCK:
                    output_ready = false;
                    return true;
                case WSAENETRESET:
                case WSAECONNABORTED:
                case WSAECONNRESET:
                case WSAESHUTDOWN:
                case WSAENOTCONN:
                    output_ready = false;
                    OnNotify(NOTIFY_NONE);
                    break;
                default:
                    DMSG(0, "ProtoSocket::Send() WSASend() error: %s\n", GetErrorString());
                    break;
            }
            return false;
        }
        else
        {
            output_ready= true;
            numBytes = bytesSent;
            return true;
        }
#else
        int result = send(handle, buffer, (size_t)numBytes, 0);
        if (result < 0)
        {
            numBytes = 0;
            switch (errno)
            {
                case EINTR:
                case EAGAIN:
                    return true;
                case ENETRESET:
                case ECONNABORTED:
                case ECONNRESET:
                case ESHUTDOWN:
                case ENOTCONN:
                    OnNotify(NOTIFY_NONE);
                    break;
                default:
                    DMSG(0, "ProtoSocket::Send() send() error: %s\n", GetErrorString());
                    break;
            }
            return false;  
        }
        else
        {
            numBytes = result;
            return true;
        }
#endif // if/else WIN32/UNIX
    }
    else
    {
        DMSG(0, "ProtoSocket::Send() error unconnected socket\n");
        return false;   
    }
}  // end ProtoSocket::Send()

bool ProtoSocket::Recv(char*            buffer, 
                       unsigned int&    numBytes)
{
#ifdef WIN32
    WSABUF recvBuf;
    recvBuf.buf = buffer;
    recvBuf.len = numBytes;
    DWORD bytesReceived;
    DWORD flags = 0;
    if (SOCKET_ERROR == WSARecv(handle, &recvBuf, 1, &bytesReceived, &flags, NULL, NULL))
    {
        numBytes = bytesReceived;
        switch (WSAGetLastError())
        {
            case WSAEINTR:
            case WSAEWOULDBLOCK:
                break;
            case WSAENETRESET:
            case WSAECONNABORTED:
            case WSAECONNRESET:
            case WSAESHUTDOWN:
            case WSAENOTCONN:
                DMSG(0, "ProtoSocket::Recv() recv() error: %s\n", GetErrorString());
                OnNotify(NOTIFY_NONE);
                break;
            default:
                DMSG(0, "ProtoSocket::Recv() recv() error: %s\n", GetErrorString());
                break;
        }
        return false;
    }
    else
    {
        numBytes = bytesReceived;
        return true;
    }
#else
    int result = recv(handle, buffer, numBytes, 0);
    if (result < 0)
    {
        numBytes = 0;
        switch (errno)
        {
            case EINTR:
            case EAGAIN:
                DMSG(4, "ProtoSocket::Recv() recv() error: %s\n", GetErrorString());               
                break;
            case ENETRESET:
            case ECONNABORTED:
            case ECONNRESET:
            case ESHUTDOWN:
            case ENOTCONN:
                OnNotify(NOTIFY_NONE);
                break;
            default:
                DMSG(0, "ProtoSocket::Recv() recv() error: %s\n", GetErrorString());
                break;
        }
        return false;
    }
    else
    {
        numBytes = result;
        if (0 == result) OnNotify(NOTIFY_NONE);
        return true;
    }
#endif // if/else WIN32/UNIX
}  // end ProtoSocket::Recv()

bool ProtoSocket::SendTo(const char*         buffer, 
                         unsigned int        buflen,
                         const ProtoAddress& dstAddr)
{
    if (!IsOpen() && !Open(0, dstAddr.GetType()))
    {
        DMSG(0, "ProtoSocket::SendTo() error: socket not open\n");
        return false;
    }
    if (IsConnected())
    {
        unsigned int numBytes = buflen;
        if (!Send(buffer, numBytes))
        {
            DMSG(0, "ProtoSocket::SendTo() error: Send() error\n");
            return false;
        }
        else if (numBytes != buflen)
        {
            DMSG(0, "ProtoSocket::SendTo() error: Send() incomplete\n");
            return false;
        }
        else
        {
            return true;
        }
    }
    else
    {
        socklen_t addrSize;
#ifdef HAVE_IPV6
        if (flow_label && (ProtoAddress::IPv6 == dstAddr.GetType()))
            ((struct sockaddr_in6*)(&dstAddr.GetSockAddr()))->sin6_flowinfo = flow_label;
        if (ProtoAddress::IPv6 == dstAddr.GetType())
            addrSize = sizeof(struct sockaddr_in6);
        else
#endif //HAVE_IPV6
            addrSize = sizeof(struct sockaddr_in);
#ifdef WIN32
        WSABUF wsaBuf;
        wsaBuf.len = buflen;
        wsaBuf.buf = (char*)buffer;
        DWORD numBytes;
        if (SOCKET_ERROR == WSASendTo(handle, &wsaBuf, 1, &numBytes, 0, 
            &dstAddr.GetSockAddr(), addrSize, NULL, NULL))
#else
        int result = sendto(handle, buffer, (size_t)buflen, 0, 
                            &dstAddr.GetSockAddr(), addrSize);
        if (result < 0)
#endif // if/else WIN32/UNIX
        {
            DMSG(4, "ProtoSocket::SendTo() sendto() error: %s\n", GetErrorString());
            return false;
        }
        else
        {   
            return true;
        }
    }
}  // end ProtoSocket::SendTo()


bool ProtoSocket::RecvFrom(char*            buffer, 
                           unsigned int&    numBytes, 
                           ProtoAddress&    sourceAddr)
{
    if (!IsBound())
    {
        DMSG(0, "ProtoSocket::RecvFrom() error: socket not bound\n");
        numBytes = 0;    
    }
    struct sockaddr sockAddr;
    socklen_t addrLen = sizeof(sockAddr);
    int result = recvfrom(handle, buffer, (size_t)numBytes, 0, &sockAddr, &addrLen);
    if (result < 0)
    {
#ifdef WIN32
        DMSG(14, "ProtoSocket::RecvFrom() recvfrom() error: %s\n", GetErrorString());
#else
        if ((errno != EINTR) && (errno != EAGAIN))
            DMSG(4, "ProtoSocket::RecvFrom() recvfrom() error: %s\n", GetErrorString());
#endif // UNIX
        numBytes = 0;
        return false;
    }
    else
    {
        numBytes = result;
        sourceAddr.SetSockAddr(sockAddr);
        if (!sourceAddr.IsValid())
        {
            DMSG(0, "ProtoSocket::RecvFrom() Unsupported address type!\n");
            return false;
        }
        return true;
    }
}  // end ProtoSocket::RecvFrom()

#ifdef HAVE_IPV6
#ifndef IPV6_ADD_MEMBERSHIP
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
#endif // !IPV6_ADD_MEMBERSHIP
#endif // HAVE_IPV6 


bool ProtoSocket::JoinGroup(const ProtoAddress& groupAddress, 
                            const char*         interfaceName)
{
    if (!IsOpen() && !Open(0, groupAddress.GetType(), false))
    {
        DMSG(0, "ProtoSocket::JoinGroup() error: socket not open\n");
        return false;
    }    
#if defined(WIN32) && !defined(_WIN32_WCE)
    if (interfaceName && !SetMulticastInterface(interfaceName))
        DMSG(0, "ProtoSocket::JoinGroup() warning: error setting socket multicast interface\n");
    SOCKET result = WSAJoinLeaf(handle, &groupAddress.GetSockAddr(), sizeof(struct sockaddr), 
                                NULL, NULL, NULL, NULL, JL_BOTH);
    if (INVALID_SOCKET == result)
    {
        DMSG(0, "WSAJoinLeaf() error: %d\n", WSAGetLastError());
        return false;
    }
#else
    int result;
#ifdef HAVE_IPV6
    if  (ProtoAddress::IPv6 == groupAddress.GetType())
    {
        if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6*)&groupAddress.GetSockAddr())->sin6_addr))
        {
            struct ip_mreq mreq;
            mreq.imr_multiaddr.s_addr = 
                IN6_V4MAPPED_ADDR(&(((struct sockaddr_in6*)&groupAddress.GetSockAddr())->sin6_addr));
            if (interfaceName)
            {
                ProtoAddress interfaceAddress;
                if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
                {
                    mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
                }
                else
                {
                    DMSG(0, "ProtoSocket::JoinGroup() invalid interface name\n");
                    return false;
                }
            }
            else
            {
                mreq.imr_interface.s_addr = INADDR_ANY;  
            } 
            result = setsockopt(handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
        }
        else
        {
            struct ipv6_mreq mreq;
            mreq.ipv6mr_multiaddr = ((struct sockaddr_in6*)&groupAddress.GetSockAddr())->sin6_addr;
            if (interfaceName)
                mreq.ipv6mr_interface = GetInterfaceIndex(interfaceName);               
            else
                mreq.ipv6mr_interface = 0;
            result = setsockopt(handle, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
        }
    }
    else
#endif // HAVE_IPV6
    {
        struct ip_mreq mreq;
        mreq.imr_multiaddr = ((struct sockaddr_in*)&groupAddress.GetSockAddr())->sin_addr;
        if (interfaceName)
        {
            ProtoAddress interfaceAddress;
            if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
            {
                mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
            }
            else
            {
                DMSG(0, "ProtoSocket::JoinGroup() invalid interface name\n");
                return false;
            }
        }
        else
        {
            mreq.imr_interface.s_addr = INADDR_ANY;  
        }
        result = setsockopt(handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
    }
    if (result < 0)
    { 
        DMSG(0, "ProtoSocket: Error joining multicast group: %s\n", GetErrorString());
        return false;
    }    
#endif // if/else WIN32
    return true;
}  // end ProtoSocket::JoinGroup() 

#ifdef WIN32

bool ProtoSocket::GetInterfaceAddress(const char*         interfaceName,
                                      ProtoAddress::Type  addressType,
                                      ProtoAddress&       theAddress)
{
    if (!strcmp(interfaceName, "lo"))  // loopback interface
    {
        // (TBD) should we also test for interfaceName == loopback address string?
        if (ProtoAddress::IPv4 == addressType)
        {
            theAddress.ResolveFromString("127.0.0.1");
            return true;
        }
        else
        {
            theAddress.ResolveFromString("::1");
            return true;
        }
    }
    unsigned long bufferSize = 0;
    if (ERROR_BUFFER_OVERFLOW != GetAdaptersInfo(NULL, &bufferSize))
    {
        DMSG(0, "ProtoSocket::GetInterfaceIndex() GetAdaptorsInfo() error\n");
        return false;
    }
    char* infoBuffer = new char[bufferSize];
    if (NULL == infoBuffer)
    {
        DMSG(0, "ProtoSocket::GetInterfaceIndex() new infoBuffer error: %s\n", ::GetErrorString());
        return false;
    }
    IP_ADAPTER_INFO* adapter = (IP_ADAPTER_INFO*)infoBuffer;
    if (ERROR_SUCCESS != GetAdaptersInfo(adapter, &bufferSize))
    {
        DMSG(0, "ProtoSocket::GetInterfaceIndex() GetAdaptorsInfo() error\n");
        delete[] infoBuffer;
        return false;
    }
    bool result;

    // First compare "interfaceName" with adapter names
    while (adapter)
    {
        if (!strcmp(interfaceName, adapter->AdapterName)) 
        {
            break;
        }
        adapter = adapter->Next;
    }
    if (NULL == adapter)
    {
        // Perhaps "interfaceName" is an address string?
        ProtoAddress tempAddr;
        if (tempAddr.ResolveFromString(interfaceName))
        {
            IP_ADAPTER_INFO* adapter = (IP_ADAPTER_INFO*)infoBuffer;
            while (adapter)
            {
                IP_ADDR_STRING* addr = &adapter->IpAddressList;
                while (addr)
                {
                    ProtoAddress ifAddr;
                    if (ifAddr.ResolveFromString(addr->IpAddress.String))
                        if (tempAddr.HostIsEqual(ifAddr))
                            break;
                    addr = addr->Next;
                }
                if (addr) break;
                adapter = adapter->Next;
            }
        }
    }
    if (adapter)
    {
        // OK, we found the adapter, now return its "default" address (of matching type)
        // First, try the "CurrentIpAddress"
        IP_ADDR_STRING* addr = adapter->CurrentIpAddress;
        if (addr)
        {
            if (theAddress.ResolveFromString(addr->IpAddress.String))
                if (theAddress.GetType() != addressType)
                    addr = NULL;
        }
        if (NULL == addr)
        {
            // Go through the "IpAddressList"
            addr = &adapter->IpAddressList;
            while (addr)
            {
                if (theAddress.ResolveFromString(addr->IpAddress.String))
                    if (theAddress.GetType() == addressType)
                        break;
                addr = addr->Next;
            }
        }
        if (NULL != addr)
        {
            result = true;
        }
        else
        {
            DMSG(4, "ProtoSocket::GetInterfaceAddress() no address of given type\n");
            result = false;
        }
    }
    else
    {
        
        DMSG(0, "ProtoSocket::GetInterfaceAddress() unknown interface\n");
        result = false;
    }
    delete[] infoBuffer;
    return result;
}  // end ProtoSocket::GetInterfaceAddress()

unsigned int ProtoSocket::GetInterfaceIndex(const char* interfaceName)
{
    // (TBD) should we also test for interfaceName == loopback address string?
    if (!strcmp(interfaceName, "lo")) return 1;  // loopback interface
    unsigned long bufferSize = 0;
    if (ERROR_BUFFER_OVERFLOW != GetAdaptersInfo(NULL, &bufferSize))
    {
        DMSG(0, "ProtoSocket::GetInterfaceIndex() GetAdaptorsInfo() error\n");
        return false;
    }
    char* infoBuffer = new char[bufferSize];
    if (NULL == infoBuffer)
    {
        DMSG(0, "ProtoSocket::GetInterfaceIndex() new infoBuffer error: %s\n", ::GetErrorString());
        return false;
    }
    IP_ADAPTER_INFO* adapter = (IP_ADAPTER_INFO*)infoBuffer;
    if (ERROR_SUCCESS != GetAdaptersInfo(adapter, &bufferSize))
    {
        DMSG(0, "ProtoSocket::GetInterfaceIndex() GetAdaptorsInfo() error\n");
        delete[] infoBuffer;
        return false;
    }
    unsigned int index = 0;
    while (adapter)
    {
        if (!strcmp(interfaceName, adapter->AdapterName)) 
        {
            index = adapter->Index;
            break;
        }
        IP_ADDR_STRING* addr = &adapter->IpAddressList;
        while (addr)
        {
            if (!strcmp(interfaceName, addr->IpAddress.String)) 
            {
                index = adapter->Index;
                break;
            }
            addr = addr->Next;
        }
        if (addr) break;
        adapter = adapter->Next;
    }
    delete[] infoBuffer;
    return index;
}  // end ProtoSocket::GetInterfaceIndex()

bool ProtoSocket::GetInterfaceName(unsigned int index, char* buffer, unsigned int buflen)
{
    if (1 == index) // loopback interface
    {
        strncpy(buffer, "lo", buflen);
        return true;
    }
    unsigned long bufferSize = 0;
    if (ERROR_BUFFER_OVERFLOW != GetAdaptersInfo(NULL, &bufferSize))
    {
        DMSG(0, "ProtoSocket::GetInterfaceIndex() GetAdaptorsInfo() error\n");
        return false;
    }
    char* infoBuffer = new char[bufferSize];
    if (NULL == infoBuffer)
    {
        DMSG(0, "ProtoSocket::GetInterfaceIndex() new infoBuffer error: %s\n", ::GetErrorString());
        return false;
    }
    IP_ADAPTER_INFO* adapter = (IP_ADAPTER_INFO*)infoBuffer;
    if (ERROR_SUCCESS != GetAdaptersInfo(adapter, &bufferSize))
    {
        DMSG(0, "ProtoSocket::GetInterfaceIndex() GetAdaptorsInfo() error\n");
        delete[] buffer;
        return false;
    }
    while (adapter)
    {
        if (index == adapter->Index)
        {
            strncpy(buffer, adapter->AdapterName, buflen);
            break;
        }
        adapter = adapter->Next;
    }
    delete[] infoBuffer;
    return (NULL != adapter);
}  // end ProtoSocket::GetInterfaceName(by index)

bool ProtoSocket::GetInterfaceName(const ProtoAddress& ifAddr, char* buffer, unsigned int buflen)
{
    if (ifAddr.IsLoopback())
    {
        strncpy(buffer, "lo", buflen);
        return true;
    }
    char addrString[64];
    if (ifAddr.GetHostString(addrString))
    {
        unsigned int ifIndex = GetInterfaceIndex(addrString);
        if (0 != ifIndex)
        {
            return GetInterfaceName(ifIndex, buffer, buflen);
        }
        else
        {
            DMSG(0, "ProtoSocket::GetInterfaceName() unknown interface address\n");
            return false;
        }
    }
    else
    {
        DMSG(0, "ProtoSocket::GetInterfaceName() invalid address\n");
        return false;
    }
}  // end ProtoSocket::GetInterfaceName(by addr)


#else  // UNIX

// Helper functions for group joins & leaves
bool ProtoSocket::GetInterfaceAddress(const char*         interfaceName,
                                      ProtoAddress::Type  addressType,
                                      ProtoAddress&       theAddress)
{
    struct ifreq req;
    strncpy(req.ifr_name, interfaceName, IFNAMSIZ);
    memset(&req.ifr_addr, 0, sizeof(struct sockaddr));
    int socketFd = -1;
    switch (addressType)
    {
        case ProtoAddress::IPv4:
            req.ifr_addr.sa_family = AF_INET;
            socketFd = socket(PF_INET, SOCK_DGRAM, 0);
            break;
#ifdef HAVE_IPV6
        case ProtoAddress::IPv6:
            req.ifr_addr.sa_family = AF_INET6;
            socketFd = socket(PF_INET6, SOCK_DGRAM, 0);
            break;
#endif // HAVE_IPV6
        default:
            req.ifr_addr.sa_family = AF_UNSPEC;
            socketFd = socket(PF_INET, SOCK_DGRAM, 0);
            break;
    }
    
    if (socketFd < 0)
    {
        DMSG(0, "ProtoSocket::GetInterfaceAddress() socket() error: %s\n", GetErrorString()); 
        return false;
    }   
    if (ioctl(socketFd, SIOCGIFADDR, &req) < 0)
    {
        DMSG(4, "ProtoSocket::GetInterfaceAddress() ioctl(SIOCGIFADDR) error: %s\n",
                GetErrorString()); 
        close(socketFd);
        // Perhaps "interfaceName" is an address string?
        ProtoAddress ifAddr;
        if (ifAddr.ResolveFromString(interfaceName))
        {
            char nameBuffer[IFNAMSIZ+1];
            if (GetInterfaceName(ifAddr, nameBuffer, IFNAMSIZ+1))
                return GetInterfaceAddress(nameBuffer, addressType, theAddress);
        }
        return false; 
    }
    close(socketFd);
    if (theAddress.SetSockAddr(req.ifr_addr))
    {
        return true;
    }
    else
    {
        DMSG(0, "ProtoSocket::GetInterfaceAddress() invalid address family\n");
        return false;
    }
}  // end ProtoSocket::GetInterfaceAddress()

unsigned int ProtoSocket::GetInterfaceIndex(const char* interfaceName)
{
    struct if_nameindex* ifdx = if_nameindex();
    unsigned int index = 0;
    struct if_nameindex* ptr = ifdx;
    do
    {
        if (!strncmp(ptr->if_name, interfaceName, IFNAMSIZ))
        {
            index = ptr->if_index;
            break;  
        }
        ptr++;
    } while (0 != ptr->if_index);
    if_freenameindex(ifdx);
    if (0 == index)
    {
        // Perhaps "interfaceName" was an address string;
        ProtoAddress ifAddr;
        if (ifAddr.ResolveFromString(interfaceName))
        {
            char nameBuffer[IFNAMSIZ+1]; 
            if (GetInterfaceName(ifAddr, nameBuffer, IFNAMSIZ+1))
                return GetInterfaceIndex(nameBuffer);
        }
    }
    return index;
}  // end ProtoSocket::GetInterfaceIndex()

bool ProtoSocket::GetInterfaceName(unsigned int index, char* buffer, unsigned int buflen)
{
    bool result = false;
    struct if_nameindex* ifdx = if_nameindex();
    struct if_nameindex* ptr = ifdx;
    do
    {
        if (index == ptr->if_index)
        {
            strncpy(buffer, ptr->if_name, buflen);
            result = true;
            break;
        }
        ptr++;
    } while (0 != ptr->if_index);
    if_freenameindex(ifdx);
    return result;
}  // end ProtoSocket::GetInterfaceName(by index)

bool ProtoSocket::GetInterfaceName(const ProtoAddress& ifAddr, char* buffer, unsigned int buflen)
{   
    // Go through list, looking for matching address
    bool result = false;
    struct if_nameindex* ifdx = if_nameindex();
    struct if_nameindex* ptr = ifdx;
    do
    {
        if (0 != ptr->if_index)
        {
            ProtoAddress theAddress;
            if (GetInterfaceAddress(ptr->if_name, ifAddr.GetType(), theAddress))
            {
                if (ifAddr.HostIsEqual(theAddress))
                {
                    strncpy(buffer, ptr->if_name, buflen);
                    result = true;
                    break;
                }   
            }    
        }
        ptr++;
    } while (0 != ptr->if_index);
    if_freenameindex(ifdx);
    return result;
}  // end  ProtoSocket::GetInterfaceName(by address)

#endif  // if/else WIN32/UNIX


bool ProtoSocket::LeaveGroup(const ProtoAddress& groupAddress,
                             const char*         interfaceName)
{    
    if (!IsOpen()) return true;
    int result;
#ifdef HAVE_IPV6
    if (ProtoAddress::IPv6 == groupAddress.GetType())
    {
        if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6*)&groupAddress.GetSockAddr())->sin6_addr))
        {
            struct ip_mreq mreq;
            mreq.imr_multiaddr.s_addr = 
            IN6_V4MAPPED_ADDR(&(((struct sockaddr_in6*)&groupAddress.GetSockAddr())->sin6_addr));
#ifdef UNIX
            if (interfaceName)
            {
                ProtoAddress interfaceAddress;
                if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
                {
                    mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
                }
                else
                {
                    DMSG(0, "ProtoSocket::LeaveGroup() invalid interface name\n");
                    return false;
                }
            }
            else
#endif // UNIX
                mreq.imr_interface.s_addr = INADDR_ANY; 
            result = setsockopt(handle, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
        }
        else
        {
            struct ipv6_mreq mreq;
            mreq.ipv6mr_multiaddr = ((struct sockaddr_in6*)&groupAddress.GetSockAddr())->sin6_addr;
#ifdef UNIX
            if (interfaceName)
                mreq.ipv6mr_interface = GetInterfaceIndex(interfaceName);
            else
#endif // UNIX
                mreq.ipv6mr_interface = 0;
            result = setsockopt(handle, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
        }
    }
    else
#endif //HAVE_IPV6
    {
        struct ip_mreq mreq;
        mreq.imr_multiaddr = ((struct sockaddr_in*)&groupAddress.GetSockAddr())->sin_addr;
//#ifdef UNIX
        if (interfaceName)
        {
            ProtoAddress interfaceAddress;
            if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, 
                                    interfaceAddress))
            {
                mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
            }
            else
            {
                DMSG(0, "ProtoSocket::JoinGroup() invalid interface name\n");
                return false;
            }
        }
        else
//#endif // UNIX
                mreq.imr_interface.s_addr = INADDR_ANY; 
        result = setsockopt(handle, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
    }
    if (result < 0)
    {
        DMSG(0, "ProtoSocket: Error leaving multicast group: %s\n", GetErrorString());
        return false;
    }
    else
    {
        return true;
    }
}  // end ProtoSocket::LeaveGroup() 

bool ProtoSocket::SetTTL(unsigned char ttl)
{   
#if defined(WIN32) && !defined(_WIN32_WCE)
    DWORD dwTTL = (DWORD)ttl; 
    DWORD dwBytesXfer;
    if (WSAIoctl(handle, SIO_MULTICAST_SCOPE, &dwTTL, sizeof(dwTTL),
                 NULL, 0, &dwBytesXfer, NULL, NULL))
#else
    int result;
#ifdef HAVE_IPV6
    if (ProtoAddress::IPv6 == address_type)
    {
        unsigned int hops = (unsigned int) ttl;
        result = setsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 
                            &hops, sizeof(hops));
    }
    else
#endif // HAVE_IPV6
    {
        result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL, 
                            (char*)&ttl, sizeof(ttl));
    }
    if (result < 0) 
#endif // if/else WIN32
    { 
        DMSG(0, "ProtoSocket: setsockopt(IP_MULTICAST_TTL) error: %s\n", GetErrorString()); 
        return false;
    }
    else
    {   
        return true;
    }
}  // end ProtoSocket::SetTTL()

bool ProtoSocket::SetTOS(unsigned char tos)
{ 
#ifdef NEVER_EVER // (for older LINUX?)                  
   int precedence = IPTOS_PREC(tos);
   if (setsockopt(handle, SOL_SOCKET, SO_PRIORITY, (char*)&precedence, sizeof(precedence)) < 0)           
   {     
       DMSG(0, "ProtoSocket: setsockopt(SO_PRIORITY) error: %s\n", GetErrorString()); 
       return false;
    }                          
   int tos_bits = IPTOS_TOS(tos);
   if (setsockopt(handle, SOL_IP, IP_TOS, (char*)&tos_bits, sizeof(tos_bits)) < 0) 
#else
    int theTOS = tos;
    int result;
#ifdef HAVE_IPV6
    if (ProtoAddress::IPv6 == address_type)
        result = setsockopt(handle, IPPROTO_IPV6, IP_TOS, (char*)&theTOS, sizeof(theTOS)); 
    else 
#endif // HAVE_IPV6  
        result =  setsockopt(handle, IPPROTO_IP, IP_TOS, (char*)&theTOS, sizeof(theTOS));    
#endif  // if/else NEVER_EVER
    if (result < 0)
    {               
        DMSG(0, "ProtoSocket: setsockopt(IP_TOS) error\n");
        return false;
    }
    return true; 
}  // end ProtoSocket::SetTOS()

bool ProtoSocket::SetBroadcast(bool broadcast)
{
#ifdef SO_BROADCAST
#ifdef WIN32
    BOOL state = broadcast ? TRUE : FALSE;
#else
    int state = broadcast ? 1 : 0;
#endif // if/else WIN32
    if (setsockopt(handle, SOL_SOCKET, SO_BROADCAST, (char*)&state, sizeof(state)) < 0)
    {
        DMSG(0, "ProtoSocket::SetBroadcast(): setsockopt(SO_BROADCAST) error: %s\n",
                GetErrorString());
        return false;
    }
#endif // SO_BROADCAST
    return true;
}  // end ProtoSocket::SetBroadcast()


bool ProtoSocket::SetLoopback(bool loopback)
{
    ASSERT(IsOpen());
#ifdef WIN32
    DWORD dwLoop = loopback ? TRUE: FALSE;
    DWORD dwBytesXfer;
    if (WSAIoctl(handle, SIO_MULTIPOINT_LOOPBACK , &dwLoop, sizeof(dwLoop),
                 NULL, 0, &dwBytesXfer, NULL, NULL))
#else 
    int result;
    char loop = loopback ? 1 : 0;
#ifdef HAVE_IPV6
    if (ProtoAddress::IPv6 == address_type)
        result = setsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 
                            (char*)&loop, sizeof(loop));
    else
#endif // HAVE_IPV6
    result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_LOOP, 
                        (char*)&loop, sizeof(loop));
    if (result < 0)
#endif // if/else WIN32
    {
        DMSG(0, "ProtoSocket: setsockopt(IP_MULTICAST_LOOP) error: %s\n", GetErrorString());
        return false;
    } 
    else
    {
        return true;
    }
}  // end ProtoSocket::SetLoopback() 

bool ProtoSocket::SetMulticastInterface(const char* interfaceName)
{    
    if (interfaceName)
    {
        int result;
#ifdef HAVE_IPV6
        if (ProtoAddress::IPv6 == address_type)  
        {
#ifdef WIN32
            // (TBD) figure out Win32 IPv6 multicast interface
            DMSG(0, "ProtoSocket::SetMulticastInterface() not yet supported for IPv6 on WIN32\n");
            return false;
#else
            unsigned int interfaceIndex = GetInterfaceIndex(interfaceName);
            result = setsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_IF,
                                (char*)&interfaceIndex, sizeof(interfaceIndex));
#endif // if/else WIN32
        }
        else 
#endif // HAVE_IPV6 
        {  
            struct in_addr localAddr;
            ProtoAddress interfaceAddress;
/*#ifdef WIN32 // don't think it needs to be different for windows systems anymore
            // Assume "interfaceName" is the IP address on WIN32
            if (interfaceAddress.ResolveFromString(interfaceName))
#else
            if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#endif // if/else WIN32 */
			if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
			{
                localAddr.s_addr = htonl(interfaceAddress.IPv4GetAddress());
            }
            else
            {
                DMSG(0, "ProtoSocket::SetMulticastInterface() invalid interface name\n");
                return false;
            }
            result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, (char*)&localAddr, 
                                sizeof(localAddr));
        }
        if (result < 0)
        { 
            DMSG(0, "ProtoSocket: setsockopt(IP_MULTICAST_IF) error: %s\n", 
                     GetErrorString());
            return false;
        }         
    }     
    return true;
}  // end ProtoSocket::SetMulticastInterface()

bool ProtoSocket::SetReuse(bool state)
{
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT)
    bool success = true;
#else
    return false;
#endif
    int reuse = state ? 1 : 0;
#ifdef SO_REUSEADDR
#ifdef WIN32
    BOOL breuse = (BOOL)reuse;
    if (setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, (char*)&breuse, sizeof(breuse)) < 0)
#else
    if (setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) < 0)
#endif // if/else WIN32
    {
        DMSG(0, "ProtoSocket: setsockopt(REUSE_ADDR) error: %s\n", GetErrorString());
        success = false;
    }
#endif // SO_REUSEADDR            
            
#ifdef SO_REUSEPORT  // not defined on Linux for some reason?
#ifdef WIN32
    BOOL breuse = (BOOL)reuse;
    if (setsockopt(handle, SOL_SOCKET, SO_REUSEPORT, (char*)&breuse, sizeof(i_reuse)) < 0)
#else
    if (setsockopt(handle, SOL_SOCKET, SO_REUSEPORT, (char*)&reuse, sizeof(reuse)) < 0)
#endif // if/else WIN32
    {
        DMSG(0, "ProtoSocket: setsockopt(SO_REUSEPORT) error: %s\n", GetErrorString());
        success = false;
    }
#endif // SO_REUSEPORT
    return success;
}  // end ProtoSocketError::SetReuse()

#ifdef HAVE_IPV6
bool ProtoSocket::HostIsIPv6Capable()
{
#ifdef WIN32
    if (!ProtoAddress::Win32Startup())
    {
        DMSG(0, "ProtoSocket::HostIsIPv6Capable() WSAStartup() error: %s\n", GetErrorString());
        return false;
    }
    SOCKET handle = socket(AF_INET6, SOCK_DGRAM, 0);
    closesocket(handle);
    ProtoAddress::Win32Cleanup();
    if(INVALID_SOCKET == handle)
        return false;
    else
        return true;
#else
#ifdef RES_USE_INET6
    if (0 == (_res.options & RES_INIT)) res_init();
    if (0 == (_res.options & RES_USE_INET6))
        return false;
    else
#endif // RES_USE_INET6
        return true;
#endif  // if/else WIN32
}  // end ProtoSocket::HostIsIPv6Capable()

bool ProtoSocket::SetHostIPv6Capable()
{
#ifdef WIN32
    if (!ProtoAddress::Win32Startup())
    {
        DMSG(0, "ProtoSocket::SetHostIPv6Capable() WSAStartup() error: %s\n", GetErrorString());
        return false;
    }
    SOCKET handle = socket(AF_INET6, SOCK_DGRAM, 0);
    closesocket(handle);
    ProtoAddress::Win32Cleanup();
    if(INVALID_SOCKET == handle)
        return false;
    else
        return true;
#else
#ifdef RES_USE_INET6
    if (0 == (_res.options & RES_INIT)) 
        res_init();
    if (0 == (_res.options & RES_USE_INET6)) 
        _res.options |= RES_USE_INET6; 
    if (0 == (_res.options & RES_USE_INET6))
        return false;
    else
#endif // RES_USE_INET6
        return true;
#endif  // if/else WIN32
}  // end ProtoSocket::SetIPv6Capable()
#endif // HAVE_IPV6


bool ProtoSocket::SetTxBufferSize(unsigned int bufferSize)
{
   if (!IsOpen())
   {
        DMSG(0, "ProtoSocket::SetTxBufferSize() error: socket closed\n");     
        return false;
   }
   unsigned int oldBufferSize = GetTxBufferSize();
   if (setsockopt(handle, SOL_SOCKET, SO_SNDBUF, (char*)&bufferSize, sizeof(bufferSize)) < 0) 
   {
        setsockopt(handle, SOL_SOCKET, SO_SNDBUF, (char*)&oldBufferSize, sizeof(oldBufferSize));
        DMSG(0, "ProtoSocket::SetTxBufferSize() setsockopt(SO_SNDBUF) error: %s\n",
                GetErrorString());
        return false;
   }
   return true;
}  // end ProtoSocket::SetTxBufferSize()

unsigned int ProtoSocket::GetTxBufferSize()
{
    if (!IsOpen()) return 0;
    unsigned int txBufferSize = 0;
    socklen_t len = sizeof(txBufferSize);
    if (getsockopt(handle, SOL_SOCKET, SO_SNDBUF, (char*)&txBufferSize, &len) < 0) 
    {
        DMSG(0, "ProtoSocket::GetTxBufferSize() getsockopt(SO_SNDBUF) error: %s\n",
                 GetErrorString());
	    return 0; 
    }
    return ((unsigned int)txBufferSize);
}  // end ProtoSocket::GetTxBufferSize()

bool ProtoSocket::SetRxBufferSize(unsigned int bufferSize)
{
   if (!IsOpen())
   {
        DMSG(0, "ProtoSocket::SetRxBufferSize() error: socket closed\n");    
        return false;
   }
   unsigned int oldBufferSize = GetTxBufferSize();
   if (setsockopt(handle, SOL_SOCKET, SO_RCVBUF, (char*)&bufferSize, sizeof(bufferSize)) < 0) 
   {
        setsockopt(handle, SOL_SOCKET, SO_RCVBUF, (char*)&oldBufferSize, sizeof(oldBufferSize));
        DMSG(0, "ProtoSocket::SetRxBufferSize() setsockopt(SO_RCVBUF) error: %s\n",
                GetErrorString());
        return false;
   }
   return true;
}  // end ProtoSocket::SetRxBufferSize()

unsigned int ProtoSocket::GetRxBufferSize()
{
    if (!IsOpen()) return 0;
    unsigned int rxBufferSize = 0;
    socklen_t len = sizeof(rxBufferSize);
    if (getsockopt(handle, SOL_SOCKET, SO_RCVBUF, (char*)&rxBufferSize, &len) < 0) 
    {
        DMSG(0, "ProtoSocket::GetRxBufferSize() getsockopt(SO_RCVBUF) error: %s\n",
                GetErrorString());
	    return 0; 
    }
    return ((unsigned int)rxBufferSize);
}  // end ProtoSocket::GetRxBufferSize()

#ifdef HAVE_IPV6
bool ProtoSocket::SetFlowLabel(UINT32 label)  
{
    int result = 0; 
#ifdef SOL_IPV6
    if (label && !flow_label)
    {
       int on = 1;
       result = setsockopt(handle, SOL_IPV6, IPV6_FLOWINFO_SEND, (void*)&on, sizeof(on));
    }
    else if (!label && flow_label)
    {
        int off = 0;
        result = setsockopt(handle, SOL_IPV6, IPV6_FLOWINFO_SEND, (void*)&off, sizeof(off));
    }
#endif  //SOL_IPV6
    if (0 == result)
    {
        flow_label = htonl(label);
        return true;
    }
    else
    {
        DMSG(0, "ProtoSocket::SetFlowLabel() setsockopt(SOL_IPV6) error\n");
        return false;
    }    
}  // end ProtoSocket::SetFlowLabel()
#endif  //HAVE_IPV6


ProtoSocket::List::List()
 : head(NULL)
{
}

ProtoSocket::List::~List()
{
    Destroy();
}

void ProtoSocket::List::Destroy()
{
    Item* next = head;
    while (next)
    {
        Item* current = next;
        next = next->GetNext();
        delete current->GetSocket();
        delete current;
    }   
}  // end ProtoSocket::List::Destroy()

bool ProtoSocket::List::AddSocket(ProtoSocket& theSocket)
{
    Item* item = new Item(&theSocket);
    if (item)
    {
        item->SetNext(head);
        head = item;
        return true;
    }
    else
    {
        DMSG(0, "ProtoSocket::List::AddSocket() new Item error: %s\n", GetErrorString());
        return false;
    }
}  // end ProtoSocket::List::AddSocket()

void ProtoSocket::List::RemoveSocket(ProtoSocket& theSocket)
{
    Item* item = head;
    while (item)
    {
        if (&theSocket == item->GetSocket())
        {
            Item* prev = item->GetPrev();
            Item* next = item->GetNext();
            if (prev) 
                prev->SetNext(next);
            else
                head = next;
            if (next) next->SetPrev(prev);
            delete item;
            break;
        }
        item = item->GetNext();   
    }
}  // end ProtoSocket::List::AddSocket()

ProtoSocket::List::Item::Item(ProtoSocket* theSocket)
 : socket(theSocket), prev(NULL), next(NULL)
{
}

ProtoSocket::List::Iterator::Iterator(const ProtoSocket::List& theList)
 : list(theList), next(theList.head)
{
}
