client.c 25.1 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
/* client.c
2
3
4
5
6
 *
 * $Id$ */

/* lsh, an implementation of the ssh protocol
 *
7
 * Copyright (C) 1998, 1999, 2000 Niels Mller
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * 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
J.H.M. Dassen's avatar
J.H.M. Dassen committed
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Niels Möller's avatar
Niels Möller committed
22
23
24
 */

#include "client.h"
25

Niels Möller's avatar
Niels Möller committed
26
#include "abstract_io.h"
27
#include "channel.h"
28
#include "channel_commands.h"
29
#include "connection.h"
30
31
32
#include "format.h"
#include "interact.h"
#include "io.h"
Niels Möller's avatar
Niels Möller committed
33
#include "pad.h"
34
35
#include "parse.h"
#include "ssh.h"
36
#include "suspend.h"
37
#include "tcpforward_commands.h"
Niels Möller's avatar
Niels Möller committed
38
#include "translate_signal.h"
39
40
#include "werror.h"
#include "xalloc.h"
Niels Möller's avatar
Niels Möller committed
41

Niels Möller's avatar
Niels Möller committed
42
#include <string.h>
43
#include <assert.h>
Niels Möller's avatar
Niels Möller committed
44

45
46
47
48
49
50
51
52
53
54
55
56
#include <signal.h>

#if HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "lsh_argp.h"

57
58
59
60
#define GABA_DEFINE
#include "client.h.x"
#undef GABA_DEFINE

61
62
#include "client.c.x"

63
64
#define DEFAULT_ESCAPE_CHAR '~'

65
66
67
68
69
70
static struct lsh_string *
format_service_request(int name)
{
  return ssh_format("%c%a", SSH_MSG_SERVICE_REQUEST, name);
}

71
72
/* Start a service that the server has accepted (for instance
 * ssh-userauth). */
73
/* GABA:
74
75
76
77
   (class
     (name accept_service_handler)
     (super packet_handler)
     (vars
Niels Möller's avatar
Niels Möller committed
78
       (service . int)
79
       (c object command_continuation)
80
81

       ;; FIXME: Do we really need the exception handler here?
82
       (e object exception_handler)))
83
84
*/

Niels Möller's avatar
Niels Möller committed
85
86
87
88
static void
do_accept_service(struct packet_handler *c,
		  struct ssh_connection *connection,
		  struct lsh_string *packet)
89
{
90
  CAST(accept_service_handler, closure, c);
91
92

  struct simple_buffer buffer;
93
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
94
  int name;
95

96
97
  simple_buffer_init(&buffer, packet->length, packet->data);
  
98
99
  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_SERVICE_ACCEPT)
100
101
102
103
104
105
106
107
108
      && (
#if DATAFELLOWS_WORKAROUNDS
	  (connection->peer_flags & PEER_SERVICE_ACCEPT_KLUDGE)
#else
	  0
#endif
	  || (parse_atom(&buffer, &name)
	      && (name == closure->service)))
      && parse_eod(&buffer))
109
    {
110
      connection->dispatch[SSH_MSG_SERVICE_ACCEPT] = &connection_fail_handler;
111
      
Niels Möller's avatar
Niels Möller committed
112
      COMMAND_RETURN(closure->c, connection);
113
    }
Niels Möller's avatar
Niels Möller committed
114
  else
115
    PROTOCOL_ERROR(closure->e, "Invalid SSH_MSG_SERVICE_ACCEPT message");
116
117
}

118
struct packet_handler *
119
make_accept_service_handler(UINT32 service,
120
121
			    struct command_continuation *c,
			    struct exception_handler *e)
122
{
123
  NEW(accept_service_handler, closure);
124

Niels Möller's avatar
Niels Möller committed
125
  closure->super.handler = do_accept_service;
126
  closure->service = service;
127
  closure->c = c;
128
129
  closure->e = e;
  
130
131
132
  return &closure->super;
}

