Skip to content

Commit

Permalink
Add automatic port forwarding
Browse files Browse the repository at this point in the history
  • Loading branch information
RetGal committed Apr 15, 2023
1 parent 582fb5c commit 2acaf1f
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 24 deletions.
100 changes: 82 additions & 18 deletions src/main/java/mpo/dayon/assistant/gui/Assistant.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package mpo.dayon.assistant.gui;

import com.dosse.upnp.UPnP;
import mpo.dayon.assistant.control.ControlEngine;
import mpo.dayon.assistant.decompressor.DeCompressorEngine;
import mpo.dayon.assistant.decompressor.DeCompressorEngineListener;
Expand Down Expand Up @@ -36,12 +37,15 @@
import java.net.URL;
import java.util.List;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;

import static java.lang.Math.abs;
import static java.lang.String.format;
import static java.lang.String.valueOf;
import static java.lang.Thread.sleep;
import static mpo.dayon.common.babylon.Babylon.translate;
import static mpo.dayon.common.gui.common.FrameType.ASSISTANT;
import static mpo.dayon.common.gui.common.ImageUtilities.getOrCreateIcon;
Expand Down Expand Up @@ -93,6 +97,8 @@ public class Assistant implements ClipboardOwner {

private String token;

private Boolean upnpEnabled;

public Assistant() {
receivedBitCounter = new BitCounter("receivedBits", translate("networkBandwidth"));
receivedBitCounter.start(1000);
Expand Down Expand Up @@ -144,12 +150,25 @@ public void start() {
FatalErrorHandler.attachFrame(frame);
frame.addListener(control);
frame.setVisible(true);
initUpnp();
}

private boolean isUpnpEnabled() {
while (upnpEnabled == null) {
try {
sleep(10L);
} catch (InterruptedException e) {
Log.warn("Swallowed", e);
Thread.currentThread().interrupt();
}
}
return upnpEnabled;
}

private AssistantActions createAssistantActions() {
AssistantActions assistantActions = new AssistantActions();
assistantActions.setIpAddressAction(createWhatIsMyIpAction());
assistantActions.setNetworkConfigurationAction(createNetworkAssistantConfigurationAction());
assistantActions.setNetworkConfigurationAction(createNetworkAssistantConfigurationAction(this));
assistantActions.setCaptureEngineConfigurationAction(createCaptureConfigurationAction());
assistantActions.setCompressionEngineConfigurationAction(createCompressionConfigurationAction());
assistantActions.setResetAction(createResetAction());
Expand All @@ -163,11 +182,8 @@ private AssistantActions createAssistantActions() {
return assistantActions;
}

private void startNetwork() {
network.start();
}

private void stopNetwork() {
frame.hideSpinner();
network.cancel();
}

Expand All @@ -191,13 +207,16 @@ public void actionPerformed(ActionEvent ev) {
final Cursor cursor = frame.getCursor();
frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
final URL url = new URL(WHATSMYIP_SERVER_URL);
try (final BufferedReader lines = new BufferedReader(new InputStreamReader(url.openStream()))) {
publicIp = lines.readLine();
publicIp = UPnP.getExternalIP();
if (publicIp == null) {
final URL url = new URL(WHATSMYIP_SERVER_URL);
try (final BufferedReader lines = new BufferedReader(new InputStreamReader(url.openStream()))) {
publicIp = lines.readLine();
}
}
} catch (IOException ex) {
Log.error("Could not determine public IP", ex);
JOptionPane.showMessageDialog(frame, translate("ipAddress.msg1"), translate("ipAddress"),
JOptionPane.showMessageDialog(frame, translate("ipAddress.msg2"), translate("ipAddress"),
JOptionPane.ERROR_MESSAGE);
} finally {
frame.setCursor(cursor);
Expand Down Expand Up @@ -284,19 +303,28 @@ private JMenuItem getJMenuItemCopyIpAndPort(JButton button) {
return menuItem;
}

private Action createNetworkAssistantConfigurationAction() {
private Action createNetworkAssistantConfigurationAction(Assistant assistant) {
final Action exit = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent ev) {
JFrame networkFrame = (JFrame) SwingUtilities.getRoot((Component) ev.getSource());

final JPanel pane = new JPanel();
pane.setLayout(new GridLayout(1, 2, 10, 10));
pane.setLayout(new GridLayout(4, 1, 10, -10));
final JPanel subPane = new JPanel();
subPane.setLayout(new GridLayout(1, 2, 10, 10));
final JLabel portNumberLbl = new JLabel(translate("connection.settings.portNumber"));
portNumberLbl.setToolTipText(translate("connection.settings.portNumber.tooltip"));
final JTextField portNumberTextField = new JTextField();
portNumberTextField.setText(valueOf(networkConfiguration.getPort()));
pane.add(portNumberLbl);
pane.add(portNumberTextField);
final JLabel upnpStatus = new JLabel(format(translate("connection.settings.upnp." + assistant.isUpnpEnabled()), UPnP.getDefaultGatewayIP()));
final JLabel upnpHint = new JLabel(translate("connection.settings.portforward." + assistant.isUpnpEnabled()));
subPane.add(portNumberLbl);
subPane.add(portNumberTextField);
pane.add(upnpStatus);
pane.add(upnpHint);
pane.add(new JLabel(""));
pane.add(subPane);

final boolean ok = DialogFactory.showOkCancel(networkFrame, translate("connection.network"), pane, true, () -> {
final String portNumber = portNumberTextField.getText();
Expand All @@ -307,13 +335,13 @@ public void actionPerformed(ActionEvent ev) {
});

if (ok) {
final NetworkAssistantEngineConfiguration xnetworkConfiguration = new NetworkAssistantEngineConfiguration(
final NetworkAssistantEngineConfiguration newNetworkConfiguration = new NetworkAssistantEngineConfiguration(
Integer.parseInt(portNumberTextField.getText()));

if (!xnetworkConfiguration.equals(networkConfiguration)) {
networkConfiguration = xnetworkConfiguration;
if (!newNetworkConfiguration.equals(networkConfiguration)) {
network.manageRouterPorts(networkConfiguration.getPort(), newNetworkConfiguration.getPort());
networkConfiguration = newNetworkConfiguration;
networkConfiguration.persist();

network.reconfigure(networkConfiguration);
}
}
Expand Down Expand Up @@ -673,7 +701,7 @@ private Action createStartAction() {
final Action startAction = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent ev) {
startNetwork();
new Assistant.NetWorker().execute();
}
};
startAction.putValue(Action.NAME, "start");
Expand Down Expand Up @@ -730,6 +758,42 @@ private void switchLookAndFeel(UIManager.LookAndFeelInfo lnf) {
}
}

private class NetWorker extends SwingWorker<String, String> {
@Override
protected String doInBackground() {
if (!isCancelled()) {
startNetwork();
}
return null;
}

private void startNetwork() {
frame.onGettingReady();
network.start();
}

@Override
protected void done() {
try {
if (!isCancelled()) {
super.get();
Log.debug("NetWorker is done");
}
} catch (InterruptedException | ExecutionException ie) {
Log.info("NetWorker was cancelled");
Thread.currentThread().interrupt();
}
}
}

private void initUpnp() {
CompletableFuture.supplyAsync(() -> {
upnpEnabled = Boolean.valueOf(UPnP.isUPnPAvailable());
Log.info(format("UPnP is %s", isUpnpEnabled() ? "enabled" : "disabled"));
return upnpEnabled;
});
}

private String margin(String in) {
return " " + in;
}
Expand Down
19 changes: 13 additions & 6 deletions src/main/java/mpo/dayon/assistant/gui/AssistantFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ public void actionPerformed(ActionEvent ev) {
}

void onReady() {
removeCenter();
hideSpinner();
validate();
repaint();
actions.getStartAction().setEnabled(true);
Expand All @@ -269,13 +269,20 @@ void onReady() {
}

void onHttpStarting(int port) {
actions.getStartAction().setEnabled(false);
actions.getStopAction().setEnabled(true);
actions.getNetworkConfigurationAction().setEnabled(false);
actions.getIpAddressAction().setEnabled(false);
getStatusBar().setMessage(translate("listening", port));
}

void onGettingReady() {
showSpinner();
actions.getStartAction().setEnabled(false);
}

private void showSpinner() {
center = new Spinner();
add(center, BorderLayout.CENTER);
getStatusBar().setMessage(translate("listening", port));
}

boolean onAccepted(Socket connection) {
Expand All @@ -284,7 +291,7 @@ boolean onAccepted(Socket connection) {
getOrCreateIcon(ImageNames.USERS), OK_CANCEL_OPTIONS, OK_CANCEL_OPTIONS[1]) == 0) {
return false;
}
removeCenter();
hideSpinner();
getStatusBar().setMessage(translate("connection.incoming.msg2", connection.getInetAddress().getHostAddress()));
center = assistantPanelWrapper;
add(center, BorderLayout.CENTER);
Expand Down Expand Up @@ -330,7 +337,7 @@ void onIOError(IOException error) {
actions.getResetAction().setEnabled(false);
disableControls();
stopSessionTimer();
removeCenter();
hideSpinner();
validate();
repaint();
String errorMessage = error.getMessage() != null ? translate("comm.error.msg1", translate(error.getMessage())) : translate("comm.error.msg1", "!");
Expand Down Expand Up @@ -385,7 +392,7 @@ private void stopSessionTimer() {
}
}

private void removeCenter() {
void hideSpinner() {
if (center != null) {
remove(center);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package mpo.dayon.assistant.network;

import com.dosse.upnp.UPnP;
import mpo.dayon.assisted.compressor.CompressorEngineConfiguration;
import mpo.dayon.common.capture.CaptureEngineConfiguration;
import mpo.dayon.common.concurrent.RunnableEx;
Expand Down Expand Up @@ -36,6 +37,8 @@ public class NetworkAssistantEngine extends NetworkEngine implements ReConfigura

private SSLServerSocketFactory ssf;

private static final String APP_NAME = "Dayon!";

public NetworkAssistantEngine(NetworkCaptureMessageHandler captureMessageHandler, NetworkMouseLocationMessageHandler mouseMessageHandler, ClipboardOwner clipboardOwner) {
this.captureMessageHandler = captureMessageHandler;
this.mouseMessageHandler = mouseMessageHandler;
Expand Down Expand Up @@ -64,6 +67,9 @@ public void start() {
if (cancelling.get() || receiver != null) {
return;
}
if (UPnP.isUPnPAvailable() && !UPnP.isMappedTCP(configuration.getPort())) {
UPnP.openPortTCP(configuration.getPort(), APP_NAME);
}
receiver = new Thread(new RunnableEx() {
@Override
protected void doRun() throws NoSuchAlgorithmException, KeyManagementException {
Expand All @@ -84,6 +90,14 @@ public void cancel() {
fireOnDisconnecting();
}

public boolean manageRouterPorts(int oldPort, int newPort) {
if (UPnP.isUPnPAvailable()) {
UPnP.closePortTCP(oldPort);
return UPnP.openPortTCP(newPort, APP_NAME);
}
return false;
}

// right, keep streams open - forever!
@java.lang.SuppressWarnings({"squid:S2189", "squid:S2093"})
private void receivingLoop() throws NoSuchAlgorithmException, KeyManagementException {
Expand Down Expand Up @@ -112,6 +126,7 @@ private void receivingLoop() throws NoSuchAlgorithmException, KeyManagementExcep
handleIOException(ex);
} finally {
closeConnections();
UPnP.closePortTCP(configuration.getPort());
fireOnReady();
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/Babylon.properties
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ connection.settings.portNumber = Port number
connection.settings.portNumber.tooltip = Port number for the incoming connection
connection.settings.token = Token
connection.settings.invalidToken = This token is invalid.
connection.settings.upnp.true = UPnP is enabled on this gateway %s
connection.settings.upnp.false = UPnP is disabled in this network.
connection.settings.portforward.true = Port forwarding is configured automatically.
connection.settings.portforward.false = Remember to configure the port forwarding.

# Token
token = Token
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/Babylon_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ connection.settings.portNumber = Portnummer
connection.settings.portNumber.tooltip = Portnummer f\u00FCr die eingehende Verbindungen
connection.settings.token = Token
connection.settings.invalidToken = Das Token ist ung\u00FCltig.
connection.settings.upnp.true = UPnP ist aktiviert - Gateway %s
connection.settings.upnp.false = UPnP ist in diesem Netzwerk deaktiviert.
connection.settings.portforward.true = Portweiterleitung wird automatisch konfiguriert.
connection.settings.portforward.false = Einrichten der Portweiterleitung nicht vergessen.

# Token
token = Token
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/Babylon_es.properties
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ connection.settings.portNumber = N\u00FAmero de puerto
connection.settings.portNumber.tooltip = N\u00FAmero de puerto para la conexion entrante
connection.settings.token = C\u00F3digo
connection.settings.invalidToken = C\u00F3digo de acceso es inv\u00E1lido.
connection.settings.upnp.true = UPnP est\u00E1 habilitado en esta puerta de enlace %s
connection.settings.upnp.false = UPnP est\u00E1 deshabilitado en esta red.
connection.settings.portforward.true = El reenv\u00EDo de puertos se configura autom\u00E1ticamente.
connection.settings.portforward.false = Recuerde configurar el reenv\u00EDo de puertos.

# Token
token = C\u00F3digo
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/Babylon_fr.properties
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ connection.settings.portNumber = Num\u00E9ro du Port
connection.settings.portNumber.tooltip = Num\u00E9ro du port pour la connexion entrante
connection.settings.token = Code
connection.settings.invalidToken = Le code d'acc\u00E8s est invalide.
connection.settings.upnp.true = UPnP est activ\u00E9 sur cette passerelle %s
connection.settings.upnp.false = UPnP est d\u00E9sactiv\u00E9 dans ce r\u00E9seau.
connection.settings.portforward.true = La redirection de port est configur\u00E9e automatiquement.
connection.settings.portforward.false = N'oubliez pas de configurer la redirection de port.

# Token
token = Code
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/Babylon_it.properties
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ connection.settings.portNumber = Numero di porta
connection.settings.portNumber.tooltip = Numero di porta per la connessione in entrata
connection.settings.token = Token
connection.settings.invalidToken = Il token non \u00E8 valido.
connection.settings.upnp.true = UPnP \u00E8 abilitato su questo gateway %s
connection.settings.upnp.false = UPnP \u00E8 disabilitato in questa rete.
connection.settings.portforward.true = Il port forwarding \u00E8 configurato automaticamente.
connection.settings.portforward.false = Ricordati di configurare il port forwarding.

# Token
token = Token
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/Babylon_ru.properties
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ connection.settings.portNumber = \u041D\u043E\u043C\u0435\u0440 \u043F\
connection.settings.portNumber.tooltip = \u041D\u043E\u043C\u0435\u0440 \u043F\u043E\u0440\u0442\u0430 \u0434\u043B\u044F \u0432\u0445\u043E\u0434\u044F\u0449\u0435\u0433\u043E \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044F
connection.settings.token = \u041A\u043E\u0434 \u0434\u043E\u0441\u0442\u0443\u043F\u0430
connection.settings.invalidToken = \u041A\u043E\u0434 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043D\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u0435\u043D.
connection.settings.upnp.true = UPnP \u0432\u043A\u043B\u044E\u0447\u0435\u043D \u043D\u0430 \u044D\u0442\u043E\u043C \u0448\u043B\u044E\u0437\u0435 %s
connection.settings.upnp.false = UPnP \u043E\u0442\u043A\u043B\u044E\u0447\u0435\u043D \u0432 \u044D\u0442\u043E\u0439 \u0441\u0435\u0442\u0438.
connection.settings.portforward.true = \u041F\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044F \u043F\u043E\u0440\u0442\u043E\u0432 \u043D\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438.
connection.settings.portforward.false = \u041D\u0435 \u0437\u0430\u0431\u0443\u0434\u044C\u0442\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0438\u0442\u044C \u043F\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044E \u043F\u043E\u0440\u0442\u043E\u0432.

# Token (access code)
token = \u041A\u043E\u0434 \u0434\u043E\u0441\u0442\u0443\u043F\u0430
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/Babylon_tr.properties
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ connection.settings.portNumber = Port numaras\u0131
connection.settings.portNumber.tooltip = Gelen ba\u011Flant\u0131 i\u00E7in port numaras\u0131
connection.settings.token = Giri\u015F kodu
connection.settings.invalidToken = Eri\u015Fim kodu ge\u00E7ersiz.
connection.settings.upnp.true = UPnP bu a\u011F ge\u00E7idinde etkinle\u015Ftirildi %s
connection.settings.upnp.false = UPnP bu a\u011Fda devre d\u0131\u015F\u0131.
connection.settings.portforward.true = Ba\u011Flant\u0131 noktas\u0131 y\u00F6nlendirme otomatik olarak yap\u0131land\u0131r\u0131l\u0131r.
connection.settings.portforward.false = Ba\u011Flant\u0131 noktas\u0131 y\u00F6nlendirmeyi yap\u0131land\u0131rmay\u0131 unutmay\u0131n.

# Token (access code)
token = Giri\u015F kodu
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/Babylon_zh.properties
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ connection.settings.portNumber.tooltip = \u76D1\u542C\u7684\u7AEF\u53E3\u53F7
connection.settings.token = \u8BBF\u95EE\u4EE3\u7801
# The access token is invalid.
connection.settings.invalidToken = \u8BBF\u95EE\u4EE3\u7801\u65E0\u6548.
connection.settings.upnp.true = \u6B64\u7F51\u5173 %s \u4E0A\u542F\u7528\u4E86 UPnP
connection.settings.upnp.false = UPnP \u5728\u6B64\u7F51\u7EDC\u4E2D\u88AB\u7981\u7528\u3002
connection.settings.portforward.true = \u7AEF\u53E3\u8F6C\u53D1\u662F\u81EA\u52A8\u914D\u7F6E\u7684\u3002
connection.settings.portforward.false = \u8BF7\u8BB0\u4F4F\u914D\u7F6E\u7AEF\u53E3\u8F6C\u53D1\u3002

# Token (access code)
token = \u8BBF\u95EE\u4EE3\u7801
Expand Down

0 comments on commit 2acaf1f

Please sign in to comment.