Download | Plain Text | No Line Numbers
- /*
- * Copyright (c) 2010, Manuel Mausz. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - The names of the authors may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
- import javax.crypto.*;
- import java.security.*;
- import org.bouncycastle.openssl.PEMReader;
- import org.bouncycastle.util.encoders.Base64;
- import org.bouncycastle.openssl.PasswordFinder;
- import org.bouncycastle.util.encoders.Hex;
-
- import java.nio.ByteBuffer;
- import java.nio.channels.ServerSocketChannel;
- import java.nio.channels.SocketChannel;
- import java.nio.channels.DatagramChannel;
-
- import java.util.concurrent.ScheduledExecutorService;
- import java.util.concurrent.ScheduledFuture;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.TimeUnit;
- import java.util.MissingResourceException;
- import java.util.Collections;
- import java.util.Enumeration;
- import java.util.ArrayList;
- import java.util.Calendar;
- import java.util.Iterator;
- import java.util.HashMap;
- import java.util.Arrays;
- import java.util.Map;
- import java.net.*;
- import java.io.*;
-
- /*
- * Proxy implementation for Lab#1 of DSLab WS10
- * See angabe.pdf for details
- *
- * This code is not documented at all. This is volitional
- *
- * @author Manuel Mausz (0728348)
- */
- {
- public class FSRecord
- implements Comparable<FSRecord>
- {
- public final int port;
- public int usage;
- public boolean online;
- public long lastUpdate;
-
- {
- this.host = host;
- this.port = port;
- usage = 0;
- online = true;
- }
-
- public void ping()
- {
- online = true;
- }
-
- public boolean equals(FSRecord o)
- {
- return online == o.online && usage == o.usage;
- }
-
- public int compareTo(FSRecord o)
- {
- return usage - o.usage;
- }
- }
-
- public class FSRecords
- {}
-
- /*==========================================================================*/
-
- public class UserRecord
- {
- public int credits;
- public ArrayList<String> loggedin;
-
- {
- this.name = name;
- this.pass = null;
- this.credits = credits;
- loggedin = new ArrayList<String>();
- }
- }
-
- public class UserRecords
- {}
-
- /*==========================================================================*/
-
-
- public class ProxyConnection
- implements Runnable
- {
- private final int port;
- private FSRecords fileservers;
-
- {
- this.host = host;
- this.port = port;
- this.fileservers = fileservers;
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- synchronized(fileservers)
- {
- FSRecord record = fileservers.get(key);
- if (record == null)
- {
- fileservers.put(key, new FSRecord(host, port));
- out.println("New fileserver registered: " + key);
- }
- else
- {
- if (!record.online)
- out.println("Fileserver is online again: " + key);
- record.ping();
- }
- }
- }
- }
-
- /*==========================================================================*/
-
- public class UDPSocketReader
- implements Runnable
- {
- private final DatagramChannel dchannel;
- private FSRecords fileservers;
- private final ExecutorService pool;
-
- UDPSocketReader(DatagramChannel dchannel, FSRecords fileservers,
- Object mainLock)
- {
- this.dchannel = dchannel;
- this.fileservers = fileservers;
- this.mainLock = mainLock;
- this.pool = Executors.newCachedThreadPool();
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- try
- {
- ByteBuffer buffer = ByteBuffer.allocate(tmp.getBytes().length);
- while(true)
- {
- buffer.clear();
- InetSocketAddress proxyaddr = (InetSocketAddress) dchannel.receive(buffer);
- if (msg.length() != tmp.length())
- continue;
- assert msg.matches("!alive 1[0-9]{4}");
- try
- {
- pool.execute(new ProxyConnection(proxyaddr.getHostName(),
- }
- {
- /* simple ignore that packet */
- }
- }
- }
- {
- /* ignore that exception
- * thread will shutdown and unlock the main thread
- * which will shutdown the application
- */
- }
-
- pool.shutdown();
- try
- {
- if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS))
- out.println("Trying to shutdown the UDP Proxy connections. This may take up to 15 seconds...");
- if (!pool.awaitTermination(5, TimeUnit.SECONDS))
- {
- pool.shutdownNow();
- if (!pool.awaitTermination(5, TimeUnit.SECONDS))
- err.println("Error: UDP Proxy connections did not terminate. You may have to kill that appplication.");
- }
- }
- {
- pool.shutdownNow();
- }
-
- synchronized(mainLock)
- {
- mainLock.notify();
- }
- }
- }
-
- /*==========================================================================*/
-
- public class ClientConnection
- extends CommandNetwork
- implements Runnable
- {
- private final SocketChannel sock;
- private final FSRecords fileservers;
- private final UserRecords users;
- private final Utils.EncObjectInputStream clin;
- private final Utils.EncObjectOutputStream clout;
- private UserRecord user = null;
-
- private CommandNetwork fscmd;
- private ArrayList<SocketChannel> fsschannel;
- private ArrayList<Utils.EncObjectInputStream> fsin;
- private ArrayList<Utils.EncObjectOutputStream> fsout;
- private ArrayList<FSRecord> fileserver;
- private int curconn;
-
-
- ClientConnection(SocketChannel sock, FSRecords fileservers,
- UserRecords users)
- {
- this.sock = sock;
- this.clout = new Utils.EncObjectOutputStream(sock.socket().getOutputStream());
- this.clin = new Utils.EncObjectInputStream(new BufferedInputStream(sock.socket().getInputStream()));
- this.fileservers = fileservers;
- this.users = users;
- this.clientaddr = "tcp:/" + sock.socket().getInetAddress() + ":" + sock.socket().getPort();
-
- fsschannel = new ArrayList<SocketChannel>();
- fsin = new ArrayList<Utils.EncObjectInputStream>();
- fsout = new ArrayList<Utils.EncObjectOutputStream>();
- fileserver = new ArrayList<FSRecord>();
-
- clin.setCipher(rsadecrypt);
-
- cmdHandler.register("!login", this, "cmdLogin");
- cmdHandler.register("!buy", this, "cmdBuy");
- cmdHandler.register("!credits", this, "cmdCredits");
- cmdHandler.register("!list", this, "cmdList");
- cmdHandler.register("!download", this, "cmdDownload");
- cmdHandler.register("!upload", this, "cmdUpload");
- cmdHandler.register("!upload2", this, "cmdUpload2");
- cmdHandler.register("unknown", this, "cmdUnknown");
-
- fscmd = new CommandNetwork();
- fscmd.setOneCommandMode(true);
- fscmd.cmdHandler.register("!error", this, "cmdFSRelayOutput");
- fscmd.cmdHandler.register("!output", this, "cmdFSRelayOutput");
- fscmd.cmdHandler.register("!download", this, "cmdFSDownload");
- fscmd.cmdHandler.register("!list", this, "cmdFSList");
- fscmd.cmdHandler.register("!hasherr", this, "cmdFSHashError");
- fscmd.cmdHandler.register("unknown", this, "cmdFSRelayOutput");
- }
-
- /*------------------------------------------------------------------------*/
-
- public boolean checkLogin()
- throws IOException
- {
- if (user == null)
- {
- Utils.sendError(clout, "Not logged in");
- return false;
- }
- return true;
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (user != null)
- {
- Utils.sendError(clout, "Already logged in");
- return;
- }
-
- if (args.length != 2)
- {
- Utils.sendError(clout, "Invalid Syntax: !login <username>");
- return;
- }
-
- assert firstMessage.matches("!login \\w+ [" + B64 + "]{43}=") : "1st message";
-
- synchronized(users)
- {
- UserRecord record = users.get(args[0]);
- if (record == null)
- {
- Utils.sendError(clout, "Invalid username");
- return;
- }
-
- user = record;
- }
-
- /* read and init users public key */
- if (!pemfile.isFile())
- {
- Utils.sendError(clout, "No public keyfile");
- return;
- }
- if (!pemfile.canRead())
- {
- Utils.sendError(clout, "Your public keyfile is not readable");
- return;
- }
- try
- {
- rsaencrypt.init(Cipher.ENCRYPT_MODE, publicKey);
- clout.setCipher(rsaencrypt);
- }
- {
- Utils.sendError(clout, "Your public keyfile is not readable");
- return;
- }
- {
- Utils.sendError(clout, "While reading users public key");
- return;
- }
- {
- Utils.sendError(clout, "invalid public key file: " + e.getMessage());
- return;
- }
-
-
- /* generates a 32 byte secure random number */
- byte[] tmp = new byte[32];
- secureRandom.nextBytes(tmp);
-
- SecretKey seckey = null;
- javax.crypto.spec.IvParameterSpec iv = null;
- try
- {
- /* generate aes key */
- KeyGenerator generator = KeyGenerator.getInstance("AES");
- generator.init(256);
- seckey = generator.generateKey();
-
- /* generate iv */
- byte[] tmpiv = new byte[16];
- secureRandom.nextBytes(tmpiv);
- iv = new javax.crypto.spec.IvParameterSpec(tmpiv);
-
- /* init aes */
- aesencrypt.init(Cipher.ENCRYPT_MODE, seckey, iv);
- aesdecrypt.init(Cipher.DECRYPT_MODE, seckey, iv);
- }
- {
- err.println("Error: Unable to generate AES key: " + e.getMessage());
- return;
- }
- {
- err.println("Error: invalid AES key: " + e.getMessage());
- return;
- }
- {
- err.println("Error: invalid AES parameters: " + e.getMessage());
- return;
- }
-
- ArrayList<String> msg = new ArrayList<String>();
- msg.add("!ok");
- msg.add(clichallenge);
- msg.add(mychallenge);
-
- assert secondMessage.matches("!ok [" + B64 + "]{43}= [" + B64 + "]{43}= [" + B64 + "]{43}= [" + B64 + "]{22}==") : "2nd message";
- clout.writeLine(secondMessage);
- clout.flush();
-
- clout.setCipher(aesencrypt);
- clin.setCipher(aesdecrypt);
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (!checkLogin())
- return;
- if (args.length != 1)
- {
- Utils.sendError(clout, "Invalid Syntax: !buy <credits>");
- return;
- }
-
- int add = 0;
- try
- {
- if (add <= 0)
- }
- {
- Utils.sendError(clout, "Credits must be numberic and positive");
- return;
- }
-
- synchronized(users)
- {
- Utils.sendError(clout, "You can't buy that much/more credits");
- else
- {
- user.credits += add;
- Utils.sendOutput(clout, "You now have " + user.credits + " credits");
- }
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (!checkLogin())
- return;
- if (args.length != 0)
- {
- Utils.sendError(clout, "Invalid Syntax: !credits");
- return;
- }
-
- synchronized(users)
- {
- Utils.sendOutput(clout, "You have " + user.credits + " credits left");
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- public int connectFileserver(FSRecord fileserver)
- throws IOException
- {
- if (fileserver == null || !fileserver.online)
- {
- Utils.sendError(clout, "Unable to execute command. Fileserver not online");
- return -1;
- }
-
- synchronized(fsschannel)
- {
- int conn = fsschannel.size();
- try
- {
- fsschannel.add(conn, SocketChannel.open(new InetSocketAddress(fileserver.host,
- fileserver.port)));
- fsschannel.get(conn).socket().getInputStream())));
- fsout.add(conn, new Utils.EncObjectOutputStream(fsschannel.get(conn).socket().getOutputStream()));
- this.fileserver.add(conn, fileserver);
-
- fsin.get(conn).setMAC(hmac);
- fsout.get(conn).setMAC(hmac);
- }
- {
- err.println("Error: Unable to connect to fileserver: " + e.getMessage());
- Utils.sendError(clout, "Unable to connect to fileserver");
-
- synchronized(fileservers)
- {
- fileserver.online = false;
- out.println("Fileserver marked as offline: " + fileserver.host + ":" + fileserver.port);
- }
-
- disconnectFileserver(conn);
- return -1;
- }
- return conn;
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- public void disconnectFileserver(int conn)
- {
- if (conn < 0)
- return;
-
- synchronized(fsschannel)
- {
- try
- {
- if (fsout.get(conn) != null)
- fsout.get(conn).flush();
- }
- {}
- {}
-
- try
- {
- if (fsin.get(conn) != null)
- fsin.get(conn).close();
- fsin.set(conn, null);
- for(int i = fsin.size() - 1; i >= 0; --i)
- {
- if (fsin.get(i) != null)
- break;
- fsin.remove(i);
- }
- }
- {}
- {}
-
- try
- {
- if (fsout.get(conn) != null)
- fsout.get(conn).close();
- fsout.set(conn, null);
- for(int i = fsout.size() - 1; i >= 0; --i)
- {
- if (fsout.get(i) != null)
- break;
- fsout.remove(i);
- }
- }
- {}
- {}
-
- try
- {
- if (fsschannel.get(conn) != null)
- fsschannel.get(conn).close();
- fsschannel.set(conn, null);
- for(int i = fsschannel.size() - 1; i >= 0; --i)
- {
- if (fsschannel.get(i) != null)
- break;
- fsschannel.remove(i);
- }
- }
- {}
- {}
-
- try
- {
- fileserver.set(conn, null);
- for(int i = fileserver.size() - 1; i >= 0; --i)
- {
- if (fileserver.get(i) != null)
- break;
- fileserver.remove(i);
- }
- }
- {}
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (!checkLogin())
- return;
- if (args.length != 0)
- {
- Utils.sendError(clout, "Invalid Syntax: !list");
- return;
- }
-
- synchronized(fileservers)
- {
- for(FSRecord fileserver : fileservers.values())
- {
- if (!fileserver.online)
- continue;
- int conn = -1;
- try
- {
- if ((conn = connectFileserver(fileserver)) < 0)
- return;
- fsout.get(conn).flush();
- curconn = conn;
- fscmd.run(fsin.get(conn));
- }
- catch(Utils.HashError e)
- {
- fileserver.online = false;
- }
- {
- fileserver.online = false;
- Utils.sendError(clout, "Connection to fileserver " + fileserver.host + ":" + fileserver.port + " terminated unexpected");
- }
- disconnectFileserver(conn);
- }
- }
-
- if (filelist.size() == 0)
- Utils.sendOutput(clout, "No files available");
- else
- {
- ArrayList<String> tmp = new ArrayList<String>();
- tmp.add(file.getKey() + " v=" + file.getValue());
- }
-
- clout.flush();
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (!checkLogin())
- return;
- if (args.length != 1)
- {
- Utils.sendError(clout, "Invalid Syntax: !download <filename>");
- return;
- }
-
- FSRecord curfs = null;
- long curversion = -1;
- synchronized(fileservers)
- {
- calcGifford();
- for(FSRecord fileserver : rfileservers)
- {
- int conn = -1;
- try
- {
- if ((conn = connectFileserver(fileserver)) < 0)
- return;
- fsout.get(conn).writeLine("!list");
- fsout.get(conn).flush();
- curconn = conn;
- fscmd.run(fsin.get(conn));
- }
- catch(Utils.HashError e)
- {
- fileserver.online = false;
- }
- {
- fileserver.online = false;
- Utils.sendError(clout, "Connection to fileserver " + fileserver.host + ":" + fileserver.port + " terminated unexpected");
- }
- disconnectFileserver(conn);
-
- if (version != null && version > curversion)
- {
- curfs = fileserver;
- curversion = version;
- }
- }
- }
-
- if (curfs == null)
- {
- Utils.sendError(clout, "File not found or no fileservers online");
- return;
- }
-
- int conn = -1;
- try
- {
- if ((conn = connectFileserver(curfs)) < 0)
- return;
- synchronized(users)
- {
- }
- fsout.get(conn).flush();
- curconn = conn;
- fscmd.run(fsin.get(conn));
- }
- catch(Utils.HashError e)
- {
- curfs.online = false;
- }
- {
- Utils.sendError(clout, "Connection to fileserver " + fileserver.get(conn).host + ":" + fileserver.get(conn).port + "terminated unexpected");
- }
- clout.flush();
- disconnectFileserver(conn);
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (!checkLogin())
- return;
- if (args.length != 1)
- {
- Utils.sendError(clout, "Invalid Syntax: !upload <filename>");
- return;
- }
-
- synchronized(fileservers)
- {
- if (fileservers.size() == 0)
- {
- Utils.sendError(clout, "Unable to execute command. No fileservers online");
- return;
- }
- }
-
- clout.writeLine(cmd + " " + args[0]);
- clout.flush();
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (!checkLogin())
- return;
- if (args.length < 2 || args[1].length() <= 0)
- {
- err.println("Error: Invalid " + cmd + "-command paket from client. Ignoring...");
- Utils.sendError(clout, "Internal Error: Invalid packet from client");
- return;
- }
-
- long filesize, filesizecpy;
- if ((filesize = Utils.parseHeaderNum(args, 1)) < 0)
- return;
- filesizecpy = filesize;
-
- long curversion = 0;
- synchronized(fileservers)
- {
- calcGifford();
- for(FSRecord fileserver : rfileservers)
- {
- int conn = -1;
- try
- {
- if ((conn = connectFileserver(fileserver)) < 0)
- return;
- fsout.get(conn).writeLine("!list");
- fsout.get(conn).flush();
- curconn = conn;
- fscmd.run(fsin.get(conn));
- }
- catch(Utils.HashError e)
- {
- fileserver.online = false;
- }
- {
- fileserver.online = false;
- Utils.sendError(clout, "Connection to fileserver " + fileserver.host + ":" + fileserver.port + " terminated unexpected");
- }
- disconnectFileserver(conn);
-
- if (version != null && version > curversion)
- curversion = version;
- }
-
- ArrayList<Integer> conns = new ArrayList<Integer>();
- for(FSRecord fileserver : wfileservers)
- {
- int conn = -1;
- if ((conn = connectFileserver(fileserver)) < 0)
- return;
- conns.add(conn);
- }
-
- try
- {
- fsout.get(conn).writeLine(cmd + " " + Utils.join(Arrays.asList(args), " ") + " " + (curversion + 1));
-
- byte[] buffer = new byte[8 * 1024];
- int toread = buffer.length;
- while(filesize > 0)
- {
- if (filesize < toread)
- toread = (int) filesize;
- int count = clin.read(buffer, 0, toread);
- if (count == -1)
-
- /* decode + hash that chunk */
- byte[] decbuffer = new byte[aesdecrypt.getOutputSize(count)];
- int deccount = aesdecrypt.update(buffer, 0, count, decbuffer);
- hmac.update(decbuffer, 0, deccount);
-
- fsout.get(conn).write(decbuffer, 0, deccount);
- filesize -= count;
- }
- /* decryption + hash must be finalized */
- byte[] decbuffer = aesdecrypt.doFinal();
- byte[] hash = hmac.doFinal(decbuffer);
- fsout.get(conn).write(decbuffer);
- fsout.get(conn).write(hash);
- fsout.get(conn).flush();
-
- synchronized(users)
- {
- user.credits += 2 * filesizecpy;
- }
- fileserver.get(conn).usage += filesizecpy;
- Utils.sendOutput(clout, "File '" + args[0] + "' successfully uploaded.");
- }
- {
- err.println("Error during file transfer: " + e.getMessage() + ". Closing connection to client");
- stop();
-
- {
- fileserver.get(conn).usage += filesizecpy;
- fileserver.get(conn).online = false;
- out.println("Fileserver marked as offline: " + fileserver.get(conn).host + ":" + fileserver.get(conn).port);
- }
- }
- {
- err.println("Error during encrypting file transfer: " + e.getMessage() + ". Closing connection to client");
- stop();
- }
-
- disconnectFileserver(conn);
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (args.length == 0 && cmd.equals(mychallenge))
- {
- assert thirdMessage.matches("[" + B64 + "]{43}=") : "3rd message";
-
- synchronized(users)
- {
- user.loggedin.add(clientaddr);
- }
-
- Utils.sendOutput(clout, "Successfully logged in");
- return;
- }
-
- err.println("Error: Unknown data from client: " + cmd + " "
- Utils.sendError(clout, "Unknown command");
- }
-
- /*------------------------------------------------------------------------*/
-
- @SuppressWarnings("deprecation")
- throws IOException
- {
- long num;
- if ((num = Utils.parseHeaderNum(args, 0)) < 0)
- return;
- String msg;
- for (; num > 0 && (msg = fsin.get(curconn).readLine()) != null; --num)
- clout.writeLine(msg);
- clout.flush();
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (args.length < 2 || args[1].length() <= 0)
- {
- err.println("Error: Invalid " + cmd + "-command paket from fileserver. Ignoring...");
- Utils.sendError(clout, "Internal Error: Invalid packet from fileserver");
- return;
- }
-
- long filesize, filesizecpy;
- if ((filesize = Utils.parseHeaderNum(args, 1)) < 0)
- return;
- filesizecpy = filesize;
-
-
- try
- {
- byte[] buffer = new byte[8 * 1024];
- int toread = buffer.length;
- while(filesize > 0)
- {
- if (filesize < toread)
- toread = (int) filesize;
- int count = fsin.get(curconn).read(buffer, 0, toread);
- if (count == -1)
-
- hmac.update(buffer, 0, count);
-
- /* encode that chunk */
- byte[] encbuffer = new byte[aesencrypt.getOutputSize(count)];
- int enccount = aesencrypt.update(buffer, 0, count, encbuffer);
-
- clout.write(encbuffer, 0, enccount);
- filesize -= count;
- }
-
- /* encryption must be finalized */
- clout.write(aesencrypt.doFinal());
-
- byte[] hashc = hmac.doFinal();
- byte[] hasht = new byte[hmac.getMacLength()];
- fsin.get(curconn).readFully(hasht);
- {
- synchronized(fileservers)
- {
- err.println("Error: invalid MAC during filetransfer. Taking fileserver offline and restarting download.");
- fileserver.get(curconn).online = false;
- out.println("Fileserver marked as offline: " + fileserver.get(curconn).host + ":" + fileserver.get(curconn).port);
- cmdDownload("!download", tmp);
- return;
- }
- }
-
- Utils.sendOutput(clout, "File '" + file + "' successfully downloaded.");
-
- synchronized(users)
- {
- user.credits -= filesizecpy;
- }
-
- synchronized(fileservers)
- {
- fileserver.get(curconn).usage += filesizecpy;
- }
- }
- {
- err.println("Error during file transfer: " + e.getMessage() + ". Closing connection to client");
- stop();
-
- synchronized(fileservers)
- {
- fileserver.get(curconn).usage += filesizecpy;
- fileserver.get(curconn).online = false;
- out.println("Fileserver marked as offline: " + fileserver.get(curconn).host + ":" + fileserver.get(curconn).port);
- }
- }
- {
- err.println("Error during encrypting file transfer: " + e.getMessage() + ". Closing connection to client");
- stop();
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- @SuppressWarnings("unchecked")
- throws IOException
- {
- long num;
- if ((num = Utils.parseHeaderNum(args, 0)) < 0)
- return;
-
- String msg;
- for (; num > 0 && (msg = fsin.get(curconn).readLine()) != null; --num)
- {
- int ix = msg.lastIndexOf(' ');
- if (ix == -1)
- {
- err.println("Error: Unknown filelist-message. Ignoring...");
- continue;
- }
-
- try
- {
-
- if (version == null || fileversion > version)
- filelist.put(file, fileversion);
- }
- {
- err.println("Error: Unable to parse file version. Ignoring...");
- continue;
- }
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- err.println("Error: Hasherror from fileserver!");
- synchronized(fileservers)
- {
- fileserver.get(curconn).online = false;
- out.println("Fileserver marked as offline: " + fileserver.get(curconn).host + ":" + fileserver.get(curconn).port);
- }
- cmdHandler.call2();
- }
-
- /*------------------------------------------------------------------------*/
-
- public void shutdown()
- {
- for(int i = fsschannel.size() - 1; i >= 0; --i)
- disconnectFileserver(i);
-
- try
- {
- clout.flush();
- }
- {}
-
- try
- {
- clin.close();
- }
- {}
-
- try
- {
- clout.close();
- }
- {}
-
- try
- {
- if (sock.isOpen())
- sock.close();
- }
- {}
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- try
- {
- + clientaddr);
- run(clin);
- clout.flush();
- }
- {
- err.println("Internal Error: " + e.getMessage());
- e.printStackTrace();
- }
- {
- /* ignore that exception
- * it's usually a closed connection from client so
- * we can't do anything about it anyway
- */
- }
-
- if (user != null)
- {
- synchronized(users)
- {
- user.loggedin.remove(user.loggedin.indexOf(clientaddr));
- }
- }
-
- shutdown();
- }
- }
-
- /*==========================================================================*/
-
- public class TCPSocketReader
- implements Runnable
- {
- private final ServerSocketChannel sschannel;
- private final FSRecords fileservers;
- private final UserRecords users;
- private final ExecutorService pool;
-
- TCPSocketReader(ServerSocketChannel sschannel, FSRecords fileservers,
- UserRecords users, Object mainLock)
- {
- this.sschannel = sschannel;
- this.fileservers = fileservers;
- this.users = users;
- this.mainLock = mainLock;
- this.pool = Executors.newCachedThreadPool();
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- try
- {
- while(true)
- pool.execute(new ClientConnection(sschannel.accept(), fileservers, users));
- }
- {
- err.println("Error: Unable to setup remote command handler");
- }
- {
- /* ignore that exception
- * thread will shutdown and unlock the main thread
- * which will shutdown the application
- */
- }
-
- pool.shutdown();
- try
- {
- if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS))
- out.println("Trying to shutdown the client connections. This may take up to 15 seconds...");
- if (!pool.awaitTermination(5, TimeUnit.SECONDS))
- {
- pool.shutdownNow();
- if (!pool.awaitTermination(5, TimeUnit.SECONDS))
- err.println("Error: Client connections did not terminate. You may have to kill that appplication.");
- }
- }
- {
- pool.shutdownNow();
- }
-
- synchronized(mainLock)
- {
- mainLock.notify();
- }
- }
- }
-
- /*==========================================================================*/
-
- public class Interactive
- extends CommandInteractive
- implements Runnable
- {
-
- throws NoSuchMethodException
- {
- this.sin = sin;
- this.mainLock = mainLock;
-
- cmdHandler.register("unknown", this, "cmdUnknown");
- cmdHandler.register("!fileservers", this, "cmdFileservers");
- cmdHandler.register("!users", this, "cmdUsers");
- cmdHandler.register("!exit", this, "cmdExit");
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- err.println("Unknown command: " + cmd + " "
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- synchronized(fileservers)
- {
- if (fileservers.size() == 0)
- out.println("No fileservers registered");
- else
- {
- calcGifford();
- int line = 1;
- {
- FSRecord record = entry.getValue();
- line, record.host, record.port,
- (record.online) ? "online" : "offline",
- record.usage,
- (rfileservers.contains(record)) ? "R" : "",
- (wfileservers.contains(record)) ? "W" : ""));
- ++line;
- }
- }
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- synchronized(users)
- {
- if (users.size() == 0)
- out.println("No users registered");
- else
- {
- int line = 1;
- {
- UserRecord record = entry.getValue();
- line, record.name,
- (record.loggedin.size() > 0) ? "online" : "offline",
- record.credits));
- " ", host));
- ++line;
- }
- }
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- stop();
- }
-
- /*------------------------------------------------------------------------*/
-
- public void printPrompt()
- {
- out.print(">: ");
- out.flush();
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- try
- {
- run(sin);
- }
- {
- err.println("Internal Error: " + e.getMessage());
- }
- {
- /* ignore that exception
- * thread will shutdown and unlock the main thread
- * which will shutdown the application
- */
- }
-
- synchronized(mainLock)
- {
- mainLock.notify();
- }
- }
- }
-
- /*==========================================================================*/
-
- public class CheckFSTask
- implements Runnable
- {
- private FSRecords fileservers;
- private final int fserverTimeout;
-
- CheckFSTask(FSRecords fileservers, int fserverTimeout)
- {
- this.fileservers = fileservers;
- this.fserverTimeout = fserverTimeout;
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- synchronized(fileservers)
- {
- {
- if (entry.getValue().online && entry.getValue().lastUpdate + fserverTimeout < curTime)
- {
- entry.getValue().online = false;
- out.println("Fileserver has gone offline: " + entry.getKey());
- }
- }
- }
- }
- }
-
- /*==========================================================================*/
-
- private static int tcpPort;
- private static int udpPort;
- private static int fserverTimeout;
- private static int checkPeriod;
- private FSRecords fileservers;
- private ArrayList<FSRecord> rfileservers;
- private ArrayList<FSRecord> wfileservers;
- private UserRecords users;
- private ScheduledExecutorService scheduler = null;
- private DatagramChannel dchannel = null;
- private ServerSocketChannel sschannel = null;
-
- private Cipher rsaencrypt, rsadecrypt;
- private Cipher aesencrypt, aesdecrypt;
- private Mac hmac;
-
- /*--------------------------------------------------------------------------*/
-
- {
- fileservers = new FSRecords();
- rfileservers = new ArrayList<FSRecord>();
- wfileservers = new ArrayList<FSRecord>();
- users = new UserRecords();
-
- try
- {
- rsaencrypt = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding");
- rsadecrypt = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding");
- aesencrypt = Cipher.getInstance("AES/CTR/NoPadding");
- aesdecrypt = Cipher.getInstance("AES/CTR/NoPadding");
- }
- {
- bailout("Unable to initialize cipher: " + e.getMessage());
- }
- catch(NoSuchPaddingException e)
- {
- bailout("Unable to initialize cipher: " + e.getMessage());
- }
- }
-
- /*--------------------------------------------------------------------------*/
-
- public static void usage()
- throws Utils.Shutdown
- {
- out.println("Usage: Proxy\n");
-
- // Java is some piece of crap which doesn't allow me to set exitcode w/o
- // using System.exit. Maybe someday Java will be a fully functional
- // programming language, but I wouldn't bet my money
- //System.exit(1);
- throw new Utils.Shutdown("FUCK YOU JAVA");
- }
-
- /*--------------------------------------------------------------------------*/
-
- throws Utils.Shutdown
- {
- err.println("Error: " + error);
- shutdown();
-
- // Java is some piece of crap which doesn't allow me to set exitcode w/o
- // using System.exit. Maybe someday Java will be a fully functional
- // programming language, but I wouldn't bet my money
- //System.exit(2);
- throw new Utils.Shutdown("FUCK YOU JAVA");
- }
-
- /*--------------------------------------------------------------------------*/
-
- {
- if (args.length != 0)
- usage();
- }
-
- /*--------------------------------------------------------------------------*/
-
- public void parseConfig()
- {
- Config config = null;
- try
- {
- config = new Config("proxy");
- }
- {
- bailout("configuration file doesn't exist or isn't readable");
- }
-
- try
- {
- directive = "tcp.port";
- tcpPort = config.getInt(directive);
- if (tcpPort <= 0 || tcpPort > 65536)
- bailout("configuration directive '" + directive + "' must be a valid port number (1 - 65535)");
-
- directive = "udp.port";
- udpPort = config.getInt(directive);
- if (udpPort <= 0 || udpPort > 65536)
- bailout("configuration directive '" + directive + "' must be a valid port number (1 - 65535)");
-
- directive = "fileserver.timeout";
- fserverTimeout = config.getInt(directive);
- if (fserverTimeout <= 0)
- bailout("configuration directive '" + directive + "' must be a positive number");
-
- directive = "fileserver.checkPeriod";
- checkPeriod = config.getInt(directive);
- if (checkPeriod <= 0)
- bailout("configuration directive '" + directive + "' must be a positive number");
-
- directive = "keys.dir";
- keysDir = config.getString(directive);
- if (!dir.isDirectory())
- bailout("configuration directive '" + directive + "' is not a directory");
- if (!dir.canRead())
- bailout("configuration directive '" + directive + "' is not readable");
-
- directive = "key";
- proxyKey = config.getString(directive);
- if (!key.isFile())
- bailout("configuration directive '" + directive + "' is not a file");
- if (!key.canRead())
- bailout("configuration directive '" + directive + "' is not readable");
- {
- @Override
- public char[] getPassword()
- {
- try
- {
- /* reads the password from standard input for decrypting the private key */
- out.println("Enter pass phrase for proxy key:");
- }
- {
- char[] tmp = {};
- return tmp;
- }
- }
- });
- try
- {
- privateKey = keyPair.getPrivate();
- rsadecrypt.init(Cipher.DECRYPT_MODE, privateKey);
- }
- {
- bailout("Error while reading private key of proxy. Maybe wrong pass phrase");
- }
-
- directive = "hmac.key";
- if (!hmackey.isFile())
- bailout("configuration directive '" + directive + "' is not a file");
- if (!hmackey.canRead())
- bailout("configuration directive '" + directive + "' is not readable");
-
- byte[] keybytes = new byte[1024];
- fis.read(keybytes);
- fis.close();
-
- hmac = Mac.getInstance("HmacSHA256");
- hmac.init(new javax.crypto.spec.SecretKeySpec(Hex.decode(keybytes), "HmacSHA256"));
- }
- {
- bailout("unable to read file of directive '" + directive + "'");
- }
- {
- bailout("Error while reading file: " + e.getMessage());
- }
- {
- bailout("invalid key file: " + e.getMessage());
- }
- {
- bailout("configuration directive '" + directive + "' is not set");
- }
- {
- bailout("configuration directive '" + directive + "' must be numeric");
- }
- {
- bailout("Unable to initialize cipher: " + e.getMessage());
- }
- }
-
- /*--------------------------------------------------------------------------*/
-
- public void parseUsers()
- {
- Config config = null;
- try
- {
- config = new Config("user");
- }
- {
- bailout("Unable to read from user properties file: " + e.getMessage());
- }
-
- /* first load the users only */
- {
-
- if (pieces.length == 1)
- {
- int credits = config.getInt(prop);
- if (credits < 0)
- {
- err.println("Property " + prop + " must be positive number. Skipping...");
- continue;
- }
- users.put(user, new UserRecord(user, credits));
- }
- }
-
- /* next load their properties */
- {
-
- if (pieces.length == 1)
- continue;
- else if (pieces.length == 2)
- {
- UserRecord record = users.get(user);
- if (record == null)
- {
- err.println("Can't load user properties for unknown user '" + user + "'. Skipping...");
- continue;
- }
-
- if (pieces[1].equals("password"))
- record.pass = config.getString(prop);
- else
- err.println("Property " + prop + " is unknown. Skipping...");
- }
- else
- err.println("Property " + prop + " is unknown. Skipping...");
- }
- }
-
- /*--------------------------------------------------------------------------*/
-
- public void calcGifford()
- {
- synchronized(fileservers)
- {
- rfileservers.clear();
- wfileservers.clear();
-
- ArrayList<FSRecord> fslist = new ArrayList<FSRecord>();
- for(FSRecord record : fileservers.values())
- {
- if (!record.online)
- continue;
- fslist.add(record);
- }
- if (fslist.size() == 0)
- return;
-
- int numread = fslist.size() - numwrite + 1;
-
- for(int i = 0; i < numread; ++i)
- rfileservers.add(fslist.get(i));
- for(int i = 0; i < numwrite; ++i)
- wfileservers.add(fslist.get(i));
- }
- }
-
- /*--------------------------------------------------------------------------*/
-
- public void shutdown()
- {
- try
- {
- if (scheduler != null)
- {
- scheduler.shutdownNow();
- }
- }
- {}
-
- try
- {
- if (dchannel != null)
- dchannel.close();
- }
- {}
-
- try
- {
- if (tUDPSocketReader != null)
- tUDPSocketReader.join();
- }
- {}
-
- try
- {
- if (sschannel != null)
- sschannel.close();
- }
- {}
-
- try
- {
- if (tTCPSocketReader != null)
- tTCPSocketReader.join();
- }
- {}
-
- try
- {
- if (tInteractive != null)
- {
- tInteractive.interrupt();
- tInteractive.join();
- }
- }
- {}
-
- try
- {
- if (stdin != null)
- stdin.close();
- }
- {}
- }
-
- /*--------------------------------------------------------------------------*/
-
- {
- parseArgs(args);
- parseConfig();
-
- synchronized(users)
- {
- parseUsers();
- out.println("Users loaded successfully: " + users.size() + " users loaded");
- }
-
- synchronized(mainLock)
- {
- scheduler = Executors.newScheduledThreadPool(1);
- ScheduledFuture<?> checkFSTimer = scheduler.scheduleAtFixedRate(
- new CheckFSTask(fileservers, fserverTimeout),
- 0, checkPeriod, TimeUnit.MILLISECONDS);
-
- try
- {
- dchannel = DatagramChannel.open();
- dchannel.socket().bind(new InetSocketAddress(udpPort));
- mainLock));
- tUDPSocketReader.start();
- out.println("Listening on udp:/" + dchannel.socket().getLocalSocketAddress());
- }
- {
- bailout("Unable to create UDP Socket: " + e.getMessage());
- }
-
- try
- {
- sschannel = ServerSocketChannel.open();
- sschannel.socket().bind(new InetSocketAddress(tcpPort));
- fileservers, users, mainLock));
- tTCPSocketReader.start();
- out.println("Listening on tcp:/" + sschannel.socket().getLocalSocketAddress());
- }
- {
- bailout("Unable to create TCP Socket: " + e.getMessage());
- }
-
- try
- {
- tInteractive.start();
- }
- {
- bailout("Unable to setup interactive command handler");
- }
-
- out.println("Proxy startup successful!");
- try
- {
- mainLock.wait();
- }
- {
- /* if we get interrupted -> ignore */
- }
-
- try
- {
- /* let the threads shutdown */
- }
- {}
- }
-
- if (tUDPSocketReader != null && !tUDPSocketReader.isAlive())
- bailout("Listening UDP socket closed unexpected. Terminating...");
- if (tTCPSocketReader != null && !tTCPSocketReader.isAlive())
- bailout("Listening TCP socket closed unexpected. Terminating...");
-
- shutdown();
- }
-
- /*--------------------------------------------------------------------------*/
-
- {
- try
- {
- proxy.run(args);
- }
- catch(Utils.Shutdown e)
- {}
- }
- }
-