Search code examples
c++builderindyindy10

How to display messages in every Indy event?


I am working on a C++ Builder application using the Indy library. There are two main objectives:

  1. To perform a forward between a server and a client.
  2. To visualize the traffic and its decoding on a window (not console, windows app)

After implementing the solution described in:

Why do I need to send a message twice to trigger Indy's OnExecute event?

And modifying the solution to my needs, the application started to hang. Apparently, the application works well until I want to stop the TIdMappedPortTCP component or close the application.

As the application is small so far, I created a new project and started migrating portions of the code to try to identify the reason this is happening.

I was able to reproduce the undesired behaviour by just "printing" messages during Indy's events.

The OnExecute event has not been implemented yet (in the new project) which means that TCP data is flowing both ways.

This is my sync class implementation:

// TTextToDisplay.cpp
TTextToDisplay::TTextToDisplay() {
    lineToAdd = NULL;
    stringsToAdd = NULL;
}

TTextToDisplay::TTextToDisplay(String str) {
    lineToAdd = str;
    stringsToAdd = NULL;
}

void __fastcall TTextToDisplay::AddSingleLine(String str)
{
    lineToAdd = str;
}

void __fastcall TTextToDisplay::AddStringList(TStringList* strings)
{
    stringsToAdd = strings;
}

void __fastcall TTextToDisplay::DoSynchronize(){
            // Use the input parameters here...
        if (stringsToAdd)
            Form1->Display->Lines->AddStrings(stringsToAdd);
        else if (lineToAdd != NULL) {
            Form1->Display->Lines->Add(lineToAdd);
        }
}

Where Display is a TRichEdit control, lineToAdd is a String object and stringsToAdd is a TStringList.

Indy manages several events and I would like to add a text on each of them (if it's not forbidden by design or any other restriction).

I added this event:

void __fastcall TForm1::MITMProxyBeforeConnect(TIdContext *AContext)
{
    String tempStr;

    // displaying remote address.
    tempStr = "Received connection from " +
        AContext->Connection->Socket->Binding->PeerIP;
    TTextToDisplay *TextToDisplay = new TTextToDisplay(tempStr);
    TextToDisplay->Synchronize();
    delete TextToDisplay;
}

The application was working fine. Then I added this:

void __fastcall TForm1::MITMProxyDisconnect(TIdContext *AContext)
{
    String tempStr;

    // displaying remote address.
    tempStr = "Client disconnected"; TTextToDisplay *TextToDisplay =
        new TTextToDisplay(tempStr); TextToDisplay->Synchronize();
    delete TextToDisplay;
}

The application is still working fine. So I added one more

void __fastcall TForm1::MITMProxyConnect(TIdContext *AContext)
{
    String tempStr;

    // displaying remote address.
    tempStr = "Attempting to connect to the remote server " +
        MITMProxy->MappedHost + ":" + MITMProxy->MappedPort;
    TTextToDisplay *TextToDisplay = new TTextToDisplay(tempStr);
    TextToDisplay->Synchronize();
    delete TextToDisplay;

}

And now the application starts to hang.

Working fine means that I can close the TIdMappedPortTCP

MITMProxy->Active = False;

and reactivate it

MITMProxy->Active = True;

several times, receive some messages and then close the application without it becoming irresponsive.

I would like to keep the application as verbose as possible, so is there a better way to log every Indy event?

The application that I was originally working on was doing great until I add some custom display of the data. Then the problem appeared. I don't know if the cause is related but the behaviour is the same.

So my application instead of showing this string:

008460000000190210703800000EC00000164593560001791662000000000000080000000002104302040235313531353135313531353153414C4535313030313233343536373831323334353637383930313233

Now shows:

0000: 60 00 00 00 19 02 10 70 38 00 00 0E C0 00 00 16 
0010: 45 93 56 00 01 79 16 62 00 00 00 00 00 00 08 00 
0020: 00 00 00 02 10 43 02 04 02 35 31 35 31 35 31 35 
0030: 31 35 31 35 31 53 41 4C 45 35 31 30 30 31 32 33 
0040: 34 35 36 37 38 31 32 33 34 35 36 37 38 39 30 31 
0050: 32 33 

This is a TStringList because it's easier for me to add all those lines to the TRichEdit at once. I want a second TRichEdit to show the decoded message.

To give you an idea it would be something like this:

000 MsgType              : "0200"
001 BitMap               : "70 24 06 80 20 C0 06 10"
002 PAN                  : "4593560001791662"
003 ProcessingCode       : "000000"
004 TxnAmount            : "000000080000"
011 SystemTraceNo        : "000001"
014 ExpirationDate       : "2411"
022 POSEntryMode         : "520"
023 CardSequenceNo       : "000"
025 POSConditionCode     : "00"
035 Track2               : "4593560001791662=24111190000063900000"
041 TerminalID           : "00064600"
042 AcquirerID           : "000010585800001"
054 AddAmounts           : "0"
055 Field55              : "9F 26 08 35 C2 C4 DF B5 FC 7B 0E 9F 27 01 80 9F 10 07 06 01 0A 03 A0 B8 03 9F 37 04 C1 5C 4B 3B 9F 36 02 01 3A 95 05 00 80 00 80 00 9A 03 22 04 02 9C 01 00 9F 02"
060 Field60              : "00 00 08"
--------------------------------------------------------------
Field 55 by Tag:
9F26 AppCryptogram        : "35 C2 C4 DF B5 FC 7B 0E"
9F27 CryptogramInfoData   : "80"
9F10 IssuerAppData        : "06 01 0A 03 A0 B8 03"
9F37 UnpredictableNo      : "C1 5C 4B 3B"
9F36 AppTxnCounter        : "01 3A"
95   TermVerifResults     : "00 80 00 80 00"
9A   TxnDate              : "220402"
9C   TxnType              : "00"
9F02 AmountAuthNum        : "20"

A final consideration, I know apps that do the forwarding, and I know applications that decode, but I haven't seen apps that do both and that is why I want to create it. But, so far the hardest part has been the visualization. So I don't know if I am using the right tools or the right approach to build what I want. Any advice will be highly appreciated.


Solution

  • Indy servers are multi-threaded, and serrver shutdown is synchronous - it waits for the server's threads to fully terminate before returning to the caller.

    TIdSync (and TThread::Synchronize() and equivalent) delegate to the main UI thread, blocking the calling thread until the main thread finishes processing the sync request.

    So, you can easily get into a deadlock situation if you Synchronize with the main thread while the main thread is shutting down the server. The main thread will be waiting on the server's threads, while a syncing thread will be waiting on the main thread.

    So, your options are to either:

    • Simply don't Synchronize at all during server shutdown. Set a flag somewhere before starting the shutdown, and then look at that flag before performing a sync. Although, this won't catch the scenario where the shutdown begins after a sync is already in flight.

    • Don't perform a synchronous sync, perform an asynchronous sync instead (TIdNotify, TThread::Queue(), or equivalent). There is no good reason to block your server threads waiting for the main thread to perform UI status updates.

    • Shutdown the server in a separate thread, leaving the main thread free to process sync requests.