1
0
Fork 0
mirror of https://repo.or.cz/socat.git synced 2025-06-07 10:36:52 +00:00

Reworked IPAPP clients

This commit is contained in:
Gerhard 2025-02-10 12:48:04 +01:00
parent 63f67101f4
commit 7b26406d96
16 changed files with 1660 additions and 649 deletions

18
CHANGES
View file

@ -60,10 +60,24 @@ Corrections:
now handled properly. now handled properly.
Test: UNIX_L_BIND Test: UNIX_L_BIND
Removed unused bytes variable from gettimestamp(), corrected #elsif and Removed unused bytes variable from gettimestamp(), corrected #elsif,
socks4 record length. and socks4 record length.
Thanks to clang-18 and gcc-13. Thanks to clang-18 and gcc-13.
Address TCP-CONNECT, when target address resolves to both IPv4 and
IPv6, now tries to take into account bind address for protocol
selection.
Reworked and harmonized ipapp client addresses.
Tests: TCP_CONNECT_RETRY SCTP_CONNECT_RETRY DCCP_CONNECT_RETRY
OPENSSL_CONNECT_RETRY SOCKS4_RETRY SOCKS5_CONNECT_RETRY
PROXY_CONNECT_RETRY
Socks and proxy clients now also support option max-children.
Tests: TCP_CONNECT_MAXCHILDREN SCTP_CONNECT_MAXCHILDREN
DCCP_CONNECT_MAXCHILDREN OPENSSL_CONNECT_MAXCHILDREN
SOCKS4_MAXCHILDREN SOCKS5_CONNECT_MAXCHILDREN PROXY_CONNECT_MAXCHILDREN
Features: Features:
POSIXMQ-RECV now takes option o-nonblock; this, in combination with -T, POSIXMQ-RECV now takes option o-nonblock; this, in combination with -T,
makes it possible to terminate Socat in case the queue is empty. makes it possible to terminate Socat in case the queue is empty.

View file

@ -9,6 +9,7 @@
# it is required for test.sh # it is required for test.sh
# for TCP, use this script as: # for TCP, use this script as:
# socat tcp-l:1080,reuseaddr,crlf system:"socks4echo.sh" # socat tcp-l:1080,reuseaddr,crlf system:"socks4echo.sh"
# Then connect with a socks4 request for 32.98.76.54:32109 and user nobody
# older bash and ksh do not have -n option to read command; we try dd then # older bash and ksh do not have -n option to read command; we try dd then
#if echo a |read -n 1 null >/dev/null 2>&1; then #if echo a |read -n 1 null >/dev/null 2>&1; then

926
test.sh

File diff suppressed because it is too large Load diff

View file

