diff --git a/conductor.go b/conductor.go index ae400a7..af5f04a 100644 --- a/conductor.go +++ b/conductor.go @@ -3,6 +3,7 @@ package prox5 import ( "context" "errors" + "strings" "sync/atomic" ) @@ -39,7 +40,9 @@ func (pe *ProxyEngine) Pause() error { return errors.New("not running") } - pe.dbgPrint("pausing...") + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("pausing...") + pe.dbgPrint(buf) pe.quit() diff --git a/daemons.go b/daemons.go index 24742dd..94ba182 100644 --- a/daemons.go +++ b/daemons.go @@ -3,6 +3,7 @@ package prox5 import ( "errors" "strconv" + "strings" "sync" "sync/atomic" "time" @@ -76,10 +77,10 @@ func (pe *ProxyEngine) mapBuilder() { pe.pool.Reboot() } - pe.dbgPrint("map builder started") + pe.dbgPrint(simpleString("map builder started")) go func() { - defer pe.dbgPrint("map builder paused") + defer pe.dbgPrint(simpleString("map builder paused")) for { select { case <-pe.ctx.Done(): @@ -128,8 +129,8 @@ func (pe *ProxyEngine) jobSpawner() { pe.pool.Reboot() } - pe.dbgPrint("job spawner started") - defer pe.dbgPrint("job spawner paused") + pe.dbgPrint(simpleString("job spawner started")) + defer pe.dbgPrint(simpleString("job spawner paused")) q := make(chan bool) @@ -142,12 +143,16 @@ func (pe *ProxyEngine) jobSpawner() { return case sock := <-pe.Pending: if err := pe.pool.Submit(sock.validate); err != nil { - pe.dbgPrint(err.Error()) + pe.dbgPrint(simpleString(err.Error())) } default: time.Sleep(25 * time.Millisecond) count := pe.recycling() - pe.dbgPrint("recycled " + strconv.Itoa(count) + " proxies from our map") + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("recycled ") + buf.WriteString(strconv.Itoa(count)) + buf.WriteString(" proxies from our map") + pe.dbgPrint(buf) } } }() diff --git a/debug.go b/debug.go index cddda57..5431886 100644 --- a/debug.go +++ b/debug.go @@ -2,6 +2,7 @@ package prox5 import ( "fmt" + "strings" "sync" ) @@ -63,18 +64,29 @@ func (pe *ProxyEngine) DisableDebug() { pe.swampopt.debug = false } -func (pe *ProxyEngine) dbgPrint(str string) { +func simpleString(s string) *strings.Builder { + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString(s) + return buf +} + +func (pe *ProxyEngine) dbgPrint(builder *strings.Builder) { + defer discardBuffer(builder) if !pe.swampopt.debug { return } if !useDebugChannel { - pe.Debug.Print(str) + pe.Debug.Print(builder.String()) return } select { - case debugChan <- str: + case debugChan <- builder.String(): return default: - pe.Debug.Print("overflow: " + str) + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("overflow: ") + buf.WriteString(builder.String()) + pe.Debug.Print(buf.String()) + discardBuffer(buf) } } diff --git a/defs.go b/defs.go index 43d1304..f0d8ea5 100644 --- a/defs.go +++ b/defs.go @@ -2,6 +2,7 @@ package prox5 import ( "context" + "strings" "sync" "sync/atomic" "time" @@ -190,7 +191,10 @@ func NewProxyEngine() *ProxyEngine { })) if err != nil { - pe.dbgPrint("CRITICAL: " + err.Error()) + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("CRITICAL: ") + buf.WriteString(err.Error()) + pe.dbgPrint(buf) panic(err) } diff --git a/dispense.go b/dispense.go index 3c95657..67e76b6 100644 --- a/dispense.go +++ b/dispense.go @@ -1,6 +1,7 @@ package prox5 import ( + "strings" "sync/atomic" "time" @@ -103,19 +104,28 @@ func (pe *ProxyEngine) stillGood(sock *Proxy) bool { defer atomic.StoreUint32(&sock.lock, stateUnlocked) if atomic.LoadInt64(&sock.timesBad) > int64(pe.GetRemoveAfter()) && pe.GetRemoveAfter() != -1 { - pe.dbgPrint("deleting from map (too many failures): " + sock.Endpoint) + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("deleting from map (too many failures): ") + buf.WriteString(sock.Endpoint) + pe.dbgPrint(buf) if err := pe.swampmap.delete(sock.Endpoint); err != nil { - pe.dbgPrint(err.Error()) + pe.dbgPrint(simpleString(err.Error())) } } if pe.badProx.Peek(sock) { - pe.dbgPrint("badProx dial ratelimited: " + sock.Endpoint) + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("badProx dial ratelimited: ") + buf.WriteString(sock.Endpoint) + pe.dbgPrint(buf) return false } if time.Since(sock.lastValidated) > pe.swampopt.stale { - pe.dbgPrint("proxy stale: " + sock.Endpoint) + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("proxy stale: ") + buf.WriteString(sock.Endpoint) + pe.dbgPrint(buf) go pe.stats.stale() return false } diff --git a/example/go.mod b/example/go.mod new file mode 100644 index 0000000..daf6ca3 --- /dev/null +++ b/example/go.mod @@ -0,0 +1,29 @@ +module p5example + +go 1.18 + +require ( + git.tcp.direct/kayos/prox5 v0.5.3-0.20220709184754-7ecedfdd0c63 + github.com/haxii/socks5 v1.0.0 + github.com/mattn/go-tty v0.0.4 +) + +require ( + git.tcp.direct/kayos/common v0.7.0 // indirect + git.tcp.direct/kayos/go-socks5 v1.0.1 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/miekg/dns v1.1.50 // indirect + github.com/panjf2000/ants/v2 v2.5.0 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/yunginnanet/Rate5 v1.0.1 // indirect + go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect + go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect + golang.org/x/mod v0.4.2 // indirect + golang.org/x/net v0.0.0-20220630215102-69896b714898 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + h12.io/socks v1.0.3 // indirect + inet.af/netaddr v0.0.0-20220617031823-097006376321 // indirect + nullprogram.com/x/rng v1.1.0 // indirect +) diff --git a/example/go.sum b/example/go.sum new file mode 100644 index 0000000..40b7063 --- /dev/null +++ b/example/go.sum @@ -0,0 +1,90 @@ +git.tcp.direct/kayos/common v0.7.0 h1:KZDwoCzUiwQaYSWESr080N8wUVyLD27QYgzXgc7LiAQ= +git.tcp.direct/kayos/common v0.7.0/go.mod h1:7tMZBVNPLFSZk+JXTA6pgXWpf/XHqYRfT7Q3OziI++Y= +git.tcp.direct/kayos/go-socks5 v1.0.1 h1:Pe9PlSXofibIJyWkrr9rwWcgyfUxSdUcDCQ//6fAi0U= +git.tcp.direct/kayos/go-socks5 v1.0.1/go.mod h1:I9xU/uzFAZKukMJgEgWPrfC6rDlcPQe8wXMibF3qvhE= +git.tcp.direct/kayos/prox5 v0.5.3-0.20220709184754-7ecedfdd0c63 h1:eidAXI7o5gIf4linhOIFEtSiYQQOsqHLcMqhBrF9vgA= +git.tcp.direct/kayos/prox5 v0.5.3-0.20220709184754-7ecedfdd0c63/go.mod h1:/44/UlcKPOXZ/jr+NdftkZvLkW0k4QokPXqAdQ2O3BQ= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= +github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI= +github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c= +github.com/haxii/socks5 v1.0.0 h1:78BIzd4lHibdRNOKdMwKCnnsgYLW9SeotqU+nMhWSSo= +github.com/haxii/socks5 v1.0.0/go.mod h1:6O9Ba2yrLlvuSe/L1e84eZI8cPw6H+q1Ilr4hjgm4uY= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-tty v0.0.4 h1:NVikla9X8MN0SQAqCYzpGyXv0jY7MNl3HOWD2dkle7E= +github.com/mattn/go-tty v0.0.4/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3pxse28= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/panjf2000/ants/v2 v2.5.0 h1:1rWGWSnxCsQBga+nQbA4/iY6VMeNoOIAM0ZWh9u3q2Q= +github.com/panjf2000/ants/v2 v2.5.0/go.mod h1:cU93usDlihJZ5CfRGNDYsiBYvoilLvBF5Qp/BT2GNRE= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yunginnanet/Rate5 v1.0.1 h1:OePwNrj9/A/flmhyr/gKI5RYgrW6d1oWCqrZRXUev3k= +github.com/yunginnanet/Rate5 v1.0.1/go.mod h1:f0r66kVQZojRqUgVdLC/CKexMlF0nUDAmd01tBeF4Ms= +go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= +go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220630215102-69896b714898 h1:K7wO6V1IrczY9QOQ2WkVpw4JQSwCd52UsxVEirZUfiw= +golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0= +golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo= +h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck= +inet.af/netaddr v0.0.0-20220617031823-097006376321 h1:B4dC8ySKTQXasnjDTMsoCMf1sQG4WsMej0WXaHxunmU= +inet.af/netaddr v0.0.0-20220617031823-097006376321/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k= +nullprogram.com/x/rng v1.1.0 h1:SMU7DHaQSWtKJNTpNFIFt8Wd/KSmOuSDPXrMFp/UMro= +nullprogram.com/x/rng v1.1.0/go.mod h1:glGw6V87vyfawxCzqOABL3WfL95G65az9Z2JZCylCkg= diff --git a/example/main.go b/example/main.go index fcf0fb2..88f4d16 100644 --- a/example/main.go +++ b/example/main.go @@ -6,20 +6,47 @@ import ( "strconv" "time" - "github.com/mattn/go-tty" - "git.tcp.direct/kayos/prox5" + "github.com/haxii/socks5" + "github.com/mattn/go-tty" ) -var swamp *prox5.ProxyEngine -var quit chan bool -var t *tty.TTY +var ( + swamp *prox5.ProxyEngine + quit chan bool + t *tty.TTY +) + +type socksLogger struct{} + +var socklog = socksLogger{} + +// Printf is used to handle socks server logging. +func (s socksLogger) Printf(format string, a ...interface{}) { + println(fmt.Sprintf(format, a)) +} + +func StartUpstreamProxy(listen string) { + conf := &socks5.Config{Dial: swamp.DialContext, Logger: socklog} + server, err := socks5.New(conf) + if err != nil { + println(err.Error()) + return + } + + socklog.Printf("starting proxy server on %s", listen) + if err := server.ListenAndServe("tcp", listen); err != nil { + println(err.Error()) + return + } +} func init() { quit = make(chan bool) swamp = prox5.NewProxyEngine() swamp.SetMaxWorkers(5) swamp.EnableDebug() + go StartUpstreamProxy("127.0.0.1:1555") count := swamp.LoadProxyTXT(os.Args[1]) if count < 1 { diff --git a/getters.go b/getters.go index da126b5..6a8ea9e 100644 --- a/getters.go +++ b/getters.go @@ -2,6 +2,7 @@ package prox5 import ( "strconv" + "strings" "sync/atomic" "time" @@ -13,6 +14,30 @@ func (sock *Proxy) GetProto() ProxyProtocol { return sock.proto } +// GetProto safely retrieves the protocol value of the Proxy. +func (sock *Proxy) String() string { + tout := "" + if sock.parent.GetServerTimeoutStr() != "-1" { + tbuf := copABuffer.Get().(*strings.Builder) + tbuf.WriteString("?timeout=") + tbuf.WriteString(sock.parent.GetServerTimeoutStr()) + tbuf.WriteString("s") + tout = tbuf.String() + discardBuffer(tbuf) + } + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("socks") + buf.WriteString(getProtoStr(sock.GetProto())) + buf.WriteString("://") + buf.WriteString(sock.Endpoint) + if tout != "" { + buf.WriteString(tout) + } + out := buf.String() + discardBuffer(buf) + return out +} + // GetStatistics returns all current statistics. // * This is a pointer, do not modify it! func (pe *ProxyEngine) GetStatistics() *statistics { diff --git a/go.mod b/go.mod index a9b8409..f130b3f 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,8 @@ module git.tcp.direct/kayos/prox5 go 1.18 require ( - git.tcp.direct/kayos/common v0.6.0 + git.tcp.direct/kayos/common v0.7.0 git.tcp.direct/kayos/go-socks5 v1.0.1 - github.com/mattn/go-tty v0.0.4 github.com/miekg/dns v1.1.50 github.com/panjf2000/ants/v2 v2.5.0 github.com/yunginnanet/Rate5 v1.0.1 @@ -14,13 +13,12 @@ require ( ) require ( - github.com/mattn/go-isatty v0.0.10 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect golang.org/x/mod v0.4.2 // indirect golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 // indirect - golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect nullprogram.com/x/rng v1.1.0 // indirect diff --git a/go.sum b/go.sum index d5b2951..e5afcd9 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,11 @@ -git.tcp.direct/kayos/common v0.6.0 h1:x3qXJxGalhv3y4bX191Lx8R73Se1UzPgAP3fL2gqhyA= -git.tcp.direct/kayos/common v0.6.0/go.mod h1:jG1yXbN+5PrRZwGe32qIGWgLC4x5JWdyNBbMj1gIWB0= +git.tcp.direct/kayos/common v0.7.0 h1:KZDwoCzUiwQaYSWESr080N8wUVyLD27QYgzXgc7LiAQ= +git.tcp.direct/kayos/common v0.7.0/go.mod h1:7tMZBVNPLFSZk+JXTA6pgXWpf/XHqYRfT7Q3OziI++Y= git.tcp.direct/kayos/go-socks5 v1.0.1 h1:Pe9PlSXofibIJyWkrr9rwWcgyfUxSdUcDCQ//6fAi0U= git.tcp.direct/kayos/go-socks5 v1.0.1/go.mod h1:I9xU/uzFAZKukMJgEgWPrfC6rDlcPQe8wXMibF3qvhE= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI= github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-tty v0.0.4 h1:NVikla9X8MN0SQAqCYzpGyXv0jY7MNl3HOWD2dkle7E= -github.com/mattn/go-tty v0.0.4/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3pxse28= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/panjf2000/ants/v2 v2.5.0 h1:1rWGWSnxCsQBga+nQbA4/iY6VMeNoOIAM0ZWh9u3q2Q= @@ -49,10 +42,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -60,8 +50,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/list_management.go b/list_management.go index 731dc55..78cd5db 100644 --- a/list_management.go +++ b/list_management.go @@ -96,19 +96,19 @@ func (pe *ProxyEngine) filter(in string) (filtered string, ok bool) { func (pe *ProxyEngine) LoadProxyTXT(seedFile string) (count int) { f, err := os.Open(seedFile) if err != nil { - pe.dbgPrint(err.Error()) + pe.dbgPrint(simpleString(err.Error())) return 0 } defer func() { if err := f.Close(); err != nil { - pe.dbgPrint(err.Error()) + pe.dbgPrint(simpleString(err.Error())) } }() bs, err := io.ReadAll(f) if err != nil { - pe.dbgPrint(err.Error()) + pe.dbgPrint(simpleString(err.Error())) return 0 } sockstr := string(bs) diff --git a/mystery_dialer.go b/mystery_dialer.go index 74f0916..127eb27 100644 --- a/mystery_dialer.go +++ b/mystery_dialer.go @@ -2,22 +2,43 @@ package prox5 import ( "context" - "errors" + "fmt" "net" - "strconv" + "strings" + "sync" "sync/atomic" + "time" "h12.io/socks" ) -// DialContext is a simple stub adapter to implement a net.Dialer. -func (s *ProxyEngine) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { - return s.MysteryDialer(ctx, network, addr) +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 (s *ProxyEngine) Dial(network, addr string) (net.Conn, error) { - return s.DialContext(context.Background(), network, addr) +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. @@ -27,31 +48,60 @@ func (pe *ProxyEngine) MysteryDialer(ctx context.Context, network, addr 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, errors.New("giving up after " + strconv.Itoa(max) + " tries") + return nil, fmt.Errorf("giving up after %d tries", max) } if err := ctx.Err(); err != nil { - return nil, err + return nil, fmt.Errorf("context error: %v", err) } var sock *Proxy + popSockAndLockIt: for { sock = pe.GetAnySOCKS(false) - if !atomic.CompareAndSwapUint32(&sock.lock, stateUnlocked, stateLocked) { - continue + 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 + } } - break } - pe.dbgPrint("dialer trying: " + sock.Endpoint + "...") - tout := "" + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("try dial with: ") + buf.WriteString(sock.Endpoint) + pe.dbgPrint(buf) if pe.GetServerTimeoutStr() != "-1" { - tout = "?timeout=" + pe.GetServerTimeoutStr() + "s" + tout := copABuffer.Get().(*strings.Builder) + tout.WriteString("?timeout=") + tout.WriteString(pe.GetServerTimeoutStr()) + tout.WriteRune('s') } - socksString = "socks" + getProtoStr(sock.proto) + "://" + sock.Endpoint + tout var ok bool if sock, ok = pe.dispenseMiddleware(sock); !ok { - pe.dbgPrint("failed middleware check, " + socksString + ", cycling...") + 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) @@ -59,10 +109,17 @@ func (pe *ProxyEngine) MysteryDialer(ctx context.Context, network, addr string) conn, err := dialSocks(network, addr) if err != nil { count++ - pe.dbgPrint("unable to reach [redacted] with " + socksString + ", cycling...") + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("unable to reach [redacted] with ") + buf.WriteString(socksString) + buf.WriteString(", cycling...") + pe.dbgPrint(buf) continue } - pe.dbgPrint("MysteryDialer using socks: " + socksString) + buf = copABuffer.Get().(*strings.Builder) + buf.WriteString("MysteryDialer using socks: ") + buf.WriteString(socksString) + pe.dbgPrint(buf) return conn, nil } } diff --git a/socks5_server.go b/socks5_server.go index 66d062b..485933c 100644 --- a/socks5_server.go +++ b/socks5_server.go @@ -2,8 +2,8 @@ package prox5 import ( "fmt" - "git.tcp.direct/kayos/go-socks5" + "strings" ) type socksLogger struct { @@ -12,7 +12,9 @@ type socksLogger struct { // Printf is used to handle socks server logging. func (s socksLogger) Printf(format string, a ...interface{}) { - s.parent.dbgPrint(fmt.Sprintf(format, a...)) + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString(fmt.Sprintf(format, a...)) + s.parent.dbgPrint(buf) } type socksCreds struct { @@ -40,7 +42,10 @@ func (pe *ProxyEngine) StartSOCKS5Server(listen, username, password string) erro Dial: pe.MysteryDialer, } - pe.dbgPrint("listening for SOCKS5 connections on " + listen) + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("listening for SOCKS5 connections on ") + buf.WriteString(listen) + pe.dbgPrint(buf) server, err := socks5.New(conf) if err != nil { diff --git a/validator_engine.go b/validator_engine.go index 00064ac..dd5f7b0 100644 --- a/validator_engine.go +++ b/validator_engine.go @@ -156,7 +156,10 @@ func (sock *Proxy) validate() { // determined as bad, won't try again until it expires from that cache if s.badProx.Peek(sock) { - s.dbgPrint("badProx ratelimited: " + sock.Endpoint) + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("badProx ratelimited: ") + buf.WriteString(sock.Endpoint) + s.dbgPrint(buf) return } @@ -179,10 +182,18 @@ func (sock *Proxy) validate() { switch sock.proto { case ProtoSOCKS4, ProtoSOCKS4a, ProtoSOCKS5, ProtoHTTP: - s.dbgPrint("verified " + sock.Endpoint + " as SOCKS" + getProtoStr(sock.proto)) + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("verified ") + buf.WriteString(sock.Endpoint) + buf.WriteString(" as SOCKS") + buf.WriteString(getProtoStr(sock.proto)) + s.dbgPrint(buf) break default: - s.dbgPrint("failed to verify: " + sock.Endpoint) + buf := copABuffer.Get().(*strings.Builder) + buf.WriteString("failed to verify: ") + buf.WriteString(sock.Endpoint) + s.dbgPrint(buf) sock.bad() s.badProx.Check(sock) return