首页 >> 大全

iOS中关于苹果审核IPv6的问题

2023-06-22 大全 58 作者:考证青年

在上苹果宣布iOS9将支持纯IPv6的网络服务。2016年初开始所有提交到App Store的应用必须支持IPv6。有好多人都是因为IPv6的问题上架被拒了,让人心惊胆战。

总的来说有三个方面需要进行检查和修改:

1、项目里面涉及和网络有关的网络请求是否支持ipv6。(如,,这些三方最好更新到最新的)

2、项目里面涉及和网络有关的sdk是否支持ipv6。(如环信的聊天SDK等)

3、项目的服务器是否支持ipv6。(这个很容易忽略,这个让后台去整就可以了)

有以下几条建议:

1.不建议使用底层的网络API

2.不要用IP地址

3.检查不兼容IPv6的代码

可以使用Mac本地搭建IPv6的测试环境,测试应用是否支持IPv6访问

附上链接:本地如何搭建IPv6环境测试你的APP

一、IPV6-Only支持是啥?

首先IPV6,是对IPV4地址空间的扩充。目前当我们用iOS设备连接上Wifi、4G、3G等网络时,设备被分配的地址均是IPV4地址,但是随着运营商和企业逐渐部署IPV6 DNS64/NAT64网络之后,设备被分配的地址会变成IPV6的地址,而这些网络就是所谓的IPV6-Only网络,并且仍然可以通过此网络去获取IPV4地址提供的内容。客户端向服务器端请求域名解析,首先通过DNS64 查询IPv6的地址,如果查询不到,再向DNS 查询IPv4地址,通过DNS64 合成一个IPV6的地址,最终将一个IPV6的地址返回给客户端。如图所示:

NAT64-DNS64-.png

在Mac OS 10.11+的双网卡的Mac机器(以太网口+无线网卡),我们可以通过模拟构建这么一个local IPv6 DNS64/NAT64 的网络环境去测试应用是否支持IPV6-Only网络,大概原理如下:

.png

二、Apple如何审核支持IPV6-Only?

首先第一点:这里说的支持IPV6-Only网络,其实就是说让应用在 IPv6 DNS64/NAT64 网络环境下仍然能够正常运行。但是考虑到我们目前的实际网络环境仍然是IPV4网络,所以应用需要能够同时保证IPV4和IPV6环境下的可用性。从这点来说,苹果不会去扫描IPV4的专有API来拒绝审核通过,因为IPV4的API和IPV6的API调用都会同时存在于代码中(不过为了减小审核被拒风险,建议将IPV4专有API通过IPV6的兼容API来替换)。

其次第二点:Apple官方声明iOS9开始向IPV6支持过渡,在iOS9.2+支持通过方法将IPV4地址合成IPV6地址(The to IPv6 was added to in iOS 9.2 and OS X 10.11.2)。其提供的库在iOS8系统下,当从IPV4切换到IPV6网络,或者从IPV6网络切换到IPV4,是无法监控到网络状态的变化。也有一些开发者针对这些Bug询问Apple的审核部门,给予的答复是只需要在苹果最新的系统上保证IPV6的兼容性即可。

最后第三点:只要应用的主流程支持IPV6,通过苹果审核即可。对于不支持IPV6的模块,考虑到我们现实IPV6网络的部署还需要一段时间,短时间内不会影响我们用户的使用。但随着4G网络IPV6的部署,这部分模块还是需要逐渐安排人力进行支持。

追加第四点:如果应用一直直接使用IPV4地址通过或者进行网络请求(一般需要服务器允许,且客户端需要在中伪装host);经测试,IPV6网络环境下,直接使用IPV4地址在iOS9及以上的系统仍然能够正常访问;在iOS8.4及以下不能正常访问;这一点苹果的解释和建议是这样的:

Note: In iOS 9 and OS X 10.11 and later, and IPv6 from IPv4 on on DNS64/NAT64 . , you still work to rid your code of IP .

