Domain registry project http://reg.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

275 lines
7.2 KiB

<?php
namespace App;
class Utils {
// I2P uses custom base64 alphabet, defining translation here
private const b64Trans = array("-" => "+", "~" => "/");
// base32 alpabet
private const b32alphabet = 'abcdefghijklmnopqrstuvwxyz234567';
/**
* base32 encoding function.
* @param string $data
* @return string
*/
private static function b32encode(string $data): string
{
if (empty($data)) {
return "";
}
/* Create binary string zeropadded to eight bits. */
$data = str_split($data);
$binary = implode("", array_map(function ($character) {
return sprintf("%08b", ord($character));
}, $data));
/* Split to five bit chunks and make sure last chunk has five bits. */
$binary = str_split($binary, 5);
$last = array_pop($binary);
if (null !== $last) {
$binary[] = str_pad($last, 5, "0", STR_PAD_RIGHT);
}
/* Convert each five bits to Base32 character. */
$encoded = implode("", array_map(function ($fivebits) {
$index = bindec($fivebits);
return self::b32alphabet[$index];
}, $binary));
return $encoded;
}
/**
* I2P base64 decoding function.
* @param string $data
* @return string
*/
private static function b64decode(string $data): string
{
return base64_decode(strtr($data, static::b64Trans));
}
/**
* Checks if the given domain is in Punycode.
* @param string $domain The domain to check.
* @return bool Whether the domain is in Punycode.
*/
public static function isPunycodeDomain($domain): bool
{
$hasPunycode = false;
foreach (explode('.', $domain) as $part) {
if (false === static::isAscii($part))
return false;
if (static::isPunycode($part))
$hasPunycode = true;
}
return $hasPunycode;
}
/**
* Checks if the given value is in ASCII character encoding.
* @param string $value The value to check.
* @return bool Whether the value is in ASCII character encoding.
*/
public static function isAscii($value): bool
{
return ('ASCII' === mb_detect_encoding($value, 'ASCII', true));
}
/**
* Checks if the given value is in Punycode.
* @param string $value The value to check.
* @return bool Whether the value is in Punycode.
* @throws \LogicException If the string is not encoded by UTF-8.
*/
public static function isPunycode($value): bool
{
if (false === static::isAscii($value))
return false;
if ('UTF-8' !== mb_detect_encoding($value, 'UTF-8', true))
throw new \LogicException('The string should be encoded by UTF-8 to do the right check.');
return (0 === mb_stripos($value, 'xn--', 0, 'UTF-8'));
}
/**
* base64 validation function.
* @param string $data
* @return bool
*/
public static function isValidBase64(string $data): bool
{
$len = strlen($data);
if($len < 516 || $len > 616)
{
return false;
}
/* .i2p in string */
if(preg_match('/\.i2p/', $data))
{
return false;
}
/* DSA-SHA1. Will reject them because they are old (length 516) */
if($len == 516 && preg_match('/^[a-zA-Z0-9\-~]+AA$/', $data))
{
return false;
}
/* ECDSA or EdDSA */
if($len == 524 && !preg_match('/^[a-zA-Z0-9\-~]+AEAA[Ec]AAA==$/', $data))
{
return false;
}
return true;
}
/**
* I2P base64 to base32 conversion function.
* @param string $data
* @return string
*/
public static function b32from64(string $data): string
{
return self::b32encode(hash('sha256', self::b64decode($data), true));
}
/**
* Domain validation function for registration.
* @param string $data
* @return bool
*/
public static function isValidAddress(string $data, string &$result): bool
{
$len = strlen($data);
if($len < 5 || $len > 255) // rfc2181, section 11
{
$result = "Domain must be longer than 5 and lesser than 255 chars.";
return false;
}
if(preg_match('/\.b32\.i2p$/', $data))
{
$result = "Domain can't end with .b32.i2p.";
return false;
}
if(filter_var($data, FILTER_VALIDATE_DOMAIN) !== false && preg_match('/\.i2p$/', $data)
&& (static::isPunycodeDomain($data) || static::isAscii($data)))
{
return true;
}
else
{
$result = "Domain is not valid. Check if you domain label is lesser than 63 chars, and uses only ASCII or Punycode format.";
return false;
}
return false;
}
/**
* Domain validation function.
* @param string $data
* @return bool
*/
public static function isValidDomain(string $data, string &$result): bool
{
$len = strlen($data);
if($len < 5 || $len > 255) // rfc2181, section 11
{
$result = "Domain must be longer than 5 and lesser than 255 chars.";
return false;
}
if(preg_match('/\.b32\.i2p$/', $data))
{
$result = "Domain can't end with .b32.i2p.";
return false;
}
if(filter_var($data, FILTER_VALIDATE_DOMAIN) !== false && preg_match('/\.i2p$/', $data))
{
return true;
}
else
{
$result = "Inputed data or domain is not valid.";
return false;
}
return false;
}
public static function verifyHostRecord(string $data, &$output): bool
{
$retval = null;
$cmd = dirname(__FILE__) . "/../bin/verifyhost '" . $data . "'";
exec($cmd, $output, $retval);
if (!$retval)
return true;
return false;
}
public static function parseHostRecord(string $data): array
{
$record = [];
/* Return empty array if no data provided */
if(!strlen($data))
return $record;
$data = trim($data);
/* Split addressbook host record and extended commands */
$tmp = preg_split("/#!/", $data);
/* Parse host record */
$host = preg_split("/\=/", $tmp[0], 2);
$record["host"] = $host[0];
$record["b64"] = $host[1];
/* Parse extended commands */
if(isset($tmp[1]))
{
$cmds = [];
$cmdlist = preg_split("/#/", $tmp[1]);
foreach($cmdlist as $i)
{
list($name,$value) = explode('=', $i, 2);
$cmds[$name] = $value;
}
$record["commands"] = $cmds;
}
return $record;
}
public static function getResponseHeader($header, $response) {
foreach ($response as $key => $r) {
// Match the header name up to ':', compare lower case
if (stripos($r, $header . ':') === 0) {
list($headername, $headervalue) = explode(":", $r, 2);
return trim($headervalue);
}
}
}
}