diff --git a/common/file.c b/common/file.c index c8be7af5..05f243ca 100644 --- a/common/file.c +++ b/common/file.c @@ -242,8 +242,19 @@ l_file_read_section(int fd, int max_file_size, const char *section, for (index = 0; index < len; index++) { + if (!s_check_rem(s, 1)) + { + break; + } in_uint8(s, c); - + if ((c == '#') || (c == ';')) + { + file_read_line(s, text); + in_it = 0; + in_it_index = 0; + g_memset(text, 0, 512); + continue; + } if (c == '[') { in_it = 1; @@ -253,7 +264,6 @@ l_file_read_section(int fd, int max_file_size, const char *section, if (g_strcasecmp(section, text) == 0) { file_read_line(s, text); - while (file_read_line(s, text) == 0) { if (g_strlen(text) > 0) @@ -296,7 +306,6 @@ l_file_read_section(int fd, int max_file_size, const char *section, } } } - free_stream(s); return 1; } diff --git a/common/os_calls.c b/common/os_calls.c index 500db201..af54facf 100644 --- a/common/os_calls.c +++ b/common/os_calls.c @@ -1,7 +1,7 @@ /** * xrdp: A Remote Desktop Protocol server. * - * Copyright (C) Jay Sorg 2004-2012 + * Copyright (C) Jay Sorg 2004-2013 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -427,29 +427,42 @@ g_tcp_set_keepalive(int sck) /*****************************************************************************/ /* returns a newly created socket or -1 on error */ +/* in win32 a socket is an unsigned int, in linux, its an int */ int APP_CC g_tcp_socket(void) { -#if defined(_WIN32) int rv; int option_value; +#if defined(_WIN32) int option_len; #else - int rv; - int option_value; unsigned int option_len; #endif - /* in win32 a socket is an unsigned int, in linux, its an int */ - rv = (int)socket(PF_INET, SOCK_STREAM, 0); - +#if !defined(NO_ARPA_INET_H_IP6) + rv = (int)socket(AF_INET6, SOCK_STREAM, 0); +#else + rv = (int)socket(AF_INET, SOCK_STREAM, 0); +#endif if (rv < 0) { return -1; } - +#if !defined(NO_ARPA_INET_H_IP6) + option_len = sizeof(option_value); + if (getsockopt(rv, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&option_value, + &option_len) == 0) + { + if (option_value != 0) + { + option_value = 0; + option_len = sizeof(option_value); + setsockopt(rv, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&option_value, + option_len); + } + } +#endif option_len = sizeof(option_value); - if (getsockopt(rv, SOL_SOCKET, SO_REUSEADDR, (char *)&option_value, &option_len) == 0) { @@ -494,17 +507,18 @@ g_tcp_local_socket(void) void APP_CC g_tcp_close(int sck) { - char ip[256] ; + char ip[256]; + if (sck == 0) { return; } - #if defined(_WIN32) closesocket(sck); #else - g_write_ip_address(sck,ip,255); - log_message(LOG_LEVEL_INFO,"An established connection closed to endpoint: %s", ip); + g_write_ip_address(sck, ip, 255); + log_message(LOG_LEVEL_INFO, "An established connection closed to " + "endpoint: %s", ip); close(sck); #endif } @@ -514,34 +528,55 @@ g_tcp_close(int sck) int APP_CC g_tcp_connect(int sck, const char *address, const char *port) { - struct sockaddr_in s; - struct hostent *h; - - g_memset(&s, 0, sizeof(struct sockaddr_in)); - s.sin_family = AF_INET; - s.sin_port = htons((tui16)atoi(port)); - s.sin_addr.s_addr = inet_addr(address); - - if (s.sin_addr.s_addr == INADDR_NONE) + int res = 0; + struct addrinfo p; + struct addrinfo *h = (struct addrinfo *)NULL; + struct addrinfo *rp = (struct addrinfo *)NULL; + + /* initialize (zero out) local variables: */ + g_memset(&p, 0, sizeof(struct addrinfo)); + + /* in IPv6-enabled environments, set the AI_V4MAPPED + * flag in ai_flags and specify ai_family=AF_INET6 in + * order to ensure that getaddrinfo() returns any + * available IPv4-mapped addresses in case the target + * host does not have a true IPv6 address: + */ + p.ai_socktype = SOCK_STREAM; + p.ai_protocol = IPPROTO_TCP; +#if !defined(NO_ARPA_INET_H_IP6) + p.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED; + p.ai_family = AF_INET6; + if (g_strcmp(address, "127.0.0.1") == 0) + { + res = getaddrinfo("::1", port, &p, &h); + } + else { - h = gethostbyname(address); - - if (h != 0) + res = getaddrinfo(address, port, &p, &h); + } +#else + p.ai_flags = AI_ADDRCONFIG; + p.ai_family = AF_INET; + res = getaddrinfo(address, port, &p, &h); +#endif + if (res > -1) + { + if (h != NULL) { - if (h->h_name != 0) + for (rp = h; rp != NULL; rp = rp->ai_next) { - if (h->h_addr_list != 0) + rp = h; + res = connect(sck, (struct sockaddr *)(rp->ai_addr), + rp->ai_addrlen); + if (res != -1) { - if ((*(h->h_addr_list)) != 0) - { - s.sin_addr.s_addr = *((int *)(*(h->h_addr_list))); - } + break; /* Success */ } } } } - - return connect(sck, (struct sockaddr *)&s, sizeof(struct sockaddr_in)); + return res; } /*****************************************************************************/ @@ -578,23 +613,117 @@ g_tcp_set_non_blocking(int sck) return 0; } +/*****************************************************************************/ +/* return boolean */ +static int APP_CC +address_match(const char *address, struct addrinfo *j) +{ + struct sockaddr_in *ipv4_in; + struct sockaddr_in6 *ipv6_in; + + if (address == 0) + { + return 1; + } + if (address[0] == 0) + { + return 1; + } + if (g_strcmp(address, "0.0.0.0") == 0) + { + return 1; + } + if ((g_strcmp(address, "127.0.0.1") == 0) || + (g_strcmp(address, "::1") == 0) || + (g_strcmp(address, "localhost") == 0)) + { + if (j->ai_addr != 0) + { + if (j->ai_addr->sa_family == AF_INET) + { + ipv4_in = (struct sockaddr_in *) (j->ai_addr); + if (inet_pton(AF_INET, "127.0.0.1", &(ipv4_in->sin_addr))) + { + return 1; + } + } + if (j->ai_addr->sa_family == AF_INET6) + { + ipv6_in = (struct sockaddr_in6 *) (j->ai_addr); + if (inet_pton(AF_INET6, "::1", &(ipv6_in->sin6_addr))) + { + return 1; + } + } + } + } + if (j->ai_addr != 0) + { + if (j->ai_addr->sa_family == AF_INET) + { + ipv4_in = (struct sockaddr_in *) (j->ai_addr); + if (inet_pton(AF_INET, address, &(ipv4_in->sin_addr))) + { + return 1; + } + } + if (j->ai_addr->sa_family == AF_INET6) + { + ipv6_in = (struct sockaddr_in6 *) (j->ai_addr); + if (inet_pton(AF_INET6, address, &(ipv6_in->sin6_addr))) + { + return 1; + } + } + } + return 0; +} + +/*****************************************************************************/ +/* returns error, zero is good */ +static int APP_CC +g_tcp_bind_flags(int sck, const char *port, const char *address, int flags) +{ + int res; + int error; + struct addrinfo hints; + struct addrinfo *i; + + res = -1; + g_memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = flags; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + error = getaddrinfo(NULL, port, &hints, &i); + if (error == 0) + { + while ((i != 0) && (res < 0)) + { + if (address_match(address, i)) + { + res = bind(sck, i->ai_addr, i->ai_addrlen); + } + i = i->ai_next; + } + } + return res; +} + /*****************************************************************************/ /* returns error, zero is good */ int APP_CC -g_tcp_bind(int sck, char *port) +g_tcp_bind(int sck, const char *port) { - struct sockaddr_in s; + int flags; - memset(&s, 0, sizeof(struct sockaddr_in)); - s.sin_family = AF_INET; - s.sin_port = htons((tui16)atoi(port)); - s.sin_addr.s_addr = INADDR_ANY; - return bind(sck, (struct sockaddr *)&s, sizeof(struct sockaddr_in)); + flags = AI_ADDRCONFIG | AI_PASSIVE; + return g_tcp_bind_flags(sck, port, 0, flags); } /*****************************************************************************/ int APP_CC -g_tcp_local_bind(int sck, char *port) +g_tcp_local_bind(int sck, const char *port) { #if defined(_WIN32) return -1; @@ -611,21 +740,12 @@ g_tcp_local_bind(int sck, char *port) /*****************************************************************************/ /* returns error, zero is good */ int APP_CC -g_tcp_bind_address(int sck, char *port, const char *address) +g_tcp_bind_address(int sck, const char *port, const char *address) { - struct sockaddr_in s; - - memset(&s, 0, sizeof(struct sockaddr_in)); - s.sin_family = AF_INET; - s.sin_port = htons((tui16)atoi(port)); - s.sin_addr.s_addr = INADDR_ANY; - - if (inet_aton(address, &s.sin_addr) < 0) - { - return -1; /* bad address */ - } + int flags; - return bind(sck, (struct sockaddr *)&s, sizeof(struct sockaddr_in)); + flags = AI_ADDRCONFIG | AI_PASSIVE; + return g_tcp_bind_flags(sck, port, address, flags); } /*****************************************************************************/ @@ -2017,7 +2137,7 @@ g_htoi(char *str) /*****************************************************************************/ int APP_CC -g_pos(char *str, const char *to_find) +g_pos(const char *str, const char *to_find) { char *pp; diff --git a/common/os_calls.h b/common/os_calls.h index d4b86365..5844df27 100644 --- a/common/os_calls.h +++ b/common/os_calls.h @@ -1,7 +1,7 @@ /** * xrdp: A Remote Desktop Protocol server. * - * Copyright (C) Jay Sorg 2004-2012 + * Copyright (C) Jay Sorg 2004-2013 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,11 +78,11 @@ g_tcp_force_recv(int sck, char* data, int len); int APP_CC g_tcp_set_non_blocking(int sck); int APP_CC -g_tcp_bind(int sck, char* port); +g_tcp_bind(int sck, const char *port); int APP_CC -g_tcp_local_bind(int sck, char* port); +g_tcp_local_bind(int sck, const char* port); int APP_CC -g_tcp_bind_address(int sck, char* port, const char* address); +g_tcp_bind_address(int sck, const char* port, const char* address); int APP_CC g_tcp_listen(int sck); int APP_CC @@ -196,7 +196,7 @@ g_atoi(const char* str); int APP_CC g_htoi(char* str); int APP_CC -g_pos(char* str, const char* to_find); +g_pos(const char* str, const char* to_find); int APP_CC g_mbstowcs(twchar* dest, const char* src, int n); int APP_CC diff --git a/configure.ac b/configure.ac index b8c03445..52a44b3c 100644 --- a/configure.ac +++ b/configure.ac @@ -69,6 +69,11 @@ then fi fi +AC_CHECK_MEMBER([struct in6_addr.s6_addr], + [], + [AC_DEFINE(NO_ARPA_INET_H_IP6, 1, [for IPv6])], + [#include ]) + if test "x$enable_nopam" = "xyes" then AC_DEFINE([USE_NOPAM],1,[Disable PAM]) diff --git a/instfiles/pam.d/xrdp-sesman b/instfiles/pam.d/xrdp-sesman index 7fdbee5b..789ce8f7 100644 --- a/instfiles/pam.d/xrdp-sesman +++ b/instfiles/pam.d/xrdp-sesman @@ -1,4 +1,5 @@ #%PAM-1.0 -auth required pam_unix.so shadow nullok -auth required pam_env.so readenv=1 -account required pam_unix.so +@include common-auth +@include common-account +@include common-session +@include common-password diff --git a/sesman/chansrv/chansrv.c b/sesman/chansrv/chansrv.c index 74e2db4a..0f7ff042 100644 --- a/sesman/chansrv/chansrv.c +++ b/sesman/chansrv/chansrv.c @@ -1,7 +1,7 @@ /** * xrdp: A Remote Desktop Protocol server. * - * Copyright (C) Jay Sorg 2009-2012 + * Copyright (C) Jay Sorg 2009-2013 * Copyright (C) Laxmikant Rashinkar 2009-2012 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -45,6 +45,7 @@ static int g_rdpsnd_index = -1; static int g_rdpdr_index = -1; static int g_rail_index = -1; static int g_drdynvc_index = -1; +static int g_sent = 0; /* if sent data to xrdp, waiting response */ /* state info for dynamic virtual channels */ static struct xrdp_api_data *g_dvc_channels[MAX_DVC_CHANNELS]; @@ -149,6 +150,7 @@ send_data_from_chan_item(struct chan_item *chan_item) s_mark_end(s); LOGM((LOG_LEVEL_DEBUG, "chansrv::send_channel_data: -- " "size %d chan_flags 0x%8.8x", size, chan_flags)); + g_sent = 1; error = trans_force_write(g_con_trans); if (error != 0) @@ -214,7 +216,10 @@ send_channel_data(int chan_id, char *data, int size) if (g_chan_items[index].id == chan_id) { add_data_to_chan_item(g_chan_items + index, data, size); - check_chan_items(); + if (g_sent == 0) + { + check_chan_items(); + } return 0; } } @@ -478,6 +483,7 @@ static int APP_CC process_message_channel_data_response(struct stream *s) { LOG(10, ("process_message_channel_data_response:")); + g_sent = 0; check_chan_items(); return 0; } diff --git a/sesman/chansrv/pulse/module-xrdp-sink.c b/sesman/chansrv/pulse/module-xrdp-sink.c index 017d757e..92f4b674 100644 --- a/sesman/chansrv/pulse/module-xrdp-sink.c +++ b/sesman/chansrv/pulse/module-xrdp-sink.c @@ -262,7 +262,6 @@ static int data_send(struct userdata *u, pa_memchunk *chunk) { char *data; int bytes; int sent; - int display_num; int fd; struct header h; struct sockaddr_un s; @@ -276,9 +275,8 @@ static int data_send(struct userdata *u, pa_memchunk *chunk) { fd = socket(PF_LOCAL, SOCK_STREAM, 0); memset(&s, 0, sizeof(s)); s.sun_family = AF_UNIX; - display_num = get_display_num_from_display(getenv("DISPLAY")); bytes = sizeof(s.sun_path) - 1; - snprintf(s.sun_path, bytes, CHANSRV_PORT_STR, display_num); + snprintf(s.sun_path, bytes, CHANSRV_PORT_STR, u->display_num); pa_log("trying to conenct to %s", s.sun_path); if (connect(fd, (struct sockaddr *)&s, sizeof(struct sockaddr_un)) != 0) { @@ -296,15 +294,11 @@ static int data_send(struct userdata *u, pa_memchunk *chunk) { //pa_log("bytes %d", bytes); /* from rewind */ - if (u->skip_bytes > 0) - { - if (bytes > u->skip_bytes) - { + if (u->skip_bytes > 0) { + if (bytes > u->skip_bytes) { bytes -= u->skip_bytes; u->skip_bytes = 0; - } - else - { + } else { u->skip_bytes -= bytes; return bytes; } diff --git a/sesman/chansrv/pulse/pulse-notes.txt b/sesman/chansrv/pulse/pulse-notes.txt index 4a4b7273..b5d51572 100644 --- a/sesman/chansrv/pulse/pulse-notes.txt +++ b/sesman/chansrv/pulse/pulse-notes.txt @@ -4,7 +4,7 @@ Pulse audio notes. to see what version of PA is on your machine pulseaudio --version - +IMA ADPCM To build xrdp pulse sink, get the pulse source that most closely matches your version on @@ -25,3 +25,48 @@ autospawn = no daemon-binary = /bin/true xfreerdp -a 24 -z --plugin rdpsnd --data alsa:hw:0,0 -- 127.0.0.1 + + +to get ./configure on pulse source to run +apt-get install libsndfile1-dev +apt-get install libspeex-dev +apt-get install libspeexdsp-dev + +alsamixer +apt-get install alsa-utils + + +/etc/asound.conf +--------------------------------- +pcm.pulse { + type pulse +} + +ctl.pulse { + type pulse +} + +pcm.!default { + type pulse +} + +ctl.!default { + type pulse +} +--------------------------------- + + +/etc/pulse/default.pa +--------------------------------- +.nofail +.fail +load-module module-augment-properties +#load-module module-alsa-sink device=hw:0 +#load-module module-alsa-source device=hw:0 +#load-module module-pipe-sink +#load-module module-pipe-source +#load-module module-null-sink +load-module module-xrdp-sink +load-module module-native-protocol-unix +#load-module module-udev-detect tsched=0 +--------------------------------- diff --git a/sesman/libscp/libscp_tcp.c b/sesman/libscp/libscp_tcp.c index 29870563..30e8006c 100644 --- a/sesman/libscp/libscp_tcp.c +++ b/sesman/libscp/libscp_tcp.c @@ -26,12 +26,6 @@ #include "libscp_tcp.h" -#include -#include -#include -#include -#include - extern struct log_config *s_log; /*****************************************************************************/ @@ -124,11 +118,5 @@ scp_tcp_force_send(int sck, char *data, int len) int DEFAULT_CC scp_tcp_bind(int sck, char *addr, char *port) { - struct sockaddr_in s; - - memset(&s, 0, sizeof(struct sockaddr_in)); - s.sin_family = AF_INET; - s.sin_port = htons(atoi(port)); - s.sin_addr.s_addr = inet_addr(addr); - return bind(sck, (struct sockaddr *)&s, sizeof(struct sockaddr_in)); + return g_tcp_bind_address(sck, port, addr); } diff --git a/tests/gtcp_proxy/README.txt b/tests/gtcp_proxy/README.txt new file mode 100644 index 00000000..41907e2e --- /dev/null +++ b/tests/gtcp_proxy/README.txt @@ -0,0 +1,4 @@ + +gtcp-proxy is a 'man in the middle' program to monitor data flowing between +two network connections. + diff --git a/tests/gtcp_proxy/gtcp-proxy b/tests/gtcp_proxy/gtcp-proxy new file mode 100755 index 00000000..6896b437 Binary files /dev/null and b/tests/gtcp_proxy/gtcp-proxy differ diff --git a/tests/gtcp_proxy/gtcp-proxy.c b/tests/gtcp_proxy/gtcp-proxy.c new file mode 100644 index 00000000..39555c76 --- /dev/null +++ b/tests/gtcp_proxy/gtcp-proxy.c @@ -0,0 +1,678 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@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 +#include +#include + +#include "gtcp.h" + +#define WINDOW_TITLE "Tcp Proxy Version 1.0" +#define CONTEXT_ID 1 +#define MSG_INFO GTK_MESSAGE_INFO +#define MSG_WARN GTK_MESSAGE_WARNING +#define MSG_ERROR GTK_MESSAGE_ERROR +#define MAIN_THREAD_YES 0 +#define MAIN_THREAD_NO 1 + +/* globals */ +pthread_t g_tid; +int g_keep_running = 1; +int g_loc_io_count = 0; /* bytes read from local port */ +int g_rem_io_count = 0; /* bytes read from remote port */ + +GtkWidget *g_btn_start; +GtkWidget *g_tbx_loc_port; +GtkWidget *g_tbx_rem_ip; +GtkWidget *g_tbx_rem_port; +GtkWidget *g_tbx_loc_stats; +GtkWidget *g_tbx_rem_stats; +GtkWidget *g_statusbar; +GtkWidget *g_txtvu_loc_port; +GtkWidget *g_txtvu_rem_port; + +/* forward declarations */ +static void *tcp_proxy(void *arg); + +static void show_msg(int not_main_thread, int style, + const gchar *title, const gchar *msg); + +static void show_status(int not_main_thread, char *msg); +static void clear_status(int not_main_thread); +static void enable_btn_start(int main_thread); +static void disable_btn_start(int main_thread); + +/* getters */ +static char *get_local_port(); +static char *get_remote_ip(); +static char *get_remote_port(); + +/* setters */ +static void show_loc_port_stats(int main_thread, int count); +static void show_rem_port_stats(int main_thread, int count); + +/* handlers */ +static gboolean on_delete_event(GtkWidget *widget, GdkEvent *ev, gpointer data); +static void on_destroy(GtkWidget *widget, gpointer data); +static void on_start_clicked(GtkWidget *widget, gpointer data); +static void on_clear_clicked(GtkWidget *widget, gpointer data); +static void on_quit_clicked(GtkWidget *widget, gpointer data); + +int main(int argc, char **argv) +{ + /* init threads */ + g_thread_init(NULL); + gdk_threads_init(); + + /* setup GTK */ + gtk_init(&argc, &argv); + + /* create labels and right justify them */ + GtkWidget *lbl_loc_port = gtk_label_new("Local port"); + GtkWidget *lbl_rem_ip = gtk_label_new("Remote IP"); + GtkWidget *lbl_rem_port = gtk_label_new("Remote port"); + GtkWidget *lbl_loc_stats = gtk_label_new("Local port recv stats"); + GtkWidget *lbl_rem_stats = gtk_label_new("Remote port recv stats"); + + gtk_misc_set_alignment(GTK_MISC(lbl_loc_port), 1.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(lbl_rem_ip), 1.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(lbl_rem_port), 1.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(lbl_loc_stats), 1.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(lbl_rem_stats), 1.0, 0.5); + + /* create text boxes */ + g_tbx_loc_port = gtk_entry_new(); + g_tbx_rem_ip = gtk_entry_new(); + g_tbx_rem_port = gtk_entry_new(); + g_tbx_loc_stats = gtk_entry_new(); + g_tbx_rem_stats = gtk_entry_new(); + + /* stat text boxes are read only */ + gtk_entry_set_editable(GTK_ENTRY(g_tbx_loc_stats), FALSE); + gtk_entry_set_editable(GTK_ENTRY(g_tbx_rem_stats), FALSE); + + /* enable when debugging */ +#if 0 + gtk_entry_set_text(GTK_ENTRY(g_tbx_loc_port), "1234"); + gtk_entry_set_text(GTK_ENTRY(g_tbx_rem_ip), "192.168.2.20"); + gtk_entry_set_text(GTK_ENTRY(g_tbx_rem_port), "43222"); +#endif + + /* setup buttons inside a HBox */ + g_btn_start = gtk_button_new_with_label("Start listening"); + GtkWidget *btn_clear = gtk_button_new_with_label("Clear receive stats"); + GtkWidget *btn_quit = gtk_button_new_with_label("Quit application"); + + GtkWidget *hbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start_defaults(GTK_BOX(hbox), g_btn_start); + gtk_box_pack_start_defaults(GTK_BOX(hbox), btn_clear); + gtk_box_pack_start_defaults(GTK_BOX(hbox), btn_quit); + +#if 0 + g_txtvu_loc_port = gtk_text_view_new(); + g_txtvu_rem_port = gtk_text_view_new(); +#endif + + /* create table */ + GtkWidget *table = gtk_table_new(8, 3, FALSE); + + int row = 0; + + /* insert labels into table */ + gtk_table_attach(GTK_TABLE(table), lbl_loc_port, 0, 1, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), lbl_rem_ip, 0, 1, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), lbl_rem_port, 0, 1, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), lbl_loc_stats, 0, 1, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), lbl_rem_stats, 0, 1, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + row = 0; + + /* insert text boxes into table */ + gtk_table_attach(GTK_TABLE(table), g_tbx_loc_port, 1, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), g_tbx_rem_ip, 1, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), g_tbx_rem_port, 1, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), g_tbx_loc_stats, 1, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), g_tbx_rem_stats, 1, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + row++; + + /* insert hbox with buttons into table */ + gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + +#if 0 + /* text view to display hexdumps */ + gtk_table_attach(GTK_TABLE(table), g_txtvu_loc_port, 0, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; +#endif + + /* status bar to display messages */ + g_statusbar = gtk_statusbar_new(); + gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(g_statusbar), TRUE); + gtk_table_attach(GTK_TABLE(table), g_statusbar, 0, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + + /* setup main window */ + GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width(GTK_CONTAINER(window), 5); + gtk_window_set_default_size(GTK_WINDOW(window), 640, 480); + gtk_window_set_title(GTK_WINDOW(window), WINDOW_TITLE); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + + gtk_container_add(GTK_CONTAINER(window), table); + gtk_widget_show_all(window); + + /* setup callbacks */ + g_signal_connect(window, "delete-event", G_CALLBACK(on_delete_event), NULL); + g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), NULL); + + g_signal_connect(g_btn_start, "clicked", G_CALLBACK(on_start_clicked), NULL); + g_signal_connect(btn_clear, "clicked", G_CALLBACK(on_clear_clicked), NULL); + g_signal_connect(btn_quit, "clicked", G_CALLBACK(on_quit_clicked), NULL); + + gdk_threads_enter(); + gtk_main(); + gdk_threads_leave(); + + return 0; +} + +/** + * Start listening on specifed local socket; when we get a connection, + * connect to specified remote server and transfer data between local + * and remote server + *****************************************************************************/ + +static void *tcp_proxy(void *arg) +{ + char buf[1024 * 32]; + + int lis_skt = -1; + int acc_skt = -1; + int con_skt = -1; + int sel = 0; + int rv = 0; + int i = 0; + int count = 0; + int sent = 0; + + /* create listener socket */ + if ((lis_skt = tcp_socket_create()) < 0) + { + show_msg(MAIN_THREAD_NO, MSG_ERROR, "Creating socket", + "\nOperation failed. System out of resources"); + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } + + /* place it in non blocking mode */ + tcp_set_non_blocking(lis_skt); + + if ((rv = tcp_bind(lis_skt, get_local_port())) != 0) + { + show_msg(MAIN_THREAD_NO, MSG_ERROR, "Bind error", + "\nUnable to bind socket, cannot continue"); + tcp_close(lis_skt); + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } + + /* listen for incoming connection */ + if (tcp_listen(lis_skt)) + { + show_msg(MAIN_THREAD_NO, MSG_ERROR, "Listen error", + "\nUnable to listen on socket, cannot continue"); + tcp_close(lis_skt); + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } + + show_status(MAIN_THREAD_NO, "Waiting for client connections"); + + /* accept incoming connection */ + while (g_keep_running) + { + acc_skt = tcp_accept(lis_skt); + if (acc_skt > 0) + { + /* client connected */ + show_status(MAIN_THREAD_NO, "Client connected"); + tcp_close(lis_skt); + lis_skt = -1; + break; + } + if ((acc_skt < 0) && (tcp_last_error_would_block())) + { + /* no connection, try again */ + usleep(250); + continue; + } + else + { + tcp_close(lis_skt); + lis_skt = -1; + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } + } + + /* we have a client connection, try connecting to server */ + if ((con_skt = tcp_socket()) < 0) + { + show_msg(MAIN_THREAD_NO, MSG_ERROR, "Creating socket", + "\nOperation failed. System out of resources"); + tcp_close(lis_skt); + tcp_close(acc_skt); + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } + + /* place it in non blocking mode */ + tcp_set_non_blocking(con_skt); + + rv = tcp_connect(con_skt, get_remote_ip(), get_remote_port()); +#if 0 + if (rv < 0) + { + show_status(MAIN_THREAD_NO, "Could not connect to server"); + tcp_close(lis_skt); + tcp_close(acc_skt); + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } +#endif + + if ((rv < 0) && (tcp_last_error_would_block(con_skt))) + { + for (i = 0; i < 100; i++) + { + if (tcp_can_send(con_skt, 100)) + break; + + usleep(100); + } + + if (i == 100) + { + show_status(MAIN_THREAD_NO, "Could not connect to server"); + tcp_close(lis_skt); + tcp_close(acc_skt); + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } + } + + show_status(MAIN_THREAD_NO, "Connected to server"); + rv = 0; + + while (g_keep_running && rv == 0) + { + if ((sel = tcp_select(con_skt, acc_skt)) == 0) + { + usleep(10); + continue; + } + + if (sel & 1) + { + /* can read from con_skt without blocking */ + count = tcp_recv(con_skt, buf, 1024 * 16, 0); + rv = count < 1; + + if (rv == 0) + { + g_loc_io_count += count; + show_loc_port_stats(MAIN_THREAD_NO, g_loc_io_count); + + /* TODO: hexdump data here */ + + sent = 0; + + while ((sent < count) && (rv == 0) && (g_keep_running)) + { + i = tcp_send(acc_skt, buf + sent, count - sent, 0); + + if ((i == -1) && tcp_last_error_would_block(acc_skt)) + { + tcp_can_send(acc_skt, 1000); + } + else if (i < 1) + { + rv = 1; + } + else + { + sent += i; + } + } + } + } + + if (sel & 2) + { + /* can read from acc_skt without blocking */ + count = tcp_recv(acc_skt, buf, 1024 * 16, 0); + rv = count < 1; + + if (rv == 0) + { + g_rem_io_count += count; + show_rem_port_stats(MAIN_THREAD_NO, g_rem_io_count); + + /* TODO: hexdump data here */ + + sent = 0; + + while ((sent < count) && (rv == 0) && (g_keep_running)) + { + i = tcp_send(con_skt, buf + sent, count - sent, 0); + + if ((i == -1) && tcp_last_error_would_block(con_skt)) + { + tcp_can_send(con_skt, 1000); + } + else if (i < 1) + { + rv = 1; + } + else + { + sent += i; + } + } + } + } + } + + tcp_close(lis_skt); + tcp_close(con_skt); + tcp_close(acc_skt); + + show_status(MAIN_THREAD_NO, "Connection closed"); + enable_btn_start(MAIN_THREAD_NO); + return NULL; +} + +/** + * Display a message using specified style dialog + * + * @param style information, warning or error + * @param title text to be displayed in title bar + * @param msg message to be displayed + *****************************************************************************/ + +static void show_msg(int not_main_window, int style, + const gchar *title, const gchar *msg) +{ + GtkWidget *dialog; + + if (not_main_window) + gdk_threads_enter(); + + dialog = gtk_message_dialog_new(GTK_WINDOW(NULL), + GTK_DIALOG_DESTROY_WITH_PARENT, + style, + GTK_BUTTONS_OK, + "%s", msg); + + gtk_window_set_title(GTK_WINDOW(dialog), title); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + + if (not_main_window) + gdk_threads_leave(); +} + +/** + * Write message to status bar + *****************************************************************************/ + +static void show_status(int not_main_thread, char *msg) +{ + if (not_main_thread) + gdk_threads_enter(); + + gtk_statusbar_push(GTK_STATUSBAR(g_statusbar), CONTEXT_ID, msg); + + if (not_main_thread) + gdk_threads_leave(); +} + +/** + * Clear status bar + *****************************************************************************/ + +static void clear_status(int not_main_thread) +{ + if (not_main_thread) + gdk_threads_enter(); + + gtk_statusbar_remove_all(GTK_STATUSBAR(g_statusbar), CONTEXT_ID); + + if (not_main_thread) + gdk_threads_leave(); +} + +/** + * Enable 'Start Listening' button + *****************************************************************************/ + +static void enable_btn_start(int not_main_thread) +{ + if (not_main_thread) + gdk_threads_enter(); + + gtk_widget_set_sensitive(GTK_WIDGET(g_btn_start), TRUE); + + if (not_main_thread) + gdk_threads_leave(); +} + +/** + * Disable 'Start Listening' button + *****************************************************************************/ + +static void disable_btn_start(int not_main_thread) +{ + if (not_main_thread) + gdk_threads_enter(); + + gtk_widget_set_sensitive(GTK_WIDGET(g_btn_start), FALSE); + + if (not_main_thread) + gdk_threads_leave(); +} + +/** + * Return local port setting + *****************************************************************************/ + +static char *get_local_port() +{ + const char *cptr = gtk_entry_get_text(GTK_ENTRY(g_tbx_loc_port)); + return (char *) cptr; +} + +/** + * Return remote IP setting + *****************************************************************************/ + +static char *get_remote_ip() +{ + const char *cptr = gtk_entry_get_text(GTK_ENTRY(g_tbx_rem_ip)); + return (char *) cptr; +} + +/** + * Return remote port setting + *****************************************************************************/ + +static char *get_remote_port() +{ + const char *cptr = gtk_entry_get_text(GTK_ENTRY(g_tbx_rem_port)); + return (char *) cptr; +} + +/** + * Update local port stat counter + *****************************************************************************/ + +static void show_loc_port_stats(int not_main_thread, int count) +{ + char buf[128]; + + sprintf(buf, "%d", count); + + if (not_main_thread) + gdk_threads_enter(); + + gtk_entry_set_text(GTK_ENTRY(g_tbx_loc_stats), buf); + + if (not_main_thread) + gdk_threads_leave(); +} + +/** + * Update remote port stat counter + *****************************************************************************/ + +static void show_rem_port_stats(int not_main_thread, int count) +{ + char buf[128]; + + sprintf(buf, "%d", count); + + if (not_main_thread) + gdk_threads_enter(); + + gtk_entry_set_text(GTK_ENTRY(g_tbx_rem_stats), buf); + + if (not_main_thread) + gdk_threads_leave(); +} + +/** + * User clicked on window close button + *****************************************************************************/ + +static gboolean on_delete_event(GtkWidget *widget, GdkEvent *ev, gpointer data) +{ + return FALSE; +} + +/** + * Close application + *****************************************************************************/ + +static void on_destroy(GtkWidget *widget, gpointer data) +{ + /* this will destory all windows and return control to gtk_main() */ + gtk_main_quit(); +} + +/** + * Start a thread that listens for incoming connections + *****************************************************************************/ + +static void on_start_clicked(GtkWidget *widget, gpointer data) +{ + /* local port must be specified */ + if (gtk_entry_get_text_length(GTK_ENTRY(g_tbx_loc_port)) == 0) + { + show_msg(MAIN_THREAD_YES, MSG_ERROR, "Invalid entry", + "\nYou must enter a value for Local Port"); + return; + } + + /* remote IP must be specified */ + if (gtk_entry_get_text_length(GTK_ENTRY(g_tbx_rem_ip)) == 0) + { + show_msg(MAIN_THREAD_YES, MSG_ERROR, "Invalid entry", + "\nYou must enter a value for Remote IP"); + return; + } + + /* remote port must be specified */ + if (gtk_entry_get_text_length(GTK_ENTRY(g_tbx_rem_port)) == 0) + { + show_msg(MAIN_THREAD_YES, MSG_ERROR, "Invalid entry", + "\nYou must enter a value for Remote Port"); + return; + } + + if (pthread_create(&g_tid, NULL, tcp_proxy, NULL)) + { + show_msg(MAIN_THREAD_YES, MSG_ERROR, "Starting listener", + "\nThread create error. System out of resources"); + return; + } + + disable_btn_start(MAIN_THREAD_YES); +} + +/** + * Clear stat counters + *****************************************************************************/ + +static void on_clear_clicked(GtkWidget *widget, gpointer data) +{ + g_loc_io_count = 0; + g_rem_io_count = 0; + show_loc_port_stats(MAIN_THREAD_YES, g_loc_io_count); + show_rem_port_stats(MAIN_THREAD_YES, g_rem_io_count); +} + +/** + * Quit application + *****************************************************************************/ + +static void on_quit_clicked(GtkWidget *widget, gpointer data) +{ + /* this will destory all windows and return control to gtk_main() */ + gtk_main_quit(); +} diff --git a/tests/gtcp_proxy/gtcp.c b/tests/gtcp_proxy/gtcp.c new file mode 100644 index 00000000..9f0fcf88 --- /dev/null +++ b/tests/gtcp_proxy/gtcp.c @@ -0,0 +1,394 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2013 + * + * 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 "gtcp.h" + +/** + * Return a newly created socket or -1 on error + *****************************************************************************/ + +int tcp_socket_create(void) +{ + int rv; + int option_value; + +#if defined(_WIN32) + int option_len; +#else + unsigned int option_len; +#endif + + /* in win32 a socket is an unsigned int, in linux, its an int */ + if ((rv = (int) socket(PF_INET, SOCK_STREAM, 0)) < 0) + return -1; + + option_len = sizeof(option_value); + + if (getsockopt(rv, SOL_SOCKET, SO_REUSEADDR, (char *) &option_value, + &option_len) == 0) + { + if (option_value == 0) + { + option_value = 1; + option_len = sizeof(option_value); + setsockopt(rv, SOL_SOCKET, SO_REUSEADDR, (char *) &option_value, + option_len); + } + } + + option_len = sizeof(option_value); + + if (getsockopt(rv, SOL_SOCKET, SO_SNDBUF, (char *) &option_value, + &option_len) == 0) + { + if (option_value < (1024 * 32)) + { + option_value = 1024 * 32; + option_len = sizeof(option_value); + setsockopt(rv, SOL_SOCKET, SO_SNDBUF, (char *) &option_value, + option_len); + } + } + + return rv; +} + +/** + * Place specifed socket in non blocking mode + *****************************************************************************/ + +void tcp_set_non_blocking(int skt) +{ + unsigned long i; + +#if defined(_WIN32) + i = 1; + ioctlsocket(skt, FIONBIO, &i); +#else + i = fcntl(skt, F_GETFL); + i = i | O_NONBLOCK; + fcntl(skt, F_SETFL, i); +#endif +} + +/** + * Assign name to socket + * + * @param skt the socket to bind + * @param port the port to bind to + * + * @return 0 on success, -1 on error + *****************************************************************************/ + +int tcp_bind(int skt, char *port) +{ + struct sockaddr_in s; + + memset(&s, 0, sizeof(struct sockaddr_in)); + s.sin_family = AF_INET; + s.sin_port = htons((uint16_t) atoi(port)); + s.sin_addr.s_addr = INADDR_ANY; + + return bind(skt, (struct sockaddr *) &s, sizeof(struct sockaddr_in)); +} + +/** + * Listen for incoming connections + * + * @param skt the socket to listen on + * + * @return 0 on success, -1 on error + *****************************************************************************/ + +int tcp_listen(int skt) +{ + return listen(skt, 2); +} + +/** + * Accept incoming connection + * + * @param skt socket to accept incoming connection on + * + * @return 0 on success, -1 on error + *****************************************************************************/ + +int tcp_accept(int skt) +{ + int ret ; + char ipAddr[256] ; + struct sockaddr_in s; + +#if defined(_WIN32) + int i; +#else + unsigned int i; +#endif + + i = sizeof(struct sockaddr_in); + memset(&s, 0, i); + return accept(skt, (struct sockaddr *)&s, &i); +} + +/** + * Check if the socket would block + * + * @return TRUE if would block, else FALSE + *****************************************************************************/ + +int tcp_last_error_would_block() +{ +#if defined(_WIN32) + return WSAGetLastError() == WSAEWOULDBLOCK; +#else + return (errno == EWOULDBLOCK) || (errno == EAGAIN) || (errno == EINPROGRESS); +#endif +} + +/** + * Close specified socket + *****************************************************************************/ + +void tcp_close(int skt) +{ + if (skt <= 0) + return; + +#if defined(_WIN32) + closesocket(skt); +#else + close(skt); +#endif +} + +/** + * Create a new socket + * + * @return new socket or -1 on error + *****************************************************************************/ + +int tcp_socket(void) +{ + int rv; + int option_value; + +#if defined(_WIN32) + int option_len; +#else + unsigned int option_len; +#endif + + /* in win32 a socket is an unsigned int, in linux, its an int */ + if ((rv = (int) socket(PF_INET, SOCK_STREAM, 0)) < 0) + return -1; + + option_len = sizeof(option_value); + + if (getsockopt(rv, SOL_SOCKET, SO_REUSEADDR, (char *) &option_value, + &option_len) == 0) + { + if (option_value == 0) + { + option_value = 1; + option_len = sizeof(option_value); + setsockopt(rv, SOL_SOCKET, SO_REUSEADDR, (char *) &option_value, + option_len); + } + } + + option_len = sizeof(option_value); + + if (getsockopt(rv, SOL_SOCKET, SO_SNDBUF, (char *) &option_value, + &option_len) == 0) + { + if (option_value < (1024 * 32)) + { + option_value = 1024 * 32; + option_len = sizeof(option_value); + setsockopt(rv, SOL_SOCKET, SO_SNDBUF, (char *) &option_value, + option_len); + } + } + + return rv; +} + +/** + * Connect to a server + * + * @param skt opaque socket obj + * @param address connect to this server + * @param port using this port + * + * @return 0 on success, -1 on error + *****************************************************************************/ + +int tcp_connect(int skt, const char *hostname, const char *port) +{ + struct sockaddr_in s; + struct hostent *h; + + memset(&s, 0, sizeof(struct sockaddr_in)); + s.sin_family = AF_INET; + s.sin_port = htons((uint16_t) atoi(port)); + s.sin_addr.s_addr = inet_addr(hostname); + + if (s.sin_addr.s_addr == INADDR_NONE) + { + h = gethostbyname(hostname); + + if (h != 0) + { + if (h->h_name != 0) + { + if (h->h_addr_list != 0) + { + if ((*(h->h_addr_list)) != 0) + { + s.sin_addr.s_addr = *((int *)(*(h->h_addr_list))); + } + } + } + } + } + + return connect(skt, (struct sockaddr *) &s, sizeof(struct sockaddr_in)); +} + +/** + * Return 1 if we can write to the socket, 0 otherwise + *****************************************************************************/ + +int tcp_can_send(int skt, int millis) +{ + fd_set wfds; + struct timeval time; + int rv; + + time.tv_sec = millis / 1000; + time.tv_usec = (millis * 1000) % 1000000; + FD_ZERO(&wfds); + + if (skt > 0) + { + FD_SET(((unsigned int) skt), &wfds); + rv = select(skt + 1, 0, &wfds, 0, &time); + + if (rv > 0) + { + return tcp_socket_ok(skt); + } + } + + return 0; +} + +/** + * Return 1 if socket is OK, 0 otherwise + *****************************************************************************/ + +int tcp_socket_ok(int skt) +{ + int opt; + +#if defined(_WIN32) + int opt_len; +#else + unsigned int opt_len; +#endif + + opt_len = sizeof(opt); + + if (getsockopt(skt, SOL_SOCKET, SO_ERROR, (char *) (&opt), &opt_len) == 0) + { + if (opt == 0) + return 1; + } + + return 0; +} + +/** + * Check if specified sockets can be operated on without blocking + * + * @return 1 if they can be operated on or 0 if blocking would occur + *****************************************************************************/ + +int tcp_select(int sck1, int sck2) +{ + fd_set rfds; + struct timeval time; + + int max = 0; + int rv = 0; + + memset(&rfds, 0, sizeof(fd_set)); + memset(&time, 0, sizeof(struct timeval)); + + time.tv_sec = 0; + time.tv_usec = 0; + FD_ZERO(&rfds); + + if (sck1 > 0) + FD_SET(((unsigned int) sck1), &rfds); + + if (sck2 > 0) + FD_SET(((unsigned int) sck2), &rfds); + + max = sck1; + + if (sck2 > max) + max = sck2; + + rv = select(max + 1, &rfds, 0, 0, &time); + + if (rv > 0) + { + rv = 0; + + if (FD_ISSET(((unsigned int) sck1), &rfds)) + rv = rv | 1; + + if (FD_ISSET(((unsigned int)sck2), &rfds)) + rv = rv | 2; + } + else + { + rv = 0; + } + + return rv; +} + +int tcp_recv(int skt, void *ptr, int len, int flags) +{ +#if defined(_WIN32) + return recv(skt, (char *) ptr, len, flags); +#else + return recv(skt, ptr, len, flags); +#endif +} + +int tcp_send(int skt, const void *ptr, int len, int flags) +{ +#if defined(_WIN32) + return send(skt, (const char *)ptr, len, flags); +#else + return send(skt, ptr, len, flags); +#endif +} diff --git a/tests/gtcp_proxy/gtcp.h b/tests/gtcp_proxy/gtcp.h new file mode 100644 index 00000000..970540ef --- /dev/null +++ b/tests/gtcp_proxy/gtcp.h @@ -0,0 +1,48 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2013 + * + * 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 _GTCP_H +#define _GTCP_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int tcp_socket_create(void); +void tcp_set_non_blocking(int skt); +int tcp_bind(int skt, char *port); +int tcp_listen(int skt); +int tcp_accept(int skt); +int tcp_last_error_would_block(); +void tcp_close(int skt); +int tcp_socket(void); +int tcp_connect(int skt, const char *hostname, const char *port); +int tcp_can_send(int skt, int millis); +int tcp_socket_ok(int skt); +int tcp_select(int sck1, int sck2); +int tcp_recv(int skt, void *ptr, int len, int flags); +int tcp_send(int skt, const void *ptr, int len, int flags); + +#endif