OLD | NEW |
---|---|
(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 class _TlsSocket implements TlsSocket { | |
6 static final int _BUFFER_SIZE = 2048; | |
7 | |
8 // Status states | |
9 static final int NOT_CONNECTED = 200; | |
10 static final int HANDSHAKE = 201; | |
11 static final int CONNECTED = 202; | |
12 static final int CLOSED = 203; | |
13 | |
14 // Buffer identifiers. | |
15 static final int kReadPlaintext = 0; | |
16 static final int kWritePlaintext = 1; | |
17 static final int kReadEncrypted = 2; | |
18 static final int kWriteEncrypted = 3; | |
19 static final int kNumBuffers = 4; | |
20 | |
21 // Constructs a new secure client socket. | |
22 _TlsSocket(String host, | |
23 int port) | |
24 : _socket = new Socket(host, port), | |
25 _tlsFilter = new _TlsFilter() { | |
26 _socket.onConnect = _tlsConnectHandler; | |
27 _socket.onWrite = _tlsWriteHandler; | |
28 _socket.onData = _tlsDataHandler; | |
29 _socket.onClosed = _tlsCloseHandler; | |
30 _tlsFilter.init(); | |
31 _tlsFilter.registerHandshakeCallbacks(_tlsHandshakeStartHandler, | |
32 _tlsHandshakeFinishHandler); | |
33 } | |
34 | |
35 static void setCertificateDatabase(String pkcertDirectory) | |
36 native "TlsSocket_SetCertificateDatabase"; | |
37 | |
38 void set onConnect(void callback()) { | |
39 _socketConnectHandler = callback; | |
40 } | |
41 | |
42 void set onWrite(void callback()) { | |
43 _socketWriteHandler = callback; | |
44 // Reset the one-shot onWrite handler. | |
45 _socket.onWrite = _tlsWriteHandler; | |
46 } | |
47 | |
48 void set onData(void callback()) { | |
49 _socketDataHandler = callback; | |
50 } | |
51 | |
52 void set onClosed(void callback()) { | |
53 _socketCloseHandler = callback; | |
54 } | |
55 | |
56 void _tlsConnectHandler() { | |
57 _connectPending = true; | |
58 _tlsFilter.connect(); | |
Søren Gjesse
2012/11/01 11:49:11
Please explain why you call _tlsFilter.connect() b
Bill Hesse
2012/11/11 22:34:34
_tlsFilter.connect() was both the initial connecti
| |
59 } | |
60 | |
61 void _tlsWriteHandler() { | |
62 if (_status == HANDSHAKE) { | |
63 _writeEncryptedData(); | |
64 _readEncryptedData(); | |
65 _tlsFilter.connect(); | |
66 // Only do this if we have more data to write. | |
67 if (_tlsFilter.buffers[kWriteEncrypted].length > 0) { | |
68 _socket.onWrite = _tlsWriteHandler; | |
69 } | |
70 } else if (_status == CONNECTED) { | |
71 if (_socketWriteHandler != null) { | |
72 _socketWriteHandler(); | |
73 } | |
74 } | |
75 } | |
76 | |
77 void _tlsDataHandler() { | |
78 if (_status == HANDSHAKE) { | |
79 _readEncryptedData(); | |
80 _writeEncryptedData(); | |
81 _tlsFilter.connect(); | |
82 _socket.onWrite = _tlsWriteHandler; | |
83 } else { | |
84 if (scheduledDataEvent != null) { | |
85 scheduledDataEvent.cancel(); | |
86 scheduledDataEvent = null; | |
87 } | |
88 if (_socketDataHandler != null) { | |
89 _readEncryptedData(); | |
90 _socketDataHandler(); | |
91 } | |
92 } | |
93 } | |
94 | |
95 void _tlsCloseHandler() { | |
96 _socketClosed = true; | |
97 _status = CLOSED; | |
98 _socket.close(); | |
99 if (_filterEmpty) { | |
100 _fireCloseEvent(); | |
101 } else { | |
102 _fireCloseEventPending = true; | |
103 } | |
104 } | |
105 | |
106 void _tlsHandshakeStartHandler() { | |
107 _status = HANDSHAKE; | |
108 _socket.onWrite = _tlsWriteHandler; | |
109 } | |
110 | |
111 void _tlsHandshakeFinishHandler() { | |
Søren Gjesse
2012/11/01 11:49:11
In this case the handshake will always have succee
Bill Hesse
2012/11/11 22:34:34
Yes. Terminating the connection on a failed hands
| |
112 _status = CONNECTED; | |
113 if (_connectPending && _socketConnectHandler != null) { | |
114 _connectPending = false; | |
115 _socketConnectHandler(); | |
116 } | |
117 } | |
118 | |
119 void _fireCloseEvent() { | |
120 _fireCloseEventPending = false; | |
121 _tlsFilter.destroy(); | |
122 _tlsFilter = null; | |
123 if (scheduledDataEvent != null) { | |
124 scheduledDataEvent.cancel(); | |
125 } | |
126 if (_socketCloseHandler != null) { | |
127 _socketCloseHandler(); | |
128 } | |
129 } | |
130 | |
131 void close([bool halfClose]) { | |
132 _socket.close(halfClose); | |
133 } | |
134 | |
135 int readList(List<int> data, int offset, int bytes) { | |
136 print("Entering readList"); | |
137 _readEncryptedData(); | |
Søren Gjesse
2012/11/01 11:49:11
Do you need to call _readEncryptedData here? Shoul
Bill Hesse
2012/11/11 22:34:34
We need to call _readEncryptedData here because it
| |
138 if (offset < 0 || bytes < 0 || offset + bytes > data.length) { | |
139 throw new IllegalArgumentException( | |
140 "Invalid offset or bytes in TlsSocket.readList"); | |
141 } | |
142 int bytesRead = 0; | |
143 var buffer = _tlsFilter.buffers[kReadPlaintext]; | |
144 if (buffer.length == 0 && buffer.start != 0) { | |
145 throw "Unexpected buffer state in TlsSocket.readList"; | |
146 } | |
147 if (buffer.length > 0) { | |
148 int toRead = min(bytes, buffer.length); | |
149 data.setRange(offset, toRead, buffer.data, buffer.start); | |
150 buffer.advanceStart(toRead); | |
151 bytesRead += toRead; | |
152 } | |
153 print("Before processBuffer(readPlaintext)"); | |
Søren Gjesse
2012/11/01 11:49:11
Debug print more below.
Bill Hesse
2012/11/11 22:34:34
Removed.
On 2012/11/01 11:49:11, Søren Gjesse wrot
| |
154 int newBytes = _tlsFilter.processBuffer(kReadPlaintext); | |
155 if (newBytes > 0) { | |
156 buffer.length += newBytes; | |
157 } | |
158 if (bytes - bytesRead > 0 && buffer.length > 0) { | |
159 int toRead = min(bytes - bytesRead, buffer.length); | |
160 data.setRange(offset + bytesRead, toRead, buffer.data, buffer.start); | |
161 buffer.advanceStart(toRead); | |
162 bytesRead += toRead; | |
163 } | |
164 | |
165 // If bytesRead is 0, then something is blocked or empty, and | |
166 // we are guaranteed an event when it becomes unblocked. | |
167 // Otherwise, give an event if there is data available, and | |
168 // there has been a read call since the last data event. | |
169 // This gives the invariant that: | |
170 // If there is data available, and there has been a read after the | |
171 // last data event (or no previous one fired), then we are guaranteed | |
172 // to get a data event. | |
173 _filterEmpty = (bytesRead == 0); | |
174 if (bytesRead > 0 && scheduledDataEvent == null) { | |
175 scheduledDataEvent = new Timer(0, (_) => _tlsDataHandler()); | |
176 } else if (bytesRead == 0) { | |
177 if (_fireCloseEventPending) { | |
178 _fireCloseEvent(); | |
179 } else if (scheduledDataEvent != null) { | |
180 scheduledDataEvent.cancel(); | |
181 scheduledDataEvent = null; | |
182 } | |
183 } | |
184 return bytesRead; | |
185 } | |
186 | |
187 | |
188 // Write the data to the socket, and flush it as much as possible | |
189 // without blocking. If not all the data is written, enable the | |
190 // onWrite event. If data is not all flushed, add handlers to all | |
191 // relevant events. | |
192 int writeList(List<int> data, int offset, int bytes) { | |
193 _writeEncryptedData(); // Tries to flush all post-filter stages. | |
194 var buffer = _tlsFilter.buffers[kWritePlaintext]; | |
195 if (bytes > buffer.free) { | |
196 bytes = buffer.free; | |
197 } | |
198 if (bytes > 0) { | |
199 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset); | |
200 buffer.length += bytes; | |
201 } | |
202 int bytesWritten = _tlsFilter.processBuffer(kWritePlaintext); | |
203 buffer.advanceStart(bytesWritten); | |
204 print("bytesWritten $bytesWritten"); | |
205 _readEncryptedData(); | |
206 _writeEncryptedData(); | |
207 return bytes; | |
208 } | |
209 | |
210 void _readEncryptedData() { | |
211 // Read from the socket and write to the filter. | |
212 print("Entering readEncryptedData"); | |
213 var buffer = _tlsFilter.buffers[kReadEncrypted]; | |
214 while (true) { | |
215 print(" buffer.length: ${buffer.length}"); | |
216 if (buffer.length > 0) { | |
217 int bytes = _tlsFilter.processBuffer(kReadEncrypted); | |
218 if (bytes > 0) { | |
219 buffer.advanceStart(bytes); | |
220 } else { | |
221 break; | |
222 } | |
223 } else if (!_socketClosed) { | |
224 int bytes = _socket.readList(buffer.data, | |
225 buffer.start + buffer.length, | |
226 buffer.free); | |
227 print(" bytes read from socket: $bytes"); | |
228 if (bytes <= 0) break; | |
229 buffer.length += bytes; | |
230 } else { | |
231 break; // Socket is closed and read buffer is empty. | |
232 } | |
233 } | |
234 } | |
235 | |
236 void _writeEncryptedData() { | |
237 // Write from the filter to the socket. | |
238 var buffer = _tlsFilter.buffers[kWriteEncrypted]; | |
239 while (true) { | |
240 print("top of while loop: buffer.length = ${buffer.length}"); | |
241 if (buffer.length > 0) { | |
242 int bytes = _socket.writeList(buffer.data, buffer.start, buffer.length); | |
243 if (bytes <= 0) break; | |
244 buffer.advanceStart(bytes); | |
245 } else { | |
246 if (buffer.start != 0 || buffer.length != 0) { | |
247 throw "Unexpected state in _writeEncryptedData"; | |
248 } | |
249 int bytes = _tlsFilter.processBuffer(kWriteEncrypted); | |
250 print("foo writeEncrypted processBuffer returned $bytes"); | |
251 if (bytes <= 0) break; | |
252 buffer.length += bytes; | |
253 } | |
254 } | |
255 } | |
256 | |
257 // _TlsSocket cannot extend _Socket and use _Socket's factory constructor. | |
258 Socket _socket; | |
259 | |
260 var _status = NOT_CONNECTED; | |
261 bool _socketClosed = false; | |
262 bool _filterEmpty = false; | |
263 bool _connectPending = false; | |
264 bool _fireCloseEventPending = false; | |
265 Function _socketConnectHandler; | |
266 Function _socketWriteHandler; | |
267 Function _socketDataHandler; | |
268 Function _socketCloseHandler; | |
269 Timer scheduledDataEvent; | |
270 | |
271 var _tlsFilter; | |
272 } | |
273 | |
274 class _TlsExternalBuffer { | |
275 static final int kSize = 8 * 1024; | |
276 _TlsExternalBuffer() : start = 0, length = 0; | |
277 | |
278 void advanceStart(int numBytes) { | |
279 start += numBytes; | |
280 length -= numBytes; | |
281 if (length == 0) { | |
282 start = 0; | |
283 } | |
284 } | |
285 | |
286 int get free() => kSize - start - length; | |
287 | |
288 List data; // This will be a ExternalByteArray, backed by C allocated data. | |
289 int start; | |
290 int length; | |
291 } | |
292 | |
293 /** | |
294 * _TlsFilter wraps a filter that encrypts and decrypts data travelling | |
295 * over a TLS encrypted socket. The filter also handles the handshaking | |
296 * and certificate verification. | |
297 * | |
298 * The filter exposes its input and output buffers as Dart objects that | |
299 * are backed by an external C array of bytes, so that both Dart code and | |
300 * native code can access the same data. | |
301 */ | |
302 class _TlsFilter extends NativeFieldWrapperClass1 { | |
303 _TlsFilter() { | |
304 buffers = new List<_TlsExternalBuffer>(_TlsSocket.kNumBuffers); | |
305 for (int i = 0; i < _TlsSocket.kNumBuffers; ++i) { | |
306 buffers[i] = new _TlsExternalBuffer(); | |
307 } | |
308 } | |
309 | |
310 void init() native "TlsSocket_Init"; | |
311 | |
312 void connect() native "TlsSocket_Connect"; | |
313 | |
314 void registerHandshakeCallbacks(Function startHandshakeHandler, | |
315 Function finishHandshakeHandler) | |
316 native "TlsSocket_RegisterHandshakeCallbacks"; | |
317 int processBuffer(int bufferIndex) native "TlsSocket_ProcessBuffer"; | |
318 void destroy() native "TlsSocket_Destroy"; | |
319 | |
320 List<_TlsExternalBuffer> buffers; | |
321 } | |
OLD | NEW |