# Miscellaneous objects appearing in all the network utilities (not
# Pageant or PuTTYgen).
-MISC = timing callback misc version settings tree234 proxy conf
+MISC = timing callback misc version settings tree234 proxy conf be_misc
WINMISC = MISC winstore winnet winhandl cmdline windefs winmisc winproxy
+ wintime winhsock errsock winsecur
UXMISC = MISC uxstore uxsel uxnet uxpeer cmdline uxmisc uxproxy time
--- /dev/null
+/*
+ * be_misc.c: helper functions shared between main network backends.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#define DEFINE_PLUG_METHOD_MACROS
+#include "putty.h"
+#include "network.h"
+
+void backend_socket_log(void *frontend, int type, SockAddr addr, int port,
+ const char *error_msg, int error_code, Conf *conf,
+ int session_started)
+{
+ char addrbuf[256], *msg;
+
+ switch (type) {
+ case 0:
+ sk_getaddr(addr, addrbuf, lenof(addrbuf));
+ if (sk_addr_needs_port(addr)) {
+ msg = dupprintf("Connecting to %s port %d", addrbuf, port);
+ } else {
+ msg = dupprintf("Connecting to %s", addrbuf);
+ }
+ break;
+ case 1:
+ sk_getaddr(addr, addrbuf, lenof(addrbuf));
+ msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);
+ break;
+ case 2:
+ /* Proxy-related log messages have their own identifying
+ * prefix already, put on by our caller. */
+ {
+ int len, log_to_term;
+
+ /* Suffix \r\n temporarily, so we can log to the terminal. */
+ msg = dupprintf("%s\r\n", error_msg);
+ len = strlen(msg);
+ assert(len >= 2);
+
+ log_to_term = conf_get_int(conf, CONF_proxy_log_to_term);
+ if (log_to_term == AUTO)
+ log_to_term = session_started ? FORCE_OFF : FORCE_ON;
+ if (log_to_term == FORCE_ON)
+ from_backend(frontend, TRUE, msg, len);
+
+ msg[len-2] = '\0'; /* remove the \r\n again */
+ }
+ break;
+ default:
+ msg = NULL; /* shouldn't happen, but placate optimiser */
+ break;
+ }
+
+ if (msg) {
+ logevent(frontend, msg);
+ sfree(msg);
+ }
+}
+
+void log_proxy_stderr(Plug plug, bufchain *buf, const void *vdata, int len)
+{
+ const char *data = (const char *)vdata;
+ int pos = 0;
+ int msglen;
+ char *nlpos, *msg, *fullmsg;
+
+ /*
+ * This helper function allows us to collect the data written to a
+ * local proxy command's standard error in whatever size chunks we
+ * happen to get from its pipe, and whenever we have a complete
+ * line, we pass it to plug_log.
+ *
+ * Prerequisites: a plug to log to, and a bufchain stored
+ * somewhere to collect the data in.
+ */
+
+ while (pos < len && (nlpos = memchr(data+pos, '\n', len-pos)) != NULL) {
+ /*
+ * Found a newline in the current input buffer. Append it to
+ * the bufchain (which may contain a partial line from last
+ * time).
+ */
+ bufchain_add(buf, data + pos, nlpos - (data + pos));
+
+ /*
+ * Collect the resulting line of data and pass it to plug_log.
+ */
+ msglen = bufchain_size(buf);
+ msg = snewn(msglen+1, char);
+ bufchain_fetch(buf, msg, msglen);
+ bufchain_consume(buf, msglen);
+ msg[msglen] = '\0';
+ fullmsg = dupprintf("proxy: %s", msg);
+ plug_log(plug, 2, NULL, 0, fullmsg, 0);
+ sfree(fullmsg);
+ sfree(msg);
+
+ /*
+ * Advance past the newline.
+ */
+ pos += nlpos+1 - (data + pos);
+ }
+
+ /*
+ * Now any remaining data is a partial line, which we save for
+ * next time.
+ */
+ bufchain_add(buf, data + pos, len - pos);
+}
HELPCTX(proxy_command),
conf_editbox_handler,
I(CONF_proxy_telnet_command), I(1));
+
+ ctrl_radiobuttons(s, "Print proxy diagnostics "
+ "in the terminal window", 'r', 5,
+ HELPCTX(proxy_main),
+ conf_radiobutton_handler,
+ I(CONF_proxy_log_to_term),
+ "No", I(FORCE_OFF),
+ "Yes", I(FORCE_ON),
+ "Only until session starts", I(AUTO), NULL);
}
/*
if PuTTY decides of its own accord to send data: due to a repeat key
exchange in SSH-2 (see \k{config-ssh-kex-rekey}) or due to
keepalives (\k{config-keepalive}).
+
+\H{errors-cannotassignaddress} \q{Network error: Cannot assign requested
+address}
+
+This means that the operating system rejected the parameters of the
+network connection PuTTY tried to make, usually without actually
+trying to connect to anything, because they were simply invalid.
+
+A common way to provoke this error is to accidentally try to connect
+to port 0, which is not a valid port number.
* fatal error - we may well have other candidate addresses
* to fall back to. When it _is_ fatal, the closing()
* function will be called.
+ * - type==2 means that error_msg contains a line of generic
+ * logging information about setting up the connection. This
+ * will typically be a wodge of standard-error output from a
+ * proxy command, so the receiver should probably prefix it to
+ * indicate this.
*/
int (*closing)
(Plug p, const char *error_msg, int error_code, int calling_back);
Socket new_listener(const char *srcaddr, int port, Plug plug,
int local_host_only, Conf *conf, int addressfamily);
SockAddr name_lookup(const char *host, int port, char **canonicalname,
- Conf *conf, int addressfamily);
+ Conf *conf, int addressfamily, void *frontend_for_logging,
+ const char *lookup_reason_for_logging);
int proxy_for_destination (SockAddr addr, const char *hostname, int port,
Conf *conf);
*/
Socket new_error_socket(const char *errmsg, Plug plug);
-/********** SSL stuff **********/
+/* ----------------------------------------------------------------------
+ * Functions defined outside the network code, which have to be
+ * declared in this header file rather than the main putty.h because
+ * they use types defined here.
+ */
/*
- * This section is subject to change, but you get the general idea
- * of what it will eventually look like.
+ * Exports from be_misc.c.
*/
-
-typedef struct certificate *Certificate;
-typedef struct our_certificate *Our_Certificate;
- /* to be defined somewhere else, somehow */
-
-typedef struct ssl_client_socket_function_table **SSL_Client_Socket;
-typedef struct ssl_client_plug_function_table **SSL_Client_Plug;
-
-struct ssl_client_socket_function_table {
- struct socket_function_table base;
- void (*renegotiate) (SSL_Client_Socket s);
- /* renegotiate the cipher spec */
-};
-
-struct ssl_client_plug_function_table {
- struct plug_function_table base;
- int (*refuse_cert) (SSL_Client_Plug p, Certificate cert[]);
- /* do we accept this certificate chain? If not, why not? */
- /* cert[0] is the server's certificate, cert[] is NULL-terminated */
- /* the last certificate may or may not be the root certificate */
- Our_Certificate(*client_cert) (SSL_Client_Plug p);
- /* the server wants us to identify ourselves */
- /* may return NULL if we want anonymity */
-};
-
-SSL_Client_Socket sk_ssl_client_over(Socket s, /* pre-existing (tcp) connection */
- SSL_Client_Plug p);
-
-#define sk_renegotiate(s) (((*s)->renegotiate) (s))
+void backend_socket_log(void *frontend, int type, SockAddr addr, int port,
+ const char *error_msg, int error_code, Conf *conf,
+ int session_started);
+typedef struct bufchain_tag bufchain; /* rest of declaration in misc.c */
+void log_proxy_stderr(Plug plug, bufchain *buf, const void *vdata, int len);
#endif
/*
* Try to find host.
*/
- addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily);
+ addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily,
+ NULL, NULL);
if ((err = sk_addr_error(addr)) != NULL) {
char *err_ret = dupstr(err);
sk_addr_free(addr);
return 1;
}
+static char *dns_log_msg(const char *host, int addressfamily,
+ const char *reason)
+{
+ return dupprintf("Looking up host \"%s\"%s for %s", host,
+ (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+ addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
+ ""), reason);
+}
+
SockAddr name_lookup(const char *host, int port, char **canonicalname,
- Conf *conf, int addressfamily)
+ Conf *conf, int addressfamily, void *frontend,
+ const char *reason)
{
+ char *logmsg;
if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&
do_proxy_dns(conf) &&
proxy_for_destination(NULL, host, port, conf)) {
+
+ if (frontend) {
+ logmsg = dupprintf("Leaving host lookup to proxy of \"%s\""
+ " (for %s)", host, reason);
+ logevent(frontend, logmsg);
+ sfree(logmsg);
+ }
+
*canonicalname = dupstr(host);
return sk_nonamelookup(host);
+ } else {
+ if (frontend) {
+ logmsg = dns_log_msg(host, addressfamily, reason);
+ logevent(frontend, logmsg);
+ sfree(logmsg);
+ }
+
+ return sk_namelookup(host, canonicalname, addressfamily);
}
-
- return sk_namelookup(host, canonicalname, addressfamily);
}
Socket new_connection(SockAddr addr, const char *hostname,
Proxy_Plug pplug;
SockAddr proxy_addr;
char *proxy_canonical_name;
+ const char *proxy_type;
Socket sret;
int type;
type = conf_get_int(conf, CONF_proxy_type);
if (type == PROXY_HTTP) {
ret->negotiate = proxy_http_negotiate;
+ proxy_type = "HTTP";
} else if (type == PROXY_SOCKS4) {
ret->negotiate = proxy_socks4_negotiate;
+ proxy_type = "SOCKS 4";
} else if (type == PROXY_SOCKS5) {
ret->negotiate = proxy_socks5_negotiate;
+ proxy_type = "SOCKS 5";
} else if (type == PROXY_TELNET) {
ret->negotiate = proxy_telnet_negotiate;
+ proxy_type = "Telnet";
} else {
ret->error = "Proxy error: Unknown proxy method";
return (Socket) ret;
}
+ {
+ char *logmsg = dupprintf("Will use %s proxy at %s:%d to connect"
+ " to %s:%d", proxy_type,
+ conf_get_str(conf, CONF_proxy_host),
+ conf_get_int(conf, CONF_proxy_port),
+ hostname, port);
+ plug_log(plug, 2, NULL, 0, logmsg, 0);
+ sfree(logmsg);
+ }
+
/* create the proxy plug to map calls from the actual
* socket into our proxy socket layer */
pplug = snew(struct Plug_proxy_tag);
pplug->fn = &plug_fn_table;
pplug->proxy_socket = ret;
+ {
+ char *logmsg = dns_log_msg(conf_get_str(conf, CONF_proxy_host),
+ conf_get_int(conf, CONF_addressfamily),
+ "proxy");
+ plug_log(plug, 2, NULL, 0, logmsg, 0);
+ sfree(logmsg);
+ }
+
/* look-up proxy */
proxy_addr = sk_namelookup(conf_get_str(conf, CONF_proxy_host),
&proxy_canonical_name,
}
sfree(proxy_canonical_name);
+ {
+ char addrbuf[256], *logmsg;
+ sk_getaddr(addr, addrbuf, lenof(addrbuf));
+ logmsg = dupprintf("Connecting to %s proxy at %s port %d",
+ proxy_type, addrbuf,
+ conf_get_int(conf, CONF_proxy_port));
+ plug_log(plug, 2, NULL, 0, logmsg, 0);
+ sfree(logmsg);
+ }
+
/* create the actual socket we will be using,
* connected to our proxy server and port.
*/
formatted_cmd = format_telnet_command(p->remote_addr, p->remote_port,
p->conf);
+ {
+ /*
+ * Re-escape control chars in the command, for logging.
+ */
+ char *reescaped = snewn(4*strlen(formatted_cmd) + 1, char);
+ const char *in;
+ char *out;
+ char *logmsg;
+
+ for (in = formatted_cmd, out = reescaped; *in; in++) {
+ if (*in == '\n') {
+ *out++ = '\\'; *out++ = 'n';
+ } else if (*in == '\r') {
+ *out++ = '\\'; *out++ = 'r';
+ } else if (*in == '\t') {
+ *out++ = '\\'; *out++ = 't';
+ } else if (*in == '\\') {
+ *out++ = '\\'; *out++ = '\\';
+ } else if ((unsigned)(((unsigned char)*in) - 0x20) <
+ (0x7F-0x20)) {
+ *out++ = *in;
+ } else {
+ out += sprintf(out, "\\x%02X", (unsigned)*in & 0xFF);
+ }
+ }
+ *out = '\0';
+
+ logmsg = dupprintf("Sending Telnet proxy command: %s", reescaped);
+ plug_log(p->plug, 2, NULL, 0, logmsg, 0);
+ sfree(logmsg);
+ sfree(reescaped);
+ }
+
sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd));
sfree(formatted_cmd);
* three-way settings whose values are `always yes', `always
* no', and `decide by some more complex automated means'. This
* is true of line discipline options (local echo and line
- * editing), proxy DNS, Close On Exit, and SSH server bug
- * workarounds. Accordingly I supply a single enum here to deal
- * with them all.
+ * editing), proxy DNS, proxy terminal logging, Close On Exit, and
+ * SSH server bug workarounds. Accordingly I supply a single enum
+ * here to deal with them all.
*/
FORCE_ON, FORCE_OFF, AUTO
};
X(STR, NONE, proxy_username) \
X(STR, NONE, proxy_password) \
X(STR, NONE, proxy_telnet_command) \
+ X(INT, NONE, proxy_log_to_term) \
/* SSH options */ \
X(STR, NONE, remote_cmd) \
X(STR, NONE, remote_cmd2) /* fallback if remote_cmd fails; never loaded or saved */ \
int closed_on_socket_error;
int bufsize;
void *frontend;
- int sent_console_eof, sent_socket_eof;
+ int sent_console_eof, sent_socket_eof, session_started;
+
+ Conf *conf;
} *Raw;
static void raw_size(void *handle, int width, int height);
const char *error_msg, int error_code)
{
Raw raw = (Raw) plug;
- char addrbuf[256], *msg;
-
- sk_getaddr(addr, addrbuf, lenof(addrbuf));
-
- if (type == 0)
- msg = dupprintf("Connecting to %s port %d", addrbuf, port);
- else
- msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);
-
- logevent(raw->frontend, msg);
- sfree(msg);
+ backend_socket_log(raw->frontend, type, addr, port,
+ error_msg, error_code, raw->conf, raw->session_started);
}
static void raw_check_close(Raw raw)
{
Raw raw = (Raw) plug;
c_write(raw, data, len);
+ /* We count 'session start', for proxy logging purposes, as being
+ * when data is received from the network and printed. */
+ raw->session_started = TRUE;
return 1;
}
*backend_handle = raw;
raw->sent_console_eof = raw->sent_socket_eof = FALSE;
raw->bufsize = 0;
+ raw->session_started = FALSE;
+ raw->conf = conf_copy(conf);
raw->frontend = frontend_handle;
/*
* Try to find host.
*/
- {
- char *buf;
- buf = dupprintf("Looking up host \"%s\"%s", host,
- (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
- (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
- "")));
- logevent(raw->frontend, buf);
- sfree(buf);
- }
- addr = name_lookup(host, port, realhost, conf, addressfamily);
+ addr = name_lookup(host, port, realhost, conf, addressfamily,
+ raw->frontend, "main connection");
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
if (raw->s)
sk_close(raw->s);
+ conf_free(raw->conf);
sfree(raw);
}
const char *error_msg, int error_code)
{
Rlogin rlogin = (Rlogin) plug;
- char addrbuf[256], *msg;
-
- sk_getaddr(addr, addrbuf, lenof(addrbuf));
-
- if (type == 0)
- msg = dupprintf("Connecting to %s port %d", addrbuf, port);
- else
- msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);
-
- logevent(rlogin->frontend, msg);
- sfree(msg);
+ backend_socket_log(rlogin->frontend, type, addr, port,
+ error_msg, error_code,
+ rlogin->conf, !rlogin->firstbyte);
}
static int rlogin_closing(Plug plug, const char *error_msg, int error_code,
/*
* Try to find host.
*/
- {
- char *buf;
- buf = dupprintf("Looking up host \"%s\"%s", host,
- (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
- (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
- "")));
- logevent(rlogin->frontend, buf);
- sfree(buf);
- }
- addr = name_lookup(host, port, realhost, conf, addressfamily);
+ addr = name_lookup(host, port, realhost, conf, addressfamily,
+ rlogin->frontend, "rlogin connection");
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
write_setting_s(sesskey, "ProxyUsername", conf_get_str(conf, CONF_proxy_username));
write_setting_s(sesskey, "ProxyPassword", conf_get_str(conf, CONF_proxy_password));
write_setting_s(sesskey, "ProxyTelnetCommand", conf_get_str(conf, CONF_proxy_telnet_command));
+ write_setting_i(sesskey, "ProxyLogToTerm", conf_get_int(conf, CONF_proxy_log_to_term));
wmap(sesskey, "Environment", conf, CONF_environmt, TRUE);
write_setting_s(sesskey, "UserName", conf_get_str(conf, CONF_username));
write_setting_i(sesskey, "UserNameFromEnvironment", conf_get_int(conf, CONF_username_from_env));
gpps(sesskey, "ProxyPassword", "", conf, CONF_proxy_password);
gpps(sesskey, "ProxyTelnetCommand", "connect %host %port\\n",
conf, CONF_proxy_telnet_command);
+ gppi(sesskey, "ProxyLogToTerm", FORCE_OFF, conf, CONF_proxy_log_to_term);
gppmap(sesskey, "Environment", conf, CONF_environmt);
gpps(sesskey, "UserName", "", conf, CONF_username);
gppi(sesskey, "UserNameFromEnvironment", 0, conf, CONF_username_from_env);
int send_ok;
int echoing, editing;
+ int session_started;
void *frontend;
int ospeed, ispeed; /* temporaries */
crReturn(1);
}
+ ssh->session_started = TRUE;
+
s->vstrsize = sizeof(protoname) + 16;
s->vstring = snewn(s->vstrsize, char);
strcpy(s->vstring, protoname);
const char *error_msg, int error_code)
{
Ssh ssh = (Ssh) plug;
- char addrbuf[256], *msg;
-
- if (ssh->attempting_connshare) {
- /*
- * While we're attempting connection sharing, don't loudly log
- * everything that happens. Real TCP connections need to be
- * logged when we _start_ trying to connect, because it might
- * be ages before they respond if something goes wrong; but
- * connection sharing is local and quick to respond, and it's
- * sufficient to simply wait and see whether it worked
- * afterwards.
- */
- } else {
- sk_getaddr(addr, addrbuf, lenof(addrbuf));
- if (type == 0) {
- if (sk_addr_needs_port(addr)) {
- msg = dupprintf("Connecting to %s port %d", addrbuf, port);
- } else {
- msg = dupprintf("Connecting to %s", addrbuf);
- }
- } else {
- msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);
- }
+ /*
+ * While we're attempting connection sharing, don't loudly log
+ * everything that happens. Real TCP connections need to be logged
+ * when we _start_ trying to connect, because it might be ages
+ * before they respond if something goes wrong; but connection
+ * sharing is local and quick to respond, and it's sufficient to
+ * simply wait and see whether it worked afterwards.
+ */
- logevent(msg);
- sfree(msg);
- }
+ if (!ssh->attempting_connshare)
+ backend_socket_log(ssh->frontend, type, addr, port,
+ error_msg, error_code, ssh->conf,
+ ssh->session_started);
}
void ssh_connshare_log(Ssh ssh, int event, const char *logtext,
* Try to find host.
*/
addressfamily = conf_get_int(ssh->conf, CONF_addressfamily);
- logeventf(ssh, "Looking up host \"%s\"%s", host,
- (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
- (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : "")));
- addr = name_lookup(host, port, realhost, ssh->conf, addressfamily);
+ addr = name_lookup(host, port, realhost, ssh->conf, addressfamily,
+ ssh->frontend, "SSH connection");
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
ssh->X11_fwd_enabled = FALSE;
ssh->connshare = NULL;
ssh->attempting_connshare = FALSE;
+ ssh->session_started = FALSE;
*backend_handle = ssh;
int sb_opt, sb_len;
unsigned char *sb_buf;
int sb_size;
+ int session_started;
enum {
TOP_LEVEL, SEENIAC, SEENWILL, SEENWONT, SEENDO, SEENDONT,
const char *error_msg, int error_code)
{
Telnet telnet = (Telnet) plug;
- char addrbuf[256], *msg;
-
- sk_getaddr(addr, addrbuf, lenof(addrbuf));
-
- if (type == 0)
- msg = dupprintf("Connecting to %s port %d", addrbuf, port);
- else
- msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);
-
- logevent(telnet->frontend, msg);
- sfree(msg);
+ backend_socket_log(telnet->frontend, type, addr, port,
+ error_msg, error_code, telnet->conf,
+ telnet->session_started);
}
static int telnet_closing(Plug plug, const char *error_msg, int error_code,
Telnet telnet = (Telnet) plug;
if (urgent)
telnet->in_synch = TRUE;
+ telnet->session_started = TRUE;
do_telnet_read(telnet, data, len);
return 1;
}
telnet->state = TOP_LEVEL;
telnet->ldisc = NULL;
telnet->pinger = NULL;
+ telnet->session_started = TRUE;
*backend_handle = telnet;
/*
* Try to find host.
*/
- {
- char *buf;
- addressfamily = conf_get_int(telnet->conf, CONF_addressfamily);
- buf = dupprintf("Looking up host \"%s\"%s", host,
- (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
- (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
- "")));
- logevent(telnet->frontend, buf);
- sfree(buf);
- }
- addr = name_lookup(host, port, realhost, telnet->conf, addressfamily);
+ addressfamily = conf_get_int(telnet->conf, CONF_addressfamily);
+ addr = name_lookup(host, port, realhost, telnet->conf, addressfamily,
+ telnet->frontend, "Telnet connection");
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
{
#if GTK_CHECK_VERSION(3,16,0)
gtk_label_set_xalign(label, 0.0);
+#elif GTK_CHECK_VERSION(3,14,0)
+ gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_START);
#else
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
#endif
Filename *platform_default_filename(const char *name) { return filename_from_str(""); }
char *x_get_default(const char *key) { return NULL; }
void log_eventlog(void *handle, const char *event) {}
+int from_backend(void *frontend, int is_stderr, const char *data, int datalen)
+{ assert(!"only here to satisfy notional call from backend_socket_log"); }
/*
* Short description of parameters.
const struct socket_function_table *fn;
/* the above variable absolutely *must* be the first in this structure */
- int to_cmd, from_cmd; /* fds */
+ int to_cmd, from_cmd, cmd_err; /* fds */
char *error;
bufchain pending_output_data;
bufchain pending_input_data;
+ bufchain pending_error_data;
enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
};
/*
* Trees to look up the pipe fds in.
*/
-static tree234 *localproxy_by_fromfd, *localproxy_by_tofd;
+static tree234 *localproxy_by_fromfd;
+static tree234 *localproxy_by_tofd;
+static tree234 *localproxy_by_errfd;
static int localproxy_fromfd_cmp(void *av, void *bv)
{
Local_Proxy_Socket a = (Local_Proxy_Socket)av;
return +1;
return 0;
}
+static int localproxy_errfd_cmp(void *av, void *bv)
+{
+ Local_Proxy_Socket a = (Local_Proxy_Socket)av;
+ Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
+ if (a->cmd_err < b->cmd_err)
+ return -1;
+ if (a->cmd_err > b->cmd_err)
+ return +1;
+ return 0;
+}
+static int localproxy_errfd_find(void *av, void *bv)
+{
+ int a = *(int *)av;
+ Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
+ if (a < b->cmd_err)
+ return -1;
+ if (a > b->cmd_err)
+ return +1;
+ return 0;
+}
/* basic proxy socket functions */
uxsel_del(ps->from_cmd);
close(ps->from_cmd);
+ del234(localproxy_by_errfd, ps);
+ uxsel_del(ps->cmd_err);
+ close(ps->cmd_err);
+
+ bufchain_clear(&ps->pending_input_data);
+ bufchain_clear(&ps->pending_output_data);
+ bufchain_clear(&ps->pending_error_data);
+
sfree(ps);
}
int ret;
if (!(s = find234(localproxy_by_fromfd, &fd, localproxy_fromfd_find)) &&
+ !(s = find234(localproxy_by_fromfd, &fd, localproxy_errfd_find)) &&
!(s = find234(localproxy_by_tofd, &fd, localproxy_tofd_find)) )
return 1; /* boggle */
if (event == 1) {
- assert(fd == s->from_cmd);
- ret = read(fd, buf, sizeof(buf));
- if (ret < 0) {
- return plug_closing(s->plug, strerror(errno), errno, 0);
- } else if (ret == 0) {
- return plug_closing(s->plug, NULL, 0, 0);
- } else {
- return plug_receive(s->plug, 0, buf, ret);
- }
+ if (fd == s->cmd_err) {
+ ret = read(fd, buf, sizeof(buf));
+ if (ret > 0)
+ log_proxy_stderr(s->plug, &s->pending_error_data, buf, ret);
+ } else {
+ assert(fd == s->from_cmd);
+ ret = read(fd, buf, sizeof(buf));
+ if (ret < 0) {
+ return plug_closing(s->plug, strerror(errno), errno, 0);
+ } else if (ret == 0) {
+ return plug_closing(s->plug, NULL, 0, 0);
+ } else {
+ return plug_receive(s->plug, 0, buf, ret);
+ }
+ }
} else if (event == 2) {
assert(fd == s->to_cmd);
if (localproxy_try_send(s))
};
Local_Proxy_Socket ret;
- int to_cmd_pipe[2], from_cmd_pipe[2], pid, proxytype;
+ int to_cmd_pipe[2], from_cmd_pipe[2], cmd_err_pipe[2], pid, proxytype;
proxytype = conf_get_int(conf, CONF_proxy_type);
if (proxytype != PROXY_CMD && proxytype != PROXY_FUZZ)
bufchain_init(&ret->pending_input_data);
bufchain_init(&ret->pending_output_data);
+ bufchain_init(&ret->pending_error_data);
if (proxytype == PROXY_CMD) {
cmd = format_telnet_command(addr, port, conf);
+ if (flags & FLAG_STDERR) {
+ /* If we have a sensible stderr, the proxy command can
+ * send its own standard error there, so we won't
+ * interfere. */
+ cmd_err_pipe[0] = cmd_err_pipe[1] = -1;
+ } else {
+ /* If we don't have a sensible stderr, we should catch the
+ * proxy command's standard error to put in our event
+ * log. */
+ cmd_err_pipe[0] = cmd_err_pipe[1] = 0;
+ }
+
+ {
+ char *logmsg = dupprintf("Starting local proxy command: %s", cmd);
+ plug_log(plug, 2, NULL, 0, logmsg, 0);
+ sfree(logmsg);
+ }
+
/*
* Create the pipes to the proxy command, and spawn the proxy
* command process.
*/
if (pipe(to_cmd_pipe) < 0 ||
- pipe(from_cmd_pipe) < 0) {
+ pipe(from_cmd_pipe) < 0 ||
+ (cmd_err_pipe[0] == 0 && pipe(cmd_err_pipe) < 0)) {
ret->error = dupprintf("pipe: %s", strerror(errno));
sfree(cmd);
return (Socket)ret;
}
cloexec(to_cmd_pipe[1]);
cloexec(from_cmd_pipe[0]);
+ if (cmd_err_pipe[0] >= 0)
+ cloexec(cmd_err_pipe[0]);
pid = fork();
dup2(from_cmd_pipe[1], 1);
close(to_cmd_pipe[0]);
close(from_cmd_pipe[1]);
+ if (cmd_err_pipe[0] >= 0) {
+ dup2(cmd_err_pipe[1], 2);
+ close(cmd_err_pipe[1]);
+ }
noncloexec(0);
noncloexec(1);
execl("/bin/sh", "sh", "-c", cmd, (void *)NULL);
close(to_cmd_pipe[0]);
close(from_cmd_pipe[1]);
+ if (cmd_err_pipe[0] >= 0)
+ close(cmd_err_pipe[1]);
ret->to_cmd = to_cmd_pipe[1];
ret->from_cmd = from_cmd_pipe[0];
+ ret->cmd_err = cmd_err_pipe[0];
} else {
cmd = format_telnet_command(addr, port, conf);
ret->to_cmd = open("/dev/null", O_WRONLY);
return (Socket)ret;
}
sfree(cmd);
+ ret->cmd_err = -1;
}
if (!localproxy_by_fromfd)
localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp);
if (!localproxy_by_tofd)
localproxy_by_tofd = newtree234(localproxy_tofd_cmp);
+ if (!localproxy_by_errfd)
+ localproxy_by_errfd = newtree234(localproxy_errfd_cmp);
add234(localproxy_by_fromfd, ret);
add234(localproxy_by_tofd, ret);
+ if (ret->cmd_err >= 0)
+ add234(localproxy_by_errfd, ret);
uxsel_set(ret->from_cmd, 1, localproxy_select_result);
+ if (ret->cmd_err >= 0)
+ uxsel_set(ret->cmd_err, 1, localproxy_select_result);
/* We are responsible for this and don't need it any more */
sk_addr_free(addr);
{
struct fd *newfd;
+ assert(fd >= 0);
+
uxsel_del(fd);
if (rwx) {
const struct socket_function_table *fn;
/* the above variable absolutely *must* be the first in this structure */
- HANDLE send_H, recv_H;
- struct handle *send_h, *recv_h;
+ HANDLE send_H, recv_H, stderr_H;
+ struct handle *send_h, *recv_h, *stderr_h;
/*
* Freezing one of these sockets is a slightly fiddly business,
/* We buffer data here if we receive it from winhandl while frozen. */
bufchain inputdata;
+ /* Data received from stderr_H, if we have one. */
+ bufchain stderrdata;
+
char *error;
Plug plug;
}
}
+static int handle_stderr(struct handle *h, void *data, int len)
+{
+ Handle_Socket ps = (Handle_Socket) handle_get_privdata(h);
+
+ if (len > 0)
+ log_proxy_stderr(ps->plug, &ps->stderrdata, data, len);
+
+ return 0;
+}
+
static void handle_sentdata(struct handle *h, int new_backlog)
{
Handle_Socket ps = (Handle_Socket) handle_get_privdata(h);
if (ps->recv_H != ps->send_H)
CloseHandle(ps->recv_H);
bufchain_clear(&ps->inputdata);
+ bufchain_clear(&ps->stderrdata);
sfree(ps);
}
return NULL;
}
-Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
- int overlapped)
+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,
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);
return (Socket) ret;
}
#include "winsecur.h"
-Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
- int overlapped);
+Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
+ Plug plug, int overlapped);
Socket new_named_pipe_client(const char *pipename, Plug plug)
{
LocalFree(psd);
sfree(usersid);
- return make_handle_socket(pipehandle, pipehandle, plug, TRUE);
+ return make_handle_socket(pipehandle, pipehandle, NULL, plug, TRUE);
}
#endif /* !defined NO_SECURITY */
#include "winsecur.h"
-Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
- int overlapped);
+Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
+ Plug plug, int overlapped);
typedef struct Socket_named_pipe_server_tag *Named_Pipe_Server_Socket;
struct Socket_named_pipe_server_tag {
{
HANDLE conn = (HANDLE)ctx.p;
- return make_handle_socket(conn, conn, plug, TRUE);
+ return make_handle_socket(conn, conn, NULL, plug, TRUE);
}
/*
#include "network.h"
#include "proxy.h"
-Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
- int overlapped);
+Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
+ Plug plug, int overlapped);
Socket platform_new_connection(SockAddr addr, const char *hostname,
int port, int privport,
Plug plug, Conf *conf)
{
char *cmd;
- HANDLE us_to_cmd, us_from_cmd, cmd_to_us, cmd_from_us;
+ HANDLE us_to_cmd, cmd_from_us;
+ HANDLE us_from_cmd, cmd_to_us;
+ HANDLE us_from_cmd_err, cmd_err_to_us;
SECURITY_ATTRIBUTES sa;
STARTUPINFO si;
PROCESS_INFORMATION pi;
{
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);
+ plug_log(plug, 2, NULL, 0, msg, 0);
sfree(msg);
}
return ret;
}
+ if (flags & FLAG_STDERR) {
+ /* If we have a sensible stderr, the proxy command can send
+ * its own standard error there, so we won't interfere. */
+ us_from_cmd_err = cmd_err_to_us = NULL;
+ } else {
+ /* If we don't have a sensible stderr, we should catch the
+ * proxy command's standard error to put in our event log. */
+ if (!CreatePipe(&us_from_cmd_err, &cmd_err_to_us, &sa, 0)) {
+ Socket ret = new_error_socket
+ ("Unable to create pipes for proxy command", plug);
+ sfree(cmd);
+ return ret;
+ }
+ }
+
SetHandleInformation(us_to_cmd, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(us_from_cmd, HANDLE_FLAG_INHERIT, 0);
+ if (us_from_cmd_err != NULL)
+ SetHandleInformation(us_from_cmd_err, HANDLE_FLAG_INHERIT, 0);
si.cb = sizeof(si);
si.lpReserved = NULL;
si.lpReserved2 = NULL;
si.hStdInput = cmd_from_us;
si.hStdOutput = cmd_to_us;
- si.hStdError = NULL;
+ si.hStdError = cmd_err_to_us;
CreateProcess(NULL, cmd, NULL, NULL, TRUE,
CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS,
NULL, NULL, &si, &pi);
CloseHandle(cmd_from_us);
CloseHandle(cmd_to_us);
- return make_handle_socket(us_to_cmd, us_from_cmd, plug, FALSE);
+ if (cmd_err_to_us != NULL)
+ CloseHandle(cmd_err_to_us);
+
+ return make_handle_socket(us_to_cmd, us_from_cmd, us_from_cmd_err,
+ plug, FALSE);
}
disp->port = 6000 + disp->displaynum;
disp->addr = name_lookup(disp->hostname, disp->port,
- &disp->realhost, conf, ADDRTYPE_UNSPEC);
+ &disp->realhost, conf, ADDRTYPE_UNSPEC,
+ NULL, NULL);
if ((err = sk_addr_error(disp->addr)) != NULL) {
sk_addr_free(disp->addr);