| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| // |
| // VMOptions= |
| // VMOptions=--short_socket_read |
| // VMOptions=--short_socket_write |
| // VMOptions=--short_socket_read --short_socket_write |
| |
| import "dart:async"; |
| import "dart:io"; |
| |
| import "package:async_helper/async_helper.dart"; |
| import "package:expect/expect.dart"; |
| |
| InternetAddress HOST; |
| const CERTIFICATE = "localhost_cert"; |
| |
| // This test sends corrupt data in the middle of a secure network connection. |
| // This tests the error handling of RawSecureSocket. |
| // A RawSocket connection is upgraded to a secure connection, so that we |
| // have a reference to the underlying socket. Then we send some |
| // unencrypted data on it, in the middle of an encrypted data transfer. |
| |
| |
| // This test creates a server and then connects a client to the server. |
| // After connecting, the connection is upgraded to a secure connection. |
| // The client writes data to the server, then writes unencrypted data |
| // on the underlying socket. When the server gets the unencrypted data, |
| // this causes an error in the NSS code. When the NSS code is in debug |
| // mode, bad data on the connection can cause an assertion failure, rather |
| // than an exception. |
| |
| const messageSize = 1000; |
| |
| |
| List<int> createTestData() { |
| List<int> data = new List<int>(messageSize); |
| for (int i = 0; i < messageSize; i++) { |
| data[i] = i & 0xff; |
| } |
| return data; |
| } |
| |
| |
| void verifyTestData(List<int> data) { |
| Expect.equals(messageSize, data.length); |
| List<int> expected = createTestData(); |
| for (int i = 0; i < messageSize; i++) { |
| Expect.equals(expected[i], data[i]); |
| } |
| } |
| |
| |
| Future runServer(RawSocket client) { |
| final completer = new Completer(); |
| final dataReceived = new List<int>(messageSize); |
| int bytesRead = 0; |
| client.writeEventsEnabled = false; |
| client.listen((event) { |
| switch (event) { |
| case RawSocketEvent.READ: |
| Expect.isTrue(client.available() > 0); |
| var buffer = client.read(200); |
| dataReceived.setRange(bytesRead, bytesRead + buffer.length, buffer); |
| bytesRead += buffer.length; |
| if (bytesRead == dataReceived.length) { |
| verifyTestData(dataReceived); |
| } |
| break; |
| case RawSocketEvent.WRITE: |
| Expect.fail('WRITE event received'); |
| break; |
| case RawSocketEvent.READ_CLOSED: |
| Expect.fail('READ_CLOSED event received'); |
| client.shutdown(SocketDirection.SEND); |
| completer.complete(null); |
| break; |
| } |
| }, |
| onError: (e) { |
| Expect.isTrue(e is TlsException); |
| Expect.isTrue(e.toString().contains( |
| 'received a record with an incorrect Message Authentication Code')); |
| completer.complete(null); |
| }); |
| return completer.future; |
| } |
| |
| |
| Future<RawSocket> runClient(List sockets) { |
| final RawSocket baseSocket = sockets[0]; |
| final RawSecureSocket socket = sockets[1]; |
| final data = createTestData(); |
| var completer = new Completer(); |
| void tryComplete() { |
| if (completer != null) { |
| completer.complete(null); |
| completer = null; |
| } |
| } |
| |
| int bytesWritten = 0; |
| socket.listen((event) { |
| switch (event) { |
| case RawSocketEvent.READ: |
| Expect.fail('READ event received'); |
| break; |
| case RawSocketEvent.WRITE: |
| if (bytesWritten < data.length) { |
| bytesWritten += socket.write(data, bytesWritten); |
| } |
| if (bytesWritten < data.length) { |
| socket.writeEventsEnabled = true; |
| } |
| if (bytesWritten == data.length) { |
| baseSocket.write(data, 0, 300); |
| socket.shutdown(SocketDirection.SEND); |
| } |
| break; |
| case RawSocketEvent.READ_CLOSED: |
| tryComplete(); |
| break; |
| } |
| }, |
| onError: (e) { |
| Expect.isTrue(e is IOException); |
| tryComplete(); |
| }); |
| return completer.future; |
| } |
| |
| |
| Future<List> connectClient(int port, bool hostnameInConnect) => |
| RawSocket.connect(HOST, port) |
| .then((socket) => |
| (hostnameInConnect ? RawSecureSocket.secure(socket) |
| : RawSecureSocket.secure(socket, host: HOST)) |
| .then((secureSocket) => [socket, secureSocket])); |
| |
| |
| Future test(bool hostnameInConnect) { |
| return RawServerSocket.bind(HOST, 0).then((server) { |
| server.listen((client) { |
| RawSecureSocket.secureServer(client, CERTIFICATE) |
| .then((secureClient) { |
| Expect.throws(() => client.add([0])); |
| return runServer(secureClient); |
| }, |
| onError: (e) { |
| Expect.isTrue(e is HandshakeException); |
| }) |
| .whenComplete(server.close); |
| }); |
| return connectClient(server.port, hostnameInConnect).then(runClient); |
| }); |
| } |
| |
| |
| main() { |
| asyncStart(); |
| InternetAddress.lookup("localhost").then((hosts) { |
| HOST = hosts.first; |
| return Future.wait([test(false), test(true)]); |
| }).then((_) => asyncEnd()); |
| } |