/*
 * Copyright (C) 2001-2018 Jacek Sieka, arnetheduck on gmail point com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "stdinc.h"
#include "Socket.h"
#include <signal.h>
#include "ConnectivityManager.h"
#include "format.h"
#include "SettingsManager.h"
#include "TimerManager.h"

/// @todo remove when MinGW has this
#ifdef __MINGW32__
#ifndef EADDRNOTAVAIL
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
#endif
#endif

#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG 0
#endif

#ifndef IPV6_V6ONLY
#ifdef _WIN32 // Mingw seems to lack this...
#define IPV6_V6ONLY 27
#endif
#endif

namespace dcpp {
	
bool Socket::isV6Valid() const noexcept {
	return sock6.valid();
}

Socket::Stats Socket::stats;
	 
namespace {

#ifdef _WIN32

template<typename F>
inline auto check(F f, bool blockOk = false) -> decltype(f()) {
	for(;;) {
		auto ret = f();
		if(ret != static_cast<decltype(ret)>(SOCKET_ERROR)) {
			return ret;
		}

		auto error = Socket::getLastError();
		if(blockOk && error == WSAEWOULDBLOCK) {
			return static_cast<decltype(ret)>(-1);
		}

		if(error != EINTR) {
			throw SocketException(error);
		}
	}
}

inline void setBlocking2(socket_t sock, bool block) noexcept {
	u_long b = block ? 0 : 1;
	ioctlsocket(sock, FIONBIO, &b);
}

#else

template<typename F>
inline auto check(F f, bool blockOk = false) -> decltype(f()) {
	for(;;) {
		auto ret = f();
		if(ret != -1) {
			return ret;
		}

		auto error = Socket::getLastError();
		if(blockOk && (error == EWOULDBLOCK || error == ENOBUFS || error == EINPROGRESS || error == EAGAIN)) {
			return -1;
		}

		if(error != EINTR) {
			throw SocketException(error);
		}
	}
}

inline void setBlocking2(socket_t sock, bool block) noexcept {
	int flags = fcntl(sock, F_GETFL, 0);
	if(block) {
		fcntl(sock, F_SETFL, flags & (~O_NONBLOCK));
	} else {
		fcntl(sock, F_SETFL, flags | O_NONBLOCK);
	}
}

#endif

inline int getSocketOptInt2(socket_t sock, int option) {
	int val;
	socklen_t len = sizeof(val);
	check([&] { return ::getsockopt(sock, SOL_SOCKET, option, (char*)&val, &len); });
	return val;
}

inline int setSocketOpt2(socket_t sock, int level, int option, int val) {
	int len = sizeof(val);
	return ::setsockopt(sock, level, option, (char*)&val, len);
}

inline bool isConnected(socket_t sock) {
	fd_set wfd;
	struct timeval tv = { 0,0 };

	FD_ZERO(&wfd);
	FD_SET(sock, &wfd);

    if(::select(sock + 1, NULL, &wfd, NULL, &tv) == 1) {
        if (getSocketOptInt2(sock, SO_ERROR) == 0) {
            return true;
        }
    }

	return false;
}

inline int readable(socket_t sock0, socket_t sock1) {
	fd_set rfd;
	struct timeval tv = { 0,0 };

	FD_ZERO(&rfd);
	if(sock0 != -1)
		FD_SET(sock0, &rfd);
	if(sock1 != -1)		
		FD_SET(sock1, &rfd);

    if(::select(std::max(sock0, sock1) + 1, &rfd, NULL, NULL, &tv) > 0) {
        return FD_ISSET(sock0, &rfd) ? sock0 : sock1;
    }

	return sock0;
}

}

sockaddr_storage Socket::udpAddr;
socklen_t Socket::udpAddrLen;

#ifdef _DEBUG

SocketException::SocketException(int aError) noexcept {
	error = "SocketException: " + errorToString(aError);
	dcdebug("Thrown: %s\n", error.c_str());
}

#else // _DEBUG

SocketException::SocketException(int aError) noexcept : Exception(errorToString(aError)) { }

#endif

#ifdef _WIN32

void SocketHandle::reset(socket_t s) {
	if(valid()) {
		::closesocket(sock);
	}

	sock = s;
}

int Socket::getLastError() { return ::WSAGetLastError(); }

#else

void SocketHandle::reset(socket_t s) {
	if(valid()) {
		::close(sock);
	}

	sock = s;
}

int Socket::getLastError() { return errno; }

#endif

//Socket::Stats Socket::stats = { 0, 0 };

static const uint32_t SOCKS_TIMEOUT = 30000;

string SocketException::errorToString(int aError) noexcept {
	string msg = Util::translateError(aError);
	if(msg.empty()) {
		msg = _("Unknown error: 0x ") +Util::toString(aError);
	}

	return msg;
}

socket_t Socket::setSock(socket_t s, int af) {
	setBlocking2(s, false);
	setSocketOpt2(s, SOL_SOCKET, SO_REUSEADDR, 1);


	if(af == AF_INET) {
		dcassert(sock4 == INVALID_SOCKET);
		sock4 = s;
	} else if(af == AF_INET6) {
		dcassert(sock6 == INVALID_SOCKET);
		int ret = setSocketOpt2(s, IPPROTO_IPV6, IPV6_V6ONLY, 1);
		if(ret == -1)
            throw SocketException("Unknow error");
		sock6 = s;
	} else {
		throw SocketException(_("Unknown protocol ") + Util::toString(af));
	}

	return s;
}

socket_t Socket::getSock() const {
	if(sock6.valid()) {
		if(sock4.valid()) {
			if(isConnected(sock6)) {
				dcdebug("Closing IPv4, IPv6 connected");
				sock4.reset();
			} else if(isConnected(sock4)) {
				dcdebug("Closing IPv6, IPv4 connected");
				sock6.reset();
				return sock4;
			}

			dcdebug("Both v4 & v6 sockets valid and unconnected, returning v6...\n");
			// TODO Neither connected - but this will become a race if the IPv4 socket connects while
			// we're still using the IPv6 one...
		}

		return sock6;
	}

	return sock4;
}

void Socket::setBlocking(bool block) noexcept {
	if(sock4.valid()) setBlocking2(sock4, block);
	if(sock6.valid()) setBlocking2(sock6, block);
}

socket_t Socket::create(const addrinfo& ai) {
	return setSock(check([&] { return ::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol); }), ai.ai_family);
}

void Socket::accept(const Socket& listeningSocket) {
	disconnect();

	sockaddr_storage sock_addr;
	socklen_t sz = sizeof(sock_addr);

	auto sock = check([&] { return ::accept(readable(listeningSocket.sock4, listeningSocket.sock6), (struct sockaddr*)&sock_addr, &sz); });
	setSock(sock,  ((struct sockaddr*)&sock_addr)->sa_family);

#ifdef _WIN32
	// Make sure we disable any inherited windows message things for this socket.
	::WSAAsyncSelect(sock, NULL, 0, 0);
#endif

	// remote IP
	char ipstr[INET6_ADDRSTRLEN + 1];
	// return the remote port
	if(((struct sockaddr*)&sock_addr)->sa_family == AF_INET) {
		struct sockaddr_in *s = (struct sockaddr_in *)&sock_addr;
		#ifdef _WIN32
		inet_ntop(&s->sin_addr, ipstr, sizeof ipstr);
		#else
		inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
		#endif
		setIp(ipstr);
	}
	else if(((struct sockaddr*)&sock_addr)->sa_family == AF_INET6) {
		struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sock_addr;
		
		#ifdef _WIN32
		inet_ntop(&s->sin6_addr, ipstr, sizeof ipstr);
		#else
		inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
		#endif
		
		setIp(ipstr);
	}
}

int16_t Socket::listen(const int16_t& port) {
	disconnect();

	// For server sockets we create both ipv4 and ipv6 if possible
	// We use the same port for both sockets to deal with the fact that
	// there's no way in ADC to have different ports for v4 and v6 TCP sockets

	uint16_t ret = 0;

	addrinfo_p ai(nullptr, nullptr);

//	if(!v4only) {
		try { ai = resolveAddr(localIp6, port, AF_INET6, AI_PASSIVE | AI_ADDRCONFIG); }
		catch(const SocketException&) { ai.reset(); }
		for(auto a = ai.get(); a && !sock6.valid(); a = a->ai_next) {
			try {
				create(*a);
				if(ret != 0) {
					((sockaddr_in6*)a->ai_addr)->sin6_port = ret;
				}

				check([&] { return ::bind(sock6, a->ai_addr, a->ai_addrlen); });
				check([&] { return ::getsockname(sock6, a->ai_addr, (socklen_t*)&a->ai_addrlen); });
				ret = ((sockaddr_in6*)a->ai_addr)->sin6_port;

				if(type == TYPE_TCP) {
					check([&] { return ::listen(sock6, 20); });
				}
			} catch(const SocketException&) { }
		}
//	}

	try { ai = resolveAddr(localIp4, port, AF_INET, AI_PASSIVE | AI_ADDRCONFIG); }
	catch(const SocketException&) { ai.reset(); }
	for(auto a = ai.get(); a && !sock4.valid(); a = a->ai_next) {
		try {
			create(*a);
			if(ret != 0) {
				((sockaddr_in*)a->ai_addr)->sin_port = ret;
			}

			check([&] { return ::bind(sock4, a->ai_addr, a->ai_addrlen); });
			check([&] { return ::getsockname(sock4, a->ai_addr, (socklen_t*)&a->ai_addrlen); });
			ret = ((sockaddr_in*)a->ai_addr)->sin_port;

			if(type == TYPE_TCP) {
				check([&] { return ::listen(sock4, 20); });
			}
		} catch(const SocketException&) { }
	}

	if(ret == 0) {
		throw SocketException(_("Could not open port for listening"));
	}
	return ntohs(ret);
}

void Socket::connect(const string& aAddr, const int16_t& aPort, const string& localPort) {
	disconnect();

	// We try to connect to both IPv4 and IPv6 if available
	auto addr = resolveAddr(aAddr, aPort);
	string address;
	if(addr->ai_family == AF_INET6) {
        char sIP[46];
        sIP[0] = '\0';
        #ifdef _WIN32
        inet_ntop(&((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr, sIP, 46);
        #else
        inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr, sIP, 46);
        #endif
        address = sIP;
    } else {
        address = inet_ntoa(((sockaddr_in *)addr->ai_addr)->sin_addr);
    }
	setIp(address);

	string lastError;
	
	for(auto ai = addr.get(); ai; ai = ai->ai_next) {
		if((ai->ai_family == AF_INET && !sock4.valid()) ||
			(ai->ai_family == AF_INET6 && !sock6.valid() /*&& !v4only*/))
		{
			try {
				auto sock = create(*ai);
				auto localIp =  ai->ai_family == AF_INET6 ? getLocalIp6() : getLocalIp4();

				if(!localPort.empty() || !localIp.empty()) {
					auto local = resolveAddr(localIp,Util::toInt(localPort), ai->ai_family);
					check([&] { return ::bind(sock, local->ai_addr, local->ai_addrlen); });
				}

				check([&] { return ::connect(sock, addr.get()->ai_addr, addr.get()->ai_addrlen); }, true);//ai
				
			} catch(const SocketException& e) {
				ai->ai_family == AF_INET ? sock4.reset() : sock6.reset();
				lastError = e.getError();
			}
		}
	}

	// An IP should be set if at least one connection attempt succeeded
	if(ip.empty()) {
		throw SocketException(lastError);
	}
}

namespace {
	inline uint64_t timeLeft(uint64_t start, uint64_t timeout) {
		if(timeout == 0) {
			return 0;
		}
		uint64_t now = GET_TICK();
		if(start + timeout < now)
			throw SocketException(_("Connection timeout"));
		return start + timeout - now;
	}
}

void Socket::socksConnect(const string& aAddr, const int16_t& aPort, uint32_t timeout) {
	if(SETTING(SOCKS_SERVER).empty() || SETTING(SOCKS_PORT) == 0) {
		throw SocketException(_("The socks server failed establish a connection"));
	}

	uint64_t start = GET_TICK();

	connect(SETTING(SOCKS_SERVER), SETTING(SOCKS_PORT),"");

	if(!waitConnected(timeLeft(start, timeout))) {
		throw SocketException(_("The socks server failed establish a connection"));
	}

	socksAuth(timeLeft(start, timeout));

	ByteVector connStr;

	// Authenticated, let's get on with it...
	connStr.push_back(5);			// SOCKSv5
	connStr.push_back(1);			// Connect
	connStr.push_back(0);			// Reserved

	if(SETTING(SOCKS_RESOLVE)) {
		connStr.push_back(3);		// Address type: domain name
		connStr.push_back((uint8_t)aAddr.size());
		connStr.insert(connStr.end(), aAddr.begin(), aAddr.end());
	} else {
		connStr.push_back(1);		// Address type: IPv4;
		unsigned long addr = inet_addr(resolve(aAddr, AF_INET).c_str());
		uint8_t* paddr = (uint8_t*)&addr;
		connStr.insert(connStr.end(), paddr, paddr+4);
	}

	uint16_t port = htons(aPort);
	uint8_t* pport = (uint8_t*)&port;
	connStr.push_back(pport[0]);
	connStr.push_back(pport[1]);

	writeAll(&connStr[0], connStr.size(), timeLeft(start, timeout));

	// We assume we'll get a ipv4 address back...therefore, 10 bytes...
	/// @todo add support for ipv6
	if(readAll(&connStr[0], 10, timeLeft(start, timeout)) != 10) {
		throw SocketException(_("The socks server failed establish a connection"));
	}

	if(connStr[0] != 5 || connStr[1] != 0) {
		throw SocketException(_("The socks server failed establish a connection"));
	}

	in_addr sock_addr;

	memset(&sock_addr, 0, sizeof(sock_addr));
	sock_addr.s_addr = *((unsigned long*)&connStr[4]);
	setIp(inet_ntoa(sock_addr));
}

