PHP Classes

PHP RDAP Server: Process RDAP queries about an IP address or domain

Recommend this page to a friend!
  Info   Example   Demos   View files Files   Install with Composer Install with Composer   Download Download   Reputation   Support forum   Blog    
Last Updated Ratings Unique User Downloads Download Rankings
2024-11-08 (21 days ago) RSS 2.0 feedNot enough user ratingsTotal: 45 All time: 10,804 This week: 52Up
Version License PHP version Categories
rdap-server 1.0.1MIT/X Consortium ...7Networking, Searching, Web services, H..., P...
Description 

Authors

Hiqdev Team
Daniel Marschall
Till Wehowski


Contributor

This package can process RDAP queries about an IP address or domain.

It implements an RDAP Web server that can process queries about IP addresses and domains.

The server returns responses in JSON format according to the RDAP protocol specification.

This server supports falling back to Whois queries when performing an RDAP query about an IP address or domain is impossible.

Innovation Award
PHP Programming Innovation award nominee
December 2023
Number 2
RDAP is an Internet protocol that allows information about IP addresses and domains to be answered.

It is a successor of the Whois protocol. RDAP allows to perform queries using HTTP requests and get query responses in JSON format.

So, the RDAP server responses are more accessible to parse and process than the WHOIS server responses because, nowadays, it is easy to parse data in JSON format in PHP and many other languages.

This package implements an RDAP server in pure PHP code. It allows any PHP developer to implement the responses to queries about domains and IP addresses they can manage.

Manuel Lemos
Picture of Melanie Wehowski
  Performance   Level  
Innovation award
Innovation award
Nominee: 12x

Example

<?php
declare(strict_types=1);

namespace
Webfan\Webfat\Module\RdapServer;

use
App\Application\Handlers\HttpErrorHandler;
use
App\Application\Handlers\ShutdownHandler;
use
App\Application\ResponseEmitter\ResponseEmitter;
use
DI\ContainerBuilder;
use
Slim\Factory\AppFactory;
use
Slim\Factory\ServerRequestCreatorFactory;
use
frdl\security\floodprotection\FloodProtection;



/**
* @toDo: use libs
*/
class Helper
{
   
 public static
$protocoll = 'HTTP/1.1';

 public static function
sendCorsHeaders(array $allowedOrigins = null, $fallbackOrigin = 'https://invalid.dns.frdl.de') {
   
// CORS
    // Author: Till Wehowski
 
if(null === $allowedOrigins){
    
$allowedOrigins = [
      
"*",
      
$_SERVER['SERVER_NAME'],
      
$_SERVER['HTTP_ORIGIN'],
      
$_SERVER['HTTP_HOST']
     ];
  }
  
  
 
$originRequested = strip_tags(((isset($_SERVER['HTTP_ORIGIN'])) ? $_SERVER['HTTP_ORIGIN'] : "*"));
 
$origin = (in_array($originRequested, $allowedOrigins)) ? $originRequested : $fallbackOrigin;
  
   
header("Access-Control-Allow-Credentials: true");
   
header("Access-Control-Allow-Origin: ".$origin);

   
header("Access-Control-Allow-Headers: If-None-Match, X-Requested-With, Origin, X-Frdlweb-Bugs, Etag, X-Forgery-Protection-Token, X-CSRF-Token, X-Frdl-Request-Negotiation");

    if (isset(
$_SERVER['HTTP_ORIGIN'])) {
       
header('X-Frame-Options: ALLOW-FROM '.$origin);
    } else {
       
header_remove("X-Frame-Options");
    }
  
   
$expose = array('Etag', 'X-CSRF-Token');
    foreach (
headers_list() as $num => $header) {
       
$h = explode(':', $header);
       
$expose[] = trim($h[0]);
    }
   
header("Access-Control-Expose-Headers: ".implode(',',$expose));

   
header("Vary: Origin");
 }
 
  public static function
header($name, $value)
    {
       return
header($name.': '.$value);
    }



