您好,欢迎访问沃航(武汉)科技有限公司官方网站
一个端口同时支持http代理与socks5代理---史上最强大代理服务器诞生,完全开源
2020-11-27 00:14:19

小沃最近开发了一个同时支持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端口,大家可以根据自己的需求改为自己所需要的端口。


文章作者:沃航科技

联系我们
地址:
武汉市洪山区蓝晶国际7栋903
QQ:
932773931
电话:
027-59761089-806
手机:
13397158231
邮箱:
jevian_ma@worldflying.cn
×
物联网组态平台
试用账号:123456
试用密码:123456
如需测试更多功能或者有疑问可发送邮件至:jevian_ma@worldflying.cn
×
积木编程平台
试用方式:试用手机号码注册即可使用
如需测试更多功能或者有疑问可发送邮件至:jevian_ma@worldflying.cn