void Socket::socksAuth(uint32_t timeout) {
	vector<uint8_t> connStr;

	uint64_t start = GET_TICK();

	if(SETTING(SOCKS_USER).empty() && SETTING(SOCKS_PASSWORD).empty()) {
		// No username and pw, easier...=)
		connStr.push_back(5);			// SOCKSv5
		connStr.push_back(1);			// 1 method
		connStr.push_back(0);			// Method 0: No auth...

		writeAll(&connStr[0], 3, timeLeft(start, timeout));

		if(readAll(&connStr[0], 2, timeLeft(start, timeout)) != 2) {
			throw SocketException(_("The socks server failed establish a connection"));
		}

		if(connStr[1] != 0) {
			throw SocketException(_("The socks server requires authentication"));
		}
	} else {
		// We try the username and password auth type (no, we don't support gssapi)

		connStr.push_back(5);			// SOCKSv5
		connStr.push_back(1);			// 1 method
		connStr.push_back(2);			// Method 2: Name/Password...
		writeAll(&connStr[0], 3, timeLeft(start, timeout));

		if(readAll(&connStr[0], 2, timeLeft(start, timeout)) != 2) {
			throw SocketException(_("The socks server failed establish a connection"));
		}
		if(connStr[1] != 2) {
			throw SocketException(_("The socks server doesn't support login / password authentication"));
		}

		connStr.clear();
		// Now we send the username / pw...
		connStr.push_back(1);
		connStr.push_back((uint8_t)SETTING(SOCKS_USER).length());
		connStr.insert(connStr.end(), SETTING(SOCKS_USER).begin(), SETTING(SOCKS_USER).end());
		connStr.push_back((uint8_t)SETTING(SOCKS_PASSWORD).length());
		connStr.insert(connStr.end(), SETTING(SOCKS_PASSWORD).begin(), SETTING(SOCKS_PASSWORD).end());

		writeAll(&connStr[0], connStr.size(), timeLeft(start, timeout));

		if(readAll(&connStr[0], 2, timeLeft(start, timeout)) != 2) {
			throw SocketException(_("Socks server authentication failed (bad login / password?)"));
		}

		if(connStr[1] != 0) {
			throw SocketException(_("Socks server authentication failed (bad login / password?)"));
		}
	}
}

