1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-08 08:58:00 +00:00

Auxiliary application: 'psocks', a simple SOCKS server.

This is built more or less entirely out of pieces I already had. The
SOCKS server code is provided by the dynamic forwarding code in
portfwd.c. When that accepts a connection request, it wants to talk to
an SSH ConnectionLayer, which is already a trait with interchangeable
implementations - so I just provide one of my own which only supports
the lportfwd_open() method. And that in turn returns an SshChannel
object, with a special trait implementation all of whose methods
just funnel back to an ordinary Socket.

Result: you get a Socket-to-Socket SOCKS implementation with no SSH
anywhere, and even a minimal amount of need to _pretend_ internally to
be an SSH implementation.

Additional features include the ability to log all the traffic in the
form of diagnostics to standard error, or log each direction of each
connection separately to a file, or for anything more general, to log
each direction of each connection through a pipe to a subcommand that
can filter out whatever you think are the interesting parts. Also, you
can spawn a subcommand after the SOCKS server is set up, and terminate
automatically when that subcommand does - e.g. you might use this to
wrap the execution of a single SOCKS-using program.

This is a modernisation of a diagnostic utility I've had kicking
around out-of-tree for a long time. With all of last year's
refactorings, it now becomes feasible to keep it in-tree without
needing huge amounts of scaffolding. Also, this version runs on
Windows, which is more than the old one did. (On Windows I haven't
implemented the subprocess parts, although there's no reason I
_couldn't_.)

As well as diagnostic uses, this may also be useful in some situations
as a thing to forward ports to: PuTTY doesn't currently support
reverse dynamic port forwarding (in which the remote listening port
acts as a SOCKS server), but you could get the same effect by
forwarding a remote port to a local instance of this. (Although, of
course, that's nothing you couldn't achieve using any other SOCKS
server.)
This commit is contained in:
Simon Tatham 2020-02-23 16:27:04 +00:00
parent 5a9bfca3d5
commit 1b40d9f3ba
6 changed files with 870 additions and 2 deletions

1
.gitignore vendored
View File

@ -40,6 +40,7 @@
/psusan
/osxlaunch
/uppity
/psocks
/unix/PuTTY.app
/unix/Pterm.app
/fuzzterm

11
Recipe
View File

@ -411,15 +411,22 @@ uppity : [UT] uxserver SSHSERVER UXMISC uxsignal uxnoise uxgss uxnogtk
psusan : [UT] uxpsusan SSHSERVER UXMISC uxsignal uxnoise nogss uxnogtk
+ uxpty uxsftpserver ux_x11 uxagentsock procnet uxcliloop
PSOCKS = psocks portfwd conf sshutils logging proxy nocproxy timing callback
+ time tree234 version errsock be_misc norand MISC
psocks : [C] PSOCKS winsocks wincons winproxy winnet winmisc winselcli
+ winhsock winhandl winmiscs winnohlp wincliloop LIBS
psocks : [UT] PSOCKS uxsocks uxcons uxproxy uxnet uxmisc uxpoll uxsel uxnogtk
+ uxpeer uxfdsock uxcliloop uxsignal
# ----------------------------------------------------------------------
# On Windows, provide a means of removing local test binaries that we
# aren't going to actually ship. (I prefer this to not building them
# in the first place, so that we find out about build breakage early.)
!begin vc
cleantestprogs:
-del $(BUILDDIR)testcrypt.exe
-del $(BUILDDIR)testcrypt.exe $(BUILDDIR)psocks.exe
!end
!begin clangcl
cleantestprogs:
-rm -f $(BUILDDIR)testcrypt.exe
-rm -f $(BUILDDIR)testcrypt.exe $(BUILDDIR)psocks.exe
!end

630
psocks.c Normal file
View File

@ -0,0 +1,630 @@
/*
* Platform-independent parts of a standalone SOCKS server program
* based on the PuTTY SOCKS code.
*/
#include <string.h>
#include <errno.h>
#include "putty.h"
#include "ssh.h"
#include "sshchan.h"
#include "psocks.h"
/*
* Possible later TODOs:
*
* - verbosity setting for log messages
*
* - could import proxy.c and use name_lookup rather than
* sk_namelookup, to allow forwarding via some other proxy type
*/
#define BUFLIMIT 16384
#define LOGBITS(X) \
X(CONNSTATUS) \
X(DIALOGUE) \
/* end of list */
#define BITINDEX_ENUM(x) LOG_##x##_bitindex,
enum { LOGBITS(BITINDEX_ENUM) };
#define BITFLAG_ENUM(x) LOG_##x = 1 << LOG_##x##_bitindex,
enum { LOGBITS(BITFLAG_ENUM) };
typedef struct psocks_connection psocks_connection;
typedef enum RecordDestination {
REC_NONE, REC_FILE, REC_PIPE
} RecordDestination;
struct psocks_state {
const PsocksPlatform *platform;
int listen_port;
bool acceptall;
PortFwdManager *portfwdmgr;
uint64_t next_conn_index;
FILE *logging_fp;
unsigned log_flags;
RecordDestination rec_dest;
char *rec_cmd;
strbuf *subcmd;
ConnectionLayer cl;
};
struct psocks_connection {
psocks_state *ps;
Channel *chan;
char *host, *realhost;
int port;
SockAddr *addr;
Socket *socket;
bool connecting, eof_pfmgr_to_socket, eof_socket_to_pfmgr;
uint64_t index;
PsocksDataSink *rec_sink;
Plug plug;
SshChannel sc;
};
static SshChannel *psocks_lportfwd_open(
ConnectionLayer *cl, const char *hostname, int port,
const char *description, const SocketPeerInfo *pi, Channel *chan);
static const struct ConnectionLayerVtable psocks_clvt = {
NULL /* rportfwd_alloc */,
NULL /* rportfwd_remove */,
psocks_lportfwd_open,
NULL /* session_open */,
NULL /* serverside_x11_open */,
NULL /* serverside_agent_open */,
NULL /* add_x11_display */,
NULL /* add_sharing_x11_display */,
NULL /* remove_sharing_x11_display */,
NULL /* send_packet_from_downstream */,
NULL /* alloc_sharing_channel */,
NULL /* delete_sharing_channel */,
NULL /* sharing_queue_global_request */,
NULL /* sharing_no_more_downstreams */,
NULL /* agent_forwarding_permitted */,
NULL /* terminal_size */,
NULL /* stdout_unthrottle */,
NULL /* stdin_backlog */,
NULL /* throttle_all_channels */,
NULL /* ldisc_option */,
NULL /* set_ldisc_option */,
NULL /* enable_x_fwd */,
NULL /* enable_agent_fwd */,
NULL /* set_wants_user_input */,
};
static size_t psocks_sc_write(SshChannel *sc, bool is_stderr, const void *,
size_t);
static void psocks_sc_write_eof(SshChannel *sc);
static void psocks_sc_initiate_close(SshChannel *sc, const char *err);
static void psocks_sc_unthrottle(SshChannel *sc, size_t bufsize);
static const struct SshChannelVtable psocks_scvt = {
psocks_sc_write,
psocks_sc_write_eof,
psocks_sc_initiate_close,
psocks_sc_unthrottle,
NULL /* get_conf */,
NULL /* window_override_removed */,
NULL /* x11_sharing_handover */,
NULL /* send_exit_status */,
NULL /* send_exit_signal */,
NULL /* send_exit_signal_numeric */,
NULL /* request_x11_forwarding */,
NULL /* request_agent_forwarding */,
NULL /* request_pty */,
NULL /* send_env_var */,
NULL /* start_shell */,
NULL /* start_command */,
NULL /* start_subsystem */,
NULL /* send_serial_break */,
NULL /* send_signal */,
NULL /* send_terminal_size_change */,
NULL /* hint_channel_is_simple */,
};
static void psocks_plug_log(Plug *p, PlugLogType type, SockAddr *addr,
int port, const char *error_msg, int error_code);
static void psocks_plug_closing(Plug *p, const char *error_msg,
int error_code, bool calling_back);
static void psocks_plug_receive(Plug *p, int urgent,
const char *data, size_t len);
static void psocks_plug_sent(Plug *p, size_t bufsize);
static const PlugVtable psocks_plugvt = {
psocks_plug_log,
psocks_plug_closing,
psocks_plug_receive,
psocks_plug_sent,
NULL /* accepting */,
};
static void psocks_conn_log(psocks_connection *conn, const char *fmt, ...)
{
if (!conn->ps->logging_fp)
return;
va_list ap;
va_start(ap, fmt);
char *msg = dupvprintf(fmt, ap);
va_end(ap);
fprintf(conn->ps->logging_fp, "c#%"PRIu64": %s\n", conn->index, msg);
sfree(msg);
fflush(conn->ps->logging_fp);
}
static void print_c_string(FILE *fp, const char *data, size_t len)
{
while (len--) {
char c = *data++;
if (c == '\n')
fputs("\\n", fp);
else if (c == '\r')
fputs("\\r", fp);
else if (c == '\t')
fputs("\\t", fp);
else if (c == '\b')
fputs("\\b", fp);
else if (c == '\\')
fputs("\\\\", fp);
else if (c == '"')
fputs("\\\"", fp);
else if (c >= 32 && c <= 126)
fputc(c, fp);
else
fprintf(fp, "\\%03o", (unsigned char)c);
}
}
static void psocks_conn_log_data(psocks_connection *conn, PsocksDirection dir,
const void *vdata, size_t len)
{
if ((conn->ps->log_flags & LOG_DIALOGUE) && conn->ps->logging_fp) {
const char *data = vdata;
while (len > 0) {
const char *nl = memchr(data, '\n', len);
size_t thislen = nl ? (nl+1) - data : len;
const char *thisdata = data;
data += thislen;
len -= thislen;
static const char *const direction_names[2] = {
[UP] = "send", [DN] = "recv" };
fprintf(conn->ps->logging_fp, "c#%"PRIu64": %s \"", conn->index,
direction_names[dir]);
print_c_string(conn->ps->logging_fp, thisdata, thislen);
fprintf(conn->ps->logging_fp, "\"\n");
}
fflush(conn->ps->logging_fp);
}
if (conn->rec_sink)
put_data(conn->rec_sink->s[dir], vdata, len);
}
static void psocks_connection_establish(void *vctx);
static SshChannel *psocks_lportfwd_open(
ConnectionLayer *cl, const char *hostname, int port,
const char *description, const SocketPeerInfo *pi, Channel *chan)
{
psocks_state *ps = container_of(cl, psocks_state, cl);
psocks_connection *conn = snew(psocks_connection);
memset(conn, 0, sizeof(*conn));
conn->ps = ps;
conn->sc.vt = &psocks_scvt;
conn->plug.vt = &psocks_plugvt;
conn->chan = chan;
conn->host = dupstr(hostname);
conn->port = port;
conn->index = ps->next_conn_index++;
if (conn->ps->log_flags & LOG_CONNSTATUS)
psocks_conn_log(conn, "request from %s for %s port %d",
pi->log_text, hostname, port);
switch (conn->ps->rec_dest) {
case REC_FILE:
{
char *fnames[2];
FILE *fp[2];
bool ok = true;
static const char *const direction_names[2] = {
[UP] = "sockout", [DN] = "sockin" };
for (size_t i = 0; i < 2; i++) {
fnames[i] = dupprintf("%s.%"PRIu64, direction_names[i],
conn->index);
fp[i] = fopen(fnames[i], "wb");
if (!fp[i]) {
psocks_conn_log(conn, "cannot log this connection: "
"creating file '%s': %s",
fnames[i], strerror(errno));
ok = false;
}
}
if (ok) {
if (conn->ps->log_flags & LOG_CONNSTATUS)
psocks_conn_log(conn, "logging to '%s' / '%s'",
fnames[0], fnames[1]);
conn->rec_sink = pds_stdio(fp);
} else {
for (size_t i = 0; i < 2; i++) {
if (fp[i]) {
remove(fnames[i]);
fclose(fp[i]);
}
}
}
for (size_t i = 0; i < 2; i++)
sfree(fnames[i]);
}
break;
case REC_PIPE:
{
static const char *const direction_args[2] = {
[UP] = "out", [DN] = "in" };
char *index_arg = dupprintf("%"PRIu64, conn->index);
char *err;
conn->rec_sink = conn->ps->platform->open_pipes(
conn->ps->rec_cmd, direction_args, index_arg, &err);
if (!conn->rec_sink) {
psocks_conn_log(conn, "cannot log this connection: "
"creating pipes: %s", err);
sfree(err);
}
sfree(index_arg);
}
break;
default:
break;
}
queue_toplevel_callback(psocks_connection_establish, conn);
return &conn->sc;
}
static void psocks_conn_free(psocks_connection *conn)
{
if (conn->ps->log_flags & LOG_CONNSTATUS)
psocks_conn_log(conn, "closed");
sfree(conn->host);
sfree(conn->realhost);
if (conn->socket)
sk_close(conn->socket);
if (conn->chan)
chan_free(conn->chan);
if (conn->rec_sink)
pds_free(conn->rec_sink);
delete_callbacks_for_context(conn);
sfree(conn);
}
static void psocks_connection_establish(void *vctx)
{
psocks_connection *conn = (psocks_connection *)vctx;
/*
* Look up destination host name.
*/
conn->addr = sk_namelookup(conn->host, &conn->realhost, ADDRTYPE_UNSPEC);
const char *err = sk_addr_error(conn->addr);
if (err) {
char *msg = dupprintf("name lookup failed: %s", err);
chan_open_failed(conn->chan, msg);
sfree(msg);
psocks_conn_free(conn);
return;
}
/*
* Make the connection.
*/
conn->connecting = true;
conn->socket = sk_new(conn->addr, conn->port, false, false, false, false,
&conn->plug);
}
static size_t psocks_sc_write(SshChannel *sc, bool is_stderr,
const void *data, size_t len)
{
psocks_connection *conn = container_of(sc, psocks_connection, sc);
if (!conn->socket) return 0;
psocks_conn_log_data(conn, UP, data, len);
return sk_write(conn->socket, data, len);
}
static void psocks_check_close(void *vctx)
{
psocks_connection *conn = (psocks_connection *)vctx;
if (chan_want_close(conn->chan, conn->eof_pfmgr_to_socket,
conn->eof_socket_to_pfmgr))
psocks_conn_free(conn);
}
static void psocks_sc_write_eof(SshChannel *sc)
{
psocks_connection *conn = container_of(sc, psocks_connection, sc);
if (!conn->socket) return;
sk_write_eof(conn->socket);
conn->eof_pfmgr_to_socket = true;
if (conn->ps->log_flags & LOG_DIALOGUE)
psocks_conn_log(conn, "send eof");
queue_toplevel_callback(psocks_check_close, conn);
}
static void psocks_sc_initiate_close(SshChannel *sc, const char *err)
{
psocks_connection *conn = container_of(sc, psocks_connection, sc);
sk_close(conn->socket);
conn->socket = NULL;
}
static void psocks_sc_unthrottle(SshChannel *sc, size_t bufsize)
{
psocks_connection *conn = container_of(sc, psocks_connection, sc);
if (bufsize < BUFLIMIT)
sk_set_frozen(conn->socket, false);
}
static void psocks_plug_log(Plug *plug, PlugLogType type, SockAddr *addr,
int port, const char *error_msg, int error_code)
{
psocks_connection *conn = container_of(plug, psocks_connection, plug);
char addrbuf[256];
if (!(conn->ps->log_flags & LOG_CONNSTATUS))
return;
switch (type) {
case PLUGLOG_CONNECT_TRYING:
sk_getaddr(addr, addrbuf, sizeof(addrbuf));
if (sk_addr_needs_port(addr))
psocks_conn_log(conn, "trying to connect to %s port %d",
addrbuf, port);
else
psocks_conn_log(conn, "trying to connect to %s", addrbuf);
break;
case PLUGLOG_CONNECT_FAILED:
psocks_conn_log(conn, "connection attempt failed: %s", error_msg);
break;
case PLUGLOG_CONNECT_SUCCESS:
psocks_conn_log(conn, "connection established", error_msg);
if (conn->connecting) {
chan_open_confirmation(conn->chan);
conn->connecting = false;
}
break;
case PLUGLOG_PROXY_MSG:
psocks_conn_log(conn, "connection setup: %s", error_msg);
break;
};
}
static void psocks_plug_closing(Plug *plug, const char *error_msg,
int error_code, bool calling_back)
{
psocks_connection *conn = container_of(plug, psocks_connection, plug);
if (conn->connecting) {
if (conn->ps->log_flags & LOG_CONNSTATUS)
psocks_conn_log(conn, "unable to connect: %s", error_msg);
chan_open_failed(conn->chan, error_msg);
conn->eof_socket_to_pfmgr = true;
conn->eof_pfmgr_to_socket = true;
conn->connecting = false;
} else {
if (conn->ps->log_flags & LOG_DIALOGUE)
psocks_conn_log(conn, "recv eof");
chan_send_eof(conn->chan);
conn->eof_socket_to_pfmgr = true;
}
queue_toplevel_callback(psocks_check_close, conn);
}
static void psocks_plug_receive(Plug *plug, int urgent,
const char *data, size_t len)
{
psocks_connection *conn = container_of(plug, psocks_connection, plug);
size_t bufsize = chan_send(conn->chan, false, data, len);
sk_set_frozen(conn->socket, bufsize > BUFLIMIT);
psocks_conn_log_data(conn, DN, data, len);
}
static void psocks_plug_sent(Plug *plug, size_t bufsize)
{
psocks_connection *conn = container_of(plug, psocks_connection, plug);
sk_set_frozen(conn->socket, bufsize > BUFLIMIT);
}
psocks_state *psocks_new(const PsocksPlatform *platform)
{
psocks_state *ps = snew(psocks_state);
memset(ps, 0, sizeof(*ps));
ps->listen_port = 1080;
ps->acceptall = false;
ps->cl.vt = &psocks_clvt;
ps->portfwdmgr = portfwdmgr_new(&ps->cl);
ps->logging_fp = stderr; /* could make this configurable later */
ps->log_flags = LOG_CONNSTATUS;
ps->rec_dest = REC_NONE;
ps->platform = platform;
ps->subcmd = strbuf_new();
return ps;
}
void psocks_free(psocks_state *ps)
{
portfwdmgr_free(ps->portfwdmgr);
strbuf_free(ps->subcmd);
sfree(ps->rec_cmd);
sfree(ps);
}
void psocks_cmdline(psocks_state *ps, int argc, char **argv)
{
bool doing_opts = true;
bool accumulating_exec_args = false;
size_t args_seen = 0;
while (--argc > 0) {
const char *p = *++argv;
if (doing_opts && p[0] == '-' && p[1]) {
if (!strcmp(p, "--")) {
doing_opts = false;
} else if (!strcmp(p, "-g")) {
ps->acceptall = true;
} else if (!strcmp(p, "-d")) {
ps->log_flags |= LOG_DIALOGUE;
} else if (!strcmp(p, "-f")) {
ps->rec_dest = REC_FILE;
} else if (!strcmp(p, "-p")) {
if (!ps->platform->open_pipes) {
fprintf(stderr, "psocks: '-p' is not supported on this "
"platform\n");
exit(1);
}
if (--argc > 0) {
ps->rec_cmd = dupstr(*++argv);
} else {
fprintf(stderr, "psocks: expected an argument to '-p'\n");
exit(1);
}
ps->rec_dest = REC_PIPE;
} else if (!strcmp(p, "--exec")) {
if (!ps->platform->start_subcommand) {
fprintf(stderr, "psocks: running a subcommand is not "
"supported on this platform\n");
exit(1);
}
accumulating_exec_args = true;
/* Now consume all further argv words for the
* subcommand, even if they look like options */
doing_opts = false;
} else if (!strcmp(p, "--help")) {
printf("usage: psocks [ -d | -f");
if (ps->platform->open_pipes)
printf(" | -p pipe-cmd");
printf(" ] [ -g ] port-number");
printf("\n");
printf("where: -d log all connection contents to"
" standard output\n");
printf(" -f record each half-connection to "
"a file sockin.N/sockout.N\n");
if (ps->platform->open_pipes)
printf(" -p pipe-cmd pipe each half-connection"
" to 'pipe-cmd [in|out] N'\n");
printf(" -g accept connections from anywhere,"
" not just localhost\n");
if (ps->platform->start_subcommand)
printf(" --exec subcmd [args...] run command, and "
"terminate when it exits\n");
printf(" port-number listen on this port"
" (default 1080)\n");
printf("also: psocks --help display this help text\n");
exit(0);
} else {
fprintf(stderr, "psocks: unrecognised option '%s'\n", p);
exit(1);
}
} else {
if (accumulating_exec_args) {
put_asciz(ps->subcmd, p);
} else switch (args_seen++) {
case 0:
ps->listen_port = atoi(p);
break;
default:
fprintf(stderr, "psocks: unexpected extra argument '%s'\n", p);
exit(1);
break;
}
}
}
}
void psocks_start(psocks_state *ps)
{
Conf *conf = conf_new();
conf_set_bool(conf, CONF_lport_acceptall, ps->acceptall);
char *key = dupprintf("AL%d", ps->listen_port);
conf_set_str_str(conf, CONF_portfwd, key, "D");
sfree(key);
portfwdmgr_config(ps->portfwdmgr, conf);
if (ps->subcmd->len)
ps->platform->start_subcommand(ps->subcmd);
conf_free(conf);
}
/*
* Some stubs that are needed to link against PuTTY modules.
*/
int verify_host_key(const char *hostname, int port,
const char *keytype, const char *key)
{
unreachable("host keys not handled in this tool");
}
void store_host_key(const char *hostname, int port,
const char *keytype, const char *key)
{
unreachable("host keys not handled in this tool");
}
/*
* stdio-targeted PsocksDataSink.
*/
typedef struct PsocksDataSinkStdio {
stdio_sink sink[2];
PsocksDataSink pds;
} PsocksDataSinkStdio;
static void stdio_free(PsocksDataSink *pds)
{
PsocksDataSinkStdio *pdss = container_of(pds, PsocksDataSinkStdio, pds);
for (size_t i = 0; i < 2; i++)
fclose(pdss->sink[i].fp);
sfree(pdss);
}
PsocksDataSink *pds_stdio(FILE *fp[2])
{
PsocksDataSinkStdio *pdss = snew(PsocksDataSinkStdio);
for (size_t i = 0; i < 2; i++) {
setvbuf(fp[i], NULL, _IONBF, 0);
stdio_sink_init(&pdss->sink[i], fp[i]);
pdss->pds.s[i] = BinarySink_UPCAST(&pdss->sink[i]);
}
pdss->pds.free = stdio_free;
return &pdss->pds;
}

28
psocks.h Normal file
View File

@ -0,0 +1,28 @@
typedef struct psocks_state psocks_state;
typedef struct PsocksPlatform PsocksPlatform;
typedef struct PsocksDataSink PsocksDataSink;
/* indices into PsocksDataSink arrays */
typedef enum PsocksDirection { UP, DN } PsocksDirection;
typedef struct PsocksDataSink {
void (*free)(PsocksDataSink *);
BinarySink *s[2];
} PsocksDataSink;
static inline void pds_free(PsocksDataSink *pds)
{ pds->free(pds); }
PsocksDataSink *pds_stdio(FILE *fp[2]);
struct PsocksPlatform {
PsocksDataSink *(*open_pipes)(
const char *cmd, const char *const *direction_args,
const char *index_arg, char **err);
void (*start_subcommand)(strbuf *args);
};
psocks_state *psocks_new(const PsocksPlatform *);
void psocks_free(psocks_state *ps);
void psocks_cmdline(psocks_state *ps, int argc, char **argv);
void psocks_start(psocks_state *ps);

178
unix/uxsocks.c Normal file
View File

@ -0,0 +1,178 @@
/*
* Main program for Unix psocks.
*/
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include "putty.h"
#include "ssh.h"
#include "psocks.h"
const bool buildinfo_gtk_relevant = false;
typedef struct PsocksDataSinkPopen {
stdio_sink sink[2];
PsocksDataSink pds;
} PsocksDataSinkPopen;
static void popen_free(PsocksDataSink *pds)
{
PsocksDataSinkPopen *pdsp = container_of(pds, PsocksDataSinkPopen, pds);
for (size_t i = 0; i < 2; i++)
pclose(pdsp->sink[i].fp);
sfree(pdsp);
}
static PsocksDataSink *open_pipes(
const char *cmd, const char *const *direction_args,
const char *index_arg, char **err)
{
FILE *fp[2];
char *errmsg = NULL;
for (size_t i = 0; i < 2; i++) {
/* No escaping needed: the provided command is already
* shell-quoted, and our extra arguments are simple */
char *command = dupprintf("%s %s %s", cmd,
direction_args[i], index_arg);
fp[i] = popen(command, "w");
sfree(command);
if (!fp[i]) {
if (!errmsg)
errmsg = dupprintf("%s", strerror(errno));
}
}
if (errmsg) {
for (size_t i = 0; i < 2; i++)
if (fp[i])
pclose(fp[i]);
*err = errmsg;
return NULL;
}
PsocksDataSinkPopen *pdsp = snew(PsocksDataSinkPopen);
for (size_t i = 0; i < 2; i++) {
setvbuf(fp[i], NULL, _IONBF, 0);
stdio_sink_init(&pdsp->sink[i], fp[i]);
pdsp->pds.s[i] = BinarySink_UPCAST(&pdsp->sink[i]);
}
pdsp->pds.free = popen_free;
return &pdsp->pds;
}
static int signalpipe[2] = { -1, -1 };
static void sigchld(int signum)
{
if (write(signalpipe[1], "x", 1) <= 0)
/* not much we can do about it */;
}
static pid_t subcommand_pid = -1;
static bool still_running = true;
static void start_subcommand(strbuf *args)
{
pid_t pid;
/*
* Set up the pipe we'll use to tell us about SIGCHLD.
*/
if (pipe(signalpipe) < 0) {
perror("pipe");
exit(1);
}
putty_signal(SIGCHLD, sigchld);
/*
* Make an array of argument pointers that execvp will like.
*/
size_t nargs = 0;
for (size_t i = 0; i < args->len; i++)
if (args->s[i] == '\0')
nargs++;
char **exec_args = snewn(nargs + 1, char *);
char *p = args->s;
for (size_t a = 0; a < nargs; a++) {
exec_args[a] = p;
size_t len = strlen(p);
assert(len < args->len - (p - args->s));
p += 1 + len;
}
exec_args[nargs] = NULL;
pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
} else if (pid == 0) {
execvp(exec_args[0], exec_args);
perror("exec");
_exit(127);
} else {
subcommand_pid = pid;
sfree(exec_args);
}
}
static const PsocksPlatform platform = {
open_pipes,
start_subcommand,
};
static bool psocks_pw_setup(void *ctx, pollwrapper *pw)
{
if (signalpipe[0] >= 0)
pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R);
return true;
}
static void psocks_pw_check(void *ctx, pollwrapper *pw)
{
if (signalpipe[0] >= 0 &&
pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) {
while (true) {
int status;
pid_t pid = waitpid(-1, &status, WNOHANG);
if (pid <= 0)
break;
if (pid == subcommand_pid)
still_running = false;
}
}
}
static bool psocks_continue(void *ctx, bool found_any_fd,
bool ran_any_callback)
{
return still_running;
}
typedef bool (*cliloop_continue_t)(void *ctx, bool found_any_fd,
bool ran_any_callback);
int main(int argc, char **argv)
{
psocks_state *ps = psocks_new(&platform);
psocks_cmdline(ps, argc, argv);
sk_init();
uxsel_init();
psocks_start(ps);
cli_main_loop(psocks_pw_setup, psocks_pw_check, psocks_continue, NULL);
}

24
windows/winsocks.c Normal file
View File

@ -0,0 +1,24 @@
/*
* Main program for Windows psocks.
*/
#include "putty.h"
#include "ssh.h"
#include "psocks.h"
static const PsocksPlatform platform = {
NULL /* open_pipes */,
NULL /* start_subcommand */,
};
int main(int argc, char **argv)
{
psocks_state *ps = psocks_new(&platform);
psocks_cmdline(ps, argc, argv);
sk_init();
winselcli_setup();
psocks_start(ps);
cli_main_loop(cliloop_null_pre, cliloop_null_post, NULL);
}