三、应用如何支持IPV6-Only?

对于如何支持IPV6-Only,官方给出了如下几点标准:(这里就不对其进行解释了,大家看上面的参考链接即可)

1. Use High-Level Networking Frameworks;
2. Don’t Use IP Address Literals;
3. Check Source Code for IPv6 DNS64/NAT64 Incompatibilities;
4. Use System APIs to Synthesize IPv6 Addresses;

3.1 是否支持IPV6?

官方的这句话让我们疑惑顿生:

using high-level APIs such as and the and you by name, you not need to for your app to work with IPv6

只说了和的API不需要改变,但是并没有提及到。 从上文的参考资料中,我们看到、同属于Cocoa的url ,可以猜测出在ios9上是支持IPV6的。

应用里面的API网络请求,大家一般都会选择进行请求发送,由于历史原因,应用的代码基本上都深度引用了on类,所以目前API网络请求均需要通过发送出去,所以必须确认是否支持IPV6. 经过测试,在最新的iOS9系统上是支持IPV6的。

3.2 Cocoa的URL 从iOS哪个版本开始支持IPV6?

目前我们的应用最低版本还需要支持iOS7,虽然苹果只要求最新版本支持IPV6-Only,但是出于对用户负责的态度,我们仍然需要搞清楚在低版本上URL 的API是否支持IPV6.

(to fix me, make some )待续~~~

3.3 是否需要修改支持IPV6?

我们可以查到应用中大量使用了进行网络状态判断,但是在里面却使用了IPV4的专用API。

Pods:ReachabilityAF_INET                  Files:Reachability.m
struct sockaddr_in       Files:Reachability.h , Reachability.m

那应该如何支持IPV6呢?

(1)目前的开源库的最新版本是3.2,苹果也出了一个 IPV6 的的官方样例,我们比较了一下源码,跟上的没有什么差异。

(2)我们通常都是通过一个0.0.0.0 ()去开启网络状态监控,经过我们测试,在iOS9以上的系统上IPV4和IPV6网络环境均能够正常使用;但是在iOS8上IPV4和IPV6相互切换的时候无法监控到网络状态的变化,可能是因为苹果在iOS8上还并没有对IPV6进行相关支持相关。(但是这仍然满足苹果要求在最新系统版本上支持IPV6的网络)。

(3)当大家都在要求添加对于IPV6的支持,其实苹果在iOS9以上对Zero 进行了特别处理,官方发言是这样的:

: This the 0.0.0.0,

which as a token that it to

the of the , both IPv4 and IPv6.

+ (instancetype)reachabilityForInternetConnection {struct sockaddr_in zeroAddress;bzero(&zeroAddress, sizeof(zeroAddress));zeroAddress.sin_len = sizeof(zeroAddress);zeroAddress.sin_family = AF_INET;return [self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress];
}

综上所述,不需要做任何修改,在iOS9上就可以支持IPV6和IPV4,但是在iOS9以下会存在bug,但是苹果审核并不关心。

四、底层的 API如何同时支持IPV4和IPV6?

由于在应用中使用了网络诊断的组件,大量使用了底层的 API,所以对于IPV6支持,这块是个重头戏。如果你的应用中使用了长连接,其必然会使用底层 API,这一块也是需要支持IPV6的。 对于如何同时支持IPV4和IPV6,可以参考谷歌的开源库.

下面我针对我们的开源 网络诊断组件, 说一下是如何同时支持IPV4和IPV6的。

开源地址:

这个网络诊断组件的主要功能如下:

4.1 IP地址从二进制到符号的转化

之前我们都是通过()进行二进制到符号,这个API只能转化IPV4地址。而()能够兼容转化IPV4和IPV6地址。 写了一个公用的的转化方法如下:

