From f79557ae0452974ab7750388e6bb2524f2a8e192 Mon Sep 17 00:00:00 2001 From: zm <1334717033@qq.com> Date: Fri, 7 Jun 2024 11:21:15 +0800 Subject: [PATCH] =?UTF-8?q?2024=E5=B9=B46=E6=9C=887=E6=97=A511:21:00=20soc?= =?UTF-8?q?ket5=20=E4=BB=A3=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 6 ++ go.mod | 3 + helper/helper.go | 28 +++++++++ server/main.go | 14 +++++ socket5/auth.go | 68 ++++++++++++++++++++ socket5/request.go | 151 +++++++++++++++++++++++++++++++++++++++++++++ socket5/socket5.go | 131 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 401 insertions(+) create mode 100644 config/config.go create mode 100644 go.mod create mode 100644 helper/helper.go create mode 100644 server/main.go create mode 100644 socket5/auth.go create mode 100644 socket5/request.go create mode 100644 socket5/socket5.go diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..88ec20c --- /dev/null +++ b/config/config.go @@ -0,0 +1,6 @@ +package config + +const ( + LOCAL_SOCKET_ADDR = "127.0.0.1" + LOCAL_SOCKET_PORT = 1080 +) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9b4733b --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module go-socket5-proxy + +go 1.20 diff --git a/helper/helper.go b/helper/helper.go new file mode 100644 index 0000000..64052b6 --- /dev/null +++ b/helper/helper.go @@ -0,0 +1,28 @@ +package helper + +import ( + "log" + "net" +) + +// CreateListen 监听 +func CreateListen(serverAddr string) (*net.TCPListener, error) { + tcpAddr, err := net.ResolveTCPAddr("tcp", serverAddr) + if err != nil { + log.Println("创建监听出错:" + serverAddr) + return nil, err + } + tcpListen, err := net.ListenTCP("tcp", tcpAddr) + return tcpListen, err +} + +// CreateConn 创建连接 +func CreateConn(serverAddr string) (*net.TCPConn, error) { + tcpAddr, err := net.ResolveTCPAddr("tcp", serverAddr) + if err != nil { + log.Println("创建连接出错:" + serverAddr) + return nil, err + } + conn, err := net.DialTCP("tcp", nil, tcpAddr) + return conn, err +} diff --git a/server/main.go b/server/main.go new file mode 100644 index 0000000..91fd2bc --- /dev/null +++ b/server/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "go-socket5-proxy/config" + "go-socket5-proxy/socket5" +) + +func main() { + server := &socket5.Socket5Server{ + IP: config.LOCAL_SOCKET_ADDR, + Port: config.LOCAL_SOCKET_PORT, + } + server.Run() +} diff --git a/socket5/auth.go b/socket5/auth.go new file mode 100644 index 0000000..52047c7 --- /dev/null +++ b/socket5/auth.go @@ -0,0 +1,68 @@ +package socket5 + +import ( + "errors" + "io" + "net" +) + +var ( + ERROR_SOCKET_VERSION_SUPPORTED = errors.New("不支持该socket版本") + ERROR_COMMAND_NOT_SUPPORTED = errors.New("不支持该COMMAND命令") + ERRADDRESS_TYPE_NOT_SUPPORTED = errors.New("不支持该地址类型") +) + +const ( + SOCKET_5_VERSION = 0x05 + Method_NO_AUTH = 0x00 + Method_GSS_API = 0x01 + Method_PASS_WORD = 0x02 + Method_NO_ACCEPT_TABLE = 0xff + ReservedFild = 0x00 +) + +type Method = byte + +type ClientAuthMessage struct { + Version byte + + NMethods byte + + Methods []Method +} + +func NewServerAuthMessage(conn net.Conn, method Method) error { + buf := []byte{SOCKET_5_VERSION, method} + _, err := conn.Write(buf) + return err +} + +func NewClientAuthMessage(conn net.Conn) (*ClientAuthMessage, error) { + + //读取socket版本 + buf := make([]byte, 2) + _, err := io.ReadFull(conn, buf) + if err != nil { + return nil, err + } + //校验socket 版本 + if buf[0] != SOCKET_5_VERSION { + return nil, ERROR_SOCKET_VERSION_SUPPORTED + } + + //读取method + nmethos := buf[1] + + buf = make([]byte, nmethos) + _, err = io.ReadFull(conn, buf) + + if err != nil { + return nil, err + } + + return &ClientAuthMessage{ + Version: SOCKET_5_VERSION, + NMethods: nmethos, + Methods: buf, + }, nil +} diff --git a/socket5/request.go b/socket5/request.go new file mode 100644 index 0000000..3e469eb --- /dev/null +++ b/socket5/request.go @@ -0,0 +1,151 @@ +package socket5 + +import ( + "io" + "net" +) + +type ClientRequestMessage struct { + CMD Command + Address string + Port uint16 + AddressType Command +} + +type Command = byte + +const ( + IPV4_LENGHT = 4 + IPV6_LENGHT = 6 + PORT_LENGHT = 2 +) + +const ( + CmdConnect Command = 0x01 + CmdBind Command = 0x02 + CmdUDP Command = 0x03 +) + +type AddressType = byte + +const ( + TypeIPV4 AddressType = 0x01 + TypeDoMain AddressType = 0x03 + TypeIPV6 AddressType = 0x04 +) + +func NewClientRequestMessage(conn net.Conn) (*ClientRequestMessage, error) { + // 版本 命令 reseved 地址类型 + buf := make([]byte, 4) + if _, err := io.ReadFull(conn, buf); err != nil { + return nil, err + } + version, command, reserved, addType := buf[0], buf[1], buf[2], buf[3] + if version != SOCKET_5_VERSION { + return nil, ERROR_SOCKET_VERSION_SUPPORTED + } + + if command != CmdConnect && command != CmdBind && command != CmdUDP { + return nil, ERROR_COMMAND_NOT_SUPPORTED + } + + if reserved != ReservedFild { + return nil, ERROR_SOCKET_VERSION_SUPPORTED + } + + if addType == TypeIPV4 || addType == TypeDoMain { + var message ClientRequestMessage + message.CMD = command + message.AddressType = addType + + //fmt.Printf("请求地址类型为:%v\n", addType) + + switch addType { + + case TypeIPV6: + buf = make([]byte, IPV6_LENGHT) + if _, err := io.ReadFull(conn, buf); err != nil { + return nil, err + } + ip := net.ParseIP(string(buf)) + message.Address = ip.String() + //fmt.Println("TypeIPV6 地址为:" + ip.String()) + case TypeIPV4: + buf = make([]byte, IPV4_LENGHT) + if _, err := io.ReadFull(conn, buf); err != nil { + return nil, err + } + ip := net.IP(buf) + message.Address = ip.String() + //fmt.Println("TypeIPV4 地址为:" + ip.String()) + case TypeDoMain: + + if _, err := io.ReadFull(conn, buf[:1]); err != nil { + return nil, err + } + + domain_length := buf[0] + if domain_length > IPV4_LENGHT { + buf = make([]byte, domain_length) + } + if _, err := io.ReadFull(conn, buf[:domain_length]); err != nil { + message.Address = string(buf[:domain_length]) + } + + //fmt.Println("TypeDoMain 地址为:" + message.Address) + } + + //读取 端口 + if _, err := io.ReadFull(conn, buf[:PORT_LENGHT]); err != nil { + return nil, err + } + message.Port = (uint16(buf[0]) << 8) + uint16(buf[1]) + return &message, nil + } else { + return nil, ERRADDRESS_TYPE_NOT_SUPPORTED + } +} + +type ReplyType = byte + +const ( + ReplySuccess ReplyType = iota + ReplyServerFailure + ReplyConnectionNotAllowed + ReplyNetworkUnreachable + ReplyHostUnreachable + ReplyConnectionRefused + ReplyTTLExpired + ReplyCommandNotSupported + ReplyAddressTypeNotSupported +) + +func WriteRequestSuccessMessage(conn net.Conn, ip net.IP, port uint16) error { + + addressType := TypeIPV4 + if len(ip) == IPV6_LENGHT { + addressType = TypeIPV6 + } + + // Write version, reply success, reserved, address type + _, err := conn.Write([]byte{SOCKET_5_VERSION, ReplySuccess, ReservedFild, addressType}) + if err != nil { + return err + } + + // write bind IP(IPV4/IPV6) + if _, err := conn.Write(ip); err != nil { + return err + } + // write bind port + buf := make([]byte, 2) + buf[0] = byte(port >> 8) + buf[1] = byte(port - uint16(buf[0])) + _, err = conn.Write(buf) + return err +} + +func WriteRequestFailureMessage(conn net.Conn, replyType ReplyType) error { + _, err := conn.Write([]byte{SOCKET_5_VERSION, replyType, ReservedFild, TypeIPV4, 0, 0, 0, 0, 0, 0}) + return err +} diff --git a/socket5/socket5.go b/socket5/socket5.go new file mode 100644 index 0000000..a164649 --- /dev/null +++ b/socket5/socket5.go @@ -0,0 +1,131 @@ +package socket5 + +import ( + "errors" + "fmt" + "go-socket5-proxy/helper" + "io" + "log" + "net" +) + +type Server interface { + Run() error +} + +type Socket5Server struct { + Port int64 + IP string +} + +func (s *Socket5Server) Run() error { + address := fmt.Sprintf("%s:%d", s.IP, s.Port) + fmt.Printf("%s:%d \n", s.IP, s.Port) + listen, err := helper.CreateListen(address) + if err != nil { + log.Fatal(err) + return err + } + + for { + conn, err := listen.Accept() + if err != nil { + log.Printf("连接失败:%s: %s", conn.RemoteAddr(), err) + } + + go func() { + defer conn.Close() + err = handleConnection(conn) + if err != nil { + //log.Printf("处理连接 handleConnection %s: %s", conn.RemoteAddr(), err) + } + + }() + + } +} + +// 处理连接 handleConnection +func handleConnection(conn net.Conn) error { + + //协商过程 + if err := auth(conn); err != nil { + return err + } + + //请求过程 + targetConn, err := request(conn) + if err != nil { + return err + } + if targetConn == nil { + return nil + } + //转发过程 + return forward(conn, targetConn) +} + +func forward(conn net.Conn, targetConn net.Conn) error { + defer targetConn.Close() + go io.Copy(targetConn, conn) + _, err := io.Copy(conn, targetConn) + return err +} + +func request(conn net.Conn) (net.Conn, error) { + message, err := NewClientRequestMessage(conn) + if err != nil { + return nil, err + } + + //检查命令是否支持 + if message.CMD != CmdConnect { + return nil, WriteRequestFailureMessage(conn, ReplyCommandNotSupported) + } + + //ipv6 + if message.AddressType == TypeIPV6 { + return nil, WriteRequestFailureMessage(conn, ReplyAddressTypeNotSupported) + } + + //请求访问目标服务器 + addres := fmt.Sprintf("%s:%d", message.Address, message.Port) + //fmt.Printf("请求访问目标服务器:%v\n", message.AddressType) + targetConn, err := net.Dial("tcp", addres) + if err != nil { + return nil, WriteRequestFailureMessage(conn, ReplyConnectionRefused) + } + + //发送成功 + addrValue := targetConn.LocalAddr() + addr := addrValue.(*net.TCPAddr) + + if targetConn == nil { + fmt.Printf("targetConn is nil :" + addr.IP.String()) + } + + return targetConn, WriteRequestSuccessMessage(conn, addr.IP, uint16(addr.Port)) +} + +func auth(conn net.Conn) error { + client, err := NewClientAuthMessage(conn) + if err != nil { + return err + } + //log.Println(client.Version, client.NMethods, client.Methods) + + // 是否支持 no-auth + var accept_table bool + for _, method := range client.Methods { + if method == Method_NO_AUTH { + accept_table = true + } + } + + if !accept_table { + NewServerAuthMessage(conn, Method_NO_ACCEPT_TABLE) + return errors.New("方法不支持 not auth") + } + + return NewServerAuthMessage(conn, Method_NO_AUTH) +}