Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Dec 08 20:26
    Bi0T1N opened #273
  • Dec 06 18:14
    rlebeau commented #183
  • Dec 06 18:00
    JPeterMugaas commented #183
  • Dec 06 17:42
    rlebeau commented #183
  • Dec 06 17:42
    rlebeau commented #183
  • Dec 06 17:41

    rlebeau on OpenSSL-1.1.x

    (compare)

  • Dec 06 17:18
    rlebeau commented #270
  • Dec 06 17:17
    jgv-Flexsys commented #183
  • Dec 06 17:10
    rlebeau commented #183
  • Dec 05 15:04
    Fulgan commented #270
  • Dec 05 13:25
    winkelsdorf commented #183
  • Dec 05 13:23
    winkelsdorf commented #183
  • Dec 03 22:30

    rlebeau on master

    Correctly define timezone funct… Merge pull request #272 from Bi… (compare)

  • Dec 03 22:30
    rlebeau closed #272
  • Dec 03 21:15

    rlebeau on master

    Fixing compiler errors in TIdIP… (compare)

  • Dec 03 20:50
    Bi0T1N opened #272
  • Dec 03 01:33

    rlebeau on master

    Fix for missing declaration of … Fix for compiler error in Local… Merge branch 'master' of https:… and 2 more (compare)

  • Dec 02 20:27

    rlebeau on master

    Update IdGlobal.pas Fix for co… (compare)

  • Dec 01 20:17

    rlebeau on master

    Update IdGlobal.pas Fix for mi… (compare)

  • Dec 01 10:46
    geoffsmith82 commented #192
Walter Prins
@ByteJuggler
Here is the modified code:
type TAuth = class
  procedure DoSelectAuthorization(Sender: TObject; var AuthenticationClass: TIdAuthenticationClass; AuthInfo: TIdHeaderList);
  procedure DoProxyAuthorization (Sender: TObject; Authentication: TIdAuthentication; var Handled: Boolean);
  end;

procedure TAuth.DoSelectAuthorization(Sender: TObject; var AuthenticationClass: TIdAuthenticationClass; AuthInfo: TIdHeaderList);
begin
  //It turns out AuthenticateClass is already set to TIdSSPINTLMAuthentication by
  //TIdCustomHTTP.DoOnProxyAuthorization when this event is called.
  //
  //It does this by inspecting AResponse.ProxyAuthenticate
  //list to see what protocols are requested/supported by the proxy and then
  //trying to look up a suitable registered class to use to handle the requirement.
  //
  //In our case this of course contains 'NTLM' which results in TIdSSPINTLMAuthentication
  //being looked up as appropriate class to use.

  //TIdSSPINTLMAuthentication gets looked up because this class was previously
  //automatically registered by idAuthenticationSSPI unit initialization
  //via line 1320 call to RegisterAuthenticationMethod('NTLM', TIdSSPINTLMAuthentication);
  //which registers it into global "AuthList".  It is registered by inclusion in uses clause
  //via IdAuthenticationSSPI (or IdAllAuthentications).

  //Consequently we don't have to provide anything here and I instead just
  //assert here that we're using the expected auth class instead:

  Assert(AuthenticationClass = TIdSSPINTLMAuthentication);
end;

procedure TAuth.DoProxyAuthorization (Sender: TObject; Authentication: TIdAuthentication; var Handled: Boolean);
begin
  //This is never called? (Presumably since not applicable because with SSP (single sign-on protocol)
  //the NTLM response is generated automatically.)
  ShowMessage(Authentication.Authentication);
  Assert(Authentication.Authentication <> '');
end;

procedure TTextMagicTests.test_SSL_proxy_access_to_google_via_NTLMSSP;
var
  LIdHTTP : TIdHTTP;
  LSSLIOHandler : TIdSSLIOHandlerSocketOpenSSL;
  LRequestStr, LResult : String;
  LAuth : TAuth;