//for IPV6
+(NSString *)formatIPV6Address:(struct in6_addr)ipv6Addr{NSString *address = nil;char dstStr[INET6_ADDRSTRLEN];char srcStr[INET6_ADDRSTRLEN];memcpy(srcStr, &ipv6Addr, sizeof(struct in6_addr));if(inet_ntop(AF_INET6, srcStr, dstStr, INET6_ADDRSTRLEN) != NULL){address = [NSString stringWithUTF8String:dstStr];}return address;
}//for IPV4
+(NSString *)formatIPV4Address:(struct in_addr)ipv4Addr{NSString *address = nil;char dstStr[INET_ADDRSTRLEN];char srcStr[INET_ADDRSTRLEN];memcpy(srcStr, &ipv4Addr, sizeof(struct in_addr));if(inet_ntop(AF_INET, srcStr, dstStr, INET_ADDRSTRLEN) != NULL){address = [NSString stringWithUTF8String:dstStr];}return address;
}

4.2 本机IP获取支持IPV6

相当于我们在终端中输入命令获取字符串,然后对结果字符串进行解析,获取其中en0(Wifi)、(移动网络)的ip地址。

注意:

(1)在模拟器和真机上都会出现以FE80开头的IPV6单播地址影响我们判断,所以在这里进行特殊的处理(当第一次遇到不是单播地址的IP地址即为本机IP地址)。

(2)在IPV6环境下,真机测试的时候,第一个出现的是一个IPV4地址,所以在IPV4条件下第一次遇到单播地址不退出。

+ (NSString *)deviceIPAdress
{while (temp_addr != NULL) {NSLog(@"ifa_name===%@",[NSString stringWithUTF8String:temp_addr->ifa_name]);// Check if interface is en0 which is the wifi connection on the iPhoneif ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"] || [[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"pdp_ip0"]){//如果是IPV4地址,直接转化if (temp_addr->ifa_addr->sa_family == AF_INET){// Get NSString from C Stringaddress = [self formatIPV4Address:((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr];}//如果是IPV6地址else if (temp_addr->ifa_addr->sa_family == AF_INET6){address = [self formatIPV6Address:((struct sockaddr_in6 *)temp_addr->ifa_addr)->sin6_addr];if (address && ![address isEqualToString:@""] && ![address.uppercaseString hasPrefix:@"FE80"]) break;}}temp_addr = temp_addr->ifa_next;}}
}

4.3 设备网关地址获取获取支持IPV6

其实是在IPV4获取网关地址的源码的基础上进行了修改,初开把->, -> 之外,还需要注意如下修改,就是拷贝的地址字节数。去掉了的处理。 (解析出来的地址老是少了4个字节,结果是偏移量搞错了,纠结了半天),具体参考源码库。

   /* net.route.0.inet.flags.gateway */int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_FLAGS, RTF_GATEWAY};if (sysctl(mib, sizeof(mib) / sizeof(int), buf, &l, 0, 0) < 0) {address = @"192.168.0.1";}....//for IPV4for (i = 0; i < RTAX_MAX; i++) {if (rt->rtm_addrs & (1 << i)) {sa_tab[i] = sa;sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len));} else {sa_tab[i] = NULL;}}//for IPV6for (i = 0; i < RTAX_MAX; i++) {if (rt->rtm_addrs & (1 << i)) {sa_tab[i] = sa;sa = (struct sockaddr_in6 *)((char *)sa + sa->sin6_len);} else {sa_tab[i] = NULL;}}

4.4 设备DNS地址获取支持IPV6

IPV4时只需要通过进行初始化就可以获取,但是在IPV6环境下需要通过()接口才能获取。