  public static function
status($code = 200)
    {
       if((int)
$code == 200)return header(self::$protocoll.' 200 Ok');
       if((int)
$code == 201)return header(self::$protocoll.' 201 Created');

       if((int)
$code == 400)return header(self::$protocoll.' 400 Bad Request');
       if((int)
$code == 401)return header(self::$protocoll.' 401 Unauthorized');
       if((int)
$code == 403)return header(self::$protocoll.' 403 Forbidden');
       if((int)
$code == 404)return header(self::$protocoll.' 404 Not Found');
       if((int)
$code == 409)return header(self::$protocoll.' 409 Conflict');
      
       if((int)
$code == 422)return header(self::$protocoll.' 422 Validation Failure');
      
       if((int)
$code == 429)return header(self::$protocoll.' 429 Too Many Requests');
      
       if((int)
$code == 455)return header(self::$protocoll.' 455 Blocked Due To Misbehavior');

      
       if((int)
$code == 500)return header(self::$protocoll.' 500 Internal Server Error');
       if((int)
$code == 501)return header(self::$protocoll.' 501 Not Implemented');
       if((int)
$code == 503)return header(self::$protocoll.' 503 Service Unavailable');
       if((int)
$code == 511)return header(self::$protocoll.' 511 Network Authentication Required');
     
     
       if(
0===intval($code)){
      return
header(self::$protocoll.' 501 Not Implemented');
       }
     
      \
trigger_error('status code '.intval($code).' not implemented in \'' . get_class($this) . '\' ' . __METHOD__. ' '.__LINE__, E_USER_ERROR);
    }
   
   
}

ini_set('display_errors', '0');
require_once
__DIR__ . '/../vendor/autoload.php';
//require_once __DIR__.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'dns.frdl.de'.\DIRECTORY_SEPARATOR.'vendor'.\DIRECTORY_SEPARATOR.'autoload.php';

//\Webfan\Psr4Loader\RemoteFromWebfan::getInstance('frdl.webfan.de', true, 'latest', false);

if(!is_dir(__DIR__ . '/../cache/flood-protection/')){
 
mkdir(__DIR__ . '/../cache/flood-protection/', 0777, true);
}

if(!
is_dir(__DIR__ . '/../cache/container/')){
 
mkdir(__DIR__ . '/../cache/container/', 0777, true);
}


// Instantiate PHP-DI ContainerBuilder
$containerBuilder = new ContainerBuilder();
 if(!
is_dir(__DIR__ . '/../cache/container')){
    
mkdir(__DIR__ . '/../cache/container', 0775, true);
 }
//if (false) { // Should be set to true in production
   
$containerBuilder->enableCompilation(__DIR__ . '/../cache/container');
//}

// Set up settings
$settings = require __DIR__ . '/../app/settings.php';
$settings($containerBuilder);

// Set up dependencies
$dependencies = require __DIR__ . '/../app/dependencies.php';
$dependencies($containerBuilder);

// Build PHP-DI Container instance
$container = $containerBuilder->build();





 
$FloodProtection = new FloodProtection('rdap', 30, 60, __DIR__ . '/../cache/flood-protection/');
 if(
$_SERVER['REMOTE_ADDR'] !== $_SERVER['SERVER_ADDR']
     && !
in_array($_SERVER['REMOTE_ADDR'], ['212.53.140.43', '212.72.182.211'])
    &&
$FloodProtection->check($_SERVER['REMOTE_ADDR'])
   ){
   
header("HTTP/1.1 429 Too Many Requests");
    exit(
"Too Many Requests, try again later!");
 }

/*
  @ToDO: Prune cache / dependencies loader...

        $ShutdownTasks = \frdlweb\Thread\ShutdownTasks::mutex();
        $ShutdownTasks(function($container, $FloodProtection, $tempRdapDir, $CacheDir, $maxCacheTime){
            @\ignore_user_abort(true);
            $container->get('rdap.cache')->prune();
            if(is_dir($tempRdapDir)){
            // \webfan\hps\patch\Fs::pruneDir($tempRdapDir, $maxCacheTime * 4, true, false);
            }
           
           
            $FloodProtection->prune();
            if(is_dir($CacheDir)){
              \webfan\hps\patch\Fs::pruneDir($CacheDir, $maxCacheTime, true, false);
            }
        }, $container, $FloodProtection, __DIR__.'/../cache/rdap/', __DIR__ . '/../cache/flood-protection/', 31 * 24 * 60 * 60);

*/


