package org.wso2.carbon.device.mgt.iot.agent.kura.firealarm.core.communication;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.File;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class CommunicationUtils {
private static final Log log = LogFactory.getLog(CommunicationUtils.class);
public static final int MIN_PORT_NUMBER = 9000;
public static final int MAX_PORT_NUMBER = 11000;
/**
* Given a server endpoint as a String, this method splits it into Protocol, Host and Port
*
* @param ipString a network endpoint in the format - '<PROTOCOL>://<HOST>:<PORT>'
* @return a map with keys "Protocol", "Host" & "Port" for the related values from the ipString
* @throws CommunicationHandlerException
*/
public static Map<String, String> getHostAndPort(String ipString)
throws CommunicationHandlerException {
Map<String, String> ipPortMap = new HashMap<String, String>();
String[] ipPortArray = ipString.split(":");
if (ipPortArray.length != 3) {
String errorMsg =
"The IP String - '" + ipString +
"' is invalid. It needs to be in format '<PROTOCOL>://<HOST>:<PORT>'.";
log.info(errorMsg);
throw new CommunicationHandlerException(errorMsg);
}
ipPortMap.put("Protocol", ipPortArray[0]);
ipPortMap.put("Host", ipPortArray[1].replace(File.separator, ""));
ipPortMap.put("Port", ipPortArray[2]);
return ipPortMap;
}
/**
* This method validates whether a specific IP Address is of IPv4 type
*
* @param ipAddress the IP Address which needs to be validated
* @return true if it is of IPv4 type and false otherwise
*/
public static boolean validateIPv4(String ipAddress) {
try {
if (ipAddress == null || ipAddress.isEmpty()) {
return false;
}
String[] parts = ipAddress.split("\\.");
if (parts.length != 4) {
return false;
}
for (String s : parts) {
int i = Integer.parseInt(s);
if ((i < 0) || (i > 255)) {
return false;
}
}
return !ipAddress.endsWith(".");
} catch (NumberFormatException nfe) {
log.warn("The IP Address: " + ipAddress + " could not " +
"be validated against IPv4-style");
return false;
}
}
public static Map<String, String> getInterfaceIPMap() throws CommunicationHandlerException {
Map<String, String> interfaceToIPMap = new HashMap<String, String>();
Enumeration<NetworkInterface> networkInterfaces;
String networkInterfaceName = "";
String ipAddress;
try {
networkInterfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException exception) {
String errorMsg =
"Error encountered whilst trying to get the list of network-interfaces";
log.error(errorMsg);
throw new CommunicationHandlerException(errorMsg, exception);
}
try {
for (; networkInterfaces.hasMoreElements(); ) {
networkInterfaceName = networkInterfaces.nextElement().getName();
if (log.isDebugEnabled()) {
log.debug("Network Interface: " + networkInterfaceName);
log.debug("------------------------------------------");
}
Enumeration<InetAddress> interfaceIPAddresses = NetworkInterface.getByName(
networkInterfaceName).getInetAddresses();
for (; interfaceIPAddresses.hasMoreElements(); ) {
ipAddress = interfaceIPAddresses.nextElement().getHostAddress();
if (log.isDebugEnabled()) {
log.debug("IP Address: " + ipAddress);
}
if (CommunicationUtils.validateIPv4(ipAddress)) {
interfaceToIPMap.put(networkInterfaceName, ipAddress);
}
}
if (log.isDebugEnabled()) {
log.debug("------------------------------------------");
}
}
} catch (SocketException exception) {
String errorMsg =
"Error encountered whilst trying to get the IP Addresses of the network " +
"interface: " + networkInterfaceName;
log.error(errorMsg);
throw new CommunicationHandlerException(errorMsg, exception);
}
return interfaceToIPMap;
}
/**
* Attempts to find a free port between the MIN_PORT_NUMBER(9000) and MAX_PORT_NUMBER(11000).
* Tries 'RANDOMLY picked' port numbers between this range up-until "randomAttempts" number of
* times. If still fails, then tries each port in descending order from the MAX_PORT_NUMBER
* whilst skipping already attempted ones via random selection.
*
* @param randomAttempts no of times to TEST port numbers picked randomly over the given range
* @return an available/free port
*/
public static synchronized int getAvailablePort(int randomAttempts) {
ArrayList<Integer> failedPorts = new ArrayList<Integer>(randomAttempts);
Random randomNum = new Random();
int randomPort = MAX_PORT_NUMBER;
while (randomAttempts > 0) {
randomPort = randomNum.nextInt(MAX_PORT_NUMBER - MIN_PORT_NUMBER) + MIN_PORT_NUMBER;
if (checkIfPortAvailable(randomPort)) {
return randomPort;
}
failedPorts.add(randomPort);
randomAttempts--;
}
randomPort = MAX_PORT_NUMBER;
while (true) {
if (!failedPorts.contains(randomPort) && checkIfPortAvailable(randomPort)) {
return randomPort;
}
randomPort--;
}
}
private static boolean checkIfPortAvailable(int port) {
ServerSocket tcpSocket = null;
DatagramSocket udpSocket = null;
try {
tcpSocket = new ServerSocket(port);
tcpSocket.setReuseAddress(true);
udpSocket = new DatagramSocket(port);
udpSocket.setReuseAddress(true);
return true;
} catch (IOException ex) {
// denotes the port is in use
} finally {
if (tcpSocket != null) {
try {
tcpSocket.close();
} catch (IOException e) {
/* not to be thrown */
}
}
if (udpSocket != null) {
udpSocket.close();
}
}
return false;
}
}