Release IKAR 1.0.7: port navigation and sharing improvements, harden calls
Tests / test (push) Canceled after 0s
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Native crypto / native-crypto (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s
Tests / test (push) Canceled after 0s
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Native crypto / native-crypto (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s
This commit is contained in:
@@ -130,6 +130,21 @@ Future<void> main(List<String> args) async {
|
||||
'Nonmember could read chat: ${forbidden['payload']}',
|
||||
);
|
||||
checks.add('persistent history and nonmember isolation');
|
||||
final lookup = await b.ok(71, {
|
||||
'chatId': chatId,
|
||||
'messageId': message['id'],
|
||||
});
|
||||
check(
|
||||
lookup['message']['id'] == message['id'] &&
|
||||
lookup['message']['text'] == 'IKAR live delivery',
|
||||
'Message lookup failed',
|
||||
);
|
||||
final privateLookup = await outsider.transport.request(71, {
|
||||
'chatId': chatId,
|
||||
'messageId': message['id'],
|
||||
});
|
||||
check(privateLookup['cmd'] == 3, 'Nonmember could look up a message');
|
||||
checks.add('reply lookup and nonmember isolation');
|
||||
final retryCid = DateTime.now().microsecondsSinceEpoch;
|
||||
final richPayload = {
|
||||
'chatId': chatId,
|
||||
|
||||
+57
-23
@@ -4,7 +4,7 @@ import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import '../lib/core/transport/ikar_network.dart';
|
||||
import 'package:ikar/core/transport/ikar_network.dart';
|
||||
import 'live_ikar_smoke.dart' as live;
|
||||
|
||||
List<int> attr(int type, List<int> value) {
|
||||
@@ -36,7 +36,7 @@ class Frame {
|
||||
}
|
||||
|
||||
class Turn {
|
||||
Turn(this.socket) {
|
||||
Turn(Socket socket) : write = socket.add, stop = socket.destroy {
|
||||
socket.listen(
|
||||
(chunk) {
|
||||
buffer.addAll(chunk);
|
||||
@@ -51,7 +51,26 @@ class Turn {
|
||||
onDone: frames.close,
|
||||
);
|
||||
}
|
||||
final Socket socket;
|
||||
Turn.datagram(RawDatagramSocket socket, InternetAddress address)
|
||||
: write = ((bytes) => socket.send(bytes, address, 3478)),
|
||||
stop = socket.close {
|
||||
socket.listen(
|
||||
(event) {
|
||||
if (event != RawSocketEvent.read) return;
|
||||
Datagram? packet;
|
||||
while ((packet = socket.receive()) != null) {
|
||||
if (packet!.address.address == address.address &&
|
||||
packet.port == 3478) {
|
||||
frames.add(Frame(packet.data));
|
||||
}
|
||||
}
|
||||
},
|
||||
onError: frames.addError,
|
||||
onDone: frames.close,
|
||||
);
|
||||
}
|
||||
final void Function(List<int>) write;
|
||||
final void Function() stop;
|
||||
final frames = StreamController<Frame>.broadcast();
|
||||
final buffer = <int>[];
|
||||
late String username, password, realm, nonce;
|
||||
@@ -63,7 +82,9 @@ class Turn {
|
||||
..setUint32(4, 0x2112a442);
|
||||
final bytes = header.buffer.asUint8List();
|
||||
final random = Random.secure();
|
||||
for (var i = 8; i < 20; i++) bytes[i] = random.nextInt(256);
|
||||
for (var i = 8; i < 20; i++) {
|
||||
bytes[i] = random.nextInt(256);
|
||||
}
|
||||
final prefix = [...bytes, ...body];
|
||||
if (!auth) return Uint8List.fromList(prefix);
|
||||
final key = md5.convert(utf8.encode('$username:$realm:$password')).bytes;
|
||||
@@ -84,7 +105,7 @@ class Turn {
|
||||
final response = frames.stream
|
||||
.firstWhere((f) => f.transaction == transaction)
|
||||
.timeout(const Duration(seconds: 10));
|
||||
socket.add(wire);
|
||||
write(wire);
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -94,7 +115,9 @@ class Turn {
|
||||
..setUint16(2, address.$2 ^ 0x2112);
|
||||
final ip = InternetAddress(address.$1).rawAddress;
|
||||
final magic = [0x21, 0x12, 0xa4, 0x42];
|
||||
for (var i = 0; i < 4; i++) value.setUint8(4 + i, ip[i] ^ magic[i]);
|
||||
for (var i = 0; i < 4; i++) {
|
||||
value.setUint8(4 + i, ip[i] ^ magic[i]);
|
||||
}
|
||||
return attr(0x12, value.buffer.asUint8List());
|
||||
}
|
||||
|
||||
@@ -130,22 +153,24 @@ class Turn {
|
||||
);
|
||||
}
|
||||
|
||||
void send((String, int) address, String message) => socket.add(
|
||||
void send((String, int) address, String message) => write(
|
||||
encode(0x16, [...peer(address), ...attr(0x13, utf8.encode(message))]),
|
||||
);
|
||||
Future<String> receive() => frames.stream
|
||||
.firstWhere((f) => f.type == 0x17)
|
||||
.timeout(const Duration(seconds: 10))
|
||||
.then((f) => utf8.decode(f.attributes[0x13]!));
|
||||
void close() => socket.destroy();
|
||||
void close() => stop();
|
||||
}
|
||||
|
||||
Future<void> main(List<String> args) async {
|
||||
final udp = args.contains('--udp');
|
||||
final hosts = args.where((arg) => !arg.startsWith('--')).toList();
|
||||
final devices = <live.Device>[];
|
||||
final relays = <Turn>[];
|
||||
WebSocket? signaling;
|
||||
try {
|
||||
for (var i = 0; i < 2; i++)
|
||||
for (var i = 0; i < 2; i++) {
|
||||
devices.add(
|
||||
await live.Device.create(
|
||||
Uri.parse('https://ikar.kusoft.xyz'),
|
||||
@@ -154,6 +179,7 @@ Future<void> main(List<String> args) async {
|
||||
'IKAR TURN test',
|
||||
),
|
||||
);
|
||||
}
|
||||
final call = await devices[0].ok(78, {
|
||||
'calleeIds': [devices[1].id],
|
||||
'isVideo': false,
|
||||
@@ -164,18 +190,26 @@ Future<void> main(List<String> args) async {
|
||||
);
|
||||
final hello = jsonDecode(await signaling.first as String);
|
||||
final turn = hello['conversationParams']['turn'];
|
||||
final address = args.isNotEmpty
|
||||
? args.first
|
||||
final address = hosts.isNotEmpty
|
||||
? hosts.first
|
||||
: RegExp(
|
||||
r'^turn:([^:?/]+)',
|
||||
).firstMatch((turn['urls'] as List).first)!.group(1)!;
|
||||
for (var i = 0; i < 2; i++) {
|
||||
final relay = Turn(
|
||||
await Socket.connect(
|
||||
address,
|
||||
3478,
|
||||
).timeout(const Duration(seconds: 10)),
|
||||
);
|
||||
final relay = udp
|
||||
? Turn.datagram(
|
||||
await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0),
|
||||
(await InternetAddress.lookup(
|
||||
address,
|
||||
type: InternetAddressType.IPv4,
|
||||
)).first,
|
||||
)
|
||||
: Turn(
|
||||
await Socket.connect(
|
||||
address,
|
||||
3478,
|
||||
).timeout(const Duration(seconds: 10)),
|
||||
);
|
||||
relays.add(relay);
|
||||
await relay.allocate(turn['username'], turn['credential']);
|
||||
}
|
||||
@@ -196,22 +230,22 @@ Future<void> main(List<String> args) async {
|
||||
);
|
||||
final report = {
|
||||
'passed': true,
|
||||
'transport': 'TCP',
|
||||
'transport': udp ? 'UDP' : 'TCP',
|
||||
'serverAddress': address,
|
||||
'externalAccess': args.isEmpty,
|
||||
'externalAccess': hosts.isEmpty,
|
||||
'relayPorts': [a.relay.$2, b.relay.$2],
|
||||
'bidirectionalData': true,
|
||||
'client': 'Windows',
|
||||
'timestamp': DateTime.now().toUtc().toIso8601String(),
|
||||
};
|
||||
await File(
|
||||
args.isEmpty
|
||||
? 'artifacts/public-turn-verification.json'
|
||||
: 'artifacts/local-turn-verification.json',
|
||||
'artifacts/${hosts.isEmpty ? 'public' : 'local'}-turn-verification${udp ? '-udp' : ''}.json',
|
||||
).writeAsString(const JsonEncoder.withIndent(' ').convert(report));
|
||||
stdout.writeln(jsonEncode(report));
|
||||
} finally {
|
||||
for (final relay in relays) relay.close();
|
||||
for (final relay in relays) {
|
||||
relay.close();
|
||||
}
|
||||
await signaling?.close();
|
||||
for (final d in devices) {
|
||||
await d.ok(199, {});
|
||||
|
||||
Reference in New Issue
Block a user