OBJS1 = sshcrc.$(OBJ) sshdes.$(OBJ) sshmd5.$(OBJ) sshrsa.$(OBJ) sshrand.$(OBJ)
OBJS2 = sshsha.$(OBJ) sshblowf.$(OBJ) noise.$(OBJ) sshdh.$(OBJ) sshdss.$(OBJ)
OBJS3 = sshbn.$(OBJ) sshpubk.$(OBJ) ssh.$(OBJ) pageantc.$(OBJ) sshzlib.$(OBJ)
-OBJS4 = x11fwd.$(OBJ) sshaes.$(OBJ)
+OBJS4 = x11fwd.$(OBJ) portforward.$(OBJ) sshaes.$(OBJ)
##-- objects pageant
PAGE1 = pageant.$(OBJ) sshrsa.$(OBJ) sshpubk.$(OBJ) sshdes.$(OBJ) sshbn.$(OBJ)
PAGE2 = sshmd5.$(OBJ) version.$(OBJ) tree234.$(OBJ) misc.$(OBJ) sshaes.$(OBJ)
window.$(OBJ): window.c network.h puttymem.h storage.h winstuff.h putty.h win_res.h
winnet.$(OBJ): winnet.c network.h puttymem.h putty.h tree234.h
winstore.$(OBJ): winstore.c network.h puttymem.h storage.h putty.h
-x11fwd.$(OBJ): x11fwd.c network.h puttymem.h ssh.h putty.h
+x11fwd.$(OBJ): x11fwd.c network.h puttymem.h ssh.h putty.h
+portforward.$(OBJ): portforward.c network.h puttymem.h ssh.h putty.h
xlat.$(OBJ): xlat.c network.h puttymem.h putty.h
##--
* - urgent==2. `data' points to `len' bytes of data,
* the first of which was the one at the Urgent mark.
*/
+ int (*accepting)(Plug p, struct sockaddr *addr, void *sock);
+ /*
+ * returns 0 if the host at address addr is a valid host for connecting or error
+ */
};
Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
Plug p);
+Socket sk_newlistenner(int port, Plug plug);
+
+Socket sk_register(void *sock, Plug plug);
+
#define sk_plug(s,p) (((*s)->plug) (s, p))
#define sk_close(s) (((*s)->close) (s))
#define sk_write(s,buf,len) (((*s)->write) (s, buf, len))
#ifdef DEFINE_PLUG_METHOD_MACROS
#define plug_closing(p,msg,code,callback) (((*p)->closing) (p, msg, code, callback))
#define plug_receive(p,urgent,buf,len) (((*p)->receive) (p, urgent, buf, len))
+#define plug_accepting(p, addr, sock) (((*p)->accepting)(p, addr, sock))
#endif
/*
char *sk_addr_error(SockAddr addr);
#define sk_socket_error(s) (((*s)->socket_error) (s))
+/*
+ * Set the `frozen' flag on a socket. A frozen socket is one in
+ * which all sends are buffered and receives are ignored. This is
+ * so that (for example) a new port-forwarding can sit in limbo
+ * until its associated SSH channel is ready, and then pending data
+ * can be sent on.
+ */
+void sk_set_frozen(Socket sock, int is_frozen);
/********** SSL stuff **********/
* of what it will eventually look like.
*/
-
typedef struct certificate *Certificate;
typedef struct our_certificate *Our_Certificate;
/* to be defined somewhere else, somehow */
{
int events;
if (startup) {
- events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
+ events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE | FD_ACCEPT;
} else {
events = 0;
}
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) {
/* X11 forwarding */
int x11_forward;
char x11_display[128];
+ /* port forwarding */
+ int lport_acceptall; /* accepts connection from hosts other than localhost */
+ char portfwd[1024]; /* [LR]localport\thost:port\000[LR]localport\thost:port\000\000 */
} Config;
/*
write_setting_i(sesskey, "BlinkText", cfg->blinktext);
write_setting_i(sesskey, "X11Forward", cfg->x11_forward);
write_setting_s(sesskey, "X11Display", cfg->x11_display);
+ write_setting_i(sesskey, "LocalPortAcceptAll", cfg->lport_acceptall);
+ {
+ char buf[2 * sizeof(cfg->portfwd)], *p, *q;
+ p = buf;
+ q = cfg->portfwd;
+ while (*q) {
+ while (*q) {
+ int c = *q++;
+ if (c == '=' || c == ',' || c == '\\')
+ *p++ = '\\';
+ if (c == '\t')
+ c = '=';
+ *p++ = c;
+ }
+ *p++ = ',';
+ q++;
+ }
+ *p = '\0';
+ write_setting_s(sesskey, "PortForwardings", buf);
+ }
close_settings_w(sesskey);
}
gpps(sesskey, "X11Display", "localhost:0", cfg->x11_display,
sizeof(cfg->x11_display));
+ gppi(sesskey, "LocalPortAcceptAll", 0, &cfg->lport_acceptall);
+ {
+ char buf[2 * sizeof(cfg->portfwd)], *p, *q;
+ gpps(sesskey, "PortForwardings", "", buf, sizeof(buf));
+ p = buf;
+ q = cfg->portfwd;
+ while (*p) {
+ while (*p && *p != ',') {
+ int c = *p++;
+ if (c == '=')
+ c = '\t';
+ if (c == '\\')
+ c = *p++;
+ *q++ = c;
+ }
+ if (*p == ',')
+ p++;
+ *q++ = '\0';
+ }
+ *q = '\0';
+ }
+
close_settings_r(sesskey);
}
extern void x11_send(Socket, char *, int);
extern void x11_invent_auth(char *, int, char *, int);
+extern char *pfd_newconnect(Socket * s, char *hostname, int port, void *c);
+extern char *pfd_addforward(char *desthost, int destport, int port);
+extern void pfd_close(Socket s);
+extern void pfd_send(Socket s, char *data, int len);
+extern void pfd_confirm(Socket s);
+
/*
* Ciphers for SSH2. We miss out single-DES because it isn't
* supported; also 3DES and Blowfish are both done differently from
CHAN_MAINSESSION,
CHAN_X11,
CHAN_AGENT,
+ CHAN_SOCKDATA
};
/*
struct ssh_x11_channel {
Socket s;
} x11;
+ struct ssh_pfd_channel {
+ Socket s;
+ } pfd;
} u;
};
+/*
+ * 2-3-4 tree storing remote->local port forwardings (so we can
+ * reject any attempt to open a port we didn't explicitly ask to
+ * have forwarded).
+ */
+struct ssh_rportfwd {
+ unsigned port;
+ char host[256];
+};
+
struct Packet {
long length;
int type;
static tree234 *ssh_channels; /* indexed by local id */
static struct ssh_channel *mainchan; /* primary session channel */
+static tree234 *ssh_rportfwds;
+
static enum {
SSH_STATE_PREPACKET,
SSH_STATE_BEFORE_SIZE,
return 0;
}
+static int ssh_rportcmp(void *av, void *bv)
+{
+ struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;
+ struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;
+ int i;
+ if ( (i = strcmp(a->host, b->host)) != 0)
+ return i < 0 ? -1 : +1;
+ if (a->port > b->port)
+ return +1;
+ return 0;
+}
+
static int alloc_channel_id(void)
{
const unsigned CHANNEL_NUMBER_OFFSET = 256;
c->closes = 1;
if (c->type == CHAN_X11) {
c->u.x11.s = NULL;
- logevent("X11 connection terminated");
+ logevent("Forwarded X11 connection terminated");
+ } else if (c->type == CHAN_SOCKDATA) {
+ c->u.pfd.s = NULL;
+ logevent("Forwarded port closed");
}
}
}
}
}
+ {
+ char type, *e;
+ int n;
+ int sport,dport;
+ char sports[256], dports[256], host[256];
+ char buf[1024];
+
+ ssh_rportfwds = newtree234(ssh_rportcmp);
+ /* Add port forwardings. */
+ e = cfg.portfwd;
+ while (*e) {
+ type = *e++;
+ n = 0;
+ while (*e && *e != '\t')
+ sports[n++] = *e++;
+ sports[n] = 0;
+ if (*e == '\t')
+ e++;
+ n = 0;
+ while (*e && *e != ':')
+ host[n++] = *e++;
+ host[n] = 0;
+ if (*e == ':')
+ e++;
+ n = 0;
+ while (*e)
+ dports[n++] = *e++;
+ dports[n] = 0;
+ e++;
+ dport = atoi(dports);
+ sport = atoi(sports);
+ if (sport && dport) {
+ if (type == 'L') {
+ pfd_addforward(host, dport, sport);
+ sprintf(buf, "Local port %d forwarding to %s:%d",
+ sport, host, dport);
+ logevent(buf);
+ } else {
+ struct ssh_rportfwd *pf;
+ pf = smalloc(sizeof(*pf));
+ strcpy(pf->host, host);
+ pf->port = dport;
+ if (add234(ssh_rportfwds, pf) != pf) {
+ sprintf(buf,
+ "Duplicate remote port forwarding to %s:%s",
+ host, dport);
+ logevent(buf);
+ } else {
+ sprintf(buf, "Requesting remote port %d forward to %s:%d",
+ sport, host, dport);
+ logevent(buf);
+ send_packet(SSH1_CMSG_PORT_FORWARD_REQUEST,
+ PKT_INT, sport,
+ PKT_STR, host,
+ PKT_INT, dport,
+ PKT_END);
+ }
+ }
+ }
+ }
+ }
+
if (!cfg.nopty) {
send_packet(SSH1_CMSG_REQUEST_PTY,
PKT_STR, cfg.termtype,
PKT_INT, c->remoteid, PKT_INT, c->localid,
PKT_END);
}
+ } else if (pktin.type == SSH1_MSG_PORT_OPEN) {
+ /* Remote side is trying to open a channel to talk to a
+ * forwarded port. Give them back a local channel number. */
+ struct ssh_channel *c;
+ struct ssh_rportfwd pf;
+ int hostsize, port;
+ char host[256], buf[1024];
+ char *p, *h, *e;
+ c = smalloc(sizeof(struct ssh_channel));
+
+ hostsize = GET_32BIT(pktin.body+4);
+ for(h = host, p = pktin.body+8; hostsize != 0; hostsize--) {
+ if (h+1 < host+sizeof(host))
+ *h++ = *p;
+ *p++;
+ }
+ *h = 0;
+ port = GET_32BIT(p);
+
+ strcpy(pf.host, host);
+ pf.port = port;
+
+ if (find234(ssh_rportfwds, &pf, NULL) == NULL) {
+ sprintf(buf, "Rejected remote port open request for %s:%d",
+ host, port);
+ logevent(buf);
+ send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
+ PKT_INT, GET_32BIT(pktin.body), PKT_END);
+ } else {
+ sprintf(buf, "Received remote port open request for %s:%d",
+ host, port);
+ logevent(buf);
+ e = pfd_newconnect(&c->u.pfd.s, host, port, c);
+ if (e != NULL) {
+ char buf[256];
+ sprintf(buf, "Port open failed: %s", e);
+ logevent(buf);
+ sfree(c);
+ send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
+ PKT_INT, GET_32BIT(pktin.body),
+ PKT_END);
+ } else {
+ c->remoteid = GET_32BIT(pktin.body);
+ c->localid = alloc_channel_id();
+ c->closes = 0;
+ c->type = CHAN_SOCKDATA; /* identify channel type */
+ add234(ssh_channels, c);
+ send_packet(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
+ PKT_INT, c->remoteid, PKT_INT,
+ c->localid, PKT_END);
+ logevent("Forwarded port opened successfully");
+ }
+ }
+
+ } else if (pktin.type == SSH1_MSG_CHANNEL_OPEN_CONFIRMATION) {
+ unsigned int remoteid = GET_32BIT(pktin.body);
+ unsigned int localid = GET_32BIT(pktin.body+4);
+ struct ssh_channel *c;
+
+ c = find234(ssh_channels, &remoteid, ssh_channelfind);
+ if (c) {
+ c->remoteid = localid;
+ pfd_confirm(c->u.pfd.s);
+ } else {
+ sshfwd_close(c);
+ }
+
} else if (pktin.type == SSH1_MSG_CHANNEL_CLOSE ||
pktin.type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION) {
/* Remote side closes a channel. */
send_packet(pktin.type, PKT_INT, c->remoteid,
PKT_END);
if ((c->closes == 0) && (c->type == CHAN_X11)) {
- logevent("X11 connection closed");
+ logevent("Forwarded X11 connection terminated");
assert(c->u.x11.s != NULL);
x11_close(c->u.x11.s);
c->u.x11.s = NULL;
}
+ if ((c->closes == 0) && (c->type == CHAN_SOCKDATA)) {
+ logevent("Forwarded port closed");
+ assert(c->u.pfd.s != NULL);
+ pfd_close(c->u.pfd.s);
+ c->u.pfd.s = NULL;
+ }
c->closes |= closetype;
if (c->closes == 3) {
del234(ssh_channels, c);
case CHAN_X11:
x11_send(c->u.x11.s, p, len);
break;
+ case CHAN_SOCKDATA:
+ pfd_send(c->u.pfd.s, p, len);
+ break;
case CHAN_AGENT:
/* Data for an agent message. Buffer it. */
while (len > 0) {
case CHAN_X11:
x11_send(c->u.x11.s, data, length);
break;
+ case CHAN_SOCKDATA:
+ pfd_send(c->u.pfd.s, data, length);
+ break;
case CHAN_AGENT:
while (length > 0) {
if (c->u.a.lensofar < 4) {
sshfwd_close(c);
} else if (c->type == CHAN_AGENT) {
sshfwd_close(c);
+ } else if (c->type == CHAN_SOCKDATA) {
+ pfd_close(c->u.pfd.s);
+ sshfwd_close(c);
}
} else if (pktin.type == SSH2_MSG_CHANNEL_CLOSE) {
unsigned i = ssh2_pkt_getuint32();
break;
case CHAN_AGENT:
break;
+ case CHAN_SOCKDATA:
+ break;
}
del234(ssh_channels, c);
sfree(c->v2.outbuffer);
}
}
+void *new_sock_channel(Socket s)
+{
+ struct ssh_channel *c;
+ c = smalloc(sizeof(struct ssh_channel));
+
+ if (c) {
+ c->remoteid = GET_32BIT(pktin.body);
+ c->localid = alloc_channel_id();
+ c->closes = 0;
+ c->type = CHAN_SOCKDATA; /* identify channel type */
+ c->u.pfd.s = s;
+ add234(ssh_channels, c);
+ }
+ return c;
+}
+
+void ssh_send_port_open(void *channel, char *hostname, int port, char *org)
+{
+ struct ssh_channel *c = (struct ssh_channel *)channel;
+ char buf[1024];
+
+ sprintf(buf, "Opening forwarded connection to %.512s:%d", hostname, port);
+ logevent(buf);
+
+ send_packet(SSH1_MSG_PORT_OPEN,
+ PKT_INT, c->localid,
+ PKT_STR, hostname,
+ PKT_INT, port,
+ //PKT_STR, org,
+ PKT_END);
+}
+
+
static Socket ssh_socket(void)
{
return s;
ssh_sendok,
ssh_ldisc,
22
-};
+};
\ No newline at end of file
#include <string.h>
#include "puttymem.h"
+#include "network.h"
/*
* Useful thing.
void random_add_heavynoise(void *noise, int length);
void logevent(char *);
+void *new_sock_channel(Socket s); // allocates and register a new channel for port forwarding
+void ssh_send_port_open(void *channel, char *hostname, int port, char *org);
Bignum copybn(Bignum b);
Bignum bn_power_2(int n);
Bignum primegen(int bits, int modulus, int residue, int phase,
progfn_t pfn, void *pfnparam);
+
/*
* zlib compression.
*/
cp->ypos += 8 + GAPWITHIN + 12 + GAPBETWEEN;
}
-/*
- * A set of radio buttons on the same line, with a static above
- * them. `nacross' dictates how many parts the line is divided into
- * (you might want this not to equal the number of buttons if you
- * needed to line up some 2s and some 3s to look good in the same
- * panel).
- *
- * There's a bit of a hack in here to ensure that if nacross
- * exceeds the actual number of buttons, the rightmost button
- * really does get all the space right to the edge of the line, so
- * you can do things like
- *
- * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle
- */
-void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...)
+static void radioline_common(struct ctlpos *cp, int nacross, va_list ap)
{
RECT r;
- va_list ap;
int group;
int i;
char *btext;
- r.left = GAPBETWEEN;
- r.top = cp->ypos;
- r.right = cp->width;
- r.bottom = STATICHEIGHT;
- cp->ypos += r.bottom + GAPWITHIN;
- doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
- va_start(ap, nacross);
group = WS_GROUP;
i = 0;
btext = va_arg(ap, char *);
i++;
btext = nextbtext;
}
- va_end(ap);
cp->ypos += r.bottom + GAPBETWEEN;
}
/*
+ * A set of radio buttons on the same line, with a static above
+ * them. `nacross' dictates how many parts the line is divided into
+ * (you might want this not to equal the number of buttons if you
+ * needed to line up some 2s and some 3s to look good in the same
+ * panel).
+ *
+ * There's a bit of a hack in here to ensure that if nacross
+ * exceeds the actual number of buttons, the rightmost button
+ * really does get all the space right to the edge of the line, so
+ * you can do things like
+ *
+ * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle
+ */
+void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...)
+{
+ RECT r;
+ va_list ap;
+
+ r.left = GAPBETWEEN;
+ r.top = cp->ypos;
+ r.right = cp->width;
+ r.bottom = STATICHEIGHT;
+ cp->ypos += r.bottom + GAPWITHIN;
+ doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
+ va_start(ap, nacross);
+ radioline_common(cp, nacross, ap);
+ va_end(ap);
+}
+
+/*
+ * A set of radio buttons on the same line, without a static above
+ * them. Otherwise just like radioline.
+ */
+void bareradioline(struct ctlpos *cp, int nacross, ...)
+{
+ va_list ap;
+
+ va_start(ap, nacross);
+ radioline_common(cp, nacross, ap);
+ va_end(ap);
+}
+
+/*
* A set of radio buttons on multiple lines, with a static above
* them.
*/
#endif
, WS_EX_CLIENTEDGE, "", id);
}
+
+/*
+ * Another special control: the forwarding options setter. First a
+ * list box; next a static header line, introducing a pair of edit
+ * boxes with associated statics, another button, and a radio
+ * button pair.
+ */
+void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid,
+ char *e1stext, int e1sid, int e1id,
+ char *e2stext, int e2sid, int e2id,
+ char *btext, int bid)
+{
+ RECT r;
+ const int height = (STATICHEIGHT > EDITHEIGHT
+ && STATICHEIGHT >
+ PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT >
+ PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
+ const static int percents[] = { 25, 35, 15, 25 };
+ int i, j, xpos, percent;
+ const int LISTHEIGHT = 42;
+
+ /* The list box. */
+ r.left = GAPBETWEEN;
+ r.top = cp->ypos;
+ r.right = cp->width;
+ r.bottom = LISTHEIGHT;
+ cp->ypos += r.bottom + GAPBETWEEN;
+ doctl(cp, r, "LISTBOX",
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
+ | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid);
+
+ /* The static control. */
+ r.left = GAPBETWEEN;
+ r.top = cp->ypos;
+ r.right = cp->width;
+ r.bottom = STATICHEIGHT;
+ cp->ypos += r.bottom + GAPWITHIN;
+ doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
+
+ /* The statics+edits+buttons. */
+ for (j = 0; j < 2; j++) {
+ percent = 0;
+ for (i = 0; i < (j ? 2 : 4); i++) {
+ xpos = (cp->width + GAPBETWEEN) * percent / 100;
+ r.left = xpos + GAPBETWEEN;
+ percent += percents[i];
+ if (j==1 && i==1) percent = 100;
+ xpos = (cp->width + GAPBETWEEN) * percent / 100;
+ r.right = xpos - r.left;
+ r.top = cp->ypos;
+ r.bottom = (i == 0 ? STATICHEIGHT :
+ i == 1 ? EDITHEIGHT : PUSHBTNHEIGHT);
+ r.top += (height - r.bottom) / 2;
+ if (i == 0) {
+ doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0,
+ j == 0 ? e1stext : e2stext, j == 0 ? e1sid : e2sid);
+ } else if (i == 1) {
+ doctl(cp, r, "EDIT",
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
+ WS_EX_CLIENTEDGE, "", j == 0 ? e1id : e2id);
+ } else if (i == 3) {
+ doctl(cp, r, "BUTTON",
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+ 0, btext, bid);
+ }
+ }
+ cp->ypos += height + GAPWITHIN;
+ }
+}
tunnelspanelstart,
IDC_TITLE_TUNNELS,
- IDC_BOX_TUNNELS,
+ IDC_BOX_TUNNELS1,
+ IDC_BOX_TUNNELS2,
IDC_X11_FORWARD,
IDC_X11_DISPSTATIC,
IDC_X11_DISPLAY,
+ IDC_LPORT_ALL,
+ IDC_PFWDSTATIC,
+ IDC_PFWDSTATIC2,
+ IDC_PFWDREMOVE,
+ IDC_PFWDLIST,
+ IDC_PFWDADD,
+ IDC_SPORTSTATIC,
+ IDC_SPORTEDIT,
+ IDC_DPORTSTATIC,
+ IDC_DPORTEDIT,
+ IDC_PFWDLOCAL,
+ IDC_PFWDREMOTE,
+
tunnelspanelend,
controlendvalue
(LPARAM) p);
p += strlen(p) + 1;
}
+ p = cfg.portfwd;
+ while (*p) {
+ SendDlgItemMessage(hwnd, IDC_PFWDLIST, LB_ADDSTRING, 0,
+ (LPARAM) p);
+ p += strlen(p) + 1;
+ }
}
CheckRadioButton(hwnd, IDC_EMBSD, IDC_EMRFC,
cfg.rfc_environ ? IDC_EMRFC : IDC_EMBSD);
CheckDlgButton(hwnd, IDC_X11_FORWARD, cfg.x11_forward);
SetDlgItemText(hwnd, IDC_X11_DISPLAY, cfg.x11_display);
+
+ CheckDlgButton(hwnd, IDC_LPORT_ALL, cfg.lport_acceptall);
+ CheckRadioButton(hwnd, IDC_PFWDLOCAL, IDC_PFWDREMOTE, IDC_PFWDLOCAL);
}
struct treeview_faff {
}
if (panel == tunnelspanelstart) {
- /* The Tunnels panel. Accelerators used: [acgo] ex */
+ /* The Tunnels panel. Accelerators used: [acgo] deilmrstx */
struct ctlpos cp;
ctlposinit(&cp, hwnd, 80, 3, 13);
if (dlgtype == 0) {
bartitle(&cp, "Options controlling SSH tunnelling",
IDC_TITLE_TUNNELS);
- beginbox(&cp, "X11 forwarding options", IDC_BOX_TUNNELS);
+ beginbox(&cp, "X11 forwarding", IDC_BOX_TUNNELS1);
checkbox(&cp, "&Enable X11 forwarding", IDC_X11_FORWARD);
multiedit(&cp, "&X display location", IDC_X11_DISPSTATIC,
IDC_X11_DISPLAY, 50, NULL);
endbox(&cp);
+ beginbox(&cp, "Port forwarding", IDC_BOX_TUNNELS2);
+ checkbox(&cp, "Local ports accept connections from o&ther hosts", IDC_LPORT_ALL);
+ staticbtn(&cp, "Forwarded ports:", IDC_PFWDSTATIC,
+ "&Remove", IDC_PFWDREMOVE);
+ fwdsetter(&cp, IDC_PFWDLIST,
+ "Add new forwarded port:", IDC_PFWDSTATIC2,
+ "&Source port", IDC_SPORTSTATIC, IDC_SPORTEDIT,
+ "Dest&ination", IDC_DPORTSTATIC, IDC_DPORTEDIT,
+ "A&dd", IDC_PFWDADD);
+ bareradioline(&cp, 2,
+ "&Local", IDC_PFWDLOCAL, "Re&mote", IDC_PFWDREMOTE, NULL);
+ endbox(&cp);
+
}
}
}
cfg.x11_forward =
IsDlgButtonChecked(hwnd, IDC_X11_FORWARD);
break;
+ case IDC_LPORT_ALL:
+ if (HIWORD(wParam) == BN_CLICKED ||
+ HIWORD(wParam) == BN_DOUBLECLICKED)
+ cfg.lport_acceptall =
+ IsDlgButtonChecked(hwnd, IDC_LPORT_ALL);
+ break;
case IDC_X11_DISPLAY:
if (HIWORD(wParam) == EN_CHANGE)
GetDlgItemText(hwnd, IDC_X11_DISPLAY, cfg.x11_display,
sizeof(cfg.x11_display) - 1);
break;
+ case IDC_PFWDADD:
+ if (HIWORD(wParam) == BN_CLICKED ||
+ HIWORD(wParam) == BN_DOUBLECLICKED) {
+ char str[sizeof(cfg.portfwd)];
+ char *p;
+ if (IsDlgButtonChecked(hwnd, IDC_PFWDLOCAL))
+ str[0] = 'L';
+ else
+ str[0] = 'R';
+ GetDlgItemText(hwnd, IDC_SPORTEDIT, str+1,
+ sizeof(str) - 2);
+ if (!str[1]) {
+ MessageBox(hwnd,
+ "You need to specify a source port number",
+ "PuTTY Error", MB_OK | MB_ICONERROR);
+ break;
+ }
+ p = str + strlen(str);
+ *p++ = '\t';
+ GetDlgItemText(hwnd, IDC_DPORTEDIT, p,
+ sizeof(str) - 1 - (p - str));
+ if (!*p || !strchr(p, ':')) {
+ MessageBox(hwnd,
+ "You need to specify a destination address\n"
+ "in the form \"host.name:port\"",
+ "PuTTY Error", MB_OK | MB_ICONERROR);
+ break;
+ }
+ p = cfg.portfwd;
+ while (*p) {
+ while (*p)
+ p++;
+ p++;
+ }
+ if ((p - cfg.portfwd) + strlen(str) + 2 <
+ sizeof(cfg.portfwd)) {
+ strcpy(p, str);
+ p[strlen(str) + 1] = '\0';
+ SendDlgItemMessage(hwnd, IDC_PFWDLIST, LB_ADDSTRING,
+ 0, (LPARAM) str);
+ SetDlgItemText(hwnd, IDC_SPORTEDIT, "");
+ SetDlgItemText(hwnd, IDC_DPORTEDIT, "");
+ } else {
+ MessageBox(hwnd, "Too many forwardings",
+ "PuTTY Error", MB_OK | MB_ICONERROR);
+ }
+ }
+ break;
+ case IDC_PFWDREMOVE:
+ if (HIWORD(wParam) != BN_CLICKED &&
+ HIWORD(wParam) != BN_DOUBLECLICKED) break;
+ i = SendDlgItemMessage(hwnd, IDC_PFWDLIST,
+ LB_GETCURSEL, 0, 0);
+ if (i == LB_ERR)
+ MessageBeep(0);
+ else {
+ char *p, *q;
+
+ SendDlgItemMessage(hwnd, IDC_PFWDLIST, LB_DELETESTRING,
+ i, 0);
+ p = cfg.portfwd;
+ while (i > 0) {
+ if (!*p)
+ goto disaster2;
+ while (*p)
+ p++;
+ p++;
+ i--;
+ }
+ q = p;
+ if (!*p)
+ goto disaster2;
+ while (*p)
+ p++;
+ p++;
+ while (*p) {
+ while (*p)
+ *q++ = *p++;
+ *q++ = *p++;
+ }
+ *q = '\0';
+ disaster2:;
+ }
+ break;
}
return 0;
case WM_CLOSE:
int msg, events;
if (startup) {
msg = WM_NETEVENT;
- events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
+ events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE | FD_ACCEPT;
} else {
msg = events = 0;
}
void *private_ptr;
struct buffer *head, *tail;
int writable;
+ int frozen; /* this tells the write stuff not to even bother trying to send at this point */
int sending_oob;
int oobinline;
};
static void sk_tcp_write_oob(Socket s, char *data, int len);
static char *sk_tcp_socket_error(Socket s);
+extern char *do_select(SOCKET skt, int startup);
+
+Socket sk_register(void *sock, Plug plug)
+{
+ static struct socket_function_table fn_table = {
+ sk_tcp_plug,
+ sk_tcp_close,
+ sk_tcp_write,
+ sk_tcp_write_oob,
+ sk_tcp_flush,
+ sk_tcp_socket_error
+ };
+
+ DWORD err;
+ char *errstr;
+ Actual_Socket ret;
+
+ /*
+ * Create Socket structure.
+ */
+ ret = smalloc(sizeof(struct Socket_tag));
+ ret->fn = &fn_table;
+ ret->error = NULL;
+ ret->plug = plug;
+ ret->head = ret->tail = NULL;
+ ret->writable = 1; /* to start with */
+ ret->sending_oob = 0;
+ ret->frozen = 1;
+
+ ret->s = (SOCKET)sock;
+
+ if (ret->s == INVALID_SOCKET) {
+ err = WSAGetLastError();
+ ret->error = winsock_error_string(err);
+ return (Socket) ret;
+ }
+
+ ret->oobinline = 0;
+
+ /* Set up a select mechanism. This could be an AsyncSelect on a
+ * window, or an EventSelect on an event object. */
+ errstr = do_select(ret->s, 1);
+ if (errstr) {
+ ret->error = errstr;
+ return (Socket) ret;
+ }
+
+ add234(sktree, ret);
+
+ return (Socket) ret;
+}
+
Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
Plug plug)
{
DWORD err;
char *errstr;
Actual_Socket ret;
- extern char *do_select(SOCKET skt, int startup);
short localport;
/*
ret->head = ret->tail = NULL;
ret->writable = 1; /* to start with */
ret->sending_oob = 0;
+ ret->frozen = 0;
/*
* Open socket.
return (Socket) ret;
}
+Socket sk_newlistenner(int port, Plug plug)
+{
+ static struct socket_function_table fn_table = {
+ sk_tcp_plug,
+ sk_tcp_close,
+ sk_tcp_write,
+ sk_tcp_write_oob,
+ sk_tcp_flush,
+ sk_tcp_socket_error
+ };
+
+ SOCKET s;
+#ifdef IPV6
+ SOCKADDR_IN6 a6;
+#endif
+ SOCKADDR_IN a;
+ DWORD err;
+ char *errstr;
+ Actual_Socket ret;
+ int retcode;
+ int on = 1;
+
+ /*
+ * Create Socket structure.
+ */
+ ret = smalloc(sizeof(struct Socket_tag));
+ ret->fn = &fn_table;
+ ret->error = NULL;
+ ret->plug = plug;
+ ret->head = ret->tail = NULL;
+ ret->writable = 0; /* to start with */
+ ret->sending_oob = 0;
+ ret->frozen = 0;
+
+ /*
+ * Open socket.
+ */
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ ret->s = s;
+
+ if (s == INVALID_SOCKET) {
+ err = WSAGetLastError();
+ ret->error = winsock_error_string(err);
+ return (Socket) ret;
+ }
+
+ ret->oobinline = 0;
+
+
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
+
+
+#ifdef IPV6
+ if (addr->family == AF_INET6) {
+ memset(&a6, 0, sizeof(a6));
+ a6.sin6_family = AF_INET6;
+/*a6.sin6_addr = in6addr_any; *//* == 0 */
+ a6.sin6_port = htons(port);
+ } else
+#endif
+ {
+ a.sin_family = AF_INET;
+ a.sin_addr.s_addr = htonl(INADDR_ANY);
+ a.sin_port = htons((short)port);
+ }
+#ifdef IPV6
+ retcode = bind(s, (addr->family == AF_INET6 ?
+ (struct sockaddr *) &a6 :
+ (struct sockaddr *) &a),
+ (addr->family ==
+ AF_INET6 ? sizeof(a6) : sizeof(a)));
+#else
+ retcode = bind(s, (struct sockaddr *) &a, sizeof(a));
+#endif
+ if (retcode != SOCKET_ERROR) {
+ err = 0;
+ } else {
+ err = WSAGetLastError();
+ }
+
+ if (err) {
+ ret->error = winsock_error_string(err);
+ return (Socket) ret;
+ }
+
+
+ if (listen(s, SOMAXCONN) == SOCKET_ERROR) {
+ closesocket(s);
+ ret->error = winsock_error_string(err);
+ return (Socket) ret;
+ }
+
+ /* Set up a select mechanism. This could be an AsyncSelect on a
+ * window, or an EventSelect on an event object. */
+ errstr = do_select(s, 1);
+ if (errstr) {
+ ret->error = errstr;
+ return (Socket) ret;
+ }
+
+ add234(sktree, ret);
+
+ return (Socket) ret;
+}
+
static void sk_tcp_close(Socket sock)
{
extern char *do_select(SOCKET skt, int startup);
*/
void try_send(Actual_Socket s)
{
+ if (s->frozen) return;
while (s->head) {
int nsent;
DWORD err;
switch (WSAGETSELECTEVENT(lParam)) {
case FD_READ:
+
+ /* In the case the socket is still frozen, we don't even bother */
+ if (s->frozen)
+ break;
+
/*
* We have received data on the socket. For an oobinline
* socket, this might be data _before_ an urgent pointer,
}
} while (ret > 0);
return open;
+ case FD_ACCEPT:
+ {
+ struct sockaddr isa;
+ int addrlen = sizeof(struct sockaddr);
+ SOCKET t; /* socket of connection */
+
+ memset(&isa, 0, sizeof(struct sockaddr));
+ err = 0;
+ t = accept(s->s,&isa,&addrlen);
+ if (t == INVALID_SOCKET)
+ {
+ err = WSAGetLastError();
+ if (err == WSATRY_AGAIN)
+ break;
+ }
+
+ if (plug_accepting(s->plug, &isa, (void*)t)) {
+ closesocket(t); // denied or error
+ }
+ }
}
return 1;
return s->error;
}
+void sk_set_frozen(Socket sock, int is_frozen)
+{
+ Actual_Socket s = (Actual_Socket) sock;
+ s->frozen = is_frozen;
+ if (!is_frozen) {
+ char c;
+ recv(s->s, &c, 1, MSG_PEEK);
+ }
+}
+
/*
* For Plink: enumerate all sockets currently active.
*/
void endbox(struct ctlpos *cp);
void multiedit(struct ctlpos *cp, ...);
void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...);
+void bareradioline(struct ctlpos *cp, int nacross, ...);
void radiobig(struct ctlpos *cp, char *text, int id, ...);
void checkbox(struct ctlpos *cp, char *text, int id);
void statictext(struct ctlpos *cp, char *text, int id);
void colouredit(struct ctlpos *cp, char *stext, int sid, int listid,
char *btext, int bid, ...);
void progressbar(struct ctlpos *cp, int id);
+void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid,
+ char *e1stext, int e1sid, int e1id,
+ char *e2stext, int e2sid, int e2id,
+ char *btext, int bid);
void x11_close(Socket s)
{
struct X11Private *pr;
- \rif (!s)
- \rreturn;
- \rpr = (struct X11Private *) sk_get_private_ptr(s);
-
+ if (!s)
+ return;
+ pr = (struct X11Private *) sk_get_private_ptr(s);
if (pr->auth_protocol) {
sfree(pr->auth_protocol);
sfree(pr->auth_data);