153 lines
3.8 KiB
Go
153 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/qdm12/golibs/network"
|
|
)
|
|
|
|
func main() {
|
|
os.Exit(_main())
|
|
}
|
|
|
|
// Find subdomains from .ovpn files contained in a .zip file
|
|
func _main() int {
|
|
provider := flag.String("provider", "surfshark", "VPN provider to parse openvpn files for, can be 'surfshark' or 'vyprvpn")
|
|
flag.Parse()
|
|
|
|
var urls []string
|
|
var suffix string
|
|
switch *provider {
|
|
case "surfshark":
|
|
urls = []string{
|
|
"https://account.surfshark.com/api/v1/server/configurations",
|
|
"https://v2uploads.zopim.io/p/2/L/p2LbwLkvfQoSdzOl6VEltzQA6StiZqrs/12500634259669c77012765139bcfe4f4c90db1e.zip",
|
|
}
|
|
suffix = ".prod.surfshark.com"
|
|
case "vyprvpn":
|
|
urls = []string{
|
|
"https://support.vyprvpn.com/hc/article_attachments/360052617332/Vypr_OpenVPN_20200320.zip",
|
|
}
|
|
suffix = ".vyprvpn.com"
|
|
default:
|
|
fmt.Printf("Provider %q is not supported\n", *provider)
|
|
return 1
|
|
}
|
|
contents, err := fetchAndExtractFiles(urls...)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return 1
|
|
}
|
|
|
|
uniqueSubdomainsToFilename := make(map[string]string)
|
|
for fileName, content := range contents {
|
|
subdomain, err := extractInformation(content, suffix)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return 1
|
|
} else if len(subdomain) > 0 {
|
|
fileName = strings.TrimSuffix(fileName, ".ovpn")
|
|
fileName = strings.ReplaceAll(fileName, " - ", " ")
|
|
uniqueSubdomainsToFilename[subdomain] = fileName
|
|
}
|
|
}
|
|
type subdomainFilename struct {
|
|
subdomain string
|
|
fileName string
|
|
}
|
|
subdomains := make([]subdomainFilename, len(uniqueSubdomainsToFilename))
|
|
i := 0
|
|
for subdomain, fileName := range uniqueSubdomainsToFilename {
|
|
subdomains[i] = subdomainFilename{
|
|
subdomain: subdomain,
|
|
fileName: fileName,
|
|
}
|
|
i++
|
|
}
|
|
sort.Slice(subdomains, func(i, j int) bool {
|
|
return subdomains[i].subdomain < subdomains[j].subdomain
|
|
})
|
|
fmt.Println("Subdomain Filename")
|
|
for i := range subdomains {
|
|
fmt.Printf("%s %s\n", subdomains[i].subdomain, subdomains[i].fileName)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func fetchAndExtractFiles(urls ...string) (contents map[string][]byte, err error) {
|
|
client := network.NewClient(10 * time.Second)
|
|
contents = make(map[string][]byte)
|
|
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
|
|
}
|
|
for fileName, content := range newContents {
|
|
contents[fileName] = content
|
|
}
|
|
}
|
|
return contents, nil
|
|
}
|
|
|
|
func zipExtractAll(zipBytes []byte) (contents map[string][]byte, err error) {
|
|
r, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
contents = map[string][]byte{}
|
|
for _, zf := range r.File {
|
|
fileName := filepath.Base(zf.Name)
|
|
if !strings.HasSuffix(fileName, ".ovpn") {
|
|
continue
|
|
}
|
|
f, err := zf.Open()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
contents[fileName], err = ioutil.ReadAll(f)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := f.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return contents, nil
|
|
}
|
|
|
|
func extractInformation(content []byte, suffix string) (subdomain string, err error) {
|
|
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]
|
|
if net.ParseIP(host) != nil {
|
|
return "", nil // ignore IP addresses
|
|
}
|
|
return strings.TrimSuffix(host, suffix), nil
|
|
}
|
|
}
|
|
return "", fmt.Errorf("could not find remote line in: %s", string(content))
|
|
}
|