rawhttp version:
v.0.1.0
(latest)
Current Behavior:
RawHTTP clients waits forever, if the server does not send any data and the protocol was https
In the current implementation the tlsConn does not respect the timeout settings once the connection is established.
The function tlsConn.Handshake() will wait until the server sends data or closes the connection.
This means a single tarpit server, which sends TCP-Alive packets but no data, is able to halt rawhttp clients.
Related Ticket for nuclei: projectdiscovery/nuclei#2432
Expected Behavior:
cancel the connection and apply the specified timeout for connection and reading
Steps To Reproduce:
See test
Anything else:
Related: #54
The following PR fixes a bug when requesting "https" targets.
Simple test
The following snippet creates a server which holds the connection open and a client with a timeout connecting to the server.
The server component can also be a netcat listener nc -v -l -p 1337
.
2022/08/15 18:09:08 [c] request to https://127.0.0.1:1337
2022/08/15 18:09:08 [s] Starting srv on 127.0.0.1:1337
2022/08/15 18:09:08 [s] tarpitting 127.0.0.1:41670 for 1m30s
2022/08/15 18:10:39 [s] release 127.0.0.1:41670 from tarpit
2022/08/15 18:10:39 [c] finished
2022/08/15 18:10:39 took 90.087851282
package main
import(
"log"
"fmt"
"time"
"net"
"github.com/projectdiscovery/rawhttp"
)
func main() {
addr := "127.0.0.1:1337"
sleep := 90 * time.Second
timeout := 2 * time.Second
ln, err := ListenAndServe(addr, sleep)
if err != nil {
log.Fatal(err)
}
defer ln.Close()
// perform check
start := time.Now()
Check(addr, timeout)
took := time.Now().Sub(start)
log.Println("took", took.Seconds())
}
func Check(addr string, timeout time.Duration)(err error){
options := rawhttp.DefaultOptions
options.Timeout = timeout
client := rawhttp.NewClient(options)
uri := fmt.Sprintf("https://%s",addr)
log.Printf("[c] request to %s", uri)
_, err = client.Get(uri)
log.Printf("[c] finished")
return
}
// --[Server Components]--
func ListenAndServe(addr string, sleep time.Duration)(listener net.Listener, err error){
listener, err = net.Listen("tcp", addr)
if err != nil {
return
}
go StartServer(listener, sleep)
return
}
func StartServer(ln net.Listener, sleep time.Duration){
log.Println("[s] Starting srv on", ln.Addr())
for {
conn, err := ln.Accept()
if err != nil {
continue
}
go handleClientConn(conn, sleep)
}
}
// tarpit function
func handleClientConn(conn net.Conn, sleepDur time.Duration) {
defer conn.Close()
log.Printf("[s] tarpitting %s for %s\n",conn.RemoteAddr(), sleepDur.String())
time.Sleep(sleepDur)
log.Printf("[s] release %s from tarpit\n", conn.RemoteAddr())
}