1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22/** 23 * A parser for IPv6 address. 24 */ 25class CIPv6Parser extends CParser { 26 27 const STATE_NEW = 0; 28 const STATE_AFTER_DIGITS = 1; 29 const STATE_AFTER_COLON = 2; 30 const STATE_AFTER_DBLCOLON = 3; 31 32 /** 33 * @var CIPv4Parser 34 */ 35 private $ipv4_parser; 36 37 public function __construct() { 38 $this->ipv4_parser = new CIPv4Parser(); 39 } 40 41 /** 42 * @param string $source 43 * @param int $pos 44 * 45 * @return int 46 */ 47 public function parse($source, $pos = 0) { 48 $this->length = 0; 49 $this->match = ''; 50 51 $state = self::STATE_NEW; 52 $colons = 0; 53 $dbl_colons = 0; 54 55 for ($p = $pos; isset($source[$p]); $p++) { 56 switch ($state) { 57 case self::STATE_NEW: 58 if (self::parseDoubleColon($source, $p)) { 59 if ($dbl_colons++ == 1) { 60 return self::PARSE_FAIL; 61 } 62 $state = self::STATE_AFTER_DBLCOLON; 63 } 64 elseif (self::parseXDigits($source, $p)) { 65 $state = self::STATE_AFTER_DIGITS; 66 } 67 else { 68 return self::PARSE_FAIL; 69 } 70 break; 71 72 case self::STATE_AFTER_COLON: 73 if (self::parseXDigits($source, $p)) { 74 $state = self::STATE_AFTER_DIGITS; 75 } 76 else { 77 return self::PARSE_FAIL; 78 } 79 break; 80 81 case self::STATE_AFTER_DBLCOLON: 82 if (self::parseXDigits($source, $p)) { 83 $state = self::STATE_AFTER_DIGITS; 84 } 85 else { 86 break 2; 87 } 88 break; 89 90 case self::STATE_AFTER_DIGITS: 91 if (self::parseDoubleColon($source, $p)) { 92 if ($dbl_colons++ == 1) { 93 return self::PARSE_FAIL; 94 } 95 $state = self::STATE_AFTER_DBLCOLON; 96 } 97 elseif ($source[$p] == ':') { 98 if ($colons++ == 7) { 99 return self::PARSE_FAIL; 100 } 101 $state = self::STATE_AFTER_COLON; 102 } 103 else { 104 break 2; 105 } 106 break; 107 } 108 } 109 110 if ($state == self::STATE_AFTER_COLON || $state == self::STATE_NEW) { 111 return self::PARSE_FAIL; 112 } 113 114 if (isset($source[$p]) && $source[$p] == '.' && $state == self::STATE_AFTER_DIGITS 115 && ($colons != 0 || $dbl_colons != 0)) { 116 if (($dbl_colons == 0 && $colons != 6) || ($dbl_colons == 1 && $colons > 4)) { 117 return self::PARSE_FAIL; 118 } 119 120 while ($source[$p - 1] != ':') { 121 $p--; 122 } 123 124 if ($this->ipv4_parser->parse($source, $p) == self::PARSE_FAIL) { 125 return self::PARSE_FAIL; 126 } 127 128 $p += $this->ipv4_parser->getLength(); 129 } 130 elseif (($dbl_colons == 0 && $colons != 7) || ($dbl_colons == 1 && $colons > 5)) { 131 return self::PARSE_FAIL; 132 } 133 134 $this->length = $p - $pos; 135 $this->match = substr($source, $pos, $this->length); 136 137 return (isset($source[$pos + $this->length]) ? self::PARSE_SUCCESS_CONT : self::PARSE_SUCCESS); 138 } 139 140 private static function parseDoubleColon($source, &$pos) { 141 $p = $pos; 142 143 if ($source[$p] !== ':') { 144 return false; 145 } 146 $p++; 147 148 if (!isset($source[$p]) || $source[$p] !== ':') { 149 return false; 150 } 151 152 $pos = $p; 153 154 return true; 155 } 156 157 private static function parseXDigits($source, &$pos) { 158 $p = $pos; 159 160 while (isset($source[$p]) && ctype_xdigit($source[$p])) { 161 $p++; 162 } 163 164 if ($p == $pos || $p - $pos > 4) { 165 return false; 166 } 167 168 $pos = $p - 1; 169 170 return true; 171 } 172} 173