prox5/mystery_dialer.go

123 lines
3.1 KiB
Go
Raw Normal View History

2022-05-23 01:05:50 +00:00
package prox5
import (
2021-09-14 01:50:39 +00:00
"context"
"fmt"
"net"
2022-07-25 06:23:12 +00:00
"strings"
"sync"
"sync/atomic"
"time"
"h12.io/socks"
)
2022-07-25 06:23:12 +00:00
var copABuffer = &sync.Pool{New: func() interface{} { return &strings.Builder{} }}
func discardBuffer(buf *strings.Builder) {
buf.Reset()
copABuffer.Put(buf)
2021-09-28 06:25:00 +00:00
}
2022-07-09 18:36:45 +00:00
// DialContext is a simple stub adapter to implement a net.Dialer.
2022-07-25 06:23:12 +00:00
func (pe *ProxyEngine) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
return pe.MysteryDialer(ctx, network, addr)
2021-09-28 06:25:00 +00:00
}
// Dial is a simple stub adapter to implement a net.Dialer.
2022-07-25 06:23:12 +00:00
func (pe *ProxyEngine) Dial(network, addr string) (net.Conn, error) {
return pe.MysteryDialer(context.Background(), network, addr)
2022-07-09 18:36:45 +00:00
}
// DialTimeout is a simple stub adapter to implement a net.Dialer with a timeout.
2022-07-25 06:23:12 +00:00
func (pe *ProxyEngine) DialTimeout(network, addr string, timeout time.Duration) (net.Conn, error) {
2022-07-15 08:36:54 +00:00
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(timeout))
2022-07-25 07:14:26 +00:00
go func() { // this is a goroutine that calls cancel() upon the deadline expiring to avoid context leaks
<-ctx.Done()
cancel()
2022-07-25 06:23:12 +00:00
}()
return pe.MysteryDialer(ctx, network, addr)
2021-09-28 06:25:00 +00:00
}
2022-07-25 07:14:26 +00:00
func (pe *ProxyEngine) addTimeout(socksString string) string {
tout := copABuffer.Get().(*strings.Builder)
tout.WriteString(socksString)
tout.WriteString("?timeout=")
tout.WriteString(pe.GetServerTimeoutStr())
tout.WriteRune('s')
socksString = tout.String()
discardBuffer(tout)
return socksString
}
func (pe *ProxyEngine) popSockAndLockIt(ctx context.Context) (*Proxy, error) {
sock := pe.GetAnySOCKS(false)
socksString := sock.String()
select {
case <-ctx.Done():
return nil, fmt.Errorf("context done: %w", ctx.Err())
default:
if atomic.CompareAndSwapUint32(&sock.lock, stateUnlocked, stateLocked) {
pe.msgGotLock(socksString)
return sock, nil
}
select {
case pe.Pending <- sock:
pe.msgCantGetLock(socksString, true)
return nil, nil
default:
pe.msgCantGetLock(socksString, false)
return nil, nil
}
}
}
// MysteryDialer is a dialer function that will use a different proxy for every request.
2022-06-26 02:51:42 +00:00
func (pe *ProxyEngine) MysteryDialer(ctx context.Context, network, addr string) (net.Conn, error) {
var (
socksString string
count int
)
// pull down proxies from channel until we get a proxy good enough for our spoiled asses
2022-07-25 06:23:12 +00:00
for {
2022-06-26 02:51:42 +00:00
max := pe.GetDialerBailout()
2021-09-29 08:31:19 +00:00
if count > max {
2022-07-25 06:23:12 +00:00
return nil, fmt.Errorf("giving up after %d tries", max)
2021-09-28 06:25:00 +00:00
}
if err := ctx.Err(); err != nil {
2022-07-25 07:14:26 +00:00
return nil, fmt.Errorf("context error: %w", err)
}
var sock *Proxy
for {
2022-07-25 07:14:26 +00:00
var err error
sock, err = pe.popSockAndLockIt(ctx)
if err != nil {
return nil, err
}
if sock != nil {
break
}
}
2022-06-26 02:51:42 +00:00
if pe.GetServerTimeoutStr() != "-1" {
2022-07-25 07:14:26 +00:00
socksString = pe.addTimeout(socksString)
}
var ok bool
2022-06-26 02:51:42 +00:00
if sock, ok = pe.dispenseMiddleware(sock); !ok {
2022-07-25 07:14:26 +00:00
pe.msgFailedMiddleware(socksString)
continue
2021-10-28 15:51:10 +00:00
}
2022-07-25 07:14:26 +00:00
pe.msgTry(socksString)
atomic.StoreUint32(&sock.lock, stateUnlocked)
2021-09-24 16:38:57 +00:00
dialSocks := socks.Dial(socksString)
conn, err := dialSocks(network, addr)
if err != nil {
2021-09-28 06:25:00 +00:00
count++
2022-07-25 07:14:26 +00:00
pe.msgUnableToReach(socksString)
2021-09-24 16:38:57 +00:00
continue
}
2022-07-25 07:14:26 +00:00
pe.msgUsingProxy(socksString)
return conn, nil
}
}