
/*---------------------------------------------------------------------------*/
/* client_sockets.cpp                                                        */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include "platform/platform.h"

#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#ifdef __ANDROID__
#include <fcntl.h>
#include <ctype.h>
#include <sys/endian.h>
#include <asm/ioctls.h>
#include <linux/if.h>
#include <jni.h>
#include "platform/platform.h"
#include "client/android/android_jni/android_jni.h"

JNIEnv *get_jni_env(void);

extern jobject phone_android_context;

#else
#ifdef _IOS_
#include <netdb.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <sys/ioctl.h>
#include <ifaddrs.h>
#include <net/ethernet.h>
#include <sys/types.h>
#endif
#endif

#include "common/os/iomux.h"
#include "client/linux/os-cpp/iomux.h"
#include "common/interface/socket.h"
#include "common/linux/udp/udpsocket.h"
#include "common/linux/tcp/tcpsocket.h"
#include "common/linux/uds/udsocket.h"
#include "client_sockets.h"

#define SOCKET_ACTIVE_TIMEOUT  3

/*---------------------------------------------------------------------------*/
/* class ClientSocketProvider	                                             */
/*---------------------------------------------------------------------------*/

class ClientSockets : public UClientSockets
{
    class IClientSockets * sockets;
    int providerCount;
    int rtpSocketCount;
    int multicastReceiveCount;
    int socketActiveCounter;

    void ClientSocketsTimeout() override;
public:
    ClientSockets();
    ~ClientSockets();

    void RegisterProvider(IIoMux * const iomux);
    void UnregisterProvider();
    void SocketActive();
    void RegisterRtpSocketActive();
    void UnregisterRtpSocketActive();
    void RegisterMulticastReceiveActive();
    void UnregisterMulticastReceiveActive();
};

ClientSockets::ClientSockets()
{
    providerCount = 0;
    rtpSocketCount = 0;
    multicastReceiveCount = 0;
    socketActiveCounter = 0;
}

ClientSockets::~ClientSockets()
{
}

void ClientSockets::ClientSocketsTimeout()
{
    if(socketActiveCounter != 0) {
        socketActiveCounter--;
        if(socketActiveCounter != 0) {
            sockets->StartTimer(1000);
        }
        else {
            if(rtpSocketCount == 0) {
                sockets->SocketsActive(false);
            }
        }
    }
}

void ClientSockets::RegisterProvider(IIoMux * const iomux)
{
    if (providerCount == 0) {
        sockets = IClientSockets::Create(iomux, this);
    }
    providerCount++;
}

void ClientSockets::UnregisterProvider()
{
    providerCount--;
    if (providerCount == 0) {
        if(socketActiveCounter != 0) {
            socketActiveCounter = 0;
            sockets->CancelTimer();
            sockets->SocketsActive(false);
        }
        delete sockets;
    }
}

void ClientSockets::SocketActive()
{
    if(socketActiveCounter < SOCKET_ACTIVE_TIMEOUT) {
        if(socketActiveCounter == 0) {
            if(rtpSocketCount == 0) {
                sockets->SocketsActive(true);
            }
            sockets->StartTimer(1000);
        }
        socketActiveCounter = SOCKET_ACTIVE_TIMEOUT;
    }
}

void ClientSockets::RegisterRtpSocketActive()
{
    if(rtpSocketCount == 0) {
        sockets->RtpSocketsActive(true);
        if(socketActiveCounter == 0) {
            sockets->SocketsActive(true);
        }
    }
    rtpSocketCount++;
}

void ClientSockets::UnregisterRtpSocketActive()
{
    rtpSocketCount--;
    if(rtpSocketCount == 0) {
        sockets->RtpSocketsActive(false);
        if(socketActiveCounter == 0) {
            sockets->SocketsActive(false);
        }
    }
}

