shadowsocks.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. // Copyright 2024 The Outline Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package service
  15. import (
  16. "context"
  17. "log/slog"
  18. "net"
  19. "time"
  20. "github.com/Jigsaw-Code/outline-sdk/transport"
  21. onet "github.com/Jigsaw-Code/outline-ss-server/net"
  22. )
  23. const (
  24. // 59 seconds is most common timeout for servers that do not respond to invalid requests
  25. tcpReadTimeout time.Duration = 59 * time.Second
  26. // A UDP NAT timeout of at least 5 minutes is recommended in RFC 4787 Section 4.3.
  27. defaultNatTimeout time.Duration = 5 * time.Minute
  28. )
  29. // ShadowsocksConnMetrics is used to report Shadowsocks related metrics on connections.
  30. type ShadowsocksConnMetrics interface {
  31. AddCipherSearch(accessKeyFound bool, timeToCipher time.Duration)
  32. }
  33. type ServiceMetrics interface {
  34. UDPMetrics
  35. AddOpenTCPConnection(conn net.Conn) TCPConnMetrics
  36. AddCipherSearch(proto string, accessKeyFound bool, timeToCipher time.Duration)
  37. }
  38. type Service interface {
  39. HandleStream(ctx context.Context, conn transport.StreamConn)
  40. HandlePacket(conn net.PacketConn)
  41. }
  42. // Option is a Shadowsocks service constructor option.
  43. type Option func(s *ssService)
  44. type ssService struct {
  45. logger *slog.Logger
  46. metrics ServiceMetrics
  47. ciphers CipherList
  48. natTimeout time.Duration
  49. targetIPValidator onet.TargetIPValidator
  50. replayCache *ReplayCache
  51. streamDialer transport.StreamDialer
  52. sh StreamHandler
  53. packetListener transport.PacketListener
  54. ph PacketHandler
  55. }
  56. // NewShadowsocksService creates a new Shadowsocks service.
  57. func NewShadowsocksService(opts ...Option) (Service, error) {
  58. s := &ssService{}
  59. for _, opt := range opts {
  60. opt(s)
  61. }
  62. // If no NAT timeout is provided via options, use the recommended default.
  63. if s.natTimeout == 0 {
  64. s.natTimeout = defaultNatTimeout
  65. }
  66. // If no logger is provided via options, use a noop logger.
  67. if s.logger == nil {
  68. s.logger = noopLogger()
  69. }
  70. // TODO: Register initial data metrics at zero.
  71. s.sh = NewStreamHandler(
  72. NewShadowsocksStreamAuthenticator(s.ciphers, s.replayCache, &ssConnMetrics{ServiceMetrics: s.metrics, proto: "tcp"}, s.logger),
  73. tcpReadTimeout,
  74. )
  75. if s.streamDialer != nil {
  76. s.sh.SetTargetDialer(s.streamDialer)
  77. }
  78. s.sh.SetLogger(s.logger)
  79. s.ph = NewPacketHandler(s.natTimeout, s.ciphers, s.metrics, &ssConnMetrics{ServiceMetrics: s.metrics, proto: "udp"})
  80. if s.packetListener != nil {
  81. s.ph.SetTargetPacketListener(s.packetListener)
  82. }
  83. s.ph.SetLogger(s.logger)
  84. return s, nil
  85. }
  86. // WithLogger can be used to provide a custom log target. If not provided,
  87. // the service uses a noop logger (i.e., no logging).
  88. func WithLogger(l *slog.Logger) Option {
  89. return func(s *ssService) {
  90. s.logger = l
  91. }
  92. }
  93. // WithCiphers option function.
  94. func WithCiphers(ciphers CipherList) Option {
  95. return func(s *ssService) {
  96. s.ciphers = ciphers
  97. }
  98. }
  99. // WithMetrics option function.
  100. func WithMetrics(metrics ServiceMetrics) Option {
  101. return func(s *ssService) {
  102. s.metrics = metrics
  103. }
  104. }
  105. // WithReplayCache option function.
  106. func WithReplayCache(replayCache *ReplayCache) Option {
  107. return func(s *ssService) {
  108. s.replayCache = replayCache
  109. }
  110. }
  111. // WithNatTimeout option function.
  112. func WithNatTimeout(natTimeout time.Duration) Option {
  113. return func(s *ssService) {
  114. s.natTimeout = natTimeout
  115. }
  116. }
  117. // WithStreamDialer option function.
  118. func WithStreamDialer(dialer transport.StreamDialer) Option {
  119. return func(s *ssService) {
  120. s.streamDialer = dialer
  121. }
  122. }
  123. // WithPacketListener option function.
  124. func WithPacketListener(listener transport.PacketListener) Option {
  125. return func(s *ssService) {
  126. s.packetListener = listener
  127. }
  128. }
  129. // HandleStream handles a Shadowsocks stream-based connection.
  130. func (s *ssService) HandleStream(ctx context.Context, conn transport.StreamConn) {
  131. var connMetrics TCPConnMetrics
  132. if s.metrics != nil {
  133. connMetrics = s.metrics.AddOpenTCPConnection(conn)
  134. }
  135. s.sh.Handle(ctx, conn, connMetrics)
  136. }
  137. // HandlePacket handles a Shadowsocks packet connection.
  138. func (s *ssService) HandlePacket(conn net.PacketConn) {
  139. s.ph.Handle(conn)
  140. }
  141. type ssConnMetrics struct {
  142. ServiceMetrics
  143. proto string
  144. }
  145. var _ ShadowsocksConnMetrics = (*ssConnMetrics)(nil)
  146. func (cm *ssConnMetrics) AddCipherSearch(accessKeyFound bool, timeToCipher time.Duration) {
  147. if cm.ServiceMetrics != nil {
  148. cm.ServiceMetrics.AddCipherSearch(cm.proto, accessKeyFound, timeToCipher)
  149. }
  150. }
  151. // NoOpShadowsocksConnMetrics is a [ShadowsocksConnMetrics] that doesn't do anything. Useful in tests
  152. // or if you don't want to track metrics.
  153. type NoOpShadowsocksConnMetrics struct{}
  154. var _ ShadowsocksConnMetrics = (*NoOpShadowsocksConnMetrics)(nil)
  155. func (m *NoOpShadowsocksConnMetrics) AddCipherSearch(accessKeyFound bool, timeToCipher time.Duration) {
  156. }