server_test.go 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321
  1. /*
  2. * Copyright (c) 2016, 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 server
  20. import (
  21. "context"
  22. "encoding/json"
  23. "errors"
  24. "flag"
  25. "fmt"
  26. "io/ioutil"
  27. "net"
  28. "net/http"
  29. "net/url"
  30. "os"
  31. "path/filepath"
  32. "strconv"
  33. "sync"
  34. "syscall"
  35. "testing"
  36. "time"
  37. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
  38. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  39. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/accesscontrol"
  40. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/marionette"
  41. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
  42. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
  43. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/tactics"
  44. "golang.org/x/net/proxy"
  45. )
  46. var serverIPAddress, testDataDirName string
  47. var mockWebServerURL, mockWebServerExpectedResponse string
  48. var mockWebServerPort = 8080
  49. func TestMain(m *testing.M) {
  50. flag.Parse()
  51. var err error
  52. for _, interfaceName := range []string{"eth0", "en0"} {
  53. var serverIPv4Address, serverIPv6Address net.IP
  54. serverIPv4Address, serverIPv6Address, err = common.GetInterfaceIPAddresses(interfaceName)
  55. if err == nil {
  56. if serverIPv4Address != nil {
  57. serverIPAddress = serverIPv4Address.String()
  58. } else {
  59. serverIPAddress = serverIPv6Address.String()
  60. }
  61. break
  62. }
  63. }
  64. if err != nil {
  65. fmt.Printf("error getting server IP address: %s\n", err)
  66. os.Exit(1)
  67. }
  68. testDataDirName, err = ioutil.TempDir("", "psiphon-server-test")
  69. if err != nil {
  70. fmt.Printf("TempDir failed: %s\n", err)
  71. os.Exit(1)
  72. }
  73. defer os.RemoveAll(testDataDirName)
  74. psiphon.SetEmitDiagnosticNotices(true)
  75. mockWebServerURL, mockWebServerExpectedResponse = runMockWebServer()
  76. os.Exit(m.Run())
  77. }
  78. func runMockWebServer() (string, string) {
  79. responseBody, _ := common.MakeSecureRandomStringHex(100000)
  80. serveMux := http.NewServeMux()
  81. serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  82. w.Write([]byte(responseBody))
  83. })
  84. webServerAddress := fmt.Sprintf("%s:%d", serverIPAddress, mockWebServerPort)
  85. server := &http.Server{
  86. Addr: webServerAddress,
  87. Handler: serveMux,
  88. }
  89. go func() {
  90. err := server.ListenAndServe()
  91. if err != nil {
  92. fmt.Printf("error running mock web server: %s\n", err)
  93. os.Exit(1)
  94. }
  95. }()
  96. // TODO: properly synchronize with web server readiness
  97. time.Sleep(1 * time.Second)
  98. return fmt.Sprintf("http://%s/", webServerAddress), responseBody
  99. }
  100. // Note: not testing fronting meek protocols, which client is
  101. // hard-wired to except running on privileged ports 80 and 443.
  102. func TestSSH(t *testing.T) {
  103. runServer(t,
  104. &runServerConfig{
  105. tunnelProtocol: "SSH",
  106. enableSSHAPIRequests: true,
  107. doHotReload: false,
  108. doDefaultSponsorID: false,
  109. denyTrafficRules: false,
  110. requireAuthorization: true,
  111. omitAuthorization: false,
  112. doTunneledWebRequest: true,
  113. doTunneledNTPRequest: true,
  114. })
  115. }
  116. func TestOSSH(t *testing.T) {
  117. runServer(t,
  118. &runServerConfig{
  119. tunnelProtocol: "OSSH",
  120. enableSSHAPIRequests: true,
  121. doHotReload: false,
  122. doDefaultSponsorID: false,
  123. denyTrafficRules: false,
  124. requireAuthorization: true,
  125. omitAuthorization: false,
  126. doTunneledWebRequest: true,
  127. doTunneledNTPRequest: true,
  128. })
  129. }
  130. func TestUnfrontedMeek(t *testing.T) {
  131. runServer(t,
  132. &runServerConfig{
  133. tunnelProtocol: "UNFRONTED-MEEK-OSSH",
  134. enableSSHAPIRequests: true,
  135. doHotReload: false,
  136. doDefaultSponsorID: false,
  137. denyTrafficRules: false,
  138. requireAuthorization: true,
  139. omitAuthorization: false,
  140. doTunneledWebRequest: true,
  141. doTunneledNTPRequest: true,
  142. })
  143. }
  144. func TestUnfrontedMeekHTTPS(t *testing.T) {
  145. runServer(t,
  146. &runServerConfig{
  147. tunnelProtocol: "UNFRONTED-MEEK-HTTPS-OSSH",
  148. tlsProfile: protocol.TLS_PROFILE_RANDOMIZED,
  149. enableSSHAPIRequests: true,
  150. doHotReload: false,
  151. doDefaultSponsorID: false,
  152. denyTrafficRules: false,
  153. requireAuthorization: true,
  154. omitAuthorization: false,
  155. doTunneledWebRequest: true,
  156. doTunneledNTPRequest: true,
  157. })
  158. }
  159. func TestUnfrontedMeekHTTPSTLS13(t *testing.T) {
  160. runServer(t,
  161. &runServerConfig{
  162. tunnelProtocol: "UNFRONTED-MEEK-HTTPS-OSSH",
  163. tlsProfile: protocol.TLS_PROFILE_TLS13_RANDOMIZED,
  164. enableSSHAPIRequests: true,
  165. doHotReload: false,
  166. doDefaultSponsorID: false,
  167. denyTrafficRules: false,
  168. requireAuthorization: true,
  169. omitAuthorization: false,
  170. doTunneledWebRequest: true,
  171. doTunneledNTPRequest: true,
  172. })
  173. }
  174. func TestUnfrontedMeekSessionTicket(t *testing.T) {
  175. runServer(t,
  176. &runServerConfig{
  177. tunnelProtocol: "UNFRONTED-MEEK-SESSION-TICKET-OSSH",
  178. tlsProfile: protocol.TLS_PROFILE_RANDOMIZED,
  179. enableSSHAPIRequests: true,
  180. doHotReload: false,
  181. doDefaultSponsorID: false,
  182. denyTrafficRules: false,
  183. requireAuthorization: true,
  184. omitAuthorization: false,
  185. doTunneledWebRequest: true,
  186. doTunneledNTPRequest: true,
  187. })
  188. }
  189. func TestUnfrontedMeekSessionTicketTLS13(t *testing.T) {
  190. runServer(t,
  191. &runServerConfig{
  192. tunnelProtocol: "UNFRONTED-MEEK-SESSION-TICKET-OSSH",
  193. tlsProfile: protocol.TLS_PROFILE_TLS13_RANDOMIZED,
  194. enableSSHAPIRequests: true,
  195. doHotReload: false,
  196. doDefaultSponsorID: false,
  197. denyTrafficRules: false,
  198. requireAuthorization: true,
  199. omitAuthorization: false,
  200. doTunneledWebRequest: true,
  201. doTunneledNTPRequest: true,
  202. })
  203. }
  204. func TestQUICOSSH(t *testing.T) {
  205. runServer(t,
  206. &runServerConfig{
  207. tunnelProtocol: "QUIC-OSSH",
  208. enableSSHAPIRequests: true,
  209. doHotReload: false,
  210. doDefaultSponsorID: false,
  211. denyTrafficRules: false,
  212. requireAuthorization: true,
  213. omitAuthorization: false,
  214. doTunneledWebRequest: true,
  215. doTunneledNTPRequest: true,
  216. })
  217. }
  218. func TestMarionetteOSSH(t *testing.T) {
  219. if !marionette.Enabled() {
  220. t.Skip("Marionette is not enabled")
  221. }
  222. runServer(t,
  223. &runServerConfig{
  224. tunnelProtocol: "MARIONETTE-OSSH",
  225. enableSSHAPIRequests: true,
  226. doHotReload: false,
  227. doDefaultSponsorID: false,
  228. denyTrafficRules: false,
  229. requireAuthorization: true,
  230. omitAuthorization: false,
  231. doTunneledWebRequest: true,
  232. doTunneledNTPRequest: true,
  233. })
  234. }
  235. func TestWebTransportAPIRequests(t *testing.T) {
  236. runServer(t,
  237. &runServerConfig{
  238. tunnelProtocol: "OSSH",
  239. enableSSHAPIRequests: false,
  240. doHotReload: false,
  241. doDefaultSponsorID: false,
  242. denyTrafficRules: false,
  243. requireAuthorization: false,
  244. omitAuthorization: true,
  245. doTunneledWebRequest: true,
  246. doTunneledNTPRequest: true,
  247. })
  248. }
  249. func TestHotReload(t *testing.T) {
  250. runServer(t,
  251. &runServerConfig{
  252. tunnelProtocol: "OSSH",
  253. enableSSHAPIRequests: true,
  254. doHotReload: true,
  255. doDefaultSponsorID: false,
  256. denyTrafficRules: false,
  257. requireAuthorization: true,
  258. omitAuthorization: false,
  259. doTunneledWebRequest: true,
  260. doTunneledNTPRequest: true,
  261. })
  262. }
  263. func TestDefaultSessionID(t *testing.T) {
  264. runServer(t,
  265. &runServerConfig{
  266. tunnelProtocol: "OSSH",
  267. enableSSHAPIRequests: true,
  268. doHotReload: true,
  269. doDefaultSponsorID: true,
  270. denyTrafficRules: false,
  271. requireAuthorization: true,
  272. omitAuthorization: false,
  273. doTunneledWebRequest: true,
  274. doTunneledNTPRequest: true,
  275. })
  276. }
  277. func TestDenyTrafficRules(t *testing.T) {
  278. runServer(t,
  279. &runServerConfig{
  280. tunnelProtocol: "OSSH",
  281. enableSSHAPIRequests: true,
  282. doHotReload: true,
  283. doDefaultSponsorID: false,
  284. denyTrafficRules: true,
  285. requireAuthorization: true,
  286. omitAuthorization: false,
  287. doTunneledWebRequest: true,
  288. doTunneledNTPRequest: true,
  289. })
  290. }
  291. func TestOmitAuthorization(t *testing.T) {
  292. runServer(t,
  293. &runServerConfig{
  294. tunnelProtocol: "OSSH",
  295. enableSSHAPIRequests: true,
  296. doHotReload: true,
  297. doDefaultSponsorID: false,
  298. denyTrafficRules: false,
  299. requireAuthorization: true,
  300. omitAuthorization: true,
  301. doTunneledWebRequest: true,
  302. doTunneledNTPRequest: true,
  303. })
  304. }
  305. func TestNoAuthorization(t *testing.T) {
  306. runServer(t,
  307. &runServerConfig{
  308. tunnelProtocol: "OSSH",
  309. enableSSHAPIRequests: true,
  310. doHotReload: true,
  311. doDefaultSponsorID: false,
  312. denyTrafficRules: false,
  313. requireAuthorization: false,
  314. omitAuthorization: true,
  315. doTunneledWebRequest: true,
  316. doTunneledNTPRequest: true,
  317. })
  318. }
  319. func TestUnusedAuthorization(t *testing.T) {
  320. runServer(t,
  321. &runServerConfig{
  322. tunnelProtocol: "OSSH",
  323. enableSSHAPIRequests: true,
  324. doHotReload: true,
  325. doDefaultSponsorID: false,
  326. denyTrafficRules: false,
  327. requireAuthorization: false,
  328. omitAuthorization: false,
  329. doTunneledWebRequest: true,
  330. doTunneledNTPRequest: true,
  331. })
  332. }
  333. func TestTCPOnlySLOK(t *testing.T) {
  334. runServer(t,
  335. &runServerConfig{
  336. tunnelProtocol: "OSSH",
  337. enableSSHAPIRequests: true,
  338. doHotReload: false,
  339. doDefaultSponsorID: false,
  340. denyTrafficRules: false,
  341. requireAuthorization: true,
  342. omitAuthorization: false,
  343. doTunneledWebRequest: true,
  344. doTunneledNTPRequest: false,
  345. })
  346. }
  347. func TestUDPOnlySLOK(t *testing.T) {
  348. runServer(t,
  349. &runServerConfig{
  350. tunnelProtocol: "OSSH",
  351. enableSSHAPIRequests: true,
  352. doHotReload: false,
  353. doDefaultSponsorID: false,
  354. denyTrafficRules: false,
  355. requireAuthorization: true,
  356. omitAuthorization: false,
  357. doTunneledWebRequest: false,
  358. doTunneledNTPRequest: true,
  359. })
  360. }
  361. type runServerConfig struct {
  362. tunnelProtocol string
  363. tlsProfile string
  364. enableSSHAPIRequests bool
  365. doHotReload bool
  366. doDefaultSponsorID bool
  367. denyTrafficRules bool
  368. requireAuthorization bool
  369. omitAuthorization bool
  370. doTunneledWebRequest bool
  371. doTunneledNTPRequest bool
  372. }
  373. func runServer(t *testing.T, runConfig *runServerConfig) {
  374. // configure authorized access
  375. accessType := "test-access-type"
  376. accessControlSigningKey, accessControlVerificationKey, err := accesscontrol.NewKeyPair(accessType)
  377. if err != nil {
  378. t.Fatalf("error creating access control key pair: %s", err)
  379. }
  380. accessControlVerificationKeyRing := accesscontrol.VerificationKeyRing{
  381. Keys: []*accesscontrol.VerificationKey{accessControlVerificationKey},
  382. }
  383. var authorizationID [32]byte
  384. clientAuthorization, err := accesscontrol.IssueAuthorization(
  385. accessControlSigningKey,
  386. authorizationID[:],
  387. time.Now().Add(1*time.Hour))
  388. if err != nil {
  389. t.Fatalf("error issuing authorization: %s", err)
  390. }
  391. // Enable tactics when the test protocol is meek. Both the client and the
  392. // server will be configured to support tactics. The client config will be
  393. // set with a nonfunctional config so that the tactics request must
  394. // succeed, overriding the nonfunctional values, for the tunnel to
  395. // establish.
  396. doTactics := protocol.TunnelProtocolUsesMeek(runConfig.tunnelProtocol)
  397. // All servers require a tactics config with valid keys.
  398. tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey, err :=
  399. tactics.GenerateKeys()
  400. if err != nil {
  401. t.Fatalf("error generating tactics keys: %s", err)
  402. }
  403. // create a server
  404. psiphonServerIPAddress := serverIPAddress
  405. if protocol.TunnelProtocolUsesQUIC(runConfig.tunnelProtocol) ||
  406. protocol.TunnelProtocolUsesMarionette(runConfig.tunnelProtocol) {
  407. // Workaround for macOS firewall.
  408. psiphonServerIPAddress = "127.0.0.1"
  409. }
  410. generateConfigParams := &GenerateConfigParams{
  411. ServerIPAddress: psiphonServerIPAddress,
  412. EnableSSHAPIRequests: runConfig.enableSSHAPIRequests,
  413. WebServerPort: 8000,
  414. TunnelProtocolPorts: map[string]int{runConfig.tunnelProtocol: 4000},
  415. }
  416. if protocol.TunnelProtocolUsesMarionette(runConfig.tunnelProtocol) {
  417. generateConfigParams.TunnelProtocolPorts[runConfig.tunnelProtocol] = 0
  418. generateConfigParams.MarionetteFormat = "http_simple_nonblocking"
  419. }
  420. if doTactics {
  421. generateConfigParams.TacticsRequestPublicKey = tacticsRequestPublicKey
  422. generateConfigParams.TacticsRequestObfuscatedKey = tacticsRequestObfuscatedKey
  423. }
  424. serverConfigJSON, _, _, _, encodedServerEntry, err := GenerateConfig(generateConfigParams)
  425. if err != nil {
  426. t.Fatalf("error generating server config: %s", err)
  427. }
  428. // customize server config
  429. // Pave psinet with random values to test handshake homepages.
  430. psinetFilename := filepath.Join(testDataDirName, "psinet.json")
  431. sponsorID, expectedHomepageURL := pavePsinetDatabaseFile(
  432. t, runConfig.doDefaultSponsorID, psinetFilename)
  433. // Pave OSL config for SLOK testing
  434. oslConfigFilename := filepath.Join(testDataDirName, "osl_config.json")
  435. propagationChannelID := paveOSLConfigFile(t, oslConfigFilename)
  436. // Pave traffic rules file which exercises handshake parameter filtering. Client
  437. // must handshake with specified sponsor ID in order to allow ports for tunneled
  438. // requests.
  439. trafficRulesFilename := filepath.Join(testDataDirName, "traffic_rules.json")
  440. paveTrafficRulesFile(
  441. t, trafficRulesFilename, propagationChannelID, accessType,
  442. runConfig.requireAuthorization, runConfig.denyTrafficRules)
  443. var tacticsConfigFilename string
  444. // Only pave the tactics config when tactics are required. This exercises the
  445. // case where the tactics config is omitted.
  446. if doTactics {
  447. tacticsConfigFilename = filepath.Join(testDataDirName, "tactics_config.json")
  448. paveTacticsConfigFile(
  449. t, tacticsConfigFilename,
  450. tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey,
  451. runConfig.tunnelProtocol,
  452. propagationChannelID)
  453. }
  454. var serverConfig map[string]interface{}
  455. json.Unmarshal(serverConfigJSON, &serverConfig)
  456. serverConfig["GeoIPDatabaseFilename"] = ""
  457. serverConfig["PsinetDatabaseFilename"] = psinetFilename
  458. serverConfig["TrafficRulesFilename"] = trafficRulesFilename
  459. serverConfig["OSLConfigFilename"] = oslConfigFilename
  460. if doTactics {
  461. serverConfig["TacticsConfigFilename"] = tacticsConfigFilename
  462. }
  463. serverConfig["LogFilename"] = filepath.Join(testDataDirName, "psiphond.log")
  464. serverConfig["LogLevel"] = "debug"
  465. serverConfig["AccessControlVerificationKeyRing"] = accessControlVerificationKeyRing
  466. // Set this parameter so at least the semaphore functions are called.
  467. // TODO: test that the concurrency limit is correctly enforced.
  468. serverConfig["MaxConcurrentSSHHandshakes"] = 1
  469. // Exercise this option.
  470. serverConfig["PeriodicGarbageCollectionSeconds"] = 1
  471. serverConfigJSON, _ = json.Marshal(serverConfig)
  472. // run server
  473. serverWaitGroup := new(sync.WaitGroup)
  474. serverWaitGroup.Add(1)
  475. go func() {
  476. defer serverWaitGroup.Done()
  477. err := RunServices(serverConfigJSON)
  478. if err != nil {
  479. // TODO: wrong goroutine for t.FatalNow()
  480. t.Fatalf("error running server: %s", err)
  481. }
  482. }()
  483. defer func() {
  484. // Test: orderly server shutdown
  485. p, _ := os.FindProcess(os.Getpid())
  486. p.Signal(os.Interrupt)
  487. shutdownTimeout := time.NewTimer(5 * time.Second)
  488. shutdownOk := make(chan struct{}, 1)
  489. go func() {
  490. serverWaitGroup.Wait()
  491. shutdownOk <- *new(struct{})
  492. }()
  493. select {
  494. case <-shutdownOk:
  495. case <-shutdownTimeout.C:
  496. t.Fatalf("server shutdown timeout exceeded")
  497. }
  498. }()
  499. // TODO: monitor logs for more robust wait-until-loaded
  500. time.Sleep(1 * time.Second)
  501. // Test: hot reload (of psinet and traffic rules)
  502. if runConfig.doHotReload {
  503. // Pave new config files with different random values.
  504. sponsorID, expectedHomepageURL = pavePsinetDatabaseFile(
  505. t, runConfig.doDefaultSponsorID, psinetFilename)
  506. propagationChannelID = paveOSLConfigFile(t, oslConfigFilename)
  507. paveTrafficRulesFile(
  508. t, trafficRulesFilename, propagationChannelID, accessType,
  509. runConfig.requireAuthorization, runConfig.denyTrafficRules)
  510. p, _ := os.FindProcess(os.Getpid())
  511. p.Signal(syscall.SIGUSR1)
  512. // TODO: monitor logs for more robust wait-until-reloaded
  513. time.Sleep(1 * time.Second)
  514. // After reloading psinet, the new sponsorID/expectedHomepageURL
  515. // should be active, as tested in the client "Homepage" notice
  516. // handler below.
  517. }
  518. // Exercise server_load logging
  519. p, _ := os.FindProcess(os.Getpid())
  520. p.Signal(syscall.SIGUSR2)
  521. // connect to server with client
  522. // TODO: currently, TargetServerEntry only works with one tunnel
  523. numTunnels := 1
  524. localSOCKSProxyPort := 1081
  525. localHTTPProxyPort := 8081
  526. jsonNetworkID := ""
  527. if doTactics {
  528. // Use a distinct prefix for network ID for each test run to
  529. // ensure tactics from different runs don't apply; this is
  530. // a workaround for the singleton datastore.
  531. prefix := time.Now().String()
  532. jsonNetworkID = fmt.Sprintf(`,"NetworkID" : "%s-%s"`, prefix, "NETWORK1")
  533. }
  534. jsonLimitTLSProfiles := ""
  535. if runConfig.tlsProfile != "" {
  536. jsonLimitTLSProfiles = fmt.Sprintf(`,"LimitTLSProfiles" : ["%s"]`, runConfig.tlsProfile)
  537. }
  538. clientConfigJSON := fmt.Sprintf(`
  539. {
  540. "ClientPlatform" : "Windows",
  541. "ClientVersion" : "0",
  542. "SponsorId" : "0",
  543. "PropagationChannelId" : "0",
  544. "DisableRemoteServerListFetcher" : true,
  545. "EstablishTunnelPausePeriodSeconds" : 1,
  546. "ConnectionWorkerPoolSize" : %d,
  547. "LimitTunnelProtocols" : ["%s"]
  548. %s
  549. %s
  550. }`, numTunnels, runConfig.tunnelProtocol, jsonLimitTLSProfiles, jsonNetworkID)
  551. clientConfig, err := psiphon.LoadConfig([]byte(clientConfigJSON))
  552. if err != nil {
  553. t.Fatalf("error processing configuration file: %s", err)
  554. }
  555. clientConfig.DataStoreDirectory = testDataDirName
  556. if !runConfig.doDefaultSponsorID {
  557. clientConfig.SponsorId = sponsorID
  558. }
  559. clientConfig.PropagationChannelId = propagationChannelID
  560. clientConfig.TunnelPoolSize = numTunnels
  561. clientConfig.TargetServerEntry = string(encodedServerEntry)
  562. clientConfig.LocalSocksProxyPort = localSOCKSProxyPort
  563. clientConfig.LocalHttpProxyPort = localHTTPProxyPort
  564. clientConfig.EmitSLOKs = true
  565. if !runConfig.omitAuthorization {
  566. clientConfig.Authorizations = []string{clientAuthorization}
  567. }
  568. err = clientConfig.Commit()
  569. if err != nil {
  570. t.Fatalf("error committing configuration file: %s", err)
  571. }
  572. if doTactics {
  573. // Configure nonfunctional values that must be overridden by tactics.
  574. applyParameters := make(map[string]interface{})
  575. applyParameters[parameters.TunnelConnectTimeout] = "1s"
  576. applyParameters[parameters.TunnelRateLimits] = common.RateLimits{WriteBytesPerSecond: 1}
  577. err = clientConfig.SetClientParameters("", true, applyParameters)
  578. if err != nil {
  579. t.Fatalf("SetClientParameters failed: %s", err)
  580. }
  581. }
  582. err = psiphon.OpenDataStore(clientConfig)
  583. if err != nil {
  584. t.Fatalf("error initializing client datastore: %s", err)
  585. }
  586. defer psiphon.CloseDataStore()
  587. psiphon.DeleteSLOKs()
  588. controller, err := psiphon.NewController(clientConfig)
  589. if err != nil {
  590. t.Fatalf("error creating client controller: %s", err)
  591. }
  592. tunnelsEstablished := make(chan struct{}, 1)
  593. homepageReceived := make(chan struct{}, 1)
  594. slokSeeded := make(chan struct{}, 1)
  595. psiphon.SetNoticeWriter(psiphon.NewNoticeReceiver(
  596. func(notice []byte) {
  597. //fmt.Printf("%s\n", string(notice))
  598. noticeType, payload, err := psiphon.GetNotice(notice)
  599. if err != nil {
  600. return
  601. }
  602. switch noticeType {
  603. case "Tunnels":
  604. count := int(payload["count"].(float64))
  605. if count >= numTunnels {
  606. sendNotificationReceived(tunnelsEstablished)
  607. }
  608. case "Homepage":
  609. homepageURL := payload["url"].(string)
  610. if homepageURL != expectedHomepageURL {
  611. // TODO: wrong goroutine for t.FatalNow()
  612. t.Fatalf("unexpected homepage: %s", homepageURL)
  613. }
  614. sendNotificationReceived(homepageReceived)
  615. case "SLOKSeeded":
  616. sendNotificationReceived(slokSeeded)
  617. }
  618. }))
  619. ctx, cancelFunc := context.WithCancel(context.Background())
  620. controllerWaitGroup := new(sync.WaitGroup)
  621. controllerWaitGroup.Add(1)
  622. go func() {
  623. defer controllerWaitGroup.Done()
  624. controller.Run(ctx)
  625. }()
  626. defer func() {
  627. cancelFunc()
  628. shutdownTimeout := time.NewTimer(20 * time.Second)
  629. shutdownOk := make(chan struct{}, 1)
  630. go func() {
  631. controllerWaitGroup.Wait()
  632. shutdownOk <- *new(struct{})
  633. }()
  634. select {
  635. case <-shutdownOk:
  636. case <-shutdownTimeout.C:
  637. t.Fatalf("controller shutdown timeout exceeded")
  638. }
  639. }()
  640. // Test: tunnels must be established, and correct homepage
  641. // must be received, within 30 seconds
  642. timeoutSignal := make(chan struct{})
  643. go func() {
  644. timer := time.NewTimer(30 * time.Second)
  645. <-timer.C
  646. close(timeoutSignal)
  647. }()
  648. waitOnNotification(t, tunnelsEstablished, timeoutSignal, "tunnel establish timeout exceeded")
  649. waitOnNotification(t, homepageReceived, timeoutSignal, "homepage received timeout exceeded")
  650. expectTrafficFailure := runConfig.denyTrafficRules || (runConfig.omitAuthorization && runConfig.requireAuthorization)
  651. if runConfig.doTunneledWebRequest {
  652. // Test: tunneled web site fetch
  653. err = makeTunneledWebRequest(
  654. t, localHTTPProxyPort, mockWebServerURL, mockWebServerExpectedResponse)
  655. if err == nil {
  656. if expectTrafficFailure {
  657. t.Fatalf("unexpected tunneled web request success")
  658. }
  659. } else {
  660. if !expectTrafficFailure {
  661. t.Fatalf("tunneled web request failed: %s", err)
  662. }
  663. }
  664. }
  665. if runConfig.doTunneledNTPRequest {
  666. // Test: tunneled UDP packets
  667. udpgwServerAddress := serverConfig["UDPInterceptUdpgwServerAddress"].(string)
  668. err = makeTunneledNTPRequest(t, localSOCKSProxyPort, udpgwServerAddress)
  669. if err == nil {
  670. if expectTrafficFailure {
  671. t.Fatalf("unexpected tunneled NTP request success")
  672. }
  673. } else {
  674. if !expectTrafficFailure {
  675. t.Fatalf("tunneled NTP request failed: %s", err)
  676. }
  677. }
  678. }
  679. // Test: await SLOK payload
  680. if !expectTrafficFailure {
  681. time.Sleep(1 * time.Second)
  682. waitOnNotification(t, slokSeeded, timeoutSignal, "SLOK seeded timeout exceeded")
  683. numSLOKs := psiphon.CountSLOKs()
  684. if numSLOKs != expectedNumSLOKs {
  685. t.Fatalf("unexpected number of SLOKs: %d", numSLOKs)
  686. }
  687. }
  688. }
  689. func makeTunneledWebRequest(
  690. t *testing.T,
  691. localHTTPProxyPort int,
  692. requestURL, expectedResponseBody string) error {
  693. roundTripTimeout := 30 * time.Second
  694. proxyUrl, err := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", localHTTPProxyPort))
  695. if err != nil {
  696. return fmt.Errorf("error initializing proxied HTTP request: %s", err)
  697. }
  698. httpClient := &http.Client{
  699. Transport: &http.Transport{
  700. Proxy: http.ProxyURL(proxyUrl),
  701. },
  702. Timeout: roundTripTimeout,
  703. }
  704. response, err := httpClient.Get(requestURL)
  705. if err != nil {
  706. return fmt.Errorf("error sending proxied HTTP request: %s", err)
  707. }
  708. body, err := ioutil.ReadAll(response.Body)
  709. if err != nil {
  710. return fmt.Errorf("error reading proxied HTTP response: %s", err)
  711. }
  712. response.Body.Close()
  713. if string(body) != expectedResponseBody {
  714. return fmt.Errorf("unexpected proxied HTTP response")
  715. }
  716. return nil
  717. }
  718. func makeTunneledNTPRequest(t *testing.T, localSOCKSProxyPort int, udpgwServerAddress string) error {
  719. timeout := 20 * time.Second
  720. var err error
  721. for _, testHostname := range []string{"time.google.com", "time.nist.gov", "pool.ntp.org"} {
  722. err = makeTunneledNTPRequestAttempt(t, testHostname, timeout, localSOCKSProxyPort, udpgwServerAddress)
  723. if err == nil {
  724. break
  725. }
  726. t.Logf("makeTunneledNTPRequestAttempt failed: %s", err)
  727. }
  728. return err
  729. }
  730. var nextUDPProxyPort = 7300
  731. func makeTunneledNTPRequestAttempt(
  732. t *testing.T, testHostname string, timeout time.Duration, localSOCKSProxyPort int, udpgwServerAddress string) error {
  733. nextUDPProxyPort++
  734. localUDPProxyAddress, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", nextUDPProxyPort))
  735. if err != nil {
  736. return fmt.Errorf("ResolveUDPAddr failed: %s", err)
  737. }
  738. // Note: this proxy is intended for this test only -- it only accepts a single connection,
  739. // handles it, and then terminates.
  740. localUDPProxy := func(destinationIP net.IP, destinationPort uint16, waitGroup *sync.WaitGroup) {
  741. if waitGroup != nil {
  742. defer waitGroup.Done()
  743. }
  744. destination := net.JoinHostPort(destinationIP.String(), strconv.Itoa(int(destinationPort)))
  745. serverUDPConn, err := net.ListenUDP("udp", localUDPProxyAddress)
  746. if err != nil {
  747. t.Logf("ListenUDP for %s failed: %s", destination, err)
  748. return
  749. }
  750. defer serverUDPConn.Close()
  751. udpgwPreambleSize := 11 // see writeUdpgwPreamble
  752. buffer := make([]byte, udpgwProtocolMaxMessageSize)
  753. packetSize, clientAddr, err := serverUDPConn.ReadFromUDP(
  754. buffer[udpgwPreambleSize:])
  755. if err != nil {
  756. t.Logf("serverUDPConn.Read for %s failed: %s", destination, err)
  757. return
  758. }
  759. socksProxyAddress := fmt.Sprintf("127.0.0.1:%d", localSOCKSProxyPort)
  760. dialer, err := proxy.SOCKS5("tcp", socksProxyAddress, nil, proxy.Direct)
  761. if err != nil {
  762. t.Logf("proxy.SOCKS5 for %s failed: %s", destination, err)
  763. return
  764. }
  765. socksTCPConn, err := dialer.Dial("tcp", udpgwServerAddress)
  766. if err != nil {
  767. t.Logf("dialer.Dial for %s failed: %s", destination, err)
  768. return
  769. }
  770. defer socksTCPConn.Close()
  771. flags := uint8(0)
  772. if destinationPort == 53 {
  773. flags = udpgwProtocolFlagDNS
  774. }
  775. err = writeUdpgwPreamble(
  776. udpgwPreambleSize,
  777. flags,
  778. 0,
  779. destinationIP,
  780. destinationPort,
  781. uint16(packetSize),
  782. buffer)
  783. if err != nil {
  784. t.Logf("writeUdpgwPreamble for %s failed: %s", destination, err)
  785. return
  786. }
  787. _, err = socksTCPConn.Write(buffer[0 : udpgwPreambleSize+packetSize])
  788. if err != nil {
  789. t.Logf("socksTCPConn.Write for %s failed: %s", destination, err)
  790. return
  791. }
  792. udpgwProtocolMessage, err := readUdpgwMessage(socksTCPConn, buffer)
  793. if err != nil {
  794. t.Logf("readUdpgwMessage for %s failed: %s", destination, err)
  795. return
  796. }
  797. _, err = serverUDPConn.WriteToUDP(udpgwProtocolMessage.packet, clientAddr)
  798. if err != nil {
  799. t.Logf("serverUDPConn.Write for %s failed: %s", destination, err)
  800. return
  801. }
  802. }
  803. // Tunneled DNS request
  804. waitGroup := new(sync.WaitGroup)
  805. waitGroup.Add(1)
  806. go localUDPProxy(
  807. net.IP(make([]byte, 4)), // ignored due to transparent DNS forwarding
  808. 53,
  809. waitGroup)
  810. // TODO: properly synchronize with local UDP proxy startup
  811. time.Sleep(1 * time.Second)
  812. clientUDPConn, err := net.DialUDP("udp", nil, localUDPProxyAddress)
  813. if err != nil {
  814. return fmt.Errorf("DialUDP failed: %s", err)
  815. }
  816. clientUDPConn.SetReadDeadline(time.Now().Add(timeout))
  817. clientUDPConn.SetWriteDeadline(time.Now().Add(timeout))
  818. addrs, _, err := psiphon.ResolveIP(testHostname, clientUDPConn)
  819. clientUDPConn.Close()
  820. if err == nil && (len(addrs) == 0 || len(addrs[0]) < 4) {
  821. err = errors.New("no address")
  822. }
  823. if err != nil {
  824. return fmt.Errorf("ResolveIP failed: %s", err)
  825. }
  826. waitGroup.Wait()
  827. // Tunneled NTP request
  828. waitGroup = new(sync.WaitGroup)
  829. waitGroup.Add(1)
  830. go localUDPProxy(
  831. addrs[0][len(addrs[0])-4:],
  832. 123,
  833. waitGroup)
  834. // TODO: properly synchronize with local UDP proxy startup
  835. time.Sleep(1 * time.Second)
  836. clientUDPConn, err = net.DialUDP("udp", nil, localUDPProxyAddress)
  837. if err != nil {
  838. return fmt.Errorf("DialUDP failed: %s", err)
  839. }
  840. clientUDPConn.SetReadDeadline(time.Now().Add(timeout))
  841. clientUDPConn.SetWriteDeadline(time.Now().Add(timeout))
  842. // NTP protocol code from: https://groups.google.com/d/msg/golang-nuts/FlcdMU5fkLQ/CAeoD9eqm-IJ
  843. ntpData := make([]byte, 48)
  844. ntpData[0] = 3<<3 | 3
  845. _, err = clientUDPConn.Write(ntpData)
  846. if err != nil {
  847. clientUDPConn.Close()
  848. return fmt.Errorf("NTP Write failed: %s", err)
  849. }
  850. _, err = clientUDPConn.Read(ntpData)
  851. if err != nil {
  852. clientUDPConn.Close()
  853. return fmt.Errorf("NTP Read failed: %s", err)
  854. }
  855. clientUDPConn.Close()
  856. var sec, frac uint64
  857. sec = uint64(ntpData[43]) | uint64(ntpData[42])<<8 | uint64(ntpData[41])<<16 | uint64(ntpData[40])<<24
  858. frac = uint64(ntpData[47]) | uint64(ntpData[46])<<8 | uint64(ntpData[45])<<16 | uint64(ntpData[44])<<24
  859. nsec := sec * 1e9
  860. nsec += (frac * 1e9) >> 32
  861. ntpNow := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nsec)).Local()
  862. now := time.Now()
  863. diff := ntpNow.Sub(now)
  864. if diff < 0 {
  865. diff = -diff
  866. }
  867. if diff > 1*time.Minute {
  868. return fmt.Errorf("Unexpected NTP time: %s; local time: %s", ntpNow, now)
  869. }
  870. waitGroup.Wait()
  871. return nil
  872. }
  873. func pavePsinetDatabaseFile(
  874. t *testing.T, useDefaultSponsorID bool, psinetFilename string) (string, string) {
  875. sponsorID, _ := common.MakeSecureRandomStringHex(8)
  876. fakeDomain, _ := common.MakeSecureRandomStringHex(4)
  877. fakePath, _ := common.MakeSecureRandomStringHex(4)
  878. expectedHomepageURL := fmt.Sprintf("https://%s.com/%s", fakeDomain, fakePath)
  879. psinetJSONFormat := `
  880. {
  881. "default_sponsor_id" : "%s",
  882. "sponsors": {
  883. "%s": {
  884. "home_pages": {
  885. "None": [
  886. {
  887. "region": null,
  888. "url": "%s"
  889. }
  890. ]
  891. }
  892. }
  893. }
  894. }
  895. `
  896. defaultSponsorID := ""
  897. if useDefaultSponsorID {
  898. defaultSponsorID = sponsorID
  899. }
  900. psinetJSON := fmt.Sprintf(
  901. psinetJSONFormat, defaultSponsorID, sponsorID, expectedHomepageURL)
  902. err := ioutil.WriteFile(psinetFilename, []byte(psinetJSON), 0600)
  903. if err != nil {
  904. t.Fatalf("error paving psinet database file: %s", err)
  905. }
  906. return sponsorID, expectedHomepageURL
  907. }
  908. func paveTrafficRulesFile(
  909. t *testing.T, trafficRulesFilename, propagationChannelID, accessType string,
  910. requireAuthorization, deny bool) {
  911. allowTCPPorts := fmt.Sprintf("%d", mockWebServerPort)
  912. allowUDPPorts := "53, 123"
  913. if deny {
  914. allowTCPPorts = "0"
  915. allowUDPPorts = "0"
  916. }
  917. authorizationFilterFormat := `,
  918. "AuthorizedAccessTypes" : ["%s"]
  919. `
  920. authorizationFilter := ""
  921. if requireAuthorization {
  922. authorizationFilter = fmt.Sprintf(authorizationFilterFormat, accessType)
  923. }
  924. trafficRulesJSONFormat := `
  925. {
  926. "DefaultRules" : {
  927. "RateLimits" : {
  928. "ReadBytesPerSecond": 16384,
  929. "WriteBytesPerSecond": 16384
  930. },
  931. "AllowTCPPorts" : [0],
  932. "AllowUDPPorts" : [0],
  933. "MeekRateLimiterHistorySize" : 10,
  934. "MeekRateLimiterThresholdSeconds" : 1,
  935. "MeekRateLimiterGarbageCollectionTriggerCount" : 1,
  936. "MeekRateLimiterReapHistoryFrequencySeconds" : 1,
  937. "MeekRateLimiterRegions" : []
  938. },
  939. "FilteredRules" : [
  940. {
  941. "Filter" : {
  942. "HandshakeParameters" : {
  943. "propagation_channel_id" : ["%s"]
  944. }%s
  945. },
  946. "Rules" : {
  947. "RateLimits" : {
  948. "ReadUnthrottledBytes": 132352,
  949. "WriteUnthrottledBytes": 132352
  950. },
  951. "AllowTCPPorts" : [%s],
  952. "AllowUDPPorts" : [%s]
  953. }
  954. }
  955. ]
  956. }
  957. `
  958. trafficRulesJSON := fmt.Sprintf(
  959. trafficRulesJSONFormat, propagationChannelID, authorizationFilter, allowTCPPorts, allowUDPPorts)
  960. err := ioutil.WriteFile(trafficRulesFilename, []byte(trafficRulesJSON), 0600)
  961. if err != nil {
  962. t.Fatalf("error paving traffic rules file: %s", err)
  963. }
  964. }
  965. var expectedNumSLOKs = 3
  966. func paveOSLConfigFile(t *testing.T, oslConfigFilename string) string {
  967. oslConfigJSONFormat := `
  968. {
  969. "Schemes" : [
  970. {
  971. "Epoch" : "%s",
  972. "Regions" : [],
  973. "PropagationChannelIDs" : ["%s"],
  974. "MasterKey" : "wFuSbqU/pJ/35vRmoM8T9ys1PgDa8uzJps1Y+FNKa5U=",
  975. "SeedSpecs" : [
  976. {
  977. "ID" : "IXHWfVgWFkEKvgqsjmnJuN3FpaGuCzQMETya+DSQvsk=",
  978. "UpstreamSubnets" : ["0.0.0.0/0"],
  979. "Targets" :
  980. {
  981. "BytesRead" : 1,
  982. "BytesWritten" : 1,
  983. "PortForwardDurationNanoseconds" : 1
  984. }
  985. },
  986. {
  987. "ID" : "qvpIcORLE2Pi5TZmqRtVkEp+OKov0MhfsYPLNV7FYtI=",
  988. "UpstreamSubnets" : ["0.0.0.0/0"],
  989. "Targets" :
  990. {
  991. "BytesRead" : 1,
  992. "BytesWritten" : 1,
  993. "PortForwardDurationNanoseconds" : 1
  994. }
  995. }
  996. ],
  997. "SeedSpecThreshold" : 2,
  998. "SeedPeriodNanoseconds" : 2592000000000000,
  999. "SeedPeriodKeySplits": [
  1000. {
  1001. "Total": 2,
  1002. "Threshold": 2
  1003. }
  1004. ]
  1005. },
  1006. {
  1007. "Epoch" : "%s",
  1008. "Regions" : [],
  1009. "PropagationChannelIDs" : ["%s"],
  1010. "MasterKey" : "HDc/mvd7e+lKDJD0fMpJW66YJ/VW4iqDRjeclEsMnro=",
  1011. "SeedSpecs" : [
  1012. {
  1013. "ID" : "/M0vsT0IjzmI0MvTI9IYe8OVyeQGeaPZN2xGxfLw/UQ=",
  1014. "UpstreamSubnets" : ["0.0.0.0/0"],
  1015. "Targets" :
  1016. {
  1017. "BytesRead" : 1,
  1018. "BytesWritten" : 1,
  1019. "PortForwardDurationNanoseconds" : 1
  1020. }
  1021. }
  1022. ],
  1023. "SeedSpecThreshold" : 1,
  1024. "SeedPeriodNanoseconds" : 2592000000000000,
  1025. "SeedPeriodKeySplits": [
  1026. {
  1027. "Total": 1,
  1028. "Threshold": 1
  1029. }
  1030. ]
  1031. }
  1032. ]
  1033. }
  1034. `
  1035. propagationChannelID, _ := common.MakeSecureRandomStringHex(8)
  1036. now := time.Now().UTC()
  1037. epoch := now.Truncate(720 * time.Hour)
  1038. epochStr := epoch.Format(time.RFC3339Nano)
  1039. oslConfigJSON := fmt.Sprintf(
  1040. oslConfigJSONFormat,
  1041. epochStr, propagationChannelID,
  1042. epochStr, propagationChannelID)
  1043. err := ioutil.WriteFile(oslConfigFilename, []byte(oslConfigJSON), 0600)
  1044. if err != nil {
  1045. t.Fatalf("error paving osl config file: %s", err)
  1046. }
  1047. return propagationChannelID
  1048. }
  1049. func paveTacticsConfigFile(
  1050. t *testing.T, tacticsConfigFilename string,
  1051. tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey string,
  1052. tunnelProtocol string,
  1053. propagationChannelID string) {
  1054. // Setting LimitTunnelProtocols passively exercises the
  1055. // server-side LimitTunnelProtocols enforcement.
  1056. tacticsConfigJSONFormat := `
  1057. {
  1058. "RequestPublicKey" : "%s",
  1059. "RequestPrivateKey" : "%s",
  1060. "RequestObfuscatedKey" : "%s",
  1061. "EnforceServerSide" : true,
  1062. "DefaultTactics" : {
  1063. "TTL" : "60s",
  1064. "Probability" : 1.0,
  1065. "Parameters" : {
  1066. "LimitTunnelProtocols" : ["%s"]
  1067. }
  1068. },
  1069. "FilteredTactics" : [
  1070. {
  1071. "Filter" : {
  1072. "APIParameters" : {"propagation_channel_id" : ["%s"]},
  1073. "SpeedTestRTTMilliseconds" : {
  1074. "Aggregation" : "Median",
  1075. "AtLeast" : 1
  1076. }
  1077. },
  1078. "Tactics" : {
  1079. "Parameters" : {
  1080. "TunnelConnectTimeout" : "20s",
  1081. "TunnelRateLimits" : {"WriteBytesPerSecond": 1000000}
  1082. }
  1083. }
  1084. }
  1085. ]
  1086. }
  1087. `
  1088. tacticsConfigJSON := fmt.Sprintf(
  1089. tacticsConfigJSONFormat,
  1090. tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey,
  1091. tunnelProtocol,
  1092. propagationChannelID)
  1093. err := ioutil.WriteFile(tacticsConfigFilename, []byte(tacticsConfigJSON), 0600)
  1094. if err != nil {
  1095. t.Fatalf("error paving tactics config file: %s", err)
  1096. }
  1097. }
  1098. func sendNotificationReceived(c chan<- struct{}) {
  1099. select {
  1100. case c <- *new(struct{}):
  1101. default:
  1102. }
  1103. }
  1104. func waitOnNotification(t *testing.T, c, timeoutSignal <-chan struct{}, timeoutMessage string) {
  1105. select {
  1106. case <-c:
  1107. case <-timeoutSignal:
  1108. t.Fatalf(timeoutMessage)
  1109. }
  1110. }