├── .github ├── .screenshot │ ├── old-output.png │ ├── proxy-check.png │ └── socks5.png └── workflows │ └── release.yml ├── Makefile ├── README.md ├── go.mod ├── go.sum └── proxy-check.go /.github/.screenshot/old-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmpx12/proxy-check/a63219eb3bb65c3cb1c8d8ae2edecf817359ab8a/.github/.screenshot/old-output.png -------------------------------------------------------------------------------- /.github/.screenshot/proxy-check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmpx12/proxy-check/a63219eb3bb65c3cb1c8d8ae2edecf817359ab8a/.github/.screenshot/proxy-check.png -------------------------------------------------------------------------------- /.github/.screenshot/socks5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmpx12/proxy-check/a63219eb3bb65c3cb1c8d8ae2edecf817359ab8a/.github/.screenshot/socks5.png -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release binaries 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | release-binaries: 9 | name: release binaries linux/windows/android 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | goos: [linux, windows, android] 14 | goarch: ["386", amd64, arm64] 15 | exclude: 16 | - goarch: 386 17 | goos: android 18 | - goarch: amd64 19 | goos: android 20 | - goarch: arm64 21 | goos: windows 22 | steps: 23 | - uses: actions/checkout@v2 24 | - uses: wangyoucao577/go-release-action@v1.48 25 | with: 26 | github_token: ${{ secrets.GITHUB_TOKEN }} 27 | goos: ${{ matrix.goos }} 28 | goarch: ${{ matrix.goarch }} 29 | goversion: "1.22.0" 30 | ldflags: "-w -s" 31 | md5sum: FALSE 32 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | go build -ldflags="-w -s" 3 | 4 | install: 5 | cp proxy-check /usr/bin/proxy-check 6 | 7 | termux-install: 8 | mv proxy-check /data/data/com.termux/files/usr/bin/proxy-check 9 | 10 | all: build install 11 | 12 | termux-all: build termux-install 13 | 14 | clean: 15 | rm -f proxy-check /usr/bin/proxy-check 16 | 17 | termux-clean: 18 | rm -f proxy-check /data/data/com.termux/files/usr/bin/proxy-check 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PROXY-CHECK 2 | 3 | Check if proxies are working (http, socks4 & socks5) 4 | 5 | ![](.github/.screenshot/proxy-check.png) 6 | 7 | ### usage: 8 | 9 | ``` 10 | -h, --help Show this help 11 | -s, --socks4 Test socks4 proxies 12 | -S, --socks5 Test socks5 proxies 13 | -H, --http Test http proxies 14 | -i, --ip Test all proto if no proto is specified in input file 15 | -r, --randomize-file Shuffle proxies files 16 | -t, --thread=NBR Number of threads 17 | -T, --timeout=SEC Set timeout (default 3 seconds) 18 | -u, --url=TARGET url to test proxies 19 | -f, --proxies-file=FILE File to check proxy 20 | -m, --max-valid=NBR Stop when NBR valid proxies are found 21 | -U, --proxies-url=URL url with proxies file 22 | -p, --dis-progressbar Disable progress bar 23 | -g, --github use github.com/mmpx12/proxy-list 24 | -o, --output=FILE File to write valid proxies 25 | -v, --version Print version and exit 26 | ``` 27 | 28 | Without **-i|--ip** flag local or remote file with proxy should be on format: proto://ip:port; If -i flag is set and no protocol is set for an ip, all protocols will be test. 29 | 30 | If you disable the progress bar `-p|--dis-progressbar` you will have the old ouptut: 31 | 32 | ![](.github/.screenshot/old-output.png) 33 | 34 | ### Examples: 35 | 36 | Only check socks5 proxies from "https://raw.githubusercontent.com/mmpx12/proxy-list/master/proxies.txt" and stop after founding 30 valid proxies. 37 | 38 | `proxy-check -r -m 30 --socks5 -o valid-socks5.txt -g` 39 | 40 | 41 | 42 | Check all proxies from "/path/to/proxy" to url "www.urltotest.me" with a timeout f 6 second. 43 | 44 | `proxy-check -u www.urltotest.me -T 6 /path/to/proxy` 45 | 46 | 47 | #### Warnings: 48 | 49 | By default this will check to "checkip.amazonaws.com" and some false negative might occurs (timeout, rate limit, flagged ip etc...). 50 | 51 | ### Install 52 | 53 | With one liner if **$GOROOT/bin/** is in **$PATH**: 54 | 55 | ```sh 56 | go install github.com/mmpx12/proxy-check@latest 57 | ``` 58 | 59 | or from source with: 60 | 61 | ```sh 62 | git clone https://github.com/mmpx12/proxy-check.git 63 | cd proxy-check 64 | make 65 | sudo make install 66 | # or 67 | sudo make all 68 | ``` 69 | 70 | for **termux** you can do: 71 | 72 | ```sh 73 | git clone https://github.com/mmpx12/proxy-check.git 74 | cd proxy-check 75 | make 76 | make termux-install 77 | # or 78 | make termux-all 79 | ``` 80 | 81 | 82 | There is also prebuild binaries [here](https://github.com/mmpx12/proxy-check/releases/latest). 83 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mmpx12/proxy-check 2 | 3 | go 1.22.0 4 | 5 | require ( 6 | github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 7 | github.com/mmpx12/optionparser v1.1.0 8 | github.com/schollz/progressbar/v3 v3.14.2 9 | ) 10 | 11 | require ( 12 | github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect 13 | github.com/mattn/go-isatty v0.0.20 // indirect 14 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect 15 | github.com/rivo/uniseg v0.4.7 // indirect 16 | golang.org/x/sys v0.17.0 // indirect 17 | golang.org/x/term v0.17.0 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= 2 | github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= 7 | github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= 8 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 9 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 10 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= 11 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= 12 | github.com/mmpx12/optionparser v1.1.0 h1:CgfC8WBDxkHOlg9myndDMezNiyXeMzVRDLWDRIjdlf8= 13 | github.com/mmpx12/optionparser v1.1.0/go.mod h1:1Ub9+E2fDinPCmAU2lCuJcXE8x0HCmkurDv+lcXgRd8= 14 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 15 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 16 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 17 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 18 | github.com/schollz/progressbar/v3 v3.14.2 h1:EducH6uNLIWsr560zSV1KrTeUb/wZGAHqyMFIEa99ks= 19 | github.com/schollz/progressbar/v3 v3.14.2/go.mod h1:aQAZQnhF4JGFtRJiw/eobaXpsqpVQAftEQ+hLGXaRc4= 20 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 21 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 22 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 23 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 24 | golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= 25 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 26 | golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= 27 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 28 | -------------------------------------------------------------------------------- /proxy-check.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "fmt" 7 | "math/rand" 8 | "net/http" 9 | URL "net/url" 10 | "os" 11 | "regexp" 12 | "strconv" 13 | "strings" 14 | "sync" 15 | "sync/atomic" 16 | "time" 17 | 18 | "github.com/k0kubun/go-ansi" 19 | "github.com/mmpx12/optionparser" 20 | "github.com/schollz/progressbar/v3" 21 | ) 22 | 23 | var ( 24 | Proxies []string 25 | mu = &sync.Mutex{} 26 | valid []string 27 | checkmax bool 28 | counter int32 29 | maxvalid int 30 | disableBar bool 31 | noProto bool 32 | delete bool 33 | file string 34 | version = "1.1.3" 35 | ) 36 | 37 | func ProxyTest(client *http.Client, proxy, urlTarget, timeout string) bool { 38 | timeouts, _ := strconv.Atoi(timeout) 39 | ctx, cncl := context.WithTimeout(context.Background(), time.Second*time.Duration(timeouts)) 40 | defer cncl() 41 | proxyURL, _ := URL.Parse(proxy) 42 | client = &http.Client{ 43 | Transport: &http.Transport{ 44 | Proxy: http.ProxyURL(proxyURL), 45 | }, 46 | } 47 | 48 | req, err := http.NewRequestWithContext(ctx, "GET", urlTarget, nil) 49 | req.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64)") 50 | resp, err := client.Do(req) 51 | 52 | if resp != nil { 53 | defer resp.Body.Close() 54 | } 55 | if err != nil { 56 | if disableBar { 57 | fmt.Println("\033[31m[X] ", proxy, "\033[0m") 58 | } 59 | return false 60 | } 61 | 62 | if resp.StatusCode != http.StatusOK { 63 | if disableBar { 64 | fmt.Println("\033[31m[X] ", proxy, "\033[0m") 65 | } 66 | return false 67 | } 68 | if disableBar { 69 | fmt.Println("\033[32m[√] ", proxy, "\033[0m") 70 | } 71 | mu.Lock() 72 | valid = append(valid, proxy) 73 | mu.Unlock() 74 | return true 75 | } 76 | 77 | func readLines(http, socks4, socks5, all bool) { 78 | file, err := os.Open(file) 79 | if err != nil { 80 | print(err.Error(), "\n") 81 | os.Exit(1) 82 | } 83 | defer file.Close() 84 | scanner := bufio.NewScanner(file) 85 | var proto = []string{"http://", "socks4://", "socks5://"} 86 | for scanner.Scan() { 87 | if strings.HasPrefix(scanner.Text(), "http://") { 88 | if http == true || all == true { 89 | Proxies = append(Proxies, scanner.Text()) 90 | } 91 | } else if strings.HasPrefix(scanner.Text(), "socks4://") { 92 | if socks4 == true || all == true { 93 | Proxies = append(Proxies, scanner.Text()) 94 | } 95 | } else if strings.HasPrefix(scanner.Text(), "socks5://") { 96 | if socks5 == true || all == true { 97 | Proxies = append(Proxies, scanner.Text()) 98 | } 99 | } else if noProto { 100 | var ipv4 = regexp.MustCompile(`^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$|:[0-9]{1,5})){4})`) 101 | if ipv4.MatchString(scanner.Text()) { 102 | proxy := scanner.Text() 103 | for _, i := range proto { 104 | Proxies = append(Proxies, i+proxy) 105 | } 106 | } 107 | } 108 | } 109 | } 110 | 111 | func writeResult(output, file string) { 112 | if output != "" { 113 | ff, _ := os.OpenFile(output, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666) 114 | defer ff.Close() 115 | for _, i := range valid { 116 | fmt.Fprintln(ff, i) 117 | } 118 | } 119 | 120 | if delete { 121 | os.Remove(file) 122 | } 123 | } 124 | 125 | func main() { 126 | var nologo, socks5, socks4, httpp, all, random, github, printversion bool 127 | var url, goroutine, timeout, urlfile, output, nbrvalid string 128 | op := optionparser.NewOptionParser() 129 | op.Banner = "Proxy tester\n\nUsage:\n" 130 | op.On("-s", "--socks4", "Test socks4 proxies", &socks4) 131 | op.On("-S", "--socks5", "Test socks5 proxies", &socks5) 132 | op.On("-H", "--http", "Test http proxies", &httpp) 133 | op.On("-r", "--randomize-file", "Shuffle proxies files", &random) 134 | op.On("-t", "--thread NBR", "Number of threads", &goroutine) 135 | op.On("-T", "--timeout SEC", "Set timeout (seconds)", &timeout) 136 | op.On("-u", "--url TARGET", "set URL for testing proxies", &url) 137 | op.On("-f", "--proxies-file FILE", "files with proxies (proto://ip:port)", &file) 138 | op.On("-m", "--max-valid NBR", "Stop when NBR valid proxies are found", &nbrvalid) 139 | op.On("-i", "--ip", "Test all proto if no proto is specified in input", &noProto) 140 | op.On("-U", "--proxies-url URL", "url with proxies file", &urlfile) 141 | op.On("-p", "--dis-progressbar", "Disable progress bar", &disableBar) 142 | op.On("-g", "--github", "use github.com/mmpx12/proxy-list", &github) 143 | op.On("-o", "--output FILE", "File to write valid proxies", &output) 144 | op.On("-v", "--version", "Print version and exit", &printversion) 145 | op.Exemple("proxy-check -r -m 30 --socks5 -o valid-socks5.txt -g") 146 | op.Exemple("proxy-check -m 30 -o valid.txt -U 'https://raw.githubusercontent.com/mmpx12/proxy-list/master/proxies.txt'") 147 | op.Exemple("proxy-check -u ipinfo.io -T 6 /path/to/proxy") 148 | err := op.Parse() 149 | if err != nil { 150 | print(err.Error(), "\n") 151 | os.Exit(1) 152 | } 153 | op.Logo("Proxy-check", "smslant", nologo) 154 | 155 | if printversion { 156 | fmt.Println("version:", version) 157 | os.Exit(1) 158 | } 159 | 160 | if err != nil || len(os.Args) == 1 { 161 | op.Help() 162 | os.Exit(1) 163 | } 164 | 165 | if nbrvalid != "" { 166 | maxvalid, _ = strconv.Atoi(nbrvalid) 167 | checkmax = true 168 | } else { 169 | maxvalid = 0 170 | } 171 | 172 | opts := strings.Join(strings.Fields(strings.TrimSpace(strings.Join(op.Extra, ""))), " ") 173 | if (opts != "" || !github || urlfile == "") && file == "" { 174 | file = opts 175 | } else if file == "" { 176 | fmt.Println("Error: Need file or url with proxies") 177 | os.Exit(1) 178 | } 179 | 180 | if github { 181 | delete = true 182 | urlfile = "https://raw.githubusercontent.com/mmpx12/proxy-list/master/proxies.txt" 183 | } 184 | 185 | if urlfile != "" { 186 | if !strings.Contains(urlfile, "http://") && !strings.Contains(urlfile, "https://") { 187 | urlfile = "http://" + urlfile 188 | } 189 | delete = true 190 | b := make([]byte, 5) 191 | rand.Seed(time.Now().UnixNano()) 192 | rand.Read(b) 193 | file = fmt.Sprintf("proxies-%x.txt", b) 194 | r, err := http.Get(urlfile) 195 | if err != nil { 196 | print(err.Error(), "\n") 197 | os.Exit(1) 198 | } 199 | defer r.Body.Close() 200 | f, _ := os.Create(file) 201 | defer f.Close() 202 | r.Write(f) 203 | } 204 | 205 | if !httpp && !socks4 && !socks5 { 206 | all = true 207 | } 208 | 209 | if url == "" { 210 | url = "http://checkip.amazonaws.com" 211 | } else if !strings.Contains(url, "http://") && !strings.Contains(url, "https://") { 212 | url = "http://" + url 213 | } 214 | 215 | if timeout == "" { 216 | timeout = "3" 217 | } 218 | 219 | if goroutine == "" { 220 | goroutine = "50" 221 | } 222 | 223 | var wg = sync.WaitGroup{} 224 | goroutines, _ := strconv.Atoi(goroutine) 225 | maxGoroutines := goroutines 226 | guard := make(chan struct{}, maxGoroutines) 227 | readLines(httpp, socks4, socks5, all) 228 | 229 | if random { 230 | rand.Seed(time.Now().UnixNano()) 231 | rand.Shuffle(len(Proxies), 232 | func(i, j int) { 233 | Proxies[i], Proxies[j] = Proxies[j], Proxies[i] 234 | }) 235 | } 236 | 237 | var size int 238 | if maxvalid > 0 { 239 | size = maxvalid 240 | } else { 241 | size = len(Proxies) 242 | } 243 | bar := progressbar.NewOptions(size, 244 | progressbar.OptionSetWriter(ansi.NewAnsiStdout()), 245 | progressbar.OptionEnableColorCodes(true), 246 | progressbar.OptionSetPredictTime(false), 247 | progressbar.OptionClearOnFinish(), 248 | progressbar.OptionSetVisibility(!disableBar), 249 | progressbar.OptionSetDescription("[green]"+strconv.Itoa(int(counter))), 250 | progressbar.OptionSetTheme(progressbar.Theme{ 251 | Saucer: "[green]━[reset]", 252 | SaucerHead: "[green][dark_gray]", 253 | SaucerPadding: "━", 254 | BarStart: "[light_gray][[dark_gray]", 255 | BarEnd: "[light_gray]][reset]", 256 | })) 257 | 258 | var client *http.Client 259 | for i, j := range Proxies { 260 | 261 | bar.Describe("[green]" + strconv.Itoa(int(counter)) + "[light_gray]|[red]" + strconv.Itoa(i-int(counter)) + "[light_gray]|[yellow]" + strconv.Itoa(size) + "[reset]") 262 | mu.Lock() 263 | if checkmax && counter >= int32(maxvalid) { 264 | writeResult(output, file) 265 | bar.Finish() 266 | fmt.Println() 267 | fmt.Println("\033[4mValid proxies:\033[0m\n") 268 | for _, v := range valid { 269 | fmt.Println(v) 270 | } 271 | if delete { 272 | os.Remove(file) 273 | } 274 | os.Exit(0) 275 | } 276 | mu.Unlock() 277 | guard <- struct{}{} 278 | wg.Add(1) 279 | go func() { 280 | defer wg.Done() 281 | var res bool 282 | res = ProxyTest(client, j, url, timeout) 283 | if res == true { 284 | mu.Lock() 285 | bar.Add(1) 286 | atomic.AddInt32(&counter, 1) 287 | mu.Unlock() 288 | } 289 | <-guard 290 | }() 291 | } 292 | wg.Wait() 293 | bar.Finish() 294 | writeResult(output, file) 295 | fmt.Println() 296 | fmt.Println("\033[4mValid proxies:\033[0m\n") 297 | for _, v := range valid { 298 | fmt.Println(v) 299 | } 300 | if delete { 301 | os.Remove(file) 302 | } 303 | } 304 | --------------------------------------------------------------------------------