@ -161,6 +161,79 @@ int Res_init(void) {
#endif /* HAVE_RESOLV_H */ #endif /* HAVE_RESOLV_H */
/* Looks for a bind option and, if found, passes it to resolver;
for IP (v4, v6) and raw (PF_UNSPEC);
returns list of addrinfo results;
returns STAT_OK if option exists and could be resolved,
STAT_NORETRY if option exists but had error,
or STAT_NOACTION if it does not exist */
int retropt_bind_ip(
struct opt *opts,
int af,
int socktype,
int ipproto,
struct addrinfo ***bindlist,
int feats, /* TCP etc: 1..address allowed,
3..address and port allowed
*/
const int ai_flags[2])
{
const char portsep[] = ":";
const char *ends[] = { portsep, NULL };
const char *nests[] = { "[", "]", NULL };
bool portallowed;
char *bindname, *bindp;
char hostname[512], *hostp = hostname, *portp = NULL;
size_t hostlen = sizeof(hostname)-1;
int parsres;
int ai_flags2[2];
int result;
if (retropt_string(opts, OPT_BIND, &bindname) < 0) {
return STAT_NOACTION;
}
bindp = bindname;
portallowed = (feats>=2);
parsres =
nestlex((const char **)&bindp, &hostp, &hostlen, ends, NULL, NULL, nests,
true, false, false);
if (parsres < 0) {
Error1("option too long: \"%s\"", bindp);
return STAT_NORETRY;
} else if (parsres > 0) {
Error1("syntax error in \"%s\"", bindp);
return STAT_NORETRY;
}
*hostp++ = '\0';
if (!strncmp(bindp, portsep, strlen(portsep))) {
if (!portallowed) {
Error("port specification not allowed in this bind option");
return STAT_NORETRY;
} else {
portp = bindp + strlen(portsep);
}
}
/* Set AI_PASSIVE, except when it is explicitely disabled */
ai_flags2[0] = ai_flags[0];
ai_flags2[1] = ai_flags[1];
if (!(ai_flags2[1] & AI_PASSIVE))
ai_flags2[0] |= AI_PASSIVE;
if ((result =
xiogetaddrinfo(hostname[0]!='\0'?hostname:NULL, portp,
af, socktype, ipproto,
bindlist, ai_flags2))
!= STAT_OK) {
Error2("error resolving bind option \"%s\" with af=%d", bindname, af);
return STAT_NORETRY;
}
return STAT_OK;
}
#if WITH_DEVTESTS #if WITH_DEVTESTS
/* Have a couple of hard coded sockaddr records, to be copied and adapted when /* Have a couple of hard coded sockaddr records, to be copied and adapted when
@ -516,11 +589,11 @@ int _xiogetaddrinfo(const char *node, const char *service,
continue; continue;
} }
if ((error_num = Getaddrinfo(node, service, &hints, res)) != 0) { if ((error_num = Getaddrinfo(node, service, &hints, res)) != 0) {
Warn7("getaddrinfo(\"%s\", \"%s\", {0x%02x,%d,%d,%d}, {}): %d", Warn7("getaddrinfo(\"%s\", \"%s\", {0x%02x,%d,%d,%d}, {}): %s",
node?node:"NULL", service?service:"NULL", node?node:"NULL", service?service:"NULL",
hints.ai_flags, hints.ai_family, hints.ai_flags, hints.ai_family,
hints.ai_socktype, hints.ai_protocol, hints.ai_socktype, hints.ai_protocol,
error_num); gai_strerror(error_num));
if (numnode) if (numnode)
free(numnode); free(numnode);
@ -760,6 +833,9 @@ void xiofreeaddrinfo(struct addrinfo **ai_sorted) {
int ain; int ain;
struct addrinfo *res; struct addrinfo *res;
if (ai_sorted == NULL)
return;
/* Find the original *res from getaddrinfo past NULL */ /* Find the original *res from getaddrinfo past NULL */
ain = 0; ain = 0;
while (ai_sorted[ain] != NULL) while (ai_sorted[ain] != NULL)

View file

@ -49,6 +49,7 @@ extern const struct optdesc opt_res_nsaddr;
extern int xioinit_ip(int *pf, char ipv); extern int xioinit_ip(int *pf, char ipv);
extern int retropt_bind_ip(struct opt *opts, int af, int socktype, int ipproto, struct addrinfo ***bindlist, int feats, const int ai_flags[2]);
extern int xiogetaddrinfo(const char *node, const char *service, int family, int socktype, int protocol, struct addrinfo ***ai_sorted, const int ai_flags[2]); extern int xiogetaddrinfo(const char *node, const char *service, int family, int socktype, int protocol, struct addrinfo ***ai_sorted, const int ai_flags[2]);
extern void xiofreeaddrinfo(struct addrinfo **ai_sorted); extern void xiofreeaddrinfo(struct addrinfo **ai_sorted);
extern int _xio_sort_ip_addresses(struct addrinfo *themlist, struct addrinfo **ai_sorted); extern int _xio_sort_ip_addresses(struct addrinfo *themlist, struct addrinfo **ai_sorted);

View file

@ -19,6 +19,7 @@ const struct optdesc opt_sourceport = { "sourceport", "sp", OPT_SOURCEPORT
/*const struct optdesc opt_port = { "port", NULL, OPT_PORT, GROUP_IPAPP, PH_BIND, TYPE_USHORT, OFUNC_SPEC };*/ /*const struct optdesc opt_port = { "port", NULL, OPT_PORT, GROUP_IPAPP, PH_BIND, TYPE_USHORT, OFUNC_SPEC };*/
const struct optdesc opt_lowport = { "lowport", NULL, OPT_LOWPORT, GROUP_IPAPP, PH_LATE, TYPE_BOOL, OFUNC_SPEC }; const struct optdesc opt_lowport = { "lowport", NULL, OPT_LOWPORT, GROUP_IPAPP, PH_LATE, TYPE_BOOL, OFUNC_SPEC };
#if _WITH_IP4 || _WITH_IP6 #if _WITH_IP4 || _WITH_IP6
/* we expect the form "host:port" */ /* we expect the form "host:port" */
int xioopen_ipapp_connect( int xioopen_ipapp_connect(
@ -31,20 +32,18 @@ int xioopen_ipapp_connect(
{ {
struct single *sfd = &xxfd->stream; struct single *sfd = &xxfd->stream;
struct opt *opts0 = NULL; struct opt *opts0 = NULL;
const char *hostname = argv[1], *portname = argv[2];
int pf = addrdesc->arg3;
int socktype = addrdesc->arg1; int socktype = addrdesc->arg1;
int ipproto = addrdesc->arg2; int ipproto = addrdesc->arg2;
int pf = addrdesc->arg3;
const char *hostname = argv[1], *portname = argv[2];
bool dofork = false; bool dofork = false;
int maxchildren = 0; int maxchildren = 0;
union sockaddr_union us_sa, *us = &us_sa; struct addrinfo **bindarr = NULL;
socklen_t uslen = sizeof(us_sa); struct addrinfo **themarr = NULL;
struct addrinfo **themarr, *themp; uint16_t bindport = 0;
char infobuff[256];
bool needbind = false; bool needbind = false;
bool lowport = false; bool lowport = false;
int level; int level = E_ERROR;
int i;
int result; int result;
if (argc != 3) { if (argc != 3) {
@ -52,97 +51,84 @@ int xioopen_ipapp_connect(
return STAT_NORETRY; return STAT_NORETRY;
} }
if (sfd->howtoend == END_UNSPEC) /* Apply and retrieve some options */
sfd->howtoend = END_SHUTDOWN; result = _xioopen_ipapp_init(sfd, xioflags, opts,
&dofork, &maxchildren,
&pf, &socktype, &ipproto);
if (result != STAT_OK)
return result;
if (applyopts_single(sfd, opts, PH_INIT) < 0) opts0 = opts; /* save remaining options for each loop */
return -1; opts = NULL;
applyopts(sfd, -1, opts, PH_INIT);
retropt_bool(opts, OPT_FORK, &dofork); Notice2("opening connection to %s:%s", hostname, portname);
if (dofork) {
if (!(xioflags & XIO_MAYFORK)) {
Error("option fork not allowed here");
return STAT_NORETRY;
}
sfd->flags |= XIO_DOESFORK;
}
retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren); do { /* loop over retries and/or forks */
int _errno;
if (! dofork && maxchildren) {
Error("option max-children not allowed without option fork");
return STAT_NORETRY;
}
if (_xioopen_ipapp_prepare(opts, &opts0, hostname, portname, &pf, ipproto,
sfd->para.socket.ip.ai_flags,
&themarr, us, &uslen, &needbind, &lowport,
socktype) != STAT_OK) {
return STAT_NORETRY;
}
if (dofork) {
xiosetchilddied(); /* set SIGCHLD handler */
}
if (xioparms.logopt == 'm') {
Info("starting connect loop, switching to syslog");
diag_set('y', xioparms.syslogfac); xioparms.logopt = 'y';
} else {
Info("starting connect loop");
}
do { /* loop over retries, and forks */
/* Loop over themarr (which had been "ai_sorted") */
result = STAT_RETRYLATER;
i = 0;
themp = themarr[i++];
while (themp != NULL) {
Notice1("opening connection to %s",
sockaddr_info(themp->ai_addr, themp->ai_addrlen,
infobuff, sizeof(infobuff)));
#if WITH_RETRY #if WITH_RETRY
if (sfd->forever || sfd->retry) { if (sfd->forever || sfd->retry) {
level = E_INFO; level = E_NOTICE;
} else if (themarr[i] != NULL) { } else
level = E_WARN;
} else
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
level = E_ERROR; level = E_WARN;
result = opts = copyopts(opts0, GROUP_ALL);
_xioopen_connect(sfd,
needbind?us:NULL, uslen, result =
themp->ai_addr, themp->ai_addrlen, _xioopen_ipapp_prepare(&opts, opts0, hostname, portname,
opts, pf?pf:themp->ai_family, socktype, ipproto, pf, socktype, ipproto,
lowport, level); sfd->para.socket.ip.ai_flags,
if (result == STAT_OK) &themarr, &bindarr, &bindport, &needbind, &lowport);
break;
themp = themarr[i++];
if (themp == NULL) {
result = STAT_RETRYLATER;
}
}
switch (result) { switch (result) {
case STAT_OK: break; case STAT_OK: break;
#if WITH_RETRY #if WITH_RETRY
case STAT_RETRYLATER: case STAT_RETRYLATER:
case STAT_RETRYNOW: case STAT_RETRYNOW:
if (sfd->forever || sfd->retry) { if (sfd->forever || sfd->retry--) {
--sfd->retry; if (result == STAT_RETRYLATER)
if (result == STAT_RETRYLATER) {
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
} if (bindarr != NULL) xiofreeaddrinfo(bindarr);
dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL); xiofreeaddrinfo(themarr);
freeopts(opts);
continue; continue;
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: /* FALLTHROUGH */
case STAT_NORETRY:
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr); xiofreeaddrinfo(themarr);
free(opts0);free(opts); freeopts(opts);
freeopts(opts0);
return result;
}
result =
_xioopen_ipapp_connect(sfd, hostname, 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], argv[1], argv[2],
_errno?strerror(_errno):"(See above)");
freeopts(opts);
freeopts(opts0);
return result; return result;
} }
@ -155,16 +141,18 @@ int xioopen_ipapp_connect(
so Notice is too weak */ so Notice is too weak */
} }
while ((pid = xio_fork(false, level, sfd->shutup)) < 0) { while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
if (sfd->forever || --sfd->retry) { if (sfd->forever || sfd->retry--) {
Nanosleep(&sfd->intervall, NULL); continue; Nanosleep(&sfd->intervall, NULL);
continue;
} }
xiofreeaddrinfo(themarr); freeopts(opts);
free(opts0); freeopts(opts0);
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
if (pid == 0) { /* child process */ if (pid == 0) { /* child process */
sfd->forever = false; sfd->retry = 0; sfd->forever = false;
sfd->retry = 0;
break; break;
} }
@ -176,109 +164,266 @@ int xioopen_ipapp_connect(
Info1("all %d allowed children are active, waiting", maxchildren); Info1("all %d allowed children are active, waiting", maxchildren);
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
} }
dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL); freeopts(opts);
continue; /* with next socket() bind() connect() */ continue; /* with next socket() bind() connect() */
} else } else
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
{ {
break; break;
} }
} while (true); } while (true); /* end of loop over retries and/or forks */
/* only "active" process breaks (master without fork, or child) */ /* only "active" process breaks (master without fork, or child) */
xiofreeaddrinfo(themarr);
if ((result = _xio_openlate(sfd, opts)) < 0) { Notice2("successfully connected to %s:%s", hostname, portname);
free(opts0);free(opts);
return result; result = _xio_openlate(sfd, opts);
} freeopts(opts);
free(opts0); free(opts); freeopts(opts0);
return 0; return result;
} }
/* returns STAT_OK on success or some other value on failure /* This function performs static initializations for addresses like TCP-CONNECT
before start of the outer loop:
it retrieves some options
returns STAT_OK on success or some other value on failure;
applies and consumes the following options:
PH_INIT, OPT_FORK, OPT_MAX_CHILDREN, OPT_PROTOCOL_FAMILY, OPT_SO_TYPE,
OPT_SO_PROTOTYPE
*/
int _xioopen_ipapp_init(
struct single *sfd,
int xioflags,
struct opt *opts,
bool *dofork,
int *maxchildren,
int *pf,
int *socktype,
int *ipproto)
{
if (sfd->howtoend == END_UNSPEC)
sfd->howtoend = END_SHUTDOWN;
if (applyopts_single(sfd, opts, PH_INIT) < 0)
return -1;
if (applyopts(sfd, -1, opts, PH_INIT) < 0)
return -1;
retropt_bool(opts, OPT_FORK, dofork);
if (dofork) {
if (!(xioflags & XIO_MAYFORK)) {
Error1("%s: option fork not allowed here", sfd->addr->defname);
return STAT_NORETRY;
}
sfd->flags |= XIO_DOESFORK;
}
retropt_int(opts, OPT_MAX_CHILDREN, maxchildren);
if (! dofork && maxchildren) {
Error1("%s: option max-children not allowed without option fork", sfd->addr->defname);
return STAT_NORETRY;
}
retropt_socket_pf(opts, pf);
retropt_int(opts, OPT_SO_TYPE, socktype);
retropt_int(opts, OPT_SO_PROTOTYPE, ipproto);
if (dofork) {
xiosetchilddied(); /* set SIGCHLD handler */
}
if (xioparms.logopt == 'm') {
Info("starting connect loop, switching to syslog");
diag_set('y', xioparms.syslogfac);
xioparms.logopt = 'y';
} else {
Info("starting connect loop");
}
return STAT_OK;
}
/* This function performs preparations for addresses like TCP-CONNECT
at the beginning of the outer (retry/fork) loop:
it evaluates some options and performs name resolution of both server
(target, "them") address and bind ("us") address.
It is intended to be invoked before the connect loop starts;
returns STAT_OK on success or some other value on failure;
applies and consumes the following options: applies and consumes the following options:
PH_EARLY PH_EARLY
OPT_PROTOCOL_FAMILY, OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT
*/ returns STAT_OK, STAT_RETRYLATER, or STAT_NORETRY (+errno)
int */
_xioopen_ipapp_prepare( int _xioopen_ipapp_prepare(
struct opt *opts, struct opt **opts,
struct opt **opts0, struct opt *opts0,
const char *hostname, const char *hostname,
const char *portname, const char *portname,
int *pf, int pf,
int protocol, int socktype,
const int ai_flags[2], int protocol,
struct addrinfo ***themarr, const int ai_flags[2],
union sockaddr_union *us, struct addrinfo ***themarr, /* always from getaddrinfo(); xiofreeaddrinfo()! */
socklen_t *uslen, struct addrinfo ***bindarr, /* on bind from getaddrinfo(); xiofreeaddrinfo()! */
bool *needbind, uint16_t *bindport, /* for bind without address */
bool *lowport, bool *needbind,
int socktype) { bool *lowport)
{
uint16_t port; uint16_t port;
int rc; int rc;
retropt_socket_pf(opts, pf); *opts = copyopts(opts0, GROUP_ALL);
if (hostname != NULL || portname != NULL) { if (hostname != NULL || portname != NULL) {
rc = xiogetaddrinfo(hostname, portname, *pf, socktype, protocol, rc = xiogetaddrinfo(hostname, portname, pf, socktype, protocol,
themarr, ai_flags); themarr, ai_flags);
if (rc == EAI_AGAIN) { if (rc == EAI_AGAIN) {
Warn4("_xioopen_ipapp_prepare(node=\"%s\", service=\"%s\", pf=%d, ...): %s", Warn4("_xioopen_ipapp_prepare(node=\"%s\", service=\"%s\", pf=%d, ...): %s",
hostname?hostname:"NULL", portname?portname:"NULL", hostname?hostname:"NULL", portname?portname:"NULL",
*pf, gai_strerror(rc)); pf, gai_strerror(rc));
errno = EAGAIN;
return STAT_RETRYLATER; return STAT_RETRYLATER;
} else if (rc != 0) { } else if (rc != 0) {
Error4("_xioopen_ipapp_prepare(node=\"%s\", service=\"%s\", pf=%d, ...): %s", Error4("_xioopen_ipapp_prepare(node=\"%s\", service=\"%s\", pf=%d, ...): %s",
hostname?hostname:"NULL", portname?portname:"NULL", hostname?hostname:"NULL", portname?portname:"NULL",
*pf, (rc == EAI_SYSTEM)?strerror(errno):gai_strerror(rc)); pf, (rc == EAI_SYSTEM)?strerror(errno):gai_strerror(rc));
errno = 0; /* unspecified */
return STAT_NORETRY; /*! STAT_RETRYLATER? */ return STAT_NORETRY; /*! STAT_RETRYLATER? */
} }
} }
applyopts(NULL, -1, opts, PH_EARLY); applyopts(NULL, -1, *opts, PH_EARLY);
/* 3 means: IP address AND port accepted */ /* 3 means: IP address AND port accepted */
if (retropt_bind(opts, (*pf!=PF_UNSPEC)?*pf:(**themarr)->ai_family, if (retropt_bind_ip(*opts, pf, socktype, protocol, bindarr, 3, ai_flags)
socktype, protocol, (struct sockaddr *)us, uslen, 3,
ai_flags)
!= STAT_NOACTION) { != STAT_NOACTION) {
*needbind = true; *needbind = true;
} else {
switch ((*pf!=PF_UNSPEC)?*pf:(**themarr)->ai_family) {
#if WITH_IP4
case PF_INET: socket_in_init(&us->ip4); *uslen = sizeof(us->ip4); break;
#endif /* WITH_IP4 */
#if WITH_IP6
case PF_INET6: socket_in6_init(&us->ip6); *uslen = sizeof(us->ip6); break;
#endif /* WITH_IP6 */
default: Error("unsupported protocol family");
}
} }
if (retropt_2bytes(*opts, OPT_SOURCEPORT, &port) >= 0) {
if (retropt_2bytes(opts, OPT_SOURCEPORT, &port) >= 0) { if (*bindarr) {
switch ((*pf!=PF_UNSPEC)?*pf:(**themarr)->ai_family) { struct addrinfo **bindp;
bindp = *bindarr;
switch ((*bindp)->ai_family) {
#if WITH_IP4 #if WITH_IP4
case PF_INET: us->ip4.sin_port = htons(port); break; case PF_INET: ((struct sockaddr_in *)(*bindp)->ai_addr)->sin_port = htons(port); break;
#endif /* WITH_IP4 */ #endif /* WITH_IP4 */
#if WITH_IP6 #if WITH_IP6
case PF_INET6: us->ip6.sin6_port = htons(port); break; case PF_INET6: ((struct sockaddr_in6 *)(*bindp)->ai_addr)->sin6_port = htons(port); break;
#endif /* WITH_IP6 */ #endif /* WITH_IP6 */
default: Error("unsupported protocol family"); default:
Error("unsupported protocol family");
errno = EPROTONOSUPPORT;
return STAT_NORETRY;
}
} else {
*bindport = port;
} }
*needbind = true; *needbind = true;
} }
retropt_bool(opts, OPT_LOWPORT, lowport); retropt_bool(*opts, OPT_LOWPORT, lowport);
*opts0 = copyopts(opts, GROUP_ALL);
return STAT_OK; return STAT_OK;
} }
#endif /* _WITH_IP4 || _WITH_IP6 */ #endif /* _WITH_IP4 || _WITH_IP6 */
/* Tries to connect to the addresses in themarr, for each one it tries to bind
to the addresses in bindarr.
Ends on success or when all attempts failed.
Returns STAT_OK on success, or STAT_RETRYLATER (+errno) on failure. */
int _xioopen_ipapp_connect(struct single *sfd,
const char *hostname,
struct opt *opts,
struct addrinfo **themarr,
bool needbind,
struct addrinfo **bindarr,
uint16_t bindport,
bool lowport,
int level)
{
struct addrinfo **themp;
struct addrinfo **bindp;
union sockaddr_union bindaddr = {0};
union sockaddr_union *bindaddrp = NULL;
socklen_t bindlen = 0;
char infobuff[256];
int _errno;
int result = STAT_OK;
--level;
/* Loop over server addresses (themarr) */
themp = themarr;
while (*themp != NULL) {
Notice1("opening connection to %s",
sockaddr_info((*themp)->ai_addr, (*themp)->ai_addrlen,
infobuff, sizeof(infobuff)));
if (*(themp+1) == NULL) {
++level; /* last attempt */
}
/* Loop over array (list) of bind addresses */
if (needbind && bindarr != NULL) {
/* Bind by hostname, use resolvers results list */
bindp = bindarr;
while (*bindp != NULL) {
if ((*bindp)->ai_family == (*themp)->ai_family)
break;
++bindp;
}
if (*bindp == NULL) {
Warn3("%s: No bind address with matching address family (%d) of %s available",
sfd->addr->defname, (*themp)->ai_family, hostname);
++themp;
if ((*themp) == NULL) {
result = STAT_RETRYLATER;
}
_errno = ENOPROTOOPT;
continue;
}
bindaddrp = (union sockaddr_union *)(*bindp)->ai_addr;
bindlen = (*bindp)->ai_addrlen;
} else if (needbind && bindport) {
/* Bind by sourceport option */
switch ((*themp)->ai_family) {
case PF_INET:
bindaddr.ip4.sin_family = (*themp)->ai_family;
bindaddr.ip4.sin_port = htons(bindport);
bindaddrp = &bindaddr;
bindlen = sizeof(bindaddr.ip4);
break;
case PF_INET6:
bindaddr.ip6.sin6_family = (*themp)->ai_family;
bindaddr.ip6.sin6_port = htons(bindport);
bindaddrp = &bindaddr;
bindlen = sizeof(bindaddr.ip6);
break;
}
}
result =
_xioopen_connect(sfd,
bindaddrp, bindlen,
(*themp)->ai_addr, (*themp)->ai_addrlen,
opts, /*pf?pf:*/(*themp)->ai_family, (*themp)->ai_socktype, (*themp)->ai_protocol,
lowport, level);
if (result == STAT_OK)
break;
_errno = errno;
++themp;
if (*themp == NULL)
result = STAT_RETRYLATER;
} /* end of loop over target addresses */
if (result != STAT_OK)
errno = _errno;
return result;
}
#if WITH_TCP && WITH_LISTEN #if WITH_TCP && WITH_LISTEN
/* /*
applies and consumes the following options: applies and consumes the following options:

View file

@ -15,11 +15,9 @@ extern const struct optdesc opt_sourceport;
extern const struct optdesc opt_lowport; extern const struct optdesc opt_lowport;
extern int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, const struct addrdesc *addrdesc); extern int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, const struct addrdesc *addrdesc);
extern int _xioopen_ipapp_prepare(struct opt *opts, struct opt **opts0, const char *hostname, const char *portname, int *pf, int protocol, const int ai_flags[2], struct addrinfo ***themlist, union sockaddr_union *us, socklen_t *uslen, bool *needbind, bool *lowport, int socktype); extern int _xioopen_ipapp_init(struct single *sfd, int xioflags, struct opt *opts, bool *dofork, int *maxchildren, int *pf, int *socktype, int *protocol);
extern int _xioopen_ip4app_connect(const char *hostname, const char *portname, extern int _xioopen_ipapp_prepare(struct opt **opts, struct opt *opts0, const char *hostname, const char *portname, int pf, int socktype, int protocol, const int ai_flags[2], struct addrinfo ***themarr, struct addrinfo ***bindarr, uint16_t *bindport, bool *needbind, bool *lowport);
struct single *xfd, extern int _xioopen_ipapp_connect(struct single *sfd, const char *hostname, struct opt *opts, struct addrinfo **themarr, bool needbind, struct addrinfo **bindarr, uint16_t bindport, bool lowport, int level);
int socktype, int ipproto, void *protname,
struct opt *opts);
extern int xioopen_ipapp_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, const struct addrdesc *addrdesc); extern int xioopen_ipapp_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, const struct addrdesc *addrdesc);
extern int _xioopen_ipapp_listen_prepare(struct opt *opts, struct opt **opts0, const char *portname, int *pf, int ipproto, const int ai_flags[2], union sockaddr_union *us, socklen_t *uslen, int socktype); extern int _xioopen_ipapp_listen_prepare(struct opt *opts, struct opt **opts0, const char *portname, int *pf, int ipproto, const int ai_flags[2], union sockaddr_union *us, socklen_t *uslen, int socktype);

View file

@ -238,13 +238,13 @@ static int xioopen_openssl_connect(
int socktype = SOCK_STREAM; int socktype = SOCK_STREAM;
int ipproto = IPPROTO_TCP; int ipproto = IPPROTO_TCP;
bool dofork = false; bool dofork = false;
union sockaddr_union us_sa, *us = &us_sa; int maxchildren = 0;
socklen_t uslen = sizeof(us_sa); struct addrinfo **bindarr = NULL;
struct addrinfo **themarr, *themp; struct addrinfo **themarr = NULL;
uint16_t bindport = 0;
bool needbind = false; bool needbind = false;
bool lowport = false; bool lowport = false;
int level = E_ERROR; int level = E_ERROR;
int i;
SSL_CTX* ctx; SSL_CTX* ctx;
bool opt_ver = true; /* verify peer certificate */ bool opt_ver = true; /* verify peer certificate */
char *opt_cert = NULL; /* file name of client certificate */ char *opt_cert = NULL; /* file name of client certificate */
@ -254,7 +254,7 @@ static int xioopen_openssl_connect(
int result; int result;
if (!(xioflags & XIO_MAYCONVERT)) { if (!(xioflags & XIO_MAYCONVERT)) {
Error("address with data processing not allowed here"); Error1("%s: address with data processing not allowed here", argv[0]);
return STAT_NORETRY; return STAT_NORETRY;
} }
sfd->flags |= XIO_DOESCONVERT; sfd->flags |= XIO_DOESCONVERT;
@ -272,14 +272,6 @@ static int xioopen_openssl_connect(
return STAT_NORETRY; return STAT_NORETRY;
} }
if (sfd->howtoend == END_UNSPEC)
sfd->howtoend = END_SHUTDOWN;
if (applyopts_single(sfd, opts, PH_INIT) < 0)
return -1;
applyopts(sfd, -1, opts, PH_INIT);
retropt_bool(opts, OPT_FORK, &dofork);
retropt_string(opts, OPT_OPENSSL_CERTIFICATE, &opt_cert); retropt_string(opts, OPT_OPENSSL_CERTIFICATE, &opt_cert);
retropt_string(opts, OPT_OPENSSL_COMMONNAME, (char **)&opt_commonname); retropt_string(opts, OPT_OPENSSL_COMMONNAME, (char **)&opt_commonname);
#if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name) #if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name)
@ -315,73 +307,83 @@ static int xioopen_openssl_connect(
socktype = SOCK_DGRAM; socktype = SOCK_DGRAM;
ipproto = IPPROTO_UDP; ipproto = IPPROTO_UDP;
} }
retropt_int(opts, OPT_SO_TYPE, &socktype);
retropt_int(opts, OPT_SO_PROTOTYPE, &ipproto);
result = /* Apply and retrieve some options */
_xioopen_ipapp_prepare(opts, &opts0, hostname, portname, &pf, ipproto, result = _xioopen_ipapp_init(sfd, xioflags, opts,
sfd->para.socket.ip.ai_flags, &dofork, &maxchildren,
&themarr, us, &uslen, &pf, &socktype, &ipproto);
&needbind, &lowport, socktype); if (result != STAT_OK)
if (result != STAT_OK) return STAT_NORETRY; return result;
if (xioparms.logopt == 'm') { opts0 = opts; /* save remaining options for each loop */
Info("starting connect loop, switching to syslog"); opts = NULL;
diag_set('y', xioparms.syslogfac); xioparms.logopt = 'y';
} else {
Info("starting connect loop");
}
do { /* loop over failed connect and SSL handshake attempts */ Notice2("opening OpenSSL connection to %s:%s", hostname, portname);
/* Loop over themarr (which had been "ai_sorted") */ do { /* loop over retries (failed connect and SSL handshake attempts) and/or forks */
i = 0; int _errno;
themp = themarr[i++];
while (themp != NULL) {
#if WITH_RETRY #if WITH_RETRY
if (sfd->forever || sfd->retry || themarr[i] != NULL) { if (sfd->forever || sfd->retry) {
level = E_INFO; level = E_NOTICE;
} else } else
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
level = E_ERROR; level = E_WARN;
/* This cannot fork because we retrieved fork option above */ opts = copyopts(opts0, GROUP_ALL);
result =
_xioopen_connect(sfd, result =
needbind?us:NULL, uslen, _xioopen_ipapp_prepare(&opts, opts0, hostname, portname,
themp->ai_addr, themp->ai_addrlen, pf, socktype, ipproto,
opts, pf?pf:themp->ai_addr->sa_family, socktype, ipproto, lowport, level); sfd->para.socket.ip.ai_flags,
if (result == STAT_OK) &themarr, &bindarr, &bindport, &needbind, &lowport);
break;
themp = themarr[i++];
if (themp == NULL) {
result = STAT_RETRYLATER;
}
}
switch (result) { switch (result) {
case STAT_OK: break; case STAT_OK: break;
#if WITH_RETRY #if WITH_RETRY
case STAT_RETRYLATER: case STAT_RETRYLATER:
case STAT_RETRYNOW: case STAT_RETRYNOW:
if (sfd->forever || sfd->retry) { if (sfd->forever || sfd->retry--) {
dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); if (result == STAT_RETRYLATER)
Nanosleep(&sfd->intervall, NULL);
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr);
freeopts(opts);
continue;
}
#endif /* WITH_RETRY */
case STAT_NORETRY:
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr);
freeopts(opts);
freeopts(opts0);
return result;
}
Notice2("opening connection to server %s:%s", hostname, portname);
result =
_xioopen_ipapp_connect(sfd, hostname, 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) { if (result == STAT_RETRYLATER) {
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
} }
--sfd->retry; freeopts(opts);
continue; continue;
} }
xiofreeaddrinfo(themarr);
return STAT_NORETRY;
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: default:
xiofreeaddrinfo(themarr); Error4("%s:%s:%s: %s", argv[0], hostname, portname,
return result; _errno?strerror(_errno):"(See above)");
} freeopts(opts0);
/*! isn't this too early? */ freeopts(opts);
if ((result = _xio_openlate(sfd, opts)) < 0) {
xiofreeaddrinfo(themarr);
return result; return result;
} }
@ -392,19 +394,19 @@ static int xioopen_openssl_connect(
#if WITH_RETRY #if WITH_RETRY
case STAT_RETRYLATER: case STAT_RETRYLATER:
case STAT_RETRYNOW: case STAT_RETRYNOW:
if (sfd->forever || sfd->retry) { if (sfd->forever || sfd->retry--) {
Close(sfd->fd);
dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
if (result == STAT_RETRYLATER) { if (result == STAT_RETRYLATER) {
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
} }
--sfd->retry; freeopts(opts);
Close(sfd->fd);
continue; continue;
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: default:
xiofreeaddrinfo(themarr); freeopts(opts);
return STAT_NORETRY; freeopts(opts0);
return result;
} }
if (dofork) { if (dofork) {
@ -419,15 +421,19 @@ static int xioopen_openssl_connect(
level = E_WARN; level = E_WARN;
} }
while ((pid = xio_fork(false, level, sfd->shutup)) < 0) { while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
if (sfd->forever || --sfd->retry) { if (sfd->forever || sfd->retry--) {
Nanosleep(&sfd->intervall, NULL); continue; Nanosleep(&sfd->intervall, NULL);
freeopts(opts);
continue;
} }
xiofreeaddrinfo(themarr); freeopts(opts);
freeopts(opts0);
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
if (pid == 0) { /* child process */ if (pid == 0) { /* child process */
sfd->forever = false; sfd->retry = 0; sfd->forever = false;
sfd->retry = 0;
break; break;
} }
@ -437,21 +443,29 @@ static int xioopen_openssl_connect(
sfd->para.openssl.ssl = NULL; sfd->para.openssl.ssl = NULL;
/* with and without retry */ /* with and without retry */
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); while (maxchildren > 0 && num_child >= maxchildren) {
Info1("all %d allowed children are active, waiting", maxchildren);
Nanosleep(&sfd->intervall, NULL);
}
freeopts(opts);
continue; /* with next socket() bind() connect() */ continue; /* with next socket() bind() connect() */
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
break; break;
} while (true); /* drop out on success */ } while (true); /* drop out on success */
xiofreeaddrinfo(themarr);
openssl_conn_loginfo(sfd->para.openssl.ssl); openssl_conn_loginfo(sfd->para.openssl.ssl);
free((void *)opt_commonname); free((void *)opt_commonname);
free((void *)opt_snihost); free((void *)opt_snihost);
/* fill in the fd structure */ Notice2("successfully connected to SSL server %s:%s", hostname, portname);
return STAT_OK;
result = _xio_openlate(sfd, opts);
freeopts(opts);
freeopts(opts0);
return result;
} }