+(NSArray *)outPutDNSServers{res_state res = malloc(sizeof(struct __res_state));int result = res_ninit(res);NSMutableArray *servers = [[NSMutableArray alloc] init];if (result == 0) {union res_9_sockaddr_union *addr_union = malloc(res->nscount * sizeof(union res_9_sockaddr_union));res_getservers(res, addr_union, res->nscount);for (int i = 0; i < res->nscount; i++) {if (addr_union[i].sin.sin_family == AF_INET) {char ip[INET_ADDRSTRLEN];inet_ntop(AF_INET, &(addr_union[i].sin.sin_addr), ip, INET_ADDRSTRLEN);NSString *dnsIP = [NSString stringWithUTF8String:ip];[servers addObject:dnsIP];NSLog(@"IPv4 DNS IP: %@", dnsIP);} else if (addr_union[i].sin6.sin6_family == AF_INET6) {char ip[INET6_ADDRSTRLEN];inet_ntop(AF_INET6, &(addr_union[i].sin6.sin6_addr), ip, INET6_ADDRSTRLEN);NSString *dnsIP = [NSString stringWithUTF8String:ip];[servers addObject:dnsIP];NSLog(@"IPv6 DNS IP: %@", dnsIP);} else {NSLog(@"Undefined family.");}}}res_nclose(res);free(res);return [NSArray arrayWithArray:servers];
}

4.4 域名DNS地址获取支持IPV6

在IPV4网络下我们通过获取,而在IPV6环境下,通过新的函数获取。

//ipv4
phot = gethostbyname(hostN);//ipv6phot = gethostbyname2(hostN, AF_INET6);

4.5 ping方案支持IPV6

Apple的官方提供了最新的支持IPV6的ping方案,参考地址如下:

只是需要注意的是:

(1)返回的去掉了部分,IPV6的部分也不返回TTL(Time to Live)字段;

(2)IPV6的ICMP报文不进行的处理;

4.6 方案支持IPV6

其实是通过创建套接字模拟ICMP报文的发送,以计算耗时;

两个关键的地方需要注意:

(1)IPV6中去掉字段,改用跳数来表示;

(2)方法可以兼容支持IPV4和IPV6,但是需要最后一个参数,制定目标IP地址的大小;因为前一个参数只是指明了IP地址的开始地址。千万不要用统一的( ), 因为 和 都是16个字节,两者可以通用,但是的数据结构是28个字节,如果不显式指定,方法就会一直返回-1,报22 的错误。

关键代码如下:(完整代码参考开源组件)

//构造通用的IP地址结构stuck sockaddrNSString *ipAddr0 = [serverDNSs objectAtIndex:0];//设置server主机的套接口地址NSData *addrData = nil;BOOL isIPV6 = NO;if ([ipAddr0 rangeOfString:@":"].location == NSNotFound) {isIPV6 = NO;struct sockaddr_in nativeAddr4;memset(&nativeAddr4, 0, sizeof(nativeAddr4));nativeAddr4.sin_len = sizeof(nativeAddr4);nativeAddr4.sin_family = AF_INET;nativeAddr4.sin_port = htons(udpPort);inet_pton(AF_INET, ipAddr0.UTF8String, &nativeAddr4.sin_addr.s_addr);addrData = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)];} else {isIPV6 = YES;struct sockaddr_in6 nativeAddr6;memset(&nativeAddr6, 0, sizeof(nativeAddr6));nativeAddr6.sin6_len = sizeof(nativeAddr6);nativeAddr6.sin6_family = AF_INET6;nativeAddr6.sin6_port = htons(udpPort);inet_pton(AF_INET6, ipAddr0.UTF8String, &nativeAddr6.sin6_addr);addrData = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];}struct sockaddr *destination;destination = (struct sockaddr *)[addrData bytes];//创建socket
if ((recv_sock = socket(destination->sa_family, SOCK_DGRAM, isIPV6?IPPROTO_ICMPV6:IPPROTO_ICMP)) < 0)
if ((send_sock = socket(destination->sa_family, SOCK_DGRAM, 0)) < 0)//设置sender 套接字的ttl
if ((isIPV6? 
setsockopt(send_sock,IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)):
setsockopt(send_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) < 0)//发送成功返回值等于发送消息的长度
ssize_t sentLen = sendto(send_sock, cmsg, sizeof(cmsg), 0, 
(struct sockaddr *)destination, 
isIPV6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in));

参考:

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了