vukoye / xmpp_dart

Lightweight XMPP client library written in Dart
Apache License 2.0
82 stars 65 forks source link

Error when connecting to ejabberd. #76

Open mugambikimathi opened 2 years ago

mugambikimathi commented 2 years ago

Error I get [log] D/[ConnectionNegotiatorManager]: Negotiating features [log] ---Xmpp Receiving:--- [log] Idle connection</stream:error></stream:stream>

Tested with desktop clients and TLS works ok

abhay-s-rawat commented 2 years ago

same problem here.... I figured after some research that istlsrequired was false in all XmppWebsocketIo.dart inside connection folder

bool isTlsRequired() {
  return true; //Change this to true in XmppWebsocketIo.dart and XmppWebsocketHtml.dart
}

Starttlsnegotiator was not getting negotiated because of added condition that it will only trigger when tls is enabled which we cannot. There should be parameter that we can pass to XmppWebsocketIo.dart and XmppWebsocketHtml.dart. below is the responsible code for this

void _initSupportedNegotiatorList() {
    var streamManagement = StreamManagementModule.getInstance(_connection);
    streamManagement.reset();
    if (_connection.isTlsRequired()) { //<<<<---------------- This condition is always false because XmppWebsocketIo.dart and XmppWebsocketHtml.dart has tls set to false always.
      supportedNegotiatorList.add(StartTlsNegotiator(_connection)); //priority 1
    }
    supportedNegotiatorList
        .add(SaslAuthenticationFeature(_connection, _accountSettings.password));
    if (streamManagement.isResumeAvailable()) {
      supportedNegotiatorList.add(streamManagement);
    }
    supportedNegotiatorList
        .add(BindingResourceConnectionNegotiator(_connection));
    supportedNegotiatorList
        .add(streamManagement); //doesn't care if success it will be done
    supportedNegotiatorList.add(SessionInitiationNegotiator(_connection));
    // supportedNegotiatorList
    //     .add(ServiceDiscoveryNegotiator.getInstance(_connection));
    supportedNegotiatorList.add(CarbonsNegotiator.getInstance(_connection));
    supportedNegotiatorList.add(MAMNegotiator.getInstance(_connection));
    Log.d(TAG, 'Negotiators list ==> ${supportedNegotiatorList.join("\n")}');
  }
abhay-s-rawat commented 2 years ago

