本文介绍了应用程序在IPv6网络上进行了审查.请确保您的应用支持IPv6网络,因为需要IPv6兼容性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近几天,我收到来自Apple的以下拒绝我的应用程序的请求.我的应用程序与UDP通信,并且远程服务器始终为IPv4.我用过BSD插槽.请指导我如何解决这个问题.

I have received the following rejection from Apple for my app the last couple of days. My app communicates with UDP and the remote server is always IPv4. I have used BSD sockets. Please guide me how can I solve this problem.

我尝试使用IPv4网络创建NAT64热点,但无法将任何数据包发送到服务器.而且,我们现在没有IPv6可用.

I have tried to create a NAT64 hotspot using an IPv4 network but I was unable to send any packets to the server. Moreover, we don't have IPv6 available at my place now.

从Apple:

在Wi-Fi和蜂窝网络上运行iOS 9.3.2的iPad和运行iOS 9.3.2的iPhone上进行检查时,我们发现您的应用程序中存在一个或多个错误.

We discovered one or more bugs in your app when reviewed on an iPad running iOS 9.3.2 and iPhone running iOS 9.3.2 on both Wi-Fi and cellular networks.

尤其是,在审核过程中,我们无法绕过初始化"页面.等待加载应用程序时遇到错误.我们已附上屏幕截图供您参考.

Specifically, during review we were unable to bypass the Initializing page. We encountered an error while waiting for the app to load. We've attached a screenshot for your reference.

后续步骤

请在设备上运行您的应用,以识别问题,然后修改并重新提交您的应用以供审核.

Please run your app on a device to identify the issue(s), then revise and resubmit your app for review.

在IPv6网络上审查了应用程序.请确保您的应用支持IPv6网络,因为需要IPv6兼容性.

Apps are reviewed on an IPv6 network. Please ensure that your app supports IPv6 networks, as IPv6 compatibility is required.

有关支持IPv6网络的其他信息,请参阅支持iPv6 DNS64/NAT64网络.

For additional information about supporting IPv6 Networks, please refer to Supporting iPv6 DNS64/NAT64 Networks.

下面的源代码:

UdpSocketManager.h >>

UdpSocketManager.h >>

#ifndef UDP_SOCKET_MANAGER_H__
#define UDP_SOCKET_MANAGER_H__

#import "TInetAddr.h"

class UdpSocketManager
{
public:
    UdpSocketManager();
    ~UdpSocketManager();

    void getLocalAddress();
    void initializeSocket();
    void start();
    void stop();

    void sendSignal(int p_type, TInetAddr *p_destAddress, unsigned char *p_data, int p_length);
    void receiveSignal();

    int localPort;
    int signalingSocket;
    int signalSocketRecvLength;
    int socketFamily;

    int isIPV4Available;
    int isIPV6Available;

    char wifiIP[INET_ADDRSTRLEN];
    char cellularIP[INET_ADDRSTRLEN];

    char wifiIP_v6[INET6_ADDRSTRLEN];
    char cellularIP_v6[INET6_ADDRSTRLEN];

    long returnLength;
    struct sockaddr_in remoteAddrForRecv;

    struct sockaddr_in  srcAddrV4;
    struct sockaddr_in6 srcAddrV6;

    struct sockaddr_in  sendAddr4;
    struct sockaddr_in6 sendAddr6;

    bool running;
    pthread_t thread;
};

#endif

UdpSocketManager.m >>

UdpSocketManager.m >>

#include <string.h>
#include <pthread.h>
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#include "UdpSocketManager.h"
#import <Foundation/Foundation.h>

unsigned int ipAddressToUnsignedInt(char *ipAddress)
{
    unsigned int ipAddressLongValue = 0L;
    int byteSegment = 0;

    for(int i = 0; i < strlen(ipAddress); i++)
    {
        char ch = ipAddress[i];
        if(ch == '.')
        {
            ipAddressLongValue <<= 8;
            ipAddressLongValue |= byteSegment;
            byteSegment = 0;
        }
        else
        {
            byteSegment = byteSegment * 10 + (ch - 48);
        }
    }

    ipAddressLongValue <<= 8;
    ipAddressLongValue |= byteSegment;
    return ipAddressLongValue;
}

int custom_random(int max=65535)
{
    int randomValue;
    randomValue = arc4random_uniform(65535)%max;
    return randomValue;
}

int custom_random(int min, int max)
{
    int randomValue;
    randomValue = arc4random_uniform(max);
    if(randomValue<min)
        randomValue=(min+custom_random(max-min));
    return randomValue;
}



void* runUdpSocketManager(void *objRef)
{
    UdpSocketManager *THIS = (UdpSocketManager *) objRef;
    THIS->running=true;

    while (THIS->running)
    {
        THIS->receiveSignal();
    }

    pthread_exit(NULL);
    return 0;
}


UdpSocketManager::UdpSocketManager()
{
    socketFamily=AF_INET;
    signalingSocket=-1;
    running=false;
    initializeSocket();
}

UdpSocketManager::~UdpSocketManager()
{

}

void UdpSocketManager::getLocalAddress()
{
    //Read local address
    getLocalAddress();

    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;
    int success=0;

    isIPV4Available=FALSE;
    isIPV6Available=FALSE;
    success = getifaddrs(&interfaces);

    if (success == 0)
    {
        // Loop through linked list of interfaces
        temp_addr = interfaces;
        while(temp_addr != NULL)
        {
            if(temp_addr->ifa_addr->sa_family==AF_INET)
            {
                if([[NSString stringWithUTF8String:temp_addr->ifa_name] hasPrefix:@"en"])
                {
                    isIPV4Available=TRUE;
                    strcpy(wifiIP, inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr));
                    printf("IP Address: %s\n",inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr));
                }
                else if([[NSString stringWithUTF8String:temp_addr->ifa_name] hasPrefix:@"pdp_ip0"])
                {
                    isIPV4Available=TRUE;
                    strcpy(cellularIP, inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr));
                    printf("IP Address: %s\n",inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr));
                }
            }
            else if(temp_addr->ifa_addr->sa_family==AF_INET6)
            {
                if([[NSString stringWithUTF8String:temp_addr->ifa_name] hasPrefix:@"en"])
                {
                    isIPV6Available=TRUE;
                    inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)temp_addr->ifa_addr)->sin6_addr), (char*)wifiIP_v6, INET6_ADDRSTRLEN);
                    printf("Interface: %s IPV6: %s\n",temp_addr->ifa_name,wifiIP_v6);
                }
                else if([[NSString stringWithUTF8String:temp_addr->ifa_name] hasPrefix:@"pdp_ip0"])
                {
                    isIPV6Available=TRUE;
                    inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)temp_addr->ifa_addr)->sin6_addr), (char*)cellularIP_v6, INET6_ADDRSTRLEN);
                    printf("Interface: %s IPV6: %s\n",temp_addr->ifa_name,cellularIP_v6);
                }
            }
            temp_addr = temp_addr->ifa_next;
        }
    }
    freeifaddrs(interfaces);
}

