remmina_ssh.c 41.7 KB
Newer Older
Antenore Gatta's avatar
Antenore Gatta committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Remmina - The GTK+ Remote Desktop Client
 * Copyright (C) 2009-2011 Vic Lee
 * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
 *
 * 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
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
Antenore Gatta's avatar
Antenore Gatta committed
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 * Boston, MA  02110-1301, USA.
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU General Public License in all respects
 *  for all of the code used other than OpenSSL. *  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so. *  If you
 *  do not wish to do so, delete this exception statement from your
 *  version. *  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */
35 36 37 38 39 40 41 42

#include "config.h"

#ifdef HAVE_LIBSSH

/* Define this before stdlib.h to have posix_openpt */
#define _XOPEN_SOURCE 600

Antenore Gatta's avatar
Antenore Gatta committed
43
#define LIBSSH_STATIC 1
44
#include <libssh/libssh.h>
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <pthread.h>
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
74 75 76
#include "remmina_public.h"
#include "remmina_log.h"
#include "remmina_ssh.h"
77
#include "remmina_pref.h"
78
#include "remmina/remmina_trace_calls.h"
79

80 81
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
82 83 84 85 86 87 88

#if defined(__FreeBSD__) || defined(__OpenBSD__)
#ifndef SOL_TCP
#define SOL_TCP	IPPROTO_TCP
#endif
#endif

89 90 91 92 93 94 95 96
#define SSH_SOCKET_TCP_KEEPIDLE 5
#define SSH_SOCKET_TCP_KEEPCNT 3
#define SSH_SOCKET_TCP_KEEPINTVL 3
/* Remember to lower SSH_SOCKET_TCP_USER_TIMEOUT to 4 when kernel bug 108191 will be fixed */
#define SSH_SOCKET_TCP_USER_TIMEOUT 9
#endif


97 98 99
/*-----------------------------------------------------------------------------*
 *                           SSH Base                                          *
 *-----------------------------------------------------------------------------*/
100

101 102 103
#define LOCK_SSH(ssh) pthread_mutex_lock (&REMMINA_SSH (ssh)->ssh_mutex);
#define UNLOCK_SSH(ssh) pthread_mutex_unlock (&REMMINA_SSH (ssh)->ssh_mutex);

104 105
static const gchar *common_identities[] =
{
106 107 108 109
	".ssh/id_rsa",
	".ssh/id_dsa",
	".ssh/identity",
	NULL
110 111
};

112
gchar*
113 114
remmina_ssh_identity_path (const gchar *id)
{
115
	TRACE_CALL("remmina_ssh_identity_path");
116 117
	if (id == NULL) return NULL;
	if (id[0] == '/') return g_strdup (id);
118
	return g_strdup_printf("%s/%s", g_get_home_dir (), id);
119 120
}

121
gchar*
122 123
remmina_ssh_find_identity (void)
{
124
	TRACE_CALL("remmina_ssh_find_identity");
125 126 127
	gchar *path;
	gint i;

128 129
	for (i = 0; common_identities[i]; i++)
	{
130
		path = remmina_ssh_identity_path (common_identities[i]);
131 132
		if (g_file_test (path, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS))
		{
133 134
			return path;
		}
135
		g_free(path);
136 137
	}
	return NULL;
138 139
}

140
void
141 142
remmina_ssh_set_error (RemminaSSH *ssh, const gchar *fmt)
{
143
	TRACE_CALL("remmina_ssh_set_error");
144
	const gchar *err;
145

146
	err = ssh_get_error (ssh->session);
147
	ssh->error = g_strdup_printf(fmt, err);
148 149
}

150
void
151 152
remmina_ssh_set_application_error (RemminaSSH *ssh, const gchar *fmt, ...)
{
153
	TRACE_CALL("remmina_ssh_set_application_error");
154
	va_list args;
155

156 157 158
	va_start (args, fmt);
	ssh->error = g_strdup_vprintf (fmt, args);
	va_end (args);
159 160
}

161
static gint
162 163
remmina_ssh_auth_password (RemminaSSH *ssh)
{
164
	TRACE_CALL("remmina_ssh_auth_password");
165 166 167 168 169
	gint ret;
	gint authlist;
	gint n;
	gint i;

170
	ret = SSH_AUTH_ERROR;
171 172 173 174
	if (ssh->authenticated) return 1;
	if (ssh->password == NULL) return -1;

	authlist = ssh_userauth_list (ssh->session, NULL);
175 176 177 178
	if (authlist & SSH_AUTH_METHOD_INTERACTIVE)
	{
		while ((ret = ssh_userauth_kbdint (ssh->session, NULL, NULL)) == SSH_AUTH_INFO)
		{
179
			n = ssh_userauth_kbdint_getnprompts (ssh->session);
180 181
			for (i = 0; i < n; i++)
			{
182 183 184 185
				ssh_userauth_kbdint_setanswer(ssh->session, i, ssh->password);
			}
		}
	}
186 187
	if (ret != SSH_AUTH_SUCCESS && authlist & SSH_AUTH_METHOD_PASSWORD)
	{
188 189
		ret = ssh_userauth_password (ssh->session, NULL, ssh->password);
	}
190 191
	if (ret != SSH_AUTH_SUCCESS)
	{
192 193 194 195 196 197
		remmina_ssh_set_error (ssh, _("SSH password authentication failed: %s"));
		return 0;
	}

	ssh->authenticated = TRUE;
	return 1;
198 199
}

200
static gint
201 202
remmina_ssh_auth_pubkey (RemminaSSH *ssh)
{
203
	TRACE_CALL("remmina_ssh_auth_pubkey");
204
	gint ret;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
205
	ssh_key priv_key;
206 207 208

	if (ssh->authenticated) return 1;

209 210
	if (ssh->privkeyfile == NULL)
	{
211
		ssh->error = g_strdup_printf(_("SSH public key authentication failed: %s"),
212
		                             _("SSH Key file not yet set."));
213 214 215
		return 0;
	}

Giovanni Panozzo's avatar
Giovanni Panozzo committed
216
	if ( ssh_pki_import_privkey_file( ssh->privkeyfile, (ssh->password ? ssh->password : ""),
217 218
	                                  NULL, NULL, &priv_key ) != SSH_OK )
	{
219 220 221 222 223 224
		if (ssh->password == NULL || ssh->password[0] == '\0') return -1;

		remmina_ssh_set_error (ssh, _("SSH public key authentication failed: %s"));
		return 0;
	}

Giovanni Panozzo's avatar
Giovanni Panozzo committed
225 226
	ret = ssh_userauth_publickey (ssh->session, NULL, priv_key);
	ssh_key_free(priv_key);
227

228 229
	if (ret != SSH_AUTH_SUCCESS)
	{
230 231 232 233 234 235
		remmina_ssh_set_error (ssh, _("SSH public key authentication failed: %s"));
		return 0;
	}

	ssh->authenticated = TRUE;
	return 1;
236 237
}

