GO语言项目实战案例(2) | 青训营笔记

62 阅读22分钟
package main

import (
	"bufio"
	"log"
	"net"
)

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	if err != nil {
		panic(err)
	}
	for {
		client, err := server.Accept()
		if err != nil {
			log.Printf("Accept failed %v", err)
			continue
		}
		go process(client)
	}
}

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	for {
		b, err := reader.ReadByte()
		if err != nil {
			break
		}
		_, err = conn.Write([]byte{b})
		if err != nil {
			break
		}
	}
}
  • net.Listen() 是一个函数,用于创建网络监听器。

  • 第一个参数 "tcp" 指定了监听器的网络协议,这里使用 TCP 协议。

  • 第二个参数 "127.0.0.1:1080" 指定了服务器监听的地址和端口。127.0.0.1 是一个特殊的 IP 地址,表示本地回环地址(loopback address),用于在本机内部进行通信。:1080 表示监听端口号是 1080。

    • server 是一个 net.Listener 对象,表示创建的监听器。通过该对象,可以接受客户端的连接请求,并与客户端建立连接。
  • err 是一个错误对象,表示在创建监听器时是否发生了错误。如果监听器创建成功,则 err 的值为 nil;否则,它将包含一个非空的错误值,指示出现了什么问题。

    • for {} 是一个无限循环,它会一直执行下去,直到程序被显式地中断或退出。
  • 在每次循环中,server.Accept() 方法被调用,用于接受客户端的连接请求。这个方法会一直阻塞,直到有客户端连接到达。一旦有连接到达,该方法会返回一个 net.Conn 对象,表示与客户端建立的连接,以及可能的错误信息。

  • client 是一个 net.Conn 对象,表示与新客户端建立的连接。

  • err 是一个错误对象,表示在接受连接时是否发生了错误。如果接受连接成功,则 err 的值为 nil;否则,它将包含一个非空的错误值,指示出现了什么问题。

  • if err != nil 条件语句中,检查是否发生了错误。如果发生了错误,通过 log.Printf() 打印错误信息,并使用 continue 关键字跳过当前循环,继续下一次循环。

  • 如果没有发生错误,表示成功接受了一个客户端的连接。然后,通过 go process(client) 启动一个新的 Go 协程,调用 process() 函数来处理这个客户端连接。使用协程的方式可以并发地处理多个客户端连接,而不阻塞主循环。

package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
)

const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	if err != nil {
		panic(err)
	}
	for {
		client, err := server.Accept()
		if err != nil {
			log.Printf("Accept failed %v", err)
			continue
		}
		go process(client)
	}
}

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	err := auth(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
	log.Println("auth success")
}

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
	// +----+----------+----------+
	// |VER | NMETHODS | METHODS  |
	// +----+----------+----------+
	// | 1  |    1     | 1 to 255 |
	// +----+----------+----------+
	// VER: 协议版本,socks5为0x05
	// NMETHODS: 支持认证的方法数量
	// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
	// X’00’ NO AUTHENTICATION REQUIRED
	// X’02’ USERNAME/PASSWORD

	ver, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read ver failed:%w", err)
	}
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	methodSize, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read methodSize failed:%w", err)
	}
	method := make([]byte, methodSize)
	_, err = io.ReadFull(reader, method)
	if err != nil {
		return fmt.Errorf("read method failed:%w", err)
	}
	log.Println("ver", ver, "method", method)
	// +----+--------+
	// |VER | METHOD |
	// +----+--------+
	// | 1  |   1    |
	// +----+--------+
	_, err = conn.Write([]byte{socks5Ver, 0x00})
	if err != nil {
		return fmt.Errorf("write failed:%w", err)
	}
	return nil
}

这段代码是一个简单的 SOCKS5 代理服务器的实现。以下是代码的逐行解释:

package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
)

代码开始处导入了一些必要的包。

const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

