/**
 **	File ......... Socket.cpp
 **	Published ....  2004-02-13
 **	Author ....... grymse@alhem.net
**/
/*
Copyright (C) 2004,2005  Anders Hedstrom

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.
*/
#ifdef _WIN32
#pragma warning(disable:4786)
#include <stdlib.h>
#else
#include <errno.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include "Parse.h"
#include "SocketHandler.h"
#include "SocketThread.h"
#include "Utility.h"

#include "Socket.h"

#ifdef _DEBUG
#define DEB(x) x
#else
#define DEB(x)
#endif


// statics
#ifdef _WIN32
WSAInitializer Socket::m_winsock_init;
#endif


Socket::Socket(SocketHandler& h)
:m_handler(h)
,m_socket( INVALID_SOCKET )
,m_bDel(false)
,m_bClose(false)
,m_bConnecting(false)
,m_tCreate(time(NULL))
,m_line_protocol(false)
,m_ssl_connecting(false)
//, m_tActive(time(NULL))
//, m_timeout(0)
,m_detach(false)
,m_detached(false)
,m_pThread(NULL)
,m_ipv6(false)
,m_sa_len(0)
,m_parent(NULL)
,m_socket_type(0)
,m_bClient(false)
,m_bRetain(false)
,m_bLost(false)
,m_call_on_connect(false)
,m_opt_reuse(true)
,m_opt_keepalive(true)
,m_bSocks4(false)
,m_socks4_host(h.GetSocks4Host())
,m_socks4_port(h.GetSocks4Port())
,m_socks4_userid(h.GetSocks4Userid())
,m_connect_timeout(5)
,m_b_enable_ssl(false)
,m_b_ssl(false)
,m_b_ssl_server(false)
,m_b_disable_read(false)
{
}


Socket::~Socket()
{
	if (m_socket != INVALID_SOCKET && !m_bRetain)
	{
		Close();
	}
	if (m_pThread)
	{
		delete m_pThread;
	}
}


void Socket::Init()
{
}


void Socket::OnRead()
{
}


void Socket::OnWrite()
{
}


void Socket::OnException()
{
	// errno valid here?
	int err;
	socklen_t errlen = sizeof(err);
#ifdef _WIN32
	getsockopt(m_socket, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen);
#else
	getsockopt(m_socket, SOL_SOCKET, SO_ERROR, &err, &errlen);
#endif
	Handler().LogError(this, "exception on select", Errno, StrError(Errno), LOG_LEVEL_FATAL);
	SetCloseAndDelete();
}


void Socket::OnDelete()
{
}


void Socket::OnConnect()
{
}


bool Socket::CheckConnect()
{
	int err;
	socklen_t errlen = sizeof(err);
	bool r = true;
#ifdef _WIN32
	getsockopt(m_socket, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen);
#else
	getsockopt(m_socket, SOL_SOCKET, SO_ERROR, &err, &errlen);
#endif
	if (err)
	{
		Handler().LogError(this, "connect failed", err, StrError(err), LOG_LEVEL_FATAL);
		r = false;
	}
	SetConnecting(false);
	// %! add to read fd_set here
	if (r) // ok
	{
		Set(!IsDisableRead(), false);
	}
	return r;
}


void Socket::OnAccept()
{
}


int Socket::Close()
{
	if (m_socket == INVALID_SOCKET) // this could happen
	{
		Handler().LogError(this, "Socket::Close", 0, "file descriptor invalid", LOG_LEVEL_WARNING);
		return 0;
	}
	int n;
	SetNonblocking(true);
	if (shutdown(m_socket, SHUT_RDWR) == -1)
	{
		// failed...
		Handler().LogError(this, "shutdown", Errno, StrError(Errno), LOG_LEVEL_ERROR);
	}
	//
	char tmp[100];
	if ((n = recv(m_socket,tmp,100,0)) == -1)
	{
//		Handler().LogError(this, "read() after shutdown", Errno, StrError(Errno), LOG_LEVEL_WARNING);
	}
	else
	{
		if (n)
			Handler().LogError(this, "read() after shutdown", n, "bytes read", LOG_LEVEL_WARNING);
	}
	if ((n = closesocket(m_socket)) == -1)
	{
		// failed...
		Handler().LogError(this, "close", Errno, StrError(Errno), LOG_LEVEL_ERROR);
	}
	m_socket = INVALID_SOCKET;
	return n;
}