133
void
Niels Möller's avatar
Niels Möller committed
134
135
136
137
do_request_service(struct command *s,
		   struct lsh_object *x,
		   struct command_continuation *c,
		   struct exception_handler *e)
138
{
139
140
  CAST(request_service, self, s);
  CAST(ssh_connection, connection, x);
141
  
Niels Möller's avatar
Niels Möller committed
142
  connection->dispatch[SSH_MSG_SERVICE_ACCEPT]
143
    = make_accept_service_handler(self->service, c, e);
Niels Möller's avatar
Niels Möller committed
144
  
Niels Möller's avatar
Niels Möller committed
145
146
  C_WRITE(connection,
	  format_service_request(self->service));
147
148
}

149
150
struct command *
make_request_service(int service)
151
{
152
  NEW(request_service, closure);
153

154
  closure->super.call = do_request_service;
155
156
157
158
159
  closure->service = service;

  return &closure->super;
}

160

161
/* GABA:
162
163
164
165
   (class
     (name exit_handler)
     (super channel_request)
     (vars
Niels Möller's avatar
Niels Möller committed
166
       (exit_status . "int *")))
167
168
*/

Niels Möller's avatar
Niels Möller committed
169
170
171
static void
do_exit_status(struct channel_request *c,
	       struct ssh_channel *channel,
172
	       struct ssh_connection *connection UNUSED,
Niels Möller's avatar
Niels Möller committed
173
	       struct channel_request_info *info,
174
175
176
	       struct simple_buffer *args,
	       struct command_continuation *s,
	       struct exception_handler *e)
Niels Möller's avatar
Niels Möller committed
177
{
178
  CAST(exit_handler, closure, c);
179
  UINT32 status;
Niels Möller's avatar
Niels Möller committed
180

Niels Möller's avatar
Niels Möller committed
181
  if (!info->want_reply
Niels Möller's avatar
Niels Möller committed
182
183
184
      && parse_uint32(args, &status)
      && parse_eod(args))
    {
185
186
187
      verbose("client.c: Receiving exit-status %i on channel %i\n",
	      status, channel->channel_number);
      
Niels Möller's avatar
Niels Möller committed
188
189
      *closure->exit_status = status;

190
191
      ALIST_SET(channel->request_types, ATOM_EXIT_STATUS, NULL);
      ALIST_SET(channel->request_types, ATOM_EXIT_SIGNAL, NULL);
Niels Möller's avatar
Niels Möller committed
192

193
      COMMAND_RETURN(s, NULL);
Niels Möller's avatar
Niels Möller committed
194
    }
Niels Möller's avatar
Niels Möller committed
195
196
  else
    /* Invalid request */
197
    PROTOCOL_ERROR(e, "Invalid exit-status message");
Niels Möller's avatar
Niels Möller committed
198
199
}

Niels Möller's avatar
Niels Möller committed
200
201
202
static void
do_exit_signal(struct channel_request *c,
	       struct ssh_channel *channel,
203
	       struct ssh_connection *connection UNUSED,
Niels Möller's avatar
Niels Möller committed
204
	       struct channel_request_info *info,
205
206
207
	       struct simple_buffer *args,
	       struct command_continuation *s,
	       struct exception_handler *e)
