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