238
static gint
239 240
remmina_ssh_auth_auto_pubkey (RemminaSSH* ssh)
{
241
	TRACE_CALL("remmina_ssh_auth_auto_pubkey");
242 243
	gint ret;
	ret = ssh_userauth_autopubkey (ssh->session, "");
244

245 246
	if (ret != SSH_AUTH_SUCCESS)
	{
247 248 249
		remmina_ssh_set_error (ssh, _("SSH automatic public key authentication failed: %s"));
		return 0;
	}
250

251 252
	ssh->authenticated = TRUE;
	return 1;
253 254
}

255
static gint
256 257 258 259 260 261
remmina_ssh_auth_agent (RemminaSSH* ssh)
{
	TRACE_CALL("remmina_ssh_auth_agent");
	gint ret;
	ret = ssh_userauth_agent (ssh->session, NULL);

262 263
	if (ret != SSH_AUTH_SUCCESS)
	{
264 265 266 267 268 269 270 271
		remmina_ssh_set_error (ssh, _("SSH public key authentication with ssh agent failed: %s"));
		return 0;
	}

	ssh->authenticated = TRUE;
	return 1;
}

272
gint
273 274
remmina_ssh_auth (RemminaSSH *ssh, const gchar *password)
{
275
	TRACE_CALL("remmina_ssh_auth");
276
	/* Check known host again to ensure it's still the original server when user forks
Antenore Gatta's avatar
Antenore Gatta committed
277
	   a new session from existing one */
278 279
	if (ssh_is_server_known (ssh->session) != SSH_SERVER_KNOWN_OK)
	{
280 281 282 283
		remmina_ssh_set_application_error (ssh, "SSH public key has changed!");
		return 0;
	}

284 285
	if (password)
	{
286
		g_free(ssh->password);
287 288 289
		ssh->password = g_strdup (password);
	}

290 291
	switch (ssh->auth)
	{
292

293 294
	case SSH_AUTH_PASSWORD:
		return remmina_ssh_auth_password (ssh);
295

296 297
	case SSH_AUTH_PUBLICKEY:
		return remmina_ssh_auth_pubkey (ssh);
298

299 300
	case SSH_AUTH_AGENT:
		return remmina_ssh_auth_agent (ssh);
301

302 303
	case SSH_AUTH_AUTO_PUBLICKEY:
		return remmina_ssh_auth_auto_pubkey (ssh);
304

305 306
	default:
		return 0;
307
	}
308 309
}

310
gint
311
remmina_ssh_auth_gui (RemminaSSH *ssh, RemminaInitDialog *dialog)
312
{
313
	TRACE_CALL("remmina_ssh_auth_gui");
314 315 316
	gchar *tips;
	gchar *keyname;
	gint ret;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
317
	size_t len;
318
	guchar *pubkey;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
319
	ssh_key server_pubkey;
320 321 322

	/* Check if the server's public key is known */
	ret = ssh_is_server_known (ssh->session);
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
	switch (ret)
	{
	case SSH_SERVER_KNOWN_OK:
		break;                          /* ok */

	case SSH_SERVER_FILE_NOT_FOUND:
	/*  fallback to SSH_SERVER_NOT_KNOWN behavior */
	case SSH_SERVER_NOT_KNOWN:
	case SSH_SERVER_KNOWN_CHANGED:
	case SSH_SERVER_FOUND_OTHER:
		if ( ssh_get_publickey(ssh->session, &server_pubkey) != SSH_OK )
		{
			remmina_ssh_set_error(ssh, "ssh_get_publickey() has failed: %s");
			return 0;
		}
		if ( ssh_get_publickey_hash(server_pubkey, SSH_PUBLICKEY_HASH_MD5, &pubkey, &len) != 0 )
		{
Giovanni Panozzo's avatar
Giovanni Panozzo committed
340
			ssh_key_free(server_pubkey);
341 342 343 344 345
			remmina_ssh_set_error(ssh, "ssh_get_publickey_hash() has failed: %s");
			return 0;
		}
		ssh_key_free(server_pubkey);
		keyname = ssh_get_hexa (pubkey, len);
346

347

348 349 350 351 352 353 354 355
		if (ret == SSH_SERVER_NOT_KNOWN || ret == SSH_SERVER_FILE_NOT_FOUND)
		{
			ret = remmina_init_dialog_serverkey_unknown (dialog, keyname);
		}
		else
		{
			ret = remmina_init_dialog_serverkey_changed (dialog, keyname);
		}
356

357 358 359 360 361 362 363 364 365
		ssh_string_free_char(keyname);
		ssh_clean_pubkey_hash (&pubkey);
		if (ret != GTK_RESPONSE_OK) return -1;
		ssh_write_knownhost (ssh->session);
		break;
	case SSH_SERVER_ERROR:
	default:
		remmina_ssh_set_error (ssh, "SSH known host checking failed: %s");
		return 0;
366 367 368 369 370 371 372
	}

	/* Try empty password or existing password first */
	ret = remmina_ssh_auth (ssh, NULL);
	if (ret > 0) return 1;

	/* Requested for a non-empty password */
373 374
	if (ret < 0)
	{
375 376
		if (!dialog) return -1;

377 378 379 380 381 382 383 384 385 386 387 388
		switch (ssh->auth)
		{
		case SSH_AUTH_PASSWORD:
			tips = _("Authenticating %s's password to SSH server %s...");
			keyname = _("SSH password");
			break;
		case SSH_AUTH_PUBLICKEY:
			tips = _("Authenticating %s's identity to SSH server %s...");
			keyname = _("SSH private key passphrase");
			break;
		default:
			return FALSE;
389 390
		}

391 392
		if (ssh->auth != SSH_AUTH_AUTO_PUBLICKEY)
		{
393 394 395 396 397 398 399 400
			remmina_init_dialog_set_status (dialog, tips, ssh->user, ssh->server);
			ret = remmina_init_dialog_authpwd (dialog, keyname, FALSE);

			if (ret != GTK_RESPONSE_OK) return -1;
		}
		ret = remmina_ssh_auth (ssh, dialog->password);
	}

401 402
	if (ret <= 0)
	{
403 404 405 406
		return 0;
	}

	return 1;
407 408
}

