Merge pull request #51 from llyzs/server

Initial server implementation (still in progress)
This commit is contained in:
Marc-André Moreau 2011-08-19 11:04:01 -07:00
commit e4c7f28acf
36 changed files with 1969 additions and 48 deletions

View File

@ -134,3 +134,4 @@ if(NOT WIN32)
endif()
add_subdirectory(client)
add_subdirectory(server)

View File

@ -0,0 +1,64 @@
/**
* FreeRDP: A Remote Desktop Protocol client.
* RDP Server Listener
*
* Copyright 2011 Vic Lee
*
* Licensed 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.
*/
#ifndef __FREERDP_LISTENER_H
#define __FREERDP_LISTENER_H
typedef struct rdp_freerdp_listener freerdp_listener;
#include <freerdp/api.h>
#include <freerdp/types.h>
#include <freerdp/settings.h>
#include <freerdp/peer.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef boolean (*psListenerOpen)(freerdp_listener* instance, const char* bind_address, uint16 port);
typedef boolean (*psListenerGetFileDescriptor)(freerdp_listener* instance, void** rfds, int* rcount);
typedef boolean (*psListenerCheckFileDescriptor)(freerdp_listener* instance);
typedef void (*psListenerClose)(freerdp_listener* instance);
typedef void (*psPeerAccepted)(freerdp_listener* instance, freerdp_peer* client);
struct rdp_freerdp_listener
{
void* listener;
void* param1;
void* param2;
void* param3;
void* param4;
psListenerOpen Open;
psListenerGetFileDescriptor GetFileDescriptor;
psListenerCheckFileDescriptor CheckFileDescriptor;
psListenerClose Close;
psPeerAccepted PeerAccepted;
};
FREERDP_API freerdp_listener* freerdp_listener_new(void);
FREERDP_API void freerdp_listener_free(freerdp_listener* instance);
#ifdef __cplusplus
}
#endif
#endif

54
include/freerdp/peer.h Normal file
View File

@ -0,0 +1,54 @@
/**
* FreeRDP: A Remote Desktop Protocol client.
* RDP Server Peer
*
* Copyright 2011 Vic Lee
*
* Licensed 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.
*/
#ifndef __FREERDP_PEER_H
#define __FREERDP_PEER_H
typedef struct rdp_freerdp_peer freerdp_peer;
#include <freerdp/api.h>
#include <freerdp/types.h>
#include <freerdp/settings.h>
typedef boolean (*psPeerInitialize)(freerdp_peer* client);
typedef boolean (*psPeerGetFileDescriptor)(freerdp_peer* client, void** rfds, int* rcount);
typedef boolean (*psPeerCheckFileDescriptor)(freerdp_peer* client);
typedef void (*psPeerDisconnect)(freerdp_peer* client);
struct rdp_freerdp_peer
{
void* peer;
void* param1;
void* param2;
void* param3;
void* param4;
rdpSettings* settings;
psPeerInitialize Initialize;
psPeerGetFileDescriptor GetFileDescriptor;
psPeerCheckFileDescriptor CheckFileDescriptor;
psPeerDisconnect Disconnect;
};
FREERDP_API freerdp_peer* freerdp_peer_new(int sockfd);
FREERDP_API void freerdp_peer_free(freerdp_peer* client);
#endif /* __FREERDP_PEER_H */

View File

@ -171,6 +171,7 @@ struct rdp_settings
uint32 kbd_subtype;
uint32 kbd_fn_keys;
uint32 client_build;
uint32 requested_protocols;
uint32 selected_protocol;
uint32 encryption_method;
uint32 encryption_level;
@ -204,6 +205,9 @@ struct rdp_settings
char* directory;
uint32 performance_flags;
char* cert_file;
char* privatekey_file;
boolean autologon;
boolean compression;

View File

@ -84,6 +84,10 @@ set(LIBFREERDP_CORE_SRCS
vchan.h
window.c
window.h
listener.c
listener.h
peer.c
peer.h
)
add_library(freerdp-core SHARED ${LIBFREERDP_CORE_SRCS})

View File

@ -251,6 +251,13 @@ boolean ber_read_enumerated(STREAM* s, uint8* enumerated, uint8 count)
return True;
}
void ber_write_enumerated(STREAM* s, uint8 enumerated, uint8 count)
{
ber_write_universal_tag(s, BER_TAG_ENUMERATED, False);
ber_write_length(s, 1);
stream_write_uint8(s, enumerated);
}
boolean ber_read_bit_string(STREAM* s, int* length, uint8* padding)
{
ber_read_universal_tag(s, BER_TAG_BIT_STRING, False);
@ -294,6 +301,27 @@ int ber_skip_octet_string(int length)
return 1 + _ber_skip_length(length) + length;
}
/**
* Read a BER BOOLEAN
* @param s
* @param value
*/
boolean ber_read_boolean(STREAM* s, boolean* value)
{
int length;
uint8 v;
if (!ber_read_universal_tag(s, BER_TAG_BOOLEAN, False))
return False;
ber_read_length(s, &length);
if (length != 1)
return False;
stream_read_uint8(s, v);
*value = (v ? True : False);
return True;
}
/**
* Write a BER BOOLEAN
* @param s

View File

@ -60,6 +60,7 @@ boolean ber_read_application_tag(STREAM* s, uint8 tag, int* length);
void ber_write_application_tag(STREAM* s, uint8 tag, int length);
boolean ber_read_application_tag(STREAM* s, uint8 tag, int* length);
boolean ber_read_enumerated(STREAM* s, uint8* enumerated, uint8 count);
void ber_write_enumerated(STREAM* s, uint8 enumerated, uint8 count);
boolean ber_read_contextual_tag(STREAM* s, uint8 tag, int* length, boolean pc);
int ber_write_contextual_tag(STREAM* s, uint8 tag, int length, boolean pc);
int ber_skip_contextual_tag(int length);
@ -72,6 +73,7 @@ boolean ber_read_octet_string(STREAM* s, int* length);
void ber_write_octet_string(STREAM* s, uint8* oct_str, int length);
int ber_write_octet_string_tag(STREAM* s, int length);
int ber_skip_octet_string(int length);
boolean ber_read_boolean(STREAM* s, boolean* value);
void ber_write_boolean(STREAM* s, boolean value);
boolean ber_read_integer(STREAM* s, uint32* value);
int ber_write_integer(STREAM* s, uint32 value);

View File

@ -108,3 +108,108 @@ boolean rdp_client_connect(rdpRdp* rdp)
return True;
}
boolean rdp_server_accept_nego(rdpRdp* rdp, STREAM* s)
{
boolean ret;
transport_set_blocking_mode(rdp->transport, True);
if (!nego_recv_request(rdp->nego, s))
return False;
if (rdp->nego->requested_protocols == PROTOCOL_RDP)
{
printf("Standard RDP encryption is not supported.\n");
return False;
}
printf("Requested protocols:");
if ((rdp->nego->requested_protocols | PROTOCOL_TLS))
{
printf(" TLS");
if (rdp->settings->tls_security)
{
printf("(Y)");
rdp->nego->selected_protocol |= PROTOCOL_TLS;
}
else
printf("(n)");
}
if ((rdp->nego->requested_protocols | PROTOCOL_NLA))
{
printf(" NLA");
if (rdp->settings->nla_security)
{
printf("(Y)");
rdp->nego->selected_protocol |= PROTOCOL_NLA;
}
else
printf("(n)");
}
printf("\n");
nego_send_negotiation_response(rdp->nego);
ret = False;
if (rdp->nego->selected_protocol & PROTOCOL_NLA)
ret = transport_accept_nla(rdp->transport);
else if (rdp->nego->selected_protocol & PROTOCOL_TLS)
ret = transport_accept_tls(rdp->transport);
else if (rdp->nego->selected_protocol & PROTOCOL_RDP)
ret = transport_accept_rdp(rdp->transport);
if (!ret)
return False;
transport_set_blocking_mode(rdp->transport, False);
rdp->state = CONNECTION_STATE_NEGO;
return True;
}
boolean rdp_server_accept_mcs_connect_initial(rdpRdp* rdp, STREAM* s)
{
int i;
if (!mcs_read_connect_initial(rdp->mcs, s))
return False;
printf("Accepted client: %s\n", rdp->settings->client_hostname);
printf("Accepted channels:");
for (i = 0; i < rdp->settings->num_channels; i++)
{
printf(" %s", rdp->settings->channels[i].name);
}
printf("\n");
if (!mcs_send_connect_response(rdp->mcs))
return False;
rdp->state = CONNECTION_STATE_MCS_CONNECT;
return True;
}
boolean rdp_server_accept_mcs_erect_domain_request(rdpRdp* rdp, STREAM* s)
{
if (!mcs_read_erect_domain_request(rdp->mcs, s))
return False;
rdp->state = CONNECTION_STATE_MCS_ERECT_DOMAIN;
return True;
}
boolean rdp_server_accept_mcs_attach_user_request(rdpRdp* rdp, STREAM* s)
{
if (!mcs_read_attach_user_request(rdp->mcs, s))
return False;
if (!mcs_send_attach_user_confirm(rdp->mcs))
return False;
rdp->state = CONNECTION_STATE_MCS_ATTACH_USER;
return True;
}

View File

@ -31,6 +31,20 @@
#include <freerdp/settings.h>
#include <freerdp/utils/memory.h>
enum CONNECTION_STATE
{
CONNECTION_STATE_INITIAL = 0,
CONNECTION_STATE_NEGO,
CONNECTION_STATE_MCS_CONNECT,
CONNECTION_STATE_MCS_ERECT_DOMAIN,
CONNECTION_STATE_MCS_ATTACH_USER
};
boolean rdp_client_connect(rdpRdp* rdp);
boolean rdp_server_accept_nego(rdpRdp* rdp, STREAM* s);
boolean rdp_server_accept_mcs_connect_initial(rdpRdp* rdp, STREAM* s);
boolean rdp_server_accept_mcs_erect_domain_request(rdpRdp* rdp, STREAM* s);
boolean rdp_server_accept_mcs_attach_user_request(rdpRdp* rdp, STREAM* s);
#endif /* __CONNECTION_H */