int Socket::getSocketOptInt(int option) {
	int val;
	socklen_t len = sizeof(val);
	check([&] { return ::getsockopt(getSock(), SOL_SOCKET, option, (char*)&val, &len); });
	return val;
}

void Socket::setSocketOpt(int option, int val) {
	int len = sizeof(val);
	if(sock4.valid()) {
		check([&] { return ::setsockopt(sock4, SOL_SOCKET, option, (char*)&val, len); });
	}

	if(sock6.valid()) {
		check([&] { return ::setsockopt(sock6, SOL_SOCKET, option, (char*)&val, len); });
	}
}

int Socket::read(void* aBuffer, int aBufLen) {
			if(aBufLen == 0)
                  return 0;
	int len = check([&] {
		return type == TYPE_TCP
			? ::recv(getSock(), (char*)aBuffer, aBufLen, 0)
			: ::recvfrom(readable(sock4, sock6), (char*)aBuffer, aBufLen, 0, NULL, NULL);
	}, true);

	if(len > 0) {
		stats.totalDown += len;
	}

	return len;
}

static string AddressToString(const sockaddr_storage * sasAddr) {
    if(sasAddr->ss_family == AF_INET6) {
        if(IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sasAddr)->sin6_addr)) {
            return inet_ntoa(*((in_addr *)(((struct sockaddr_in6 *)sasAddr)->sin6_addr.s6_addr + 12)));
        } else {
            char sIP[40];
            sIP[0] = '\0';
            #ifdef _WIN32
            Socket::inet_ntop(&((struct sockaddr_in6 *)sasAddr)->sin6_addr, sIP, 40);
            #else
            inet_ntop(AF_INET6,&((struct sockaddr_in6 *)sasAddr)->sin6_addr, sIP, 40);
            #endif
            return sIP;
        }
    } else {
	   return inet_ntoa(((struct sockaddr_in *)sasAddr)->sin_addr);
    }
}

