X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=tunnel.c;fp=tunnel.c;h=a03ef0b32757085673185bdec436ac88f9a48188;hb=c333f350d703997b67db67b8a578ea4bc70089fa;hp=0000000000000000000000000000000000000000;hpb=efd8c4e6a2aaab12972ebe9d0c70f1d4d7a41435;p=ovirt-viewer.git diff --git a/tunnel.c b/tunnel.c new file mode 100644 index 0000000..a03ef0b --- /dev/null +++ b/tunnel.c @@ -0,0 +1,319 @@ +/* ovirt viewer console application + * Copyright (C) 2008 Red Hat Inc. + * Written by Mohammed Morsi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* ovirt-viewer starts listening on network port to + * encapsulate vnc packets including the vm's name + * so as to be able to be proxied. + * + * This operation takes place in another thread + * which can be started/stopped by calling + * start_tunnel / stop_tunnel. + * + * An additional connection thread is created and maintained + * internally for each vm / vnc connection open in ovirt-viewer + * establishing a connection w/ the ovirt server. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "internal.h" + +/* constants */ + +// port to try to listen on, if we can't, increment until we find one we can +const int PORT_RANGE_START = 5600; + +// max length of a vm name +const int VM_NAME_MAX_LEN = 250; + +// max length of vnc data +const int VNC_DATA_MAX_LEN = 800000; + +// port which to connect to on ovirt server +const int OVIRT_SERVER_PORT = 5900; + +/* Private thread functions */ +static gpointer tunnel_thread(gpointer data); +static gpointer client_server_thread(gpointer data); +static gpointer server_client_thread(gpointer data); + +/* Other private functions */ +static void close_socket(gpointer _socket, gpointer data); +static void wait_for_thread(gpointer _thread, gpointer data); + +/* tunnel and main threads */ +static GThread *tunnel_gthread = NULL; +static GThread *main_gthread = NULL; + +/* list of communication threads */ +static GSList *communication_threads = NULL; + +/* list of sockets */ +static GSList *sockets = NULL; + +/* thread termination flag */ +static gboolean run_tunnel = FALSE; + +/* internal.h shared constructs */ +int tunnel_port; + +///////////////// + +/** public implementations **/ + +/* start tunnel thread */ +void +start_tunnel(void) +{ + GError *error = NULL; + + DEBUG ("starting the tunnel thread"); + + assert (tunnel_gthread == NULL); + + run_tunnel = TRUE; + + main_gthread = g_thread_self (); + + tunnel_gthread = g_thread_create (tunnel_thread, NULL, TRUE, &error); + if (error) { + g_print ("%s\n", error->message); + g_error_free (error); + exit (1); + } +}; + +/* stop tunnel thread */ +void +stop_tunnel(void) +{ + if(!run_tunnel) + return; + + DEBUG ("stopping the tunnel thread"); + + assert (tunnel_gthread != NULL); + ASSERT_IS_MAIN_THREAD (); + + run_tunnel = FALSE; + + g_slist_foreach(sockets, close_socket, NULL); + + (void) g_thread_join (tunnel_gthread); + tunnel_gthread = NULL; +}; + +///////////////// + +/** private implementations **/ + +/* the tunnel thread */ +static gpointer +tunnel_thread (gpointer _data) +{ + //char vm_data[VM_NAME_MAX_LEN]; + int local_server_socketfd, ovirt_server_socket, client_socketfd; + unsigned int local_server_len, client_len, ovirt_server_len; + + struct sockaddr_in local_server_address; + struct sockaddr_in ovirt_server_address; + struct sockaddr_in client_address; + + GThread *client_server_gthread = NULL; + GThread *server_client_gthread = NULL; + + int sockets_param[2]; + int * c_socket; + + DEBUG ("tunnel thread starting up"); + + // ovirt server address + ovirt_server_address.sin_family = PF_INET; + ovirt_server_address.sin_addr.s_addr = inet_addr(hostname); + ovirt_server_address.sin_port = htons(OVIRT_SERVER_PORT); + ovirt_server_len = sizeof(ovirt_server_address); + + // create local net socket + local_server_socketfd = socket(PF_INET, SOCK_STREAM, 0); + c_socket = malloc(sizeof(int)); *c_socket = local_server_socketfd; + sockets = g_slist_prepend(sockets, c_socket); + + // local server address + tunnel_port = PORT_RANGE_START; + local_server_address.sin_family = PF_INET; + local_server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); + local_server_address.sin_port = htons(tunnel_port); + local_server_len = sizeof(local_server_address); + + // increment ports until one is available + while(bind(local_server_socketfd, (struct sockaddr*)&local_server_address, local_server_len) < 0){ + tunnel_port += 1; + local_server_address.sin_port += htons(tunnel_port); + } + + DEBUG ("tunnel bound to local port %i", tunnel_port); + + // increase client buffer size? + listen(local_server_socketfd, 5); + + while(run_tunnel) { + // accept a client connection + DEBUG("tunnel accepting"); + client_len = sizeof(client_address); + client_socketfd = accept(local_server_socketfd, (struct sockaddr*)&client_address, &client_len); + if(client_socketfd < 0){ + DEBUG("tunnel accept failed"); + break; + } + // TODO check accept return value for err + c_socket = malloc(sizeof(int)); *c_socket = client_socketfd; + sockets = g_slist_prepend(sockets, c_socket); + + DEBUG ("client connected to tunnel"); + + // establish connection w/ ovirt server + ovirt_server_socket = socket(PF_INET, SOCK_STREAM, 0); + c_socket = malloc(sizeof(int)); *c_socket = ovirt_server_socket; + sockets = g_slist_prepend(sockets, c_socket); + DEBUG ("connecting to ovirt server %s on %i", hostname, OVIRT_SERVER_PORT); + if(connect(ovirt_server_socket, (struct sockaddr*)&ovirt_server_address, ovirt_server_len) < 0){ + DEBUG ("could not connect to ovirt server"); + break; + //return NULL; + } + DEBUG ("connected to ovirt server"); + + sockets_param[0] = ovirt_server_socket; + sockets_param[1] = client_socketfd; + + // launch thread for client -> server traffic + client_server_gthread = g_thread_create (client_server_thread, + &sockets_param, TRUE, NULL); + + // launch thread for server -> client traffic + server_client_gthread = g_thread_create (server_client_thread, + &sockets_param, TRUE, NULL); + + communication_threads = g_slist_prepend(communication_threads, client_server_gthread); + communication_threads = g_slist_prepend(communication_threads, server_client_gthread); + + // send target vm for this session + //strcpy(vm_data, vm_in_focus->description); + DEBUG ("sending vm %s", vm_in_focus->description); + write(ovirt_server_socket, vm_in_focus->description, strlen(vm_in_focus->description)); + + } + + DEBUG("terminating tunnel thread"); + + // wait for connection threads to finish + g_slist_foreach(communication_threads, wait_for_thread, NULL); + + DEBUG ("tunnel thread completed"); + return NULL; +}; + +/* the tunnel thread */ +static gpointer +client_server_thread (gpointer _data){ + int nbytes; + char vnc_data[VNC_DATA_MAX_LEN]; + + int ovirt_server_socket = ((int*)_data)[0], + client_socket = ((int*)_data)[1]; + + DEBUG ("client/server thread starting up"); + + while(run_tunnel){ + VERBOSE( "accepting client data"); + + // grab vnc data + nbytes = read(client_socket, vnc_data, VNC_DATA_MAX_LEN); + if(nbytes <= 0){ + DEBUG ( "error reading data from client" ); + break; + } + VERBOSE ("read %i bytes from client", nbytes); + + // send network_data onto server + nbytes = write(ovirt_server_socket, vnc_data, nbytes); + if(nbytes <= 0){ + DEBUG ( "error writing data to server" ); + break; + } + VERBOSE ("wrote %i bytes to server", nbytes); + } + + DEBUG ("client/server thread completed"); + return NULL; +}; + +/* the server thread */ +static gpointer +server_client_thread (gpointer _data){ + char vnc_data[VNC_DATA_MAX_LEN]; + + int ovirt_server_socket = ((int*)_data)[0], + client_socket = ((int*)_data)[1]; + + int nbytes; + + DEBUG ("server/client thread starting up"); + + while(run_tunnel){ + // grab vnc data + nbytes = read(ovirt_server_socket, vnc_data, VNC_DATA_MAX_LEN); + if(nbytes <= 0){ + DEBUG ( "error reading data from server" ); + break; + } + VERBOSE ("read %i bytes from server", nbytes); + + // send network_data onto client + nbytes = write(client_socket, vnc_data, nbytes); + if(nbytes <= 0){ + DEBUG ( "error writing data to client" ); + break; + } + VERBOSE ("wrote %i bytes to client", nbytes); + } + + DEBUG ("server/client thread completed"); + return NULL; +}; + +static void close_socket(gpointer _socket, gpointer data){ + shutdown(*(int*) _socket, 2); + close(*(int*) _socket); + free((int*) _socket); +}; + +static void wait_for_thread(gpointer _thread, gpointer data){ + g_thread_join((GThread*)_thread); +};