Bug 4159

Summary: ACE_SSL_SOCK_Stream::close doesn't close TCP socket for unreachable endpoint (handle leak)
Product: ACE Reporter: Norbert Kloetzer <Norbert.Kloetzer>
Component: SSL WrappersAssignee: DOC Center Support List (internal) <tao-support>
Status: NEW ---    
Severity: major CC: Norbert.Kloetzer
Priority: P3    
Version: 6.1.4   
Hardware: x86   
OS: Windows 2008   

Description Norbert Kloetzer 2014-02-27 12:02:17 CST
Opening an SSL connection to an unreachable endpoint leads to a timeout.
But the used TCP socket remains unclosed and its handle leaks.
Within programs running for a long time periodical connection retries 
make socket resources to run out.

The following test code shows the problem:

#include <ace/SSL/SSL_SOCK_Stream.h>
#include <ace/Connector.h>
#include <ace/SSL/SSL_SOCK_Connector.h>

#include <sstream>

class ConnHandler : public ACE_Svc_Handler <ACE_SSL_SOCK_Stream, ACE_MT_SYNCH>
{
 public:
  ConnHandler()
  {
   printf("ConnHandler constructor\n");
  }
  virtual ~ConnHandler()
  {
   printf("ConnHandler destructor\n");
  }

  virtual int open(void * = NULL)
  {
   printf("ConnHandler open\n");
   return -1;
  }

  virtual int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE)
  {
   printf("ConnHandler handle_input\n");
   return 0;
  }
 
  /**
   * Perform termination activities on the SVC_HANDLER.  The default
   * behavior is to close down the <peer_> (to avoid descriptor leaks)
   * and to <destroy> this object (to avoid memory leaks)!  If you
   * don't want this behavior make sure you override this method...
   */
  virtual int handle_close(ACE_HANDLE = ACE_INVALID_HANDLE,
                           ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK)
  {
   printf("ConnHandler handle_close\n");

   // For SSL peer() contains the SSL socket and peer().peer() contains the TCP socket.
   // ACE Handle Leak: 
   //  At connect timeout the SSL socket handle is invalid, the TCP socket handle isn't.
   //  ACE_SSL_SOCK_STREAM::close() doesn't anything when its SSL handle is invalid.
   //   See your code in ACE_Wrappers/ace/SSL/SSL_SOCK_Stream.inl in line 317:
   // 312  ACE_INLINE int
   // 313  ACE_SSL_SOCK_Stream::close (void)
   // 314  {
   // 315   ACE_TRACE ("ACE_SSL_SOCK_Stream::close");
   // 316
   // 317   if (this->ssl_ == 0 || this->get_handle () == ACE_INVALID_HANDLE)
   // 318     return 0;  // SSL_SOCK_Stream was never opened.
   // ...
   // 352  }

   //  So the TCP socket remains NOT closed and the handle leaks.

   // Here our workaround to prevent a handle leak:
   ACE_SOCK_Stream & TCP_Peer = this->peer().peer();

   if(TCP_Peer.get_handle() != ACE_INVALID_HANDLE)
   {
    std::stringstream sstr;
    sstr << "Closing TCP socket with handle "
         << (int)TCP_Peer.get_handle();
    printf("handle_close %s\n", sstr.str().c_str());

    TCP_Peer.close();
   }

   // From the default function handle_close() from Svc_Handler.cpp line 304:
   //  ACE_Svc_Handler<PEER_STREAM, SYNCH_TRAITS>::handle_close (ACE_HANDLE,ACE_Reactor_Mask)
   this->destroy();
   return 0;
  }

  virtual int svc()
  {
   printf("ConnHandler svc\n");
   return 0;
  }
};

int main(int , char ** )
{
 // Using ACE 6.1.4
 ACE_Connector < ConnHandler, ACE_SSL_SOCK_Connector > cSockConnector;

 ConnHandler * pcConnHandler = new ConnHandler();

 // Use a not reachable endpoint.
 const ACE_INET_Addr ConnectAddr("172.22.113.7:35000");

 int iConnectReturn = cSockConnector.connect(pcConnHandler, ConnectAddr);
 if(iConnectReturn < 0)
 {
  int iErrno = errno;
  printf("connect failed: Errno=%d\n", iErrno);
 }
 else
 {
  printf("connect successful\n");
 }

 return 0;
}