JAVA调用DNS解析方法:
public static InetAddress getByName(String host)
public static InetAddress[] getAllByName(String host)
getByName先调用getAllByName,然后返回地址列表的第一个地址;
getAllByName:
public static InetAddress getByName(String host)
throws UnknownHostException {
return InetAddress.getAllByName(host)[0];
}
private static InetAddress[] getAllByName0 (String host, InetAddress reqAddr, boolean check)
throws UnknownHostException {
/* If it gets here it is presumed to be a hostname */
/* Cache.get can return: null, unknownAddress, or InetAddress[] */
/* make sure the connection to the host is allowed, before we
* give out a hostname
*/
if (check) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkConnect(host, -1);
}
}
InetAddress[] addresses = getCachedAddresses(host);
/* If no entry in cache, then do the host lookup */
if (addresses == null) {
addresses = getAddressesFromNameService(host, reqAddr);
}
if (addresses == unknown_array)
throw new UnknownHostException(host);
return addresses.clone();
}
首先调用getCachedAddresses,如果为空,则调用getAddressesFromNameService进行解析;
/*
* Lookup hostname in cache (positive & negative cache). If
* found return addresses, null if not found.
*/
private static InetAddress[] getCachedAddresses(String hostname) {
hostname = hostname.toLowerCase();
// search both positive & negative caches
synchronized (addressCache) {
cacheInitIfNeeded();
CacheEntry entry = addressCache.get(hostname);
if (entry == null) {
entry = negativeCache.get(hostname);
}
if (entry != null) {
return entry.addresses;
}
}
// not found
return null;
}
getAddressesFromNameService: 循环调用nameService的lookupAllHostAddr方法,直到找到地址:
for (NameService nameService : nameServices) {
try {
/*
* Do not put the call to lookup() inside the
* constructor. if you do you will still be
* allocating space when the lookup fails.
*/
addresses = nameService.lookupAllHostAddr(host);
success = true;
break;
} catch (UnknownHostException uhe) {
if (host.equalsIgnoreCase("localhost")) {
InetAddress[] local = new InetAddress[] { impl.loopbackAddress() };
addresses = local;
success = true;
break;
}
else {
addresses = unknown_array;
success = false;
ex = uhe;
}
}
}
namespace初始化代码:
static {
// create the impl
impl = InetAddressImplFactory.create();
// get name service if provided and requested
String provider = null;;
String propPrefix = "sun.net.spi.nameservice.provider.";
int n = 1;
nameServices = new ArrayList<NameService>();
provider = AccessController.doPrivileged(
new GetPropertyAction(propPrefix + n));
while (provider != null) {
NameService ns = createNSProvider(provider);
if (ns != null)
nameServices.add(ns);
n++;
provider = AccessController.doPrivileged(
new GetPropertyAction(propPrefix + n));
}
// if not designate any name services provider,
// create a default one
if (nameServices.size() == 0) {
NameService ns = createNSProvider("default");
nameServices.add(ns);
}
}
在这里要特别提下Java提供的DNSNameService,该类可以通过下述参数启用:
-Dsun.net.spi.nameservice.provider.1=dns,sun
-Dsun.net.spi.nameservice.nameservers=192.168.1.188
该类会根据sun.net.spi.nameservice.nameservers指定的name server或/etc/resolv.conf文件中配置的name server进行DNS解析;
创建NameService方法代码如下:
private static NameService createNSProvider(String provider) {
if (provider == null)
return null;
NameService nameService = null;
if (provider.equals("default")) {
// initialize the default name service
nameService = new NameService() {
public InetAddress[] lookupAllHostAddr(String host)
throws UnknownHostException {
return impl.lookupAllHostAddr(host);
}
public String getHostByAddr(byte[] addr)
throws UnknownHostException {
return impl.getHostByAddr(addr);
}
};
} else {
final String providerName = provider;
try {
nameService = java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<NameService>() {
public NameService run() {
Iterator<NameServiceDescriptor> itr =
ServiceLoader.load(NameServiceDescriptor.class)
.iterator();
while (itr.hasNext()) {
NameServiceDescriptor nsd = itr.next();
if (providerName.
equalsIgnoreCase(nsd.getType()+","
+nsd.getProviderName())) {
try {
return nsd.createNameService();
} catch (Exception e) {
e.printStackTrace();
System.err.println(
"Cannot create name service:"
+providerName+": " + e);
}
}
}
return null;
}
}
);
} catch (java.security.PrivilegedActionException e) {
}
}
return nameService;
}
对于DNSNameServiceDescriptor,其Type和ProviderName分别为dns,sun;
nameService.lookupAllHostAddr(host):
class InetAddressImplFactory {
static InetAddressImpl create() {
return InetAddress.loadImpl(isIPv6Supported() ?
"Inet6AddressImpl" : "Inet4AddressImpl");
}
static native boolean isIPv6Supported();
}
以Inet4AddressImpl为例,说明DNS的解析:
public native InetAddress[] lookupAllHostAddr(String var1) throws UnknownHostException;
Inet4AddressImp类的方法是native的,是采用本地方法实现的:
JNIEXPORT jobjectArray JNICALL
Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
jstring host) {
const char *hostname;
jobjectArray ret = 0;
int retLen = 0;
int error = 0;
struct addrinfo hints, *res, *resNew = NULL;
if (!initializeInetClasses(env))
return NULL;
if (IS_NULL(host)) {
JNU_ThrowNullPointerException(env, "host is null");
return 0;
}
hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
CHECK_NULL_RETURN(hostname, NULL);
/* Try once, with our static buffer. */
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_INET;
error = getaddrinfo(hostname, NULL, &hints, &res);
if (error) {
/* report error */
ThrowUnknownHostExceptionWithGaiError(env, hostname, error);
JNU_ReleaseStringPlatformChars(env, host, hostname);
return NULL;
} else {
int i = 0;
struct addrinfo *itr, *last = NULL, *iterator = res;
while (iterator != NULL) {
// remove the duplicate one
int skip = 0;
itr = resNew;
while (itr != NULL) {
struct sockaddr_in *addr1, *addr2;
addr1 = (struct sockaddr_in *)iterator->ai_addr;
addr2 = (struct sockaddr_in *)itr->ai_addr;
if (addr1->sin_addr.s_addr ==
addr2->sin_addr.s_addr) {
skip = 1;
break;
}
itr = itr->ai_next;
}
if (!skip) {
struct addrinfo *next
= (struct addrinfo*) malloc(sizeof(struct addrinfo));
if (!next) {
JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
ret = NULL;
goto cleanupAndReturn;
}
memcpy(next, iterator, sizeof(struct addrinfo));
next->ai_next = NULL;
if (resNew == NULL) {
resNew = next;
} else {
last->ai_next = next;
}
last = next;
i++;
}
iterator = iterator->ai_next;
}
retLen = i;
iterator = resNew;
ret = (*env)->NewObjectArray(env, retLen, ni_iacls, NULL);
if (IS_NULL(ret)) {
/* we may have memory to free at the end of this */
goto cleanupAndReturn;
}
i = 0;
while (iterator != NULL) {
jobject iaObj = (*env)->NewObject(env, ni_ia4cls, ni_ia4ctrID);
if (IS_NULL(iaObj)) {
ret = NULL;
goto cleanupAndReturn;
}
setInetAddress_addr(env, iaObj, ntohl(((struct sockaddr_in*)iterator->ai_addr)->sin_addr.s_addr));
setInetAddress_hostName(env, iaObj, host);
(*env)->SetObjectArrayElement(env, ret, i++, iaObj);
iterator = iterator->ai_next;
}
}
}
getaddrinfo函数方法:
int getaddrinfo (const char *__restrict name, const char *__restrict service,
const struct addrinfo *__restrict hints,
struct addrinfo **__restrict pai)
{
int i = 0, j = 0, last_i = 0;
int nresults = 0;
struct addrinfo *p = NULL, **end;
struct gaih *g = gaih, *pg = NULL;
struct gaih_service gaih_service, *pservice;
struct addrinfo local_hints;
while (g->gaih)
{
if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
{
j++;
if (pg == NULL || pg->gaih != g->gaih)
{
pg = g;
i = g->gaih (name, pservice, hints, end);
if (i != 0)
{
/* EAI_NODATA is a more specific result as it says that
we found a result but it is not usable. */
if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
last_i = i;
if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
{
++g;
continue;
}
freeaddrinfo (p);
return -(i & GAIH_EAI);
}
if (end)
while (*end)
{
end = &((*end)->ai_next);
++nresults;
}
}
}
++g;
}
if (j == 0)
return EAI_FAMILY;
if (nresults > 1)
{
/* Sort results according to RFC 3484. */
struct sort_result results[nresults];
struct addrinfo *q;
struct addrinfo *last = NULL;
char *canonname = NULL;
for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next)
{
results[i].dest_addr = q;
results[i].got_source_addr = false;
/* If we just looked up the address for a different
protocol, reuse the result. */
if (last != NULL && last->ai_addrlen == q->ai_addrlen
&& memcmp (last->ai_addr, q->ai_addr, q->ai_addrlen) == 0)
{
memcpy (&results[i].source_addr, &results[i - 1].source_addr,
results[i - 1].source_addr_len);
results[i].source_addr_len = results[i - 1].source_addr_len;
results[i].got_source_addr = results[i - 1].got_source_addr;
}
else
{
/* We overwrite the type with SOCK_DGRAM since we do not
want connect() to connect to the other side. If we
cannot determine the source address remember this
fact. */
int fd = socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP);
socklen_t sl = sizeof (results[i].source_addr);
if (fd != -1
&& connect (fd, q->ai_addr, q->ai_addrlen) == 0
&& getsockname (fd,
(struct sockaddr *) &results[i].source_addr,
&sl) == 0)
{
results[i].source_addr_len = sl;
results[i].got_source_addr = true;
}
else
/* Just make sure that if we have to process the same
address again we do not copy any memory. */
results[i].source_addr_len = 0;
if (fd != -1)
close_not_cancel_no_status (fd);
}
/* Remember the canonical name. */
if (q->ai_canonname != NULL)
{
assert (canonname == NULL);
canonname = q->ai_canonname;
q->ai_canonname = NULL;
}
}
/* We got all the source addresses we can get, now sort using
the information. */
qsort (results, nresults, sizeof (results[0]), rfc3484_sort);
/* Queue the results up as they come out of sorting. */
q = p = results[0].dest_addr;
for (i = 1; i < nresults; ++i)
q = q->ai_next = results[i].dest_addr;
q->ai_next = NULL;
/* Fill in the canonical name into the new first entry. */
p->ai_canonname = canonname;
}
if (p)
{
*pai = p;
return 0;
}
if (pai == NULL && last_i == 0)
return 0;
return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
}