methods.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file).
  2. package client
  3. import (
  4. "context"
  5. "crypto/tls"
  6. "errors"
  7. "fmt"
  8. "net"
  9. "net/url"
  10. "strconv"
  11. "time"
  12. "github.com/syncthing/syncthing/lib/dialer"
  13. "github.com/syncthing/syncthing/lib/osutil"
  14. syncthingprotocol "github.com/syncthing/syncthing/lib/protocol"
  15. "github.com/syncthing/syncthing/lib/relay/protocol"
  16. )
  17. type incorrectResponseCodeErr struct {
  18. code int32
  19. msg string
  20. }
  21. func (e *incorrectResponseCodeErr) Error() string {
  22. return fmt.Sprintf("incorrect response code %d: %s", e.code, e.msg)
  23. }
  24. func GetInvitationFromRelay(ctx context.Context, uri *url.URL, id syncthingprotocol.DeviceID, certs []tls.Certificate, timeout time.Duration) (protocol.SessionInvitation, error) {
  25. if uri.Scheme != "relay" {
  26. return protocol.SessionInvitation{}, fmt.Errorf("unsupported relay scheme: %v", uri.Scheme)
  27. }
  28. ctx, cancel := context.WithTimeout(ctx, timeout)
  29. defer cancel()
  30. rconn, err := dialer.DialContext(ctx, "tcp", uri.Host)
  31. if err != nil {
  32. return protocol.SessionInvitation{}, err
  33. }
  34. conn := tls.Client(rconn, configForCerts(certs))
  35. conn.SetDeadline(time.Now().Add(timeout))
  36. if err := performHandshakeAndValidation(conn, uri); err != nil {
  37. return protocol.SessionInvitation{}, err
  38. }
  39. defer conn.Close()
  40. request := protocol.ConnectRequest{
  41. ID: id[:],
  42. }
  43. if err := protocol.WriteMessage(conn, request); err != nil {
  44. return protocol.SessionInvitation{}, err
  45. }
  46. message, err := protocol.ReadMessage(conn)
  47. if err != nil {
  48. return protocol.SessionInvitation{}, err
  49. }
  50. switch msg := message.(type) {
  51. case protocol.Response:
  52. return protocol.SessionInvitation{}, &incorrectResponseCodeErr{msg.Code, msg.Message}
  53. case protocol.SessionInvitation:
  54. l.Debugln("Received invitation", msg, "via", conn.LocalAddr())
  55. ip := net.IP(msg.Address)
  56. if len(ip) == 0 || ip.IsUnspecified() {
  57. msg.Address, _ = osutil.IPFromAddr(conn.RemoteAddr())
  58. }
  59. return msg, nil
  60. default:
  61. return protocol.SessionInvitation{}, fmt.Errorf("protocol error: unexpected message %v", msg)
  62. }
  63. }
  64. func JoinSession(ctx context.Context, invitation protocol.SessionInvitation) (net.Conn, error) {
  65. addr := net.JoinHostPort(net.IP(invitation.Address).String(), strconv.Itoa(int(invitation.Port)))
  66. ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
  67. defer cancel()
  68. conn, err := dialer.DialContext(ctx, "tcp", addr)
  69. if err != nil {
  70. return nil, err
  71. }
  72. request := protocol.JoinSessionRequest{
  73. Key: invitation.Key,
  74. }
  75. conn.SetDeadline(time.Now().Add(10 * time.Second))
  76. err = protocol.WriteMessage(conn, request)
  77. if err != nil {
  78. return nil, err
  79. }
  80. message, err := protocol.ReadMessage(conn)
  81. if err != nil {
  82. return nil, err
  83. }
  84. conn.SetDeadline(time.Time{})
  85. switch msg := message.(type) {
  86. case protocol.Response:
  87. if msg.Code != 0 {
  88. return nil, fmt.Errorf("incorrect response code %d: %s", msg.Code, msg.Message)
  89. }
  90. return conn, nil
  91. default:
  92. return nil, fmt.Errorf("protocol error: expecting response got %v", msg)
  93. }
  94. }
  95. func TestRelay(ctx context.Context, uri *url.URL, certs []tls.Certificate, sleep, timeout time.Duration, times int) error {
  96. id := syncthingprotocol.NewDeviceID(certs[0].Certificate[0])
  97. c, err := NewClient(uri, certs, timeout)
  98. if err != nil {
  99. return fmt.Errorf("creating client: %w", err)
  100. }
  101. ctx, cancel := context.WithCancel(context.Background())
  102. go c.Serve(ctx)
  103. go func() {
  104. for {
  105. select {
  106. case <-c.Invitations():
  107. case <-ctx.Done():
  108. return
  109. }
  110. }
  111. }()
  112. defer cancel()
  113. for range times {
  114. _, err = GetInvitationFromRelay(ctx, uri, id, certs, timeout)
  115. if err == nil {
  116. return nil
  117. }
  118. incorrectResponseCodeErr := &incorrectResponseCodeErr{}
  119. if errors.As(err, &incorrectResponseCodeErr) {
  120. return fmt.Errorf("getting invitation: %w", err)
  121. }
  122. time.Sleep(sleep)
  123. }
  124. return fmt.Errorf("getting invitation: %w", err) // last of the above errors
  125. }
  126. func configForCerts(certs []tls.Certificate) *tls.Config {
  127. return &tls.Config{
  128. Certificates: certs,
  129. NextProtos: []string{protocol.ProtocolName},
  130. ClientAuth: tls.RequestClientCert,
  131. SessionTicketsDisabled: true,
  132. InsecureSkipVerify: true,
  133. MinVersion: tls.VersionTLS12,
  134. CipherSuites: []uint16{
  135. tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
  136. tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
  137. tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
  138. tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
  139. tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
  140. tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
  141. },
  142. }
  143. }