///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // 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 #include #ifndef COMPILE_ON_WINDOWS #include //for gettimeofday() #endif //#include //#include // 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 : HTTP/\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); }