Skip to content
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
8 changes: 8 additions & 0 deletions .github/workflows/daily-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,18 @@ jobs:
contents: write

steps:
- name: Generate app token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.CLOUDCHECK_APP_ID }}
private-key: ${{ secrets.CLOUDCHECK_APP_PRIVATE_KEY }}

- name: Checkout stable branch
uses: actions/checkout@v6
with:
ref: stable
token: ${{ steps.app-token.outputs.token }}

- name: Set up Python
uses: actions/setup-python@v6
Expand Down
36 changes: 35 additions & 1 deletion .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- uses: actions/checkout@v6
Expand Down Expand Up @@ -47,8 +47,19 @@ jobs:
runs-on: ubuntu-latest
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/stable'
permissions:
contents: write
steps:
- name: Generate app token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.CLOUDCHECK_APP_ID }}
private-key: ${{ secrets.CLOUDCHECK_APP_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
- name: Set up Python
uses: actions/setup-python@v6
with:
Expand All @@ -57,10 +68,33 @@ jobs:
uses: dtolnay/rust-toolchain@stable
- name: Set up uv
uses: astral-sh/setup-uv@v7
- name: Get current version
id: get_version
run: |
VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
echo "VERSION=v${VERSION}" >> $GITHUB_OUTPUT
- name: Check for version change
id: version_check
run: |
git fetch --tags
LATEST_TAG=$(git describe --tags $(git rev-list --tags --max-count=1) 2>/dev/null || echo "none")
CURRENT="${{ steps.get_version.outputs.VERSION }}"
if [ "$LATEST_TAG" = "$CURRENT" ]; then
echo "changed=false" >> $GITHUB_OUTPUT
else
echo "changed=true" >> $GITHUB_OUTPUT
fi
- name: Build PyPi package
run: uv run maturin build --release --out dist
- name: Publish PyPi package
run: uv run maturin publish --skip-existing --username __token__ --password ${{ secrets.PYPI_TOKEN }}
- name: Tag release
if: steps.version_check.outputs.changed == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "${{ steps.get_version.outputs.VERSION }}" -m "Release ${{ steps.get_version.outputs.VERSION }}"
git push origin "refs/tags/${{ steps.get_version.outputs.VERSION }}"
linux:
runs-on: ${{ matrix.platform.runner }}
needs: publish
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cloudcheck"
version = "10.0.1"
version = "11.0.0"
edition = "2024"
description = "CloudCheck is a simple Rust tool to check whether an IP address or hostname belongs to a cloud provider."
license = "GPL-3.0"
Expand Down
9 changes: 7 additions & 2 deletions cloudcheck/helpers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import ipaddress
import os
import sys
import httpx
import asyncio
import blasthttp
from pathlib import Path
from typing import List, Set, Union

Expand Down Expand Up @@ -204,6 +205,9 @@ def strings_to_cidrs(
}


_client = blasthttp.BlastHTTP()


def request(url, include_api_key=False, browser_headers=False, timeout=60, **kwargs):
global _warned_missing_api_key
headers = kwargs.get("headers", {})
Expand All @@ -224,7 +228,8 @@ def request(url, include_api_key=False, browser_headers=False, timeout=60, **kwa
kwargs["headers"] = headers
kwargs["timeout"] = timeout
kwargs.setdefault("follow_redirects", True)
return httpx.get(url, **kwargs)
kwargs.setdefault("verify_certs", True)
return asyncio.run(_client.request(url, **kwargs))


def parse_v2fly_domain_file(file_path: Path) -> Set[str]:
Expand Down
13 changes: 13 additions & 0 deletions cloudcheck/providers/bunnycdn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from cloudcheck.providers.base import BaseProvider
from typing import List


class Bunnycdn(BaseProvider):
tags: List[str] = ["cdn"]
short_description: str = "Bunny CDN"
long_description: str = "A global content delivery network and edge platform."
# {"org_id": "ORG-BISD2-RIPE", "org_name": "BUNNYWAY, informacijske storitve d.o.o.", "country": "SI", "asns": [200325]}
asns: List[int] = [200325]
org_ids: List[str] = [
"ORG-BISD2-RIPE",
]
1 change: 1 addition & 0 deletions cloudcheck/providers/gabia.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Gabia(BaseProvider):
short_description: str = "Gabia (가비아)"
long_description: str = "A Korean cloud hosting and infrastructure provider."
# {"org_id": "@aut-17589-APNIC", "org_name": null, "country": null, "asns": [17589]}
asns: List[int] = [17589]
org_ids: List[str] = [
"@aut-17589-APNIC",
]
1 change: 1 addition & 0 deletions cloudcheck/providers/hostway.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Hostway(BaseProvider):
long_description: str = "A Korean cloud hosting and infrastructure provider."
# {"org_id": "@aut-9952-APNIC", "org_name": null, "country": null, "asns": [9952]}
# {"asn":9952,"asn_name":"HOSTWAY-AS-KR","org_id":"@aut-9952-APNIC"}
asns: List[int] = [9952]
org_ids: List[str] = [
"@aut-9952-APNIC",
]
1 change: 1 addition & 0 deletions cloudcheck/providers/kinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Kinx(BaseProvider):
"A Korean content delivery network and cloud infrastructure provider."
)
# {"org_id": "@aut-9286-APNIC", "org_name": null, "country": null, "asns": [9286,9957,17604]}
asns: List[int] = [9286, 9957, 17604]
org_ids: List[str] = [
"@aut-9286-APNIC",
]
1 change: 1 addition & 0 deletions cloudcheck/providers/ktcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Ktcloud(BaseProvider):
"A Korean cloud computing service provided by KT Corporation."
)
# {"asn":9947,"asn_name":"KTC-AS-KR","country":null,"org":null,"org_id":"@aut-152232-APNIC","rir":null,"subnets":["61.100.71.0/24","61.100.72.0/24"]}
asns: List[int] = [9947]
org_ids: List[str] = [
"@aut-152232-APNIC",
]
1 change: 1 addition & 0 deletions cloudcheck/providers/lgtelecom.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Lgtelecom(BaseProvider):
long_description: str = "A Korean telecommunications company offering CDN services."
# {"org_id": "@aut-17853-APNIC", "org_name": null, "country": null, "asns": [17853]}
# {"asn":17853,"asn_name":"LGTELECOM-AS-KR","org_id":"@aut-17853-APNIC"}
asns: List[int] = [17853]
org_ids: List[str] = [
"@aut-17853-APNIC",
]
17 changes: 11 additions & 6 deletions cloudcheck/providers/microsoft.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import re

from cloudcheck.providers.base import BaseProvider
from typing import List, Dict

Expand Down Expand Up @@ -27,13 +29,16 @@ class Microsoft(BaseProvider):
],
}