View File

@ -123,6 +123,63 @@ 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}
@ -200,6 +257,89 @@ void gcc_read_conference_create_response(STREAM* s, rdpSettings* settings)
gcc_read_server_data_blocks(s, settings, length);
}
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);
@ -241,6 +381,13 @@ void gcc_read_server_data_blocks(STREAM* s, rdpSettings *settings, int length)
}
}
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);
}
void gcc_read_user_data_header(STREAM* s, uint16* type, uint16* length)
{
stream_read_uint16(s, *type); /* type */
@ -261,6 +408,161 @@ void gcc_write_user_data_header(STREAM* s, uint16 type, uint16 length)
stream_write_uint16(s, length); /* 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}
@ -373,6 +675,33 @@ void gcc_read_server_core_data(STREAM* s, rdpSettings *settings)
settings->rdp_version = 7;
}
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}
@ -432,6 +761,50 @@ void gcc_read_server_security_data(STREAM* s, rdpSettings *settings)
}
}
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].chan_id = MCS_GLOBAL_CHANNEL_ID + 1 + i;
}
return True;
}
/**
* Write a client network data block (TS_UD_CS_NET).\n
* @msdn{cc240512}
@ -487,6 +860,46 @@ void gcc_read_server_network_data(STREAM* s, rdpSettings *settings)
stream_seek(s, 2); /* padding */
}
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].chan_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}
@ -509,6 +922,19 @@ void gcc_write_client_cluster_data(STREAM* s, rdpSettings *settings)
stream_write_uint32(s, settings->redirected_session_id); /* redirectedSessionID */
}
/**
* 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}
@ -546,3 +972,4 @@ void gcc_write_client_monitor_data(STREAM* s, rdpSettings *settings)
}
}
}

View File

@ -100,19 +100,31 @@
/* Monitor Flags */
#define MONITOR_PRIMARY 0x00000001
boolean gcc_read_conference_create_request(STREAM* s, rdpSettings* settings);
void gcc_write_conference_create_request(STREAM* s, STREAM* user_data);
void gcc_read_conference_create_response(STREAM* s, rdpSettings* settings);
void gcc_write_conference_create_response(STREAM* s, STREAM* user_data);
boolean gcc_read_client_data_blocks(STREAM* s, rdpSettings *settings, int length);
void gcc_write_client_data_blocks(STREAM* s, rdpSettings *settings);
void gcc_read_server_data_blocks(STREAM* s, rdpSettings *settings, int length);
void gcc_write_server_data_blocks(STREAM* s, rdpSettings *settings);
void gcc_read_user_data_header(STREAM* s, uint16* type, uint16* length);
void gcc_write_user_data_header(STREAM* s, uint16 type, uint16 length);
boolean gcc_read_client_core_data(STREAM* s, rdpSettings *settings, uint16 blockLength);
void gcc_write_client_core_data(STREAM* s, rdpSettings *settings);
void gcc_read_server_core_data(STREAM* s, rdpSettings *settings);
void gcc_write_server_core_data(STREAM* s, rdpSettings *settings);
boolean gcc_read_client_security_data(STREAM* s, rdpSettings *settings, uint16 blockLength);
void gcc_write_client_security_data(STREAM* s, rdpSettings *settings);
void gcc_read_server_security_data(STREAM* s, rdpSettings *settings);
void gcc_write_server_security_data(STREAM* s, rdpSettings *settings);
boolean gcc_read_client_network_data(STREAM* s, rdpSettings *settings, uint16 blockLength);
void gcc_write_client_network_data(STREAM* s, rdpSettings *settings);
void gcc_read_server_network_data(STREAM* s, rdpSettings *settings);
void gcc_write_server_network_data(STREAM* s, rdpSettings *settings);
boolean gcc_read_client_cluster_data(STREAM* s, rdpSettings *settings, uint16 blockLength);
void gcc_write_client_cluster_data(STREAM* s, rdpSettings *settings);
boolean gcc_read_client_monitor_data(STREAM* s, rdpSettings *settings, uint16 blockLength);
void gcc_write_client_monitor_data(STREAM* s, rdpSettings *settings);
#endif /* __GCC_H */

215
libfreerdp-core/listener.c Normal file
View File