这里定义了一些常量,用于表示 SOCKS5 协议的各种数值。

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	if err != nil {
		panic(err)
	}
	for {
		client, err := server.Accept()
		if err != nil {
			log.Printf("Accept failed %v", err)
			continue
		}
		go process(client)
	}
}

main() 函数中,代码首先创建一个 TCP 服务器监听器,并监听在地址 127.0.0.1:1080 上。如果监听器创建失败,则会触发一个 panic,导致程序崩溃。然后进入一个无限循环,通过 server.Accept() 方法接受客户端的连接请求。每当有新的客户端连接到达时,会创建一个新的 net.Conn 对象来表示连接,并在一个单独的 Go 协程中调用 process() 函数来处理连接。

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	err := auth(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
	log.Println("auth success")
}

process() 函数用于处理客户端连接。函数开始时通过 defer 关键字延迟关闭连接,以确保在函数结束时关闭连接。然后创建一个 bufio.Reader 对象,用于从连接中读取数据。接下来调用 auth() 函数进行认证,如果认证失败,则通过日志记录错误信息,并返回。如果认证成功,则打印认证成功的消息。

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
	ver, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read ver failed:%w", err)
	}
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	methodSize, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read methodSize failed:%w", err)
	}
	method := make([]byte, methodSize)
	_, err = io.ReadFull(reader, method)
	if err != nil {
		return fmt.Errorf("read method failed:%w", err)
	}
	log.Println("ver", ver, "method", method)
	_, err = conn.Write([]byte{socks5Ver, 0x00})
	if err != nil {
		return fmt.Errorf("write failed:%w", err)
	}
	return nil
}

auth() 函数用于进行 SOCKS5 认证过程。首先从 reader 中读取协议版本号 ver,如果读取协议版本号 ver 失败,会返回一个包含错误信息的 fmt.Errorf 对象。

接下来,代码检查协议版本号 ver 是否为预期的 SOCKS5 版本号 socks5Ver,如果不匹配,则返回一个错误对象。

然后,从 reader 中读取方法数量 methodSize,如果读取失败,则返回一个错误对象。

接着,创建一个长度为 methodSize 的字节数组 method,并从 reader 中读取方法数据到该数组中。

接下来,代码打印协议版本号和方法数据。

然后,通过 conn.Write() 方法向客户端发送认证响应,即回复协议版本号和方法选择结果。这里直接回复使用无需认证的方法。

最后,如果发送响应时发生错误,会返回一个错误对象;否则,返回 nil 表示认证成功。

整体上,这段代码实现了 SOCKS5 代理服务器中的认证阶段。它读取客户端发送的协议版本号和支持的认证方法列表,并根据规范进行处理和回复。

关于'process'函数的解释:这段代码定义了一个名为 process 的函数,用于处理与客户端的连接。

函数签名为 func process(conn net.Conn),表示该函数接受一个 net.Conn 类型的参数 conn,表示与客户端建立的连接。

以下是代码的执行流程和解释:

  1. 延迟关闭连接:

    • 使用 defer conn.Close() 语句,将连接的关闭操作延迟到函数执行结束时执行。这样可以确保在处理完客户端请求后正确地关闭连接,释放资源。
  2. 创建读取器:

    • 使用 bufio.NewReader(conn) 方法创建一个 bufio.Reader 对象 reader,用于从连接中读取数据。
  3. 进行认证:

    • 调用 auth(reader, conn) 函数来进行 SOCKS5 协议的认证阶段。
    • readerconn 作为参数传递给 auth 函数。
    • auth 函数返回的错误赋值给变量 err
  4. 处理认证结果:

    • 如果 err 不为 nil,表示认证失败。
      • 使用 log.Printf() 方法记录认证失败的信息,包括客户端地址和错误信息。
      • 函数提前返回,结束函数执行。
    • 如果 errnil,表示认证成功。
      • 使用 log.Println() 方法打印认证成功的信息。

