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/modules/dcc/marshal.cpp

648 lines
14 KiB

//
// File : marshal.cpp
// Creation date : Sun Sep 17 2000 15:59:11 by Szymon Stefanek
//
// This file is part of the KVirc irc client distribution
// Copyright (C) 1999-2000 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.
//
#include "marshal.h"
#include "kvi_settings.h"
#include "kvi_netutils.h"
#include "kvi_error.h"
#include "kvi_options.h"
#include "kvi_locale.h"
#include "kvi_memmove.h"
#include "kvi_socket.h"
#include "kvi_fileutils.h"
#include <stdlib.h> //for exit()
KviDccMarshal::KviDccMarshal(KviDccMarshalOutputContext * ctx)
: TQObject(0,"dcc_marshal")
{
m_pSn = 0;
m_fd = KVI_INVALID_SOCKET;
m_pTimeoutTimer = 0;
m_bIpV6 = false;
m_pOutputContext = ctx;
#ifdef COMPILE_SSL_SUPPORT
m_pSSL = 0;
#endif
m_szIp = "";
m_szPort = "";
m_szSecondaryIp = "";
m_szSecondaryPort = "";
}
KviDccMarshal::~KviDccMarshal()
{
reset();
}
kvi_socket_t KviDccMarshal::releaseSocket()
{
kvi_socket_t aux_fd = m_fd;
m_fd = KVI_INVALID_SOCKET;
return aux_fd;
}
#ifdef COMPILE_SSL_SUPPORT
KviSSL * KviDccMarshal::releaseSSL()
{
KviSSL * theSSL = m_pSSL;
m_pSSL = 0;
return theSSL;
}
#endif
void KviDccMarshal::reset()
{
if(m_pSn)
{
delete m_pSn;
m_pSn = 0;
}
if(m_fd != KVI_INVALID_SOCKET)
{
kvi_socket_close(m_fd);
m_fd = KVI_INVALID_SOCKET;
}
#ifdef COMPILE_SSL_SUPPORT
// tqDebug("MARSHAL RESETTING (SSL=%d)",m_pSSL);
if(m_pSSL)
{
// tqDebug("MARSHAL CLEARING THE SSL");
KviSSLMaster::freeSSL(m_pSSL);
m_pSSL = 0;
}
#endif
if(m_pTimeoutTimer)
{
delete m_pTimeoutTimer;
m_pTimeoutTimer = 0;
}
m_bIpV6 = false;
}
int KviDccMarshal::dccListen(const TQString &ip,const TQString &port,bool bUseTimeout,bool bUseSSL)
{
if(m_fd != KVI_INVALID_SOCKET)return KviError_anotherConnectionInProgress;
m_szIp = ip;
m_szPort = port;
m_bOutgoing = false;
m_bUseTimeout = bUseTimeout;
#ifdef COMPILE_SSL_SUPPORT
m_bUseSSL = bUseSSL;
#else
if(bUseSSL)return KviError_noSSLSupport;
#endif
if(m_pTimeoutTimer)delete m_pTimeoutTimer;
m_pTimeoutTimer = new TQTimer();
connect(m_pTimeoutTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(doListen()));
m_pTimeoutTimer->start(100,true);
return KviError_success;
}
void KviDccMarshal::doListen()
{
if(m_pTimeoutTimer)
{
delete m_pTimeoutTimer;
m_pTimeoutTimer = 0;
}
// Check the address type
if(!kvi_isValidStringIp(m_szIp))
{
#ifdef COMPILE_IPV6_SUPPORT
if(!kvi_isValidStringIp_V6(m_szIp))
{
emit error(KviError_invalidIpAddress);
return;
} else m_bIpV6 = true;
#else
emit error(KviError_invalidIpAddress);
return;
#endif
}
bool bOk;
m_uPort = m_szPort.toUInt(&bOk);
if(!bOk)
{
emit error(KviError_invalidPortNumber);
return;
}
#ifndef COMPILE_IPV6_SUPPORT
if(m_bIpV6)
{
emit error(KviError_noIpV6Support);
return;
}
#endif
#ifdef COMPILE_IPV6_SUPPORT
m_fd = kvi_socket_create(m_bIpV6 ? KVI_SOCKET_PF_INET6 : KVI_SOCKET_PF_INET,
KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP);
#else
m_fd = kvi_socket_create(KVI_SOCKET_PF_INET,KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP);
#endif
if(m_fd == KVI_INVALID_SOCKET)
{
emit error(KviError_socketCreationFailed);
return;
}
if((!KVI_OPTION_BOOL(KviOption_boolUserDefinedPortRange)) || (m_uPort != 0))
{
#ifdef COMPILE_IPV6_SUPPORT
KviSockaddr sa(m_szIp,m_uPort,m_bIpV6);
#else
KviSockaddr sa(m_szIp,m_uPort,false);
#endif
if(!sa.socketAddress())
{
reset();
emit error(KviError_bindFailed);
return;
}
if(!kvi_socket_bind(m_fd,sa.socketAddress(),sa.addressLength()))
{
reset();
emit error(KviError_bindFailed);
return;
}
} else {
m_uPort = KVI_OPTION_UINT(KviOption_uintDccMinPort);
if(KVI_OPTION_UINT(KviOption_uintDccMaxPort) > 65535)KVI_OPTION_UINT(KviOption_uintDccMaxPort) = 65535;
bool bBindSuccess;
do {
#ifdef COMPILE_IPV6_SUPPORT
KviSockaddr sa(m_szIp,m_uPort,m_bIpV6);
#else
KviSockaddr sa(m_szIp,m_uPort,false);
#endif
if(!sa.socketAddress())
{
reset();
emit error(KviError_bindFailed);
return;
}
bBindSuccess = kvi_socket_bind(m_fd,sa.socketAddress(),sa.addressLength());
if(!bBindSuccess)
{
if(m_uPort == 65535)
{
reset();
emit error(KviError_bindFailed);
return;
}
m_uPort++;
}
} while((!bBindSuccess) && (m_uPort <= KVI_OPTION_UINT(KviOption_uintDccMaxPort)));
if(!bBindSuccess)
{
reset();
emit error(KviError_bindFailed);
return;
}
}
if(!kvi_socket_listen(m_fd,1))
{
reset();
emit error(KviError_listenFailed);
return;
}
// Reread the port in case we're binding to a random one (0)
#ifdef COMPILE_IPV6_SUPPORT
KviSockaddr sareal(0,m_bIpV6);
#else
KviSockaddr sareal(0,false);
#endif
int size = sareal.addressLength();
if(kvi_socket_getsockname(m_fd,sareal.socketAddress(),&size))
{
// tqDebug("GETSOCKNAMEOK");
m_szPort.setNum(sareal.port());
m_uPort = sareal.port();
// tqDebug("REALPORT %u",m_uPort);
} else {
// tqDebug("GETSOCKNAMEFAILED");
}
// and setup the READ notifier...
m_pSn = new TQSocketNotifier(m_fd,TQSocketNotifier::Read);
TQObject::connect(m_pSn,TQT_SIGNAL(activated(int)),this,TQT_SLOT(snActivated(int)));
m_pSn->setEnabled(true);
// set the timer
if(KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) < 5)
KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) = 5;
if(m_bUseTimeout)
{
m_pTimeoutTimer = new TQTimer();
connect(m_pTimeoutTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(connectionTimedOut()));
m_pTimeoutTimer->start(KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) * 1000,true);
}
// and wait for connect
emit inProgress();
}
int KviDccMarshal::dccConnect(const char * ip,const char * port,bool bUseTimeout,bool bUseSSL)
{
if(m_fd != KVI_INVALID_SOCKET)return KviError_anotherConnectionInProgress;
m_bUseTimeout = bUseTimeout;
m_szIp = ip;
m_szPort = port;
m_bOutgoing = true;
#ifdef COMPILE_SSL_SUPPORT
m_bUseSSL = bUseSSL;
#else
if(bUseSSL)return KviError_noSSLSupport;
#endif
if(m_pTimeoutTimer)delete m_pTimeoutTimer;
m_pTimeoutTimer = new TQTimer();
connect(m_pTimeoutTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(doConnect()));
m_pTimeoutTimer->start(100,true);
return KviError_success;
}
void KviDccMarshal::doConnect()
{
if(m_pTimeoutTimer)
{
delete m_pTimeoutTimer;
m_pTimeoutTimer = 0;
}
// Check the address type
if(!kvi_isValidStringIp(m_szIp))
{
#ifdef COMPILE_IPV6_SUPPORT
if(!kvi_isValidStringIp_V6(m_szIp))
{
emit error(KviError_invalidIpAddress);
return;
} else m_bIpV6 = true;
#else
emit error(KviError_invalidIpAddress);
return;
#endif
}
bool bOk;
m_uPort = m_szPort.toUInt(&bOk);
if(!bOk)
{
emit error(KviError_invalidPortNumber);
return;
}
// create the socket
#ifdef COMPILE_IPV6_SUPPORT
m_fd = kvi_socket_create(m_bIpV6 ? KVI_SOCKET_PF_INET6 : KVI_SOCKET_PF_INET,
KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP);
#else
m_fd = kvi_socket_create(KVI_SOCKET_PF_INET,
KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP);
#endif
if(m_fd == KVI_INVALID_SOCKET)
{
emit error(KviError_socketCreationFailed);
return;
}
// make it non blocking
if(!kvi_socket_setNonBlocking(m_fd))
{
reset();
emit error(KviError_asyncSocketFailed);
return;
}
// fill the sockaddr structure
#ifdef COMPILE_IPV6_SUPPORT
KviSockaddr sa(m_szIp,m_uPort,m_bIpV6);
#else
KviSockaddr sa(m_szIp,m_uPort,false);
#endif
if(!sa.socketAddress())
{
reset();
emit error(KviError_socketCreationFailed);
return;
}
if(!kvi_socket_connect(m_fd,sa.socketAddress(),sa.addressLength()))
{
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_fd,SOL_SOCKET,SO_ERROR,
(void *)&sockError,&iSize))sockError=0;
}
// Die
reset();
// And declare problems :)
if(sockError)emit error(KviError::translateSystemError(sockError));
else emit error(KviError_unknownError); //Error 0 ?
return;
}
}
// and setup the WRITE notifier...
m_pSn = new TQSocketNotifier(m_fd,TQSocketNotifier::Write);
TQObject::connect(m_pSn,TQT_SIGNAL(activated(int)),this,TQT_SLOT(snActivated(int)));
m_pSn->setEnabled(true);
// set the timer
if(KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) < 5)
KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) = 5;
if(m_bUseTimeout)
{
m_pTimeoutTimer = new TQTimer();
connect(m_pTimeoutTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(connectionTimedOut()));
m_pTimeoutTimer->start(KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) * 1000,true);
}
// and wait for connect
emit inProgress();
}
void KviDccMarshal::snActivated(int)
{
if(m_pTimeoutTimer)
{
delete m_pTimeoutTimer;
m_pTimeoutTimer = 0;
}
#ifdef COMPILE_IPV6_SUPPORT
struct sockaddr_in6 hostSockAddr6;
#endif
struct sockaddr_in hostSockAddr;
int size = sizeof(hostSockAddr);
struct sockaddr * addr = (struct sockaddr *)&hostSockAddr;
#ifdef COMPILE_IPV6_SUPPORT
if(m_bIpV6)
{
addr = (struct sockaddr *)&hostSockAddr6;
size = sizeof(hostSockAddr6);
}
#endif
if(m_bOutgoing)
{
// outgoing connection (we have called connect())
// Check for errors...
int sockError;
int iSize=sizeof(int);
if(!kvi_socket_getsockopt(m_fd,SOL_SOCKET,SO_ERROR,(void *)&sockError,&iSize))sockError = -1;
if(sockError != 0)
{
//failed
if(sockError > 0)sockError = KviError::translateSystemError(sockError);
else sockError = KviError_unknownError; //Error 0 ?
reset();
emit error(sockError);
return;
}
//Succesfully connected...
delete m_pSn;
m_pSn = 0;
// get the local address
if(!kvi_socket_getsockname(m_fd,addr,&size))
{
m_szSecondaryIp = "localhost";
m_szSecondaryPort = __tr2qs_ctx("unknown","dcc");
} else {
#ifdef COMPILE_IPV6_SUPPORT
if(m_bIpV6)
{
m_szSecondaryPort.setNum(ntohs(((struct sockaddr_in6 *)addr)->sin6_port));
if(!kvi_binaryIpToStringIp_V6(((struct sockaddr_in6 *)addr)->sin6_addr,m_szSecondaryIp))
m_szSecondaryIp = "localhost";
} else {
#endif
m_szSecondaryPort.setNum(ntohs(((struct sockaddr_in *)addr)->sin_port));
if(!kvi_binaryIpToStringIp(((struct sockaddr_in *)addr)->sin_addr,m_szSecondaryIp))
m_szSecondaryIp = "localhost";
#ifdef COMPILE_IPV6_SUPPORT
}
#endif
}
} else {
// Incoming connection
int newsock = kvi_socket_accept(m_fd,addr,&size);
if(newsock != KVI_INVALID_SOCKET)
{
// Connected
delete m_pSn;
m_pSn = 0;
#ifdef COMPILE_IPV6_SUPPORT
if(m_bIpV6)
{
m_szSecondaryPort.setNum(ntohs(((struct sockaddr_in6 *)addr)->sin6_port));
if(!kvi_binaryIpToStringIp_V6(((struct sockaddr_in6 *)addr)->sin6_addr,m_szSecondaryIp))
m_szSecondaryIp = __tr2qs_ctx("unknown","dcc");
} else {
#endif
m_szSecondaryPort.setNum(ntohs(((struct sockaddr_in *)addr)->sin_port));
if(!kvi_binaryIpToStringIp(((struct sockaddr_in *)addr)->sin_addr,m_szSecondaryIp))
m_szSecondaryIp = __tr2qs_ctx("unknown","dcc");
#ifdef COMPILE_IPV6_SUPPORT
}
#endif
kvi_socket_close(m_fd);
m_fd = newsock;
if(!kvi_socket_setNonBlocking(m_fd))
{
reset();
emit error(KviError_asyncSocketFailed);
return;
}
} else {
// Huh ?.. wait for the next notifier call
return;
}
}
#ifdef COMPILE_SSL_SUPPORT
// SSL Handshake needed ?
if(m_bUseSSL)
{
m_pSSL = KviSSLMaster::allocSSL(m_pOutputContext->dccMarshalOutputWindow(),m_fd,m_bOutgoing ? KviSSL::Client : KviSSL::Server,m_pOutputContext->dccMarshalOutputContextString());
if(m_pSSL)
{
emit startingSSLHandshake();
doSSLHandshake(0);
} else {
reset();
emit error(KviError_SSLError);
}
return;
}
#endif
emit connected();
}
void KviDccMarshal::doSSLHandshake(int)
{
#ifdef COMPILE_SSL_SUPPORT
// tqDebug("DO SSL HANDSHAKE");
if(m_pSn)
{
delete m_pSn;
m_pSn = 0;
}
if(!m_pSSL)
{
tqDebug("Ops... I've lost the SSL class ?");
reset();
emit error(KviError_internalError);
return; // ops ?
}
KviSSL::Result r = m_bOutgoing ? m_pSSL->connect() : m_pSSL->accept();
switch(r)
{
case KviSSL::Success:
// done!
// tqDebug("EMITTING CONNECTED");
emit connected();
// tqDebug("CONNECTED EMITTED");
break;
case KviSSL::WantRead:
m_pSn = new TQSocketNotifier((int)m_fd,TQSocketNotifier::Read);
TQObject::connect(m_pSn,TQT_SIGNAL(activated(int)),this,TQT_SLOT(doSSLHandshake(int)));
m_pSn->setEnabled(true);
break;
case KviSSL::WantWrite:
m_pSn = new TQSocketNotifier((int)m_fd,TQSocketNotifier::Write);
TQObject::connect(m_pSn,TQT_SIGNAL(activated(int)),this,TQT_SLOT(doSSLHandshake(int)));
m_pSn->setEnabled(true);
break;
case KviSSL::RemoteEndClosedConnection:
reset();
emit error(KviError_remoteEndClosedConnection);
break;
case KviSSL::SyscallError:
{
// syscall problem
int err = kvi_socket_error();
if(kvi_socket_recoverableError(err))
{
// can recover ? (EAGAIN , EINTR ?)
m_pSn = new TQSocketNotifier((int)m_fd,TQSocketNotifier::Write);
TQObject::connect(m_pSn,TQT_SIGNAL(activated(int)),this,TQT_SLOT(doSSLHandshake(int)));
m_pSn->setEnabled(true);
return;
} else {
// Declare problems :)
reset();
emit error(err ? KviError::translateSystemError(err) : KviError_unknownError);
}
}
break;
default:
{
KviStr buffer;
while(m_pSSL->getLastErrorString(buffer))emit sslError(buffer.ptr());
reset();
emit error(KviError_SSLError);
}
break;
}
#else //!COMPILE_SSL_SUPPORT
tqDebug("Ops.. ssl handshake without ssl support!...aborting!");
exit(-1);
#endif //!COMPILE_SSL_SUPPORT
}
void KviDccMarshal::abort()
{
reset();
}
void KviDccMarshal::connectionTimedOut()
{
reset();
emit error(KviError_connectionTimedOut);
}
#include "m_marshal.moc"