Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Generate
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 4
182
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
0.00%
0 / 1
110
 showProgress
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 canExecuteWithoutLocalSettings
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\TrustedXFF;
4
5use Maintenance;
6use Wikimedia\IPSet;
7use Wikimedia\IPUtils;
8
9require_once getenv( 'MW_INSTALL_PATH' ) !== false
10    ? getenv( 'MW_INSTALL_PATH' ) . '/maintenance/Maintenance.php'
11    : __DIR__ . '/../../../maintenance/Maintenance.php';
12
13class Generate extends Maintenance {
14    public function __construct() {
15        parent::__construct();
16        $this->addDescription( 'Generates the PHP/JSON XFF file from the trusted-hosts.txt file' );
17        $this->addOption( 'outdir', 'The output directory, default ' . dirname( __DIR__ ) );
18
19        // This script does not require the extension to be installed
20    }
21
22    public function execute() {
23        $outDir = $this->getOption( 'outdir', dirname( __DIR__ ) );
24        $inFileName = dirname( __DIR__ ) . '/trusted-hosts.txt';
25        $inFile = fopen( $inFileName, 'r' );
26        if ( !$inFile ) {
27            $this->fatalError( "Unable to open input file \"$inFileName\"\n" );
28        }
29
30        $lineNum = 0;
31        $ranges = [];
32        $names = [];
33
34        while ( !feof( $inFile ) ) {
35            $line = fgets( $inFile );
36            $lineNum++;
37            if ( $line === false ) {
38                break;
39            }
40            // Remove comment
41            $hashPos = strpos( $line, '#' );
42            if ( $hashPos !== false ) {
43                $line = substr( $line, 0, $hashPos );
44            }
45            // Strip spaces
46            $line = trim( $line );
47
48            if ( $line === '' ) {
49                // Comment or blank line
50                continue;
51            }
52
53            [ $start, ] = IPUtils::parseRange( $line );
54            if ( $start === false ) {
55                // Try DNS
56                $names[] = [ $lineNum, $line ];
57                continue;
58            }
59            $ranges[] = $line;
60        }
61
62        $this->output( count( $names ) . " DNS queries to do...\n" );
63
64        foreach ( $names as $i => $nameInfo ) {
65            [ $lineNum, $name ] = $nameInfo;
66            $ips = gethostbynamel( $name );
67            if ( $ips === false ) {
68                $this->output( "Not a valid host or IP address on line $lineNum$name\n" );
69            } else {
70                $ranges = array_merge( $ranges, $ips );
71            }
72            $this->showProgress( $i, count( $names ) );
73            // Don't DoS the recursor
74            usleep( 10000 );
75        }
76        $this->output( "\n" );
77
78        natsort( $ranges );
79        $ranges = array_values( array_unique( $ranges ) );
80
81        $header = sprintf(
82            "Generated by %s/%s on %s;\nNote this file is deprecated in favour of trusted-hosts.json.",
83            basename( __DIR__ ),
84            basename( __FILE__ ),
85            gmdate( 'c' )
86        );
87
88        $out = "<" . "?php\n" . preg_replace( '/^/m', '# ', $header ) . "\nreturn [\n";
89        foreach ( $ranges as $range ) {
90            $out .= "\t" . var_export( $range, true ) . ",\n";
91        }
92        $out .= "];\n";
93        file_put_contents(
94            "$outDir/trusted-hosts.php",
95            $out
96        );
97
98        file_put_contents(
99            "$outDir/trusted-hosts.json",
100            json_encode( new IPSet( $ranges ) )
101        );
102
103        $count = count( $ranges );
104        $this->output( "$count ips or ranges listed\n" );
105    }
106
107    /**
108     * @param int $current
109     * @param int $total
110     */
111    private function showProgress( $current, $total ) {
112        $length = 50;
113        $dots = intval( ( $current + 1 ) / $total * $length );
114        printf( "%6.2f%%  [" .
115            str_repeat( '=', $dots ) . str_repeat( '.', $length - $dots ) . "]\r",
116            ( $current + 1 ) / $total * 100
117        );
118    }
119
120    public function canExecuteWithoutLocalSettings(): bool {
121        return true;
122    }
123}
124
125$maintClass = Generate::class;
126require_once RUN_MAINTENANCE_IF_MAIN;