@ -0,0 +1,215 @@
/**
* FreeRDP: A Remote Desktop Protocol client.
* RDP Server Listener
*
* Copyright 2011 Vic Lee
*
* Licensed 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#ifndef _WIN32
#include <netdb.h>
#include <net/if.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#else
#define close(_fd) closesocket(_fd)
#endif
#include "listener.h"
static boolean freerdp_listener_open(freerdp_listener* instance, const char* bind_address, uint16 port)
{
rdpListener* listener = (rdpListener*)instance->listener;
int status;
int sockfd;
char servname[10];
struct addrinfo hints = { 0 };
struct addrinfo* res;
struct addrinfo* ai;
int option_value;
void* sin_addr;
char buf[50];
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (bind_address == NULL)
hints.ai_flags = AI_PASSIVE;
snprintf(servname, sizeof(servname), "%d", port);
status = getaddrinfo(bind_address, servname, &hints, &res);
if (status != 0)
{
perror("getaddrinfo");
return False;
}
for (ai = res; ai && listener->num_sockfds < 5; ai = ai->ai_next)
{
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
continue;
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd == -1)
{
perror("socket");
continue;
}
option_value = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &option_value, sizeof(option_value)) == -1)
{
perror("setsockopt");
}
fcntl(sockfd, F_SETFL, O_NONBLOCK);
status = bind(sockfd, ai->ai_addr, ai->ai_addrlen);
if (status != 0)
{
perror("bind");
close(sockfd);
continue;
}
status = listen(sockfd, 10);
if (status != 0)
{
perror("listen");
close(sockfd);
continue;
}
listener->sockfds[listener->num_sockfds++] = sockfd;
if (ai->ai_family == AF_INET)
sin_addr = &(((struct sockaddr_in*)ai->ai_addr)->sin_addr);
else
sin_addr = &(((struct sockaddr_in6*)ai->ai_addr)->sin6_addr);
printf("Listening on %s port %s.\n", inet_ntop(ai->ai_family, sin_addr, buf, sizeof(buf)), servname);
}
freeaddrinfo(res);
return (listener->num_sockfds > 0 ? True : False);
}
static void freerdp_listener_close(freerdp_listener* instance)
{
int i;
rdpListener* listener = (rdpListener*)instance->listener;
for (i = 0; i < listener->num_sockfds; i++)
{
close(listener->sockfds[i]);
}
listener->num_sockfds = 0;
}
static boolean freerdp_listener_get_fds(freerdp_listener* instance, void** rfds, int* rcount)
{
rdpListener* listener = (rdpListener*)instance->listener;
int i;
if (listener->num_sockfds < 1)
return False;
for (i = 0; i < listener->num_sockfds; i++)
{
rfds[*rcount] = (void*)(long)(listener->sockfds[i]);
(*rcount)++;
}
return True;
}
static boolean freerdp_listener_check_fds(freerdp_listener* instance)
{
rdpListener* listener = (rdpListener*)instance->listener;
struct sockaddr_storage peer_addr;
socklen_t peer_addr_size;
int peer_sockfd;
int i;
freerdp_peer* client;
void* sin_addr;
if (listener->num_sockfds < 1)
return False;
for (i = 0; i < listener->num_sockfds; i++)
{
peer_addr_size = sizeof(peer_addr);
peer_sockfd = accept(listener->sockfds[i], (struct sockaddr *)&peer_addr, &peer_addr_size);
if (peer_sockfd == -1)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
continue;
perror("accept");
return False;
}
client = freerdp_peer_new(peer_sockfd);
if (peer_addr.ss_family == AF_INET)
sin_addr = &(((struct sockaddr_in*)&peer_addr)->sin_addr);
else
sin_addr = &(((struct sockaddr_in6*)&peer_addr)->sin6_addr);
client->settings->hostname = xzalloc(50);
inet_ntop(peer_addr.ss_family, sin_addr, client->settings->hostname, 50);
printf("Accepted client from %s.\n", client->settings->hostname);
IFCALL(instance->PeerAccepted, instance, client);
}
return True;
}
freerdp_listener* freerdp_listener_new(void)
{
freerdp_listener* instance;
rdpListener* listener;
instance = xnew(freerdp_listener);
instance->Open = freerdp_listener_open;
instance->GetFileDescriptor = freerdp_listener_get_fds;
instance->CheckFileDescriptor = freerdp_listener_check_fds;
instance->Close = freerdp_listener_close;
listener = xnew(rdpListener);
listener->instance = instance;
instance->listener = (void*)listener;
return instance;
}
void freerdp_listener_free(freerdp_listener* instance)
{
rdpListener* listener;
listener = (rdpListener*)instance->listener;
xfree(listener);
xfree(instance);
}

View File

@ -0,0 +1,37 @@
/**
* FreeRDP: A Remote Desktop Protocol client.
* RDP Server Listener
*
* Copyright 2011 Vic Lee
*
* Licensed 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.
*/
#ifndef __LISTENER_H
#define __LISTENER_H
typedef struct rdp_listener rdpListener;
#include "rdp.h"
#include <freerdp/listener.h>
struct rdp_listener
{
freerdp_listener* instance;
int sockfds[5];
int num_sockfds;
};
#endif

View File

