6 Commits

15 changed files with 136 additions and 22 deletions

View File

@@ -18,6 +18,10 @@ if a DNS implementation is given, and supports StartTLS.
To begin, use [melos](https://github.com/invertase/melos) to bootstrap the project: `melos bootstrap`. Then, the example To begin, use [melos](https://github.com/invertase/melos) to bootstrap the project: `melos bootstrap`. Then, the example
can be run with `flutter run` on Linux or Android. can be run with `flutter run` on Linux or Android.
To run the example, make sure that Flutter is correctly set up and working. If you use
the development shell provided by the NixOS Flake, ensure that `ANDROID_HOME` and
`ANDROID_AVD_HOME` are pointing to the correct directories.
## License ## License
See `./LICENSE`. See `./LICENSE`.

View File

@@ -12,3 +12,4 @@ analyzer:
- "**/*.g.dart" - "**/*.g.dart"
- "**/*.freezed.dart" - "**/*.freezed.dart"
- "test/" - "test/"
- "integration_test/"

View File

@@ -16,10 +16,10 @@ dependencies:
version: 0.1.4+1 version: 0.1.4+1
moxxmpp: moxxmpp:
hosted: https://git.polynom.me/api/packages/Moxxy/pub hosted: https://git.polynom.me/api/packages/Moxxy/pub
version: 0.1.1 version: 0.1.2+1
moxxmpp_socket_tcp: moxxmpp_socket_tcp:
hosted: https://git.polynom.me/api/packages/Moxxy/pub hosted: https://git.polynom.me/api/packages/Moxxy/pub
version: 0.1.1 version: 0.1.2+1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@@ -58,9 +58,7 @@
CPATH = "${pkgs.xorg.libX11.dev}/include:${pkgs.xorg.xorgproto}/include"; CPATH = "${pkgs.xorg.libX11.dev}/include:${pkgs.xorg.xorgproto}/include";
LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath [ atk cairo epoxy gdk-pixbuf glib gtk3 harfbuzz pango ]; LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath [ atk cairo epoxy gdk-pixbuf glib gtk3 harfbuzz pango ];
ANDROID_HOME = (toString ./.) + "/.android/sdk";
JAVA_HOME = pinnedJDK; JAVA_HOME = pinnedJDK;
ANDROID_AVD_HOME = (toString ./.) + "/.android/avd";
}; };
}); });
} }

View File

@@ -1,3 +1,11 @@
## 0.1.2+1
- **FIX**: A certificate rejection does not crash the connection.
## 0.1.2
- **FEAT**: Remove Moxxy specific strings.
## 0.1.1 ## 0.1.1
- **REFACTOR**: Move packages into packages/. - **REFACTOR**: Move packages into packages/.

View File

@@ -1 +1 @@
include: ../analysis_options.yaml include: ../../analysis_options.yaml

View File

@@ -796,6 +796,15 @@ class XmppConnection {
_updateRoutingState(RoutingState.handleStanzas); _updateRoutingState(RoutingState.handleStanzas);
await _onNegotiationsDone(); await _onNegotiationsDone();
} else if (_currentNegotiator!.state == NegotiatorState.error) {
_log.severe('Negotiator returned an error');
_updateRoutingState(RoutingState.error);
await _setConnectionState(XmppConnectionState.error);
_connectionCompleter?.complete(const XmppConnectionResult(false));
_connectionCompleter = null;
_closeSocket();
} }
} }

View File

@@ -1,6 +1,6 @@
name: moxxmpp name: moxxmpp
description: A pure-Dart XMPP library description: A pure-Dart XMPP library
version: 0.1.1 version: 0.1.2+1
homepage: https://codeberg.org/moxxy/moxxmpp homepage: https://codeberg.org/moxxy/moxxmpp
publish_to: https://git.polynom.me/api/packages/Moxxy/pub publish_to: https://git.polynom.me/api/packages/Moxxy/pub

View File

@@ -1,3 +1,11 @@
## 0.1.2+1
- **FIX**: A certificate rejection does not crash the connection.
## 0.1.2
- **FEAT**: Make onBadCertificate available.
## 0.1.1 ## 0.1.1
- **REFACTOR**: Move packages into packages/. - **REFACTOR**: Move packages into packages/.

