⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uxplink.c

📁 putty
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 * PLink - a command-line (stdin/stdout) variant of PuTTY.
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <stdarg.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <pwd.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#ifndef HAVE_NO_SYS_SELECT_H
#include <sys/select.h>
#endif

#define PUTTY_DO_GLOBALS	       /* actually _define_ globals */
#include "putty.h"
#include "storage.h"
#include "tree234.h"

#define MAX_STDIN_BACKLOG 4096

void *logctx;

void fatalbox(char *p, ...)
{
    va_list ap;
    fprintf(stderr, "FATAL ERROR: ");
    va_start(ap, p);
    vfprintf(stderr, p, ap);
    va_end(ap);
    fputc('\n', stderr);
    if (logctx) {
        log_free(logctx);
        logctx = NULL;
    }
    cleanup_exit(1);
}
void modalfatalbox(char *p, ...)
{
    va_list ap;
    fprintf(stderr, "FATAL ERROR: ");
    va_start(ap, p);
    vfprintf(stderr, p, ap);
    va_end(ap);
    fputc('\n', stderr);
    if (logctx) {
        log_free(logctx);
        logctx = NULL;
    }
    cleanup_exit(1);
}
void connection_fatal(void *frontend, char *p, ...)
{
    va_list ap;
    fprintf(stderr, "FATAL ERROR: ");
    va_start(ap, p);
    vfprintf(stderr, p, ap);
    va_end(ap);
    fputc('\n', stderr);
    if (logctx) {
        log_free(logctx);
        logctx = NULL;
    }
    cleanup_exit(1);
}
void cmdline_error(char *p, ...)
{
    va_list ap;
    fprintf(stderr, "plink: ");
    va_start(ap, p);
    vfprintf(stderr, p, ap);
    va_end(ap);
    fputc('\n', stderr);
    exit(1);
}

static int local_tty = 0; /* do we have a local tty? */
static struct termios orig_termios;

static Backend *back;
static void *backhandle;
static Config cfg;

/*
 * Default settings that are specific to pterm.
 */
char *platform_default_s(const char *name)
{
    if (!strcmp(name, "TermType"))
	return dupstr(getenv("TERM"));
    if (!strcmp(name, "UserName"))
	return get_username();
    if (!strcmp(name, "SerialLine"))
	return dupstr("/dev/ttyS0");
    return NULL;
}

int platform_default_i(const char *name, int def)
{
    if (!strcmp(name, "TermWidth") ||
	!strcmp(name, "TermHeight")) {
	struct winsize size;
	if (ioctl(0, TIOCGWINSZ, (void *)&size) >= 0)
	    return (!strcmp(name, "TermWidth") ? size.ws_col : size.ws_row);
    }
    return def;
}

FontSpec platform_default_fontspec(const char *name)
{
    FontSpec ret;
    *ret.name = '\0';
    return ret;
}

Filename platform_default_filename(const char *name)
{
    Filename ret;
    if (!strcmp(name, "LogFileName"))
	strcpy(ret.path, "putty.log");
    else
	*ret.path = '\0';
    return ret;
}

char *x_get_default(const char *key)
{
    return NULL;		       /* this is a stub */
}
int term_ldisc(Terminal *term, int mode)
{
    return FALSE;
}
void ldisc_update(void *frontend, int echo, int edit)
{
    /* Update stdin read mode to reflect changes in line discipline. */
    struct termios mode;

    if (!local_tty) return;

    mode = orig_termios;

    if (echo)
	mode.c_lflag |= ECHO;
    else
	mode.c_lflag &= ~ECHO;

    if (edit) {
	mode.c_iflag |= ICRNL;
	mode.c_lflag |= ISIG | ICANON;
	mode.c_oflag |= OPOST;
    } else {
	mode.c_iflag &= ~ICRNL;
	mode.c_lflag &= ~(ISIG | ICANON);
	mode.c_oflag &= ~OPOST;
	/* Solaris sets these to unhelpful values */
	mode.c_cc[VMIN] = 1;
	mode.c_cc[VTIME] = 0;
	/* FIXME: perhaps what we do with IXON/IXOFF should be an
	 * argument to ldisc_update(), to allow implementation of SSH-2
	 * "xon-xoff" and Rlogin's equivalent? */
	mode.c_iflag &= ~IXON;
	mode.c_iflag &= ~IXOFF;
    }
    /* 
     * Mark parity errors and (more important) BREAK on input.  This
     * is more complex than it need be because POSIX-2001 suggests
     * that escaping of valid 0xff in the input stream is dependent on
     * IGNPAR being clear even though marking of BREAK isn't.  NetBSD
     * 2.0 goes one worse and makes it dependent on INPCK too.  We
     * deal with this by forcing these flags into a useful state and
     * then faking the state in which we found them in from_tty() if
     * we get passed a parity or framing error.
     */
    mode.c_iflag = (mode.c_iflag | INPCK | PARMRK) & ~IGNPAR;

    tcsetattr(0, TCSANOW, &mode);
}

