summaryrefslogtreecommitdiffstats
path: root/src/modules/dcc/marshal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/dcc/marshal.cpp')
-rw-r--r--src/modules/dcc/marshal.cpp647
1 files changed, 647 insertions, 0 deletions
diff --git a/src/modules/dcc/marshal.cpp b/src/modules/dcc/marshal.cpp
new file mode 100644
index 00000000..9aedec41
--- /dev/null
+++ b/src/modules/dcc/marshal.cpp
@@ -0,0 +1,647 @@
+//
+// 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)
+: QObject(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
+// debug("MARSHAL RESETTING (SSL=%d)",m_pSSL);
+ if(m_pSSL)
+ {
+// debug("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 QString &ip,const QString &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 QTimer();
+ connect(m_pTimeoutTimer,SIGNAL(timeout()),this,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))
+ {
+// debug("GETSOCKNAMEOK");
+ m_szPort.setNum(sareal.port());
+ m_uPort = sareal.port();
+// debug("REALPORT %u",m_uPort);
+ } else {
+// debug("GETSOCKNAMEFAILED");
+ }
+
+ // and setup the READ notifier...
+ m_pSn = new QSocketNotifier(m_fd,QSocketNotifier::Read);
+ QObject::connect(m_pSn,SIGNAL(activated(int)),this,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 QTimer();
+ connect(m_pTimeoutTimer,SIGNAL(timeout()),this,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 QTimer();
+ connect(m_pTimeoutTimer,SIGNAL(timeout()),this,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 QSocketNotifier(m_fd,QSocketNotifier::Write);
+ QObject::connect(m_pSn,SIGNAL(activated(int)),this,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 QTimer();
+ connect(m_pTimeoutTimer,SIGNAL(timeout()),this,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
+// debug("DO SSL HANDSHAKE");
+ if(m_pSn)
+ {
+ delete m_pSn;
+ m_pSn = 0;
+ }
+
+ if(!m_pSSL)
+ {
+ debug("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!
+// debug("EMITTING CONNECTED");
+ emit connected();
+// debug("CONNECTED EMITTED");
+ break;
+ case KviSSL::WantRead:
+ m_pSn = new QSocketNotifier((int)m_fd,QSocketNotifier::Read);
+ QObject::connect(m_pSn,SIGNAL(activated(int)),this,SLOT(doSSLHandshake(int)));
+ m_pSn->setEnabled(true);
+ break;
+ case KviSSL::WantWrite:
+ m_pSn = new QSocketNotifier((int)m_fd,QSocketNotifier::Write);
+ QObject::connect(m_pSn,SIGNAL(activated(int)),this,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 QSocketNotifier((int)m_fd,QSocketNotifier::Write);
+ QObject::connect(m_pSn,SIGNAL(activated(int)),this,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
+ debug("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"