├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── scripts └── rdap_bootstrap_checker.sh ├── settings.gradle └── src ├── main ├── java │ └── net │ │ └── arin │ │ └── rdap_bootstrap │ │ ├── Constants.java │ │ ├── json │ │ ├── Link.java │ │ ├── Notice.java │ │ └── Response.java │ │ ├── service │ │ ├── AsBootstrap.java │ │ ├── DefaultBootstrap.java │ │ ├── DomainBootstrap.java │ │ ├── EntityBootstrap.java │ │ ├── IpV4Bootstrap.java │ │ ├── IpV6Bootstrap.java │ │ ├── JsonBootstrapFile.java │ │ ├── RedirectServlet.java │ │ ├── ResourceFiles.java │ │ └── Statistics.java │ │ └── spring │ │ ├── AppProperties.java │ │ ├── RdapBootstrapApp.java │ │ └── SpringUtils.java ├── resources │ ├── application.properties │ ├── as_bootstrap.json │ ├── default_bootstrap.json │ ├── domain_bootstrap.json │ ├── entity_bootstrap.json │ ├── rdap_bootstrap.properties │ ├── resource_files.properties │ ├── v4_bootstrap.json │ └── v6_bootstrap.json └── webapp │ ├── WEB-INF │ ├── jboss-web.xml │ └── web.xml │ └── index.jsp └── test └── java └── net └── arin └── rdap_bootstrap └── service ├── AsBootstrapTest.java ├── DefaultBootstrapTest.java ├── DomainBootstrapTest.java ├── IpV4BootstrapTest.java ├── IpV6BootstrapTest.java ├── RedirectServletTest.java └── TestConstants.java /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Continuous Integration 5 | 6 | on: 7 | push: 8 | branches: 9 | - 'master' 10 | tags-ignore: 11 | - '*.*' 12 | 13 | jobs: 14 | build: 15 | name: Build 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v2 20 | 21 | - name: Set up JDK 11 22 | uses: actions/setup-java@v1 23 | with: 24 | java-version: 11 25 | 26 | - name: Compile java 27 | run: ./gradlew compileJava compileTestJava -i 28 | 29 | - name: Run tests 30 | run: ./gradlew test -i 31 | 32 | - name: Publish jars 33 | run: ./gradlew publish -i 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | 37 | - name: Publish unit test results 38 | uses: EnricoMi/publish-unit-test-result-action@v1.5 39 | if: always() 40 | with: 41 | github_token: ${{ secrets.GITHUB_TOKEN }} 42 | files: build/test-results/**/*.xml -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build 3 | target 4 | *.idea 5 | *.iml 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, ARIN Labs <> 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RDAP Bootstrap Server 2 | 3 | The Registration Data Access Protocol (RDAP) defines a bootstrapping process in 4 | [RFC 9224](https://tools.ietf.org/html/rfc9224). A bootstrap server aids clients by reading the 5 | [bootstrapping information published by IANA](https://data.iana.org/rdap/) and using it to send HTTP redirects to RDAP 6 | queries. Clients utilizing a bootstrap server will not need to conduct their own bootstrapping. 7 | 8 | ## Versioning, Building, and Runtime Requirements 9 | 10 | Any version containing the word `SNAPSHOT` is a development version. Versions are: 11 | 12 | * [1.0.0](https://github.com/arineng/rdap_bootstrap_server/releases/tag/1.0.0) - First release. At the time of this 13 | release, the IANA bootstrap files are available but contain no usable content, and the embedded bootstrap files point to 14 | our best known locations for servers. Minor point release may occur to update the embedded files until the IANA files 15 | become populated with useful data. 16 | * [1.1.0](https://github.com/arineng/rdap_bootstrap_server/releases/tag/1.1.0) - A few small things: 17 | - IANA and AFRINIC have been taken out of the bootstrap files as they do not have servers ready yet. 18 | - `/help` now works instead of giving a `500`. 19 | - `/help` shows the dates of load and dates of the bootstrap files. 20 | - `/help` limits the URLs in the statistics to 100 per category. 21 | * [1.1.1](https://github.com/arineng/rdap_bootstrap_server/releases/tag/1.1.1) - Bugfixes to `/help` and to parsing of 22 | IANA files. 23 | * 1.2.0 - Added `match_scheme_on_redirect` option. 24 | * 1.2.1 - Fix to using IANA bootstrapping files according to current RFCs. 25 | * 2.0.1 26 | - Upgraded to build against Java 11 or higher. 27 | - Fixed IPv6 redirection for non-existent space. 28 | - Updated the default bootstrap files to the latest IANA files. 29 | - Can run as a Spring Boot application. 30 | - Can build a Docker image. 31 | - [A built-in timer to download IANA files](https://github.com/arineng/rdap_bootstrap_server/issues/1). 32 | - A new [rdap_bootstrap_checker](./scripts/rdap_bootstrap_checker.sh) script to check the correctness of an RDAP 33 | Bootstrap service as per [RFC 7484](https://tools.ietf.org/html/rfc7484). 34 | * 2.0.2 35 | - Updated the `default_bootstrap.json` file for the domain entry. 36 | - Updated the default bootstrap files to the latest IANA files. 37 | - Upgraded Gradle, Spring Boot, and JUnit. 38 | * 2.0.3 39 | - Use GitHub actions to perform builds 40 | 41 | This server is written as a Java servlet and should run in any Java Servlet 3.0 container or higher, as a Spring Boot 42 | application, or as a Docker container. It should build against Java 11 or higher. 43 | 44 | To build using Gradle: 45 | 46 | ./gradlew clean build test 47 | 48 | This will produce a WAR file in `build/libs` after running the unit tests. The WAR can be directly run from the command 49 | line using either the `java` command or the Gradle `bootRun` command: 50 | 51 | java -jar build/libs/rdap_bootstrap_server-1000.0-SNAPSHOT.war 52 | ./gradlew bootRun 53 | 54 | Beside a WAR, build a JAR using the Gradle `bootJar` task and run it using the `java` command: 55 | 56 | ./gradlew bootJar 57 | java -jar build/libs/rdap_bootstrap_server-1000.0-SNAPSHOT.jar 58 | 59 | System properties can be passed in as `-D` options and/or environment variables. 60 | 61 | ## Deploying and Testing 62 | 63 | Deploying the WAR file depends on the Servlet container/server you are using. It is usually as simple as copying the WAR 64 | file to a particular directory. The WAR file comes bundled with some bootstrap files so testing can take place before 65 | fully configuring the bootstrap. 66 | 67 | The URL path for querying the servlet will depend on your Servlet container and the configuration you have given to the 68 | container for this servlet. It defaults to `/rdapbootstrap`. 69 | 70 | To test the bootstrap server, issue an RDAP query such as `ip/1.1.1.1`. You should see a redirect to APNIC's RDAP 71 | server. 72 | 73 | ``` 74 | $ wget http://localhost:8080/rdapbootstrap/ip/1.1.1.1 75 | --2015-04-24 16:24:39-- http://localhost:8080/rdapbootstrap/ip/1.1.1.1 76 | Resolving localhost... ::1, 127.0.0.1 77 | Connecting to localhost|::1|:8080... connected. 78 | HTTP request sent, awaiting response... 302 Moved Temporarily 79 | Location: http://rdap.apnic.net/ip/1.1.1.1 [following] 80 | --2015-04-24 16:24:39-- http://rdap.apnic.net/ip/1.1.1.1 81 | Resolving rdap.apnic.net... 2001:dd8:9:2::101:43, 203.119.101.43 82 | Connecting to rdap.apnic.net|2001:dd8:9:2::101:43|:80... connected. 83 | HTTP request sent, awaiting response... 200 OK 84 | ``` 85 | 86 | ## Docker 87 | 88 | Build a docker image using the Gradle `bootBuildImage` command: 89 | 90 | ./gradlew bootBuildImage --imageName=NAME[:TAG] 91 | 92 | Run the docker image: 93 | 94 | docker run -p 8080:8080 -ti NAME[:TAG] 95 | 96 | ## Getting Help 97 | 98 | If you have questions or need help with this software, you may use the issue tracker on 99 | [GitHub](https://github.com/arineng/rdap_bootstrap_server/issues) or you may use the 100 | [ARIN Technical Discussions ](http://lists.arin.net/mailman/listinfo/arin-tech-discuss) mailing list (it is a very low 101 | volume list). 102 | 103 | ## Properties and Bootstrap Files 104 | 105 | Bootstrap files may either be listed in a properties file pointed to by the system property 106 | `arin.rdapbootstrap.resource_files` or they may be listed using system properties directly or indirectly. Here is an 107 | example of a properties file pointed to by `arin.rdapbootstrap.resource_files`: 108 | 109 | default_bootstrap = /default_bootstrap.json 110 | as_bootstrap = /as_bootstrap.json 111 | domain_bootstrap = /domain_bootstrap.json 112 | v4_bootstrap = /v4_bootstrap.json 113 | v6_bootstrap = /v6_bootstrap.json 114 | entity_bootstrap = /entity_bootstrap.json 115 | 116 | The system properties directly listing these are the keys of the properties file prefixed with 117 | `arin.rdapbootstrap.bootfile.`. So the AS bootstrap would be `arin.rdapbootstrap.bootfile.as_bootstrap`, etc. 118 | 119 | The server ships with a properties file that points to a set of built-in bootstrap files. These bootstrap files are 120 | useful for getting the server up and running, but ultimately will need to be replaced with files that are updated 121 | periodically from the IANA. 122 | 123 | So there are four types of configuration. 124 | 125 | ### Configuration Setup Type 1 Example 126 | 127 | Do nothing and let the server use the bootstrap files that ship with it. 128 | 129 | ### Configuration Setup Type 2 Example 130 | 131 | Set the Java system property `arin.rdapbootstrap.resource_files` to be `/var/rdap/resource_files.properties`. 132 | 133 | In the `/var/rdap/resource_files.properties` file have the following: 134 | 135 | default_bootstrap = /var/rdap/default_bootstrap.json 136 | as_bootstrap = /var/rdap/as_bootstrap.json 137 | domain_bootstrap = /var/rdap/domain_bootstrap.json 138 | v4_bootstrap = /var/rdap/v4_bootstrap.json 139 | v6_bootstrap = /var/rdap/v6_bootstrap.json 140 | entity_bootstrap = /var/rdap/entity_bootstrap.json 141 | 142 | ### Configuration Setup Type 3 Example 143 | 144 | Have the following Java system properties: 145 | 146 | arin.rdapbootstrap.bootfile.default_bootstrap = /var/rdap/default_bootstrap.json 147 | arin.rdapbootstrap.bootfile.as_bootstrap = /var/rdap/as_bootstrap.json 148 | arin.rdapbootstrap.bootfile.domain_bootstrap = /var/rdap/domain_bootstrap.json 149 | arin.rdapbootstrap.bootfile.v4_bootstrap = /var/rdap/v4_bootstrap.json 150 | arin.rdapbootstrap.bootfile.v6_bootstrap = /var/rdap/v6_bootstrap.json 151 | arin.rdapbootstrap.bootfile.entity_bootstrap = /var/rdap/entity_bootstrap.json 152 | 153 | ### Configuration Setup Type 4 Example 154 | 155 | Set the following Java system properties for the scheduler to periodically download bootstrap files from IANA: 156 | 157 | arin.rdapbootstrap.download_bootstrap_files=true 158 | arin.rdapbootstrap.download_directory=/var/rdap 159 | 160 | There are additional Java system properties with defaults that if needed could be tweaked for the scheduler: 161 | 162 | arin.rdapbootstrap.download_interval=86400 163 | arin.rdapbootstrap.download_asn_file_url=https://data.iana.org/rdap/asn.json 164 | arin.rdapbootstrap.download_domain_file_url=https://data.iana.org/rdap/dns.json 165 | arin.rdapbootstrap.download_ipv4_file_url=https://data.iana.org/rdap/ipv4.json 166 | arin.rdapbootstrap.download_ipv6_file_url=https://data.iana.org/rdap/ipv6.json 167 | 168 | ## Updating Bootstrap Files 169 | 170 | The server checks every minute to see if a file has been modified, and if any of them have it will automatically reload 171 | all of them. 172 | 173 | The AS, v4, v6, and domain files are published periodically by IANA. You can set a cron or system process (see 174 | Configuration Setup Type 4 Example) to fetch them, perhaps once a week, from the following places: 175 | 176 | https://data.iana.org/rdap/asn.json 177 | https://data.iana.org/rdap/ipv4.json 178 | https://data.iana.org/rdap/ipv6.json 179 | https://data.iana.org/rdap/dns.json 180 | 181 | The other bootstrap files take the form of the IANA files but are custom to your particular installation of the 182 | bootstrap server. 183 | 184 | ### Entity Bootstrap File 185 | 186 | The entity bootstrap file is used to redirect queries for entities based on the last component of the entity handle or 187 | identifier. Some registries, most notably all of the RIRs, append a registry signifier such as `-ARIN`. While entity 188 | bootstrapping is not officially part of the IETF specification, this server attempts to issue redirects based on those 189 | signifiers if present. Here is an example of an entity bootstrap file: 190 | 191 | ```json 192 | { 193 | "version": "1.0", 194 | "publication": "2014-09-09T15:39:03-0400", 195 | "services": [ 196 | [ 197 | [ 198 | "ARIN" 199 | ], 200 | [ 201 | "https://rdap.arin.net/registry", 202 | "http://rdap.arin.net/registry" 203 | ] 204 | ], 205 | [ 206 | [ 207 | "AP" 208 | ], 209 | [ 210 | "https://rdap.apnic.net/" 211 | ] 212 | ], 213 | [ 214 | [ 215 | "RIPE" 216 | ], 217 | [ 218 | "https://rdap.db.ripe.net/", 219 | "http://rdap.db.ripe.net/" 220 | ] 221 | ], 222 | [ 223 | [ 224 | "LACNIC" 225 | ], 226 | [ 227 | "https://rdap.lacnic.net/rdap/" 228 | ] 229 | ] 230 | ] 231 | } 232 | ``` 233 | 234 | ### Default Bootstrap File 235 | 236 | The default bootstrap file is consulted when all the other bootstrap files have failed. It takes the following form: 237 | 238 | ```json 239 | { 240 | "version": "1.0", 241 | "publication": "2024-07-02T12:15:00-0400", 242 | "services": [ 243 | [ 244 | [ 245 | "ip", 246 | "autnum", 247 | "domain", 248 | "nameserver", 249 | "entity" 250 | ], 251 | [ 252 | "https://rdap.arin.net/registry/", 253 | "http://rdap.arin.net/registry/" 254 | ] 255 | ] 256 | ] 257 | } 258 | ``` 259 | 260 | ## Redirect Scheme Matching 261 | 262 | By default, this server will always attempt to issue HTTPS redirects. This should not be a problem because all RDAP 263 | clients are _REQUIRED_ to support both HTTP and HTTPS. However, if it is necessary to try to keep the scheme (HTTP or 264 | HTTPS) for the redirect that was given in the query, this behavior can be set with the system property 265 | `arin.rdapbootstrap.match_scheme_on_redirect=true`. Note that this is a system property and is not part of the 266 | `resouce_files.properties` file. 267 | 268 | ## Environment Variables / System Properties Mapping 269 | 270 | The bootstrap server can be configured using environment variables and/or system properties. Here is how they map: 271 | 272 | Environment Variable: RDAPBOOTSTRAP_MATCH_SCHEME_ON_REDIRECT 273 | System Property: arin.rdapbootstrap.match_scheme_on_redirect 274 | Description: Keep the scheme (HTTP or HTTPS) for the redirect that was given in the query or not 275 | Type: BOOLEAN 276 | Required: No 277 | Default Value: false 278 | Possible Values: false | true 279 | 280 | Environment Variable: RDAPBOOTSTRAP_DOWNLOAD_BOOTSTRAP_FILES 281 | System Property: arin.rdapbootstrap.download_bootstrap_files 282 | Description: Download bootstrap files from IANA or not 283 | Type: BOOLEAN 284 | Required: No 285 | Default Value: false 286 | Possible Values: false | true 287 | 288 | Environment Variable: RDAPBOOTSTRAP_DOWNLOAD_ASN_FILE_URL 289 | System Property: arin.rdapbootstrap.download_asn_file_url 290 | Description: Download URL for the ASN file 291 | Type: URL 292 | Required: No 293 | Default Value: https://data.iana.org/rdap/asn.json 294 | 295 | Environment Variable: RDAPBOOTSTRAP_DOWNLOAD_DOMAIN_FILE_URL 296 | System Property: arin.rdapbootstrap.download_domain_file_url 297 | Description: Download URL for the domain file 298 | Type: URL 299 | Required: No 300 | Default Value: https://data.iana.org/rdap/dns.json 301 | 302 | Environment Variable: RDAPBOOTSTRAP_DOWNLOAD_IPV4_FILE_URL 303 | System Property: arin.rdapbootstrap.download_ipv4_file_url 304 | Description: Download URL for the IPv4 file 305 | Type: URL 306 | Required: No 307 | Default Value: https://data.iana.org/rdap/ipv4.json 308 | 309 | Environment Variable: RDAPBOOTSTRAP_DOWNLOAD_IPV6_FILE_URL 310 | System Property: arin.rdapbootstrap.download_ipv6_file_url 311 | Description: Download URL for the IPv6 file 312 | Type: URL 313 | Required: No 314 | Default Value: https://data.iana.org/rdap/ipv6.json 315 | 316 | Environment Variable: RDAPBOOTSTRAP_DOWNLOAD_DIRECTORY 317 | System Property: arin.rdapbootstrap.download_directory 318 | Description: Directory to download IANA files into 319 | Type: DIRECTORY_PATH 320 | Required: Only if arin.rdapbootstrap.download_bootstrap_files is set to true 321 | 322 | Environment Variable: RDAPBOOTSTRAP_DOWNLOAD_INTERVAL 323 | System Property: arin.rdapbootstrap.download_interval 324 | Description: Download interval in seconds 325 | Type: POSITIVE_LONG 326 | Required: No 327 | Default Value: 86400 328 | 329 | Environment Variable: RDAPBOOTSTRAP_BOOTFILE_DEFAULT_BOOTSTRAP 330 | System Property: arin.rdapbootstrap.bootfile.default_bootstrap 331 | Description: Location of the default bootstrap file 332 | Type: FILE_PATH 333 | Required: Only if configuration setup type 3 334 | 335 | Environment Variable: RDAPBOOTSTRAP_BOOTFILE_AS_BOOTSTRAP 336 | System Property: arin.rdapbootstrap.bootfile.as_bootstrap 337 | Description: Location of the ASN file 338 | Type: FILE_PATH 339 | Required: Only if configuration setup type 3 340 | 341 | Environment Variable: RDAPBOOTSTRAP_BOOTFILE_DOMAIN_BOOTSTRAP 342 | System Property: arin.rdapbootstrap.bootfile.domain_bootstrap 343 | Description: Location of the domain file 344 | Type: FILE_PATH 345 | Required: Only if configuration setup type 3 346 | 347 | Environment Variable: RDAPBOOTSTRAP_BOOTFILE_V4_BOOTSTRAP 348 | System Property: arin.rdapbootstrap.bootfile.v4_bootstrap 349 | Description: Location of the IPv4 file 350 | Type: FILE_PATH 351 | Required: Only if configuration setup type 3 352 | 353 | Environment Variable: RDAPBOOTSTRAP_BOOTFILE_V6_BOOTSTRAP 354 | System Property: arin.rdapbootstrap.bootfile.v6_bootstrap 355 | Description: Location of the IPv6 file 356 | Type: FILE_PATH 357 | Required: Only if configuration setup type 3 358 | 359 | Environment Variable: RDAPBOOTSTRAP_BOOTFILE_ENTITY_BOOTSTRAP 360 | System Property: arin.rdapbootstrap.bootfile.entity_bootstrap 361 | Description: Location of the entity file 362 | Type: FILE_PATH 363 | Required: Only if configuration setup type 3 364 | 365 | Environment Variable: RDAPBOOTSTRAP_LOG_LEVEL 366 | System Properties: logging.level.org.springframework.web, logging.level.net.arin.rdap_bootstrap 367 | Description: Adjust the server log level 368 | Type: LOG_LEVEL 369 | Required: No 370 | Default Value: INFO 371 | Possible Values: TRACE | DEBUG | INFO | WARN | ERROR | FATAL 372 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.18' 3 | id 'io.spring.dependency-management' version '1.1.4' 4 | id 'java-library' 5 | id 'war' 6 | id 'maven-publish' 7 | } 8 | 9 | import org.gradle.internal.jvm.Jvm 10 | 11 | assert Jvm.current().javaVersion >= JavaVersion.VERSION_11: 'This project requires Java 11+' 12 | 13 | group = 'net.arin' 14 | description = 'RDAP Bootstrap Server' 15 | 16 | version = '2.0.5-SNAPSHOT' 17 | if (project.hasProperty('artifactVersion')) { 18 | version = "${project.properties.artifactVersion}" 19 | } 20 | 21 | sourceCompatibility = JavaVersion.VERSION_11 22 | targetCompatibility = JavaVersion.VERSION_11 23 | 24 | repositories { 25 | maven { url 'https://repo.maven.apache.org/maven2' } 26 | } 27 | 28 | ext { 29 | logbackVersion = '1.2.13' 30 | } 31 | 32 | dependencies { 33 | implementation 'org.springframework.boot:spring-boot-starter-web' 34 | implementation 'org.springframework.boot:spring-boot-starter-actuator' 35 | implementation group: 'com.googlecode.java-ipv6', name: 'java-ipv6', version: '0.17' 36 | implementation group: 'net.ripe.ipresource', name: 'ipresource', version: '1.47' 37 | implementation group: 'commons-io', name: 'commons-io', version: '2.7' 38 | 39 | providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' 40 | 41 | testImplementation group: 'junit', name: 'junit', version: '4.13.2' 42 | } 43 | 44 | springBoot { 45 | mainClass = 'net.arin.rdap_bootstrap.spring.RdapBootstrapApp' 46 | } 47 | 48 | bootRun { 49 | systemProperties System.properties 50 | } 51 | 52 | publishing { 53 | repositories { 54 | maven { 55 | name = "GitHubPackages" 56 | url = "https://maven.pkg.github.com/arineng/rdap_bootstrap_server" 57 | credentials { 58 | username = System.getenv("GITHUB_ACTOR") 59 | password = System.getenv("GITHUB_TOKEN") 60 | } 61 | } 62 | } 63 | publications { 64 | bootJava(MavenPublication) { 65 | artifact bootJar 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arineng/rdap_bootstrap_server/1f0ef07edef740f3f495802200af8f9c3e113445/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https://services.gradle.org/distributions/gradle-8.8-all.zip 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /scripts/rdap_bootstrap_checker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | pass=0 6 | fail=0 7 | 8 | function print_usage() { 9 | echo 'This script checks the correctness of an RDAP Bootstrap service as per RFC 7484.' 10 | echo 11 | echo 'Usage:' 12 | echo ' rdap_bootstrap_checker.sh RDAP_BOOTSTRAP_BASE_URL' 13 | echo 14 | echo 'Where:' 15 | echo ' RDAP_BOOTSTRAP_BASE_URL Base URL of the RDAP Bootstrap service (e.g. https://rdap.arin.net/bootstrap)' 16 | } 17 | 18 | function query() { 19 | echo 20 | echo -n "$1 - " 21 | status=$(curl -s -o /dev/null -w '%{http_code}' "$1") 22 | if [[ $status -ne $2 ]]; then 23 | echo "FAIL (expected $2)" 24 | ((fail+=1)) 25 | else 26 | echo "PASS" 27 | ((pass+=1)) 28 | fi 29 | curl -s -I "$1" 30 | } 31 | 32 | if [[ $# -ne 1 ]]; then 33 | print_usage 34 | exit 1 35 | fi 36 | 37 | # /domain 38 | query "$1/domain/google.com" 302 39 | query "$1/domain/google.foo" 302 40 | query "$1/domain/xn--flw351e" 302 41 | query "$1/domain/2.in-addr.arpa" 302 42 | query "$1/domain/15.in-addr.arpa" 302 43 | query "$1/domain/0.0.e.0.1.0.0.2.ip6.arpa" 302 44 | 45 | # /nameserver 46 | query "$1/nameserver/ns1.cnn.com" 302 47 | query "$1/nameserver/ns1.15.in-addr.arpa" 302 # Default redirection 48 | 49 | # /ip 50 | query "$1/ip/2.0.0.0/8" 302 51 | query "$1/ip/15.0.0.0/8" 302 52 | query "$1/ip/2c00::/12" 302 53 | query "$1/ip/2c00::/13" 302 54 | query "$1/ip/3c00::/12" 302 # Default redirection 55 | 56 | # /autnum 57 | query "$1/autnum/1" 302 58 | query "$1/autnum/272796" 302 59 | query "$1/autnum/272797" 302 # Default redirection 60 | 61 | # /entity 62 | query "$1/entity/ARINN-ARIN" 302 63 | query "$1/entity/IRT-APNIC-AP" 302 64 | query "$1/entity/ATTW" 302 # Default redirection 65 | 66 | echo "PASS=$pass FAIL=$fail" 67 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'rdap-bootstrap-server' 2 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | package net.arin.rdap_bootstrap; 17 | 18 | /** 19 | * Holds app-wide constants. 20 | */ 21 | public class Constants 22 | { 23 | public final static String PROPERTY_PREFIX = "arin.rdapbootstrap."; 24 | 25 | public final static String MATCH_SCHEME_ON_REDIRECT_PROPERTY = "arin.rdapbootstrap.match_scheme_on_redirect"; 26 | public final static String DOWNLOAD_BOOTSTRAP_FILES_PROPERTY = "arin.rdapbootstrap.download_bootstrap_files"; 27 | public final static String DOWNLOAD_ASN_FILE_URL_PROPERTY = "arin.rdapbootstrap.download_asn_file_url"; 28 | public final static String DOWNLOAD_DOMAIN_FILE_URL_PROPERTY = "arin.rdapbootstrap.download_domain_file_url"; 29 | public final static String DOWNLOAD_IPV4_FILE_URL_PROPERTY = "arin.rdapbootstrap.download_ipv4_file_url"; 30 | public final static String DOWNLOAD_IPV6_FILE_URL_PROPERTY = "arin.rdapbootstrap.download_ipv6_file_url"; 31 | public final static String DOWNLOAD_DIRECTORY_PROPERTY = "arin.rdapbootstrap.download_directory"; 32 | public final static String DOWNLOAD_INTERVAL_PROPERTY = "arin.rdapbootstrap.download_interval"; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/json/Link.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | package net.arin.rdap_bootstrap.json; 17 | 18 | /** 19 | * An RDAP JSON link. 20 | */ 21 | public class Link 22 | { 23 | private String value; 24 | private String rel; 25 | private String type; 26 | private String href; 27 | 28 | public String getValue() 29 | { 30 | return value; 31 | } 32 | 33 | public void setValue( String value ) 34 | { 35 | this.value = value; 36 | } 37 | 38 | public String getRel() 39 | { 40 | return rel; 41 | } 42 | 43 | public void setRel( String rel ) 44 | { 45 | this.rel = rel; 46 | } 47 | 48 | public String getType() 49 | { 50 | return type; 51 | } 52 | 53 | public void setType( String type ) 54 | { 55 | this.type = type; 56 | } 57 | 58 | public String getHref() 59 | { 60 | return href; 61 | } 62 | 63 | public void setHref( String href ) 64 | { 65 | this.href = href; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/json/Notice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | package net.arin.rdap_bootstrap.json; 17 | 18 | /** 19 | * An RDAP JSON notice. 20 | */ 21 | public class Notice 22 | { 23 | private String title; 24 | private String[] description; 25 | private Link[] links; 26 | 27 | public String getTitle() 28 | { 29 | return title; 30 | } 31 | 32 | public void setTitle( String title ) 33 | { 34 | this.title = title; 35 | } 36 | 37 | public String[] getDescription() 38 | { 39 | return description; 40 | } 41 | 42 | public void setDescription( String[] description ) 43 | { 44 | this.description = description; 45 | } 46 | 47 | public Link[] getLinks() 48 | { 49 | return links; 50 | } 51 | 52 | public void setLinks( Link[] links ) 53 | { 54 | this.links = links; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/json/Response.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | package net.arin.rdap_bootstrap.json; 17 | 18 | import java.util.List; 19 | import java.util.Objects; 20 | 21 | /** 22 | * An RDAP JSON response. 23 | */ 24 | public class Response 25 | { 26 | private String[] rdapConformance; 27 | private Notice[] notices; 28 | 29 | public Response( String[] rdapConformance ) 30 | { 31 | this.rdapConformance = Objects.requireNonNullElseGet( rdapConformance, () -> new String[]{ "rdap_level_0" } ); 32 | } 33 | 34 | public String[] getRdapConformance() 35 | { 36 | return rdapConformance; 37 | } 38 | 39 | public void setRdapConformance( String[] rdapConformance ) 40 | { 41 | this.rdapConformance = rdapConformance; 42 | } 43 | 44 | public Notice[] getNotices() 45 | { 46 | return notices; 47 | } 48 | 49 | public void setNotices( Notice[] notices ) 50 | { 51 | this.notices = notices; 52 | } 53 | 54 | public void setNotices( List notices ) 55 | { 56 | this.notices = notices.toArray( new Notice[0] ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/service/AsBootstrap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import net.arin.rdap_bootstrap.service.JsonBootstrapFile.ServiceUrls; 20 | import net.arin.rdap_bootstrap.service.ResourceFiles.BootFiles; 21 | 22 | import java.util.Map; 23 | import java.util.TreeMap; 24 | 25 | public class AsBootstrap implements JsonBootstrapFile.Handler 26 | { 27 | private static class AsRangeInfo 28 | { 29 | private final Long asStart; 30 | private final Long asEnd; 31 | private final ServiceUrls serviceUrls; 32 | 33 | public AsRangeInfo( Long asStart, Long asEnd, ServiceUrls serviceUrls ) 34 | { 35 | this.asStart = asStart; 36 | this.asEnd = asEnd; 37 | this.serviceUrls = serviceUrls; 38 | } 39 | 40 | public Long getAsStart() 41 | { 42 | return asStart; 43 | } 44 | 45 | public Long getAsEnd() 46 | { 47 | return asEnd; 48 | } 49 | 50 | public ServiceUrls getServiceUrls() 51 | { 52 | return serviceUrls; 53 | } 54 | } 55 | 56 | private volatile TreeMap allocations = new TreeMap<>(); 57 | private TreeMap _allocations; 58 | 59 | private JsonBootstrapFile.ServiceUrls serviceUrls; 60 | private String publication; 61 | private String description; 62 | 63 | @Override 64 | public void startServices() 65 | { 66 | _allocations = new TreeMap<>(); 67 | } 68 | 69 | @Override 70 | public void endServices() 71 | { 72 | allocations = _allocations; 73 | } 74 | 75 | @Override 76 | public void startService() 77 | { 78 | serviceUrls = new ServiceUrls(); 79 | } 80 | 81 | @Override 82 | public void endService() 83 | { 84 | // Nothing to do. 85 | } 86 | 87 | @Override 88 | public void addServiceEntry( String entry ) 89 | { 90 | if ( entry != null ) 91 | { 92 | String[] arr = entry.split( "-" ); 93 | long key = Long.parseLong( arr[0] ); 94 | if ( !_allocations.containsKey( key ) ) 95 | { 96 | long max = key; 97 | if ( arr.length == 2 ) 98 | { 99 | max = Long.parseLong( arr[1] ); 100 | } 101 | AsRangeInfo asRangeInfo = new AsRangeInfo( key, max, serviceUrls ); 102 | _allocations.put( key, asRangeInfo ); 103 | } 104 | } 105 | } 106 | 107 | @Override 108 | public void addServiceUrl( String url ) 109 | { 110 | serviceUrls.addUrl( url ); 111 | } 112 | 113 | public void loadData( ResourceFiles resourceFiles ) 114 | throws Exception 115 | { 116 | JsonBootstrapFile bsFile = new JsonBootstrapFile(); 117 | bsFile.loadData( resourceFiles.getInputStream( BootFiles.AS.getKey() ), this ); 118 | } 119 | 120 | public ServiceUrls getServiceUrls( String autnum ) 121 | { 122 | long number = Long.parseLong( autnum ); 123 | Map.Entry entry = allocations.floorEntry( number ); 124 | if ( entry != null ) 125 | { 126 | AsRangeInfo asRangeInfo = entry.getValue(); 127 | if ( number <= asRangeInfo.getAsEnd() ) 128 | { 129 | return asRangeInfo.getServiceUrls(); 130 | } 131 | } 132 | // else 133 | return null; 134 | } 135 | 136 | @Override 137 | public void setPublication( String publication ) 138 | { 139 | this.publication = publication; 140 | } 141 | 142 | public String getPublication() 143 | { 144 | return publication; 145 | } 146 | 147 | public String getDescription() 148 | { 149 | return description; 150 | } 151 | 152 | @Override 153 | public void setDescription( String description ) 154 | { 155 | this.description = description; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/service/DefaultBootstrap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import net.arin.rdap_bootstrap.service.JsonBootstrapFile.ServiceUrls; 20 | import net.arin.rdap_bootstrap.service.ResourceFiles.BootFiles; 21 | 22 | import java.util.HashMap; 23 | 24 | public class DefaultBootstrap implements JsonBootstrapFile.Handler 25 | { 26 | public enum Type 27 | { 28 | NAMESERVER( "nameserver" ), 29 | IP( "ip" ), 30 | AUTNUM( "autnum" ), 31 | ENTITY( "entity" ), 32 | DOMAIN( "domain" ); 33 | 34 | private final String pValue; 35 | 36 | Type( String pValue ) 37 | { 38 | this.pValue = pValue; 39 | } 40 | 41 | public String getPValue() 42 | { 43 | return pValue; 44 | } 45 | } 46 | 47 | private volatile HashMap allocations = new HashMap<>(); 48 | private HashMap _allocations; 49 | 50 | private ServiceUrls serviceUrls; 51 | private String publication; 52 | private String description; 53 | 54 | public void loadData( ResourceFiles resourceFiles ) 55 | throws Exception 56 | { 57 | JsonBootstrapFile bsFile = new JsonBootstrapFile(); 58 | bsFile.loadData( resourceFiles.getInputStream( BootFiles.DEFAULT.getKey() ), this ); 59 | } 60 | 61 | @Override 62 | public void startServices() 63 | { 64 | _allocations = new HashMap<>(); 65 | } 66 | 67 | @Override 68 | public void endServices() 69 | { 70 | allocations = _allocations; 71 | } 72 | 73 | @Override 74 | public void startService() 75 | { 76 | serviceUrls = new ServiceUrls(); 77 | } 78 | 79 | @Override 80 | public void endService() 81 | { 82 | // Nothing to do. 83 | } 84 | 85 | @Override 86 | public void addServiceEntry( String entry ) 87 | { 88 | _allocations.put( entry, serviceUrls ); 89 | } 90 | 91 | @Override 92 | public void addServiceUrl( String url ) 93 | { 94 | serviceUrls.addUrl( url ); 95 | } 96 | 97 | public ServiceUrls getServiceUrls( Type type ) 98 | { 99 | return allocations.get( type.getPValue() ); 100 | } 101 | 102 | @Override 103 | public void setPublication( String publication ) 104 | { 105 | this.publication = publication; 106 | } 107 | 108 | public String getPublication() 109 | { 110 | return publication; 111 | } 112 | 113 | @Override 114 | public void setDescription( String description ) 115 | { 116 | this.description = description; 117 | } 118 | 119 | public String getDescription() 120 | { 121 | return description; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/service/DomainBootstrap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import net.arin.rdap_bootstrap.service.JsonBootstrapFile.ServiceUrls; 20 | import net.arin.rdap_bootstrap.service.ResourceFiles.BootFiles; 21 | 22 | import java.util.HashMap; 23 | 24 | public class DomainBootstrap implements JsonBootstrapFile.Handler 25 | { 26 | private volatile HashMap allocations = new HashMap<>(); 27 | private HashMap _allocations; 28 | 29 | private ServiceUrls serviceUrls; 30 | private String publication; 31 | private String description; 32 | 33 | public void loadData( ResourceFiles resourceFiles ) 34 | throws Exception 35 | { 36 | JsonBootstrapFile bsFile = new JsonBootstrapFile(); 37 | bsFile.loadData( resourceFiles.getInputStream( BootFiles.DOMAIN.getKey() ), this ); 38 | } 39 | 40 | @Override 41 | public void startServices() 42 | { 43 | _allocations = new HashMap<>(); 44 | } 45 | 46 | @Override 47 | public void endServices() 48 | { 49 | allocations = _allocations; 50 | } 51 | 52 | @Override 53 | public void startService() 54 | { 55 | serviceUrls = new ServiceUrls(); 56 | } 57 | 58 | @Override 59 | public void endService() 60 | { 61 | // Nothing to do. 62 | } 63 | 64 | @Override 65 | public void addServiceEntry( String entry ) 66 | { 67 | _allocations.put( entry.toUpperCase(), serviceUrls ); 68 | } 69 | 70 | @Override 71 | public void addServiceUrl( String url ) 72 | { 73 | serviceUrls.addUrl( url ); 74 | } 75 | 76 | public ServiceUrls getServiceUrls( String domain ) 77 | { 78 | domain = domain.toUpperCase(); 79 | int idx = 0; 80 | ServiceUrls retval = null; 81 | while ( idx != -1 ) 82 | { 83 | retval = allocations.get( domain.substring( idx ) ); 84 | if ( retval != null ) 85 | { 86 | break; 87 | } 88 | // else 89 | idx = domain.indexOf( ".", idx ); 90 | if ( idx != -1 ) 91 | { 92 | idx++; 93 | } 94 | } 95 | return retval; 96 | } 97 | 98 | @Override 99 | public void setPublication( String publication ) 100 | { 101 | this.publication = publication; 102 | } 103 | 104 | public String getPublication() 105 | { 106 | return publication; 107 | } 108 | 109 | public String getDescription() 110 | { 111 | return description; 112 | } 113 | 114 | @Override 115 | public void setDescription( String description ) 116 | { 117 | this.description = description; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/service/EntityBootstrap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import net.arin.rdap_bootstrap.service.JsonBootstrapFile.ServiceUrls; 20 | import net.arin.rdap_bootstrap.service.ResourceFiles.BootFiles; 21 | 22 | import java.util.HashMap; 23 | 24 | public class EntityBootstrap implements JsonBootstrapFile.Handler 25 | { 26 | private volatile HashMap allocations = new HashMap<>(); 27 | private HashMap _allocations; 28 | 29 | private ServiceUrls serviceUrls; 30 | private String publication; 31 | private String description; 32 | 33 | public void loadData( ResourceFiles resourceFiles ) 34 | throws Exception 35 | { 36 | JsonBootstrapFile bsFile = new JsonBootstrapFile(); 37 | bsFile.loadData( resourceFiles.getInputStream( BootFiles.ENTITY.getKey() ), this ); 38 | } 39 | 40 | @Override 41 | public void startServices() 42 | { 43 | _allocations = new HashMap<>(); 44 | } 45 | 46 | @Override 47 | public void endServices() 48 | { 49 | allocations = _allocations; 50 | } 51 | 52 | @Override 53 | public void startService() 54 | { 55 | serviceUrls = new ServiceUrls(); 56 | } 57 | 58 | @Override 59 | public void endService() 60 | { 61 | // Nothing to do. 62 | } 63 | 64 | @Override 65 | public void addServiceEntry( String entry ) 66 | { 67 | _allocations.put( entry, serviceUrls ); 68 | } 69 | 70 | @Override 71 | public void addServiceUrl( String url ) 72 | { 73 | serviceUrls.addUrl( url ); 74 | } 75 | 76 | public ServiceUrls getServiceUrls( String defaultType ) 77 | { 78 | return allocations.get( defaultType ); 79 | } 80 | 81 | @Override 82 | public void setPublication( String publication ) 83 | { 84 | this.publication = publication; 85 | } 86 | 87 | public String getPublication() 88 | { 89 | return publication; 90 | } 91 | 92 | public String getDescription() 93 | { 94 | return description; 95 | } 96 | 97 | @Override 98 | public void setDescription( String description ) 99 | { 100 | this.description = description; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/service/IpV4Bootstrap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import java.util.HashMap; 20 | import java.util.Set; 21 | 22 | import net.arin.rdap_bootstrap.service.JsonBootstrapFile.ServiceUrls; 23 | import net.arin.rdap_bootstrap.service.ResourceFiles.BootFiles; 24 | import net.ripe.ipresource.IpRange; 25 | import net.ripe.ipresource.UniqueIpResource; 26 | 27 | public class IpV4Bootstrap implements JsonBootstrapFile.Handler 28 | { 29 | private volatile HashMap allocations = new HashMap<>(); 30 | private HashMap _allocations; 31 | 32 | private ServiceUrls serviceUrls; 33 | private String publication; 34 | private String description; 35 | 36 | public void loadData( ResourceFiles resourceFiles ) throws Exception 37 | { 38 | JsonBootstrapFile bsFile = new JsonBootstrapFile(); 39 | bsFile.loadData( resourceFiles.getInputStream( BootFiles.V4.getKey() ), this ); 40 | } 41 | 42 | @Override 43 | public void startServices() 44 | { 45 | _allocations = new HashMap<>(); 46 | } 47 | 48 | @Override 49 | public void endServices() 50 | { 51 | allocations = _allocations; 52 | } 53 | 54 | @Override 55 | public void startService() 56 | { 57 | serviceUrls = new ServiceUrls(); 58 | } 59 | 60 | @Override 61 | public void endService() 62 | { 63 | // Nothing to do. 64 | } 65 | 66 | @Override 67 | public void addServiceEntry( String entry ) 68 | { 69 | _allocations.put( entry, serviceUrls ); 70 | } 71 | 72 | @Override 73 | public void addServiceUrl( String url ) 74 | { 75 | serviceUrls.addUrl( url ); 76 | } 77 | 78 | public ServiceUrls getServiceUrls( String prefix ) 79 | { 80 | UniqueIpResource start; 81 | 82 | if ( !prefix.contains( "/" ) && prefix.contains( "." ) ) 83 | { 84 | // Single host. 85 | start = UniqueIpResource.parse( prefix ); 86 | } 87 | else if ( !prefix.contains( "/" ) ) 88 | { 89 | // /8 single int behaviour. 90 | try 91 | { 92 | Integer.valueOf( prefix ); 93 | start = IpRange.parse( prefix + ".0.0.0/8" ).getStart(); 94 | } 95 | catch ( NumberFormatException e ) 96 | { 97 | // Network. 98 | start = IpRange.parse( prefix ).getStart(); 99 | } 100 | } 101 | else 102 | { 103 | // Network. 104 | start = IpRange.parse( prefix ).getStart(); 105 | } 106 | 107 | ServiceUrls resultUrl = null; 108 | IpRange resultNetwork = IpRange.parse( "0.0.0.0/0" ); 109 | final Set keys = allocations.keySet(); 110 | for ( String key : keys ) 111 | { 112 | final IpRange network = IpRange.parse( key ); 113 | if ( network.contains( start ) && ( resultNetwork.getPrefixLength() < network.getPrefixLength() ) ) 114 | { 115 | resultNetwork = network; 116 | resultUrl = allocations.get( key ); 117 | } 118 | } 119 | return resultUrl; 120 | } 121 | 122 | @Override 123 | public void setPublication( String publication ) 124 | { 125 | this.publication = publication; 126 | } 127 | 128 | public String getPublication() 129 | { 130 | return publication; 131 | } 132 | 133 | public String getDescription() 134 | { 135 | return description; 136 | } 137 | 138 | @Override 139 | public void setDescription( String description ) 140 | { 141 | this.description = description; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/service/IpV6Bootstrap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import java.util.Map; 20 | import java.util.TreeMap; 21 | 22 | import net.arin.rdap_bootstrap.service.JsonBootstrapFile.ServiceUrls; 23 | import net.arin.rdap_bootstrap.service.ResourceFiles.BootFiles; 24 | 25 | import com.googlecode.ipv6.IPv6Address; 26 | import com.googlecode.ipv6.IPv6Network; 27 | 28 | public class IpV6Bootstrap implements JsonBootstrapFile.Handler 29 | { 30 | private static class HighBitsRangeInfo 31 | { 32 | private final Long highBitsStart; 33 | private final Long highBitsEnd; 34 | private final ServiceUrls serviceUrls; 35 | 36 | public HighBitsRangeInfo( Long highBitsStart, Long highBitsEnd, ServiceUrls serviceUrls ) 37 | { 38 | this.highBitsStart = highBitsStart; 39 | this.highBitsEnd = highBitsEnd; 40 | this.serviceUrls = serviceUrls; 41 | } 42 | 43 | public Long getHighBitsStart() 44 | { 45 | return highBitsStart; 46 | } 47 | 48 | public Long getHighBitsEnd() 49 | { 50 | return highBitsEnd; 51 | } 52 | 53 | public ServiceUrls getServiceUrls() 54 | { 55 | return serviceUrls; 56 | } 57 | } 58 | 59 | private volatile TreeMap allocations = new TreeMap<>(); 60 | private TreeMap _allocations; 61 | 62 | private ServiceUrls serviceUrls; 63 | private String publication; 64 | private String description; 65 | 66 | @Override 67 | public void startServices() 68 | { 69 | _allocations = new TreeMap<>(); 70 | } 71 | 72 | @Override 73 | public void endServices() 74 | { 75 | allocations = _allocations; 76 | } 77 | 78 | @Override 79 | public void startService() 80 | { 81 | serviceUrls = new ServiceUrls(); 82 | } 83 | 84 | @Override 85 | public void endService() 86 | { 87 | // Nothing to do. 88 | } 89 | 90 | @Override 91 | public void addServiceEntry( String entry ) 92 | { 93 | IPv6Network v6net = IPv6Network.fromString( entry ); 94 | long key = v6net.getFirst().getHighBits(); 95 | int prefixLength = v6net.getNetmask().asPrefixLength(); 96 | _allocations.put( key, new HighBitsRangeInfo( key, key + ( long ) ( Math.pow( 2, 64 - prefixLength ) - 1 ), serviceUrls ) ); 97 | } 98 | 99 | @Override 100 | public void addServiceUrl( String url ) 101 | { 102 | serviceUrls.addUrl( url ); 103 | } 104 | 105 | public void loadData( ResourceFiles resourceFiles ) 106 | throws Exception 107 | { 108 | JsonBootstrapFile bsFile = new JsonBootstrapFile(); 109 | bsFile.loadData( resourceFiles.getInputStream( BootFiles.V6.getKey() ), this ); 110 | } 111 | 112 | public ServiceUrls getServiceUrls( long prefixStart, long prefixEnd ) 113 | { 114 | ServiceUrls retval = null; 115 | Map.Entry entry = allocations.floorEntry( prefixStart ); 116 | if ( entry != null ) 117 | { 118 | HighBitsRangeInfo highBitsRangeInfo = entry.getValue(); 119 | if ( highBitsRangeInfo.getHighBitsStart() <= prefixStart && prefixEnd <= highBitsRangeInfo.getHighBitsEnd() ) 120 | { 121 | retval = highBitsRangeInfo.getServiceUrls(); 122 | } 123 | } 124 | return retval; 125 | } 126 | 127 | public ServiceUrls getServiceUrls( IPv6Address addr ) 128 | { 129 | return getServiceUrls( addr.getHighBits(), addr.getHighBits() ); 130 | } 131 | 132 | public ServiceUrls getServiceUrls( IPv6Network net ) 133 | { 134 | long prefixStart = net.getFirst().getHighBits(); 135 | int prefixLength = net.getNetmask().asPrefixLength(); 136 | long prefixEnd = prefixStart + ( long ) ( Math.pow( 2, 64 - prefixLength ) - 1 ); 137 | return getServiceUrls( prefixStart, prefixEnd ); 138 | } 139 | 140 | @Override 141 | public void setPublication( String publication ) 142 | { 143 | this.publication = publication; 144 | } 145 | 146 | public String getPublication() 147 | { 148 | return publication; 149 | } 150 | 151 | public String getDescription() 152 | { 153 | return description; 154 | } 155 | 156 | @Override 157 | public void setDescription( String description ) 158 | { 159 | this.description = description; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/service/JsonBootstrapFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import com.fasterxml.jackson.core.JsonFactory; 20 | import com.fasterxml.jackson.core.JsonParser; 21 | import com.fasterxml.jackson.core.JsonToken; 22 | 23 | import java.io.InputStream; 24 | import java.util.ArrayList; 25 | 26 | /** 27 | * An abstraction for generically processing the JSON bootstrap files. 28 | */ 29 | public class JsonBootstrapFile 30 | { 31 | /** 32 | * The various versions of the bootstrap are expected to pass in an implementation of this handler. 33 | */ 34 | public interface Handler 35 | { 36 | void startServices(); 37 | 38 | void endServices(); 39 | 40 | void startService(); 41 | 42 | void endService(); 43 | 44 | void addServiceEntry( String entry ); 45 | 46 | void addServiceUrl( String url ); 47 | 48 | void setPublication( String publication ); 49 | 50 | void setDescription( String description ); 51 | } 52 | 53 | /** 54 | * A utility class for referencing URLs. 55 | */ 56 | public static class ServiceUrls 57 | { 58 | private ArrayList urls = new ArrayList<>(); 59 | private int httpIdx = -1; 60 | private int httpsIdx = -1; 61 | 62 | public void addUrl( String url ) 63 | { 64 | if ( url != null ) 65 | { 66 | if ( url.endsWith( "/" ) ) 67 | { 68 | url = url.substring( 0, url.length() - 1 ); 69 | } 70 | urls.add( url ); 71 | if ( url.startsWith( "https://" ) ) 72 | { 73 | httpsIdx = urls.size() - 1; 74 | } 75 | else if ( url.startsWith( "http://" ) ) 76 | { 77 | httpIdx = urls.size() - 1; 78 | } 79 | } 80 | } 81 | 82 | public ArrayList getUrls() 83 | { 84 | return urls; 85 | } 86 | 87 | public void setUrls( ArrayList urls ) 88 | { 89 | this.urls = urls; 90 | } 91 | 92 | public String getHttpUrl() 93 | { 94 | if ( httpIdx != -1 ) 95 | { 96 | return urls.get( httpIdx ); 97 | } 98 | // else 99 | return null; 100 | } 101 | 102 | public String getHttpsUrl() 103 | { 104 | if ( httpsIdx != -1 ) 105 | { 106 | return urls.get( httpsIdx ); 107 | } 108 | // else 109 | return null; 110 | } 111 | } 112 | 113 | public void loadData( InputStream inputStream, Handler handler ) 114 | throws Exception 115 | { 116 | JsonFactory jsonFactory = new JsonFactory(); 117 | JsonParser jsonParser = jsonFactory.createParser( inputStream ); 118 | 119 | if ( jsonParser.nextToken() != JsonToken.START_OBJECT ) 120 | { 121 | throw new RuntimeException( "JSON file does not start with a JSON object" ); 122 | } 123 | // else 124 | while ( jsonParser.nextToken() != JsonToken.END_OBJECT ) 125 | { 126 | if ( jsonParser.getCurrentToken() == JsonToken.FIELD_NAME && 127 | jsonParser.getCurrentName().equals( "version" ) ) 128 | { 129 | if ( jsonParser.nextToken() != JsonToken.VALUE_STRING ) 130 | { 131 | throw new RuntimeException( "'version' is not a string" ); 132 | } 133 | // else 134 | String version = jsonParser.getValueAsString(); 135 | if ( version == null || !version.equals( "1.0" ) ) 136 | { 137 | throw new RuntimeException( "'version' is not '1.0'" ); 138 | } 139 | } 140 | else if ( jsonParser.getCurrentToken() == JsonToken.FIELD_NAME && 141 | jsonParser.getCurrentName().equals( "publication" ) ) 142 | { 143 | // These are dates but since we're outputting them as Strings anyway and not really doing anything 144 | // else with them, leaving them as Strings should be okay. 145 | if ( jsonParser.nextToken() != JsonToken.VALUE_STRING ) 146 | { 147 | throw new RuntimeException( "'publication' is not a string" ); 148 | } 149 | 150 | handler.setPublication( jsonParser.getValueAsString() ); 151 | } 152 | else if ( jsonParser.getCurrentToken() == JsonToken.FIELD_NAME && 153 | jsonParser.getCurrentName().equals( "description" ) ) 154 | { 155 | if ( jsonParser.nextToken() != JsonToken.VALUE_STRING ) 156 | { 157 | throw new RuntimeException( "'description' is not a string" ); 158 | } 159 | 160 | handler.setDescription( jsonParser.getValueAsString() ); 161 | } 162 | else if ( jsonParser.getCurrentToken() == JsonToken.FIELD_NAME && 163 | jsonParser.getCurrentName().equals( "services" ) ) 164 | { 165 | if ( jsonParser.nextToken() != JsonToken.START_ARRAY ) 166 | { 167 | throw new RuntimeException( "'services' is not an array" ); 168 | } 169 | // else 170 | handler.startServices(); 171 | while ( jsonParser.nextToken() != JsonToken.END_ARRAY ) 172 | { 173 | if ( jsonParser.getCurrentToken() != JsonToken.START_ARRAY ) 174 | { 175 | throw new RuntimeException( "Expected array at " + jsonParser.getCurrentLocation() ); 176 | } 177 | // else 178 | handler.startService(); 179 | while ( jsonParser.nextToken() != JsonToken.END_ARRAY ) 180 | { 181 | if ( jsonParser.getCurrentToken() != JsonToken.START_ARRAY ) 182 | { 183 | throw new RuntimeException( "Expected array at " + jsonParser.getCurrentLocation() ); 184 | } 185 | // else 186 | while ( jsonParser.nextToken() != JsonToken.END_ARRAY ) 187 | { 188 | if ( jsonParser.getCurrentToken() != JsonToken.VALUE_STRING ) 189 | { 190 | throw new RuntimeException( "Service entry at " + 191 | jsonParser.getCurrentLocation() + " is not a string" ); 192 | } 193 | // else 194 | handler.addServiceEntry( jsonParser.getValueAsString() ); 195 | } 196 | if ( jsonParser.nextToken() != JsonToken.START_ARRAY ) 197 | { 198 | throw new RuntimeException( "Expected array at " + jsonParser.getCurrentLocation() ); 199 | } 200 | // else 201 | while ( jsonParser.nextToken() != JsonToken.END_ARRAY ) 202 | { 203 | if ( jsonParser.getCurrentToken() != JsonToken.VALUE_STRING ) 204 | { 205 | throw new RuntimeException( "Service URL at " + 206 | jsonParser.getCurrentLocation() + " is not a string" ); 207 | } 208 | // else 209 | handler.addServiceUrl( jsonParser.getValueAsString() ); 210 | } 211 | } 212 | handler.endService(); 213 | } 214 | handler.endServices(); 215 | } 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/service/RedirectServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import java.io.File; 20 | import java.io.IOException; 21 | import java.io.OutputStream; 22 | import java.net.URL; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import java.nio.file.Paths; 26 | import java.nio.file.StandardCopyOption; 27 | import java.util.ArrayList; 28 | import java.util.Arrays; 29 | import java.util.Date; 30 | import java.util.Map.Entry; 31 | import java.util.Set; 32 | import java.util.Timer; 33 | import java.util.TimerTask; 34 | import java.util.concurrent.atomic.AtomicLong; 35 | 36 | import javax.servlet.ServletConfig; 37 | import javax.servlet.ServletException; 38 | import javax.servlet.http.HttpServlet; 39 | import javax.servlet.http.HttpServletRequest; 40 | import javax.servlet.http.HttpServletResponse; 41 | 42 | import net.arin.rdap_bootstrap.spring.AppProperties; 43 | import net.arin.rdap_bootstrap.Constants; 44 | import net.arin.rdap_bootstrap.json.Notice; 45 | import net.arin.rdap_bootstrap.json.Response; 46 | import net.arin.rdap_bootstrap.service.DefaultBootstrap.Type; 47 | import net.arin.rdap_bootstrap.service.JsonBootstrapFile.ServiceUrls; 48 | import net.arin.rdap_bootstrap.service.ResourceFiles.BootFiles; 49 | import net.arin.rdap_bootstrap.service.Statistics.UrlHits; 50 | 51 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 52 | import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; 53 | import com.fasterxml.jackson.databind.ObjectMapper; 54 | import com.fasterxml.jackson.databind.ObjectWriter; 55 | import com.googlecode.ipv6.IPv6Address; 56 | import com.googlecode.ipv6.IPv6Network; 57 | import org.apache.commons.io.FileUtils; 58 | import org.apache.commons.io.FilenameUtils; 59 | import org.apache.logging.log4j.LogManager; 60 | import org.apache.logging.log4j.Logger; 61 | 62 | public class RedirectServlet extends HttpServlet 63 | { 64 | private final DefaultBootstrap defaultBootstrap = new DefaultBootstrap(); 65 | private final DomainBootstrap domainBootstrap = new DomainBootstrap(); 66 | private final IpV6Bootstrap ipV6Bootstrap = new IpV6Bootstrap(); 67 | private final IpV4Bootstrap ipV4Bootstrap = new IpV4Bootstrap(); 68 | private final AsBootstrap asBootstrap = new AsBootstrap(); 69 | private final EntityBootstrap entityBootstrap = new EntityBootstrap(); 70 | 71 | private volatile Statistics statistics; 72 | 73 | private ResourceFiles resourceFiles; 74 | 75 | // Defaults for system properties. 76 | Boolean matchSchemeOnRedirect = Boolean.FALSE; 77 | Boolean downloadBootstrapFiles = Boolean.FALSE; 78 | long downloadInterval = 86400; // a day 79 | 80 | private static final long CHECK_CONFIG_FILES = 60000L; // every 1 minute 81 | 82 | private static final Logger logger = LogManager.getLogger( RedirectServlet.class ); 83 | 84 | @Override 85 | public void init( ServletConfig config ) throws ServletException 86 | { 87 | super.init( config ); 88 | 89 | if ( config != null ) 90 | { 91 | logProperties(); 92 | } 93 | 94 | statistics = new Statistics(); 95 | 96 | matchSchemeOnRedirect = AppProperties.lookupBoolean( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY, matchSchemeOnRedirect ); 97 | 98 | downloadBootstrapFiles = AppProperties.lookupBoolean( Constants.DOWNLOAD_BOOTSTRAP_FILES_PROPERTY, downloadBootstrapFiles ); 99 | if ( downloadBootstrapFiles ) 100 | { 101 | try 102 | { 103 | DownloadBootstrapFilesTask downloadBootstrapFilesTask = new DownloadBootstrapFilesTask(); 104 | if ( config != null ) 105 | { 106 | Timer timer = new Timer(); 107 | downloadInterval = AppProperties.lookupLong( Constants.DOWNLOAD_INTERVAL_PROPERTY, downloadInterval ); 108 | timer.schedule( downloadBootstrapFilesTask, 0L, downloadInterval * 1000L ); 109 | } 110 | 111 | // Pause for the download to complete before loading the config. 112 | Thread.sleep( 10000L ); // 10 seconds 113 | } 114 | catch ( Exception e ) 115 | { 116 | throw new ServletException( e ); 117 | } 118 | } 119 | 120 | try 121 | { 122 | LoadConfigTask loadConfigTask = new LoadConfigTask(); 123 | 124 | // Load data initially. 125 | loadConfigTask.loadData(); 126 | logger.info( "Initial data load successful" ); 127 | 128 | if ( config != null ) 129 | { 130 | Timer timer = new Timer(); 131 | timer.schedule( loadConfigTask, CHECK_CONFIG_FILES, CHECK_CONFIG_FILES ); 132 | } 133 | } 134 | catch ( Exception e ) 135 | { 136 | throw new ServletException( e ); 137 | } 138 | } 139 | 140 | protected void serve( UrlHits urlHits, BaseMaker baseMaker, DefaultBootstrap.Type defaultType, String pathInfo, 141 | HttpServletRequest req, HttpServletResponse resp ) 142 | throws IOException 143 | { 144 | try 145 | { 146 | ServiceUrls urls = baseMaker.makeBase( pathInfo ); 147 | if ( urls == null && defaultType != null ) 148 | { 149 | urls = defaultBootstrap.getServiceUrls( defaultType ); 150 | urlHits = UrlHits.DEFAULTHITS; 151 | } 152 | if ( urls == null ) 153 | { 154 | resp.sendError( HttpServletResponse.SC_NOT_FOUND ); 155 | logger.info( pathInfo + " " + resp.getStatus() ); 156 | statistics.getTotalMisses().incrementAndGet(); 157 | } 158 | else 159 | { 160 | String redirectUrl = getRedirectUrl( req.getScheme(), req.getPathInfo(), urls ); 161 | if ( urlHits != null ) 162 | { 163 | urlHits.hit( redirectUrl ); 164 | } 165 | statistics.getTotalHits().incrementAndGet(); 166 | resp.sendRedirect( redirectUrl ); 167 | logger.info( pathInfo + " " + resp.getStatus() + " " + redirectUrl ); 168 | } 169 | } 170 | catch ( Exception e ) 171 | { 172 | resp.sendError( HttpServletResponse.SC_BAD_REQUEST, e.getMessage() ); 173 | logger.info( pathInfo + " " + resp.getStatus() ); 174 | } 175 | } 176 | 177 | String getRedirectUrl( String scheme, String pathInfo, ServiceUrls urls ) 178 | { 179 | String redirectUrl; 180 | if ( matchSchemeOnRedirect ) 181 | { 182 | if ( scheme.equals( "https" ) && urls.getHttpsUrl() != null ) 183 | { 184 | redirectUrl = urls.getHttpsUrl() + pathInfo; 185 | } 186 | else if ( scheme.equals( "http" ) && urls.getHttpUrl() != null ) 187 | { 188 | redirectUrl = urls.getHttpUrl() + pathInfo; 189 | } 190 | else 191 | { 192 | redirectUrl = urls.getUrls().get( 0 ) + pathInfo; 193 | } 194 | } 195 | else 196 | { 197 | redirectUrl = urls.getHttpsUrl(); 198 | if ( redirectUrl == null ) 199 | { 200 | redirectUrl = urls.getHttpUrl(); 201 | } 202 | if ( redirectUrl != null ) 203 | { 204 | redirectUrl += pathInfo; 205 | } 206 | } 207 | return redirectUrl; 208 | } 209 | 210 | @Override 211 | protected void service( HttpServletRequest req, HttpServletResponse resp ) 212 | throws IOException 213 | { 214 | if ( req == null ) 215 | { 216 | resp.sendError( HttpServletResponse.SC_BAD_REQUEST ); 217 | } 218 | else if ( req.getPathInfo() == null ) 219 | { 220 | resp.sendError( HttpServletResponse.SC_BAD_REQUEST ); 221 | } 222 | else 223 | { 224 | String pathInfo = req.getPathInfo(); 225 | if ( pathInfo.startsWith( "/domain/" ) ) 226 | { 227 | serve( UrlHits.DOMAINHITS, new MakeDomainBase(), Type.DOMAIN, pathInfo, req, resp ); 228 | } 229 | // The /nameserver path leverages the domain bootstrap logic to provide redirection for the nameserver 230 | // queries. 231 | else if ( pathInfo.startsWith( "/nameserver/" ) ) 232 | { 233 | serve( UrlHits.NAMESERVERHITS, new MakeNameserverBase(), Type.NAMESERVER, pathInfo, req, resp ); 234 | } 235 | else if ( pathInfo.startsWith( "/ip/" ) ) 236 | { 237 | serve( UrlHits.IPHITS, new MakeIpBase(), Type.IP, pathInfo, req, resp ); 238 | } 239 | else if ( pathInfo.startsWith( "/autnum/" ) ) 240 | { 241 | serve( UrlHits.ASHITS, new MakeAutnumBase(), Type.AUTNUM, pathInfo, req, resp ); 242 | } 243 | // The /entity path provides redirection for the RIR entity queries. 244 | else if ( pathInfo.startsWith( "/entity/" ) ) 245 | { 246 | serve( UrlHits.ENTITYHITS, new MakeEntityBase(), Type.ENTITY, pathInfo, req, resp ); 247 | } 248 | // The /help path returns statistics for ARIN's RDAP Bootstrap service. 249 | else if ( pathInfo.startsWith( "/help" ) ) 250 | { 251 | resp.setContentType( "application/rdap+json" ); 252 | makeHelp( resp.getOutputStream() ); 253 | logger.info( pathInfo + " " + resp.getStatus() ); 254 | } 255 | else 256 | { 257 | resp.sendError( HttpServletResponse.SC_NOT_FOUND ); 258 | logger.info( pathInfo + " " + resp.getStatus() ); 259 | } 260 | } 261 | } 262 | 263 | public interface BaseMaker 264 | { 265 | ServiceUrls makeBase( String pathInfo ); 266 | } 267 | 268 | // Domain names. 269 | 270 | public ServiceUrls makeDomainBase( String pathInfo ) 271 | { 272 | return new MakeDomainBase().makeBase( pathInfo ); 273 | } 274 | 275 | public class MakeDomainBase implements BaseMaker 276 | { 277 | public ServiceUrls makeBase( String pathInfo ) 278 | { 279 | // Strip leading "/domain/". 280 | pathInfo = pathInfo.substring( 8 ); 281 | // Strip possible trailing period. 282 | if ( pathInfo.endsWith( "." ) ) 283 | { 284 | pathInfo = pathInfo.substring( 0, pathInfo.length() - 1 ); 285 | } 286 | if ( pathInfo.endsWith( ".in-addr.arpa" ) ) 287 | { 288 | final int bitsPerWord = 8; 289 | final int divisor = 1; 290 | final String delimiter = "."; 291 | 292 | String[] words = new String[4]; 293 | Arrays.fill( words, "0" ); 294 | 295 | final String[] _split = pathInfo.split( "\\." ); 296 | int n = _split.length - 2; 297 | 298 | StringBuilder s = new StringBuilder(); 299 | StringBuilder _s = new StringBuilder(); 300 | for ( int i = n - 1, j = 1; i >= 0; i--, j++ ) 301 | { 302 | _s.append( _split[i] ); 303 | words[j / divisor - 1] = _s.toString(); 304 | _s = new StringBuilder(); 305 | } 306 | 307 | // Get the CIDR string (prefix slash prefix length) to query the IPv4 bootstrap. 308 | for ( int i = 0; i < words.length - 1; i++ ) 309 | { 310 | s.append( words[i] ).append( delimiter ); 311 | } 312 | s.append( words[words.length - 1] ); 313 | s.append( "/" ).append( bitsPerWord * n ); 314 | return ipV4Bootstrap.getServiceUrls( s.toString() ); 315 | } 316 | else if ( pathInfo.endsWith( ".ip6.arpa" ) ) 317 | { 318 | String[] labels = pathInfo.split( "\\." ); 319 | byte[] bytes = new byte[16]; 320 | Arrays.fill( bytes, ( byte ) 0 ); 321 | int labelIdx = labels.length - 3; 322 | int byteIdx = 0; 323 | int idxJump = 1; 324 | while ( labelIdx > 0 ) 325 | { 326 | char ch = labels[labelIdx].charAt( 0 ); 327 | byte value = 0; 328 | if ( ch >= '0' && ch <= '9' ) 329 | { 330 | value = ( byte ) ( ch - '0' ); 331 | } 332 | else if ( ch >= 'A' && ch <= 'F' ) 333 | { 334 | value = ( byte ) ( ch - ( 'A' - 0xaL ) ); 335 | } 336 | else if ( ch >= 'a' && ch <= 'f' ) 337 | { 338 | value = ( byte ) ( ch - ( 'a' - 0xaL ) ); 339 | } 340 | if ( idxJump % 2 == 1 ) 341 | { 342 | bytes[byteIdx] = ( byte ) ( value << 4 ); 343 | } 344 | else 345 | { 346 | bytes[byteIdx] = ( byte ) ( bytes[byteIdx] + value ); 347 | } 348 | labelIdx--; 349 | idxJump++; 350 | if ( idxJump % 2 == 1 ) 351 | { 352 | byteIdx++; 353 | } 354 | } 355 | return ipV6Bootstrap.getServiceUrls( IPv6Address.fromByteArray( bytes ) ); 356 | } 357 | // else a forward domain 358 | String[] labels = pathInfo.split( "\\." ); 359 | return domainBootstrap.getServiceUrls( labels[labels.length - 1] ); 360 | } 361 | } 362 | 363 | // Nameservers. Only for forward domains. 364 | 365 | public ServiceUrls makeNameserverBase( String pathInfo ) 366 | { 367 | return new MakeNameserverBase().makeBase( pathInfo ); 368 | } 369 | 370 | public class MakeNameserverBase implements BaseMaker 371 | { 372 | public ServiceUrls makeBase( String pathInfo ) 373 | { 374 | // Strip leading "/nameserver/". 375 | pathInfo = pathInfo.substring( 12 ); 376 | // Strip possible trailing period. 377 | if ( pathInfo.endsWith( "." ) ) 378 | { 379 | pathInfo = pathInfo.substring( 0, pathInfo.length() - 1 ); 380 | } 381 | String[] labels = pathInfo.split( "\\." ); 382 | return domainBootstrap.getServiceUrls( labels[labels.length - 1] ); 383 | } 384 | } 385 | 386 | // IP addresses. 387 | 388 | public ServiceUrls makeIpBase( String pathInfo ) 389 | { 390 | return new MakeIpBase().makeBase( pathInfo ); 391 | } 392 | 393 | public class MakeIpBase implements BaseMaker 394 | { 395 | public ServiceUrls makeBase( String pathInfo ) 396 | { 397 | // Strip leading "/ip/". 398 | pathInfo = pathInfo.substring( 4 ); 399 | if ( !pathInfo.contains( ":" ) ) // is not IPv6 400 | { 401 | return ipV4Bootstrap.getServiceUrls( pathInfo ); 402 | } 403 | // else 404 | IPv6Address addr; 405 | if ( !pathInfo.contains( "/" ) ) 406 | { 407 | addr = IPv6Address.fromString( pathInfo ); 408 | return ipV6Bootstrap.getServiceUrls( addr ); 409 | } 410 | else 411 | { 412 | IPv6Network net = IPv6Network.fromString( pathInfo ); 413 | return ipV6Bootstrap.getServiceUrls( net ); 414 | } 415 | } 416 | } 417 | 418 | // AS numbers. 419 | 420 | public ServiceUrls makeAutnumBase( String pathInfo ) 421 | { 422 | return new MakeAutnumBase().makeBase( pathInfo ); 423 | } 424 | 425 | public class MakeAutnumBase implements BaseMaker 426 | { 427 | public ServiceUrls makeBase( String pathInfo ) 428 | { 429 | return asBootstrap.getServiceUrls( pathInfo.split( "/" )[2] ); 430 | } 431 | } 432 | 433 | // Entities. 434 | 435 | public ServiceUrls makeEntityBase( String pathInfo ) 436 | { 437 | return new MakeEntityBase().makeBase( pathInfo ); 438 | } 439 | 440 | public class MakeEntityBase implements BaseMaker 441 | { 442 | public ServiceUrls makeBase( String pathInfo ) 443 | { 444 | int i = pathInfo.lastIndexOf( '-' ); 445 | if ( i != -1 && i + 1 < pathInfo.length() ) 446 | { 447 | // Use the RIR label in the entity handle to get the redirection URL. 448 | return entityBootstrap.getServiceUrls( pathInfo.substring( i + 1 ) ); 449 | } 450 | // else 451 | return null; 452 | } 453 | } 454 | 455 | // Statistics. 456 | 457 | private Notice makeStatsNotice( Statistics.UrlHits stats ) 458 | { 459 | Notice notice = new Notice(); 460 | notice.setTitle( stats.getTitle() ); 461 | ArrayList description = new ArrayList<>(); 462 | Set> entrySet = stats.getEntrySet(); 463 | if ( entrySet.size() != 0 ) 464 | { 465 | for ( Entry entry : entrySet ) 466 | { 467 | description.add( String.format( "%-5d = %25s", entry.getValue().get(), entry.getKey() ) ); 468 | } 469 | } 470 | else 471 | { 472 | description.add( "Zero queries." ); 473 | } 474 | notice.setDescription( description.toArray( new String[0] ) ); 475 | return notice; 476 | } 477 | 478 | public void makeHelp( OutputStream outputStream ) throws IOException 479 | { 480 | Response response = new Response( null ); 481 | ArrayList notices = new ArrayList<>(); 482 | 483 | // Do statistics. 484 | for ( Statistics.UrlHits stats : Statistics.UrlHits.values() ) 485 | { 486 | notices.add( makeStatsNotice( stats ) ); 487 | } 488 | 489 | // Totals. 490 | Notice notice = new Notice(); 491 | notice.setTitle( "Totals" ); 492 | String[] description = new String[2]; 493 | description[0] = String.format( "Hits = %5d", statistics.getTotalHits().get() ); 494 | description[1] = String.format( "Misses = %5d", statistics.getTotalMisses().get() ); 495 | notice.setDescription( description ); 496 | notices.add( notice ); 497 | 498 | // Modified dates for various bootstrap files. Done this way so that Publication dates can be published as well. 499 | notices.add( createPublicationDateNotice( "Default", 500 | resourceFiles.getLastModified( BootFiles.DEFAULT.getKey() ), 501 | defaultBootstrap.getPublication() ) ); 502 | notices.add( createPublicationDateNotice( "Domain", 503 | resourceFiles.getLastModified( BootFiles.DOMAIN.getKey() ), 504 | domainBootstrap.getPublication() ) ); 505 | notices.add( createPublicationDateNotice( "IPv4", 506 | resourceFiles.getLastModified( BootFiles.V4.getKey() ), 507 | ipV4Bootstrap.getPublication() ) ); 508 | notices.add( createPublicationDateNotice( "IPv6", 509 | resourceFiles.getLastModified( BootFiles.V6.getKey() ), 510 | ipV6Bootstrap.getPublication() ) ); 511 | notices.add( createPublicationDateNotice( "AS", 512 | resourceFiles.getLastModified( BootFiles.AS.getKey() ), 513 | asBootstrap.getPublication() ) ); 514 | notices.add( createPublicationDateNotice( "Entity", 515 | resourceFiles.getLastModified( BootFiles.ENTITY.getKey() ), 516 | entityBootstrap.getPublication() ) ); 517 | 518 | response.setNotices( notices ); 519 | 520 | ObjectMapper mapper = new ObjectMapper(); 521 | mapper.setSerializationInclusion( Include.NON_EMPTY ); 522 | ObjectWriter writer = mapper.writer( new DefaultPrettyPrinter() ); 523 | writer.writeValue( outputStream, response ); 524 | } 525 | 526 | private Notice createPublicationDateNotice( String file, long lastModified, 527 | String publicationDate ) 528 | { 529 | Notice bootFileModifiedNotice = new Notice(); 530 | 531 | bootFileModifiedNotice.setTitle( String.format( "%s Bootstrap File Modified and Published Dates", file ) ); 532 | String[] bootFileModifiedDescription = new String[2]; 533 | // Date format as 2015-05-15T17:04:06-0500 (Y-m-d'T'H:M:Sz). 534 | bootFileModifiedDescription[0] = String.format( "%1$tFT%1$tT%1$tz", lastModified ); 535 | bootFileModifiedDescription[1] = publicationDate; 536 | bootFileModifiedNotice.setDescription( bootFileModifiedDescription ); 537 | 538 | return bootFileModifiedNotice; 539 | } 540 | 541 | private class LoadConfigTask extends TimerTask 542 | { 543 | private boolean isModified( long currentTime, long lastModified ) 544 | { 545 | return ( currentTime - CHECK_CONFIG_FILES ) < lastModified; 546 | } 547 | 548 | @Override 549 | public void run() 550 | { 551 | boolean load = false; 552 | long currentTime = System.currentTimeMillis(); 553 | for ( BootFiles bootFiles : BootFiles.values() ) 554 | { 555 | if ( isModified( currentTime, resourceFiles.getLastModified( bootFiles.getKey() ) ) ) 556 | { 557 | logger.info( String.format( "%s was last modified at %s", bootFiles.getKey(), 558 | new Date( resourceFiles.getLastModified( bootFiles.getKey() ) ) ) ); 559 | load = true; 560 | } 561 | } 562 | if ( load ) 563 | { 564 | try 565 | { 566 | loadData(); 567 | } 568 | catch ( Exception e ) 569 | { 570 | logger.warn( "Problem loading data", e ); 571 | } 572 | } 573 | } 574 | 575 | public void loadData() throws Exception 576 | { 577 | logger.info( "Loading resource files" ); 578 | resourceFiles = new ResourceFiles(); 579 | defaultBootstrap.loadData( resourceFiles ); 580 | domainBootstrap.loadData( resourceFiles ); 581 | ipV4Bootstrap.loadData( resourceFiles ); 582 | ipV6Bootstrap.loadData( resourceFiles ); 583 | asBootstrap.loadData( resourceFiles ); 584 | entityBootstrap.loadData( resourceFiles ); 585 | } 586 | } 587 | 588 | private static class DownloadBootstrapFilesTask extends TimerTask 589 | { 590 | @Override 591 | public void run() 592 | { 593 | try 594 | { 595 | logger.info( "Downloading files from IANA RDAP Bootstrap registry" ); 596 | 597 | String downloadDir = AppProperties.getProperty( Constants.DOWNLOAD_DIRECTORY_PROPERTY ); 598 | if ( downloadDir == null ) 599 | { 600 | throw new IOException( "Specify download directory" ); 601 | } 602 | Path downloadDirPath = Paths.get( downloadDir ); 603 | if ( !downloadDirPath.isAbsolute() ) 604 | { 605 | throw new IOException( "Specify absolute path of the download directory: " + downloadDir ); 606 | } 607 | Files.createDirectories( downloadDirPath ); 608 | 609 | downloadFileSafely( AppProperties.getProperty( Constants.DOWNLOAD_ASN_FILE_URL_PROPERTY ), downloadDir ); 610 | downloadFileSafely( AppProperties.getProperty( Constants.DOWNLOAD_DOMAIN_FILE_URL_PROPERTY ), downloadDir ); 611 | downloadFileSafely( AppProperties.getProperty( Constants.DOWNLOAD_IPV4_FILE_URL_PROPERTY ), downloadDir ); 612 | downloadFileSafely( AppProperties.getProperty( Constants.DOWNLOAD_IPV6_FILE_URL_PROPERTY ), downloadDir ); 613 | } 614 | catch ( IOException e ) 615 | { 616 | logger.warn( "Problem downloading files from IANA RDAP Bootstrap registry", e ); 617 | } 618 | } 619 | 620 | private void downloadFileSafely( String downloadUrlStr, String downloadDir ) 621 | throws IOException 622 | { 623 | logger.info( "Downloading " + downloadUrlStr ); 624 | 625 | URL downloadUrl = new URL( downloadUrlStr ); 626 | String fileName = FilenameUtils.getName( downloadUrl.getPath() ); 627 | Path filePath = Paths.get( downloadDir + "/" + fileName ); 628 | String newFilePathname = downloadDir + "/" + fileName + ".new"; 629 | Path newFilePath = Paths.get( newFilePathname ); 630 | Path curFilePath = Paths.get( downloadDir + "/" + fileName + ".cur" ); 631 | Path oldFilePath = Paths.get( downloadDir + "/" + fileName + ".old" ); 632 | 633 | FileUtils.copyURLToFile( downloadUrl, new File( newFilePathname ), 5000, 5000 ); // 10 seconds wait 634 | 635 | Files.deleteIfExists( oldFilePath ); 636 | 637 | if ( Files.exists( curFilePath ) ) 638 | { 639 | Files.copy( curFilePath, oldFilePath, StandardCopyOption.REPLACE_EXISTING ); 640 | Files.deleteIfExists( filePath ); 641 | Files.createSymbolicLink( filePath, oldFilePath ); 642 | } 643 | 644 | Files.copy( newFilePath, curFilePath, StandardCopyOption.REPLACE_EXISTING ); 645 | Files.deleteIfExists( filePath ); 646 | Files.createSymbolicLink( filePath, curFilePath ); 647 | } 648 | } 649 | 650 | private void logProperties() 651 | { 652 | logger.info( "RDAP Bootstrap server properties: " ); 653 | 654 | logger.info( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY + "=" + 655 | AppProperties.lookupBoolean( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY, matchSchemeOnRedirect ) ); 656 | 657 | logger.info( Constants.DOWNLOAD_BOOTSTRAP_FILES_PROPERTY + "=" + 658 | AppProperties.lookupBoolean( Constants.DOWNLOAD_BOOTSTRAP_FILES_PROPERTY, downloadBootstrapFiles ) ); 659 | 660 | logger.info( Constants.DOWNLOAD_ASN_FILE_URL_PROPERTY + "=" + 661 | AppProperties.getProperty( Constants.DOWNLOAD_ASN_FILE_URL_PROPERTY ) ); 662 | 663 | logger.info( Constants.DOWNLOAD_DOMAIN_FILE_URL_PROPERTY + "=" + 664 | AppProperties.getProperty( Constants.DOWNLOAD_DOMAIN_FILE_URL_PROPERTY ) ); 665 | 666 | logger.info( Constants.DOWNLOAD_IPV4_FILE_URL_PROPERTY + "=" + 667 | AppProperties.getProperty( Constants.DOWNLOAD_IPV4_FILE_URL_PROPERTY ) ); 668 | 669 | logger.info( Constants.DOWNLOAD_IPV6_FILE_URL_PROPERTY + "=" + 670 | AppProperties.getProperty( Constants.DOWNLOAD_IPV6_FILE_URL_PROPERTY ) ); 671 | 672 | logger.info( Constants.DOWNLOAD_DIRECTORY_PROPERTY + "=" + 673 | AppProperties.getProperty( Constants.DOWNLOAD_DIRECTORY_PROPERTY ) ); 674 | 675 | logger.info( Constants.DOWNLOAD_INTERVAL_PROPERTY + "=" + 676 | AppProperties.lookupLong( Constants.DOWNLOAD_INTERVAL_PROPERTY, downloadInterval ) ); 677 | 678 | for ( BootFiles bootFiles : BootFiles.values() ) 679 | { 680 | String property = Constants.PROPERTY_PREFIX + "bootfile." + bootFiles.getKey(); 681 | logger.info( property + "=" + AppProperties.getProperty( property ) ); 682 | } 683 | } 684 | } 685 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/service/ResourceFiles.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | package net.arin.rdap_bootstrap.service; 17 | 18 | import net.arin.rdap_bootstrap.spring.AppProperties; 19 | import net.arin.rdap_bootstrap.Constants; 20 | import org.apache.commons.io.FilenameUtils; 21 | import org.apache.commons.lang3.StringUtils; 22 | import org.apache.logging.log4j.LogManager; 23 | import org.apache.logging.log4j.Logger; 24 | 25 | import java.io.File; 26 | import java.io.FileInputStream; 27 | import java.io.FileNotFoundException; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.net.URL; 31 | import java.nio.file.Path; 32 | import java.nio.file.Paths; 33 | import java.util.HashMap; 34 | import java.util.Map.Entry; 35 | import java.util.Properties; 36 | 37 | /** 38 | * Manages getting resource files. 39 | */ 40 | public class ResourceFiles 41 | { 42 | public enum BootFiles 43 | { 44 | DEFAULT( "default_bootstrap" ), 45 | DOMAIN( "domain_bootstrap" ), 46 | V4( "v4_bootstrap" ), 47 | V6( "v6_bootstrap" ), 48 | AS( "as_bootstrap" ), 49 | ENTITY( "entity_bootstrap" ); 50 | 51 | private final String key; 52 | 53 | public String getKey() 54 | { 55 | return key; 56 | } 57 | 58 | BootFiles( String key ) 59 | { 60 | this.key = key; 61 | } 62 | } 63 | 64 | private final Properties resourceFiles; 65 | private final HashMap isFile; 66 | 67 | private static final Logger logger = LogManager.getLogger( ResourceFiles.class ); 68 | 69 | public ResourceFiles() throws IOException 70 | { 71 | String extFileName = AppProperties.getProperty( Constants.PROPERTY_PREFIX + "resource_files" ); 72 | resourceFiles = new Properties(); 73 | File file; 74 | if ( extFileName == null ) 75 | { 76 | InputStream inputStream = getClass().getResourceAsStream( "/resource_files.properties" ); 77 | resourceFiles.load( inputStream ); 78 | } 79 | else if ( ( file = new File( extFileName ) ).isFile() ) 80 | { 81 | InputStream inputStream = new FileInputStream( file ); 82 | resourceFiles.load( inputStream ); 83 | } 84 | // Override with explicitly set system properties. 85 | Boolean downloadBootstrapFiles = AppProperties.lookupBoolean( Constants.DOWNLOAD_BOOTSTRAP_FILES_PROPERTY, false ); 86 | String downloadDir = AppProperties.getProperty( Constants.DOWNLOAD_DIRECTORY_PROPERTY ); 87 | for ( BootFiles bootFiles : BootFiles.values() ) 88 | { 89 | String bootfilePropertyName = Constants.PROPERTY_PREFIX + "bootfile." + bootFiles.key; 90 | if ( downloadBootstrapFiles && StringUtils.isNotBlank( downloadDir ) ) 91 | { 92 | if ( bootfilePropertyName.contains( BootFiles.AS.key ) ) 93 | { 94 | setBootfileProperty( bootfilePropertyName, Constants.DOWNLOAD_ASN_FILE_URL_PROPERTY, downloadDir ); 95 | } 96 | else if ( bootfilePropertyName.contains( BootFiles.DOMAIN.key ) ) 97 | { 98 | setBootfileProperty( bootfilePropertyName, Constants.DOWNLOAD_DOMAIN_FILE_URL_PROPERTY, downloadDir ); 99 | } 100 | else if ( bootfilePropertyName.contains( BootFiles.V4.key ) ) 101 | { 102 | setBootfileProperty( bootfilePropertyName, Constants.DOWNLOAD_IPV4_FILE_URL_PROPERTY, downloadDir ); 103 | } 104 | else if ( bootfilePropertyName.contains( BootFiles.V6.key ) ) 105 | { 106 | setBootfileProperty( bootfilePropertyName, Constants.DOWNLOAD_IPV6_FILE_URL_PROPERTY, downloadDir ); 107 | } 108 | } 109 | String value = AppProperties.getProperty( bootfilePropertyName ); 110 | if ( value != null && value.length() > 0 ) 111 | { 112 | resourceFiles.put( bootFiles.key, value ); 113 | } 114 | } 115 | isFile = new HashMap<>(); 116 | for ( Entry entry : resourceFiles.entrySet() ) 117 | { 118 | file = new File( entry.getValue().toString() ); 119 | isFile.put( entry.getKey().toString(), file.exists() ); 120 | } 121 | } 122 | 123 | private void setBootfileProperty( String bootfilePropertyName, String downloadUrlPropertyName, String downloadDir ) 124 | throws IOException 125 | { 126 | String downloadUrlStr = AppProperties.getProperty( downloadUrlPropertyName ); 127 | if ( StringUtils.isNotBlank( downloadUrlStr ) ) 128 | { 129 | URL downloadUrl = new URL( downloadUrlStr ); 130 | String fileName = FilenameUtils.getName( downloadUrl.getPath() ); 131 | Path filePath = Paths.get( downloadDir + "/" + fileName ); 132 | System.setProperty( bootfilePropertyName, filePath.toString() ); 133 | logger.debug( "Set " + bootfilePropertyName + "=" + AppProperties.getProperty( bootfilePropertyName ) ); 134 | } 135 | } 136 | 137 | public InputStream getInputStream( String key ) throws FileNotFoundException 138 | { 139 | logger.debug( "Load data from " + resourceFiles.getProperty( key ) ); 140 | if ( isFile.get( key ) ) 141 | { 142 | File file = new File( resourceFiles.getProperty( key ) ); 143 | return new FileInputStream( file ); 144 | } 145 | // else 146 | return getClass().getResourceAsStream( resourceFiles.getProperty( key ) ); 147 | } 148 | 149 | public long getLastModified( String key ) 150 | { 151 | if ( !isFile.get( key ) ) 152 | { 153 | return System.currentTimeMillis(); 154 | } 155 | // else 156 | File file = new File( resourceFiles.getProperty( key ) ); 157 | return file.lastModified(); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/service/Statistics.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | package net.arin.rdap_bootstrap.service; 17 | 18 | import java.util.Collections; 19 | import java.util.LinkedHashMap; 20 | import java.util.Map; 21 | import java.util.Map.Entry; 22 | import java.util.Set; 23 | import java.util.concurrent.atomic.AtomicLong; 24 | 25 | public class Statistics 26 | { 27 | private static class LruMap extends LinkedHashMap 28 | { 29 | private final int maxEntries; 30 | 31 | public LruMap( int maxEntries ) 32 | { 33 | this.maxEntries = maxEntries; 34 | } 35 | 36 | @Override 37 | protected boolean removeEldestEntry( Entry entry ) 38 | { 39 | return super.size() > maxEntries; 40 | } 41 | } 42 | 43 | public enum UrlHits 44 | { 45 | DEFAULTHITS( "Default Hits" ), 46 | DOMAINHITS( "Domain Hits" ), 47 | NAMESERVERHITS( "Nameserver Hits" ), 48 | IPHITS( "IP Hits" ), 49 | ASHITS( "Autnum Hits" ), 50 | ENTITYHITS( "Entity Hits" ); 51 | 52 | private final Map hitsMap = Collections.synchronizedMap( new LruMap<>( 100 ) ); 53 | private final String title; 54 | 55 | public void hit( String url ) 56 | { 57 | AtomicLong counter = hitsMap.get( url ); 58 | if ( counter == null ) 59 | { 60 | hitsMap.put( url, new AtomicLong( 1 ) ); 61 | } 62 | else 63 | { 64 | counter.incrementAndGet(); 65 | } 66 | } 67 | 68 | public Set> getEntrySet() 69 | { 70 | return hitsMap.entrySet(); 71 | } 72 | 73 | public String getTitle() 74 | { 75 | return title; 76 | } 77 | 78 | UrlHits( String title ) 79 | { 80 | this.title = title; 81 | } 82 | } 83 | 84 | private final AtomicLong totalHits = new AtomicLong( 0 ); 85 | private final AtomicLong totalMisses = new AtomicLong( 0 ); 86 | 87 | public AtomicLong getTotalHits() 88 | { 89 | return totalHits; 90 | } 91 | 92 | public AtomicLong getTotalMisses() 93 | { 94 | return totalMisses; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/spring/AppProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | package net.arin.rdap_bootstrap.spring; 17 | 18 | import org.springframework.core.env.PropertyResolver; 19 | import org.springframework.core.env.StandardEnvironment; 20 | 21 | import java.io.File; 22 | import java.math.BigDecimal; 23 | import java.util.SortedMap; 24 | import java.util.TreeMap; 25 | import java.util.function.Function; 26 | import java.util.stream.Collectors; 27 | 28 | public abstract class AppProperties 29 | { 30 | protected AppProperties() 31 | { 32 | } 33 | 34 | public static final String PLACE_VALUE_HERE = "***PLACE_VALUE_HERE***"; 35 | 36 | private static final boolean allowForDefaultValues = true; 37 | 38 | private static PropertyResolver resolver = new StandardEnvironment(); 39 | 40 | public static void updateResolver( PropertyResolver resolver ) 41 | { 42 | if ( resolver == null ) 43 | { 44 | throw new IllegalArgumentException( "Resolver cannot be null" ); 45 | } 46 | 47 | AppProperties.resolver = resolver; 48 | } 49 | 50 | public static String getProperty( String name ) 51 | { 52 | return resolver.containsProperty( name ) ? resolver.getProperty( name ) : null; 53 | } 54 | 55 | public static String getProperty( String name, String defaultValue ) 56 | { 57 | return resolver.containsProperty( name ) ? resolver.getProperty( name ) : defaultValue; 58 | } 59 | 60 | public static void bind( String name, Object value ) 61 | { 62 | System.setProperty( name, value.toString() ); 63 | } 64 | 65 | public static void unbind( String name ) 66 | { 67 | System.clearProperty( name ); 68 | } 69 | 70 | public static Integer lookupInteger( String name ) 71 | { 72 | return lookup( Integer.class, name ); 73 | } 74 | 75 | public static Long lookupLong( String name ) 76 | { 77 | return lookup( Long.class, name ); 78 | } 79 | 80 | public static Long lookupLong( String name, long defaultValue ) 81 | { 82 | return lookup( Long.class, name, defaultValue ); 83 | } 84 | 85 | public static BigDecimal lookupBigDecimal( String name ) 86 | { 87 | return lookup( BigDecimal.class, name ); 88 | } 89 | 90 | public static String lookupString( String name ) 91 | { 92 | return lookup( String.class, name ); 93 | } 94 | 95 | public static Boolean lookupBoolean( String name ) 96 | { 97 | return lookup( Boolean.class, name ); 98 | } 99 | 100 | public static Integer lookupInteger( String name, int defaultValue ) 101 | { 102 | return lookup( Integer.class, name, defaultValue ); 103 | } 104 | 105 | public static String lookupString( String name, String defaultValue ) 106 | { 107 | return lookup( String.class, name, defaultValue ); 108 | } 109 | 110 | public static String lookupDirectory( String name ) 111 | { 112 | String directory = lookupString( name ); 113 | return directory.endsWith( File.separator ) ? directory : directory + File.separator; 114 | } 115 | 116 | public static Boolean lookupBoolean( String name, Boolean defaultValue ) 117 | { 118 | return lookup( Boolean.class, name, defaultValue ); 119 | } 120 | 121 | public static T lookup( Class clazz, String name ) 122 | { 123 | String value = getProperty( name, false, allowForDefaultValues ); 124 | return parseValue( value, clazz, name ); 125 | } 126 | 127 | public static T lookup( Class clazz, String name, T defaultValue ) 128 | { 129 | String value = getProperty( name, true, allowForDefaultValues ); 130 | if ( value == null ) 131 | { 132 | return defaultValue; 133 | } 134 | 135 | return parseValue( value, clazz, name ); 136 | } 137 | 138 | public static T lookupForceAllowDefault( Class clazz, String name, T defaultValue ) 139 | { 140 | String value = getProperty( name, true, true ); 141 | if ( value == null ) 142 | { 143 | return defaultValue; 144 | } 145 | 146 | return parseValue( value, clazz, name ); 147 | } 148 | 149 | private static String getProperty( String name, boolean nullable, boolean allowForDefaultValues ) 150 | { 151 | String value = getProperty( name ); 152 | 153 | if ( value == null ) 154 | { 155 | if ( !nullable ) 156 | { 157 | throw new RuntimeException( "System property '" + name + "' not found." ); 158 | } 159 | if ( !allowForDefaultValues ) 160 | { 161 | throw new RuntimeException( "System property '" + name + "' not found and default values are not allowed." ); 162 | } 163 | } 164 | else if ( value.equalsIgnoreCase( PLACE_VALUE_HERE ) ) 165 | { 166 | if ( !nullable ) 167 | { 168 | throw new RuntimeException( "System property '" + name + "' is '" + PLACE_VALUE_HERE + "' (i.e., not set)" ); 169 | } 170 | if ( !allowForDefaultValues ) 171 | { 172 | throw new RuntimeException( "System property '" + name + "' is '" + PLACE_VALUE_HERE + "' (i.e., not set) and default values are not allowed." ); 173 | } 174 | } 175 | 176 | return value; 177 | } 178 | 179 | @SuppressWarnings( "unchecked" ) 180 | private static T parseValue( String value, Class clazz, String name ) 181 | { 182 | if ( clazz == String.class ) 183 | { 184 | return ( T ) value; 185 | } 186 | else if ( clazz == Boolean.class ) 187 | { 188 | if ( value.equalsIgnoreCase( Boolean.TRUE.toString() ) ) 189 | { 190 | return ( T ) Boolean.TRUE; 191 | } 192 | else if ( value.equalsIgnoreCase( Boolean.FALSE.toString() ) ) 193 | { 194 | return ( T ) Boolean.FALSE; 195 | } 196 | else 197 | { 198 | throw new RuntimeException( "System property '" + name + "' is not a boolean value" ); 199 | } 200 | } 201 | else if ( clazz == Integer.class ) 202 | { 203 | return ( T ) Integer.decode( value ); 204 | } 205 | else if ( clazz == Long.class ) 206 | { 207 | return ( T ) Long.decode( removeEndingL( value ) ); 208 | } 209 | else if ( clazz == BigDecimal.class ) 210 | { 211 | return ( T ) new BigDecimal( removeEndingL( value ) ); 212 | } 213 | else 214 | { 215 | throw new UnsupportedOperationException( "System property of " + clazz + " type is not supported" ); 216 | } 217 | } 218 | 219 | private static String removeEndingL( String value ) 220 | { 221 | String newValue = value.trim(); 222 | return newValue.endsWith( "L" ) ? newValue.substring( 0, newValue.length() - 1 ) : newValue; 223 | } 224 | 225 | public static SortedMap getSystemProperties( String prefix ) 226 | { 227 | return System.getProperties().keySet().stream() 228 | .map( Object::toString ) 229 | .filter( name -> name.startsWith( prefix ) ) 230 | .collect( Collectors.toMap( Function.identity(), AppProperties::getProperty, ( v1, v2 ) -> v1, TreeMap::new ) ); 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/spring/RdapBootstrapApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | package net.arin.rdap_bootstrap.spring; 17 | 18 | import javax.servlet.Servlet; 19 | 20 | import org.springframework.beans.factory.config.BeanPostProcessor; 21 | import org.springframework.boot.SpringApplication; 22 | import org.springframework.boot.autoconfigure.SpringBootApplication; 23 | import org.springframework.boot.web.servlet.ServletRegistrationBean; 24 | import org.springframework.context.ConfigurableApplicationContext; 25 | import org.springframework.context.annotation.Bean; 26 | import org.springframework.context.annotation.PropertySource; 27 | 28 | @PropertySource( { "classpath:rdap_bootstrap.properties" } ) 29 | @SpringBootApplication 30 | public class RdapBootstrapApp 31 | { 32 | public static void main( String[] args ) 33 | { 34 | SpringApplication.run( RdapBootstrapApp.class, args ); 35 | } 36 | 37 | @Bean 38 | public static BeanPostProcessor postProcessor( ConfigurableApplicationContext ctx ) 39 | { 40 | return SpringUtils.createInitBean( ctx ); 41 | } 42 | 43 | @Bean 44 | public static ServletRegistrationBean rdapBootstrapRedirectServlet() throws Exception 45 | { 46 | ServletRegistrationBean registrationBean = new ServletRegistrationBean<>(); 47 | registrationBean.setServlet( ( Servlet ) Class.forName( "net.arin.rdap_bootstrap.service.RedirectServlet" ).getConstructor().newInstance() ); 48 | registrationBean.addUrlMappings( "/rdapbootstrap/*" ); 49 | registrationBean.setLoadOnStartup( 1 ); 50 | return registrationBean; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/net/arin/rdap_bootstrap/spring/SpringUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | package net.arin.rdap_bootstrap.spring; 17 | 18 | import org.springframework.beans.factory.config.BeanPostProcessor; 19 | import org.springframework.context.ApplicationContext; 20 | 21 | public abstract class SpringUtils 22 | { 23 | private static ApplicationContext ctx; 24 | 25 | public static BeanPostProcessor createInitBean( ApplicationContext ctx ) 26 | { 27 | init( ctx ); 28 | AppProperties.updateResolver( ctx.getEnvironment() ); 29 | return new BeanPostProcessor() 30 | { 31 | }; 32 | } 33 | 34 | public static void init( ApplicationContext ctx ) 35 | { 36 | if ( ctx == null ) 37 | { 38 | throw new IllegalArgumentException( "Application context cannot be null" ); 39 | } 40 | else 41 | { 42 | SpringUtils.ctx = ctx; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.main.banner-mode=off 2 | 3 | management.health.probes.enabled=true 4 | 5 | logging.level.org.springframework.web=${RDAPBOOTSTRAP_LOG_LEVEL:INFO} 6 | logging.level.net.arin.rdap_bootstrap=${RDAPBOOTSTRAP_LOG_LEVEL:INFO} 7 | -------------------------------------------------------------------------------- /src/main/resources/as_bootstrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "RDAP bootstrap file for Autonomous System Number allocations", 3 | "publication": "2024-04-10T20:00:01Z", 4 | "services": [ 5 | [ 6 | [ 7 | "36864-37887", 8 | "327680-328703", 9 | "328704-329727" 10 | ], 11 | [ 12 | "https://rdap.afrinic.net/rdap/", 13 | "http://rdap.afrinic.net/rdap/" 14 | ] 15 | ], 16 | [ 17 | [ 18 | "4608-4865", 19 | "7467-7722", 20 | "9216-10239", 21 | "17408-18431", 22 | "23552-24575", 23 | "37888-38911", 24 | "45056-46079", 25 | "55296-56319", 26 | "58368-59391", 27 | "63488-63999", 28 | "64000-64098", 29 | "64297-64395", 30 | "131072-132095", 31 | "132096-133119", 32 | "133120-133631", 33 | "133632-134556", 34 | "134557-135580", 35 | "135581-136505", 36 | "136506-137529", 37 | "137530-138553", 38 | "138554-139577", 39 | "139578-140601", 40 | "140602-141625", 41 | "141626-142649", 42 | "142650-143673", 43 | "143674-144697", 44 | "144698-145721", 45 | "145722-146745", 46 | "146746-147769", 47 | "147770-148793", 48 | "148794-149817", 49 | "149818-150841", 50 | "150842-151865", 51 | "151866-152889", 52 | "152890-153913" 53 | ], 54 | [ 55 | "https://rdap.apnic.net/" 56 | ] 57 | ], 58 | [ 59 | [ 60 | "1-1876", 61 | "1902-2042", 62 | "2044-2046", 63 | "2048-2106", 64 | "2137-2584", 65 | "2615-2772", 66 | "2823-2829", 67 | "2880-3153", 68 | "3354-4607", 69 | "4866-5376", 70 | "5632-6655", 71 | "6912-7466", 72 | "7723-8191", 73 | "10240-12287", 74 | "13312-15359", 75 | "16384-17407", 76 | "18432-20479", 77 | "21504-23455", 78 | "23457-23551", 79 | "25600-26623", 80 | "26624-27647", 81 | "29696-30719", 82 | "31744-32767", 83 | "32768-33791", 84 | "35840-36863", 85 | "39936-40959", 86 | "46080-47103", 87 | "53248-54271", 88 | "54272-55295", 89 | "62464-63487", 90 | "64198-64296", 91 | "393216-394239", 92 | "394240-395164", 93 | "395165-396188", 94 | "396189-397212", 95 | "397213-398236", 96 | "398237-399260", 97 | "399261-400284", 98 | "400285-401308", 99 | "401309-402332" 100 | ], 101 | [ 102 | "https://rdap.arin.net/registry/", 103 | "http://rdap.arin.net/registry/" 104 | ] 105 | ], 106 | [ 107 | [ 108 | "1877-1901", 109 | "2043", 110 | "2047", 111 | "2107-2136", 112 | "2585-2614", 113 | "2773-2822", 114 | "2830-2879", 115 | "3154-3353", 116 | "5377-5631", 117 | "6656-6911", 118 | "8192-9215", 119 | "12288-13311", 120 | "15360-16383", 121 | "20480-21503", 122 | "24576-25599", 123 | "28672-29695", 124 | "30720-31743", 125 | "33792-34815", 126 | "34816-35839", 127 | "38912-39935", 128 | "40960-41983", 129 | "41984-43007", 130 | "43008-44031", 131 | "44032-45055", 132 | "47104-48127", 133 | "48128-49151", 134 | "49152-50175", 135 | "50176-51199", 136 | "51200-52223", 137 | "56320-57343", 138 | "57344-58367", 139 | "59392-60415", 140 | "60416-61439", 141 | "61952-62463", 142 | "64396-64495", 143 | "196608-197631", 144 | "197632-198655", 145 | "198656-199679", 146 | "199680-200191", 147 | "200192-201215", 148 | "201216-202239", 149 | "202240-203263", 150 | "203264-204287", 151 | "204288-205211", 152 | "205212-206235", 153 | "206236-207259", 154 | "207260-208283", 155 | "208284-209307", 156 | "209308-210331", 157 | "210332-211355", 158 | "211356-212379", 159 | "212380-213403", 160 | "213404-214427", 161 | "214428-215451", 162 | "215452-216475" 163 | ], 164 | [ 165 | "https://rdap.db.ripe.net/" 166 | ] 167 | ], 168 | [ 169 | [ 170 | "27648-28671", 171 | "52224-53247", 172 | "61440-61951", 173 | "64099-64197", 174 | "262144-263167", 175 | "263168-263679", 176 | "263680-264604", 177 | "264605-265628", 178 | "265629-266652", 179 | "266653-267676", 180 | "267677-268700", 181 | "268701-269724", 182 | "269725-270748", 183 | "270749-271772", 184 | "271773-272796", 185 | "272797-273820", 186 | "273821-274844" 187 | ], 188 | [ 189 | "https://rdap.lacnic.net/rdap/" 190 | ] 191 | ] 192 | ], 193 | "version": "1.0" 194 | } -------------------------------------------------------------------------------- /src/main/resources/default_bootstrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "publication": "2024-07-02T12:15:00-0400", 4 | "services": [ 5 | [ 6 | [ 7 | "ip", 8 | "autnum", 9 | "domain", 10 | "nameserver", 11 | "entity" 12 | ], 13 | [ 14 | "https://rdap.arin.net/registry/", 15 | "http://rdap.arin.net/registry/" 16 | ] 17 | ] 18 | ] 19 | } -------------------------------------------------------------------------------- /src/main/resources/entity_bootstrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "publication": "2014-09-09T15:39:03-0400", 4 | "services": [ 5 | [ 6 | [ 7 | "ARIN" 8 | ], 9 | [ 10 | "https://rdap.arin.net/registry", 11 | "http://rdap.arin.net/registry" 12 | ] 13 | ], 14 | [ 15 | [ 16 | "AP" 17 | ], 18 | [ 19 | "https://rdap.apnic.net/" 20 | ] 21 | ], 22 | [ 23 | [ 24 | "RIPE" 25 | ], 26 | [ 27 | "https://rdap.db.ripe.net/", 28 | "http://rdap.db.ripe.net/" 29 | ] 30 | ], 31 | [ 32 | [ 33 | "LACNIC" 34 | ], 35 | [ 36 | "https://rdap.lacnic.net/rdap/" 37 | ] 38 | ] 39 | ] 40 | } -------------------------------------------------------------------------------- /src/main/resources/rdap_bootstrap.properties: -------------------------------------------------------------------------------- 1 | arin.rdapbootstrap.match_scheme_on_redirect=${RDAPBOOTSTRAP_MATCH_SCHEME_ON_REDIRECT:false} 2 | arin.rdapbootstrap.download_bootstrap_files=${RDAPBOOTSTRAP_DOWNLOAD_BOOTSTRAP_FILES:false} 3 | arin.rdapbootstrap.download_asn_file_url=${RDAPBOOTSTRAP_DOWNLOAD_ASN_FILE_URL:https://data.iana.org/rdap/asn.json} 4 | arin.rdapbootstrap.download_domain_file_url=${RDAPBOOTSTRAP_DOWNLOAD_DOMAIN_FILE_URL:https://data.iana.org/rdap/dns.json} 5 | arin.rdapbootstrap.download_ipv4_file_url=${RDAPBOOTSTRAP_DOWNLOAD_IPV4_FILE_URL:https://data.iana.org/rdap/ipv4.json} 6 | arin.rdapbootstrap.download_ipv6_file_url=${RDAPBOOTSTRAP_DOWNLOAD_IPV6_FILE_URL:https://data.iana.org/rdap/ipv6.json} 7 | arin.rdapbootstrap.download_directory=${RDAPBOOTSTRAP_DOWNLOAD_DIRECTORY:} 8 | arin.rdapbootstrap.download_interval=${RDAPBOOTSTRAP_DOWNLOAD_INTERVAL:86400} 9 | arin.rdapbootstrap.bootfile.default_bootstrap=${RDAPBOOTSTRAP_BOOTFILE_DEFAULT_BOOTSTRAP:} 10 | arin.rdapbootstrap.bootfile.domain_bootstrap=${RDAPBOOTSTRAP_BOOTFILE_DOMAIN_BOOTSTRAP:} 11 | arin.rdapbootstrap.bootfile.v4_bootstrap=${RDAPBOOTSTRAP_BOOTFILE_V4_BOOTSTRAP:} 12 | arin.rdapbootstrap.bootfile.v6_bootstrap=${RDAPBOOTSTRAP_BOOTFILE_V6_BOOTSTRAP:} 13 | arin.rdapbootstrap.bootfile.as_bootstrap=${RDAPBOOTSTRAP_BOOTFILE_AS_BOOTSTRAP:} 14 | arin.rdapbootstrap.bootfile.entity_bootstrap=${RDAPBOOTSTRAP_BOOTFILE_ENTITY_BOOTSTRAP:} 15 | -------------------------------------------------------------------------------- /src/main/resources/resource_files.properties: -------------------------------------------------------------------------------- 1 | default_bootstrap=/default_bootstrap.json 2 | domain_bootstrap=/domain_bootstrap.json 3 | v4_bootstrap=/v4_bootstrap.json 4 | v6_bootstrap=/v6_bootstrap.json 5 | as_bootstrap=/as_bootstrap.json 6 | entity_bootstrap=/entity_bootstrap.json 7 | -------------------------------------------------------------------------------- /src/main/resources/v4_bootstrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "RDAP bootstrap file for IPv4 address allocations", 3 | "publication": "2019-06-07T19:00:02Z", 4 | "services": [ 5 | [ 6 | [ 7 | "41.0.0.0/8", 8 | "102.0.0.0/8", 9 | "105.0.0.0/8", 10 | "154.0.0.0/8", 11 | "196.0.0.0/8", 12 | "197.0.0.0/8" 13 | ], 14 | [ 15 | "https://rdap.afrinic.net/rdap/", 16 | "http://rdap.afrinic.net/rdap/" 17 | ] 18 | ], 19 | [ 20 | [ 21 | "1.0.0.0/8", 22 | "14.0.0.0/8", 23 | "27.0.0.0/8", 24 | "36.0.0.0/8", 25 | "39.0.0.0/8", 26 | "42.0.0.0/8", 27 | "43.0.0.0/8", 28 | "49.0.0.0/8", 29 | "58.0.0.0/8", 30 | "59.0.0.0/8", 31 | "60.0.0.0/8", 32 | "61.0.0.0/8", 33 | "101.0.0.0/8", 34 | "103.0.0.0/8", 35 | "106.0.0.0/8", 36 | "110.0.0.0/8", 37 | "111.0.0.0/8", 38 | "112.0.0.0/8", 39 | "113.0.0.0/8", 40 | "114.0.0.0/8", 41 | "115.0.0.0/8", 42 | "116.0.0.0/8", 43 | "117.0.0.0/8", 44 | "118.0.0.0/8", 45 | "119.0.0.0/8", 46 | "120.0.0.0/8", 47 | "121.0.0.0/8", 48 | "122.0.0.0/8", 49 | "123.0.0.0/8", 50 | "124.0.0.0/8", 51 | "125.0.0.0/8", 52 | "126.0.0.0/8", 53 | "133.0.0.0/8", 54 | "150.0.0.0/8", 55 | "153.0.0.0/8", 56 | "163.0.0.0/8", 57 | "171.0.0.0/8", 58 | "175.0.0.0/8", 59 | "180.0.0.0/8", 60 | "182.0.0.0/8", 61 | "183.0.0.0/8", 62 | "202.0.0.0/8", 63 | "203.0.0.0/8", 64 | "210.0.0.0/8", 65 | "211.0.0.0/8", 66 | "218.0.0.0/8", 67 | "219.0.0.0/8", 68 | "220.0.0.0/8", 69 | "221.0.0.0/8", 70 | "222.0.0.0/8", 71 | "223.0.0.0/8" 72 | ], 73 | [ 74 | "https://rdap.apnic.net/" 75 | ] 76 | ], 77 | [ 78 | [ 79 | "3.0.0.0/8", 80 | "4.0.0.0/8", 81 | "6.0.0.0/8", 82 | "7.0.0.0/8", 83 | "8.0.0.0/8", 84 | "9.0.0.0/8", 85 | "11.0.0.0/8", 86 | "12.0.0.0/8", 87 | "13.0.0.0/8", 88 | "15.0.0.0/8", 89 | "16.0.0.0/8", 90 | "17.0.0.0/8", 91 | "18.0.0.0/8", 92 | "19.0.0.0/8", 93 | "20.0.0.0/8", 94 | "21.0.0.0/8", 95 | "22.0.0.0/8", 96 | "23.0.0.0/8", 97 | "24.0.0.0/8", 98 | "26.0.0.0/8", 99 | "28.0.0.0/8", 100 | "29.0.0.0/8", 101 | "30.0.0.0/8", 102 | "32.0.0.0/8", 103 | "33.0.0.0/8", 104 | "34.0.0.0/8", 105 | "35.0.0.0/8", 106 | "38.0.0.0/8", 107 | "40.0.0.0/8", 108 | "44.0.0.0/8", 109 | "45.0.0.0/8", 110 | "47.0.0.0/8", 111 | "48.0.0.0/8", 112 | "50.0.0.0/8", 113 | "52.0.0.0/8", 114 | "54.0.0.0/8", 115 | "55.0.0.0/8", 116 | "56.0.0.0/8", 117 | "63.0.0.0/8", 118 | "64.0.0.0/8", 119 | "65.0.0.0/8", 120 | "66.0.0.0/8", 121 | "67.0.0.0/8", 122 | "68.0.0.0/8", 123 | "69.0.0.0/8", 124 | "70.0.0.0/8", 125 | "71.0.0.0/8", 126 | "72.0.0.0/8", 127 | "73.0.0.0/8", 128 | "74.0.0.0/8", 129 | "75.0.0.0/8", 130 | "76.0.0.0/8", 131 | "96.0.0.0/8", 132 | "97.0.0.0/8", 133 | "98.0.0.0/8", 134 | "99.0.0.0/8", 135 | "100.0.0.0/8", 136 | "104.0.0.0/8", 137 | "107.0.0.0/8", 138 | "108.0.0.0/8", 139 | "128.0.0.0/8", 140 | "129.0.0.0/8", 141 | "130.0.0.0/8", 142 | "131.0.0.0/8", 143 | "132.0.0.0/8", 144 | "134.0.0.0/8", 145 | "135.0.0.0/8", 146 | "136.0.0.0/8", 147 | "137.0.0.0/8", 148 | "138.0.0.0/8", 149 | "139.0.0.0/8", 150 | "140.0.0.0/8", 151 | "142.0.0.0/8", 152 | "143.0.0.0/8", 153 | "144.0.0.0/8", 154 | "146.0.0.0/8", 155 | "147.0.0.0/8", 156 | "148.0.0.0/8", 157 | "149.0.0.0/8", 158 | "152.0.0.0/8", 159 | "155.0.0.0/8", 160 | "156.0.0.0/8", 161 | "157.0.0.0/8", 162 | "158.0.0.0/8", 163 | "159.0.0.0/8", 164 | "160.0.0.0/8", 165 | "161.0.0.0/8", 166 | "162.0.0.0/8", 167 | "164.0.0.0/8", 168 | "165.0.0.0/8", 169 | "166.0.0.0/8", 170 | "167.0.0.0/8", 171 | "168.0.0.0/8", 172 | "169.0.0.0/8", 173 | "170.0.0.0/8", 174 | "172.0.0.0/8", 175 | "173.0.0.0/8", 176 | "174.0.0.0/8", 177 | "184.0.0.0/8", 178 | "192.0.0.0/8", 179 | "198.0.0.0/8", 180 | "199.0.0.0/8", 181 | "204.0.0.0/8", 182 | "205.0.0.0/8", 183 | "206.0.0.0/8", 184 | "207.0.0.0/8", 185 | "208.0.0.0/8", 186 | "209.0.0.0/8", 187 | "214.0.0.0/8", 188 | "215.0.0.0/8", 189 | "216.0.0.0/8" 190 | ], 191 | [ 192 | "https://rdap.arin.net/registry/", 193 | "http://rdap.arin.net/registry/" 194 | ] 195 | ], 196 | [ 197 | [ 198 | "2.0.0.0/8", 199 | "5.0.0.0/8", 200 | "25.0.0.0/8", 201 | "31.0.0.0/8", 202 | "37.0.0.0/8", 203 | "46.0.0.0/8", 204 | "51.0.0.0/8", 205 | "53.0.0.0/8", 206 | "57.0.0.0/8", 207 | "62.0.0.0/8", 208 | "77.0.0.0/8", 209 | "78.0.0.0/8", 210 | "79.0.0.0/8", 211 | "80.0.0.0/8", 212 | "81.0.0.0/8", 213 | "82.0.0.0/8", 214 | "83.0.0.0/8", 215 | "84.0.0.0/8", 216 | "85.0.0.0/8", 217 | "86.0.0.0/8", 218 | "87.0.0.0/8", 219 | "88.0.0.0/8", 220 | "89.0.0.0/8", 221 | "90.0.0.0/8", 222 | "91.0.0.0/8", 223 | "92.0.0.0/8", 224 | "93.0.0.0/8", 225 | "94.0.0.0/8", 226 | "95.0.0.0/8", 227 | "109.0.0.0/8", 228 | "141.0.0.0/8", 229 | "145.0.0.0/8", 230 | "151.0.0.0/8", 231 | "176.0.0.0/8", 232 | "178.0.0.0/8", 233 | "185.0.0.0/8", 234 | "188.0.0.0/8", 235 | "193.0.0.0/8", 236 | "194.0.0.0/8", 237 | "195.0.0.0/8", 238 | "212.0.0.0/8", 239 | "213.0.0.0/8", 240 | "217.0.0.0/8" 241 | ], 242 | [ 243 | "https://rdap.db.ripe.net/" 244 | ] 245 | ], 246 | [ 247 | [ 248 | "177.0.0.0/8", 249 | "179.0.0.0/8", 250 | "181.0.0.0/8", 251 | "186.0.0.0/8", 252 | "187.0.0.0/8", 253 | "189.0.0.0/8", 254 | "190.0.0.0/8", 255 | "191.0.0.0/8", 256 | "200.0.0.0/8", 257 | "201.0.0.0/8" 258 | ], 259 | [ 260 | "https://rdap.lacnic.net/rdap/" 261 | ] 262 | ] 263 | ], 264 | "version": "1.0" 265 | } -------------------------------------------------------------------------------- /src/main/resources/v6_bootstrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "RDAP bootstrap file for IPv6 address allocations", 3 | "publication": "2019-11-06T19:00:04Z", 4 | "services": [ 5 | [ 6 | [ 7 | "2001:4200::/23", 8 | "2c00::/12" 9 | ], 10 | [ 11 | "https://rdap.afrinic.net/rdap/", 12 | "http://rdap.afrinic.net/rdap/" 13 | ] 14 | ], 15 | [ 16 | [ 17 | "2001:200::/23", 18 | "2001:4400::/23", 19 | "2001:8000::/19", 20 | "2001:a000::/20", 21 | "2001:b000::/20", 22 | "2001:c00::/23", 23 | "2001:e00::/23", 24 | "2400::/12" 25 | ], 26 | [ 27 | "https://rdap.apnic.net/" 28 | ] 29 | ], 30 | [ 31 | [ 32 | "2001:1800::/23", 33 | "2001:400::/23", 34 | "2001:4800::/23", 35 | "2600::/12", 36 | "2610::/23", 37 | "2620::/23", 38 | "2630::/12" 39 | ], 40 | [ 41 | "https://rdap.arin.net/registry/", 42 | "http://rdap.arin.net/registry/" 43 | ] 44 | ], 45 | [ 46 | [ 47 | "2001:1400::/22", 48 | "2001:1a00::/23", 49 | "2001:1c00::/22", 50 | "2001:2000::/19", 51 | "2001:4000::/23", 52 | "2001:4600::/23", 53 | "2001:4a00::/23", 54 | "2001:4c00::/23", 55 | "2001:5000::/20", 56 | "2001:600::/23", 57 | "2001:800::/22", 58 | "2003::/18", 59 | "2a00::/12", 60 | "2a10::/12" 61 | ], 62 | [ 63 | "https://rdap.db.ripe.net/" 64 | ] 65 | ], 66 | [ 67 | [ 68 | "2001:1200::/23", 69 | "2800::/12" 70 | ], 71 | [ 72 | "https://rdap.lacnic.net/rdap/" 73 | ] 74 | ] 75 | ], 76 | "version": "1.0" 77 | } -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jboss-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | rdapbootstrap 6 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | RDAPRoot 8 | RDAP Root Redirector 9 | A servlet that redirects to other RDAP servers 10 | net.arin.rdap_bootstrap.service.RedirectServlet 11 | 0 12 | 13 | 14 | RDAPRoot 15 | /* 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hello World!

4 | 5 | 6 | -------------------------------------------------------------------------------- /src/test/java/net/arin/rdap_bootstrap/service/AsBootstrapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import org.junit.Test; 20 | 21 | import static junit.framework.Assert.assertEquals; 22 | import static junit.framework.Assert.assertNull; 23 | import static net.arin.rdap_bootstrap.service.TestConstants.AFRINIC_HTTP; 24 | import static net.arin.rdap_bootstrap.service.TestConstants.AFRINIC_HTTPS; 25 | import static net.arin.rdap_bootstrap.service.TestConstants.APNIC_HTTPS; 26 | import static net.arin.rdap_bootstrap.service.TestConstants.ARIN_HTTP; 27 | import static net.arin.rdap_bootstrap.service.TestConstants.ARIN_HTTPS; 28 | import static net.arin.rdap_bootstrap.service.TestConstants.LACNIC_HTTPS; 29 | import static net.arin.rdap_bootstrap.service.TestConstants.RIPE_HTTPS; 30 | 31 | public class AsBootstrapTest 32 | { 33 | @Test 34 | public void testBootstrap() throws Exception 35 | { 36 | AsBootstrap asBootstrap = new AsBootstrap(); 37 | asBootstrap.loadData( new ResourceFiles() ); 38 | 39 | assertEquals( AFRINIC_HTTP, asBootstrap.getServiceUrls( "36864" ).getHttpUrl() ); 40 | assertEquals( AFRINIC_HTTPS, asBootstrap.getServiceUrls( "329727" ).getHttpsUrl() ); 41 | 42 | assertNull( asBootstrap.getServiceUrls( "4608" ).getHttpUrl() ); 43 | assertEquals( APNIC_HTTPS, asBootstrap.getServiceUrls( "4608" ).getHttpsUrl() ); 44 | assertEquals( APNIC_HTTPS, asBootstrap.getServiceUrls( "140603" ).getHttpsUrl() ); 45 | 46 | assertEquals( ARIN_HTTP, asBootstrap.getServiceUrls( "1" ).getHttpUrl() ); 47 | assertEquals( ARIN_HTTPS, asBootstrap.getServiceUrls( "399259" ).getHttpsUrl() ); 48 | 49 | assertNull( asBootstrap.getServiceUrls( "27648" ).getHttpUrl() ); 50 | assertEquals( LACNIC_HTTPS, asBootstrap.getServiceUrls( "27648" ).getHttpsUrl() ); 51 | assertEquals( LACNIC_HTTPS, asBootstrap.getServiceUrls( "271774" ).getHttpsUrl() ); 52 | 53 | assertNull( asBootstrap.getServiceUrls( "1877" ).getHttpUrl() ); 54 | assertEquals( RIPE_HTTPS, asBootstrap.getServiceUrls( "1877" ).getHttpsUrl() ); 55 | assertEquals( RIPE_HTTPS, asBootstrap.getServiceUrls( "213403" ).getHttpsUrl() ); 56 | 57 | assertNull( asBootstrap.getServiceUrls( "4294967294" ) ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/net/arin/rdap_bootstrap/service/DefaultBootstrapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2024 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import net.arin.rdap_bootstrap.service.DefaultBootstrap.Type; 20 | import org.junit.Test; 21 | 22 | import static junit.framework.Assert.assertEquals; 23 | import static net.arin.rdap_bootstrap.service.TestConstants.ARIN_HTTP; 24 | import static net.arin.rdap_bootstrap.service.TestConstants.ARIN_HTTPS; 25 | 26 | public class DefaultBootstrapTest 27 | { 28 | @Test 29 | public void testAllocations() throws Exception 30 | { 31 | DefaultBootstrap d = new DefaultBootstrap(); 32 | d.loadData( new ResourceFiles() ); 33 | 34 | assertEquals( ARIN_HTTP, d.getServiceUrls( Type.AUTNUM ).getHttpUrl() ); 35 | assertEquals( ARIN_HTTPS, d.getServiceUrls( Type.DOMAIN ).getHttpsUrl() ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/net/arin/rdap_bootstrap/service/DomainBootstrapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import org.junit.Test; 20 | 21 | import static junit.framework.Assert.assertEquals; 22 | import static net.arin.rdap_bootstrap.service.TestConstants.GOOGLE_HTTPS; 23 | import static net.arin.rdap_bootstrap.service.TestConstants.INFO_HTTPS; 24 | 25 | public class DomainBootstrapTest 26 | { 27 | @Test 28 | public void testAllocations() throws Exception 29 | { 30 | DomainBootstrap domain = new DomainBootstrap(); 31 | domain.loadData( new ResourceFiles() ); 32 | 33 | assertEquals( GOOGLE_HTTPS, domain.getServiceUrls( "xn--flw351e" ).getHttpsUrl() ); 34 | 35 | assertEquals( INFO_HTTPS, domain.getServiceUrls( "foo.info" ).getHttpsUrl() ); 36 | assertEquals( INFO_HTTPS, domain.getServiceUrls( "info" ).getHttpsUrl() ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/net/arin/rdap_bootstrap/service/IpV4BootstrapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import static junit.framework.Assert.assertEquals; 20 | import static net.arin.rdap_bootstrap.service.TestConstants.AFRINIC_HTTP; 21 | import static net.arin.rdap_bootstrap.service.TestConstants.APNIC_HTTPS; 22 | import static net.arin.rdap_bootstrap.service.TestConstants.ARIN_HTTP; 23 | import static net.arin.rdap_bootstrap.service.TestConstants.LACNIC_HTTPS; 24 | import static net.arin.rdap_bootstrap.service.TestConstants.RIPE_HTTPS; 25 | 26 | import org.junit.Test; 27 | 28 | public class IpV4BootstrapTest 29 | { 30 | @Test 31 | public void testAllocations() throws Exception 32 | { 33 | IpV4Bootstrap v4 = new IpV4Bootstrap(); 34 | v4.loadData( new ResourceFiles() ); 35 | 36 | // Test prefixes. 37 | 38 | assertEquals( AFRINIC_HTTP, v4.getServiceUrls( "41" ).getHttpUrl() ); 39 | 40 | assertEquals( APNIC_HTTPS, v4.getServiceUrls( "1" ).getHttpsUrl() ); 41 | assertEquals( APNIC_HTTPS, v4.getServiceUrls( "27" ).getHttpsUrl() ); 42 | 43 | assertEquals( LACNIC_HTTPS, v4.getServiceUrls( "177" ).getHttpsUrl() ); 44 | assertEquals( LACNIC_HTTPS, v4.getServiceUrls( "191" ).getHttpsUrl() ); 45 | 46 | assertEquals( RIPE_HTTPS, v4.getServiceUrls( "31" ).getHttpsUrl() ); 47 | assertEquals( RIPE_HTTPS, v4.getServiceUrls( "188" ).getHttpsUrl() ); 48 | 49 | // Test full prefixes. 50 | 51 | assertEquals( ARIN_HTTP, v4.getServiceUrls( "216.0.0.0/8" ).getHttpUrl() ); 52 | 53 | assertEquals( LACNIC_HTTPS, v4.getServiceUrls( "177.0.0.0/8" ).getHttpsUrl() ); 54 | assertEquals( LACNIC_HTTPS, v4.getServiceUrls( "177.0.0.1/32" ).getHttpsUrl() ); 55 | assertEquals( LACNIC_HTTPS, v4.getServiceUrls( "177.0.0.1" ).getHttpsUrl() ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/net/arin/rdap_bootstrap/service/IpV6BootstrapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import com.googlecode.ipv6.IPv6Address; 20 | import com.googlecode.ipv6.IPv6Network; 21 | import org.junit.Test; 22 | 23 | import static junit.framework.Assert.assertEquals; 24 | import static net.arin.rdap_bootstrap.service.TestConstants.AFRINIC_HTTP; 25 | import static net.arin.rdap_bootstrap.service.TestConstants.APNIC_HTTPS; 26 | import static net.arin.rdap_bootstrap.service.TestConstants.ARIN_HTTP; 27 | import static net.arin.rdap_bootstrap.service.TestConstants.LACNIC_HTTPS; 28 | import static net.arin.rdap_bootstrap.service.TestConstants.RIPE_HTTPS; 29 | import static org.junit.Assert.assertNull; 30 | 31 | public class IpV6BootstrapTest 32 | { 33 | @Test 34 | public void testAllocations() throws Exception 35 | { 36 | IpV6Bootstrap v6 = new IpV6Bootstrap(); 37 | v6.loadData( new ResourceFiles() ); 38 | 39 | assertEquals( AFRINIC_HTTP, v6.getServiceUrls( IPv6Network.fromString( "2c00:0000::/12" ) ).getHttpUrl() ); 40 | assertEquals( AFRINIC_HTTP, v6.getServiceUrls( IPv6Network.fromString( "2c00:0000::/13" ) ).getHttpUrl() ); 41 | assertNull( v6.getServiceUrls( IPv6Network.fromString( "3c00:0000::/12" ) ) ); 42 | 43 | assertEquals( APNIC_HTTPS, v6.getServiceUrls( IPv6Network.fromString( "2001:0200::/23" ) ).getHttpsUrl() ); 44 | 45 | assertEquals( ARIN_HTTP, v6.getServiceUrls( IPv6Address.fromString( "2620:0000:0000:0000:0000:0000:0000:0000" ) ).getHttpUrl() ); 46 | assertEquals( ARIN_HTTP, v6.getServiceUrls( IPv6Address.fromString( "2620:0000:0000:0000:0000:0000:0000:ffff" ) ).getHttpUrl() ); 47 | assertEquals( ARIN_HTTP, v6.getServiceUrls( IPv6Address.fromString( "2620:01ff:ffff:ffff:ffff:ffff:ffff:0000" ) ).getHttpUrl() ); 48 | assertEquals( ARIN_HTTP, v6.getServiceUrls( IPv6Address.fromString( "2620:01ff:ffff:ffff:ffff:ffff:ffff:ffff" ) ).getHttpUrl() ); 49 | 50 | assertEquals( LACNIC_HTTPS, v6.getServiceUrls( IPv6Address.fromString( "2800:0000:0000:0000:0000:0000:0000:0000" ) ).getHttpsUrl() ); 51 | assertEquals( LACNIC_HTTPS, v6.getServiceUrls( IPv6Address.fromString( "2800:0000:0000:0000:0000:0000:0000:ffff" ) ).getHttpsUrl() ); 52 | assertEquals( LACNIC_HTTPS, v6.getServiceUrls( IPv6Address.fromString( "280f:ffff:ffff:ffff:ffff:ffff:ffff:0000" ) ).getHttpsUrl() ); 53 | assertEquals( LACNIC_HTTPS, v6.getServiceUrls( IPv6Address.fromString( "280f:ffff:ffff:ffff:ffff:ffff:ffff:ffff" ) ).getHttpsUrl() ); 54 | assertEquals( LACNIC_HTTPS, v6.getServiceUrls( IPv6Network.fromString( "2800:0000::/12" ) ).getHttpsUrl() ); 55 | 56 | assertEquals( RIPE_HTTPS, v6.getServiceUrls( IPv6Address.fromString( "2a00:0000:0000:0000:0000:0000:0000:0000" ) ).getHttpsUrl() ); 57 | assertEquals( RIPE_HTTPS, v6.getServiceUrls( IPv6Address.fromString( "2a0f:ffff:ffff:ffff:ffff:ffff:ffff:ffff" ) ).getHttpsUrl() ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/net/arin/rdap_bootstrap/service/RedirectServletTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2020 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | import net.arin.rdap_bootstrap.Constants; 20 | import net.arin.rdap_bootstrap.service.JsonBootstrapFile.ServiceUrls; 21 | import org.junit.Test; 22 | 23 | import static junit.framework.Assert.assertEquals; 24 | import static net.arin.rdap_bootstrap.service.TestConstants.APNIC_HTTPS; 25 | import static net.arin.rdap_bootstrap.service.TestConstants.ARIN_HTTP; 26 | import static net.arin.rdap_bootstrap.service.TestConstants.EXAMPLE_HTTP; 27 | import static net.arin.rdap_bootstrap.service.TestConstants.EXAMPLE_HTTPS; 28 | import static net.arin.rdap_bootstrap.service.TestConstants.INFO_HTTPS; 29 | import static net.arin.rdap_bootstrap.service.TestConstants.LACNIC_HTTPS; 30 | import static net.arin.rdap_bootstrap.service.TestConstants.RIPE_HTTP; 31 | import static net.arin.rdap_bootstrap.service.TestConstants.RIPE_HTTPS; 32 | import static org.junit.Assert.assertNull; 33 | 34 | public class RedirectServletTest 35 | { 36 | @Test 37 | public void testGetRedirectUrlDefault() throws Exception 38 | { 39 | System.clearProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY ); 40 | 41 | ServiceUrls urls = new ServiceUrls(); 42 | urls.addUrl( EXAMPLE_HTTP ); 43 | urls.addUrl( EXAMPLE_HTTPS ); 44 | 45 | RedirectServlet servlet = new RedirectServlet(); 46 | servlet.init( null ); 47 | 48 | assertEquals( EXAMPLE_HTTPS + "/bar", servlet.getRedirectUrl( "http", "/bar", urls ) ); 49 | assertEquals( EXAMPLE_HTTPS + "/bar", servlet.getRedirectUrl( "https", "/bar", urls ) ); 50 | } 51 | 52 | @Test 53 | public void testGetRedirectUrlDefaultOnlyHttp() throws Exception 54 | { 55 | System.clearProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY ); 56 | 57 | ServiceUrls urls = new ServiceUrls(); 58 | urls.addUrl( EXAMPLE_HTTP ); 59 | 60 | RedirectServlet servlet = new RedirectServlet(); 61 | servlet.init( null ); 62 | 63 | assertEquals( EXAMPLE_HTTP + "/bar", servlet.getRedirectUrl( "http", "/bar", urls ) ); 64 | assertEquals( EXAMPLE_HTTP + "/bar", servlet.getRedirectUrl( "https", "/bar", urls ) ); 65 | } 66 | 67 | @Test 68 | public void testGetRedirectUrlDefaultOnlyHttps() throws Exception 69 | { 70 | System.clearProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY ); 71 | 72 | ServiceUrls urls = new ServiceUrls(); 73 | urls.addUrl( EXAMPLE_HTTPS ); 74 | 75 | RedirectServlet servlet = new RedirectServlet(); 76 | servlet.init( null ); 77 | 78 | assertEquals( EXAMPLE_HTTPS + "/bar", servlet.getRedirectUrl( "http", "/bar", urls ) ); 79 | assertEquals( EXAMPLE_HTTPS + "/bar", servlet.getRedirectUrl( "https", "/bar", urls ) ); 80 | } 81 | 82 | @Test 83 | public void testGetRedirectUrlFalse() throws Exception 84 | { 85 | System.clearProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY ); 86 | System.setProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY, "false" ); 87 | 88 | ServiceUrls urls = new ServiceUrls(); 89 | urls.addUrl( EXAMPLE_HTTP ); 90 | urls.addUrl( EXAMPLE_HTTPS ); 91 | 92 | RedirectServlet servlet = new RedirectServlet(); 93 | servlet.init( null ); 94 | 95 | assertEquals( EXAMPLE_HTTPS + "/bar", servlet.getRedirectUrl( "http", "/bar", urls ) ); 96 | assertEquals( EXAMPLE_HTTPS + "/bar", servlet.getRedirectUrl( "https", "/bar", urls ) ); 97 | 98 | System.clearProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY ); 99 | } 100 | 101 | @Test 102 | public void testGetRedirectUrlTrue() throws Exception 103 | { 104 | System.clearProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY ); 105 | System.setProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY, "true" ); 106 | 107 | ServiceUrls urls = new ServiceUrls(); 108 | urls.addUrl( EXAMPLE_HTTP ); 109 | urls.addUrl( EXAMPLE_HTTPS ); 110 | 111 | RedirectServlet servlet = new RedirectServlet(); 112 | servlet.init( null ); 113 | 114 | assertEquals( EXAMPLE_HTTP + "/bar", servlet.getRedirectUrl( "http", "/bar", urls ) ); 115 | assertEquals( EXAMPLE_HTTPS + "/bar", servlet.getRedirectUrl( "https", "/bar", urls ) ); 116 | 117 | System.clearProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY ); 118 | } 119 | 120 | @Test 121 | public void testGetRedirectUrlTrueOnlyHttp() throws Exception 122 | { 123 | System.clearProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY ); 124 | System.setProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY, "true" ); 125 | 126 | ServiceUrls urls = new ServiceUrls(); 127 | urls.addUrl( EXAMPLE_HTTP ); 128 | 129 | RedirectServlet servlet = new RedirectServlet(); 130 | servlet.init( null ); 131 | 132 | assertEquals( EXAMPLE_HTTP + "/bar", servlet.getRedirectUrl( "http", "/bar", urls ) ); 133 | assertEquals( EXAMPLE_HTTP + "/bar", servlet.getRedirectUrl( "https", "/bar", urls ) ); 134 | 135 | System.clearProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY ); 136 | } 137 | 138 | @Test 139 | public void testGetRedirectUrlTrueOnlyHttps() throws Exception 140 | { 141 | System.clearProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY ); 142 | System.setProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY, "true" ); 143 | 144 | ServiceUrls urls = new ServiceUrls(); 145 | urls.addUrl( EXAMPLE_HTTPS ); 146 | 147 | RedirectServlet servlet = new RedirectServlet(); 148 | servlet.init( null ); 149 | 150 | assertEquals( EXAMPLE_HTTPS + "/bar", servlet.getRedirectUrl( "http", "/bar", urls ) ); 151 | assertEquals( EXAMPLE_HTTPS + "/bar", servlet.getRedirectUrl( "https", "/bar", urls ) ); 152 | 153 | System.clearProperty( Constants.MATCH_SCHEME_ON_REDIRECT_PROPERTY ); 154 | } 155 | 156 | @Test 157 | public void testMakeAutNumInt() throws Exception 158 | { 159 | RedirectServlet servlet = new RedirectServlet(); 160 | servlet.init( null ); 161 | 162 | assertEquals( ARIN_HTTP, servlet.makeAutnumBase( "/autnum/10" ).getHttpUrl() ); 163 | 164 | assertEquals( RIPE_HTTPS, servlet.makeAutnumBase( "/autnum/42222" ).getHttpsUrl() ); 165 | } 166 | 167 | @Test 168 | public void testMakeIpBase() throws Exception 169 | { 170 | RedirectServlet servlet = new RedirectServlet(); 171 | servlet.init( null ); 172 | 173 | assertEquals( ARIN_HTTP, servlet.makeIpBase( "/ip/7.0.0.0/8" ).getHttpUrl() ); 174 | assertEquals( ARIN_HTTP, servlet.makeIpBase( "/ip/7.0.0.0/16" ).getHttpUrl() ); 175 | assertEquals( ARIN_HTTP, servlet.makeIpBase( "/ip/2620:0000:0000:0000:0000:0000:0000:0000" ).getHttpUrl() ); 176 | 177 | assertEquals( LACNIC_HTTPS, servlet.makeIpBase( "/ip/191.0.1.0/24" ).getHttpsUrl() ); 178 | assertEquals( LACNIC_HTTPS, servlet.makeIpBase( "/ip/2800:0000::/12" ).getHttpsUrl() ); 179 | assertEquals( LACNIC_HTTPS, servlet.makeIpBase( "/ip/191.0.1.1/32" ).getHttpsUrl() ); 180 | assertEquals( LACNIC_HTTPS, servlet.makeIpBase( "/ip/191.0.1.1" ).getHttpsUrl() ); 181 | } 182 | 183 | @Test 184 | public void testMakeDomainBase() throws Exception 185 | { 186 | RedirectServlet servlet = new RedirectServlet(); 187 | servlet.init( null ); 188 | 189 | assertEquals( ARIN_HTTP, servlet.makeDomainBase( "/domain/0.0.0.7.in-addr.arpa." ).getHttpUrl() ); 190 | assertEquals( ARIN_HTTP, servlet.makeDomainBase( "/domain/0.0.0.7.in-addr.arpa" ).getHttpUrl() ); 191 | assertEquals( ARIN_HTTP, servlet.makeDomainBase( "/domain/0.7.in-addr.arpa" ).getHttpUrl() ); 192 | assertEquals( ARIN_HTTP, servlet.makeDomainBase( "/domain/7.in-addr.arpa" ).getHttpUrl() ); 193 | assertEquals( ARIN_HTTP, servlet.makeDomainBase( "/domain/0.2.6.2.ip6.arpa" ).getHttpUrl() ); 194 | 195 | assertEquals( INFO_HTTPS, servlet.makeDomainBase( "/domain/example.INFO" ).getHttpsUrl() ); 196 | assertEquals( INFO_HTTPS, servlet.makeDomainBase( "/domain/example.INFO." ).getHttpsUrl() ); 197 | 198 | assertEquals( LACNIC_HTTPS, servlet.makeDomainBase( "/domain/0.0.8.2.ip6.arpa" ).getHttpsUrl() ); 199 | } 200 | 201 | @Test 202 | public void testMakeNameserverBase() throws Exception 203 | { 204 | RedirectServlet servlet = new RedirectServlet(); 205 | servlet.init( null ); 206 | 207 | assertEquals( INFO_HTTPS, servlet.makeNameserverBase( "/nameserver/ns1.example.INFO" ).getHttpsUrl() ); 208 | assertEquals( INFO_HTTPS, servlet.makeNameserverBase( "/nameserver/ns1.example.INFO." ).getHttpsUrl() ); 209 | 210 | assertNull( servlet.makeNameserverBase( "/nameserver/ns1.5.in-addr.arpa." ) ); 211 | } 212 | 213 | @Test 214 | public void testMakeEntityBase() throws Exception 215 | { 216 | RedirectServlet servlet = new RedirectServlet(); 217 | servlet.init( null ); 218 | 219 | assertEquals( APNIC_HTTPS, servlet.makeEntityBase( "/entity/ABC123-AP" ).getHttpsUrl() ); 220 | 221 | assertEquals( ARIN_HTTP, servlet.makeEntityBase( "/entity/ABC123-ARIN" ).getHttpUrl() ); 222 | 223 | assertEquals( LACNIC_HTTPS, servlet.makeEntityBase( "/entity/ABC123-LACNIC" ).getHttpsUrl() ); 224 | 225 | assertEquals( RIPE_HTTP, servlet.makeEntityBase( "/entity/ABC123-RIPE" ).getHttpUrl() ); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/test/java/net/arin/rdap_bootstrap/service/TestConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020-2024 American Registry for Internet Numbers (ARIN) 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 14 | * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | */ 17 | package net.arin.rdap_bootstrap.service; 18 | 19 | public class TestConstants 20 | { 21 | public final static String AFRINIC_HTTP = "http://rdap.afrinic.net/rdap"; 22 | public final static String AFRINIC_HTTPS = "https://rdap.afrinic.net/rdap"; 23 | 24 | public final static String APNIC_HTTPS = "https://rdap.apnic.net"; 25 | 26 | public final static String ARIN_HTTP = "http://rdap.arin.net/registry"; 27 | public final static String ARIN_HTTPS = "https://rdap.arin.net/registry"; 28 | 29 | public final static String GOOGLE_HTTPS = "https://www.registry.google/rdap"; 30 | 31 | public final static String LACNIC_HTTPS = "https://rdap.lacnic.net/rdap"; 32 | 33 | public final static String RIPE_HTTP = "http://rdap.db.ripe.net"; 34 | public final static String RIPE_HTTPS = "https://rdap.db.ripe.net"; 35 | 36 | public static final String INFO_HTTPS = "https://rdap.identitydigital.services/rdap"; 37 | 38 | public static final String EXAMPLE_HTTP = "http://example.com"; 39 | public static final String EXAMPLE_HTTPS = "https://example.com"; 40 | } 41 | --------------------------------------------------------------------------------