I’m currently trying to debug a problem in Syncthing Lite regarding TLS handshakes. Hello messages are transmitted fine, but after that, the Syncthing side shows this error:
However, the Android site shows a successful TLS handshake, with the ciphersuite TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256. The Syncthing web gui also shows the Android device as connected. But reading from the socket afterwards causes an EOFException.
Originally I thought this was caused because ALPN isn’t working on the Android side, but looking at the Syncthing code, this doesn’t seem to be required.
Can you tell me how I can get more error information out of Syncthing, or other ways to debug and fix this? Source code for connecting and TLS is in these files:
Does it fail immediately? Things that come to mind is not performing the SSL handshake (you might not have to do it explicitly tho, depends on libraries), but you should not get to the hello part in that case. Other could be timeout, if syncthing doesn’t see a message in some amount of time, it assumes the connection is dead.
You are sending index messages before cluster config, that should cause us to abort, as first message needs to be hello, followed by cluster config, followed by whatever. Also first index message needs to be Index, all others need to be IndexUpdate (for no good reason).
I am afraid the return value of that might be discarded or something like that.
It would be best if you could capture the bytes you send, so that we could feed the same into the protocol package.
I for some reason don’t think it’s related to TLS. You could create a simple Go program that echoes back whatever is sent and then try to send stuff from your library, to validate that it’s not TLS.
Your original two log lines from Syncthing indicate it thinks the connection was closed from the other side during the handshake. Maybe the handshake includes the hello exchange, I don’t remember.
I don’t know. Your hello message looks fine as far as the magic and length, and then I’m not going to parse protobuf in my head any further. There used to be a really low level protocol trace option in Syncthing but I don’t think there is any more. If you add this diff you’ll see all reads and writes above TLS and maybe that makes sense of things:
--- a/lib/connections/structs.go
+++ b/lib/connections/structs.go
@@ -8,6 +8,7 @@ package connections
import (
"crypto/tls"
+ "encoding/hex"
"fmt"
"io"
"net"
@@ -47,6 +48,17 @@ type internalConn struct {
priority int
}
+func (c internalConn) Write(data []byte) (int, error) {
+ n, err := c.Conn.Write(data)
+ fmt.Printf(">>> WROTE %d bytes (err=%v)\n%s\n", n, err, hex.Dump(data[:n]))
+ return n, err
+}
+
+func (c internalConn) Read(data []byte) (int, error) {
+ n, err := c.Conn.Read(data)
+ fmt.Printf("<<< READ %d bytes (err=%v)\n%s\n", n, err, hex.Dump(data[:n]))
+ return n, err
+}
+
type connType int
const (
In a healthy setup you should see something like:
>>> WROTE 65 bytes (err=<nil>)
00000000 2e a7 d9 0b 00 3b 0a 02 73 31 12 09 73 79 6e 63 |.....;..s1..sync|
00000010 74 68 69 6e 67 1a 2a 76 30 2e 31 34 2e 34 34 2d |thing.*v0.14.44-|
00000020 72 63 2e 32 2b 33 33 2d 67 36 33 36 31 34 37 31 |rc.2+33-g6361471|
00000030 63 2d 64 69 72 74 79 2d 76 61 72 62 6c 6f 63 6b |c-dirty-varblock|
00000040 73 |s|
(that's the outgoing hello)
<<< READ 4 bytes (err=<nil>)
00000000 2e a7 d9 0b |....|
(incoming read of magic)
<<< READ 2 bytes (err=<nil>)
00000000 00 19 |..|
(incoming read of length)
<<< READ 25 bytes (err=<nil>)
00000000 0a 02 73 32 12 09 73 79 6e 63 74 68 69 6e 67 1a |..s2..syncthing.|
00000010 08 76 30 2e 31 34 2e 34 33 |.v0.14.43|
(incoming read of hello)
[I6KAH] 11:26:22 INFO: Established secure connection to MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC at 127.0.0.1:49331-127.0.0.1:22002/tcp-client (TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305)
(at this point we've accepted the connection)
If that’s useful to you we can add it as an actual trace option for the future.
One thing I forgot to mention: sometimes connections do work, but this seems random. So when they go through, the logging looks fine. But when they fail, the error seems to happen before your logging patch is invoked. Here is the log:
[UIR6A] DEBUG: close due to reading length: EOF
[UIR6A] INFO: Connection to 3ZTLVYK-G4ZLJ5E-Q7PZ4WW-IUBWAAU-FFBJDRE-ORNBBSY-IOX2FV3-3X2EJAM closed: reading length: EOF
[UIR6A] DEBUG: Listen (BEP/tcp): connect from 172.30.1.26:53159
[UIR6A] INFO: Listen (BEP/tcp): TLS handshake: EOF
[UIR6A] DEBUG: Listen (BEP/tcp): connect from 172.30.1.254:45662
[UIR6A] INFO: Listen (BEP/tcp): TLS handshake: EOF
[UIR6A] DEBUG: Reconnect loop
[UIR6A] DEBUG: Reconnect loop for 3ZTLVYK-G4ZLJ5E-Q7PZ4WW-IUBWAAU-FFBJDRE-ORNBBSY-IOX2FV3-3X2EJAM []
[UIR6A] DEBUG: sleep until next dial 1m0s
[UIR6A] DEBUG: Listen (BEP/tcp): connect from 172.30.1.254:39378
[UIR6A] INFO: Listen (BEP/tcp): TLS handshake: EOF
[UIR6A] DEBUG: Listen (BEP/tcp): connect from 172.30.1.254:45082
[UIR6A] INFO: Listen (BEP/tcp): TLS handshake: EOF
[UIR6A] DEBUG: Listen (BEP/tcp): connect from 172.30.1.254:45162
[UIR6A] INFO: Peer 172.30.1.254:45162 did not negotiate bep/1.0
>>> WROTE 42 bytes (err=<nil>)
00000000 2e a7 d9 0b 00 24 0a 07 4c 47 2d 48 38 31 35 12 |.....$..LG-H815.|
00000010 09 73 79 6e 63 74 68 69 6e 67 1a 0e 76 30 2e 31 |.syncthing..v0.1|
00000020 34 2e 34 33 2d 64 69 72 74 79 |4.43-dirty|
<<< READ 4 bytes (err=<nil>)
00000000 2e a7 d9 0b |....|
<<< READ 2 bytes (err=<nil>)
00000000 00 22 |."|
<<< READ 34 bytes (err=<nil>)
00000000 0a 0b 4c 47 45 20 4e 65 78 75 73 20 35 12 0e 73 |..LGE Nexus 5..s|
00000010 79 6e 63 74 68 69 6e 67 2d 6a 61 76 61 1a 03 30 |yncthing-java..0|
00000020 2e 30 |.0|
[UIR6A] INFO: Established secure connection to 3ZTLVYK-G4ZLJ5E-Q7PZ4WW-IUBWAAU-FFBJDRE-ORNBBSY-IOX2FV3-3X2EJAM at 172.30.1.19:33008-172.30.1.254:45162 (tcp-server) (TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
[UIR6A] INFO: Device 3ZTLVYK-G4ZLJ5E-Q7PZ4WW-IUBWAAU-FFBJDRE-ORNBBSY-IOX2FV3-3X2EJAM client is "syncthing-java 0.0" named "LGE Nexus 5"
>>> WROTE 6 bytes (err=<nil>)
00000000 00 00 00 00 00 00 |......|
[UIR6A] DEBUG: wrote 6 bytes on the wire (2 bytes length, 0 bytes header, 4 bytes message length, 0 bytes message), err=<nil>
<<< READ 2 bytes (err=<nil>)
00000000 00 04 |..|
<<< READ 4 bytes (err=<nil>)
00000000 08 00 10 00 |....|
<<< READ 4 bytes (err=<nil>)
00000000 00 00 00 6c |...l|
<<< READ 108 bytes (err=<nil>)
00000000 0a 6a 0a 04 74 65 73 74 12 04 74 65 73 74 82 01 |.j..test..test..|
00000010 36 0a 20 de 66 ba e1 46 e6 56 9e c3 ef cf 2d 64 |6. .f..F.V....-d|
00000020 50 36 00 0a 50 a4 71 23 a2 d0 86 48 75 f4 5a ef |P6..P.q#...Hu.Z.|
00000030 77 d1 12 30 b0 cc cb b3 a1 ec b1 92 7b 40 f1 bd |w..0........{@..|
00000040 b0 cb ed 96 da ec 02 82 01 22 0a 20 a2 23 e0 12 |.........". .#..|
00000050 2c 65 bb 55 59 37 8f b9 47 b0 8b 6f 3f 2d 24 4e |,e.UY7..G..o?-$N|
00000060 ad f3 7d 7f 7a 74 65 72 67 03 70 6d |..}.zterg.pm|
[UIR6A] DEBUG: read ClusterConfig message
Maybe it would help if you give it a try yourself, here is an apk with some extra logging: syncthing-lite-debug.apk (6.3 MB)
By the way, running that gives me SocketException: Connection closed by remote host.
Edit: Make sure you stop any Syncthing instances on the same host, and use a peer on a different host. Otherwise it might have trouble with the discovery.
c:\Go\src\github.com\Nutomic\a-sync>gradlew tasks --all | grep run
All tasks runnable from root project
tasks - Displays the tasks runnable from root project 'a-sync' (some of the displayed tasks may belong to subprojects).
a-sync-parent:tasks - Displays the tasks runnable from project ':a-sync-parent' (some of the displayed tasks may belong to subprojects).
a-sync-parent:a-sync-bep:tasks - Displays the tasks runnable from project ':a-sync-parent:a-sync-bep'.
a-sync-parent:a-sync-client:tasks - Displays the tasks runnable from project ':a-sync-parent:a-sync-client'.
a-sync-parent:a-sync-core:tasks - Displays the tasks runnable from project ':a-sync-parent:a-sync-core'.
a-sync-parent:a-sync-discovery:tasks - Displays the tasks runnable from project ':a-sync-parent:a-sync-discovery'.
a-sync-parent:a-sync-http-relay:tasks - Displays the tasks runnable from project ':a-sync-parent:a-sync-http-relay'.
a-sync-parent:a-sync-http-relay-server:tasks - Displays the tasks runnable from project ':a-sync-parent:a-sync-http-relay-server'.
a-sync-parent:a-sync-relay:tasks - Displays the tasks runnable from project ':a-sync-parent:a-sync-relay'.
a-sync-parent:a-sync-repository:tasks - Displays the tasks runnable from project ':a-sync-parent:a-sync-repository'.
You seem to be on the wrong/outdated project. The libraries aren’t called “a-sync-…” anymore. For current master of https://github.com/Nutomic/syncthing-java and the same command I get
All tasks runnable from root project
discovery:run - Runs this project as a JVM application
syncthing-java:run - Runs this project as a JVM application
tasks - Displays the tasks runnable from root project 'syncthing-java' (some of the displayed tasks may belong to subprojects).
bep:tasks - Displays the tasks runnable from project ':bep'.
core:tasks - Displays the tasks runnable from project ':core'.
discovery:tasks - Displays the tasks runnable from project ':discovery'.
http-relay:tasks - Displays the tasks runnable from project ':http-relay'.
relay:tasks - Displays the tasks runnable from project ':relay'.
repository:tasks - Displays the tasks runnable from project ':repository'.
syncthing-java:tasks - Displays the tasks runnable from project ':syncthing-java'.
discovery:startScripts - Creates OS specific scripts to run the project as a JVM application.
syncthing-java:startScripts - Creates OS specific scripts to run the project as a JVM application.
c:\Go\bin\src\github.com\Nutomic\a-sync>git show -1 -q
commit 35ce5fd9a416bf5431a8a0c8b8199d302c48ebfc (HEAD -> listen-for-connections, origin/listen-for-connections)
Author: Felix Ableitner <me@nutomic.com>
Date: Thu Dec 21 22:56:42 2017 +0900
WIP: listen for local discovery connections
c:\Go\bin\src\github.com\Nutomic\a-sync>git branch
* listen-for-connections
master
c:\Go\bin\src\github.com\Nutomic\a-sync>git status
On branch listen-for-connections
Your branch is up to date with 'origin/listen-for-connections'.
nothing to commit, working tree clean
c:\Go\bin\src\github.com\Nutomic\a-sync>