Niels Möller's avatar
Niels Möller committed
208
{
209
210
  CAST(exit_handler, closure, c);

211
  UINT32 signal;
Niels Möller's avatar
Niels Möller committed
212
213
  int core;

Niels Möller's avatar
Niels Möller committed
214
  const UINT8 *msg;
Niels Möller's avatar
Niels Möller committed
215
216
  UINT32 length;

Niels Möller's avatar
Niels Möller committed
217
  const UINT8 *language;
Niels Möller's avatar
Niels Möller committed
218
219
  UINT32 language_length;
  
Niels Möller's avatar
Niels Möller committed
220
  if (!info->want_reply
221
      && parse_atom(args, &signal)
Niels Möller's avatar
Niels Möller committed
222
223
224
225
226
227
228
229
230
231
      && parse_boolean(args, &core)
      && parse_string(args, &length, &msg)
      && parse_string(args, &language_length, &language)
      && parse_eod(args))
    {
      /* FIXME: What exit status should be returned when the remote
       * process dies violently? */

      *closure->exit_status = 7;

232
233
      werror("Remote process was killed by signal: %ups %z\n",
	     length, msg,
234
	     core ? "(core dumped remotely)\n": "");
235
      
236
237
      ALIST_SET(channel->request_types, ATOM_EXIT_STATUS, NULL);
      ALIST_SET(channel->request_types, ATOM_EXIT_SIGNAL, NULL);
Niels Möller's avatar
Niels Möller committed
238

239
      COMMAND_RETURN(s, NULL);
Niels Möller's avatar
Niels Möller committed
240
    }
Niels Möller's avatar
Niels Möller committed
241
242
  else
    /* Invalid request */
243
    PROTOCOL_ERROR(e, "Invalid exit-signal message");
Niels Möller's avatar
Niels Möller committed
244
245
}

246
247
struct channel_request *
make_handle_exit_status(int *exit_status)
Niels Möller's avatar
Niels Möller committed
248
{
249
  NEW(exit_handler, self);
Niels Möller's avatar
Niels Möller committed
250
251
252
253
254
255
256
257

  self->super.handler = do_exit_status;

  self->exit_status = exit_status;

  return &self->super;
}

258
259
struct channel_request *
make_handle_exit_signal(int *exit_status)
Niels Möller's avatar
Niels Möller committed
260
{
261
  NEW(exit_handler, self);
Niels Möller's avatar
Niels Möller committed
262
263
264
265
266
267
268
269

  self->super.handler = do_exit_signal;

  self->exit_status = exit_status;

  return &self->super;
}

270
271
272
273
274
275
276
/* GABA:
   (class
     (name session_open_command)
     (super channel_open_command)
     (vars
       ; This command can only be executed once,
       ; so we can allocate the session object in advance.
277
       (session object ssh_channel)))
278
279
280
281
282
*/

static struct ssh_channel *
new_session(struct channel_open_command *s,
	    struct ssh_connection *connection,
283
	    UINT32 local_channel_number,
284
285
286
287
	    struct lsh_string **request)
{
  CAST(session_open_command, self, s);
  struct ssh_channel *res;
288
289
290

  self->session->write = connection->write;
  
291
292
293
  *request = format_channel_open(ATOM_SESSION,
				 local_channel_number,
				 self->session, "");
294
  
295
  res = self->session;
296
297
298
299
300
301
302

  /* Make sure this command can not be invoked again */
  self->session = NULL;

  return res;
}

303
304
struct command *
make_open_session_command(struct ssh_channel *session)
305
306
307
308
309
{
  NEW(session_open_command, self);
  self->super.super.call = do_channel_open_command;
  self->super.new_channel = new_session;
  self->session = session;
310
311

  return &self->super.super;
312
313
}

314
315
316
317
318
319
320
321

static struct lsh_string *
do_format_shell_request(struct channel_request_command *s UNUSED,
			struct ssh_channel *channel,
			struct command_continuation **c)
{
  return format_channel_request(ATOM_SHELL, channel, !!*c, "");
}
322

323
324
struct channel_request_command request_shell =
{ { STATIC_HEADER, do_channel_request_command }, do_format_shell_request };
325

326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358

/* GABA:
   (class
     (name exec_request)
     (super channel_request_command)
     (vars
       (command string)))
*/

static struct lsh_string *
do_format_exec_request(struct channel_request_command *s,
		       struct ssh_channel *channel,
		       struct command_continuation **c)
{
  CAST(exec_request, self, s);

  verbose("lsh: Requesting remote exec.\n");

