diff --git a/internal/updater/countries.go b/internal/constants/countries.go similarity index 98% rename from internal/updater/countries.go rename to internal/constants/countries.go index e02465a7..d98d1a78 100644 --- a/internal/updater/countries.go +++ b/internal/constants/countries.go @@ -1,6 +1,6 @@ -package updater +package constants -func getCountryCodes() map[string]string { //nolint:dupl +func CountryCodes() map[string]string { return map[string]string{ "af": "Afghanistan", "ax": "Aland Islands", diff --git a/internal/publicip/info.go b/internal/publicip/info.go new file mode 100644 index 00000000..f6b3d5a3 --- /dev/null +++ b/internal/publicip/info.go @@ -0,0 +1,54 @@ +package publicip + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net" + "net/http" + "strings" + + "github.com/qdm12/gluetun/internal/constants" +) + +type ipInfoData struct { + Region string `json:"region"` + Country string `json:"country"` + City string `json:"city"` +} + +var ErrBadHTTPStatus = errors.New("bad HTTP status received") + +func Info(ctx context.Context, client *http.Client, ip net.IP) ( //nolint:interfacer + country, region, city string, err error) { + const baseURL = "https://ipinfo.io/" + url := baseURL + ip.String() + request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return "", "", "", err + } + + response, err := client.Do(request) + if err != nil { + return "", "", "", err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + return "", "", "", fmt.Errorf("%w: %d", ErrBadHTTPStatus, response.StatusCode) + } + + decoder := json.NewDecoder(response.Body) + var data ipInfoData + if err := decoder.Decode(&data); err != nil { + return "", "", "", err + } + + countryCode := strings.ToLower(data.Country) + country, ok := constants.CountryCodes()[countryCode] + if !ok { + country = data.Country + } + return country, data.Region, data.City, nil +} diff --git a/internal/publicip/loop.go b/internal/publicip/loop.go index 96cf13fc..dd427adb 100644 --- a/internal/publicip/loop.go +++ b/internal/publicip/loop.go @@ -28,6 +28,7 @@ type looper struct { state state // Objects getter IPGetter + client *http.Client logger logging.Logger os os.OS // Fixed settings @@ -57,6 +58,7 @@ func NewLooper(client *http.Client, logger logging.Logger, settings: settings, }, // Objects + client: client, getter: NewIPGetter(client), logger: logger.WithPrefix("ip getter: "), os: os, @@ -150,8 +152,17 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) { case ip := <-ipCh: getCancel() l.state.setPublicIP(ip) - l.logger.Info("Public IP address is %s", ip) - err := persistPublicIP(l.os.OpenFile, l.state.settings.IPFilepath, + + message := "Public IP address is " + ip.String() + country, region, city, err := Info(ctx, l.client, ip) + if err != nil { + l.logger.Warn(err) + } else { + message += " (" + country + ", " + region + ", " + city + ")" + } + l.logger.Info(message) + + err = persistPublicIP(l.os.OpenFile, l.state.settings.IPFilepath, ip.String(), l.puid, l.pgid) if err != nil { l.logger.Error(err) diff --git a/internal/updater/cyberghost.go b/internal/updater/cyberghost.go index f58602d9..57e0fcb7 100644 --- a/internal/updater/cyberghost.go +++ b/internal/updater/cyberghost.go @@ -5,6 +5,7 @@ import ( "fmt" "sort" + "github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/models" ) @@ -23,7 +24,7 @@ func (u *updater) updateCyberghost(ctx context.Context) (err error) { func findCyberghostServers(ctx context.Context, lookupIP lookupIPFunc) (servers []models.CyberghostServer, err error) { groups := getCyberghostGroups() - allCountryCodes := getCountryCodes() + allCountryCodes := constants.CountryCodes() cyberghostCountryCodes := getCyberghostSubdomainToRegion() possibleCountryCodes := mergeCountryCodes(cyberghostCountryCodes, allCountryCodes) @@ -115,7 +116,7 @@ func mergeCountryCodes(base, extend map[string]string) (merged map[string]string return merged } -func getCyberghostSubdomainToRegion() map[string]string { //nolint:dupl +func getCyberghostSubdomainToRegion() map[string]string { return map[string]string{ "af": "Afghanistan", "ax": "Aland Islands", diff --git a/internal/updater/ips.go b/internal/updater/ips.go index 9062ef8f..85589a69 100644 --- a/internal/updater/ips.go +++ b/internal/updater/ips.go @@ -2,16 +2,8 @@ package updater import ( "bytes" - "context" - "encoding/json" - "errors" - "fmt" "net" - "net/http" "sort" - "strings" - - "github.com/qdm12/golibs/network" ) func uniqueSortedIPs(ips []net.IP) []net.IP { @@ -33,35 +25,3 @@ func uniqueSortedIPs(ips []net.IP) []net.IP { }) return ips } - -var errBadHTTPStatus = errors.New("bad HTTP status received") - -type ipInfoData struct { - Region string `json:"region"` - Country string `json:"country"` - City string `json:"city"` -} - -func getIPInfo(ctx context.Context, client network.Client, ip net.IP) (country, region, city string, err error) { - const baseURL = "https://ipinfo.io/" - url := baseURL + ip.String() - request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return "", "", "", err - } - b, status, err := client.Do(request) - if err != nil { - return "", "", "", err - } else if status != http.StatusOK { - return "", "", "", fmt.Errorf("%w: %d", errBadHTTPStatus, status) - } - var data ipInfoData - if err := json.Unmarshal(b, &data); err != nil { - return "", "", "", err - } - country, ok := getCountryCodes()[strings.ToLower(data.Country)] - if !ok { - country = data.Country - } - return country, data.Region, data.City, nil -} diff --git a/internal/updater/purevpn.go b/internal/updater/purevpn.go index 45cd23e9..4cbee0a8 100644 --- a/internal/updater/purevpn.go +++ b/internal/updater/purevpn.go @@ -3,10 +3,13 @@ package updater import ( "context" "fmt" + "net/http" "sort" "strings" + "time" "github.com/qdm12/gluetun/internal/models" + "github.com/qdm12/gluetun/internal/publicip" "github.com/qdm12/golibs/network" ) @@ -60,7 +63,11 @@ func findPurevpnServers(ctx context.Context, client network.Client, lookupIP loo warnings = append(warnings, warning) continue } - country, region, city, err := getIPInfo(ctx, client, IPs[0]) + + // TODO remove once we move away from network.Client + const httpTimeout = 3 * time.Second + httpClient := &http.Client{Timeout: httpTimeout} + country, region, city, err := publicip.Info(ctx, httpClient, IPs[0]) if err != nil { return nil, warnings, err }