int main(int argc, char **argv) { bool sending; SOCKET *sklist; size_t skcount, sksize; int exitcode; bool errors; bool use_subsystem = false; bool just_test_share_exists = false; enum TriState sanitise_stdout = AUTO, sanitise_stderr = AUTO; unsigned long now, next, then; const struct BackendVtable *vt; dll_hijacking_protection(); sklist = NULL; skcount = sksize = 0; /* * Initialise port and protocol to sensible defaults. (These * will be overridden by more or less anything.) */ default_protocol = PROT_SSH; default_port = 22; flags = 0; cmdline_tooltype |= (TOOLTYPE_HOST_ARG | TOOLTYPE_HOST_ARG_CAN_BE_SESSION | TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX | TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD); /* * 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(); } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints(); exit(1); } else if (!strcmp(p, "-shareexists")) { just_test_share_exists = true; } 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); /* * 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. */ vt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol)); if (vt == NULL) { fprintf(stderr, "Internal fault: Unsupported protocol found\n"); return 1; } sk_init(); if (p_WSAEventSelect == NULL) { fprintf(stderr, "Plink requires WinSock 2\n"); 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_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); logctx = log_init(default_logpolicy, conf); if (just_test_share_exists) { if (!vt->test_for_upstream) { fprintf(stderr, "Connection sharing not supported for connection " "type '%s'\n", vt->name); return 1; } if (vt->test_for_upstream(conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), conf)) return 0; else return 1; } if (restricted_acl) { lp_eventlog(default_logpolicy, "Running with restricted process ACL"); } /* * Start up the connection. */ netevent = CreateEvent(NULL, false, false, NULL); { const char *error; char *realhost; /* nodelay is only useful if stdin is a character device (console) */ bool nodelay = conf_get_bool(conf, CONF_tcp_nodelay) && (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR); error = backend_init(vt, 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", error); return 1; } sfree(realhost); } inhandle = GetStdHandle(STD_INPUT_HANDLE); outhandle = GetStdHandle(STD_OUTPUT_HANDLE); errhandle = GetStdHandle(STD_ERROR_HANDLE); /* * 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. */ GetConsoleMode(inhandle, &orig_console_mode); SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT); /* * Pass the output handles to the handle-handling subsystem. * (The input one we leave until we're through the * authentication process.) */ stdout_handle = handle_output_new(outhandle, stdouterr_sent, NULL, 0); stderr_handle = handle_output_new(errhandle, stdouterr_sent, NULL, 0); handle_sink_init(&stdout_hs, stdout_handle); handle_sink_init(&stderr_hs, stderr_handle); stdout_bs = BinarySink_UPCAST(&stdout_hs); stderr_bs = BinarySink_UPCAST(&stderr_hs); /* * 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 console, 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 console, 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 && is_console_handle(outhandle) && 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 && is_console_handle(errhandle) && conf_get_bool(conf, CONF_nopty))) { stderr_scc = stripctrl_new(stderr_bs, true, L'\0'); stderr_bs = BinarySink_UPCAST(stderr_scc); } main_thread_id = GetCurrentThreadId(); sending = false; now = GETTICKCOUNT(); while (1) { int nhandles; HANDLE *handles; int n; DWORD ticks; if (!sending && backend_sendok(backend)) { stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL, 0); sending = true; } 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 */ } handles = handle_get_events(&nhandles); handles = sresize(handles, nhandles+1, HANDLE); handles[nhandles] = netevent; n = MsgWaitForMultipleObjects(nhandles+1, handles, false, ticks, QS_POSTMESSAGE); 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; int i, socketstate; /* * We must not call select_result() for any socket * until we have finished enumerating within the tree. * This is because select_result() may close the socket * and modify the tree. */ /* Count the active sockets. */ i = 0; for (socket = first_socket(&socketstate); socket != INVALID_SOCKET; socket = next_socket(&socketstate)) i++; /* Expand the buffer if necessary. */ sgrowarray(sklist, sksize, i); /* Retrieve the sockets into sklist. */ skcount = 0; for (socket = first_socket(&socketstate); socket != INVALID_SOCKET; socket = next_socket(&socketstate)) { sklist[skcount++] = socket; } /* Now we're done enumerating; go through the list. */ for (i = 0; i < skcount; i++) { WPARAM wp; socket = sklist[i]; wp = (WPARAM) socket; if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) { static const struct { int bit, mask; } eventtypes[] = { {FD_CONNECT_BIT, FD_CONNECT}, {FD_READ_BIT, FD_READ}, {FD_CLOSE_BIT, FD_CLOSE}, {FD_OOB_BIT, FD_OOB}, {FD_WRITE_BIT, FD_WRITE}, {FD_ACCEPT_BIT, FD_ACCEPT}, }; int e; noise_ultralight(NOISE_SOURCE_IOID, socket); for (e = 0; e < lenof(eventtypes); e++) if (things.lNetworkEvents & eventtypes[e].mask) { LPARAM lp; int err = things.iErrorCode[eventtypes[e].bit]; lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err); select_result(wp, lp); } } } } else if (n == WAIT_OBJECT_0 + nhandles + 1) { MSG msg; while (PeekMessage(&msg, INVALID_HANDLE_VALUE, WM_AGENT_CALLBACK, WM_AGENT_CALLBACK, PM_REMOVE)) { struct agent_callback *c = (struct agent_callback *)msg.lParam; c->callback(c->callback_ctx, c->data, c->len); sfree(c); } } run_toplevel_callbacks(); if (n == WAIT_TIMEOUT) { now = next; } else { now = GETTICKCOUNT(); } sfree(handles); if (sending) handle_unthrottle(stdin_handle, backend_sendbuffer(backend)); if (!backend_connected(backend) && handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 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 0; /* placate compiler warning */ }
int do_eventsel_loop(HANDLE other_event) { int n, nhandles, nallhandles, netindex, otherindex; long next, ticks; HANDLE *handles; SOCKET *sklist; int skcount; long now = GETTICKCOUNT(); static int timeoutCount = 0; if (sk_isClosed()) { return -1; } if (run_timers(now, &next)) { ticks = next - GETTICKCOUNT(); if (ticks < 0) ticks = 0; /* just in case */ } else { ticks = INFINITE; } if (ticks < 0 || ticks > 100) ticks = 100; handles = handle_get_events(&nhandles); handles = sresize(handles, nhandles+2, HANDLE); nallhandles = nhandles; if (netevent != INVALID_HANDLE_VALUE) handles[netindex = nallhandles++] = netevent; else netindex = -1; if (other_event != INVALID_HANDLE_VALUE) handles[otherindex = nallhandles++] = other_event; else otherindex = -1; n = WaitForMultipleObjects(nallhandles, handles, FALSE, ticks); if (STATUS_TIMEOUT == n) { sfree(handles); ++timeoutCount; return timeoutCount > 50 ? -1 : 0; } timeoutCount = 0; if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { handle_got_event(handles[n - WAIT_OBJECT_0]); } else if (netindex >= 0 && n == WAIT_OBJECT_0 + netindex) { WSANETWORKEVENTS things; SOCKET socket; extern SOCKET first_socket(int *), next_socket(int *); extern int select_result(WPARAM, LPARAM); int i, socketstate; /* * We must not call select_result() for any socket * until we have finished enumerating within the * tree. This is because select_result() may close * the socket and modify the tree. */ /* Count the active sockets. */ i = 0; for (socket = first_socket(&socketstate); socket != INVALID_SOCKET; socket = next_socket(&socketstate)) i++; /* Expand the buffer if necessary. */ sklist = snewn(i, SOCKET); /* Retrieve the sockets into sklist. */ skcount = 0; for (socket = first_socket(&socketstate); socket != INVALID_SOCKET; socket = next_socket(&socketstate)) { sklist[skcount++] = socket; } /* Now we're done enumerating; go through the list. */ for (i = 0; i < skcount; i++) { WPARAM wp; socket = sklist[i]; wp = (WPARAM) socket; if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) { static const struct { int bit, mask; } eventtypes[] = { {FD_CONNECT_BIT, FD_CONNECT}, {FD_READ_BIT, FD_READ}, {FD_CLOSE_BIT, FD_CLOSE}, {FD_OOB_BIT, FD_OOB}, {FD_WRITE_BIT, FD_WRITE}, {FD_ACCEPT_BIT, FD_ACCEPT}, }; int e; noise_ultralight(socket); noise_ultralight(things.lNetworkEvents); for (e = 0; e < lenof(eventtypes); e++) if (things.lNetworkEvents & eventtypes[e].mask) { LPARAM lp; int err = things.iErrorCode[eventtypes[e].bit]; lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err); select_result(wp, lp); } } } sfree(sklist); } sfree(handles); if (n == WAIT_TIMEOUT) { now = next; } else { now = GETTICKCOUNT(); } if (otherindex >= 0 && n == WAIT_OBJECT_0 + otherindex) return 1; return 0; }
int main(int argc, char **argv) { WSADATA wsadata; WORD winsock_ver; WSAEVENT stdinevent; HANDLE handles[2]; DWORD threadid; struct input_data idata; int sending; int portnumber = -1; SOCKET *sklist; int skcount, sksize; int connopen; ssh_get_password = get_password; sklist = NULL; skcount = sksize = 0; flags = FLAG_STDERR; /* * Process the command line. */ do_defaults(NULL, &cfg); default_protocol = cfg.protocol; default_port = cfg.port; { /* * 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; } } } } while (--argc) { char *p = *++argv; if (*p == '-') { if (!strcmp(p, "-ssh")) { default_protocol = cfg.protocol = PROT_SSH; default_port = cfg.port = 22; } else if (!strcmp(p, "-telnet")) { default_protocol = cfg.protocol = PROT_TELNET; default_port = cfg.port = 23; } else if (!strcmp(p, "-raw")) { default_protocol = cfg.protocol = PROT_RAW; } else if (!strcmp(p, "-v")) { flags |= FLAG_VERBOSE; } else if (!strcmp(p, "-log")) { logfile = "putty.log"; } else if (!strcmp(p, "-pw") && argc > 1) { --argc, password = *++argv; } else if (!strcmp(p, "-l") && argc > 1) { char *username; --argc, username = *++argv; strncpy(cfg.username, username, sizeof(cfg.username)); cfg.username[sizeof(cfg.username)-1] = '\0'; } else if (!strcmp(p, "-P") && argc > 1) { --argc, portnumber = atoi(*++argv); } } else if (*p) { if (!*cfg.host) { char *q = p; /* * If the hostname starts with "telnet:", set the * protocol to Telnet and process the string as a * Telnet URL. */ if (!strncmp(q, "telnet:", 7)) { char c; q += 7; if (q[0] == '/' && q[1] == '/') q += 2; cfg.protocol = PROT_TELNET; p = q; while (*p && *p != ':' && *p != '/') p++; c = *p; if (*p) *p++ = '\0'; if (c == ':') cfg.port = atoi(p); else cfg.port = -1; strncpy (cfg.host, q, sizeof(cfg.host)-1); cfg.host[sizeof(cfg.host)-1] = '\0'; } else { char *r; /* * Before we process the [user@]host string, we * first check for the presence of a protocol * prefix (a protocol name followed by ","). */ 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; } } } /* * Three cases. Either (a) there's a nonzero * length string followed by an @, in which * case that's user and the remainder is host. * Or (b) there's only one string, not counting * a potential initial @, and it exists in the * saved-sessions database. Or (c) only one * string and it _doesn't_ exist in the * database. */ r = strrchr(p, '@'); if (r == p) p++, r = NULL; /* discount initial @ */ if (r == NULL) { /* * One string. */ Config cfg2; do_defaults (p, &cfg2); if (cfg2.host[0] == '\0') { /* No settings for this host; use defaults */ strncpy(cfg.host, p, sizeof(cfg.host)-1); cfg.host[sizeof(cfg.host)-1] = '\0'; cfg.port = 22; } else cfg = cfg2; } else { *r++ = '\0'; strncpy(cfg.username, p, sizeof(cfg.username)-1); cfg.username[sizeof(cfg.username)-1] = '\0'; strncpy(cfg.host, r, sizeof(cfg.host)-1); cfg.host[sizeof(cfg.host)-1] = '\0'; cfg.port = 22; } } } else { int len = sizeof(cfg.remote_cmd) - 1; char *cp = cfg.remote_cmd; int len2; strncpy(cp, p, len); cp[len] = '\0'; len2 = strlen(cp); len -= len2; cp += len2; while (--argc) { if (len > 0) len--, *cp++ = ' '; strncpy(cp, *++argv, len); cp[len] = '\0'; len2 = strlen(cp); len -= len2; cp += len2; } cfg.nopty = TRUE; /* command => no terminal */ cfg.ldisc_term = TRUE; /* use stdin like a line buffer */ break; /* done with cmdline */ } } } if (!*cfg.host) { usage(); } if (!*cfg.remote_cmd) 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; } } /* * Select port. */ if (portnumber != -1) cfg.port = portnumber; /* * Initialise WinSock. */ winsock_ver = MAKEWORD(2, 0); if (WSAStartup(winsock_ver, &wsadata)) { MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error", MB_OK | MB_ICONEXCLAMATION); return 1; } if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 0) { MessageBox(NULL, "WinSock version is incompatible with 2.0", "WinSock Error", MB_OK | MB_ICONEXCLAMATION); WSACleanup(); return 1; } sk_init(); /* * Start up the connection. */ netevent = CreateEvent(NULL, FALSE, FALSE, NULL); { char *error; char *realhost; error = back->init (cfg.host, cfg.port, &realhost); if (error) { fprintf(stderr, "Unable to open connection:\n%s", error); return 1; } } connopen = 1; stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL); GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &orig_console_mode); SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT); outhandle = GetStdHandle(STD_OUTPUT_HANDLE); errhandle = GetStdHandle(STD_ERROR_HANDLE); /* * 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; sending = FALSE; while (1) { int n; if (!sending && back->sendok()) { /* * 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, &threadid)) { fprintf(stderr, "Unable to create second thread\n"); exit(1); } sending = TRUE; } n = WaitForMultipleObjects(2, handles, FALSE, INFINITE); if (n == 0) { WSANETWORKEVENTS things; enum234 e; SOCKET socket; extern SOCKET first_socket(enum234 *), next_socket(enum234 *); extern int select_result(WPARAM, LPARAM); int i; /* * We must not call select_result() for any socket * until we have finished enumerating within the tree. * This is because select_result() may close the socket * and modify the tree. */ /* Count the active sockets. */ i = 0; for (socket = first_socket(&e); socket != INVALID_SOCKET; socket = next_socket(&e)) i++; /* Expand the buffer if necessary. */ if (i > sksize) { sksize = i+16; sklist = srealloc(sklist, sksize * sizeof(*sklist)); } /* Retrieve the sockets into sklist. */ skcount = 0; for (socket = first_socket(&e); socket != INVALID_SOCKET; socket = next_socket(&e)) { sklist[skcount++] = socket; } /* Now we're done enumerating; go through the list. */ for (i = 0; i < skcount; i++) { WPARAM wp; socket = sklist[i]; wp = (WPARAM)socket; if (!WSAEnumNetworkEvents(socket, netevent, &things)) { noise_ultralight(socket); noise_ultralight(things.lNetworkEvents); if (things.lNetworkEvents & FD_READ) connopen &= select_result(wp, (LPARAM)FD_READ); if (things.lNetworkEvents & FD_CLOSE) connopen &= select_result(wp, (LPARAM)FD_CLOSE); if (things.lNetworkEvents & FD_OOB) connopen &= select_result(wp, (LPARAM)FD_OOB); if (things.lNetworkEvents & FD_WRITE) connopen &= select_result(wp, (LPARAM)FD_WRITE); } } } else if (n == 1) { noise_ultralight(idata.len); if (idata.len > 0) { back->send(idata.buffer, idata.len); } else { back->special(TS_EOF); } SetEvent(idata.eventback); } if (!connopen || back->socket() == NULL) break; /* we closed the connection */ } WSACleanup(); return 0; }