server_test.go 34 KB

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