void UdpSocketManager::initializeSocket()
{
    if(signalingSocket!=-1)
        close(signalingSocket);

    if (isIPV4Available)
    {
        if((signalingSocket=socket(AF_INET, SOCK_DGRAM, 0))==-1)
        {
            NSLog(@"Unable to create signaling socket of AF_INET");
        }
        else
        {
            socketFamily=AF_INET;
            NSLog(@"Socket created successfully. [AF_INET]");
        }
    }
    else if(!isIPV4Available && isIPV6Available)
    {
        if((signalingSocket=socket(AF_INET6, SOCK_DGRAM, 0))==-1)
        {
            NSLog(@"Unable to create signaling socket of AF_INET6");
        }
        else
        {
            socketFamily=AF_INET6;
            NSLog(@"Socket created successfully. [AF_INET6]");
        }
    }
    else
    {
        if((signalingSocket=socket(AF_INET, SOCK_DGRAM, 0))==-1)
        {
            NSLog(@"Unable to create signaling socket of AF_INET");
        }
        else
        {
            socketFamily=AF_INET;
            NSLog(@"Socket created successfully. [AF_INET]");
        }
    }

    int count=0;
    while(true)
    {
        count++;
        if(socketFamily==AF_INET)
        {
            srcAddrV4.sin_len = sizeof(srcAddrV4);
            srcAddrV4.sin_family = socketFamily;
            srcAddrV4.sin_addr.s_addr = INADDR_ANY;
            srcAddrV4.sin_port = htons(localPort);

            if (bind(signalingSocket, (struct sockaddr *) &srcAddrV4, sizeof(srcAddrV4)) < 0)
            {
                NSLog(@"[AF_INET] ERROR occured creating signaling port at attempt (%d) Port: %d", count, localPort);
                localPort=(int)custom_random(1024, 65535);
            }
            else
            {
                int on=1;
                setsockopt(signalingSocket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&on, sizeof(on));
                setsockopt(signalingSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));

                NSLog(@"[AF_INET] SignalingSocket Created Successfully at attempt (%d) Port: %d\n", count, localPort);
                break;
            }
        }
        else
        {
            srcAddrV6.sin6_len = sizeof(srcAddrV6);
            srcAddrV6.sin6_family = socketFamily;
            srcAddrV6.sin6_addr = in6addr_any;
            srcAddrV6.sin6_port = htons(localPort);

            if (bind(signalingSocket, (struct sockaddr *) &srcAddrV6, sizeof(srcAddrV6)) < 0)
            {
                NSLog(@"[AF_INET] ERROR occured creating signaling port at attempt (%d) Port: %d", count, localPort);
                localPort=(int)custom_random(1024, 65535);
            }
            else
            {
                int on=1;
                setsockopt(signalingSocket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&on, sizeof(on));
                setsockopt(signalingSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
                NSLog(@"[AF_INET6] SignalingSocket Created Successfully at attempt (%d) Port: %d\n", count, localPort);
                break;
            }
        }
    }
}

void UdpSocketManager::start()
{
    pthread_create(&thread, NULL, runUdpSocketManager, (void *) this);
}

void UdpSocketManager::stop()
{
    running=false;
}

void UdpSocketManager::receiveSignal()
{
    int port;
    char ipAddress[16];

    socklen_t fromlen;
    unsigned char udpSignalRecvBuffer[1600];

    fromlen = sizeof(remoteAddrForRecv);
    signalSocketRecvLength = (int)recvfrom(signalingSocket, (char *)udpSignalRecvBuffer,1600,0,(struct sockaddr *)&remoteAddrForRecv,&fromlen);

    if(signalSocketRecvLength>0)
    {
        strcpy(ipAddress, inet_ntoa(remoteAddrForRecv.sin_addr));
        port = ntohs(remoteAddrForRecv.sin_port);
        NSLog(@"RECEIVED %d bytes from %s:%d", signalSocketRecvLength, ipAddress, port);
    }
    else
    {
        usleep(10000);// 10 ms
    }
}