int Socket::read(void* aBuffer, int aBufLen, string &aIP) {
	dcassert(type == TYPE_UDP);

	sockaddr_storage remote_addr;
	socklen_t addr_length = sizeof(remote_addr);

	int len = check([&] {
		return ::recvfrom(readable(sock4, sock6), (char*)aBuffer, aBufLen, 0, (struct sockaddr*)&remote_addr, &addr_length);
	}, true);

	if(len > 0) {
		aIP = AddressToString(&remote_addr);
		stats.totalDown += len;
	} else {
		aIP.clear();
	}

	return len;
}

int Socket::readAll(void* aBuffer, int aBufLen, uint32_t timeout) {
	uint8_t* buf = (uint8_t*)aBuffer;
	int i = 0;
	while(i < aBufLen) {
		int j = read(buf + i, aBufLen - i);
		if(j == 0) {
			return i;
		} else if(j == -1) {
			if(!wait(timeout, true, false).first) {
				return i;
			}
			continue;
		}

		i += j;
	}
	return i;
}

void Socket::writeAll(const void* aBuffer, int aLen, uint32_t timeout) {
	const uint8_t* buf = (const uint8_t*)aBuffer;
	int pos = 0;
	// No use sending more than this at a time...
	int sendSize = getSocketOptInt(SO_SNDBUF);

	while(pos < aLen) {
		int i = write(buf+pos, (int)std::min(aLen-pos, sendSize));
		if(i == -1) {
			wait(timeout, false, true);
		} else {
			pos+=i;
			stats.totalUp += i;
		}
	}
}

int Socket::write(const void* aBuffer, int aLen) {
	if(aBuffer == NULL || aLen == 0)
		return 0;
	int sent = check([&] { return ::send(getSock(), (const char*)aBuffer, aLen, 0); }, true);
	if(sent > 0) {
		stats.totalUp += sent;
	}
	return sent;
}

/**
 * Sends data, will block until all data has been sent or an exception occurs
 * @param aBuffer Buffer with data
 * @param aLen Data length
 * @throw SocketExcpetion Send failed.
 */
void Socket::writeTo(const string& aAddr, const uint16_t& aPort, const void* aBuffer, int aLen, bool proxy) {
	if(aLen <= 0)
		return;

	if(aAddr.empty() || aPort == 0) {
		throw SocketException(EADDRNOTAVAIL);
	}

	auto buf = (const uint8_t*)aBuffer;

	int sent;
	if(/*proxy && */CONNSETTING(OUTGOING_CONNECTIONS) == SettingsManager::OUTGOING_SOCKS5) {
		if( ((struct sockaddr*)&udpAddr)->sa_family == 0) {
			throw SocketException(_("Failed to set up the socks server for UDP relay (check socks address and port)"));
		}

		vector<uint8_t> connStr;

		connStr.reserve(aLen + 24);

		connStr.push_back(0);		// Reserved
		connStr.push_back(0);		// Reserved
		connStr.push_back(0);		// Fragment number, always 0 in our case...

		if(SETTING(SOCKS_RESOLVE)) {
			connStr.push_back(3);
			connStr.push_back((uint8_t)aAddr.size());
			connStr.insert(connStr.end(), aAddr.begin(), aAddr.end());
		} else {
			auto ai = resolveAddr(aAddr, aPort);

			if(ai->ai_family == AF_INET) {
				connStr.push_back(1);		// Address type: IPv4
				uint8_t* paddr = (uint8_t*)&((sockaddr_in*)ai->ai_addr)->sin_addr;
				connStr.insert(connStr.end(), paddr, paddr+4);
			} else if(ai->ai_family == AF_INET6) {
				connStr.push_back(4);		// Address type: IPv6
				uint8_t* paddr = (uint8_t*)&((sockaddr_in6*)ai->ai_addr)->sin6_addr;
				connStr.insert(connStr.end(), paddr, paddr+16);
			}
		}

		connStr.insert(connStr.end(), buf, buf + aLen);

		sent = check([&] { return ::sendto( (((struct sockaddr*)&udpAddr)->sa_family == AF_INET) ? sock4 : sock6,
			(const char*)&connStr[0], (int)connStr.size(), 0, (struct sockaddr*)&udpAddr, udpAddrLen); });
	} else {
		auto ai = resolveAddr(aAddr, aPort);
		if((ai->ai_family == AF_INET && !sock4.valid()) || (ai->ai_family == AF_INET6 && !sock6.valid())) {
			create(*ai);
		}
		sent = check([&] { return ::sendto(ai->ai_family == AF_INET ? sock4 : sock6,
			(const char*)aBuffer, (int)aLen, 0, (struct sockaddr*)ai->ai_addr, ai->ai_addrlen); });
	}

	stats.totalUp += sent;
}

