assertEquals(1, count($entries), 'resolvePinned() liefert genau einen CURLOPT_RESOLVE-Eintrag'); $entry = $entries[0]; $this->assert( str_starts_with($entry, 'example.com:443:'), 'Eintrag beginnt mit "example.com:443:" (Eintrag: ' . $entry . ')' ); $addresses = explode(',', substr($entry, strlen('example.com:443:'))); $this->assertNotEmpty($addresses, 'Der Eintrag enthält mindestens eine Adresse'); foreach ($addresses as $address) { // IPv6 muss geklammert sein, sonst sind seine Doppelpunkte in der Liste nicht abgrenzbar $isBracketed = str_starts_with($address, '[') && str_ends_with($address, ']'); $ip = $isBracketed ? substr($address, 1, -1) : $address; $this->assert( filter_var($ip, FILTER_VALIDATE_IP) !== false, 'Adresse "' . $address . '" ist eine gültige IP' ); $this->assertEquals( str_contains($ip, ':'), $isBracketed, 'Genau IPv6-Adressen stehen in eckigen Klammern (Adresse: ' . $address . ')' ); } // Ein Dual-Stack-Host darf nicht dazu führen, dass eine Adressfamilie verlorengeht: // beide aufgelösten Familien müssen im selben Eintrag landen. $resolved = []; foreach ((array)@dns_get_record('example.com', DNS_A + DNS_AAAA) as $record) { if (!empty($record['ip'])) { $resolved[] = $record['ip']; } if (!empty($record['ipv6'])) { $resolved[] = $record['ipv6']; } } foreach ($resolved as $ip) { $expected = str_contains($ip, ':') ? '[' . $ip . ']' : $ip; $this->assert( in_array($expected, $addresses, true), 'Aufgelöste Adresse ' . $ip . ' ist im Pinning-Eintrag enthalten' ); } } }