2020-05-29 10:13:42 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"archive/zip"
|
|
|
|
|
"bytes"
|
|
|
|
|
"flag"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io/ioutil"
|
2020-05-31 19:50:28 +00:00
|
|
|
"net"
|
2020-05-29 10:13:42 +00:00
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"sort"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/qdm12/golibs/network"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
os.Exit(_main())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func _main() int {
|
|
|
|
|
provider := flag.String("provider", "surfshark", "VPN provider to parse openvpn files for, can be 'surfshark'")
|
|
|
|
|
flag.Parse()
|
|
|
|
|
|
2020-05-31 19:50:28 +00:00
|
|
|
var urls []string
|
|
|
|
|
var suffix string
|
2020-05-29 10:13:42 +00:00
|
|
|
switch *provider {
|
|
|
|
|
case "surfshark":
|
2020-05-31 19:50:28 +00:00
|
|
|
urls = []string{
|
|
|
|
|
"https://account.surfshark.com/api/v1/server/configurations",
|
|
|
|
|
"https://v2uploads.zopim.io/p/2/L/p2LbwLkvfQoSdzOl6VEltzQA6StiZqrs/12500634259669c77012765139bcfe4f4c90db1e.zip",
|
|
|
|
|
}
|
|
|
|
|
suffix = ".prod.surfshark.com"
|
2020-05-29 10:13:42 +00:00
|
|
|
default:
|
|
|
|
|
fmt.Printf("Provider %q is not supported\n", *provider)
|
|
|
|
|
return 1
|
|
|
|
|
}
|
2020-05-31 19:50:28 +00:00
|
|
|
contents, err := fetchAndExtractFiles(urls...)
|
2020-05-29 10:13:42 +00:00
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
return 1
|
|
|
|
|
}
|
2020-05-31 19:50:28 +00:00
|
|
|
|
2020-05-29 10:13:42 +00:00
|
|
|
uniqueSubdomains := make(map[string]struct{})
|
|
|
|
|
for _, content := range contents {
|
2020-05-31 19:50:28 +00:00
|
|
|
subdomain, err := extractInformation(content, suffix)
|
2020-05-29 10:13:42 +00:00
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
return 1
|
2020-05-31 19:50:28 +00:00
|
|
|
} else if len(subdomain) > 0 {
|
|
|
|
|
uniqueSubdomains[subdomain] = struct{}{}
|
2020-05-29 10:13:42 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
subdomains := make([]string, len(uniqueSubdomains))
|
|
|
|
|
i := 0
|
|
|
|
|
for subdomain := range uniqueSubdomains {
|
|
|
|
|
subdomains[i] = subdomain
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
sort.Slice(subdomains, func(i, j int) bool {
|
|
|
|
|
return subdomains[i] < subdomains[j]
|
|
|
|
|
})
|
|
|
|
|
fmt.Println("Subdomains found are: ", strings.Join(subdomains, ","))
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-31 19:50:28 +00:00
|
|
|
func fetchAndExtractFiles(urls ...string) (contents [][]byte, err error) {
|
|
|
|
|
client := network.NewClient(10 * time.Second)
|
|
|
|
|
for _, url := range urls {
|
|
|
|
|
zipBytes, status, err := client.GetContent(url)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
} else if status != http.StatusOK {
|
|
|
|
|
return nil, fmt.Errorf("Getting %s results in HTTP status code %d", url, status)
|
|
|
|
|
}
|
|
|
|
|
newContents, err := zipExtractAll(zipBytes)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
contents = append(contents, newContents...)
|
|
|
|
|
}
|
|
|
|
|
return contents, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-29 10:13:42 +00:00
|
|
|
func zipExtractAll(zipBytes []byte) (contents [][]byte, err error) {
|
|
|
|
|
r, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
contents = make([][]byte, len(r.File))
|
|
|
|
|
for i, zf := range r.File {
|
|
|
|
|
f, err := zf.Open()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer f.Close()
|
|
|
|
|
contents[i], err = ioutil.ReadAll(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if err := f.Close(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return contents, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-31 19:50:28 +00:00
|
|
|
func extractInformation(content []byte, suffix string) (subdomain string, err error) {
|
2020-05-29 10:13:42 +00:00
|
|
|
lines := strings.Split(string(content), "\n")
|
|
|
|
|
for _, line := range lines {
|
|
|
|
|
if strings.HasPrefix(line, "remote ") {
|
|
|
|
|
words := strings.Fields(line)
|
|
|
|
|
if len(words) < 2 {
|
|
|
|
|
return "", fmt.Errorf("not enough words on line %q", line)
|
|
|
|
|
}
|
|
|
|
|
host := words[1]
|
2020-05-31 19:50:28 +00:00
|
|
|
if net.ParseIP(host) != nil {
|
|
|
|
|
return "", nil // ignore IP addresses
|
|
|
|
|
}
|
|
|
|
|
return strings.TrimSuffix(host, suffix), nil
|
2020-05-29 10:13:42 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "", fmt.Errorf("could not find remote line")
|
|
|
|
|
}
|