_ips_url = "https://download.microsoft.com/download/0/1/8/018E208D-54F8-44CD-AA26-CD7BC9524A8C/PublicIPs_20200824.xml"
_ips_confirmation_url = "https://www.microsoft.com/en-us/download/confirmation.aspx?id=56519"

def fetch_cidrs(self):
response = self.request(self._ips_url)
confirmation = self.request(self._ips_confirmation_url, browser_headers=True)
match = re.search(r'https://download\.microsoft\.com/download/[^"]+\.json', confirmation.text)
if not match:
raise ValueError("Could not find Azure IP ranges download URL")
response = self.request(match.group(0))
ranges = set()
for line in response.text.splitlines():
if "IpRange Subnet" in line:
ip_range = line.split('"')[1]
ranges.add(ip_range)
for entry in response.json().get("values", []):
for prefix in entry.get("properties", {}).get("addressPrefixes", []):
ranges.add(prefix)
return list(ranges)
1 change: 1 addition & 0 deletions cloudcheck/providers/navercloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Navercloud(BaseProvider):
)
# "org_id": "@aut-23576-APNIC", "org_name": null, "country": null, "asns": [23576,23982]}
# {"asn":23576,"asn_name":"nhn-AS-KR","org_id":"@aut-23576-APNIC"}
asns: List[int] = [23576, 23982]
org_ids: List[str] = [
"@aut-23576-APNIC",
]
1 change: 1 addition & 0 deletions cloudcheck/providers/nhncloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Nhncloud(BaseProvider):
)
# {"org_id": "@aut-10038-APNIC", "org_name": null, "country": null, "asns": [10038,45974,152291]}
# {"asn":45974,"asn_name":"NHN-AS-KR","org_id":"@aut-10038-APNIC"}
asns: List[int] = [10038, 45974, 152291]
org_ids: List[str] = [
"@aut-10038-APNIC",
]
1 change: 1 addition & 0 deletions cloudcheck/providers/skbroadband.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Skbroadband(BaseProvider):
long_description: str = "A Korean telecommunications company offering CDN services."
# {"org_id": "@aut-10049-APNIC", "org_name": null, "country": null, "asns": [9705,10049]}
# {"asn":10049,"asn_name":"SKNET-AS","country":null,"org":null,"org_id":"@aut-10049-APNIC"}
asns: List[int] = [9705, 10049]
org_ids: List[str] = [
"@aut-10049-APNIC",
]
15 changes: 15 additions & 0 deletions cloudcheck/providers/upcloud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from cloudcheck.providers.base import BaseProvider
from typing import List


class Upcloud(BaseProvider):
tags: List[str] = ["cloud"]
short_description: str = "UpCloud"
long_description: str = "A Finnish cloud infrastructure provider offering high-performance cloud servers."
# {"org_id": "ORG-UL87-RIPE", "org_name": "UpCloud Ltd", "country": "FI", "asns": [202053]}
# {"org_id": "UU-7-ARIN", "org_name": "UpCloud USA Inc", "country": "US", "asns": [25697]}
asns: List[int] = [202053, 25697]
org_ids: List[str] = [
"ORG-UL87-RIPE",
"UU-7-ARIN",
]
13 changes: 13 additions & 0 deletions cloudcheck/providers/vultr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from cloudcheck.providers.base import BaseProvider
from typing import List


class Vultr(BaseProvider):
tags: List[str] = ["cloud"]
short_description: str = "Vultr"
long_description: str = "A global cloud hosting provider offering SSD-based cloud compute, bare metal, and managed Kubernetes."
# {"org_id": "CHOOP-1-ARIN", "org_name": "The Constant Company, LLC", "country": "US", "asns": [11508,20473,40504,46407,54094]}
asns: List[int] = [11508, 20473, 40504, 46407, 54094]
org_ids: List[str] = [
"CHOOP-1-ARIN",
]
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[project]
name = "cloudcheck"
version = "10.0.1"
version = "11.0.0"
description = "Detailed database of cloud providers. Instantly look up a domain or IP address"
readme = "README.md"
requires-python = ">=3.9"
requires-python = ">=3.10"
dependencies = []

[build-system]
Expand All @@ -12,7 +12,7 @@ build-backend = "maturin"

[dependency-groups]
dev = [
"httpx>=0.28.1",
"blasthttp>=0.9.0",
"maturin>=1.10.2",
"pydantic>=2.12.5",
"pytest>=8.4.2",
Expand Down
Loading
Loading