/* * This file is part of the Haven & Hearth game client. * Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and * Björn Johannessen <johannessen.bjorn@gmail.com> * * Redistribution and/or modification of this file is subject to the * terms of the GNU Lesser General Public License, version 3, as * published by the Free Software Foundation. * * This program 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 the * GNU General Public License for more details. * * Other parts of this source tree adhere to other copying * rights. Please see the file `COPYING' in the root directory of the * source tree for details. * * A copy the GNU Lesser General Public License is distributed along * with the source tree of which this file is a part in the file * `doc/LPGL-3'. If it is missing for any reason, please see the Free * Software Foundation's website at <http://www.fsf.org/>, or write * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA */ package haven.rs; import haven.*; import java.util.*; import java.io.*; import java.net.*; import java.security.*; import haven.Composited.MD; import haven.Composited.ED; public class Server extends Thread { public static final Map<String, Command> commands = new HashMap<String, Command>(); private final ServerSocket sk; private final Random rng; private final byte[] key; { try { rng = SecureRandom.getInstance("SHA1PRNG"); } catch(NoSuchAlgorithmException e) { throw(new Error(e)); } } public interface Command { public Object[] run(Client cl, Object... args) throws InterruptedException; } static { commands.put("ava", AvaRender.call); } public class Client extends Thread { private final Socket sk; private boolean auth = false; private final byte[] nonce, ckey; { nonce = new byte[32]; rng.nextBytes(nonce); MessageDigest dig; try { dig = MessageDigest.getInstance("SHA-256"); } catch(NoSuchAlgorithmException e) { throw(new Error(e)); } dig.update(key); dig.update(nonce); ckey = dig.digest(); } private Client(Socket sk) { super("Render server handler"); this.sk = sk; setDaemon(true); start(); } byte[] read(InputStream in, int bytes) throws IOException { byte[] ret = new byte[bytes]; int n = 0; while(n < bytes) { int rv = in.read(ret, n, bytes - n); if(rv < 0) throw(new IOException("Unexpected end-of-file")); n += rv; } return(ret); } public void run() { try { InputStream in; OutputStream out; try { in = sk.getInputStream(); out = sk.getOutputStream(); } catch(IOException e) { throw(new RuntimeException(e)); } while(true) { try { int len = Utils.int32d(read(in, 4), 0); if(!auth && (len > 256)) return; Message msg = new Message(0, read(in, len)); String cmd = msg.string(); Object[] args = msg.list(); Object[] reply; if(auth) { Command cc = commands.get(cmd); if(cc != null) reply = cc.run(this, args); else reply = new Object[] {"nocmd"}; } else { if(cmd.equals("nonce")) { reply = new Object[] {nonce}; } else if(cmd.equals("auth")) { if(Arrays.equals((byte[])args[0], ckey)) { reply = new Object[] {"ok"}; auth = true; } else { reply = new Object[] {"no"}; } } else { return; } } Message rb = new Message(0); rb.addlist(reply); byte[] rbuf = new byte[4 + rb.blob.length]; Utils.uint32e(rb.blob.length, rbuf, 0); System.arraycopy(rb.blob, 0, rbuf, 4, rb.blob.length); out.write(rbuf); } catch(IOException e) { return; } } } catch(InterruptedException e) { } finally { try { sk.close(); } catch(IOException e) { throw(new RuntimeException(e)); } } } } public Server(int port, byte[] key) throws IOException { super("Render server"); this.key = key; sk = new ServerSocket(port); start(); } public void run() { try { while(true) { Socket nsk; try { nsk = sk.accept(); } catch(IOException e) { break; } new Client(nsk); } } finally { try { sk.close(); } catch(IOException e) { throw(new RuntimeException(e)); } } } public static void main(String[] args) throws Exception { new Server(Integer.parseInt(args[0]), Utils.base64dec(System.getenv("AUTHKEY"))); } }