limitProtocols_test.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*
  2. * Copyright (c) 2018, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package psiphon
  20. import (
  21. "context"
  22. "fmt"
  23. "io/ioutil"
  24. "os"
  25. "sync"
  26. "testing"
  27. "time"
  28. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  29. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
  30. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
  31. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/server"
  32. )
  33. func TestLimitTunnelProtocols(t *testing.T) {
  34. testDataDirName, err := ioutil.TempDir("", "psiphon-limit-tunnel-protocols-test")
  35. if err != nil {
  36. t.Fatalf("TempDir failed: %s", err)
  37. }
  38. defer os.RemoveAll(testDataDirName)
  39. initialLimitTunnelProtocols := protocol.TunnelProtocols{"OSSH", "UNFRONTED-MEEK-HTTPS-OSSH"}
  40. initialLimitTunnelProtocolsCandidateCount := 100
  41. limitTunnelProtocols := protocol.TunnelProtocols{"SSH", "UNFRONTED-MEEK-OSSH"}
  42. initialConnectingCount := 0
  43. connectingCount := 0
  44. SetNoticeWriter(NewNoticeReceiver(
  45. func(notice []byte) {
  46. noticeType, payload, err := GetNotice(notice)
  47. if err != nil {
  48. return
  49. }
  50. if noticeType == "ConnectingServer" {
  51. connectingCount += 1
  52. protocolField := payload["protocol"]
  53. protocol := protocolField.(string)
  54. if common.Contains(initialLimitTunnelProtocols, protocol) {
  55. initialConnectingCount += 1
  56. }
  57. if common.Contains(limitTunnelProtocols, protocol) {
  58. connectingCount += 1
  59. }
  60. // At the end of the InitialLimit phase, the order of
  61. // ConnectingServer notices isn't strictly synchronized and
  62. // it's possible for a Limit candidate ConnectingServer notice
  63. // to arrive before the last InitialLimit notice. So strict
  64. // checking of notice order is performed only up to 90% of
  65. // InitialLimitTunnelProtocolsCandidateCount.
  66. if initialConnectingCount <= (initialLimitTunnelProtocolsCandidateCount*9)/10 {
  67. var expectedProtocols []string
  68. if connectingCount <= initialLimitTunnelProtocolsCandidateCount {
  69. expectedProtocols = initialLimitTunnelProtocols
  70. } else {
  71. expectedProtocols = limitTunnelProtocols
  72. }
  73. if !common.Contains(expectedProtocols, protocol) {
  74. t.Fatalf("unexpected protocol: %s (%d %+v)", protocol, connectingCount, expectedProtocols)
  75. }
  76. }
  77. }
  78. }))
  79. clientConfigJSON := `
  80. {
  81. "ClientPlatform" : "Windows",
  82. "ClientVersion" : "0",
  83. "SponsorId" : "0",
  84. "PropagationChannelId" : "0",
  85. "DisableRemoteServerListFetcher" : true
  86. }`
  87. clientConfig, err := LoadConfig([]byte(clientConfigJSON))
  88. if err != nil {
  89. t.Fatalf("error processing configuration file: %s", err)
  90. }
  91. clientConfig.DataRootDirectory = testDataDirName
  92. err = clientConfig.Commit(false)
  93. if err != nil {
  94. t.Fatalf("error committing configuration file: %s", err)
  95. }
  96. applyParameters := make(map[string]interface{})
  97. applyParameters[parameters.ConnectionWorkerPoolSize] = initialLimitTunnelProtocolsCandidateCount / 2
  98. applyParameters[parameters.LimitIntensiveConnectionWorkers] = initialLimitTunnelProtocolsCandidateCount / 4
  99. applyParameters[parameters.TunnelConnectTimeout] = "1s"
  100. applyParameters[parameters.EstablishTunnelPausePeriod] = "1s"
  101. applyParameters[parameters.InitialLimitTunnelProtocols] = initialLimitTunnelProtocols
  102. applyParameters[parameters.InitialLimitTunnelProtocolsCandidateCount] = initialLimitTunnelProtocolsCandidateCount
  103. applyParameters[parameters.LimitTunnelProtocols] = limitTunnelProtocols
  104. err = clientConfig.SetParameters("", true, applyParameters)
  105. if err != nil {
  106. t.Fatalf("error setting client parameters: %s", err)
  107. }
  108. err = OpenDataStore(clientConfig)
  109. if err != nil {
  110. t.Fatalf("error initializing client datastore: %s", err)
  111. }
  112. defer CloseDataStore()
  113. if CountServerEntries() > 0 {
  114. t.Fatalf("unexpected server entries")
  115. }
  116. serverEntries := make([]map[string]interface{}, len(protocol.SupportedTunnelProtocols))
  117. for i, tunnelProtocol := range protocol.SupportedTunnelProtocols {
  118. _, _, _, _, encodedServerEntry, err := server.GenerateConfig(
  119. &server.GenerateConfigParams{
  120. ServerIPAddress: fmt.Sprintf("0.1.0.0"),
  121. EnableSSHAPIRequests: true,
  122. WebServerPort: 8000,
  123. TunnelProtocolPorts: map[string]int{tunnelProtocol: 4000},
  124. })
  125. if err != nil {
  126. t.Fatalf("error generating server config: %s", err)
  127. }
  128. serverEntryFields, err := protocol.DecodeServerEntryFields(
  129. string(encodedServerEntry),
  130. common.GetCurrentTimestamp(),
  131. protocol.SERVER_ENTRY_SOURCE_REMOTE)
  132. if err != nil {
  133. t.Fatalf("error decoding server entry: %s", err)
  134. }
  135. serverEntries[i] = serverEntryFields
  136. }
  137. for i := 0; i < 1000; i++ {
  138. serverEntryFields := serverEntries[i%len(protocol.SupportedTunnelProtocols)]
  139. serverEntryFields["ipAddress"] = fmt.Sprintf("0.1.%d.%d", (i>>8)&0xFF, i&0xFF)
  140. err = StoreServerEntry(serverEntryFields, true)
  141. if err != nil {
  142. t.Fatalf("error storing server entry: %s", err)
  143. }
  144. }
  145. controller, err := NewController(clientConfig)
  146. if err != nil {
  147. t.Fatalf("error creating client controller: %s", err)
  148. }
  149. ctx, cancelFunc := context.WithCancel(context.Background())
  150. controllerWaitGroup := new(sync.WaitGroup)
  151. controllerWaitGroup.Add(1)
  152. go func() {
  153. defer controllerWaitGroup.Done()
  154. controller.Run(ctx)
  155. }()
  156. time.Sleep(10 * time.Second)
  157. cancelFunc()
  158. controllerWaitGroup.Wait()
  159. t.Logf("initial-connecting and connecting count: %d/%d", initialConnectingCount, connectingCount)
  160. if initialConnectingCount != initialLimitTunnelProtocolsCandidateCount {
  161. t.Fatalf("unexpected initial-connecting count")
  162. }
  163. if connectingCount < 3*initialLimitTunnelProtocolsCandidateCount {
  164. t.Fatalf("unexpected connecting count")
  165. }
  166. }