1
0

shadowtls_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. package main
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "net"
  6. "net/http"
  7. "net/netip"
  8. "testing"
  9. C "github.com/sagernet/sing-box/constant"
  10. "github.com/sagernet/sing-box/option"
  11. "github.com/sagernet/sing-shadowsocks/shadowaead_2022"
  12. "github.com/sagernet/sing/common"
  13. F "github.com/sagernet/sing/common/format"
  14. "github.com/sagernet/sing/common/json/badoption"
  15. "github.com/stretchr/testify/require"
  16. )
  17. func TestShadowTLS(t *testing.T) {
  18. t.Run("v1", func(t *testing.T) {
  19. testShadowTLS(t, 1, "", false)
  20. })
  21. t.Run("v2", func(t *testing.T) {
  22. testShadowTLS(t, 2, "hello", false)
  23. })
  24. t.Run("v3", func(t *testing.T) {
  25. testShadowTLS(t, 3, "hello", false)
  26. })
  27. t.Run("v2-utls", func(t *testing.T) {
  28. testShadowTLS(t, 2, "hello", true)
  29. })
  30. t.Run("v3-utls", func(t *testing.T) {
  31. testShadowTLS(t, 3, "hello", true)
  32. })
  33. }
  34. func testShadowTLS(t *testing.T, version int, password string, utlsEanbled bool) {
  35. method := shadowaead_2022.List[0]
  36. ssPassword := mkBase64(t, 16)
  37. startInstance(t, option.Options{
  38. Inbounds: []option.Inbound{
  39. {
  40. Type: C.TypeMixed,
  41. Options: &option.HTTPMixedInboundOptions{
  42. ListenOptions: option.ListenOptions{
  43. Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
  44. ListenPort: clientPort,
  45. },
  46. },
  47. },
  48. {
  49. Type: C.TypeShadowTLS,
  50. Tag: "in",
  51. Options: &option.ShadowTLSInboundOptions{
  52. ListenOptions: option.ListenOptions{
  53. Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
  54. ListenPort: serverPort,
  55. InboundOptions: option.InboundOptions{
  56. Detour: "detour",
  57. },
  58. },
  59. Handshake: option.ShadowTLSHandshakeOptions{
  60. ServerOptions: option.ServerOptions{
  61. Server: "google.com",
  62. ServerPort: 443,
  63. },
  64. },
  65. Version: version,
  66. Password: password,
  67. Users: []option.ShadowTLSUser{{Password: password}},
  68. },
  69. },
  70. {
  71. Type: C.TypeShadowsocks,
  72. Tag: "detour",
  73. Options: &option.ShadowsocksInboundOptions{
  74. ListenOptions: option.ListenOptions{
  75. Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
  76. ListenPort: otherPort,
  77. },
  78. Method: method,
  79. Password: ssPassword,
  80. },
  81. },
  82. },
  83. Outbounds: []option.Outbound{
  84. {
  85. Type: C.TypeShadowsocks,
  86. Options: &option.ShadowsocksOutboundOptions{
  87. Method: method,
  88. Password: ssPassword,
  89. DialerOptions: option.DialerOptions{
  90. Detour: "detour",
  91. },
  92. },
  93. },
  94. {
  95. Type: C.TypeShadowTLS,
  96. Tag: "detour",
  97. Options: &option.ShadowTLSOutboundOptions{
  98. ServerOptions: option.ServerOptions{
  99. Server: "127.0.0.1",
  100. ServerPort: serverPort,
  101. },
  102. OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
  103. TLS: &option.OutboundTLSOptions{
  104. Enabled: true,
  105. ServerName: "google.com",
  106. UTLS: &option.OutboundUTLSOptions{
  107. Enabled: utlsEanbled,
  108. },
  109. },
  110. },
  111. Version: version,
  112. Password: password,
  113. },
  114. },
  115. {
  116. Type: C.TypeDirect,
  117. Tag: "direct",
  118. },
  119. },
  120. Route: &option.RouteOptions{
  121. Rules: []option.Rule{
  122. {
  123. Type: C.RuleTypeDefault,
  124. DefaultOptions: option.DefaultRule{
  125. RawDefaultRule: option.RawDefaultRule{
  126. Inbound: []string{"detour"},
  127. },
  128. RuleAction: option.RuleAction{
  129. Action: C.RuleActionTypeRoute,
  130. RouteOptions: option.RouteActionOptions{
  131. Outbound: "direct",
  132. },
  133. },
  134. },
  135. },
  136. },
  137. },
  138. })
  139. testTCP(t, clientPort, testPort)
  140. }
  141. func TestShadowTLSFallback(t *testing.T) {
  142. startInstance(t, option.Options{
  143. Inbounds: []option.Inbound{
  144. {
  145. Type: C.TypeShadowTLS,
  146. Options: &option.ShadowTLSInboundOptions{
  147. ListenOptions: option.ListenOptions{
  148. Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
  149. ListenPort: serverPort,
  150. },
  151. Handshake: option.ShadowTLSHandshakeOptions{
  152. ServerOptions: option.ServerOptions{
  153. Server: "bing.com",
  154. ServerPort: 443,
  155. },
  156. },
  157. Version: 3,
  158. Users: []option.ShadowTLSUser{
  159. {Password: "hello"},
  160. },
  161. },
  162. },
  163. },
  164. })
  165. client := &http.Client{
  166. Transport: &http.Transport{
  167. DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  168. var d net.Dialer
  169. return d.DialContext(ctx, network, "127.0.0.1:"+F.ToString(serverPort))
  170. },
  171. },
  172. }
  173. response, err := client.Get("https://bing.com")
  174. require.NoError(t, err)
  175. require.Equal(t, response.StatusCode, 200)
  176. response.Body.Close()
  177. client.CloseIdleConnections()
  178. }
  179. func TestShadowTLSFallbackWildcardAll(t *testing.T) {
  180. startInstance(t, option.Options{
  181. Inbounds: []option.Inbound{
  182. {
  183. Type: C.TypeShadowTLS,
  184. Options: &option.ShadowTLSInboundOptions{
  185. ListenOptions: option.ListenOptions{
  186. Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
  187. ListenPort: serverPort,
  188. },
  189. Version: 3,
  190. Users: []option.ShadowTLSUser{
  191. {Password: "hello"},
  192. },
  193. WildcardSNI: option.ShadowTLSWildcardSNIAll,
  194. },
  195. },
  196. },
  197. })
  198. client := &http.Client{
  199. Transport: &http.Transport{
  200. DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  201. var d net.Dialer
  202. return d.DialContext(ctx, network, "127.0.0.1:"+F.ToString(serverPort))
  203. },
  204. },
  205. }
  206. response, err := client.Get("https://www.bing.com")
  207. require.NoError(t, err)
  208. require.Equal(t, response.StatusCode, 200)
  209. response.Body.Close()
  210. client.CloseIdleConnections()
  211. }
  212. func TestShadowTLSFallbackWildcardAuthedFail(t *testing.T) {
  213. startInstance(t, option.Options{
  214. Inbounds: []option.Inbound{
  215. {
  216. Type: C.TypeShadowTLS,
  217. Options: &option.ShadowTLSInboundOptions{
  218. ListenOptions: option.ListenOptions{
  219. Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
  220. ListenPort: serverPort,
  221. },
  222. Handshake: option.ShadowTLSHandshakeOptions{
  223. ServerOptions: option.ServerOptions{
  224. Server: "bing.com",
  225. ServerPort: 443,
  226. },
  227. },
  228. Version: 3,
  229. Users: []option.ShadowTLSUser{
  230. {Password: "hello"},
  231. },
  232. WildcardSNI: option.ShadowTLSWildcardSNIAuthed,
  233. },
  234. },
  235. },
  236. })
  237. client := &http.Client{
  238. Transport: &http.Transport{
  239. DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  240. var d net.Dialer
  241. return d.DialContext(ctx, network, "127.0.0.1:"+F.ToString(serverPort))
  242. },
  243. },
  244. }
  245. _, err := client.Get("https://baidu.com")
  246. expected := &tls.CertificateVerificationError{}
  247. require.ErrorAs(t, err, &expected)
  248. client.CloseIdleConnections()
  249. }
  250. func TestShadowTLSFallbackWildcardOffFail(t *testing.T) {
  251. startInstance(t, option.Options{
  252. Inbounds: []option.Inbound{
  253. {
  254. Type: C.TypeShadowTLS,
  255. Options: &option.ShadowTLSInboundOptions{
  256. ListenOptions: option.ListenOptions{
  257. Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
  258. ListenPort: serverPort,
  259. },
  260. Handshake: option.ShadowTLSHandshakeOptions{
  261. ServerOptions: option.ServerOptions{
  262. Server: "bing.com",
  263. ServerPort: 443,
  264. },
  265. },
  266. Version: 3,
  267. Users: []option.ShadowTLSUser{
  268. {Password: "hello"},
  269. },
  270. WildcardSNI: option.ShadowTLSWildcardSNIOff,
  271. },
  272. },
  273. },
  274. })
  275. client := &http.Client{
  276. Transport: &http.Transport{
  277. DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  278. var d net.Dialer
  279. return d.DialContext(ctx, network, "127.0.0.1:"+F.ToString(serverPort))
  280. },
  281. },
  282. }
  283. _, err := client.Get("https://baidu.com")
  284. expected := &tls.CertificateVerificationError{}
  285. require.ErrorAs(t, err, &expected)
  286. client.CloseIdleConnections()
  287. }
  288. func TestShadowTLSInbound(t *testing.T) {
  289. method := shadowaead_2022.List[0]
  290. password := mkBase64(t, 16)
  291. startDockerContainer(t, DockerOptions{
  292. Image: ImageShadowTLS,
  293. Ports: []uint16{serverPort, otherPort},
  294. EntryPoint: "shadow-tls",
  295. Cmd: []string{"--v3", "--threads", "1", "client", "--listen", "0.0.0.0:" + F.ToString(otherPort), "--server", "127.0.0.1:" + F.ToString(serverPort), "--sni", "google.com", "--password", password},
  296. })
  297. startInstance(t, option.Options{
  298. Inbounds: []option.Inbound{
  299. {
  300. Type: C.TypeMixed,
  301. Tag: "in",
  302. Options: &option.HTTPMixedInboundOptions{
  303. ListenOptions: option.ListenOptions{
  304. Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
  305. ListenPort: clientPort,
  306. },
  307. },
  308. },
  309. {
  310. Type: C.TypeShadowTLS,
  311. Options: &option.ShadowTLSInboundOptions{
  312. ListenOptions: option.ListenOptions{
  313. Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
  314. ListenPort: serverPort,
  315. InboundOptions: option.InboundOptions{
  316. Detour: "detour",
  317. },
  318. },
  319. Handshake: option.ShadowTLSHandshakeOptions{
  320. ServerOptions: option.ServerOptions{
  321. Server: "google.com",
  322. ServerPort: 443,
  323. },
  324. },
  325. Version: 3,
  326. Users: []option.ShadowTLSUser{
  327. {Password: password},
  328. },
  329. },
  330. },
  331. {
  332. Type: C.TypeShadowsocks,
  333. Tag: "detour",
  334. Options: &option.ShadowsocksInboundOptions{
  335. ListenOptions: option.ListenOptions{
  336. Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
  337. },
  338. Method: method,
  339. Password: password,
  340. },
  341. },
  342. },
  343. Outbounds: []option.Outbound{
  344. {
  345. Type: C.TypeDirect,
  346. },
  347. {
  348. Type: C.TypeShadowsocks,
  349. Tag: "out",
  350. Options: &option.ShadowsocksOutboundOptions{
  351. ServerOptions: option.ServerOptions{
  352. Server: "127.0.0.1",
  353. ServerPort: otherPort,
  354. },
  355. Method: method,
  356. Password: password,
  357. },
  358. },
  359. },
  360. Route: &option.RouteOptions{
  361. Rules: []option.Rule{
  362. {
  363. Type: C.RuleTypeDefault,
  364. DefaultOptions: option.DefaultRule{
  365. RawDefaultRule: option.RawDefaultRule{
  366. Inbound: []string{"in"},
  367. },
  368. RuleAction: option.RuleAction{
  369. Action: C.RuleActionTypeRoute,
  370. RouteOptions: option.RouteActionOptions{
  371. Outbound: "out",
  372. },
  373. },
  374. },
  375. },
  376. },
  377. },
  378. })
  379. testTCP(t, clientPort, testPort)
  380. }
  381. func TestShadowTLSOutbound(t *testing.T) {
  382. method := shadowaead_2022.List[0]
  383. password := mkBase64(t, 16)
  384. startDockerContainer(t, DockerOptions{
  385. Image: ImageShadowTLS,
  386. Ports: []uint16{serverPort, otherPort},
  387. EntryPoint: "shadow-tls",
  388. Cmd: []string{"--v3", "--threads", "1", "server", "--listen", "0.0.0.0:" + F.ToString(serverPort), "--server", "127.0.0.1:" + F.ToString(otherPort), "--tls", "google.com:443", "--password", "hello"},
  389. Env: []string{"RUST_LOG=trace"},
  390. })
  391. startInstance(t, option.Options{
  392. Inbounds: []option.Inbound{
  393. {
  394. Type: C.TypeMixed,
  395. Options: &option.HTTPMixedInboundOptions{
  396. ListenOptions: option.ListenOptions{
  397. Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
  398. ListenPort: clientPort,
  399. },
  400. },
  401. },
  402. {
  403. Type: C.TypeShadowsocks,
  404. Tag: "detour",
  405. Options: &option.ShadowsocksInboundOptions{
  406. ListenOptions: option.ListenOptions{
  407. Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
  408. ListenPort: otherPort,
  409. },
  410. Method: method,
  411. Password: password,
  412. },
  413. },
  414. },
  415. Outbounds: []option.Outbound{
  416. {
  417. Type: C.TypeShadowsocks,
  418. Options: &option.ShadowsocksOutboundOptions{
  419. Method: method,
  420. Password: password,
  421. DialerOptions: option.DialerOptions{
  422. Detour: "detour",
  423. },
  424. },
  425. },
  426. {
  427. Type: C.TypeShadowTLS,
  428. Tag: "detour",
  429. Options: &option.ShadowTLSOutboundOptions{
  430. ServerOptions: option.ServerOptions{
  431. Server: "127.0.0.1",
  432. ServerPort: serverPort,
  433. },
  434. OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
  435. TLS: &option.OutboundTLSOptions{
  436. Enabled: true,
  437. ServerName: "google.com",
  438. },
  439. },
  440. Version: 3,
  441. Password: "hello",
  442. },
  443. },
  444. {
  445. Type: C.TypeDirect,
  446. Tag: "direct",
  447. },
  448. },
  449. Route: &option.RouteOptions{
  450. Rules: []option.Rule{
  451. {
  452. Type: C.RuleTypeDefault,
  453. DefaultOptions: option.DefaultRule{
  454. RawDefaultRule: option.RawDefaultRule{
  455. Inbound: []string{"detour"},
  456. },
  457. RuleAction: option.RuleAction{
  458. Action: C.RuleActionTypeRoute,
  459. RouteOptions: option.RouteActionOptions{
  460. Outbound: "direct",
  461. },
  462. },
  463. },
  464. },
  465. },
  466. },
  467. })
  468. testTCP(t, clientPort, testPort)
  469. }