2017-09-17 67 views
-6

Apple需要IPv6兼容性。似乎有點不成熟,但我認爲有人不得不迫使它被廣泛採用。低級別C API的IPv6兼容性問題

我有一位客戶,他的一些遺留代碼因爲不兼容而不再被批准用於App Store。我沒有自己編寫軟件,並且特別從未涉及過這部分代碼。當談到低級C網絡時,我並不是一個忍者。

我告訴客戶,問題的一部分是,他們有一個IPv4的服務器,並讓我們硬編碼到該服務器的IPv4地址。我將IPv4地址更新爲域名,並告訴他們他們的服務器必須支持IPv6。所以,他們把它們全部移開,在做任何測試之前翻轉開關。幾天前我收到通知,他們店裏的所有軟件都不再有效。這就是我們所處的困境。

這是潛在的很多問題之一,我可以使用一些幫助。

連接到端口的服務器不僅不響應IPv6,還有一些低級別的API正在使用中不兼容。

我遇到的第一件事情就是使用gethostbyname()。顯然這不是IPv6的能力。我一直試圖用getaddrinfo()來解決這個問題,但我的sockaddr並不完全一樣。

我能看到的第二個問題是,顯然我需要使用AF_UNSPEC而不是AF_INET。所以我試圖用以下方式打開一個插座:

int sockfd = socket(AF_UNSPEC, SOCK_STREAM, 0); 

我總是得到-1。

如果我使用AF_INET打開襪子我得到進一步的,但後來我有這樣的問題:

otherAddr sockaddr * 0x618000220680 0x0000618000220680 
    sa_len __uint8_t '\x1c' 
    sa_family sa_family_t '\x1e' 
    sa_data char [14] "\x13\x88" 
otherAddrCast sockaddr * 0x7000052d3c28 0x00007000052d3c28 
    sa_len __uint8_t '\0' 
    sa_family sa_family_t '\0' 
    sa_data char [14] "\x13\x88\"Ԛ\\" 

所以一些sa_data的是一樣的。如果我沒有將sockaddr_in轉換爲struct sockaddr*那麼sa_data是相同的。

問題是我不知道這是什麼意思。幫幫我?

編輯:這是我的代碼。假設我只得到1個地址回來(因爲我)

struct hostent* server = gethostbyname([_hostname UTF8String]); 
    struct addrinfo *ai; 
    struct addrinfo hints; 


    memset(&hints, 0x00, sizeof(hints)); 
    hints.ai_family = AF_INET; 
    hints.ai_socktype = SOCK_STREAM; 

    NSString* portString = [NSString stringWithFormat:@"%d", _port]; 
    getaddrinfo([_hostname UTF8String], [portString cStringUsingEncoding:kCFStringEncodingASCII] , &hints, &ai); 

    struct sockaddr_in addr = {0}; 
    addr.sin_family = AF_INET; 
    bcopy(server->h_addr, &addr.sin_addr.s_addr, server->h_length); 
    addr.sin_port = htons(_port); 
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); 

    _addr = (struct sockaddr*)&addr; 
    int status = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)); 
    int status2 = connect(socket(ai->ai_family,ai->ai_socktype,0), ai->ai_addr, ai->ai_addrlen); 
+1

你可能需要找到一個IPv6套接字教程。這個問題可能是一個寬泛的stackoverflow。 –

+1

「看起來有點不成熟」 - 是的,只是IPv4地址已經用完了,而且IPv6只有大約20年的標準化。絕對過早... – Olaf

+1

我發現http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html非常有幫助 –

回答

1

你不應該使用AF_UNSPECsocket()可言。您必須使用AF_INET(IPv4)或AF_INET6(IPv6)。

您可以使用AF_UNSPEChints輸入參數getaddrinfo()來指示您願意接受IPv4和IPv6地址作爲輸出。輸出中的實際地址將爲AF_INETAF_INET6。或者,您可以將提示設置爲AF_INET以獲取僅限IPv4的輸出。或AF_INET6僅用於IPv6輸出。

您應該遍歷由getaddrinfo()返回的列表。對於列表中的每個地址:

  • 通過它ai_familyai_socktype,並ai_protocol領域socket()
  • 然後通過它ai_addrai_addrlen領域bind()(服務器)或connect()(客戶端)。

對於爲監聽服務器報告的所有地址以及爲客戶端報告的所有地址重複此操作,直到成功連接一個爲止。

這樣,每次創建插座,它正在與地址的IP版本相匹配,並要傳遞或適當匹配sockaddr_in(IPv4)的(IPv6)可將bind()/connect()

一旦你成功監聽的服務器套接字(S),或成功連接客戶端套接字,如果你需要使用accept()getsockname()getpeername()檢索從插座的IP,一定要傳遞一個sockaddr_storage結構填寫sockaddr_storage足夠大以容納所有定義的基於sockaddr的結構(並且有很多)。如果成功,則可以根據其sa_family字段(分別爲AF_INETAF_INET6)將其類型轉換爲sockaddr_in或。其他類似的功能也是如此,如inet_pton()inet_ntop()


更新:給你顯示的客戶端代碼,試試這個來代替:

struct addrinfo hints = {0}; 
hints.ai_family = AF_INET; 
hints.ai_socktype = SOCK_STREAM; 
hints.ai_protocol = IPPROTO_TCP; 

NSString* portString = [NSString stringWithFormat:@"%d", _port]; 

struct addrinfo *ai; 
int sockfd = -1; 

int status = getaddrinfo([_hostname UTF8String], [portString cStringUsingEncoding:kCFStringEncodingASCII] , &hints, &ai); 
if (status == 0) { 
    sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 
    if (sockfd == -1) { 
     status = errno; 
    } 
    else if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { 
     status = errno; 
     close(sockfd); 
     sockfd = -1; 
    } 
    freeaddrinfo(ai); 
} 

if (sockfd == -1) { 
    // handle status error as needed... 
} 
else { 
    // use sockfd as needed... 
    close(sockfd); 
} 
+0

Downvoter,請解釋你爲什麼downvote。 –

+0

謝謝你回答我的問題。我很少處理任何低級別的API。我很欣賞你花時間理解,儘管這些問題我沒有辦法進一步完善,因爲這些圖書館缺乏經驗。你花時間來解釋我對常量和類型的使用方式沒有意義,而其他人則只是將它們標記爲過於寬泛。再次謝謝你。 – Xials

+0

我對此有了進一步的瞭解,但不幸的是,我無法分辨爲什麼我的結果是不同的。請參閱上面的編輯 – Xials