* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . **/ class qcEvents_Stream_DNS_Recordset implements IteratorAggregate, ArrayAccess, Countable { /* All records on this set */ private $Records = array (); // {{{ getIterator /** * Retrive a new iterator for this recordset * * @access public * @return ArrayIterator **/ public function getIterator () { return new ArrayIterator ($this->Records); } // }}} // {{{ offsetExists /** * Check if a given index exists on our recordset * * @param mixed $Index * * @access public * @return bool **/ public function offsetExists ($Index) { return isset ($this->Records [$Index]); } // }}} // {{{ offsetGet /** * Retrive a single record from this set * * @param mixed $Index * * @access public * @return qcEvents_Stream_DNS_Record **/ public function offsetGet ($Index) { if (isset ($this->Records [$Index])) return $this->Records [$Index]; } // }}} // {{{ offsetSet /** * Store a new record on this recrodset * * @param mixed $Index * @param qcEvents_Stream_DNS_Record $Record * * @access public * @return void **/ public function offsetSet ($Index, $Record) { if (!($Record instanceof qcEvents_Stream_DNS_Record)) return; if ($Index === null) $this->Records [] = $Record; else $this->Records [$Index] = $Record; } // }}} // {{{ offsetUnset /** * Remove a record from this set * * @param mixed $Index * * @access public * @return void **/ public function offsetUnset ($Index) { unset ($this->Records [$Index]); } // }}} // {{{ count /** * Count all records on this set * * @access public * @return int **/ public function count () { return count ($this->Records); } // }}} // {{{ getRecords /** * Retrive all records (of a given type) from this set * * @param int $Type (optional) * * @access public * @return qcEvents_Stream_DNS_Recordset **/ public function getRecords ($Type = null) { // Check if this is a senseless call if ($Type === null) return $this; // Create a cloned result $Result = clone $this; // Filter the result foreach ($Result as $ID=>$Record) if ($Record->getType () != $Type) unset ($Result [$ID]); return $Result; } // }}} // {{{ validate /** * Try to validate the entire resultset * * @param array $Keys Array with available DNS-Keys * * @access public * @return bool **/ public function validate (array $Keys) { // Inline data of IANA DNS Root Signing Public Key Certificate static $rootCertificate = <<getRecords (qcEvents_Stream_DNS_Record_RRSIG::DEFAULT_TYPE); if (count ($Signatures) == 0) return null; // Validate each signature foreach ($Signatures as $Signature) { // Retrive all Records for that type from this resultset $Records = $this->getRecords ($Type = $Signature->getCoveredType ()); if (count ($Records) == 0) { trigger_error ('RRSIG present without records to validate'); continue; } // Find a matching key for this $sigKeys = array (); foreach ($Keys as $Key) if (($Key instanceof qcEvents_Stream_DNS_Record_DNSKEY) && ($Key->getKeyTag () == $Signature->getKeyTag ())) $sigKeys [] = $Key; if (count ($sigKeys) == 0) { foreach ($Records as $Record) if (strlen ($Record->getLabel ()) == 1) { require_once ('X509/Certificate.php'); $sigKeys [] = @x509_Certificate::fromPEM ($rootCertificate); break; } if (count ($sigKeys) == 0) { trigger_error ('No matching DNS-Key found'); return false; } } // Make sure the records are ordered correctly $Records->orderCanonical (); // Build the verify-buffer $Labels = array (); $vBuffer = substr ($Signature->buildPayload (0, $Labels), 0, 18) . qcEvents_Stream_DNS_Message::setLabel ($Signature->getSigner ()); $Class = $Signature->getClass (); $TTL = $Signature->getOriginalTTL (); $Fixed = chr (($Type & 0xFF00) >> 8) . chr ($Type & 0xFF) . chr (($Class & 0xFF00) >> 8) . chr ($Class & 0xFF) . chr (($TTL & 0xFF000000) >> 24) . chr (($TTL & 0x00FF0000) >> 16) . chr (($TTL & 0x0000FF00) >> 8) . chr ($TTL & 0x000000FF); foreach ($Records as $Record) { $Labels = array (); $Length = strlen ($Payload = $Record->getPayload (0, $Labels)); $vBuffer .= qcEvents_Stream_DNS_Message::setLabel ($Record->getLabel ()) . $Fixed . chr (($Length & 0xFF00) >> 8) . chr ($Length & 0xFF) . $Payload; } // Try to verify the buffer with all available keys foreach ($sigKeys as $Key) if ((($Key instanceof qcEvents_Stream_DNS_Record_DNSKEY) && $Key->verifySignature ($vBuffer, $Signature)) || (($Key instanceof X509_Certificate) && $Key->verifySignature ($vBuffer, $Signature->getSignature (), $Signature->getAlgorithmObjectID ()))) continue (2); return false; } return true; } // }}} // {{{ orderCanonical /** * Order records on this set in canonical order * * @access public * @return void **/ public function orderCanonical () { usort ($this->Records, function (qcEvents_Stream_DNS_Record $Left, qcEvents_Stream_DNS_Record $Right) { // Compare labels of records $lLen = count ($lLabel = $Left->getLabel ()); $rLen = count ($rLabel = $Right->getLabel ()); if (($lLen == 0) && ($rLen > 0)) return -1; elseif (($rLen == 0) && ($lLen > 0)) return 1; while (($lLen-- > 0) && ($rLen-- > 0)) { if (($r = strcasecmp ($lLabel [$lLen], $rLabel [$rLen])) != 0) return $r; } // Compare the type if (($d = ($Left->getType () - $Right->getType ())) != 0) return $d; // Compare the class if (($d = ($Left->getClass () - $Right->getClass ())) != 0) return $d; return strcmp ($Left->getPayload (), $Right->getPayload ()); }); } // }}} } ?>