OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * TlsSocket provides a secure (SSL or TLS) client connection to a server. | 6 * TlsSocket provides a secure (SSL or TLS) client connection to a server. |
7 * The certificate provided by the server is checked | 7 * The certificate provided by the server is checked |
8 * using the certificate database provided in setCertificateDatabase. | 8 * using the certificate database provided in setCertificateDatabase. |
9 */ | 9 */ |
10 abstract class TlsSocket implements Socket { | 10 abstract class TlsSocket implements Socket { |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
114 OutputStream get outputStream { | 114 OutputStream get outputStream { |
115 // TODO(6701): Implement stream interfaces on TlsSocket. | 115 // TODO(6701): Implement stream interfaces on TlsSocket. |
116 throw new UnimplementedError("TlsSocket.inputStream not implemented yet"); | 116 throw new UnimplementedError("TlsSocket.inputStream not implemented yet"); |
117 } | 117 } |
118 | 118 |
119 int available() { | 119 int available() { |
120 throw new UnimplementedError("TlsSocket.available not implemented yet"); | 120 throw new UnimplementedError("TlsSocket.available not implemented yet"); |
121 } | 121 } |
122 | 122 |
123 void close([bool halfClose]) { | 123 void close([bool halfClose]) { |
124 _socket.close(halfClose); | 124 if (halfClose) { |
| 125 _closedWrite = true; |
| 126 _writeEncryptedData(); |
| 127 if (_filterWriteEmpty) { |
| 128 _socket.close(true); |
| 129 _socketClosedWrite = true; |
| 130 } |
| 131 } else { |
| 132 _closedWrite = true; |
| 133 _closedRead = true; |
| 134 _socket.close(false); |
| 135 _socketClosedWrite = true; |
| 136 _socketClosedRead = true; |
| 137 _tlsFilter.destroy(); |
| 138 _tlsFilter = null; |
| 139 if (scheduledDataEvent != null) { |
| 140 scheduledDataEvent.cancel(); |
| 141 } |
| 142 _status = CLOSED; |
| 143 } |
125 } | 144 } |
126 | 145 |
127 List<int> read([int len]) { | 146 List<int> read([int len]) { |
| 147 if (_closedRead) { |
| 148 throw new SocketException("Reading from a closed socket"); |
| 149 } |
128 var buffer = _tlsFilter.buffers[READ_PLAINTEXT]; | 150 var buffer = _tlsFilter.buffers[READ_PLAINTEXT]; |
129 _readEncryptedData(); | 151 _readEncryptedData(); |
130 int toRead = buffer.length; | 152 int toRead = buffer.length; |
131 if (len != null) { | 153 if (len != null) { |
132 if (len is! int || len < 0) { | 154 if (len is! int || len < 0) { |
133 throw new ArgumentError( | 155 throw new ArgumentError( |
134 "Invalid len parameter in TlsSocket.read (len: $len)"); | 156 "Invalid len parameter in TlsSocket.read (len: $len)"); |
135 } | 157 } |
136 if (len < toRead) { | 158 if (len < toRead) { |
137 toRead = len; | 159 toRead = len; |
138 } | 160 } |
139 } | 161 } |
140 List<int> result = buffer.data.getRange(buffer.start, toRead); | 162 List<int> result = buffer.data.getRange(buffer.start, toRead); |
141 buffer.advanceStart(toRead); | 163 buffer.advanceStart(toRead); |
142 _setHandlersAfterRead(); | 164 _setHandlersAfterRead(); |
143 return result; | 165 return result; |
144 } | 166 } |
145 | 167 |
146 int readList(List<int> data, int offset, int bytes) { | 168 int readList(List<int> data, int offset, int bytes) { |
| 169 if (_closedRead) { |
| 170 throw new SocketException("Reading from a closed socket"); |
| 171 } |
147 if (offset < 0 || bytes < 0 || offset + bytes > data.length) { | 172 if (offset < 0 || bytes < 0 || offset + bytes > data.length) { |
148 throw new ArgumentError( | 173 throw new ArgumentError( |
149 "Invalid offset or bytes in TlsSocket.readList"); | 174 "Invalid offset or bytes in TlsSocket.readList"); |
150 } | 175 } |
151 | 176 |
152 int bytesRead = 0; | 177 int bytesRead = 0; |
153 var buffer = _tlsFilter.buffers[READ_PLAINTEXT]; | 178 var buffer = _tlsFilter.buffers[READ_PLAINTEXT]; |
154 // TODO(whesse): Currently this fails if the if is turned into a while loop. | 179 // TODO(whesse): Currently this fails if the if is turned into a while loop. |
155 // Fix it so that it can loop and read more than one buffer's worth of data. | 180 // Fix it so that it can loop and read more than one buffer's worth of data. |
156 if (bytes > bytesRead) { | 181 if (bytes > bytesRead) { |
157 _readEncryptedData(); | 182 _readEncryptedData(); |
158 if (buffer.length > 0) { | 183 if (buffer.length > 0) { |
159 int toRead = min(bytes - bytesRead, buffer.length); | 184 int toRead = min(bytes - bytesRead, buffer.length); |
160 data.setRange(offset, toRead, buffer.data, buffer.start); | 185 data.setRange(offset, toRead, buffer.data, buffer.start); |
161 buffer.advanceStart(toRead); | 186 buffer.advanceStart(toRead); |
162 bytesRead += toRead; | 187 bytesRead += toRead; |
163 offset += toRead; | 188 offset += toRead; |
164 } | 189 } |
165 } | 190 } |
166 | 191 |
167 _setHandlersAfterRead(); | 192 _setHandlersAfterRead(); |
168 return bytesRead; | 193 return bytesRead; |
169 } | 194 } |
170 | 195 |
171 // Write the data to the socket, and flush it as much as possible | 196 // Write the data to the socket, and flush it as much as possible |
172 // until it would block. If the write would block, _writeEncryptedData sets | 197 // until it would block. If the write would block, _writeEncryptedData sets |
173 // up handlers to flush the pipeline when possible. | 198 // up handlers to flush the pipeline when possible. |
174 int writeList(List<int> data, int offset, int bytes) { | 199 int writeList(List<int> data, int offset, int bytes) { |
| 200 if (_closedWrite) { |
| 201 throw new SocketException("Writing to a closed socket"); |
| 202 } |
175 var buffer = _tlsFilter.buffers[WRITE_PLAINTEXT]; | 203 var buffer = _tlsFilter.buffers[WRITE_PLAINTEXT]; |
176 if (bytes > buffer.free) { | 204 if (bytes > buffer.free) { |
177 bytes = buffer.free; | 205 bytes = buffer.free; |
178 } | 206 } |
179 if (bytes > 0) { | 207 if (bytes > 0) { |
180 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset); | 208 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset); |
181 buffer.length += bytes; | 209 buffer.length += bytes; |
182 } | 210 } |
183 _writeEncryptedData(); // Tries to flush all pipeline stages. | 211 _writeEncryptedData(); // Tries to flush all pipeline stages. |
184 return bytes; | 212 return bytes; |
185 } | 213 } |
186 | 214 |
187 void _tlsConnectHandler() { | 215 void _tlsConnectHandler() { |
188 _connectPending = true; | 216 _connectPending = true; |
189 _tlsFilter.connect(_host, _port, _is_server, _certificateName); | 217 _tlsFilter.connect(_host, _port, _is_server, _certificateName); |
190 _status = HANDSHAKE; | 218 _status = HANDSHAKE; |
191 _tlsHandshake(); | 219 _tlsHandshake(); |
192 } | 220 } |
193 | 221 |
194 void _tlsWriteHandler() { | 222 void _tlsWriteHandler() { |
| 223 _writeEncryptedData(); |
| 224 if (_filterWriteEmpty && _closedWrite && !_socketClosedWrite) { |
| 225 _socket.close(true); |
| 226 _sockedClosedWrite = true; |
| 227 } |
195 if (_status == HANDSHAKE) { | 228 if (_status == HANDSHAKE) { |
196 _tlsHandshake(); | 229 _tlsHandshake(); |
197 } else if (_status == CONNECTED) { | 230 } else if (_status == CONNECTED && |
198 if (_socketWriteHandler != null) { | 231 _socketWriteHandler != null && |
199 _socketWriteHandler(); | 232 _tlsFilter.buffers[WRITE_PLAINTEXT].free > 0) { |
200 } | 233 // We must be able to set onWrite from the onWrite callback. |
| 234 var handler = _socketWriteHandler; |
| 235 // Reset the one-shot handler. |
| 236 _socketWriteHandler = null; |
| 237 handler(); |
201 } | 238 } |
202 } | 239 } |
203 | 240 |
204 void _tlsDataHandler() { | 241 void _tlsDataHandler() { |
205 if (_status == HANDSHAKE) { | 242 if (_status == HANDSHAKE) { |
206 _tlsHandshake(); | 243 _tlsHandshake(); |
207 } else { | 244 } else { |
208 _writeEncryptedData(); // TODO(whesse): Removing this causes a failure. | 245 _writeEncryptedData(); // TODO(whesse): Removing this causes a failure. |
209 _readEncryptedData(); | 246 _readEncryptedData(); |
210 var buffer = _tlsFilter.buffers[READ_PLAINTEXT]; | 247 if (!_filterReadEmpty) { |
211 if (_filterEmpty) { | 248 // Call the onData event. |
212 if (_fireCloseEventPending) { | |
213 _fireCloseEvent(); | |
214 } | |
215 } else { // Filter is not empty. | |
216 if (scheduledDataEvent != null) { | 249 if (scheduledDataEvent != null) { |
217 scheduledDataEvent.cancel(); | 250 scheduledDataEvent.cancel(); |
218 scheduledDataEvent = null; | 251 scheduledDataEvent = null; |
219 } | 252 } |
220 if (_socketDataHandler != null) { | 253 if (_socketDataHandler != null) { |
221 _socketDataHandler(); | 254 _socketDataHandler(); |
222 } | 255 } |
223 } | 256 } |
224 } | 257 } |
225 } | 258 } |
226 | 259 |
227 void _tlsCloseHandler() { | 260 void _tlsCloseHandler() { |
228 _socketClosed = true; | 261 _socketClosedRead = true; |
229 _status = CLOSED; | 262 if (_filterReadEmpty) { |
230 _socket.close(); | 263 _closedRead = true; |
231 if (_filterEmpty) { | |
232 _fireCloseEvent(); | 264 _fireCloseEvent(); |
233 } else { | 265 if (_socketClosedWrite) { |
234 _fireCloseEventPending = true; | 266 _tlsFilter.destroy(); |
| 267 _tlsFilter = null; |
| 268 _status = CLOSED; |
| 269 } |
235 } | 270 } |
236 } | 271 } |
237 | 272 |
238 void _tlsHandshake() { | 273 void _tlsHandshake() { |
239 _readEncryptedData(); | 274 _readEncryptedData(); |
240 _tlsFilter.handshake(); | 275 _tlsFilter.handshake(); |
241 _writeEncryptedData(); | 276 _writeEncryptedData(); |
242 if (_tlsFilter.buffers[WRITE_ENCRYPTED].length > 0) { | 277 if (_tlsFilter.buffers[WRITE_ENCRYPTED].length > 0) { |
243 _socket.onWrite = _tlsWriteHandler; | 278 _socket.onWrite = _tlsWriteHandler; |
244 } | 279 } |
245 } | 280 } |
246 | 281 |
247 void _tlsHandshakeCompleteHandler() { | 282 void _tlsHandshakeCompleteHandler() { |
248 _status = CONNECTED; | 283 _status = CONNECTED; |
249 if (_connectPending && _socketConnectHandler != null) { | 284 if (_connectPending && _socketConnectHandler != null) { |
250 _connectPending = false; | 285 _connectPending = false; |
251 _socketConnectHandler(); | 286 _socketConnectHandler(); |
252 } | 287 } |
253 } | 288 } |
254 | 289 |
255 void _fireCloseEvent() { | 290 void _fireCloseEvent() { |
256 _fireCloseEventPending = false; | |
257 _tlsFilter.destroy(); | |
258 _tlsFilter = null; | |
259 if (scheduledDataEvent != null) { | 291 if (scheduledDataEvent != null) { |
260 scheduledDataEvent.cancel(); | 292 scheduledDataEvent.cancel(); |
261 } | 293 } |
262 if (_socketCloseHandler != null) { | 294 if (_socketCloseHandler != null) { |
263 _socketCloseHandler(); | 295 _socketCloseHandler(); |
264 } | 296 } |
265 } | 297 } |
266 | 298 |
267 void _readEncryptedData() { | 299 void _readEncryptedData() { |
268 // Read from the socket, and push it through the filter as far as | 300 // Read from the socket, and push it through the filter as far as |
269 // possible. | 301 // possible. |
270 var encrypted = _tlsFilter.buffers[READ_ENCRYPTED]; | 302 var encrypted = _tlsFilter.buffers[READ_ENCRYPTED]; |
271 var plaintext = _tlsFilter.buffers[READ_PLAINTEXT]; | 303 var plaintext = _tlsFilter.buffers[READ_PLAINTEXT]; |
272 bool progress = true; | 304 bool progress = true; |
273 while (progress) { | 305 while (progress) { |
274 progress = false; | 306 progress = false; |
275 // Do not try to read plaintext from the filter while handshaking. | 307 // Do not try to read plaintext from the filter while handshaking. |
276 if ((_status == CONNECTED || _status == CLOSED) && plaintext.free > 0) { | 308 if ((_status == CONNECTED) && plaintext.free > 0) { |
277 int bytes = _tlsFilter.processBuffer(READ_PLAINTEXT); | 309 int bytes = _tlsFilter.processBuffer(READ_PLAINTEXT); |
278 if (bytes > 0) { | 310 if (bytes > 0) { |
279 plaintext.length += bytes; | 311 plaintext.length += bytes; |
280 progress = true; | 312 progress = true; |
281 } | 313 } |
282 } | 314 } |
283 if (encrypted.length > 0) { | 315 if (encrypted.length > 0) { |
284 int bytes = _tlsFilter.processBuffer(READ_ENCRYPTED); | 316 int bytes = _tlsFilter.processBuffer(READ_ENCRYPTED); |
285 if (bytes > 0) { | 317 if (bytes > 0) { |
286 encrypted.advanceStart(bytes); | 318 encrypted.advanceStart(bytes); |
287 progress = true; | 319 progress = true; |
288 } | 320 } |
289 } | 321 } |
290 if (!_socketClosed) { | 322 if (!_socketClosedRead) { |
291 int bytes = _socket.readList(encrypted.data, | 323 int bytes = _socket.readList(encrypted.data, |
292 encrypted.start + encrypted.length, | 324 encrypted.start + encrypted.length, |
293 encrypted.free); | 325 encrypted.free); |
294 if (bytes > 0) { | 326 if (bytes > 0) { |
295 encrypted.length += bytes; | 327 encrypted.length += bytes; |
296 progress = true; | 328 progress = true; |
297 } | 329 } |
298 } | 330 } |
299 } | 331 } |
300 // TODO(whesse): This can be incorrect if there is a partial | 332 // If there is any data in any stages of the filter, there should |
301 // encrypted block stuck in the tlsFilter, and no other data. | 333 // be data in the plaintext buffer after this process. |
302 // Fix this - we do need to know when the filter is empty. | 334 // TODO(whesse): Verify that this is true, and there can be no |
303 _filterEmpty = (plaintext.length == 0); | 335 // partial encrypted block stuck in the tlsFilter. |
| 336 _filterReadEmpty = (plaintext.length == 0); |
304 } | 337 } |
305 | 338 |
306 void _writeEncryptedData() { | 339 void _writeEncryptedData() { |
307 // Write from the filter to the socket. | 340 if (_socketClosedWrite) return; |
308 var buffer = _tlsFilter.buffers[WRITE_ENCRYPTED]; | 341 var encrypted = _tlsFilter.buffers[WRITE_ENCRYPTED]; |
| 342 var plaintext = _tlsFilter.buffers[WRITE_PLAINTEXT]; |
309 while (true) { | 343 while (true) { |
310 if (buffer.length > 0) { | 344 if (encrypted.length > 0) { |
311 int bytes = _socket.writeList(buffer.data, buffer.start, buffer.length); | 345 // Write from the filter to the socket. |
| 346 int bytes = _socket.writeList(encrypted.data, |
| 347 encrypted.start, |
| 348 encrypted.length); |
312 if (bytes == 0) { | 349 if (bytes == 0) { |
313 // The socket has blocked while we have data to write. | 350 // The socket has blocked while we have data to write. |
314 // We must be notified when it becomes unblocked. | 351 // We must be notified when it becomes unblocked. |
315 _socket.onWrite = _tlsWriteHandler; | 352 _socket.onWrite = _tlsWriteHandler; |
| 353 _filterWriteEmpty = false; |
316 break; | 354 break; |
317 } | 355 } |
318 buffer.advanceStart(bytes); | 356 encrypted.advanceStart(bytes); |
319 } else { | 357 } else { |
320 var plaintext = _tlsFilter.buffers[WRITE_PLAINTEXT]; | 358 var plaintext = _tlsFilter.buffers[WRITE_PLAINTEXT]; |
321 if (plaintext.length > 0) { | 359 if (plaintext.length > 0) { |
322 int plaintext_bytes = _tlsFilter.processBuffer(WRITE_PLAINTEXT); | 360 int plaintext_bytes = _tlsFilter.processBuffer(WRITE_PLAINTEXT); |
323 plaintext.advanceStart(plaintext_bytes); | 361 plaintext.advanceStart(plaintext_bytes); |
324 } | 362 } |
325 int bytes = _tlsFilter.processBuffer(WRITE_ENCRYPTED); | 363 int bytes = _tlsFilter.processBuffer(WRITE_ENCRYPTED); |
326 if (bytes <= 0) break; | 364 if (bytes <= 0) { |
327 buffer.length += bytes; | 365 // We know the WRITE_ENCRYPTED buffer is empty, and the |
| 366 // filter wrote zero bytes to it, so the filter must be empty. |
| 367 // Also, the WRITE_PLAINTEXT buffer must have been empty, or |
| 368 // it would have written to the filter. |
| 369 // TODO(whesse): Verify that the filter works this way. |
| 370 _filterWriteEmpty = true; |
| 371 break; |
| 372 } |
| 373 encrypted.length += bytes; |
328 } | 374 } |
329 } | 375 } |
330 } | 376 } |
331 | 377 |
332 /* After a read, the onData handler is enabled to fire again. | 378 /* After a read, the onData handler is enabled to fire again. |
333 * We may also have a close event waiting for the TlsFilter to empty. | 379 * We may also have a close event waiting for the TlsFilter to empty. |
334 */ | 380 */ |
335 void _setHandlersAfterRead() { | 381 void _setHandlersAfterRead() { |
336 // If the filter is empty, then we are guaranteed an event when it | 382 // If the filter is empty, then we are guaranteed an event when it |
337 // becomes unblocked. | 383 // becomes unblocked. Cancel any _tlsDataHandler call. |
338 // Otherwise, schedule a _tlsDataHandler call since there may data | 384 // Otherwise, schedule a _tlsDataHandler call since there may data |
339 // available, and this read call enables the data event. | 385 // available, and this read call enables the data event. |
340 if (!_filterEmpty && scheduledDataEvent == null) { | 386 if (_filterReadEmpty) { |
341 scheduledDataEvent = new Timer(0, (_) => _tlsDataHandler()); | 387 if (scheduledDataEvent != null) { |
342 } else if (_filterEmpty && scheduledDataEvent != null) { | |
343 scheduledDataEvent.cancel(); | 388 scheduledDataEvent.cancel(); |
344 scheduledDataEvent = null; | 389 scheduledDataEvent = null; |
| 390 } |
| 391 } else if (scheduledDataEvent == null) { |
| 392 scheduledDataEvent = new Timer(0, (_) => _tlsDataHandler()); |
345 } | 393 } |
346 if (_filterEmpty && _fireCloseEventPending) { | 394 |
347 _fireCloseEvent(); | 395 if (_socketClosedRead) { // An onClose event is pending. |
| 396 // _closedRead is false, since we are in a read or readList call. |
| 397 if (!_filterReadEmpty) { |
| 398 // _filterReadEmpty may be out of date since read and readList empty |
| 399 // the plaintext buffer after calling _readEncryptedData. |
| 400 // TODO(whesse): Fix this as part of fixing read and readList. |
| 401 _readEncryptedData(); |
| 402 } |
| 403 if (_filterReadEmpty) { |
| 404 // This can't be an else clause: the value of _filterReadEmpty changes. |
| 405 // This must be asynchronous, because we are in a read or readList call. |
| 406 new Timer(0, (_) => _fireCloseEvent()); |
| 407 } |
348 } | 408 } |
349 } | 409 } |
350 | 410 |
351 // _TlsSocket cannot extend _Socket and use _Socket's factory constructor. | 411 // _TlsSocket cannot extend _Socket and use _Socket's factory constructor. |
352 Socket _socket; | 412 Socket _socket; |
353 String _host; | 413 String _host; |
354 int _port; | 414 int _port; |
355 bool _is_server; | 415 bool _is_server; |
356 String _certificateName; | 416 String _certificateName; |
357 | 417 |
358 var _status = NOT_CONNECTED; | 418 var _status = NOT_CONNECTED; |
359 bool _socketClosed = false; | 419 bool _socketClosedRead = false; // The network socket is closed for reading. |
360 bool _filterEmpty = false; | 420 bool _socketClosedWrite = false; // The network socket is closed for writing. |
| 421 bool _closedRead = false; // The secure socket has fired an onClosed event. |
| 422 bool _closedWrite = false; // The secure socket has been closed for writing. |
| 423 bool _filterReadEmpty = true; // There is no buffered data to read. |
| 424 bool _filterWriteEmpty = true; // There is no buffered data to be written. |
361 bool _connectPending = false; | 425 bool _connectPending = false; |
362 bool _fireCloseEventPending = false; | |
363 Function _socketConnectHandler; | 426 Function _socketConnectHandler; |
364 Function _socketWriteHandler; | 427 Function _socketWriteHandler; |
365 Function _socketDataHandler; | 428 Function _socketDataHandler; |
366 Function _socketCloseHandler; | 429 Function _socketCloseHandler; |
367 Timer scheduledDataEvent; | 430 Timer scheduledDataEvent; |
368 | 431 |
369 _TlsFilter _tlsFilter; | 432 _TlsFilter _tlsFilter; |
370 } | 433 } |
371 | 434 |
372 | 435 |
(...skipping 26 matching lines...) Expand all Loading... |
399 bool is_server, | 462 bool is_server, |
400 String certificateName); | 463 String certificateName); |
401 void destroy(); | 464 void destroy(); |
402 void handshake(); | 465 void handshake(); |
403 void init(); | 466 void init(); |
404 int processBuffer(int bufferIndex); | 467 int processBuffer(int bufferIndex); |
405 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); | 468 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); |
406 | 469 |
407 List<_TlsExternalBuffer> get buffers; | 470 List<_TlsExternalBuffer> get buffers; |
408 } | 471 } |
OLD | NEW |