  return format_channel_request(ATOM_EXEC, channel,
				!!*c, "%S", self->command);
}

struct command *
make_exec_request(struct lsh_string *command)
{
  NEW(exec_request, req);

  req->super.format_request = do_format_exec_request;
  req->super.super.call = do_channel_request_command;
  req->command = command;

  return &req->super.super;
}
359

360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392

/* Handling of options and operations shared by the plain lsh client
   and lshg. */

/* Forward declaration */

static struct ssh_channel *
make_client_session(struct client_options *options);

/* Block size for stdout and stderr buffers */
#define BLOCK_SIZE 32768

/* Window size for the session channel
 *
 * NOTE: Large windows seem to trig a bug in sshd2. */
#define WINDOW_SIZE 10000

#define ARG_NOT 0x400

#define OPT_STDIN 0x210
#define OPT_STDOUT 0x211
#define OPT_STDERR 0x212

#define OPT_FORK_STDIO 0x213

void
init_client_options(struct client_options *self,
		    struct io_backend *backend,
		    struct exception_handler *handler,
		    int *exit_code)			 
{
  self->backend = backend;
  self->tty = make_unix_interact(backend);
393
394
  self->escape = -1;
  
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  self->handler = handler;

  self->exit_code = exit_code;
  
  self->not = 0;
  self->port = "ssh";
  self->remote = NULL;

  self->local_user = self->user = getenv("LOGNAME");

  self->with_remote_peers = 0; 
  self->with_pty = -1;

  self->stdin_file = NULL;
  self->stdout_file = NULL;
  self->stderr_file = NULL;

  self->stdin_fork = 0;
  self->stdout_fork = 0;
414
  self->stderr_fork = 1;
415
416
417
418
419

  self->used_stdin = 0;
  self->used_pty = 0;

  self->start_shell = 1;
420
  self->remote_forward = 0;
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453

  object_queue_init(&self->actions);  
}

/* Host to connect to */
DEFINE_COMMAND_SIMPLE(client_options2remote, a)
{
  CAST_SUBTYPE(client_options, options, a);
  trace("client.c: client_options2remote\n");
  
  return &options->remote->super;
}

/* Host to connect to */
DEFINE_COMMAND_SIMPLE(client_options2actions, a)
{
  CAST_SUBTYPE(client_options, options, a);

  trace("client.c: client_options2actions, %i actions\n",
	options->actions.length);
  
  return &queue_to_list(&options->actions)->super.super;
}

