server_test.go 27 KB

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