Jump to content
JeanCremers

update indy to use tls 1.2 in c++builder 6?

Recommended Posts

Hi Folks,

Is there a version of indy which can be installed on borland c++builder 6 that can use a newer protocol version, and the newer ssleay32.dll and libeay32.dll?

I get a protocol error with the old dll and the newer from https://indy.fulgan.com/SSL/. refuse to load.

 

Share this post


Link to post

The latest version of Indy 10 (https://github.com/IndySockets/Indy/) supports back to C++Builder 5, and can officially use OpenSSL DLLs up to 1.0.2u (for OpenSSL 1.1.x+, use this WIP code).  TLS 1.2 has been available in OpenSSL since 1.0.1, and Indy 10 has supported TLS 1.2 for a long time.  OpenSSL DLLs are available at https://github.com/IndySockets/OpenSSL-Binaries (https://indy.fulgan.com/SSL/ is retired).

 

If you are getting errors from Indy's standard TIdSSLIOHandlerSocketOpenSSL component when it tries to load the DLLs, then you are likely using DLLs that are not compatible with your version of Indy.  You can call Indy's WhichFailedToLoad() function in the IdSSLOpenSSLHeaders unit to find out why the DLLs are failing to load.

Edited by Remy Lebeau
  • Like 2

Share this post


Link to post

Thanks Remy,

WhichFailedToLoad reported ssleay32.dll, the newer one, but it also reports NULL when i try some other dll's.

I looked at the WIP code but i don't feel like trying to use it, i'm afraid that i will break something. For now http still works, i'm using indy so my prog can download additional files / updates from my provider, this still works. Another prog i had refuses since awhile, i guess that website had some ssl update. So here's hoping http will keep working...

ps what's up with all the q,r,s,t,u in the names?

Share this post


Link to post
9 hours ago, JeanCremers said:

WhichFailedToLoad reported ssleay32.dll, the newer one, but it also reports NULL when i try some other dll's.

That means the DLL itself failed to load into memory, before Indy could even try to access any of its functions.  Such a failure is most commonly because the DLL itself, or one of its dependent DLLs or imports, can't be found by the OS.

9 hours ago, JeanCremers said:

ps what's up with all the q,r,s,t,u in the names?

OpenSSL build numbers are represented as letters in the English alphabet.  So, 1.0.2q is build 1.0.2.17, 1.0.2u is build 1.0.2.21, etc.

Share this post


Link to post

Remy I'm trying to make a dll in c++builder 10.3 to use that Indy version with my application made in c++builder 6.

Is there a way to export OnWork(), OnWorkBegin(), OnWorkEnd()? How should those functions be declared, Something with TNotifyEvent?

Thanks!

 


 

#define EXPORT extern "C" __declspec(dllexport)

 

EXPORT int getdata(char *url, char *data, ???onwork???)
{
TIdHTTP *http = new TIdHTTP;
http->OnWork = onwork; // ??????????????????????????????

TIdSSLIOHandlerSocketOpenSSL *SSL;
SSL = new TIdSSLIOHandlerSocketOpenSSL(NULL);
IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0)));
SSL->SSLOptions->Method = sslvTLSv1_2;
http->IOHandler = SSL;
http->ReadTimeout = 10000;
String s = http->Get(url);
memcpy(data, s.c_str(), s.Length());
}


 

 

Btw i was getting an error about strcpy() not found, for now i'm using memcpy, probably to do with widechar, still haven't got into unicode that much because of my c++builder 6 code.

Edited by JeanCremers

Share this post


Link to post
On 10/18/2021 at 9:05 AM, JeanCremers said:

Remy I'm trying to make a dll in c++builder 10.3 to use that Indy version with my application made in c++builder 6.

Why not just install the latest Indy in C++Builder 6?

Quote

Is there a way to export OnWork(), OnWorkBegin(), OnWorkEnd()?

No, not directly.  Nor should you be trying to.  What you should do instead is let the caller pass in its own function pointer(s), which your DLL then remembers, and can then call from inside its own OnWork event handler(s).

 

For example, the following uses a little-known trick for assigning a standalone procedure as a VCL event handler, while also passing a user-defined parameter to it.  There are other ways this could be implemented, but this is the most straight-forward way I can think of right now for your scenario:

// HEADER

#ifndef MY_DLL_H
#define MY_DLL_H

