blob: ec3b0f00609eda9d1a318246df7065ef42806449 [file] [log] [blame]
/*
* 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.netbeans.modules.payara.tooling.utils;
import java.io.IOException;
import java.net.ConnectException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import org.netbeans.modules.payara.tooling.PayaraIdeException;
import org.netbeans.modules.payara.tooling.logging.Logger;
import java.io.PrintWriter;
/**
* Networking utilities
* <p/>
* @author Tomas Kraus
*/
public class NetUtils {
////////////////////////////////////////////////////////////////////////////
// Inner classes //
////////////////////////////////////////////////////////////////////////////
/**
* Comparator for {@link InetAddress} instances to be sorted.
*/
public static class InetAddressComparator
implements Comparator<InetAddress> {
/**
* Compares values of <code>InetAddr</code> instances.
* <p/>
* @param ip1 First <code>InetAddr</code> instance to be compared.
* @param ip2 Second <code>InetAddr</code> instance to be compared.
* @return A negative integer, zero, or a positive integer as the first
* argument is less than, equal to, or greater than the second.
*/
@Override
public int compare(final InetAddress ip1, final InetAddress ip2) {
byte[] addr1 = ip1.getAddress();
byte[] addr2 = ip2.getAddress();
int result = addr2.length - addr1.length;
if (result == 0) {
for (int i = 0; result == 0 && i < addr1.length; i++) {
result = addr1[i] - addr2[i];
}
}
return result;
}
}
////////////////////////////////////////////////////////////////////////////
// Class attributes //
////////////////////////////////////////////////////////////////////////////
/** Logger instance for this class. */
private static final Logger LOGGER = new Logger(ServerUtils.class);
/** Port check timeout [ms]. */
public static final int PORT_CHECK_TIMEOUT = 2000;
/** Comparator for {@link InetAddress} instances to be sorted. */
private static final InetAddressComparator INET_ADDRESS_COMPARATOR
= new InetAddressComparator();
////////////////////////////////////////////////////////////////////////////
// Static methods //
////////////////////////////////////////////////////////////////////////////
/**
* Finds out if server is running on remote host by connecting to remote
* host and port.
* <p/>
* @param host Server host.
* @param port Server port.
* @param timeout Network connection timeout [ms].
* @return Returns <code>true</code> when server port is accepting
* connections or <code>false</code> otherwise.
*/
public static boolean isPortListeningRemote(final String host,
final int port, final int timeout) {
final String METHOD = "isPortListeningRemote";
if (null == host) {
return false;
}
Socket socket = null;
try {
socket = new Socket();
socket.connect(new InetSocketAddress(host, port), timeout);
return true;
} catch (IOException ex) {
return false;
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException ioe) {
LOGGER.log(Level.INFO, METHOD,
"closeError", ioe.getLocalizedMessage());
}
}
}
}
/**
* Finds out if server is running on remote host by connecting to remote
* host and port.
* <p/>
* @param host Server host.
* @param port Server port.
* @return Returns <code>true</code> when server port is accepting
* connections or <code>false</code> otherwise.
*/
public static boolean isPortListeningRemote(final String host,
final int port) {
return isPortListeningRemote(host, port, 0);
}
/**
* Finds out if server is running on local host by binding to local port.
* <p/>
* @param host Server host or <code>null</code> value for address of the
* loopback interface.
* @param port Server port.
* @return Returns <code>true</code> when server port is accepting
* connections or <code>false</code> otherwise.
*/
public static boolean isPortListeningLocal(final String host,
final int port) {
final String METHOD = "isPortListeningLocal";
ServerSocket socket = null;
try {
InetAddress ia = InetAddress.getByName(host);
socket = new ServerSocket(port, 1, ia);
return false;
} catch (IOException ioe) {
return true;
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException ioe) {
LOGGER.log(Level.INFO, METHOD,
"closeError", ioe.getLocalizedMessage());
}
}
}
}
/**
* Determine whether an HTTP listener is secure or not..
* <p/>
* This method accepts a host name and port #. It uses this information
* to attempt to connect to the port, send a test query, analyze the
* result to determine if the port is secure or is not secure (currently
* only HTTP / HTTPS is supported).
* it might emit a warning in the server log for Payara cases.
* No Harm, just an annoying warning, so we need to use this call only
* when really needed.
* <p/>
* @param hostname The host for the HTTP listener.
* @param port The port for the HTTP listener.
* @throws IOException
* @throws SocketTimeoutException
* @throws ConnectException
*/
public static boolean isSecurePort(String hostname, int port)
throws IOException, ConnectException, SocketTimeoutException {
return isSecurePort(hostname,port, 0);
}
/**
* Determine whether an HTTP listener is secure or not..
* <p/>
* This method accepts a host name and port #. It uses this information
* to attempt to connect to the port, send a test query, analyze the
* result to determine if the port is secure or is not secure (currently
* only HTTP / HTTPS is supported).
* it might emit a warning in the server log for Payara cases.
* No Harm, just an annoying warning, so we need to use this call only
* when really needed.
* <p/>
* @param hostname The host for the HTTP listener.
* @param port The port for the HTTP listener.
* @param depth Method calling depth.
* @throws IOException
* @throws SocketTimeoutException
* @throws ConnectException
*/
private static boolean isSecurePort(String hostname, int port, int depth)
throws IOException, ConnectException, SocketTimeoutException {
final String METHOD = "isSecurePort";
boolean isSecure = true;
try (Socket socket = new Socket()) {
try {
LOGGER.log(Level.FINE, METHOD, "socket");
socket.connect(new InetSocketAddress(hostname, port), PORT_CHECK_TIMEOUT);
socket.setSoTimeout(PORT_CHECK_TIMEOUT);
// This could be bug 70020 due to SOCKs proxy not having localhost
} catch (SocketException ex) {
String socksNonProxyHosts = System.getProperty("socksNonProxyHosts");
if(socksNonProxyHosts != null && socksNonProxyHosts.indexOf("localhost") < 0) {
String localhost = socksNonProxyHosts.length() > 0 ? "|localhost" : "localhost";
System.setProperty("socksNonProxyHosts", socksNonProxyHosts + localhost);
ConnectException ce = new ConnectException();
ce.initCause(ex);
throw ce; //status unknow at this point
//next call, we'll be ok and it will really detect if we are secure or not
}
}
java.io.InputStream istream = socket.getInputStream();
//This is the test query used to ping the server in an attempt to
//determine if it is secure or not.
String testQuery = "GET / HTTP/1.0";
PrintWriter pw = new PrintWriter(socket.getOutputStream());
pw.println(testQuery);
pw.println();
pw.flush();
byte[] respArr = new byte[1024];
while (istream.read(respArr) != -1) {
String resp = new String(respArr);
if (checkHelper(resp) == false) {
isSecure = false;
break;
}
}
}
return isSecure;
}
private static boolean checkHelper(String respText) {
boolean isSecure = true;
if (respText.startsWith("http/1.") || respText.startsWith("HTTP/1.")) {
isSecure = false;
} else if (respText.contains("<html")) {
isSecure = false;
} else if (respText.contains("</html")) {
// New test added to resolve 106245
// when the user has the IDE use a proxy (like webcache.foo.bar.com),
// the response comes back as "d><title>....</html>". It looks like
// something eats the "<html><hea" off the front of the data that
// gets returned.
//
// This test makes an allowance for that behavior. I figure testing
// the likely "last bit" is better than testing a bit that is close
// to the data that seems to get eaten.
//
isSecure = false;
} else if (respText.contains("connection: ")) {
isSecure = false;
}
return isSecure;
}
/**
* Retrieve {@link Set} of IP addresses of this host.
* <p/>
* @return {@link Set} of IP addresses of this host.
* @throws PayaraIdeException if addresses of this host could not
* be retrieved.
*/
public static Set<InetAddress> getHostIPs() {
final String METHOD = "getHostIPs";
Set<InetAddress> addrs = new TreeSet<>(INET_ADDRESS_COMPARATOR);
try {
Enumeration<NetworkInterface> ifaces = NetworkInterface.
getNetworkInterfaces();
while (ifaces.hasMoreElements()) {
NetworkInterface iface = ifaces.nextElement();
for (Enumeration<InetAddress> e = iface.getInetAddresses(); e.hasMoreElements(); ) {
InetAddress a = e.nextElement();
addrs.add(a);
}
}
} catch (SocketException se) {
addrs = null;
throw new PayaraIdeException(LOGGER.excMsg(METHOD, "exception"));
}
return addrs;
}
/**
* Retrieve {@link Set} of IPv4 addresses of this host.
* <p/>
* @return {@link Set} of IPv4 addresses of this host.
*/
public static Set<Inet4Address> getHostIP4s() {
Set<Inet4Address> addrs = new TreeSet<>(INET_ADDRESS_COMPARATOR);
for (InetAddress a : getHostIPs()) {
if (a instanceof Inet4Address) {
addrs.add((Inet4Address) a);
}
}
return addrs;
}
/**
* Retrieve {@link Set} of IPv6 addresses of this host.
* <p/>
* @return {@link Set} of IPv6 addresses of this host.
*/
public static Set<Inet6Address> getHostIP6s() {
Set<Inet6Address> addrs = new TreeSet<>(INET_ADDRESS_COMPARATOR);
for (InetAddress a : getHostIPs()) {
if (a instanceof Inet6Address) {
addrs.add((Inet6Address) a);
}
}
return addrs;
}
}