View File

@@ -1,4 +1,4 @@
include: ../analysis_options.yaml include: ../../analysis_options.yaml
analyzer: analyzer:
exclude: exclude:

View File

@@ -0,0 +1,64 @@
import 'package:logging/logging.dart';
import 'package:moxxmpp/moxxmpp.dart';
import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart';
import 'package:test/test.dart';
Future<void> _runTest(String domain) async {
var gotTLSException = false;
final socket = TCPSocketWrapper(false);
final log = Logger('TestLogger');
socket.getEventStream().listen((event) {
if (event is XmppSocketTLSFailedEvent) {
log.info('Got XmppSocketTLSFailedEvent from socket');
gotTLSException = true;
}
});
final connection = XmppConnection(
ExponentialBackoffReconnectionPolicy(),
socket,
);
connection.registerFeatureNegotiators([
StartTlsNegotiator(),
]);
connection.registerManagers([
DiscoManager(),
RosterManager(),
PingManager(),
MessageManager(),
PresenceManager('http://moxxmpp.example'),
]);
connection.setConnectionSettings(
ConnectionSettings(
jid: JID.fromString('testuser@$domain'),
password: 'abc123',
useDirectTLS: true,
allowPlainAuth: true,
),
);
final result = await connection.connectAwaitable();
expect(result.success, false);
expect(gotTLSException, true);
}
void main() {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});
for (final domain in [
'self-signed.badxmpp.eu',
'expired.badxmpp.eu',
'wrong-name.badxmpp.eu',
'missing-chain.badxmpp.eu',
// TODO(Unknown): Technically, this one should not fail
//'ecdsa.badxmpp.eu',
]) {
test('$domain with connectAwaitable', () async {
await _runTest(domain);
});
}
}

View File

@@ -1,4 +1,5 @@
library moxxmpp_socket_tcp; library moxxmpp_socket_tcp;
export 'src/events.dart';
export 'src/record.dart'; export 'src/record.dart';
export 'src/socket.dart'; export 'src/socket.dart';

View File

@@ -0,0 +1,4 @@
import 'package:moxxmpp/moxxmpp.dart';
/// Triggered when TLS errors occur
class XmppSocketTLSFailedEvent extends XmppSocketEvent {}

View File

