diff --git a/G-Earth/src/main/java/gearth/protocol/HConnection.java b/G-Earth/src/main/java/gearth/protocol/HConnection.java index 75f79fe..5724528 100644 --- a/G-Earth/src/main/java/gearth/protocol/HConnection.java +++ b/G-Earth/src/main/java/gearth/protocol/HConnection.java @@ -59,7 +59,9 @@ public class HConnection { private void startMITM() { try { - proxyProvider.start(); + if (proxyProvider != null) { + proxyProvider.start(); + } } catch (IOException e) { e.printStackTrace(); } @@ -150,4 +152,8 @@ public class HConnection { public boolean isRawIpMode() { return proxyProvider != null && proxyProvider instanceof RawIpProxyProvider; } + + public ProxyProvider getProxyProvider() { + return proxyProvider; + } } diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/ProxyProviderFactory.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/ProxyProviderFactory.java index 7159240..558d2a7 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/ProxyProviderFactory.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/ProxyProviderFactory.java @@ -84,7 +84,15 @@ public class ProxyProviderFactory { } if (hostIsIpAddress(domain)) { - return new RawIpProxyProvider(proxySetter, stateSetter, hConnection, domain, port); + SocksConfiguration config = SocksProxyProvider.getSocksConfig(); + if (RawIpProxyProvider.isNoneConnected(domain) && + (!config.useSocks() || config.dontUseFirstTime()) ) { + return new RawIpProxyProvider(proxySetter, stateSetter, hConnection, domain, port); + } + else if (config.useSocks()) { + return new SocksProxyProvider(proxySetter, stateSetter, hConnection, domain, port); + } + return null; } else { List potentialHost = new ArrayList<>(); diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/RawIpProxyProvider.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/RawIpProxyProvider.java index 7d777ee..1c16c13 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/RawIpProxyProvider.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/RawIpProxyProvider.java @@ -1,5 +1,6 @@ package gearth.protocol.connection.proxy; +import gearth.misc.Cacher; import gearth.protocol.HConnection; import gearth.protocol.connection.HProxy; import gearth.protocol.connection.HProxySetter; @@ -11,11 +12,14 @@ import javafx.application.Platform; import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; import javafx.scene.layout.Region; +import org.json.JSONObject; import java.io.IOException; +import java.math.BigInteger; import java.net.*; import java.util.LinkedList; import java.util.Queue; +import java.util.UUID; public class RawIpProxyProvider extends ProxyProvider { @@ -25,7 +29,7 @@ public class RawIpProxyProvider extends ProxyProvider { private IpMapper ipMapper = IpMapperFactory.get(); private boolean hasMapped = false; - private HProxy proxy = null; + protected HProxy proxy = null; public RawIpProxyProvider(HProxySetter proxySetter, HStateSetter stateSetter, HConnection hConnection, String input_host, int input_port) { super(proxySetter, stateSetter, hConnection); @@ -48,31 +52,8 @@ public class RawIpProxyProvider extends ProxyProvider { stateSetter.setState(HState.PREPARING); proxy = new HProxy(input_host, input_host, input_port, input_port, "0.0.0.0"); - Queue preConnectedServerConnections = new LinkedList<>(); - for (int i = 0; i < 3; i++) { - Socket s1 = new Socket(); - s1.setSoTimeout(1200); - try { - s1.connect(new InetSocketAddress(proxy.getActual_domain(), proxy.getActual_port()), 1200); - } - catch (SocketTimeoutException e) { - stateSetter.setState(HState.NOT_CONNECTED); - Platform.runLater(() -> { - Alert alert = new Alert(Alert.AlertType.ERROR, "You entered invalid connection information, G-Earth could not connect", ButtonType.OK); - alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); - alert.setResizable(false); - alert.show(); - }); - return; - } - - preConnectedServerConnections.add(s1); - Thread.sleep(50); - } - - ipMapper.enable(); - ipMapper.addMapping(proxy.getActual_domain()); - hasMapped = true; + onBeforeIpMapping(); + maybeAddMapping(); if (HConnection.DEBUG) System.out.println("Added mapping for raw IP"); @@ -80,9 +61,6 @@ public class RawIpProxyProvider extends ProxyProvider { proxy.initProxy(proxy_server); stateSetter.setState(HState.WAITING_FOR_CLIENT); - - - while ((hConnection.getState() == HState.WAITING_FOR_CLIENT) && !proxy_server.isClosed()) { try { if (HConnection.DEBUG) System.out.println("try accept proxy"); @@ -92,18 +70,13 @@ public class RawIpProxyProvider extends ProxyProvider { new Thread(() -> { try { - if (preConnectedServerConnections.isEmpty()) { - if (HConnection.DEBUG) System.out.println("pre-made server connections ran out of stock"); - } - else { - startProxyThread(client, preConnectedServerConnections.poll(), proxy); - } + createProxyThread(client); } catch (InterruptedException | IOException e) { e.printStackTrace(); } }).start(); - } catch (IOException e1) { + } catch (IOException ignored) { } } @@ -119,8 +92,7 @@ public class RawIpProxyProvider extends ProxyProvider { public void abort() { stateSetter.setState(HState.ABORTING); if (hasMapped) { - ipMapper.deleteMapping(proxy.getActual_domain()); - hasMapped = false; + maybeRemoveMapping(); } tryCloseProxy(); super.abort(); @@ -129,8 +101,7 @@ public class RawIpProxyProvider extends ProxyProvider { @Override protected void onConnectEnd() { if (hasMapped) { - ipMapper.deleteMapping(proxy.getActual_domain()); - hasMapped = false; + maybeRemoveMapping(); } tryCloseProxy(); super.onConnectEnd(); @@ -145,4 +116,147 @@ public class RawIpProxyProvider extends ProxyProvider { } } } + + private Queue preConnectedServerConnections; + + protected void onBeforeIpMapping() throws IOException, InterruptedException { + preConnectedServerConnections = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + Socket s1 = new Socket(); + s1.setSoTimeout(1200); + try { + s1.connect(new InetSocketAddress(proxy.getActual_domain(), proxy.getActual_port()), 1200); + } + catch (SocketTimeoutException e) { + stateSetter.setState(HState.NOT_CONNECTED); + showInvalidConnectionError(); + return; + } + + preConnectedServerConnections.add(s1); + Thread.sleep(50); + } + } + + protected void createProxyThread(Socket client) throws IOException, InterruptedException { + if (preConnectedServerConnections.isEmpty()) { + if (HConnection.DEBUG) System.out.println("pre-made server connections ran out of stock"); + } + else { + startProxyThread(client, preConnectedServerConnections.poll(), proxy); + } + } + + protected void showInvalidConnectionError() { + Platform.runLater(() -> { + Alert alert = new Alert(Alert.AlertType.ERROR, "You entered invalid connection information, G-Earth could not connect", ButtonType.OK); + alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); + alert.setResizable(false); + alert.show(); + }); + } + + + private void maybeAddMapping() { + if (!hasMapped) { + hasMapped = true; + if (isNoneConnected()) { + ipMapper.enable(); + ipMapper.addMapping(proxy.getActual_domain()); + } + addMappingCache(); + } + + } + + protected void maybeRemoveMapping() { + if (hasMapped) { + hasMapped = false; + removeMappingCache(); + if (isNoneConnected()) { + ipMapper.deleteMapping(proxy.getActual_domain()); + } + } + } + + + + private static final UUID INSTANCE_ID = UUID.randomUUID(); + private static final String RAWIP_CONNECTIONS = "rawip_connections"; + + + // let other G-Earth instances know you're connected + private void addMappingCache() { + new Thread(() -> { + while (hasMapped) { + updateMappingCache(); + try { + Thread.sleep(55000); + } catch (InterruptedException ignored) {} + } + }).start(); + } + + // checks if no G-Earth instances are connected + // every G-Earth instance is supposed to update if it's still connected every 60 seconds + private boolean isNoneConnected() { + return isNoneConnected(proxy.getActual_domain()); + } + + private void updateMappingCache() { + JSONObject connections = getCurrentConnectionsCache(); + + JSONObject instance = new JSONObject(); + instance.put("timestamp", BigInteger.valueOf(System.currentTimeMillis())); + + connections.put(INSTANCE_ID.toString(), instance); + saveCurrentConnectionsCache(connections); + } + + private void removeMappingCache() { + JSONObject connections = getCurrentConnectionsCache(); + connections.remove(proxy.getActual_domain()); + saveCurrentConnectionsCache(connections); + } + + private JSONObject getCurrentConnectionsCache() { + return getCurrentConnectionsCache(proxy.getActual_domain()); + } + + private void saveCurrentConnectionsCache(JSONObject connections) { + JSONObject gearthConnections = Cacher.getCacheContents().getJSONObject(RAWIP_CONNECTIONS); + gearthConnections.put(proxy.getActual_domain(), connections); + Cacher.put(RAWIP_CONNECTIONS, gearthConnections); + } + + + static private JSONObject getCurrentConnectionsCache(String actual_host) { + JSONObject gearthConnections = Cacher.getCacheContents().getJSONObject(RAWIP_CONNECTIONS); + if (gearthConnections == null) { + gearthConnections = new JSONObject(); + Cacher.put(RAWIP_CONNECTIONS, gearthConnections); + } + JSONObject connections = gearthConnections.getJSONObject(actual_host); + if (connections == null) { + connections = new JSONObject(); + gearthConnections.put(actual_host, connections); + Cacher.put(RAWIP_CONNECTIONS, gearthConnections); + } + return connections; + } + + static boolean isNoneConnected(String actual_host) { + JSONObject connections = getCurrentConnectionsCache(actual_host); + + BigInteger timeoutTimestamp = BigInteger.valueOf(System.currentTimeMillis() - 60000); + for (String key : connections.keySet()) { + JSONObject connection = connections.getJSONObject(key); + if (!connection.getString("id").equals(INSTANCE_ID.toString())) { + if (connection.getBigInteger("timestamp").compareTo(timeoutTimestamp) > 0) { + return false; + } + } + } + return true; + } } diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/SocksConfiguration.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/SocksConfiguration.java new file mode 100644 index 0000000..3821bf2 --- /dev/null +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/SocksConfiguration.java @@ -0,0 +1,9 @@ +package gearth.protocol.connection.proxy; + +public interface SocksConfiguration { + boolean useSocks(); + + int getSocksPort(); + String getSocksHost(); + boolean dontUseFirstTime(); +} diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/SocksProxyProvider.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/SocksProxyProvider.java new file mode 100644 index 0000000..d970864 --- /dev/null +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/SocksProxyProvider.java @@ -0,0 +1,57 @@ +package gearth.protocol.connection.proxy; + +import gearth.protocol.HConnection; +import gearth.protocol.connection.HProxySetter; +import gearth.protocol.connection.HState; +import gearth.protocol.connection.HStateSetter; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Socket; +import java.net.SocketTimeoutException; + +public class SocksProxyProvider extends RawIpProxyProvider { + + private static SocksConfiguration socksConfig = null; + + public SocksProxyProvider(HProxySetter proxySetter, HStateSetter stateSetter, HConnection hConnection, String input_host, int input_port) { + super(proxySetter, stateSetter, hConnection, input_host, input_port); + } + + @Override + protected void onBeforeIpMapping() { + // do nothing + } + + @Override + protected void createProxyThread(Socket client) throws IOException, InterruptedException { + if (socksConfig == null) { + maybeRemoveMapping(); + stateSetter.setState(HState.NOT_CONNECTED); + showInvalidConnectionError(); + return; + } + + Proxy socks = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(socksConfig.getSocksHost(), socksConfig.getSocksPort())); + Socket server = new Socket(socks); + server.setSoTimeout(1200); + try { + server.connect(new InetSocketAddress(proxy.getActual_domain(), proxy.getActual_port()), 1200); + startProxyThread(client, server, proxy); + } + catch (SocketTimeoutException e) { + maybeRemoveMapping(); + stateSetter.setState(HState.NOT_CONNECTED); + showInvalidConnectionError(); + } + } + + public static void setSocksConfig(SocksConfiguration configuration) { + socksConfig = configuration; + } + + public static SocksConfiguration getSocksConfig() { + return socksConfig; + } +} diff --git a/G-Earth/src/main/java/gearth/protocol/hostreplacer/ipmapping/WindowsIpMapper.java b/G-Earth/src/main/java/gearth/protocol/hostreplacer/ipmapping/WindowsIpMapper.java index c3acd5f..6271dda 100644 --- a/G-Earth/src/main/java/gearth/protocol/hostreplacer/ipmapping/WindowsIpMapper.java +++ b/G-Earth/src/main/java/gearth/protocol/hostreplacer/ipmapping/WindowsIpMapper.java @@ -31,7 +31,7 @@ public class WindowsIpMapper implements IpMapper { runCommand("netsh", "interface", "ip", "delete", "address", "\"Loopback\"", ip); } - //todo implement + //todo implement, or don't @Override public List getCurrentMappings() { return new ArrayList<>(); diff --git a/G-Earth/src/main/java/gearth/ui/extra/ExtraController.java b/G-Earth/src/main/java/gearth/ui/extra/ExtraController.java index f489610..1cf96d8 100644 --- a/G-Earth/src/main/java/gearth/ui/extra/ExtraController.java +++ b/G-Earth/src/main/java/gearth/ui/extra/ExtraController.java @@ -2,19 +2,27 @@ package gearth.ui.extra; import gearth.misc.Cacher; import gearth.protocol.HConnection; -import gearth.protocol.connection.HProxy; import gearth.protocol.connection.HState; +import gearth.protocol.connection.proxy.SocksConfiguration; +import gearth.protocol.connection.proxy.SocksProxyProvider; import gearth.ui.SubForm; import gearth.ui.info.InfoController; import javafx.scene.control.*; import javafx.scene.layout.GridPane; +import org.json.JSONObject; /** * Created by Jonas on 06/04/18. */ -public class ExtraController extends SubForm { +public class ExtraController extends SubForm implements SocksConfiguration { public static final String NOTEPAD_CACHE_KEY = "notepad_text"; + public static final String SOCKS_CACHE_KEY = "socks_config"; + + public static final String SOCKS_IP = "ip"; + public static final String SOCKS_PORT = "port"; + public static final String IGNORE_ONCE = "ignore_once"; + public TextArea txtarea_notepad; @@ -24,17 +32,16 @@ public class ExtraController extends SubForm { public CheckBox cbx_advanced; public GridPane grd_advanced; - public CheckBox cbx_ovcinfo; - public GridPane grd_ovcinfo; - - public TextField txt_realPort; - public TextField txt_mitmIP; - public TextField txt_realIp; - public TextField txt_mitmPort; - public CheckBox cbx_disableDecryption; public CheckBox cbx_debug; + + public CheckBox cbx_useSocks; + public GridPane grd_socksInfo; + public TextField txt_socksPort; + public TextField txt_socksIp; + public CheckBox cbx_ignoreSocksOnce; + public void initialize() { url_troubleshooting.setTooltip(new Tooltip("https://github.com/sirjonasxx/G-Earth/wiki/Troubleshooting")); @@ -45,10 +52,19 @@ public class ExtraController extends SubForm { txtarea_notepad.setText(notepadInitValue); } + JSONObject socksInitValue = Cacher.getCacheContents().getJSONObject(SOCKS_CACHE_KEY); + if (socksInitValue != null) { + txt_socksIp.setText(socksInitValue.getString(SOCKS_IP)); + txt_socksPort.setText(socksInitValue.getString(SOCKS_PORT)); + cbx_ignoreSocksOnce.setSelected(socksInitValue.getBoolean(IGNORE_ONCE)); + } + cbx_debug.selectedProperty().addListener(observable -> HConnection.DEBUG = cbx_debug.isSelected()); cbx_disableDecryption.selectedProperty().addListener(observable -> HConnection.DECRYPTPACKETS = !cbx_disableDecryption.isSelected()); - cbx_ovcinfo.selectedProperty().addListener(observable -> grd_ovcinfo.setDisable(!cbx_ovcinfo.isSelected())); + cbx_useSocks.selectedProperty().addListener(observable -> grd_socksInfo.setDisable(!cbx_useSocks.isSelected())); + + SocksProxyProvider.setSocksConfig(this); } @Override @@ -69,12 +85,21 @@ public class ExtraController extends SubForm { @Override protected void onExit() { Cacher.put(NOTEPAD_CACHE_KEY, txtarea_notepad.getText()); + saveSocksConfig(); + } + + private void saveSocksConfig() { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(SOCKS_IP, txt_socksIp.getText()); + jsonObject.put(SOCKS_PORT, txt_socksPort.getText()); + jsonObject.put(IGNORE_ONCE, cbx_ignoreSocksOnce.isSelected()); + Cacher.put(SOCKS_CACHE_KEY, jsonObject); } private void updateAdvancedUI() { if (!cbx_advanced.isSelected()) { cbx_debug.setSelected(false); - cbx_ovcinfo.setSelected(false); + cbx_useSocks.setSelected(false); if (getHConnection().getState() == HState.NOT_CONNECTED) { cbx_disableDecryption.setSelected(false); } @@ -83,4 +108,25 @@ public class ExtraController extends SubForm { cbx_disableDecryption.setDisable(getHConnection().getState() != HState.NOT_CONNECTED); } + + @Override + public boolean useSocks() { + saveSocksConfig(); + return cbx_useSocks.isSelected(); + } + + @Override + public int getSocksPort() { + return Integer.parseInt(txt_socksPort.getText()); + } + + @Override + public String getSocksHost() { + return txt_socksIp.getText(); + } + + @Override + public boolean dontUseFirstTime() { + return cbx_ignoreSocksOnce.isSelected(); + } } diff --git a/G-Earth/src/main/resources/gearth/ui/extra/Extra.fxml b/G-Earth/src/main/resources/gearth/ui/extra/Extra.fxml index e24e31b..364f018 100644 --- a/G-Earth/src/main/resources/gearth/ui/extra/Extra.fxml +++ b/G-Earth/src/main/resources/gearth/ui/extra/Extra.fxml @@ -1,17 +1,10 @@ - - - - - - - - - - + + + - + @@ -42,10 +35,11 @@ - - - - + + + + + @@ -54,62 +48,60 @@ - - + + + + - - + - - - - + + - - + + + + + + + + + + + + + + + + + + + + + - -