Полное решение PHP + MySQL IPv4 и IPv6? - PullRequest
0 голосов
/ 11 октября 2011

Я не очень разбираюсь в сетевых темах, но мне нужно хранить IP-адреса для моего проекта, и я хочу быть готовым к работе как с IPv4, так и с IPv6.Лучшее решение, которое я прочитал, это два поля BIGINT без знака, одно из которых равно нулю в случае IPv4:
Как сохранить IPv6-совместимый адрес в реляционной базе данных Есть ли у кого-нибудь полное решение?

Мне нужен код для перехода от строкового адреса (как производит $ _SERVER ['HTTP_CLIENT_IP']) к числовым значениям и наоборот.

Большое спасибо за любую помощь.Я хочу убедиться, что я делаю это правильно.

Ответы [ 3 ]

1 голос
/ 11 октября 2011

Или вы можете использовать базу данных, такую ​​как PostgreSQL, если это возможно. Он имеет собственные типы данных для хранения и поиска адресов и префиксов IPv4 и IPv6. Ввод и вывод выполняются в строковом представлении (обычно).

Если вам нужно использовать MySQL, это действительно зависит от того, как вы хотите использовать адреса. Если вы хотите искать подсети, группировать по префиксу и т. Д., То целые числа являются наиболее полезными. Если вам просто нужно хранить их, тогда вам подойдет varchar.

0 голосов
/ 11 октября 2011

Для проверки IP-адреса (формата) я в настоящее время использую это как часть чего-то, над чем я работаю - не уверен на 100%, что это все еще правильно - нужно добавить больше данных (и я неМне не нравится соглашение об именах, которое я использовал для закрытых членов - но это легко исправить с помощью рефакторинга):

class IPAddress {

  //IP Address string
  private $ip_address;

  //IPv4 verification (RegExp insert)
  private $match_ipv4;

  //IPv6 verification (RegExp insert)
  private $match_ipv6;

  /**
   * Constructor function
   *
   * The $sIPAddress parameter is optional -
   * it allows you to set the IP address in
   * the object at creation.
   *
   * @param  string  $sIPAddress
   * @return void
   */
  public function __construct($sIPAddress=null) {
    //setup regexp inserts
    //IPv4 decimal octets match
    $sDecOctet = "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])";

    //IPv4 match
    $this->match_ipv4 = "({$sDecOctet}\.){3}{$sDecOctet}";

    //Hex 16 match
    $sH16 = "[0-9a-fA-F]{1,4}";

    //Char32 match
    $sLS32 = "({$sH16}:{$sH16}|{$this->match_ipv4})";

    //IPv6 match
    $this->match_ipv6 = "((({$sH16}:){6}"
    . "|::({$sH16}:){5}"
    . "|({$sH16})?::({$sH16}:){4}"
    . "|(({$sH16}:){0,1}{$sH16})?::({$sH16}:){3}"
    . "|(({$sH16}:){0,2}{$sH16})?::({$sH16}:){2}"
    . "|(({$sH16}:){0,3}{$sH16})?::{$sH16}:"
    . "|(({$sH16}:){0,4}{$sH16})?::"
    . "){$sLS32}"
    . "|((({$sH16}:){0,5}{$sH16})?::{$sH16}"
    . "|(({$sH16}:){0,6}{$sH16})?::"
    . "))";

    //set the IP address if required
    if(!is_null($sIPAddress)) {
      $this->setIPAddress($sIPAddress);
    }
  }

  /**
   * IP Address setter
   *
   * Sets the IP address string - this can
   * be either IPv4 or IPv6 format.
   *
   * @param  string  $sIPAddress
   * @return void
   */
  public function setIPAddress($sIPAddress) {
    $this->ip_address = $sIPAddress;
  }

  /**
   * IP Address getter
   *
   * Returns the IP address string - this
   * can be either IPv4 or IPv6 format.
   *
   * @return string
   */
  public function getIPAddress() {
    return $this->ip_address;
  }

  /**
   * IPv4 RegExp getter
   *
   * Returns Regular Expression used to
   * validate IPv4 addresses.
   *
   * @return string
   */
  public function getIPv4RegExp() {
    return '/^' . $this->match_ipv4 . '$/';
  }

  /**
   * IPv6 RegExp getter
   *
   * Returns the Regular Expression used to
   * validate IPv6 addresses.
   *
   * @return string
   */
  public function getIPv6RegExp() {
    return '/^' . $this->match_ipv6 . '$/i';
  }

  /**
   * IPv4 validation
   *
   * Validates the stored IP address
   * against the IPv4 pattern and returns
   * a boolean denoting whether the address
   * if of IPv4 format or not.
   *
   * @return bool
   */
  public function validateIPv4() {
    return ip2long($this->ip_address) && ip2long($this->ip_address) !== -1 ? true : false;
  }

