| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- /*
- * Copyright (c) 2025, Psiphon Inc.
- * All rights reserved.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- package server
- import (
- "bytes"
- "crypto/rand"
- "io"
- "net"
- "testing"
- "time"
- "github.com/Jigsaw-Code/outline-sdk/transport"
- "github.com/Jigsaw-Code/outline-sdk/transport/shadowsocks"
- "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
- "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
- )
- func TestShadowsocksServer(t *testing.T) {
- listener, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("net.Listen failed %v", err)
- }
- defer listener.Close()
- numIrregularTunnels := 0
- irregularTunnelLogger := func(clientIP string, tunnelError error, logFields common.LogFields) {
- numIrregularTunnels++
- }
- secretText := "TEST"
- listener, err = ListenShadowsocks(nil, listener, secretText, irregularTunnelLogger)
- if err != nil {
- t.Fatalf("ListenShadowsocks failed %v", err)
- }
- type listenerState struct {
- err error
- recv []byte
- }
- wantRecv := []byte("hello world")
- runListener := func(listener net.Listener, recv chan *listenerState) {
- conn, err := listener.Accept()
- if err != nil {
- recv <- &listenerState{
- err: errors.TraceMsg(err, "listener.Accept failed"),
- }
- return
- }
- // Set a short deadline as Read will block indefinitely if there is
- // no deadline and a replay is detected because the underlying conn
- // will be drained synchronously before Read returns and the client
- // will not hang up until it receives a response from the server.
- err = conn.SetDeadline(time.Now().Add(1 * time.Second))
- if err != nil {
- recv <- &listenerState{
- err: errors.TraceMsg(err, "conn.SetDeadline failed"),
- }
- return
- }
- defer conn.Close()
- b := make([]byte, len(wantRecv))
- // A single Read should be sufficient because multiple requests
- // in a single connection are not supported by this test.
- n, err := conn.Read(b)
- if err != nil {
- recv <- &listenerState{
- err: errors.TraceMsg(err, "conn.Read failed"),
- }
- return
- }
- b = b[:n]
- _, err = conn.Write(b)
- if err != nil {
- recv <- &listenerState{
- err: errors.TraceMsg(err, "conn.Write failed"),
- }
- return
- }
- recv <- &listenerState{
- recv: b,
- err: nil,
- }
- }
- recv := make(chan *listenerState)
- go runListener(listener, recv)
- conn, err := net.Dial("tcp", listener.Addr().String())
- if err != nil {
- t.Fatalf("net.Dial failed %v", err)
- }
- defer conn.Close()
- key, err := shadowsocks.NewEncryptionKey(shadowsocks.CHACHA20IETFPOLY1305, secretText)
- if err != nil {
- t.Fatalf("shadowsocks.NewEncryptionKey failed %v", err)
- }
- // Based on shadowsocks.DialStream
- clientToServerRecorder := NewWriteRecorder(conn)
- ssw := shadowsocks.NewWriter(clientToServerRecorder, key)
- serverToClientRecorder := NewReadRecorder(conn)
- ssr := shadowsocks.NewReader(serverToClientRecorder, key)
- conn = transport.WrapConn(conn.(*net.TCPConn), ssr, ssw)
- n, err := conn.Write(wantRecv)
- if err != nil {
- t.Fatalf("conn.Write failed %v", err)
- }
- if n != len(wantRecv) {
- t.Fatalf("expected to write %d bytes but wrote %d", len(wantRecv), n)
- }
- // read response
- b := make([]byte, 512)
- n, err = conn.Read(b)
- if err != nil {
- t.Fatalf("conn.Read failed %v", err)
- }
- b = b[:n]
- r := <-recv
- if r.err != nil {
- t.Fatalf("listener failed %v", r.err)
- }
- if !bytes.Equal(r.recv, wantRecv) {
- t.Fatalf("expected \"%s\" of len %d but got \"%s\" of len %d", string(wantRecv), len(wantRecv), string(r.recv), len(r.recv))
- }
- // Server echos bytes back
- if !bytes.Equal(b, wantRecv) {
- t.Fatalf("expected \"%s\" of len %d but got \"%s\" of len %d", string(wantRecv), len(wantRecv), string(b), len(b))
- }
- if numIrregularTunnels > 0 {
- t.Fatal("expected no irregular tunnels")
- }
- // Mimic a replay attack
- go runListener(listener, recv)
- conn, err = net.Dial("tcp", listener.Addr().String())
- if err != nil {
- t.Fatalf("net.Dial failed %v", err)
- }
- defer conn.Close()
- _, err = conn.Write(clientToServerRecorder.Bytes())
- if err != nil {
- t.Fatalf("conn.Read failed %v", err)
- }
- r = <-recv
- if r.err == nil {
- t.Fatalf("expected error")
- }
- if numIrregularTunnels != 1 {
- t.Fatal("expected 1 irregular tunnel")
- }
- // Mimic a reflection attack
- go runListener(listener, recv)
- conn, err = net.Dial("tcp", listener.Addr().String())
- if err != nil {
- t.Fatalf("net.Dial failed %v", err)
- }
- defer conn.Close()
- _, err = conn.Write(serverToClientRecorder.Bytes())
- if err != nil {
- t.Fatalf("conn.Read failed %v", err)
- }
- r = <-recv
- if r.err == nil {
- t.Fatalf("expected error")
- }
- if numIrregularTunnels != 2 {
- t.Fatal("expected 2 irregular tunnels")
- }
- // Mimic random bytes
- go runListener(listener, recv)
- conn, err = net.Dial("tcp", listener.Addr().String())
- if err != nil {
- t.Fatalf("net.Dial failed %v", err)
- }
- defer conn.Close()
- randomBytes := make([]byte, clientToServerRecorder.Len())
- _, err = rand.Read(randomBytes)
- if err != nil {
- t.Fatalf("rand.Read failed %v", err)
- }
- _, err = conn.Write(randomBytes)
- if err != nil {
- t.Fatalf("conn.Read failed %v", err)
- }
- r = <-recv
- if r.err == nil {
- t.Fatalf("expected error")
- }
- // Note: currently an invalid message from the client is not logged as an
- // irregular tunnel due to the limitations described in
- // ShadowsocksConn.Read so do not expect another irregular tunnel.
- if numIrregularTunnels != 2 {
- t.Fatal("expected 2 irregular tunnels")
- }
- }
- type writeRecorder struct {
- io.Writer
- bytes.Buffer
- }
- func NewWriteRecorder(writer io.Writer) *writeRecorder {
- return &writeRecorder{
- Writer: writer,
- }
- }
- func (w *writeRecorder) Write(p []byte) (n int, err error) {
- _, err = w.Buffer.Write(p)
- if err != nil {
- panic(err)
- }
- return w.Writer.Write(p)
- }
- type readRecorder struct {
- io.Reader
- bytes.Buffer
- }
- func NewReadRecorder(reader io.Reader) *readRecorder {
- return &readRecorder{
- Reader: reader,
- }
- }
- func (r *readRecorder) Read(p []byte) (n int, err error) {
- n, err = r.Reader.Read(p)
- r.Buffer.Write(p[:n])
- return n, err
- }
|