Skip to content

Instantly share code, notes, and snippets.

@jim3ma
Created January 25, 2017 02:11
Show Gist options
  • Save jim3ma/3750675f141669ac4702bc9deaf31c6b to your computer and use it in GitHub Desktop.
Save jim3ma/3750675f141669ac4702bc9deaf31c6b to your computer and use it in GitHub Desktop.
Register Dialer Type for HTTP&HTTPS Proxy in golang
package main
import (
"bufio"
"fmt"
"net"
"net/http"
"net/url"
"crypto/tls"
"golang.org/x/net/proxy"
)
type direct struct{}
// Direct is a direct proxy: one that makes network connections directly.
var Direct = direct{}
func (direct) Dial(network, addr string) (net.Conn, error) {
return net.Dial(network, addr)
}
// httpsDialer
type httpsDialer struct {}
// HTTPSDialer is a https proxy: one that makes network connections on tls.
var HttpsDialer = httpsDialer{}
var TlsConfig = &tls.Config{}
func (d httpsDialer) Dial(network, addr string) (c net.Conn, err error) {
c, err = tls.Dial("tcp", addr, TlsConfig)
if err != nil {
fmt.Println(err)
}
return
}
// httpProxy is a HTTP/HTTPS connect proxy.
type httpProxy struct {
host string
haveAuth bool
username string
password string
forward proxy.Dialer
}
func newHTTPProxy(uri *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
s := new(httpProxy)
s.host = uri.Host
s.forward = forward
if uri.User != nil {
s.haveAuth = true
s.username = uri.User.Username()
s.password, _ = uri.User.Password()
}
return s, nil
}
func (s *httpProxy) Dial(network, addr string) (net.Conn, error) {
// Dial and create the https client connection.
c, err := s.forward.Dial("tcp", s.host)
if err != nil {
return nil, err
}
// HACK. http.ReadRequest also does this.
reqURL, err := url.Parse("http://" + addr)
if err != nil {
c.Close()
return nil, err
}
reqURL.Scheme = ""
req, err := http.NewRequest("CONNECT", reqURL.String(), nil)
if err != nil {
c.Close()
return nil, err
}
req.Close = false
if s.haveAuth {
req.SetBasicAuth(s.username, s.password)
}
req.Header.Set("User-Agent", "Powerby Gota")
err = req.Write(c)
if err != nil {
c.Close()
return nil, err
}
resp, err := http.ReadResponse(bufio.NewReader(c), req)
if err != nil {
// TODO close resp body ?
resp.Body.Close()
c.Close()
return nil, err
}
resp.Body.Close()
if resp.StatusCode != 200 {
c.Close()
err = fmt.Errorf("Connect server using proxy error, StatusCode [%d]", resp.StatusCode)
return nil, err
}
return c, nil
}
func FromURL(u *url.URL, forward proxy.Dialer) (proxy.Dialer, error){
return proxy.FromURL(u, forward)
}
func FromEnvironment() proxy.Dialer {
return proxy.FromEnvironment()
}
func init() {
proxy.RegisterDialerType("http", newHTTPProxy)
proxy.RegisterDialerType("https", newHTTPProxy)
}
func main() {
// http proxy
httpProxyURI, _ := url.Parse("http://your http proxy:3128")
httpDialer, err := proxy.FromURL(httpProxyURI, Direct)
conn, err := httpDialer.Dial("tcp", "google.com:80")
if err != nil {
panic(err)
}
fmt.Printf("Create http tunnel OK: %+v\n", conn)
httpsProxyURI, _ := url.Parse("https://your https proxy:443")
httpsDialer, err := proxy.FromURL(httpsProxyURI, HttpsDialer)
conn, err = httpsDialer.Dial("tcp", "google.com:443")
if err != nil {
panic(err)
}
fmt.Printf("Create https tunnel OK: %+v\n", conn)
// TODO use conn
}
@jim3ma
Copy link
Author

jim3ma commented Jan 25, 2017

Copy from https://github.com/Yawning/obfs4/blob/master/obfs4proxy/obfs4proxy.go, but remove the deprecated httputil.ClientConn
Currently, the http.Client doesn't support hijack, so we just export the raw tcp or tls over tcp Conn.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment