mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-26 09:42:25 +00:00
ad0c502cef
LogContext is now the owner of the logevent() function that back ends and so forth are constantly calling. Previously, logevent was owned by the Frontend, which would store the message into its list for the GUI Event Log dialog (or print it to standard error, or whatever) and then pass it _back_ to LogContext to write to the currently open log file. Now it's the other way round: LogContext gets the message from the back end first, writes it to its log file if it feels so inclined, and communicates it back to the front end. This means that lots of parts of the back end system no longer need to have a pointer to a full-on Frontend; the only thing they needed it for was logging, so now they just have a LogContext (which many of them had to have anyway, e.g. for logging SSH packets or session traffic). LogContext itself also doesn't get a full Frontend pointer any more: it now talks back to the front end via a little vtable of its own called LogPolicy, which contains the method that passes Event Log entries through, the old askappend() function that decides whether to truncate a pre-existing log file, and an emergency function for printing an especially prominent message if the log file can't be created. One minor nice effect of this is that console and GUI apps can implement that last function subtly differently, so that Unix console apps can write it with a plain \n instead of the \r\n (harmless but inelegant) that the old centralised implementation generated. One other consequence of this is that the LogContext has to be provided to backend_init() so that it's available to backends from the instant of creation, rather than being provided via a separate API call a couple of function calls later, because backends have typically started doing things that need logging (like making network connections) before the call to backend_provide_logctx. Fortunately, there's no case in the whole code base where we don't already have logctx by the time we make a backend (so I don't actually remember why I ever delayed providing one). So that shortens the backend API by one function, which is always nice. While I'm tidying up, I've also moved the printf-style logeventf() and the handy logevent_and_free() into logging.c, instead of having copies of them scattered around other places. This has also let me remove some stub functions from a couple of outlying applications like Pageant. Finally, I've removed the pointless "_tag" at the end of LogContext's official struct name.
648 lines
19 KiB
C
648 lines
19 KiB
C
/*
|
|
* PLink - a Windows command-line (stdin/stdout) variant of PuTTY.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
|
|
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
|
|
#include "putty.h"
|
|
#include "storage.h"
|
|
#include "tree234.h"
|
|
#include "winsecur.h"
|
|
|
|
#define WM_AGENT_CALLBACK (WM_APP + 4)
|
|
|
|
struct agent_callback {
|
|
void (*callback)(void *, void *, int);
|
|
void *callback_ctx;
|
|
void *data;
|
|
int len;
|
|
};
|
|
|
|
void modalfatalbox(const 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 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(Frontend *frontend, const 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(const char *p, ...)
|
|
{
|
|
va_list ap;
|
|
fprintf(stderr, "plink: ");
|
|
va_start(ap, p);
|
|
vfprintf(stderr, p, ap);
|
|
va_end(ap);
|
|
fputc('\n', stderr);
|
|
exit(1);
|
|
}
|
|
|
|
HANDLE inhandle, outhandle, errhandle;
|
|
struct handle *stdin_handle, *stdout_handle, *stderr_handle;
|
|
DWORD orig_console_mode;
|
|
|
|
WSAEVENT netevent;
|
|
|
|
static Backend *backend;
|
|
static Conf *conf;
|
|
|
|
int term_ldisc(Terminal *term, int mode)
|
|
{
|
|
return FALSE;
|
|
}
|
|
void frontend_echoedit_update(Frontend *frontend, int echo, int edit)
|
|
{
|
|
/* Update stdin read mode to reflect changes in line discipline. */
|
|
DWORD mode;
|
|
|
|
mode = ENABLE_PROCESSED_INPUT;
|
|
if (echo)
|
|
mode = mode | ENABLE_ECHO_INPUT;
|
|
else
|
|
mode = mode & ~ENABLE_ECHO_INPUT;
|
|
if (edit)
|
|
mode = mode | ENABLE_LINE_INPUT;
|
|
else
|
|
mode = mode & ~ENABLE_LINE_INPUT;
|
|
SetConsoleMode(inhandle, mode);
|
|
}
|
|
|
|
char *get_ttymode(Frontend *frontend, const char *mode) { return NULL; }
|
|
|
|
int from_backend(Frontend *frontend, int is_stderr,
|
|
const void *data, int len)
|
|
{
|
|
if (is_stderr) {
|
|
handle_write(stderr_handle, data, len);
|
|
} else {
|
|
handle_write(stdout_handle, data, len);
|
|
}
|
|
|
|
return handle_backlog(stdout_handle) + handle_backlog(stderr_handle);
|
|
}
|
|
|
|
int from_backend_eof(Frontend *frontend)
|
|
{
|
|
handle_write_eof(stdout_handle);
|
|
return FALSE; /* do not respond to incoming EOF with outgoing */
|
|
}
|
|
|
|
int get_userpass_input(prompts_t *p, bufchain *input)
|
|
{
|
|
int ret;
|
|
ret = cmdline_get_passwd_input(p);
|
|
if (ret == -1)
|
|
ret = console_get_userpass_input(p);
|
|
return ret;
|
|
}
|
|
|
|
static DWORD main_thread_id;
|
|
|
|
void agent_schedule_callback(void (*callback)(void *, void *, int),
|
|
void *callback_ctx, void *data, int len)
|
|
{
|
|
struct agent_callback *c = snew(struct agent_callback);
|
|
c->callback = callback;
|
|
c->callback_ctx = callback_ctx;
|
|
c->data = data;
|
|
c->len = len;
|
|
PostThreadMessage(main_thread_id, WM_AGENT_CALLBACK, 0, (LPARAM)c);
|
|
}
|
|
|
|
/*
|
|
* Short description of parameters.
|
|
*/
|
|
static void usage(void)
|
|
{
|
|
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");
|
|
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 -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(" -proxycmd command\n");
|
|
printf(" use 'command' as local proxy\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");
|
|
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");
|
|
printf(" Forward remote port to local address\n");
|
|
printf(" -X -x enable / disable X11 forwarding\n");
|
|
printf(" -A -a enable / disable agent forwarding\n");
|
|
printf(" -t -T enable / disable pty allocation\n");
|
|
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 user authentication\n");
|
|
printf(" -noagent disable use of Pageant\n");
|
|
printf(" -agent enable use of Pageant\n");
|
|
printf(" -noshare disable use of connection sharing\n");
|
|
printf(" -share enable use of connection sharing\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)
|
|
{
|
|
char *buildinfo_text = buildinfo("\n");
|
|
printf("plink: %s\n%s\n", ver, buildinfo_text);
|
|
sfree(buildinfo_text);
|
|
exit(0);
|
|
}
|
|
|
|
char *do_select(SOCKET skt, int startup)
|
|
{
|
|
int events;
|
|
if (startup) {
|
|
events = (FD_CONNECT | FD_READ | FD_WRITE |
|
|
FD_OOB | FD_CLOSE | FD_ACCEPT);
|
|
} else {
|
|
events = 0;
|
|
}
|
|
if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {
|
|
switch (p_WSAGetLastError()) {
|
|
case WSAENETDOWN:
|
|
return "Network is down";
|
|
default:
|
|
return "WSAEventSelect(): unknown error";
|
|
}
|
|
}
|
|
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 (backend_connected(backend)) {
|
|
if (len > 0) {
|
|
return backend_send(backend, data, len);
|
|
} else {
|
|
backend_special(backend, SS_EOF, 0);
|
|
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 (backend_connected(backend)) {
|
|
backend_unthrottle(backend, (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)
|
|
{
|
|
int sending;
|
|
SOCKET *sklist;
|
|
int skcount, sksize;
|
|
int exitcode;
|
|
int errors;
|
|
int use_subsystem = 0;
|
|
int just_test_share_exists = FALSE;
|
|
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 = 0;
|
|
{
|
|
/*
|
|
* 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 = 1;
|
|
} else if (ret == 2) {
|
|
--argc, ++argv;
|
|
} else if (ret == 1) {
|
|
continue;
|
|
} else if (!strcmp(p, "-batch")) {
|
|
console_batch_mode = 1;
|
|
} else if (!strcmp(p, "-s")) {
|
|
/* Save status to write to conf later. */
|
|
use_subsystem = 1;
|
|
} 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 (*p != '-') {
|
|
char *command;
|
|
int cmdlen, cmdsize;
|
|
cmdlen = cmdsize = 0;
|
|
command = NULL;
|
|
|
|
while (argc) {
|
|
while (*p) {
|
|
if (cmdlen >= cmdsize) {
|
|
cmdsize = cmdlen + 512;
|
|
command = sresize(command, cmdsize, char);
|
|
}
|
|
command[cmdlen++]=*p++;
|
|
}
|
|
if (cmdlen >= cmdsize) {
|
|
cmdsize = cmdlen + 512;
|
|
command = sresize(command, cmdsize, char);
|
|
}
|
|
command[cmdlen++]=' '; /* always add trailing space */
|
|
if (--argc) p = *++argv;
|
|
}
|
|
if (cmdlen) command[--cmdlen]='\0';
|
|
/* change trailing blank to NUL */
|
|
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 */
|
|
} else {
|
|
fprintf(stderr, "plink: unknown option \"%s\"\n", p);
|
|
errors = 1;
|
|
}
|
|
}
|
|
|
|
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_int(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_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(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) */
|
|
int nodelay = conf_get_int(conf, CONF_tcp_nodelay) &&
|
|
(GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR);
|
|
|
|
error = backend_init(vt, NULL, &backend, logctx, 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;
|
|
}
|
|
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);
|
|
|
|
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;
|
|
extern SOCKET first_socket(int *), next_socket(int *);
|
|
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. */
|
|
if (i > sksize) {
|
|
sksize = i + 16;
|
|
sklist = sresize(sklist, sksize, 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);
|
|
}
|
|
}
|
|
}
|
|
} 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 */
|
|
}
|