Skip to content

Commit 409c82b

Browse files
authored
Merge pull request #96 from phenixphp/feature/improve-ip-parser
Improve ip parser
2 parents 1395bae + 03a917f commit 409c82b

File tree

8 files changed

+341
-80
lines changed

8 files changed

+341
-80
lines changed

src/Auth/Middlewares/Authenticated.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use Phenix\Facades\Config;
1717
use Phenix\Facades\Event;
1818
use Phenix\Http\Constants\HttpStatus;
19-
use Phenix\Http\IpAddress;
19+
use Phenix\Http\Ip;
2020
use Phenix\Http\Request as HttpRequest;
2121

2222
class Authenticated implements Middleware
@@ -34,7 +34,7 @@ public function handleRequest(Request $request, RequestHandler $next): Response
3434
/** @var AuthenticationManager $auth */
3535
$auth = App::make(AuthenticationManager::class);
3636

37-
$clientIp = IpAddress::hash($request);
37+
$clientIp = Ip::make($request)->hash();
3838

3939
if (! $token || ! $auth->validate($token)) {
4040
Event::emitAsync(new FailedTokenValidation(

src/Auth/Middlewares/TokenRateLimit.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
use Phenix\Auth\AuthenticationManager;
1313
use Phenix\Facades\Config;
1414
use Phenix\Http\Constants\HttpStatus;
15-
use Phenix\Http\IpAddress;
15+
use Phenix\Http\Ip;
1616

1717
use function str_starts_with;
1818

@@ -29,7 +29,7 @@ public function handleRequest(Request $request, RequestHandler $next): Response
2929
/** @var AuthenticationManager $auth */
3030
$auth = App::make(AuthenticationManager::class);
3131

32-
$clientIp = IpAddress::hash($request);
32+
$clientIp = Ip::make($request)->hash();
3333

3434
$attemptLimit = (int) (Config::get('auth.tokens.rate_limit.attempts', 5));
3535
$windowSeconds = (int) (Config::get('auth.tokens.rate_limit.window', 300));

src/Cache/RateLimit/Middlewares/RateLimiter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
use Phenix\Cache\RateLimit\RateLimitManager;
1313
use Phenix\Facades\Config;
1414
use Phenix\Http\Constants\HttpStatus;
15-
use Phenix\Http\IpAddress;
15+
use Phenix\Http\Ip;
1616

1717
class RateLimiter implements Middleware
1818
{
@@ -29,7 +29,7 @@ public function handleRequest(Request $request, RequestHandler $next): Response
2929
return $next->handleRequest($request);
3030
}
3131

32-
$clientIp = IpAddress::hash($request);
32+
$clientIp = Ip::make($request)->hash();
3333
$current = $this->rateLimiter->increment($clientIp);
3434

3535
$perMinuteLimit = (int) Config::get('cache.rate_limit.per_minute', 60);

src/Http/Ip.php

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Phenix\Http;
6+
7+
use Amp\Http\Server\Request;
8+
9+
class Ip
10+
{
11+
protected string $address;
12+
13+
protected string $host;
14+
15+
protected int|null $port = null;
16+
17+
protected array $forwardingAddresses = [];
18+
19+
public function __construct(Request $request)
20+
{
21+
$this->address = $request->getClient()->getRemoteAddress()->toString();
22+
23+
if ($forwardingHeader = $request->getHeader('X-Forwarded-For')) {
24+
$parts = array_map(static fn ($v) => trim($v), explode(',', $forwardingHeader));
25+
$this->forwardingAddresses = $parts;
26+
}
27+
}
28+
29+
public static function make(Request $request): self
30+
{
31+
$ip = new self($request);
32+
$ip->parse();
33+
34+
return $ip;
35+
}
36+
37+
public function address(): string
38+
{
39+
return $this->address;
40+
}
41+
42+
public function host(): string
43+
{
44+
return $this->host;
45+
}
46+
47+
public function port(): int|null
48+
{
49+
return $this->port;
50+
}
51+
52+
public function isForwarded(): bool
53+
{
54+
return ! empty($this->forwardingAddresses);
55+
}
56+
57+
public function forwardingAddresses(): array
58+
{
59+
return $this->forwardingAddresses;
60+
}
61+
62+
public function hash(): string
63+
{
64+
return hash('sha256', $this->host);
65+
}
66+
67+
protected function parse(): void
68+
{
69+
$address = trim($this->address);
70+
71+
if (preg_match('/^\[(?<addr>[^\]]+)\](?::(?<port>\d+))?$/', $address, $m) === 1) {
72+
$this->host = $m['addr'];
73+
$this->port = isset($m['port']) ? (int) $m['port'] : null;
74+
75+
return;
76+
}
77+
78+
if (filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
79+
$this->host = $address;
80+
$this->port = null;
81+
82+
return;
83+
}
84+
85+
if (str_contains($address, ':')) {
86+
[$maybeHost, $maybePort] = explode(':', $address, 2);
87+
88+
if (
89+
filter_var($maybeHost, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ||
90+
filter_var($maybeHost, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)
91+
) {
92+
$this->host = $maybeHost;
93+
$this->port = is_numeric($maybePort) ? (int) $maybePort : null;
94+
95+
return;
96+
}
97+
}
98+
99+
$this->host = $address;
100+
$this->port = null;
101+
}
102+
}

src/Http/IpAddress.php

Lines changed: 0 additions & 66 deletions
This file was deleted.

src/Http/Request.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ public function session(string|null $key = null, array|string|int|null $default
147147
return $this->session;
148148
}
149149

150-
public function ip(): string|null
150+
public function ip(): Ip
151151
{
152-
return IpAddress::parse($this->request);
152+
return Ip::make($this->request);
153153
}
154154

155155
public function toArray(): array

0 commit comments

Comments
 (0)