1 /*
2     Mosh: the mobile shell
3     Copyright 2012 Keith Winstein
4 
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18     In addition, as a special exception, the copyright holders give
19     permission to link the code of portions of this program with the
20     OpenSSL library under certain conditions as described in each
21     individual source file, and distribute linked combinations including
22     the two.
23 
24     You must obey the GNU General Public License in all respects for all
25     of the code used other than OpenSSL. If you modify file(s) with this
26     exception, you may extend this exception to your version of the
27     file(s), but you are not obligated to do so. If you do not wish to do
28     so, delete this exception statement from your version. If you delete
29     this exception statement from all source files in the program, then
30     also delete it here.
31 */
32 
33 #include "config.h"
34 #include "version.h"
35 
36 #include <stdlib.h>
37 #include <unistd.h>
38 
39 #include "stmclient.h"
40 #include "crypto.h"
41 #include "locale_utils.h"
42 #include "fatal_assert.h"
43 
44 /* These need to be included last because of conflicting defines. */
45 /*
46  * stmclient.h includes termios.h, and that will break termio/termios pull in on Solaris.
47  * The solution is to include termio.h also.
48  * But Mac OS X doesn't have termio.h, so this needs a guard.
49  */
50 #ifdef HAVE_TERMIO_H
51 #include <termio.h>
52 #endif
53 
54 #if defined HAVE_NCURSESW_CURSES_H
55 #  include <ncursesw/curses.h>
56 #  include <ncursesw/term.h>
57 #elif defined HAVE_NCURSESW_H
58 #  include <ncursesw.h>
59 #  include <term.h>
60 #elif defined HAVE_NCURSES_CURSES_H
61 #  include <ncurses/curses.h>
62 #  include <ncurses/term.h>
63 #elif defined HAVE_NCURSES_H
64 #  include <ncurses.h>
65 #  include <term.h>
66 #elif defined HAVE_CURSES_H
67 #  include <curses.h>
68 #  include <term.h>
69 #else
70 #  error "SysV or X/Open-compatible Curses header file required"
71 #endif
72 
print_version(FILE * file)73 static void print_version( FILE *file )
74 {
75   fprintf( file, "mosh-client (%s) [build %s]\n", PACKAGE_STRING, BUILD_VERSION );
76   fprintf( file, "Copyright 2012 Keith Winstein <mosh-devel@mit.edu>\n" );
77   fprintf( file, "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.\n" );
78 }
79 
print_usage(FILE * file,const char * argv0)80 static void print_usage( FILE *file, const char *argv0 )
81 {
82   print_version( file );
83   fprintf( file, "\n" );
84   fprintf( file, "Usage: %s [-# 'ARGS'] IP PORT\n       %s -c\n", argv0, argv0 );
85 }
86 
print_colorcount(void)87 static void print_colorcount( void )
88 {
89   /* check colors */
90   setupterm((char *)0, 1, (int *)0);
91 
92   char colors_name[] = "colors";
93   int color_val = tigetnum( colors_name );
94   if ( color_val == -2 ) {
95     fprintf( stderr, "Invalid terminfo numeric capability: %s\n",
96 	     colors_name );
97   }
98 
99   printf( "%d\n", color_val );
100 }
101 
102 #ifdef NACL
mosh_main(int argc,char * argv[])103 int mosh_main( int argc, char *argv[] )
104 #else
105 int main( int argc, char *argv[] )
106 #endif
107 {
108   unsigned int verbose = 0;
109   /* For security, make sure we don't dump core */
110   Crypto::disable_dumping_core();
111 
112   /* Detect edge case */
113   fatal_assert( argc > 0 );
114 
115   /* Get arguments */
116   for ( int i = 1; i < argc; i++ ) {
117     if ( 0 == strcmp( argv[ i ], "--help" ) ) {
118       print_usage( stdout, argv[ 0 ] );
119       exit( 0 );
120     }
121     if ( 0 == strcmp( argv[ i ], "--version" ) ) {
122       print_version( stdout );
123       exit( 0 );
124     }
125   }
126 
127   int opt;
128   while ( (opt = getopt( argc, argv, "#:cv" )) != -1 ) {
129     switch ( opt ) {
130     case '#':
131       // Ignore the original arguments to mosh wrapper
132       break;
133     case 'c':
134       print_colorcount();
135       exit( 0 );
136       break;
137     case 'v':
138       verbose++;
139       break;
140     default:
141       print_usage( stderr, argv[ 0 ] );
142       exit( 1 );
143       break;
144     }
145   }
146 
147   char *ip, *desired_port;
148 
149   if ( argc - optind != 2 ) {
150     print_usage( stderr, argv[ 0 ] );
151     exit( 1 );
152   }
153 
154   ip = argv[ optind ];
155   desired_port = argv[ optind + 1 ];
156 
157   /* Sanity-check arguments */
158   if ( desired_port
159        && ( strspn( desired_port, "0123456789" ) != strlen( desired_port ) ) ) {
160     fprintf( stderr, "%s: Bad UDP port (%s)\n\n", argv[ 0 ], desired_port );
161     print_usage( stderr, argv[ 0 ] );
162     exit( 1 );
163   }
164 
165   /* Read key from environment */
166   char *env_key = getenv( "MOSH_KEY" );
167   if ( env_key == NULL ) {
168     fprintf( stderr, "MOSH_KEY environment variable not found.\n" );
169     exit( 1 );
170   }
171 
172   /* Read prediction preference */
173   char *predict_mode = getenv( "MOSH_PREDICTION_DISPLAY" );
174   /* can be NULL */
175 
176   char *key = strdup( env_key );
177   if ( key == NULL ) {
178     perror( "strdup" );
179     exit( 1 );
180   }
181 
182   if ( unsetenv( "MOSH_KEY" ) < 0 ) {
183     perror( "unsetenv" );
184     exit( 1 );
185   }
186 
187   /* Adopt native locale */
188   set_native_locale();
189 
190   bool success = false;
191   try {
192     STMClient client( ip, desired_port, key, predict_mode, verbose );
193     client.init();
194 
195     try {
196       success = client.main();
197     } catch ( ... ) {
198       client.shutdown();
199       throw;
200     }
201 
202     client.shutdown();
203   } catch ( const Network::NetworkException &e ) {
204     fprintf( stderr, "Network exception: %s\r\n",
205 	     e.what() );
206     success = false;
207   } catch ( const Crypto::CryptoException &e ) {
208     fprintf( stderr, "Crypto exception: %s\r\n",
209 	     e.what() );
210     success = false;
211   } catch ( const std::exception &e ) {
212     fprintf( stderr, "Error: %s\r\n", e.what() );
213     success = false;
214   }
215 
216   printf( "[mosh is exiting.]\n" );
217 
218   free( key );
219 
220   return !success;
221 }
222