#ifdef __cplusplus
extern "C" {
#endif

#ifdef BUILDING_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif

enum OnWorkMode { WorkReading, WorkWriting };

typedef void (*OnWorkBeginFunc)(OnWorkMode AWorkMode, __int64 AWorkCountMax);
typedef void (*OnWorkEndFunc)(OnWorkMode AWorkMode);
typedef void (*OnWorkFunc)(OnWorkMode AWorkMode, __int64 AWorkCount);

EXPORT char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork);
EXPORT void freedata(char *data);

#ifdef __cplusplus
}
#endif

#endif
// SOURCE

#define BUILDING_DLL
#include "mydll.h"

static void __fastcall MyOnWorkBeginHandler(void *ASelf, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax)
{
	OnWorkBeginFunc func = static_cast<OnWorkBeginFunc*>(ASelf);
	func(AWorkMode, AWorkCountMax);
}

static void __fastcall MyOnWorkEndHandler(void *ASelf, TObject *ASender, TWorkMode AWorkMode)
{
	OnWorkEndFunc *func = static_cast<OnWorkEndFunc*>(ASelf);
	func(AWorkMode);
}

static void __fastcall MyOnWorkHandler(void *ASelf, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount)
{
	OnWorkFunc *func = static_cast<OnWorkFunc*>(ASelf);
	func(AWorkMode, AWorkCount);
}

template <typename EventType, typename FuncType, typename HandlerType>
static EventType makeEventHandler(FuncType *func, HandlerType *handler)
{
	TMethod m;
	m.Data = func;
	m.Code = handler;
	return reinterpret_cast<EventType&>(m);

	/* alternatively:
	EventType evt;
	TMethod &m = reinterpret_cast<TMethod&>(evt);
	m.Data = func;
	m.Code = handler;
	return evt;
	*/
}

static bool sslPathSet = false;

char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork)
{
	char *result = NULL;

	try
	{
		if (!sslPathSet) {
			sslPathSet = true;
			IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0)));
		}

		TIdHTTP *http = new TIdHTTP;
		try
		{
			if (onWorkBegin)
				http->OnWorkBegin = makeEventHandler<TWorkBeginEvent>(onWorkBegin, &MyOnWorkBeginHandler);

			if (onWorkEnd)
				http->OnWorkEnd = makeEventHandler<TWorkEndEvent>(onWorkEnd, &MyOnWorkEndHandler);

			if (onWork)
				http->OnWork = makeEventHandler<TWorkEvent>(onWork, &MyOnWorkHandler);

			TIdSSLIOHandlerSocketOpenSSL *SSL = new TIdSSLIOHandlerSocketOpenSSL(http);
			SSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2;
			http->IOHandler = SSL;
			http->ReadTimeout = 10000;

			UTF8String s = http->Get(url);

			result = new char[s.Length()+1];
			memcpy(result, s.c_str(), s.Length()+1);
		}
		__finally
		{
			delete http;
		}
	}
	catch (...)
	{
		return NULL;
	}

	return result;
}

void freedata(char *data)
{
	delete[] data;
}

And then you C++Builder 6 code can use it like this:

#include "mydll.h"
#pragma comment(lib, "mydll.lib") // or add the .lib file to your project

void OnWorkBegin(OnWorkMode AWorkMode, __int64 AWorkCountMax)
{
	//...
}

void OnWorkEnd(OnWorkMode AWorkMode)
{
	//...
}

void OnWork(OnWorkMode AWorkMode, __int64 AWorkCount)
{
	//...
}

char *data = getdata("url", &OnWorkBegin, &OnWorkEnd, &OnWork);
if (data) {
	// ...
	freedata(data);
}
Quote

Btw i was getting an error about strcpy() not found, for now i'm using memcpy, probably to do with widechar

Yes, because System::String is an alias for System::UnicodeString in CB10.3, but is an alias for System::AnsiString in BCB6.  Thus, you were indeed trying to pass a wchar_t* pointer to strcpy() rather than a char* pointer.

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post

Wow Remy, thanks a bunch, gonna work with it.

The reason why i didn't want to use that WIP code is because i'm afraid to break things.

Share this post


Link to post

Hi Remy,

Getting errors on the lines in the three functions, void(*) vs void(**):


OnWorkBeginFunc func = static_cast<OnWorkBeginFunc*>(ASelf);

 

[bcc32c Error] main.cpp(23): cannot initialize a variable of type 'OnWorkBeginFunc' (aka 'void (*)(OnWorkMode, long long)') with an rvalue of type 'OnWorkBeginFunc *' (aka 'void (**)(OnWorkMode, long long)')

Share this post


Link to post

My bad, too many *s in the DLL's event handlers.  Try this instead:

static void __fastcall MyOnWorkBeginHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax)
{
	OnWorkBeginFunc func = static_cast<OnWorkBeginFunc>(AData);
	func(AWorkMode, AWorkCountMax);
}

static void __fastcall MyOnWorkEndHandler(void *AData, TObject *ASender, TWorkMode AWorkMode)
{
	OnWorkEndFunc func = static_cast<OnWorkEndFunc>(AData);
	func(AWorkMode);
}

static void __fastcall MyOnWorkHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount)
{
	OnWorkFunc func = static_cast<OnWorkFunc>(AData);
	func(AWorkMode, AWorkCount);
}

 

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post

Gives these..

I've got it working nicely without the events though.


[bcc32c Error] main.cpp(23): static_cast from 'void *' to 'OnWorkBeginFunc' (aka 'void (*)(OnWorkMode, long long)') is not allowed
[bcc32c Error] main.cpp(24): cannot initialize a parameter of type 'OnWorkMode' with an lvalue of type 'Idcomponent::TWorkMode'

Edited by JeanCremers

Share this post


Link to post

<sigh> Try this:

// SOURCE

#define BUILDING_DLL
#include "mydll.h"

struct userFunctions
{
	OnWorkBeginFunc onWorkBegin;
	OnWorkEndFunc onWorkEnd;
	OnWorkFunc onWork;
};

static void __fastcall MyOnWorkBeginHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax)
{
	userFunctions *funcs = static_cast<userFunctions*>(AData);
	funcs->onWorkBegin((OnWorkMode)AWorkMode, AWorkCountMax);
}

static void __fastcall MyOnWorkEndHandler(void *AData, TObject *ASender, TWorkMode AWorkMode)
{
	userFunctions *funcs = static_cast<userFunctions*>(AData);
	funcs->onWorkEnd((OnWorkMode)AWorkMode);
}

static void __fastcall MyOnWorkHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount)
{
	userFunctions *funcs = static_cast<userFunctions*>(AData);
	funcs->onWork((OnWorkMode)AWorkMode, AWorkCount);
}

template <typename EventType, typename HandlerType>
static EventType makeEventHandler(void *data, HandlerType *handler)
{
	TMethod m;
	m.Data = data;
	m.Code = handler;
	return reinterpret_cast<EventType&>(m);

	/* alternatively:
	EventType evt;
	TMethod &m = reinterpret_cast<TMethod&>(evt);
	m.Data = data;
	m.Code = handler;
	return evt;
	*/
}

static bool sslPathSet = false;

char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork)
{
	char *result = NULL;

	try
	{
		if (!sslPathSet) {
			sslPathSet = true;
			IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0)));
		}

		TIdHTTP *http = new TIdHTTP;
		try
		{
			userFunctions funcs;
  
			if (onWorkBegin) {
				funcs.onWorkBegin = onWorkBegin;
				http->OnWorkBegin = makeEventHandler<TWorkBeginEvent>(&funcs, &MyOnWorkBeginHandler);
			}

			if (onWorkEnd) {
				funcs.onWorkEnd = onWorkEnd;
				http->OnWorkEnd = makeEventHandler<TWorkEndEvent>(&funcs, &MyOnWorkEndHandler);
			}

			if (onWork) {
				funcs.onWork = onWork;
				http->OnWork = makeEventHandler<TWorkEvent>(&funcs, &MyOnWorkHandler);
			}

			TIdSSLIOHandlerSocketOpenSSL *SSL = new TIdSSLIOHandlerSocketOpenSSL(http);
			SSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2;
			http->IOHandler = SSL;
			http->ReadTimeout = 10000;

			UTF8String s = http->Get(url);

			result = new char[s.Length()+1];
			memcpy(result, s.c_str(), s.Length()+1);
		}
		__finally
		{
			delete http;
		}
	}
	catch (...)
	{
		return NULL;
	}

	return result;
}

void freedata(char *data)
{
	delete[] data;
}
  • Like 1

Share this post


Link to post

Hi Remy,

I got it to compile with warnings

m.Code = handler;
[bcc32c Warning] ssl.cpp(50): implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension

and..

http->OnWorkBegin = makeEventHandler<TWorkBeginEvent>(&funcs, &MyOnWorkBeginHandler);
ssl.cpp(72): in instantiation of function template specialization 'makeEventHandler<void (__closure *)(System::TObject *, Idcomponent::TWorkMode) __attribute__((fastcall)), void (void *, System::TObject *, Idcomponent::TWorkMode) __attribute__((fastcall))>' requested here

 

