github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/server/server_test.go (about) 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 20 package server 21 22 import ( 23 "context" 24 "encoding/base64" 25 "encoding/json" 26 std_errors "errors" 27 "flag" 28 "fmt" 29 "io/ioutil" 30 "net" 31 "net/http" 32 "net/url" 33 "os" 34 "path/filepath" 35 "reflect" 36 "regexp" 37 "strconv" 38 "strings" 39 "sync" 40 "syscall" 41 "testing" 42 "time" 43 44 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon" 45 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common" 46 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/accesscontrol" 47 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors" 48 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters" 49 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng" 50 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol" 51 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/quic" 52 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/tactics" 53 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/values" 54 "github.com/miekg/dns" 55 "golang.org/x/net/proxy" 56 ) 57 58 var serverIPAddress, testDataDirName string 59 var mockWebServerURL, mockWebServerExpectedResponse string 60 var mockWebServerPort = "8080" 61 62 func TestMain(m *testing.M) { 63 flag.Parse() 64 65 serverIPv4Address, serverIPv6Address, err := common.GetRoutableInterfaceIPAddresses() 66 if err != nil { 67 fmt.Printf("error getting server IP address: %s\n", err) 68 os.Exit(1) 69 } 70 if serverIPv4Address != nil { 71 serverIPAddress = serverIPv4Address.String() 72 } else { 73 serverIPAddress = serverIPv6Address.String() 74 } 75 76 testDataDirName, err = ioutil.TempDir("", "psiphon-server-test") 77 if err != nil { 78 fmt.Printf("TempDir failed: %s\n", err) 79 os.Exit(1) 80 } 81 defer os.RemoveAll(testDataDirName) 82 83 psiphon.SetEmitDiagnosticNotices(true, true) 84 85 mockWebServerURL, mockWebServerExpectedResponse = runMockWebServer() 86 87 os.Exit(m.Run()) 88 } 89 90 func runMockWebServer() (string, string) { 91 92 responseBody := prng.HexString(100000) 93 94 serveMux := http.NewServeMux() 95 serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 96 w.Write([]byte(responseBody)) 97 }) 98 webServerAddress := net.JoinHostPort(serverIPAddress, mockWebServerPort) 99 server := &http.Server{ 100 Addr: webServerAddress, 101 Handler: serveMux, 102 } 103 104 go func() { 105 err := server.ListenAndServe() 106 if err != nil { 107 fmt.Printf("error running mock web server: %s\n", err) 108 os.Exit(1) 109 } 110 }() 111 112 // TODO: properly synchronize with web server readiness 113 time.Sleep(1 * time.Second) 114 115 return fmt.Sprintf("http://%s/", webServerAddress), responseBody 116 } 117 118 // Note: not testing fronted meek protocols, which client is 119 // hard-wired to expect running on privileged ports 80 and 443. 120 121 func TestSSH(t *testing.T) { 122 runServer(t, 123 &runServerConfig{ 124 tunnelProtocol: "SSH", 125 enableSSHAPIRequests: true, 126 doHotReload: false, 127 doDefaultSponsorID: false, 128 denyTrafficRules: false, 129 requireAuthorization: true, 130 omitAuthorization: false, 131 doTunneledWebRequest: true, 132 doTunneledNTPRequest: true, 133 forceFragmenting: false, 134 forceLivenessTest: false, 135 doPruneServerEntries: false, 136 doDanglingTCPConn: true, 137 doPacketManipulation: false, 138 doBurstMonitor: false, 139 doSplitTunnel: false, 140 limitQUICVersions: false, 141 doDestinationBytes: false, 142 doChangeBytesConfig: false, 143 }) 144 } 145 146 func TestOSSH(t *testing.T) { 147 runServer(t, 148 &runServerConfig{ 149 tunnelProtocol: "OSSH", 150 enableSSHAPIRequests: true, 151 doHotReload: false, 152 doDefaultSponsorID: false, 153 denyTrafficRules: false, 154 requireAuthorization: true, 155 omitAuthorization: false, 156 doTunneledWebRequest: true, 157 doTunneledNTPRequest: true, 158 forceFragmenting: false, 159 forceLivenessTest: false, 160 doPruneServerEntries: false, 161 doDanglingTCPConn: true, 162 doPacketManipulation: false, 163 doBurstMonitor: false, 164 doSplitTunnel: false, 165 limitQUICVersions: false, 166 doDestinationBytes: false, 167 doChangeBytesConfig: false, 168 }) 169 } 170 171 func TestFragmentedOSSH(t *testing.T) { 172 runServer(t, 173 &runServerConfig{ 174 tunnelProtocol: "OSSH", 175 enableSSHAPIRequests: true, 176 doHotReload: false, 177 doDefaultSponsorID: false, 178 denyTrafficRules: false, 179 requireAuthorization: true, 180 omitAuthorization: false, 181 doTunneledWebRequest: true, 182 doTunneledNTPRequest: true, 183 forceFragmenting: true, 184 forceLivenessTest: false, 185 doPruneServerEntries: false, 186 doDanglingTCPConn: true, 187 doPacketManipulation: false, 188 doBurstMonitor: false, 189 doSplitTunnel: false, 190 limitQUICVersions: false, 191 doDestinationBytes: false, 192 doChangeBytesConfig: false, 193 }) 194 } 195 196 func TestUnfrontedMeek(t *testing.T) { 197 runServer(t, 198 &runServerConfig{ 199 tunnelProtocol: "UNFRONTED-MEEK-OSSH", 200 enableSSHAPIRequests: true, 201 doHotReload: false, 202 doDefaultSponsorID: false, 203 denyTrafficRules: false, 204 requireAuthorization: true, 205 omitAuthorization: false, 206 doTunneledWebRequest: true, 207 doTunneledNTPRequest: true, 208 forceFragmenting: false, 209 forceLivenessTest: false, 210 doPruneServerEntries: false, 211 doDanglingTCPConn: true, 212 doPacketManipulation: false, 213 doBurstMonitor: false, 214 doSplitTunnel: false, 215 limitQUICVersions: false, 216 doDestinationBytes: false, 217 doChangeBytesConfig: false, 218 }) 219 } 220 221 func TestFragmentedUnfrontedMeek(t *testing.T) { 222 runServer(t, 223 &runServerConfig{ 224 tunnelProtocol: "UNFRONTED-MEEK-OSSH", 225 enableSSHAPIRequests: true, 226 doHotReload: false, 227 doDefaultSponsorID: false, 228 denyTrafficRules: false, 229 requireAuthorization: true, 230 omitAuthorization: false, 231 doTunneledWebRequest: true, 232 doTunneledNTPRequest: true, 233 forceFragmenting: true, 234 forceLivenessTest: false, 235 doPruneServerEntries: false, 236 doDanglingTCPConn: true, 237 doPacketManipulation: false, 238 doBurstMonitor: false, 239 doSplitTunnel: false, 240 limitQUICVersions: false, 241 doDestinationBytes: false, 242 doChangeBytesConfig: false, 243 }) 244 } 245 246 func TestUnfrontedMeekHTTPS(t *testing.T) { 247 runServer(t, 248 &runServerConfig{ 249 tunnelProtocol: "UNFRONTED-MEEK-HTTPS-OSSH", 250 tlsProfile: protocol.TLS_PROFILE_RANDOMIZED, 251 enableSSHAPIRequests: true, 252 doHotReload: false, 253 doDefaultSponsorID: false, 254 denyTrafficRules: false, 255 requireAuthorization: true, 256 omitAuthorization: false, 257 doTunneledWebRequest: true, 258 doTunneledNTPRequest: true, 259 forceFragmenting: false, 260 forceLivenessTest: false, 261 doPruneServerEntries: false, 262 doDanglingTCPConn: true, 263 doPacketManipulation: false, 264 doBurstMonitor: false, 265 doSplitTunnel: false, 266 limitQUICVersions: false, 267 doDestinationBytes: false, 268 doChangeBytesConfig: false, 269 }) 270 } 271 272 func TestFragmentedUnfrontedMeekHTTPS(t *testing.T) { 273 runServer(t, 274 &runServerConfig{ 275 tunnelProtocol: "UNFRONTED-MEEK-HTTPS-OSSH", 276 tlsProfile: protocol.TLS_PROFILE_RANDOMIZED, 277 enableSSHAPIRequests: true, 278 doHotReload: false, 279 doDefaultSponsorID: false, 280 denyTrafficRules: false, 281 requireAuthorization: true, 282 omitAuthorization: false, 283 doTunneledWebRequest: true, 284 doTunneledNTPRequest: true, 285 forceFragmenting: true, 286 forceLivenessTest: false, 287 doPruneServerEntries: false, 288 doDanglingTCPConn: true, 289 doPacketManipulation: false, 290 doBurstMonitor: false, 291 doSplitTunnel: false, 292 limitQUICVersions: false, 293 doDestinationBytes: false, 294 doChangeBytesConfig: false, 295 }) 296 } 297 298 func TestUnfrontedMeekHTTPSTLS13(t *testing.T) { 299 runServer(t, 300 &runServerConfig{ 301 tunnelProtocol: "UNFRONTED-MEEK-HTTPS-OSSH", 302 tlsProfile: protocol.TLS_PROFILE_CHROME_70, 303 enableSSHAPIRequests: true, 304 doHotReload: false, 305 doDefaultSponsorID: false, 306 denyTrafficRules: false, 307 requireAuthorization: true, 308 omitAuthorization: false, 309 doTunneledWebRequest: true, 310 doTunneledNTPRequest: true, 311 forceFragmenting: false, 312 forceLivenessTest: false, 313 doPruneServerEntries: false, 314 doDanglingTCPConn: true, 315 doPacketManipulation: false, 316 doBurstMonitor: false, 317 doSplitTunnel: false, 318 limitQUICVersions: false, 319 doDestinationBytes: false, 320 doChangeBytesConfig: false, 321 }) 322 } 323 324 func TestUnfrontedMeekSessionTicket(t *testing.T) { 325 runServer(t, 326 &runServerConfig{ 327 tunnelProtocol: "UNFRONTED-MEEK-SESSION-TICKET-OSSH", 328 tlsProfile: protocol.TLS_PROFILE_CHROME_58, 329 enableSSHAPIRequests: true, 330 doHotReload: false, 331 doDefaultSponsorID: false, 332 denyTrafficRules: false, 333 requireAuthorization: true, 334 omitAuthorization: false, 335 doTunneledWebRequest: true, 336 doTunneledNTPRequest: true, 337 forceFragmenting: false, 338 forceLivenessTest: false, 339 doPruneServerEntries: false, 340 doDanglingTCPConn: true, 341 doPacketManipulation: false, 342 doBurstMonitor: false, 343 doSplitTunnel: false, 344 limitQUICVersions: false, 345 doDestinationBytes: false, 346 doChangeBytesConfig: false, 347 }) 348 } 349 350 func TestUnfrontedMeekSessionTicketTLS13(t *testing.T) { 351 runServer(t, 352 &runServerConfig{ 353 tunnelProtocol: "UNFRONTED-MEEK-SESSION-TICKET-OSSH", 354 tlsProfile: protocol.TLS_PROFILE_CHROME_70, 355 enableSSHAPIRequests: true, 356 doHotReload: false, 357 doDefaultSponsorID: false, 358 denyTrafficRules: false, 359 requireAuthorization: true, 360 omitAuthorization: false, 361 doTunneledWebRequest: true, 362 doTunneledNTPRequest: true, 363 forceFragmenting: false, 364 forceLivenessTest: false, 365 doPruneServerEntries: false, 366 doDanglingTCPConn: true, 367 doPacketManipulation: false, 368 doBurstMonitor: false, 369 doSplitTunnel: false, 370 limitQUICVersions: false, 371 doDestinationBytes: false, 372 doChangeBytesConfig: false, 373 }) 374 } 375 376 func TestQUICOSSH(t *testing.T) { 377 if !quic.Enabled() { 378 t.Skip("QUIC is not enabled") 379 } 380 runServer(t, 381 &runServerConfig{ 382 tunnelProtocol: "QUIC-OSSH", 383 enableSSHAPIRequests: true, 384 doHotReload: false, 385 doDefaultSponsorID: false, 386 denyTrafficRules: false, 387 requireAuthorization: true, 388 omitAuthorization: false, 389 doTunneledWebRequest: true, 390 doTunneledNTPRequest: true, 391 forceFragmenting: false, 392 forceLivenessTest: false, 393 doPruneServerEntries: false, 394 doDanglingTCPConn: false, 395 doPacketManipulation: false, 396 doBurstMonitor: false, 397 doSplitTunnel: false, 398 limitQUICVersions: false, 399 doDestinationBytes: false, 400 doChangeBytesConfig: false, 401 }) 402 } 403 404 func TestLimitedQUICOSSH(t *testing.T) { 405 if !quic.Enabled() { 406 t.Skip("QUIC is not enabled") 407 } 408 runServer(t, 409 &runServerConfig{ 410 tunnelProtocol: "QUIC-OSSH", 411 enableSSHAPIRequests: true, 412 doHotReload: false, 413 doDefaultSponsorID: false, 414 denyTrafficRules: false, 415 requireAuthorization: true, 416 omitAuthorization: false, 417 doTunneledWebRequest: true, 418 doTunneledNTPRequest: true, 419 forceFragmenting: false, 420 forceLivenessTest: false, 421 doPruneServerEntries: false, 422 doDanglingTCPConn: false, 423 doPacketManipulation: false, 424 doBurstMonitor: false, 425 doSplitTunnel: false, 426 limitQUICVersions: true, 427 doDestinationBytes: false, 428 doChangeBytesConfig: false, 429 }) 430 } 431 432 func TestWebTransportAPIRequests(t *testing.T) { 433 runServer(t, 434 &runServerConfig{ 435 tunnelProtocol: "OSSH", 436 enableSSHAPIRequests: false, 437 doHotReload: false, 438 doDefaultSponsorID: false, 439 denyTrafficRules: false, 440 requireAuthorization: false, 441 omitAuthorization: true, 442 doTunneledWebRequest: true, 443 doTunneledNTPRequest: true, 444 forceFragmenting: false, 445 forceLivenessTest: false, 446 doPruneServerEntries: false, 447 doDanglingTCPConn: false, 448 doPacketManipulation: false, 449 doBurstMonitor: false, 450 doSplitTunnel: false, 451 limitQUICVersions: false, 452 doDestinationBytes: false, 453 doChangeBytesConfig: false, 454 }) 455 } 456 457 func TestHotReload(t *testing.T) { 458 runServer(t, 459 &runServerConfig{ 460 tunnelProtocol: "OSSH", 461 enableSSHAPIRequests: true, 462 doHotReload: true, 463 doDefaultSponsorID: false, 464 denyTrafficRules: false, 465 requireAuthorization: true, 466 omitAuthorization: false, 467 doTunneledWebRequest: true, 468 doTunneledNTPRequest: true, 469 forceFragmenting: false, 470 forceLivenessTest: false, 471 doPruneServerEntries: false, 472 doDanglingTCPConn: false, 473 doPacketManipulation: false, 474 doBurstMonitor: false, 475 doSplitTunnel: false, 476 limitQUICVersions: false, 477 doDestinationBytes: false, 478 doChangeBytesConfig: false, 479 }) 480 } 481 482 func TestDefaultSponsorID(t *testing.T) { 483 runServer(t, 484 &runServerConfig{ 485 tunnelProtocol: "OSSH", 486 enableSSHAPIRequests: true, 487 doHotReload: true, 488 doDefaultSponsorID: true, 489 denyTrafficRules: false, 490 requireAuthorization: true, 491 omitAuthorization: false, 492 doTunneledWebRequest: true, 493 doTunneledNTPRequest: true, 494 forceFragmenting: false, 495 forceLivenessTest: false, 496 doPruneServerEntries: false, 497 doDanglingTCPConn: false, 498 doPacketManipulation: false, 499 doBurstMonitor: false, 500 doSplitTunnel: false, 501 limitQUICVersions: false, 502 doDestinationBytes: false, 503 doChangeBytesConfig: false, 504 }) 505 } 506 507 func TestDenyTrafficRules(t *testing.T) { 508 runServer(t, 509 &runServerConfig{ 510 tunnelProtocol: "OSSH", 511 enableSSHAPIRequests: true, 512 doHotReload: true, 513 doDefaultSponsorID: false, 514 denyTrafficRules: true, 515 requireAuthorization: true, 516 omitAuthorization: false, 517 doTunneledWebRequest: true, 518 doTunneledNTPRequest: true, 519 forceFragmenting: false, 520 forceLivenessTest: false, 521 doPruneServerEntries: false, 522 doDanglingTCPConn: false, 523 doPacketManipulation: false, 524 doBurstMonitor: false, 525 doSplitTunnel: false, 526 limitQUICVersions: false, 527 doDestinationBytes: false, 528 doChangeBytesConfig: false, 529 }) 530 } 531 532 func TestOmitAuthorization(t *testing.T) { 533 runServer(t, 534 &runServerConfig{ 535 tunnelProtocol: "OSSH", 536 enableSSHAPIRequests: true, 537 doHotReload: true, 538 doDefaultSponsorID: false, 539 denyTrafficRules: false, 540 requireAuthorization: true, 541 omitAuthorization: true, 542 doTunneledWebRequest: true, 543 doTunneledNTPRequest: true, 544 forceFragmenting: false, 545 forceLivenessTest: false, 546 doPruneServerEntries: false, 547 doDanglingTCPConn: false, 548 doPacketManipulation: false, 549 doBurstMonitor: false, 550 doSplitTunnel: false, 551 limitQUICVersions: false, 552 doDestinationBytes: false, 553 doChangeBytesConfig: false, 554 }) 555 } 556 557 func TestNoAuthorization(t *testing.T) { 558 runServer(t, 559 &runServerConfig{ 560 tunnelProtocol: "OSSH", 561 enableSSHAPIRequests: true, 562 doHotReload: true, 563 doDefaultSponsorID: false, 564 denyTrafficRules: false, 565 requireAuthorization: false, 566 omitAuthorization: true, 567 doTunneledWebRequest: true, 568 doTunneledNTPRequest: true, 569 forceFragmenting: false, 570 forceLivenessTest: false, 571 doPruneServerEntries: false, 572 doDanglingTCPConn: false, 573 doPacketManipulation: false, 574 doBurstMonitor: false, 575 doSplitTunnel: false, 576 limitQUICVersions: false, 577 doDestinationBytes: false, 578 doChangeBytesConfig: false, 579 }) 580 } 581 582 func TestUnusedAuthorization(t *testing.T) { 583 runServer(t, 584 &runServerConfig{ 585 tunnelProtocol: "OSSH", 586 enableSSHAPIRequests: true, 587 doHotReload: true, 588 doDefaultSponsorID: false, 589 denyTrafficRules: false, 590 requireAuthorization: false, 591 omitAuthorization: false, 592 doTunneledWebRequest: true, 593 doTunneledNTPRequest: true, 594 forceFragmenting: false, 595 forceLivenessTest: false, 596 doPruneServerEntries: false, 597 doDanglingTCPConn: false, 598 doPacketManipulation: false, 599 doBurstMonitor: false, 600 doSplitTunnel: false, 601 limitQUICVersions: false, 602 doDestinationBytes: false, 603 doChangeBytesConfig: false, 604 }) 605 } 606 607 func TestTCPOnlySLOK(t *testing.T) { 608 runServer(t, 609 &runServerConfig{ 610 tunnelProtocol: "OSSH", 611 enableSSHAPIRequests: true, 612 doHotReload: false, 613 doDefaultSponsorID: false, 614 denyTrafficRules: false, 615 requireAuthorization: true, 616 omitAuthorization: false, 617 doTunneledWebRequest: true, 618 doTunneledNTPRequest: false, 619 forceFragmenting: false, 620 forceLivenessTest: false, 621 doPruneServerEntries: false, 622 doDanglingTCPConn: false, 623 doPacketManipulation: false, 624 doBurstMonitor: false, 625 doSplitTunnel: false, 626 limitQUICVersions: false, 627 doDestinationBytes: false, 628 doChangeBytesConfig: false, 629 }) 630 } 631 632 func TestUDPOnlySLOK(t *testing.T) { 633 runServer(t, 634 &runServerConfig{ 635 tunnelProtocol: "OSSH", 636 enableSSHAPIRequests: true, 637 doHotReload: false, 638 doDefaultSponsorID: false, 639 denyTrafficRules: false, 640 requireAuthorization: true, 641 omitAuthorization: false, 642 doTunneledWebRequest: false, 643 doTunneledNTPRequest: true, 644 forceFragmenting: false, 645 forceLivenessTest: false, 646 doPruneServerEntries: false, 647 doDanglingTCPConn: false, 648 doPacketManipulation: false, 649 doBurstMonitor: false, 650 doSplitTunnel: false, 651 limitQUICVersions: false, 652 doDestinationBytes: false, 653 doChangeBytesConfig: false, 654 }) 655 } 656 657 func TestLivenessTest(t *testing.T) { 658 runServer(t, 659 &runServerConfig{ 660 tunnelProtocol: "OSSH", 661 enableSSHAPIRequests: true, 662 doHotReload: false, 663 doDefaultSponsorID: false, 664 denyTrafficRules: false, 665 requireAuthorization: true, 666 omitAuthorization: false, 667 doTunneledWebRequest: true, 668 doTunneledNTPRequest: true, 669 forceFragmenting: false, 670 forceLivenessTest: true, 671 doPruneServerEntries: false, 672 doDanglingTCPConn: false, 673 doPacketManipulation: false, 674 doBurstMonitor: false, 675 doSplitTunnel: false, 676 limitQUICVersions: false, 677 doDestinationBytes: false, 678 doChangeBytesConfig: false, 679 }) 680 } 681 682 func TestPruneServerEntries(t *testing.T) { 683 runServer(t, 684 &runServerConfig{ 685 tunnelProtocol: "OSSH", 686 enableSSHAPIRequests: true, 687 doHotReload: false, 688 doDefaultSponsorID: false, 689 denyTrafficRules: false, 690 requireAuthorization: true, 691 omitAuthorization: false, 692 doTunneledWebRequest: true, 693 doTunneledNTPRequest: true, 694 forceFragmenting: false, 695 forceLivenessTest: true, 696 doPruneServerEntries: true, 697 doDanglingTCPConn: false, 698 doPacketManipulation: false, 699 doBurstMonitor: false, 700 doSplitTunnel: false, 701 limitQUICVersions: false, 702 doDestinationBytes: false, 703 doChangeBytesConfig: false, 704 }) 705 } 706 707 func TestBurstMonitorAndDestinationBytes(t *testing.T) { 708 runServer(t, 709 &runServerConfig{ 710 tunnelProtocol: "OSSH", 711 enableSSHAPIRequests: true, 712 doHotReload: false, 713 doDefaultSponsorID: false, 714 denyTrafficRules: false, 715 requireAuthorization: true, 716 omitAuthorization: false, 717 doTunneledWebRequest: true, 718 doTunneledNTPRequest: true, 719 forceFragmenting: false, 720 forceLivenessTest: false, 721 doPruneServerEntries: false, 722 doDanglingTCPConn: true, 723 doPacketManipulation: false, 724 doBurstMonitor: true, 725 doSplitTunnel: false, 726 limitQUICVersions: false, 727 doDestinationBytes: true, 728 doChangeBytesConfig: false, 729 }) 730 } 731 732 func TestChangeBytesConfig(t *testing.T) { 733 runServer(t, 734 &runServerConfig{ 735 tunnelProtocol: "OSSH", 736 enableSSHAPIRequests: true, 737 doHotReload: false, 738 doDefaultSponsorID: false, 739 denyTrafficRules: false, 740 requireAuthorization: true, 741 omitAuthorization: false, 742 doTunneledWebRequest: true, 743 doTunneledNTPRequest: true, 744 forceFragmenting: false, 745 forceLivenessTest: false, 746 doPruneServerEntries: false, 747 doDanglingTCPConn: true, 748 doPacketManipulation: false, 749 doBurstMonitor: false, 750 doSplitTunnel: false, 751 limitQUICVersions: false, 752 doDestinationBytes: true, 753 doChangeBytesConfig: true, 754 }) 755 } 756 757 func TestSplitTunnel(t *testing.T) { 758 runServer(t, 759 &runServerConfig{ 760 tunnelProtocol: "OSSH", 761 enableSSHAPIRequests: true, 762 doHotReload: false, 763 doDefaultSponsorID: false, 764 denyTrafficRules: false, 765 requireAuthorization: true, 766 omitAuthorization: false, 767 doTunneledWebRequest: true, 768 doTunneledNTPRequest: true, 769 forceFragmenting: false, 770 forceLivenessTest: false, 771 doPruneServerEntries: false, 772 doDanglingTCPConn: true, 773 doPacketManipulation: false, 774 doBurstMonitor: false, 775 doSplitTunnel: true, 776 limitQUICVersions: false, 777 doDestinationBytes: false, 778 doChangeBytesConfig: false, 779 }) 780 } 781 782 type runServerConfig struct { 783 tunnelProtocol string 784 tlsProfile string 785 enableSSHAPIRequests bool 786 doHotReload bool 787 doDefaultSponsorID bool 788 denyTrafficRules bool 789 requireAuthorization bool 790 omitAuthorization bool 791 doTunneledWebRequest bool 792 doTunneledNTPRequest bool 793 forceFragmenting bool 794 forceLivenessTest bool 795 doPruneServerEntries bool 796 doDanglingTCPConn bool 797 doPacketManipulation bool 798 doBurstMonitor bool 799 doSplitTunnel bool 800 limitQUICVersions bool 801 doDestinationBytes bool 802 doChangeBytesConfig bool 803 } 804 805 var ( 806 testSSHClientVersions = []string{"SSH-2.0-A", "SSH-2.0-B", "SSH-2.0-C"} 807 testUserAgents = []string{"ua1", "ua2", "ua3"} 808 testNetworkType = "WIFI" 809 testCustomHostNameRegex = `[a-z0-9]{5,10}\.example\.org` 810 testClientFeatures = []string{"feature 1", "feature 2"} 811 testDisallowedTrafficAlertActionURLs = []string{"https://example.org/disallowed"} 812 ) 813 814 var serverRuns = 0 815 816 func runServer(t *testing.T, runConfig *runServerConfig) { 817 818 serverRuns += 1 819 820 // configure authorized access 821 822 accessType := "test-access-type" 823 824 accessControlSigningKey, accessControlVerificationKey, err := accesscontrol.NewKeyPair(accessType) 825 if err != nil { 826 t.Fatalf("error creating access control key pair: %s", err) 827 } 828 829 accessControlVerificationKeyRing := accesscontrol.VerificationKeyRing{ 830 Keys: []*accesscontrol.VerificationKey{accessControlVerificationKey}, 831 } 832 833 var seedAuthorizationID [32]byte 834 835 clientAuthorization, authorizationID, err := accesscontrol.IssueAuthorization( 836 accessControlSigningKey, 837 seedAuthorizationID[:], 838 time.Now().Add(1*time.Hour)) 839 if err != nil { 840 t.Fatalf("error issuing authorization: %s", err) 841 } 842 843 authorizationIDStr := base64.StdEncoding.EncodeToString(authorizationID) 844 845 // Enable tactics when the test protocol is meek. Both the client and the 846 // server will be configured to support tactics. The client config will be 847 // set with a nonfunctional config so that the tactics request must 848 // succeed, overriding the nonfunctional values, for the tunnel to 849 // establish. 850 851 doClientTactics := protocol.TunnelProtocolUsesMeek(runConfig.tunnelProtocol) 852 doServerTactics := doClientTactics || 853 runConfig.forceFragmenting || 854 runConfig.doBurstMonitor || 855 runConfig.doDestinationBytes 856 857 // All servers require a tactics config with valid keys. 858 tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey, err := 859 tactics.GenerateKeys() 860 if err != nil { 861 t.Fatalf("error generating tactics keys: %s", err) 862 } 863 864 livenessTestSize := 0 865 if doClientTactics || runConfig.forceLivenessTest { 866 livenessTestSize = 1048576 867 } 868 869 // create a server 870 871 psiphonServerIPAddress := serverIPAddress 872 if protocol.TunnelProtocolUsesQUIC(runConfig.tunnelProtocol) { 873 // Workaround for macOS firewall. 874 psiphonServerIPAddress = "127.0.0.1" 875 } 876 psiphonServerPort := 4000 877 878 var limitQUICVersions protocol.QUICVersions 879 if runConfig.limitQUICVersions { 880 881 // Limit the server entry to one specific QUICv1 version, and check 882 // that this is used (see expectQUICVersion below). This test case 883 // also exercises disabling gQUIC in the server config and 884 // using "QUICv1" as the server entry capability. 885 886 selectedQUICVersion := protocol.SupportedQUICv1Versions[prng.Intn( 887 len(protocol.SupportedQUICv1Versions))] 888 limitQUICVersions = protocol.QUICVersions{selectedQUICVersion} 889 } 890 891 generateConfigParams := &GenerateConfigParams{ 892 ServerIPAddress: psiphonServerIPAddress, 893 EnableSSHAPIRequests: runConfig.enableSSHAPIRequests, 894 WebServerPort: 8000, 895 TunnelProtocolPorts: map[string]int{runConfig.tunnelProtocol: psiphonServerPort}, 896 LimitQUICVersions: limitQUICVersions, 897 EnableGQUIC: !runConfig.limitQUICVersions, 898 } 899 900 if doServerTactics { 901 generateConfigParams.TacticsRequestPublicKey = tacticsRequestPublicKey 902 generateConfigParams.TacticsRequestObfuscatedKey = tacticsRequestObfuscatedKey 903 } 904 905 serverConfigJSON, _, _, _, encodedServerEntry, err := GenerateConfig(generateConfigParams) 906 if err != nil { 907 t.Fatalf("error generating server config: %s", err) 908 } 909 910 // customize server config 911 912 // Initialize prune server entry test cases and associated data to pave into psinet. 913 pruneServerEntryTestCases, psinetValidServerEntryTags, expectedNumPruneNotices := 914 initializePruneServerEntriesTest(t, runConfig) 915 916 // Pave psinet with random values to test handshake homepages. 917 psinetFilename := filepath.Join(testDataDirName, "psinet.json") 918 sponsorID, expectedHomepageURL := pavePsinetDatabaseFile( 919 t, psinetFilename, "", runConfig.doDefaultSponsorID, true, psinetValidServerEntryTags) 920 921 // Pave OSL config for SLOK testing 922 oslConfigFilename := filepath.Join(testDataDirName, "osl_config.json") 923 propagationChannelID := paveOSLConfigFile(t, oslConfigFilename) 924 925 // Pave traffic rules file which exercises handshake parameter filtering. Client 926 // must handshake with specified sponsor ID in order to allow ports for tunneled 927 // requests. 928 trafficRulesFilename := filepath.Join(testDataDirName, "traffic_rules.json") 929 paveTrafficRulesFile( 930 t, 931 trafficRulesFilename, 932 propagationChannelID, 933 accessType, 934 authorizationIDStr, 935 runConfig.requireAuthorization, 936 runConfig.denyTrafficRules, 937 livenessTestSize) 938 939 var tacticsConfigFilename string 940 941 // Only pave the tactics config when tactics are required. This exercises the 942 // case where the tactics config is omitted. 943 if doServerTactics { 944 tacticsConfigFilename = filepath.Join(testDataDirName, "tactics_config.json") 945 paveTacticsConfigFile( 946 t, 947 tacticsConfigFilename, 948 tacticsRequestPublicKey, 949 tacticsRequestPrivateKey, 950 tacticsRequestObfuscatedKey, 951 runConfig.tunnelProtocol, 952 propagationChannelID, 953 livenessTestSize, 954 runConfig.doBurstMonitor, 955 runConfig.doDestinationBytes) 956 } 957 958 blocklistFilename := filepath.Join(testDataDirName, "blocklist.csv") 959 paveBlocklistFile(t, blocklistFilename) 960 961 var serverConfig map[string]interface{} 962 json.Unmarshal(serverConfigJSON, &serverConfig) 963 964 // The test GeoIP databases map all IPs to a single, non-"None" country 965 // and ASN. 966 // 967 // When split tunnel mode is enabled, this should cause port forwards to 968 // be untunneled. When split tunnel mode is not enabled, port forwards 969 // should be tunneled despite the country match. 970 // 971 // When destination bytes metrics are enabled, all traffic will map to the 972 // single ASN. 973 geoIPCityDatabaseFilename := filepath.Join(testDataDirName, "geoip_city_database.mmbd") 974 geoIPISPDatabaseFilename := filepath.Join(testDataDirName, "geoip_isp_database.mmbd") 975 paveGeoIPDatabaseFiles(t, geoIPCityDatabaseFilename, geoIPISPDatabaseFilename) 976 serverConfig["GeoIPDatabaseFilenames"] = []string{geoIPCityDatabaseFilename, geoIPISPDatabaseFilename} 977 978 serverConfig["PsinetDatabaseFilename"] = psinetFilename 979 serverConfig["TrafficRulesFilename"] = trafficRulesFilename 980 serverConfig["OSLConfigFilename"] = oslConfigFilename 981 if doServerTactics { 982 serverConfig["TacticsConfigFilename"] = tacticsConfigFilename 983 } 984 serverConfig["BlocklistFilename"] = blocklistFilename 985 986 serverConfig["LogFilename"] = filepath.Join(testDataDirName, "psiphond.log") 987 serverConfig["LogLevel"] = "debug" 988 989 serverConfig["AccessControlVerificationKeyRing"] = accessControlVerificationKeyRing 990 991 // Set this parameter so at least the semaphore functions are called. 992 // TODO: test that the concurrency limit is correctly enforced. 993 serverConfig["MaxConcurrentSSHHandshakes"] = 1 994 995 // Ensure peak failure rate log fields for a single port forward attempt 996 serverConfig["PeakUpstreamFailureRateMinimumSampleSize"] = 1 997 998 // Exercise this option. 999 serverConfig["PeriodicGarbageCollectionSeconds"] = 1 1000 1001 // Allow port forwards to local test web server. 1002 serverConfig["AllowBogons"] = true 1003 1004 serverConfig["RunPacketManipulator"] = runConfig.doPacketManipulation 1005 1006 if protocol.TunnelProtocolUsesQUIC(runConfig.tunnelProtocol) && quic.GQUICEnabled() { 1007 // Enable legacy QUIC version support. 1008 serverConfig["EnableGQUIC"] = true 1009 } 1010 1011 serverConfigJSON, _ = json.Marshal(serverConfig) 1012 1013 uniqueUserLog := make(chan map[string]interface{}, 1) 1014 domainBytesLog := make(chan map[string]interface{}, 1) 1015 serverTunnelLog := make(chan map[string]interface{}, 1) 1016 1017 setLogCallback(func(log []byte) { 1018 1019 logFields := make(map[string]interface{}) 1020 1021 err := json.Unmarshal(log, &logFields) 1022 if err != nil { 1023 return 1024 } 1025 1026 if logFields["event_name"] == nil { 1027 return 1028 } 1029 1030 switch logFields["event_name"].(string) { 1031 case "unique_user": 1032 select { 1033 case uniqueUserLog <- logFields: 1034 default: 1035 } 1036 case "domain_bytes": 1037 select { 1038 case domainBytesLog <- logFields: 1039 default: 1040 } 1041 case "server_tunnel": 1042 select { 1043 case serverTunnelLog <- logFields: 1044 default: 1045 } 1046 } 1047 }) 1048 1049 // run server 1050 1051 serverWaitGroup := new(sync.WaitGroup) 1052 serverWaitGroup.Add(1) 1053 go func() { 1054 defer serverWaitGroup.Done() 1055 err := RunServices(serverConfigJSON) 1056 if err != nil { 1057 // TODO: wrong goroutine for t.FatalNow() 1058 t.Errorf("error running server: %s", err) 1059 } 1060 }() 1061 1062 stopServer := func() { 1063 1064 // Test: orderly server shutdown 1065 1066 p, _ := os.FindProcess(os.Getpid()) 1067 p.Signal(os.Interrupt) 1068 1069 shutdownTimeout := time.NewTimer(5 * time.Second) 1070 1071 shutdownOk := make(chan struct{}, 1) 1072 go func() { 1073 serverWaitGroup.Wait() 1074 shutdownOk <- struct{}{} 1075 }() 1076 1077 select { 1078 case <-shutdownOk: 1079 case <-shutdownTimeout.C: 1080 t.Errorf("server shutdown timeout exceeded") 1081 } 1082 } 1083 1084 // Stop server on early exits due to failure. 1085 defer func() { 1086 if stopServer != nil { 1087 stopServer() 1088 } 1089 }() 1090 1091 // TODO: monitor logs for more robust wait-until-loaded. For example, 1092 // especially with the race detector on, QUIC-OSSH tests can fail as the 1093 // client sends its initial packet before the server is ready. 1094 time.Sleep(1 * time.Second) 1095 1096 // Test: hot reload (of psinet and traffic rules) 1097 1098 if runConfig.doHotReload { 1099 1100 // Pave new config files with different random values. 1101 sponsorID, expectedHomepageURL = pavePsinetDatabaseFile( 1102 t, psinetFilename, "", runConfig.doDefaultSponsorID, true, psinetValidServerEntryTags) 1103 1104 propagationChannelID = paveOSLConfigFile(t, oslConfigFilename) 1105 1106 paveTrafficRulesFile( 1107 t, 1108 trafficRulesFilename, 1109 propagationChannelID, 1110 accessType, 1111 authorizationIDStr, 1112 runConfig.requireAuthorization, 1113 runConfig.denyTrafficRules, 1114 livenessTestSize) 1115 1116 p, _ := os.FindProcess(os.Getpid()) 1117 p.Signal(syscall.SIGUSR1) 1118 1119 // TODO: monitor logs for more robust wait-until-reloaded 1120 time.Sleep(1 * time.Second) 1121 1122 // After reloading psinet, the new sponsorID/expectedHomepageURL 1123 // should be active, as tested in the client "Homepage" notice 1124 // handler below. 1125 } 1126 1127 // Exercise server_load logging 1128 p, _ := os.FindProcess(os.Getpid()) 1129 p.Signal(syscall.SIGUSR2) 1130 1131 // configure client 1132 1133 values.SetSSHClientVersionsSpec(values.NewPickOneSpec(testSSHClientVersions)) 1134 1135 values.SetUserAgentsSpec(values.NewPickOneSpec(testUserAgents)) 1136 1137 // TODO: currently, TargetServerEntry only works with one tunnel 1138 numTunnels := 1 1139 localSOCKSProxyPort := 1081 1140 localHTTPProxyPort := 8081 1141 1142 // Use a distinct suffix for network ID for each test run to ensure tactics 1143 // from different runs don't apply; this is a workaround for the singleton 1144 // datastore. 1145 jsonNetworkID := fmt.Sprintf(`,"NetworkID" : "WIFI-%s"`, time.Now().String()) 1146 1147 jsonLimitTLSProfiles := "" 1148 if runConfig.tlsProfile != "" { 1149 jsonLimitTLSProfiles = fmt.Sprintf(`,"LimitTLSProfiles" : ["%s"]`, runConfig.tlsProfile) 1150 } 1151 1152 testClientFeaturesJSON, _ := json.Marshal(testClientFeatures) 1153 1154 clientConfigJSON := fmt.Sprintf(` 1155 { 1156 "ClientPlatform" : "Android_10_com.test.app", 1157 "ClientVersion" : "0", 1158 "ClientFeatures" : %s, 1159 "SponsorId" : "0", 1160 "PropagationChannelId" : "0", 1161 "DeviceRegion" : "US", 1162 "DisableRemoteServerListFetcher" : true, 1163 "EstablishTunnelPausePeriodSeconds" : 1, 1164 "ConnectionWorkerPoolSize" : %d, 1165 "LimitTunnelProtocols" : ["%s"] 1166 %s 1167 %s 1168 }`, 1169 string(testClientFeaturesJSON), 1170 numTunnels, 1171 runConfig.tunnelProtocol, 1172 jsonLimitTLSProfiles, 1173 jsonNetworkID) 1174 1175 clientConfig, err := psiphon.LoadConfig([]byte(clientConfigJSON)) 1176 if err != nil { 1177 t.Fatalf("error processing configuration file: %s", err) 1178 } 1179 1180 clientConfig.DataRootDirectory = testDataDirName 1181 1182 if !runConfig.doDefaultSponsorID { 1183 clientConfig.SponsorId = sponsorID 1184 } 1185 clientConfig.PropagationChannelId = propagationChannelID 1186 clientConfig.TunnelPoolSize = numTunnels 1187 clientConfig.TargetServerEntry = string(encodedServerEntry) 1188 clientConfig.LocalSocksProxyPort = localSOCKSProxyPort 1189 clientConfig.LocalHttpProxyPort = localHTTPProxyPort 1190 clientConfig.EmitSLOKs = true 1191 clientConfig.EmitServerAlerts = true 1192 1193 if runConfig.doSplitTunnel { 1194 clientConfig.SplitTunnelOwnRegion = true 1195 } 1196 1197 if !runConfig.omitAuthorization { 1198 clientConfig.Authorizations = []string{clientAuthorization} 1199 } 1200 1201 err = clientConfig.Commit(false) 1202 if err != nil { 1203 t.Fatalf("error committing configuration file: %s", err) 1204 } 1205 1206 if doClientTactics { 1207 // Configure nonfunctional values that must be overridden by tactics. 1208 1209 applyParameters := make(map[string]interface{}) 1210 1211 applyParameters[parameters.TunnelConnectTimeout] = "1s" 1212 applyParameters[parameters.TunnelRateLimits] = common.RateLimits{WriteBytesPerSecond: 1} 1213 1214 err = clientConfig.SetParameters("", true, applyParameters) 1215 if err != nil { 1216 t.Fatalf("SetParameters failed: %s", err) 1217 } 1218 1219 } else { 1220 1221 // Directly apply same parameters that would've come from tactics. 1222 1223 applyParameters := make(map[string]interface{}) 1224 1225 if runConfig.forceFragmenting { 1226 applyParameters[parameters.FragmentorLimitProtocols] = protocol.TunnelProtocols{runConfig.tunnelProtocol} 1227 applyParameters[parameters.FragmentorProbability] = 1.0 1228 applyParameters[parameters.FragmentorMinTotalBytes] = 1000 1229 applyParameters[parameters.FragmentorMaxTotalBytes] = 2000 1230 applyParameters[parameters.FragmentorMinWriteBytes] = 1 1231 applyParameters[parameters.FragmentorMaxWriteBytes] = 100 1232 applyParameters[parameters.FragmentorMinDelay] = 1 * time.Millisecond 1233 applyParameters[parameters.FragmentorMaxDelay] = 10 * time.Millisecond 1234 } 1235 1236 if runConfig.forceLivenessTest { 1237 applyParameters[parameters.LivenessTestMinUpstreamBytes] = livenessTestSize 1238 applyParameters[parameters.LivenessTestMaxUpstreamBytes] = livenessTestSize 1239 applyParameters[parameters.LivenessTestMinDownstreamBytes] = livenessTestSize 1240 applyParameters[parameters.LivenessTestMaxDownstreamBytes] = livenessTestSize 1241 } 1242 1243 if runConfig.doPruneServerEntries { 1244 applyParameters[parameters.PsiphonAPIStatusRequestShortPeriodMin] = 1 * time.Millisecond 1245 applyParameters[parameters.PsiphonAPIStatusRequestShortPeriodMax] = 1 * time.Millisecond 1246 } 1247 1248 err = clientConfig.SetParameters("", true, applyParameters) 1249 if err != nil { 1250 t.Fatalf("SetParameters failed: %s", err) 1251 } 1252 } 1253 1254 // connect to server with client 1255 1256 err = psiphon.OpenDataStore(clientConfig) 1257 if err != nil { 1258 t.Fatalf("error initializing client datastore: %s", err) 1259 } 1260 defer psiphon.CloseDataStore() 1261 1262 // Test unique user counting cases. 1263 var expectUniqueUser bool 1264 switch serverRuns % 3 { 1265 case 0: 1266 // Mock no last_connected. 1267 psiphon.SetKeyValue("lastConnected", "") 1268 expectUniqueUser = true 1269 case 1: 1270 // Mock previous day last_connected. 1271 psiphon.SetKeyValue( 1272 "lastConnected", 1273 time.Now().UTC().AddDate(0, 0, -1).Truncate(1*time.Hour).Format(time.RFC3339)) 1274 expectUniqueUser = true 1275 case 2: 1276 // Leave previous last_connected. 1277 expectUniqueUser = false 1278 } 1279 1280 // Clear SLOKs from previous test runs. 1281 psiphon.DeleteSLOKs() 1282 1283 // Store prune server entry test server entries and failed tunnel records. 1284 storePruneServerEntriesTest( 1285 t, runConfig, testDataDirName, pruneServerEntryTestCases) 1286 1287 controller, err := psiphon.NewController(clientConfig) 1288 if err != nil { 1289 t.Fatalf("error creating client controller: %s", err) 1290 } 1291 1292 connectedServer := make(chan struct{}, 1) 1293 tunnelsEstablished := make(chan struct{}, 1) 1294 homepageReceived := make(chan struct{}, 1) 1295 slokSeeded := make(chan struct{}, 1) 1296 numPruneNotices := 0 1297 pruneServerEntriesNoticesEmitted := make(chan struct{}, 1) 1298 serverAlertDisallowedNoticesEmitted := make(chan struct{}, 1) 1299 untunneledPortForward := make(chan struct{}, 1) 1300 1301 psiphon.SetNoticeWriter(psiphon.NewNoticeReceiver( 1302 func(notice []byte) { 1303 1304 //fmt.Printf("%s\n", string(notice)) 1305 1306 noticeType, payload, err := psiphon.GetNotice(notice) 1307 if err != nil { 1308 return 1309 } 1310 1311 switch noticeType { 1312 1313 case "ConnectedServer": 1314 sendNotificationReceived(connectedServer) 1315 1316 case "Tunnels": 1317 count := int(payload["count"].(float64)) 1318 if count >= numTunnels { 1319 sendNotificationReceived(tunnelsEstablished) 1320 } 1321 1322 case "Homepage": 1323 homepageURL := payload["url"].(string) 1324 if homepageURL != expectedHomepageURL { 1325 // TODO: wrong goroutine for t.FatalNow() 1326 t.Errorf("unexpected homepage: %s", homepageURL) 1327 } 1328 sendNotificationReceived(homepageReceived) 1329 1330 case "SLOKSeeded": 1331 sendNotificationReceived(slokSeeded) 1332 1333 case "PruneServerEntry": 1334 numPruneNotices += 1 1335 if numPruneNotices == expectedNumPruneNotices { 1336 sendNotificationReceived(pruneServerEntriesNoticesEmitted) 1337 } 1338 1339 case "ServerAlert": 1340 1341 reason := payload["reason"].(string) 1342 actionURLsPayload := payload["actionURLs"].([]interface{}) 1343 actionURLs := make([]string, len(actionURLsPayload)) 1344 for i, value := range actionURLsPayload { 1345 actionURLs[i] = value.(string) 1346 } 1347 if reason == protocol.PSIPHON_API_ALERT_DISALLOWED_TRAFFIC && 1348 reflect.DeepEqual(actionURLs, testDisallowedTrafficAlertActionURLs) { 1349 sendNotificationReceived(serverAlertDisallowedNoticesEmitted) 1350 } 1351 1352 case "Untunneled": 1353 sendNotificationReceived(untunneledPortForward) 1354 1355 } 1356 })) 1357 1358 ctx, cancelFunc := context.WithCancel(context.Background()) 1359 1360 controllerWaitGroup := new(sync.WaitGroup) 1361 1362 controllerWaitGroup.Add(1) 1363 go func() { 1364 defer controllerWaitGroup.Done() 1365 controller.Run(ctx) 1366 }() 1367 1368 stopClient := func() { 1369 cancelFunc() 1370 1371 shutdownTimeout := time.NewTimer(20 * time.Second) 1372 1373 shutdownOk := make(chan struct{}, 1) 1374 go func() { 1375 controllerWaitGroup.Wait() 1376 shutdownOk <- struct{}{} 1377 }() 1378 1379 select { 1380 case <-shutdownOk: 1381 case <-shutdownTimeout.C: 1382 t.Errorf("controller shutdown timeout exceeded") 1383 } 1384 } 1385 1386 // Stop client on early exits due to failure. 1387 defer func() { 1388 if stopClient != nil { 1389 stopClient() 1390 } 1391 }() 1392 1393 // Test: tunnels must be established, and correct homepage 1394 // must be received, within 30 seconds 1395 1396 timeoutSignal := make(chan struct{}) 1397 go func() { 1398 timer := time.NewTimer(30 * time.Second) 1399 <-timer.C 1400 close(timeoutSignal) 1401 }() 1402 1403 waitOnNotification(t, connectedServer, timeoutSignal, "connected server timeout exceeded") 1404 waitOnNotification(t, tunnelsEstablished, timeoutSignal, "tunnel established timeout exceeded") 1405 waitOnNotification(t, homepageReceived, timeoutSignal, "homepage received timeout exceeded") 1406 1407 if runConfig.doChangeBytesConfig { 1408 1409 if !runConfig.doDestinationBytes { 1410 t.Fatalf("invalid test configuration") 1411 } 1412 1413 // Test: now that the client is connected, change the domain bytes and 1414 // destination bytes configurations. No stats should be logged, even 1415 // with an already connected client. 1416 1417 // Pave psinet without domain bytes; retain the same sponsor ID. The 1418 // random homepage URLs will change, but this has no effect on the 1419 // already connected client. 1420 _, _ = pavePsinetDatabaseFile( 1421 t, psinetFilename, sponsorID, runConfig.doDefaultSponsorID, false, psinetValidServerEntryTags) 1422 1423 // Pave tactics without destination bytes. 1424 paveTacticsConfigFile( 1425 t, 1426 tacticsConfigFilename, 1427 tacticsRequestPublicKey, 1428 tacticsRequestPrivateKey, 1429 tacticsRequestObfuscatedKey, 1430 runConfig.tunnelProtocol, 1431 propagationChannelID, 1432 livenessTestSize, 1433 runConfig.doBurstMonitor, 1434 false) 1435 1436 p, _ := os.FindProcess(os.Getpid()) 1437 p.Signal(syscall.SIGUSR1) 1438 1439 // TODO: monitor logs for more robust wait-until-reloaded 1440 time.Sleep(1 * time.Second) 1441 } 1442 1443 expectTrafficFailure := runConfig.denyTrafficRules || (runConfig.omitAuthorization && runConfig.requireAuthorization) 1444 1445 // The client still reports zero domain_bytes when no port forwards are allowed (expectTrafficFailure) 1446 expectDomainBytes := !runConfig.doChangeBytesConfig 1447 1448 if runConfig.doTunneledWebRequest { 1449 1450 // Test: tunneled web site fetch 1451 1452 err = makeTunneledWebRequest( 1453 t, localHTTPProxyPort, mockWebServerURL, mockWebServerExpectedResponse) 1454 1455 if err == nil { 1456 if expectTrafficFailure { 1457 t.Fatalf("unexpected tunneled web request success") 1458 } 1459 } else { 1460 if !expectTrafficFailure { 1461 t.Fatalf("tunneled web request failed: %s", err) 1462 } 1463 } 1464 } 1465 1466 if runConfig.doTunneledNTPRequest { 1467 1468 // Test: tunneled UDP packets 1469 1470 udpgwServerAddress := serverConfig["UDPInterceptUdpgwServerAddress"].(string) 1471 1472 err = makeTunneledNTPRequest(t, localSOCKSProxyPort, udpgwServerAddress) 1473 1474 if err == nil { 1475 if expectTrafficFailure { 1476 t.Fatalf("unexpected tunneled NTP request success") 1477 } 1478 } else { 1479 if !expectTrafficFailure { 1480 t.Fatalf("tunneled NTP request failed: %s", err) 1481 } 1482 } 1483 } 1484 1485 // Test: await SLOK payload or server alert notice 1486 1487 time.Sleep(1 * time.Second) 1488 1489 if !expectTrafficFailure { 1490 1491 waitOnNotification(t, slokSeeded, timeoutSignal, "SLOK seeded timeout exceeded") 1492 1493 numSLOKs := psiphon.CountSLOKs() 1494 if numSLOKs != expectedNumSLOKs { 1495 t.Fatalf("unexpected number of SLOKs: %d", numSLOKs) 1496 } 1497 1498 } else { 1499 1500 // Note: in expectTrafficFailure case, timeoutSignal may have already fired. 1501 1502 waitOnNotification(t, serverAlertDisallowedNoticesEmitted, nil, "") 1503 } 1504 1505 // Test: await expected prune server entry notices 1506 // 1507 // Note: will take up to PsiphonAPIStatusRequestShortPeriodMax to emit. 1508 1509 if expectedNumPruneNotices > 0 { 1510 waitOnNotification(t, pruneServerEntriesNoticesEmitted, nil, "") 1511 } 1512 1513 if runConfig.doDanglingTCPConn { 1514 1515 // Test: client that has established TCP connection but not completed 1516 // any handshakes must not block/delay server shutdown 1517 1518 danglingConn, err := net.Dial( 1519 "tcp", net.JoinHostPort(psiphonServerIPAddress, strconv.Itoa(psiphonServerPort))) 1520 if err != nil { 1521 t.Fatalf("TCP dial failed: %s", err) 1522 } 1523 defer danglingConn.Close() 1524 } 1525 1526 // Test: check for split tunnel notice 1527 1528 if runConfig.doSplitTunnel { 1529 if !runConfig.doTunneledWebRequest || expectTrafficFailure { 1530 t.Fatalf("invalid test run configuration") 1531 } 1532 waitOnNotification(t, untunneledPortForward, nil, "") 1533 } else { 1534 // There should be no "Untunneled" notice. This check assumes that any 1535 // unexpected Untunneled notice will have been delivered at this point, 1536 // after the SLOK notice. 1537 select { 1538 case <-untunneledPortForward: 1539 t.Fatalf("unexpected untunnedl port forward") 1540 default: 1541 } 1542 } 1543 1544 // Trigger server_load logging once more, to exercise 1545 // sshClient.peakMetrics. As we don't have a reference to the server's 1546 // Support struct, we can't invoke logServerLoad directly and there's a 1547 // potential race between asynchronous logServerLoad invocation and 1548 // client shutdown. For now, we sleep as a workaround. 1549 1550 p.Signal(syscall.SIGUSR2) 1551 time.Sleep(1 * time.Second) 1552 1553 // Shutdown to ensure logs/notices are flushed 1554 1555 stopClient() 1556 stopClient = nil 1557 stopServer() 1558 stopServer = nil 1559 1560 // Test: all expected server logs were emitted 1561 1562 // TODO: stops should be fully synchronous, but, intermittently, 1563 // server_tunnel fails to appear ("missing server tunnel log") 1564 // without this delay. 1565 time.Sleep(100 * time.Millisecond) 1566 1567 expectClientBPFField := psiphon.ClientBPFEnabled() && doClientTactics 1568 expectServerBPFField := ServerBPFEnabled() && doServerTactics 1569 expectServerPacketManipulationField := runConfig.doPacketManipulation 1570 expectBurstFields := runConfig.doBurstMonitor 1571 expectTCPPortForwardDial := runConfig.doTunneledWebRequest 1572 expectTCPDataTransfer := runConfig.doTunneledWebRequest && !expectTrafficFailure && !runConfig.doSplitTunnel 1573 // Even with expectTrafficFailure, DNS port forwards will succeed 1574 expectUDPDataTransfer := runConfig.doTunneledNTPRequest 1575 expectQUICVersion := "" 1576 if runConfig.limitQUICVersions { 1577 expectQUICVersion = limitQUICVersions[0] 1578 } 1579 expectDestinationBytesFields := runConfig.doDestinationBytes && !runConfig.doChangeBytesConfig 1580 1581 select { 1582 case logFields := <-serverTunnelLog: 1583 err := checkExpectedServerTunnelLogFields( 1584 runConfig, 1585 expectClientBPFField, 1586 expectServerBPFField, 1587 expectServerPacketManipulationField, 1588 expectBurstFields, 1589 expectTCPPortForwardDial, 1590 expectTCPDataTransfer, 1591 expectUDPDataTransfer, 1592 expectQUICVersion, 1593 expectDestinationBytesFields, 1594 logFields) 1595 if err != nil { 1596 t.Fatalf("invalid server tunnel log fields: %s", err) 1597 } 1598 default: 1599 t.Fatalf("missing server tunnel log") 1600 } 1601 1602 if expectUniqueUser { 1603 select { 1604 case logFields := <-uniqueUserLog: 1605 err := checkExpectedUniqueUserLogFields( 1606 runConfig, 1607 logFields) 1608 if err != nil { 1609 t.Fatalf("invalid unique user log fields: %s", err) 1610 } 1611 default: 1612 t.Fatalf("missing unique user log") 1613 } 1614 } else { 1615 select { 1616 case <-uniqueUserLog: 1617 t.Fatalf("unexpected unique user log") 1618 default: 1619 } 1620 } 1621 1622 if expectDomainBytes { 1623 select { 1624 case logFields := <-domainBytesLog: 1625 err := checkExpectedDomainBytesLogFields( 1626 runConfig, 1627 logFields) 1628 if err != nil { 1629 t.Fatalf("invalid domain bytes log fields: %s", err) 1630 } 1631 default: 1632 t.Fatalf("missing domain bytes log") 1633 } 1634 } else { 1635 select { 1636 case <-domainBytesLog: 1637 t.Fatalf("unexpected domain bytes log") 1638 default: 1639 } 1640 } 1641 1642 // Check that datastore had retained/pruned server entries as expected. 1643 checkPruneServerEntriesTest(t, runConfig, testDataDirName, pruneServerEntryTestCases) 1644 } 1645 1646 func sendNotificationReceived(c chan<- struct{}) { 1647 select { 1648 case c <- struct{}{}: 1649 default: 1650 } 1651 } 1652 1653 func waitOnNotification(t *testing.T, c, timeoutSignal <-chan struct{}, timeoutMessage string) { 1654 if timeoutSignal == nil { 1655 <-c 1656 } else { 1657 select { 1658 case <-c: 1659 case <-timeoutSignal: 1660 t.Fatalf(timeoutMessage) 1661 } 1662 } 1663 } 1664 1665 func checkExpectedServerTunnelLogFields( 1666 runConfig *runServerConfig, 1667 expectClientBPFField bool, 1668 expectServerBPFField bool, 1669 expectServerPacketManipulationField bool, 1670 expectBurstFields bool, 1671 expectTCPPortForwardDial bool, 1672 expectTCPDataTransfer bool, 1673 expectUDPDataTransfer bool, 1674 expectQUICVersion string, 1675 expectDestinationBytesFields bool, 1676 fields map[string]interface{}) error { 1677 1678 // Limitations: 1679 // 1680 // - client_build_rev not set in test build (see common/buildinfo.go) 1681 // - egress_region, upstream_proxy_type, upstream_proxy_custom_header_names not exercised in test 1682 // - fronting_provider_id/meek_dial_ip_address/meek_resolved_ip_address only logged for FRONTED meek protocols 1683 1684 for _, name := range []string{ 1685 "start_time", 1686 "duration", 1687 "session_id", 1688 "is_first_tunnel_in_session", 1689 "last_connected", 1690 "establishment_duration", 1691 "propagation_channel_id", 1692 "sponsor_id", 1693 "client_platform", 1694 "client_features", 1695 "relay_protocol", 1696 "device_region", 1697 "ssh_client_version", 1698 "server_entry_region", 1699 "server_entry_source", 1700 "server_entry_timestamp", 1701 "dial_port_number", 1702 "is_replay", 1703 "dial_duration", 1704 "candidate_number", 1705 "established_tunnels_count", 1706 "network_latency_multiplier", 1707 "network_type", 1708 1709 // The test run ensures that logServerLoad is invoked while the client 1710 // is connected, so the following must be logged. 1711 "peak_concurrent_proximate_accepted_clients", 1712 "peak_concurrent_proximate_established_clients", 1713 } { 1714 if fields[name] == nil || fmt.Sprintf("%s", fields[name]) == "" { 1715 return fmt.Errorf("missing expected field '%s'", name) 1716 } 1717 } 1718 1719 if fields["relay_protocol"].(string) != runConfig.tunnelProtocol { 1720 return fmt.Errorf("unexpected relay_protocol '%s'", fields["relay_protocol"]) 1721 } 1722 1723 if !common.Contains(testSSHClientVersions, fields["ssh_client_version"].(string)) { 1724 return fmt.Errorf("unexpected ssh_client_version '%s'", fields["ssh_client_version"]) 1725 } 1726 1727 clientFeatures := fields["client_features"].([]interface{}) 1728 if len(clientFeatures) != len(testClientFeatures) { 1729 return fmt.Errorf("unexpected client_features '%s'", fields["client_features"]) 1730 } 1731 for i, feature := range testClientFeatures { 1732 if clientFeatures[i].(string) != feature { 1733 return fmt.Errorf("unexpected client_features '%s'", fields["client_features"]) 1734 } 1735 } 1736 1737 if fields["network_type"].(string) != testNetworkType { 1738 return fmt.Errorf("unexpected network_type '%s'", fields["network_type"]) 1739 } 1740 1741 // With interruptions, timeouts, and retries in some tests, there may be 1742 // more than one dangling accepted_client. 1743 1744 peakConcurrentProximateAcceptedClients := 1745 int(fields["peak_concurrent_proximate_accepted_clients"].(float64)) 1746 if peakConcurrentProximateAcceptedClients < 0 || 1747 peakConcurrentProximateAcceptedClients > 10 { 1748 return fmt.Errorf( 1749 "unexpected peak_concurrent_proximate_accepted_clients '%v'", 1750 fields["peak_concurrent_proximate_accepted_clients"]) 1751 } 1752 1753 peakConcurrentProximateEstablishedClients := 1754 int(fields["peak_concurrent_proximate_established_clients"].(float64)) 1755 if peakConcurrentProximateEstablishedClients != 0 { 1756 return fmt.Errorf( 1757 "unexpected peak_concurrent_proximate_established_clients '%v'", 1758 fields["peak_concurrent_proximate_established_clients"]) 1759 } 1760 1761 // In some negative test cases, no port forwards are attempted, in which 1762 // case these fields are not logged. 1763 1764 if expectTCPDataTransfer { 1765 1766 if fields["peak_tcp_port_forward_failure_rate"] == nil { 1767 return fmt.Errorf("missing expected field 'peak_tcp_port_forward_failure_rate'") 1768 } 1769 if fields["peak_tcp_port_forward_failure_rate"].(float64) != 0.0 { 1770 return fmt.Errorf( 1771 "unexpected peak_tcp_port_forward_failure_rate '%v'", 1772 fields["peak_tcp_port_forward_failure_rate"]) 1773 } 1774 1775 if fields["peak_tcp_port_forward_failure_rate_sample_size"] == nil { 1776 return fmt.Errorf("missing expected field 'peak_tcp_port_forward_failure_rate_sample_size'") 1777 } 1778 if fields["peak_tcp_port_forward_failure_rate_sample_size"].(float64) <= 0.0 { 1779 return fmt.Errorf( 1780 "unexpected peak_tcp_port_forward_failure_rate_sample_size '%v'", 1781 fields["peak_tcp_port_forward_failure_rate_sample_size"]) 1782 } 1783 1784 } else { 1785 1786 if fields["peak_tcp_port_forward_failure_rate"] != nil { 1787 return fmt.Errorf("unexpected field 'peak_tcp_port_forward_failure_rate'") 1788 } 1789 1790 if fields["peak_tcp_port_forward_failure_rate_sample_size"] != nil { 1791 return fmt.Errorf("unexpected field 'peak_tcp_port_forward_failure_rate_sample_size'") 1792 } 1793 } 1794 1795 if expectUDPDataTransfer { 1796 1797 if fields["peak_dns_failure_rate"] == nil { 1798 return fmt.Errorf("missing expected field 'peak_dns_failure_rate'") 1799 } 1800 if fields["peak_dns_failure_rate"].(float64) != 0.0 { 1801 return fmt.Errorf( 1802 "unexpected peak_dns_failure_rate '%v'", fields["peak_dns_failure_rate"]) 1803 } 1804 1805 if fields["peak_dns_failure_rate_sample_size"] == nil { 1806 return fmt.Errorf("missing expected field 'peak_dns_failure_rate_sample_size'") 1807 } 1808 if fields["peak_dns_failure_rate_sample_size"].(float64) <= 0.0 { 1809 return fmt.Errorf( 1810 "unexpected peak_dns_failure_rate_sample_size '%v'", 1811 fields["peak_dns_failure_rate_sample_size"]) 1812 } 1813 1814 } else { 1815 1816 if fields["peak_dns_failure_rate"] != nil { 1817 return fmt.Errorf("unexpected field 'peak_dns_failure_rate'") 1818 } 1819 1820 if fields["peak_dns_failure_rate_sample_size"] != nil { 1821 return fmt.Errorf("unexpected field 'peak_dns_failure_rate_sample_size'") 1822 } 1823 } 1824 1825 // TODO: the following cases should check that fields are not logged when 1826 // not expected. 1827 1828 if runConfig.doSplitTunnel { 1829 1830 if fields["split_tunnel"] == nil { 1831 return fmt.Errorf("missing expected field 'split_tunnel'") 1832 } 1833 if fields["split_tunnel"].(bool) != true { 1834 return fmt.Errorf("missing split_tunnel value") 1835 } 1836 } 1837 1838 if protocol.TunnelProtocolUsesObfuscatedSSH(runConfig.tunnelProtocol) { 1839 1840 for _, name := range []string{ 1841 "padding", 1842 "pad_response", 1843 } { 1844 if fields[name] == nil || fmt.Sprintf("%s", fields[name]) == "" { 1845 return fmt.Errorf("missing expected field '%s'", name) 1846 } 1847 } 1848 } 1849 1850 if protocol.TunnelProtocolUsesMeek(runConfig.tunnelProtocol) { 1851 1852 for _, name := range []string{ 1853 "user_agent", 1854 "meek_transformed_host_name", 1855 "meek_cookie_size", 1856 "meek_limit_request", 1857 "meek_underlying_connection_count", 1858 tactics.APPLIED_TACTICS_TAG_PARAMETER_NAME, 1859 } { 1860 if fields[name] == nil || fmt.Sprintf("%s", fields[name]) == "" { 1861 return fmt.Errorf("missing expected field '%s'", name) 1862 } 1863 } 1864 1865 if !common.Contains(testUserAgents, fields["user_agent"].(string)) { 1866 return fmt.Errorf("unexpected user_agent '%s'", fields["user_agent"]) 1867 } 1868 } 1869 1870 if protocol.TunnelProtocolUsesMeekHTTP(runConfig.tunnelProtocol) { 1871 1872 for _, name := range []string{ 1873 "meek_host_header", 1874 } { 1875 if fields[name] == nil || fmt.Sprintf("%s", fields[name]) == "" { 1876 return fmt.Errorf("missing expected field '%s'", name) 1877 } 1878 } 1879 1880 hostName := fields["meek_host_header"].(string) 1881 dialPortNumber := int(fields["dial_port_number"].(float64)) 1882 if dialPortNumber != 80 { 1883 hostName, _, _ = net.SplitHostPort(hostName) 1884 } 1885 if regexp.MustCompile(testCustomHostNameRegex).FindString(hostName) != hostName { 1886 return fmt.Errorf("unexpected meek_host_header '%s'", fields["meek_host_header"]) 1887 } 1888 1889 for _, name := range []string{ 1890 "meek_dial_ip_address", 1891 "meek_resolved_ip_address", 1892 } { 1893 if fields[name] != nil { 1894 return fmt.Errorf("unexpected field '%s'", name) 1895 } 1896 } 1897 } 1898 1899 if protocol.TunnelProtocolUsesMeekHTTPS(runConfig.tunnelProtocol) { 1900 1901 for _, name := range []string{ 1902 "tls_profile", 1903 "tls_version", 1904 "meek_sni_server_name", 1905 } { 1906 if fields[name] == nil || fmt.Sprintf("%s", fields[name]) == "" { 1907 return fmt.Errorf("missing expected field '%s'", name) 1908 } 1909 } 1910 1911 hostName := fields["meek_sni_server_name"].(string) 1912 if regexp.MustCompile(testCustomHostNameRegex).FindString(hostName) != hostName { 1913 return fmt.Errorf("unexpected meek_sni_server_name '%s'", fields["meek_sni_server_name"]) 1914 } 1915 1916 for _, name := range []string{ 1917 "meek_dial_ip_address", 1918 "meek_resolved_ip_address", 1919 "meek_host_header", 1920 } { 1921 if fields[name] != nil { 1922 return fmt.Errorf("unexpected field '%s'", name) 1923 } 1924 } 1925 1926 if !common.Contains(protocol.SupportedTLSProfiles, fields["tls_profile"].(string)) { 1927 return fmt.Errorf("unexpected tls_profile '%s'", fields["tls_profile"]) 1928 } 1929 1930 tlsVersion := fields["tls_version"].(string) 1931 if !strings.HasPrefix(tlsVersion, protocol.TLS_VERSION_12) && 1932 !strings.HasPrefix(tlsVersion, protocol.TLS_VERSION_13) { 1933 return fmt.Errorf("unexpected tls_version '%s'", fields["tls_version"]) 1934 } 1935 } 1936 1937 if protocol.TunnelProtocolUsesQUIC(runConfig.tunnelProtocol) { 1938 1939 for _, name := range []string{ 1940 "quic_version", 1941 "quic_dial_sni_address", 1942 } { 1943 if fields[name] == nil || fmt.Sprintf("%s", fields[name]) == "" { 1944 return fmt.Errorf("missing expected field '%s'", name) 1945 } 1946 } 1947 1948 quicVersion := fields["quic_version"].(string) 1949 if !common.Contains(protocol.SupportedQUICVersions, quicVersion) || 1950 (runConfig.limitQUICVersions && quicVersion != expectQUICVersion) { 1951 1952 return fmt.Errorf("unexpected quic_version '%s'", fields["quic_version"]) 1953 } 1954 } 1955 1956 if runConfig.forceFragmenting { 1957 1958 for _, name := range []string{ 1959 "upstream_bytes_fragmented", 1960 "upstream_min_bytes_written", 1961 "upstream_max_bytes_written", 1962 "upstream_min_delayed", 1963 "upstream_max_delayed", 1964 } { 1965 if fields[name] == nil || fmt.Sprintf("%s", fields[name]) == "" { 1966 return fmt.Errorf("missing expected field '%s'", name) 1967 } 1968 } 1969 } 1970 1971 if expectClientBPFField { 1972 name := "client_bpf" 1973 if fields[name] == nil { 1974 return fmt.Errorf("missing expected field '%s'", name) 1975 } else if fmt.Sprintf("%s", fields[name]) != "test-client-bpf" { 1976 return fmt.Errorf("unexpected field value %s: '%s'", name, fields[name]) 1977 } 1978 } 1979 1980 if expectServerBPFField { 1981 name := "server_bpf" 1982 if fields[name] == nil { 1983 return fmt.Errorf("missing expected field '%s'", name) 1984 } else if fmt.Sprintf("%s", fields[name]) != "test-server-bpf" { 1985 return fmt.Errorf("unexpected field value %s: '%s'", name, fields[name]) 1986 } 1987 } 1988 1989 if expectServerPacketManipulationField { 1990 name := "server_packet_manipulation" 1991 if fields[name] == nil { 1992 return fmt.Errorf("missing expected field '%s'", name) 1993 } else if fmt.Sprintf("%s", fields[name]) != "test-packetman-spec" { 1994 return fmt.Errorf("unexpected field value %s: '%s'", name, fields[name]) 1995 } 1996 } 1997 1998 if expectBurstFields { 1999 2000 // common.TestBurstMonitoredConn covers inclusion of additional fields. 2001 for _, name := range []string{ 2002 "burst_upstream_first_rate", 2003 "burst_upstream_last_rate", 2004 "burst_upstream_min_rate", 2005 "burst_upstream_max_rate", 2006 "burst_downstream_first_rate", 2007 "burst_downstream_last_rate", 2008 "burst_downstream_min_rate", 2009 "burst_downstream_max_rate", 2010 } { 2011 if fields[name] == nil || fmt.Sprintf("%s", fields[name]) == "" { 2012 return fmt.Errorf("missing expected field '%s'", name) 2013 } 2014 } 2015 } 2016 2017 var checkTCPMetric func(float64) bool 2018 if expectTCPPortForwardDial { 2019 checkTCPMetric = func(f float64) bool { return f > 0 } 2020 } else { 2021 checkTCPMetric = func(f float64) bool { return f == 0 } 2022 } 2023 2024 for _, name := range []string{ 2025 "peak_concurrent_dialing_port_forward_count_tcp", 2026 } { 2027 if fields[name] == nil { 2028 return fmt.Errorf("missing expected field '%s'", name) 2029 } 2030 if !checkTCPMetric(fields[name].(float64)) { 2031 return fmt.Errorf("unexpected field value %s: '%v'", name, fields[name]) 2032 } 2033 } 2034 2035 if expectTCPDataTransfer { 2036 checkTCPMetric = func(f float64) bool { return f > 0 } 2037 } else { 2038 checkTCPMetric = func(f float64) bool { return f == 0 } 2039 } 2040 2041 for _, name := range []string{ 2042 "bytes_up_tcp", 2043 "bytes_down_tcp", 2044 "peak_concurrent_port_forward_count_tcp", 2045 "total_port_forward_count_tcp", 2046 } { 2047 if fields[name] == nil { 2048 return fmt.Errorf("missing expected field '%s'", name) 2049 } 2050 if !checkTCPMetric(fields[name].(float64)) { 2051 return fmt.Errorf("unexpected field value %s: '%v'", name, fields[name]) 2052 } 2053 } 2054 2055 var checkUDPMetric func(float64) bool 2056 if expectUDPDataTransfer { 2057 checkUDPMetric = func(f float64) bool { return f > 0 } 2058 } else { 2059 checkUDPMetric = func(f float64) bool { return f == 0 } 2060 } 2061 2062 for _, name := range []string{ 2063 "bytes_up_udp", 2064 "bytes_down_udp", 2065 "peak_concurrent_port_forward_count_udp", 2066 "total_port_forward_count_udp", 2067 "total_udpgw_channel_count", 2068 } { 2069 if fields[name] == nil { 2070 return fmt.Errorf("missing expected field '%s'", name) 2071 } 2072 if !checkUDPMetric(fields[name].(float64)) { 2073 return fmt.Errorf("unexpected field value %s: '%v'", name, fields[name]) 2074 } 2075 } 2076 2077 for _, name := range []string{ 2078 "dest_bytes_asn", 2079 "dest_bytes_up_tcp", 2080 "dest_bytes_down_tcp", 2081 "dest_bytes_up_udp", 2082 "dest_bytes_down_udp", 2083 "dest_bytes", 2084 } { 2085 if expectDestinationBytesFields && fields[name] == nil { 2086 return fmt.Errorf("missing expected field '%s'", name) 2087 2088 } else if !expectDestinationBytesFields && fields[name] != nil { 2089 return fmt.Errorf("unexpected field '%s'", name) 2090 } 2091 } 2092 2093 if expectDestinationBytesFields { 2094 name := "dest_bytes_asn" 2095 if fields[name].(string) != testGeoIPASN { 2096 return fmt.Errorf("unexpected field value %s: '%v'", name, fields[name]) 2097 } 2098 for _, pair := range [][]string{ 2099 []string{"dest_bytes_up_tcp", "bytes_up_tcp"}, 2100 []string{"dest_bytes_down_tcp", "bytes_down_tcp"}, 2101 []string{"dest_bytes_up_udp", "bytes_up_udp"}, 2102 []string{"dest_bytes_down_udp", "bytes_down_udp"}, 2103 []string{"dest_bytes", "bytes"}, 2104 } { 2105 value0 := int64(fields[pair[0]].(float64)) 2106 value1 := int64(fields[pair[1]].(float64)) 2107 ok := value0 == value1 2108 if pair[0] == "dest_bytes_up_udp" || pair[0] == "dest_bytes_down_udp" || pair[0] == "dest_bytes" { 2109 // DNS requests are excluded from destination bytes counting 2110 ok = value0 > 0 && value0 < value1 2111 } 2112 if !ok { 2113 return fmt.Errorf("unexpected field value %s: %v != %v", pair[0], fields[pair[0]], fields[pair[1]]) 2114 } 2115 } 2116 } 2117 2118 return nil 2119 } 2120 2121 func checkExpectedUniqueUserLogFields( 2122 runConfig *runServerConfig, 2123 fields map[string]interface{}) error { 2124 2125 for _, name := range []string{ 2126 "session_id", 2127 "last_connected", 2128 "propagation_channel_id", 2129 "sponsor_id", 2130 "client_platform", 2131 "device_region", 2132 } { 2133 if fields[name] == nil || fmt.Sprintf("%s", fields[name]) == "" { 2134 return fmt.Errorf("missing expected field '%s'", name) 2135 } 2136 } 2137 2138 return nil 2139 } 2140 2141 func checkExpectedDomainBytesLogFields( 2142 runConfig *runServerConfig, 2143 fields map[string]interface{}) error { 2144 2145 for _, name := range []string{ 2146 "session_id", 2147 "propagation_channel_id", 2148 "sponsor_id", 2149 "client_platform", 2150 "device_region", 2151 "domain", 2152 "bytes", 2153 } { 2154 if fields[name] == nil || fmt.Sprintf("%s", fields[name]) == "" { 2155 return fmt.Errorf("missing expected field '%s'", name) 2156 } 2157 2158 if name == "domain" { 2159 if fields[name].(string) != "ALL" && fields[name].(string) != "(OTHER)" { 2160 return fmt.Errorf("unexpected field value %s: '%v'", name, fields[name]) 2161 } 2162 } 2163 } 2164 2165 return nil 2166 } 2167 2168 func makeTunneledWebRequest( 2169 t *testing.T, 2170 localHTTPProxyPort int, 2171 requestURL, expectedResponseBody string) error { 2172 2173 roundTripTimeout := 30 * time.Second 2174 2175 proxyUrl, err := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", localHTTPProxyPort)) 2176 if err != nil { 2177 return fmt.Errorf("error initializing proxied HTTP request: %s", err) 2178 } 2179 2180 httpClient := &http.Client{ 2181 Transport: &http.Transport{ 2182 Proxy: http.ProxyURL(proxyUrl), 2183 }, 2184 Timeout: roundTripTimeout, 2185 } 2186 2187 response, err := httpClient.Get(requestURL) 2188 if err != nil { 2189 return fmt.Errorf("error sending proxied HTTP request: %s", err) 2190 } 2191 2192 body, err := ioutil.ReadAll(response.Body) 2193 if err != nil { 2194 return fmt.Errorf("error reading proxied HTTP response: %s", err) 2195 } 2196 response.Body.Close() 2197 2198 if string(body) != expectedResponseBody { 2199 return fmt.Errorf("unexpected proxied HTTP response") 2200 } 2201 2202 return nil 2203 } 2204 2205 func makeTunneledNTPRequest(t *testing.T, localSOCKSProxyPort int, udpgwServerAddress string) error { 2206 2207 timeout := 20 * time.Second 2208 var err error 2209 2210 testHostnames := []string{"time.google.com", "time.nist.gov", "pool.ntp.org"} 2211 indexes := prng.Perm(len(testHostnames)) 2212 2213 for _, index := range indexes { 2214 testHostname := testHostnames[index] 2215 err = makeTunneledNTPRequestAttempt(t, testHostname, timeout, localSOCKSProxyPort, udpgwServerAddress) 2216 if err == nil { 2217 break 2218 } 2219 t.Logf("makeTunneledNTPRequestAttempt failed: %s", err) 2220 } 2221 2222 return err 2223 } 2224 2225 var nextUDPProxyPort = 7300 2226 2227 func makeTunneledNTPRequestAttempt( 2228 t *testing.T, testHostname string, timeout time.Duration, localSOCKSProxyPort int, udpgwServerAddress string) error { 2229 2230 nextUDPProxyPort++ 2231 localUDPProxyAddress, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", nextUDPProxyPort)) 2232 if err != nil { 2233 return fmt.Errorf("ResolveUDPAddr failed: %s", err) 2234 } 2235 2236 // Note: this proxy is intended for this test only -- it only accepts a single connection, 2237 // handles it, and then terminates. 2238 2239 localUDPProxy := func(destinationIP net.IP, destinationPort uint16, waitGroup *sync.WaitGroup) { 2240 2241 if waitGroup != nil { 2242 defer waitGroup.Done() 2243 } 2244 2245 destination := net.JoinHostPort(destinationIP.String(), strconv.Itoa(int(destinationPort))) 2246 2247 serverUDPConn, err := net.ListenUDP("udp", localUDPProxyAddress) 2248 if err != nil { 2249 t.Logf("ListenUDP for %s failed: %s", destination, err) 2250 return 2251 } 2252 defer serverUDPConn.Close() 2253 2254 udpgwPreambleSize := 11 // see writeUdpgwPreamble 2255 buffer := make([]byte, udpgwProtocolMaxMessageSize) 2256 packetSize, clientAddr, err := serverUDPConn.ReadFromUDP( 2257 buffer[udpgwPreambleSize:]) 2258 if err != nil { 2259 t.Logf("serverUDPConn.Read for %s failed: %s", destination, err) 2260 return 2261 } 2262 2263 socksProxyAddress := fmt.Sprintf("127.0.0.1:%d", localSOCKSProxyPort) 2264 2265 dialer, err := proxy.SOCKS5("tcp", socksProxyAddress, nil, proxy.Direct) 2266 if err != nil { 2267 t.Logf("proxy.SOCKS5 for %s failed: %s", destination, err) 2268 return 2269 } 2270 2271 socksTCPConn, err := dialer.Dial("tcp", udpgwServerAddress) 2272 if err != nil { 2273 t.Logf("dialer.Dial for %s failed: %s", destination, err) 2274 return 2275 } 2276 defer socksTCPConn.Close() 2277 2278 flags := uint8(0) 2279 if destinationPort == 53 { 2280 flags = udpgwProtocolFlagDNS 2281 } 2282 2283 err = writeUdpgwPreamble( 2284 udpgwPreambleSize, 2285 flags, 2286 0, 2287 destinationIP, 2288 destinationPort, 2289 uint16(packetSize), 2290 buffer) 2291 if err != nil { 2292 t.Logf("writeUdpgwPreamble for %s failed: %s", destination, err) 2293 return 2294 } 2295 2296 _, err = socksTCPConn.Write(buffer[0 : udpgwPreambleSize+packetSize]) 2297 if err != nil { 2298 t.Logf("socksTCPConn.Write for %s failed: %s", destination, err) 2299 return 2300 } 2301 2302 udpgwProtocolMessage, err := readUdpgwMessage(socksTCPConn, buffer) 2303 if err != nil { 2304 t.Logf("readUdpgwMessage for %s failed: %s", destination, err) 2305 return 2306 } 2307 2308 _, err = serverUDPConn.WriteToUDP(udpgwProtocolMessage.packet, clientAddr) 2309 if err != nil { 2310 t.Logf("serverUDPConn.Write for %s failed: %s", destination, err) 2311 return 2312 } 2313 } 2314 2315 // Tunneled DNS request 2316 2317 waitGroup := new(sync.WaitGroup) 2318 waitGroup.Add(1) 2319 go localUDPProxy( 2320 net.IP(make([]byte, 4)), // ignored due to transparent DNS forwarding 2321 53, 2322 waitGroup) 2323 // TODO: properly synchronize with local UDP proxy startup 2324 time.Sleep(1 * time.Second) 2325 2326 clientUDPConn, err := net.DialUDP("udp", nil, localUDPProxyAddress) 2327 if err != nil { 2328 return fmt.Errorf("DialUDP failed: %s", err) 2329 } 2330 2331 clientUDPConn.SetReadDeadline(time.Now().Add(timeout)) 2332 clientUDPConn.SetWriteDeadline(time.Now().Add(timeout)) 2333 2334 addrs, err := resolveIP(testHostname, clientUDPConn) 2335 2336 clientUDPConn.Close() 2337 2338 if err == nil && (len(addrs) == 0 || len(addrs[0]) < 4) { 2339 err = std_errors.New("no address") 2340 } 2341 if err != nil { 2342 return fmt.Errorf("resolveIP failed: %s", err) 2343 } 2344 2345 waitGroup.Wait() 2346 2347 // Tunneled NTP request 2348 2349 waitGroup = new(sync.WaitGroup) 2350 waitGroup.Add(1) 2351 go localUDPProxy( 2352 addrs[0][len(addrs[0])-4:], 2353 123, 2354 waitGroup) 2355 // TODO: properly synchronize with local UDP proxy startup 2356 time.Sleep(1 * time.Second) 2357 2358 clientUDPConn, err = net.DialUDP("udp", nil, localUDPProxyAddress) 2359 if err != nil { 2360 return fmt.Errorf("DialUDP failed: %s", err) 2361 } 2362 2363 clientUDPConn.SetReadDeadline(time.Now().Add(timeout)) 2364 clientUDPConn.SetWriteDeadline(time.Now().Add(timeout)) 2365 2366 // NTP protocol code from: https://groups.google.com/d/msg/golang-nuts/FlcdMU5fkLQ/CAeoD9eqm-IJ 2367 2368 ntpData := make([]byte, 48) 2369 ntpData[0] = 3<<3 | 3 2370 2371 _, err = clientUDPConn.Write(ntpData) 2372 if err != nil { 2373 clientUDPConn.Close() 2374 return fmt.Errorf("NTP Write failed: %s", err) 2375 } 2376 2377 _, err = clientUDPConn.Read(ntpData) 2378 if err != nil { 2379 clientUDPConn.Close() 2380 return fmt.Errorf("NTP Read failed: %s", err) 2381 } 2382 2383 clientUDPConn.Close() 2384 2385 var sec, frac uint64 2386 sec = uint64(ntpData[43]) | uint64(ntpData[42])<<8 | uint64(ntpData[41])<<16 | uint64(ntpData[40])<<24 2387 frac = uint64(ntpData[47]) | uint64(ntpData[46])<<8 | uint64(ntpData[45])<<16 | uint64(ntpData[44])<<24 2388 2389 nsec := sec * 1e9 2390 nsec += (frac * 1e9) >> 32 2391 2392 ntpNow := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nsec)).Local() 2393 2394 now := time.Now() 2395 2396 diff := ntpNow.Sub(now) 2397 if diff < 0 { 2398 diff = -diff 2399 } 2400 2401 if diff > 1*time.Minute { 2402 return fmt.Errorf("Unexpected NTP time: %s; local time: %s", ntpNow, now) 2403 } 2404 2405 waitGroup.Wait() 2406 2407 return nil 2408 } 2409 2410 func resolveIP(host string, conn net.Conn) (addrs []net.IP, err error) { 2411 2412 // Send the DNS query (A record only) 2413 dnsConn := &dns.Conn{Conn: conn} 2414 defer dnsConn.Close() 2415 query := new(dns.Msg) 2416 query.SetQuestion(dns.Fqdn(host), dns.TypeA) 2417 query.RecursionDesired = true 2418 dnsConn.WriteMsg(query) 2419 2420 // Process the response 2421 response, err := dnsConn.ReadMsg() 2422 if err == nil && response.MsgHdr.Id != query.MsgHdr.Id { 2423 err = dns.ErrId 2424 } 2425 if err != nil { 2426 return nil, errors.Trace(err) 2427 } 2428 addrs = make([]net.IP, 0) 2429 for _, answer := range response.Answer { 2430 if a, ok := answer.(*dns.A); ok { 2431 addrs = append(addrs, a.A) 2432 } 2433 } 2434 return addrs, nil 2435 } 2436 2437 func pavePsinetDatabaseFile( 2438 t *testing.T, 2439 psinetFilename string, 2440 sponsorID string, 2441 useDefaultSponsorID bool, 2442 doDomainBytes bool, 2443 validServerEntryTags []string) (string, string) { 2444 2445 if sponsorID == "" { 2446 sponsorID = prng.HexString(8) 2447 } 2448 2449 defaultSponsorID := "" 2450 if useDefaultSponsorID { 2451 defaultSponsorID = sponsorID 2452 } 2453 2454 fakeDomain := prng.HexString(4) 2455 fakePath := prng.HexString(4) 2456 expectedHomepageURL := fmt.Sprintf("https://%s.com/%s", fakeDomain, fakePath) 2457 2458 psinetJSONFormat := ` 2459 { 2460 "default_sponsor_id" : "%s", 2461 "sponsors" : { 2462 "%s" : { 2463 %s 2464 "home_pages" : { 2465 "None" : [ 2466 { 2467 "region" : null, 2468 "url" : "%s" 2469 } 2470 ] 2471 } 2472 } 2473 }, 2474 "default_alert_action_urls" : { 2475 "%s" : %s 2476 }, 2477 "valid_server_entry_tags" : { 2478 %s 2479 } 2480 } 2481 ` 2482 2483 domainBytes := "" 2484 if doDomainBytes { 2485 domainBytes = ` 2486 "https_request_regexes" : [ 2487 { 2488 "regex" : ".*", 2489 "replace" : "ALL" 2490 } 2491 ], 2492 ` 2493 } 2494 2495 actionURLsJSON, _ := json.Marshal(testDisallowedTrafficAlertActionURLs) 2496 2497 validServerEntryTagsJSON := "" 2498 for _, serverEntryTag := range validServerEntryTags { 2499 if len(validServerEntryTagsJSON) > 0 { 2500 validServerEntryTagsJSON += ", " 2501 } 2502 validServerEntryTagsJSON += fmt.Sprintf("\"%s\" : true", serverEntryTag) 2503 } 2504 2505 psinetJSON := fmt.Sprintf( 2506 psinetJSONFormat, 2507 defaultSponsorID, 2508 sponsorID, 2509 domainBytes, 2510 expectedHomepageURL, 2511 protocol.PSIPHON_API_ALERT_DISALLOWED_TRAFFIC, 2512 actionURLsJSON, 2513 validServerEntryTagsJSON) 2514 2515 err := ioutil.WriteFile(psinetFilename, []byte(psinetJSON), 0600) 2516 if err != nil { 2517 t.Fatalf("error paving psinet database file: %s", err) 2518 } 2519 2520 return sponsorID, expectedHomepageURL 2521 } 2522 2523 func paveTrafficRulesFile( 2524 t *testing.T, 2525 trafficRulesFilename string, 2526 propagationChannelID string, 2527 accessType string, 2528 authorizationID string, 2529 requireAuthorization bool, 2530 deny bool, 2531 livenessTestSize int) { 2532 2533 // Test both default and fast lookups 2534 if intLookupThreshold != 10 { 2535 t.Fatalf("unexpected intLookupThreshold") 2536 } 2537 2538 TCPPorts := mockWebServerPort 2539 UDPPorts := "53, 123, 10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008, 10009, 10010" 2540 2541 allowTCPPorts := TCPPorts 2542 allowUDPPorts := UDPPorts 2543 disallowTCPPorts := "1" 2544 disallowUDPPorts := "1" 2545 2546 if deny { 2547 allowTCPPorts = "1" 2548 allowUDPPorts = "1" 2549 disallowTCPPorts = TCPPorts 2550 disallowUDPPorts = UDPPorts 2551 } 2552 2553 authorizationFilterFormat := `, 2554 "AuthorizedAccessTypes" : ["%s"], 2555 "ActiveAuthorizationIDs" : ["%s"] 2556 ` 2557 2558 authorizationFilter := "" 2559 if requireAuthorization { 2560 authorizationFilter = fmt.Sprintf( 2561 authorizationFilterFormat, accessType, authorizationID) 2562 } 2563 2564 // Supports two traffic rule test cases: 2565 // 2566 // 1. no ports are allowed until after the filtered rule is applied 2567 // 2. no required ports are allowed (deny = true) 2568 2569 trafficRulesJSONFormat := ` 2570 { 2571 "DefaultRules" : { 2572 "RateLimits" : { 2573 "ReadBytesPerSecond": 16384, 2574 "WriteBytesPerSecond": 16384, 2575 "ReadUnthrottledBytes": %d, 2576 "WriteUnthrottledBytes": %d 2577 }, 2578 "AllowTCPPorts" : [1], 2579 "AllowUDPPorts" : [1], 2580 "MeekRateLimiterHistorySize" : 10, 2581 "MeekRateLimiterThresholdSeconds" : 1, 2582 "MeekRateLimiterGarbageCollectionTriggerCount" : 1, 2583 "MeekRateLimiterReapHistoryFrequencySeconds" : 1, 2584 "MeekRateLimiterRegions" : [] 2585 }, 2586 "FilteredRules" : [ 2587 { 2588 "Filter" : { 2589 "HandshakeParameters" : { 2590 "propagation_channel_id" : ["%s"] 2591 }%s 2592 }, 2593 "Rules" : { 2594 "RateLimits" : { 2595 "ReadBytesPerSecond": 2097152, 2596 "WriteBytesPerSecond": 2097152 2597 }, 2598 "AllowTCPPorts" : [%s], 2599 "AllowUDPPorts" : [%s], 2600 "DisallowTCPPorts" : [%s], 2601 "DisallowUDPPorts" : [%s] 2602 } 2603 } 2604 ] 2605 } 2606 ` 2607 2608 trafficRulesJSON := fmt.Sprintf( 2609 trafficRulesJSONFormat, 2610 livenessTestSize, livenessTestSize, 2611 propagationChannelID, authorizationFilter, 2612 allowTCPPorts, allowUDPPorts, disallowTCPPorts, disallowUDPPorts) 2613 2614 err := ioutil.WriteFile(trafficRulesFilename, []byte(trafficRulesJSON), 0600) 2615 if err != nil { 2616 t.Fatalf("error paving traffic rules file: %s", err) 2617 } 2618 } 2619 2620 var expectedNumSLOKs = 3 2621 2622 func paveOSLConfigFile(t *testing.T, oslConfigFilename string) string { 2623 2624 oslConfigJSONFormat := ` 2625 { 2626 "Schemes" : [ 2627 { 2628 "Epoch" : "%s", 2629 "Regions" : [], 2630 "PropagationChannelIDs" : ["%s"], 2631 "MasterKey" : "wFuSbqU/pJ/35vRmoM8T9ys1PgDa8uzJps1Y+FNKa5U=", 2632 "SeedSpecs" : [ 2633 { 2634 "ID" : "IXHWfVgWFkEKvgqsjmnJuN3FpaGuCzQMETya+DSQvsk=", 2635 "UpstreamSubnets" : ["0.0.0.0/0"], 2636 "Targets" : 2637 { 2638 "BytesRead" : 1, 2639 "BytesWritten" : 1, 2640 "PortForwardDurationNanoseconds" : 1 2641 } 2642 }, 2643 { 2644 "ID" : "qvpIcORLE2Pi5TZmqRtVkEp+OKov0MhfsYPLNV7FYtI=", 2645 "UpstreamSubnets" : ["0.0.0.0/0"], 2646 "Targets" : 2647 { 2648 "BytesRead" : 1, 2649 "BytesWritten" : 1, 2650 "PortForwardDurationNanoseconds" : 1 2651 } 2652 } 2653 ], 2654 "SeedSpecThreshold" : 2, 2655 "SeedPeriodNanoseconds" : 2592000000000000, 2656 "SeedPeriodKeySplits": [ 2657 { 2658 "Total": 2, 2659 "Threshold": 2 2660 } 2661 ] 2662 }, 2663 { 2664 "Epoch" : "%s", 2665 "Regions" : [], 2666 "PropagationChannelIDs" : ["%s"], 2667 "MasterKey" : "HDc/mvd7e+lKDJD0fMpJW66YJ/VW4iqDRjeclEsMnro=", 2668 "SeedSpecs" : [ 2669 { 2670 "ID" : "/M0vsT0IjzmI0MvTI9IYe8OVyeQGeaPZN2xGxfLw/UQ=", 2671 "UpstreamSubnets" : ["0.0.0.0/0"], 2672 "Targets" : 2673 { 2674 "BytesRead" : 1, 2675 "BytesWritten" : 1, 2676 "PortForwardDurationNanoseconds" : 1 2677 } 2678 } 2679 ], 2680 "SeedSpecThreshold" : 1, 2681 "SeedPeriodNanoseconds" : 2592000000000000, 2682 "SeedPeriodKeySplits": [ 2683 { 2684 "Total": 1, 2685 "Threshold": 1 2686 } 2687 ] 2688 } 2689 ] 2690 } 2691 ` 2692 2693 propagationChannelID := prng.HexString(8) 2694 2695 now := time.Now().UTC() 2696 epoch := now.Truncate(720 * time.Hour) 2697 epochStr := epoch.Format(time.RFC3339Nano) 2698 2699 oslConfigJSON := fmt.Sprintf( 2700 oslConfigJSONFormat, 2701 epochStr, propagationChannelID, 2702 epochStr, propagationChannelID) 2703 2704 err := ioutil.WriteFile(oslConfigFilename, []byte(oslConfigJSON), 0600) 2705 if err != nil { 2706 t.Fatalf("error paving osl config file: %s", err) 2707 } 2708 2709 return propagationChannelID 2710 } 2711 2712 func paveTacticsConfigFile( 2713 t *testing.T, tacticsConfigFilename string, 2714 tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey string, 2715 tunnelProtocol string, 2716 propagationChannelID string, 2717 livenessTestSize int, 2718 doBurstMonitor bool, 2719 doDestinationBytes bool) { 2720 2721 // Setting LimitTunnelProtocols passively exercises the 2722 // server-side LimitTunnelProtocols enforcement. 2723 2724 tacticsConfigJSONFormat := ` 2725 { 2726 "RequestPublicKey" : "%s", 2727 "RequestPrivateKey" : "%s", 2728 "RequestObfuscatedKey" : "%s", 2729 "DefaultTactics" : { 2730 "TTL" : "60s", 2731 "Probability" : 1.0, 2732 "Parameters" : { 2733 %s 2734 %s 2735 "LimitTunnelProtocols" : ["%s"], 2736 "FragmentorLimitProtocols" : ["%s"], 2737 "FragmentorProbability" : 1.0, 2738 "FragmentorMinTotalBytes" : 1000, 2739 "FragmentorMaxTotalBytes" : 2000, 2740 "FragmentorMinWriteBytes" : 1, 2741 "FragmentorMaxWriteBytes" : 100, 2742 "FragmentorMinDelay" : "1ms", 2743 "FragmentorMaxDelay" : "10ms", 2744 "FragmentorDownstreamLimitProtocols" : ["%s"], 2745 "FragmentorDownstreamProbability" : 1.0, 2746 "FragmentorDownstreamMinTotalBytes" : 1000, 2747 "FragmentorDownstreamMaxTotalBytes" : 2000, 2748 "FragmentorDownstreamMinWriteBytes" : 1, 2749 "FragmentorDownstreamMaxWriteBytes" : 100, 2750 "FragmentorDownstreamMinDelay" : "1ms", 2751 "FragmentorDownstreamMaxDelay" : "10ms", 2752 "LivenessTestMinUpstreamBytes" : %d, 2753 "LivenessTestMaxUpstreamBytes" : %d, 2754 "LivenessTestMinDownstreamBytes" : %d, 2755 "LivenessTestMaxDownstreamBytes" : %d, 2756 "BPFServerTCPProgram": { 2757 "Name" : "test-server-bpf", 2758 "Instructions" : [ 2759 {"Op": "RetConstant", "Args": {"Val": 65535}}]}, 2760 "BPFServerTCPProbability" : 1.0, 2761 "BPFClientTCPProgram": { 2762 "Name" : "test-client-bpf", 2763 "Instructions" : [ 2764 {"Op": "RetConstant", "Args": {"Val": 65535}}]}, 2765 "BPFClientTCPProbability" : 1.0, 2766 "ServerPacketManipulationSpecs" : [{"Name": "test-packetman-spec", "PacketSpecs": [["TCP-flags S"]]}], 2767 "ServerPacketManipulationProbability" : 1.0, 2768 "ServerProtocolPacketManipulations": {"All" : ["test-packetman-spec"]} 2769 } 2770 }, 2771 "FilteredTactics" : [ 2772 { 2773 "Filter" : { 2774 "APIParameters" : {"propagation_channel_id" : ["%s"]}, 2775 "SpeedTestRTTMilliseconds" : { 2776 "Aggregation" : "Median", 2777 "AtLeast" : 1 2778 } 2779 }, 2780 "Tactics" : { 2781 "Parameters" : { 2782 "TunnelConnectTimeout" : "20s", 2783 "TunnelRateLimits" : {"WriteBytesPerSecond": 1000000}, 2784 "TransformHostNameProbability" : 1.0, 2785 "PickUserAgentProbability" : 1.0, 2786 "ApplicationParameters" : { 2787 "AppFlag1" : true, 2788 "AppConfig1" : {"Option1" : "A", "Option2" : "B"}, 2789 "AppSwitches1" : [1, 2, 3, 4] 2790 }, 2791 "CustomHostNameRegexes": ["%s"], 2792 "CustomHostNameProbability": 1.0, 2793 "CustomHostNameLimitProtocols": ["%s"] 2794 } 2795 } 2796 } 2797 ] 2798 } 2799 ` 2800 2801 burstParameters := "" 2802 if doBurstMonitor { 2803 burstParameters = ` 2804 "ServerBurstUpstreamDeadline" : "100ms", 2805 "ServerBurstUpstreamTargetBytes" : 1000, 2806 "ServerBurstDownstreamDeadline" : "100ms", 2807 "ServerBurstDownstreamTargetBytes" : 100000, 2808 "ClientBurstUpstreamDeadline" : "100ms", 2809 "ClientBurstUpstreamTargetBytes" : 1000, 2810 "ClientBurstDownstreamDeadline" : "100ms", 2811 "ClientBurstDownstreamTargetBytes" : 100000, 2812 ` 2813 } 2814 2815 destinationBytesParameters := "" 2816 if doDestinationBytes { 2817 destinationBytesParameters = fmt.Sprintf(` 2818 "DestinationBytesMetricsASN" : "%s", 2819 `, testGeoIPASN) 2820 } 2821 2822 tacticsConfigJSON := fmt.Sprintf( 2823 tacticsConfigJSONFormat, 2824 tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey, 2825 burstParameters, 2826 destinationBytesParameters, 2827 tunnelProtocol, 2828 tunnelProtocol, 2829 tunnelProtocol, 2830 livenessTestSize, livenessTestSize, livenessTestSize, livenessTestSize, 2831 propagationChannelID, 2832 strings.ReplaceAll(testCustomHostNameRegex, `\`, `\\`), 2833 tunnelProtocol) 2834 2835 err := ioutil.WriteFile(tacticsConfigFilename, []byte(tacticsConfigJSON), 0600) 2836 if err != nil { 2837 t.Fatalf("error paving tactics config file: %s", err) 2838 } 2839 } 2840 2841 func paveBlocklistFile(t *testing.T, blocklistFilename string) { 2842 2843 blocklistContent := 2844 "255.255.255.255,test-source,test-subject\n2001:db8:f75c::0951:58bc:ef22,test-source,test-subject\nexample.org,test-source,test-subject\n" 2845 2846 err := ioutil.WriteFile(blocklistFilename, []byte(blocklistContent), 0600) 2847 if err != nil { 2848 t.Fatalf("error paving blocklist file: %s", err) 2849 } 2850 } 2851 2852 type pruneServerEntryTestCase struct { 2853 IPAddress string 2854 ExplicitTag bool 2855 ExpectedTag string 2856 LocalTimestamp string 2857 PsinetValid bool 2858 ExpectPrune bool 2859 IsEmbedded bool 2860 DialPort0 bool 2861 ServerEntryFields protocol.ServerEntryFields 2862 } 2863 2864 func initializePruneServerEntriesTest( 2865 t *testing.T, 2866 runConfig *runServerConfig) ([]*pruneServerEntryTestCase, []string, int) { 2867 2868 if !runConfig.doPruneServerEntries { 2869 return nil, nil, 0 2870 } 2871 2872 newTimeStamp := time.Now().UTC().Format(time.RFC3339) 2873 oldTimeStamp := time.Now().Add(-30 * 24 * time.Hour).UTC().Format(time.RFC3339) 2874 2875 // Test Cases: 2876 // - ExplicitTag: server entry includes a tag; vs. generate a derived tag 2877 // - LocalTimestamp: server entry is sufficiently old to be pruned; vs. not 2878 // - PsinetValid: server entry is reported valid by psinet; vs. deleted 2879 // - ExpectPrune: prune outcome based on flags above 2880 // - IsEmbedded: pruned embedded server entries leave a tombstone and cannot 2881 // be reimported 2882 // - DialPort0: set dial port to 0, a special prune case (see statusAPIRequestHandler) 2883 2884 pruneServerEntryTestCases := []*pruneServerEntryTestCase{ 2885 &pruneServerEntryTestCase{IPAddress: "192.0.2.1", ExplicitTag: true, LocalTimestamp: newTimeStamp, PsinetValid: true, ExpectPrune: false}, 2886 &pruneServerEntryTestCase{IPAddress: "192.0.2.2", ExplicitTag: false, LocalTimestamp: newTimeStamp, PsinetValid: true, ExpectPrune: false}, 2887 &pruneServerEntryTestCase{IPAddress: "192.0.2.3", ExplicitTag: true, LocalTimestamp: oldTimeStamp, PsinetValid: true, ExpectPrune: false}, 2888 &pruneServerEntryTestCase{IPAddress: "192.0.2.4", ExplicitTag: false, LocalTimestamp: oldTimeStamp, PsinetValid: true, ExpectPrune: false}, 2889 &pruneServerEntryTestCase{IPAddress: "192.0.2.5", ExplicitTag: true, LocalTimestamp: newTimeStamp, PsinetValid: false, ExpectPrune: false}, 2890 &pruneServerEntryTestCase{IPAddress: "192.0.2.6", ExplicitTag: false, LocalTimestamp: newTimeStamp, PsinetValid: false, ExpectPrune: false}, 2891 &pruneServerEntryTestCase{IPAddress: "192.0.2.7", ExplicitTag: true, LocalTimestamp: oldTimeStamp, PsinetValid: false, ExpectPrune: true, IsEmbedded: false}, 2892 &pruneServerEntryTestCase{IPAddress: "192.0.2.8", ExplicitTag: false, LocalTimestamp: oldTimeStamp, PsinetValid: false, ExpectPrune: true, IsEmbedded: false}, 2893 &pruneServerEntryTestCase{IPAddress: "192.0.2.9", ExplicitTag: true, LocalTimestamp: oldTimeStamp, PsinetValid: false, ExpectPrune: true, IsEmbedded: true}, 2894 &pruneServerEntryTestCase{IPAddress: "192.0.2.10", ExplicitTag: false, LocalTimestamp: oldTimeStamp, PsinetValid: false, ExpectPrune: true, IsEmbedded: true}, 2895 &pruneServerEntryTestCase{IPAddress: "192.0.2.11", ExplicitTag: true, LocalTimestamp: oldTimeStamp, PsinetValid: true, ExpectPrune: true, IsEmbedded: false, DialPort0: true}, 2896 &pruneServerEntryTestCase{IPAddress: "192.0.2.12", ExplicitTag: false, LocalTimestamp: oldTimeStamp, PsinetValid: true, ExpectPrune: true, IsEmbedded: true, DialPort0: true}, 2897 &pruneServerEntryTestCase{IPAddress: "192.0.2.13", ExplicitTag: true, LocalTimestamp: oldTimeStamp, PsinetValid: true, ExpectPrune: true, IsEmbedded: true, DialPort0: true}, 2898 } 2899 2900 for _, testCase := range pruneServerEntryTestCases { 2901 2902 dialPort := 4000 2903 if testCase.DialPort0 { 2904 dialPort = 0 2905 } 2906 2907 _, _, _, _, encodedServerEntry, err := GenerateConfig( 2908 &GenerateConfigParams{ 2909 ServerIPAddress: testCase.IPAddress, 2910 WebServerPort: 8000, 2911 TunnelProtocolPorts: map[string]int{runConfig.tunnelProtocol: dialPort}, 2912 }) 2913 if err != nil { 2914 t.Fatalf("GenerateConfig failed: %s", err) 2915 } 2916 2917 serverEntrySource := protocol.SERVER_ENTRY_SOURCE_REMOTE 2918 if testCase.IsEmbedded { 2919 serverEntrySource = protocol.SERVER_ENTRY_SOURCE_EMBEDDED 2920 } 2921 2922 serverEntryFields, err := protocol.DecodeServerEntryFields( 2923 string(encodedServerEntry), 2924 testCase.LocalTimestamp, 2925 serverEntrySource) 2926 if err != nil { 2927 t.Fatalf("DecodeServerEntryFields failed: %s", err) 2928 } 2929 2930 if testCase.ExplicitTag { 2931 testCase.ExpectedTag = prng.Base64String(32) 2932 serverEntryFields.SetTag(testCase.ExpectedTag) 2933 } else { 2934 testCase.ExpectedTag = protocol.GenerateServerEntryTag( 2935 serverEntryFields.GetIPAddress(), 2936 serverEntryFields.GetWebServerSecret()) 2937 } 2938 2939 testCase.ServerEntryFields = serverEntryFields 2940 } 2941 2942 psinetValidServerEntryTags := make([]string, 0) 2943 expectedNumPruneNotices := 0 2944 2945 for _, testCase := range pruneServerEntryTestCases { 2946 2947 if testCase.PsinetValid { 2948 psinetValidServerEntryTags = append( 2949 psinetValidServerEntryTags, testCase.ExpectedTag) 2950 } 2951 2952 if testCase.ExpectPrune { 2953 expectedNumPruneNotices += 1 2954 } 2955 } 2956 2957 return pruneServerEntryTestCases, 2958 psinetValidServerEntryTags, 2959 expectedNumPruneNotices 2960 } 2961 2962 func storePruneServerEntriesTest( 2963 t *testing.T, 2964 runConfig *runServerConfig, 2965 testDataDirName string, 2966 pruneServerEntryTestCases []*pruneServerEntryTestCase) { 2967 2968 if !runConfig.doPruneServerEntries { 2969 return 2970 } 2971 2972 for _, testCase := range pruneServerEntryTestCases { 2973 2974 err := psiphon.StoreServerEntry(testCase.ServerEntryFields, true) 2975 if err != nil { 2976 t.Fatalf("StoreServerEntry failed: %s", err) 2977 } 2978 } 2979 2980 clientConfig := &psiphon.Config{ 2981 SponsorId: "0", 2982 PropagationChannelId: "0", 2983 2984 // DataRootDirectory must to be set to avoid a migration in the current 2985 // working directory. 2986 DataRootDirectory: testDataDirName, 2987 } 2988 err := clientConfig.Commit(false) 2989 if err != nil { 2990 t.Fatalf("Commit failed: %s", err) 2991 } 2992 2993 resolver := psiphon.NewResolver(clientConfig, true) 2994 defer resolver.Stop() 2995 clientConfig.SetResolver(resolver) 2996 2997 applyParameters := make(map[string]interface{}) 2998 applyParameters[parameters.RecordFailedTunnelPersistentStatsProbability] = 1.0 2999 3000 err = clientConfig.SetParameters("", true, applyParameters) 3001 if err != nil { 3002 t.Fatalf("SetParameters failed: %s", err) 3003 } 3004 3005 verifyTestCasesStored := make(verifyTestCasesStoredLookup) 3006 for _, testCase := range pruneServerEntryTestCases { 3007 verifyTestCasesStored.mustBeStored(testCase.IPAddress) 3008 } 3009 3010 scanServerEntries(t, clientConfig, pruneServerEntryTestCases, func( 3011 t *testing.T, 3012 testCase *pruneServerEntryTestCase, 3013 serverEntry *protocol.ServerEntry) { 3014 3015 verifyTestCasesStored.isStored(testCase.IPAddress) 3016 3017 // Check that random tag was retained or derived tag was calculated as 3018 // expected 3019 3020 if serverEntry.Tag != testCase.ExpectedTag { 3021 t.Fatalf("unexpected tag for %s got %s expected %s", 3022 testCase.IPAddress, serverEntry.Tag, testCase.ExpectedTag) 3023 } 3024 3025 // Create failed tunnel event records to exercise pruning 3026 3027 dialParams, err := psiphon.MakeDialParameters( 3028 clientConfig, 3029 nil, 3030 func(_ *protocol.ServerEntry, _ string) bool { return true }, 3031 func(serverEntry *protocol.ServerEntry) (string, bool) { 3032 return runConfig.tunnelProtocol, true 3033 }, 3034 serverEntry, 3035 false, 3036 0, 3037 0) 3038 if err != nil { 3039 t.Fatalf("MakeDialParameters failed: %s", err) 3040 } 3041 3042 err = psiphon.RecordFailedTunnelStat( 3043 clientConfig, dialParams, nil, 0, 0, std_errors.New("test error")) 3044 if err != nil { 3045 t.Fatalf("RecordFailedTunnelStat failed: %s", err) 3046 } 3047 }) 3048 3049 verifyTestCasesStored.checkStored( 3050 t, "missing prune test case server entries") 3051 } 3052 3053 func checkPruneServerEntriesTest( 3054 t *testing.T, 3055 runConfig *runServerConfig, 3056 testDataDirName string, 3057 pruneServerEntryTestCases []*pruneServerEntryTestCase) { 3058 3059 if !runConfig.doPruneServerEntries { 3060 return 3061 } 3062 3063 clientConfig := &psiphon.Config{ 3064 SponsorId: "0", 3065 PropagationChannelId: "0", 3066 3067 // DataRootDirectory must to be set to avoid a migration in the current 3068 // working directory. 3069 DataRootDirectory: testDataDirName, 3070 } 3071 err := clientConfig.Commit(false) 3072 if err != nil { 3073 t.Fatalf("Commit failed: %s", err) 3074 } 3075 3076 // Check that server entries remain or are pruned as expected 3077 3078 verifyTestCasesStored := make(verifyTestCasesStoredLookup) 3079 for _, testCase := range pruneServerEntryTestCases { 3080 if !testCase.ExpectPrune { 3081 verifyTestCasesStored.mustBeStored(testCase.IPAddress) 3082 } 3083 } 3084 3085 scanServerEntries(t, clientConfig, pruneServerEntryTestCases, func( 3086 t *testing.T, 3087 testCase *pruneServerEntryTestCase, 3088 serverEntry *protocol.ServerEntry) { 3089 3090 if testCase.ExpectPrune { 3091 t.Fatalf("expected prune for %s", testCase.IPAddress) 3092 } else { 3093 verifyTestCasesStored.isStored(testCase.IPAddress) 3094 } 3095 }) 3096 3097 verifyTestCasesStored.checkStored( 3098 t, "missing prune test case server entries") 3099 3100 // Check that pruned server entries reimport or not, as expected 3101 3102 for _, testCase := range pruneServerEntryTestCases { 3103 3104 err := psiphon.StoreServerEntry(testCase.ServerEntryFields, true) 3105 if err != nil { 3106 t.Fatalf("StoreServerEntry failed: %s", err) 3107 } 3108 } 3109 3110 verifyTestCasesStored = make(verifyTestCasesStoredLookup) 3111 for _, testCase := range pruneServerEntryTestCases { 3112 if !testCase.ExpectPrune || !testCase.IsEmbedded { 3113 verifyTestCasesStored.mustBeStored(testCase.IPAddress) 3114 } 3115 } 3116 3117 scanServerEntries(t, clientConfig, pruneServerEntryTestCases, func( 3118 t *testing.T, 3119 testCase *pruneServerEntryTestCase, 3120 serverEntry *protocol.ServerEntry) { 3121 3122 if testCase.ExpectPrune && testCase.IsEmbedded { 3123 t.Fatalf("expected tombstone for %s", testCase.IPAddress) 3124 } else { 3125 verifyTestCasesStored.isStored(testCase.IPAddress) 3126 } 3127 }) 3128 3129 verifyTestCasesStored.checkStored( 3130 t, "missing reimported prune test case server entries") 3131 3132 // Non-embedded server entries with tombstones _can_ be reimported 3133 3134 for _, testCase := range pruneServerEntryTestCases { 3135 3136 testCase.ServerEntryFields.SetLocalSource(protocol.SERVER_ENTRY_SOURCE_REMOTE) 3137 3138 err := psiphon.StoreServerEntry(testCase.ServerEntryFields, true) 3139 if err != nil { 3140 t.Fatalf("StoreServerEntry failed: %s", err) 3141 } 3142 } 3143 3144 verifyTestCasesStored = make(verifyTestCasesStoredLookup) 3145 for _, testCase := range pruneServerEntryTestCases { 3146 verifyTestCasesStored.mustBeStored(testCase.IPAddress) 3147 } 3148 3149 scanServerEntries(t, clientConfig, pruneServerEntryTestCases, func( 3150 t *testing.T, 3151 testCase *pruneServerEntryTestCase, 3152 serverEntry *protocol.ServerEntry) { 3153 3154 verifyTestCasesStored.isStored(testCase.IPAddress) 3155 }) 3156 3157 verifyTestCasesStored.checkStored( 3158 t, "missing non-embedded reimported prune test case server entries") 3159 } 3160 3161 func scanServerEntries( 3162 t *testing.T, 3163 clientConfig *psiphon.Config, 3164 pruneServerEntryTestCases []*pruneServerEntryTestCase, 3165 scanner func( 3166 t *testing.T, 3167 testCase *pruneServerEntryTestCase, 3168 serverEntry *protocol.ServerEntry)) { 3169 3170 _, iterator, err := psiphon.NewServerEntryIterator(clientConfig) 3171 if err != nil { 3172 t.Fatalf("NewServerEntryIterator failed: %s", err) 3173 } 3174 defer iterator.Close() 3175 3176 for { 3177 3178 serverEntry, err := iterator.Next() 3179 if err != nil { 3180 t.Fatalf("ServerIterator.Next failed: %s", err) 3181 } 3182 if serverEntry == nil { 3183 break 3184 } 3185 3186 for _, testCase := range pruneServerEntryTestCases { 3187 if testCase.IPAddress == serverEntry.IpAddress { 3188 scanner(t, testCase, serverEntry) 3189 break 3190 } 3191 } 3192 } 3193 } 3194 3195 type verifyTestCasesStoredLookup map[string]bool 3196 3197 func (v verifyTestCasesStoredLookup) mustBeStored(s string) { 3198 v[s] = true 3199 } 3200 3201 func (v verifyTestCasesStoredLookup) isStored(s string) { 3202 delete(v, s) 3203 } 3204 3205 func (v verifyTestCasesStoredLookup) checkStored(t *testing.T, errMessage string) { 3206 if len(v) != 0 { 3207 t.Fatalf("%s: %+v", errMessage, v) 3208 } 3209 }