1 /*
2  * vinagre-spice-tunnel.c
3  * SSH Tunneling for Vinagre
4  * This file is part of vinagre
5  *
6  * Copyright (C) 2009 - Jonh Wendell <wendell@bani.com.br>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 #include <vinagre/vinagre-ssh.h>
24 
25 #ifdef G_OS_WIN32
26 #undef DATADIR
27 #include <winsock2.h>
28 #include <ws2tcpip.h>
29 #else /* !G_OS_WIN32 */
30 #include <netinet/in.h>
31 #include <sys/socket.h>
32 #endif /* G_OS_WIN32 */
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <glib/gi18n.h>
36 
37 #include "vinagre-spice-tunnel.h"
38 
39 static const int TUNNEL_PORT_OFFSET = 5500;
40 
41 static int
find_free_port(void)42 find_free_port (void)
43 {
44   int sock, port;
45   struct sockaddr_in6 addr;
46 
47   memset (&addr, 0, sizeof (addr));
48   addr.sin6_family = AF_INET6;
49   addr.sin6_addr = in6addr_any;
50 
51   sock = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
52   if (sock < 0)
53     return 0;
54 
55   for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--)
56     {
57       addr.sin6_port = htons (port);
58       if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) == 0)
59 	{
60 	  close (sock);
61 	  return port;
62 	}
63     }
64 
65   close (sock);
66   return 0;
67 }
68 
69 static void
split_gateway(const gchar * gateway,gchar ** host,gint * port)70 split_gateway (const gchar *gateway, gchar **host, gint *port)
71 {
72   if (g_strrstr (gateway, ":") == NULL)
73     {
74       *host = g_strdup (gateway);
75       *port = 22;
76     }
77   else
78     {
79       gchar **server = g_strsplit (gateway, ":", 2);
80       *host = g_strdup (server[0]);
81       *port = server[1] ? atoi (server[1]) : 22;
82       g_strfreev (server);
83     }
84 }
85 
86 
87 gboolean
vinagre_spice_tunnel_create(GtkWindow * parent,gchar ** original_host,gchar ** original_port,gchar * gateway,GError ** error)88 vinagre_spice_tunnel_create (GtkWindow *parent,
89 			   gchar **original_host,
90 			   gchar **original_port,
91 			   gchar *gateway,
92 			   GError **error)
93 {
94   int local_port, gateway_port;
95   gchar **tunnel_str, **command_str, *gateway_host;
96 
97   local_port = find_free_port ();
98   if (local_port == 0)
99     {
100       g_set_error (error,
101 		   VINAGRE_SPICE_TUNNEL_ERROR,
102 		   VINAGRE_SPICE_TUNNEL_ERROR_NO_FREE_PORT,
103 		   _("Unable to find a free TCP port"));
104       return FALSE;
105     }
106 
107   tunnel_str = g_new (gchar *, 4);
108   tunnel_str[0] = g_strdup ("-f");
109   tunnel_str[1] = g_strdup ("-L");
110   tunnel_str[2] = g_strdup_printf ("%d:%s:%s",
111 				   local_port,
112 				   *original_host,
113 				   *original_port);
114   tunnel_str[3] = NULL;
115 
116   command_str = g_new (gchar *, 5);
117   command_str[0] = g_strdup ("echo");
118   command_str[1] = g_strdup_printf ("%s;", VINAGRE_SSH_CHECK);
119   command_str[2] = g_strdup ("sleep");
120   command_str[3] = g_strdup ("15");
121   command_str[4] = NULL;
122 
123   split_gateway (gateway, &gateway_host, &gateway_port);
124 
125   if (!vinagre_ssh_connect (parent,
126 			    gateway_host,
127 			    gateway_port,
128 			    NULL,
129 			    tunnel_str,
130 			    command_str,
131 			    NULL,
132 			    error))
133     {
134       g_strfreev (tunnel_str);
135       g_strfreev (command_str);
136       g_free (gateway_host);
137       return FALSE;
138     }
139 
140   g_strfreev (tunnel_str);
141   g_strfreev (command_str);
142   g_free (gateway_host);
143   g_free (*original_host);
144   *original_host = g_strdup ("localhost");
145   g_free (*original_port);
146   *original_port = g_strdup_printf ("%d", local_port);
147 
148   return TRUE;
149 }
150 
151 GQuark
vinagre_spice_tunnel_error_quark(void)152 vinagre_spice_tunnel_error_quark (void)
153 {
154   static GQuark quark = 0;
155 
156   if (!quark)
157     quark = g_quark_from_string ("vinagre_spice_tunnel_error");
158 
159   return quark;
160 }
161 
162 /* vim: set ts=8: */
163