/**
 * Blocks until timeout is reached one of the specified conditions have been fulfilled
 * @param millis Max milliseconds to block.
 * @param checkRead Check for reading
 * @param checkWrite Check for writing
 * @return pair with read/write state respectively
 * @throw SocketException Select or the connection attempt failed.
 */
std::pair<bool, bool> Socket::wait(int32_t millis, bool checkRead, bool checkWrite) {
	timeval tv = { millis/1000, (millis%1000)*1000 };
	fd_set rfd, wfd;
	fd_set *rfdp = NULL, *wfdp = NULL;

	int nfds = -1;

	if(checkRead) {
		rfdp = &rfd;
		FD_ZERO(rfdp);
		if(sock4.valid()) {
			FD_SET(sock4, &rfd);
			nfds = std::max((int)sock4, nfds);
		}

		if(sock6.valid()) {
			FD_SET(sock6, &rfd);
			nfds = std::max((int)sock6, nfds);
		}
	}

	if(checkWrite) {
		wfdp = &wfd;
		FD_ZERO(wfdp);
		if(sock4.valid()) {
			FD_SET(sock4, &wfd);
			nfds = std::max((int)sock4, nfds);
		}

		if(sock6.valid()) {
			FD_SET(sock6, &wfd);
			nfds = std::max((int)sock6, nfds);
		}
	}

	check([&] { return ::select(nfds + 1, rfdp, wfdp, NULL, &tv); });

	return std::make_pair(
		rfdp && ((sock4.valid() && FD_ISSET(sock4, rfdp)) || (sock6.valid() && FD_ISSET(sock6, rfdp))),
		wfdp && ((sock4.valid() && FD_ISSET(sock4, wfdp)) || (sock6.valid() && FD_ISSET(sock6, wfdp))));
}

bool Socket::waitConnected(int32_t millis) {
	timeval tv = { millis/1000, (millis%1000)*1000 };
	fd_set fd;
	FD_ZERO(&fd);

	int nfds = -1;
	if(sock4.valid()) {
		FD_SET(sock4, &fd);
		nfds = sock4;
	}

	if(sock6.valid()) {
		FD_SET(sock6, &fd);
		nfds = std::max((int)sock6, nfds);
	}

	check([&] { return ::select(nfds + 1, NULL, &fd, NULL, &tv); });

	if(sock6.valid() && FD_ISSET(sock6, &fd)) {
		int err6 = getSocketOptInt2(sock6, SO_ERROR);
		if(err6 == 0) {
			sock4.reset(); // We won't be needing this any more...
			return true;
		}

		if(!sock4.valid()) {
			throw SocketException(err6);
		}

		sock6.reset();
	}

	if(sock4.valid() && FD_ISSET(sock4, &fd)) {
		int err4 = getSocketOptInt2(sock4, SO_ERROR);
		if(err4 == 0) {
			sock6.reset(); // We won't be needing this any more...
			return true;
		}

		if(!sock6.valid()) {
			throw SocketException(err4);
		}

		sock4.reset();
	}

	return false;
}

bool Socket::waitAccepted(int32_t ) {
	// Normal sockets are always connected after a call to accept
	return true;
}

string Socket::resolve(const string& aDns, int af) noexcept {
	struct addrinfo hints;
	memset(&hints, 0, sizeof(addrinfo));
	hints.ai_family = af;

	addrinfo *result = nullptr;

	string ret;

	if(!::getaddrinfo(aDns.c_str(), NULL, &hints, &result)) {
		try { ret = resolveName(result->ai_addr, result->ai_addrlen); }
		catch(const SocketException&) { }

		::freeaddrinfo(result);
	}

	return ret;
}

