Back to index

lightning-sunbird  0.9+nobinonly
Public Member Functions | Static Public Member Functions | Static Public Attributes | Static Protected Member Functions | Protected Attributes | Private Member Functions | Static Private Member Functions | Private Attributes
nsSSLThread Class Reference

#include <nsSSLThread.h>

Inheritance diagram for nsSSLThread:
Inheritance graph
[legend]
Collaboration diagram for nsSSLThread:
Collaboration graph
[legend]

List of all members.

Public Member Functions

 nsSSLThread ()
 ~nsSSLThread ()
nsresult startThread ()
void requestExit ()

Static Public Member Functions

static PRInt32 requestRead (nsNSSSocketInfo *si, void *buf, PRInt32 amount)
static PRInt32 requestWrite (nsNSSSocketInfo *si, const void *buf, PRInt32 amount)
static PRInt16 requestPoll (nsNSSSocketInfo *si, PRInt16 in_flags, PRInt16 *out_flags)
static PRInt32 requestRecvMsgPeek (nsNSSSocketInfo *si, void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout)
static PRStatus requestClose (nsNSSSocketInfo *si)
static PRStatus requestGetsockname (nsNSSSocketInfo *si, PRNetAddr *addr)
static PRStatus requestGetpeername (nsNSSSocketInfo *si, PRNetAddr *addr)
static PRStatus requestGetsocketoption (nsNSSSocketInfo *si, PRSocketOptionData *data)
static PRStatus requestSetsocketoption (nsNSSSocketInfo *si, const PRSocketOptionData *data)
static PRStatus requestConnectcontinue (nsNSSSocketInfo *si, PRInt16 out_flags)
static nsresult requestActivateSSL (nsNSSSocketInfo *si)
static void rememberPendingHTTPRequest (nsIRequest *aRequest)
static void cancelPendingHTTPRequest ()

Static Public Attributes

static nsSSLThreadssl_thread_singleton = nsnull

Static Protected Member Functions

static void PR_CALLBACK nsThreadRunner (void *arg)

Protected Attributes

PRThreadmThreadHandle
PRLockmMutex
PRCondVarmCond
PRBool mExitRequested

Private Member Functions

virtual void Run (void)

Static Private Member Functions

static PRInt32 checkHandshake (PRInt32 bytesTransfered, PRBool wasReading, PRFileDesc *fd, nsNSSSocketInfo *socketInfo)
static void restoreOriginalSocket_locked (nsNSSSocketInfo *si)
static PRFileDescgetRealSSLFD (nsNSSSocketInfo *si)

Private Attributes

nsNSSSocketInfomBusySocket
nsNSSSocketInfomSocketScheduledToBeDestroyed
nsIRequestmPendingHTTPRequest

Detailed Description

Definition at line 48 of file nsSSLThread.h.


Constructor & Destructor Documentation

Definition at line 49 of file nsSSLThread.cpp.

: mBusySocket(nsnull),
  mSocketScheduledToBeDestroyed(nsnull),
  mPendingHTTPRequest(nsnull)
{
  NS_ASSERTION(!ssl_thread_singleton, "nsSSLThread is a singleton, caller attempts to create another instance!");
  
  ssl_thread_singleton = this;
}

Definition at line 59 of file nsSSLThread.cpp.


Member Function Documentation

PRInt32 nsSSLThread::checkHandshake ( PRInt32  bytesTransfered,
PRBool  wasReading,
PRFileDesc fd,
nsNSSSocketInfo socketInfo 
) [static, private]

Definition at line 1192 of file nsNSSIOLayer.cpp.