void ClientSockets::RegisterMulticastReceiveActive()
{
    if(multicastReceiveCount == 0) {
        sockets->MulticastReceiveActive(true);
    }
    multicastReceiveCount++;
}

void ClientSockets::UnregisterMulticastReceiveActive()
{
    multicastReceiveCount--;
    if (multicastReceiveCount == 0) {
        sockets->MulticastReceiveActive(false);
    }
}

static class ClientSockets clientSockets;

/*---------------------------------------------------------------------------*/
/* class ClientUDPSocketProvider	                                         */
/*---------------------------------------------------------------------------*/

class UDPSocket * ClientUDPSocketProvider::NewSocket(IIoMux * const iomux, USocket * const user, class IInstanceLog * const log)
{
    if (!registered) {
        registered = true;
        clientSockets.RegisterProvider(iomux);
    }
    ClientUDPSocket * tmp = new ClientUDPSocket(iomux, user, log, this);
    sockets.push_back(tmp);
    return tmp;
}

void ClientUDPSocketProvider::CloseListen()
{
    ClientUDPSocket *s;
    if(!sockets.empty()) {
        for(s = sockets.front(); s; s = s->goNext()) {
            s->CloseListen();
        }
    }
}

void ClientUDPSocketProvider::RestartListen()
{
    ClientUDPSocket *s;
    if(!sockets.empty()) {
        for(s = sockets.front(); s; s = s->goNext()) {
            s->RestartListen();
        }
    }
}

ClientUDPSocketProvider::ClientUDPSocketProvider()
{
    registered = false;
}

ClientUDPSocketProvider::~ClientUDPSocketProvider()
{
    if (registered) {
        clientSockets.UnregisterProvider();
    }
}

ClientUDPSocketProvider * ClientCreateUDPSocketProvider()
{
    return new ClientUDPSocketProvider;
}

/*---------------------------------------------------------------------------*/
/* class ClientUDPSocket                                                     */
/*---------------------------------------------------------------------------*/

ClientUDPSocket::ClientUDPSocket(IIoMux * iomux, USocket * const user, class IInstanceLog * const log, ClientUDPSocketProvider * provider) :
    UDPSocket(iomux, user, log, provider)
{
    rtpSocketActive = false;
    multicastReceiveActive = false;
}

ClientUDPSocket::~ClientUDPSocket()
{
    if(rtpSocketActive) {
        clientSockets.UnregisterRtpSocketActive();
        rtpSocketActive = false;
    }
    if (multicastReceiveActive) {
        clientSockets.UnregisterMulticastReceiveActive();
        multicastReceiveActive = false;
    }
}

