/* VChannels.java * Component: ProperJavaRDP * * Revision: $Revision: 1.4 $ * Author: $Author: telliott $ * Date: $Date: 2005/09/27 14:15:40 $ * * Copyright (c) 2005 Propero Limited * * Purpose: Static store for all registered channels * * 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.rdp5; import java.io.IOException; import java.lang.reflect.Array; import org.apache.log4j.Logger; import org.jopenray.rdp.Input; import org.jopenray.rdp.MCS; import org.jopenray.rdp.Options; import org.jopenray.rdp.RdesktopException; import org.jopenray.rdp.RdpPacket_Localised; import org.jopenray.rdp.crypto.CryptoException; public class VChannels { protected static Logger logger = Logger.getLogger(Input.class); /* Sound format constants */ public static final int WAVE_FORMAT_PCM = 1; public static final int WAVE_FORMAT_ADPCM = 2; public static final int WAVE_FORMAT_ALAW = 6; public static final int WAVE_FORMAT_MULAW = 7; /* Virtual channel options */ public static final int CHANNEL_OPTION_INITIALIZED = 0x80000000; public static final int CHANNEL_OPTION_ENCRYPT_RDP = 0x40000000; public static final int CHANNEL_OPTION_COMPRESS_RDP = 0x00800000; public static final int CHANNEL_OPTION_SHOW_PROTOCOL = 0x00200000; /* NT status codes for RDPDR */ public static final int STATUS_SUCCESS = 0x00000000; public static final int STATUS_INVALID_PARAMETER = 0xc000000d; public static final int STATUS_INVALID_DEVICE_REQUEST = 0xc0000010; public static final int STATUS_ACCESS_DENIED = 0xc0000022; public static final int MAX_CHANNELS = 4; public static final int CHANNEL_CHUNK_LENGTH = 1600; public static final int CHANNEL_FLAG_FIRST = 0x01; public static final int CHANNEL_FLAG_LAST = 0x02; public static final int CHANNEL_FLAG_SHOW_PROTOCOL = 0x10; private VChannel channels[] = new VChannel[MAX_CHANNELS]; private int num_channels; public int num_channels() { return num_channels; } private byte[] fragment_buffer = null; /** * Obtain the MCS ID for a specific numbered channel * * @param c * Channel number for which to obtain MCS ID * @return MCS ID associated with the supplied channel number */ public int mcs_id(int c) { return MCS.MCS_GLOBAL_CHANNEL + 1 + c; } /** * Initialise the maximum number of Virtual Channels */ public VChannels() { channels = new VChannel[MAX_CHANNELS]; } /** * Retrieve the VChannel object for the numbered channel * * @param c * Channel number * @return The requested Virtual Channel */ public VChannel channel(int c) { if (c < num_channels) return channels[c]; else return null; } /** * Retrieve the VChannel object for the specified MCS channel ID * * @param channelno * MCS ID for the required channel * @return Virtual Channel associated with the supplied MCS ID */ public VChannel find_channel_by_channelno(int channelno) { if (channelno > MCS.MCS_GLOBAL_CHANNEL + num_channels) { logger.warn("Channel " + channelno + " not defined. Highest channel defined is " + MCS.MCS_GLOBAL_CHANNEL + num_channels); return null; } else return channels[channelno - MCS.MCS_GLOBAL_CHANNEL - 1]; } /** * Remove all registered virtual channels */ public void clear() { channels = new VChannel[MAX_CHANNELS]; num_channels = 0; } /** * Register a new virtual channel * @param v Virtual channel to be registered * @return True if successful * @throws RdesktopException */ public boolean register(VChannel v) throws RdesktopException { if (!Options.use_rdp5) { return false; } if (num_channels >= MAX_CHANNELS) throw new RdesktopException( "Channel table full. Could not register channel."); channels[num_channels] = v; v.set_mcs_id(MCS.MCS_GLOBAL_CHANNEL + 1 + num_channels); num_channels++; return true; } /** * Process a packet sent on a numbered channel * @param data Packet sent to channel * @param mcsChannel Number specified for channel * @throws RdesktopException * @throws IOException * @throws CryptoException */ public void channel_process(RdpPacket_Localised data, int mcsChannel) throws RdesktopException, IOException, CryptoException { int length, flags; int thislength = 0; VChannel channel = null; int i; for (i = 0; i < num_channels; i++) { if (mcs_id(i) == mcsChannel) { channel = channels[i]; break; } } if (i >= num_channels) return; length = data.getLittleEndian32(); flags = data.getLittleEndian32(); if (((flags & CHANNEL_FLAG_FIRST) != 0) && ((flags & CHANNEL_FLAG_LAST) != 0)) { // single fragment - pass straight up channel.process(data); } else { // append to the defragmentation buffer byte[] content = new byte[data.getEnd() - data.getPosition()]; data .copyToByteArray(content, 0, data.getPosition(), content.length); fragment_buffer = append(fragment_buffer, content); if ((flags & CHANNEL_FLAG_LAST) != 0) { RdpPacket_Localised fullpacket = new RdpPacket_Localised( fragment_buffer.length); fullpacket.copyFromByteArray(fragment_buffer, 0, 0, fragment_buffer.length); // process the entire reconstructed packet channel.process(fullpacket); fragment_buffer = null; } } } /** * Increase the size of an array * @param a Array to expand * @param amount Number of elements to add to the array * @return Expanded array */ static Object arrayExpand(Object a, int amount) { Class cl = a.getClass(); if (!cl.isArray()) return null; int length = Array.getLength(a); int newLength = length + amount; // 50% more Class componentType = a.getClass().getComponentType(); Object newArray = Array.newInstance(componentType, newLength); System.arraycopy(a, 0, newArray, 0, length); return newArray; } /** * Concatenate two byte arrays * @param target Contains initial bytes in output * @param source Appended to target array * @return Concatenation of arrays, target+source */ static byte[] append(byte[] target, byte[] source) { if (target == null || target.length <= 0) return source; else if (source == null || source.length <= 0) return target; else { byte[] out = (byte[]) arrayExpand(target, source.length); System.arraycopy(source, 0, out, target.length, source.length); return out; } } }