Download | Plain Text | No Line Numbers


  1. /*
  2.  * Copyright (c) 2010, Manuel Mausz. All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * - Redistributions of source code must retain the above copyright
  9.  * notice, this list of conditions and the following disclaimer.
  10.  *
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  * notice, this list of conditions and the following disclaimer in the
  13.  * documentation and/or other materials provided with the distribution.
  14.  *
  15.  * - The names of the authors may not be used to endorse or promote products
  16.  * derived from this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  19.  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  20.  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  21.  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  22.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  23.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  24.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  25.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  26.  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  27.  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  28.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29.  */
  30.  
  31. import static java.lang.System.err;
  32. import static java.lang.System.out;
  33.  
  34. import javax.crypto.*;
  35. import java.security.*;
  36. import org.bouncycastle.openssl.PEMReader;
  37. import org.bouncycastle.util.encoders.Base64;
  38. import org.bouncycastle.openssl.PasswordFinder;
  39. import org.bouncycastle.util.encoders.Hex;
  40.  
  41. import java.nio.ByteBuffer;
  42. import java.nio.channels.ServerSocketChannel;
  43. import java.nio.channels.SocketChannel;
  44. import java.nio.channels.DatagramChannel;
  45.  
  46. import java.util.concurrent.ScheduledExecutorService;
  47. import java.util.concurrent.ScheduledFuture;
  48. import java.util.concurrent.ExecutorService;
  49. import java.util.concurrent.Executors;
  50. import java.util.concurrent.TimeUnit;
  51. import java.util.MissingResourceException;
  52. import java.util.Collections;
  53. import java.util.Enumeration;
  54. import java.util.ArrayList;
  55. import java.util.Calendar;
  56. import java.util.Iterator;
  57. import java.util.HashMap;
  58. import java.util.Arrays;
  59. import java.util.Map;
  60. import java.net.*;
  61. import java.io.*;
  62.  
  63. /*
  64.  * Proxy implementation for Lab#1 of DSLab WS10
  65.  * See angabe.pdf for details
  66.  *
  67.  * This code is not documented at all. This is volitional
  68.  *
  69.  * @author Manuel Mausz (0728348)
  70.  */
  71. public class Proxy
  72. {
  73. public class FSRecord
  74. implements Comparable<FSRecord>
  75. {
  76. public final String host;
  77. public final int port;
  78. public int usage;
  79. public boolean online;
  80. public long lastUpdate;
  81.  
  82. FSRecord(String host, int port)
  83. {
  84. this.host = host;
  85. this.port = port;
  86. usage = 0;
  87. online = true;
  88. lastUpdate = Calendar.getInstance().getTimeInMillis();
  89. }
  90.  
  91. public void ping()
  92. {
  93. online = true;
  94. lastUpdate = Calendar.getInstance().getTimeInMillis();
  95. }
  96.  
  97. public boolean equals(FSRecord o)
  98. {
  99. return online == o.online && usage == o.usage;
  100. }
  101.  
  102. public int compareTo(FSRecord o)
  103. {
  104. return usage - o.usage;
  105. }
  106. }
  107.  
  108. public class FSRecords
  109. extends HashMap<String, FSRecord>
  110. {}
  111.  
  112. /*==========================================================================*/
  113.  
  114. public class UserRecord
  115. {
  116. public final String name;
  117. public String pass;
  118. public int credits;
  119. public ArrayList<String> loggedin;
  120.  
  121. UserRecord(String name, int credits)
  122. {
  123. this.name = name;
  124. this.pass = null;
  125. this.credits = credits;
  126. loggedin = new ArrayList<String>();
  127. }
  128. }
  129.  
  130. public class UserRecords
  131. extends HashMap<String, UserRecord>
  132. {}
  133.  
  134. /*==========================================================================*/
  135.  
  136.  
  137. public class ProxyConnection
  138. implements Runnable
  139. {
  140. private final String host;
  141. private final int port;
  142. private FSRecords fileservers;
  143.  
  144. ProxyConnection(String host, int port, FSRecords fileservers)
  145. {
  146. this.host = host;
  147. this.port = port;
  148. this.fileservers = fileservers;
  149. }
  150.  
  151. /*------------------------------------------------------------------------*/
  152.  
  153. public void run()
  154. {
  155. String key = host + ":" + port;
  156. synchronized(fileservers)
  157. {
  158. FSRecord record = fileservers.get(key);
  159. if (record == null)
  160. {
  161. fileservers.put(key, new FSRecord(host, port));
  162. out.println("New fileserver registered: " + key);
  163. }
  164. else
  165. {
  166. if (!record.online)
  167. out.println("Fileserver is online again: " + key);
  168. record.ping();
  169. }
  170. }
  171. }
  172. }
  173.  
  174. /*==========================================================================*/
  175.  
  176. public class UDPSocketReader
  177. implements Runnable
  178. {
  179. private final DatagramChannel dchannel;
  180. private FSRecords fileservers;
  181. private final Object mainLock;
  182. private final ExecutorService pool;
  183.  
  184. UDPSocketReader(DatagramChannel dchannel, FSRecords fileservers,
  185. Object mainLock)
  186. {
  187. this.dchannel = dchannel;
  188. this.fileservers = fileservers;
  189. this.mainLock = mainLock;
  190. this.pool = Executors.newCachedThreadPool();
  191. }
  192.  
  193. /*------------------------------------------------------------------------*/
  194.  
  195. public void run()
  196. {
  197. try
  198. {
  199. String tmp = "!alive 12345";
  200. ByteBuffer buffer = ByteBuffer.allocate(tmp.getBytes().length);
  201. while(true)
  202. {
  203. buffer.clear();
  204. InetSocketAddress proxyaddr = (InetSocketAddress) dchannel.receive(buffer);
  205. String msg = new String(buffer.array());
  206. if (msg.length() != tmp.length())
  207. continue;
  208. assert msg.matches("!alive 1[0-9]{4}");
  209. try
  210. {
  211. pool.execute(new ProxyConnection(proxyaddr.getHostName(),
  212. Integer.parseInt(msg.substring("!alive ".length())), fileservers));
  213. }
  214. catch(NumberFormatException e)
  215. {
  216. /* simple ignore that packet */
  217. }
  218. }
  219. }
  220. catch(IOException e)
  221. {
  222. /* ignore that exception
  223.   * thread will shutdown and unlock the main thread
  224.   * which will shutdown the application
  225.   */
  226. }
  227.  
  228. pool.shutdown();
  229. try
  230. {
  231. if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS))
  232. out.println("Trying to shutdown the UDP Proxy connections. This may take up to 15 seconds...");
  233. if (!pool.awaitTermination(5, TimeUnit.SECONDS))
  234. {
  235. pool.shutdownNow();
  236. if (!pool.awaitTermination(5, TimeUnit.SECONDS))
  237. err.println("Error: UDP Proxy connections did not terminate. You may have to kill that appplication.");
  238. }
  239. }
  240. catch(InterruptedException e)
  241. {
  242. pool.shutdownNow();
  243. }
  244.  
  245. synchronized(mainLock)
  246. {
  247. mainLock.notify();
  248. }
  249. }
  250. }
  251.  
  252. /*==========================================================================*/
  253.  
  254. public class ClientConnection
  255. extends CommandNetwork
  256. implements Runnable
  257. {
  258. private final SocketChannel sock;
  259. private final FSRecords fileservers;
  260. private final UserRecords users;
  261. private final Utils.EncObjectInputStream clin;
  262. private final Utils.EncObjectOutputStream clout;
  263. private UserRecord user = null;
  264. private final String clientaddr;
  265. HashMap<String, Long> filelist = null;
  266.  
  267. private CommandNetwork fscmd;
  268. private ArrayList<SocketChannel> fsschannel;
  269. private ArrayList<Utils.EncObjectInputStream> fsin;
  270. private ArrayList<Utils.EncObjectOutputStream> fsout;
  271. private ArrayList<FSRecord> fileserver;
  272. private int curconn;
  273.  
  274. private String mychallenge;
  275.  
  276. ClientConnection(SocketChannel sock, FSRecords fileservers,
  277. UserRecords users)
  278. throws NoSuchMethodException, IOException
  279. {
  280. this.sock = sock;
  281. this.clout = new Utils.EncObjectOutputStream(sock.socket().getOutputStream());
  282. this.clin = new Utils.EncObjectInputStream(new BufferedInputStream(sock.socket().getInputStream()));
  283. this.fileservers = fileservers;
  284. this.users = users;
  285. this.clientaddr = "tcp:/" + sock.socket().getInetAddress() + ":" + sock.socket().getPort();
  286.  
  287. fsschannel = new ArrayList<SocketChannel>();
  288. fsin = new ArrayList<Utils.EncObjectInputStream>();
  289. fsout = new ArrayList<Utils.EncObjectOutputStream>();
  290. fileserver = new ArrayList<FSRecord>();
  291.  
  292. clin.setCipher(rsadecrypt);
  293.  
  294. cmdHandler.register("!login", this, "cmdLogin");
  295. cmdHandler.register("!buy", this, "cmdBuy");
  296. cmdHandler.register("!credits", this, "cmdCredits");
  297. cmdHandler.register("!list", this, "cmdList");
  298. cmdHandler.register("!download", this, "cmdDownload");
  299. cmdHandler.register("!upload", this, "cmdUpload");
  300. cmdHandler.register("!upload2", this, "cmdUpload2");
  301. cmdHandler.register("unknown", this, "cmdUnknown");
  302.  
  303. fscmd = new CommandNetwork();
  304. fscmd.setOneCommandMode(true);
  305. fscmd.cmdHandler.register("!error", this, "cmdFSRelayOutput");
  306. fscmd.cmdHandler.register("!output", this, "cmdFSRelayOutput");
  307. fscmd.cmdHandler.register("!download", this, "cmdFSDownload");
  308. fscmd.cmdHandler.register("!list", this, "cmdFSList");
  309. fscmd.cmdHandler.register("!hasherr", this, "cmdFSHashError");
  310. fscmd.cmdHandler.register("unknown", this, "cmdFSRelayOutput");
  311. }
  312.  
  313. /*------------------------------------------------------------------------*/
  314.  
  315. public boolean checkLogin()
  316. throws IOException
  317. {
  318. if (user == null)
  319. {
  320. Utils.sendError(clout, "Not logged in");
  321. return false;
  322. }
  323. return true;
  324. }
  325.  
  326. /*------------------------------------------------------------------------*/
  327.  
  328. public void cmdLogin(String cmd, String[] args)
  329. throws IOException
  330. {
  331. if (user != null)
  332. {
  333. Utils.sendError(clout, "Already logged in");
  334. return;
  335. }
  336.  
  337. if (args.length != 2)
  338. {
  339. Utils.sendError(clout, "Invalid Syntax: !login <username>");
  340. return;
  341. }
  342.  
  343. String firstMessage = cmd + " " + Utils.join(Arrays.asList(args), " ");
  344. assert firstMessage.matches("!login \\w+ [" + B64 + "]{43}=") : "1st message";
  345.  
  346. synchronized(users)
  347. {
  348. UserRecord record = users.get(args[0]);
  349. if (record == null)
  350. {
  351. Utils.sendError(clout, "Invalid username");
  352. return;
  353. }
  354.  
  355. user = record;
  356. }
  357.  
  358. /* read and init users public key */
  359. File pemfile = new File(keysDir, args[0] + ".pub.pem");
  360. if (!pemfile.isFile())
  361. {
  362. Utils.sendError(clout, "No public keyfile");
  363. return;
  364. }
  365. if (!pemfile.canRead())
  366. {
  367. Utils.sendError(clout, "Your public keyfile is not readable");
  368. return;
  369. }
  370. try
  371. {
  372. PEMReader in = new PEMReader(new FileReader(pemfile));
  373. PublicKey publicKey = (PublicKey) in.readObject();
  374. rsaencrypt.init(Cipher.ENCRYPT_MODE, publicKey);
  375. clout.setCipher(rsaencrypt);
  376. }
  377. catch(FileNotFoundException e)
  378. {
  379. Utils.sendError(clout, "Your public keyfile is not readable");
  380. return;
  381. }
  382. catch(IOException e)
  383. {
  384. Utils.sendError(clout, "While reading users public key");
  385. return;
  386. }
  387. catch(InvalidKeyException e)
  388. {
  389. Utils.sendError(clout, "invalid public key file: " + e.getMessage());
  390. return;
  391. }
  392.  
  393. String clichallenge = args[1];
  394.  
  395. /* generates a 32 byte secure random number */
  396. SecureRandom secureRandom = new SecureRandom();
  397. byte[] tmp = new byte[32];
  398. secureRandom.nextBytes(tmp);
  399. mychallenge = new String(Base64.encode(tmp));
  400.  
  401. SecretKey seckey = null;
  402. javax.crypto.spec.IvParameterSpec iv = null;
  403. try
  404. {
  405. /* generate aes key */
  406. KeyGenerator generator = KeyGenerator.getInstance("AES");
  407. generator.init(256);
  408. seckey = generator.generateKey();
  409.  
  410. /* generate iv */
  411. byte[] tmpiv = new byte[16];
  412. secureRandom.nextBytes(tmpiv);
  413. iv = new javax.crypto.spec.IvParameterSpec(tmpiv);
  414.  
  415. /* init aes */
  416. aesencrypt.init(Cipher.ENCRYPT_MODE, seckey, iv);
  417. aesdecrypt.init(Cipher.DECRYPT_MODE, seckey, iv);
  418. }
  419. catch(NoSuchAlgorithmException e)
  420. {
  421. err.println("Error: Unable to generate AES key: " + e.getMessage());
  422. return;
  423. }
  424. catch(InvalidKeyException e)
  425. {
  426. err.println("Error: invalid AES key: " + e.getMessage());
  427. return;
  428. }
  429. catch(InvalidAlgorithmParameterException e)
  430. {
  431. err.println("Error: invalid AES parameters: " + e.getMessage());
  432. return;
  433. }
  434.  
  435. ArrayList<String> msg = new ArrayList<String>();
  436. msg.add("!ok");
  437. msg.add(clichallenge);
  438. msg.add(mychallenge);
  439. msg.add(new String(Base64.encode(seckey.getEncoded())));
  440. msg.add(new String(Base64.encode(iv.getIV())));
  441.  
  442. String secondMessage = Utils.join(msg, " ");
  443. assert secondMessage.matches("!ok [" + B64 + "]{43}= [" + B64 + "]{43}= [" + B64 + "]{43}= [" + B64 + "]{22}==") : "2nd message";
  444. clout.writeLine(secondMessage);
  445. clout.flush();
  446.  
  447. clout.setCipher(aesencrypt);
  448. clin.setCipher(aesdecrypt);
  449. }
  450.  
  451. /*------------------------------------------------------------------------*/
  452.  
  453. public void cmdBuy(String cmd, String[] args)
  454. throws IOException
  455. {
  456. if (!checkLogin())
  457. return;
  458. if (args.length != 1)
  459. {
  460. Utils.sendError(clout, "Invalid Syntax: !buy <credits>");
  461. return;
  462. }
  463.  
  464. int add = 0;
  465. try
  466. {
  467. add = Integer.parseInt(args[0]);
  468. if (add <= 0)
  469. throw new NumberFormatException("");
  470. }
  471. catch(NumberFormatException e)
  472. {
  473. Utils.sendError(clout, "Credits must be numberic and positive");
  474. return;
  475. }
  476.  
  477. synchronized(users)
  478. {
  479. if (user.credits > Integer.MAX_VALUE - add)
  480. Utils.sendError(clout, "You can't buy that much/more credits");
  481. else
  482. {
  483. user.credits += add;
  484. Utils.sendOutput(clout, "You now have " + user.credits + " credits");
  485. }
  486. }
  487. }
  488.  
  489. /*------------------------------------------------------------------------*/
  490.  
  491. public void cmdCredits(String cmd, String[] args)
  492. throws IOException
  493. {
  494. if (!checkLogin())
  495. return;
  496. if (args.length != 0)
  497. {
  498. Utils.sendError(clout, "Invalid Syntax: !credits");
  499. return;
  500. }
  501.  
  502. synchronized(users)
  503. {
  504. Utils.sendOutput(clout, "You have " + user.credits + " credits left");
  505. }
  506. }
  507.  
  508. /*------------------------------------------------------------------------*/
  509.  
  510. public int connectFileserver(FSRecord fileserver)
  511. throws IOException
  512. {
  513. if (fileserver == null || !fileserver.online)
  514. {
  515. Utils.sendError(clout, "Unable to execute command. Fileserver not online");
  516. return -1;
  517. }
  518.  
  519. synchronized(fsschannel)
  520. {
  521. int conn = fsschannel.size();
  522. try
  523. {
  524. fsschannel.add(conn, SocketChannel.open(new InetSocketAddress(fileserver.host,
  525. fileserver.port)));
  526. fsin.add(conn, new Utils.EncObjectInputStream(new BufferedInputStream(
  527. fsschannel.get(conn).socket().getInputStream())));
  528. fsout.add(conn, new Utils.EncObjectOutputStream(fsschannel.get(conn).socket().getOutputStream()));
  529. this.fileserver.add(conn, fileserver);
  530.  
  531. fsin.get(conn).setMAC(hmac);
  532. fsout.get(conn).setMAC(hmac);
  533. }
  534. catch(IOException e)
  535. {
  536. err.println("Error: Unable to connect to fileserver: " + e.getMessage());
  537. Utils.sendError(clout, "Unable to connect to fileserver");
  538.  
  539. synchronized(fileservers)
  540. {
  541. fileserver.online = false;
  542. out.println("Fileserver marked as offline: " + fileserver.host + ":" + fileserver.port);
  543. }
  544.  
  545. disconnectFileserver(conn);
  546. return -1;
  547. }
  548. return conn;
  549. }
  550. }
  551.  
  552. /*------------------------------------------------------------------------*/
  553.  
  554. public void disconnectFileserver(int conn)
  555. {
  556. if (conn < 0)
  557. return;
  558.  
  559. synchronized(fsschannel)
  560. {
  561. try
  562. {
  563. if (fsout.get(conn) != null)
  564. fsout.get(conn).flush();
  565. }
  566. catch(IndexOutOfBoundsException e)
  567. {}
  568. catch(IOException e)
  569. {}
  570.  
  571. try
  572. {
  573. if (fsin.get(conn) != null)
  574. fsin.get(conn).close();
  575. fsin.set(conn, null);
  576. for(int i = fsin.size() - 1; i >= 0; --i)
  577. {
  578. if (fsin.get(i) != null)
  579. break;
  580. fsin.remove(i);
  581. }
  582. }
  583. catch(IndexOutOfBoundsException e)
  584. {}
  585. catch(IOException e)
  586. {}
  587.  
  588. try
  589. {
  590. if (fsout.get(conn) != null)
  591. fsout.get(conn).close();
  592. fsout.set(conn, null);
  593. for(int i = fsout.size() - 1; i >= 0; --i)
  594. {
  595. if (fsout.get(i) != null)
  596. break;
  597. fsout.remove(i);
  598. }
  599. }
  600. catch(IndexOutOfBoundsException e)
  601. {}
  602. catch(IOException e)
  603. {}
  604.  
  605. try
  606. {
  607. if (fsschannel.get(conn) != null)
  608. fsschannel.get(conn).close();
  609. fsschannel.set(conn, null);
  610. for(int i = fsschannel.size() - 1; i >= 0; --i)
  611. {
  612. if (fsschannel.get(i) != null)
  613. break;
  614. fsschannel.remove(i);
  615. }
  616. }
  617. catch(IndexOutOfBoundsException e)
  618. {}
  619. catch(IOException e)
  620. {}
  621.  
  622. try
  623. {
  624. fileserver.set(conn, null);
  625. for(int i = fileserver.size() - 1; i >= 0; --i)
  626. {
  627. if (fileserver.get(i) != null)
  628. break;
  629. fileserver.remove(i);
  630. }
  631. }
  632. catch(IndexOutOfBoundsException e)
  633. {}
  634. }
  635. }
  636.  
  637. /*------------------------------------------------------------------------*/
  638.  
  639. public void cmdList(String cmd, String[] args)
  640. throws IOException
  641. {
  642. if (!checkLogin())
  643. return;
  644. if (args.length != 0)
  645. {
  646. Utils.sendError(clout, "Invalid Syntax: !list");
  647. return;
  648. }
  649.  
  650. filelist = new HashMap<String, Long>();
  651. synchronized(fileservers)
  652. {
  653. for(FSRecord fileserver : fileservers.values())
  654. {
  655. if (!fileserver.online)
  656. continue;
  657. int conn = -1;
  658. try
  659. {
  660. if ((conn = connectFileserver(fileserver)) < 0)
  661. return;
  662. fsout.get(conn).writeLine(cmd + " " + Utils.join(Arrays.asList(args), " "));
  663. fsout.get(conn).flush();
  664. curconn = conn;
  665. fscmd.run(fsin.get(conn));
  666. }
  667. catch(Utils.HashError e)
  668. {
  669. fileserver.online = false;
  670. }
  671. catch(IOException e)
  672. {
  673. fileserver.online = false;
  674. Utils.sendError(clout, "Connection to fileserver " + fileserver.host + ":" + fileserver.port + " terminated unexpected");
  675. }
  676. disconnectFileserver(conn);
  677. }
  678. }
  679.  
  680. if (filelist.size() == 0)
  681. Utils.sendOutput(clout, "No files available");
  682. else
  683. {
  684. ArrayList<String> tmp = new ArrayList<String>();
  685. for(Map.Entry<String, Long> file : filelist.entrySet())
  686. tmp.add(file.getKey() + " v=" + file.getValue());
  687. Utils.sendOutput(clout, tmp.toArray(new String[tmp.size()]));
  688. }
  689.  
  690. clout.flush();
  691. }
  692.  
  693. /*------------------------------------------------------------------------*/
  694.  
  695. public void cmdDownload(String cmd, String[] args)
  696. throws IOException
  697. {
  698. if (!checkLogin())
  699. return;
  700. if (args.length != 1)
  701. {
  702. Utils.sendError(clout, "Invalid Syntax: !download <filename>");
  703. return;
  704. }
  705.  
  706. FSRecord curfs = null;
  707. long curversion = -1;
  708. synchronized(fileservers)
  709. {
  710. calcGifford();
  711. for(FSRecord fileserver : rfileservers)
  712. {
  713. filelist = new HashMap<String, Long>();
  714. int conn = -1;
  715. try
  716. {
  717. if ((conn = connectFileserver(fileserver)) < 0)
  718. return;
  719. fsout.get(conn).writeLine("!list");
  720. fsout.get(conn).flush();
  721. curconn = conn;
  722. fscmd.run(fsin.get(conn));
  723. }
  724. catch(Utils.HashError e)
  725. {
  726. fileserver.online = false;
  727. }
  728. catch(IOException e)
  729. {
  730. fileserver.online = false;
  731. Utils.sendError(clout, "Connection to fileserver " + fileserver.host + ":" + fileserver.port + " terminated unexpected");
  732. }
  733. disconnectFileserver(conn);
  734.  
  735. Long version = filelist.get(args[0]);
  736. if (version != null && version > curversion)
  737. {
  738. curfs = fileserver;
  739. curversion = version;
  740. }
  741. }
  742. }
  743.  
  744. if (curfs == null)
  745. {
  746. Utils.sendError(clout, "File not found or no fileservers online");
  747. return;
  748. }
  749.  
  750. int conn = -1;
  751. try
  752. {
  753. if ((conn = connectFileserver(curfs)) < 0)
  754. return;
  755. synchronized(users)
  756. {
  757. fsout.get(conn).writeLine(cmd + " " + Utils.join(Arrays.asList(args), " ") + " " + user.credits);
  758. }
  759. fsout.get(conn).flush();
  760. curconn = conn;
  761. fscmd.run(fsin.get(conn));
  762. }
  763. catch(Utils.HashError e)
  764. {
  765. curfs.online = false;
  766. }
  767. catch(IOException e)
  768. {
  769. Utils.sendError(clout, "Connection to fileserver " + fileserver.get(conn).host + ":" + fileserver.get(conn).port + "terminated unexpected");
  770. }
  771. clout.flush();
  772. disconnectFileserver(conn);
  773. }
  774.  
  775. /*------------------------------------------------------------------------*/
  776.  
  777. public void cmdUpload(String cmd, String[] args)
  778. throws IOException
  779. {
  780. if (!checkLogin())
  781. return;
  782. if (args.length != 1)
  783. {
  784. Utils.sendError(clout, "Invalid Syntax: !upload <filename>");
  785. return;
  786. }
  787.  
  788. synchronized(fileservers)
  789. {
  790. if (fileservers.size() == 0)
  791. {
  792. Utils.sendError(clout, "Unable to execute command. No fileservers online");
  793. return;
  794. }
  795. }
  796.  
  797. clout.writeLine(cmd + " " + args[0]);
  798. clout.flush();
  799. }
  800.  
  801. /*------------------------------------------------------------------------*/
  802.  
  803. public void cmdUpload2(String cmd, String[] args)
  804. throws IOException
  805. {
  806. if (!checkLogin())
  807. return;
  808. if (args.length < 2 || args[1].length() <= 0)
  809. {
  810. err.println("Error: Invalid " + cmd + "-command paket from client. Ignoring...");
  811. Utils.sendError(clout, "Internal Error: Invalid packet from client");
  812. return;
  813. }
  814.  
  815. long filesize, filesizecpy;
  816. if ((filesize = Utils.parseHeaderNum(args, 1)) < 0)
  817. return;
  818. filesizecpy = filesize;
  819.  
  820. long curversion = 0;
  821. synchronized(fileservers)
  822. {
  823. calcGifford();
  824. for(FSRecord fileserver : rfileservers)
  825. {
  826. filelist = new HashMap<String, Long>();
  827. int conn = -1;
  828. try
  829. {
  830. if ((conn = connectFileserver(fileserver)) < 0)
  831. return;
  832. fsout.get(conn).writeLine("!list");
  833. fsout.get(conn).flush();
  834. curconn = conn;
  835. fscmd.run(fsin.get(conn));
  836. }
  837. catch(Utils.HashError e)
  838. {
  839. fileserver.online = false;
  840. }
  841. catch(IOException e)
  842. {
  843. fileserver.online = false;
  844. Utils.sendError(clout, "Connection to fileserver " + fileserver.host + ":" + fileserver.port + " terminated unexpected");
  845. }
  846. disconnectFileserver(conn);
  847.  
  848. Long version = filelist.get(args[0]);
  849. if (version != null && version > curversion)
  850. curversion = version;
  851. }
  852.  
  853. ArrayList<Integer> conns = new ArrayList<Integer>();
  854. for(FSRecord fileserver : wfileservers)
  855. {
  856. int conn = -1;
  857. if ((conn = connectFileserver(fileserver)) < 0)
  858. return;
  859. conns.add(conn);
  860. }
  861.  
  862. try
  863. {
  864. for(Integer conn : conns)
  865. fsout.get(conn).writeLine(cmd + " " + Utils.join(Arrays.asList(args), " ") + " " + (curversion + 1));
  866.  
  867. byte[] buffer = new byte[8 * 1024];
  868. int toread = buffer.length;
  869. while(filesize > 0)
  870. {
  871. if (filesize < toread)
  872. toread = (int) filesize;
  873. int count = clin.read(buffer, 0, toread);
  874. if (count == -1)
  875. throw new IOException("Connection reset by peer");
  876.  
  877. /* decode + hash that chunk */
  878. byte[] decbuffer = new byte[aesdecrypt.getOutputSize(count)];
  879. int deccount = aesdecrypt.update(buffer, 0, count, decbuffer);
  880. hmac.update(decbuffer, 0, deccount);
  881.  
  882. for(Integer conn : conns)
  883. fsout.get(conn).write(decbuffer, 0, deccount);
  884. filesize -= count;
  885. }
  886. /* decryption + hash must be finalized */
  887. byte[] decbuffer = aesdecrypt.doFinal();
  888. byte[] hash = hmac.doFinal(decbuffer);
  889. for(Integer conn : conns)
  890. fsout.get(conn).write(decbuffer);
  891. for(Integer conn : conns)
  892. fsout.get(conn).write(hash);
  893. for(Integer conn : conns)
  894. fsout.get(conn).flush();
  895.  
  896. synchronized(users)
  897. {
  898. user.credits += 2 * filesizecpy;
  899. }
  900. for(Integer conn : conns)
  901. fileserver.get(conn).usage += filesizecpy;
  902. Utils.sendOutput(clout, "File '" + args[0] + "' successfully uploaded.");
  903. }
  904. catch(IOException e)
  905. {
  906. err.println("Error during file transfer: " + e.getMessage() + ". Closing connection to client");
  907. stop();
  908.  
  909. for(Integer conn : conns)
  910. {
  911. fileserver.get(conn).usage += filesizecpy;
  912. fileserver.get(conn).online = false;
  913. out.println("Fileserver marked as offline: " + fileserver.get(conn).host + ":" + fileserver.get(conn).port);
  914. }
  915. }
  916. catch(GeneralSecurityException e)
  917. {
  918. err.println("Error during encrypting file transfer: " + e.getMessage() + ". Closing connection to client");
  919. stop();
  920. }
  921.  
  922. for(Integer conn : conns)
  923. disconnectFileserver(conn);
  924. }
  925. }
  926.  
  927. /*------------------------------------------------------------------------*/
  928.  
  929. public void cmdUnknown(String cmd, String[] args)
  930. throws IOException
  931. {
  932. if (args.length == 0 && cmd.equals(mychallenge))
  933. {
  934. String thirdMessage = cmd;
  935. assert thirdMessage.matches("[" + B64 + "]{43}=") : "3rd message";
  936.  
  937. synchronized(users)
  938. {
  939. user.loggedin.add(clientaddr);
  940. }
  941.  
  942. Utils.sendOutput(clout, "Successfully logged in");
  943. return;
  944. }
  945.  
  946. err.println("Error: Unknown data from client: " + cmd + " "
  947. + Utils.join(Arrays.asList(args), " "));
  948. Utils.sendError(clout, "Unknown command");
  949. }
  950.  
  951. /*------------------------------------------------------------------------*/
  952.  
  953. @SuppressWarnings("deprecation")
  954. public void cmdFSRelayOutput(String cmd, String[] args)
  955. throws IOException
  956. {
  957. long num;
  958. if ((num = Utils.parseHeaderNum(args, 0)) < 0)
  959. return;
  960. String msg;
  961. clout.writeLine(cmd + " " + Utils.join(Arrays.asList(args), " "));
  962. for (; num > 0 && (msg = fsin.get(curconn).readLine()) != null; --num)
  963. clout.writeLine(msg);
  964. clout.flush();
  965. }
  966.  
  967. /*------------------------------------------------------------------------*/
  968.  
  969. public void cmdFSDownload(String cmd, String[] args)
  970. throws IOException
  971. {
  972. if (args.length < 2 || args[1].length() <= 0)
  973. {
  974. err.println("Error: Invalid " + cmd + "-command paket from fileserver. Ignoring...");
  975. Utils.sendError(clout, "Internal Error: Invalid packet from fileserver");
  976. return;
  977. }
  978.  
  979. String file = args[0];
  980. long filesize, filesizecpy;
  981. if ((filesize = Utils.parseHeaderNum(args, 1)) < 0)
  982. return;
  983. filesizecpy = filesize;
  984.  
  985. clout.writeLine(cmd + " " + Utils.join(Arrays.asList(args), " "));
  986.  
  987. try
  988. {
  989. byte[] buffer = new byte[8 * 1024];
  990. int toread = buffer.length;
  991. while(filesize > 0)
  992. {
  993. if (filesize < toread)
  994. toread = (int) filesize;
  995. int count = fsin.get(curconn).read(buffer, 0, toread);
  996. if (count == -1)
  997. throw new IOException("Connection reset by peer");
  998.  
  999. hmac.update(buffer, 0, count);
  1000.  
  1001. /* encode that chunk */
  1002. byte[] encbuffer = new byte[aesencrypt.getOutputSize(count)];
  1003. int enccount = aesencrypt.update(buffer, 0, count, encbuffer);
  1004.  
  1005. clout.write(encbuffer, 0, enccount);
  1006. filesize -= count;
  1007. }
  1008.  
  1009. /* encryption must be finalized */
  1010. clout.write(aesencrypt.doFinal());
  1011.  
  1012. byte[] hashc = hmac.doFinal();
  1013. byte[] hasht = new byte[hmac.getMacLength()];
  1014. fsin.get(curconn).readFully(hasht);
  1015. if (!Arrays.equals(hashc, hasht))
  1016. {
  1017. synchronized(fileservers)
  1018. {
  1019. err.println("Error: invalid MAC during filetransfer. Taking fileserver offline and restarting download.");
  1020. fileserver.get(curconn).online = false;
  1021. out.println("Fileserver marked as offline: " + fileserver.get(curconn).host + ":" + fileserver.get(curconn).port);
  1022. String[] tmp = { file };
  1023. cmdDownload("!download", tmp);
  1024. return;
  1025. }
  1026. }
  1027.  
  1028. Utils.sendOutput(clout, "File '" + file + "' successfully downloaded.");
  1029.  
  1030. synchronized(users)
  1031. {
  1032. user.credits -= filesizecpy;
  1033. }
  1034.  
  1035. synchronized(fileservers)
  1036. {
  1037. fileserver.get(curconn).usage += filesizecpy;
  1038. }
  1039. }
  1040. catch(IOException e)
  1041. {
  1042. err.println("Error during file transfer: " + e.getMessage() + ". Closing connection to client");
  1043. stop();
  1044.  
  1045. synchronized(fileservers)
  1046. {
  1047. fileserver.get(curconn).usage += filesizecpy;
  1048. fileserver.get(curconn).online = false;
  1049. out.println("Fileserver marked as offline: " + fileserver.get(curconn).host + ":" + fileserver.get(curconn).port);
  1050. }
  1051. }
  1052. catch(GeneralSecurityException e)
  1053. {
  1054. err.println("Error during encrypting file transfer: " + e.getMessage() + ". Closing connection to client");
  1055. stop();
  1056. }
  1057. }
  1058.  
  1059. /*------------------------------------------------------------------------*/
  1060.  
  1061. @SuppressWarnings("unchecked")
  1062. public void cmdFSList(String cmd, String[] args)
  1063. throws IOException
  1064. {
  1065. long num;
  1066. if ((num = Utils.parseHeaderNum(args, 0)) < 0)
  1067. return;
  1068.  
  1069. String msg;
  1070. for (; num > 0 && (msg = fsin.get(curconn).readLine()) != null; --num)
  1071. {
  1072. int ix = msg.lastIndexOf(' ');
  1073. if (ix == -1)
  1074. {
  1075. err.println("Error: Unknown filelist-message. Ignoring...");
  1076. continue;
  1077. }
  1078.  
  1079. try
  1080. {
  1081. String file = msg.substring(0, ix);
  1082. Long fileversion = Long.valueOf(msg.substring(ix + 1));
  1083.  
  1084. Long version = filelist.get(file);
  1085. if (version == null || fileversion > version)
  1086. filelist.put(file, fileversion);
  1087. }
  1088. catch(NumberFormatException e)
  1089. {
  1090. err.println("Error: Unable to parse file version. Ignoring...");
  1091. continue;
  1092. }
  1093. }
  1094. }
  1095.  
  1096. /*------------------------------------------------------------------------*/
  1097.  
  1098. public void cmdFSHashError(String cmd, String[] args)
  1099. throws IOException
  1100. {
  1101. err.println("Error: Hasherror from fileserver!");
  1102. synchronized(fileservers)
  1103. {
  1104. fileserver.get(curconn).online = false;
  1105. out.println("Fileserver marked as offline: " + fileserver.get(curconn).host + ":" + fileserver.get(curconn).port);
  1106. }
  1107. cmdHandler.call2();
  1108. }
  1109.  
  1110. /*------------------------------------------------------------------------*/
  1111.  
  1112. public void shutdown()
  1113. {
  1114. for(int i = fsschannel.size() - 1; i >= 0; --i)
  1115. disconnectFileserver(i);
  1116.  
  1117. try
  1118. {
  1119. clout.flush();
  1120. }
  1121. catch(IOException e)
  1122. {}
  1123.  
  1124. try
  1125. {
  1126. clin.close();
  1127. }
  1128. catch(IOException e)
  1129. {}
  1130.  
  1131. try
  1132. {
  1133. clout.close();
  1134. }
  1135. catch(IOException e)
  1136. {}
  1137.  
  1138. try
  1139. {
  1140. if (sock.isOpen())
  1141. sock.close();
  1142. }
  1143. catch(IOException e)
  1144. {}
  1145. }
  1146.  
  1147. /*------------------------------------------------------------------------*/
  1148.  
  1149. public void run()
  1150. {
  1151. try
  1152. {
  1153. out.println("[" + Thread.currentThread().getId() + "] New client connection from "
  1154. + clientaddr);
  1155. run(clin);
  1156. clout.flush();
  1157. }
  1158. catch(CommandHandler.Exception e)
  1159. {
  1160. err.println("Internal Error: " + e.getMessage());
  1161. e.printStackTrace();
  1162. }
  1163. catch(IOException e)
  1164. {
  1165. /* ignore that exception
  1166.   * it's usually a closed connection from client so
  1167.   * we can't do anything about it anyway
  1168.   */
  1169. }
  1170.  
  1171. if (user != null)
  1172. {
  1173. synchronized(users)
  1174. {
  1175. user.loggedin.remove(user.loggedin.indexOf(clientaddr));
  1176. }
  1177. }
  1178.  
  1179. out.println("[" + Thread.currentThread().getId() + "] Connection closed");
  1180. shutdown();
  1181. }
  1182. }
  1183.  
  1184. /*==========================================================================*/
  1185.  
  1186. public class TCPSocketReader
  1187. implements Runnable
  1188. {
  1189. private final ServerSocketChannel sschannel;
  1190. private final FSRecords fileservers;
  1191. private final UserRecords users;
  1192. private final Object mainLock;
  1193. private final ExecutorService pool;
  1194.  
  1195. TCPSocketReader(ServerSocketChannel sschannel, FSRecords fileservers,
  1196. UserRecords users, Object mainLock)
  1197. {
  1198. this.sschannel = sschannel;
  1199. this.fileservers = fileservers;
  1200. this.users = users;
  1201. this.mainLock = mainLock;
  1202. this.pool = Executors.newCachedThreadPool();
  1203. }
  1204.  
  1205. /*------------------------------------------------------------------------*/
  1206.  
  1207. public void run()
  1208. {
  1209. try
  1210. {
  1211. while(true)
  1212. pool.execute(new ClientConnection(sschannel.accept(), fileservers, users));
  1213. }
  1214. catch(NoSuchMethodException e)
  1215. {
  1216. err.println("Error: Unable to setup remote command handler");
  1217. }
  1218. catch(IOException e)
  1219. {
  1220. /* ignore that exception
  1221.   * thread will shutdown and unlock the main thread
  1222.   * which will shutdown the application
  1223.   */
  1224. }
  1225.  
  1226. pool.shutdown();
  1227. try
  1228. {
  1229. if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS))
  1230. out.println("Trying to shutdown the client connections. This may take up to 15 seconds...");
  1231. if (!pool.awaitTermination(5, TimeUnit.SECONDS))
  1232. {
  1233. pool.shutdownNow();
  1234. if (!pool.awaitTermination(5, TimeUnit.SECONDS))
  1235. err.println("Error: Client connections did not terminate. You may have to kill that appplication.");
  1236. }
  1237. }
  1238. catch(InterruptedException e)
  1239. {
  1240. pool.shutdownNow();
  1241. }
  1242.  
  1243. synchronized(mainLock)
  1244. {
  1245. mainLock.notify();
  1246. }
  1247. }
  1248. }
  1249.  
  1250. /*==========================================================================*/
  1251.  
  1252. public class Interactive
  1253. extends CommandInteractive
  1254. implements Runnable
  1255. {
  1256. private final InputStream sin;
  1257. private final Object mainLock;
  1258.  
  1259. Interactive(InputStream sin, Object mainLock)
  1260. throws NoSuchMethodException
  1261. {
  1262. this.sin = sin;
  1263. this.mainLock = mainLock;
  1264.  
  1265. cmdHandler.register("unknown", this, "cmdUnknown");
  1266. cmdHandler.register("!fileservers", this, "cmdFileservers");
  1267. cmdHandler.register("!users", this, "cmdUsers");
  1268. cmdHandler.register("!exit", this, "cmdExit");
  1269. }
  1270.  
  1271. /*------------------------------------------------------------------------*/
  1272.  
  1273. public void cmdUnknown(String cmd, String[] args)
  1274. {
  1275. err.println("Unknown command: " + cmd + " "
  1276. + Utils.join(Arrays.asList(args), " "));
  1277. }
  1278.  
  1279. /*------------------------------------------------------------------------*/
  1280.  
  1281. public void cmdFileservers(String cmd, String[] args)
  1282. {
  1283. synchronized(fileservers)
  1284. {
  1285. if (fileservers.size() == 0)
  1286. out.println("No fileservers registered");
  1287. else
  1288. {
  1289. calcGifford();
  1290. int line = 1;
  1291. int pad = Integer.toString(fileservers.size()).length();
  1292. for(Map.Entry<String, FSRecord> entry : fileservers.entrySet())
  1293. {
  1294. FSRecord record = entry.getValue();
  1295. out.println(String.format("%0" + pad + "d. IP: %s, Port: %d, %s, Usage: %d, Quorum: %s%s",
  1296. line, record.host, record.port,
  1297. (record.online) ? "online" : "offline",
  1298. record.usage,
  1299. (rfileservers.contains(record)) ? "R" : "",
  1300. (wfileservers.contains(record)) ? "W" : ""));
  1301. ++line;
  1302. }
  1303. }
  1304. }
  1305. }
  1306.  
  1307. /*------------------------------------------------------------------------*/
  1308.  
  1309. public void cmdUsers(String cmd, String[] args)
  1310. {
  1311. synchronized(users)
  1312. {
  1313. if (users.size() == 0)
  1314. out.println("No users registered");
  1315. else
  1316. {
  1317. int line = 1;
  1318. int pad = Integer.toString(users.size()).length();
  1319. for(Map.Entry<String, UserRecord> entry : users.entrySet())
  1320. {
  1321. UserRecord record = entry.getValue();
  1322. out.println(String.format("%0" + pad + "d. User: %s, %s, Credits: %d",
  1323. line, record.name,
  1324. (record.loggedin.size() > 0) ? "online" : "offline",
  1325. record.credits));
  1326. for(String host : record.loggedin)
  1327. out.println(String.format("%1$#" + (pad + 1) + "s Client: %2$s",
  1328. " ", host));
  1329. ++line;
  1330. }
  1331. }
  1332. }
  1333. }
  1334.  
  1335. /*------------------------------------------------------------------------*/
  1336.  
  1337. public void cmdExit(String cmd, String[] args)
  1338. {
  1339. stop();
  1340. }
  1341.  
  1342. /*------------------------------------------------------------------------*/
  1343.  
  1344. public void printPrompt()
  1345. {
  1346. out.print(">: ");
  1347. out.flush();
  1348. }
  1349.  
  1350. /*------------------------------------------------------------------------*/
  1351.  
  1352. public void run()
  1353. {
  1354. try
  1355. {
  1356. run(sin);
  1357. }
  1358. catch(CommandHandler.Exception e)
  1359. {
  1360. err.println("Internal Error: " + e.getMessage());
  1361. }
  1362. catch (IOException e)
  1363. {
  1364. /* ignore that exception
  1365.   * thread will shutdown and unlock the main thread
  1366.   * which will shutdown the application
  1367.   */
  1368. }
  1369.  
  1370. synchronized(mainLock)
  1371. {
  1372. mainLock.notify();
  1373. }
  1374. }
  1375. }
  1376.  
  1377. /*==========================================================================*/
  1378.  
  1379. public class CheckFSTask
  1380. implements Runnable
  1381. {
  1382. private FSRecords fileservers;
  1383. private final int fserverTimeout;
  1384.  
  1385. CheckFSTask(FSRecords fileservers, int fserverTimeout)
  1386. {
  1387. this.fileservers = fileservers;
  1388. this.fserverTimeout = fserverTimeout;
  1389. }
  1390.  
  1391. /*------------------------------------------------------------------------*/
  1392.  
  1393. public void run()
  1394. {
  1395. synchronized(fileservers)
  1396. {
  1397. long curTime = Calendar.getInstance().getTimeInMillis();
  1398. for(Map.Entry<String, FSRecord> entry : fileservers.entrySet())
  1399. {
  1400. if (entry.getValue().online && entry.getValue().lastUpdate + fserverTimeout < curTime)
  1401. {
  1402. entry.getValue().online = false;
  1403. out.println("Fileserver has gone offline: " + entry.getKey());
  1404. }
  1405. }
  1406. }
  1407. }
  1408. }
  1409.  
  1410. /*==========================================================================*/
  1411.  
  1412. private static int tcpPort;
  1413. private static int udpPort;
  1414. private static int fserverTimeout;
  1415. private static int checkPeriod;
  1416. private FSRecords fileservers;
  1417. private ArrayList<FSRecord> rfileservers;
  1418. private ArrayList<FSRecord> wfileservers;
  1419. private UserRecords users;
  1420. private ScheduledExecutorService scheduler = null;
  1421. private DatagramChannel dchannel = null;
  1422. private Thread tUDPSocketReader = null;
  1423. private ServerSocketChannel sschannel = null;
  1424. private Thread tTCPSocketReader = null;
  1425. private Thread tInteractive = null;
  1426. private InputStream stdin = null;
  1427. private final Object mainLock = new Object();
  1428.  
  1429. private static String keysDir;
  1430. private static String proxyKey;
  1431. private PrivateKey privateKey = null;
  1432. private Cipher rsaencrypt, rsadecrypt;
  1433. private Cipher aesencrypt, aesdecrypt;
  1434. private Mac hmac;
  1435. private final String B64 = "a-zA-Z0-9/+";
  1436.  
  1437. /*--------------------------------------------------------------------------*/
  1438.  
  1439. Proxy()
  1440. {
  1441. fileservers = new FSRecords();
  1442. rfileservers = new ArrayList<FSRecord>();
  1443. wfileservers = new ArrayList<FSRecord>();
  1444. users = new UserRecords();
  1445.  
  1446. try
  1447. {
  1448. rsaencrypt = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding");
  1449. rsadecrypt = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding");
  1450. aesencrypt = Cipher.getInstance("AES/CTR/NoPadding");
  1451. aesdecrypt = Cipher.getInstance("AES/CTR/NoPadding");
  1452. }
  1453. catch(NoSuchAlgorithmException e)
  1454. {
  1455. bailout("Unable to initialize cipher: " + e.getMessage());
  1456. }
  1457. catch(NoSuchPaddingException e)
  1458. {
  1459. bailout("Unable to initialize cipher: " + e.getMessage());
  1460. }
  1461. }
  1462.  
  1463. /*--------------------------------------------------------------------------*/
  1464.  
  1465. public static void usage()
  1466. throws Utils.Shutdown
  1467. {
  1468. out.println("Usage: Proxy\n");
  1469.  
  1470. // Java is some piece of crap which doesn't allow me to set exitcode w/o
  1471. // using System.exit. Maybe someday Java will be a fully functional
  1472. // programming language, but I wouldn't bet my money
  1473. //System.exit(1);
  1474. throw new Utils.Shutdown("FUCK YOU JAVA");
  1475. }
  1476.  
  1477. /*--------------------------------------------------------------------------*/
  1478.  
  1479. public void bailout(String error)
  1480. throws Utils.Shutdown
  1481. {
  1482. err.println("Error: " + error);
  1483. shutdown();
  1484.  
  1485. // Java is some piece of crap which doesn't allow me to set exitcode w/o
  1486. // using System.exit. Maybe someday Java will be a fully functional
  1487. // programming language, but I wouldn't bet my money
  1488. //System.exit(2);
  1489. throw new Utils.Shutdown("FUCK YOU JAVA");
  1490. }
  1491.  
  1492. /*--------------------------------------------------------------------------*/
  1493.  
  1494. public void parseArgs(String[] args)
  1495. {
  1496. if (args.length != 0)
  1497. usage();
  1498. }
  1499.  
  1500. /*--------------------------------------------------------------------------*/
  1501.  
  1502. public void parseConfig()
  1503. {
  1504. Config config = null;
  1505. try
  1506. {
  1507. config = new Config("proxy");
  1508. }
  1509. catch(MissingResourceException e)
  1510. {
  1511. bailout("configuration file doesn't exist or isn't readable");
  1512. }
  1513.  
  1514. String directive = null;
  1515. try
  1516. {
  1517. directive = "tcp.port";
  1518. tcpPort = config.getInt(directive);
  1519. if (tcpPort <= 0 || tcpPort > 65536)
  1520. bailout("configuration directive '" + directive + "' must be a valid port number (1 - 65535)");
  1521.  
  1522. directive = "udp.port";
  1523. udpPort = config.getInt(directive);
  1524. if (udpPort <= 0 || udpPort > 65536)
  1525. bailout("configuration directive '" + directive + "' must be a valid port number (1 - 65535)");
  1526.  
  1527. directive = "fileserver.timeout";
  1528. fserverTimeout = config.getInt(directive);
  1529. if (fserverTimeout <= 0)
  1530. bailout("configuration directive '" + directive + "' must be a positive number");
  1531.  
  1532. directive = "fileserver.checkPeriod";
  1533. checkPeriod = config.getInt(directive);
  1534. if (checkPeriod <= 0)
  1535. bailout("configuration directive '" + directive + "' must be a positive number");
  1536.  
  1537. directive = "keys.dir";
  1538. keysDir = config.getString(directive);
  1539. File dir = new File(keysDir);
  1540. if (!dir.isDirectory())
  1541. bailout("configuration directive '" + directive + "' is not a directory");
  1542. if (!dir.canRead())
  1543. bailout("configuration directive '" + directive + "' is not readable");
  1544.  
  1545. directive = "key";
  1546. proxyKey = config.getString(directive);
  1547. File key = new File(proxyKey);
  1548. if (!key.isFile())
  1549. bailout("configuration directive '" + directive + "' is not a file");
  1550. if (!key.canRead())
  1551. bailout("configuration directive '" + directive + "' is not readable");
  1552. PEMReader in = new PEMReader(new FileReader(key), new PasswordFinder()
  1553. {
  1554. @Override
  1555. public char[] getPassword()
  1556. {
  1557. try
  1558. {
  1559. /* reads the password from standard input for decrypting the private key */
  1560. out.println("Enter pass phrase for proxy key:");
  1561. return new BufferedReader(new InputStreamReader(System.in)).readLine().toCharArray();
  1562. }
  1563. catch(IOException e)
  1564. {
  1565. char[] tmp = {};
  1566. return tmp;
  1567. }
  1568. }
  1569. });
  1570. try
  1571. {
  1572. KeyPair keyPair = (KeyPair) in.readObject();
  1573. privateKey = keyPair.getPrivate();
  1574. rsadecrypt.init(Cipher.DECRYPT_MODE, privateKey);
  1575. }
  1576. catch(IOException e)
  1577. {
  1578. bailout("Error while reading private key of proxy. Maybe wrong pass phrase");
  1579. }
  1580.  
  1581. directive = "hmac.key";
  1582. File hmackey = new File(config.getString(directive));
  1583. if (!hmackey.isFile())
  1584. bailout("configuration directive '" + directive + "' is not a file");
  1585. if (!hmackey.canRead())
  1586. bailout("configuration directive '" + directive + "' is not readable");
  1587.  
  1588. byte[] keybytes = new byte[1024];
  1589. FileInputStream fis = new FileInputStream(hmackey);
  1590. fis.read(keybytes);
  1591. fis.close();
  1592.  
  1593. hmac = Mac.getInstance("HmacSHA256");
  1594. hmac.init(new javax.crypto.spec.SecretKeySpec(Hex.decode(keybytes), "HmacSHA256"));
  1595. }
  1596. catch(FileNotFoundException e)
  1597. {
  1598. bailout("unable to read file of directive '" + directive + "'");
  1599. }
  1600. catch(IOException e)
  1601. {
  1602. bailout("Error while reading file: " + e.getMessage());
  1603. }
  1604. catch(InvalidKeyException e)
  1605. {
  1606. bailout("invalid key file: " + e.getMessage());
  1607. }
  1608. catch(MissingResourceException e)
  1609. {
  1610. bailout("configuration directive '" + directive + "' is not set");
  1611. }
  1612. catch(NumberFormatException e)
  1613. {
  1614. bailout("configuration directive '" + directive + "' must be numeric");
  1615. }
  1616. catch(NoSuchAlgorithmException e)
  1617. {
  1618. bailout("Unable to initialize cipher: " + e.getMessage());
  1619. }
  1620. }
  1621.  
  1622. /*--------------------------------------------------------------------------*/
  1623.  
  1624. public void parseUsers()
  1625. {
  1626. Config config = null;
  1627. try
  1628. {
  1629. config = new Config("user");
  1630. }
  1631. catch(MissingResourceException e)
  1632. {
  1633. bailout("Unable to read from user properties file: " + e.getMessage());
  1634. }
  1635.  
  1636. /* first load the users only */
  1637. for (Enumeration e = config.getKeys(); e.hasMoreElements();)
  1638. {
  1639. String prop = (String)e.nextElement();
  1640. String[] pieces = prop.split("\\.", 2);
  1641. String user = pieces[0];
  1642.  
  1643. if (pieces.length == 1)
  1644. {
  1645. int credits = config.getInt(prop);
  1646. if (credits < 0)
  1647. {
  1648. err.println("Property " + prop + " must be positive number. Skipping...");
  1649. continue;
  1650. }
  1651. users.put(user, new UserRecord(user, credits));
  1652. }
  1653. }
  1654.  
  1655. /* next load their properties */
  1656. for (Enumeration e = config.getKeys(); e.hasMoreElements();)
  1657. {
  1658. String prop = (String)e.nextElement();
  1659. String[] pieces = prop.split("\\.", 2);
  1660. String user = pieces[0];
  1661.  
  1662. if (pieces.length == 1)
  1663. continue;
  1664. else if (pieces.length == 2)
  1665. {
  1666. UserRecord record = users.get(user);
  1667. if (record == null)
  1668. {
  1669. err.println("Can't load user properties for unknown user '" + user + "'. Skipping...");
  1670. continue;
  1671. }
  1672.  
  1673. if (pieces[1].equals("password"))
  1674. record.pass = config.getString(prop);
  1675. else
  1676. err.println("Property " + prop + " is unknown. Skipping...");
  1677. }
  1678. else
  1679. err.println("Property " + prop + " is unknown. Skipping...");
  1680. }
  1681. }
  1682.  
  1683. /*--------------------------------------------------------------------------*/
  1684.  
  1685. public void calcGifford()
  1686. {
  1687. synchronized(fileservers)
  1688. {
  1689. rfileservers.clear();
  1690. wfileservers.clear();
  1691.  
  1692. ArrayList<FSRecord> fslist = new ArrayList<FSRecord>();
  1693. for(FSRecord record : fileservers.values())
  1694. {
  1695. if (!record.online)
  1696. continue;
  1697. fslist.add(record);
  1698. }
  1699. if (fslist.size() == 0)
  1700. return;
  1701. Collections.sort(fslist);
  1702.  
  1703. int numwrite = (int) Math.floor(fslist.size() / 2) + 1;
  1704. int numread = fslist.size() - numwrite + 1;
  1705.  
  1706. for(int i = 0; i < numread; ++i)
  1707. rfileservers.add(fslist.get(i));
  1708. for(int i = 0; i < numwrite; ++i)
  1709. wfileservers.add(fslist.get(i));
  1710. }
  1711. }
  1712.  
  1713. /*--------------------------------------------------------------------------*/
  1714.  
  1715. public void shutdown()
  1716. {
  1717. try
  1718. {
  1719. if (scheduler != null)
  1720. {
  1721. scheduler.shutdownNow();
  1722. scheduler.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
  1723. }
  1724. }
  1725. catch(InterruptedException e)
  1726. {}
  1727.  
  1728. try
  1729. {
  1730. if (dchannel != null)
  1731. dchannel.close();
  1732. }
  1733. catch(IOException e)
  1734. {}
  1735.  
  1736. try
  1737. {
  1738. if (tUDPSocketReader != null)
  1739. tUDPSocketReader.join();
  1740. }
  1741. catch(InterruptedException e)
  1742. {}
  1743.  
  1744. try
  1745. {
  1746. if (sschannel != null)
  1747. sschannel.close();
  1748. }
  1749. catch(IOException e)
  1750. {}
  1751.  
  1752. try
  1753. {
  1754. if (tTCPSocketReader != null)
  1755. tTCPSocketReader.join();
  1756. }
  1757. catch(InterruptedException e)
  1758. {}
  1759.  
  1760. try
  1761. {
  1762. if (tInteractive != null)
  1763. {
  1764. tInteractive.interrupt();
  1765. tInteractive.join();
  1766. }
  1767. }
  1768. catch(InterruptedException e)
  1769. {}
  1770.  
  1771. try
  1772. {
  1773. if (stdin != null)
  1774. stdin.close();
  1775. }
  1776. catch(IOException e)
  1777. {}
  1778. }
  1779.  
  1780. /*--------------------------------------------------------------------------*/
  1781.  
  1782. public void run(String[] args)
  1783. {
  1784. parseArgs(args);
  1785. parseConfig();
  1786.  
  1787. synchronized(users)
  1788. {
  1789. parseUsers();
  1790. out.println("Users loaded successfully: " + users.size() + " users loaded");
  1791. }
  1792.  
  1793. synchronized(mainLock)
  1794. {
  1795. scheduler = Executors.newScheduledThreadPool(1);
  1796. ScheduledFuture<?> checkFSTimer = scheduler.scheduleAtFixedRate(
  1797. new CheckFSTask(fileservers, fserverTimeout),
  1798. 0, checkPeriod, TimeUnit.MILLISECONDS);
  1799.  
  1800. try
  1801. {
  1802. dchannel = DatagramChannel.open();
  1803. dchannel.socket().bind(new InetSocketAddress(udpPort));
  1804. tUDPSocketReader = new Thread(new UDPSocketReader(dchannel, fileservers,
  1805. mainLock));
  1806. tUDPSocketReader.start();
  1807. out.println("Listening on udp:/" + dchannel.socket().getLocalSocketAddress());
  1808. }
  1809. catch(IOException e)
  1810. {
  1811. bailout("Unable to create UDP Socket: " + e.getMessage());
  1812. }
  1813.  
  1814. try
  1815. {
  1816. sschannel = ServerSocketChannel.open();
  1817. sschannel.socket().bind(new InetSocketAddress(tcpPort));
  1818. tTCPSocketReader = new Thread(new TCPSocketReader(sschannel,
  1819. fileservers, users, mainLock));
  1820. tTCPSocketReader.start();
  1821. out.println("Listening on tcp:/" + sschannel.socket().getLocalSocketAddress());
  1822. }
  1823. catch(IOException e)
  1824. {
  1825. bailout("Unable to create TCP Socket: " + e.getMessage());
  1826. }
  1827.  
  1828. try
  1829. {
  1830. InputStream stdin = java.nio.channels.Channels.newInputStream(
  1831. new FileInputStream(FileDescriptor.in).getChannel());
  1832. tInteractive = new Thread(new Interactive(stdin, mainLock));
  1833. tInteractive.start();
  1834. }
  1835. catch(NoSuchMethodException e)
  1836. {
  1837. bailout("Unable to setup interactive command handler");
  1838. }
  1839.  
  1840. out.println("Proxy startup successful!");
  1841. try
  1842. {
  1843. mainLock.wait();
  1844. }
  1845. catch(InterruptedException e)
  1846. {
  1847. /* if we get interrupted -> ignore */
  1848. }
  1849.  
  1850. try
  1851. {
  1852. /* let the threads shutdown */
  1853. Thread.sleep(100);
  1854. }
  1855. catch(InterruptedException e)
  1856. {}
  1857. }
  1858.  
  1859. if (tUDPSocketReader != null && !tUDPSocketReader.isAlive())
  1860. bailout("Listening UDP socket closed unexpected. Terminating...");
  1861. if (tTCPSocketReader != null && !tTCPSocketReader.isAlive())
  1862. bailout("Listening TCP socket closed unexpected. Terminating...");
  1863.  
  1864. shutdown();
  1865. }
  1866.  
  1867. /*--------------------------------------------------------------------------*/
  1868.  
  1869. public static void main(String[] args)
  1870. {
  1871. try
  1872. {
  1873. Proxy proxy = new Proxy();
  1874. proxy.run(args);
  1875. }
  1876. catch(Utils.Shutdown e)
  1877. {}
  1878. }
  1879. }
  1880.