From d1bf5d0336b8b1ca1de71a50edafa65694eff8b7 Mon Sep 17 00:00:00 2001 From: Roger Meier Date: Wed, 11 Apr 2012 21:38:56 +0000 Subject: [PATCH] THRIFT-1558 Named Pipe and Anonymous Pipe transport for Windows Patch: Peace C git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1325020 13f79535-47bb-0310-9956-ffa450edef68 --- lib/cpp/README_WINDOWS | 9 +- lib/cpp/libthrift.vcxproj | 8 +- lib/cpp/libthrift.vcxproj.filters | 12 + lib/cpp/src/transport/TPipe.cpp | 209 +++++++++++++++++ lib/cpp/src/transport/TPipe.h | 89 ++++++++ lib/cpp/src/transport/TPipeServer.cpp | 313 ++++++++++++++++++++++++++ lib/cpp/src/transport/TPipeServer.h | 85 +++++++ lib/cpp/src/windows/config.h | 1 + 8 files changed, 723 insertions(+), 3 deletions(-) create mode 100644 lib/cpp/src/transport/TPipe.cpp create mode 100644 lib/cpp/src/transport/TPipe.h create mode 100644 lib/cpp/src/transport/TPipeServer.cpp create mode 100644 lib/cpp/src/transport/TPipeServer.h diff --git a/lib/cpp/README_WINDOWS b/lib/cpp/README_WINDOWS index a955949f..f4c887ce 100644 --- a/lib/cpp/README_WINDOWS +++ b/lib/cpp/README_WINDOWS @@ -82,10 +82,17 @@ difference is in the Windows-specific implementation of the socket poll function. To target Vista, Win7 or other versions, comment out the line #define TARGET_WIN_XP. +Named Pipes +=========== +- Named Pipe transport has been added in the TPipe and TPipeServer classes. + This is currently Windows-only (see below). + Known issues ============ -- Does not support named pipes. (Supported in unix through unix domain sockets). +- Named pipe transport for *NIX has not been implemented. Domain sockets are + a better choice for local IPC under non-Windows OS's. *NIX named pipes + only support 1:1 client-server connection. TODO ==== diff --git a/lib/cpp/libthrift.vcxproj b/lib/cpp/libthrift.vcxproj index 286fc5b3..57add634 100644 --- a/lib/cpp/libthrift.vcxproj +++ b/lib/cpp/libthrift.vcxproj @@ -114,6 +114,8 @@ _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) @@ -179,6 +181,8 @@ + + @@ -248,13 +252,13 @@ - $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(ProjectDir)\src\transport\;$(THIRD_PARTY)\boost\boost_1_47_0\include;$(THIRD_PARTY)\boost\boost_1_47_0\;$(THIRD_PARTY)\openssl\OpenSSL-Win32\include\;$(IncludePath) + $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(ProjectDir)\src\transport\;$(THIRD_PARTY)\boost\boost_1_47_0\include;$(THIRD_PARTY)\boost\boost_1_47_0\;$(THIRD_PARTY)\openssl\OpenSSL-Win32\include\;..\..\..\..\boost;..\..\..\..\boost\boost;$(IncludePath) $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(ProjectDir)\src\transport\;$(THIRD_PARTY)\boost\boost_1_47_0\include;$(THIRD_PARTY)\boost\boost_1_47_0\;$(THIRD_PARTY)\openssl\OpenSSL-Win32\include\;$(IncludePath) - $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(ProjectDir)\src\transport\;$(THIRD_PARTY)\boost\boost_1_47_0\include;$(THIRD_PARTY)\boost\boost_1_47_0\;$(THIRD_PARTY)\openssl\OpenSSL-Win32\include\;$(IncludePath) + $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(ProjectDir)\src\transport\;$(THIRD_PARTY)\boost\boost_1_47_0\include;$(THIRD_PARTY)\boost\boost_1_47_0\;$(THIRD_PARTY)\openssl\OpenSSL-Win32\include\;..\..\..\..\boost;..\..\..\..\boost\boost;$(IncludePath) $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(ProjectDir)\src\transport\;$(THIRD_PARTY)\boost\boost_1_47_0\include;$(THIRD_PARTY)\boost\boost_1_47_0\;$(THIRD_PARTY)\openssl\OpenSSL-Win32\include\;$(IncludePath) diff --git a/lib/cpp/libthrift.vcxproj.filters b/lib/cpp/libthrift.vcxproj.filters index 0b0003c9..0762fb40 100644 --- a/lib/cpp/libthrift.vcxproj.filters +++ b/lib/cpp/libthrift.vcxproj.filters @@ -96,6 +96,12 @@ windows + + transport + + + transport + @@ -212,6 +218,12 @@ windows + + transport + + + transport + diff --git a/lib/cpp/src/transport/TPipe.cpp b/lib/cpp/src/transport/TPipe.cpp new file mode 100644 index 00000000..2c7cf56d --- /dev/null +++ b/lib/cpp/src/transport/TPipe.cpp @@ -0,0 +1,209 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#ifdef _WIN32 + +#include "TTransportException.h" +#include "TPipe.h" + +namespace apache { namespace thrift { namespace transport { + +using namespace std; + +/** +* TPipe implementation. +*/ + +//---- Constructors ---- +TPipe::TPipe(HANDLE hpipe) : + pipename_(""), + hPipe_(hpipe), + TimeoutSeconds_(3), + isAnonymous(false) +{} + +TPipe::TPipe(string pipename) : + pipename_(pipename), + hPipe_(INVALID_HANDLE_VALUE), + TimeoutSeconds_(3), + isAnonymous(false) +{} + +TPipe::TPipe(HANDLE hPipeRd, HANDLE hPipeWrt) : + pipename_(""), + hPipe_(hPipeRd), + hPipeWrt_(hPipeWrt), + TimeoutSeconds_(3), + isAnonymous(true) +{} + + TPipe::TPipe() : + pipename_(""), + hPipe_(INVALID_HANDLE_VALUE), + TimeoutSeconds_(3) +{} + +//---- Destructor ---- +TPipe::~TPipe() { + close(); +} + + +bool TPipe::isOpen() { + return (hPipe_ != INVALID_HANDLE_VALUE); +} + +//--------------------------------------------------------- +// Transport callbacks +//--------------------------------------------------------- + +bool TPipe::peek() { + if (!isOpen()) { + return false; + } + DWORD bytesavail = 0; + int PeekRet = 0; + PeekRet = PeekNamedPipe(hPipe_, NULL, 0, NULL, &bytesavail, NULL); + return (PeekRet != 0 && bytesavail > 0); +} + +void TPipe::open() { + if (isOpen()) { + return; + } + + int SleepInterval = 500; //ms + int retries = TimeoutSeconds_ * 1000 / SleepInterval; + for(int i=0; i { + public: + + // Constructs a new pipe object. + TPipe(); + // Named pipe constructors - + TPipe(HANDLE hPipe); + TPipe(std::string path); + // Anonymous pipe - + TPipe(HANDLE hPipeRd, HANDLE hPipeWrt); + + // Destroys the pipe object, closing it if necessary. + virtual ~TPipe(); + + // Returns whether the pipe is open & valid. + virtual bool isOpen(); + + // Checks whether more data is available in the pipe. + virtual bool peek(); + + // Creates and opens the named/anonymous pipe. + virtual void open(); + + // Shuts down communications on the pipe. + virtual void close(); + + // Reads from the pipe. + virtual uint32_t read(uint8_t* buf, uint32_t len); + + // Writes to the pipe. + virtual void write(const uint8_t* buf, uint32_t len); + + + //Accessors + std::string getPipename(); + void setPipename(std::string pipename); + HANDLE getPipeHandle(); //doubles as the read handle for anon pipe + void setPipeHandle(HANDLE pipehandle); + HANDLE getWrtPipeHandle(); + void setWrtPipeHandle(HANDLE pipehandle); + long getConnectTimeout(); + void setConnectTimeout(long seconds); + + private: + std::string pipename_; + //Named pipe handles are R/W, while anonymous pipes are one or the other (half duplex). + HANDLE hPipe_, hPipeWrt_; + long TimeoutSeconds_; + bool isAnonymous; + +}; + +}}} // apache::thrift::transport + +#endif //_WIN32 +#endif // #ifndef _THRIFT_TRANSPORT_TPIPE_H_ + diff --git a/lib/cpp/src/transport/TPipeServer.cpp b/lib/cpp/src/transport/TPipeServer.cpp new file mode 100644 index 00000000..6f2f73db --- /dev/null +++ b/lib/cpp/src/transport/TPipeServer.cpp @@ -0,0 +1,313 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifdef _WIN32 + +#ifdef HAVE_CONFIG_H +#include +#endif +#include + +#include "TPipe.h" +#include "TPipeServer.h" +#include +#include +#include + +namespace apache { namespace thrift { namespace transport { + +using namespace std; +using boost::shared_ptr; + +//---- Constructors ---- +TPipeServer::TPipeServer(string pipename, uint32_t bufsize) : + pipename_(pipename), + bufsize_(bufsize), + hPipe_(INVALID_HANDLE_VALUE), + isAnonymous(false), + maxconns_(TPIPE_SERVER_MAX_CONNS_DEFAULT) + {} + +TPipeServer::TPipeServer(string pipename, uint32_t bufsize, uint32_t maxconnections) : + pipename_(pipename), + bufsize_(bufsize), + hPipe_(INVALID_HANDLE_VALUE), + isAnonymous(false) + { //Restrict maxconns_ to 1-255 + if(maxconnections == 0) + maxconns_ = 1; + else if (maxconnections > 255) + maxconns_ = 255; + else + maxconns_ = maxconnections; + } + +TPipeServer::TPipeServer(string pipename) : + pipename_(pipename), + bufsize_(1024), + hPipe_(INVALID_HANDLE_VALUE), + isAnonymous(false), + maxconns_(TPIPE_SERVER_MAX_CONNS_DEFAULT) + {} + +TPipeServer::TPipeServer(int bufsize) : + pipename_(""), + bufsize_(bufsize), + hPipe_(INVALID_HANDLE_VALUE), + isAnonymous(true), + maxconns_(1) + { + //The anonymous pipe needs to be created first so that the server can + //pass the handles on to the client before the serve (acceptImpl) + //blocking call. + if (!TCreateAnonPipe()) { + GlobalOutput.perror("TPipeServer Create(Anon)Pipe failed, GLE=", GetLastError()); + throw TTransportException(TTransportException::NOT_OPEN, " TPipeServer Create(Anon)Pipe failed"); + } +} + +TPipeServer::TPipeServer() : + pipename_(""), + bufsize_(1024), + hPipe_(INVALID_HANDLE_VALUE), + isAnonymous(true), + maxconns_(1) +{ + if (!TCreateAnonPipe()) { + GlobalOutput.perror("TPipeServer Create(Anon)Pipe failed, GLE=", GetLastError()); + throw TTransportException(TTransportException::NOT_OPEN, " TPipeServer Create(Anon)Pipe failed"); + } +} + +//---- Destructor ---- +TPipeServer::~TPipeServer() { + close(); +} + +//--------------------------------------------------------- +// Transport callbacks +//--------------------------------------------------------- + +shared_ptr TPipeServer::acceptImpl() { + shared_ptr client; + + if(isAnonymous) + { //Anonymous Pipe + //This 0-byte read serves merely as a blocking call. + byte buf; + DWORD br; + int fSuccess = ReadFile( + hPipe_, // pipe handle + &buf, // buffer to receive reply + 0, // size of buffer + &br, // number of bytes read + NULL); // not overlapped + + if ( !fSuccess && GetLastError() != ERROR_MORE_DATA ) { + GlobalOutput.perror("TPipeServer unable to initiate pipe comms, GLE=", GetLastError()); + throw TTransportException(TTransportException::NOT_OPEN, " TPipeServer unable to initiate pipe comms"); + } + client.reset(new TPipe(hPipe_, hPipeW_)); + } + else + { //Named Pipe + int ConnectRet; + while (true) + { + if (!TCreateNamedPipe()) { + GlobalOutput.perror("TPipeServer CreateNamedPipe failed, GLE=", GetLastError()); + throw TTransportException(TTransportException::NOT_OPEN, " TPipeServer CreateNamedPipe failed"); + } + + // Wait for the client to connect; if it succeeds, the + // function returns a nonzero value. If the function returns + // zero, GetLastError should return ERROR_PIPE_CONNECTED. + ConnectRet = ConnectNamedPipe(hPipe_, NULL) ? + TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); + + if (ConnectRet == TRUE) + { + GlobalOutput.printf("Client connected."); + break; + } + else + { + close(); + GlobalOutput.perror("TPipeServer ConnectNamedPipe GLE=", GetLastError()); + throw TTransportException(TTransportException::NOT_OPEN, "TPipeServer: client connection failed"); + } + } + client.reset(new TPipe(hPipe_)); + } + + return client; +} + +void TPipeServer::interrupt() { + if(hPipe_ != INVALID_HANDLE_VALUE) { + CancelIo(hPipe_); + } +} + +void TPipeServer::close() { + if(!isAnonymous) + { + if(hPipe_ != INVALID_HANDLE_VALUE) { + DisconnectNamedPipe(hPipe_); + CloseHandle(hPipe_); + hPipe_ = INVALID_HANDLE_VALUE; + } + } + else + { + try { + CloseHandle(hPipe_); + CloseHandle(hPipeW_); + CloseHandle(ClientAnonRead); + CloseHandle(ClientAnonWrite); + } + catch(...) { + GlobalOutput.perror("TPipeServer anon close GLE=", GetLastError()); + } + } +} + + +bool TPipeServer::TCreateNamedPipe() { + + //Windows - set security to allow non-elevated apps + //to access pipes created by elevated apps. + SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; + PSID everyone_sid = NULL; + AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyone_sid); + + EXPLICIT_ACCESS ea; + ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS)); + ea.grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL; + ea.grfAccessMode = SET_ACCESS; + ea.grfInheritance = NO_INHERITANCE; + ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + ea.Trustee.ptstrName = (LPSTR)everyone_sid; + + PACL acl = NULL; + SetEntriesInAcl(1, &ea, NULL, &acl); + + PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH); + InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE); + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = sd; + sa.bInheritHandle = FALSE; + + // Create an instance of the named pipe + hPipe_ = CreateNamedPipe( + pipename_.c_str(), // pipe name + PIPE_ACCESS_DUPLEX, // read/write access + PIPE_TYPE_MESSAGE | // message type pipe + PIPE_READMODE_MESSAGE, // message-read mode + maxconns_, // max. instances + bufsize_, // output buffer size + bufsize_, // input buffer size + 0, // client time-out + &sa); // default security attribute + + return (hPipe_ != INVALID_HANDLE_VALUE); +} + +bool TPipeServer::TCreateAnonPipe() { + SECURITY_ATTRIBUTES sa; + SECURITY_DESCRIPTOR sd; //security information for pipes + + InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd, true, NULL, false); + sa.lpSecurityDescriptor = &sd; + sa.lpSecurityDescriptor = NULL; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = true; //allow passing handle to child + + if (!CreatePipe(&ClientAnonRead,&hPipeW_,&sa,0)) //create stdin pipe + { + GlobalOutput.perror("TPipeServer CreatePipe (anon) failed, GLE=", GetLastError()); + return false; + } + if (!CreatePipe(&hPipe_,&ClientAnonWrite,&sa,0)) //create stdout pipe + { + GlobalOutput.perror("TPipeServer CreatePipe (anon) failed, GLE=", GetLastError()); + CloseHandle(ClientAnonRead); + CloseHandle(hPipeW_); + return false; + } + + return true; +} + + +//--------------------------------------------------------- +// Accessors +//--------------------------------------------------------- + +string TPipeServer::getPipename() { + return pipename_; +} + +void TPipeServer::setPipename(std::string pipename) { + pipename_ = pipename; +} + +int TPipeServer::getBufferSize() { + return bufsize_; +} + +void TPipeServer::setBufferSize(int bufsize) { + bufsize_ = bufsize; +} + +HANDLE TPipeServer::getPipeHandle() { + return hPipe_; +} + +HANDLE TPipeServer::getWrtPipeHandle() +{ + return hPipeW_; +} + +HANDLE TPipeServer::getClientRdPipeHandle() +{ + return ClientAnonRead; +} + +HANDLE TPipeServer::getClientWrtPipeHandle() +{ + return ClientAnonWrite; +} + +bool TPipeServer::getAnonymous() { + return isAnonymous; +} + +void TPipeServer::setAnonymous(bool anon) { + isAnonymous = anon; +} + +}}} // apache::thrift::transport + +#endif //_WIN32 diff --git a/lib/cpp/src/transport/TPipeServer.h b/lib/cpp/src/transport/TPipeServer.h new file mode 100644 index 00000000..1732546a --- /dev/null +++ b/lib/cpp/src/transport/TPipeServer.h @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _THRIFT_TRANSPORT_TSERVERWINPIPES_H_ +#define _THRIFT_TRANSPORT_TSERVERWINPIPES_H_ 1 +#ifdef _WIN32 + +#include "TServerTransport.h" +#include + +#define TPIPE_SERVER_MAX_CONNS_DEFAULT 10 + +namespace apache { namespace thrift { namespace transport { + +/** + * Windows Pipes implementation of TServerTransport. + */ +class TPipeServer : public TServerTransport { + public: + //Constructors + // Named Pipe - + TPipeServer(std::string pipename, uint32_t bufsize); + TPipeServer(std::string pipename, uint32_t bufsize, uint32_t maxconnections); + TPipeServer(std::string pipename); + // Anonymous pipe - + TPipeServer(int bufsize); + TPipeServer(); + + //Destructor + ~TPipeServer(); + + //Standard transport callbacks + //void listen(); //Unnecessary for Windows pipes + void interrupt(); + void close(); + protected: + boost::shared_ptr acceptImpl(); + + bool TCreateNamedPipe(); + bool TCreateAnonPipe(); + + public: + //Accessors + std::string getPipename(); + void setPipename(std::string pipename); + int getBufferSize(); + void setBufferSize(int bufsize); + HANDLE getPipeHandle(); //Named Pipe R/W -or- Anonymous pipe Read handle + HANDLE getWrtPipeHandle(); + HANDLE getClientRdPipeHandle(); + HANDLE getClientWrtPipeHandle(); + bool getAnonymous(); + void setAnonymous(bool anon); + + private: + std::string pipename_; + uint32_t bufsize_; + uint32_t maxconns_; + HANDLE hPipe_; //Named Pipe (R/W) or Anonymous Pipe (R) + HANDLE hPipeW_; //Anonymous Pipe (W) + HANDLE ClientAnonRead, ClientAnonWrite; //Client side anonymous pipe handles + //? Do we need duplicates to send to client? + bool isAnonymous; +}; + +}}} // apache::thrift::transport + +#endif //_WIN32 +#endif // #ifndef _THRIFT_TRANSPORT_TSERVERWINPIPES_H_ diff --git a/lib/cpp/src/windows/config.h b/lib/cpp/src/windows/config.h index d875094f..aa361ffb 100644 --- a/lib/cpp/src/windows/config.h +++ b/lib/cpp/src/windows/config.h @@ -53,6 +53,7 @@ typedef boost::uint8_t uint8_t; #include #include #pragma comment(lib, "Ws2_32.lib") +#pragma comment(lib, "advapi32.lib") //For security APIs in TPipeServer // pthreads #if 0 -- 2.17.1