● 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 / wrappers.go
← Back to directory
218 lines · 5.1 KB
Raw
1package traffic
2
3import (
4 "context"
5 "io"
6 "net"
7)
8
9// PacedReader wraps an io.Reader and throttles read throughput using RateLimiter.
10type PacedReader struct {
11 r io.Reader
12 limiter RateLimiter
13 metrics *TrafficMetrics
14 ctx context.Context
15 maxChunk int // Maximum chunk read slice (default 32KB)
16}
17
18// NewPacedReader creates a bandwidth-limited io.Reader.
19func NewPacedReader(ctx context.Context, r io.Reader, limiter RateLimiter, metrics *TrafficMetrics) *PacedReader {
20 if ctx == nil {
21 ctx = context.Background()
22 }
23 return &PacedReader{
24 r: r,
25 limiter: limiter,
26 metrics: metrics,
27 ctx: ctx,
28 maxChunk: 32 * 1024, // 32KB chunks prevent huge instantaneous reads
29 }
30}
31
32// Read implements io.Reader, consuming tokens and pacing stream flow.
33func (pr *PacedReader) Read(p []byte) (n int, err error) {
34 if len(p) == 0 {
35 return 0, nil
36 }
37
38 sliceSize := len(p)
39 if pr.maxChunk > 0 && sliceSize > pr.maxChunk {
40 sliceSize = pr.maxChunk
41 }
42
43 // 1. Wait for bandwidth rate limiter tokens
44 if err := pr.limiter.WaitN(pr.ctx, sliceSize); err != nil {
45 return 0, err
46 }
47
48 // 2. Read from underlying source
49 n, err = pr.r.Read(p[:sliceSize])
50
51 // 3. Record metrics
52 if n > 0 && pr.metrics != nil {
53 pr.metrics.RecordBytesSent(int64(n))
54 pr.metrics.RecordPacket()
55 }
56
57 // 4. Refund tokens if read was shorter than requested slice (e.g. EOF)
58 if n < sliceSize {
59 pr.limiter.Refund(sliceSize - n)
60 }
61
62 return n, err
63}
64
65// Close closes the underlying reader if it implements io.Closer.
66func (pr *PacedReader) Close() error {
67 if closer, ok := pr.r.(io.Closer); ok {
68 return closer.Close()
69 }
70 return nil
71}
72
73// PacedWriter wraps an io.Writer with rate limiting.
74type PacedWriter struct {
75 w io.Writer
76 limiter RateLimiter
77 metrics *TrafficMetrics
78 ctx context.Context
79 maxChunk int
80}
81
82// NewPacedWriter creates a bandwidth-limited io.Writer.
83func NewPacedWriter(ctx context.Context, w io.Writer, limiter RateLimiter, metrics *TrafficMetrics) *PacedWriter {
84 if ctx == nil {
85 ctx = context.Background()
86 }
87 return &PacedWriter{
88 w: w,
89 limiter: limiter,
90 metrics: metrics,
91 ctx: ctx,
92 maxChunk: 32 * 1024,
93 }
94}
95
96// Write slices large buffers, consumes tokens, and writes to underlying writer.
97func (pw *PacedWriter) Write(p []byte) (int, error) {
98 if len(p) == 0 {
99 return 0, nil
100 }
101 total := 0
102 for total < len(p) {
103 remaining := len(p) - total
104 chunk := remaining
105 if chunk > pw.maxChunk {
106 chunk = pw.maxChunk
107 }
108 if err := pw.limiter.WaitN(pw.ctx, chunk); err != nil {
109 return total, err
110 }
111 n, err := pw.w.Write(p[total : total+chunk])
112 if n > 0 {
113 total += n
114 if pw.metrics != nil {
115 pw.metrics.RecordBytesSent(int64(n))
116 pw.metrics.RecordPacket()
117 }
118 }
119 if n < chunk {
120 pw.limiter.Refund(chunk - n)
121 }
122 if err != nil {
123 return total, err
124 }
125 }
126 return total, nil
127}
128
129// PacedConn wraps net.Conn to provide transparent bandwidth pacing and packet gap pacing.
130// 100% compatible with crypto/tls, uTLS, HTTP/1.1, HTTP/2, WebSocket, gRPC, and raw TCP.
131type PacedConn struct {
132 net.Conn
133 limiter RateLimiter
134 cadence *CadenceEngine
135 metrics *TrafficMetrics
136 ctx context.Context
137 maxWriteChunk int
138}
139
140// NewPacedConn wraps an existing net.Conn with bandwidth rate limiting and packet pacing.
141func NewPacedConn(ctx context.Context, conn net.Conn, limiter RateLimiter, cadence *CadenceEngine, metrics *TrafficMetrics) *PacedConn {
142 if ctx == nil {
143 ctx = context.Background()
144 }
145 return &PacedConn{
146 Conn: conn,
147 limiter: limiter,
148 cadence: cadence,
149 metrics: metrics,
150 ctx: ctx,
151 maxWriteChunk: 32 * 1024, // 32KB write chunking
152 }
153}
154
155// Write slices large buffers, enforces token consumption, and paces packet gaps.
156func (pc *PacedConn) Write(b []byte) (int, error) {
157 if len(b) == 0 {
158 return 0, nil
159 }
160
161 totalWritten := 0
162 chunkSize := pc.maxWriteChunk
163 if chunkSize <= 0 {
164 chunkSize = 32 * 1024
165 }
166
167 for totalWritten < len(b) {
168 remaining := len(b) - totalWritten
169 toWrite := remaining
170 if toWrite > chunkSize {
171 toWrite = chunkSize
172 }
173
174 // 1. Enforce token bucket rate limiting
175 if err := pc.limiter.WaitN(pc.ctx, toWrite); err != nil {
176 return totalWritten, err
177 }
178
179 // 2. Perform write to underlying network connection
180 n, err := pc.Conn.Write(b[totalWritten : totalWritten+toWrite])
181 if n > 0 {
182 totalWritten += n
183 if pc.metrics != nil {
184 pc.metrics.RecordBytesSent(int64(n))
185 pc.metrics.RecordPacket()
186 }
187 }
188
189 // 3. Refund if partial socket write occurred
190 if n < toWrite {
191 pc.limiter.Refund(toWrite - n)
192 }
193
194 if err != nil {
195 return totalWritten, err
196 }
197
198 // 4. Microsecond packet gap pacing between consecutive writes
199 if pc.cadence != nil && totalWritten < len(b) {
200 if gap := pc.limiter.PacketGap(); gap > 0 {
201 if err := pc.cadence.SleepDuration(pc.ctx, gap); err != nil {
202 return totalWritten, err
203 }
204 }
205 }
206 }
207
208 return totalWritten, nil
209}
210
211// Read delegates to underlying net.Conn and records received byte metrics.
212func (pc *PacedConn) Read(b []byte) (int, error) {
213 n, err := pc.Conn.Read(b)
214 if n > 0 && pc.metrics != nil {
215 pc.metrics.RecordBytesRecv(int64(n))
216 }
217 return n, err
218}