diff --git a/src/qz/common/Constants.java b/src/qz/common/Constants.java index ad8f7174f..60ab1730a 100644 --- a/src/qz/common/Constants.java +++ b/src/qz/common/Constants.java @@ -1,7 +1,6 @@ package qz.common; import com.github.zafarkhaja.semver.Version; -import qz.utils.ArgValue; import qz.utils.SystemUtilities; import java.awt.*; @@ -98,7 +97,7 @@ public class Constants { public static final String PDF_PRINT = ABOUT_TITLE + " PDF Print"; public static final String HTML_PRINT = ABOUT_TITLE + " HTML Print"; - public static final Integer[] WSS_PORTS = {8181, 8282, 8383, 8484}; - public static final Integer[] WS_PORTS = {8182, 8283, 8384, 8485}; + public static final Integer[] DEFAULT_WSS_PORTS = {8181, 8282, 8383, 8484}; + public static final Integer[] DEFAULT_WS_PORTS = {8182, 8283, 8384, 8485}; public static final Integer[] CUPS_RSS_PORTS = {8586, 8687, 8788, 8889}; } diff --git a/src/qz/common/TrayManager.java b/src/qz/common/TrayManager.java index edb860381..a96506e5d 100644 --- a/src/qz/common/TrayManager.java +++ b/src/qz/common/TrayManager.java @@ -536,8 +536,8 @@ private void blackList(Certificate cert) { public void setServer(Server server, int insecurePortInUse, int securePortInUse) { if (server != null && server.getConnectors().length > 0) { - singleInstanceCheck(PrintSocketServer.INSECURE_PORTS, insecurePortInUse, false); - singleInstanceCheck(PrintSocketServer.SECURE_PORTS, securePortInUse, true); + singleInstanceCheck(PrintSocketServer.insecurePorts, insecurePortInUse, false); + singleInstanceCheck(PrintSocketServer.securePorts, securePortInUse, true); displayInfoMessage("Server started on port(s) " + PrintSocketServer.getPorts(server)); diff --git a/src/qz/installer/WindowsInstaller.java b/src/qz/installer/WindowsInstaller.java index 87645882a..781f857f0 100644 --- a/src/qz/installer/WindowsInstaller.java +++ b/src/qz/installer/WindowsInstaller.java @@ -148,7 +148,8 @@ public Installer addSystemSettings() { WindowsUtilities.addNumberedRegValue(HKEY_LOCAL_MACHINE, "SOFTWARE\\Policies\\Google\\Chrome\\URLWhitelist", String.format("%s://*", DATA_DIR)); // Firewall rules - String ports = StringUtils.join(PrintSocketServer.SECURE_PORTS, ",") + "," + StringUtils.join(PrintSocketServer.INSECURE_PORTS, ","); + // FIXME: This breaks Windows Installers!! + String ports = StringUtils.join(PrintSocketServer.securePorts, ",") + "," + StringUtils.join(PrintSocketServer.insecurePorts, ","); ShellUtilities.execute("netsh.exe", "advfirewall", "firewall", "delete", "rule", String.format("name=%s", ABOUT_TITLE)); ShellUtilities.execute("netsh.exe", "advfirewall", "firewall", "add", "rule", String.format("name=%s", ABOUT_TITLE), "dir=in", "action=allow", "profile=any", String.format("localport=%s", ports), "localip=any", "protocol=tcp"); diff --git a/src/qz/utils/ArgValue.java b/src/qz/utils/ArgValue.java index cfe0856d7..3f3ce7a4c 100644 --- a/src/qz/utils/ArgValue.java +++ b/src/qz/utils/ArgValue.java @@ -1,5 +1,6 @@ package qz.utils; +import org.apache.commons.lang3.StringUtils; import qz.common.Constants; import java.util.ArrayList; @@ -81,6 +82,10 @@ public enum ArgValue { "security.wss.httpsonly"), SECURITY_WSS_HOST(PREFERENCES, "Influences which physical adapter to bind to by setting the host parameter for http/websocket listening", null, "0.0.0.0", "security.wss.host"), + WEBSOCKET_SECURE_PORTS(PREFERENCES, "Comma separated list of secure websocket (wss://) ports to use", null, StringUtils.join(Constants.DEFAULT_WSS_PORTS, ","), + "websocket.secure.ports"), + WEBSOCKET_INSECURE_PORTS(PREFERENCES, "Comma separated list of insecure websocket (ws://) ports to use", null, StringUtils.join(Constants.DEFAULT_WS_PORTS, ","), + "websocket.insecure.ports"), LOG_DISABLE(PREFERENCES, "Disable/enable logging features", null, false, "log.disable"), LOG_ROTATE(PREFERENCES, "Number of log files to retain when the size fills up", null, 5, diff --git a/src/qz/utils/PrefsSearch.java b/src/qz/utils/PrefsSearch.java index 21913ae38..fa264835a 100644 --- a/src/qz/utils/PrefsSearch.java +++ b/src/qz/utils/PrefsSearch.java @@ -5,6 +5,8 @@ import qz.installer.certificate.CertificateManager; import qz.installer.certificate.KeyPairWrapper; +import java.util.ArrayList; +import java.util.List; import java.util.Properties; import static qz.installer.certificate.KeyPairWrapper.Type.CA; @@ -90,6 +92,22 @@ public static int getInt(ArgValue argValue, Properties ... propsArray) { return getInt(argValue, true, propsArray); } + public static Integer[] getIntegerArray(ArgValue argValue, Properties ... propsArray) { + List parsed = new ArrayList<>(); + String unparsed = getString(argValue, propsArray); + try { + if (unparsed != null && !unparsed.isEmpty()) { + String[] split = unparsed.split(","); + for(String item : split) { + parsed.add(Integer.parseInt(item)); + } + } + } catch(NumberFormatException nfe) { + log.warn("Failed parsing {} as {}", unparsed, argValue, nfe); + } + return parsed.toArray(new Integer[parsed.size()]); + } + public static boolean getBoolean(ArgValue argValue, Properties ... propsArray) { return getBoolean(argValue, true, propsArray); } diff --git a/src/qz/ws/PrintSocketServer.java b/src/qz/ws/PrintSocketServer.java index c45c76cd3..be5d4294c 100644 --- a/src/qz/ws/PrintSocketServer.java +++ b/src/qz/ws/PrintSocketServer.java @@ -44,8 +44,8 @@ public class PrintSocketServer { private static final Logger log = LogManager.getLogger(PrintSocketServer.class); private static final int MAX_MESSAGE_SIZE = Integer.MAX_VALUE; - public static final List SECURE_PORTS = Collections.unmodifiableList(Arrays.asList(Constants.WSS_PORTS)); - public static final List INSECURE_PORTS = Collections.unmodifiableList(Arrays.asList(Constants.WS_PORTS)); + public static List securePorts; + public static List insecurePorts; private static final AtomicInteger securePortIndex = new AtomicInteger(0); private static final AtomicInteger insecurePortIndex = new AtomicInteger(0); @@ -65,6 +65,8 @@ public static void runServer(CertificateManager certManager, boolean headless) t wssHost = PrefsSearch.getString(ArgValue.SECURITY_WSS_HOST, certManager.getProperties()); httpsOnly = PrefsSearch.getBoolean(ArgValue.SECURITY_WSS_HTTPSONLY, certManager.getProperties()); sniStrict = PrefsSearch.getBoolean(ArgValue.SECURITY_WSS_SNISTRICT, certManager.getProperties()); + parseWebSocketPorts(); + server = findAvailableSecurePort(certManager); Connector secureConnector = null; @@ -77,7 +79,7 @@ public static void runServer(CertificateManager certManager, boolean headless) t return; } - while(!running.get() && insecurePortIndex.get() < INSECURE_PORTS.size()) { + while(!running.get() && insecurePortIndex.get() < insecurePorts.size()) { try { ServerConnector connector = new ServerConnector(server); connector.setPort(getInsecurePortInUse()); @@ -155,7 +157,7 @@ private static Server findAvailableSecurePort(CertificateManager certManager) { if (certManager != null) { final AtomicBoolean runningSecure = new AtomicBoolean(false); - while(!runningSecure.get() && securePortIndex.get() < SECURE_PORTS.size()) { + while(!runningSecure.get() && securePortIndex.get() < securePorts.size()) { try { // Bind the secure socket on the proper port number (i.e. 8181), add it as an additional connector SslConnectionFactory sslConnection = new SslConnectionFactory(certManager.configureSslContextFactory(), HttpVersion.HTTP_1_1.asString()); @@ -223,12 +225,37 @@ public static void main(String ... args) { App.main(args); } + /** + * Parses WebSocket ports from preferences or fallback to defaults is a problem is found + */ + public static void parseWebSocketPorts() { + Integer[] secure = PrefsSearch.getIntegerArray(ArgValue.WEBSOCKET_SECURE_PORTS); + Integer[] insecure = PrefsSearch.getIntegerArray(ArgValue.WEBSOCKET_INSECURE_PORTS); + boolean fallback = false; + if(secure.length == 0 || insecure.length == 0) { + log.warn("One or more WebSocket ports is empty, falling back to defaults"); + fallback = true; + } + if(secure.length != insecure.length) { + log.warn("Secure ({}) and insecure ({}) WebSocket port counts mismatch, falling back to defaults", secure, insecure); + fallback = true; + } + if(fallback) { + log.warn("Falling back to default WebSocket ports: ({}), ({})", secure, insecure); + secure = Constants.DEFAULT_WSS_PORTS; + insecure = Constants.DEFAULT_WS_PORTS; + } + + securePorts = Collections.unmodifiableList(Arrays.asList(secure)); + insecurePorts = Collections.unmodifiableList(Arrays.asList(insecure)); + } + public static int getSecurePortInUse() { - return SECURE_PORTS.get(securePortIndex.get()); + return securePorts.get(securePortIndex.get()); } public static int getInsecurePortInUse() { - return INSECURE_PORTS.get(insecurePortIndex.get()); + return insecurePorts.get(insecurePortIndex.get()); } /**