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

Side by Side Diff: sdk/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: Create interface TlsFilter, patched with implementation _TlsFilter. 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
« no previous file with comments | « sdk/lib/io/iolib_sources.gypi ('k') | tests/standalone/io/tls_socket_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
23 class _TlsSocket implements TlsSocket {
24 // Status states
25 static final int NOT_CONNECTED = 200;
Bill Hesse 2012/11/11 22:34:34 The form of these constants seems right to me, but
Mads Ager (google) 2012/11/12 11:39:08 We should use this format for now. That is what is
26 static final int HANDSHAKE = 201;
27 static final int CONNECTED = 202;
28 static final int CLOSED = 203;
29
30 // Buffer identifiers.
31 static final int kReadPlaintext = 0;
32 static final int kWritePlaintext = 1;
33 static final int kReadEncrypted = 2;
34 static final int kWriteEncrypted = 3;
35 static final int kNumBuffers = 4;
36
37 // Constructs a new secure client socket.
38 _TlsSocket(String host, int port)
39 : _host = host,
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(_host);
72 _tlsFilter.handshake();
73 _socket.onWrite = _tlsWriteHandler;
74 }
75
76 void _tlsWriteHandler() {
77 if (_status == HANDSHAKE) {
78 _tlsHandshake();
79 } else if (_status == CONNECTED) {
80 if (_socketWriteHandler != null) {
81 _socketWriteHandler();
82 }
83 }
84 }
85
86 void _tlsDataHandler() {
87 if (_status == HANDSHAKE) {
88 _tlsHandshake();
89 } else {
90 if (scheduledDataEvent != null) {
91 scheduledDataEvent.cancel();
92 scheduledDataEvent = null;
93 }
94 if (_socketDataHandler != null) {
95 _readEncryptedData();
96 _socketDataHandler();
97 }
98 }
99 }
100
101 void _tlsCloseHandler() {
102 _socketClosed = true;
103 _status = CLOSED;
104 _socket.close();
105 if (_filterEmpty) {
106 _fireCloseEvent();
107 } else {
108 _fireCloseEventPending = true;
109 }
110 }
111
112 void _tlsHandshake() {
113 _writeEncryptedData();
114 _readEncryptedData();
115 _tlsFilter.handshake();
116 // TODO(whesse): Set the write handler only when there is filter data
117 // to be written to the socket. Currently Windows stalls if we do this.
118 // The condition should be_tlsFilter.buffers[kWriteEncrypted].length > 0.
119 _socket.onWrite = _tlsWriteHandler;
120 }
121
122 void _tlsHandshakeStartHandler() {
123 _status = HANDSHAKE;
124 _socket.onWrite = _tlsWriteHandler;
125 }
126
127 void _tlsHandshakeFinishHandler() {
128 _status = CONNECTED;
129 if (_connectPending && _socketConnectHandler != null) {
130 _connectPending = false;
131 _socketConnectHandler();
132 }
133 }
134
135 void _fireCloseEvent() {
136 _fireCloseEventPending = false;
137 _tlsFilter.destroy();
138 _tlsFilter = null;
139 if (scheduledDataEvent != null) {
140 scheduledDataEvent.cancel();
141 }
142 if (_socketCloseHandler != null) {
143 _socketCloseHandler();
144 }
145 }
146
147 void close([bool halfClose]) {
148 _socket.close(halfClose);
149 }
150
151 int readList(List<int> data, int offset, int bytes) {
152 _readEncryptedData();
153 if (offset < 0 || bytes < 0 || offset + bytes > data.length) {
154 throw new IllegalArgumentException(
155 "Invalid offset or bytes in TlsSocket.readList");
156 }
157 int bytesRead = 0;
158 var buffer = _tlsFilter.buffers[kReadPlaintext];
159 if (buffer.length > 0) {
160 int toRead = min(bytes, buffer.length);
161 data.setRange(offset, toRead, buffer.data, buffer.start);
162 buffer.advanceStart(toRead);
163 bytesRead += toRead;
164 }
165 int newBytes = _tlsFilter.processBuffer(kReadPlaintext);
166 if (newBytes > 0) {
167 buffer.length += newBytes;
168 }
169 if (bytes - bytesRead > 0 && buffer.length > 0) {
170 int toRead = min(bytes - bytesRead, buffer.length);
171 data.setRange(offset + bytesRead, toRead, buffer.data, buffer.start);
172 buffer.advanceStart(toRead);
173 bytesRead += toRead;
174 }
175
176 // If bytesRead is 0, then something is blocked or empty, and
177 // we are guaranteed an event when it becomes unblocked.
178 // Otherwise, give an event if there is data available, and
179 // there has been a read call since the last data event.
180 // This gives the invariant that:
181 // If there is data available, and there has been a read after the
182 // last data event (or no previous one fired), then we are guaranteed
183 // to get a data event.
184 _filterEmpty = (bytesRead == 0);
185 if (bytesRead > 0 && scheduledDataEvent == null) {
186 scheduledDataEvent = new Timer(0, (_) => _tlsDataHandler());
187 } else if (bytesRead == 0) {
188 if (_fireCloseEventPending) {
189 _fireCloseEvent();
190 } else if (scheduledDataEvent != null) {
191 scheduledDataEvent.cancel();
192 scheduledDataEvent = null;
193 }
194 }
195 return bytesRead;
196 }
197
198
199 // Write the data to the socket, and flush it as much as possible
200 // without blocking. If not all the data is written, enable the
201 // onWrite event. If data is not all flushed, add handlers to all
202 // relevant events.
203 int writeList(List<int> data, int offset, int bytes) {
204 _writeEncryptedData(); // Tries to flush all post-filter stages.
205 var buffer = _tlsFilter.buffers[kWritePlaintext];
206 if (bytes > buffer.free) {
207 bytes = buffer.free;
208 }
209 if (bytes > 0) {
210 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset);
211 buffer.length += bytes;
212 }
213 int bytesWritten = _tlsFilter.processBuffer(kWritePlaintext);
214 buffer.advanceStart(bytesWritten);
215 _readEncryptedData();
216 _writeEncryptedData();
217 return bytes;
218 }
219
220 void _readEncryptedData() {
221 // Read from the socket and write to the filter.
222 var buffer = _tlsFilter.buffers[kReadEncrypted];
223 while (true) {
224 if (buffer.length > 0) {
225 int bytes = _tlsFilter.processBuffer(kReadEncrypted);
226 if (bytes > 0) {
227 buffer.advanceStart(bytes);
228 } else {
229 break;
230 }
231 } else if (!_socketClosed) {
232 int bytes = _socket.readList(buffer.data,
233 buffer.start + buffer.length,
234 buffer.free);
235 if (bytes <= 0) break;
236 buffer.length += bytes;
237 } else {
238 break; // Socket is closed and read buffer is empty.
239 }
240 }
241 }
242
243 void _writeEncryptedData() {
244 // Write from the filter to the socket.
245 var buffer = _tlsFilter.buffers[kWriteEncrypted];
246 while (true) {
247 if (buffer.length > 0) {
248 int bytes = _socket.writeList(buffer.data, buffer.start, buffer.length);
249 if (bytes <= 0) break;
250 buffer.advanceStart(bytes);
251 } else {
252 int bytes = _tlsFilter.processBuffer(kWriteEncrypted);
253 if (bytes <= 0) break;
254 buffer.length += bytes;
255 }
256 }
257 }
258
259 // _TlsSocket cannot extend _Socket and use _Socket's factory constructor.
260 Socket _socket;
261 String _host;
262
263 var _status = NOT_CONNECTED;
264 bool _socketClosed = false;
265 bool _filterEmpty = false;
266 bool _connectPending = false;
267 bool _fireCloseEventPending = false;
268 Function _socketConnectHandler;
269 Function _socketWriteHandler;
270 Function _socketDataHandler;
271 Function _socketCloseHandler;
272 Timer scheduledDataEvent;
273
274 // TODO(whesse): Specify an abstract class for TlsFilter
275 TlsFilter _tlsFilter;
276 }
277
278
279 class _TlsExternalBuffer {
280 static final int kSize = 8 * 1024;
281 _TlsExternalBuffer() : start = 0, length = 0;
282
283 void advanceStart(int numBytes) {
284 start += numBytes;
285 length -= numBytes;
286 if (length == 0) {
287 start = 0;
288 }
289 }
290
291 int get free => kSize - (start + length);
292
293 List data; // This will be a ExternalByteArray, backed by C allocated data.
294 int start;
295 int length;
296 }
297
298
299 abstract class TlsFilter {
300 external factory TlsFilter();
301
302 void connect(String hostName);
303 void destroy();
304 void handshake();
305 void init();
306 int processBuffer(int bufferIndex);
307 void registerHandshakeCallbacks(Function startHandshakeHandler,
308 Function finishHandshakeHandler);
309 }
OLDNEW
« no previous file with comments | « sdk/lib/io/iolib_sources.gypi ('k') | tests/standalone/io/tls_socket_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698