总结来说,process() 函数用于处理与客户端的连接。它在函数开始时创建了一个读取器,并调用 auth() 函数进行认证。根据认证的结果,它会打印相应的日志信息,表示认证成功或失败。同时,通过使用 defer 关键字延迟关闭连接,确保连接在函数执行结束时被正确关闭。 关于auth函数的解释:这段代码定义了一个名为 process 的函数,用于处理与客户端的连接。

函数签名为 func process(conn net.Conn),表示该函数接受一个 net.Conn 类型的参数 conn,表示与客户端建立的连接。

以下是代码的执行流程和解释:

  1. 延迟关闭连接:

    • 使用 defer conn.Close() 语句,将连接的关闭操作延迟到函数执行结束时执行。这样可以确保在处理完客户端请求后正确地关闭连接,释放资源。
  2. 创建读取器:

    • 使用 bufio.NewReader(conn) 方法创建一个 bufio.Reader 对象 reader,用于从连接中读取数据。
  3. 进行认证:

    • 调用 auth(reader, conn) 函数来进行 SOCKS5 协议的认证阶段。
    • readerconn 作为参数传递给 auth 函数。
    • auth 函数返回的错误赋值给变量 err
  4. 处理认证结果:

    • 如果 err 不为 nil,表示认证失败。
      • 使用 log.Printf() 方法记录认证失败的信息,包括客户端地址和错误信息。
      • 函数提前返回,结束函数执行。
    • 如果 errnil,表示认证成功。
      • 使用 log.Println() 方法打印认证成功的信息。

总结来说,process() 函数用于处理与客户端的连接。它在函数开始时创建了一个读取器,并调用 auth() 函数进行认证。根据认证的结果,它会打印相应的日志信息,表示认证成功或失败。同时,通过使用 defer 关键字延迟关闭连接,确保连接在函数执行结束时被正确关闭。

关于auth函数的解释:这段代码是一个名为 auth 的函数,用于处理 SOCKS5 协议中的认证阶段。 函数签名为 func auth(reader *bufio.Reader, conn net.Conn) (err error),表示该函数接受一个 bufio.Reader 类型的参数 reader 和一个 net.Conn 类型的参数 conn,并返回一个 error 类型的结果。

下面是代码的执行流程和解释:

  1. 读取协议版本号 ver

    • 使用 reader.ReadByte() 方法从 reader 中读取一个字节作为协议版本号 ver
    • 如果读取过程中发生错误 err,函数会返回一个带有错误信息的 fmt.Errorf 对象,使用 %w 占位符将原始错误信息包装在其中。
  2. 检查协议版本号 ver

    • 如果读取的协议版本号 ver 不等于预定义的 SOCKS5 版本号 socks5Ver,则表示不支持该协议版本。
    • 函数会返回一个带有错误信息的 fmt.Errorf 对象,指示不支持的协议版本。
  3. 读取方法数量 methodSize

    • 使用 reader.ReadByte() 方法从 reader 中读取一个字节作为方法数量 methodSize
    • 如果读取过程中发生错误 err,函数会返回一个带有错误信息的 fmt.Errorf 对象,使用 %w 占位符将原始错误信息包装在其中。
  4. 读取方法数据 method

    • 创建一个长度为 methodSize 的字节数组 method,用于存储方法数据。
    • 使用 io.ReadFull(reader, method) 方法从 reader 中读取完整的方法数据,填充到 method 数组中。
    • 如果读取过程中发生错误 err,函数会返回一个带有错误信息的 fmt.Errorf 对象,使用 %w 占位符将原始错误信息包装在其中。
  5. 打印协议版本号和方法数据:

    • 使用 log.Println() 方法打印协议版本号 ver 和方法数据 method
  6. 发送认证响应:

    • 使用 conn.Write([]byte{socks5Ver, 0x00}) 方法向客户端发送认证响应。
    • 认证响应包括回复的协议版本号和选择的认证方法,这里回复的是 SOCKS5 版本号和无需认证的方法。
    • 如果发送过程中发生错误 err,函数会返回一个带有错误信息的 fmt.Errorf 对象,使用 %w 占位符将原始错误信息包装在其中。
  7. 返回结果:

    • 如果函数的执行过程中没有发生错误,函数会返回 nil,表示认证成功。
    • 如果发生了错误,函数会返回一个带有错误信息的 fmt.Errorf 对象。
