// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Element; import streamer.Link; import streamer.OneTimeSwitch; import streamer.Pipeline; import streamer.PipelineImpl; import streamer.debug.MockSink; import streamer.debug.MockSource; /** * The MCS Channel Join Request PDUs are sent sequentially. The first PDU is * sent after receiving the MCS Attach User Confirm PDU and subsequent PDUs are * sent after receiving the MCS Channel Join Confirm PDU for the previous * request. Sending of the MCS Channel Join Request PDUs MUST continue until all * channels have been successfully joined. * * @see http://msdn.microsoft.com/en-us/library/cc240686.aspx */ public class ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs extends OneTimeSwitch { private static final int MCS_CHANNEL_CONFIRM_PDU = 15; protected int[] channels; protected int channelRequestsSent = 0; protected RdpState state; public ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs(String id, int[] channels, RdpState state) { super(id); this.channels = channels; this.state = state; } @Override protected void handleOneTimeData(ByteBuffer buf, Link link) { if (buf == null) return; // Parse channel confirm response int typeAndFlags = buf.readUnsignedByte(); int type = typeAndFlags >> 2; // int flags = typeAndFlags & 0x3; if (type != MCS_CHANNEL_CONFIRM_PDU) throw new RuntimeException("[" + this + "] ERROR: Incorrect type of MCS AttachUserConfirm PDU. Expected value: 15, actual value: " + type + ", data: " + buf + "."); int rtSuccess = buf.readUnsignedByte() >> 4; if (rtSuccess != 0) throw new RuntimeException("[" + this + "] ERROR: Cannot connect to channel: request failed. Error code: " + rtSuccess + ", channel ID: " + channels[channelRequestsSent - 1] + ", data: " + buf + "."); // Initiator and requested fields MAY be ignored, however, the channelId // field MUST be examined. If the value of the channelId field does not // correspond with the value of the channelId field sent in the previous MCS // Channel Join Request PDU the connection SHOULD be dropped. // Initiator: 1007 (6+1001) // int initator=buf.readUnsignedShort(); buf.skipBytes(2); // Requested channel // int requestedChannel=buf.readUnsignedShort(); buf.skipBytes(2); // Actual channel int actualChannel = buf.readUnsignedShort(); if (actualChannel != channels[channelRequestsSent - 1]) throw new RuntimeException("Unexpeceted channeld ID returned. Expected channeld ID: " + channels[channelRequestsSent - 1] + ", actual channel ID: " + actualChannel + ", data: " + buf + "."); state.channelJoined(actualChannel); buf.unref(); if (channelRequestsSent < channels.length) sendChannelRequest(channels[channelRequestsSent++]); else switchOff(); } @Override protected void onStart() { super.onStart(); sendChannelRequest(channels[channelRequestsSent++]); // Switch off after receiving response(s) } private void sendChannelRequest(int channel) { ByteBuffer buf = new ByteBuffer(5, true); buf.writeByte(0x38); // Channel Join request buf.writeShort(state.serverUserChannelId - 1001); // ChannelJoinRequest::initiator: 1004 buf.writeShort(channel); pushDataToOTOut(buf); } /** * Example. * * @see http://msdn.microsoft.com/en-us/library/cc240834.aspx */ public static void main(String args[]) { // System.setProperty("streamer.Link.debug", "true"); System.setProperty("streamer.Element.debug", "true"); // System.setProperty("streamer.Pipeline.debug", "true"); /* @formatter:off */ byte[] clientRequestPacket = new byte[] { 0x03, 0x00, 0x00, 0x0c, // TPKT Header (length = 12 bytes) 0x02, (byte) 0xf0, (byte) 0x80, // X.224 Data TPDU // PER encoded (ALIGNED variant of BASIC-PER) PDU contents: 0x38, 0x00, 0x03, 0x03, (byte) 0xef, // 0x38: // 0 - --\ // 0 - | // 1 - | CHOICE: From DomainMCSPDU select channelJoinRequest (14) // 1 - | of type ChannelJoinRequest // 1 - | // 0 - --/ // 0 - padding // 0 - padding // 0x00: // 0 - --\ // 0 - | // 0 - | // 0 - | // 0 - | // 0 - | // 0 - | // 0 - | // | ChannelJoinRequest::initiator = 0x03 + 1001 = 1004 // 0x03: | // 0 - | // 0 - | // 0 - | // 0 - | // 0 - | // 1 - | // 1 - | // 0 - --/ // 0x03: // 0 - --\ // 0 - | // 0 - | // 0 - | // 0 - | // 0 - | // 1 - | // 1 - | // | ChannelJoinRequest::channelId = 0x03ef = 1007 // 0xef: | // 1 - | // 1 - | // 1 - | // 0 - | // 1 - | // 1 - | // 1 - | // 1 - --/ }; byte[] serverResponsePacket = new byte[] { // MCS Channel Confirm (byte)0x3e, // result: rt-successful (0) (byte)0x00, // Initiator: 1007 (6+1001) (byte)0x00, (byte)0x06, // Requested channel (byte)0x03, (byte)0xef, // Actual channel (byte)0x03, (byte)0xef, }; /* @formatter:on */ RdpState rdpState = new RdpState(); rdpState.serverUserChannelId = 1004; MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(serverResponsePacket, new byte[] {1, 2, 3})); Element todo = new ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs("channels", new int[] {1007}, rdpState); Element x224 = new ClientX224DataPDU("x224"); Element tpkt = new ClientTpkt("tpkt"); Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(clientRequestPacket)); Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); Pipeline pipeline = new PipelineImpl("test"); pipeline.add(source, todo, x224, tpkt, sink, mainSink); pipeline.link("source", "channels", "mainSink"); pipeline.link("channels >" + OTOUT, "x224", "tpkt", "sink"); pipeline.runMainLoop("source", STDOUT, false, false); } }