2021-09-12 02:59:05 +00:00
package pxndscvm
import (
"bufio"
"bytes"
"crypto/tls"
2021-09-12 04:55:36 +00:00
"errors"
2021-09-13 09:37:59 +00:00
"io"
2021-09-12 02:59:05 +00:00
"io/ioutil"
"net"
"net/http"
"os"
2021-09-13 17:40:23 +00:00
"strconv"
2021-09-12 02:59:05 +00:00
"time"
"golang.org/x/net/proxy"
"h12.io/socks"
)
2021-09-13 09:37:59 +00:00
const (
grn = "\033[32m"
2021-09-13 11:26:18 +00:00
ylw = "\033[33m"
2021-09-13 09:37:59 +00:00
rst = "\033[0m"
)
2021-09-12 04:55:36 +00:00
// LoadProxyTXT loads proxies from a given seed file and randomly feeds them to the workers.
2021-09-13 14:23:44 +00:00
// The first call to this function will start all background pool operations, essentially initializing the proxy pool.
// Additional calls will add more proxies to the pool to be validated.
2021-09-13 09:37:59 +00:00
func ( s * Swamp ) LoadProxyTXT ( seedFile string ) error {
s . dbgPrint ( "LoadProxyTXT start" )
2021-09-12 02:59:05 +00:00
f , err := os . Open ( seedFile )
if err != nil {
2021-09-13 09:37:59 +00:00
return err
2021-09-12 02:59:05 +00:00
}
scan := bufio . NewScanner ( f )
2021-09-13 15:17:20 +00:00
2021-09-13 10:08:04 +00:00
if ! s . started {
go s . tossUp ( )
}
2021-09-13 15:17:20 +00:00
2021-09-12 02:59:05 +00:00
for scan . Scan ( ) {
2021-09-12 04:55:36 +00:00
s . scvm = append ( s . scvm , scan . Text ( ) )
2021-09-12 02:59:05 +00:00
}
2021-09-13 10:08:04 +00:00
if ! s . started {
go s . feed ( )
}
s . started = true
2021-09-13 15:17:20 +00:00
2021-09-13 09:37:59 +00:00
if err := f . Close ( ) ; err != nil {
s . dbgPrint ( err . Error ( ) )
return err
}
return nil
}
func ( s * Swamp ) feed ( ) {
s . dbgPrint ( "swamp feed start" )
2021-09-12 02:59:05 +00:00
for {
2021-09-13 19:08:41 +00:00
if s . Status == Paused {
return
}
2021-09-12 02:59:05 +00:00
select {
2021-09-13 15:12:02 +00:00
case s . Pending <- randStrChoice ( s . scvm ) :
2021-09-12 02:59:05 +00:00
//
2021-09-13 19:08:41 +00:00
case <- s . quit :
s . dbgPrint ( "feed() paused" )
return
2021-09-12 02:59:05 +00:00
default :
time . Sleep ( 1 * time . Second )
}
}
}
2021-09-13 11:26:18 +00:00
func ( s * Swamp ) checkHTTP ( sock Proxy ) ( string , error ) {
2021-09-13 15:36:30 +00:00
req , err := http . NewRequest ( "GET" , s . GetRandomEndpoint ( ) , bytes . NewBuffer ( [ ] byte ( "" ) ) )
2021-09-12 02:59:05 +00:00
if err != nil {
return "" , err
}
headers := make ( map [ string ] string )
2021-09-13 11:26:18 +00:00
headers [ "User-Agent" ] = s . RandomUserAgent ( )
2021-09-12 02:59:05 +00:00
headers [ "Accept" ] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
headers [ "Accept-Language" ] = "en-US,en;q=0.5"
headers [ "'Accept-Encoding'" ] = "gzip, deflate, br"
headers [ "Connection" ] = "keep-alive"
for header , value := range headers {
req . Header . Set ( header , value )
}
2021-09-13 17:40:23 +00:00
var dialSocks = socks . Dial ( "socks" + sock . Proto + "://" +
sock . Endpoint + "?timeout=" + strconv . Itoa ( s . GetValidationTimeout ( ) ) + "s" )
2021-09-12 02:59:05 +00:00
var client * http . Client
2021-09-13 08:30:49 +00:00
if sock . Proto == "none" {
2021-09-12 02:59:05 +00:00
//goland:noinspection GoDeprecation
client = & http . Client {
Transport : & http . Transport {
Dial : proxy . Direct . Dial ,
DisableKeepAlives : true ,
TLSClientConfig : & tls . Config { InsecureSkipVerify : true } ,
2021-09-13 17:40:23 +00:00
TLSHandshakeTimeout : time . Duration ( s . GetValidationTimeout ( ) ) * time . Second ,
2021-09-12 02:59:05 +00:00
} ,
}
} else {
//goland:noinspection GoDeprecation
client = & http . Client {
Transport : & http . Transport {
Dial : dialSocks ,
DisableKeepAlives : true ,
TLSClientConfig : & tls . Config { InsecureSkipVerify : true } ,
2021-09-13 17:40:23 +00:00
TLSHandshakeTimeout : time . Duration ( s . GetValidationTimeout ( ) ) * time . Second ,
2021-09-12 02:59:05 +00:00
} ,
}
}
resp , err := client . Do ( req )
if err != nil {
return "" , err
}
2021-09-13 09:37:59 +00:00
defer func ( Body io . ReadCloser ) {
err := Body . Close ( )
if err != nil {
panic ( err )
}
} ( resp . Body )
rbody , err := ioutil . ReadAll ( resp . Body )
return string ( rbody ) , err
2021-09-12 02:59:05 +00:00
}
2021-09-13 11:26:18 +00:00
func ( s * Swamp ) singleProxyCheck ( sock Proxy ) error {
2021-09-13 17:40:23 +00:00
if _ , err := net . DialTimeout ( "tcp" , sock . Endpoint , time . Duration ( s . GetValidationTimeout ( ) ) * time . Second ) ; err != nil {
2021-09-13 08:30:49 +00:00
badProx . Check ( sock )
2021-09-13 09:37:59 +00:00
return err
2021-09-12 02:59:05 +00:00
}
2021-09-13 19:08:41 +00:00
2021-09-13 11:26:18 +00:00
resp , err := s . checkHTTP ( sock )
2021-09-12 02:59:05 +00:00
if err != nil {
2021-09-13 08:30:49 +00:00
badProx . Check ( sock )
return err
2021-09-12 02:59:05 +00:00
}
2021-09-13 15:17:20 +00:00
2021-09-12 02:59:05 +00:00
if newip := net . ParseIP ( resp ) ; newip == nil {
2021-09-13 08:30:49 +00:00
badProx . Check ( sock )
2021-09-13 11:26:18 +00:00
return errors . New ( "bad response from http request: " + resp )
2021-09-12 02:59:05 +00:00
}
2021-09-13 09:37:59 +00:00
2021-09-13 11:26:18 +00:00
sock . ProxiedIP = resp
2021-09-13 08:30:49 +00:00
return nil
2021-09-12 02:59:05 +00:00
}
2021-09-13 19:08:41 +00:00
func ( s * Swamp ) validate ( ) {
2021-09-13 09:37:59 +00:00
var sversions = [ ] string { "5" , "4" , "4a" }
2021-09-12 02:59:05 +00:00
for {
2021-09-13 19:08:41 +00:00
if s . Status == Paused {
return
}
sock := <- s . Pending
p := Proxy {
Endpoint : sock ,
}
// ratelimited
if useProx . Check ( p ) {
// s.dbgPrint(blu+"useProx ratelimited: " + p.Endpoint+rst)
continue
}
// determined as bad, won't try again until it expires from that cache
if badProx . Peek ( p ) {
s . dbgPrint ( ylw + "badProx ratelimited: " + p . Endpoint + rst )
continue
}
// try to use the proxy with all 3 SOCKS versions
var good = false
for _ , sver := range sversions {
if s . Status == Paused {
return
}
p . Proto = sver
if err := s . singleProxyCheck ( p ) ; err == nil {
s . dbgPrint ( grn + "verified " + p . Endpoint + " as SOCKS" + sver + rst )
good = true
break
2021-09-12 02:59:05 +00:00
}
2021-09-13 19:08:41 +00:00
}
if ! good {
s . dbgPrint ( ylw + "failed to verify " + p . Endpoint + rst )
badProx . Check ( p )
continue
}
p . Verified = time . Now ( )
switch p . Proto {
case "4" :
s . Stats . v4 ( )
s . Socks4 <- p
case "4a" :
s . Stats . v4a ( )
s . Socks4a <- p
case "5" :
s . Stats . v5 ( )
s . Socks5 <- p
}
2021-09-12 02:59:05 +00:00
}
}
2021-09-13 19:08:41 +00:00
func ( s * Swamp ) tossUp ( ) {
s . dbgPrint ( "tossUp() proxy checking loop start" )
for {
if s . Status == Paused {
return
}
select {
case <- s . quit :
s . dbgPrint ( "tossUp() paused" )
return
default :
go s . pool . Submit ( s . validate )
time . Sleep ( time . Duration ( 10 ) * time . Millisecond )
}
}
}
// Pause will cease all proxy pool operation. You will be able to start the proxy pool again, it will have the same Statistics, options, and ratelimits.
// Options may be changed and proxy lists may be loaded when paused.
// NOTE: There will be a few leftover validation attemps after pause, but no new jobs will be added.
func ( s * Swamp ) Pause ( ) {
s . mu . Lock ( )
for n := 2 ; n > 0 ; n -- {
s . quit <- true
}
s . Status = Paused
}
// Resume will resume pause proxy pool operations, must be called after Pause or it will block.
func ( s * Swamp ) Resume ( ) {
s . mu . Unlock ( )
s . Status = Running
go s . feed ( )
go s . tossUp ( )
}