view src/main/java/jp/ac/u_ryukyu/treevnc/TreeVncCommandChannelListener.java @ 381:47c018aed50e

Change condition to ZRLEESender.decode method
author innparusu
date Thu, 03 Sep 2015 19:44:39 +0900
parents 7050b41329c6
children ffe01c959cdd
line wrap: on
line source

package jp.ac.u_ryukyu.treevnc;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.glavsoft.exceptions.TransportException;
import com.glavsoft.rfb.protocol.ProtocolContext;
import com.glavsoft.rfb.protocol.ProtocolContext.TreeCommand;
import com.glavsoft.rfb.protocol.state.HandshakeState;
import com.glavsoft.transport.Reader;
import com.glavsoft.transport.Writer;


public class TreeVncCommandChannelListener implements Runnable {
    public TreeRFBProto rfb = null;
    byte[] imageBytes;
    int port;
    LinkedBlockingQueue<TreeVncCommand> cmdQueue = new LinkedBlockingQueue<TreeVncCommand>();
    private Thread acceptThread;
    protected final static String versionMsg_3_856 = "RFB 003.856\n";

    public TreeVncCommandChannelListener(TreeRFBProto _rfb, int p) {
        rfb = _rfb;
        port = p;

    }

    public void commandMainLoop() {
        acceptThread = new Thread(new Runnable() {
            @Override
            public void run() {
                TreeVncCommand cmd = null;
                do {
                    try {
                        cmd = cmdQueue.poll(1000, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) {
                        continue;
                    }
                    if (cmd!=null) {
                        cmd.handleTreeVncCommand();
                    }
                } while (cmd == null || cmd.getCommand() != TreeCommand.QUIT_LOOP);
            }
        },"root-command-loop");
        acceptThread.start();
    }

    public void run() {
        commandMainLoop();
        while (true) {
            try {
                final Socket newCli = rfb.accept();
                final OutputStream os = newCli.getOutputStream();
                final InputStream is = newCli.getInputStream();
                new Thread(new Runnable() {
                    public void run() {
                        TreeVncCommand cmd = newClientHandler(newCli, new Writer(os), new Reader(is));
                        if (cmd!=null) cmdQueue.add(cmd);
                    }
                },"accepting").start();
            } catch (Exception e) {
                System.out.println("failed to connect incoming client" + e.getMessage());
            }
        }
    }


    public TreeVncCommand newClientHandler (final Socket newCli, final Writer os, final Reader is) {
        TreeVncCommand cmd = null;
        try {
            cmd  = initialConnection(rfb, os, is, newCli);
            if (cmd!=null && cmd.getCommand()!=TreeCommand.NEW_NODE)  {
                // TreeVNC command is processed
                newCli.close();
                return cmd;
            }
        } catch (Exception e) {
            try {
                System.out.println("new client faild :" + e.getMessage());
                newCli.close();
                return null;
            } catch (IOException e1) {
                System.out.println("new client close faild");
                return null;
            }
        }
        return cmd;
    }

    public TreeVncCommand initialConnection(TreeRFBProto myRfbProto, final Writer os, final Reader is, Socket connection)
            throws IOException, TransportException {
        /**
         * initial connection of RFB protocol
         */
        InetAddress adr = connection.getLocalAddress();
        String myHostAddress = adr.getHostAddress();
        String intf = NetworkInterface.getByInetAddress(adr).getName();
        sendRfbVersion(os);
        byte[] b;
        if ((b = readVersionMsg(is, os))!=null) {
            // direct TreeVNC Command from TreeRoot
            //   such as connectTo, connectToAsLeader
            //   or direct TreeVNC Command from lower node
            //   such as LostParent, LostChild
            TreeVncCommand cmd = treeVncCommand(b,is,os,myHostAddress,intf);
            if (cmd!=null) return cmd;
        }
        // normal connection from TreeVNC node candidate
        sendSecurityType(os);
        readSecType(is);
        sendSecResult(os);
        readClientInit(is);
        sendInitData(os);
        System.out.println("direct connection from "+connection.getInetAddress());
        return new TreeVncCommand(rfb, myHostAddress, TreeCommand.NEW_NODE, os, is, connection);
    }

    /**
     * handle TreeVNC Command
     * @param b   byte [] command
     * @param is
     * @param os
     * @param myHostName 
     * @return 
     * @throws TransportException 
     * @throws IOException 
     * 
     * TreeVNC Command is sent as a possible replied version message. 12 bytes is already read.
     *       Command 4 byte (including padding)
     *       lenght      4 byte 
     *       port         4 byte 
     *       rest of data ( add padding if it is shorter than 12 byte)
     */
    TreeVncCommand treeVncCommand(byte[] b, Reader is, Writer os, String myHostName,String intf) throws TransportException, IOException {
        ByteBuffer buf = ByteBuffer.wrap(b);
        TreeCommand command = TreeCommand.create(buf.get()&0xff);  // make it unsigned
        buf.get();
        short value = buf.getShort();
        int length = buf.getInt();
        int port = buf.getInt();
        String hostname = null;
        if (length>4) {
            if (length>1024) {
                System.out.println("Too long TreeVncCommand ");
                return null;
            }
            byte namebuf[] = new byte[length-4];
            try {
                is.readBytes(namebuf);
            } catch (TransportException e) {
                return null;
            }
            hostname = new String(namebuf);
        }
        return new TreeVncCommand(rfb, myHostName, command, port, hostname, intf, value);
    }


    void sendRfbVersion(Writer writer) throws IOException, TransportException {
        writer.write(versionMsg_3_856.getBytes());
    }

    byte[] readVersionMsg(Reader reader, Writer writer) throws IOException, TransportException {

        byte[] b = new byte[HandshakeState.PROTOCOL_STRING_LENGTH];

        reader.readBytes(b);

        if ((b[0]&0xff)>=220) return b; // TreeVNC extension command.
        if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ')
                || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9')
                || (b[6] < '0') || (b[6] > '9') || (b[7] != '.')
                || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9')
                || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) {
            throw new IOException("this is not an RFB server");
        }

        int rfbMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0');
        //      int rfbMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0');

        if (rfbMajor < 3) {
            throw new IOException(
                    "RFB server does not support protocol version 3");
        }

        return null;
    }


    void readSecType(Reader reader) throws TransportException {
        byte[] b = new byte[1];
        reader.read(b);
    }

    void sendSecurityType(Writer os) throws TransportException {
        // number-of-security-types
        os.writeInt(1);
        // security-types
        // 1:None
        os.writeInt(1);

        /*
         * os.write(4); os.write(30); os.write(31); os.write(32); os.write(35);
         * os.flush();
         */
    }

    void sendSecResult(Writer os) throws TransportException {
        ByteBuffer b = ByteBuffer.allocate(4);
        b.order(ByteOrder.BIG_ENDIAN);
        b.putInt(0);
        os.write(b.array());
    }

    void readClientInit(Reader in) throws TransportException {
        byte[] b = new byte[0];
        in.readBytes(b);
    }

    void sendInitData(Writer os) throws TransportException {
        ProtocolContext context = rfb.context;
        if (context != null) {
            if (rfb.filterSingleDisplay) {
                int width = rfb.getSingleWidth();
                int height = rfb.getSingleHeight();
                byte[] initData = createOriginalInitData(width, height, context.getRemoteDesktopName());
//                os.write(initData);
                os.write(context.getInitData());
            } else {
                os.write(context.getInitData());
            }
        } else {
            // In case of "-d" we have no context
            // Send dummy data
            // width : 1920
            // height : 1080
            // title : "girefly"
            byte[] dummyInitData = {7, -128, 4, 56, 32, 24, 0, 1, 0, -1, 0, -1, 0, -1, 16, 8, 0, 0, 0, 0, 0, 0, 0, 7, 103, 105, 114, 101, 102, 108, 121};
            os.write(dummyInitData);
        }
    }

    public byte[] createOriginalInitData(int width, int height, String desktopName) {
        byte[] titleBytes = null;
        int titleLength = 0;
        byte[] pixelFormat = {32, 24, 0, 1, 0, -1, 0, -1, 0, -1, 16, 8, 0, 0, 0, 0, 0, 0, 0};
        try {
            titleBytes = desktopName.getBytes("UTF-8");
            titleLength = titleBytes.length;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        ByteBuffer initData = ByteBuffer.allocate(2+2+19+1+titleLength); // w+h+pixelFormat+titleLength+titleBytes
        initData.putShort((short) width);
        initData.putShort((short) height);
        initData.put(pixelFormat);
        initData.put((byte) titleLength);
        initData.put(titleBytes);
        initData.flip();

        return initData.array();
    }

    public void waitForShutdown() {
        if (acceptThread!=null) {
            try {
                acceptThread.join();
            } catch (InterruptedException e) {
            }
        }
    }
}