@ -247,11 +247,11 @@ boolean mcs_read_domain_mcspdu_header(STREAM* s, enum DomainMCSPDU* domainMCSPDU
* @param length TPKT length
*/
void mcs_write_domain_mcspdu_header(STREAM* s, enum DomainMCSPDU domainMCSPDU, int length)
void mcs_write_domain_mcspdu_header(STREAM* s, enum DomainMCSPDU domainMCSPDU, int length, uint8 options)
{
tpkt_write_header(s, length);
tpdu_write_data(s);
per_write_choice(s, domainMCSPDU << 2);
per_write_choice(s, (domainMCSPDU << 2) | options);
}
/**
@ -347,6 +347,58 @@ void mcs_print_domain_parameters(DomainParameters* domainParameters)
printf("}\n");
}
/**
* Read an MCS Connect Initial PDU.\n
* @msdn{cc240508}
* @param mcs MCS module
* @param s stream
*/
boolean mcs_read_connect_initial(rdpMcs* mcs, STREAM* s)
{
int length;
boolean upwardFlag;
tpkt_read_header(s);
if (tpdu_read_data(s) == 0)
return False;
if (!ber_read_application_tag(s, MCS_TYPE_CONNECT_INITIAL, &length))
return False;
/* callingDomainSelector (OCTET_STRING) */
if (!ber_read_octet_string(s, &length))
return False;
stream_seek(s, length);
/* calledDomainSelector (OCTET_STRING) */
if (!ber_read_octet_string(s, &length))
return False;
stream_seek(s, length);
/* upwardFlag (BOOLEAN) */
if (!ber_read_boolean(s, &upwardFlag))
return False;
/* targetParameters (DomainParameters) */
mcs_read_domain_parameters(s, &mcs->targetParameters);
/* minimumParameters (DomainParameters) */
mcs_read_domain_parameters(s, &mcs->minimumParameters);
/* maximumParameters (DomainParameters) */
mcs_read_domain_parameters(s, &mcs->maximumParameters);
if (!ber_read_octet_string(s, &length))
return False;
if (!gcc_read_conference_create_request(s, mcs->transport->settings))
return False;
return True;
}
/**
* Write an MCS Connect Initial PDU.\n
* @msdn{cc240508}
@ -395,6 +447,41 @@ void mcs_write_connect_initial(STREAM* s, rdpMcs* mcs, STREAM* user_data)
stream_set_mark(s, em);
}
/**
* Write an MCS Connect Response PDU.\n
* @msdn{cc240508}
* @param s stream
* @param mcs MCS module
* @param user_data GCC Conference Create Response
*/
void mcs_write_connect_response(STREAM* s, rdpMcs* mcs, STREAM* user_data)
{
int length;
uint8 *bm, *em;
int gcc_CCrsp_length = stream_get_length(user_data);
stream_get_mark(s, bm);
stream_seek(s, 3);
ber_write_enumerated(s, 0, MCS_Result_enum_length);
ber_write_integer(s, 0); /* calledConnectId */
mcs->domainParameters = mcs->targetParameters;
mcs_write_domain_parameters(s, &(mcs->domainParameters));
/* userData (OCTET_STRING) */
ber_write_octet_string(s, user_data->data, gcc_CCrsp_length);
stream_get_mark(s, em);
length = (em - bm) - 3;
stream_set_mark(s, bm);
ber_write_application_tag(s, MCS_TYPE_CONNECT_RESPONSE, length);
stream_set_mark(s, em);
}
/**
* Send MCS Connect Initial.\n
* @msdn{cc240508}
@ -467,6 +554,67 @@ void mcs_recv_connect_response(rdpMcs* mcs)
gcc_read_conference_create_response(s, mcs->transport->settings);
}
/**
* Send MCS Connect Response.\n
* @msdn{cc240501}
* @param mcs mcs module
*/
boolean mcs_send_connect_response(rdpMcs* mcs)
{
STREAM* s;
int length;
uint8 *bm, *em;
STREAM* gcc_CCrsp;
STREAM* server_data;
server_data = stream_new(512);
gcc_write_server_data_blocks(server_data, mcs->transport->settings);
gcc_CCrsp = stream_new(512);
gcc_write_conference_create_response(gcc_CCrsp, server_data);
length = stream_get_length(gcc_CCrsp) + 7;
s = transport_send_stream_init(mcs->transport, 1024);
stream_get_mark(s, bm);
stream_seek(s, 7);
mcs_write_connect_response(s, mcs, gcc_CCrsp);
stream_get_mark(s, em);
length = (em - bm);
stream_set_mark(s, bm);
tpkt_write_header(s, length);
tpdu_write_data(s);
stream_set_mark(s, em);
transport_write(mcs->transport, s);
stream_free(gcc_CCrsp);
stream_free(server_data);
return True;
}
/**
* Read MCS Erect Domain Request.\n
* @msdn{cc240523}
* @param mcs
* @param s stream
*/
boolean mcs_read_erect_domain_request(rdpMcs* mcs, STREAM* s)
{
int length;
enum DomainMCSPDU MCSPDU;
MCSPDU = DomainMCSPDU_ErectDomainRequest;
if (!mcs_read_domain_mcspdu_header(s, &MCSPDU, &length))
return False;
return True;
}
/**
* Send MCS Erect Domain Request.\n
* @msdn{cc240523}
@ -479,7 +627,7 @@ void mcs_send_erect_domain_request(rdpMcs* mcs)
int length = 12;
s = transport_send_stream_init(mcs->transport, length);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_ErectDomainRequest, length);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_ErectDomainRequest, length, 0);
per_write_integer(s, 0); /* subHeight (INTEGER) */
per_write_integer(s, 0); /* subInterval (INTEGER) */
@ -487,6 +635,25 @@ void mcs_send_erect_domain_request(rdpMcs* mcs)
transport_write(mcs->transport, s);
}
/**
* Read MCS Attach User Request.\n
* @msdn{cc240524}
* @param mcs mcs module
* @param s stream
*/
boolean mcs_read_attach_user_request(rdpMcs* mcs, STREAM* s)
{
int length;
enum DomainMCSPDU MCSPDU;
MCSPDU = DomainMCSPDU_AttachUserRequest;
if (!mcs_read_domain_mcspdu_header(s, &MCSPDU, &length))
return False;
return True;
}
/**
* Send MCS Attach User Request.\n
* @msdn{cc240524}
@ -499,7 +666,7 @@ void mcs_send_attach_user_request(rdpMcs* mcs)
int length = 8;
s = transport_send_stream_init(mcs->transport, length);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_AttachUserRequest, length);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_AttachUserRequest, length, 0);
transport_write(mcs->transport, s);
}
@ -527,6 +694,30 @@ void mcs_recv_attach_user_confirm(rdpMcs* mcs)
per_read_integer16(s, &(mcs->user_id), MCS_BASE_CHANNEL_ID); /* initiator (UserId) */
}
/**
* Send MCS Attach User Confirm.\n
* @msdn{cc240525}
* @param mcs mcs module
*/
boolean mcs_send_attach_user_confirm(rdpMcs* mcs)
{
STREAM* s;
int length = 11;
s = transport_send_stream_init(mcs->transport, length);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_AttachUserConfirm, length, 2);
per_write_enumerated(s, 0, MCS_Result_enum_length); /* result */
mcs->user_id = 1002;
per_write_integer16(s, mcs->user_id, MCS_BASE_CHANNEL_ID); /* initiator (UserId) */
transport_write(mcs->transport, s);
return True;
}
/**
* Send MCS Channel Join Request.\n
* @msdn{cc240526}
@ -540,7 +731,7 @@ void mcs_send_channel_join_request(rdpMcs* mcs, uint16 channel_id)
int length = 12;
s = transport_send_stream_init(mcs->transport, 12);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_ChannelJoinRequest, length);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_ChannelJoinRequest, length, 0);
per_write_integer16(s, mcs->user_id, MCS_BASE_CHANNEL_ID);
per_write_integer16(s, channel_id, 0);

View File

@ -129,16 +129,22 @@ typedef struct rdp_mcs rdpMcs;
boolean mcs_connect(rdpMcs* mcs);
void mcs_write_connect_initial(STREAM* s, rdpMcs* mcs, STREAM* user_data);
void mcs_write_connect_response(STREAM* s, rdpMcs* mcs, STREAM* user_data);
boolean mcs_read_connect_initial(rdpMcs* mcs, STREAM* s);
void mcs_send_connect_initial(rdpMcs* mcs);
void mcs_recv_connect_response(rdpMcs* mcs);
boolean mcs_send_connect_response(rdpMcs* mcs);
boolean mcs_read_erect_domain_request(rdpMcs* mcs, STREAM* s);
void mcs_send_erect_domain_request(rdpMcs* mcs);
boolean mcs_read_attach_user_request(rdpMcs* mcs, STREAM* s);
void mcs_send_attach_user_request(rdpMcs* mcs);
void mcs_recv_attach_user_confirm(rdpMcs* mcs);
boolean mcs_send_attach_user_confirm(rdpMcs* mcs);
void mcs_send_channel_join_request(rdpMcs* mcs, uint16 channel_id);
void mcs_recv_channel_join_confirm(rdpMcs* mcs);
boolean mcs_read_domain_mcspdu_header(STREAM* s, enum DomainMCSPDU* domainMCSPDU, int* length);
void mcs_write_domain_mcspdu_header(STREAM* s, enum DomainMCSPDU domainMCSPDU, int length);
void mcs_write_domain_mcspdu_header(STREAM* s, enum DomainMCSPDU domainMCSPDU, int length, uint8 options);
rdpMcs* mcs_new(rdpTransport* transport);
void mcs_free(rdpMcs* mcs);

View File

@ -82,6 +82,7 @@ boolean nego_connect(rdpNego* nego)
DEBUG_NEGO("Negotiated %s security", PROTOCOL_SECURITY_STRINGS[nego->selected_protocol]);
/* update settings with negotiated protocol security */
nego->transport->settings->requested_protocols = nego->requested_protocols;
nego->transport->settings->selected_protocol = nego->selected_protocol;
return True;
@ -269,6 +270,59 @@ int nego_recv(rdpTransport* transport, STREAM* s, void* extra)
return 0;
}
/**
* Receive protocol security negotiation request message.\n
* @param nego
* @param s stream
*/
boolean nego_recv_request(rdpNego* nego, STREAM* s)
{
uint8 li;
uint8 c;
uint8 type;
tpkt_read_header(s);
li = tpdu_read_connection_request(s);
if (li != stream_get_left(s) + 6)
{
printf("Incorrect TPDU length indicator.\n");
return False;
}
if (stream_get_left(s) > 8)
{
/* Optional routingToken or cookie, ending with CR+LF */
while (stream_get_left(s) > 0)
{
stream_read_uint8(s, c);
if (c != '\x0D')
continue;
stream_peek_uint8(s, c);
if (c != '\x0A')
continue;
break;
}
}
if (stream_get_left(s) >= 8)
{
/* rdpNegData (optional) */
stream_read_uint8(s, type); /* Type */
if (type != TYPE_RDP_NEG_REQ)
{
printf("Incorrect negotiation request type %d\n", type);
return False;
}
nego_process_negotiation_request(nego, s);
}
return True;
}
/**
* Send protocol security negotiation message.
* @param nego
@ -339,6 +393,26 @@ void nego_send_negotiation_request(rdpNego* nego)
transport_write(nego->transport, s);
}
/**
* Process Negotiation Request from Connection Request message.
* @param nego
* @param s
*/
void nego_process_negotiation_request(rdpNego* nego, STREAM* s)
{
uint8 flags;
uint16 length;
DEBUG_NEGO("RDP_NEG_REQ");
stream_read_uint8(s, flags);
stream_read_uint16(s, length);
stream_read_uint32(s, nego->requested_protocols);
nego->state = NEGO_STATE_FINAL;
}
/**
* Process Negotiation Response from Connection Confirm message.
* @param nego
@ -402,6 +476,45 @@ void nego_process_negotiation_failure(rdpNego* nego, STREAM* s)
nego->state = NEGO_STATE_FAIL;
}
/**
* Send RDP Negotiation Response (RDP_NEG_RSP).\n
* @param nego
*/
void nego_send_negotiation_response(rdpNego* nego)
{
STREAM* s;
int length;
uint8 *bm, *em;
s = stream_new(64);
length = TPDU_CONNECTION_CONFIRM_LENGTH;
stream_get_mark(s, bm);
stream_seek(s, length);
if (nego->selected_protocol > PROTOCOL_RDP)
{
/* RDP_NEG_DATA must be present for TLS and NLA */
stream_write_uint8(s, TYPE_RDP_NEG_RSP);
stream_write_uint8(s, EXTENDED_CLIENT_DATA_SUPPORTED); /* flags */
stream_write_uint16(s, 8); /* RDP_NEG_DATA length (8) */
stream_write_uint32(s, nego->selected_protocol); /* selectedProtocol */
length += 8;
}
stream_get_mark(s, em);
stream_set_mark(s, bm);
tpkt_write_header(s, length);
tpdu_write_connection_confirm(s, length - 5);
stream_set_mark(s, em);
transport_write(nego->transport, s);
/* update settings with negotiated protocol security */
nego->transport->settings->requested_protocols = nego->requested_protocols;
nego->transport->settings->selected_protocol = nego->selected_protocol;
}
/**
* Initialize NEGO state machine.
* @param nego

View File

@ -65,6 +65,8 @@ enum RDP_NEG_MSG
TYPE_RDP_NEG_FAILURE = 0x3
};
#define EXTENDED_CLIENT_DATA_SUPPORTED 0x01
extern char NEGO_STATE_STRINGS[6][25];
extern char PROTOCOL_SECURITY_STRINGS[3][4];
@ -92,10 +94,13 @@ void nego_attempt_rdp(rdpNego* nego);
void nego_send(rdpNego* nego);
int nego_recv(rdpTransport* transport, STREAM* s, void* extra);
void nego_recv_response(rdpNego* nego);
boolean nego_recv_request(rdpNego* nego, STREAM* s);
void nego_send_negotiation_request(rdpNego* nego);
void nego_process_negotiation_request(rdpNego* nego, STREAM* s);
void nego_process_negotiation_response(rdpNego* nego, STREAM* s);
void nego_process_negotiation_failure(rdpNego* nego, STREAM* s);
void nego_send_negotiation_response(rdpNego* nego);
rdpNego* nego_new(struct rdp_transport * transport);
void nego_free(rdpNego* nego);

130
libfreerdp-core/peer.c Normal file
View File

@ -0,0 +1,130 @@
/**
* FreeRDP: A Remote Desktop Protocol client.
* RDP Server Peer
*
* Copyright 2011 Vic Lee
*
* Licensed 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.
*/
#include "peer.h"
static boolean freerdp_peer_initialize(freerdp_peer* client)
{
rdpPeer* peer = (rdpPeer*)client->peer;
peer->rdp->state = CONNECTION_STATE_INITIAL;
return True;
}
static boolean freerdp_peer_get_fds(freerdp_peer* client, void** rfds, int* rcount)
{
rdpPeer* peer = (rdpPeer*)client->peer;
rfds[*rcount] = (void*)(long)(peer->rdp->transport->tcp->sockfd);
(*rcount)++;
return True;
}
static boolean freerdp_peer_check_fds(freerdp_peer* client)
{
rdpPeer* peer = (rdpPeer*)client->peer;
rdpRdp* rdp;
int status;
rdp = (rdpRdp*) peer->rdp;
status = rdp_check_fds(rdp);
if (status < 0)
return False;
return True;
}
static int peer_recv_callback(rdpTransport* transport, STREAM* s, void* extra)
{
rdpPeer* peer = (rdpPeer*)extra;
switch (peer->rdp->state)
{
case CONNECTION_STATE_INITIAL:
if (!rdp_server_accept_nego(peer->rdp, s))
return -1;
break;
case CONNECTION_STATE_NEGO:
if (!rdp_server_accept_mcs_connect_initial(peer->rdp, s))
return -1;
break;
case CONNECTION_STATE_MCS_CONNECT:
if (!rdp_server_accept_mcs_erect_domain_request(peer->rdp, s))
return -1;
break;
case CONNECTION_STATE_MCS_ERECT_DOMAIN:
if (!rdp_server_accept_mcs_attach_user_request(peer->rdp, s))
return -1;
break;
default:
printf("Invalid state %d\n", peer->rdp->state);
return -1;
}
return 1;
}
static void freerdp_peer_disconnect(freerdp_peer* client)
{
}
freerdp_peer* freerdp_peer_new(int sockfd)
{
freerdp_peer* client;
rdpPeer* peer;
client = xnew(freerdp_peer);
client->Initialize = freerdp_peer_initialize;
client->GetFileDescriptor = freerdp_peer_get_fds;
client->CheckFileDescriptor = freerdp_peer_check_fds;
client->Disconnect = freerdp_peer_disconnect;
peer = xnew(rdpPeer);
peer->client = client;
peer->rdp = rdp_new(NULL);
client->peer = (void*)peer;
client->settings = peer->rdp->settings;
transport_attach(peer->rdp->transport, sockfd);
peer->rdp->transport->recv_callback = peer_recv_callback;
peer->rdp->transport->recv_extra = peer;
transport_set_blocking_mode(peer->rdp->transport, False);
return client;
}
void freerdp_peer_free(freerdp_peer* client)
{
rdpPeer* peer = (rdpPeer*)client->peer;
rdp_free(peer->rdp);
xfree(peer);
xfree(client);
}