View file

@ -83,18 +83,18 @@ static int xioopen_proxy_connect(
xiofile_t *xxfd, xiofile_t *xxfd,
const struct addrdesc *addrdesc) const struct addrdesc *addrdesc)
{ {
/* we expect the form: host:host:port */ /* we expect the form: host:host:port */
struct single *sfd = &xxfd->stream; struct single *sfd = &xxfd->stream;
struct opt *opts0 = NULL; struct opt *opts0 = NULL;
struct proxyvars struct_proxyvars = { 0 }, *proxyvars = &struct_proxyvars; struct proxyvars struct_proxyvars = { 0 }, *proxyvars = &struct_proxyvars;
/* variables to be filled with address option values */ /* variables to be filled with address option values */
bool dofork = false; bool dofork = false;
int maxchildren = 0;
/* */ /* */
int pf = PF_UNSPEC; int pf = PF_UNSPEC;
union sockaddr_union us_sa, *us = &us_sa; struct addrinfo **bindarr = NULL;
socklen_t uslen = sizeof(us_sa); struct addrinfo **themarr = NULL;
struct addrinfo **themarr, *themp; uint16_t bindport = 0;
int i;
const char *proxyname; char *proxyport = NULL; const char *proxyname; char *proxyport = NULL;
const char *targetname, *targetport; const char *targetname, *targetport;
int ipproto = IPPROTO_TCP; int ipproto = IPPROTO_TCP;
@ -112,90 +112,123 @@ static int xioopen_proxy_connect(
targetname = argv[2]; targetname = argv[2];
targetport = argv[3]; targetport = argv[3];
if (sfd->howtoend == END_UNSPEC)
sfd->howtoend = END_SHUTDOWN;
if (applyopts_single(sfd, opts, PH_INIT) < 0)
return -1;
applyopts(sfd, 1, opts, PH_INIT);
retropt_int(opts, OPT_SO_TYPE, &socktype);
retropt_bool(opts, OPT_FORK, &dofork);
if (retropt_string(opts, OPT_PROXYPORT, &proxyport) < 0) { if (retropt_string(opts, OPT_PROXYPORT, &proxyport) < 0) {
if ((proxyport = strdup(PROXYPORT)) == NULL) { if ((proxyport = strdup(PROXYPORT)) == NULL) {
errno = ENOMEM; return -1; errno = ENOMEM; return -1;
} }
} }
result = _xioopen_proxy_prepare(proxyvars, opts, targetname, targetport, result =
sfd->para.socket.ip.ai_flags); _xioopen_ipapp_init(sfd, xioflags, opts,
if (result != STAT_OK) &dofork, &maxchildren,
return result; &pf, &socktype, &ipproto);
Notice4("opening connection to %s:%u via proxy %s:%s",
proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport);
i = 0;
do { /* loop over retries (failed connect and proxy-request attempts) */
level = E_INFO;
result =
_xioopen_ipapp_prepare(opts, &opts0, proxyname, proxyport,
&pf, ipproto,
sfd->para.socket.ip.ai_flags,
&themarr, us, &uslen,
&needbind, &lowport, socktype);
if (result != STAT_OK) if (result != STAT_OK)
return result; return result;
/* Loop over themlist */ result = _xioopen_proxy_init(proxyvars, opts, targetname, targetport);
i = 0; if (result != STAT_OK)
themp = themarr[i++]; return result;
while (themp != NULL) {
Notice4("opening connection to %s:%u via proxy %s:%s",
proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport);
#if WITH_RETRY
if (sfd->forever || sfd->retry || themarr[i] != NULL) {
level = E_INFO;
} else
#endif /* WITH_RETRY */
level = E_ERROR;
result = opts0 = opts; /* save remaining options for each loop */
_xioopen_connect(sfd, opts = NULL;
needbind?us:NULL, uslen,
themp->ai_addr, themp->ai_addrlen, Notice4("opening connection to %s:%s via proxy %s:%s",
opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP, lowport, level); targetname, targetport, proxyname, proxyport);
if (result == STAT_OK)
break; do { /* loop over retries (failed connect and proxy-request attempts)
themp = themarr[i++]; and/or forks */
if (themp == NULL) { int _errno;
result = STAT_RETRYLATER;
} #if WITH_RETRY
if (sfd->forever || sfd->retry) {
level = E_NOTICE;
} else
#endif /* WITH_RETRY */
level = E_WARN;
opts = copyopts(opts0, GROUP_ALL);
result =
_xioopen_ipapp_prepare(&opts, opts0, proxyname, proxyport,
pf, socktype, ipproto,
sfd->para.socket.ip.ai_flags,
&themarr, &bindarr, &bindport, &needbind, &lowport);
switch (result) { switch (result) {
case STAT_OK: break; case STAT_OK: break;
#if WITH_RETRY #if WITH_RETRY
case STAT_RETRYLATER: case STAT_RETRYLATER:
case STAT_RETRYNOW: case STAT_RETRYNOW:
if (sfd->forever || sfd->retry) { if (sfd->forever || sfd->retry--) {
--sfd->retry;
if (result == STAT_RETRYLATER) if (result == STAT_RETRYLATER)
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr);
freeopts(opts);
continue; continue;
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: /* FALLTHROUGH */
case STAT_NORETRY:
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr); xiofreeaddrinfo(themarr);
freeopts(opts);
freeopts(opts0);
return result; return result;
} }
}
xiofreeaddrinfo(themarr);
applyopts(sfd, -1, opts, PH_ALL);
if ((result = _xio_openlate(sfd, opts)) < 0) result =
_xioopen_proxy_prepare(proxyvars, opts, targetname, targetport,
sfd->para.socket.ip.ai_flags);
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 */
default:
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr);
freeopts(opts);
freeopts(opts0);
return result; return result;
}
Notice2("opening connection to proxy %s:%s", proxyname, proxyport);
result =
_xioopen_ipapp_connect(sfd, proxyname, 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:...,proxyport=%s: %s", argv[0], proxyname, proxyport,
_errno?strerror(_errno):"(See above)");
freeopts(opts0);
freeopts(opts);
return result;
}
result = _xioopen_proxy_connect(sfd, proxyvars, level); result = _xioopen_proxy_connect(sfd, proxyvars, level);
switch (result) { switch (result) {
@ -204,11 +237,16 @@ static int xioopen_proxy_connect(
case STAT_RETRYLATER: case STAT_RETRYLATER:
case STAT_RETRYNOW: case STAT_RETRYNOW:
if (sfd->forever || sfd->retry--) { if (sfd->forever || sfd->retry--) {
if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL); if (result == STAT_RETRYLATER)
Nanosleep(&sfd->intervall, NULL);
freeopts(opts);
continue; continue;
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
/* FALLTHROUGH */
default: default:
freeopts(opts);
freeopts(opts0);
return result; return result;
} }
@ -225,21 +263,32 @@ static int xioopen_proxy_connect(
} }
while ((pid = xio_fork(false, level, sfd->shutup)) < 0) { while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
if (sfd->forever || --sfd->retry) { if (sfd->forever || --sfd->retry) {
Nanosleep(&sfd->intervall, NULL); continue; if (sfd->retry > 0)
--sfd->retry;
Nanosleep(&sfd->intervall, NULL);
freeopts(opts);
continue;
} }
freeopts(opts);
freeopts(opts0);
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
if (pid == 0) { /* child process */ if (pid == 0) { /* child process */
sfd->forever = false; sfd->retry = 0; sfd->forever = false;
sfd->retry = 0;
break; break;
} }
/* parent process */ /* parent process */
Close(sfd->fd); Close(sfd->fd);
/* With and without retry */
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
dropopts(opts, PH_ALL); while (maxchildren > 0 && num_child >= maxchildren) {
opts = copyopts(opts0, GROUP_ALL); Info1("all %d allowed children are active, waiting", maxchildren);
Nanosleep(&sfd->intervall, NULL);
}
freeopts(opts);
continue; continue;
} else } else
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
@ -248,25 +297,25 @@ static int xioopen_proxy_connect(
} }
} while (true); /* end of complete open loop - drop out on success */ } while (true); /* end of complete open loop - drop out on success */
/* Only "active" process breaks (master without fork, or child) */
Notice4("successfully connected to %s:%u via proxy %s:%s", Notice4("successfully connected to %s:%u via proxy %s:%s",
proxyvars->targetaddr, proxyvars->targetport, proxyvars->targetaddr, proxyvars->targetport,
proxyname, proxyport); proxyname, proxyport);
return 0; result = _xio_openlate(sfd, opts);
freeopts(opts);
freeopts(opts0);
return result;
} }
int _xioopen_proxy_prepare( int _xioopen_proxy_init(
struct proxyvars *proxyvars, struct proxyvars *proxyvars,
struct opt *opts, struct opt *opts,
const char *targetname, const char *targetname,
const char *targetport, const char *targetport)
const int ai_flags[2]) { {
union sockaddr_union host;
socklen_t socklen = sizeof(host);
int rc;
retropt_bool(opts, OPT_IGNORECR, &proxyvars->ignorecr); retropt_bool(opts, OPT_IGNORECR, &proxyvars->ignorecr);
retropt_bool(opts, OPT_PROXY_RESOLVE, &proxyvars->doresolve); retropt_bool(opts, OPT_PROXY_RESOLVE, &proxyvars->doresolve);
retropt_string(opts, OPT_HTTP_VERSION, &proxyvars->version); retropt_string(opts, OPT_HTTP_VERSION, &proxyvars->version);
@ -316,6 +365,28 @@ int _xioopen_proxy_prepare(
Close(authfd); Close(authfd);
} }
if (!proxyvars->doresolve) {
proxyvars->targetaddr = strdup(targetname);
if (proxyvars->targetaddr == NULL) {
Error1("strdup(\"%s\"): out of memory", targetname);
return STAT_RETRYLATER;
}
}
return STAT_OK;
}
int _xioopen_proxy_prepare(
struct proxyvars *proxyvars,
struct opt *opts,
const char *targetname,
const char *targetport,
const int ai_flags[2])
{
union sockaddr_union host;
socklen_t socklen = sizeof(host);
int rc;
if (proxyvars->doresolve) { if (proxyvars->doresolve) {
/* currently we only resolve to IPv4 addresses. This is in accordance to /* currently we only resolve to IPv4 addresses. This is in accordance to
RFC 2396; however once it becomes clear how IPv6 addresses should be RFC 2396; however once it becomes clear how IPv6 addresses should be
@ -325,6 +396,10 @@ int _xioopen_proxy_prepare(
&host, &socklen, ai_flags); &host, &socklen, ai_flags);
if (rc != STAT_OK) { if (rc != STAT_OK) {
proxyvars->targetaddr = strdup(targetname); proxyvars->targetaddr = strdup(targetname);
if (proxyvars->targetaddr == NULL) {
Error1("strdup(\"%s\"): out of memory", targetname);
return STAT_RETRYLATER;
}
} else { } else {
#define LEN 16 /* www.xxx.yyy.zzz\0 */ #define LEN 16 /* www.xxx.yyy.zzz\0 */
if ((proxyvars->targetaddr = Malloc(LEN)) == NULL) { if ((proxyvars->targetaddr = Malloc(LEN)) == NULL) {
@ -337,8 +412,6 @@ int _xioopen_proxy_prepare(
((unsigned char *)&host.ip4.sin_addr.s_addr)[3]); ((unsigned char *)&host.ip4.sin_addr.s_addr)[3]);
#undef LEN #undef LEN
} }
} else {
proxyvars->targetaddr = strdup(targetname);
} }
proxyvars->targetport = htons(parseport(targetport, IPPROTO_TCP)); proxyvars->targetport = htons(parseport(targetport, IPPROTO_TCP));

