package games.strategy.net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.google.common.base.Joiner;
import com.google.common.collect.FluentIterable;
import com.google.common.primitives.Bytes;
import games.strategy.debug.ClientLogger;
import games.strategy.util.MD5Crypt;
public class MacFinder {
/**
* Should result in something like this: $1$MH$345ntXD4G3AKpAeHZdaGe3.
*/
public static String getHashedMacAddress() {
final String mac = getMacAddress();
if (mac == null) {
throw new IllegalArgumentException(
"You have an invalid MAC address or TripleA can't find your mac address");
}
return MD5Crypt.crypt(mac, "MH");
}
private static String getMacAddress() {
// We must try different methods of obtaining the mac address because not all the methods work on each system, and
// if we can't obtain
// the mac, we can't login to the lobby
// First, try to get the mac address of the local host network interface
try {
final InetAddress address = InetAddress.getLocalHost();
final NetworkInterface localHostNI = NetworkInterface.getByInetAddress(address);
if (localHostNI != null) {
final byte[] rawMac = localHostNI.getHardwareAddress();
final String mac = convertMacBytesToString(rawMac);
if (isMacValid(mac)) {
return mac;
}
}
} catch (final SocketException | UnknownHostException e) {
ClientLogger.logError("Error while trying to get a valid MAC adress", e);
}
// Next, try to get the mac address of the first network interfaces that has an accessible mac address
try {
for (final NetworkInterface ni : Collections.list(NetworkInterface.getNetworkInterfaces())) {
final byte[] rawMac = ni.getHardwareAddress();
final String mac = convertMacBytesToString(rawMac);
if (isMacValid(mac)) {
return mac;
}
}
} catch (final SocketException e) {
ClientLogger.logError("Error while trying to get a valid MAC adress", e);
}
// Next, try to get the mac address by calling the 'getmac' app that exists in Windows, Mac, and possibly others.
/*
* Physical Address Transport Name
* =================== ==========================================================
* 00-1F-C6-F9-EC-E8 \Device\Tcpip_{99F55DF7-8C43-464C-A8A9-FA3F847467CB}
*/
try {
final String results = executeCommandAndGetResults("getmac");
final String mac = tryToParseMACFromOutput(results, Arrays.asList("-", ":", "."), false);
if (isMacValid(mac)) {
return mac;
}
} catch (final Exception e) {
ClientLogger.logQuietly("Error while trying to get mac address", e);
}
// Next, try to get the mac address by calling the 'ipconfig -all' app that exists in Windows and possibly others.
/*
* ...
* Physical Address. . . . . . . . . : 00-1C-D3-F8-DC-E8
* ...
*/
try {
final String results = executeCommandAndGetResults("ipconfig -all");
final String mac = tryToParseMACFromOutput(results, Arrays.asList("-", ":", "."), false);
if (isMacValid(mac)) {
return mac;
}
} catch (final Exception e) {
ClientLogger.logQuietly("Error while trying to get mac address", e);
}
try {
// ipconfig -all does not work on my computer, while ipconfig /all does not work on others computers
final String results = executeCommandAndGetResults("ipconfig /all");
final String mac = tryToParseMACFromOutput(results, Arrays.asList("-", ":", "."), false);
if (isMacValid(mac)) {
return mac;
}
} catch (final Exception e) {
ClientLogger.logQuietly("Error while trying to get mac address", e);
}
// Next, try to get the mac address by calling the 'ifconfig -a' app that exists in Linux and possibly others. May
// have 1 or 2 spaces
// between Ethernet and HWaddr, and may be wireless instead of ethernet.
/*
* ...
* eth0 Link encap:Ethernet HWaddr 00:08:C7:1B:8C:02
* ...
*/
try {
final String results = executeCommandAndGetResults("ifconfig -a");
// Allow the parser to try adding a zero to
// the beginning
final String mac = tryToParseMACFromOutput(results, Arrays.asList(":", "-", "."), true);
if (isMacValid(mac)) {
return mac;
}
} catch (final Exception e) {
ClientLogger.logQuietly("Error while trying to get mac address", e);
}
// Next, try to get the mac address by calling the '/sbin/ifconfig -a' app that exists in Linux and possibly others.
// May have 1 or 2
// spaces between Ethernet and HWaddr, and may be wireless instead of ethernet.
/*
* ...
* eth0 Link encap:Ethernet HWaddr 00:08:C7:1B:8C:02
* ...
*/
try {
final String results = executeCommandAndGetResults("/sbin/ifconfig -a");
// Allow the parser to try adding a zero to
// the beginning
final String mac = tryToParseMACFromOutput(results, Arrays.asList(":", "-", "."), true);
if (isMacValid(mac)) {
return mac;
}
} catch (final Exception e) {
ClientLogger.logQuietly("Error while trying to get mac address", e);
}
// Next, try to get the mac address by calling the 'dmesg' app that exists in FreeBSD and possibly others.
/*
* ...
* [ 405.681688] wlan0_rename: associate with AP 00:16:f8:40:3e:bd
* [ 405.683255] wlan0_rename: RX ReassocResp from 00:16:f8:40:3e:bd (capab=0x411 status=0 aid=4)
* ...
*/
try {
final String results = executeCommandAndGetResults("dmesg");
final String mac = tryToParseMACFromOutput(results, Arrays.asList(":", "-", "."), false);
if (isMacValid(mac)) {
return mac;
}
} catch (final Exception e) {
ClientLogger.logQuietly("Error while trying to get mac address", e);
}
return null;
}
private static String executeCommandAndGetResults(final String command) {
Process p = null;
try {
p = new ProcessBuilder(command).start();
} catch (final Exception e) {
try {
p = Runtime.getRuntime().exec(command);
} catch (final IOException e2) {
ClientLogger.logQuietly("Ignoring error while executing command: " + command, e);
}
}
if (p == null) {
return null;
}
try {
final StringBuilder builder = new StringBuilder();
final BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
while (true) {
try {
final String line = in.readLine();
if (line == null) {
break;
}
builder.append(line).append("\r\n");
} catch (final IOException e) {
break;
}
}
in.close();
return builder.toString();
} catch (final IOException e) {
ClientLogger.logQuietly("IOException while executing command: " + command, e);
return null;
}
}
private static String convertMacBytesToString(final byte[] mac) {
if (mac == null) {
return null;
}
return Joiner.on('.')
.join(FluentIterable.from(Bytes.asList(mac)).transform(macbyte -> String.format("%02X", macbyte)));
}
private static boolean isMacValid(final String mac) {
if (mac == null || mac.length() != 17 || !mac.contains(".") || !mac.matches("[0-9A-Fa-f.]+")) {
return false;
}
final char[] chars = mac.toCharArray();
int periodCount = 0;
int nonZeroNumberCount = 0;
int i = 1;
for (final char ch : chars) {
if (ch == '.' && (i % 3 != 0)) {
return false;
}
if (ch == '.') {
periodCount++;
}
if (ch != '.' && ch != '0') {
nonZeroNumberCount++;
}
i++;
}
if (periodCount != 5 || mac.equals("00.00.00.00.00.E0") || nonZeroNumberCount == 0) {
return false;
}
return true;
}
private static String tryToParseMACFromOutput(final String output, final List<String> possibleSeparators,
final boolean allowAppendedZeroCheck) {
if (output == null || output.trim().length() < 6) {
return null;
}
for (final String separator : possibleSeparators) {
String leftToSearch = output;
while (leftToSearch != null && leftToSearch.length() > 0 && leftToSearch.contains(separator)) {
int macStartIndex = Math.max(0, leftToSearch.indexOf(separator) - 2);
String rawMac = leftToSearch.substring(macStartIndex, Math.min(macStartIndex + 17, leftToSearch.length()));
if (rawMac != null && rawMac.length() > 0) {
String mac = rawMac.replace(separator, ".");
if (isMacValid(mac)) {
return mac;
} else if (allowAppendedZeroCheck && rawMac.substring(2, 3).equals(separator)) {
// If mac is invalid, see if it works after adding a zero to the front
macStartIndex = Math.max(0, leftToSearch.indexOf(separator) - 1);
rawMac = "0" + leftToSearch.substring(macStartIndex, Math.min(macStartIndex + 16, leftToSearch.length()));
mac = rawMac.replace(separator, ".");
if (isMacValid(mac)) {
return mac;
}
}
}
// We only invalidate the one separator char and what's before it, so that '-ether 89-94-19...' would not fail,
// then cause the -
// after 89 to get ignored (Not sure if this situation really occurs)
leftToSearch = leftToSearch.substring(Math.min(macStartIndex + 1, leftToSearch.length()));
}
}
return null;
}
}