Make WordPress Core

Changeset 41629


Ignore:
Timestamp:
09/28/2017 05:36:34 AM (7 years ago)
Author:
pento
Message:

Database: Add support for connecting to IPv6 hosts

IPv4 addresses are scarce, overworked, and underpaid. They're ready to retire, but we just won't let them go. If you care about their wellbeing, switch to IPv6 today.

Props schlessera, birgire.
Fixes #41722.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/wp-db.php

    r41628 r41629  
    14611461            $this->dbh = mysqli_init();
    14621462
    1463             // mysqli_real_connect doesn't support the host param including a port or socket
    1464             // like mysql_connect does. This duplicates how mysql_connect detects a port and/or socket file.
    1465             $port = null;
    1466             $socket = null;
    1467             $host = $this->dbhost;
    1468             $port_or_socket = strstr( $host, ':' );
    1469             if ( ! empty( $port_or_socket ) ) {
    1470                 $host = substr( $host, 0, strpos( $host, ':' ) );
    1471                 $port_or_socket = substr( $port_or_socket, 1 );
    1472                 if ( 0 !== strpos( $port_or_socket, '/' ) ) {
    1473                     $port = intval( $port_or_socket );
    1474                     $maybe_socket = strstr( $port_or_socket, ':' );
    1475                     if ( ! empty( $maybe_socket ) ) {
    1476                         $socket = substr( $maybe_socket, 1 );
    1477                     }
    1478                 } else {
    1479                     $socket = $port_or_socket;
    1480                 }
     1463            $host    = $this->dbhost;
     1464            $port    = null;
     1465            $socket  = null;
     1466            $is_ipv6 = false;
     1467           
     1468            if ( $host_data = $this->parse_db_host( $this->dbhost ) ) {
     1469                list( $host, $port, $socket, $is_ipv6 ) = $host_data;
     1470            }
     1471
     1472            /*
     1473             * If using the `mysqlnd` library, the IPv6 address needs to be
     1474             * enclosed in square brackets, whereas it doesn't while using the
     1475             * `libmysqlclient` library.
     1476             * @see https://bugs.php.net/bug.php?id=67563
     1477             */
     1478            if ( $is_ipv6 && extension_loaded( 'mysqlnd' ) ) {
     1479                $host = "[$host]";
    14811480            }
    14821481
     
    14901489                $this->dbh = null;
    14911490
    1492                 /* It's possible ext/mysqli is misconfigured. Fall back to ext/mysql if:
     1491                /*
     1492                 * It's possible ext/mysqli is misconfigured. Fall back to ext/mysql if:
    14931493                 *  - We haven't previously connected, and
    14941494                 *  - WP_USE_EXT_MYSQL isn't set to false, and
     
    15681568
    15691569        return false;
     1570    }
     1571
     1572    /**
     1573     * Parse the DB_HOST setting to interpret it for mysqli_real_connect.
     1574     *
     1575     * mysqli_real_connect doesn't support the host param including a port or
     1576     * socket like mysql_connect does. This duplicates how mysql_connect detects
     1577     * a port and/or socket file.
     1578     *
     1579     * @since 4.9.0
     1580     *
     1581     * @param string $host The DB_HOST setting to parse.
     1582     * @return array|bool Array containing the host, the port, the socket and whether
     1583     *                    it is an IPv6 address, in that order. If $host couldn't be parsed,
     1584     *                    returns false.
     1585     */
     1586    public function parse_db_host( $host ) {
     1587        $port    = null;
     1588        $socket  = null;
     1589        $is_ipv6 = false;
     1590
     1591        // We need to check for an IPv6 address first.
     1592        // An IPv6 address will always contain at least two colons.
     1593        if ( substr_count( $host, ':' ) > 1 ) {
     1594            $pattern = '#^(?:\[)?(?<host>[0-9a-fA-F:]+)(?:\]:(?<port>[\d]+))?(?:/(?<socket>.+))?#';
     1595            $is_ipv6 = true;
     1596        } else {
     1597            // We seem to be dealing with an IPv4 address.
     1598            $pattern = '#^(?<host>[^:/]*)(?::(?<port>[\d]+))?(?::(?<socket>.+))?#';
     1599        }
     1600
     1601        $matches = array();
     1602        $result = preg_match( $pattern, $host, $matches );
     1603
     1604        if ( 1 !== $result ) {
     1605            // Couldn't parse the address, bail.
     1606            return false;
     1607        }
     1608
     1609        foreach ( array( 'host', 'port', 'socket' ) as $component ) {
     1610            if ( array_key_exists( $component, $matches ) ) {
     1611                $$component = $matches[$component];
     1612            }
     1613        }
     1614
     1615        return array( $host, $port, $socket, $is_ipv6 );
    15701616    }
    15711617
  • trunk/tests/phpunit/tests/db.php

    r41496 r41629  
    11271127        $this->assertEquals( '1 %1$d %% %', $sql );
    11281128    }
     1129
     1130    /**
     1131     * @dataProvider parse_db_host_data_provider
     1132     * @ticket 41722
     1133     */
     1134    public function test_parse_db_host( $host_string, $expect_bail, $host, $port, $socket, $is_ipv6 ) {
     1135        global $wpdb;
     1136        $data = $wpdb->parse_db_host( $host_string );
     1137        if ( $expect_bail ) {
     1138            $this->assertFalse( $data );
     1139        } else {
     1140            $this->assertInternalType( 'array', $data );
     1141
     1142            list( $parsed_host, $parsed_port, $parsed_socket, $parsed_is_ipv6 ) = $data;
     1143
     1144            $this->assertEquals( $host, $parsed_host );
     1145            $this->assertEquals( $port, $parsed_port );
     1146            $this->assertEquals( $socket, $parsed_socket );
     1147            $this->assertEquals( $is_ipv6, $parsed_is_ipv6 );
     1148        }
     1149    }
     1150
     1151    public function parse_db_host_data_provider() {
     1152        return array(
     1153            array(
     1154                '',    // DB_HOST
     1155                false, // Expect parse_db_host to bail for this hostname
     1156                null,  // Parsed host
     1157                null,  // Parsed port
     1158                null,  // Parsed socket
     1159                false, // is_ipv6
     1160            ),
     1161            array(
     1162                ':3306',
     1163                false,
     1164                null,
     1165                '3306',
     1166                null,
     1167                false,
     1168            ),
     1169            array(
     1170                ':/tmp/mysql.sock',
     1171                false,
     1172                null,
     1173                null,
     1174                '/tmp/mysql.sock',
     1175                false,
     1176            ),
     1177            array(
     1178                '127.0.0.1',
     1179                false,
     1180                '127.0.0.1',
     1181                null,
     1182                null,
     1183                false,
     1184            ),
     1185            array(
     1186                '127.0.0.1:3306',
     1187                false,
     1188                '127.0.0.1',
     1189                '3306',
     1190                null,
     1191                false,
     1192            ),
     1193            array(
     1194                'example.com',
     1195                false,
     1196                'example.com',
     1197                null,
     1198                null,
     1199                false,
     1200            ),
     1201            array(
     1202                'example.com:3306',
     1203                false,
     1204                'example.com',
     1205                '3306',
     1206                null,
     1207                false,
     1208            ),
     1209            array(
     1210                'localhost',
     1211                false,
     1212                'localhost',
     1213                null,
     1214                null,
     1215                false,
     1216            ),
     1217            array(
     1218                'localhost:/tmp/mysql.sock',
     1219                false,
     1220                'localhost',
     1221                null,
     1222                '/tmp/mysql.sock',
     1223                false,
     1224            ),
     1225            array(
     1226                '0000:0000:0000:0000:0000:0000:0000:0001',
     1227                false,
     1228                '0000:0000:0000:0000:0000:0000:0000:0001',
     1229                null,
     1230                null,
     1231                true,
     1232            ),
     1233            array(
     1234                '::1',
     1235                false,
     1236                '::1',
     1237                null,
     1238                null,
     1239                true,
     1240            ),
     1241            array(
     1242                '[::1]',
     1243                false,
     1244                '::1',
     1245                null,
     1246                null,
     1247                true,
     1248            ),
     1249            array(
     1250                '[::1]:3306',
     1251                false,
     1252                '::1',
     1253                '3306',
     1254                null,
     1255                true,
     1256            ),
     1257            array(
     1258                '2001:0db8:0000:0000:0000:ff00:0042:8329',
     1259                false,
     1260                '2001:0db8:0000:0000:0000:ff00:0042:8329',
     1261                null,
     1262                null,
     1263                true,
     1264            ),
     1265            array(
     1266                '2001:db8:0:0:0:ff00:42:8329',
     1267                false,
     1268                '2001:db8:0:0:0:ff00:42:8329',
     1269                null,
     1270                null,
     1271                true,
     1272            ),
     1273            array(
     1274                '2001:db8::ff00:42:8329',
     1275                false,
     1276                '2001:db8::ff00:42:8329',
     1277                null,
     1278                null,
     1279                true,
     1280            ),
     1281            array(
     1282                '?::',
     1283                true,
     1284                null,
     1285                null,
     1286                null,
     1287                false,
     1288            ),
     1289        );
     1290    }
    11291291}
Note: See TracChangeset for help on using the changeset viewer.