mirror of
https://bitbucket.org/projectswg/lightspeed.git
synced 2026-09-21 07:12:57 -04:00
Added SSL encryption
This commit is contained in:
+1
-1
@@ -6,7 +6,6 @@
|
||||
<classpathentry kind="lib" path="lib/sqlite-jdbc-3.8.11.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/fast-json-1.4.jar"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||
<classpathentry kind="lib" path="/home/josh/Downloads/nanohttpd-nanohttpd-project-2.3.1/core/target/nanohttpd-2.3.1.jar"/>
|
||||
<classpathentry kind="lib" path="lib/org.eclipse.jgit-4.6.0.201612231935-r.jar">
|
||||
<attributes>
|
||||
<attribute name="javadoc_location" value="jar:platform:/resource/Lightspeed/lib/org.eclipse.jgit-4.6.0.201612231935-r-javadoc.jar!/"/>
|
||||
@@ -29,5 +28,6 @@
|
||||
<classpathentry kind="lib" path="lib/ant/ant-testutil.jar"/>
|
||||
<classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/>
|
||||
<classpathentry kind="lib" path="lib/junit-4.12.jar"/>
|
||||
<classpathentry kind="lib" path="lib/nanohttpd-2.3.2-SNAPSHOT.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -27,14 +27,30 @@
|
||||
***********************************************************************************/
|
||||
package com.projectswg.lightspeed;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyStore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import org.nanohttpd.protocols.http.ClientHandler;
|
||||
import org.nanohttpd.protocols.http.IHTTPSession;
|
||||
import org.nanohttpd.protocols.http.NanoHTTPD;
|
||||
import org.nanohttpd.protocols.http.response.Response;
|
||||
import org.nanohttpd.protocols.http.response.Status;
|
||||
import org.nanohttpd.protocols.http.threading.IAsyncRunner;
|
||||
|
||||
import me.joshlarson.json.JSONObject;
|
||||
|
||||
import com.projectswg.common.concurrency.PswgThreadPool;
|
||||
@@ -47,11 +63,6 @@ import com.projectswg.common.intents.RegisterHttpListenerIntent;
|
||||
import com.projectswg.common.network.packets.PacketType;
|
||||
import com.projectswg.lightspeed.communication.HttpResponder;
|
||||
|
||||
import fi.iki.elonen.NanoHTTPD;
|
||||
import fi.iki.elonen.NanoHTTPD.IHTTPSession;
|
||||
import fi.iki.elonen.NanoHTTPD.Response;
|
||||
import fi.iki.elonen.NanoHTTPD.Response.Status;
|
||||
|
||||
public class CommunicationService extends LightspeedService {
|
||||
|
||||
private final NanoHTTPD httpServer;
|
||||
@@ -75,6 +86,13 @@ public class CommunicationService extends LightspeedService {
|
||||
public boolean initialize() {
|
||||
Config config = getConfig(ConfigFile.LIGHTSPEED);
|
||||
try {
|
||||
String pubkey = config.getString("PUBKEY", "");
|
||||
if (!pubkey.isEmpty()) {
|
||||
String pass = config.getString("PUBKEY-PASS", "");
|
||||
File cert = new File("data", pubkey);
|
||||
System.setProperty("javax.net.ssl.trustStore", cert.getAbsolutePath());
|
||||
httpServer.makeSecure(createSSLSocketFactory(cert, pass), null);
|
||||
}
|
||||
runner.start();
|
||||
httpServer.start(config.getInt("HTTP-TIMEOUT", 2000), false);
|
||||
} catch (IOException e) {
|
||||
@@ -91,6 +109,25 @@ public class CommunicationService extends LightspeedService {
|
||||
return super.terminate();
|
||||
}
|
||||
|
||||
private SSLServerSocketFactory createSSLSocketFactory(File keystoreFile, String password) throws IOException {
|
||||
try {
|
||||
char [] passphrase = password.toCharArray();
|
||||
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
InputStream keystoreStream = new FileInputStream(keystoreFile);
|
||||
|
||||
keystore.load(keystoreStream, passphrase);
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
keyManagerFactory.init(keystore, passphrase);
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(keystore);
|
||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||
ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
|
||||
return ctx.getServerSocketFactory();
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Response serve(IHTTPSession session) {
|
||||
try {
|
||||
JSONObject params = getJSONObject(session);
|
||||
@@ -126,7 +163,7 @@ public class CommunicationService extends LightspeedService {
|
||||
JSONObject response = responder.request(request, params);
|
||||
if (response == null)
|
||||
return getNotFound("Invalid Request Parameters");
|
||||
return NanoHTTPD.newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_PLAINTEXT, response.toString());
|
||||
return Response.newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_PLAINTEXT, response.toString());
|
||||
}
|
||||
|
||||
private JSONObject getJSONObject(IHTTPSession session) {
|
||||
@@ -156,15 +193,15 @@ public class CommunicationService extends LightspeedService {
|
||||
}
|
||||
|
||||
private Response getMethodNotAllowed() {
|
||||
return NanoHTTPD.newFixedLengthResponse(Status.METHOD_NOT_ALLOWED, NanoHTTPD.MIME_PLAINTEXT, "Method Not Allowed");
|
||||
return Response.newFixedLengthResponse(Status.METHOD_NOT_ALLOWED, NanoHTTPD.MIME_PLAINTEXT, "Method Not Allowed");
|
||||
}
|
||||
|
||||
private Response getNotFound(String str) {
|
||||
return NanoHTTPD.newFixedLengthResponse(Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, str);
|
||||
return Response.newFixedLengthResponse(Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, str);
|
||||
}
|
||||
|
||||
private Response getServerError(String str) {
|
||||
return NanoHTTPD.newFixedLengthResponse(Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, str);
|
||||
return Response.newFixedLengthResponse(Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, str);
|
||||
}
|
||||
|
||||
private void handleRegisterListener(RegisterHttpListenerIntent rhli) {
|
||||
@@ -176,10 +213,10 @@ public class CommunicationService extends LightspeedService {
|
||||
return httpServer.getListeningPort();
|
||||
}
|
||||
|
||||
private static class BoundRunner implements NanoHTTPD.AsyncRunner {
|
||||
private static class BoundRunner implements IAsyncRunner {
|
||||
|
||||
private final PswgThreadPool threadPool;
|
||||
private final List<NanoHTTPD.ClientHandler> running;
|
||||
private final List<ClientHandler> running;
|
||||
|
||||
public BoundRunner(int threads) {
|
||||
if (threads <= 0)
|
||||
@@ -203,21 +240,21 @@ public class CommunicationService extends LightspeedService {
|
||||
public void closeAll() {
|
||||
// copy of the list for concurrency
|
||||
synchronized (running) {
|
||||
for (NanoHTTPD.ClientHandler clientHandler : running) {
|
||||
for (ClientHandler clientHandler : running) {
|
||||
clientHandler.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closed(NanoHTTPD.ClientHandler clientHandler) {
|
||||
public void closed(ClientHandler clientHandler) {
|
||||
synchronized (running) {
|
||||
running.remove(clientHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exec(NanoHTTPD.ClientHandler clientHandler) {
|
||||
public void exec(ClientHandler clientHandler) {
|
||||
threadPool.execute(clientHandler);
|
||||
synchronized (running) {
|
||||
running.add(clientHandler);
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
***********************************************************************************/
|
||||
package com.projectswg.lightspeed_frontend;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
@@ -35,6 +37,7 @@ import com.projectswg.common.concurrency.PswgThreadPool;
|
||||
import com.projectswg.common.data.TestDetails;
|
||||
import com.projectswg.common.debug.Assert;
|
||||
import com.projectswg.common.debug.Log;
|
||||
import com.projectswg.common.info.Config;
|
||||
import com.projectswg.common.network.packets.Packet;
|
||||
import com.projectswg.common.network.packets.request.RequestBuildDetailedPacket;
|
||||
import com.projectswg.common.network.packets.request.RequestBuildListPacket;
|
||||
@@ -62,13 +65,19 @@ public class Frontend {
|
||||
private final PswgScheduledThreadPool scheduledUpdater;
|
||||
private final FrontendCommunication communication;
|
||||
private final FrontendData data;
|
||||
private final Config config;
|
||||
|
||||
public Frontend() {
|
||||
this.threadPool = new PswgThreadPool(10, "frontend-thread-pool");
|
||||
this.scheduledUpdater = new PswgScheduledThreadPool(1, "frontend-updater");
|
||||
this.communication = new FrontendCommunication();
|
||||
this.communication.setCallback(packet -> process(packet));
|
||||
this.data = new FrontendData();
|
||||
File data = new File("cfg");
|
||||
data.mkdirs();
|
||||
File cfgFile = new File(data, "frontend.cfg");
|
||||
try { cfgFile.createNewFile(); } catch (IOException e) { Log.e(e); }
|
||||
this.config = new Config(cfgFile);
|
||||
this.communication = new FrontendCommunication(config);
|
||||
this.communication.setCallback(packet -> process(packet));
|
||||
}
|
||||
|
||||
public void setAddress(InetSocketAddress address) {
|
||||
@@ -97,6 +106,10 @@ public class Frontend {
|
||||
return data;
|
||||
}
|
||||
|
||||
public Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void send(Packet p, ResponseCallback callback) {
|
||||
threadPool.execute(() -> {
|
||||
Packet response = communication.send(p);
|
||||
|
||||
@@ -49,16 +49,18 @@ import com.projectswg.lightspeed_frontend.data.SharedServerData;
|
||||
|
||||
public class LightspeedFrontendText {
|
||||
|
||||
private final Frontend frontend;
|
||||
private final FrontendCommunication communication;
|
||||
private final Queue<Packet> inbound;
|
||||
private boolean running;
|
||||
|
||||
public LightspeedFrontendText() {
|
||||
communication = new FrontendCommunication();
|
||||
inbound = new ArrayDeque<>();
|
||||
running = false;
|
||||
communication.setAddress(new InetSocketAddress(InetAddress.getLoopbackAddress(), 44444));
|
||||
communication.setCallback(packet -> {
|
||||
this.frontend = new Frontend();
|
||||
this.communication = frontend.getCommunication();
|
||||
this.inbound = new ArrayDeque<>();
|
||||
this.running = false;
|
||||
this.communication.setAddress(new InetSocketAddress(InetAddress.getLoopbackAddress(), 44444));
|
||||
this.communication.setCallback(packet -> {
|
||||
synchronized (inbound) {
|
||||
inbound.add(packet);
|
||||
inbound.notify();
|
||||
|
||||
@@ -27,11 +27,13 @@
|
||||
***********************************************************************************/
|
||||
package com.projectswg.lightspeed_frontend.communication;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.projectswg.common.debug.Assert;
|
||||
import com.projectswg.common.debug.Log;
|
||||
import com.projectswg.common.info.Config;
|
||||
import com.projectswg.common.network.packets.Packet;
|
||||
|
||||
public class FrontendCommunication {
|
||||
@@ -40,10 +42,13 @@ public class FrontendCommunication {
|
||||
private ResponseCallback callback;
|
||||
private InetSocketAddress address;
|
||||
|
||||
public FrontendCommunication() {
|
||||
running = new AtomicBoolean(false);
|
||||
callback = null;
|
||||
address = null;
|
||||
public FrontendCommunication(Config config) {
|
||||
this.running = new AtomicBoolean(false);
|
||||
this.callback = null;
|
||||
this.address = null;
|
||||
String keystore = config.getString("KEYSTORE", "");
|
||||
if (!keystore.isEmpty())
|
||||
HttpClient.setupSSL(new File(keystore), config.getString("KEYSTORE-PASS", ""));
|
||||
}
|
||||
|
||||
public InetSocketAddress getAddress() {
|
||||
|
||||
@@ -28,15 +28,24 @@
|
||||
package com.projectswg.lightspeed_frontend.communication;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import me.joshlarson.json.JSON;
|
||||
import me.joshlarson.json.JSONObject;
|
||||
@@ -49,7 +58,35 @@ import com.projectswg.common.network.packets.request.RequestPacket;
|
||||
|
||||
public class HttpClient {
|
||||
|
||||
private static final AtomicBoolean SSL = new AtomicBoolean(false);
|
||||
|
||||
public static void setupSSL(File keystoreFile, String password) {
|
||||
try {
|
||||
InputStream keystoreStream = new FileInputStream(keystoreFile);
|
||||
char[] trustPassword = password.toCharArray();
|
||||
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
|
||||
keystore.load(keystoreStream, trustPassword);
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
keyManagerFactory.init(keystore, trustPassword);
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(keystore);
|
||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||
ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
|
||||
SSLContext.setDefault(ctx);
|
||||
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
|
||||
SSL.set(true);
|
||||
} catch (Exception e) {
|
||||
Log.e(e);
|
||||
SSL.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static Packet requestPacket(InetSocketAddress address, Packet packet) {
|
||||
if (!SSL.get()) {
|
||||
Log.e("Cannot send packet - SSL not initialized yet!");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String str = requestString(address, packet);
|
||||
JSONObject obj = JSON.readObject(str, true);
|
||||
@@ -70,7 +107,7 @@ public class HttpClient {
|
||||
|
||||
public static String requestString(InetSocketAddress address, Packet packet) throws IOException {
|
||||
URL url = new URL(generateURL(address, packet));
|
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
|
||||
|
||||
con.setRequestMethod((packet instanceof PostPacket) ? "POST" : "GET");
|
||||
con.setRequestProperty("User-Agent", "LightspeedFrontend");
|
||||
@@ -90,7 +127,7 @@ public class HttpClient {
|
||||
}
|
||||
|
||||
private static String generateURL(InetSocketAddress address, Packet packet) throws UnsupportedEncodingException {
|
||||
StringBuilder url = new StringBuilder("http://"+address.getAddress().getHostAddress()+":"+address.getPort()+"/?");
|
||||
StringBuilder url = new StringBuilder("https://"+address.getAddress().getHostAddress()+":"+address.getPort()+"/?");
|
||||
JSONObject obj = packet.getJSON();
|
||||
if (packet instanceof PostPacket)
|
||||
url.append("type=" + ((PostPacket) packet).getType().name().replace("POST_", "") + '&');
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/***********************************************************************************
|
||||
* Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
|
||||
* *
|
||||
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
|
||||
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
|
||||
* Our goal is to create an emulator which will provide a server for players to *
|
||||
* continue playing a game similar to the one they used to play. We are basing *
|
||||
* it on the final publish of the game prior to end-game events. *
|
||||
* *
|
||||
* This file is part of Holocore. *
|
||||
* *
|
||||
* -------------------------------------------------------------------------------- *
|
||||
* *
|
||||
* Holocore is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* Holocore is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***********************************************************************************/
|
||||
package com.projectswg.lightspeed.frontend;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.projectswg.lightspeed_frontend.communication.FrontendCommunication;
|
||||
|
||||
public class LinearTestFrontendCommunication {
|
||||
|
||||
private final FrontendCommunication communication;
|
||||
private final AtomicBoolean connected;
|
||||
|
||||
public LinearTestFrontendCommunication() {
|
||||
communication = new FrontendCommunication();
|
||||
connected = new AtomicBoolean(false);
|
||||
communication.start();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
communication.stop();
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return connected.get();
|
||||
}
|
||||
|
||||
public boolean waitForConnectionState(boolean state, long timeout) {
|
||||
while (connected.get() != state && timeout >= 0) {
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
return false;
|
||||
}
|
||||
timeout--;
|
||||
}
|
||||
return connected.get() == state;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user