socks5学习笔记

243 阅读2分钟

原理

通过下图可以看到,通过sock5建立代理服务器的步骤有三步:

  1. 协商阶段(auth阶段)
  2. 请求阶段
  3. relay阶段

实现

1 实现监听并收发数据

就是net包的listen函数和accept函数实现对连接的监听,然后用bufio中缓冲流读取连接中的数据,直接写回去

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("Acceptfailed %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
		}
	}
}

2 auth阶段

auth阶段需要从连接读取字节 需要读取VER NMETHODS METHOD 中的信息,前两个都是一个字节长度,最后一个我们直接读完 这个阶段需要ver为0x05,methods 可为0x00(不需要认证),0x02(用户名密码认证)

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
}

3 请求阶段和relay阶段

请求阶段就是在认证过后先建立连接的阶段 需要的请求参数有 VER CMD RSV ATYP DST.ADDR DST.PORT 同样需要 ver 为0x05,cmd表示连接方式,atyp:0x01表示ipv4,0x03表示域名,0x04表示ipv6,dst.addr就是目标地址,dst.port就是目标端口 我们建立连接后,还需要开启两个goroutine进行读写操作,使用context包控制协程的完成情况。

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", ver)
	}
	addr := ""
	switch atyp {
	case atypIPV4:
		_, 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
}

总结

之前学习golang也只是一个很浅的状态吧,尤其是没用过熟悉的语言实现这些底层的东西,通过这次学习,进一步得到了提升