From 35818ec65791bc26ff07bc69ac09eae80e6010cb Mon Sep 17 00:00:00 2001 From: mojocn Date: Sat, 9 Nov 2024 15:53:47 +0800 Subject: [PATCH] temp ok --- go.mod | 38 +++++++++- go.sum | 5 +- shadowos/app.go | 154 ++++++++++++++++++---------------------- shadowos/app_test.go | 4 +- shadowos/sock5_test.go | 2 +- shadowos/socks5_test.py | 79 +++++++++++++++++++++ 6 files changed, 189 insertions(+), 93 deletions(-) create mode 100644 shadowos/socks5_test.py diff --git a/go.mod b/go.mod index a5919dd..44f1026 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/mojocn/felix -go 1.12 +go 1.23 require ( github.com/PuerkitoBio/goquery v1.5.0 @@ -11,7 +11,7 @@ require ( 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/gorilla/websocket v1.5.3 github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365 github.com/jinzhu/gorm v1.9.5 github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a @@ -30,3 +30,37 @@ require ( golang.org/x/net v0.0.0-20190522155817-f3200d17e092 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b ) + +require ( + cloud.google.com/go v0.37.4 // indirect + github.com/andybalholm/cascadia v1.0.0 // indirect + github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect + github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02 // indirect + github.com/fsnotify/fsnotify v1.4.7 // indirect + github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 // indirect + github.com/go-sql-driver/mysql v1.4.1 // indirect + github.com/golang/protobuf v1.3.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/johntdyer/slack-go v0.0.0-20180213144715-95fac1160b22 // indirect + github.com/json-iterator/go v1.1.6 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect + github.com/kr/fs v0.1.0 // indirect + github.com/lib/pq v1.1.0 // indirect + github.com/magiconair/properties v1.8.0 // indirect + github.com/mattn/go-colorable v0.1.1 // indirect + github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/pelletier/go-toml v1.2.0 // indirect + github.com/pkg/errors v0.8.0 // indirect + github.com/spf13/afero v1.1.2 // indirect + github.com/spf13/cast v1.3.0 // indirect + github.com/spf13/jwalterweatherman v1.0.0 // indirect + github.com/spf13/pflag v1.0.3 // indirect + github.com/ugorji/go v1.1.4 // indirect + golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 // indirect + google.golang.org/appengine v1.4.0 // indirect + gopkg.in/go-playground/validator.v8 v8.18.2 // indirect + gopkg.in/yaml.v2 v2.2.2 // indirect +) diff --git a/go.sum b/go.sum index 4975844..9acabab 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,6 @@ github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02/go.mod h1:zA github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dhowden/itl v0.0.0-20170329215456-9fbe21093131/go.mod h1:eVWQJVQ67aMvYhpkDwaH2Goy2vo6v8JCMfGXfQ9sPtw= -github.com/dhowden/plist v0.0.0-20141002110153-5db6e0d9931a/go.mod h1:sLjdR6uwx3L6/Py8F+QgAfeiuY87xuYGwCDqRFrvCzw= github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 h1:OtSeLS5y0Uy01jaKK4mA/WVIYtpzVm63vLVAPzJXigg= github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8/go.mod h1:apkPC/CR3s48O2D7Y++n1XWEpgPNNCjXYga3PPbJe2E= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= @@ -86,8 +84,9 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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= -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= diff --git a/shadowos/app.go b/shadowos/app.go index 9a76c71..6f7400c 100644 --- a/shadowos/app.go +++ b/shadowos/app.go @@ -51,7 +51,7 @@ func (ss *ShadowosApp) Run() { } } -func socks5packet(conn net.Conn, uuidS string) (connData []byte, err error) { +func handshake(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) @@ -141,8 +141,7 @@ func socks5packet(conn net.Conn, uuidS string) (connData []byte, err error) { func (ss *ShadowosApp) handleConnection(conn net.Conn) { defer conn.Close() - - connBytes, err := socks5packet(conn, ss.UUID) + connBytes, err := handshake(conn, ss.UUID) if err != nil { log.Printf("failed to parse SOCKS5 request: %v", err) return @@ -150,7 +149,7 @@ func (ss *ShadowosApp) handleConnection(conn net.Conn) { // Read the version and number of authentication methods // Connect to the target server - ws, err := NewWebsocketConn(ss.AddrWs) + session, err := NewProxySession(ss.AddrWs, connBytes) 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}) @@ -158,108 +157,91 @@ func (ss *ShadowosApp) handleConnection(conn net.Conn) { } 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 - } + defer session.Close() - } + go session.socks2ws(conn) + session.ws2socks(conn) } -type WebsocketConn struct { - c *websocket.Conn +type ProxySession struct { + ws *websocket.Conn + connData []byte + isFirstData bool + ch chan struct{} } -func NewWebsocketConn(url string) (*WebsocketConn, error) { +func NewProxySession(url string, initialData []byte) (*ProxySession, 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 + return &ProxySession{ + ws: c, + connData: initialData, + isFirstData: true, + ch: make(chan struct{}, 1), + }, nil } -func (w WebsocketConn) Close() error { - err := w.c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) +func (ps ProxySession) Close() error { + log.Println("websocket close message sent") + + err := ps.ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) if err != nil { log.Println("failed to send close message", err) return err } - return w.c.Close() + return ps.ws.Close() } -func (w WebsocketConn) Write(bytes []byte) (int, error) { - err := w.c.WriteMessage(websocket.BinaryMessage, bytes) - if err != nil { - return 0, err +func (ps *ProxySession) socks2ws(socks net.Conn) { + for { + buf := make([]byte, 1024) + n, err := socks.Read(buf) + log.Println("n", n) + if n > 0 { + log.Println("socks read N:", n) + if len(ps.connData) > 0 { + buf = append(ps.connData, buf[:n]...) + ps.connData = nil + } else { + buf = buf[:n] + } + err = ps.ws.WriteMessage(websocket.BinaryMessage, buf) + } + if err == io.EOF { + log.Println("socks5 connection closed") + return + } + if err != nil { + log.Println("failed to read from socks5 to websocket", err) + return + } } - 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) +func (ps *ProxySession) ws2socks(socks net.Conn) { + for { + messageType, data, err := ps.ws.ReadMessage() + log.Println("messageType", messageType) + log.Println("data", data) + log.Println("err", err) + if len(data) > 0 && messageType == websocket.BinaryMessage || messageType == websocket.TextMessage { + if ps.isFirstData && len(data) > 1 { + ps.isFirstData = false + extraN := int(data[1]) + 2 + data = data[extraN:] + } + _, err = socks.Write(data) + } + if err == io.EOF { + return + } + if err != nil { + log.Println("messageType", messageType) + log.Println("failed to read from websocket to socks5", err) + return + } } - n = copy(p, bytes) - return n, nil } diff --git a/shadowos/app_test.go b/shadowos/app_test.go index 2dd4276..a670b0a 100644 --- a/shadowos/app_test.go +++ b/shadowos/app_test.go @@ -1,13 +1,14 @@ package shadowos import ( + "github.com/sirupsen/logrus" "log" "testing" ) var ( app = &ShadowosApp{ - AddrWs: "ws://127.0.0.1:8787", + AddrWs: "ws://127.0.0.1:8787/53881505-c10c-464a-8949-e57184a576a9?clash", AddrSocks5: "127.0.0.1:2080", UUID: "53881505-c10c-464a-8949-e57184a576a9", } @@ -15,5 +16,6 @@ var ( func TestShadowosApp_Run(t *testing.T) { log.SetFlags(log.LstdFlags | log.Lshortfile) + logrus.SetReportCaller(true) app.Run() } diff --git a/shadowos/sock5_test.go b/shadowos/sock5_test.go index 591f11b..92bbb6f 100644 --- a/shadowos/sock5_test.go +++ b/shadowos/sock5_test.go @@ -57,7 +57,7 @@ func TestHttpOverSocks5(t *testing.T) { } func TestAAAAA(t *testing.T) { - list := []string{"a", "b", "c", "1"} + list := []string{"a", "b", "ws", "1"} aa := list[1:2] t.Log(aa) } diff --git a/shadowos/socks5_test.py b/shadowos/socks5_test.py new file mode 100644 index 0000000..7af2d47 --- /dev/null +++ b/shadowos/socks5_test.py @@ -0,0 +1,79 @@ +import socket +import socks +import json +import requests + + +def test_dns_query(): + # Proxy details + proxy_host = '127.0.0.1' + proxy_port = 2080 + + # DNS server details + dns_server = '114.114.114.114' # Example: Google DNS + dns_port = 53 + + # Create a UDP socket with SOCKS5 proxy + sock = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM) + sock.set_proxy(socks.SOCKS5, proxy_host, proxy_port) + # DNS query message + # This is a simple DNS query for the domain "example.com" + # Transaction ID: 0x1234 + # Flags: Standard query + # Questions: 1 + # Answer RRs: 0 + # Authority RRs: 0 + # Additional RRs: 0 + # Query: example.com, Type: A, Class: IN + message = b'\x12\x34' # Transaction ID + message += b'\x01\x00' # Flags + message += b'\x00\x01' # Questions + message += b'\x00\x00' # Answer RRs + message += b'\x00\x00' # Authority RRs + message += b'\x00\x00' # Additional RRs + message += b'\x07example\x03com\x00' # Query: example.com + message += b'\x00\x01' # Type: A + message += b'\x00\x01' # Class: IN + + # Send the DNS query + sock.sendto(message, (dns_server, dns_port)) + + # Receive a response + response, addr = sock.recvfrom(4096) + print(f'Received response from {addr}: {response}') + + # Close the socket + sock.close() + + +def test_http(size=1 * 1024): + # SOCKS5 proxy configuration + proxies = { + 'http': 'socks5h://localhost:2080', + 'https': 'socks5h://localhost:2080' + } + + # Generate a 2MB JSON body + data = {'key': 'a' * size} # Adjusting for JSON formatting characters + + # Convert the data to JSON format + json_data = json.dumps(data) + + # URL to send the POST request to + url = 'http://httpbin.org/post' + + # Send the POST request + response = requests.post(url, data=json_data, headers={'Content-Type': 'application/json'}, proxies=proxies) + + # Print the response + print(f'Status Code: {response.status_code}') + print(f'Response Body: {response.text}') + + + +# test_via_http() + +test_http(5) +# test_dns_query() +# test_dns_query() +# test_http()