【问题标题】:Connect via Proxy while using UTLS and HTTP 1.1 Request使用 UTLS 和 HTTP 1.1 请求时通过代理连接
【发布时间】:2020-03-10 19:50:01
【问题描述】:

我正在尝试使用随机 TLS 指纹识别连接到主机。我正在使用https://github.com/refraction-networking/utls(请参阅我在https://github.com/refraction-networking/utls/issues/42 上创建的问题)

我现在的问题是,我如何在打开该连接时使用 HTTP 或 SOCKS5 代理?

我现在使用的代码是:

package main

import (
    "bufio"
    "fmt"
    "log"
    "net"
    "net/http"
    "net/http/httputil"
    "net/url"
    "time"

    "github.com/refraction-networking/utls"
)


var (
    dialTimeout   = time.Duration(15) * time.Second
)

var requestHostname = "google.com"
var requestAddr = "172.217.22.110:443"


// this example generates a randomized fingeprint, then re-uses it in a follow-up connection
func HttpGetConsistentRandomized(hostname string, addr , uri string) (*http.Response, error) {
    config := tls.Config{ServerName: hostname}
    tcpConn, err := net.DialTimeout("tcp", addr, dialTimeout)
    if err != nil {
        return nil, fmt.Errorf("net.DialTimeout error: %+v", err)
    }
    uTlsConn := tls.UClient(tcpConn, &config, tls.HelloRandomized)
    defer uTlsConn.Close()
    err = uTlsConn.Handshake()
    if err != nil {
        return nil, fmt.Errorf("uTlsConn.Handshake() error: %+v", err)
    }
    uTlsConn.Close()

    // At this point uTlsConn.ClientHelloID holds a seed that was used to generate
    // randomized fingerprint. Now we can establish second connection with same fp
    tcpConn2, err := net.DialTimeout("tcp", addr, dialTimeout)
    if err != nil {
        return nil, fmt.Errorf("net.DialTimeout error: %+v", err)
    }
    uTlsConn2 := tls.UClient(tcpConn2, &config, uTlsConn.ClientHelloID)
    defer uTlsConn2.Close()
    err = uTlsConn2.Handshake()
    if err != nil {
        return nil, fmt.Errorf("uTlsConn.Handshake() error: %+v", err)
    }

    return httpGetOverConn(uTlsConn2, uTlsConn2.HandshakeState.ServerHello.AlpnProtocol, uri)
}

func main() {
    var response *http.Response
    var err error

    response, err = HttpGetConsistentRandomized(requestHostname, requestAddr, "/2.0/ssocookie")
    if err != nil {
        fmt.Printf("#> HttpGetConsistentRandomized() failed: %+v\n", err)
    } else {
        //fmt.Printf("#> HttpGetConsistentRandomized() response: %+s\n", httputil.DumpResponse(response,true))
        dump, err := httputil.DumpResponse(response, true)
        if err != nil {
            log.Fatal(err)
        }

        fmt.Printf("%+s\n", dump)
    }
    return
}

func httpGetOverConn(conn net.Conn, alpn string, uri string) (*http.Response, error) {

    req := &http.Request{
        Method: "GET",
        URL:    &url.URL{Host: "www." + requestHostname  + uri},
        Header: make(http.Header),
        Host:   "www." + requestHostname,
    }

        req.Proto = "HTTP/1.1"
        req.ProtoMajor = 1
        req.ProtoMinor = 1

        err := req.Write(conn)
        if err != nil {
            return nil, err
        }
        return http.ReadResponse(bufio.NewReader(conn), req)
}

【问题讨论】:

    标签: http go


    【解决方案1】:

    正如 Steffen 所说,您必须先创建一个代理拨号器,拨号代理以创建一个 net.Conn,然后在创建 uTLS 客户端时使用该 net.Conn,然后再进行握手。为简洁起见,您的自定义 dialTLS 函数如下所示:

    import (
        "crypto/tls"
        "net"
        "net/url"    
    
        "github.com/magisterquis/connectproxy"
        "golang.org/x/net/proxy"
        utls "github.com/refraction-networking/utls"
    )
    
    var proxyString = "http://127.0.0.1:8080"
                                                                              
    dialTLS := func(network, addr string, _ *tls.Config) (net.Conn, error) {
        proxyURI, _ := url.Parse(proxyString)
    
        switch proxyURI.Scheme {                                                       
        case "socks5":                                                                 
                proxyDialer, err = proxy.SOCKS5("tcp", proxyString, nil, proxy.Direct)
        case "http":                                                          
                proxyDialer, err = connectproxy.New(proxyURI, proxy.Direct)            
        }   
    
        conn, err := proxyDialer.Dial("tcp", addr)
        uconn := utls.UClient(conn, cfg, &utls.HelloRandomizedALPN) 
    
        ...
    } 
    

    两个建议:

    1. 如果您打算通过 HTTP CONNECT 代理进行隧道传输,请使用上面提到的“connectproxy”模块。
    2. 让您的生活更轻松,并查看用于 Tor 的 Meek pluggable transport source。有一个“utls.go”模块可以为您处理所有事情,包括根据协商的 ALPN 协议设置 http 或 http2 传输。它仅支持 SOCKS,但您可以轻松调整它以处理 HTTP 代理。

    【讨论】:

      【解决方案2】:

      HTTP 代理和 SOCKS 代理在 TCP 连接后进行一些初始代理特定的握手。握手完成后,它们会提供一个普通的 TCP 套接字,然后可用于进行 TLS 握手等。因此,您只需更换您的

      tcpConn, err := net.DialTimeout("tcp", addr, dialTimeout)
      

      使用特定于代理的方法来设置 TCP 连接。这可以通过在x/net/proxy 中使用SOCKS5 来创建适当的拨号器或类似的方法来完成,使用HTTP CONNECT 方法在connectproxy 中完成。

      【讨论】:

      • 谢谢。所以这就是我的代码现在的样子,尽管什么都没有发生。 //tcpConn, err := net.DialTimeout("tcp", addr, dialTimeout) urll, err := url.Parse("https://127.0.0.1:8080") prox, err := connectproxy.NewWithConfig( urll, proxy.Direct, &connectproxy.Config{ ServerName: requestHostname, }, ) if nil != err{ panic(err) } tcpConn, err := prox.Dial("tcp", addr) if err != nil { return nil, fmt.Errorf("net.DialTimeout error: %+v", err) }
      • @NecronomiconCoding:我不知道你说的 是什么意思......虽然什么都没有发生了。而且我不知道您的特定代理的实际要求是什么。根据您的代码,您似乎假设一个 HTTP 代理本身可以通过 HTTPS 访问。但是大多数 HTTP 代理实际上是通过纯 HTTP 访问的(并且仍然可以用于代理 HTTPS 流量)。因此,由于您缺乏详细信息,您可能只是以错误的方式访问代理,或者说不可能。
      • 我将它与 mitmproxy 结合使用(已安装 SSL 证书)。 Mitmproxylog 显示:“127.0.0.1:53557:clientconnect”。问题是代码不再完全运行。在我的第一篇文章中的代码显示服务器响应之前。现在我没有得到任何回应或类似的。
      • @NecronomiconCoding:可能需要通过 HTTP 而不是 HTTPS 访问代理。由于您做错了,它无法与代理进行正确的代理握手,即代理需要 HTTP CONNECT 请求,而您的代码以 TLS 握手开始。
      • 谢谢,当通过 HTTP 访问它时,它给了我错误 failed: malformed HTTP response: http2_handshake_failed
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多