Socket::addrinfo_p Socket::resolveAddr(const string& name, const uint16_t& port, int family, int flags) {
	struct addrinfo hints;
	memset(&hints, 0, sizeof(addrinfo));
	hints.ai_family = family;
	hints.ai_flags = flags;
	hints.ai_socktype = type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM;
	hints.ai_protocol = type;

	addrinfo *result = nullptr;

	auto err = ::getaddrinfo(name.c_str(), Util::toString(port).c_str(), &hints, &result);
	if(err) {
		throw SocketException(err);
	}

	dcdebug("Resolved %s:%d to %s, next is %p\n", name.c_str(), port,
		resolveName(result->ai_addr, result->ai_addrlen).c_str(), result->ai_next);

	return addrinfo_p(result, &freeaddrinfo);
}

string Socket::resolveName(const sockaddr* sa, socklen_t sa_len, int flags) {
	char buf[1024];

	int err = ::getnameinfo(sa, sa_len, buf, sizeof(buf), NULL, 0, flags);
	if(err) {
		throw SocketException(err);
	}

	return string(buf);
}

uint16_t Socket::getLocalPort() noexcept {
	if(getSock() == INVALID_SOCKET)
		return 0;

	sockaddr_storage sock_addr;
	socklen_t len = sizeof(sock_addr);
	if(::getsockname(getSock(),(struct sockaddr*)&sock_addr, &len) == 0) {
		if((((struct sockaddr_in*)&sock_addr)->sin_family) == AF_INET) {
			return ntohs(((struct sockaddr_in*)&sock_addr)->sin_port);
		} else if(((struct sockaddr_in6*)&sock_addr)->sin6_family == AF_INET6) {
			return ntohs(((struct sockaddr_in6*)&sock_addr)->sin6_port);
		}
	}
	return 0;
}

void Socket::socksUpdated() {
	memset(&udpAddr, 0, sizeof(udpAddr));
	udpAddrLen = sizeof(udpAddr);

	if(CONNSETTING(OUTGOING_CONNECTIONS) == SettingsManager::OUTGOING_SOCKS5) {
		try {
			Socket s(TYPE_TCP);
			s.setBlocking(false);
			s.connect(SETTING(SOCKS_SERVER), static_cast<uint16_t>(SETTING(SOCKS_PORT)));
			s.socksAuth(SOCKS_TIMEOUT);

			char connStr[10];
			connStr[0] = 5;			// SOCKSv5
			connStr[1] = 3;			// UDP Associate
			connStr[2] = 0;			// Reserved
			connStr[3] = 1;			// Address type: IPv4;
			*((long*)(&connStr[4])) = 0;		// No specific outgoing UDP address
			*((uint16_t*)(&connStr[8])) = 0;	// No specific port...

			s.writeAll(connStr, 10, SOCKS_TIMEOUT);

			// We assume we'll get a ipv4 address back...therefore, 10 bytes...if not, things
			// will break, but hey...noone's perfect (and I'm tired...)...
			if(s.readAll(connStr, 10, SOCKS_TIMEOUT) != 10) {
				return;
			}

			if(connStr[0] != 5 || connStr[1] != 0) {
				return;
			}

			((struct sockaddr_in*)&udpAddr)->sin_family = AF_INET;
			((struct sockaddr_in*)&udpAddr)->sin_port = *((uint16_t*)(&connStr[8]));
#ifdef _WIN32
//			udpAddr.sai.sin_addr.S_un.S_addr = *((long*)(&connStr[4]));
		inet_pton(&connStr[4],&((struct sockaddr_in*)&udpAddr)->sin_addr);
#else
//
//			((struct sockaddr_in*)&udpAddr)->sin_addr = (struct in_addr)*((long*)(&connStr[4]));
			inet_pton(AF_INET,&connStr[4],&((struct sockaddr_in*)&udpAddr)->sin_addr);
#endif
			udpAddrLen = sizeof(udpAddr);
		} catch(const SocketException&) {
			dcdebug("Socket: Failed to register with socks server\n");
		}
	}
}

void Socket::shutdown() noexcept {
	if(sock4.valid()) ::shutdown(sock4, 2);
	if(sock6.valid()) ::shutdown(sock6, 2);
}

void Socket::close() noexcept {
	sock4.reset();
	sock6.reset();
}

void Socket::disconnect() noexcept {
	shutdown();
	close();
}


