getaddrinfo


SYNOPSIS 

#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *restrict nodename, /* host 或者IP地址 */
    const char *restrict servname, /* 十進制端口號 或者常用服務名稱如"ftp"、"http"等 */
    const struct addrinfo *restrict hints, /* 獲取信息要求設置 */
    struct addrinfo **restrict res); /* 獲取信息結果 */

void freeaddrinfo(struct addrinfo *ai); 

Data Structure

http://www.cnblogs.com/LubinLew/p/POSIX-DataStructure.html#struct_addrinfo

Description

IPv4中使用gethostbyname()函數完成主機名到地址解析,這個函數僅僅支持IPv4,

且不允許調用者指定所需地址類型的任何信息,返回的結構只包含了用於存儲IPv4地址的空間。

IPv6中引入了新的API getaddrinfo(),它是協議無關的,既可用於IPv4也可用於IPv6。

getaddrinfo() 函數能夠處理名字到地址以及服務到端口這兩種轉換,返回的是一個 struct addrinfo 的結構體(列表)指針而不是一個地址清單。

這些 struct addrinfo 結構體隨后可由套接口函數直接使用。如此以來,getaddrinfo()函數把協議相關性安全隱藏在這個庫函數內部。

應用程序只要處理由getaddrinfo()函數填寫的套接口地址結構。

Parameter

1) nodename

主機名("www.baidu.com")或者是數字化的地址字符串(IPv4的點分十進制串("192.168.1.100")或者IPv6的16進制串("2000::1:2345:6789:abcd")),

如果 ai_flags 中設置了AI_NUMERICHOST 標志,那么該參數只能是數字化的地址字符串,不能是域名,

該標志的作用就是阻止進行域名解析。

nodename 和 servname 可以設置為NULL,但是同時只能有一個為NUL。

2) servname

服務名可以是十進制的端口號("8080")字符串,也可以是已定義的服務名稱,如"ftp"、"http"等,詳細請查看/etc/services 文件,

最后翻譯成對應服務的端口號。如果此參數設置為NULL,那么返回的socket地址中的端口號不會被設置。

如果 ai_flags 設置了AI_NUMERICSERV 標志並且該參數未設置為NULL,那么該參數必須是一個指向10進制的端口號字符串,

不能設定成服務名,該標志就是用來阻止服務名解析。

 

3) hints

該參數指向用戶設定的 struct addrinfo 結構體,只能設定該結構體中 ai_family、ai_socktype、ai_protocol 和 ai_flags 四個域,

其他域必須設置為0 或者 NULL, 通常是申請 結構體變量后使用memset()初始化再設定指定的四個域。

該參數可以設置為NULL,等價於 ai_socktype = 0, ai_protocol = 0,ai_family = AF_UNSPEC, 

ai_flags = 0 (在GNU Linux中ai_flag = AI_V4MAPPED | AI_ADDRCONFIG,可以看作是GNU的改進)。

 ① ai_family

  指定返回地址的協議簇,取值范圍:AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNSPEC(IPv4 and IPv6)

   ② ai_socktype

  具體類型請查看struct addrinfo 中的 enum __socket_type 類型,用於設定返回地址的socket類型,

  常用的有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW, 設置為0表示所有類型都可以。

 ③ ai_protocol

  具體取值范圍請查看 Ip Protocol ,常用的有 IPPROTO_TCP、IPPROTO_UDP 等設置為0表示所有協議。

 ④ ai_flags

  附加選項,多個選項可以使用或操作進行結合,具體取值范圍請查看struct addrinfo , 常用的標志如下:

  • AI_PASSIVE

    如果設置了 AI_PASSIVE 標志,並且 nodename 是 NULL, 那么返回的socket地址可以用於的bind()函數,

       返回的地址是通配符地址(wildcard address, IPv4時是INADDR_ANY,IPv6時是IN6ADDR_ANY_INIT),

       這樣應用程序(典型是server)就可以使用這個通配符地址用來接收任何請求主機地址的連接,

       如果 nodename 不是NULL,那么 AI_PASSIVE 標志被忽略;

    如果未設置AI_PASSIVE標志,返回的socket地址可以用於connect(), sendto(), 或者 sendmsg()函數。

    如果 nodename 是NULL,那么網絡地址會被設置為lookback接口地址(IPv4時是INADDR_LOOPBACK,IPv6時是IN6ADDR_LOOPBACK_INIT),

    這種情況下,應用是想與運行在同一個主機上另一個應用通信。

  • AI_CANONNAME

     請求canonical(主機的official name)名字。如果設置了該標志,那么 res 返回的第一個 struct addrinfo 中的 ai_canonname 域會存儲official name的指針。

  • AI_NUMERICHOST

     阻止域名解析,具體見 nodename 中的說明。

  • AI_NUMERICSERV

    阻止服務名解析,具體見 servname 中的說明。

  • AI_V4MAPPED

     當 ai_family 指定為AF_INT6(IPv6)時,如果沒有找到IPv6地址,那么會返回IPv4-mapped IPv6 地址,

    也就是說如果沒有找到AAAA record(用來將域名解析到IPv6地址的DNS記錄),那么就查詢A record(IPv4),

    將找到的IPv4地址映射到IPv6地址, IPv4-mapped IPv6 地址其實是IPv6內嵌IPv4的一種方式,

    地址的形式為"0::FFFF:a.b.c.d",例如"::ffff:192.168.89.9"(混合格式)這個地址仍然是一個IPv6地址,

           只是"0000:0000:0000:0000:0000:ffff:c0a8:5909"(16機制格式)的另外一種寫法罷了。

    當 ai_family 不是AF_INT6(IPv6)時,該標志被忽略。

  • AI_ALL

     查詢IPv4和IPv6地址

  • AI_ADDRCONFIG

    只有當主機配置了IPv4地址才進行查詢IPv4地址;只有當主機配置了IPv6地址才進行查詢IPv6地址.

