diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c62bf55dc1..c583667e3a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## 1.19.0 +### Core library changes + +* `dart:io` + * Report a better error when a bind fails because of a bad source address. + ### Tool Changes * `dartfmt` - upgraded to v0.2.9 diff --git a/runtime/bin/io_natives.cc b/runtime/bin/io_natives.cc index 609bbef264f..8c9c8c8fd5e 100644 --- a/runtime/bin/io_natives.cc +++ b/runtime/bin/io_natives.cc @@ -121,6 +121,7 @@ namespace bin { V(Socket_CreateConnect, 3) \ V(Socket_CreateBindConnect, 4) \ V(Socket_CreateBindDatagram, 4) \ + V(Socket_IsBindError, 2) \ V(Socket_Available, 1) \ V(Socket_Read, 2) \ V(Socket_RecvFrom, 1) \ diff --git a/runtime/bin/socket.cc b/runtime/bin/socket.cc index 7b6f95ae27b..745306d0e53 100644 --- a/runtime/bin/socket.cc +++ b/runtime/bin/socket.cc @@ -234,6 +234,12 @@ void FUNCTION_NAME(Socket_CreateBindConnect)(Dart_NativeArguments args) { } } +void FUNCTION_NAME(Socket_IsBindError)(Dart_NativeArguments args) { + intptr_t error_number = + DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 1)); + bool is_bind_error = Socket::IsBindError(error_number); + Dart_SetReturnValue(args, is_bind_error ? Dart_True() : Dart_False()); +} void FUNCTION_NAME(Socket_CreateBindDatagram)(Dart_NativeArguments args) { RawAddr addr; diff --git a/runtime/bin/socket.h b/runtime/bin/socket.h index 0b17706446b..6a50d796da6 100644 --- a/runtime/bin/socket.h +++ b/runtime/bin/socket.h @@ -278,6 +278,9 @@ class Socket { // specified as the port component of the passed RawAddr structure. static intptr_t CreateBindConnect(const RawAddr& addr, const RawAddr& source_addr); + // Returns true if the given error-number is because the system was not able + // to bind the socket to a specific IP. + static bool IsBindError(intptr_t error_number); // Creates a datagram socket which is bound. The port to bind // to is specified as the port component of the RawAddr structure. static intptr_t CreateBindDatagram(const RawAddr& addr, bool reuseAddress); diff --git a/runtime/bin/socket_android.cc b/runtime/bin/socket_android.cc index b3e3fc4cb10..caddb3a0de1 100644 --- a/runtime/bin/socket_android.cc +++ b/runtime/bin/socket_android.cc @@ -101,6 +101,12 @@ intptr_t Socket::CreateBindConnect(const RawAddr& addr, } +bool Socket::IsBindError(intptr_t error_number) { + return error_number == EADDRINUSE || error_number == EADDRNOTAVAIL || + error_number == EINVAL; +} + + intptr_t Socket::Available(intptr_t fd) { return FDUtils::AvailableBytes(fd); } diff --git a/runtime/bin/socket_fuchsia.cc b/runtime/bin/socket_fuchsia.cc index 78b5071dff4..b79d1c0956b 100644 --- a/runtime/bin/socket_fuchsia.cc +++ b/runtime/bin/socket_fuchsia.cc @@ -45,6 +45,12 @@ intptr_t Socket::CreateBindConnect(const RawAddr& addr, } +bool Socket::IsBindError(intptr_t error_number) { + UNIMPLEMENTED(); + return false; +} + + intptr_t Socket::Available(intptr_t fd) { UNIMPLEMENTED(); return -1; diff --git a/runtime/bin/socket_linux.cc b/runtime/bin/socket_linux.cc index 63935676fab..fc0f1e413c3 100644 --- a/runtime/bin/socket_linux.cc +++ b/runtime/bin/socket_linux.cc @@ -101,6 +101,12 @@ intptr_t Socket::CreateBindConnect(const RawAddr& addr, } +bool Socket::IsBindError(intptr_t error_number) { + return error_number == EADDRINUSE || error_number == EADDRNOTAVAIL || + error_number == EINVAL; +} + + intptr_t Socket::Available(intptr_t fd) { return FDUtils::AvailableBytes(fd); } diff --git a/runtime/bin/socket_macos.cc b/runtime/bin/socket_macos.cc index 708cd3cdea9..2e336a20037 100644 --- a/runtime/bin/socket_macos.cc +++ b/runtime/bin/socket_macos.cc @@ -103,6 +103,12 @@ intptr_t Socket::CreateBindConnect(const RawAddr& addr, } +bool Socket::IsBindError(intptr_t error_number) { + return error_number == EADDRINUSE || error_number == EADDRNOTAVAIL || + error_number == EINVAL; +} + + intptr_t Socket::Available(intptr_t fd) { return FDUtils::AvailableBytes(fd); } diff --git a/runtime/bin/socket_patch.dart b/runtime/bin/socket_patch.dart index 2d3ba73b3f7..a8b60c3e063 100644 --- a/runtime/bin/socket_patch.dart +++ b/runtime/bin/socket_patch.dart @@ -431,7 +431,13 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject { if (result is OSError) { // Keep first error, if present. if (error == null) { - error = createError(result, "Connection failed", address, port); + int errorCode = result.errorCode; + if (errorCode != null && socket.isBindError(errorCode)) { + error = createError(result, "Bind failed", sourceAddress); + } else { + error = + createError(result, "Connection failed", address, port); + } } connectNext(); } else { @@ -1075,6 +1081,7 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject { nativeCreateBindConnect( List addr, int port, List sourceAddr) native "Socket_CreateBindConnect"; + bool isBindError(int errorNumber) native "Socket_IsBindError"; nativeCreateBindListen(List addr, int port, int backlog, bool v6Only, bool shared) native "ServerSocket_CreateBindListen"; diff --git a/runtime/bin/socket_unsupported.cc b/runtime/bin/socket_unsupported.cc index 0ea27f1aee0..089e48e8967 100644 --- a/runtime/bin/socket_unsupported.cc +++ b/runtime/bin/socket_unsupported.cc @@ -35,6 +35,12 @@ void FUNCTION_NAME(Socket_CreateBindConnect)(Dart_NativeArguments args) { } +void FUNCTION_NAME(Socket_IsBindError)(Dart_NativeArguments args) { + Dart_ThrowException(DartUtils::NewDartArgumentError( + "Sockets unsupported on this platform")); +} + + void FUNCTION_NAME(Socket_CreateBindDatagram)(Dart_NativeArguments args) { Dart_ThrowException(DartUtils::NewDartArgumentError( "Sockets unsupported on this platform")); diff --git a/runtime/bin/socket_win.cc b/runtime/bin/socket_win.cc index e3d366da53b..f76f764c7c4 100644 --- a/runtime/bin/socket_win.cc +++ b/runtime/bin/socket_win.cc @@ -250,6 +250,12 @@ intptr_t Socket::CreateBindConnect(const RawAddr& addr, } +bool Socket::IsBindError(intptr_t error_number) { + return error_number == WSAEADDRINUSE || error_number == WSAEADDRNOTAVAIL || + error_number == WSAEINVAL; +} + + void Socket::GetError(intptr_t fd, OSError* os_error) { Handle* handle = reinterpret_cast(fd); os_error->SetCodeAndMessage(OSError::kSystem, handle->last_error()); diff --git a/tests/standalone/io/socket_source_address_test.dart b/tests/standalone/io/socket_source_address_test.dart index 4b95433e2bc..64dc490bde9 100644 --- a/tests/standalone/io/socket_source_address_test.dart +++ b/tests/standalone/io/socket_source_address_test.dart @@ -43,7 +43,8 @@ Future testArguments(connectFunction) async { await throws(() => connectFunction('127.0.0.1', server.port, sourceAddress: sourceAddress), - (e) => e is SocketException); + (e) => e is SocketException && + e.address == new InternetAddress('8.8.8.8')); } // Address family mismatch. for (sourceAddress in ['::1', InternetAddress.LOOPBACK_IP_V6]) {