base
This commit is contained in:
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
SOCKS server utils.
|
||||
Copyright (C) 2008, 2009, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include <time.h> /* for time() */
|
||||
|
||||
#include "ne_socket.h"
|
||||
#include "ne_utils.h"
|
||||
#include "ne_alloc.h"
|
||||
|
||||
#include "child.h"
|
||||
#include "tests.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define V5_METH_NONE 0x00
|
||||
#define V5_METH_AUTH 0x02
|
||||
#define V5_ADDR_IPV4 0x01
|
||||
#define V5_ADDR_FQDN 0x03
|
||||
#define V5_ADDR_IPV6 0x04
|
||||
|
||||
static int read_socks_string(ne_socket *sock, const char *ctx,
|
||||
unsigned char *buf, unsigned int *olen)
|
||||
{
|
||||
unsigned char len;
|
||||
ssize_t ret;
|
||||
|
||||
ret = ne_sock_read(sock, (char *)&len, 1);
|
||||
ONV(ret != 1, ("%s length read failed: %s", ctx, ne_sock_error(sock)));
|
||||
|
||||
ONV(len == 0, ("%s gave zero-length string", ctx));
|
||||
|
||||
ret = ne_sock_fullread(sock, (char *)buf, len);
|
||||
ONV(ret != 0, ("%s string read failed, got %" NE_FMT_SSIZE_T
|
||||
" bytes (%s)", ctx, ret, ne_sock_error(sock)));
|
||||
|
||||
*olen = len;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int read_socks_byte(ne_socket *sock, const char *ctx,
|
||||
unsigned char *buf)
|
||||
{
|
||||
ONV(ne_sock_read(sock, (char *)buf, 1) != 1,
|
||||
("%s byte read failed: %s", ctx, ne_sock_error(sock)));
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int expect_socks_byte(ne_socket *sock, const char *ctx,
|
||||
unsigned char c)
|
||||
{
|
||||
unsigned char b;
|
||||
|
||||
CALL(read_socks_byte(sock, ctx, &b));
|
||||
|
||||
ONV(b != c, ("%s got byte %hx not %hx", ctx, b, c));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int read_socks_0string(ne_socket *sock, const char *ctx,
|
||||
unsigned char *buf, unsigned *len)
|
||||
{
|
||||
unsigned char *end = buf + *len, *p = buf;
|
||||
|
||||
while (p < end) {
|
||||
CALL(read_socks_byte(sock, ctx, p));
|
||||
|
||||
if (*p == '\0')
|
||||
break;
|
||||
p++;
|
||||
|
||||
}
|
||||
|
||||
*len = p - buf;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int socks_server(ne_socket *sock, void *userdata)
|
||||
{
|
||||
struct socks_server *srv = userdata;
|
||||
unsigned char buf[1024];
|
||||
unsigned int len, port, version;
|
||||
unsigned char atype;
|
||||
ssize_t ret;
|
||||
|
||||
version = srv->version == NE_SOCK_SOCKSV5 ? 5 : 4;
|
||||
|
||||
ne_sock_read_timeout(sock, 5);
|
||||
|
||||
CALL(expect_socks_byte(sock, "client version", version));
|
||||
|
||||
if (version != 5) {
|
||||
unsigned char raw[16];
|
||||
|
||||
CALL(expect_socks_byte(sock, "v4 command", 0x01));
|
||||
|
||||
ret = ne_sock_fullread(sock, (char *)buf, 6);
|
||||
ONV(ret != 0,
|
||||
("v4 address read failed with %" NE_FMT_SSIZE_T
|
||||
" (%s)", ret, ne_sock_error(sock)));
|
||||
|
||||
ONN("bad v4A bogus address",
|
||||
srv->version == NE_SOCK_SOCKSV4A && srv->expect_addr == NULL
|
||||
&& memcmp(buf + 2, "\0\0\0", 3) != 0 && buf[6] != 0);
|
||||
|
||||
ONN("v4 server with no expected address! fail",
|
||||
srv->version == NE_SOCK_SOCKSV4 && srv->expect_addr == NULL);
|
||||
|
||||
if (srv->expect_addr) {
|
||||
ONN("v4 address mismatch",
|
||||
memcmp(ne_iaddr_raw(srv->expect_addr, raw), buf + 2, 4) != 0);
|
||||
}
|
||||
|
||||
port = (buf[0] << 8) | buf[1];
|
||||
ONV(port != srv->expect_port,
|
||||
("got bad v4 port %u, expected %u", port, srv->expect_port));
|
||||
|
||||
len = sizeof buf;
|
||||
CALL(read_socks_0string(sock, "v4 username read", buf, &len));
|
||||
|
||||
ONV(srv->username == NULL && len, ("unexpected v4 username %s", buf));
|
||||
ONV(srv->username && !len,
|
||||
("no v4 username given, expected %s", srv->username));
|
||||
ONV(srv->username && len && strcmp(srv->username, (char *)buf),
|
||||
("bad v4 username, expected %s got %s", srv->username, buf));
|
||||
|
||||
if (srv->expect_addr == NULL) {
|
||||
len = sizeof buf;
|
||||
CALL(read_socks_0string(sock, "v4A hostname read", buf, &len));
|
||||
ONV(strcmp(srv->expect_fqdn, (char *)buf) != 0,
|
||||
("bad v4A hostname: %s not %s", buf, srv->expect_fqdn));
|
||||
}
|
||||
|
||||
{
|
||||
static const char msg[] = "\x00\x5A"
|
||||
"\x00\x00" "\x00\x00\x00\x00"
|
||||
"ok!\n";
|
||||
|
||||
if (srv->say_hello)
|
||||
CALL(full_write(sock, msg, 12));
|
||||
else
|
||||
CALL(full_write(sock, msg, 8));
|
||||
}
|
||||
|
||||
return srv->server(sock, srv->userdata);
|
||||
}
|
||||
|
||||
CALL(read_socks_string(sock, "client method list", buf, &len));
|
||||
|
||||
if (srv->failure == fail_init_vers) {
|
||||
CALL(full_write(sock, "\x01\x02", 2));
|
||||
return OK;
|
||||
}
|
||||
else if (srv->failure == fail_init_close) {
|
||||
return OK;
|
||||
}
|
||||
else if (srv->failure == fail_init_trunc) {
|
||||
CALL(full_write(sock, "\x05", 1));
|
||||
return OK;
|
||||
}
|
||||
else if (srv->failure == fail_no_auth) {
|
||||
CALL(full_write(sock, "\x05\xff", 2));
|
||||
return OK;
|
||||
}
|
||||
else if (srv->failure == fail_bogus_auth) {
|
||||
CALL(full_write(sock, "\x05\xfe", 2));
|
||||
return OK;
|
||||
}
|
||||
|
||||
ONN("client did not advertise no-auth method",
|
||||
memchr(buf, V5_METH_NONE, len) == NULL);
|
||||
|
||||
if (srv->username) {
|
||||
int match = 0;
|
||||
|
||||
ONN("client did not advertise authn method",
|
||||
memchr(buf, V5_METH_AUTH, len) == NULL);
|
||||
|
||||
CALL(full_write(sock, "\x05\x02", 2));
|
||||
|
||||
CALL(expect_socks_byte(sock, "client auth version", 0x01));
|
||||
|
||||
CALL(read_socks_string(sock, "client username", buf, &len));
|
||||
|
||||
match = len == strlen(srv->username)
|
||||
&& memcmp(buf, srv->username, len) == 0;
|
||||
|
||||
CALL(read_socks_string(sock, "client password", buf, &len));
|
||||
|
||||
match = match && len == strlen(srv->password)
|
||||
&& memcmp(buf, srv->password, len) == 0;
|
||||
|
||||
if (srv->failure == fail_auth_close) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (match && srv->failure != fail_auth_denied) {
|
||||
CALL(full_write(sock, "\x01\x00", 2));
|
||||
}
|
||||
else {
|
||||
CALL(full_write(sock, "\x01\x01", 2));
|
||||
}
|
||||
|
||||
if (srv->failure == fail_auth_denied) {
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
else {
|
||||
CALL(full_write(sock, "\x05\x00", 2));
|
||||
}
|
||||
|
||||
CALL(expect_socks_byte(sock, "command version", version));
|
||||
|
||||
CALL(expect_socks_byte(sock, "command number", 0x01));
|
||||
CALL(read_socks_byte(sock, "reserved byte", buf));
|
||||
|
||||
CALL(read_socks_byte(sock, "address type", &atype));
|
||||
|
||||
ONN("bad address type byte",
|
||||
(atype != V5_ADDR_IPV4 && atype != V5_ADDR_IPV6
|
||||
&& atype != V5_ADDR_FQDN));
|
||||
|
||||
if (atype == V5_ADDR_FQDN) {
|
||||
ONN("unexpected FQDN from client", srv->expect_fqdn == NULL);
|
||||
CALL(read_socks_string(sock, "read FQDN", buf, &len));
|
||||
ONV(len != strlen(srv->expect_fqdn)
|
||||
|| memcmp(srv->expect_fqdn, buf, len) != 0,
|
||||
("FQDN mismatch: %.*s not %s", len, buf,
|
||||
srv->expect_fqdn));
|
||||
}
|
||||
else {
|
||||
unsigned char raw[16];
|
||||
|
||||
ONN("unexpected IP literal from client", srv->expect_addr == NULL);
|
||||
|
||||
ONV((atype == V5_ADDR_IPV4
|
||||
&& ne_iaddr_typeof(srv->expect_addr) != ne_iaddr_ipv4)
|
||||
|| (atype == V5_ADDR_IPV6
|
||||
&& ne_iaddr_typeof(srv->expect_addr) != ne_iaddr_ipv6),
|
||||
("address type mismatch: %hx not %d",
|
||||
atype, ne_iaddr_typeof(srv->expect_addr)));
|
||||
|
||||
len = atype == V5_ADDR_IPV4 ? 4 : 16;
|
||||
ret = ne_sock_fullread(sock, (char *)buf, len);
|
||||
ONV(ret != 0,
|
||||
("address read failed with %" NE_FMT_SSIZE_T
|
||||
" (%s)", ret, ne_sock_error(sock)));
|
||||
|
||||
ne_iaddr_raw(srv->expect_addr, raw);
|
||||
|
||||
ONN("address mismatch", memcmp(raw, buf, len) != 0);
|
||||
}
|
||||
|
||||
CALL(read_socks_byte(sock, "port high byte", buf));
|
||||
CALL(read_socks_byte(sock, "port low byte", buf + 1));
|
||||
|
||||
port = (buf[0] << 8) | buf[1];
|
||||
ONV(port != srv->expect_port,
|
||||
("got bad port %u, expected %u", port, srv->expect_port));
|
||||
|
||||
{
|
||||
static const char msg[] =
|
||||
"\x05\x00\x00"
|
||||
"\x01" "\x00\x00\x00\x00"
|
||||
"\x00\x00"
|
||||
"ok!\n";
|
||||
|
||||
if (srv->say_hello)
|
||||
CALL(full_write(sock, msg, 14));
|
||||
else
|
||||
CALL(full_write(sock, msg, 10));
|
||||
}
|
||||
|
||||
|
||||
return srv->server(sock, srv->userdata);
|
||||
}
|
||||
Reference in New Issue
Block a user