4) res

 該參數獲取一個指向存儲結果的 struct addrinfo 結構體列表,使用完成后調用 freeaddrinfo() 釋放存儲結果空間。

 

Return Value

如果 getaddrinfo() 函數執行成功,返回值為 0 , 其他情況返回值表示錯誤種別。使用函數gai_strerror() 可以獲取可讀性的錯誤信息,用法用strerror()相同,

錯誤種別如下:

  • EAI_ADDRFAMILY

    指定的主機上沒有請求的address family對應的網絡地址.

  • EAI_AGAIN

    DNS(name server)返回臨時性錯誤. 可以稍后重試.

  • EAI_BADFLAGS

    hints.ai_flags 包含了無效的標志; 或者 hints.ai_flags 包含了 AI_CANONNAME 標志但是 name 是 NULL.

  • EAI_FAIL

    DNS(name server)返回永久性錯誤

  • EAI_FAMILY

    不支持的 address family(hints.ai_family).

  • EAI_MEMORY

    內存耗盡.

  • EAI_NODATA

    指定的網絡主機存在,但是其未定義任何網絡地址.

  • EAI_NONAME

    nodename 或者 servname 未知;或者兩者都設置為NULL;

    或者設置了 AI_NUMERICSERV 標志但是 servname 不是一個數字化的端口名字符串。

  • EAI_SERVICE

    請求的socket類型不支持請求的服務類型.例如服務類型是 "shell" (基於流的socket服務),

    但是 hints.ai_protocol 是  IPPROTO_UDP 或者hints.ai_socktype 是 SOCK_DGRAM;

    或者 servname 不是NULL 但是 hints.ai_socktype 是 SOCK_RAW (原始套接字不支持服務的概念).

  • EAI_SOCKTYPE

    不支持請求的socket類型. 例如, hints.ai_socktype 和 hints.ai_protocol 沖突 (例如分別是SOCK_DGRAM、IPPROTO_TCP).

  • EAI_SYSTEM

    系統調用錯誤,檢查 errno.

Example

man getaddrinfo 最后有客戶端和服務器端2個例子。

下面是一個獲取google https 所有服務地址(IPv4 和 IPv6 的例子)

 

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

#define BUF_SIZE 500

int main(int argc, char *argv[])
{
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    int sfd, s, j;
    size_t len;
    ssize_t nread;
    char buf[BUF_SIZE];
    struct sockaddr_in  *ipv4;
    struct sockaddr_in6 *ipv6;

    /* Obtain address(es) matching host/port */
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_ALL;
    hints.ai_protocol = IPPROTO_TCP;

    s = getaddrinfo("www.google.com", "https", &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        exit(EXIT_FAILURE);
    }

    for (rp = result; rp != NULL; rp = rp->ai_next) {
        switch (rp->ai_family) {
        case AF_INET:
            ipv4 = (struct sockaddr_in *)rp->ai_addr;
            inet_ntop(rp->ai_family, &ipv4->sin_addr, buf, sizeof(buf));
            break;
        case AF_INET6:
            ipv6 = (struct sockaddr_in6 *)rp->ai_addr;
            inet_ntop(rp->ai_family, &ipv6->sin6_addr, buf, sizeof(buf));
            break;
        }
        
        printf("[IPv%d]%s\n", rp->ai_family==AF_INET?4:6, buf);
    }

    /* No longer needed */
    freeaddrinfo(result);           

    exit(EXIT_SUCCESS);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM