Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(567)

Side by Side Diff: lib/io/tls_socket.dart

Issue 10916081: Add secure sockets to dart:io (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments (done). Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 abstract class TlsSocket implements Socket {
6 /**
7 * Constructs a new secure socket and connect it to the given
8 * host on the given port. The returned socket is not yet connected
9 * but ready for registration of callbacks.
10 */
11 factory TlsSocket(String host, int port) => new _TlsSocket(host, port);
12
13 /**
14 * Initializes the TLS library with the path to a certificate database
15 * containing root certificates for verifying certificate paths on
16 * client connections, and server certificates to provide on server
17 * connections.
18 */
19 external static void setCertificateDatabase(String pkcertDirectory);
20 }
21
22 class _TlsSocket implements TlsSocket {
Anders Johnsen 2012/11/01 07:27:21 A higher level question: Would it make sense to ex
Bill Hesse 2012/11/11 22:34:34 I can't see a way anyone could really use it, beca
23 static final int _BUFFER_SIZE = 2048;
Mads Ager (google) 2012/11/01 10:09:01 The name here is using an '_' in from of it and no
Bill Hesse 2012/11/11 22:34:34 Constant removed. On 2012/11/01 10:09:01, Mads Age
24
25 // Status states
26 static final int NOT_CONNECTED = 200;
27 static final int HANDSHAKE = 201;
28 static final int CONNECTED = 202;
29 static final int CLOSED = 203;
30
31 // Buffer identifiers.
32 static final int kReadPlaintext = 0;
Mads Ager (google) 2012/11/01 10:09:01 This is using a different naming style than the ot
Bill Hesse 2012/11/11 22:34:34 Should we be consistent? Which do you prefer? It
Mads Ager (google) 2012/11/12 11:39:08 Of course we should be consistent! The are all ju
Bob Nystrom 2012/11/14 18:06:36 Style nit. These are technically not constants, th
33 static final int kWritePlaintext = 1;
34 static final int kReadEncrypted = 2;
35 static final int kWriteEncrypted = 3;
36 static final int kNumBuffers = 4;
37
38 // Constructs a new secure client socket.
39 _TlsSocket(String host, int port)
40 : _socket = new Socket(host, port),
41 _tlsFilter = new _TlsFilter() {
42 _socket.onConnect = _tlsConnectHandler;
43 _socket.onWrite = _tlsWriteHandler;
44 _socket.onData = _tlsDataHandler;
45 _socket.onClosed = _tlsCloseHandler;
46 _tlsFilter.init();
47 _tlsFilter.registerHandshakeCallbacks(_tlsHandshakeStartHandler,
48 _tlsHandshakeFinishHandler);
49 }
50
51 void set onConnect(void callback()) {
52 _socketConnectHandler = callback;
53 }
54
55 void set onWrite(void callback()) {
56 _socketWriteHandler = callback;
57 // Reset the one-shot onWrite handler.
58 _socket.onWrite = _tlsWriteHandler;
59 }
60
61 void set onData(void callback()) {
62 _socketDataHandler = callback;
63 }
64
65 void set onClosed(void callback()) {
66 _socketCloseHandler = callback;
67 }
68
69 void _tlsConnectHandler() {
70 _connectPending = true;
71 _tlsFilter.connect();
72 }
73
74 void _tlsWriteHandler() {
75 if (_status == HANDSHAKE) {
76 _writeEncryptedData();
77 _readEncryptedData();
78 _tlsFilter.connect();
79 // Only do this if we have more data to write.
80 if (_tlsFilter.buffers[kWriteEncrypted].length > 0) {
81 _socket.onWrite = _tlsWriteHandler;
82 }
83 } else if (_status == CONNECTED) {
84 if (_socketWriteHandler != null) {
85 _socketWriteHandler();
86 }
87 }
88 }
89
90 void _tlsDataHandler() {
91 if (_status == HANDSHAKE) {
92 _readEncryptedData();
93 _writeEncryptedData();
94 _tlsFilter.connect();
95 _socket.onWrite = _tlsWriteHandler;
96 } else {
97 if (scheduledDataEvent != null) {
98 scheduledDataEvent.cancel();
99 scheduledDataEvent = null;
100 }
101 if (_socketDataHandler != null) {
102 _readEncryptedData();
103 _socketDataHandler();
104 }
105 }
106 }
107
108 void _tlsCloseHandler() {
109 _socketClosed = true;
110 _status = CLOSED;
111 _socket.close();
112 if (_filterEmpty) {
113 _fireCloseEvent();
114 } else {
115 _fireCloseEventPending = true;
116 }
117 }
118
119 void _tlsHandshakeStartHandler() {
120 _status = HANDSHAKE;
121 _socket.onWrite = _tlsWriteHandler;
122 }
123
124 void _tlsHandshakeFinishHandler() {
125 _status = CONNECTED;
126 if (_connectPending && _socketConnectHandler != null) {
127 _connectPending = false;
128 _socketConnectHandler();
129 }
130 }
131
132 void _fireCloseEvent() {
133 _fireCloseEventPending = false;
134 _tlsFilter.destroy();
135 _tlsFilter = null;
136 if (scheduledDataEvent != null) {
137 scheduledDataEvent.cancel();
138 }
139 if (_socketCloseHandler != null) {
140 _socketCloseHandler();
141 }
142 }
143
144 void close([bool halfClose]) {
145 _socket.close(halfClose);
146 }
147
148 int readList(List<int> data, int offset, int bytes) {
149 print("Entering readList");
Mads Ager (google) 2012/11/01 10:09:01 Debug printing.
Bill Hesse 2012/11/11 22:34:34 Done.
150 _readEncryptedData();
151 if (offset < 0 || bytes < 0 || offset + bytes > data.length) {
152 throw new IllegalArgumentException(
153 "Invalid offset or bytes in TlsSocket.readList");
154 }
155 int bytesRead = 0;
156 var buffer = _tlsFilter.buffers[kReadPlaintext];
157 if (buffer.length == 0 && buffer.start != 0) {
158 throw "Unexpected buffer state in TlsSocket.readList";
159 }
160 if (buffer.length > 0) {
161 int toRead = min(bytes, buffer.length);
162 data.setRange(offset, toRead, buffer.data, buffer.start);
163 buffer.advanceStart(toRead);
164 bytesRead += toRead;
165 }
166 print("Before processBuffer(readPlaintext)");
167 int newBytes = _tlsFilter.processBuffer(kReadPlaintext);
168 if (newBytes > 0) {
169 buffer.length += newBytes;
170 }
171 if (bytes - bytesRead > 0 && buffer.length > 0) {
172 int toRead = min(bytes - bytesRead, buffer.length);
173 data.setRange(offset + bytesRead, toRead, buffer.data, buffer.start);
174 buffer.advanceStart(toRead);
175 bytesRead += toRead;
176 }
177
178 // If bytesRead is 0, then something is blocked or empty, and
179 // we are guaranteed an event when it becomes unblocked.
180 // Otherwise, give an event if there is data available, and
181 // there has been a read call since the last data event.
182 // This gives the invariant that:
183 // If there is data available, and there has been a read after the
184 // last data event (or no previous one fired), then we are guaranteed
185 // to get a data event.
186 _filterEmpty = (bytesRead == 0);
187 if (bytesRead > 0 && scheduledDataEvent == null) {
188 scheduledDataEvent = new Timer(0, (_) => _tlsDataHandler());
189 } else if (bytesRead == 0) {
190 if (_fireCloseEventPending) {
191 _fireCloseEvent();
192 } else if (scheduledDataEvent != null) {
193 scheduledDataEvent.cancel();
194 scheduledDataEvent = null;
195 }
196 }
197 return bytesRead;
198 }
199
200
201 // Write the data to the socket, and flush it as much as possible
202 // without blocking. If not all the data is written, enable the
203 // onWrite event. If data is not all flushed, add handlers to all
204 // relevant events.
205 int writeList(List<int> data, int offset, int bytes) {
206 _writeEncryptedData(); // Tries to flush all post-filter stages.
207 var buffer = _tlsFilter.buffers[kWritePlaintext];
208 if (bytes > buffer.free) {
209 bytes = buffer.free;
210 }
211 if (bytes > 0) {
212 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset);
213 buffer.length += bytes;
214 }
215 int bytesWritten = _tlsFilter.processBuffer(kWritePlaintext);
216 buffer.advanceStart(bytesWritten);
217 print("bytesWritten $bytesWritten");
218 _readEncryptedData();
219 _writeEncryptedData();
220 return bytes;
221 }
222
223 void _readEncryptedData() {
224 // Read from the socket and write to the filter.
225 print("Entering readEncryptedData");
Anders Johnsen 2012/11/01 07:27:21 Debug info.
226 var buffer = _tlsFilter.buffers[kReadEncrypted];
227 while (true) {
228 print(" buffer.length: ${buffer.length}");
Anders Johnsen 2012/11/01 07:27:21 Debug info.
229 if (buffer.length > 0) {
230 int bytes = _tlsFilter.processBuffer(kReadEncrypted);
231 if (bytes > 0) {
232 buffer.advanceStart(bytes);
233 } else {
234 break;
235 }
236 } else if (!_socketClosed) {
237 int bytes = _socket.readList(buffer.data,
238 buffer.start + buffer.length,
239 buffer.free);
240 print(" bytes read from socket: $bytes");
Anders Johnsen 2012/11/01 07:27:21 Debug info.
241 if (bytes <= 0) break;
242 buffer.length += bytes;
243 } else {
244 break; // Socket is closed and read buffer is empty.
245 }
246 }
247 }
248
249 void _writeEncryptedData() {
250 // Write from the filter to the socket.
251 var buffer = _tlsFilter.buffers[kWriteEncrypted];
252 while (true) {
253 print("top of while loop: buffer.length = ${buffer.length}");
Anders Johnsen 2012/11/01 07:27:21 Debug info
254 if (buffer.length > 0) {
255 int bytes = _socket.writeList(buffer.data, buffer.start, buffer.length);
256 if (bytes <= 0) break;
257 buffer.advanceStart(bytes);
258 } else {
259 if (buffer.start != 0 || buffer.length != 0) {
260 throw "Unexpected state in _writeEncryptedData";
261 }
262 int bytes = _tlsFilter.processBuffer(kWriteEncrypted);
263 print("foo writeEncrypted processBuffer returned $bytes");
Anders Johnsen 2012/11/01 07:27:21 Debug info.
264 if (bytes <= 0) break;
265 buffer.length += bytes;
266 }
267 }
268 }
269
270 // _TlsSocket cannot extend _Socket and use _Socket's factory constructor.
271 Socket _socket;
272
273 var _status = NOT_CONNECTED;
274 bool _socketClosed = false;
275 bool _filterEmpty = false;
276 bool _connectPending = false;
277 bool _fireCloseEventPending = false;
278 Function _socketConnectHandler;
279 Function _socketWriteHandler;
280 Function _socketDataHandler;
281 Function _socketCloseHandler;
282 Timer scheduledDataEvent;
283
284 var _tlsFilter;
285 }
286
287 class _TlsExternalBuffer {
288 static final int kSize = 8 * 1024;
289 _TlsExternalBuffer() : start = 0, length = 0;
290
291 void advanceStart(int numBytes) {
292 start += numBytes;
293 length -= numBytes;
294 if (length == 0) {
295 start = 0;
296 }
297 }
298
299 int get free() => kSize - start - length;
Anders Johnsen 2012/11/01 07:27:21 remove ().
Anders Johnsen 2012/11/01 07:27:21 Please add (...) to explicit express precedence fo
Bill Hesse 2012/11/11 22:34:34 Done.
300
301 List data; // This will be a ExternalByteArray, backed by C allocated data.
302 int start;
303 int length;
304 }
305
306 /**
307 * _TlsFilter wraps a filter that encrypts and decrypts data travelling
308 * over a TLS encrypted socket. The filter also handles the handshaking
309 * and certificate verification.
310 *
311 * The filter exposes its input and output buffers as Dart objects that
312 * are backed by an external C array of bytes, so that both Dart code and
313 * native code can access the same data.
314 */
315 class _TlsFilter extends NativeFieldWrapperClass1 {
Anders Johnsen 2012/11/01 07:27:21 AFAIK, this is VM specific, I think this should be
Bill Hesse 2012/11/11 22:34:34 Done. We have created an abstract interface TlsFi
316 _TlsFilter() {
317 buffers = new List<_TlsExternalBuffer>(_TlsSocket.kNumBuffers);
318 for (int i = 0; i < _TlsSocket.kNumBuffers; ++i) {
319 buffers[i] = new _TlsExternalBuffer();
320 }
321 }
322
323 external void init();
324
325 external void connect();
326
327 external void registerHandshakeCallbacks(Function startHandshakeHandler,
328 Function finishHandshakeHandler);
329 external int processBuffer(int bufferIndex);
330 external void destroy();
331
332 List<_TlsExternalBuffer> buffers;
333 }
OLDNEW
« no previous file with comments | « lib/io/iolib_sources.gypi ('k') | runtime/bin/bin.gypi » ('j') | runtime/bin/tls_socket.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698