1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00
Files
winscp/source/putty/proxy/newmain.c
Martin Prikryl cc81f2c3fc Updating code to PuTTY 0.84
Source commit: 71b0bdd5bb7ff22e916234fffeedf5db87668460
2026-05-30 01:07:44 +02:00

379 lines
11 KiB
C

#include "putty.h"
// pre_connect_command not supported in WinSCP
// (needs subprocess_waiter.c - what needs callback injection)
#ifndef WINSCP
typedef struct PreConnWrapper PreConnWrapper;
struct PreConnWrapper {
/* The Plug from the client who instantiated us */
Plug *plug;
/* Initially non-NULL, until the pre-connect command finishes */
Socket *preconn;
SubprocessWaiter *waiter;
/* Initially NULL, until we've run the pre-connect command */
Socket *mainsocket;
/* Used to collect stdout from the subprocess. (fd-socket collects
* stderr) */
ProxyStderrBuf psb;
/* Pending stuff to pass on to the main socket once we open it */
bufchain writebuf, oobbuf;
bool frozen;
bool pending_eof;
/* Parameters for calling new_connection */
SockAddr *addr;
char *hostname;
int port;
bool privport, oobinline, nodelay, keepalive;
Conf *conf;
Interactor *itr;
/* For logging the pre-connection command progress */
LogContext *logctx;
/* Our implementations of Plug and Socket */
Socket sockimpl;
Plug cmdplugimpl, mainplugimpl;
};
static void preconn_open_mainsocket(void *vctx)
{
PreConnWrapper *wr = (PreConnWrapper *)vctx;
assert(!wr->mainsocket);
wr->mainsocket = new_connection(
wr->addr, wr->hostname, wr->port, wr->privport, wr->oobinline,
wr->nodelay, wr->keepalive, &wr->mainplugimpl, wr->conf, wr->itr);
wr->addr = NULL; /* we've transferred ownership */
sk_set_frozen(wr->mainsocket, wr->frozen);
while (bufchain_size(&wr->oobbuf) > 0) {
ptrlen data = bufchain_prefix(&wr->oobbuf);
sk_write_oob(wr->mainsocket, data.ptr, data.len);
bufchain_consume(&wr->oobbuf, data.len);
}
while (bufchain_size(&wr->writebuf) > 0) {
ptrlen data = bufchain_prefix(&wr->writebuf);
sk_write(wr->mainsocket, data.ptr, data.len);
bufchain_consume(&wr->writebuf, data.len);
}
if (wr->pending_eof)
sk_write_eof(wr->mainsocket);
sfree(wr->hostname);
wr->hostname = NULL;
conf_free(wr->conf);
wr->conf = NULL;
}
static void preconn_cmdplug_log(
Plug *p, Socket *s, PlugLogType type, SockAddr *addr,
int port, const char *error_msg, int error_code)
{
PreConnWrapper *wr = container_of(p, PreConnWrapper, cmdplugimpl);
switch (type) {
case PLUGLOG_CONNECT_TRYING:
case PLUGLOG_CONNECT_FAILED:
case PLUGLOG_CONNECT_SUCCESS:
/* Ignore these messages from the subcommand. We don't expect
* to receive them anyway, and if we did, they'd be boring. */
break;
case PLUGLOG_PROXY_MSG:
/* But this contains the stderr from the subcommand, so pass
* it on. */
plug_log(wr->plug, &wr->sockimpl, type, addr, port,
error_msg, error_code);
break;
}
}
static void preconn_cmdplug_closing(
Plug *p, PlugCloseType type, const char *error_msg)
{
PreConnWrapper *wr = container_of(p, PreConnWrapper, cmdplugimpl);
switch (type) {
case PLUGCLOSE_NORMAL:
/* We don't really care when stdout from the subprocess
* closes. Our trigger for moving on to the main connection is
* the subprocess itself exiting. */
break;
case PLUGCLOSE_ERROR:
case PLUGCLOSE_BROKEN_PIPE:
/* But if it terminates _confusedly_, at least log that. */
logeventf(wr->logctx, "Error reading output from "
"pre-connect command: %s", error_msg);
break;
case PLUGCLOSE_USER_ABORT:
/* Hard to imagine how a subprocess socket might produce this,
* but if it ever does grow a means of doing so, we should
* interpret it as a request to abort the whole connection. */
plug_closing(wr->plug, type, error_msg);
break;
}
}
static void preconn_subproc_terminated(
void *vctx, int exittype, uint32_t exitdata)
{
PreConnWrapper *wr = (PreConnWrapper *)vctx;
switch (exittype) {
case EXITTYPE_NORMAL:
logeventf(wr->logctx, "Pre-connect command exited with status %"PRIu32,
exitdata);
break;
case EXITTYPE_SIGNAL:
logeventf(wr->logctx, "Pre-connect command killed by signal %"PRIu32,
exitdata);
break;
}
queue_toplevel_callback(preconn_open_mainsocket, wr);
}
static void preconn_cmdplug_receive(
Plug *p, int urgent, const char *data, size_t len)
{
PreConnWrapper *wr = container_of(p, PreConnWrapper, cmdplugimpl);
log_proxy_stderr(wr->plug, &wr->sockimpl, &wr->psb, data, len);
}
static void preconn_cmdplug_sent(Plug *p, size_t bufsize)
{
}
static void preconn_mainplug_log(
Plug *p, Socket *s, PlugLogType type, SockAddr *addr, int port,
const char *error_msg, int error_code)
{
PreConnWrapper *wr = container_of(p, PreConnWrapper, mainplugimpl);
plug_log(wr->plug, &wr->sockimpl, type, addr, port,
error_msg, error_code);
}
static void preconn_mainplug_closing(
Plug *p, PlugCloseType type, const char *error_msg)
{
PreConnWrapper *wr = container_of(p, PreConnWrapper, mainplugimpl);
plug_closing(wr->plug, type, error_msg);
}
static void preconn_mainplug_receive(
Plug *p, int urgent, const char *data, size_t len)
{
PreConnWrapper *wr = container_of(p, PreConnWrapper, mainplugimpl);
plug_receive(wr->plug, urgent, data, len);
}
static void preconn_mainplug_sent(Plug *p, size_t bufsize)
{
PreConnWrapper *wr = container_of(p, PreConnWrapper, mainplugimpl);
plug_sent(wr->plug, bufsize);
}
static int preconn_never_accepting(Plug *p, accept_fn_t constructor,
accept_ctx_t ctx)
{
unreachable("new_main_connection never creates a listening Socket");
}
static const PlugVtable preconn_cmd_plugvt = {
.log = preconn_cmdplug_log,
.closing = preconn_cmdplug_closing,
.receive = preconn_cmdplug_receive,
.sent = preconn_cmdplug_sent,
.accepting = preconn_never_accepting,
};
static const PlugVtable preconn_main_plugvt = {
.log = preconn_mainplug_log,
.closing = preconn_mainplug_closing,
.receive = preconn_mainplug_receive,
.sent = preconn_mainplug_sent,
.accepting = preconn_never_accepting,
};
static Plug *preconn_plug(Socket *s, Plug *p)
{
PreConnWrapper *wr = container_of(s, PreConnWrapper, sockimpl);
Plug *prev = wr->plug;
if (p)
wr->plug = p;
return prev;
}
static void preconn_close(Socket *s)
{
PreConnWrapper *wr = container_of(s, PreConnWrapper, sockimpl);
if (wr->preconn)
sk_close(wr->preconn);
if (wr->waiter)
subproc_waiter_free(wr->waiter);
if (wr->mainsocket)
sk_close(wr->mainsocket);
if (wr->addr)
sk_addr_free(wr->addr);
if (wr->hostname)
sfree(wr->hostname);
if (wr->conf)
conf_free(wr->conf);
bufchain_clear(&wr->writebuf);
bufchain_clear(&wr->oobbuf);
delete_callbacks_for_context(wr);
sfree(wr);
}
static size_t preconn_write(Socket *s, const void *data, size_t len)
{
PreConnWrapper *wr = container_of(s, PreConnWrapper, sockimpl);
if (wr->mainsocket)
return sk_write(wr->mainsocket, data, len);
bufchain_add(&wr->writebuf, data, len);
return bufchain_size(&wr->writebuf);
}
static size_t preconn_write_oob(Socket *s, const void *data, size_t len)
{
PreConnWrapper *wr = container_of(s, PreConnWrapper, sockimpl);
if (wr->mainsocket)
return sk_write_oob(wr->mainsocket, data, len);
bufchain_clear(&wr->writebuf);
bufchain_clear(&wr->oobbuf);
bufchain_add(&wr->oobbuf, data, len);
return bufchain_size(&wr->oobbuf);
}
static void preconn_write_eof(Socket *s)
{
PreConnWrapper *wr = container_of(s, PreConnWrapper, sockimpl);
if (wr->mainsocket) {
sk_write_eof(wr->mainsocket);
} else {
wr->pending_eof = true;
}
}
static void preconn_set_frozen(Socket *s, bool is_frozen)
{
PreConnWrapper *wr = container_of(s, PreConnWrapper, sockimpl);
if (wr->mainsocket) {
sk_set_frozen(wr->mainsocket, is_frozen);
} else {
wr->frozen = is_frozen;
}
}
static const char *preconn_socket_error(Socket *s)
{
PreConnWrapper *wr = container_of(s, PreConnWrapper, sockimpl);
if (wr->mainsocket)
return sk_socket_error(wr->mainsocket);
return NULL;
}
static SocketEndpointInfo *preconn_endpoint_info(Socket *s, bool peer)
{
PreConnWrapper *wr = container_of(s, PreConnWrapper, sockimpl);
if (wr->mainsocket)
return sk_endpoint_info(wr->mainsocket, peer);
return NULL;
}
static const SocketVtable preconn_sockvt = {
.plug = preconn_plug,
.close = preconn_close,
.write = preconn_write,
.write_oob = preconn_write_oob,
.write_eof = preconn_write_eof,
.set_frozen = preconn_set_frozen,
.socket_error = preconn_socket_error,
.endpoint_info = preconn_endpoint_info,
};
#endif
Socket *new_main_connection(
SockAddr *addr, const char *hostname, int port, bool privport,
bool oobinline, bool nodelay, bool keepalive, Plug *plug, Conf *conf,
Interactor *itr, LogContext *logctx)
{
#ifndef WINSCP
char *command = NULL;
const char *template = conf_get_str(conf, CONF_pre_connect_command);
assert(template);
if (*template) {
Conf *tmpconf = conf_copy(conf);
conf_set_str(conf, CONF_proxy_username, "");
conf_set_str(conf, CONF_proxy_password, "");
unsigned flags;
command = format_connection_setup_command(
template, addr, port, tmpconf, &flags);
if (flags & (TELNET_CMD_MISSING_USERNAME |
TELNET_CMD_MISSING_PASSWORD)) {
logeventf(logctx, "Pre-connect command: references to %%user or "
"%%pass not supported");
sfree(command);
command = NULL;
}
conf_free(tmpconf);
}
if (!command) {
#endif
/* No pre-connection command: just open the main socket immediately */
return new_connection(addr, hostname, port, privport, oobinline,
nodelay, keepalive, plug, conf, itr);
#ifndef WINSCP
}
PreConnWrapper *wr = snew(PreConnWrapper);
memset(wr, 0, sizeof(PreConnWrapper));
wr->plug = plug;
wr->addr = addr; /* we now own this */
wr->hostname = dupstr(hostname);
wr->port = port;
wr->privport = privport;
wr->oobinline = oobinline;
wr->nodelay = nodelay;
wr->keepalive = keepalive;
wr->conf = conf_copy(conf);
wr->itr = itr;
wr->logctx = logctx;
psb_init(&wr->psb);
psb_set_prefix(&wr->psb, "pre-connect command stdout");
bufchain_init(&wr->writebuf);
bufchain_init(&wr->oobbuf);
wr->cmdplugimpl.vt = &preconn_cmd_plugvt;
wr->mainplugimpl.vt = &preconn_main_plugvt;
wr->sockimpl.vt = &preconn_sockvt;
logeventf(logctx, "Running pre-connect command: %s", command);
wr->preconn = platform_start_subprocess(
command, &wr->cmdplugimpl, "pre-connect command stderr", &wr->waiter);
subproc_waiter_set_callback(wr->waiter, preconn_subproc_terminated, wr);
sk_write_eof(wr->preconn);
sfree(command);
return &wr->sockimpl;
#endif
}