view src/main/java/jp/ac/u_ryukyu/treevnc/TreeVncCommandChannelListener.java @ 157:7cea8789387b

thread base command listening loop
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Fri, 13 Jun 2014 23:12:28 +0900
parents src/main/java/jp/ac/u_ryukyu/treevnc/TreeVncCommandCannelListener.java@e68dfd1972ac
children 1c9f6acdfeb2
line wrap: on
line source

package jp.ac.u_ryukyu.treevnc;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.LinkedBlockingDeque;
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.state.HandshakeState;
import com.glavsoft.transport.Reader;
import com.glavsoft.transport.Writer;

public class TreeVncCommandChannelListener implements Runnable {
    public MyRfbProto rfb = null;
    byte[] imageBytes;
    int port;
    LinkedBlockingDeque<TreeVncCommand> cmdQueue = new LinkedBlockingDeque<TreeVncCommand>();
    protected final static String versionMsg_3_856 = "RFB 003.856\n";
    
    public TreeVncCommandChannelListener(MyRfbProto _rfb, int p) {
        rfb = _rfb;
        port = p;
    }

    public void changeRfb(MyRfbProto _rfb) {
        rfb = _rfb;
    }
   
    public void commandMainLoop() {
        while(true) {
            TreeVncCommand cmd = cmdQueue.poll();
            cmd.handleTreeVncCommand();
        }
    }

    public void run() {
        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)  {
                // 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(MyRfbProto myRfbProto, final Writer os, final Reader is, Socket connection)
            throws IOException, TransportException {
        /**
         * initial connection of RFB protocol
         */
        String myHostAddress = connection.getLocalAddress().getHostAddress();
        sendRfbVersion(os);
        byte[] b;
        if ((b = readVersionMsg(is, os))!=null) {
            TreeVncCommand cmd = treeVncCommand(b,is,os,myHostAddress);
            if (cmd!=null) return cmd;
        }
        sendSecurityType(os);
        readSecType(is);
        sendSecResult(os);
        readClientInit(is);
        sendInitData(os);
        return new TreeVncCommand(rfb, myHostAddress, ProtocolContext.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) throws TransportException, IOException {
        ByteBuffer buf = ByteBuffer.wrap(b);
        int command = buf.get()&0xff;  // make it unsigned
        buf.position(buf.position()+3);
        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);
    }
    

    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 extention 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);
    }
    
    byte initData[] = {7, -128, 4, 56, 32, 24, 0, 1, 0, -1, 0, -1, 0, -1, 16, 8, 0, 0, 0, 0, 0, 0, 0, 7, 102, 105, 114, 101, 102, 108, 121};
    void sendInitData(Writer os) throws TransportException {
        // In case of "-d" we have no context
        ProtocolContext context = rfb.context;
        if (context != null){
            os.write(context.getInitData());            
        } else {
            // Send dummy data
            os.write(initData);
        }
    }
}