+static void
+client_status_confirm(int type, Channel *c, void *ctx)
+{
+ struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx;
+ char errmsg[256];
+ int tochan;
+
+ /* XXX supress on mux _client_ quietmode */
+ tochan = options.log_level >= SYSLOG_LEVEL_ERROR &&
+ c->ctl_fd != -1 && c->extended_usage == CHAN_EXTENDED_WRITE;
+
+ if (type == SSH2_MSG_CHANNEL_SUCCESS) {
+ debug2("%s request accepted on channel %d",
+ cr->request_type, c->self);
+ } else if (type == SSH2_MSG_CHANNEL_FAILURE) {
+ if (tochan) {
+ snprintf(errmsg, sizeof(errmsg),
+ "%s request failed\r\n", cr->request_type);
+ } else {
+ snprintf(errmsg, sizeof(errmsg),
+ "%s request failed on channel %d",
+ cr->request_type, c->self);
+ }
+ /* If error occurred on primary session channel, then exit */
+ if (cr->do_close && c->self == session_ident)
+ fatal("%s", errmsg);
+ /* If error occurred on mux client, append to their stderr */
+ if (tochan)
+ buffer_append(&c->extended, errmsg, strlen(errmsg));
+ else
+ error("%s", errmsg);
+ if (cr->do_close) {
+ chan_read_failed(c);
+ chan_write_failed(c);
+ }
+ }
+ xfree(cr);
+}
+
+static void
+client_abandon_status_confirm(Channel *c, void *ctx)
+{
+ xfree(ctx);
+}
+
+static void
+client_expect_confirm(int id, const char *request, int do_close)
+{
+ struct channel_reply_ctx *cr = xmalloc(sizeof(*cr));
+
+ cr->request_type = request;
+ cr->do_close = do_close;
+
+ channel_register_status_confirm(id, client_status_confirm,
+ client_abandon_status_confirm, cr);
+}
+
+void
+client_register_global_confirm(global_confirm_cb *cb, void *ctx)
+{
+ struct global_confirm *gc, *last_gc;
+
+ /* Coalesce identical callbacks */
+ last_gc = TAILQ_LAST(&global_confirms, global_confirms);
+ if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) {
+ if (++last_gc->ref_count >= INT_MAX)
+ fatal("%s: last_gc->ref_count = %d",
+ __func__, last_gc->ref_count);
+ return;
+ }
+
+ gc = xmalloc(sizeof(*gc));
+ gc->cb = cb;
+ gc->ctx = ctx;
+ gc->ref_count = 1;
+ TAILQ_INSERT_TAIL(&global_confirms, gc, entry);
+}
+
+static void
+process_cmdline(void)
+{
+ void (*handler)(int);
+ char *s, *cmd, *cancel_host;
+ int delete = 0;
+ int local = 0, remote = 0, dynamic = 0;
+ int cancel_port;
+ Forward fwd;
+
+ bzero(&fwd, sizeof(fwd));
+ fwd.listen_host = fwd.connect_host = NULL;
+
+ leave_raw_mode();
+ handler = signal(SIGINT, SIG_IGN);
+ cmd = s = read_passphrase("\r\nssh> ", RP_ECHO);
+ if (s == NULL)
+ goto out;
+ while (isspace(*s))
+ s++;
+ if (*s == '-')
+ s++; /* Skip cmdline '-', if any */
+ if (*s == '\0')
+ goto out;
+
+ if (*s == 'h' || *s == 'H' || *s == '?') {
+ logit("Commands:");
+ logit(" -L[bind_address:]port:host:hostport "
+ "Request local forward");
+ logit(" -R[bind_address:]port:host:hostport "
+ "Request remote forward");
+ logit(" -D[bind_address:]port "
+ "Request dynamic forward");
+ logit(" -KR[bind_address:]port "
+ "Cancel remote forward");
+ if (!options.permit_local_command)
+ goto out;
+ logit(" !args "
+ "Execute local command");
+ goto out;
+ }
+
+ if (*s == '!' && options.permit_local_command) {
+ s++;
+ ssh_local_cmd(s);
+ goto out;
+ }
+
+ if (*s == 'K') {
+ delete = 1;
+ s++;
+ }
+ if (*s == 'L')
+ local = 1;
+ else if (*s == 'R')
+ remote = 1;
+ else if (*s == 'D')
+ dynamic = 1;
+ else {
+ logit("Invalid command.");
+ goto out;
+ }
+
+ if ((local || dynamic) && delete) {
+ logit("Not supported.");
+ goto out;
+ }
+ if (remote && delete && !compat20) {
+ logit("Not supported for SSH protocol version 1.");
+ goto out;
+ }
+
+ while (isspace(*++s))
+ ;
+
+ if (delete) {
+ cancel_port = 0;
+ cancel_host = hpdelim(&s); /* may be NULL */
+ if (s != NULL) {
+ cancel_port = a2port(s);
+ cancel_host = cleanhostname(cancel_host);
+ } else {
+ cancel_port = a2port(cancel_host);
+ cancel_host = NULL;
+ }
+ if (cancel_port <= 0) {
+ logit("Bad forwarding close port");
+ goto out;
+ }
+ channel_request_rforward_cancel(cancel_host, cancel_port);
+ } else {
+ if (!parse_forward(&fwd, s, dynamic, remote)) {
+ logit("Bad forwarding specification.");
+ goto out;
+ }
+ if (local || dynamic) {
+ if (channel_setup_local_fwd_listener(fwd.listen_host,
+ fwd.listen_port, fwd.connect_host,
+ fwd.connect_port, options.gateway_ports) < 0) {
+ logit("Port forwarding failed.");
+ goto out;
+ }
+ } else {
+ if (channel_request_remote_forwarding(fwd.listen_host,
+ fwd.listen_port, fwd.connect_host,
+ fwd.connect_port) < 0) {
+ logit("Port forwarding failed.");
+ goto out;
+ }
+ }
+
+ logit("Forwarding port.");
+ }
+
+out:
+ signal(SIGINT, handler);
+ enter_raw_mode();
+ if (cmd)
+ xfree(cmd);
+ if (fwd.listen_host != NULL)
+ xfree(fwd.listen_host);
+ if (fwd.connect_host != NULL)
+ xfree(fwd.connect_host);
+}
+
+/*
+ * Process the characters one by one, call with c==NULL for proto1 case.
+ */