1<?php 2 3/** 4 * DNS Library for handling lookups and updates. 5 * 6 * Copyright (c) 2020, Mike Pultz <mike@mikepultz.com>. All rights reserved. 7 * 8 * See LICENSE for more details. 9 * 10 * @category Networking 11 * @package Net_DNS2 12 * @author Mike Pultz <mike@mikepultz.com> 13 * @copyright 2020 Mike Pultz <mike@mikepultz.com> 14 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 15 * @link https://netdns2.com/ 16 * @since File available since Release 1.0.0 17 * 18 */ 19 20/** 21 * APL Resource Record - RFC3123 22 * 23 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 * | ADDRESSFAMILY | 25 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 26 * | PREFIX | N | AFDLENGTH | 27 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 * / AFDPART / 29 * | | 30 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 31 * 32 */ 33class Net_DNS2_RR_APL extends Net_DNS2_RR 34{ 35 /* 36 * a list of all the address prefix list items 37 */ 38 public $apl_items = []; 39 40 /** 41 * method to return the rdata portion of the packet as a string 42 * 43 * @return string 44 * @access protected 45 * 46 */ 47 protected function rrToString() 48 { 49 $out = ''; 50 51 foreach ($this->apl_items as $item) { 52 53 if ($item['n'] == 1) { 54 55 $out .= '!'; 56 } 57 58 $out .= $item['address_family'] . ':' . 59 $item['afd_part'] . '/' . $item['prefix'] . ' '; 60 } 61 62 return trim($out); 63 } 64 65 /** 66 * parses the rdata portion from a standard DNS config line 67 * 68 * @param array $rdata a string split line of values for the rdata 69 * 70 * @return boolean 71 * @access protected 72 * 73 */ 74 protected function rrFromString(array $rdata) 75 { 76 foreach ($rdata as $item) { 77 78 if (preg_match('/^(!?)([1|2])\:([^\/]*)\/([0-9]{1,3})$/', $item, $m)) { 79 80 $i = [ 81 82 'address_family' => $m[2], 83 'prefix' => $m[4], 84 'n' => ($m[1] == '!') ? 1 : 0, 85 'afd_part' => strtolower($m[3]) 86 ]; 87 88 $address = $this->_trimZeros( 89 $i['address_family'], $i['afd_part'] 90 ); 91 92 $i['afd_length'] = count(explode('.', $address)); 93 94 $this->apl_items[] = $i; 95 } 96 } 97 98 return true; 99 } 100 101 /** 102 * parses the rdata of the Net_DNS2_Packet object 103 * 104 * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from 105 * 106 * @return boolean 107 * @access protected 108 * 109 */ 110 protected function rrSet(Net_DNS2_Packet &$packet) 111 { 112 if ($this->rdlength > 0) { 113 114 $offset = 0; 115 116 while ($offset < $this->rdlength) { 117 118 // 119 // unpack the family, prefix, negate and length values 120 // 121 $x = unpack( 122 'naddress_family/Cprefix/Cextra', substr($this->rdata, $offset) 123 ); 124 125 $item = [ 126 127 'address_family' => $x['address_family'], 128 'prefix' => $x['prefix'], 129 'n' => ($x['extra'] >> 7) & 0x1, 130 'afd_length' => $x['extra'] & 0xf 131 ]; 132 133 switch($item['address_family']) { 134 135 case 1: 136 $r = unpack( 137 'C*', substr($this->rdata, $offset + 4, $item['afd_length']) 138 ); 139 if (count($r) < 4) { 140 141 for ($c=count($r)+1; $c<4+1; $c++) { 142 143 $r[$c] = 0; 144 } 145 } 146 147 $item['afd_part'] = implode('.', $r); 148 149 break; 150 case 2: 151 $r = unpack( 152 'C*', substr($this->rdata, $offset + 4, $item['afd_length']) 153 ); 154 if (count($r) < 8) { 155 156 for ($c=count($r)+1; $c<8+1; $c++) { 157 158 $r[$c] = 0; 159 } 160 } 161 162 $item['afd_part'] = sprintf( 163 '%x:%x:%x:%x:%x:%x:%x:%x', 164 $r[1], $r[2], $r[3], $r[4], $r[5], $r[6], $r[7], $r[8] 165 ); 166 167 break; 168 default: 169 return false; 170 } 171 172 $this->apl_items[] = $item; 173 174 $offset += 4 + $item['afd_length']; 175 } 176 177 return true; 178 } 179 180 return false; 181 } 182 183 /** 184 * returns the rdata portion of the DNS packet 185 * 186 * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for 187 * compressed names 188 * 189 * @return mixed either returns a binary packed 190 * string or null on failure 191 * @access protected 192 * 193 */ 194 protected function rrGet(Net_DNS2_Packet &$packet) 195 { 196 if (count($this->apl_items) > 0) { 197 198 $data = ''; 199 200 foreach ($this->apl_items as $item) { 201 202 // 203 // pack the address_family and prefix values 204 // 205 $data .= pack( 206 'nCC', 207 $item['address_family'], 208 $item['prefix'], 209 ($item['n'] << 7) | $item['afd_length'] 210 ); 211 212 switch($item['address_family']) { 213 case 1: 214 $address = explode( 215 '.', 216 $this->_trimZeros($item['address_family'], $item['afd_part']) 217 ); 218 219 foreach ($address as $b) { 220 $data .= chr($b); 221 } 222 break; 223 case 2: 224 $address = explode( 225 ':', 226 $this->_trimZeros($item['address_family'], $item['afd_part']) 227 ); 228 229 foreach ($address as $b) { 230 $data .= pack('H', $b); 231 } 232 break; 233 default: 234 return null; 235 } 236 } 237 238 $packet->offset += strlen($data); 239 240 return $data; 241 } 242 243 return null; 244 } 245 246 /** 247 * returns an IP address with the right-hand zero's trimmed 248 * 249 * @param integer $family the IP address family from the rdata 250 * @param string $address the IP address 251 * 252 * @return string the trimmed IP addresss. 253 * 254 * @access private 255 * 256 */ 257 private function _trimZeros($family, $address) 258 { 259 $a = []; 260 261 switch($family) { 262 case 1: 263 $a = array_reverse(explode('.', $address)); 264 break; 265 case 2: 266 $a = array_reverse(explode(':', $address)); 267 break; 268 default: 269 return ''; 270 } 271 272 foreach ($a as $value) { 273 274 if ($value === '0') { 275 276 array_shift($a); 277 } 278 } 279 280 $out = ''; 281 282 switch($family) { 283 case 1: 284 $out = implode('.', array_reverse($a)); 285 break; 286 case 2: 287 $out = implode(':', array_reverse($a)); 288 break; 289 default: 290 return ''; 291 } 292 293 return $out; 294 } 295} 296