github.com/coreos/goproxy@v0.0.0-20190513173959-f8dc2d7ba04e/examples/goproxy-transparent/transparent.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "bytes" 6 "flag" 7 "fmt" 8 "log" 9 "net" 10 "net/http" 11 "net/url" 12 "regexp" 13 14 "github.com/elazarl/goproxy" 15 "github.com/inconshreveable/go-vhost" 16 ) 17 18 func orPanic(err error) { 19 if err != nil { 20 panic(err) 21 } 22 } 23 24 func main() { 25 verbose := flag.Bool("v", true, "should every proxy request be logged to stdout") 26 http_addr := flag.String("httpaddr", ":3129", "proxy http listen address") 27 https_addr := flag.String("httpsaddr", ":3128", "proxy https listen address") 28 flag.Parse() 29 30 proxy := goproxy.NewProxyHttpServer() 31 proxy.Verbose = *verbose 32 if proxy.Verbose { 33 log.Printf("Server starting up! - configured to listen on http interface %s and https interface %s", *http_addr, *https_addr) 34 } 35 36 proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 37 if req.Host == "" { 38 fmt.Fprintln(w, "Cannot handle requests without Host header, e.g., HTTP 1.0") 39 return 40 } 41 req.URL.Scheme = "http" 42 req.URL.Host = req.Host 43 proxy.ServeHTTP(w, req) 44 }) 45 proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))). 46 HandleConnect(goproxy.AlwaysMitm) 47 proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*:80$"))). 48 HijackConnect(func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) { 49 defer func() { 50 if e := recover(); e != nil { 51 ctx.Logf("error connecting to remote: %v", e) 52 client.Write([]byte("HTTP/1.1 500 Cannot reach destination\r\n\r\n")) 53 } 54 client.Close() 55 }() 56 clientBuf := bufio.NewReadWriter(bufio.NewReader(client), bufio.NewWriter(client)) 57 remote, err := connectDial(proxy, "tcp", req.URL.Host) 58 orPanic(err) 59 remoteBuf := bufio.NewReadWriter(bufio.NewReader(remote), bufio.NewWriter(remote)) 60 for { 61 req, err := http.ReadRequest(clientBuf.Reader) 62 orPanic(err) 63 orPanic(req.Write(remoteBuf)) 64 orPanic(remoteBuf.Flush()) 65 resp, err := http.ReadResponse(remoteBuf.Reader, req) 66 orPanic(err) 67 orPanic(resp.Write(clientBuf.Writer)) 68 orPanic(clientBuf.Flush()) 69 } 70 }) 71 72 go func() { 73 log.Fatalln(http.ListenAndServe(*http_addr, proxy)) 74 }() 75 76 // listen to the TLS ClientHello but make it a CONNECT request instead 77 ln, err := net.Listen("tcp", *https_addr) 78 if err != nil { 79 log.Fatalf("Error listening for https connections - %v", err) 80 } 81 for { 82 c, err := ln.Accept() 83 if err != nil { 84 log.Printf("Error accepting new connection - %v", err) 85 continue 86 } 87 go func(c net.Conn) { 88 tlsConn, err := vhost.TLS(c) 89 if err != nil { 90 log.Printf("Error accepting new connection - %v", err) 91 } 92 if tlsConn.Host() == "" { 93 log.Printf("Cannot support non-SNI enabled clients") 94 return 95 } 96 connectReq := &http.Request{ 97 Method: "CONNECT", 98 URL: &url.URL{ 99 Opaque: tlsConn.Host(), 100 Host: net.JoinHostPort(tlsConn.Host(), "443"), 101 }, 102 Host: tlsConn.Host(), 103 Header: make(http.Header), 104 } 105 resp := dumbResponseWriter{tlsConn} 106 proxy.ServeHTTP(resp, connectReq) 107 }(c) 108 } 109 } 110 111 // copied/converted from https.go 112 func dial(proxy *goproxy.ProxyHttpServer, network, addr string) (c net.Conn, err error) { 113 if proxy.Tr.Dial != nil { 114 return proxy.Tr.Dial(network, addr) 115 } 116 return net.Dial(network, addr) 117 } 118 119 // copied/converted from https.go 120 func connectDial(proxy *goproxy.ProxyHttpServer, network, addr string) (c net.Conn, err error) { 121 if proxy.ConnectDial == nil { 122 return dial(proxy, network, addr) 123 } 124 return proxy.ConnectDial(network, addr) 125 } 126 127 type dumbResponseWriter struct { 128 net.Conn 129 } 130 131 func (dumb dumbResponseWriter) Header() http.Header { 132 panic("Header() should not be called on this ResponseWriter") 133 } 134 135 func (dumb dumbResponseWriter) Write(buf []byte) (int, error) { 136 if bytes.Equal(buf, []byte("HTTP/1.0 200 OK\r\n\r\n")) { 137 return len(buf), nil // throw away the HTTP OK response from the faux CONNECT request 138 } 139 return dumb.Conn.Write(buf) 140 } 141 142 func (dumb dumbResponseWriter) WriteHeader(code int) { 143 panic("WriteHeader() should not be called on this ResponseWriter") 144 } 145 146 func (dumb dumbResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 147 return dumb, bufio.NewReadWriter(bufio.NewReader(dumb), bufio.NewWriter(dumb)), nil 148 }