/* Helper function to extract a special character from a termios. */
static char *get_ttychar(struct termios *t, int index)
{
    cc_t c = t->c_cc[index];
#if defined(_POSIX_VDISABLE)
    if (c == _POSIX_VDISABLE)
	return dupprintf("");
#endif
    return dupprintf("^<%d>", c);
}

char *get_ttymode(void *frontend, const char *mode)
{
    /*
     * Propagate appropriate terminal modes from the local terminal,
     * if any.
     */
    if (!local_tty) return NULL;

#define GET_CHAR(ourname, uxname) \
    do { \
	if (strcmp(mode, ourname) == 0) \
	    return get_ttychar(&orig_termios, uxname); \
    } while(0)
#define GET_BOOL(ourname, uxname, uxmemb, transform) \
    do { \
	if (strcmp(mode, ourname) == 0) { \
	    int b = (orig_termios.uxmemb & uxname) != 0; \
	    transform; \
	    return dupprintf("%d", b); \
	} \
    } while (0)

    /*
     * Modes that want to be the same on all terminal devices involved.
     */
    /* All the special characters supported by SSH */
#if defined(VINTR)
    GET_CHAR("INTR", VINTR);
#endif
#if defined(VQUIT)
    GET_CHAR("QUIT", VQUIT);
#endif
#if defined(VERASE)
    GET_CHAR("ERASE", VERASE);
#endif
#if defined(VKILL)
    GET_CHAR("KILL", VKILL);
#endif
#if defined(VEOF)
    GET_CHAR("EOF", VEOF);
#endif
#if defined(VEOL)
    GET_CHAR("EOL", VEOL);
#endif
#if defined(VEOL2)
    GET_CHAR("EOL2", VEOL2);
#endif
#if defined(VSTART)
    GET_CHAR("START", VSTART);
#endif
#if defined(VSTOP)
    GET_CHAR("STOP", VSTOP);
#endif
#if defined(VSUSP)
    GET_CHAR("SUSP", VSUSP);
#endif
#if defined(VDSUSP)
    GET_CHAR("DSUSP", VDSUSP);
#endif
#if defined(VREPRINT)
    GET_CHAR("REPRINT", VREPRINT);
#endif
#if defined(VWERASE)
    GET_CHAR("WERASE", VWERASE);
#endif
#if defined(VLNEXT)
    GET_CHAR("LNEXT", VLNEXT);
#endif
#if defined(VFLUSH)
    GET_CHAR("FLUSH", VFLUSH);
#endif
#if defined(VSWTCH)
    GET_CHAR("SWTCH", VSWTCH);
#endif
#if defined(VSTATUS)
    GET_CHAR("STATUS", VSTATUS);
#endif
#if defined(VDISCARD)
    GET_CHAR("DISCARD", VDISCARD);
#endif
    /* Modes that "configure" other major modes. These should probably be
     * considered as user preferences. */
    /* Configuration of ICANON */
#if defined(ECHOK)
    GET_BOOL("ECHOK", ECHOK, c_lflag, );
#endif
#if defined(ECHOKE)
    GET_BOOL("ECHOKE", ECHOKE, c_lflag, );
#endif
#if defined(ECHOE)
    GET_BOOL("ECHOE", ECHOE, c_lflag, );
#endif
#if defined(ECHONL)
    GET_BOOL("ECHONL", ECHONL, c_lflag, );
#endif
#if defined(XCASE)
    GET_BOOL("XCASE", XCASE, c_lflag, );
#endif
    /* Configuration of ECHO */
#if defined(ECHOCTL)
    GET_BOOL("ECHOCTL", ECHOCTL, c_lflag, );
#endif
    /* Configuration of IXON/IXOFF */
#if defined(IXANY)
    GET_BOOL("IXANY", IXANY, c_iflag, );
#endif
    /* Configuration of OPOST */
#if defined(OLCUC)
    GET_BOOL("OLCUC", OLCUC, c_oflag, );
#endif
#if defined(ONLCR)
    GET_BOOL("ONLCR", ONLCR, c_oflag, );
#endif
#if defined(OCRNL)
    GET_BOOL("OCRNL", OCRNL, c_oflag, );
#endif
#if defined(ONOCR)
    GET_BOOL("ONOCR", ONOCR, c_oflag, );
#endif
#if defined(ONLRET)
    GET_BOOL("ONLRET", ONLRET, c_oflag, );
#endif

    /*
     * Modes that want to be set in only one place, and that we have
     * squashed locally.
     */
#if defined(ISIG)
    GET_BOOL("ISIG", ISIG, c_lflag, );
#endif
#if defined(ICANON)
    GET_BOOL("ICANON", ICANON, c_lflag, );
#endif
#if defined(ECHO)
    GET_BOOL("ECHO", ECHO, c_lflag, );
#endif
#if defined(IXON)
    GET_BOOL("IXON", IXON, c_iflag, );
#endif
#if defined(IXOFF)
    GET_BOOL("IXOFF", IXOFF, c_iflag, );
#endif
#if defined(OPOST)
    GET_BOOL("OPOST", OPOST, c_oflag, );
#endif

    /*
     * We do not propagate the following modes:
     *  - Parity/serial settings, which are a local affair and don't
     *    make sense propagated over SSH's 8-bit byte-stream.
     *      IGNPAR PARMRK INPCK CS7 CS8 PARENB PARODD
     *  - Things that want to be enabled in one place that we don't
     *    squash locally.
     *      IUCLC
     *  - Status bits.
     *      PENDIN
     *  - Things I don't know what to do with. (FIXME)
     *      ISTRIP IMAXBEL NOFLSH TOSTOP IEXTEN
     *      INLCR IGNCR ICRNL
     */

#undef GET_CHAR
#undef GET_BOOL

    /* Fall through to here for unrecognised names, or ones that are
     * unsupported on this platform */
    return NULL;
}

