2024年6月7日11:21:00 socket5 代理

master
zm 2024-06-07 11:21:15 +08:00
commit f79557ae04
7 changed files with 401 additions and 0 deletions

6
config/config.go Normal file
View File

@ -0,0 +1,6 @@
package config
const (
LOCAL_SOCKET_ADDR = "127.0.0.1"
LOCAL_SOCKET_PORT = 1080
)

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module go-socket5-proxy
go 1.20

28
helper/helper.go Normal file
View File

@ -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
}

14
server/main.go Normal file
View File

@ -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()
}

68
socket5/auth.go Normal file
View File

@ -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
}

151
socket5/request.go Normal file
View File

@ -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
}

131
socket5/socket5.go Normal file
View File

@ -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)
}