/* ISO.java
* Component: ProperJavaRDP
*
* Revision: $Revision: 1.1.1.1 $
* Author: $Author: suvarov $
* Date: $Date: 2007/03/08 00:26:21 $
*
* Copyright (c) 2005 Propero Limited
*
* Purpose: ISO layer of communication
*/
package com.lixia.rdp;
import com.alssoftrd.utils.RDPConnection;
import java.io.*;
import java.net.*;
import com.lixia.rdp.crypto.CryptoException;
import com.lixia.rdp.Package.RdpPackage;
import com.lixia.uag.websocket.LocalHttpTunnel;
public abstract class ISO {
// static Logger logger = Logger.getLogger("ISO");
private HexDump dump = null;
protected Socket rdpsock = null;
private DataInputStream in = null;
private DataOutputStream out = null;
/* this for the ISO Layer */
private static final int CONNECTION_REQUEST = 0xE0;
private static final int CONNECTION_CONFIRM = 0xD0;
private static final int DISCONNECT_REQUEST = 0x80;
private static final int DATA_TRANSFER = 0xF0;
private static final int ERROR = 0x70;
private static final int PROTOCOL_VERSION = 0x03;
private static final int EOT = 0x80;
/**
* Construct ISO object, initialises hex dump
*/
public ISO() {
dump = new HexDump();
}
/**
* Initialise an ISO PDU
*
* @param length Desired length of PDU
* @return Packet configured as ISO PDU, ready to write at higher level
*/
public RdpPackage init(int length) {
RdpPackage data = new RdpPackage(length + 7);//getMemory(length+7);
data.incrementPosition(7);
data.setStart(data.getPosition());
return data;
}
/*
protected Socket negotiateSSL(Socket sock) throws Exception{
return sock;
}*/
/**
* Create a socket for this ISO object
*
* @param host Address of server
* @param port Port on which to connect socket
* @throws IOException
*/
protected void doSocketConnect(InetAddress host, int port) throws IOException {
int timeout_ms = 3000; // timeout in milliseconds
if (RDPConnection.conf.http_mode && RDPConnection.conf.http_server != null) {
LocalHttpTunnel tunnel = new LocalHttpTunnel(RDPConnection.conf.http_server,
new InetSocketAddress(host, port),
RDPConnection.conf.username,
RDPConnection.conf.password,
RDPConnection.conf.command);
SocketAddress endpoint = tunnel.createTunnel();
if (endpoint != null) {
this.rdpsock = new Socket();
rdpsock.connect(endpoint, timeout_ms);
} else {
throw new IOException("failed to create tunnel!");
}
} else {
rdpsock = new Socket();
rdpsock.connect(new InetSocketAddress(host, port), timeout_ms);
}
}
/**
* Connect to a server
*
* @param host Address of server
* @param port Port to connect to on server
* @throws IOException
* @throws RdesktopException
* @throws OrderException
* @throws CryptoException
*/
public void connect(InetAddress host, int port) throws IOException, RdesktopException, OrderException, CryptoException {
int[] code = new int[1];
doSocketConnect(host, port);
rdpsock.setTcpNoDelay(RDPConnection.conf.low_latency);
// this.in = new InputStreamReader(rdpsock.getInputStream());
this.in = new DataInputStream(new BufferedInputStream(rdpsock.getInputStream()));
this.out = new DataOutputStream(new BufferedOutputStream(rdpsock.getOutputStream()));
send_connection_request();
receiveMessage(code);
if (code[0] != CONNECTION_CONFIRM) {
throw new RdesktopException("Expected CC got:" + Integer.toHexString(code[0]).toUpperCase());
}
}
/**
* Send a self contained iso-pdu
*
* @param type one of the following CONNECT_RESPONSE, DISCONNECT_REQUEST
* @exception IOException when an I/O Error occurs
*/
private void sendMessage(int type) throws IOException {
RdpPackage buffer = new RdpPackage(11);//getMemory(11);
byte[] packet = new byte[11];
buffer.set8(PROTOCOL_VERSION); // send Version Info
buffer.set8(0); // reserved byte
buffer.setBigEndian16(11); // Length
buffer.set8(6); // Length of Header
buffer.set8(type); //where code = CR or DR
buffer.setBigEndian16(0); // Destination reference ( 0 at CC and DR)
buffer.setBigEndian16(0); // source reference should be a reasonable address we use 0
buffer.set8(0); //service class
buffer.copyToByteArray(packet, 0, 0, packet.length);
out.write(packet);
out.flush();
}
/**
* Send a packet to the server, wrapped in ISO PDU
*
* @param buffer Packet containing data to send to server
* @throws RdesktopException
* @throws IOException
*/
public void send(RdpPackage buffer) throws RdesktopException, IOException {
if (rdpsock == null || out == null) {
return;
}
if (buffer.getEnd() < 0) {
throw new RdesktopException("No End Mark!");
} else {
int length = buffer.getEnd();
byte[] packet = new byte[length];
//RdpPacket data = this.getMemory(length+7);
buffer.setPosition(0);
buffer.set8(PROTOCOL_VERSION); // Version
buffer.set8(0); // reserved
buffer.setBigEndian16(length); //length of packet
buffer.set8(2); //length of header
buffer.set8(DATA_TRANSFER);
buffer.set8(EOT);
buffer.copyToByteArray(packet, 0, 0, buffer.getEnd());
if (RDPConnection.conf.debug_hexdump) {
dump.encode(packet, "SEND"/*System.out*/);
}
out.write(packet);
out.flush();
}
}
/**
* Receive a data transfer message from the server
*
* @return Packet containing message (as ISO PDU)
* @throws IOException
* @throws RdesktopException
* @throws OrderException
* @throws CryptoException
*/
public RdpPackage receive() throws IOException, RdesktopException, OrderException, CryptoException {
int[] type = new int[1];
RdpPackage buffer = receiveMessage(type);
if (buffer == null) {
return null;
}
if (type[0] != DATA_TRANSFER) {
throw new RdesktopException("Expected DT got:" + type[0]);
}
return buffer;
}
/**
* Receive a specified number of bytes from the server, and store in a
* packet
*
* @param p Packet to append data to, null results in a new packet being
* created
* @param length Length of data to read
* @return Packet containing read data, appended to original data if
* provided
* @throws IOException
*/
private RdpPackage tcp_recv(RdpPackage p, int length) throws IOException {
RdpPackage buffer;
byte[] packet = new byte[length];
in.readFully(packet, 0, length);
// try{ }
// catch(IOException e){ logger.warn("IOException: " + e.getMessage()); return null; }
if (RDPConnection.conf.debug_hexdump) {
dump.encode(packet, "RECEIVE" /*System.out*/);
}
if (p == null) {
buffer = new RdpPackage(length);
buffer.copyFromByteArray(packet, 0, 0, packet.length);
buffer.markEnd(length);
buffer.setStart(buffer.getPosition());
} else {
buffer = new RdpPackage((p.getEnd() - p.getStart())
+ length);
buffer.copyFromPacket(p, p.getStart(), 0, p.getEnd());
buffer.copyFromByteArray(packet, 0, p.getEnd(), packet.length);
buffer.markEnd(p.size() + packet.length);
buffer.setPosition(p.getPosition());
buffer.setStart(0);
}
return buffer;
}
/**
* Receive a message from the server
*
* @param type Array containing message type, stored in type[0]
* @return Packet object containing data of message
* @throws IOException
* @throws RdesktopException
* @throws OrderException
* @throws CryptoException
*/
private RdpPackage receiveMessage(int[] type) throws IOException, RdesktopException, OrderException, CryptoException {
RdpPackage s = null;
int length, version;
next_packet:
while (true) {
s = tcp_recv(null, 4);
if (s == null) {
return null;
}
version = s.get8();
if (version == 3) {
s.incrementPosition(1); // pad
length = s.getBigEndian16();
} else {
length = s.get8();
if ((length & 0x80) != 0) {
length &= ~0x80;
length = (length << 8) + s.get8();
}
}
s = tcp_recv(s, length - 4);
if (s == null) {
return null;
}
if ((version & 3) == 0) {
RDPConnection.conf.rdp.rdp5_process(s, (version & 0x80) != 0);
continue next_packet;
} else {
break;
}
}
s.get8();
type[0] = s.get8();
if (type[0] == DATA_TRANSFER) {
s.incrementPosition(1); // eot
return s;
}
s.incrementPosition(5); // dst_ref, src_ref, class
return s;
}
/**
* Disconnect from an RDP session, closing all sockets
*/
public void disconnect() {
if (rdpsock == null) {
return;
}
try {
sendMessage(DISCONNECT_REQUEST);
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
if (rdpsock != null) {
rdpsock.close();
}
} catch (IOException e) {
in = null;
out = null;
rdpsock = null;
return;
}
in = null;
out = null;
rdpsock = null;
}
/**
* Send the server a connection request, detailing client protocol version
*
* @throws IOException
*/
void send_connection_request() throws IOException {
String uname = RDPConnection.conf.username;
if (uname.length() > 9) {
uname = uname.substring(0, 9);
}
int length = 11 + (RDPConnection.conf.username.length() > 0 ? ("Cookie: mstshash="
.length()
+ uname.length() + 2) : 0) + 8;
RdpPackage buffer = new RdpPackage(length);
byte[] packet = new byte[length];
buffer.set8(PROTOCOL_VERSION); // send Version Info
buffer.set8(0); // reserved byte
buffer.setBigEndian16(length); // Length
buffer.set8(length - 5); // Length of Header
buffer.set8(CONNECTION_REQUEST);
buffer.setBigEndian16(0); // Destination reference ( 0 at CC and DR)
buffer.setBigEndian16(0); // source reference should be a reasonable address we use 0
buffer.set8(0); //service class
if (RDPConnection.conf.username.length() > 0) {
// logger.debug("Including username");
buffer
.out_uint8p("Cookie: mstshash=", "Cookie: mstshash="
.length());
buffer.out_uint8p(uname, uname.length());
buffer.set8(0x0d); // unknown
buffer.set8(0x0a); // unknown
}
/*
// Authentication request?
buffer.setLittleEndian16(0x01);
buffer.setLittleEndian16(0x08);
// Do we try to use SSL?
buffer.set8(RDPConnection.conf.use_ssl? 0x01 : 0x00);
buffer.incrementPosition(3);
*/
buffer.copyToByteArray(packet, 0, 0, packet.length);
out.write(packet);
out.flush();
}
}