void cleanup_termios(void)
{
    if (local_tty)
	tcsetattr(0, TCSANOW, &orig_termios);
}

bufchain stdout_data, stderr_data;

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);
    }
}

int from_backend(void *frontend_handle, int is_stderr,
		 const char *data, int len)
{
    int osize, esize;

    if (is_stderr) {
	bufchain_add(&stderr_data, data, len);
	try_output(1);
    } else {
	bufchain_add(&stdout_data, data, len);
	try_output(0);
    }

    osize = bufchain_size(&stdout_data);
    esize = bufchain_size(&stderr_data);

    return osize + esize;
}

int from_backend_untrusted(void *frontend_handle, const char *data, int len)
{
    /*
     * No "untrusted" output should get here (the way the code is
     * currently, it's all diverted by FLAG_STDERR).
     */
    assert(!"Unexpected call to from_backend_untrusted()");
    return 0; /* not reached */
}

int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
{
    int ret;
    ret = cmdline_get_passwd_input(p, in, inlen);
    if (ret == -1)
	ret = console_get_userpass_input(p, in, inlen);
    return ret;
}

/*
 * Handle data from a local tty in PARMRK format.
 */
static void from_tty(void *vbuf, unsigned len)
{
    char *p, *q, *end, *buf = vbuf;
    static enum {NORMAL, FF, FF00} state = NORMAL;

    p = buf; end = buf + len;
    while (p < end) {
	switch (state) {
	    case NORMAL:
		if (*p == '\xff') {
		    p++;
		    state = FF;
		} else {
		    q = memchr(p, '\xff', end - p);
		    if (q == NULL) q = end;
		    back->send(backhandle, p, q - p);
		    p = q;
		}
		break;
	    case FF:
		if (*p == '\xff') {
		    back->send(backhandle, p, 1);
		    p++;
		    state = NORMAL;
		} else if (*p == '\0') {
		    p++;
		    state = FF00;
		} else abort();
		break;
	    case FF00:
		if (*p == '\0') {
		    back->special(backhandle, TS_BRK);
		} else {
		    /* 
		     * Pretend that PARMRK wasn't set.  This involves
		     * faking what INPCK and IGNPAR would have done if
		     * we hadn't overridden them.  Unfortunately, we
		     * can't do this entirely correctly because INPCK
		     * distinguishes between framing and parity
		     * errors, but PARMRK format represents both in
		     * the same way.  We assume that parity errors are
		     * more common than framing errors, and hence
		     * treat all input errors as being subject to
		     * INPCK.
		     */
		    if (orig_termios.c_iflag & INPCK) {
			/* If IGNPAR is set, we throw away the character. */
			if (!(orig_termios.c_iflag & IGNPAR)) {
			    /* PE/FE get passed on as NUL. */
			    *p = 0;
			    back->send(backhandle, p, 1);
			}
		    } else {
			/* INPCK not set.  Assume we got a parity error. */
			back->send(backhandle, p, 1);
		    }
		}
		p++;
		state = NORMAL;
	}
    }
}

int signalpipe[2];

void sigwinch(int signum)
{
    write(signalpipe[1], "x", 1);
}

/*
 * In Plink our selects are synchronous, so these functions are
 * empty stubs.
 */
int uxsel_input_add(int fd, int rwx) { return 0; }
void uxsel_input_remove(int id) { }

/*
 * Short description of parameters.
 */
static void usage(void)
{
    printf("PuTTY Link: command-line connection utility\n");
    printf("%s\n", ver);
    printf("Usage: plink [options] [user@]host [command]\n");
    printf("       (\"host\" can also be a PuTTY saved session name)\n");
    printf("Options:\n");
    printf("  -V        print version information and exit\n");
    printf("  -pgpfp    print PGP key fingerprints and exit\n");
    printf("  -v        show verbose messages\n");
    printf("  -load sessname  Load settings from saved session\n");
    printf("  -ssh -telnet -rlogin -raw\n");
    printf("            force use of a particular protocol\n");
    printf("  -P port   connect to specified port\n");
    printf("  -l user   connect with specified username\n");
    printf("  -batch    disable all interactive prompts\n");
    printf("The following options only apply to SSH connections:\n");
    printf("  -pw passw login with specified password\n");
    printf("  -D [listen-IP:]listen-port\n");
    printf("            Dynamic SOCKS-based port forwarding\n");
    printf("  -L [listen-IP:]listen-port:host:port\n");
    printf("            Forward local port to remote address\n");
    printf("  -R [listen-IP:]listen-port:host:port\n");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -