1 (*
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 *)
19 unit Thrift.Transport.Pipes;
20
21 {$WARN SYMBOL_PLATFORM OFF}
22 {$I Thrift.Defines.inc}
23
24 interface
25
26 uses
27 {$IFDEF OLD_UNIT_NAMES}
28 Windows, SysUtils, Math, AccCtrl, AclAPI, SyncObjs,
29 {$ELSE}
30 Winapi.Windows, System.SysUtils, System.Math, Winapi.AccCtrl, Winapi.AclAPI, System.SyncObjs,
31 {$ENDIF}
32 Thrift.Configuration,
33 Thrift.Transport,
34 Thrift.Utils,
35 Thrift.Stream;
36
37 const
38 DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT = 10; // default: fail fast on open
39
40
41 type
42 //--- Pipe Streams ---
43
44
45 TPipeStreamBase = class( TThriftStreamImpl)
46 strict protected
47 FPipe : THandle;
48 FTimeout : DWORD;
49 FOpenTimeOut : DWORD; // separate value to allow for fail-fast-on-open scenarios
50 FOverlapped : Boolean;
51
52 procedure Write( const pBuf : Pointer; offset, count : Integer); override;
Readnull53 function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
54 //procedure Open; override; - see derived classes
55 procedure Close; override;
56 procedure Flush; override;
57
ReadDirectnull58 function ReadDirect( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; overload;
ReadOverlappednull59 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;
62
IsOpennull63 function IsOpen: Boolean; override;
ToArraynull64 function ToArray: TBytes; override;
65 public
66 constructor Create( aEnableOverlapped : Boolean;
67 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
68 const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT
69 ); reintroduce; overload;
70
71 destructor Destroy; override;
72 end;
73
74
75 TNamedPipeStreamImpl = class sealed( TPipeStreamBase)
76 strict private
77 FPipeName : string;
78 FShareMode : DWORD;
79 FSecurityAttribs : PSecurityAttributes;
80
81 strict protected
82 procedure Open; override;
83
84 public
85 constructor Create( const aPipeName : string;
86 const aEnableOverlapped : Boolean;
87 const aShareMode: DWORD = 0;
88 const aSecurityAttributes: PSecurityAttributes = nil;
89 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
90 const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT
91 ); reintroduce; overload;
92 end;
93
94
95 THandlePipeStreamImpl = class sealed( TPipeStreamBase)
96 strict private
97 FSrcHandle : THandle;
98
99 strict protected
100 procedure Open; override;
101
102 public
103 constructor Create( const aPipeHandle : THandle;
104 const aOwnsHandle, aEnableOverlapped : Boolean;
105 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT
106 ); reintroduce; overload;
107
108 destructor Destroy; override;
109 end;
110
111
112 //--- Pipe Transports ---
113
114
115 IPipeTransport = interface( IStreamTransport)
116 ['{5E05CC85-434F-428F-BFB2-856A168B5558}']
117 end;
118
119
120 TPipeTransportBase = class( TStreamTransportImpl, IPipeTransport)
121 strict protected
122 // ITransport
GetIsOpennull123 function GetIsOpen: Boolean; override;
124 procedure Open; override;
125 procedure Close; override;
126 end;
127
128
129 TNamedPipeTransportClientEndImpl = class( TPipeTransportBase)
130 public
131 // Named pipe constructors
132 constructor Create( const aPipe : THandle;
133 const aOwnsHandle : Boolean;
134 const aTimeOut : DWORD;
135 const aConfig : IThriftConfiguration = nil
136 ); reintroduce; overload;
137
138 constructor Create( const aPipeName : string;
139 const aShareMode: DWORD = 0;
140 const aSecurityAttributes: PSecurityAttributes = nil;
141 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
142 const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT;
143 const aConfig : IThriftConfiguration = nil
144 ); reintroduce; overload;
145 end;
146
147
148 TNamedPipeTransportServerEndImpl = class( TNamedPipeTransportClientEndImpl)
149 strict private
150 FHandle : THandle;
151 strict protected
152 // ITransport
153 procedure Close; override;
154 public
155 constructor Create( const aPipe : THandle;
156 const aOwnsHandle : Boolean;
157 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
158 const aConfig : IThriftConfiguration = nil
159 ); reintroduce; overload;
160
161 end;
162
163
164 TAnonymousPipeTransportImpl = class( TPipeTransportBase)
165 public
166 // Anonymous pipe constructor
167 constructor Create( const aPipeRead, aPipeWrite : THandle;
168 const aOwnsHandles : Boolean;
169 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
170 const aConfig : IThriftConfiguration = nil
171 ); reintroduce; overload;
172 end;
173
174
175 //--- Server Transports ---
176
177
178 IAnonymousPipeServerTransport = interface( IServerTransport)
179 ['{7AEE6793-47B9-4E49-981A-C39E9108E9AD}']
180 // Server side anonymous pipe ends
ReadHandlenull181 function ReadHandle : THandle;
WriteHandlenull182 function WriteHandle : THandle;
183 // Client side anonymous pipe ends
ClientAnonReadnull184 function ClientAnonRead : THandle;
ClientAnonWritenull185 function ClientAnonWrite : THandle;
186 end;
187
188
189 INamedPipeServerTransport = interface( IServerTransport)
190 ['{9DF9EE48-D065-40AF-8F67-D33037D3D960}']
Handlenull191 function Handle : THandle;
192 end;
193
194
195 TPipeServerTransportBase = class( TServerTransportImpl)
196 strict protected
197 FStopServer : TEvent;
198 procedure InternalClose; virtual; abstract;
QueryStopServernull199 function QueryStopServer : Boolean;
200 public
201 constructor Create( const aConfig : IThriftConfiguration);
202 destructor Destroy; override;
203 procedure Listen; override;
204 procedure Close; override;
205 end;
206
207
208 TAnonymousPipeServerTransportImpl = class( TPipeServerTransportBase, IAnonymousPipeServerTransport)
209 strict private
210 FBufSize : DWORD;
211
212 // Server side anonymous pipe handles
213 FReadHandle,
214 FWriteHandle : THandle;
215
216 //Client side anonymous pipe handles
217 FClientAnonRead,
218 FClientAnonWrite : THandle;
219
220 FTimeOut: DWORD;
221 strict protected
Acceptnull222 function Accept(const fnAccepting: TProc): ITransport; override;
223
CreateAnonPipenull224 function CreateAnonPipe : Boolean;
225
226 // IAnonymousPipeServerTransport
ReadHandlenull227 function ReadHandle : THandle;
WriteHandlenull228 function WriteHandle : THandle;
ClientAnonReadnull229 function ClientAnonRead : THandle;
ClientAnonWritenull230 function ClientAnonWrite : THandle;
231
232 procedure InternalClose; override;
233
234 public
235 constructor Create( const aBufsize : Cardinal = 4096;
236 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
237 const aConfig : IThriftConfiguration = nil
238 ); reintroduce; overload;
239 end;
240
241
242 TNamedPipeFlag = (
243 OnlyLocalClients // sets PIPE_REJECT_REMOTE_CLIENTS
244 );
245 TNamedPipeFlags = set of TNamedPipeFlag;
246
247
248 TNamedPipeServerTransportImpl = class( TPipeServerTransportBase, INamedPipeServerTransport)
249 strict private
250 FPipeName : string;
251 FMaxConns : DWORD;
252 FBufSize : DWORD;
253 FTimeout : DWORD;
254 FHandle : THandle;
255 FConnected : Boolean;
256 FOnlyLocalClients : Boolean;
257
258 strict protected
Acceptnull259 function Accept(const fnAccepting: TProc): ITransport; override;
CreateNamedPipenull260 function CreateNamedPipe : THandle;
CreateTransportInstancenull261 function CreateTransportInstance : ITransport;
262
263 // INamedPipeServerTransport
Handlenull264 function Handle : THandle;
265 procedure InternalClose; override;
266
267 public
268 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
273 ); 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
281 ); reintroduce; overload;
282 end;
283
284
285 implementation
286
287 const
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
305
306 procedure ClosePipeHandle( var hPipe : THandle);
307 begin
308 if hPipe <> INVALID_HANDLE_VALUE
309 then try
310 CloseHandle( hPipe);
311 finally
312 hPipe := INVALID_HANDLE_VALUE;
313 end;
314 end;
315
316
317 function DuplicatePipeHandle( const hSource : THandle) : THandle;
318 begin
319 if not DuplicateHandle( GetCurrentProcess, hSource,
320 GetCurrentProcess, @result,
321 0, FALSE, DUPLICATE_SAME_ACCESS)
322 then raise TTransportExceptionNotOpen.Create('DuplicateHandle: '+SysErrorMessage(GetLastError));
323 end;
324
325
326
327 { TPipeStreamBase }
328
329
330 constructor TPipeStreamBase.Create( aEnableOverlapped : Boolean; const aTimeOut, aOpenTimeOut : DWORD);
331 begin
332 inherited Create;
333 FPipe := INVALID_HANDLE_VALUE;
334 FTimeout := aTimeOut;
335 FOpenTimeOut := aOpenTimeOut;
336 FOverlapped := aEnableOverlapped;
337 ASSERT( FTimeout > 0); // FOpenTimeout may be 0
338 end;
339
340
341 destructor TPipeStreamBase.Destroy;
342 begin
343 try
344 Close;
345 finally
346 inherited Destroy;
347 end;
348 end;
349
350
351 procedure TPipeStreamBase.Close;
352 begin
353 ClosePipeHandle( FPipe);
354 end;
355
356
357 procedure TPipeStreamBase.Flush;
358 begin
359 FlushFileBuffers( FPipe);
360 end;
361
362
IsOpennull363 function TPipeStreamBase.IsOpen: Boolean;
364 begin
365 result := (FPipe <> INVALID_HANDLE_VALUE);
366 end;
367
368
369 procedure TPipeStreamBase.Write( const pBuf : Pointer; offset, count : Integer);
370 begin
371 if FOverlapped
372 then WriteOverlapped( pBuf, offset, count)
373 else WriteDirect( pBuf, offset, count);
374 end;
375
376
Readnull377 function TPipeStreamBase.Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
378 begin
379 if FOverlapped
380 then result := ReadOverlapped( pBuf, buflen, offset, count)
381 else result := ReadDirect( pBuf, buflen, offset, count);
382 end;
383
384
385 procedure TPipeStreamBase.WriteDirect( const pBuf : Pointer; offset: Integer; count: Integer);
386 var cbWritten, nBytes : DWORD;
387 pData : PByte;
388 begin
389 if not IsOpen
390 then raise TTransportExceptionNotOpen.Create('Called write on non-open pipe');
391
392 // 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
396 pData := pBuf;
397 Inc( pData, offset);
398 while nBytes > 0 do begin
399 if not WriteFile( FPipe, pData^, nBytes, cbWritten, nil)
400 then raise TTransportExceptionNotOpen.Create('Write to pipe failed');
401
402 Inc( pData, cbWritten);
403 Dec( count, cbWritten);
404 nBytes := Min( nBytes, count);
405 end;
406 end;
407
408
409 procedure TPipeStreamBase.WriteOverlapped( const pBuf : Pointer; offset: Integer; count: Integer);
410 var cbWritten, dwWait, dwError, nBytes : DWORD;
411 overlapped : IOverlappedHelper;
412 pData : PByte;
413 begin
414 if not IsOpen
415 then raise TTransportExceptionNotOpen.Create('Called write on non-open pipe');
416
417 // 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
421 pData := pBuf;
422 Inc( pData, offset);
423 while nBytes > 0 do begin
424 overlapped := TOverlappedHelperImpl.Create;
425 if not WriteFile( FPipe, pData^, nBytes, cbWritten, overlapped.OverlappedPtr)
426 then begin
427 dwError := GetLastError;
428 case dwError of
429 ERROR_IO_PENDING : begin
430 dwWait := overlapped.WaitFor(FTimeout);
431
432 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;
436
437 if (dwWait <> WAIT_OBJECT_0)
438 or not GetOverlappedResult( FPipe, overlapped.Overlapped, cbWritten, TRUE)
439 then raise TTransportExceptionUnknown.Create('Pipe write error');
440 end;
441
442 else
443 raise TTransportExceptionUnknown.Create(SysErrorMessage(dwError));
444 end;
445 end;
446
447 ASSERT( DWORD(nBytes) = cbWritten);
448
449 Inc( pData, cbWritten);
450 Dec( count, cbWritten);
451 nBytes := Min( nBytes, count);
452 end;
453 end;
454
455
ReadDirectnull456 function TPipeStreamBase.ReadDirect( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
457 var cbRead, dwErr, nRemaining : DWORD;
458 bytes, retries : LongInt;
459 bOk : Boolean;
460 pData : PByte;
461 const INTERVAL = 10; // ms
462 begin
463 if not IsOpen
464 then raise TTransportExceptionNotOpen.Create('Called read on non-open pipe');
465
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
472 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
484 end;
485
486 Dec( retries);
487 if retries > 0
488 then Sleep( INTERVAL)
489 else raise TTransportExceptionTimedOut.Create('Pipe read timed out');
490 end;
491 end;
492
493 result := 0;
494 nRemaining := count;
495 pData := pBuf;
496 Inc( pData, offset);
497 while nRemaining > 0 do begin
498 // read the data (or block INFINITE-ly)
499 bOk := ReadFile( FPipe, pData^, nRemaining, cbRead, nil);
500 if (not bOk) and (GetLastError() <> ERROR_MORE_DATA)
501 then Break; // No more data, possibly because client disconnected.
502
503 Dec( nRemaining, cbRead);
504 Inc( pData, cbRead);
505 Inc( result, cbRead);
506 end;
507 end;
508
509
ReadOverlappednull510 function TPipeStreamBase.ReadOverlapped( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
511 var cbRead, dwWait, dwError, nRemaining : DWORD;
512 bOk : Boolean;
513 overlapped : IOverlappedHelper;
514 pData : PByte;
515 begin
516 if not IsOpen
517 then raise TTransportExceptionNotOpen.Create('Called read on non-open pipe');
518
519 result := 0;
520 nRemaining := count;
521 pData := pBuf;
522 Inc( pData, offset);
523 while nRemaining > 0 do begin
524 overlapped := TOverlappedHelperImpl.Create;
525
526 // read the data
527 bOk := ReadFile( FPipe, pData^, nRemaining, cbRead, overlapped.OverlappedPtr);
528 if not bOk then begin
529 dwError := GetLastError;
530 case dwError of
531 ERROR_IO_PENDING : begin
532 dwWait := overlapped.WaitFor(FTimeout);
533
534 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;
538
539 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));
546 end;
547 end;
548
549 ASSERT( cbRead > 0); // see TTransportImpl.ReadAll()
550 ASSERT( cbRead <= DWORD(nRemaining));
551 Dec( nRemaining, cbRead);
552 Inc( pData, cbRead);
553 Inc( result, cbRead);
554 end;
555 end;
556
557
ToArraynull558 function TPipeStreamBase.ToArray: TBytes;
559 var bytes : LongInt;
560 begin
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;
571 end;
572
573
574 { TNamedPipeStreamImpl }
575
576
577 constructor TNamedPipeStreamImpl.Create( const aPipeName : string;
578 const aEnableOverlapped : Boolean;
579 const aShareMode: DWORD;
580 const aSecurityAttributes: PSecurityAttributes;
581 const aTimeOut, aOpenTimeOut : DWORD);
582 begin
583 inherited Create( aEnableOverlapped, aTimeOut, aOpenTimeOut);
584
585 FPipeName := aPipeName;
586 FShareMode := aShareMode;
587 FSecurityAttribs := aSecurityAttributes;
588
589 if Copy(FPipeName,1,2) <> '\\'
590 then FPipeName := '\\.\pipe\' + FPipeName; // assume localhost
591 end;
592
593
594 procedure TNamedPipeStreamImpl.Open;
595 var hPipe : THandle;
596 retries, timeout, dwErr, dwFlagsAndAttributes : DWORD;
597 const INTERVAL = 10; // ms
598 begin
599 if IsOpen then Exit;
600
601 retries := Max( 1, Round( 1.0 * FOpenTimeOut / INTERVAL));
602 timeout := FOpenTimeOut;
603
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.
607 // Always use INTERVAL, since WaitNamedPipe(0) defaults to some other value
608 while not WaitNamedPipe( PChar(FPipeName), INTERVAL) do begin
609 dwErr := GetLastError;
610 if dwErr <> ERROR_FILE_NOT_FOUND
611 then raise TTransportExceptionNotOpen.Create('Unable to open pipe, '+SysErrorMessage(dwErr));
612
613 if timeout <> INFINITE then begin
614 if (retries > 0)
615 then Dec(retries)
616 else raise TTransportExceptionNotOpen.Create('Unable to open pipe, timed out');
617 end;
618
619 Sleep(INTERVAL)
620 end;
621
622 dwFlagsAndAttributes := FILE_FLAG_OVERLAPPED
623 or FILE_FLAG_WRITE_THROUGH // async+fast, please
624 or PREVENT_PIPE_IMPERSONATION;
625
626 // open that thingy
627 hPipe := CreateFile( PChar( FPipeName),
628 GENERIC_READ or GENERIC_WRITE,
629 FShareMode, // sharing
630 FSecurityAttribs, // security attributes
631 OPEN_EXISTING, // opens existing pipe
632 dwFlagsAndAttributes, // flags + attribs
633 0); // no template file
634
635 if hPipe = INVALID_HANDLE_VALUE
636 then raise TTransportExceptionNotOpen.Create('Unable to open pipe, '+SysErrorMessage(GetLastError));
637
638 // everything fine
639 FPipe := hPipe;
640 end;
641
642
643 { THandlePipeStreamImpl }
644
645
646 constructor THandlePipeStreamImpl.Create( const aPipeHandle : THandle;
647 const aOwnsHandle, aEnableOverlapped : Boolean;
648 const aTimeOut : DWORD);
649 begin
650 inherited Create( aEnableOverlapped, aTimeout, aTimeout);
651
652 if aOwnsHandle
653 then FSrcHandle := aPipeHandle
654 else FSrcHandle := DuplicatePipeHandle( aPipeHandle);
655
656 Open;
657 end;
658
659
660 destructor THandlePipeStreamImpl.Destroy;
661 begin
662 try
663 ClosePipeHandle( FSrcHandle);
664 finally
665 inherited Destroy;
666 end;
667 end;
668
669
670 procedure THandlePipeStreamImpl.Open;
671 begin
672 if not IsOpen
673 then FPipe := DuplicatePipeHandle( FSrcHandle);
674 end;
675
676
677 { TPipeTransportBase }
678
679
TPipeTransportBase.GetIsOpennull680 function TPipeTransportBase.GetIsOpen: Boolean;
681 begin
682 result := (FInputStream <> nil) and (FInputStream.IsOpen)
683 and (FOutputStream <> nil) and (FOutputStream.IsOpen);
684 end;
685
686
687 procedure TPipeTransportBase.Open;
688 begin
689 FInputStream.Open;
690 FOutputStream.Open;
691 end;
692
693
694 procedure TPipeTransportBase.Close;
695 begin
696 FInputStream.Close;
697 FOutputStream.Close;
698 end;
699
700
701 { TNamedPipeTransportClientEndImpl }
702
703
704 constructor TNamedPipeTransportClientEndImpl.Create( const aPipeName : string;
705 const aShareMode: DWORD;
706 const aSecurityAttributes: PSecurityAttributes;
707 const aTimeOut, aOpenTimeOut : DWORD;
708 const aConfig : IThriftConfiguration);
709 // Named pipe constructor
710 begin
711 inherited Create( nil, nil, aConfig);
712 FInputStream := TNamedPipeStreamImpl.Create( aPipeName, TRUE, aShareMode, aSecurityAttributes, aTimeOut, aOpenTimeOut);
713 FOutputStream := FInputStream; // true for named pipes
714 end;
715
716
717 constructor TNamedPipeTransportClientEndImpl.Create( const aPipe : THandle;
718 const aOwnsHandle : Boolean;
719 const aTimeOut : DWORD;
720 const aConfig : IThriftConfiguration);
721 // Named pipe constructor
722 begin
723 inherited Create( nil, nil, aConfig);
724 FInputStream := THandlePipeStreamImpl.Create( aPipe, aOwnsHandle, TRUE, aTimeOut);
725 FOutputStream := FInputStream; // true for named pipes
726 end;
727
728
729 { TNamedPipeTransportServerEndImpl }
730
731
732 constructor TNamedPipeTransportServerEndImpl.Create( const aPipe : THandle;
733 const aOwnsHandle : Boolean;
734 const aTimeOut : DWORD;
735 const aConfig : IThriftConfiguration);
736 // Named pipe constructor
737 begin
738 FHandle := DuplicatePipeHandle( aPipe);
739 inherited Create( aPipe, aOwnsHandle, aTimeout, aConfig);
740 end;
741
742
743 procedure TNamedPipeTransportServerEndImpl.Close;
744 begin
745 FlushFileBuffers( FHandle);
746 DisconnectNamedPipe( FHandle); // force client off the pipe
747 ClosePipeHandle( FHandle);
748
749 inherited Close;
750 end;
751
752
753 { TAnonymousPipeTransportImpl }
754
755
756 constructor TAnonymousPipeTransportImpl.Create( const aPipeRead, aPipeWrite : THandle;
757 const aOwnsHandles : Boolean;
758 const aTimeOut : DWORD;
759 const aConfig : IThriftConfiguration);
760 // Anonymous pipe constructor
761 begin
762 inherited Create( nil, nil, aConfig);
763 // overlapped is not supported with AnonPipes, see MSDN
764 FInputStream := THandlePipeStreamImpl.Create( aPipeRead, aOwnsHandles, FALSE, aTimeout);
765 FOutputStream := THandlePipeStreamImpl.Create( aPipeWrite, aOwnsHandles, FALSE, aTimeout);
766 end;
767
768
769 { TPipeServerTransportBase }
770
771
772 constructor TPipeServerTransportBase.Create( const aConfig : IThriftConfiguration);
773 begin
774 inherited Create( aConfig);
775 FStopServer := TEvent.Create(nil,TRUE,FALSE,''); // manual reset
776 end;
777
778
779 destructor TPipeServerTransportBase.Destroy;
780 begin
781 try
782 FreeAndNil( FStopServer);
783 finally
784 inherited Destroy;
785 end;
786 end;
787
788
QueryStopServernull789 function TPipeServerTransportBase.QueryStopServer : Boolean;
790 begin
791 result := (FStopServer = nil)
792 or (FStopServer.WaitFor(0) <> wrTimeout);
793 end;
794
795
796 procedure TPipeServerTransportBase.Listen;
797 begin
798 FStopServer.ResetEvent;
799 end;
800
801
802 procedure TPipeServerTransportBase.Close;
803 begin
804 FStopServer.SetEvent;
805 InternalClose;
806 end;
807
808
809 { TAnonymousPipeServerTransportImpl }
810
811 constructor TAnonymousPipeServerTransportImpl.Create( const aBufsize : Cardinal;
812 const aTimeOut : DWORD;
813 const aConfig : IThriftConfiguration);
814 // Anonymous pipe CTOR
815 begin
816 inherited Create(aConfig);
817 FBufsize := aBufSize;
818 FReadHandle := INVALID_HANDLE_VALUE;
819 FWriteHandle := INVALID_HANDLE_VALUE;
820 FClientAnonRead := INVALID_HANDLE_VALUE;
821 FClientAnonWrite := INVALID_HANDLE_VALUE;
822 FTimeOut := aTimeOut;
823
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
828 then raise TTransportExceptionNotOpen.Create(ClassName+'.Create() failed');
829 end;
830
831
Acceptnull832 function TAnonymousPipeServerTransportImpl.Accept(const fnAccepting: TProc): ITransport;
833 var buf : Byte;
834 br : DWORD;
835 begin
836 if Assigned(fnAccepting)
837 then fnAccepting();
838
839 // 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)
842 then raise TTransportExceptionNotOpen.Create('TServerPipe unable to initiate pipe communication');
843
844 // create the transport impl
845 result := TAnonymousPipeTransportImpl.Create( FReadHandle, FWriteHandle, FALSE, FTimeOut, Configuration);
846 end;
847
848
849 procedure TAnonymousPipeServerTransportImpl.InternalClose;
850 begin
851 ClosePipeHandle( FReadHandle);
852 ClosePipeHandle( FWriteHandle);
853 ClosePipeHandle( FClientAnonRead);
854 ClosePipeHandle( FClientAnonWrite);
855 end;
856
857
ReadHandlenull858 function TAnonymousPipeServerTransportImpl.ReadHandle : THandle;
859 begin
860 result := FReadHandle;
861 end;
862
863
WriteHandlenull864 function TAnonymousPipeServerTransportImpl.WriteHandle : THandle;
865 begin
866 result := FWriteHandle;
867 end;
868
869
TAnonymousPipeServerTransportImpl.ClientAnonReadnull870 function TAnonymousPipeServerTransportImpl.ClientAnonRead : THandle;
871 begin
872 result := FClientAnonRead;
873 end;
874
875
TAnonymousPipeServerTransportImpl.ClientAnonWritenull876 function TAnonymousPipeServerTransportImpl.ClientAnonWrite : THandle;
877 begin
878 result := FClientAnonWrite;
879 end;
880
881
CreateAnonPipenull882 function TAnonymousPipeServerTransportImpl.CreateAnonPipe : Boolean;
883 var sd : PSECURITY_DESCRIPTOR;
884 sa : SECURITY_ATTRIBUTES; //TSecurityAttributes;
885 hCAR, hPipeW, hCAW, hPipe : THandle;
886 begin
887 sd := PSECURITY_DESCRIPTOR( LocalAlloc( LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH));
888 try
889 Win32Check( InitializeSecurityDescriptor( sd, SECURITY_DESCRIPTOR_REVISION));
890 Win32Check( SetSecurityDescriptorDacl( sd, TRUE, nil, FALSE));
891
892 sa.nLength := sizeof( sa);
893 sa.lpSecurityDescriptor := sd;
894 sa.bInheritHandle := TRUE; //allow passing handle to child
895
896 Result := CreatePipe( hCAR, hPipeW, @sa, FBufSize); //create stdin pipe
897 if not Result then begin //create stdin pipe
898 raise TTransportExceptionNotOpen.Create('TServerPipe CreatePipe (anon) failed, '+SysErrorMessage(GetLastError));
899 Exit;
900 end;
901
902 Result := CreatePipe( hPipe, hCAW, @sa, FBufSize); //create stdout pipe
903 if not Result then begin //create stdout pipe
904 CloseHandle( hCAR);
905 CloseHandle( hPipeW);
906 raise TTransportExceptionNotOpen.Create('TServerPipe CreatePipe (anon) failed, '+SysErrorMessage(GetLastError));
907 Exit;
908 end;
909
910 FClientAnonRead := hCAR;
911 FClientAnonWrite := hCAW;
912 FReadHandle := hPipe;
913 FWriteHandle := hPipeW;
914 finally
915 if sd <> nil then LocalFree( Cardinal(sd));
916 end;
917 end;
918
919
920 { TNamedPipeServerTransportImpl }
921
922
923 constructor TNamedPipeServerTransportImpl.Create( const aPipename : string;
924 const aFlags : TNamedPipeFlags;
925 const aConfig : IThriftConfiguration;
926 const aBufsize, aMaxConns, aTimeOut : Cardinal);
927 // Named Pipe CTOR
928 begin
929 inherited Create( aConfig);
930 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;
936 ASSERT( FTimeout > 0);
937
938 FOnlyLocalClients := (TNamedPipeFlag.OnlyLocalClients in aFlags);
939
940 if Copy(FPipeName,1,2) <> '\\'
941 then FPipeName := '\\.\pipe\' + FPipeName; // assume localhost
942 end;
943
944
945 constructor TNamedPipeServerTransportImpl.Create( const aPipename : string;
946 const aBufsize, aMaxConns, aTimeOut : Cardinal;
947 const aConfig : IThriftConfiguration);
948 // Named Pipe CTOR (deprecated)
949 begin
950 {$WARN SYMBOL_DEPRECATED OFF} // Delphi XE emits a false warning here
951 Create( aPipeName, [], aConfig, aBufsize, aMaxConns, aTimeOut);
952 {$WARN SYMBOL_DEPRECATED ON}
953 end;
954
955
Acceptnull956 function TNamedPipeServerTransportImpl.Accept(const fnAccepting: TProc): ITransport;
957 var dwError, dwWait, dwDummy : DWORD;
958 overlapped : IOverlappedHelper;
959 handles : array[0..1] of THandle;
960 begin
961 overlapped := TOverlappedHelperImpl.Create;
962
963 ASSERT( not FConnected);
964 CreateNamedPipe;
965 while not FConnected do begin
966
967 if QueryStopServer then begin
968 InternalClose;
969 Abort;
970 end;
971
972 if Assigned(fnAccepting)
973 then fnAccepting();
974
975 // Wait for the client to connect; if it succeeds, the
returnsnull976 // 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;
981 end;
982
983 // 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;
990
991 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;
1002 raise TTransportExceptionNotOpen.Create('Client connection failed');
1003 end;
1004 end;
1005
1006 // create the transport impl
1007 result := CreateTransportInstance;
1008 end;
1009
1010
CreateTransportInstancenull1011 function TNamedPipeServerTransportImpl.CreateTransportInstance : ITransport;
1012 // create the transport impl
1013 var hPipe : THandle;
1014 begin
1015 hPipe := THandle( InterlockedExchangePointer( Pointer(FHandle), Pointer(INVALID_HANDLE_VALUE)));
1016 try
1017 FConnected := FALSE;
1018 result := TNamedPipeTransportServerEndImpl.Create( hPipe, TRUE, FTimeout, Configuration);
1019 except
1020 ClosePipeHandle(hPipe);
1021 raise;
1022 end;
1023 end;
1024
1025
1026 procedure TNamedPipeServerTransportImpl.InternalClose;
1027 var hPipe : THandle;
1028 begin
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;
1041 end;
1042
1043
TNamedPipeServerTransportImpl.Handlenull1044 function TNamedPipeServerTransportImpl.Handle : THandle;
1045 begin
1046 {$IFDEF WIN64}
1047 result := THandle( InterlockedExchangeAdd64( Int64(FHandle), 0));
1048 {$ELSE}
1049 result := THandle( InterlockedExchangeAdd( Integer(FHandle), 0));
1050 {$ENDIF}
1051 end;
1052
1053
CreateNamedPipenull1054 function TNamedPipeServerTransportImpl.CreateNamedPipe : THandle;
1055 var SIDAuthWorld : SID_IDENTIFIER_AUTHORITY ;
1056 everyone_sid : PSID;
1057 ea : EXPLICIT_ACCESS;
1058 acl : PACL;
1059 sd : PSECURITY_DESCRIPTOR;
1060 sa : SECURITY_ATTRIBUTES;
1061 dwPipeModeXtra : DWORD;
1062 const
1063 SECURITY_WORLD_SID_AUTHORITY : TSIDIdentifierAuthority = (Value : (0,0,0,0,0,1));
1064 SECURITY_WORLD_RID = $00000000;
1065 begin
1066 sd := nil;
1067 everyone_sid := nil;
1068 try
1069 ASSERT( (FHandle = INVALID_HANDLE_VALUE) and not FConnected);
1070
1071 // 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);
1075
1076 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);
1083
1084 acl := nil;
1085 SetEntriesInAcl( 1, @ea, nil, acl);
1086
1087 sd := PSECURITY_DESCRIPTOR( LocalAlloc( LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH));
1088 Win32Check( InitializeSecurityDescriptor( sd, SECURITY_DESCRIPTOR_REVISION));
1089 Win32Check( SetSecurityDescriptorDacl( sd, TRUE, acl, FALSE));
1090
1091 sa.nLength := SizeOf(sa);
1092 sa.lpSecurityDescriptor := sd;
1093 sa.bInheritHandle := FALSE;
1094
1095 // any extra flags we want to add to dwPipeMode
1096 dwPipeModeXtra := 0;
1097 if FOnlyLocalClients then dwPipeModeXtra := dwPipeModeXtra or PIPE_REJECT_REMOTE_CLIENTS;
1098
1099 // Create an instance of the named pipe
1100 {$IFDEF OLD_UNIT_NAMES}
1101 result := Windows.CreateNamedPipe(
1102 {$ELSE}
1103 result := Winapi.Windows.CreateNamedPipe(
1104 {$ENDIF}
1105 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
1113 );
1114
1115 if( result <> INVALID_HANDLE_VALUE)
1116 then InterlockedExchangePointer( Pointer(FHandle), Pointer(result))
1117 else raise TTransportExceptionNotOpen.Create('CreateNamedPipe() failed ' + IntToStr(GetLastError));
1118
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);
1123 end;
1124 end;
1125
1126
1127
1128 end.
1129
1130
1131
1132