#include "storage.h"
#include "tree234.h"
+#define MAX_STDIN_BACKLOG 4096
+
void fatalbox(char *p, ...)
{
va_list ap;
}
}
-HANDLE inhandle, outhandle, errhandle;
-DWORD orig_console_mode;
+/*
+ * Ask whether the selected cipher is acceptable (since it was
+ * below the configured 'warn' threshold).
+ * cs: 0 = both ways, 1 = client->server, 2 = server->client
+ */
+void askcipher(char *ciphername, int cs)
+{
+ HANDLE hin;
+ DWORD savemode, i;
-WSAEVENT netevent;
+ static const char msg[] =
+ "The first %scipher supported by the server is\n"
+ "%s, which is below the configured warning threshold.\n"
+ "Continue with connection? (y/n) ";
+ static const char abandoned[] = "Connection abandoned.\n";
-void from_backend(int is_stderr, char *data, int len)
-{
- int pos;
- DWORD ret;
- HANDLE h = (is_stderr ? errhandle : outhandle);
+ char line[32];
+
+ fprintf(stderr, msg,
+ (cs == 0) ? "" :
+ (cs == 1) ? "client-to-server " :
+ "server-to-client ",
+ ciphername);
+ fflush(stderr);
- pos = 0;
- while (pos < len) {
- if (!WriteFile(h, data + pos, len - pos, &ret, NULL))
- return; /* give up in panic */
- pos += ret;
+ hin = GetStdHandle(STD_INPUT_HANDLE);
+ GetConsoleMode(hin, &savemode);
+ SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
+ ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
+ ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
+ SetConsoleMode(hin, savemode);
+
+ if (line[0] == 'y' || line[0] == 'Y') {
+ return;
+ } else {
+ fprintf(stderr, abandoned);
+ exit(0);
}
}
+HANDLE inhandle, outhandle, errhandle;
+DWORD orig_console_mode;
+
+WSAEVENT netevent;
+
int term_ldisc(int mode)
{
return FALSE;
SetConsoleMode(inhandle, mode);
}
-struct input_data {
- DWORD len;
- char buffer[4096];
- HANDLE event, eventback;
-};
-
static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
{
HANDLE hin, hout;
return 1;
}
+struct input_data {
+ DWORD len;
+ char buffer[4096];
+ HANDLE event, eventback;
+};
+
static DWORD WINAPI stdin_read_thread(void *param)
{
struct input_data *idata = (struct input_data *) param;
return 0;
}
+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)
+{
+ 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;
+}
+
+bufchain stdout_data, stderr_data;
+struct output_data odata, edata;
+
+void try_output(int is_stderr)
+{
+ 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;
+ }
+}
+
+int from_backend(int is_stderr, char *data, int len)
+{
+ HANDLE h = (is_stderr ? errhandle : outhandle);
+ 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;
+}
+
/*
* Short description of parameters.
*/
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 show verbose messages\n");
printf(" -ssh force use of ssh protocol\n");
{
int events;
if (startup) {
- events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
+ events = (FD_CONNECT | FD_READ | FD_WRITE |
+ FD_OOB | FD_CLOSE | FD_ACCEPT);
} else {
events = 0;
}
{
WSADATA wsadata;
WORD winsock_ver;
- WSAEVENT stdinevent;
- HANDLE handles[2];
- DWORD threadid;
+ WSAEVENT stdinevent, stdoutevent, stderrevent;
+ HANDLE handles[4];
+ DWORD in_threadid, out_threadid, err_threadid;
struct input_data idata;
+ int reading;
int sending;
int portnumber = -1;
SOCKET *sklist;
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 = FLAG_STDERR;
/*
command[cmdlen++] = d;
} while (c != EOF);
cfg.remote_cmd_ptr = command;
+ cfg.remote_cmd_ptr2 = NULL;
cfg.nopty = TRUE; /* command => no terminal */
} else if (!strcmp(p, "-P") && argc > 1) {
--argc, portnumber = atoi(*++argv);
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);
*/
handles[0] = netevent;
handles[1] = stdinevent;
+ handles[2] = stdoutevent;
+ handles[3] = stderrevent;
sending = FALSE;
+
+ /*
+ * Create spare threads to write to stdout and stderr, so we
+ * can arrange asynchronous writes.
+ */
+ 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");
+ 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");
+ exit(1);
+ }
+
while (1) {
int n;
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");
+ &idata, 0, &in_threadid)) {
+ fprintf(stderr, "Unable to create input thread\n");
exit(1);
}
sending = TRUE;
}
- n = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ n = WaitForMultipleObjects(4, handles, FALSE, INFINITE);
if (n == 0) {
WSANETWORKEVENTS things;
SOCKET socket;
if (!WSAEnumNetworkEvents(socket, NULL, &things)) {
noise_ultralight(socket);
noise_ultralight(things.lNetworkEvents);
+ if (things.lNetworkEvents & FD_CONNECT)
+ connopen &= select_result(wp, (LPARAM) FD_CONNECT);
if (things.lNetworkEvents & FD_READ)
connopen &= select_result(wp, (LPARAM) FD_READ);
if (things.lNetworkEvents & FD_CLOSE)
connopen &= select_result(wp, (LPARAM) FD_OOB);
if (things.lNetworkEvents & FD_WRITE)
connopen &= select_result(wp, (LPARAM) FD_WRITE);
+ if (things.lNetworkEvents & FD_ACCEPT)
+ connopen &= select_result(wp, (LPARAM) FD_ACCEPT);
+
}
}
} else if (n == 1) {
+ reading = 0;
noise_ultralight(idata.len);
- if (idata.len > 0) {
- back->send(idata.buffer, idata.len);
- } else {
- back->special(TS_EOF);
+ if (connopen && back->socket() != NULL) {
+ if (idata.len > 0) {
+ back->send(idata.buffer, idata.len);
+ } else {
+ back->special(TS_EOF);
+ }
}
+ } else if (n == 2) {
+ odata.busy = 0;
+ if (!odata.writeret) {
+ fprintf(stderr, "Unable to write to standard output\n");
+ exit(0);
+ }
+ bufchain_consume(&stdout_data, odata.lenwritten);
+ if (bufchain_size(&stdout_data) > 0)
+ try_output(0);
+ if (connopen && back->socket() != NULL) {
+ back->unthrottle(bufchain_size(&stdout_data) +
+ bufchain_size(&stderr_data));
+ }
+ } else if (n == 3) {
+ edata.busy = 0;
+ if (!edata.writeret) {
+ fprintf(stderr, "Unable to write to standard output\n");
+ exit(0);
+ }
+ bufchain_consume(&stderr_data, edata.lenwritten);
+ if (bufchain_size(&stderr_data) > 0)
+ try_output(1);
+ if (connopen && back->socket() != NULL) {
+ back->unthrottle(bufchain_size(&stdout_data) +
+ bufchain_size(&stderr_data));
+ }
+ }
+ if (!reading && back->sendbuffer() < MAX_STDIN_BACKLOG) {
SetEvent(idata.eventback);
+ reading = 1;
}
- if (!connopen || back->socket() == NULL)
+ if ((!connopen || back->socket() == NULL) &&
+ bufchain_size(&stdout_data) == 0 &&
+ bufchain_size(&stderr_data) == 0)
break; /* we closed the connection */
}
WSACleanup();