view src/goodjava/lucene/backup/BackupServer.java @ 1828:09e90d94b7b5

add DomainFilter
author Franklin Schmidt <fschmidt@gmail.com>
date Sun, 15 Sep 2024 19:51:16 -0600
parents 2dbcc8360a3e
children
line wrap: on
line source

package goodjava.lucene.backup;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.net.InetAddress;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLServerSocket;
import goodjava.util.SoftCacheMap;
import goodjava.io.IoUtils;
import goodjava.rpc.RpcServer;
import goodjava.rpc.RpcCall;
import goodjava.rpc.RpcClient;
import goodjava.rpc.RpcResult;
import goodjava.rpc.Rpc;
import goodjava.rpc.RpcException;
import goodjava.logging.Logger;
import goodjava.logging.LoggerFactory;


public final class BackupServer {
	private static final Logger logger = LoggerFactory.getLogger(BackupServer.class);

	public static int port = 9102;
	public static String[] cipherSuites = new String[] {
		"TLS_DH_anon_WITH_AES_128_GCM_SHA256",
		"TLS_DH_anon_WITH_AES_128_CBC_SHA256",
		"TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
		"TLS_DH_anon_WITH_AES_128_CBC_SHA",
		"TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
		"SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
		"TLS_ECDH_anon_WITH_RC4_128_SHA",
		"SSL_DH_anon_WITH_RC4_128_MD5",
		"SSL_DH_anon_WITH_DES_CBC_SHA",
		"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
		"SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
	};

	private final File backupDir;
	private static final ExecutorService threadPool = Executors.newCachedThreadPool();
	private static final Map<String,Backup> backups = new SoftCacheMap<String,Backup>();

	public BackupServer(File backupDir) throws IOException {
		this.backupDir = backupDir;
		IoUtils.mkdirs(backupDir);
	}

	public synchronized void start() throws IOException {
		final ServerSocket ss;
		if( cipherSuites == null ) {
			ss = new ServerSocket(port);
		} else {
			ss = IoUtils.getSSLServerSocketFactory().createServerSocket(port);
			((SSLServerSocket)ss).setEnabledCipherSuites(cipherSuites);
		}
		threadPool.execute(new Runnable(){public void run() {
			try {
				while(!threadPool.isShutdown()) {
					final Socket socket = ss.accept();
					threadPool.execute(new Runnable(){public void run() {
						handle(socket);
					}});
				}
			} catch(IOException e) {
				logger.error("",e);
			}
		}});
		logger.info("started server on port "+port);
	}

	private static String getName(RpcServer rpc,Object[] args) {
		String domain = (String)args[0];
		InetAddress addr;
		try {
			addr = InetAddress.getByName(domain);
		} catch(UnknownHostException e) {
			rpc.write( new RpcException("domain lookup failed") );
			rpc.close();
			return null;
		}
		if( !rpc.socket.getInetAddress().equals(addr) ) {
			rpc.write( new RpcException("domain doesn't match client") );
			rpc.close();
			return null;
		}
		String name = (String)args[1];
		return name==null ? domain : domain + "~" + name;
	}

	private Backup getBackup(String name) {
		synchronized(backups) {
			Backup backup = backups.get(name);
			if( backup == null ) {
				backup = new Backup( new File(backupDir,name) );
				backups.put(name,backup);
			}
			return backup;
		}
	}

	private void handle(Socket socket) {
		RpcServer rpc = new RpcServer(socket);
		Backup backup = null;
		while( !rpc.isClosed() ) {
			RpcCall call = rpc.read();
			if( call == null )
				break;
			if( call.cmd.equals("exists") ) {
				String name = getName(rpc,call.args);
				if( name==null )  return;
				rpc.write( new RpcResult(new Object[]{new File(backupDir,name).exists()}) );
			} else if( call.cmd.equals("login") ) {
				String name = getName(rpc,call.args);
				if( name==null )  return;
				backup = getBackup(name);
				rpc.write(Rpc.OK);
			} else if( backup != null ) {
				backup.handle(rpc,call);
			} else if( call.cmd.equals("copy") ) {
				try {
					if( !IoUtils.getInetAddresses().contains(rpc.socket.getInetAddress()) ) {
						rpc.write( new RpcException("only localhost allowed") );
						rpc.close();
						return;
					}
				} catch(IOException e) {
					throw new RuntimeException(e);
				}
				String dirName = (String)call.args[0];
				copy(new File(dirName));
				rpc.write(Rpc.OK);
			} else {
				rpc.write( new RpcException("login expected") );
				rpc.close();
				return;
			}
		}
	}

	private void copy(File dir) {
		try {
			IoUtils.deleteRecursively(dir);
			IoUtils.mkdirs(dir);
			for( File f : backupDir.listFiles() ) {
				if( f.isDirectory() && new File(f,"index.json").exists() ) {
					String name = f.getName();
					Backup backup = getBackup(name);
					backup.copyTo( new File(dir,name) );
				}
			}
		} catch(IOException e) {
			throw new RuntimeException(e);
		}
	}


	// for client

	public static RpcClient rpcClient(String backupDomain) throws IOException {
		Socket socket;
		if( BackupServer.cipherSuites == null ) {
			socket = new Socket(backupDomain,BackupServer.port);
		} else {
			socket = IoUtils.getSSLSocketFactory().createSocket(backupDomain,BackupServer.port);
			((SSLSocket)socket).setEnabledCipherSuites(BackupServer.cipherSuites);
		}
		return new RpcClient(socket);
	}

	public static void copyBackupTo(String dirName) throws IOException, RpcException {
		RpcClient rpc = BackupServer.rpcClient("localhost");
		rpc.write( new RpcCall("copy",dirName) );
		RpcResult result = rpc.read();
		rpc.close();
	}

}