source: josm/trunk/src/org/openstreetmap/josm/io/remotecontrol/RemoteControlHttpsServer.java@ 7034

Last change on this file since 7034 was 7034, checked in by Don-vip, 10 years ago

fix #9967, see #8465 - RemoteControl broken by changes in r7033

File size: 8.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io.remotecontrol;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.io.IOException;
8import java.io.InputStream;
9import java.net.BindException;
10import java.net.InetAddress;
11import java.net.ServerSocket;
12import java.net.Socket;
13import java.net.SocketException;
14import java.security.KeyManagementException;
15import java.security.KeyStore;
16import java.security.KeyStoreException;
17import java.security.NoSuchAlgorithmException;
18import java.security.UnrecoverableKeyException;
19import java.security.cert.CertificateException;
20import java.util.Arrays;
21import java.util.Enumeration;
22
23import javax.net.ssl.KeyManagerFactory;
24import javax.net.ssl.SSLContext;
25import javax.net.ssl.SSLServerSocket;
26import javax.net.ssl.SSLServerSocketFactory;
27import javax.net.ssl.SSLSocket;
28import javax.net.ssl.TrustManagerFactory;
29
30import org.openstreetmap.josm.Main;
31import org.openstreetmap.josm.tools.Utils;
32
33/**
34 * Simple HTTPS server that spawns a {@link RequestProcessor} for every secure connection.
35 *
36 * @since 6941
37 */
38public class RemoteControlHttpsServer extends Thread {
39
40 /** The server socket */
41 private ServerSocket server;
42
43 private static RemoteControlHttpsServer instance;
44 private boolean initOK = false;
45 private SSLContext sslContext;
46
47 private static final String KEYSTORE_PATH = "/data/josm.keystore";
48 private static final String KEYSTORE_PASSWORD = "josm_ssl";
49
50 private void initialize() {
51 if (!initOK) {
52 try {
53 // Create new keystore
54 KeyStore ks = KeyStore.getInstance("JKS");
55 char[] password = KEYSTORE_PASSWORD.toCharArray();
56
57 // Load keystore
58 try (InputStream in = RemoteControlHttpsServer.class.getResourceAsStream(KEYSTORE_PATH)) {
59 if (in == null) {
60 Main.error(tr("Unable to find JOSM keystore at {0}. Remote control will not be available on HTTPS.", KEYSTORE_PATH));
61 } else {
62 try {
63 ks.load(in, password);
64 } finally {
65 Utils.close(in);
66 }
67
68 if (Main.isDebugEnabled()) {
69 for (Enumeration<String> aliases = ks.aliases(); aliases.hasMoreElements();) {
70 Main.debug("Alias in keystore: "+aliases.nextElement());
71 }
72 }
73
74 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
75 kmf.init(ks, password);
76
77 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
78 tmf.init(ks);
79
80 sslContext = SSLContext.getInstance("TLS");
81 sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
82
83 if (Main.isDebugEnabled()) {
84 Main.debug("SSL Context protocol: " + sslContext.getProtocol());
85 Main.debug("SSL Context provider: " + sslContext.getProvider());
86 }
87
88 initOK = true;
89 }
90 }
91 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException |
92 IOException | UnrecoverableKeyException | KeyManagementException e) {
93 Main.error(e);
94 }
95 }
96 }
97
98 /**
99 * Starts or restarts the HTTPS server
100 */
101 public static void restartRemoteControlHttpsServer() {
102 int port = Main.pref.getInteger("remote.control.https.port", 8112);
103 try {
104 stopRemoteControlHttpsServer();
105
106 instance = new RemoteControlHttpsServer(port);
107 if (instance.initOK) {
108 instance.start();
109 }
110 } catch (BindException ex) {
111 Main.warn(marktr("Cannot start remotecontrol https server on port {0}: {1}"),
112 Integer.toString(port), ex.getLocalizedMessage());
113 } catch (IOException ioe) {
114 Main.error(ioe);
115 } catch (NoSuchAlgorithmException e) {
116 Main.error(e);
117 }
118 }
119
120 /**
121 * Stops the HTTPS server
122 */
123 public static void stopRemoteControlHttpsServer() {
124 if (instance != null) {
125 try {
126 instance.stopServer();
127 instance = null;
128 } catch (IOException ioe) {
129 Main.error(ioe);
130 }
131 }
132 }
133
134 /**
135 * Constructs a new {@code RemoteControlHttpsServer}.
136 * @param port The port this server will listen on
137 * @throws IOException when connection errors
138 * @throws NoSuchAlgorithmException if the JVM does not support TLS (can not happen)
139 */
140 public RemoteControlHttpsServer(int port) throws IOException, NoSuchAlgorithmException {
141 super("RemoteControl HTTPS Server");
142 this.setDaemon(true);
143
144 initialize();
145
146 // Create SSL Server factory
147 SSLServerSocketFactory factory = sslContext.getServerSocketFactory();
148 if (Main.isDebugEnabled()) {
149 Main.debug("SSL factory - Supported Cipher suites: "+Arrays.toString(factory.getSupportedCipherSuites()));
150 }
151
152 // Start the server socket with only 1 connection.
153 // Also make sure we only listen
154 // on the local interface so nobody from the outside can connect!
155 // NOTE: On a dual stack machine with old Windows OS this may not listen on both interfaces!
156 this.server = factory.createServerSocket(port, 1,
157 InetAddress.getByName(Main.pref.get("remote.control.host", "localhost")));
158
159 if (Main.isDebugEnabled() && server instanceof SSLServerSocket) {
160 SSLServerSocket sslServer = (SSLServerSocket) server;
161 Main.debug("SSL server - Enabled Cipher suites: "+Arrays.toString(sslServer.getEnabledCipherSuites()));
162 Main.debug("SSL server - Enabled Protocols: "+Arrays.toString(sslServer.getEnabledProtocols()));
163 Main.debug("SSL server - Enable Session Creation: "+sslServer.getEnableSessionCreation());
164 Main.debug("SSL server - Need Client Auth: "+sslServer.getNeedClientAuth());
165 Main.debug("SSL server - Want Client Auth: "+sslServer.getWantClientAuth());
166 Main.debug("SSL server - Use Client Mode: "+sslServer.getUseClientMode());
167 }
168 }
169
170 /**
171 * The main loop, spawns a {@link RequestProcessor} for each connection.
172 */
173 @Override
174 public void run() {
175 Main.info(marktr("RemoteControl::Accepting secure connections on port {0}"),
176 Integer.toString(server.getLocalPort()));
177 while (true) {
178 try {
179 @SuppressWarnings("resource")
180 Socket request = server.accept();
181 if (Main.isDebugEnabled() && request instanceof SSLSocket) {
182 SSLSocket sslSocket = (SSLSocket) request;
183 Main.debug("SSL socket - Enabled Cipher suites: "+Arrays.toString(sslSocket.getEnabledCipherSuites()));
184 Main.debug("SSL socket - Enabled Protocols: "+Arrays.toString(sslSocket.getEnabledProtocols()));
185 Main.debug("SSL socket - Enable Session Creation: "+sslSocket.getEnableSessionCreation());
186 Main.debug("SSL socket - Need Client Auth: "+sslSocket.getNeedClientAuth());
187 Main.debug("SSL socket - Want Client Auth: "+sslSocket.getWantClientAuth());
188 Main.debug("SSL socket - Use Client Mode: "+sslSocket.getUseClientMode());
189 Main.debug("SSL socket - Session: "+sslSocket.getSession());
190 }
191 RequestProcessor.processRequest(request);
192 } catch (SocketException se) {
193 if (!server.isClosed()) {
194 Main.error(se);
195 }
196 } catch (IOException ioe) {
197 Main.error(ioe);
198 }
199 }
200 }
201
202 /**
203 * Stops the HTTPS server.
204 *
205 * @throws IOException if any I/O error occurs
206 */
207 public void stopServer() throws IOException {
208 server.close();
209 Main.info(marktr("RemoteControl::Server (https) stopped."));
210 }
211}
Note: See TracBrowser for help on using the repository browser.