使用 Objective-C 确定 IPv6 地址是否在范围内
Determine if IPv6 Address is within range using Objective-C
我正在尝试找出最有效的方法来确定 IPv6 地址是否在某个地址范围内(包括起始地址和结束地址)。考虑到起始地址和结束地址之间可能存在的大量地址,我当然不想遍历范围以寻找匹配项。
我收到一些建议,尝试进行这种范围匹配不是一个好方法,或者 practical/useful 对于 IPv6 来说不是,我应该研究前缀匹配。在走那条路之前,我想看看可能存在哪些有效的解决方案来确定 IPv6 地址是否在给定范围内。
我不是网络专家,所以请原谅我在这件事上的一些无知。
我发现 this Stack Overflow post 有一个未被接受的答案,表明最好的方法是将 IPv6 地址的所有十六进制转换为二进制,然后进行比较。这个 post 有两个问题:1. 我不确定如何将 PHP 脚本改编成 Objective-C,以及 2. 这个答案有一些赞成票,但不是一个可以接受的答案所以我不确定这是否值得追求。
我还发现 this other Stack Overflow post 似乎很适合将十六进制转换为二进制,但同样,我不确定如何比较生成的二进制值以确定范围。请注意,我使用的是问题中的示例代码,而不是答案。使用示例 IPv6 地址 fe80::34cb:9850:4868:9d2c
:
fe80 = 1111111010000000
0000 = 0
0000 = 0
0000 = 0
34cb = 11010011001011
9850 = 1001100001010000
4868 = 100100001101000
9d2c = 1001110100101100
我试图实现的最终结果是能够在 Mac 的 IP 地址在定义范围内时做出反应。我已经有了适用于 IPv4 地址的解决方案,但我想为我的免费 macOS 应用程序 Amphetamine 添加 IPv6 支持。在此先感谢您的帮助。这就是我用于 IPv4 范围检查的内容...不确定它是否可以适用于 IPv6:
- (bool) ipAddress: (NSString *)ipAddress isBetweenIpAddress: (NSString *)rangeStart andIpAddress: (NSString *)rangeEnd
{
uint32_t ip = [self convertIpAddress: ipAddress];
uint32_t start = [self convertIpAddress: rangeStart];
uint32_t end = [self convertIpAddress: rangeEnd];
return ip >= start && ip <= end;
}
- (uint32_t) convertIpAddress: (NSString *) ipAddress
{
struct sockaddr_in sin;
inet_aton([ipAddress UTF8String], &sin.sin_addr);
return ntohl(sin.sin_addr.s_addr);
}
这道题需要两步:(1)从字符串中解析出地址和范围start/end; (2) 比较数值。可用于这两个部分的 API 都是基于 C 的,因此对用户来说不是特别友好。
放在一起:
NSString *rangeStart = @"2001:db8::";
NSString *rangeEnd = @"2001:db8::ffff:ffff:ffff:ffff";
NSString *address = @"2001:db8::abc";
// Parse strings into in6_addrs
struct in6_addr rangeStartAddr;
struct in6_addr rangeEndAddr;
struct in6_addr addr;
if (inet_pton(AF_INET6, rangeStart.UTF8String, &rangeStartAddr) != 1) {
abort();
}
if (inet_pton(AF_INET6, rangeEnd.UTF8String, &rangeEndAddr) != 1) {
abort();
}
if (inet_pton(AF_INET6, address.UTF8String, &addr) != 1) {
abort();
}
// Use memcmp to compare binary values
if (memcmp(rangeStartAddr.s6_addr, addr.s6_addr, 16) <= 0
&& memcmp(addr.s6_addr, rangeEndAddr.s6_addr, 16) <= 0) {
NSLog(@"In range");
} else {
NSLog(@"Not in range");
}
将每个地址转换为单个 __uint128_t
数字也是可能的,但很麻烦。但是,如果您需要做的只是比较,memcmp
似乎就足够了。
同样可以在 Swift 中使用 UnsafeRawBufferPointer.lexicographicallyPrecedes 而不是 memcmp:
import Darwin
extension in6_addr {
init?(_ str: String) {
self.init()
if inet_pton(AF_INET6, str, &self) != 1 {
return nil
}
}
}
extension in6_addr: Comparable {
public static func ==(lhs: in6_addr, rhs: in6_addr) -> Bool {
return withUnsafeBytes(of: lhs) { lhsBytes in
withUnsafeBytes(of: rhs) { rhsBytes in
lhsBytes.prefix(16).elementsEqual(rhsBytes.prefix(16))
}
}
}
public static func <(lhs: in6_addr, rhs: in6_addr) -> Bool {
return withUnsafeBytes(of: lhs) { lhsBytes in
withUnsafeBytes(of: rhs) { rhsBytes in
lhsBytes.prefix(16).lexicographicallyPrecedes(rhsBytes.prefix(16))
}
}
}
}
var rangeStartAddr = in6_addr("2001:db8::")!
var rangeEndAddr = in6_addr("2001:db8::ffff:ffff:ffff:ffff")!
var addr = in6_addr("2001:db8::abc")!
(rangeStartAddr...rangeEndAddr).contains(addr)
我正在尝试找出最有效的方法来确定 IPv6 地址是否在某个地址范围内(包括起始地址和结束地址)。考虑到起始地址和结束地址之间可能存在的大量地址,我当然不想遍历范围以寻找匹配项。
我收到一些建议,尝试进行这种范围匹配不是一个好方法,或者 practical/useful 对于 IPv6 来说不是,我应该研究前缀匹配。在走那条路之前,我想看看可能存在哪些有效的解决方案来确定 IPv6 地址是否在给定范围内。
我不是网络专家,所以请原谅我在这件事上的一些无知。
我发现 this Stack Overflow post 有一个未被接受的答案,表明最好的方法是将 IPv6 地址的所有十六进制转换为二进制,然后进行比较。这个 post 有两个问题:1. 我不确定如何将 PHP 脚本改编成 Objective-C,以及 2. 这个答案有一些赞成票,但不是一个可以接受的答案所以我不确定这是否值得追求。
我还发现 this other Stack Overflow post 似乎很适合将十六进制转换为二进制,但同样,我不确定如何比较生成的二进制值以确定范围。请注意,我使用的是问题中的示例代码,而不是答案。使用示例 IPv6 地址 fe80::34cb:9850:4868:9d2c
:
fe80 = 1111111010000000
0000 = 0
0000 = 0
0000 = 0
34cb = 11010011001011
9850 = 1001100001010000
4868 = 100100001101000
9d2c = 1001110100101100
我试图实现的最终结果是能够在 Mac 的 IP 地址在定义范围内时做出反应。我已经有了适用于 IPv4 地址的解决方案,但我想为我的免费 macOS 应用程序 Amphetamine 添加 IPv6 支持。在此先感谢您的帮助。这就是我用于 IPv4 范围检查的内容...不确定它是否可以适用于 IPv6:
- (bool) ipAddress: (NSString *)ipAddress isBetweenIpAddress: (NSString *)rangeStart andIpAddress: (NSString *)rangeEnd
{
uint32_t ip = [self convertIpAddress: ipAddress];
uint32_t start = [self convertIpAddress: rangeStart];
uint32_t end = [self convertIpAddress: rangeEnd];
return ip >= start && ip <= end;
}
- (uint32_t) convertIpAddress: (NSString *) ipAddress
{
struct sockaddr_in sin;
inet_aton([ipAddress UTF8String], &sin.sin_addr);
return ntohl(sin.sin_addr.s_addr);
}
这道题需要两步:(1)从字符串中解析出地址和范围start/end; (2) 比较数值。可用于这两个部分的 API 都是基于 C 的,因此对用户来说不是特别友好。
放在一起:
NSString *rangeStart = @"2001:db8::";
NSString *rangeEnd = @"2001:db8::ffff:ffff:ffff:ffff";
NSString *address = @"2001:db8::abc";
// Parse strings into in6_addrs
struct in6_addr rangeStartAddr;
struct in6_addr rangeEndAddr;
struct in6_addr addr;
if (inet_pton(AF_INET6, rangeStart.UTF8String, &rangeStartAddr) != 1) {
abort();
}
if (inet_pton(AF_INET6, rangeEnd.UTF8String, &rangeEndAddr) != 1) {
abort();
}
if (inet_pton(AF_INET6, address.UTF8String, &addr) != 1) {
abort();
}
// Use memcmp to compare binary values
if (memcmp(rangeStartAddr.s6_addr, addr.s6_addr, 16) <= 0
&& memcmp(addr.s6_addr, rangeEndAddr.s6_addr, 16) <= 0) {
NSLog(@"In range");
} else {
NSLog(@"Not in range");
}
将每个地址转换为单个 __uint128_t
数字也是可能的,但很麻烦。但是,如果您需要做的只是比较,memcmp
似乎就足够了。
同样可以在 Swift 中使用 UnsafeRawBufferPointer.lexicographicallyPrecedes 而不是 memcmp:
import Darwin
extension in6_addr {
init?(_ str: String) {
self.init()
if inet_pton(AF_INET6, str, &self) != 1 {
return nil
}
}
}
extension in6_addr: Comparable {
public static func ==(lhs: in6_addr, rhs: in6_addr) -> Bool {
return withUnsafeBytes(of: lhs) { lhsBytes in
withUnsafeBytes(of: rhs) { rhsBytes in
lhsBytes.prefix(16).elementsEqual(rhsBytes.prefix(16))
}
}
}
public static func <(lhs: in6_addr, rhs: in6_addr) -> Bool {
return withUnsafeBytes(of: lhs) { lhsBytes in
withUnsafeBytes(of: rhs) { rhsBytes in
lhsBytes.prefix(16).lexicographicallyPrecedes(rhsBytes.prefix(16))
}
}
}
}
var rangeStartAddr = in6_addr("2001:db8::")!
var rangeEndAddr = in6_addr("2001:db8::ffff:ffff:ffff:ffff")!
var addr = in6_addr("2001:db8::abc")!
(rangeStartAddr...rangeEndAddr).contains(addr)