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