package zeta;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.net.Authenticator;
import java.net.BindException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import zeta.crypto.Encrypter;
import zeta.crypto.Key;
import zeta.crypto.KeyManager;
import zeta.crypto.Signature;
import zeta.util.Base64;
import zeta.util.StreamUtils;
public class ZetaClient {
public static void main(String[] args) {
String f = properties.get("exit.filename");
if (args != null && args.length > 0 && args[0].equals("exit")) {
exit();
if (f != null && f.length() > 0) {
new File(f).delete();
}
} else if (f != null && f.length() > 0) {
File file = new File(f);
if (file.exists()) {
exit();
file.delete();
}
} else {
System.setProperty("file.encoding", "8859_15");
ZetaClient zeta = new ZetaClient();
}
}
public static boolean isUnknownHostExceptionOccur() {
return unknownHostExceptionOccur;
}
private ZetaClient() {
try {
ZetaInfo.init(properties);
Thread t = new Thread() {
public void run() {
int portNumber = properties.get("port", 10000);
try {
ServerSocket serverSocket = new ServerSocket(portNumber, 0, InetAddress.getByName("127.0.0.1"));
active = true;
serverSocket.accept();
} catch (BindException be) {
ZetaInfo.write("Error: The program is already running or the port number " + portNumber + " is not available (can be changed in the configuration).");
} catch (Throwable e) {
ZetaInfo.handle(e);
} finally {
System.exit(1);
}
}
};
t.start();
while (!active) {
Thread.sleep(100);
}
t.setPriority(Thread.MIN_PRIORITY);
ZetaInfo.write("Initialization of client");
setProxyAuthentification();
downloadFiles();
startComputationManager();
} catch (Throwable e) {
ZetaInfo.handle(e);
System.exit(1);
}
}
private void downloadFiles() {
unknownHostExceptionOccur = false;
StringBuffer localFiles = new StringBuffer(1000);
String[] list = new File(".").list();
if (list != null && list.length > 0) {
for (int i = 0; i < list.length && localFiles.length() < 900; ++i) { String s = list[i].toLowerCase();
if (!ignoreFilename(s)) {
if (localFiles.length() == 0) {
localFiles.append('\'');
localFiles.append(s);
} else {
localFiles.append("','");
localFiles.append(s);
}
}
}
localFiles.append('\'');
}
HttpURLConnection connection = null;
try {
InetAddress localHost = InetAddress.getLocalHost();
String osName = System.getProperty("os.name", "?");
String arch = System.getProperty("os.arch", "?");
URL url = new URL("http", properties.get("host.name", "www.zetagrid.net"),
properties.get("host.port", 80),
encryptURLFile(properties.get("downloadURL", "/servlet/service/getClient")
+ "?hostname=" + URLEncoder.encode(localHost.getHostName().toLowerCase())
+ "&hostaddr=" + URLEncoder.encode(localHost.getHostAddress())
+ "&key=" + URLEncoder.encode(getKey())
+ "&os_name=" + URLEncoder.encode(osName)
+ "&os_version=" + URLEncoder.encode(System.getProperty("os.version", "?"))
+ "&os_arch=" + URLEncoder.encode(arch)
+ "&task=" + URLEncoder.encode(properties.get("task", "zeta-zeros"))
+ "&processors=" + URLEncoder.encode(properties.get("processors", "1"))
+ "&files=" + URLEncoder.encode(localFiles.toString())));
connection = (HttpURLConnection)url.openConnection();
connection.setUseCaches(false);
connection.setRequestMethod("GET");
connection.setDoInput(true);
connection.connect();
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
int idx = osName.indexOf(' ');
if (idx > 0) {
osName = osName.substring(0, idx);
}
ZipInputStream zip = new ZipInputStream(connection.getInputStream());
ZipEntry entry = zip.getNextEntry();
if (entry != null && entry.getName().equalsIgnoreCase("signature.txt")) {
String signatures = getSignatures(zip);
int count = 0;
while (true) {
entry = zip.getNextEntry();
if (entry == null) {
break;
}
if (++count == 1) {
if (!verifySignatures(signatures)) { ZetaInfo.write("The digital signatures are invalid.");
return;
}
}
try {
String outName = entry.getName().toLowerCase();
Object[] o = extractSignature(signatures, osName, arch, outName);
String signature = (o != null)? (String)o[0] : null;
Key key = (o != null)? (Key)o[1] : null;
writeData(signature, key, zip, outName);
} catch (IOException ioe) {
ZetaInfo.handle(ioe);
}
}
if (count > 0) {
downloadCompleted = true;
}
return;
}
}
} catch (UnknownHostException uhe) {
ZetaInfo.handle(uhe);
unknownHostExceptionOccur = true;
} catch (MalformedURLException e) {
ZetaInfo.handle(e);
} catch (IOException e) {
ZetaInfo.handle(e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
if (!unknownHostExceptionOccur) {
ZetaInfo.write("Could not download client.");
}
}
private boolean ignoreFilename(String filename) {
return filename.endsWith(".log") || filename.endsWith(".tmp") || filename.endsWith(".txt");
}
private void startComputationManager() throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
Class mainClass = Class.forName(properties.get("exec.class", "zeta.ZetaCalc"), true, new ClassLoader(ClassLoader.getSystemClassLoader()) {
protected Class findClass(String name) throws ClassNotFoundException {
if (downloadCompleted) {
ZetaInfo.write("All necessary files are downloaded. Please start again.");
System.exit(1);
return null;
} else {
throw new ClassNotFoundException(name);
}
}
});
Object obj = mainClass.newInstance();
mainClass.getDeclaredMethod(properties.get("exec.method", "run"), new Class[] {}).invoke(obj, new Object[] {});
}
private static void exit() {
try {
ZetaInfo.write("The client process will be terminated.");
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), properties.get("port", 10000));
socket.close();
} catch (IOException ioe) {
ZetaInfo.handle(ioe);
}
}
private void writeData(String signature, Key key, InputStream in, String outName) throws IOException {
ZetaInfo.write("Download file '" + outName + '\'');
ByteArrayOutputStream out = new ByteArrayOutputStream(64 * 1024);
StreamUtils.writeData(in, out, false, true);
ZetaInfo.write("Check digital signature of file '" + outName + '\'');
if (signature != null) {
Signature sig = new Signature(key);
if (sig.verify(signature, out.toByteArray())) {
File file = new File(outName);
file.delete(); FileOutputStream fout = new FileOutputStream(outName);
out.writeTo(fout);
fout.close();
return;
}
}
throw new IOException("Wrong signature for " + outName);
}
private String getSignatures(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(2 * 1024);
StreamUtils.writeData(in, out, false, true);
return out.toString("UTF-8");
}
private boolean verifySignatures(String signatures) throws IOException {
int i = signatures.lastIndexOf('\n');
int j = signatures.lastIndexOf('\r');
if (i >= 0 && i < j) {
i = j;
}
if (i >= 0 && i+1 < signatures.length()) {
return verify(null, signatures.substring(i+1), signatures.substring(0, i+1).getBytes("UTF-8"));
}
return false;
}
private Object[] extractSignature(String signatures, String osName, String arch, String program) throws IOException {
BufferedReader reader = new BufferedReader(new StringReader(signatures));
try {
while (true) {
String filename = reader.readLine();
if (filename == null) {
break;
}
int idx1 = filename.indexOf(',');
int idx2 = filename.indexOf(',', idx1+1);
int idx3 = filename.indexOf(',', idx2+1);
if (idx1 < idx2 && idx1 > 0 && idx2+1 < filename.length() && !program.equalsIgnoreCase("zeta.jar") && !program.equalsIgnoreCase("zeta_client.jar") && !program.equalsIgnoreCase("default.cfg")) {
if (filename.substring(idx1+1, idx2).equalsIgnoreCase(osName) && filename.substring(0, idx1).equalsIgnoreCase(program)) {
if (idx2 < idx3 && idx3+1 < filename.length() && filename.substring(idx2+1, idx3).equalsIgnoreCase(arch)) {
return new Object[] { reader.readLine(), KeyManager.getKey(filename.substring(idx3+1)) };
} else if (filename.substring(idx2+1).equalsIgnoreCase(arch)) {
return new Object[] { reader.readLine(), KeyManager.getKey(null) };
}
}
} else if (filename.equals(program)) {
return new Object[] { reader.readLine(), KeyManager.getKey(null) };
}
reader.readLine();
}
return null;
} finally {
reader.close();
}
}
private void setProxyAuthentification() {
String proxyHost = System.getProperties().getProperty("http.proxyHost");
if (proxyHost != null && proxyHost.length() > 0) {
final String username = properties.get("proxy.authentication.username");
final String password = properties.get("proxy.authentication.password");
if (username != null && username.length() > 0 && password != null && password.length() > 0) {
Authenticator.setDefault(new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password.toCharArray());
}
});
}
}
}
public static String getKey() {
String key = "";
try {
key = properties.get("name") + properties.get("eMail") + InetAddress.getLocalHost().getHostName();
key = Base64.encode(key.toLowerCase().getBytes("UTF-8"));
} catch (Exception e) {
ZetaInfo.write("Could not create key of workstation.");
}
return key;
}
static void encrypt(int randomize, String inFilename, String outFilename) throws IOException {
encrypt(randomize, null, inFilename, outFilename);
}
static void encrypt(int randomize, byte[] keyClassData, String inFilename, String outFilename) throws IOException {
Key key = null;
try {
key = KeyManager.getEncryptorKey(keyClassData);
} catch (Exception e) {
ZetaInfo.handle(e);
}
if (key == null) {
ZetaInfo.write("Encryption key is invalid.");
throw new IOException("Encryption key is invalid");
}
Encrypter encrypter = new Encrypter(key);
encrypter.encrypt(randomize, inFilename, outFilename);
}
static String encryptURLFile(String urlFile) throws IOException {
if (urlFile != null && "true".equals(properties.get("encryption.url"))) {
Key key = null;
try {
key = KeyManager.getEncryptorKey(null);
} catch (Exception e) {
ZetaInfo.handle(e);
}
if (key == null) {
ZetaInfo.write("Encryption key is invalid.");
throw new IOException("Encryption key is invalid");
}
Encrypter encrypter = new Encrypter(key);
urlFile = encrypter.encryptURLFile(urlFile);
}
return urlFile;
}
static boolean verify(String key, String signature, byte[] data) throws IOException {
Signature sig = new Signature(KeyManager.getKey(key));
return sig.verify(signature, data);
}
private boolean active = false;
private static boolean downloadCompleted = false;
private static boolean unknownHostExceptionOccur = false;
private static ZetaProperties properties = new ZetaProperties();
}