  /**
   * IPv6 validation
   *
   * Validates the stored IP address
   * against the IPv6 pattern and returns
   * a boolean denoting whether the address
   * if of IPv6 format or not.
   *
   * @return bool
   */
  public function validateIPv6() {
    return preg_match($this->getIPv6RegExp(), $this->ip_address) ? true : false;
  }

  /**
   * General validity check
   *
   * Validates the stored IP address against
   * both the IPv4 and IPv6 patterns - if
   * EITHER matches then true is returned
   * (it's a correctly formatted IP address).
   *
   * Otherwise it's not a valid IP address
   * and false is returned.
   *
   * @return bool
   */
  public function isValid() {
    return $this->validateIPv4() || $this->validateIPv6() ? true : false;
  }

  /**
   * Reserved state checker
   *
   * This method checks wheter the stored IP address
   * is part of the local network range (i.e. it's in
   * the private reserved IP address range)
   *
   * A boolean is returned denoting this reserved state
   * unless the IP address itself is invalid - in which
   * case null is returned.
   *
   * @return bool
   */
  public function isReserved() {

    //IPv4 format
    if($this->validateIPv4()) {
      return $this->_getIPv4IsReserved($this->ip_address);
    }

    //IPv6 format
    elseif($this->validateIPv6()) {
      //IPv4 masking
      // this falls over if the IPv4 part is short-handed
      // for instance ::ffff:192.0.2.128 can be written as ::ffff:c000:280
      $reIPv4Masking = '/^((0{1,4}:){6}|(0{1,4}:){1,5}ffff:|::ffff:)(([0-9]{1,3}\.){3}[0-9]{1,3})/';

      //standard reserved IPv6 addresses
      //local loopback = 0:0:0:0:0:0:0:1 || ::1
      if(preg_match('/^(0{1,4}:){1,7}1|::1|fc00:.*$/i', $this->ip_address)) {
        return true;
      }

      //if this is really an IPv4 address stacked in IPv6...
      elseif(preg_match($reIPv4Masking, $this->ip_address)) {
        $sIPv4Address = preg_replace($reIPv4Masking, "$2", $this->ip_address);
        return $this->_getIPv4IsReserved($sIPv4Address);
      }

      //not reserved
      else {
        return false;
      }
    }

    //invalid format
    else {
      return null;
    }
  }

  /**
   * IPv4 reserved state checker
   *
   * Private method to determine whether an IPv4 address is in
   * one of the reserved private brackets (e.g. it's probably local)
   *
   * Returns a boolean denoting whether it's a reserved IPv4 address
   * or null should the IP address fail validation
   *
   * @param  string  $sIPv4Address
   * @return bool
   */
  private function _getIPv4IsReserved($sIPv4Address) {
    $sIP = long2ip(ip2long($sIPv4Address));
    $reIPv4 = '/([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/$'; //just a quick and dirty RegExp without sanity checking since we've already done that

    if(preg_match($reIPv4, $sIP)) {
      //break the IP address into parts and cast to integers
      $iIPp1 = VParse::toInt(preg_replace($reIPv4, "$1", $sIP));
      $iIPp2 = VParse::toInt(preg_replace($reIPv4, "$2", $sIP));
      $iIPp3 = VParse::toInt(preg_replace($reIPv4, "$3", $sIP));
      $iIPp4 = VParse::toInt(preg_replace($reIPv4, "$4", $sIP));

      //check for reserved IP addresses
      // 127.0.0.1 (local loopback)
      // 10.0.0.0 - 10.255.255.255
      // 172.16.0.0 - 172.31.255.255
      // 192.168.0.0 - 192.168.255.255
      if( ($iIPp1 == 127 && $iIPp2 == 0 && $iIPp3 == 0 && $iIPp4 == 1) || $iIPp1 == 10 || ($iIPp1 == 172 && $iIP2 >= 16 && $iIP2 <= 31) || ($iIPp1 == 192 && $iIPp2 == 168) ) {
        return true;
      }

      //not part of the standard private IP address ranges
      else {
        return false;
      }
    }

    //invalid format
    else {
      return null;
    }
  }

//end class
}

РЕДАКТИРОВАТЬ: только что заметил, что это зависит от моего класса синтаксического анализа VParse - вы можете в значительной степенизамените любой экземпляр VParse::toInt() стандартными функциями приведения типа (int) в PHP.

0 голосов
/ 11 октября 2011

Я на самом деле храню ординалы так, как их пишут люди.Таким образом, 8 16-битных целых полей без знака для IPv6 и 4 8-битных целых полей без знака для IPv4.Для меня это упрощает поиск определенных сетей, хотя я вижу, что 2 больших знака без знака также могут быть простыми.

Или вы можете сохранить его в том формате, в котором вы чаще всего его используете, в своем коде ивосстановить его как таковой.Кажется, что это строка длиной около 40 символов.Если вы сделаете это, вы захотите использовать каноническое представление, подобное тому, что было предложено в rfc5952 .

. Я не могу просто написать код конвертации для вас;вам нужно будет привести пример того, что вы пробовали, но это не сработало и почему.

...