// Instantiate the app
AppFactory::setContainer($container);
$app = AppFactory::create();
$callableResolver = $app->getCallableResolver();

// Register routes
$routes = require __DIR__ . '/../app/routes.php';
$routes($app);

/** @var bool $displayErrorDetails */
$displayErrorDetails = $container->get('settings')['displayErrorDetails'];

// Create Request object from globals
$serverRequestCreator = ServerRequestCreatorFactory::create();



$request = $serverRequestCreator->createServerRequestFromGlobals();




// Create Error Handler
$responseFactory = $app->getResponseFactory();
$errorHandler = new HttpErrorHandler($callableResolver, $responseFactory);
$errorHandler->setDisplayErrorDetailsFlag($displayErrorDetails);

// Create Shutdown Handler
$shutdownHandler = new ShutdownHandler($request, $errorHandler, $displayErrorDetails);
register_shutdown_function($shutdownHandler);

// Add Routing Middleware
$app->addRoutingMiddleware();

// Add Error Middleware
$errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, false, false);
$errorMiddleware->setDefaultErrorHandler($errorHandler);
 
Helper::status(200);

    
Helper::sendCorsHeaders([(isset($_SERVER['HTTP_ORIGIN']))?$_SERVER['HTTP_ORIGIN']:'*'], '*');

// Run App & Emit Response
$response = @$app->handle($request);
Helper::status($response->getStatusCode());
$responseEmitter = new ResponseEmitter();
ob_start(function($c) use($response) {
 
Helper::sendCorsHeaders([(isset($_SERVER['HTTP_ORIGIN']))?$_SERVER['HTTP_ORIGIN']:'*'], '*');
  
Helper::status($response->getStatusCode());
    return
$c;
});
$responseEmitter->emit($response);



Details

RDAP server application example

This is a sample application uses the HiQDev RDAP library which packs domain info into PHP objects and can serialize it to JSON string.

This implementation fetches public WHOIS servers, parses the response by HiQDev RDAP-Whois proxy, fills RDAP objects and responds according to the RFC7483.

Install and run

The preferred way to install this project is through composer.

php composer.phar create-project "hiqdev/rdap-server-example:*" directory2install

Then run `php composer.phar update` to setup all dependencies

Setup project

You can run this application by two ways.

  1. With build-in PHP web server:

    php -S {host}:{port} -t public/ public/index.php

  2. With docker-compose:

    docker-compose up -d --build

Now you can check you app on `localhost:8080` or on your custom port and see

Hello world!
    

Usage

    

You can use this application to find domain names by URL

localhost:8080/domain/example.com

And see something like


  "ldhName": "example.com",
  "unicodeName": "example.com",
  "entities": [
    {
      "vcardArray": [
        [
            ...


  Frdlweb RDAP ServerExternal page  

Open in a separate window

  Files folder image Files (995)  
File Role Description
Files folder imageapp (3 files)
Files folder imagecache (3 directories)
Files folder imageclasses (5 files, 2 directories)
Files folder imagecss (1 file)
Files folder imageimg (13 files)
Files folder imagepublic (5 files, 1 directory)
Files folder imagesrc (1 directory)
Files folder imagetests (1 file)
Files folder imagevendor (1 file, 11 directories)
Plain text file autoloader.php Class Class source
Accessible without login Plain text file composer.json Data Auxiliary data
Accessible without login Plain text file composer.lock Data Auxiliary data
Accessible without login Plain text file docker-compose.yml Data Auxiliary data
Accessible without login Image file favicon.ico Data Auxiliary data
Accessible without login Plain text file hidev.yml Data Auxiliary data
Accessible without login HTML file index.html Doc. Documentation
Accessible without login Plain text file LICENSE Lic. License text
Accessible without login Plain text file phpunit.xml Data Auxiliary data
Accessible without login Plain text file README.md Doc. Documentation

The PHP Classes site has supported package installation using the Composer tool since 2013, as you may verify by reading this instructions page.
Install with Composer Install with Composer
 Version Control Unique User Downloads Download Rankings  
 100%
Total:45
This week:0
All time:10,804
This week:52Up