summaryrefslogtreecommitdiffstats
path: root/channels.c
diff options
context:
space:
mode:
authorBen Lindstrom <mouring@eviladmin.org>2001-04-08 18:30:26 +0000
committerBen Lindstrom <mouring@eviladmin.org>2001-04-08 18:30:26 +0000
commit3bb4f9da7382e62dc08eb72ba8307c28d4ccc566 (patch)
tree543bfbb95923d8b21e28086d1d60641a8d4f8f28 /channels.c
parent3fcf1a22b52ed8e66d2cf548d5d4528f67377af9 (diff)
- markus@cvs.openbsd.org 2001/04/07 08:55:18
[buffer.c channels.c channels.h readconf.c ssh.c] allow the ssh client act as a SOCKS4 proxy (dynamic local portforwarding). work by Dan Kaminsky <dankamin@cisco.com> and me. thanks to Dan for this great patch: use 'ssh -D 1080 host' and make netscape use localhost:1080 as a socks proxy.
Diffstat (limited to 'channels.c')
-rw-r--r--channels.c228
1 files changed, 182 insertions, 46 deletions
diff --git a/channels.c b/channels.c
index d5526fb8..0e334db8 100644
--- a/channels.c
+++ b/channels.c
@@ -40,7 +40,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: channels.c,v 1.102 2001/04/06 21:00:10 markus Exp $");
+RCSID("$OpenBSD: channels.c,v 1.103 2001/04/07 08:55:17 markus Exp $");
#include <openssl/rsa.h>
#include <openssl/dsa.h>
@@ -51,6 +51,7 @@ RCSID("$OpenBSD: channels.c,v 1.102 2001/04/06 21:00:10 markus Exp $");
#include "packet.h"
#include "xmalloc.h"
#include "buffer.h"
+#include "bufaux.h"
#include "uidswap.h"
#include "log.h"
#include "misc.h"
@@ -133,6 +134,8 @@ static int have_hostname_in_open = 0;
/* AF_UNSPEC or AF_INET or AF_INET6 */
extern int IPv4or6;
+void port_open_helper(Channel *c, char *rtype);
+
/* Sets specific protocol options. */
void
@@ -539,6 +542,89 @@ channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset)
}
}
+#define SSH_SOCKS_HEAD 1+1+2+4
+
+void
+channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset)
+{
+ u_char *p, *host;
+ int len, i, done, have;
+ char username[256];
+ struct {
+ u_int8_t version;
+ u_int8_t command;
+ u_int16_t dest_port;
+ struct in_addr dest_ip;
+ } s4_req, s4_rsp;
+
+ have = buffer_len(&c->input);
+
+ debug("channel %d: pre_dynamic have: %d", c->self, have);
+ /*buffer_dump(&c->input);*/
+
+ /* Check if the fixed size part of the packet is in buffer. */
+ if (have < SSH_SOCKS_HEAD + 1) {
+ /* need more */
+ FD_SET(c->sock, readset);
+ return;
+ }
+ /* Check for end of username */
+ p = buffer_ptr(&c->input);
+ done = 0;
+ for (i = SSH_SOCKS_HEAD; i < have; i++) {
+ if (p[i] == '\0') {
+ done = 1;
+ break;
+ }
+ }
+ if (!done) {
+ /* need more */
+ FD_SET(c->sock, readset);
+ return;
+ }
+ buffer_get(&c->input, (char *)&s4_req.version, 1);
+ buffer_get(&c->input, (char *)&s4_req.command, 1);
+ buffer_get(&c->input, (char *)&s4_req.dest_port, 2);
+ buffer_get(&c->input, (char *)&s4_req.dest_ip, 4);
+ p = buffer_ptr(&c->input);
+ len = strlen(p);
+ have = buffer_len(&c->input);
+ debug2("channel %d: pre_dynamic user: %s/%d", c->self, p, len);
+ if (len > have)
+ fatal("channel %d: pre_dynamic: len %d > have %d",
+ c->self, len, have);
+ strlcpy(username, p, sizeof(username));
+ buffer_consume(&c->input, len);
+ buffer_consume(&c->input, 1); /* trailing '\0' */
+
+ host = inet_ntoa(s4_req.dest_ip);
+ strlcpy(c->path, host, sizeof(c->path));
+ c->host_port = ntohs(s4_req.dest_port);
+
+ debug("channel %d: dynamic request received: "
+ "socks%x://%s@%s:%u/command?%u",
+ c->self, s4_req.version, username, host, c->host_port,
+ s4_req.command);
+
+ if ((s4_req.version != 4) || (s4_req.command != 1)) {
+ debug("channel %d: cannot handle: socks VN %d CN %d",
+ c->self, s4_req.version, s4_req.command);
+ channel_free(c->self);
+ return;
+ }
+
+ s4_rsp.version = 0; /* VN: version of reply code */
+ s4_rsp.command = 90; /* CD: request granted */
+ s4_rsp.dest_port = 0; /* ignored */
+ s4_rsp.dest_ip.s_addr = INADDR_ANY; /* ignored */
+ buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp));
+
+ /* switch to next state */
+ c->type = SSH_CHANNEL_OPENING;
+ port_open_helper(c, "direct-tcpip");
+}
+
+
/* This is our fake X11 server socket. */
void
channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset)
@@ -591,73 +677,100 @@ channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset)
}
}
+void
+port_open_helper(Channel *c, char *rtype)
+{
+ int direct;
+ char buf[1024];
+ char *remote_ipaddr = get_peer_ipaddr(c->sock);
+ u_short remote_port = get_peer_port(c->sock);
+
+ direct = (strcmp(rtype, "direct-tcpip") == 0);
+
+ snprintf(buf, sizeof buf,
+ "%s: listening port %d for %.100s port %d, "
+ "connect from %.200s port %d",
+ rtype, c->listening_port, c->path, c->host_port,
+ remote_ipaddr, remote_port);
+
+ xfree(c->remote_name);
+ c->remote_name = xstrdup(buf);
+
+ if (compat20) {
+ packet_start(SSH2_MSG_CHANNEL_OPEN);
+ packet_put_cstring(rtype);
+ packet_put_int(c->self);
+ packet_put_int(c->local_window_max);
+ packet_put_int(c->local_maxpacket);
+ if (direct) {
+ /* target host, port */
+ packet_put_cstring(c->path);
+ packet_put_int(c->host_port);
+ } else {
+ /* listen address, port */
+ packet_put_cstring(c->path);
+ packet_put_int(c->listening_port);
+ }
+ /* originator host and port */
+ packet_put_cstring(remote_ipaddr);
+ packet_put_int(remote_port);
+ packet_send();
+ } else {
+ packet_start(SSH_MSG_PORT_OPEN);
+ packet_put_int(c->self);
+ packet_put_cstring(c->path);
+ packet_put_int(c->host_port);
+ if (have_hostname_in_open)
+ packet_put_cstring(c->remote_name);
+ packet_send();
+ }
+ xfree(remote_ipaddr);
+}
+
/*
* This socket is listening for connections to a forwarded TCP/IP port.
*/
void
channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset)
{
+ Channel *nc;
struct sockaddr addr;
- int newsock, newch;
+ int newsock, newch, nextstate;
socklen_t addrlen;
- char buf[1024], *remote_ipaddr, *rtype;
- int remote_port;
-
- rtype = (c->type == SSH_CHANNEL_RPORT_LISTENER) ?
- "forwarded-tcpip" : "direct-tcpip";
+ char *rtype;
if (FD_ISSET(c->sock, readset)) {
debug("Connection to port %d forwarding "
"to %.100s port %d requested.",
c->listening_port, c->path, c->host_port);
+
+ rtype = (c->type == SSH_CHANNEL_RPORT_LISTENER) ?
+ "forwarded-tcpip" : "direct-tcpip";
+ nextstate = (c->host_port == 0) ? SSH_CHANNEL_DYNAMIC :
+ SSH_CHANNEL_OPENING;
+
addrlen = sizeof(addr);
newsock = accept(c->sock, &addr, &addrlen);
if (newsock < 0) {
error("accept: %.100s", strerror(errno));
return;
}
- remote_ipaddr = get_peer_ipaddr(newsock);
- remote_port = get_peer_port(newsock);
- snprintf(buf, sizeof buf,
- "listen port %d for %.100s port %d, "
- "connect from %.200s port %d",
- c->listening_port, c->path, c->host_port,
- remote_ipaddr, remote_port);
-
newch = channel_new(rtype,
- SSH_CHANNEL_OPENING, newsock, newsock, -1,
+ nextstate, newsock, newsock, -1,
c->local_window_max, c->local_maxpacket,
- 0, xstrdup(buf), 1);
- if (compat20) {
- packet_start(SSH2_MSG_CHANNEL_OPEN);
- packet_put_cstring(rtype);
- packet_put_int(newch);
- packet_put_int(c->local_window_max);
- packet_put_int(c->local_maxpacket);
- if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
- /* listen address, port */
- packet_put_string(c->path, strlen(c->path));
- packet_put_int(c->listening_port);
- } else {
- /* target host, port */
- packet_put_string(c->path, strlen(c->path));
- packet_put_int(c->host_port);
- }
- /* originator host and port */
- packet_put_cstring(remote_ipaddr);
- packet_put_int(remote_port);
- packet_send();
- } else {
- packet_start(SSH_MSG_PORT_OPEN);
- packet_put_int(newch);
- packet_put_string(c->path, strlen(c->path));
- packet_put_int(c->host_port);
- if (have_hostname_in_open) {
- packet_put_string(buf, strlen(buf));
- }
- packet_send();
+ 0, xstrdup(rtype), 1);
+
+ nc = channel_lookup(newch);
+ if (nc == NULL) {
+ error("xxx: no new channel:");
+ return;
}
- xfree(remote_ipaddr);
+ nc->listening_port = c->listening_port;
+ nc->host_port = c->host_port;
+ strlcpy(nc->path, c->path, sizeof(nc->path));
+
+ if (nextstate != SSH_CHANNEL_DYNAMIC)
+ port_open_helper(nc, rtype);
}
}
@@ -733,6 +846,15 @@ channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
if (len <= 0) {
debug("channel %d: read<=0 rfd %d len %d",
c->self, c->rfd, len);
+ if (c->type == SSH_CHANNEL_DYNAMIC) {
+ /*
+ * we are not yet connected to a remote peer,
+ * so the connection-close protocol won't work
+ */
+ debug("channel %d: dynamic: closed", c->self);
+ channel_free(c->self);
+ return -1;
+ }
if (compat13) {
buffer_consume(&c->output, buffer_len(&c->output));
c->type = SSH_CHANNEL_INPUT_DRAINING;
@@ -894,6 +1016,12 @@ channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)
}
void
+channel_post_dynamic(Channel *c, fd_set * readset, fd_set * writeset)
+{
+ channel_handle_rfd(c, readset, writeset);
+}
+
+void
channel_handler_init_20(void)
{
channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_20;
@@ -903,6 +1031,7 @@ channel_handler_init_20(void)
channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting;
+ channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic;
channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_2;
channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener;
@@ -910,6 +1039,7 @@ channel_handler_init_20(void)
channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener;
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener;
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting;
+ channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_dynamic;
}
void
@@ -923,6 +1053,7 @@ channel_handler_init_13(void)
channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining;
channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining;
channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting;
+ channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic;
channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1;
channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener;
@@ -930,6 +1061,7 @@ channel_handler_init_13(void)
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener;
channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13;
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting;
+ channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_dynamic;
}
void
@@ -941,12 +1073,14 @@ channel_handler_init_15(void)
channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting;
+ channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic;
channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener;
channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener;
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener;
channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1;
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting;
+ channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_dynamic;
}
void
@@ -1500,6 +1634,7 @@ channel_still_open()
case SSH_CHANNEL_RPORT_LISTENER:
case SSH_CHANNEL_CLOSED:
case SSH_CHANNEL_AUTH_SOCKET:
+ case SSH_CHANNEL_DYNAMIC:
case SSH_CHANNEL_CONNECTING: /* XXX ??? */
continue;
case SSH_CHANNEL_LARVAL:
@@ -1551,6 +1686,7 @@ channel_open_message()
case SSH_CHANNEL_LARVAL:
case SSH_CHANNEL_OPENING:
case SSH_CHANNEL_CONNECTING:
+ case SSH_CHANNEL_DYNAMIC:
case SSH_CHANNEL_OPEN:
case SSH_CHANNEL_X11_OPEN:
case SSH_CHANNEL_INPUT_DRAINING: