|
@@ -0,0 +1,147 @@
|
|
|
|
+package main
|
|
|
|
+
|
|
|
|
+import (
|
|
|
|
+ "fmt"
|
|
|
|
+ "io"
|
|
|
|
+ "log"
|
|
|
|
+ "net"
|
|
|
|
+ "net/http"
|
|
|
|
+ "strings"
|
|
|
|
+
|
|
|
|
+ "github.com/gin-gonic/gin"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+const (
|
|
|
|
+ TUNNEL_PACKET = `HTTP/1.1 200 Connection Established\r\nProxy-agent: spit-wizard.void\r\n\r\n`
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+func main() {
|
|
|
|
+ r := gin.Default()
|
|
|
|
+ r.NoRoute(routeProxy) // NO Route is every Route!!!
|
|
|
|
+ r.Run(":8080") // listen and serve on 0.0.0.0:8080
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Then I can process all routes
|
|
|
|
+func routeProxy(c *gin.Context) {
|
|
|
|
+ req := c.Request
|
|
|
|
+ go resolveReq(req) // just print basic info. Remember you can't proxy youself.
|
|
|
|
+
|
|
|
|
+ if req.Method == http.MethodConnect {
|
|
|
|
+ // create http tunnel process https
|
|
|
|
+ httpsProxy(c, req)
|
|
|
|
+ } else {
|
|
|
|
+ // process plain http
|
|
|
|
+ httpProxy(c, req)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func resolveReq(req *http.Request) {
|
|
|
|
+ fmt.Printf("Method: %s, Host: %s, URL: %s, Version: %s\n", req.Method, req.Host, req.URL.Path, req.Proto)
|
|
|
|
+ //fmt.Printf("%+v\n", req)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func httpProxy(c *gin.Context, req *http.Request) {
|
|
|
|
+ newReq, _ := http.NewRequest(req.Method, req.URL.String(), req.Body)
|
|
|
|
+ resp, err := http.DefaultClient.Do(newReq)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+ defer resp.Body.Close()
|
|
|
|
+
|
|
|
|
+ data, err := io.ReadAll(resp.Body)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+ fmt.Printf("%s\n%s\n%s\n", req.Host, req.Method, req.RequestURI)
|
|
|
|
+ code := resp.StatusCode
|
|
|
|
+ c.Status(code) // change the status code, default is 404 !!!
|
|
|
|
+ for k, v := range resp.Header {
|
|
|
|
+ c.Header(k, strings.Join(v, ","))
|
|
|
|
+ }
|
|
|
|
+ c.Header("Server", "spit-wizard.void") // just change it, this is yours gin proxy.
|
|
|
|
+ c.Writer.Write(data)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func httpsProxy(c *gin.Context, req *http.Request) {
|
|
|
|
+ // established connect tunnel
|
|
|
|
+ c.Status(200)
|
|
|
|
+ c.Header("Server", "spit-wizard.void")
|
|
|
|
+ c.Writer.Write([]byte(TUNNEL_PACKET))
|
|
|
|
+ // c.Writer.Flush() // this may cause proble, but I don't know.
|
|
|
|
+
|
|
|
|
+ address := req.URL.Host // it contains the port
|
|
|
|
+ tunnelConn, err := net.Dial("tcp", address)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+ //fmt.Printf("try to established Connect Tunnel to: %s has been successfully.\n", address)
|
|
|
|
+
|
|
|
|
+ // But next is a TCP communication, but the tcp conn is a non-export variable,
|
|
|
|
+ // so I can't get it, in the same, client's data is binary, so gin can't parse it.
|
|
|
|
+ // so I can't do anything!!!
|
|
|
|
+ // LOL, I find HTTP hijacker, it can make me take over the connection!!!
|
|
|
|
+ hj, ok := c.Writer.(http.Hijacker)
|
|
|
|
+ if !ok {
|
|
|
|
+ http.Error(c.Writer, "webserver doesn't support hijacking", http.StatusInternalServerError)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ conn, bufrw, err := hj.Hijack()
|
|
|
|
+ if err != nil {
|
|
|
|
+ http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ defer conn.Close()
|
|
|
|
+
|
|
|
|
+ // first read request, then write response.
|
|
|
|
+ // data flow direction:
|
|
|
|
+ // client <---> proxy <---> server
|
|
|
|
+
|
|
|
|
+ done := make(chan struct{})
|
|
|
|
+ go func() {
|
|
|
|
+
|
|
|
|
+ io.Copy(tunnelConn, bufrw)
|
|
|
|
+ /*
|
|
|
|
+ // this is my first write, but then I find other use io.Copy
|
|
|
|
+ log.Println("client --> proxy --> server")
|
|
|
|
+ data := make([]byte, 1024)
|
|
|
|
+ for {
|
|
|
|
+ log.Println("client --> proxy")
|
|
|
|
+ n, err := bufrw.Read(data)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Printf("%v\n", err)
|
|
|
|
+ done <- struct{}{}
|
|
|
|
+ }
|
|
|
|
+ log.Println("proxy --> server")
|
|
|
|
+ tunnelConn.Write(data[:n])
|
|
|
|
+ }
|
|
|
|
+ */
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ go func() {
|
|
|
|
+
|
|
|
|
+ io.Copy(bufrw, tunnelConn)
|
|
|
|
+ /*
|
|
|
|
+ log.Println("server --> proxy --> client")
|
|
|
|
+ data := make([]byte, 1024)
|
|
|
|
+ for {
|
|
|
|
+ log.Println("server --> proxy")
|
|
|
|
+ for {
|
|
|
|
+ n, err := tunnelConn.Read(data)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Printf("%v\n", err)
|
|
|
|
+ done <- struct{}{}
|
|
|
|
+ }
|
|
|
|
+ log.Println("proxy --> client")
|
|
|
|
+ bufrw.Write(data[:n])
|
|
|
|
+ if n < 1024 {
|
|
|
|
+ break
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ bufrw.Flush()
|
|
|
|
+ }
|
|
|
|
+ */
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ <-done
|
|
|
|
+ //fmt.Println("The Tunnel has closed.")
|
|
|
|
+}
|