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 java.nio.ByteBuffer;
- import java.nio.channels.ServerSocketChannel;
- import java.nio.channels.SocketChannel;
-
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.MissingResourceException;
- import java.util.concurrent.Executors;
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.ScheduledFuture;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.ScheduledExecutorService;
-
- import javax.crypto.Mac;
- import java.security.InvalidKeyException;
- import java.security.NoSuchAlgorithmException;
- import org.bouncycastle.util.encoders.Hex;
- import org.bouncycastle.util.encoders.Base64;
-
- import java.net.*;
- import java.io.*;
-
- /*
- * Fileserver 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 Fileserver
- {
- public class ProxyConnection
- extends CommandNetwork
- implements Runnable
- {
- private final SocketChannel sock;
- private final Utils.EncObjectInputStream oin;
- private final Utils.EncObjectOutputStream oout;
-
- {
- this.sock = sock;
- this.oout = new Utils.EncObjectOutputStream(sock.socket().getOutputStream());
- this.sharedFolder = sharedFolder;
- this.oout.setMAC(hmac);
- this.oin.setMAC(hmac);
-
- setOneCommandMode(true);
- cmdHandler.register("!list", this, "cmdList");
- cmdHandler.register("!download", this, "cmdDownload");
- cmdHandler.register("!upload2", this, "cmdUpload2");
- cmdHandler.register("unknown", this, "cmdUnknown");
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (args.length != 0)
- {
- err.println("Error: Invalid " + cmd + "-command paket from proxy. Ignoring...");
- return;
- }
-
- oout.writeLine(cmd + " " + filelist.size());
- oout.writeLine(file.getKey() + " " + file.getValue().toString());
- oout.flush();
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (args.length < 2 || args[1].length() <= 0)
- {
- err.println("Error: Invalid " + cmd + "-command paket from proxy. Ignoring...");
- return;
- }
-
- long credits;
- if ((credits = Utils.parseHeaderNum(args, 1)) < 0)
- return;
-
- if (!file.exists() || !file.getParent().equals(sharedFolder))
- {
- Utils.sendError(oout, "File '" + args[0] + "' does not exist");
- return;
- }
- if (!file.isFile())
- {
- Utils.sendError(oout, "File '" + args[0] + "' is not a file");
- return;
- }
- if (!file.canRead())
- {
- Utils.sendError(oout, "File '" + args[0] + "' is not readable");
- return;
- }
-
- long filesize = file.length();
- if (credits < filesize)
- {
- Utils.sendOutput(oout, "You don't have enough credits");
- return;
- }
-
- try
- {
-
- oout.writeLine(cmd + " " + file.getName() + " " + filesize);
-
- byte[] buffer = new byte[8 * 1024];
- int toread = buffer.length;
- while(filesize > 0)
- {
- if (filesize < toread)
- toread = (int) filesize;
- int count = fin.read(buffer, 0, toread);
- if (count == -1)
- hmac.update(buffer, 0, count);
- oout.write(buffer, 0, count);
- filesize -= count;
- }
- oout.write(hmac.doFinal());
- }
- {
- Utils.sendError(oout, "File '" + args[0] + "' is not readable");
- }
- {
- err.println("Error during file transfer: " + e.getMessage());
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (args.length < 3 || args[1].length() <= 0)
- {
- err.println("Error: Invalid " + cmd + "-command paket from proxy. Ignoring...");
- return;
- }
-
- long filesize;
- if ((filesize = Utils.parseHeaderNum(args, 1)) < 0)
- return;
-
- long version;
- if ((version = Utils.parseHeaderNum(args, 2)) < 0)
- return;
-
- file.delete();
- try
- {
- try
- {
- }
- {
- err.println("Error: Unable to write to file '" + file + "': " + e.getMessage());
- }
-
- byte[] buffer = new byte[8 * 1024];
- int toread = buffer.length;
- while(filesize > 0)
- {
- if (filesize < toread)
- toread = (int) filesize;
- int count = oin.read(buffer, 0, toread);
- if (count == -1)
- if (fout != null)
- {
- hmac.update(buffer, 0, count);
- fout.write(buffer, 0, count);
- }
- filesize -= count;
- }
- byte[] hasht = new byte[hmac.getMacLength()];
- oin.readFully(hasht);
-
- if (fout != null)
- {
- byte[] hashc = hmac.doFinal();
- //TODO
- {
- err.println("HASH NOT EQUAL: File may be corrupt");
- filelist.remove(file.getName());
- }
- else
- filelist.put(file.getName(), version);
- }
- }
- {
- err.println("Error: Error during file transfer: " + e.getMessage());
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- err.println("Error: Unknown data from proxy: " + cmd + " "
- Utils.sendError(oout, "Unknown command");
- }
-
- /*------------------------------------------------------------------------*/
-
- public void hashError()
- throws IOException
- {
- oout.writeLine("!hasherr");
- }
-
- /*------------------------------------------------------------------------*/
-
- public void shutdown()
- {
- try
- {
- oout.flush();
- }
- {}
-
- try
- {
- oin.close();
- }
- {}
-
- try
- {
- oout.close();
- }
- {}
-
- try
- {
- if (sock.isOpen())
- sock.close();
- }
- {}
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- try
- {
- + sock.socket().getInetAddress() + ":" + sock.socket().getPort());
- try
- {
- run(oin);
- }
- catch(Utils.HashError e)
- {
- hashError();
- }
- oout.flush();
- }
- {
- err.println("Internal Error: " + e.getMessage());
- }
- {
- /* ignore that exception
- * it's usually a closed connection from client so
- * we can't do anything about it anyway
- */
- }
-
- shutdown();
- }
- }
-
- /*==========================================================================*/
-
- public class TCPSocketReader
- implements Runnable
- {
- private final ServerSocketChannel sschannel;
- private final ExecutorService pool;
-
- TCPSocketReader(ServerSocketChannel sschannel, String sharedFolder,
- Object mainLock)
- {
- this.sschannel = sschannel;
- this.sharedFolder = sharedFolder;
- this.mainLock = mainLock;
- this.pool = Executors.newCachedThreadPool();
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- try
- {
- while(true)
- pool.execute(new ProxyConnection(sschannel.accept(), sharedFolder));
- }
- {
- 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 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: Proxy 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("!exit", this, "cmdExit");
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- err.println("Unknown command: " + cmd + " "
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- 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 PingTask
- implements Runnable
- {
-
- {
- this.sock = sock;
- this.packet = packet;
- this.mainLock = mainLock;
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- try
- {
- sock.send(packet);
- }
- {
- err.println("Error while sending UDP ping packet: " + e.getMessage()
- + ". Terminating...");
- synchronized(mainLock)
- {
- mainLock.notify();
- }
- }
- }
- }
-
- /*==========================================================================*/
-
- private static int tcpPort;
- private static int proxyUDPPort;
- private static int alivePeriod;
- private ScheduledExecutorService scheduler = null;
- private ServerSocketChannel sschannel = null;
-
- private Mac hmac;
-
- /*--------------------------------------------------------------------------*/
-
- public Fileserver()
- {
- }
-
- /*--------------------------------------------------------------------------*/
-
- public static void usage()
- throws Utils.Shutdown
- {
- out.println("Usage: Fileserver sharedFolder tcpPort\n");
- out.println("sharedFolder\t...the directory that contains all the files clients can download or have uploaded");
- out.println("tcpPort\t\t...the port to be used for instantiating a ServerSocket");
-
- // 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 != 2)
- usage();
-
- sharedFolder = args[0];
- if (!sharedir.isDirectory())
- bailout("sharedFolder '" + sharedFolder + "' is not a directory");
- if (!sharedir.canRead())
- bailout("sharedFolder '" + sharedFolder + "' is not readable");
-
- try
- {
- if (tcpPort <= 0 || tcpPort > 65536)
- bailout("tcpPort must be a valid port number (1 - 65535)");
- }
- {
- bailout("tcpPort must be numeric");
- }
- }
-
- /*--------------------------------------------------------------------------*/
-
- public void parseConfig()
- {
- Config config = null;
- try
- {
- config = new Config("fileserver");
- }
- {
- bailout("configuration file doesn't exist or isn't readable");
- }
-
- try
- {
- directive = "proxy.host";
- proxyHost = config.getString(directive);
- if (proxyHost.length() == 0)
- bailout("configuration directive '" + directive + "' is empty or invalid");
-
- directive = "proxy.udp.port";
- proxyUDPPort = config.getInt(directive);
- if (proxyUDPPort <= 0 || proxyUDPPort > 65536)
- bailout("configuration directive '" + directive + "' must be a valid port number (1 - 65535)");
-
- directive = "fileserver.alive";
- alivePeriod = config.getInt(directive);
- if (alivePeriod <= 0)
- bailout("configuration directive '" + directive + "' must be a positive number");
-
- 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("configuration directive '" + directive + "' is not set");
- }
- {
- bailout("configuration directive '" + directive + "' must be numeric");
- }
- {
- bailout("unable to read file of directive '" + directive + "'");
- }
- {
- bailout("Unable to initialize cipher: " + e.getMessage());
- }
- {
- bailout("invalid key file: " + e.getMessage());
- }
- {
- bailout("Error while reading file: " + e.getMessage());
- }
- }
-
- /*--------------------------------------------------------------------------*/
-
- public void readFiles()
- {
- {
- {
- return (file.isFile() && file.canRead());
- }
- };
- {
- if (!filelist.containsKey(filename))
- }
- }
-
- /*--------------------------------------------------------------------------*/
-
- public void shutdown()
- {
- try
- {
- if (scheduler != null)
- {
- scheduler.shutdownNow();
- }
- }
- {}
-
- if (dsock != null)
- dsock.close();
-
- 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();
- readFiles();
-
- synchronized(mainLock)
- {
- try
- {
- msg.getBytes().length, proxyaddr, proxyUDPPort);
-
- scheduler = Executors.newScheduledThreadPool(1);
- ScheduledFuture<?> pingTimer = scheduler.scheduleAtFixedRate(
- new PingTask(dsock, dpacket, mainLock),
- 0, alivePeriod, TimeUnit.MILLISECONDS);
- }
- {
- bailout("Unable to create UDP Socket: " + e.getMessage());
- }
- {
- bailout("Unable to resolve hostname: " + e.getMessage());
- }
-
- try
- {
- sschannel = ServerSocketChannel.open();
- sschannel.socket().bind(new InetSocketAddress(tcpPort));
- sharedFolder, 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("Fileserver startup successful!");
- try
- {
- mainLock.wait();
- }
- {
- /* if we get interrupted -> ignore */
- }
-
- try
- {
- /* let the threads shutdown */
- }
- {}
- }
-
- if (tTCPSocketReader != null && !tTCPSocketReader.isAlive())
- bailout("Listening TCP socket closed unexpected. Terminating...");
-
- shutdown();
- }
-
- /*--------------------------------------------------------------------------*/
-
- {
- try
- {
- Fileserver fserver = new Fileserver();
- fserver.run(args);
- }
- catch(Utils.Shutdown e)
- {}
- }
- }
-