+2
-1
.gitignore
+2
-1
.gitignore
+1
-1
Dockerfile.deployment
+1
-1
Dockerfile.deployment
···
52
52
COPY . .
53
53
RUN mkdir -p bootstrap/cache storage/logs storage/framework/cache storage/framework/views storage/framework/sessions public/uploads public/uploads-avatar public/uploads-replay
54
54
RUN composer dump-autoload
55
-
RUN ./bin/update_ip2asn.sh
56
55
57
56
ARG APP_URL
58
57
ARG DOCS_URL
59
58
RUN yarn production
60
59
60
+
RUN php artisan ip2asn:update
61
61
RUN php artisan scribe:generate
62
62
63
63
RUN rm -rf node_modules
+20
app/Console/Commands/Ip2AsnUpdate.php
+20
app/Console/Commands/Ip2AsnUpdate.php
···
1
+
<?php
2
+
3
+
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4
+
// See the LICENCE file in the repository root for full licence text.
5
+
6
+
namespace App\Console\Commands;
7
+
8
+
use App\Libraries\Ip2AsnUpdater;
9
+
use Illuminate\Console\Command;
10
+
11
+
class Ip2AsnUpdate extends Command
12
+
{
13
+
protected $signature = 'ip2asn:update';
14
+
protected $description = 'Update or initialise ip2asn database';
15
+
16
+
public function handle()
17
+
{
18
+
(new Ip2AsnUpdater())->run();
19
+
}
20
+
}
+2
app/Console/Kernel.php
+2
app/Console/Kernel.php
+13
-23
app/Libraries/Ip2Asn.php
+13
-23
app/Libraries/Ip2Asn.php
···
11
11
12
12
class Ip2Asn
13
13
{
14
-
private array $db;
15
-
private $fh;
14
+
private $dbFh;
15
+
private string $index;
16
+
private int $count;
16
17
17
18
public function __construct()
18
19
{
19
-
$this->fh = fopen(database_path('ip2asn-combined.tsv'), 'r');
20
-
if ($this->fh === false) {
20
+
$this->dbFh = fopen(Ip2AsnUpdater::getDbPath(), 'r');
21
+
$this->index = file_get_contents(Ip2AsnUpdater::getIndexPath());
22
+
if ($this->dbFh === false || $this->index === false) {
21
23
throw new Exception('failed opening ip2asn database');
22
24
}
23
-
$this->db = $this->parseFileForDb();
25
+
26
+
// 4 bytes per entry (int32)
27
+
$this->count = strlen($this->index) / 4;
24
28
}
25
29
26
30
public function lookup(string $ip): string
27
31
{
28
32
$start = 0;
29
-
$end = count($this->db) - 1;
33
+
$end = $this->count - 1;
30
34
$search = inet_pton($this->prefixIPv4($ip));
31
35
32
36
if ($search === false) {
···
35
39
36
40
while ($start <= $end) {
37
41
$current = (int) (($start + $end) / 2);
38
-
$loc = $this->db[$current];
39
-
fseek($this->fh, $loc);
40
-
$row = fgets($this->fh);
42
+
$loc = unpack('l', substr($this->index, $current * 4, 4))[1];
43
+
fseek($this->dbFh, $loc);
44
+
$row = fgets($this->dbFh);
41
45
$data = explode("\t", $row, 4);
42
46
$compare = inet_pton($this->prefixIPv4($data[1]));
43
47
$asn = $data[2];
···
52
56
}
53
57
54
58
return $lastInnerSearchAsn ?? $asn;
55
-
}
56
-
57
-
private function parseFileForDb(): array
58
-
{
59
-
fseek($this->fh, 0);
60
-
61
-
$ret = [ftell($this->fh)];
62
-
while (($row = fgets($this->fh)) !== false) {
63
-
if ($row !== '') {
64
-
$ret[] = ftell($this->fh);
65
-
}
66
-
}
67
-
68
-
return $ret;
69
59
}
70
60
71
61
/**
+56
app/Libraries/Ip2AsnUpdater.php
+56
app/Libraries/Ip2AsnUpdater.php
···
1
+
<?php
2
+
3
+
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4
+
// See the LICENCE file in the repository root for full licence text.
5
+
6
+
declare(strict_types=1);
7
+
8
+
namespace App\Libraries;
9
+
10
+
class Ip2AsnUpdater
11
+
{
12
+
public static function getDbPath()
13
+
{
14
+
return database_path('ip2asn.tsv');
15
+
}
16
+
17
+
public static function getIndexPath()
18
+
{
19
+
return database_path('ip2asn.idx');
20
+
}
21
+
22
+
private string $tsv;
23
+
24
+
public function run(): void
25
+
{
26
+
$dbPath = static::getDbPath();
27
+
$indexPath = static::getIndexPath();
28
+
29
+
$dbExists = file_exists($dbPath);
30
+
31
+
$dbExpireTime = time() - (24 * 3600);
32
+
$newDb = !$dbExists || filemtime($dbPath) < $dbExpireTime;
33
+
34
+
$newIndex = !$dbExists || !file_exists($indexPath) || filemtime($dbPath) > filemtime($indexPath);
35
+
if (!$newDb && !$newIndex) {
36
+
return;
37
+
}
38
+
39
+
$tsv = $newDb
40
+
? gzdecode(file_get_contents('https://iptoasn.com/data/ip2asn-combined.tsv.gz'))
41
+
: file_get_contents($dbPath);
42
+
43
+
$currentLine = 0;
44
+
$index = pack('l', $currentLine);
45
+
while (($currentLine = strpos($tsv, "\n", $currentLine)) !== false) {
46
+
$currentLine++;
47
+
if (isset($tsv[$currentLine])) {
48
+
$index .= pack('l', $currentLine);
49
+
}
50
+
}
51
+
if ($newDb) {
52
+
file_put_contents($dbPath, $tsv);
53
+
}
54
+
file_put_contents($indexPath, $index);
55
+
}
56
+
}
-12
bin/update_ip2asn.sh
-12
bin/update_ip2asn.sh
···
1
-
#!/bin/sh
2
-
3
-
set -e
4
-
set -u
5
-
6
-
echo 'Updating ip2asn database...'
7
-
8
-
outfile="$(dirname "${0}")/../database/ip2asn-combined.tsv"
9
-
curl https://iptoasn.com/data/ip2asn-combined.tsv.gz | gzip -d > "${outfile}.new"
10
-
mv "${outfile}.new" "${outfile}"
11
-
12
-
echo 'Finished updating ip2asn database.'
+1
-1
build.sh
+1
-1
build.sh
+3
-1
docker/development/prepare.sh
+3
-1
docker/development/prepare.sh
···
69
69
cp .docker/.my.cnf.example .docker/.my.cnf
70
70
fi
71
71
72
-
./bin/update_ip2asn.sh
72
+
if [ ! -f database/ip2asn.tsv ]; then
73
+
_run artisan ip2asn:update
74
+
fi
73
75
74
76
echo "Preparation completed. Adjust .env file if needed and run 'docker compose up' followed by running migration."