void UdpSocketManager::sendSignal(int p_type, TInetAddr *p_destAddress, unsigned char *p_data, int p_length)
{
    if(socketFamily==AF_INET6)
    {
        // Convert IPv4 address to IPv4-mapped-into-IPv6 address.
        sendAddr6.sin6_family = AF_INET6;
        sendAddr6.sin6_port = p_destAddress->m_port;

        sendAddr6.sin6_addr.__u6_addr.__u6_addr32[0] = 0;
        sendAddr6.sin6_addr.__u6_addr.__u6_addr32[1] = 0;
        sendAddr6.sin6_addr.__u6_addr.__u6_addr32[2] = htonl(0xffff);
        sendAddr6.sin6_addr.__u6_addr.__u6_addr32[3] = ntohl(ipAddressToUnsignedInt(p_destAddress->m_address));

        sendAddr6.sin6_addr.__u6_addr.__u6_addr16[4] = 0;
        sendAddr6.sin6_addr.__u6_addr.__u6_addr16[5] = 0xffff;

        char ipV6Address[INET6_ADDRSTRLEN];
        inet_ntop(AF_INET6, &sendAddr6.sin6_addr, ipV6Address, INET6_ADDRSTRLEN);
        NSLog(@"ipV6Address: %s\n", ipV6Address);

        sendAddr6.sin6_flowinfo = 0;
        sendAddr6.sin6_scope_id = 0;
    }
    else
    {
        sendAddr4.sin_family = AF_INET;
        sendAddr4.sin_port   = htons(p_destAddress->m_port);

        if(inet_aton((char *) p_destAddress->m_address, &sendAddr4.sin_addr)==0)
        {
            NSLog(@"signal message - inet_aton() failed, %s", p_destAddress->m_address);
        }
    }


    if(socketFamily==AF_INET)
        returnLength=sendto(signalingSocket, p_data, p_length, 0, (struct sockaddr *)&sendAddr4, sizeof(sendAddr4));
    else
        returnLength=sendto(signalingSocket, p_data, p_length, 0, (struct sockaddr *)&sendAddr6, sizeof(sendAddr6));

    NSLog(@"SENT %ld bytes to %s:%d\n", returnLength,p_destAddress->m_address,p_destAddress->m_port);
}

推荐答案

我认为您的将IPv4地址转换为映射到IPv4的IPv6地址"是不正确的.与其尝试自己构造地址,不如使用要连接的对象的字符串(可以是主机名或IPv4地址文字)调用getaddrinfo(),它将返回sockaddr s;您应该使用那里的第一个传递到sendto.它将为您提供适当的IP地址族来使用,如果您提供了IPv4地址并且它是仅IPv6的网络(无需测试任何内容),它将自动为您提供用于该NAT64的正确IPv6地址.路由器(无需您自己弄清楚). (如果要使用NAT64/DNS64从IPv4手动构建IPv6地址而不使用getaddrinfo(),则必须遵循 RFC 7050 .)

I don't think your "Convert IPv4 address to IPv4-mapped-into-IPv6 address" is correct. Rather than trying to construct an address yourself, you should call getaddrinfo() with a string of the thing you want to connect to (which could be either a hostname or an IPv4 address literal), and it will return to you a list of sockaddrs; you should use the first one from there to pass to sendto. It will give you the appropriate IP address family to use, and if you give an IPv4 address and it is an IPv6-only network (without you having to test anything), it will automatically give you the correct IPv6 address to use for that NAT64 router (without you needing to figure this out yourself). (If you wanted to manually construct an IPv6 address from IPv4 using NAT64/DNS64 without using getaddrinfo(), you would have to follow the complicated procedure in RFC 7050.)

此外,您在getLocalAddress()中所做的所有事情都是不必要的,并可能导致更多问题.您不需要isIPV4AvailableisIPV6Available-那时您无需理会.只需在一开始就创建并绑定两者一个IPv4和一个IPv6套接字(无需关心哪个工作原理),并且每次需要发送时,您都会获得正确的sockaddr,可以使用getaddrinfo()如上,然后发送到其地址族与您正在使用的sockaddr相对应的套接字.当您想接收时,可以在两个套接字上调用recvfrom.

Also, all the things you are doing in getLocalAddress() is unnecessary and potentially leads to more problems. You don't need isIPV4Available or isIPV6Available -- you shouldn't care at that point. Just create and bind both an IPv4 and an IPv6 socket in the beginning (not needing to care which one works), and each time you need to send, you get the right sockaddr to use using getaddrinfo() as above, and then send to the socket whose address family corresponds to the sockaddr you are using. And when you want to receive, you call recvfrom on both sockets.

这篇关于应用程序在IPv6网络上进行了审查.请确保您的应用支持IPv6网络,因为需要IPv6兼容性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 15:54