View file

@ -25,9 +25,8 @@ extern const struct optdesc opt_proxy_authorization_file;
extern const struct addrdesc xioaddr_proxy_connect; extern const struct addrdesc xioaddr_proxy_connect;
extern int _xioopen_proxy_init(struct proxyvars *proxyvars, struct opt *opts, const char *targetname, const char *targetport);
extern int _xioopen_proxy_prepare(struct proxyvars *proxyvars, struct opt *opts, const char *targetname, const char *targetport, const int ai_flags[2]); extern int _xioopen_proxy_prepare(struct proxyvars *proxyvars, struct opt *opts, const char *targetname, const char *targetport, const int ai_flags[2]);
int _xioopen_proxy_connect(struct single *xfd, extern int _xioopen_proxy_connect(struct single *xfd, struct proxyvars *proxyvars, int level);
struct proxyvars *proxyvars,
int level);
#endif /* !defined(__xio_proxy_h_included) */ #endif /* !defined(__xio_proxy_h_included) */

View file

@ -805,7 +805,7 @@ int xiogetpacketinfo(struct single *sfd, int fd)
with IP sockets: lowport (selects randomly a free port from 640 to 1023) with IP sockets: lowport (selects randomly a free port from 640 to 1023)
with UNIX and abstract sockets: uses tmpname() to find a free file system with UNIX and abstract sockets: uses tmpname() to find a free file system
entry. entry.
returns 0 on success. Returns STAT_OK on success, or STAT_RETRYLATER (+errno) on failure.
*/ */
int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen, int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen,
struct sockaddr *them, size_t themlen, struct sockaddr *them, size_t themlen,
@ -835,7 +835,7 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen,
applyopts_cloexec(sfd->fd, opts); applyopts_cloexec(sfd->fd, opts);
if (xiobind(sfd, us, uslen, opts, pf, alt, level) < 0) { if (xiobind(sfd, us, uslen, opts, pf, alt, level) < 0) {
return -1; return STAT_RETRYLATER;
} }
applyopts(sfd, -1, opts, PH_CONNECT); applyopts(sfd, -1, opts, PH_CONNECT);
@ -875,6 +875,7 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen,
Msg4(level, "xiopoll({%d,POLLOUT|POLLERR},,{"F_tv_sec"."F_tv_usec"): %s", Msg4(level, "xiopoll({%d,POLLOUT|POLLERR},,{"F_tv_sec"."F_tv_usec"): %s",
sfd->fd, timeout.tv_sec, timeout.tv_usec, strerror(errno)); sfd->fd, timeout.tv_sec, timeout.tv_usec, strerror(errno));
Close(sfd->fd); Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
if (result == 0) { if (result == 0) {
@ -882,6 +883,7 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen,
sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
strerror(ETIMEDOUT)); strerror(ETIMEDOUT));
Close(sfd->fd); Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
if (writefd.revents & POLLERR) { if (writefd.revents & POLLERR) {
@ -897,15 +899,19 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen,
sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
themlen, strerror(errno)); themlen, strerror(errno));
#endif #endif
_errno = errno;
Close(sfd->fd); Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
/* otherwise OK or network error */ /* otherwise OK or network error */
result = Getsockopt(sfd->fd, SOL_SOCKET, SO_ERROR, &err, &errlen); result = Getsockopt(sfd->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
if (result != 0) { if (result != 0) {
_errno = errno;
Msg2(level, "getsockopt(%d, SOL_SOCKET, SO_ERROR, ...): %s", Msg2(level, "getsockopt(%d, SOL_SOCKET, SO_ERROR, ...): %s",
sfd->fd, strerror(err)); sfd->fd, strerror(err));
Close(sfd->fd); Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
Debug2("getsockopt(%d, SOL_SOCKET, SO_ERROR, { %d }) -> 0", Debug2("getsockopt(%d, SOL_SOCKET, SO_ERROR, { %d }) -> 0",
@ -915,6 +921,7 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen,
sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
themlen, strerror(err)); themlen, strerror(err));
Close(sfd->fd); Close(sfd->fd);
errno = err;
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
Fcntl_l(sfd->fd, F_SETFL, fcntl_flags); Fcntl_l(sfd->fd, F_SETFL, fcntl_flags);
@ -945,6 +952,7 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen,
sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
themlen, strerror(errno)); themlen, strerror(errno));
Close(sfd->fd); Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
} else { /* result >= 0 */ } else { /* result >= 0 */
@ -1679,9 +1687,7 @@ int xiocheckrange(union sockaddr_union *sa, struct xiorange *range) {
int xiocheckpeer(xiosingle_t *sfd, int xiocheckpeer(xiosingle_t *sfd,
union sockaddr_union *pa, union sockaddr_union *la) { union sockaddr_union *pa, union sockaddr_union *la) {
char infobuff[256]; char infobuff[256];
#if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
int result; int result;
#endif
#if WITH_IP4 || WITH_IP6 #if WITH_IP4 || WITH_IP6
if (sfd->para.socket.dorange) { if (sfd->para.socket.dorange) {
@ -2091,9 +2097,8 @@ int xiosetsockaddrenv(const char *lr,
/* retrieves options so-type and so-prototype from opts, calls socket, and /* retrieves options so-type and so-prototype from opts, calls socket, and
ev. generates an appropriate error message. ev. generates an appropriate error message.
returns 0 on success or -1 if an error occurred. */ returns 0 on success or -1 (and errno) if an error occurred. */
int int xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) {
xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) {
int result; int result;
retropt_int(opts, OPT_SO_TYPE, &socktype); retropt_int(opts, OPT_SO_TYPE, &socktype);
@ -2118,6 +2123,7 @@ xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) {
with IP sockets: lowport (selects randomly a free port from 640 to 1023) with IP sockets: lowport (selects randomly a free port from 640 to 1023)
with UNIX and abstract sockets: uses a method similar to tmpname() to with UNIX and abstract sockets: uses a method similar to tmpname() to
find a free file system entry. find a free file system entry.
On success returns STAT_OK, otherwise errno is set.
*/ */
int xiobind( int xiobind(
struct single *sfd, struct single *sfd,
@ -2154,18 +2160,20 @@ int xiobind(
usrname = strndup(us->un.sun_path, sizeof(us->un.sun_path)); usrname = strndup(us->un.sun_path, sizeof(us->un.sun_path));
if (usrname == NULL) { if (usrname == NULL) {
int _errno = errno; int _errno = errno;
Error2("strndup(\"%s\", "F_Zu"): out of memory", Msg2(level, "strndup(\"%s\", "F_Zu"): out of memory",
us->un.sun_path, sizeof(us->un.sun_path)); us->un.sun_path, sizeof(us->un.sun_path));
errno = _errno; errno = _errno;
return -1; return STAT_RETRYLATER;
} }
} }
do { /* loop over tempnam bind() attempts */ do { /* loop over tempnam bind() attempts */
sockname = xio_tempnam(usrname, abstract); sockname = xio_tempnam(usrname, abstract);
if (sockname == NULL) { if (sockname == NULL) {
int _errno = errno;
Error2("tempnam(\"%s\"): %s", usrname, strerror(errno)); Error2("tempnam(\"%s\"): %s", usrname, strerror(errno));
free(usrname); free(usrname);
errno = _errno;
return -1; return -1;
} }
strncpy(us->un.sun_path+(abstract?1:0), sockname, sizeof(us->un.sun_path)); strncpy(us->un.sun_path+(abstract?1:0), sockname, sizeof(us->un.sun_path));
@ -2179,8 +2187,10 @@ int xiobind(
infobuff, sizeof(infobuff)), infobuff, sizeof(infobuff)),
uslen, strerror(errno)); uslen, strerror(errno));
if (errno != EADDRINUSE) { if (errno != EADDRINUSE) {
int _errno = errno;
free(usrname); free(usrname);
Close(sfd->fd); Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
} else { } else {
@ -2193,10 +2203,12 @@ int xiobind(
if (us != NULL) { if (us != NULL) {
if (Bind(sfd->fd, &us->soa, uslen) < 0) { if (Bind(sfd->fd, &us->soa, uslen) < 0) {
int _errno = errno;
Msg4(level, "bind(%d, {%s}, "F_Zd"): %s", Msg4(level, "bind(%d, {%s}, "F_Zd"): %s",
sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)),
uslen, strerror(errno)); uslen, strerror(errno));
Close(sfd->fd); Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
applyopts_named(us->un.sun_path, opts, PH_PREOPEN); applyopts_named(us->un.sun_path, opts, PH_PREOPEN);
@ -2268,12 +2280,14 @@ int xiobind(
do { /* loop over lowport bind() attempts */ do { /* loop over lowport bind() attempts */
*port = htons(i); *port = htons(i);
if (Bind(sfd->fd, &sinp->soa, sizeof(*sinp)) < 0) { if (Bind(sfd->fd, &sinp->soa, sizeof(*sinp)) < 0) {
int _errno = errno;
Msg4(errno==EADDRINUSE?E_INFO:level, Msg4(errno==EADDRINUSE?E_INFO:level,
"bind(%d, {%s}, "F_Zd"): %s", sfd->fd, "bind(%d, {%s}, "F_Zd"): %s", sfd->fd,
sockaddr_info(&sinp->soa, sizeof(*sinp), infobuff, sizeof(infobuff)), sockaddr_info(&sinp->soa, sizeof(*sinp), infobuff, sizeof(infobuff)),
sizeof(*sinp), strerror(errno)); sizeof(*sinp), strerror(errno));
if (errno != EADDRINUSE) { if (_errno != EADDRINUSE) {
Close(sfd->fd); Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
} else { } else {
@ -2282,8 +2296,8 @@ int xiobind(
--i; if (i < XIO_IPPORT_LOWER) i = IPPORT_RESERVED-1; --i; if (i < XIO_IPPORT_LOWER) i = IPPORT_RESERVED-1;
if (i == N) { if (i == N) {
Msg(level, "no low port available"); Msg(level, "no low port available");
/*errno = EADDRINUSE; still assigned */
Close(sfd->fd); Close(sfd->fd);
errno = EADDRINUSE;
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
} while (i != N); } while (i != N);
@ -2294,17 +2308,19 @@ int xiobind(
if (us) { if (us) {
applyopts(sfd, sfd->fd, opts, PH_BIND); applyopts(sfd, sfd->fd, opts, PH_BIND);
if (Bind(sfd->fd, &us->soa, uslen) < 0) { if (Bind(sfd->fd, &us->soa, uslen) < 0) {
int _errno = errno;
Msg4(level, "bind(%d, {%s}, "F_Zd"): %s", Msg4(level, "bind(%d, {%s}, "F_Zd"): %s",
sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)),
uslen, strerror(errno)); uslen, strerror(errno));
Close(sfd->fd); Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
} }
} }
applyopts(sfd, -1, opts, PH_PASTBIND); applyopts(sfd, -1, opts, PH_PASTBIND);
return 0; return STAT_OK;
} }
/* Handles the SO_REUSEADDR socket option for TCP LISTEN addresses depending on /* Handles the SO_REUSEADDR socket option for TCP LISTEN addresses depending on

View file

@ -54,13 +54,12 @@ static int xioopen_socks4_connect(
int pf = PF_UNSPEC; int pf = PF_UNSPEC;
int ipproto = IPPROTO_TCP; int ipproto = IPPROTO_TCP;
bool dofork = false; bool dofork = false;
union sockaddr_union us_sa, *us = &us_sa; int maxchildren = 0;
socklen_t uslen = sizeof(us_sa); struct addrinfo **bindarr = NULL;
struct addrinfo **themarr, *themp; struct addrinfo **themarr = NULL;
int i; uint16_t bindport = 0;
bool needbind = false; bool needbind = false;
bool lowport = false; bool lowport = false;
char infobuff[256];
unsigned char buff[BUFF_LEN]; unsigned char buff[BUFF_LEN];
struct socks4 *sockhead = (struct socks4 *)buff; struct socks4 *sockhead = (struct socks4 *)buff;
size_t buflen = sizeof(buff); size_t buflen = sizeof(buff);
@ -76,41 +75,43 @@ static int xioopen_socks4_connect(
targetname = argv[2]; targetname = argv[2];
targetport = argv[3]; targetport = argv[3];
if (sfd->howtoend == END_UNSPEC) /* Apply and retrieve some options */
sfd->howtoend = END_SHUTDOWN; result = _xioopen_ipapp_init(sfd, xioflags, opts,
if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1; &dofork, &maxchildren,
applyopts(sfd, 1, opts, PH_INIT); &pf, &socktype, &ipproto);
retropt_int(opts, OPT_SO_TYPE, &socktype);
retropt_bool(opts, OPT_FORK, &dofork);
result = _xioopen_socks4_prepare(targetport, opts, &socksport, sockhead, &buflen);
if (result != STAT_OK) if (result != STAT_OK)
return result; return result;
result = _xioopen_socks4_init(targetport, opts, &socksport, sockhead,
&buflen);
if (result != STAT_OK)
return result;
opts0 = opts; /* save remaining options for each loop */
opts = NULL;
Notice5("opening connection to %s:%u via socks4 server %s:%s as user \"%s\"", Notice5("opening connection to %s:%u via socks4 server %s:%s as user \"%s\"",
targetname, targetname, ntohs(sockhead->port),
ntohs(sockhead->port),
sockdname, socksport, sockhead->userid); sockdname, socksport, sockhead->userid);
i = 0; do { /* loop over retries (failed connect and socks-request attempts)
do { /* loop over retries (failed connect and socks-request attempts) */ and/or forks */
int _errno;
level = E_INFO; #if WITH_RETRY
if (sfd->forever || sfd->retry) {
level = E_NOTICE;
} else
#endif /* WITH_RETRY */
level = E_WARN;
opts = copyopts(opts0, GROUP_ALL);
result = result =
_xioopen_ipapp_prepare(opts, &opts0, sockdname, socksport, _xioopen_ipapp_prepare(&opts, opts0, sockdname, socksport,
&pf, ipproto, pf, socktype, ipproto,
sfd->para.socket.ip.ai_flags, sfd->para.socket.ip.ai_flags,
&themarr, us, &uslen, &themarr, &bindarr, &bindport, &needbind, &lowport);
&needbind, &lowport, socktype);
/* we try to resolve the target address _before_ connecting to the socks
server: this avoids unnecessary socks connects and timeouts */
result =
_xioopen_socks4_connect0(sfd, targetname, socks4a, sockhead,
(ssize_t *)&buflen, level);
switch (result) { switch (result) {
case STAT_OK: break; case STAT_OK: break;
#if WITH_RETRY #if WITH_RETRY
@ -119,60 +120,78 @@ static int xioopen_socks4_connect(
if (sfd->forever || sfd->retry--) { if (sfd->forever || sfd->retry--) {
if (result == STAT_RETRYLATER) if (result == STAT_RETRYLATER)
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr);
freeopts(opts);
continue; continue;
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: /* FALLTHROUGH */
case STAT_NORETRY:
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr);
freeopts(opts);
freeopts(opts0);
return result; return result;
} }
/* loop over themarr */ /* we try to resolve the target address _before_ connecting to the socks
i = 0; server: this may avoid unnecessary connects and timeouts */
themp = themarr[i++]; result =
while (themp != NULL) { _xioopen_socks4_prepare(sfd, targetname, socks4a, sockhead,
Notice1("opening connection to %s", (ssize_t *)&buflen, level);
sockaddr_info(themp->ai_addr, themp->ai_addrlen,
infobuff, sizeof(infobuff)));
#if WITH_RETRY
if (sfd->forever || sfd->retry || themarr[i] != NULL) {
level = E_INFO;
} else
#endif /* WITH_RETRY */
level = E_ERROR;
/* this cannot fork because we retrieved fork option above */
result =
_xioopen_connect(sfd,
needbind?us:NULL, uslen,
themp->ai_addr, themp->ai_addrlen,
opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP, lowport, level);
if (result == STAT_OK)
break;
themp = themarr[i++];
if (themp == NULL)
result = STAT_RETRYLATER;
}
switch (result) { switch (result) {
case STAT_OK: break; case STAT_OK: break;
#if WITH_RETRY #if WITH_RETRY
case STAT_RETRYLATER: case STAT_RETRYLATER:
case STAT_RETRYNOW: case STAT_RETRYNOW:
if (sfd->forever || sfd->retry) { if (sfd->forever || sfd->retry--) {
--sfd->retry;
if (result == STAT_RETRYLATER) if (result == STAT_RETRYLATER)
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr);
freeopts(opts);
continue; continue;
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
/* FALLTHROUGH */
default: default:
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr); xiofreeaddrinfo(themarr);
freeopts(opts);
freeopts(opts0);
return result; return result;
} }
xiofreeaddrinfo(themarr);
applyopts(sfd, -1, opts, PH_ALL);
if ((result = _xio_openlate(sfd, opts)) < 0) Notice2("opening connection to sockd %s:%s", sockdname, socksport);
result =
_xioopen_ipapp_connect(sfd, sockdname, 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:
errno = _errno;
Error4("%s:%s:...,socksport=%s: %s", argv[0], sockdname, socksport,
_errno?strerror(_errno):"(See above)");
freeopts(opts0);
freeopts(opts);
return result; return result;
}
result = _xioopen_socks4_connect(sfd, sockhead, buflen, level); result = _xioopen_socks4_connect(sfd, sockhead, buflen, level);
switch (result) { switch (result) {
@ -181,11 +200,16 @@ static int xioopen_socks4_connect(
case STAT_RETRYLATER: case STAT_RETRYLATER:
case STAT_RETRYNOW: case STAT_RETRYNOW:
if (sfd->forever || sfd->retry--) { if (sfd->forever || sfd->retry--) {
if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL); if (result == STAT_RETRYLATER)
Nanosleep(&sfd->intervall, NULL);
freeopts(opts);
continue; continue;
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
/* FALLTHROUGH */
default: default:
freeopts(opts);
freeopts(opts0);
return result; return result;
} }
@ -202,10 +226,13 @@ static int xioopen_socks4_connect(
so Notice is too weak */ so Notice is too weak */
} }
while ((pid = xio_fork(false, level, sfd->shutup)) < 0) { while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
if (sfd->forever || --sfd->retry) { if (sfd->forever || sfd->retry--) {
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
freeopts(opts);
continue; continue;
} }
freeopts(opts);
freeopts(opts0);
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
@ -217,8 +244,13 @@ static int xioopen_socks4_connect(
/* parent process */ /* parent process */
Close(sfd->fd); Close(sfd->fd);
/* with and without retry */
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); while (maxchildren > 0 && num_child >= maxchildren) {
Info1("all %d allowed children are active, waiting", maxchildren);
Nanosleep(&sfd->intervall, NULL);
}
freeopts(opts);
continue; continue;
} else } else
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
@ -227,7 +259,15 @@ static int xioopen_socks4_connect(
} }
} while (true); /* end of complete open loop - drop out on success */ } while (true); /* end of complete open loop - drop out on success */
return 0; /* only "active" process breaks (master without fork, or child) */
Notice4("successfully connected to %s:%s via sockd %s:%s",
targetname, targetport, sockdname, socksport);
result = _xio_openlate(sfd, opts);
freeopts(opts);
freeopts(opts0);
return result;
} }
#endif /* WITH_SOCKS4 || WITH_SOCKS4A */ #endif /* WITH_SOCKS4 || WITH_SOCKS4A */
@ -260,7 +300,13 @@ int _xioopen_opt_socksport(
#endif /* WITH_SOCKS4 || WITH_SOCKS4A || WITH_SOCKS5 */ #endif /* WITH_SOCKS4 || WITH_SOCKS4A || WITH_SOCKS5 */
#if WITH_SOCKS4 || WITH_SOCKS4A #if WITH_SOCKS4 || WITH_SOCKS4A
int _xioopen_socks4_prepare(const char *targetport, struct opt *opts, char **socksport, struct socks4 *sockhead, size_t *headlen) { int _xioopen_socks4_init(
const char *targetport,
struct opt *opts,
char **socksport,
struct socks4 *sockhead,
size_t *headlen)
{
char *userid; char *userid;
/* generate socks header - points to final target */ /* generate socks header - points to final target */
@ -286,14 +332,14 @@ int _xioopen_socks4_prepare(const char *targetport, struct opt *opts, char **soc
/* called within retry/fork loop, before connect() */ /* called within retry/fork loop, before connect() */
int int _xioopen_socks4_prepare(
_xioopen_socks4_connect0(struct single *sfd, struct single *sfd,
const char *hostname, /* socks target host */ const char *hostname, /* socks target host */
int socks4a, int socks4a,
struct socks4 *sockhead, struct socks4 *sockhead,
ssize_t *headlen, /* get available space, ssize_t *headlen, /* get available space, return used length*/
return used length*/ int level)
int level) { {
int result; int result;
if (!socks4a) { if (!socks4a) {
@ -435,16 +481,7 @@ int _xioopen_socks4_connect(struct single *sfd,
switch (replyhead->action) { switch (replyhead->action) {
case SOCKS_CD_GRANTED: case SOCKS_CD_GRANTED:
/* Notice("socks: connect request succeeded"); */ /* Notice("socks: connect request succeeded"); */
#if 0
if (Getsockname(sfd->fd, (struct sockaddr *)&us, &uslen) < 0) {
Warn4("getsockname(%d, %p, {%d}): %s",
sfd->fd, &us, uslen, strerror(errno));
}
Notice1("successfully connected from %s via socks4",
sockaddr_info((struct sockaddr *)&us, infobuff, sizeof(infobuff)));
#else
Notice("successfully connected via socks4"); Notice("successfully connected via socks4");
#endif
break; break;
case SOCKS_CD_FAILED: case SOCKS_CD_FAILED:

View file

@ -21,15 +21,8 @@ extern const struct addrdesc xioaddr_socks4_connect;
extern const struct addrdesc xioaddr_socks4a_connect; extern const struct addrdesc xioaddr_socks4a_connect;
extern int _xioopen_opt_socksport(struct opt *opts, char **socksport); extern int _xioopen_opt_socksport(struct opt *opts, char **socksport);
extern int _xioopen_socks4_prepare(const char *targetport, struct opt *opts, char **socksport, struct socks4 *sockhead, size_t *headlen); extern int _xioopen_socks4_init(const char *targetport, struct opt *opts, char **socksport, struct socks4 *sockhead, size_t *headlen);
extern int extern int _xioopen_socks4_prepare(struct single *xfd, const char *hostname, int socks4a, struct socks4 *sockhead, ssize_t *headlen, int level);
_xioopen_socks4_connect0(struct single *xfd,
const char *hostname, /* socks target host */
int socks4a,
struct socks4 *sockhead,
ssize_t *headlen, /* get available space,
return used length*/
int level);
extern int _xioopen_socks4_connect(struct single *xfd, extern int _xioopen_socks4_connect(struct single *xfd,
struct socks4 *sockhead, struct socks4 *sockhead,
size_t headlen, size_t headlen,

View file

@ -512,6 +512,7 @@ static int xioopen_socks5(
{ {
int socks_command = addrdesc->arg1; int socks_command = addrdesc->arg1;
bool dofork = false; bool dofork = false;
int maxchildren = 0;
int socktype = SOCK_STREAM; int socktype = SOCK_STREAM;
int pf = PF_UNSPEC; int pf = PF_UNSPEC;
int ipproto = IPPROTO_TCP; int ipproto = IPPROTO_TCP;
@ -519,12 +520,11 @@ static int xioopen_socks5(
struct opt *opts0 = NULL; struct opt *opts0 = NULL;
struct single *sfd = &xxfd->stream; struct single *sfd = &xxfd->stream;
const char *socks_server, *target_name, *target_port, *socks_port; const char *socks_server, *target_name, *target_port, *socks_port;
union sockaddr_union us_sa, *us = &us_sa; struct addrinfo **bindarr = NULL;
socklen_t uslen = sizeof(us_sa); struct addrinfo **themarr = NULL;
struct addrinfo **themarr, *themp; uint16_t bindport = 0;
bool needbind = false; bool needbind = false;
bool lowport = false; bool lowport = false;
char infobuff[256];
if (argc < 4 || argc > 5) { if (argc < 4 || argc > 5) {
xio_syntax(argv[0], 4, argc-1, addrdesc->syntax); xio_syntax(argv[0], 4, argc-1, addrdesc->syntax);
@ -542,75 +542,115 @@ static int xioopen_socks5(
target_port = argv[3]; target_port = argv[3];
} }
if (sfd->howtoend == END_UNSPEC) /* Apply and retrieve some options */
sfd->howtoend = END_SHUTDOWN; result = _xioopen_ipapp_init(sfd, xioflags, opts,
if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1; &dofork, &maxchildren,
applyopts(sfd, -1, opts, PH_INIT); &pf, &socktype, &ipproto);
if (result != STAT_OK)
retropt_int(opts, OPT_SO_TYPE, &socktype); return result;
retropt_bool(opts, OPT_FORK, &dofork);
if (_xioopen_opt_socksport(opts, (char **)&socks_port) < 0) { if (_xioopen_opt_socksport(opts, (char **)&socks_port) < 0) {
return STAT_NORETRY; return STAT_NORETRY;
} }
/*! possible memory leak */ /*! possible memory leak */
result = _xioopen_ipapp_prepare(opts, &opts0, socks_server, socks_port, opts0 = opts;
&pf, ipproto, opts = NULL;
sfd->para.socket.ip.ai_flags,
&themarr, us, &uslen,
&needbind, &lowport, socktype);
Notice2("connecting to socks5 server %s:%s", Notice4("opening connection to %s:%s vis socks5 server %s:%s",
socks_server, socks_port); target_name, target_port, socks_server, socks_port);
do { do { /* loop over retries (failed connect and socks-request attempts)
and/or forks */
int _errno;
#if WITH_RETRY #if WITH_RETRY
if (sfd->forever || sfd->retry) { if (sfd->forever || sfd->retry) {
level = E_INFO; level = E_NOTICE;
} else { } else
level = E_ERROR;
}
#endif #endif
level = E_WARN;
/* loop over themarr */ opts = copyopts(opts0, GROUP_ALL);
themp = themarr[0];
while (themp != NULL) {
Notice1("opening connection to %s",
sockaddr_info(themp->ai_addr, themp->ai_addrlen,
infobuff, sizeof(infobuff)));
result = _xioopen_connect(sfd, needbind?us:NULL, sizeof(*us),
themp->ai_addr, themp->ai_addrlen,
opts, pf?pf:themp->ai_family, socktype,
IPPROTO_TCP, lowport, level);
if (result == STAT_OK)
break;
themp = themp->ai_next;
if (themp == NULL)
result = STAT_RETRYLATER;
switch(result){ result =
break; _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 #if WITH_RETRY
case STAT_RETRYLATER: case STAT_RETRYLATER:
case STAT_RETRYNOW: case STAT_RETRYNOW:
if (sfd->forever || sfd->retry-- ) { if (sfd->forever || sfd->retry--) {
if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL); if (result == STAT_RETRYLATER)
continue; Nanosleep(&sfd->intervall, NULL);
} if (bindarr != NULL) xiofreeaddrinfo(bindarr);
#endif
default:
xiofreeaddrinfo(themarr); xiofreeaddrinfo(themarr);
return result; freeopts(opts);
continue;
} }
} #endif /* WITH_RETRY */
xiofreeaddrinfo(themarr); /* FALLTHROUGH */
applyopts(sfd, -1, opts, PH_ALL); case STAT_NORETRY:
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
if ((result = _xio_openlate(sfd, opts)) < 0) xiofreeaddrinfo(themarr);
freeopts(opts);
freeopts(opts0);
return result; return result;
}
if ((result = _xioopen_socks5_handshake(sfd, level)) != STAT_OK) { 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; return result;
} }
@ -622,11 +662,16 @@ static int xioopen_socks5(
case STAT_RETRYLATER: case STAT_RETRYLATER:
case STAT_RETRYNOW: case STAT_RETRYNOW:
if ( sfd->forever || sfd->retry-- ) { if ( sfd->forever || sfd->retry-- ) {
if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL); if (result == STAT_RETRYLATER)
Nanosleep(&sfd->intervall, NULL);
freeopts(opts);
continue; continue;
} }
#endif #endif /* WITH_RETRY */
/* FALLTHROUGH */
default: default:
freeopts(opts);
freeopts(opts0);
return result; return result;
} }
@ -642,12 +687,18 @@ static int xioopen_socks5(
level = E_WARN; level = E_WARN;
} }
while ((pid = xio_fork(false, level, sfd->shutup)) < 0) { while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
if (sfd->forever || --sfd->retry) { if (sfd->forever || sfd->retry) {
if (sfd->retry > 0)
--sfd->retry;
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
freeopts(opts);
continue; continue;
} }
freeopts(opts);
freeopts(opts0);
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
if ( pid == 0 ) { if ( pid == 0 ) {
sfd->forever = false; sfd->forever = false;
sfd->retry = 0; sfd->retry = 0;
@ -656,17 +707,26 @@ static int xioopen_socks5(
Close(sfd->fd); Close(sfd->fd);
Nanosleep(&sfd->intervall, NULL); Nanosleep(&sfd->intervall, NULL);
dropopts(opts, PH_ALL); while (maxchildren > 0 && num_child >= maxchildren) {
opts = copyopts(opts0, GROUP_ALL); Info1("all %d allowed children are active, waiting", maxchildren);
Nanosleep(&sfd->intervall, NULL);
}
freeopts(opts);
continue; continue;
} else } else
#endif #endif /* WITH_RETRY */
{ {
break; break;
} }
} while (true); } while (true);
return 0; 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 */ #endif /* WITH_SOCKS5 */

View file

@ -2912,7 +2912,6 @@ int showleft(const struct opt *opts) {
return 0; return 0;
} }
/* determines the address group from mode_t */ /* determines the address group from mode_t */
/* does not set GROUP_FD; cannot determine GROUP_TERMIOS ! */ /* does not set GROUP_FD; cannot determine GROUP_TERMIOS ! */
groups_t _groupbits(mode_t mode) { groups_t _groupbits(mode_t mode) {
@ -4690,3 +4689,11 @@ int dumpopts(struct opt *opts)
} }
return 0; return 0;
} }
/* Better with type specific free function */
void freeopts(
struct opt *opts)
{
free(opts);
return;
}

View file

@ -1031,6 +1031,7 @@ extern groups_t _groupbits(mode_t mode);
extern int dropopts(struct opt *opts, unsigned int phase); extern int dropopts(struct opt *opts, unsigned int phase);
extern int dropopts2(struct opt *opts, unsigned int from, unsigned int to); extern int dropopts2(struct opt *opts, unsigned int from, unsigned int to);
extern int dumpopts(struct opt *opts); extern int dumpopts(struct opt *opts);
extern void freeopts(struct opt *opts);
#if HAVE_BASIC_UID_T==1 #if HAVE_BASIC_UID_T==1
# define retropt_uid(o,c,r) retropt_short(o,c,r) # define retropt_uid(o,c,r) retropt_short(o,c,r)