從實(shí)現(xiàn)代碼可以看出,函數(shù)的內(nèi)部實(shí)際的地址轉(zhuǎn)換過(guò)程是由函數(shù)find_address完成的。不過(guò)在調(diào)用find_address之前,函數(shù)進(jìn)行了相關(guān)檢查和預(yù)處理,這些檢查和預(yù)處理包括:
1、APR_IPV4_ADDR_OK標(biāo)記只有在hostname為NULL,同時(shí)family為APR_UNSPEC的時(shí)候才會(huì)有效,而APR_IPV6_ADDR_OK和APR_IPV4_ADDR_OK是相互排斥的,一旦定義了APR_IPV4_ADDR_OK,就不能使用APR_IPV6_ADDR_OK,反之亦然。只有在hostname為NULL,同時(shí)family為APR_UNSPEC并且沒(méi)有定義APR_IPV4_ADDR_OK的時(shí)候APR_IPV6_ADDR_OK才會(huì)有效。
2、如果操作系統(tǒng)平臺(tái)并不支持IPV6,同時(shí)并沒(méi)有限定獲取的地址族,那么此時(shí)將默認(rèn)為IPV6。如果指定必須獲取IPV6的地址信息,但系統(tǒng)并不提供支持,此時(shí)返回APR_EINVAL。
一般情況下,在IPV4中從主機(jī)名到網(wǎng)絡(luò)地址的解析可以通過(guò)gethostbyname()函數(shù)完成,不過(guò)該API不允許調(diào)用者指定所需地址類型的任何信息,這意味著它僅返回包含IPV4地址的信息,對(duì)于目前新的IPV6則無(wú)能為力。一些平臺(tái)中為了支持IPV6地址的解析,提供了新的地址解析函數(shù)getaddrinfo()以及新的地址描述結(jié)構(gòu)struct addrinfo。APR中通過(guò)宏HAVE_GETADDRINFO判斷是否支持IPV6地址的解析。目前Window 2000/XP以上的操作系統(tǒng)都能支持新特性。為此APR中根據(jù)系統(tǒng)平臺(tái)的特性采取不同的方法完成地址解析。
首先我們來(lái)看支持IPV6地址解析平臺(tái)下的實(shí)現(xiàn)代碼,find_address函數(shù)的實(shí)現(xiàn)如下:
static apr_status_t find_addresses(apr_sockaddr_t **sa,
const char *hostname, apr_int32_t family,
apr_port_t port, apr_int32_t flags,
apr_pool_t *p)
{
if (flags & APR_IPV4_ADDR_OK) {
apr_status_t error = call_resolver(sa, hostname, AF_INET, port, flags, p);
#if APR_HAVE_IPV6
if (error) {
family = AF_INET6; /* try again */ u
}
else
#endif
return error;
}
#if APR_HAVE_IPV6
else if (flags & APR_IPV6_ADDR_OK) {
apr_status_t error = call_resolver(sa, hostname, AF_INET6, port, flags, p);
if (error) { v
family = AF_INET; /* try again */
}
else {
return APR_SUCCESS;
}
}
#endif
return call_resolver(sa, hostname, family, port, flags, p); w
}
從上面的代碼可以清晰的看到APR_IPV4_ADDR_OK和APR_IPV6_ADDR_OK的含義:對(duì)于前者,函數(shù)內(nèi)部首先查詢對(duì)應(yīng)主機(jī)的IPV4地址,只有在IPV4查詢失敗的時(shí)候才會(huì)繼續(xù)查詢IPV6地址;而后者則與之相反,對(duì)于給定的主機(jī)名稱,首先查詢IPV6地址,只有在查詢失敗的時(shí)候才會(huì)查詢IPV4。因此APR_IPV4_ADDR_OK和APR_IPV6_ADDR_OK決定了查詢的優(yōu)先性,任何時(shí)候一旦查詢成功都不會(huì)繼續(xù)查詢另外協(xié)議地址,即使被查詢主機(jī)具有該協(xié)議地址。
查詢的核心代碼封裝在內(nèi)部函數(shù)call_resolve中,該函數(shù)的參數(shù)和apr_sockaddr_info_get函數(shù)的參數(shù)完全相同且對(duì)應(yīng),call_resolve中的宏處理比較的多,因此我們將分開(kāi)描述:
static apr_status_t call_resolver(apr_sockaddr_t **sa,
const char *hostname, apr_int32_t family,
apr_port_t port, apr_int32_t flags,
apr_pool_t *p)
{
struct addrinfo hints, *ai, *ai_list;
apr_sockaddr_t *prev_sa;
int error;
char *servname = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
#ifdef HAVE_GAI_ADDRCONFIG
if (family == APR_UNSPEC) {
hints.ai_flags = AI_ADDRCONFIG;
}
#endif
在了解上面的代碼之前我們首先簡(jiǎn)要的了解一些getaddrinfo函數(shù)的用法,該函數(shù)定義如下:
int getaddrinfo(const char *hostname, const char *service, const struct addinfo *hints,struct addrinfo **result);
hostname是需要進(jìn)行地址解析的主機(jī)名稱或者是二進(jìn)制的地址串(IPV4的點(diǎn)分十進(jìn)制或者Ipv6的十六進(jìn)制數(shù)串),service則是一個(gè)服務(wù)名或者是一個(gè)十進(jìn)制的端口號(hào)數(shù)串。其中hints是addfinfo結(jié)構(gòu),該結(jié)構(gòu)定義如下:
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
size_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for nodename */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};
hints參數(shù)可以是一個(gè)空置針,也可以是一個(gè)指向某個(gè)addrinfo結(jié)構(gòu)的指針,調(diào)用者在該結(jié)構(gòu)中填入關(guān)于期望返回的信息類型的暗示,這些暗示將控制內(nèi)部的轉(zhuǎn)換細(xì)節(jié)。比如,如果指定的服務(wù)器既支持TCP,也支持UDP,那么調(diào)用者可以把hints結(jié)構(gòu)中的ai_socktype成員設(shè)置為SOCK_DGRAM,使得返回的僅僅是適用于數(shù)據(jù)報(bào)套接口的信息。
hints結(jié)構(gòu)中調(diào)用者可以設(shè)置的成員包括ai_flags,ai_family,ai_socktype和ai_protocol。
其中,ai_flags成員可用的標(biāo)志值及含義如下:
標(biāo)志名稱 標(biāo)志含義
AI_PASSIVE 套接口將用于被動(dòng)打開(kāi)
AI_CANONNAME 告知getaddrinfo函數(shù)返回主機(jī)的規(guī)范名稱
AI_NUMERICHOST 防止任何類型的名字到地址的映射;hostname必須是一個(gè)地址串
AI_NUMERICSERV 防止任何類型的名字到服務(wù)的映射,service參數(shù)必須是一個(gè)十進(jìn)制端口號(hào)數(shù)串
AI_V4MAPPED 如果同時(shí)指定ai_family成員的值為AF_INET6和AF_INET,那么如果沒(méi)有可用的AAAA記錄就返回與A記錄對(duì)應(yīng)得Ipv4映射的IPV6地址
AI_ALL 如果同時(shí)指定AI_V4MAPPED標(biāo)志,那么除了返回與AAAA對(duì)應(yīng)得IPV6地址之外,還會(huì)返回與A記錄對(duì)應(yīng)的IPV4映射的Ipv6地址。
AI_ADDRCONFIG 按照所在主機(jī)的配置選擇返回的地址類型,也就是只查找與所在主機(jī)回饋接口以外的網(wǎng)絡(luò)接口配置的IP地址版本一直的地址。只有當(dāng)本地系統(tǒng)中配置僅僅配置了IPV4地址才會(huì)將主機(jī)名稱轉(zhuǎn)換位IPV4地址;同樣只有當(dāng)本地系統(tǒng)中僅配置了IPV6地址的時(shí)候才會(huì)返回IPV6地址。Loopback地址并不在這種限制之中。
ai_family參數(shù)指定調(diào)用者期待返回的套接口地址結(jié)構(gòu)的類型。它的值包括三種:AF_INET,AF_INET6和AF_UNSPEC。如果指定AF_INET,那么函數(shù)九不能返回任何IPV6相關(guān)的地址信息;如果僅指定了AF_INET6,則就不能返回任何IPV4地址信息。AF_UNSPEC則意味著函數(shù)返回的是適用于指定主機(jī)名和服務(wù)名且適合任何協(xié)議族的地址。如果某個(gè)主機(jī)既有AAAA記錄(IPV6)地址,同時(shí)又有A記錄(IPV4)地址,那么AAAA記錄將作為sockaddr_in6結(jié)構(gòu)返回,而A記錄則作為sockaddr_in結(jié)構(gòu)返回。
if(hostname == NULL) {
#ifdef AI_PASSIVE
hints.ai_flags |= AI_PASSIVE;
#endif
#ifdef OSF1
hostname = family == AF_INET6 ? "::" : "0.0.0.0";
servname = NULL;
#ifdef AI_NUMERICHOST
hints.ai_flags |= AI_NUMERICHOST;
#endif
#else
#ifdef _AIX
if (!port) {
servname = "1";
}
else
#endif /* _AIX */
servname = apr_itoa(p, port);
#endif /* OSF1 */
}
#ifdef HAVE_GAI_ADDRCONFIG
if (error == EAI_BADFLAGS && family == APR_UNSPEC) {
hints.ai_flags = 0;
error = getaddrinfo(hostname, servname, &hints, &ai_list);
}
#endif
if (error) {
#ifndef WIN32
if (error == EAI_SYSTEM) {
return errno;
}
else
#endif
{
#if defined(NEGATIVE_EAI)
error = -error;
#endif
return error + APR_OS_START_EAIERR;
}
}


