summaryrefslogtreecommitdiff
path: root/ACE/ace/SSL/SSL_SOCK_Acceptor.cpp
blob: 9ec3d58e9a23edb4a6edf624de199a280b398169 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
// -*- C++ -*-

#include "SSL_SOCK_Acceptor.h"

#include "ace/Handle_Set.h"
#include "ace/OS_Errno.h"
#include "ace/OS_NS_errno.h"
#include "ace/Log_Category.h"
#include "ace/Time_Value.h"
#include "ace/Countdown_Time.h"
#include "ace/Truncate.h"

#if !defined (__ACE_INLINE__)
#include "SSL_SOCK_Acceptor.inl"
#endif /* __ACE_INLINE__ */

ACE_BEGIN_VERSIONED_NAMESPACE_DECL

ACE_ALLOC_HOOK_DEFINE(ACE_SSL_SOCK_Acceptor)

ACE_SSL_SOCK_Acceptor::~ACE_SSL_SOCK_Acceptor (void)
{
  ACE_TRACE ("ACE_SSL_SOCK_Acceptor::~ACE_SSL_SOCK_Acceptor");
}

int
ACE_SSL_SOCK_Acceptor::ssl_accept (ACE_SSL_SOCK_Stream &new_stream,
                                   ACE_Time_Value *timeout) const
{
  SSL *ssl = new_stream.ssl ();

  if (SSL_is_init_finished (ssl))
    return 0;

  if (!SSL_in_accept_init (ssl))
    ::SSL_set_accept_state (ssl);

  ACE_HANDLE handle = new_stream.get_handle ();

  // We're going to call SSL_accept, optionally doing ACE::select and
  // retrying the SSL_accept, until the SSL handshake is done or
  // it fails.
  // To get the timeout affect, set the socket to nonblocking mode
  // before beginning if there is a timeout specified. If the timeout
  // is 0 (wait as long as it takes) then don't worry about the blocking
  // status; we'll block in SSL_accept if the socket is blocking, and
  // block in ACE::select if not.
  int reset_blocking_mode = 0;
  if (timeout != 0)
    {
      reset_blocking_mode = ACE_BIT_DISABLED (ACE::get_flags (handle),
                                              ACE_NONBLOCK);
          // Set the handle into non-blocking mode if it's not already
          // in it.
          if (reset_blocking_mode
              && ACE::set_flags (handle,
                                 ACE_NONBLOCK) == -1)
            return -1;
    }

  // Take into account the time between each select() call below.
  ACE_Countdown_Time countdown (timeout);

  int status;
  do
    {
      // These handle sets are used to set up for whatever SSL_accept
      // says it wants next. They're reset on each pass around the loop.
      ACE_Handle_Set rd_handle;
      ACE_Handle_Set wr_handle;

      status = ::SSL_accept (ssl);
      switch (::SSL_get_error (ssl, status))
        {
        case SSL_ERROR_NONE:
          status = 0;               // To tell caller about success
          break;                    // Done

        case SSL_ERROR_WANT_WRITE:
          wr_handle.set_bit (handle);
          status = 1;               // Wait for more activity
          break;

        case SSL_ERROR_WANT_READ:
          rd_handle.set_bit (handle);
          status = 1;               // Wait for more activity
          break;

        case SSL_ERROR_ZERO_RETURN:
          // The peer has notified us that it is shutting down via
          // the SSL "close_notify" message so we need to
          // shutdown, too.
          status = -1;
          break;

        case SSL_ERROR_SYSCALL:
          // On some platforms (e.g. MS Windows) OpenSSL does not
          // store the last error in errno so explicitly do so.
          //
          // Explicitly check for EWOULDBLOCK since it doesn't get
          // converted to an SSL_ERROR_WANT_{READ,WRITE} on some
          // platforms. If SSL_accept failed outright, though, don't
          // bother checking more. This can happen if the socket gets
          // closed during the handshake.
          if (ACE_OS::set_errno_to_last_error () == EWOULDBLOCK &&
              status == -1)
            {
              // Although the SSL_ERROR_WANT_READ/WRITE isn't getting
              // set correctly, the read/write state should be valid.
              // Use that to decide what to do.
              status = 1;               // Wait for more activity
              if (SSL_want_write (ssl))
                wr_handle.set_bit (handle);
              else if (SSL_want_read (ssl))
                rd_handle.set_bit (handle);
              else
                status = -1;            // Doesn't want anything - bail out
            }
          else
            status = -1;
          break;

        default:
          ACE_SSL_Context::report_error ();
          status = -1;
          break;
        }

      if (status == 1)
        {
          // Must have at least one handle to wait for at this point.
          ACE_ASSERT (rd_handle.num_set() == 1 || wr_handle.num_set () == 1);
          status = ACE::select (int (handle) + 1,
                                &rd_handle,
                                &wr_handle,
                                0,
                                timeout);

          (void) countdown.update ();

          // 0 is timeout, so we're done.
          // -1 is error, so we're done.
          // Could be both handles set (same handle in both masks) so
          // set to 1.
          if (status >= 1)
            status = 1;
          else                   // Timeout or failure
            status = -1;
        }

    } while (status == 1 && !SSL_is_init_finished (ssl));

  if (reset_blocking_mode)
    {
      ACE_Errno_Guard eguard (errno);
      ACE::clr_flags (handle, ACE_NONBLOCK);
    }

  return (status == -1 ? -1 : 0);

}

// General purpose routine for accepting new connections.
// Since our underlying acceptor is of the plain old ACE_SOCK_Acceptor
// variety, get the basic socket setup done with it, then take care of
// the SSL handshake if the socket is accepted.
int
ACE_SSL_SOCK_Acceptor::accept (ACE_SSL_SOCK_Stream &new_stream,
                               ACE_Addr *remote_addr,
                               ACE_Time_Value *timeout,
                               bool restart,
                               bool reset_new_handle) const
{
  ACE_TRACE ("ACE_SSL_SOCK_Acceptor::accept");

  // Take into account the time to complete the basic TCP handshake
  // and the SSL handshake.
  ACE_Countdown_Time countdown (timeout);

  ACE_SOCK_Stream temp_stream;
  if (-1 == this->acceptor_.accept (temp_stream,
                                    remote_addr,
                                    timeout,
                                    restart,
                                    reset_new_handle))
    return -1;

  (void) countdown.update ();

  new_stream.set_handle (temp_stream.get_handle ());
  temp_stream.set_handle (ACE_INVALID_HANDLE);

  if (this->ssl_accept (new_stream, timeout) == -1)
    {
      new_stream.close ();
      new_stream.set_handle (ACE_INVALID_HANDLE);
      return -1;
    }

  return 0;

}

int
ACE_SSL_SOCK_Acceptor::accept (ACE_SSL_SOCK_Stream &new_stream,
                               ACE_Accept_QoS_Params qos_params,
                               ACE_Addr *remote_addr,
                               ACE_Time_Value *timeout,
                               bool restart,
                               bool reset_new_handle) const
{
  ACE_TRACE ("ACE_SSL_SOCK_Acceptor::accept");

  // Take into account the time to complete the basic TCP handshake
  // and the SSL handshake.
  ACE_Countdown_Time countdown (timeout);

  ACE_SOCK_Stream temp_stream;
  if (-1 == this->acceptor_.accept (temp_stream,
                                    qos_params,
                                    remote_addr,
                                    timeout,
                                    restart,
                                    reset_new_handle))
    return -1;

  (void) countdown.update ();

  new_stream.set_handle (temp_stream.get_handle ());
  temp_stream.set_handle (ACE_INVALID_HANDLE);

  if (this->ssl_accept (new_stream, timeout) == -1)
    {
      new_stream.close ();
      new_stream.set_handle (ACE_INVALID_HANDLE);
      return -1;
    }

  return 0;
}

ACE_END_VERSIONED_NAMESPACE_DECL