GithubHelp home page GithubHelp logo

saltyrtc-client-java's People

Contributors

dbrgn avatar dependabot[bot] avatar lgrahl avatar ovalseven8 avatar rugk avatar threema-danilo avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

saltyrtc-client-java's Issues

Signaling messages after handshake encrypted twice

Signaling messages after the handshake are encrypted twice, because the Signaling.send method passes already encrypted bytes to the Task.sendSignalingMessage method where they're encrypted again.

We can't simply send the bytes through the task channel as-is, because the nonce structure is different. Therefore the client library needs to pass unencrypted message bytes to the task.

Create CryptoProvider interface

Create an interface that proxies all NaCl calls, so that users can use their own favorite implementation.

Depends on #89 (default methods).

Properly handle protocol errors

https://github.com/saltyrtc/saltyrtc-meta/blob/master/Protocol.md#client-to-client-messages

Compared to client-to-server messages, protocol errors for client-to-client message MUST be handled differently. In case that any check fails, the procedure below MUST be followed unless otherwise stated:

  • If the other client is not authenticated yet, an initiator SHALL drop the corresponding responder by sending a 'drop-responder' message with the responder's address in the id field to the server and a close code of 3001 (Protocol Error) in the reason field. A responder SHALL close the connection to the server with a close code of 3001.
  • If the other client is authenticated and the error cause is not a 'send-error' message from the server, the client SHALL send a 'close' message to the other client containing the close code 3001 (Protocol Error). In any case, both clients SHALL terminate the connection to the server (normal close code).

Is this client thread-safe?

I get null pointer exceptions when I connect/disconnect simultaneously:

[ConnectThread] ERROR SaltyRTC.ISignaling - WebSocket callback error: java.lang.NullPointerException
java.lang.NullPointerException
	at org.saltyrtc.client.signaling.Signaling$1.onConnectError(Signaling.java:330)
	at com.neovisionaries.ws.client.ListenerManager.callOnConnectError(ListenerManager.java:206)
	at com.neovisionaries.ws.client.ConnectThread.handleError(ConnectThread.java:46)
	at com.neovisionaries.ws.client.ConnectThread.runMain(ConnectThread.java:36)
	at com.neovisionaries.ws.client.WebSocketThread.run(WebSocketThread.java:45)

It's this code:

Signaling.this.ws.recreate(Signaling.this.wsConnectTimeout).connectAsynchronously();