static const struct argp_option
client_options[] =
{
  /* Name, key, arg-name, flags, doc, group */
  { "port", 'p', "Port", 0, "Connect to this port.", 0 },
  { "user", 'l', "User name", 0, "Login as this user.", 0 },

  { NULL, 0, NULL, 0, "Actions:", CLIENT_ARGP_ACTION_GROUP },
  { "forward-local-port", 'L', "local-port:target-host:target-port", 0, "", 0 },
454
#if 0
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
  { "forward-remote-port", 'R', "remote-port:target-host:target-port", 0, "", 0 },
#endif
  { "nop", 'N', NULL, 0, "No operation (suppresses the default action, "
    "which is to spawn a remote shell)", 0 },
  { "execute", 'E', "command", 0, "Execute a command on the remote machine", 0 },
  { "shell", 'S', "command", 0, "Spawn a remote shell", 0 },
  /* { "gateway", 'G', NULL, 0, "Setup a local gateway", 0 }, */
  { NULL, 0, NULL, 0, "Universal not:", 0 },
  { "no", 'n', NULL, 0, "Inverts the effect of the next modifier", 0 },

  { NULL, 0, NULL, 0, "Modifiers that apply to port forwarding:",
    CLIENT_ARGP_MODIFIER_GROUP - 10 },
  { "remote-peers", 'g', NULL, 0, "Allow remote access to forwarded ports", 0 },
  { "no-remote-peers", 'g' | ARG_NOT, NULL, 0, 
    "Disallow remote access to forwarded ports (default).", 0 },

  { NULL, 0, NULL, 0, "Modifiers that apply to remote execution:", 0 },
  { "stdin", OPT_STDIN, "Filename", 0, "Redirect stdin", 0},
  { "no-stdin", OPT_STDIN | ARG_NOT, NULL, 0, "Redirect stdin from /dev/null", 0}, 
  { "stdout", OPT_STDOUT, "Filename", 0, "Redirect stdout", 0},
  { "no-stdout", OPT_STDOUT | ARG_NOT, NULL, 0, "Redirect stdout to /dev/null", 0}, 
  { "stderr", OPT_STDERR, "Filename", 0, "Redirect stderr", 0},
  { "no-stderr", OPT_STDERR | ARG_NOT, NULL, 0, "Redirect stderr to /dev/null", 0}, 
478

479
  { "cvs-workaround", OPT_FORK_STDIO, "i?o?e?", OPTION_ARG_OPTIONAL,
480
481
482
483
484
485
    "Avoid setting one or more of the stdio file descriptors into "
    "non-blocking mode. If no argument is provided, the workaround is "
    "applied to all three file descriptors. By default, the workaround "
    "is applied to stderr only.", 0 },
  { "no-cvs-workaround", OPT_FORK_STDIO | ARG_NOT, NULL, 0,
    "Disable the cvs workaround.", 0 },
486
487
488
489
#if WITH_PTY_SUPPORT
  { "pty", 't', NULL, 0, "Request a remote pty (default).", 0 },
  { "no-pty", 't' | ARG_NOT, NULL, 0, "Don't request a remote pty.", 0 },
#endif /* WITH_PTY_SUPPORT */
490
491
  { "escape-char", 'e', "Character", 0, "Escape char. `none' means disable. "
    "Default is to use `~' if we have a tty, otherwise none.", 0 },
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
  { NULL, 0, NULL, 0, NULL, 0 }
};


/* GABA:
   (expr
     (name make_start_session)
     (params
       (open_session object command)
       (requests object object_list))
     (expr (lambda (connection)
       ((progn requests)
         ; Create a "session" channel
         (open_session connection)))))
*/
507

508
509
510
511
512
513
514
515
516
/* Requests a shell or command, and connects the channel to our stdio. */
/* GABA:
   (expr
     (name client_start_session)
     (params
       (request object command))
     (expr
       (lambda (session)
          (client_start_io (request session)))))
517
518
*/

519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
static struct command *
make_client_start_session(struct command *request)
{
  CAST_SUBTYPE(command, r, client_start_session(request));
  return r;
}

/* Create an interactive session */
static struct command *
client_shell_session(struct client_options *options)
{
  struct command *get_pty = NULL;
  struct command *get_shell;
  
  struct object_list *session_requests;
  struct ssh_channel *session = make_client_session(options);

  if (!session)
    return NULL;
  
#if WITH_PTY_SUPPORT
  if (options->with_pty && !options->used_pty)
    {
      options->used_pty = 1;
      
      if (options->tty && INTERACT_IS_TTY(options->tty))
	{
	  get_pty = make_pty_request(options->tty);
	  if (!get_pty)
	    {
549
	      werror("lsh: Can't use tty (probably getattr or atexit failed).\n");
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
	    }
	}
      else
	{
	  werror("lsh: No tty available.\n");
	}
    }

  get_shell = make_client_start_session(&request_shell.super);
  
  /* FIXME: We need a non-varargs constructor for lists. */
  if (get_pty)
    session_requests
      = make_object_list(2,
			 /* Ignore EXC_CHANNEL_REQUEST for the pty allocation call. */
			 make_catch_apply
			 (make_catch_handler_info(EXC_ALL, EXC_CHANNEL_REQUEST,
						  0, NULL),
			  get_pty),
			 get_shell, -1);
  else
#endif /* WITH_PTY_SUPPORT */
    session_requests = make_object_list(1, get_shell, -1);

  {
    CAST_SUBTYPE(command, r,
		 make_start_session
		 (make_open_session_command(session), session_requests));
    return r;
  }
}

