| 40 | } |
| 41 | |
| 42 | func FetchAWSIPRanges(ctx context.Context, url string) (*AWSIPRanges, error) { |
| 43 | client := &http.Client{} |
| 44 | reqCtx, reqCancel := context.WithTimeout(ctx, 5*time.Second) |
| 45 | defer reqCancel() |
| 46 | req, _ := http.NewRequestWithContext(reqCtx, http.MethodGet, url, nil) |
| 47 | resp, err := client.Do(req) |
| 48 | if err != nil { |
| 49 | return nil, err |
| 50 | } |
| 51 | defer resp.Body.Close() |
| 52 | |
| 53 | if resp.StatusCode != http.StatusOK { |
| 54 | b, _ := io.ReadAll(resp.Body) |
| 55 | return nil, xerrors.Errorf("unexpected status code %d: %s", resp.StatusCode, b) |
| 56 | } |
| 57 | |
| 58 | var body awsIPRangesResponse |
| 59 | err = json.NewDecoder(resp.Body).Decode(&body) |
| 60 | if err != nil { |
| 61 | return nil, xerrors.Errorf("json decode: %w", err) |
| 62 | } |
| 63 | |
| 64 | out := &AWSIPRanges{ |
| 65 | V4: make([]netip.Prefix, 0, len(body.IPV4Prefixes)), |
| 66 | V6: make([]netip.Prefix, 0, len(body.IPV6Prefixes)), |
| 67 | } |
| 68 | |
| 69 | for _, p := range body.IPV4Prefixes { |
| 70 | prefix, err := netip.ParsePrefix(p.Prefix) |
| 71 | if err != nil { |
| 72 | return nil, xerrors.Errorf("parse ip prefix: %w", err) |
| 73 | } |
| 74 | if prefix.Addr().Is6() { |
| 75 | return nil, xerrors.Errorf("ipv4 prefix contains ipv6 address: %s", p.Prefix) |
| 76 | } |
| 77 | out.V4 = append(out.V4, prefix) |
| 78 | } |
| 79 | |
| 80 | for _, p := range body.IPV6Prefixes { |
| 81 | prefix, err := netip.ParsePrefix(p.Prefix) |
| 82 | if err != nil { |
| 83 | return nil, xerrors.Errorf("parse ip prefix: %w", err) |
| 84 | } |
| 85 | if prefix.Addr().Is4() { |
| 86 | return nil, xerrors.Errorf("ipv6 prefix contains ipv4 address: %s", p.Prefix) |
| 87 | } |
| 88 | out.V6 = append(out.V6, prefix) |
| 89 | } |
| 90 | |
| 91 | return out, nil |
| 92 | } |
| 93 | |
| 94 | // CheckIP checks if the given IP address is an AWS IP. |
| 95 | func (r *AWSIPRanges) CheckIP(ip netip.Addr) bool { |