Skip to content

Commit

Permalink
socks5 over ws
Browse files Browse the repository at this point in the history
  • Loading branch information
EricZhou committed Nov 8, 2024
1 parent 4e1bcef commit 2fcd78a
Show file tree
Hide file tree
Showing 6 changed files with 363 additions and 12 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/gin-contrib/cors v1.3.0
github.com/gin-gonic/gin v1.4.0
github.com/gliderlabs/ssh v0.2.2
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.4.0
github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365
github.com/inconshreveable/mousetrap v1.0.0 // indirect
Expand Down
14 changes: 2 additions & 12 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
Expand Down Expand Up @@ -69,7 +67,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand All @@ -80,6 +77,8 @@ github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
Expand Down Expand Up @@ -169,7 +168,6 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
Expand Down Expand Up @@ -197,7 +195,6 @@ github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
Expand All @@ -213,7 +210,6 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
Expand All @@ -231,11 +227,8 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
Expand All @@ -244,7 +237,6 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand All @@ -254,7 +246,6 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -279,7 +270,6 @@ google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRn
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
Expand Down
265 changes: 265 additions & 0 deletions shadowos/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
package shadowos

import (
"fmt"
"github.com/gorilla/websocket"
"github.com/libragen/felix/util"
"io"
"log"
"net"
)

type VlessCmd byte
type VlessAddrType byte

const (
VlessCmdTcp VlessCmd = 0x01
VlessCmdUdp VlessCmd = 0x02
VlessCmdMux VlessCmd = 0x03

VlessAddrTypeIPv4 VlessAddrType = 0x01
VlessAddrTypeDomain VlessAddrType = 0x02
VlessAddrTypeIPv6 VlessAddrType = 0x03

SOCKS5VERSION = 0x05
CMD_CONNECT = 0x01
CMD_BIND = 0x02
CMD_UDP_ASSOC = 0x03
)

type ShadowosApp struct {
AddrWs string
AddrSocks5 string
UUID string
}

func (ss *ShadowosApp) Run() {
listener, err := net.Listen("tcp", ss.AddrSocks5)
if err != nil {
log.Fatalf("Failed to listen on port %s: %v", ss.AddrSocks5, err)
}
defer listener.Close()
log.Println("SOCKS5 server listening on: " + ss.AddrSocks5)

for {
conn, err := listener.Accept()
if err != nil {
log.Printf("Failed to accept connection: %v", err)
continue
}
go ss.handleConnection(conn)
}
}

func socks5packet(conn net.Conn, uuidS string) (connData []byte, err error) {
uuidBytes, err := util.UUID2bytes(uuidS)
if err != nil {
return nil, fmt.Errorf("failed to parse UUID: %w", err)
}
buf := make([]byte, 2)
if _, err := io.ReadFull(conn, buf); err != nil {
return nil, fmt.Errorf("failed to read version and nmethods: %w", err)
}
if buf[0] != SOCKS5VERSION {
return nil, fmt.Errorf("socks5 only. unsupported SOCKS version: %d", buf[0])
}

// Read the supported authentication methods
nMethods := int(buf[1])
nMethodsData := make([]byte, nMethods)
if _, err := io.ReadFull(conn, nMethodsData); err != nil {
return nil, fmt.Errorf("failed to read methods: %w", err)
}

// Select no authentication (0x00)
if _, err := conn.Write([]byte{SOCKS5VERSION, 0x00}); err != nil {
return nil, fmt.Errorf("failed to write method selection: %w", err)
}

// Read the request
buf = make([]byte, 4)
if _, err := io.ReadFull(conn, buf); err != nil {
return nil, fmt.Errorf("failed to read request: %w", err)
}

if buf[0] != SOCKS5VERSION {
return nil, fmt.Errorf("unsupported SOCKS version: %d", buf[0])
}
var vlessCmd VlessCmd
if buf[1] == CMD_CONNECT {
vlessCmd = VlessCmdTcp
} else if buf[1] == CMD_UDP_ASSOC {
vlessCmd = VlessCmdUdp
} else {
return nil, fmt.Errorf("unsupported command: %d", buf[1])
}
var vlessAddrType VlessAddrType
addrLen := 0
// Read the address
switch buf[3] {
case 0x01: // IPv4
vlessAddrType = VlessAddrTypeIPv4
addrLen = net.IPv4len
case 0x03: // Domain name
vlessAddrType = VlessAddrTypeDomain
if _, err := io.ReadFull(conn, buf[:1]); err != nil {
return nil, fmt.Errorf("failed to read domain length: %w", err)
}
addrLen = int(buf[0])
case 0x04: // IPv6
vlessAddrType = VlessAddrTypeIPv6
addrLen = net.IPv6len
default:
return nil, fmt.Errorf("unsupported address type: %d", buf[3])
}
addrBytes := make([]byte, addrLen)
if _, err := io.ReadFull(conn, addrBytes); err != nil {
return nil, fmt.Errorf("failed to read address: %w", err)
}
if vlessAddrType == VlessAddrTypeDomain {
addrBytes = append([]byte{byte(addrLen)}, addrBytes...)
}

// Read the port
remotePort := make([]byte, 2)

if _, err = io.ReadFull(conn, remotePort); err != nil {
log.Printf("Failed to read port: %v", err)
return
}
// Construct vless packet
connData = make([]byte, 0, 1+16+1+1+2+1+len(addrBytes))
connData = append(connData, 0x01) // version
connData = append(connData, uuidBytes...) //16 bytes of UUID
connData = append(connData, 0x00) // additional info length M
connData = append(connData, byte(vlessCmd)) //1 byte of command
connData = append(connData, remotePort...) //2 bytes of port
connData = append(connData, byte(vlessAddrType)) //1 byte of address type
connData = append(connData, addrBytes...) //n bytes of address
return connData, nil
}

