在 UNKNOWN DB 中存储 IPv6 的最佳方式?
Posted
技术标签:
【中文标题】在 UNKNOWN DB 中存储 IPv6 的最佳方式?【英文标题】:Best way to store an IPv6 in UNKNOWN DB? 【发布时间】:2013-01-03 20:34:35 【问题描述】:我知道这个问题已经被问过很多次了,但我找不到任何最终解决方案;所以这里是:
如何在不知道数据库的情况下使用最佳实践在数据库中存储 IP(IPv4 和 IPv6)?这以用于 DB 抽象目的,例如 php PDO。
顺便说一句,我有 PHP 5.2.17,因为我需要 Windows 上的 PEAR。
人们一直建议将其存储为 varbinary(16) 并使用 mysql 函数 inet6_ntop 和 inet6_pton 将 IP 作为字符串来回传递;像Extending MySQL 5 with IPv6 functions 建议。在 PHP 中,函数 inet_pton() 和 inet_ntop() 可以将 IPv4 和 IPv6 来回转换为二进制格式,如ThiefMaster in this question is suggesting,但不清楚如何将二进制内容传递到 SQL INSERT/UPDATE 字符串(并且这些 php 函数仅由php 5.3.0 on windows 提供,即使可以对这些函数进行逆向工程)。我真的很喜欢Jake 和his results with regards to integer representations of IPs in DBs 所做的事情,如果我要将它实现到我的数据库中,这可能会在遥远的、不可预见的未来派上用场,但是我不确定数据库抽象的数据库交叉兼容性使用 PHP PDO。 This post 似乎提供了一个关于存储二进制值的接近答案,但未转义的二进制注入字符串不是潜在的危险吗?另外,如果您遵循这条路线,如果某些开发人员想要进行一些快速查找,有多少 DB 可以将 varbinary(16)/int(128bit) 转换为代表性 IP?
在我看来,最简单的方法是将ip string as-is 插入varchar(45)。但是那些想要遵循复杂路线的人如何使用 inet_ntop() 和 inet_pton() 函数在 PHP(逆向工程 as djmaze(AT)dragonflycms(.)org 或 MagicalTux at FF dot st 建议)中将 IPv6 存储和检索为二进制文件?有人可以举个例子,使用来自<?php $strIP = "2001:4860:b002::68"; ?>
的 PDO,使用 INSERT 然后 SELECT 准备好的语句?
如您所见,我已经完成了研究,但我并不清楚 IPv6 的最终良好实践。
【问题讨论】:
这真的取决于您需要能够使用 IP 地址做什么。如果您只需要存储和检索它,或者将其与另一个 IP 地址进行完全匹配,则可以使用文本字符串。然后,您可以在二进制格式客户端进行所有转换(无论如何,在插入时您都需要这样做一次,以保证地址是规范形式)。如果您需要进行涉及前缀长度匹配的数据库查询(例如,给我前缀 2001:db8:1234::/46 所涵盖的所有 IP 地址),文本字符串会变得更加不方便。 【参考方案1】:正如您的研究表明,将 IP 地址存储为规范形式的字符串、二进制字符串或整数存在好处和问题。也许在数据库中存储 IP 地址是一种中间立场。
如何将它们存储为字符串,但扩展到最大长度。这样您仍然可以比较它们(==、>、
如何做到这一点的示例:
function expand_ip_address($addr_str)
/* First convert to binary, which also does syntax checking */
$addr_bin = @inet_pton($addr_str);
if ($addr_bin === FALSE)
return FALSE;
/* Then differentiate between IPv4 and IPv6 */
if (strlen($addr_bin) == 4)
/* IPv4: print each byte as 3 digits and add dots between them */
return implode('.', array_map(
create_function('$byte', 'return sprintf("%03d", ord($byte));'),
str_split($addr_bin)
));
else
/* IPv6: print as hex and add colons between each group of 4 hex digits */
return implode(':', str_split(bin2hex($addr_bin), 4));
还有一些例子:
/* Test IPv4 */
var_dump(expand_ip_address('192.0.2.55'));
/* Test IPv6 */
var_dump(expand_ip_address('2001:db8::abc'));
/* Test invalid */
var_dump(expand_ip_address('192:0:2:55'));
哪些产品:
string(15) "192.000.002.055"
string(39) "2001:0db8:0000:0000:0000:0000:0000:0abc"
bool(false)
【讨论】:
那太好了!但是例如0000:0000:0000:0000:0000:0000:192.168.0.1
呢?
另外,我认为如果您要将它们存储在数据库中,用 0000 填充所有 IP 部分(或者它们被称为)确实是有意义的,但这可能不再特别需要对于 IPv4。如果没有其他数字,我将如何更改您的代码以仅反映 1 个“0”? (我认为这部分你有一个相当复杂的array_map(create_function(...))
如果您不希望 IPv4 使用此功能,那么我将发送至 return inet_ntop($addr_bin);
。但是您不能再像<
和>
这样的运算符来搜索地址范围。如果每个数字的长度相同,则字符串比较和数字比较会给出相同的结果(因为所有点都在同一个位置)。像0000:0000:0000:0000:0000:0000:192.168.0.1
这样的地址写成0000:0000:0000:0000:0000:0000:c0a8:0001
,这是完全相同的地址,但使用十六进制表示法。
是的:array_map 部分有点复杂。我可能不应该试图把它全部放在一行上:-)以上是关于在 UNKNOWN DB 中存储 IPv6 的最佳方式?的主要内容,如果未能解决你的问题,请参考以下文章
Django - 在DB答案中存储可以是数字还是文本的最佳方法是什么?
最佳实践:通过存储在 DB 中的方案在运行时创建 ORM 对象