36
libfreerdp-core/peer.h Normal file
View File

@ -0,0 +1,36 @@
/**
* FreeRDP: A Remote Desktop Protocol client.
* RDP Server Peer
*
* Copyright 2011 Vic Lee
*
* Licensed 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.
*/
#ifndef __PEER
#define __PEER
typedef struct rdp_peer rdpPeer;
#include "rdp.h"
#include <freerdp/peer.h>
struct rdp_peer
{
freerdp_peer* client;
rdpRdp* rdp;
};
#endif /* __PEER */

View File

@ -133,6 +133,19 @@ void per_write_number_of_sets(STREAM* s, uint8 number)
stream_write_uint8(s, number);
}
/**
* Read PER padding with zeros.
* @param s stream
* @param length
*/
boolean per_read_padding(STREAM* s, int length)
{
stream_seek(s, length);
return True;
}
/**
* Write PER padding with zeros.
* @param s stream
@ -246,6 +259,19 @@ boolean per_read_enumerated(STREAM* s, uint8* enumerated, uint8 count)
return True;
}
/**
* Write PER ENUMERATED.
* @param s stream
* @param enumerated enumerated
* @param count enumeration count
* @return
*/
void per_write_enumerated(STREAM* s, uint8 enumerated, uint8 count)
{
stream_write_uint8(s, enumerated);
}
/**
* Read PER OBJECT_IDENTIFIER (OID).
* @param s stream
@ -371,6 +397,32 @@ void per_write_octet_string(STREAM* s, uint8* oct_str, int length, int min)
stream_write_uint8(s, oct_str[i]);
}
/**
* Read PER NumericString.
* @param s stream
* @param num_str numeric string
* @param length string length
* @param min minimum string length
*/
boolean per_read_numeric_string(STREAM* s, int min)
{
int i;
int length;
uint16 mlength;
per_read_length(s, &mlength);
length = mlength + min;
for (i = 0; i < length; i += 2)
{
stream_seek(s, 1);
}
return True;
}
/**
* Write PER NumericString.
* @param s stream

View File

@ -30,18 +30,19 @@ boolean per_read_selection(STREAM* s, uint8* selection);
void per_write_selection(STREAM* s, uint8 selection);
boolean per_read_number_of_sets(STREAM* s, uint8* number);
void per_write_number_of_sets(STREAM* s, uint8 number);
boolean per_read_padding(STREAM* s, int length);
void per_write_padding(STREAM* s, int length);
boolean per_read_integer(STREAM* s, uint32* integer);
boolean per_read_integer16(STREAM* s, uint16* integer, uint16 min);
void per_write_integer(STREAM* s, uint32 integer);
void per_write_integer16(STREAM* s, uint16 integer, uint16 min);
boolean per_read_enumerated(STREAM* s, uint8* enumerated, uint8 count);
void per_write_enumerated(STREAM* s, uint8 enumerated, uint8 count);
void per_write_object_identifier(STREAM* s, uint8 oid[6]);
boolean per_read_object_identifier(STREAM* s, uint8 oid[6]);
boolean per_read_octet_string(STREAM* s, uint8* oct_str, int length, int min);
void per_write_octet_string(STREAM* s, uint8* oct_str, int length, int min);
boolean per_read_numeric_string(STREAM* s, int min);
void per_write_numeric_string(STREAM* s, uint8* num_str, int length, int min);
boolean per_read_integer16(STREAM* s, uint16* integer, uint16 min);
void per_write_integer16(STREAM* s, uint16 integer, uint16 min);
#endif /* __PER_H */

