blob: 44dfef7803003cde7741ee3afb7188ca92f95e1f [file] [log] [blame]
Roger Meier3bef8c22012-10-06 06:58:00 +00001(*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *)
19unit Thrift.Transport.Pipes;
20
21{$WARN SYMBOL_PLATFORM OFF}
Jens Geyer9f7f11e2016-04-14 21:37:11 +020022{$I Thrift.Defines.inc}
Roger Meier3bef8c22012-10-06 06:58:00 +000023
24interface
25
26uses
Jens Geyer9f7f11e2016-04-14 21:37:11 +020027 {$IFDEF OLD_UNIT_NAMES}
Jens Geyer06045cf2013-03-27 20:26:25 +020028 Windows, SysUtils, Math, AccCtrl, AclAPI, SyncObjs,
Jens Geyer9f7f11e2016-04-14 21:37:11 +020029 {$ELSE}
Nick4f5229e2016-04-14 16:43:22 +030030 Winapi.Windows, System.SysUtils, System.Math, Winapi.AccCtrl, Winapi.AclAPI, System.SyncObjs,
Jens Geyer9f7f11e2016-04-14 21:37:11 +020031 {$ENDIF}
Jens Geyera019cda2019-11-09 23:24:52 +010032 Thrift.Configuration,
Roger Meier3bef8c22012-10-06 06:58:00 +000033 Thrift.Transport,
Jens Geyere9651362014-03-20 22:46:17 +020034 Thrift.Utils,
Roger Meier3bef8c22012-10-06 06:58:00 +000035 Thrift.Stream;
36
37const
Jens Geyer653f0de2016-04-20 12:46:57 +020038 DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT = 10; // default: fail fast on open
Roger Meier3bef8c22012-10-06 06:58:00 +000039
Jens Geyere9651362014-03-20 22:46:17 +020040
Roger Meier3bef8c22012-10-06 06:58:00 +000041type
Roger Meier79655fb2012-10-20 20:59:41 +000042 //--- Pipe Streams ---
Roger Meier3bef8c22012-10-06 06:58:00 +000043
44
Jens Geyer06045cf2013-03-27 20:26:25 +020045 TPipeStreamBase = class( TThriftStreamImpl)
Roger Meier79655fb2012-10-20 20:59:41 +000046 strict protected
47 FPipe : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +000048 FTimeout : DWORD;
Jens Geyer653f0de2016-04-20 12:46:57 +020049 FOpenTimeOut : DWORD; // separate value to allow for fail-fast-on-open scenarios
Jens Geyere9651362014-03-20 22:46:17 +020050 FOverlapped : Boolean;
Roger Meier3bef8c22012-10-06 06:58:00 +000051
Jens Geyer17c3ad92017-09-05 20:31:27 +020052 procedure Write( const pBuf : Pointer; offset, count : Integer); override;
53 function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
Roger Meier79655fb2012-10-20 20:59:41 +000054 //procedure Open; override; - see derived classes
Roger Meier3bef8c22012-10-06 06:58:00 +000055 procedure Close; override;
56 procedure Flush; override;
57
Jens Geyer17c3ad92017-09-05 20:31:27 +020058 function ReadDirect( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; overload;
59 function ReadOverlapped( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; overload;
60 procedure WriteDirect( const pBuf : Pointer; offset: Integer; count: Integer); overload;
61 procedure WriteOverlapped( const pBuf : Pointer; offset: Integer; count: Integer); overload;
Jens Geyere9651362014-03-20 22:46:17 +020062
Roger Meier3bef8c22012-10-06 06:58:00 +000063 function IsOpen: Boolean; override;
64 function ToArray: TBytes; override;
65 public
Jens Geyer653f0de2016-04-20 12:46:57 +020066 constructor Create( aEnableOverlapped : Boolean;
67 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
Jens Geyera019cda2019-11-09 23:24:52 +010068 const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT
69 ); reintroduce; overload;
70
Roger Meier3bef8c22012-10-06 06:58:00 +000071 destructor Destroy; override;
72 end;
73
74
Jens Geyer06045cf2013-03-27 20:26:25 +020075 TNamedPipeStreamImpl = class sealed( TPipeStreamBase)
Jens Geyere9651362014-03-20 22:46:17 +020076 strict private
Roger Meier79655fb2012-10-20 20:59:41 +000077 FPipeName : string;
78 FShareMode : DWORD;
79 FSecurityAttribs : PSecurityAttributes;
Roger Meier3bef8c22012-10-06 06:58:00 +000080
Jens Geyere9651362014-03-20 22:46:17 +020081 strict protected
Roger Meier79655fb2012-10-20 20:59:41 +000082 procedure Open; override;
83
84 public
85 constructor Create( const aPipeName : string;
Jens Geyere9651362014-03-20 22:46:17 +020086 const aEnableOverlapped : Boolean;
Roger Meier79655fb2012-10-20 20:59:41 +000087 const aShareMode: DWORD = 0;
88 const aSecurityAttributes: PSecurityAttributes = nil;
Jens Geyer653f0de2016-04-20 12:46:57 +020089 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
Jens Geyera019cda2019-11-09 23:24:52 +010090 const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT
91 ); reintroduce; overload;
Roger Meier79655fb2012-10-20 20:59:41 +000092 end;
93
94
Jens Geyer06045cf2013-03-27 20:26:25 +020095 THandlePipeStreamImpl = class sealed( TPipeStreamBase)
Jens Geyere9651362014-03-20 22:46:17 +020096 strict private
Roger Meier79655fb2012-10-20 20:59:41 +000097 FSrcHandle : THandle;
98
Jens Geyere9651362014-03-20 22:46:17 +020099 strict protected
Roger Meier79655fb2012-10-20 20:59:41 +0000100 procedure Open; override;
101
102 public
Jens Geyere9651362014-03-20 22:46:17 +0200103 constructor Create( const aPipeHandle : THandle;
104 const aOwnsHandle, aEnableOverlapped : Boolean;
Jens Geyera019cda2019-11-09 23:24:52 +0100105 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT
106 ); reintroduce; overload;
107
Roger Meier79655fb2012-10-20 20:59:41 +0000108 destructor Destroy; override;
109 end;
110
111
112 //--- Pipe Transports ---
113
114
Jens Geyer06045cf2013-03-27 20:26:25 +0200115 IPipeTransport = interface( IStreamTransport)
Roger Meier79655fb2012-10-20 20:59:41 +0000116 ['{5E05CC85-434F-428F-BFB2-856A168B5558}']
117 end;
118
119
Jens Geyer06045cf2013-03-27 20:26:25 +0200120 TPipeTransportBase = class( TStreamTransportImpl, IPipeTransport)
Jens Geyera019cda2019-11-09 23:24:52 +0100121 strict protected
Roger Meier79655fb2012-10-20 20:59:41 +0000122 // ITransport
123 function GetIsOpen: Boolean; override;
124 procedure Open; override;
125 procedure Close; override;
126 end;
127
128
Jens Geyer06045cf2013-03-27 20:26:25 +0200129 TNamedPipeTransportClientEndImpl = class( TPipeTransportBase)
Roger Meier79655fb2012-10-20 20:59:41 +0000130 public
Roger Meier3bef8c22012-10-06 06:58:00 +0000131 // Named pipe constructors
Jens Geyer41f47af2019-11-09 23:24:52 +0100132 constructor Create( const aPipe : THandle;
133 const aOwnsHandle : Boolean;
134 const aTimeOut : DWORD;
Jens Geyera019cda2019-11-09 23:24:52 +0100135 const aConfig : IThriftConfiguration = nil
136 ); reintroduce; overload;
Jens Geyer41f47af2019-11-09 23:24:52 +0100137
Roger Meier3bef8c22012-10-06 06:58:00 +0000138 constructor Create( const aPipeName : string;
139 const aShareMode: DWORD = 0;
140 const aSecurityAttributes: PSecurityAttributes = nil;
Jens Geyer653f0de2016-04-20 12:46:57 +0200141 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
Jens Geyer41f47af2019-11-09 23:24:52 +0100142 const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT;
Jens Geyera019cda2019-11-09 23:24:52 +0100143 const aConfig : IThriftConfiguration = nil
144 ); reintroduce; overload;
Roger Meier3bef8c22012-10-06 06:58:00 +0000145 end;
146
147
Jens Geyer06045cf2013-03-27 20:26:25 +0200148 TNamedPipeTransportServerEndImpl = class( TNamedPipeTransportClientEndImpl)
Roger Meier79655fb2012-10-20 20:59:41 +0000149 strict private
150 FHandle : THandle;
Jens Geyera019cda2019-11-09 23:24:52 +0100151 strict protected
Roger Meier79655fb2012-10-20 20:59:41 +0000152 // ITransport
153 procedure Close; override;
Jens Geyera019cda2019-11-09 23:24:52 +0100154 public
Jens Geyer41f47af2019-11-09 23:24:52 +0100155 constructor Create( const aPipe : THandle;
156 const aOwnsHandle : Boolean;
157 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
Jens Geyera019cda2019-11-09 23:24:52 +0100158 const aConfig : IThriftConfiguration = nil
159 ); reintroduce; overload;
160
Roger Meier79655fb2012-10-20 20:59:41 +0000161 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000162
Roger Meier79655fb2012-10-20 20:59:41 +0000163
Jens Geyer06045cf2013-03-27 20:26:25 +0200164 TAnonymousPipeTransportImpl = class( TPipeTransportBase)
Roger Meier79655fb2012-10-20 20:59:41 +0000165 public
Roger Meier3bef8c22012-10-06 06:58:00 +0000166 // Anonymous pipe constructor
Jens Geyer41f47af2019-11-09 23:24:52 +0100167 constructor Create( const aPipeRead, aPipeWrite : THandle;
168 const aOwnsHandles : Boolean;
169 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
Jens Geyera019cda2019-11-09 23:24:52 +0100170 const aConfig : IThriftConfiguration = nil
171 ); reintroduce; overload;
Roger Meier3bef8c22012-10-06 06:58:00 +0000172 end;
173
174
Roger Meier79655fb2012-10-20 20:59:41 +0000175 //--- Server Transports ---
176
177
Jens Geyer06045cf2013-03-27 20:26:25 +0200178 IAnonymousPipeServerTransport = interface( IServerTransport)
Roger Meier3bef8c22012-10-06 06:58:00 +0000179 ['{7AEE6793-47B9-4E49-981A-C39E9108E9AD}']
180 // Server side anonymous pipe ends
Roger Meier79655fb2012-10-20 20:59:41 +0000181 function ReadHandle : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000182 function WriteHandle : THandle;
183 // Client side anonymous pipe ends
184 function ClientAnonRead : THandle;
185 function ClientAnonWrite : THandle;
186 end;
187
188
Jens Geyer06045cf2013-03-27 20:26:25 +0200189 INamedPipeServerTransport = interface( IServerTransport)
Roger Meier79655fb2012-10-20 20:59:41 +0000190 ['{9DF9EE48-D065-40AF-8F67-D33037D3D960}']
191 function Handle : THandle;
192 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000193
Roger Meier79655fb2012-10-20 20:59:41 +0000194
Jens Geyer06045cf2013-03-27 20:26:25 +0200195 TPipeServerTransportBase = class( TServerTransportImpl)
Jens Geyere9651362014-03-20 22:46:17 +0200196 strict protected
197 FStopServer : TEvent;
Jens Geyer06045cf2013-03-27 20:26:25 +0200198 procedure InternalClose; virtual; abstract;
Jens Geyere9651362014-03-20 22:46:17 +0200199 function QueryStopServer : Boolean;
Roger Meier79655fb2012-10-20 20:59:41 +0000200 public
Jens Geyera019cda2019-11-09 23:24:52 +0100201 constructor Create( const aConfig : IThriftConfiguration);
Jens Geyere9651362014-03-20 22:46:17 +0200202 destructor Destroy; override;
Roger Meier79655fb2012-10-20 20:59:41 +0000203 procedure Listen; override;
Jens Geyer06045cf2013-03-27 20:26:25 +0200204 procedure Close; override;
Roger Meier79655fb2012-10-20 20:59:41 +0000205 end;
206
207
Jens Geyer06045cf2013-03-27 20:26:25 +0200208 TAnonymousPipeServerTransportImpl = class( TPipeServerTransportBase, IAnonymousPipeServerTransport)
Jens Geyere9651362014-03-20 22:46:17 +0200209 strict private
Roger Meier79655fb2012-10-20 20:59:41 +0000210 FBufSize : DWORD;
211
212 // Server side anonymous pipe handles
213 FReadHandle,
Roger Meier3bef8c22012-10-06 06:58:00 +0000214 FWriteHandle : THandle;
215
216 //Client side anonymous pipe handles
217 FClientAnonRead,
218 FClientAnonWrite : THandle;
219
Jens Geyerdd074e72016-04-19 23:31:33 +0200220 FTimeOut: DWORD;
Jens Geyerfad7fd32019-11-09 23:24:52 +0100221 strict protected
Jens Geyer01640402013-09-25 21:12:21 +0200222 function Accept(const fnAccepting: TProc): ITransport; override;
Roger Meier3bef8c22012-10-06 06:58:00 +0000223
Roger Meier3bef8c22012-10-06 06:58:00 +0000224 function CreateAnonPipe : Boolean;
225
Jens Geyer06045cf2013-03-27 20:26:25 +0200226 // IAnonymousPipeServerTransport
Roger Meier79655fb2012-10-20 20:59:41 +0000227 function ReadHandle : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000228 function WriteHandle : THandle;
229 function ClientAnonRead : THandle;
230 function ClientAnonWrite : THandle;
231
Jens Geyer06045cf2013-03-27 20:26:25 +0200232 procedure InternalClose; override;
233
Roger Meier3bef8c22012-10-06 06:58:00 +0000234 public
Jens Geyera019cda2019-11-09 23:24:52 +0100235 constructor Create( const aBufsize : Cardinal = 4096;
236 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
237 const aConfig : IThriftConfiguration = nil
238 ); reintroduce; overload;
Roger Meier3bef8c22012-10-06 06:58:00 +0000239 end;
240
241
Jens Geyer20a86d62021-04-02 11:34:08 +0200242 TNamedPipeFlag = (
243 OnlyLocalClients // sets PIPE_REJECT_REMOTE_CLIENTS
244 );
245 TNamedPipeFlags = set of TNamedPipeFlag;
246
247
Jens Geyer06045cf2013-03-27 20:26:25 +0200248 TNamedPipeServerTransportImpl = class( TPipeServerTransportBase, INamedPipeServerTransport)
Jens Geyere9651362014-03-20 22:46:17 +0200249 strict private
Roger Meier79655fb2012-10-20 20:59:41 +0000250 FPipeName : string;
251 FMaxConns : DWORD;
252 FBufSize : DWORD;
Jens Geyer0b20cc82013-03-07 20:47:01 +0100253 FTimeout : DWORD;
Jens Geyer06045cf2013-03-27 20:26:25 +0200254 FHandle : THandle;
255 FConnected : Boolean;
Jens Geyer20a86d62021-04-02 11:34:08 +0200256 FOnlyLocalClients : Boolean;
Jens Geyer01640402013-09-25 21:12:21 +0200257
Jens Geyere9651362014-03-20 22:46:17 +0200258 strict protected
Jens Geyer01640402013-09-25 21:12:21 +0200259 function Accept(const fnAccepting: TProc): ITransport; override;
Jens Geyer06045cf2013-03-27 20:26:25 +0200260 function CreateNamedPipe : THandle;
261 function CreateTransportInstance : ITransport;
Roger Meier79655fb2012-10-20 20:59:41 +0000262
Jens Geyer06045cf2013-03-27 20:26:25 +0200263 // INamedPipeServerTransport
Roger Meier79655fb2012-10-20 20:59:41 +0000264 function Handle : THandle;
Jens Geyer06045cf2013-03-27 20:26:25 +0200265 procedure InternalClose; override;
Roger Meier79655fb2012-10-20 20:59:41 +0000266
267 public
Jens Geyera019cda2019-11-09 23:24:52 +0100268 constructor Create( const aPipename : string;
269 const aBufsize : Cardinal = 4096;
270 const aMaxConns : Cardinal = PIPE_UNLIMITED_INSTANCES;
271 const aTimeOut : Cardinal = INFINITE;
272 const aConfig : IThriftConfiguration = nil
Jens Geyer20a86d62021-04-02 11:34:08 +0200273 ); reintroduce; overload; deprecated 'use the other CTOR instead';
274
275 constructor Create( const aPipename : string;
276 const aFlags : TNamedPipeFlags;
277 const aConfig : IThriftConfiguration = nil;
278 const aBufsize : Cardinal = 4096;
279 const aMaxConns : Cardinal = PIPE_UNLIMITED_INSTANCES;
280 const aTimeOut : Cardinal = INFINITE
Jens Geyera019cda2019-11-09 23:24:52 +0100281 ); reintroduce; overload;
Roger Meier79655fb2012-10-20 20:59:41 +0000282 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000283
284
285implementation
286
Jens Geyer20a86d62021-04-02 11:34:08 +0200287const
288 // flags used but not declared in all Delphi versions, see MSDN
289 PIPE_ACCEPT_REMOTE_CLIENTS = 0; // CreateNamedPipe() -> dwPipeMode = default
290 PIPE_REJECT_REMOTE_CLIENTS = $00000008; // CreateNamedPipe() -> dwPipeMode
291
292 // Windows platfoms only
293 // https://github.com/dotnet/coreclr/pull/379/files
294 // https://referencesource.microsoft.com/#System.Runtime.Remoting/channels/ipc/win32namedpipes.cs,46b96e3f3828f497,references
295 // Citation from the first source:
296 // > For mitigating local elevation of privilege attack through named pipes
297 // > make sure we always call CreateFile with SECURITY_ANONYMOUS so that the
298 // > named pipe server can't impersonate a high privileged client security context
299 {$IFDEF MSWINDOWS}
300 PREVENT_PIPE_IMPERSONATION = SECURITY_SQOS_PRESENT or SECURITY_ANONYMOUS;
301 {$ELSE}
302 PREVENT_PIPE_IMPERSONATION = 0; // not available on Linux etc
303 {$ENDIF}
304
Roger Meier3bef8c22012-10-06 06:58:00 +0000305
Roger Meier79655fb2012-10-20 20:59:41 +0000306procedure ClosePipeHandle( var hPipe : THandle);
Roger Meier3bef8c22012-10-06 06:58:00 +0000307begin
Roger Meier79655fb2012-10-20 20:59:41 +0000308 if hPipe <> INVALID_HANDLE_VALUE
309 then try
310 CloseHandle( hPipe);
311 finally
312 hPipe := INVALID_HANDLE_VALUE;
313 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000314end;
315
316
Roger Meier79655fb2012-10-20 20:59:41 +0000317function DuplicatePipeHandle( const hSource : THandle) : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000318begin
Roger Meier79655fb2012-10-20 20:59:41 +0000319 if not DuplicateHandle( GetCurrentProcess, hSource,
320 GetCurrentProcess, @result,
321 0, FALSE, DUPLICATE_SAME_ACCESS)
Jens Geyere0e32402016-04-20 21:50:48 +0200322 then raise TTransportExceptionNotOpen.Create('DuplicateHandle: '+SysErrorMessage(GetLastError));
Roger Meier3bef8c22012-10-06 06:58:00 +0000323end;
324
325
Roger Meier79655fb2012-10-20 20:59:41 +0000326
Jens Geyer06045cf2013-03-27 20:26:25 +0200327{ TPipeStreamBase }
Roger Meier79655fb2012-10-20 20:59:41 +0000328
329
Jens Geyera019cda2019-11-09 23:24:52 +0100330constructor TPipeStreamBase.Create( aEnableOverlapped : Boolean; const aTimeOut, aOpenTimeOut : DWORD);
Roger Meier79655fb2012-10-20 20:59:41 +0000331begin
332 inherited Create;
Jens Geyer653f0de2016-04-20 12:46:57 +0200333 FPipe := INVALID_HANDLE_VALUE;
334 FTimeout := aTimeOut;
335 FOpenTimeOut := aOpenTimeOut;
336 FOverlapped := aEnableOverlapped;
Jens Geyera019cda2019-11-09 23:24:52 +0100337 ASSERT( FTimeout > 0); // FOpenTimeout may be 0
Roger Meier79655fb2012-10-20 20:59:41 +0000338end;
339
340
Jens Geyer06045cf2013-03-27 20:26:25 +0200341destructor TPipeStreamBase.Destroy;
Roger Meier3bef8c22012-10-06 06:58:00 +0000342begin
343 try
344 Close;
345 finally
346 inherited Destroy;
347 end;
348end;
349
350
Jens Geyer06045cf2013-03-27 20:26:25 +0200351procedure TPipeStreamBase.Close;
Roger Meier3bef8c22012-10-06 06:58:00 +0000352begin
Roger Meier79655fb2012-10-20 20:59:41 +0000353 ClosePipeHandle( FPipe);
Roger Meier3bef8c22012-10-06 06:58:00 +0000354end;
355
356
Jens Geyer06045cf2013-03-27 20:26:25 +0200357procedure TPipeStreamBase.Flush;
Roger Meier3bef8c22012-10-06 06:58:00 +0000358begin
Jens Geyer0d227b12015-12-02 19:50:55 +0100359 FlushFileBuffers( FPipe);
Roger Meier3bef8c22012-10-06 06:58:00 +0000360end;
361
362
Jens Geyer06045cf2013-03-27 20:26:25 +0200363function TPipeStreamBase.IsOpen: Boolean;
Roger Meier3bef8c22012-10-06 06:58:00 +0000364begin
365 result := (FPipe <> INVALID_HANDLE_VALUE);
366end;
367
368
Jens Geyer17c3ad92017-09-05 20:31:27 +0200369procedure TPipeStreamBase.Write( const pBuf : Pointer; offset, count : Integer);
Jens Geyere9651362014-03-20 22:46:17 +0200370begin
371 if FOverlapped
Jens Geyer17c3ad92017-09-05 20:31:27 +0200372 then WriteOverlapped( pBuf, offset, count)
373 else WriteDirect( pBuf, offset, count);
Jens Geyere9651362014-03-20 22:46:17 +0200374end;
375
376
Jens Geyer17c3ad92017-09-05 20:31:27 +0200377function TPipeStreamBase.Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
Jens Geyere9651362014-03-20 22:46:17 +0200378begin
379 if FOverlapped
Jens Geyer17c3ad92017-09-05 20:31:27 +0200380 then result := ReadOverlapped( pBuf, buflen, offset, count)
381 else result := ReadDirect( pBuf, buflen, offset, count);
Jens Geyere9651362014-03-20 22:46:17 +0200382end;
383
384
Jens Geyer17c3ad92017-09-05 20:31:27 +0200385procedure TPipeStreamBase.WriteDirect( const pBuf : Pointer; offset: Integer; count: Integer);
Jens Geyerd4df9172017-10-25 22:30:23 +0200386var cbWritten, nBytes : DWORD;
Jens Geyer85827152018-01-12 21:20:59 +0100387 pData : PByte;
Roger Meier3bef8c22012-10-06 06:58:00 +0000388begin
389 if not IsOpen
Jens Geyere0e32402016-04-20 21:50:48 +0200390 then raise TTransportExceptionNotOpen.Create('Called write on non-open pipe');
Roger Meier3bef8c22012-10-06 06:58:00 +0000391
Jens Geyerd4df9172017-10-25 22:30:23 +0200392 // if necessary, send the data in chunks
393 // there's a system limit around 0x10000 bytes that we hit otherwise
394 // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section."
395 nBytes := Min( 15*4096, count); // 16 would exceed the limit
Jens Geyer85827152018-01-12 21:20:59 +0100396 pData := pBuf;
397 Inc( pData, offset);
Jens Geyerd4df9172017-10-25 22:30:23 +0200398 while nBytes > 0 do begin
Jens Geyer85827152018-01-12 21:20:59 +0100399 if not WriteFile( FPipe, pData^, nBytes, cbWritten, nil)
Jens Geyerd4df9172017-10-25 22:30:23 +0200400 then raise TTransportExceptionNotOpen.Create('Write to pipe failed');
401
Jens Geyer85827152018-01-12 21:20:59 +0100402 Inc( pData, cbWritten);
Jens Geyerd4df9172017-10-25 22:30:23 +0200403 Dec( count, cbWritten);
404 nBytes := Min( nBytes, count);
405 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000406end;
407
408
Jens Geyer17c3ad92017-09-05 20:31:27 +0200409procedure TPipeStreamBase.WriteOverlapped( const pBuf : Pointer; offset: Integer; count: Integer);
Jens Geyerd4df9172017-10-25 22:30:23 +0200410var cbWritten, dwWait, dwError, nBytes : DWORD;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200411 overlapped : IOverlappedHelper;
Jens Geyer85827152018-01-12 21:20:59 +0100412 pData : PByte;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200413begin
414 if not IsOpen
415 then raise TTransportExceptionNotOpen.Create('Called write on non-open pipe');
416
Jens Geyerd4df9172017-10-25 22:30:23 +0200417 // if necessary, send the data in chunks
418 // there's a system limit around 0x10000 bytes that we hit otherwise
419 // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section."
420 nBytes := Min( 15*4096, count); // 16 would exceed the limit
Jens Geyer85827152018-01-12 21:20:59 +0100421 pData := pBuf;
422 Inc( pData, offset);
Jens Geyerd4df9172017-10-25 22:30:23 +0200423 while nBytes > 0 do begin
424 overlapped := TOverlappedHelperImpl.Create;
Jens Geyer85827152018-01-12 21:20:59 +0100425 if not WriteFile( FPipe, pData^, nBytes, cbWritten, overlapped.OverlappedPtr)
Jens Geyerd4df9172017-10-25 22:30:23 +0200426 then begin
427 dwError := GetLastError;
428 case dwError of
429 ERROR_IO_PENDING : begin
430 dwWait := overlapped.WaitFor(FTimeout);
Jens Geyer17c3ad92017-09-05 20:31:27 +0200431
Jens Geyer00645162018-02-01 23:38:10 +0100432 if (dwWait = WAIT_TIMEOUT) then begin
433 CancelIo( FPipe); // prevents possible AV on invalid overlapped ptr
434 raise TTransportExceptionTimedOut.Create('Pipe write timed out');
435 end;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200436
Jens Geyerd4df9172017-10-25 22:30:23 +0200437 if (dwWait <> WAIT_OBJECT_0)
438 or not GetOverlappedResult( FPipe, overlapped.Overlapped, cbWritten, TRUE)
439 then raise TTransportExceptionUnknown.Create('Pipe write error');
440 end;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200441
Jens Geyerd4df9172017-10-25 22:30:23 +0200442 else
443 raise TTransportExceptionUnknown.Create(SysErrorMessage(dwError));
Jens Geyer17c3ad92017-09-05 20:31:27 +0200444 end;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200445 end;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200446
Jens Geyerd4df9172017-10-25 22:30:23 +0200447 ASSERT( DWORD(nBytes) = cbWritten);
448
Jens Geyer85827152018-01-12 21:20:59 +0100449 Inc( pData, cbWritten);
Jens Geyerd4df9172017-10-25 22:30:23 +0200450 Dec( count, cbWritten);
451 nBytes := Min( nBytes, count);
452 end;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200453end;
454
455
456function TPipeStreamBase.ReadDirect( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
Jens Geyerd4df9172017-10-25 22:30:23 +0200457var cbRead, dwErr, nRemaining : DWORD;
Roger Meier3bef8c22012-10-06 06:58:00 +0000458 bytes, retries : LongInt;
459 bOk : Boolean;
Jens Geyer85827152018-01-12 21:20:59 +0100460 pData : PByte;
Roger Meier3bef8c22012-10-06 06:58:00 +0000461const INTERVAL = 10; // ms
462begin
463 if not IsOpen
Jens Geyere0e32402016-04-20 21:50:48 +0200464 then raise TTransportExceptionNotOpen.Create('Called read on non-open pipe');
Roger Meier3bef8c22012-10-06 06:58:00 +0000465
466 // MSDN: Handle can be a handle to a named pipe instance,
467 // or it can be a handle to the read end of an anonymous pipe,
468 // The handle must have GENERIC_READ access to the pipe.
469 if FTimeOut <> INFINITE then begin
470 retries := Max( 1, Round( 1.0 * FTimeOut / INTERVAL));
471 while TRUE do begin
Jens Geyer5988f482016-04-19 23:01:24 +0200472 if not PeekNamedPipe( FPipe, nil, 0, nil, @bytes, nil) then begin
473 dwErr := GetLastError;
474 if (dwErr = ERROR_INVALID_HANDLE)
475 or (dwErr = ERROR_BROKEN_PIPE)
476 or (dwErr = ERROR_PIPE_NOT_CONNECTED)
477 then begin
478 result := 0; // other side closed the pipe
479 Exit;
480 end;
481 end
482 else if bytes > 0 then begin
483 Break; // there are data
Roger Meier79655fb2012-10-20 20:59:41 +0000484 end;
485
Roger Meier3bef8c22012-10-06 06:58:00 +0000486 Dec( retries);
487 if retries > 0
488 then Sleep( INTERVAL)
Jens Geyere0e32402016-04-20 21:50:48 +0200489 else raise TTransportExceptionTimedOut.Create('Pipe read timed out');
Roger Meier3bef8c22012-10-06 06:58:00 +0000490 end;
491 end;
492
Jens Geyerd4df9172017-10-25 22:30:23 +0200493 result := 0;
494 nRemaining := count;
Jens Geyer85827152018-01-12 21:20:59 +0100495 pData := pBuf;
496 Inc( pData, offset);
Jens Geyerd4df9172017-10-25 22:30:23 +0200497 while nRemaining > 0 do begin
498 // read the data (or block INFINITE-ly)
Jens Geyer85827152018-01-12 21:20:59 +0100499 bOk := ReadFile( FPipe, pData^, nRemaining, cbRead, nil);
Jens Geyerd4df9172017-10-25 22:30:23 +0200500 if (not bOk) and (GetLastError() <> ERROR_MORE_DATA)
501 then Break; // No more data, possibly because client disconnected.
502
503 Dec( nRemaining, cbRead);
Jens Geyer85827152018-01-12 21:20:59 +0100504 Inc( pData, cbRead);
Jens Geyerd4df9172017-10-25 22:30:23 +0200505 Inc( result, cbRead);
506 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000507end;
508
509
Jens Geyer17c3ad92017-09-05 20:31:27 +0200510function TPipeStreamBase.ReadOverlapped( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
Jens Geyerd4df9172017-10-25 22:30:23 +0200511var cbRead, dwWait, dwError, nRemaining : DWORD;
Jens Geyere9651362014-03-20 22:46:17 +0200512 bOk : Boolean;
513 overlapped : IOverlappedHelper;
Jens Geyer85827152018-01-12 21:20:59 +0100514 pData : PByte;
Jens Geyere9651362014-03-20 22:46:17 +0200515begin
516 if not IsOpen
Jens Geyere0e32402016-04-20 21:50:48 +0200517 then raise TTransportExceptionNotOpen.Create('Called read on non-open pipe');
Jens Geyere9651362014-03-20 22:46:17 +0200518
Jens Geyerd4df9172017-10-25 22:30:23 +0200519 result := 0;
520 nRemaining := count;
Jens Geyer85827152018-01-12 21:20:59 +0100521 pData := pBuf;
522 Inc( pData, offset);
Jens Geyerd4df9172017-10-25 22:30:23 +0200523 while nRemaining > 0 do begin
524 overlapped := TOverlappedHelperImpl.Create;
Jens Geyere9651362014-03-20 22:46:17 +0200525
Jens Geyerd4df9172017-10-25 22:30:23 +0200526 // read the data
Jens Geyer85827152018-01-12 21:20:59 +0100527 bOk := ReadFile( FPipe, pData^, nRemaining, cbRead, overlapped.OverlappedPtr);
Jens Geyerd4df9172017-10-25 22:30:23 +0200528 if not bOk then begin
529 dwError := GetLastError;
530 case dwError of
531 ERROR_IO_PENDING : begin
532 dwWait := overlapped.WaitFor(FTimeout);
Jens Geyere9651362014-03-20 22:46:17 +0200533
Jens Geyer00645162018-02-01 23:38:10 +0100534 if (dwWait = WAIT_TIMEOUT) then begin
535 CancelIo( FPipe); // prevents possible AV on invalid overlapped ptr
536 raise TTransportExceptionTimedOut.Create('Pipe read timed out');
537 end;
Jens Geyere9651362014-03-20 22:46:17 +0200538
Jens Geyerd4df9172017-10-25 22:30:23 +0200539 if (dwWait <> WAIT_OBJECT_0)
540 or not GetOverlappedResult( FPipe, overlapped.Overlapped, cbRead, TRUE)
541 then raise TTransportExceptionUnknown.Create('Pipe read error');
542 end;
543
544 else
545 raise TTransportExceptionUnknown.Create(SysErrorMessage(dwError));
Jens Geyere9651362014-03-20 22:46:17 +0200546 end;
Jens Geyere9651362014-03-20 22:46:17 +0200547 end;
Jens Geyere9651362014-03-20 22:46:17 +0200548
Jens Geyerd4df9172017-10-25 22:30:23 +0200549 ASSERT( cbRead > 0); // see TTransportImpl.ReadAll()
550 ASSERT( cbRead <= DWORD(nRemaining));
551 Dec( nRemaining, cbRead);
Jens Geyer85827152018-01-12 21:20:59 +0100552 Inc( pData, cbRead);
Jens Geyerd4df9172017-10-25 22:30:23 +0200553 Inc( result, cbRead);
554 end;
Jens Geyere9651362014-03-20 22:46:17 +0200555end;
556
557
Jens Geyer06045cf2013-03-27 20:26:25 +0200558function TPipeStreamBase.ToArray: TBytes;
Roger Meier3bef8c22012-10-06 06:58:00 +0000559var bytes : LongInt;
560begin
561 SetLength( result, 0);
562 bytes := 0;
563
564 if IsOpen
565 and PeekNamedPipe( FPipe, nil, 0, nil, @bytes, nil)
566 and (bytes > 0)
567 then begin
568 SetLength( result, bytes);
569 Read( result, 0, bytes);
570 end;
571end;
572
573
Roger Meier79655fb2012-10-20 20:59:41 +0000574{ TNamedPipeStreamImpl }
Roger Meier3bef8c22012-10-06 06:58:00 +0000575
576
Jens Geyere9651362014-03-20 22:46:17 +0200577constructor TNamedPipeStreamImpl.Create( const aPipeName : string;
578 const aEnableOverlapped : Boolean;
579 const aShareMode: DWORD;
Roger Meier79655fb2012-10-20 20:59:41 +0000580 const aSecurityAttributes: PSecurityAttributes;
Jens Geyer653f0de2016-04-20 12:46:57 +0200581 const aTimeOut, aOpenTimeOut : DWORD);
Roger Meier3bef8c22012-10-06 06:58:00 +0000582begin
Jens Geyera019cda2019-11-09 23:24:52 +0100583 inherited Create( aEnableOverlapped, aTimeOut, aOpenTimeOut);
Roger Meier79655fb2012-10-20 20:59:41 +0000584
585 FPipeName := aPipeName;
586 FShareMode := aShareMode;
587 FSecurityAttribs := aSecurityAttributes;
588
589 if Copy(FPipeName,1,2) <> '\\'
590 then FPipeName := '\\.\pipe\' + FPipeName; // assume localhost
Roger Meier3bef8c22012-10-06 06:58:00 +0000591end;
592
593
Roger Meier79655fb2012-10-20 20:59:41 +0000594procedure TNamedPipeStreamImpl.Open;
595var hPipe : THandle;
Jens Geyer20a86d62021-04-02 11:34:08 +0200596 retries, timeout, dwErr, dwFlagsAndAttributes : DWORD;
Jens Geyerb89b5b92016-04-19 23:09:41 +0200597const INTERVAL = 10; // ms
Roger Meier79655fb2012-10-20 20:59:41 +0000598begin
599 if IsOpen then Exit;
600
Jens Geyer653f0de2016-04-20 12:46:57 +0200601 retries := Max( 1, Round( 1.0 * FOpenTimeOut / INTERVAL));
602 timeout := FOpenTimeOut;
Jens Geyerb89b5b92016-04-19 23:09:41 +0200603
604 // if the server hasn't gotten to the point where the pipe has been created, at least wait the timeout
605 // According to MSDN, if no instances of the specified named pipe exist, the WaitNamedPipe function
606 // returns IMMEDIATELY, regardless of the time-out value.
Jens Geyer653f0de2016-04-20 12:46:57 +0200607 // Always use INTERVAL, since WaitNamedPipe(0) defaults to some other value
Jens Geyerb89b5b92016-04-19 23:09:41 +0200608 while not WaitNamedPipe( PChar(FPipeName), INTERVAL) do begin
609 dwErr := GetLastError;
610 if dwErr <> ERROR_FILE_NOT_FOUND
Jens Geyere0e32402016-04-20 21:50:48 +0200611 then raise TTransportExceptionNotOpen.Create('Unable to open pipe, '+SysErrorMessage(dwErr));
Jens Geyerb89b5b92016-04-19 23:09:41 +0200612
613 if timeout <> INFINITE then begin
614 if (retries > 0)
615 then Dec(retries)
Jens Geyere0e32402016-04-20 21:50:48 +0200616 else raise TTransportExceptionNotOpen.Create('Unable to open pipe, timed out');
Jens Geyerb89b5b92016-04-19 23:09:41 +0200617 end;
618
619 Sleep(INTERVAL)
620 end;
621
Jens Geyer20a86d62021-04-02 11:34:08 +0200622 dwFlagsAndAttributes := FILE_FLAG_OVERLAPPED
623 or FILE_FLAG_WRITE_THROUGH // async+fast, please
624 or PREVENT_PIPE_IMPERSONATION;
625
Roger Meier79655fb2012-10-20 20:59:41 +0000626 // open that thingy
Roger Meier79655fb2012-10-20 20:59:41 +0000627 hPipe := CreateFile( PChar( FPipeName),
628 GENERIC_READ or GENERIC_WRITE,
Jens Geyer20a86d62021-04-02 11:34:08 +0200629 FShareMode, // sharing
630 FSecurityAttribs, // security attributes
631 OPEN_EXISTING, // opens existing pipe
632 dwFlagsAndAttributes, // flags + attribs
633 0); // no template file
Roger Meier79655fb2012-10-20 20:59:41 +0000634
635 if hPipe = INVALID_HANDLE_VALUE
Jens Geyere0e32402016-04-20 21:50:48 +0200636 then raise TTransportExceptionNotOpen.Create('Unable to open pipe, '+SysErrorMessage(GetLastError));
Roger Meier79655fb2012-10-20 20:59:41 +0000637
Roger Meier79655fb2012-10-20 20:59:41 +0000638 // everything fine
639 FPipe := hPipe;
640end;
641
642
643{ THandlePipeStreamImpl }
644
645
Jens Geyere9651362014-03-20 22:46:17 +0200646constructor THandlePipeStreamImpl.Create( const aPipeHandle : THandle;
647 const aOwnsHandle, aEnableOverlapped : Boolean;
648 const aTimeOut : DWORD);
Roger Meier79655fb2012-10-20 20:59:41 +0000649begin
Jens Geyera019cda2019-11-09 23:24:52 +0100650 inherited Create( aEnableOverlapped, aTimeout, aTimeout);
Roger Meier79655fb2012-10-20 20:59:41 +0000651
652 if aOwnsHandle
653 then FSrcHandle := aPipeHandle
654 else FSrcHandle := DuplicatePipeHandle( aPipeHandle);
655
656 Open;
657end;
658
659
660destructor THandlePipeStreamImpl.Destroy;
661begin
662 try
663 ClosePipeHandle( FSrcHandle);
664 finally
665 inherited Destroy;
666 end;
667end;
668
669
670procedure THandlePipeStreamImpl.Open;
671begin
672 if not IsOpen
673 then FPipe := DuplicatePipeHandle( FSrcHandle);
674end;
675
676
Jens Geyer06045cf2013-03-27 20:26:25 +0200677{ TPipeTransportBase }
Roger Meier79655fb2012-10-20 20:59:41 +0000678
679
Jens Geyer06045cf2013-03-27 20:26:25 +0200680function TPipeTransportBase.GetIsOpen: Boolean;
Roger Meier79655fb2012-10-20 20:59:41 +0000681begin
Jens Geyer0b20cc82013-03-07 20:47:01 +0100682 result := (FInputStream <> nil) and (FInputStream.IsOpen)
683 and (FOutputStream <> nil) and (FOutputStream.IsOpen);
Roger Meier79655fb2012-10-20 20:59:41 +0000684end;
685
686
Jens Geyer06045cf2013-03-27 20:26:25 +0200687procedure TPipeTransportBase.Open;
Roger Meier79655fb2012-10-20 20:59:41 +0000688begin
689 FInputStream.Open;
690 FOutputStream.Open;
691end;
692
693
Jens Geyer06045cf2013-03-27 20:26:25 +0200694procedure TPipeTransportBase.Close;
Roger Meier79655fb2012-10-20 20:59:41 +0000695begin
696 FInputStream.Close;
697 FOutputStream.Close;
698end;
699
700
Jens Geyer06045cf2013-03-27 20:26:25 +0200701{ TNamedPipeTransportClientEndImpl }
Roger Meier79655fb2012-10-20 20:59:41 +0000702
703
Jens Geyera019cda2019-11-09 23:24:52 +0100704constructor TNamedPipeTransportClientEndImpl.Create( const aPipeName : string;
705 const aShareMode: DWORD;
Jens Geyer41f47af2019-11-09 23:24:52 +0100706 const aSecurityAttributes: PSecurityAttributes;
707 const aTimeOut, aOpenTimeOut : DWORD;
Jens Geyera019cda2019-11-09 23:24:52 +0100708 const aConfig : IThriftConfiguration);
Roger Meier3bef8c22012-10-06 06:58:00 +0000709// Named pipe constructor
710begin
Jens Geyera019cda2019-11-09 23:24:52 +0100711 inherited Create( nil, nil, aConfig);
Jens Geyer653f0de2016-04-20 12:46:57 +0200712 FInputStream := TNamedPipeStreamImpl.Create( aPipeName, TRUE, aShareMode, aSecurityAttributes, aTimeOut, aOpenTimeOut);
Roger Meier3bef8c22012-10-06 06:58:00 +0000713 FOutputStream := FInputStream; // true for named pipes
Roger Meier3bef8c22012-10-06 06:58:00 +0000714end;
715
716
Jens Geyer41f47af2019-11-09 23:24:52 +0100717constructor TNamedPipeTransportClientEndImpl.Create( const aPipe : THandle;
718 const aOwnsHandle : Boolean;
719 const aTimeOut : DWORD;
Jens Geyera019cda2019-11-09 23:24:52 +0100720 const aConfig : IThriftConfiguration);
Roger Meier3bef8c22012-10-06 06:58:00 +0000721// Named pipe constructor
722begin
Jens Geyera019cda2019-11-09 23:24:52 +0100723 inherited Create( nil, nil, aConfig);
724 FInputStream := THandlePipeStreamImpl.Create( aPipe, aOwnsHandle, TRUE, aTimeOut);
Roger Meier3bef8c22012-10-06 06:58:00 +0000725 FOutputStream := FInputStream; // true for named pipes
Roger Meier3bef8c22012-10-06 06:58:00 +0000726end;
727
728
Jens Geyer06045cf2013-03-27 20:26:25 +0200729{ TNamedPipeTransportServerEndImpl }
Roger Meier79655fb2012-10-20 20:59:41 +0000730
731
Jens Geyer41f47af2019-11-09 23:24:52 +0100732constructor TNamedPipeTransportServerEndImpl.Create( const aPipe : THandle;
733 const aOwnsHandle : Boolean;
734 const aTimeOut : DWORD;
Jens Geyera019cda2019-11-09 23:24:52 +0100735 const aConfig : IThriftConfiguration);
Roger Meier79655fb2012-10-20 20:59:41 +0000736// Named pipe constructor
Roger Meier3bef8c22012-10-06 06:58:00 +0000737begin
Roger Meier79655fb2012-10-20 20:59:41 +0000738 FHandle := DuplicatePipeHandle( aPipe);
Jens Geyera019cda2019-11-09 23:24:52 +0100739 inherited Create( aPipe, aOwnsHandle, aTimeout, aConfig);
Roger Meier3bef8c22012-10-06 06:58:00 +0000740end;
741
742
Jens Geyer06045cf2013-03-27 20:26:25 +0200743procedure TNamedPipeTransportServerEndImpl.Close;
Roger Meier3bef8c22012-10-06 06:58:00 +0000744begin
Roger Meier79655fb2012-10-20 20:59:41 +0000745 FlushFileBuffers( FHandle);
746 DisconnectNamedPipe( FHandle); // force client off the pipe
747 ClosePipeHandle( FHandle);
Roger Meier3bef8c22012-10-06 06:58:00 +0000748
Roger Meier79655fb2012-10-20 20:59:41 +0000749 inherited Close;
Roger Meier3bef8c22012-10-06 06:58:00 +0000750end;
751
752
Jens Geyer06045cf2013-03-27 20:26:25 +0200753{ TAnonymousPipeTransportImpl }
Roger Meier3bef8c22012-10-06 06:58:00 +0000754
755
Jens Geyerdd074e72016-04-19 23:31:33 +0200756constructor TAnonymousPipeTransportImpl.Create( const aPipeRead, aPipeWrite : THandle;
Jens Geyer41f47af2019-11-09 23:24:52 +0100757 const aOwnsHandles : Boolean;
758 const aTimeOut : DWORD;
Jens Geyera019cda2019-11-09 23:24:52 +0100759 const aConfig : IThriftConfiguration);
Roger Meier3bef8c22012-10-06 06:58:00 +0000760// Anonymous pipe constructor
761begin
Jens Geyera019cda2019-11-09 23:24:52 +0100762 inherited Create( nil, nil, aConfig);
Jens Geyere9651362014-03-20 22:46:17 +0200763 // overlapped is not supported with AnonPipes, see MSDN
Jens Geyera019cda2019-11-09 23:24:52 +0100764 FInputStream := THandlePipeStreamImpl.Create( aPipeRead, aOwnsHandles, FALSE, aTimeout);
765 FOutputStream := THandlePipeStreamImpl.Create( aPipeWrite, aOwnsHandles, FALSE, aTimeout);
Roger Meier3bef8c22012-10-06 06:58:00 +0000766end;
767
768
Jens Geyer06045cf2013-03-27 20:26:25 +0200769{ TPipeServerTransportBase }
Roger Meier79655fb2012-10-20 20:59:41 +0000770
771
Jens Geyera019cda2019-11-09 23:24:52 +0100772constructor TPipeServerTransportBase.Create( const aConfig : IThriftConfiguration);
Jens Geyere9651362014-03-20 22:46:17 +0200773begin
Jens Geyera019cda2019-11-09 23:24:52 +0100774 inherited Create( aConfig);
Jens Geyere9651362014-03-20 22:46:17 +0200775 FStopServer := TEvent.Create(nil,TRUE,FALSE,''); // manual reset
776end;
777
778
779destructor TPipeServerTransportBase.Destroy;
780begin
781 try
782 FreeAndNil( FStopServer);
783 finally
784 inherited Destroy;
785 end;
786end;
787
788
789function TPipeServerTransportBase.QueryStopServer : Boolean;
790begin
791 result := (FStopServer = nil)
792 or (FStopServer.WaitFor(0) <> wrTimeout);
793end;
794
795
Jens Geyer06045cf2013-03-27 20:26:25 +0200796procedure TPipeServerTransportBase.Listen;
Roger Meier3bef8c22012-10-06 06:58:00 +0000797begin
Jens Geyere9651362014-03-20 22:46:17 +0200798 FStopServer.ResetEvent;
Roger Meier3bef8c22012-10-06 06:58:00 +0000799end;
800
801
Jens Geyer06045cf2013-03-27 20:26:25 +0200802procedure TPipeServerTransportBase.Close;
803begin
Jens Geyere9651362014-03-20 22:46:17 +0200804 FStopServer.SetEvent;
Jens Geyer06045cf2013-03-27 20:26:25 +0200805 InternalClose;
806end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000807
808
Jens Geyer06045cf2013-03-27 20:26:25 +0200809{ TAnonymousPipeServerTransportImpl }
810
Jens Geyera019cda2019-11-09 23:24:52 +0100811constructor TAnonymousPipeServerTransportImpl.Create( const aBufsize : Cardinal;
812 const aTimeOut : DWORD;
813 const aConfig : IThriftConfiguration);
Roger Meier3bef8c22012-10-06 06:58:00 +0000814// Anonymous pipe CTOR
815begin
Jens Geyera019cda2019-11-09 23:24:52 +0100816 inherited Create(aConfig);
Roger Meier3bef8c22012-10-06 06:58:00 +0000817 FBufsize := aBufSize;
Roger Meier79655fb2012-10-20 20:59:41 +0000818 FReadHandle := INVALID_HANDLE_VALUE;
Roger Meier3bef8c22012-10-06 06:58:00 +0000819 FWriteHandle := INVALID_HANDLE_VALUE;
820 FClientAnonRead := INVALID_HANDLE_VALUE;
821 FClientAnonWrite := INVALID_HANDLE_VALUE;
Jens Geyerdd074e72016-04-19 23:31:33 +0200822 FTimeOut := aTimeOut;
Roger Meier3bef8c22012-10-06 06:58:00 +0000823
824 // The anonymous pipe needs to be created first so that the server can
825 // pass the handles on to the client before the serve (acceptImpl)
826 // blocking call.
827 if not CreateAnonPipe
Jens Geyere0e32402016-04-20 21:50:48 +0200828 then raise TTransportExceptionNotOpen.Create(ClassName+'.Create() failed');
Roger Meier3bef8c22012-10-06 06:58:00 +0000829end;
830
831
Jens Geyer01640402013-09-25 21:12:21 +0200832function TAnonymousPipeServerTransportImpl.Accept(const fnAccepting: TProc): ITransport;
Roger Meier3bef8c22012-10-06 06:58:00 +0000833var buf : Byte;
834 br : DWORD;
Roger Meier3bef8c22012-10-06 06:58:00 +0000835begin
Jens Geyer01640402013-09-25 21:12:21 +0200836 if Assigned(fnAccepting)
837 then fnAccepting();
838
Roger Meier79655fb2012-10-20 20:59:41 +0000839 // This 0-byte read serves merely as a blocking call.
840 if not ReadFile( FReadHandle, buf, 0, br, nil)
841 and (GetLastError() <> ERROR_MORE_DATA)
Jens Geyere0e32402016-04-20 21:50:48 +0200842 then raise TTransportExceptionNotOpen.Create('TServerPipe unable to initiate pipe communication');
Jens Geyer06045cf2013-03-27 20:26:25 +0200843
844 // create the transport impl
Jens Geyera019cda2019-11-09 23:24:52 +0100845 result := TAnonymousPipeTransportImpl.Create( FReadHandle, FWriteHandle, FALSE, FTimeOut, Configuration);
Roger Meier3bef8c22012-10-06 06:58:00 +0000846end;
847
848
Jens Geyer06045cf2013-03-27 20:26:25 +0200849procedure TAnonymousPipeServerTransportImpl.InternalClose;
Roger Meier3bef8c22012-10-06 06:58:00 +0000850begin
Roger Meier79655fb2012-10-20 20:59:41 +0000851 ClosePipeHandle( FReadHandle);
852 ClosePipeHandle( FWriteHandle);
853 ClosePipeHandle( FClientAnonRead);
854 ClosePipeHandle( FClientAnonWrite);
Roger Meier3bef8c22012-10-06 06:58:00 +0000855end;
856
857
Jens Geyer06045cf2013-03-27 20:26:25 +0200858function TAnonymousPipeServerTransportImpl.ReadHandle : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000859begin
Roger Meier79655fb2012-10-20 20:59:41 +0000860 result := FReadHandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000861end;
862
863
Jens Geyer06045cf2013-03-27 20:26:25 +0200864function TAnonymousPipeServerTransportImpl.WriteHandle : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000865begin
866 result := FWriteHandle;
867end;
868
869
Jens Geyer06045cf2013-03-27 20:26:25 +0200870function TAnonymousPipeServerTransportImpl.ClientAnonRead : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000871begin
872 result := FClientAnonRead;
873end;
874
875
Jens Geyer06045cf2013-03-27 20:26:25 +0200876function TAnonymousPipeServerTransportImpl.ClientAnonWrite : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000877begin
878 result := FClientAnonWrite;
879end;
880
881
Jens Geyer06045cf2013-03-27 20:26:25 +0200882function TAnonymousPipeServerTransportImpl.CreateAnonPipe : Boolean;
Roger Meier79655fb2012-10-20 20:59:41 +0000883var sd : PSECURITY_DESCRIPTOR;
884 sa : SECURITY_ATTRIBUTES; //TSecurityAttributes;
885 hCAR, hPipeW, hCAW, hPipe : THandle;
886begin
Roger Meier79655fb2012-10-20 20:59:41 +0000887 sd := PSECURITY_DESCRIPTOR( LocalAlloc( LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH));
Jens Geyerb64a7742013-01-23 20:58:47 +0100888 try
889 Win32Check( InitializeSecurityDescriptor( sd, SECURITY_DESCRIPTOR_REVISION));
890 Win32Check( SetSecurityDescriptorDacl( sd, TRUE, nil, FALSE));
Roger Meier79655fb2012-10-20 20:59:41 +0000891
Jens Geyerb64a7742013-01-23 20:58:47 +0100892 sa.nLength := sizeof( sa);
893 sa.lpSecurityDescriptor := sd;
894 sa.bInheritHandle := TRUE; //allow passing handle to child
Roger Meier79655fb2012-10-20 20:59:41 +0000895
Jens Geyer17c3ad92017-09-05 20:31:27 +0200896 Result := CreatePipe( hCAR, hPipeW, @sa, FBufSize); //create stdin pipe
897 if not Result then begin //create stdin pipe
Jens Geyere0e32402016-04-20 21:50:48 +0200898 raise TTransportExceptionNotOpen.Create('TServerPipe CreatePipe (anon) failed, '+SysErrorMessage(GetLastError));
Jens Geyerb64a7742013-01-23 20:58:47 +0100899 Exit;
900 end;
901
Jens Geyer17c3ad92017-09-05 20:31:27 +0200902 Result := CreatePipe( hPipe, hCAW, @sa, FBufSize); //create stdout pipe
903 if not Result then begin //create stdout pipe
Jens Geyerb64a7742013-01-23 20:58:47 +0100904 CloseHandle( hCAR);
905 CloseHandle( hPipeW);
Jens Geyere0e32402016-04-20 21:50:48 +0200906 raise TTransportExceptionNotOpen.Create('TServerPipe CreatePipe (anon) failed, '+SysErrorMessage(GetLastError));
Jens Geyerb64a7742013-01-23 20:58:47 +0100907 Exit;
908 end;
909
910 FClientAnonRead := hCAR;
911 FClientAnonWrite := hCAW;
912 FReadHandle := hPipe;
913 FWriteHandle := hPipeW;
Jens Geyerb64a7742013-01-23 20:58:47 +0100914 finally
915 if sd <> nil then LocalFree( Cardinal(sd));
Roger Meier79655fb2012-10-20 20:59:41 +0000916 end;
Roger Meier79655fb2012-10-20 20:59:41 +0000917end;
918
919
Jens Geyer06045cf2013-03-27 20:26:25 +0200920{ TNamedPipeServerTransportImpl }
Roger Meier79655fb2012-10-20 20:59:41 +0000921
922
Jens Geyera019cda2019-11-09 23:24:52 +0100923constructor TNamedPipeServerTransportImpl.Create( const aPipename : string;
Jens Geyer20a86d62021-04-02 11:34:08 +0200924 const aFlags : TNamedPipeFlags;
925 const aConfig : IThriftConfiguration;
926 const aBufsize, aMaxConns, aTimeOut : Cardinal);
Roger Meier79655fb2012-10-20 20:59:41 +0000927// Named Pipe CTOR
928begin
Jens Geyera019cda2019-11-09 23:24:52 +0100929 inherited Create( aConfig);
Jens Geyer06045cf2013-03-27 20:26:25 +0200930 FPipeName := aPipename;
931 FBufsize := aBufSize;
932 FMaxConns := Max( 1, Min( PIPE_UNLIMITED_INSTANCES, aMaxConns));
933 FHandle := INVALID_HANDLE_VALUE;
934 FTimeout := aTimeOut;
935 FConnected := FALSE;
Jens Geyera019cda2019-11-09 23:24:52 +0100936 ASSERT( FTimeout > 0);
Roger Meier79655fb2012-10-20 20:59:41 +0000937
Jens Geyer20a86d62021-04-02 11:34:08 +0200938 FOnlyLocalClients := (TNamedPipeFlag.OnlyLocalClients in aFlags);
939
Roger Meier79655fb2012-10-20 20:59:41 +0000940 if Copy(FPipeName,1,2) <> '\\'
941 then FPipeName := '\\.\pipe\' + FPipeName; // assume localhost
942end;
943
944
Jens Geyer20a86d62021-04-02 11:34:08 +0200945constructor TNamedPipeServerTransportImpl.Create( const aPipename : string;
946 const aBufsize, aMaxConns, aTimeOut : Cardinal;
947 const aConfig : IThriftConfiguration);
948// Named Pipe CTOR (deprecated)
949begin
950 {$WARN SYMBOL_DEPRECATED OFF} // Delphi XE emits a false warning here
951 Create( aPipeName, [], aConfig, aBufsize, aMaxConns, aTimeOut);
952 {$WARN SYMBOL_DEPRECATED ON}
953end;
954
955
Jens Geyer01640402013-09-25 21:12:21 +0200956function TNamedPipeServerTransportImpl.Accept(const fnAccepting: TProc): ITransport;
Jens Geyer06045cf2013-03-27 20:26:25 +0200957var dwError, dwWait, dwDummy : DWORD;
Jens Geyere9651362014-03-20 22:46:17 +0200958 overlapped : IOverlappedHelper;
959 handles : array[0..1] of THandle;
Jens Geyer01640402013-09-25 21:12:21 +0200960begin
Jens Geyere9651362014-03-20 22:46:17 +0200961 overlapped := TOverlappedHelperImpl.Create;
Jens Geyer01640402013-09-25 21:12:21 +0200962
Jens Geyere9651362014-03-20 22:46:17 +0200963 ASSERT( not FConnected);
Jens Geyer2ad6c302015-02-26 19:38:53 +0100964 CreateNamedPipe;
Jens Geyere9651362014-03-20 22:46:17 +0200965 while not FConnected do begin
Jens Geyer2ad6c302015-02-26 19:38:53 +0100966
Jens Geyer00645162018-02-01 23:38:10 +0100967 if QueryStopServer then begin
968 InternalClose;
969 Abort;
970 end;
Roger Meier79655fb2012-10-20 20:59:41 +0000971
Jens Geyere9651362014-03-20 22:46:17 +0200972 if Assigned(fnAccepting)
973 then fnAccepting();
Jens Geyer01640402013-09-25 21:12:21 +0200974
Jens Geyere9651362014-03-20 22:46:17 +0200975 // Wait for the client to connect; if it succeeds, the
976 // function returns a nonzero value. If the function returns
977 // zero, GetLastError should return ERROR_PIPE_CONNECTED.
978 if ConnectNamedPipe( Handle, overlapped.OverlappedPtr) then begin
979 FConnected := TRUE;
980 Break;
Jens Geyer01640402013-09-25 21:12:21 +0200981 end;
Roger Meier79655fb2012-10-20 20:59:41 +0000982
Jens Geyere9651362014-03-20 22:46:17 +0200983 // ConnectNamedPipe() returns FALSE for OverlappedIO, even if connected.
984 // We have to check GetLastError() explicitly to find out
985 dwError := GetLastError;
986 case dwError of
987 ERROR_PIPE_CONNECTED : begin
988 FConnected := not QueryStopServer; // special case: pipe immediately connected
989 end;
Roger Meier79655fb2012-10-20 20:59:41 +0000990
Jens Geyere9651362014-03-20 22:46:17 +0200991 ERROR_IO_PENDING : begin
992 handles[0] := overlapped.WaitHandle;
993 handles[1] := FStopServer.Handle;
994 dwWait := WaitForMultipleObjects( 2, @handles, FALSE, FTimeout);
995 FConnected := (dwWait = WAIT_OBJECT_0)
996 and GetOverlappedResult( Handle, overlapped.Overlapped, dwDummy, TRUE)
997 and not QueryStopServer;
998 end;
999
1000 else
1001 InternalClose;
Jens Geyere0e32402016-04-20 21:50:48 +02001002 raise TTransportExceptionNotOpen.Create('Client connection failed');
Jens Geyere9651362014-03-20 22:46:17 +02001003 end;
Roger Meier79655fb2012-10-20 20:59:41 +00001004 end;
Jens Geyere9651362014-03-20 22:46:17 +02001005
1006 // create the transport impl
1007 result := CreateTransportInstance;
Roger Meier79655fb2012-10-20 20:59:41 +00001008end;
1009
1010
Jens Geyer06045cf2013-03-27 20:26:25 +02001011function TNamedPipeServerTransportImpl.CreateTransportInstance : ITransport;
1012// create the transport impl
1013var hPipe : THandle;
Roger Meier79655fb2012-10-20 20:59:41 +00001014begin
Jens Geyer06045cf2013-03-27 20:26:25 +02001015 hPipe := THandle( InterlockedExchangePointer( Pointer(FHandle), Pointer(INVALID_HANDLE_VALUE)));
1016 try
1017 FConnected := FALSE;
Jens Geyera019cda2019-11-09 23:24:52 +01001018 result := TNamedPipeTransportServerEndImpl.Create( hPipe, TRUE, FTimeout, Configuration);
Jens Geyer06045cf2013-03-27 20:26:25 +02001019 except
Jens Geyer01640402013-09-25 21:12:21 +02001020 ClosePipeHandle(hPipe);
1021 raise;
1022 end;
Roger Meier79655fb2012-10-20 20:59:41 +00001023end;
1024
1025
Jens Geyer06045cf2013-03-27 20:26:25 +02001026procedure TNamedPipeServerTransportImpl.InternalClose;
1027var hPipe : THandle;
1028begin
1029 hPipe := THandle( InterlockedExchangePointer( Pointer(FHandle), Pointer(INVALID_HANDLE_VALUE)));
1030 if hPipe = INVALID_HANDLE_VALUE then Exit;
1031
1032 try
1033 if FConnected
1034 then FlushFileBuffers( hPipe)
1035 else CancelIo( hPipe);
1036 DisconnectNamedPipe( hPipe);
1037 finally
1038 ClosePipeHandle( hPipe);
1039 FConnected := FALSE;
1040 end;
1041end;
1042
1043
1044function TNamedPipeServerTransportImpl.Handle : THandle;
1045begin
1046 {$IFDEF WIN64}
Jens Geyer9f7f11e2016-04-14 21:37:11 +02001047 result := THandle( InterlockedExchangeAdd64( Int64(FHandle), 0));
Jens Geyer06045cf2013-03-27 20:26:25 +02001048 {$ELSE}
1049 result := THandle( InterlockedExchangeAdd( Integer(FHandle), 0));
1050 {$ENDIF}
1051end;
1052
1053
1054function TNamedPipeServerTransportImpl.CreateNamedPipe : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +00001055var SIDAuthWorld : SID_IDENTIFIER_AUTHORITY ;
1056 everyone_sid : PSID;
1057 ea : EXPLICIT_ACCESS;
1058 acl : PACL;
1059 sd : PSECURITY_DESCRIPTOR;
1060 sa : SECURITY_ATTRIBUTES;
Jens Geyer20a86d62021-04-02 11:34:08 +02001061 dwPipeModeXtra : DWORD;
Roger Meier3bef8c22012-10-06 06:58:00 +00001062const
1063 SECURITY_WORLD_SID_AUTHORITY : TSIDIdentifierAuthority = (Value : (0,0,0,0,0,1));
1064 SECURITY_WORLD_RID = $00000000;
1065begin
Jens Geyerb64a7742013-01-23 20:58:47 +01001066 sd := nil;
Roger Meier3bef8c22012-10-06 06:58:00 +00001067 everyone_sid := nil;
Jens Geyerb64a7742013-01-23 20:58:47 +01001068 try
Jens Geyer06045cf2013-03-27 20:26:25 +02001069 ASSERT( (FHandle = INVALID_HANDLE_VALUE) and not FConnected);
1070
Jens Geyerb64a7742013-01-23 20:58:47 +01001071 // Windows - set security to allow non-elevated apps
1072 // to access pipes created by elevated apps.
1073 SIDAuthWorld := SECURITY_WORLD_SID_AUTHORITY;
1074 AllocateAndInitializeSid( SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, everyone_sid);
Roger Meier3bef8c22012-10-06 06:58:00 +00001075
Jens Geyerb64a7742013-01-23 20:58:47 +01001076 ZeroMemory( @ea, SizeOf(ea));
1077 ea.grfAccessPermissions := GENERIC_ALL; //SPECIFIC_RIGHTS_ALL or STANDARD_RIGHTS_ALL;
1078 ea.grfAccessMode := SET_ACCESS;
1079 ea.grfInheritance := NO_INHERITANCE;
1080 ea.Trustee.TrusteeForm := TRUSTEE_IS_SID;
1081 ea.Trustee.TrusteeType := TRUSTEE_IS_WELL_KNOWN_GROUP;
1082 ea.Trustee.ptstrName := PChar(everyone_sid);
Roger Meier3bef8c22012-10-06 06:58:00 +00001083
Jens Geyerb64a7742013-01-23 20:58:47 +01001084 acl := nil;
1085 SetEntriesInAcl( 1, @ea, nil, acl);
Roger Meier3bef8c22012-10-06 06:58:00 +00001086
Jens Geyerb64a7742013-01-23 20:58:47 +01001087 sd := PSECURITY_DESCRIPTOR( LocalAlloc( LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH));
1088 Win32Check( InitializeSecurityDescriptor( sd, SECURITY_DESCRIPTOR_REVISION));
1089 Win32Check( SetSecurityDescriptorDacl( sd, TRUE, acl, FALSE));
Roger Meier3bef8c22012-10-06 06:58:00 +00001090
Jens Geyerb64a7742013-01-23 20:58:47 +01001091 sa.nLength := SizeOf(sa);
1092 sa.lpSecurityDescriptor := sd;
1093 sa.bInheritHandle := FALSE;
Roger Meier3bef8c22012-10-06 06:58:00 +00001094
Jens Geyer20a86d62021-04-02 11:34:08 +02001095 // any extra flags we want to add to dwPipeMode
1096 dwPipeModeXtra := 0;
1097 if FOnlyLocalClients then dwPipeModeXtra := dwPipeModeXtra or PIPE_REJECT_REMOTE_CLIENTS;
1098
Jens Geyerb64a7742013-01-23 20:58:47 +01001099 // Create an instance of the named pipe
Jens Geyer9f7f11e2016-04-14 21:37:11 +02001100 {$IFDEF OLD_UNIT_NAMES}
1101 result := Windows.CreateNamedPipe(
1102 {$ELSE}
1103 result := Winapi.Windows.CreateNamedPipe(
1104 {$ENDIF}
Jens Geyer20a86d62021-04-02 11:34:08 +02001105 PChar( FPipeName), // pipe name
1106 PIPE_ACCESS_DUPLEX or FILE_FLAG_OVERLAPPED, // read/write access + async mode
1107 PIPE_TYPE_BYTE or PIPE_READMODE_BYTE or dwPipeModeXtra, // byte type pipe + byte read mode + extras
1108 FMaxConns, // max. instances
1109 FBufSize, // output buffer size
1110 FBufSize, // input buffer size
1111 FTimeout, // time-out, see MSDN
1112 @sa // default security attribute
Jens Geyer9f7f11e2016-04-14 21:37:11 +02001113 );
Roger Meier3bef8c22012-10-06 06:58:00 +00001114
Jens Geyer06045cf2013-03-27 20:26:25 +02001115 if( result <> INVALID_HANDLE_VALUE)
1116 then InterlockedExchangePointer( Pointer(FHandle), Pointer(result))
Jens Geyere0e32402016-04-20 21:50:48 +02001117 else raise TTransportExceptionNotOpen.Create('CreateNamedPipe() failed ' + IntToStr(GetLastError));
Jens Geyerb64a7742013-01-23 20:58:47 +01001118
1119 finally
1120 if sd <> nil then LocalFree( Cardinal( sd));
1121 if acl <> nil then LocalFree( Cardinal( acl));
1122 if everyone_sid <> nil then FreeSid(everyone_sid);
Roger Meier3bef8c22012-10-06 06:58:00 +00001123 end;
Roger Meier3bef8c22012-10-06 06:58:00 +00001124end;
1125
1126
Roger Meier3bef8c22012-10-06 06:58:00 +00001127
1128end.
1129
1130
1131