mirror of
https://repo.or.cz/socat.git
synced 2025-06-10 11:46:50 +00:00
732 lines
20 KiB
C
732 lines
20 KiB
C
/* source: xio-socks5.c */
|
|
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
|
|
/* Published under the GNU General Public License V.2, see file COPYING */
|
|
|
|
/* This file contains the source for opening addresses of socks5 type */
|
|
|
|
/*
|
|
* At the moment UDP ASSOCIATE is not supported, but CONNECT and BIND are.
|
|
* At the moment no authentication methods are supported (i.e only NO AUTH),
|
|
* which is technically not compliant with RFC1928.
|
|
*/
|
|
|
|
#include "xiosysincludes.h"
|
|
|
|
#if WITH_SOCKS5
|
|
|
|
#include "xioopen.h"
|
|
#include "xio-ascii.h"
|
|
#include "xio-socket.h"
|
|
#include "xio-ip.h"
|
|
#include "xio-ipapp.h"
|
|
#include "xio-socks.h" /* _xioopen_opt_socksport() */
|
|
|
|
#include "xio-socks5.h"
|
|
|
|
|
|
#define SOCKS5_VERSION 5
|
|
|
|
#define SOCKS5_MAX_REPLY_SIZE (6 + 256)
|
|
|
|
#define SOCKS5_AUTH_NONE 0
|
|
#define SOCKS5_AUTH_FAIL 0xff
|
|
|
|
#define SOCKS5_COMMAND_CONNECT 1
|
|
#define SOCKS5_COMMAND_BIND 2
|
|
#define SOCKS5_COMMAND_UDP_ASSOCIATE 3
|
|
|
|
#define SOCKS5_ATYPE_IPv4 1
|
|
#define SOCKS5_ATYPE_DOMAINNAME 3
|
|
#define SOCKS5_ATYPE_IPv6 4
|
|
|
|
#define SOCKS5_STATUS_SUCCESS 0
|
|
#define SOCKS5_STATUS_GENERAL_FAILURE 1
|
|
#define SOCKS5_STATUS_CONNECTION_NOT_ALLOWED 2
|
|
#define SOCKS5_STATUS_NETWORK_UNREACHABLE 3
|
|
#define SOCKS5_STATUS_HOST_UNREACHABLE 4
|
|
#define SOCKS5_STATUS_CONNECTION_REFUSED 5
|
|
#define SOCKS5_STATUS_TTL_EXPIRED 6
|
|
#define SOCKS5_STATUS_COMMAND_NOT_SUPPORTED 7
|
|
#define SOCKS5_STATUS_ADDRESS_TYPE_NOT_SUPPORTED 8
|
|
|
|
static int xioopen_socks5(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
|
|
|
|
const struct addrdesc xioaddr_socks5_connect = { "SOCKS5-CONNECT", 1+XIO_RDWR, xioopen_socks5, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_TCP|GROUP_IP_SOCKS|GROUP_CHILD|GROUP_RETRY, SOCKS5_COMMAND_CONNECT, 0, 0 HELP(":<socks-server>[:<socks-port>]:<target-host>:<target-port>") };
|
|
|
|
const struct addrdesc xioaddr_socks5_listen = { "SOCKS5-LISTEN", 1+XIO_RDWR, xioopen_socks5, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_TCP|GROUP_IP_SOCKS|GROUP_CHILD|GROUP_RETRY, SOCKS5_COMMAND_BIND, 0, 0 HELP(":<socks-server>[:<socks-port>]:<listen-host>:<listen-port>") };
|
|
|
|
static const char * _xioopen_socks5_strerror(uint8_t r)
|
|
{
|
|
switch(r) {
|
|
case SOCKS5_STATUS_SUCCESS:
|
|
return "succeeded";
|
|
case SOCKS5_STATUS_GENERAL_FAILURE:
|
|
return "general SOCKS server failure";
|
|
case SOCKS5_STATUS_CONNECTION_NOT_ALLOWED:
|
|
return "connection not allowed by ruleset";
|
|
case SOCKS5_STATUS_NETWORK_UNREACHABLE:
|
|
return "network unreachable";
|
|
case SOCKS5_STATUS_HOST_UNREACHABLE:
|
|
return "host unreachable";
|
|
case SOCKS5_STATUS_CONNECTION_REFUSED:
|
|
return "connection refused";
|
|
case SOCKS5_STATUS_TTL_EXPIRED:
|
|
return "TTL expired";
|
|
case SOCKS5_STATUS_COMMAND_NOT_SUPPORTED:
|
|
return "command not supported";
|
|
case SOCKS5_STATUS_ADDRESS_TYPE_NOT_SUPPORTED:
|
|
return "address type not supported";
|
|
default:
|
|
return "unknown error";
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Performs the SOCKS5 handshake, i.e sends client hello and receives server
|
|
* hello back.
|
|
* If successful the connection is now ready for sending a SOCKS5 request.
|
|
*
|
|
* The code is unnecessarily complex right now, for what is essentially
|
|
* send(0x050100) followed by "return read() == 0x0500", but will be easier to
|
|
* extend for other auth mode support.
|
|
*/
|
|
static int _xioopen_socks5_handshake(struct single *sfd, int level)
|
|
{
|
|
int result;
|
|
ssize_t bytes;
|
|
struct socks5_server_hello server_hello;
|
|
int nmethods = 1; /* support only 1 auth method - no auth */
|
|
int client_hello_size =
|
|
sizeof(struct socks5_client_hello) +
|
|
(sizeof(uint8_t) * nmethods);
|
|
|
|
struct socks5_client_hello *client_hello = Malloc(client_hello_size);
|
|
if (client_hello == NULL) {
|
|
Msg2(level, "malloc(%d): %s",
|
|
client_hello_size, strerror(errno));
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd, strerror(errno));
|
|
}
|
|
|
|
/* malloc failed - could succeed later, so retry then */
|
|
return STAT_RETRYLATER;
|
|
}
|
|
|
|
unsigned char *server_hello_ptr = (unsigned char *)&server_hello;
|
|
|
|
/* SOCKS5 Hello with 1 authentication mechanism -
|
|
0x00 NO AUTHENTICATION */
|
|
client_hello->version = SOCKS5_VERSION;
|
|
client_hello->nmethods = 1;
|
|
client_hello->methods[0]= SOCKS5_AUTH_NONE;
|
|
|
|
/* Send SOCKS5 Client Hello */
|
|
Info2("sending socks5 client hello version=%d nmethods=%d",
|
|
client_hello->version,
|
|
client_hello->nmethods);
|
|
#if WITH_MSGLEVEL <= E_DEBUG
|
|
{
|
|
char *msgbuf;
|
|
if ((msgbuf = Malloc(3 * client_hello_size)) != NULL) {
|
|
xiohexdump((unsigned char *)client_hello,
|
|
client_hello_size, msgbuf);
|
|
Debug1("sending socks5 client hello %s", msgbuf);
|
|
free(msgbuf);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (writefull(sfd->fd, client_hello, client_hello_size, NULL) < 0) {
|
|
Msg4(level, "write(%d, %p, %d): %s",
|
|
sfd->fd, client_hello, client_hello_size,
|
|
strerror(errno));
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd, strerror(errno));
|
|
}
|
|
free(client_hello);
|
|
|
|
/* writefull() failed, but might succeed later, so RETRYLATER */
|
|
return STAT_RETRYLATER;
|
|
}
|
|
free(client_hello);
|
|
|
|
bytes = 0;
|
|
Info("waiting for socks5 reply");
|
|
while (bytes >= 0) {
|
|
do {
|
|
result = Read(sfd->fd, server_hello_ptr + bytes,
|
|
sizeof(struct socks5_server_hello)-bytes);
|
|
} while (result < 0 && errno == EINTR);
|
|
if (result < 0) {
|
|
Msg4(level, "read(%d, %p, "F_Zu"): %s",
|
|
sfd->fd, server_hello_ptr + bytes,
|
|
sizeof(struct socks5_server_hello)-bytes,
|
|
strerror(errno));
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd, strerror(errno));
|
|
}
|
|
}
|
|
if (result == 0) {
|
|
Msg(level, "read(): EOF during read of SOCKS5 server hello, peer might not be a SOCKS5 server");
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd,
|
|
strerror(errno));
|
|
}
|
|
|
|
return STAT_RETRYLATER;
|
|
}
|
|
|
|
bytes += result;
|
|
if (bytes == sizeof(struct socks5_server_hello)) {
|
|
Debug1("received all "F_Zd" bytes", bytes);
|
|
break;
|
|
}
|
|
Debug2("received %d bytes, waiting for "F_Zu" more bytes",
|
|
result, sizeof(struct socks5_server_hello)-bytes);
|
|
}
|
|
if (result <= 0) {
|
|
return STAT_RETRYLATER;
|
|
}
|
|
|
|
Debug2("received SOCKS5 server hello %02x %02x",
|
|
server_hello_ptr[0], server_hello_ptr[1]);
|
|
Info2("received SOCKS5 server hello version=%d method=%d",
|
|
server_hello.version,
|
|
server_hello.method);
|
|
|
|
if (server_hello.version != SOCKS5_VERSION) {
|
|
Msg2(level, "SOCKS5 Server Hello version was %d, not the expected %d, peer might not be a SOCKS5 server",
|
|
server_hello.version, SOCKS5_VERSION);
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd, strerror(errno));
|
|
}
|
|
return STAT_RETRYLATER;
|
|
}
|
|
|
|
if (server_hello.method == SOCKS5_AUTH_FAIL) {
|
|
Msg(level, "SOCKS5 authentication negotiation failed - client & server have no common supported methods");
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd, strerror(errno));
|
|
}
|
|
return STAT_RETRYLATER;
|
|
}
|
|
|
|
if (server_hello.method != SOCKS5_AUTH_NONE) {
|
|
Msg1(level, "SOCKS5 server requested unsupported auth method (%d)",
|
|
server_hello.method);
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd, strerror(errno));
|
|
}
|
|
return STAT_RETRYLATER;
|
|
}
|
|
|
|
/* Server accepted using no auth */
|
|
return STAT_OK;
|
|
}
|
|
|
|
/*
|
|
* Generates the SOCKS5 request for a given command, host and port
|
|
*/
|
|
static struct socks5_request *_xioopen_socks5_prepare_request(
|
|
int *bytes, const char *target_name, const char *target_port,
|
|
uint8_t socks_command, int level)
|
|
{
|
|
struct socks5_request *req;
|
|
char ipaddr[16];
|
|
uint16_t *dstport;
|
|
|
|
*bytes = 0;
|
|
|
|
if (inet_pton(AF_INET, target_name, ipaddr)){ /* if(valid_ipv4) */
|
|
*bytes = sizeof(struct socks5_request) + 4 + sizeof(uint16_t);
|
|
req = (struct socks5_request *)Malloc(*bytes);
|
|
if (req == NULL){
|
|
Info2("Malloc(%d): %s", *bytes, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
req->address_type = SOCKS5_ATYPE_IPv4;
|
|
memcpy(req->dstdata, ipaddr, 4);
|
|
|
|
dstport = (uint16_t *) &req->dstdata[4];
|
|
*dstport = parseport(target_port, IPPROTO_TCP);
|
|
} else if (inet_pton(AF_INET6, target_name, ipaddr)) { /* else if(valid_ipv6) */
|
|
*bytes = sizeof(struct socks5_request) + 16 + sizeof(uint16_t);
|
|
req = (struct socks5_request *)Malloc(*bytes);
|
|
if (req == NULL){
|
|
Info2("Malloc(%d): %s", *bytes, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
req->address_type = SOCKS5_ATYPE_IPv6;
|
|
memcpy(req->dstdata, ipaddr, 16);
|
|
|
|
dstport = (uint16_t *) &req->dstdata[16];
|
|
*dstport = parseport(target_port, IPPROTO_TCP);
|
|
} else { /* invalid IP, assume hostname */
|
|
int hlen = strlen(target_name);
|
|
if (hlen > 255) {
|
|
Msg(level, "target hostname too long (>255 bytes), aborting");
|
|
return NULL;
|
|
}
|
|
|
|
*bytes = sizeof(struct socks5_request) + 1 + hlen +
|
|
sizeof(uint16_t);
|
|
req = (struct socks5_request *)Malloc(*bytes);
|
|
if (req == NULL ){
|
|
Info2("malloc(%d): %s", *bytes, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
req->address_type = SOCKS5_ATYPE_DOMAINNAME;
|
|
req->dstdata[0] = (unsigned char) hlen;
|
|
memcpy(&req->dstdata[1], target_name, hlen);
|
|
|
|
dstport = (uint16_t *) &req->dstdata[hlen + 1];
|
|
*dstport = parseport(target_port, IPPROTO_TCP);
|
|
}
|
|
|
|
|
|
if (*dstport == 0){
|
|
free(req);
|
|
return NULL;
|
|
}
|
|
|
|
req->version = SOCKS5_VERSION;
|
|
req->command = socks_command;
|
|
req->reserved = 0;
|
|
|
|
return req;
|
|
}
|
|
|
|
/*
|
|
* Reads a server reply after a request has been sent
|
|
*/
|
|
static int _xioopen_socks5_read_reply(
|
|
struct single *sfd, struct socks5_reply *reply, int level)
|
|
{
|
|
int result = 0;
|
|
int bytes_read = 0;
|
|
int bytes_to_read = 5;
|
|
bool typechecked = false;
|
|
|
|
while (bytes_to_read >= 0) {
|
|
Info("reading SOCKS5 reply");
|
|
do {
|
|
result = Read(sfd->fd,
|
|
((unsigned char *)reply) + bytes_read,
|
|
bytes_to_read-bytes_read);
|
|
} while (result < 0 && errno == EINTR);
|
|
if (result < 0) {
|
|
Msg4(level, "read(%d, %p, %d): %s",
|
|
sfd->fd, ((unsigned char *)reply) + bytes_read,
|
|
bytes_to_read-bytes_read, strerror(errno));
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd, strerror(errno));
|
|
}
|
|
return STAT_RETRYLATER;
|
|
}
|
|
if (result == 0) {
|
|
Msg(level, "read(): EOF during read of SOCKS5 reply");
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s",
|
|
sfd->fd, strerror(errno));
|
|
}
|
|
return STAT_RETRYLATER;
|
|
}
|
|
Debug5("received SOCKS5 reply %02x %02x %02x %02x %02x",
|
|
((unsigned char *)reply+bytes_read)[0],
|
|
((unsigned char *)reply+bytes_read)[1],
|
|
((unsigned char *)reply+bytes_read)[2],
|
|
((unsigned char *)reply+bytes_read)[3],
|
|
((unsigned char *)reply+bytes_read)[4]);
|
|
bytes_read += result;
|
|
|
|
/* Once we've read 5 bytes, figure out total message length and
|
|
* update bytes_to_read accordingly. */
|
|
if (!typechecked && bytes_read <= 5) {
|
|
switch(reply->address_type) {
|
|
case SOCKS5_ATYPE_IPv4:
|
|
/* 6 fixed bytes, and 4 bytes for v4 address */
|
|
bytes_to_read = 10;
|
|
break;
|
|
case SOCKS5_ATYPE_IPv6:
|
|
/* 6 fixed bytes, and 16 bytes for v6 address */
|
|
bytes_to_read = 22;
|
|
break;
|
|
case SOCKS5_ATYPE_DOMAINNAME:
|
|
/* 6 fixed bytes, 1 byte for strlen,
|
|
and 0-255 bytes for domain name */
|
|
bytes_to_read = 7 + reply->dstdata[0];
|
|
break;
|
|
default:
|
|
Msg1(level, "invalid SOCKS5 reply address type (%d)",
|
|
reply->address_type);
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s",
|
|
sfd->fd, strerror(errno));
|
|
}
|
|
return STAT_RETRYLATER;
|
|
}
|
|
typechecked = true;
|
|
continue;
|
|
}
|
|
|
|
if (bytes_to_read == bytes_read) {
|
|
Debug1("received all %d bytes", bytes_read);
|
|
break;
|
|
}
|
|
|
|
Debug2("received %d of %d bytes, waiting",
|
|
bytes_read, bytes_to_read);
|
|
}
|
|
|
|
if (result <= 0) {
|
|
return STAT_RETRYLATER;
|
|
}
|
|
|
|
return STAT_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
* Sends a request and receives the reply.
|
|
* If command is BIND we receive two replies.
|
|
*/
|
|
static int _xioopen_socks5_request(
|
|
struct single *sfd, const char *target_name, const char *target_port,
|
|
uint8_t socks_command, int level)
|
|
{
|
|
struct socks5_request *req;
|
|
int bytes, result = 0;
|
|
|
|
req = _xioopen_socks5_prepare_request(&bytes, target_name, target_port,
|
|
socks_command, level);
|
|
if (req == NULL) {
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd, strerror(errno));
|
|
}
|
|
|
|
/* Prepare_request could fail due to malloc, but most likely
|
|
the destination is invalid, e.g too long hostname, so NORETRY */
|
|
return STAT_NORETRY;
|
|
}
|
|
|
|
Info4("sending socks5 request version=%d command=%d reserved=%d address_type=%d",
|
|
req->version, req->command, req->reserved, req->address_type);
|
|
|
|
#if WITH_MSGLEVEL <= E_DEBUG
|
|
{
|
|
char *msgbuf;
|
|
if ((msgbuf = Malloc(3 * bytes)) != NULL) {
|
|
xiohexdump((const unsigned char *)req, bytes, msgbuf);
|
|
Debug1("sending socks5 request %s", msgbuf);
|
|
free(msgbuf);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (writefull(sfd->fd, req, bytes, NULL) < 0) {
|
|
Msg4(level, "write(%d, %p, %d): %s",
|
|
sfd->fd, req, bytes, strerror(errno));
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd, strerror(errno));
|
|
}
|
|
free(req);
|
|
return STAT_RETRYLATER;
|
|
}
|
|
free(req);
|
|
req = NULL;
|
|
|
|
struct socks5_reply *reply = Malloc(SOCKS5_MAX_REPLY_SIZE);
|
|
if (reply == NULL) {
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd, strerror(errno));
|
|
}
|
|
|
|
return STAT_RETRYLATER;
|
|
}
|
|
|
|
result = _xioopen_socks5_read_reply(sfd, reply, level);
|
|
if (result != STAT_OK) {
|
|
free(reply);
|
|
return result;
|
|
}
|
|
|
|
/* TODO: maybe output nicer debug, like including address */
|
|
Info3("received SOCKS5 reply version=%d reply=%d address_type=%d",
|
|
reply->version, reply->reply, reply->address_type);
|
|
|
|
if (reply->version != SOCKS5_VERSION) {
|
|
Msg2(level, "SOCKS5 reply version was %d, not the expected %d, peer might not be a SOCKS5 server",
|
|
reply->version, SOCKS5_VERSION);
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd, strerror(errno));
|
|
}
|
|
free(reply);
|
|
return STAT_RETRYLATER;
|
|
}
|
|
|
|
if (reply->reply == SOCKS5_STATUS_SUCCESS &&
|
|
socks_command == SOCKS5_COMMAND_BIND) {
|
|
Notice("listening on remote host, waiting for connection");
|
|
/* TODO: nicer debug output */
|
|
/* For BIND, we read two replies */
|
|
result = _xioopen_socks5_read_reply(sfd, reply, level);
|
|
if (result != STAT_OK) {
|
|
free(reply);
|
|
return result;
|
|
}
|
|
Notice("received connection on remote host");
|
|
/* TODO: maybe output nicer debug, like including address */
|
|
Info3("received second SOCKS5 reply version=%d reply=%d address_type=%d",
|
|
reply->version, reply->reply, reply->address_type);
|
|
}
|
|
|
|
switch (reply->reply) {
|
|
case SOCKS5_STATUS_SUCCESS:
|
|
break;
|
|
default:
|
|
Msg2(level, "SOCKS5 server error %d: %s",
|
|
reply->reply,
|
|
_xioopen_socks5_strerror(reply->reply));
|
|
if (Close(sfd->fd) < 0) {
|
|
Info2("close(%d): %s", sfd->fd, strerror(errno));
|
|
}
|
|
free(reply);
|
|
return STAT_RETRYLATER;
|
|
}
|
|
|
|
free(reply);
|
|
return STAT_OK;
|
|
}
|
|
|
|
/* Same function for all socks5-modes, determined by argv[0] */
|
|
static int xioopen_socks5(
|
|
int argc,
|
|
const char *argv[],
|
|
struct opt *opts,
|
|
int xioflags,
|
|
xiofile_t *xxfd,
|
|
const struct addrdesc *addrdesc)
|
|
{
|
|
int socks_command = addrdesc->arg1;
|
|
bool dofork = false;
|
|
int maxchildren = 0;
|
|
int socktype = SOCK_STREAM;
|
|
int pf = PF_UNSPEC;
|
|
int ipproto = IPPROTO_TCP;
|
|
int level, result;
|
|
struct opt *opts0 = NULL;
|
|
struct single *sfd = &xxfd->stream;
|
|
const char *socks_server, *target_name, *target_port, *socks_port;
|
|
struct addrinfo **bindarr = NULL;
|
|
struct addrinfo **themarr = NULL;
|
|
uint16_t bindport = 0;
|
|
bool needbind = false;
|
|
bool lowport = false;
|
|
|
|
if (argc < 4 || argc > 5) {
|
|
xio_syntax(argv[0], 4, argc-1, addrdesc->syntax);
|
|
return STAT_NORETRY;
|
|
}
|
|
|
|
socks_server = argv[1];
|
|
if (argc == 5) {
|
|
socks_port = argv[2];
|
|
target_name = argv[3];
|
|
target_port = argv[4];
|
|
} else {
|
|
socks_port = NULL;
|
|
target_name = argv[2];
|
|
target_port = argv[3];
|
|
}
|
|
|
|
/* Apply and retrieve some options */
|
|
result = _xioopen_ipapp_init(sfd, xioflags, opts,
|
|
&dofork, &maxchildren,
|
|
&pf, &socktype, &ipproto);
|
|
if (result != STAT_OK)
|
|
return result;
|
|
|
|
if (_xioopen_opt_socksport(opts, (char **)&socks_port) < 0) {
|
|
return STAT_NORETRY;
|
|
}
|
|
/*! possible memory leak */
|
|
|
|
opts0 = opts;
|
|
opts = NULL;
|
|
|
|
Notice4("opening connection to %s:%s vis socks5 server %s:%s",
|
|
target_name, target_port, socks_server, socks_port);
|
|
|
|
do { /* loop over retries (failed connect and socks-request attempts)
|
|
and/or forks */
|
|
int _errno;
|
|
#if WITH_RETRY
|
|
if (sfd->forever || sfd->retry) {
|
|
level = E_NOTICE;
|
|
} else
|
|
#endif
|
|
level = E_WARN;
|
|
|
|
opts = copyopts(opts0, GROUP_ALL);
|
|
|
|
result =
|
|
_xioopen_ipapp_prepare(&opts, opts0, socks_server, socks_port,
|
|
pf, socktype, ipproto,
|
|
sfd->para.socket.ip.ai_flags,
|
|
&themarr, &bindarr, &bindport, &needbind,
|
|
&lowport);
|
|
switch (result) {
|
|
case STAT_OK: break;
|
|
#if WITH_RETRY
|
|
case STAT_RETRYLATER:
|
|
case STAT_RETRYNOW:
|
|
if (sfd->forever || sfd->retry--) {
|
|
if (result == STAT_RETRYLATER)
|
|
Nanosleep(&sfd->intervall, NULL);
|
|
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
|
|
xiofreeaddrinfo(themarr);
|
|
freeopts(opts);
|
|
continue;
|
|
}
|
|
#endif /* WITH_RETRY */
|
|
/* FALLTHROUGH */
|
|
case STAT_NORETRY:
|
|
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
|
|
xiofreeaddrinfo(themarr);
|
|
freeopts(opts);
|
|
freeopts(opts0);
|
|
return result;
|
|
}
|
|
|
|
Notice2("opening connection to socks5 server %s:%s",
|
|
socks_server, socks_port);
|
|
result =
|
|
_xioopen_ipapp_connect(sfd, socks_server, opts, themarr,
|
|
needbind, bindarr, bindport, lowport, level);
|
|
_errno = errno;
|
|
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
|
|
xiofreeaddrinfo(themarr);
|
|
switch (result) {
|
|
case STAT_OK: break;
|
|
#if WITH_RETRY
|
|
case STAT_RETRYLATER:
|
|
case STAT_RETRYNOW:
|
|
if (sfd->forever || sfd->retry--) {
|
|
if (result == STAT_RETRYLATER) {
|
|
Nanosleep(&sfd->intervall, NULL);
|
|
}
|
|
freeopts(opts);
|
|
continue;
|
|
}
|
|
#endif /* WITH_RETRY */
|
|
/* FALLTHROUGH */
|
|
default:
|
|
Error4("%s:%s:%s:...: %s",
|
|
argv[0], socks_server, socks_port,
|
|
_errno?strerror(_errno):"(See above)");
|
|
freeopts(opts0);
|
|
freeopts(opts);
|
|
return result;
|
|
}
|
|
|
|
result = _xioopen_socks5_handshake(sfd, level);
|
|
switch (result) {
|
|
case STAT_OK: break;
|
|
#if WITH_RETRY
|
|
case STAT_RETRYLATER:
|
|
case STAT_RETRYNOW:
|
|
if (sfd->forever || sfd->retry--) {
|
|
if (result == STAT_RETRYLATER) {
|
|
Nanosleep(&sfd->intervall, NULL);
|
|
}
|
|
freeopts(opts);
|
|
continue;
|
|
}
|
|
#endif /* WITH_RETRY */
|
|
/* FALLTHROUGH */
|
|
default:
|
|
Error3("%s:%s:%s: Connection failed", argv[0], socks_server, socks_port);
|
|
freeopts(opts0);
|
|
freeopts(opts);
|
|
return result;
|
|
}
|
|
|
|
result = _xioopen_socks5_request(sfd, target_name, target_port, socks_command, level);
|
|
switch (result) {
|
|
case STAT_OK:
|
|
break;
|
|
#if WITH_RETRY
|
|
case STAT_RETRYLATER:
|
|
case STAT_RETRYNOW:
|
|
if ( sfd->forever || sfd->retry-- ) {
|
|
if (result == STAT_RETRYLATER)
|
|
Nanosleep(&sfd->intervall, NULL);
|
|
freeopts(opts);
|
|
continue;
|
|
}
|
|
#endif /* WITH_RETRY */
|
|
/* FALLTHROUGH */
|
|
default:
|
|
freeopts(opts);
|
|
freeopts(opts0);
|
|
return result;
|
|
}
|
|
|
|
if (dofork) {
|
|
xiosetchilddied();
|
|
}
|
|
|
|
#if WITH_RETRY
|
|
if (dofork) {
|
|
pid_t pid;
|
|
int level = E_ERROR;
|
|
if (sfd->forever || sfd->retry) {
|
|
level = E_WARN;
|
|
}
|
|
while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
|
|
if (sfd->forever || sfd->retry) {
|
|
if (sfd->retry > 0)
|
|
--sfd->retry;
|
|
Nanosleep(&sfd->intervall, NULL);
|
|
freeopts(opts);
|
|
continue;
|
|
}
|
|
freeopts(opts);
|
|
freeopts(opts0);
|
|
return STAT_RETRYLATER;
|
|
}
|
|
|
|
if ( pid == 0 ) {
|
|
sfd->forever = false;
|
|
sfd->retry = 0;
|
|
break;
|
|
}
|
|
|
|
Close(sfd->fd);
|
|
Nanosleep(&sfd->intervall, NULL);
|
|
while (maxchildren > 0 && num_child >= maxchildren) {
|
|
Info1("all %d allowed children are active, waiting", maxchildren);
|
|
Nanosleep(&sfd->intervall, NULL);
|
|
}
|
|
freeopts(opts);
|
|
continue;
|
|
} else
|
|
#endif /* WITH_RETRY */
|
|
{
|
|
break;
|
|
}
|
|
} while (true);
|
|
|
|
Notice4("successfully connected to %s:%s via socks5 server %s:%s",
|
|
target_name, target_port, socks_server, socks_port);
|
|
|
|
result = _xio_openlate(sfd, opts);
|
|
freeopts(opts);
|
|
freeopts(opts0);
|
|
return STAT_OK;
|
|
}
|
|
|
|
#endif /* WITH_SOCKS5 */
|