prox5/mystery_dialer.go

187 lines
4.7 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"
"io"
"net"
2023-10-28 07:23:53 +00:00
"os"
"sync/atomic"
"time"
"git.tcp.direct/kayos/socks"
)
2022-07-09 18:36:45 +00:00
// DialContext is a simple stub adapter to implement a net.Dialer.
2022-10-16 10:53:04 +00:00
func (p5 *ProxyEngine) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
2022-12-27 12:55:03 +00:00
return p5.mysteryDialer(ctx, network, addr)
2021-09-28 06:25:00 +00:00
}
// Dial is a simple stub adapter to implement a net.Dialer.
2022-10-16 10:53:04 +00:00
func (p5 *ProxyEngine) Dial(network, addr string) (net.Conn, error) {
2022-12-27 12:55:03 +00:00
return p5.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-10-16 10:53:04 +00:00
func (p5 *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))
2023-02-02 05:37:59 +00:00
defer cancel()
2023-01-31 09:21:29 +00:00
nc, err := p5.mysteryDialer(ctx, network, addr)
return nc, err
2021-09-28 06:25:00 +00:00
}
2022-10-16 10:53:04 +00:00
func (p5 *ProxyEngine) addTimeout(socksString string) string {
tout := strs.Get()
tout.MustWriteString(socksString)
tout.MustWriteString("?timeout=")
tout.MustWriteString(p5.GetServerTimeoutStr())
_, _ = tout.WriteRune('s')
2022-07-25 07:14:26 +00:00
socksString = tout.String()
strs.MustPut(tout)
2022-07-25 07:14:26 +00:00
return socksString
}
func (p5 *ProxyEngine) isEmpty() bool {
if p5.GetStatistics().Checked.Load() == 0 {
return true
}
// if stats.Valid5.Load()+stats.Valid4.Load()+stats.Valid4a.Load()+stats.ValidHTTP.Load() == 0 {
if p5.GetTotalValidated() == 0 {
return true
}
return false
}
var ErrNoProxies = fmt.Errorf("no proxies available")
2022-10-16 10:53:04 +00:00
func (p5 *ProxyEngine) popSockAndLockIt(ctx context.Context) (*Proxy, error) {
if p5.isEmpty() {
p5.scale()
return nil, ErrNoProxies
}
2022-09-22 23:48:08 +00:00
sock := p5.GetAnySOCKS()
2022-07-25 07:14:26 +00:00
select {
case <-ctx.Done():
return nil, fmt.Errorf("context done: %w", ctx.Err())
default:
2023-01-31 09:21:29 +00:00
//
2022-07-25 07:14:26 +00:00
}
2023-01-31 09:21:29 +00:00
if sock == nil {
return nil, nil
}
if atomic.CompareAndSwapUint32(&sock.lock, stateUnlocked, stateLocked) {
// p5.msgGotLock(socksString)
return sock, nil
}
switch sock.GetProto() {
case ProtoSOCKS5:
p5.Valids.SOCKS5.add(sock)
case ProtoSOCKS4:
p5.Valids.SOCKS4.add(sock)
case ProtoSOCKS4a:
p5.Valids.SOCKS4a.add(sock)
case ProtoHTTP:
p5.Valids.HTTP.add(sock)
default:
return nil, fmt.Errorf("unknown protocol: %s", sock.GetProto())
}
return nil, nil
}
func (p5 *ProxyEngine) announceDial(network, addr string) {
s := strs.Get()
s.MustWriteString("prox5 dialing: ")
s.MustWriteString(network)
s.MustWriteString("://")
if p5.opt.redact {
s.MustWriteString("[redacted]")
} else {
s.MustWriteString(addr)
}
s.MustWriteString(addr)
s.MustWriteString("...")
p5.dbgPrint(s)
}
2022-12-27 12:55:03 +00:00
// mysteryDialer is a dialer function that will use a different proxy for every request.
// If you're looking for this function, it has been unexported. Use Dial, DialTimeout, or DialContext instead.
func (p5 *ProxyEngine) mysteryDialer(ctx context.Context, network, addr string) (net.Conn, error) {
p5.announceDial(network, addr)
if p5.isEmpty() {
// p5.dbgPrint(simpleString("prox5: no proxies available"))
return nil, ErrNoProxies
}
2023-10-28 07:23:53 +00:00
timeout := time.NewTimer(p5.GetServerTimeout())
defer timeout.Stop()
// pull down proxies from channel until we get a proxy good enough for our spoiled asses
var count = 0
for {
2023-09-27 05:12:53 +00:00
maxBail := p5.GetDialerBailout()
2023-01-31 09:21:29 +00:00
switch {
2023-09-27 05:12:53 +00:00
case count > maxBail:
return nil, fmt.Errorf("giving up after %d tries", maxBail)
2023-01-31 09:21:29 +00:00
case ctx.Err() != nil:
return nil, fmt.Errorf("context error: %w", ctx.Err())
default:
select {
case <-ctx.Done():
return nil, fmt.Errorf("context done: %w", ctx.Err())
case <-p5.ctx.Done():
return nil, fmt.Errorf("prox5 closed: %w", p5.ctx.Err())
case <-p5.conKiller:
return nil, fmt.Errorf("prox5 closed: %w", io.ErrClosedPipe)
2023-10-28 07:23:53 +00:00
case <-timeout.C:
return nil, fmt.Errorf("timeout: %w, %w", io.ErrClosedPipe, os.ErrDeadlineExceeded)
default:
}
2022-10-16 10:53:04 +00:00
}
var sock *Proxy
for {
2022-12-27 15:25:10 +00:00
if p5.scale() {
2023-01-31 09:21:29 +00:00
time.Sleep(5 * time.Millisecond)
2022-12-27 15:25:10 +00:00
}
2022-07-25 07:14:26 +00:00
var err error
2022-09-22 23:45:15 +00:00
sock, err = p5.popSockAndLockIt(ctx)
2022-07-25 07:14:26 +00:00
if err != nil {
2023-01-31 10:13:36 +00:00
// println(err.Error())
2022-07-25 07:14:26 +00:00
return nil, err
}
if sock != nil {
break
}
}
socksString := sock.String()
var ok bool
2022-09-22 23:45:15 +00:00
if sock, ok = p5.dispenseMiddleware(sock); !ok {
atomic.StoreUint32(&sock.lock, stateUnlocked)
2022-09-22 23:45:15 +00:00
p5.msgFailedMiddleware(socksString)
continue
}
2022-09-22 23:45:15 +00:00
p5.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-09-22 23:45:15 +00:00
p5.msgUnableToReach(socksString, addr, err)
2021-09-24 16:38:57 +00:00
continue
}
2022-09-22 23:45:15 +00:00
p5.msgUsingProxy(socksString)
2023-01-31 10:13:36 +00:00
go func() {
select {
case <-ctx.Done():
_ = conn.Close()
case <-p5.conKiller:
2023-01-31 10:13:36 +00:00
_ = conn.Close()
case <-p5.ctx.Done():
_ = conn.Close()
}
}()
return conn, nil
}
}