void ClientUDPSocket::UserSocketBindResult(const char * localAddr, word localPort)
{
    if (localPort != 0) {
        if(!rtpSocketActive/* && (type == SOCKET_RTP)*/) {
            rtpSocketActive = true;
            clientSockets.RegisterRtpSocketActive();
        }
        if (!multicastReceiveActive) {
            multicastReceiveActive = useIPv6 ?
                ((((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[0] == 0xff) ||
                 ((((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[0] == 0x00) &&
                  (((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[1] == 0x00) &&
                  (((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[2] == 0x00) &&
                  (((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[3] == 0x00) &&
                  (((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[4] == 0x00) &&
                  (((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[5] == 0x00) &&
                  (((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[6] == 0x00) &&
                  (((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[7] == 0x00) &&
                  (((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[8] == 0x00) &&
                  (((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[9] == 0x00) &&
                  (((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[10] == 0xff) &&
                  (((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[11] == 0xff) &&
                  ((((sockaddr_in6*)&localAddr)->sin6_addr.s6_addr[12] & 0xf0) == 0xe0))) :
                ((((byte *)(&(((sockaddr_in*)&localAddr)->sin_addr)))[0] & 0xf0) == 0xe0);
            if(multicastReceiveActive) {
                clientSockets.RegisterMulticastReceiveActive();
            }
        }
    }
    UDPSocket::UserSocketBindResult(localAddr, localPort);
}

void ClientUDPSocket::UserSocketShutdown(shutdownreason_t reason)
{
    if(rtpSocketActive) {
        clientSockets.UnregisterRtpSocketActive();
        rtpSocketActive = false;
    }
    if (multicastReceiveActive) {
        clientSockets.UnregisterMulticastReceiveActive();
        multicastReceiveActive = false;
    }
    UDPSocket::UserSocketShutdown(reason);
}

void ClientUDPSocket::CloseListen()
{
    if ((socketStatus == SST_BIND_START) || (socketStatus == SST_BIND_DONE) || (socketStatus == SST_CONNECTION_INITIATED) || (socketStatus == SST_CONNECTED)) {
        shutdown(FDIIoMux, SHUT_RDWR);
        iomux->FDClose(this);
    }
}

void ClientUDPSocket::RestartListen()
{
    if ((socketStatus == SST_BIND_START) || (socketStatus == SST_BIND_DONE) || (socketStatus == SST_CONNECTION_INITIATED) || (socketStatus == SST_CONNECTED)) {
        int restoreStatus = socketStatus;
        socketStatus = SST_CLOSED;
        OpenSocket(useIPv6);
        if (useIPv6) {
            int res = bind(FDIIoMux, (struct sockaddr *)(&localAddr), sizeof(sockaddr_in6));
            if (res != 0) {
                log->Log(LOG_UDP, "ClientUDPSocket(%08x,%08x)::BindIPv6 bind() failed (%u : %s)", (UDPSocket *)this, user, errno, strerror(errno));
            }
        }
        else {
            int res = bind(FDIIoMux, (struct sockaddr *)(&localAddr), sizeof(sockaddr_in));
            if (res != 0) {
                log->Log(LOG_UDP, "ClientUDPSocket(%08x,%08x)::BindIPv4 bind() failed (%u : %s)", (UDPSocket *)this, user, errno, strerror(errno));
            }
        }
        iomux->FDAdd(FDIIoMux, this, false);
        if ((restoreStatus == SST_CONNECTION_INITIATED) || (restoreStatus == SST_CONNECTED)) {
            if (useIPv6) {
                int res = connect(FDIIoMux, (struct sockaddr *)(&connectedAddr), sizeof(sockaddr_in6));
                if (res < 0 && errno != EINPROGRESS) {
                    log->Log(LOG_UDP, "ClientUDPSocket(%08x,%08x)::ConnectIPv6 connect() failed (%u : %s)", (UDPSocket *)this, user, errno, strerror(errno));
                }
            }
            else {
                int res = connect(FDIIoMux, (struct sockaddr *)(&connectedAddr), sizeof(sockaddr_in));
                if (res < 0 && errno != EINPROGRESS) {
                    log->Log(LOG_UDP, "ClientUDPSocket(%08x,%08x)::ConnectIPv4 connect() failed (%u : %s)", (UDPSocket *)this, user, errno, strerror(errno));
                }
            }
        }
        socketStatus = restoreStatus;
    }
}

/*---------------------------------------------------------------------------*/
/* class ClientTCPSocketProvider                                             */
/*---------------------------------------------------------------------------*/

class TCPSocket * ClientTCPSocketProvider::NewSocket(IIoMux * const iomux, USocket * const user, class IInstanceLog * const log)
{
    if (!registered) {
        registered = true;
        clientSockets.RegisterProvider(iomux);
    }
    ClientTCPSocket * tmp = new ClientTCPSocket(iomux, user, log, this);
    sockets.push_back(tmp);
    return tmp;
}

void ClientTCPSocketProvider::CloseListen()
{
    ClientTCPSocket *s;
    if(!sockets.empty()) {
        for(s = sockets.front(); s; s = s->goNext()) {
            s->CloseListen();
        }
    }
}

void ClientTCPSocketProvider::RestartListen()
{
    ClientTCPSocket *s;
    if(!sockets.empty()) {
        for(s = sockets.front(); s; s = s->goNext()) {
            s->RestartListen();
        }
    }
}

void ClientTCPSocketProvider::CloseSockets()
{
    ClientTCPSocket *s, *next;
    if(!sockets.empty()) {
        for(s = sockets.front(); s; s = next) {
            next = s->goNext();
            s->CloseSockets();
        }
    }
}

void ClientTCPSocketProvider::CheckInterface()
{
#ifdef __ANDROID__
    ClientTCPSocket *s, *next;
    if(!sockets.empty()) {
        for(s = sockets.front(); s; s = next) {
            next = s->goNext();
            s->CheckInterface();
        }
    }
#else
#ifdef _IOS_
    ClientTCPSocket *s, *next;
    if(!getifaddrs (&ifap)) {
        struct ifaddrs *ifa = ifap;
        while (ifa
         && (!ifa->ifa_addr
          || (ifa->ifa_addr->sa_family != AF_INET))) {
            ifa = ifa->ifa_next;
        }
        ipv4Interface = (ifa != NULL);
        ifa = ifap;
        while (ifa
         && (!ifa->ifa_addr
          || (ifa->ifa_addr->sa_family != AF_INET6))) {
            ifa = ifa->ifa_next;
        }
        ipv6Interface = (ifa != NULL);
        if (!ipv4Interface && !ipv6Interface) {
            ipv4Interface = true;
            ipv6Interface = true;
        }
        if(!sockets.empty()) {
            for(s = sockets.front(); s; s = next) {
                next = s->goNext();
                s->CheckInterface();
            }
        }
    }
    else {
        ipv4Interface = true;
        ipv6Interface = true;
    }
    freeifaddrs(ifap);
    ifap = NULL;
#endif
#endif
}

ClientTCPSocketProvider::ClientTCPSocketProvider()
{
    registered = false;
#ifdef _IOS_
    ifap = NULL;
    CheckInterface();
#endif
}

ClientTCPSocketProvider::~ClientTCPSocketProvider()
{
    if (registered) {
        clientSockets.UnregisterProvider();
    }
}

ClientTCPSocketProvider * ClientCreateTCPSocketProvider()
{
    return new ClientTCPSocketProvider;
}

/*---------------------------------------------------------------------------*/
/* class ClientTCPSocket                                                     */
/*---------------------------------------------------------------------------*/

ClientTCPSocket::ClientTCPSocket(IIoMux * iomux, USocket * const user, class IInstanceLog * const log, ClientTCPSocketProvider * provider) :
    TCPSocket(iomux, user, log, provider)
{
}

ClientTCPSocket::~ClientTCPSocket()
{
}

void ClientTCPSocket::FDNotify(bool notifyRX, bool notifyTX)
{
    if (notifyRX) {
        clientSockets.SocketActive();
    }
    (static_cast<IIoMuxEx *>(iomux))->FDNotify(this, notifyRX, notifyTX);
}

#ifdef _IOS_
void ClientTCPSocket::Connect(const char *address, const char * serverName)
{
    TCPSocket::Connect(address, serverName);
    CheckInterface();
}
#endif

void ClientTCPSocket::CloseListen()
{
    if ((socketStatus == SST_BIND_START) || (socketStatus == SST_BIND_DONE) || (socketStatus == SST_LISTEN)) {
        shutdown(FDIIoMux, SHUT_RDWR);
        iomux->FDClose(this);
    }
}

void ClientTCPSocket::RestartListen()
{
    if ((socketStatus == SST_BIND_START) || (socketStatus == SST_BIND_DONE) || (socketStatus == SST_LISTEN)) {
        int res;
        int restoreStatus = socketStatus;
        socketStatus = SST_CLOSED;
        OpenSocket(useIPv6);
        // It will be good to allow the socket to reuse the address.
        int r = 1;
        if (setsockopt(FDIIoMux, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(r)) != 0) {
            log->Log(LOG_TCP, "ClientTCPSocket(%08x,%08x)::Bind setsockopt() failed (%u : %s)", (TCPSocket *)this, user, errno, strerror(errno));
        }
#ifdef SO_REUSEPORT
        setsockopt(FDIIoMux, SOL_SOCKET, SO_REUSEPORT, &r, sizeof(r));
#endif
        if (useIPv6) {
            res = bind(FDIIoMux, (struct sockaddr *)(&localAddr), sizeof(sockaddr_in6));
            if (res != 0) {
                log->Log(LOG_TCP, "ClientTCPSocket(%08x,%08x)::BindIPv6 bind() failed (%u : %s)", (TCPSocket *)this, user, errno, strerror(errno));
            }
        }
        else {
            res = bind(FDIIoMux, (struct sockaddr *)(&localAddr), sizeof(sockaddr_in));
            if (res != 0) {
                log->Log(LOG_TCP, "ClientTCPSocket(%08x,%08x)::BindIPv4 bind() failed (%u : %s)", (TCPSocket *)this, user, errno, strerror(errno));
            }
        }
        if ((restoreStatus == SST_LISTEN) && (res == 0)) {
            if (listen(FDIIoMux, SOMAXCONN) != 0) {
                log->Log(LOG_TCP, "ClientTCPSocket(%08x,%08x)::Listen listen() failed (%u : %s)", (TCPSocket *)this, user, errno, strerror(errno));
            }
            iomux->FDAdd(FDIIoMux, this, true);
        }
        socketStatus = restoreStatus;
    }
}

void ClientTCPSocket::CloseSockets()
{
    if (close) {
        if (!isServer && (socketStatus == SST_CONNECTED)) {
            DoShutdown();
        }
    }
}

void ClientTCPSocket::CheckInterface()
{
#ifdef __ANDROID__
/*
    if (!isServer && (socketStatus == SST_CONNECTED)) {
        bool found = false;
        if(useIPv6) {
            JNIEnv *phone_android_jni_env;
            jobject network_interfaces_ref, network_interface_ref, inet_addresses_ref, inet_address_ref;
            jbyteArray address;
            jsize address_length;
            jbyte *address_buf;
            sockaddr_in6 addr;
            socklen_t l = sizeof(sockaddr_in6);
            getsockname(FDIIoMux, (sockaddr *)&addr, &l);
            phone_android_jni_env = get_jni_env();
            network_interfaces_ref = NetworkInterface_getNetworkInterfaces();
            while (Enumeration_hasMoreElements(network_interfaces_ref)) {
                network_interface_ref = Enumeration_nextElement(network_interfaces_ref);
                inet_addresses_ref = NetworkInterface_getInetAddresses(network_interface_ref);
                if (inet_addresses_ref) {
                    while (Enumeration_hasMoreElements(inet_addresses_ref)) {
                        inet_address_ref = Enumeration_nextElement(inet_addresses_ref);
                        address = InetAddress_getAddress(inet_address_ref);
                        address_length = JNI_GetArrayLength(address);
                        if (address_length == sizeof(struct in6_addr)) {
                            address_buf = JNI_GetByteArrayElements(address, NULL);
                            if (memcmp(address_buf, addr.sin6_addr.s6_addr, sizeof(struct in6_addr)) == 0) {
                                found = true;
                            }
                            JNI_ReleaseByteArrayElements(address, address_buf, 0);
                        }
                        JNI_DeleteLocalRef(address);
                        JNI_DeleteLocalRef(inet_address_ref);
                    }
                    JNI_DeleteLocalRef(inet_addresses_ref);
                }
                JNI_DeleteLocalRef(network_interface_ref);
            }
            JNI_DeleteLocalRef(network_interfaces_ref);
        }
        else {
            struct ifconf if_conf;
            struct ifreq if_req_buffer[64];
            struct ifreq *ip;
            int res;
            sockaddr_in addr;
            socklen_t l = sizeof(sockaddr_in);
            getsockname(FDIIoMux, (sockaddr *)&addr, &l);
            if_conf.ifc_len = sizeof(if_req_buffer);
            if_conf.ifc_req = if_req_buffer;
            res = ioctl(FDIIoMux, SIOCGIFCONF, (void *)(&if_conf));
            if(res >= 0) {
                ip = if_conf.ifc_req;
                while ((ip < (struct ifreq *)(((byte *)(if_conf.ifc_req)) + if_conf.ifc_len))
                 && ((ip->ifr_addr.sa_family != AF_INET)
                  || (((sockaddr_in*)&ip->ifr_addr)->sin_addr.s_addr != addr.sin_addr.s_addr))) {
                    ip++;
                }
                found = (ip < (struct ifreq *)(((byte *)(if_conf.ifc_req)) + if_conf.ifc_len));
            }
        }
        if (!found) {
            char ipaddr[INET6_ADDRSTRLEN];
            word port;
            GetLocalAddress(ipaddr, INET6_ADDRSTRLEN, &port);
//            log->Log(LOG_TCP, "ClientTCPSocket(%08x,%08x)::CheckInterface %s gone, so shutdown", (TCPSocket *)this, user, ipaddr);
            debug->printf("ClientTCPSocket(%08x,%08x)::CheckInterface %s gone, so shutdown", (TCPSocket *)this, user, ipaddr);
            DoShutdown();
        }
    }
*/
#else
#ifdef _IOS_
    if (!isServer) {
        if (socketStatus == SST_CONNECTED) {
            struct ifaddrs *ifap = NULL;
            struct ifaddrs *ifa = ((ClientTCPSocketProvider *)provider)->ifap ? ((ClientTCPSocketProvider *)provider)->ifap : (getifaddrs (&ifap) ? NULL : ifap);
            if(useIPv6) {
                sockaddr_in6 addr;
                socklen_t l = sizeof(sockaddr_in6);
                getsockname(FDIIoMux, (sockaddr *)&addr, &l);
                while (ifa
                 && (!ifa->ifa_addr
                  || (ifa->ifa_addr->sa_family != AF_INET6)
#ifdef _IOS_
                    /* Patch IP6_ADDRESS_TYPE_SITE_LOCAL since ios clears the second word of the address and returns it as sin6_scope_id */
                  || (((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[0] != addr.sin6_addr.s6_addr[0])
                  || (((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[1] != addr.sin6_addr.s6_addr[1])
                  || ((((((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[0] == 0xfe) && ((((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[1] & 0xc0) == 0xc0) &&
                      (((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[2] == 0) && (((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[3] == 0)) ? (byte)(((sockaddr_in6*)ifa->ifa_addr)->sin6_scope_id >> 8) : ((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[2]) != addr.sin6_addr.s6_addr[2])
                  || ((((((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[0] == 0xfe) && ((((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[1] & 0xc0) == 0xc0) &&
                      (((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[2] == 0) && (((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[3] == 0)) ? (byte)(((sockaddr_in6*)ifa->ifa_addr)->sin6_scope_id) : ((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[3]) != addr.sin6_addr.s6_addr[3])
                  || (memcmp(&((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr[4], &addr.sin6_addr.s6_addr[4], sizeof(struct in6_addr) - 4) != 0)
#else
                  || (memcmp(((sockaddr_in6*)ifa->ifa_addr)->sin6_addr.s6_addr, addr.sin6_addr.s6_addr, sizeof(struct in6_addr)) != 0)
#endif
                  )) {
                    ifa = ifa->ifa_next;
                }
            }
            else {
                sockaddr_in addr;
                socklen_t l = sizeof(sockaddr_in);
                getsockname(FDIIoMux, (sockaddr *)&addr, &l);
                while (ifa
                 && (!ifa->ifa_addr
                  || (ifa->ifa_addr->sa_family != AF_INET)
                  || (((sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr != addr.sin_addr.s_addr))) {
                    ifa = ifa->ifa_next;
                }
            }
            if (!((ClientTCPSocketProvider *)provider)->ifap) {
                freeifaddrs(ifap);
            }
            if (!ifa) {
                char ipaddr[INET6_ADDRSTRLEN];
                word port;
                GetLocalAddress(ipaddr, INET6_ADDRSTRLEN, &port);
//                log->Log(LOG_TCP, "ClientTCPSocket(%08x,%08x)::CheckInterface %s gone, so shutdown", (TCPSocket *)this, user, ipaddr);
                debug->printf("ClientTCPSocket(%08x,%08x)::CheckInterface %s gone, so shutdown", (TCPSocket *)this, user, ipaddr);
                DoShutdown();
            }
        }
        else if (socketStatus == SST_CONNECTION_INITIATED) {
            bool ipv4Interface = ((ClientTCPSocketProvider *)provider)->ipv4Interface;
            bool ipv6Interface = ((ClientTCPSocketProvider *)provider)->ipv6Interface;
            if (!ipv4Interface && !ipv6Interface) {
                struct ifaddrs *ifap;
                if(!getifaddrs (&ifap)) {
                    struct ifaddrs *ifa = ifap;
                    while (ifa
                     && (!ifa->ifa_addr
                      || (ifa->ifa_addr->sa_family != AF_INET))) {
                        ifa = ifa->ifa_next;
                    }
                    ipv4Interface = (ifa != NULL);
                    ifa = ifap;
                    while (ifa
                     && (!ifa->ifa_addr
                      || (ifa->ifa_addr->sa_family != AF_INET6))) {
                        ifa = ifa->ifa_next;
                    }
                    ipv6Interface = (ifa != NULL);
                    if (!ipv4Interface && !ipv6Interface) {
                        ipv4Interface = true;
                        ipv6Interface = true;
                    }
                }
                else {
                    ipv4Interface = true;
                    ipv6Interface = true;
                }
                freeifaddrs(ifap);
                ifap = NULL;
            }
            if ((useIPv6 && !ipv6Interface) || (!useIPv6 && !ipv4Interface)) {
//                log->Log(LOG_TCP, "ClientTCPSocket(%08x,%08x)::CheckInterface \"%s\" no interface, so shutdown", (TCPSocket *)this, user, remoteAddress);
                debug->printf("ClientTCPSocket(%08x,%08x)::CheckInterface \"%s\" no interface, so shutdown", (TCPSocket *)this, user, remoteAddress);
                DoShutdown();
            }
        }
    }
#endif
#endif
}

/*---------------------------------------------------------------------------*/
/* class ClientUDSocketProvider                                              */
/*---------------------------------------------------------------------------*/

class UDSocket * ClientUDSocketProvider::NewSocket(IIoMux * const iomux, USocket * const user, class IInstanceLog * const log)
{
    return new ClientUDSocket(iomux, user, log, this);
}

ClientUDSocketProvider * ClientCreateLocalSocketProvider()
{
    return new ClientUDSocketProvider;
}

/*---------------------------------------------------------------------------*/
/* class ClientUDSocket                                                     */
/*---------------------------------------------------------------------------*/

ClientUDSocket::ClientUDSocket(IIoMux * iomux, USocket * const user, class IInstanceLog * const log, ClientUDSocketProvider * provider) :
    UDSocket(iomux, user, log, provider)
{
}

void ClientUDSocket::FDNotify(bool notifyRX, bool notifyTX)
{
    (static_cast<IIoMuxEx *>(iomux))->FDNotify(this, notifyRX, notifyTX);
}

/*---------------------------------------------------------------------------*/