begin
  //Using 'https://www.google.com' as LRequestStr results in HTTP 302 from google
  //which seems to result in what ends up looking like a 407 Indy Exception to
  //the Delphi code...
  //
  //Why is this? It seems incorrect behaviour.  Indy should just deal with the
  //redirect, like a browser does faced with the same situation.
  //
  //Probably not the right diagnosis, but currently it seems Indy is seemingly
  //conflating the 302 as the tail-end "error" of proxy auth protocol (maybe?)
  //and is then surfacing the whole lot as the original 407 exception/response.
  //
  //However when using the redirected URL instead, the proxy auth works as expected
  //and no problems occur, and the test passes. So there the SSPINTLM auth works
  //correctly, aside from this specfic situation, it seems.

  LRequestStr := 'https://www.google.co.uk/?gfe_rd=cr&ei=aeJQWc2JM6nHXpaklfgG';

  LIdHTTP := TIdHTTP.Create(nil);
  LAuth := TAuth.Create;
  try
    LSSLIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(LIdHTTP);
    LIdHTTP.IOHandler := LSSLIOHandler;
    LSSLIOHandler.SSLOptions.Method := TIdSSLVersion.sslvTLSv1_2;

    //The following 2 lines are unnecesary as SSPI is used automatically due to
    //registration of TIdSSPINTLMAuthentication as handler for NTLM.
    //The proxy below only supports NTLM as well. They are kept in for context.
    LIdHTTP.OnSelectProxyAuthorization := LAuth.DoSelectAuthorization;
    LIdHTTP.OnProxyAuthorization := LAuth.DoProxyAuthorization;

    LIdHTTP.HandleRedirects := True;
    LIdHTTP.AllowCookies := True;
    LIdHTTP.ConnectTimeout := 10000;
    LIdHTTP.ReadTimeout := 10000;
    LIdHTTP.HTTPOptions := LIdHTTP.HTTPOptions + [hoKeepOrigProtocol] + [hoInProcessAuth];
    LIdHTTP.ProtocolVersion := pv1_1;
    LIdHTTP.ProxyParams.Clear;
    LIdHTTP.ProxyParams.BasicAuthentication := False;
    LIdHTTP.ProxyParams.ProxyServer := 'proxy';
    LIdHTTP.ProxyParams.ProxyPort := 3128;
    try
      LResult := LIdHTTP.Get(LRequestStr);
    except
      on E:EIdHTTPProtocolException do
      begin
        Fail('HTTP Error code: ' + IntToStr(E.ErrorCode));
      end;
    end;

    CheckEquals(200, LIdHTTP.ResponseCode);
  finally
    LAuth.Free;
    LIdHTTP.Free;
  end;
end;
Walter Prins
@ByteJuggler
Just to add, I'll try to look into this a bit more if I get some time with sniffers like Wireshark or Fiddler etc. and post back if I find anything.
Remy Lebeau
@rlebeau
@ByteJuggler HTTP redirects are not part of authentication. And you can't even get an HTTP redirect in the first place if a proxy is used and you havn't authenticated with the proxy yet. Proxies don't generally use redirects, a request to connect to a server either succeeds or fails, with or without authentication. There is no way Indy would treat 302 as a 407 failure. Look at the code in TIdHTTPProtocol.ProcessResponse(). The very first thing it does after validating the response headers is check for a 3xx redirect and handle it, otherwise it checks for 4xx authentication and handle it, otherwise it checks for 2xx success and handles it, otherwise it fails.
elekgeek
@elekgeek

What is the correct way of shutting IdTCPServer down while it has clients connected to it? If I use this code:

IdTCPServer1.Active := False;

the application simply hangs.

Matthijs ter Woord
@mterwoord
you mean you want to force disconnect all existing connections?
elekgeek
@elekgeek
yes
I am wondering why IdTCPServer1.Active := False; is not allowing me to kill all existing connections
Kudzu
@czhower
because you likely have code in them preventing the threads from terminating properly.
Walter Prins
@ByteJuggler
@rlebeau I know redirects are not part of authentication(!) -- However I'm describing what appears to be happening. The bottom line remains: The same NTLM proxy with the same request (with a 302 thrown in) works via browser and does not with Indy. This seems to imply Indy somehow being improperly set up (where? how?), or at fault. But both work if I manually avoid the 302 redirect. I'll debug some more, though for the time being I've just changed the URL to avoid the problem. Thanks for your comments.
elekgeek
@elekgeek
What am I supposed to do then?
Kudzu
@czhower
You need to trace your code and find out where its getting stuck. Indy code will not get stuck on its own during thread shut down.
elekgeek
@elekgeek
OnExecute
try
  with AContext.Connection.IOHandler do
  begin
    CheckForDataOnSource(10);
    if not InputBufferIsEmpty then
    begin
      RxBufStr := InputBuffer.ExtractToString(-1, IndyTextEncoding_UTF8);
      Log(RxBufStr);
      AContext.Binding.SendTo(AContext.Binding.PeerIP, 7, RxBufStr, Id_IPv4);
