version 1.103, 2001/04/07 08:55:17 |
version 1.104, 2001/04/10 07:46:58 |
|
|
} |
} |
} |
} |
|
|
#define SSH_SOCKS_HEAD 1+1+2+4 |
|
|
|
void |
int |
channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) |
channel_decode_helper(Channel *c, int start, int lookfor) |
{ |
{ |
|
u_char *p; |
|
int i, have; |
|
|
|
p = buffer_ptr(&c->input); |
|
have = buffer_len(&c->input); |
|
debug2("channel %d: decode_helper: start %d have %d lookfor %d", |
|
c->self, start, have, lookfor); |
|
if (have < start) |
|
return 0; |
|
for (i = start; i < have; i++) { |
|
if (p[i] == lookfor) { |
|
debug2("channel %d: decode_helper: matched at %d", |
|
c->self, i); |
|
if (lookfor == '\0' || |
|
(i+3 < have && |
|
p[i+1] == '\n' && |
|
p[i+2] == '\r' && |
|
p[i+3] == '\n')) |
|
return i; |
|
} |
|
if (i > 4096) { |
|
/* the peer is probably sending garbage */ |
|
debug("channel %d: decode_helper: too long", |
|
c->self); |
|
return -1; |
|
} |
|
} |
|
return 0; /* need more */ |
|
} |
|
|
|
/* try to decode a socks4 header */ |
|
int |
|
channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) |
|
{ |
u_char *p, *host; |
u_char *p, *host; |
int len, i, done, have; |
int len, have, ret; |
char username[256]; |
char username[256]; |
struct { |
struct { |
u_int8_t version; |
u_int8_t version; |
u_int8_t command; |
u_int8_t command; |
u_int16_t dest_port; |
u_int16_t dest_port; |
struct in_addr dest_ip; |
struct in_addr dest_addr; |
} s4_req, s4_rsp; |
} s4_req, s4_rsp; |
|
|
have = buffer_len(&c->input); |
debug2("channel %d: decode socks4", c->self); |
|
ret = channel_decode_helper(c, sizeof(s4_req), '\0'); |
debug("channel %d: pre_dynamic have: %d", c->self, have); |
if (ret <= 0) |
/*buffer_dump(&c->input);*/ |
return ret; |
|
|
/* 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.version, 1); |
buffer_get(&c->input, (char *)&s4_req.command, 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_port, 2); |
buffer_get(&c->input, (char *)&s4_req.dest_ip, 4); |
buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); |
p = buffer_ptr(&c->input); |
p = buffer_ptr(&c->input); |
len = strlen(p); |
len = strlen(p); |
have = buffer_len(&c->input); |
have = buffer_len(&c->input); |
debug2("channel %d: pre_dynamic user: %s/%d", c->self, p, len); |
debug2("channel %d: pre_dynamic: user %s/%d", c->self, p, len); |
if (len > have) |
if (len > have) |
fatal("channel %d: pre_dynamic: len %d > have %d", |
fatal("channel %d: decode socks4: len %d > have %d", |
c->self, len, have); |
c->self, len, have); |
strlcpy(username, p, sizeof(username)); |
strlcpy(username, p, sizeof(username)); |
buffer_consume(&c->input, len); |
buffer_consume(&c->input, len); |
buffer_consume(&c->input, 1); /* trailing '\0' */ |
buffer_consume(&c->input, 1); /* trailing '\0' */ |
|
|
host = inet_ntoa(s4_req.dest_ip); |
host = inet_ntoa(s4_req.dest_addr); |
strlcpy(c->path, host, sizeof(c->path)); |
strlcpy(c->path, host, sizeof(c->path)); |
c->host_port = ntohs(s4_req.dest_port); |
c->host_port = ntohs(s4_req.dest_port); |
|
|
debug("channel %d: dynamic request received: " |
debug("channel %d: dynamic request: " |
"socks%x://%s@%s:%u/command?%u", |
"socks%x://%s@%s:%u/command?%u", |
c->self, s4_req.version, username, host, c->host_port, |
c->self, s4_req.version, username, host, c->host_port, |
s4_req.command); |
s4_req.command); |
|
|
if ((s4_req.version != 4) || (s4_req.command != 1)) { |
if (s4_req.command != 1) { |
debug("channel %d: cannot handle: socks VN %d CN %d", |
debug("channel %d: cannot handle: socks4 cn %d", |
c->self, s4_req.version, s4_req.command); |
c->self, s4_req.command); |
channel_free(c->self); |
return -1; |
return; |
|
} |
} |
|
s4_rsp.version = 0; /* vn: 0 for reply */ |
s4_rsp.version = 0; /* VN: version of reply code */ |
s4_rsp.command = 90; /* cd: req granted */ |
s4_rsp.command = 90; /* CD: request granted */ |
|
s4_rsp.dest_port = 0; /* ignored */ |
s4_rsp.dest_port = 0; /* ignored */ |
s4_rsp.dest_ip.s_addr = INADDR_ANY; /* ignored */ |
s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ |
buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp)); |
buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp)); |
|
return 1; |
/* switch to next state */ |
|
c->type = SSH_CHANNEL_OPENING; |
|
port_open_helper(c, "direct-tcpip"); |
|
} |
} |
|
|
|
/* dynamic port forwarding */ |
|
void |
|
channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) |
|
{ |
|
u_char *p; |
|
int have, ret; |
|
|
|
have = buffer_len(&c->input); |
|
|
|
debug2("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 < 4) { |
|
/* need more */ |
|
FD_SET(c->sock, readset); |
|
return; |
|
} |
|
/* try to guess the protocol */ |
|
p = buffer_ptr(&c->input); |
|
switch (p[0]) { |
|
case 0x04: |
|
ret = channel_decode_socks4(c, readset, writeset); |
|
break; |
|
#if 0 |
|
case 'C': |
|
ret = channel_decode_https(c, readset, writeset); |
|
break; |
|
case 0x05: |
|
ret = channel_decode_socks5(c, readset, writeset); |
|
break; |
|
#endif |
|
default: |
|
ret = -1; |
|
break; |
|
} |
|
if (ret < 0) { |
|
channel_free(c->self); |
|
} else if (ret == 0) { |
|
debug2("channel %d: pre_dynamic: need more", c->self); |
|
/* need more */ |
|
FD_SET(c->sock, readset); |
|
} else { |
|
/* switch to the next state */ |
|
c->type = SSH_CHANNEL_OPENING; |
|
port_open_helper(c, "direct-tcpip"); |
|
} |
|
} |
|
|
/* This is our fake X11 server socket. */ |
/* This is our fake X11 server socket. */ |
void |
void |
channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) |
channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) |
|
|
if (len <= 0) { |
if (len <= 0) { |
debug("channel %d: read<=0 rfd %d len %d", |
debug("channel %d: read<=0 rfd %d len %d", |
c->self, c->rfd, len); |
c->self, c->rfd, len); |
if (c->type == SSH_CHANNEL_DYNAMIC) { |
if (c->type != SSH_CHANNEL_OPEN) { |
/* |
debug("channel %d: not open", c->self); |
* 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); |
channel_free(c->self); |
return -1; |
return -1; |
} |
} else if (compat13) { |
if (compat13) { |
|
buffer_consume(&c->output, buffer_len(&c->output)); |
buffer_consume(&c->output, buffer_len(&c->output)); |
c->type = SSH_CHANNEL_INPUT_DRAINING; |
c->type = SSH_CHANNEL_INPUT_DRAINING; |
debug("Channel %d status set to input draining.", c->self); |
debug("Channel %d status set to input draining.", c->self); |
|
|
if (len < 0 && (errno == EINTR || errno == EAGAIN)) |
if (len < 0 && (errno == EINTR || errno == EAGAIN)) |
return 1; |
return 1; |
if (len <= 0) { |
if (len <= 0) { |
if (compat13) { |
if (c->type != SSH_CHANNEL_OPEN) { |
|
debug("channel %d: not open", c->self); |
|
channel_free(c->self); |
|
return -1; |
|
} else if (compat13) { |
buffer_consume(&c->output, buffer_len(&c->output)); |
buffer_consume(&c->output, buffer_len(&c->output)); |
debug("Channel %d status set to input draining.", c->self); |
debug("Channel %d status set to input draining.", c->self); |
c->type = SSH_CHANNEL_INPUT_DRAINING; |
c->type = SSH_CHANNEL_INPUT_DRAINING; |
|
|
int |
int |
channel_check_window(Channel *c) |
channel_check_window(Channel *c) |
{ |
{ |
if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && |
if (c->type == SSH_CHANNEL_OPEN && |
|
!(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && |
c->local_window < c->local_window_max/2 && |
c->local_window < c->local_window_max/2 && |
c->local_consumed > 0) { |
c->local_consumed > 0) { |
packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); |
packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); |
|
|
} |
} |
|
|
void |
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_handler_init_20(void) |
{ |
{ |
channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_20; |
channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_20; |
|
|
channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; |
channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; |
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; |
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; |
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; |
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; |
channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_dynamic; |
channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open_2; |
} |
} |
|
|
void |
void |
|
|
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; |
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_OUTPUT_DRAINING] = &channel_post_output_drain_13; |
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; |
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; |
channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_dynamic; |
channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open_1; |
} |
} |
|
|
void |
void |
|
|
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; |
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; |
channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1; |
channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1; |
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; |
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; |
channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_dynamic; |
channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open_1; |
} |
} |
|
|
void |
void |