package models import ( "encoding/hex" "fmt" "net" "strings" ) type CyberghostServer struct { Region string `json:"region"` Group string `json:"group"` IPs []net.IP `json:"ips"` } func (s *CyberghostServer) String() string { return fmt.Sprintf("{Region: %q, Group: %q, IPs: %s}", s.Region, s.Group, goStringifyIPs(s.IPs)) } type FastestvpnServer struct { Hostname string `json:"hostname"` TCP bool `json:"tcp"` UDP bool `json:"udp"` Country string `json:"country"` IPs []net.IP `json:"ips"` } func (s *FastestvpnServer) String() string { return fmt.Sprintf("{Country: %q, Hostname: %q, UDP: %t, TCP: %t, IPs: %s}", s.Country, s.Hostname, s.UDP, s.TCP, goStringifyIPs(s.IPs)) } type HideMyAssServer struct { Country string `json:"country"` Region string `json:"region"` City string `json:"city"` Hostname string `json:"hostname"` TCP bool `json:"tcp"` UDP bool `json:"udp"` IPs []net.IP `json:"ips"` } func (s *HideMyAssServer) String() string { return fmt.Sprintf("{Country: %q, Region: %q, City: %q, Hostname: %q, TCP: %t, UDP: %t, IPs: %s}", s.Country, s.Region, s.City, s.Hostname, s.TCP, s.UDP, goStringifyIPs(s.IPs)) } type MullvadServer struct { IPs []net.IP `json:"ips"` IPsV6 []net.IP `json:"ipsv6"` Country string `json:"country"` City string `json:"city"` ISP string `json:"isp"` Owned bool `json:"owned"` } func (s *MullvadServer) String() string { return fmt.Sprintf("{Country: %q, City: %q, ISP: %q, Owned: %t, IPs: %s, IPsV6: %s}", s.Country, s.City, s.ISP, s.Owned, goStringifyIPs(s.IPs), goStringifyIPs(s.IPsV6)) } type NordvpnServer struct { //nolint:maligned Region string `json:"region"` Number uint16 `json:"number"` IP net.IP `json:"ip"` TCP bool `json:"tcp"` UDP bool `json:"udp"` } func (s *NordvpnServer) String() string { return fmt.Sprintf("{Region: %q, Number: %d, TCP: %t, UDP: %t, IP: %s}", s.Region, s.Number, s.TCP, s.UDP, goStringifyIP(s.IP)) } type PrivadoServer struct { IP net.IP `json:"ip"` Hostname string `json:"hostname"` } func (s *PrivadoServer) String() string { return fmt.Sprintf("{Hostname: %q, IP: %s}", s.Hostname, goStringifyIP(s.IP)) } type PIAServer struct { Region string `json:"region"` ServerName string `json:"server_name"` TCP bool `json:"tcp"` UDP bool `json:"udp"` PortForward bool `json:"port_forward"` IP net.IP `json:"ip"` } func (p *PIAServer) String() string { return fmt.Sprintf("{Region: %q, ServerName: %q, TCP: %t, UDP: %t, PortForward: %t, IP: %s}", p.Region, p.ServerName, p.TCP, p.UDP, p.PortForward, goStringifyIP(p.IP)) } type PrivatevpnServer struct { Country string `json:"country"` City string `json:"city"` Hostname string `json:"hostname"` IPs []net.IP `json:"ip"` } func (s *PrivatevpnServer) String() string { return fmt.Sprintf("{Country: %q, City: %q, Hostname: %q, IPs: %s}", s.Country, s.City, s.Hostname, goStringifyIPs(s.IPs)) } type PurevpnServer struct { Country string `json:"country"` Region string `json:"region"` City string `json:"city"` IPs []net.IP `json:"ips"` } func (s *PurevpnServer) String() string { return fmt.Sprintf("{Country: %q, Region: %q, City: %q, IPs: %s}", s.Country, s.Region, s.City, goStringifyIPs(s.IPs)) } type SurfsharkServer struct { Region string `json:"region"` IPs []net.IP `json:"ips"` } func (s *SurfsharkServer) String() string { return fmt.Sprintf("{Region: %q, IPs: %s}", s.Region, goStringifyIPs(s.IPs)) } type TorguardServer struct { Country string `json:"country"` City string `json:"city"` Hostname string `json:"hostname"` IP net.IP `json:"ip"` } func (s *TorguardServer) String() string { return fmt.Sprintf("{Country: %q, City: %q, Hostname: %q, IP: %s}", s.Country, s.City, s.Hostname, goStringifyIP(s.IP)) } type VyprvpnServer struct { Region string `json:"region"` IPs []net.IP `json:"ips"` } func (s *VyprvpnServer) String() string { return fmt.Sprintf("{Region: %q, IPs: %s}", s.Region, goStringifyIPs(s.IPs)) } type WindscribeServer struct { Region string `json:"region"` City string `json:"city"` Hostname string `json:"hostname"` IP net.IP `json:"ip"` } func (s *WindscribeServer) String() string { return fmt.Sprintf("{Region: %q, City: %q, Hostname: %q, IP: %s}", s.Region, s.City, s.Hostname, goStringifyIP(s.IP)) } func goStringifyIP(ip net.IP) string { s := fmt.Sprintf("%#v", ip) s = strings.TrimSuffix(strings.TrimPrefix(s, "net.IP{"), "}") fields := strings.Split(s, ", ") isIPv4 := ip.To4() != nil if isIPv4 { fields = fields[len(fields)-4:] } // Count leading zeros leadingZeros := 0 for i := range fields { if fields[i] == "0x0" { leadingZeros++ } else { break } } // Remove leading zeros fields = fields[leadingZeros:] for i := range fields { // IPv4 is better understood in integer notation, whereas IPv6 is written in hex notation if isIPv4 { hexString := strings.Replace(fields[i], "0x", "", 1) if len(hexString) == 1 { hexString = "0" + hexString } b, _ := hex.DecodeString(hexString) fields[i] = fmt.Sprintf("%d", b[0]) } } return fmt.Sprintf("net.IP{%s}", strings.Join(fields, ", ")) } func goStringifyIPs(ips []net.IP) string { ipStrings := make([]string, len(ips)) for i := range ips { ipStrings[i] = goStringifyIP(ips[i]) ipStrings[i] = strings.TrimPrefix(ipStrings[i], "net.IP") } return "[]net.IP{" + strings.Join(ipStrings, ", ") + "}" }