IdBuffer.Extract()
    end;
  end;
finally
end;
So, why would it hang when its asked to terminate its own threads... this is silly...
Why would I even be responsible to terminate internal socket connections threads...
Remy Lebeau
@rlebeau
@elekgeek why are you using code like this? This is not a good use of TIdBuffer.ExtractString(), especially with a text encoding involved. You are reading arbitrary bytes and assuming they constitute a complete sequence of characters, which is not guaranteed, so the bytes may not decode to a string properly. And, you shouldn't be calling Binding.SendTo() directly at all (and besides, using sendto() on a TCP/IP socket is meaningless anyway, the destination parameters are ignored so it acts the same as send()). This kind of code is a good way to bypass TIdTCPServer's ability to auto-stop its client threads. It expects an exception to be raised when the socket is closed, but you are not allowing it to raise anything. The correct thing to do would be to use TIdIOHandler.ReadBytes() instead, letting it block until new bytes arrive and raise an exception if the client disconnects or the server is being shutting down. And use TIdIOHandler.Write() instead of Binding.Send/To() directly.
@elekgeek If you are just trying to implement an echoing server with logging, I would suggest using this instead:
var
  RxBufStr: string;
begin
  with AContext.Connection.IOHandler do
  begin
    CheckForDataOnSource(10);
    CheckForDisconnect; // <-- add this
    if not InputBufferIsEmpty then
    begin
      RxBufStr := InputBufferAsString(IndyTextEncoding_8Bit); // <-- don't assume any encoding
      Log(RxBufStr);
      Write(RxBufStr, IndyTextEncoding_8Bit); // <-- use TIdIOHandler.Write() instead
    end;
  end;
end;
@elekgeek Personally, I would not use a string at all:
var
  RxBuf: TIdBytes;
begin
  with AContext.Connection.IOHandler do
  begin
    ReadBytes(RxBuf, -1);
    Log(RxBuf);
    Write(RxBuf);
  end;
end;
@elekgeek in which case, you might consider using TIdECHOServer instead of TIdTCPServer. For logging, you can assign a TIdLog... component (TIdLogFile, TIdLogEvent, etc) to the AContext.Connection.IOHandler.Intercept property, such as in the OnConnect event.
elekgeek
@elekgeek
I am trying to do echo thing just for testing purposes, other than that I will not need the echo functionality.
Remy Lebeau
@rlebeau
@ByteJuggler handling redirects should not be affecting TIdHTTP's ability to handle authentication. If anything, a redirect simply resets the AuthRetries and ProxyAuthRetries counters, giving TIdHTTP more chances to continue authentication attempts. Please don't assume what you think is happening, debug and find out exactly what is really happening.
elekgeek
@elekgeek
concerning the code above, it did not solve the issue. Once a client is connected, it is not possible to deactivate the server, so same issue. Also rubbish text is received.
Remy Lebeau
@rlebeau
@elekgeek the preferred way to use TIdTCPServeris to use blocking I/O that follows a defined protocol, raising exceptions on errors/disconnects/shutdowns, and let the server handle the exceptions. If you skip that logic, you become responsible for handling certain things manually, like shutdown. For instance, you could set a variable before setting Active=False (or, just look at Active itself, since it is toggled to false before thread shutdowns occur), and then have OnExecute look at that variable, and if set then call AContext.Connection.Disconnect, or raise your own exception, before exiting the event.
Remy Lebeau
@rlebeau
@elekgeek the code I gave works just fine for shutting down clients. So, the most likely possibility is if Log() itself is deadlocking, such as if it tries to synchronize with the main UI thread while the main UI thread is blocked waiting for the server to finish shutdown. Do not synchronize with the thread that is shutting down the server, that is a guaranteed deadlock. Either skip the synched operation during shutdown, or use a separate worker thread to shutdown the server so syncs can still be processed.
Kudzu
@czhower
I realize you are having issues elekgeek, but it is either your code, or milliions of Indy servers out there are all somehow not having the same Indy issue. I can tell you which I think it is.
Remy Lebeau
@rlebeau
@elekgeek As for "rubbish text", that is just because of the use of the 8bit encoding, which stores each byte as-is as a separate char and your code is not accounting for that. You can't use UTF-8 unless you ensure complete byte sequences for multi-byte characters, but your original code was not doing that. What kind of protocol is your server implementing? If the text is line-based, for instance, then you could just use TIdIOHandler.ReadLn(), in which case using UTF-8 would be OK. But if you use ReadBytes(-1) or ExtractString(-1) or InputBufferAsString then all guarantees about the completeness of multi-byte sequences go out the window
elekgeek
@elekgeek

