-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathdispense.go
138 lines (124 loc) · 3.01 KB
/
dispense.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package prox5
import (
"sync/atomic"
"time"
)
func (p5 *ProxyEngine) getSocksStr(proto ProxyProtocol) string {
var sock *Proxy
var list *proxyList
switch proto {
case ProtoSOCKS4:
list = &p5.Valids.SOCKS4
case ProtoSOCKS4a:
list = &p5.Valids.SOCKS4a
case ProtoSOCKS5:
list = &p5.Valids.SOCKS5
case ProtoHTTP:
list = &p5.Valids.HTTP
}
for {
if list.Len() == 0 {
p5.recycling()
time.Sleep(250 * time.Millisecond)
continue
}
list.Lock()
sock = list.Remove(list.Front()).(*Proxy)
list.Unlock()
switch {
case sock == nil:
p5.recycling()
time.Sleep(250 * time.Millisecond)
continue
case !p5.stillGood(sock):
continue
default:
p5.stats.dispense()
return sock.Endpoint
}
}
}
// Socks5Str gets a SOCKS5 proxy that we have fully verified (dialed and then retrieved our IP address from a what-is-my-ip endpoint.
// Will block if one is not available!
func (p5 *ProxyEngine) Socks5Str() string {
return p5.getSocksStr(ProtoSOCKS5)
}
// Socks4Str gets a SOCKS4 proxy that we have fully verified.
// Will block if one is not available!
func (p5 *ProxyEngine) Socks4Str() string {
return p5.getSocksStr(ProtoSOCKS4)
}
// Socks4aStr gets a SOCKS4 proxy that we have fully verified.
// Will block if one is not available!
func (p5 *ProxyEngine) Socks4aStr() string {
return p5.getSocksStr(ProtoSOCKS4a)
}
// GetHTTPTunnel checks for an available HTTP CONNECT proxy in our pool.
func (p5 *ProxyEngine) GetHTTPTunnel() string {
return p5.getSocksStr(ProtoHTTP)
}
// GetAnySOCKS retrieves any version SOCKS proxy as a Proxy type
// Will block if one is not available!
func (p5 *ProxyEngine) GetAnySOCKS() *Proxy {
defer p5.stats.dispense()
for {
var sock *Proxy
select {
case <-p5.ctx.Done():
return nil
default:
time.Sleep(2 * time.Millisecond)
}
for _, list := range p5.Valids.Slice() {
list.RLock()
if list.Len() < 1 {
time.Sleep(15 * time.Millisecond)
list.RUnlock()
continue
}
list.RUnlock()
sock = list.pop()
switch {
case sock == nil:
p5.recycling()
time.Sleep(50 * time.Millisecond)
case p5.stillGood(sock):
return sock
default:
}
continue
}
}
}
func (p5 *ProxyEngine) stillGood(sock *Proxy) bool {
if sock == nil {
return false
}
if !atomic.CompareAndSwapUint32(&sock.lock, stateUnlocked, stateLocked) {
return false
}
defer atomic.StoreUint32(&sock.lock, stateUnlocked)
if p5.GetRemoveAfter() != -1 && atomic.LoadInt64(&sock.timesBad) > int64(p5.GetRemoveAfter()) {
buf := strs.Get()
buf.MustWriteString("deleting from map (too many failures): ")
buf.MustWriteString(sock.Endpoint)
p5.dbgPrint(buf)
if err := p5.proxyMap.delete(sock.Endpoint); err != nil {
p5.dbgPrint(simpleString(err.Error()))
}
return false
}
if p5.badProx.Peek(sock) {
p5.msgBadProxRate(sock)
return false
}
if time.Since(sock.lastValidated) > p5.opt.stale {
buf := strs.Get()
buf.MustWriteString("proxy stale: ")
buf.MustWriteString(sock.Endpoint)
p5.dbgPrint(buf)
p5.stats.stale()
return false
}
return true
}