/* * MirrorServer.java * * Copyright (C) 2015 Pixelgaffer * * This work is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; either version 2 of the License, or any later * version. * * This work is distributed in the hope that it will be useful, but without * any warranty; without even the implied warranty of merchantability or * fitness for a particular purpose. See version 2 and version 3 of the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.pixelgaffer.turnierserver.worker.server; import static java.nio.charset.StandardCharsets.UTF_8; import static org.pixelgaffer.turnierserver.PropertyUtils.WORKER_MIRROR_PASSWORD; import static org.pixelgaffer.turnierserver.PropertyUtils.WORKER_MIRROR_PASSWORD_REPEATS; import static org.pixelgaffer.turnierserver.PropertyUtils.WORKER_MIRROR_SALT_LENGTH; import static org.pixelgaffer.turnierserver.PropertyUtils.getIntRequired; import static org.pixelgaffer.turnierserver.PropertyUtils.getStringRequired; import static org.pixelgaffer.turnierserver.networking.SHA256.sha256; import java.io.BufferedReader; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.security.SecureRandom; import java.util.Arrays; import java.util.Base64; import java.util.Random; import org.pixelgaffer.turnierserver.Airbrake; import org.pixelgaffer.turnierserver.networking.DatastoreFtpClient; import org.pixelgaffer.turnierserver.worker.LibraryCache; import org.pixelgaffer.turnierserver.worker.WorkerMain; /** * Dieser Server spiegelt den FTP-Server auf dem Datastore für die Sandboxen, * die aus Sicherheitsgründen nur mit dem Worker kommunizieren dürfen. */ public class MirrorServer extends Thread { public static final int DEFAULT_PORT = 1338; private ServerSocket server; /** * Öffnet den Server auf dem angegebenen Port. */ public MirrorServer (int port) throws IOException { server = new ServerSocket(port); } @Override public void run () { while (!server.isClosed()) { try { Socket client = server.accept(); WorkerMain.getLogger().info(client.getInetAddress().getHostAddress() + " hat sich verbunden"); new Thread( () -> { boolean ai = true; int id = 0, version = 0; // ai String name = "nonexistent-library", language = "nonexistent-language"; // lib try { BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); String line = in.readLine(); if (line == null) throw new EOFException(); try { id = Integer.valueOf(line); } catch (NumberFormatException nfe) { ai = false; name = line; } if (ai) { line = in.readLine(); if (line == null) throw new EOFException(); version = Integer.valueOf(line); } else { language = in.readLine(); if (language == null) throw new EOFException(); } OutputStream out = client.getOutputStream(); byte[] salt = generateSalt(); out.write(Base64.getEncoder().encode(salt)); out.write(0xa); byte[] hash = sha256(getStringRequired(WORKER_MIRROR_PASSWORD).getBytes(), salt, getIntRequired(WORKER_MIRROR_PASSWORD_REPEATS)); line = in.readLine(); if (line == null) throw new EOFException(); if (!Arrays.equals(Base64.getDecoder().decode(line), hash)) { WorkerMain.getLogger() .critical("Der Mirror-Klient " + client + " hat das falsche Passwort gesendet!"); return; } if (ai) { out.write((Long.toString(DatastoreFtpClient.aiSize(id, version)) + "\n").getBytes(UTF_8)); DatastoreFtpClient.retrieveAi(id, version, out); } else { File tar = LibraryCache.getCache().getLibTarBz2(language, name); out.write((Long.toString(tar.length()) + "\n").getBytes(UTF_8)); FileInputStream fin = new FileInputStream(tar); byte buf[] = new byte[8192]; int read; while ((read = fin.read(buf)) > 0) out.write(buf, 0, read); fin.close(); } } catch (EOFException eofe) { WorkerMain.getLogger().warning("Der Client hat sich während der Übertragung disconnected"); } catch (Exception e) { WorkerMain.getLogger().critical("Die angeforderte " + (ai ? "KI " + id + "v" + version : "Bibliothek " + language + "/" + name) + " konnte nicht gesendet werden: " + e); Airbrake.log(e).printStackTrace(); } finally { try { client.close(); } catch (IOException ioe) { } } }).start(); } catch (Exception e) { Airbrake.log(e).printStackTrace(); } } } private static final Random r = new SecureRandom(); private static byte[] generateSalt () { byte[] salt = new byte[getIntRequired(WORKER_MIRROR_SALT_LENGTH)]; r.nextBytes(salt); return salt; } }