#!/usr/bin/perl # Launch ms rdp client with ssh tunnel # S. Radford # $version = " Version: 0.0 2013-08"; # $version = " Version: 0.1 2013-13"; # $version = " Version: 0.2 2014-07-02"; # $version = " Version: 0.3 2014-07-03"; # $version = " Version: 0.3.1 2014-07-30"; # $version = " Version: 0.3.2 2014-08-26"; $version = " Version: 0.4 2014-09-29"; # Portions adapted from tunnelopen.pl # Copyright (c) 2008 Thomas A. Fine # Redistribution is permitted, in whole or in part, for commercial and # non-commercial purposes, provided only that this copyright notice # remains. use Socket; $timeout=30; $startport=12345; $maxtries=50; $options=""; $user=""; $localhost="localhost"; $command="sleep $timeout"; # sleep doesn't exist on win hosts $width = 1024; $height = 768; $geometry = $width."x".$height; $diskmode = 0; $diskpath=""; # application versions and paths $rdpport=3389; $rdpver=2; $rdpapp2 = "/Applications/Remote Desktop Connection.app"; $rdpapp8 = "/Applications/Microsoft Remote Desktop.app"; # template files $rdpconn="$ENV{HOME}/Documents/RDC Connections/"; # $rdptemplate = "template$$.rdp"; $rdptmp="tmp$$.rdp"; while (substr($ARGV[0],0,1) eq "-") { $opt=shift(@ARGV); if ($opt eq "-b" || $opt eq "--debug") { $debug=1; } elsif ($opt eq "-C" || $opt eq "--comp") { $options .= "-C "; } elsif ($opt eq "-d" || $opt eq "--disk") { $diskmode=shift(@ARGV); if ($debug) { print "diskmode: " . $diskmode . "\n"; } if ($debug) { print "diskpath: " . $diskpath . "\n"; } if (not $diskmode ~~ [qw(0 1 2 3 )] ) { $diskpath = $diskmode ; $diskmode = 4; if ($debug) { print "diskpath: " . $diskpath . "\n"; } } if ($debug) { print "diskmode: " . $diskmode . "\n"; } } elsif ($opt eq "-h" || $opt eq "--help") { &usage; exit(0); } elsif ($opt eq "-g" || $opt eq "--geometry") { $geometry=shift(@ARGV); if (index(lc($geometry),"x") > 0) { ($width, $height) = split('x', lc($geometry), 2) ; } else { $width = $geometry ; } $geometry = $width."x".$height; if ($width !~ /^[0-9]/ || $height !~ /^[0-9]/) { &usage; exit(-1); } } elsif ($opt eq "-l" || $opt eq "--login") { $user=shift(@ARGV); if (index($user,"\@") == 0) { $ssh_host=substr($user,1); $user=""; } elsif (index($user,"@") > 0) { ($user,$ssh_host)=split('@',$user,2); $user .= "@"; } else { $user .= "@"; } } elsif ($opt eq "-p" || $opt eq "--port") { $ssh_port=shift(@ARGV); $options .= "-p $ssh_port "; } elsif ($opt eq "-t" || $opt eq "--timeout") { if (length($command) == 0) { &usage; exit(-1); } $timeout=shift(@ARGV); if ($timeout !~ /^[0-9]+[hmd]?$/) { &usage; exit(-1); } if ($timeout =~ /[hmd]$/) { $u=substr($timeout,length($timeout)-1,1); $timeout=substr($timeout,0,length($timeout)-1); if ($u eq "m") { $timeout *= 60; } if ($u eq "h") { $timeout *= 3600; } if ($u eq "d") { $timeout *= 86400; } } $command="sleep $timeout"; ++$customtimeout; } elsif ($opt eq "-v") { $rdpver=shift(@ARGV); } } die ( "Unknown (to this script) RDC version\n" ) unless ($rdpver eq 2 || $rdpver eq 8) ; if ($rdpver == 2) { unless (-e $rdpapp2) { $rdpver=8; } } if ($rdpver == 2) { die "Application not found: $rdpapp2\n" unless (-e $rdpapp2) ; $rdpapp = $rdpapp2 ; } elsif ($rdpver == 8) { die "Application not found: $rdpapp8\n" unless (-e $rdpapp8) ; $rdpapp = $rdpapp8 ; } if ($debug) { print STDERR "app: $rdpapp \n"; } if ($#ARGV == -1 || $#ARGV > 0) { &usage; exit(0); } $url=shift(@ARGV); if ($debug) { print STDERR "url: $url \n"; } if ($url !~ /:\/\//) { print STDERR "ERROR: argument must be in URL form.\n"; &usage; exit(0); } ($service,$rest)=split(/:\/\//,$url,2); ($hostport,$path)=split('/',$rest); ($host,$port)=split(':',$hostport); if (index($host,'@') > 0) { ($servuser,$host)=split('@',$host); # $servuser .= '@'; } if ($debug) { print STDERR "host: ", $host, "\n"; } if ($debug) { print STDERR "port: ", $port, "\n"; } if ($debug) { print STDERR "servuser: ", $servuser, "\n"; } if (length($ssh_host) == 0) { $ssh_host=$host; } if ($service eq "rdp") { if ($port>0) { $remoteport=$port; } else { $remoteport=$rdpport; } } else { print STDERR "Unknown (to this script) service: $service\n"; exit(-1); } $proto = getprotobyname('tcp'); if (!socket(S,PF_INET,SOCK_STREAM,$proto)) { print STDERR "Can't create socket: $!\n"; exit(-1); } for ($i=$startport; $i<$startport+$maxtries; ++$i) { $here=sockaddr_in($i,inet_aton($localhost)); if (bind(S, $here)) { if ($debug) { print STDERR "using local port $i\n"; } $localport=$i; #this shouldn't cause TIME_WAIT, because it isn't connected or listening close(S); last; } } if ($localport == 0) { print STDERR "Can't find a port to bind to!\n"; exit(-1); } if ($host eq $ssh_host) { $host="localhost"; } # Prepare temporary RDC configuration file from template # create directory and templates if necessary if ($debug) { print STDERR "template: $rdpconn$rdptemplate\n", "tmp: $rdpconn$rdptmp\n"; } unless (-d "$rdpconn") { if ($debug) { print STDERR "Creating RDP directory $rdpconn\n"; } mkdir "$rdpconn" or die "Cannot create $rdpconn\n"; } if ($debug) { print STDERR "Creating RDP configuration $rdpconn$rdptmp\n"; } &configuration; if ($debug) { print STDERR "ssh -L $localport:$host:$remoteport -f $options $user$ssh_host $command\n"; } print "ssh $user$ssh_host\n"; system("ssh -L $localport:$host:$remoteport -f $options $user$ssh_host $command"); if (length($command) == 0 || $timeout >= 60) { print "Using $service://$servuser\@$localhost:$localport/\n"; } # open configuration file, then system opens rdp app $rdpapp =~ s/ /\\ /g ; # escape embedded spaces $rdpconn =~ s/ /\\ /g ; # escape embedded spaces if ($debug) { print STDERR "open -a $rdpapp $rdpconn$rdptmp \n"; } system("open -a $rdpapp $rdpconn$rdptmp "); sleep 5; # wait for app to start # should test process id, etc. # remove configuration file $rdpconn =~ s/\\ / /g ; # un-escape embedded spaces if (! $debug) { unlink "$rdpconn$rdptmp" or warn "Could not unlink $rdpconn$rdptmp:\n $!"; } # # END OF MAIN # sub usage { print <", "$rdpconn$rdptmp") or die "Cannot open $rdpconn$rdptmp\n $!"; if ($rdpver == 2) { # RDC v2 print OUF < AddToKeychain ApplicationPath AudioRedirectionMode 0 AuthenticateLevel 0 AutoReconnect BitmapCaching ColorDepth 2 ConnectionString EEE # rdphost:98765 print OUF "\t" . $localhost . ":" . $localport ." \n"; print OUF <DesktopSize DesktopHeight EEE # 768 print OUF "\t\t" . $height . " \n"; print OUF "\t\tDesktopWidth \n"; # 1024 print OUF "\t\t" . $width . " \n"; print OUF < Display 0 Domain EEE # rdphost print OUF "\t" . $host ." \n"; print OUF <DontWarnOnChange DontWarnOnDriveMount DontWarnOnQuit DriveRedirectionMode EEE # 0 print OUF "\t" . $diskmode ." \n"; print OUF <FontSmoothing FullWindowDrag HideMacDock KeyMappingTable UI_ALPHANUMERIC_KEY MacKeyCode 102 MacModifier 0 On UI_ALT_KEY MacKeyCode 4294967295 MacModifier 2048 On UI_CONTEXT_MENU_KEY MacKeyCode 120 MacModifier 2048 On UI_CONVERSION_KEY MacKeyCode 4294967295 MacModifier 0 On UI_HALF_FULL_WIDTH_KEY MacKeyCode 49 MacModifier 256 On UI_HIRAGANA_KEY MacKeyCode 104 MacModifier 0 On UI_NON_CONVERSION_KEY MacKeyCode 4294967295 MacModifier 0 On UI_NUM_LOCK_KEY MacKeyCode 71 MacModifier 0 On UI_PAUSE_BREAK_KEY MacKeyCode 99 MacModifier 2048 On UI_PRINT_SCREEN_KEY MacKeyCode 118 MacModifier 2048 On UI_SCROLL_LOCK_KEY MacKeyCode 107 MacModifier 0 On UI_SECONDARY_MOUSE_BUTTON MacKeyCode 256 MacModifier 4608 On UI_WINDOWS_START_KEY MacKeyCode 122 MacModifier 2048 On MenuAnimations PrinterRedirection RedirectFolder EEE # print OUF "\t" . $diskpath ." \n"; print OUF <RedirectPrinter RemoteApplication Themes UserName EEE # rdpuser print OUF "\t" . $servuser ." \n"; print OUF <Wallpaper WorkingDirectory EEE } else { # MS RD v8 print OUF "desktopwidth:i:" . $width . "\n"; print OUF "desktopheight:i:" . $height . "\n"; print OUF "full address:s:" . $localhost . ":" . $localport . "\n"; print OUF "username:s:". $servuser . "\n"; # 2014-09-29 # although these parameters are documented, they don't seem to work print OUF "redirectdrives:i:" . $diskmode . "\n"; print OUF "drivestoredirect:s:" . $diskpath . "\n"; print OUF <