diff --git a/Makefile b/Makefile index ff22b150..ea9b1bb3 100644 --- a/Makefile +++ b/Makefile @@ -89,7 +89,7 @@ MOBJ2 = tree234.$(OBJ) 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) @@ -298,7 +298,8 @@ windlg.$(OBJ): windlg.c network.h puttymem.h storage.h winstuff.h putty.h ssh.h 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 ##-- diff --git a/network.h b/network.h index e4bb578f..e205b875 100644 --- a/network.h +++ b/network.h @@ -48,6 +48,10 @@ struct plug_function_table { * - 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 + */ }; @@ -59,6 +63,10 @@ void sk_addr_free(SockAddr addr); 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)) @@ -68,6 +76,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, #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 /* @@ -88,6 +97,14 @@ void *sk_get_private_ptr(Socket s); 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 **********/ @@ -96,7 +113,6 @@ char *sk_addr_error(SockAddr addr); * of what it will eventually look like. */ - typedef struct certificate *Certificate; typedef struct our_certificate *Our_Certificate; /* to be defined somewhere else, somehow */ diff --git a/plink.c b/plink.c index 3de6af2a..4113a812 100644 --- a/plink.c +++ b/plink.c @@ -256,7 +256,7 @@ char *do_select(SOCKET skt, int startup) { 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; } @@ -649,6 +649,9 @@ int main(int argc, char **argv) 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) { diff --git a/putty.h b/putty.h index 3fbde4ef..f2c9cc6e 100644 --- a/putty.h +++ b/putty.h @@ -313,6 +313,9 @@ typedef struct { /* 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; /* diff --git a/settings.c b/settings.c index b621fbf3..f75bac36 100644 --- a/settings.c +++ b/settings.c @@ -160,6 +160,26 @@ void save_settings(char *section, int do_host, Config * cfg) 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); } @@ -365,6 +385,28 @@ void load_settings(char *section, int do_host, Config * cfg) 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); } diff --git a/ssh.c b/ssh.c index a41e3bf4..cbcf439f 100644 --- a/ssh.c +++ b/ssh.c @@ -196,6 +196,12 @@ extern void x11_close(Socket); 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 @@ -263,6 +269,7 @@ enum { /* channel types */ CHAN_MAINSESSION, CHAN_X11, CHAN_AGENT, + CHAN_SOCKDATA }; /* @@ -286,9 +293,22 @@ struct ssh_channel { 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; @@ -330,6 +350,8 @@ static int ssh_echoing, ssh_editing; 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, @@ -393,6 +415,18 @@ static int ssh_channelfind(void *av, void *bv) 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; @@ -2263,7 +2297,10 @@ void sshfwd_close(struct ssh_channel *c) 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"); } } } @@ -2337,6 +2374,68 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) } } + { + 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, @@ -2460,6 +2559,73 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) 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. */ @@ -2474,11 +2640,17 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) 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); @@ -2497,6 +2669,9 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) 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) { @@ -3976,6 +4151,9 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) 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) { @@ -4059,6 +4237,9 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) 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(); @@ -4080,6 +4261,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) break; case CHAN_AGENT: break; + case CHAN_SOCKDATA: + break; } del234(ssh_channels, c); sfree(c->v2.outbuffer); @@ -4306,6 +4489,39 @@ static void ssh_special(Telnet_Special code) } } +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; @@ -4334,4 +4550,4 @@ Backend ssh_backend = { ssh_sendok, ssh_ldisc, 22 -}; +}; \ No newline at end of file diff --git a/ssh.h b/ssh.h index 014c406b..d7b5b162 100644 --- a/ssh.h +++ b/ssh.h @@ -1,6 +1,7 @@ #include #include "puttymem.h" +#include "network.h" /* * Useful thing. @@ -199,6 +200,8 @@ void random_add_noise(void *noise, int length); 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); @@ -271,6 +274,7 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, Bignum primegen(int bits, int modulus, int residue, int phase, progfn_t pfn, void *pfnparam); + /* * zlib compression. */ diff --git a/winctrls.c b/winctrls.c index f87d2658..bee184cc 100644 --- a/winctrls.c +++ b/winctrls.c @@ -148,35 +148,13 @@ void multiedit(struct ctlpos *cp, ...) 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 *); @@ -206,10 +184,52 @@ void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...) 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. @@ -763,3 +783,72 @@ void progressbar(struct ctlpos *cp, int id) #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; + } +} diff --git a/windlg.c b/windlg.c index 34d35af9..550e4794 100644 --- a/windlg.c +++ b/windlg.c @@ -503,10 +503,24 @@ enum { IDCX_ABOUT = 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 @@ -669,6 +683,12 @@ static void init_dlg_ctrls(HWND hwnd, int keepsess) (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); @@ -736,6 +756,9 @@ static void init_dlg_ctrls(HWND hwnd, int keepsess) 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 { @@ -1195,17 +1218,30 @@ static void create_controls(HWND hwnd, int dlgtype, int panel) } 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); + } } } @@ -2400,11 +2436,101 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg, 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: diff --git a/window.c b/window.c index ff7ae2a4..15ad7b3c 100644 --- a/window.c +++ b/window.c @@ -706,7 +706,7 @@ char *do_select(SOCKET skt, int startup) 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; } diff --git a/winnet.c b/winnet.c index 621c3cab..bcb93481 100644 --- a/winnet.c +++ b/winnet.c @@ -65,6 +65,7 @@ struct Socket_tag { 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; }; @@ -368,6 +369,58 @@ static void sk_tcp_write(Socket s, char *data, int len); 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) { @@ -388,7 +441,6 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, DWORD err; char *errstr; Actual_Socket ret; - extern char *do_select(SOCKET skt, int startup); short localport; /* @@ -401,6 +453,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, ret->head = ret->tail = NULL; ret->writable = 1; /* to start with */ ret->sending_oob = 0; + ret->frozen = 0; /* * Open socket. @@ -519,6 +572,111 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, 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); @@ -536,6 +694,7 @@ static void sk_tcp_close(Socket sock) */ void try_send(Actual_Socket s) { + if (s->frozen) return; while (s->head) { int nsent; DWORD err; @@ -694,6 +853,11 @@ int select_result(WPARAM wParam, LPARAM lParam) 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, @@ -769,6 +933,26 @@ int select_result(WPARAM wParam, LPARAM lParam) } } 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; @@ -805,6 +989,16 @@ static char *sk_tcp_socket_error(Socket sock) 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. */ diff --git a/winstuff.h b/winstuff.h index 17eaca35..eb610e00 100644 --- a/winstuff.h +++ b/winstuff.h @@ -38,6 +38,7 @@ void beginbox(struct ctlpos *cp, char *name, int idbox); 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); @@ -63,3 +64,7 @@ void charclass(struct ctlpos *cp, char *stext, int sid, int listid, 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); diff --git a/x11fwd.c b/x11fwd.c index 5be69b86..b13f695f 100644 --- a/x11fwd.c +++ b/x11fwd.c @@ -197,10 +197,9 @@ char *x11_init(Socket * s, char *display, void *c) void x11_close(Socket s) { struct X11Private *pr; - if (!s) - return; - pr = (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);