409
void
410
remmina_ssh_log_callback(ssh_session session, int priority, const char *message, void *userdata)
411
{
412
	TRACE_CALL("remmina_ssh_log_callback");
413
	remmina_log_printf ("[SSH] %s\n", message);
414 415
}

416
gboolean
417 418
remmina_ssh_init_session (RemminaSSH *ssh)
{
419
	TRACE_CALL("remmina_ssh_init_session");
420
	gint verbosity;
421 422 423 424
#ifdef HAVE_NETINET_TCP_H
	socket_t sshsock;
	gint optval;
#endif
425 426 427 428 429 430 431 432

	ssh->callback = g_new0 (struct ssh_callbacks_struct, 1);

	/* Init & startup the SSH session */
	ssh->session = ssh_new ();
	ssh_options_set (ssh->session, SSH_OPTIONS_HOST, ssh->server);
	ssh_options_set (ssh->session, SSH_OPTIONS_PORT, &ssh->port);
	ssh_options_set (ssh->session, SSH_OPTIONS_USER, ssh->user);
433 434

	ssh_callbacks_init(ssh->callback);
435 436
	if (remmina_log_running ())
	{
437
		verbosity = remmina_pref.ssh_loglevel;
438 439
		ssh_options_set (ssh->session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
		ssh->callback->log_function = remmina_ssh_log_callback;
440 441
		/* Reset libssh legacy userdata. This is a workaround for a libssh bug */
		ssh_set_log_userdata(ssh->session);
442
	}
443
	ssh->callback->userdata = ssh;
444 445
	ssh_set_callbacks(ssh->session, ssh->callback);

446
	/* As the latest parse the ~/.ssh/config file */
447 448 449 450
	if (remmina_pref.ssh_parseconfig) {
		ssh_options_parse_config(ssh->session, NULL);
	}

451 452
	if (ssh_connect (ssh->session))
	{
453 454 455 456
		remmina_ssh_set_error (ssh, _("Failed to startup SSH session: %s"));
		return FALSE;
	}

457 458 459 460 461 462 463 464 465
#ifdef HAVE_NETINET_TCP_H
	/* Set keepalive on ssh socket, so we can keep firewalls awaken and detect
	 * when we loss the tunnel */
	sshsock = ssh_get_fd(ssh->session);
	if (sshsock >= 0) {
		optval = 1;
		if(setsockopt (sshsock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof (optval)) < 0){
			remmina_log_printf ("[SSH] TCP KeepAlive not set\n");
		}
466
#ifdef TCP_KEEPIDLE
467 468 469 470
		optval = SSH_SOCKET_TCP_KEEPIDLE;
		if (setsockopt(sshsock, IPPROTO_TCP, TCP_KEEPIDLE,  &optval, sizeof (optval)) < 0) {
			remmina_log_printf ("[SSH] TCP_KEEPIDLE not set\n");
		}
471 472
#endif
#ifdef TCP_KEEPCNT
473 474 475 476
		optval = SSH_SOCKET_TCP_KEEPCNT;
		if (setsockopt(sshsock, IPPROTO_TCP, TCP_KEEPCNT,  &optval, sizeof (optval)) < 0) {
			remmina_log_printf ("[SSH] TCP_KEEPCNT not set\n");
		}
477 478
#endif
#ifdef TCP_KEEPINTVL
479 480 481 482
		optval = SSH_SOCKET_TCP_KEEPINTVL;
		if (setsockopt(sshsock, IPPROTO_TCP, TCP_KEEPINTVL,  &optval, sizeof (optval)) < 0) {
			remmina_log_printf ("[SSH] TCP_KEEPINTVL not set\n");
		}
483 484
#endif
#ifdef TCP_USER_TIMEOUT
485 486 487 488
		optval = SSH_SOCKET_TCP_USER_TIMEOUT;
		if (setsockopt(sshsock, IPPROTO_TCP, TCP_USER_TIMEOUT,  &optval, sizeof (optval)) < 0) {
			remmina_log_printf ("[SSH] TCP_USER_TIMEOUT not set\n");
		}
489
#endif
490 491 492
	}
#endif

493
	/* Try the "none" authentication */
494 495
	if (ssh_userauth_none (ssh->session, NULL) == SSH_AUTH_SUCCESS)
	{
496 497 498
		ssh->authenticated = TRUE;
	}
	return TRUE;
499 500
}

501
gboolean
502 503
remmina_ssh_init_from_file (RemminaSSH *ssh, RemminaFile *remminafile)
{
504
	TRACE_CALL("remmina_ssh_init_from_file");
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
	const gchar *ssh_server;
	const gchar *ssh_username;
	const gchar *ssh_privatekey;
	const gchar *server;
	gchar *s;

	ssh->session = NULL;
	ssh->callback = NULL;
	ssh->authenticated = FALSE;
	ssh->error = NULL;
	pthread_mutex_init (&ssh->ssh_mutex, NULL);

	/* Parse the address and port */
	ssh_server = remmina_file_get_string (remminafile, "ssh_server");
	ssh_username = remmina_file_get_string (remminafile, "ssh_username");
	ssh_privatekey = remmina_file_get_string (remminafile, "ssh_privatekey");
	server = remmina_file_get_string (remminafile, "server");
522 523
	if (ssh_server)
	{
524
		remmina_public_get_server_port (ssh_server, 22, &ssh->server, &ssh->port);
525 526
		if (ssh->server[0] == '\0')
		{
527
			g_free(ssh->server);
528 529
			remmina_public_get_server_port (server, 0, &ssh->server, NULL);
		}
530 531 532
	}
	else if (server == NULL)
	{
533 534
		ssh->server = g_strdup ("localhost");
		ssh->port = 22;
535 536 537
	}
	else
	{
538 539 540 541 542 543 544 545 546 547 548
		remmina_public_get_server_port (server, 0, &ssh->server, NULL);
		ssh->port = 22;
	}

	ssh->user = g_strdup (ssh_username ? ssh_username : g_get_user_name ());
	ssh->password = NULL;
	ssh->auth = remmina_file_get_int (remminafile, "ssh_auth", 0);
	ssh->charset = g_strdup (remmina_file_get_string (remminafile, "ssh_charset"));

	/* Public/Private keys */
	s = (ssh_privatekey ? g_strdup (ssh_privatekey) : remmina_ssh_find_identity ());
549 550
	if (s)
	{
551
		ssh->privkeyfile = remmina_ssh_identity_path (s);
552
		g_free(s);
553 554 555
	}
	else
	{
556 557 558 559
		ssh->privkeyfile = NULL;
	}

	return TRUE;
560 561
}

562
static gboolean
563 564
remmina_ssh_init_from_ssh (RemminaSSH *ssh, const RemminaSSH *ssh_src)
{
565
	TRACE_CALL("remmina_ssh_init_from_ssh");
566 567 568 569 570 571 572 573 574 575 576 577 578 579
	ssh->session = NULL;
	ssh->authenticated = FALSE;
	ssh->error = NULL;
	pthread_mutex_init (&ssh->ssh_mutex, NULL);

	ssh->server = g_strdup (ssh_src->server);
	ssh->port = ssh_src->port;
	ssh->user = g_strdup (ssh_src->user);
	ssh->auth = ssh_src->auth;
	ssh->password = g_strdup (ssh_src->password);
	ssh->privkeyfile = g_strdup (ssh_src->privkeyfile);
	ssh->charset = g_strdup (ssh_src->charset);

	return TRUE;
580 581
}

582
gchar*
583 584
remmina_ssh_convert (RemminaSSH *ssh, const gchar *from)
{
585
	TRACE_CALL("remmina_ssh_convert");
586 587
	gchar *to = NULL;

588 589
	if (ssh->charset && from)
	{
590 591 592 593
		to = g_convert (from, -1, "UTF-8", ssh->charset, NULL, NULL, NULL);
	}
	if (!to) to = g_strdup (from);
	return to;
594 595
}

596
gchar*
597 598
remmina_ssh_unconvert (RemminaSSH *ssh, const gchar *from)
{
599
	TRACE_CALL("remmina_ssh_unconvert");
600 601
	gchar *to = NULL;

602 603
	if (ssh->charset && from)
	{
604 605 606 607
		to = g_convert (from, -1, ssh->charset, "UTF-8", NULL, NULL, NULL);
	}
	if (!to) to = g_strdup (from);
	return to;
608 609
}

610
void
611 612
remmina_ssh_free (RemminaSSH *ssh)
{
613
	TRACE_CALL("remmina_ssh_free");
614 615
	if (ssh->session)
	{
616
		ssh_disconnect (ssh->session);
617 618 619
		ssh_free (ssh->session);
		ssh->session = NULL;
	}
620 621 622 623 624 625 626
	g_free(ssh->callback);
	g_free(ssh->server);
	g_free(ssh->user);
	g_free(ssh->password);
	g_free(ssh->privkeyfile);
	g_free(ssh->charset);
	g_free(ssh->error);
627
	pthread_mutex_destroy (&ssh->ssh_mutex);
628
	g_free(ssh);
629 630
}

631 632 633
/*-----------------------------------------------------------------------------*
 *                           SSH Tunnel                                        *
 *-----------------------------------------------------------------------------*/
634 635
struct _RemminaSSHTunnelBuffer
{
636 637 638
	gchar *data;
	gchar *ptr;
	ssize_t len;
639 640
};

641
static RemminaSSHTunnelBuffer*
642 643
remmina_ssh_tunnel_buffer_new (ssize_t len)
{
644
	TRACE_CALL("remmina_ssh_tunnel_buffer_new");
645
	RemminaSSHTunnelBuffer *buffer;
646

647 648 649 650 651
	buffer = g_new (RemminaSSHTunnelBuffer, 1);
	buffer->data = (gchar*) g_malloc (len);
	buffer->ptr = buffer->data;
	buffer->len = len;
	return buffer;
652 653
}

654
static void
655 656
remmina_ssh_tunnel_buffer_free (RemminaSSHTunnelBuffer *buffer)
{
657
	TRACE_CALL("remmina_ssh_tunnel_buffer_free");
658 659
	if (buffer)
	{
660 661
		g_free(buffer->data);
		g_free(buffer);
662
	}
663 664
}

665
RemminaSSHTunnel*
666 667
remmina_ssh_tunnel_new_from_file (RemminaFile *remminafile)
{
668
	TRACE_CALL("remmina_ssh_tunnel_new_from_file");
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
	RemminaSSHTunnel *tunnel;

	tunnel = g_new (RemminaSSHTunnel, 1);

	remmina_ssh_init_from_file (REMMINA_SSH (tunnel), remminafile);

	tunnel->tunnel_type = -1;
	tunnel->channels = NULL;
	tunnel->sockets = NULL;
	tunnel->socketbuffers = NULL;
	tunnel->num_channels = 0;
	tunnel->max_channels = 0;
	tunnel->x11_channel = NULL;
	tunnel->thread = 0;
	tunnel->running = FALSE;
	tunnel->server_sock = -1;
	tunnel->dest = NULL;
	tunnel->port = 0;
	tunnel->buffer = NULL;
	tunnel->buffer_len = 0;
	tunnel->channels_out = NULL;
	tunnel->remotedisplay = 0;
	tunnel->localdisplay = NULL;
	tunnel->init_func = NULL;
	tunnel->connect_func = NULL;
	tunnel->disconnect_func = NULL;
	tunnel->callback_data = NULL;

	return tunnel;
698 699
}

700
static void
701 702
remmina_ssh_tunnel_close_all_channels (RemminaSSHTunnel *tunnel)
{
703
	TRACE_CALL("remmina_ssh_tunnel_close_all_channels");
704 705
	int i;

706 707
	for (i = 0; i < tunnel->num_channels; i++)
	{
708 709
		close (tunnel->sockets[i]);
		remmina_ssh_tunnel_buffer_free (tunnel->socketbuffers[i]);
710 711
		ssh_channel_close (tunnel->channels[i]);
		ssh_channel_free (tunnel->channels[i]);
712 713
	}

714
	g_free(tunnel->channels);
715
	tunnel->channels = NULL;
716
	g_free(tunnel->sockets);
717
	tunnel->sockets = NULL;
718
	g_free(tunnel->socketbuffers);
719 720 721 722 723
	tunnel->socketbuffers = NULL;

	tunnel->num_channels = 0;
	tunnel->max_channels = 0;

724 725
	if (tunnel->x11_channel)
	{
726 727
		ssh_channel_close (tunnel->x11_channel);
		ssh_channel_free (tunnel->x11_channel);
728 729
		tunnel->x11_channel = NULL;
	}
730 731
}

732
static void
733 734
remmina_ssh_tunnel_remove_channel (RemminaSSHTunnel *tunnel, gint n)
{
735
	TRACE_CALL("remmina_ssh_tunnel_remove_channel");
736 737
	ssh_channel_close (tunnel->channels[n]);
	ssh_channel_free (tunnel->channels[n]);
738 739 740 741 742 743 744
	close (tunnel->sockets[n]);
	remmina_ssh_tunnel_buffer_free (tunnel->socketbuffers[n]);
	tunnel->num_channels--;
	tunnel->channels[n] = tunnel->channels[tunnel->num_channels];
	tunnel->channels[tunnel->num_channels] = NULL;
	tunnel->sockets[n] = tunnel->sockets[tunnel->num_channels];
	tunnel->socketbuffers[n] = tunnel->socketbuffers[tunnel->num_channels];
745 746 747
}

/* Register the new channel/socket pair */
748
static void
749 750
remmina_ssh_tunnel_add_channel (RemminaSSHTunnel *tunnel, ssh_channel channel, gint sock)
{
751
	TRACE_CALL("remmina_ssh_tunnel_add_channel");
752 753 754 755
	gint flags;
	gint i;

	i = tunnel->num_channels++;
756 757
	if (tunnel->num_channels > tunnel->max_channels)
	{
758 759
		/* Allocate an extra NULL pointer in channels for ssh_select */
		tunnel->channels = (ssh_channel*) g_realloc (tunnel->channels,
760
		                   sizeof (ssh_channel) * (tunnel->num_channels + 1));
761
		tunnel->sockets = (gint*) g_realloc (tunnel->sockets,
762
		                                     sizeof (gint) * tunnel->num_channels);
763
		tunnel->socketbuffers = (RemminaSSHTunnelBuffer**) g_realloc (tunnel->socketbuffers,
764
		                        sizeof (RemminaSSHTunnelBuffer*) * tunnel->num_channels);
765 766 767
		tunnel->max_channels = tunnel->num_channels;

		tunnel->channels_out = (ssh_channel*) g_realloc (tunnel->channels_out,
768
		                       sizeof (ssh_channel) * (tunnel->num_channels + 1));
769 770 771 772 773 774 775 776
	}
	tunnel->channels[i] = channel;
	tunnel->channels[i + 1] = NULL;
	tunnel->sockets[i] = sock;
	tunnel->socketbuffers[i] = NULL;

	flags = fcntl (sock, F_GETFL, 0);
	fcntl (sock, F_SETFL, flags | O_NONBLOCK);
777 778
}

779
static gpointer
780
remmina_ssh_tunnel_main_thread_proc (gpointer data)
781
{
782
	TRACE_CALL("remmina_ssh_tunnel_main_thread_proc");
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
	RemminaSSHTunnel *tunnel = (RemminaSSHTunnel*) data;
	gchar *ptr;
	ssize_t len = 0, lenw = 0;
	fd_set set;
	struct timeval timeout;
	GTimeVal t1, t2;
	glong diff;
	ssh_channel channel = NULL;
	gboolean first = TRUE;
	gboolean disconnected;
	gint sock;
	gint maxfd;
	gint i;
	gint ret;
	struct sockaddr_in sin;

	g_get_current_time (&t1);
	t2 = t1;

802 803 804 805 806 807 808 809 810 811 812
	switch (tunnel->tunnel_type)
	{
	case REMMINA_SSH_TUNNEL_OPEN:
		/* Accept a local connection */
		sock = accept (tunnel->server_sock, NULL, NULL);
		if (sock < 0)
		{
			REMMINA_SSH (tunnel)->error = g_strdup ("Failed to accept local socket");
			tunnel->thread = 0;
			return NULL;
		}
813

814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
		if ((channel = ssh_channel_new (tunnel->ssh.session)) == NULL)
		{
			close (sock);
			remmina_ssh_set_error (REMMINA_SSH (tunnel), "Failed to createt channel : %s");
			tunnel->thread = 0;
			return NULL;
		}
		/* Request the SSH server to connect to the destination */
		if (ssh_channel_open_forward (channel, tunnel->dest, tunnel->port, "127.0.0.1", 0) != SSH_OK)
		{
			close (sock);
			ssh_channel_close (channel);
			ssh_channel_free (channel);
			remmina_ssh_set_error (REMMINA_SSH (tunnel), _("Failed to connect to the SSH tunnel destination: %s"));
			tunnel->thread = 0;
			return NULL;
		}
		remmina_ssh_tunnel_add_channel (tunnel, channel, sock);
		break;

	case REMMINA_SSH_TUNNEL_X11:
		if ((tunnel->x11_channel = ssh_channel_new (tunnel->ssh.session)) == NULL)
		{
			remmina_ssh_set_error (REMMINA_SSH (tunnel), "Failed to create channel : %s");
			tunnel->thread = 0;
			return NULL;
		}
		if (!remmina_public_get_xauth_cookie (tunnel->localdisplay, &ptr))
		{
			remmina_ssh_set_application_error (REMMINA_SSH (tunnel), "%s", ptr);
844
			g_free(ptr);
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
			tunnel->thread = 0;
			return NULL;
		}
		if (ssh_channel_open_session (tunnel->x11_channel) ||
		        ssh_channel_request_x11 (tunnel->x11_channel, TRUE, NULL, ptr,
		                                 gdk_screen_get_number (gdk_screen_get_default ())))
		{
			g_free(ptr);
			remmina_ssh_set_error (REMMINA_SSH (tunnel), "Failed to open channel : %s");
			tunnel->thread = 0;
			return NULL;
		}
		g_free(ptr);
		if (ssh_channel_request_exec (tunnel->x11_channel, tunnel->dest))
		{
			ptr = g_strdup_printf(_("Failed to execute %s on SSH server : %%s"), tunnel->dest);
			remmina_ssh_set_error (REMMINA_SSH (tunnel), ptr);
			g_free(ptr);
			tunnel->thread = 0;
			return NULL;
		}
866

867 868 869 870 871 872
		if (tunnel->init_func &&
		        ! (*tunnel->init_func) (tunnel, tunnel->callback_data))
		{
			if (tunnel->disconnect_func)
			{
				(*tunnel->disconnect_func) (tunnel, tunnel->callback_data);
873
			}
874 875 876
			tunnel->thread = 0;
			return NULL;
		}
877

878
		break;
879

880 881 882 883
	case REMMINA_SSH_TUNNEL_XPORT:
		/* Detect the next available port starting from 6010 on the server */
		for (i = 10; i <= MAX_X_DISPLAY_NUMBER; i++)
		{
884
			G_GNUC_BEGIN_IGNORE_DEPRECATIONS
885 886 887
			if (ssh_forward_listen (REMMINA_SSH (tunnel)->session, (tunnel->bindlocalhost ? "localhost" : NULL), 6000 + i, NULL))
			{
				continue;
888
			}
889 890 891 892 893
			else
			{
				tunnel->remotedisplay = i;
				break;
			}
894
			G_GNUC_END_IGNORE_DEPRECATIONS
895 896 897 898 899 900 901
		}
		if (tunnel->remotedisplay < 1)
		{
			remmina_ssh_set_error (REMMINA_SSH (tunnel), _("Failed to request port forwarding : %s"));
			if (tunnel->disconnect_func)
			{
				(*tunnel->disconnect_func) (tunnel, tunnel->callback_data);
902
			}
903 904 905
			tunnel->thread = 0;
			return NULL;
		}
906

907 908 909 910 911 912
		if (tunnel->init_func &&
		        ! (*tunnel->init_func) (tunnel, tunnel->callback_data))
		{
			if (tunnel->disconnect_func)
			{
				(*tunnel->disconnect_func) (tunnel, tunnel->callback_data);
913
			}
914 915 916
			tunnel->thread = 0;
			return NULL;
		}
917

918
		break;
919

920
	case REMMINA_SSH_TUNNEL_REVERSE:
921
		G_GNUC_BEGIN_IGNORE_DEPRECATIONS
922 923
		if (ssh_forward_listen (REMMINA_SSH (tunnel)->session, NULL, tunnel->port, NULL))
		{
924
			G_GNUC_END_IGNORE_DEPRECATIONS
925 926 927 928
			remmina_ssh_set_error (REMMINA_SSH (tunnel), _("Failed to request port forwarding : %s"));
			if (tunnel->disconnect_func)
			{
				(*tunnel->disconnect_func) (tunnel, tunnel->callback_data);
929
			}
930 931 932
			tunnel->thread = 0;
			return NULL;
		}
933
		G_GNUC_BEGIN_IGNORE_DEPRECATIONS
934

935 936 937 938 939 940
		if (tunnel->init_func &&
		        ! (*tunnel->init_func) (tunnel, tunnel->callback_data))
		{
			if (tunnel->disconnect_func)
			{
				(*tunnel->disconnect_func) (tunnel, tunnel->callback_data);
941
			}
942 943 944
			tunnel->thread = 0;
			return NULL;
		}
945

946
		break;
947 948 949 950 951 952
	}

	tunnel->buffer_len = 10240;
	tunnel->buffer = g_malloc (tunnel->buffer_len);

	/* Start the tunnel data transmittion */
953 954
	while (tunnel->running)
	{
955
		if (tunnel->tunnel_type == REMMINA_SSH_TUNNEL_XPORT ||
956 957 958 959 960
		        tunnel->tunnel_type == REMMINA_SSH_TUNNEL_X11 ||
		        tunnel->tunnel_type == REMMINA_SSH_TUNNEL_REVERSE)
		{
			if (first)
			{
961 962
				first = FALSE;
				/* Wait for a period of time for the first incoming connection */
963 964
				if (tunnel->tunnel_type == REMMINA_SSH_TUNNEL_X11)
				{
965
					channel = ssh_channel_accept_x11 (tunnel->x11_channel, 15000);
966 967 968
				}
				else
				{
969
					channel = ssh_channel_accept_forward (REMMINA_SSH (tunnel)->session, 15000, &tunnel->port);
970
				}
971 972
				if (!channel)
				{
973
					remmina_ssh_set_application_error (REMMINA_SSH (tunnel), _("No response from the server."));
974 975
					if (tunnel->disconnect_func)
					{
976 977 978 979 980
						(*tunnel->disconnect_func) (tunnel, tunnel->callback_data);
					}
					tunnel->thread = 0;
					return NULL;
				}
981 982
				if (tunnel->connect_func)
				{
983 984
					(*tunnel->connect_func) (tunnel, tunnel->callback_data);
				}
985 986
				if (tunnel->tunnel_type == REMMINA_SSH_TUNNEL_REVERSE)
				{
987
					G_GNUC_BEGIN_IGNORE_DEPRECATIONS
988
					/* For reverse tunnel, we only need one connection. */
989
					G_GNUC_BEGIN_IGNORE_DEPRECATIONS
990
					ssh_forward_cancel (REMMINA_SSH (tunnel)->session, NULL, tunnel->port);
991
					G_GNUC_END_IGNORE_DEPRECATIONS
992
				}
993 994 995
			}
			else if (tunnel->tunnel_type != REMMINA_SSH_TUNNEL_REVERSE)
			{
996 997 998 999
				/* Poll once per some period of time if no incoming connections.
				 * Don't try to poll continuously as it will significantly slow down the loop */
				g_get_current_time (&t1);
				diff = (t1.tv_sec - t2.tv_sec) * 10 + (t1.tv_usec - t2.tv_usec) / 100000;
1000 1001 1002 1003
				if (diff > 1)
				{
					if (tunnel->tunnel_type == REMMINA_SSH_TUNNEL_X11)
					{
1004
						channel = ssh_channel_accept_x11 (tunnel->x11_channel, 0);
1005 1006 1007
					}
					else
					{
1008
						channel = ssh_channel_accept_forward (REMMINA_SSH (tunnel)->session, 0, &tunnel->port);
1009
					}
1010 1011
					if (channel == NULL)
					{
1012 1013 1014 1015 1016
						t2 = t1;
					}
				}
			}

1017 1018 1019 1020
			if (channel)
			{
				if (tunnel->tunnel_type == REMMINA_SSH_TUNNEL_REVERSE)
				{
1021 1022 1023 1024
					sin.sin_family = AF_INET;
					sin.sin_port = htons (tunnel->localport);
					sin.sin_addr.s_addr = inet_addr ("127.0.0.1");
					sock = socket (AF_INET, SOCK_STREAM, 0);
1025 1026
					if (connect (sock, (struct sockaddr *) &sin, sizeof (sin)) < 0)
					{
1027
						remmina_ssh_set_application_error (REMMINA_SSH (tunnel),
1028
						                                   "Cannot connect to local port %i.", tunnel->localport);
1029 1030 1031
						close (sock);
						sock = -1;
					}
1032 1033 1034
				}
				else
				{
1035 1036
					sock = remmina_public_open_xdisplay (tunnel->localdisplay);
				}
1037 1038
				if (sock >= 0)
				{
1039
					remmina_ssh_tunnel_add_channel (tunnel, channel, sock);
1040 1041 1042
				}
				else
				{
1043
					/* Failed to create unix socket. Will this happen? */
1044 1045
					ssh_channel_close (channel);
					ssh_channel_free (channel);
1046 1047 1048 1049 1050
				}
				channel = NULL;
			}
		}

1051 1052
		if (tunnel->num_channels <= 0)
		{
1053 1054 1055 1056 1057 1058 1059 1060 1061
			/* No more connections. We should quit */
			break;
		}

		timeout.tv_sec = 0;
		timeout.tv_usec = 200000;

		FD_ZERO (&set);
		maxfd = 0;
1062 1063 1064 1065
		for (i = 0; i < tunnel->num_channels; i++)
		{
			if (tunnel->sockets[i] > maxfd)
			{
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
				maxfd = tunnel->sockets[i];
			}
			FD_SET (tunnel->sockets[i], &set);
		}

		ret = ssh_select (tunnel->channels, tunnel->channels_out, maxfd + 1, &set, &timeout);
		if (!tunnel->running) break;
		if (ret == SSH_EINTR) continue;
		if (ret == -1) break;

		i = 0;
1077 1078
		while (tunnel->running && i < tunnel->num_channels)
		{
1079
			disconnected = FALSE;
1080 1081
			if (FD_ISSET (tunnel->sockets[i], &set))
			{
1082
				while (!disconnected &&
1083 1084 1085 1086
				        (len = read (tunnel->sockets[i], tunnel->buffer, tunnel->buffer_len)) > 0)
				{
					for (ptr = tunnel->buffer, lenw = 0; len > 0; len -= lenw, ptr += lenw)
					{
1087
						lenw = ssh_channel_write (tunnel->channels[i], (char*) ptr, len);
1088 1089
						if (lenw <= 0)
						{
1090
							disconnected = TRUE;
1091
							remmina_ssh_set_error (REMMINA_SSH (tunnel), "ssh_channel_write() returned an error: %s");
1092 1093 1094 1095
							break;
						}
					}
				}
1096 1097 1098 1099
				if (len == 0) {
					remmina_ssh_set_error (REMMINA_SSH (tunnel), "read on tunnel listening socket returned an error: %s");
					disconnected = TRUE;
				}
1100
			}
1101 1102
			if (disconnected)
			{
1103
				remmina_log_printf("[SSH] tunnel has been disconnected. Reason: %s\n", REMMINA_SSH(tunnel)->error);
1104 1105 1106 1107 1108 1109 1110 1111
				remmina_ssh_tunnel_remove_channel (tunnel, i);
				continue;
			}
			i++;
		}
		if (!tunnel->running) break;

		i = 0;
1112 1113
		while (tunnel->running && i < tunnel->num_channels)
		{
1114 1115
			disconnected = FALSE;

1116 1117
			if (!tunnel->socketbuffers[i])
			{
1118
				len = ssh_channel_poll (tunnel->channels[i], 0);
1119 1120
				if (len == SSH_ERROR || len == SSH_EOF)
				{
1121
					remmina_ssh_set_error (REMMINA_SSH (tunnel), "ssh_channel_poll() returned an error : %s");
1122
					disconnected = TRUE;
1123 1124 1125
				}
				else if (len > 0)
				{
1126
					tunnel->socketbuffers[i] = remmina_ssh_tunnel_buffer_new (len);
1127
					len = ssh_channel_read_nonblocking (tunnel->channels[i], tunnel->socketbuffers[i]->data, len, 0);
1128 1129
					if (len <= 0)
					{
1130
						remmina_ssh_set_error (REMMINA_SSH (tunnel), "ssh_channel_read_nonblocking() returned an error : %s");
1131
						disconnected = TRUE;
1132 1133 1134
					}
					else
					{
1135 1136 1137 1138 1139
						tunnel->socketbuffers[i]->len = len;
					}
				}
			}

1140 1141
			if (!disconnected && tunnel->socketbuffers[i])
			{
1142
				for (lenw = 0; tunnel->socketbuffers[i]->len > 0;
1143 1144
				        tunnel->socketbuffers[i]->len -= lenw, tunnel->socketbuffers[i]->ptr += lenw)
				{
1145
					lenw = write (tunnel->sockets[i], tunnel->socketbuffers[i]->ptr, tunnel->socketbuffers[i]->len);
1146 1147
					if (lenw == -1 && errno == EAGAIN && tunnel->running)
					{
1148 1149 1150 1151 1152
						/* Sometimes we cannot write to a socket (always EAGAIN), probably because it's internal
						 * buffer is full. We need read the pending bytes from the socket first. so here we simply
						 * break, leave the buffer there, and continue with other data */
						break;
					}
1153 1154
					if (lenw <= 0)
					{
1155
						remmina_ssh_set_error (REMMINA_SSH (tunnel), "write on tunnel listening socket returned an error: %s");
1156 1157 1158 1159
						disconnected = TRUE;
						break;
					}
				}
1160 1161
				if (tunnel->socketbuffers[i]->len <= 0)
				{
1162 1163 1164 1165 1166
					remmina_ssh_tunnel_buffer_free (tunnel->socketbuffers[i]);
					tunnel->socketbuffers[i] = NULL;
				}
			}

1167 1168
			if (disconnected)
			{
1169
				remmina_log_printf("[SSH] tunnel has been disconnected. Reason: %s\n", REMMINA_SSH(tunnel)->error);
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179
				remmina_ssh_tunnel_remove_channel (tunnel, i);
				continue;
			}
			i++;
		}
	}

	remmina_ssh_tunnel_close_all_channels (tunnel);

	return NULL;
1180 1181
}

1182
static gpointer
1183 1184
remmina_ssh_tunnel_main_thread (gpointer data)
{
1185
	TRACE_CALL("remmina_ssh_tunnel_main_thread");
1186
	RemminaSSHTunnel *tunnel = (RemminaSSHTunnel*) data;
1187

1188
	pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);
1189

1190 1191
	while (TRUE)
	{
1192 1193 1194 1195 1196
		remmina_ssh_tunnel_main_thread_proc (data);
		if (tunnel->server_sock < 0 || tunnel->thread == 0 || !tunnel->running) break;
	}
	tunnel->thread = 0;
	return NULL;
1197 1198
}

