/* 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 #include "internal.h" /* constants */ // 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; /* 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) { struct hostent *dns_serv; //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; struct sockaddr_in local_server_address_lookup; unsigned int local_server_address_lookup_len = sizeof(local_server_address_lookup); 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 dns_serv = gethostbyname(hostname); if(dns_serv == NULL){ DEBUG("ovirt server lookup failed"); return NULL; } ovirt_server_address.sin_family = PF_INET; ovirt_server_address.sin_addr.s_addr = ((struct in_addr*)(dns_serv->h_addr))->s_addr; //inet_addr(hostname); ovirt_server_address.sin_port = htons(ovirt_server_vnc_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 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 = 0; local_server_len = sizeof(local_server_address); // increment ports until one is available if(bind(local_server_socketfd, (struct sockaddr*)&local_server_address, local_server_len) < 0){ DEBUG("tunnel bind failed"); return NULL; } getsockname(local_server_socketfd, (struct sockaddr*) &local_server_address_lookup, &local_server_address_lookup_len); tunnel_port = (int)ntohs(local_server_address_lookup.sin_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_vnc_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); };