Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug, int overlapped) { static const struct socket_function_table socket_fn_table = { sk_handle_plug, sk_handle_close, sk_handle_write, sk_handle_write_oob, sk_handle_write_eof, sk_handle_flush, sk_handle_set_frozen, sk_handle_socket_error }; Handle_Socket ret; int flags = (overlapped ? HANDLE_FLAG_OVERLAPPED : 0); ret = snew(struct Socket_handle_tag); ret->fn = &socket_fn_table; ret->plug = plug; ret->error = NULL; ret->frozen = UNFROZEN; bufchain_init(&ret->inputdata); ret->recv_H = recv_H; ret->recv_h = handle_input_new(ret->recv_H, handle_gotdata, ret, flags); ret->send_H = send_H; ret->send_h = handle_output_new(ret->send_H, handle_sentdata, ret, flags); return (Socket) ret; }
Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H, Plug plug, int overlapped) { static const struct socket_function_table socket_fn_table = { sk_handle_plug, sk_handle_close, sk_handle_write, sk_handle_write_oob, sk_handle_write_eof, sk_handle_flush, sk_handle_set_frozen, sk_handle_socket_error, sk_handle_peer_info, }; Handle_Socket ret; int flags = (overlapped ? HANDLE_FLAG_OVERLAPPED : 0); ret = snew(struct Socket_handle_tag); ret->fn = &socket_fn_table; ret->plug = plug; ret->error = NULL; ret->frozen = UNFROZEN; bufchain_init(&ret->inputdata); bufchain_init(&ret->stderrdata); ret->recv_H = recv_H; ret->recv_h = handle_input_new(ret->recv_H, handle_gotdata, ret, flags); ret->send_H = send_H; ret->send_h = handle_output_new(ret->send_H, handle_sentdata, ret, flags); ret->stderr_H = stderr_H; if (ret->stderr_H) ret->stderr_h = handle_input_new(ret->stderr_H, handle_stderr, ret, flags); #ifdef MPEXT // WinSCP core uses do_select as signalization of connection up/down do_select(plug, INVALID_SOCKET, 1); #endif ret->defer_close = ret->deferred_close = FALSE; return (Socket) ret; }
/* * Called to set up the serial connection. * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *serial_init(void *frontend_handle, void **backend_handle, Config *cfg, char *host, int port, char **realhost, int nodelay, int keepalive) { Serial serial; HANDLE serport; const char *err; serial = snew(struct serial_backend_data); serial->port = INVALID_HANDLE_VALUE; serial->out = serial->in = NULL; serial->bufsize = 0; serial->break_in_progress = FALSE; *backend_handle = serial; serial->frontend = frontend_handle; { char *msg = dupprintf("Opening serial device %s", cfg->serline); logevent(serial->frontend, msg); } serport = CreateFile(cfg->serline, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (serport == INVALID_HANDLE_VALUE) return "Unable to open serial port"; err = serial_configure(serial, serport, cfg); if (err) return err; serial->port = serport; serial->out = handle_output_new(serport, serial_sentdata, serial, HANDLE_FLAG_OVERLAPPED); serial->in = handle_input_new(serport, serial_gotdata, serial, HANDLE_FLAG_OVERLAPPED | HANDLE_FLAG_IGNOREEOF | HANDLE_FLAG_UNITBUFFER); *realhost = dupstr(cfg->serline); /* * Specials are always available. */ update_specials_menu(serial->frontend); return NULL; }
Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, Plug plug, const Config *cfg) { char *cmd; static const struct socket_function_table socket_fn_table = { sk_localproxy_plug, sk_localproxy_close, sk_localproxy_write, sk_localproxy_write_oob, sk_localproxy_flush, sk_localproxy_set_private_ptr, sk_localproxy_get_private_ptr, sk_localproxy_set_frozen, sk_localproxy_socket_error }; Local_Proxy_Socket ret; HANDLE us_to_cmd, us_from_cmd, cmd_to_us, cmd_from_us; SECURITY_ATTRIBUTES sa; STARTUPINFO si; PROCESS_INFORMATION pi; if (cfg->proxy_type != PROXY_CMD) return NULL; cmd = format_telnet_command(addr, port, cfg); { char *msg = dupprintf("Starting local proxy command: %s", cmd); /* We're allowed to pass NULL here, because we're part of the Windows * front end so we know logevent doesn't expect any data. */ logevent(NULL, msg); sfree(msg); } ret = snew(struct Socket_localproxy_tag); ret->fn = &socket_fn_table; ret->plug = plug; ret->error = NULL; /* * Create the pipes to the proxy command, and spawn the proxy * command process. */ sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; /* default */ sa.bInheritHandle = TRUE; if (!CreatePipe(&us_from_cmd, &cmd_to_us, &sa, 0)) { ret->error = dupprintf("Unable to create pipes for proxy command"); return (Socket)ret; } if (!CreatePipe(&cmd_from_us, &us_to_cmd, &sa, 0)) { CloseHandle(us_from_cmd); CloseHandle(cmd_to_us); ret->error = dupprintf("Unable to create pipes for proxy command"); return (Socket)ret; } SetHandleInformation(us_to_cmd, HANDLE_FLAG_INHERIT, 0); SetHandleInformation(us_from_cmd, HANDLE_FLAG_INHERIT, 0); si.cb = sizeof(si); si.lpReserved = NULL; si.lpDesktop = NULL; si.lpTitle = NULL; si.dwFlags = STARTF_USESTDHANDLES; si.cbReserved2 = 0; si.lpReserved2 = NULL; si.hStdInput = cmd_from_us; si.hStdOutput = cmd_to_us; si.hStdError = NULL; CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); CloseHandle(cmd_from_us); CloseHandle(cmd_to_us); ret->to_cmd_H = us_to_cmd; ret->from_cmd_H = us_from_cmd; ret->from_cmd_h = handle_input_new(ret->from_cmd_H, localproxy_gotdata, ret, 0); ret->to_cmd_h = handle_output_new(ret->to_cmd_H, localproxy_sentdata, ret, 0); /* We are responsible for this and don't need it any more */ sk_addr_free(addr); return (Socket) ret; }
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 */ }
/* * Called to set up the serial connection. * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *serial_init(Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { Serial *serial; HANDLE serport; const char *err; char *serline; /* No local authentication phase in this protocol */ seat_set_trust_status(seat, false); serial = snew(Serial); serial->port = INVALID_HANDLE_VALUE; serial->out = serial->in = NULL; serial->bufsize = 0; serial->break_in_progress = false; serial->backend.vt = &serial_backend; *backend_handle = &serial->backend; serial->seat = seat; serial->logctx = logctx; serline = conf_get_str(conf, CONF_serline); logeventf(serial->logctx, "Opening serial device %s", serline); { /* * Munge the string supplied by the user into a Windows filename. * * Windows supports opening a few "legacy" devices (including * COM1-9) by specifying their names verbatim as a filename to * open. (Thus, no files can ever have these names. See * <http://msdn2.microsoft.com/en-us/library/aa365247.aspx> * ("Naming a File") for the complete list of reserved names.) * * However, this doesn't let you get at devices COM10 and above. * For that, you need to specify a filename like "\\.\COM10". * This is also necessary for special serial and serial-like * devices such as \\.\WCEUSBSH001. It also works for the "legacy" * names, so you can do \\.\COM1 (verified as far back as Win95). * See <http://msdn2.microsoft.com/en-us/library/aa363858.aspx> * (CreateFile() docs). * * So, we believe that prepending "\\.\" should always be the * Right Thing. However, just in case someone finds something to * talk to that doesn't exist under there, if the serial line * contains a backslash, we use it verbatim. (This also lets * existing configurations using \\.\ continue working.) */ char *serfilename = dupprintf("%s%s", strchr(serline, '\\') ? "" : "\\\\.\\", serline); serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); sfree(serfilename); } if (serport == INVALID_HANDLE_VALUE) return "Unable to open serial port"; err = serial_configure(serial, serport, conf); if (err) return err; serial->port = serport; serial->out = handle_output_new(serport, serial_sentdata, serial, HANDLE_FLAG_OVERLAPPED); serial->in = handle_input_new(serport, serial_gotdata, serial, HANDLE_FLAG_OVERLAPPED | HANDLE_FLAG_IGNOREEOF | HANDLE_FLAG_UNITBUFFER); *realhost = dupstr(serline); /* * Specials are always available. */ seat_update_specials_menu(serial->seat); return NULL; }