sdp_test.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. //go:build PSIPHON_ENABLE_INPROXY
  2. /*
  3. * Copyright (c) 2024, Psiphon Inc.
  4. * All rights reserved.
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. package inproxy
  21. import (
  22. "context"
  23. "fmt"
  24. "net"
  25. "strings"
  26. "testing"
  27. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  28. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  29. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/internal/testutils"
  30. )
  31. func TestProcessSDP(t *testing.T) {
  32. err := runTestProcessSDP()
  33. if err != nil {
  34. t.Error(errors.Trace(err).Error())
  35. }
  36. }
  37. func runTestProcessSDP() error {
  38. config := &webRTCConfig{
  39. Logger: testutils.NewTestLogger(),
  40. WebRTCDialCoordinator: &testWebRTCDialCoordinator{
  41. disableSTUN: true,
  42. disablePortMapping: true,
  43. },
  44. }
  45. hasPersonalCompartmentIDs := false
  46. errorOnNoCandidates := true
  47. disableIPv6Candidates := false
  48. allowPrivateIPAddressCandidates := false
  49. filterPrivateIPAddressCandidates := false
  50. // Create a valid, base SDP, including private network (bogon) candidates.
  51. SetAllowBogonWebRTCConnections(true)
  52. defer SetAllowBogonWebRTCConnections(false)
  53. conn, webRTCSDP, metrics, err := newWebRTCConnForOffer(
  54. context.Background(), config, hasPersonalCompartmentIDs)
  55. if err != nil {
  56. return errors.Trace(err)
  57. }
  58. defer conn.Close()
  59. SDP := []byte(webRTCSDP.SDP)
  60. // Test disallow IPv6
  61. disableIPv6Candidates = true
  62. if metrics.hasIPv6 {
  63. preparedSDP, metrics, err := prepareSDPAddresses(
  64. SDP,
  65. errorOnNoCandidates,
  66. "",
  67. disableIPv6Candidates,
  68. allowPrivateIPAddressCandidates)
  69. if err != nil {
  70. return errors.Trace(err)
  71. }
  72. found := false
  73. for _, reason := range metrics.filteredICECandidates {
  74. if strings.Contains(reason, "disabled") {
  75. found = true
  76. break
  77. }
  78. }
  79. if !found {
  80. return errors.TraceNew("unexpected filteredICECandidates")
  81. }
  82. if len(preparedSDP) >= len(SDP) {
  83. return errors.TraceNew("unexpected SDP length")
  84. }
  85. }
  86. disableIPv6Candidates = false
  87. // Test filter unexpected GeoIP
  88. // This IP must not be a bogon; this address is not dialed.
  89. testIP := "1.1.1.1"
  90. expectedGeoIP := common.GeoIPData{Country: "AA", ASN: "1"}
  91. lookupGeoIP := func(IP string) common.GeoIPData {
  92. if IP == testIP {
  93. return common.GeoIPData{Country: "BB", ASN: "2"}
  94. }
  95. return expectedGeoIP
  96. }
  97. // Add the testIP as a port mapping candidate.
  98. preparedSDP, metrics, err := prepareSDPAddresses(
  99. SDP,
  100. errorOnNoCandidates,
  101. net.JoinHostPort(testIP, "80"),
  102. disableIPv6Candidates,
  103. allowPrivateIPAddressCandidates)
  104. if err != nil {
  105. return errors.Trace(err)
  106. }
  107. filteredSDP, metrics, err := filterSDPAddresses(
  108. preparedSDP,
  109. errorOnNoCandidates,
  110. lookupGeoIP,
  111. expectedGeoIP,
  112. allowPrivateIPAddressCandidates,
  113. filterPrivateIPAddressCandidates)
  114. if err != nil {
  115. return errors.Trace(err)
  116. }
  117. found := false
  118. for _, reason := range metrics.filteredICECandidates {
  119. if strings.Contains(reason, "unexpected GeoIP") {
  120. found = true
  121. break
  122. }
  123. }
  124. if !found {
  125. return errors.TraceNew("unexpected filteredICECandidates")
  126. }
  127. if len(filteredSDP) >= len(preparedSDP) {
  128. return errors.TraceNew("unexpected SDP length")
  129. }
  130. // Test filter bogons
  131. SetAllowBogonWebRTCConnections(false)
  132. // Allow no candidates
  133. errorOnNoCandidates = false
  134. filteredSDP, metrics, err = filterSDPAddresses(
  135. SDP,
  136. errorOnNoCandidates,
  137. nil,
  138. common.GeoIPData{},
  139. allowPrivateIPAddressCandidates,
  140. filterPrivateIPAddressCandidates)
  141. if err != nil {
  142. return errors.Trace(err)
  143. }
  144. found = false
  145. for _, reason := range metrics.filteredICECandidates {
  146. if strings.Contains(reason, "bogon") {
  147. found = true
  148. break
  149. }
  150. }
  151. if !found {
  152. return errors.TraceNew("unexpected filteredICECandidates")
  153. }
  154. if len(filteredSDP) >= len(SDP) {
  155. return errors.TraceNew("unexpected SDP length")
  156. }
  157. errorOnNoCandidates = true
  158. // Test private IP addresses
  159. SetAllowBogonWebRTCConnections(false)
  160. hasPersonalCompartmentIDs = true
  161. allowPrivateIPAddressCandidates = true
  162. filterPrivateIPAddressCandidates = true
  163. conn, webRTCSDP, metrics, err = newWebRTCConnForOffer(
  164. context.Background(), config, hasPersonalCompartmentIDs)
  165. if err != nil {
  166. return errors.Trace(err)
  167. }
  168. defer conn.Close()
  169. SDP = []byte(webRTCSDP.SDP)
  170. hasPrivateIP := metrics.hasPrivateIP
  171. if !hasPrivateIP {
  172. // Test may run on host without RFC 1918/4193 private IP address
  173. fmt.Printf("No private IP address\n")
  174. }
  175. // Filter should retain any private IP address(es)
  176. filteredSDP, metrics, err = filterSDPAddresses(
  177. SDP,
  178. errorOnNoCandidates,
  179. nil,
  180. common.GeoIPData{},
  181. allowPrivateIPAddressCandidates,
  182. filterPrivateIPAddressCandidates)
  183. if err != nil {
  184. return errors.Trace(err)
  185. }
  186. if hasPrivateIP != metrics.hasPrivateIP {
  187. return errors.TraceNew("unexpected metrics.hasPrivateIP")
  188. }
  189. if len(filteredSDP) != len(SDP) {
  190. return errors.TraceNew("unexpected SDP length")
  191. }
  192. return nil
  193. }