Merge pull request #347 from pjd/pullreq
[platform/upstream/freerdp.git] / libfreerdp-core / gcc.c
index 9d7f4fb..c87e87f 100644 (file)
  * limitations under the License.
  */
 
+#include <freerdp/utils/print.h>
+
 #include "gcc.h"
+#include "certificate.h"
 
 /**
  * T.124 GCC is defined in:
  *     conferenceMode                  ConferenceMode OPTIONAL
  * }
  *
+ * ConferenceCreateResponse ::= SEQUENCE
+ * {
+ *     nodeID                          UserID,
+ *     tag                             INTEGER,
+ *     result                          ENUMERATED
+ *     {
+ *             success                         (0),
+ *             userRejected                    (1),
+ *             resourcesNotAvailable           (2),
+ *             rejectedForSymmetryBreaking     (3),
+ *             lockedConferenceNotSupported    (4)
+ *     },
+ *     userData                        UserData OPTIONAL,
+ *     ...
+ * }
+ *
  * ConferenceName ::= SEQUENCE
  * {
  *     numeric                         SimpleNumericString
  *
  * H221NonStandardIdentifier ::= OCTET STRING (SIZE (4..255))
  *
+ * UserID ::= DynamicChannelID
+ *
+ * ChannelID ::= INTEGER (1..65535)
+ * StaticChannelID ::= INTEGER (1..1000)
+ * DynamicChannelID ::= INTEGER (1001..65535)
+ *
  */
 
 /*
  */
 uint8 t124_02_98_oid[6] = { 0, 0, 20, 124, 0, 1 };
 
+uint8 h221_cs_key[4] = "Duca";
+uint8 h221_sc_key[4] = "McDn";
+
+/**
+ * Read a GCC Conference Create Request.\n
+ * @msdn{cc240836}
+ * @param s stream
+ * @param settings rdp settings
+ */
+
+boolean gcc_read_conference_create_request(STREAM* s, rdpSettings* settings)
+{
+       uint16 length;
+       uint8 choice;
+       uint8 number;
+       uint8 selection;
+
+       /* ConnectData */
+       if (!per_read_choice(s, &choice))
+               return false;
+       if (!per_read_object_identifier(s, t124_02_98_oid))
+               return false;
+
+       /* ConnectData::connectPDU (OCTET_STRING) */
+       if (!per_read_length(s, &length))
+               return false;
+
+       /* ConnectGCCPDU */
+       if (!per_read_choice(s, &choice))
+               return false;
+       if (!per_read_selection(s, &selection))
+               return false;
+
+       /* ConferenceCreateRequest::conferenceName */
+       if (!per_read_numeric_string(s, 1)) /* ConferenceName::numeric */
+               return false;
+       if (!per_read_padding(s, 1)) /* padding */
+               return false;
+
+       /* UserData (SET OF SEQUENCE) */
+       if (!per_read_number_of_sets(s, &number) || number != 1) /* one set of UserData */
+               return false;
+       if (!per_read_choice(s, &choice) || choice != 0xC0) /* UserData::value present + select h221NonStandard (1) */
+               return false;
+
+       /* h221NonStandard */
+       if (!per_read_octet_string(s, h221_cs_key, 4, 4)) /* h221NonStandard, client-to-server H.221 key, "Duca" */
+               return false;
+
+       /* userData::value (OCTET_STRING) */
+       if (!per_read_length(s, &length))
+               return false;
+       if (stream_get_left(s) < length)
+               return false;
+       if (!gcc_read_client_data_blocks(s, settings, length))
+               return false;
+
+       return true;
+}
+
 /**
  * Write a GCC Conference Create Request.\n
  * @msdn{cc240836}
@@ -105,7 +190,7 @@ uint8 t124_02_98_oid[6] = { 0, 0, 20, 124, 0, 1 };
  * @param user_data client data blocks
  */
 
-void gcc_write_create_conference_request(STREAM* s, STREAM* user_data)
+void gcc_write_conference_create_request(STREAM* s, STREAM* user_data)
 {
        /* ConnectData */
        per_write_choice(s, 0); /* From Key select object (0) of type OBJECT_IDENTIFIER */
@@ -119,7 +204,7 @@ void gcc_write_create_conference_request(STREAM* s, STREAM* user_data)
        per_write_selection(s, 0x08); /* select optional userData from ConferenceCreateRequest */
 
        /* ConferenceCreateRequest::conferenceName */
-       per_write_numeric_string(s, "1", 1, 1); /* ConferenceName::numeric */
+       per_write_numeric_string(s, (uint8*)"1", 1, 1); /* ConferenceName::numeric */
        per_write_padding(s, 1); /* padding */
 
        /* UserData (SET OF SEQUENCE) */
@@ -127,12 +212,231 @@ void gcc_write_create_conference_request(STREAM* s, STREAM* user_data)
        per_write_choice(s, 0xC0); /* UserData::value present + select h221NonStandard (1) */
 
        /* h221NonStandard */
-       per_write_octet_string(s, "Duca", 4, 4); /* h221NonStandard, client-to-server H.221 key, "Duca" */
+       per_write_octet_string(s, h221_cs_key, 4, 4); /* h221NonStandard, client-to-server H.221 key, "Duca" */
 
        /* userData::value (OCTET_STRING) */
        per_write_octet_string(s, user_data->data, stream_get_length(user_data), 0); /* array of client data blocks */
 }
 
+boolean gcc_read_conference_create_response(STREAM* s, rdpSettings* settings)
+{
+       uint16 length;
+       uint32 tag;
+       uint16 nodeID;
+       uint8 result;
+       uint8 choice;
+       uint8 number;
+
+       /* ConnectData */
+       per_read_choice(s, &choice);
+       per_read_object_identifier(s, t124_02_98_oid);
+
+       /* ConnectData::connectPDU (OCTET_STRING) */
+       per_read_length(s, &length);
+
+       /* ConnectGCCPDU */
+       per_read_choice(s, &choice);
+
+       /* ConferenceCreateResponse::nodeID (UserID) */
+       per_read_integer16(s, &nodeID, 1001);
+
+       /* ConferenceCreateResponse::tag (INTEGER) */
+       per_read_integer(s, &tag);
+
+       /* ConferenceCreateResponse::result (ENUMERATED) */
+       per_read_enumerated(s, &result, MCS_Result_enum_length);
+
+       /* number of UserData sets */
+       per_read_number_of_sets(s, &number);
+
+       /* UserData::value present + select h221NonStandard (1) */
+       per_read_choice(s, &choice);
+
+       /* h221NonStandard */
+       if (!per_read_octet_string(s, h221_sc_key, 4, 4)) /* h221NonStandard, server-to-client H.221 key, "McDn" */
+               return false;
+
+       /* userData (OCTET_STRING) */
+       per_read_length(s, &length);
+       if (!gcc_read_server_data_blocks(s, settings, length))
+       {
+               printf("gcc_read_conference_create_response: gcc_read_server_data_blocks failed\n");
+               return false;
+       }
+
+       return true;
+}
+
+void gcc_write_conference_create_response(STREAM* s, STREAM* user_data)
+{
+       /* ConnectData */
+       per_write_choice(s, 0);
+       per_write_object_identifier(s, t124_02_98_oid);
+
+       /* ConnectData::connectPDU (OCTET_STRING) */
+       per_write_length(s, stream_get_length(user_data) + 2);
+
+       /* ConnectGCCPDU */
+       per_write_choice(s, 0x14);
+
+       /* ConferenceCreateResponse::nodeID (UserID) */
+       per_write_integer16(s, 0x79F3, 1001);
+
+       /* ConferenceCreateResponse::tag (INTEGER) */
+       per_write_integer(s, 1);
+
+       /* ConferenceCreateResponse::result (ENUMERATED) */
+       per_write_enumerated(s, 0, MCS_Result_enum_length);
+
+       /* number of UserData sets */
+       per_write_number_of_sets(s, 1);
+
+       /* UserData::value present + select h221NonStandard (1) */
+       per_write_choice(s, 0xC0);
+
+       /* h221NonStandard */
+       per_write_octet_string(s, h221_sc_key, 4, 4); /* h221NonStandard, server-to-client H.221 key, "McDn" */
+
+       /* userData (OCTET_STRING) */
+       per_write_octet_string(s, user_data->data, stream_get_length(user_data), 0); /* array of server data blocks */
+}
+
+boolean gcc_read_client_data_blocks(STREAM* s, rdpSettings *settings, int length)
+{
+       uint16 type;
+       uint16 blockLength;
+       int pos;
+
+       while (length > 0)
+       {
+               pos = stream_get_pos(s);
+               gcc_read_user_data_header(s, &type, &blockLength);
+
+               switch (type)
+               {
+                       case CS_CORE:
+                               if (!gcc_read_client_core_data(s, settings, blockLength - 4))
+                                       return false;
+                               break;
+
+                       case CS_SECURITY:
+                               if (!gcc_read_client_security_data(s, settings, blockLength - 4))
+                                       return false;
+                               break;
+
+                       case CS_NET:
+                               if (!gcc_read_client_network_data(s, settings, blockLength - 4))
+                                       return false;
+                               break;
+
+                       case CS_CLUSTER:
+                               if (!gcc_read_client_cluster_data(s, settings, blockLength - 4))
+                                       return false;
+                               break;
+
+                       case CS_MONITOR:
+                               if (!gcc_read_client_monitor_data(s, settings, blockLength - 4))
+                                       return false;
+                               break;
+
+                       default:
+                               break;
+               }
+
+               length -= blockLength;
+               stream_set_pos(s, pos + blockLength);
+       }
+
+       return true;
+}
+
+void gcc_write_client_data_blocks(STREAM* s, rdpSettings* settings)
+{
+       gcc_write_client_core_data(s, settings);
+       gcc_write_client_cluster_data(s, settings);
+       gcc_write_client_security_data(s, settings);
+       gcc_write_client_network_data(s, settings);
+
+       /* extended client data supported */
+
+       if (settings->negotiationFlags)
+               gcc_write_client_monitor_data(s, settings);
+}
+
+boolean gcc_read_server_data_blocks(STREAM* s, rdpSettings* settings, int length)
+{
+       uint16 type;
+       uint16 offset = 0;
+       uint16 blockLength;
+       uint8* holdp;
+
+       while (offset < length)
+       {
+               holdp = s->p;
+
+               if (!gcc_read_user_data_header(s, &type, &blockLength))
+               {
+                       printf("gcc_read_server_data_blocks: gcc_read_user_data_header failed\n");
+                       return false;
+               }
+
+               switch (type)
+               {
+                       case SC_CORE:
+                               if (!gcc_read_server_core_data(s, settings))
+                               {
+                                       printf("gcc_read_server_data_blocks: gcc_read_server_core_data failed\n");
+                                       return false;
+                               }
+                               break;
+
+                       case SC_SECURITY:
+                               if (!gcc_read_server_security_data(s, settings))
+                               {
+                                       printf("gcc_read_server_data_blocks: gcc_read_server_security_data failed\n");
+                                       return false;
+                               }
+                               break;
+
+                       case SC_NET:
+                               if (!gcc_read_server_network_data(s, settings))
+                               {
+                                       printf("gcc_read_server_data_blocks: gcc_read_server_network_data failed\n");
+                                       return false;
+                               }
+                               break;
+
+                       default:
+                               break;
+               }
+               offset += blockLength;
+               s->p = holdp + blockLength;
+       }
+
+       return true;
+}
+
+void gcc_write_server_data_blocks(STREAM* s, rdpSettings *settings)
+{
+       gcc_write_server_core_data(s, settings);
+       gcc_write_server_network_data(s, settings);
+       gcc_write_server_security_data(s, settings);
+}
+
+boolean gcc_read_user_data_header(STREAM* s, uint16* type, uint16* length)
+{
+       stream_read_uint16(s, *type); /* type */
+       stream_read_uint16(s, *length); /* length */
+
+       if (*length < 4)
+               return false;
+
+       if (stream_get_left(s) < *length - 4)
+               return false;
+
+       return true;
+}
+
 /**
  * Write a user data header (TS_UD_HEADER).\n
  * @msdn{cc240509}
@@ -148,6 +452,161 @@ void gcc_write_user_data_header(STREAM* s, uint16 type, uint16 length)
 }
 
 /**
+ * Read a client core data block (TS_UD_CS_CORE).\n
+ * @msdn{cc240510}
+ * @param s stream
+ * @param settings rdp settings
+ */
+
+boolean gcc_read_client_core_data(STREAM* s, rdpSettings *settings, uint16 blockLength)
+{
+       uint32 version;
+       uint16 colorDepth = 0;
+       uint16 postBeta2ColorDepth = 0;
+       uint16 highColorDepth = 0;
+       uint16 supportedColorDepths = 0;
+       uint16 earlyCapabilityFlags = 0;
+       uint32 serverSelectedProtocol = 0;
+       char* str;
+
+       /* Length of all required fields, until imeFileName */
+       if (blockLength < 128)
+               return false;
+
+       stream_read_uint32(s, version); /* version */
+       settings->rdp_version = (version == RDP_VERSION_4 ? 4 : 7);
+
+       stream_read_uint16(s, settings->width); /* desktopWidth */
+       stream_read_uint16(s, settings->height); /* desktopHeight */
+       stream_read_uint16(s, colorDepth); /* colorDepth */
+       stream_seek_uint16(s); /* SASSequence (Secure Access Sequence) */
+       stream_read_uint32(s, settings->kbd_layout); /* keyboardLayout */
+       stream_read_uint32(s, settings->client_build); /* clientBuild */
+       
+       /* clientName (32 bytes, null-terminated unicode, truncated to 15 characters) */
+       str = freerdp_uniconv_in(settings->uniconv, stream_get_tail(s), 32);
+       stream_seek(s, 32);
+       snprintf(settings->client_hostname, sizeof(settings->client_hostname), "%s", str);
+       xfree(str);
+
+       stream_read_uint32(s, settings->kbd_type); /* keyboardType */
+       stream_read_uint32(s, settings->kbd_subtype); /* keyboardSubType */
+       stream_read_uint32(s, settings->kbd_fn_keys); /* keyboardFunctionKey */
+
+       stream_seek(s, 64); /* imeFileName */
+
+       blockLength -= 128;
+
+       /**
+        * The following fields are all optional. If one field is present, all of the preceding
+        * fields MUST also be present. If one field is not present, all of the subsequent fields
+        * MUST NOT be present.
+        * We must check the bytes left before reading each field.
+        */
+
+       do
+       {
+               if (blockLength < 2)
+                       break;
+               stream_read_uint16(s, postBeta2ColorDepth); /* postBeta2ColorDepth */
+               blockLength -= 2;
+
+               if (blockLength < 2)
+                       break;
+               stream_seek_uint16(s); /* clientProductID */
+               blockLength -= 2;
+
+               if (blockLength < 4)
+                       break;
+               stream_seek_uint32(s); /* serialNumber */
+               blockLength -= 4;
+
+               if (blockLength < 2)
+                       break;
+               stream_read_uint16(s, highColorDepth); /* highColorDepth */
+               blockLength -= 2;
+
+               if (blockLength < 2)
+                       break;
+               stream_read_uint16(s, supportedColorDepths); /* supportedColorDepths */
+               blockLength -= 2;
+
+               if (blockLength < 2)
+                       break;
+               stream_read_uint16(s, earlyCapabilityFlags); /* earlyCapabilityFlags */
+               blockLength -= 2;
+
+               if (blockLength < 64)
+                       break;
+               str = freerdp_uniconv_in(settings->uniconv, stream_get_tail(s), 64);
+               stream_seek(s, 64);
+               snprintf(settings->client_product_id, sizeof(settings->client_product_id), "%s", str);
+               xfree(str);
+               blockLength -= 64;
+
+               if (blockLength < 1)
+                       break;
+               stream_read_uint8(s, settings->performance_flags); /* connectionType */
+               blockLength -= 1;
+
+               if (blockLength < 1)
+                       break;
+               stream_seek_uint8(s); /* pad1octet */
+               blockLength -= 1;
+
+               if (blockLength < 4)
+                       break;
+               stream_read_uint32(s, serverSelectedProtocol); /* serverSelectedProtocol */
+               blockLength -= 4;
+
+               if (settings->selected_protocol != serverSelectedProtocol)
+                       return false;
+       } while (0);
+
+       if (highColorDepth > 0)
+               settings->color_depth = highColorDepth;
+       else if (postBeta2ColorDepth > 0)
+       {
+               switch (postBeta2ColorDepth)
+               {
+                       case RNS_UD_COLOR_4BPP:
+                               settings->color_depth = 4;
+                               break;
+                       case RNS_UD_COLOR_8BPP:
+                               settings->color_depth = 8;
+                               break;
+                       case RNS_UD_COLOR_16BPP_555:
+                               settings->color_depth = 15;
+                               break;
+                       case RNS_UD_COLOR_16BPP_565:
+                               settings->color_depth = 16;
+                               break;
+                       case RNS_UD_COLOR_24BPP:
+                               settings->color_depth = 24;
+                               break;
+                       default:
+                               return false;
+               }
+       }
+       else
+       {
+               switch (colorDepth)
+               {
+                       case RNS_UD_COLOR_4BPP:
+                               settings->color_depth = 4;
+                               break;
+                       case RNS_UD_COLOR_8BPP:
+                               settings->color_depth = 8;
+                               break;
+                       default:
+                               return false;
+               }
+       }
+
+       return true;
+}
+
+/**
  * Write a client core data block (TS_UD_CS_CORE).\n
  * @msdn{cc240510}
  * @param s stream
@@ -157,13 +616,13 @@ void gcc_write_user_data_header(STREAM* s, uint16 type, uint16 length)
 void gcc_write_client_core_data(STREAM* s, rdpSettings *settings)
 {
        uint32 version;
-       uint8* clientName;
+       char* clientName;
        size_t clientNameLength;
        uint8 connectionType;
        uint16 highColorDepth;
        uint16 supportedColorDepths;
        uint16 earlyCapabilityFlags;
-       uint8* clientDigProductId;
+       char* clientDigProductId;
        size_t clientDigProductIdLength;
 
        gcc_write_user_data_header(s, CS_CORE, 216);
@@ -202,16 +661,20 @@ void gcc_write_client_core_data(STREAM* s, rdpSettings *settings)
        stream_write_uint32(s, 0); /* serialNumber (should be initialized to 0) */
 
        highColorDepth = MIN(settings->color_depth, 24);
