1.实现tcp echo server
呈现效果为:给服务器发送什么,服务器就回复什么
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
}
}
}
2.SOCKS5代理-auth
认证阶段:浏览器会给代理服务器发送一个报文,共三个字段,第一个字段是协议版本号, 第二个字段鉴权方式的数目,第三个字段每个鉴权方式的编码
const socks5Ver=0x05
func auth(reader *bufio.Reader,conn net.Conn)(err,error){
//+----+--------+-------+
//| VER|NMETHODS|METHODS|
//+----+--------+-------+
//| 1 | 1 |1to255 |
//+----+--------+-------+
//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.Errof("read ver failed:%w",err)
}
if ver!=socks5Ver{
return fmt.Errof("not supported ver:%v",ver)
}
methodSize,err:=reader.ReadByte()
if err!=nil{
return fmt.Errof("read methodSize failed:%w",err)
}
method:=make([]byte,methodSize)
//创建一个method缓冲区
_,err:=io.ReadFull(reader,method)
//将缓冲区填充满
if err!=nil{
return fmt.Errof("read method failed:%w",err)
}
log.Println("ver",ver,"method",method)
//+------+--------+
//| VER | METHOD |
//+------+--------+
//按照协议返回一个包,告诉浏览器我们选择了哪种鉴权方式
_,err:=conn.Write([]byte{socks5Ver,0x00})
if err!=nil{
return fmt.Error("write failed:%w",err)
}
return nil
}
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 process")
}
3.SOCKS5代理-请求阶段
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
//这里我们只支持connect连接,即就是让代理服务器和下一个服务器创建连接
//CMD 0x01表示connect连接
//RSV 保留字段,值为0x00
//ATYP目标地址类型,DST.ADDR的数据对应这个字段的类型
// 0x01 表示IPV4地址,DST.ADDR为4个字节
//0x03表示域名,DST.ADDR是一个可变长度的域名
//DST.PORT目标端口,固定2个字节
buf:=make([]byte,4)
//创建一个长度为4的缓冲区
_,err:=io.ReadFull(reader,buf)
if err!=nil{
return fmt.Errof("read headr 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.Errof("not supported cmd:%v",ver)
}
addr:=""
switch atype{
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])
//从buf[:2]中读取端口号并赋值给port
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' succeed
//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 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
}
}
4.SOCKS5代理-realy阶段
dest,err:=net.Dial("tcp",fmt.Sprintf("%V:%v",addr,port))
//用tcp协议往对应域名或ip加端口去建立tcp连接
if err!=nil{
return fmt.Errorf("dial dst failed:%w",err)
}
defer dest.Close()
//在函数结束的时候去关闭连接
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)
}
//建立浏览器和下一个服务器的双向数据转发
ctx,cancel:=context.WithCancel(context.Background())
defer cancel()
//context.Background() 返回一个空的Context
//我们可以用这个 空的 Context 作为 goroutine 的root 节点(如果把整个 goroutine //的关系看作 树状)
//使用context.WithCancel(parent)函数,创建一个可取消的子Context
//函数返回值有两个:子Context Cancel 取消函数
go func(){
_,i=io.Copy(dest,reader)
cancel()
}()
//从用户浏览器到底层服务器
go func(){
_,_=io.Copy(conn,dest)
cancel()
}()
//从底层服务器到用户浏览器
<-ctx.Done()
//这个函数执行的时机是cancel被调用的时机
//在任何一个gorutine执行出错的时候调用cancel()函数
return nil
5.SOCKS5代理-完整代码
package main | |
| ------------ | ----------------------------------------------------------------------- |
| | |
| | import ( |
| | "bufio" |
| | "context" |
| | "encoding/binary" |
| | "errors" |
| | "fmt" |
| | "io" |
| | "log" |
| | "net" |
| | ) |
| | |
| | const socks5Ver = 0x05 |
| | const cmdBind = 0x01 |
| | const atypIPV4 = 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", 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]) |
| | |
| | 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 |
| | }