@@ -16,6 +16,7 @@ import (
1616 "slices"
1717 "strings"
1818 "sync"
19+ "sync/atomic"
1920 "time"
2021
2122 "github.com/goccy/go-yaml"
@@ -31,9 +32,10 @@ const (
3132 flushTimeout time.Duration = 10 * time .Millisecond
3233 availProxyUpdateInterval time.Duration = 30 * time .Second
3334 kbSize int64 = 1000
35+ rrIndexMax uint32 = 1_000_000
3436)
3537
36- var supportedChainTypes = []string {"strict" , "dynamic" , "random" } // TODO: round_robin chain
38+ var supportedChainTypes = []string {"strict" , "dynamic" , "random" , " round_robin" }
3739
3840// Hop-by-hop headers
3941// https://datatracker.ietf.org/doc/html/rfc2616#section-13.5.1
@@ -105,6 +107,8 @@ type proxyApp struct {
105107 keyFile string
106108 httpServerAddr string
107109 proxychain * proxyChainConfig
110+ rrIndex uint32
111+ rrIndexReset uint32
108112
109113 mu sync.RWMutex
110114 availProxyList []proxyEntry
@@ -215,17 +219,35 @@ func (p *proxyApp) getSocks() (proxy.Dialer, *http.Client, error) {
215219 p .mu .RLock ()
216220 defer p .mu .RUnlock ()
217221 chainType := p .proxychain .Chain .Type
218- copyProxyList := make ([]proxyEntry , len (p .availProxyList ))
222+ var chainLength int
223+ if p .proxychain .Chain .Length > len (p .availProxyList ) || p .proxychain .Chain .Length <= 0 {
224+ chainLength = len (p .availProxyList )
225+ } else {
226+ chainLength = p .proxychain .Chain .Length
227+ }
228+ copyProxyList := make ([]proxyEntry , 0 , len (p .availProxyList ))
219229 if chainType == "random" {
220230 copy (copyProxyList , p .availProxyList )
221231 shuffle (copyProxyList )
222- var chainLength int
223- if p .proxychain .Chain .Length > len (copyProxyList ) || p .proxychain .Chain .Length <= 0 {
224- chainLength = len (copyProxyList )
225- } else {
226- chainLength = p .proxychain .Chain .Length
227- }
228232 copyProxyList = copyProxyList [:chainLength ]
233+ } else if chainType == "round_robin" {
234+ var start uint32
235+ for {
236+ start = atomic .LoadUint32 (& p .rrIndex )
237+ next := start + 1
238+ if start >= p .rrIndexReset {
239+ p .logger .Debug ().Msg ("Resetting round robin index" )
240+ next = 0
241+ }
242+ if atomic .CompareAndSwapUint32 (& p .rrIndex , start , next ) {
243+ break
244+ }
245+ }
246+ startIdx := int (start % uint32 (len (p .availProxyList )))
247+ for i := 0 ; i < chainLength ; i ++ {
248+ idx := (startIdx + i ) % len (p .availProxyList )
249+ copyProxyList = append (copyProxyList , p .availProxyList [idx ])
250+ }
229251 } else {
230252 copyProxyList = p .availProxyList
231253 }
@@ -639,6 +661,7 @@ func New(conf *Config) *proxyApp {
639661 if ! slices .Contains (supportedChainTypes , chainType ) {
640662 p .logger .Fatal ().Msgf ("[proxychain config] Chain type `%s` is not supported" , chainType )
641663 }
664+ p .rrIndexReset = rrIndexMax
642665 } else {
643666 var dialer proxy.Dialer
644667 var err error
0 commit comments