/* Create a session executing a command line */
static struct command *
client_command_session(struct client_options *options,
		       struct lsh_string *command)
586
{
587
588
589
590
591
592
593
594
595
596
597
598
599
  struct ssh_channel *session = make_client_session(options);

  if (session)
    {
      CAST_SUBTYPE(command, r,
		   make_start_session
		   (make_open_session_command(session),
		    make_object_list
		    (1, make_client_start_session(make_exec_request(command)),
		     -1)));
      return r;
    }
  
600
601
602
  return NULL;
}

603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
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
698
699
700
struct command *
client_add_action(struct client_options *options,
		  struct command *action)
{
  if (action)
    object_queue_add_tail(&options->actions, &action->super);

  return action;
}

/* NOTE: Some of the original quoting is lost here. */
static struct lsh_string *
rebuild_command_line(unsigned argc, char **argv)
{
  unsigned length;
  unsigned i;
  unsigned pos;
  struct lsh_string *r;
  unsigned *alengths = alloca(sizeof(unsigned) * argc);
  
  assert (argc);
  length = argc - 1; /* Number of separating spaces. */

  for (i = 0; i<argc; i++)
    {
      alengths[i] = strlen(argv[i]);
      length += alengths[i];
    }

  r = lsh_string_alloc(length);
  memcpy(r->data, argv[0], alengths[0]);
  pos = alengths[0];
  for (i = 1; i<argc; i++)
    {
      r->data[pos++] = ' ';
      memcpy(r->data + pos, argv[i], alengths[i]);
      pos += alengths[i];
    }

  assert(pos == r->length);

  return r;
}

static int
fork_input(int in)
{
  /* pipe[0] for reading, pipe[1] for writing. */
  int pipe[2];

  if (!lsh_make_pipe(pipe))
    return -1;

  switch (fork())
    {
    case -1:
      /* Error */
      return -1;
    case 0:
      close(pipe[0]);
      if (lsh_copy_file(in, pipe[1]))
	_exit(EXIT_SUCCESS);
      else
	_exit(EXIT_FAILURE);
    default:
      /* Parent */
      close(pipe[1]);
      return pipe[0];
    }
}

static int
fork_output(int out)
{
  /* pipe[0] for reading, pipe[1] for writing. */
  int pipe[2];

  if (!lsh_make_pipe(pipe))
    return -1;

  switch (fork())
    {
    case -1:
      /* Error */
      return -1;
    case 0:
      close(pipe[1]);
      if (lsh_copy_file(pipe[0], out))
	_exit(EXIT_SUCCESS);
      else
	_exit(EXIT_FAILURE);
    default:
      /* Parent */
      close(pipe[0]);
      return pipe[1];
    }
}

701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
static struct escape_info *
client_escape_info(struct client_options *options)
{
  struct escape_info *info = NULL;

  if (!options->escape)
    return NULL;

  info = make_escape_info(options->escape);

  /* C-z */
  info->dispatch[26] = &suspend_callback;

  return info;
}

717
718
719
720
721
/* Create a session object. stdout and stderr are shared (although
 * with independent lsh_fd objects). stdin can be used by only one
 * session (until something "session-control"/"job-control" is added).
 * */