@rlebeau this has worked for me and solved my problem:

     with IdTCPServer1.Contexts.LockList do
        try
           for iA := Count - 1 downto 0 do
           begin
              Context := Items[iA];
              if Context = nil then
                 Continue;
              Context.Connection.IOHandler.WriteBufferClear;
              Context.Connection.IOHandler.InputBuffer.Clear;
              Context.Connection.IOHandler.Close;
              if Context.Connection.Connected then
                 Context.Connection.Disconnect;
           end;
        finally
           IdTCPServer1.Contexts.UnlockList;
           IdTCPServer1.Active := False;
        end;

What do you think?

This code also suggested by you works very well:

  with AContext.Connection.IOHandler do
  begin
    CheckForDataOnSource(10);
    CheckForDisconnect; // <-- add this
    if not InputBufferIsEmpty then
    begin
      RxBufStr := InputBufferAsString(IndyTextEncoding_8Bit); // <-- don't assume any encoding
      Log(RxBufStr);
      Write(RxBufStr, IndyTextEncoding_8Bit); // <-- use TIdIOHandler.Write() instead
    end;
  end;

Now my question:

  • why were you not been able to suggest such a solution, no offense intended, but for sure you have a good reason.
  • How can the client check if the server went down and close its own connection?
Remy Lebeau
@rlebeau
@elekgeek you don't need any of that. Just set Active to false, it already disconnects the clients for you, and then waits for their threads to terminate. Your issue is that your code is blocking the threads from terminating correctly. That is what you need to fix properly, not hack around it.
elekgeek
@elekgeek

Look again please, I edited my previous answer

man my code is very simple, Look at my last answer, I am not blocking anything

and BTW
procedure TForm2.Log(const s: string);
begin
  mLog.Lines.Add(s);
end;
elekgeek
@elekgeek
@rlebeau Just set Active to false, it already disconnects the clients for you apparently it is not shutting down any threads.. my code is very simple and you have already seen it..
Remy Lebeau
@rlebeau

@elekgeek your Log() function is directly accessing a UI component (a TMemo?). THAT IS NOT THREAD-SAFE! That could easily cause deadlocks (amongst many other problems). OnExecute is fired in a worker thread, so Log() MUST synchronize with the main UI thread. I suggest it use TThread.Queue() or TIdNotify for that purpose (depending on your version of Delphi). That would avoid any cross-thread access issues, avoid any deadlock scenarios, and avoid blocking the OnExecute code (which should not need to wait on the UI to display log messages). For example:

procedure TForm2.Log(const s: string);
begin
  TThread.Queue(nil,
    procedure
    begin
      mLog.Lines.Add(s);
    end
  );
end;

or:

uses
  ..., IdSync;

type
  TLog = class(TIdNotify)
  protected
    FMsg: string;
    procedure DoNotify; override;
  public
    constructor Create(const s: string); reintroduce;
  end;

constructor TLog.Create(const s: string);
begin
  inherited Create;
  FMsg := s;
end;

procedure TLog.DoNotify;
begin
  Form2.mLog.Lines.Add(FMsg);
end;

procedure TForm2.Log(const s: string);
begin
  TLog.Create(s).Notify;
end;
Justin
@klsyzzz
hi there, just wondering is there an example for using TIdHTTP and NTLM Authentication in delphi?
elekgeek
@elekgeek

@rlebeau fabulous, both work like a charm

thanks a lot

