● 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 / traffic / pool.go
← Back to directory
190 lines · 4.0 KB
Raw
1package traffic
2
3import (
4 "context"
5 "fmt"
6 "sync"
7 "time"
8
9 "github.com/shehzade/contraband/pkg/common"
10)
11
12// ExfilChunkTask models a work item submitted to the multi-threaded pool.
13type ExfilChunkTask struct {
14 Sequence uint32
15 TotalChunks uint32
16 Payload []byte
17 Execute func(ctx context.Context, seq, total uint32, chunk []byte) error
18}
19
20// ExfilChunkResult models outcome of an exfiltrated chunk.
21type ExfilChunkResult struct {
22 Sequence uint32
23 Bytes int64
24 Duration time.Duration
25 Err error
26}
27
28// WorkerPool coordinates 1–64 concurrent worker threads sharing a global RateLimiter.
29type WorkerPool struct {
30 concurrency int
31 limiter RateLimiter
32 cadence *CadenceEngine
33 metrics *TrafficMetrics
34 tasks chan ExfilChunkTask
35 results chan ExfilChunkResult
36 wg sync.WaitGroup
37 ctx context.Context
38 cancel context.CancelFunc
39 closeMu sync.RWMutex
40 closed bool
41}
42
43// NewWorkerPool creates and starts worker pool with shared bandwidth throttling.
44func NewWorkerPool(ctx context.Context, concurrency int, limiter RateLimiter, cadence *CadenceEngine, metrics *TrafficMetrics) (*WorkerPool, error) {
45 if concurrency < 1 {
46 concurrency = 1
47 } else if concurrency > common.MaxThreads {
48 return nil, fmt.Errorf("worker pool concurrency %d exceeds maximum %d", concurrency, common.MaxThreads)
49 }
50
51 poolCtx, cancel := context.WithCancel(ctx)
52 pool := &WorkerPool{
53 concurrency: concurrency,
54 limiter: limiter,
55 cadence: cadence,
56 metrics: metrics,
57 tasks: make(chan ExfilChunkTask, concurrency*2),
58 results: make(chan ExfilChunkResult, concurrency*2),
59 ctx: poolCtx,
60 cancel: cancel,
61 }
62
63 for i := 0; i < concurrency; i++ {
64 pool.wg.Add(1)
65 go pool.workerLoop(i)
66 }
67
68 return pool, nil
69}
70
71func (p *WorkerPool) workerLoop(workerID int) {
72 defer p.wg.Done()
73
74 for {
75 select {
76 case <-p.ctx.Done():
77 return
78 case task, ok := <-p.tasks:
79 if !ok {
80 return
81 }
82 p.processTask(task)
83 }
84 }
85}
86
87func (p *WorkerPool) processTask(task ExfilChunkTask) {
88 start := time.Now()
89 chunkLen := len(task.Payload)
90
91 // 1. Inter-chunk cadence sleep if configured
92 if p.cadence != nil {
93 if err := p.cadence.Sleep(p.ctx); err != nil {
94 select {
95 case <-p.ctx.Done():
96 return
97 case p.results <- ExfilChunkResult{
98 Sequence: task.Sequence,
99 Err: err,
100 }:
101 }
102 return
103 }
104 }
105
106 // 2. Bandwidth rate limiting (shared across all workers)
107 if p.limiter != nil && chunkLen > 0 {
108 if err := p.limiter.WaitN(p.ctx, chunkLen); err != nil {
109 select {
110 case <-p.ctx.Done():
111 return
112 case p.results <- ExfilChunkResult{
113 Sequence: task.Sequence,
114 Err: err,
115 }:
116 }
117 return
118 }
119 }
120
121 // 3. Worker panic recovery and execution
122 var execErr error
123 if task.Execute != nil {
124 func() {
125 defer func() {
126 if r := recover(); r != nil {
127 execErr = fmt.Errorf("worker panic on chunk %d: %v", task.Sequence, r)
128 }
129 }()
130 execErr = task.Execute(p.ctx, task.Sequence, task.TotalChunks, task.Payload)
131 }()
132 }
133
134 dur := time.Since(start)
135 if execErr == nil && p.metrics != nil {
136 p.metrics.RecordBytesSent(int64(chunkLen))
137 p.metrics.RecordPacket()
138 }
139
140 select {
141 case <-p.ctx.Done():
142 return
143 case p.results <- ExfilChunkResult{
144 Sequence: task.Sequence,
145 Bytes: int64(chunkLen),
146 Duration: dur,
147 Err: execErr,
148 }:
149 }
150}
151
152// Submit sends a chunk task to the worker pool. Blocks if queue is full.
153func (p *WorkerPool) Submit(task ExfilChunkTask) error {
154 p.closeMu.RLock()
155 defer p.closeMu.RUnlock()
156
157 if p.closed {
158 return fmt.Errorf("worker pool is closed")
159 }
160 if p.ctx.Err() != nil {
161 return p.ctx.Err()
162 }
163 select {
164 case <-p.ctx.Done():
165 return p.ctx.Err()
166 case p.tasks <- task:
167 return nil
168 }
169}
170
171// Results returns the output results channel.
172func (p *WorkerPool) Results() <-chan ExfilChunkResult {
173 return p.results
174}
175
176// Close gracefully drains tasks and shuts down workers.
177func (p *WorkerPool) Close() {
178 p.closeMu.Lock()
179 if p.closed {
180 p.closeMu.Unlock()
181 return
182 }
183 p.closed = true
184 close(p.tasks)
185 p.closeMu.Unlock()
186
187 p.wg.Wait()
188 close(p.results)
189 p.cancel()
190}