├── .github └── workflows │ └── run-geoip2dat.yml ├── .gitignore ├── CHANGELOG ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── geoip2fast-legacy ├── .gitignore ├── CHANGELOG ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── geoip2fast-1.1.11.tar.gz ├── geoip2fast │ ├── __init__.py │ ├── geoip2dat.py │ ├── geoip2fast-asn-ipv6.dat.gz │ ├── geoip2fast-asn.dat.gz │ ├── geoip2fast-ipv6.dat.gz │ ├── geoip2fast.dat.gz │ └── geoip2fast.py ├── images │ ├── cli.jpg │ ├── clicode.png │ ├── code_example.png │ ├── coverage_test.jpg │ ├── coverage_verbose.jpg │ ├── geoip2dat01.jpg │ ├── geoip2dat02.jpg │ ├── geoip2dat03.jpg │ ├── geoip2fast-win32.jpg │ ├── geoip2fast.jpg │ ├── geoip2fast_selftest.jpg │ ├── geoip2fastv4.jpg │ ├── geoip2fastv6.jpg │ ├── geoipv6_coverage.jpg │ └── speed_test.jpg ├── setup.py └── tests │ ├── .gitignore │ ├── compare_with_mmdb.py │ ├── coverage_test.py │ ├── geoip2fast_test.py │ ├── random_test.py │ └── speed_test.py ├── geoip2fast ├── __init__.py ├── geoip2dat.py ├── geoip2fast-asn-ipv6.dat.gz ├── geoip2fast-asn.dat.gz ├── geoip2fast-city-asn-ipv6.dat.gz ├── geoip2fast-city-asn.dat.gz ├── geoip2fast-city-ipv6.dat.gz ├── geoip2fast-city.dat.gz ├── geoip2fast-ipv6.dat.gz ├── geoip2fast.dat.gz ├── geoip2fast.py ├── geoip2fastmin.py ├── geoip2fastminified.py └── tests │ ├── .gitignore │ ├── compare_with_mmdb.py │ ├── coverage_test.py │ ├── geoip2fast_test.py │ ├── random_test.py │ └── speed_test.py ├── images ├── geoip2dat01.jpg ├── geoip2dat02.jpg ├── geoip2dat03.jpg ├── geoip2fast_selftest.jpg ├── geoip2fastcity.jpg └── geoip2fastv4.jpg └── setup.py /.github/workflows/run-geoip2dat.yml: -------------------------------------------------------------------------------- 1 | name: Update GeoIP2Fast dat files 2 | on: 3 | repository_dispatch: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: 0 22 * * 2,5 7 | jobs: 8 | run: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@v4.1.1 13 | - name: Download Geolite2 CSV files from Maxmind 14 | run: | 15 | mkdir -p geolite2 16 | wget -nv -O GeoLite2-ASN-CSV.zip "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN-CSV&license_key=${{ secrets.MAXMIND_KEY }}&suffix=zip" 17 | wget -nv -O GeoLite2-Country-CSV.zip "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country-CSV&license_key=${{ secrets.MAXMIND_KEY }}&suffix=zip" 18 | wget -nv -O GeoLite2-City-CSV.zip "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City-CSV&license_key=${{ secrets.MAXMIND_KEY }}&suffix=zip" 19 | - name: Decompress Geolite2 zip files 20 | uses: TonyBogdanov/zip@1.0 21 | with: 22 | args: unzip -qq GeoLite2-*-CSV.zip 23 | - name: Prepare ENV variables 24 | run: | 25 | TAG_DATE=$(ls -d GeoLite2-Country-CSV_* | awk -F '_' '{print $2}') 26 | echo "TAG_DATE=$TAG_DATE" >> $GITHUB_ENV 27 | cat $GITHUB_ENV > Maxmind-Geolite2-CSV_$TAG_DATE 28 | - name: Copy GeoLite2 CSV files 29 | run: | 30 | cp -v GeoLite*/*.csv geolite2 31 | 32 | - name: Create v1.1.X geoip2fast.dat.gz with Country + IPv4 33 | run: | 34 | python3 geoip2fast-legacy/geoip2fast/geoip2dat.py --country-dir ./geolite2/ --output-dir ./ 35 | mv -vf geoip2fast.dat.gz geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz 36 | - name: Create v1.1.X geoip2fast.dat.gz with Country + IPv4 + IPv6 37 | run: | 38 | python3 geoip2fast-legacy/geoip2fast/geoip2dat.py --country-dir ./geolite2/ --output-dir ./ --with-ipv6 39 | mv -vf geoip2fast.dat.gz geoip2fast-legacy/geoip2fast/geoip2fast-ipv6.dat.gz 40 | - name: Create v1.1.X geoip2fast.dat.gz with Country + ASN + IPv4 41 | run: | 42 | python3 geoip2fast-legacy/geoip2fast/geoip2dat.py --country-dir ./geolite2/ --asn-dir ./geolite2/ --output-dir ./ 43 | mv -vf geoip2fast.dat.gz geoip2fast-legacy/geoip2fast/geoip2fast-asn.dat.gz 44 | - name: Create v1.1.X geoip2fast.dat.gz with Country + ASN + IPv4 + IPv6 45 | run: | 46 | python3 geoip2fast-legacy/geoip2fast/geoip2dat.py --country-dir ./geolite2/ --asn-dir ./geolite2/ --output-dir ./ --with-ipv6 47 | # PERCENTAGE=$(python3 geoip2fast-legacy/geoip2fast/geoip2fast.py --coverage | grep IPv4 | awk '{print $4}' | sed 's/%//g') 48 | # python3 geoip2fast-legacy/geoip2fast/geoip2fast.py --coverage > "IPv4_Coverage_${PERCENTAGE}_percent.txt" 49 | mv -vf geoip2fast.dat.gz geoip2fast-legacy/geoip2fast/geoip2fast-asn-ipv6.dat.gz 50 | 51 | - name: Create v1.2.X geoip2fast.dat.gz with City + IPv4 52 | run: | 53 | python3 geoip2fast/geoip2dat.py --city-dir ./geolite2/ --output-dir ./ 54 | mv -vf geoip2fast.dat.gz geoip2fast/geoip2fast-city.dat.gz 55 | - name: Create v1.2.X geoip2fast.dat.gz with City + IPv4 + IPv6 56 | run: | 57 | python3 geoip2fast/geoip2dat.py --city-dir ./geolite2/ --output-dir ./ --with-ipv6 58 | mv -vf geoip2fast.dat.gz geoip2fast/geoip2fast-city-ipv6.dat.gz 59 | - name: Create v1.2.X geoip2fast.dat.gz with City + ASN + IPv4 60 | run: | 61 | python3 geoip2fast/geoip2dat.py --city-dir ./geolite2/ --asn-dir ./geolite2/ --output-dir ./ 62 | mv -vf geoip2fast.dat.gz geoip2fast/geoip2fast-city-asn.dat.gz 63 | - name: Create v1.2.X geoip2fast.dat.gz with City + ASN + IPv4 + IPv6 64 | run: | 65 | python3 geoip2fast/geoip2dat.py --city-dir ./geolite2/ --asn-dir ./geolite2/ --output-dir ./ --with-ipv6 66 | mv -vf geoip2fast.dat.gz geoip2fast/geoip2fast-city-asn-ipv6.dat.gz 67 | - name: Create v1.2.X geoip2fast.dat.gz with Country + IPv4 68 | run: | 69 | python3 geoip2fast/geoip2dat.py --country-dir ./geolite2/ --output-dir ./ 70 | mv -vf geoip2fast.dat.gz geoip2fast/geoip2fast.dat.gz 71 | - name: Create v1.2.X geoip2fast.dat.gz with Country + IPv4 + IPv6 72 | run: | 73 | python3 geoip2fast/geoip2dat.py --country-dir ./geolite2/ --output-dir ./ --with-ipv6 74 | mv -vf geoip2fast.dat.gz geoip2fast/geoip2fast-ipv6.dat.gz 75 | - name: Create v1.2.X geoip2fast.dat.gz with Country + ASN + IPv4 76 | run: | 77 | python3 geoip2fast/geoip2dat.py --country-dir ./geolite2/ --asn-dir ./geolite2/ --output-dir ./ 78 | mv -vf geoip2fast.dat.gz geoip2fast/geoip2fast-asn.dat.gz 79 | - name: Create v1.2.X geoip2fast.dat.gz with Country + ASN + IPv4 + IPv6 80 | run: | 81 | python3 geoip2fast/geoip2dat.py --country-dir ./geolite2/ --asn-dir ./geolite2/ --output-dir ./ --with-ipv6 82 | # PERCENTAGE=$(python3 geoip2fast/geoip2fast.py --coverage | grep IPv4 | awk '{print $4}' | sed 's/%//g') 83 | # python3 geoip2fast/geoip2fast.py --coverage > "IPv4_Coverage_${PERCENTAGE}_percent.txt" 84 | mv -vf geoip2fast.dat.gz geoip2fast/geoip2fast-asn-ipv6.dat.gz 85 | 86 | - name: Push to "main" branch 87 | run: | 88 | git config user.name "${{ github.actor }}" 89 | git config user.email "${{ github.actor }}@users.noreply.github.com" 90 | cd geoip2fast 91 | git add geoip2fast*.dat.gz 92 | git commit -m "Updated dat.gz files from Maxmind-${{ env.TAG_DATE }} for v1.2.X" 93 | cd ../geoip2fast-legacy/geoip2fast/ 94 | git add geoip2fast*.dat.gz 95 | git commit -m "Updated dat.gz files from Maxmind-${{ env.TAG_DATE }} for v1.1.X" 96 | git remote set-url origin "https://${{ secrets.TOKEN }}@github.com/${{ github.repository }}" 97 | git push -f origin main 98 | 99 | - name: Delete LATEST and LEGACY release 100 | env: 101 | GH_TOKEN: ${{ github.token }} 102 | run: | 103 | gh release list 104 | gh release delete LATEST 105 | gh release delete LEGACY 106 | gh release list 107 | 108 | - name: Upload to Release Legacy v1.1.X 109 | uses: softprops/action-gh-release@v0.1.15 110 | env: 111 | GITHUB_TOKEN: ${{ secrets.TOKEN }} 112 | with: 113 | name: Latest DAT files ${{ env.TAG_DATE }} (Legacy) 114 | tag_name: LEGACY 115 | body: Latest dat.gz files for GeoIP2Fast v1.1.X (LEGACY) with Maxmind Geolite2 database from ${{ env.TAG_DATE }}

**To download dat.gz files for GeoIP2Fast v1.2.X, go to tag [LATEST](https://github.com/rabuchaim/geoip2fast/releases/tag/LATEST)**. 116 | token: ${{ secrets.TOKEN }} 117 | files: | 118 | Maxmind-Geolite2-CSV_* 119 | IPv4_Coverage_* 120 | geoip2fast-legacy/geoip2fast/*.gz 121 | 122 | - name: Upload to Release Latest v1.2.X 123 | uses: softprops/action-gh-release@v0.1.15 124 | env: 125 | GITHUB_TOKEN: ${{ secrets.TOKEN }} 126 | with: 127 | name: Latest DAT files ${{ env.TAG_DATE }} 128 | tag_name: LATEST 129 | body: Latest dat.gz files for GeoIP2Fast v1.2.X (LATEST) with Maxmind Geolite2 database from ${{ env.TAG_DATE }}.

**To download dat.gz files for GeoIP2Fast v1.1.X, go to tag [LEGACY](https://github.com/rabuchaim/geoip2fast/releases/tag/LEGACY)**. 130 | token: ${{ secrets.TOKEN }} 131 | files: | 132 | Maxmind-Geolite2-CSV_* 133 | IPv4_Coverage_* 134 | geoip2fast/*.gz 135 | 136 | - name: Set LATEST release 137 | env: 138 | GH_TOKEN: ${{ github.token }} 139 | run: | 140 | gh release list 141 | gh release edit LATEST --latest 142 | gh release list 143 | 144 | permissions: 145 | contents: write 146 | discussions: write -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | .vscode 6 | .mypy_cache 7 | create_all_dat_files.sh 8 | geoip2fast-ipv4.dat.gz 9 | old* 10 | *egg-info/ 11 | dist/ 12 | build 13 | *turbo*.py 14 | *.tar.gz 15 | test*.py 16 | old*.py 17 | *old.py 18 | new*.py 19 | *new.py 20 | *2023*.gz 21 | *.json 22 | *.txt 23 | *tar.gz 24 | database-update.py 25 | *.php 26 | # C extensions 27 | *.so 28 | 29 | # Distribution / packaging 30 | .Python 31 | build/ 32 | develop-eggs/ 33 | dist/ 34 | downloads/ 35 | eggs/ 36 | .eggs/ 37 | lib/ 38 | lib64/ 39 | parts/ 40 | sdist/ 41 | var/ 42 | wheels/ 43 | share/python-wheels/ 44 | *.egg-info/ 45 | .installed.cfg 46 | *.egg 47 | MANIFEST 48 | 49 | # PyInstaller 50 | # Usually these files are written by a python script from a template 51 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 52 | *.manifest 53 | *.spec 54 | 55 | # Installer logs 56 | pip-log.txt 57 | pip-delete-this-directory.txt 58 | 59 | # Unit test / coverage reports 60 | htmlcov/ 61 | .tox/ 62 | .nox/ 63 | .coverage 64 | .coverage.* 65 | .cache 66 | nosetests.xml 67 | coverage.xml 68 | *.cover 69 | *.py,cover 70 | .hypothesis/ 71 | .pytest_cache/ 72 | cover/ 73 | 74 | # Translations 75 | *.mo 76 | *.pot 77 | 78 | # Django stuff: 79 | *.log 80 | local_settings.py 81 | db.sqlite3 82 | db.sqlite3-journal 83 | 84 | # Flask stuff: 85 | instance/ 86 | .webassets-cache 87 | 88 | # Scrapy stuff: 89 | .scrapy 90 | 91 | # Sphinx documentation 92 | docs/_build/ 93 | 94 | # PyBuilder 95 | .pybuilder/ 96 | target/ 97 | 98 | # Jupyter Notebook 99 | .ipynb_checkpoints 100 | 101 | # IPython 102 | profile_default/ 103 | ipython_config.py 104 | 105 | # pyenv 106 | # For a library or package, you might want to ignore these files since the code is 107 | # intended to run in multiple environments; otherwise, check them in: 108 | # .python-version 109 | 110 | # pipenv 111 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 112 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 113 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 114 | # install all needed dependencies. 115 | #Pipfile.lock 116 | 117 | # poetry 118 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 119 | # This is especially recommended for binary packages to ensure reproducibility, and is more 120 | # commonly ignored for libraries. 121 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 122 | #poetry.lock 123 | 124 | # pdm 125 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 126 | #pdm.lock 127 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 128 | # in version control. 129 | # https://pdm.fming.dev/#use-with-ide 130 | .pdm.toml 131 | 132 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 133 | __pypackages__/ 134 | 135 | # Celery stuff 136 | celerybeat-schedule 137 | celerybeat.pid 138 | 139 | # SageMath parsed files 140 | *.sage.py 141 | 142 | # Environments 143 | .env 144 | .venv 145 | env/ 146 | venv/ 147 | ENV/ 148 | env.bak/ 149 | venv.bak/ 150 | 151 | # Spyder project settings 152 | .spyderproject 153 | .spyproject 154 | 155 | # Rope project settings 156 | .ropeproject 157 | 158 | # mkdocs documentation 159 | /site 160 | 161 | # mypy 162 | .mypy_cache/ 163 | .dmypy.json 164 | dmypy.json 165 | 166 | # Pyre type checker 167 | .pyre/ 168 | 169 | # pytype static type analyzer 170 | .pytype/ 171 | 172 | # Cython debug symbols 173 | cython_debug/ 174 | 175 | # PyCharm 176 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 177 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 178 | # and can be added to the global gitignore or merged into this file. For a more nuclear 179 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 180 | #.idea/ 181 | geoip2fast.backup 182 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | .oPYo. o .oPYo. .oPYo. ooooo o 3 | 8 8 8 8 8 `8 8 8 4 | 8 .oPYo. .oPYo. 8 o8YooP' oP' o8oo .oPYo. .oPYo. o8P 5 | 8 oo 8oooo8 8 8 8 8 .oP' 8 .oooo8 Yb.. 8 6 | 8 8 8. 8 8 8 8 8' 8 8 8 'Yb. 8 7 | `YooP8 `Yooo' `YooP' 8 8 8ooooo 8 `YooP8 `YooP' 8 8 | :....8 :.....::.....:..:..:::::.......:..:::::.....::.....:::..: 9 | :::::8 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 10 | :::::..::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 11 | 12 | Author: Ricardo Abuchaim - ricardoabuchaim@gmail.com 13 | https://github.com/rabuchaim/geoip2fast/ 14 | 15 | License: MIT 16 | 17 | ############################################################################ 18 | What's new in v1.2.2 - 20/Jun/2024 19 | - DAT files updated with MAXMIND:GeoLite2-CSV_20240618 20 | - Removed the line "sys.tracebacklimit = 0" that was causing some problems 21 | in Django. This line is unnecessary (https://github.com/rabuchaim/geoip2fast/issues/10) 22 | - There are 2 reduced versions available for you to copy and paste 23 | into your own code without any dependencies and fast as always! 24 | Check these files in your library path: 25 | - geoip2fastmin.py (429 lines) 26 | - geoip2fastminified.py (183 lines) 27 | - As requested, 2 new methods to return a coverage of IPv4 and IPv6. 28 | def get_ipv4_coverage()->float 29 | def get_ipv6_coverage()->float 30 | - New function get_database_info() that returns a dictionary with 31 | detailed information about the data file currently in use. 32 | - Made some adjustments to the --missing-ips and --coverage functions. 33 | - Now you can specify the data filename to be used on geoip2fast cli: 34 | geoip2fast geoip2fast-ipv6.dat.gz --self-test 35 | geoip2fast 9.9.9.9,1.1.1.1,2a10:8b40:: geoip2fast-asn-ipv6.dat.gz 36 | - New functions to generate random IP addresses to be used in tests. 37 | Returns a list if more than 1 IP is requested, otherwise returns a 38 | string with only 1 IP address. If you request an IPv6 and the database 39 | loaded does not have IPv6 data, returns False. And the fuction of 40 | private address, returns an random IPv4 from network 10.0.0.0/8 or 41 | 172.16.0.0/12 or 192.168.0.0/16. 42 | def generate_random_private_address(self,num_ips=1)->string or a list 43 | def generate_random_ipv4_address(self,num_ips=1)->string or a list 44 | def generate_random_ipv6_address(self,num_ips=1)->string or a list 45 | - Removed functools.lru_cache. It is very useful when you have a function 46 | that is repeated several times but takes a long time, which is not the 47 | case of GeoIP2Fast where functions take milliseconds. On each call, 48 | functools checks whether the value is already cached or not, and this 49 | takes time. And we noticed that without functools and using the processor 50 | and operating system's own cache makes GeoIP2Fast much faster without it 51 | even if you are searching for an IP for the first time. 52 | If you want to use lru_cache, you can uncomment the respective lines 53 | of code. There are 5 lines commented with @functools.lru_cache 54 | - Put some flowers 55 | 56 | What's new in v1.2.1 - 01/Dec/2023 57 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231201 58 | - Improved speed! faster than ever! 59 | - Automatic updates! you can update to the newest dat.gz file via command line or via code 60 | 61 | What's new in v1.2.0 - 27/Nov/2023 62 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231124 63 | - CITY NAMES SUPPORT! We are fast and complete! beard, hair and mustache! 64 | - To don´t increase the package size, the CITY files were 65 | not included in the PyPI installation. If you need city names support 66 | with/without ASN or with IPv6, you have to create by your own OR download at 67 | https://github.com/rabuchaim/geoip2fast/releases/latest 68 | - Significant changes in geoip2dat.py file. Read the new instructions! 69 | - Data files generated with version 1.1.X will no longer work in versions 1.2.X 70 | Legacy dat.gz files will continue to be created and made available twice a week 71 | at the URL https://github.com/rabuchaim/geoip2fast/releases/tag/LEGACY_v1.1.9 72 | - Changes to the "source info" information of geoip2fast.py files. Nothing for 73 | worry unless you use this information. The --source-info option 74 | still working, but it is hidden in the geoip2dat.py menu. 75 | - New property "asn_cidr" 76 | - Fix in memory usage under MacOS 77 | - When trying to load a non-existent file it now raise an exception. Previously, 78 | the default file was loaded and no message was displayed. 79 | - a new method to return the path of the dat.gz file that is currently being used 80 | from geoip2fast import GeoIP2Fast 81 | G = GeoIP2Fast(geoip2fast_data_file="/tmp/geoip2fast-asn.dat.gz") 82 | G.get_database_path() 83 | 84 | What's new in v1.1.10 - 01/Dec/2023 (LEGACY) 85 | - Automatic download of dat.gz files. 86 | try: geoip2fast --download-all 87 | 88 | What's new in v1.1.9 - 22/Nov/2023 89 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231121 90 | - Fix in memory usage under MacOS 91 | - Fix a problem when loading specific datafiles 92 | - a new method to return the path of the dat.gz file that is currently being used 93 | from geoip2fast import GeoIP2Fast 94 | G = GeoIP2Fast(geoip2fast_data_file="/tmp/geoip2fast-asn.dat.gz") 95 | G.get_database_path() 96 | 97 | What's new in v1.1.8 - 14/Nov/2023 98 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231114 99 | - Fix in './geoip2fast.py --coverage' test when using IPv6 database 100 | 101 | What's new in v1.1.6 - 10/Nov/2023 102 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231110 103 | 104 | What's new in v1.1.5 - 03/Nov/2023 105 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231103 106 | - Fully tested with Python 3.12 and Python 3.13 (> 150.000 lookups/sec) 107 | - Fixed an issue in the function that adjusts the terminal window of the 108 | geoip2dat.py file. This problem prevented the geoip2dat.py script from 109 | being executed by crontab. 110 | - Added entry_points to setup.py, now it's possible to run geoip2fast and 111 | geoip2dat as an executable under Windows. If it doesn't work, you need 112 | to add the path of your python scripts directory to your PATH environment 113 | variable. 114 | - To see the path of your scripts directory on win32, run: pip show geoip2fast 115 | - Check the "Location" information, and change the "site-packages" word 116 | to "scripts" and add this path to your PATH environment variable (google it). 117 | - After this change, you can run geoip2fast and geoip2dat from any path 118 | of your Windows command prompt. Sometimes this change is unnecessary, 119 | try running 'geoip2fast' from any path of your Win32 command prompt. 120 | 121 | What's new in v1.1.4 - 27/Oct/2023 122 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231027 123 | 124 | What's new in v1.1.3 - 20/Oct/2023 125 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231020 126 | - Bug fix in the coverage test of v1.1.2. Didn´t affect the search accuracy. 127 | 128 | What's new in v1.1.2 - 03/Oct/2023 129 | - DAT files updated with MAXMIND:GeoLite2-Country-ASN-CSV_20231003 130 | - IPv6 transparent support!!! the same class, you just need to choose 131 | which data file you wanna use. 132 | - fast as always! the lookup speed is the same for any data file. 133 | - removed some useless code and put some colored flowers. 134 | 135 | What's new in v1.0.6 - 01/Oct/2023 136 | - geoip2fast.dat.gz updated with MAXMIND:GeoLite2-Country-CSV_20230929 137 | - geoip2fast-asn.dat.gz updated with MAXMIND:GeoLite2-ASN-CSV_20230929 138 | - bug fix: Fail on Windows while getting a memory usage 139 | - bug fix: Error when specifying the data file manually 140 | 141 | What's new in v1.0.5 - 20/Sep/2023 142 | - geoip2fast.dat.gz updated with MAXMIND:GeoLite2-Country-ASN-CSV_20230919 143 | - faster than ever!!! a lookup around ~0.00001 144 | - A new option in geoip2dat to create .dat.gz with asn support: --asn-dir 145 | - ASN support - the dat file already has support for ASN of the network 146 | ranges. The footprint was incresed to 64Mb. The speed is the same. 147 | - If you want, you can create an another dat file only with country data, 148 | just use the option --country-dir without the option --asn-dir 149 | - geoip2dat updated too! older versions won't work anymore. Sorry. 150 | - more flowers 151 | 152 | What's new in v1.0.4 - 13/Sep/2023 153 | - geoip2fast.dat.gz updated with MAXMIND:GeoLite2-Country-CSV_20230912 154 | - fix in search of IPs that end in ".0" 155 | - fix in _locate_database() function that search dat.gz file in 156 | $current_application_file path and library path 157 | - added some cli parameters: --speed-test, --self-test, and --coverage 158 | ( try: ./geoip2fast.py --coverage -v to see all networks included in 159 | dat file. ) 160 | - added a parameter in tests: --missing-ips (take care, uses 100% of CPU) 161 | - geoip2dat updated too! older versions won't work anymore. Sorry. 162 | - more flowers 163 | 164 | What's new in v1.0.3 - 08/Sep/2023 165 | - geoip2fast.dat.gz updated with MAXMIND:GeoLite2-Country-CSV_20230908 166 | - IMPROVED SPEED!! >100.000 LOOKUPS PER SECOND! GeoIP2Flash! 167 | - geoip2fast.dat.gz decreased to ONE MEGABYTE! (-60%) 168 | - RAM footprint dropped to 25 MiB! (-50%) 169 | - load time around ~0,05 seconds! (-50%) 170 | - the number of networks and content still the same, we just 171 | converted all data to integers and sliced the lists a lot! This 172 | was necessary to implement the ASN data (not yet). 173 | - geoip2dat updated to create the new dat file structure 174 | - inserted a tag in dat file to record the version. 175 | - as requested, property elapsed_time_hostname for hostnames lookups 176 | 177 | What's new in v1.0.2 - 04/Sep/2023 178 | - geoip2fast.dat.gz updated with MAXMIND:GeoLite2-Country-CSV_20230901 179 | - fully tested with Python 3.11.5. Much faster than 3.10.12 >100K lookups/sec. 180 | - fix encoding of pp_json() method. Now it's showing all chars as it is. 181 | - in verbose mode it is now showing the memory footprint. 182 | - new test files at /usr/local/lib/python3.10/dist-packages/geoip2fast/tests/ 183 | - new class CIDRDetail will be used to create gepip2fast.dat file 184 | - geoip2dat - a script to import Maxmind-Country-CSV into geoip2fast.dat.gz. 185 | You can update your geoip2fast.dat.gz file whenever you want. It should work 186 | with paid version also. Please let me know if there are any problems. 187 | - put some flowers; 188 | 189 | What's new in v1.0.1 - 1º/Sep/2023 190 | - geoip2fast.dat.gz updated with MAXMIND:GeoLite2-Country-CSV_20230901 191 | - improved speed in >20%! removed ipaddress module. Now we do some IP calcs. 192 | - new methods to set the error code for the situations PRIVATE NETWORKS and for 193 | NETWORKS NOT FOUND: 194 | GeoIP2Fast.set_error_code_private_networks(new_value) 195 | GeoIP2Fast.set_error_code_network_not_found(new_value) 196 | - new method to calculate the current speed. Returns a value of current lookups per 197 | seconds or print a formatted result: 198 | GeoIP2Fast.calculate_speed(print_result=True) 199 | - new method to calculate how many IPv4 of all internet are covered by geoip2fast.dat 200 | file. Returns a percentage relative to all possible IPv4 on the internet or print a 201 | formatted result. Useful to track the changes in getip2fast.dat.gz file: 202 | GeoIP2Fast.calculate_coverage(print_result=True) 203 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | ricardoabuchaim@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | 130 | GeoIP2Fast v1.2.2 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Ricardo Abuchaim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GeoIP2Fast v1.2.2 2 | 3 | GeoIP2Fast is the fastest GeoIP2 country/city/asn lookup library. A search takes less than 0.00003 seconds. It has its own data file updated with Maxmind-Geolite2-CSV, supports IPv4 and IPv6, works on Python3, Pypy3 and is Pure Python! 4 | 5 | With it´s own datafile (geoip2fast.dat.gz), can be loaded into memory in ~0.07 seconds and has a small footprint for all data, so you don´t need to make requests to any webservices or connect to an external database. And it works on Linux, Windows and MacOS! 6 | 7 | And now you can download .dat.gz from your own application or via the command line. See more at [Update your database automatically](#automatic-update-of-datgz-files) 8 | 9 | GeoIP2Fast returns ASN NAME, COUNTRY ISO CODE, COUNTRY NAME, CITY NAME/STATE/DISTRICT and CIDR. There is no external dependencies, you just need the ```geoip2fast.py``` file and the desired data file ```.dat.gz```. The lookup speed is the same for any data file. 10 | 11 | ### Country and ASN databases with IPv4 and IPv6 12 | 13 | | Download Content | File Name | File Size | Load Time | RAM Footprint | 14 | | ---------- | :---------: | ---------: | --------: | ---------: | 15 | | [Country IPv4](https://github.com/rabuchaim/geoip2fast/releases/latest/download/geoip2fast.dat.gz) | ```geoip2fast.dat.gz``` | 1.2 MiB | ~0.04 sec | ~22.0 MiB | 16 | | [Country IPv4+IPv6](https://github.com/rabuchaim/geoip2fast/releases/latest/download/geoip2fast-ipv6.dat.gz) | ```geoip2fast-ipv6.dat.gz``` | 1.8 MiB | ~0.07 sec | ~41.0 MiB | 17 | | [Country+ASN IPv4](https://github.com/rabuchaim/geoip2fast/releases/latest/download/geoip2fast-asn.dat.gz) | ```geoip2fast-asn.dat.gz``` | 2.3 MiB | ~0.08 sec | ~40.0 MiB | 18 | | [Country+ASN IPv4+IPv6](https://github.com/rabuchaim/geoip2fast/releases/latest/download/geoip2fast-asn-ipv6.dat.gz) | ```geoip2fast-asn-ipv6.dat.gz``` | 3.1 MiB | ~0.15 sec | ~97.0 MiB | 19 | 20 | ### City and ASN databases with IPv4 and IPv6 21 | 22 | City databases are not included in the pip installation package, if you want to: 23 | - download them manually from [DAT Files LATEST Release](https://github.com/rabuchaim/geoip2fast/releases/LATEST) 24 | - or create them yourself using ```geoip2dat.py```. *City data already includes country data.* 25 | - or download using the new function GeoIP2Fast.update_all() [read more below](#automatic-update-of-datgz-files) 26 | 27 | | Download Content | File Name | File Size | Load Time | RAM Footprint | 28 | | ---------- | :---------: | ---------: | --------: | ---------: | 29 | | [City IPv4](https://github.com/rabuchaim/geoip2fast/releases/latest/download/geoip2fast-city.dat.gz) | ```geoip2fast-city.dat.gz``` | 12 MiB | ~0.50 sec | ~270.0 MiB | 30 | | [City IPv4+IPv6](https://github.com/rabuchaim/geoip2fast/releases/latest/download/geoip2fast-city-ipv6.dat.gz) | ```geoip2fast-city-ipv6.dat.gz``` | 14 MiB | ~0.60 sec | ~400.0 MiB | 31 | | [City+ASN IPv4](https://github.com/rabuchaim/geoip2fast/releases/latest/download/geoip2fast-city-asn.dat.gz) | ```geoip2fast-city-asn.dat.gz``` | 13 MiB | ~0.60 sec | ~310.0 MiB | 32 | | [City+ASN IPv4+IPv6](https://github.com/rabuchaim/geoip2fast/releases/latest/download/geoip2fast-city-asn-ipv6.dat.gz) | ```geoip2fast-city-asn-ipv6.dat.gz``` | 17 MiB | ~0.70 sec | ~430.0 MiB | 33 | 34 | > *If you are using pypy3, the loading time will increase a little, but the speed will be a little faster and the memory footprint will be lower.* 35 | 36 | **The new v1.2.X databases are not compatible with versions equal to or older than v1.1.X, and vice versa, obviously. The last version with support only for countries and ASN only is v1.1.X, which is still available with this command line: ```pip install geoip2fast==1.1.10```.** 37 | 38 | ``` 39 | What's new in v1.2.2 - 20/Jun/2024 40 | - DAT files updated with MAXMIND:GeoLite2-CSV_20240618 41 | - Removed the line "sys.tracebacklimit = 0" that was causing some problems 42 | in Django. This line is unnecessary (https://github.com/rabuchaim/geoip2fast/issues/10) 43 | - Maxmind inserted a new field into the CSV files called "is_anycast", and this broke 44 | geoip2dat.py CSV reader. Insertion of the new field in the list of "fields" of 45 | the CSV reader that generates the .dat.gz files so that they can be updated. 46 | 47 | - There are 2 reduced versions available for you to copy and paste 48 | into your own code without any dependencies and fast as always! 49 | Check these files in your library path: 50 | - geoip2fastmin.py (429 lines) 51 | - geoip2fastminified.py (183 lines) 52 | It's in testing, but please open an issue if you have any problems 53 | with either of these 2 versions. 54 | 55 | - Removed functools.lru_cache. It is very useful when you have a function 56 | that is repeated several times but takes a long time, which is not the 57 | case of GeoIP2Fast where functions take milliseconds. On each call, 58 | functools checks whether the value is already cached or not, and this 59 | takes time. And we noticed that without functools and using the processor 60 | and operating system's own cache makes GeoIP2Fast much faster without it 61 | even if you are searching for an IP for the first time. 62 | You can run tests and you will see that without functools.lru_cache 63 | it is faster. If you want to use lru_cache, you can uncomment the 64 | respective lines of code. There are 5 lines commented with @functools.lru_cache 65 | 66 | - As requested, 2 new methods to return a coverage of IPv4 and IPv6. 67 | def get_ipv4_coverage()->float 68 | def get_ipv6_coverage()->float 69 | 70 | - New function get_database_info() that returns a dictionary with 71 | detailed information about the data file currently in use. 72 | 73 | - Made some adjustments to the --missing-ips and --coverage functions. 74 | 75 | - Now you can specify the data filename to be used on geoip2fast cli: 76 | geoip2fast geoip2fast-ipv6.dat.gz --self-test 77 | geoip2fast 9.9.9.9,1.1.1.1,2a10:8b40:: geoip2fast-asn-ipv6.dat.gz 78 | 79 | - New functions to generate random IP addresses to be used in tests. 80 | Returns a list if more than 1 IP is requested, otherwise returns a 81 | string with only 1 IP address. If you request an IPv6 and the database 82 | loaded does not have IPv6 data, returns False. And the fuction of 83 | private address, returns an random IPv4 from network 10.0.0.0/8 or 84 | 172.16.0.0/12 or 192.168.0.0/16. 85 | def generate_random_private_address(self,num_ips=1)->string or a list 86 | def generate_random_ipv4_address(self,num_ips=1)->string or a list 87 | def generate_random_ipv6_address(self,num_ips=1)->string or a list 88 | 89 | - Put some flowers 90 | 91 | ``` 92 |
93 | 94 | ![](https://raw.githubusercontent.com/rabuchaim/geoip2fast/main/images/geoip2fast_selftest.jpg) 95 | 96 |
97 | 98 | ## Installation 99 | ```bash 100 | pip install geoip2fast 101 | ``` 102 |
103 | 104 | ## DAT files updates 105 | 106 | - You can create your own dat.gz file using [geoip2dat.py](#geoip2dat---update-geoip2fastdatgz-file-anytime) file. 107 | - You can also [download the latest dat files](https://github.com/rabuchaim/geoip2fast/releases/tag/LATEST) that are updated automatically on Tuesdays and Fridays 108 | - And you can [update the dat files downloading from our releases repository](#automatic-update-of-datgz-files), via code or via command line. 109 | 110 |
111 | 112 | ## How does it work? 113 | 114 | GeoIP2Fast has 4 datafiles included. Tha main file is ```geoip2fast.dat.gz``` with support Country lookups and only IPv4. Usually, these files are located into the library directory (```/usr/local/lib/python3.XX/dist-packages/geoip2fast```), but you can place it into the same directory of your application. The library automatically checks both paths, And the directory of your application overlaps the directory of the library. You can use an specific location also. 115 | 116 | The ```bisect()``` function is used together with some ordered lists of integers to search the Network/CountryCode (Yes! an IP address has an integer representation, try to ping this number: ```ping 134744072``` or this ```ping 2130706433``` ). 117 | 118 | If GeoIP2Fast does not have a network IP address that was requested, a "not found in database" error will be returned. Unlike many other libraries that when not finding a requested network, gives you the geographical location of the network immediately below. The result is not always correct. 119 | 120 | Data file updates are made available twice a week in our [github releases](https://github.com/rabuchaim/geoip2fast/releases/latest). Files with City data and IPv6 support can also be downloaded there. 121 | 122 | There are network gaps in the files we use as a source of data, and these missing networks are probably addresses that those responsible have not yet declared their location. Of all IPv4 on the internet (almost 4.3 billions IPs), our coverage is around 99.64% and we do not have information on approximately 15 million of them (~0,36%). It must be remembered that the geographical accuracy is the responsibility of the network block owners. If the owner (aka ASN) of the XXX.YYY.ZZZ.D/24 network range declares that his network range is located at "Foo Island", we must believe that an IP address of that network is there. 123 | 124 | > *Don't go to Foo Island visit a girl you met on the internet just because you looked up her IP on GeoIP2Fast and the result indicated that she is there.* 125 | 126 |
127 | 128 | ## Quick Start 129 | 130 | Once the object is created, GeoIP2Fast loads automatically all needed data into memory. The lookup function returns an object called ```GeoIPDetail```. And you can get the values of it's properties just calling the name of proprerty: ```result.ip```, ```result.country_code```, ```result.country_name```, ```result.city.name```, ```result.city.subsubdivision_code```, ```result.city.subsubdivision_name```, ```result.cidr```, ```result.is_private```, ```result.asn_name```,```result.asn_cidr``` and ```result.elapsed_time```. Or use the function ```to_dict()``` to get the result as a dict. You can get values like ```result.to_dict()['country_code']``` 131 | 132 | When using it as a Python library, if you want to load a file other than ```geoip2fast.dat.gz```, you simply create the object by typing the name of the file you want to work with. The library first looks for the given file in the current directory and then in the library directory. If desired, you can directly specify the path. If don´t specify any file, the default ```geoip2fast.dat.gz``` will be used. Example: 133 | 134 | ```python 135 | >>> from geoip2fast import GeoIP2Fast 136 | >>> geoip = GeoIP2Fast(geoip2fast_data_file="/my_data_files_path/geoip2fast-city-asn-ipv6.dat.gz",verbose=True) 137 | GeoIP2Fast v1.2.1 is ready! geoip2fast-city-asn-ipv6.dat.gz loaded with 4.488.721 networks in 0.54506 seconds and using 426.28 MiB. 138 | >>> print(geoip.lookup("191.251.128.1").pp_json()) 139 | { 140 | "ip": "191.251.128.1", 141 | "country_code": "BR", 142 | "country_name": "Brazil", 143 | "city": { 144 | "name": "Araraquara", 145 | "subdivision_code": "SP", 146 | "subdivision_name": "Sao Paulo" 147 | }, 148 | "cidr": "191.251.128.0/20", 149 | "hostname": "", 150 | "asn_name": "TELEFONICA BRASIL S.A", 151 | "asn_cidr": "191.250.0.0/15", 152 | "is_private": false, 153 | "elapsed_time": "0.000147356 sec" 154 | } 155 | >>> geoip.get_database_path() 156 | '/my_data_files_path/geoip2fast-city-asn-ipv6.dat.gz' 157 | >>> 158 | >>> result = geoip.lookup("3.2.35.65") 159 | >>> result.city.name 160 | 'São Paulo' 161 | >>> result.city.subdivision1_code 162 | 'SP' 163 | >>> result.country_name 164 | 'Brazil' 165 | >>> result.country_code 166 | 'BR' 167 | >>> result.asn_name 168 | 'AMAZON-02' 169 | >>> result.elapsed_time 170 | '0.000011612 sec' 171 | >>> 172 | >>> result = geoip.lookup("10.20.30.40") 173 | >>> result.country_name 174 | 'Private Network Class A' 175 | >>> result.cidr 176 | '10.0.0.0/8' 177 | >>> result.is_private 178 | True 179 | >>> 180 | ``` 181 | 182 | 183 | ```python 184 | from geoip2fast import GeoIP2Fast 185 | 186 | GEOIP = GeoIP2Fast() 187 | result = GEOIP.lookup("200.204.0.10") 188 | print(result) 189 | 190 | # to use the country_code property 191 | print(result.country_code) 192 | 193 | # to print the ASN name property 194 | print(result.asn_name) 195 | 196 | # Before call the function get_hostname(), the property hostname will always be empty. 197 | print("Hostname: "+result.hostname) 198 | result.get_hostname() 199 | print("Hostname: "+result.hostname) 200 | 201 | # to work with output as a dict, use the function to_dict() 202 | print(result.to_dict()['country_code'],result.to_dict()['country_name']) 203 | 204 | # to check the date of the CSV files used to create the .dat file 205 | print(GEOIP.get_source_info()) 206 | 207 | # to show the path of current loaded dat file 208 | print(GEOIP.get_database_path()) 209 | 210 | # info about internal cache 211 | print(GEOIP.cache_info()) 212 | 213 | # clear the internal cache 214 | print(GEOIP.clear_cache()) 215 | 216 | # to see the difference after clear cache 217 | print(GEOIP.cache_info()) 218 | ``` 219 | There is a method to pretty print the result as json.dumps(): 220 | ```python 221 | >>> result = MyGeoIP.lookup("191.251.128.1") 222 | >>> print(result.pp_json()) 223 | { 224 | "ip": "191.251.128.1", 225 | "country_code": "BR", 226 | "country_name": "Brazil", 227 | "city": { 228 | "name": "Araraquara", 229 | "subdivision_code": "SP", 230 | "subdivision_name": "Sao Paulo" 231 | }, 232 | "cidr": "191.251.128.0/20", 233 | "hostname": "", 234 | "asn_name": "TELEFONICA BRASIL S.A", 235 | "asn_cidr": "191.250.0.0/15", 236 | "is_private": false, 237 | "elapsed_time": "0.000147356 sec" 238 | } 239 | ``` 240 | or simply: ```result.pp_json(print_result=True)``` 241 | 242 | To see the start-up line without set ```verbose=True``` : 243 | ```python 244 | >>> from geoip2fast import GeoIP2Fast 245 | >>> G = GeoIP2Fast() 246 | >>> G.startup_line_text 247 | 'GeoIP2Fast v1.2.0 is ready! geoip2fast.dat.gz loaded with 459.270 networks in 0.03393 seconds and using 25.25 MiB.' 248 | >>> 249 | ``` 250 | 251 | Private/Reserved networks were included in the database just to be able to provide an answer if one of these IPs is searched. When it happens, the country_code will return "--", the "network name" will be displayed in the country_name and the range of that network will be displayed in the cidr property, and the property **is_private** is setted to **True**. 252 | 253 | ```python 254 | >>> from geoip2fast import GeoIP2Fast 255 | >>> geoip = GeoIP2Fast(verbose=True) 256 | 'GeoIP2Fast v1.2.0 is ready! geoip2fast.dat.gz loaded with 459.270 networks in 0.03393 seconds and using 25.25 MiB.' 257 | >>> 258 | >>> G.lookup("10.20.30.40") 259 | {'ip': '10.20.30.40', 'country_code': '--', 'country_name': 'Private Network Class A', 'cidr': '10.0.0.0/8', 'hostname': '', 'asn_name': 'IANA.ORG', 'asn_cidr': '10.0.0.0/8', 'is_private': True, 'elapsed_time': '0.000015946 sec'} 260 | >>> G.lookup("169.254.10.20") 261 | {'ip': '169.254.10.20', 'country_code': '--', 'country_name': 'APIPA Automatic Priv.IP Addressing', 'cidr': '169.254.0.0/16', 'hostname': '', 'asn_name': 'IANA.ORG', 'asn_cidr': '169.254.0.0/16', 'is_private': True, 'elapsed_time': '0.000093106 sec'} 262 | >>> 263 | ``` 264 | 265 | You can change the behavior of what will be returned in country_code property of "private networks" and for "networks not found": 266 | 267 | ```python 268 | >>> from geoip2fast import GeoIP2Fast 269 | >>> G = GeoIP2Fast() 270 | >>> G.set_error_code_private_networks("@@") 271 | '@@' 272 | >>> G.lookup("10.20.30.40") 273 | {'ip': '10.20.30.40', 'country_code': '@@', 'country_name': 'Private Network Class A', 'cidr': '10.0.0.0/8', 'hostname': '', 'asn_name': 'IANA.ORG', 'asn_cidr': '10.0.0.0/8', 'is_private': True, 'elapsed_time': '0.000144236 sec'} 274 | >>> 275 | >>> G.set_error_code_network_not_found("##") 276 | '##' 277 | >>> G.lookup("23.142.129.0") 278 | {'ip': '23.142.129.0', 'country_code': '##', 'country_name': '', 'cidr': '', 'hostname': '', 'asn_name': '', 'asn_cidr': '', 'is_private': False, 'elapsed_time': '0.000012352 sec'} 279 | >>> 280 | ``` 281 | You can use it as a CLI also from any path: 282 | 283 | ```bash 284 | # geoip2fast 285 | GeoIP2Fast v1.2.0 Usage: geoip2fast.py [-h] [-v] [-d] ,,,... 286 | # geoip2fast 9.9.9.9,15.20.25.30 -d 287 | { 288 | "ip": "9.9.9.9", 289 | "country_code": "US", 290 | "country_name": "United States", 291 | "cidr": "9.9.9.9/32", 292 | "hostname": "dns9.quad9.net", 293 | "asn_name": "", 294 | "asn_cidr": "", 295 | "is_private": false, 296 | "elapsed_time": "0.000034214 sec", 297 | "elapsed_time_hostname": "0.001079759 sec" 298 | } 299 | { 300 | "ip": "15.20.25.30", 301 | "country_code": "US", 302 | "country_name": "United States", 303 | "cidr": "15.0.0.0/10", 304 | "hostname": "", 305 | "asn_name": "", 306 | "asn_cidr": "", 307 | "is_private": false, 308 | "elapsed_time": "0.000022537 sec" 309 | } 310 | # geoip2fast "2.3.4.5, 4.5.6.7, 8.9.10.11" | jq -r '.country_code' 311 | FR 312 | US 313 | US 314 | # geoip2fast 8.8.8.8,1.1.1.1,200.204.0.10 -d | jq -r '.hostname' 315 | dns.google 316 | one.one.one.one 317 | resolver1.telesp.net.br 318 | ``` 319 | 320 | ## How fast is it? 321 | 322 | With an virtual machine with 1 CPU and 4Gb of RAM, we have lookups **lower than 0,00003 seconds**. And if the lookup still in library´s internal cache, the elapsed time goes down to 0,000003 seconds. **GeoIP2Fast can do more than 100K queries per second, per core**. It takes less than 0,07 seconds to load the datafile into memory and get ready to lookup. Use ```verbose=True``` to create the object GeoIP2Fast to see the spent time to start. 323 | 324 | Now some tests are included in the geoip2fast.py file. 325 | 326 | ```bash 327 | # geoip2fast -h 328 | GeoIP2Fast v1.2.2 Usage: geoip2fast.py [-h] [-v] [-d] ,,,... 329 | 330 | Tests parameters: 331 | --self-test Starts a self-test with some randomic IP addresses. 332 | --self-test-city Starts a self-test with some randomic IP addresses and with city names support. 333 | --speed-test Do a speed test with 1 million on randomic IP addresses. 334 | --random-test Start a test with 1.000.000 of randomic IPs and calculate a lookup average time. 335 | 336 | --coverage [-v] Shows a statistic of how many IPs are covered by current dat file. 337 | --missing-ips [-v] Print all IP networks that doesn't have geo information (only for IPv4). 338 | 339 | Automatic update: 340 | --update-all [-v] Download all dat.gz files available in the repository below: 341 | https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/ 342 | 343 | --update-file [-v] 344 | Download a specific filename from the repository. Only one file is allowed. 345 | Allowed values are: geoip2fast.dat.gz OR geoip2fast-ipv6.dat.gz OR 346 | geoip2fast-asn.dat.gz OR geoip2fast-asn-ipv6.dat.gz OR 347 | geoip2fast-city.dat.gz OR geoip2fast-city-ipv6.dat.gz OR 348 | geoip2fast-city-asn.dat.gz OR geoip2fast-city-asn-ipv6.dat.gz 349 | 350 | --dest [-v] 351 | Specify the destination directory for the downloaded files. When combined with 352 | the '--update-file' parameter, you can specify an existing directory, with or 353 | without a file name, or you can just specify a file name. In the absence of 354 | this parameter or directory information, the library directory will be used 355 | as default value. This parameter is optional. The filename must end 356 | with .dat.gz extension. 357 | 358 | Use the verbose parameter (-v) if you want to see the download progress, 359 | otherwise there will be no output. You also have to use this parameter 360 | to view possible errors in your console. 361 | 362 | More options: 363 | -d Resolve the DNS of given IP address. 364 | -h Show this help text. 365 | -v Verbose mode. 366 | -vvv Shows the location of current dat file in use. 367 | ``` 368 | 369 | ```geoip2fast --self-test``` 370 | ```bash 371 | # geoip2fast --self-test 372 | GeoIP2Fast v1.2.0 is ready! geoip2fast.dat.gz loaded with 459.270 networks in 0.09268 seconds and using 71.04 MiB. 373 | 374 | Starting a self-test... 375 | 376 | > 266.266.266.266 [0.000018189 sec] Cached > [0.000003038 sec] 377 | > 192,0x0/32 [0.000002468 sec] Cached > [0.000001868 sec] 378 | > 10.20.30.40 -- Private Network Class A [0.000020032 sec] Cached > [0.000001231 sec] IANA.ORG 379 | > 38.174.13.98 CA Canada [0.000015079 sec] Cached > [0.000001414 sec] AS-NSL-261 380 | > 102.195.139.98 -- [0.000004650 sec] Cached > [0.000000471 sec] 381 | > 139.5.38.161 IN India [0.000012635 sec] Cached > [0.000000845 sec] Five network Broadband Solution Pvt Ltd 382 | > 4.219.112.222 NO Norway [0.000009926 sec] Cached > [0.000000727 sec] MICROSOFT-CORP-MSN-AS-BLOCK 383 | > 137.44.167.24 GB United Kingdom [0.000009575 sec] Cached > [0.000000663 sec] Jisc Services Limited 384 | > 28.92.226.103 US United States [0.000009599 sec] Cached > [0.000000605 sec] DNIC-AS-00749 385 | > 180.115.120.126 CN China [0.000010468 sec] Cached > [0.000000702 sec] Chinanet 386 | (.....) 387 | ``` 388 | 389 | ```geoip2fast --speed-test``` 390 | ```bash 391 | # geoip2fast --speed-test 392 | GeoIP2Fast v1.2.0 is ready! geoip2fast.dat.gz loaded with 459.270 networks in 0.09292 seconds and using 71.05 MiB. 393 | 394 | Calculating current speed... wait a few seconds please... 395 | 396 | Current speed: 143159.49 lookups per second (1.000.000 IPs with an average of 0.000006985 seconds per lookup) [6.98522 sec] 397 | ``` 398 | 399 | ```geoip2fast --coverage``` 400 | ```bash 401 | # geoip2fast --coverage 402 | GeoIP2Fast v1.2.0 is ready! geoip2fast.dat.gz loaded with 4.488.723 networks in 0.47685 seconds and using 408.66 MiB. 403 | 404 | Use the parameter '-v' to see all networks included in your /opt/geoip2fast/geoip2fast/geoip2fast.dat.gz file. 405 | 406 | Current IPv4 coverage: 99.64% (4.279.552.038 IPv4 in 3.246.406 networks) [0.59747 sec] 407 | Current IPv6 coverage: 0.40% (1.364.444.943.175.748.876.962.337.373.462.462.464 IPv6 in 1.242.317 networks) [0.59747 sec] 408 | ``` 409 | ```geoip2fast --coverage -v``` 410 | ```bash 411 | # geoip2fast --coverage -v 412 | GeoIP2Fast v1.2.0 is ready! geoip2fast.dat.gz loaded with 753.871 networks in 0.13392 seconds and using 103.54 MiB. 413 | 414 | Use the parameter '-v' to see all networks included in your /usr/local/lib/python3.11/dist-packages/geoip2fast/geoip2fast.dat.gz file. 415 | 416 | - Network: 0.0.0.0/8 IPs: 16777216 -- Reserved for self identification 0.000042983 sec 417 | - Network: 1.0.0.0/24 IPs: 256 AU Australia 0.000017615 sec 418 | - Network: 1.0.1.0/24 IPs: 256 CN China 0.000011633 sec 419 | - Network: 1.0.2.0/23 IPs: 512 CN China 0.000009464 sec 420 | - Network: 1.0.4.0/22 IPs: 1024 AU Australia 0.000008925 sec 421 | (.....) 422 | - Network: 2c0f:ffc0::/32 IPs: 79228162514264337593543950336 ZA South Africa 0.000026057 sec 423 | - Network: 2c0f:ffc8::/32 IPs: 79228162514264337593543950336 ZA South Africa 0.000028565 sec 424 | - Network: 2c0f:ffd0::/32 IPs: 79228162514264337593543950336 ZA South Africa 0.000026283 sec 425 | - Network: 2c0f:ffd8::/32 IPs: 79228162514264337593543950336 ZA South Africa 0.000026832 sec 426 | - Network: 2c0f:ffe8::/32 IPs: 79228162514264337593543950336 NG Nigeria 0.000027137 sec 427 | - Network: 2c0f:fff0::/32 IPs: 79228162514264337593543950336 NG Nigeria 0.000028008 sec 428 | - Network: fd00::/8 IPs: 1329227995784915872903807060280344576 -- Reserved for Unique Local Addresses 0.000031203 sec 429 | 430 | Current IPv4 coverage: 99.64% (4.279.420.966 IPv4 in 459.270 networks) [64.13669 sec] 431 | Current IPv6 coverage: 0.40% (1.364.444.943.175.748.876.962.337.373.462.462.464 IPv6 in 294.601 networks) [64.13669 sec] 432 | ``` 433 | 434 | ```geoip2fast --missing-ips``` 435 | ```bash 436 | # geoip2fast --missing-ips 437 | GeoIP2Fast v1.2.0 is ready! geoip2fast.dat.gz loaded with 753.871 networks in 0.13392 seconds and using 103.54 MiB. 438 | 439 | Searching for missing IPs... 440 | 441 | From 1.34.65.179 to 1.34.65.179 > Network 1.34.65.180/32 > Missing IPs: 1 442 | From 1.46.23.235 to 1.46.23.235 > Network 1.46.23.236/32 > Missing IPs: 1 443 | From 2.12.211.171 to 2.12.211.171 > Network 2.12.211.172/32 > Missing IPs: 1 444 | (.....) 445 | From 220.158.148.0 to 220.158.151.255 > Network 220.158.152.0/22 > Missing IPs: 1024 446 | From 223.165.0.0 to 223.165.3.255 > Network 223.165.4.0/22 > Missing IPs: 1024 447 | 448 | >>> Valid IP addresses without geo information: 15.551.441 (0.36% of all IPv4) [27.17811 sec] 449 | ``` 450 | > Some IPs are excluded as described in page "Do Not Sell My Personal Information Requests" at Maxmind website. 451 | 452 |
453 | 454 | ## GeoIP2Dat - update geoip2fast.dat.gz file anytime 455 | 456 | The updates of geoip2fast.dat.gz file will be published twice a week on Github https://github.com/rabuchaim/geoip2fast/releases/LATEST. You can also create your own dat file whenever you want, see instructions below. 457 | 458 | Download the Geolite2 (COUNTRY, CITY and ASN) CSV files from Maxmind website and place it into some diretory (in this example, was placed into ```/opt/maxmind/```). Extract all zip files in this directory and run ```geoip2dat``` to see the options. 459 | 460 | ![](https://raw.githubusercontent.com/rabuchaim/geoip2fast/main/images/geoip2dat01.jpg) 461 | 462 | ![](https://raw.githubusercontent.com/rabuchaim/geoip2fast/main/images/geoip2dat02.jpg) 463 | 464 | The options \(```--country-dir``` OR ```--city-dir```\) AND ```--output-dir``` are mandatory. Specify the path of extracted files in ```--country-dir``` or ```--city-dir``` option. And for ```--output-dir```, put the current path ```./```. 465 | 466 | If you want to add support for ASN data, add the option ```--asn-dir```. And if you want to add IPv6 support, just add ```--with-ipv6``` to your command line. 467 | 468 | You can choose the language of country locations. The default is ```en```. 469 | 470 | After creation of ```geoip2dat.dat.gz``` file, move or copy this file to the directory of your application or to the directory of GeoIP2Fast library. You choose. 471 | 472 | ![](https://raw.githubusercontent.com/rabuchaim/geoip2fast/main/images/geoip2dat03.jpg) 473 | 474 | **From now you don't depend on anyone to have your data file updated.** There's no point the code being open-source if you're dependent of a single file. 475 | 476 | > *The Philosophers call it 'Libertas'* 477 | 478 |
479 | 480 | ## Automatic update of dat.gz files 481 | 482 | From version v1.2.1 onwards, it is now possible to update the dat.gz files that were made available in our releases repository. You can update via command line or via code. 483 | 484 | - Download the file "geoip2fast-asn-ipv6.dat.gz" and save it as "geoip2fast.dat.gz": 485 | 486 | ```python 487 | >>> from geoip2fast import GeoIP2Fast 488 | >>> G = GeoIP2Fast(verbose=True) 489 | GeoIP2Fast v1.2.1 is ready! geoip2fast.dat.gz loaded with 459270 networks in 0.03297 seconds and using 25.12 MiB. 490 | >>> update_result = G.update_file('geoip2fast-asn-ipv6.dat.gz','geoip2fast.dat.gz',verbose=False) 491 | >>> G.reload_data(verbose=True) 492 | GeoIP2Fast v1.2.1 is ready! geoip2fast.dat.gz loaded with 753871 networks in 0.12917 seconds and using 113.54 MiBTrue 493 | >>> 494 | ``` 495 | - Update all files: 496 | 497 | ```python 498 | >>> from geoip2fast import GeoIP2Fast 499 | >>> G = GeoIP2Fast() 500 | >>> update_result = G.update_all(verbose=True) 501 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/geoip2fast.dat.gz 502 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 503 | - Downloading geoip2fast.dat.gz... 100.00% of 1.06 MiB [6.51 MiB/s] [0.163 sec] 504 | - File saved to: /opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz 505 | 506 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/geoip2fast-ipv6.dat.gz 507 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 508 | - Downloading geoip2fast-ipv6.dat.gz... 100.00% of 1.73 MiB [9.66 MiB/s] [0.179 sec] 509 | - File saved to: /opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast-ipv6.dat.gz 510 | 511 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/geoip2fast-asn.dat.gz 512 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 513 | - Downloading geoip2fast-asn.dat.gz... 100.00% of 3.06 MiB [8.63 MiB/s] [0.354 sec] 514 | - File saved to: /opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast-asn.dat.gz 515 | 516 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/geoip2fast-asn-ipv6.dat.gz 517 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 518 | - Downloading geoip2fast-asn-ipv6.dat.gz... 100.00% of 4.09 MiB [7.66 MiB/s] [0.534 sec] 519 | - File saved to: /opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast-asn-ipv6.dat.gz 520 | >>> 521 | ``` 522 | - Update all files silently and verify if there are errors: 523 | ```python 524 | >>> from geoip2fast import GeoIP2Fast 525 | >>> G = GeoIP2Fast() 526 | >>> update_result = G.update_all(verbose=False) 527 | >>> errors_result = [item for item in update_result if item['error'] is not None] 528 | >>> print(errors_result) 529 | [] 530 | ``` 531 | - You can change the update URL if you want. 532 | ``` 533 | >>> G.get_update_url() 534 | 'https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/' 535 | >>> G.set_update_url("https://github.com/YOUR_OWN_REPO/YOUR_PROJECT/releases/download/latest/") 536 | True 537 | >>> G.get_update_url() 538 | 'https://github.com/YOUR_OWN_REPO/YOUR_PROJECT/releases/download/latest/' 539 | >>> 540 | ``` 541 | - Update the file "geoip2fast-asn-ipv6.dat.gz" and overwrite "geoip2fast.dat.gz" and print the result. 542 | ```python 543 | >>> from geoip2fast import GeoIP2Fast 544 | >>> G = GeoIP2Fast(verbose=True) 545 | GeoIP2Fast v1.2.1 is ready! geoip2fast.dat.gz loaded with 459270 networks in 0.04414 seconds and using 5.02 MiB. 546 | >>> update_result = G.update_file('geoip2fast-asn-ipv6.dat.gz','geoip2fast.dat.gz',verbose=False) 547 | >>> from pprint import pprint as pp 548 | >>> pp(update_result,sort_dicts=False) 549 | {'error': None, 550 | 'url': 'https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/geoip2fast-asn-ipv6.dat.gz', 551 | 'remote_filename': 'geoip2fast-asn-ipv6.dat.gz', 552 | 'last_modified_date': 'Mon, 27 Nov 2023 02:40:08 GMT', 553 | 'file_size': 4289564, 554 | 'file_destination': '/opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz', 555 | 'average_download_speed': '4.09 MiB/sec', 556 | 'elapsed_time': '0.587267'} 557 | >>> 558 | >>> G.reload_data(verbose=True) 559 | GeoIP2Fast v1.2.1 is ready! geoip2fast.dat.gz loaded with 753871 networks in 0.12245 seconds and using 57.55 MiB. 560 | >>> 561 | 562 | ``` 563 | - **Using the command line, no message will be displayed on the console unless you use the -v parameter** 564 | - Update all files via command line and save them in '/tmp/' directory: 565 | ```bash 566 | # geoip2fast --update-all --dest /tmp/ -v 567 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/geoip2fast.dat.gz 568 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 569 | - Downloading geoip2fast.dat.gz... 100.00% of 1.06 MiB [10.41 MiB/s] [0.102 sec] 570 | - File saved to: /tmp/geoip2fast.dat.gz 571 | 572 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/geoip2fast-ipv6.dat.gz 573 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 574 | - Downloading geoip2fast-ipv6.dat.gz... 100.00% of 1.73 MiB [9.46 MiB/s] [0.183 sec] 575 | - File saved to: /tmp/geoip2fast-ipv6.dat.gz 576 | 577 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/geoip2fast-asn.dat.gz 578 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 579 | - Downloading geoip2fast-asn.dat.gz... 100.00% of 3.06 MiB [7.06 MiB/s] [0.433 sec] 580 | - File saved to: /tmp/geoip2fast-asn.dat.gz 581 | 582 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/geoip2fast-asn-ipv6.dat.gz 583 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 584 | - Downloading geoip2fast-asn-ipv6.dat.gz... 100.00% of 4.09 MiB [7.31 MiB/s] [0.560 sec] 585 | - File saved to: /tmp/geoip2fast-asn-ipv6.dat.gz 586 | ``` 587 | - Update the file "geoip2fast-asn-ipv6.dat.gz" and overwrite "geoip2fast.dat.gz" 588 | ```bash 589 | # geoip2fast --update-file geoip2fast-asn-ipv6.dat.gz --dest geoip2fast.dat.gz -v 590 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/geoip2fast-asn-ipv6.dat.gz 591 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 592 | - Downloading geoip2fast-asn-ipv6.dat.gz... 100.00% of 4.09 MiB [4.29 MiB/s] [0.954 sec] 593 | - File saved to: /opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz 594 | ``` 595 | - Update the file "geoip2fast.dat.gz" and save it in the library path 596 | ```bash 597 | # geoip2fast --update-file geoip2fast.dat.gz -v 598 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/geoip2fast.dat.gz 599 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 600 | - Downloading geoip2fast.dat.gz... 100.00% of 1.06 MiB [9.54 MiB/s] [0.111 sec] 601 | - File saved to: /opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz 602 | ``` 603 | - Update the file "geoip2fast.dat.gz" and save it in the library path 604 | 605 | ```bash 606 | # geoip2fast --update-file geoip2fast.dat.gz 607 | # echo $? 608 | 0 609 | ``` 610 | - An example of a simulated download failure: 611 | ```bash 612 | # geoip2fast.py --update-file geoip2fast.dat.gz -v 613 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/A_LATEST/geoip2fast.dat.gz 614 | - Error: HTTP Error 404: Not Found - https://github.com/rabuchaim/geoip2fast/releases/download/A_LATEST/geoip2fast.dat.gz 615 | # echo $? 616 | 1 617 | ``` 618 | ```bash 619 | # geoip2fast.py --update-file geoip2fast.dat.gz -v 620 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LATEST/geoip2fast.dat.gz 621 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 622 | PermissionError: [Errno 1] Operation not permitted: '/opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz' 623 | # echo $? 624 | 1 625 | ``` 626 | 627 |
628 | 629 | ## Using your own networks 630 | 631 | I´m planning to create an option to include your own networks in the database. For example, imagine you have a large private network on AWS, and each subnet is in a different region. You will be able to include them in the database to use GeoIP2Fast in your application. For now, you can edit the **reservedNetworks** variable at the beginning of the ```geoip2dat.py``` file and then generate your own data file. 632 | 633 | Suggestions and ideas are welcome. The initial idea is to include an option ```--include-networks``` in ```geoip2dat.py``` where you enter a json file. 634 | 635 |
636 | 637 | ## Create your own GeoIP CLI with 6 lines 638 | 639 | 1. Create a file named ```geoipcli.py``` and save it in your home directory with the text below: 640 | ```python 641 | #!/usr/bin/env python3 642 | import os, sys, geoip2fast 643 | if len(sys.argv) > 1 and sys.argv[1] is not None: 644 | geoip2fast.GeoIP2Fast().lookup(sys.argv[1]).pp_json(print_result=True) 645 | else: 646 | print(f"Usage: {os.path.basename(__file__)} ") 647 | ``` 648 | 2. Give execution permisstion to your file and create a symbolic link to your new file into ```/usr/sbin``` folder, like this (let's assume that you saved this file into directory ```/root```) 649 | ```bash 650 | chmod 750 /root/geoipcli.py 651 | ln -s /root/geoipcli.py /usr/sbin/geoipcli 652 | ``` 653 | 3. Now, you just need to call ```geoipcli``` from any path. 654 | ```bash 655 | # geoipcli 656 | Usage: geoipcli 657 | # geoipcli 1.2.3.4 658 | { 659 | "ip": "1.2.3.4", 660 | "country_code": "AU", 661 | "country_name": "Australia", 662 | "cidr": "1.2.3.0/24", 663 | "hostname": "", 664 | "is_private": false, 665 | "elapsed_time": "0.000019727 sec" 666 | } 667 | ``` 668 |
669 | 670 | ## GeoIP libraries that inspired me 671 | 672 | **GeoIP2Nation - https://pypi.org/project/geoip2nation/** (Created by Avi Asher) 673 | 674 | This library uses sqlite3 in-memory tables and use the same search concepts as GeoIP2Fast (based on search by the first´s IPs). Simple and fast! Unfortunately it is no longer being updated and that is why I developed GeoIP2Fast. 675 | 676 | **GeoIP2 - https://pypi.org/project/geoip2/** (Created by Maxmind) 677 | 678 | This is the best library to work with Maxmind paid subscription or with the free version. You can use http requests to Maxmind services or work with local Maxmind MMDB binary files. Pretty fast too. Sign-up to have access to all files of the free version https://dev.maxmind.com/geoip/geolite2-free-geolocation-data 679 | 680 | **\* Maxmind is a registered trademark** - https://www.maxmind.com 681 | 682 | ## TO DO list 683 | - a GeoIP2Fast Server that supports our dat.gz and any kind of mmdb file; **<<< In final tests! the FASTORNADO Server ** 684 | - a version to support any kind of MMDB file with coordinates and more; **<<< ON THE WAY! ** 685 | - a better manual, maybe at readthedocs.io; 686 | - a mod_geoip2fast for NGINX; 687 | - **Done in v1.1.10/v1.2.1** - automatic update of dat.gz files; 688 | - **Done in v1.2.0** - a version with cities; 689 | - **Done in v1.1.0** - *IPv6 support*. 690 | - **Done in v1.0.5** - *a version with ASN*. 691 | - **Done in v1.0.2** - *provide a script to update the base. If you have the paid subscription of Maxmind, you can download the files, extract into some directory and use this script to create your own geoip2fast.dat.gz file with the most complete, reliable and updated GeoIP information*. 692 | 693 | ## Sugestions, feedbacks, bugs, wrong locations... 694 | E-mail me: ricardoabuchaim at gmail.com 695 | -------------------------------------------------------------------------------- /geoip2fast-legacy/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | .vscode 6 | .mypy_cache 7 | create_all_dat_files.sh 8 | geoip2fast-ipv4.dat.gz 9 | geoip2fast-city*.dat.gz 10 | old* 11 | *egg-info/ 12 | dist/ 13 | build/ 14 | 15 | # C extensions 16 | *.so 17 | 18 | # Distribution / packaging 19 | .Python 20 | build/ 21 | develop-eggs/ 22 | dist/ 23 | downloads/ 24 | eggs/ 25 | .eggs/ 26 | lib/ 27 | lib64/ 28 | parts/ 29 | sdist/ 30 | var/ 31 | wheels/ 32 | share/python-wheels/ 33 | *.egg-info/ 34 | .installed.cfg 35 | *.egg 36 | MANIFEST 37 | 38 | # PyInstaller 39 | # Usually these files are written by a python script from a template 40 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 41 | *.manifest 42 | *.spec 43 | 44 | # Installer logs 45 | pip-log.txt 46 | pip-delete-this-directory.txt 47 | 48 | # Unit test / coverage reports 49 | htmlcov/ 50 | .tox/ 51 | .nox/ 52 | .coverage 53 | .coverage.* 54 | .cache 55 | nosetests.xml 56 | coverage.xml 57 | *.cover 58 | *.py,cover 59 | .hypothesis/ 60 | .pytest_cache/ 61 | cover/ 62 | 63 | # Translations 64 | *.mo 65 | *.pot 66 | 67 | # Django stuff: 68 | *.log 69 | local_settings.py 70 | db.sqlite3 71 | db.sqlite3-journal 72 | 73 | # Flask stuff: 74 | instance/ 75 | .webassets-cache 76 | 77 | # Scrapy stuff: 78 | .scrapy 79 | 80 | # Sphinx documentation 81 | docs/_build/ 82 | 83 | # PyBuilder 84 | .pybuilder/ 85 | target/ 86 | 87 | # Jupyter Notebook 88 | .ipynb_checkpoints 89 | 90 | # IPython 91 | profile_default/ 92 | ipython_config.py 93 | 94 | # pyenv 95 | # For a library or package, you might want to ignore these files since the code is 96 | # intended to run in multiple environments; otherwise, check them in: 97 | # .python-version 98 | 99 | # pipenv 100 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 101 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 102 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 103 | # install all needed dependencies. 104 | #Pipfile.lock 105 | 106 | # poetry 107 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 108 | # This is especially recommended for binary packages to ensure reproducibility, and is more 109 | # commonly ignored for libraries. 110 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 111 | #poetry.lock 112 | 113 | # pdm 114 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 115 | #pdm.lock 116 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 117 | # in version control. 118 | # https://pdm.fming.dev/#use-with-ide 119 | .pdm.toml 120 | 121 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 122 | __pypackages__/ 123 | 124 | # Celery stuff 125 | celerybeat-schedule 126 | celerybeat.pid 127 | 128 | # SageMath parsed files 129 | *.sage.py 130 | 131 | # Environments 132 | .env 133 | .venv 134 | env/ 135 | venv/ 136 | ENV/ 137 | env.bak/ 138 | venv.bak/ 139 | 140 | # Spyder project settings 141 | .spyderproject 142 | .spyproject 143 | 144 | # Rope project settings 145 | .ropeproject 146 | 147 | # mkdocs documentation 148 | /site 149 | 150 | # mypy 151 | .mypy_cache/ 152 | .dmypy.json 153 | dmypy.json 154 | 155 | # Pyre type checker 156 | .pyre/ 157 | 158 | # pytype static type analyzer 159 | .pytype/ 160 | 161 | # Cython debug symbols 162 | cython_debug/ 163 | 164 | # PyCharm 165 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 166 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 167 | # and can be added to the global gitignore or merged into this file. For a more nuclear 168 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 169 | #.idea/ 170 | dist 171 | -------------------------------------------------------------------------------- /geoip2fast-legacy/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | .oPYo. o .oPYo. .oPYo. ooooo o 3 | 8 8 8 8 8 `8 8 8 4 | 8 .oPYo. .oPYo. 8 o8YooP' oP' o8oo .oPYo. .oPYo. o8P 5 | 8 oo 8oooo8 8 8 8 8 .oP' 8 .oooo8 Yb.. 8 6 | 8 8 8. 8 8 8 8 8' 8 8 8 'Yb. 8 7 | `YooP8 `Yooo' `YooP' 8 8 8ooooo 8 `YooP8 `YooP' 8 8 | :....8 :.....::.....:..:..:::::.......:..:::::.....::.....:::..: 9 | :::::8 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 10 | :::::..::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 11 | 12 | Author: Ricardo Abuchaim - ricardoabuchaim@gmail.com 13 | https://github.com/rabuchaim/geoip2fast/ 14 | 15 | License: MIT 16 | 17 | ############################################################################ 18 | What's new in v1.1.11 - 20/Jun/2024 19 | - Moved line "sys.tracebacklimit = 0" to the main_function() because 20 | was causing some problems in Django. This line is unnecessary to 21 | the GeoIP2Fast class and was there only for the command line use. 22 | https://github.com/rabuchaim/geoip2fast/issues/10 23 | - Fix in geoip2dat.py so that future field insertions made 24 | by MaxMind in the CSV files no longer affect the creation of new 25 | dat.gz files. 26 | 27 | What's new in v1.1.10 - 27/Nov/2023 28 | - Automatic download of dat.gz files. 29 | try: geoip2fast --download-all -v 30 | 31 | What's new in v1.1.9 - 22/Nov/2023 32 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231121 33 | - Fix in memory usage under MacOS 34 | - Fix a problem when loading specific datafiles 35 | - a new method to return the path of the dat.gz file that is currently being used 36 | from geoip2fast import GeoIP2Fast 37 | G = GeoIP2Fast(geoip2fast_data_file="/tmp/geoip2fast-asn.dat.gz") 38 | G.get_database_path() 39 | 40 | What's new in v1.1.8 - 14/Nov/2023 41 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231114 42 | - Fix in './geoip2fast.py --coverage' test when using IPv6 database 43 | 44 | What's new in v1.1.6 - 10/Nov/2023 45 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231110 46 | 47 | What's new in v1.1.5 - 03/Nov/2023 48 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231103 49 | - Fully tested with Python 3.12 and Python 3.13 (> 150.000 lookups/sec) 50 | - Fixed an issue in the function that adjusts the terminal window of the 51 | geoip2dat.py file. This problem prevented the geoip2dat.py script from 52 | being executed by crontab. 53 | - Added entry_points to setup.py, now it's possible to run geoip2fast and 54 | geoip2dat as an executable under Windows. If it doesn't work, you need 55 | to add the path of your python scripts directory to your PATH environment 56 | variable. 57 | - To see the path of your scripts directory on win32, run: pip show geoip2fast 58 | - Check the "Location" information, and change the "site-packages" word 59 | to "scripts" and add this path to your PATH environment variable (google it). 60 | - After this change, you can run geoip2fast and geoip2dat from any path 61 | of your Windows command prompt. Sometimes this change is unnecessary, 62 | try running 'geoip2fast' from any path of your Win32 command prompt. 63 | 64 | What's new in v1.1.4 - 27/Oct/2023 65 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231027 66 | 67 | What's new in v1.1.3 - 20/Oct/2023 68 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231020 69 | - Bug fix in the coverage test of v1.1.2. Didn´t affect the search accuracy. 70 | 71 | What's new in v1.1.2 - 03/Oct/2023 72 | - DAT files updated with MAXMIND:GeoLite2-Country-ASN-CSV_20231003 73 | - IPv6 transparent support!!! the same class, you just need to choose 74 | which data file you wanna use. 75 | - fast as always! the lookup speed is the same for any data file. 76 | - removed some useless code and put some colored flowers. 77 | 78 | What's new in v1.0.6 - 01/Oct/2023 79 | - geoip2fast.dat.gz updated with MAXMIND:GeoLite2-Country-CSV_20230929 80 | - geoip2fast-asn.dat.gz updated with MAXMIND:GeoLite2-ASN-CSV_20230929 81 | - bug fix: Fail on Windows while getting a memory usage 82 | - bug fix: Error when specifying the data file manually 83 | 84 | What's new in v1.0.5 - 20/Sep/2023 85 | - geoip2fast.dat.gz updated with MAXMIND:GeoLite2-Country-ASN-CSV_20230919 86 | - faster than ever!!! a lookup around ~0.00001 87 | - A new option in geoip2dat to create .dat.gz with asn support: --asn-dir 88 | - ASN support - the dat file already has support for ASN of the network 89 | ranges. The footprint was incresed to 64Mb. The speed is the same. 90 | - If you want, you can create an another dat file only with country data, 91 | just use the option --country-dir without the option --asn-dir 92 | - geoip2dat updated too! older versions won't work anymore. Sorry. 93 | - more flowers 94 | 95 | What's new in v1.0.4 - 13/Sep/2023 96 | - geoip2fast.dat.gz updated with MAXMIND:GeoLite2-Country-CSV_20230912 97 | - fix in search of IPs that end in ".0" 98 | - fix in _locate_database() function that search dat.gz file in 99 | $current_application_file path and library path 100 | - added some cli parameters: --speed-test, --self-test, and --coverage 101 | ( try: ./geoip2fast.py --coverage -v to see all networks included in 102 | dat file. ) 103 | - added a parameter in tests: --missing-ips (take care, uses 100% of CPU) 104 | - geoip2dat updated too! older versions won't work anymore. Sorry. 105 | - more flowers 106 | 107 | What's new in v1.0.3 - 08/Sep/2023 108 | - geoip2fast.dat.gz updated with MAXMIND:GeoLite2-Country-CSV_20230908 109 | - IMPROVED SPEED!! >100.000 LOOKUPS PER SECOND! GeoIP2Flash! 110 | - geoip2fast.dat.gz decreased to ONE MEGABYTE! (-60%) 111 | - RAM footprint dropped to 25 MiB! (-50%) 112 | - load time around ~0,05 seconds! (-50%) 113 | - the number of networks and content still the same, we just 114 | converted all data to integers and sliced the lists a lot! This 115 | was necessary to implement the ASN data (not yet). 116 | - geoip2dat updated to create the new dat file structure 117 | - inserted a tag in dat file to record the version. 118 | - as requested, property elapsed_time_hostname for hostnames lookups 119 | 120 | What's new in v1.0.2 - 04/Sep/2023 121 | - geoip2fast.dat.gz updated with MAXMIND:GeoLite2-Country-CSV_20230901 122 | - fully tested with Python 3.11.5. Much faster than 3.10.12 >100K lookups/sec. 123 | - fix encoding of pp_json() method. Now it's showing all chars as it is. 124 | - in verbose mode it is now showing the memory footprint. 125 | - new test files at /usr/local/lib/python3.10/dist-packages/geoip2fast/tests/ 126 | - new class CIDRDetail will be used to create gepip2fast.dat file 127 | - geoip2dat - a script to import Maxmind-Country-CSV into geoip2fast.dat.gz. 128 | You can update your geoip2fast.dat.gz file whenever you want. It should work 129 | with paid version also. Please let me know if there are any problems. 130 | - put some flowers; 131 | 132 | What's new in v1.0.1 - 1º/Sep/2023 133 | - geoip2fast.dat.gz updated with MAXMIND:GeoLite2-Country-CSV_20230901 134 | - improved speed in >20%! removed ipaddress module. Now we do some IP calcs. 135 | - new methods to set the error code for the situations PRIVATE NETWORKS and for 136 | NETWORKS NOT FOUND: 137 | GeoIP2Fast.set_error_code_private_networks(new_value) 138 | GeoIP2Fast.set_error_code_network_not_found(new_value) 139 | - new method to calculate the current speed. Returns a value of current lookups per 140 | seconds or print a formatted result: 141 | GeoIP2Fast.calculate_speed(print_result=True) 142 | - new method to calculate how many IPv4 of all internet are covered by geoip2fast.dat 143 | file. Returns a percentage relative to all possible IPv4 on the internet or print a 144 | formatted result. Useful to track the changes in getip2fast.dat.gz file: 145 | GeoIP2Fast.calculate_coverage(print_result=True) 146 | -------------------------------------------------------------------------------- /geoip2fast-legacy/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | ricardoabuchaim@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. -------------------------------------------------------------------------------- /geoip2fast-legacy/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Ricardo Abuchaim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /geoip2fast-legacy/README.md: -------------------------------------------------------------------------------- 1 | # GeoIP2Fast v1.1.11 2 | 3 | GeoIP2Fast is the fastest GeoIP2 country/asn lookup library. A search takes less than 0.00003 seconds. It has its own data file updated with Maxmind-Geolite2-CSV, supports IPv4 and IPv6 and is Pure Python! 4 | 5 | With it´s own datafile (geoip2fast.dat.gz), can be loaded into memory in ~0.07 seconds and has a small footprint for all data, so you don´t need to make requests to any webservices or connect to an external database. 6 | 7 | There are 4 databases included in the installation package: 8 | 9 | | Content | File Name | File Size | Load Time | RAM Footprint | Download Latest | 10 | | ---------- | :---------: | ---------: | --------: | ---------: | :-------------: | 11 | | Country IPv4 | ```geoip2fast.dat.gz``` | 1.1 MiB | ~0.04 sec | ~22.0 MiB | [geoip2fast.dat.gz](https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast.dat.gz) | 12 | | Country IPv4+IPv6 | ```geoip2fast-ipv6.dat.gz``` | 1.1 MiB | ~0.08 sec | ~43.0 MiB | [geoip2fast-ipv6.dat.gz](https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast-ipv6.dat.gz) | 13 | | Country+ASN IPv4 | ```geoip2fast-asn.dat.gz``` | 3.1 MiB | ~0.11 sec | ~66.0 MiB | [geoip2fast-asn.dat.gz](https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast-asn.dat.gz) | 14 | | Country+ASN IPv4+IPv6 | ```geoip2fast-asn-ipv6.dat.gz``` | 4.0 MiB | ~0.15 sec | ~97.0 MiB | [geoip2fast-asn-ipv6.dat.gz](https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast-asn-ipv6.dat.gz) | 15 | 16 | GeoIP2Fast returns ASN NAME, COUNTRY ISO CODE, COUNTRY NAME and CIDR. There is no external dependencies, you just need the ```geoip2fast.py``` file and the desired data file ```.dat.gz```. **The lookup speed is the same for any data file**. 17 | 18 | **There is also version 1.2.X that returns city names, visit the previous directory [https://github.com/rabuchaim/geoip2fast/](https://github.com/rabuchaim/geoip2fast/)** 19 | 20 | ``` 21 | What's new in v1.1.11 - 20/Jun/2024 22 | - Moved line "sys.tracebacklimit = 0" to the main_function() because 23 | was causing some problems in Django. This line is unnecessary to 24 | the GeoIP2Fast class and was there only for the command line use. 25 | https://github.com/rabuchaim/geoip2fast/issues/10 26 | - Fix in geoip2dat.py so that future field insertions made 27 | by MaxMind in the CSV files no longer affect the creation of new 28 | dat.gz files. 29 | 30 | What's new in v1.1.10 - 22/Nov/2023 31 | - DAT files updated with MAXMIND:GeoLite2-CSV_20231201 32 | - Automatic updates! you can update to the newest dat.gz file via command line or via code 33 | Using command line: 34 | geoip2fast --update-all -v 35 | 36 | Using the class GeoIP2Fast: 37 | from geoip2fast import GeoIP2Fast 38 | from pprint import pprint 39 | G = GeoIP2Fast(verbose=True) 40 | G.get_database_path() 41 | update_file_result = G.update_file(filename="geoip2fast-asn-ipv6.dat.gz",destination="geoip2fast.dat.gz",verbose=True) 42 | pprint(update_file_result,sort_dicts=False) 43 | G.reload_data(verbose=True) 44 | update_all_result = G.update_all(destination_path="",verbose=True) 45 | pprint(update_all_result,sort_dicts=False) 46 | 47 | ``` 48 |
49 | 50 | ![](https://raw.githubusercontent.com/rabuchaim/geoip2fast/main/geoip2fast-legacy/images/geoip2fast_selftest.jpg) 51 | 52 |
53 | 54 | ## Installation 55 | ```bash 56 | pip install geoip2fast==1.1.11 57 | ``` 58 | 59 |
60 | 61 | ## DAT files updates 62 | 63 | - You can create your own dat.gz file using [geoip2dat.py](#geoip2dat---update-geoip2fastdatgz-file-anytime) file. 64 | - You can also [download the latest dat files](https://github.com/rabuchaim/geoip2fast/releases/tag/LEGACY) that are updated automatically on Tuesdays and Fridays 65 | - And you can [update the dat files downloading from our releases repository](#automatic-update-of-datgz-files), via code or via command line. 66 | 67 |
68 | 69 | ## How does it work? 70 | 71 | GeoIP2Fast has 4 datafiles included. Tha main file is ```geoip2fast.dat.gz``` with support Country lookups and only IPv4. Usually, these files are located into the library directory (```/usr/local/lib/python3/dist-packages/geoip2fast```), but you can place it into the same directory of your application. The library automatically checks both paths, And the directory of your application overlaps the directory of the library. You can use an specific location also. 72 | 73 | The ```bisect()``` function is used together with some ordered lists of integers to search the Network/CountryCode (Yes! an IP address has an integer representation, try to ping this number: ```ping 134744072``` or this ```ping 2130706433``` ). 74 | 75 | If GeoIP2Fast does not have a network IP address that was requested, a "not found in database" error will be returned. Unlike many other libraries that when not finding a requested network, gives you the geographical location of the network immediately below. The result is not always correct. 76 | 77 | There are network gaps in the files we use as a source of data, and these missing networks are probably addresses that those responsible have not yet declared their location. Of all almost 4.3 billion IPv4 on the internet, we do not have information on approximately 15 million of them (~0,35%). It must be remembered that the geographical accuracy is the responsibility of the network block owners. If the owner (aka ASN) of the XXX.YYY.ZZZ.D/24 network range declares that his network range is located at "Foo Island", we must believe that an IP address of that network is there. 78 | 79 | > *Don't go to Foo Island visit a girl you met on the internet just because you looked up her IP on GeoIP2Fast and the result indicated that she is there.* 80 | 81 |
82 | 83 | ## Quick Start 84 | 85 | Once the object is created, GeoIP2Fast loads automatically all needed data into memory. The lookup function returns an object called ```GeoIPDetail```. And you can get the values of it's properties just calling the name of proprerty: ```result.ip, result.country_code, result.country_name, result.cidr, result.is_private, result.asn_name``` and ```result.elapsed_time```. Or use the function ```to_dict()``` to get the result as a dict. You can get values like ```result.to_dict()['country_code']``` 86 | 87 | At the moment of creation, you can define which data you want to use. Country+IPv4, Country+IPv4+IPv6, Country+ASN+IPv4 or Country+ASN+IPv4+IPv6. If don´t specify any file, the default ```geoip2fast.dat.gz``` will be used. 88 | 89 | 90 | ```python 91 | from geoip2fast import GeoIP2Fast 92 | 93 | GEOIP = GeoIP2Fast() 94 | print(GEOIP.get_database_path()) 95 | 96 | result = GEOIP.lookup("200.204.0.10") 97 | print(result) 98 | 99 | # to use the country_code property 100 | print(result.country_code) 101 | 102 | # to print the ASN name property 103 | print(result.asn_name) 104 | 105 | # Before call the function get_hostname(), the property hostname will always be empty. 106 | print("Hostname: "+result.hostname) 107 | result.get_hostname() 108 | print("Hostname: "+result.hostname) 109 | 110 | # to work with output as a dict, use the function to_dict() 111 | print(result.to_dict()['country_code'],result.to_dict()['country_name']) 112 | 113 | # to check the date of the CSV files used to create the .dat file 114 | print(GEOIP.get_source_info()) 115 | 116 | # info about internal cache 117 | print(GEOIP.cache_info()) 118 | 119 | # clear the internal cache 120 | print(GEOIP.clear_cache()) 121 | 122 | # to see the difference after clear cache 123 | print(GEOIP.cache_info()) 124 | 125 | ``` 126 | There is a method to pretty print the result as json.dumps(): 127 | ```python 128 | >>> result = MyGeoIP.lookup("100.200.100.200") 129 | >>> print(result.pp_json()) 130 | { 131 | "ip": "100.200.100.200", 132 | "country_code": "US", 133 | "country_name": "United States", 134 | "cidr": "100.128.0.0/9", 135 | "hostname": "", 136 | "is_private": false, 137 | "asn_name": "T-MOBILE-AS21928", 138 | "elapsed_time": "0.000014487 sec" 139 | } 140 | ``` 141 | or simply: ```result.pp_json(print_result=True)``` 142 | 143 | To see the start-up line without set ```verbose=True``` : 144 | ```python 145 | >>> from geoip2fast import GeoIP2Fast 146 | >>> MyGeoIP = GeoIP2Fast() 147 | >>> MyGeoIP.startup_line_text 148 | 'GeoIP2Fast v1.1.10 is ready! geoip2fast.dat.gz loaded with 433468 networks in 0.04153 seconds and using 23.75 MiB.' 149 | ``` 150 | 151 | If you call geoip2fast from command line, it´s only use ```geoip2fast.dat.gz``` file, so if you want more data like ASN or IPv6 support, you have to copy the respective file over ```geoip2fast.dat.gz``` file. If you are using GeoIP2Fast as a Python library, you don´t need to rename or copy any file, you can load the desired data file in the moment of object creation. The library first looks for the given file in the current directory and then in the library directory. If desired, you can directly specify the path. 152 | 153 | ```python 154 | >>> from geoip2fast import GeoIP2Fast 155 | >>> geoip = GeoIP2Fast(geoip2fast_data_file="geoip2fast-asn.dat.gz",verbose=True) 156 | GeoIP2Fast v1.1.10 is ready! geoip2fast-asn.dat.gz loaded with 456271 networks in 0.09355 seconds and using 66.99 MiB. 157 | >>> geoip.get_database_path() 158 | '/usr/local/lib/python3.11/dist-packages/geoip2fast/geoip2fast-asn.dat.gz' 159 | >>> 160 | >>> geoip.lookup("2a02:26f0:6d00:5bc::b63") 161 | {'ip': '2a02:26f0:6d00:5bc::b63', 'country_code': 'NL', 'country_name': 'Netherlands', 'cidr': '2a02:26f0:6d00::/40', 'hostname': '', 'is_private': False, 'asn_name': 'Akamai International B.V.', 'elapsed_time': '0.000600145 sec'} 162 | >>> 163 | >>> geoip = GeoIP2Fast(geoip2fast_data_file="/opt/maxmind/geoip2fast-asn.dat.gz",verbose=True) 164 | GeoIP2Fast v1.1.10 is ready! geoip2fast-asn.dat.gz loaded with 456271 networks in 0.11666 seconds and using 67.05 MiB. 165 | >>> geoip.get_database_path() 166 | '/opt/maxmind/geoip2fast-asn.dat.gz' 167 | ``` 168 | 169 | Private/Reserved networks were included in the database just to be able to provide an answer if one of these IPs is searched. When it happens, the country_code will return "--", the "network name" will be displayed in the country_name and the range of that network will be displayed in the cidr property, and the property **is_private** is setted to **True**. 170 | 171 | ```python 172 | >>> from geoip2fast import GeoIP2Fast 173 | >>> geoip = GeoIP2Fast(verbose=True) 174 | GeoIP2Fast v1.1.10 is ready! geoip2fast.dat.gz loaded with 433468 networks in 0.04153 seconds and using 23.75 MiB. 175 | >>> 176 | >>> geoip.lookup("10.20.30.40") 177 | {'ip': '10.20.30.40', 'country_code': '--', 'country_name': 'Private Network Class A', 'cidr': '10.0.0.0/8', 'hostname': '', 'is_private': True, 'asn_name': 'IANA.ORG', 'elapsed_time': '0.000094584 sec'} 178 | >>> 179 | >>> geoip.lookup("169.254.10.20") 180 | {'ip': '169.254.10.20', 'country_code': '--', 'country_name': 'APIPA Automatic Priv.IP Addressing', 'cidr': '169.254.0.0/16', 'hostname': '', 'is_private': True, 'asn_name': 'IANA.ORG', 'elapsed_time': '0.000048402 sec'} 181 | ``` 182 | 183 | You can change the behavior of what will be returned in country_code property of "private networks" and for "networks not found": 184 | 185 | ```python 186 | >>> from geoip2fast import GeoIP2Fast 187 | >>> geoip = GeoIP2Fast(verbose=True) 188 | GeoIP2Fast v1.1.10 is ready! geoip2fast.dat.gz loaded with 433468 networks in 0.04153 seconds and using 23.75 MiB. 189 | >>> geoip.set_error_code_private_networks("@@") 190 | '@@' 191 | >>> 192 | >>> geoip.lookup("10.20.30.40") 193 | {'ip': '10.20.30.40', 'country_code': '@@', 'country_name': 'Private Network Class A', 'cidr': '10.0.0.0/8', 'hostname': '', 'is_private': True, 'asn_name': 'IANA.ORG', 'elapsed_time': '0.000060297 sec'} 194 | >>> 195 | >>> geoip.set_error_code_network_not_found("##") 196 | '##' 197 | >>> geoip.lookup("57.242.128.144") 198 | {'ip': '57.242.128.144', 'country_code': '##', 'country_name': '', 'cidr': '', 'hostname': '', 'is_private': False, 'asn_name': '', 'elapsed_time': '0.000008152 sec'} 199 | >>> 200 | ``` 201 | 202 |
203 | 204 | ## How fast is it? 205 | 206 | With an virtual machine with 1 CPU and 4Gb of RAM, we have lookups **lower than 0,00003 seconds**. And if the lookup still in library´s internal cache, the elapsed time goes down to 0,000003 seconds. **GeoIP2Fast can do more than 100K queries per second, per core**. It takes less than 0,07 seconds to load the datafile into memory and get ready to lookup. Use ```verbose=True``` to create the object GeoIP2Fast to see the spent time to start. 207 | 208 | ```geoip2fast --self-test``` 209 | ```bash 210 | # geoip2fast --self-test 211 | GeoIP2Fast v1.1.19 is ready! geoip2fast.dat.gz loaded with 433468 networks in 0.04153 seconds and using 23.75 MiB. 212 | 213 | Starting a self-test... 214 | 215 | > 223.130.10.1 -- [0.000034988 sec] Cached > [0.000001427 sec] 216 | > 266.266.266.266 [0.000015505 sec] Cached > [0.000001393 sec] 217 | > 192,0x0/32 [0.000001101 sec] Cached > [0.000000881 sec] 218 | > 127.0.0.10 -- Localhost [0.000023153 sec] Cached > [0.000002716 sec] 127.0.0.0/8 219 | > 10.20.30.40 -- Private Network Class A [0.000012335 sec] Cached > [0.000001526 sec] 10.0.0.0/8 220 | > 200.204.0.10 BR Brazil [0.000014939 sec] Cached > [0.000002163 sec] 200.204.0.0/14 221 | > 57.242.128.144 -- [0.000004927 sec] Cached > [0.000000707 sec] 222 | > 192.168.10.10 -- Private Network Class C [0.000009447 sec] Cached > [0.000001244 sec] 192.168.0.0/16 223 | > 200.200.200.200 BR Brazil [0.000004481 sec] Cached > [0.000001852 sec] 200.200.200.200/32 224 | > 11.22.33.44 US United States [0.000005417 sec] Cached > [0.000001573 sec] 11.0.0.0/10 225 | > 200.147.0.20 BR Brazil [0.000004278 sec] Cached > [0.000001466 sec] 200.144.0.0/14 226 | (.....) 227 | ``` 228 | 229 | ```geoip2fast --speed-test``` 230 | ```bash 231 | # geoip2fast --speed-test 232 | GeoIP2Fast v1.1.10 is ready! geoip2fast.dat.gz loaded with 433468 networks in 0.04153 seconds and using 23.75 MiB. 233 | 234 | Calculating current speed... wait a few seconds please... 235 | 236 | Current speed: 136572.73 lookups per second (searched for 1,000,000 IPs in 7.322106013 seconds) [7.32211 sec] 237 | ``` 238 | 239 | ```geoip2fast --coverage``` 240 | ```bash 241 | # geoip2fast --coverage 242 | GeoIP2Fast v1.1.10 is ready! geoip2fast.dat.gz loaded with 748074 networks in 0.15510 seconds and using 98.30 MiB. 243 | 244 | Use the parameter '-v' to see all networks included in your /opt/pypi-geoip2fast/git-geoip2fast/geoip2fast/geoip2fast.dat.gz file. 245 | 246 | Current IPv4 coverage: 99.64% (4,279,396,946 IPv4 in 453615 networks) [0.12512 sec] 247 | Current IPv6 coverage: 0.40% (1,364,425,945,439,630,011,748,628,700,499,804,160 IPv6 in 294459 networks) [0.12518 sec] 248 | ``` 249 | ```geoip2fast --coverage -v``` 250 | ```bash 251 | # geoip2fast --coverage -v 252 | GeoIP2Fast v1.1.10 is ready! geoip2fast.dat.gz loaded with 748074 networks in 0.15510 seconds and using 98.30 MiB. 253 | 254 | Use the parameter '-v' to see all networks included in your /opt/pypi-geoip2fast/git-geoip2fast/geoip2fast/geoip2fast.dat.gz file. 255 | 256 | - Network: 0.0.0.0/8 IPs: 16777216 -- Reserved for self identification 0.000128794 sec 257 | - Network: 1.0.0.0/24 IPs: 256 AU Australia 0.000073830 sec 258 | - Network: 1.0.1.0/24 IPs: 256 CN China 0.000084977 sec 259 | - Network: 1.0.2.0/23 IPs: 512 CN China 0.000015769 sec 260 | - Network: 1.0.4.0/22 IPs: 1024 AU Australia 0.000015369 sec 261 | - Network: 1.0.8.0/21 IPs: 2048 CN China 0.000023131 sec 262 | - Network: 1.0.16.0/20 IPs: 4096 JP Japan 0.000095840 sec 263 | (.....) 264 | - Network: 2c0f:ffb8::/32 IPs: 79228162514264337593543950336 SD Sudan 0.000018255 sec 265 | - Network: 2c0f:ffc0::/32 IPs: 79228162514264337593543950336 ZA South Africa 0.000032078 sec 266 | - Network: 2c0f:ffc8::/32 IPs: 79228162514264337593543950336 ZA South Africa 0.000031547 sec 267 | - Network: 2c0f:ffd0::/32 IPs: 79228162514264337593543950336 ZA South Africa 0.000022423 sec 268 | - Network: 2c0f:ffd8::/32 IPs: 79228162514264337593543950336 ZA South Africa 0.000012151 sec 269 | - Network: 2c0f:ffe8::/32 IPs: 79228162514264337593543950336 NG Nigeria 0.000010622 sec 270 | - Network: 2c0f:fff0::/32 IPs: 79228162514264337593543950336 NG Nigeria 0.000017496 sec 271 | - Network: fd00::/8 IPs: 1329227995784915872903807060280344576 -- Reserved for Unique Local Addresses 0.000033054 sec 272 | 273 | Current IPv4 coverage: 99.64% (4,279,396,946 IPv4 in 453615 networks) [18.88599 sec] 274 | Current IPv6 coverage: 0.40% (1,364,425,945,439,630,011,748,628,700,499,804,160 IPv6 in 294459 networks) [18.88601 sec] 275 | ``` 276 | 277 | ```geoip2fast --missing-ips``` 278 | ```bash 279 | # geoip2fast --missing-ips 280 | GeoIP2Fast v1.1.10 is ready! geoip2fast.dat.gz loaded with 748074 networks in 0.15510 seconds and using 98.30 MiB. 281 | 282 | Searching for missing IPs... 283 | 284 | From 1.34.65.179 to 1.34.65.179 > Network 1.34.65.180/32 > Missing IPs: 1 285 | From 1.46.23.235 to 1.46.23.235 > Network 1.46.23.236/32 > Missing IPs: 1 286 | From 2.12.211.171 to 2.12.211.171 > Network 2.12.211.172/32 > Missing IPs: 1 287 | (.....) 288 | From 216.238.200.0 to 216.238.207.255 > Network 216.238.208.0/21 > Missing IPs: 2048 289 | From 217.26.216.0 to 217.26.223.255 > Network 217.26.224.0/21 > Missing IPs: 2048 290 | From 217.78.64.0 to 217.78.79.255 > Network 217.78.80.0/20 > Missing IPs: 4096 291 | 292 | >>> Valid IP addresses without geo information: 15,169,195 (0.35% of all IPv4) [44.30283 sec] 293 | ``` 294 | 295 | > Some IPs are excluded as described in page "Do Not Sell My Personal Information Requests" at Maxmind website. 296 | 297 |
298 | 299 | ## You can use it as a CLI 300 | 301 | ```bash 302 | # geoip2fast -h 303 | GeoIP2Fast v1.1.3 Usage: geoip2fast [-h] [-v] [-d] ,,,... 304 | 305 | Tests parameters: 306 | --self-test Starts a self-test with some randomic IP addresses. 307 | --speed-test Do a speed test with 1 million on randomic IP addresses. 308 | --random-test Start a test with 1.000.000 of randomic IPs and calculate a lookup average time. 309 | 310 | --coverage [-v] Shows a statistic of how many IPs are covered by current dat file. 311 | --missing-ips [-v] Print all IP networks that doesn't have geo information. 312 | 313 | More options: 314 | -d Resolve the DNS of given IP address. 315 | -h Show this help text. 316 | -v Verbose mode. 317 | -vvv Shows the location of current dat file. 318 | ``` 319 | 320 | ```bash 321 | # geoip2fast 322 | GeoIP2Fast v1.1.3 Usage: geoip2fast [-h] [-v] [-d] ,,,... 323 | # geoip2fast -v 9.9.9.9,15.20.25.30 -d 324 | GeoIP2Fast v1.1.3 is ready! geoip2fast.dat.gz loaded with 433468 networks in 0.10803 seconds and using 65.61 MiB. 325 | { 326 | "ip": "9.9.9.9", 327 | "country_code": "US", 328 | "country_name": "United States", 329 | "cidr": "9.9.9.9/32", 330 | "hostname": "dns9.quad9.net", 331 | "is_private": false, 332 | "asn_name": "QUAD9-AS-1", 333 | "elapsed_time": "0.000041463 sec", 334 | "elapsed_time_hostname": "0.014539683 sec" 335 | } 336 | { 337 | "ip": "15.20.25.30", 338 | "country_code": "US", 339 | "country_name": "United States", 340 | "cidr": "15.0.0.0/10", 341 | "hostname": "", 342 | "is_private": false, 343 | "asn_name": "ATT-IPFR", 344 | "elapsed_time": "0.000024009 sec" 345 | } 346 | # geoip2fast "2.3.4.5, 4.5.6.7, 8.9.10.11" | jq -r '.country_code' 347 | FR 348 | US 349 | US 350 | # ./geoip2fast.py 8.8.8.8,1.1.1.1,200.204.0.10 -d | jq -r '.hostname' 351 | dns.google 352 | one.one.one.one 353 | resolver1.telesp.net.br 354 | ``` 355 |
356 | 357 | ## GeoIP2Dat - update geoip2fast.dat.gz file anytime 358 | 359 | The updates of geoip2fast.dat.gz file will be published twice a week on Github https://github.com/rabuchaim/geoip2fast/releases/tag/LEGACY. You can also create your own dat file whenever you want, see instructions below. 360 | 361 | Download the Geolite2 Country CSV files from Maxmind website and place it into some diretory (in this example, was placed into ```/opt/maxmind/```). Extract this zip file into this directory and run ```geoip2dat``` to see the options. 362 | 363 | ![](https://raw.githubusercontent.com/rabuchaim/geoip2fast/main/geoip2fast-legacy/images/geoip2dat01.jpg) 364 | 365 | ![](https://raw.githubusercontent.com/rabuchaim/geoip2fast/main/geoip2fast-legacy/images/geoip2dat02.jpg) 366 | 367 | The options ```--country-dir``` and ```--output-dir``` are mandatory. Specify the path of extracted files in ```--country-dir``` option. And for ```--output-dir```, put the current path ```./```. 368 | 369 | If you want to add support for ASN data, add the option ```--asn-dir```. And if you want to add IPv6 support, just add ```--with-ipv6``` to your command line. 370 | 371 | You can choose the language of country locations. The default is ```en```. 372 | 373 | After creation of ```geoip2dat.dat.gz``` file, move or copy this file to the directory of your application or to the directory of GeoIP2Fast library. You choose. 374 | 375 | ![](https://raw.githubusercontent.com/rabuchaim/geoip2fast/main/geoip2fast-legacy/images/geoip2dat03.jpg) 376 | 377 | **From now you don't depend on anyone to have your data file updated.** There's no point the code being open-source if you're dependent of a single file. 378 | 379 | > *The Philosophers call it 'Libertas'* 380 | 381 |
382 | 383 | ## Automatic update of dat.gz files 384 | 385 | From version 1.1.10 onwards, it is now possible to update the dat.gz files that were made available in our releases repository. You can update via command line or via code. 386 | 387 | - Download the file "geoip2fast-asn-ipv6.dat.gz" and save it as "geoip2fast.dat.gz": 388 | 389 | ```python 390 | >>> from geoip2fast import GeoIP2Fast 391 | >>> G = GeoIP2Fast(verbose=True) 392 | GeoIP2Fast v1.1.10 is ready! geoip2fast.dat.gz loaded with 459270 networks in 0.03297 seconds and using 25.12 MiB. 393 | >>> update_result = G.update_file('geoip2fast-asn-ipv6.dat.gz','geoip2fast.dat.gz',verbose=False) 394 | >>> G.reload_data(verbose=True) 395 | GeoIP2Fast v1.1.10 is ready! geoip2fast.dat.gz loaded with 753871 networks in 0.12917 seconds and using 113.54 MiBTrue 396 | >>> 397 | ``` 398 | 399 | - Update all files: 400 | 401 | ```python 402 | >>> from geoip2fast import GeoIP2Fast 403 | >>> G = GeoIP2Fast() 404 | >>> update_result = G.update_all(verbose=True) 405 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast.dat.gz 406 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 407 | - Downloading geoip2fast.dat.gz... 100.00% of 1.06 MiB [6.51 MiB/s] [0.163 sec] 408 | - File saved to: /opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz 409 | 410 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast-ipv6.dat.gz 411 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 412 | - Downloading geoip2fast-ipv6.dat.gz... 100.00% of 1.73 MiB [9.66 MiB/s] [0.179 sec] 413 | - File saved to: /opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast-ipv6.dat.gz 414 | 415 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast-asn.dat.gz 416 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 417 | - Downloading geoip2fast-asn.dat.gz... 100.00% of 3.06 MiB [8.63 MiB/s] [0.354 sec] 418 | - File saved to: /opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast-asn.dat.gz 419 | 420 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast-asn-ipv6.dat.gz 421 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 422 | - Downloading geoip2fast-asn-ipv6.dat.gz... 100.00% of 4.09 MiB [7.66 MiB/s] [0.534 sec] 423 | - File saved to: /opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast-asn-ipv6.dat.gz 424 | >>> 425 | ``` 426 | - Update all files silently and verify if there are errors: 427 | ```python 428 | >>> from geoip2fast import GeoIP2Fast 429 | >>> G = GeoIP2Fast() 430 | >>> update_result = G.update_all(verbose=False) 431 | >>> errors_result = [item for item in update_result if item['error'] is not None] 432 | >>> print(errors_result) 433 | [] 434 | ``` 435 | - You can change the update URL if you want. 436 | ``` 437 | >>> G.get_update_url() 438 | 'https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/' 439 | >>> G.set_update_url("https://github.com/YOUR_OWN_REPO/YOUR_PROJECT/releases/download/latest/") 440 | True 441 | >>> G.get_update_url() 442 | 'https://github.com/YOUR_OWN_REPO/YOUR_PROJECT/releases/download/latest/' 443 | >>> 444 | ``` 445 | - Update the file "geoip2fast-asn-ipv6.dat.gz" and overwrite "geoip2fast.dat.gz" and print the result. 446 | ```python 447 | >>> from geoip2fast import GeoIP2Fast 448 | >>> G = GeoIP2Fast(verbose=True) 449 | GeoIP2Fast v1.1.10 is ready! geoip2fast.dat.gz loaded with 459270 networks in 0.04414 seconds and using 5.02 MiB. 450 | >>> update_result = G.update_file('geoip2fast-asn-ipv6.dat.gz','geoip2fast.dat.gz',verbose=False) 451 | >>> from pprint import pprint as pp 452 | >>> pp(update_result,sort_dicts=False) 453 | {'error': None, 454 | 'url': 'https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast-asn-ipv6.dat.gz', 455 | 'remote_filename': 'geoip2fast-asn-ipv6.dat.gz', 456 | 'last_modified_date': 'Mon, 27 Nov 2023 02:40:08 GMT', 457 | 'file_size': 4289564, 458 | 'file_destination': '/opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz', 459 | 'average_download_speed': '4.09 MiB/sec', 460 | 'elapsed_time': '0.587267'} 461 | >>> 462 | >>> G.reload_data(verbose=True) 463 | GeoIP2Fast v1.1.10 is ready! geoip2fast.dat.gz loaded with 753871 networks in 0.12245 seconds and using 57.55 MiB. 464 | >>> 465 | 466 | ``` 467 | - **Using the command line, no message will be displayed on the console unless you use the -v parameter** 468 | - Update all files via command line and save them in '/tmp/' directory: 469 | ```bash 470 | # geoip2fast --update-all --dest /tmp/ -v 471 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast.dat.gz 472 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 473 | - Downloading geoip2fast.dat.gz... 100.00% of 1.06 MiB [10.41 MiB/s] [0.102 sec] 474 | - File saved to: /tmp/geoip2fast.dat.gz 475 | 476 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast-ipv6.dat.gz 477 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 478 | - Downloading geoip2fast-ipv6.dat.gz... 100.00% of 1.73 MiB [9.46 MiB/s] [0.183 sec] 479 | - File saved to: /tmp/geoip2fast-ipv6.dat.gz 480 | 481 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast-asn.dat.gz 482 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 483 | - Downloading geoip2fast-asn.dat.gz... 100.00% of 3.06 MiB [7.06 MiB/s] [0.433 sec] 484 | - File saved to: /tmp/geoip2fast-asn.dat.gz 485 | 486 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast-asn-ipv6.dat.gz 487 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 488 | - Downloading geoip2fast-asn-ipv6.dat.gz... 100.00% of 4.09 MiB [7.31 MiB/s] [0.560 sec] 489 | - File saved to: /tmp/geoip2fast-asn-ipv6.dat.gz 490 | ``` 491 | - Update the file "geoip2fast-asn-ipv6.dat.gz" and overwrite "geoip2fast.dat.gz" 492 | ```bash 493 | # geoip2fast --update-file geoip2fast-asn-ipv6.dat.gz --dest geoip2fast.dat.gz -v 494 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast-asn-ipv6.dat.gz 495 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 496 | - Downloading geoip2fast-asn-ipv6.dat.gz... 100.00% of 4.09 MiB [4.29 MiB/s] [0.954 sec] 497 | - File saved to: /opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz 498 | ``` 499 | - Update the file "geoip2fast.dat.gz" and save it in the library path 500 | ```bash 501 | # geoip2fast --update-file geoip2fast.dat.gz -v 502 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast.dat.gz 503 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 504 | - Downloading geoip2fast.dat.gz... 100.00% of 1.06 MiB [9.54 MiB/s] [0.111 sec] 505 | - File saved to: /opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz 506 | ``` 507 | - Update the file "geoip2fast.dat.gz" and save it in the library path 508 | 509 | ```bash 510 | # geoip2fast --update-file geoip2fast.dat.gz 511 | # echo $? 512 | 0 513 | ``` 514 | - An example of a simulated download failure: 515 | ```bash 516 | # geoip2fast.py --update-file geoip2fast.dat.gz -v 517 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/aLEGACY_v1.1.9/geoip2fast.dat.gz 518 | - Error: HTTP Error 404: Not Found - https://github.com/rabuchaim/geoip2fast/releases/download/aLEGACY_v1.1.9/geoip2fast.dat.gz 519 | # echo $? 520 | 1 521 | ``` 522 | ```bash 523 | # geoip2fast.py --update-file geoip2fast.dat.gz -v 524 | - Opening URL https://github.com/rabuchaim/geoip2fast/releases/download/LEGACY/geoip2fast.dat.gz 525 | - Last Modified Date: Mon, 27 Nov 2023 02:40:08 GMT 526 | PermissionError: [Errno 1] Operation not permitted: '/opt/geoip2fast/geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz' 527 | # echo $? 528 | 1 529 | ``` 530 | ## Create your own GeoIP CLI with 6 lines 531 | 532 | 1. Create a file named ```geoipcli.py``` and save it in your home directory with the text below: 533 | ```python 534 | #!/usr/bin/env python3 535 | import os, sys, geoip2fast 536 | if len(sys.argv) > 1 and sys.argv[1] is not None: 537 | geoip2fast.GeoIP2Fast().lookup(sys.argv[1]).pp_json(print_result=True) 538 | else: 539 | print(f"Usage: {os.path.basename(__file__)} ") 540 | ``` 541 | 2. Give execution permisstion to your file and create a symbolic link to your new file into ```/usr/sbin``` folder, like this (let's assume that you saved the file into directory /root) 542 | ```bash 543 | chmod 750 /root/geoipcli.py 544 | ln -s /root/geoipcli.py /usr/sbin/geoipcli 545 | ``` 546 | 3. Now, you just need to call ```geoipcli``` from any path. 547 | ```bash 548 | # geoipcli 549 | Usage: geoipcli 550 | 551 | # geoipcli 1.2.3.4 552 | { 553 | "ip": "1.2.3.4", 554 | "country_code": "AU", 555 | "country_name": "Australia", 556 | "cidr": "1.2.3.0/24", 557 | "hostname": "", 558 | "is_private": false, 559 | "elapsed_time": "0.000019727 sec" 560 | } 561 | 562 | # geoipcli x.y.z.w 563 | { 564 | "ip": "x.y.z.w", 565 | "country_code": "", 566 | "country_name": "", 567 | "cidr": "", 568 | "hostname": "", 569 | "is_private": false, 570 | "elapsed_time": "0.000012493 sec" 571 | } 572 | 573 | # geoipcli 57.242.128.144 574 | { 575 | "ip": "57.242.128.144", 576 | "country_code": "--", 577 | "country_name": "", 578 | "cidr": "", 579 | "hostname": "", 580 | "is_private": false, 581 | "elapsed_time": "0.000019127 sec" 582 | } 583 | ``` 584 | 585 | ## GeoIP libraries that inspired me 586 | 587 | **GeoIP2Nation - https://pypi.org/project/geoip2nation/** (Created by Avi Asher) 588 | 589 | This library uses sqlite3 in-memory tables and use the same search concepts as GeoIP2Fast (based on search by the first´s IPs). Simple and fast! Unfortunately it is no longer being updated and that is why I developed GeoIP2Fast. 590 | 591 | **GeoIP2 - https://pypi.org/project/geoip2/** (created by Maxmind) 592 | 593 | This is the best library to work with Maxmind (paid subscription or with the free version). You can use http requests to Maxmind services or work with local Maxmind MMDB binary files. Pretty fast too. Sign-up to have access to all files of the free version https://dev.maxmind.com/geoip/geolite2-free-geolocation-data 594 | 595 | **\* Maxmind is a registered trademark** - https://www.maxmind.com 596 | 597 | ## TO DO list 598 | - a pure-python version for REDIS with a very small footprint (pure protocol, won´t use any REDIS library) **<<< On the way at https://github.com/rabuchaim/geoip2redis** 599 | - a GeoIP Server; **<<< On the way a docker container, your own GeoIP server inside your network. With rest API or socket (super fast)** 600 | - a mod_geoip2fast for NGINX; 601 | - a better manual, maybe at readthedocs.io; 602 | - **Done in v1.1.10/v1.2.1** - automatic update of dat.gz files; 603 | - **Done in v1.2.0** - a version with cities; 604 | - **Done in v1.1.0** - *IPv6 support*. 605 | - **Done in v1.0.5** - *a version with ASN*. 606 | - **Done in v1.0.2** - *provide a script to update the base. If you have the paid subscription of Maxmind, you can download the files, extract into some directory and use this script to create your own geoip2fast.dat.gz file with the most complete, reliable and updated GeoIP information*. 607 | 608 | ## Sugestions, feedbacks, bugs, wrong locations... 609 | E-mail me: ricardoabuchaim at gmail.com 610 | -------------------------------------------------------------------------------- /geoip2fast-legacy/geoip2fast-1.1.11.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/geoip2fast-1.1.11.tar.gz -------------------------------------------------------------------------------- /geoip2fast-legacy/geoip2fast/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | # -*- coding: utf-8 -*- 4 | """ 5 | .oPYo. o .oPYo. .oPYo. ooooo o 6 | 8 8 8 8 8 `8 8 8 7 | 8 .oPYo. .oPYo. 8 o8YooP' oP' o8oo .oPYo. .oPYo. o8P 8 | 8 oo 8oooo8 8 8 8 8 .oP' 8 .oooo8 Yb.. 8 9 | 8 8 8. 8 8 8 8 8' 8 8 8 'Yb. 8 10 | `YooP8 `Yooo' `YooP' 8 8 8ooooo 8 `YooP8 `YooP' 8 11 | :....8 :.....::.....:..:..:::::.......:..:::::.....::.....:::..: 12 | :::::8 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 13 | :::::..::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 14 | """ 15 | __author__ = 'Ricardo Abuchaim' 16 | __license__ = 'MIT' 17 | __version__ = "1.1.11" 18 | __releasedate__ = "20/Jun/2024" 19 | 20 | from geoip2fast.geoip2fast import GeoIP2Fast,GeoIPDetail,UpdateGeoIP2Fast -------------------------------------------------------------------------------- /geoip2fast-legacy/geoip2fast/geoip2fast-asn-ipv6.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/geoip2fast/geoip2fast-asn-ipv6.dat.gz -------------------------------------------------------------------------------- /geoip2fast-legacy/geoip2fast/geoip2fast-asn.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/geoip2fast/geoip2fast-asn.dat.gz -------------------------------------------------------------------------------- /geoip2fast-legacy/geoip2fast/geoip2fast-ipv6.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/geoip2fast/geoip2fast-ipv6.dat.gz -------------------------------------------------------------------------------- /geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/geoip2fast/geoip2fast.dat.gz -------------------------------------------------------------------------------- /geoip2fast-legacy/images/cli.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/cli.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/images/clicode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/clicode.png -------------------------------------------------------------------------------- /geoip2fast-legacy/images/code_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/code_example.png -------------------------------------------------------------------------------- /geoip2fast-legacy/images/coverage_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/coverage_test.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/images/coverage_verbose.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/coverage_verbose.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/images/geoip2dat01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/geoip2dat01.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/images/geoip2dat02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/geoip2dat02.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/images/geoip2dat03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/geoip2dat03.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/images/geoip2fast-win32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/geoip2fast-win32.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/images/geoip2fast.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/geoip2fast.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/images/geoip2fast_selftest.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/geoip2fast_selftest.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/images/geoip2fastv4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/geoip2fastv4.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/images/geoip2fastv6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/geoip2fastv6.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/images/geoipv6_coverage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/geoipv6_coverage.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/images/speed_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast-legacy/images/speed_test.jpg -------------------------------------------------------------------------------- /geoip2fast-legacy/setup.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | from setuptools import setup, find_packages 3 | 4 | setup( 5 | name='geoip2fast', 6 | version='1.1.11', 7 | description='GeoIP2Fast is the fastest GeoIP2 country/asn lookup library that supports IPv4 and IPv6. A search takes less than 0.00003 seconds. It has its own data file updated twice a week with Maxmind-Geolite2-CSV, supports IPv4/IPv6 and is Pure Python!', 8 | url='https://github.com/rabuchaim/geoip2fast/tree/main/geoip2fast-legacy', 9 | author='Ricardo Abuchaim', 10 | author_email='ricardoabuchaim@gmail.com', 11 | maintainer='Ricardo Abuchaim', 12 | maintainer_email='ricardoabuchaim@gmail.com', 13 | project_urls={ 14 | "Issue Tracker": "https://github.com/rabuchaim/geoip2fast/issues", 15 | "Source code": "https://github.com/rabuchaim/geoip2fast/tree/main/geoip2fast-legacy/", 16 | "Latest DAT files for v1.1.X": "https://github.com/rabuchaim/geoip2fast/releases/tag/LEGACY", 17 | }, 18 | bugtrack_url='https://github.com/rabuchaim/geoip2fast/issues', 19 | license='MIT', 20 | keywords=['geoip','geoip2','geolite2','maxmind','geoip2fast','geolocation','geolocalization','geo ip','ipaddress','ip','geo','ipv4','ipv6','pure-python','purepython','pure python','geoiptofast','geoiptoofast','geoip2dat','mmdb','tools'], 21 | packages=['geoip2fast'], 22 | py_modules=['geoip2fast','geoip2dat'], 23 | package_dir = {'geoip2fast': 'geoip2fast'}, 24 | include_package_data=True, 25 | zip_safe = False, 26 | package_data={ 27 | 'geoip2fast': [ 28 | 'CHANGELOG', 29 | 'geoip2fast.dat.gz', 30 | 'geoip2fast-ipv6.dat.gz', 31 | 'geoip2fast-asn.dat.gz', 32 | 'geoip2fast-asn-ipv6.dat.gz', 33 | 'tests/geoip2fast_test.py', 34 | 'tests/speed_test.py', 35 | 'tests/coverage_test.py', 36 | 'tests/compare_with_mmdb.py', 37 | 'tests/random_test.py', 38 | 'tests/geoipcli.py', 39 | ], 40 | }, 41 | entry_points={ 42 | 'console_scripts': [ 43 | 'geoip2fast = geoip2fast.geoip2fast:main_function', 44 | 'geoip2dat = geoip2fast.geoip2dat:main_function' 45 | ] 46 | }, 47 | python_requires=">=3.7", 48 | install_requires=[], 49 | classifiers=[ 50 | 'Development Status :: 5 - Production/Stable', 51 | 'Topic :: Security', 52 | 'Topic :: Internet', 53 | 'Topic :: Internet :: Finger', 54 | 'Topic :: Scientific/Engineering', 55 | 'Topic :: System :: Monitoring', 56 | 'Topic :: System :: Networking', 57 | 'Topic :: System :: Systems Administration', 58 | 'Topic :: Software Development :: Libraries', 59 | 'Topic :: Software Development :: Libraries :: Python Modules', 60 | 'Topic :: Software Development :: Localization', 61 | 'Topic :: Utilities', 62 | 'Intended Audience :: Developers', 63 | 'Intended Audience :: Information Technology', 64 | 'Intended Audience :: System Administrators', 65 | 'Operating System :: MacOS', 66 | 'Operating System :: Microsoft :: Windows :: Windows 10', 67 | 'Operating System :: Microsoft :: Windows :: Windows 11', 68 | 'Operating System :: Unix', 69 | 'Operating System :: POSIX', 70 | 'Operating System :: POSIX :: Linux', 71 | 'Operating System :: POSIX :: BSD', 72 | 'Operating System :: POSIX :: BSD :: FreeBSD', 73 | 'Programming Language :: Python', 74 | 'Programming Language :: Python :: 3', 75 | 'Programming Language :: Python :: 3.7', 76 | 'Programming Language :: Python :: 3.8', 77 | 'Programming Language :: Python :: 3.9', 78 | 'Programming Language :: Python :: 3.10', 79 | 'Programming Language :: Python :: 3.11', 80 | 'Programming Language :: Python :: 3.12', 81 | 'Programming Language :: Python :: 3.13', 82 | 'Programming Language :: Python :: Implementation :: PyPy', 83 | 'License :: OSI Approved :: MIT License', 84 | ], 85 | long_description=codecs.open("README.md","r","utf-8").read(), 86 | long_description_content_type='text/markdown', 87 | ) 88 | -------------------------------------------------------------------------------- /geoip2fast-legacy/tests/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | GeoLite2-Country.mmdb 3 | -------------------------------------------------------------------------------- /geoip2fast-legacy/tests/compare_with_mmdb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | ##──── This test shows what GeoIP2Fast has that Maxmind's MMDB doesn't, and vice versa ────────────────────────────────────────── 4 | ##──── Download the file "GeoLite2-Country.mmdb" from Maxmind website and put it in the same directory ─────────────────────────── 5 | ##──── of this script to run the test ──────────────────────────────────────────────────────────────────────────────────────────── 6 | ##──── if you have geoipupdate installed, you should already have this file in the directory /var/lib/GeoIP/ It wIll works too ─── 7 | 8 | ##──── The BLUE lines shows that GeoIP2Fast result is the same as the Maxmind result with MMDB files. ──────────────────────────── 9 | ##──── Lines in RED means that there is a divergence between the base of GeoIP2Fast and Maxmind. ───────────────────────────────── 10 | ##──── You can confirm 'who is right' using whois or geoiplookup application in linux ──────────────────────────────────────────── 11 | 12 | import os, sys, re, ctypes 13 | from geoip2fast import GeoIP2Fast 14 | from random import randrange 15 | 16 | try: 17 | import geoip2.database # pip install geoip2 18 | except: 19 | print("Run 'pip install geoip2' first and try again") 20 | sys.exit(1) 21 | 22 | def cRed(msg): 23 | return '\033[91m'+msg+'\033[0m' 24 | def cBlue(msg): 25 | return '\033[94m'+msg+'\033[0m' 26 | 27 | def create_iplist(quant): 28 | a_list = [] 29 | for I in range(quant): 30 | IP = f"{randrange(1,224)}.{randrange(0,254)}.{randrange(0,254)}.{randrange(0,254)}" 31 | a_list.append(IP) 32 | if I % 5000 == 0: 33 | print("\rGenerating random IPs: "+str(I+1),end="") 34 | print(f"\rGenerating randomically {len(a_list)} IPs...") 35 | return a_list 36 | 37 | def get_geoip_from_mmdb(ipaddr)->str: 38 | try: 39 | response = reader.country(ipaddr) 40 | return str(response.country.iso_code) 41 | except Exception as ERR: 42 | # print(str(ERR)) 43 | return "--" 44 | 45 | if __name__ == "__main__": 46 | GeoIP = GeoIP2Fast(verbose=False) 47 | if os.stat('/var/lib/GeoIP/GeoLite2-Country.mmdb').st_mode: 48 | reader = geoip2.database.Reader('/var/lib/GeoIP/GeoLite2-Country.mmdb',mode=geoip2.database.MODE_MMAP) 49 | else: 50 | reader = geoip2.database.Reader('GeoLite2-Country.mmdb',mode=geoip2.database.MODE_MMAP) 51 | 52 | gIpList = create_iplist(100000) 53 | counter = 0 54 | print("") 55 | print("This test shows what GeoIP2Fast has that Maxmind's MMDB files doesn't") 56 | print("") 57 | print(f"{'IP address'.center(20)}|{'from GeoIP2Fast'.center(20)}|{'from MMDB file'.center(20)}|") 58 | for IP in gIpList: 59 | geoip_info = GeoIP.lookup(IP) 60 | getFromLocal = geoip_info.country_code 61 | getFromMMDB = get_geoip_from_mmdb(IP) 62 | 63 | if getFromLocal == "": 64 | getFromLocal = "--" 65 | 66 | logString = (f"{('IP: '+IP).ljust(20)}|{str(getFromLocal).center(20)}|{str(getFromMMDB).center(20)}|") 67 | 68 | if getFromMMDB != getFromLocal: 69 | print(cRed("\r"+logString)) 70 | counter += 1 71 | else: 72 | print(cBlue("\r"+logString),end="") 73 | 74 | # clear the last line 75 | if IP == gIpList[-1]: 76 | print("\r".ljust(105," "),end="") 77 | 78 | print("\n") 79 | print("Lines in RED means that there are a divergence between the base of GeoIP2Fast and Maxmind.") 80 | print("You can confirm 'who is right' checking different sources like a geoip lookup application ") 81 | print("like geoiplookup (ubuntu: apt install geoip-bin) or a whois service (ubuntu: apt install whois)") 82 | print("") 83 | print(" From %s random IP addresses tested, were found %s IP addresses"%((re.sub(r'(? "+IP.ljust(15)+" "+str(geoip.country_code).ljust(3)+str(geoip.country_name).ljust(35)+ \ 21 | " ["+geoip.elapsed_time+"]\tAfter cache: ["+GEOIP.lookup(IP).elapsed_time+"] "+geoip.cidr) 22 | 23 | print("") 24 | 25 | result = GEOIP.lookup("200.204.0.10") 26 | print(result) 27 | 28 | # Before call the function get_hostname(), 'hostname' property will always be empty 29 | print("Hostname: "+result.hostname+"\t\t\t << must be empty before call result.get_hostname()") 30 | result.get_hostname() 31 | 32 | print(result) 33 | 34 | # to work with output as a dict, use the function to_dict() 35 | print(result.to_dict()['country_code'],result.to_dict()['country_name']) 36 | 37 | # To pretty print the object result like json.dumps() 38 | result = GEOIP.lookup("200.204.0.138") 39 | result.get_hostname() 40 | print(result.pp_json(indent=3,sort_keys=False)) 41 | 42 | # info about internal cache 43 | print(GEOIP.cache_info()) 44 | 45 | # clear the internal cache 46 | print(GEOIP.clear_cache()) 47 | 48 | # info about internal cache 49 | print(GEOIP.cache_info()) 50 | 51 | # to check the date of the CSV files used to create the .dat file 52 | print(GEOIP.get_source_info()) 53 | 54 | print("") 55 | 56 | sys.exit(0) -------------------------------------------------------------------------------- /geoip2fast-legacy/tests/random_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from geoip2fast import GeoIP2Fast 3 | from random import randrange 4 | from time import sleep 5 | 6 | MAX_IPS = 1000000 7 | GeoIP = GeoIP2Fast(verbose=True) 8 | 9 | print("\n- Starting a %d random IP test in"%(MAX_IPS),end="") 10 | print(" 3...",end="") 11 | sleep(1) 12 | print(" 2...",end="") 13 | sleep(1) 14 | print(" 1...",end="") 15 | sleep(1) 16 | print("\n") 17 | 18 | avgList, avgCacheList = [], [] 19 | 20 | total = 0 21 | while total < MAX_IPS: 22 | IP = f"{randrange(1,223)}.{randrange(0,254)}.{randrange(0,254)}.{randrange(0,254)}" 23 | result = GeoIP.lookup(IP) 24 | avgList.append(float(result.elapsed_time.split(" ")[0])) 25 | total += 1 26 | cachedResult = GeoIP.lookup(IP) 27 | avgCacheList.append(float(cachedResult.elapsed_time.split(" ")[0])) 28 | print(f"IP {result.ip.ljust(20)}{result.country_code.ljust(4)}{result.country_name.ljust(40)}[{result.elapsed_time}] - Cached [{cachedResult.elapsed_time}]") 29 | print("") 30 | print("Test with %d randomic IP addresses."%(MAX_IPS)) 31 | print("\t- Average Lookup Time: %.9f seconds. "%(sum(avgList)/MAX_IPS)) 32 | print("\t- Average Cached Lookups: %.9f seconds. "%(sum(avgCacheList)/MAX_IPS)) 33 | print("") 34 | 35 | -------------------------------------------------------------------------------- /geoip2fast-legacy/tests/speed_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from geoip2fast import GeoIP2Fast 3 | 4 | GeoIP = GeoIP2Fast(verbose=True) 5 | 6 | print("\n- Starting 'lookups per second' test...\n") 7 | GeoIP.calculate_speed(print_result=True) 8 | print("") 9 | -------------------------------------------------------------------------------- /geoip2fast/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | # -*- coding: utf-8 -*- 4 | """ 5 | .oPYo. o .oPYo. .oPYo. ooooo o 6 | 8 8 8 8 8 `8 8 8 7 | 8 .oPYo. .oPYo. 8 o8YooP' oP' o8oo .oPYo. .oPYo. o8P 8 | 8 oo 8oooo8 8 8 8 8 .oP' 8 .oooo8 Yb.. 8 9 | 8 8 8. 8 8 8 8 8' 8 8 8 'Yb. 8 10 | `YooP8 `Yooo' `YooP' 8 8 8ooooo 8 `YooP8 `YooP' 8 11 | :....8 :.....::.....:..:..:::::.......:..:::::.....::.....:::..: 12 | :::::8 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 13 | :::::..::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 14 | """ 15 | __author__ = 'Ricardo Abuchaim' 16 | __license__ = 'MIT' 17 | __version__ = "1.2.2" 18 | __releasedate__ = "20/Jun/2024" 19 | 20 | from geoip2fast.geoip2fast import GeoIP2Fast,GeoIPDetail,GeoIPDetailCity,UpdateGeoIP2Fast -------------------------------------------------------------------------------- /geoip2fast/geoip2fast-asn-ipv6.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast/geoip2fast-asn-ipv6.dat.gz -------------------------------------------------------------------------------- /geoip2fast/geoip2fast-asn.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast/geoip2fast-asn.dat.gz -------------------------------------------------------------------------------- /geoip2fast/geoip2fast-city-asn-ipv6.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast/geoip2fast-city-asn-ipv6.dat.gz -------------------------------------------------------------------------------- /geoip2fast/geoip2fast-city-asn.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast/geoip2fast-city-asn.dat.gz -------------------------------------------------------------------------------- /geoip2fast/geoip2fast-city-ipv6.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast/geoip2fast-city-ipv6.dat.gz -------------------------------------------------------------------------------- /geoip2fast/geoip2fast-city.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast/geoip2fast-city.dat.gz -------------------------------------------------------------------------------- /geoip2fast/geoip2fast-ipv6.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast/geoip2fast-ipv6.dat.gz -------------------------------------------------------------------------------- /geoip2fast/geoip2fast.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/geoip2fast/geoip2fast.dat.gz -------------------------------------------------------------------------------- /geoip2fast/geoip2fastmin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | # -*- coding: utf-8 -*- 4 | """ 5 | GeoIP2FastMin - Version v1.2.2 6 | 7 | Author: Ricardo Abuchaim - ricardoabuchaim@gmail.com 8 | https://github.com/rabuchaim/geoip2fast/ 9 | 10 | License: MIT 11 | 12 | .oPYo. o .oPYo. .oPYo. ooooo o o o o 13 | 8 8 8 8 8 `8 8 8 8b d8 14 | 8 .oPYo. .oPYo. 8 o8YooP' oP' o8oo .oPYo. .oPYo. o8P 8`b d'8 o8 odYo. 15 | 8 oo 8oooo8 8 8 8 8 .oP' 8 .oooo8 Yb.. 8 8 `o' 8 8 8' `8 16 | 8 8 8. 8 8 8 8 8' 8 8 8 'Yb. 8 8 8 8 8 8 17 | `YooP8 `Yooo' `YooP' 8 8 8ooooo 8 `YooP8 `YooP' 8 8 8 8 8 8 18 | :....8 :.....::.....:..:..:::::.......:..:::::.....::.....:::..:::..::::..:....::.. 19 | :::::8 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 20 | :::::..:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 21 | 22 | This version has been reduced to essential functions. Just copy the entire class and paste it into your 23 | code and use the GeoIP2Fast databases with countries, cities and asn. Is Pure Python! No dependencies. 24 | 25 | From GeoIP2Fast's natural code, we removed the coverage testing functions, missing ips, automatic updates, 26 | and a few other things. Nothing that affects speed. Usage examples: 27 | 28 | G = GeoIP2FastMin(verbose=False,geoip2fast_data_file="") 29 | 30 | print(G.startup_line_text) 31 | print(G.database_path) 32 | 33 | result = G.lookup("1.1.1.1") 34 | 35 | print(result.country_code) 36 | print(result.country_name) 37 | print(result.cidr) 38 | print(result.pp_json()) 39 | G.self_test(max_ips=30) 40 | 41 | """ 42 | 43 | class GeoIP2FastMin(object): 44 | import os, sys, bisect, pickle, ctypes, subprocess, gzip, json, random, socket, struct, binascii, time 45 | __appid__ = "GeoIP2Fast" 46 | __version__ = "1.2.2" 47 | GEOIP2FAST_DAT_GZ_FILE = os.path.join(os.path.dirname(__file__),"geoip2fast.dat.gz") 48 | os.environ["PYTHONWARNINGS"] = "ignore" 49 | os.environ["PYTHONIOENCODING"] = "utf-8" 50 | ##──── Define here what do you want to return if one of these errors occurs ───────────────────────────────────────────────────── 51 | ##──── ECCODE = Error Country Code ─────────────────────────────────────────────────────────────────────────────────────────────── 52 | GEOIP_ECCODE_PRIVATE_NETWORKS, GEOIP_ECCODE_NETWORK_NOT_FOUND = "--", "--" 53 | GEOIP_ECCODE_INVALID_IP, GEOIP_ECCODE_LOOKUP_INTERNAL_ERROR = "", "" 54 | GEOIP_NOT_FOUND_STRING = "" 55 | GEOIP_INTERNAL_ERROR_STRING = "" 56 | GEOIP_INVALID_IP_STRING = "" 57 | ##──── Number os possible IPs in a network range. (/0, /1 .. /8 .. /24 .. /30, /31, /32) ───────────────────────────────────────── 58 | numIPsv4 = sorted([2**num for num in range(0,33)],reverse=True) # from 0 to 32 59 | numIPsv6 = sorted([2**num for num in range(0,129)],reverse=True) # from 0 to 128 60 | MAX_IPv4 = numIPsv4[0] 61 | ##──── CLASS INIT ──────────────────────────────────────────────────────────────────────────────────────────────────────────────── 62 | def __init__(self, verbose=False, geoip2fast_data_file=""): 63 | self.name = "GeoIP2FastMin" 64 | self.ipv6, self.city, self.asn, self.is_loaded = False, False, False, False 65 | self.data_file, self._load_data_text = "", '' 66 | self.verbose = verbose 67 | if verbose == False: 68 | self._print_verbose = self.__print_verbose_empty 69 | ##────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 70 | self.error_code_private_networks = self.GEOIP_ECCODE_PRIVATE_NETWORKS 71 | self.error_code_network_not_found = self.GEOIP_ECCODE_NETWORK_NOT_FOUND 72 | self.error_code_invalid_ip = self.GEOIP_ECCODE_INVALID_IP 73 | self.error_code_lookup_internal_error = self.GEOIP_ECCODE_LOOKUP_INTERNAL_ERROR 74 | ##────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 75 | if geoip2fast_data_file != "": 76 | try: 77 | if self.os.path.isfile(geoip2fast_data_file) == True: 78 | self.data_file = geoip2fast_data_file 79 | else: 80 | if geoip2fast_data_file.find("/") < 0: 81 | databasePath = self.__locate_database_file(geoip2fast_data_file) 82 | if databasePath is False: 83 | raise self.GeoIPError("Unable to find GeoIP2Fast database file %s"%(self.os.path.basename(geoip2fast_data_file))) 84 | else: 85 | self.data_file = databasePath 86 | else: 87 | raise self.GeoIPError("Check path of specified file and try again.") 88 | except Exception as ERR: 89 | raise self.GeoIPError("Unable to access the specified file %s. %s"%(geoip2fast_data_file,str(ERR))) 90 | 91 | self.__load_data(self.data_file, verbose) 92 | ##──── GeoIP2Fast Exception Class ──────────────────────────────────────────────────────────────────────────────────────────────── 93 | class GeoIPError(Exception): 94 | def __init__(self, message): 95 | self.message = message 96 | def __str__(self): 97 | return self.message 98 | def __repr__(self): 99 | return self.message 100 | class CityDetail(object): 101 | def __init__(self, city_string="||||"): 102 | try: 103 | self.name, self.subdivision_code, self.subdivision_name, self.subdivision2_code, self.subdivision2_name = city_string.split("|") 104 | except: 105 | self.name, self.subdivision_code, self.subdivision_name, self.subdivision2_code, self.subdivision2_name = GeoIP2FastMin.GEOIP_INTERNAL_ERROR_STRING,"","","","" 106 | def to_dict(self): 107 | return { 108 | "name": self.name, 109 | "subdivision_code": self.subdivision_code, 110 | "subdivision_name": self.subdivision_name} 111 | ##────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 112 | class GeoIPDetail(object): 113 | def __init__(self, ip, country_code="", country_name="", cidr="", is_private=False, asn_name="", asn_cidr="", elapsed_time=""): 114 | self.ip, self.country_code, self.country_name, self.cidr, self.hostname = ip, country_code, country_name, cidr, "" 115 | self.is_private, self.asn_name, self.asn_cidr, self.elapsed_time = is_private, asn_name, asn_cidr, elapsed_time 116 | @property 117 | def city(self): 118 | return GeoIP2FastMin.CityDetail() 119 | def __str__(self): 120 | return f"{self.__dict__}" 121 | def __repr__(self): 122 | return f"{self.to_dict()}" 123 | def get_hostname(self,dns_timeout=0.1): 124 | try: 125 | startTime = GeoIP2FastMin.time.perf_counter() 126 | self.socket.setdefaulttimeout(dns_timeout) 127 | result = self.socket.gethostbyaddr(self.ip)[0] 128 | self.hostname = result if result != self.ip else "" 129 | self.elapsed_time_hostname = "%.9f sec"%(GeoIP2FastMin.time.perf_counter()-startTime) 130 | return self.hostname 131 | except OSError as ERR: 132 | self.hostname = f"<{str(ERR.strerror)}>" 133 | return self.hostname 134 | except Exception as ERR: 135 | self.hostname = "" 136 | return self.hostname 137 | def to_dict(self): 138 | try: 139 | d = { 140 | "ip": self.ip, 141 | "country_code": self.country_code, 142 | "country_name": self.country_name, 143 | "city":'', 144 | "cidr": self.cidr, 145 | "hostname": self.hostname, 146 | "asn_name": self.asn_name, 147 | "asn_cidr": self.asn_cidr, 148 | "is_private": self.is_private, 149 | "elapsed_time": self.elapsed_time 150 | } 151 | if not hasattr(self, 'city'): 152 | del d['city'] 153 | try: 154 | a = self.elapsed_time_hostname 155 | d['elapsed_time_hostname'] = self.elapsed_time_hostname 156 | except: 157 | pass 158 | return d 159 | except Exception as ERR: 160 | raise GeoIP2FastMin.GeoIPError("Failed to_dict() %s"%(str(ERR))) 161 | def pp_json(self,indent=3,sort_keys=False,print_result=False): 162 | try: 163 | dump = GeoIP2FastMin.json.dumps(self.to_dict(),sort_keys=sort_keys,indent=indent,ensure_ascii=False) 164 | if print_result == True: 165 | print(dump) 166 | return dump 167 | except Exception as ERR: 168 | raise GeoIP2FastMin.GeoIPError("Failed pp_json() %s"%(str(ERR))) 169 | class GeoIPDetailCity(GeoIPDetail): 170 | """Extended version of GeoIPDetail with city information 171 | """ 172 | def __init__(self, ip, country_code="", country_name="", city=None, cidr="", is_private=False, asn_name="", asn_cidr="", elapsed_time=""): 173 | super().__init__(ip, country_code, country_name, cidr, is_private, asn_name, asn_cidr, elapsed_time) 174 | self._city = city if city else GeoIP2FastMin.CityDetail() 175 | @property 176 | def city(self): 177 | return self._city 178 | @city.setter 179 | def city(self, value): 180 | raise AttributeError("Cannot set 'city' attribute in GeoIPDetailCity") 181 | def to_dict(self): 182 | base_dict = super().to_dict() 183 | base_dict['city'] = self.city.to_dict() 184 | return base_dict 185 | ##──── Function used to avoid "if verbose == True". The code is swaped at __init__ ─────────────────────────────────────────────── 186 | def __print_verbose_empty(self,msg):return 187 | def __print_verbose_regular(self,msg): 188 | print(msg,flush=True) 189 | def _print_debug(self,msg): 190 | print("[DEBUG] "+msg,flush=True) 191 | def _print_verbose(self,msg): 192 | print(msg,flush=True) 193 | ##──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 194 | ##────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 195 | def __locate_database_file(self, filename): 196 | try: 197 | curDir = self.os.path.join(self.os.path.abspath(self.os.path.curdir),filename) # path of your application 198 | libDir = self.os.path.join(self.os.path.dirname(__file__),filename) # path where the library is installed 199 | except Exception as ERR: 200 | raise GeoIP2FastMin.GeoIPError("Unable to determine the path of application %s. %s"%(filename,str(ERR))) 201 | try: 202 | self.os.stat(curDir).st_mode 203 | return curDir 204 | except Exception as ERR: 205 | try: 206 | self.os.stat(libDir).st_mode 207 | return libDir 208 | except Exception as ERR: 209 | raise GeoIP2FastMin.GeoIPError("Unable to determine the path of library %s - %s"%(filename,str(ERR))) 210 | ##────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 211 | def __load_data(self, gzip_data_file:str, verbose=False)->bool: 212 | global __DAT_VERSION__, source_info, totalNetworks,mainListNamesCountry,geoipCountryNamesDict,geoipCountryCodesList,\ 213 | mainIndex,mainListNamesCountry,mainListFirstIP,mainListIDCountryCodes,mainListNetlength,\ 214 | mainIndexASN,mainListNamesASN,mainListFirstIPASN,mainListIDASN,mainListNetlengthASN,\ 215 | mainListNamesCity, mainListIDCity 216 | if self.is_loaded == True: 217 | return True 218 | self._print_verbose = self.__print_verbose_regular if verbose == True else self.__print_verbose_empty 219 | startMem = self.get_mem_usage() 220 | startLoadData = self.time.perf_counter() 221 | ##──── Try to locate the database file in the directory of the application that called GeoIP2Fast() ───────────────────────── 222 | ##──── or in the directory of the GeoIP2Fast Library ──────────────────────────────────────────────────────────────────────── 223 | try: 224 | if gzip_data_file == "": 225 | gzip_data_file = GeoIP2FastMin.GEOIP2FAST_DAT_GZ_FILE 226 | try: 227 | databasePath = self.__locate_database_file(self.os.path.basename(gzip_data_file)) 228 | if databasePath is False: 229 | raise GeoIP2FastMin.GeoIPError("(1) Unable to find GeoIP2Fast database file %s"%(self.os.path.basename(gzip_data_file))) 230 | else: 231 | self.data_file = databasePath 232 | except Exception as ERR: 233 | raise GeoIP2FastMin.GeoIPError("(2) Unable to find GeoIP2Fast database file %s %s"%(self.os.path.basename(gzip_data_file),str(ERR))) 234 | except Exception as ERR: 235 | raise GeoIP2FastMin.GeoIPError("Failed at locate data file %s"%(str(ERR))) 236 | ##──── Open the dat.gz file ────────────────────────────────────────────────────────────────────────────────────────────────────── 237 | try: 238 | try: 239 | inputFile = self.gzip.open(str(self.data_file),'rb') 240 | except: 241 | try: 242 | inputFile = open(str(self.data_file).replace(".gz",""),'rb') 243 | self.data_file = self.data_file.replace(".gz","") 244 | except Exception as ERR: 245 | raise GeoIP2FastMin.GeoIPError(f"Unable to find {gzip_data_file} or {gzip_data_file} {str(ERR)}") 246 | except Exception as ERR: 247 | raise GeoIP2FastMin.GeoIPError(f"Failed to 'load' GeoIP2Fast! the data file {gzip_data_file} appears to be invalid or does not exist! {str(ERR)}") 248 | ##──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 249 | self._database_path = self.os.path.realpath(self.data_file) 250 | ##──── Load the dat.gz file into memory ────────────────────────────────────────────────────────────────────────────────────────── 251 | try: 252 | __DAT_VERSION__, source_info, totalNetworks, mainDatabase = self.pickle.load(inputFile) 253 | if __DAT_VERSION__ != 120: 254 | raise GeoIP2FastMin.GeoIPError(f"Failed to pickle the data file {gzip_data_file}. Reason: Invalid version - requires 120, current {str(__DAT_VERSION__)}") 255 | self.source_info = source_info['info'] 256 | self.country = source_info['country'] 257 | self.city = source_info['city'] 258 | self.asn = source_info['asn'] 259 | ##──── ONLY COUNTRY ────────────────────────────────────────────────────────────────────────────────────────────────────────────── 260 | if self.country == True and self.asn == False: 261 | mainIndex,mainListNamesCountry,mainListFirstIP,mainListIDCountryCodes,mainListNetlength = mainDatabase 262 | ##──── COUNTRY WITH ASN ────────────────────────────────────────────────────────────────────────────────────────────────────────── 263 | elif self.country == True and self.asn == True: 264 | mainIndex,mainIndexASN,mainListNamesCountry,mainListNamesASN,mainListFirstIP,\ 265 | mainListFirstIPASN,mainListIDCountryCodes,mainListIDASN,mainListNetlength,mainListNetlengthASN = mainDatabase 266 | ##──── ONLY CITY ───────────────────────────────────────────────────────────────────────────────────────────────────────────────── 267 | elif self.city == True and self.asn == False: 268 | mainIndex,mainListNamesCountry,mainListNamesCity,mainListFirstIP,mainListIDCity,mainListNetlength = mainDatabase 269 | ##──── CITY WITH ASN ───────────────────────────────────────────────────────────────────────────────────────────────────────────── 270 | elif self.city == True and self.asn == True: 271 | mainIndex,mainIndexASN,mainListNamesCountry,mainListNamesCity,mainListNamesASN,\ 272 | mainListFirstIP,mainListFirstIPASN,mainListIDCity,mainListIDASN,mainListNetlength,mainListNetlengthASN = mainDatabase 273 | self.ipv6 = mainIndex[-1] > GeoIP2FastMin.numIPsv4[0] 274 | geoipCountryNamesDict = {item.split(":")[0]:item.split(":")[1] for item in mainListNamesCountry} 275 | geoipCountryCodesList = list(geoipCountryNamesDict.keys()) 276 | inputFile.close() 277 | del inputFile 278 | except Exception as ERR: 279 | raise GeoIP2FastMin.GeoIPError(f"Failed to pickle the data file {gzip_data_file} {str(ERR)}") 280 | ##──── Warming-up ──────────────────────────────────────────────────────────────────────────────────────────────────────────────── 281 | try: 282 | [self._main_index_lookup(iplong) for iplong in [2894967295]] 283 | except Exception as ERR: 284 | raise GeoIP2FastMin.GeoIPError("Failed at warming-up... exiting... %s"%(str(ERR))) 285 | ##──── Load Time Info ────────────────────────────────────────────────────────────────────────────────────────────────────────── 286 | try: 287 | totalLoadTime = (self.time.perf_counter() - startLoadData) 288 | totalMemUsage = abs((self.get_mem_usage() - startMem)) 289 | self._load_data_text = f"GeoIP2Fast v{self.__version__} is ready! {self.os.path.basename(gzip_data_file)} "+ \ 290 | "loaded with %s networks in %.5f seconds and using %.2f MiB."%('{:,d}'.format(totalNetworks).replace(',','.'),totalLoadTime,totalMemUsage) 291 | self._print_verbose(self._load_data_text) 292 | except Exception as ERR: 293 | raise GeoIP2FastMin.GeoIPError("Failed at the end of load data %s"%(str(ERR))) 294 | ##──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 295 | self.is_loaded = True 296 | return True 297 | @property 298 | def startup_line_text(self): 299 | return self._load_data_text 300 | def _main_index_lookup(self,iplong): 301 | try: 302 | matchRoot = self.bisect.bisect_right(mainIndex,iplong)-1 303 | matchChunk = self.bisect.bisect_right(mainListFirstIP[matchRoot],iplong)-1 304 | first_ip2int = mainListFirstIP[matchRoot][matchChunk] 305 | netlen = mainListNetlength[matchRoot][matchChunk] 306 | if iplong <= GeoIP2FastMin.MAX_IPv4: 307 | last_ip2int = first_ip2int + GeoIP2FastMin.numIPsv4[netlen]-1 308 | else: 309 | last_ip2int = first_ip2int + GeoIP2FastMin.numIPsv6[netlen]-1 310 | return matchRoot, matchChunk, first_ip2int, last_ip2int, netlen 311 | except Exception as ERR: 312 | return GeoIP2FastMin.GeoIPError("Failed at _main_index_lookup: %s"%(str(ERR))) 313 | def _country_lookup(self,match_root,match_chunk): 314 | try: 315 | country_code_index = mainListIDCountryCodes[match_root][match_chunk] 316 | country_code, country_name = mainListNamesCountry[country_code_index].split(":") 317 | is_private = country_code_index < 16 318 | country_code = self.error_code_private_networks if is_private else country_code 319 | return country_code, country_name, is_private 320 | except Exception as ERR: 321 | return GeoIP2FastMin.GeoIPError("Failed at _country_lookup: %s"%(str(ERR))) 322 | def _city_country_name_lookup(self,country_code): 323 | try: 324 | country_name = geoipCountryNamesDict[country_code] 325 | country_code_index = geoipCountryCodesList.index(country_code) 326 | is_private = country_code_index < 16 327 | country_code = self.error_code_private_networks if is_private else country_code 328 | return country_code, country_name, is_private 329 | except Exception as ERR: 330 | return GeoIP2FastMin.GeoIPError("Failed at _city_country_name_lookup: %s"%(str(ERR))) 331 | def _city_lookup(self,match_root,match_chunk): 332 | try: 333 | code = mainListIDCity[match_root][match_chunk] 334 | country_code, city_name = mainListNamesCity[code].split(":") 335 | country_code, country_name, is_private = self._city_country_name_lookup(country_code) 336 | city_info = GeoIP2FastMin.CityDetail(city_name) 337 | return country_code, country_name, city_info, is_private 338 | except Exception as ERR: 339 | return GeoIP2FastMin.GeoIPError("Failed at _country_lookup: %s"%(str(ERR))) 340 | def _asn_lookup(self,iplong): 341 | if self.asn == False: 342 | return "", "" 343 | try: 344 | matchRoot = self.bisect.bisect_right(mainIndexASN,iplong)-1 345 | matchChunk = self.bisect.bisect_right(mainListFirstIPASN[matchRoot],iplong)-1 346 | first_ip2int = mainListFirstIPASN[matchRoot][matchChunk] 347 | asn_id = mainListIDASN[matchRoot][matchChunk] 348 | netlen = mainListNetlengthASN[matchRoot][matchChunk] 349 | if not self.ipv6: 350 | if iplong > ((first_ip2int + GeoIP2FastMin.numIPsv4[netlen]) - 1): 351 | return "", "" 352 | else: 353 | if iplong > ((first_ip2int + GeoIP2FastMin.numIPsv6[netlen]) - 1): 354 | return "", "" 355 | return mainListNamesASN[asn_id], self._int2ip(first_ip2int)+"/"+str(netlen) 356 | except Exception as ERR: 357 | return "", "" 358 | def _ip2int(self,ipaddr:str)->int: 359 | try: 360 | try: 361 | return int(self.struct.unpack('>L', self.socket.inet_aton(ipaddr))[0]) 362 | except: 363 | return int.from_bytes(self.socket.inet_pton(self.socket.AF_INET6, ipaddr), byteorder='big') 364 | except Exception as ERR: 365 | raise GeoIP2FastMin.GeoIPError("Failed at ip2int: %s"%(str(ERR))) 366 | def _int2ip(self,iplong:int)->str: 367 | try: 368 | if iplong < GeoIP2FastMin.MAX_IPv4: 369 | return self.socket.inet_ntoa(self.struct.pack('>L', iplong)) 370 | else: 371 | return self.socket.inet_ntop(self.socket.AF_INET6, self.binascii.unhexlify(hex(iplong)[2:].zfill(32))) 372 | except Exception as ERR: 373 | raise GeoIP2FastMin.GeoIPError("Failed at int2ip: %s"%(str(ERR))) 374 | def set_error_code_private_networks(self,new_value)->str: 375 | global GEOIP_ECCODE_PRIVATE_NETWORKS 376 | try: 377 | self.error_code_private_networks = new_value 378 | GEOIP_ECCODE_PRIVATE_NETWORKS = new_value 379 | return new_value 380 | except Exception as ERR: 381 | raise GeoIP2FastMin.GeoIPError("Unable to set a new value for GEOIP_ECCODE_PRIVATE_NETWORKS: %s"%(str(ERR))) 382 | def set_error_code_network_not_found(self,new_value)->str: 383 | global GEOIP_ECCODE_NETWORK_NOT_FOUND 384 | try: 385 | self.error_code_network_not_found = new_value 386 | GEOIP_ECCODE_NETWORK_NOT_FOUND = new_value 387 | return new_value 388 | except Exception as ERR: 389 | raise GeoIP2FastMin.GeoIPError("Unable to set a new value for GEOIP_ECCODE_NETWORK_NOT_FOUND: %s"%(str(ERR))) 390 | ##──── NO-CACHE: This function cannot be cached to don´t cache the elapsed timer. ──────────────────────────────────────────────────────────── 391 | def lookup(self,ipaddr:str)->GeoIPDetail: 392 | startTime = self.time.perf_counter() 393 | try: 394 | iplong = self._ip2int(ipaddr) 395 | except Exception as ERR: 396 | return GeoIP2FastMin.GeoIPDetail(ipaddr,country_code=self.error_code_invalid_ip,\ 397 | country_name=GeoIP2FastMin.GEOIP_INVALID_IP_STRING,elapsed_time='%.9f sec'%(self.time.perf_counter()-startTime)) 398 | try: 399 | matchRoot, matchChunk, first_ip2int, last_ip2int, netlen = self._main_index_lookup(iplong) 400 | if iplong > last_ip2int: 401 | return GeoIP2FastMin.GeoIPDetail(ip=ipaddr,country_code=self.error_code_network_not_found, \ 402 | country_name=GeoIP2FastMin.GEOIP_NOT_FOUND_STRING,elapsed_time='%.9f sec'%(self.time.perf_counter()-startTime)) 403 | cidr = self._int2ip(first_ip2int)+"/"+str(netlen) 404 | asn_name, asn_cidr = self._asn_lookup(iplong) 405 | if self.country: 406 | country_code, country_name, is_private = self._country_lookup(matchRoot, matchChunk) 407 | ##──── SUCCESS! ──── 408 | return GeoIP2FastMin.GeoIPDetail(ipaddr,country_code,country_name,cidr,is_private,asn_name,asn_cidr,elapsed_time='%.9f sec'%((self.time.perf_counter()-startTime))) 409 | else: 410 | country_code, country_name, city_info, is_private = self._city_lookup(matchRoot, matchChunk) 411 | ##──── SUCCESS! ──── 412 | try: 413 | return GeoIP2FastMin.GeoIPDetailCity(ipaddr,country_code,country_name,city_info,cidr,is_private,asn_name,asn_cidr,elapsed_time='%.9f sec'%((self.time.perf_counter()-startTime))) 414 | except Exception as ERR: 415 | raise Exception(ERR) 416 | ##──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 417 | except Exception as ERR: 418 | return GeoIP2FastMin.GeoIPDetail(ip=ipaddr,country_code=self.error_code_lookup_internal_error,\ 419 | country_name=GeoIP2FastMin.GEOIP_INTERNAL_ERROR_STRING,elapsed_time='%.9f sec'%(self.time.perf_counter()-startTime)) 420 | #──── GET MEMORY USAGE ─────────────────────────────────────────────────────────────────────────────────────────────────────── 421 | def get_mem_usage(self)->float: 422 | ''' Memory usage in MiB ''' 423 | PROCESS_QUERY_INFORMATION = 0x0400 424 | PROCESS_VM_READ = 0x0010 425 | class PROCESS_MEMORY_COUNTERS(self.ctypes.Structure): 426 | _fields_ = [("cb", self.ctypes.c_ulong), 427 | ("PageFaultCount", self.ctypes.c_ulong), 428 | ("PeakWorkingSetSize", self.ctypes.c_size_t), 429 | ("WorkingSetSize", self.ctypes.c_size_t), 430 | ("QuotaPeakPagedPoolUsage", self.ctypes.c_size_t), 431 | ("QuotaPagedPoolUsage", self.ctypes.c_size_t), 432 | ("QuotaPeakNonPagedPoolUsage", self.ctypes.c_size_t), 433 | ("QuotaNonPagedPoolUsage", self.ctypes.c_size_t), 434 | ("PagefileUsage", self.ctypes.c_size_t), 435 | ("PeakPagefileUsage", self.ctypes.c_size_t)] 436 | try: 437 | result = self.subprocess.check_output(['ps', '-p', str(self.os.getpid()), '-o', 'rss=']) 438 | return float(int(result.strip()) / 1024) 439 | except: 440 | try: 441 | pid = self.ctypes.windll.kernel32.GetCurrentProcessId() 442 | process_handle = self.ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid) 443 | counters = PROCESS_MEMORY_COUNTERS() 444 | counters.cb = self.ctypes.sizeof(PROCESS_MEMORY_COUNTERS) 445 | if self.ctypes.windll.psapi.GetProcessMemoryInfo(process_handle, self.ctypes.byref(counters), self.ctypes.sizeof(counters)): 446 | memory_usage = counters.WorkingSetSize 447 | return float((int(memory_usage) / 1024) / 1024) 448 | except: 449 | return 0.0 450 | def self_test(self,max_ips=30): 451 | """ 452 | Do a self-test with some random IPs 453 | """ 454 | ip_list = [] 455 | ip_list.append("x"+self._int2ip(self.random.randint(16777216,3758096383)).replace(".",",")) # generates an invalid IP inserting the 'x' letter and changing dot by comma 456 | ip_list.append(self._int2ip(self.random.randint(16777216,3758096383))+"/32") # generates an invalid IP adding '/32' to the end. Is a valid CIDR but an invalid IP 457 | ip_list.append(self._int2ip(self.random.randint(397189376,397191423))) # generates a random IP between 23.172.161.0 and 23.172.168.255 to force a 'not found' response 458 | ip_list.append(self._int2ip(self.random.choice([self.random.randint(167772160,184549375),self.random.randint(3232235520,3232301055),self.random.randint(2886729728,2887778303)]))) # generates a random IP of a private network 459 | while len(ip_list) < max_ips: 460 | ip_list.append(self._int2ip(self.random.randint(16777216,3758096383))) 461 | print("\nStarting a self-test with %s randomic IPv4 addresses...\n"%('{:,d}'.format(len(ip_list)))) 462 | avgList, avgCacheList = [], [] 463 | for IP in ip_list: 464 | geoip = self.lookup(IP) 465 | avgList.append(float(geoip.elapsed_time.split(" ")[0])) 466 | cachedResult = self.lookup(IP) 467 | avgCacheList.append(float(cachedResult.elapsed_time.split(" ")[0])) 468 | print("> "+IP.ljust(18)+" "+str(geoip.country_code).ljust(3)+str(geoip.country_name[:30]).ljust(30)+ \ 469 | " ["+geoip.elapsed_time+"] Cached > ["+cachedResult.elapsed_time+"] "+geoip.asn_name) 470 | print("\n\t- Average Lookup Time: %.9f seconds - Average Cached Lookups: %.9f seconds.\n"%(sum(sorted(avgList)[1:-1])/(len(ip_list)-2),sum(sorted(avgCacheList)[1:-1])/(len(ip_list)-2))) 471 | 472 | if __name__ == "__main__": 473 | G = GeoIP2FastMin(verbose=True,geoip2fast_data_file="") 474 | G.self_test() -------------------------------------------------------------------------------- /geoip2fast/geoip2fastminified.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | # -*- coding: utf-8 -*- 4 | """ 5 | GeoIP2FastMin - Version v1.2.2 6 | 7 | Author: Ricardo Abuchaim - ricardoabuchaim@gmail.com 8 | https://github.com/rabuchaim/geoip2fast/ 9 | 10 | License: MIT 11 | 12 | .oPYo. o .oPYo. .oPYo. ooooo o o o o o d'b o 8 13 | 8 8 8 8 8 `8 8 8 8b d8 8 8 14 | 8 .oPYo. .oPYo. 8 o8YooP' oP' o8oo .oPYo. .oPYo. o8P 8`b d'8 o8 odYo. o8 o8P o8 .oPYo. .oPYo8 15 | 8 oo 8oooo8 8 8 8 8 .oP' 8 .oooo8 Yb.. 8 8 `o' 8 8 8' `8 8 8 8 8oooo8 8 8 16 | 8 8 8. 8 8 8 8 8' 8 8 8 'Yb. 8 8 8 8 8 8 8 8 8 8. 8 8 17 | `YooP8 `Yooo' `YooP' 8 8 8ooooo 8 `YooP8 `YooP' 8 8 8 8 8 8 8 8 8 `Yooo' `YooP' 18 | :....8 :.....::.....:..:..:::::.......:..:::::.....::.....:::..:::..::::..:....::..:..:..:::..:.....::.....: 19 | :::::8 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 20 | :::::..::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 21 | 22 | This version was minified by pyminify. Just copy the entire class and paste it into your code and use the 23 | GeoIP2Fast databases with countries, cities and asn. Is Pure Python! No dependencies. 24 | 25 | From GeoIP2Fast's natural code, we removed the coverage testing functions, missing ips, automatic updates, 26 | and a few other things. Nothing that affects speed. Usage examples: 27 | 28 | G = GeoIP2FastMin(verbose=False,geoip2fast_data_file="") 29 | 30 | print(G.startup_line_text) 31 | print(G.database_path) 32 | 33 | result = G.lookup("1.1.1.1") 34 | 35 | print(result.country_code) 36 | print(result.country_name) 37 | print(result.cidr) 38 | print(result.pp_json()) 39 | G.self_test(max_ips=30) 40 | 41 | """ 42 | class GeoIP2FastMin: 43 | import os,sys,bisect,pickle,ctypes,subprocess,gzip,json,random,socket,struct,binascii,time;__appid__='GeoIP2Fast';__version__='1.2.2';GEOIP2FAST_DAT_GZ_FILE=os.path.join(os.path.dirname(__file__),'geoip2fast.dat.gz');os.environ['PYTHONWARNINGS']='ignore';os.environ['PYTHONIOENCODING']='utf-8';GEOIP_ECCODE_PRIVATE_NETWORKS,GEOIP_ECCODE_NETWORK_NOT_FOUND='--','--';GEOIP_ECCODE_INVALID_IP,GEOIP_ECCODE_LOOKUP_INTERNAL_ERROR='','';GEOIP_NOT_FOUND_STRING='';GEOIP_INTERNAL_ERROR_STRING='';GEOIP_INVALID_IP_STRING='';numIPsv4=sorted([2**A for A in range(0,33)],reverse=True);numIPsv6=sorted([2**A for A in range(0,129)],reverse=True);MAX_IPv4=numIPsv4[0] 44 | def __init__(A,verbose=False,geoip2fast_data_file=''): 45 | C=verbose;B=geoip2fast_data_file;A.name='GeoIP2FastMin';A.ipv6,A.city,A.asn,A.is_loaded=False,False,False,False;A.data_file,A._load_data_text='','';A.verbose=C 46 | if C==False:A._print_verbose=A.__print_verbose_empty 47 | A.error_code_private_networks=A.GEOIP_ECCODE_PRIVATE_NETWORKS;A.error_code_network_not_found=A.GEOIP_ECCODE_NETWORK_NOT_FOUND;A.error_code_invalid_ip=A.GEOIP_ECCODE_INVALID_IP;A.error_code_lookup_internal_error=A.GEOIP_ECCODE_LOOKUP_INTERNAL_ERROR 48 | if B!='': 49 | try: 50 | if A.os.path.isfile(B)==True:A.data_file=B 51 | elif B.find('/')<0: 52 | D=A.__locate_database_file(B) 53 | if D is False:raise A.GeoIPError('Unable to find GeoIP2Fast database file %s'%A.os.path.basename(B)) 54 | else:A.data_file=D 55 | else:raise A.GeoIPError('Check path of specified file and try again.') 56 | except Exception as E:raise A.GeoIPError('Unable to access the specified file %s. %s'%(B,str(E))) 57 | A.__load_data(A.data_file,C) 58 | class GeoIPError(Exception): 59 | def __init__(A,message):A.message=message 60 | def __str__(A):return A.message 61 | def __repr__(A):return A.message 62 | class CityDetail: 63 | def __init__(A,city_string='||||'): 64 | try:A.name,A.subdivision_code,A.subdivision_name,A.subdivision2_code,A.subdivision2_name=city_string.split('|') 65 | except:A.name,A.subdivision_code,A.subdivision_name,A.subdivision2_code,A.subdivision2_name=GeoIP2FastMin.GEOIP_INTERNAL_ERROR_STRING,'','','','' 66 | def to_dict(A):return{'name':A.name,'subdivision_code':A.subdivision_code,'subdivision_name':A.subdivision_name} 67 | class GeoIPDetail: 68 | def __init__(A,ip,country_code='',country_name='',cidr='',is_private=False,asn_name='',asn_cidr='',elapsed_time=''):A.ip,A.country_code,A.country_name,A.cidr,A.hostname=ip,country_code,country_name,cidr,'';A.is_private,A.asn_name,A.asn_cidr,A.elapsed_time=is_private,asn_name,asn_cidr,elapsed_time 69 | @property 70 | def city(self):return GeoIP2FastMin.CityDetail() 71 | def __str__(A):return f"{A.__dict__}" 72 | def __repr__(A):return f"{A.to_dict()}" 73 | def get_hostname(A,dns_timeout=.1): 74 | try:D=GeoIP2FastMin.time.perf_counter();A.socket.setdefaulttimeout(dns_timeout);B=A.socket.gethostbyaddr(A.ip)[0];A.hostname=B if B!=A.ip else'';A.elapsed_time_hostname='%.9f sec'%(GeoIP2FastMin.time.perf_counter()-D);return A.hostname 75 | except OSError as C:A.hostname=f"<{str(C.strerror)}>";return A.hostname 76 | except Exception as C:A.hostname='';return A.hostname 77 | def to_dict(A): 78 | try: 79 | B={'ip':A.ip,'country_code':A.country_code,'country_name':A.country_name,'city':'','cidr':A.cidr,'hostname':A.hostname,'asn_name':A.asn_name,'asn_cidr':A.asn_cidr,'is_private':A.is_private,'elapsed_time':A.elapsed_time} 80 | if not hasattr(A,'city'):del B['city'] 81 | try:D=A.elapsed_time_hostname;B['elapsed_time_hostname']=A.elapsed_time_hostname 82 | except:pass 83 | return B 84 | except Exception as C:raise GeoIP2FastMin.GeoIPError('Failed to_dict() %s'%str(C)) 85 | def pp_json(B,indent=3,sort_keys=False,print_result=False): 86 | try: 87 | A=GeoIP2FastMin.json.dumps(B.to_dict(),sort_keys=sort_keys,indent=indent,ensure_ascii=False) 88 | if print_result==True:print(A) 89 | return A 90 | except Exception as C:raise GeoIP2FastMin.GeoIPError('Failed pp_json() %s'%str(C)) 91 | class GeoIPDetailCity(GeoIPDetail): 92 | 'Extended version of GeoIPDetail with city information\n ' 93 | def __init__(A,ip,country_code='',country_name='',city=None,cidr='',is_private=False,asn_name='',asn_cidr='',elapsed_time=''):super().__init__(ip,country_code,country_name,cidr,is_private,asn_name,asn_cidr,elapsed_time);A._city=city if city else GeoIP2FastMin.CityDetail() 94 | @property 95 | def city(self):return self._city 96 | @city.setter 97 | def city(self,value):raise AttributeError("Cannot set 'city' attribute in GeoIPDetailCity") 98 | def to_dict(B):A=super().to_dict();A['city']=B.city.to_dict();return A 99 | def __print_verbose_empty(A,msg):0 100 | def __print_verbose_regular(A,msg):print(msg,flush=True) 101 | def _print_debug(A,msg):print('[DEBUG] '+msg,flush=True) 102 | def _print_verbose(A,msg):print(msg,flush=True) 103 | def __locate_database_file(A,filename): 104 | B=filename 105 | try:D=A.os.path.join(A.os.path.abspath(A.os.path.curdir),B);E=A.os.path.join(A.os.path.dirname(__file__),B) 106 | except Exception as C:raise GeoIP2FastMin.GeoIPError('Unable to determine the path of application %s. %s'%(B,str(C))) 107 | try:A.os.stat(D).st_mode;return D 108 | except Exception as C: 109 | try:A.os.stat(E).st_mode;return E 110 | except Exception as C:raise GeoIP2FastMin.GeoIPError('Unable to determine the path of library %s - %s'%(B,str(C))) 111 | def __load_data(A,gzip_data_file,verbose=False): 112 | C=gzip_data_file;global __DAT_VERSION__,source_info,totalNetworks,mainListNamesCountry,geoipCountryNamesDict,geoipCountryCodesList,mainIndex,mainListNamesCountry,mainListFirstIP,mainListIDCountryCodes,mainListNetlength,mainIndexASN,mainListNamesASN,mainListFirstIPASN,mainListIDASN,mainListNetlengthASN,mainListNamesCity,mainListIDCity 113 | if A.is_loaded==True:return True 114 | A._print_verbose=A.__print_verbose_regular if verbose==True else A.__print_verbose_empty;G=A.get_mem_usage();H=A.time.perf_counter() 115 | try: 116 | if C=='': 117 | C=GeoIP2FastMin.GEOIP2FAST_DAT_GZ_FILE 118 | try: 119 | F=A.__locate_database_file(A.os.path.basename(C)) 120 | if F is False:raise GeoIP2FastMin.GeoIPError('(1) Unable to find GeoIP2Fast database file %s'%A.os.path.basename(C)) 121 | else:A.data_file=F 122 | except Exception as B:raise GeoIP2FastMin.GeoIPError('(2) Unable to find GeoIP2Fast database file %s %s'%(A.os.path.basename(C),str(B))) 123 | except Exception as B:raise GeoIP2FastMin.GeoIPError('Failed at locate data file %s'%str(B)) 124 | try: 125 | try:D=A.gzip.open(str(A.data_file),'rb') 126 | except: 127 | try:D=open(str(A.data_file).replace('.gz',''),'rb');A.data_file=A.data_file.replace('.gz','') 128 | except Exception as B:raise GeoIP2FastMin.GeoIPError(f"Unable to find {C} or {C} {str(B)}") 129 | except Exception as B:raise GeoIP2FastMin.GeoIPError(f"Failed to 'load' GeoIP2Fast! the data file {C} appears to be invalid or does not exist! {str(B)}") 130 | A.database_path=A.os.path.realpath(A.data_file) 131 | try: 132 | __DAT_VERSION__,source_info,totalNetworks,E=A.pickle.load(D) 133 | if __DAT_VERSION__!=120:raise GeoIP2FastMin.GeoIPError(f"Failed to pickle the data file {C}. Reason: Invalid version - requires 120, current {str(__DAT_VERSION__)}") 134 | A.source_info=source_info['info'];A.country=source_info['country'];A.city=source_info['city'];A.asn=source_info['asn'] 135 | if A.country==True and A.asn==False:mainIndex,mainListNamesCountry,mainListFirstIP,mainListIDCountryCodes,mainListNetlength=E 136 | elif A.country==True and A.asn==True:mainIndex,mainIndexASN,mainListNamesCountry,mainListNamesASN,mainListFirstIP,mainListFirstIPASN,mainListIDCountryCodes,mainListIDASN,mainListNetlength,mainListNetlengthASN=E 137 | elif A.city==True and A.asn==False:mainIndex,mainListNamesCountry,mainListNamesCity,mainListFirstIP,mainListIDCity,mainListNetlength=E 138 | elif A.city==True and A.asn==True:mainIndex,mainIndexASN,mainListNamesCountry,mainListNamesCity,mainListNamesASN,mainListFirstIP,mainListFirstIPASN,mainListIDCity,mainListIDASN,mainListNetlength,mainListNetlengthASN=E 139 | A.ipv6=mainIndex[-1]>GeoIP2FastMin.numIPsv4[0];geoipCountryNamesDict={A.split(':')[0]:A.split(':')[1]for A in mainListNamesCountry};geoipCountryCodesList=list(geoipCountryNamesDict.keys());D.close();del D 140 | except Exception as B:raise GeoIP2FastMin.GeoIPError(f"Failed to pickle the data file {C} {str(B)}") 141 | try:[A._main_index_lookup(B)for B in[2894967295]] 142 | except Exception as B:raise GeoIP2FastMin.GeoIPError('Failed at warming-up... exiting... %s'%str(B)) 143 | try:I=A.time.perf_counter()-H;J=abs(A.get_mem_usage()-G);A._load_data_text=f"GeoIP2Fast v{A.__version__} is ready! {A.os.path.basename(C)} "+'loaded with %s networks in %.5f seconds and using %.2f MiB.'%('{:,d}'.format(totalNetworks).replace(',','.'),I,J);A._print_verbose(A._load_data_text) 144 | except Exception as B:raise GeoIP2FastMin.GeoIPError('Failed at the end of load data %s'%str(B)) 145 | A.is_loaded=True;return True 146 | @property 147 | def startup_line_text(self):return self._load_data_text 148 | def _main_index_lookup(F,iplong): 149 | B=iplong 150 | try: 151 | A=F.bisect.bisect_right(mainIndex,B)-1;C=F.bisect.bisect_right(mainListFirstIP[A],B)-1;D=mainListFirstIP[A][C];E=mainListNetlength[A][C] 152 | if B<=GeoIP2FastMin.MAX_IPv4:G=D+GeoIP2FastMin.numIPsv4[E]-1 153 | else:G=D+GeoIP2FastMin.numIPsv6[E]-1 154 | return A,C,D,G,E 155 | except Exception as H:return GeoIP2FastMin.GeoIPError('Failed at _main_index_lookup: %s'%str(H)) 156 | def _country_lookup(D,match_root,match_chunk): 157 | try:B=mainListIDCountryCodes[match_root][match_chunk];A,E=mainListNamesCountry[B].split(':');C=B<16;A=D.error_code_private_networks if C else A;return A,E,C 158 | except Exception as F:return GeoIP2FastMin.GeoIPError('Failed at _country_lookup: %s'%str(F)) 159 | def _city_country_name_lookup(C,country_code): 160 | A=country_code 161 | try:D=geoipCountryNamesDict[A];E=geoipCountryCodesList.index(A);B=E<16;A=C.error_code_private_networks if B else A;return A,D,B 162 | except Exception as F:return GeoIP2FastMin.GeoIPError('Failed at _city_country_name_lookup: %s'%str(F)) 163 | def _city_lookup(B,match_root,match_chunk): 164 | try:C=mainListIDCity[match_root][match_chunk];A,D=mainListNamesCity[C].split(':');A,E,F=B._city_country_name_lookup(A);G=GeoIP2FastMin.CityDetail(D);return A,E,G,F 165 | except Exception as H:return GeoIP2FastMin.GeoIPError('Failed at _country_lookup: %s'%str(H)) 166 | def _asn_lookup(A,iplong): 167 | B=iplong 168 | if A.asn==False:return'','' 169 | try: 170 | C=A.bisect.bisect_right(mainIndexASN,B)-1;D=A.bisect.bisect_right(mainListFirstIPASN[C],B)-1;E=mainListFirstIPASN[C][D];G=mainListIDASN[C][D];F=mainListNetlengthASN[C][D] 171 | if not A.ipv6: 172 | if B>E+GeoIP2FastMin.numIPsv4[F]-1:return'','' 173 | elif B>E+GeoIP2FastMin.numIPsv6[F]-1:return'','' 174 | return mainListNamesASN[G],A._int2ip(E)+'/'+str(F) 175 | except Exception as H:return'','' 176 | def _ip2int(A,ipaddr): 177 | B=ipaddr 178 | try: 179 | try:return int(A.struct.unpack('>L',A.socket.inet_aton(B))[0]) 180 | except:return int.from_bytes(A.socket.inet_pton(A.socket.AF_INET6,B),byteorder='big') 181 | except Exception as C:raise GeoIP2FastMin.GeoIPError('Failed at ip2int: %s'%str(C)) 182 | def _int2ip(A,iplong): 183 | B=iplong 184 | try: 185 | if BL',B)) 186 | else:return A.socket.inet_ntop(A.socket.AF_INET6,A.binascii.unhexlify(hex(B)[2:].zfill(32))) 187 | except Exception as C:raise GeoIP2FastMin.GeoIPError('Failed at int2ip: %s'%str(C)) 188 | def set_error_code_private_networks(B,new_value): 189 | A=new_value;global GEOIP_ECCODE_PRIVATE_NETWORKS 190 | try:B.error_code_private_networks=A;GEOIP_ECCODE_PRIVATE_NETWORKS=A;return A 191 | except Exception as C:raise GeoIP2FastMin.GeoIPError('Unable to set a new value for GEOIP_ECCODE_PRIVATE_NETWORKS: %s'%str(C)) 192 | def set_error_code_network_not_found(B,new_value): 193 | A=new_value;global GEOIP_ECCODE_NETWORK_NOT_FOUND 194 | try:B.error_code_network_not_found=A;GEOIP_ECCODE_NETWORK_NOT_FOUND=A;return A 195 | except Exception as C:raise GeoIP2FastMin.GeoIPError('Unable to set a new value for GEOIP_ECCODE_NETWORK_NOT_FOUND: %s'%str(C)) 196 | def lookup(A,ipaddr): 197 | B=ipaddr;C=A.time.perf_counter() 198 | try:D=A._ip2int(B) 199 | except Exception as E:return GeoIP2FastMin.GeoIPDetail(B,country_code=A.error_code_invalid_ip,country_name=GeoIP2FastMin.GEOIP_INVALID_IP_STRING,elapsed_time='%.9f sec'%(A.time.perf_counter()-C)) 200 | try: 201 | I,J,N,O,P=A._main_index_lookup(D) 202 | if D>O:return GeoIP2FastMin.GeoIPDetail(ip=B,country_code=A.error_code_network_not_found,country_name=GeoIP2FastMin.GEOIP_NOT_FOUND_STRING,elapsed_time='%.9f sec'%(A.time.perf_counter()-C)) 203 | K=A._int2ip(N)+'/'+str(P);L,M=A._asn_lookup(D) 204 | if A.country:F,G,H=A._country_lookup(I,J);return GeoIP2FastMin.GeoIPDetail(B,F,G,K,H,L,M,elapsed_time='%.9f sec'%(A.time.perf_counter()-C)) 205 | else: 206 | F,G,Q,H=A._city_lookup(I,J) 207 | try:return GeoIP2FastMin.GeoIPDetailCity(B,F,G,Q,K,H,L,M,elapsed_time='%.9f sec'%(A.time.perf_counter()-C)) 208 | except Exception as E:raise Exception(E) 209 | except Exception as E:return GeoIP2FastMin.GeoIPDetail(ip=B,country_code=A.error_code_lookup_internal_error,country_name=GeoIP2FastMin.GEOIP_INTERNAL_ERROR_STRING,elapsed_time='%.9f sec'%(A.time.perf_counter()-C)) 210 | def get_mem_usage(A): 211 | ' Memory usage in MiB ';D=1024;E=16 212 | class C(A.ctypes.Structure):_fields_=[('cb',A.ctypes.c_ulong),('PageFaultCount',A.ctypes.c_ulong),('PeakWorkingSetSize',A.ctypes.c_size_t),('WorkingSetSize',A.ctypes.c_size_t),('QuotaPeakPagedPoolUsage',A.ctypes.c_size_t),('QuotaPagedPoolUsage',A.ctypes.c_size_t),('QuotaPeakNonPagedPoolUsage',A.ctypes.c_size_t),('QuotaNonPagedPoolUsage',A.ctypes.c_size_t),('PagefileUsage',A.ctypes.c_size_t),('PeakPagefileUsage',A.ctypes.c_size_t)] 213 | try:F=A.subprocess.check_output(['ps','-p',str(A.os.getpid()),'-o','rss=']);return float(int(F.strip())/1024) 214 | except: 215 | try: 216 | G=A.ctypes.windll.kernel32.GetCurrentProcessId();H=A.ctypes.windll.kernel32.OpenProcess(D|E,False,G);B=C();B.cb=A.ctypes.sizeof(C) 217 | if A.ctypes.windll.psapi.GetProcessMemoryInfo(H,A.ctypes.byref(B),A.ctypes.sizeof(B)):I=B.WorkingSetSize;return float(int(I)/1024/1024) 218 | except:return .0 219 | def self_test(A,max_ips=30): 220 | '\n Do a self-test with some random IPs\n ';B=[];B.append('x'+A._int2ip(A.random.randint(16777216,3758096383)).replace('.',','));B.append(A._int2ip(A.random.randint(16777216,3758096383))+'/32');B.append(A._int2ip(A.random.randint(397189376,397191423)));B.append(A._int2ip(A.random.choice([A.random.randint(167772160,184549375),A.random.randint(3232235520,3232301055),A.random.randint(2886729728,2887778303)]))) 221 | while len(B) '+D.ljust(18)+' '+str(C.country_code).ljust(3)+str(C.country_name[:30]).ljust(30)+' ['+C.elapsed_time+'] Cached > ['+G.elapsed_time+'] '+C.asn_name) 224 | print('\n\t- Average Lookup Time: %.9f seconds - Average Cached Lookups: %.9f seconds.\n'%(sum(sorted(E)[1:-1])/(len(B)-2),sum(sorted(F)[1:-1])/(len(B)-2))) 225 | if __name__ == "__main__": 226 | G = GeoIP2FastMin(verbose=True,geoip2fast_data_file="") 227 | G.self_test() -------------------------------------------------------------------------------- /geoip2fast/tests/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | GeoLite2-Country.mmdb 3 | -------------------------------------------------------------------------------- /geoip2fast/tests/compare_with_mmdb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | ##──── This test shows what GeoIP2Fast has that Maxmind's MMDB doesn't, and vice versa ────────────────────────────────────────── 4 | ##──── Download the file "GeoLite2-Country.mmdb" from Maxmind website and put it in the same directory ─────────────────────────── 5 | ##──── of this script to run the test ──────────────────────────────────────────────────────────────────────────────────────────── 6 | ##──── if you have geoipupdate installed, you should already have this file in the directory /var/lib/GeoIP/ It wIll works too ─── 7 | 8 | ##──── The BLUE lines shows that GeoIP2Fast result is the same as the Maxmind result with MMDB files. ──────────────────────────── 9 | ##──── Lines in RED means that there is a divergence between the base of GeoIP2Fast and Maxmind. ───────────────────────────────── 10 | ##──── You can confirm 'who is right' using whois or geoiplookup application in linux ──────────────────────────────────────────── 11 | 12 | import os, sys, re, ctypes 13 | from geoip2fast import GeoIP2Fast 14 | from random import randrange 15 | 16 | try: 17 | import geoip2.database # pip install geoip2 18 | except: 19 | print("Run 'pip install geoip2' first and try again") 20 | sys.exit(1) 21 | 22 | def cRed(msg): 23 | return '\033[91m'+msg+'\033[0m' 24 | def cBlue(msg): 25 | return '\033[94m'+msg+'\033[0m' 26 | 27 | def create_iplist(quant): 28 | a_list = [] 29 | for I in range(quant): 30 | IP = f"{randrange(1,224)}.{randrange(0,254)}.{randrange(0,254)}.{randrange(0,254)}" 31 | a_list.append(IP) 32 | if I % 5000 == 0: 33 | print("\rGenerating random IPs: "+str(I+1),end="") 34 | print(f"\rGenerating randomically {len(a_list)} IPs...") 35 | return a_list 36 | 37 | def get_geoip_from_mmdb(ipaddr)->str: 38 | try: 39 | response = reader.country(ipaddr) 40 | return str(response.country.iso_code) 41 | except Exception as ERR: 42 | # print(str(ERR)) 43 | return "--" 44 | 45 | if __name__ == "__main__": 46 | GeoIP = GeoIP2Fast(verbose=True) 47 | if os.stat('/var/lib/GeoIP/GeoLite2-Country.mmdb').st_mode: 48 | reader = geoip2.database.Reader('/var/lib/GeoIP/GeoLite2-Country.mmdb',mode=geoip2.database.MODE_MMAP) 49 | else: 50 | reader = geoip2.database.Reader('GeoLite2-Country.mmdb',mode=geoip2.database.MODE_MMAP) 51 | 52 | gIpList = create_iplist(100000) 53 | counter = 0 54 | print("") 55 | print("This test shows what GeoIP2Fast has that Maxmind's MMDB files doesn't") 56 | print("") 57 | print(f"{'IP address'.center(20)}|{'from GeoIP2Fast'.center(20)}|{'from MMDB file'.center(20)}|") 58 | for IP in gIpList: 59 | geoip_info = GeoIP.lookup(IP) 60 | getFromLocal = geoip_info.country_code 61 | getFromMMDB = get_geoip_from_mmdb(IP) 62 | 63 | if getFromLocal == "": 64 | getFromLocal = "--" 65 | 66 | logString = (f"{('IP: '+IP).ljust(20)}|{str(getFromLocal).center(20)}|{str(getFromMMDB).center(20)}|") 67 | 68 | if getFromMMDB != getFromLocal: 69 | print(cRed("\r"+logString)) 70 | counter += 1 71 | else: 72 | print(cBlue("\r"+logString),end="") 73 | 74 | # clear the last line 75 | if IP == gIpList[-1]: 76 | print("\r".ljust(105," "),end="") 77 | 78 | print("\n") 79 | print("Lines in RED means that there are a divergence between the base of GeoIP2Fast and Maxmind.") 80 | print("You can confirm 'who is right' checking different sources like a geoip lookup application ") 81 | print("like geoiplookup (ubuntu: apt install geoip-bin) or a whois service (ubuntu: apt install whois)") 82 | print("") 83 | print(" From %s random IP addresses tested, were found %s IP addresses"%((re.sub(r'(? "+IP.ljust(15)+" "+str(geoip.country_code).ljust(3)+str(geoip.country_name).ljust(35)+ \ 21 | " ["+geoip.elapsed_time+"]\tAfter cache: ["+GEOIP.lookup(IP).elapsed_time+"] "+geoip.cidr) 22 | 23 | print("") 24 | 25 | result = GEOIP.lookup("200.204.0.10") 26 | print(result) 27 | 28 | # Before call the function get_hostname(), 'hostname' property will always be empty 29 | print("Hostname: "+result.hostname+"\t\t\t << must be empty before call result.get_hostname()") 30 | result.get_hostname() 31 | 32 | print(result) 33 | 34 | # to work with output as a dict, use the function to_dict() 35 | print(result.to_dict()['country_code'],result.to_dict()['country_name']) 36 | 37 | # To pretty print the object result like json.dumps() 38 | result = GEOIP.lookup("200.204.0.138") 39 | result.get_hostname() 40 | print(result.pp_json(indent=3,sort_keys=False)) 41 | 42 | # info about internal cache 43 | print(GEOIP.cache_info()) 44 | 45 | # clear the internal cache 46 | print(GEOIP.clear_cache()) 47 | 48 | # info about internal cache 49 | print(GEOIP.cache_info()) 50 | 51 | # to check the date of the CSV files used to create the .dat file 52 | print(GEOIP.get_source_info()) 53 | 54 | print("") 55 | 56 | sys.exit(0) -------------------------------------------------------------------------------- /geoip2fast/tests/random_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from geoip2fast import GeoIP2Fast 3 | from random import randrange 4 | from time import sleep 5 | 6 | MAX_IPS = 1000000 7 | GeoIP = GeoIP2Fast(verbose=True) 8 | 9 | print("\n- Starting a %d random IP test in"%(MAX_IPS),end="") 10 | print(" 3...",end="") 11 | sleep(1) 12 | print(" 2...",end="") 13 | sleep(1) 14 | print(" 1...",end="") 15 | sleep(1) 16 | print("\n") 17 | 18 | avgList, avgCacheList = [], [] 19 | 20 | total = 0 21 | while total < MAX_IPS: 22 | IP = f"{randrange(1,223)}.{randrange(0,254)}.{randrange(0,254)}.{randrange(0,254)}" 23 | result = GeoIP.lookup(IP) 24 | avgList.append(float(result.elapsed_time.split(" ")[0])) 25 | total += 1 26 | cachedResult = GeoIP.lookup(IP) 27 | avgCacheList.append(float(cachedResult.elapsed_time.split(" ")[0])) 28 | print(f"IP {result.ip.ljust(20)}{result.country_code.ljust(4)}{result.country_name.ljust(40)}[{result.elapsed_time}] - Cached [{cachedResult.elapsed_time}]") 29 | print("") 30 | print("Test with %d randomic IP addresses."%(MAX_IPS)) 31 | print("\t- Average Lookup Time: %.9f seconds. "%(sum(avgList)/MAX_IPS)) 32 | print("\t- Average Cached Lookups: %.9f seconds. "%(sum(avgCacheList)/MAX_IPS)) 33 | print("") 34 | 35 | -------------------------------------------------------------------------------- /geoip2fast/tests/speed_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from geoip2fast import GeoIP2Fast 3 | 4 | GeoIP = GeoIP2Fast(verbose=True) 5 | 6 | print("\n- Starting 'lookups per second' test...\n") 7 | GeoIP.calculate_speed(print_result=True) 8 | print("") 9 | -------------------------------------------------------------------------------- /images/geoip2dat01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/images/geoip2dat01.jpg -------------------------------------------------------------------------------- /images/geoip2dat02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/images/geoip2dat02.jpg -------------------------------------------------------------------------------- /images/geoip2dat03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/images/geoip2dat03.jpg -------------------------------------------------------------------------------- /images/geoip2fast_selftest.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/images/geoip2fast_selftest.jpg -------------------------------------------------------------------------------- /images/geoip2fastcity.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/images/geoip2fastcity.jpg -------------------------------------------------------------------------------- /images/geoip2fastv4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabuchaim/geoip2fast/5ce875696cef5f1c0c0fc6e3aa5951cbeb65278a/images/geoip2fastv4.jpg -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | from setuptools import setup, find_packages 3 | 4 | setup( 5 | name='geoip2fast', 6 | version='1.2.2', 7 | description='GeoIP2Fast is the fastest GeoIP2 country/city/asn lookup library that supports IPv4 and IPv6. A search takes less than 0.00003 seconds. It has its own data file updated twice a week with Maxmind-Geolite2-CSV, supports IPv4/IPv6 and is Pure Python!', 8 | url='https://github.com/rabuchaim/geoip2fast', 9 | author='Ricardo Abuchaim', 10 | author_email='ricardoabuchaim@gmail.com', 11 | maintainer='Ricardo Abuchaim', 12 | maintainer_email='ricardoabuchaim@gmail.com', 13 | project_urls={ 14 | "Issue Tracker": "https://github.com/rabuchaim/geoip2fast/issues", 15 | "Source code": "https://github.com/rabuchaim/geoip2fast", 16 | "Latest DAT files": "https://github.com/rabuchaim/geoip2fast/releases/latest", 17 | "Legacy v1.1.X DAT files": "https://github.com/rabuchaim/geoip2fast/releases/tag/LEGACY", 18 | }, 19 | bugtrack_url='https://github.com/rabuchaim/geoip2fast/issues', 20 | license='MIT', 21 | keywords=['geoip','geoip2','geolite2','maxmind','geoip2fast','geolocation','geolocalization','geo ip','ipaddress','ip','geo','ipv4','ipv6','pure-python','purepython','pure python','geoiptofast','geoiptoofast','geoip2dat','mmdb','tools'], 22 | packages=['geoip2fast'], 23 | py_modules=['geoip2fast', 'geoip2dat'], 24 | package_dir = {'geoip2fast': 'geoip2fast'}, 25 | include_package_data=True, 26 | zip_safe = False, 27 | package_data={ 28 | 'geoip2fast': [ 29 | 'CHANGELOG', 30 | 'geoip2fast.dat.gz', 31 | 'geoip2fast-ipv6.dat.gz', 32 | 'geoip2fast-asn.dat.gz', 33 | 'geoip2fast-asn-ipv6.dat.gz', 34 | 'tests/geoip2fast_test.py', 35 | 'tests/speed_test.py', 36 | 'tests/coverage_test.py', 37 | 'tests/compare_with_mmdb.py', 38 | 'tests/random_test.py', 39 | 'tests/geoipcli.py', 40 | ], 41 | }, 42 | entry_points={ 43 | 'console_scripts': [ 44 | 'geoip2fast = geoip2fast.geoip2fast:main_function', 45 | 'geoip2dat = geoip2fast.geoip2dat:main_function' 46 | ] 47 | }, 48 | python_requires=">=3.7", 49 | install_requires=[], 50 | classifiers=[ 51 | 'Development Status :: 5 - Production/Stable', 52 | 'Topic :: Security', 53 | 'Topic :: Internet', 54 | 'Topic :: Internet :: Finger', 55 | 'Topic :: Scientific/Engineering', 56 | 'Topic :: System :: Monitoring', 57 | 'Topic :: System :: Networking', 58 | 'Topic :: System :: Systems Administration', 59 | 'Topic :: Software Development :: Libraries', 60 | 'Topic :: Software Development :: Libraries :: Python Modules', 61 | 'Topic :: Software Development :: Localization', 62 | 'Topic :: Utilities', 63 | 'Intended Audience :: Developers', 64 | 'Intended Audience :: Information Technology', 65 | 'Intended Audience :: System Administrators', 66 | 'Operating System :: MacOS', 67 | 'Operating System :: Microsoft :: Windows :: Windows 10', 68 | 'Operating System :: Microsoft :: Windows :: Windows 11', 69 | 'Operating System :: Unix', 70 | 'Operating System :: POSIX', 71 | 'Operating System :: POSIX :: Linux', 72 | 'Operating System :: POSIX :: BSD', 73 | 'Operating System :: POSIX :: BSD :: FreeBSD', 74 | 'Programming Language :: Python', 75 | 'Programming Language :: Python :: 3', 76 | 'Programming Language :: Python :: 3.7', 77 | 'Programming Language :: Python :: 3.8', 78 | 'Programming Language :: Python :: 3.9', 79 | 'Programming Language :: Python :: 3.10', 80 | 'Programming Language :: Python :: 3.11', 81 | 'Programming Language :: Python :: 3.12', 82 | 'Programming Language :: Python :: 3.13', 83 | 'Programming Language :: Python :: Implementation :: PyPy', 84 | 'License :: OSI Approved :: MIT License', 85 | ], 86 | long_description=codecs.open("README.md","r","utf-8").read(), 87 | long_description_content_type='text/markdown', 88 | ) 89 | --------------------------------------------------------------------------------