1199
void
1200 1201
remmina_ssh_tunnel_cancel_accept (RemminaSSHTunnel *tunnel)
{
1202
	TRACE_CALL("remmina_ssh_tunnel_cancel_accept");
1203 1204
	if (tunnel->server_sock >= 0)
	{
1205 1206 1207
		close (tunnel->server_sock);
		tunnel->server_sock = -1;
	}
1208 1209
}

1210
gboolean
1211
remmina_ssh_tunnel_open (RemminaSSHTunnel* tunnel, const gchar *host, gint port, gint local_port)
1212
{
1213
	TRACE_CALL("remmina_ssh_tunnel_open");
1214 1215 1216 1217 1218 1219 1220
	gint sock;
	gint sockopt = 1;
	struct sockaddr_in sin;

	tunnel->tunnel_type = REMMINA_SSH_TUNNEL_OPEN;
	tunnel->dest = g_strdup (host);
	tunnel->port = port;
1221 1222
	if (tunnel->port == 0)
	{
1223 1224 1225 1226 1227 1228
		REMMINA_SSH (tunnel)->error = g_strdup ("Destination port has not been assigned");
		return FALSE;
	}

	/* Create the server socket that listens on the local port */
	sock = socket (AF_INET, SOCK_STREAM, 0);
1229 1230
	if (sock < 0)
	{
1231 1232 1233 1234 1235 1236 1237 1238 1239
		REMMINA_SSH (tunnel)->error = g_strdup ("Failed to create socket.");
		return FALSE;
	}
	setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof (sockopt));

	sin.sin_family = AF_INET;
	sin.sin_port = htons (local_port);
	sin.sin_addr.s_addr = inet_addr ("127.0.0.1");

