Beispiel #1
0
static void logfopen_callback(void *handle, int mode)
{
    struct LogContext *ctx = (struct LogContext *)handle;
    char buf[256], *event;
    struct tm tm;
    const char *fmode;

    if (mode == 0) {
        ctx->state = L_ERROR;	       /* disable logging */
    } else {
        fmode = (mode == 1 ? "ab" : "wb");
        ctx->lgfp = f_open(ctx->currlogfilename, fmode, FALSE);
        if (ctx->lgfp)
            ctx->state = L_OPEN;
        else
            ctx->state = L_ERROR;
    }

    if (ctx->state == L_OPEN) {
        /* Write header line into log file. */
        tm = ltime();
        strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
        logprintf(ctx, "=~=~=~=~=~=~=~=~=~=~=~= PuTTY log %s"
                  " =~=~=~=~=~=~=~=~=~=~=~=\r\n", buf);
    }

    event = dupprintf("%s session log (%s mode) to file: %s",
                      ctx->state == L_ERROR ?
                      (mode == 0 ? "Disabled writing" : "Error writing") :
                      (mode == 1 ? "Appending" : "Writing new"),
                      (ctx->logtype == LGTYP_ASCII ? "ASCII" :
                       ctx->logtype == LGTYP_DEBUG ? "raw" :
                       ctx->logtype == LGTYP_PACKETS ? "SSH packets" :
                       ctx->logtype == LGTYP_SSHRAW ? "SSH raw data" :
                       "unknown"),
                      filename_to_str(ctx->currlogfilename));
    logevent(ctx->frontend, event);
    sfree(event);

    /*
     * Having either succeeded or failed in opening the log file,
     * we should write any queued data out.
     */
    assert(ctx->state != L_OPENING);   /* make _sure_ it won't be requeued */
    while (bufchain_size(&ctx->queue)) {
        void *data;
        int len;
        bufchain_prefix(&ctx->queue, &data, &len);
        logwrite(ctx, data, len);
        bufchain_consume(&ctx->queue, len);
    }
}
Beispiel #2
0
static void sk_proxy_set_frozen(Socket s, int is_frozen)
{
  Proxy_Socket ps = (Proxy_Socket)s;

  if (ps->state != PROXY_STATE_ACTIVE) {
    ps->freeze = is_frozen;
    return;
  }

  /* handle any remaining buffered recv data first */
  if (bufchain_size(&ps->pending_input_data) > 0) {
    ps->freeze = is_frozen;

    /* loop while we still have buffered data, and while we are
     * unfrozen. the plug_receive call in the loop could result
     * in a call back into this function refreezing the socket,
     * so we have to check each time.
     */
    while (!ps->freeze && bufchain_size(&ps->pending_input_data) > 0) {
      void *data;
      char databuf[512];
      int len;
      bufchain_prefix(&ps->pending_input_data, &data, &len);
      if (len > lenof(databuf))
        len = lenof(databuf);
      memcpy(databuf, data, len);
      bufchain_consume(&ps->pending_input_data, len);
      plug_receive(ps->plug, 0, databuf, len);
    }

    /* if we're still frozen, we'll have to wait for another
     * call from the backend to finish unbuffering the data.
     */
    if (ps->freeze)
      return;
  }

  sk_set_frozen(ps->sub_socket, is_frozen);
}
Beispiel #3
0
static void handle_try_output(struct handle_output *ctx)
{
    void *senddata;
    int sendlen;

    if (!ctx->busy && bufchain_size(&ctx->queued_data)) {
	bufchain_prefix(&ctx->queued_data, &senddata, &sendlen);
	ctx->buffer = senddata;
	ctx->len = sendlen;
	SetEvent(ctx->ev_from_main);
	ctx->busy = TRUE;
    }
}
Beispiel #4
0
static int localproxy_try_send(Local_Proxy_Socket ps)
{
    int sent = 0;

    while (bufchain_size(&ps->pending_output_data) > 0) {
	void *data;
	int len, ret;

	bufchain_prefix(&ps->pending_output_data, &data, &len);
	ret = write(ps->to_cmd, data, len);
	if (ret < 0 && errno != EWOULDBLOCK) {
	    /* We're inside the Unix frontend here, so we know
	     * that the frontend handle is unnecessary. */
	    logevent(NULL, strerror(errno));
	    fatalbox("%s", strerror(errno));
	} else if (ret <= 0) {
	    break;
	} else {
	    bufchain_consume(&ps->pending_output_data, ret);
	    sent += ret;
	}
    }

    if (ps->outgoingeof == EOF_PENDING) {
        del234(localproxy_by_tofd, ps);
        close(ps->to_cmd);
        uxsel_del(ps->to_cmd);
        ps->to_cmd = -1;
        ps->outgoingeof = EOF_SENT;
    }

    if (bufchain_size(&ps->pending_output_data) == 0)
	uxsel_del(ps->to_cmd);
    else
	uxsel_set(ps->to_cmd, 2, localproxy_select_result);

    return sent;
}
Beispiel #5
0
void log_proxy_stderr(Plug plug, bufchain *buf, const void *vdata, int len)
{
    const char *data = (const char *)vdata;
    int pos = 0;
    int msglen;
    char *nlpos, *msg, *fullmsg;

    /*
     * This helper function allows us to collect the data written to a
     * local proxy command's standard error in whatever size chunks we
     * happen to get from its pipe, and whenever we have a complete
     * line, we pass it to plug_log.
     *
     * Prerequisites: a plug to log to, and a bufchain stored
     * somewhere to collect the data in.
     */

    while (pos < len && (nlpos = memchr(data+pos, '\n', len-pos)) != NULL) {
        /*
         * Found a newline in the current input buffer. Append it to
         * the bufchain (which may contain a partial line from last
         * time).
         */
        bufchain_add(buf, data + pos, nlpos - (data + pos));

        /*
         * Collect the resulting line of data and pass it to plug_log.
         */
        msglen = bufchain_size(buf);
        msg = snewn(msglen+1, char);
        bufchain_fetch(buf, msg, msglen);
        bufchain_consume(buf, msglen);
        msg[msglen] = '\0';
        fullmsg = dupprintf("proxy: %s", msg);
        plug_log(plug, 2, NULL, 0, fullmsg, 0);
        sfree(fullmsg);
        sfree(msg);

        /*
         * Advance past the newline.
         */
        pos += (int)(nlpos+1 - (data + pos));
    }

    /*
     * Now any remaining data is a partial line, which we save for
     * next time.
     */
    bufchain_add(buf, data + pos, len - pos);
}
Beispiel #6
0
static int localproxy_try_send(Local_Proxy_Socket ps)
{
    int sent = 0;

    while (bufchain_size(&ps->pending_output_data) > 0) {
	void *data;
	int len, ret;

	bufchain_prefix(&ps->pending_output_data, &data, &len);
	ret = write(ps->to_cmd, data, len);
	if (ret < 0 && errno != EWOULDBLOCK) {
            plug_closing(ps->plug, strerror(errno), errno, 0);
            return 0;
	} else if (ret <= 0) {
	    break;
	} else {
	    bufchain_consume(&ps->pending_output_data, ret);
	    sent += ret;
	}
    }

    if (ps->outgoingeof == EOF_PENDING) {
        del234(localproxy_by_tofd, ps);
        close(ps->to_cmd);
        uxsel_del(ps->to_cmd);
        ps->to_cmd = -1;
        ps->outgoingeof = EOF_SENT;
    }

    if (bufchain_size(&ps->pending_output_data) == 0)
	uxsel_del(ps->to_cmd);
    else
	uxsel_set(ps->to_cmd, 2, localproxy_select_result);

    return sent;
}
Beispiel #7
0
static void pty_uxsel_setup(Pty pty)
{
    int rwx;

    rwx = 1;                           /* always want to read from pty */
    if (bufchain_size(&pty->output_data))
        rwx |= 2;                      /* might also want to write to it */
    uxsel_set(pty->master_fd, rwx, pty_select_result);

    /*
     * In principle this only needs calling once for all pty
     * backend instances, but it's simplest just to call it every
     * time; uxsel won't mind.
     */
    uxsel_set(pty_signal_pipe[0], 1, pty_select_result);
}
Beispiel #8
0
static void handle_socket_unfreeze(void *psv)
{
    Handle_Socket ps = (Handle_Socket) psv;
    void *data;
    int len;

    /*
     * If we've been put into a state other than THAWING since the
     * last callback, then we're done.
     */
    if (ps->frozen != THAWING)
        return;

    /*
     * Get some of the data we've buffered.
     */
    bufchain_prefix(&ps->inputdata, &data, &len);
    assert(len > 0);

    /*
     * Hand it off to the plug. Be careful of re-entrance - that might
     * have the effect of trying to close this socket.
     */
    ps->defer_close = TRUE;
    plug_receive(ps->plug, 0, data, len);
    bufchain_consume(&ps->inputdata, len);
    ps->defer_close = FALSE;
    if (ps->deferred_close) {
        sk_handle_close(ps);
        return;
    }

    if (bufchain_size(&ps->inputdata) > 0) {
        /*
         * If there's still data in our buffer, stay in THAWING state,
         * and reschedule ourself.
         */
        queue_toplevel_callback(handle_socket_unfreeze, ps);
    } else {
        /*
         * Otherwise, we've successfully thawed!
         */
        ps->frozen = UNFROZEN;
        handle_unthrottle(ps->recv_h, 0);
    }
}
Beispiel #9
0
void try_output(int is_stderr)
{
    bufchain *chain = (is_stderr ? &stderr_data : &stdout_data);
    int fd = (is_stderr ? 2 : 1);
    void *senddata;
    int sendlen, ret;

    if (bufchain_size(chain) == 0)
        return;

    bufchain_prefix(chain, &senddata, &sendlen);
    ret = write(fd, senddata, sendlen);
    if (ret > 0)
	bufchain_consume(chain, ret);
    else if (ret < 0) {
	perror(is_stderr ? "stderr: write" : "stdout: write");
	exit(1);
    }
}
Beispiel #10
0
static void handle_socket_unfreeze(void *psv)
{
    Handle_Socket ps = (Handle_Socket) psv;
    void *data;
    int len, new_backlog;

    /*
     * If we've been put into a state other than THAWING since the
     * last callback, then we're done.
     */
    if (ps->frozen != THAWING)
        return;

    /*
     * Get some of the data we've buffered.
     */
    bufchain_prefix(&ps->inputdata, &data, &len);
    assert(len > 0);

    /*
     * Hand it off to the plug.
     */
    new_backlog = plug_receive(ps->plug, 0, data, len);

    if (bufchain_size(&ps->inputdata) > 0) {
        /*
         * If there's still data in our buffer, stay in THAWING state,
         * and reschedule ourself.
         */
        queue_toplevel_callback(handle_socket_unfreeze, ps);
    } else {
        /*
         * Otherwise, we've successfully thawed!
         */
        ps->frozen = UNFROZEN;
        handle_unthrottle(ps->recv_h, new_backlog);
    }
}
Beispiel #11
0
static int localproxy_select_result(int fd, int event)
{
    Local_Proxy_Socket s;
    char buf[20480];
    int ret;

    if (!(s = find234(localproxy_by_fromfd, &fd, localproxy_fromfd_find)) &&
	!(s = find234(localproxy_by_fromfd, &fd, localproxy_errfd_find)) &&
	!(s = find234(localproxy_by_tofd, &fd, localproxy_tofd_find)) )
	return 1;		       /* boggle */

    if (event == 1) {
        if (fd == s->cmd_err) {
            ret = read(fd, buf, sizeof(buf));
            if (ret > 0)
                log_proxy_stderr(s->plug, &s->pending_error_data, buf, ret);
        } else {
            assert(fd == s->from_cmd);
            ret = read(fd, buf, sizeof(buf));
            if (ret < 0) {
                return plug_closing(s->plug, strerror(errno), errno, 0);
            } else if (ret == 0) {
                return plug_closing(s->plug, NULL, 0, 0);
            } else {
                return plug_receive(s->plug, 0, buf, ret);
            }
        }
    } else if (event == 2) {
	assert(fd == s->to_cmd);
	if (localproxy_try_send(s))
	    plug_sent(s->plug, bufchain_size(&s->pending_output_data));
	return 1;
    }

    return 1;
}
Beispiel #12
0
int handle_backlog(struct handle *h)
{
    assert(h->type == HT_OUTPUT);
    return bufchain_size(&h->u.o.queued_data);
}
Beispiel #13
0
void handle_got_event(HANDLE event)
#endif
{
    struct handle *h;

    assert(handles_by_evtomain);
    h = find234(handles_by_evtomain, &event, handle_find_evtomain);
    if (!h) {
	/*
	 * This isn't an error condition. If two or more event
	 * objects were signalled during the same select operation,
	 * and processing of the first caused the second handle to
	 * be closed, then it will sometimes happen that we receive
	 * an event notification here for a handle which is already
	 * deceased. In that situation we simply do nothing.
	 */
    #ifdef MPEXT
    return 0;
    #else
	return;
    #endif
    }

    if (h->u.g.moribund) {
	/*
	 * A moribund handle is one which we have either already
	 * signalled to die, or are waiting until its current I/O op
	 * completes to do so. Either way, it's treated as already
	 * dead from the external user's point of view, so we ignore
	 * the actual I/O result. We just signal the thread to die if
	 * we haven't yet done so, or destroy the handle if not.
	 */
	if (h->u.g.done) {
	    handle_destroy(h);
	} else {
	    h->u.g.done = TRUE;
	    h->u.g.busy = TRUE;
	    SetEvent(h->u.g.ev_from_main);
	}
    #ifdef MPEXT
    return 0;
    #else
	return;
    #endif
    }

    switch (h->type) {
	int backlog;

      case HT_INPUT:
	h->u.i.busy = FALSE;

	/*
	 * A signal on an input handle means data has arrived.
	 */
	if (h->u.i.len == 0) {
	    /*
	     * EOF, or (nearly equivalently) read error.
	     */
	    h->u.i.defunct = TRUE;
	    h->u.i.gotdata(h, NULL, -h->u.i.readerr);
	} else {
	    backlog = h->u.i.gotdata(h, h->u.i.buffer, h->u.i.len);
	    handle_throttle(&h->u.i, backlog);
	}
    #ifdef MPEXT
        return 1;
    #else
        break;
    #endif

      case HT_OUTPUT:
	h->u.o.busy = FALSE;

	/*
	 * A signal on an output handle means we have completed a
	 * write. Call the callback to indicate that the output
	 * buffer size has decreased, or to indicate an error.
	 */
	if (h->u.o.writeerr) {
	    /*
	     * Write error. Send a negative value to the callback,
	     * and mark the thread as defunct (because the output
	     * thread is terminating by now).
	     */
	    h->u.o.defunct = TRUE;
	    h->u.o.sentdata(h, -h->u.o.writeerr);
	} else {
	    bufchain_consume(&h->u.o.queued_data, h->u.o.lenwritten);
	    h->u.o.sentdata(h, bufchain_size(&h->u.o.queued_data));
	    handle_try_output(&h->u.o);
	}
    #ifdef MPEXT
        return 0;
    #else
        break;
    #endif

      case HT_FOREIGN:
        /* Just call the callback. */
        h->u.f.callback(h->u.f.ctx);
    #ifdef MPEXT
        return 0;
    #else
        break;
    #endif
    }
#ifdef MPEXT
    return 0;
#endif
}
Beispiel #14
0
int proxy_socks5_handlechap(Proxy_Socket p)
{

  /* CHAP authentication reply format:
   *  version number (1 bytes) = 1
   *  number of commands (1 byte)
   *
   * For each command:
   *  command identifier (1 byte)
   *  data length (1 byte)
   */
  unsigned char data[260];
  unsigned char outbuf[20];

  while (p->chap_num_attributes == 0 ||
         p->chap_num_attributes_processed < p->chap_num_attributes) {
    if (p->chap_num_attributes == 0 || p->chap_current_attribute == -1) {
      /* CHAP normally reads in two bytes, either at the
       * beginning or for each attribute/value pair.  But if
       * we're waiting for the value's data, we might not want
       * to read 2 bytes.
       */

      if (bufchain_size(&p->pending_input_data) < 2)
        return 1; /* not got anything yet */

      /* get the response */
      bufchain_fetch(&p->pending_input_data, data, 2);
      bufchain_consume(&p->pending_input_data, 2);
    }

    if (p->chap_num_attributes == 0) {
      /* If there are no attributes, this is our first msg
       * with the server, where we negotiate version and
       * number of attributes
       */
      if (data[0] != 0x01) {
        plug_closing(p->plug,
                     "Proxy error: SOCKS proxy wants"
                     " a different CHAP version",
                     PROXY_ERROR_GENERAL,
                     0);
        return 1;
      }
      if (data[1] == 0x00) {
        plug_closing(p->plug,
                     "Proxy error: SOCKS proxy won't"
                     " negotiate CHAP with us",
                     PROXY_ERROR_GENERAL,
                     0);
        return 1;
      }
      p->chap_num_attributes = data[1];
    } else {
      if (p->chap_current_attribute == -1) {
        /* We have to read in each attribute/value pair -
         * those we don't understand can be ignored, but
         * there are a few we'll need to handle.
         */
        p->chap_current_attribute = data[0];
        p->chap_current_datalen = data[1];
      }
      if (bufchain_size(&p->pending_input_data) < p->chap_current_datalen)
        return 1; /* not got everything yet */

      /* get the response */
      bufchain_fetch(&p->pending_input_data, data, p->chap_current_datalen);

      bufchain_consume(&p->pending_input_data, p->chap_current_datalen);

      switch (p->chap_current_attribute) {
      case 0x00:
        /* Successful authentication */
        if (data[0] == 0x00)
          p->state = 2;
        else {
          plug_closing(p->plug,
                       "Proxy error: SOCKS proxy"
                       " refused CHAP authentication",
                       PROXY_ERROR_GENERAL,
                       0);
          return 1;
        }
        break;
      case 0x03:
        outbuf[0] = 0x01; /* Version */
        outbuf[1] = 0x01; /* One attribute */
        outbuf[2] = 0x04; /* Response */
        outbuf[3] = 0x10; /* Length */
        hmacmd5_chap(data,
                     p->chap_current_datalen,
                     conf_get_str(p->conf, CONF_proxy_password),
                     &outbuf[4]);
        sk_write(p->sub_socket, (char *)outbuf, 20);
        break;
      case 0x11:
        /* Chose a protocol */
        if (data[0] != 0x85) {
          plug_closing(p->plug,
                       "Proxy error: Server chose "
                       "CHAP of other than HMAC-MD5 but we "
                       "didn't offer it!",
                       PROXY_ERROR_GENERAL,
                       0);
          return 1;
        }
        break;
      }
      p->chap_current_attribute = -1;
      p->chap_num_attributes_processed++;
    }
    if (p->state == 8 &&
        p->chap_num_attributes_processed >= p->chap_num_attributes) {
      p->chap_num_attributes = 0;
      p->chap_num_attributes_processed = 0;
      p->chap_current_datalen = 0;
    }
  }
  return 0;
}
Beispiel #15
0
int main(int argc, char **argv)
{
  int masterr, masterw, slaver, slavew, pid;
  char ptyname[FILENAME_MAX];
  bufchain tochild, tostdout;
  int tochild_active, tostdout_active, fromstdin_active, fromchild_active;
  int exitcode = -1;
  pid_t childpid = -1;

        --argc, ++argv;         /* point at argument after "--" */

  /*
   * Allocate the pipe for transmitting signals back to the
   * top-level select loop.
   */
  if (pipe(signalpipe) < 0) {
    perror("pipe");
    return 1;
  }

  /*
   * Now that pipe exists, we can set up the SIGCHLD handler to
   * write to one end of it. We needn't already know details like
   * which child pid we're waiting for, because we don't need that
   * until we respond to reading the far end of the pipe in the
   * main select loop.
   */
  signal(SIGCHLD, sigchld);

  /*
   * Allocate the pty or pipes.
   */
  masterr = pty_get(ptyname);
  masterw = dup(masterr);
  slaver = open(ptyname, O_RDWR);
  slavew = dup(slaver);
  if (slaver < 0) {
    perror("slave pty: open");
    return 1;
  }

  bufchain_init(&tochild);
  bufchain_init(&tostdout);
  tochild_active = tostdout_active = TRUE;
  fromchild_active = fromstdin_active = TRUE;

  /*
   * Fork and execute the command.
   */
  pid = fork();
  if (pid < 0) {
    perror("fork");
    return 1;
  }

  if (pid == 0) {
    int i;
    /*
     * We are the child.
     */
    close(masterr);
    close(masterw);

    fcntl(slaver, F_SETFD, 0);    /* don't close on exec */
    fcntl(slavew, F_SETFD, 0);    /* don't close on exec */
    close(0);
    dup2(slaver, 0);
    close(1);
    dup2(slavew, 1);
    int fd;
    close(2);
    dup2(slavew, 2);
    setsid();
    setpgid(0, 0);
    tcsetpgrp(0, getpgrp());
    if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
      ioctl(fd, TIOCNOTTY);
      close(fd);
    }
    ioctl(slavew, TIOCSCTTY);
    
    /* Close everything _else_, for tidiness. */
    for (i = 3; i < 1024; i++)
      close(i);
    if (argc > 0) {
      execvp(argv[0], argv);     /* assumes argv has trailing NULL */
    } else {
      execl(getenv("SHELL"), getenv("SHELL"), NULL);
    }
    /*
     * If we're here, exec has gone badly foom.
     */
    perror("exec");
    exit(127);
  }

  /*
   * Now we're the parent. Close the slave fds and start copying
   * stuff back and forth.
   */
  close(slaver);
  close(slavew);
  childpid = pid;

  tcgetattr(0, &oldattrs);
  newattrs = oldattrs;
  newattrs.c_iflag &= ~(IXON | IXOFF | ICRNL | INLCR);
  newattrs.c_oflag &= ~(ONLCR | OCRNL);
  newattrs.c_lflag &= ~(ISIG | ICANON | ECHO);
  atexit(attrsonexit);
  tcsetattr(0, TCSADRAIN, &newattrs);
  

  while (1) {
    fd_set rset, wset;
    char buf[65536];
    int maxfd, ret;

    FD_ZERO(&rset);
    FD_ZERO(&wset);
    maxfd = 0;

    FD_SET(signalpipe[0], &rset);
    maxfd = max(signalpipe[0]+1, maxfd);

    if (tochild_active && bufchain_size(&tochild)) {
      FD_SET(masterw, &wset);
      maxfd = max(masterw+1, maxfd);
    }
    if (tostdout_active && bufchain_size(&tostdout)) {
      FD_SET(1, &wset);
      maxfd = max(1+1, maxfd);
    }
    if (fromstdin_active && bufchain_size(&tochild) < LOCALBUF_LIMIT) {
      FD_SET(0, &rset);
      maxfd = max(0+1, maxfd);
    }
    if (fromchild_active && bufchain_size(&tostdout) < LOCALBUF_LIMIT) {
      FD_SET(masterr, &rset);
      maxfd = max(masterr+1, maxfd);
    }

    do {
      ret = select(maxfd, &rset, &wset, NULL, NULL);
    } while (ret < 0 && (errno == EINTR || errno == EAGAIN));

    if (ret < 0) {
      perror("select");
      return 1;
    }

    if (FD_ISSET(masterr, &rset)) {

      if (FD_ISSET(masterr, &rset)) {
        ret = read(masterr, buf, sizeof(buf));
        if (ret <= 0) {
          /*
           * EIO from a pty master just means end of
           * file, annoyingly. Why can't it report
           * ordinary EOF?
           */
          if (errno == EIO)
            ret = 0;
          if (ret < 0) {
            perror("child process: read");
          }
          close(masterr);
          fromchild_active = FALSE;
          ret = 0;
        }
      } else
        ret = 0;

      if (ret) {
        bufchain_add(&tostdout, buf, ret);
      }
    }
    if (FD_ISSET(0, &rset)) {

      if (FD_ISSET(0, &rset)) {
        ret = read(0, buf, sizeof(buf));
        if (ret <= 0) {
          if (ret < 0) {
            perror("stdin: read");
          }
          close(0);
          fromstdin_active = FALSE;
          ret = 0;
        }
      } else
        ret = 0;

      if (ret) {
        bufchain_add(&tochild, buf, ret);
      }
    }
    if (FD_ISSET(1, &wset)) {
      void *data;
      int len, ret;
      bufchain_prefix(&tostdout, &data, &len);
      if ((ret = write(1, data, len)) < 0) {
        perror("stdout: write");
        close(1);
        close(masterr);
        tostdout_active = fromchild_active = FALSE;
      } else
        bufchain_consume(&tostdout, ret);
    }
    if (FD_ISSET(masterw, &wset)) {
      void *data;
      int len;
      bufchain_prefix(&tochild, &data, &len);
      if ((ret = write(masterw, data, len)) < 0) {
        perror("child process: write");
        close(0);
        close(masterw);
        tochild_active = fromstdin_active = FALSE;
      } else
        bufchain_consume(&tochild, ret);
    }
    if (FD_ISSET(signalpipe[0], &rset)) {
      ret = read(signalpipe[0], buf, 1);
      if (ret == 1 && buf[0] == 'C') {
        int pid, code;
        pid = wait(&code);     /* reap the exit code */
        if (pid == childpid)
          exitcode = code;
      }
    }

    /*
     * If there can be no further data from a direction (the
     * input fd has been closed and the buffered data is used
     * up) but its output fd is still open, close it.
     */
    if (!fromstdin_active && !bufchain_size(&tochild) && tochild_active) {
      tochild_active = FALSE;
      close(masterw);
    }
    if (!fromchild_active && !bufchain_size(&tostdout) && tostdout_active){
      tostdout_active = FALSE;
      close(1);
    }

    /*
     * Termination condition with pipes is that there's still
     * data flowing in at least one direction.
     * 
     * Termination condition for a pty-based run is that the
     * child process hasn't yet terminated and/or there is
     * still buffered data to send.
     */
    if (exitcode < 0)
      /* process is still active */;
    else if (tochild_active && bufchain_size(&tochild))
      /* data still to be sent to child's children */;
    else if (tostdout_active && bufchain_size(&tostdout))
      /* data still to be sent to stdout */;
    else
      break;           /* terminate */
  }

  close(masterw);
  close(masterr);

  if (exitcode < 0) {
    int pid, code;
    pid = wait(&code);
    exitcode = code;
  }

  return (WIFEXITED(exitcode) ? WEXITSTATUS(exitcode) :
      128 | WTERMSIG(exitcode));
}
Beispiel #16
0
/*
 * Called to query the current sendability status.
 */
static int serial_sendbuffer(void *handle)
{
    Serial serial = (Serial) handle;
    return bufchain_size(&serial->output_data);
}
Beispiel #17
0
static void logfopen_callback(void *handle, int mode)
{
    struct LogContext *ctx = (struct LogContext *)handle;
    char buf[256], *event;
    struct tm tm;
    const char *fmode;
    int shout = FALSE;

    if (mode == 0) {
	ctx->state = L_ERROR;	       /* disable logging */
    } else {
	fmode = (mode == 1 ? "ab" : "wb");
	ctx->lgfp = f_open(ctx->currlogfilename, fmode, FALSE);
	if (ctx->lgfp) {
	    ctx->state = L_OPEN;
        } else {
	    ctx->state = L_ERROR;
            shout = TRUE;
        }
    }

    if (ctx->state == L_OPEN) {
	/* Write header line into log file. */
	tm = ltime();
	strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
	logprintf(ctx, "=~=~=~=~=~=~=~=~=~=~=~= PuTTY log %s"
		  " =~=~=~=~=~=~=~=~=~=~=~=\r\n", buf);
    }

    event = dupprintf(MPEXT_BOM "%s session log (%s mode) to file: %s",
		      ctx->state == L_ERROR ?
		      (mode == 0 ? "Disabled writing" : "Error writing") :
		      (mode == 1 ? "Appending" : "Writing new"),
		      (ctx->logtype == LGTYP_ASCII ? "ASCII" :
		       ctx->logtype == LGTYP_DEBUG ? "raw" :
		       ctx->logtype == LGTYP_PACKETS ? "SSH packets" :
		       ctx->logtype == LGTYP_SSHRAW ? "SSH raw data" :
		       "unknown"),
		      filename_to_str(ctx->currlogfilename));
    logevent(ctx->frontend, event);
    if (shout) {
        /*
         * If we failed to open the log file due to filesystem error
         * (as opposed to user action such as clicking Cancel in the
         * askappend box), we should log it more prominently. We do
         * this by sending it to the same place that stderr output
         * from the main session goes (so, either a console tool's
         * actual stderr, or a terminal window).
         *
         * Of course this is one case in which that policy won't cause
         * it to turn up embarrassingly in a log file of real server
         * output, because the whole point is that we haven't managed
         * to open any such log file :-)
         */
        from_backend(ctx->frontend, 1, event, strlen(event));
        from_backend(ctx->frontend, 1, "\r\n", 2);
    }
    sfree(event);

    /*
     * Having either succeeded or failed in opening the log file,
     * we should write any queued data out.
     */
    assert(ctx->state != L_OPENING);   /* make _sure_ it won't be requeued */
    while (bufchain_size(&ctx->queue)) {
	void *data;
	int len;
	bufchain_prefix(&ctx->queue, &data, &len);
	logwrite(ctx, data, len);
	bufchain_consume(&ctx->queue, len);
    }
}
Beispiel #18
0
int main(int argc, char **argv)
{
    bool sending;
    int *fdlist;
    int fd;
    int i, fdstate;
    size_t fdsize;
    int exitcode;
    bool errors;
    enum TriState sanitise_stdout = AUTO, sanitise_stderr = AUTO;
    bool use_subsystem = false;
    bool just_test_share_exists = false;
    unsigned long now;
    struct winsize size;
    const struct BackendVtable *backvt;

    fdlist = NULL;
    fdsize = 0;
    /*
     * Initialise port and protocol to sensible defaults. (These
     * will be overridden by more or less anything.)
     */
    default_protocol = PROT_SSH;
    default_port = 22;

    bufchain_init(&stdout_data);
    bufchain_init(&stderr_data);
    bufchain_sink_init(&stdout_bcs, &stdout_data);
    bufchain_sink_init(&stderr_bcs, &stderr_data);
    stdout_bs = BinarySink_UPCAST(&stdout_bcs);
    stderr_bs = BinarySink_UPCAST(&stderr_bcs);
    outgoingeof = EOF_NO;

    flags = FLAG_STDERR_TTY;
    cmdline_tooltype |=
        (TOOLTYPE_HOST_ARG |
         TOOLTYPE_HOST_ARG_CAN_BE_SESSION |
         TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX |
         TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD);

    stderr_tty_init();
    /*
     * Process the command line.
     */
    conf = conf_new();
    do_defaults(NULL, conf);
    loaded_session = false;
    default_protocol = conf_get_int(conf, CONF_protocol);
    default_port = conf_get_int(conf, CONF_port);
    errors = false;
    {
	/*
	 * Override the default protocol if PLINK_PROTOCOL is set.
	 */
	char *p = getenv("PLINK_PROTOCOL");
	if (p) {
            const struct BackendVtable *vt = backend_vt_from_name(p);
            if (vt) {
                default_protocol = vt->protocol;
                default_port = vt->default_port;
		conf_set_int(conf, CONF_protocol, default_protocol);
		conf_set_int(conf, CONF_port, default_port);
	    }
	}
    }
    while (--argc) {
	char *p = *++argv;
        int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
                                        1, conf);
        if (ret == -2) {
            fprintf(stderr,
                    "plink: option \"%s\" requires an argument\n", p);
            errors = true;
        } else if (ret == 2) {
            --argc, ++argv;
        } else if (ret == 1) {
            continue;
        } else if (!strcmp(p, "-batch")) {
            console_batch_mode = true;
        } else if (!strcmp(p, "-s")) {
            /* Save status to write to conf later. */
            use_subsystem = true;
        } else if (!strcmp(p, "-V") || !strcmp(p, "--version")) {
            version();
        } else if (!strcmp(p, "--help")) {
            usage();
            exit(0);
        } else if (!strcmp(p, "-pgpfp")) {
            pgp_fingerprints();
            exit(1);
        } else if (!strcmp(p, "-o")) {
            if (argc <= 1) {
                fprintf(stderr,
                        "plink: option \"-o\" requires an argument\n");
                errors = true;
            } else {
                --argc;
                /* Explicitly pass "plink" in place of appname for
                 * error reporting purposes. appname will have been
                 * set by be_foo.c to something more generic, probably
                 * "PuTTY". */
                provide_xrm_string(*++argv, "plink");
            }
        } else if (!strcmp(p, "-shareexists")) {
            just_test_share_exists = true;
        } else if (!strcmp(p, "-fuzznet")) {
            conf_set_int(conf, CONF_proxy_type, PROXY_FUZZ);
            conf_set_str(conf, CONF_proxy_telnet_command, "%host");
        } else if (!strcmp(p, "-sanitise-stdout") ||
                   !strcmp(p, "-sanitize-stdout")) {
            sanitise_stdout = FORCE_ON;
        } else if (!strcmp(p, "-no-sanitise-stdout") ||
                   !strcmp(p, "-no-sanitize-stdout")) {
            sanitise_stdout = FORCE_OFF;
        } else if (!strcmp(p, "-sanitise-stderr") ||
                   !strcmp(p, "-sanitize-stderr")) {
            sanitise_stderr = FORCE_ON;
        } else if (!strcmp(p, "-no-sanitise-stderr") ||
                   !strcmp(p, "-no-sanitize-stderr")) {
            sanitise_stderr = FORCE_OFF;
        } else if (!strcmp(p, "-no-antispoof")) {
            console_antispoof_prompt = false;
	} else if (*p != '-') {
            strbuf *cmdbuf = strbuf_new();

            while (argc > 0) {
                if (cmdbuf->len > 0)
                    put_byte(cmdbuf, ' '); /* add space separator */
                put_datapl(cmdbuf, ptrlen_from_asciz(p));
                if (--argc > 0)
                    p = *++argv;
            }

            conf_set_str(conf, CONF_remote_cmd, cmdbuf->s);
            conf_set_str(conf, CONF_remote_cmd2, "");
            conf_set_bool(conf, CONF_nopty, true);  /* command => no tty */

            strbuf_free(cmdbuf);
            break;		       /* done with cmdline */
        } else {
            fprintf(stderr, "plink: unknown option \"%s\"\n", p);
            errors = true;
	}
    }

    if (errors)
	return 1;

    if (!cmdline_host_ok(conf)) {
	usage();
    }

    prepare_session(conf);

    /*
     * Perform command-line overrides on session configuration.
     */
    cmdline_run_saved(conf);

    /*
     * If we have no better ideas for the remote username, use the local
     * one, as 'ssh' does.
     */
    if (conf_get_str(conf, CONF_username)[0] == '\0') {
	char *user = get_username();
	if (user) {
	    conf_set_str(conf, CONF_username, user);
	    sfree(user);
	}
    }

    /*
     * Apply subsystem status.
     */
    if (use_subsystem)
        conf_set_bool(conf, CONF_ssh_subsys, true);

    if (!*conf_get_str(conf, CONF_remote_cmd) &&
	!*conf_get_str(conf, CONF_remote_cmd2) &&
	!*conf_get_str(conf, CONF_ssh_nc_host))
	flags |= FLAG_INTERACTIVE;

    /*
     * Select protocol. This is farmed out into a table in a
     * separate file to enable an ssh-free variant.
     */
    backvt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol));
    if (!backvt) {
	fprintf(stderr,
		"Internal fault: Unsupported protocol found\n");
	return 1;
    }

    /*
     * Block SIGPIPE, so that we'll get EPIPE individually on
     * particular network connections that go wrong.
     */
    putty_signal(SIGPIPE, SIG_IGN);

    /*
     * Set up the pipe we'll use to tell us about SIGWINCH.
     */
    if (pipe(signalpipe) < 0) {
	perror("pipe");
	exit(1);
    }
    /* We don't want the signal handler to block if the pipe's full. */
    nonblock(signalpipe[0]);
    nonblock(signalpipe[1]);
    cloexec(signalpipe[0]);
    cloexec(signalpipe[1]);
    putty_signal(SIGWINCH, sigwinch);

    /*
     * Now that we've got the SIGWINCH handler installed, try to find
     * out the initial terminal size.
     */
    if (ioctl(STDIN_FILENO, TIOCGWINSZ, &size) >= 0) {
	conf_set_int(conf, CONF_width, size.ws_col);
	conf_set_int(conf, CONF_height, size.ws_row);
    }

    /*
     * Decide whether to sanitise control sequences out of standard
     * output and standard error.
     *
     * If we weren't given a command-line override, we do this if (a)
     * the fd in question is pointing at a terminal, and (b) we aren't
     * trying to allocate a terminal as part of the session.
     *
     * (Rationale: the risk of control sequences is that they cause
     * confusion when sent to a local terminal, so if there isn't one,
     * no problem. Also, if we allocate a remote terminal, then we
     * sent a terminal type, i.e. we told it what kind of escape
     * sequences we _like_, i.e. we were expecting to receive some.)
     */
    if (sanitise_stdout == FORCE_ON ||
        (sanitise_stdout == AUTO && isatty(STDOUT_FILENO) &&
         conf_get_bool(conf, CONF_nopty))) {
        stdout_scc = stripctrl_new(stdout_bs, true, L'\0');
        stdout_bs = BinarySink_UPCAST(stdout_scc);
    }
    if (sanitise_stderr == FORCE_ON ||
        (sanitise_stderr == AUTO && isatty(STDERR_FILENO) &&
         conf_get_bool(conf, CONF_nopty))) {
        stderr_scc = stripctrl_new(stderr_bs, true, L'\0');
        stderr_bs = BinarySink_UPCAST(stderr_scc);
    }

    sk_init();
    uxsel_init();

    /*
     * Plink doesn't provide any way to add forwardings after the
     * connection is set up, so if there are none now, we can safely set
     * the "simple" flag.
     */
    if (conf_get_int(conf, CONF_protocol) == PROT_SSH &&
	!conf_get_bool(conf, CONF_x11_forward) &&
	!conf_get_bool(conf, CONF_agentfwd) &&
	!conf_get_str_nthstrkey(conf, CONF_portfwd, 0))
	conf_set_bool(conf, CONF_ssh_simple, true);

    if (just_test_share_exists) {
        if (!backvt->test_for_upstream) {
            fprintf(stderr, "Connection sharing not supported for connection "
                    "type '%s'\n", backvt->name);
            return 1;
        }
        if (backvt->test_for_upstream(conf_get_str(conf, CONF_host),
                                      conf_get_int(conf, CONF_port), conf))
            return 0;
        else
            return 1;
    }

    /*
     * Start up the connection.
     */
    logctx = log_init(default_logpolicy, conf);
    {
	const char *error;
	char *realhost;
	/* nodelay is only useful if stdin is a terminal device */
	bool nodelay = conf_get_bool(conf, CONF_tcp_nodelay) && isatty(0);

	/* This is a good place for a fuzzer to fork us. */
#ifdef __AFL_HAVE_MANUAL_CONTROL
	__AFL_INIT();
#endif

        error = backend_init(backvt, plink_seat, &backend, logctx, conf,
                             conf_get_str(conf, CONF_host),
                             conf_get_int(conf, CONF_port),
                             &realhost, nodelay,
                             conf_get_bool(conf, CONF_tcp_keepalives));
	if (error) {
	    fprintf(stderr, "Unable to open connection:\n%s\n", error);
	    return 1;
	}
        ldisc_create(conf, NULL, backend, plink_seat);
	sfree(realhost);
    }

    /*
     * Set up the initial console mode. We don't care if this call
     * fails, because we know we aren't necessarily running in a
     * console.
     */
    local_tty = (tcgetattr(STDIN_FILENO, &orig_termios) == 0);
    atexit(cleanup_termios);
    seat_echoedit_update(plink_seat, 1, 1);
    sending = false;
    now = GETTICKCOUNT();

    pollwrapper *pw = pollwrap_new();

    while (1) {
	int rwx;
	int ret;
        unsigned long next;

        pollwrap_clear(pw);

	pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R);

	if (!sending &&
            backend_connected(backend) &&
            backend_sendok(backend) &&
            backend_sendbuffer(backend) < MAX_STDIN_BACKLOG) {
	    /* If we're OK to send, then try to read from stdin. */
            pollwrap_add_fd_rwx(pw, STDIN_FILENO, SELECT_R);
	}

	if (bufchain_size(&stdout_data) > 0) {
	    /* If we have data for stdout, try to write to stdout. */
            pollwrap_add_fd_rwx(pw, STDOUT_FILENO, SELECT_W);
	}

	if (bufchain_size(&stderr_data) > 0) {
	    /* If we have data for stderr, try to write to stderr. */
            pollwrap_add_fd_rwx(pw, STDERR_FILENO, SELECT_W);
	}

	/* Count the currently active fds. */
	i = 0;
	for (fd = first_fd(&fdstate, &rwx); fd >= 0;
	     fd = next_fd(&fdstate, &rwx)) i++;

	/* Expand the fdlist buffer if necessary. */
        sgrowarray(fdlist, fdsize, i);

	/*
	 * Add all currently open fds to pw, and store them in fdlist
	 * as well.
	 */
	int fdcount = 0;
	for (fd = first_fd(&fdstate, &rwx); fd >= 0;
	     fd = next_fd(&fdstate, &rwx)) {
	    fdlist[fdcount++] = fd;
            pollwrap_add_fd_rwx(pw, fd, rwx);
	}

        if (toplevel_callback_pending()) {
            ret = pollwrap_poll_instant(pw);
        } else if (run_timers(now, &next)) {
            do {
                unsigned long then;
                long ticks;

		then = now;
		now = GETTICKCOUNT();
		if (now - then > next - then)
		    ticks = 0;
		else
		    ticks = next - now;

                bool overflow = false;
                if (ticks > INT_MAX) {
                    ticks = INT_MAX;
                    overflow = true;
                }

                ret = pollwrap_poll_timeout(pw, ticks);
                if (ret == 0 && !overflow)
                    now = next;
                else
                    now = GETTICKCOUNT();
            } while (ret < 0 && errno == EINTR);
        } else {
            ret = pollwrap_poll_endless(pw);
        }

        if (ret < 0 && errno == EINTR)
            continue;

	if (ret < 0) {
	    perror("poll");
	    exit(1);
	}

	for (i = 0; i < fdcount; i++) {
	    fd = fdlist[i];
            int rwx = pollwrap_get_fd_rwx(pw, fd);
            /*
             * We must process exceptional notifications before
             * ordinary readability ones, or we may go straight
             * past the urgent marker.
             */
	    if (rwx & SELECT_X)
		select_result(fd, SELECT_X);
	    if (rwx & SELECT_R)
		select_result(fd, SELECT_R);
	    if (rwx & SELECT_W)
		select_result(fd, SELECT_W);
	}

	if (pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) {
	    char c[1];
	    struct winsize size;
	    if (read(signalpipe[0], c, 1) <= 0)
		/* ignore error */;
	    /* ignore its value; it'll be `x' */
	    if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0)
                backend_size(backend, size.ws_col, size.ws_row);
	}

	if (pollwrap_check_fd_rwx(pw, STDIN_FILENO, SELECT_R)) {
	    char buf[4096];
	    int ret;

            if (backend_connected(backend)) {
		ret = read(STDIN_FILENO, buf, sizeof(buf));
                noise_ultralight(NOISE_SOURCE_IOLEN, ret);
		if (ret < 0) {
		    perror("stdin: read");
		    exit(1);
		} else if (ret == 0) {
                    backend_special(backend, SS_EOF, 0);
		    sending = false;   /* send nothing further after this */
		} else {
		    if (local_tty)
			from_tty(buf, ret);
		    else
                        backend_send(backend, buf, ret);
		}
	    }
	}

	if (pollwrap_check_fd_rwx(pw, STDOUT_FILENO, SELECT_W)) {
            backend_unthrottle(backend, try_output(false));
	}

	if (pollwrap_check_fd_rwx(pw, STDERR_FILENO, SELECT_W)) {
            backend_unthrottle(backend, try_output(true));
	}

        run_toplevel_callbacks();

        if (!backend_connected(backend) &&
	    bufchain_size(&stdout_data) == 0 &&
	    bufchain_size(&stderr_data) == 0)
	    break;		       /* we closed the connection */
    }
    exitcode = backend_exitcode(backend);
    if (exitcode < 0) {
	fprintf(stderr, "Remote process exit code unavailable\n");
	exitcode = 1;		       /* this is an error condition */
    }
    cleanup_exit(exitcode);
    return exitcode;		       /* shouldn't happen, but placates gcc */
}
Beispiel #19
0
int handle_backlog(struct handle *h)
{
    assert(h->output);
    return bufchain_size(&h->u.o.queued_data);
}
Beispiel #20
0
void handle_got_event(HANDLE event)
{
    struct handle *h;

    assert(handles_by_evtomain);
    h = find234(handles_by_evtomain, &event, handle_find_evtomain);
    if (!h) {
	/*
	 * This isn't an error condition. If two or more event
	 * objects were signalled during the same select operation,
	 * and processing of the first caused the second handle to
	 * be closed, then it will sometimes happen that we receive
	 * an event notification here for a handle which is already
	 * deceased. In that situation we simply do nothing.
	 */
	return;
    }

    if (h->u.g.moribund) {
	/*
	 * A moribund handle is already treated as dead from the
	 * external user's point of view, so do nothing with the
	 * actual event. Just signal the thread to die if
	 * necessary, or destroy the handle if not.
	 */
	if (h->u.g.done) {
	    handle_destroy(h);
	} else {
	    h->u.g.done = TRUE;
	    h->u.g.busy = TRUE;
	    SetEvent(h->u.g.ev_from_main);
	}
	return;
    }

    if (!h->output) {
	int backlog;

	h->u.i.busy = FALSE;

	/*
	 * A signal on an input handle means data has arrived.
	 */
	if (h->u.i.len == 0) {
	    /*
	     * EOF, or (nearly equivalently) read error.
	     */
	    h->u.i.gotdata(h, NULL, -h->u.i.readerr);
	    h->u.i.defunct = TRUE;
	} else {
	    backlog = h->u.i.gotdata(h, h->u.i.buffer, h->u.i.len);
	    handle_throttle(&h->u.i, backlog);
	}
    } else {
	h->u.o.busy = FALSE;

	/*
	 * A signal on an output handle means we have completed a
	 * write. Call the callback to indicate that the output
	 * buffer size has decreased, or to indicate an error.
	 */
	if (h->u.o.writeerr) {
	    /*
	     * Write error. Send a negative value to the callback,
	     * and mark the thread as defunct (because the output
	     * thread is terminating by now).
	     */
	    h->u.o.sentdata(h, -h->u.o.writeerr);
	    h->u.o.defunct = TRUE;
	} else {
	    bufchain_consume(&h->u.o.queued_data, h->u.o.lenwritten);
	    h->u.o.sentdata(h, bufchain_size(&h->u.o.queued_data));
	    handle_try_output(&h->u.o);
	}
    }
}