server_test.go 30 KB

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