How to peek or ready to check whether HTTP request or not in TCP proxy?

32 views Asked by At

After spending many hours on this. I can't decide which way is better to implement a TCP proxy that block http request. I tried both peek some bytes without reading it and read some bytes and then write back to upstream servie. When comparing both case, reading some bytes is better performace than the peeking some bytes. I'm not sure why? what i don't like is I have to read and write before copy it. Share me or guide me better way to implement such kind of blocking HTTP request in a TCP proxy with better performance and handling to many connections.

Read and Write before copying -

func main() {
        proxyPort := ":1883"

        listener, err := net.Listen("tcp", proxyPort)
        if err != nil {
                fmt.Println(err)
        }
        defer listener.Close()

        for {
                downstreamConn, err := listener.Accept()
                if err != nil {
                        fmt.Println(err)
                        continue
                }

                go func(downstreamConn net.Conn) {
                        defer downstreamConn.Close()

                        buf := make([]byte, 32)
                        n, err := downstreamConn.Read(buf)
                        if err != nil {
                                fmt.Println("Error reading from downstream connection:", err)
                                return
                        }

                        upstreamConn, err := net.Dial("tcp", ":1887")
                        if err != nil {
                                fmt.Println("Error connecting to upstream server:", err)
                                return
                        }
                        defer upstreamConn.Close()
                        if strings.Contains(string(buf), "HTTP") {
                                fmt.Println("[+] Detected HTTP request, dropping packet")
                                return
                        }

                        _, err = upstreamConn.Write(buf[:n])
                        if err != nil {
                                fmt.Println("Error writing data to upstream connection:", err)
                                return
                        }

                        go func() {
                                _, err := io.Copy(upstreamConn, downstreamConn)
                                if err != nil {
                                        fmt.Println("Error copying from downstream to upstream:", err)
                                }
                        }()
                        _, err = io.Copy(downstreamConn, upstreamConn)
                        if err != nil {
                                fmt.Println("Error copying from upstream to downstream:", err)
                        }
                        upstreamConn.Close()
                }(downstreamConn)
        }
}

peek before copying -

func main() {
proxyPort := ":1883"

        listener, err := net.Listen("tcp", proxyPort)
        if err != nil {
                fmt.Println(err)
        }
        defer listener.Close()
    
        for {
                downstreamConn, err := listener.Accept()
                if err != nil {
                        fmt.Println(err)
                        continue
                }
    
                go func(downstreamConn net.Conn) {
                        defer downstreamConn.Close()
    
                        isHTTPReq, conn, err := peekHTTRequest(downstreamConn)
                        if err != nil {
                                return
                        }
    
                        if isHTTPReq {
                                fmt.Println("[+] Detected HTTP request, dropping packet")
                                return
                        }
    
                        upstreamConn, err := net.Dial("tcp", ":1887")
                        if err != nil {
                                fmt.Println("Error connecting to upstream server:", err)
                                return
                        }
                        defer upstreamConn.Close()
    
                        go func() {
                                _, err := io.Copy(upstreamConn, conn)
                                if err != nil {
                                        fmt.Println("Error copying from downstream to upstream:", err)
                                }
                        }()
                        _, err = io.Copy(downstreamConn, upstreamConn)
                        if err != nil {
                                fmt.Println("Error copying from upstream to downstream:", err)
                        }
                        upstreamConn.Close()
                }(downstreamConn)
        }

}

type readOnlyConn struct {
reader io.Reader
}

func (conn readOnlyConn) Read(p []byte) (int, error)         { return conn.reader.Read(p) }
func (conn readOnlyConn) Write(p []byte) (int, error)        { return 0, io.ErrClosedPipe }
func (conn readOnlyConn) Close() error                       { return nil }
func (conn readOnlyConn) LocalAddr() net.Addr                { return nil }
func (conn readOnlyConn) RemoteAddr() net.Addr               { return nil }
func (conn readOnlyConn) SetDeadline(t time.Time) error      { return nil }
func (conn readOnlyConn) SetReadDeadline(t time.Time) error  { return nil }
func (conn readOnlyConn) SetWriteDeadline(t time.Time) error { return nil }

func peekHTTRequest(conn net.Conn) (bool, io.Reader, error) {
peekedBytes := new(bytes.Buffer)
ok, err := isHTTPRequest(io.TeeReader(conn, peekedBytes))
if err != nil {
return false, nil, err
}
return ok, io.MultiReader(peekedBytes, conn), nil
}

func isHTTPRequest(reader io.Reader) (bool, error) {
buf := make([]byte, 64)
readOnly := &readOnlyConn{reader: reader}
_, err := readOnly.Read(buf)
if err != nil {
return false, err
}

        if strings.Contains(string(buf), "HTTP") {
                return true, nil
        }
    
        return false, nil

}
0

There are 0 answers