version 1.108, 2001/04/14 16:17:14 |
version 1.109, 2001/04/17 12:55:03 |
|
|
} |
} |
} |
} |
|
|
|
|
int |
|
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 http connect header */ |
|
int |
|
channel_decode_https(Channel *c, fd_set * readset, fd_set * writeset) |
|
{ |
|
u_char *p, *host, *buf; |
|
int port, ret; |
|
char httpok[] = "HTTP/1.0 200\r\n\r\n"; |
|
|
|
debug2("channel %d: decode https connect", c->self); |
|
ret = channel_decode_helper(c, strlen("connect "), '\r'); |
|
if (ret <= 0) |
|
return ret; |
|
p = buffer_ptr(&c->input); |
|
buf = xmalloc(ret+1); |
|
host = xmalloc(ret); |
|
memcpy(buf, p, ret); |
|
buf[ret] = '\0'; |
|
if (sscanf(buf, "CONNECT %[^:]:%u HTTP/", host, &port) != 2) { |
|
debug("channel %d: cannot parse http header", c->self); |
|
return -1; |
|
} |
|
debug("channel %d: dynamic request: https host %s port %u", |
|
c->self, host, port); |
|
strlcpy(c->path, host, sizeof(c->path)); |
|
c->host_port = port; |
|
xfree(host); |
|
xfree(buf); |
|
buffer_consume(&c->input, ret+4); |
|
buffer_append(&c->output, httpok, strlen(httpok)); |
|
|
|
return 1; |
|
} |
|
|
|
/* try to decode a socks4 header */ |
/* try to decode a socks4 header */ |
int |
int |
channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) |
channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) |
{ |
{ |
u_char *p, *host; |
u_char *p, *host; |
int len, have, ret; |
int len, have, i, found; |
char username[256]; |
char username[256]; |
struct { |
struct { |
u_int8_t version; |
u_int8_t version; |
|
|
} s4_req, s4_rsp; |
} s4_req, s4_rsp; |
|
|
debug2("channel %d: decode socks4", c->self); |
debug2("channel %d: decode socks4", c->self); |
ret = channel_decode_helper(c, sizeof(s4_req), '\0'); |
|
if (ret <= 0) |
have = buffer_len(&c->input); |
return ret; |
len = sizeof(s4_req); |
|
if (have < len) |
|
return 0; |
|
p = buffer_ptr(&c->input); |
|
for (found = 0, i = len; i < have; i++) { |
|
if (p[i] == '\0') { |
|
found = 1; |
|
break; |
|
} |
|
if (i > 1024) { |
|
/* the peer is probably sending garbage */ |
|
debug("channel %d: decode socks4: too long", |
|
c->self); |
|
return -1; |
|
} |
|
} |
|
if (!found) |
|
return 0; |
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_addr, 4); |
buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); |
|
have = buffer_len(&c->input); |
p = buffer_ptr(&c->input); |
p = buffer_ptr(&c->input); |
len = strlen(p); |
len = strlen(p); |
have = buffer_len(&c->input); |
|
debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); |
debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); |
if (len > have) |
if (len > have) |
fatal("channel %d: decode socks4: len %d > have %d", |
fatal("channel %d: decode socks4: len %d > have %d", |
|
|
return 1; |
return 1; |
} |
} |
|
|
/* try to decode a socks5 header */ |
|
#define SSH_SOCKS5_AUTHDONE 0x1000 |
|
#define SSH_SOCKS5_NOAUTH 0x00 |
|
#define SSH_SOCKS5_IPV4 0x01 |
|
#define SSH_SOCKS5_DOMAIN 0x03 |
|
#define SSH_SOCKS5_IPV6 0x04 |
|
#define SSH_SOCKS5_CONNECT 0x01 |
|
#define SSH_SOCKS5_SUCCESS 0x00 |
|
|
|
int |
|
channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) |
|
{ |
|
struct { |
|
u_int8_t version; |
|
u_int8_t command; |
|
u_int8_t reserved; |
|
u_int8_t atyp; |
|
} s5_req, s5_rsp; |
|
u_int16_t dest_port; |
|
u_char *p, dest_addr[255+1]; |
|
int i, have, found, nmethods, addrlen, af; |
|
|
|
debug2("channel %d: decode socks5", c->self); |
|
p = buffer_ptr(&c->input); |
|
if (p[0] != 0x05) |
|
return -1; |
|
have = buffer_len(&c->input); |
|
if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { |
|
/* format: ver | nmethods | methods */ |
|
if (have < 2) |
|
return 0; |
|
nmethods = p[1]; |
|
if (have < nmethods + 2) |
|
return 0; |
|
/* look for method: "NO AUTHENTICATION REQUIRED" */ |
|
for (found = 0, i = 2 ; i < nmethods + 2; i++) { |
|
if (p[i] == SSH_SOCKS5_NOAUTH ) { |
|
found = 1; |
|
break; |
|
} |
|
} |
|
if (!found) { |
|
debug("channel %d: method SSH_SOCKS5_NOAUTH not found", |
|
c->self); |
|
return -1; |
|
} |
|
buffer_consume(&c->input, nmethods + 2); |
|
buffer_put_char(&c->output, 0x05); /* version */ |
|
buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ |
|
FD_SET(c->sock, writeset); |
|
c->flags |= SSH_SOCKS5_AUTHDONE; |
|
debug2("channel %d: socks5 auth done", c->self); |
|
return 0; /* need more */ |
|
} |
|
debug2("channel %d: socks5 post auth", c->self); |
|
if (have < sizeof(s5_req)+1) |
|
return 0; /* need more */ |
|
memcpy((char *)&s5_req, p, sizeof(s5_req)); |
|
if (s5_req.version != 0x05 || |
|
s5_req.command != SSH_SOCKS5_CONNECT || |
|
s5_req.reserved != 0x00) { |
|
debug("channel %d: only socks5 connect supported", c->self); |
|
return -1; |
|
} |
|
switch(s5_req.atyp){ |
|
case SSH_SOCKS5_IPV4: |
|
addrlen = 4; |
|
af = AF_INET; |
|
break; |
|
case SSH_SOCKS5_DOMAIN: |
|
addrlen = p[sizeof(s5_req)]; |
|
af = -1; |
|
break; |
|
case SSH_SOCKS5_IPV6: |
|
addrlen = 16; |
|
af = AF_INET6; |
|
break; |
|
default: |
|
debug("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); |
|
return -1; |
|
} |
|
if (have < 4 + addrlen + 2) |
|
return 0; |
|
buffer_consume(&c->input, sizeof(s5_req)); |
|
buffer_get(&c->input, (char *)&dest_addr, addrlen); |
|
buffer_get(&c->input, (char *)&dest_port, 2); |
|
dest_addr[addrlen] = '\0'; |
|
if (s5_req.atyp == SSH_SOCKS5_DOMAIN) |
|
strlcpy(c->path, dest_addr, sizeof(c->path)); |
|
else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL) |
|
return -1; |
|
c->host_port = ntohs(dest_port); |
|
|
|
debug("channel %d: dynamic request: socks5 host %s port %u command %u", |
|
c->self, c->path, c->host_port, s5_req.command); |
|
|
|
s5_rsp.version = 0x05; |
|
s5_rsp.command = SSH_SOCKS5_SUCCESS; |
|
s5_rsp.reserved = 0; /* ignored */ |
|
s5_rsp.atyp = SSH_SOCKS5_IPV4; |
|
((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; |
|
dest_port = 0; /* ignored */ |
|
|
|
buffer_append(&c->output, (char *)&s5_rsp, sizeof(s5_rsp)); |
|
buffer_append(&c->output, (char *)&dest_addr, sizeof(struct in_addr)); |
|
buffer_append(&c->output, (char *)&dest_port, sizeof(dest_port)); |
|
return 1; |
|
} |
|
|
|
/* dynamic port forwarding */ |
/* dynamic port forwarding */ |
void |
void |
channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) |
channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) |
|
|
/* try to guess the protocol */ |
/* try to guess the protocol */ |
p = buffer_ptr(&c->input); |
p = buffer_ptr(&c->input); |
switch (p[0]) { |
switch (p[0]) { |
case 'C': |
|
ret = channel_decode_https(c, readset, writeset); |
|
break; |
|
case 0x04: |
case 0x04: |
ret = channel_decode_socks4(c, readset, writeset); |
ret = channel_decode_socks4(c, readset, writeset); |
break; |
|
case 0x05: |
|
ret = channel_decode_socks5(c, readset, writeset); |
|
break; |
break; |
default: |
default: |
ret = -1; |
ret = -1; |