View Javadoc
1   /*
2    * junixsocket
3    *
4    * Copyright 2009-2024 Christian Kohlschütter
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.newsclub.net.unix.demo.ssl;
19  
20  import java.io.File;
21  import java.io.FileDescriptor;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.io.PrintStream;
26  import java.nio.charset.Charset;
27  import java.nio.charset.StandardCharsets;
28  import java.security.GeneralSecurityException;
29  import java.util.Arrays;
30  import java.util.concurrent.TimeUnit;
31  
32  import javax.net.ssl.SNIMatcher;
33  import javax.net.ssl.SSLSocket;
34  import javax.net.ssl.SSLSocketFactory;
35  import javax.security.auth.DestroyFailedException;
36  
37  import org.newsclub.net.unix.AFSocket;
38  import org.newsclub.net.unix.AFUNIXSocket;
39  import org.newsclub.net.unix.AFUNIXSocketAddress;
40  import org.newsclub.net.unix.FileDescriptorCast;
41  import org.newsclub.net.unix.server.AFSocketServer;
42  import org.newsclub.net.unix.ssl.SNIHostnameCapture;
43  import org.newsclub.net.unix.ssl.SSLContextBuilder;
44  
45  /**
46   * A simple SSL demo server.
47   *
48   * @author Christian Kohlschütter
49   * @see SSLDemoClient
50   * @see SSLDemoPrerequisites
51   */
52  @SuppressWarnings("CatchAndPrintStackTrace" /* errorprone */)
53  public class SSLDemoServer {
54    public static void main(String[] args) throws InterruptedException, IOException,
55        GeneralSecurityException, DestroyFailedException {
56  
57      // Enable to see SSL debugging
58      // System.setProperty("javax.net.debug", "all");
59  
60      AFUNIXSocketAddress addr = AFUNIXSocketAddress.of(new File("/tmp/ssldemo"));
61  
62      SSLSocketFactory serverSocketFactory = SSLContextBuilder.forServer() //
63          .withKeyStore(new File("juxserver.p12"), () -> "serverpass".toCharArray()) //
64          .withTrustStore(new File("juxserver.truststore"), () -> "servertrustpass".toCharArray()) //
65          .withDefaultSSLParameters((p) -> {
66            // A simple way to disable some undesired protocols:
67            // SSLParametersUtil.disableProtocols(p, "TLSv1.0", "TLSv1.1", "TLSv1.2");
68  
69            // Uncomment to require client-authentication
70            // (which is meaningful in the context of UNIX sockets)
71            // p.setNeedClientAuth(true);
72  
73            // Uncomment to perform endpoint checking of client certificates
74            // p.setEndpointIdentificationAlgorithm("HTTPS");
75          }) //
76          .buildAndDestroyBuilder().getSocketFactory();
77  
78      AFSocketServer<AFUNIXSocketAddress> server = new AFSocketServer<AFUNIXSocketAddress>(addr) {
79  
80        // Match any SNI hostname, including none sent
81        final SNIMatcher sniHostnameMatcher = SNIHostnameCapture.ACCEPT_ANY_HOSTNAME;
82  
83        // Replace with code below to enforce receiving a valid SNI hostname
84        // final SNIMatcher sniHostnameMatcher = SNIHostName.createSNIMatcher("(^|.+?\\.)" + Pattern
85        // .quote("example.com"));
86  
87        @Override
88        protected void doServeSocket(AFSocket<? extends AFUNIXSocketAddress> plainSocket)
89            throws IOException {
90          try (SSLSocket sslSocket = (SSLSocket) serverSocketFactory.createSocket(plainSocket,
91              // This is considered the peer name (the client's name)
92              "localhost.junixsocket", plainSocket.getPort(), //
93              // you can also replace the above line with:
94              // null,
95              false)) {
96  
97            // Setting the client mode manually is not necessary when using SSLContextBuilder
98            // sslSocket.setUseClientMode(false);
99  
100           SNIHostnameCapture sniHostname = SNIHostnameCapture.configure(sslSocket,
101               sniHostnameMatcher);
102 
103           // Set to 0 to disable receiving file descriptors, etc.
104           plainSocket.setAncillaryReceiveBufferSize(1); // rounds up to minimum buffer size
105 
106           sslSocket.startHandshake();
107           // Make sure the handshake is complete before we check the hostname
108           // (otherwise: IllegalStateException)
109           try {
110             if (sniHostname.isComplete(1, TimeUnit.SECONDS)) {
111               System.out.println("Requested SNI hostname: " + sniHostname.getHostname());
112             }
113           } catch (InterruptedException e) {
114             e.printStackTrace();
115           }
116 
117           try (InputStream in = sslSocket.getInputStream();
118               OutputStream out = sslSocket.getOutputStream();) {
119             System.out.println("Received byte: " + Integer.toHexString(in.read()));
120 
121             System.out.println("Sending hello...");
122             out.write("Hello World".getBytes(StandardCharsets.UTF_8));
123 
124             FileDescriptor[] fds = ((AFUNIXSocket) plainSocket).getReceivedFileDescriptors();
125             if (fds.length > 0) {
126               System.out.println("File descriptor received: " + Arrays.asList(fds));
127               System.out.println(
128                   "Sending an extra message directly to a FileDescriptor of the other process...");
129               try (PrintStream ps = new PrintStream(FileDescriptorCast.using(fds[0]).as(
130                   OutputStream.class), true, Charset.defaultCharset().name())) {
131                 ps.println("Greetings from the server, right to your stderr");
132               }
133             }
134           }
135         }
136       }
137 
138       @Override
139       protected void onServingException(AFSocket<? extends AFUNIXSocketAddress> socket,
140           Exception e) {
141         e.printStackTrace();
142       }
143     };
144     server.startAndWaitToBecomeReady();
145   }
146 }