SOCKET Socket::CreateSocket4(int type, const std::string& protocol)
{
	struct protoent *p = NULL;
	int optval;
	SOCKET s;

	m_socket_type = type;
	m_socket_protocol = protocol;
	if (protocol.size())
	{
		p = getprotobyname( protocol.c_str() );
		if (!p)
		{
			Handler().LogError(this, "getprotobyname", Errno, StrError(Errno), LOG_LEVEL_FATAL);
			return INVALID_SOCKET;
		}
	}
	int protno = p ? p -> p_proto : 0;

	s = socket(AF_INET, type, protno);
	if (s == INVALID_SOCKET)
	{
		Handler().LogError(this, "socket", Errno, StrError(Errno), LOG_LEVEL_FATAL);
		return INVALID_SOCKET;
	}
	OnOptions(AF_INET, type, protno, s);
	if (type == SOCK_STREAM)
	{
		optval = m_opt_reuse ? 1 : 0;
		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)) == -1)
		{
			Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_REUSEADDR)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
			closesocket(s);
			return INVALID_SOCKET;
		}

		optval = m_opt_keepalive ? 1 : 0;
		if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(optval)) == -1)
		{
			Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_KEEPALIVE)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
			closesocket(s);
			return INVALID_SOCKET;
		}
	}

	return s;
}


#ifdef IPPROTO_IPV6
SOCKET Socket::CreateSocket6(int type, const std::string& protocol)
{
	struct protoent *p = NULL;
	int optval;
	SOCKET s;

	m_socket_type = type;
	m_socket_protocol = protocol;
	if (protocol.size())
	{
		p = getprotobyname( protocol.c_str() );
		if (!p)
		{
			Handler().LogError(this, "getprotobyname", Errno, StrError(Errno), LOG_LEVEL_FATAL);
			return INVALID_SOCKET;
		}
	}
	int protno = p ? p -> p_proto : 0;

	s = socket(AF_INET6, type, protno);
	if (s == INVALID_SOCKET)
	{
		Handler().LogError(this, "socket", Errno, StrError(Errno), LOG_LEVEL_FATAL);
		return INVALID_SOCKET;
	}
	OnOptions(AF_INET6, type, protno, s);
	if (type == SOCK_STREAM)
	{
		optval = m_opt_reuse ? 1 : 0;
		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)) == -1)
		{
			Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_REUSEADDR)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
			closesocket(s);
			return INVALID_SOCKET;
		}

		optval = m_opt_keepalive ? 1 : 0;
		if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(optval)) == -1)
		{
			Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_KEEPALIVE)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
			closesocket(s);
			return INVALID_SOCKET;
		}
	}
	m_ipv6 = true;
	return s;
}
#endif


bool Socket::isip(const std::string& str)
{
	if (m_ipv6)
	{
		size_t qc = 0;
		size_t qd = 0;
		for (size_t i = 0; i < str.size(); i++)
		{
			qc += (str[i] == ':') ? 1 : 0;
			qd += (str[i] == '.') ? 1 : 0;
		}
		if (qc > 7)
		{
			return false;
		}
		if (qd && qd != 3)
		{
			return false;
		}
		Parse pa(str,":.");
		std::string tmp = pa.getword();
		while (tmp.size())
		{
			if (tmp.size() > 4)
			{
				return false;
			}
			for (size_t i = 0; i < tmp.size(); i++)
			{
				if (tmp[i] < '0' || (tmp[i] > '9' && tmp[i] < 'A') ||
					(tmp[i] > 'F' && tmp[i] < 'a') || tmp[i] > 'f')
				{
					return false;
				}
			}
			//
			tmp = pa.getword();
		}
		return true;
	}
	for (size_t i = 0; i < str.size(); i++)
		if (!isdigit(str[i]) && str[i] != '.')
			return false;
	return true;
}


bool Socket::u2ip(const std::string& str, ipaddr_t& l)
{
	if (m_ipv6)
	{
		Handler().LogError(this, "u2ip", 0, "converting to ipv4 on ipv6 socket", LOG_LEVEL_ERROR);
		return false;
	}
	if (isip(str))
	{
		Parse pa((char *)str.c_str(), ".");
		union {
			struct {
				unsigned char b1;
				unsigned char b2;
				unsigned char b3;
				unsigned char b4;
			} a;
			ipaddr_t l;
		} u;
		u.a.b1 = static_cast<unsigned char>(pa.getvalue());
		u.a.b2 = static_cast<unsigned char>(pa.getvalue());
		u.a.b3 = static_cast<unsigned char>(pa.getvalue());
		u.a.b4 = static_cast<unsigned char>(pa.getvalue());
		l = u.l;
		return true;
	}
	else
	{
		struct hostent *he = gethostbyname( str.c_str() );
		if (!he)
		{
			Handler().LogError(this, "gethostbyname", Errno, StrError(Errno), LOG_LEVEL_WARNING);
			return false;
		}
		memcpy(&l, he -> h_addr, 4);
		return true;
	}
	return false;
}


