mirror of
https://repo.or.cz/socat.git
synced 2025-07-19 09:22:57 +00:00
Reworked IPAPP clients
This commit is contained in:
parent
63f67101f4
commit
7b26406d96
16 changed files with 1660 additions and 649 deletions
427
xio-ipapp.c
427
xio-ipapp.c
|
@ -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:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue