mirror of https://github.com/FreeRDP/FreeRDP
work on rdp encryption
This commit is contained in:
parent
afc9ac89ae
commit
2ac4a5fa96
|
@ -53,7 +53,8 @@ endif()
|
|||
|
||||
# Compiler-specific flags
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-unused-but-set-variable")
|
||||
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-unused-but-set-variable")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -lncurses")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
|
||||
endif()
|
||||
|
|
|
@ -198,6 +198,13 @@ struct rdp_settings
|
|||
rdpBlob server_certificate;
|
||||
struct rdp_certificate* server_cert;
|
||||
|
||||
uint8 sign_key[16];
|
||||
uint8 decrypt_key[16];
|
||||
uint8 encrypt_key[16];
|
||||
uint8 decrypt_update_key[16];
|
||||
uint8 encrypt_update_key[16];
|
||||
int rc4_key_len;
|
||||
|
||||
boolean console_audio;
|
||||
boolean console_session;
|
||||
uint32 redirected_session_id;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "connection.h"
|
||||
#include "info.h"
|
||||
#include "per.h"
|
||||
|
||||
/**
|
||||
* Connection Sequence
|
||||
|
@ -107,10 +108,12 @@ boolean rdp_client_connect(rdpRdp* rdp)
|
|||
static boolean rdp_establish_keys(rdpRdp* rdp)
|
||||
{
|
||||
uint8 client_random[32];
|
||||
uint8 crypt_client_random[256];
|
||||
uint8 crypt_client_random[256 + 8];
|
||||
uint32 key_len;
|
||||
uint8* mod;
|
||||
uint8* exp;
|
||||
uint32 length;
|
||||
STREAM* s;
|
||||
|
||||
printf("rdp_establish_keys:\n");
|
||||
if (rdp->settings->encryption == False)
|
||||
|
@ -119,12 +122,51 @@ static boolean rdp_establish_keys(rdpRdp* rdp)
|
|||
return True;
|
||||
}
|
||||
|
||||
memset(client_random, 0x5e, 32); /* TODO: get real random */
|
||||
crypto_nonce(client_random, 32);
|
||||
/* encrypt client random */
|
||||
memset(crypt_client_random, 0, sizeof(crypt_client_random));
|
||||
memset(client_random, 0x5e, 32);
|
||||
//crypto_nonce(client_random, 32);
|
||||
printf("client random\n");
|
||||
freerdp_hexdump(client_random, 32);
|
||||
key_len = rdp->settings->server_cert->cert_info.modulus.length;
|
||||
printf("key_len %d %d %d\n", key_len, rdp->mcs->user_id, MCS_BASE_CHANNEL_ID);
|
||||
mod = rdp->settings->server_cert->cert_info.modulus.data;
|
||||
exp = rdp->settings->server_cert->cert_info.exponent;
|
||||
crypto_rsa_encrypt(client_random, 32, key_len, mod, exp, crypt_client_random);
|
||||
printf("client crypt random\n");
|
||||
freerdp_hexdump(crypt_client_random, key_len);
|
||||
|
||||
/* send crypt client random to server */
|
||||
length = 7 + 8 + 4 + 4 + key_len + 8;
|
||||
s = transport_send_stream_init(rdp->mcs->transport, length);
|
||||
tpkt_write_header(s, length);
|
||||
tpdu_write_header(s, 2, 0xf0);
|
||||
per_write_choice(s, DomainMCSPDU_SendDataRequest << 2);
|
||||
per_write_integer16(s, rdp->mcs->user_id, MCS_BASE_CHANNEL_ID);
|
||||
per_write_integer16(s, MCS_GLOBAL_CHANNEL_ID, 0);
|
||||
stream_write_uint8(s, 0x70);
|
||||
length = (4 + 4 + key_len + 8) | 0x8000;
|
||||
stream_write_uint16_be(s, length);
|
||||
stream_write_uint32(s, 1); /* SEC_CLIENT_RANDOM */
|
||||
length = key_len + 8;
|
||||
stream_write_uint32(s, length);
|
||||
memcpy(s->p, crypt_client_random, length);
|
||||
stream_seek(s, length);
|
||||
if (transport_write(rdp->mcs->transport, s) < 0)
|
||||
{
|
||||
return False;
|
||||
}
|
||||
|
||||
/* now calculate encrypt / decrypt and upate keys */
|
||||
if (!security_establish_keys(client_random, rdp->settings))
|
||||
{
|
||||
return False;
|
||||
}
|
||||
|
||||
rdp->rc4_decrypt_key = crypto_rc4_init(rdp->settings->decrypt_key, rdp->settings->rc4_key_len);
|
||||
rdp->rc4_encrypt_key = crypto_rc4_init(rdp->settings->encrypt_key, rdp->settings->rc4_key_len);
|
||||
|
||||
rdp->do_crypt = True;
|
||||
|
||||
return True;
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ uint16 fastpath_read_header(rdpFastPath* fastpath, STREAM* s)
|
|||
if (fastpath != NULL)
|
||||
{
|
||||
fastpath->encryptionFlags = (header & 0xC0) >> 6;
|
||||
printf("fastpath_read_header: fastpath->encryptionFlags %d\n", fastpath->encryptionFlags);
|
||||
fastpath->numberEvents = (header & 0x3C) >> 2;
|
||||
}
|
||||
|
||||
|
|
|
@ -584,11 +584,10 @@ boolean rdp_send_client_info(rdpRdp* rdp)
|
|||
{
|
||||
STREAM* s;
|
||||
|
||||
//rdp->settings->crypt_flags |= SEC_INFO_PKT;
|
||||
rdp->sec_flags |= SEC_INFO_PKT;
|
||||
s = rdp_send_stream_init(rdp);
|
||||
|
||||
rdp_write_security_header(s, SEC_INFO_PKT);
|
||||
rdp_write_info_packet(s, rdp->settings);
|
||||
|
||||
return rdp_send(rdp, s, MCS_GLOBAL_CHANNEL_ID);
|
||||
}
|
||||
|
||||
|
|
|
@ -179,7 +179,7 @@ boolean license_recv(rdpLicense* license, STREAM* s)
|
|||
rdp_read_security_header(s, &sec_flags);
|
||||
if (!(sec_flags & SEC_LICENSE_PKT))
|
||||
{
|
||||
printf("Unexpected license packet.\n");
|
||||
printf("Unexpected license packet. got 0x%4.4x need bit 0x%4.4x set\n", sec_flags, SEC_LICENSE_PKT);
|
||||
return False;
|
||||
}
|
||||
|
||||
|
|
|
@ -146,6 +146,21 @@ void rdp_write_share_data_header(STREAM* s, uint16 length, uint8 type, uint32 sh
|
|||
stream_write_uint16(s, 0); /* compressedLength (2 bytes) */
|
||||
}
|
||||
|
||||
static int rdp_security_stream_init(rdpRdp* rdp, STREAM* s)
|
||||
{
|
||||
printf("rdp_security_stream_init:\n");
|
||||
if (rdp->do_crypt)
|
||||
{
|
||||
stream_seek(s, 12);
|
||||
rdp->sec_flags |= SEC_ENCRYPT;
|
||||
}
|
||||
else if (rdp->sec_flags != 0)
|
||||
{
|
||||
stream_seek(s, 4);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize an RDP packet stream.\n
|
||||
* @param rdp rdp module
|
||||
|
@ -155,8 +170,10 @@ void rdp_write_share_data_header(STREAM* s, uint16 length, uint8 type, uint32 sh
|
|||
STREAM* rdp_send_stream_init(rdpRdp* rdp)
|
||||
{
|
||||
STREAM* s;
|
||||
|
||||
s = transport_send_stream_init(rdp->transport, 2048);
|
||||
stream_seek(s, RDP_PACKET_HEADER_LENGTH);
|
||||
rdp_security_stream_init(rdp, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -165,6 +182,7 @@ STREAM* rdp_pdu_init(rdpRdp* rdp)
|
|||
STREAM* s;
|
||||
s = transport_send_stream_init(rdp->transport, 2048);
|
||||
stream_seek(s, RDP_PACKET_HEADER_LENGTH);
|
||||
rdp_security_stream_init(rdp, s);
|
||||
stream_seek(s, RDP_SHARE_CONTROL_HEADER_LENGTH);
|
||||
return s;
|
||||
}
|
||||
|
@ -174,6 +192,7 @@ STREAM* rdp_data_pdu_init(rdpRdp* rdp)
|
|||
STREAM* s;
|
||||
s = transport_send_stream_init(rdp->transport, 2048);
|
||||
stream_seek(s, RDP_PACKET_HEADER_LENGTH);
|
||||
rdp_security_stream_init(rdp, s);
|
||||
stream_seek(s, RDP_SHARE_CONTROL_HEADER_LENGTH);
|
||||
stream_seek(s, RDP_SHARE_DATA_HEADER_LENGTH);
|
||||
return s;
|
||||
|
@ -229,6 +248,46 @@ void rdp_write_header(rdpRdp* rdp, STREAM* s, uint16 length, uint16 channel_id)
|
|||
stream_write_uint16_be(s, length); /* userData (OCTET_STRING) */
|
||||
}
|
||||
|
||||
static uint32 rdp_security_stream_out(rdpRdp* rdp, STREAM* s, int length)
|
||||
{
|
||||
uint32 sec_flags;
|
||||
uint32 ml;
|
||||
uint8* mk;
|
||||
uint8* data;
|
||||
|
||||
printf("rdp_security_stream_out:\n");
|
||||
sec_flags = rdp->sec_flags;
|
||||
if (sec_flags != 0)
|
||||
{
|
||||
rdp_write_security_header(s, sec_flags);
|
||||
if (sec_flags & SEC_ENCRYPT)
|
||||
{
|
||||
data = s->p + 8;
|
||||
length = length - (data - s->data);
|
||||
mk = rdp->settings->sign_key;
|
||||
ml = rdp->settings->rc4_key_len;
|
||||
security_mac_signature(mk, ml, data, length, s->p);
|
||||
stream_seek(s, 8);
|
||||
security_encrypt(s->p, length, rdp);
|
||||
}
|
||||
rdp->sec_flags = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32 rdp_get_sec_bytes(uint32 sec_flags)
|
||||
{
|
||||
uint32 sec_bytes;
|
||||
|
||||
if (sec_flags & SEC_ENCRYPT)
|
||||
sec_bytes = 12;
|
||||
else if (sec_flags != 0)
|
||||
sec_bytes = 4;
|
||||
else
|
||||
sec_bytes = 0;
|
||||
return sec_bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an RDP packet.\n
|
||||
* @param rdp RDP module
|
||||
|
@ -239,12 +298,22 @@ void rdp_write_header(rdpRdp* rdp, STREAM* s, uint16 length, uint16 channel_id)
|
|||
boolean rdp_send(rdpRdp* rdp, STREAM* s, uint16 channel_id)
|
||||
{
|
||||
uint16 length;
|
||||
uint32 sec_bytes;
|
||||
uint8* sec_hold;
|
||||
|
||||
printf("rdp_send:\n");
|
||||
length = stream_get_length(s);
|
||||
stream_set_pos(s, 0);
|
||||
|
||||
rdp_write_header(rdp, s, length, channel_id);
|
||||
|
||||
sec_bytes = rdp_get_sec_bytes(rdp->sec_flags);
|
||||
sec_hold = s->p;
|
||||
stream_seek(s, sec_bytes);
|
||||
|
||||
s->p = sec_hold;
|
||||
rdp_security_stream_out(rdp, s, length);
|
||||
|
||||
stream_set_pos(s, length);
|
||||
if (transport_write(rdp->transport, s) < 0)
|
||||
return False;
|
||||
|
@ -255,13 +324,24 @@ boolean rdp_send(rdpRdp* rdp, STREAM* s, uint16 channel_id)
|
|||
boolean rdp_send_pdu(rdpRdp* rdp, STREAM* s, uint16 type, uint16 channel_id)
|
||||
{
|
||||
uint16 length;
|
||||
uint32 sec_bytes;
|
||||
uint8* sec_hold;
|
||||
|
||||
printf("rdp_send_pdu:\n");
|
||||
length = stream_get_length(s);
|
||||
stream_set_pos(s, 0);
|
||||
|
||||
rdp_write_header(rdp, s, length, MCS_GLOBAL_CHANNEL_ID);
|
||||
|
||||
sec_bytes = rdp_get_sec_bytes(rdp->sec_flags);
|
||||
sec_hold = s->p;
|
||||
stream_seek(s, sec_bytes);
|
||||
|
||||
rdp_write_share_control_header(s, length, type, channel_id);
|
||||
|
||||
s->p = sec_hold;
|
||||
rdp_security_stream_out(rdp, s, length);
|
||||
|
||||
stream_set_pos(s, length);
|
||||
if (transport_write(rdp->transport, s) < 0)
|
||||
return False;
|
||||
|
@ -272,16 +352,27 @@ boolean rdp_send_pdu(rdpRdp* rdp, STREAM* s, uint16 type, uint16 channel_id)
|
|||
boolean rdp_send_data_pdu(rdpRdp* rdp, STREAM* s, uint8 type, uint16 channel_id)
|
||||
{
|
||||
uint16 length;
|
||||
uint32 sec_bytes;
|
||||
uint8* sec_hold;
|
||||
|
||||
printf("rdp_send_data_pdu:\n");
|
||||
length = stream_get_length(s);
|
||||
stream_set_pos(s, 0);
|
||||
|
||||
rdp_write_header(rdp, s, length, MCS_GLOBAL_CHANNEL_ID);
|
||||
|
||||
sec_bytes = rdp_get_sec_bytes(rdp->sec_flags);
|
||||
sec_hold = s->p;
|
||||
stream_seek(s, sec_bytes);
|
||||
|
||||
rdp_write_share_control_header(s, length, PDU_TYPE_DATA, channel_id);
|
||||
rdp_write_share_data_header(s, length, type, rdp->settings->share_id);
|
||||
|
||||
//printf("send %s Data PDU (0x%02X), length:%d\n", DATA_PDU_TYPE_STRINGS[type], type, length);
|
||||
|
||||
s->p = sec_hold;
|
||||
rdp_security_stream_out(rdp, s, length);
|
||||
|
||||
stream_set_pos(s, length);
|
||||
if (transport_write(rdp->transport, s) < 0)
|
||||
return False;
|
||||
|
|
|
@ -123,6 +123,12 @@ struct rdp_rdp
|
|||
struct rdp_settings* settings;
|
||||
struct rdp_transport* transport;
|
||||
struct rdp_vchan* vchan;
|
||||
struct crypto_rc4_struct* rc4_decrypt_key;
|
||||
int decrypt_use_count;
|
||||
struct crypto_rc4_struct* rc4_encrypt_key;
|
||||
int encrypt_use_count;
|
||||
uint32 sec_flags;
|
||||
boolean do_crypt;
|
||||
};
|
||||
|
||||
void rdp_read_security_header(STREAM* s, uint16* flags);
|
||||
|
|
|
@ -85,10 +85,10 @@ static void security_master_hash(char* input, int length, uint8* master_secret,
|
|||
|
||||
void security_session_key_blob(uint8* master_secret, uint8* client_random, uint8* server_random, uint8* output)
|
||||
{
|
||||
/* MasterHash = MasterHash('A') + MasterHash('BB') + MasterHash('CCC') */
|
||||
security_master_hash("A", 1, master_secret, client_random, server_random, &output[0]);
|
||||
security_master_hash("BB", 2, master_secret, client_random, server_random, &output[16]);
|
||||
security_master_hash("CCC", 3, master_secret, client_random, server_random, &output[32]);
|
||||
/* MasterHash = MasterHash('X') + MasterHash('YY') + MasterHash('ZZZ') */
|
||||
security_master_hash("X", 1, master_secret, client_random, server_random, &output[0]);
|
||||
security_master_hash("YY", 2, master_secret, client_random, server_random, &output[16]);
|
||||
security_master_hash("ZZZ", 3, master_secret, client_random, server_random, &output[32]);
|
||||
}
|
||||
|
||||
void security_mac_salt_key(uint8* session_key_blob, uint8* client_random, uint8* server_random, uint8* output)
|
||||
|
@ -97,19 +97,23 @@ void security_mac_salt_key(uint8* session_key_blob, uint8* client_random, uint8*
|
|||
memcpy(output, session_key_blob, 16);
|
||||
}
|
||||
|
||||
void security_licensing_encryption_key(uint8* session_key_blob, uint8* client_random, uint8* server_random, uint8* output)
|
||||
void security_md5_16_32_32(uint8* in0, uint8* in1, uint8* in2, uint8* output)
|
||||
{
|
||||
CryptoMd5 md5;
|
||||
|
||||
/* LicensingEncryptionKey = MD5(Second128Bits(SessionKeyBlob) + ClientRandom + ServerRandom)) */
|
||||
|
||||
md5 = crypto_md5_init();
|
||||
crypto_md5_update(md5, &session_key_blob[16], 16); /* Second128Bits(SessionKeyBlob) */
|
||||
crypto_md5_update(md5, client_random, 32); /* ClientRandom */
|
||||
crypto_md5_update(md5, server_random, 32); /* ServerRandom */
|
||||
crypto_md5_update(md5, in0, 16);
|
||||
crypto_md5_update(md5, in1, 32);
|
||||
crypto_md5_update(md5, in2, 32);
|
||||
crypto_md5_final(md5, output);
|
||||
}
|
||||
|
||||
void security_licensing_encryption_key(uint8* session_key_blob, uint8* client_random, uint8* server_random, uint8* output)
|
||||
{
|
||||
/* LicensingEncryptionKey = MD5(Second128Bits(SessionKeyBlob) + ClientRandom + ServerRandom)) */
|
||||
security_md5_16_32_32(&session_key_blob[16], client_random, server_random, output);
|
||||
}
|
||||
|
||||
void security_uint32_le(uint8* output, uint32 value)
|
||||
{
|
||||
output[0] = (value) & 0xFF;
|
||||
|
@ -172,3 +176,102 @@ void security_mac_signature(uint8* mac_key, int mac_key_length, uint8* data, uin
|
|||
|
||||
memcpy(output, md5_digest, 8);
|
||||
}
|
||||
|
||||
boolean security_establish_keys(uint8* client_random, rdpSettings* settings)
|
||||
{
|
||||
uint8 pre_master_secret[48];
|
||||
uint8 master_secret[48];
|
||||
uint8 session_key_blob[48];
|
||||
uint8* server_random;
|
||||
uint8 salt40[] = { 0xD1, 0x26, 0x9E };
|
||||
|
||||
printf("security_establish_keys:\n");
|
||||
|
||||
server_random = settings->server_random.data;
|
||||
|
||||
memcpy(pre_master_secret, client_random, 24);
|
||||
memcpy(pre_master_secret + 24, server_random, 24);
|
||||
|
||||
security_master_secret(pre_master_secret, client_random, server_random, master_secret);
|
||||
security_session_key_blob(master_secret, client_random, server_random, session_key_blob);
|
||||
|
||||
memcpy(settings->sign_key, session_key_blob, 16);
|
||||
|
||||
security_md5_16_32_32(&session_key_blob[16], client_random, server_random, settings->decrypt_key);
|
||||
security_md5_16_32_32(&session_key_blob[32], client_random, server_random, settings->encrypt_key);
|
||||
|
||||
if (settings->encryption_method == 1) /* 40 and 56 bit */
|
||||
{
|
||||
memcpy(settings->sign_key, salt40, 3); /* TODO 56 bit */
|
||||
memcpy(settings->decrypt_key, salt40, 3); /* TODO 56 bit */
|
||||
memcpy(settings->encrypt_key, salt40, 3); /* TODO 56 bit */
|
||||
settings->rc4_key_len = 8;
|
||||
}
|
||||
else /* 128 bit */
|
||||
{
|
||||
settings->rc4_key_len = 16;
|
||||
}
|
||||
|
||||
memcpy(settings->decrypt_update_key, settings->decrypt_key, 16);
|
||||
memcpy(settings->encrypt_update_key, settings->encrypt_key, 16);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
boolean security_key_update(uint8* key, uint8* update_key, int key_len)
|
||||
{
|
||||
uint8 sha1h[20];
|
||||
CryptoMd5 md5;
|
||||
CryptoSha1 sha1;
|
||||
CryptoRc4 rc4;
|
||||
uint8 salt40[] = { 0xD1, 0x26, 0x9E };
|
||||
|
||||
sha1 = crypto_sha1_init();
|
||||
crypto_sha1_update(sha1, update_key, key_len);
|
||||
crypto_sha1_update(sha1, pad1, sizeof(pad1));
|
||||
crypto_sha1_update(sha1, key, key_len);
|
||||
crypto_sha1_final(sha1, sha1h);
|
||||
|
||||
md5 = crypto_md5_init();
|
||||
crypto_md5_update(md5, update_key, key_len);
|
||||
crypto_md5_update(md5, pad2, sizeof(pad2));
|
||||
crypto_md5_update(md5, sha1h, 20);
|
||||
crypto_md5_final(md5, key);
|
||||
|
||||
rc4 = crypto_rc4_init(key, key_len);
|
||||
crypto_rc4(rc4, key_len, key, key);
|
||||
crypto_rc4_free(rc4);
|
||||
|
||||
if (key_len == 8)
|
||||
memcpy(key, salt40, 3); /* TODO 56 bit */
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
boolean security_encrypt(uint8* data, int length, rdpRdp* rdp)
|
||||
{
|
||||
if (rdp->encrypt_use_count >= 4096)
|
||||
{
|
||||
security_key_update(rdp->settings->encrypt_key, rdp->settings->encrypt_update_key, rdp->settings->rc4_key_len);
|
||||
crypto_rc4_free(rdp->rc4_encrypt_key);
|
||||
rdp->rc4_encrypt_key = crypto_rc4_init(rdp->settings->encrypt_key, rdp->settings->rc4_key_len);
|
||||
rdp->encrypt_use_count = 0;
|
||||
}
|
||||
crypto_rc4(rdp->rc4_encrypt_key, length, data, data);
|
||||
rdp->encrypt_use_count += 1;
|
||||
return True;
|
||||
}
|
||||
|
||||
boolean security_decrypt(uint8* data, int length, rdpRdp* rdp)
|
||||
{
|
||||
if (rdp->decrypt_use_count >= 4096)
|
||||
{
|
||||
security_key_update(rdp->settings->decrypt_key, rdp->settings->decrypt_update_key, rdp->settings->rc4_key_len);
|
||||
crypto_rc4_free(rdp->rc4_decrypt_key);
|
||||
rdp->rc4_decrypt_key = crypto_rc4_init(rdp->settings->decrypt_key, rdp->settings->rc4_key_len);
|
||||
rdp->decrypt_use_count = 0;
|
||||
}
|
||||
crypto_rc4(rdp->rc4_decrypt_key, length, data, data);
|
||||
rdp->decrypt_use_count += 1;
|
||||
return True;
|
||||
}
|
||||
|
|
|
@ -33,5 +33,9 @@ void security_licensing_encryption_key(uint8* session_key_blob, uint8* client_ra
|
|||
void security_mac_data(uint8* mac_salt_key, uint8* data, uint32 length, uint8* output);
|
||||
|
||||
void security_mac_signature(uint8* mac_key, int mac_key_length, uint8* data, uint32 length, uint8* output);
|
||||
boolean security_establish_keys(uint8* client_random, rdpSettings* settings);
|
||||
|
||||
boolean security_encrypt(uint8* data, int length, rdpRdp* rdp);
|
||||
boolean security_decrypt(uint8* data, int length, rdpRdp* rdp);
|
||||
|
||||
#endif /* __SECURITY_H */
|
||||
|
|
|
@ -226,6 +226,8 @@ int transport_write(rdpTransport* transport, STREAM* s)
|
|||
length = stream_get_length(s);
|
||||
stream_set_pos(s, 0);
|
||||
|
||||
printf("transport_write:\n");
|
||||
|
||||
#ifdef WITH_DEBUG_TRANSPORT
|
||||
if (length > 0)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue