alnickels 0 Posted March 19, 2020 How can I read a message from a tidtcpserver context without removing it from the read buffer? I want to preview the message and leave it in the buffer. Share this post Link to post
Remy Lebeau 1656 Posted March 19, 2020 (copied from my answer to your same question on StackOverflow😞 Indy is not really designed for peeking data, it would rather that you read whole data, letting it block until the requested data has arrived in full. That being said, TIdBuffer does have a PeekByte() method: function PeekByte(AIndex: Integer): Byte; var B: Byte; if AContext.Connection.IOHandler.InputBuffer.Size > 0 then begin B := AContext.Connection.IOHandler.InputBuffer.PeekByte(0); ... end; Or, if you are looking for something in particular in the buffer (ie, a message delimiter, etc), TIdBuffer has several overloaded IndexOf() methods: function IndexOf(const AByte: Byte; AStartPos: Integer = 0): Integer; overload; function IndexOf(const ABytes: TIdBytes; AStartPos: Integer = 0): Integer; overload; function IndexOf(const AString: string; AStartPos: Integer = 0; AByteEncoding: IIdTextEncoding = nil {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} ): Integer; overload; var Index: Integer; Index := AContext.Connection.IOHandler.InputBuffer.IndexOf(SingleByte); Index := AContext.Connection.IOHandler.InputBuffer.IndexOf(ArrayOfBytes); Index := AContext.Connection.IOHandler.InputBuffer.IndexOf('string'); ... Share this post Link to post
FearDC 1 Posted yesterday at 10:48 AM (edited) For those who are still or will get interested in future. Got into same situation using Indy, and here is the hack: uses {$IFDEF POSIX} Posix.SysSocket, {$ELSE} WinAPI.WinSock, {$ENDIF} {$IFDEF POSIX} type TTimeVal = record tv_sec, tv_usec: LongInt; end; {$ENDIF} var LPeek: Array [0..1] of Byte; // required peek length {$IFDEF POSIX} LTime: TTimeVal; {$ELSE} LTime: DWord; {$ENDIF} begin // set one second timeout since indy sockets are blocking {$IFDEF POSIX} LTime.tv_sec := 1; LTime.tv_usec := 0; Posix.SysSocket.SetSockOpt(AContext.Binding.Handle, Posix.SysSocket.SOL_SOCKET, Posix.SysSocket.SO_RCVTIMEO, <ime, SizeOf(LTime)); {$ELSE} LTime := 1 * 1000; WinAPI.WinSock.SetSockOpt(AContext.Binding.Handle, WinAPI.WinSock.SOL_SOCKET, WinAPI.WinSock.SO_RCVTIMEO, PAnsiChar(@LTime), SizeOf(LTime)); {$ENDIF} if {$IFDEF POSIX} (Posix.SysSocket.Recv(AContext.Binding.Handle, LPeek, 2, Posix.SysSocket.MSG_PEEK) = 2) {$ELSE} (WinAPI.WinSock.Recv(AContext.Binding.Handle, LPeek, 2, WinAPI.WinSock.MSG_PEEK) = 2) {$ENDIF} and (LPeek[0] = 22) and (LPeek[1] = 03) then // in my situation: tls magic bytes TIdSSLIOHandlerSocketOpenSSL(AContext.Connection.IOHandler).PassThrough := False; // reset socket timeout {$IFDEF POSIX} LTime.tv_sec := 0; LTime.tv_usec := 0; Posix.SysSocket.SetSockOpt(AContext.Binding.Handle, Posix.SysSocket.SOL_SOCKET, Posix.SysSocket.SO_RCVTIMEO, <ime, SizeOf(LTime)); {$ELSE} LTime := 0; WinAPI.WinSock.SetSockOpt(AContext.Binding.Handle, WinAPI.WinSock.SOL_SOCKET, WinAPI.WinSock.SO_RCVTIMEO, PAnsiChar(@LTime), SizeOf(LTime)); {$ENDIF} end; 😃 Edited yesterday at 10:54 AM by FearDC Share this post Link to post
Remy Lebeau 1656 Posted yesterday at 04:48 PM 5 hours ago, FearDC said: For those who are still or will get interested in future. Got into same situation using Indy, and here is the hack: Indy has some wrappers for setsockopt() and recv(), so your code can be cleaned up a little bit. Try something more like this (based on https://stackoverflow.com/a/78873272/65863): uses ..., IdStack, IdStackBSDBase, IdSSL; type TIdStackBSDBaseAccess = class(TIdStackBSDBase) end; function SslTlsHandshakeDetected(ASocket: TIdStackSocketHandle): Boolean; var Buffer: array[0..5] of Byte; NumBytes, Len: Integer; MsgType: Byte; begin Result := False; NumBytes := GStack.CheckForSocketError( TIdStackBSDBaseAccess(GBSDStack).WSRecv(ASocket, Buffer, SizeOf(Buffer), MSG_PEEK) ); if NumBytes < 3 then Exit; // SSL v3 or TLS v1.x ? if (Buffer[0] = $16) and // type (22 = handshake) (Buffer[1] = $3) then // protocol major version (3.0 = SSL v3, 3.x = TLS v1.x) begin Result := True; Exit; end; // SSL v2 ? if (Buffer[0] and $80) <> 0 then begin // no padding, 2-byte header Len := (Integer(buffer[0] and $7F) shl 8) + buffer[1]; MsgType := buffer[2]; end else begin // padding, 3-byte header Len := (Integer(buffer[0] and $3F) shl 8) + buffer[1]; MsgType := Buffer[3]; end; Result := (Len > 9) and (MsgType = $1); // msg type (1 = client hello) end; procedure SetRecvTimeout(AContext: TIdContext; const ASecs: Integer); {$IFDEF POSIX} type TTimeVal = record tv_sec, tv_usec: LongInt; end; var LTime: TTimeVal; {$ENDIF} begin {$IFDEF POSIX} LTime.tv_sec := ASecs; LTime.tv_usec := 0; GBSDStack.SetSocketOption(AContext.Binding.Handle, Id_SOL_SOCKET, Id_SO_RCVTIMEO, LTime, SizeOf(LTime)); {$ELSE} AContext.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_RCVTIMEO, ASecs * 1000); {$ENDIF} end; ... begin SetRecvTimeout(AContext, 1); TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough := not SslTlsHandshakeDetected(AContext.Binding.Handle); SetRecvTimeout(AContext, 0); end; Share this post Link to post