-
Notifications
You must be signed in to change notification settings - Fork 3
/
block.go
130 lines (113 loc) · 3.03 KB
/
block.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
package scanblock
import (
"fmt"
"math/rand"
"net/http"
"os"
"time"
)
func (sb *ScanBlock) block(w http.ResponseWriter, r *http.Request) {
// Attempt to clean the cache.
removedEntries := sb.cache.CleanEntries(
time.Duration(sb.config.RememberSeconds) * time.Second,
)
if removedEntries > 0 {
fmt.Fprintf(os.Stdout, "scanblock plugin %q purged %d cache entries\n", sb.name, removedEntries)
}
// If we are not playing any games, reply with a plain error message.
if !sb.config.PlayGames {
http.Error(w, "blocked by scanblock", http.StatusTooManyRequests)
return
}
// Always wait a little.
select {
case <-r.Context().Done():
case <-time.After(getRandomWaitDuration()):
}
// Select random game until one works.
start := rand.Intn(len(games)) //nolint:gosec // Not for security.
for {
// Try game and return if successful.
if games[start](w, r) {
return
}
// Select next game and wrap to start.
start = (start + 1) % len(games)
}
}
var games = []func(w http.ResponseWriter, r *http.Request) (ok bool){
playClose,
play4xx,
play4xxEmoji,
play4xxMessage,
}
func playClose(w http.ResponseWriter, r *http.Request) (ok bool) {
// Check if the response writer supports hijacking.
hijacker, ok := w.(http.Hijacker)
if !ok {
return false
}
// Hijack connection.
conn, _, err := hijacker.Hijack()
if err != nil {
return false
}
// Close connection.
_ = conn.Close()
return true
}
func play4xx(w http.ResponseWriter, r *http.Request) (ok bool) {
w.WriteHeader(getRandom4xxStatusCode())
return true
}
func play4xxEmoji(w http.ResponseWriter, r *http.Request) (ok bool) {
http.Error(w, "😛", getRandom4xxStatusCode())
return true
}
func play4xxMessage(w http.ResponseWriter, r *http.Request) (ok bool) {
http.Error(w, "Let's play a game.", getRandom4xxStatusCode())
return true
}
const (
waitDurationMin = 10 * time.Second
waitDurationMax = 25 * time.Second
)
func getRandomWaitDuration() time.Duration {
return time.Duration(
rand.Intn(int(waitDurationMax-waitDurationMin)), //nolint:gosec // Not for security.
) + waitDurationMin
}
func getRandom4xxStatusCode() int {
return statusCodes[rand.Intn(len(statusCodes))] //nolint:gosec // Not for security.
}
var statusCodes = []int{
http.StatusBadRequest,
http.StatusUnauthorized,
http.StatusPaymentRequired,
http.StatusForbidden,
http.StatusNotFound,
http.StatusMethodNotAllowed,
http.StatusNotAcceptable,
http.StatusProxyAuthRequired,
http.StatusRequestTimeout,
http.StatusConflict,
http.StatusGone,
http.StatusLengthRequired,
http.StatusPreconditionFailed,
http.StatusRequestEntityTooLarge,
http.StatusRequestURITooLong,
http.StatusUnsupportedMediaType,
http.StatusRequestedRangeNotSatisfiable,
http.StatusExpectationFailed,
http.StatusTeapot,
http.StatusMisdirectedRequest,
http.StatusUnprocessableEntity,
http.StatusLocked,
http.StatusFailedDependency,
http.StatusTooEarly,
http.StatusUpgradeRequired,
http.StatusPreconditionRequired,
http.StatusTooManyRequests,
http.StatusRequestHeaderFieldsTooLarge,
http.StatusUnavailableForLegalReasons,
}