#ifdef IPPROTO_IPV6
bool Socket::u2ip(const std::string& str, struct in6_addr& l)
{
	if (!m_ipv6)
	{
		Handler().LogError(this, "u2ip", 0, "converting to ipv6 on ipv4 socket", LOG_LEVEL_ERROR);
		return false;
	}
	if (isip(str))
	{
		std::list<std::string> vec;
		size_t x = 0;
		char s[100];
		for (size_t i = 0; i <= str.size(); i++)
		{
			if (i == str.size() || str[i] == ':')
			{
				strncpy(s, str.substr(x,i - x).c_str(), i - x);
				s[i - x] = 0;
				//
				if (strstr(s,".")) // x.x.x.x
				{
					Parse pa(s,".");
					char slask[100];
					unsigned long b0 = static_cast<unsigned long>(pa.getvalue());
					unsigned long b1 = static_cast<unsigned long>(pa.getvalue());
					unsigned long b2 = static_cast<unsigned long>(pa.getvalue());
					unsigned long b3 = static_cast<unsigned long>(pa.getvalue());
					sprintf(slask,"%lx",b0 * 256 + b1);
					vec.push_back(slask);
					sprintf(slask,"%lx",b2 * 256 + b3);
					vec.push_back(slask);
				}
				else
				{
					vec.push_back(s);
				}
				//
				x = i + 1;
			}
		}
		size_t sz = vec.size(); // number of byte pairs
		size_t i = 0; // index in in6_addr.in6_u.u6_addr16[] ( 0 .. 7 )
		for (std::list<std::string>::iterator it = vec.begin(); it != vec.end(); it++)
		{
			std::string bytepair = *it;
			if (bytepair.size())
			{
				l.s6_addr16[i++] = htons(Utility::hex2unsigned(bytepair));
			}
			else
			{
				l.s6_addr16[i++] = 0;
				while (sz++ < 8)
				{
					l.s6_addr16[i++] = 0;
				}
			}
		}
		return true;
	}
	else
	{
#ifdef SOLARIS
		int errnum = 0;
		struct hostent *he = getipnodebyname( str.c_str(), AF_INET6, 0, &errnum );
#else
		struct hostent *he = gethostbyname2( str.c_str(), AF_INET6 );
#endif
		if (!he)
		{
#ifdef SOLARIS
			Handler().LogError(this, "getipnodebyname", errnum, "failed, see error code");
#else
			Handler().LogError(this, "gethostbyname2", Errno, StrError(Errno), LOG_LEVEL_WARNING);
#endif
			return false;
		}
		memcpy(&l,he -> h_addr_list[0],he -> h_length);
#ifdef SOLARIS
		free(he);
#endif
		return true;
	}
	return false;
}
#endif


void Socket::l2ip(const ipaddr_t ip, std::string& str)
{
	if (m_ipv6)
	{
		Handler().LogError(this, "l2ip", 0, "converting to ipv4 on ipv6 socket", LOG_LEVEL_ERROR);
		str = "";
		return;
	}
	union {
		struct {
			unsigned char b1;
			unsigned char b2;
			unsigned char b3;
			unsigned char b4;
		} a;
		ipaddr_t l;
	} u;
	u.l = ip;
	char tmp[100];
	sprintf(tmp, "%u.%u.%u.%u", u.a.b1, u.a.b2, u.a.b3, u.a.b4);
	str = tmp;
}


#ifdef IPPROTO_IPV6
void Socket::l2ip(const struct in6_addr& ip, std::string& str,bool mixed)
{
	if (!m_ipv6)
	{
		Handler().LogError(this, "l2ip", 0, "converting to ipv6 on ipv4 socket", LOG_LEVEL_ERROR);
		str = "";
		return;
	}
	char slask[100];
	*slask = 0;
	unsigned int prev = 0;
	if (mixed)
	{
		unsigned int x;
		for (size_t i = 0; i < 6; i++)
		{
			x = ntohs(ip.s6_addr16[i]);
			if (*slask && (x || prev))
				strcat(slask,":");
			if (x)
			{
				sprintf(slask + strlen(slask),"%X", x);
			}
			prev = x;
		}
		x = ntohs(ip.s6_addr16[6]);
		sprintf(slask + strlen(slask),":%u.%u",x / 256,x & 255);
		x = ntohs(ip.s6_addr16[7]);
		sprintf(slask + strlen(slask),".%u.%u",x / 256,x & 255);
	}
	else
	{
		for (size_t i = 0; i < 8; i++)
		{
			unsigned int x = ntohs(ip.s6_addr16[i]);
			if (*slask && (x || prev))
				strcat(slask,":");
			if (x)
			{
				sprintf(slask + strlen(slask),"%X", x);
			}
			prev = x;
		}
	}
	str = slask;
}
#endif


void Socket::Attach(SOCKET s)
{
	m_socket = s;
}


SOCKET Socket::GetSocket()
{
	return m_socket;
}


void Socket::SetDeleteByHandler(bool x)
{
	m_bDel = x;
}


bool Socket::DeleteByHandler()
{
	return m_bDel;
}


void Socket::SetCloseAndDelete(bool x)
{
	m_bClose = x;
}


bool Socket::CloseAndDelete()
{
	return m_bClose;
}


void Socket::SetConnecting(bool x)
{
	m_bConnecting = x;
	m_tConnect = time(NULL);
}


bool Socket::Connecting()
{
	return m_bConnecting;
}


void Socket::SetRemoteAddress(struct sockaddr* sa, socklen_t l)
{
	memcpy(&m_sa, sa, l);
	m_sa_len = l;
}


void Socket::GetRemoteSocketAddress(struct sockaddr& sa,socklen_t& sa_len)
{
	memcpy(&sa, &m_sa, m_sa_len);
	sa_len = m_sa_len;
}


SocketHandler& Socket::Handler() const
{
	return m_handler;
}


ipaddr_t Socket::GetRemoteIP4()
{
	ipaddr_t l = 0;
	struct sockaddr_in* saptr = (struct sockaddr_in*)&m_sa;
	if (m_ipv6)
	{
		Handler().LogError(this, "GetRemoteIP4", 0, "get ipv4 address for ipv6 socket", LOG_LEVEL_WARNING);
	}
	memcpy(&l, &saptr -> sin_addr, 4);
	return l;
}


#ifdef IPPROTO_IPV6
struct in6_addr Socket::GetRemoteIP6()
{
	struct sockaddr_in6 *p = (struct sockaddr_in6 *)&m_sa;
	if (!m_ipv6)
	{
		Handler().LogError(this, "GetRemoteIP6", 0, "get ipv6 address for ipv4 socket", LOG_LEVEL_WARNING);
	}
	return p -> sin6_addr;
}
#endif


port_t Socket::GetRemotePort()
{
#ifdef IPPROTO_IPV6
	if (m_ipv6)
	{
		struct sockaddr_in6 *p = (struct sockaddr_in6 *)&m_sa;
		return ntohs(p -> sin6_port);
	}
#endif
	struct sockaddr_in* saptr = (struct sockaddr_in*)&m_sa;
	return ntohs(saptr -> sin_port);
}


std::string Socket::GetRemoteAddress()
{
	std::string str;
#ifdef IPPROTO_IPV6
	if (m_ipv6)
	{
		l2ip(GetRemoteIP6(), str);
		return str;
	}
#endif
	l2ip(GetRemoteIP4(), str);
	return str;
}


std::string Socket::GetRemoteHostname()
{
	std::string str;
#ifdef IPPROTO_IPV6
	if (m_ipv6)
	{
		Handler().LogError(this, "GetRemoteHostname", 0, "not implemented for ipv6", LOG_LEVEL_WARNING);
		return GetRemoteAddress();
	}
#endif
	long l = GetRemoteIP4();
//#ifdef LINUX
//	struct hostent *he = gethostbyaddr(&l, sizeof(long), AF_INET);
//#else // _WIN32, MACOSX and SOLARIS
	struct hostent *he = gethostbyaddr( (char *)&l, sizeof(long), AF_INET);
//#endif
	if (!he)
	{
		return GetRemoteAddress();
	}
	str = he -> h_name;
	return str;
}


bool Socket::SetNonblocking(bool bNb)
{
#ifdef _WIN32
	unsigned long l = bNb ? 1 : 0;
	int n = ioctlsocket(m_socket, FIONBIO, &l);
	if (n != 0)
	{
		Handler().LogError(this, "ioctlsocket(FIONBIO)", Errno, "");
		return false;
	}
	return true;
#else
	if (bNb)
	{
		if (fcntl(m_socket, F_SETFL, O_NONBLOCK) == -1)
		{
			Handler().LogError(this, "fcntl(F_SETFL, O_NONBLOCK)", Errno, StrError(Errno), LOG_LEVEL_ERROR);
			return false;
		}
	}
	else
	{
		if (fcntl(m_socket, F_SETFL, 0) == -1)
		{
			Handler().LogError(this, "fcntl(F_SETFL, 0)", Errno, StrError(Errno), LOG_LEVEL_ERROR);
			return false;
		}
	}
	return true;
#endif
}


