import java.io.*;
import java.net.*;
import java.security.KeyStore;
import javax.net.*;
import javax.net.ssl.*;
import javax.security.cert.X509Certificate;


public class TCPSecureServer {

    public static void main(String args[])
    {
        // Processing the command line arguments
        if (args.length < 1) {
            System.err.println("Too few arguments...need 1 or 2");
            System.err.println("USAGE: java TCPSecureServer port [true]");
            System.exit(-1);
        }
        int portNumber = -1;
        try {
            portNumber = Integer.parseInt(args[0]);
        } catch (IllegalArgumentException e) {
            System.err.println("Error converting port number " + portNumber);
            System.err.println("USAGE: java TCPSecureServer port [true]"); 
            System.exit(-1);
        }

        // Set up the secure listening socket
        ServerSocket listenSocket = null;
	try {
	    ServerSocketFactory theFactory = TCPSecureServer.getServerSocketFactory();
	    listenSocket = theFactory.createServerSocket(portNumber);
	    if (args.length >= 2) { // if two commmand line args, require client auth.
		((SSLServerSocket)listenSocket).setNeedClientAuth(true);
	    }
        } catch (Exception e) { 
            System.err.println("Server died setting up SSL: " + e.getMessage()); 
            e.printStackTrace();
            System.exit(-1); 
        }

        while (true) {
            Socket clientSocket = null;
            try {
                clientSocket = listenSocket.accept();
                Connection c = new Connection(clientSocket);
            } catch (IOException e) {
               System.err.println("Client connection failed Server died: " + e.getMessage());
               e.printStackTrace();
               try {
                  if (clientSocket != null)
                     clientSocket.close();
               } catch (Exception eclose) {
                 System.err.println("Failed closing client socket: " + eclose.getMessage());
                 eclose.printStackTrace();
               }
            }
        }
    }

    private static ServerSocketFactory getServerSocketFactory() {
       SSLServerSocketFactory theFactory = null;
       try {
	// set up key manager to do server authentication
          SSLContext theContext;
          KeyManagerFactory theKeyManagerFactory;
          KeyStore theKeyStore;
          char[] thePassword = "cs5523".toCharArray();

          theContext = SSLContext.getInstance("TLS");
          theKeyManagerFactory = KeyManagerFactory.getInstance("SunX509");
          theKeyStore = KeyStore.getInstance("JKS");

          theKeyStore.load(new FileInputStream("cs5523keys"), thePassword);
          theKeyManagerFactory.init(theKeyStore, thePassword);
          theContext.init(theKeyManagerFactory.getKeyManagers(), null, null);

          theFactory = theContext.getServerSocketFactory();
          return theFactory;
       } catch (Exception e) {
          System.err.println("Failed to create a server socket factory..."); 
          e.printStackTrace();
       }
     return null;
   }
}



class Connection extends Thread {
    private static final int DEFAULT_SIZE = 1024;
    private Socket clientSocket;

    public Connection (Socket aClientSocket) {
       clientSocket = aClientSocket;
       this.start();
    }

    public void run()
    {
       try {
          DataOutputStream out = new DataOutputStream(clientSocket.getOutputStream());
          DataInputStream in = new DataInputStream(clientSocket.getInputStream());
          System.out.println("Waiting for client message...");
          byte [] buf = new byte[DEFAULT_SIZE];
          int bytesRead = in.read(buf);
          String sendBack = "<Server Received [" + new String(buf, 0, bytesRead) + "]" +">";
          System.out.println("Echo..." + sendBack);
          out.write(sendBack.getBytes());
          out.flush();
        } catch (IOException e) {
          System.err.println("Server error processing response: " + e.getMessage());
          e.printStackTrace();
        } finally {
          try {
             clientSocket.close();
          } catch (IOException e) {
          }
        }    
    }    
}



