You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kvirc/src/kvirc/kernel/kvi_ircsocket.cpp

1911 lines
54 KiB

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// File : kvi_ircsocket.cpp
// Creation date : Tue Jul 30 19:25:18 2002 GMT by Szymon Stefanek
//
// This file is part of the KVirc irc client distribution
// Copyright (C) 2002 Szymon Stefanek (pragma at kvirc dot net)
//
// 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 opinion) 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. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define __KVIRC__
#include "kvi_ircsocket.h"
#include "kvi_ircserver.h"
#include "kvi_proxydb.h"
#include "kvi_netutils.h"
#include "kvi_settings.h"
#include "kvi_error.h"
#include "kvi_locale.h"
#include "kvi_malloc.h"
#include "kvi_debug.h"
#include "kvi_string.h"
#include "kvi_options.h"
#include "kvi_memmove.h"
#include "kvi_socket.h"
#include "kvi_console.h"
#include "kvi_out.h"
#include "kvi_irclink.h"
#include "kvi_ircconnection.h"
#include "kvi_databuffer.h"
#ifdef COMPILE_SSL_SUPPORT
#include "kvi_sslmaster.h"
#endif
#include <tqtimer.h>
#include <tqsocketnotifier.h>
#ifndef COMPILE_ON_WINDOWS
#include <unistd.h> //for gettimeofday()
#endif
//#include <fcntl.h>
//#include <errno.h>
// FIXME: #warning "Lag-o-meter"
unsigned int g_uNextIrcLinkId = 1;
KviIrcSocket::KviIrcSocket(KviIrcLink * pLink)
: TQObject()
{
m_uId = g_uNextIrcLinkId;
g_uNextIrcLinkId++;
m_pLink = pLink;
m_pConsole = m_pLink->console();
m_state = Idle; // current socket state
m_pRsn = 0; // read socket notifier
m_pWsn = 0; // write socket notifier
m_sock = KVI_INVALID_SOCKET; // socket
m_pIrcServer = 0; // current server data
m_pProxy = 0; // current proxy data
m_pTimeoutTimer = 0; // timeout for connect()
m_uReadBytes = 0; // total read bytes per session
m_uSentBytes = 0; // total sent bytes per session
m_uSentPackets = 0; // total packets sent per session
m_pSendQueueHead = 0; // data queue
m_pSendQueueTail = 0; //
m_iLastError = KviError_success;
#ifdef COMPILE_SSL_SUPPORT
m_pSSL = 0;
#endif
m_tAntiFloodLastMessageTime.tv_sec = 0;
m_tAntiFloodLastMessageTime.tv_usec = 0;
if(KVI_OPTION_UINT(KviOption_uintSocketQueueFlushTimeout) < 100)
KVI_OPTION_UINT(KviOption_uintSocketQueueFlushTimeout) = 100; // this is our minimum , we don't want to lag the app
m_bInProcessData = false;
m_pFlushTimer = new TQTimer(); // queue flush timer
connect(m_pFlushTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(flushSendQueue()));
}
KviIrcSocket::~KviIrcSocket()
{
reset();
delete m_pFlushTimer;
}
void KviIrcSocket::reset()
{
#ifdef COMPILE_SSL_SUPPORT
if(m_pSSL)
{
KviSSLMaster::freeSSL(m_pSSL);
m_pSSL = 0;
}
#endif
if(m_pIrcServer)
{
delete m_pIrcServer;
m_pIrcServer = 0;
}
if(m_pProxy)
{
delete m_pProxy;
m_pProxy = 0;
}
if(m_pRsn)
{
delete m_pRsn;
m_pRsn = 0;
}
if(m_pWsn)
{
delete m_pWsn;
m_pWsn = 0;
}
if(kvi_socket_isValid(m_sock))
{
kvi_socket_destroy(m_sock);
m_sock = KVI_INVALID_SOCKET;
}
if(m_pTimeoutTimer)
{
m_pTimeoutTimer->stop();
delete m_pTimeoutTimer;
m_pTimeoutTimer = 0;
}
m_bInProcessData = false;
m_uReadBytes = 0;
m_uSentBytes = 0;
m_uSentPackets = 0;
m_tAntiFloodLastMessageTime.tv_sec = 0;
m_tAntiFloodLastMessageTime.tv_usec = 0;
m_bInProcessData = false;
if(m_pFlushTimer->isActive())m_pFlushTimer->stop();
queue_removeAllMessages();
setState(Idle);
}
void KviIrcSocket::outputSSLMessage(const TQString &szMsg)
{
m_pConsole->output(KVI_OUT_SSL,__tr2qs("[SSL]: %Q"),&szMsg);
}
void KviIrcSocket::outputSSLError(const TQString &szMsg)
{
m_pConsole->output(KVI_OUT_SSL,__tr2qs("[SSL ERROR]: %Q"),&szMsg);
}
void KviIrcSocket::outputProxyMessage(const TQString &szMsg)
{
TQStringList list=TQStringList::split("\n",szMsg);
for(TQStringList::Iterator it = list.begin(); it != list.end(); ++it)
{
TQString szTemporary = (*it).stripWhiteSpace();
m_pConsole->output(KVI_OUT_SOCKETMESSAGE,__tr2qs("[PROXY]: %Q"),&(szTemporary));
}
}
void KviIrcSocket::outputProxyError(const TQString &szMsg)
{
TQStringList list=TQStringList::split("\n",szMsg);
for(TQStringList::Iterator it = list.begin(); it != list.end(); ++it)
{
TQString szTemporary = (*it).stripWhiteSpace();
m_pConsole->output(KVI_OUT_SOCKETERROR,__tr2qs("[PROXY ERROR]: %Q"),&(szTemporary));
}
}
void KviIrcSocket::outputSocketMessage(const TQString &szMsg)
{
m_pConsole->output(KVI_OUT_SOCKETMESSAGE,__tr2qs("[SOCKET]: %Q"),&szMsg);
}
void KviIrcSocket::outputSocketError(const TQString &szMsg)
{
m_pConsole->output(KVI_OUT_SOCKETERROR,__tr2qs("[SOCKET ERROR]: %Q"),&szMsg);
}
void KviIrcSocket::outputSocketWarning(const TQString &szMsg)
{
m_pConsole->output(KVI_OUT_SOCKETWARNING,__tr2qs("[SOCKET WARNING]: %Q"),&szMsg);
}
void KviIrcSocket::setState(SocketState st)
{
if(st != m_state)
{
m_state = st;
m_pLink->socketStateChange();
}
}
void KviIrcSocket::raiseError(int iError)
{
m_iLastError = iError;
//m_pConsole->socketError(iError);
if( (m_iLastError==KviError_remoteEndClosedConnection) && ( m_state == ProxyHttpError) )
outputSocketMessage(KviError::getDescription(iError));
else
outputSocketError(KviError::getDescription(iError));
}
int KviIrcSocket::startConnection(KviIrcServer *srv,KviProxy * prx,const char * bindAddress)
{
// Attempts to estabilish an IRC connection
// to the server specified by *srv.
// Uses the proxy *prx if not 0
if(m_state != Idle)return KviError_anotherConnectionInProgress;
// Coherent state, thnx.
reset();
if(srv->useSSL())
{
#ifndef COMPILE_SSL_SUPPORT
return KviError_noSSLSupport;
#endif //!COMPILE_SSL_SUPPORT
}
// Copy the server
m_pIrcServer = new KviIrcServer(*srv);
bool bTargetIpV6 = false;
bool bNeedServerIp = !prx;
if(prx) bNeedServerIp = (
prx->protocol() != KviProxy::Http && prx->protocol() != KviProxy::Socks5
);
// We're going to check the addresses now
// check the proxy stuff...
if(prx)
{
// Yeah...here comes the proxy
m_pProxy = new KviProxy(*prx);
// check the proxy IP address
if(m_pProxy->isIpV6())
{
// IpV6 proxy :) (STILL QUITE UNTESTED ?)
#ifdef COMPILE_IPV6_SUPPORT
bTargetIpV6 = true;
if(!kvi_isValidStringIp_V6(m_pProxy->ip()))return KviError_invalidProxyAddress;
// SOCKSV4 does not support IPV6 addresses
if(m_pProxy->protocol() == KviProxy::Socks4)return KviError_socksV4LacksIpV6Support;
#else
return KviError_noIpV6Support;
#endif
} else {
// IpV4 proxy
if(!kvi_isValidStringIp(m_pProxy->ip()))return KviError_invalidProxyAddress;
}
}
if(bNeedServerIp)
{
// check the irc host ip
#ifdef COMPILE_IPV6_SUPPORT
if(m_pIrcServer->isIpV6())
{
// We have an IpV6 server host (Interesting if proxy is IpV4)
if( !KviNetUtils::isValidStringIp_V6(m_pIrcServer->ip()) )return KviError_invalidIpAddress;
if(!m_pProxy)bTargetIpV6 = true; // otherwise the proxy rules
} else {
#endif
// We have an IpV4 server host
if(!KviNetUtils::isValidStringIp(m_pIrcServer->ip())) return KviError_invalidIpAddress;
#ifdef COMPILE_IPV6_SUPPORT
}
#endif
}
KviSockaddr sa(prx ? m_pProxy->ip() : m_pIrcServer->ip().utf8().data(),prx ? m_pProxy->port() : m_pIrcServer->port(),bTargetIpV6);
if(!sa.socketAddress())return KviError_invalidIpAddress;
// create the socket
#ifdef COMPILE_IPV6_SUPPORT
m_sock = kvi_socket_create(bTargetIpV6 ? KVI_SOCKET_PF_INET6 : KVI_SOCKET_PF_INET,KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP);
#else
m_sock = kvi_socket_create(KVI_SOCKET_PF_INET,KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP);
#endif
if(m_sock < 0)return KviError_socketCreationFailed;
if(bindAddress)
{
// we have to bind the socket to a local address
KviSockaddr localSa(bindAddress,0,bTargetIpV6);
bool bBindOk = localSa.socketAddress();
if(bBindOk)
{
bBindOk = kvi_socket_bind(m_sock,localSa.socketAddress(),((int)(localSa.addressLength())));
}
TQString tmp;
if(bBindOk)
{
if(_OUTPUT_VERBOSE)
KviTQString::sprintf(tmp,__tr2qs("Binding to local address %s"),bindAddress);
outputSocketMessage(tmp);
} else {
if(_OUTPUT_VERBOSE)
KviTQString::sprintf(tmp,__tr2qs("Binding to local address %s failed: the kernel will choose the correct interface"),bindAddress);
outputSocketWarning(tmp);
}
}
// make it non blocking
if(!kvi_socket_setNonBlocking(m_sock))
{
reset();
return KviError_asyncSocketFailed;
}
if(!kvi_socket_connect(m_sock,sa.socketAddress(),((int)(sa.addressLength()))))
{
// ops...
int err = kvi_socket_error();
if(!kvi_socket_recoverableConnectError(err))
{
// Ops...
int sockError=err;
if(sockError==0)
{
// Zero error ?...let's look closer
int iSize=sizeof(int);
if(!kvi_socket_getsockopt(m_sock,SOL_SOCKET,SO_ERROR,(void *)&sockError,&iSize))sockError=0;
}
// die :(
reset();
// And declare problems :)
if(sockError)return KviError::translateSystemError(sockError);
else return KviError_unknownError; //Error 0 ?
}
}
// and setup the WRITE notifier...
m_pWsn = new TQSocketNotifier((int)m_sock,TQSocketNotifier::Write);
TQObject::connect(m_pWsn,TQT_SIGNAL(activated(int)),this,TQT_SLOT(writeNotifierFired(int)));
m_pWsn->setEnabled(true);
// set the timer
if(KVI_OPTION_UINT(KviOption_uintIrcSocketTimeout) < 5)KVI_OPTION_UINT(KviOption_uintIrcSocketTimeout) = 5;
m_pTimeoutTimer = new TQTimer();
TQObject::connect(m_pTimeoutTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(connectionTimedOut()));
m_pTimeoutTimer->start(KVI_OPTION_UINT(KviOption_uintIrcSocketTimeout) * 1000,true);
// and wait for connect
setState(Connecting);
return KviError_success;
}
void KviIrcSocket::connectionTimedOut()
{
// the m_pTimeoutTimer fired :(
raiseError(KviError_connectionTimedOut);
reset();
}
void KviIrcSocket::writeNotifierFired(int)
{
// kill the timeout timer
if(m_pTimeoutTimer)
{
delete m_pTimeoutTimer;
m_pTimeoutTimer = 0;
}
// Check for errors...
int sockError;
int iSize=sizeof(int);
if(!kvi_socket_getsockopt(m_sock,SOL_SOCKET,SO_ERROR,(void *)&sockError,&iSize))sockError = -1;
//sockError = 0;
if(sockError != 0)
{
//failed
if(sockError > 0)sockError = KviError::translateSystemError(sockError);
else sockError = KviError_unknownError; //Error 0 ?
raiseError(sockError);
reset();
return;
}
// kill the write notifier
delete m_pWsn;
m_pWsn = 0;
//Succesfully connected...
connectionEstabilished();
}
void KviIrcSocket::connectionEstabilished()
{
// the connection with a remote end has been estabilished
// if it is a proxy we need to perform the login operations
// otherwise we're connected to the irc server
if(m_sock == KVI_INVALID_SOCKET)return; // ops...disconnected in setState() ????
if(m_pProxy)connectedToProxy();
else connectedToIrcServer();
}
void KviIrcSocket::connectedToProxy()
{
if(!m_pProxy)tqDebug("WARNING: connectedToProxy() without a m_pProxy!");
// FIXME: Do we want to support SSL proxies ?
// it would be just a matter of SSL handshaking
// with the proxy
setState(ProxyLogin);
if(m_pRsn)
{
delete m_pRsn;
m_pRsn = 0;
}
m_pRsn = new TQSocketNotifier((int)m_sock,TQSocketNotifier::Read);
TQObject::connect(m_pRsn,TQT_SIGNAL(activated(int)),this,TQT_SLOT(readProxyData(int)));
switch(m_pProxy->protocol())
{
case KviProxy::Http:
proxyLoginHttp();
break;
case KviProxy::Socks5:
proxyLoginV5();
break;
default:
proxyLoginV4();
break;
}
}
void KviIrcSocket::readHttpProxyErrorData(int)
{
char buffer[256];
int readLength;
readLength = kvi_socket_recv(m_sock,buffer,255);
if(readLength <= 0)
{
handleInvalidSocketRead(readLength);
return;
}
outputProxyMessage(m_pConsole->decodeText(buffer));
}
void KviIrcSocket::connectedToIrcServer()
{
#ifdef COMPILE_SSL_SUPPORT
if(m_pIrcServer->useSSL())
{
m_pSSL = KviSSLMaster::allocSSL(m_pConsole,m_sock,KviSSL::Client);
if(!m_pSSL)
{
raiseSSLError();
raiseError(KviError_SSLError);
reset();
return;
}
setState(SSLHandshake);
doSSLHandshake(0);
return;
}
#endif
linkUp();
}
void KviIrcSocket::readProxyData(int)
{
char buffer[256];
int readLength;
/*
// THIS IS WORKING CODE THAT SUPPORTS SSL PROXIES!
#ifdef COMPILE_SSL_SUPPORT
if(m_pSSL)
{
readLength = m_pSSL->read(buffer,256);
if(readLength <= 0)
{
// ssl error....?
switch(m_pSSL->getProtocolError(readLength))
{
case KviSSL::ZeroReturn:
readLength = 0;
break;
case KviSSL::WantRead:
case KviSSL::WantWrite:
// hmmm...
return;
break;
case KviSSL::SyscallError:
{
int iE = m_pSSL->getLastError(true);
if(iE != 0)
{
raiseSSLError();
raiseError(KviError_SSLError);
reset();
return;
}
}
break;
case KviSSL::SSLError:
raiseSSLError();
raiseError(KviError_SSLError);
reset();
return;
break;
default:
raiseError(KviError_SSLError);
reset();
return;
break;
}
handleInvalidSocketRead(readLength);
return;
}
} else {
#endif
*/
readLength = kvi_socket_recv(m_sock,buffer,255);
if(readLength <= 0)
{
handleInvalidSocketRead(readLength);
return;
}
/*
#ifdef COMPILE_SSL_SUPPORT
}
#endif
*/
// we need at least two bytes...
if(readLength < 2)
{
// a single byte of reply means:
// - connection through a 1 bps modem
// - a totally blocked network
// - remote host is not a SOCKS/HTTP server
// Anyway....it is always a meaningless reply
// better to try again later :)
raiseError(KviError_unrecognizedProxyReply);
reset();
return;
}
// handle the reply
switch(m_state)
{
case ProxyFinalV4:
//V4 final reply
proxyHandleV4FinalReply((unsigned char)buffer[1]);
break;
case ProxySelectAuthMethodV5:
//V5 method selection reply
proxyHandleV5MethodReply((unsigned char)buffer[1]);
break;
case ProxyUserPassV5:
//V5 user and pass reply
proxyHandleV5AuthReply((unsigned char)buffer[1]);
break;
case ProxyFinalV5:
//V5 final reply
proxyHandleV5FinalReply((unsigned char)buffer[1]);
break;
case ProxyFinalHttp:
//Http final reply
buffer[readLength] = '\0';
proxyHandleHttpFinalReply(buffer,readLength);
break;
default:
// what ?
raiseError(KviError_unrecognizedProxyReply);
reset();
break;
}
}
void KviIrcSocket::proxyLoginHttp()
{
// Well..this is just plain and easy: connect to the proxy machine
// and say "CONNECT <irc.server>:<port> HTTP/<version>\n\n"
// if it requires auth than say Proxy-Authorization: Basic user:passwd
// Then expect a server reply header (2 newlines)
// HTTP 200 = Success
// HTTP Anything else = Failure
if(_OUTPUT_VERBOSE)
outputProxyMessage(__tr2qs("Using HTTP protocol."));
setState(ProxyFinalHttp);
KviStr tmp(KviStr::Format,"CONNECT %s:%u HTTP/1.0\r\n",m_pIrcServer->hostName().utf8().data(),(unsigned int)(m_pIrcServer->port()));
if(m_pProxy->hasUser())
{
KviStr auth(KviStr::Format,"%s:%s",m_pProxy->user(),m_pProxy->pass());
KviStr encoded;
encoded.bufferToBase64(auth.ptr(),auth.len());
tmp.append(KviStr::Format,"Proxy-Authorization: Basic %s\r\n\r\n",encoded.ptr());
} else {
tmp.append("\r\n");
}
// tqDebug(tmp.ptr());
sendRawData(tmp.ptr(),tmp.len());
}
void KviIrcSocket::proxyLoginV4()
{
// SOCKSV4 protocol
//
// 1) CONNECT
//
// The client connects to the SOCKS server and sends a CONNECT request when
// it wants to establish a connection to an application server. The client
// includes in the request packet the IP address and the port number of the
// destination host, and userid, in the following format.
//
// +----+----+----+----+----+----+----+----+----+----+....+----+
// | VN | CD | DSTPORT | DSTIP | USERID |NULL|
// +----+----+----+----+----+----+----+----+----+----+....+----+
// # of bytes: 1 1 2 4 variable 1
//
// VN is the SOCKS protocol version number and should be 4. CD is the
// SOCKS command code and should be 1 for CONNECT request. NULL is a byte
// of all zero bits.
//
// The SOCKS server checks to see whether such a request should be granted
// based on any combination of source IP address, destination IP address,
// destination port number, the userid, and information it may obtain by
// consulting IDENT, cf. RFC 1413. If the request is granted, the SOCKS
// server makes a connection to the specified port of the destination host.
// A reply packet is sent to the client when this connection is established,
// or when the request is rejected or the operation fails.
//
if(_OUTPUT_VERBOSE)
outputProxyMessage(__tr2qs("Using SOCKSV4 protocol."));
m_pProxy->normalizeUserAndPass();
// the protocol does not specify the "userid" format...
// so build an userid from the pass and/or username...
KviStr szUserAndPass=m_pProxy->user();
if(m_pProxy->hasPass()){
if(szUserAndPass.hasData())szUserAndPass.append(' ');
szUserAndPass.append(m_pProxy->pass());
}
int iLen = szUserAndPass.len()+9;
// build the request packet
char *bufToSend = new char[iLen];
bufToSend[0]=(unsigned char)4; //Version 4
bufToSend[1]=(unsigned char)1; //Connect
TQ_UINT16 port=(TQ_UINT16)htons(m_pIrcServer->port());
kvi_memmove((void *)(bufToSend+2),(void *)&port,2);
struct in_addr ircInAddr;
if(!kvi_stringIpToBinaryIp(m_pIrcServer->ip(),&ircInAddr))
tqDebug("SOCKET INTERNAL ERROR IN IPV4 (SOCKS4) ADDR CONVERSION");
TQ_UINT32 host=(TQ_UINT32)ircInAddr.s_addr;
kvi_memmove((void *)(bufToSend+4),(void *)&host,4);
kvi_memmove((void *)(bufToSend+8),(void *)(szUserAndPass.ptr()),szUserAndPass.len());
// send it into hyperspace...
setState(ProxyFinalV4);
sendRawData(bufToSend,iLen);
delete[] bufToSend;
// and wait for reply...
}
void KviIrcSocket::proxyLoginV5()
{
// SOCKSV5 protocol.
//
// When a TCP-based client wishes to establish a connection to an object
// that is reachable only via a firewall (such determination is left up
// to the implementation), it must open a TCP connection to the
// appropriate SOCKS port on the SOCKS server system. The SOCKS service
// is conventionally located on TCP port 1080. If the connection
// request succeeds, the client enters a negotiation for the
// authentication method to be used, authenticates with the chosen
// method, then sends a relay request. The SOCKS server evaluates the
// request, and either establishes the appropriate connection or denies
// it.
//
// The client connects to the server, and sends a version
// identifier/method selection message:
//
// +----+----------+----------+
// |VER | NMETHODS | METHODS |
// +----+----------+----------+
// | 1 | 1 | 1 to 255 |
// +----+----------+----------+
//
// The VER field is set to X'05' for this version of the protocol. The
// NMETHODS field contains the number of method identifier octets that
// appear in the METHODS field.
// The values currently defined for TQT_METHOD are:
//
// o X'00' NO AUTHENTICATION REQUIRED
// o X'01' GSSAPI
// o X'02' USERNAME/PASSWORD
// o X'03' CHAP
// o X'04' to X'7F' IANA ASSIGNED
// o X'80' to X'FE' RESERVED FOR PRIVATE METHODS
// o X'FF' NO ACCEPTABLE METHODS
//
if(_OUTPUT_VERBOSE)
outputProxyMessage(__tr2qs("Using SOCKSv5 protocol."));
m_pProxy->normalizeUserAndPass();
// the protocol does not specify the "userid" format...
// so build an userid from the pass and/or username...
char bufToSend[4];
bufToSend[0]=(unsigned char)5; //use version 5
int sendLen = 3;
if(!(m_pProxy->hasUser() || m_pProxy->hasPass()))
{
// no auth needed.
bufToSend[1]=(unsigned char)1; //select one method
bufToSend[2]=(unsigned char)0; //select method 0 : no auth
if(_OUTPUT_VERBOSE)
outputProxyMessage(__tr2qs("We can accept auth method 0 (no auth)"));
} else {
// we can provide a password and username if needed...
bufToSend[1]=(unsigned char)2; //select from two methods
bufToSend[2]=(unsigned char)0; //method 0 or
bufToSend[3]=(unsigned char)2; //method 2 username/pass auth
sendLen = 4;
if(_OUTPUT_VERBOSE)
outputProxyMessage(__tr2qs("We can accept auth method 0 (no auth) or 2 (user/pass)"));
}
// notify the user before sending...since we may get disconnected
setState(ProxySelectAuthMethodV5);
sendRawData(bufToSend,sendLen);
// and wait for response
}
void KviIrcSocket::proxyAuthUserPassV5()
{
// Once the SOCKS V5 server has started, and the client has selected the
// Username/Password Authentication protocol, the Username/Password
// subnegotiation begins. This begins with the client producing a
// Username/Password request:
//
// +----+------+----------+------+----------+
// |VER | ULEN | UNAME | PLEN | PASSWD |
// +----+------+----------+------+----------+
// | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
// +----+------+----------+------+----------+
//
// The VER field contains the current version of the subnegotiation,
// which is X'01'. The ULEN field contains the length of the UNAME field
// that follows. The UNAME field contains the username as known to the
// source operating system. The PLEN field contains the length of the
// PASSWD field that follows. The PASSWD field contains the password
// association with the given UNAME.
//
unsigned int lPass=(unsigned int)m_pProxy->passLen();
if(lPass>255)lPass=255;
unsigned int lUser=(unsigned int)m_pProxy->userLen();
if(lUser>255)lUser=255;
int iLen=lPass+lUser+3;
char *bufToSend=new char[iLen];
bufToSend[0]=(unsigned char)1; //version x'01'
bufToSend[1]=(unsigned char)lUser; //length of the username
kvi_memmove((void *)(bufToSend+2),(void *)m_pProxy->user(),lUser); //username
bufToSend[2+lUser]=(unsigned char)lPass; //length of the password
kvi_memmove((void *)(bufToSend+3+lUser),(void *)m_pProxy->pass(),lPass);
// spit out the buffer and wait
setState(ProxyUserPassV5);
sendRawData(bufToSend,iLen);
delete[] bufToSend;
// and wait for response...
}
void KviIrcSocket::proxySendTargetDataV5()
{
// Once the method-dependent subnegotiation has completed, the client
// sends the request details. If the negotiated method includes
// encapsulation for purposes of integrity checking and/or
// confidentiality, these requests MUST be encapsulated in the method-
// dependent encapsulation.
//
// The SOCKS request is formed as follows:
//
// +----+-----+------+------+----------+----------+
// |VER | CMD | FLAG | ATYP | DST.ADDR | DST.PORT |
// +----+-----+------+------+----------+----------+
// | 1 | 1 | 1 | 1 | Variable | 2 |
// +----+-----+------+------+----------+----------+
//
// Where:
//
// o VER protocol version: X'05'
// o CMD
// o CONNECT X'01'
// o BIND X'02'
// o UDP ASSOCIATE X'03'
// o X'04' to X'7F' IANA ASSIGNED
// o X'80' to X'FF' RESERVED FOR PRIVATE METHODS
// o FLAG command dependent flag (defaults to X'00')
// o ATYP address type of following address
// o IP V4 address: X'01'
// o DOMAINNAME: X'03'
// o IP V6 address: X'04'
// o DST.ADDR desired destination address
// o DST.PORT desired destination port in network octet
// order
//
// The SOCKS server will typically evaluate the request based on
// source and destination addresses, and return one or more reply
// messages, as appropriate for the request type.
//
// In an address field (DST.ADDR, BND.ADDR), the ATYP field specifies
// the type of address contained within the field:
//
// o X'01'
//
// The address is a version-4 IP address, with a length of 4 octets.
//
// o X'03'
//
// The address field contains a fully-qualified domain name. The first
// octet of the address field contains the number of octets of name that
// follow, there is no terminating NUL octet.
//
// o X'04'
//
// The address is a version-6 IP address, with a length of 16 octets.
bool bRemoteDns=!(
(
KviNetUtils::isValidStringIp(m_pIrcServer->ip())
#ifdef COMPILE_IPV6_SUPPORT
|| KviNetUtils::isValidStringIp_V6(m_pIrcServer->ip())
#endif
)
&& m_pIrcServer->cacheIp()
);
int bufLen = bRemoteDns ? 4 + 1 + m_pIrcServer->hostName().utf8().length() + 2
: (m_pIrcServer->isIpV6() ? 22 : 10);
char * bufToSend = (char *)kvi_malloc(sizeof(char) * bufLen);
bufToSend[0]=(unsigned char)5; //Proto 5
bufToSend[1]=(unsigned char)1; //CONNECT
bufToSend[2]=(unsigned char)0; //RSV
if(bRemoteDns)
{
bRemoteDns=true;
bufToSend[3]=3;
bufToSend[4]=m_pIrcServer->hostName().utf8().length();
} else {
bufToSend[3]=(unsigned char)m_pIrcServer->isIpV6() ? 4 : 1; // IPV6 : IPV4
}
if(bRemoteDns)
{
kvi_memmove((void *)(bufToSend + 5),
(void *)(m_pIrcServer->hostName().utf8().data()),
m_pIrcServer->hostName().utf8().length());
TQ_UINT16 port = (TQ_UINT16)htons(m_pIrcServer->port());
kvi_memmove((void *)(bufToSend + 4 + 1 + m_pIrcServer->hostName().utf8().length()),(void *)&port,2);
} else if(m_pIrcServer->isIpV6()) {
#ifdef COMPILE_IPV6_SUPPORT
struct in6_addr ircInAddr;
if(!kvi_stringIpToBinaryIp_V6(m_pIrcServer->ip(),&ircInAddr))tqDebug("SOCKET INTERNAL ERROR IN IPV6 ADDR CONVERSION");
kvi_memmove((void *)(bufToSend + 4),(void *)(&ircInAddr),4);
TQ_UINT16 port = (TQ_UINT16)htons(m_pIrcServer->port());
kvi_memmove((void *)(bufToSend + 20),(void *)&port,2);
#endif
} else {
struct in_addr ircInAddr;
if(!kvi_stringIpToBinaryIp(m_pIrcServer->ip(),&ircInAddr))tqDebug("SOCKET INTERNAL ERROR IN IPV4 ADDR CONVERSION");
TQ_UINT32 host = (TQ_UINT32)ircInAddr.s_addr;
kvi_memmove((void *)(bufToSend + 4),(void *)&host,4);
TQ_UINT16 port = (TQ_UINT16)htons(m_pIrcServer->port());
kvi_memmove((void *)(bufToSend + 8),(void *)&port,2);
}
// send it into hyperspace...
setState(ProxyFinalV5);
sendRawData(bufToSend,bufLen);
kvi_free(bufToSend);
// and wait for reply...
}
void KviIrcSocket::proxyHandleV5AuthReply(unsigned char reply)
{
// The server verifies the supplied UNAME and PASSWD, and sends the
// following response:
//
// +----+--------+
// |VER | STATUS |
// +----+--------+
// | 1 | 1 |
// +----+--------+
//
// A STATUS field of X'00' indicates success. If the server returns a
// `failure' (STATUS value other than X'00') status, it MUST close the
// connection.
//
if(reply == 0)
{
if(_OUTPUT_VERBOSE)
outputProxyMessage(__tr2qs("Proxy response: auth OK: access granted"));
proxySendTargetDataV5();
return;
}
raiseError(KviError_proxyAuthFailed);
reset();
}
void KviIrcSocket::proxyHandleV5MethodReply(unsigned char reply)
{
// The server selects from one of the methods given in METHODS, and
// sends a TQT_METHOD selection message:
//
// +----+--------+
// |VER | TQT_METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
//
// If the selected TQT_METHOD is X'FF', none of the methods listed by the
// client are acceptable, and the client MUST close the connection.
//
// The values currently defined for TQT_METHOD are:
//
// o X'00' NO AUTHENTICATION REQUIRED
// o X'01' GSSAPI
// o X'02' USERNAME/PASSWORD
// o X'03' CHAP
// o X'04' to X'7F' IANA ASSIGNED
// o X'80' to X'FE' RESERVED FOR PRIVATE METHODS
// o X'FF' NO ACCEPTABLE METHODS
//
if(reply == 0)
{
if(_OUTPUT_VERBOSE)
outputProxyMessage(__tr2qs("Proxy response: Auth method OK: using method 0 (no auth)"));
proxySendTargetDataV5();
return;
}
if(reply == 2)
{
if(_OUTPUT_VERBOSE)
outputProxyMessage(__tr2qs("Proxy response: Auth method OK: using method 2 (user/pass)"));
proxyAuthUserPassV5();
return;
}
//Request rejected
if(reply == 0xFF)
{
raiseError(KviError_proxyNoAcceptableAuthMethod);
reset();
} else {
// unrecognized...
raiseError(KviError_unrecognizedProxyReply);
reset();
}
}
void KviIrcSocket::proxyHandleV5FinalReply(unsigned char reply)
{
//
// The SOCKS request information is sent by the client as soon as it has
// established a connection to the SOCKS server, and completed the
// authentication negotiations. The server evaluates the request, and
// returns a reply formed as follows:
//
// +----+-----+------+------+----------+----------+
// |VER | REP | FLAG | ATYP | BND.ADDR | BND.PORT |
// +----+-----+------+------+----------+----------+
// | 1 | 1 | 1 | 1 | Variable | 2 |
// +----+-----+------+------+----------+----------+
//
// Where:
// o VER protocol version: X'05'
// o REP Reply field:
// o X'00' succeeded
// o X'01' general SOCKS server failure
// o X'02' connection not allowed by ruleset
// o X'03' Network unreachable
// o X'04' Host unreachable
// o X'05' Connection refused
// o X'06' TTL expired
// o X'07' Command not supported
// o X'08' Address type not supported
// o X'09' Invalid address
// o X'0A' to X'FF' unassigned
// o FLAG command dependent flag
// o ATYP address type of following address
// o IP V4 address: X'01'
// o DOMAINNAME: X'03'
// o IP V6 address: X'04'
// o BND.ADDR server bound address
// o BND.PORT server bound port in network octet order
//
if(reply==0)
{
// Request granted
if(_OUTPUT_VERBOSE)
outputProxyMessage(__tr2qs("Proxy response: target data OK: request granted"));
connectedToIrcServer();
} else {
//Request rejected
int err;
switch(reply)
{
case 1: err = KviError_proxyReply01GeneralSOCKSFailure; break;
case 2: err = KviError_proxyReply02ConnectionNotAllowed; break;
case 3: err = KviError_proxyReply03NetworkUnreachable; break;
case 4: err = KviError_proxyReply04HostUnreachable; break;
case 5: err = KviError_proxyReply05ConnectionRefused; break;
case 6: err = KviError_proxyReply06TTLExpired; break;
case 7: err = KviError_proxyReply07CommandNotSupported; break;
case 8: err = KviError_proxyReply08AddressTypeNotSupported; break;
case 9: err = KviError_proxyReply09InvalidAddress; break;
default: err = KviError_unrecognizedProxyReply; break;
}
raiseError(err);
reset();
}
}
void KviIrcSocket::proxyHandleV4FinalReply(unsigned char reply)
{
// If the request is granted, the SOCKS
// server makes a connection to the specified port of the destination host.
// A reply packet is sent to the client when this connection is established,
// or when the request is rejected or the operation fails.
//
//
// +----+----+----+----+----+----+----+----+
// | VN | CD | DSTPORT | DSTIP |
// +----+----+----+----+----+----+----+----+
// # of bytes: 1 1 2 4
//
// VN is the version of the reply code and should be 0. CD is the result
// code with one of the following values:
//
// 90: request granted
// 91: request rejected or failed
// 92: request rejected becasue SOCKS server cannot connect to
// identd on the client
// 93: request rejected because the client program and identd
// report different user-ids
//
// The remaining fields are ignored.
//
// The SOCKS server closes its connection immediately after notifying
// the client of a failed or rejected request. For a successful request,
// the SOCKS server gets ready to relay traffic on both directions. This
// enables the client to do I/O on its connection as if it were directly
// connected to the application server.
if(reply==90)
{
// Request granted
if(_OUTPUT_VERBOSE)
outputProxyMessage(__tr2qs("Proxy response: target data OK: request granted"));
connectedToIrcServer();
} else {
//Request rejected
int err;
switch(reply)
{
case 91: err = KviError_proxyReply91RequestFailed; break;
case 92: err = KviError_proxyReply92IdentFailed; break;
case 93: err = KviError_proxyReply93IdentNotMatching; break;
default: err = KviError_unrecognizedProxyReply; break;
}
raiseError(err);
reset();
}
// Just looked out of the window...
// Hmmmm...strange light outside...
// Looked at the clock...6:34 !
// I think I'll go sleep.... :)
}
void KviIrcSocket::proxyHandleHttpFinalReply(const char * buffer,int bufLen)
{
// Escape character is '^]'.
// CONNECT warszawa.irc.pl:6667 HTTP/1.0
//
// HTTP/1.0 200 Connection established
KviStr tmp = buffer;
// FIXME: #warning "We could even show the proxy output here...!"
tmp.cutFromFirst('\n');
tmp.stripWhiteSpace();
if(kvi_strEqualCIN(tmp.ptr(),"HTTP",4))
{
int idx = tmp.findFirstIdx(" 200 ");
if(idx != -1)
{
if(idx == tmp.findFirstIdx(' '))
{
TQString msg = __tr2qs("Proxy response: ");
msg += tmp.ptr();
if(_OUTPUT_VERBOSE)
outputProxyMessage(msg);
connectedToIrcServer();
return;
}
}
}
outputProxyError(__tr2qs("Proxy said something about: \n"));
outputProxyMessage(m_pConsole->decodeText(buffer));
//Read HTTP error page and show it
if(m_pWsn)
{
delete m_pWsn;
m_pWsn = 0;
}
if(m_pRsn)
{
delete m_pRsn;
m_pRsn = 0;
}
m_pRsn = new TQSocketNotifier((int)m_sock,TQSocketNotifier::Read);
TQObject::connect(m_pRsn,TQT_SIGNAL(activated(int)),this,TQT_SLOT(readHttpProxyErrorData(int)));
m_pRsn->setEnabled(true);
setState(ProxyHttpError);
// raiseError(KviError_proxyHttpFailure);
// reset();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SSL HANDSHAKE
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef COMPILE_SSL_SUPPORT
void KviIrcSocket::printSSLPeerCertificate()
{
KviSSLCertificate * c = m_pSSL->getPeerCertificate();
if(c)
{
//m_pConsole->socketEvent(SSLCertificate,(void *)c);
if(_OUTPUT_VERBOSE)
KviSSLMaster::printSSLCertificate(m_pConsole,__tr("Server X509 certificate"),c);
delete c;
} else {
if(_OUTPUT_VERBOSE)
outputSSLMessage(__tr2qs("The server didn't provide a certificate"));
}
}
void KviIrcSocket::printSSLCipherInfo()
{
KviSSLCipherInfo * ci = m_pSSL->getCurrentCipherInfo();
if(ci)
{
//m_pConsole->socketEvent(SSLCipherInfo,(void *)ci);
KviSSLMaster::printSSLCipherInfo(m_pConsole,__tr2qs("Current transmission cipher"),ci);
delete ci;
} else {
if(_OUTPUT_VERBOSE)
outputSSLMessage(__tr2qs("Unable to determine the current cipher"));
}
}
void KviIrcSocket::raiseSSLError()
{
KviStr buffer;
while(m_pSSL->getLastErrorString(buffer))
{
outputSSLError(buffer.ptr());
}
}
#endif
void KviIrcSocket::doSSLHandshake(int)
{
#ifdef COMPILE_SSL_SUPPORT
__range_valid(m_pSSL);
if(m_pRsn)
{
delete m_pRsn;
m_pRsn = 0;
}
if(m_pWsn)
{
delete m_pWsn;
m_pWsn = 0;
}
if(!m_pSSL)
{
tqDebug("Ops... I've lost the SSL class ?");
reset();
return; // ops ?
}
switch(m_pSSL->connect())
{
case KviSSL::Success:
// done!
printSSLCipherInfo();
printSSLPeerCertificate();
linkUp();
break;
case KviSSL::WantRead:
m_pRsn = new TQSocketNotifier((int)m_sock,TQSocketNotifier::Read);
TQObject::connect(m_pRsn,TQT_SIGNAL(activated(int)),this,TQT_SLOT(doSSLHandshake(int)));
m_pRsn->setEnabled(true);
break;
case KviSSL::WantWrite:
m_pWsn = new TQSocketNotifier((int)m_sock,TQSocketNotifier::Write);
TQObject::connect(m_pWsn,TQT_SIGNAL(activated(int)),this,TQT_SLOT(doSSLHandshake(int)));
m_pWsn->setEnabled(true);
break;
case KviSSL::RemoteEndClosedConnection:
raiseError(KviError_remoteEndClosedConnection);
reset();
break;
case KviSSL::SSLError:
raiseSSLError();
raiseError(KviError_SSLError);
reset();
break;
case KviSSL::SyscallError:
{
// syscall problem
int err = kvi_socket_error();
if(!kvi_socket_recoverableError(err))
{
// Declare problems :)
raiseError((err ? KviError::translateSystemError(err) : KviError_unknownError));
} else {
// can recover ? (EAGAIN , EINTR ?)
m_pWsn = new TQSocketNotifier((int)m_sock,TQSocketNotifier::Write);
TQObject::connect(m_pWsn,TQT_SIGNAL(activated(int)),this,TQT_SLOT(doSSLHandshake(int)));
m_pWsn->setEnabled(true);
return;
}
reset();
}
break;
default:
raiseError(KviError_SSLError);
reset();
break;
}
#else //!COMPILE_SSL_SUPPORT
tqDebug("Ops.. ssl handshake without ssl support!...aborting!");
exit(-1);
#endif //!COMPILE_SSL_SUPPORT
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// LINK UP
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void KviIrcSocket::linkUp()
{
setState(Connected);
// the last check
if(m_sock == KVI_INVALID_SOCKET)return; // ops...disconnected in setState() ????
// ok.. it seems that we're really up and running now!
if(m_pWsn)
{
delete m_pWsn;
m_pWsn = 0;
}
if(m_pRsn)
{
delete m_pRsn;
m_pRsn = 0;
}
m_pRsn = new TQSocketNotifier((int)m_sock,TQSocketNotifier::Read);
TQObject::connect(m_pRsn,TQT_SIGNAL(activated(int)),this,TQT_SLOT(readData(int)));
m_pRsn->setEnabled(true);
// yahoo!
}
void KviIrcSocket::readData(int)
{
//read data
char buffer[1025];
int readLength;
#ifdef COMPILE_SSL_SUPPORT
if(m_pSSL)
{
readLength = m_pSSL->read(buffer,1024);
if(readLength <= 0)
{
// ssl error....?
switch(m_pSSL->getProtocolError(readLength))
{
case KviSSL::ZeroReturn:
readLength = 0;
break;
case KviSSL::WantRead:
case KviSSL::WantWrite:
// hmmm...
return;
break;
case KviSSL::SyscallError:
{
int iE = m_pSSL->getLastError(true);
if(iE != 0)
{
raiseSSLError();
raiseError(KviError_SSLError);
reset();
return;
}
}
break;
case KviSSL::SSLError:
raiseSSLError();
raiseError(KviError_SSLError);
reset();
return;
break;
default:
raiseError(KviError_SSLError);
reset();
return;
break;
}
handleInvalidSocketRead(readLength);
return;
}
} else {
#endif
readLength = kvi_socket_recv(m_sock,buffer,1024);
if(readLength <= 0)
{
handleInvalidSocketRead(readLength);
return;
}
#ifdef COMPILE_SSL_SUPPORT
}
#endif
//terminate our buffer
(*(buffer+readLength))='\0';
m_uReadBytes += readLength;
// Shut up the socket notifier
// in case that we enter in a local loop somewhere
// while processing data...
m_pRsn->setEnabled(false);
// shut also the flushing of the message queue
// in this way we prevent disconnect detection
// during the processing of a message effectively
// making it always an asynchronous event.
m_bInProcessData = true;
m_pLink->processData(buffer,readLength);
// after this line there should be nothing that relies
// on the "connected" state of this socket.
// It may happen that it has been reset() in the middle of the processData() call
// and (unverified) it might have been even deleted.
// re-enable the socket notifier... (if it's still there)
if(m_pRsn)m_pRsn->setEnabled(true);
// and the message queue flushing
m_bInProcessData = false;
// and flush the queue too!
if(m_pSendQueueHead)flushSendQueue();
}
/*
void KviIrcSocket::processData(char * buffer,int)
{
char *p=buffer;
char *beginOfCurData = buffer;
int bufLen = 0;
char *messageBuffer = (char *)kvi_malloc(1);
// Shut up the socket notifier
// in case that we enter in a local loop somewhere
// while processing data...
m_pRsn->setEnabled(false);
// shut also the flushing of the message queue
// in this way we prevent disconnect detection
// during the processing of a message effectively
// making it always an asynchronous event.
m_bInProcessData = true;
while(*p)
{
if((*p == '\r' )||(*p == '\n'))
{
//found a CR or LF...
//prepare a message buffer
bufLen = p - beginOfCurData;
//check for previous unterminated data
if(m_uReadBufferLen > 0){
__range_valid(m_pReadBuffer);
messageBuffer = (char *)kvi_realloc(messageBuffer,bufLen + m_uReadBufferLen + 1);
kvi_memmove(messageBuffer,m_pReadBuffer,m_uReadBufferLen);
kvi_memmove((void *)(messageBuffer + m_uReadBufferLen),beginOfCurData,bufLen);
*(messageBuffer + bufLen + m_uReadBufferLen) = '\0';
m_uReadBufferLen = 0;
kvi_free(m_pReadBuffer);
m_pReadBuffer = 0;
} else {
__range_invalid(m_pReadBuffer);
messageBuffer = (char *)kvi_realloc(messageBuffer,bufLen + 1);
kvi_memmove(messageBuffer,beginOfCurData,bufLen);
*(messageBuffer + bufLen) = '\0';
}
m_uReadPackets++;
// FIXME: actually it can happen that the socket gets disconnected
// in a incomingMessage() call.
// The problem might be that some other parts of kvirc assume
// that the irc context still exists after a failed write to the socket
// (some parts don't even check the return value!)
// If the problem presents itself again then the solution is:
// disable queue flushing for the "incomingMessage" call
// and just call queue_insertMessage()
// then after the call terminates flush the queue (eventually detecting
// the disconnect and thus destroying the irc context).
// For now we try to rely on the remaining parts to handle correctly
// such conditions. Let's see...
m_pConsole->incomingMessage(messageBuffer);
if(m_state != Connected)
{
// Disconnected in KviConsole::incomingMessage() call.
// This may happen for several reasons (local event loop
// with the user hitting the disconnect button, a scripting
// handler event that disconnects explicitly)
//
// We handle it by simply returning control to readData() which
// will return immediately (and safely) control to TQt
kvi_free(messageBuffer);
m_bInProcessData = false;
return;
}
while(*p && ((*p=='\r')||(*p=='\n')) )p++;
beginOfCurData = p;
} else p++;
}
//now *p == '\0'
//beginOfCurData points to '\0' if we have
//no more stuff to parse , or points to something
//different than '\r' or '\n'...
if(*beginOfCurData)
{
//Have remaining data...in the local buffer
bufLen = p - beginOfCurData;
if(m_uReadBufferLen > 0){
//and there was more stuff saved... (really slow connection)
__range_valid(m_pReadBuffer);
m_pReadBuffer =(char *)kvi_realloc(m_pReadBuffer,m_uReadBufferLen + bufLen);
kvi_memmove((void *)(m_pReadBuffer+m_uReadBufferLen),beginOfCurData,bufLen);
m_uReadBufferLen += bufLen;
} else {
//
__range_invalid(m_pReadBuffer);
m_uReadBufferLen = bufLen;
m_pReadBuffer =(char *)kvi_malloc(m_uReadBufferLen);
kvi_memmove(m_pReadBuffer,beginOfCurData,m_uReadBufferLen);
}
//The m_pReadBuffer contains at max 1 irc message...
//that can not be longer than 510 bytes (the message is not CRLF terminated)
// FIXME: Is this limit *really* valid on all servers ?
if(m_uReadBufferLen > 510)tqDebug("WARNING : Receiving an invalid irc message from server.");
}
kvi_free(messageBuffer);
// re-enable the socket notifier...
m_pRsn->setEnabled(true);
// and the message queue flushing
m_bInProcessData = false;
// and flush the queue too!
if(m_pSendQueueHead)flushSendQueue();
}
*/
void KviIrcSocket::abort()
{
// flush the send queue if possible (and if not yet disconnected in fact)
if(m_state == Connected)flushSendQueue();
if(m_state != Idle)
raiseError(KviError_operationAborted);
// and reset
reset();
}
//=== handleInvalidSocketRead ===============================================//
//
// Checks if the socket error is a transient error
// If it is not a transient error it resets the socket
// and fires the appropriate event.
// Otherwise it does nothing.
//
void KviIrcSocket::handleInvalidSocketRead(int readedLength)
{
__range_valid(readedLength <= 0);
if(readedLength==0)
{
raiseError(KviError_remoteEndClosedConnection);
reset();
} else {
//check for transmission errors
int err = kvi_socket_error();
#ifdef COMPILE_ON_WINDOWS
if((err != EAGAIN) && (err != EINTR) && (err != WSAEWOULDBLOCK))
#else
if((err != EAGAIN) && (err != EINTR))
#endif
{
if(err > 0)raiseError((KviError::translateSystemError(err)));
else raiseError(KviError_remoteEndClosedConnection);
reset();
} //else transient error...wait again...
}
}
//=== data queue functions ==================================================//
//
// queue_insertMessage : appends a KviIrcSocketMsgEntry to the tail of
// the message queue. The next_ptr for this message is set to 0.
// queue_removeMessage : removes a message from the head of the queue.
//
void KviIrcSocket::queue_insertMessage(KviIrcSocketMsgEntry *msg_ptr)
{
__range_valid(msg_ptr);
__range_valid(msg_ptr->data_ptr);
__range_valid(msg_ptr->data_len);
msg_ptr->next_ptr = 0;
if(m_pSendQueueHead)
{
m_pSendQueueTail->next_ptr = msg_ptr;
m_pSendQueueTail = msg_ptr;
} else {
m_pSendQueueHead = msg_ptr;
m_pSendQueueTail = msg_ptr;
}
}
void KviIrcSocket::free_msgEntry(KviIrcSocketMsgEntry * e)
{
if(e->pData)delete e->pData;
e->pData = 0;
kvi_free(e);
}
bool KviIrcSocket::queue_removeMessage()
{
__range_valid(m_pSendQueueTail);
__range_valid(m_pSendQueueHead);
if(m_pSendQueueHead->pData)delete m_pSendQueueHead->pData;
KviIrcSocketMsgEntry *aux_ptr = m_pSendQueueHead;
m_pSendQueueHead = aux_ptr->next_ptr;
kvi_free((void *)aux_ptr);
if(m_pSendQueueHead == 0)
{
m_pSendQueueTail = 0;
return false;
} else return true;
}
void KviIrcSocket::queue_removeAllMessages()
{
if(m_pSendQueueHead)while(queue_removeMessage());
}
//=== flushSendQueue ========================================================//
//
// Attempts to send as much as possible to the server
// If fails (happens only on really lagged servers)
// calls itself with a TQTimer shot after KVI_OPTION_UINT(KviOption_uintSocketQueueFlushTimeout) ms
// to retry again...
//
void KviIrcSocket::flushSendQueue()
{
// If we're called from the flush timer , stop it
if(m_pFlushTimer->isActive())m_pFlushTimer->stop();
// Ok...have something to send...
__range_valid(m_state != Idle);
struct timeval curTime;
while(m_pSendQueueHead)
{
if(KVI_OPTION_BOOL(KviOption_boolLimitOutgoingTraffic))
{
kvi_gettimeofday(&curTime,0);
int timeDiff = curTime.tv_usec - m_tAntiFloodLastMessageTime.tv_usec;
timeDiff += (curTime.tv_sec - m_tAntiFloodLastMessageTime.tv_sec) * 1000000;
if(((unsigned int)timeDiff) < KVI_OPTION_UINT(KviOption_uintOutgoingTrafficLimitUSeconds))
{
// need to wait for a while....
m_pFlushTimer->start(((KVI_OPTION_UINT(KviOption_uintOutgoingTrafficLimitUSeconds) - timeDiff) / 1000) + 1);
return;
} // else can send
}
// Write one data buffer...
int result;
#ifdef COMPILE_SSL_SUPPORT
if(m_pSSL)
{
result = m_pSSL->write((char *)(m_pSendQueueHead->pData->data()),m_pSendQueueHead->pData->size());
} else {
#endif
result = kvi_socket_send(m_sock,(char *)(m_pSendQueueHead->pData->data()),m_pSendQueueHead->pData->size());
#ifdef COMPILE_SSL_SUPPORT
}
#endif
if(result == (int)m_pSendQueueHead->pData->size())
{
// Succesfull send...remove this data buffer
m_uSentPackets++;
m_uSentBytes += result;
//if(m_pConsole->hasMonitors())outgoingMessageNotifyMonitors((char *)(m_pSendQueueHead->pData->data()),result);
queue_removeMessage();
if(KVI_OPTION_BOOL(KviOption_boolLimitOutgoingTraffic))
{
m_tAntiFloodLastMessageTime.tv_sec = curTime.tv_sec;
m_tAntiFloodLastMessageTime.tv_usec = curTime.tv_usec;
}
// And try next buffer...
continue;
} else {
// Something wrong ?
#ifdef COMPILE_SSL_SUPPORT
if(result <= 0)
{
if(m_pSSL)
{
// ops...might be an SSL error
switch(m_pSSL->getProtocolError(result))
{
case KviSSL::WantWrite:
case KviSSL::WantRead:
// Async continue...
m_pFlushTimer->start(KVI_OPTION_UINT(KviOption_uintSocketQueueFlushTimeout));
return;
break;
case KviSSL::SyscallError:
if(result == 0)
{
raiseSSLError();
raiseError(KviError_remoteEndClosedConnection);
reset();
return;
} else {
int iSSLErr = m_pSSL->getLastError(true);
if(iSSLErr != 0)
{
raiseSSLError();
raiseError(KviError_SSLError);
reset();
return;
} else {
goto handle_system_error;
}
}
break;
case KviSSL::SSLError:
raiseSSLError();
raiseError(KviError_SSLError);
reset();
return;
break;
default:
raiseError(KviError_SSLError);
reset();
return;
break;
}
}
} else {
#else //!COMPILE_SSL_SUPPORT
if(result >= 0)
{
if(result > 0)
{
#endif //!COMPILE_SSL_SUPPORT
// Partial send...need to finish it later
m_pSendQueueHead->pData->remove(result);
m_uSentBytes += result;
if(_OUTPUT_VERBOSE)
outputSocketWarning(__tr2qs("Partial socket write: packet broken into smaller pieces."));
#ifndef COMPILE_SSL_SUPPORT
}
#endif //!COMPILE_SSL_SUPPORT
// Async continue...
m_pFlushTimer->start(KVI_OPTION_UINT(KviOption_uintSocketQueueFlushTimeout));
return;
}
handle_system_error:
// Oops...error ?
int err = kvi_socket_error();
#ifdef COMPILE_ON_WINDOWS
if((err == EAGAIN) || (err == EINTR) || (err == WSAEWOULDBLOCK))
#else
if((err == EAGAIN)||(err == EINTR))
#endif
{
// Transient error...partial send as before...
if(_OUTPUT_VERBOSE)
outputSocketWarning(__tr2qs("Partial socket write: packet broken into smaller pieces."));
// Async continue...
m_pFlushTimer->start(KVI_OPTION_UINT(KviOption_uintSocketQueueFlushTimeout));
return;
} else {
// Disconnected... :(
raiseError((KviError::translateSystemError(err)));
reset();
return;
}
}
}
//flushed completely ...
}
bool KviIrcSocket::getLocalHostIp(TQString &szIp,bool bIpV6)
{
if(m_state != Connected)return false;
if(bIpV6)
{
#ifdef COMPILE_IPV6_SUPPORT
struct sockaddr_in6 name;
int len = sizeof(name);
if(!kvi_socket_getsockname(m_sock, (struct sockaddr *)&name,&len))return false;
//I assume that getsockname returns data in Network byte order...
//The man page misses to specify that...
if(!kvi_binaryIpToStringIp_V6(name.sin6_addr,szIp))return false;
return true;
#else
return false; // no support
#endif
}
struct sockaddr_in name;
int len = sizeof(name);
if(!kvi_socket_getsockname(m_sock, (struct sockaddr *)&name,&len))return false;
//I assume that getsockname returns data in Network byte order...
//The man page misses to specify that...
if(!kvi_binaryIpToStringIp(name.sin_addr,szIp))return false;
return true;
}
/*
bool KviIrcSocket::sendFmtData(const char *fmt,...)
{
if(m_state != Connected)return false;
//new buffer
KviIrcSocketMsgEntry *ptr = (KviIrcSocketMsgEntry *)kvi_malloc(sizeof(KviIrcSocketMsgEntry));
ptr->pData = new KviDataBuffer(512);
kvi_va_list(list);
kvi_va_start(list,fmt);
bool bTruncated;
//sprintf the buffer up to 512 chars (adds a CRLF too)
int iLen = kvi_irc_vsnprintf((char *)(ptr->pData->data()),fmt,list,&bTruncated);
kvi_va_end(list);
//adjust the buffer size
if(iLen < 512)ptr->pData->resize(iLen);
if(bTruncated)
{
if(_OUTPUT_VERBOSE)
outputSocketWarning(__tr2qs("Socket message truncated to 512 bytes."));
}
queue_insertMessage(ptr);
if(!m_bInProcessData)flushSendQueue();
return (m_state != Idle);
}
*/
/*
bool KviIrcSocket::sendData(const char *buffer,int buflen)
{
if(m_state != Connected)return false;
//new buffer
KviIrcSocketMsgEntry *ptr = (KviIrcSocketMsgEntry *)kvi_malloc(sizeof(KviIrcSocketMsgEntry));
if(buflen < 0)buflen = strlen(buffer);
if(buflen > 510)
{
buflen = 510;
if(_OUTPUT_VERBOSE)
outputSocketWarning(__tr2qs("Socket message truncated to 512 bytes."));
}
ptr->pData = new KviDataBuffer(buflen + 2);
kvi_memmove(ptr->pData->data(),buffer,buflen);
*(ptr->pData->data()+buflen)='\r';
*(ptr->pData->data()+buflen+1)='\n';
queue_insertMessage(ptr);
if(!m_bInProcessData)flushSendQueue();
return (m_state != Idle);
}
*/
bool KviIrcSocket::sendRawData(const char *buffer,int buflen)
{
if((m_state == Idle) || (m_state == Connecting))return false;
//new buffer
KviIrcSocketMsgEntry *ptr = (KviIrcSocketMsgEntry *)kvi_malloc(sizeof(KviIrcSocketMsgEntry));
ptr->pData = new KviDataBuffer(buflen);
kvi_memmove(ptr->pData->data(),buffer,buflen);
queue_insertMessage(ptr);
if(!m_bInProcessData)flushSendQueue();
return (m_state != Idle);
}
bool KviIrcSocket::sendPacket(KviDataBuffer * pData)
{
if(m_state != Connected)
{
delete pData;
pData = 0;
return false;
}
KviIrcSocketMsgEntry *ptr = (KviIrcSocketMsgEntry *)kvi_malloc(sizeof(KviIrcSocketMsgEntry));
ptr->pData = pData;
queue_insertMessage(ptr);
if(!m_bInProcessData)flushSendQueue();
return (m_state != Idle);
}