● Notice //GitHub (@shehzade) is temporarily suspended pending an automated false-positive review. All tools are hosted on this Code Mirror and available on request (contact); GitHub links will return once reinstated.
PUBLIC MIRRORGo v1.0

Multi-protocol network egress assessment and DLP verification framework featuring automated L4 sweeps, corporate proxy traversal, and covert protocol smuggling across 20+ channels.

$ git clone https://shehzade.io/repos/contraband.git Copy
Source ZIP (1.93 MB)
Code 174 Commits 1
contraband / pkg / client / sweep / rate.go
← Back to directory
130 lines · 2.4 KB
Raw
1package sweep
2
3import (
4 "context"
5 "crypto/rand"
6 "math/big"
7 "sync"
8 "time"
9)
10
11// TokenBucket implements a pure Go thread-safe token bucket rate limiter.
12type TokenBucket struct {
13 mu sync.Mutex
14 capacity float64
15 tokens float64
16 rate float64 // tokens per second
17 lastRefill time.Time
18}
19
20// NewTokenBucket creates a token bucket with capacity and refill rate in tokens/sec.
21func NewTokenBucket(rate, capacity int) *TokenBucket {
22 if rate < 1 {
23 rate = 1
24 }
25 if capacity < 1 {
26 capacity = 1
27 }
28 now := time.Now()
29 return &TokenBucket{
30 capacity: float64(capacity),
31 tokens: float64(capacity),
32 rate: float64(rate),
33 lastRefill: now,
34 }
35}
36
37// Wait blocks until a token is available or ctx is canceled.
38func (tb *TokenBucket) Wait(ctx context.Context) error {
39 for {
40 select {
41 case <-ctx.Done():
42 return ctx.Err()
43 default:
44 }
45
46 tb.mu.Lock()
47 now := time.Now()
48 elapsed := now.Sub(tb.lastRefill).Seconds()
49 if elapsed > 0 {
50 tb.tokens += elapsed * tb.rate
51 if tb.tokens > tb.capacity {
52 tb.tokens = tb.capacity
53 }
54 tb.lastRefill = now
55 }
56
57 if tb.tokens >= 1.0 {
58 tb.tokens -= 1.0
59 tb.mu.Unlock()
60 return nil
61 }
62
63 // Calculate sleep time until 1 token is available
64 missing := 1.0 - tb.tokens
65 sleepSec := missing / tb.rate
66 sleepDur := time.Duration(sleepSec * float64(time.Second))
67 if sleepDur < time.Microsecond {
68 sleepDur = time.Microsecond
69 }
70 tb.mu.Unlock()
71
72 timer := time.NewTimer(sleepDur)
73 select {
74 case <-ctx.Done():
75 if !timer.Stop() {
76 select {
77 case <-timer.C:
78 default:
79 }
80 }
81 return ctx.Err()
82 case <-timer.C:
83 }
84 }
85}
86
87// ApplyJitter sleeps for a random duration between min and max, respecting ctx.
88func ApplyJitter(ctx context.Context, min, max time.Duration) error {
89 if max <= 0 || min >= max {
90 if min > 0 {
91 timer := time.NewTimer(min)
92 select {
93 case <-ctx.Done():
94 if !timer.Stop() {
95 select {
96 case <-timer.C:
97 default:
98 }
99 }
100 return ctx.Err()
101 case <-timer.C:
102 return nil
103 }
104 }
105 return nil
106 }
107
108 diff := max - min
109 n, err := rand.Int(rand.Reader, big.NewInt(int64(diff)))
110 var jitterDur time.Duration
111 if err == nil {
112 jitterDur = min + time.Duration(n.Int64())
113 } else {
114 jitterDur = min
115 }
116
117 timer := time.NewTimer(jitterDur)
118 select {
119 case <-ctx.Done():
120 if !timer.Stop() {
121 select {
122 case <-timer.C:
123 default:
124 }
125 }
126 return ctx.Err()
127 case <-timer.C:
128 return nil
129 }
130}