string Socket::getRemoteHost(const string& aIp) {
	if(aIp.empty())
		return string();
	hostent *h = NULL;
	unsigned int addr;
	addr = inet_addr(aIp.c_str());

	h = gethostbyaddr(reinterpret_cast<char *>(&addr), 4, AF_INET);
	if (h == NULL) {
		return string();
	} else {
		return h->h_name;
	}
}

string Socket::getLocalIp() noexcept  {
	if(sock6.valid()) {
	
	if(sock6.get() == INVALID_SOCKET) {
		return string();
    }
 
    sockaddr_storage sas_addr;
	socklen_t sas_len = sizeof(sockaddr_storage);

	if(getsockname(sock6.get(), (struct sockaddr *)&sas_addr, &sas_len) == 0) {
		if(sas_addr.ss_family == AF_INET6) {
            if(IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&sas_addr)->sin6_addr)) {
                return (inet_ntoa(*((in_addr *)(((struct sockaddr_in6 *)&sas_addr)->sin6_addr.s6_addr + 12))));
            } else {
                char sIP[46];
                sIP[0] = '\0';
                #ifdef _WIN32
                inet_ntop(&((struct sockaddr_in6 *)&sas_addr)->sin6_addr, sIP, 46);
                #else
                inet_ntop(AF_INET6,&((struct sockaddr_in6 *)&sas_addr)->sin6_addr, sIP, 46);
                #endif
                return (sIP);
            }
        } else {
            return (inet_ntoa(((sockaddr_in *)&sas_addr)->sin_addr));
        }
	}
 }else if(sock4.valid()) {
	
	if(sock4.get() == INVALID_SOCKET) {
		return string();
    }
 
    sockaddr_storage sas_addr;
	socklen_t sas_len = sizeof(sockaddr_storage);

	if(getsockname(sock4.get(), (struct sockaddr *)&sas_addr, &sas_len) == 0) {
		if(sas_addr.ss_family == AF_INET6) {
            if(IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&sas_addr)->sin6_addr)) {
                return (inet_ntoa(*((in_addr *)(((struct sockaddr_in6 *)&sas_addr)->sin6_addr.s6_addr + 12))));
            } else {
                char sIP[46];
                sIP[0] = '\0';
                #ifdef _WIN32
                inet_ntop(&((struct sockaddr_in6 *)&sas_addr)->sin6_addr, sIP, 46);
                #else
                inet_ntop(AF_INET6,&((struct sockaddr_in6 *)&sas_addr)->sin6_addr, sIP, 46);
                #endif
                return (sIP);
            }
        } else {
            return (inet_ntoa(((sockaddr_in *)&sas_addr)->sin_addr));
        }
	}
 }
	return string();
}

//...
#ifdef _WIN32

typedef INT (WSAAPI * pInetPton)(INT, PCSTR, PVOID);
pInetPton MyInetPton = NULL;
typedef PCTSTR (WSAAPI *pInetNtop)(INT, PVOID, PSTR, size_t);
pInetNtop MyInetNtop = NULL;

INT Socket::inet_pton(PCSTR pAddrString, PVOID pAddrBuf) {
    if(MyInetPton != NULL) {
        return MyInetPton(AF_INET6, pAddrString, pAddrBuf);
    } else {
        sockaddr_storage sas_addr;
        socklen_t sas_len = sizeof(sockaddr_storage);
        memset(&sas_addr, 0, sas_len);

        if(::WSAStringToAddressA((LPSTR)pAddrString, AF_INET6, NULL, (struct sockaddr *)&sas_addr, &sas_len) == 0) {
            memcpy(pAddrBuf, &((struct sockaddr_in6 *)&sas_addr)->sin6_addr.s6_addr, 16);
            return 1;
        } else {
            return 0;
        }
    }
}

void Socket::inet_ntop(PVOID pAddr, PSTR pStringBuf, size_t szStringBufSize) {
    if(MyInetNtop != NULL) {
        MyInetNtop(AF_INET6, pAddr, pStringBuf, szStringBufSize);
    } else {
        struct sockaddr_in6 sin6;
        socklen_t sin6_len = sizeof(sockaddr_in6);

        memset(&sin6, 0, sin6_len);
        sin6.sin6_family = AF_INET6;
        memcpy(&sin6.sin6_addr, pAddr, 16);

        ::WSAAddressToStringA((struct sockaddr *)&sin6, sin6_len, NULL, pStringBuf, (LPDWORD)&szStringBufSize);
    }
}
#endif
///

} // namespace dcpp
