小沃最近开发了一个同时支持http与socks5的代理服务器,仅仅只需要监听一个端口。大家都知道,go语言每监听一个端口就需要启动一个协程,虽然一个协程所需要的资源很少,但是依旧还是存在的,因此两种协议只需要监听一个端口,其实就是可以少一个协程。
好了,话不多说,直接上代码:
package main import ( "log" "net" "io" "runtime" "strconv" "bytes" "strings" "net/url" ) func main() { runtime.GOMAXPROCS(1) log.SetFlags(log.LstdFlags | log.Lshortfile) log.Println("version: " + runtime.Version()) tcpaddr, err := net.ResolveTCPAddr("tcp", ":1080") l, err := net.ListenTCP("tcp", tcpaddr) if err != nil { log.Println(err) return } for { client, err := l.AcceptTCP() if err != nil { log.Println(err) l.Close() return } go ProxyRequest(client) } } func ProxyRequest (client *net.TCPConn) { client.SetReadBuffer(32*1024) client.SetWriteBuffer(32*1024) b := make([]byte, 32*1024) n, err := client.Read(b) if err != nil { log.Println(err) client.Close() return } if b[0] == 0x05 { //Socks5代理 // 客户端回应:Socks服务端不需要验证方式 _, err = client.Write([]byte{0x05, 0x00}) if err != nil { log.Println(err) client.Close() return } n, err := client.Read(b) if err != nil { log.Println(err) client.Close() return } var host string switch b[3] { case 0x01: //IP V4 if n <= 9 { client.Close() return } host = net.IPv4(b[4], b[5], b[6], b[7]).String() case 0x03: //域名 if n <= 7 { client.Close() return } host = string(b[5 : n-2]) //b[4]表示域名的长度 case 0x04: //IP V6 if n <= 21 { client.Close() return } host = net.IP{b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]}.String() default: client.Close() return } address := net.JoinHostPort(host, strconv.Itoa(int(b[n-2])<<8 | int(b[n-1]))) log.Println(address); tcpaddr, err := net.ResolveTCPAddr("tcp", address) if err != nil { log.Println(err) client.Close() return } server, err := net.DialTCP("tcp", nil, tcpaddr) if err != nil { log.Println(err) client.Close() return } _, err = client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) //响应客户端连接成功 if err != nil { log.Println(err) client.Close() server.Close() return } go IoCopy(client, server) go IoCopy(server, client) } else { // http代理 maddr := bytes.IndexByte(b, ' ') method := string(b[:maddr]) host := string(b[maddr+1:maddr+1+bytes.IndexByte(b[maddr+1:], ' ')]) if method == "CONNECT" { log.Println(host) tcpaddr, err := net.ResolveTCPAddr("tcp", host) if err != nil { log.Println(err) client.Close() return } server, err := net.DialTCP("tcp", nil, tcpaddr) if err != nil { log.Println(err) client.Close() return } server.SetReadBuffer(32*1024) server.SetWriteBuffer(32*1024) _, err = client.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) if err != nil { log.Println(err) client.Close() server.Close() return } go IoCopy(client, server) go IoCopy(server, client) } else { hostPortURL, err := url.Parse(host) if err != nil { log.Println(err) client.Close() return } host := hostPortURL.Host if strings.Index(host, ":") == -1 { host = host + ":80" } log.Println(host) tcpaddr, err := net.ResolveTCPAddr("tcp", host) if err != nil { log.Println(err) client.Close() return } server, err := net.DialTCP("tcp", nil, tcpaddr) if err != nil { log.Println(err) client.Close() return } server.SetReadBuffer(32*1024) server.SetWriteBuffer(32*1024) httpheadlen := bytes.Index(b, []byte("\r\n\r\n"))+4 httphead := b[:httpheadlen] start := bytes.Index(httphead, []byte(hostPortURL.Host)) + len(hostPortURL.Host) if start != -1 { httphead = append(httphead[:maddr+1], httphead[start:]...) } start = bytes.Index(httphead, []byte("Proxy-Connection:")) if start != -1 { httphead = append(httphead[:start], httphead[start+6:]...) } if httpheadlen < n { b = append(httphead, b[httpheadlen:n]...) } else { b = httphead } _, err = server.Write(b) if err != nil { log.Println(err) client.Close() server.Close() return } go IoCopy(client, server) go IoCopy(server, client) } } } func IoCopy(dst *net.TCPConn, src *net.TCPConn) { io.Copy(dst, src) dst.Close() }
代码一共194行,监听的是1080端口,大家可以根据自己的需求改为自己所需要的端口。
文章作者:沃航科技