Justin
@klsyzzz
I need to call a httpPost action from within Delphi and using NTLM auth
Remy Lebeau
@rlebeau
@klsyzzz NTLM support in Indy is spotty at best. There are two different NTLM classes available for TIdHTTP - TIdNTLMAuthentication in IdAuthenticationNTLM.pas (portable but untested), and TIdSSPINTLMAuthentication in IdAuthenticationSSPI.pas (Windows only). In theory, you should be able to just add one of those units to your uses clause and let TIdHTTP handle the rest. In practice, .... umm, good luck? They either work or they don't. I have no way of testing either one, as I have no access to any HTTP servers that use NTLM
Justin
@klsyzzz
thank you @rlebeau , I will have a try and let you know how it goes
Justin
@klsyzzz
hi @rlebeau , I added IdAuthenticationNTLM.pas to my uses clause, and created an instance of TidHTTP, what I need to do next to enable NTLM auth? I didn't find any property on TidHTTP to select auth type, do I need to do the following:
auth := TIdNTLMAuthentication.Create;
IdHTTP1.AuthenticationManager.AddAuthentication(auth);
Remy Lebeau
@rlebeau
@klsyzzz just add the unit to the uses clause to register the class with TIdHTTP. TIdHTTP should then automatically use the class on any server that supports NTLM (though you can use the TIdHTTP.OnSelectAuthorization event to make sure that is what TIdHTTP picks, if multiple authentications are supported and registered). Now put your NTLM credentials in the TIdHTTP.Request.UserName and TIdHTTP.Request.Password properties, and assign a TIdHTTP.OnAuthorization event handler to promt the user for new credentials if the initial credentials don't work
Justin
@klsyzzz
thanks @rlebeau, having issues getting it work, will ask you further questions once I'm more clear with it.
DelphiWorlds
@DelphiWorlds
I'm back on the Multicast issue - it seems I just need to find a multicast group that works. The information available is pretty confusing, as is iOS's apparent refusal to accept what I thought are valid groups. Any ideas on how to find something definitive?
I'm using a TIdIPMCastClient, and the following code:
procedure TForm1.FormCreate(Sender: TObject);
var
  I: Integer;
begin
  BroadcastListenerIPv6.DefaultPort := 6000;
  for I := 0 to BroadcastListenerIPv6.Bindings.Count - 1 do
    BroadcastListenerIPv6.Bindings.Items[I].Port := BroadcastListenerIPv6.DefaultPort;
  BroadcastListenerIPv6.MulticastGroup := 'FF02:0:0:0:0:0:0:1';
  BroadcastListenerIPv6.Active := True;
end;
Remy Lebeau
@rlebeau
@DelphiWorlds Obviously, you have to use whatever group IP is actually running on your local network. Is there an actual multicast group FF02:0:0:0:0:0:0:1 on your network?
DelphiWorlds
@DelphiWorlds
That's my point.. how do I know if there is one? Do I actually need to create it, and how?
Remy Lebeau
@rlebeau
@DelphiWorlds of course you need to have something setup on the network that will be sending packets to the group, and you need to know what that group is ahead of time. What else are you going to listen for packets from? TIdIPMCastClient joins a group. Anyone can send packets to the same group, and they will be delivered to whoever is joined
DelphiWorlds
@DelphiWorlds
procedure TForm1.FormCreate(Sender: TObject);
var
  I: Integer;
begin
  BroadcastServerIPv6.Port := 6000;
  BroadcastServerIPv6.MulticastGroup := 'FF02:0:0:0:0:0:0:1';
  BroadcastServerIPv6.Active := True;

  BroadcastListenerIPv6.DefaultPort := 6000;
  for I := 0 to BroadcastListenerIPv6.Bindings.Count - 1 do
    BroadcastListenerIPv6.Bindings.Items[I].Port := BroadcastListenerIPv6.DefaultPort;
  BroadcastListenerIPv6.MulticastGroup := 'FF02:0:0:0:0:0:0:1';
  BroadcastListenerIPv6.Active := True;
end;
Still does not work
Same error, i.e. 49 (Address invalid)
Remy Lebeau
@rlebeau
@DelphiWorlds well, you didn't say that earlier. That is an OS error, not an Indy error. Where exactly are you seeing that error? What local IP(s) are you binding the client to? You are only setting TIdSocketHandle.Port in code, are you leaving TIdSocketHandle.IP blank? I don't know if you can do that when joining a multicast group as a client