From 9da984b86692ae492a2d7876574b9731a6c9cf53 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 20 Dec 2016 14:10:14 -0500 Subject: [PATCH 01/36] use ElGamalEncrypt --- Crypto.cpp | 24 ++++++++---------------- Crypto.h | 16 +--------------- Garlic.cpp | 3 +-- TunnelConfig.h | 3 +-- 4 files changed, 11 insertions(+), 35 deletions(-) diff --git a/Crypto.cpp b/Crypto.cpp index a4a794ac..cc138923 100644 --- a/Crypto.cpp +++ b/Crypto.cpp @@ -272,10 +272,9 @@ namespace crypto } // ElGamal - - ElGamalEncryption::ElGamalEncryption (const uint8_t * key) + void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, bool zeroPadding) { - ctx = BN_CTX_new (); + BN_CTX * ctx = BN_CTX_new (); // select random k BIGNUM * k = BN_new (); #if defined(__x86_64__) @@ -284,6 +283,7 @@ namespace crypto BN_rand (k, ELGAMAL_SHORT_EXPONENT_NUM_BITS, -1, 1); // short exponent of 226 bits #endif // calculate a + BIGNUM * a; if (g_ElggTable) a = ElggPow (k, g_ElggTable, ctx); else @@ -295,21 +295,10 @@ namespace crypto BIGNUM * y = BN_new (); BN_bin2bn (key, 256, y); // calculate b1 - b1 = BN_new (); + BIGNUM * b1 = BN_new (); BN_mod_exp (b1, y, k, elgp, ctx); BN_free (y); BN_free (k); - } - - ElGamalEncryption::~ElGamalEncryption () - { - BN_CTX_free (ctx); - BN_free (a); - BN_free (b1); - } - - void ElGamalEncryption::Encrypt (const uint8_t * data, uint8_t * encrypted, bool zeroPadding) const - { // create m uint8_t m[255]; m[0] = 0xFF; @@ -319,6 +308,7 @@ namespace crypto BIGNUM * b = BN_new (); BN_bin2bn (m, 255, b); BN_mod_mul (b, b1, b, elgp, ctx); + BN_free (b1); // copy a and b if (zeroPadding) { @@ -333,8 +323,10 @@ namespace crypto bn2buf (b, encrypted + 256, 256); } BN_free (b); + BN_free (a); + BN_CTX_free (ctx); } - + bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, bool zeroPadding) { diff --git a/Crypto.h b/Crypto.h index 9e35f073..00255a37 100644 --- a/Crypto.h +++ b/Crypto.h @@ -47,21 +47,7 @@ namespace crypto }; // ElGamal - class ElGamalEncryption - { - public: - - ElGamalEncryption (const uint8_t * key); - ~ElGamalEncryption (); - - void Encrypt (const uint8_t * data, uint8_t * encrypted, bool zeroPadding = false) const; - - private: - - BN_CTX * ctx; - BIGNUM * a, * b1; - }; - + void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, bool zeroPadding = false); bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, bool zeroPadding = false); void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub); diff --git a/Garlic.cpp b/Garlic.cpp index 81941cfc..51fa6c3b 100644 --- a/Garlic.cpp +++ b/Garlic.cpp @@ -187,8 +187,7 @@ namespace garlic RAND_bytes (elGamal.preIV, 32); // Pre-IV uint8_t iv[32]; // IV is first 16 bytes SHA256(elGamal.preIV, 32, iv); - i2p::crypto::ElGamalEncryption elGamalEncryption (m_Destination->GetEncryptionPublicKey ()); - elGamalEncryption.Encrypt ((uint8_t *)&elGamal, buf, true); + i2p::crypto::ElGamalEncrypt (m_Destination->GetEncryptionPublicKey (), (uint8_t *)&elGamal, buf, true); m_Encryption.SetIV (iv); buf += 514; len += 514; diff --git a/TunnelConfig.h b/TunnelConfig.h index cb31243f..c131059c 100644 --- a/TunnelConfig.h +++ b/TunnelConfig.h @@ -101,8 +101,7 @@ namespace tunnel htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ()); htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET); - i2p::crypto::ElGamalEncryption elGamalEncryption (ident->GetEncryptionPublicKey ()); - elGamalEncryption.Encrypt (clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET); + i2p::crypto::ElGamalEncrypt (ident->GetEncryptionPublicKey (), clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET); memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); } }; From e699d3d02d66d12c032a94600096210d0dd2930a Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 21 Dec 2016 07:41:18 -0500 Subject: [PATCH 02/36] SNI support --- Reseed.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Reseed.cpp b/Reseed.cpp index adba530b..b4eaa76c 100644 --- a/Reseed.cpp +++ b/Reseed.cpp @@ -444,6 +444,7 @@ namespace data s.lowest_layer().connect (*it, ecode); if (!ecode) { + SSL_set_tlsext_host_name(s.native_handle(), url.host.c_str ()); s.handshake (boost::asio::ssl::stream_base::client, ecode); if (!ecode) { From 84b3ad3221072497bb737d4ca7c3e1ace3bd2f4e Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 21 Dec 2016 14:52:17 -0500 Subject: [PATCH 03/36] removed non-used ceritificates --- .../reseed/cheezybudz_at_mail.i2p.crt | 32 ----------------- .../reseed/j_at_torontocrypto.org.crt | 34 ------------------- 2 files changed, 66 deletions(-) delete mode 100644 contrib/certificates/reseed/cheezybudz_at_mail.i2p.crt delete mode 100644 contrib/certificates/reseed/j_at_torontocrypto.org.crt diff --git a/contrib/certificates/reseed/cheezybudz_at_mail.i2p.crt b/contrib/certificates/reseed/cheezybudz_at_mail.i2p.crt deleted file mode 100644 index fecdf0b9..00000000 --- a/contrib/certificates/reseed/cheezybudz_at_mail.i2p.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFhTCCA22gAwIBAgIEeCJXkjANBgkqhkiG9w0BAQ0FADBzMQswCQYDVQQGEwJY -WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt -b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEcMBoGA1UEAwwTY2hlZXp5YnVkekBt -YWlsLmkycDAeFw0xNDEyMTYyMTU3MTZaFw0yNDEyMTUyMTU3MTZaMHMxCzAJBgNV -BAYTAlhYMQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBB -bm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRwwGgYDVQQDDBNjaGVlenli -dWR6QG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgLkj -Jsp8pjRi5N/JHHz+MXisgbI9G0vpd3yDhHvae3oF87iiQbxflcdcoH0l5RZL0cAn -w4amhqoOk2qhf+NSAEkiPWhk7CzPBRwDExEM/gmHYLWXbfnoHGaEls9ORGuDlDmN -hCFJVrxaZocIOi/7gZ4A+tC8wq+1aoe0Yhr381OW59w9AdUAWjBWibO3V59dEklL -7HqfOc2v7AMKDLJWgPekj8ZbqA9lRxHM6Djtjz4d9QXeQa8j3xLXTX1QbkvJBBX1 -9Rzi/Nzv622lzoZ/Z/61jW7Bz+h9aJ6qp4on9K4ygUw/VduTOH/1ryQmw87x4MFQ -Z/Y86lOl7XZxBjtpYpGQW/5LmBe2BCfWgIYe9N5ionNgAe5TNEIDngP9AvJmcTyF -KcGgOgXQO9EeHEdgf4nC6RbGrb2sBtRjWJv5nOhHRG9tpwYkw/Zc5ZNHOymYpPMg -wce3me+1psJFt+gXhDcvxpRgTZpXfz91K/nKt3+szcYFluqhJLi6nL1TmXQVn51X -lGD1bcy1VUof+uKyb223JX5rm9WQ48GzUfy5cK4o+khEo0RLb21FwG5iJwVzhtoN -xQS1TO6pwLn8Si1ePRwntzlOm8DPIwdUkPBQNJ9DDkcdVia2GgbVM6LH8lrukekq -soYfwmOTsFRkGo04ujDI/IeMrl3zmJphyQkGx18CAwEAAaMhMB8wHQYDVR0OBBYE -FJ2MHeHnfCpEuYvC/9eK2ML9ne2eMA0GCSqGSIb3DQEBDQUAA4ICAQA3XUS7Zw1i -RJWPSu2oLzV7oTtIW5po2Gd5BL3oU6BvlK1zLw/z/soF/LopeHQudBYxYckyv4MG -gTNS9fcKkVdhNyLI/R2S0nQ/VFhTzuvq8HnnTOpvopA/cXTQlgrhGB2ajIZMYsXe -lei0V5H23etXTbYZWK6/IgoALk5vowde9lpJEIBhupIafqFg0tAo4LX07/eNxDOp -nXrShsYhHNaRhglS+0Gqj1UK0WvgMJxQKJm/VLi7jx8vfRkqXs/b76XT+VMQuUJd -l5llQwpOicQhX/ZTAO+iWrDaO7mz/ZDweLxnfWd3m2JwDJlE9K5l98zdcve96NRZ -ePnK8vBoAPQ9iHhwFSC5GpirK1KmT/BDLjqEF3H/HgPdPWSh97AUFpBryEIdZk1q -Czi9DCvwHNpbpI20Fo48+2N7sbvq4onZZqx5V0SjTj/9bHSSDwG9ok1JqWoZmRvo -p4MIywAJowlvPNc++jSHT3R7segeNUi/UdYCmm70j1av+0aEknmvPtF6atsHJ22X -5OMBhiPi1pudFWFJFWk4WOjrK/juwHHfHNgFVyziva4q6wPKrPno0gO5pCpfRUld -QAoSPgo8LAB3dugt5Xfsuone2GhLi1SLQlWFJWHswd/ypWa0FB+xn6Edkc1noOWY -06dwfEP/gTCAnSplLyrFWxnyHManBxq/bQ== ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/j_at_torontocrypto.org.crt b/contrib/certificates/reseed/j_at_torontocrypto.org.crt deleted file mode 100644 index 4a2789ec..00000000 --- a/contrib/certificates/reseed/j_at_torontocrypto.org.crt +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- From 88c3532162230c2f2c48430117b56455cdd62890 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 21 Dec 2016 14:54:48 -0500 Subject: [PATCH 04/36] removed ssl ceritifcates --- .../ssl/download.xxlspeed.com.crt | 19 ----------- contrib/certificates/ssl/i2p.mooo.com.crt | 23 ------------- .../ssl/i2pseed.zarrenspry.info.crt | 27 --------------- contrib/certificates/ssl/netdb.i2p2.no.crt | 23 ------------- .../ssl/reseed.i2p-projekt.de.crt | 24 -------------- .../certificates/ssl/reseed.i2p.vzaws.com.crt | 33 ------------------- .../certificates/ssl/uk.reseed.i2p2.no.crt | 33 ------------------- .../certificates/ssl/us.reseed.i2p2.no.crt | 20 ----------- contrib/certificates/ssl/user.mx24.eu.crt | 17 ---------- 9 files changed, 219 deletions(-) delete mode 100644 contrib/certificates/ssl/download.xxlspeed.com.crt delete mode 100644 contrib/certificates/ssl/i2p.mooo.com.crt delete mode 100644 contrib/certificates/ssl/i2pseed.zarrenspry.info.crt delete mode 100644 contrib/certificates/ssl/netdb.i2p2.no.crt delete mode 100644 contrib/certificates/ssl/reseed.i2p-projekt.de.crt delete mode 100644 contrib/certificates/ssl/reseed.i2p.vzaws.com.crt delete mode 100644 contrib/certificates/ssl/uk.reseed.i2p2.no.crt delete mode 100644 contrib/certificates/ssl/us.reseed.i2p2.no.crt delete mode 100644 contrib/certificates/ssl/user.mx24.eu.crt diff --git a/contrib/certificates/ssl/download.xxlspeed.com.crt b/contrib/certificates/ssl/download.xxlspeed.com.crt deleted file mode 100644 index 253c0509..00000000 --- a/contrib/certificates/ssl/download.xxlspeed.com.crt +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDLDCCArKgAwIBAgIJAMOgj4vE9qpcMAoGCCqGSM49BAMEMIHTMQswCQYDVQQG -EwJERTEeMBwGA1UECAwVZG93bmxvYWQueHhsc3BlZWQuY29tMR4wHAYDVQQHDBVk -b3dubG9hZC54eGxzcGVlZC5jb20xHjAcBgNVBAoMFWRvd25sb2FkLnh4bHNwZWVk -LmNvbTEeMBwGA1UECwwVZG93bmxvYWQueHhsc3BlZWQuY29tMR4wHAYDVQQDDBVk -b3dubG9hZC54eGxzcGVlZC5jb20xJDAiBgkqhkiG9w0BCQEWFWRvd25sb2FkLnh4 -bHNwZWVkLmNvbTAeFw0xNTEyMzAxMTI4NDJaFw0yMTA2MjExMTI4NDJaMIHTMQsw -CQYDVQQGEwJERTEeMBwGA1UECAwVZG93bmxvYWQueHhsc3BlZWQuY29tMR4wHAYD -VQQHDBVkb3dubG9hZC54eGxzcGVlZC5jb20xHjAcBgNVBAoMFWRvd25sb2FkLnh4 -bHNwZWVkLmNvbTEeMBwGA1UECwwVZG93bmxvYWQueHhsc3BlZWQuY29tMR4wHAYD -VQQDDBVkb3dubG9hZC54eGxzcGVlZC5jb20xJDAiBgkqhkiG9w0BCQEWFWRvd25s -b2FkLnh4bHNwZWVkLmNvbTB2MBAGByqGSM49AgEGBSuBBAAiA2IABFObW+pRshVD -gvMPvGdPGji2DAfdvkl3gvpyiQ0PUqxuTxwtBlwBo6cz2cMnkKdActuvE/VOTRG5 -/z7CcvG7b0+qgrHDffg7C2wWlAN0dSjuoV2Av7VoN1vEU96TCtheSqNQME4wHQYD -VR0OBBYEFPbEZH9oidjadUfvsnsh23b1jZnVMB8GA1UdIwQYMBaAFPbEZH9oidja -dUfvsnsh23b1jZnVMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwQDaAAwZQIwT1py -AV2hLFL/5ZgwmybdaCBBUsj3cGYroXb/Z2BHLDYmH8enK0DhhWyPdN1a7eCsAjEA -oQRU7lhXrisckjA2911Q5mA8y2sFAN/PDPrUeU9PI5vDF/ezTi20zULMOqbU1uRz ------END CERTIFICATE----- diff --git a/contrib/certificates/ssl/i2p.mooo.com.crt b/contrib/certificates/ssl/i2p.mooo.com.crt deleted file mode 100644 index 839767b3..00000000 --- a/contrib/certificates/ssl/i2p.mooo.com.crt +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDvTCCAqWgAwIBAgIJAOeW0ejPrHimMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNV -BAYTAlVTMQ0wCwYDVQQIDARub25lMQ0wCwYDVQQHDARub25lMQ0wCwYDVQQKDARu -b25lMQ0wCwYDVQQLDARub25lMRUwEwYDVQQDDAxpMnAubW9vby5jb20xEzARBgkq -hkiG9w0BCQEWBG5vbmUwHhcNMTUwMjA4MTczMzA5WhcNMTkwMzE5MTczMzA5WjB1 -MQswCQYDVQQGEwJVUzENMAsGA1UECAwEbm9uZTENMAsGA1UEBwwEbm9uZTENMAsG -A1UECgwEbm9uZTENMAsGA1UECwwEbm9uZTEVMBMGA1UEAwwMaTJwLm1vb28uY29t -MRMwEQYJKoZIhvcNAQkBFgRub25lMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAqxej7oRl9GOb8benIBCENrJXoow1iWhI9M+2nU0SaonrCDql5M2YMlwd -HzYUWtFbRjz2NinjB0fgFq9cfzHfr1Sc8k/OeGg1jvNfqt8wWo9tryQNjiHtDQUZ -6lQ5T13I+lj0CBasowgbApKQfrYjvaeuTaVYTfP8IVA60hoUQ+sy9JN+Unsx3/0Y -PLLd98+bT27qYuBNRB1g/ifUTd9Wosj2PevGBlCxYDaUjmCG4Q8kcQr87KvM6RTu -3AV61s/Wyy1j2YemlGG/ZhJ44YnlVMSu1vTjt9HInVf3lRRx/+RzbQO3lqeVC8LC -Bq3KbSlfJVx4vHslfHwBFw9A4rmD1QIDAQABo1AwTjAdBgNVHQ4EFgQUsSUvX0ED -yivB67iksVwZ+b8vLtQwHwYDVR0jBBgwFoAUsSUvX0EDyivB67iksVwZ+b8vLtQw -DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAde4wts7Q8TylFEc38ftJ -2f285fFIR7P1SSbBcHPK2eBwLEg0zJyFrCeiHuEpPrn+d5GqL2zOskjfcESGmDBT -aFajj8jPBJj/AmpkdWJG6a1YKro5tu9wrlenGwHOHu2/Cl0IJvafxrOs2x4G+2Nl -5Hcw/FIy8mK7eIch4pACfi0zNMZ6KMCKfX9bxPrQo78WdBfVjbrIBlgyOQJ5NJEF -JlWvS7Butv7eERi4I2huN5VRJSCFzjbuO+tjP3I8IB6WgdBmTeqq8ObtXRgahBuD -ZmkvqVSfIzK5JN4GjO8FOdCBomuwm9A92kgmAptwQwAHM9qCDJpH8L07/7poxlGb -iA== ------END CERTIFICATE----- diff --git a/contrib/certificates/ssl/i2pseed.zarrenspry.info.crt b/contrib/certificates/ssl/i2pseed.zarrenspry.info.crt deleted file mode 100644 index e855d6ed..00000000 --- a/contrib/certificates/ssl/i2pseed.zarrenspry.info.crt +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEkzCCA3ugAwIBAgIJAKsW7idQxp0aMA0GCSqGSIb3DQEBCwUAMIHfMQswCQYD -VQQGEwJVSzEgMB4GA1UECAwXaTJwc2VlZC56YXJyZW5zcHJ5LmluZm8xIDAeBgNV -BAcMF2kycHNlZWQuemFycmVuc3ByeS5pbmZvMSAwHgYDVQQKDBdpMnBzZWVkLnph -cnJlbnNwcnkuaW5mbzEgMB4GA1UECwwXaTJwc2VlZC56YXJyZW5zcHJ5LmluZm8x -IDAeBgNVBAMMF2kycHNlZWQuemFycmVuc3ByeS5pbmZvMSYwJAYJKoZIhvcNAQkB -FhdpMnBzZWVkLnphcnJlbnNwcnkuaW5mbzAeFw0xNDEyMjgxOTI3MDdaFw0xOTAy -MDUxOTI3MDdaMIHfMQswCQYDVQQGEwJVSzEgMB4GA1UECAwXaTJwc2VlZC56YXJy -ZW5zcHJ5LmluZm8xIDAeBgNVBAcMF2kycHNlZWQuemFycmVuc3ByeS5pbmZvMSAw -HgYDVQQKDBdpMnBzZWVkLnphcnJlbnNwcnkuaW5mbzEgMB4GA1UECwwXaTJwc2Vl -ZC56YXJyZW5zcHJ5LmluZm8xIDAeBgNVBAMMF2kycHNlZWQuemFycmVuc3ByeS5p -bmZvMSYwJAYJKoZIhvcNAQkBFhdpMnBzZWVkLnphcnJlbnNwcnkuaW5mbzCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANrEncHwS+7R0Ti/jZa2Ex7ujglV -huYO59nLxeAOpEQwn6V41X5+L0hmhM0zuYavuP1jzKfF/Cn0CG1PqkGbEnXrTOGf -4gMj2wy/UVVFXaPQwldi+CEiNo6nI5S+T/upg5VK6M5/ahYbfIbX5xF27QNPV5qW -RnM0VK4gIQkFFtpiI0dFcEU9VYe+cg7a4Jvxc5LzqaIBZHWMX6alPfBT70LkYiiQ -76IRw5oBmqZjfIdiudRhFkezMkDomKSgLR2/0HJbekq2WeLXJLMPM1rdpCYldBEi -t6Zng9uAJa1mA6Al4RhO1aQEPj9Vo5h+Vj6FHJAJJcb+YW6wLKBkJVGLF4UCAwEA -AaNQME4wHQYDVR0OBBYEFL538Fr1l/9YQgG+iZvJUuOzAaVaMB8GA1UdIwQYMBaA -FL538Fr1l/9YQgG+iZvJUuOzAaVaMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL -BQADggEBAKq7KEnR0V43PsA5D23Lhawy5W/BDs4RO3LkYSxi+zR4EAMC8RhafrmG -6IZVp+ykplZtFK3Kkw1osakcvmHRLoPCXPWLibXtWMEpmH4GhWJKf5Ct1kY0VkEE -ALP7vCtjDm5l6WBaNOZYv25wwg5wgjyhzfJtLxzyRRPOjUuv0M3FFwJEAauzoo+4 -nle91IHNcWPIq1kgWUwWBHpLgZ2RpSOZS9MBOCkjHwQhoebhpgwSPgUHvBJ7FoLb -AeAdwpgPdIQ9gZEZEPfCPfG/Qp60yLAhkT2CF7F1h47VYe8LGBDbd1HGpSwjulq/ -lnvV4zDIoKhbQhUpxwgHo79nxcgddOA= ------END CERTIFICATE----- diff --git a/contrib/certificates/ssl/netdb.i2p2.no.crt b/contrib/certificates/ssl/netdb.i2p2.no.crt deleted file mode 100644 index 7c792d99..00000000 --- a/contrib/certificates/ssl/netdb.i2p2.no.crt +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID1TCCAr2gAwIBAgIJAOd9wIt+w/I5MA0GCSqGSIb3DQEBCwUAMIGAMQswCQYD -VQQGEwJOTzENMAsGA1UECAwET3NsbzENMAsGA1UEBwwET3NsbzEMMAoGA1UECgwD -STJQMQwwCgYDVQQLDANJMlAxFjAUBgNVBAMMDW5ldGRiLmkycDIubm8xHzAdBgkq -hkiG9w0BCQEWEG1lZWhAaTJwbWFpbC5vcmcwHhcNMTQxMjA2MjM1OTM1WhcNMjAw -NTI4MjM1OTM1WjCBgDELMAkGA1UEBhMCTk8xDTALBgNVBAgMBE9zbG8xDTALBgNV -BAcMBE9zbG8xDDAKBgNVBAoMA0kyUDEMMAoGA1UECwwDSTJQMRYwFAYDVQQDDA1u -ZXRkYi5pMnAyLm5vMR8wHQYJKoZIhvcNAQkBFhBtZWVoQGkycG1haWwub3JnMIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmtRtAALMImh0G0X+AtMpJNBa -HduNkg5t+0juitKRboXXAp5k7yN9qnimlBxlAicNb+QubcDuL+WV91NKz43dd6Xp -SAewqMFRPUAki8uYzoh+hQEfzyd3NmadUKquYZsYwomhHnraOmLZLbxD6ED3FEwl -hGBJwYnhyMZUCgB5+DEEHg8RdLz+H0bMrwz3e7/0lMtH6lM1lIHz0KBULWLp7Om0 -sk3rmmhPUIXqfoY8X3vClI74o0KcslMVaF4rt3lAHdoi3lwA6Qbdqq9nC9rPWHUS -USQQ/MKsNfDTGsHkbW2l0VgNvJkw92DwHTXSJrsEqgkdV/B1hHxCKgL44c/CbwID -AQABo1AwTjAdBgNVHQ4EFgQUCkebDZE05yKMbXORa6gO+aLdCscwHwYDVR0jBBgw -FoAUCkebDZE05yKMbXORa6gO+aLdCscwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B -AQsFAAOCAQEAfHO0g5M//X5xDIuXGCeqQMUrF3r1N45a+0kqo2b/rd9USueNGrJl -KE7MfDgShy2d4strZ1m0M4StW0RlUUZ4V4FYwzcknF6VXbOQK3BTrAeOwuxsrHoT -abrMZ36ABYur5WakOYtPyQ5oXFUAIpGBe9LH7q3XLegSOfftvc2xdJ+VK0n4MEfY -GfaRGMNW/pxGYLWvao3soOJMtp6cQ5KIYGuX92DMon/UgPBqEygeUj7aIqjhRss0 -b0dUZQyHccAG+e5NeTF2ifHCEh2rZY18VGxPL7KLrCQigu5lif1TTv5CDO5rKrHl -TuTOsnooMxUH4ThIVI9cxXk6bzRMehLghA== ------END CERTIFICATE----- diff --git a/contrib/certificates/ssl/reseed.i2p-projekt.de.crt b/contrib/certificates/ssl/reseed.i2p-projekt.de.crt deleted file mode 100644 index 75f43dd6..00000000 --- a/contrib/certificates/ssl/reseed.i2p-projekt.de.crt +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID7TCCAtWgAwIBAgIJAOHakoadaLRiMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD -VQQGEwJBVDEQMA4GA1UECAwHQXVzdHJpYTENMAsGA1UEBwwER3JhejEMMAoGA1UE -CgwDSTJQMQ8wDQYDVQQLDAZSZXNlZWQxHjAcBgNVBAMMFXJlc2VlZC5pMnAtcHJv -amVrdC5kZTEdMBsGCSqGSIb3DQEJARYOcmVzZWVkQGkycDIuZGUwHhcNMTQwNTEw -MTAxOTM3WhcNMjQwNTA3MTAxOTM3WjCBjDELMAkGA1UEBhMCQVQxEDAOBgNVBAgM -B0F1c3RyaWExDTALBgNVBAcMBEdyYXoxDDAKBgNVBAoMA0kyUDEPMA0GA1UECwwG -UmVzZWVkMR4wHAYDVQQDDBVyZXNlZWQuaTJwLXByb2pla3QuZGUxHTAbBgkqhkiG -9w0BCQEWDnJlc2VlZEBpMnAyLmRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEA8t5igIeAUZVX9k/A2gudRWVfToIV4yvlxmnH9UTJ8DTkWfHGbY9MmW2+ -b0ZdvIZDcgg1nvcLEKqCDQnIp3wLGdM8fdVSXqxA1dLyHdk6IrGVqb60qpGENeIc -EHiUeB1g0KqP4kLcj2sNlo+Vupjnu7qS8v0/LfZ3fq2m4vtx8dYnvo+JIzGL9K0f -/DOil8QIcdTZupzMbXd6P936Blm/1RdbW/uKROOuuYE38NwYOUCq2/Nd+T86S5DD -9wQBjy0U+9nNayWf6BOSuP6m2mxx/pA1CvKRq7CzI0Gqjo2Msd+i0dTL2WIO2JDp -5uykZ0GabRW3UrMEuyrzzK6U2RZ1dQIDAQABo1AwTjAdBgNVHQ4EFgQUIejD2MMl -6PpcCernYd3ku3sEWfswHwYDVR0jBBgwFoAUIejD2MMl6PpcCernYd3ku3sEWfsw -DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAupUg3ZTBSE7iRebjcZ+y -zgnRaClmgrv8Mpa1/weTuXKhJZ65k6+G5mplI5hN/crKi/3b6oyfRrYhgdTdb0rD -2CbrhBkPGGlubhkjkxWjAhibzU6Kt3a7WOjykGnslpCZhwS/hiVB7ZE2JGdphFld -aJTKt12CytyP3GyIQyyX7O2t92dk8cW4tlxRVpaPNr59lk0V50qpvNmNyhxv3yDz -taop/etfjHStq1YrltHWH0d4Dxy8ubb7nV19uvPcE0+MrR2xm7jvOBfGjAf1bQ7Z -rk7RMHio4xWFJZO7TSzL5/8EH2jX6ZqpH+hZ6sV8TmzuRWsPkm0doXWr+HBZ/gMt -5w== ------END CERTIFICATE----- diff --git a/contrib/certificates/ssl/reseed.i2p.vzaws.com.crt b/contrib/certificates/ssl/reseed.i2p.vzaws.com.crt deleted file mode 100644 index 25f9e369..00000000 --- a/contrib/certificates/ssl/reseed.i2p.vzaws.com.crt +++ /dev/null @@ -1,33 +0,0 @@ ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- diff --git a/contrib/certificates/ssl/uk.reseed.i2p2.no.crt b/contrib/certificates/ssl/uk.reseed.i2p2.no.crt deleted file mode 100644 index 84f3f073..00000000 --- a/contrib/certificates/ssl/uk.reseed.i2p2.no.crt +++ /dev/null @@ -1,33 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFsTCCA5mgAwIBAgIJANgzPow6thRuMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV -BAYTAk5PMQ0wCwYDVQQIDARPc2xvMQswCQYDVQQHDAJVSzETMBEGA1UECgwKSTJQ -IFJlc2VlZDETMBEGA1UECwwKSTJQIFJlc2VlZDEaMBgGA1UEAwwRdWsucmVzZWVk -LmkycDIubm8wHhcNMTQwNjI4MjA0OTA3WhcNMjQwNjI1MjA0OTA3WjBvMQswCQYD -VQQGEwJOTzENMAsGA1UECAwET3NsbzELMAkGA1UEBwwCVUsxEzARBgNVBAoMCkky -UCBSZXNlZWQxEzARBgNVBAsMCkkyUCBSZXNlZWQxGjAYBgNVBAMMEXVrLnJlc2Vl -ZC5pMnAyLm5vMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxlVlXWn5 -Ham6ZqM6FkH6ZoXeXbncY/PnF669mCPcrPH56V2xZXwpeCXHWfu7YiHuhXXZSmzP -zwRrawHZTJulHt4e6j27JnDuEj69gmFpyi4B1djQ0kav0aJeagwCPG2do/UD7Cbr -4nITkU4CifLe47IUW/2K/EBI6bZGsRIDHJ3A+fAQmLnvehEkpvLN+cvtkpJOtZYx -6WvbwLsirkISnaio4//UY8M4poIu9mSG5pvNLagn9uoRPUSuj8jDEysB1Nmh12Zu -gFnt2XcxQB9/0krB5GnDTodrgfsz/UPbk44l4kFmQoLv5ACFndH69RKftogisauj -VVUrqCL3l9TcNsx8GLqZkeWhCwdZycZFjBhK01zihTYPEiU2HXfCNWhzLqxrM2Hh -r1ci+56fyNdn/ssO4o3hrGaWPDiayiHlEGEJxaG/ueKX2c3c0UJKkIGBPTEcdBjW -q42n/7EhY/ISaieQXPRK+gVm18I1OlGUH5FEYELO20bL88J8pr/bYuJyJnC8fiMP -YzKZuiVhey6dPr0zZgNDHyRbOlZqQllzKd1wbzbE4xqdUZfBWYwtRpdOJKDw4eoi -M69TwPQFfudeiudnMcR1gN37OkxS7UTEdsYIB5urgLb6qQD+tYFsxpcVPkedJw62 -3TobhZjucaEZWzePd4u9faT9mQBXBAgY6VcCAwEAAaNQME4wHQYDVR0OBBYEFDTN -QRqhzaLc6XX2gFg26K//e0+8MB8GA1UdIwQYMBaAFDTNQRqhzaLc6XX2gFg26K// -e0+8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggIBACcJ99Z45ghglvL3 -/yMnx6IkOSneEm2/ADQoOabBQSC2grRAMBescKUiqpgbpBFalIPbPJUVrlH9tXYB -izNhqWETBY2tNy7AEHcJcCsAFuC2gOhaFH7FLgPA8V5IJmZ+McjB8REyowcN+CP4 -GDY8s5/yr9S3HpKLD80UV18UX/j5m4b6I1w61QceMOSt6ahTtlnyvNBonFW94L1c -RmkdbhxYWn2eeUas62Q/+9bjr24E0weDKqopa3bbO7MWJ3mKkS4rua42j8GG3Q3q -UWPGh4zm+2+Ncjmz0Ho73RyYDDcp9IjwlAEv+NW86rz/5Pdkhoy+SzQwFYAwNgaQ -FRKb6ltpslxmu3tUdZ7Ydrj6MBGQyH2gRVm9qByro7WGI4UsyzsjP009Iu6dbhdC -2ddTGMisXF3dOmdRWh8dlggmW6gV4iaVgZkzLtrc9S0SK66utKMVXa4EUTm6XogX -F5ImPnVzIMo2qF2pP31aGDzKqJF3GNjGj+xHRVau5whz0a4ESY6V14PLTEL4Vc/H -J9uLCySifvqN+jzs5iY2QvNXjg2zPaTJbnjxxpYQJVSQHX6SyRcszhChqQzxnbyo -+S19BRclqzufRq6pp6VcOiID0BB7qPcrUHM9h1ingMXcZZlGBgHew9cY7tb5TAox -o+aTNc4k/7E543FVbs40dpOD2Fcr ------END CERTIFICATE----- diff --git a/contrib/certificates/ssl/us.reseed.i2p2.no.crt b/contrib/certificates/ssl/us.reseed.i2p2.no.crt deleted file mode 100644 index eae724e9..00000000 --- a/contrib/certificates/ssl/us.reseed.i2p2.no.crt +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDUDCCAjgCCQCkTcCJMdZV7zANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJO -TzENMAsGA1UECAwET3NsbzENMAsGA1UEBwwET3NsbzENMAsGA1UECgwET3NsbzES -MBAGA1UECwwJTm9yZGNsb3VkMRowGAYDVQQDDBF1cy5yZXNlZWQuaTJwMi5ubzAe -Fw0xNDA2MjcyMjQxMjFaFw0yNDA2MjQyMjQxMjFaMGoxCzAJBgNVBAYTAk5PMQ0w -CwYDVQQIDARPc2xvMQ0wCwYDVQQHDARPc2xvMQ0wCwYDVQQKDARPc2xvMRIwEAYD -VQQLDAlOb3JkY2xvdWQxGjAYBgNVBAMMEXVzLnJlc2VlZC5pMnAyLm5vMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomVoBEc53jzy3xGMfgRaKyX6MaGG -KAmwu0uMTX6bVzGjy56JMMq3luoxOrpvgrNZF52lu7i36Tejo0HM75AHoea1es55 -DNLmrlDeqzlBU2WibOnizbB8G+tlMEbx8eAGAWk/Wv/vH8CAKmxjImslmbajzZC2 -LEH7inp3J5T2sVV7zmXeL9OEPKNyohbu6Mrno2IAlEOr8cu+lWAaFWzpknnR1gBX -NkB/8+7vK5Fq4MT7B0qnXPxmaWDbUOepPPni8u+2L9+qt19vZH4/6KNuH7xd7JLz -FfIdol6jy2cBQyAK7cVKWDHNk7ceB4Dl0mjBDbBIRTtLK+rfdnVmfWn8aQIDAQAB -MA0GCSqGSIb3DQEBBQUAA4IBAQCQH4QJMp5xneh2ah7fiuVdtKbiv6QNunRz7nb/ -mWYyqmBX7EHL8jOG5qmPELDgDt58HmnaYMo05nEJb9JhAoviEDXSYw0s6eN4n4nc -MKqgR/HLLSiXPwT+Wi1MI57OYim5AFTUCYTSaWFUT+dZKYb0QPE1XjGpQXi3ppsJ -3TJG71tOzJmZT6vRPmdTHJO70v6ZEhr5w4SiGx07gNmcgO8WRyb5ajOwSHiGKrj6 -UsuRNhtCyZaAEmelR9mfKBR1J2Nb+9jTz6mJtpT82WY3bst6mFk+A+mMWBQy7Hjt -gpdSDBCcFx9if+AKINGLgFvFKV2q8UzbfXms19NsVt9Hu7W3 ------END CERTIFICATE----- diff --git a/contrib/certificates/ssl/user.mx24.eu.crt b/contrib/certificates/ssl/user.mx24.eu.crt deleted file mode 100644 index 38c68ab5..00000000 --- a/contrib/certificates/ssl/user.mx24.eu.crt +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICwDCCAkagAwIBAgIJAKXCoCBjd/C0MAoGCCqGSM49BAMEMIGdMQswCQYDVQQG -EwJERTEVMBMGA1UECAwMdXNlci5teDI0LmV1MRUwEwYDVQQHDAx1c2VyLm14MjQu -ZXUxFTATBgNVBAoMDHVzZXIubXgyNC5ldTEVMBMGA1UECwwMdXNlci5teDI0LmV1 -MRUwEwYDVQQDDAx1c2VyLm14MjQuZXUxGzAZBgkqhkiG9w0BCQEWDHVzZXIubXgy -NC5ldTAeFw0xNTA5MDMxNjMyNDVaFw0yMTAyMjMxNjMyNDVaMIGdMQswCQYDVQQG -EwJERTEVMBMGA1UECAwMdXNlci5teDI0LmV1MRUwEwYDVQQHDAx1c2VyLm14MjQu -ZXUxFTATBgNVBAoMDHVzZXIubXgyNC5ldTEVMBMGA1UECwwMdXNlci5teDI0LmV1 -MRUwEwYDVQQDDAx1c2VyLm14MjQuZXUxGzAZBgkqhkiG9w0BCQEWDHVzZXIubXgy -NC5ldTB2MBAGByqGSM49AgEGBSuBBAAiA2IABPlKs5fYTqVhIOMiR6U9U4TimxS3 -P5NBDVzeeIAgbw5KBC8UImScZVt9g4V1wQe5kPs7TxA2BfanAPZ+ekQiRRvMVQxD -bSlRYupEWhq5BrJI6Lq/HDc7VJe9UUWffWKUoKNQME4wHQYDVR0OBBYEFBGJ0Yr+ -PZXnrk5RafQEALUpAU6ZMB8GA1UdIwQYMBaAFBGJ0Yr+PZXnrk5RafQEALUpAU6Z -MAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwQDaAAwZQIxAPcovePHMCosrAQNzS5i -VDUiyPNLOxHyRBm79yKXGl13LxysB6OK+2M7t8j8E/udBwIwXVVjxN6aSgXYTJ7d -p+Hg/2CuBMwf41/ENRcYQA+oGS9bU6A+7U9KJ1xTWWoqsUEs ------END CERTIFICATE----- From 4145251afdbc69716d7a52b747e4b36e5a1de319 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 21 Dec 2016 21:29:46 -0500 Subject: [PATCH 05/36] new reseed --- Config.cpp | 5 +-- .../reseed/hottuna_at_mail.i2p.crt | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 contrib/certificates/reseed/hottuna_at_mail.i2p.crt diff --git a/Config.cpp b/Config.cpp index 20036b83..610ab4aa 100644 --- a/Config.cpp +++ b/Config.cpp @@ -170,13 +170,14 @@ namespace config { "https://reseed.i2p-projekt.de/," "https://i2p.mooo.com/netDb/," "https://netdb.i2p2.no/," - "https://us.reseed.i2p2.no:444/," +// "https://us.reseed.i2p2.no:444/," // mamoth's shit // "https://uk.reseed.i2p2.no:444/," // mamoth's shit "https://i2p-0.manas.ca:8443/," "https://reseed.i2p.vzaws.com:8443/," "https://download.xxlspeed.com/," "https://reseed-ru.lngserv.ru/," - "https://reseed.atomike.ninja/" + "https://reseed.atomike.ninja/," + "https://reseed.memcpy.io/" ), "Reseed URLs, separated by comma") ; diff --git a/contrib/certificates/reseed/hottuna_at_mail.i2p.crt b/contrib/certificates/reseed/hottuna_at_mail.i2p.crt new file mode 100644 index 00000000..d0ff7c33 --- /dev/null +++ b/contrib/certificates/reseed/hottuna_at_mail.i2p.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- From 0ad30785247a4bddcb79c8d594b11c52f718c4d6 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 22 Dec 2016 10:08:35 -0500 Subject: [PATCH 06/36] open log stream in log thread --- Log.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Log.cpp b/Log.cpp index a38fc808..7cd4205e 100644 --- a/Log.cpp +++ b/Log.cpp @@ -72,7 +72,6 @@ namespace log { { if (!m_IsRunning) { - Reopen (); m_IsRunning = true; m_Thread = new std::thread (std::bind (&Log::Run, this)); } @@ -162,6 +161,7 @@ namespace log { void Log::Run () { + Reopen (); while (m_IsRunning) { std::shared_ptr msg; From 5babfb0f1eb123a37f2082b2dc6044dac6bf1f57 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 22 Dec 2016 10:52:26 -0500 Subject: [PATCH 07/36] fixed #724 --- DaemonLinux.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/DaemonLinux.cpp b/DaemonLinux.cpp index edd15f07..07e27e9d 100644 --- a/DaemonLinux.cpp +++ b/DaemonLinux.cpp @@ -76,12 +76,10 @@ namespace i2p return false; } -#if !defined(__OpenBSD__) // point std{in,out,err} descriptors to /dev/null - stdin = freopen("/dev/null", "r", stdin); - stdout = freopen("/dev/null", "w", stdout); - stderr = freopen("/dev/null", "w", stderr); -#endif + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); } // set proc limits From 442c63d7a4a447a120400fcb3428bfc2c1ec18cc Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 22 Dec 2016 13:32:06 -0500 Subject: [PATCH 08/36] #746. initialize io_service after daeminization --- Transports.cpp | 59 +++++++++++++++++++++++++++++++------------------- Transports.h | 8 +++---- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/Transports.cpp b/Transports.cpp index 1b9d52a1..b422297f 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -108,8 +108,8 @@ namespace transport Transports transports; Transports::Transports (): - m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), - m_PeerCleanupTimer (m_Service), m_PeerTestTimer (m_Service), + m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Service (nullptr), + m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr), m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_InBandwidth (0), m_OutBandwidth (0), m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0) @@ -119,10 +119,25 @@ namespace transport Transports::~Transports () { Stop (); + if (m_Service) + { + delete m_PeerCleanupTimer; m_PeerCleanupTimer = nullptr; + delete m_PeerTestTimer; m_PeerTestTimer = nullptr; + delete m_Work; m_Work = nullptr; + delete m_Service; m_Service = nullptr; + } } void Transports::Start (bool enableNTCP, bool enableSSU) { + if (!m_Service) + { + m_Service = new boost::asio::io_service (); + m_Work = new boost::asio::io_service::work (*m_Service); + m_PeerCleanupTimer = new boost::asio::deadline_timer (*m_Service); + m_PeerTestTimer = new boost::asio::deadline_timer (*m_Service); + } + m_DHKeysPairSupplier.Start (); m_IsRunning = true; m_Thread = new std::thread (std::bind (&Transports::Run, this)); @@ -167,16 +182,16 @@ namespace transport LogPrint (eLogError, "Transports: SSU server already exists"); } } - m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); - m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); - m_PeerTestTimer.expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL)); - m_PeerTestTimer.async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); + m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); + m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); + m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL)); + m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); } void Transports::Stop () { - m_PeerCleanupTimer.cancel (); - m_PeerTestTimer.cancel (); + if (m_PeerCleanupTimer) m_PeerCleanupTimer->cancel (); + if (m_PeerTestTimer) m_PeerTestTimer->cancel (); m_Peers.clear (); if (m_SSUServer) { @@ -193,7 +208,7 @@ namespace transport m_DHKeysPairSupplier.Stop (); m_IsRunning = false; - m_Service.stop (); + if (m_Service) m_Service->stop (); if (m_Thread) { m_Thread->join (); @@ -204,11 +219,11 @@ namespace transport void Transports::Run () { - while (m_IsRunning) + while (m_IsRunning && m_Service) { try { - m_Service.run (); + m_Service->run (); } catch (std::exception& ex) { @@ -251,7 +266,7 @@ namespace transport #ifdef WITH_EVENTS EmitEvent({{"type" , "transport.sendmsg"}, {"ident", ident.ToBase64()}, {"number", std::to_string(msgs.size())}}); #endif - m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs)); + m_Service->post (std::bind (&Transports::PostMessages, this, ident, msgs)); } void Transports::PostMessages (i2p::data::IdentHash ident, std::vector > msgs) @@ -386,7 +401,7 @@ namespace transport void Transports::RequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident) { - m_Service.post (std::bind (&Transports::HandleRequestComplete, this, r, ident)); + m_Service->post (std::bind (&Transports::HandleRequestComplete, this, r, ident)); } void Transports::HandleRequestComplete (std::shared_ptr r, i2p::data::IdentHash ident) @@ -411,7 +426,7 @@ namespace transport void Transports::NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident) { - auto resolver = std::make_shared(m_Service); + auto resolver = std::make_shared(*m_Service); resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""), std::bind (&Transports::HandleNTCPResolve, this, std::placeholders::_1, std::placeholders::_2, ident, resolver)); @@ -454,7 +469,7 @@ namespace transport void Transports::SSUResolve (const std::string& addr, const i2p::data::IdentHash& ident) { - auto resolver = std::make_shared(m_Service); + auto resolver = std::make_shared(*m_Service); resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""), std::bind (&Transports::HandleSSUResolve, this, std::placeholders::_1, std::placeholders::_2, ident, resolver)); @@ -497,7 +512,7 @@ namespace transport void Transports::CloseSession (std::shared_ptr router) { if (!router) return; - m_Service.post (std::bind (&Transports::PostCloseSession, this, router)); + m_Service->post (std::bind (&Transports::PostCloseSession, this, router)); } void Transports::PostCloseSession (std::shared_ptr router) @@ -584,7 +599,7 @@ namespace transport void Transports::PeerConnected (std::shared_ptr session) { - m_Service.post([session, this]() + m_Service->post([session, this]() { auto remoteIdentity = session->GetRemoteIdentity (); if (!remoteIdentity) return; @@ -632,7 +647,7 @@ namespace transport void Transports::PeerDisconnected (std::shared_ptr session) { - m_Service.post([session, this]() + m_Service->post([session, this]() { auto remoteIdentity = session->GetRemoteIdentity (); if (!remoteIdentity) return; @@ -690,8 +705,8 @@ namespace transport UpdateBandwidth (); // TODO: use separate timer(s) for it if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test DetectExternalIP (); - m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); - m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); + m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); + m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); } } @@ -700,8 +715,8 @@ namespace transport if (ecode != boost::asio::error::operation_aborted) { PeerTest (); - m_PeerTestTimer.expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL)); - m_PeerTestTimer.async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); + m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL)); + m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); } } diff --git a/Transports.h b/Transports.h index d83c0370..a68838e2 100644 --- a/Transports.h +++ b/Transports.h @@ -84,7 +84,7 @@ namespace transport bool IsOnline() const { return m_IsOnline; }; void SetOnline (bool online) { m_IsOnline = online; }; - boost::asio::io_service& GetService () { return m_Service; }; + boost::asio::io_service& GetService () { return *m_Service; }; std::shared_ptr GetNextDHKeysPair (); void ReuseDHKeysPair (std::shared_ptr pair); @@ -144,9 +144,9 @@ namespace transport bool m_IsOnline, m_IsRunning; std::thread * m_Thread; - boost::asio::io_service m_Service; - boost::asio::io_service::work m_Work; - boost::asio::deadline_timer m_PeerCleanupTimer, m_PeerTestTimer; + boost::asio::io_service * m_Service; + boost::asio::io_service::work * m_Work; + boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer; NTCPServer * m_NTCPServer; SSUServer * m_SSUServer; From f2c401b6c008d190e38386d0d58752043dae23eb Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 22 Dec 2016 15:00:40 -0500 Subject: [PATCH 09/36] fixed some memory leak --- util.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/util.cpp b/util.cpp index ca2b61c2..5cc1fdb5 100644 --- a/util.cpp +++ b/util.cpp @@ -275,14 +275,15 @@ namespace net if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af) { // match - char * addr = new char[INET6_ADDRSTRLEN]; - bzero(addr, INET6_ADDRSTRLEN); - if(af == AF_INET) - inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN); - else - inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN); - freeifaddrs(addrs); - std::string cur_ifaddr(addr); + char * addr = new char[INET6_ADDRSTRLEN]; + bzero(addr, INET6_ADDRSTRLEN); + if(af == AF_INET) + inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN); + else + inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN); + freeifaddrs(addrs); + std::string cur_ifaddr(addr); + delete[] addr; return boost::asio::ip::address::from_string(cur_ifaddr); } cur = cur->ifa_next; From 27e1579e4caa1dfdd7bc090f89911ae04de6d41d Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 22 Dec 2016 19:38:17 -0500 Subject: [PATCH 10/36] rollback --- SAM.cpp | 107 ++++++++++++++------------------------------------------ SAM.h | 19 +++------- 2 files changed, 31 insertions(+), 95 deletions(-) diff --git a/SAM.cpp b/SAM.cpp index c61e8577..cb843685 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -54,7 +54,11 @@ namespace client case eSAMSocketTypeAcceptor: { if (m_Session) + { m_Session->DelSocket (shared_from_this ()); + if (m_Session->localDestination) + m_Session->localDestination->StopAcceptingStreams (); + } break; } default: @@ -285,11 +289,6 @@ namespace client dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); } - else - { - // start accepting streams because we're not a datagram session - m_Session->localDestination->AcceptStreams (std::bind (&SAMSession::AcceptI2P, m_Session, std::placeholders::_1)); - } if (m_Session->localDestination->IsReady ()) SendSessionCreateReplyOk (); @@ -402,25 +401,20 @@ namespace client m_Session = m_Owner.FindSession (id); if (m_Session) { - m_SocketType = eSAMSocketTypeAcceptor; - m_Session->AddSocket (shared_from_this ()); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + if (!m_Session->localDestination->IsAcceptingStreams ()) + { + m_SocketType = eSAMSocketTypeAcceptor; + m_Session->AddSocket (shared_from_this ()); + m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + } + else + SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true); } else SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); } - void SAMSocket::Accept(std::shared_ptr stream) - { - if(stream) { - m_SocketType = eSAMSocketTypeStream; - HandleI2PAccept(stream); - } else { - SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true); - auto s = shared_from_this (); - m_Owner.GetService ().post ([s] { s->Terminate (); }); - } - } size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data) { LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len); @@ -665,6 +659,10 @@ namespace client LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID); m_Stream = stream; context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ()); + auto session = m_Owner.FindSession (m_ID); + if (session) + session->localDestination->StopAcceptingStreams (); + m_SocketType = eSAMSocketTypeStream; if (!m_IsSilent) { // get remote peer address @@ -706,76 +704,26 @@ namespace client } SAMSession::SAMSession (std::shared_ptr dest): - localDestination (dest), - m_BacklogPumper(dest->GetService()) + localDestination (dest) { - PumpBacklog(); } - - void SAMSession::AcceptI2P(std::shared_ptr stream) + + SAMSession::~SAMSession () { - if(!stream) return; // fail - std::unique_lock lock(m_SocketsMutex); - if(m_Backlog.size() > SAM_MAX_ACCEPT_BACKLOG) { - stream->Close(); - return; - } - m_Backlog.push_back(stream); - } - - void SAMSession::PumpBacklog() - { - // pump backlog every 100ms - boost::posix_time::milliseconds dlt(100); - m_BacklogPumper.expires_from_now(dlt); - m_BacklogPumper.async_wait(std::bind(&SAMSession::HandlePumpBacklog, this, std::placeholders::_1)); - } - - std::shared_ptr SAMSession::FindAcceptor() - { - for (auto & sock : m_Sockets) { - auto t = sock->GetSocketType(); - if(t == eSAMSocketTypeAcceptor) { - return sock; - } - } - return nullptr; - } - - void SAMSession::HandlePumpBacklog(const boost::system::error_code & ec) - { - if(ec) return; - { - std::unique_lock lock(m_SocketsMutex); - auto itr = m_Backlog.begin(); - while(itr != m_Backlog.end()) { - auto sock = FindAcceptor(); - if (sock) { - sock->Accept(*itr); - itr = m_Backlog.erase(itr); - } else { - ++itr; - } - } - } - PumpBacklog(); + CloseStreams(); + i2p::client::context.DeleteLocalDestination (localDestination); } void SAMSession::CloseStreams () { - m_BacklogPumper.cancel(); - localDestination->GetService().post([&] () { + { std::lock_guard lock(m_SocketsMutex); for (auto& sock : m_Sockets) { sock->CloseStream(); } - for(auto & stream : m_Backlog) { - stream->Close(); - } - m_Sockets.clear(); - m_Backlog.clear(); - i2p::client::context.DeleteLocalDestination (localDestination); - }); + } + // XXX: should this be done inside locked parts? + m_Sockets.clear(); } SAMBridge::SAMBridge (const std::string& address, int port): @@ -886,9 +834,8 @@ namespace client auto session = std::make_shared(localDestination); std::unique_lock l(m_SessionsMutex); auto ret = m_Sessions.insert (std::make_pair(id, session)); - if (!ret.second) { + if (!ret.second) LogPrint (eLogWarning, "SAM: Session ", id, " already exists"); - } return ret.first->second; } return nullptr; diff --git a/SAM.h b/SAM.h index 7d7d6d3a..db08c5a0 100644 --- a/SAM.h +++ b/SAM.h @@ -20,8 +20,7 @@ namespace client { const size_t SAM_SOCKET_BUFFER_SIZE = 8192; const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds - const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds - const int SAM_MAX_ACCEPT_BACKLOG = 50; + const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds const char SAM_HANDSHAKE[] = "HELLO VERSION"; const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n"; @@ -85,8 +84,6 @@ namespace client void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; }; SAMSocketType GetSocketType () const { return m_SocketType; }; - void Accept(std::shared_ptr stream); - private: void Terminate (); @@ -137,8 +134,6 @@ namespace client struct SAMSession { std::shared_ptr localDestination; - boost::asio::deadline_timer m_BacklogPumper; - std::list > m_Backlog; std::list > m_Sockets; std::mutex m_SocketsMutex; @@ -163,15 +158,9 @@ namespace client } return l; } - - SAMSession (std::shared_ptr dest); - - void AcceptI2P(std::shared_ptr stream); - - std::shared_ptr FindAcceptor(); - - void PumpBacklog(); - void HandlePumpBacklog(const boost::system::error_code & ec); + + SAMSession (std::shared_ptr dest); + ~SAMSession (); void CloseStreams (); }; From 213629ef52c87f637c1bb5e57b81ec858078255b Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 22 Dec 2016 20:30:50 -0500 Subject: [PATCH 11/36] drop highest bit for token --- HTTPServer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 84818e8e..af5dfe73 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -775,6 +775,7 @@ namespace http { { uint32_t token; RAND_bytes ((uint8_t *)&token, 4); + token |= 0x7FFFFFFF; // clear first bit auto ts = i2p::util::GetSecondsSinceEpoch (); for (auto it = m_Tokens.begin (); it != m_Tokens.end (); ) { From 573ee0b5849262bea7a3fb0f0ffc2006dc344fd4 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 22 Dec 2016 20:34:06 -0500 Subject: [PATCH 12/36] fixed typo --- HTTPServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HTTPServer.cpp b/HTTPServer.cpp index af5dfe73..48ec9bd2 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -775,7 +775,7 @@ namespace http { { uint32_t token; RAND_bytes ((uint8_t *)&token, 4); - token |= 0x7FFFFFFF; // clear first bit + token &= 0x7FFFFFFF; // clear first bit auto ts = i2p::util::GetSecondsSinceEpoch (); for (auto it = m_Tokens.begin (); it != m_Tokens.end (); ) { From 88a48a5c793b9336363872b5c1073cf6d2595f38 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 23 Dec 2016 10:09:40 -0500 Subject: [PATCH 13/36] implement AcceptOnce for multiple acceptors --- Streaming.cpp | 23 +++++++++++++++++++++++ Streaming.h | 2 ++ 2 files changed, 25 insertions(+) diff --git a/Streaming.cpp b/Streaming.cpp index 762dd9d6..ff14aadb 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -1037,6 +1037,29 @@ namespace stream m_Acceptor = nullptr; } + void StreamingDestination::AcceptOnce (const Acceptor& acceptor) + { + m_Owner->GetService ().post([acceptor, this](void) + { + if (!m_PendingIncomingStreams.empty ()) + { + acceptor (m_PendingIncomingStreams.front ()); + m_PendingIncomingStreams.pop_front (); + if (m_PendingIncomingStreams.empty ()) + m_PendingIncomingTimer.cancel (); + } + else // we must save old acceptor and set it back + { + auto oldAcceptor = m_Acceptor; + m_Acceptor = [acceptor, oldAcceptor, this](std::shared_ptr stream) + { + acceptor (stream); + m_Acceptor = oldAcceptor; + }; + } + }); + } + void StreamingDestination::HandlePendingIncomingTimer (const boost::system::error_code& ecode) { if (ecode != boost::asio::error::operation_aborted) diff --git a/Streaming.h b/Streaming.h index 65a8d8b4..1593f961 100644 --- a/Streaming.h +++ b/Streaming.h @@ -223,6 +223,8 @@ namespace stream void SetAcceptor (const Acceptor& acceptor); void ResetAcceptor (); bool IsAcceptorSet () const { return m_Acceptor != nullptr; }; + void AcceptOnce (const Acceptor& acceptor); + std::shared_ptr GetOwner () const { return m_Owner; }; void SetOwner (std::shared_ptr owner) { m_Owner = owner; }; uint16_t GetLocalPort () const { return m_LocalPort; }; From b363b50320cbea66420da184e7b62b046a4e7570 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 24 Dec 2016 08:53:35 -0500 Subject: [PATCH 14/36] multiple acceptors --- Destination.cpp | 6 ++++++ Destination.h | 3 ++- SAM.cpp | 17 +++++++++++++---- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Destination.cpp b/Destination.cpp index 491a9f29..f3cb9af8 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -844,6 +844,12 @@ namespace client return false; } + void ClientDestination::AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor) + { + if (m_StreamingDestination) + m_StreamingDestination->AcceptOnce (acceptor); + } + std::shared_ptr ClientDestination::CreateStreamingDestination (int port, bool gzip) { auto dest = std::make_shared (GetSharedFromThis (), port, gzip); diff --git a/Destination.h b/Destination.h index 1ccb0a9c..7a4e0b64 100644 --- a/Destination.h +++ b/Destination.h @@ -186,7 +186,8 @@ namespace client void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor); void StopAcceptingStreams (); bool IsAcceptingStreams () const; - + void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor); + // datagram i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; i2p::datagram::DatagramDestination * CreateDatagramDestination (); diff --git a/SAM.cpp b/SAM.cpp index cb843685..aa7de1c8 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -405,7 +405,8 @@ namespace client { m_SocketType = eSAMSocketTypeAcceptor; m_Session->AddSocket (shared_from_this ()); - m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); + if (!m_Session->localDestination->IsAcceptingStreams ()) + m_Session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); } else @@ -657,12 +658,20 @@ namespace client if (stream) { LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID); + m_SocketType = eSAMSocketTypeStream; m_Stream = stream; context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ()); auto session = m_Owner.FindSession (m_ID); - if (session) - session->localDestination->StopAcceptingStreams (); - m_SocketType = eSAMSocketTypeStream; + if (session) + { + // find more pending acceptors + for (auto it: session->ListSockets ()) + if (it->m_SocketType == eSAMSocketTypeAcceptor) + { + session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); + break; + } + } if (!m_IsSilent) { // get remote peer address From 8f51dc2c228b0ce534be15a3bbfb4ab32dc417af Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 24 Dec 2016 09:55:59 -0500 Subject: [PATCH 15/36] reload acceptor with correct stream --- SAM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SAM.cpp b/SAM.cpp index aa7de1c8..20f565cc 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -668,7 +668,7 @@ namespace client for (auto it: session->ListSockets ()) if (it->m_SocketType == eSAMSocketTypeAcceptor) { - session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); + session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, it, std::placeholders::_1)); break; } } From 652226dbf020685b024e0b1b2a1aa0afd0e34b45 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 24 Dec 2016 16:34:18 -0500 Subject: [PATCH 16/36] allow multiple acceptors --- SAM.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/SAM.cpp b/SAM.cpp index 20f565cc..cb704ebd 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -400,17 +400,12 @@ namespace client m_ID = id; m_Session = m_Owner.FindSession (id); if (m_Session) - { + { + m_SocketType = eSAMSocketTypeAcceptor; + m_Session->AddSocket (shared_from_this ()); if (!m_Session->localDestination->IsAcceptingStreams ()) - { - m_SocketType = eSAMSocketTypeAcceptor; - m_Session->AddSocket (shared_from_this ()); - if (!m_Session->localDestination->IsAcceptingStreams ()) - m_Session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } - else - SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true); + m_Session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); } else SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); From 5bc2001ce3fd2dc679ed5a82c65419d4f5f4c4a2 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sat, 24 Dec 2016 16:05:44 -0500 Subject: [PATCH 17/36] Fix Tunnel Gateway Leak --- TunnelGateway.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/TunnelGateway.cpp b/TunnelGateway.cpp index ad423fc0..9f13d84c 100644 --- a/TunnelGateway.cpp +++ b/TunnelGateway.cpp @@ -20,6 +20,7 @@ namespace tunnel TunnelGatewayBuffer::~TunnelGatewayBuffer () { + ClearTunnelDataMsgs (); } void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block) @@ -48,7 +49,7 @@ namespace tunnel di[0] = block.deliveryType << 5; // set delivery type // create fragments - std::shared_ptr msg = block.data; + const std::shared_ptr & msg = block.data; size_t fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length if (fullMsgLen <= m_RemainingSize) { @@ -115,9 +116,13 @@ namespace tunnel m_CurrentTunnelDataMsg->len += s+7; if (isLastFragment) { - m_RemainingSize -= s+7; - if (!m_RemainingSize) - CompleteCurrentTunnelDataMessage (); + if(m_RemainingSize < (s+7)) { + LogPrint (eLogError, "TunnelGateway: remaining size overflow: ", m_RemainingSize, " < ", s+7); + } else { + m_RemainingSize -= s+7; + if (m_RemainingSize == 0) + CompleteCurrentTunnelDataMessage (); + } } else CompleteCurrentTunnelDataMessage (); @@ -138,10 +143,12 @@ namespace tunnel void TunnelGatewayBuffer::ClearTunnelDataMsgs () { m_TunnelDataMsgs.clear (); + m_CurrentTunnelDataMsg = nullptr; } void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage () { + m_CurrentTunnelDataMsg = nullptr; m_CurrentTunnelDataMsg = NewI2NPShortMessage (); m_CurrentTunnelDataMsg->Align (12); // we reserve space for padding @@ -196,7 +203,7 @@ namespace tunnel void TunnelGateway::SendBuffer () { m_Buffer.CompleteCurrentTunnelDataMessage (); - auto tunnelMsgs = m_Buffer.GetTunnelDataMsgs (); + const auto & tunnelMsgs = m_Buffer.GetTunnelDataMsgs (); for (auto& tunnelMsg : tunnelMsgs) { m_Tunnel->EncryptTunnelMsg (tunnelMsg, tunnelMsg); From 59dd479a6d3460fea9042e244c4348f4399a93e4 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 26 Dec 2016 17:19:54 -0500 Subject: [PATCH 18/36] check if address not found --- AddressBook.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/AddressBook.cpp b/AddressBook.cpp index 685acbc2..4dad5614 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -636,7 +636,11 @@ namespace client if (address.length () > 0) { // TODO: verify from - m_Addresses[address] = buf + 8; + i2p::data::IdentHash hash(buf + 8); + if (!hash.IsZero ()) + m_Addresses[address] = hash; + else + LogPrint (eLogInfo, "AddressBook: Lookup response: ", address, " not found"); } } From f3d4077142bc0b57c22be3d2b66ba64bd5651165 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 26 Dec 2016 18:47:47 -0500 Subject: [PATCH 19/36] dont re-request LS --- Datagram.cpp | 9 +++++++-- Datagram.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Datagram.cpp b/Datagram.cpp index 852e54f6..db29e83c 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -168,7 +168,8 @@ namespace datagram const i2p::data::IdentHash & remoteIdent) : m_LocalDestination(localDestination), m_RemoteIdent(remoteIdent), - m_SendQueueTimer(localDestination->GetService()) + m_SendQueueTimer(localDestination->GetService()), + m_RequestingLS(false) { m_LastUse = i2p::util::GetMillisecondsSinceEpoch (); ScheduleFlushSendQueue(); @@ -221,7 +222,10 @@ namespace datagram } if(!m_RemoteLeaseSet) { // no remote lease set - m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1)); + if(!m_RequestingLS) { + m_RequestingLS = true; + m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1)); + } return nullptr; } m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); @@ -290,6 +294,7 @@ namespace datagram void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr ls) { + m_RequestingLS = false; if(!ls) return; // only update lease set if found and newer than previous lease set uint64_t oldExpire = 0; diff --git a/Datagram.h b/Datagram.h index 8891f0cc..2eb180d6 100644 --- a/Datagram.h +++ b/Datagram.h @@ -88,7 +88,7 @@ namespace datagram boost::asio::deadline_timer m_SendQueueTimer; std::vector > m_SendQueue; uint64_t m_LastUse; - + bool m_RequestingLS; }; const size_t MAX_DATAGRAM_SIZE = 32768; From b1b5904852b3141d2a4314244efb733e386bc64e Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 27 Dec 2016 22:45:51 -0500 Subject: [PATCH 20/36] show SOCKS proxy as client tunnel --- ClientContext.h | 1 + HTTPServer.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/ClientContext.h b/ClientContext.h index db74a19e..e392c8a2 100644 --- a/ClientContext.h +++ b/ClientContext.h @@ -113,6 +113,7 @@ namespace client const decltype(m_ClientForwards)& GetClientForwards () const { return m_ClientForwards; } const decltype(m_ServerForwards)& GetServerForwards () const { return m_ServerForwards; } const i2p::proxy::HTTPProxy * GetHttpProxy () const { return m_HttpProxy; } + const i2p::proxy::SOCKSProxy * GetSocksProxy () const { return m_SocksProxy; } }; extern ClientContext context; diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 48ec9bd2..1ba22303 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -604,6 +604,15 @@ namespace http { s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << "
\r\n"<< std::endl; } + auto socksProxy = i2p::client::context.GetSocksProxy (); + if (socksProxy) + { + auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash(); + s << ""; + s << "SOCKS Proxy" << " ⇐ "; + s << i2p::client::context.GetAddressBook ().ToAddress(ident); + s << "
\r\n"<< std::endl; + } s << "
\r\nServer Tunnels:
\r\n
\r\n"; for (auto& it: i2p::client::context.GetServerTunnels ()) { From 2e8fa88fcb706c87c2334eba06e81663f664a9d2 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 29 Dec 2016 22:06:33 -0500 Subject: [PATCH 21/36] fixed memory leak --- SSU.cpp | 5 +++++ SSU.h | 1 + SSUSession.cpp | 13 +++++++------ SSUSession.h | 3 ++- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/SSU.cpp b/SSU.cpp index 198fed4b..7950dc45 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -159,6 +159,11 @@ namespace transport m_Relays[tag] = relay; } + void SSUServer::RemoveRelay (uint32_t tag) + { + m_Relays.erase (tag); + } + std::shared_ptr SSUServer::FindRelaySession (uint32_t tag) { auto it = m_Relays.find (tag); diff --git a/SSU.h b/SSU.h index 9d8e2878..8753ff77 100644 --- a/SSU.h +++ b/SSU.h @@ -58,6 +58,7 @@ namespace transport const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; }; void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay); + void RemoveRelay (uint32_t tag); std::shared_ptr FindRelaySession (uint32_t tag); void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr session = nullptr); diff --git a/SSUSession.cpp b/SSUSession.cpp index 0e277432..e67ff1de 100644 --- a/SSUSession.cpp +++ b/SSUSession.cpp @@ -16,7 +16,7 @@ namespace transport TransportSession (router, SSU_TERMINATION_TIMEOUT), m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_ConnectTimer (GetService ()), m_IsPeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false), - m_RelayTag (0),m_Data (*this), m_IsDataReceived (false) + m_RelayTag (0), m_SentRelayTag (0), m_Data (*this), m_IsDataReceived (false) { if (router) { @@ -458,14 +458,13 @@ namespace transport else s.Insert (address->host.to_v6 ().to_bytes ().data (), 16); // our IP V6 s.Insert (htobe16 (address->port)); // our port - uint32_t relayTag = 0; if (sendRelayTag && i2p::context.GetRouterInfo ().IsIntroducer () && !IsV6 ()) { - RAND_bytes((uint8_t *)&relayTag, 4); - if (!relayTag) relayTag = 1; - m_Server.AddRelay (relayTag, m_RemoteEndpoint); + RAND_bytes((uint8_t *)&m_SentRelayTag, 4); + if (!m_SentRelayTag) m_SentRelayTag = 1; + m_Server.AddRelay (m_SentRelayTag, m_RemoteEndpoint); } - htobe32buf (payload, relayTag); + htobe32buf (payload, m_SentRelayTag); payload += 4; // relay tag htobe32buf (payload, i2p::util::GetSecondsSinceEpoch ()); // signed on time payload += 4; @@ -870,6 +869,8 @@ namespace transport transports.PeerDisconnected (shared_from_this ()); m_Data.Stop (); m_ConnectTimer.cancel (); + if (m_SentRelayTag) + m_Server.RemoveRelay (m_SentRelayTag); // relay tag is not valid anymore } void SSUSession::Done () diff --git a/SSUSession.h b/SSUSession.h index 4838be2a..b25beeae 100644 --- a/SSUSession.h +++ b/SSUSession.h @@ -141,7 +141,8 @@ namespace transport bool m_IsPeerTest; SessionState m_State; bool m_IsSessionKey; - uint32_t m_RelayTag; + uint32_t m_RelayTag; // received from peer + uint32_t m_SentRelayTag; // sent by us i2p::crypto::CBCEncryption m_SessionKeyEncryption; i2p::crypto::CBCDecryption m_SessionKeyDecryption; i2p::crypto::AESKey m_SessionKey; From 178dedf78cb74f1385d409596959a21ed261b21c Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 30 Dec 2016 17:53:54 -0500 Subject: [PATCH 22/36] store relay session directly --- SSU.cpp | 9 +++++++-- SSU.h | 4 ++-- SSUSession.cpp | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/SSU.cpp b/SSU.cpp index 7950dc45..9f74185f 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -154,7 +154,7 @@ namespace transport } } - void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay) + void SSUServer::AddRelay (uint32_t tag, std::shared_ptr relay) { m_Relays[tag] = relay; } @@ -168,7 +168,12 @@ namespace transport { auto it = m_Relays.find (tag); if (it != m_Relays.end ()) - return FindSession (it->second); + { + if (it->second->GetState () == eSessionStateEstablished) + return it->second; + else + m_Relays.erase (it); + } return nullptr; } diff --git a/SSU.h b/SSU.h index 8753ff77..adbed739 100644 --- a/SSU.h +++ b/SSU.h @@ -57,7 +57,7 @@ namespace transport boost::asio::io_service& GetServiceV6 () { return m_ServiceV6; }; const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; }; void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); - void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay); + void AddRelay (uint32_t tag, std::shared_ptr relay); void RemoveRelay (uint32_t tag); std::shared_ptr FindRelaySession (uint32_t tag); @@ -120,7 +120,7 @@ namespace transport m_TerminationTimer, m_TerminationTimerV6; std::list m_Introducers; // introducers we are connected to std::map > m_Sessions, m_SessionsV6; - std::map m_Relays; // we are introducer + std::map > m_Relays; // we are introducer std::map m_PeerTests; // nonce -> creation time in milliseconds public: diff --git a/SSUSession.cpp b/SSUSession.cpp index e67ff1de..a74c6646 100644 --- a/SSUSession.cpp +++ b/SSUSession.cpp @@ -462,7 +462,7 @@ namespace transport { RAND_bytes((uint8_t *)&m_SentRelayTag, 4); if (!m_SentRelayTag) m_SentRelayTag = 1; - m_Server.AddRelay (m_SentRelayTag, m_RemoteEndpoint); + m_Server.AddRelay (m_SentRelayTag, shared_from_this ()); } htobe32buf (payload, m_SentRelayTag); payload += 4; // relay tag From c115131ed2de9a3db5dc7fdc8b2af8f95e65cce0 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 30 Dec 2016 20:09:41 -0500 Subject: [PATCH 23/36] removed IdentHash from RoutingProfile --- Profiling.cpp | 55 +++++++++++++++++++++++++++++++-------------------- Profiling.h | 7 +++---- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/Profiling.cpp b/Profiling.cpp index 890a2620..34e0681f 100644 --- a/Profiling.cpp +++ b/Profiling.cpp @@ -12,8 +12,8 @@ namespace data { i2p::fs::HashedStorage m_ProfilesStorage("peerProfiles", "p", "profile-", "txt"); - RouterProfile::RouterProfile (const IdentHash& identHash): - m_IdentHash (identHash), m_LastUpdateTime (boost::posix_time::second_clock::local_time()), + RouterProfile::RouterProfile (): + m_LastUpdateTime (boost::posix_time::second_clock::local_time()), m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0), m_NumTimesTaken (0), m_NumTimesRejected (0) { @@ -29,7 +29,7 @@ namespace data m_LastUpdateTime = GetTime (); } - void RouterProfile::Save () + void RouterProfile::Save (const IdentHash& identHash) { // fill sections boost::property_tree::ptree participation; @@ -46,7 +46,7 @@ namespace data pt.put_child (PEER_PROFILE_SECTION_USAGE, usage); // save to file - std::string ident = m_IdentHash.ToBase64 (); + std::string ident = identHash.ToBase64 (); std::string path = m_ProfilesStorage.Path(ident); try { @@ -57,51 +57,64 @@ namespace data } } - void RouterProfile::Load () + void RouterProfile::Load (const IdentHash& identHash) { - std::string ident = m_IdentHash.ToBase64 (); + std::string ident = identHash.ToBase64 (); std::string path = m_ProfilesStorage.Path(ident); boost::property_tree::ptree pt; - if (!i2p::fs::Exists(path)) { + if (!i2p::fs::Exists(path)) + { LogPrint(eLogWarning, "Profiling: no profile yet for ", ident); return; } - try { + try + { boost::property_tree::read_ini (path, pt); - } catch (std::exception& ex) { + } catch (std::exception& ex) + { /* boost exception verbose enough */ LogPrint (eLogError, "Profiling: ", ex.what ()); return; } - try { + try + { auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, ""); if (t.length () > 0) m_LastUpdateTime = boost::posix_time::time_from_string (t); - if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT) { - try { + if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT) + { + try + { // read participations auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION); m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0); m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0); m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0); - } catch (boost::property_tree::ptree_bad_path& ex) { + } + catch (boost::property_tree::ptree_bad_path& ex) + { LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION, " in profile for ", ident); } - try { + try + { // read usage auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE); m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0); m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0); - } catch (boost::property_tree::ptree_bad_path& ex) { + } + catch (boost::property_tree::ptree_bad_path& ex) + { LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE, " in profile for ", ident); } - } else { - *this = RouterProfile (m_IdentHash); - } - } catch (std::exception& ex) { + } + else + *this = RouterProfile (); + } + catch (std::exception& ex) + { LogPrint (eLogError, "Profiling: Can't read profile ", ident, " :", ex.what ()); } } @@ -149,8 +162,8 @@ namespace data std::shared_ptr GetRouterProfile (const IdentHash& identHash) { - auto profile = std::make_shared (identHash); - profile->Load (); // if possible + auto profile = std::make_shared (); + profile->Load (identHash); // if possible return profile; } diff --git a/Profiling.h b/Profiling.h index 26d5c2f7..34ed4f72 100644 --- a/Profiling.h +++ b/Profiling.h @@ -26,11 +26,11 @@ namespace data { public: - RouterProfile (const IdentHash& identHash); + RouterProfile (); RouterProfile& operator= (const RouterProfile& ) = default; - void Save (); - void Load (); + void Save (const IdentHash& identHash); + void Load (const IdentHash& identHash); bool IsBad (); @@ -48,7 +48,6 @@ namespace data private: - IdentHash m_IdentHash; boost::posix_time::ptime m_LastUpdateTime; // participation uint32_t m_NumTunnelsAgreed; From d77c782f69cc30078254f635c7aca70968375fb0 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 30 Dec 2016 20:59:18 -0500 Subject: [PATCH 24/36] removed IdentHash from RoutingProfile --- RouterInfo.h | 2 +- Transports.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RouterInfo.h b/RouterInfo.h index e8a4ea75..c51d3ce4 100644 --- a/RouterInfo.h +++ b/RouterInfo.h @@ -168,7 +168,7 @@ namespace data bool SaveToFile (const std::string& fullPath); std::shared_ptr GetProfile () const; - void SaveProfile () { if (m_Profile) m_Profile->Save (); }; + void SaveProfile () { if (m_Profile) m_Profile->Save (GetIdentHash ()); }; void Update (const uint8_t * buf, int len); void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; }; diff --git a/Transports.cpp b/Transports.cpp index b422297f..23e90e29 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -694,7 +694,7 @@ namespace transport if (profile) { profile->TunnelNonReplied(); - profile->Save(); + profile->Save(it->first); } std::unique_lock l(m_PeersMutex); it = m_Peers.erase (it); From f4de68cb2234b0afb56618b2a470c8eb60f9483c Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 31 Dec 2016 10:50:48 -0500 Subject: [PATCH 25/36] avoid exception if not connected --- I2PTunnel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index ff3e06a3..e94b5ed4 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -92,7 +92,8 @@ namespace client m_Stream->Close (); m_Stream.reset (); } - m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send); // avoid RST + boost::system::error_code ec; + m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); // avoid RST m_Socket->close (); Done(shared_from_this ()); From 6b6df15dd9c246485fc90d144dd49a9861f7bc92 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 31 Dec 2016 10:51:42 -0500 Subject: [PATCH 26/36] eliminate ban list overhead --- NTCPSession.cpp | 47 ++++------------------------------------------- NTCPSession.h | 6 +----- 2 files changed, 5 insertions(+), 48 deletions(-) diff --git a/NTCPSession.cpp b/NTCPSession.cpp index 81cfe687..712006c4 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -116,16 +116,10 @@ namespace transport void NTCPSession::ServerLogin () { - boost::system::error_code ec; - auto ep = m_Socket.remote_endpoint(ec); - if (!ec) - { - m_ConnectedFrom = ep.address (); - // receive Phase1 - boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } + // receive Phase1 + boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); } void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred) @@ -502,8 +496,6 @@ namespace transport if (ecode) { if (ecode != boost::asio::error::operation_aborted) LogPrint (eLogDebug, "NTCP: Read error: ", ecode.message ()); - if (!m_NumReceivedBytes) - m_Server.Ban (m_ConnectedFrom); //if (ecode != boost::asio::error::operation_aborted) Terminate (); } @@ -890,18 +882,6 @@ namespace transport if (!ec) { LogPrint (eLogDebug, "NTCP: Connected from ", ep); - auto it = m_BanList.find (ep.address ()); - if (it != m_BanList.end ()) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts < it->second) - { - LogPrint (eLogWarning, "NTCP: ", ep.address (), " is banned for ", it->second - ts, " more seconds"); - conn = nullptr; - } - else - m_BanList.erase (it); - } if (conn) conn->ServerLogin (); } @@ -927,18 +907,6 @@ namespace transport if (!ec) { LogPrint (eLogDebug, "NTCP: Connected from ", ep); - auto it = m_BanList.find (ep.address ()); - if (it != m_BanList.end ()) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts < it->second) - { - LogPrint (eLogWarning, "NTCP: ", ep.address (), " is banned for ", it->second - ts, " more seconds"); - conn = nullptr; - } - else - m_BanList.erase (it); - } if (conn) conn->ServerLogin (); } @@ -996,13 +964,6 @@ namespace transport } } - void NTCPServer::Ban (const boost::asio::ip::address& addr) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - m_BanList[addr] = ts + NTCP_BAN_EXPIRATION_TIMEOUT; - LogPrint (eLogWarning, "NTCP: ", addr, " has been banned for ", NTCP_BAN_EXPIRATION_TIMEOUT, " seconds"); - } - void NTCPServer::ScheduleTermination () { m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_CHECK_TIMEOUT)); diff --git a/NTCPSession.h b/NTCPSession.h index 639942f2..b02543be 100644 --- a/NTCPSession.h +++ b/NTCPSession.h @@ -122,8 +122,6 @@ namespace transport bool m_IsSending; std::vector > m_SendQueue; - - boost::asio::ip::address m_ConnectedFrom; // for ban }; // TODO: move to NTCP.h/.cpp @@ -145,8 +143,7 @@ namespace transport bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; }; bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; }; - boost::asio::io_service& GetService () { return m_Service; }; - void Ban (const boost::asio::ip::address& addr); + boost::asio::io_service& GetService () { return m_Service; }; private: @@ -169,7 +166,6 @@ namespace transport boost::asio::deadline_timer m_TerminationTimer; boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor; std::map > m_NTCPSessions; // access from m_Thread only - std::map m_BanList; // IP -> ban expiration time in seconds public: From 585a6c29d4622476218c3acea38fb8059c974297 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 31 Dec 2016 13:52:26 -0500 Subject: [PATCH 27/36] add relaytag after session established --- SSUSession.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SSUSession.cpp b/SSUSession.cpp index a74c6646..a47295fa 100644 --- a/SSUSession.cpp +++ b/SSUSession.cpp @@ -462,7 +462,6 @@ namespace transport { RAND_bytes((uint8_t *)&m_SentRelayTag, 4); if (!m_SentRelayTag) m_SentRelayTag = 1; - m_Server.AddRelay (m_SentRelayTag, shared_from_this ()); } htobe32buf (payload, m_SentRelayTag); payload += 4; // relay tag @@ -887,6 +886,8 @@ namespace transport transports.PeerConnected (shared_from_this ()); if (m_IsPeerTest) SendPeerTest (); + if (m_SentRelayTag) + m_Server.AddRelay (m_SentRelayTag, shared_from_this ()); m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); } From 7ef6c72fc0365e2a4497c6a7306a6c43d855d124 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 1 Jan 2017 08:52:36 -0500 Subject: [PATCH 28/36] fix http auth fail when auth too long --- HTTPServer.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 1ba22303..b9c26cc5 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -713,9 +713,11 @@ namespace http { } /* method #2: 'Authorization' header sent */ if (req.headers.count("Authorization") > 0) { + bool result = false; std::string provided = req.headers.find("Authorization")->second; std::string expected = user + ":" + pass; - char b64_creds[64]; + size_t b64_sz = i2p::data::Base64EncodingBufferSize(expected.length()); + char * b64_creds = new char[b64_sz+1]; std::size_t len = 0; len = i2p::data::ByteStreamToBase64((unsigned char *)expected.c_str(), expected.length(), b64_creds, sizeof(b64_creds)); /* if we decoded properly then check credentials */ @@ -723,10 +725,10 @@ namespace http { b64_creds[len] = '\0'; expected = "Basic "; expected += b64_creds; - return expected == provided; + result = expected == provided; } - /** we decoded wrong so it's not a correct login credential */ - return false; + delete [] b64_creds; + return result; } LogPrint(eLogWarning, "HTTPServer: auth failure from ", m_Socket->remote_endpoint().address ()); From 75d790137dc9823da671c6f4c8c65fb9103c4ee8 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 1 Jan 2017 08:54:11 -0500 Subject: [PATCH 29/36] don't use sizeof --- HTTPServer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HTTPServer.cpp b/HTTPServer.cpp index b9c26cc5..9cd8954d 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -716,10 +716,10 @@ namespace http { bool result = false; std::string provided = req.headers.find("Authorization")->second; std::string expected = user + ":" + pass; - size_t b64_sz = i2p::data::Base64EncodingBufferSize(expected.length()); - char * b64_creds = new char[b64_sz+1]; + size_t b64_sz = i2p::data::Base64EncodingBufferSize(expected.length()) + 1; + char * b64_creds = new char[b64_sz]; std::size_t len = 0; - len = i2p::data::ByteStreamToBase64((unsigned char *)expected.c_str(), expected.length(), b64_creds, sizeof(b64_creds)); + len = i2p::data::ByteStreamToBase64((unsigned char *)expected.c_str(), expected.length(), b64_creds, b64_sz); /* if we decoded properly then check credentials */ if(len) { b64_creds[len] = '\0'; From a8973f5463359956771cea8454f1baaf480181a3 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 1 Jan 2017 08:58:21 -0500 Subject: [PATCH 30/36] add peer count to transports --- HTTPServer.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 9cd8954d..184e02b1 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -494,8 +494,9 @@ namespace http { auto ntcpServer = i2p::transport::transports.GetNTCPServer (); if (ntcpServer) { - s << "NTCP
\r\n"; - for (const auto& it: ntcpServer->GetNTCPSessions ()) + auto sessions = ntcpServer->GetNTCPSessions (); + s << "NTCP ( " << (int) sessions.size() << " )
\r\n"; + for (const auto& it: sessions ) { if (it.second && it.second->IsEstablished ()) { @@ -512,8 +513,9 @@ namespace http { auto ssuServer = i2p::transport::transports.GetSSUServer (); if (ssuServer) { - s << "
\r\nSSU
\r\n"; - for (const auto& it: ssuServer->GetSessions ()) + auto sessions = ssuServer->GetSessions (); + s << "
\r\nSSU ( " << (int) sessions.size() << " )
\r\n"; + for (const auto& it: sessions) { auto endpoint = it.second->GetRemoteEndpoint (); if (it.second->IsOutgoing ()) s << " ⇒ "; From c2e7bc13a67b4ba6efe434db8ad4df68346ab032 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 1 Jan 2017 14:29:39 -0500 Subject: [PATCH 31/36] last sample should have more relevance for latency --- Tunnel.cpp | 31 +++---------------------------- Tunnel.h | 31 ++++++++----------------------- 2 files changed, 11 insertions(+), 51 deletions(-) diff --git a/Tunnel.cpp b/Tunnel.cpp index e1f5c035..84c4979f 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -19,36 +19,11 @@ namespace i2p { namespace tunnel -{ - - void TunnelLatency::AddSample(Sample s) - { - std::unique_lock l(m_access); - m_samples.push_back(s); - } - - bool TunnelLatency::HasSamples() const - { - std::unique_lock l(m_access); - return m_samples.size() > 0; - } - - TunnelLatency::Latency TunnelLatency::GetMeanLatency() const - { - std::unique_lock lock(m_access); - if (m_samples.size() > 0) { - Latency l = 0; - for(auto s : m_samples) - l += s; - return l / m_samples.size(); - } - return 0; - } - - +{ Tunnel::Tunnel (std::shared_ptr config): TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()), - m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false) + m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false), + m_Latency (0) { } diff --git a/Tunnel.h b/Tunnel.h index f3c31461..08533ec4 100644 --- a/Tunnel.h +++ b/Tunnel.h @@ -78,21 +78,6 @@ namespace tunnel eTunnelStateFailed, eTunnelStateExpiring }; - - /** @brief for storing latency history */ - struct TunnelLatency - { - typedef uint64_t Sample; - typedef uint64_t Latency; - - - void AddSample(Sample s); - bool HasSamples() const; - Latency GetMeanLatency() const; - - std::vector m_samples; - mutable std::mutex m_access; - }; class OutboundTunnel; class InboundTunnel; @@ -133,14 +118,14 @@ namespace tunnel void SendTunnelDataMsg (std::shared_ptr msg); void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out); - /** @brief add latency sample */ - void AddLatencySample(const uint64_t ms) { m_Latency.AddSample(ms); } - /** @brief get this tunnel's estimated latency */ - uint64_t GetMeanLatency() const { return m_Latency.GetMeanLatency(); } - /** @breif return true if this tunnel's latency fits in range [lowerbound, upperbound] */ - bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const; + /** @brief add latency sample */ + void AddLatencySample(const uint64_t ms) { m_Latency = (m_Latency + ms) >> 1; } + /** @brief get this tunnel's estimated latency */ + uint64_t GetMeanLatency() const { return m_Latency; } + /** @breif return true if this tunnel's latency fits in range [lowerbound, upperbound] */ + bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const; - bool LatencyIsKnown() const { return m_Latency.HasSamples(); } + bool LatencyIsKnown() const { return m_Latency > 0; } protected: void PrintHops (std::stringstream& s) const; @@ -152,7 +137,7 @@ namespace tunnel std::shared_ptr m_Pool; // pool, tunnel belongs to, or null TunnelState m_State; bool m_IsRecreated; - TunnelLatency m_Latency; + uint64_t m_Latency; // in milliseconds }; class OutboundTunnel: public Tunnel From 7d5a929b5e23984727d28558e53fac2c6605b612 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 2 Jan 2017 09:03:12 -0500 Subject: [PATCH 32/36] #761 info instead error --- Transports.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Transports.cpp b/Transports.cpp index 23e90e29..163a7da9 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -384,7 +384,7 @@ namespace transport } } } - LogPrint (eLogError, "Transports: No NTCP or SSU addresses available"); + LogPrint (eLogInfo, "Transports: No NTCP or SSU addresses available"); peer.Done (); std::unique_lock l(m_PeersMutex); m_Peers.erase (ident); From b35e5f158295fecbd873e1e865d933a772dc2ef4 Mon Sep 17 00:00:00 2001 From: hypnosis-i2p Date: Sun, 18 Dec 2016 18:13:35 +0800 Subject: [PATCH 33/36] Create README.md --- qt/i2pd_qt/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 qt/i2pd_qt/README.md diff --git a/qt/i2pd_qt/README.md b/qt/i2pd_qt/README.md new file mode 100644 index 00000000..9016be06 --- /dev/null +++ b/qt/i2pd_qt/README.md @@ -0,0 +1,3 @@ +# Build Requirements + + * Qt 5 is necessary since Qt4 lacks QtWidgets/ folder From b776b85fc384576dc627303d6200f5498768dd61 Mon Sep 17 00:00:00 2001 From: hypnosis-i2p Date: Sun, 18 Dec 2016 18:19:46 +0800 Subject: [PATCH 34/36] Update README.md --- qt/i2pd_qt/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt/i2pd_qt/README.md b/qt/i2pd_qt/README.md index 9016be06..94186ecf 100644 --- a/qt/i2pd_qt/README.md +++ b/qt/i2pd_qt/README.md @@ -1,3 +1,3 @@ # Build Requirements - * Qt 5 is necessary since Qt4 lacks QtWidgets/ folder + * Qt 5 is necessary (because Qt4 lacks QtWidgets/ folder) From c91f6db68a4d0feb4b1e1eb2b0f7f6ccc5bee39f Mon Sep 17 00:00:00 2001 From: hypnosis-i2p Date: Mon, 2 Jan 2017 23:38:08 +0800 Subject: [PATCH 35/36] updated to newer android sdk --- android/project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/project.properties b/android/project.properties index 7ce68660..f72b9716 100644 --- a/android/project.properties +++ b/android/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-24 +target=android-25 From 52035651758351077d14ec9b534abce4458ed81e Mon Sep 17 00:00:00 2001 From: hypnosis-i2p Date: Tue, 3 Jan 2017 01:14:44 +0800 Subject: [PATCH 36/36] updated icons --- android/res/drawable/icon.png | Bin 8712 -> 37177 bytes .../drawable/itoopie_notification_icon.png | Bin 33199 -> 37239 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/android/res/drawable/icon.png b/android/res/drawable/icon.png index a5dc7b680ba9dee30161ffb1c9fcd17bbd6ddd84..9a2f7404a20eef22adc6da0fb1538b6c507c97f4 100644 GIT binary patch literal 37177 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 RE#UwF002ovPDHLkV1gi%A?yGE literal 8712 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 diff --git a/android/res/drawable/itoopie_notification_icon.png b/android/res/drawable/itoopie_notification_icon.png index 8fbe24689b0af36100a6afa238829d7ce96a6529..d055bc8f897bdceee70c5a3e78ebedd926765a88 100644 GIT binary patch literal 37239 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