Date: Thu, 12 Feb 2015 20:05:55 +0100
Subject: [PATCH 20/95] LuaAPI: Added client TLS support for TCP links.
---
MCServer/Plugins/APIDump/Classes/Network.lua | 11 +-
MCServer/Plugins/NetworkTest/Info.lua | 6 +
MCServer/Plugins/NetworkTest/NetworkTest.lua | 44 ++++
src/Bindings/LuaState.cpp | 12 ++
src/Bindings/LuaState.h | 2 +
src/Bindings/LuaTCPLink.cpp | 201 +++++++++++++++++++
src/Bindings/LuaTCPLink.h | 55 +++++
src/Bindings/ManualBindings_Network.cpp | 56 +++++-
src/Globals.h | 3 +-
src/HTTPServer/SslHTTPConnection.cpp | 9 +
src/HTTPServer/SslHTTPConnection.h | 2 +
src/PolarSSL++/CryptoKey.cpp | 2 +-
12 files changed, 395 insertions(+), 8 deletions(-)
diff --git a/MCServer/Plugins/APIDump/Classes/Network.lua b/MCServer/Plugins/APIDump/Classes/Network.lua
index 065a743d8..ace6c2449 100644
--- a/MCServer/Plugins/APIDump/Classes/Network.lua
+++ b/MCServer/Plugins/APIDump/Classes/Network.lua
@@ -282,7 +282,15 @@ g_Server = nil
calling {{cNetwork}}:Connect() to connect to a remote server, or by listening using
{{cNetwork}}:Listen() and accepting incoming connections. The links are callback-based - when an event
such as incoming data or remote disconnect happens on the link, a specific callback is called. See the
- additional information in {{cNetwork}} documentation for details.
+ additional information in {{cNetwork}} documentation for details.
+
+ The link can also optionally perform TLS encryption. Plugins can use the StartTLSClient() function to
+ start the TLS handshake as the client side. Since that call, the OnReceivedData() callback is
+ overridden internally so that the data is first routed through the TLS decryptor, and the plugin's
+ callback is only called for the decrypted data, once it starts arriving. The Send function changes its
+ behavior so that the data written by the plugin is first encrypted and only then sent over the
+ network. Note that calling Send() before the TLS handshake finishes is supported, but the data is
+ queued internally and only sent once the TLS handshake is completed.
]],
Functions =
@@ -292,6 +300,7 @@ g_Server = nil
GetRemoteIP = { Params = "", Return = "string", Notes = "Returns the IP address of the remote endpoint of the TCP connection." },
GetRemotePort = { Params = "", Return = "number", Notes = "Returns the port of the remote endpoint of the TCP connection." },
Send = { Params = "Data", Return = "", Notes = "Sends the data (raw string) to the remote peer. The data is sent asynchronously and there is no report on the success of the send operation, other than the connection being closed or reset by the underlying OS." },
+ StartTLSClient = { Params = "OwnCert, OwnPrivateKey, OwnPrivateKeyPassword", Return = "", Notes = "Starts a TLS handshake on the link, as a client side of the TLS. The Own___ parameters specify the client certificate and its corresponding private key and password; all three parameters are optional and no client certificate is presented to the remote peer if they are not used or all empty. Once the TLS handshake is started by this call, all incoming data is first decrypted before being sent to the OnReceivedData callback, and all outgoing data is queued until the TLS handshake completes, and then sent encrypted over the link." },
},
}, -- cTCPLink
diff --git a/MCServer/Plugins/NetworkTest/Info.lua b/MCServer/Plugins/NetworkTest/Info.lua
index f366fd1be..c3c2ea8fc 100644
--- a/MCServer/Plugins/NetworkTest/Info.lua
+++ b/MCServer/Plugins/NetworkTest/Info.lua
@@ -84,6 +84,12 @@ g_PluginInfo =
},
}, -- lookup
+ wasc =
+ {
+ HelpString = "Requests the webadmin homepage using https",
+ Handler = HandleConsoleNetWasc,
+ }, -- wasc
+
}, -- Subcommands
}, -- net
},
diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua
index 7932f4b88..21f89c7f9 100644
--- a/MCServer/Plugins/NetworkTest/NetworkTest.lua
+++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua
@@ -252,3 +252,47 @@ end
+
+function HandleConsoleNetWasc(a_Split)
+ local Callbacks =
+ {
+ OnConnected = function (a_Link)
+ LOG("Connected to webadmin, starting TLS...")
+ local res, msg = a_Link:StartTLSClient("", "", "")
+ if not(res) then
+ LOG("Failed to start TLS client: " .. msg)
+ return
+ end
+ -- We need to send a keep-alive due to #1737
+ a_Link:Send("GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n")
+ end,
+
+ OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
+ LOG("Connection to webadmin failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end,
+
+ OnReceivedData = function (a_Link, a_Data)
+ LOG("Received data from webadmin:\r\n" .. a_Data)
+
+ -- Close the link once all the data is received:
+ if (a_Data == "0\r\n\r\n") then -- Poor man's end-of-data detection; works on localhost
+ -- TODO: The Close() method is not yet exported to Lua
+ -- a_Link:Close()
+ end
+ end,
+
+ OnRemoteClosed = function (a_Link)
+ LOG("Connection to webadmin was closed")
+ end,
+ }
+
+ if not(cNetwork:Connect("localhost", "8080", Callbacks)) then
+ LOG("Canot connect to webadmin")
+ end
+
+ return true
+end
+
+
+
+
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index 73b114599..81770058c 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -343,6 +343,18 @@ bool cLuaState::PushFunction(const cTableRef & a_TableRef)
+void cLuaState::PushNil(void)
+{
+ ASSERT(IsValid());
+
+ lua_pushnil(m_LuaState);
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
void cLuaState::Push(const AString & a_String)
{
ASSERT(IsValid());
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index f68b022ea..7fc3197eb 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -184,6 +184,8 @@ public:
/** Returns true if a_FunctionName is a valid Lua function that can be called */
bool HasFunction(const char * a_FunctionName);
+ void PushNil(void);
+
// Push a const value onto the stack (keep alpha-sorted):
void Push(const AString & a_String);
void Push(const AStringVector & a_Vector);
diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp
index 6b8395806..7e2c10e13 100644
--- a/src/Bindings/LuaTCPLink.cpp
+++ b/src/Bindings/LuaTCPLink.cpp
@@ -64,6 +64,13 @@ cLuaTCPLink::~cLuaTCPLink()
bool cLuaTCPLink::Send(const AString & a_Data)
{
+ // If running in SSL mode, push the data into the SSL context instead:
+ if (m_SslContext != nullptr)
+ {
+ m_SslContext->Send(a_Data);
+ return true;
+ }
+
// Safely grab a copy of the link:
cTCPLinkPtr Link = m_Link;
if (Link == nullptr)
@@ -179,6 +186,58 @@ void cLuaTCPLink::Close(void)
+AString cLuaTCPLink::StartTLSClient(
+ const AString & a_OwnCertData,
+ const AString & a_OwnPrivKeyData,
+ const AString & a_OwnPrivKeyPassword
+)
+{
+ // Check preconditions:
+ if (m_SslContext != nullptr)
+ {
+ return "TLS is already active on this link";
+ }
+ if (
+ (a_OwnCertData.empty() && !a_OwnPrivKeyData.empty()) ||
+ (!a_OwnCertData.empty() && a_OwnPrivKeyData.empty())
+ )
+ {
+ return "Either provide both the certificate and private key, or neither";
+ }
+
+ // Create the SSL context:
+ m_SslContext = std::make_unique(*this);
+ m_SslContext->Initialize(true);
+
+ // Create the peer cert, if required:
+ if (!a_OwnCertData.empty() && !a_OwnPrivKeyData.empty())
+ {
+ auto OwnCert = std::make_shared();
+ int res = OwnCert->Parse(a_OwnCertData.data(), a_OwnCertData.size());
+ if (res != 0)
+ {
+ m_SslContext.reset();
+ return Printf("Cannot parse peer certificate: -0x%x", res);
+ }
+ auto OwnPrivKey = std::make_shared();
+ res = OwnPrivKey->ParsePrivate(a_OwnPrivKeyData.data(), a_OwnPrivKeyData.size(), a_OwnPrivKeyPassword);
+ if (res != 0)
+ {
+ m_SslContext.reset();
+ return Printf("Cannot parse peer private key: -0x%x", res);
+ }
+ m_SslContext->SetOwnCert(OwnCert, OwnPrivKey);
+ }
+
+ // Start the handshake:
+ m_SslContext->Handshake();
+ return "";
+}
+
+
+
+
+
void cLuaTCPLink::Terminated(void)
{
// Disable the callbacks:
@@ -207,6 +266,26 @@ void cLuaTCPLink::Terminated(void)
+void cLuaTCPLink::ReceivedCleartextData(const char * a_Data, size_t a_NumBytes)
+{
+ // Check if we're still valid:
+ if (!m_Callbacks.IsValid())
+ {
+ return;
+ }
+
+ // Call the callback:
+ cPluginLua::cOperation Op(m_Plugin);
+ if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnReceivedData"), this, AString(a_Data, a_NumBytes)))
+ {
+ LOGINFO("cTCPLink OnReceivedData callback failed in plugin %s.", m_Plugin.GetName().c_str());
+ }
+}
+
+
+
+
+
void cLuaTCPLink::OnConnected(cTCPLink & a_Link)
{
// Check if we're still valid:
@@ -269,6 +348,13 @@ void cLuaTCPLink::OnReceivedData(const char * a_Data, size_t a_Length)
return;
}
+ // If we're running in SSL mode, put the data into the SSL decryptor:
+ if (m_SslContext != nullptr)
+ {
+ m_SslContext->StoreReceivedData(a_Data, a_Length);
+ return;
+ }
+
// Call the callback:
cPluginLua::cOperation Op(m_Plugin);
if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnReceivedData"), this, AString(a_Data, a_Length)))
@@ -302,3 +388,118 @@ void cLuaTCPLink::OnRemoteClosed(void)
+
+////////////////////////////////////////////////////////////////////////////////
+// cLuaTCPLink::cLinkSslContext:
+
+cLuaTCPLink::cLinkSslContext::cLinkSslContext(cLuaTCPLink & a_Link):
+ m_Link(a_Link)
+{
+}
+
+
+
+
+
+void cLuaTCPLink::cLinkSslContext::StoreReceivedData(const char * a_Data, size_t a_NumBytes)
+{
+ m_EncryptedData.append(a_Data, a_NumBytes);
+
+ // Try to finish a pending handshake:
+ TryFinishHandshaking();
+
+ // Flush any cleartext data that can be "received":
+ FlushBuffers();
+}
+
+
+
+
+
+void cLuaTCPLink::cLinkSslContext::FlushBuffers(void)
+{
+ // If the handshake didn't complete yet, bail out:
+ if (!HasHandshaken())
+ {
+ return;
+ }
+
+ char Buffer[1024];
+ int NumBytes;
+ while ((NumBytes = ReadPlain(Buffer, sizeof(Buffer))) > 0)
+ {
+ m_Link.ReceivedCleartextData(Buffer, static_cast(NumBytes));
+ }
+}
+
+
+
+
+
+void cLuaTCPLink::cLinkSslContext::TryFinishHandshaking(void)
+{
+ // If the handshake hasn't finished yet, retry:
+ if (!HasHandshaken())
+ {
+ Handshake();
+ }
+
+ // If the handshake succeeded, write all the queued plaintext data:
+ if (HasHandshaken())
+ {
+ WritePlain(m_CleartextData.data(), m_CleartextData.size());
+ m_CleartextData.clear();
+ }
+}
+
+
+
+
+
+void cLuaTCPLink::cLinkSslContext::Send(const AString & a_Data)
+{
+ // If the handshake hasn't completed yet, queue the data:
+ if (!HasHandshaken())
+ {
+ m_CleartextData.append(a_Data);
+ TryFinishHandshaking();
+ return;
+ }
+
+ // The connection is all set up, write the cleartext data into the SSL context:
+ WritePlain(a_Data.data(), a_Data.size());
+ FlushBuffers();
+}
+
+
+
+
+
+int cLuaTCPLink::cLinkSslContext::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes)
+{
+ // If there's nothing queued in the buffer, report empty buffer:
+ if (m_EncryptedData.empty())
+ {
+ return POLARSSL_ERR_NET_WANT_READ;
+ }
+
+ // Copy as much data as possible to the provided buffer:
+ size_t BytesToCopy = std::min(a_NumBytes, m_EncryptedData.size());
+ memcpy(a_Buffer, m_EncryptedData.data(), BytesToCopy);
+ m_EncryptedData.erase(0, BytesToCopy);
+ return static_cast(BytesToCopy);
+}
+
+
+
+
+
+int cLuaTCPLink::cLinkSslContext::SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes)
+{
+ m_Link.m_Link->Send(a_Buffer, a_NumBytes);
+ return static_cast(a_NumBytes);
+}
+
+
+
+
diff --git a/src/Bindings/LuaTCPLink.h b/src/Bindings/LuaTCPLink.h
index f2af911ec..9536c052b 100644
--- a/src/Bindings/LuaTCPLink.h
+++ b/src/Bindings/LuaTCPLink.h
@@ -11,6 +11,7 @@
#include "../OSSupport/Network.h"
#include "PluginLua.h"
+#include "../PolarSSL++/SslContext.h"
@@ -62,7 +63,53 @@ public:
Sends the RST packet, queued outgoing and incoming data is lost. */
void Close(void);
+ /** Starts a TLS handshake as a client connection.
+ If a client certificate should be used for the connection, set the certificate into a_OwnCertData and
+ its corresponding private key to a_OwnPrivKeyData. If both are empty, no client cert is presented.
+ a_OwnPrivKeyPassword is the password to be used for decoding PrivKey, empty if not passworded.
+ Returns empty string on success, non-empty error description on failure. */
+ AString StartTLSClient(
+ const AString & a_OwnCertData,
+ const AString & a_OwnPrivKeyData,
+ const AString & a_OwnPrivKeyPassword
+ );
+
protected:
+ /** Wrapper around cSslContext that is used when this link is being encrypted by SSL. */
+ class cLinkSslContext :
+ public cSslContext
+ {
+ cLuaTCPLink & m_Link;
+
+ /** Buffer for storing the incoming encrypted data until it is requested by the SSL decryptor. */
+ AString m_EncryptedData;
+
+ /** Buffer for storing the outgoing cleartext data until the link has finished handshaking. */
+ AString m_CleartextData;
+
+ public:
+ cLinkSslContext(cLuaTCPLink & a_Link);
+
+ /** Stores the specified block of data into the buffer of the data to be decrypted (incoming from remote).
+ Also flushes the SSL buffers by attempting to read any data through the SSL context. */
+ void StoreReceivedData(const char * a_Data, size_t a_NumBytes);
+
+ /** Tries to read any cleartext data available through the SSL, reports it in the link. */
+ void FlushBuffers(void);
+
+ /** Tries to finish handshaking the SSL. */
+ void TryFinishHandshaking(void);
+
+ /** Sends the specified cleartext data over the SSL to the remote peer.
+ If the handshake hasn't been completed yet, queues the data for sending when it completes. */
+ void Send(const AString & a_Data);
+
+ // cSslContext overrides:
+ virtual int ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) override;
+ virtual int SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) override;
+ };
+
+
/** The plugin for which the link is created. */
cPluginLua & m_Plugin;
@@ -76,11 +123,19 @@ protected:
/** The server that is responsible for this link, if any. */
cLuaServerHandleWPtr m_Server;
+ /** The SSL context used for encryption, if this link uses SSL.
+ If valid, the link uses encryption through this context. */
+ UniquePtr m_SslContext;
+
/** Common code called when the link is considered as terminated.
Releases m_Link, m_Callbacks and this from m_Server, each when applicable. */
void Terminated(void);
+ /** Called by the SSL context when there's incoming data available in the cleartext.
+ Reports the data via the Lua callback function. */
+ void ReceivedCleartextData(const char * a_Data, size_t a_NumBytes);
+
// cNetwork::cConnectCallbacks overrides:
virtual void OnConnected(cTCPLink & a_Link) override;
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override;
diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp
index 902f687c8..ff0f3568c 100644
--- a/src/Bindings/ManualBindings_Network.cpp
+++ b/src/Bindings/ManualBindings_Network.cpp
@@ -389,6 +389,51 @@ static int tolua_cTCPLink_GetRemotePort(lua_State * L)
+/** Binds cLuaTCPLink::StartTLSClient */
+static int tolua_cTCPLink_StartTLSClient(lua_State * L)
+{
+ // Function signature:
+ // LinkInstance:StartTLSClient(OwnCert, OwnPrivKey, OwnPrivKeyPassword) -> [true] or [nil, ErrMsg]
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserType(1, "cTCPLink") ||
+ !S.CheckParamString(2, 4) ||
+ !S.CheckParamEnd(5)
+ )
+ {
+ return 0;
+ }
+
+ // Get the link:
+ cLuaTCPLink * Link;
+ if (lua_isnil(L, 1))
+ {
+ LOGWARNING("cTCPLink:StartTLSClient(): invalid link object. Stack trace:");
+ S.LogStackTrace();
+ return 0;
+ }
+ Link = *static_cast(lua_touserdata(L, 1));
+
+ // Read the params:
+ AString OwnCert, OwnPrivKey, OwnPrivKeyPassword;
+ S.GetStackValues(2, OwnCert, OwnPrivKey, OwnPrivKeyPassword);
+
+ // Start the TLS handshake:
+ AString res = Link->StartTLSClient(OwnCert, OwnPrivKey, OwnPrivKeyPassword);
+ if (!res.empty())
+ {
+ S.PushNil();
+ S.Push(Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str()));
+ return 2;
+ }
+ return 1;
+}
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cServerHandle bindings (routed through cLuaServerHandle):
@@ -495,11 +540,12 @@ void ManualBindings::BindNetwork(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cTCPLink");
- tolua_function(tolua_S, "Send", tolua_cTCPLink_Send);
- tolua_function(tolua_S, "GetLocalIP", tolua_cTCPLink_GetLocalIP);
- tolua_function(tolua_S, "GetLocalPort", tolua_cTCPLink_GetLocalPort);
- tolua_function(tolua_S, "GetRemoteIP", tolua_cTCPLink_GetRemoteIP);
- tolua_function(tolua_S, "GetRemotePort", tolua_cTCPLink_GetRemotePort);
+ tolua_function(tolua_S, "Send", tolua_cTCPLink_Send);
+ tolua_function(tolua_S, "GetLocalIP", tolua_cTCPLink_GetLocalIP);
+ tolua_function(tolua_S, "GetLocalPort", tolua_cTCPLink_GetLocalPort);
+ tolua_function(tolua_S, "GetRemoteIP", tolua_cTCPLink_GetRemoteIP);
+ tolua_function(tolua_S, "GetRemotePort", tolua_cTCPLink_GetRemotePort);
+ tolua_function(tolua_S, "StartTLSClient", tolua_cTCPLink_StartTLSClient);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cServerHandle");
diff --git a/src/Globals.h b/src/Globals.h
index 29eaac871..7c2ab38d8 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -379,9 +379,10 @@ void inline LOG(const char * a_Format, ...)
#define assert_test(x) ( !!(x) || (assert(!#x), exit(1), 0))
#endif
-// Unified shared ptr from before C++11. Also no silly undercores.
+// Unified ptr types from before C++11. Also no silly undercores.
#define SharedPtr std::shared_ptr
#define WeakPtr std::weak_ptr
+#define UniquePtr std::unique_ptr
diff --git a/src/HTTPServer/SslHTTPConnection.cpp b/src/HTTPServer/SslHTTPConnection.cpp
index f09daac8f..f8dea0731 100644
--- a/src/HTTPServer/SslHTTPConnection.cpp
+++ b/src/HTTPServer/SslHTTPConnection.cpp
@@ -25,6 +25,15 @@ cSslHTTPConnection::cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509Ce
+cSslHTTPConnection::~cSslHTTPConnection()
+{
+ m_Ssl.NotifyClose();
+}
+
+
+
+
+
void cSslHTTPConnection::OnReceivedData(const char * a_Data, size_t a_Size)
{
// Process the received data:
diff --git a/src/HTTPServer/SslHTTPConnection.h b/src/HTTPServer/SslHTTPConnection.h
index dc54b1eff..c461a3a24 100644
--- a/src/HTTPServer/SslHTTPConnection.h
+++ b/src/HTTPServer/SslHTTPConnection.h
@@ -25,6 +25,8 @@ public:
/** Creates a new connection on the specified server.
Sends the specified cert as the server certificate, uses the private key for decryption. */
cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey);
+
+ ~cSslHTTPConnection();
protected:
cBufferedSslContext m_Ssl;
diff --git a/src/PolarSSL++/CryptoKey.cpp b/src/PolarSSL++/CryptoKey.cpp
index 7c4f021b3..9354ddf50 100644
--- a/src/PolarSSL++/CryptoKey.cpp
+++ b/src/PolarSSL++/CryptoKey.cpp
@@ -45,7 +45,7 @@ cCryptoKey::cCryptoKey(const AString & a_PrivateKeyData, const AString & a_Passw
if (res != 0)
{
LOGWARNING("Failed to parse private key: -0x%x", res);
- ASSERT(!"Cannot parse PubKey");
+ ASSERT(!"Cannot parse PrivKey");
return;
}
}
From 505dce1fc38def5d28f8f71fa060a2bc76dd40b4 Mon Sep 17 00:00:00 2001
From: Mattes D
Date: Thu, 12 Feb 2015 20:22:39 +0100
Subject: [PATCH 21/95] Fixed Linux compilation.
std::make_unique is not available in C++11.
---
src/Bindings/LuaTCPLink.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp
index 7e2c10e13..c533456ad 100644
--- a/src/Bindings/LuaTCPLink.cpp
+++ b/src/Bindings/LuaTCPLink.cpp
@@ -206,7 +206,7 @@ AString cLuaTCPLink::StartTLSClient(
}
// Create the SSL context:
- m_SslContext = std::make_unique(*this);
+ m_SslContext.reset(new cLinkSslContext(*this));
m_SslContext->Initialize(true);
// Create the peer cert, if required:
From bae8b2e1faa918ad483f9f2c88621e7b7498ca3a Mon Sep 17 00:00:00 2001
From: Mattes D
Date: Thu, 12 Feb 2015 20:23:04 +0100
Subject: [PATCH 22/95] PolarSSL++: Fixed debugging output.
---
src/PolarSSL++/SslContext.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/PolarSSL++/SslContext.cpp b/src/PolarSSL++/SslContext.cpp
index 66dfefc65..8ab207df6 100644
--- a/src/PolarSSL++/SslContext.cpp
+++ b/src/PolarSSL++/SslContext.cpp
@@ -7,6 +7,7 @@
#include "SslContext.h"
#include "EntropyContext.h"
#include "CtrDrbgContext.h"
+#include "polarssl/debug.h"
@@ -69,6 +70,8 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr &
// These functions allow us to debug SSL and certificate problems, but produce way too much output,
// so they're disabled until someone needs them
ssl_set_dbg(&m_Ssl, &SSLDebugMessage, this);
+ debug_set_threshold(4);
+
ssl_set_verify(&m_Ssl, &SSLVerifyCert, this);
//*/
From b8bf795dd1701a32075950a6ea98a16eacb9edc9 Mon Sep 17 00:00:00 2001
From: Mattes D
Date: Fri, 13 Feb 2015 18:31:54 +0100
Subject: [PATCH 23/95] Exported cTCPLink:Close and :Shutdown() to Lua API.
---
MCServer/Plugins/APIDump/Classes/Network.lua | 2 +
src/Bindings/ManualBindings_Network.cpp | 96 +++++++++++++++++---
2 files changed, 85 insertions(+), 13 deletions(-)
diff --git a/MCServer/Plugins/APIDump/Classes/Network.lua b/MCServer/Plugins/APIDump/Classes/Network.lua
index ace6c2449..1dc0f3ae7 100644
--- a/MCServer/Plugins/APIDump/Classes/Network.lua
+++ b/MCServer/Plugins/APIDump/Classes/Network.lua
@@ -295,11 +295,13 @@ g_Server = nil
Functions =
{
+ Close = { Params = "", Return = "", Notes = "Closes the link forcefully (TCP RST). There's no guarantee that the last sent data is even being delivered. See also the Shutdown() method." },
GetLocalIP = { Params = "", Return = "string", Notes = "Returns the IP address of the local endpoint of the TCP connection." },
GetLocalPort = { Params = "", Return = "number", Notes = "Returns the port of the local endpoint of the TCP connection." },
GetRemoteIP = { Params = "", Return = "string", Notes = "Returns the IP address of the remote endpoint of the TCP connection." },
GetRemotePort = { Params = "", Return = "number", Notes = "Returns the port of the remote endpoint of the TCP connection." },
Send = { Params = "Data", Return = "", Notes = "Sends the data (raw string) to the remote peer. The data is sent asynchronously and there is no report on the success of the send operation, other than the connection being closed or reset by the underlying OS." },
+ Shutdown = { Params = "", Return = "", Notes = "Shuts the socket down for sending data. Notifies the remote peer that there will be no more data coming from us (TCP FIN). The data that is in flight will still be delivered. The underlying socket will be closed when the remote end shuts down as well, or after a timeout." },
StartTLSClient = { Params = "OwnCert, OwnPrivateKey, OwnPrivateKeyPassword", Return = "", Notes = "Starts a TLS handshake on the link, as a client side of the TLS. The Own___ parameters specify the client certificate and its corresponding private key and password; all three parameters are optional and no client certificate is presented to the remote peer if they are not used or all empty. Once the TLS handshake is started by this call, all incoming data is first decrypted before being sent to the OnReceivedData callback, and all outgoing data is queued until the TLS handshake completes, and then sent encrypted over the link." },
},
}, -- cTCPLink
diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp
index ff0f3568c..4a6b7bc0e 100644
--- a/src/Bindings/ManualBindings_Network.cpp
+++ b/src/Bindings/ManualBindings_Network.cpp
@@ -214,17 +214,16 @@ static int tolua_cNetwork_Listen(lua_State * L)
////////////////////////////////////////////////////////////////////////////////
// cTCPLink bindings (routed through cLuaTCPLink):
-/** Binds cLuaTCPLink::Send */
-static int tolua_cTCPLink_Send(lua_State * L)
+/** Binds cLuaTCPLink::Close */
+static int tolua_cTCPLink_Close(lua_State * L)
{
// Function signature:
- // LinkInstance:Send(DataString)
+ // LinkInstance:Close()
cLuaState S(L);
if (
!S.CheckParamUserType(1, "cTCPLink") ||
- !S.CheckParamString(2) ||
- !S.CheckParamEnd(3)
+ !S.CheckParamEnd(2)
)
{
return 0;
@@ -234,18 +233,14 @@ static int tolua_cTCPLink_Send(lua_State * L)
cLuaTCPLink * Link;
if (lua_isnil(L, 1))
{
- LOGWARNING("cTCPLink:Send(): invalid link object. Stack trace:");
+ LOGWARNING("cTCPLink:Close(): invalid link object. Stack trace:");
S.LogStackTrace();
return 0;
}
Link = *static_cast(lua_touserdata(L, 1));
- // Get the data to send:
- AString Data;
- S.GetStackValues(2, Data);
-
- // Send the data:
- Link->Send(Data);
+ // CLose the link:
+ Link->Close();
return 0;
}
@@ -389,6 +384,79 @@ static int tolua_cTCPLink_GetRemotePort(lua_State * L)
+/** Binds cLuaTCPLink::Send */
+static int tolua_cTCPLink_Send(lua_State * L)
+{
+ // Function signature:
+ // LinkInstance:Send(DataString)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserType(1, "cTCPLink") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the link:
+ cLuaTCPLink * Link;
+ if (lua_isnil(L, 1))
+ {
+ LOGWARNING("cTCPLink:Send(): invalid link object. Stack trace:");
+ S.LogStackTrace();
+ return 0;
+ }
+ Link = *static_cast(lua_touserdata(L, 1));
+
+ // Get the data to send:
+ AString Data;
+ S.GetStackValues(2, Data);
+
+ // Send the data:
+ Link->Send(Data);
+ return 0;
+}
+
+
+
+
+
+/** Binds cLuaTCPLink::Shutdown */
+static int tolua_cTCPLink_Shutdown(lua_State * L)
+{
+ // Function signature:
+ // LinkInstance:Shutdown()
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserType(1, "cTCPLink") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the link:
+ cLuaTCPLink * Link;
+ if (lua_isnil(L, 1))
+ {
+ LOGWARNING("cTCPLink:Shutdown(): invalid link object. Stack trace:");
+ S.LogStackTrace();
+ return 0;
+ }
+ Link = *static_cast(lua_touserdata(L, 1));
+
+ // Shutdown the link:
+ Link->Shutdown();
+ return 0;
+}
+
+
+
+
+
/** Binds cLuaTCPLink::StartTLSClient */
static int tolua_cTCPLink_StartTLSClient(lua_State * L)
{
@@ -540,11 +608,13 @@ void ManualBindings::BindNetwork(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cTCPLink");
- tolua_function(tolua_S, "Send", tolua_cTCPLink_Send);
+ tolua_function(tolua_S, "Close", tolua_cTCPLink_Close);
tolua_function(tolua_S, "GetLocalIP", tolua_cTCPLink_GetLocalIP);
tolua_function(tolua_S, "GetLocalPort", tolua_cTCPLink_GetLocalPort);
tolua_function(tolua_S, "GetRemoteIP", tolua_cTCPLink_GetRemoteIP);
tolua_function(tolua_S, "GetRemotePort", tolua_cTCPLink_GetRemotePort);
+ tolua_function(tolua_S, "Send", tolua_cTCPLink_Send);
+ tolua_function(tolua_S, "Shutdown", tolua_cTCPLink_Shutdown);
tolua_function(tolua_S, "StartTLSClient", tolua_cTCPLink_StartTLSClient);
tolua_endmodule(tolua_S);
From 557adf3be944b8a91c768ee85241b7c8bc57c0a6 Mon Sep 17 00:00:00 2001
From: Mattes D
Date: Fri, 13 Feb 2015 23:18:22 +0100
Subject: [PATCH 24/95] Exported TLS server start on cTCPLink to Lua API.
---
MCServer/Plugins/APIDump/Classes/Network.lua | 1 +
MCServer/Plugins/NetworkTest/NetworkTest.lua | 106 ++++++++++++++++-
src/Bindings/LuaTCPLink.cpp | 117 ++++++++++++++++++-
src/Bindings/LuaTCPLink.h | 31 ++++-
src/Bindings/ManualBindings_Network.cpp | 47 ++++++++
5 files changed, 292 insertions(+), 10 deletions(-)
diff --git a/MCServer/Plugins/APIDump/Classes/Network.lua b/MCServer/Plugins/APIDump/Classes/Network.lua
index 1dc0f3ae7..274c8d035 100644
--- a/MCServer/Plugins/APIDump/Classes/Network.lua
+++ b/MCServer/Plugins/APIDump/Classes/Network.lua
@@ -303,6 +303,7 @@ g_Server = nil
Send = { Params = "Data", Return = "", Notes = "Sends the data (raw string) to the remote peer. The data is sent asynchronously and there is no report on the success of the send operation, other than the connection being closed or reset by the underlying OS." },
Shutdown = { Params = "", Return = "", Notes = "Shuts the socket down for sending data. Notifies the remote peer that there will be no more data coming from us (TCP FIN). The data that is in flight will still be delivered. The underlying socket will be closed when the remote end shuts down as well, or after a timeout." },
StartTLSClient = { Params = "OwnCert, OwnPrivateKey, OwnPrivateKeyPassword", Return = "", Notes = "Starts a TLS handshake on the link, as a client side of the TLS. The Own___ parameters specify the client certificate and its corresponding private key and password; all three parameters are optional and no client certificate is presented to the remote peer if they are not used or all empty. Once the TLS handshake is started by this call, all incoming data is first decrypted before being sent to the OnReceivedData callback, and all outgoing data is queued until the TLS handshake completes, and then sent encrypted over the link." },
+ StartTLSServer = { Params = "Certificate, PrivateKey, PrivateKeyPassword, StartTLSData", Return = "", Notes = "Starts a TLS handshake on the link, as a server side of the TLS. The plugin needs to specify the server certificate and its corresponding private key and password. The StartTLSData can contain data that the link has already reported as received but it should be used as part of the TLS handshake. Once the TLS handshake is started by this call, all incoming data is first decrypted before being sent to the OnReceivedData callback, and all outgoing data is queued until the TLS handshake completes, and then sent encrypted over the link." },
},
}, -- cTCPLink
diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua
index 21f89c7f9..251e29884 100644
--- a/MCServer/Plugins/NetworkTest/NetworkTest.lua
+++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua
@@ -19,6 +19,62 @@ local g_Fortunes =
"Empty splashes.txt",
}
+-- HTTPS certificate to be used for the SSL server:
+local g_HTTPSCert = [[
+-----BEGIN CERTIFICATE-----
+MIIDfzCCAmegAwIBAgIJAOBHN+qOWodcMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV
+BAYTAmN6MQswCQYDVQQIDAJjejEMMAoGA1UEBwwDbG9jMQswCQYDVQQKDAJfWDEL
+MAkGA1UECwwCT1UxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTAxMjQwODQ2MzFa
+Fw0yNTAxMjEwODQ2MzFaMFYxCzAJBgNVBAYTAmN6MQswCQYDVQQIDAJjejEMMAoG
+A1UEBwwDbG9jMQswCQYDVQQKDAJfWDELMAkGA1UECwwCT1UxEjAQBgNVBAMMCWxv
+Y2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJkFYSElu/jw
+nxqjimmj246DejKJK8uy/l9QQibb/Z4kO/3s0gVPOYo0mKv32xUFP7wYIE3XWT61
+zyfvK+1jpnlQTCtM8T5xw/7CULKgLmuIzlQx5Dhy7d+tW46kOjFKwQajS9YzwqWu
+KBOPnFamQWz6vIzuM05+7aIMXbzamInvW/1x3klIrpGQgALwSB1N+oUzTInTBRKK
+21pecUE9t3qrU40Cs5bN0fQBnBjLwbgmnTh6LEplfQZHG5wLvj0IeERVU9vH7luM
+e9/IxuEZluCiu5ViF3jqLPpjYOrkX7JDSKme64CCmNIf0KkrwtFjF104Qylike60
+YD3+kw8Q+DECAwEAAaNQME4wHQYDVR0OBBYEFHHIDTc7mrLDXftjQ5ejU9Udfdyo
+MB8GA1UdIwQYMBaAFHHIDTc7mrLDXftjQ5ejU9UdfdyoMAwGA1UdEwQFMAMBAf8w
+DQYJKoZIhvcNAQEFBQADggEBAHxCJxZPmH9tvx8GKiDV3rgGY++sMItzrW5Uhf0/
+bl3DPbVz51CYF8nXiWvSJJzxhH61hKpZiqvRlpyMuovV415dYQ+Xc2d2IrTX6e+d
+Z4Pmwfb4yaX+kYqIygjXMoyNxOJyhTnCbJzycV3v5tvncBWN9Wqez6ZonWDdFdAm
+J+Moty+atc4afT02sUg1xz+CDr1uMbt62tHwKYCdxXCwT//bOs6W21+mQJ5bEAyA
+YrHQPgX76uo8ed8rPf6y8Qj//lzq/+33EIWqf9pnbklQgIPXJU07h+5L+Y63RF4A
+ComLkzas+qnQLcEN28Dg8QElXop6hfiD0xq3K0ac2bDnjoU=
+-----END CERTIFICATE-----
+]]
+
+local g_HTTPSPrivKey = [[
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZBWEhJbv48J8a
+o4ppo9uOg3oyiSvLsv5fUEIm2/2eJDv97NIFTzmKNJir99sVBT+8GCBN11k+tc8n
+7yvtY6Z5UEwrTPE+ccP+wlCyoC5riM5UMeQ4cu3frVuOpDoxSsEGo0vWM8KlrigT
+j5xWpkFs+ryM7jNOfu2iDF282piJ71v9cd5JSK6RkIAC8EgdTfqFM0yJ0wUSitta
+XnFBPbd6q1ONArOWzdH0AZwYy8G4Jp04eixKZX0GRxucC749CHhEVVPbx+5bjHvf
+yMbhGZbgoruVYhd46iz6Y2Dq5F+yQ0ipnuuAgpjSH9CpK8LRYxddOEMpYpHutGA9
+/pMPEPgxAgMBAAECggEAWxQ4m+I54BJYoSJ2YCqHpGvdb/b1emkvvsumlDqc2mP2
+0U0ENOTS+tATj0gXvotBRFOX5r0nAYx1oO9a1hFaJRsGOz+w19ofLqO6JJfzCU6E
+gNixXmgJ7fjhZiWZ/XzhJ3JK0VQ9px/h+sKf63NJvfQABmJBZ5dlGe8CXEZARNin
+03TnE3RUIEK+jEgwShN2OrGjwK9fjcnXMHwEnKZtCBiYEfD2N+pQmS20gIm13L1t
++ZmObIC24NqllXxl4I821qzBdhmcT7+rGmKR0OT5YKbt6wFA5FPKD9dqlzXzlKck
+r2VAh+JlCtFKxcScmWtQOnVDtf5+mcKFbP4ck724AQKBgQDLk+RDhvE5ykin5R+B
+dehUQZgHb2pPL7N1DAZShfzwSmyZSOPQDFr7c0CMijn6G0Pw9VX6Vrln0crfTQYz
+Hli+zxlmcMAD/WC6VImM1LCUzouNRy37rSCnuPtngZyHdsyzfheGnjORH7HlPjtY
+JCTLaekg0ckQvt//HpRV3DCdaQKBgQDAbLmIOTyGfne74HLswWnY/kCOfFt6eO+E
+lZ724MWmVPWkxq+9rltC2CDx2i8jjdkm90dsgR5OG2EaLnUWldUpkE0zH0ATrZSV
+ezJWD9SsxTm8ksbThD+pJKAVPxDAboejF7kPvpaO2wY+bf0AbO3M24rJ2tccpMv8
+AcfXBICDiQKBgQCSxp81/I3hf7HgszaC7ZLDZMOK4M6CJz847aGFUCtsyAwCfGYb
+8zyJvK/WZDam14+lpA0IQAzPCJg/ZVZJ9uA/OivzCum2NrHNxfOiQRrLPxuokaBa
+q5k2tA02tGE53fJ6mze1DEzbnkFxqeu5gd2xdzvpOLfBxgzT8KU8PlQiuQKBgGn5
+NvCj/QZhDhYFVaW4G1ArLmiKamL3yYluUV7LiW7CaYp29gBzzsTwfKxVqhJdo5NH
+KinCrmr7vy2JGmj22a+LTkjyU/rCZQsyDxXAoDMKZ3LILwH8WocPqa4pzlL8TGzw
+urXGE+rXCwhE0Mp0Mz7YRgZHJKMcy06duG5dh11pAoGBALHbsBIDihgHPyp2eKMP
+K1f42MdKrTBiIXV80hv2OnvWVRCYvnhrqpeRMzCR1pmVbh+QhnwIMAdWq9PAVTTn
+ypusoEsG8Y5fx8xhgjs0D2yMcrmi0L0kCgHIFNoym+4pI+sv6GgxpemfrmaPNcMx
+DXi9JpaquFRJLGJ7jMCDgotL
+-----END PRIVATE KEY-----
+]]
+
--- Map of all services that can be run as servers
-- g_Services[ServiceName] = function() -> accept-callbacks
local g_Services =
@@ -66,7 +122,7 @@ local g_Services =
return
{
OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
- LOG("FortuneServer(" .. a_Port .. ": Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ LOG("FortuneServer(" .. a_Port .. "): Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
end,
OnReceivedData = function (a_Link, a_Data)
@@ -86,11 +142,55 @@ local g_Services =
-- There was an error listening on the port:
OnError = function (a_ErrorCode, a_ErrorMsg)
- LOGINFO("FortuneServer(" .. a_Port .. ": Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ LOGINFO("FortuneServer(" .. a_Port .. "): Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
end, -- OnError()
} -- Listen callbacks
end, -- fortune
+ -- HTTPS time - serves current time for each https request received
+ httpstime = function (a_Port)
+ return
+ {
+ -- A new connection has come, give it new link-callbacks:
+ OnIncomingConnection = function (a_RemoteIP, a_RemotePort)
+ local IncomingData = "" -- accumulator for the incoming data, until processed by the http
+ return
+ {
+ OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
+ LOG("https-time server(" .. a_Port .. "): Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end,
+
+ OnReceivedData = function (a_Link, a_Data)
+ IncomingData = IncomingData .. a_Data
+ if (IncomingData:find("\r\n\r\n")) then
+ local Content = os.date()
+ a_Link:Send("HTTP/1.0 200 OK\r\nContent-type: text/plain\r\nContent-length: " .. #Content .. "\r\n\r\n" .. Content)
+ -- TODO: shutdown is not yet properly implemented in cTCPLink
+ -- a_Link:Shutdown()
+ end
+ end,
+
+ OnRemoteClosed = function (a_Link)
+ end
+ } -- Link callbacks
+ end, -- OnIncomingConnection()
+
+ -- Start TLS on the new link:
+ OnAccepted = function (a_Link)
+ local res, msg = a_Link:StartTLSServer(g_HTTPSCert, g_HTTPSPrivKey, "")
+ if not(res) then
+ LOG("https-time server(" .. a_Port .. "): Cannot start TLS server: " .. msg)
+ a_Link:Close()
+ end
+ end, -- OnAccepted()
+
+ -- There was an error listening on the port:
+ OnError = function (a_ErrorCode, a_ErrorMsg)
+ LOGINFO("https-time server(" .. a_Port .. "): Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end, -- OnError()
+ } -- Listen callbacks
+ end, -- httpstime
+
-- TODO: Other services (daytime, ...)
}
@@ -229,7 +329,7 @@ function HandleConsoleNetListen(a_Split)
-- Get the params:
local Port = tonumber(a_Split[3] or 1024)
if not(Port) then
- return true, "Invalid port: \"" .. Port .. "\"."
+ return true, "Invalid port: \"" .. a_Split[3] .. "\"."
end
local Service = string.lower(a_Split[4] or "echo")
diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp
index c533456ad..40371d6da 100644
--- a/src/Bindings/LuaTCPLink.cpp
+++ b/src/Bindings/LuaTCPLink.cpp
@@ -160,10 +160,14 @@ void cLuaTCPLink::Shutdown(void)
cTCPLinkPtr Link = m_Link;
if (Link != nullptr)
{
+ if (m_SslContext != nullptr)
+ {
+ m_SslContext->NotifyClose();
+ m_SslContext->ResetSelf();
+ m_SslContext.reset();
+ }
Link->Shutdown();
}
-
- Terminated();
}
@@ -176,6 +180,12 @@ void cLuaTCPLink::Close(void)
cTCPLinkPtr Link = m_Link;
if (Link != nullptr)
{
+ if (m_SslContext != nullptr)
+ {
+ m_SslContext->NotifyClose();
+ m_SslContext->ResetSelf();
+ m_SslContext.reset();
+ }
Link->Close();
}
@@ -228,6 +238,58 @@ AString cLuaTCPLink::StartTLSClient(
}
m_SslContext->SetOwnCert(OwnCert, OwnPrivKey);
}
+ m_SslContext->SetSelf(cLinkSslContextWPtr(m_SslContext));
+
+ // Start the handshake:
+ m_SslContext->Handshake();
+ return "";
+}
+
+
+
+
+
+AString cLuaTCPLink::StartTLSServer(
+ const AString & a_OwnCertData,
+ const AString & a_OwnPrivKeyData,
+ const AString & a_OwnPrivKeyPassword,
+ const AString & a_StartTLSData
+)
+{
+ // Check preconditions:
+ if (m_SslContext != nullptr)
+ {
+ return "TLS is already active on this link";
+ }
+ if (a_OwnCertData.empty() || a_OwnPrivKeyData.empty())
+ {
+ return "Provide the server certificate and private key";
+ }
+
+ // Create the SSL context:
+ m_SslContext.reset(new cLinkSslContext(*this));
+ m_SslContext->Initialize(false);
+
+ // Create the peer cert:
+ auto OwnCert = std::make_shared();
+ int res = OwnCert->Parse(a_OwnCertData.data(), a_OwnCertData.size());
+ if (res != 0)
+ {
+ m_SslContext.reset();
+ return Printf("Cannot parse server certificate: -0x%x", res);
+ }
+ auto OwnPrivKey = std::make_shared();
+ res = OwnPrivKey->ParsePrivate(a_OwnPrivKeyData.data(), a_OwnPrivKeyData.size(), a_OwnPrivKeyPassword);
+ if (res != 0)
+ {
+ m_SslContext.reset();
+ return Printf("Cannot parse server private key: -0x%x", res);
+ }
+ m_SslContext->SetOwnCert(OwnCert, OwnPrivKey);
+ m_SslContext->SetSelf(cLinkSslContextWPtr(m_SslContext));
+
+ // Push the initial data:
+ m_SslContext->StoreReceivedData(a_StartTLSData.data(), a_StartTLSData.size());
// Start the handshake:
m_SslContext->Handshake();
@@ -254,12 +316,17 @@ void cLuaTCPLink::Terminated(void)
}
// If the link is still open, close it:
- cTCPLinkPtr Link = m_Link;
- if (Link != nullptr)
{
- Link->Close();
- m_Link.reset();
+ cTCPLinkPtr Link = m_Link;
+ if (Link != nullptr)
+ {
+ Link->Close();
+ m_Link.reset();
+ }
}
+
+ // If the SSL context still exists, free it:
+ m_SslContext.reset();
}
@@ -401,8 +468,29 @@ cLuaTCPLink::cLinkSslContext::cLinkSslContext(cLuaTCPLink & a_Link):
+void cLuaTCPLink::cLinkSslContext::SetSelf(cLinkSslContextWPtr & a_Self)
+{
+ m_Self = a_Self;
+}
+
+
+
+
+
+void cLuaTCPLink::cLinkSslContext::ResetSelf(void)
+{
+ m_Self.reset();
+}
+
+
+
+
+
void cLuaTCPLink::cLinkSslContext::StoreReceivedData(const char * a_Data, size_t a_NumBytes)
{
+ // Hold self alive for the duration of this function
+ cLinkSslContextPtr Self(m_Self);
+
m_EncryptedData.append(a_Data, a_NumBytes);
// Try to finish a pending handshake:
@@ -418,6 +506,9 @@ void cLuaTCPLink::cLinkSslContext::StoreReceivedData(const char * a_Data, size_t
void cLuaTCPLink::cLinkSslContext::FlushBuffers(void)
{
+ // Hold self alive for the duration of this function
+ cLinkSslContextPtr Self(m_Self);
+
// If the handshake didn't complete yet, bail out:
if (!HasHandshaken())
{
@@ -429,6 +520,11 @@ void cLuaTCPLink::cLinkSslContext::FlushBuffers(void)
while ((NumBytes = ReadPlain(Buffer, sizeof(Buffer))) > 0)
{
m_Link.ReceivedCleartextData(Buffer, static_cast(NumBytes));
+ if (m_Self.expired())
+ {
+ // The callback closed the SSL context, bail out
+ return;
+ }
}
}
@@ -438,6 +534,9 @@ void cLuaTCPLink::cLinkSslContext::FlushBuffers(void)
void cLuaTCPLink::cLinkSslContext::TryFinishHandshaking(void)
{
+ // Hold self alive for the duration of this function
+ cLinkSslContextPtr Self(m_Self);
+
// If the handshake hasn't finished yet, retry:
if (!HasHandshaken())
{
@@ -458,6 +557,9 @@ void cLuaTCPLink::cLinkSslContext::TryFinishHandshaking(void)
void cLuaTCPLink::cLinkSslContext::Send(const AString & a_Data)
{
+ // Hold self alive for the duration of this function
+ cLinkSslContextPtr Self(m_Self);
+
// If the handshake hasn't completed yet, queue the data:
if (!HasHandshaken())
{
@@ -477,6 +579,9 @@ void cLuaTCPLink::cLinkSslContext::Send(const AString & a_Data)
int cLuaTCPLink::cLinkSslContext::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes)
{
+ // Hold self alive for the duration of this function
+ cLinkSslContextPtr Self(m_Self);
+
// If there's nothing queued in the buffer, report empty buffer:
if (m_EncryptedData.empty())
{
diff --git a/src/Bindings/LuaTCPLink.h b/src/Bindings/LuaTCPLink.h
index 9536c052b..4e0d7dcec 100644
--- a/src/Bindings/LuaTCPLink.h
+++ b/src/Bindings/LuaTCPLink.h
@@ -74,7 +74,27 @@ public:
const AString & a_OwnPrivKeyPassword
);
+ /** Starts a TLS handshake as a server connection.
+ Set the server certificate into a_CertData and its corresponding private key to a_OwnPrivKeyData.
+ a_OwnPrivKeyPassword is the password to be used for decoding PrivKey, empty if not passworded.
+ a_StartTLSData is any data that should be pushed into the TLS before reading more data from the remote.
+ This is used mainly for protocols starting TLS in the middle of communication, when the TLS start command
+ can be received together with the TLS Client Hello message in one OnReceivedData() call, to re-queue the
+ Client Hello message into the TLS handshake buffer.
+ Returns empty string on success, non-empty error description on failure. */
+ AString StartTLSServer(
+ const AString & a_OwnCertData,
+ const AString & a_OwnPrivKeyData,
+ const AString & a_OwnPrivKeyPassword,
+ const AString & a_StartTLSData
+ );
+
protected:
+ // fwd:
+ class cLinkSslContext;
+ typedef SharedPtr cLinkSslContextPtr;
+ typedef WeakPtr cLinkSslContextWPtr;
+
/** Wrapper around cSslContext that is used when this link is being encrypted by SSL. */
class cLinkSslContext :
public cSslContext
@@ -87,9 +107,18 @@ protected:
/** Buffer for storing the outgoing cleartext data until the link has finished handshaking. */
AString m_CleartextData;
+ /** Shared ownership of self, so that this object can keep itself alive for as long as it needs. */
+ cLinkSslContextWPtr m_Self;
+
public:
cLinkSslContext(cLuaTCPLink & a_Link);
+ /** Shares ownership of self, so that this object can keep itself alive for as long as it needs. */
+ void SetSelf(cLinkSslContextWPtr & a_Self);
+
+ /** Removes the self ownership so that we can detect the SSL closure. */
+ void ResetSelf(void);
+
/** Stores the specified block of data into the buffer of the data to be decrypted (incoming from remote).
Also flushes the SSL buffers by attempting to read any data through the SSL context. */
void StoreReceivedData(const char * a_Data, size_t a_NumBytes);
@@ -125,7 +154,7 @@ protected:
/** The SSL context used for encryption, if this link uses SSL.
If valid, the link uses encryption through this context. */
- UniquePtr m_SslContext;
+ cLinkSslContextPtr m_SslContext;
/** Common code called when the link is considered as terminated.
diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp
index 4a6b7bc0e..30a34815c 100644
--- a/src/Bindings/ManualBindings_Network.cpp
+++ b/src/Bindings/ManualBindings_Network.cpp
@@ -502,6 +502,52 @@ static int tolua_cTCPLink_StartTLSClient(lua_State * L)
+/** Binds cLuaTCPLink::StartTLSServer */
+static int tolua_cTCPLink_StartTLSServer(lua_State * L)
+{
+ // Function signature:
+ // LinkInstance:StartTLSServer(OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData) -> [true] or [nil, ErrMsg]
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserType(1, "cTCPLink") ||
+ !S.CheckParamString(2, 4) ||
+ // Param 5 is optional, don't check
+ !S.CheckParamEnd(6)
+ )
+ {
+ return 0;
+ }
+
+ // Get the link:
+ cLuaTCPLink * Link;
+ if (lua_isnil(L, 1))
+ {
+ LOGWARNING("cTCPLink:StartTLSServer(): invalid link object. Stack trace:");
+ S.LogStackTrace();
+ return 0;
+ }
+ Link = *static_cast(lua_touserdata(L, 1));
+
+ // Read the params:
+ AString OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData;
+ S.GetStackValues(2, OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData);
+
+ // Start the TLS handshake:
+ AString res = Link->StartTLSServer(OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData);
+ if (!res.empty())
+ {
+ S.PushNil();
+ S.Push(Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str()));
+ return 2;
+ }
+ return 1;
+}
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cServerHandle bindings (routed through cLuaServerHandle):
@@ -616,6 +662,7 @@ void ManualBindings::BindNetwork(lua_State * tolua_S)
tolua_function(tolua_S, "Send", tolua_cTCPLink_Send);
tolua_function(tolua_S, "Shutdown", tolua_cTCPLink_Shutdown);
tolua_function(tolua_S, "StartTLSClient", tolua_cTCPLink_StartTLSClient);
+ tolua_function(tolua_S, "StartTLSServer", tolua_cTCPLink_StartTLSServer);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cServerHandle");
From 1f3d11de32e36dfc983393266f105b969d8dfef8 Mon Sep 17 00:00:00 2001
From: Mattes D
Date: Sat, 14 Feb 2015 09:17:26 +0100
Subject: [PATCH 25/95] Fixed Linux compilation.
---
src/Bindings/LuaTCPLink.cpp | 2 +-
src/Bindings/LuaTCPLink.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp
index 40371d6da..d88c41120 100644
--- a/src/Bindings/LuaTCPLink.cpp
+++ b/src/Bindings/LuaTCPLink.cpp
@@ -468,7 +468,7 @@ cLuaTCPLink::cLinkSslContext::cLinkSslContext(cLuaTCPLink & a_Link):
-void cLuaTCPLink::cLinkSslContext::SetSelf(cLinkSslContextWPtr & a_Self)
+void cLuaTCPLink::cLinkSslContext::SetSelf(cLinkSslContextWPtr a_Self)
{
m_Self = a_Self;
}
diff --git a/src/Bindings/LuaTCPLink.h b/src/Bindings/LuaTCPLink.h
index 4e0d7dcec..c8ae776fe 100644
--- a/src/Bindings/LuaTCPLink.h
+++ b/src/Bindings/LuaTCPLink.h
@@ -114,7 +114,7 @@ protected:
cLinkSslContext(cLuaTCPLink & a_Link);
/** Shares ownership of self, so that this object can keep itself alive for as long as it needs. */
- void SetSelf(cLinkSslContextWPtr & a_Self);
+ void SetSelf(cLinkSslContextWPtr a_Self);
/** Removes the self ownership so that we can detect the SSL closure. */
void ResetSelf(void);
From 1ca0a4915ece26d5700808320496227650819a9b Mon Sep 17 00:00:00 2001
From: Mattes D
Date: Sat, 14 Feb 2015 13:31:31 +0100
Subject: [PATCH 26/95] SslContext: Turned debug messages off.
---
src/PolarSSL++/SslContext.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/PolarSSL++/SslContext.cpp b/src/PolarSSL++/SslContext.cpp
index 8ab207df6..5ac4bc227 100644
--- a/src/PolarSSL++/SslContext.cpp
+++ b/src/PolarSSL++/SslContext.cpp
@@ -70,7 +70,7 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr &
// These functions allow us to debug SSL and certificate problems, but produce way too much output,
// so they're disabled until someone needs them
ssl_set_dbg(&m_Ssl, &SSLDebugMessage, this);
- debug_set_threshold(4);
+ debug_set_threshold(2);
ssl_set_verify(&m_Ssl, &SSLVerifyCert, this);
//*/
From d336a3ea9e581372e225ee64113fe7fd7e080d45 Mon Sep 17 00:00:00 2001
From: Mattes D
Date: Sat, 14 Feb 2015 13:55:54 +0100
Subject: [PATCH 27/95] Fixed TCP link shutdown.
The shutdown is postponed until there's no more outgoing data in the LibEvent buffers.
---
MCServer/Plugins/NetworkTest/NetworkTest.lua | 5 +-
src/OSSupport/TCPLinkImpl.cpp | 65 +++++++++++++++++---
src/OSSupport/TCPLinkImpl.h | 12 ++++
3 files changed, 71 insertions(+), 11 deletions(-)
diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua
index 251e29884..39774f403 100644
--- a/MCServer/Plugins/NetworkTest/NetworkTest.lua
+++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua
@@ -163,14 +163,15 @@ local g_Services =
OnReceivedData = function (a_Link, a_Data)
IncomingData = IncomingData .. a_Data
if (IncomingData:find("\r\n\r\n")) then
+ -- We have received the entire request headers, just send the response and shutdown the link:
local Content = os.date()
a_Link:Send("HTTP/1.0 200 OK\r\nContent-type: text/plain\r\nContent-length: " .. #Content .. "\r\n\r\n" .. Content)
- -- TODO: shutdown is not yet properly implemented in cTCPLink
- -- a_Link:Shutdown()
+ a_Link:Shutdown()
end
end,
OnRemoteClosed = function (a_Link)
+ LOG("httpstime: link closed by remote")
end
} -- Link callbacks
end, -- OnIncomingConnection()
diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp
index 88fb57838..c6f1978ad 100644
--- a/src/OSSupport/TCPLinkImpl.cpp
+++ b/src/OSSupport/TCPLinkImpl.cpp
@@ -7,6 +7,7 @@
#include "TCPLinkImpl.h"
#include "NetworkSingleton.h"
#include "ServerHandleImpl.h"
+#include "event2/buffer.h"
@@ -17,7 +18,10 @@
cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks):
super(a_LinkCallbacks),
- m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE))
+ m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE)),
+ m_LocalPort(0),
+ m_RemotePort(0),
+ m_ShouldShutdown(false)
{
LOGD("Created new cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent);
}
@@ -29,7 +33,10 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks):
cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImplPtr a_Server, const sockaddr * a_Address, socklen_t a_AddrLen):
super(a_LinkCallbacks),
m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), a_Socket, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE)),
- m_Server(a_Server)
+ m_Server(a_Server),
+ m_LocalPort(0),
+ m_RemotePort(0),
+ m_ShouldShutdown(false)
{
LOGD("Created new cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent);
@@ -111,7 +118,7 @@ void cTCPLinkImpl::Enable(cTCPLinkImplPtr a_Self)
m_Self = a_Self;
// Set the LibEvent callbacks and enable processing:
- bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this);
+ bufferevent_setcb(m_BufferEvent, ReadCallback, WriteCallback, EventCallback, this);
bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE);
}
@@ -121,6 +128,11 @@ void cTCPLinkImpl::Enable(cTCPLinkImplPtr a_Self)
bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length)
{
+ if (m_ShouldShutdown)
+ {
+ LOGD("%s: Cannot send data, the link is already shut down.", __FUNCTION__);
+ return false;
+ }
return (bufferevent_write(m_BufferEvent, a_Data, a_Length) == 0);
}
@@ -130,12 +142,15 @@ bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length)
void cTCPLinkImpl::Shutdown(void)
{
- #ifdef _WIN32
- shutdown(bufferevent_getfd(m_BufferEvent), SD_SEND);
- #else
- shutdown(bufferevent_getfd(m_BufferEvent), SHUT_WR);
- #endif
- bufferevent_disable(m_BufferEvent, EV_WRITE);
+ // If there's no outgoing data, shutdown the socket directly:
+ if (evbuffer_get_length(bufferevent_get_output(m_BufferEvent)) == 0)
+ {
+ DoActualShutdown();
+ return;
+ }
+
+ // There's still outgoing data in the LibEvent buffer, schedule a shutdown when it's written to OS's TCP stack:
+ m_ShouldShutdown = true;
}
@@ -181,6 +196,24 @@ void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self)
+void cTCPLinkImpl::WriteCallback(bufferevent * a_BufferEvent, void * a_Self)
+{
+ ASSERT(a_Self != nullptr);
+ auto Self = static_cast(a_Self);
+ ASSERT(Self->m_Callbacks != nullptr);
+
+ // If there's no more data to write and the link has been scheduled for shutdown, do the shutdown:
+ auto OutLen = evbuffer_get_length(bufferevent_get_output(Self->m_BufferEvent));
+ if ((OutLen == 0) && (Self->m_ShouldShutdown))
+ {
+ Self->DoActualShutdown();
+ }
+}
+
+
+
+
+
void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self)
{
LOGD("cTCPLink event callback for link %p, BEV %p; what = 0x%02x", a_Self, a_BufferEvent, a_What);
@@ -316,6 +349,20 @@ void cTCPLinkImpl::UpdateRemoteAddress(void)
+void cTCPLinkImpl::DoActualShutdown(void)
+{
+ #ifdef _WIN32
+ shutdown(bufferevent_getfd(m_BufferEvent), SD_SEND);
+ #else
+ shutdown(bufferevent_getfd(m_BufferEvent), SHUT_WR);
+ #endif
+ bufferevent_disable(m_BufferEvent, EV_WRITE);
+}
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cNetwork API:
diff --git a/src/OSSupport/TCPLinkImpl.h b/src/OSSupport/TCPLinkImpl.h
index 735e8ed9d..bea21aeff 100644
--- a/src/OSSupport/TCPLinkImpl.h
+++ b/src/OSSupport/TCPLinkImpl.h
@@ -94,6 +94,11 @@ protected:
Initialized in Enable(), cleared in Close() and EventCallback(RemoteClosed). */
cTCPLinkImplPtr m_Self;
+ /** If true, Shutdown() has been called and is in queue.
+ No more data is allowed to be sent via Send() and after all the currently buffered
+ data is sent to the OS TCP stack, the socket gets shut down. */
+ bool m_ShouldShutdown;
+
/** Creates a new link to be queued to connect to a specified host:port.
Used for outgoing connections created using cNetwork::Connect().
@@ -104,6 +109,9 @@ protected:
/** Callback that LibEvent calls when there's data available from the remote peer. */
static void ReadCallback(bufferevent * a_BufferEvent, void * a_Self);
+ /** Callback that LibEvent calls when the remote peer can receive more data. */
+ static void WriteCallback(bufferevent * a_BufferEvent, void * a_Self);
+
/** Callback that LibEvent calls when there's a non-data-related event on the socket. */
static void EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self);
@@ -115,6 +123,10 @@ protected:
/** Updates m_RemoteIP and m_RemotePort based on the metadata read from the socket. */
void UpdateRemoteAddress(void);
+
+ /** Calls shutdown on the link and disables LibEvent writing.
+ Called after all data from LibEvent buffers is sent to the OS TCP stack and shutdown() has been called before. */
+ void DoActualShutdown(void);
};
From d2fdaf1d5bf431bf5745f0b18cc8cb18ee2c855d Mon Sep 17 00:00:00 2001
From: Creaprog
Date: Sun, 15 Feb 2015 13:08:44 +0100
Subject: [PATCH 28/95] Fixed login_template.html
Remove "/".
---
MCServer/webadmin/login_template.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MCServer/webadmin/login_template.html b/MCServer/webadmin/login_template.html
index 7a8601065..26b260bd3 100644
--- a/MCServer/webadmin/login_template.html
+++ b/MCServer/webadmin/login_template.html
@@ -17,7 +17,7 @@