1240 1241
	if (bind (sock, (struct sockaddr *) &sin, sizeof(sin)))
	{
1242 1243 1244 1245 1246
		REMMINA_SSH (tunnel)->error = g_strdup ("Failed to bind on local port.");
		close (sock);
		return FALSE;
	}

1247 1248
	if (listen (sock, 1))
	{
1249 1250 1251 1252 1253 1254 1255 1256
		REMMINA_SSH (tunnel)->error = g_strdup ("Failed to listen on local port.");
		close (sock);
		return FALSE;
	}

	tunnel->server_sock = sock;
	tunnel->running = TRUE;

1257 1258
	if (pthread_create (&tunnel->thread, NULL, remmina_ssh_tunnel_main_thread, tunnel))
	{
1259 1260 1261 1262 1263
		remmina_ssh_set_application_error (REMMINA_SSH (tunnel), "Failed to initialize pthread.");
		tunnel->thread = 0;
		return FALSE;
	}
	return TRUE;
1264 1265
}

1266
gboolean
1267 1268
remmina_ssh_tunnel_x11 (RemminaSSHTunnel *tunnel, const gchar *cmd)
{
1269
	TRACE_CALL("remmina_ssh_tunnel_x11");
1270 1271 1272 1273
	tunnel->tunnel_type = REMMINA_SSH_TUNNEL_X11;
	tunnel->dest = g_strdup (cmd);
	tunnel->running = TRUE;

1274 1275
	if (pthread_create (&tunnel->thread, NULL, remmina_ssh_tunnel_main_thread, tunnel))
	{
1276 1277 1278 1279 1280
		remmina_ssh_set_application_error (REMMINA_SSH (tunnel), "Failed to initialize pthread.");
		tunnel->thread = 0;
		return FALSE;
	}
	return TRUE;
1281 1282
}

