server_test.go 26 KB

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