func (ss *ShadowosApp) handleConnection(conn net.Conn) {
defer conn.Close()

connBytes, err := socks5packet(conn, ss.UUID)
if err != nil {
log.Printf("failed to parse SOCKS5 request: %v", err)
return
}
// Read the version and number of authentication methods

// Connect to the target server
ws, err := NewWebsocketConn(ss.AddrWs)
if err != nil {
log.Printf("failed to connect to target: %v", err)
conn.Write([]byte{SOCKS5VERSION, 0x01, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
return
} else { // Send success response
conn.Write([]byte{SOCKS5VERSION, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
}
defer ws.Close()
// Relay data between client and target server
pipeWebsocketSocks5(ws, conn, connBytes)
}

func pipeWebsocketSocks5(ws *WebsocketConn, s5 net.Conn, firstData []byte) {
go func() { // s5 -> ws
buf := make([]byte, 1024)
for {

n, err := s5.Read(buf)
if err == io.EOF {
log.Println("EOF from socks5")
continue
}
if err != nil {
log.Printf("read from socks5 error %T", err)
log.Println("read from socks5 error", err)
continue
}
log.Println("read from socks5", n)
data := buf[:n]
if len(firstData) > 0 {
log.Println("send version header only once")
data = append(firstData, buf[:n]...)
firstData = nil
}
_, err = ws.Write(data)
if err != nil {
log.Println("write error", err)
return
}

}
}()
isFirstData := true
for {
buf := make([]byte, 1024)
n, err := ws.Read(buf)
if err == io.EOF {
log.Println("EOF from ws")
continue
}
if err != nil {
log.Printf("read from ws -> socks5 error %T", err)
log.Println("read from ws -> socks5 error", err)
continue
}
fromByteIndex := 0
// skip the first data
if isFirstData && n >= 2 {
extraN := buf[1]
isFirstData = false
fromByteIndex = 2 + int(extraN)
}
_, err = s5.Write(buf[fromByteIndex:n])
if err != nil {
log.Println(" ws -> socks5 error", err)
return
}

}

}

type WebsocketConn struct {
c *websocket.Conn
}

func NewWebsocketConn(url string) (*WebsocketConn, error) {
c, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
return nil, fmt.Errorf("failed to connect to WebSocket server: %w", err)
}
return &WebsocketConn{c: c}, nil
}

func (w WebsocketConn) Close() error {
err := w.c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Println("failed to send close message", err)
return err
}
return w.c.Close()
}

func (w WebsocketConn) Write(bytes []byte) (int, error) {
err := w.c.WriteMessage(websocket.BinaryMessage, bytes)
if err != nil {
return 0, err
}
return len(bytes), nil
}

func (w WebsocketConn) Read(p []byte) (n int, err error) {
messageType, bytes, err := w.c.ReadMessage()
if err != nil {
return 0, err
}
if messageType != websocket.BinaryMessage {
return 0, fmt.Errorf("unexpected message type: %d", messageType)
}
n = copy(p, bytes)
return n, nil
}
19 changes: 19 additions & 0 deletions shadowos/app_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package shadowos

import (
"log"
"testing"
)

var (
app = &ShadowosApp{
AddrWs: "ws://127.0.0.1:8787",
AddrSocks5: "127.0.0.1:2080",
UUID: "53881505-c10c-464a-8949-e57184a576a9",
}
)

func TestShadowosApp_Run(t *testing.T) {
log.SetFlags(log.LstdFlags | log.Lshortfile)
app.Run()
}
Loading

0 comments on commit 2fcd78a

Please sign in to comment.