8000 feat(publicip): add Cloudflare support by jjlin · Pull Request #2502 · qdm12/gluetun · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat(publicip): add Cloudflare support #2502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion internal/publicip/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ type API interface {
type Provider string

const (
Cloudflare Provider = "cloudflare"
IPInfo Provider = "ipinfo"
IP2Location Provider = "ip2location"
)

func New(provider Provider, client *http.Client, token string) ( //nolint:ireturn
a API, err error) {
switch provider {
case Cloudflare:
return newCloudflare(client), nil
case IPInfo:
return newIPInfo(client, token), nil
case IP2Location:
Expand All @@ -41,12 +44,14 @@ var (

func ParseProvider(s string) (provider Provider, err error) {
switch strings.ToLower(s) {
case "cloudflare":
return Cloudflare, nil
case "ipinfo":
return IPInfo, nil
case "ip2location":
return IP2Location, nil
default:
return "", fmt.Errorf(`%w: %q can only be "ipinfo" or "ip2location"`,
return "", fmt.Errorf(`%w: %q can only be "cloudflare", "ipinfo", or "ip2location"`,
ErrProviderNotValid, s)
}
}
91 changes: 91 additions & 0 deletions internal/publicip/api/cloudflare.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package api

import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/netip"
"strings"

"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
)

type cloudflare struct {
client *http.Client
}

func newCloudflare(client *http.Client) *cloudflare {
return &cloudflare{
client: client,
}
}

// FetchInfo obtains information on the public IP address of the machine,
// and returns an error if the `ip` argument is set since the Cloudflare API
// can only be used to provide details about the current machine public IP.
func (c *cloudflare) FetchInfo(ctx context.Context, ip netip.Addr) (
result models.PublicIP, err error) {
url := "https://speed.cloudflare.com/meta"
if ip.IsValid() {
return result, fmt.Errorf("%w: cloudflare cannot provide information on the arbitrary IP address %s",
ErrServiceLimited, ip)
}

request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return result, err
}

response, err := c.client.Do(request)
if err != nil {
return result, err
}
defer response.Body.Close()

switch response.StatusCode {
case http.StatusOK:
case http.StatusTooManyRequests:
return result, fmt.Errorf("%w from %s: %d %s",
ErrTooManyRequests, url, response.StatusCode, response.Status)
default:
return result, fmt.Errorf("%w from %s: %d %s",
ErrBadHTTPStatus, url, response.StatusCode, response.Status)
}

decoder := json.NewDecoder(response.Body)
var data struct {
Hostname string `json:"hostname,omitempty"`
ClientIP netip.Addr `json:"clientIp,omitempty"`
ASOrganization string `json:"asOrganization,omitempty"`
Country string `json:"country,omitempty"`
City string `json:"city,omitempty"`
Region string `json:"region,omitempty"`
PostalCode string `json:"postalCode,omitempty"`
Latitude string `json:"latitude,omitempty"`
Longitude string `json:"longitude,omitempty"`
}
if err := decoder.Decode(&data); err != nil {
return result, fmt.Errorf("decoding response: %w", err)
}

countryCode := strings.ToLower(data.Country)
country, ok := constants.CountryCodes()[countryCode]
if ok {
data.Country = country
}

result = models.PublicIP{
IP: data.ClientIP,
Region: data.Region,
Country: data.Country,
City: data.City,
Hostname: data.Hostname,
Location: data.Latitude + "," + data.Longitude,
Organization: data.ASOrganization,
PostalCode: data.PostalCode,
Timezone: "", // no timezone
}
return result, nil
}
1 change: 1 addition & 0 deletions internal/publicip/api/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ var (
ErrTokenNotValid = errors.New("token is not valid")
ErrTooManyRequests = errors.New("too many requests sent for this month")
ErrBadHTTPStatus = errors.New("bad HTTP status received")
ErrServiceLimited = errors.New("service is limited")
)
0