From 171e4497448285deb4896b3ef4dba54731d4ec82 Mon Sep 17 00:00:00 2001 From: Rekryt Date: Fri, 30 Aug 2024 15:22:24 +0300 Subject: [PATCH] Initial commit --- .docker/php/docker-php-disable-assertions.ini | 2 + .docker/php/docker-php-enable-jit.ini | 9 + .docker/php/docker-php.ini | 15 + .dockerignore | 6 + .editorconfig | 43 + .env.example | 5 + .gitattributes | 23 + .gitignore | 133 ++ .prettierrc | 15 + Dockerfile | 45 + LICENSE | 21 + README.en.md | 165 ++ README.md | 174 ++ composer.json | 29 + config/youtube.com.json | 25 + docker-compose.yml | 23 + index.php | 17 + package.json | 11 + src/App/Controller/AbstractController.php | 60 + .../Controller/AbstractIPListController.php | 35 + src/App/Controller/ControllerInterface.php | 19 + src/App/Controller/JsonController.php | 32 + src/App/Controller/MainController.php | 23 + src/App/Controller/MikrotikController.php | 41 + src/App/Controller/TextController.php | 36 + src/App/Service/IPListService.php | 50 + src/App/Template/IndexTemplate.php | 588 +++++ src/Domain/Entity/Site.php | 115 + src/Domain/Factory/SiteFactory.php | 77 + src/Domain/Helper/DNSHelper.php | 69 + src/Domain/Helper/IP4Helper.php | 144 ++ src/Domain/Helper/IP6Helper.php | 176 ++ src/Infrastructure/API/App.php | 107 + src/Infrastructure/API/AppModuleInterface.php | 8 + .../API/Handler/HTTPErrorHandler.php | 17 + .../API/Handler/HTTPHandler.php | 58 + .../API/Handler/HTTPHandlerInterface.php | 12 + src/Infrastructure/API/Handler/Handler.php | 27 + .../API/Handler/HandlerInterface.php | 12 + src/Infrastructure/API/Server.php | 119 + src/Infrastructure/Storage/CIDRStorage.php | 44 + .../Storage/StorageInterface.php | 9 + src/Infrastructure/Task/ShellTask.php | 20 + src/Infrastructure/Task/TaskInterface.php | 11 + src/functions.php | 33 + storage/cidr.json | 1935 +++++++++++++++++ 46 files changed, 4638 insertions(+) create mode 100644 .docker/php/docker-php-disable-assertions.ini create mode 100644 .docker/php/docker-php-enable-jit.ini create mode 100644 .docker/php/docker-php.ini create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.en.md create mode 100644 README.md create mode 100644 composer.json create mode 100644 config/youtube.com.json create mode 100644 docker-compose.yml create mode 100644 index.php create mode 100644 package.json create mode 100644 src/App/Controller/AbstractController.php create mode 100644 src/App/Controller/AbstractIPListController.php create mode 100644 src/App/Controller/ControllerInterface.php create mode 100644 src/App/Controller/JsonController.php create mode 100644 src/App/Controller/MainController.php create mode 100644 src/App/Controller/MikrotikController.php create mode 100644 src/App/Controller/TextController.php create mode 100644 src/App/Service/IPListService.php create mode 100644 src/App/Template/IndexTemplate.php create mode 100644 src/Domain/Entity/Site.php create mode 100644 src/Domain/Factory/SiteFactory.php create mode 100644 src/Domain/Helper/DNSHelper.php create mode 100644 src/Domain/Helper/IP4Helper.php create mode 100644 src/Domain/Helper/IP6Helper.php create mode 100644 src/Infrastructure/API/App.php create mode 100644 src/Infrastructure/API/AppModuleInterface.php create mode 100644 src/Infrastructure/API/Handler/HTTPErrorHandler.php create mode 100644 src/Infrastructure/API/Handler/HTTPHandler.php create mode 100644 src/Infrastructure/API/Handler/HTTPHandlerInterface.php create mode 100644 src/Infrastructure/API/Handler/Handler.php create mode 100644 src/Infrastructure/API/Handler/HandlerInterface.php create mode 100644 src/Infrastructure/API/Server.php create mode 100644 src/Infrastructure/Storage/CIDRStorage.php create mode 100644 src/Infrastructure/Storage/StorageInterface.php create mode 100644 src/Infrastructure/Task/ShellTask.php create mode 100644 src/Infrastructure/Task/TaskInterface.php create mode 100644 src/functions.php create mode 100644 storage/cidr.json diff --git a/.docker/php/docker-php-disable-assertions.ini b/.docker/php/docker-php-disable-assertions.ini new file mode 100644 index 0000000..eadd8ca --- /dev/null +++ b/.docker/php/docker-php-disable-assertions.ini @@ -0,0 +1,2 @@ +[Assertion] +zend.assertions = -1 diff --git a/.docker/php/docker-php-enable-jit.ini b/.docker/php/docker-php-enable-jit.ini new file mode 100644 index 0000000..4373ef6 --- /dev/null +++ b/.docker/php/docker-php-enable-jit.ini @@ -0,0 +1,9 @@ +; Extended PHP.ini file to enable JIT. +; ==================================== +; Place this file under /usr/local/etc/php/conf.d/ +zend_extension=opcache.so +opcache.enable_cli=1 +opcache.jit_buffer_size=500000000 +opcache.jit=1235 +;opcache.jit_buffer_size=500000000 +;opcache.jit=1225 diff --git a/.docker/php/docker-php.ini b/.docker/php/docker-php.ini new file mode 100644 index 0000000..eee879b --- /dev/null +++ b/.docker/php/docker-php.ini @@ -0,0 +1,15 @@ +[php] +display_errors = On +memory_limit = 2048M +default_socket_timeout = 120 +upload_max_filesize = 20M +post_max_size = 20M + +[opcache] +opcache.revalidate_freq = 0 +opcache.validate_timestamps = 1 +opcache.max_accelerated_files = 100000 +opcache.memory_consumption = 512 +opcache.interned_strings_buffer = 64 +opcache.fast_shutdown = 1 +opcache.error_log = "/var/log/php/opcache.log" diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8fab4a7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git +.nuxt +.output +node_modules +tmp +package-lock.json \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..fe1bea6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,43 @@ +[*] +charset=utf-8 +end_of_line=lf +insert_final_newline=false +indent_style=space +tab_width=4 + +[*.vue] +indent_style=space +indent_size=4 + +[*.scss] +indent_style=space +indent_size=4 + +[*.json] +indent_style=space +indent_size=4 + +[{phpunit.xml.dist,*.jhm,*.xslt,*.xul,*.rng,*.xsl,*.xsd,*.ant,*.tld,*.fxml,*.jrxml,*.xml,*.jnlp,*.wsdl}] +indent_style=space +indent_size=4 + +[*.svg] +indent_style=space +indent_size=4 + +[.editorconfig] +indent_style=space +indent_size=4 + +[{*.gy,*.groovy,*.gant,*.gdsl}] +indent_style=space +indent_size=4 + +[{*.ats,*.ts}] +indent_style=space +indent_size=4 + +[{*.yml,*.yaml}] +indent_style=space +indent_size=4 + diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e658915 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +COMPOSE_PROJECT_NAME="iplist" +STORAGE_SAVE_INTERVAL="120" +SYS_MEMORY_LIMIT="512M" +SYS_TIMEZONE="Europe/Moscow" +DEBUG="true" \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8e72281 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto eol=lf + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.c text +*.h text + +# Declare files that will always have LF line endings on checkout. +*.sln text eol=lf +*.php text eol=lf +*.js text eol=lf +*.ts text eol=lf +*.json text eol=lf +*.vue text eol=lf +*.css text eol=lf +*.scss text eol=lf +*.sass text eol=lf +*.less text eol=lf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ce447c --- /dev/null +++ b/.gitignore @@ -0,0 +1,133 @@ +# Dependencies +node_modules +.output +.nitro +jspm_packages +vendor + +# Only keep yarn.lock in the root +package-lock.json +*/**/yarn.lock +composer.lock + +# Logs +/logs +*.log* +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Cache +.phpunit.result.cache + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Packages +packages/*/LICENSE + +# VSCode +.vscode + +# Intellij idea +*.iml +.idea + +# Zend \ Eclipse +/.settings/ +/.buildpath +/.project + +# OSX +.DS_Store +.AppleDouble +.LSOverride + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +CHANGELOG.md + + TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# Nuxt generate +dist + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless + +# Vim swap files +*.swp + +# Robots Files +robots.txt + +# Ignore Service Worker +sw.* \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..c977d74 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,15 @@ +{ + "useTabs": false, + "printWidth": 120, + "tabWidth": 4, + "trailingComma": "es5", + "jsxBracketSameLine": false, + "bracketSpacing": true, + "semi": true, + "singleQuote": true, + "htmlWhitespaceSensitivity": "ignore", + "phpVersion": "7.1", + "braceStyle": "1tbs", + "endOfLine": "lf", + "plugins": ["@prettier/plugin-php"] +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f23beac --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +FROM php:8.3-cli + +RUN apt-get update + +# dependencies +RUN apt-get install -y ntp whois dnsutils ipcalc + +# composer +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer \ + && chmod 755 /usr/bin/composer + +# pcntl +RUN docker-php-ext-configure pcntl --enable-pcntl \ + && docker-php-ext-install pcntl + +# pecl/ev +RUN pecl install -o -f ev \ + && docker-php-ext-enable ev + +# zip +RUN apt-get install -y libzip-dev zlib1g-dev zip \ + && docker-php-ext-install zip + +RUN rm -rf /var/lib/apt/lists/* + +# php.ini +ADD .docker/php/docker-php.ini /usr/local/etc/php/conf.d/docker-php-enable-jit.ini +ADD .docker/php/docker-php-disable-assertions.ini /usr/local/etc/php/conf.d/docker-php-disable-assertions.ini +ADD .docker/php/docker-php-enable-jit.ini /usr/local/etc/php/conf.d/docker-php-enable-jit.ini + +RUN apt-get clean + +COPY ./src/ /app/src/ +COPY ./config/ /app/config/ +COPY ./storage/ /app/storage/ +COPY ./composer.json /app/ +COPY ./index.php /app/ + +WORKDIR /app + +RUN composer install --no-interaction + +EXPOSE 8080 + +CMD [ "php", "./index.php" ] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..de5305f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2024 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..8258c9d --- /dev/null +++ b/README.en.md @@ -0,0 +1,165 @@ +# IP Address Collection and Management Service +This service is designed for collecting and updating IP addresses (IPv4 and IPv6) and their CIDR zones for specified domains. It is implemented as a web server using asynchronous PHP 8.1+ with the AMPHP library and integrates with Linux utilities like `whois` and `ipcalc`. The service provides interfaces for retrieving lists of domains, IPv4 addresses, IPv6 addresses, as well as CIDRv4 and CIDRv6 zones in various formats, including plain text, JSON, and scripts for adding to "Address List" on Mikrotik routers (RouterOS). +![iplist](https://github.com/user-attachments/assets/2e363fd8-1df7-4554-bf9e-98f58c13df96) + +Demo URL: [https://iplist.opencck.org](https://iplist.opencck.org) + +## Key Features + +- **Domain Monitoring**: Collect and update IP addresses and CIDR zones for specified domains. +- **Multi-Format Output**: Supports output in plain text, JSON format, and script format for RouterOS Mikrotik. +- **Integration with External Data Sources**: Supports importing initial data from external URLs. +- **Easy Deployment with Docker Compose**. +- **Configuration through JSON files for managing domains and IPs**. + +## Technologies Used + +- **PHP 8.1+ (amphp, revolt)** +- **whois, ipcalc (Linux utilities)** + +## Configuration + +Configuration files are stored in the `config` directory. Each JSON file represents a configuration for a specific portal, defining the domains to monitor and the sources of initial data for IP and CIDR. + +```json +{ + "domains": [ + "youtube.com", + "www.youtube.com", + "m.youtube.com", + "www.m.youtube.com", + "googlevideo.com", + "www.googlevideo.com", + "ytimg.com", + "i.ytimg.com" + ], + "dns": ["127.0.0.11:53", "77.88.8.88:53", "8.8.8.8:53"], + "timeout": 43200, + "ip4": [], + "ip6": [], + "cidr4": [], + "cidr6": [], + "external": { + "domains": ["https://raw.githubusercontent.com/nickspaargaren/no-google/master/categories/youtubeparsed"], + "ip4": ["https://raw.githubusercontent.com/touhidurrr/iplist-youtube/main/ipv4_list.txt"], + "ip6": ["https://raw.githubusercontent.com/touhidurrr/iplist-youtube/main/ipv6_list.txt"], + "cidr4": ["https://raw.githubusercontent.com/touhidurrr/iplist-youtube/main/cidr4.txt"], + "cidr6": ["https://raw.githubusercontent.com/touhidurrr/iplist-youtube/main/cidr6.txt"] + } +} +``` +| property | type | description | +|----------|----------|--------------------------------------------------------------| +| domains | string[] | List of portal domains | +| dns | string[] | List of DNS servers for updating IP addresses | +| timeout | int | Time interval between domain IP address updates (seconds) | +| ip4 | string[] | Initial list of IPv4 addresses | +| ip6 | string[] | Initial list of IPv6 addresses | +| cidr4 | string[] | Initial list of CIDRv4 zones of IPv4 addresses | +| cidr6 | string[] | Initial list of CIDRv6 zones of IPv6 addresses | +| external | object | Lists of URLs to retrieve initial data from external sources | + +| property | type | description | +|----------|----------|-----------------------------------------------------------------| +| domains | string[] | List of URLs to retrieve portal domains | +| ip4 | string[] | List of URLs to retrieve initial IPv4 addresses | +| ip6 | string[] | List of URLs to retrieve initial IPv6 addresses | +| cidr4 | string[] | List of URLs to retrieve initial CIDRv4 zones of IPv4 addresses | +| cidr6 | string[] | List of URLs to retrieve initial CIDRv6 zones of IPv6 addresses | + +## Setting Up and Running in Docker +```shell +git clone git@github.com:rekryt/iplist.git +cd iplist +cp .env.example .env +``` + +If needed, edit the `.env` file: + +| property | default value | description | +|-----------------------|---------------|----------------------------------------------------------------| +| COMPOSE_PROJECT_NAME | iplist | Name of the compose project | +| STORAGE_SAVE_INTERVAL | 120 | Cache save interval for whois (seconds) | +| SYS_MEMORY_LIMIT | 512M | Memory limit. 1MB is sufficient for the initial configuration | +| SYS_TIMEZONE | Europe/Moscow | List of URLs to obtain initial CIDRv4 zones for IPv4 addresses | +| DEBUG | true | Determines the logging level | + +You can access the service in your browser via the HTTP protocol on port 8080: +``` +http://0.0.0.0:8080/ +http://0.0.0.0:8080/?format=json +http://0.0.0.0:8080/?format=json&site=youtube.com&data=domains +http://0.0.0.0:8080/?format=text&site=youtube.com&data=ip4 +http://0.0.0.0:8080/?format=mikrotik&data=cidr4 +http://0.0.0.0:8080/?format=mikrotik&site=youtube.com&data=cidr4 +``` + +## SSL Setup +- Install and configure a reverse proxy, for example, [NginxProxyManager](https://nginxproxymanager.com/guide/#quick-setup). +- Create a Docker virtual network: +```shell +docker network create web +``` +- Configure it in the docker-compose.yml files of both the reverse proxy and this project: +```yml +services: + ... + app: + networks: + - web +networks: + web: + external: true + name: web +``` +- Remove the ports property from this project's docker-compose.yml file. +- Apply the changes: +```shell +docker compose up -d +``` +- You can view the container name with the command docker compose ps. +- In the reverse proxy administration panel, configure the domain to point to `iplist-app-1` on port `8080` and enable SSL. +- NginxProxyManager will automatically renew the SSL certificate. + +## Manual Launch (PHP 8.1+) +```shell +apt-get install -y ntp whois dnsutils ipcalc +cp .env.example .env +composer install +php index.php +``` + +## Настройка Mikrotik +- In the router's admin panel (or via winbox), navigate to System -> Scripts. +- Create a new script by clicking "Add new" and give it a name, for example `iplist_youtube_v4_cidr` +- In the `Source` field, enter the following code (replace `url` with your server's address, and the protocol in `mode` may differ): +``` +/tool fetch url="https://iplist.opencck.org/?format=mikrotik&site=youtube.com&data=cidr4" mode=https dst-path=iplist_youtube_v4_cidr.rsc +:delay 5s +:log info "Downloaded iplist_youtube_v4_cidr.rsc succesfully"; + +/ip firewall address-list remove [find where comment="youtube.com"]; +:delay 5s + +/import file-name=iplist_youtube_v4_cidr.rsc +:delay 10s +:log info "New iplist_youtube_v4_cidr added successfully"; +``` +![1](https://github.com/user-attachments/assets/5c88ea7a-7d5b-41de-8405-e1d2b13b96a2) +- Save the script +- Go to System -> Scheduler +- Create a new task with a name of your choice, for example `iplist_youtube_v4_cidr` +- Set the `Start time` for the task (e.g., `00:05:00`). For Interval, enter `1d 00:00:00`. +- In the `On event` field, enter the script name +``` +iplist_youtube_v4_cidr +``` +![2](https://github.com/user-attachments/assets/1b364ddc-a4b7-4563-987c-3dd382eb082d) +- Open the script in System -> Scripts and run it by clicking the `Run Script` button +- In the Logs section, you should see the message `New iplist_youtube_v4_cidr added successfully` +![3](https://github.com/user-attachments/assets/4ef15415-60f5-4c70-9f18-c8bece797e3d) +- In IP -> Firewall -> Address Lists, a new list should appear (in this example, named `youtube.com`) +![4](https://github.com/user-attachments/assets/72d00414-252c-4ddb-84ed-80b09e247e39) + +### License +The MIT License (MIT). Please see [LICENSE](./LICENSE) for more information. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5301594 --- /dev/null +++ b/README.md @@ -0,0 +1,174 @@ +### IP Address Collection and Management Service for Mikrotik RouterOS Script, JSON or Text Format +For english readme: [README.en.md](README.en.md) + +![iplist](https://github.com/user-attachments/assets/2e363fd8-1df7-4554-bf9e-98f58c13df96) + +Demo URL: [https://iplist.opencck.org](https://iplist.opencck.org) + +# Сервис сбора IP-адресов и CIDR зон +Данный сервис предназначен для сбора и обновления IP-адресов (IPv4 и IPv6), а также их CIDR зон для указанных доменов. +Это асинхронный PHP веб-сервер на основе [AMPHP](https://amphp.org/) и с Linux-утилит `whois` и `ipcalc`. +Сервис предоставляет интерфейсы для получения списков зон ip адресов указанных доменов (IPv4 адресов, IPv6 адресов, а также CIDRv4 и CIDRv6 зон) в различных форматах, включая текстовый, JSON и формате скрипта для добавления в "Address List" на роутерах Mikrotik (RouterOS). + +Основные возможности +- Мониторинг доменов: Сбор и обновление IP-адресов и CIDR зон для указанных доменов. +- Многоформатный вывод: Поддержка вывода данных в текстовом формате, формате JSON и в виде скрипта для RouterOS Mikrotik. +- Интеграция с внешними источниками данных: поддержка импорта начальных данных из внешних URL. +- Легкое развертывание с помощью Docker Compose. +- Настройка через JSON файлы для управления доменами и IP. + +Используемые технологии +- PHP 8.1+ (amphp, revolt) +- whois, ipcalc (linux) + +## Настройки +Конфигурационные файлы хранятся в директории `config`. Каждый JSON файл представляет собой конфигурацию для конкретного портала, задавая домены для мониторинга и источники начальных данных по IP и CIDR. +```json +{ + "domains": [ + "youtube.com", + "www.youtube.com", + "m.youtube.com", + "www.m.youtube.com", + "googlevideo.com", + "www.googlevideo.com", + "ytimg.com", + "i.ytimg.com" + ], + "dns": ["127.0.0.11:53", "77.88.8.88:53", "8.8.8.8:53"], + "timeout": 43200, + "ip4": [], + "ip6": [], + "cidr4": [], + "cidr6": [], + "external": { + "domains": ["https://raw.githubusercontent.com/nickspaargaren/no-google/master/categories/youtubeparsed"], + "ip4": ["https://raw.githubusercontent.com/touhidurrr/iplist-youtube/main/ipv4_list.txt"], + "ip6": ["https://raw.githubusercontent.com/touhidurrr/iplist-youtube/main/ipv6_list.txt"], + "cidr4": ["https://raw.githubusercontent.com/touhidurrr/iplist-youtube/main/cidr4.txt"], + "cidr6": ["https://raw.githubusercontent.com/touhidurrr/iplist-youtube/main/cidr6.txt"] + } +} +``` +| свойство | тип | описание | +|----------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| domains | string[] | Список доменов портала | +| dns | string[] | Список DNS серверов для обновления ip-адресов. По мимо локального и google dns, можно использовать [публичные российские DNS](https://public-dns.info/nameserver/ru.html), например [Яндекс](https://dns.yandex.ru/) | +| timeout | int | Время между обновлением ip-адресов доменов (секунды) | +| ip4 | string[] | Начальный список ipv4 адресов | +| ip6 | string[] | Начальный список ipv6 адресов | +| cidr4 | string[] | Начальный список CIDRv4 зон ipv4 адресов | +| cidr6 | string[] | Начальный список CIDRv6 зон ipv6 адресов | +| external | object | Списки URL для получения начальных данных от сторонних источников | + +| свойство | тип | описание | +|----------|----------|------------------------------------------------------------| +| domains | string[] | Список URL для получения доменов портала | +| ip4 | string[] | Список URL для получения начальных ipv4 адресов | +| ip6 | string[] | Список URL для получения начальных ipv6 адресов | +| cidr4 | string[] | Список URL для получения начальных CIDRv4 зон ipv4 адресов | +| cidr6 | string[] | Список URL для получения начальных CIDRv6 зон ipv6 адресов | + +## Настройка и запуск под docker +```shell +git clone git@github.com:rekryt/iplist.git +cd iplist +cp .env.example .env +``` + +Если требуется отредактируйте `.env` файл + +| свойство | значение по умолчанию | описание | +|-----------------------|-----------------------|---------------------------------------------------------------------| +| COMPOSE_PROJECT_NAME | iplist | Имя compose проекта | +| STORAGE_SAVE_INTERVAL | 120 | Период сохранения кеша whois (секунды) | +| SYS_MEMORY_LIMIT | 512M | Предельное кол-во памяти. Для начальной конфигурации достаточно 1МБ | +| SYS_TIMEZONE | Europe/Moscow | Список URL для получения начальных CIDRv4 зон ipv4 адресов | +| DEBUG | true | Определяет уровень логирования | + +```shell +docker compose up -d +``` + +Открыть сервис можно в браузере по протоколу http, порт `8080` +``` +http://0.0.0.0:8080/ +http://0.0.0.0:8080/?format=json +http://0.0.0.0:8080/?format=json&site=youtube.com&data=domains +http://0.0.0.0:8080/?format=text&site=youtube.com&data=ip4 +http://0.0.0.0:8080/?format=mikrotik&data=cidr4 +http://0.0.0.0:8080/?format=mikrotik&site=youtube.com&data=cidr4 +``` + +## Настройка SSL +Для настройки SSL сертификата вам понадобится домен настроенный на ваш сервер. +Если у вас нет собственного домена - как вариант бесплатный домен можно получить например на [https://noip.com](https://noip.com). +Актуальность такого домена придётся подтверждать раз в месяц. +- Установите и настройте реверс-прокси, например [NginxProxyManager](https://nginxproxymanager.com/guide/#quick-setup) +- Создайте виртуальную сеть docker +```shell +docker network create web +``` +- Настройте её в docker-compose.yml файлах реверс-прокси и данного проекта +```yml +services: + ... + app: + networks: + - web +networks: + web: + external: true + name: web +``` +- Удалите свойство ports из docker-compose.yml (этого проекта) +- Примените изменения: +```shell +docker compose up -d +``` +- Имя контейнера можно посмотреть командой `docker compose ps` +- В панели администрирования реверс-прокси настройте домен на него `iplist-app-1` порт `8080` и включите SSL +- NginxProxyManager будет продлевать ssl сертификат автоматически + +## Ручной запуск (PHP 8.1+) +```shell +apt-get install -y ntp whois dnsutils ipcalc +cp .env.example .env +composer install +php index.php +``` + +## Настройка Mikrotik +- В администраторской панели роутера (или через winbox) откройте раздел System -> Scripts +- Создайте новый скрипт "Add new" с произвольным именем, например `iplist_youtube_v4_cidr` +- В поле `Source` введите следующий код (используйте `url` адрес вашего сервера, протокол в `mode` тоже может отличаться): +``` +/tool fetch url="https://iplist.opencck.org/?format=mikrotik&site=youtube.com&data=cidr4" mode=https dst-path=iplist_youtube_v4_cidr.rsc +:delay 5s +:log info "Downloaded iplist_youtube_v4_cidr.rsc succesfully"; + +/ip firewall address-list remove [find where comment="youtube.com"]; +:delay 5s + +/import file-name=iplist_youtube_v4_cidr.rsc +:delay 10s +:log info "New iplist_youtube_v4_cidr added successfully"; +``` +![1](https://github.com/user-attachments/assets/5c88ea7a-7d5b-41de-8405-e1d2b13b96a2) +- Сохраните скрипт +- Откройте раздел планировщика System -> Scheduler +- Создайте новое задание с произвольным именем, например `iplist_youtube_v4_cidr` +- В качестве `Start time` укажите время для старта задания (пример: `00:05:00`). Для `Interval` введите значение `1d 00:00:00`. +- В поле `On event` введите имя скрипта +``` +iplist_youtube_v4_cidr +``` +![2](https://github.com/user-attachments/assets/1b364ddc-a4b7-4563-987c-3dd382eb082d) +- Откройте скрипт в разделе System -> Scripts и запустите его нажатием на кнопку `Run Script` +- В разделе Logs вы должны увидеть сообщение `New iplist_youtube_v4_cidr added successfully` +![3](https://github.com/user-attachments/assets/4ef15415-60f5-4c70-9f18-c8bece797e3d) +- А в разделе IP -> Firewall -> Address Lists должен появиться новый список (в примере с именем `youtube.com`) +![4](https://github.com/user-attachments/assets/72d00414-252c-4ddb-84ed-80b09e247e39) + +### License +The MIT License (MIT). Please see [LICENSE](./LICENSE) for more information. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..2b20bf7 --- /dev/null +++ b/composer.json @@ -0,0 +1,29 @@ +{ + "name": "rekryt/iplist", + "authors": [ + { + "name": "Rekryt", + "homepage": "https://github.com/rekryt" + } + ], + "require": { + "opencck/server": "v1.0.0", + "amphp/dns": "^2.2", + "vlucas/phpdotenv": "^5.5" + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "OpenCCK\\": [ + "src/" + ] + } + }, + "config": { + "allow-plugins": { + "php-http/discovery": true + } + } +} diff --git a/config/youtube.com.json b/config/youtube.com.json new file mode 100644 index 0000000..1adbc7d --- /dev/null +++ b/config/youtube.com.json @@ -0,0 +1,25 @@ +{ + "domains": [ + "youtube.com", + "www.youtube.com", + "m.youtube.com", + "www.m.youtube.com", + "googlevideo.com", + "www.googlevideo.com", + "ytimg.com", + "i.ytimg.com" + ], + "dns": ["127.0.0.11:53", "77.88.8.88:53", "8.8.8.8:53"], + "timeout": 43200, + "ip4": [], + "ip6": [], + "cidr4": [], + "cidr6": [], + "external": { + "domains": ["https://raw.githubusercontent.com/nickspaargaren/no-google/master/categories/youtubeparsed"], + "ip4": ["https://raw.githubusercontent.com/touhidurrr/iplist-youtube/main/ipv4_list.txt"], + "ip6": ["https://raw.githubusercontent.com/touhidurrr/iplist-youtube/main/ipv6_list.txt"], + "cidr4": ["https://raw.githubusercontent.com/touhidurrr/iplist-youtube/main/cidr4.txt"], + "cidr6": ["https://raw.githubusercontent.com/touhidurrr/iplist-youtube/main/cidr6.txt"] + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..90496dc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +services: + app: + restart: unless-stopped + build: + context: . + env_file: + - ./.env + volumes: + - ./src/:/app/src/ + - ./storage/:/app/storage/ + - ./config/:/app/config/ + ports: + - '8080:8080' + logging: + driver: 'json-file' + options: + max-size: '50m' +# networks: +# - web +#networks: +# web: +# external: true +# name: web diff --git a/index.php b/index.php new file mode 100644 index 0000000..e66048c --- /dev/null +++ b/index.php @@ -0,0 +1,17 @@ +addModule( + /** + * @throws Throwable + * @throws BufferException + */ fn(App $app) => Server::getInstance() + ) + ->start(); diff --git a/package.json b/package.json new file mode 100644 index 0000000..c65ff6e --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "devDependencies": { + "@prettier/plugin-php": "^0.21.0", + "prettier": "^3.1.0" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Rekryt", + "license": "MIT" +} diff --git a/src/App/Controller/AbstractController.php b/src/App/Controller/AbstractController.php new file mode 100644 index 0000000..acee4f6 --- /dev/null +++ b/src/App/Controller/AbstractController.php @@ -0,0 +1,60 @@ +httpStatus, headers: $this->headers, body: $this->getBody()); + } + + abstract public function getBody(): string; + + public function setHeaders(array $headers): AbstractController { + $this->headers = $headers; + return $this; + } + + /** + * @param int $httpStatus + */ + public function setHttpStatus(int $httpStatus): void { + $this->httpStatus = $httpStatus; + } + + public function redirect(string $url, bool $permanently = false): void { + $this->httpStatus = $permanently ? HttpStatus::MOVED_PERMANENTLY : HttpStatus::SEE_OTHER; + $this->headers = array_merge($this->headers ?? [], ['location' => $url]); + } + + /** + * @return string + */ + public function getBaseURL(): string { + $schemePort = ['http' => 80, 'https' => 443]; + return $this->request->getUri()->getScheme() . + '://' . + $this->request->getUri()->getHost() . + ($schemePort[$this->request->getUri()->getScheme()] !== $this->request->getUri()->getPort() + ? ':' . $this->request->getUri()->getPort() + : ''); + } +} diff --git a/src/App/Controller/AbstractIPListController.php b/src/App/Controller/AbstractIPListController.php new file mode 100644 index 0000000..2bd70c7 --- /dev/null +++ b/src/App/Controller/AbstractIPListController.php @@ -0,0 +1,35 @@ +headers); + + $this->logger = App::getLogger(); + $this->service = IPListService::getInstance(); + } + + /** + * @return string + */ + abstract public function getBody(): string; +} diff --git a/src/App/Controller/ControllerInterface.php b/src/App/Controller/ControllerInterface.php new file mode 100644 index 0000000..74ed465 --- /dev/null +++ b/src/App/Controller/ControllerInterface.php @@ -0,0 +1,19 @@ +setHeaders(['content-type' => 'application/json']); + + $site = $this->request->getQueryParameter('site') ?? ''; + $data = $this->request->getQueryParameter('data') ?? ''; + if ($site == '') { + if ($data == '') { + return json_encode($this->service->sites); + } else { + $result = []; + foreach ($this->service->sites as $site) { + $result[$site->name] = $site->$data; + } + return json_encode($result); + } + } else { + if ($data == '') { + return json_encode($this->service->sites[$site]); + } else { + return json_encode($this->service->sites[$site]->$data); + } + } + } +} diff --git a/src/App/Controller/MainController.php b/src/App/Controller/MainController.php new file mode 100644 index 0000000..30a4154 --- /dev/null +++ b/src/App/Controller/MainController.php @@ -0,0 +1,23 @@ +setHeaders(['content-type' => 'text/html; charset=utf-8']); + return $this->renderTemplate('index'); + } + + /** + * @param string $template + * @return string + */ + private function renderTemplate(string $template): string { + ob_start(); + include PATH_ROOT . '/src/App/Template/' . ucfirst($template) . 'Template.php'; + return ob_get_clean(); + } +} diff --git a/src/App/Controller/MikrotikController.php b/src/App/Controller/MikrotikController.php new file mode 100644 index 0000000..3c792d1 --- /dev/null +++ b/src/App/Controller/MikrotikController.php @@ -0,0 +1,41 @@ +setHeaders(['content-type' => 'text/plain']); + + $site = $this->request->getQueryParameter('site') ?? ''; + $data = $this->request->getQueryParameter('data') ?? ''; + if ($data == '') { + return "# Error: The 'data' GET parameter is required in the URL to access this page, but it cannot have the value 'All'"; + } + $response = '/ip firewall address-list' . "\n"; + if ($site == '') { + foreach ($this->service->sites as $site) { + $response .= $this->render($site->name, $site->$data); + } + return $response; + } else { + return $response . $this->render($site, $this->service->sites[$site]->$data); + } + } + + /** + * @param string $site + * @param iterable $array + * @return string + */ + private function render(string $site, iterable $array): string { + $response = ''; + $listName = str_replace(' ', '', $site); + foreach ($array as $item) { + $response .= 'add list=' . $listName . ' address=' . $item . ' comment=' . $listName . "\n"; + } + return $response; + } +} diff --git a/src/App/Controller/TextController.php b/src/App/Controller/TextController.php new file mode 100644 index 0000000..e585c77 --- /dev/null +++ b/src/App/Controller/TextController.php @@ -0,0 +1,36 @@ +setHeaders(['content-type' => 'text/plain']); + + $site = $this->request->getQueryParameter('site') ?? ''; + $data = $this->request->getQueryParameter('data') ?? ''; + if ($data == '') { + return "# Error: The 'data' GET parameter is required in the URL to access this page, but it cannot have the value 'All'"; + } + + $response = ''; + if ($site == '') { + foreach ($this->service->sites as $site) { + $response .= $this->render($site->name, $site->$data); + } + return $response; + } else { + return $this->render($site, $this->service->sites[$site]->$data); + } + } + + private function render(string $name, iterable $array): string { + $response = '# ' . $name . ' ' . date('Y-m-d H:i:s') . "\n"; + foreach ($array as $item) { + $response .= $item . "\n"; + } + return $response; + } +} diff --git a/src/App/Service/IPListService.php b/src/App/Service/IPListService.php new file mode 100644 index 0000000..eeafe03 --- /dev/null +++ b/src/App/Service/IPListService.php @@ -0,0 +1,50 @@ + + */ + public array $sites = []; + + /** + * @throws Exception + */ + private function __construct(Logger $logger = null) { + $this->logger = $logger ?? App::getLogger(); + + $dir = PATH_ROOT . '/config/'; + if (!is_dir($dir)) { + throw new Exception('config directory not found'); + } + foreach (scandir($dir) as $file) { + if (str_ends_with($file, '.json')) { + $this->loadConfig(substr($file, 0, -5), json_decode(file_get_contents($dir . $file))); + } + } + } + + /** + * @param ?Logger $logger + * @return IPListService + * @throws Exception + */ + public static function getInstance(Logger $logger = null): IPListService { + return self::$_instance ??= new self($logger); + } + + private function loadConfig(string $name, object $config): void { + $this->sites[$name] = SiteFactory::create($name, $config); + } +} diff --git a/src/App/Template/IndexTemplate.php b/src/App/Template/IndexTemplate.php new file mode 100644 index 0000000..dc84127 --- /dev/null +++ b/src/App/Template/IndexTemplate.php @@ -0,0 +1,588 @@ + + + + + + + + IPList + + + +
+
+ + + +
+
+ +
+
+ + \ No newline at end of file diff --git a/src/Domain/Entity/Site.php b/src/Domain/Entity/Site.php new file mode 100644 index 0000000..e5cff26 --- /dev/null +++ b/src/Domain/Entity/Site.php @@ -0,0 +1,115 @@ +dnsHelper = new DNSHelper($dns); + EventLoop::delay(0, $this->reload(...)); + } + + /** + * @return void + */ + private function reload(): void { + $ip4 = []; + $ip6 = []; + foreach ($this->domains as $domain) { + [$ipv4results, $ipv6results] = $this->dnsHelper->resolve($domain); + $ip4 = array_merge($ip4, $ipv4results); + $ip6 = array_merge($ip6, $ipv6results); + } + + $newIp4 = SiteFactory::normalize(array_diff($ip4, $this->ip4)); + $this->cidr4 = SiteFactory::normalize(IP4Helper::processCIDR($newIp4, $this->cidr4)); + + $newIp6 = SiteFactory::normalize(array_diff($ip6, $this->ip6)); + $this->cidr6 = SiteFactory::normalize(IP6Helper::processCIDR($newIp6, $this->cidr6)); + + $this->ip4 = SiteFactory::normalize(array_merge($this->ip4, $ip4)); + $this->ip6 = SiteFactory::normalize(array_merge($this->ip6, $ip6)); + + App::getLogger()->debug('Reloaded for ' . $this->name); + + EventLoop::delay($this->timeout, function () { + $this->reloadExternal(); + $this->reload(); + }); + } + + /** + * @return void + */ + private function reloadExternal(): void { + if (isset($this->external->domains)) { + foreach ($this->external->domains as $url) { + $this->domains = SiteFactory::normalize( + array_merge($this->domains, explode("\n", file_get_contents($url))) + ); + } + } + + if (isset($this->external->ip4)) { + foreach ($this->external->ip4 as $url) { + $this->ip4 = SiteFactory::normalize(array_merge($this->ip4, explode("\n", file_get_contents($url)))); + } + } + + if (isset($this->external->ip6)) { + foreach ($this->external->ip6 as $url) { + $this->ip6 = SiteFactory::normalize(array_merge($this->ip6, explode("\n", file_get_contents($url)))); + } + } + + if (isset($this->external->cidr4)) { + foreach ($this->external->cidr4 as $url) { + $this->cidr4 = SiteFactory::normalize( + array_merge($this->cidr4, explode("\n", file_get_contents($url))) + ); + } + } + + if (isset($this->external->cidr6)) { + foreach ($this->external->cidr6 as $url) { + $this->cidr6 = SiteFactory::normalize( + array_merge($this->cidr6, explode("\n", file_get_contents($url))) + ); + } + } + + App::getLogger()->debug('External reloaded for ' . $this->name); + } +} diff --git a/src/Domain/Factory/SiteFactory.php b/src/Domain/Factory/SiteFactory.php new file mode 100644 index 0000000..e50302e --- /dev/null +++ b/src/Domain/Factory/SiteFactory.php @@ -0,0 +1,77 @@ +domains ?? []; + $dns = $config->dns ?? []; + $timeout = $config->timeout ?? 1440 * 60; + $ip4 = $config->ip4 ?? []; + $ip6 = $config->ip6 ?? []; + $cidr4 = $config->cidr4 ?? []; + $cidr6 = $config->cidr6 ?? []; + $external = $config->external ?? new stdClass(); + + if (isset($external)) { + if (isset($external->domains)) { + foreach ($external->domains as $url) { + $domains = array_merge($domains, explode("\n", file_get_contents($url))); + } + } + + if (isset($external->ip4)) { + foreach ($external->ip4 as $url) { + $ip4 = array_merge($ip4, explode("\n", file_get_contents($url))); + } + } + + if (isset($external->ip6)) { + foreach ($external->ip6 as $url) { + $ip6 = array_merge($ip6, explode("\n", file_get_contents($url))); + } + } + + if (isset($external->cidr4)) { + foreach ($external->cidr4 as $url) { + $cidr4 = array_merge($cidr4, explode("\n", file_get_contents($url))); + } + } + + if (isset($external->cidr6)) { + foreach ($external->cidr6 as $url) { + $cidr6 = array_merge($cidr6, explode("\n", file_get_contents($url))); + } + } + } + + $domains = self::normalize($domains); + $ip4 = self::normalize($ip4); + $ip6 = self::normalize($ip6); + $cidr4 = self::normalize(IP4Helper::processCIDR($ip4, self::normalize($cidr4))); + $cidr6 = self::normalize(IP6Helper::processCIDR($ip6, self::normalize($cidr6))); + + return new Site($name, $domains, $dns, $timeout, $ip4, $ip6, $cidr4, $cidr6, $external); + } + + /** + * @param array $array + * @return array + */ + public static function normalize(array $array): array { + return array_values( + array_unique(array_filter($array, fn(string $item) => !str_starts_with($item, '#') && strlen($item) > 0)) + ); + } +} diff --git a/src/Domain/Helper/DNSHelper.php b/src/Domain/Helper/DNSHelper.php new file mode 100644 index 0000000..960af3d --- /dev/null +++ b/src/Domain/Helper/DNSHelper.php @@ -0,0 +1,69 @@ +dnsServers, (new HostLoader())->loadHosts()); + } + } + ) + ); + } + + /** + * @param string $domain + * @return array[] + */ + public function resolve(string $domain): array { + $ipv4 = []; + $ipv6 = []; + foreach ($this->dnsServers as $server) { + $this->setResolver([$server]); + try { + $ipv4 = array_merge( + $ipv4, + array_map(fn(DnsRecord $record) => $record->getValue(), resolve($domain, DnsRecord::A)) + ); + } catch (DnsException $e) { + App::getLogger()->error($e->getMessage(), [$server]); + } + try { + $ipv6 = array_merge( + $ipv6, + array_map(fn(DnsRecord $record) => $record->getValue(), resolve($domain, DnsRecord::AAAA)) + ); + } catch (DnsException $e) { + App::getLogger()->error($e->getMessage(), [$server]); + } + } + App::getLogger()->debug('resolve: ' . $domain, [count($ipv4), count($ipv6)]); + return [$ipv4, $ipv6]; + } +} diff --git a/src/Domain/Helper/IP4Helper.php b/src/Domain/Helper/IP4Helper.php new file mode 100644 index 0000000..957ad50 --- /dev/null +++ b/src/Domain/Helper/IP4Helper.php @@ -0,0 +1,144 @@ + $ip) { + if ($ip === '127.0.0.1') { + continue; + } + async(function () use ($ip, $i, $count, &$results) { + if (CIDRStorage::getInstance()->has($ip)) { + $searchArray = CIDRStorage::getInstance()->get($ip); + $results = array_merge($results, self::trimCIDRs($searchArray)); + + App::getLogger()->debug($ip . ' -> ' . json_encode($searchArray), [$i + 1 . '/' . $count]); + return; + } + + if (self::isInRange($ip, $results)) { + return; + } + + $search = null; + $result = shell_exec('whois ' . $ip . ' | grep CIDR'); + if ($result) { + preg_match('/^CIDR:\s*(.*)$/m', $result, $matches); + $search = $matches[1] ?? null; + } + + if (!$search) { + $search = shell_exec( + implode(' | ', [ + 'whois -a ' . $ip, + 'grep inetnum', + 'head -n 1', + "awk '{print $2\"-\"$4}'", + 'sed "s/-$//"', + 'xargs ipcalc', + "grep -oE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$'", + ]) + ); + } + + if (!$search) { + $search = shell_exec( + implode(' | ', [ + 'whois -a ' . $ip, + 'grep IPv4', + 'grep " - "', + 'head -n 1', + "awk '{print $3\"-\"$5}'", + 'xargs ipcalc', + "grep -oE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$'", + ]) + ); + } + + if ($search) { + $search = strtr($search, ["\n" => ' ', ', ' => ' ']); + $searchArray = array_filter( + explode(' ', strtr($search, ' ', '')), + fn(string $cidr) => strlen($cidr) > 0 + ); + CIDRStorage::getInstance()->set($ip, $searchArray); + $results = array_merge($results, self::trimCIDRs($searchArray)); + + App::getLogger()->debug($ip . ' -> ' . json_encode($searchArray), [$i + 1 . '/' . $count]); + } else { + App::getLogger()->error($ip . ' -> CIDR not found', [$i + 1 . '/' . $count]); + } + delay(0.001); + })->await(); + } + + return self::minimizeSubnets($results); + } + + public static function trimCIDRs(array $searchArray): array { + $subnets = []; + foreach ($searchArray as $search) { + foreach (explode(' ', $search) as $cidr) { + if (str_contains($cidr, '/')) { + $subnets[] = trim($cidr); + } + } + } + return $subnets; + } + + public static function sortSubnets(array $subnets): array { + usort($subnets, function ($a, $b) { + return (int) explode('/', $a)[1] - (int) explode('/', $b)[1]; + }); + usort($subnets, function ($a, $b) { + return ip2long(explode('/', $a)[0]) - ip2long(explode('/', $b)[0]); + }); + return $subnets; + } + + public static function minimizeSubnets(array $subnets): array { + $result = []; + foreach (self::sortSubnets(array_filter($subnets, fn(string $subnet) => !!$subnet)) as $subnet) { + $include = true; + [$ip /*, $mask*/] = explode('/', $subnet); + $ipLong = ip2long($ip); + // $maskLong = ~((1 << 32 - (int) $mask) - 1); + + foreach ($result as $resSubnet) { + [$resIp, $resMask] = explode('/', $resSubnet); + $resIpLong = ip2long($resIp); + $resMaskLong = ~((1 << 32 - (int) $resMask) - 1); + + if (($ipLong & $resMaskLong) === ($resIpLong & $resMaskLong)) { + $include = false; + break; + } + } + + if ($include) { + $result[] = $subnet; + } + } + + return $result; + } + + public static function isInRange(string $ip, array $cidrs): bool { + foreach ($cidrs as $cidr) { + [$subnet, $mask] = explode('/', $cidr); + if ((ip2long($ip) & ~((1 << 32 - $mask) - 1)) === ip2long($subnet)) { + return true; + } + } + return false; + } +} diff --git a/src/Domain/Helper/IP6Helper.php b/src/Domain/Helper/IP6Helper.php new file mode 100644 index 0000000..f901664 --- /dev/null +++ b/src/Domain/Helper/IP6Helper.php @@ -0,0 +1,176 @@ + $ip) { + if ($ip === '::1') { + continue; + } + async(function () use ($ip, $i, $count, &$results) { + if (CIDRStorage::getInstance()->has($ip)) { + $searchArray = CIDRStorage::getInstance()->get($ip); + $results = array_merge($results, self::trimCIDRs($searchArray)); + + App::getLogger()->debug($ip . ' -> ' . json_encode($searchArray), [$i + 1 . '/' . $count]); + return; + } + + if (self::isInRange($ip, $results)) { + return; + } + + $search = shell_exec( + implode(' | ', [ + 'whois -a ' . $ip, + 'grep inet6num', + 'grep -v "/0"', + 'head -n 1', + "awk '{print $2}'", + ]) + ); + + if (!$search) { + $search = shell_exec( + implode(' | ', [ + 'whois -a ' . $ip, + 'grep route6', + 'grep -v "/0"', + 'head -n 1', + "awk '{print $2}'", + ]) + ); + } + + if ($search) { + $search = strtr($search, ["\n" => ' ', ', ' => ' ']); + $searchArray = array_filter( + explode(' ', strtr($search, ' ', '')), + fn(string $cidr) => strlen($cidr) > 0 + ); + CIDRStorage::getInstance()->set($ip, $searchArray); + $results = array_merge($results, self::trimCIDRs($searchArray)); + + App::getLogger()->debug($ip . ' -> ' . json_encode($searchArray), [$i + 1 . '/' . $count]); + } else { + App::getLogger()->error($ip . ' -> CIDR not found', [$i + 1 . '/' . $count]); + } + delay(0.001); + })->await(); + } + + //return self::minimizeSubnets($results); + return $results; + } + + /** + * @param array $searchArray + * @return array + */ + public static function trimCIDRs(array $searchArray): array { + $subnets = []; + foreach ($searchArray as $search) { + foreach (explode(' ', $search) as $cidr) { + if (str_contains($cidr, '/')) { + [$address, $prefix] = explode('/', trim($cidr)); + $subnets[] = $address . '/' . max($prefix, 64); + } + } + } + return $subnets; + } + + /** + * @param array $subnets + * @return array + */ + public static function sortSubnets(array $subnets): array { + usort($subnets, function ($a, $b) { + return (int) explode('/', $a)[1] - (int) explode('/', $b)[1]; + }); + + usort($subnets, function ($a, $b) { + $ipA = inet_pton(explode('/', $a)[0]); + $ipB = inet_pton(explode('/', $b)[0]); + + return strcmp($ipA, $ipB); + }); + + return $subnets; + } + + /** + * @param array $subnets + * @return array + */ + public static function minimizeSubnets(array $subnets): array { + $result = []; + foreach (self::sortSubnets(array_filter($subnets, fn(string $subnet) => !!$subnet)) as $subnet) { + if (!$subnet) { + continue; + } + [$address, $prefix] = explode('/', $subnet); + + $addressNum = inet_pton($address); + $addressNum = unpack('J', $addressNum)[1]; + + $isUnique = true; + foreach ($result as $existingCidr) { + [$existingAddress, $existingPrefix] = explode('/', $existingCidr); + $existingAddressNum = inet_pton($existingAddress); + $existingAddressNum = unpack('J', $existingAddressNum)[1]; + + $mask = (1 << 128) - (1 << 128 - $prefix); + $existingMask = (1 << 128) - (1 << 128 - $existingPrefix); + if (($addressNum & $mask) === ($existingAddressNum & $existingMask)) { + $isUnique = false; + break; + } + } + + if ($isUnique) { + $result[] = $subnet; + } + } + + return $result; + } + + public static function isInRange(string $ip, array $cidrs): bool { + $ip = inet_pton($ip); + + foreach ($cidrs as $cidr) { + [$subnet, $mask] = explode('/', $cidr); + $subnet = inet_pton($subnet); + + $mask = intval($mask); + $binaryMask = str_repeat('f', $mask >> 2); + switch ($mask % 4) { + case 1: + $binaryMask .= '8'; + break; + case 2: + $binaryMask .= 'c'; + break; + case 3: + $binaryMask .= 'e'; + break; + } + $binaryMask = str_pad($binaryMask, 32, '0'); + $mask = pack('H*', $binaryMask); + + if (($ip & $mask) === ($subnet & $mask)) { + return true; + } + } + return false; + } +} diff --git a/src/Infrastructure/API/App.php b/src/Infrastructure/API/App.php new file mode 100644 index 0000000..3628aab --- /dev/null +++ b/src/Infrastructure/API/App.php @@ -0,0 +1,107 @@ + $modules + */ + private array $modules = []; + + private bool $isEventLoopStarted = false; + + /** + * @param ?Logger $logger + */ + private function __construct(private ?Logger $logger = null) { + ini_set('memory_limit', getEnv('SYS_MEMORY_LIMIT') ?? '2048M'); + + if (!defined('PATH_ROOT')) { + define('PATH_ROOT', dirname(__DIR__, 3)); + } + + $dotenv = Dotenv::createImmutable(PATH_ROOT); + $dotenv->safeLoad(); + + if ($timezone = getEnv('SYS_TIMEZONE')) { + date_default_timezone_set($timezone); + } + + $this->logger = $logger ?? new Logger(getEnv('COMPOSE_PROJECT_NAME') ?? 'iplist'); + $logHandler = new StreamHandler(new WritableResourceStream(STDOUT)); + $logHandler->setFormatter(new ConsoleFormatter()); + $logHandler->setLevel(getEnv('DEBUG') === 'false' ? LogLevel::INFO : LogLevel::DEBUG); + $this->logger->pushHandler($logHandler); + + EventLoop::setErrorHandler(function ($e) { + $this->logger->error($e->getMessage()); + }); + } + + public static function getInstance(?Logger $logger = null): self { + return self::$_instance ??= new self($logger); + } + + /** + * @param Closure $handler + * @return $this + */ + public function addModule(Closure $handler): self { + $module = $handler($this); + $this->modules[$module::class] = $module; + return $this; + } + + public function getModules(): array { + return $this->modules; + } + + public static function getLogger(): ?Logger { + return self::$_instance->logger; + } + + public function start(): void { + foreach ($this->getModules() as $module) { + $module->start(); + } + if (defined('SIGINT') && defined('SIGTERM')) { + // Await SIGINT or SIGTERM to be received. + try { + $signal = trapSignal([SIGINT, SIGTERM]); + $this->logger->info(sprintf('Received signal %d, stopping server', $signal)); + } catch (UnsupportedFeatureException $e) { + $this->logger->error($e->getMessage()); + } + $this->stop(); + } else { + if (!$this->isEventLoopStarted && !defined('PHPUNIT_COMPOSER_INSTALL')) { + $this->isEventLoopStarted = true; + EventLoop::run(); + } + } + } + + public function stop(): void { + foreach ($this->modules as $module) { + $module->stop(); + } + } +} diff --git a/src/Infrastructure/API/AppModuleInterface.php b/src/Infrastructure/API/AppModuleInterface.php new file mode 100644 index 0000000..d66ca28 --- /dev/null +++ b/src/Infrastructure/API/AppModuleInterface.php @@ -0,0 +1,8 @@ + 'application/json; charset=utf-8'], + body: json_encode(['message' => $reason, 'code' => $status]) + ); + } +} diff --git a/src/Infrastructure/API/Handler/HTTPHandler.php b/src/Infrastructure/API/Handler/HTTPHandler.php new file mode 100644 index 0000000..d79fb95 --- /dev/null +++ b/src/Infrastructure/API/Handler/HTTPHandler.php @@ -0,0 +1,58 @@ +getController( + ucfirst($request->getQueryParameter('format') ?: $controllerName), + $request, + $this->headers ?? [] + )(); + } catch (Throwable $e) { + $this->logger->warning('Exception', [ + 'exception' => $e::class, + 'error' => $e->getMessage(), + 'code' => $e->getCode(), + ]); + $response = new Response( + status: $e->getCode() ?: 500, + headers: $this->headers ?? ['content-type' => 'application/json; charset=utf-8'], + body: json_encode( + array_merge( + ['message' => $e->getMessage(), 'code' => $e->getCode()], + getEnv('DEBUG') === 'true' + ? ['file' => $e->getFile() . ':' . $e->getLine(), 'trace' => $e->getTrace()] + : [] + ) + ) + ); + } + + return $response; + }); + } +} diff --git a/src/Infrastructure/API/Handler/HTTPHandlerInterface.php b/src/Infrastructure/API/Handler/HTTPHandlerInterface.php new file mode 100644 index 0000000..776ffb2 --- /dev/null +++ b/src/Infrastructure/API/Handler/HTTPHandlerInterface.php @@ -0,0 +1,12 @@ + 'text/plain']); + } +} diff --git a/src/Infrastructure/API/Handler/HandlerInterface.php b/src/Infrastructure/API/Handler/HandlerInterface.php new file mode 100644 index 0000000..413bbdd --- /dev/null +++ b/src/Infrastructure/API/Handler/HandlerInterface.php @@ -0,0 +1,12 @@ +logger = $logger ?? App::getLogger(); + $serverSocketFactory = new ConnectionLimitingServerSocketFactory(new LocalSemaphore($this->connectionLimit)); + $clientFactory = new ConnectionLimitingClientFactory( + new SocketClientFactory($this->logger), + $this->logger, + $this->connectionPerIpLimit + ); + $this->httpServer = + $httpServer ?? + new SocketHttpServer( + logger: $this->logger, + serverSocketFactory: $serverSocketFactory, + clientFactory: $clientFactory, + httpDriverFactory: new DefaultHttpDriverFactory(logger: $this->logger, streamTimeout: 60) + ); + $this->bindContext = $bindContext ?? (new Socket\BindContext())->withoutTlsContext(); + $this->errorHandler = $errorHandler ?? new DefaultErrorHandler(); + + // инициализация сервиса + IPListService::getInstance($this->logger); + } + + /** + * @param ?HttpServer $httpServer + * @param ?ErrorHandler $errorHandler + * @param ?BindContext $bindContext + * @param ?Logger $logger + * @throws BufferException + * @throws Throwable + */ + public static function getInstance( + HttpServer $httpServer = null, + ErrorHandler $errorHandler = null, + Socket\BindContext $bindContext = null, + Logger $logger = null + ): Server { + return self::$_instance ??= new self($httpServer, $errorHandler, $bindContext, $logger); + } + + /** + * Запуск веб-сервера + * @return void + */ + public function start(): void { + try { + $this->httpServer->expose( + new Socket\InternetAddress(getEnv('HTTP_HOST') ?? '0.0.0.0', getEnv('HTTP_PORT') ?? 8080), + $this->bindContext + ); + //$this->socketHttpServer->expose( + // new Socket\InternetAddress('[::]', $_ENV['HTTP_PORT'] ?? 8080), + // $this->bindContext + //); + $this->httpServer->start(HTTPHandler::getInstance($this->logger)->getHandler(), $this->errorHandler); + } catch (Socket\SocketException $e) { + $this->logger->warning($e->getMessage()); + } catch (Throwable $e) { + $this->logger->error($e->getMessage()); + } + } + + /** + * @return void + */ + public function stop(): void { + $this->httpServer->stop(); + } + + /** + * @return HttpServerStatus + */ + public function getStatus(): HttpServerStatus { + return $this->httpServer->getStatus(); + } +} diff --git a/src/Infrastructure/Storage/CIDRStorage.php b/src/Infrastructure/Storage/CIDRStorage.php new file mode 100644 index 0000000..f133cae --- /dev/null +++ b/src/Infrastructure/Storage/CIDRStorage.php @@ -0,0 +1,44 @@ +data = (array) json_decode(file_get_contents($path)) ?? []; + } + + EventLoop::repeat(\OpenCCK\getEnv('STORAGE_SAVE_INTERVAL') ?? 120, $this->save(...)); + } + + public static function getInstance(): CIDRStorage { + return self::$_instance ??= new self(); + } + + public function get(string $key): ?array { + return $this->data[$key] ?? null; + } + + public function set(string $key, mixed $value): bool { + $this->data[$key] = $value; + return true; + } + + public function has(string $key): bool { + return isset($this->data[$key]); + } + + private function save(): void { + file_put_contents(PATH_ROOT . '/storage/' . self::FILENAME, json_encode($this->data, JSON_PRETTY_PRINT)); + App::getLogger()->notice('Whois storage saved', [count($this->data) . ' items']); + } +} diff --git a/src/Infrastructure/Storage/StorageInterface.php b/src/Infrastructure/Storage/StorageInterface.php new file mode 100644 index 0000000..830f912 --- /dev/null +++ b/src/Infrastructure/Storage/StorageInterface.php @@ -0,0 +1,9 @@ +command); + } +} diff --git a/src/Infrastructure/Task/TaskInterface.php b/src/Infrastructure/Task/TaskInterface.php new file mode 100644 index 0000000..7e57f4e --- /dev/null +++ b/src/Infrastructure/Task/TaskInterface.php @@ -0,0 +1,11 @@ +' . print_r($mixed, true) . ''; + } + if ($exit) { + exit(); + } +} diff --git a/storage/cidr.json b/storage/cidr.json new file mode 100644 index 0000000..5884cfd --- /dev/null +++ b/storage/cidr.json @@ -0,0 +1,1935 @@ +{ + "4.78.139.50": [ + "4.0.0.0\/9" + ], + "23.101.24.70": [ + "23.96.0.0\/13" + ], + "23.202.231.167": [ + "23.192.0.0\/11" + ], + "23.225.141.210": [ + "23.224.0.0\/15" + ], + "23.234.30.58": [ + "23.234.0.0\/18" + ], + "31.13.64.7": [ + "31.13.64.0\/18" + ], + "37.152.2.17": [ + "37.152.0.0\/22" + ], + "38.121.72.166": [ + "38.0.0.0\/8" + ], + "39.109.122.128": [ + "38.0.0.0\/7", + "40.0.0.0\/12", + "40.16.0.0\/16" + ], + "43.226.16.8": [ + "43.226.2.0\/23", + "43.226.4.0\/22", + "43.226.8.0\/21", + "43.226.16.0\/20", + "43.226.32.0\/19", + "43.226.64.0\/18", + "43.226.128.0\/17", + "43.227.0.0\/16", + "43.228.0.0\/16", + "43.229.0.0\/21" + ], + "43.245.104.77": [ + "43.243.96.0\/19", + "43.243.128.0\/17", + "43.244.0.0\/16", + "43.245.0.0\/18", + "43.245.64.0\/19", + "43.245.96.0\/20", + "43.245.112.0\/22" + ], + "45.54.28.11": [ + "45.54.0.0\/17" + ], + "45.77.186.255": [ + "45.76.0.0\/15" + ], + "45.114.11.25": [ + "45.114.8.0\/21" + ], + "45.253.131.236": [ + "45.252.240.0\/20", + "45.253.0.0\/16", + "45.254.0.0\/15" + ], + "46.61.154.76": [ + "46.61.0.0\/16" + ], + "46.134.216.207": [ + "46.134.192.0\/18" + ], + "47.88.58.234": [ + "47.88.0.0\/14" + ], + "49.231.55.13": [ + "49.14.0.0\/15", + "49.16.0.0\/12", + "49.32.0.0\/11", + "49.64.0.0\/10", + "49.128.0.0\/10", + "49.192.0.0\/11", + "49.224.0.0\/13", + "49.232.0.0\/14" + ], + "50.23.209.199": [ + "50.22.0.0\/15" + ], + "50.87.93.246": [ + "50.87.0.0\/16" + ], + "50.117.117.42": [ + "50.117.0.0\/17" + ], + "52.58.1.161": [ + "52.0.0.0\/10", + "52.64.0.0\/12" + ], + "52.175.9.80": [ + "52.145.0.0\/16", + "52.148.0.0\/14", + "52.152.0.0\/13", + "52.146.0.0\/15", + "52.160.0.0\/11" + ], + "54.89.135.129": [ + "54.64.0.0\/11" + ], + "54.144.128.85": [ + "54.208.0.0\/13", + "54.144.0.0\/12", + "54.220.0.0\/15", + "54.216.0.0\/14", + "54.192.0.0\/12", + "54.160.0.0\/11" + ], + "54.234.18.200": [ + "54.224.0.0\/11" + ], + "59.18.44.12": [ + "58.98.0.0\/15", + "58.100.0.0\/14", + "58.104.0.0\/13", + "58.112.0.0\/12", + "58.128.0.0\/9", + "59.0.0.0\/9", + "59.128.0.0\/12", + "59.144.0.0\/13", + "59.152.0.0\/18", + "59.152.64.0\/20", + "59.152.80.0\/21" + ], + "59.188.250.54": [ + "59.153.40.0\/21", + "59.153.48.0\/20", + "59.153.64.0\/18", + "59.153.128.0\/17", + "59.154.0.0\/15", + "59.156.0.0\/14", + "59.160.0.0\/11", + "59.192.0.0\/10", + "60.0.0.0\/8", + "61.0.0.0\/13", + "61.8.0.0\/17" + ], + "61.91.8.140": [ + "61.14.228.0\/22", + "61.14.232.0\/21", + "61.14.240.0\/20", + "61.15.0.0\/16", + "61.16.0.0\/12", + "61.32.0.0\/11", + "61.64.0.0\/10", + "61.128.0.0\/9" + ], + "62.0.80.141": [ + "62.0.0.0\/16" + ], + "64.13.192.74": [ + "64.13.192.0\/18" + ], + "64.53.242.206": [ + "64.53.128.0\/17" + ], + "64.233.160.91": [ + "64.233.160.0\/19" + ], + "65.49.26.97": [ + "65.49.0.0\/17" + ], + "66.102.1.91": [ + "66.102.0.0\/20" + ], + "66.220.146.94": [ + "66.220.144.0\/20" + ], + "67.15.100.252": [ + "67.15.0.0\/16" + ], + "67.228.102.32": [ + "67.228.0.0\/16" + ], + "67.230.169.182": [ + "67.230.160.0\/19" + ], + "69.30.25.21": [ + "69.30.0.0\/18" + ], + "69.50.221.20": [ + "69.50.192.0\/19" + ], + "69.63.176.15": [ + "69.63.176.0\/20" + ], + "69.162.134.178": [ + "69.162.128.0\/18" + ], + "69.171.224.36": [ + "69.171.224.0\/19" + ], + "69.197.153.180": [ + "69.197.128.0\/18" + ], + "74.86.3.208": [ + "74.86.0.0\/16" + ], + "74.125.1.169": [ + "74.125.0.0\/16" + ], + "75.126.33.156": [ + "75.126.0.0\/16" + ], + "77.37.252.172": [ + "77.37.252.0\/23" + ], + "77.120.15.44": [ + "77.120.12.0\/22" + ], + "80.87.199.46": [ + "80.87.198.0\/23" + ], + "81.23.20.205": [ + "81.23.16.0\/21" + ], + "81.192.13.12": [ + "81.192.0.0\/16" + ], + "88.191.249.182": [ + "88.191.249.0\/24" + ], + "90.201.124.12": [ + "90.200.0.0\/14" + ], + "92.87.232.76": [ + "92.80.0.0\/13" + ], + "93.179.102.140": [ + "93.179.96.0\/21" + ], + "94.24.232.12": [ + "94.24.192.0\/18" + ], + "94.31.189.142": [ + "94.31.189.0\/24" + ], + "95.59.170.207": [ + "95.59.170.0\/24" + ], + "96.44.137.28": [ + "96.44.128.0\/18" + ], + "98.159.108.57": [ + "98.159.96.0\/20" + ], + "103.39.76.66": [ + "103.39.42.0\/23", + "103.39.44.0\/22", + "103.39.48.0\/20", + "103.39.64.0\/18", + "103.39.128.0\/17", + "103.40.0.0\/16", + "103.41.0.0\/19", + "103.41.32.0\/21", + "103.41.40.0\/22" + ], + "103.42.176.244": [ + "103.41.48.0\/20", + "103.41.64.0\/18", + "103.41.128.0\/17", + "103.42.0.0\/16", + "103.43.0.0\/19", + "103.43.32.0\/21" + ], + "103.56.16.112": [ + "103.55.236.0\/22", + "103.55.240.0\/20", + "103.56.0.0\/17", + "103.56.128.0\/19", + "103.56.160.0\/21", + "103.56.168.0\/22" + ], + "103.73.161.52": [ + "103.73.33.0\/24", + "103.73.34.0\/23", + "103.73.36.0\/22", + "103.73.40.0\/21", + "103.73.48.0\/20", + "103.73.64.0\/18", + "103.73.128.0\/19", + "103.73.160.0\/21", + "103.73.168.0\/22" + ], + "103.97.3.19": [ + "103.95.128.0\/17", + "103.96.0.0\/16", + "103.97.0.0\/18", + "103.97.64.0\/20", + "103.97.80.0\/21" + ], + "103.97.176.73": [ + "103.97.92.0\/22", + "103.97.96.0\/19", + "103.97.128.0\/18", + "103.97.192.0\/21", + "103.97.200.0\/22", + "103.97.204.0\/23" + ], + "103.200.30.143": [ + "103.200.30.0\/24" + ], + "103.200.31.172": [ + "103.200.28.0\/22" + ], + "103.214.168.106": [ + "103.214.72.0\/21", + "103.214.80.0\/20", + "103.214.96.0\/19", + "103.214.128.0\/19", + "103.214.160.0\/20", + "103.214.176.0\/22" + ], + "103.226.246.99": [ + "103.226.196.0\/22", + "103.226.200.0\/21", + "103.226.208.0\/20", + "103.226.224.0\/19", + "103.227.0.0\/18", + "103.227.64.0\/20", + "103.227.80.0\/22" + ], + "103.228.130.27": [ + "103.228.128.1\/32", + "103.228.128.2\/31", + "103.228.128.4\/30", + "103.228.128.8\/29", + "103.228.128.16\/28", + "103.228.128.32\/27", + "103.228.128.64\/26", + "103.228.128.128\/25", + "103.228.129.0\/24", + "103.228.130.0\/23" + ], + "103.230.123.190": [ + "103.229.172.0\/22", + "103.229.176.0\/20", + "103.229.192.0\/18", + "103.230.0.0\/17", + "103.230.128.0\/21", + "103.230.136.0\/22" + ], + "103.240.180.117": [ + "103.240.180.0\/22" + ], + "103.246.246.144": [ + "103.246.148.0\/22", + "103.246.152.0\/21", + "103.246.160.0\/19", + "103.246.192.0\/19", + "103.246.224.0\/20", + "103.246.240.0\/21" + ], + "103.252.114.11": [ + "103.252.92.0\/22", + "103.252.96.0\/19", + "103.252.128.0\/17", + "103.253.0.0\/19", + "103.253.32.0\/22" + ], + "104.16.251.55": [ + "104.16.0.0\/12" + ], + "104.244.43.6": [ + "104.244.40.0\/21" + ], + "107.181.166.244": [ + "107.181.160.0\/19" + ], + "108.160.161.20": [ + "108.160.160.0\/20" + ], + "108.177.8.91": [ + "108.177.0.0\/17" + ], + "109.224.41.205": [ + "109.224.41.0\/24" + ], + "110.164.8.77": [ + "110.93.12.0\/22", + "110.93.16.0\/20", + "110.93.32.0\/19", + "110.93.64.0\/18", + "110.93.128.0\/17", + "110.94.0.0\/15", + "110.96.0.0\/11", + "110.128.0.0\/10", + "110.192.0.0\/11", + "110.224.0.0\/13", + "110.232.0.0\/14", + "110.236.0.0\/15", + "110.238.0.0\/19" + ], + "111.243.214.169": [ + "110.239.224.0\/19", + "110.240.0.0\/12", + "111.0.0.0\/8", + "112.0.0.0\/8", + "113.0.0.0\/12", + "113.16.0.0\/14", + "113.20.0.0\/17", + "113.20.128.0\/20", + "113.20.144.0\/21", + "113.20.152.0\/22" + ], + "113.108.239.161": [ + "113.30.224.0\/19", + "113.31.0.0\/16", + "113.32.0.0\/11", + "113.64.0.0\/10", + "113.128.0.0\/10", + "113.192.0.0\/13", + "113.200.0.0\/15", + "113.202.0.0\/16" + ], + "114.4.7.12": [ + "113.212.76.0\/22", + "113.212.80.0\/20", + "113.212.96.0\/19", + "113.212.128.0\/17", + "113.213.0.0\/16", + "113.214.0.0\/15", + "113.216.0.0\/13", + "113.224.0.0\/11", + "114.0.0.0\/9", + "114.128.0.0\/16", + "114.129.0.0\/17" + ], + "115.126.100.160": [ + "115.124.36.0\/22", + "115.124.40.0\/21", + "115.124.48.0\/20", + "115.124.64.0\/18", + "115.124.128.0\/17", + "115.125.0.0\/16", + "115.126.0.0\/15", + "115.128.0.0\/11", + "115.160.0.0\/14", + "115.164.0.0\/15", + "115.166.0.0\/17" + ], + "116.89.243.8": [ + "116.50.24.0\/21", + "116.50.32.0\/19", + "116.50.64.0\/18", + "116.50.128.0\/17", + "116.51.0.0\/16", + "116.52.0.0\/14", + "116.56.0.0\/13", + "116.64.0.0\/10", + "116.128.0.0\/10", + "116.192.0.0\/14", + "116.196.0.0\/15", + "116.198.0.0\/16", + "116.199.0.0\/17", + "116.199.128.0\/18", + "116.199.192.0\/19" + ], + "118.98.30.77": [ + "118.91.188.0\/22", + "118.91.192.0\/18", + "118.92.0.0\/14", + "118.96.0.0\/13", + "118.104.0.0\/15", + "118.106.0.0\/16", + "118.107.0.0\/17", + "118.107.128.0\/19", + "118.107.160.0\/20", + "118.107.176.0\/22" + ], + "118.107.180.216": [ + "118.107.180.0\/22" + ], + "118.184.26.113": [ + "118.107.184.0\/21", + "118.107.192.0\/18", + "118.108.0.0\/14", + "118.112.0.0\/12", + "118.128.0.0\/9", + "119.0.0.0\/13", + "119.8.0.0\/14", + "119.12.0.0\/20" + ], + "119.28.87.227": [ + "119.13.240.0\/20", + "119.14.0.0\/15", + "119.16.0.0\/12", + "119.32.0.0\/11", + "119.64.0.0\/10", + "119.128.0.0\/12", + "119.144.0.0\/14", + "119.148.0.0\/18" + ], + "120.232.233.161": [ + "119.235.128.0\/17", + "119.236.0.0\/14", + "119.240.0.0\/12", + "120.0.0.0\/8", + "121.0.0.0\/11", + "121.32.0.0\/13", + "121.40.0.0\/14", + "121.44.0.0\/15", + "121.46.0.0\/18", + "121.46.64.0\/19", + "121.46.96.0\/20", + "121.46.112.0\/21", + "121.46.120.0\/22" + ], + "121.78.42.79": [ + "121.54.188.0\/22", + "121.54.192.0\/18", + "121.55.0.0\/16", + "121.56.0.0\/13", + "121.64.0.0\/12", + "121.80.0.0\/13", + "121.88.0.0\/15", + "121.90.0.0\/16", + "121.91.0.0\/18", + "121.91.64.0\/20" + ], + "122.10.85.4": [ + "121.127.64.0\/18", + "121.127.128.0\/17", + "121.128.0.0\/9", + "122.0.0.0\/10", + "122.64.0.0\/11", + "122.96.0.0\/14", + "122.100.0.0\/15", + "122.102.0.0\/18", + "122.102.64.0\/19", + "122.102.96.0\/20", + "122.102.112.0\/22" + ], + "122.154.76.15": [ + "122.150.0.0\/15", + "122.152.0.0\/13", + "122.160.0.0\/11", + "122.192.0.0\/11", + "122.224.0.0\/12", + "122.240.0.0\/13", + "122.248.0.0\/14", + "122.252.0.0\/15", + "122.254.0.0\/18" + ], + "124.11.210.175": [ + "123.253.214.0\/23", + "123.253.216.0\/21", + "123.253.224.0\/19", + "123.254.0.0\/15", + "124.0.0.0\/9", + "124.128.0.0\/10", + "124.192.0.0\/11", + "124.224.0.0\/12", + "124.240.0.0\/13", + "124.248.0.0\/14" + ], + "128.121.146.101": [ + "128.121.0.0\/16" + ], + "128.242.240.20": [ + "128.242.0.0\/16" + ], + "130.211.15.150": [ + "130.211.0.0\/16" + ], + "142.250.0.91": [ + "142.250.0.0\/15" + ], + "145.255.14.204": [ + "145.255.0.0\/20" + ], + "148.163.48.215": [ + "148.163.0.0\/17" + ], + "150.107.3.176": [ + "150.107.0.0\/18", + "150.107.64.0\/19", + "150.107.96.0\/20", + "150.107.112.0\/21", + "150.107.120.0\/22", + "150.107.124.0\/24" + ], + "154.0.29.13": [ + "153.113.0.0\/16", + "153.114.0.0\/15", + "153.116.0.0\/14", + "153.120.0.0\/13", + "153.128.0.0\/9", + "154.0.0.0\/13" + ], + "154.83.14.134": [ + "154.33.0.0\/16", + "154.34.0.0\/15", + "154.36.0.0\/14", + "154.40.0.0\/13", + "154.48.0.0\/12", + "154.64.0.0\/10", + "154.128.0.0\/9", + "155.0.0.0\/14" + ], + "156.233.67.243": [ + "156.233.0.0\/16" + ], + "157.240.0.18": [ + "157.240.0.0\/16" + ], + "159.65.107.38": [ + "159.65.0.0\/16" + ], + "159.106.121.75": [ + "159.106.0.0\/16" + ], + "159.138.20.20": [ + "159.138.0.0\/16" + ], + "162.125.1.8": [ + "162.125.0.0\/16" + ], + "162.220.12.226": [ + "162.220.8.0\/21" + ], + "168.143.162.42": [ + "168.143.0.0\/16" + ], + "172.217.0.1": [ + "172.217.0.0\/16" + ], + "172.253.58.91": [ + "172.253.0.0\/16" + ], + "173.194.4.134": [ + "173.194.0.0\/16" + ], + "173.208.182.68": [ + "173.208.128.0\/17" + ], + "173.231.12.107": [ + "173.231.0.0\/18" + ], + "173.234.53.168": [ + "173.234.32.0\/19" + ], + "173.236.182.137": [ + "173.236.128.0\/17" + ], + "173.244.209.150": [ + "173.244.192.0\/19" + ], + "173.252.88.67": [ + "173.252.64.0\/18" + ], + "173.252.248.244": [ + "173.252.192.0\/18" + ], + "173.255.209.47": [ + "173.255.192.0\/18" + ], + "174.36.196.242": [ + "174.36.0.0\/15" + ], + "178.151.230.12": [ + "178.151.230.0\/24" + ], + "178.176.156.46": [ + "178.176.156.0\/24" + ], + "179.60.193.9": [ + "179.0.0.0\/11", + "179.32.0.0\/12", + "179.48.0.0\/13", + "179.56.0.0\/14", + "179.60.0.0\/16", + "179.61.0.0\/17" + ], + "180.163.150.33": [ + "180.149.200.0\/21", + "180.149.208.0\/20", + "180.149.224.0\/19", + "180.150.0.0\/15", + "180.152.0.0\/13", + "180.160.0.0\/11", + "180.192.0.0\/11", + "180.224.0.0\/13", + "180.232.0.0\/15" + ], + "182.50.139.56": [ + "181.224.168.0\/21", + "181.224.176.0\/20", + "181.224.192.0\/18", + "181.225.0.0\/16", + "181.226.0.0\/15", + "181.228.0.0\/14", + "181.232.0.0\/13", + "181.240.0.0\/12", + "182.0.0.0\/9", + "182.128.0.0\/11", + "182.160.0.0\/16", + "182.161.0.0\/19", + "182.161.32.0\/20", + "182.161.48.0\/21", + "182.161.56.0\/22" + ], + "184.72.1.148": [ + "184.72.0.0\/15" + ], + "184.173.136.86": [ + "184.172.0.0\/15" + ], + "185.45.6.57": [ + "185.45.4.0\/22" + ], + "185.60.216.11": [ + "185.60.216.0\/22" + ], + "185.158.208.140": [ + "185.158.208.0\/23" + ], + "186.208.210.12": [ + "186.2.176.0\/20", + "186.2.192.0\/18", + "186.3.0.0\/16", + "186.4.0.0\/14", + "186.8.0.0\/13", + "186.16.0.0\/12", + "186.32.0.0\/11", + "186.64.0.0\/10", + "186.128.0.0\/9", + "187.0.0.0\/11", + "187.32.0.0\/16", + "187.33.0.0\/17", + "187.33.128.0\/20" + ], + "190.5.235.140": [ + "189.28.96.0\/19", + "189.28.128.0\/17", + "189.29.0.0\/16", + "189.30.0.0\/15", + "189.32.0.0\/11", + "189.64.0.0\/10", + "189.128.0.0\/9", + "190.0.0.0\/10", + "190.64.0.0\/12", + "190.80.0.0\/13", + "190.88.0.0\/14", + "190.92.0.0\/17", + "190.92.128.0\/19", + "190.92.160.0\/21" + ], + "192.133.77.59": [ + "192.133.76.0\/22" + ], + "192.178.24.129": [ + "192.178.0.0\/15" + ], + "193.109.164.140": [ + "193.109.164.0\/22" + ], + "194.78.0.12": [ + "194.78.0.0\/24" + ], + "196.49.8.206": [ + "196.15.64.0\/18", + "196.15.128.0\/17", + "196.16.0.0\/12", + "196.32.0.0\/11", + "196.64.0.0\/10", + "196.128.0.0\/9", + "197.0.0.0\/8", + "198.0.0.0\/14", + "198.4.0.0\/15", + "198.6.0.0\/16", + "198.7.0.0\/18", + "198.7.64.0\/19" + ], + "198.27.124.186": [ + "198.27.64.0\/18" + ], + "198.44.185.131": [ + "198.44.160.0\/19" + ], + "199.16.156.7": [ + "199.16.156.0\/22" + ], + "199.59.148.6": [ + "199.59.148.0\/22" + ], + "199.96.58.15": [ + "199.96.56.0\/21" + ], + "199.193.116.105": [ + "199.193.112.0\/21" + ], + "201.0.223.214": [ + "200.240.224.0\/19", + "200.241.0.0\/16", + "200.242.0.0\/15", + "200.244.0.0\/14", + "200.248.0.0\/13", + "201.0.0.0\/11", + "201.32.0.0\/13", + "201.40.0.0\/14", + "201.44.0.0\/15", + "201.46.0.0\/18", + "201.46.64.0\/19", + "201.46.96.0\/20" + ], + "202.53.137.209": [ + "202.51.252.0\/22", + "202.52.0.0\/14", + "202.56.0.0\/15", + "202.58.0.0\/18", + "202.58.64.0\/19", + "202.58.96.0\/21", + "202.58.104.0\/22" + ], + "202.160.128.14": [ + "202.146.128.0\/17", + "202.147.0.0\/16", + "202.148.0.0\/14", + "202.152.0.0\/13", + "202.160.0.0\/15", + "202.162.0.0\/19", + "202.162.32.0\/20" + ], + "202.169.173.209": [ + "202.164.208.0\/20", + "202.164.224.0\/19", + "202.165.0.0\/16", + "202.166.0.0\/15", + "202.168.0.0\/15", + "202.170.0.0\/18", + "202.170.64.0\/20" + ], + "202.182.98.125": [ + "202.181.205.0\/24", + "202.181.206.0\/23", + "202.181.208.0\/20", + "202.181.224.0\/19", + "202.182.0.0\/15", + "202.184.0.0\/13", + "202.192.0.0\/10", + "203.0.0.0\/21" + ], + "203.66.182.13": [ + "203.56.179.0\/24", + "203.56.180.0\/22", + "203.56.184.0\/21", + "203.56.192.0\/18", + "203.57.0.0\/16", + "203.58.0.0\/15", + "203.60.0.0\/14", + "203.64.0.0\/13", + "203.72.0.0\/14", + "203.76.0.0\/15", + "203.78.0.0\/17", + "203.78.128.0\/19" + ], + "203.111.254.117": [ + "203.109.64.0\/18", + "203.109.128.0\/17", + "203.110.0.0\/15", + "203.112.0.0\/12", + "203.128.0.0\/12", + "203.144.0.0\/13", + "203.152.0.0\/14", + "203.156.0.0\/15", + "203.158.0.0\/16", + "203.159.0.0\/18", + "203.159.64.0\/20" + ], + "203.208.39.193": [ + "203.190.64.0\/18", + "203.190.128.0\/17", + "203.191.0.0\/16", + "203.192.0.0\/10", + "204.0.0.0\/13", + "204.8.0.0\/18", + "204.8.64.0\/20" + ], + "204.79.197.217": [ + "204.79.196.0\/23", + "204.79.195.0\/24" + ], + "205.186.152.122": [ + "205.186.128.0\/18" + ], + "208.31.254.33": [ + "208.32.0.0\/14", + "208.0.0.0\/11" + ], + "208.43.170.231": [ + "208.43.0.0\/16" + ], + "208.77.47.172": [ + "208.77.40.0\/21" + ], + "208.101.21.43": [ + "208.101.0.0\/18" + ], + "209.85.144.91": [ + "209.85.128.0\/17" + ], + "209.95.56.60": [ + "209.95.32.0\/19" + ], + "210.56.51.192": [ + "209.251.254.0\/23", + "209.252.0.0\/14", + "210.0.0.0\/7" + ], + "212.113.52.172": [ + "212.113.52.0\/24" + ], + "213.59.210.12": [ + "213.59.192.0\/18" + ], + "216.58.192.129": [ + "216.58.192.0\/19" + ], + "216.239.32.21": [ + "216.239.32.0\/19" + ], + "220.181.174.33": [ + "220.158.200.0\/21", + "220.158.208.0\/20", + "220.158.224.0\/19", + "220.159.0.0\/16", + "220.160.0.0\/11", + "220.192.0.0\/10", + "221.0.0.0\/8", + "222.0.0.0\/8", + "223.0.0.0\/12", + "223.16.0.0\/13", + "223.24.0.0\/16", + "223.25.0.0\/18", + "223.25.64.0\/19", + "223.25.96.0\/20", + "223.25.112.0\/21" + ], + "43.245.104.78": [ + "43.243.96.0\/19", + "43.243.128.0\/17", + "43.244.0.0\/16", + "43.245.0.0\/18", + "43.245.64.0\/19", + "43.245.96.0\/20", + "43.245.112.0\/22" + ], + "49.231.55.16": [ + "49.14.0.0\/15", + "49.16.0.0\/12", + "49.32.0.0\/11", + "49.64.0.0\/10", + "49.128.0.0\/10", + "49.192.0.0\/11", + "49.224.0.0\/13", + "49.232.0.0\/14" + ], + "49.231.55.19": [ + "49.14.0.0\/15", + "49.16.0.0\/12", + "49.32.0.0\/11", + "49.64.0.0\/10", + "49.128.0.0\/10", + "49.192.0.0\/11", + "49.224.0.0\/13", + "49.232.0.0\/14" + ], + "59.18.44.13": [ + "58.98.0.0\/15", + "58.100.0.0\/14", + "58.104.0.0\/13", + "58.112.0.0\/12", + "58.128.0.0\/9", + "59.0.0.0\/9", + "59.128.0.0\/12", + "59.144.0.0\/13", + "59.152.0.0\/18", + "59.152.64.0\/20", + "59.152.80.0\/21" + ], + "59.18.44.15": [ + "58.98.0.0\/15", + "58.100.0.0\/14", + "58.104.0.0\/13", + "58.112.0.0\/12", + "58.128.0.0\/9", + "59.0.0.0\/9", + "59.128.0.0\/12", + "59.144.0.0\/13", + "59.152.0.0\/18", + "59.152.64.0\/20", + "59.152.80.0\/21" + ], + "59.18.44.17": [ + "58.98.0.0\/15", + "58.100.0.0\/14", + "58.104.0.0\/13", + "58.112.0.0\/12", + "58.128.0.0\/9", + "59.0.0.0\/9", + "59.128.0.0\/12", + "59.144.0.0\/13", + "59.152.0.0\/18", + "59.152.64.0\/20", + "59.152.80.0\/21" + ], + "59.18.44.19": [ + "58.98.0.0\/15", + "58.100.0.0\/14", + "58.104.0.0\/13", + "58.112.0.0\/12", + "58.128.0.0\/9", + "59.0.0.0\/9", + "59.128.0.0\/12", + "59.144.0.0\/13", + "59.152.0.0\/18", + "59.152.64.0\/20", + "59.152.80.0\/21" + ], + "59.18.46.80": [ + "58.98.0.0\/15", + "58.100.0.0\/14", + "58.104.0.0\/13", + "58.112.0.0\/12", + "58.128.0.0\/9", + "59.0.0.0\/9", + "59.128.0.0\/12", + "59.144.0.0\/13", + "59.152.0.0\/18", + "59.152.64.0\/20", + "59.152.80.0\/21" + ], + "59.24.3.173": [ + "58.98.0.0\/15", + "58.100.0.0\/14", + "58.104.0.0\/13", + "58.112.0.0\/12", + "58.128.0.0\/9", + "59.0.0.0\/9", + "59.128.0.0\/12", + "59.144.0.0\/13", + "59.152.0.0\/18", + "59.152.64.0\/20", + "59.152.80.0\/21" + ], + "61.91.8.141": [ + "61.14.228.0\/22", + "61.14.232.0\/21", + "61.14.240.0\/20", + "61.15.0.0\/16", + "61.16.0.0\/12", + "61.32.0.0\/11", + "61.64.0.0\/10", + "61.128.0.0\/9" + ], + "61.91.8.142": [ + "61.14.228.0\/22", + "61.14.232.0\/21", + "61.14.240.0\/20", + "61.15.0.0\/16", + "61.16.0.0\/12", + "61.32.0.0\/11", + "61.64.0.0\/10", + "61.128.0.0\/9" + ], + "61.91.8.144": [ + "61.14.228.0\/22", + "61.14.232.0\/21", + "61.14.240.0\/20", + "61.15.0.0\/16", + "61.16.0.0\/12", + "61.32.0.0\/11", + "61.64.0.0\/10", + "61.128.0.0\/9" + ], + "61.91.8.147": [ + "61.14.228.0\/22", + "61.14.232.0\/21", + "61.14.240.0\/20", + "61.15.0.0\/16", + "61.16.0.0\/12", + "61.32.0.0\/11", + "61.64.0.0\/10", + "61.128.0.0\/9" + ], + "103.252.114.61": [ + "103.252.92.0\/22", + "103.252.96.0\/19", + "103.252.128.0\/17", + "103.253.0.0\/19", + "103.253.32.0\/22" + ], + "103.252.114.101": [ + "103.252.92.0\/22", + "103.252.96.0\/19", + "103.252.128.0\/17", + "103.253.0.0\/19", + "103.253.32.0\/22" + ], + "103.252.115.49": [ + "103.252.92.0\/22", + "103.252.96.0\/19", + "103.252.128.0\/17", + "103.253.0.0\/19", + "103.253.32.0\/22" + ], + "103.252.115.53": [ + "103.252.92.0\/22", + "103.252.96.0\/19", + "103.252.128.0\/17", + "103.253.0.0\/19", + "103.253.32.0\/22" + ], + "103.252.115.59": [ + "103.252.92.0\/22", + "103.252.96.0\/19", + "103.252.128.0\/17", + "103.253.0.0\/19", + "103.253.32.0\/22" + ], + "103.252.115.153": [ + "103.252.92.0\/22", + "103.252.96.0\/19", + "103.252.128.0\/17", + "103.253.0.0\/19", + "103.253.32.0\/22" + ], + "103.252.115.169": [ + "103.252.92.0\/22", + "103.252.96.0\/19", + "103.252.128.0\/17", + "103.253.0.0\/19", + "103.253.32.0\/22" + ], + "103.252.115.221": [ + "103.252.92.0\/22", + "103.252.96.0\/19", + "103.252.128.0\/17", + "103.253.0.0\/19", + "103.253.32.0\/22" + ], + "113.171.242.142": [ + "113.30.224.0\/19", + "113.31.0.0\/16", + "113.32.0.0\/11", + "113.64.0.0\/10", + "113.128.0.0\/10", + "113.192.0.0\/13", + "113.200.0.0\/15", + "113.202.0.0\/16" + ], + "113.171.242.143": [ + "113.30.224.0\/19", + "113.31.0.0\/16", + "113.32.0.0\/11", + "113.64.0.0\/10", + "113.128.0.0\/10", + "113.192.0.0\/13", + "113.200.0.0\/15", + "113.202.0.0\/16" + ], + "113.171.242.147": [ + "113.30.224.0\/19", + "113.31.0.0\/16", + "113.32.0.0\/11", + "113.64.0.0\/10", + "113.128.0.0\/10", + "113.192.0.0\/13", + "113.200.0.0\/15", + "113.202.0.0\/16" + ], + "122.248.226.57": [ + "122.150.0.0\/15", + "122.152.0.0\/13", + "122.160.0.0\/11", + "122.192.0.0\/11", + "122.224.0.0\/12", + "122.240.0.0\/13", + "122.248.0.0\/14", + "122.252.0.0\/15", + "122.254.0.0\/18" + ], + "122.252.245.13": [ + "122.150.0.0\/15", + "122.152.0.0\/13", + "122.160.0.0\/11", + "122.192.0.0\/11", + "122.224.0.0\/12", + "122.240.0.0\/13", + "122.248.0.0\/14", + "122.252.0.0\/15", + "122.254.0.0\/18" + ], + "154.83.15.20": [ + "154.33.0.0\/16", + "154.34.0.0\/15", + "154.36.0.0\/14", + "154.40.0.0\/13", + "154.48.0.0\/12", + "154.64.0.0\/10", + "154.128.0.0\/9", + "155.0.0.0\/14" + ], + "154.83.15.45": [ + "154.33.0.0\/16", + "154.34.0.0\/15", + "154.36.0.0\/14", + "154.40.0.0\/13", + "154.48.0.0\/12", + "154.64.0.0\/10", + "154.128.0.0\/9", + "155.0.0.0\/14" + ], + "154.85.102.30": [ + "154.33.0.0\/16", + "154.34.0.0\/15", + "154.36.0.0\/14", + "154.40.0.0\/13", + "154.48.0.0\/12", + "154.64.0.0\/10", + "154.128.0.0\/9", + "155.0.0.0\/14" + ], + "154.85.102.32": [ + "154.33.0.0\/16", + "154.34.0.0\/15", + "154.36.0.0\/14", + "154.40.0.0\/13", + "154.48.0.0\/12", + "154.64.0.0\/10", + "154.128.0.0\/9", + "155.0.0.0\/14" + ], + "154.92.16.97": [ + "154.33.0.0\/16", + "154.34.0.0\/15", + "154.36.0.0\/14", + "154.40.0.0\/13", + "154.48.0.0\/12", + "154.64.0.0\/10", + "154.128.0.0\/9", + "155.0.0.0\/14" + ], + "179.60.193.16": [ + "179.0.0.0\/11", + "179.32.0.0\/12", + "179.48.0.0\/13", + "179.56.0.0\/14", + "179.60.0.0\/16", + "179.61.0.0\/17" + ], + "187.7.116.13": [ + "186.2.176.0\/20", + "186.2.192.0\/18", + "186.3.0.0\/16", + "186.4.0.0\/14", + "186.8.0.0\/13", + "186.16.0.0\/12", + "186.32.0.0\/11", + "186.64.0.0\/10", + "186.128.0.0\/9", + "187.0.0.0\/11", + "187.32.0.0\/16", + "187.33.0.0\/17", + "187.33.128.0\/20" + ], + "2001::1": [ + "2001::\/32" + ], + "2001:3b8:101:3a::11": [ + "2001:3b8::\/32" + ], + "2001:c08:7f:151::11": [ + "2001:c08::\/32" + ], + "2001:ce8:b2:1a5::f": [ + "2001:ce8::\/32" + ], + "2001:ee0:3204::e": [ + "2001:ee0::\/30" + ], + "2001:fb0:1064:5::c": [ + "2001:fb0::\/31" + ], + "2001:12e0:803:3::16": [ + "2001:12e0::\/32" + ], + "2001:4288:1328::c": [ + "2001:4288::\/32" + ], + "2001:4488:f213:2042::d": [ + "2001:4488::\/30" + ], + "2001:4860:4802:32::15": [ + "2001:4860::\/32" + ], + "2001:b032:100:1::d": [ + "2001:b000::\/21" + ], + "2400:32:0:5::c": [ + "2400::\/20" + ], + "2401:3800:4001:10::1001": [ + "2401:3800::\/32" + ], + "2401:7400:8888:11::d": [ + "2401:7400::\/31" + ], + "2401:b200:40:7::d": [ + "2401:b200::\/32" + ], + "2402:800:6350:119::10": [ + "2402:800::\/32" + ], + "2403:6200:ffff:fff9::d": [ + "2403:6200::\/32" + ], + "2404:6800:4001:800::2001": [ + "2404:6800::\/32" + ], + "2404:a800:6:a::1b": [ + "2404:a800::\/32" + ], + "2405:9800:8:3000::d": [ + "2405:9800::\/32" + ], + "2407:0:0:13::c": [ + "2407::\/30" + ], + "2607:f740:100:108::1400": [ + "2607:f740:100::\/48" + ], + "2607:f8b0:4000:38::9": [ + "2607:f8b0::\/32" + ], + "2800:3f0:4001:20::9": [ + "2800:3f0::\/32" + ], + "2803:600:cace::c": [ + "2803:600::\/32" + ], + "2804:d50:80:f00d::d": [ + "2804:d50::\/28" + ], + "2a00:f46:0:302::f": [ + "2a00:f46::\/36" + ], + "2a00:1450:4001:1::9": [ + "2a00:1450:4000::\/37" + ], + "2a00:8b00:0:800::e": [ + "2a00:8b00::\/32" + ], + "2a01:620:1:a043::c": [ + "2a01:620:1:a043::\/64" + ], + "2a01:758:ffe2::c": [ + "2a01:758::\/32" + ], + "2a02:148:c:99c5::d": [ + "2a02:148::\/32" + ], + "2a02:1c8:1:104::c": [ + "2a02:1c8::\/32" + ], + "2a02:c7a:8000:1009::c": [ + "2a02:c7a:8000::\/48" + ], + "2a02:2168:4:2::c": [ + "2a02:2168::\/29" + ], + "2a02:2278:ffff:1::c": [ + "2a02:2278:ff00::\/40" + ], + "2a02:2378:1:1002::d": [ + "2a02:2378::\/32" + ], + "2a02:a000:1:202::c": [ + "2a02:a000::\/26" + ], + "2a03:2880:f102:183:face:b00c:0:25de": [ + "2a03:2880::\/29" + ], + "2a03:7380:2283:c000::c": [ + "2a03:7380:2280::\/42" + ], + "2a03:d000:2841::e": [ + "2a03:d000:2000::\/36" + ], + "2c0f:fb50:4002:800::2003": [ + "2c0f:fb50::\/32" + ], + "2001:ee0:3204::f": [ + "2001:ee0::\/30" + ], + "2001:ee0:3204::13": [ + "2001:ee0::\/30" + ], + "2001:fb0:1064:5::d": [ + "2001:fb0::\/31" + ], + "2001:fb0:1064:5::e": [ + "2001:fb0::\/31" + ], + "2001:fb0:1064:5::10": [ + "2001:fb0::\/31" + ], + "2001:fb0:1064:5::13": [ + "2001:fb0::\/31" + ], + "2001:4288:1328::d": [ + "2001:4288::\/32" + ], + "2001:4288:1328::e": [ + "2001:4288::\/32" + ], + "2001:4288:1328::f": [ + "2001:4288::\/32" + ], + "2001:4488:f213:2042::f": [ + "2001:4488::\/30" + ], + "2001:4488:f213:2042::10": [ + "2001:4488::\/30" + ], + "2001:4488:f213:2042::13": [ + "2001:4488::\/30" + ], + "2001:4488:f511:5001::e": [ + "2001:4488::\/30" + ], + "2001:4488:f511:5001::f": [ + "2001:4488::\/30" + ], + "2001:4488:f518:50e1::c": [ + "2001:4488::\/30" + ], + "2001:4488:f518:50e1::11": [ + "2001:4488::\/30" + ], + "2001:4488:f518:50e1::12": [ + "2001:4488::\/30" + ], + "2001:4488:f518:50e2::d": [ + "2001:4488::\/30" + ], + "2001:4488:f518:50e2::11": [ + "2001:4488::\/30" + ], + "2001:4488:f518:50e2::13": [ + "2001:4488::\/30" + ], + "2001:4860:4802:32::78": [ + "2001:4860::\/32" + ], + "2001:4860:4802:34::15": [ + "2001:4860::\/32" + ], + "2001:4860:4802:36::15": [ + "2001:4860::\/32" + ], + "2001:4860:4802:38::15": [ + "2001:4860::\/32" + ], + "2001:4860:4802:38::84": [ + "2001:4860::\/32" + ], + "2001:b032:100:1::e": [ + "2001:b000::\/21" + ], + "2001:b032:100:1::10": [ + "2001:b000::\/21" + ], + "2001:b032:100:1::13": [ + "2001:b000::\/21" + ], + "2400:32:0:5::d": [ + "2400::\/20" + ], + "2400:32:0:5::f": [ + "2400::\/20" + ], + "2400:32:0:5::11": [ + "2400::\/20" + ], + "2400:32:0:5::13": [ + "2400::\/20" + ], + "2400:32:0:16::10": [ + "2400::\/20" + ], + "2401:3800:4001:11::1001": [ + "2401:3800::\/32" + ], + "2401:3800:4001:14::1001": [ + "2401:3800::\/32" + ], + "2401:3800:4001:15::1001": [ + "2401:3800::\/32" + ], + "2401:3800:4001:16::1001": [ + "2401:3800::\/32" + ], + "2401:3800:4001:17::1001": [ + "2401:3800::\/32" + ], + "2401:3800:4001:804::1001": [ + "2401:3800::\/32" + ], + "2401:3800:4001:807::1001": [ + "2401:3800::\/32" + ], + "2401:3800:4001:809::1001": [ + "2401:3800::\/32" + ], + "2401:7400:8888:11::e": [ + "2401:7400::\/31" + ], + "2404:6800:4001:800::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:800::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:800::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:800::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:800::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4001:801::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4001:801::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:801::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:801::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:801::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:801::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4001:802::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4001:802::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:802::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:802::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:802::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:802::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4001:803::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4001:803::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:803::2004": [ + "2404:6800::\/32" + ], + "2404:6800:4001:803::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:803::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:803::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:803::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4001:806::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4001:806::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:806::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:806::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:806::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:806::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4001:807::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:807::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:807::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:807::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:807::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4001:808::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4001:808::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:808::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:808::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:808::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:808::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4001:809::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4001:809::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:809::2004": [ + "2404:6800::\/32" + ], + "2404:6800:4001:809::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:809::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:809::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:809::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80a::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80a::2004": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80a::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80a::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80a::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80a::200f": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80a::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80b::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80b::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80b::2004": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80b::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80b::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80b::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80b::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80e::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80e::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80e::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80e::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80e::200f": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80e::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80f::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80f::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80f::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80f::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:80f::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4001:810::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4001:810::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4001:810::2004": [ + "2404:6800::\/32" + ], + "2404:6800:4001:810::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4001:810::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4001:810::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4001:810::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4002:804::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4002:804::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:804::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:804::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4002:805::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:805::200a": [ + "2404:6800::\/32" + ], + "2404:6800:4002:805::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4002:805::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:806::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4002:806::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:806::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4002:806::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:806::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4002:807::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:807::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:807::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4002:808::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:808::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:808::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4002:809::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:809::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80a::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80a::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80a::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80a::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80b::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80b::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80b::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80c::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80c::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80c::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80c::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80d::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80d::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80d::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80d::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80e::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80e::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80e::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80f::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80f::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:80f::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4002:810::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:810::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4002:810::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:811::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:811::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:812::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4002:812::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:812::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:812::2016": [ + "2404:6800::\/32" + ], + "2404:6800:4002:813::2001": [ + "2404:6800::\/32" + ], + "2404:6800:4002:813::2003": [ + "2404:6800::\/32" + ], + "2404:6800:4002:813::200c": [ + "2404:6800::\/32" + ], + "2404:6800:4002:813::200e": [ + "2404:6800::\/32" + ], + "2404:6800:4002:813::2016": [ + "2404:6800::\/32" + ] +} \ No newline at end of file