Following test:

    @Test
    public void testConnectDisconnect() throws Exception {
        // Create peers
        final SSLContext sslContext = SSLContextHelper.getSSLContext();
        final SaltyRTC initiator = new SaltyRTCBuilder(this.cryptoProvider)
            .connectTo(Config.SALTYRTC_HOST, Config.SALTYRTC_PORT, sslContext)
            .withKeyStore(new KeyStore(this.cryptoProvider))
            .usingTasks(new Task[]{ new DummyTask() })
            .asInitiator();
        final SaltyRTC responder = new SaltyRTCBuilder(this.cryptoProvider)
            .connectTo(Config.SALTYRTC_HOST, Config.SALTYRTC_PORT, sslContext)
            .withKeyStore(new KeyStore(this.cryptoProvider))
            .usingTasks(new Task[]{ new DummyTask() })
            .initiatorInfo(initiator.getPublicPermanentKey(), initiator.getAuthToken())
            .asResponder();

        for (int i = 0; i <= 200; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        initiator.connect();
                        initiator.disconnect();
                    } catch (ConnectionException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

Events

Current events:

  • ConnectedEvent
  • ConnectionClosedEvent
  • ConnectionErrorEvent
  • HandoverEvent
  • DataEvent

We should probably replace them with the following:

  • SignalingStateChangedEvent
  • SignalingChannelChangedEvent
  • DataEvent

Handover to data channel

Doesn't seem to work properly yet:

SaltyRTC.RSignaling: Trying to send data message, but connection state is CLOSED

This was probably a message that SaltyRTC was trying to send through the already closed websocket.

NPE on disconnect

11-02 16:22:22.246 31806-31806/org.saltyrtc.demo.app E/AndroidRuntime: FATAL EXCEPTION: main
                                                                       Process: org.saltyrtc.demo.app, PID: 31806
                                                                       java.lang.NullPointerException: Attempt to invoke virtual method 'com.neovisionaries.ws.client.WebSocket com.neovisionaries.ws.client.WebSocket.disconnect(int)' on a null object reference
                                                                           at org.saltyrtc.client.signaling.Signaling.disconnect(Signaling.java:215)
                                                                           at org.saltyrtc.client.signaling.Signaling.disconnect(Signaling.java:232)
                                                                           at org.saltyrtc.client.SaltyRTC.disconnect(SaltyRTC.java:135)
                                                                           at org.saltyrtc.demo.app.MainActivity.stop(MainActivity.java:253)
                                                                           at org.saltyrtc.demo.app.MainActivity$3$1.run(MainActivity.java:171)
                                                                           at android.os.Handler.handleCallback(Handler.java:739)
                                                                           at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                           at android.os.Looper.loop(Looper.java:168)
                                                                           at android.app.ActivityThread.main(ActivityThread.java:5845)
                                                                           at java.lang.reflect.Method.invoke(Native Method)
                                                                           at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
                                                                           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)

Websocket timeout

I've seen this exception more than once:

SaltyRTC.RSigna: A WebSocket connect error occured: Failed to connect to '<domain>:443': failed to connect to <domain>/<ipv6> (port 443) after 2000ms                 
                 com.neovisionaries.ws.client.WebSocketException: Failed to connect to '<domain>:443': failed to connect to <domain>/<ipv6> (port 443) after 2000ms
                     at com.neovisionaries.ws.client.SocketConnector.doConnect(SocketConnector.java:119)
                     at com.neovisionaries.ws.client.SocketConnector.connect(SocketConnector.java:81)
                     at com.neovisionaries.ws.client.WebSocket.connect(WebSocket.java:2031)
                     at com.neovisionaries.ws.client.ConnectThread.run(ConnectThread.java:37)
                  Caused by: java.net.SocketTimeoutException: failed to connect to <domain>/<ipv6> (port 443) after 2000ms
                     at libcore.io.IoBridge.connectErrno(IoBridge.java:169)
                     at libcore.io.IoBridge.connect(IoBridge.java:122)
                     at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:183)
                     at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:456)
                     at java.net.Socket.connect(Socket.java:882)
                     at com.neovisionaries.ws.client.SocketConnector.doConnect(SocketConnector.java:110)
                     at com.neovisionaries.ws.client.SocketConnector.connect(SocketConnector.java:81at com.neovisionaries.ws.client.WebSocket.connect(WebSocket.java:2031at com.neovisionaries.ws.client.ConnectThread.run(ConnectThread.java:37SaltyRTC.RSigna: Could not connect to websocket: Failed to connect to '<domain>:443': failed to connect to <domain>/<ipv6> (port 443) after 2000ms
  • Timeout should be longer than 2000ms by default
  • Timeout should be configurable by the user application
  • Maybe add a retry, or at least pass the failed state up to the user application

Websocket: onBinaryMessage can happen before onConnected

11-16 13:11:04.671 7402-7862/ch.threema.app D/Threema:SessionConnectionContext[0]: Signaling state changed to WS_CONNECTING
11-16 13:11:04.673 7402-7862/ch.threema.app D/Threema:Webclient Session Wakeup: Webclient session started
11-16 13:11:04.942 7402-7886/ch.threema.app D/SaltyRTC.RSignaling: New binary message (81 bytes)
11-16 13:11:04.944 7402-7886/ch.threema.app E/SaltyRTC.RSignaling: Protocol error: Invalid incoming message: Cannot validate message nonce in signaling state WS_CONNECTING
11-16 13:11:04.944 7402-7886/ch.threema.app W/System.err: org.saltyrtc.client.exceptions.ValidationError: Cannot validate message nonce in signaling state WS_CONNECTING
11-16 13:11:04.952 7402-7886/ch.threema.app W/System.err:     at org.saltyrtc.client.signaling.Signaling.validateNonceSource(Signaling.java:951)
11-16 13:11:04.952 7402-7886/ch.threema.app W/System.err:     at org.saltyrtc.client.signaling.Signaling.validateNonce(Signaling.java:900)
11-16 13:11:04.952 7402-7886/ch.threema.app W/System.err:     at org.saltyrtc.client.signaling.Signaling.access$700(Signaling.java:78)
11-16 13:11:04.952 7402-7886/ch.threema.app W/System.err:     at org.saltyrtc.client.signaling.Signaling$2.onBinaryMessage(Signaling.java:380)
11-16 13:11:04.952 7402-7886/ch.threema.app W/System.err:     at com.neovisionaries.ws.client.ListenerManager.callOnBinaryMessage(ListenerManager.java:368)
11-16 13:11:04.952 7402-7886/ch.threema.app W/System.err:     at com.neovisionaries.ws.client.ReadingThread.callOnBinaryMessage(ReadingThread.java:272)
11-16 13:11:04.952 7402-7886/ch.threema.app W/System.err:     at com.neovisionaries.ws.client.ReadingThread.handleBinaryFrame(ReadingThread.java:992)
11-16 13:11:04.952 7402-7886/ch.threema.app W/System.err:     at com.neovisionaries.ws.client.ReadingThread.handleFrame(ReadingThread.java:751)
11-16 13:11:04.952 7402-7886/ch.threema.app W/System.err:     at com.neovisionaries.ws.client.ReadingThread.main(ReadingThread.java:110)
11-16 13:11:04.952 7402-7886/ch.threema.app W/System.err:     at com.neovisionaries.ws.client.ReadingThread.run(ReadingThread.java:66)
11-16 13:11:04.952 7402-7886/ch.threema.app D/Threema:SessionConnectionContext[0]: Signaling state changed to CLOSING

I'm not sure how that can happen, probably a race condition, but let's add a workaround for that.

How to connect clients across NAT?

How to connect clients across NAT?

Client 1 -> Private IP 1 (Initiator)
Client 2 -> Private IP 2 (Receiver)

Also, how is ICE server configured?

ERROR SaltyRTC.RSignaling - No shared sub-protocol could be found

Why does this come? ERROR SaltyRTC.RSignaling - No shared sub-protocol could be found

public static String HOST = "server.saltyrtc.org";
public static int PORT = 9287;
public static String STUN_SERVER = "stun.services.mozilla.com";

final SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
        final SaltyRTC client = new SaltyRTCBuilder()
        .connectTo(HOST, PORT, sslContext)
        .withKeyStore(new KeyStore())
        .withPingInterval(60)
        .withServerKey(TRUSTED_KEY)
        .usingTasks(new Task[] { new PingPongTask() })
        .asInitiator();
        
         final SaltyRTC client2 = new SaltyRTCBuilder()
        .connectTo(HOST, PORT, sslContext)
        .withKeyStore(new KeyStore())
        .withPingInterval(60)
        .initiatorInfo(client.getPublicPermanentKey(), client.getAuthToken())
        .usingTasks(new Task[] { new PingPongTask() })
        .asResponder();
        
       
            try {
                client.connect();
                client2.connect();
            } catch (ConnectionException ex) {
                java.util.logging.Logger.getLogger(RTCConnectApp.class.getName()).log(Level.SEVERE, null, ex);
            }

Refactor signaling class

There's some state checking being done, e.g. if (receiver == SALTYRTC_ADDR_SERVER) .... Replace that with polymorphism based on the new Server class.

Client-to-Client Messages/Behaviour Changes

The 'drop-responder' message has been changed. An initiator MAY now add a reason field to a 'drop-responder' message the server SHALL close the connection to the corresponding responder with. That field contains a close code described here

When to use the reason field has been described here.

The 'close' message has been added.

All WebRTC-related messages have been removed from the spec and will be moved into a separate task soon. I'll use a new branch for that.

Determining of first common task

The initiator SHALL continue by comparing the provided tasks to its own Array of available tasks and MUST choose the first common task from both Arrays.

In the following case, which one is the first common task?

initiator: ['webrtc', 'ortc']
responder: ['ortc', 'webrtc']

I assume it's webrtc, as the initiator is the one that chooses, so it gets the pick.

What about this wording?

The initiator SHALL continue by comparing the provided tasks to its own Array of available tasks. It MUST choose the first task in its own list of available tasks that is also contained in the list of provided tasks.

That would mean that in the example above, webrtc is chosen.

It would also mean that in the following case...

initiator: ['webrtc', 'ortc', 'email']
responder: ['ortc', 'websockets', 'email', 'sms', 'carrier-pigeons', 'webrtc']

...webrtc would be chosen, even though webrtc is the least preferred task of the responder, and even though ortc would look like a good match in this case.

@lgrahl what are your thoughts?

Wrong send-error validation

this.id = ValidationHelper.validateByteArray(map.get("id"), 32, "id");

03-28 21:03:50.410 19412  8296 W System.err: org.saltyrtc.client.exceptions.ValidationError: id must be 32 bytes long, not 8
03-28 21:03:50.419 19412  8296 W System.err: 	at org.saltyrtc.client.helpers.ValidationHelper.validateByteArray(ValidationHelper.java:47)
03-28 21:03:50.419 19412  8296 W System.err: 	at org.saltyrtc.client.messages.s2c.SendError.<init>(SendError.java:31)
03-28 21:03:50.419 19412  8296 W System.err: 	at org.saltyrtc.client.helpers.MessageReader.read(MessageReader.java:98)
03-28 21:03:50.419 19412  8296 W System.err: 	at org.saltyrtc.client.helpers.MessageReader.read(MessageReader.java:45)
03-28 21:03:50.419 19412  8296 W System.err: 	at org.saltyrtc.client.signaling.ResponderSignaling.onPeerHandshakeMessage(ResponderSignaling.java:372)
03-28 21:03:50.419 19412  8296 W System.err: 	at org.saltyrtc.client.signaling.Signaling$1.onBinaryMessage(Signaling.java:383)
03-28 21:03:50.420 19412  8296 W System.err: 	at com.neovisionaries.ws.client.ListenerManager.callOnBinaryMessage(ListenerManager.java:385)
03-28 21:03:50.420 19412  8296 W System.err: 	at com.neovisionaries.ws.client.ReadingThread.callOnBinaryMessage(ReadingThread.java:276)
03-28 21:03:50.420 19412  8296 W System.err: 	at com.neovisionaries.ws.client.ReadingThread.handleBinaryFrame(ReadingThread.java:996)
03-28 21:03:50.420 19412  8296 W System.err: 	at com.neovisionaries.ws.client.ReadingThread.handleFrame(ReadingThread.java:755)
03-28 21:03:50.420 19412  8296 W System.err: 	at com.neovisionaries.ws.client.ReadingThread.main(ReadingThread.java:108)
03-28 21:03:50.420 19412  8296 W System.err: 	at com.neovisionaries.ws.client.ReadingThread.runMain(ReadingThread.java:64)
03-28 21:03:50.420 19412  8296 W System.err: 	at com.neovisionaries.ws.client.WebSocketThread.run(WebSocketThread.java:45)

https://github.com/saltyrtc/saltyrtc-meta/blob/master/Protocol.md#send-error-message

Handshake attempts after 'task' state trigger a protocol error

When a new responder connects, not only is the new-responder message being ignored, but also the responder that is trying to initiate a handshake triggers a protocol error because the initiator cannot find the responder in its map of responders.

Unfortunately, this implementation of the client has not been designed to fully reset its state to be able to do another handshake. The proposed solution is to drop the responder.

Furthermore, a new-initiator message also results in a protocol error after the handshake has been completed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.