That last warning i also get on

 http->OnWorkEnd = makeEventHandler<TWorkEndEvent>(&funcs, &MyOnWorkEndHandler); 

But NOT on http->onWork

 

In the test code OnWorkBegin and OnWork are never called. I tried debugging the dll to get an idea what was going on but run to cursor didn't work there, though i used the debug version.

 

If you want we can call it a day? At least I can use ssl now..

 

Some funny thing, i could only get one function to export from the dll, if i tried to export only a small 'void mytest(void)' i got a linker error in the test app, though nirsoft's dll export vieuwer shows them allright.

ps, could it have something to do with indy not living on a form? I tried to assign OnWork etc in the dll to a plain void __fastcall MyOnWork(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) in the dll but got an error about incompatible type.

Edited by JeanCremers

Share this post


Link to post
On 10/21/2021 at 3:04 AM, JeanCremers said:

I got it to compile with warnings

<sigh> Fine, then let's go the long way around, using a full wrapper class, forget TMethod (even though it will technically work at runtime, despite the compiler warnings):

// SOURCE

#define BUILDING_DLL
#include "mydll.h"

class eventHandlers
{
public:
	OnWorkBeginFunc onWorkBegin;
	OnWorkEndFunc onWorkEnd;
	OnWorkFunc onWork;

	void __fastcall OnWorkBeginHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax)
	{
		if (onWorkBegin) onWorkBegin((OnWorkMode)AWorkMode, AWorkCountMax);
	}

	void __fastcall OnWorkEndHandler(TObject *ASender, TWorkMode AWorkMode)
	{
		if (onWorkEnd) onWorkEnd((OnWorkMode)AWorkMode);
	}

	void __fastcall OnWorkHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount)
	{
		if (onWork) onWork((OnWorkMode)AWorkMode, AWorkCount);
	}
};

static bool sslPathSet = false;

char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork)
{
	char *result = NULL;

	try
	{
		if (!sslPathSet) {
			sslPathSet = true;
			IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0)));
		}

		TIdHTTP *http = new TIdHTTP;
		try
		{
			eventHandlers events;
			events.onWorkBegin = onWorkBegin;
			events.onWorkEnd = onWorkEnd;
			events.onWork = onWork;

			http->OnWorkBegin = &events.OnWorkBeginHandler;
			http->OnWorkEnd = &events.OnWorkEndHandler;
			http->OnWork = &events.OnWorkHandler;

			TIdSSLIOHandlerSocketOpenSSL *SSL = new TIdSSLIOHandlerSocketOpenSSL(http);
			SSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2;
			http->IOHandler = SSL;
			http->ReadTimeout = 10000;

			UTF8String s = http->Get(url);

			result = new char[s.Length()+1];
			memcpy(result, s.c_str(), s.Length()+1);
		}
		__finally
		{
			delete http;
		}
	}
	catch (...)
	{
		return NULL;
	}

	return result;
}

void freedata(char *data)
{
	delete[] data;
}
Quote

In the test code OnWorkBegin and OnWork are never called.

Odd, they should be.  Especially since OnWorkEnd can't be called if OnWorkBegin is not called first.

Quote

ps, could it have something to do with indy not living on a form?

No.

Quote

I tried to assign OnWork etc in the dll to a plain void __fastcall MyOnWork(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) in the dll but got an error about incompatible type.

Correct, because the event is expecting a class method, not a standalone procedure.  Using TMethod is the way to get around that.

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post

It's working great Remy. A progressbar on the testapp to show download progress!

Your name already was in the credits and again you contributed to my program, many thanks again.

Good old TeamB, i miss those times, Kent Reisdorph and all.. Does it still exist?

 

ps, 


void __fastcall OnWorkHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount)

should be:

void __fastcall OnWorkHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount)

Edited by JeanCremers

Share this post


Link to post
7 hours ago, JeanCremers said:

Good old TeamB, i miss those times, Kent Reisdorph and all.. Does it still exist?

Not like it used to be.  A few members are still lingering around the various forums.  Several members have transitioned into MVPs, though some members (like me) haven't but are still recognized by a few Embarcadero employees to keep getting old TeamB perks (free versions, beta access, etc).

7 hours ago, JeanCremers said:

 


void __fastcall OnWorkHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount)

should be:

void __fastcall OnWorkHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount)

Thanks, fixed.

  • Like 1

Share this post


Link to post

Proud to say I have been an official borland betatester for a short while, around c++builder 1 that was, even got a thanks for my report.

Till next time 🙂

Edited by JeanCremers

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×