View File

@ -179,7 +179,7 @@ STREAM* rdp_data_pdu_init(rdpRdp* rdp)
void rdp_write_header(rdpRdp* rdp, STREAM* s, int length, uint16 channel_id)
{
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_SendDataRequest, length);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_SendDataRequest, length, 0);
per_write_integer16(s, rdp->mcs->user_id, MCS_BASE_CHANNEL_ID); /* initiator */
per_write_integer16(s, channel_id, 0); /* channelId */
stream_write_uint8(s, 0x70); /* dataPriority + segmentation */

View File

@ -115,6 +115,7 @@ struct rdp_rdp
{
boolean licensed;
boolean activated;
int state;
struct rdp_mcs* mcs;
struct rdp_nego* nego;
struct rdp_input* input;

View File

@ -137,6 +137,8 @@ void settings_free(rdpSettings* settings)
xfree(settings->shell);
xfree(settings->directory);
xfree(settings->client_dir);
xfree(settings->cert_file);
xfree(settings->privatekey_file);
xfree(settings);
}
}

View File

@ -155,12 +155,14 @@ int tcp_read(rdpTcp* tcp, uint8* data, int length)
status = recv(tcp->sockfd, data, length, 0);
if (status < 0)
if (status <= 0)
{
/* No data available */
if (errno == EAGAIN || errno == EWOULDBLOCK)
return 0;
perror("recv");
/* When peer disconnects we get status 0 with no error. */
if (status < 0)
perror("recv");
return -1;
}

View File

@ -26,6 +26,24 @@ boolean tls_connect(rdpTls* tls)
{
int connection_status;
tls->ctx = SSL_CTX_new(TLSv1_client_method());
if (tls->ctx == NULL)
{
printf("SSL_CTX_new failed\n");
return False;
}
/*
* This is necessary, because the Microsoft TLS implementation is not perfect.
* SSL_OP_ALL enables a couple of workarounds for buggy TLS implementations,
* but the most important workaround being SSL_OP_TLS_BLOCK_PADDING_BUG.
* As the size of the encrypted payload may give hints about its contents,
* block padding is normally used, but the Microsoft TLS implementation
* won't recognize it and will disconnect you after sending a TLS alert.
*/
SSL_CTX_set_options(tls->ctx, SSL_OP_ALL);
tls->ssl = SSL_new(tls->ctx);
if (tls->ssl == NULL)
@ -40,24 +58,9 @@ boolean tls_connect(rdpTls* tls)
return False;
}
while (1)
{
connection_status = SSL_connect(tls->ssl);
connection_status = SSL_connect(tls->ssl);
/*
* SSL_WANT_READ and SSL_WANT_WRITE errors are normal,
* just try again if it happens
*/
if (connection_status == SSL_ERROR_WANT_READ)
continue;
else if (connection_status == SSL_ERROR_WANT_WRITE)
continue;
else
break;
}
if (connection_status < 0)
if (connection_status <= 0)
{
if (tls_print_error("SSL_connect", tls->ssl, connection_status))
return False;
@ -68,6 +71,57 @@ boolean tls_connect(rdpTls* tls)
return True;
}
boolean tls_accept(rdpTls* tls, const char* cert_file, const char* privatekey_file)
{
int connection_status;
tls->ctx = SSL_CTX_new(TLSv1_server_method());
if (tls->ctx == NULL)
{
printf("SSL_CTX_new failed\n");
return False;
}
if (SSL_CTX_use_RSAPrivateKey_file(tls->ctx, privatekey_file, SSL_FILETYPE_PEM) <= 0)
{
printf("SSL_CTX_use_RSAPrivateKey_file failed\n");
return False;
}
tls->ssl = SSL_new(tls->ctx);
if (tls->ssl == NULL)
{
printf("SSL_new failed\n");
return False;
}
if (SSL_use_certificate_file(tls->ssl, cert_file, SSL_FILETYPE_PEM) <= 0)
{
printf("SSL_use_certificate_file failed\n");
return False;
}
if (SSL_set_fd(tls->ssl, tls->sockfd) < 1)
{
printf("SSL_set_fd failed\n");
return False;
}
connection_status = SSL_accept(tls->ssl);
if (connection_status <= 0)
{
if (tls_print_error("SSL_accept", tls->ssl, connection_status))
return False;
}
printf("TLS connection accepted\n");
return True;
}
boolean tls_disconnect(rdpTls* tls)
{
return True;
@ -85,10 +139,12 @@ int tls_read(rdpTls* tls, uint8* data, int length)
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
status = 0;
break;
default:
tls_print_error("SSL_read", tls->ssl, status);
status = -1;
break;
}
@ -107,6 +163,7 @@ int tls_write(rdpTls* tls, uint8* data, int length)
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
status = 0;
break;
@ -180,29 +237,11 @@ rdpTls* tls_new()
if (tls != NULL)
{
tls->connect = tls_connect;
tls->accept = tls_accept;
tls->disconnect = tls_disconnect;
SSL_load_error_strings();
SSL_library_init();
tls->ctx = SSL_CTX_new(TLSv1_client_method());
if (tls->ctx == NULL)
{
printf("SSL_CTX_new failed\n");
return NULL;
}
/*
* This is necessary, because the Microsoft TLS implementation is not perfect.
* SSL_OP_ALL enables a couple of workarounds for buggy TLS implementations,
* but the most important workaround being SSL_OP_TLS_BLOCK_PADDING_BUG.
* As the size of the encrypted payload may give hints about its contents,
* block padding is normally used, but the Microsoft TLS implementation
* won't recognize it and will disconnect you after sending a TLS alert.
*/
SSL_CTX_set_options(tls->ctx, SSL_OP_ALL);
}
return tls;

View File

