X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=windows%2Fwinplink.c;h=0f86b41c648dbf6b978fd97a10b119dbe0c362d2;hb=e22120fe;hp=66af541352c12b4ad692a17311e947ee1aa988f9;hpb=36fc6c0a76dc763509feaca48295a9befa7d5c4d;p=PuTTY.git diff --git a/windows/winplink.c b/windows/winplink.c index 66af5413..0f86b41c 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -11,10 +11,9 @@ #include "putty.h" #include "storage.h" #include "tree234.h" +#include "winsecur.h" -#define WM_AGENT_CALLBACK (WM_XUSER + 4) - -#define MAX_STDIN_BACKLOG 4096 +#define WM_AGENT_CALLBACK (WM_APP + 4) struct agent_callback { void (*callback)(void *, void *, int); @@ -23,7 +22,7 @@ struct agent_callback { int len; }; -void fatalbox(char *p, ...) +void fatalbox(const char *p, ...) { va_list ap; fprintf(stderr, "FATAL ERROR: "); @@ -31,9 +30,13 @@ void fatalbox(char *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, ...) +void modalfatalbox(const char *p, ...) { va_list ap; fprintf(stderr, "FATAL ERROR: "); @@ -41,9 +44,22 @@ void modalfatalbox(char *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, ...) +void nonfatal(const char *p, ...) +{ + va_list ap; + fprintf(stderr, "ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); +} +void connection_fatal(void *frontend, const char *p, ...) { va_list ap; fprintf(stderr, "FATAL ERROR: "); @@ -51,9 +67,13 @@ void connection_fatal(void *frontend, char *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, ...) +void cmdline_error(const char *p, ...) { va_list ap; fprintf(stderr, "plink: "); @@ -65,19 +85,21 @@ void cmdline_error(char *p, ...) } HANDLE inhandle, outhandle, errhandle; +struct handle *stdin_handle, *stdout_handle, *stderr_handle; DWORD orig_console_mode; +int connopen; WSAEVENT netevent; static Backend *back; static void *backhandle; -static Config cfg; +static Conf *conf; int term_ldisc(Terminal *term, int mode) { return FALSE; } -void ldisc_update(void *frontend, int echo, int edit) +void frontend_echoedit_update(void *frontend, int echo, int edit) { /* Update stdin read mode to reflect changes in line discipline. */ DWORD mode; @@ -94,97 +116,43 @@ void ldisc_update(void *frontend, int echo, int edit) SetConsoleMode(inhandle, mode); } -struct input_data { - DWORD len; - char buffer[4096]; - HANDLE event, eventback; -}; +char *get_ttymode(void *frontend, const char *mode) { return NULL; } -static DWORD WINAPI stdin_read_thread(void *param) +int from_backend(void *frontend_handle, int is_stderr, + const char *data, int len) { - struct input_data *idata = (struct input_data *) param; - HANDLE inhandle; - - inhandle = GetStdHandle(STD_INPUT_HANDLE); - - while (ReadFile(inhandle, idata->buffer, sizeof(idata->buffer), - &idata->len, NULL) && idata->len > 0) { - SetEvent(idata->event); - WaitForSingleObject(idata->eventback, INFINITE); + if (is_stderr) { + handle_write(stderr_handle, data, len); + } else { + handle_write(stdout_handle, data, len); } - idata->len = 0; - SetEvent(idata->event); - - return 0; + return handle_backlog(stdout_handle) + handle_backlog(stderr_handle); } -struct output_data { - DWORD len, lenwritten; - int writeret; - char *buffer; - int is_stderr, done; - HANDLE event, eventback; - int busy; -}; - -static DWORD WINAPI stdout_write_thread(void *param) +int from_backend_untrusted(void *frontend_handle, const char *data, int len) { - struct output_data *odata = (struct output_data *) param; - HANDLE outhandle, errhandle; - - outhandle = GetStdHandle(STD_OUTPUT_HANDLE); - errhandle = GetStdHandle(STD_ERROR_HANDLE); - - while (1) { - WaitForSingleObject(odata->eventback, INFINITE); - if (odata->done) - break; - odata->writeret = - WriteFile(odata->is_stderr ? errhandle : outhandle, - odata->buffer, odata->len, &odata->lenwritten, NULL); - SetEvent(odata->event); - } - - return 0; + /* + * 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 */ } -bufchain stdout_data, stderr_data; -struct output_data odata, edata; - -void try_output(int is_stderr) +int from_backend_eof(void *frontend_handle) { - struct output_data *data = (is_stderr ? &edata : &odata); - void *senddata; - int sendlen; - - if (!data->busy) { - bufchain_prefix(is_stderr ? &stderr_data : &stdout_data, - &senddata, &sendlen); - data->buffer = senddata; - data->len = sendlen; - SetEvent(data->eventback); - data->busy = 1; - } + handle_write_eof(stdout_handle); + return FALSE; /* do not respond to incoming EOF with outgoing */ } -int from_backend(void *frontend_handle, int is_stderr, - const char *data, int len) +int get_userpass_input(prompts_t *p, const unsigned char *in, int inlen) { - 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 ret; + ret = cmdline_get_passwd_input(p, in, inlen); + if (ret == -1) + ret = console_get_userpass_input(p, in, inlen); + return ret; } static DWORD main_thread_id; @@ -205,7 +173,7 @@ void agent_schedule_callback(void (*callback)(void *, void *, int), */ static void usage(void) { - printf("PuTTY Link: command-line connection utility\n"); + printf("Plink: 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"); @@ -214,11 +182,13 @@ static void usage(void) 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(" -ssh -telnet -rlogin -raw -serial\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(" -sercfg configuration-string (e.g. 19200,8,n,1,X)\n"); + printf(" Specify the serial configuration (serial only)\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"); @@ -233,16 +203,29 @@ static void usage(void) printf(" -1 -2 force use of particular protocol version\n"); printf(" -4 -6 force use of IPv4 or IPv6\n"); printf(" -C enable compression\n"); - printf(" -i key private key file for authentication\n"); + printf(" -i key private key file for user authentication\n"); + printf(" -noagent disable use of Pageant\n"); + printf(" -agent enable use of Pageant\n"); + printf(" -hostkey aa:bb:cc:...\n"); + printf(" manually specify a host key (may be repeated)\n"); printf(" -m file read remote command(s) from file\n"); printf(" -s remote command is an SSH subsystem (SSH-2 only)\n"); printf(" -N don't start a shell/command (SSH-2 only)\n"); + printf(" -nc host:port\n"); + printf(" open tunnel in place of session (SSH-2 only)\n"); + printf(" -sshlog file\n"); + printf(" -sshrawlog file\n"); + printf(" log protocol details to a file\n"); + printf(" -shareexists\n"); + printf(" test whether a connection-sharing upstream exists\n"); exit(1); } static void version(void) { - printf("plink: %s\n", ver); + char *buildinfo_text = buildinfo("\n"); + printf("plink: %s\n%s\n", ver, buildinfo_text); + sfree(buildinfo_text); exit(1); } @@ -266,24 +249,72 @@ char *do_select(SOCKET skt, int startup) return NULL; } +int stdin_gotdata(struct handle *h, void *data, int len) +{ + if (len < 0) { + /* + * Special case: report read error. + */ + char buf[4096]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -len, 0, + buf, lenof(buf), NULL); + buf[lenof(buf)-1] = '\0'; + if (buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = '\0'; + fprintf(stderr, "Unable to read from standard input: %s\n", buf); + cleanup_exit(0); + } + noise_ultralight(len); + if (connopen && back->connected(backhandle)) { + if (len > 0) { + return back->send(backhandle, data, len); + } else { + back->special(backhandle, TS_EOF); + return 0; + } + } else + return 0; +} + +void stdouterr_sent(struct handle *h, int new_backlog) +{ + if (new_backlog < 0) { + /* + * Special case: report write error. + */ + char buf[4096]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -new_backlog, 0, + buf, lenof(buf), NULL); + buf[lenof(buf)-1] = '\0'; + if (buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = '\0'; + fprintf(stderr, "Unable to write to standard %s: %s\n", + (h == stdout_handle ? "output" : "error"), buf); + cleanup_exit(0); + } + if (connopen && back->connected(backhandle)) { + back->unthrottle(backhandle, (handle_backlog(stdout_handle) + + handle_backlog(stderr_handle))); + } +} + +const int share_can_be_downstream = TRUE; +const int share_can_be_upstream = TRUE; + int main(int argc, char **argv) { - WSAEVENT stdinevent, stdoutevent, stderrevent; - HANDLE handles[4]; - DWORD in_threadid, out_threadid, err_threadid; - struct input_data idata; - int reading = FALSE; int sending; int portnumber = -1; SOCKET *sklist; int skcount, sksize; - int connopen; int exitcode; int errors; + int got_host = FALSE; int use_subsystem = 0; - long now, next; + int just_test_share_exists = FALSE; + unsigned long now, next, then; - ssh_get_line = console_get_line; + dll_hijacking_protection(); sklist = NULL; skcount = sksize = 0; @@ -298,25 +329,24 @@ int main(int argc, char **argv) /* * Process the command line. */ - do_defaults(NULL, &cfg); + conf = conf_new(); + do_defaults(NULL, conf); loaded_session = FALSE; - default_protocol = cfg.protocol; - default_port = cfg.port; + default_protocol = conf_get_int(conf, CONF_protocol); + default_port = conf_get_int(conf, CONF_port); errors = 0; { /* * Override the default protocol if PLINK_PROTOCOL is set. */ char *p = getenv("PLINK_PROTOCOL"); - int i; if (p) { - for (i = 0; backends[i].backend != NULL; i++) { - if (!strcmp(backends[i].name, p)) { - default_protocol = cfg.protocol = backends[i].protocol; - default_port = cfg.port = - backends[i].backend->default_port; - break; - } + const Backend *b = backend_from_name(p); + if (b) { + default_protocol = b->protocol; + default_port = b->default_port; + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); } } } @@ -324,7 +354,7 @@ int main(int argc, char **argv) char *p = *++argv; if (*p == '-') { int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), - 1, &cfg); + 1, conf); if (ret == -2) { fprintf(stderr, "plink: option \"%s\" requires an argument\n", p); @@ -336,19 +366,23 @@ int main(int argc, char **argv) } else if (!strcmp(p, "-batch")) { console_batch_mode = 1; } else if (!strcmp(p, "-s")) { - /* Save status to write to cfg later. */ + /* Save status to write to conf later. */ use_subsystem = 1; - } else if (!strcmp(p, "-V")) { + } else if (!strcmp(p, "-V") || !strcmp(p, "--version")) { version(); + } else if (!strcmp(p, "--help")) { + usage(); } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints(); exit(1); + } else if (!strcmp(p, "-shareexists")) { + just_test_share_exists = TRUE; } else { fprintf(stderr, "plink: unknown option \"%s\"\n", p); errors = 1; } } else if (*p) { - if (!*cfg.host) { + if (!conf_launchable(conf) || !(got_host || loaded_session)) { char *q = p; /* * If the hostname starts with "telnet:", set the @@ -361,19 +395,18 @@ int main(int argc, char **argv) q += 7; if (q[0] == '/' && q[1] == '/') q += 2; - cfg.protocol = PROT_TELNET; + conf_set_int(conf, CONF_protocol, PROT_TELNET); p = q; - while (*p && *p != ':' && *p != '/') - p++; + p += host_strcspn(p, ":/"); c = *p; if (*p) *p++ = '\0'; if (c == ':') - cfg.port = atoi(p); + conf_set_int(conf, CONF_port, atoi(p)); else - cfg.port = -1; - strncpy(cfg.host, q, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_int(conf, CONF_port, -1); + conf_set_str(conf, CONF_host, q); + got_host = TRUE; } else { char *r, *user, *host; /* @@ -383,19 +416,16 @@ int main(int argc, char **argv) */ r = strchr(p, ','); if (r) { - int i, j; - for (i = 0; backends[i].backend != NULL; i++) { - j = strlen(backends[i].name); - if (j == r - p && - !memcmp(backends[i].name, p, j)) { - default_protocol = cfg.protocol = - backends[i].protocol; - portnumber = - backends[i].backend->default_port; - p = r + 1; - break; - } + const Backend *b; + *r = '\0'; + b = backend_from_name(p); + if (b) { + default_protocol = b->protocol; + conf_set_int(conf, CONF_protocol, + default_protocol); + portnumber = b->default_port; } + p = r + 1; } /* @@ -419,24 +449,24 @@ int main(int argc, char **argv) * same name as the hostname. */ { - Config cfg2; - do_defaults(host, &cfg2); - if (loaded_session || cfg2.host[0] == '\0') { + Conf *conf2 = conf_new(); + do_defaults(host, conf2); + if (loaded_session || !conf_launchable(conf2)) { /* No settings for this host; use defaults */ /* (or session was already loaded with -load) */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; - cfg.port = default_port; + conf_set_str(conf, CONF_host, host); + conf_set_int(conf, CONF_port, default_port); + got_host = TRUE; } else { - cfg = cfg2; + conf_copy_into(conf, conf2); + loaded_session = TRUE; } + conf_free(conf2); } if (user) { /* Patch in specified username. */ - strncpy(cfg.username, user, - sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; + conf_set_str(conf, CONF_username, user); } } @@ -463,9 +493,9 @@ int main(int argc, char **argv) } if (cmdlen) command[--cmdlen]='\0'; /* change trailing blank to NUL */ - cfg.remote_cmd_ptr = command; - cfg.remote_cmd_ptr2 = NULL; - cfg.nopty = TRUE; /* command => no terminal */ + conf_set_str(conf, CONF_remote_cmd, command); + conf_set_str(conf, CONF_remote_cmd2, ""); + conf_set_int(conf, CONF_nopty, TRUE); /* command => no tty */ break; /* done with cmdline */ } @@ -475,89 +505,100 @@ int main(int argc, char **argv) if (errors) return 1; - if (!*cfg.host) { + if (!conf_launchable(conf) || !(got_host || loaded_session)) { usage(); } /* - * Trim leading whitespace off the hostname if it's there. + * Muck about with the hostname in various ways. */ { - int space = strspn(cfg.host, " \t"); - memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space); - } + char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); + char *host = hostbuf; + char *p, *q; - /* See if host is of the form user@host */ - if (cfg.host[0] != '\0') { - char *atsign = strrchr(cfg.host, '@'); - /* Make sure we're not overflowing the user field */ - if (atsign) { - if (atsign - cfg.host < sizeof cfg.username) { - strncpy(cfg.username, cfg.host, atsign - cfg.host); - cfg.username[atsign - cfg.host] = '\0'; + /* + * Trim leading whitespace. + */ + host += strspn(host, " \t"); + + /* + * See if host is of the form user@host, and separate out + * the username if so. + */ + if (host[0] != '\0') { + char *atsign = strrchr(host, '@'); + if (atsign) { + *atsign = '\0'; + conf_set_str(conf, CONF_username, host); + host = atsign + 1; } - memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1)); } + + /* + * Trim a colon suffix off the hostname if it's there. In + * order to protect unbracketed IPv6 address literals + * against this treatment, we do not do this if there's + * _more_ than one colon. + */ + { + char *c = host_strchr(host, ':'); + + if (c) { + char *d = host_strchr(c+1, ':'); + if (!d) + *c = '\0'; + } + } + + /* + * Remove any remaining whitespace. + */ + p = hostbuf; + q = host; + while (*q) { + if (*q != ' ' && *q != '\t') + *p++ = *q; + q++; + } + *p = '\0'; + + conf_set_str(conf, CONF_host, hostbuf); + sfree(hostbuf); } /* * Perform command-line overrides on session configuration. */ - cmdline_run_saved(&cfg); + cmdline_run_saved(conf); /* * Apply subsystem status. */ if (use_subsystem) - cfg.ssh_subsys = TRUE; - - /* - * Trim a colon suffix off the hostname if it's there. - */ - cfg.host[strcspn(cfg.host, ":")] = '\0'; - - /* - * Remove any remaining whitespace from the hostname. - */ - { - int p1 = 0, p2 = 0; - while (cfg.host[p2] != '\0') { - if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') { - cfg.host[p1] = cfg.host[p2]; - p1++; - } - p2++; - } - cfg.host[p1] = '\0'; - } + conf_set_int(conf, CONF_ssh_subsys, TRUE); - if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd) + 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. */ - { - int i; - back = NULL; - for (i = 0; backends[i].backend != NULL; i++) - if (backends[i].protocol == cfg.protocol) { - back = backends[i].backend; - break; - } - if (back == NULL) { - fprintf(stderr, - "Internal fault: Unsupported protocol found\n"); - return 1; - } + back = backend_from_proto(conf_get_int(conf, CONF_protocol)); + if (back == NULL) { + fprintf(stderr, + "Internal fault: Unsupported protocol found\n"); + return 1; } /* * Select port. */ if (portnumber != -1) - cfg.port = portnumber; + conf_set_int(conf, CONF_port, portnumber); sk_init(); if (p_WSAEventSelect == NULL) { @@ -565,6 +606,33 @@ int main(int argc, char **argv) return 1; } + /* + * 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_int(conf, CONF_x11_forward) && + !conf_get_int(conf, CONF_agentfwd) && + !conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) + conf_set_int(conf, CONF_ssh_simple, TRUE); + + logctx = log_init(NULL, conf); + console_provide_logctx(logctx); + + if (just_test_share_exists) { + if (!back->test_for_upstream) { + fprintf(stderr, "Connection sharing not supported for connection " + "type '%s'\n", back->name); + return 1; + } + if (back->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. */ @@ -573,112 +641,85 @@ int main(int argc, char **argv) const char *error; char *realhost; /* nodelay is only useful if stdin is a character device (console) */ - int nodelay = cfg.tcp_nodelay && + int nodelay = conf_get_int(conf, CONF_tcp_nodelay) && (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR); - error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, - &realhost, nodelay, cfg.tcp_keepalives); + error = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, nodelay, + conf_get_int(conf, CONF_tcp_keepalives)); if (error) { fprintf(stderr, "Unable to open connection:\n%s", error); return 1; } - logctx = log_init(NULL, &cfg); back->provide_logctx(backhandle, logctx); - console_provide_logctx(logctx); sfree(realhost); } connopen = 1; - stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL); - stdoutevent = CreateEvent(NULL, FALSE, FALSE, NULL); - stderrevent = CreateEvent(NULL, FALSE, FALSE, NULL); - inhandle = GetStdHandle(STD_INPUT_HANDLE); outhandle = GetStdHandle(STD_OUTPUT_HANDLE); errhandle = GetStdHandle(STD_ERROR_HANDLE); - GetConsoleMode(inhandle, &orig_console_mode); - SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT); - - main_thread_id = GetCurrentThreadId(); /* * Turn off ECHO and LINE input modes. We don't care if this * call fails, because we know we aren't necessarily running in * a console. */ - handles[0] = netevent; - handles[1] = stdinevent; - handles[2] = stdoutevent; - handles[3] = stderrevent; - sending = FALSE; + GetConsoleMode(inhandle, &orig_console_mode); + SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT); /* - * Create spare threads to write to stdout and stderr, so we - * can arrange asynchronous writes. + * Pass the output handles to the handle-handling subsystem. + * (The input one we leave until we're through the + * authentication process.) */ - odata.event = stdoutevent; - odata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL); - odata.is_stderr = 0; - odata.busy = odata.done = 0; - if (!CreateThread(NULL, 0, stdout_write_thread, - &odata, 0, &out_threadid)) { - fprintf(stderr, "Unable to create output thread\n"); - cleanup_exit(1); - } - edata.event = stderrevent; - edata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL); - edata.is_stderr = 1; - edata.busy = edata.done = 0; - if (!CreateThread(NULL, 0, stdout_write_thread, - &edata, 0, &err_threadid)) { - fprintf(stderr, "Unable to create error output thread\n"); - cleanup_exit(1); - } + stdout_handle = handle_output_new(outhandle, stdouterr_sent, NULL, 0); + stderr_handle = handle_output_new(errhandle, stdouterr_sent, NULL, 0); + + main_thread_id = GetCurrentThreadId(); + + sending = FALSE; now = GETTICKCOUNT(); while (1) { + int nhandles; + HANDLE *handles; int n; DWORD ticks; if (!sending && back->sendok(backhandle)) { - /* - * Create a separate thread to read from stdin. This is - * a total pain, but I can't find another way to do it: - * - * - an overlapped ReadFile or ReadFileEx just doesn't - * happen; we get failure from ReadFileEx, and - * ReadFile blocks despite being given an OVERLAPPED - * structure. Perhaps we can't do overlapped reads - * on consoles. WHY THE HELL NOT? - * - * - WaitForMultipleObjects(netevent, console) doesn't - * work, because it signals the console when - * _anything_ happens, including mouse motions and - * other things that don't cause data to be readable - * - so we're back to ReadFile blocking. - */ - idata.event = stdinevent; - idata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!CreateThread(NULL, 0, stdin_read_thread, - &idata, 0, &in_threadid)) { - fprintf(stderr, "Unable to create input thread\n"); - cleanup_exit(1); - } + stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL, + 0); sending = TRUE; - reading = TRUE; } - if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - if (ticks < 0) ticks = 0; /* just in case */ + if (toplevel_callback_pending()) { + ticks = 0; + next = now; + } else if (run_timers(now, &next)) { + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; } else { ticks = INFINITE; + /* no need to initialise next here because we can never + * get WAIT_TIMEOUT */ } - n = MsgWaitForMultipleObjects(4, handles, FALSE, ticks, + handles = handle_get_events(&nhandles); + handles = sresize(handles, nhandles+1, HANDLE); + handles[nhandles] = netevent; + n = MsgWaitForMultipleObjects(nhandles+1, handles, FALSE, ticks, QS_POSTMESSAGE); - if (n == WAIT_OBJECT_0 + 0) { + if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { + handle_got_event(handles[n - WAIT_OBJECT_0]); + } else if (n == WAIT_OBJECT_0 + nhandles) { WSANETWORKEVENTS things; SOCKET socket; extern SOCKET first_socket(int *), next_socket(int *); @@ -739,43 +780,7 @@ int main(int argc, char **argv) } } } - } else if (n == WAIT_OBJECT_0 + 1) { - reading = 0; - noise_ultralight(idata.len); - if (connopen && back->socket(backhandle) != NULL) { - if (idata.len > 0) { - back->send(backhandle, idata.buffer, idata.len); - } else { - back->special(backhandle, TS_EOF); - } - } - } else if (n == WAIT_OBJECT_0 + 2) { - odata.busy = 0; - if (!odata.writeret) { - fprintf(stderr, "Unable to write to standard output\n"); - cleanup_exit(0); - } - bufchain_consume(&stdout_data, odata.lenwritten); - if (bufchain_size(&stdout_data) > 0) - try_output(0); - if (connopen && back->socket(backhandle) != NULL) { - back->unthrottle(backhandle, bufchain_size(&stdout_data) + - bufchain_size(&stderr_data)); - } - } else if (n == WAIT_OBJECT_0 + 3) { - edata.busy = 0; - if (!edata.writeret) { - fprintf(stderr, "Unable to write to standard output\n"); - cleanup_exit(0); - } - bufchain_consume(&stderr_data, edata.lenwritten); - if (bufchain_size(&stderr_data) > 0) - try_output(1); - if (connopen && back->socket(backhandle) != NULL) { - back->unthrottle(backhandle, bufchain_size(&stdout_data) + - bufchain_size(&stderr_data)); - } - } else if (n == WAIT_OBJECT_0 + 4) { + } else if (n == WAIT_OBJECT_0 + nhandles + 1) { MSG msg; while (PeekMessage(&msg, INVALID_HANDLE_VALUE, WM_AGENT_CALLBACK, WM_AGENT_CALLBACK, @@ -786,19 +791,21 @@ int main(int argc, char **argv) } } + run_toplevel_callbacks(); + if (n == WAIT_TIMEOUT) { now = next; } else { now = GETTICKCOUNT(); } - if (!reading && back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) { - SetEvent(idata.eventback); - reading = 1; - } - if ((!connopen || back->socket(backhandle) == NULL) && - bufchain_size(&stdout_data) == 0 && - bufchain_size(&stderr_data) == 0) + sfree(handles); + + if (sending) + handle_unthrottle(stdin_handle, back->sendbuffer(backhandle)); + + if ((!connopen || !back->connected(backhandle)) && + handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0) break; /* we closed the connection */ } exitcode = back->exitcode(backhandle);