SYNCTHING-RELAY(7)                 Syncthing                SYNCTHING-RELAY(7)



NNAAMMEE
       syncthing-relay - Relay Protocol v1

WWHHAATT IISS AA RREELLAAYY??
       Relay  is a service which relays data between two _d_e_v_i_c_e_s which are not
       able to connect to each other directly otherwise. This is  usually  due
       to  both devices being behind a NAT and neither side being able to open
       a port which would be directly accessible from the internet.

       A relay was designed to relay  BEP  protocol,  hence  the  reliance  on
       device  IDâs  in  the protocol spec, but at the same time it is general
       enough that could be reused by other protocols or applications, as  the
       data  transferred  between  two devices which use a relay is completely
       obscure and does not affect the relaying.

OOPPEERRAATTIIOONN MMOODDEESS
       Relay listens on a single TCP socket, but has two different  connection
       modes,  where  a  connection mode is a predefined set of messages which
       the relay and the device are expecting to exchange.

       The first mode is the _p_r_o_t_o_c_o_l mode which allows a client  to  interact
       with  the relay, for example join the relay, or request to connect to a
       device, given it is available on the relay. Similarly to BEP,  protocol
       mode  requires  the  device  to connect via TLS using a strong suite of
       ciphers (same as BEP), which allows the relay to verify and derive  the
       identity (Device ID) of the device.

       The  second mode is the _s_e_s_s_i_o_n mode which after a few initial messages
       connects two devices directly to each other via the  relay,  and  is  a
       plain-text  protocol, which for every byte written by one device, sends
       the same set of bytes to the other device and vica versa.

IIDDEENNTTIIFFYYIINNGG TTHHEE CCOONNNNEECCTTIIOONN MMOODDEE
       Because both connection modes operate over the same  single  socket,  a
       method of detecting the connection mode is required.

       When  a  new  client  connects to the relay, the relay checks the first
       byte that the client has sent, and if that matches 0x16,  that  implies
       to  us  that  the connection is a protocol mode connection, due to 0x16
       being the first byte in the TLS handshake, and only protocol mode  con‐
       nections use TLS.

       If  the first byte is not 0x16, then we assume that the connection is a
       session mode connection.

PPRROOTTOOCCOOLL MMOODDEE
       Protocol mode uses TLS and protocol name  defined  by  the  TLS  header
       should be _b_e_p_-_r_e_l_a_y.

       Protocol mode has two submodes: 1. Permanent protocol submode - Joining
       the relay, and waiting for messages from the relay asking to connect to
       some  device which is interested in having a session with you.  2. Tem‐
       porary protocol submode - Only used to request a session with a  device
       which is connected to the relay using the permanent protocol submode.

   PPeerrmmaanneenntt pprroottooccooll ssuubbmmooddee
       A  permanent  protocol submode begins with the client sending a JoinRe‐
       layRequest message, which the relay responds to with either a  Respons‐
       eSuccess  or ResponseAlreadyConnected message if a client with the same
       device ID already exists.

       After the client has joined, no more messages are exchanged apart  from
       Ping/Pong messages for general connection keep alive checking.

       From this point onwards, the client stand-byâs and waits for SessionIn‐
       vitation messages from the relay, which implies that some other  device
       is  trying  to connect with you. SessionInvitation message contains the
       unique session key which then can be used to establish a connection  in
       session mode.

       If the client fails to send a JoinRelayRequest message within the first
       ping interval, the connection is terminated.  If the  client  fails  to
       send a message (even if itâs a ping message) every minute (by default),
       the connection is terminated.

   TTeemmppoorraarryy pprroottooccooll ssuubbmmooddee
       A temporary protocol submode begins  with  ConnectRequest  message,  to
       which the relay responds with either ResponseNotFound if the device the
       client it is after is not available, or with a SessionInvitation, which
       contains  the  unique session key which then can be used to establish a
       connection in session mode.

       The connection is terminated immediately after that.

   EExxaammppllee EExxcchhaannggee
       Client A - Permanent protocol submode Client  B  -  Temporary  protocol
       submode

       center;  |l|l|l|l|.   _  T{  # T}   T{ Client (A) T}   T{ Relay T}   T{
       Client (B) T} _ T{ 1 T}   T{ JoinRelayRequest-> T}   T{ T}   T{ T} _ T{
       2  T}   T{  T}   T{  <-ResponseSuccess T}   T{ T} _ T{ 3 T}   T{ Ping->
       T}   T{ T}   T{ T} _ T{ 4 T}   T{ T}   T{ <-Pong  T}   T{  T}  _  T{  5
       T}   T{  T}   T{  T}   T{ <-ConnectRequest(A) T} _ T{ 6 T}   T{ T}   T{
       SessionInvitation(A)-> T}   T{ T} _ T{ 7 T}   T{ T}   T{ <-SessionInvi‐
       tation(B)  T}   T{ T} _ T{ 8 T}   T{ T}   T{ T}   T{ (Disconnects) T} _
       T{ 9 T}   T{ Ping-> T}   T{ T}   T{ T} _ T{ 10 T}   T{  T}   T{  <-Pong
       T}   T{  T}  _  T{ 11 T}   T{ Ping-> T}   T{ T}   T{ T} _ T{ 12 T}   T{
       T}   T{ <-Pong T}   T{ T} _

SSEESSSSIIOONN MMOODDEE
       The first and only message the client sends in the session mode is  the
       JoinSessionRequest  message  which contains the session key identifying
       which session you are trying to join. The relay responds  with  one  of
       the following Response messages:

       1. ResponseNotFound - Session key is invalid

       2. ResponseAlreadyConnected  - Session is full (both sides already con‐
          nected)

       3. ResponseSuccess - You have successfully joined the session

       After the successful response, all the bytes written and received  will
       be relayed between the two devices in the session directly.

   EExxaammppllee EExxcchhaannggee
       Client A - Permanent protocol mode Client B - Temporary protocol mode

       center;  |l|l|l|l|.   _  T{  # T}   T{ Client (A) T}   T{ Relay T}   T{
       Client (B) T} _ T{ 1 T}   T{ JoinSessionRequest(A)-> T}   T{ T}   T{ T}
       _  T{  2  T}   T{  T}   T{  <-ResponseSuccess T}   T{ T} _ T{ 3 T}   T{
       Data-> T}   T{ (Buffers data) T}   T{ T} _ T{ 4 T}   T{ Data->  T}   T{
       (Buffers data) T}   T{ T} _ T{ 5 T}   T{ T}   T{ T}   T{ <-JoinSession‐
       Request(B) T} _ T{ 6 T}   T{ T}   T{ ResponseSuccess-> T}   T{ T} _  T{
       7  T}   T{  T}   T{  Relays  data  -> T}   T{ T} _ T{ 8 T}   T{ T}   T{
       Relays data -> T}   T{ T} _ T{ 9 T}   T{ T}   T{ <-Relays data  T}   T{
       <-Data T} _

MMEESSSSAAGGEESS
       All  messages are preceded by a header message. Header message contains
       the magic value 0x9E79BC40, message type integer, and message length.

       WWAARRNNIINNGG::
          Some messages have no content, apart from the implied  header  which
          allows us to identify what type of message it is.

   HHeeaaddeerr ssttrruuccttuurree
           0                   1                   2                   3
           0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          |                             Magic                             |
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          |                         Message Type                          |
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          |                        Message Length                         |
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


          struct Header {
                  unsigned int Magic;
                  int MessageType;
                  int MessageLength;
          }

   PPiinngg mmeessssaaggee ((TTyyppee == 00))
           0                   1                   2                   3
           0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


          struct Ping {
          }

   PPoonngg mmeessssaaggee ((TTyyppee == 11))
           0                   1                   2                   3
           0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


          struct Pong {
          }

   JJooiinnRReellaayyRReeqquueesstt mmeessssaaggee ((TTyyppee == 22))
           0                   1                   2                   3
           0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


          struct JoinRelayRequest {
          }

   JJooiinnSSeessssiioonnRReeqquueesstt mmeessssaaggee ((TTyyppee == 33))
           0                   1                   2                   3
           0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          |                         Length of Key                         |
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          /                                                               /
          \                     Key (variable length)                     \
          /                                                               /
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


          struct JoinSessionRequest {
                  opaque Key<32>;
          }

       :: KKeeyy  This  is  a  unique  random  session  key generated by the relay
              server. It is used to identify which session you are  trying  to
              connect to.

   RReessppoonnssee mmeessssaaggee ((TTyyppee == 44))
           0                   1                   2                   3
           0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          |                             Code                              |
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          |                       Length of Message                       |
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          /                                                               /
          \                   Message (variable length)                   \
          /                                                               /
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


          struct Response {
                  int Code;
                  string Message<>;
          }

       :: CCooddee An integer representing the status code.

       :: MMeessssaaggee
              Message associated with the code.

   CCoonnnneeccttRReeqquueesstt mmeessssaaggee ((TTyyppee == 55))
           0                   1                   2                   3
           0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          |                         Length of ID                          |
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          /                                                               /
          \                     ID (variable length)                      \
          /                                                               /
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


          struct ConnectRequest {
                  opaque ID<32>;
          }

       :: IIDD   Device ID to which the client would like to connect.

   SSeessssiioonnIInnvviittaattiioonn mmeessssaaggee ((TTyyppee == 66))
           0                   1                   2                   3
           0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          |                        Length of From                         |
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          /                                                               /
          \                    From (variable length)                     \
          /                                                               /
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          |                         Length of Key                         |
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          /                                                               /
          \                     Key (variable length)                     \
          /                                                               /
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          |                       Length of Address                       |
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          /                                                               /
          \                   Address (variable length)                   \
          /                                                               /
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          |            0x0000             |             Port              |
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          |                  Server Socket (V=0 or 1)                   |V|
          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


          struct SessionInvitation {
                  opaque From<32>;
                  opaque Key<32>;
                  opaque Address<32>;
                  unsigned int Port;
                  bool ServerSocket;
          }

       :: FFrroomm Device ID identifying who you will be connecting with.

       :: KKeeyy  A unique random session key generated by the relay server. It is
              used to identify which session you are trying to connect to.

       :: AAddddrreessss
              An optional IP address on which the relay  server  is  expecting
              you  to connect, in order to start a connection in session mode.
              Empty/all zero IP should be replaced with the relayâs public  IP
              address  that  was used when establishing the protocol mode con‐
              nection.

       :: PPoorrtt The port on which the relay server is expecting you to  connect,
              in order to start a connection in session mode.

       :: SSeerrvveerr SSoocckkeett
              Because  both  sides connecting to the relay use the client side
              of the socket, and some protocols behave  differently  depending
              if  the connection starts on the server side or the client side,
              this boolean indicates which side of the connection this  client
              should assume itâs getting. The value is inverted in the invita‐
              tion which is sent to the other device, so that there is  always
              one client socket, and one server socket.

HHOOWW SSYYNNCCTTHHIINNGG UUSSEESS RREELLAAYYSS,, AANNDD GGEENNEERRAALL SSEECCUURRIITTYY
       In  the  case of Syncthing and BEP, when two devices connect via relay,
       they start  their  standard  TLS  connection  encapsulated  within  the
       relayâs   plain-text  session  connection,  effectively  upgrading  the
       plain-text connection to a TLS connection.

       Even though the relay could be used for man-in-the-middle attack, using
       TLS at the application/BEP level ensures that all the traffic is safely
       encrypted, and is completely meaningless to the relay. Furthermore, the
       secure  suite  of ciphers used by BEP provides forward secrecy, meaning
       that even if the relay did capture all the traffic,  and  even  if  the
       attacker  did  get their hands on the device keys, they would still not
       be able to recover/decrypt any traffic which was  transported  via  the
       relay.

       After establishing a relay session, Syncthing looks at the SessionInvi‐
       tation message, and depending which side it has received, wraps the raw
       socket  in  either a TLS client socket or a TLS server socket depending
       on the ServerSocket boolean value in the SessionInvitation, and  starts
       the TLS handshake.

       From  that  point onwards it functions exactly the same way as if Sync‐
       thing was establishing a direct connection with the other  device  over
       the internet, performing device ID validation, and full TLS encryption,
       and provides the same security properties as it would provide when con‐
       necting over the internet.

EEXXAAMMPPLLEESS OOFF SSTTRROONNGG CCIIPPHHEERR SSUUIITTEESS
       center;  |l|l|l|.   _  T{  ID  T}   T{ Name T}   T{ Description T} _ T{
       0x009F T}   T{ DHE-RSA-AES256-GCM-SHA384 T}   T{ TLSv1.2  DH  RSA  AES‐
       GCM(256)  AEAD  T}  _  T{  0x006B T}   T{ DHE-RSA-AES256-SHA256 T}   T{
       TLSv1.2   DH   RSA   AES(256)   SHA256   T}   _   T{   0xC030   T}   T{
       ECDHE-RSA-AES256-GCM-SHA384  T}   T{  TLSv1.2 ECDH RSA AESGCM(256) AEAD
       T} _ T{ 0xC028 T}   T{ ECDHE-RSA-AES256-SHA384 T}   T{ TLSv1.2 ECDH RSA
       AES(256)  SHA384  T}  _  T{  0x009E  T}   T{  DHE-RSA-AES128-GCM-SHA256
       T}   T{ TLSv1.2  DH  RSA  AESGCM(128)  AEAD  T}  _  T{  0x0067  T}   T{
       DHE-RSA-AES128-SHA256  T}   T{  TLSv1.2  DH RSA AES(128) SHA256 T} _ T{
       0xC02F T}   T{ ECDHE-RSA-AES128-GCM-SHA256  T}   T{  TLSv1.2  ECDH  RSA
       AESGCM(128) AEAD T} _ T{ 0xC027 T}   T{ ECDHE-RSA-AES128-SHA256 T}   T{
       TLSv1.2 ECDH RSA AES(128) SHA256 T} _

AAUUTTHHOORR
       The Syncthing Authors

CCOOPPYYRRIIGGHHTT
       2014-2019, The Syncthing Authors



v1.19.2                          Apr 05, 2022               SYNCTHING-RELAY(7)