1283
gboolean
1284
remmina_ssh_tunnel_xport (RemminaSSHTunnel *tunnel, gboolean bindlocalhost)
1285
{
1286
	TRACE_CALL("remmina_ssh_tunnel_xport");
1287 1288 1289 1290
	tunnel->tunnel_type = REMMINA_SSH_TUNNEL_XPORT;
	tunnel->bindlocalhost = bindlocalhost;
	tunnel->running = TRUE;

1291 1292
	if (pthread_create (&tunnel->thread, NULL, remmina_ssh_tunnel_main_thread, tunnel))
	{
1293 1294 1295 1296 1297
		remmina_ssh_set_application_error (REMMINA_SSH (tunnel), "Failed to initialize pthread.");
		tunnel->thread = 0;
		return FALSE;
	}
	return TRUE;
1298 1299
}

1300
gboolean
1301 1302
remmina_ssh_tunnel_reverse (RemminaSSHTunnel *tunnel, gint port, gint local_port)
{
1303
	TRACE_CALL("remmina_ssh_tunnel_reverse");
1304 1305 1306 1307 1308
	tunnel->tunnel_type = REMMINA_SSH_TUNNEL_REVERSE;
	tunnel->port = port;
	tunnel->localport = local_port;
	tunnel->running = TRUE;

1309 1310
	if (pthread_create (&tunnel->thread, NULL, remmina_ssh_tunnel_main_thread, tunnel))
	{
1311 1312 1313 1314 1315
		remmina_ssh_set_application_error (REMMINA_SSH (tunnel), "Failed to initialize pthread.");
		tunnel->thread = 0;
		return FALSE;
	}
	return TRUE;
1316 1317
}

1318
gboolean
1319 1320
remmina_ssh_tunnel_terminated (RemminaSSHTunnel* tunnel)
{
1321
	TRACE_CALL("remmina_ssh_tunnel_terminated");
1322
	return (tunnel->thread == 0);
1323 1324
}

1325
void
1326 1327
remmina_ssh_tunnel_free (RemminaSSHTunnel* tunnel)
{
1328
	TRACE_CALL("remmina_ssh_tunnel_free");
1329 1330 1331
	pthread_t thread;

	thread = tunnel->thread;
1332 1333
	if (thread