Merge pull request #1 from llyzs/network

Initial transport layer implementation
This commit is contained in:
Marc-André Moreau 2011-07-03 05:01:57 -07:00
commit 3a9e3e51aa
9 changed files with 499 additions and 2 deletions

View File

@ -21,6 +21,7 @@ include_directories(${CUNIT_INCLUDE_DIRS})
include_directories(.)
include_directories(../include)
include_directories(../libfreerdp-core)
include_directories(../libfreerdp-gdi)
add_executable(test_freerdp
@ -30,11 +31,14 @@ add_executable(test_freerdp
test_libgdi.h
test_stream.c
test_stream.h
test_transport.c
test_transport.h
test_freerdp.c
test_freerdp.h)
target_link_libraries(test_freerdp ${CUNIT_LIBRARIES})
target_link_libraries(test_freerdp freerdp-core)
target_link_libraries(test_freerdp freerdp-gdi)
target_link_libraries(test_freerdp freerdp-asn1)
target_link_libraries(test_freerdp freerdp-utils)

View File

@ -22,6 +22,7 @@
#include "test_color.h"
#include "test_libgdi.h"
#include "test_stream.h"
#include "test_transport.h"
#include "test_freerdp.h"
void dump_data(unsigned char * p, int len, int width, char* name)
@ -63,6 +64,7 @@ int main(int argc, char* argv[])
add_color_suite();
add_libgdi_suite();
add_stream_suite();
add_transport_suite();
}
else
{
@ -80,6 +82,10 @@ int main(int argc, char* argv[])
{
add_stream_suite();
}
else if (strcmp("transport", argv[*pindex]) == 0)
{
add_transport_suite();
}
*pindex = *pindex + 1;
}

99
cunit/test_transport.c Normal file
View File

@ -0,0 +1,99 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* Transport Unit Tests
*
* 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 <string.h>
#include <stdlib.h>
#include <freerdp/freerdp.h>
#include <freerdp/utils/hexdump.h>
#include <freerdp/utils/stream.h>
#include "tpkt.h"
#include "transport.h"
#include "test_transport.h"
static const char test_server[] = "192.168.0.1";
static const uint8 test_x224_req[] =
{
"\x03\x00\x00\x2C\x27\xE0\x00\x00\x00\x00\x00\x43\x6F\x6F\x6B\x69"
"\x65\x3A\x20\x6D\x73\x74\x73\x68\x61\x73\x68\x3D\x65\x6C\x74\x6F"
"\x6e\x73\x0D\x0A\x01\x00\x08\x00\x00\x00\x00\x00"
};
int init_transport_suite(void)
{
return 0;
}
int clean_transport_suite(void)
{
return 0;
}
int add_transport_suite(void)
{
add_test_suite(transport);
add_test_function(transport);
return 0;
}
static int test_finished = 0;
static int
packet_received(STREAM * stream, void * callback_data)
{
uint16 len;
len = tpkt_read_header(stream);
CU_ASSERT(len == 19);
freerdp_hexdump(stream->buffer, len);
test_finished = 1;
}
void test_transport(void)
{
rdpTransport * transport;
STREAM * stream;
int r;
transport = transport_new();
transport->recv_callback = packet_received;
transport->recv_callback_data = NULL;
r = transport_connect(transport, test_server, 3389);
CU_ASSERT(r == 0);
stream = stream_new(sizeof(test_x224_req));
stream_write_buffer(stream, test_x224_req, sizeof(test_x224_req));
r = transport_send(transport, stream);
CU_ASSERT(r == 0);
while (!test_finished)
{
transport_check_fds(transport);
sleep(1);
}
r = transport_disconnect(transport);
CU_ASSERT(r == 0);
transport_free(transport);
}

26
cunit/test_transport.h Normal file
View File

@ -0,0 +1,26 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* Transport Unit Tests
*
* 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 "test_freerdp.h"
int init_transport_suite(void);
int clean_transport_suite(void);
int add_transport_suite(void);
void test_transport(void);

View File

@ -45,6 +45,8 @@ stream_extend(STREAM * stream);
#define stream_get_pos(_s) (_s->ptr - _s->buffer)
#define stream_set_pos(_s,_m) _s->ptr = _s->buffer + (_m)
#define stream_seek(_s,_offset) _s->ptr += (_offset)
#define stream_get_head(_s) _s->buffer
#define stream_get_tail(_s) _s->ptr
#define stream_read_uint8(_s, _v) do { _v = *_s->ptr++; } while (0)
#define stream_read_uint16(_s, _v) do { _v = \
@ -87,6 +89,10 @@ stream_extend(STREAM * stream);
*_s->ptr++ = ((_v) >> 40) & 0xFF; \
*_s->ptr++ = ((_v) >> 48) & 0xFF; \
*_s->ptr++ = ((_v) >> 56) & 0xFF; } while (0)
#define stream_write_buffer(_s, _b, _n) do { \
memcpy(_s->ptr, (_b), (_n)); \
_s->ptr += (_n); \
} while (0)
#define stream_peek_uint8(_s, _v) do { _v = *_s->ptr; } while (0)
#define stream_peek_uint16(_s, _v) do { _v = \
@ -110,5 +116,20 @@ stream_extend(STREAM * stream);
(((uint64)(*(_s->ptr + 7))) << 56); \
} while (0)
#define stream_read_uint16_be(_s, _v) do { _v = \
(((uint16)(*_s->ptr)) << 8) + \
(uint16)(*(_s->ptr + 1)); \
_s->ptr += 2; } while (0)
#define stream_write_uint16_be(_s, _v) do { \
*_s->ptr++ = ((_v) >> 8) & 0xFF; \
*_s->ptr++ = (_v) & 0xFF; } while (0)
#define stream_copy(_dst, _src, _n) do { \
memcpy(_dst->ptr, _src->ptr, _n); \
_dst->ptr += _n; \
_src->ptr += _n; \
} while (0)
#endif /* __STREAM_UTILS_H */

View File

@ -34,6 +34,8 @@ set(LIBFREERDP_CORE_SRCS
tpdu.h
tpkt.c
tpkt.h
transport.c
transport.h
)
add_library(freerdp-core SHARED ${LIBFREERDP_CORE_SRCS})

View File

