/* Jabber Protocol Plugin for Miranda IM Copyright ( C ) 2002-04 Santithorn Bunchua Copyright ( C ) 2005-11 George Hazan Copyright ( C ) 2007 Maxim Mluhov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or ( at your option ) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Revision : $Revision: 13898 $ Last change on : $Date: 2011-11-02 05:38:43 +0200 (Ср, 02 ноя 2011) $ Last change by : $Author: borkra $ */ #include "jabber.h" #include "jabber_iq.h" #include "jabber_byte.h" #include "jabber_caps.h" #define JABBER_NETWORK_BUFFER_SIZE 4096 ///////////////// Bytestream sending ///////////////////////// JABBER_BYTE_TRANSFER::~JABBER_BYTE_TRANSFER() { filetransfer* pft = ft; if ( pft ) pft->jbt = NULL; mir_free( srcJID ); mir_free( dstJID ); mir_free( streamhostJID ); mir_free( iqId ); mir_free( sid ); xi.destroyNode( iqNode ); // XEP-0065 proxy support mir_free( szProxyHost ); mir_free( szProxyPort ); mir_free( szProxyJid ); mir_free( szStreamhostUsed ); } void CJabberProto::IqResultProxyDiscovery( HXML iqNode, CJabberIqInfo* pInfo ) { JABBER_BYTE_TRANSFER *jbt = ( JABBER_BYTE_TRANSFER * )pInfo->GetUserData(); if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT ) { HXML queryNode = xmlGetChild( iqNode , "query" ); if ( queryNode ) { const TCHAR *queryXmlns = xmlGetAttrValue( queryNode, _T( "xmlns" )); if (queryXmlns && !_tcscmp( queryXmlns, _T(JABBER_FEAT_BYTESTREAMS))) { HXML streamHostNode = xmlGetChild( queryNode , "streamhost" ); if ( streamHostNode ) { const TCHAR *streamJid = xmlGetAttrValue( streamHostNode, _T( "jid" )); const TCHAR *streamHost = xmlGetAttrValue( streamHostNode, _T( "host" )); const TCHAR *streamPort = xmlGetAttrValue( streamHostNode, _T( "port" )); if ( streamJid && streamHost && streamPort ) { jbt->szProxyHost = mir_tstrdup( streamHost ); jbt->szProxyJid = mir_tstrdup( streamJid ); jbt->szProxyPort = mir_tstrdup( streamPort ); jbt->bProxyDiscovered = TRUE; } } } } } else if ( pInfo->GetIqType() == JABBER_IQ_TYPE_ERROR ) jbt->state = JBT_ERROR; if ( jbt->hProxyEvent ) SetEvent( jbt->hProxyEvent ); } void JabberByteSendConnection( HANDLE hConn, DWORD /*dwRemoteIP*/, void* extra ) { CJabberProto* ppro = ( CJabberProto* )extra; TCHAR szPort[8]; JABBER_BYTE_TRANSFER *jbt; int recvResult, bytesParsed; HANDLE hListen; JABBER_LIST_ITEM *item; char* buffer; int datalen; NETLIBCONNINFO connInfo = { sizeof(connInfo) }; CallService(MS_NETLIB_GETCONNECTIONINFO, (WPARAM)hConn, (LPARAM)&connInfo); mir_sntprintf( szPort, SIZEOF( szPort ), _T("%u"), connInfo.wPort ); ppro->Log( "bytestream_send_connection incoming connection accepted: %s", connInfo.szIpPort ); if (( item = ppro->ListGetItemPtr( LIST_BYTE, szPort )) == NULL ) { ppro->Log( "No bytestream session is currently active, connection closed." ); Netlib_CloseHandle( hConn ); return; } jbt = item->jbt; if (( buffer = ( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE )) == NULL ) { ppro->Log( "bytestream_send cannot allocate network buffer, connection closed." ); jbt->state = JBT_ERROR; Netlib_CloseHandle( hConn ); if ( jbt->hEvent != NULL ) SetEvent( jbt->hEvent ); return; } hListen = jbt->hConn; jbt->hConn = hConn; jbt->state = JBT_INIT; datalen = 0; while ( jbt->state!=JBT_DONE && jbt->state!=JBT_ERROR ) { recvResult = Netlib_Recv( hConn, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen, 0 ); if ( recvResult <= 0 ) break; datalen += recvResult; bytesParsed = ppro->ByteSendParse( hConn, jbt, buffer, datalen ); if ( bytesParsed < datalen ) memmove( buffer, buffer+bytesParsed, datalen-bytesParsed ); datalen -= bytesParsed; } if ( jbt->hConn ) Netlib_CloseHandle( jbt->hConn ); ppro->Log( "bytestream_send_connection closing connection" ); jbt->hConn = hListen; mir_free( buffer ); if ( jbt->hEvent != NULL ) SetEvent( jbt->hEvent ); } void CJabberProto::ByteSendThread( JABBER_BYTE_TRANSFER *jbt ) { char* localAddr = NULL; DBVARIANT dbv; TCHAR szPort[8]; HANDLE hEvent = NULL; TCHAR* proxyJid; CJabberIqInfo* pInfo = NULL; int nIqId = 0; Log( "Thread started: type=bytestream_send" ); BOOL bDirect = m_options.BsDirect; if ( m_options.BsProxyManual ) { proxyJid = NULL; if ( !DBGetContactSettingString( NULL, m_szModuleName, "BsProxyServer", &dbv )) { proxyJid = mir_a2t( dbv.pszVal ); JFreeVariant( &dbv ); } if ( proxyJid ) { jbt->bProxyDiscovered = FALSE; jbt->szProxyHost = NULL; jbt->szProxyPort = NULL; jbt->szProxyJid = NULL; jbt->hProxyEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); pInfo = m_iqManager.AddHandler( &CJabberProto::IqResultProxyDiscovery, JABBER_IQ_TYPE_GET, proxyJid, 0, -1, jbt ); nIqId = pInfo->GetIqId(); XmlNodeIq iq( pInfo ); iq << XQUERY( _T(JABBER_FEAT_BYTESTREAMS)); m_ThreadInfo->send( iq ); WaitForSingleObject( jbt->hProxyEvent, INFINITE ); m_iqManager.ExpireIq ( nIqId ); CloseHandle( jbt->hProxyEvent ); jbt->hProxyEvent = NULL; mir_free( proxyJid ); if ( jbt->state == JBT_ERROR && !bDirect ) { Log( "Bytestream proxy failure" ); MsgPopup( pInfo->GetHContact(), TranslateT("Bytestream Proxy not available"), pInfo->GetReceiver()); jbt->ft->state = FT_DENIED; (this->*jbt->pfnFinal)( FALSE, jbt->ft ); jbt->ft = NULL; delete jbt; return; } } } pInfo = m_iqManager.AddHandler( &CJabberProto::ByteInitiateResult, JABBER_IQ_TYPE_SET, jbt->dstJID, 0, -1, jbt ); nIqId = pInfo->GetIqId(); { XmlNodeIq iq( pInfo ); HXML query = iq << XQUERY( _T(JABBER_FEAT_BYTESTREAMS)) << XATTR( _T("sid"), jbt->sid ); if ( bDirect ) { if ( m_options.BsDirectManual ) { if ( !DBGetContactSettingString( NULL, m_szModuleName, "BsDirectAddr", &dbv )) localAddr = dbv.pszVal; } NETLIBBIND nlb = {0}; nlb.cbSize = sizeof( NETLIBBIND ); nlb.pfnNewConnectionV2 = JabberByteSendConnection; nlb.pExtra = this; nlb.wPort = 0; // Use user-specified incoming port ranges, if available jbt->hConn = ( HANDLE ) JCallService( MS_NETLIB_BINDPORT, ( WPARAM ) m_hNetlibUser, ( LPARAM )&nlb ); if ( jbt->hConn == NULL ) { Log( "Cannot allocate port for bytestream_send thread, thread ended." ); delete jbt; return; } if ( localAddr == NULL ) localAddr = (char*)CallService( MS_NETLIB_ADDRESSTOSTRING, 1, nlb.dwExternalIP ); mir_sntprintf( szPort, SIZEOF( szPort ), _T("%d"), nlb.wPort ); JABBER_LIST_ITEM *item = ListAdd( LIST_BYTE, szPort ); item->jbt = jbt; hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); jbt->hEvent = hEvent; jbt->hSendEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); query << XCHILD( _T("streamhost")) << XATTR( _T("jid"), m_ThreadInfo->fullJID ) << XATTR( _T("host"), _A2T(localAddr)) << XATTRI( _T("port"), nlb.wPort ); NETLIBIPLIST* ihaddr = ( NETLIBIPLIST* )CallService( MS_NETLIB_GETMYIP, 1, 0 ); for ( unsigned i = 0; i < ihaddr->cbNum; ++i ) if ( strcmp( localAddr, ihaddr->szIp[i] )) query << XCHILD( _T("streamhost")) << XATTR( _T("jid"), m_ThreadInfo->fullJID ) << XATTR( _T("host"), _A2T(ihaddr->szIp[i])) << XATTRI( _T("port"), nlb.wPort ); mir_free( ihaddr ); mir_free( localAddr ); } if ( jbt->bProxyDiscovered ) query << XCHILD( _T("streamhost")) << XATTR( _T("jid"), jbt->szProxyJid ) << XATTR( _T("host"), jbt->szProxyHost ) << XATTR( _T("port"), jbt->szProxyPort ); jbt->hProxyEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); jbt->szStreamhostUsed = NULL; m_ThreadInfo->send( iq ); } WaitForSingleObject( jbt->hProxyEvent, INFINITE ); m_iqManager.ExpireIq( nIqId ); CloseHandle( jbt->hProxyEvent ); jbt->hProxyEvent = NULL; if ( !jbt->szStreamhostUsed ) { if ( bDirect ) { SetEvent( jbt->hSendEvent ); CloseHandle( jbt->hSendEvent ); CloseHandle( hEvent ); jbt->hEvent = NULL; if ( jbt->hConn != NULL ) Netlib_CloseHandle( jbt->hConn ); jbt->hConn = NULL; ListRemove( LIST_BYTE, szPort ); } (this->*jbt->pfnFinal)(( jbt->state==JBT_DONE )?TRUE:FALSE, jbt->ft ); jbt->ft = NULL; // stupid fix: wait for listening thread exit Sleep( 100 ); delete jbt; return; } if ( jbt->bProxyDiscovered && !_tcscmp( jbt->szProxyJid, jbt->szStreamhostUsed )) { // jabber proxy used if ( bDirect ) { SetEvent( jbt->hSendEvent ); CloseHandle( jbt->hSendEvent ); CloseHandle( hEvent ); jbt->hEvent = NULL; if ( jbt->hConn != NULL ) Netlib_CloseHandle( jbt->hConn ); jbt->hConn = NULL; ListRemove( LIST_BYTE, szPort ); } ByteSendViaProxy( jbt ); } else { SetEvent( jbt->hSendEvent ); WaitForSingleObject( hEvent, INFINITE ); CloseHandle( hEvent ); CloseHandle( jbt->hSendEvent ); jbt->hEvent = NULL; (this->*jbt->pfnFinal)(( jbt->state == JBT_DONE ) ? TRUE : FALSE, jbt->ft ); jbt->ft = NULL; if ( jbt->hConn != NULL ) Netlib_CloseHandle( jbt->hConn ); jbt->hConn = NULL; ListRemove( LIST_BYTE, szPort ); } // stupid fix: wait for listening connection thread exit Sleep( 100 ); delete jbt; Log( "Thread ended: type=bytestream_send" ); } void CJabberProto::ByteInitiateResult( HXML iqNode, CJabberIqInfo* pInfo ) { JABBER_BYTE_TRANSFER *jbt = ( JABBER_BYTE_TRANSFER * )pInfo->GetUserData(); if ( pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT ) { HXML queryNode = xmlGetChild( iqNode , "query" ); if ( queryNode ) { const TCHAR* queryXmlns = xmlGetAttrValue( queryNode, _T("xmlns")); if ( queryXmlns && !_tcscmp( queryXmlns, _T( JABBER_FEAT_BYTESTREAMS ))) { HXML streamHostNode = xmlGetChild( queryNode , "streamhost-used" ); if ( streamHostNode ) { const TCHAR* streamJid = xmlGetAttrValue( streamHostNode, _T("jid")); if ( streamJid ) jbt->szStreamhostUsed = mir_tstrdup( streamJid ); } } } } if ( jbt->hProxyEvent ) SetEvent( jbt->hProxyEvent ); } int CJabberProto::ByteSendParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen ) { int nMethods; BYTE data[10]; int i; char* str; switch ( jbt->state ) { case JBT_INIT: // received: // 00-00 ver ( 0x05 ) // 01-01 nmethods // 02-xx list of methods ( nmethods bytes ) // send: // 00-00 ver ( 0x05 ) // 01-01 select method ( 0=no auth required ) if ( datalen>=2 && buffer[0]==5 && buffer[1]+2==datalen ) { nMethods = buffer[1]; for ( i=0; istate = JBT_CONNECT; } else { data[1] = 0xff; jbt->state = JBT_ERROR; } data[0] = 5; Netlib_Send( hConn, ( char* )data, 2, 0 ); } else jbt->state = JBT_ERROR; break; case JBT_CONNECT: // received: // 00-00 ver ( 0x05 ) // 01-01 cmd ( 1=connect ) // 02-02 reserved ( 0 ) // 03-03 address type ( 3 ) // 04-44 dst.addr ( 41 bytes: 1-byte length, 40-byte SHA1 hash of [sid,srcJID,dstJID] ) // 45-46 dst.port ( 0 ) // send: // 00-00 ver ( 0x05 ) // 01-01 reply ( 0=success,2=not allowed ) // 02-02 reserved ( 0 ) // 03-03 address type ( 1=IPv4 address ) // 04-07 bnd.addr server bound address // 08-09 bnd.port server bound port if ( datalen == 47 && *(( DWORD* )buffer )==0x03000105 && buffer[4]==40 && *(( WORD* )( buffer+45 ))==0 ) { TCHAR text[256]; TCHAR *szInitiatorJid = JabberPrepareJid(jbt->srcJID); TCHAR *szTargetJid = JabberPrepareJid(jbt->dstJID); mir_sntprintf( text, SIZEOF( text ), _T("%s%s%s"), jbt->sid, szInitiatorJid, szTargetJid ); mir_free(szInitiatorJid); mir_free(szTargetJid); char* szAuthString = mir_utf8encodeT( text ); Log( "Auth: '%s'", szAuthString ); if (( str = JabberSha1( szAuthString )) != NULL ) { for ( i=0; i<40 && buffer[i+5]==str[i]; i++ ); mir_free( str ); ZeroMemory( data, 10 ); data[1] = ( i>=20 )?0:2; data[0] = 5; data[3] = 1; Netlib_Send( hConn, ( char* )data, 10, 0 ); // wait stream activation WaitForSingleObject( jbt->hSendEvent, INFINITE ); if ( jbt->state == JBT_ERROR ) break; if ( i>=20 && (this->*jbt->pfnSend)( hConn, jbt->ft )==TRUE ) jbt->state = JBT_DONE; else jbt->state = JBT_ERROR; } mir_free( szAuthString ); } else jbt->state = JBT_ERROR; break; } return datalen; } ///////////////// Bytestream receiving ///////////////////////// void CJabberProto::IqResultStreamActivate( HXML iqNode ) { int id = JabberGetPacketID( iqNode ); TCHAR listJid[JABBER_MAX_JID_LEN]; mir_sntprintf(listJid, SIZEOF( listJid ), _T("ftproxy_%d"), id); JABBER_LIST_ITEM *item = ListGetItemPtr( LIST_FTIQID, listJid ); if ( !item ) return; if ( !lstrcmp( xmlGetAttrValue( iqNode, _T("type")), _T( "result" ))) item->jbt->bStreamActivated = TRUE; if ( item->jbt->hProxyEvent ) SetEvent( item->jbt->hProxyEvent ); } void CJabberProto::ByteSendViaProxy( JABBER_BYTE_TRANSFER *jbt ) { TCHAR *szHost, *szPort; WORD port; HANDLE hConn; char data[3]; char* buffer; int datalen, bytesParsed, recvResult; BOOL validStreamhost; if ( jbt == NULL ) return; if (( buffer=( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE )) == NULL ) { m_ThreadInfo->send( XmlNodeIq( _T("error"), jbt->iqId, jbt->srcJID ) << XCHILD( _T("error")) << XATTRI( _T("code"), 406 ) << XATTR( _T("type"), _T("auth")) << XCHILDNS( _T("not-acceptable"), _T("urn:ietf:params:xml:ns:xmpp-stanzas"))); return; } jbt->state = JBT_INIT; validStreamhost = FALSE; szPort = jbt->szProxyPort; szHost = jbt->szProxyHost; port = ( WORD )_ttoi( szPort ); if ( jbt->streamhostJID ) mir_free( jbt->streamhostJID ); jbt->streamhostJID = mir_tstrdup( jbt->szProxyJid ); NETLIBOPENCONNECTION nloc = { 0 }; nloc.cbSize = sizeof( nloc ); nloc.szHost = mir_t2a(szHost); nloc.wPort = port; hConn = ( HANDLE ) JCallService( MS_NETLIB_OPENCONNECTION, ( WPARAM ) m_hNetlibUser, ( LPARAM )&nloc ); mir_free((void*)nloc.szHost); if ( hConn != NULL ) { jbt->hConn = hConn; data[0] = 5; data[1] = 1; data[2] = 0; Netlib_Send( hConn, data, 3, 0 ); jbt->state = JBT_INIT; datalen = 0; while ( jbt->state!=JBT_DONE && jbt->state!=JBT_ERROR && jbt->state!=JBT_SOCKSERR ) { recvResult = Netlib_Recv( hConn, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen, 0 ); if ( recvResult <= 0 ) break; datalen += recvResult; bytesParsed = ByteSendProxyParse( hConn, jbt, buffer, datalen ); if ( bytesParsed < datalen ) memmove( buffer, buffer+bytesParsed, datalen-bytesParsed ); datalen -= bytesParsed; if ( jbt->state == JBT_DONE ) validStreamhost = TRUE; } Netlib_CloseHandle( hConn ); } mir_free( buffer ); (this->*jbt->pfnFinal)(( jbt->state == JBT_DONE ) ? TRUE : FALSE, jbt->ft ); jbt->ft = NULL; if ( !validStreamhost ) m_ThreadInfo->send( XmlNodeIq( _T("error"), jbt->iqId, jbt->srcJID ) << XCHILD( _T("error")) << XATTRI( _T("code"), 404 ) << XATTR( _T("type"), _T("cancel")) << XCHILDNS( _T("item-not-found"), _T("urn:ietf:params:xml:ns:xmpp-stanzas"))); } int CJabberProto::ByteSendProxyParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen ) { int num = datalen; switch ( jbt->state ) { case JBT_INIT: // received: // 00-00 ver ( 0x05 ) // 01-01 selected method ( 0=no auth, 0xff=error ) // send: // 00-00 ver ( 0x05 ) // 01-01 cmd ( 1=connect ) // 02-02 reserved ( 0 ) // 03-03 address type ( 3 ) // 04-44 dst.addr ( 41 bytes: 1-byte length, 40-byte SHA1 hash of [sid,srcJID,dstJID] ) // 45-46 dst.port ( 0 ) if ( datalen==2 && buffer[0]==5 && buffer[1]==0 ) { BYTE data[47]; ZeroMemory( data, sizeof( data )); *(( DWORD* )data ) = 0x03000105; data[4] = 40; TCHAR text[256]; TCHAR *szInitiatorJid = JabberPrepareJid(jbt->srcJID); TCHAR *szTargetJid = JabberPrepareJid(jbt->dstJID); mir_sntprintf( text, SIZEOF( text ), _T("%s%s%s"), jbt->sid, szInitiatorJid, szTargetJid ); mir_free(szInitiatorJid); mir_free(szTargetJid); char* szAuthString = mir_utf8encodeT( text ); Log( "Auth: '%s'", szAuthString ); char* szHash = JabberSha1( szAuthString ); strncpy(( char* )( data+5 ), szHash, 40 ); mir_free( szHash ); Netlib_Send( hConn, ( char* )data, 47, 0 ); jbt->state = JBT_CONNECT; mir_free( szAuthString ); } else jbt->state = JBT_SOCKSERR; break; case JBT_CONNECT: // received: // 00-00 ver ( 0x05 ) // 01-01 reply ( 0=success,2=not allowed ) // 02-02 reserved ( 0 ) // 03-03 address type ( 1=IPv4 address,3=host address ) // 04-mm bnd.addr server bound address ( 4-byte IP if IPv4, 1-byte length + n-byte host address string if host address ) // nn-nn+1 bnd.port server bound port if ( datalen>=5 && buffer[0]==5 && buffer[1]==0 && ( buffer[3]==1 || buffer[3]==3 || buffer[3]==0 )) { if ( buffer[3]==1 && datalen>=10 ) num = 10; else if ( buffer[3]==3 && datalen>=buffer[4]+7 ) num = buffer[4] + 7; else if ( buffer[3]==0 && datalen>=6 ) num = 6; else { jbt->state = JBT_SOCKSERR; break; } jbt->state = JBT_SENDING; jbt->hProxyEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); jbt->bStreamActivated = FALSE; int iqId = SerialNext(); TCHAR listJid[256]; mir_sntprintf(listJid, SIZEOF( listJid ), _T("ftproxy_%d"), iqId); JABBER_LIST_ITEM *item = ListAdd( LIST_FTIQID, listJid ); item->jbt = jbt; IqAdd( iqId, IQ_PROC_NONE, &CJabberProto::IqResultStreamActivate ); m_ThreadInfo->send( XmlNodeIq( _T("set"), iqId, jbt->streamhostJID ) << XQUERY( _T(JABBER_FEAT_BYTESTREAMS)) << XATTR( _T("sid"), jbt->sid ) << XCHILD( _T("activate"), jbt->dstJID )); WaitForSingleObject( jbt->hProxyEvent, INFINITE ); CloseHandle( jbt->hProxyEvent ); jbt->hProxyEvent = NULL; ListRemove( LIST_FTIQID, listJid ); if ( jbt->bStreamActivated) jbt->state = (this->*jbt->pfnSend)( hConn, jbt->ft ) ? JBT_DONE : JBT_ERROR; else jbt->state = JBT_ERROR; } else jbt->state = JBT_SOCKSERR; break; } return num; } void __cdecl CJabberProto::ByteReceiveThread( JABBER_BYTE_TRANSFER *jbt ) { HXML iqNode, queryNode = NULL, n; const TCHAR *sid = NULL, *from = NULL, *to = NULL, *szId = NULL, *szHost, *szPort, *str; int i; WORD port; HANDLE hConn; char data[3]; char* buffer; int datalen, bytesParsed, recvResult; BOOL validStreamhost = FALSE; if ( jbt == NULL ) return; jbt->state = JBT_INIT; if ( iqNode = jbt->iqNode ) { from = xmlGetAttrValue( iqNode, _T("from")); to = xmlGetAttrValue( iqNode, _T("to")); szId = xmlGetAttrValue( iqNode, _T("id")); queryNode = xmlGetChild( iqNode , "query" ); if ( queryNode ) sid = xmlGetAttrValue( queryNode, _T("sid")); } if ( szId && from && to && sid && ( n = xmlGetChild( queryNode , "streamhost" ))!=NULL ) { jbt->iqId = mir_tstrdup( szId ); jbt->srcJID = mir_tstrdup( from ); jbt->dstJID = mir_tstrdup( to ); jbt->sid = mir_tstrdup( sid ); if (( buffer=( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE ))) { for ( i=1; ( n = xmlGetNthChild( queryNode, _T("streamhost"), i ))!=NULL; i++ ) { if (( szHost = xmlGetAttrValue( n, _T("host"))) != NULL && ( szPort = xmlGetAttrValue( n, _T("port"))) != NULL && ( str = xmlGetAttrValue( n, _T("jid"))) != NULL ) { port = ( WORD )_ttoi( szPort ); if ( jbt->streamhostJID ) mir_free( jbt->streamhostJID ); jbt->streamhostJID = mir_tstrdup( str ); Log( "bytestream_recv connecting to " TCHAR_STR_PARAM ":%d", szHost, port ); NETLIBOPENCONNECTION nloc = { 0 }; nloc.cbSize = sizeof( nloc ); nloc.szHost = mir_t2a(szHost); nloc.wPort = port; hConn = ( HANDLE ) JCallService( MS_NETLIB_OPENCONNECTION, ( WPARAM ) m_hNetlibUser, ( LPARAM )&nloc ); mir_free((void*)nloc.szHost); if ( hConn == NULL ) { Log( "bytestream_recv_connection connection failed ( %d ), try next streamhost", WSAGetLastError()); continue; } jbt->hConn = hConn; data[0] = 5; data[1] = 1; data[2] = 0; Netlib_Send( hConn, data, 3, 0 ); jbt->state = JBT_INIT; datalen = 0; while ( jbt->state!=JBT_DONE && jbt->state!=JBT_ERROR && jbt->state!=JBT_SOCKSERR ) { recvResult = Netlib_Recv( hConn, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen, 0 ); if ( recvResult <= 0 ) break; datalen += recvResult; bytesParsed = ByteReceiveParse( hConn, jbt, buffer, datalen ); if ( bytesParsed < datalen ) memmove( buffer, buffer+bytesParsed, datalen-bytesParsed ); datalen -= bytesParsed; if ( jbt->state == JBT_RECVING ) validStreamhost = TRUE; } Netlib_CloseHandle( hConn ); Log( "bytestream_recv_connection closing connection" ); } if ( jbt->state==JBT_ERROR || validStreamhost==TRUE ) break; Log( "bytestream_recv_connection stream cannot be established, try next streamhost" ); } mir_free( buffer ); } } (this->*jbt->pfnFinal)(( jbt->state==JBT_DONE )?TRUE:FALSE, jbt->ft ); jbt->ft = NULL; if ( !validStreamhost && szId && from ) { Log( "bytestream_recv_connection session not completed" ); m_ThreadInfo->send( XmlNodeIq( _T("error"), szId, from ) << XCHILD( _T("error")) << XATTRI( _T("code"), 404 ) << XATTR( _T("type"), _T("cancel")) << XCHILDNS( _T("item-not-found"), _T("urn:ietf:params:xml:ns:xmpp-stanzas"))); } delete jbt; Log( "Thread ended: type=bytestream_recv" ); } int CJabberProto::ByteReceiveParse( HANDLE hConn, JABBER_BYTE_TRANSFER *jbt, char* buffer, int datalen ) { int bytesReceived, num = datalen; switch ( jbt->state ) { case JBT_INIT: // received: // 00-00 ver ( 0x05 ) // 01-01 selected method ( 0=no auth, 0xff=error ) // send: // 00-00 ver ( 0x05 ) // 01-01 cmd ( 1=connect ) // 02-02 reserved ( 0 ) // 03-03 address type ( 3 ) // 04-44 dst.addr ( 41 bytes: 1-byte length, 40-byte SHA1 hash of [sid,srcJID,dstJID] ) // 45-46 dst.port ( 0 ) if ( datalen==2 && buffer[0]==5 && buffer[1]==0 ) { BYTE data[47]; ZeroMemory( data, sizeof( data )); *(( DWORD* )data ) = 0x03000105; data[4] = 40; TCHAR text[JABBER_MAX_JID_LEN*2]; TCHAR *szInitiatorJid = JabberPrepareJid(jbt->srcJID); TCHAR *szTargetJid = JabberPrepareJid(jbt->dstJID); mir_sntprintf( text, SIZEOF( text ), _T("%s%s%s"), jbt->sid, szInitiatorJid, szTargetJid ); mir_free(szInitiatorJid); mir_free(szTargetJid); char* szAuthString = mir_utf8encodeT( text ); Log( "Auth: '%s'", szAuthString ); char* szHash = JabberSha1( szAuthString ); strncpy(( char* )( data+5 ), szHash, 40 ); mir_free( szHash ); Netlib_Send( hConn, ( char* )data, 47, 0 ); jbt->state = JBT_CONNECT; mir_free( szAuthString ); } else jbt->state = JBT_SOCKSERR; break; case JBT_CONNECT: // received: // 00-00 ver ( 0x05 ) // 01-01 reply ( 0=success,2=not allowed ) // 02-02 reserved ( 0 ) // 03-03 address type ( 1=IPv4 address,3=host address ) // 04-mm bnd.addr server bound address ( 4-byte IP if IPv4, 1-byte length + n-byte host address string if host address ) // nn-nn+1 bnd.port server bound port if ( datalen>=5 && buffer[0]==5 && buffer[1]==0 && ( buffer[3]==1 || buffer[3]==3 || buffer[3]==0 )) { if ( buffer[3]==1 && datalen>=10 ) num = 10; else if ( buffer[3]==3 && datalen>=buffer[4]+7 ) num = buffer[4] + 7; else if ( buffer[3]==0 && datalen>=6 ) num = 6; else { jbt->state = JBT_SOCKSERR; break; } jbt->state = JBT_RECVING; m_ThreadInfo->send( XmlNodeIq( _T("result"), jbt->iqId, jbt->srcJID ) << XQUERY( _T(JABBER_FEAT_BYTESTREAMS)) << XCHILD( _T("streamhost-used")) << XATTR( _T("jid"), jbt->streamhostJID )); } else jbt->state = JBT_SOCKSERR; break; case JBT_RECVING: bytesReceived = (this->*jbt->pfnRecv)( hConn, jbt->ft, buffer, datalen ); if ( bytesReceived < 0 ) jbt->state = JBT_ERROR; else if ( bytesReceived == 0 ) jbt->state = JBT_DONE; break; } return num; }