static int rdp_recv_tpkt_pdu(rdpRdp* rdp, wStream* s)
{
	int rc = 0;
	UINT16 length;
	UINT16 pduType;
	UINT16 pduLength;
	UINT16 pduSource;
	UINT16 channelId = 0;
	UINT16 securityFlags = 0;

	if (!rdp_read_header(rdp, s, &length, &channelId))
	{
		WLog_ERR(TAG, "Incorrect RDP header.");
		return -1;
	}

	if (freerdp_shall_disconnect(rdp->instance))
		return 0;

	if (rdp->autodetect->bandwidthMeasureStarted)
	{
		rdp->autodetect->bandwidthMeasureByteCount += length;
	}

	if (rdp->settings->UseRdpSecurityLayer)
	{
		if (!rdp_read_security_header(s, &securityFlags, &length))
		{
			WLog_ERR(TAG, "rdp_recv_tpkt_pdu: rdp_read_security_header() fail");
			return -1;
		}

		if (securityFlags & (SEC_ENCRYPT | SEC_REDIRECTION_PKT))
		{
			if (!rdp_decrypt(rdp, s, &length, securityFlags))
			{
				WLog_ERR(TAG, "rdp_decrypt failed");
				return -1;
			}
		}

		if (securityFlags & SEC_REDIRECTION_PKT)
		{
			/*
			 * [MS-RDPBCGR] 2.2.13.2.1
			 *  - no share control header, nor the 2 byte pad
			 */
			Stream_Rewind(s, 2);
			rdp->inPackets++;

			rc = rdp_recv_enhanced_security_redirection_packet(rdp, s);
			goto out;
		}
	}

	if (channelId == MCS_GLOBAL_CHANNEL_ID)
	{
		while (Stream_GetRemainingLength(s) > 3)
		{
			size_t startheader, endheader, start, end, diff, headerdiff;

			startheader = Stream_GetPosition(s);
			if (!rdp_read_share_control_header(s, &pduLength, &pduType, &pduSource))
			{
				WLog_ERR(TAG, "rdp_recv_tpkt_pdu: rdp_read_share_control_header() fail");
				return -1;
			}
			start = endheader = Stream_GetPosition(s);
			headerdiff = endheader - startheader;
			if (pduLength < headerdiff)
			{
				WLog_ERR(
				    TAG,
				    "rdp_recv_tpkt_pdu: rdp_read_share_control_header() invalid pduLength %" PRIu16,
				    pduLength);
				return -1;
			}
			pduLength -= headerdiff;

			rdp->settings->PduSource = pduSource;
			rdp->inPackets++;

			switch (pduType)
			{
				case PDU_TYPE_DATA:
					rc = rdp_recv_data_pdu(rdp, s);
					if (rc < 0)
						return rc;
					break;

				case PDU_TYPE_DEACTIVATE_ALL:
					if (!rdp_recv_deactivate_all(rdp, s))
					{
						WLog_ERR(TAG, "rdp_recv_tpkt_pdu: rdp_recv_deactivate_all() fail");
						return -1;
					}

					break;

				case PDU_TYPE_SERVER_REDIRECTION:
					return rdp_recv_enhanced_security_redirection_packet(rdp, s);

				case PDU_TYPE_FLOW_RESPONSE:
				case PDU_TYPE_FLOW_STOP:
				case PDU_TYPE_FLOW_TEST:
					WLog_DBG(TAG, "flow message 0x%04" PRIX16 "", pduType);
					/* http://msdn.microsoft.com/en-us/library/cc240576.aspx */
					if (!Stream_SafeSeek(s, pduLength))
						return -1;
					break;

				default:
					WLog_ERR(TAG, "incorrect PDU type: 0x%04" PRIX16 "", pduType);
					break;
			}

			end = Stream_GetPosition(s);
			diff = end - start;
			if (diff != pduLength)
			{
				WLog_WARN(TAG,
				          "pduType %s not properly parsed, %" PRIdz
				          " bytes remaining unhandled. Skipping.",
				          pdu_type_to_str(pduType), diff);
				if (!Stream_SafeSeek(s, pduLength))
					return -1;
			}
		}
	}
	else if (rdp->mcs->messageChannelId && (channelId == rdp->mcs->messageChannelId))
	{
		if (!rdp->settings->UseRdpSecurityLayer)
			if (!rdp_read_security_header(s, &securityFlags, NULL))
				return -1;
		rdp->inPackets++;
		rc = rdp_recv_message_channel_pdu(rdp, s, securityFlags);
	}
	else
	{
		rdp->inPackets++;

		if (!freerdp_channel_process(rdp->instance, s, channelId, length))
			return -1;
	}

out:
	if (!tpkt_ensure_stream_consumed(s, length))
		return -1;
	return rc;
}