TestSecurityUtil.java
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.hadoop.security;
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION;
import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.*;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.security.auth.kerberos.KerberosPrincipal;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.security.alias.CredentialProvider;
import org.apache.hadoop.security.alias.CredentialProviderFactory;
import org.apache.hadoop.security.alias.LocalJavaKeyStoreProvider;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.security.token.TokenIdentifier;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.util.ZKUtil.ZKAuthInfo;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.apache.hadoop.thirdparty.com.google.common.io.Files;
public class TestSecurityUtil {
private static final String ZK_AUTH_VALUE = "a_scheme:a_password";
@BeforeAll
public static void unsetKerberosRealm() {
// prevent failures if kinit-ed or on os x with no realm
System.setProperty("java.security.krb5.kdc", "");
System.setProperty("java.security.krb5.realm", "NONE");
}
@Test
public void isOriginalTGTReturnsCorrectValues() {
assertTrue(SecurityUtil.isTGSPrincipal
(new KerberosPrincipal("krbtgt/foo@foo")));
assertTrue(SecurityUtil.isTGSPrincipal
(new KerberosPrincipal("krbtgt/foo.bar.bat@foo.bar.bat")));
assertFalse(SecurityUtil.isTGSPrincipal
(null));
assertFalse(SecurityUtil.isTGSPrincipal
(new KerberosPrincipal("blah")));
assertFalse(SecurityUtil.isTGSPrincipal
(new KerberosPrincipal("krbtgt/hello")));
assertFalse(SecurityUtil.isTGSPrincipal
(new KerberosPrincipal("krbtgt/foo@FOO")));
}
private void verify(String original, String hostname, String expected)
throws IOException {
assertEquals(expected,
SecurityUtil.getServerPrincipal(original, hostname));
InetAddress addr = mockAddr(hostname);
assertEquals(expected,
SecurityUtil.getServerPrincipal(original, addr));
}
private InetAddress mockAddr(String reverseTo) {
InetAddress mock = Mockito.mock(InetAddress.class);
Mockito.doReturn(reverseTo).when(mock).getCanonicalHostName();
return mock;
}
@Test
public void testGetServerPrincipal() throws IOException {
String service = "hdfs/";
String realm = "@REALM";
String hostname = "foohost";
String userPrincipal = "foo@FOOREALM";
String shouldReplace = service + SecurityUtil.HOSTNAME_PATTERN + realm;
String replaced = service + hostname + realm;
verify(shouldReplace, hostname, replaced);
String shouldNotReplace = service + SecurityUtil.HOSTNAME_PATTERN + "NAME"
+ realm;
verify(shouldNotReplace, hostname, shouldNotReplace);
verify(userPrincipal, hostname, userPrincipal);
// testing reverse DNS lookup doesn't happen
InetAddress notUsed = Mockito.mock(InetAddress.class);
assertEquals(shouldNotReplace,
SecurityUtil.getServerPrincipal(shouldNotReplace, notUsed));
Mockito.verify(notUsed, Mockito.never()).getCanonicalHostName();
}
@Test
public void testPrincipalsWithLowerCaseHosts() throws IOException {
String service = "xyz/";
String realm = "@REALM";
String principalInConf = service + SecurityUtil.HOSTNAME_PATTERN + realm;
String hostname = "FooHost";
String principal =
service + StringUtils.toLowerCase(hostname) + realm;
verify(principalInConf, hostname, principal);
}
@Test
public void testLocalHostNameForNullOrWild() throws Exception {
String local = StringUtils.toLowerCase(SecurityUtil.getLocalHostName(null));
assertEquals("hdfs/" + local + "@REALM",
SecurityUtil.getServerPrincipal("hdfs/_HOST@REALM", (String)null));
assertEquals("hdfs/" + local + "@REALM",
SecurityUtil.getServerPrincipal("hdfs/_HOST@REALM", "0.0.0.0"));
}
@Test
public void testStartsWithIncorrectSettings() throws IOException {
Configuration conf = new Configuration();
SecurityUtil.setAuthenticationMethod(KERBEROS, conf);
String keyTabKey="key";
conf.set(keyTabKey, "");
UserGroupInformation.setConfiguration(conf);
boolean gotException = false;
try {
SecurityUtil.login(conf, keyTabKey, "", "");
} catch (IOException e) {
// expected
gotException=true;
}
assertTrue(gotException, "Exception for empty keytabfile name was expected");
}
@Test
public void testGetHostFromPrincipal() {
assertEquals("host",
SecurityUtil.getHostFromPrincipal("service/host@realm"));
assertEquals(null,
SecurityUtil.getHostFromPrincipal("service@realm"));
}
@Test
public void testBuildDTServiceName() {
Configuration conf = new Configuration(false);
conf.setBoolean(
CommonConfigurationKeys.HADOOP_SECURITY_TOKEN_SERVICE_USE_IP, true);
SecurityUtil.setConfiguration(conf);
assertEquals("127.0.0.1:123",
SecurityUtil.buildDTServiceName(URI.create("test://LocalHost"), 123)
);
assertEquals("127.0.0.1:123",
SecurityUtil.buildDTServiceName(URI.create("test://LocalHost:123"), 456)
);
assertEquals("127.0.0.1:123",
SecurityUtil.buildDTServiceName(URI.create("test://127.0.0.1"), 123)
);
assertEquals("127.0.0.1:123",
SecurityUtil.buildDTServiceName(URI.create("test://127.0.0.1:123"), 456)
);
}
@Test
public void testBuildTokenServiceSockAddr() {
Configuration conf = new Configuration(false);
conf.setBoolean(
CommonConfigurationKeys.HADOOP_SECURITY_TOKEN_SERVICE_USE_IP, true);
SecurityUtil.setConfiguration(conf);
assertEquals("127.0.0.1:123",
SecurityUtil.buildTokenService(new InetSocketAddress("LocalHost", 123)).toString()
);
assertEquals("127.0.0.1:123",
SecurityUtil.buildTokenService(new InetSocketAddress("127.0.0.1", 123)).toString()
);
// what goes in, comes out
assertEquals("127.0.0.1:123",
SecurityUtil.buildTokenService(NetUtils.createSocketAddr("127.0.0.1", 123)).toString()
);
}
@Test
public void testGoodHostsAndPorts() {
InetSocketAddress compare = NetUtils.createSocketAddrForHost("localhost", 123);
runGoodCases(compare, "localhost", 123);
runGoodCases(compare, "localhost:", 123);
runGoodCases(compare, "localhost:123", 456);
}
void runGoodCases(InetSocketAddress addr, String host, int port) {
assertEquals(addr, NetUtils.createSocketAddr(host, port));
assertEquals(addr, NetUtils.createSocketAddr("hdfs://"+host, port));
assertEquals(addr, NetUtils.createSocketAddr("hdfs://"+host+"/path", port));
}
@Test
public void testBadHostsAndPorts() {
runBadCases("", true);
runBadCases(":", false);
runBadCases("hdfs/", false);
runBadCases("hdfs:/", false);
runBadCases("hdfs://", true);
}
void runBadCases(String prefix, boolean validIfPosPort) {
runBadPortPermutes(prefix, false);
runBadPortPermutes(prefix+"*", false);
runBadPortPermutes(prefix+"localhost", validIfPosPort);
runBadPortPermutes(prefix+"localhost:-1", false);
runBadPortPermutes(prefix+"localhost:-123", false);
runBadPortPermutes(prefix+"localhost:xyz", false);
runBadPortPermutes(prefix+"localhost/xyz", validIfPosPort);
runBadPortPermutes(prefix+"localhost/:123", validIfPosPort);
runBadPortPermutes(prefix+":123", false);
runBadPortPermutes(prefix+":xyz", false);
}
void runBadPortPermutes(String arg, boolean validIfPosPort) {
int ports[] = { -123, -1, 123 };
boolean bad = false;
try {
NetUtils.createSocketAddr(arg);
} catch (IllegalArgumentException e) {
bad = true;
} finally {
assertTrue(bad, "should be bad: '"+arg+"'");
}
for (int port : ports) {
if (validIfPosPort && port > 0) continue;
bad = false;
try {
NetUtils.createSocketAddr(arg, port);
} catch (IllegalArgumentException e) {
bad = true;
} finally {
assertTrue(bad, "should be bad: '"+arg+"' (default port:"+port+")");
}
}
}
// check that the socket addr has:
// 1) the InetSocketAddress has the correct hostname, ie. exact host/ip given
// 2) the address is resolved, ie. has an ip
// 3,4) the socket's InetAddress has the same hostname, and the correct ip
// 5) the port is correct
private void
verifyValues(InetSocketAddress addr, String host, String ip, int port) {
assertTrue(!addr.isUnresolved());
// don't know what the standard resolver will return for hostname.
// should be host for host; host or ip for ip is ambiguous
if (!SecurityUtil.useIpForTokenService) {
assertEquals(host, addr.getHostName());
assertEquals(host, addr.getAddress().getHostName());
}
assertEquals(ip, addr.getAddress().getHostAddress());
assertEquals(port, addr.getPort());
}
// check:
// 1) buildTokenService honors use_ip setting
// 2) setTokenService & getService works
// 3) getTokenServiceAddr decodes to the identical socket addr
private void
verifyTokenService(InetSocketAddress addr, String host, String ip, int port, boolean useIp) {
//LOG.info("address:"+addr+" host:"+host+" ip:"+ip+" port:"+port);
Configuration conf = new Configuration(false);
conf.setBoolean(
CommonConfigurationKeys.HADOOP_SECURITY_TOKEN_SERVICE_USE_IP, useIp);
SecurityUtil.setConfiguration(conf);
String serviceHost = useIp ? ip : StringUtils.toLowerCase(host);
Token<?> token = new Token<TokenIdentifier>();
Text service = new Text(serviceHost+":"+port);
assertEquals(service, SecurityUtil.buildTokenService(addr));
SecurityUtil.setTokenService(token, addr);
assertEquals(service, token.getService());
InetSocketAddress serviceAddr = SecurityUtil.getTokenServiceAddr(token);
assertNotNull(serviceAddr);
verifyValues(serviceAddr, serviceHost, ip, port);
}
// check:
// 1) socket addr is created with fields set as expected
// 2) token service with ips
// 3) token service with the given host or ip
private void
verifyAddress(InetSocketAddress addr, String host, String ip, int port) {
verifyValues(addr, host, ip, port);
//LOG.info("test that token service uses ip");
verifyTokenService(addr, host, ip, port, true);
//LOG.info("test that token service uses host");
verifyTokenService(addr, host, ip, port, false);
}
// check:
// 1-4) combinations of host and port
// this will construct a socket addr, verify all the fields, build the
// service to verify the use_ip setting is honored, set the token service
// based on addr and verify the token service is set correctly, decode
// the token service and ensure all the fields of the decoded addr match
private void verifyServiceAddr(String host, String ip) {
InetSocketAddress addr;
int port = 123;
// test host, port tuple
//LOG.info("test tuple ("+host+","+port+")");
addr = NetUtils.createSocketAddrForHost(host, port);
verifyAddress(addr, host, ip, port);
// test authority with no default port
//LOG.info("test authority '"+host+":"+port+"'");
addr = NetUtils.createSocketAddr(host+":"+port);
verifyAddress(addr, host, ip, port);
// test authority with a default port, make sure default isn't used
//LOG.info("test authority '"+host+":"+port+"' with ignored default port");
addr = NetUtils.createSocketAddr(host+":"+port, port+1);
verifyAddress(addr, host, ip, port);
// test host-only authority, using port as default port
//LOG.info("test host:"+host+" port:"+port);
addr = NetUtils.createSocketAddr(host, port);
verifyAddress(addr, host, ip, port);
}
@Test
public void testSocketAddrWithName() {
String staticHost = "my";
NetUtils.addStaticResolution(staticHost, "localhost");
verifyServiceAddr("LocalHost", "127.0.0.1");
}
@Test
public void testSocketAddrWithIP() {
String staticHost = "127.0.0.1";
NetUtils.addStaticResolution(staticHost, "localhost");
verifyServiceAddr(staticHost, "127.0.0.1");
}
@Test
public void testSocketAddrWithNameToStaticName() {
String staticHost = "host1";
NetUtils.addStaticResolution(staticHost, "localhost");
verifyServiceAddr(staticHost, "127.0.0.1");
}
@Test
public void testSocketAddrWithNameToStaticIP() {
String staticHost = "host3";
NetUtils.addStaticResolution(staticHost, "255.255.255.255");
verifyServiceAddr(staticHost, "255.255.255.255");
}
@Test
public void testSocketAddrWithChangeIP() {
String staticHost = "host4";
NetUtils.addStaticResolution(staticHost, "255.255.255.255");
verifyServiceAddr(staticHost, "255.255.255.255");
NetUtils.addStaticResolution(staticHost, "255.255.255.254");
verifyServiceAddr(staticHost, "255.255.255.254");
}
// this is a bizarre case, but it's if a test tries to remap an ip address
@Test
public void testSocketAddrWithIPToStaticIP() {
String staticHost = "1.2.3.4";
NetUtils.addStaticResolution(staticHost, "255.255.255.255");
verifyServiceAddr(staticHost, "255.255.255.255");
}
@Test
public void testGetAuthenticationMethod() {
Configuration conf = new Configuration();
// default is simple
conf.unset(HADOOP_SECURITY_AUTHENTICATION);
assertEquals(SIMPLE, SecurityUtil.getAuthenticationMethod(conf));
// simple
conf.set(HADOOP_SECURITY_AUTHENTICATION, "simple");
assertEquals(SIMPLE, SecurityUtil.getAuthenticationMethod(conf));
// kerberos
conf.set(HADOOP_SECURITY_AUTHENTICATION, "kerberos");
assertEquals(KERBEROS, SecurityUtil.getAuthenticationMethod(conf));
// bad value
conf.set(HADOOP_SECURITY_AUTHENTICATION, "kaboom");
String error = null;
try {
SecurityUtil.getAuthenticationMethod(conf);
} catch (Exception e) {
error = e.toString();
}
assertEquals("java.lang.IllegalArgumentException: " +
"Invalid attribute value for " +
HADOOP_SECURITY_AUTHENTICATION + " of kaboom", error);
}
@Test
public void testSetAuthenticationMethod() {
Configuration conf = new Configuration();
// default
SecurityUtil.setAuthenticationMethod(null, conf);
assertEquals("simple", conf.get(HADOOP_SECURITY_AUTHENTICATION));
// simple
SecurityUtil.setAuthenticationMethod(SIMPLE, conf);
assertEquals("simple", conf.get(HADOOP_SECURITY_AUTHENTICATION));
// kerberos
SecurityUtil.setAuthenticationMethod(KERBEROS, conf);
assertEquals("kerberos", conf.get(HADOOP_SECURITY_AUTHENTICATION));
}
@Test
public void testAuthPlainPasswordProperty() throws Exception {
Configuration conf = new Configuration();
conf.set(CommonConfigurationKeys.ZK_AUTH, ZK_AUTH_VALUE);
List<ZKAuthInfo> zkAuths = SecurityUtil.getZKAuthInfos(conf,
CommonConfigurationKeys.ZK_AUTH);
assertEquals(1, zkAuths.size());
ZKAuthInfo zkAuthInfo = zkAuths.get(0);
assertEquals("a_scheme", zkAuthInfo.getScheme());
assertArrayEquals("a_password".getBytes(), zkAuthInfo.getAuth());
}
@Test
public void testAuthPlainTextFile() throws Exception {
Configuration conf = new Configuration();
File passwordTxtFile = File.createTempFile(
getClass().getSimpleName() + ".testAuthAtPathNotation-", ".txt");
Files.asCharSink(passwordTxtFile, StandardCharsets.UTF_8)
.write(ZK_AUTH_VALUE);
try {
conf.set(CommonConfigurationKeys.ZK_AUTH,
"@" + passwordTxtFile.getAbsolutePath());
List<ZKAuthInfo> zkAuths = SecurityUtil.getZKAuthInfos(conf,
CommonConfigurationKeys.ZK_AUTH);
assertEquals(1, zkAuths.size());
ZKAuthInfo zkAuthInfo = zkAuths.get(0);
assertEquals("a_scheme", zkAuthInfo.getScheme());
assertArrayEquals("a_password".getBytes(), zkAuthInfo.getAuth());
} finally {
boolean deleted = passwordTxtFile.delete();
assertTrue(deleted);
}
}
@Test
public void testAuthLocalJceks() throws Exception {
File localJceksFile = File.createTempFile(
getClass().getSimpleName() +".testAuthLocalJceks-", ".localjceks");
populateLocalJceksTestFile(localJceksFile.getAbsolutePath());
try {
String localJceksUri = "localjceks://file/" +
localJceksFile.getAbsolutePath();
Configuration conf = new Configuration();
conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH,
localJceksUri);
List<ZKAuthInfo> zkAuths = SecurityUtil.getZKAuthInfos(conf,
CommonConfigurationKeys.ZK_AUTH);
assertEquals(1, zkAuths.size());
ZKAuthInfo zkAuthInfo = zkAuths.get(0);
assertEquals("a_scheme", zkAuthInfo.getScheme());
assertArrayEquals("a_password".getBytes(), zkAuthInfo.getAuth());
} finally {
boolean deleted = localJceksFile.delete();
assertTrue(deleted);
}
}
private void populateLocalJceksTestFile(String path) throws IOException {
Configuration conf = new Configuration();
conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH,
"localjceks://file/" + path);
CredentialProvider provider =
CredentialProviderFactory.getProviders(conf).get(0);
assertEquals(LocalJavaKeyStoreProvider.class.getName(),
provider.getClass().getName());
provider.createCredentialEntry(CommonConfigurationKeys.ZK_AUTH,
ZK_AUTH_VALUE.toCharArray());
provider.flush();
}
@Test
public void testInitiateHostResolver() throws Exception {
// 1. useIP is false and cache interval is 0
Configuration conf = new Configuration();
conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_TOKEN_SERVICE_USE_IP, false);
conf.setTimeDuration(
CommonConfigurationKeys.HADOOP_SECURITY_HOSTNAME_CACHE_EXPIRE_INTERVAL_SECONDS,
0, TimeUnit.SECONDS);
SecurityUtil.setConfiguration(conf);
SecurityUtil.HostResolver hostResolver = SecurityUtil.hostResolver;
assertTrue(hostResolver instanceof SecurityUtil.QualifiedHostResolver,
"Resolver should be a QualifiedHostResolver");
SecurityUtil.CacheableHostResolver cacheableHostResolver =
(SecurityUtil.QualifiedHostResolver) hostResolver;
assertNull(cacheableHostResolver.getCache(),
"Cache should be null when caching interval is less than or equal 0");
// 2. useIP is false and cache interval is 10
conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_TOKEN_SERVICE_USE_IP, false);
conf.setTimeDuration(
CommonConfigurationKeys.HADOOP_SECURITY_HOSTNAME_CACHE_EXPIRE_INTERVAL_SECONDS,
10, TimeUnit.SECONDS);
SecurityUtil.setConfiguration(conf);
hostResolver = SecurityUtil.hostResolver;
assertTrue(hostResolver instanceof SecurityUtil.QualifiedHostResolver,
"Resolver should be a QualifiedHostResolver");
cacheableHostResolver = (SecurityUtil.QualifiedHostResolver) hostResolver;
assertNotNull(cacheableHostResolver.getCache(),
"Cache should be set when caching interval is enabled");
// 3. useIP is true and cache interval is 0
conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_TOKEN_SERVICE_USE_IP, true);
conf.setTimeDuration(
CommonConfigurationKeys.HADOOP_SECURITY_HOSTNAME_CACHE_EXPIRE_INTERVAL_SECONDS,
0, TimeUnit.SECONDS);
SecurityUtil.setConfiguration(conf);
hostResolver = SecurityUtil.hostResolver;
assertTrue(hostResolver instanceof SecurityUtil.StandardHostResolver,
"Resolver should be a StandardHostResolver");
cacheableHostResolver = (SecurityUtil.StandardHostResolver) hostResolver;
assertNull(cacheableHostResolver.getCache(),
"Cache should be null when caching interval is less than or equal 0");
// 4. useIP is true and cache interval is 10
conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_TOKEN_SERVICE_USE_IP, true);
conf.setTimeDuration(
CommonConfigurationKeys.HADOOP_SECURITY_HOSTNAME_CACHE_EXPIRE_INTERVAL_SECONDS,
10, TimeUnit.SECONDS);
SecurityUtil.setConfiguration(conf);
hostResolver = SecurityUtil.hostResolver;
assertTrue(hostResolver instanceof SecurityUtil.StandardHostResolver,
"Resolver should be a StandardHostResolver");
cacheableHostResolver = (SecurityUtil.StandardHostResolver) hostResolver;
assertNotNull(cacheableHostResolver.getCache(),
"Cache should be set when caching interval is enabled");
}
/**
* Test caching behavior in QualifiedHostResolver when caching is enabled.
*/
@Test
public void testQualifiedHostResolverCachingEnabled() throws Exception {
// Create a QualifiedHostResolver with expiry interval > 0
SecurityUtil.QualifiedHostResolver
resolver = new SecurityUtil.QualifiedHostResolver(1);
testCacheableResolve(resolver);
}
/**
* Test caching behavior in StandardHostResolver when caching is enabled.
*/
@Test
public void testStandardHostResolverCachingEnabled() throws Exception {
// Create a StandardHostResolver with expiry interval > 0
SecurityUtil.StandardHostResolver
resolver = new SecurityUtil.StandardHostResolver(1);
testCacheableResolve(resolver);
}
private void testCacheableResolve(SecurityUtil.CacheableHostResolver resolver)
throws Exception {
// Call getByName twice with the same host
InetAddress addr1 = resolver.getByName("127.0.0.1");
InetAddress addr2 = resolver.getByName("127.0.0.1");
assertNotNull(addr1);
assertNotNull(addr2);
// Both addresses should be the same instance (cached value)
assertSame(addr1, addr2);
// wait for timeout of cache item
Thread.sleep(1500);
InetAddress addr3 = resolver.getByName("127.0.0.1");
assertNotNull(addr3);
assertNotSame(addr1, addr3);
}
/**
* Test resolving non-existent hostname, show throw UnknownHostException.
*/
@Test
public void testInvalidHostThrowsException() {
SecurityUtil.StandardHostResolver
standardHostResolver = new SecurityUtil.StandardHostResolver(10);
String invalidHost = "invalid_host_name_which_does_not_exist";
assertThrows(UnknownHostException.class, () -> {
standardHostResolver.getByName(invalidHost);
}, "Resolving an invalid host should throw UnknownHostException");
SecurityUtil.QualifiedHostResolver
qualifiedHostResolver = new SecurityUtil.QualifiedHostResolver(10);
assertThrows(UnknownHostException.class, () -> {
qualifiedHostResolver.getByName(invalidHost);
}, "Resolving an invalid host should throw UnknownHostException");
}
}