/* ISO.java
* Component: ProperJavaRDP
*
* Revision: $Revision: 1.7 $
* Author: $Author: telliott $
* Date: $Date: 2005/09/27 14:15:39 $
*
* Copyright (c) 2005 Propero Limited
*
* Purpose: ISO layer of communication
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* (See gpl.txt for details of the GNU General Public License.)
*
*/
package org.jopenray.rdp;
import java.io.*;
import java.net.*;
import org.apache.log4j.Logger;
import org.jopenray.rdp.crypto.CryptoException;
public class ISO {
static Logger logger = Logger.getLogger(ISO.class);
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 RdpPacket_Localised init(int length) {
RdpPacket_Localised data = new RdpPacket_Localised(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
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(Options.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());
}
/*
* if(Options.use_ssl){ try { rdpsock = this.negotiateSSL(rdpsock);
* this.in = new DataInputStream(rdpsock.getInputStream()); this.out=
* new DataOutputStream(rdpsock.getOutputStream()); } catch (Exception
* e) { e.printStackTrace(); throw new
* RdesktopException("SSL negotiation failed: " + e.getMessage()); } }
*/
}
/**
* 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 {
RdpPacket_Localised buffer = new RdpPacket_Localised(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(RdpPacket_Localised 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 (Options.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 RdpPacket_Localised receive() throws IOException, RdesktopException,
OrderException, CryptoException {
int[] type = new int[1];
RdpPacket_Localised 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 RdpPacket_Localised tcp_recv(RdpPacket_Localised p, int length)
throws IOException {
logger.debug("ISO.tcp_recv");
RdpPacket_Localised buffer = null;
byte[] packet = new byte[length];
in.readFully(packet, 0, length);
// try{ }
// catch(IOException e){ logger.warn("IOException: " + e.getMessage());
// return null; }
if (Options.debug_hexdump)
dump.encode(packet, "RECEIVE" /* System.out */);
if (p == null) {
buffer = new RdpPacket_Localised(length);
buffer.copyFromByteArray(packet, 0, 0, packet.length);
buffer.markEnd(length);
buffer.setStart(buffer.getPosition());
} else {
buffer = new RdpPacket_Localised((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 RdpPacket_Localised receiveMessage(int[] type) throws IOException,
RdesktopException, OrderException, CryptoException {
logger.debug("ISO.receiveMessage");
RdpPacket_Localised s = null;
int length, version;
next_packet: while (true) {
logger.debug("next_packet");
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) {
logger.debug("Processing rdp5 packet");
Common.rdp.rdp5_process(s, (version & 0x80) != 0);
continue next_packet;
} else
break;
}
s.get8();
type[0] = s.get8();
if (type[0] == DATA_TRANSFER) {
logger.debug("Data Transfer Packet");
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 = Options.username;
if (uname.length() > 9)
uname = uname.substring(0, 9);
int length = 11 + (Options.username.length() > 0 ? ("Cookie: mstshash="
.length()
+ uname.length() + 2) : 0) + 8;
RdpPacket_Localised buffer = new RdpPacket_Localised(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 (Options.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(Options.use_ssl? 0x01 : 0x00);
* buffer.incrementPosition(3);
*/
buffer.copyToByteArray(packet, 0, 0, packet.length);
out.write(packet);
out.flush();
}
}