126 lines
3.4 KiB
Go
126 lines
3.4 KiB
Go
package prox5
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"h12.io/socks"
|
|
)
|
|
|
|
var copABuffer = &sync.Pool{New: func() interface{} { return &strings.Builder{} }}
|
|
|
|
func discardBuffer(buf *strings.Builder) {
|
|
buf.Reset()
|
|
copABuffer.Put(buf)
|
|
}
|
|
|
|
// DialContext is a simple stub adapter to implement a net.Dialer.
|
|
func (pe *ProxyEngine) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
return pe.MysteryDialer(ctx, network, addr)
|
|
}
|
|
|
|
// Dial is a simple stub adapter to implement a net.Dialer.
|
|
func (pe *ProxyEngine) Dial(network, addr string) (net.Conn, error) {
|
|
return pe.MysteryDialer(context.Background(), network, addr)
|
|
}
|
|
|
|
// DialTimeout is a simple stub adapter to implement a net.Dialer with a timeout.
|
|
func (pe *ProxyEngine) DialTimeout(network, addr string, timeout time.Duration) (net.Conn, error) {
|
|
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(timeout))
|
|
go func() {
|
|
select {
|
|
case <-ctx.Done():
|
|
cancel()
|
|
}
|
|
}()
|
|
return pe.MysteryDialer(ctx, network, addr)
|
|
}
|
|
|
|
// MysteryDialer is a dialer function that will use a different proxy for every request.
|
|
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
|
|
|
|
for {
|
|
max := pe.GetDialerBailout()
|
|
if count > max {
|
|
return nil, fmt.Errorf("giving up after %d tries", max)
|
|
}
|
|
if err := ctx.Err(); err != nil {
|
|
return nil, fmt.Errorf("context error: %v", err)
|
|
}
|
|
var sock *Proxy
|
|
popSockAndLockIt:
|
|
for {
|
|
sock = pe.GetAnySOCKS(false)
|
|
socksString = sock.String()
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, fmt.Errorf("context done: %v", ctx.Err())
|
|
default:
|
|
buf := copABuffer.Get().(*strings.Builder)
|
|
if atomic.CompareAndSwapUint32(&sock.lock, stateUnlocked, stateLocked) {
|
|
buf.WriteString("got lock for ")
|
|
buf.WriteString(socksString)
|
|
break popSockAndLockIt
|
|
}
|
|
select {
|
|
case pe.Pending <- sock:
|
|
buf.WriteString("can't get lock, putting back ")
|
|
buf.WriteString(socksString)
|
|
pe.dbgPrint(buf)
|
|
continue
|
|
default:
|
|
buf.WriteString("can't get lock, can't put back ")
|
|
buf.WriteString(socksString)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
buf := copABuffer.Get().(*strings.Builder)
|
|
buf.WriteString("try dial with: ")
|
|
buf.WriteString(sock.Endpoint)
|
|
pe.dbgPrint(buf)
|
|
if pe.GetServerTimeoutStr() != "-1" {
|
|
tout := copABuffer.Get().(*strings.Builder)
|
|
tout.WriteString("?timeout=")
|
|
tout.WriteString(pe.GetServerTimeoutStr())
|
|
tout.WriteRune('s')
|
|
}
|
|
var ok bool
|
|
if sock, ok = pe.dispenseMiddleware(sock); !ok {
|
|
buf := copABuffer.Get().(*strings.Builder)
|
|
buf.WriteString("failed middleware check, ")
|
|
buf.WriteString(sock.String())
|
|
buf.WriteString(", cycling...")
|
|
pe.dbgPrint(buf)
|
|
continue
|
|
}
|
|
atomic.StoreUint32(&sock.lock, stateUnlocked)
|
|
dialSocks := socks.Dial(socksString)
|
|
conn, err := dialSocks(network, addr)
|
|
if err != nil {
|
|
count++
|
|
buf := copABuffer.Get().(*strings.Builder)
|
|
buf.WriteString("unable to reach [redacted] with ")
|
|
buf.WriteString(socksString)
|
|
buf.WriteString(", cycling...")
|
|
pe.dbgPrint(buf)
|
|
continue
|
|
}
|
|
buf = copABuffer.Get().(*strings.Builder)
|
|
buf.WriteString("MysteryDialer using socks: ")
|
|
buf.WriteString(socksString)
|
|
pe.dbgPrint(buf)
|
|
return conn, nil
|
|
}
|
|
}
|