package main

import (
	"bufio"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
)

const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	if err != nil {
		panic(err)
	}
	for {
		client, err := server.Accept()
		if err != nil {
			log.Printf("Accept failed %v", err)
			continue
		}
		go process(client)
	}
}

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	err := auth(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
	err = connect(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
}

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
	// +----+----------+----------+
	// |VER | NMETHODS | METHODS  |
	// +----+----------+----------+
	// | 1  |    1     | 1 to 255 |
	// +----+----------+----------+
	// VER: 协议版本,socks5为0x05
	// NMETHODS: 支持认证的方法数量
	// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
	// X’00’ NO AUTHENTICATION REQUIRED
	// X’02’ USERNAME/PASSWORD

	ver, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read ver failed:%w", err)
	}
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	methodSize, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read methodSize failed:%w", err)
	}
	method := make([]byte, methodSize)
	_, err = io.ReadFull(reader, method)
	if err != nil {
		return fmt.Errorf("read method failed:%w", err)
	}

	// +----+--------+
	// |VER | METHOD |
	// +----+--------+
	// | 1  |   1    |
	// +----+--------+
	_, err = conn.Write([]byte{socks5Ver, 0x00})
	if err != nil {
		return fmt.Errorf("write failed:%w", err)
	}
	return nil
}

func connect(reader *bufio.Reader, conn net.Conn) (err error) {
	// +----+-----+-------+------+----------+----------+
	// |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
	// +----+-----+-------+------+----------+----------+
	// | 1  |  1  | X'00' |  1   | Variable |    2     |
	// +----+-----+-------+------+----------+----------+
	// VER 版本号,socks5的值为0x05
	// CMD 0x01表示CONNECT请求
	// RSV 保留字段,值为0x00
	// ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
	//   0x01表示IPv4地址,DST.ADDR为4个字节
	//   0x03表示域名,DST.ADDR是一个可变长度的域名
	// DST.ADDR 一个可变长度的值
	// DST.PORT 目标端口,固定2个字节

	buf := make([]byte, 4)
	_, err = io.ReadFull(reader, buf)
	if err != nil {
		return fmt.Errorf("read header failed:%w", err)
	}
	ver, cmd, atyp := buf[0], buf[1], buf[3]
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	if cmd != cmdBind {
		return fmt.Errorf("not supported cmd:%v", cmd)
	}
	addr := ""
	switch atyp {
	case atypeIPV4:
		_, err = io.ReadFull(reader, buf)
		if err != nil {
			return fmt.Errorf("read atyp failed:%w", err)
		}
		addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
	case atypeHOST:
		hostSize, err := reader.ReadByte()
		if err != nil {
			return fmt.Errorf("read hostSize failed:%w", err)
		}
		host := make([]byte, hostSize)
		_, err = io.ReadFull(reader, host)
		if err != nil {
			return fmt.Errorf("read host failed:%w", err)
		}
		addr = string(host)
	case atypeIPV6:
		return errors.New("IPv6: no supported yet")
	default:
		return errors.New("invalid atyp")
	}
	_, err = io.ReadFull(reader, buf[:2])
	if err != nil {
		return fmt.Errorf("read port failed:%w", err)
	}
	port := binary.BigEndian.Uint16(buf[:2])

	log.Println("dial", addr, port)

	// +----+-----+-------+------+----------+----------+
	// |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
	// +----+-----+-------+------+----------+----------+
	// | 1  |  1  | X'00' |  1   | Variable |    2     |
	// +----+-----+-------+------+----------+----------+
	// VER socks版本,这里为0x05
	// REP Relay field,内容取值如下 X’00’ succeeded
	// RSV 保留字段
	// ATYPE 地址类型
	// BND.ADDR 服务绑定的地址
	// BND.PORT 服务绑定的端口DST.PORT
	_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
	if err != nil {
		return fmt.Errorf("write failed: %w", err)
	}
	return nil
}

以下是您提供的代码逐行的解释:

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
	ver, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read ver failed:%w", err)
	}
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	methodSize, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read methodSize failed:%w", err)
	}
	method := make([]byte, methodSize)
	_, err = io.ReadFull(reader, method)
	if err != nil {
		return fmt.Errorf("read method failed:%w", err)
	}

	_, err = conn.Write([]byte{socks5Ver, 0x00})
	if err != nil {
		return fmt.Errorf("write failed:%w", err)
	}
	return nil
}
  • ver, err := reader.ReadByte(): 从输入流 reader 中读取一个字节,赋值给变量 ver,同时检查是否有错误发生。
  • if ver != socks5Ver: 检查读取的版本号 ver 是否与 socks5Ver 相等,如果不相等,则返回一个错误,指示不支持的版本号。
  • methodSize, err := reader.ReadByte(): 从输入流 reader 中读取一个字节,赋值给变量 methodSize,同时检查是否有错误发生。
  • method := make([]byte, methodSize): 创建一个字节切片 method,其长度为 methodSize
  • _, err = io.ReadFull(reader, method): 从输入流 reader 中读取 methodSize 个字节,并将其存储到 method 中,同时检查是否有错误发生。
  • _, err = conn.Write([]byte{socks5Ver, 0x00}): 向连接 conn 写入一个字节切片,该切片包含版本号 socks5Ver 和方法字节 0x00,同时检查是否有错误发生。
func connect(reader *bufio.Reader, conn net.Conn) (err error) {
	buf := make([]byte, 4)
	_, err = io.ReadFull(reader, buf)
	if err != nil {
		return fmt.Errorf("read header failed:%w", err)
	}
	ver, cmd, atyp := buf[0], buf[1], buf[3]
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	if cmd != cmdBind {
		return fmt.Errorf("not supported cmd:%v", cmd)
	}
	addr := ""
	switch atyp {
	case atypeIPV4:
		_, err = io.ReadFull(reader, buf)
		if err != nil {
			return fmt.Errorf("read atyp failed:%w", err)
		}
		addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
	case atypeHOST:
		hostSize, err := reader.ReadByte()
		if err != nil {
			return fmt.Errorf("read hostSize failed:%w", err)
		}
		host := make([]byte, hostSize)
		_, err = io.ReadFull(reader, host)
		if err != nil {
			return fmt.Errorf("read host failed:%w", err)
	}
		addr = string(host)
	case atypeIPV6:
		return errors.New("IPv6: no supported yet")
	default:
		return errors.New("invalid atyp")
	}
	_, err = io.ReadFull(reader, buf[:2])
	if err != nil {
		return fmt.Errorf("read port failed:%w", err)
	}
	port := binary.BigEndian.Uint16(buf[:2])

	log.Println("dial", addr, port)

	_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
	if err != nil {
		return fmt.Errorf("write failed: %w", err)
	}
	return nil
}
  • buf := make([]byte, 4): 创建一个长度为4的字节切片 buf
  • _, err = io.ReadFull(reader, buf): 从输入流 reader 中读取4个字节,并将其存储到 buf 中,同时检查是否有错误发生。
  • ver, cmd, atyp := buf[0], buf[1], buf[3]: 将 buf 中的第一个字节赋值给 ver,第二个字节赋值给 cmd,第四个字节赋值给 atyp
  • if ver != socks5Ver: 检查读取的版本号 ver 是否与 socks5Ver 相等,如果不相等,则返回一个错误,指示不支持的版本号。
  • if cmd != cmdBind: 检查读取的命令字节 cmd 是否与 cmdBind 相等,如果不相等,则返回一个错误,指示不支持的命令。
  • switch atyp: 根据目标地址类型 atyp 的值进行分支判断。
  • case atypeIPV4: 如果 atyp 的值为 atypeIPV4,则执行以下操作:
    • _, err = io.ReadFull(reader, buf): 从输入流 reader 中读取4个字节,并将其存储到 buf 中,同时检查是否有错误发生。
    • addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3]): 将读取的4个字节解析为 IPv4 地址格式,并使用 fmt.Sprintf 将其转换为字符串形式,存储到 addr 中。
  • case atypeHOST: 如果 atyp 的值为 atypeHOST,则执行以下操作:
    • hostSize, err := reader.ReadByte(): 从输入流 reader 中读取一个字节,赋值给 hostSize,同时检查是否有错误发生。
    • host := make([]byte, hostSize): 创建一个长度为 hostSize 的字节切片 host
    • _, err = io.ReadFull(reader, host): 从输入流 reader 中读取 hostSize 个字节,并将其存储到 host 中,同时检查是否有错误发生。
    • addr = string(host): 将 host 转换为字符串形式
    • case atypeIPV6:
      	return errors.New("IPv6: no supported yet")
      default:
      	return errors.New("invalid atyp")
      }
      _, err = io.ReadFull(reader, buf[:2])
      if err != nil {
      	return fmt.Errorf("read port failed:%w", err)
      }
      port := binary.BigEndian.Uint16(buf[:2])
      
      log.Println("dial", addr, port)
      
      _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
      if err != nil {
      	return fmt.Errorf("write failed: %w", err)
      }
      return nil
      }
      


- case atypeIPV6`: 如果 `atyp` 的值为 `atypeIPV6`,则返回一个错误,指示不支持 IPv6 地址类型。
- `default`: 如果 `atyp` 的值不匹配任何已知类型,则返回一个错误,指示无效的地址类型。
- `_, err = io.ReadFull(reader, buf[:2])`: 从输入流 `reader` 中读取2个字节,并将其存储到 `buf` 的前两个位置上,同时检查是否有错误发生。
- `port := binary.BigEndian.Uint16(buf[:2])`: 将 `buf` 的前两个字节解析为大端字节序的 16 位无符号整数,并将其存储到变量 `port` 中。
- `log.Println("dial", addr, port)`: 打印日志信息,表示正在连接的目标地址和端口。
- `_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})`: 向连接 `conn` 写入一个字节切片,该切片包含 SOCKS 版本号、回复字段、保留字段、地址类型和绑定的地址信息。
- `return nil`: 返回 `nil`,表示连接处理成功。

```go
func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	err := auth(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
	err = connect(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
	go relay(reader, conn)
}

func relay(reader io.Reader, writer io.Writer) {
	_, err := io.Copy(writer, reader)
	if err != nil {
		log.Printf("relay failed:%v", err)
	}
}
  • func process(conn net.Conn): 定义了处理连接的函数,接受一个 net.Conn 参数,表示与客户端建立的连接。
  • defer conn.Close(): 使用 defer 关键字在函数结束时关闭连接,确保连接资源得到正确释放。
  • reader := bufio.NewReader(conn): 创建一个 bufio.Reader 对象,用于从连接中读取数据。
  • err := auth(reader, conn): 调用 auth 函数进行身份验证,将 reader 和连接 conn 作为参数传递,并将返回的错误存储在 err 变量中。
  • if err != nil: 检查身份验证过程中是否发生错误。
    • 如果发生错误,打印错误日志并返回,表示身份验证失败。
  • err = connect(reader, conn): 调用 connect 函数进行连接处理,将 reader 和连接 conn 作为参数传递,并将返回的错误存储在 err 变量中。
  • if err != nil: 检查连接处理过程中是否发生错误。
    • 如果发生错误,打印错误日志并返回,表示连接处理失败。
  • go relay(reader, conn): 启动一个新的 goroutine,调用 relay 函数处理数据中继。将 reader 和连接 conn 作为参数传递。
  • func relay(reader io.Reader, writer io.Writer): 定义了数据中继的函数,接受一个实现了 io.Reader 接口和一个实现了 io.Writer 接口的参数。
  • _, err := io.Copy(writer, reader): 使用 io.Copy 函数将从 reader 中读取的数据复制到 writer 中,直到读取到 EOF(文件末尾)或发生错误。
  • if err != nil: 检查数据中继过程中是否发生错误。
    • 如果发生错误,打印错误日志。
  • 函数执行完毕,连接处理结束。数据中继会在单独的 goroutine 中持续进行,直到发生错误或连接关闭。
func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	if err != nil {
		panic(err)
	}
	defer server.Close()
	log.Println("SOCKS5 server started")

	for {
		client, err := server.Accept()
		if err != nil {
			log.Printf("Accept failed: %v", err)
			continue
		}
		go process(client)
	}
}
  • func main(): 主函数,程序入口。
  • server, err := net.Listen("tcp", "127.0.0.1:1080"): 创建一个 TCP 服务器,监听本地的 1080 端口。
  • if err != nil: 检查是否有错误发生。
    • 如果有错误,使用 panic 函数抛出错误并终止程序执行。
  • defer server.Close(): 使用 defer 关键字在函数结束时关闭服务器,确保服务器资源得到正确释放。
  • log.Println("SOCKS5 server started"): 打印日志,表示 SOCKS5 服务器已启动。
  • for { ... }: 进入无限循环,接受客户端连接并处理。
  • client, err := server.Accept(): 等待客户端连接并返回与客户端建立的连接。
  • if err != nil: 检查是否有错误发生。
    • 如果有错误,打印错误日志并继续等待下一个连接。
  • go process(client): 在新的 goroutine 中调用 process 函数处理客户端连接。
  • 循环会继续执行,等待下一个客户端连接。
package main

import (
	"bufio"
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
)

const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	if err != nil {
		panic(err)
	}
	for {
		client, err := server.Accept()
		if err != nil {
			log.Printf("Accept failed %v", err)
			continue
		}
		go process(client)
	}
}

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	err := auth(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
	err = connect(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
}

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
	// +----+----------+----------+
	// |VER | NMETHODS | METHODS  |
	// +----+----------+----------+
	// | 1  |    1     | 1 to 255 |
	// +----+----------+----------+
	// VER: 协议版本,socks5为0x05
	// NMETHODS: 支持认证的方法数量
	// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
	// X’00’ NO AUTHENTICATION REQUIRED
	// X’02’ USERNAME/PASSWORD

	ver, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read ver failed:%w", err)
	}
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	methodSize, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read methodSize failed:%w", err)
	}
	method := make([]byte, methodSize)
	_, err = io.ReadFull(reader, method)
	if err != nil {
		return fmt.Errorf("read method failed:%w", err)
	}

	// +----+--------+
	// |VER | METHOD |
	// +----+--------+
	// | 1  |   1    |
	// +----+--------+
	_, err = conn.Write([]byte{socks5Ver, 0x00})
	if err != nil {
		return fmt.Errorf("write failed:%w", err)
	}
	return nil
}

func connect(reader *bufio.Reader, conn net.Conn) (err error) {
	// +----+-----+-------+------+----------+----------+
	// |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
	// +----+-----+-------+------+----------+----------+
	// | 1  |  1  | X'00' |  1   | Variable |    2     |
	// +----+-----+-------+------+----------+----------+
	// VER 版本号,socks5的值为0x05
	// CMD 0x01表示CONNECT请求
	// RSV 保留字段,值为0x00
	// ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
	//   0x01表示IPv4地址,DST.ADDR为4个字节
	//   0x03表示域名,DST.ADDR是一个可变长度的域名
	// DST.ADDR 一个可变长度的值
	// DST.PORT 目标端口,固定2个字节

	buf := make([]byte, 4)
	_, err = io.ReadFull(reader, buf)
	if err != nil {
		return fmt.Errorf("read header failed:%w", err)
	}
	ver, cmd, atyp := buf[0], buf[1], buf[3]
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	if cmd != cmdBind {
		return fmt.Errorf("not supported cmd:%v", cmd)
	}
	addr := ""
	switch atyp {
	case atypeIPV4:
		_, err = io.ReadFull(reader, buf)
		if err != nil {
			return fmt.Errorf("read atyp failed:%w", err)
		}
		addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
	case atypeHOST:
		hostSize, err := reader.ReadByte()
		if err != nil {
			return fmt.Errorf("read hostSize failed:%w", err)
		}
		host := make([]byte, hostSize)
		_, err = io.ReadFull(reader, host)
		if err != nil {
			return fmt.Errorf("read host failed:%w", err)
		}
		addr = string(host)
	case atypeIPV6:
		return errors.New("IPv6: no supported yet")
	default:
		return errors.New("invalid atyp")
	}
	_, err = io.ReadFull(reader, buf[:2])
	if err != nil {
		return fmt.Errorf("read port failed:%w", err)
	}
	port := binary.BigEndian.Uint16(buf[:2])

	dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
	if err != nil {
		return fmt.Errorf("dial dst failed:%w", err)
	}
	defer dest.Close()
	log.Println("dial", addr, port)

	// +----+-----+-------+------+----------+----------+
	// |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
	// +----+-----+-------+------+----------+----------+
	// | 1  |  1  | X'00' |  1   | Variable |    2     |
	// +----+-----+-------+------+----------+----------+
	// VER socks版本,这里为0x05
	// REP Relay field,内容取值如下 X’00’ succeeded
	// RSV 保留字段
	// ATYPE 地址类型
	// BND.ADDR 服务绑定的地址
	// BND.PORT 服务绑定的端口DST.PORT
	_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
	if err != nil {
		return fmt.Errorf("write failed: %w", err)
	}
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		_, _ = io.Copy(dest, reader)
		cancel()
	}()
	go func() {
		_, _ = io.Copy(conn, dest)
		cancel()
	}()

	<-ctx.Done()
	return nil
}

这段代码是一个基于 SOCKS5 协议的代理服务器实现。它接受来自客户端的连接,并处理客户端的请求。

首先,代码定义了一些常量,包括协议版本号、命令类型、地址类型等。

main 函数是程序的入口函数,它通过 net.Listen 监听在本地的 1080 端口上。然后使用一个无限循环来接受客户端的连接,对每个连接创建一个 goroutine 来处理。

process 函数用于处理客户端连接。它首先调用 auth 函数进行客户端认证,然后调用 connect 函数建立与目标服务器的连接。

auth 函数用于处理客户端认证过程。根据 SOCKS5 协议规定,客户端发送一个包含认证方法的请求,服务器需要回复确认的认证方法。函数首先从客户端读取协议版本号,并检查其是否为 SOCKS5 版本。然后读取客户端支持的认证方法数量和方法列表。根据协议规定,服务器回复一个字节的数据,其中包含协议版本号和选择的认证方法。

connect 函数用于处理客户端连接请求。根据 SOCKS5 协议规定,客户端发送一个包含连接信息的请求,服务器需要解析请求中的目标地址和端口,并与目标服务器建立连接。函数首先从客户端读取请求的头部数据,包括协议版本号、命令类型和地址类型等信息。然后根据地址类型的不同,解析目标地址和端口。根据协议规定,服务器回复一个字节的数据,其中包含协议版本号、响应码等信息。最后,函数通过 net.Dial 建立与目标服务器的连接,并将客户端与目标服务器之间的数据进行转发。

connect 函数中,还使用了两个 goroutine 来实现数据的转发。一个 goroutine 将客户端的数据拷贝到目标服务器,另一个 goroutine 将目标服务器的数据拷贝到客户端。同时,使用了 contextcancel 函数来控制数据转发的结束。

总体来说,这段代码实现了一个简单的 SOCKS5 代理服务器,可以转发客户端和目标服务器之间的数据流量。