bool Socket::SetNonblocking(bool bNb, SOCKET s)
{
#ifdef _WIN32
	unsigned long l = bNb ? 1 : 0;
	int n = ioctlsocket(s, FIONBIO, &l);
	if (n != 0)
	{
		Handler().LogError(this, "ioctlsocket(FIONBIO)", Errno, "");
		return false;
	}
	return true;
#else
	if (bNb)
	{
		if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
		{
			Handler().LogError(this, "fcntl(F_SETFL, O_NONBLOCK)", Errno, StrError(Errno), LOG_LEVEL_ERROR);
			return false;
		}
	}
	else
	{
		if (fcntl(s, F_SETFL, 0) == -1)
		{
			Handler().LogError(this, "fcntl(F_SETFL, 0)", Errno, StrError(Errno), LOG_LEVEL_ERROR);
			return false;
		}
	}
	return true;
#endif
}


void Socket::Set(bool bRead, bool bWrite, bool bException)
{
	m_handler.Set(m_socket, bRead, bWrite, bException);
}


time_t Socket::GetConnectTime()
{
	return time(NULL) - m_tConnect;
}


bool Socket::Ready()
{
	if (m_socket != INVALID_SOCKET && !Connecting() && !CloseAndDelete())
		return true;
	return false;
}


bool Socket::Detach()
{
	if (!DeleteByHandler())
		return false;
	if (m_pThread)
		return false;
	if (m_detached)
		return false;
	m_detach = true;
	return true;
}


void Socket::DetachSocket()
{
	m_pThread = new SocketThread(*this);
	m_pThread -> SetRelease(true);
}


void Socket::OnLine(const std::string& ) 
{
}


void Socket::OnSSLInitDone() 
{
}


bool Socket::SSLCheckConnect() 
{ 
	return false; 
}


void Socket::SetSSLConnecting(bool x) 
{ 
	m_ssl_connecting = x; 
}


bool Socket::SSLConnecting() 
{ 
	return m_ssl_connecting; 
}


void Socket::SetLineProtocol(bool x) 
{ 
	m_line_protocol = x; 
}


bool Socket::LineProtocol() 
{ 
	return m_line_protocol; 
}


void Socket::ReadLine()
{
}


void Socket::OnConnectFailed()
{
}


Socket::Socket(const Socket& s) : m_handler(s.Handler())
{
}


Socket *Socket::GetParent()
{
	return m_parent;
}


void Socket::SetParent(Socket *x)
{
	m_parent = x;
}


port_t Socket::GetPort()
{
	Handler().LogError(this, "GetPort", 0, "GetPort only implemented for ListenSocket", LOG_LEVEL_WARNING);
	return 0;
}


void Socket::CopyConnection(Socket *sock)
{
	Attach( sock -> GetSocket() );
	SetSocketType( sock -> GetSocketType() );
	SetSocketProtocol( sock -> GetSocketProtocol() );
	SetClientRemoteAddr( sock -> GetClientRemoteAddr() );
	SetClientRemotePort( sock -> GetClientRemotePort() );

	struct sockaddr sa;
	socklen_t sa_len;
	sock -> GetRemoteSocketAddress(sa, sa_len);
	SetRemoteAddress(&sa, sa_len);
}


void Socket::OnOptions(int family,int type,int protocol,SOCKET s)
{
/*
	Handler().LogError(this, "OnOptions", family, "Address Family", LOG_LEVEL_INFO);
	Handler().LogError(this, "OnOptions", type, "Type", LOG_LEVEL_INFO);
	Handler().LogError(this, "OnOptions", protocol, "Protocol", LOG_LEVEL_INFO);
*/
	SetReuse(true);
	SetKeepalive(true);
}


int Socket::Resolve(const std::string& host,port_t port)
{
	return Handler().Resolve(this, host, port);
}


void Socket::OnSocks4Connect()
{
	Handler().LogError(this, "OnSocks4Connect", 0, "Use with TcpSocket only");
}


void Socket::OnSocks4ConnectFailed()
{
	Handler().LogError(this, "OnSocks4ConnectFailed", 0, "Use with TcpSocket only");
}


bool Socket::OnSocks4Read()
{
	Handler().LogError(this, "OnSocks4Read", 0, "Use with TcpSocket only");
	return true;
}


void Socket::SetSocks4Host(const std::string& host)
{
	u2ip(host, m_socks4_host);
}


/*
void Socket::OnWriteComplete()
{
}
*/


void Socket::Resolved(int,ipaddr_t,port_t)
{
}




syntax highlighted by Code2HTML, v. 0.9.1