@ -64,7 +64,7 @@ tpkt_read_header(STREAM* s)
if (version == 3)
{
stream_seek(s, 2);
stream_read_uint16(s, length);
stream_read_uint16_be(s, length);
}
else
{
@ -80,5 +80,5 @@ tpkt_write_header(STREAM* s, int length)
{
stream_write_uint8(s, 3); /* version */
stream_write_uint8(s, 8); /* reserved */
stream_write_uint16(s, length); /* length */
stream_write_uint16_be(s, length); /* length */
}

285
libfreerdp-core/transport.c Normal file
View File

@ -0,0 +1,285 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* Network Transport Layer
*
* 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 <freerdp/utils/stream.h>
#include <freerdp/utils/memory.h>
#include <time.h>
#include <errno.h>
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include "tpkt.h"
#include "transport.h"
#define BUFFER_SIZE 16384
rdpTransport *
transport_new(void)
{
rdpTransport * transport;
transport = (rdpTransport *) xmalloc(sizeof(rdpTransport));
memset(transport, 0, sizeof(rdpTransport));
transport->sockfd = -1;
/* a small 0.1ms delay when transport is blocking. */
transport->ts.tv_sec = 0;
transport->ts.tv_nsec = 100000;
/* receive buffer for non-blocking read. */
transport->recv_buffer = stream_new(BUFFER_SIZE);
return transport;
}
void
transport_free(rdpTransport * transport)
{
stream_free(transport->recv_buffer);
xfree(transport);
}
static int
transport_connect_sockfd(rdpTransport * transport, const char * server, int port)
{
struct addrinfo hints = { 0 };
struct addrinfo * res, * ai;
int r;
char servname[10];
int sockfd = -1;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
snprintf(servname, sizeof(servname), "%d", port);
r = getaddrinfo(server, servname, &hints, &res);
if (r != 0)
{
printf("transport_connect: getaddrinfo (%s)\n", gai_strerror(r));
return -1;
}
for (ai = res; ai; ai = ai->ai_next)
{
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd < 0)
continue;
r = connect(sockfd, ai->ai_addr, ai->ai_addrlen);
if (r == 0)
{
printf("connected to %s:%s\n", server, servname);
break;
}
sockfd = -1;
}
freeaddrinfo(res);
if (sockfd == -1)
{
printf("unable to connect to %s:%s\n", server, servname);
return -1;
}
transport->sockfd = sockfd;
return 0;
}
static int
transport_configure_sockfd(rdpTransport * transport)
{
int flags;
flags = fcntl(transport->sockfd, F_GETFL);
if (flags == -1)
{
printf("transport_configure_sockfd: fcntl failed.\n");
return -1;
}
fcntl(transport->sockfd, F_SETFL, flags | O_NONBLOCK);
return 0;
}
int
transport_connect(rdpTransport * transport, const char * server, int port)
{
int r;
r = transport_connect_sockfd(transport, server, port);
if (r != 0)
return r;
r = transport_configure_sockfd(transport);
if (r != 0)
return r;
return 0;
}
int
transport_disconnect(rdpTransport * transport)
{
if (transport->sockfd != -1)
{
close(transport->sockfd);
transport->sockfd = -1;
}
return 0;
}
int
transport_start_tls(rdpTransport * transport)
{
return 0;
}
static int
transport_delay(rdpTransport * transport)
{
nanosleep(&transport->ts, NULL);
return 0;
}
static int
transport_send_tls(rdpTransport * transport, STREAM * stream)
{
return 0;
}
static int
transport_send_tcp(rdpTransport * transport, STREAM * stream)
{
uint8 * head;
uint8 * tail;
int r;
head = stream_get_head(stream);
tail = stream_get_tail(stream);
while (head < tail)
{
r = send(transport->sockfd, head, tail - head, MSG_NOSIGNAL);
if (r < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
if (transport_delay(transport) != 0)
return -1;
continue;
}
printf("transport_send_tcp: send (%d)\n", errno);
return -1;
}
head += r;
}
return 0;
}
int
transport_send(rdpTransport * transport, STREAM * stream)
{
if (transport->tls)
return transport_send_tls(transport, stream);
else
return transport_send_tcp(transport, stream);
}
static int
transport_recv_tls(rdpTransport * transport)
{
return 0;
}
static int
transport_recv_tcp(rdpTransport * transport)
{
int r;
stream_check_capacity(transport->recv_buffer, BUFFER_SIZE);
r = recv(transport->sockfd, transport->recv_buffer->ptr, BUFFER_SIZE, 0);
if (r == -1)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
return 0;
printf("transport_recv_tcp: recv failed (%d).\n", errno);
return -1;
}
stream_seek(transport->recv_buffer, r);
return r;
}
int
transport_check_fds(rdpTransport * transport)
{
int r;
int pos;
uint16 len;
STREAM * received;
if (transport->tls)
r = transport_recv_tls(transport);
else
r = transport_recv_tcp(transport);
if (r <= 0)
return r;
pos = stream_get_pos(transport->recv_buffer);
/* Ensure the TPKT header is available. */
if (pos <= 4)
return 0;
stream_set_pos(transport->recv_buffer, 0);
len = tpkt_read_header(transport->recv_buffer);
if (len == 0)
{
printf("transport_check_fds: protocol error, not a TPKT header.\n");
return -1;
}
if (pos < len)
return 0; /* Packet is not yet completely received. */
/* A complete packet has been received. In case there are trailing data
* for the next packet, we copy it to the new receive buffer.
*/
received = transport->recv_buffer;
transport->recv_buffer = stream_new(BUFFER_SIZE);
if (pos > len)
{
stream_set_pos(received, len);
stream_check_capacity(transport->recv_buffer, pos - len);
stream_copy(transport->recv_buffer, received, pos - len);
}
stream_set_pos(received, 0);
r = transport->recv_callback(received, transport->recv_callback_data);
stream_free(received);
return r;
}

View File

@ -0,0 +1,54 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* Network Transport Layer
*
* 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 __TRANSPORT_H
#define __TRANSPORT_H
#include <freerdp/utils/stream.h>
typedef int (* PacketReceivedCallback) (STREAM * stream, void * callback_data);
struct rdp_transport
{
int sockfd;
struct crypto_tls * tls;
struct timespec ts;
STREAM * recv_buffer;
PacketReceivedCallback recv_callback;
void * recv_callback_data;
};
typedef struct rdp_transport rdpTransport;
rdpTransport *
transport_new(void);
void
transport_free(rdpTransport * transport);
int
transport_connect(rdpTransport * transport, const char * server, int port);
int
transport_disconnect(rdpTransport * transport);
int
transport_start_tls(rdpTransport * transport);
int
transport_send(rdpTransport * transport, STREAM * stream);
int
transport_check_fds(rdpTransport * transport);
#endif