package ru.gravit.launchserver.socket; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.TrustManager; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import ru.gravit.launcher.ssl.LauncherKeyStore; import ru.gravit.launcher.ssl.LauncherTrustManager; import ru.gravit.launchserver.LaunchServer; import ru.gravit.launchserver.fileserver.FileServerHandler; import ru.gravit.launchserver.response.Response; import ru.gravit.launchserver.socket.websocket.WebSocketFrameHandler; import ru.gravit.utils.helper.CommonHelper; import ru.gravit.utils.helper.LogHelper; import ru.gravit.utils.helper.VerifyHelper; @SuppressWarnings({"unused", "rawtypes"}) public final class NettyServerSocketHandler implements Runnable, AutoCloseable { private static final String WEBSOCKET_PATH = "/api"; private static SSLServerSocketFactory ssf; private static final ThreadFactory THREAD_FACTORY = r -> CommonHelper.newThread("Network Thread", true, r); public volatile boolean logConnections = Boolean.getBoolean("launcher.logConnections"); private final AtomicReference serverSocket = new AtomicReference<>(); private final ExecutorService threadPool = Executors.newCachedThreadPool(THREAD_FACTORY); // API private final Map customResponses = new ConcurrentHashMap<>(2); private final AtomicLong idCounter = new AtomicLong(0L); private Set sockets; private volatile Listener listener; public NettyServerSocketHandler(LaunchServer server) { // Instance LaunchServer server1 = server; } @Override public void close() { ServerSocket socket = serverSocket.getAndSet(null); if (socket != null) { LogHelper.info("Closing server socket listener"); try { socket.close(); } catch (IOException e) { LogHelper.error(e); } } } public SSLContext SSLContextInit() throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, KeyManagementException, IOException, CertificateException { TrustManager[] trustAllCerts = new TrustManager[]{ new LauncherTrustManager() }; KeyStore ks = LauncherKeyStore.getKeyStore("keystore", "PSP1000"); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory .getDefaultAlgorithm()); kmf.init(ks, "PSP1000".toCharArray()); SSLContext sc = SSLContext.getInstance("TLSv1.2"); sc.init(kmf.getKeyManagers(), trustAllCerts, new SecureRandom()); return sc; } @Override public void run() { /*SSLContext sc = null; try { sc = SSLContextInit(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnrecoverableKeyException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); }*/ //System.setProperty( "javax.net.ssl.keyStore","keystore"); //System.setProperty( "javax.net.ssl.keyStorePassword","PSP1000"); try { Selector selector = Selector.open(); ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); } catch (IOException e) { e.printStackTrace(); } LogHelper.info("Starting server socket thread"); //SSLEngine engine = sc.createSSLEngine(); //engine.setUseClientMode(false); EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); WebSocketFrameHandler.server = LaunchServer.server; try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.DEBUG)) .childHandler(new ChannelInitializer() { @Override public void initChannel(NioSocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); //p.addLast(new LoggingHandler(LogLevel.INFO)); System.out.println("P!"); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(65536)); pipeline.addLast(new WebSocketServerCompressionHandler()); pipeline.addLast(new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true)); pipeline.addLast(new FileServerHandler(LaunchServer.server.updatesDir, true)); pipeline.addLast(new WebSocketFrameHandler()); } }); ChannelFuture f = b.bind(new InetSocketAddress(9876)).sync(); //TEST ONLY! f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } /* try (SSLServerSocket serverSocket = (SSLServerSocket) ssf.createServerSocket()) { serverSocket.setEnabledProtocols(new String[] {"TLSv1.2"}); if (!this.serverSocket.compareAndSet(null, serverSocket)) { throw new IllegalStateException("Previous socket wasn't closed"); } // Set socket params serverSocket.setReuseAddress(true); serverSocket.setPerformancePreferences(1, 0, 2); //serverSocket.setReceiveBufferSize(0x10000); serverSocket.bind(server.config.getSocketAddress()); LogHelper.info("Server socket thread successfully started"); // Listen for incoming connections while (serverSocket.isBound()) { SSLSocket socket = (SSLSocket) serverSocket.accept(); sockets.add(socket); socket.startHandshake(); // Invoke pre-connect listener long id = idCounter.incrementAndGet(); if (listener != null && !listener.onConnect(id, socket.getInetAddress())) { continue; // Listener didn't accepted this connection } // Reply in separate thread threadPool.execute(new ResponseThread(server, id, socket)); } } catch (IOException e) { // Ignore error after close/rebind if (serverSocket.get() != null) { LogHelper.error(e); } } */ } public void registerCustomResponse(String name, Response.Factory factory) { VerifyHelper.verifyIDName(name); VerifyHelper.putIfAbsent(customResponses, name, Objects.requireNonNull(factory, "factory"), String.format("Custom response has been already registered: '%s'", name)); } public void setListener(Listener listener) { this.listener = listener; } /*package*/ void onDisconnect(long id, Exception e) { if (listener != null) { listener.onDisconnect(id, e); } } /*package*/ boolean onHandshake(long id, Integer type) { return listener == null || listener.onHandshake(id, type); } public interface Listener { boolean onConnect(long id, InetAddress address); void onDisconnect(long id, Exception e); boolean onHandshake(long id, Integer type); } }