@ -30,6 +30,7 @@
typedef struct rdp_tls rdpTls;
typedef boolean (*TlsConnect) (rdpTls* tls);
typedef boolean (*TlsAccept) (rdpTls* tls, const char* cert_file, const char* privatekey_file);
typedef boolean (*TlsDisconnect) (rdpTls* tls);
struct rdp_tls
@ -38,10 +39,12 @@ struct rdp_tls
int sockfd;
SSL_CTX* ctx;
TlsConnect connect;
TlsAccept accept;
TlsDisconnect disconnect;
};
boolean tls_connect(rdpTls* tls);
boolean tls_accept(rdpTls* tls, const char* cert_file, const char* privatekey_file);
boolean tls_disconnect(rdpTls* tls);
int tls_read(rdpTls* tls, uint8* data, int length);
int tls_write(rdpTls* tls, uint8* data, int length);

View File

@ -111,6 +111,28 @@ tpdu_write_header(STREAM* s, uint16 length, uint8 code)
}
}
/**
* Read Connection Request TPDU
* @param s stream
* @return length indicator (LI)
*/
uint8 tpdu_read_connection_request(STREAM* s)
{
uint8 li;
uint8 code;
li = tpdu_read_header(s, &code);
if (code != X224_TPDU_CONNECTION_REQUEST)
{
printf("Error: expected X224_TPDU_CONNECTION_REQUEST\n");
return 0;
}
return li;
}
/**
* Write Connection Request TPDU.
* @param s stream
@ -146,6 +168,18 @@ tpdu_read_connection_confirm(STREAM* s)
return li;
}
/**
* Write Connection Confirm TPDU.
* @param s stream
* @param length TPDU length
*/
void
tpdu_write_connection_confirm(STREAM* s, uint16 length)
{
tpdu_write_header(s, length, X224_TPDU_CONNECTION_CONFIRM);
}
/**
* Write Disconnect Request TPDU.
* @param s stream

View File

@ -43,8 +43,10 @@ enum X224_TPDU_TYPE
uint8 tpdu_read_header(STREAM* s, uint8* code);
void tpdu_write_header(STREAM* s, uint16 length, uint8 code);
uint8 tpdu_read_connection_request(STREAM* s);
void tpdu_write_connection_request(STREAM* s, uint16 length);
uint8 tpdu_read_connection_confirm(STREAM* s);
void tpdu_write_connection_confirm(STREAM* s, uint16 length);
void tpdu_write_disconnect_request(STREAM* s, uint16 length);
uint16 tpdu_read_data(STREAM* s);
void tpdu_write_data(STREAM* s);

View File

@ -63,6 +63,11 @@ boolean transport_connect(rdpTransport* transport, const char* hostname, uint16
return transport->tcp->connect(transport->tcp, hostname, port);
}
void transport_attach(rdpTransport* transport, int sockfd)
{
transport->tcp->sockfd = sockfd;
}
boolean transport_disconnect(rdpTransport* transport)
{
return transport->tcp->disconnect(transport->tcp);
@ -126,6 +131,52 @@ boolean transport_connect_nla(rdpTransport* transport)
return True;
}
boolean transport_accept_rdp(rdpTransport* transport)
{
transport->state = TRANSPORT_STATE_RDP;
/* RDP encryption */
return True;
}
boolean transport_accept_tls(rdpTransport* transport)
{
if (transport->tls == NULL)
transport->tls = tls_new();
transport->layer = TRANSPORT_LAYER_TLS;
transport->state = TRANSPORT_STATE_TLS;
transport->tls->sockfd = transport->tcp->sockfd;
if (tls_accept(transport->tls, transport->settings->cert_file, transport->settings->privatekey_file) != True)
return False;
return True;
}
boolean transport_accept_nla(rdpTransport* transport)
{
if (transport->tls == NULL)
transport->tls = tls_new();
transport->layer = TRANSPORT_LAYER_TLS;
transport->state = TRANSPORT_STATE_NLA;
transport->tls->sockfd = transport->tcp->sockfd;
if (tls_accept(transport->tls, transport->settings->cert_file, transport->settings->privatekey_file) != True)
return False;
/* Network Level Authentication */
if (transport->settings->authentication != True)
return True;
/* Blocking here until NLA is complete */
return True;
}
int transport_read(rdpTransport* transport, STREAM* s)
{
int status = -1;

View File

@ -69,10 +69,14 @@ struct rdp_transport
STREAM* transport_recv_stream_init(rdpTransport* transport, int size);
STREAM* transport_send_stream_init(rdpTransport* transport, int size);
boolean transport_connect(rdpTransport* transport, const char* hostname, uint16 port);
void transport_attach(rdpTransport* transport, int sockfd);
boolean transport_disconnect(rdpTransport* transport);
boolean transport_connect_rdp(rdpTransport* transport);
boolean transport_connect_tls(rdpTransport* transport);
boolean transport_connect_nla(rdpTransport* transport);
boolean transport_accept_rdp(rdpTransport* transport);
boolean transport_accept_tls(rdpTransport* transport);
boolean transport_accept_nla(rdpTransport* transport);
int transport_read(rdpTransport* transport, STREAM* s);
int transport_write(rdpTransport* transport, STREAM* s);
int transport_check_fds(rdpTransport* transport);

27
server/CMakeLists.txt Normal file
View File

@ -0,0 +1,27 @@
# FreeRDP: A Remote Desktop Protocol Client
# FreeRDP Servers
#
# Copyright 2011 O.S. Systems Software Ltda.
# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# Licensed 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.
# Servers
if(NOT WIN32)
# Build Test Server
add_subdirectory(test)
endif()

View File

@ -0,0 +1,25 @@
# FreeRDP: A Remote Desktop Protocol Client
# FreeRDP Test Server cmake build script
#
# Copyright 2011 O.S. Systems Software Ltda.
# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# Licensed 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.
add_executable(freerdp-server-test
freerdp_server.c)
target_link_libraries(freerdp-server-test freerdp-core)
target_link_libraries(freerdp-server-test freerdp-utils)
target_link_libraries(freerdp-server-test freerdp-rfx)

View File

@ -0,0 +1,186 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* FreeRDP Test Server
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <freerdp/utils/memory.h>
#include <freerdp/utils/thread.h>
#include <freerdp/listener.h>
static void* test_peer_mainloop(void* arg)
{
freerdp_peer* client = (freerdp_peer*)arg;
int i;
int fds;
int max_fds;
int rcount;
void* rfds[32];
fd_set rfds_set;
memset(rfds, 0, sizeof(rfds));
printf("We've got a client %s\n", client->settings->hostname);
client->settings->cert_file = xstrdup("server.crt");
client->settings->privatekey_file = xstrdup("server.key");
client->settings->nla_security = False;
client->Initialize(client);
while (1)
{
rcount = 0;
if (client->GetFileDescriptor(client, rfds, &rcount) != True)
{
printf("Failed to get FreeRDP file descriptor\n");
break;
}
max_fds = 0;
FD_ZERO(&rfds_set);
for (i = 0; i < rcount; i++)
{
fds = (int)(long)(rfds[i]);
if (fds > max_fds)
max_fds = fds;
FD_SET(fds, &rfds_set);
}
if (max_fds == 0)
break;
if (select(max_fds + 1, &rfds_set, NULL, NULL, NULL) == -1)
{
/* these are not really errors */
if (!((errno == EAGAIN) ||
(errno == EWOULDBLOCK) ||
(errno == EINPROGRESS) ||
(errno == EINTR))) /* signal occurred */
{
printf("select failed\n");
break;
}
}
if (client->CheckFileDescriptor(client) != True)
break;
}
printf("Client %s disconnected.\n", client->settings->hostname);
client->Disconnect(client);
freerdp_peer_free(client);
return NULL;
}
static void test_peer_accepted(freerdp_listener* instance, freerdp_peer* client)
{
pthread_t th;
pthread_create(&th, 0, test_peer_mainloop, client);
pthread_detach(th);
}
static void test_server_mainloop(freerdp_listener* instance)
{
int i;
int fds;
int max_fds;
int rcount;
void* rfds[32];
fd_set rfds_set;
memset(rfds, 0, sizeof(rfds));
while (1)
{
rcount = 0;
if (instance->GetFileDescriptor(instance, rfds, &rcount) != True)
{
printf("Failed to get FreeRDP file descriptor\n");
break;
}
max_fds = 0;
FD_ZERO(&rfds_set);
for (i = 0; i < rcount; i++)
{
fds = (int)(long)(rfds[i]);
if (fds > max_fds)
max_fds = fds;
FD_SET(fds, &rfds_set);
}
if (max_fds == 0)
break;
if (select(max_fds + 1, &rfds_set, NULL, NULL, NULL) == -1)
{
/* these are not really errors */
if (!((errno == EAGAIN) ||
(errno == EWOULDBLOCK) ||
(errno == EINPROGRESS) ||
(errno == EINTR))) /* signal occurred */
{
printf("select failed\n");
break;
}
}
if (instance->CheckFileDescriptor(instance) != True)
{
printf("Failed to check FreeRDP file descriptor\n");
break;
}
}
instance->Close(instance);
}
int main(int argc, char* argv[])
{
freerdp_listener* instance;
instance = freerdp_listener_new();
instance->PeerAccepted = test_peer_accepted;
/* Open the server socket and start listening. */
if (instance->Open(instance, (argc > 1 ? argv[1] : NULL), 3389))
{
/* Entering the server main loop. In a real server the listener can be run in its own thread. */
test_server_mainloop(instance);
}
freerdp_listener_free(instance);
return 0;
}

17
server/test/server.crt Normal file
View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICyzCCAbOgAwIBAgIJANbqtAWwlQZuMA0GCSqGSIb3DQEBBQUAMBIxEDAOBgNV
BAMTB0ZyZWVSRFAwHhcNMDkxMDI5MDA0MTQ5WhcNMDkxMTI4MDA0MTQ5WjASMRAw
DgYDVQQDEwdGcmVlUkRQMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
q7mxFgRbS2FYJZX7BzpNd4T/n4nEVDBY6YaObLjGpaB1TptzXTcmfDrDslTGwcEY
hTFAC4ZvY6yOURExqbph4LSgvkoa6J722RjVPfshGa4mlh2SXvTiaV26VPPxddGb
o6fbs2u029lbtBlpIVbhx5RN9vstNkll26oSZ6wfEdBNHQJLd2SU4ItWHj8zjz1f
eGxjgChHihUlwcBYKDJsKFkzHZmLrMgB37KsGlXi/WV+eEsjgvz4yP7I3TL8+GsN
MjV8fRGVEKTbKSmgunO67d5u+IaqUQb0Ad1ha1jzDQ+a6hdymrulJSIhoOVfKkwi
ptTe43FgwxVRIygJP9HjHQIDAQABoyQwIjATBgNVHSUEDDAKBggrBgEFBQcDATAL
BgNVHQ8EBAMCBDAwDQYJKoZIhvcNAQEFBQADggEBAIOdEDhOX2kbl02znltd9hCr
nV4kRPKm979RKwBNkrEuwYSlcsjAHg5MZ5itH3wFOUo2s5pjt7/vMOAg+6rOBbIa
nqr22/gKBtOmuaJLG1yjxDC2vfez7f3B26pKgxa/krM8oxiFdT9n8QbdxdkN7/D9
3RLU/aCfgrMzXxRus7eq3kR00jnSs6ggnAfE1E9gric3vFgr1wCzdcriRXmXDfUb
hRq+4VG+ZWk16TwCofV5GVU39XWCv5HNO2swAdjkNXgI5e3tQbV3wWLZLqqYzBco
iWulAXtoCGmE81+u1Ms7hLLzpXitLZSGPu1r+sDdkKPLCmOvkAaljDQ4nBz7fIA=
-----END CERTIFICATE-----

27
server/test/server.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAq7mxFgRbS2FYJZX7BzpNd4T/n4nEVDBY6YaObLjGpaB1Tptz
XTcmfDrDslTGwcEYhTFAC4ZvY6yOURExqbph4LSgvkoa6J722RjVPfshGa4mlh2S
XvTiaV26VPPxddGbo6fbs2u029lbtBlpIVbhx5RN9vstNkll26oSZ6wfEdBNHQJL
d2SU4ItWHj8zjz1feGxjgChHihUlwcBYKDJsKFkzHZmLrMgB37KsGlXi/WV+eEsj
gvz4yP7I3TL8+GsNMjV8fRGVEKTbKSmgunO67d5u+IaqUQb0Ad1ha1jzDQ+a6hdy
mrulJSIhoOVfKkwiptTe43FgwxVRIygJP9HjHQIDAQABAoIBAAVv5K54xtc1JtBR
1lfdPbSqDlnjx8aOnVIPg5TnqMp3sR8jBt0NsPc/+RA9ZOmfjoIxFAEJaZ9zSDJC
5BqmnxC5R1mfCQkSd2haQ+4pdFvWyrv4Bblh8YU6hXrJGn0LfO0KlIcywtAvKpsi
LtTyZkWmaW2HeF/+pO32jYygw38R1wd8Tl6GwjOXwTF6lFACJXOT4YAzcfp3FKSB
AiKBIGuMzozoSND7KPFNRrhGhNumJpdS5A8Fb8D2c/ZMv6Cq5IbwOgTfKun+Bz+s
mFbnzeb1uWRqQbsVXOBBW/zHfuG3SU5qeZsaAyuu4DTy+LE1oAHF9uhBSHuT5C6i
vCJ8A8ECgYEA1iaOmiEJYBrs25iAc4SjCKqhY0mwR3wtu3I06vmgUoML5fhPMv36
SvYQIqDyNw3p7TE6mZtw9+G+kK3PqhuJhogwSwg0a6o51RdKnhXH3/68oNWtKCLC
1AmR8q/Gd3FwAR3b49CuOIZ9uOiJrc/ejzKdFEJTDR1/TX1frWfZznECgYEAzUiz
XxFf7YrGel7JgmfRD2eZRYngOoteFlg5Tee42UjeAY2Pt2aiDLk+2TqQEdI9+Xg7
LcFdBqcSNd8bh33xSzgNthIkX+lTDzx0SmKGfyxfFBJcY8nzsLvvnNt3YeuMeaJQ
CPszwoZ0jcD46jTCjbrKhaLyEWmUkDp1O71NTW0CgYAXKF49Xpsz8FVyvcAOPeaf
dkwzf3F3mX8ciRId4taqdY9g1AREgGCDoK5IAF2RBIkqZCtxFvUVaS0BWjpdq9Ko
YKvQQVfh2KueVoF0LOjLWTGutsydzXyCD3Lf6pAstHCnPkJcFWHxrOGFkGfrCtKH
a7K+0RlIDsuIZqllCBjukQKBgA31+MTpYJW+D1t5IMkumEgs6n6RLt+sZLyuSU9k
B+03CGogn3qAj1rAKmcJlYywuKhDpfqpoNL3/8QMJUokpYlRCZWtTC39pzltCheY
9b6mXNz3lrLupBUL4vLO9iKBq28GO90wgEelbz3ItuTuq6CJ6IYIG+BVRtY8M4bZ
i+1NAoGANXZjYnJYDnh8Je9SDxDSc5byzK7ddkQoId64RCIfNHqNKH63P81vjgnH
YBIPtagY75ZVVNxujCF7m8Rety+d8tEFwfQKDin2EVI7PD2rOJra385/izp7HuBR
vqxvLzG9Xv3cNOU2l7PttVw4Pa2i5E37atKi3V3Zp2kMW+KaKPQ=
-----END RSA PRIVATE KEY-----