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



NNAAMMEE
       syncthing-relay - Relay Protocol v1

WWHHAATT IISS AA RREELLAAYY??
       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.

OOPPEERRAATTIIOONN MMOODDEESS
       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.

IIDDEENNTTIIFFYYIINNGG TTHHEE CCOONNNNEECCTTIIOONN MMOODDEE
       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.

PPRROOTTOOCCOOLL MMOODDEE
       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.

   PPeerrmmaanneenntt pprroottooccooll ssuubbmmooddee
       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.

   TTeemmppoorraarryy pprroottooccooll ssuubbmmooddee
       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.

   EExxaammppllee EExxcchhaannggee
       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} _

SSEESSSSIIOONN MMOODDEE
       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.

   EExxaammppllee EExxcchhaannggee
       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} _

MMEESSSSAAGGEESS
       All  messages are preceded by a header message. Header message contains
       the magic value 0x9E79BC40, message type integer, and message length.

       WWAARRNNIINNGG::
          Some messages have no content, apart from the implied  header  which
          allows us to identify what type of message it is.

   HHeeaaddeerr ssttrruuccttuurree
           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;
          }

   PPiinngg mmeessssaaggee ((TTyyppee == 00))
           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 {
          }

   PPoonngg mmeessssaaggee ((TTyyppee == 11))
           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 {
          }

   JJooiinnRReellaayyRReeqquueesstt mmeessssaaggee ((TTyyppee == 22))
           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 {
          }

   JJooiinnSSeessssiioonnRReeqquueesstt mmeessssaaggee ((TTyyppee == 33))
           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>;
          }

       :: KKeeyy  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.

   RReessppoonnssee mmeessssaaggee ((TTyyppee == 44))
           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<>;
          }

       :: CCooddee An integer representing the status code.

       :: MMeessssaaggee
              Message associated with the code.

   CCoonnnneeccttRReeqquueesstt mmeessssaaggee ((TTyyppee == 55))
           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>;
          }

       :: IIDD   Device ID to which the client would like to connect.

   SSeessssiioonnIInnvviittaattiioonn mmeessssaaggee ((TTyyppee == 66))
           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;
          }

       :: FFrroomm Device ID identifying who you will be connecting with.

       :: KKeeyy  A unique random session key generated by the relay server. It is
              used to identify which session you are trying to connect to.

       :: AAddddrreessss
              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.

       :: PPoorrtt The port on which the relay server is expecting you to  connect,
              in order to start a connection in session mode.

       :: SSeerrvveerr SSoocckkeett
              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.

HHOOWW SSYYNNCCTTHHIINNGG UUSSEESS RREELLAAYYSS,, AANNDD GGEENNEERRAALL SSEECCUURRIITTYY
       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.

EEXXAAMMPPLLEESS OOFF SSTTRROONNGG CCIIPPHHEERR SSUUIITTEESS
       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} _

AAUUTTHHOORR
       The Syncthing Authors

CCOOPPYYRRIIGGHHTT
       2014-2019, The Syncthing Authors



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