1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-03 00:08:46 +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:
Dan Field 2019-01-17 23:05:21 +00:00 committed by commit-bot@chromium.org
parent c701e76506
commit 5b1daaac6c
17 changed files with 498 additions and 14 deletions

View File

@ -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
View File

@ -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",

View File

@ -500,6 +500,14 @@ class RawSynchronousSocket {
}
}
@patch
class RawSocketOption {
@patch
static int _getOptionValue(int key) {
throw UnsupportedError("RawSocketOption._getOptionValue");
}
}
@patch
class SecurityContext {
@patch

View File

@ -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) \

View File

@ -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));

View File

@ -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,

View File

@ -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&,

View File

@ -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&,

View File

@ -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&,

View File

@ -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,

View File

@ -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&,

View File

@ -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")

View File

@ -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);
}

View File

@ -500,6 +500,14 @@ class RawSynchronousSocket {
}
}
@patch
class RawSocketOption {
@patch
static int _getOptionValue(int key) {
throw UnsupportedError("RawSocketOption._getOptionValue");
}
}
@patch
class SecurityContext {
@patch

View File

@ -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) {

View File

@ -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 {

View File

@ -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();