summaryrefslogtreecommitdiffstats
path: root/libktorrent/mse
diff options
context:
space:
mode:
Diffstat (limited to 'libktorrent/mse')
-rw-r--r--libktorrent/mse/Makefile.am9
-rw-r--r--libktorrent/mse/bigint.cpp100
-rw-r--r--libktorrent/mse/bigint.h98
-rw-r--r--libktorrent/mse/encryptedauthenticate.cpp302
-rw-r--r--libktorrent/mse/encryptedauthenticate.h82
-rw-r--r--libktorrent/mse/encryptedserverauthenticate.cpp354
-rw-r--r--libktorrent/mse/encryptedserverauthenticate.h80
-rw-r--r--libktorrent/mse/functions.cpp74
-rw-r--r--libktorrent/mse/functions.h39
-rw-r--r--libktorrent/mse/rc4encryptor.cpp100
-rw-r--r--libktorrent/mse/rc4encryptor.h96
-rw-r--r--libktorrent/mse/streamsocket.cpp326
-rw-r--r--libktorrent/mse/streamsocket.h185
13 files changed, 1845 insertions, 0 deletions
diff --git a/libktorrent/mse/Makefile.am b/libktorrent/mse/Makefile.am
new file mode 100644
index 0000000..d6a8ac5
--- /dev/null
+++ b/libktorrent/mse/Makefile.am
@@ -0,0 +1,9 @@
+INCLUDES = -I$(srcdir)/.. $(all_includes)
+METASOURCES = AUTO
+libmse_la_LDFLAGS = -lgmp $(all_libraries)
+noinst_LTLIBRARIES = libmse.la
+noinst_HEADERS = bigint.h rc4encryptor.h streamsocket.h encryptedauthenticate.h \
+ encryptedserverauthenticate.h functions.h
+libmse_la_SOURCES = bigint.cpp rc4encryptor.cpp streamsocket.cpp \
+ encryptedauthenticate.cpp encryptedserverauthenticate.cpp functions.cpp
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/libktorrent/mse/bigint.cpp b/libktorrent/mse/bigint.cpp
new file mode 100644
index 0000000..90c6d9e
--- /dev/null
+++ b/libktorrent/mse/bigint.cpp
@@ -0,0 +1,100 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <util/log.h>
+#include <util/functions.h>
+#include <torrent/globals.h>
+#include "bigint.h"
+
+using namespace bt;
+
+namespace mse
+{
+
+
+ BigInt::BigInt(Uint32 num_bits)
+ {
+ mpz_init2(val,num_bits);
+ }
+
+ BigInt::BigInt(const QString & value)
+ {
+ mpz_init2(val,(value.length() - 2)*4);
+ mpz_set_str(val,value.ascii(),0);
+ }
+
+ BigInt::BigInt(const BigInt & bi)
+ {
+ mpz_set(val,bi.val);
+ }
+
+ BigInt::~BigInt()
+ {
+ mpz_clear(val);
+ }
+
+
+ BigInt & BigInt::operator = (const BigInt & bi)
+ {
+ mpz_set(val,bi.val);
+ return *this;
+ }
+
+ BigInt BigInt::powerMod(const BigInt & x,const BigInt & e,const BigInt & d)
+ {
+ BigInt r;
+ mpz_powm(r.val,x.val,e.val,d.val);
+ return r;
+ }
+
+ BigInt BigInt::random()
+ {
+ static Uint32 rnd = 0;
+ if (rnd % 10 == 0)
+ {
+ TimeStamp now = bt::GetCurrentTime();
+ srand(now);
+ rnd = 0;
+ }
+ rnd++;
+ Uint8 tmp[20];
+ for (Uint32 i = 0;i < 20;i++)
+ tmp[i] = (Uint8)rand() % 0x100;
+
+ return BigInt::fromBuffer(tmp,20);
+ }
+
+ Uint32 BigInt::toBuffer(Uint8* buf,Uint32 max_size) const
+ {
+ size_t foo;
+ mpz_export(buf,&foo,1,1,1,0,val);
+ return foo;
+ }
+
+ BigInt BigInt::fromBuffer(const Uint8* buf,Uint32 size)
+ {
+ BigInt r(size*8);
+ mpz_import(r.val,size,1,1,1,0,buf);
+ return r;
+ }
+
+}
diff --git a/libktorrent/mse/bigint.h b/libktorrent/mse/bigint.h
new file mode 100644
index 0000000..ad94d20
--- /dev/null
+++ b/libktorrent/mse/bigint.h
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef MSEBIGINT_H
+#define MSEBIGINT_H
+
+#include <qstring.h>
+#include <util/constants.h>
+#include <stdio.h>
+#include <gmp.h>
+
+using bt::Uint8;
+using bt::Uint16;
+using bt::Uint32;
+using bt::Uint64;
+
+namespace mse
+{
+
+ /**
+ * @author Joris Guisson <[email protected]>
+ *
+ * Class which can hold an arbitrary large integer. This will be a very important part of our
+ * MSE implementation.
+ */
+ class BigInt
+ {
+ public:
+ /**
+ * Create a big integer, with num_bits bits.
+ * All bits will be set to 0.
+ * @param num_bits The number of bits
+ */
+ BigInt(Uint32 num_bits = 0);
+
+ /**
+ * Create a big integer of a string. The string must be
+ * a hexadecimal representation of an integer. For example :
+ * 12AFFE123488BBBE123
+ *
+ * Letters can be upper or lower case. Invalid chars will create an invalid number.
+ * @param value The hexadecimal representation of the number
+ */
+ BigInt(const QString & value);
+
+ /**
+ * Copy constructor.
+ * @param bi BigInt to copy
+ */
+ BigInt(const BigInt & bi);
+ virtual ~BigInt();
+
+ /**
+ * Assignment operator.
+ * @param bi The BigInt to copy
+ * @return *this
+ */
+ BigInt & operator = (const BigInt & bi);
+
+ /**
+ * Calculates
+ * (x ^ e) mod d
+ * ^ is power
+ */
+ static BigInt powerMod(const BigInt & x,const BigInt & e,const BigInt & d);
+
+ /// Make a random BigInt
+ static BigInt random();
+
+ /// Export the bigint ot a buffer
+ Uint32 toBuffer(Uint8* buf,Uint32 max_size) const;
+
+ /// Make a BigInt out of a buffer
+ static BigInt fromBuffer(const Uint8* buf,Uint32 size);
+
+ private:
+ mpz_t val;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/mse/encryptedauthenticate.cpp b/libktorrent/mse/encryptedauthenticate.cpp
new file mode 100644
index 0000000..644ba7b
--- /dev/null
+++ b/libktorrent/mse/encryptedauthenticate.cpp
@@ -0,0 +1,302 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <stdlib.h>
+#include <algorithm>
+#include <util/functions.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include <torrent/server.h>
+#include "encryptedauthenticate.h"
+#include "rc4encryptor.h"
+#include "streamsocket.h"
+#include "functions.h"
+
+using namespace bt;
+
+namespace mse
+{
+
+
+
+ EncryptedAuthenticate::EncryptedAuthenticate(
+ const QString& ip,
+ Uint16 port,
+ const SHA1Hash& info_hash,
+ const PeerID& peer_id,
+ PeerManager* pman)
+ : Authenticate(ip, port, info_hash, peer_id, pman)
+ {
+ mse::GeneratePublicPrivateKey(xa,ya);
+ state = NOT_CONNECTED;
+ buf_size = 0;
+ our_rc4 = 0;
+ vc_off = 0;
+ dec_bytes = 0;
+ crypto_select = 0;
+ pad_D_len = 0;
+ end_of_crypto_handshake = 0;
+ //Out(SYS_CON|LOG_DEBUG) << "EncryptedAuthenticate : " << ip << ":" << port << endl;
+ }
+
+
+ EncryptedAuthenticate::~EncryptedAuthenticate()
+ {
+ delete our_rc4;
+ }
+
+
+
+ void EncryptedAuthenticate::connected()
+ {
+ // we are connected so send ya and some padding
+ Uint8 tmp[608];
+ ya.toBuffer(tmp,96);
+ sock->sendData(tmp,96 + rand() % 512);
+ state = SENT_YA;
+ }
+
+ /*
+ 1 A->B: Diffie Hellman Ya, PadA
+ 2 B->A: Diffie Hellman Yb, PadB
+ 3 A->B: HASH('req1', S), HASH('req2', SKEY) xor HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)), ENCRYPT(IA)
+ 4 B->A: ENCRYPT(VC, crypto_select, len(padD), padD), ENCRYPT2(Payload Stream)
+ 5 A->B: ENCRYPT2(Payload Stream)
+ */
+
+
+
+ void EncryptedAuthenticate::handleYB()
+ {
+ // if you can't sent 96 bytes you are not worth the effort
+ if (buf_size < 96)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Not enough data received, encrypted authentication failed" << endl;
+ onFinish(false);
+ return;
+ }
+
+ // read Yb
+ yb = BigInt::fromBuffer(buf,96);
+
+ // calculate s
+ s = mse::DHSecret(xa,yb);
+
+ state = GOT_YB;
+ // now we must send line 3
+ Uint8 tmp_buf[120]; // temporary buffer
+ bt::SHA1Hash h1,h2; // temporary hash
+
+ // generate and send the first hash
+ memcpy(tmp_buf,"req1",4);
+ s.toBuffer(tmp_buf + 4,96);
+ h1 = SHA1Hash::generate(tmp_buf,100);
+ sock->sendData(h1.getData(),20);
+
+ // generate second and third hash and xor them
+ memcpy(tmp_buf,"req2",4);
+ memcpy(tmp_buf+4,info_hash.getData(),20);
+ h1 = SHA1Hash::generate(tmp_buf,24);
+
+ memcpy(tmp_buf,"req3",4);
+ s.toBuffer(tmp_buf + 4,96);
+ h2 = SHA1Hash::generate(tmp_buf,100);
+ sock->sendData((h1 ^ h2).getData(),20);
+
+ // now we enter encrypted mode the keys are :
+ // HASH('keyA', S, SKEY) for the encryption key
+ // HASH('keyB', S, SKEY) for the decryption key
+ enc = mse::EncryptionKey(true,s,info_hash);
+ dec = mse::EncryptionKey(false,s,info_hash);
+
+ our_rc4 = new RC4Encryptor(dec,enc);
+
+ // now we must send ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA))
+ memset(tmp_buf,0,16); // VC are 8 0x00's
+ if (Globals::instance().getServer().unencryptedConnectionsAllowed())
+ tmp_buf[11] = 0x03; // we support both plain text and rc4
+ else
+ tmp_buf[11] = 0x02;
+ WriteUint16(tmp_buf,12,0x0000); // no padC
+ WriteUint16(tmp_buf,14,68); // length of IA, which will be the bittorrent handshake
+ // send IA which is the handshake
+ makeHandshake(tmp_buf+16,info_hash,our_peer_id);
+ sock->sendData(our_rc4->encrypt(tmp_buf,84),84);
+
+ // search for the encrypted VC in the data
+ findVC();
+ }
+
+ void EncryptedAuthenticate::findVC()
+ {
+ Uint8 vc[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+
+ RC4Encryptor rc4(enc,dec);
+ memcpy(vc,rc4.encrypt(vc,8),8);
+
+ Uint32 max_i = buf_size - 8;
+ for (Uint32 i = 96;i < max_i;i++)
+ {
+ if (vc[0] == buf[i] && memcmp(buf+i,vc,8) == 0)
+ {
+ state = FOUND_VC;
+ vc_off = i;
+ handleCryptoSelect();
+ return;
+ }
+ }
+
+ // we haven't found it in the first 616 bytes (96 + max 512 padding + 8 bytes VC)
+ if (buf_size >= 616)
+ {
+ onFinish(false);
+ }
+ }
+
+ void EncryptedAuthenticate::handleCryptoSelect()
+ {
+ // not enough data available so lets come back later
+ if (vc_off + 14 >= buf_size)
+ return;
+
+ // now decrypt the first 14 bytes
+ our_rc4->decrypt(buf + vc_off,14);
+ // check the VC
+ for (Uint32 i = vc_off;i < vc_off + 8;i++)
+ {
+ if (buf[i])
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Invalid VC " << endl;
+ onFinish(false);
+ return;
+ }
+ }
+
+ crypto_select = ReadUint32(buf,vc_off + 8);
+ pad_D_len = ReadUint16(buf,vc_off + 12);
+ if (pad_D_len > 512)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Invalid pad D length" << endl;
+ onFinish(false);
+ return;
+ }
+
+ end_of_crypto_handshake = vc_off + 14 + pad_D_len;
+ if (!(vc_off + 14 + pad_D_len < buf_size))
+ {
+ // padD is not complete, wait for that
+ state = WAIT_FOR_PAD_D;
+ return;
+ }
+
+ handlePadD();
+ }
+
+ void EncryptedAuthenticate::handlePadD()
+ {
+ // decrypt the padding
+ our_rc4->decrypt(buf + (vc_off + 14),pad_D_len);
+
+ bool rc4 = false;
+ if (crypto_select & 0x00000001) // plain_text selected
+ {
+ delete our_rc4;
+ our_rc4 = 0;
+ }
+ else if (crypto_select & 0x00000002) // now it must be rc4 if not exit
+ {
+ sock->setRC4Encryptor(our_rc4);
+ our_rc4 = 0;
+ rc4 = true;
+ }
+ else // we don't support anything else so error out
+ {
+ onFinish(false);
+ return;
+ }
+
+ // noz we wait for the normal handshake
+ state = NORMAL_HANDSHAKE;
+ // if we have read more then the crypto handshake, reinsert it
+ if (buf_size > vc_off + 14 + pad_D_len)
+ {
+ Uint32 off = vc_off + 14 + pad_D_len;
+ sock->reinsert(buf + off,buf_size - off);
+ Authenticate::onReadyRead();
+ }
+ }
+
+ void EncryptedAuthenticate::onReadyRead()
+ {
+ if (finished)
+ return;
+
+
+ Uint32 ba = sock->bytesAvailable();
+ if (ba == 0)
+ {
+ onFinish(false);
+ return;
+ }
+
+ if (state != NORMAL_HANDSHAKE)
+ {
+ if (buf_size + ba > MAX_EA_BUF_SIZE)
+ ba = MAX_EA_BUF_SIZE - buf_size;
+
+ // do not read past the end of padD
+ if (pad_D_len > 0 && buf_size + ba > vc_off + 14 + pad_D_len)
+ ba = (vc_off + 14 + pad_D_len) - buf_size;
+ // read data
+ buf_size += sock->readData(buf + buf_size,ba);
+
+ }
+
+ switch (state)
+ {
+ case SENT_YA:
+ if (ba > 608)
+ {
+ onFinish(false);
+ }
+ else
+ {
+ handleYB();
+ }
+ break;
+ case GOT_YB:
+ findVC();
+ break;
+ case FOUND_VC:
+ handleCryptoSelect();
+ break;
+ case WAIT_FOR_PAD_D:
+ handlePadD();
+ break;
+ case NORMAL_HANDSHAKE:
+ // let AuthenticateBase deal with the data
+ AuthenticateBase::onReadyRead();
+ break;
+ };
+ }
+
+
+}
+
+#include "encryptedauthenticate.moc"
diff --git a/libktorrent/mse/encryptedauthenticate.h b/libktorrent/mse/encryptedauthenticate.h
new file mode 100644
index 0000000..74ccc1b
--- /dev/null
+++ b/libktorrent/mse/encryptedauthenticate.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef MSEENCRYPTEDAUTHENTICATE_H
+#define MSEENCRYPTEDAUTHENTICATE_H
+
+#include <util/sha1hash.h>
+#include <torrent/authenticate.h>
+#include "bigint.h"
+
+
+namespace mse
+{
+ class RC4Encryptor;
+
+ const Uint32 MAX_EA_BUF_SIZE = 622 + 512;
+
+ /**
+ * @author Joris Guisson <[email protected]>
+ *
+ * Encrypted version of the Authenticate class
+ */
+ class EncryptedAuthenticate : public bt::Authenticate
+ {
+ Q_OBJECT
+ public:
+ EncryptedAuthenticate(const QString& ip, Uint16 port, const bt::SHA1Hash& info_hash, const bt::PeerID& peer_id, bt::PeerManager* pman);
+ virtual ~EncryptedAuthenticate();
+
+ private slots:
+ virtual void connected();
+ virtual void onReadyRead();
+
+ private:
+ void handleYB();
+ void handleCryptoSelect();
+ void findVC();
+ void handlePadD();
+
+ private:
+ enum State
+ {
+ NOT_CONNECTED,
+ SENT_YA,
+ GOT_YB,
+ FOUND_VC,
+ WAIT_FOR_PAD_D,
+ NORMAL_HANDSHAKE
+ };
+
+ BigInt xa,ya,s,skey,yb;
+ State state;
+ RC4Encryptor* our_rc4;
+ Uint8 buf[MAX_EA_BUF_SIZE];
+ Uint32 buf_size;
+ Uint32 vc_off;
+ Uint32 dec_bytes;
+ bt::SHA1Hash enc,dec;
+ Uint32 crypto_select;
+ Uint16 pad_D_len;
+ Uint32 end_of_crypto_handshake;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/mse/encryptedserverauthenticate.cpp b/libktorrent/mse/encryptedserverauthenticate.cpp
new file mode 100644
index 0000000..40353ad
--- /dev/null
+++ b/libktorrent/mse/encryptedserverauthenticate.cpp
@@ -0,0 +1,354 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <stdlib.h>
+#include <util/functions.h>
+#include <util/log.h>
+#include <torrent/server.h>
+#include <torrent/globals.h>
+#include "encryptedserverauthenticate.h"
+#include "functions.h"
+#include "streamsocket.h"
+#include "rc4encryptor.h"
+
+using namespace bt;
+
+namespace mse
+{
+ EncryptedServerAuthenticate::EncryptedServerAuthenticate(mse::StreamSocket* sock, bt::Server* server): bt::ServerAuthenticate(sock, server)
+ {
+ mse::GeneratePublicPrivateKey(xb,yb);
+ state = WAITING_FOR_YA;
+ buf_size = 0;
+ req1_off = 0;
+ our_rc4 = 0;
+ pad_C_len = 0;
+ crypto_provide = crypto_select = 0;
+ }
+
+
+ EncryptedServerAuthenticate::~EncryptedServerAuthenticate()
+ {
+ delete our_rc4;
+ }
+
+ void EncryptedServerAuthenticate::sendYB()
+ {
+ Uint8 tmp[608];
+ yb.toBuffer(tmp,96);
+ // DumpBigInt("Xb",xb);
+ // DumpBigInt("Yb",yb);
+ sock->sendData(tmp,96 + rand() % 512);
+ //Out() << "Sent YB" << endl;
+ }
+
+
+ void EncryptedServerAuthenticate::handleYA()
+ {
+ sendYB();
+
+ ya = BigInt::fromBuffer(buf,96);
+ // DumpBigInt("Ya",ya);
+ // now calculate secret
+ s = mse::DHSecret(xb,ya);
+ // DumpBigInt("S",s);
+ state = WAITING_FOR_REQ1;
+ // see if we can find req1
+ findReq1();
+ }
+
+ void EncryptedServerAuthenticate::findReq1()
+ {
+ if (buf_size < 116) // safety check
+ return;
+
+ // Out() << "Find Req1" << endl;
+ Uint8 tmp[100];
+ memcpy(tmp,"req1",4);
+ s.toBuffer(tmp + 4,96);
+ SHA1Hash req1 = SHA1Hash::generate(tmp,100);
+ for (Uint32 i = 96;i < buf_size - 20;i++)
+ {
+ if (buf[i] == req1.getData()[0] && memcmp(buf+i,req1.getData(),20) == 0)
+ {
+ state = FOUND_REQ1;
+ req1_off = i;
+ calculateSKey();
+ return;
+ }
+ }
+
+ if (buf_size > 608)
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Couldn't find req1" << endl;
+ onFinish(false);
+ }
+ }
+
+ void EncryptedServerAuthenticate::calculateSKey()
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Calculate SKEY" << endl;
+ // not enough data return
+ if (req1_off + 40 > buf_size)
+ return;
+
+ Uint8 tmp[100];
+ memcpy(tmp,"req3",4);
+ s.toBuffer(tmp+4,96);
+ SHA1Hash r3 = SHA1Hash::generate(tmp,100);
+ SHA1Hash r(buf + req1_off + 20);
+
+ // r = HASH('req2', SKEY) xor HASH('req3', S)
+ SHA1Hash r2 = r ^ r3; // now calculate HASH('req2', SKEY)
+ if (!server->findInfoHash(r2,info_hash))
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Unknown info_hash" << endl;
+ onFinish(false);
+ return;
+ }
+ // we have found the info_hash, now process VC and the rest
+ state = FOUND_INFO_HASH;
+ processVC();
+ }
+
+ void EncryptedServerAuthenticate::processVC()
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Process VC" << endl;
+ if (!our_rc4)
+ {
+ // calculate the keys
+ SHA1Hash enc = mse::EncryptionKey(false,s,info_hash);
+ SHA1Hash dec = mse::EncryptionKey(true,s,info_hash);
+ //Out() << "enc = " << enc.toString() << endl;
+ //Out() << "dec = " << dec.toString() << endl;
+ our_rc4 = new RC4Encryptor(dec,enc);
+ }
+
+ // if we do not have everything return
+ if (buf_size < req1_off + 40 + 14)
+ return;
+
+
+ Uint32 off = req1_off + 40;
+ // now decrypt the vc and crypto_provide and the length of pad_C
+ our_rc4->decrypt(buf + off,14);
+
+ // check the VC
+ for (Uint32 i = 0;i < 8;i++)
+ {
+ if (buf[off + i])
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Illegal VC" << endl;
+ onFinish(false);
+ return;
+ }
+ }
+ // get crypto_provide and the length of pad_C
+ crypto_provide = bt::ReadUint32(buf,off + 8);
+ pad_C_len = bt::ReadUint16(buf,off + 12);
+ if (pad_C_len > 512)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Illegal pad C length" << endl;
+ onFinish(false);
+ return;
+ }
+
+ // now we have crypto_provide we can send
+ // ENCRYPT(VC, crypto_select, len(padD), padD)
+ Uint8 tmp[14];
+ memset(tmp,0,14); // VC
+ if (crypto_provide & 0x0000002) // RC4
+ {
+ WriteUint32(tmp,8,0x0000002);
+ crypto_select = 0x0000002;
+ }
+ else
+ {
+ WriteUint32(tmp,8,0x0000001);
+ crypto_select = 0x0000001;
+ }
+ bt::WriteUint16(tmp,12,0); // no pad D
+
+ sock->sendData(our_rc4->encrypt(tmp,14),14);
+
+ // handle pad C
+ if (buf_size < req1_off + 14 + pad_C_len)
+ {
+ // we do not have the full padC
+ state = WAIT_FOR_PAD_C;
+ return;
+ }
+
+ handlePadC();
+ }
+
+ void EncryptedServerAuthenticate::handlePadC()
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Handle PAD C" << endl;
+ // not enough data, so return, we need padC and the length of IA
+ if (buf_size < req1_off + 54 + pad_C_len + 2)
+ return;
+
+ // we have decrypted everything up to pad_C_len
+ Uint32 off = req1_off + 54;
+ our_rc4->decrypt(buf + off,pad_C_len + 2);
+ ia_len = bt::ReadUint16(buf,off + pad_C_len);
+ if (buf_size < off + ia_len)
+ {
+ // we do not have the IA, so wait for it
+ state = WAIT_FOR_IA;
+ return;
+ }
+ handleIA();
+ }
+
+ void EncryptedServerAuthenticate::handleIA()
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Handle IA" << endl;
+ // not enough data, so return, we need padC and the length of IA
+ if (buf_size < req1_off + 54 + pad_C_len + 2 + ia_len)
+ return;
+
+ // decrypt the initial argument
+ if (ia_len > 0)
+ {
+ Uint32 off = req1_off + 54 + pad_C_len + 2;
+ // reinsert everything so that the normal authentication can handle it
+ sock->reinsert(buf + off,buf_size - off);
+ }
+
+ bool allow_unenc = Globals::instance().getServer().unencryptedConnectionsAllowed();
+
+ if (crypto_select & 0x0000002)
+ {
+ sock->setRC4Encryptor(our_rc4);
+ our_rc4 = 0;
+ }
+ else if (!allow_unenc && crypto_select & 0x00000001)
+ {
+ // if no encrypted connections
+ Out(SYS_CON|LOG_DEBUG) << "Unencrypted connections not allowed" << endl;
+ onFinish(false);
+ return;
+ }
+ else
+ {
+ delete our_rc4;
+ our_rc4 = 0;
+ }
+
+ // hand it over to ServerAuthenticate
+ state = NON_ENCRYPTED_HANDSHAKE;
+ ServerAuthenticate::onReadyRead();
+ }
+
+ void EncryptedServerAuthenticate::onReadyRead()
+ {
+ if (!sock)
+ return;
+
+ Uint32 ba = sock->bytesAvailable();
+ if (!ba)
+ {
+ onFinish(false);
+ return;
+ }
+
+ // make sure we don't write past the end of the buffer
+ if (buf_size + ba > MAX_SEA_BUF_SIZE)
+ ba = MAX_SEA_BUF_SIZE - buf_size;
+
+ switch (state)
+ {
+ case WAITING_FOR_YA:
+ if (ba <= 68 && Globals::instance().getServer().unencryptedConnectionsAllowed())
+ {
+ // this is most likely an unencrypted handshake, so if we can find a peer manager
+ // for the info hash in it, add it to the list of potential peers of that peer manager
+ // so it will be contacted later on
+ /* buf_size += sock->readData(buf + buf_size,ba);
+ if (buf_size >= 48)
+ {
+ SHA1Hash rh(buf+28);
+ PeerManager* pman = server->findPeerManager(rh);
+ if (pman)
+ {
+ PotentialPeer pp;
+ pp.ip = sock->getRemoteIPAddress();
+ pp.port = sock->getRemotePort();
+ pman->addPotentialPeer(pp);
+ }
+ }
+ onFinish(false);
+ */
+ Out(SYS_CON|LOG_DEBUG) << "Switching back to normal server authenticate" << endl;
+ state = NON_ENCRYPTED_HANDSHAKE;
+ ServerAuthenticate::onReadyRead();
+ }
+ else
+ {
+ buf_size += sock->readData(buf + buf_size,ba);
+ if (buf_size >= 96)
+ handleYA();
+ }
+ break;
+ case WAITING_FOR_REQ1:
+ if (buf_size + ba > MAX_SEA_BUF_SIZE)
+ ba = MAX_SEA_BUF_SIZE - buf_size;
+
+ buf_size += sock->readData(buf + buf_size,ba);
+ findReq1();
+ break;
+ case FOUND_REQ1:
+ if (buf_size + ba > MAX_SEA_BUF_SIZE)
+ ba = MAX_SEA_BUF_SIZE - buf_size;
+
+ buf_size += sock->readData(buf + buf_size,ba);
+ calculateSKey();
+ break;
+ case FOUND_INFO_HASH:
+ if (buf_size + ba > MAX_SEA_BUF_SIZE)
+ ba = MAX_SEA_BUF_SIZE - buf_size;
+
+ buf_size += sock->readData(buf + buf_size,ba);
+ processVC();
+ break;
+ case WAIT_FOR_PAD_C:
+ if (buf_size + ba > MAX_SEA_BUF_SIZE)
+ ba = MAX_SEA_BUF_SIZE - buf_size;
+
+ buf_size += sock->readData(buf + buf_size,ba);
+ handlePadC();
+ break;
+ case WAIT_FOR_IA:
+ if (buf_size + ba > MAX_SEA_BUF_SIZE)
+ ba = MAX_SEA_BUF_SIZE - buf_size;
+
+ buf_size += sock->readData(buf + buf_size,ba);
+ handleIA();
+ break;
+ case NON_ENCRYPTED_HANDSHAKE:
+ ServerAuthenticate::onReadyRead();
+ break;
+ }
+ }
+
+
+}
+#include "encryptedserverauthenticate.moc"
diff --git a/libktorrent/mse/encryptedserverauthenticate.h b/libktorrent/mse/encryptedserverauthenticate.h
new file mode 100644
index 0000000..3c358cd
--- /dev/null
+++ b/libktorrent/mse/encryptedserverauthenticate.h
@@ -0,0 +1,80 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef MSEENCRYPTEDSERVERAUTHENTICATE_H
+#define MSEENCRYPTEDSERVERAUTHENTICATE_H
+
+#include <util/sha1hash.h>
+#include <torrent/serverauthenticate.h>
+#include "bigint.h"
+
+namespace mse
+{
+ class RC4Encryptor;
+
+
+ const Uint32 MAX_SEA_BUF_SIZE = 608 + 20 + 20 + 8 + 4 + 2 + 512 + 2 + 68;
+ /**
+ @author Joris Guisson <[email protected]>
+ */
+ class EncryptedServerAuthenticate : public bt::ServerAuthenticate
+ {
+ Q_OBJECT
+ public:
+ EncryptedServerAuthenticate(mse::StreamSocket* sock, bt::Server* server);
+ virtual ~EncryptedServerAuthenticate();
+
+ private slots:
+ virtual void onReadyRead();
+
+ private:
+ void handleYA();
+ void sendYB();
+ void findReq1();
+ void calculateSKey();
+ void processVC();
+ void handlePadC();
+ void handleIA();
+
+ private:
+ enum State
+ {
+ WAITING_FOR_YA,
+ WAITING_FOR_REQ1,
+ FOUND_REQ1,
+ FOUND_INFO_HASH,
+ WAIT_FOR_PAD_C,
+ WAIT_FOR_IA,
+ NON_ENCRYPTED_HANDSHAKE
+ };
+ BigInt xb,yb,s,ya;
+ bt::SHA1Hash skey,info_hash;
+ State state;
+ Uint8 buf[MAX_SEA_BUF_SIZE];
+ Uint32 buf_size;
+ Uint32 req1_off;
+ Uint32 crypto_provide,crypto_select;
+ Uint16 pad_C_len;
+ Uint16 ia_len;
+ RC4Encryptor* our_rc4;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/mse/functions.cpp b/libktorrent/mse/functions.cpp
new file mode 100644
index 0000000..bb19b93
--- /dev/null
+++ b/libktorrent/mse/functions.cpp
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <torrent/globals.h>
+#include <util/sha1hash.h>
+#include "functions.h"
+#include "bigint.h"
+
+using namespace bt;
+
+namespace mse
+{
+ /*
+ static const BigInt P = BigInt(
+ "0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD"
+ "129024E088A67CC74020BBEA63B139B22514A08798E3404"
+ "DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C"
+ "245E485B576625E7EC6F44C42E9A63A36210000000000090563");
+ */
+ static const BigInt P = BigInt("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563");
+
+ void GeneratePublicPrivateKey(BigInt & priv,BigInt & pub)
+ {
+ BigInt G = BigInt("0x02");
+ priv = BigInt::random();
+ pub = BigInt::powerMod(G,priv,P);
+ }
+
+ BigInt DHSecret(const BigInt & our_priv,const BigInt & peer_pub)
+ {
+ return BigInt::powerMod(peer_pub,our_priv,P);
+ }
+
+ bt::SHA1Hash EncryptionKey(bool a,const BigInt & s,const bt::SHA1Hash & skey)
+ {
+ Uint8 buf[120];
+ memcpy(buf,"key",3);
+ buf[3] = (Uint8)(a ? 'A' : 'B');
+ s.toBuffer(buf + 4,96);
+ memcpy(buf + 100,skey.getData(),20);
+ return bt::SHA1Hash::generate(buf,120);
+ }
+
+ void DumpBigInt(const QString & name,const BigInt & bi)
+ {
+ static Uint8 buf[512];
+ Uint32 nb = bi.toBuffer(buf,512);
+ bt::Log & lg = Out();
+ lg << name << " (" << nb << ") = ";
+ for (Uint32 i = 0;i < nb;i++)
+ {
+ lg << QString("0x%1 ").arg(buf[i],0,16);
+ }
+ lg << endl;
+ }
+
+}
diff --git a/libktorrent/mse/functions.h b/libktorrent/mse/functions.h
new file mode 100644
index 0000000..4be1667
--- /dev/null
+++ b/libktorrent/mse/functions.h
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef MSEFUNCTIONS_H
+#define MSEFUNCTIONS_H
+
+namespace bt
+{
+ class SHA1Hash;
+}
+
+namespace mse
+{
+ class BigInt;
+
+ void GeneratePublicPrivateKey(BigInt & pub,BigInt & priv);
+ BigInt DHSecret(const BigInt & our_priv,const BigInt & peer_pub);
+ bt::SHA1Hash EncryptionKey(bool a,const BigInt & s,const bt::SHA1Hash & skey);
+
+ void DumpBigInt(const QString & name,const BigInt & bi);
+}
+
+#endif
diff --git a/libktorrent/mse/rc4encryptor.cpp b/libktorrent/mse/rc4encryptor.cpp
new file mode 100644
index 0000000..422fe5d
--- /dev/null
+++ b/libktorrent/mse/rc4encryptor.cpp
@@ -0,0 +1,100 @@
+ /***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "rc4encryptor.h"
+
+namespace mse
+{
+ static void swap(Uint8 & a,Uint8 & b)
+ {
+ Uint8 tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ static Uint8 rc4_enc_buffer[bt::MAX_MSGLEN];
+
+ RC4::RC4(const Uint8* key,Uint32 size) : i(0),j(0)
+ {
+ // initialize state
+ for (Uint32 t = 0;t < 256;t++)
+ s[t] = t;
+
+ j = 0;
+ for (Uint32 t=0;t < 256;t++)
+ {
+ j = (j + s[t] + key[t % size]) & 0xff;
+ swap(s[t],s[j]);
+ }
+
+ i = j = 0;
+ }
+
+ RC4::~RC4()
+ {
+ }
+
+ void RC4::process(const Uint8* in,Uint8* out,Uint32 size)
+ {
+ for (Uint32 k = 0;k < size;k++)
+ {
+ out[k] = process(in[k]);
+ }
+ }
+
+ Uint8 RC4::process(Uint8 b)
+ {
+ i = (i + 1) & 0xff;
+ j = (j + s[i]) & 0xff;
+ swap(s[i],s[j]);
+ Uint8 tmp = s[ (s[i] + s[j]) & 0xff];
+ return tmp ^ b;
+ }
+
+
+ RC4Encryptor::RC4Encryptor(const bt::SHA1Hash & dk,const bt::SHA1Hash & ek)
+ : enc(ek.getData(),20),dec(dk.getData(),20)
+ {
+ Uint8 tmp[1024];
+ enc.process(tmp,tmp,1024);
+ dec.process(tmp,tmp,1024);
+ }
+
+
+ RC4Encryptor::~RC4Encryptor()
+ {}
+
+
+ void RC4Encryptor::decrypt(Uint8* data,Uint32 len)
+ {
+ dec.process(data,data,len);
+ }
+
+ const Uint8* RC4Encryptor::encrypt(const Uint8* data,Uint32 len)
+ {
+ enc.process(data,rc4_enc_buffer,len);
+ return rc4_enc_buffer;
+ }
+
+ void RC4Encryptor::encryptReplace(Uint8* data,Uint32 len)
+ {
+ enc.process(data,data,len);
+ }
+
+}
diff --git a/libktorrent/mse/rc4encryptor.h b/libktorrent/mse/rc4encryptor.h
new file mode 100644
index 0000000..650b54e
--- /dev/null
+++ b/libktorrent/mse/rc4encryptor.h
@@ -0,0 +1,96 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef MSERC4ENCRYPTOR_H
+#define MSERC4ENCRYPTOR_H
+
+#include <util/sha1hash.h>
+#include <util/constants.h>
+
+using bt::Uint8;
+using bt::Uint32;
+
+namespace mse
+{
+ /**
+ * Helper class to do the actual encryption / decryption
+ */
+ class RC4
+ {
+ Uint8 i,j;
+ Uint8 s[256];
+ public:
+ RC4(const Uint8* key,Uint32 size);
+ virtual ~RC4();
+
+ void process(const Uint8* in,Uint8* out,Uint32 size);
+ Uint8 process(Uint8 b);
+ };
+
+ /**
+ * @author Joris Guisson <[email protected]>
+ *
+ * RC4 encryptor. Uses the RC4 algorithm to encrypt and decrypt data.
+ * This class has a static encryption buffer, which makes it not thread safe
+ * because the buffer is not protected by mutexes.
+ */
+ class RC4Encryptor
+ {
+ RC4 enc,dec;
+ public:
+ RC4Encryptor(const bt::SHA1Hash & dkey,const bt::SHA1Hash & ekey);
+ virtual ~RC4Encryptor();
+
+ /**
+ * Decrypt some data, decryption happens in place (original data gets overwritten)
+ * @param data The data
+ * @param len Size of the data
+ */
+ void decrypt(Uint8* data,Uint32 len);
+
+ /**
+ * Encrypt the data. Encryption happens into the static buffer.
+ * So that the data passed to this function is never overwritten.
+ * If we send pieces we point directly to the mmap region of data,
+ * this cannot be overwritten, hence the static buffer.
+ * @param data The data
+ * @param len The length of the data
+ * @return Pointer to the static buffer
+ */
+ const Uint8* encrypt(const Uint8* data,Uint32 len);
+
+ /**
+ * Encrypt data, encryption will happen in the same buffer. So data will
+ * be changed replaced by it's encrypted version.
+ * @param data The data to encrypt
+ * @param len The length of the data
+ */
+ void encryptReplace(Uint8* data,Uint32 len);
+
+ /**
+ * Encrypts one byte.
+ * @param b The byte to encrypt
+ * @return The encrypted byte
+ */
+ Uint8 encrypt(Uint8 b) {return enc.process(b);}
+ };
+
+}
+
+#endif
diff --git a/libktorrent/mse/streamsocket.cpp b/libktorrent/mse/streamsocket.cpp
new file mode 100644
index 0000000..19a0a2e
--- /dev/null
+++ b/libktorrent/mse/streamsocket.cpp
@@ -0,0 +1,326 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <errno.h>
+#include <qsocket.h>
+#include <qsocketdevice.h>
+#include <util/sha1hash.h>
+#include <util/log.h>
+#include <torrent/peer.h>
+#include <torrent/globals.h>
+#include <torrent/authenticatebase.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <net/socketmonitor.h>
+#include "streamsocket.h"
+#include "rc4encryptor.h"
+
+using namespace bt;
+using namespace net;
+
+namespace mse
+{
+
+ Uint8 StreamSocket::tos = IPTOS_THROUGHPUT;
+ Uint32 StreamSocket::num_connecting = 0;
+ Uint32 StreamSocket::max_connecting = 50;
+
+ StreamSocket::StreamSocket() : sock(0),enc(0),monitored(false)
+ {
+ sock = new BufferedSocket(true);
+ sock->setNonBlocking();
+ reinserted_data = 0;
+ reinserted_data_size = 0;
+ reinserted_data_read = 0;
+
+ }
+
+ StreamSocket::StreamSocket(int fd) : sock(0),enc(0),monitored(false)
+ {
+ sock = new BufferedSocket(fd);
+ sock->setNonBlocking();
+ reinserted_data = 0;
+ reinserted_data_size = 0;
+ reinserted_data_read = 0;
+ sock->setTOS(tos);
+ }
+
+ StreamSocket::~StreamSocket()
+ {
+ // make sure the number of connecting sockets is updated
+ if (connecting() && num_connecting > 0)
+ num_connecting--;
+
+ SocketMonitor::instance().remove(sock);
+ delete [] reinserted_data;
+ delete enc;
+ delete sock;
+ }
+
+ void StreamSocket::startMonitoring(net::SocketReader* rdr,net::SocketWriter* wrt)
+ {
+ this->rdr = rdr;
+ this->wrt = wrt;
+ sock->setReader(this);
+ sock->setWriter(this);
+ SocketMonitor::instance().add(sock);
+ monitored = true;
+ if (reinserted_data)
+ {
+ if (enc)
+ enc->decrypt(reinserted_data + reinserted_data_read,
+ reinserted_data_size - reinserted_data_read);
+
+ rdr->onDataReady(reinserted_data + reinserted_data_read,
+ reinserted_data_size - reinserted_data_read);
+ delete [] reinserted_data;
+ reinserted_data = 0;
+ reinserted_data_size = 0;
+ }
+ }
+
+
+ Uint32 StreamSocket::sendData(const Uint8* data,Uint32 len)
+ {
+ if (enc)
+ {
+ // we need to make sure all data is sent because of the encryption
+ Uint32 ds = 0;
+ const Uint8* ed = enc->encrypt(data,len);
+ while (sock->ok() && ds < len)
+ {
+ Uint32 ret = sock->send(ed + ds,len - ds);
+ ds += ret;
+ if (ret == 0)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "ret = 0" << endl;
+ }
+ }
+ if (ds != len)
+ Out() << "ds != len" << endl;
+ return ds;
+ }
+ else
+ {
+ Uint32 ret = sock->send(data,len);
+ if (ret != len)
+ Out() << "ret != len" << endl;
+ return ret;
+ }
+ }
+
+ Uint32 StreamSocket::readData(Uint8* buf,Uint32 len)
+ {
+ Uint32 ret2 = 0;
+ if (reinserted_data)
+ {
+ Uint32 tr = reinserted_data_size - reinserted_data_read;
+ if (tr < len)
+ {
+ memcpy(buf,reinserted_data + reinserted_data_read,tr);
+ delete [] reinserted_data;
+ reinserted_data = 0;
+ reinserted_data_size = reinserted_data_read = 0;
+ ret2 = tr;
+ if (enc)
+ enc->decrypt(buf,tr);
+ }
+ else
+ {
+ tr = len;
+ memcpy(buf,reinserted_data + reinserted_data_read,tr);
+ reinserted_data_read += tr;
+ if (enc)
+ enc->decrypt(buf,tr);
+ return tr;
+ }
+ }
+
+ if (len == ret2)
+ return ret2;
+
+ Uint32 ret = sock->recv(buf + ret2,len - ret2);
+ if (ret + ret2 > 0 && enc)
+ enc->decrypt(buf,ret + ret2);
+
+ return ret;
+ }
+
+ Uint32 StreamSocket::bytesAvailable() const
+ {
+ Uint32 ba = sock->bytesAvailable();
+ if (reinserted_data_size - reinserted_data_read > 0)
+ return ba + (reinserted_data_size - reinserted_data_read);
+ else
+ return ba;
+ }
+
+ void StreamSocket::close()
+ {
+ sock->close();
+ }
+
+ bool StreamSocket::connectTo(const QString & ip,Uint16 port)
+ {
+ // do a safety check
+ if (ip.isNull() || ip.length() == 0)
+ return false;
+
+ // we don't wanna block the current thread so set non blocking
+ sock->setNonBlocking();
+ if (sock->connectTo(Address(ip,port)))
+ {
+ sock->setTOS(tos);
+ return true;
+ }
+ else if (connecting())
+ {
+ num_connecting++;
+ }
+
+ return false;
+ }
+
+ void StreamSocket::initCrypt(const bt::SHA1Hash & dkey,const bt::SHA1Hash & ekey)
+ {
+ if (enc)
+ delete enc;
+
+ enc = new RC4Encryptor(dkey,ekey);
+ }
+
+ void StreamSocket::disableCrypt()
+ {
+ delete enc;
+ enc = 0;
+ }
+
+ bool StreamSocket::ok() const
+ {
+ return sock->ok();
+ }
+
+ QString StreamSocket::getRemoteIPAddress() const
+ {
+ return sock->getPeerName().toString();
+ }
+
+ bt::Uint16 StreamSocket::getRemotePort() const
+ {
+ return sock->getPeerName().port();
+ }
+
+ net::Address StreamSocket::getRemoteAddress() const
+ {
+ return sock->getPeerName();
+ }
+
+ void StreamSocket::setRC4Encryptor(RC4Encryptor* e)
+ {
+ if (enc)
+ delete enc;
+
+ enc = e;
+ }
+
+ void StreamSocket::reinsert(const Uint8* d,Uint32 size)
+ {
+// Out() << "Reinsert : " << size << endl;
+ Uint32 off = 0;
+ if (reinserted_data)
+ {
+ off = reinserted_data_size;
+ reinserted_data = (Uint8*)realloc(reinserted_data,reinserted_data_size + size);
+ reinserted_data_size += size;
+ }
+ else
+ {
+ reinserted_data = new Uint8[size];
+ reinserted_data_size = size;
+ }
+ memcpy(reinserted_data + off,d,size);
+ }
+
+ bool StreamSocket::connecting() const
+ {
+ return sock->state() == net::Socket::CONNECTING;
+ }
+
+ void StreamSocket::onDataReady(Uint8* buf,Uint32 size)
+ {
+ if (enc)
+ enc->decrypt(buf,size);
+
+ if (rdr)
+ rdr->onDataReady(buf,size);
+ }
+
+ Uint32 StreamSocket::onReadyToWrite(Uint8* data,Uint32 max_to_write)
+ {
+ if (!wrt)
+ return 0;
+
+ Uint32 ret = wrt->onReadyToWrite(data,max_to_write);
+ if (enc && ret > 0) // do encryption if necessary
+ enc->encryptReplace(data,ret);
+
+
+ return ret;
+ }
+
+ bool StreamSocket::hasBytesToWrite() const
+ {
+ return wrt ? wrt->hasBytesToWrite() : false;
+ }
+
+ float StreamSocket::getDownloadRate() const
+ {
+ return sock ? sock->getDownloadRate() : 0.0f;
+ }
+
+ float StreamSocket::getUploadRate() const
+ {
+ return sock ? sock->getUploadRate() : 0.0f;
+ }
+
+ bool StreamSocket::connectSuccesFull() const
+ {
+ bool ret = sock->connectSuccesFull();
+ if (ret)
+ sock->setTOS(tos);
+
+ if (num_connecting > 0)
+ num_connecting--;
+
+ return ret;
+ }
+
+ void StreamSocket::setGroupIDs(Uint32 up,Uint32 down)
+ {
+ sock->setGroupID(up,true);
+ sock->setGroupID(down,false);
+ }
+}
+
+#include "streamsocket.moc"
diff --git a/libktorrent/mse/streamsocket.h b/libktorrent/mse/streamsocket.h
new file mode 100644
index 0000000..5006a7b
--- /dev/null
+++ b/libktorrent/mse/streamsocket.h
@@ -0,0 +1,185 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef MSESTREAMSOCKET_H
+#define MSESTREAMSOCKET_H
+
+#include <qobject.h>
+#include <util/constants.h>
+#include <net/bufferedsocket.h>
+
+class QString;
+
+using bt::Uint8;
+using bt::Uint16;
+using bt::Uint32;
+
+namespace bt
+{
+ class SHA1Hash;
+ class Peer;
+ class AuthenticateBase;
+}
+
+namespace mse
+{
+ class RC4Encryptor;
+
+
+
+
+ /**
+ * @author Joris Guisson <[email protected]>
+ *
+ * Wrapper around a TCP socket which handles RC4 encryption.
+ * Once authentication is done, the sendData and readData interfaces should
+ * not be used anymore, a SocketReader and SocketWriter should be provided,
+ * so that reading and writing is controlled from the monitor thread.
+ */
+ class StreamSocket : public QObject,public net::SocketReader,public net::SocketWriter
+ {
+ Q_OBJECT
+ public:
+ StreamSocket();
+ StreamSocket(int fd);
+ virtual ~StreamSocket();
+
+ /**
+ * Send a chunk of data. (Does not encrypt the data)
+ * @param data The data
+ * @param len The length
+ * @return Number of bytes written
+ */
+ Uint32 sendData(const Uint8* data,Uint32 len);
+
+ /**
+ * Reads data from the peer.
+ * @param buf The buffer to store the data
+ * @param len The maximum number of bytes to read
+ * @return The number of bytes read
+ */
+ Uint32 readData(Uint8* buf,Uint32 len);
+
+ /// Get the number of bytes available to read.
+ Uint32 bytesAvailable() const;
+
+ /// Are we using encryption
+ bool encrypted() const {return enc != 0;}
+
+ /**
+ * Initialize the RC4 encryption algorithm.
+ * @param dkey
+ * @param ekey
+ */
+ void initCrypt(const bt::SHA1Hash & dkey,const bt::SHA1Hash & ekey);
+
+ /// Set the encryptor
+ void setRC4Encryptor(RC4Encryptor* enc);
+
+ /// Disables encryption. All data will be sent over as plain text.
+ void disableCrypt();
+
+ /// Close the socket
+ void close();
+
+ /// Connect the socket to a remote host
+ bool connectTo(const QString & ip,Uint16 port);
+
+ /// Get the IP address of the remote peer
+ QString getRemoteIPAddress() const;
+
+ /// Get the port of the remote peer
+ bt::Uint16 getRemotePort() const;
+
+ /// Get the full address
+ net::Address getRemoteAddress() const;
+
+ /**
+ * Reinsert data, this is needed when we read to much during the crypto handshake.
+ * This data will be the first to read out. The data will be copied to a temporary buffer
+ * which will be destroyed when the reinserted data has been read.
+ */
+ void reinsert(const Uint8* d,Uint32 size);
+
+ /// see if the socket is still OK
+ bool ok() const;
+
+ /// Get the file descriptor
+ int fd() const {return sock->fd();}
+
+ /// Start monitoring of this socket by the monitor thread
+ void startMonitoring(net::SocketReader* rdr,net::SocketWriter* wrt);
+
+ /// Is this socket connecting to a remote host
+ bool connecting() const;
+
+ /// See if a connect was success full
+ bool connectSuccesFull() const;
+
+ /// Get the current download rate
+ float getDownloadRate() const;
+
+ /// Get the current download rate
+ float getUploadRate() const;
+
+ /**
+ * Set the TOS byte for new sockets.
+ * @param t TOS value
+ */
+ static void setTOS(Uint8 t) {tos = t;}
+
+ /**
+ * Set the download and upload group ID's
+ * @param up Upload group ID
+ * @param down Download group ID
+ */
+ void setGroupIDs(Uint32 up,Uint32 down);
+
+ /**
+ * Check if we are allowed to initiate another outgoing connection.
+ */
+ static bool canInitiateNewConnection() {return num_connecting < max_connecting;}
+
+ /**
+ * Set the maximum number of connecting sockets we are allowed to have.
+ */
+ static void setMaxConnecting(Uint32 mc) {max_connecting = mc;}
+ private:
+ virtual void onDataReady(Uint8* buf,Uint32 size);
+ virtual Uint32 onReadyToWrite(Uint8* data,Uint32 max_to_write);
+ virtual bool hasBytesToWrite() const;
+
+ private:
+ net::BufferedSocket* sock;
+ RC4Encryptor* enc;
+ Uint8* reinserted_data;
+ Uint32 reinserted_data_size;
+ Uint32 reinserted_data_read;
+ bool monitored;
+ net::SocketReader* rdr;
+ net::SocketWriter* wrt;
+
+ static Uint8 tos;
+ static Uint32 num_connecting; // the number of connections we have in SYN_SENT state
+ static Uint32 max_connecting;
+ };
+
+}
+
+#endif