funny part is after fixing it no I have error which says

 [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Bad state: StreamSink is closed
E/flutter ( 9552): #0      _StreamSinkImpl.add (dart:io/io_sink.dart:134:7)
E/flutter ( 9552): #1      _IOSinkImpl.write (dart:io/io_sink.dart:269:5)
E/flutter ( 9552): #2      _Socket.write (dart:io-patch/socket_patch.dart:2184:36)
E/flutter ( 9552): #3      XmppWebSocketIo.write
package:xmpp_stone/…/connection/XmppWebsocketIo.dart:48
E/flutter ( 9552): #4      Connection.write
package:xmpp_stone/src/Connection.dart:368
E/flutter ( 9552): #5      Connection._openStream
package:xmpp_stone/src/Connection.dart:161
E/flutter ( 9552): #6      Connection.startSecureSocket.<anonymous closure>
package:xmpp_stone/src/Connection.dart:423
E/flutter ( 9552): #7      _rootRunUnary (dart:async/zone.dart:1434:47)
E/flutter ( 9552): #8      _CustomZone.runUnary (dart:async/zone.dart:1335:19)
E/flutter ( 9552): <asynchronous suspension>
E/flutter ( 9552):

Now this error is triggered in XmppWebsocketIo.dart and XmppWebsocketHtml.dart

  @override
  void write(Object? message) {
/*     print(
        'here -------> ${(_socket == null).toString()} ${message.toString()}'); */
    _socket!.write(message);
  }
abhay-s-rawat commented 2 years ago
xmpp_stone: 0.4.1-dev.1

is working fine , please revert to that version , do not use ^ sign as its later updates are buggy.

codesxt commented 2 years ago

@abhay-s-rawat Have you identified what change in the code causes this crash? I would like to keep using the last commit in the null safety branch if possible... So far I have identified that the crash happens after the "proceed" message is sent from the ejabberd server. Seems like the error starts when the secure socket is created.

codesxt commented 2 years ago

It seems like SecureSocket closes the previous unsecure Socket, according to SecureSocket source code (line 84 of secure_socket.dart):

/// The given [socket] is closed and may not be used anymore.

This caused the rest of the code to fail because it was referring the old socket which was closed.

So what I did was going to the XmppWebsocketIo.dart and changing the secure method to override the previous socket by the secure one.

@override
  Future<SecureSocket?> secure({
    host,
    SecurityContext? context,
    bool Function(X509Certificate certificate)? onBadCertificate,
    List<String>? supportedProtocols,
  }) async {
    SecureSocket socket = await SecureSocket.secure(
      _socket!,
      onBadCertificate: onBadCertificate,
    );
    // Overwrite previous socket, since calling SecureSocket.secure makes
    // previous socket unusable.
    _socket = socket;
    return socket;
  }

I also hardcoded the value from isTlsRequired() to true as @abhay-s-rawat suggested, but it would be better to make it a parameter for the Connection in the long term.

Could you try this and confirm if it works @mugambikimathi?

moonlitknight commented 2 years ago

I eventually got this working against latest ejabberd with a default configuration

I started with 95ee3f5 because frankly I have no idea which commits are stable and which are broken.

Here are my diffs implementing the above suggesions and fix an issue with extending empty arrays in the nonce.

diff --git a/lib/src/connection/XmppWebsocketHtml.dart b/lib/src/connection/XmppWebsocketHtml.dart
index e2e75ca..ee78328 100644
--- a/lib/src/connection/XmppWebsocketHtml.dart
+++ b/lib/src/connection/XmppWebsocketHtml.dart
@@ -13,7 +12,7 @@ XmppWebSocket createSocket() {

 bool isTlsRequired() {
   // return the `false`, cause for the 'html' socket initially creates as secured
-  return false;
+  return true;
 }

 class XmppWebSocketHtml extends XmppWebSocket {
@@ -25,13 +24,12 @@ class XmppWebSocketHtml extends XmppWebSocket {
   @override
   Future<XmppWebSocket> connect<S>(String host, int port,
       {String Function(String event)? map}) {
-    print('[XmppWebSocketHtml][connect] host: $host, port: $port');
+    var path = "/ws";
+    port = 5443;
+    print('[XmppWebSocketHtml][connect] host: $host, port: $port $path');
     _socket = WebSocketChannel.connect(
-      Uri(
-        scheme: 'wss',
-        host: host,
-        port: port,
-      ),
+      /* Uri(scheme: 'wss', host: "ec2e.chalk.tel", port: 5443, path: "/ws"), */
+      Uri(scheme: 'wss', host: host, port: port, path: "/ws"),
       protocols: ['xmpp'],
     );

diff --git a/lib/src/connection/XmppWebsocketIo.dart b/lib/src/connection/XmppWebsocketIo.dart
index d4176d3..6171eb5 100644
--- a/lib/src/connection/XmppWebsocketIo.dart
+++ b/lib/src/connection/XmppWebsocketIo.dart
@@ -11,7 +11,7 @@ XmppWebSocket createSocket() {
 }

 bool isTlsRequired() {
-  return false;
+  return true;
 }

 class XmppWebSocketIo extends XmppWebSocket {
@@ -23,7 +23,9 @@ class XmppWebSocketIo extends XmppWebSocket {
   @override
   Future<XmppWebSocket> connect<S>(String host, int port,
       {String Function(String event)? map}) async {
+    print("XWS26 Connect $host $port");
     await Socket.connect(host, port).then((Socket socket) {
+      print("XWS connected");
       _socket = socket;

       if (map != null) {
@@ -57,7 +59,24 @@ class XmppWebSocketIo extends XmppWebSocket {
   }

   @override
-  Future<SecureSocket?> secure(
+  Future<SecureSocket?> secure({
+    host,
+    SecurityContext? context,
+    bool Function(X509Certificate certificate)? onBadCertificate,
+    List<String>? supportedProtocols,
+  }) async {
+    SecureSocket socket = await SecureSocket.secure(
+      _socket!,
+      onBadCertificate: onBadCertificate,
+    );
+    // Overwrite previous socket, since calling SecureSocket.secure makes
+    // previous socket unusable.
+    _socket = socket;
+    return socket;
+  }
+
+  @override
+  Future<SecureSocket?> secureOLD(
       {host,
       SecurityContext? context,
       bool Function(X509Certificate certificate)? onBadCertificate,
diff --git a/lib/src/features/sasl/ScramSaslHandler.dart b/lib/src/features/sasl/ScramSaslHandler.dart
index 26c1409..184249f 100644
--- a/lib/src/features/sasl/ScramSaslHandler.dart
+++ b/lib/src/features/sasl/ScramSaslHandler.dart
@@ -60,7 +60,7 @@ class ScramSaslHandler implements AbstractSaslHandler {
   }

   void generateRandomClientNonce() {
-    var bytes = [];
+    var bytes = new List.filled(CLIENT_NONCE_LENGTH, 1, growable: false);
     for (var i = 0; i < CLIENT_NONCE_LENGTH; i++) {
       bytes[i] = Random.secure().nextInt(256);
     }
@@ -175,7 +175,7 @@ class ScramSaslHandler implements AbstractSaslHandler {
     } catch (e) {
       _fireAuthFailed('Invalid key');
     }
-    var clientProof = [];
+    var clientProof = new List.filled(clientKey.length, 1, growable: false);
     for (var i = 0; i < clientKey.length; i++) {
       clientProof[i] = clientKey[i] ^ clientSignature[i];
     }

.