-       supportedColorDepths = RNS_UD_24BPP_SUPPORT | RNS_UD_16BPP_SUPPORT | RNS_UD_15BPP_SUPPORT;
 
-       connectionType = 0;
+       supportedColorDepths =
+                       RNS_UD_24BPP_SUPPORT |
+                       RNS_UD_16BPP_SUPPORT |
+                       RNS_UD_15BPP_SUPPORT;
+
+       connectionType = settings->connection_type;
        earlyCapabilityFlags = RNS_UD_CS_SUPPORT_ERRINFO_PDU;
 
-       if (settings->performance_flags == PERF_FLAG_NONE)
-       {
-               earlyCapabilityFlags |= RNS_UD_CS_VALID_CONNECTION_TYPE;
+       if (settings->rfx_codec)
                connectionType = CONNECTION_TYPE_LAN;
-       }
+
+       if (connectionType != 0)
+               earlyCapabilityFlags |= RNS_UD_CS_VALID_CONNECTION_TYPE;
 
        if (settings->color_depth == 32)
        {
@@ -241,6 +704,49 @@ void gcc_write_client_core_data(STREAM* s, rdpSettings *settings)
        stream_write_uint32(s, settings->selected_protocol); /* serverSelectedProtocol */
 }
 
+boolean gcc_read_server_core_data(STREAM* s, rdpSettings *settings)
+{
+       uint32 version;
+       uint32 clientRequestedProtocols;
+
+       stream_read_uint32(s, version); /* version */
+       stream_read_uint32(s, clientRequestedProtocols); /* clientRequestedProtocols */
+
+       if (version == RDP_VERSION_4 && settings->rdp_version > 4)
+               settings->rdp_version = 4;
+       else if (version == RDP_VERSION_5_PLUS && settings->rdp_version < 5)
+               settings->rdp_version = 7;
+
+       return true;
+}
+
+void gcc_write_server_core_data(STREAM* s, rdpSettings *settings)
+{
+       gcc_write_user_data_header(s, SC_CORE, 12);
+
+       stream_write_uint32(s, settings->rdp_version == 4 ? RDP_VERSION_4 : RDP_VERSION_5_PLUS);
+       stream_write_uint32(s, settings->requested_protocols); /* clientRequestedProtocols */
+}
+
+/**
+ * Read a client security data block (TS_UD_CS_SEC).\n
+ * @msdn{cc240511}
+ * @param s stream
+ * @param settings rdp settings
+ */
+
+boolean gcc_read_client_security_data(STREAM* s, rdpSettings *settings, uint16 blockLength)
+{
+       if (blockLength < 8)
+               return false;
+
+       stream_read_uint32(s, settings->encryption_method); /* encryptionMethods */
+       if (settings->encryption_method == 0)
+               stream_read_uint32(s, settings->encryption_method); /* extEncryptionMethods */
+
+       return true;
+}
+
 /**
  * Write a client security data block (TS_UD_CS_SEC).\n
  * @msdn{cc240511}
@@ -250,27 +756,117 @@ void gcc_write_client_core_data(STREAM* s, rdpSettings *settings)
 
 void gcc_write_client_security_data(STREAM* s, rdpSettings *settings)
 {
-       uint16 encryptionMethods;
-
        gcc_write_user_data_header(s, CS_SECURITY, 12);
 
-       encryptionMethods =
-                       ENCRYPTION_40BIT_FLAG |
-                       ENCRYPTION_56BIT_FLAG |
-                       ENCRYPTION_128BIT_FLAG |
-                       ENCRYPTION_FIPS_FLAG;
-
-       if (settings->encryption > 0)
+       if (settings->encryption)
        {
-               stream_write_uint32(s, encryptionMethods); /* encryptionMethods */
+               stream_write_uint32(s, settings->encryption_method); /* encryptionMethods */
                stream_write_uint32(s, 0); /* extEncryptionMethods */
        }
        else
        {
                /* French locale, disable encryption */
                stream_write_uint32(s, 0); /* encryptionMethods */
-               stream_write_uint32(s, encryptionMethods); /* extEncryptionMethods */
+               stream_write_uint32(s, settings->encryption_method); /* extEncryptionMethods */
+       }
+}
+
+boolean gcc_read_server_security_data(STREAM* s, rdpSettings *settings)
+{
+       uint8* data;
+       uint32 length;
+       uint32 serverRandomLen;
+       uint32 serverCertLen;
+
+       stream_read_uint32(s, settings->encryption_method); /* encryptionMethod */
+       stream_read_uint32(s, settings->encryption_level); /* encryptionLevel */
+
+       if (settings->encryption_method == 0 && settings->encryption_level == 0)
+       {
+               /* serverRandom and serverRandom must not be present */
+               settings->encryption = false;
+               settings->encryption_method = ENCRYPTION_METHOD_NONE;
+               settings->encryption_level = ENCRYPTION_LEVEL_NONE;
+               return true;
+       }
+
+       stream_read_uint32(s, serverRandomLen); /* serverRandomLen */
+       stream_read_uint32(s, serverCertLen); /* serverCertLen */
+
+       if (serverRandomLen > 0)
+       {
+               /* serverRandom */
+               freerdp_blob_alloc(settings->server_random, serverRandomLen);
+               stream_read(s, settings->server_random->data, serverRandomLen);
+       }
+       else
+       {
+               return false;
+       }
+
+       if (serverCertLen > 0)
+       {
+               /* serverCertificate */
+               freerdp_blob_alloc(settings->server_certificate, serverCertLen);
+               stream_read(s, settings->server_certificate->data, serverCertLen);
+               certificate_free(settings->server_cert);
+               settings->server_cert = certificate_new();
+               data = settings->server_certificate->data;
+               length = settings->server_certificate->length;
+
+               if (!certificate_read_server_certificate(settings->server_cert, data, length))
+                       return false;
+       }
+       else
+       {
+               return false;
        }
+
+       return true;
+}
+
+void gcc_write_server_security_data(STREAM* s, rdpSettings *settings)
+{
+       gcc_write_user_data_header(s, SC_SECURITY, 12);
+
+       stream_write_uint32(s, ENCRYPTION_METHOD_NONE); /* encryptionMethod */
+       stream_write_uint32(s, ENCRYPTION_LEVEL_NONE); /* encryptionLevel */
+#if 0
+       stream_write_uint32(s, 0); /* serverRandomLen */
+       stream_write_uint32(s, 0); /* serverCertLen */
+#endif
+}
+
+/**
+ * Read a client network data block (TS_UD_CS_NET).\n
+ * @msdn{cc240512}
+ * @param s stream
+ * @param settings rdp settings
+ */
+
+boolean gcc_read_client_network_data(STREAM* s, rdpSettings *settings, uint16 blockLength)
+{
+       int i;
+
+       if (blockLength < 4)
+               return false;
+
+       stream_read_uint32(s, settings->num_channels); /* channelCount */
+       if (blockLength < 4 + settings->num_channels * 12)
+               return false;
+       if (settings->num_channels > 16)
+               return false;
+
+       /* channelDefArray */
+       for (i = 0; i < settings->num_channels; i++)
+       {
+               /* CHANNEL_DEF */
+               stream_read(s, settings->channels[i].name, 8); /* name (8 bytes) */
+               stream_read_uint32(s, settings->channels[i].options); /* options (4 bytes) */
+               settings->channels[i].channel_id = MCS_GLOBAL_CHANNEL_ID + 1 + i;
+       }
+
+       return true;
 }
 
 /**
@@ -302,6 +898,74 @@ void gcc_write_client_network_data(STREAM* s, rdpSettings *settings)
        }
 }
 
+boolean gcc_read_server_network_data(STREAM* s, rdpSettings *settings)
+{
+       int i;
+       uint16 MCSChannelId;
+       uint16 channelCount;
+       uint16 channelId;
+
+       stream_read_uint16(s, MCSChannelId); /* MCSChannelId */
+       stream_read_uint16(s, channelCount); /* channelCount */
+
+       if (channelCount != settings->num_channels)
+       {
+               printf("requested %d channels, got %d instead\n",
+                               settings->num_channels, channelCount);
+       }
+
+       for (i = 0; i < channelCount; i++)
+       {
+               stream_read_uint16(s, channelId); /* channelId */
+               settings->channels[i].channel_id = channelId;
+       }
+
+       if (channelCount % 2 == 1)
+               stream_seek(s, 2); /* padding */
+
+       return true;
+}
+
+void gcc_write_server_network_data(STREAM* s, rdpSettings *settings)
+{
+       int i;
+
+       gcc_write_user_data_header(s, SC_NET, 8 + settings->num_channels * 2 + (settings->num_channels % 2 == 1 ? 2 : 0));
+
+       stream_write_uint16(s, MCS_GLOBAL_CHANNEL_ID); /* MCSChannelId */
+       stream_write_uint16(s, settings->num_channels); /* channelCount */
+
+       for (i = 0; i < settings->num_channels; i++)
+       {
+               stream_write_uint16(s, settings->channels[i].channel_id);
+       }
+
+       if (settings->num_channels % 2 == 1)
+               stream_write_uint16(s, 0);
+}
+
+/**
+ * Read a client cluster data block (TS_UD_CS_CLUSTER).\n
+ * @msdn{cc240514}
+ * @param s stream
+ * @param settings rdp settings
+ */
+
+boolean gcc_read_client_cluster_data(STREAM* s, rdpSettings *settings, uint16 blockLength)
+{
+       uint32 flags;
+
+       if (blockLength < 8)
+               return false;
+
+       stream_read_uint32(s, flags); /* flags */
+
+       if ((flags | REDIRECTED_SESSIONID_FIELD_VALID))
+               stream_read_uint32(s, settings->redirected_session_id); /* redirectedSessionID */
+
+       return true;
+}
+
 /**
  * Write a client cluster data block (TS_UD_CS_CLUSTER).\n
  * @msdn{cc240514}
@@ -325,6 +989,19 @@ void gcc_write_client_cluster_data(STREAM* s, rdpSettings *settings)
 }
 
 /**
+ * Read a client monitor data block (TS_UD_CS_MONITOR).\n
+ * @msdn{dd305336}
+ * @param s stream
+ * @param settings rdp settings
+ */
+
+boolean gcc_read_client_monitor_data(STREAM* s, rdpSettings *settings, uint16 blockLength)
+{
+       printf("CS_MONITOR\n");
+       return true;
+}
+
+/**
  * Write a client monitor data block (TS_UD_CS_MONITOR).\n
  * @msdn{dd305336}
  * @param s stream
@@ -361,3 +1038,4 @@ void gcc_write_client_monitor_data(STREAM* s, rdpSettings *settings)
                }
        }
 }
+