{
  // This is where we work around all of those SSL servers that don't 
  // conform to the SSL spec and shutdown a connection when we request
  // SSL v3.1 (aka TLS).  The spec says the client says what version
  // of the protocol we're willing to perform, in our case SSL v3.1
  // In its response, the server says which version it wants to perform.
  // Many servers out there only know how to do v3.0.  Next, we're supposed
  // to send back the version of the protocol we requested (ie v3.1).  At
  // this point many servers's implementations are broken and they shut
  // down the connection when they don't see the version they sent back.
  // This is supposed to prevent a man in the middle from forcing one
  // side to dumb down to a lower level of the protocol.  Unfortunately,
  // there are enough broken servers out there that such a gross work-around
  // is necessary.  :(

  // Additional comment added in August 2006:
  // When we begun to use TLS hello extensions, we encountered a new class of
  // broken server, which simply stall for a very long time.
  // We would like to shorten the timeout, but limit this shorter timeout 
  // to the handshake phase.
  // When we arrive here for the first time (for a given socket),
  // we know the connection is established, and the application code
  // tried the first read or write. This triggers the beginning of the
  // SSL handshake phase at the SSL FD level.
  // We'll make a note of the current time,
  // and use this to measure the elapsed time since handshake begin.

  // Do NOT assume TLS intolerance on a closed connection after bad cert ui was shown.
  // Simply retry.
  // This depends on the fact that Cert UI will not be shown again,
  // should the user override the bad cert.

  PRBool handleHandshakeResultNow;
  socketInfo->GetHandshakePending(&handleHandshakeResultNow);

  PRBool wantRetry = PR_FALSE;

  if (0 > bytesTransfered) {
    PRInt32 err = PR_GetError();

    if (handleHandshakeResultNow) {
      if (PR_WOULD_BLOCK_ERROR == err) {
        socketInfo->SetHandshakeInProgress(PR_TRUE);
        return bytesTransfered;
      }

      wantRetry = 
        isClosedConnectionAfterBadCertUIWasShown(bytesTransfered, 
                                                 wasReading, 
                                                 err, 
                                                 socketInfo->GetBadCertUIStatus());

      if (!wantRetry // no decision yet
          && isTLSIntoleranceError(err, socketInfo->GetHasCleartextPhase()))
      {
        wantRetry = nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(ssl_layer_fd, socketInfo);
      }
    }
    
    // This is the common place where we trigger an error message on a SSL socket.
    // This might be reached at any time of the connection.
    if (!wantRetry && (IS_SSL_ERROR(err) || IS_SEC_ERROR(err))) {
      nsHandleSSLError(socketInfo, err);
    }
  }
  else if (wasReading && 0 == bytesTransfered) // zero bytes on reading, socket closed
  {
    if (handleHandshakeResultNow)
    {
      wantRetry = 
        isClosedConnectionAfterBadCertUIWasShown(bytesTransfered, 
                                                 wasReading, 
                                                 0, 
                                                 socketInfo->GetBadCertUIStatus());

      if (!wantRetry // no decision yet
          && !socketInfo->GetHasCleartextPhase()) // mirror PR_CONNECT_RESET_ERROR treament
      {
        wantRetry = 
          nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(ssl_layer_fd, socketInfo);
      }
    }
  }

  if (wantRetry) {
    // We want to cause the network layer to retry the connection.
    PR_SetError(PR_CONNECT_RESET_ERROR, 0);
    if (wasReading)
      bytesTransfered = -1;
  }

  // TLS intolerant servers only cause the first transfer to fail, so let's 
  // set the HandshakePending attribute to false so that we don't try the logic
  // above again in a subsequent transfer.
  if (handleHandshakeResultNow) {
    socketInfo->SetHandshakePending(PR_FALSE);
    socketInfo->SetHandshakeInProgress(PR_FALSE);
  }
  
  return bytesTransfered;
}

Here is the call graph for this function:

Here is the caller graph for this function:

PRFileDesc * nsSSLThread::getRealSSLFD ( nsNSSSocketInfo si) [static, private]

Definition at line 64 of file nsSSLThread.cpp.

Here is the caller graph for this function:

void PR_CALLBACK nsPSMBackgroundThread::nsThreadRunner ( void arg) [static, protected, inherited]

Definition at line 41 of file nsPSMBackgroundThread.cpp.

Here is the caller graph for this function:

Definition at line 1063 of file nsSSLThread.cpp.

Here is the caller graph for this function:

Definition at line 204 of file nsSSLThread.cpp.

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 376 of file nsSSLThread.cpp.

{
  if (!ssl_thread_singleton || !si)
    return PR_FAILURE;

  PRBool close_later = PR_FALSE;
  nsIRequest* requestToCancel = nsnull;

  {
    nsAutoLock threadLock(ssl_thread_singleton->mMutex);

    if (ssl_thread_singleton->mBusySocket == si) {
    
      // That's tricky, SSL thread is currently busy with this socket,
      // and might even be blocked on it (UI or OCSP).
      // We should not close the socket directly, but rather
      // schedule closing it, at the time the SSL thread is done.
      // If there is indeed a depending OCSP request pending,
      // we should cancel it now.
      
      if (ssl_thread_singleton->mPendingHTTPRequest)
      {
        requestToCancel = ssl_thread_singleton->mPendingHTTPRequest;
        ssl_thread_singleton->mPendingHTTPRequest = nsnull;
      }
      
      close_later = PR_TRUE;
      ssl_thread_singleton->mSocketScheduledToBeDestroyed = si;

      PR_NotifyAllCondVar(ssl_thread_singleton->mCond);
    }
  }

  if (requestToCancel)
  {
    if (nsIThread::IsMainThread())
    {
      requestToCancel->Cancel(NS_ERROR_ABORT);
    }
    else
    {
      NS_WARNING("Attempt to close SSL socket from a thread that is not the main thread. Can not cancel pending HTTP request from NSS");
    }
  
    NS_RELEASE(requestToCancel);
  }
  
  if (!close_later)
  {
    return si->CloseSocketAndDestroy();
  }
  
  return PR_SUCCESS;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 119 of file nsSSLThread.cpp.

{
  PRFileDesc *fd = getRealSSLFD(si);
  if (!fd)
    return PR_FAILURE;

  return fd->methods->connectcontinue(fd, out_flags);
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 82 of file nsPSMBackgroundThread.cpp.

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 90 of file nsSSLThread.cpp.

{
  PRFileDesc *fd = getRealSSLFD(si);
  if (!fd)
    return PR_FAILURE;

  return fd->methods->getpeername(fd, addr);
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 99 of file nsSSLThread.cpp.

{
  PRFileDesc *fd = getRealSSLFD(si);
  if (!fd)
    return PR_FAILURE;

  return fd->methods->getsocketoption(fd, data);
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 81 of file nsSSLThread.cpp.

{
  PRFileDesc *fd = getRealSSLFD(si);
  if (!fd)
    return PR_FAILURE;

  return fd->methods->getsockname(fd, addr);
}

Here is the call graph for this function:

Here is the caller graph for this function:

PRInt16 nsSSLThread::requestPoll ( nsNSSSocketInfo si,
PRInt16  in_flags,
PRInt16 out_flags 
) [static]

Definition at line 219 of file nsSSLThread.cpp.

{
  if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle)
    return 0;

  *out_flags = 0;

  PRBool want_sleep_and_wakeup_on_any_socket_activity = PR_FALSE;
  PRBool handshake_timeout = PR_FALSE;
  
  {
    nsAutoLock threadLock(ssl_thread_singleton->mMutex);

    if (ssl_thread_singleton->mBusySocket)
    {
      // If there is currently any socket busy on the SSL thread,
      // use our own poll method implementation.
      
      switch (si->mThreadData->mSSLState)
      {
        case nsSSLSocketThreadData::ssl_writing_done:
        {
          if (in_flags & PR_POLL_WRITE)
          {
            *out_flags |= PR_POLL_WRITE;
          }

          return in_flags;
        }
        break;
        
        case nsSSLSocketThreadData::ssl_reading_done:
        {
          if (in_flags & PR_POLL_READ)
          {
            *out_flags |= PR_POLL_READ;
          }

          return in_flags;
        }
        break;
        
        case nsSSLSocketThreadData::ssl_pending_write:
        case nsSSLSocketThreadData::ssl_pending_read:
        {
          if (si == ssl_thread_singleton->mBusySocket)
          {
            if (nsSSLIOLayerHelpers::mSharedPollableEvent)
            {
              // The lower layer of the socket is currently the pollable event,
              // which signals the readable state.
              
              return PR_POLL_READ;
            }
            else
            {
              // Unfortunately we do not have a pollable event
              // that we could use to wake up the caller, as soon
              // as the previously requested I/O operation has completed.
              // Therefore we must use a kind of busy wait,
              // we want the caller to check again, whenever any
              // activity is detected on the associated socket.
              // Unfortunately this could mean, the caller will detect
              // activity very often, until we are finally done with
              // the previously requested action and are able to
              // return the buffered result.
              // As our real I/O activity is happening on the other thread
              // let's sleep some cycles, in order to not waste all CPU
              // resources.
              // But let's make sure we do not hold our shared mutex
              // while waiting, so let's leave this block first.

              want_sleep_and_wakeup_on_any_socket_activity = PR_TRUE;
              break;
            }
          }
          else
          {
            // We should never get here, well, at least not with the current
            // implementation of SSL thread, where we have one worker only.
            // While another socket is busy, this socket "si" 
            // can not be marked with pending I/O at the same time.
            
            NS_NOTREACHED("Socket not busy on SSL thread marked as pending");
            return 0;
          }
        }
        break;
        
        case nsSSLSocketThreadData::ssl_idle:
        {
          if (si->mThreadData->mOneBytePendingFromEarlierWrite)
          {
            if (in_flags & PR_POLL_WRITE)
            {
              // In this scenario we always want the caller to immediately
              // try a write again, because it might not wake up otherwise.
              *out_flags |= PR_POLL_WRITE;
              return in_flags;
            }
          }

          handshake_timeout = si->HandshakeTimeout();

          if (si != ssl_thread_singleton->mBusySocket)
          {
            // Some other socket is currently busy on the SSL thread.
            // It is possible that busy socket is currently blocked (e.g. by UI).
            // Therefore we should not report "si" as being readable/writeable,
            // regardless whether it is.
            // (Because if we did report readable/writeable to the caller,
            // the caller would repeatedly request us to do I/O, 
            // although our read/write function would not be able to fulfil
            // the request, because our single worker is blocked).
            // To avoid the unnecessary busy loop in that scenario, 
            // for socket "si" we report "not ready" to the caller.
            // We do this by faking our caller did not ask for neither
            // readable nor writeable when querying the lower layer.
            // (this will leave querying for exceptions enabled)
            
            in_flags &= ~(PR_POLL_READ | PR_POLL_WRITE);
          }
        }
        break;
        
        default:
          break;
      }
    }
    else
    {
      handshake_timeout = si->HandshakeTimeout();
    }

    if (handshake_timeout)
    {
      NS_ASSERTION(in_flags & PR_POLL_EXCEPT, "nsSSLThread::requestPoll handshake timeout, but caller did not poll for EXCEPT");

      *out_flags |= PR_POLL_EXCEPT;
      return in_flags;
    }
  }

  if (want_sleep_and_wakeup_on_any_socket_activity)
  {
    // This is where we wait for any socket activity,
    // because we do not have a pollable event.
    // XXX Will this really cause us to wake up
    //     whatever happens?

    PR_Sleep( PR_MillisecondsToInterval(1) );
    return PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
  }

  return si->mFd->lower->methods->poll(si->mFd->lower, in_flags, out_flags);
}

Here is the call graph for this function:

Here is the caller graph for this function:

PRInt32 nsSSLThread::requestRead ( nsNSSSocketInfo si,
void buf,
PRInt32  amount 
) [static]

Definition at line 455 of file nsSSLThread.cpp.

{
  if (!ssl_thread_singleton || !si || !buf || !amount || !ssl_thread_singleton->mThreadHandle)
  {
    PR_SetError(PR_UNKNOWN_ERROR, 0);
    return -1;
  }

  PRBool this_socket_is_busy = PR_FALSE;
  PRBool some_other_socket_is_busy = PR_FALSE;
  nsSSLSocketThreadData::ssl_state my_ssl_state;

  {
    nsAutoLock threadLock(ssl_thread_singleton->mMutex);

    if (ssl_thread_singleton->mExitRequested) {
      PR_SetError(PR_UNKNOWN_ERROR, 0);
      return -1;
    }

    my_ssl_state = si->mThreadData->mSSLState;

    if (ssl_thread_singleton->mBusySocket == si)
    {
      this_socket_is_busy = PR_TRUE;

      if (my_ssl_state == nsSSLSocketThreadData::ssl_reading_done)
      {
        // we will now care for the data that's ready,
        // the socket is no longer busy on the ssl thread
        
        restoreOriginalSocket_locked(si);

        ssl_thread_singleton->mBusySocket = nsnull;
        
        // We'll handle the results further down,
        // while not holding the lock.
      }
    }
    else if (ssl_thread_singleton->mBusySocket)
    {
      some_other_socket_is_busy = PR_TRUE;
    }

    if (!this_socket_is_busy && si->HandshakeTimeout())
    {
      restoreOriginalSocket_locked(si);
      PR_SetError(PR_CONNECT_RESET_ERROR, 0);
      checkHandshake(-1, PR_TRUE, si->mFd->lower, si);
      return -1;
    }
  }

  switch (my_ssl_state)
  {
    case nsSSLSocketThreadData::ssl_idle:
      {
        NS_ASSERTION(!this_socket_is_busy, "oops, unexpected incosistency");
        
        if (some_other_socket_is_busy)
        {
          PORT_SetError(PR_WOULD_BLOCK_ERROR);
          return -1;
        }
        
        // ssl thread is not busy, we'll continue below
      }
      break;

    case nsSSLSocketThreadData::ssl_reading_done:
      // there has been a previous request to read, that is now done!
      {
        // failure ?
        if (si->mThreadData->mSSLResultRemainingBytes < 0) {
          if (si->mThreadData->mPRErrorCode != PR_SUCCESS) {
            PR_SetError(si->mThreadData->mPRErrorCode, 0);
            si->mThreadData->mPRErrorCode = PR_SUCCESS;
          }

          si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
          return si->mThreadData->mSSLResultRemainingBytes;
        }

        PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes);

        memcpy(buf, si->mThreadData->mSSLRemainingReadResultData, return_amount);

        si->mThreadData->mSSLResultRemainingBytes -= return_amount;

        if (!si->mThreadData->mSSLResultRemainingBytes) {
          si->mThreadData->mSSLRemainingReadResultData = nsnull;
          si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
        }
        else {
          si->mThreadData->mSSLRemainingReadResultData += return_amount;
        }

        return return_amount;
      }
      // we never arrive here, see return statement above
      break;


    // We should not see the following events here,
    // because we have not yet signaled Necko that we are 
    // readable/writable again, so if we end up here,
    // it means that Necko decided to try read/write again,
    // for whatever reason. No problem, just return would_block,
    case nsSSLSocketThreadData::ssl_pending_write:
    case nsSSLSocketThreadData::ssl_pending_read:

    // We should not see this state here, because Necko has previously
    // requested us to write, Necko is not yet aware that it's done,
    // (although it meanwhile is), but Necko now tries to read?
    // If that ever happens, it's confusing, but not a problem,
    // just let Necko know we can not do that now and return would_block.
    case nsSSLSocketThreadData::ssl_writing_done:

    // for safety reasons, also return would_block on any other state,
    // although this switch statement should be complete and list
    // the appropriate behaviour for each state.
    default:
      {
        PORT_SetError(PR_WOULD_BLOCK_ERROR);
        return -1;
      }
      // we never arrive here, see return statement above
      break;
  }

  if (si->isPK11LoggedOut() || si->isAlreadyShutDown()) {
    PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
    return -1;
  }

  if (si->GetCanceled()) {
    return PR_FAILURE;
  }

  // si is idle and good, and no other socket is currently busy,
  // so it's fine to continue with the request.

  if (!si->mThreadData->ensure_buffer_size(amount))
  {
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    return -1;
  }
  
  si->mThreadData->mSSLRequestedTransferAmount = amount;
  si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_pending_read;

  // Remember we are operating on a layered file descriptor, that consists of
  // a PSM code layer (nsNSSIOLayer), a NSS code layer (SSL protocol logic), 
  // and the raw socket at the bottommost layer.
  //
  // We don't want to call the SSL layer read/write directly on this thread, 
  // because it might block, should a callback to UI (for user confirmation)
  // or Necko (for retrieving OCSP verification data) be necessary.
  // As Necko is single threaded, it is currently waiting for this 
  // function to return, and a callback into Necko from NSS couldn't succeed.
  // 
  // Therefore we must defer the request to read/write to a separate SSL thread.
  // We will return WOULD_BLOCK to Necko, and will return the real results
  // once the I/O operation on the SSL thread is ready.
  //
  // The tricky part is to wake up Necko, as soon as the I/O operation 
  // on the SSL thread is done.
  //
  // In order to achieve that, we manipulate the layering of the file 
  // descriptor. Usually the PSM layer points to the SSL layer as its lower 
  // layer. We change that to a pollable event file descriptor.
  //
  // Once we return from this original read/write function, Necko will 
  // poll/select on the file descriptor. As result data is not yet ready, we will 
  // instruct Necko to select on the bottommost file descriptor 
  // (by using appropriate flags in PSM's layer implementation of the 
  // poll method), which is the pollable event.
  //
  // Once the SSL thread is done with the call to the SSL layer, it will 
  // "set" the pollable event, causing Necko to wake up on the file descriptor 
  // and call read/write again. Now that the file descriptor is in the done state, 
  // we'll arrive in this read/write function again. We'll detect the socket is 
  // in the done state, and restore the original SSL level file descriptor.
  // Finally, we return the data obtained on the SSL thread back to our caller.

  {
    nsAutoLock threadLock(ssl_thread_singleton->mMutex);

    if (nsSSLIOLayerHelpers::mSharedPollableEvent)
    {
      NS_ASSERTION(!nsSSLIOLayerHelpers::mSocketOwningPollableEvent, 
                   "oops, some other socket still owns our shared pollable event");
  
      NS_ASSERTION(!si->mThreadData->mReplacedSSLFileDesc, "oops");
  
      si->mThreadData->mReplacedSSLFileDesc = si->mFd->lower;
      si->mFd->lower = nsSSLIOLayerHelpers::mSharedPollableEvent;
    }

    nsSSLIOLayerHelpers::mSocketOwningPollableEvent = si;
    ssl_thread_singleton->mBusySocket = si;

    // notify the thread
    PR_NotifyAllCondVar(ssl_thread_singleton->mCond);
  }

  PORT_SetError(PR_WOULD_BLOCK_ERROR);
  return -1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

PRInt32 nsSSLThread::requestRecvMsgPeek ( nsNSSSocketInfo si,
void buf,
PRInt32  amount,
PRIntn  flags,
PRIntervalTime  timeout 
) [static]

Definition at line 129 of file nsSSLThread.cpp.

{
  if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle)
  {
    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
    return -1;
  }

  PRFileDesc *realSSLFD;

  {
    nsAutoLock threadLock(ssl_thread_singleton->mMutex);

    if (si == ssl_thread_singleton->mBusySocket)
    {
      PORT_SetError(PR_WOULD_BLOCK_ERROR);
      return -1;
    }

    switch (si->mThreadData->mSSLState)
    {
      case nsSSLSocketThreadData::ssl_idle:
        break;
    
      case nsSSLSocketThreadData::ssl_reading_done:
        {
          // we have data available that we can return

          // if there was a failure, just return the failure,
          // but do not yet clear our state, that should happen
          // in the call to "read".

          if (si->mThreadData->mSSLResultRemainingBytes < 0) {
            if (si->mThreadData->mPRErrorCode != PR_SUCCESS) {
              PR_SetError(si->mThreadData->mPRErrorCode, 0);
            }

            return si->mThreadData->mSSLResultRemainingBytes;
          }

          PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes);

          memcpy(buf, si->mThreadData->mSSLRemainingReadResultData, return_amount);

          return return_amount;
        }

      case nsSSLSocketThreadData::ssl_writing_done:
      case nsSSLSocketThreadData::ssl_pending_write:
      case nsSSLSocketThreadData::ssl_pending_read:

      // for safety reasons, also return would_block on any other state,
      // although this switch statement should be complete and list
      // the appropriate behaviour for each state.
      default:
        {
          PORT_SetError(PR_WOULD_BLOCK_ERROR);
          return -1;
        }
    }

    if (si->mThreadData->mReplacedSSLFileDesc)
    {
      realSSLFD = si->mThreadData->mReplacedSSLFileDesc;
    }
    else
    {
      realSSLFD = si->mFd->lower;
    }
  }

  return realSSLFD->methods->recv(realSSLFD, buf, amount, flags, timeout);
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 109 of file nsSSLThread.cpp.

{
  PRFileDesc *fd = getRealSSLFD(si);
  if (!fd)
    return PR_FAILURE;

  return fd->methods->setsocketoption(fd, data);
}

Here is the call graph for this function:

Here is the caller graph for this function:

PRInt32 nsSSLThread::requestWrite ( nsNSSSocketInfo si,
const void buf,
PRInt32  amount 
) [static]

Definition at line 665 of file nsSSLThread.cpp.

{
  if (!ssl_thread_singleton || !si || !buf || !amount || !ssl_thread_singleton->mThreadHandle)
  {
    PR_SetError(PR_UNKNOWN_ERROR, 0);
    return -1;
  }

  PRBool this_socket_is_busy = PR_FALSE;
  PRBool some_other_socket_is_busy = PR_FALSE;
  nsSSLSocketThreadData::ssl_state my_ssl_state;
  
  {
    nsAutoLock threadLock(ssl_thread_singleton->mMutex);
    
    if (ssl_thread_singleton->mExitRequested) {
      PR_SetError(PR_UNKNOWN_ERROR, 0);
      return -1;
    }

    my_ssl_state = si->mThreadData->mSSLState;

    if (ssl_thread_singleton->mBusySocket == si)
    {
      this_socket_is_busy = PR_TRUE;
      
      if (my_ssl_state == nsSSLSocketThreadData::ssl_writing_done)
      {
        // we will now care for the data that's ready,
        // the socket is no longer busy on the ssl thread
        
        restoreOriginalSocket_locked(si);

        ssl_thread_singleton->mBusySocket = nsnull;
        
        // We'll handle the results further down,
        // while not holding the lock.
      }
    }
    else if (ssl_thread_singleton->mBusySocket)
    {
      some_other_socket_is_busy = PR_TRUE;
    }

    if (!this_socket_is_busy && si->HandshakeTimeout())
    {
      restoreOriginalSocket_locked(si);
      PR_SetError(PR_CONNECT_RESET_ERROR, 0);
      checkHandshake(-1, PR_FALSE, si->mFd->lower, si);
      return -1;
    }
  }

  switch (my_ssl_state)
  {
    case nsSSLSocketThreadData::ssl_idle:
      {
        NS_ASSERTION(!this_socket_is_busy, "oops, unexpected incosistency");
        
        if (some_other_socket_is_busy)
        {
          PORT_SetError(PR_WOULD_BLOCK_ERROR);
          return -1;
        }
        
        // ssl thread is not busy, we'll continue below
      }
      break;

    case nsSSLSocketThreadData::ssl_writing_done:
      // there has been a previous request to write, that is now done!
      {
        // failure ?
        if (si->mThreadData->mSSLResultRemainingBytes < 0) {
          if (si->mThreadData->mPRErrorCode != PR_SUCCESS) {
            PR_SetError(si->mThreadData->mPRErrorCode, 0);
            si->mThreadData->mPRErrorCode = PR_SUCCESS;
          }

          si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
          return si->mThreadData->mSSLResultRemainingBytes;
        }

        PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes);

        si->mThreadData->mSSLResultRemainingBytes -= return_amount;

        if (!si->mThreadData->mSSLResultRemainingBytes) {
          si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
        }

        return return_amount;
      }
      break;
      
    // We should not see the following events here,
    // because we have not yet signaled Necko that we are 
    // readable/writable again, so if we end up here,
    // it means that Necko decided to try read/write again,
    // for whatever reason. No problem, just return would_block,
    case nsSSLSocketThreadData::ssl_pending_write:
    case nsSSLSocketThreadData::ssl_pending_read:

    // We should not see this state here, because Necko has previously
    // requested us to read, Necko is not yet aware that it's done,
    // (although it meanwhile is), but Necko now tries to write?
    // If that ever happens, it's confusing, but not a problem,
    // just let Necko know we can not do that now and return would_block.
    case nsSSLSocketThreadData::ssl_reading_done:

    // for safety reasons, also return would_block on any other state,
    // although this switch statement should be complete and list
    // the appropriate behaviour for each state.
    default:
      {
        PORT_SetError(PR_WOULD_BLOCK_ERROR);
        return -1;
      }
      // we never arrive here, see return statement above
      break;
  }

  if (si->isPK11LoggedOut() || si->isAlreadyShutDown()) {
    PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
    return -1;
  }

  if (si->GetCanceled()) {
    return PR_FAILURE;
  }

  // si is idle and good, and no other socket is currently busy,
  // so it's fine to continue with the request.

  // However, use special handling for the 
  //   mOneBytePendingFromEarlierWrite
  // scenario, where we will not change any of our buffers at this point, 
  // as we are waiting for completion of the earlier write.

  if (!si->mThreadData->mOneBytePendingFromEarlierWrite)
  {
    if (!si->mThreadData->ensure_buffer_size(amount))
    {
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
      return -1;
    }
  
    memcpy(si->mThreadData->mSSLDataBuffer, buf, amount);
    si->mThreadData->mSSLRequestedTransferAmount = amount;
  }

  si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_pending_write;

  {
    nsAutoLock threadLock(ssl_thread_singleton->mMutex);

    if (nsSSLIOLayerHelpers::mSharedPollableEvent)
    {
      NS_ASSERTION(!nsSSLIOLayerHelpers::mSocketOwningPollableEvent, 
                   "oops, some other socket still owns our shared pollable event");
  
      NS_ASSERTION(!si->mThreadData->mReplacedSSLFileDesc, "oops");
  
      si->mThreadData->mReplacedSSLFileDesc = si->mFd->lower;
      si->mFd->lower = nsSSLIOLayerHelpers::mSharedPollableEvent;
    }

    nsSSLIOLayerHelpers::mSocketOwningPollableEvent = si;
    ssl_thread_singleton->mBusySocket = si;

    PR_NotifyAllCondVar(ssl_thread_singleton->mCond);
  }

  PORT_SetError(PR_WOULD_BLOCK_ERROR);
  return -1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void nsSSLThread::Run ( void  ) [private, virtual]

Implements nsPSMBackgroundThread.

Definition at line 842 of file nsSSLThread.cpp.

{
  // Helper variable, we don't want to call destroy 
  // while holding the mutex.
  nsNSSSocketInfo *socketToDestroy = nsnull;

  while (PR_TRUE)
  {
    if (socketToDestroy)
    {
      socketToDestroy->CloseSocketAndDestroy();
      socketToDestroy = nsnull;
    }

    // remember whether we'll write or read
    nsSSLSocketThreadData::ssl_state busy_socket_ssl_state;
  
    {
      // In this scope we need mutex protection,
      // as we find out what needs to be done.
      
      nsAutoLock threadLock(ssl_thread_singleton->mMutex);
      
      if (mSocketScheduledToBeDestroyed)
      {
        if (mBusySocket == mSocketScheduledToBeDestroyed)
        {
          // That's rare, but it happens.
          // We have received a request to close the socket,
          // although I/O results have not yet been consumed.

          restoreOriginalSocket_locked(mBusySocket);

          mBusySocket->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
          mBusySocket = nsnull;
        }
      
        socketToDestroy = mSocketScheduledToBeDestroyed;
        mSocketScheduledToBeDestroyed = nsnull;
        continue; // go back and finally destroy it, before doing anything else
      }

      if (mExitRequested)
        break;

      PRBool pending_work = PR_FALSE;

      do
      {
        if (mBusySocket
            &&
              (mBusySocket->mThreadData->mSSLState == nsSSLSocketThreadData::ssl_pending_read
              ||
              mBusySocket->mThreadData->mSSLState == nsSSLSocketThreadData::ssl_pending_write))
        {
          pending_work = PR_TRUE;
        }

        if (!pending_work)
        {
          // no work to do ? let's wait a moment

          PR_WaitCondVar(mCond, PR_INTERVAL_NO_TIMEOUT);
        }
        
      } while (!pending_work && !mExitRequested && !mSocketScheduledToBeDestroyed);
      
      if (mSocketScheduledToBeDestroyed)
        continue;
      
      if (mExitRequested)
        break;
      
      if (!pending_work)
        continue;
      
      busy_socket_ssl_state = mBusySocket->mThreadData->mSSLState;
    }

    {
      // In this scope we need to make sure NSS does not go away
      // while we are busy.
      nsNSSShutDownPreventionLock locker;

      // Reference for shorter code and to avoid multiple dereferencing.
      nsSSLSocketThreadData &bstd = *mBusySocket->mThreadData;

      PRFileDesc *realFileDesc = bstd.mReplacedSSLFileDesc;
      if (!realFileDesc)
      {
        realFileDesc = mBusySocket->mFd->lower;
      }

      if (nsSSLSocketThreadData::ssl_pending_write == busy_socket_ssl_state)
      {
        PRInt32 bytesWritten = 0;

        if (bstd.mOneBytePendingFromEarlierWrite)
        {
          // Let's try to flush the final pending byte (that libSSL might already have 
          // processed). Let's be correct and send the final byte from our buffer.
          bytesWritten = realFileDesc->methods
            ->write(realFileDesc, &bstd.mThePendingByte, 1);
  
#ifdef DEBUG_SSL_VERBOSE
          PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes\n", (void*)realFileDesc, bytesWritten));
#endif
          
          bytesWritten = checkHandshake(bytesWritten, PR_FALSE, realFileDesc, mBusySocket);
          if (bytesWritten < 0) {
            // give the error back to caller
            bstd.mPRErrorCode = PR_GetError();
          }
          else if (bytesWritten == 1) {
            // Cool, all flushed now. We can exit the one-byte-pending mode, 
            // and report the full amount back to the caller. 
            bytesWritten = bstd.mOriginalRequestedTransferAmount;
            bstd.mOriginalRequestedTransferAmount = 0;
            bstd.mOneBytePendingFromEarlierWrite = PR_FALSE;
          }
        }
        else
        {
          // standard code, try to write the buffer we've been given just now 
          bytesWritten = realFileDesc->methods
            ->write(realFileDesc, 
                    bstd.mSSLDataBuffer, 
                    bstd.mSSLRequestedTransferAmount);
  
#ifdef DEBUG_SSL_VERBOSE
          PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes (out of %d)\n", 
              (void*)realFileDesc, bytesWritten, bstd.mSSLRequestedTransferAmount));
#endif
          
          bytesWritten = checkHandshake(bytesWritten, PR_FALSE, realFileDesc, mBusySocket);
          if (bytesWritten < 0) {
            // give the error back to caller
            bstd.mPRErrorCode = PR_GetError();
          }
          else if (bstd.mSSLRequestedTransferAmount > 1 && 
                   bytesWritten == (bstd.mSSLRequestedTransferAmount - 1)) {
            // libSSL signaled us a short write.
            // While libSSL accepted all data, not all bytes were flushed to the OS socket.
            bstd.mThePendingByte = *(bstd.mSSLDataBuffer + (bstd.mSSLRequestedTransferAmount-1));
            bytesWritten = -1;
            bstd.mPRErrorCode = PR_WOULD_BLOCK_ERROR;
            bstd.mOneBytePendingFromEarlierWrite = PR_TRUE;
            bstd.mOriginalRequestedTransferAmount = bstd.mSSLRequestedTransferAmount;
          }
        }

        bstd.mSSLResultRemainingBytes = bytesWritten;
        busy_socket_ssl_state = nsSSLSocketThreadData::ssl_writing_done;
      }
      else if (nsSSLSocketThreadData::ssl_pending_read == busy_socket_ssl_state)
      {
        PRInt32 bytesRead = realFileDesc->methods
           ->read(realFileDesc, 
                  bstd.mSSLDataBuffer, 
                  bstd.mSSLRequestedTransferAmount);

#ifdef DEBUG_SSL_VERBOSE
        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] read %d bytes\n", (void*)realFileDesc, bytesRead));
#endif
        bytesRead = checkHandshake(bytesRead, PR_TRUE, realFileDesc, mBusySocket);
        if (bytesRead < 0) {
          // give the error back to caller
          bstd.mPRErrorCode = PR_GetError();
        }

        bstd.mSSLResultRemainingBytes = bytesRead;
        bstd.mSSLRemainingReadResultData = bstd.mSSLDataBuffer;
        busy_socket_ssl_state = nsSSLSocketThreadData::ssl_reading_done;
      }
    }

    // avoid setting event repeatedly
    PRBool needToSetPollableEvent = PR_FALSE;

    {
      nsAutoLock threadLock(ssl_thread_singleton->mMutex);
      
      mBusySocket->mThreadData->mSSLState = busy_socket_ssl_state;
      
      if (!nsSSLIOLayerHelpers::mPollableEventCurrentlySet)
      {
        needToSetPollableEvent = PR_TRUE;
        nsSSLIOLayerHelpers::mPollableEventCurrentlySet = PR_TRUE;
      }
    }

    if (needToSetPollableEvent && nsSSLIOLayerHelpers::mSharedPollableEvent)
    {
      // Wake up the file descriptor on the Necko thread,
      // so it can fetch the results from the SSL I/O call 
      // that we just completed.
      PR_SetPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent);

      // if we don't have a pollable event, we'll have to wake up
      // the caller by other means.
    }
  }

  {
    nsAutoLock threadLock(ssl_thread_singleton->mMutex);
    if (mBusySocket)
    {
      restoreOriginalSocket_locked(mBusySocket);
      mBusySocket = nsnull;
    }
    if (!nsSSLIOLayerHelpers::mPollableEventCurrentlySet)
    {
      nsSSLIOLayerHelpers::mPollableEventCurrentlySet = PR_TRUE;
      if (nsSSLIOLayerHelpers::mSharedPollableEvent)
      {
        PR_SetPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent);
      }
    }
  }
}

Here is the call graph for this function:

Definition at line 57 of file nsPSMBackgroundThread.cpp.

Here is the call graph for this function:

Here is the caller graph for this function:


Member Data Documentation

Definition at line 66 of file nsSSLThread.h.

PRCondVar* nsPSMBackgroundThread::mCond [protected, inherited]

Definition at line 60 of file nsPSMBackgroundThread.h.

Definition at line 63 of file nsPSMBackgroundThread.h.

PRLock* nsPSMBackgroundThread::mMutex [protected, inherited]

Definition at line 57 of file nsPSMBackgroundThread.h.

Definition at line 82 of file nsSSLThread.h.

Definition at line 74 of file nsSSLThread.h.

Definition at line 51 of file nsPSMBackgroundThread.h.

Definition at line 105 of file nsSSLThread.h.


The documentation for this class was generated from the following files: