methods.go 4.4 KB

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