@@ -4,6 +4,7 @@ import 'dart:io';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:moxxmpp/moxxmpp.dart'; import 'package:moxxmpp/moxxmpp.dart';
import 'package:moxxmpp_socket_tcp/src/events.dart';
import 'package:moxxmpp_socket_tcp/src/record.dart'; import 'package:moxxmpp_socket_tcp/src/record.dart';
import 'package:moxxmpp_socket_tcp/src/rfc_2782.dart'; import 'package:moxxmpp_socket_tcp/src/rfc_2782.dart';
@@ -50,11 +51,12 @@ class TCPSocketWrapper extends BaseSocketWrapper {
return <MoxSrvRecord>[]; return <MoxSrvRecord>[];
} }
bool _onBadCertificate(dynamic certificate, String domain) { /// Called when we encounter a certificate we cannot verify. [certificate] refers to the certificate
_log.fine('Bad certificate: ${certificate.toString()}'); /// in question, while [domain] refers to the domain we try to validate the certificate against.
//final isExpired = certificate.endValidity.isAfter(DateTime.now()); ///
// TODO(Unknown): Either validate the certificate ourselves or use a platform native /// Return true if the certificate should be accepted. Return false if it should be rejected.
// hostname verifier (or Dart adds it themselves) @visibleForOverriding
bool onBadCertificate(dynamic certificate, String domain) {
return false; return false;
} }
@@ -65,6 +67,7 @@ class TCPSocketWrapper extends BaseSocketWrapper {
return false; return false;
} }
var failedDueToTLS = false;
results.sort(srvRecordSortComparator); results.sort(srvRecordSortComparator);
for (final srv in results) { for (final srv in results) {
try { try {
@@ -83,19 +86,27 @@ class TCPSocketWrapper extends BaseSocketWrapper {
sock, sock,
host: domain, host: domain,
supportedProtocols: const [ xmppClientALPNId ], supportedProtocols: const [ xmppClientALPNId ],
onBadCertificate: (cert) => _onBadCertificate(cert, domain), onBadCertificate: (cert) => onBadCertificate(cert, domain),
); );
_ignoreSocketClosure = false; _ignoreSocketClosure = false;
_secure = true; _secure = true;
_log.finest('Success!'); _log.finest('Success!');
return true; return true;
} on SocketException catch(e) { } on Exception catch(e) {
_log.finest('Failure! $e'); _log.finest('Failure! $e');
_ignoreSocketClosure = false; _ignoreSocketClosure = false;
if (e is HandshakeException) {
failedDueToTLS = true;
}
} }
} }
if (failedDueToTLS) {
_eventStream.add(XmppSocketTLSFailedEvent());
}
return false; return false;
} }
@@ -117,7 +128,7 @@ class TCPSocketWrapper extends BaseSocketWrapper {
_ignoreSocketClosure = false; _ignoreSocketClosure = false;
_log.finest('Success!'); _log.finest('Success!');
return true; return true;
} on SocketException catch(e) { } on Exception catch(e) {
_log.finest('Failure! $e'); _log.finest('Failure! $e');
_ignoreSocketClosure = false; _ignoreSocketClosure = false;
continue; continue;
@@ -141,7 +152,7 @@ class TCPSocketWrapper extends BaseSocketWrapper {
); );
_log.finest('Success!'); _log.finest('Success!');
return true; return true;
} on SocketException catch(e) { } on Exception catch(e) {
_log.finest('Failure! $e'); _log.finest('Failure! $e');
_ignoreSocketClosure = false; _ignoreSocketClosure = false;
return false; return false;
@@ -175,15 +186,21 @@ class TCPSocketWrapper extends BaseSocketWrapper {
_socket = await SecureSocket.secure( _socket = await SecureSocket.secure(
_socket!, _socket!,
supportedProtocols: const [ xmppClientALPNId ], supportedProtocols: const [ xmppClientALPNId ],
onBadCertificate: (cert) => _onBadCertificate(cert, domain), onBadCertificate: (cert) => onBadCertificate(cert, domain),
); );
_secure = true; _secure = true;
_ignoreSocketClosure = false; _ignoreSocketClosure = false;
_setupStreams(); _setupStreams();
return true; return true;
} on SocketException { } on Exception catch (e) {
_log.severe('Failed to secure socket: $e');
_ignoreSocketClosure = false; _ignoreSocketClosure = false;
if (e is HandshakeException) {
_eventStream.add(XmppSocketTLSFailedEvent());
}
return false; return false;
} }
} }
@@ -292,7 +309,7 @@ class TCPSocketWrapper extends BaseSocketWrapper {
try { try {
_socket!.write(data); _socket!.write(data);
} on SocketException catch (e) { } on Exception catch (e) {
_log.severe(e); _log.severe(e);
_eventStream.add(XmppSocketErrorEvent(e)); _eventStream.add(XmppSocketErrorEvent(e));
} }

View File

@@ -1,6 +1,6 @@
name: moxxmpp_socket_tcp name: moxxmpp_socket_tcp
description: A socket for moxxmpp using TCP that implements the RFC6120 connection algorithm and XEP-0368 description: A socket for moxxmpp using TCP that implements the RFC6120 connection algorithm and XEP-0368
version: 0.1.1 version: 0.1.2+1
homepage: https://codeberg.org/moxxy/moxxmpp homepage: https://codeberg.org/moxxy/moxxmpp
publish_to: https://git.polynom.me/api/packages/Moxxy/pub publish_to: https://git.polynom.me/api/packages/Moxxy/pub
@@ -12,7 +12,7 @@ dependencies:
meta: ^1.6.0 meta: ^1.6.0
moxxmpp: moxxmpp:
hosted: https://git.polynom.me/api/packages/Moxxy/pub hosted: https://git.polynom.me/api/packages/Moxxy/pub
version: ^0.1.1 version: ^0.1.2+1
dev_dependencies: dev_dependencies:
lints: ^2.0.0 lints: ^2.0.0