static struct ssh_channel *
722
make_client_session(struct client_options *options)
723
724
725
726
{
  int in;
  int out;
  int err;
727
728
  struct escape_info *escape = NULL;
  
729
730
731
732
  debug("lsh.c: Setting up stdin\n");

  if (options->stdin_file)
    in = open(options->stdin_file, O_RDONLY);
733
734
  else
    {
735
      if (options->used_stdin)
736
	in = open("/dev/null", O_RDONLY);
737
      else 
738
	{
739
	  in = (options->stdin_fork ? fork_input : dup)(STDIN_FILENO);
740
741
742
743

	  /* Attach the escape char handler. */
	  escape = client_escape_info(options);

744
	  options->used_stdin = 1;
745
746
747
748
749
	}
    }
    
  if (in < 0)
    {
750
      werror("lsh: Can't dup/open stdin (errno = %i): %z!\n",
751
752
753
754
	     errno, strerror(errno));
      return NULL;
    }

755
756
757
758
759
760
761
762
763
  debug("lsh.c: Setting up stdout\n");

  if (options->stdout_file)
    out = open(options->stdout_file, O_WRONLY | O_CREAT, 0666);
  else if (options->stdout_fork)
    out = fork_output(STDOUT_FILENO);
  else
    out = dup(STDOUT_FILENO);

764
765
766
767
768
769
770
771
  if (out < 0)
    {
      werror("lsh: Can't dup/open stdout (errno = %i): %z!\n",
	     errno, strerror(errno));
      close(in);
      return NULL;
    }

772
773
774
775
776
777
  debug("lsh.c: Setting up stderr\n");
  
  if (options->stderr_file)
    err = open(options->stderr_file, O_WRONLY | O_CREAT, 0666);
  else if (options->stderr_fork)
    err = fork_output(STDERR_FILENO);
778
779
780
  else
    {
      err = dup(STDERR_FILENO);
781
      set_error_nonblocking(STDERR_FILENO);
782
783
784
785
    }

  if (err < 0) 
    {
786
      werror("lsh: Can't dup/open stderr!\n");
787
788
789
790
791
792
      close(in);
      close(out);
      return NULL;
    }

  /* Clear options */
793
  options->stdin_file = options->stdout_file = options->stderr_file = NULL;
794
  
795
  return make_client_session_channel
796
797
    (io_read(make_lsh_fd(options->backend,
			 in, "client stdin", options->handler),
798
	     NULL, NULL),
799
800
     io_write(make_lsh_fd(options->backend,
			  out, "client stdout", options->handler),
801
	      BLOCK_SIZE, NULL),
802
803
     io_write(make_lsh_fd(options->backend,
			  err, "client stderr", options->handler),
804
	      BLOCK_SIZE, NULL),
805
     escape,
806
     WINDOW_SIZE,
807
     options->exit_code);
808
809
}

810

811
812
813
814
815
/* Parse the argument for -R and -L */
int
client_parse_forward_arg(char *arg,
			 UINT32 *listen_port,
			 struct address_info **target)
816
{
817
818
819
820
  char *first;
  char *second;
  char *end;
  long port;
821
  
822
823
824
  first = strchr(arg, ':');
  if (!first)
    return 0;
825

826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
  second = strchr(first + 1, ':');
  if (!second || (second == first + 1))
    return 0;

  if (strchr(second + 1, ':'))
    return 0;

  port = strtol(arg, &end, 0);
  if ( (end == arg)  || (end != first)
       || (port < 0) || (port > 0xffff) )
    return 0;

  *listen_port = port;

  port = strtol(second + 1, &end, 0);
  if ( (end == second + 1) || (*end != '\0')
       || (port < 0) || (port > 0xffff) )
    return 0;

  *target = make_address_info(ssh_format("%ls", second - first - 1, first + 1), port);
846
  
847
848
  return 1;
}
849

850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
#define CASE_ARG(opt, attr, none)		\
  case opt:					\
    if (options->not)				\
      {						\
        options->not = 0;			\
						\
      case opt | ARG_NOT:			\
        options->attr = none;			\
        break;					\
      }						\
      						\
    options->attr = arg;			\
    break

#define CASE_FLAG(opt, flag)			\
  case opt:					\
    if (options->not)				\
      {						\
        options->not = 0;			\
						\
      case opt | ARG_NOT:			\
        options->flag = 0;			\
        break;					\
      }						\
      						\
    options->flag = 1;				\
    break

static error_t
client_argp_parser(int key, char *arg, struct argp_state *state)
{
  CAST_SUBTYPE(client_options, options, state->input);

  switch(key)
    {
    default:
      return ARGP_ERR_UNKNOWN;
    case ARGP_KEY_NO_ARGS:
      argp_usage(state);
      break;
    case ARGP_KEY_ARG:
      if (!state->arg_num)
892
	{
893
894
895
896
897
898
899
900
901
	  if (options->port)
	    options->remote = make_address_info_c(arg, options->port, 0);
	  else
	    options->remote = make_address_info_c(arg, "ssh", 22);
	  
	  if (!options->remote)
	    argp_error(state, "Invalid port or service '%s'.", options->port);

	  break;
902
903
	}
      else
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
	/* Let the next case parse it.  */
	return ARGP_ERR_UNKNOWN;

      break;
    case ARGP_KEY_ARGS:
      client_add_action
	(options,
	 client_command_session
	 (options, rebuild_command_line(state->argc - state->next,
				     state->argv + state->next)));
      options->start_shell = 0;
      break;

    case ARGP_KEY_END:
      if (!options->user)
919
	{
920
921
922
	  argp_error(state, "No user name given. Use the -l option, or set LOGNAME in the environment.");
	  break;
	}
923
924
925
926
927
928
929
930

#if WITH_TCP_FORWARD
      if (options->remote_forward)
	client_add_action(options,
			  make_install_fix_channel_open_handler
			  (ATOM_FORWARDED_TCPIP, &channel_open_forwarded_tcpip));
#endif /* WITH_TCP_FORWARD */
      
931
932
933
      /* Add shell action */
      if (options->start_shell)
	client_add_action(options, client_shell_session(options));
934

935
936
      if (options->escape < 0)
	{
Niels Möller's avatar
Niels Möller committed
937
938
	  /* FIXME: This check is wrong. We should test whether or not
	   * stdin is a tty. */
939
940
941
942
943
944
945
946
947
	  /* Default behaviour */
	  if (options->tty && INTERACT_IS_TTY(options->tty))
	    options->escape = DEFAULT_ESCAPE_CHAR;
	  else
	    options->escape = 0;
	}
      
      /* Install suspend-handler */
      suspend_install_handler();
948
949
950
951
952
953
954
955
956
957
      break;

    case 'p':
      options->port = arg;
      break;

    case 'l':
      options->user = arg;
      break;

958
959
960
961
962
963
964
965
966
967
    case 'e':
      if (arg[0] && !arg[1])
	/* A single char argument */
	options->escape = arg[0];
      else if (!strcasecmp(arg, "none"))
	options->escape = 0;
      else
	argp_error(state, "Invalid escape char: `%s'. "
		   "You must use a single character or `none'.", arg);
      break;
968
969
970
971
972
973
974
975
976
977
978
979
980
    case 'E':
      client_add_action(options, client_command_session(options, ssh_format("%lz", arg)));
      break;

    case 'S':
      client_add_action(options, client_shell_session(options));
      break;

    case 'L':
      {
	UINT32 listen_port;
	struct address_info *target;

981
	if (!client_parse_forward_arg(arg, &listen_port, &target))
982
983
984
985
986
987
988
989
990
991
992
993
	  argp_error(state, "Invalid forward specification '%s'.", arg);

	client_add_action(options, make_forward_local_port
			  (options->backend,
			   make_address_info((options->with_remote_peers
					      ? NULL
					      : ssh_format("%lz", "127.0.0.1")),
					     listen_port),
			   target));
	break;
      }      

994
#if 0
995
996
997
998
999
    case 'R':
      {
	UINT32 listen_port;
	struct address_info *target;

1000
	if (!client_parse_forward_arg(arg, &listen_port, &target))
For faster browsing, not all history is shown. View entire blame