kcp_dial.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // Copyright (C) 2016 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at http://mozilla.org/MPL/2.0/.
  6. package connections
  7. import (
  8. "crypto/tls"
  9. "net/url"
  10. "time"
  11. "github.com/syncthing/syncthing/lib/config"
  12. "github.com/syncthing/syncthing/lib/protocol"
  13. "github.com/xtaci/kcp-go"
  14. "github.com/xtaci/smux"
  15. )
  16. func init() {
  17. factory := &kcpDialerFactory{}
  18. for _, scheme := range []string{"kcp", "kcp4", "kcp6"} {
  19. dialers[scheme] = factory
  20. }
  21. }
  22. type kcpDialer struct {
  23. cfg *config.Wrapper
  24. tlsCfg *tls.Config
  25. }
  26. func (d *kcpDialer) Dial(id protocol.DeviceID, uri *url.URL) (internalConn, error) {
  27. uri = fixupPort(uri, config.DefaultKCPPort)
  28. var conn *kcp.UDPSession
  29. var err error
  30. // Try to dial via an existing listening connection
  31. // giving better changes punching through NAT.
  32. if f := getDialingFilter(); f != nil {
  33. conn, err = kcp.NewConn(uri.Host, nil, 0, 0, f.NewConn(kcpConversationFilterPriority, &kcpConversationFilter{}))
  34. l.Debugf("dial %s using existing conn on %s", uri.String(), conn.LocalAddr())
  35. } else {
  36. conn, err = kcp.DialWithOptions(uri.Host, nil, 0, 0)
  37. }
  38. if err != nil {
  39. l.Debugln(err)
  40. conn.Close()
  41. return internalConn{}, err
  42. }
  43. opts := d.cfg.Options()
  44. conn.SetStreamMode(true)
  45. conn.SetACKNoDelay(false)
  46. conn.SetWindowSize(opts.KCPSendWindowSize, opts.KCPReceiveWindowSize)
  47. conn.SetNoDelay(boolInt(opts.KCPNoDelay), opts.KCPUpdateIntervalMs, boolInt(opts.KCPFastResend), boolInt(!opts.KCPCongestionControl))
  48. ses, err := smux.Client(conn, smuxConfig)
  49. if err != nil {
  50. conn.Close()
  51. return internalConn{}, err
  52. }
  53. ses.SetDeadline(time.Now().Add(10 * time.Second))
  54. stream, err := ses.OpenStream()
  55. if err != nil {
  56. ses.Close()
  57. return internalConn{}, err
  58. }
  59. ses.SetDeadline(time.Time{})
  60. tc := tls.Client(&sessionClosingStream{stream, ses}, d.tlsCfg)
  61. tc.SetDeadline(time.Now().Add(time.Second * 10))
  62. err = tc.Handshake()
  63. if err != nil {
  64. tc.Close()
  65. return internalConn{}, err
  66. }
  67. tc.SetDeadline(time.Time{})
  68. return internalConn{tc, connTypeKCPClient, kcpPriority}, nil
  69. }
  70. func (d *kcpDialer) RedialFrequency() time.Duration {
  71. // For restricted NATs, the UDP mapping will potentially only be open for 20-30 seconds
  72. // hence try dialing just as often.
  73. return time.Duration(d.cfg.Options().StunKeepaliveS) * time.Second
  74. }
  75. type kcpDialerFactory struct{}
  76. func (kcpDialerFactory) New(cfg *config.Wrapper, tlsCfg *tls.Config) genericDialer {
  77. return &kcpDialer{
  78. cfg: cfg,
  79. tlsCfg: tlsCfg,
  80. }
  81. }
  82. func (kcpDialerFactory) Priority() int {
  83. return kcpPriority
  84. }
  85. func (kcpDialerFactory) Enabled(cfg config.Configuration) bool {
  86. return true
  87. }
  88. func (kcpDialerFactory) String() string {
  89. return "KCP Dialer"
  90. }