mirror of
https://github.com/dart-lang/sdk
synced 2024-10-03 00:29:50 +00:00
Exposes a thin layer over getsockopt/setsockopt for supported platforms.
This allows developers to have more fine grained control over socket options supported by their platforms, particularly when there is not a nice way to encapsulate differences between IPv4 and IPv6 options (as with IP_MULTICAST_IF and IPV6_MULTICAST_IF). It also begins the work of exposing socket level and option values, although keeping it for now only to a minimum necessary to assist with setting the multicast interface for datagram sockets. This CL also marks `multicastInterface` as deprecated. Bug: https://github.com/dart-lang/sdk/issues/17057 Change-Id: I39b3bf3d32d39de1c777acea4425d6eb2226355d Reviewed-on: https://dart-review.googlesource.com/c/89164 Commit-Queue: Zach Anderson <zra@google.com> Reviewed-by: Zach Anderson <zra@google.com>
This commit is contained in:
parent
c701e76506
commit
5b1daaac6c
|
@ -28,6 +28,10 @@
|
|||
[35484]: https://github.com/dart-lang/sdk/issues/35484
|
||||
[35510]: https://github.com/dart-lang/sdk/issues/35510
|
||||
|
||||
#### `dart:io`
|
||||
|
||||
* Added ability to get and set low level socket options.
|
||||
|
||||
### Dart VM
|
||||
|
||||
### Tool Changes
|
||||
|
@ -2800,7 +2804,7 @@ during isolate initialization.
|
|||
people in practice.
|
||||
|
||||
* **Breaking:** Support for `barback` versions prior to 0.15.0 (released July
|
||||
2014) has been dropped. Pub will no longer install these older barback
|
||||
1) has been dropped. Pub will no longer install these older barback
|
||||
versions.
|
||||
|
||||
* `pub serve` now GZIPs the assets it serves to make load times more similar
|
||||
|
|
2
DEPS
2
DEPS
|
@ -87,7 +87,7 @@ vars = {
|
|||
"func_rev": "25eec48146a58967d75330075ab376b3838b18a8",
|
||||
"glob_tag": "1.1.7",
|
||||
"html_tag" : "0.13.3+2",
|
||||
"http_io_rev": "265e90afbffacb7b2988385d4a6aa2f14e970d44",
|
||||
"http_io_rev": "57da05a66f5bf7df3dd7aebe7b7efe0dfc477baa",
|
||||
"http_multi_server_tag" : "2.0.5",
|
||||
"http_parser_tag" : "3.1.1",
|
||||
"http_retry_tag": "0.1.1",
|
||||
|
|
|
@ -500,6 +500,14 @@ class RawSynchronousSocket {
|
|||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
class RawSocketOption {
|
||||
@patch
|
||||
static int _getOptionValue(int key) {
|
||||
throw UnsupportedError("RawSocketOption._getOptionValue");
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
class SecurityContext {
|
||||
@patch
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "bin/builtin.h"
|
||||
#include "bin/dartutils.h"
|
||||
#include "bin/socket_base.h"
|
||||
#include "include/dart_api.h"
|
||||
#include "platform/assert.h"
|
||||
|
||||
|
@ -109,6 +110,7 @@ namespace bin {
|
|||
V(Process_ClearSignalHandler, 1) \
|
||||
V(ProcessInfo_CurrentRSS, 0) \
|
||||
V(ProcessInfo_MaxRSS, 0) \
|
||||
V(RawSocketOption_GetOptionValue, 1) \
|
||||
V(SecureSocket_Connect, 7) \
|
||||
V(SecureSocket_Destroy, 1) \
|
||||
V(SecureSocket_FilterPointer, 1) \
|
||||
|
@ -137,6 +139,7 @@ namespace bin {
|
|||
V(Socket_GetRemotePeer, 1) \
|
||||
V(Socket_GetError, 1) \
|
||||
V(Socket_GetOption, 3) \
|
||||
V(Socket_GetRawOption, 4) \
|
||||
V(Socket_GetSocketId, 1) \
|
||||
V(Socket_GetStdioHandle, 2) \
|
||||
V(Socket_GetType, 1) \
|
||||
|
@ -146,6 +149,7 @@ namespace bin {
|
|||
V(Socket_RecvFrom, 1) \
|
||||
V(Socket_SendTo, 6) \
|
||||
V(Socket_SetOption, 4) \
|
||||
V(Socket_SetRawOption, 4) \
|
||||
V(Socket_SetSocketId, 3) \
|
||||
V(Socket_WriteList, 4) \
|
||||
V(Stdin_ReadByte, 1) \
|
||||
|
|
|
@ -315,11 +315,11 @@ void FUNCTION_NAME(Socket_Available)(Dart_NativeArguments args) {
|
|||
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
|
||||
intptr_t available = SocketBase::Available(socket->fd());
|
||||
if (available >= 0) {
|
||||
Dart_SetReturnValue(args, Dart_NewInteger(available));
|
||||
Dart_SetIntegerReturnValue(args, available);
|
||||
} else {
|
||||
// Available failed. Mark socket as having data, to trigger a future read
|
||||
// event where the actual error can be reported.
|
||||
Dart_SetReturnValue(args, Dart_NewInteger(1));
|
||||
Dart_SetIntegerReturnValue(args, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -482,9 +482,9 @@ void FUNCTION_NAME(Socket_WriteList)(Dart_NativeArguments args) {
|
|||
if (short_write) {
|
||||
// If the write was forced 'short', indicate by returning the negative
|
||||
// number of bytes. A forced short write may not trigger a write event.
|
||||
Dart_SetReturnValue(args, Dart_NewInteger(-bytes_written));
|
||||
Dart_SetIntegerReturnValue(args, -bytes_written);
|
||||
} else {
|
||||
Dart_SetReturnValue(args, Dart_NewInteger(bytes_written));
|
||||
Dart_SetIntegerReturnValue(args, bytes_written);
|
||||
}
|
||||
} else {
|
||||
// Extract OSError before we release data, as it may override the error.
|
||||
|
@ -521,7 +521,7 @@ void FUNCTION_NAME(Socket_SendTo)(Dart_NativeArguments args) {
|
|||
addr, SocketBase::kAsync);
|
||||
if (bytes_written >= 0) {
|
||||
Dart_TypedDataReleaseData(buffer_obj);
|
||||
Dart_SetReturnValue(args, Dart_NewInteger(bytes_written));
|
||||
Dart_SetIntegerReturnValue(args, bytes_written);
|
||||
} else {
|
||||
// Extract OSError before we release data, as it may override the error.
|
||||
OSError os_error;
|
||||
|
@ -536,7 +536,7 @@ void FUNCTION_NAME(Socket_GetPort)(Dart_NativeArguments args) {
|
|||
OSError os_error;
|
||||
intptr_t port = SocketBase::GetPort(socket->fd());
|
||||
if (port > 0) {
|
||||
Dart_SetReturnValue(args, Dart_NewInteger(port));
|
||||
Dart_SetIntegerReturnValue(args, port);
|
||||
} else {
|
||||
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
|
||||
}
|
||||
|
@ -581,7 +581,7 @@ void FUNCTION_NAME(Socket_GetType)(Dart_NativeArguments args) {
|
|||
OSError os_error;
|
||||
intptr_t type = SocketBase::GetType(socket->fd());
|
||||
if (type >= 0) {
|
||||
Dart_SetReturnValue(args, Dart_NewInteger(type));
|
||||
Dart_SetIntegerReturnValue(args, type);
|
||||
} else {
|
||||
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
|
||||
}
|
||||
|
@ -600,7 +600,7 @@ void FUNCTION_NAME(Socket_GetSocketId)(Dart_NativeArguments args) {
|
|||
Socket* socket =
|
||||
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
|
||||
intptr_t id = reinterpret_cast<intptr_t>(socket);
|
||||
Dart_SetReturnValue(args, Dart_NewInteger(id));
|
||||
Dart_SetIntegerReturnValue(args, id);
|
||||
}
|
||||
|
||||
void FUNCTION_NAME(Socket_SetSocketId)(Dart_NativeArguments args) {
|
||||
|
@ -783,7 +783,7 @@ void FUNCTION_NAME(Socket_GetOption)(Dart_NativeArguments args) {
|
|||
bool enabled;
|
||||
ok = SocketBase::GetNoDelay(socket->fd(), &enabled);
|
||||
if (ok) {
|
||||
Dart_SetReturnValue(args, enabled ? Dart_True() : Dart_False());
|
||||
Dart_SetBooleanReturnValue(args, enabled);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -791,7 +791,7 @@ void FUNCTION_NAME(Socket_GetOption)(Dart_NativeArguments args) {
|
|||
bool enabled;
|
||||
ok = SocketBase::GetMulticastLoop(socket->fd(), protocol, &enabled);
|
||||
if (ok) {
|
||||
Dart_SetReturnValue(args, enabled ? Dart_True() : Dart_False());
|
||||
Dart_SetBooleanReturnValue(args, enabled);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -799,7 +799,7 @@ void FUNCTION_NAME(Socket_GetOption)(Dart_NativeArguments args) {
|
|||
int value;
|
||||
ok = SocketBase::GetMulticastHops(socket->fd(), protocol, &value);
|
||||
if (ok) {
|
||||
Dart_SetReturnValue(args, Dart_NewInteger(value));
|
||||
Dart_SetIntegerReturnValue(args, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -811,7 +811,7 @@ void FUNCTION_NAME(Socket_GetOption)(Dart_NativeArguments args) {
|
|||
bool enabled;
|
||||
ok = SocketBase::GetBroadcast(socket->fd(), &enabled);
|
||||
if (ok) {
|
||||
Dart_SetReturnValue(args, enabled ? Dart_True() : Dart_False());
|
||||
Dart_SetBooleanReturnValue(args, enabled);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -869,6 +869,106 @@ void FUNCTION_NAME(Socket_SetOption)(Dart_NativeArguments args) {
|
|||
}
|
||||
}
|
||||
|
||||
void FUNCTION_NAME(Socket_SetRawOption)(Dart_NativeArguments args) {
|
||||
Socket* socket =
|
||||
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
|
||||
int64_t level = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1));
|
||||
int64_t option = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2));
|
||||
Dart_Handle data_obj = Dart_GetNativeArgument(args, 3);
|
||||
ASSERT(Dart_IsList(data_obj));
|
||||
char* data = NULL;
|
||||
intptr_t length;
|
||||
Dart_TypedData_Type type;
|
||||
Dart_Handle data_result = Dart_TypedDataAcquireData(
|
||||
data_obj, &type, reinterpret_cast<void**>(&data), &length);
|
||||
if (Dart_IsError(data_result)) {
|
||||
Dart_PropagateError(data_result);
|
||||
}
|
||||
|
||||
bool result = SocketBase::SetOption(socket->fd(), static_cast<int>(level),
|
||||
static_cast<int>(option), data,
|
||||
static_cast<int>(length));
|
||||
|
||||
Dart_TypedDataReleaseData(data_obj);
|
||||
|
||||
if (result) {
|
||||
Dart_SetReturnValue(args, Dart_Null());
|
||||
} else {
|
||||
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
|
||||
}
|
||||
}
|
||||
|
||||
void FUNCTION_NAME(Socket_GetRawOption)(Dart_NativeArguments args) {
|
||||
Socket* socket =
|
||||
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
|
||||
int64_t level = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1));
|
||||
int64_t option = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2));
|
||||
Dart_Handle data_obj = Dart_GetNativeArgument(args, 3);
|
||||
ASSERT(Dart_IsList(data_obj));
|
||||
char* data = NULL;
|
||||
intptr_t length;
|
||||
Dart_TypedData_Type type;
|
||||
Dart_Handle data_result = Dart_TypedDataAcquireData(
|
||||
data_obj, &type, reinterpret_cast<void**>(&data), &length);
|
||||
if (Dart_IsError(data_result)) {
|
||||
Dart_PropagateError(data_result);
|
||||
}
|
||||
unsigned int int_length = static_cast<unsigned int>(length);
|
||||
bool result =
|
||||
SocketBase::GetOption(socket->fd(), static_cast<int>(level),
|
||||
static_cast<int>(option), data, &int_length);
|
||||
|
||||
Dart_TypedDataReleaseData(data_obj);
|
||||
|
||||
if (result) {
|
||||
Dart_SetReturnValue(args, Dart_Null());
|
||||
} else {
|
||||
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
|
||||
}
|
||||
}
|
||||
|
||||
// Keep in sync with _RawSocketOptions in socket.dart
|
||||
enum _RawSocketOptions : int64_t {
|
||||
DART_SOL_SOCKET = 0,
|
||||
DART_IPPROTO_IP = 1,
|
||||
DART_IP_MULTICAST_IF = 2,
|
||||
DART_IPPROTO_IPV6 = 3,
|
||||
DART_IPV6_MULTICAST_IF = 4,
|
||||
DART_IPPROTO_TCP = 5,
|
||||
DART_IPPROTO_UDP = 6
|
||||
};
|
||||
|
||||
void FUNCTION_NAME(RawSocketOption_GetOptionValue)(Dart_NativeArguments args) {
|
||||
_RawSocketOptions key = static_cast<_RawSocketOptions>(
|
||||
DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 0)));
|
||||
switch (key) {
|
||||
case DART_SOL_SOCKET:
|
||||
Dart_SetIntegerReturnValue(args, SOL_SOCKET);
|
||||
break;
|
||||
case DART_IPPROTO_IP:
|
||||
Dart_SetIntegerReturnValue(args, IPPROTO_IP);
|
||||
break;
|
||||
case DART_IP_MULTICAST_IF:
|
||||
Dart_SetIntegerReturnValue(args, IP_MULTICAST_IF);
|
||||
break;
|
||||
case DART_IPPROTO_IPV6:
|
||||
Dart_SetIntegerReturnValue(args, DART_IPPROTO_IPV6);
|
||||
break;
|
||||
case DART_IPV6_MULTICAST_IF:
|
||||
Dart_SetIntegerReturnValue(args, IPV6_MULTICAST_IF);
|
||||
break;
|
||||
case DART_IPPROTO_TCP:
|
||||
Dart_SetIntegerReturnValue(args, IPPROTO_TCP);
|
||||
break;
|
||||
case DART_IPPROTO_UDP:
|
||||
Dart_SetIntegerReturnValue(args, IPPROTO_UDP);
|
||||
break;
|
||||
default:
|
||||
Dart_PropagateError(Dart_NewApiError("Value outside of expected range"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FUNCTION_NAME(Socket_JoinMulticast)(Dart_NativeArguments args) {
|
||||
Socket* socket =
|
||||
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
|
||||
|
|
|
@ -182,6 +182,16 @@ class SocketBase : public AllStatic {
|
|||
static bool SetMulticastHops(intptr_t fd, intptr_t protocol, int value);
|
||||
static bool GetBroadcast(intptr_t fd, bool* value);
|
||||
static bool SetBroadcast(intptr_t fd, bool value);
|
||||
static bool GetOption(intptr_t fd,
|
||||
int level,
|
||||
int option,
|
||||
char* data,
|
||||
unsigned int* length);
|
||||
static bool SetOption(intptr_t fd,
|
||||
int level,
|
||||
int option,
|
||||
const char* data,
|
||||
int length);
|
||||
static bool JoinMulticast(intptr_t fd,
|
||||
const RawAddr& addr,
|
||||
const RawAddr& interface,
|
||||
|
|
|
@ -350,6 +350,22 @@ bool SocketBase::SetBroadcast(intptr_t fd, bool enabled) {
|
|||
sizeof(on))) == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::SetOption(intptr_t fd,
|
||||
int level,
|
||||
int option,
|
||||
const char* data,
|
||||
int length) {
|
||||
return NO_RETRY_EXPECTED(setsockopt(fd, level, option, data, length)) == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::GetOption(intptr_t fd,
|
||||
int level,
|
||||
int option,
|
||||
char* data,
|
||||
unsigned int* length) {
|
||||
return NO_RETRY_EXPECTED(getsockopt(fd, level, option, data, length)) == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::JoinMulticast(intptr_t fd,
|
||||
const RawAddr& addr,
|
||||
const RawAddr&,
|
||||
|
|
|
@ -375,6 +375,26 @@ bool SocketBase::SetBroadcast(intptr_t fd, bool enabled) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool SocketBase::SetOption(intptr_t fd,
|
||||
int level,
|
||||
int option,
|
||||
const char* data,
|
||||
int length) {
|
||||
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
|
||||
return NO_RETRY_EXPECTED(
|
||||
setsockopt(handle->fd(), level, option, data, length)) == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::GetOption(intptr_t fd,
|
||||
int level,
|
||||
int option,
|
||||
char* data,
|
||||
unsigned int* length) {
|
||||
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
|
||||
return NO_RETRY_EXPECTED(
|
||||
getsockopt(handle->fd(), level, option, data, length)) == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::JoinMulticast(intptr_t fd,
|
||||
const RawAddr& addr,
|
||||
const RawAddr&,
|
||||
|
|
|
@ -391,6 +391,22 @@ bool SocketBase::SetBroadcast(intptr_t fd, bool enabled) {
|
|||
sizeof(on))) == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::SetOption(intptr_t fd,
|
||||
int level,
|
||||
int option,
|
||||
const char* data,
|
||||
int length) {
|
||||
return NO_RETRY_EXPECTED(setsockopt(fd, level, option, data, length)) == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::GetOption(intptr_t fd,
|
||||
int level,
|
||||
int option,
|
||||
char* data,
|
||||
unsigned int* length) {
|
||||
return NO_RETRY_EXPECTED(getsockopt(fd, level, option, data, length)) == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::JoinMulticast(intptr_t fd,
|
||||
const RawAddr& addr,
|
||||
const RawAddr&,
|
||||
|
|
|
@ -381,6 +381,22 @@ bool SocketBase::SetBroadcast(intptr_t fd, bool enabled) {
|
|||
sizeof(on))) == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::SetOption(intptr_t fd,
|
||||
int level,
|
||||
int option,
|
||||
const char* data,
|
||||
int length) {
|
||||
return NO_RETRY_EXPECTED(setsockopt(fd, level, option, data, length)) == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::GetOption(intptr_t fd,
|
||||
int level,
|
||||
int option,
|
||||
char* data,
|
||||
unsigned int* length) {
|
||||
return NO_RETRY_EXPECTED(getsockopt(fd, level, option, data, length)) == 0;
|
||||
}
|
||||
|
||||
static bool JoinOrLeaveMulticast(intptr_t fd,
|
||||
const RawAddr& addr,
|
||||
const RawAddr& interface,
|
||||
|
|
|
@ -396,6 +396,27 @@ bool SocketBase::SetBroadcast(intptr_t fd, bool enabled) {
|
|||
reinterpret_cast<char*>(&on), sizeof(on)) == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::SetOption(intptr_t fd,
|
||||
int level,
|
||||
int option,
|
||||
const char* data,
|
||||
int length) {
|
||||
SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
|
||||
return setsockopt(handle->socket(), level, option, data, length) == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::GetOption(intptr_t fd,
|
||||
int level,
|
||||
int option,
|
||||
char* data,
|
||||
unsigned int* length) {
|
||||
SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
|
||||
int optlen = static_cast<int>(*length);
|
||||
auto result = getsockopt(handle->socket(), level, option, data, &optlen);
|
||||
*length = static_cast<unsigned int>(optlen);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
bool SocketBase::JoinMulticast(intptr_t fd,
|
||||
const RawAddr& addr,
|
||||
const RawAddr&,
|
||||
|
|
|
@ -28,6 +28,24 @@ class RawSocket {
|
|||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
class RawSocketOption {
|
||||
static final List<int> _optionsCache =
|
||||
List<int>(_RawSocketOptions.values.length);
|
||||
|
||||
@patch
|
||||
static int _getOptionValue(int key) {
|
||||
if (key > _RawSocketOptions.values.length) {
|
||||
throw ArgumentError.value(key, 'key');
|
||||
}
|
||||
_optionsCache[key] ??= _getNativeOptionValue(key);
|
||||
return _optionsCache[key];
|
||||
}
|
||||
|
||||
static int _getNativeOptionValue(int key)
|
||||
native "RawSocketOption_GetOptionValue";
|
||||
}
|
||||
|
||||
@patch
|
||||
class InternetAddress {
|
||||
@patch
|
||||
|
@ -1084,6 +1102,23 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject {
|
|||
return true;
|
||||
}
|
||||
|
||||
Uint8List getRawOption(RawSocketOption option) {
|
||||
if (option == null) throw new ArgumentError.notNull("option");
|
||||
if (option.value == null) throw new ArgumentError.notNull("option.value");
|
||||
|
||||
var result = nativeGetRawOption(option.level, option.option, option.value);
|
||||
if (result != null) throw result;
|
||||
return option.value;
|
||||
}
|
||||
|
||||
void setRawOption(RawSocketOption option) {
|
||||
if (option == null) throw new ArgumentError.notNull("option");
|
||||
if (option.value == null) throw new ArgumentError.notNull("option.value");
|
||||
|
||||
var result = nativeSetRawOption(option.level, option.option, option.value);
|
||||
if (result != null) throw result;
|
||||
}
|
||||
|
||||
InternetAddress multicastAddress(
|
||||
InternetAddress addr, NetworkInterface interface) {
|
||||
// On Mac OS using the interface index for joining IPv4 multicast groups
|
||||
|
@ -1146,8 +1181,12 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject {
|
|||
int nativeGetSocketId() native "Socket_GetSocketId";
|
||||
OSError nativeGetError() native "Socket_GetError";
|
||||
nativeGetOption(int option, int protocol) native "Socket_GetOption";
|
||||
OSError nativeGetRawOption(int level, int option, Uint8List data)
|
||||
native "Socket_GetRawOption";
|
||||
OSError nativeSetOption(int option, int protocol, value)
|
||||
native "Socket_SetOption";
|
||||
OSError nativeSetRawOption(int level, int option, Uint8List data)
|
||||
native "Socket_SetRawOption";
|
||||
OSError nativeJoinMulticast(List<int> addr, List<int> interfaceAddr,
|
||||
int interfaceIndex) native "Socket_JoinMulticast";
|
||||
bool nativeLeaveMulticast(List<int> addr, List<int> interfaceAddr,
|
||||
|
@ -1377,6 +1416,10 @@ class _RawSocket extends Stream<RawSocketEvent> implements RawSocket {
|
|||
bool setOption(SocketOption option, bool enabled) =>
|
||||
_socket.setOption(option, enabled);
|
||||
|
||||
Uint8List getRawOption(RawSocketOption option) =>
|
||||
_socket.getRawOption(option);
|
||||
void setRawOption(RawSocketOption option) => _socket.setRawOption(option);
|
||||
|
||||
_pause() {
|
||||
_socket.setListening(read: false, write: false);
|
||||
}
|
||||
|
@ -1642,6 +1685,15 @@ class _Socket extends Stream<List<int>> implements Socket {
|
|||
return _raw.setOption(option, enabled);
|
||||
}
|
||||
|
||||
Uint8List getRawOption(RawSocketOption option) {
|
||||
if (_raw == null) return null;
|
||||
return _raw.getRawOption(option);
|
||||
}
|
||||
|
||||
void setRawOption(RawSocketOption option) {
|
||||
_raw?.setRawOption(option);
|
||||
}
|
||||
|
||||
int get port {
|
||||
if (_raw == null) throw const SocketException.closed();
|
||||
;
|
||||
|
@ -1913,6 +1965,10 @@ class _RawDatagramSocket extends Stream<RawSocketEvent>
|
|||
_socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
Uint8List getRawOption(RawSocketOption option) =>
|
||||
_socket.getRawOption(option);
|
||||
void setRawOption(RawSocketOption option) => _socket.setRawOption(option);
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
|
|
|
@ -2926,6 +2926,14 @@ class _DetachedSocket extends Stream<List<int>> implements Socket {
|
|||
return _socket.setOption(option, enabled);
|
||||
}
|
||||
|
||||
Uint8List getRawOption(RawSocketOption option) {
|
||||
return _socket.getRawOption(option);
|
||||
}
|
||||
|
||||
void setRawOption(RawSocketOption option) {
|
||||
_socket.setRawOption(option);
|
||||
}
|
||||
|
||||
Map _toJSON(bool ref) {
|
||||
return (_socket as dynamic)._toJSON(ref);
|
||||
}
|
||||
|
|
|
@ -500,6 +500,14 @@ class RawSynchronousSocket {
|
|||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
class RawSocketOption {
|
||||
@patch
|
||||
static int _getOptionValue(int key) {
|
||||
throw UnsupportedError("RawSocketOption._getOptionValue");
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
class SecurityContext {
|
||||
@patch
|
||||
|
|
|
@ -736,6 +736,14 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
|||
return _socket.setOption(option, enabled);
|
||||
}
|
||||
|
||||
Uint8List getRawOption(RawSocketOption option) {
|
||||
return _socket?.getRawOption(option);
|
||||
}
|
||||
|
||||
void setRawOption(RawSocketOption option) {
|
||||
_socket?.setRawOption(option);
|
||||
}
|
||||
|
||||
void _eventDispatcher(RawSocketEvent event) {
|
||||
try {
|
||||
if (event == RawSocketEvent.read) {
|
||||
|
|
|
@ -395,6 +395,109 @@ class SocketOption {
|
|||
const SocketOption._(this._value);
|
||||
}
|
||||
|
||||
// Must be kept in sync with enum in socket.cc
|
||||
enum _RawSocketOptions {
|
||||
SOL_SOCKET, // 0
|
||||
IPPROTO_IP, // 1
|
||||
IP_MULTICAST_IF, // 2
|
||||
IPPROTO_IPV6, // 3
|
||||
IPV6_MULTICAST_IF, // 4
|
||||
IPPROTO_TCP, // 5
|
||||
IPPROTO_UDP, // 6
|
||||
}
|
||||
|
||||
/// The [RawSocketOption] is used as a parameter to [Socket.setRawOption] and
|
||||
/// [RawSocket.setRawOption] to set customize the behaviour of the underlying
|
||||
/// socket.
|
||||
///
|
||||
/// It allows for fine grained control of the socket options, and its values will
|
||||
/// be passed to the underlying platform's implementation of setsockopt and
|
||||
/// getsockopt.
|
||||
class RawSocketOption {
|
||||
/// Creates a RawSocketOption for getRawOption andSetRawOption.
|
||||
///
|
||||
/// All arguments are required and must not be null.
|
||||
///
|
||||
/// The level and option arguments correspond to level and optname arguments
|
||||
/// on the get/setsockopt native calls.
|
||||
///
|
||||
/// The value argument and its length correspond to the optval and length
|
||||
/// arguments on the native call.
|
||||
///
|
||||
/// For a [getRawOption] call, the value parameter will be updated after a
|
||||
/// successful call (although its length will not be changed).
|
||||
///
|
||||
/// For a [setRawOption] call, the value parameter will be used set the
|
||||
/// option.
|
||||
const RawSocketOption(this.level, this.option, this.value);
|
||||
|
||||
/// Convenience constructor for creating an int based RawSocketOption.
|
||||
factory RawSocketOption.fromInt(int level, int option, int value) {
|
||||
if (value == null) {
|
||||
value = 0;
|
||||
}
|
||||
final Uint8List list = Uint8List(4);
|
||||
final buffer = ByteData.view(list.buffer);
|
||||
buffer.setInt32(0, value);
|
||||
return RawSocketOption(level, option, list);
|
||||
}
|
||||
|
||||
/// Convenience constructor for creating a bool based RawSocketOption.
|
||||
factory RawSocketOption.fromBool(int level, int option, bool value) =>
|
||||
RawSocketOption.fromInt(level, option, value == true ? 1 : 0);
|
||||
|
||||
/// The level for the option to set or get.
|
||||
///
|
||||
/// See also:
|
||||
/// * [RawSocketOption.levelSocket]
|
||||
/// * [RawSocketOption.levelIPv4]
|
||||
/// * [RawSocketOption.levelIPv6]
|
||||
/// * [RawSocketOption.levelTcp]
|
||||
/// * [RawSocketOption.levelUdp]
|
||||
final int level;
|
||||
|
||||
/// The option to set or get.
|
||||
final int option;
|
||||
|
||||
/// The raw data to set, or the array to write the current option value into.
|
||||
///
|
||||
/// This list must be the correct length for the expected option. For most
|
||||
/// options that take int or bool values, the length should be 4. For options
|
||||
/// that expect a struct (such as an in_addr_t), the length should be the
|
||||
/// correct length for that struct.
|
||||
final Uint8List value;
|
||||
|
||||
/// Socket level option for SOL_SOCKET.
|
||||
static int get levelSocket =>
|
||||
_getOptionValue(_RawSocketOptions.SOL_SOCKET.index);
|
||||
|
||||
/// Socket level option for IPPROTO_IP.
|
||||
static int get levelIPv4 =>
|
||||
_getOptionValue(_RawSocketOptions.IPPROTO_IP.index);
|
||||
|
||||
/// Socket option for IP_MULTICAST_IF.
|
||||
static int get IPv4MulticastInterface =>
|
||||
_getOptionValue(_RawSocketOptions.IP_MULTICAST_IF.index);
|
||||
|
||||
/// Socket level option for IPPROTO_IPV6.
|
||||
static int get levelIPv6 =>
|
||||
_getOptionValue(_RawSocketOptions.IPPROTO_IPV6.index);
|
||||
|
||||
/// Socket option for IPV6_MULTICAST_IF.
|
||||
static int get IPv6MulticastInterface =>
|
||||
_getOptionValue(_RawSocketOptions.IPV6_MULTICAST_IF.index);
|
||||
|
||||
/// Socket level option for IPPROTO_TCP.
|
||||
static int get levelTcp =>
|
||||
_getOptionValue(_RawSocketOptions.IPPROTO_TCP.index);
|
||||
|
||||
/// Socket level option for IPPROTO_UDP.
|
||||
static int get levelUdp =>
|
||||
_getOptionValue(_RawSocketOptions.IPPROTO_UDP.index);
|
||||
|
||||
external static int _getOptionValue(int key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Events for the [RawSocket].
|
||||
*/
|
||||
|
@ -573,6 +676,24 @@ abstract class RawSocket implements Stream<RawSocketEvent> {
|
|||
* Returns [:true:] if the option was set successfully, false otherwise.
|
||||
*/
|
||||
bool setOption(SocketOption option, bool enabled);
|
||||
|
||||
/**
|
||||
* Use [getRawOption] to get low level information about the [RawSocket]. See
|
||||
* [RawSocketOption] for available options.
|
||||
*
|
||||
* Returns the [RawSocketOption.value] on success.
|
||||
*
|
||||
* Throws an [OSError] on failure.
|
||||
*/
|
||||
Uint8List getRawOption(RawSocketOption option);
|
||||
|
||||
/**
|
||||
* Use [setRawOption] to customize the [RawSocket]. See [RawSocketOption] for
|
||||
* available options.
|
||||
*
|
||||
* Throws an [OSError] on failure.
|
||||
*/
|
||||
void setRawOption(RawSocketOption option);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -652,6 +773,24 @@ abstract class Socket implements Stream<List<int>>, IOSink {
|
|||
*/
|
||||
bool setOption(SocketOption option, bool enabled);
|
||||
|
||||
/**
|
||||
* Use [getRawOption] to get low level information about the [RawSocket]. See
|
||||
* [RawSocketOption] for available options.
|
||||
*
|
||||
* Returns the [RawSocketOption.value] on success.
|
||||
*
|
||||
* Throws an [OSError] on failure.
|
||||
*/
|
||||
Uint8List getRawOption(RawSocketOption option);
|
||||
|
||||
/**
|
||||
* Use [setRawOption] to customize the [RawSocket]. See [RawSocketOption] for
|
||||
* available options.
|
||||
*
|
||||
* Throws an [OSError] on failure.
|
||||
*/
|
||||
void setRawOption(RawSocketOption option);
|
||||
|
||||
/**
|
||||
* Returns the port used by this socket.
|
||||
*/
|
||||
|
@ -739,6 +878,8 @@ abstract class RawDatagramSocket extends Stream<RawSocketEvent> {
|
|||
*
|
||||
* By default this value is `null`
|
||||
*/
|
||||
@Deprecated("This property is not implemented. Use getRawOption and "
|
||||
"setRawOption instead.")
|
||||
NetworkInterface multicastInterface;
|
||||
|
||||
/**
|
||||
|
@ -805,6 +946,24 @@ abstract class RawDatagramSocket extends Stream<RawSocketEvent> {
|
|||
* exception is thrown.
|
||||
*/
|
||||
void leaveMulticast(InternetAddress group, [NetworkInterface interface]);
|
||||
|
||||
/**
|
||||
* Use [getRawOption] to get low level information about the [RawSocket]. See
|
||||
* [RawSocketOption] for available options.
|
||||
*
|
||||
* Returns [RawSocketOption.value] on success.
|
||||
*
|
||||
* Throws an [OSError] on failure.
|
||||
*/
|
||||
Uint8List getRawOption(RawSocketOption option);
|
||||
|
||||
/**
|
||||
* Use [setRawOption] to customize the [RawSocket]. See [RawSocketOption] for
|
||||
* available options.
|
||||
*
|
||||
* Throws an [OSError] on failure.
|
||||
*/
|
||||
void setRawOption(RawSocketOption option);
|
||||
}
|
||||
|
||||
class SocketException implements IOException {
|
||||
|
|
|
@ -116,6 +116,35 @@ testDatagramSocketTtl() {
|
|||
test(InternetAddress.loopbackIPv6, null, false);
|
||||
}
|
||||
|
||||
testDatagramSocketMulticastIf() {
|
||||
test(address) async {
|
||||
asyncStart();
|
||||
final socket = await RawDatagramSocket.bind(address, 0);
|
||||
RawSocketOption option;
|
||||
if (address.type == InternetAddressType.IPv4) {
|
||||
option = RawSocketOption(RawSocketOption.levelIPv4,
|
||||
RawSocketOption.IPv4MulticastInterface, address.rawAddress);
|
||||
} else {
|
||||
// We'll need a Uint8List(4) for this option, since it will be an 4 byte
|
||||
// word value sent into get/setsockopt.
|
||||
option = RawSocketOption(RawSocketOption.levelIPv6,
|
||||
RawSocketOption.IPv6MulticastInterface, Uint8List(4));
|
||||
}
|
||||
|
||||
socket.setRawOption(option);
|
||||
final getResult = socket.getRawOption(option);
|
||||
|
||||
if (address.type == InternetAddressType.IPv4) {
|
||||
Expect.listEquals(getResult, address.rawAddress);
|
||||
} else {
|
||||
Expect.listEquals(getResult, [0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
asyncSuccess(socket);
|
||||
asyncEnd();
|
||||
}
|
||||
}
|
||||
|
||||
testBroadcast() {
|
||||
test(bindAddress, broadcastAddress, enabled) {
|
||||
asyncStart();
|
||||
|
@ -363,6 +392,7 @@ main() {
|
|||
testDatagramMulticastOptions();
|
||||
testDatagramSocketReuseAddress();
|
||||
testDatagramSocketTtl();
|
||||
testDatagramSocketMulticastIf();
|
||||
testBroadcast();
|
||||
testLoopbackMulticast();
|
||||
testLoopbackMulticastError();
|
||||
|
|
Loading…
Reference in a new issue