From 2391453a181a29307d0f2023021d6b2b0d5e7a86 Mon Sep 17 00:00:00 2001 From: wangwei Date: Tue, 18 Aug 2020 17:10:14 +0800 Subject: [PATCH] [ISSUE #3315] nacos client support https * common module add tls related classes * JdkHttpClientRequest support https * unified IpUtils --- .../nacos/client/config/utils/ParamUtils.java | 4 +- .../client/request/JdkHttpClientRequest.java | 48 ++++++ .../common/tls/SelfHostnameVerifier.java | 71 +++++++++ .../nacos/common/tls/SelfTrustManager.java | 111 ++++++++++++++ .../nacos/common/tls/TlsFileWatcher.java | 138 ++++++++++++++++++ .../nacos/common/tls/TlsSystemConfig.java | 117 +++++++++++++++ .../alibaba/nacos/common/utils/IpUtils.java | 8 +- .../nacos/config/server/utils/IPUtil.java | 63 -------- 8 files changed, 490 insertions(+), 70 deletions(-) create mode 100644 common/src/main/java/com/alibaba/nacos/common/tls/SelfHostnameVerifier.java create mode 100644 common/src/main/java/com/alibaba/nacos/common/tls/SelfTrustManager.java create mode 100644 common/src/main/java/com/alibaba/nacos/common/tls/TlsFileWatcher.java create mode 100644 common/src/main/java/com/alibaba/nacos/common/tls/TlsSystemConfig.java rename client/src/main/java/com/alibaba/nacos/client/utils/IpUtil.java => common/src/main/java/com/alibaba/nacos/common/utils/IpUtils.java (92%) delete mode 100644 config/src/main/java/com/alibaba/nacos/config/server/utils/IPUtil.java diff --git a/client/src/main/java/com/alibaba/nacos/client/config/utils/ParamUtils.java b/client/src/main/java/com/alibaba/nacos/client/config/utils/ParamUtils.java index 5cca69bd3ac..7b542d45744 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/utils/ParamUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/utils/ParamUtils.java @@ -17,7 +17,7 @@ package com.alibaba.nacos.client.config.utils; import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.client.utils.IpUtil; +import com.alibaba.nacos.common.utils.IpUtils; import com.alibaba.nacos.common.utils.StringUtils; import java.util.List; @@ -190,7 +190,7 @@ public static void checkBetaIps(String betaIps) throws NacosException { } String[] ipsArr = betaIps.split(","); for (String ip : ipsArr) { - if (!IpUtil.isIpv4(ip)) { + if (!IpUtils.isIpv4(ip)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "betaIps invalid"); } } diff --git a/common/src/main/java/com/alibaba/nacos/common/http/client/request/JdkHttpClientRequest.java b/common/src/main/java/com/alibaba/nacos/common/http/client/request/JdkHttpClientRequest.java index e18bc11833b..38806bc20c1 100644 --- a/common/src/main/java/com/alibaba/nacos/common/http/client/request/JdkHttpClientRequest.java +++ b/common/src/main/java/com/alibaba/nacos/common/http/client/request/JdkHttpClientRequest.java @@ -24,11 +24,22 @@ import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.model.RequestHttpEntity; +import com.alibaba.nacos.common.tls.SelfHostnameVerifier; +import com.alibaba.nacos.common.tls.SelfTrustManager; +import com.alibaba.nacos.common.tls.TlsFileWatcher; import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.common.tls.TlsSystemConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; @@ -39,10 +50,47 @@ */ public class JdkHttpClientRequest implements HttpClientRequest { + private static final Logger LOGGER = LoggerFactory.getLogger(JdkHttpClientRequest.class); + private HttpClientConfig httpClientConfig; public JdkHttpClientRequest(HttpClientConfig httpClientConfig) { this.httpClientConfig = httpClientConfig; + loadSSLContext(); + replaceHostnameVerifier(); + try { + TlsFileWatcher.getInstance().addFileChangeListener(new TlsFileWatcher.FileChangeListener() { + @Override + public void onChanged(String filePath) { + loadSSLContext(); + } + }, TlsSystemConfig.tlsClientTrustCertPath); + } catch (IOException e) { + LOGGER.error("add tls file listener fail", e); + } + } + + @SuppressWarnings("checkstyle:abbreviationaswordinname") + private void loadSSLContext() { + if (TlsSystemConfig.tlsEnable) { + try { + SSLContext sslcontext = SSLContext.getInstance("TLS"); + sslcontext.init(null, SelfTrustManager + .trustManager(TlsSystemConfig.tlsClientAuthServer, TlsSystemConfig.tlsClientTrustCertPath), + new java.security.SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory()); + + } catch (NoSuchAlgorithmException e) { + LOGGER.error("Failed to create SSLContext", e); + } catch (KeyManagementException e) { + LOGGER.error("Failed to create SSLContext", e); + } + } + } + + private void replaceHostnameVerifier() { + final HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier(); + HttpsURLConnection.setDefaultHostnameVerifier(new SelfHostnameVerifier(hv)); } @Override diff --git a/common/src/main/java/com/alibaba/nacos/common/tls/SelfHostnameVerifier.java b/common/src/main/java/com/alibaba/nacos/common/tls/SelfHostnameVerifier.java new file mode 100644 index 00000000000..2f45bf08cee --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/tls/SelfHostnameVerifier.java @@ -0,0 +1,71 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.common.tls; + +import com.alibaba.nacos.common.utils.IpUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSession; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A HostnameVerifier verify ipv4 and localhost. + * + * @author wangwei + */ + +public final class SelfHostnameVerifier implements HostnameVerifier { + + private static final Logger LOGGER = LoggerFactory.getLogger(SelfHostnameVerifier.class); + + private final HostnameVerifier hv; + + private static ConcurrentHashMap hosts = new ConcurrentHashMap(); + + private static final String[] LOCALHOST_HOSTNAME = new String[] {"localhost", "127.0.0.1"}; + + public SelfHostnameVerifier(HostnameVerifier hv) { + this.hv = hv; + } + + @Override + public boolean verify(String hostname, SSLSession session) { + if (LOCALHOST_HOSTNAME[0].equalsIgnoreCase(hostname) || LOCALHOST_HOSTNAME[1].equals(hostname)) { + return true; + } + if (isIpv4(hostname)) { + return true; + } + return hv.verify(hostname, session); + } + + private static boolean isIpv4(String host) { + if (host == null || host.isEmpty()) { + LOGGER.warn("host is empty, isIPv4 = false"); + return false; + } + Boolean cacheHostVerify = hosts.get(host); + if (cacheHostVerify != null) { + return cacheHostVerify; + } + boolean isIp = IpUtils.isIpv4(host); + hosts.putIfAbsent(host, isIp); + return isIp; + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/tls/SelfTrustManager.java b/common/src/main/java/com/alibaba/nacos/common/tls/SelfTrustManager.java new file mode 100644 index 00000000000..2931ffba867 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/tls/SelfTrustManager.java @@ -0,0 +1,111 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.common.tls; + +import com.alibaba.nacos.common.utils.IoUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLException; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Collection; + +/** + * A TrustManager tool returns the specified TrustManager. + * + * @author wangwei + */ +public final class SelfTrustManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(SelfHostnameVerifier.class); + + @SuppressWarnings("checkstyle:WhitespaceAround") + static TrustManager[] trustAll = new TrustManager[] {new X509TrustManager() { + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + }}; + + /** + * Returns the result of calling {@link #buildSecureTrustManager} if {@code clientAuth} is enable and {@code + * trustCertPath} exists. Returns the {@link trustAll} otherwise. + * + * @param clientAuth whether need client auth + * @param trustCertPath trust certificate path + * @return Array of {@link TrustManager } + */ + public static TrustManager[] trustManager(boolean clientAuth, String trustCertPath) { + if (clientAuth) { + try { + return trustCertPath == null ? null : buildSecureTrustManager(trustCertPath); + } catch (SSLException e) { + LOGGER.warn("degrade trust manager as build failed, " + "will trust all certs."); + return trustAll; + } + } else { + return trustAll; + } + } + + private static TrustManager[] buildSecureTrustManager(String trustCertPath) throws SSLException { + TrustManagerFactory selfTmf = null; + InputStream in = null; + + try { + String algorithm = TrustManagerFactory.getDefaultAlgorithm(); + selfTmf = TrustManagerFactory.getInstance(algorithm); + + KeyStore trustKeyStore = KeyStore.getInstance("JKS"); + trustKeyStore.load(null, null); + + in = new FileInputStream(trustCertPath); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + Collection certs = (Collection) cf.generateCertificates(in); + int count = 0; + for (Certificate cert : certs) { + trustKeyStore.setCertificateEntry("cert-" + (count++), cert); + } + selfTmf.init(trustKeyStore); + return selfTmf.getTrustManagers(); + } catch (Exception e) { + LOGGER.error("build client trustManagerFactory failed", e); + throw new SSLException(e); + } finally { + IoUtils.closeQuietly(in); + } + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/tls/TlsFileWatcher.java b/common/src/main/java/com/alibaba/nacos/common/tls/TlsFileWatcher.java new file mode 100644 index 00000000000..d6b4520b64d --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/tls/TlsFileWatcher.java @@ -0,0 +1,138 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.common.tls; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.common.executor.ExecutorFactory; +import com.alibaba.nacos.common.executor.NameThreadFactory; +import com.alibaba.nacos.common.utils.ClassUtils; +import com.alibaba.nacos.common.utils.IoUtils; +import com.alibaba.nacos.common.utils.MD5Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Certificate file update monitoring + * + *

Considering that the current client needs to support jdk 1.6 and module dependencies , + * the WatchFileCenter in the core module is not used + * + * @author wangwei + */ +public final class TlsFileWatcher { + + private static final Logger LOGGER = LoggerFactory.getLogger(TlsFileWatcher.class); + + private AtomicBoolean started = new AtomicBoolean(false); + + private final int checkInterval = TlsSystemConfig.tlsFileCheckInterval; + + private Map fileMd5Map = new HashMap(); + + private ConcurrentHashMap watchFilesMap = new ConcurrentHashMap(); + + private final ScheduledExecutorService service = ExecutorFactory.Managed + .newSingleScheduledExecutorService(ClassUtils.getCanonicalName(TlsFileWatcher.class), + new NameThreadFactory("com.alibaba.nacos.core.common.tls")); + + private static TlsFileWatcher tlsFileWatcher = new TlsFileWatcher(); + + private TlsFileWatcher() { + start(); + } + + public static TlsFileWatcher getInstance() { + return tlsFileWatcher; + } + + /** + * Add file change listener for specified path. + * + * @param fileChangeListener listener + * @param filePaths file paths + * @throws IOException If an I/O error occurs + */ + public void addFileChangeListener(FileChangeListener fileChangeListener, String... filePaths) throws IOException { + for (String filePath : filePaths) { + if (filePath != null && new File(filePath).exists()) { + watchFilesMap.put(filePath, fileChangeListener); + InputStream in = null; + try { + in = new FileInputStream(filePath); + fileMd5Map.put(filePath, MD5Utils.md5Hex(IoUtils.toString(in, Constants.ENCODE), Constants.ENCODE)); + } finally { + IoUtils.closeQuietly(in); + } + } + } + } + + /** + * start file watch task. Notify when the MD5 of file changed + */ + public void start() { + if (started.compareAndSet(false, true)) { + service.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + for (Map.Entry item : watchFilesMap.entrySet()) { + String filePath = item.getKey(); + String newHash; + InputStream in = null; + try { + in = new FileInputStream(filePath); + newHash = MD5Utils.md5Hex(IoUtils.toString(in, Constants.ENCODE), Constants.ENCODE); + } catch (Exception ignored) { + LOGGER.warn(" service has exception when calculate the file MD5. " + ignored); + continue; + } finally { + IoUtils.closeQuietly(in); + } + if (!newHash.equals(fileMd5Map.get(filePath))) { + LOGGER.info(filePath + " file hash changed,need reload sslcontext"); + fileMd5Map.put(filePath, newHash); + item.getValue().onChanged(filePath); + LOGGER.info(filePath + " onChanged success!"); + } + } + } + }, 1, checkInterval, TimeUnit.MINUTES); + } + } + + public interface FileChangeListener { + + /** + * listener onChanged event. + * + * @param filePath Path of changed file + */ + void onChanged(String filePath); + } + +} diff --git a/common/src/main/java/com/alibaba/nacos/common/tls/TlsSystemConfig.java b/common/src/main/java/com/alibaba/nacos/common/tls/TlsSystemConfig.java new file mode 100644 index 00000000000..5d247bb17f4 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/tls/TlsSystemConfig.java @@ -0,0 +1,117 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.common.tls; + +/** + * tls system config. + * + * @author wangwei + */ +public class TlsSystemConfig { + + public static final String TLS_TEST_MODE_ENABLE = "tls.test"; + + public static final String TLS_ENABLE = "tls.enable"; + + public static final String CLIENT_AUTH = "tls.client.authServer"; + + public static final String CLIENT_KEYPATH = "tls.client.keyPath"; + + public static final String CLIENT_KEYPASSWORD = "tls.client.keyPassword"; + + public static final String CLIENT_CERTPATH = "tls.client.certPath"; + + public static final String CLIENT_TRUST_CERT = "tls.client.trustCertPath"; + + public static final String SERVER_AUTH = "tls.server.authClient"; + + public static final String SERVER_KEYPATH = "tls.server.keyPath"; + + public static final String SERVER_KEYPASSWORD = "tls.server.keyPassword"; + + public static final String SERVER_CERTPATH = "tls.server.certPath"; + + public static final String SERVER_TRUST_CERT = "tls.server.trustCertPath"; + + public static final String CHECK_INTERVAL = "checkIntervalTlsFile"; + + /** + * To determine whether use SSL in client-side. + */ + public static boolean tlsEnable = Boolean.parseBoolean(System.getProperty(TLS_ENABLE, "false")); + + /** + * To determine whether use test mode when initialize TLS context. + */ + public static boolean tlsTestModeEnable = Boolean.parseBoolean(System.getProperty(TLS_TEST_MODE_ENABLE, "true")); + + /** + * To determine whether verify the server endpoint's certificate strictly. + */ + public static boolean tlsClientAuthServer = Boolean.parseBoolean(System.getProperty(CLIENT_AUTH, "false")); + + /** + * To determine whether verify the client endpoint's certificate strictly. + */ + public static boolean tlsServerAuthClient = Boolean.parseBoolean(System.getProperty(SERVER_AUTH, "false")); + + /** + * The store path of client-side private key. + */ + public static String tlsClientKeyPath = System.getProperty(CLIENT_KEYPATH, null); + + /** + * The password of the client-side private key. + */ + public static String tlsClientKeyPassword = System.getProperty(CLIENT_KEYPASSWORD, null); + + /** + * The store path of client-side X.509 certificate. + */ + public static String tlsClientCertPath = System.getProperty(CLIENT_CERTPATH, null); + + /** + * The store path of trusted certificates for verifying the server endpoint's certificate. + */ + public static String tlsClientTrustCertPath = System.getProperty(CLIENT_TRUST_CERT, null); + + /** + * The store path of server-side private key. + */ + public static String tlsServerKeyPath = System.getProperty(SERVER_KEYPATH, null); + + /** + * The password of the server-side private key. + */ + public static String tlsServerKeyPassword = System.getProperty(SERVER_KEYPASSWORD, null); + + /** + * The store path of server-side X.509 certificate. + */ + public static String tlsServerCertPath = System.getProperty(SERVER_CERTPATH, null); + + /** + * The store path of trusted certificates for verifying the client endpoint's certificate. + */ + public static String tlsServerTrustCertPath = System.getProperty(SERVER_TRUST_CERT, null); + + /** + * tls file check interval , default is 10 min. + */ + public static int tlsFileCheckInterval = Integer.parseInt(System.getProperty(CHECK_INTERVAL, "10")); + +} diff --git a/client/src/main/java/com/alibaba/nacos/client/utils/IpUtil.java b/common/src/main/java/com/alibaba/nacos/common/utils/IpUtils.java similarity index 92% rename from client/src/main/java/com/alibaba/nacos/client/utils/IpUtil.java rename to common/src/main/java/com/alibaba/nacos/common/utils/IpUtils.java index b6c3a3c6f16..9f092e9521c 100644 --- a/client/src/main/java/com/alibaba/nacos/client/utils/IpUtil.java +++ b/common/src/main/java/com/alibaba/nacos/common/utils/IpUtils.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.alibaba.nacos.client.utils; - -import com.alibaba.nacos.common.utils.StringUtils; +package com.alibaba.nacos.common.utils; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -26,7 +24,7 @@ * * @author Nacos */ -public class IpUtil { +public class IpUtils { private static final Pattern IPV4_PATTERN = Pattern .compile("^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$"); @@ -48,4 +46,4 @@ private static boolean isMatch(String data, Pattern pattern) { Matcher mat = pattern.matcher(data); return mat.find(); } -} +} \ No newline at end of file diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/IPUtil.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/IPUtil.java deleted file mode 100644 index 87302424f5d..00000000000 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/IPUtil.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * Licensed 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 com.alibaba.nacos.config.server.utils; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Ip util. - * - * @author Nacos - */ -@SuppressWarnings({"PMD.ClassNamingShouldBeCamelRule", "checkstyle:AbbreviationAsWordInName"}) -public class IPUtil { - - /** - * Determine if it is an IPV4 address. - */ - public static boolean isIPV4(String addr) { - if (null == addr) { - return false; - } - String rexp = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$"; - - Pattern pat = Pattern.compile(rexp); - - Matcher mat = pat.matcher(addr); - - boolean ipAddress = mat.find(); - return ipAddress; - } - - /** - * Determine if it is an IPV6 address. - */ - public static boolean isIPV6(String addr) { - if (null == addr) { - return false; - } - String rexp = "^([\\da-fA-F]{1,4}:){7}[\\da-fA-F]{1,4}$"; - - Pattern pat = Pattern.compile(rexp); - - Matcher mat = pat.matcher(addr); - - boolean ipAddress = mat.find(); - return ipAddress; - } -}