implement socks support

This commit is contained in:
sirjonasxx 2020-05-06 04:40:26 +02:00
parent 530d1651a8
commit 48458de3c7
8 changed files with 350 additions and 118 deletions

View File

@ -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;
}
}

View File

@ -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<String> potentialHost = new ArrayList<>();

View File

@ -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<Socket> 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<Socket> 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;
}
}

View File

@ -0,0 +1,9 @@
package gearth.protocol.connection.proxy;
public interface SocksConfiguration {
boolean useSocks();
int getSocksPort();
String getSocksHost();
boolean dontUseFirstTime();
}

View File

@ -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;
}
}

View File

@ -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<String> getCurrentMappings() {
return new ArrayList<>();

View File

@ -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();
}
}

View File

@ -1,17 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<GridPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="262.0" prefWidth="565.0" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gearth.ui.extra.ExtraController">
<GridPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="262.0" prefWidth="565.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gearth.ui.extra.ExtraController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="331.0" minWidth="10.0" prefWidth="318.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="390.0" minWidth="10.0" prefWidth="247.0" />
@ -42,10 +35,11 @@
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="25.0" minHeight="25.0" prefHeight="25.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="119.0" minHeight="119.0" prefHeight="119.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="158.0" minHeight="10.0" prefHeight="59.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="158.0" minHeight="10.0" prefHeight="59.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="119.0" minHeight="72.0" prefHeight="83.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="119.0" minHeight="6.0" prefHeight="20.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<GridPane.margin>
<Insets bottom="12.0" left="10.0" right="10.0" />
@ -54,62 +48,60 @@
<Insets bottom="4.0" left="7.0" top="7.0" />
</padding>
<children>
<CheckBox fx:id="cbx_ovcinfo" mnemonicParsing="false" text="Override connection info:" />
<GridPane fx:id="grd_ovcinfo" disable="true" prefHeight="119.0" prefWidth="259.0" GridPane.rowIndex="1">
<CheckBox fx:id="cbx_useSocks" mnemonicParsing="false" prefHeight="17.0" prefWidth="208.0" text="Use SOCKS proxy" />
<CheckBox fx:id="cbx_disableDecryption" mnemonicParsing="false" text="Disable decryption" GridPane.rowIndex="3" />
<CheckBox fx:id="cbx_debug" mnemonicParsing="false" text="Debug to stdout" GridPane.rowIndex="4" />
<GridPane fx:id="grd_socksInfo" disable="true" prefHeight="54.0" prefWidth="81.0" GridPane.rowIndex="1">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="159.0" minWidth="10.0" prefWidth="68.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="193.0" minWidth="10.0" prefWidth="145.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="2.0" minHeight="29.0" prefHeight="29.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="29.0" minHeight="29.0" prefHeight="29.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="29.0" minHeight="29.0" prefHeight="29.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="29.0" minHeight="29.0" prefHeight="29.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Real IP:" />
<Label text="MITM IP:" GridPane.rowIndex="1" />
<Label text="Real port:" GridPane.rowIndex="2" />
<Label text="MITM port:" GridPane.rowIndex="3" />
<TextField fx:id="txt_realPort" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" GridPane.columnIndex="1" GridPane.rowIndex="2">
<opaqueInsets>
<Insets />
</opaqueInsets>
<GridPane.margin>
<Insets bottom="3.0" right="7.0" top="3.0" />
</GridPane.margin>
</TextField>
<TextField fx:id="txt_mitmIP" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" GridPane.columnIndex="1" GridPane.rowIndex="1">
<opaqueInsets>
<Insets />
</opaqueInsets>
<GridPane.margin>
<Insets bottom="3.0" right="7.0" top="3.0" />
</GridPane.margin>
</TextField>
<TextField fx:id="txt_realIp" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" GridPane.columnIndex="1">
<opaqueInsets>
<Insets />
</opaqueInsets>
<GridPane.margin>
<Insets bottom="3.0" right="7.0" top="3.0" />
</GridPane.margin>
</TextField>
<TextField fx:id="txt_mitmPort" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" GridPane.columnIndex="1" GridPane.rowIndex="3">
<opaqueInsets>
<Insets />
</opaqueInsets>
<GridPane.margin>
<Insets bottom="3.0" right="7.0" top="3.0" />
</GridPane.margin>
</TextField>
</children>
<GridPane.margin>
<Insets />
</GridPane.margin>
<padding>
<Insets left="5.0" />
</padding>
<children>
<CheckBox fx:id="cbx_ignoreSocksOnce" mnemonicParsing="false" prefHeight="17.0" prefWidth="196.0" selected="true" text="Ignore for first connection" GridPane.rowIndex="1" />
<GridPane prefHeight="119.0" prefWidth="259.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="159.0" minWidth="10.0" prefWidth="68.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="193.0" minWidth="10.0" prefWidth="145.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="2.0" minHeight="29.0" prefHeight="29.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="29.0" minHeight="29.0" prefHeight="29.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Proxy IP:" />
<Label text="Proxy port:" GridPane.rowIndex="1" />
<TextField fx:id="txt_socksPort" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" GridPane.columnIndex="1" GridPane.rowIndex="1">
<opaqueInsets>
<Insets />
</opaqueInsets>
<GridPane.margin>
<Insets bottom="3.0" right="7.0" top="3.0" />
</GridPane.margin>
</TextField>
<TextField fx:id="txt_socksIp" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" GridPane.columnIndex="1">
<opaqueInsets>
<Insets />
</opaqueInsets>
<GridPane.margin>
<Insets bottom="3.0" right="7.0" top="3.0" />
</GridPane.margin>
</TextField>
</children>
<GridPane.margin>
<Insets />
</GridPane.margin>
</GridPane>
</children>
</GridPane>
<CheckBox fx:id="cbx_disableDecryption" mnemonicParsing="false" text="Disable decryption" GridPane.rowIndex="2" />
<CheckBox fx:id="cbx_debug" mnemonicParsing="false" text="Debug to stdout" GridPane.rowIndex="3" />
</children>
</GridPane>
<CheckBox fx:id="cbx_advanced" mnemonicParsing="false" text="Advanced" textFill="#000000ba">