1
0
Fork 0
mirror of https://repo.or.cz/socat.git synced 2025-05-23 21:22:42 +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.
Test: UNIX_L_BIND
Removed unused bytes variable from gettimestamp(), corrected #elsif and
socks4 record length.
Removed unused bytes variable from gettimestamp(), corrected #elsif,
and socks4 record length.
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:
POSIXMQ-RECV now takes option o-nonblock; this, in combination with -T,
makes it possible to terminate Socat in case the queue is empty.

View file

@ -9,6 +9,7 @@
# it is required for test.sh
# for TCP, use this script as:
# 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
#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 */
/* 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
/* 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;
}
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",
hints.ai_flags, hints.ai_family,
hints.ai_socktype, hints.ai_protocol,
error_num);
gai_strerror(error_num));
if (numnode)
free(numnode);
@ -760,6 +833,9 @@ void xiofreeaddrinfo(struct addrinfo **ai_sorted) {
int ain;
struct addrinfo *res;
if (ai_sorted == NULL)
return;
/* Find the original *res from getaddrinfo past NULL */
ain = 0;
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 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 void xiofreeaddrinfo(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_lowport = { "lowport", NULL, OPT_LOWPORT, GROUP_IPAPP, PH_LATE, TYPE_BOOL, OFUNC_SPEC };
#if _WITH_IP4 || _WITH_IP6
/* we expect the form "host:port" */
int xioopen_ipapp_connect(
@ -31,20 +32,18 @@ int xioopen_ipapp_connect(
{
struct single *sfd = &xxfd->stream;
struct opt *opts0 = NULL;
const char *hostname = argv[1], *portname = argv[2];
int pf = addrdesc->arg3;
int socktype = addrdesc->arg1;
int ipproto = addrdesc->arg2;
int pf = addrdesc->arg3;
const char *hostname = argv[1], *portname = argv[2];
bool dofork = false;
int maxchildren = 0;
union sockaddr_union us_sa, *us = &us_sa;
socklen_t uslen = sizeof(us_sa);
struct addrinfo **themarr, *themp;
char infobuff[256];
struct addrinfo **bindarr = NULL;
struct addrinfo **themarr = NULL;
uint16_t bindport = 0;
bool needbind = false;
bool lowport = false;
int level;
int i;
int level = E_ERROR;
int result;
if (argc != 3) {
@ -52,97 +51,84 @@ int xioopen_ipapp_connect(
return STAT_NORETRY;
}
if (sfd->howtoend == END_UNSPEC)
sfd->howtoend = END_SHUTDOWN;
/* Apply and retrieve some options */
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)
return -1;
applyopts(sfd, -1, opts, PH_INIT);
opts0 = opts; /* save remaining options for each loop */
opts = NULL;
retropt_bool(opts, OPT_FORK, &dofork);
if (dofork) {
if (!(xioflags & XIO_MAYFORK)) {
Error("option fork not allowed here");
return STAT_NORETRY;
}
sfd->flags |= XIO_DOESFORK;
}
Notice2("opening connection to %s:%s", hostname, portname);
retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);
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)));
do { /* loop over retries and/or forks */
int _errno;
#if WITH_RETRY
if (sfd->forever || sfd->retry) {
level = E_INFO;
} else if (themarr[i] != NULL) {
level = E_WARN;
} else
if (sfd->forever || sfd->retry) {
level = E_NOTICE;
} else
#endif /* WITH_RETRY */
level = E_ERROR;
level = E_WARN;
result =
_xioopen_connect(sfd,
needbind?us:NULL, uslen,
themp->ai_addr, themp->ai_addrlen,
opts, pf?pf:themp->ai_family, socktype, ipproto,
lowport, level);
if (result == STAT_OK)
break;
themp = themarr[i++];
if (themp == NULL) {
result = STAT_RETRYLATER;
}
}
opts = copyopts(opts0, GROUP_ALL);
result =
_xioopen_ipapp_prepare(&opts, opts0, hostname, portname,
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) {
--sfd->retry;
if (result == STAT_RETRYLATER) {
if (sfd->forever || sfd->retry--) {
if (result == STAT_RETRYLATER)
Nanosleep(&sfd->intervall, NULL);
}
dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL);
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr);
freeopts(opts);
continue;
}
#endif /* WITH_RETRY */
default:
/* FALLTHROUGH */
case STAT_NORETRY:
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
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;
}
@ -155,16 +141,18 @@ int xioopen_ipapp_connect(
so Notice is too weak */
}
while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
if (sfd->forever || --sfd->retry) {
Nanosleep(&sfd->intervall, NULL); continue;
if (sfd->forever || sfd->retry--) {
Nanosleep(&sfd->intervall, NULL);
continue;
}
xiofreeaddrinfo(themarr);
free(opts0);
freeopts(opts);
freeopts(opts0);
return STAT_RETRYLATER;
}
if (pid == 0) { /* child process */
sfd->forever = false; sfd->retry = 0;
sfd->forever = false;
sfd->retry = 0;
break;
}
@ -176,109 +164,266 @@ int xioopen_ipapp_connect(
Info1("all %d allowed children are active, waiting", maxchildren);
Nanosleep(&sfd->intervall, NULL);
}
dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL);
freeopts(opts);
continue; /* with next socket() bind() connect() */
} else
#endif /* WITH_RETRY */
{
break;
}
} while (true);
} while (true); /* end of loop over retries and/or forks */
/* only "active" process breaks (master without fork, or child) */
xiofreeaddrinfo(themarr);
if ((result = _xio_openlate(sfd, opts)) < 0) {
free(opts0);free(opts);
return result;
}
free(opts0); free(opts);
return 0;
Notice2("successfully connected to %s:%s", hostname, portname);
result = _xio_openlate(sfd, opts);
freeopts(opts);
freeopts(opts0);
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:
PH_EARLY
OPT_PROTOCOL_FAMILY, OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT
*/
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 ***themarr,
union sockaddr_union *us,
socklen_t *uslen,
bool *needbind,
bool *lowport,
int socktype) {
OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT
returns STAT_OK, STAT_RETRYLATER, or STAT_NORETRY (+errno)
*/
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, /* always from getaddrinfo(); xiofreeaddrinfo()! */
struct addrinfo ***bindarr, /* on bind from getaddrinfo(); xiofreeaddrinfo()! */
uint16_t *bindport, /* for bind without address */
bool *needbind,
bool *lowport)
{
uint16_t port;
int rc;
retropt_socket_pf(opts, pf);
*opts = copyopts(opts0, GROUP_ALL);
if (hostname != NULL || portname != NULL) {
rc = xiogetaddrinfo(hostname, portname, *pf, socktype, protocol,
rc = xiogetaddrinfo(hostname, portname, pf, socktype, protocol,
themarr, ai_flags);
if (rc == EAI_AGAIN) {
Warn4("_xioopen_ipapp_prepare(node=\"%s\", service=\"%s\", pf=%d, ...): %s",
hostname?hostname:"NULL", portname?portname:"NULL",
*pf, gai_strerror(rc));
pf, gai_strerror(rc));
errno = EAGAIN;
return STAT_RETRYLATER;
} else if (rc != 0) {
Error4("_xioopen_ipapp_prepare(node=\"%s\", service=\"%s\", pf=%d, ...): %s",
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? */
}
}
applyopts(NULL, -1, opts, PH_EARLY);
applyopts(NULL, -1, *opts, PH_EARLY);
/* 3 means: IP address AND port accepted */
if (retropt_bind(opts, (*pf!=PF_UNSPEC)?*pf:(**themarr)->ai_family,
socktype, protocol, (struct sockaddr *)us, uslen, 3,
ai_flags)
if (retropt_bind_ip(*opts, pf, socktype, protocol, bindarr, 3, ai_flags)
!= STAT_NOACTION) {
*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) {
switch ((*pf!=PF_UNSPEC)?*pf:(**themarr)->ai_family) {
if (retropt_2bytes(*opts, OPT_SOURCEPORT, &port) >= 0) {
if (*bindarr) {
struct addrinfo **bindp;
bindp = *bindarr;
switch ((*bindp)->ai_family) {
#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 */
#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 */
default: Error("unsupported protocol family");
default:
Error("unsupported protocol family");
errno = EPROTONOSUPPORT;
return STAT_NORETRY;
}
} else {
*bindport = port;
}
*needbind = true;
}
retropt_bool(opts, OPT_LOWPORT, lowport);
*opts0 = copyopts(opts, GROUP_ALL);
retropt_bool(*opts, OPT_LOWPORT, lowport);
return STAT_OK;
}
#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
/*
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 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_ip4app_connect(const char *hostname, const char *portname,
struct single *xfd,
int socktype, int ipproto, void *protname,
struct opt *opts);
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_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);
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);
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);

View file

@ -238,13 +238,13 @@ static int xioopen_openssl_connect(
int socktype = SOCK_STREAM;
int ipproto = IPPROTO_TCP;
bool dofork = false;
union sockaddr_union us_sa, *us = &us_sa;
socklen_t uslen = sizeof(us_sa);
struct addrinfo **themarr, *themp;
int maxchildren = 0;
struct addrinfo **bindarr = NULL;
struct addrinfo **themarr = NULL;
uint16_t bindport = 0;
bool needbind = false;
bool lowport = false;
int level = E_ERROR;
int i;
SSL_CTX* ctx;
bool opt_ver = true; /* verify peer certificate */
char *opt_cert = NULL; /* file name of client certificate */
@ -254,7 +254,7 @@ static int xioopen_openssl_connect(
int result;
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;
}
sfd->flags |= XIO_DOESCONVERT;
@ -272,14 +272,6 @@ static int xioopen_openssl_connect(
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_COMMONNAME, (char **)&opt_commonname);
#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;
ipproto = IPPROTO_UDP;
}
retropt_int(opts, OPT_SO_TYPE, &socktype);
retropt_int(opts, OPT_SO_PROTOTYPE, &ipproto);
result =
_xioopen_ipapp_prepare(opts, &opts0, hostname, portname, &pf, ipproto,
sfd->para.socket.ip.ai_flags,
&themarr, us, &uslen,
&needbind, &lowport, socktype);
if (result != STAT_OK) return STAT_NORETRY;
/* Apply and retrieve some options */
result = _xioopen_ipapp_init(sfd, xioflags, opts,
&dofork, &maxchildren,
&pf, &socktype, &ipproto);
if (result != STAT_OK)
return result;
if (xioparms.logopt == 'm') {
Info("starting connect loop, switching to syslog");
diag_set('y', xioparms.syslogfac); xioparms.logopt = 'y';
} else {
Info("starting connect loop");
}
opts0 = opts; /* save remaining options for each loop */
opts = NULL;
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") */
i = 0;
themp = themarr[i++];
while (themp != NULL) {
do { /* loop over retries (failed connect and SSL handshake attempts) and/or forks */
int _errno;
#if WITH_RETRY
if (sfd->forever || sfd->retry || themarr[i] != NULL) {
level = E_INFO;
} else
if (sfd->forever || sfd->retry) {
level = E_NOTICE;
} else
#endif /* WITH_RETRY */
level = E_ERROR;
level = E_WARN;
/* 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_addr->sa_family, socktype, ipproto, lowport, level);
if (result == STAT_OK)
break;
themp = themarr[i++];
if (themp == NULL) {
result = STAT_RETRYLATER;
}
}
opts = copyopts(opts0, GROUP_ALL);
result =
_xioopen_ipapp_prepare(&opts, opts0, hostname, portname,
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) {
dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
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 */
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) {
Nanosleep(&sfd->intervall, NULL);
}
--sfd->retry;
freeopts(opts);
continue;
}
xiofreeaddrinfo(themarr);
return STAT_NORETRY;
#endif /* WITH_RETRY */
default:
xiofreeaddrinfo(themarr);
return result;
}
/*! isn't this too early? */
if ((result = _xio_openlate(sfd, opts)) < 0) {
xiofreeaddrinfo(themarr);
Error4("%s:%s:%s: %s", argv[0], hostname, portname,
_errno?strerror(_errno):"(See above)");
freeopts(opts0);
freeopts(opts);
return result;
}
@ -392,19 +394,19 @@ static int xioopen_openssl_connect(
#if WITH_RETRY
case STAT_RETRYLATER:
case STAT_RETRYNOW:
if (sfd->forever || sfd->retry) {
Close(sfd->fd);
dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
if (sfd->forever || sfd->retry--) {
if (result == STAT_RETRYLATER) {
Nanosleep(&sfd->intervall, NULL);
}
--sfd->retry;
freeopts(opts);
Close(sfd->fd);
continue;
}
#endif /* WITH_RETRY */
default:
xiofreeaddrinfo(themarr);
return STAT_NORETRY;
freeopts(opts);
freeopts(opts0);
return result;
}
if (dofork) {
@ -419,15 +421,19 @@ static int xioopen_openssl_connect(
level = E_WARN;
}
while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
if (sfd->forever || --sfd->retry) {
Nanosleep(&sfd->intervall, NULL); continue;
if (sfd->forever || sfd->retry--) {
Nanosleep(&sfd->intervall, NULL);
freeopts(opts);
continue;
}
xiofreeaddrinfo(themarr);
freeopts(opts);
freeopts(opts0);
return STAT_RETRYLATER;
}
if (pid == 0) { /* child process */
sfd->forever = false; sfd->retry = 0;
sfd->forever = false;
sfd->retry = 0;
break;
}
@ -437,21 +443,29 @@ static int xioopen_openssl_connect(
sfd->para.openssl.ssl = NULL;
/* with and without retry */
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() */
}
#endif /* WITH_RETRY */
break;
} while (true); /* drop out on success */
xiofreeaddrinfo(themarr);
openssl_conn_loginfo(sfd->para.openssl.ssl);
free((void *)opt_commonname);
free((void *)opt_snihost);
/* fill in the fd structure */
return STAT_OK;
Notice2("successfully connected to SSL server %s:%s", hostname, portname);
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,
const struct addrdesc *addrdesc)
{
/* we expect the form: host:host:port */
/* we expect the form: host:host:port */
struct single *sfd = &xxfd->stream;
struct opt *opts0 = NULL;
struct proxyvars struct_proxyvars = { 0 }, *proxyvars = &struct_proxyvars;
/* variables to be filled with address option values */
bool dofork = false;
int maxchildren = 0;
/* */
int pf = PF_UNSPEC;
union sockaddr_union us_sa, *us = &us_sa;
socklen_t uslen = sizeof(us_sa);
struct addrinfo **themarr, *themp;
int i;
struct addrinfo **bindarr = NULL;
struct addrinfo **themarr = NULL;
uint16_t bindport = 0;
const char *proxyname; char *proxyport = NULL;
const char *targetname, *targetport;
int ipproto = IPPROTO_TCP;
@ -112,90 +112,123 @@ static int xioopen_proxy_connect(
targetname = argv[2];
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 ((proxyport = strdup(PROXYPORT)) == NULL) {
errno = ENOMEM; return -1;
}
}
result = _xioopen_proxy_prepare(proxyvars, opts, targetname, targetport,
sfd->para.socket.ip.ai_flags);
if (result != STAT_OK)
return result;
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);
result =
_xioopen_ipapp_init(sfd, xioflags, opts,
&dofork, &maxchildren,
&pf, &socktype, &ipproto);
if (result != STAT_OK)
return result;
/* Loop over themlist */
i = 0;
themp = themarr[i++];
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 = _xioopen_proxy_init(proxyvars, opts, targetname, targetport);
if (result != STAT_OK)
return result;
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;
}
opts0 = opts; /* save remaining options for each loop */
opts = NULL;
Notice4("opening connection to %s:%s via proxy %s:%s",
targetname, targetport, proxyname, proxyport);
do { /* loop over retries (failed connect and proxy-request attempts)
and/or forks */
int _errno;
#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) {
case STAT_OK: break;
#if WITH_RETRY
case STAT_RETRYLATER:
case STAT_RETRYNOW:
if (sfd->forever || sfd->retry) {
--sfd->retry;
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 */
default:
/* FALLTHROUGH */
case STAT_NORETRY:
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr);
freeopts(opts);
freeopts(opts0);
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;
}
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);
switch (result) {
@ -204,11 +237,16 @@ static int xioopen_proxy_connect(
case STAT_RETRYLATER:
case STAT_RETRYNOW:
if (sfd->forever || sfd->retry--) {
if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL);
if (result == STAT_RETRYLATER)
Nanosleep(&sfd->intervall, NULL);
freeopts(opts);
continue;
}
#endif /* WITH_RETRY */
/* FALLTHROUGH */
default:
freeopts(opts);
freeopts(opts0);
return result;
}
@ -225,21 +263,32 @@ static int xioopen_proxy_connect(
}
while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
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;
}
if (pid == 0) { /* child process */
sfd->forever = false; sfd->retry = 0;
sfd->forever = false;
sfd->retry = 0;
break;
}
/* parent process */
Close(sfd->fd);
/* With and without retry */
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;
} else
#endif /* WITH_RETRY */
@ -248,25 +297,25 @@ static int xioopen_proxy_connect(
}
} 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",
proxyvars->targetaddr, proxyvars->targetport,
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 opt *opts,
const char *targetname,
const char *targetport,
const int ai_flags[2]) {
union sockaddr_union host;
socklen_t socklen = sizeof(host);
int rc;
const char *targetport)
{
retropt_bool(opts, OPT_IGNORECR, &proxyvars->ignorecr);
retropt_bool(opts, OPT_PROXY_RESOLVE, &proxyvars->doresolve);
retropt_string(opts, OPT_HTTP_VERSION, &proxyvars->version);
@ -316,6 +365,28 @@ int _xioopen_proxy_prepare(
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) {
/* currently we only resolve to IPv4 addresses. This is in accordance to
RFC 2396; however once it becomes clear how IPv6 addresses should be
@ -325,6 +396,10 @@ int _xioopen_proxy_prepare(
&host, &socklen, ai_flags);
if (rc != STAT_OK) {
proxyvars->targetaddr = strdup(targetname);
if (proxyvars->targetaddr == NULL) {
Error1("strdup(\"%s\"): out of memory", targetname);
return STAT_RETRYLATER;
}
} else {
#define LEN 16 /* www.xxx.yyy.zzz\0 */
if ((proxyvars->targetaddr = Malloc(LEN)) == NULL) {
@ -337,8 +412,6 @@ int _xioopen_proxy_prepare(
((unsigned char *)&host.ip4.sin_addr.s_addr)[3]);
#undef LEN
}
} else {
proxyvars->targetaddr = strdup(targetname);
}
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 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]);
int _xioopen_proxy_connect(struct single *xfd,
struct proxyvars *proxyvars,
int level);
extern int _xioopen_proxy_connect(struct single *xfd, struct proxyvars *proxyvars, int level);
#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 UNIX and abstract sockets: uses tmpname() to find a free file system
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,
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);
if (xiobind(sfd, us, uslen, opts, pf, alt, level) < 0) {
return -1;
return STAT_RETRYLATER;
}
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",
sfd->fd, timeout.tv_sec, timeout.tv_usec, strerror(errno));
Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER;
}
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)),
strerror(ETIMEDOUT));
Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER;
}
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)),
themlen, strerror(errno));
#endif
_errno = errno;
Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER;
}
/* otherwise OK or network error */
result = Getsockopt(sfd->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
if (result != 0) {
_errno = errno;
Msg2(level, "getsockopt(%d, SOL_SOCKET, SO_ERROR, ...): %s",
sfd->fd, strerror(err));
Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER;
}
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)),
themlen, strerror(err));
Close(sfd->fd);
errno = err;
return STAT_RETRYLATER;
}
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)),
themlen, strerror(errno));
Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER;
}
} else { /* result >= 0 */
@ -1679,9 +1687,7 @@ int xiocheckrange(union sockaddr_union *sa, struct xiorange *range) {
int xiocheckpeer(xiosingle_t *sfd,
union sockaddr_union *pa, union sockaddr_union *la) {
char infobuff[256];
#if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
int result;
#endif
#if WITH_IP4 || WITH_IP6
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
ev. generates an appropriate error message.
returns 0 on success or -1 if an error occurred. */
int
xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) {
returns 0 on success or -1 (and errno) if an error occurred. */
int xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) {
int result;
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 UNIX and abstract sockets: uses a method similar to tmpname() to
find a free file system entry.
On success returns STAT_OK, otherwise errno is set.
*/
int xiobind(
struct single *sfd,
@ -2154,18 +2160,20 @@ int xiobind(
usrname = strndup(us->un.sun_path, sizeof(us->un.sun_path));
if (usrname == NULL) {
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));
errno = _errno;
return -1;
return STAT_RETRYLATER;
}
}
do { /* loop over tempnam bind() attempts */
sockname = xio_tempnam(usrname, abstract);
if (sockname == NULL) {
int _errno = errno;
Error2("tempnam(\"%s\"): %s", usrname, strerror(errno));
free(usrname);
errno = _errno;
return -1;
}
strncpy(us->un.sun_path+(abstract?1:0), sockname, sizeof(us->un.sun_path));
@ -2179,8 +2187,10 @@ int xiobind(
infobuff, sizeof(infobuff)),
uslen, strerror(errno));
if (errno != EADDRINUSE) {
int _errno = errno;
free(usrname);
Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER;
}
} else {
@ -2193,10 +2203,12 @@ int xiobind(
if (us != NULL) {
if (Bind(sfd->fd, &us->soa, uslen) < 0) {
int _errno = errno;
Msg4(level, "bind(%d, {%s}, "F_Zd"): %s",
sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)),
uslen, strerror(errno));
Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER;
}
applyopts_named(us->un.sun_path, opts, PH_PREOPEN);
@ -2268,12 +2280,14 @@ int xiobind(
do { /* loop over lowport bind() attempts */
*port = htons(i);
if (Bind(sfd->fd, &sinp->soa, sizeof(*sinp)) < 0) {
int _errno = errno;
Msg4(errno==EADDRINUSE?E_INFO:level,
"bind(%d, {%s}, "F_Zd"): %s", sfd->fd,
sockaddr_info(&sinp->soa, sizeof(*sinp), infobuff, sizeof(infobuff)),
sizeof(*sinp), strerror(errno));
if (errno != EADDRINUSE) {
if (_errno != EADDRINUSE) {
Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER;
}
} else {
@ -2282,8 +2296,8 @@ int xiobind(
--i; if (i < XIO_IPPORT_LOWER) i = IPPORT_RESERVED-1;
if (i == N) {
Msg(level, "no low port available");
/*errno = EADDRINUSE; still assigned */
Close(sfd->fd);
errno = EADDRINUSE;
return STAT_RETRYLATER;
}
} while (i != N);
@ -2294,17 +2308,19 @@ int xiobind(
if (us) {
applyopts(sfd, sfd->fd, opts, PH_BIND);
if (Bind(sfd->fd, &us->soa, uslen) < 0) {
int _errno = errno;
Msg4(level, "bind(%d, {%s}, "F_Zd"): %s",
sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)),
uslen, strerror(errno));
Close(sfd->fd);
errno = _errno;
return STAT_RETRYLATER;
}
}
}
applyopts(sfd, -1, opts, PH_PASTBIND);
return 0;
return STAT_OK;
}
/* 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 ipproto = IPPROTO_TCP;
bool dofork = false;
union sockaddr_union us_sa, *us = &us_sa;
socklen_t uslen = sizeof(us_sa);
struct addrinfo **themarr, *themp;
int i;
int maxchildren = 0;
struct addrinfo **bindarr = NULL;
struct addrinfo **themarr = NULL;
uint16_t bindport = 0;
bool needbind = false;
bool lowport = false;
char infobuff[256];
unsigned char buff[BUFF_LEN];
struct socks4 *sockhead = (struct socks4 *)buff;
size_t buflen = sizeof(buff);
@ -76,41 +75,43 @@ static int xioopen_socks4_connect(
targetname = argv[2];
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);
result = _xioopen_socks4_prepare(targetport, opts, &socksport, sockhead, &buflen);
/* Apply and retrieve some options */
result = _xioopen_ipapp_init(sfd, xioflags, opts,
&dofork, &maxchildren,
&pf, &socktype, &ipproto);
if (result != STAT_OK)
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\"",
targetname,
ntohs(sockhead->port),
targetname, ntohs(sockhead->port),
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 =
_xioopen_ipapp_prepare(opts, &opts0, sockdname, socksport,
&pf, ipproto,
_xioopen_ipapp_prepare(&opts, opts0, sockdname, socksport,
pf, socktype, ipproto,
sfd->para.socket.ip.ai_flags,
&themarr, us, &uslen,
&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);
&themarr, &bindarr, &bindport, &needbind, &lowport);
switch (result) {
case STAT_OK: break;
#if WITH_RETRY
@ -119,60 +120,78 @@ static int xioopen_socks4_connect(
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 */
default:
/* FALLTHROUGH */
case STAT_NORETRY:
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr);
freeopts(opts);
freeopts(opts0);
return result;
}
/* loop over themarr */
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 (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;
}
/* we try to resolve the target address _before_ connecting to the socks
server: this may avoid unnecessary connects and timeouts */
result =
_xioopen_socks4_prepare(sfd, targetname, socks4a, sockhead,
(ssize_t *)&buflen, level);
switch (result) {
case STAT_OK: break;
#if WITH_RETRY
case STAT_RETRYLATER:
case STAT_RETRYNOW:
if (sfd->forever || sfd->retry) {
--sfd->retry;
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;
}
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;
}
result = _xioopen_socks4_connect(sfd, sockhead, buflen, level);
switch (result) {
@ -181,11 +200,16 @@ static int xioopen_socks4_connect(
case STAT_RETRYLATER:
case STAT_RETRYNOW:
if (sfd->forever || sfd->retry--) {
if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL);
if (result == STAT_RETRYLATER)
Nanosleep(&sfd->intervall, NULL);
freeopts(opts);
continue;
}
#endif /* WITH_RETRY */
/* FALLTHROUGH */
default:
freeopts(opts);
freeopts(opts0);
return result;
}
@ -202,10 +226,13 @@ static int xioopen_socks4_connect(
so Notice is too weak */
}
while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
if (sfd->forever || --sfd->retry) {
if (sfd->forever || sfd->retry--) {
Nanosleep(&sfd->intervall, NULL);
freeopts(opts);
continue;
}
freeopts(opts);
freeopts(opts0);
return STAT_RETRYLATER;
}
@ -217,8 +244,13 @@ static int xioopen_socks4_connect(
/* parent process */
Close(sfd->fd);
/* with and without retry */
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;
} else
#endif /* WITH_RETRY */
@ -227,7 +259,15 @@ static int xioopen_socks4_connect(
}
} 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 */
@ -260,7 +300,13 @@ int _xioopen_opt_socksport(
#endif /* WITH_SOCKS4 || WITH_SOCKS4A || WITH_SOCKS5 */
#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;
/* 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() */
int
_xioopen_socks4_connect0(struct single *sfd,
const char *hostname, /* socks target host */
int socks4a,
struct socks4 *sockhead,
ssize_t *headlen, /* get available space,
return used length*/
int level) {
int _xioopen_socks4_prepare(
struct single *sfd,
const char *hostname, /* socks target host */
int socks4a,
struct socks4 *sockhead,
ssize_t *headlen, /* get available space, return used length*/
int level)
{
int result;
if (!socks4a) {
@ -435,16 +481,7 @@ int _xioopen_socks4_connect(struct single *sfd,
switch (replyhead->action) {
case SOCKS_CD_GRANTED:
/* 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");
#endif
break;
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 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_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_init(const char *targetport, struct opt *opts, char **socksport, struct socks4 *sockhead, size_t *headlen);
extern int _xioopen_socks4_prepare(struct single *xfd, const char *hostname, int socks4a, struct socks4 *sockhead, ssize_t *headlen, int level);
extern int _xioopen_socks4_connect(struct single *xfd,
struct socks4 *sockhead,
size_t headlen,

View file

@ -512,6 +512,7 @@ static int xioopen_socks5(
{
int socks_command = addrdesc->arg1;
bool dofork = false;
int maxchildren = 0;
int socktype = SOCK_STREAM;
int pf = PF_UNSPEC;
int ipproto = IPPROTO_TCP;
@ -519,12 +520,11 @@ static int xioopen_socks5(
struct opt *opts0 = NULL;
struct single *sfd = &xxfd->stream;
const char *socks_server, *target_name, *target_port, *socks_port;
union sockaddr_union us_sa, *us = &us_sa;
socklen_t uslen = sizeof(us_sa);
struct addrinfo **themarr, *themp;
struct addrinfo **bindarr = NULL;
struct addrinfo **themarr = NULL;
uint16_t bindport = 0;
bool needbind = false;
bool lowport = false;
char infobuff[256];
if (argc < 4 || argc > 5) {
xio_syntax(argv[0], 4, argc-1, addrdesc->syntax);
@ -542,75 +542,115 @@ static int xioopen_socks5(
target_port = 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);
/* 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 */
result = _xioopen_ipapp_prepare(opts, &opts0, socks_server, socks_port,
&pf, ipproto,
sfd->para.socket.ip.ai_flags,
&themarr, us, &uslen,
&needbind, &lowport, socktype);
opts0 = opts;
opts = NULL;
Notice2("connecting to socks5 server %s:%s",
socks_server, socks_port);
Notice4("opening connection to %s:%s vis socks5 server %s:%s",
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 (sfd->forever || sfd->retry) {
level = E_INFO;
} else {
level = E_ERROR;
}
level = E_NOTICE;
} else
#endif
level = E_WARN;
/* loop over themarr */
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;
opts = copyopts(opts0, GROUP_ALL);
switch(result){
break;
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);
continue;
}
#endif
default:
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);
return result;
freeopts(opts);
continue;
}
}
xiofreeaddrinfo(themarr);
applyopts(sfd, -1, opts, PH_ALL);
if ((result = _xio_openlate(sfd, opts)) < 0)
#endif /* WITH_RETRY */
/* FALLTHROUGH */
case STAT_NORETRY:
if (bindarr != NULL) xiofreeaddrinfo(bindarr);
xiofreeaddrinfo(themarr);
freeopts(opts);
freeopts(opts0);
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;
}
@ -622,11 +662,16 @@ static int xioopen_socks5(
case STAT_RETRYLATER:
case STAT_RETRYNOW:
if ( sfd->forever || sfd->retry-- ) {
if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL);
if (result == STAT_RETRYLATER)
Nanosleep(&sfd->intervall, NULL);
freeopts(opts);
continue;
}
#endif
#endif /* WITH_RETRY */
/* FALLTHROUGH */
default:
freeopts(opts);
freeopts(opts0);
return result;
}
@ -642,12 +687,18 @@ static int xioopen_socks5(
level = E_WARN;
}
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);
freeopts(opts);
continue;
}
freeopts(opts);
freeopts(opts0);
return STAT_RETRYLATER;
}
if ( pid == 0 ) {
sfd->forever = false;
sfd->retry = 0;
@ -656,17 +707,26 @@ static int xioopen_socks5(
Close(sfd->fd);
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;
} else
#endif
#endif /* WITH_RETRY */
{
break;
}
} 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 */

View file

@ -2912,7 +2912,6 @@ int showleft(const struct opt *opts) {
return 0;
}
/* determines the address group from mode_t */
/* does not set GROUP_FD; cannot determine GROUP_TERMIOS ! */
groups_t _groupbits(mode_t mode) {
@ -4690,3 +4689,11 @@ int dumpopts(struct opt *opts)
}
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 dropopts2(struct opt *opts, unsigned int from, unsigned int to);
extern int dumpopts(struct opt *opts);
extern void freeopts(struct opt *opts);
#if HAVE_BASIC_UID_T==1
# define retropt_uid(o,c,r) retropt_short(o,c,r)