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 abstract class TlsSocket implements Socket { | |
Mads Ager (google)
2012/11/12 11:39:08
Could you add a class comment for DartDoc please?
Bill Hesse
2012/11/13 20:11:08
Done.
| |
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; | |
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; | |
Mads Ager (google)
2012/11/12 11:39:08
Style update. These needs to be in sync with some
Bill Hesse
2012/11/13 20:11:08
Done.
| |
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; | |
Mads Ager (google)
2012/11/12 11:39:08
Can't we simplify this. It seems to me that the _t
Bill Hesse
2012/11/13 20:11:08
We actually don't want to set it in _tlsWriteHandl
| |
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); | |
Søren Gjesse
2012/11/12 12:04:57
How about the port - is that not needed here?
Bill Hesse
2012/11/13 20:11:08
Of course it is. Added.
On 2012/11/12 12:04:57,
| |
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(); | |
Mads Ager (google)
2012/11/12 11:39:08
Does this mean that you are reading out the data b
Søren Gjesse
2012/11/12 12:04:57
Does encrypted data on the socket always mean that
Bill Hesse
2012/11/13 20:11:08
We need to read data before the user-visible data
| |
96 _socketDataHandler(); | |
97 } | |
98 } | |
99 } | |
100 | |
101 void _tlsCloseHandler() { | |
102 _socketClosed = true; | |
103 _status = CLOSED; | |
104 _socket.close(); | |
Søren Gjesse
2012/11/12 12:04:57
I don't think we should close the socket here. If
Bill Hesse
2012/11/13 20:11:08
Sounds right. We will need to test that this work
| |
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) { | |
Mads Ager (google)
2012/11/12 11:39:08
We are missing the read method on sockets that Sor
Bill Hesse
2012/11/13 20:11:08
Added, along with all other methods on the socket
| |
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 | |
Mads Ager (google)
2012/11/12 11:39:08
This comment mentions more than the method is doin
Bill Hesse
2012/11/13 20:11:08
Yes, and it is an error that this does not do all
| |
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(); | |
Søren Gjesse
2012/11/12 12:04:57
Please provide a comment on why you are also calli
Bill Hesse
2012/11/13 20:11:08
Removed.
On 2012/11/12 12:04:57, Søren Gjesse wro
| |
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 | |
Mads Ager (google)
2012/11/12 11:39:08
This TODO is now done?
Bill Hesse
2012/11/13 20:11:08
Done.
| |
275 TlsFilter _tlsFilter; | |
276 } | |
277 | |
278 | |
279 class _TlsExternalBuffer { | |
280 static final int kSize = 8 * 1024; | |
Mads Ager (google)
2012/11/12 11:39:08
Change to Dart constant style.
Søren Gjesse
2012/11/12 12:04:57
We should consider using a larger buffer size.
Bill Hesse
2012/11/13 20:11:08
Done.
Bill Hesse
2012/11/13 20:11:08
The TLS specification does specify that packet siz
| |
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 { | |
Mads Ager (google)
2012/11/12 11:39:08
This is public and needs doc comments. Alternative
Bill Hesse
2012/11/13 20:11:08
I'm not convinced that we ever want to expose it.
| |
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 } | |
OLD | NEW |