This repository has been archived on 2024-06-26. You can view files and clone it, but cannot push or open issues or pull requests.
sakura/app/Net.php

248 lines
6 KiB
PHP
Raw Permalink Normal View History

<?php
/**
* Holds the everything networking.
* @package Sakura
*/
namespace Sakura;
2016-09-11 14:25:22 +00:00
use Sakura\Exceptions\NetAddressTypeException;
use Sakura\Exceptions\NetInvalidAddressException;
/**
* Networking methods.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
2016-02-13 13:36:21 +00:00
class Net
{
/**
* Returns the connecting IP.
2016-08-05 02:35:37 +00:00
* @return string
*/
2016-12-04 16:33:52 +00:00
public static function ip(): string
{
return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '::1';
}
/**
* Detect the version of an IP.
2016-08-05 02:35:37 +00:00
* @param string $ipAddr
* @return int
*/
2016-12-04 16:33:52 +00:00
public static function detectIPVersion(string $ipAddr): int
{
// Check if var is IP
if (filter_var($ipAddr, FILTER_VALIDATE_IP)) {
// v4
if (filter_var($ipAddr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return 4;
}
// v6
if (filter_var($ipAddr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
return 6;
}
}
// Not an IP or unknown type
return 0;
}
/**
* Converts a printable IP address into an unpacked binary string.
2016-08-05 02:35:37 +00:00
* @param string $ip
2016-09-11 14:25:22 +00:00
* @throws NetInvalidAddressException
2016-08-05 02:35:37 +00:00
* @return string
*/
2016-12-04 16:33:52 +00:00
public static function pton(string $ip): string
{
// Detect the IP version
$ipv = self::detectIPVersion($ip);
// Check for IPv4 first since that's most common
if ($ipv === 4) {
return current(unpack("A4", inet_pton($ip)));
}
// Then attempt IPv6
if ($ipv === 6) {
return current(unpack("A16", inet_pton($ip)));
}
// Throw an exception if an invalid IP was supplied
2016-09-11 14:25:22 +00:00
throw new NetInvalidAddressException;
}
/**
* Converts a binary unpacked IP to a printable packed IP.
2016-08-05 02:35:37 +00:00
* @param string $bin
2016-09-11 14:25:22 +00:00
* @throws NetAddressTypeException
2016-08-05 02:35:37 +00:00
* @return string
*/
2016-12-04 16:33:52 +00:00
public static function ntop(string $bin): string
{
// Get the length of the binary string
$len = strlen($bin);
// Throw an exception if it's not 4 or 16 bytes
if ($len !== 4 && $len !== 16) {
2016-09-11 14:25:22 +00:00
throw new NetAddressTypeException;
}
// Finally pack the IP
return inet_ntop(pack("A{$len}", $bin));
}
2016-08-05 02:35:37 +00:00
/**
* Matches an IP to a CIDR range.
* @param string $ip
* @param string $range
* @return bool
*/
2016-12-04 16:33:52 +00:00
public static function matchCIDR(string $ip, string $range): bool
{
// Break the range up in parts
2016-12-09 21:51:40 +00:00
[$net, $mask] = explode('/', $range);
// Check IP version
$ipv = self::detectIPVersion($ip);
$rangev = self::detectIPVersion($net);
// Return false if it's not a valid IP
if ($ipv !== $rangev && !$ipv) {
return false;
}
// v4
if ($ipv === 4) {
return self::matchCIDRv4($ip, $net, $mask);
}
// v6
if ($ipv === 6) {
return self::matchCIDRv6($ip, $net, $mask);
}
// Return false by default
return false;
}
/**
2016-08-05 02:35:37 +00:00
* Match an IPv4 CIDR.
* @param string $ip
* @param string $net
* @param string $mask
* @return bool
*/
2016-12-04 16:33:52 +00:00
private static function matchCIDRv4(string $ip, string $net, string $mask): bool
{
// Convert IP and Net address to long
$ip = ip2long($ip);
$net = ip2long($net);
// Generate mask
$mask = -1 << (32 - $mask);
// Do the check
return ($ip & $mask) === $net;
}
/**
2016-08-05 02:35:37 +00:00
* Converts an IPv6 mask to a byte array.
* @param int $mask
* @return string
*/
2016-12-04 16:33:52 +00:00
private static function maskToByteArray(int $mask): string
{
// Generate an address from the mask
$addr = str_repeat("f", $mask / 4);
2016-03-27 21:18:57 +00:00
// Append uneven bit
switch ($mask % 4) {
case 1:
$addr .= '8';
break;
case 2:
$addr .= 'c';
break;
case 3:
$addr .= 'e';
break;
}
2016-03-27 21:18:57 +00:00
// Pad the address with zeroes
$addr = str_pad($addr, 32, '0');
2016-03-27 21:18:57 +00:00
// Pack the address
$addr = pack('H*', $addr);
// Return the packed address
return $addr;
}
/**
2016-08-05 02:35:37 +00:00
* Match an IPv6 CIDR.
* @param string $ip
* @param string $net
* @param int $mask
* @return bool
*/
2016-12-04 16:33:52 +00:00
private static function matchCIDRv6(string $ip, string $net, int $mask): bool
{
// Pack the IP and Net addresses
$ip = inet_pton($ip);
$net = inet_pton($net);
// Convert the mask to a byte array
$mask = self::maskToByteArray($mask);
// Compare them
return ($ip & $mask) === $net;
}
2016-02-28 17:45:25 +00:00
/**
2016-08-05 19:13:55 +00:00
* Make a web request.
2016-08-05 02:35:37 +00:00
* @param string $url
2016-08-05 19:13:55 +00:00
* @param string $method
2016-12-04 16:33:52 +00:00
* @param mixed $params Can be either an array or a string.
2016-08-05 19:13:55 +00:00
* @return string
2016-02-28 17:45:25 +00:00
*/
2016-12-04 16:33:52 +00:00
public static function request(string $url, string $method = 'GET', $params = null): string
2016-02-28 17:45:25 +00:00
{
$curl = curl_init();
2016-08-05 19:13:55 +00:00
curl_setopt_array($curl, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 2,
CURLOPT_TIMEOUT => 4,
CURLOPT_USERAGENT => 'Sakura/1.0 (+https://sakura.flash.moe)',
2016-12-10 22:36:24 +00:00
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => false, // for CF flexible SSL
2016-08-05 19:13:55 +00:00
]);
switch (strtolower($method)) {
2016-12-10 22:36:24 +00:00
case 'head':
curl_setopt_array($curl, [
CURLOPT_HEADER => true,
CURLOPT_NOBODY => true,
]);
break;
2016-08-05 19:13:55 +00:00
case 'post':
curl_setopt_array($curl, [
CURLOPT_POST => is_array($params) ? count($params) : 1,
CURLOPT_POSTFIELDS => is_array($params) ? http_build_query($params) : $params,
]);
break;
}
2016-02-28 17:45:25 +00:00
$curl = curl_exec($curl);
return $curl;
}
}