1
0
Fork 0
mirror of https://repo.or.cz/socat.git synced 2025-07-26 20:12:56 +00:00

merged features ancillary, envvar

This commit is contained in:
Gerhard Rieger 2008-09-22 22:17:55 +02:00
parent bd3810642b
commit 2ffe5a324e
42 changed files with 1898 additions and 287 deletions

View file

@ -9,8 +9,10 @@
#if _WITH_SOCKET
#include "xioopen.h"
#include "xio-ascii.h"
#include "xio-socket.h"
#include "xio-named.h"
#include "xio-unix.h"
#if WITH_IP4
#include "xio-ip4.h"
#endif /* WITH_IP4 */
@ -21,6 +23,15 @@
#include "xio-ipapp.h" /*! not clean */
#include "xio-tcpwrap.h"
static int
xiolog_ancillary_socket(struct cmsghdr *cmsg, int *num,
char *typbuff, int typlen,
char *nambuff, int namlen,
char *envbuff, int envlen,
char *valbuff, int vallen);
const struct optdesc opt_so_debug = { "so-debug", "debug", OPT_SO_DEBUG, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_DEBUG };
#ifdef SO_ACCEPTCONN /* AIX433 */
const struct optdesc opt_so_acceptconn={ "so-acceptconn","acceptconn",OPT_SO_ACCEPTCONN,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_ACCEPTCONN};
@ -73,6 +84,9 @@ const struct optdesc opt_so_bsdcompat= { "so-bsdcompat","bsdcompat",OPT_SO_BSDCO
#ifdef SO_CKSUMRECV
const struct optdesc opt_so_cksumrecv= { "so-cksumrecv","cksumrecv",OPT_SO_CKSUMRECV,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_CKSUMRECV };
#endif /* SO_CKSUMRECV */
#ifdef SO_TIMESTAMP
const struct optdesc opt_so_timestamp= { "so-timestamp","timestamp",OPT_SO_TIMESTAMP,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_TIMESTAMP };
#endif
#ifdef SO_KERNACCEPT /* AIX 4.3.3 */
const struct optdesc opt_so_kernaccept={ "so-kernaccept","kernaccept",OPT_SO_KERNACCEPT,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_KERNACCEPT};
#endif /* SO_KERNACCEPT */
@ -410,30 +424,26 @@ int xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen,
#if WITH_RETRY
if (dofork) {
pid_t pid;
while ((pid = Fork()) < 0) {
int level = E_ERROR;
if (xfd->forever || --xfd->retry) {
level = E_WARN; /* most users won't expect a problem here,
int level = E_ERROR;
if (xfd->forever || xfd->retry) {
level = E_WARN; /* most users won't expect a problem here,
so Notice is too weak */
}
Msg1(level, "fork(): %s", strerror(errno));
}
while ((pid = xio_fork(false, level)) < 0) {
--xfd->retry;
if (xfd->forever || xfd->retry) {
dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
Nanosleep(&xfd->intervall, NULL); continue;
}
return STAT_RETRYLATER;
}
if (pid == 0) { /* child process */
Info1("just born: TCP client process "F_pid, Getpid());
/* drop parents locks, reset FIPS... */
if (xio_forked_inchild() != 0) {
Exit(1);
}
if (pid == 0) { /* child process */
break;
}
/* parent process */
Notice1("forked off child process "F_pid, pid);
Close(xfd->fd);
/* with and without retry */
Nanosleep(&xfd->intervall, NULL);
@ -720,6 +730,8 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
union sockaddr_union *pa = &_peername; /* peer address */
union sockaddr_union *la = &_sockname; /* local address */
socklen_t palen = sizeof(_peername); /* peer address size */
char ctrlbuff[1024]; /* ancillary messages */
struct msghdr msgh = {0};
socket_init(pf, pa);
salen = sizeof(struct sockaddr);
@ -755,14 +767,25 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
return STAT_RETRYLATER;
} while (true);
if (xiogetpacketsrc(xfd->fd, pa, &palen) < 0) {
msgh.msg_name = pa;
msgh.msg_namelen = palen;
#if HAVE_STRUCT_MSGHDR_MSGCONTROL
msgh.msg_control = ctrlbuff;
#endif
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
msgh.msg_controllen = sizeof(ctrlbuff);
#endif
if (xiogetpacketsrc(xfd->fd, &msgh) < 0) {
return STAT_RETRYLATER;
}
palen = msgh.msg_namelen;
Notice1("receiving packet from %s"/*"src"*/,
sockaddr_info((struct sockaddr *)pa, palen, peername, sizeof(peername))/*,
sockaddr_info(&la->soa, sockname, sizeof(sockname))*/);
xiodopacketinfo(&msgh, true, true);
if (xiocheckpeer(xfd, pa, la) < 0) {
/* drop packet */
char buff[512];
@ -773,6 +796,10 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
sockaddr_info((struct sockaddr *)pa, palen,
infobuff, sizeof(infobuff)));
/* set the env vars describing the local and remote sockets */
/*xiosetsockaddrenv("SOCK", la, lalen, proto);*/
xiosetsockaddrenv("PEER", pa, palen, proto);
applyopts(xfd->fd, opts, PH_FD);
applyopts(xfd->fd, opts, PH_CONNECTED);
@ -782,8 +809,6 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
if (dofork) {
sigset_t mask_sigchldusr1;
const char *forkwaitstring;
int forkwaitsecs = 0;
/* we must prevent that the current packet triggers another fork;
therefore we wait for a signal from the recent child: USR1
@ -795,17 +820,11 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
sigaddset(&mask_sigchldusr1, SIGUSR1);
Sigprocmask(SIG_BLOCK, &mask_sigchldusr1, NULL);
if ((pid = Fork()) < 0) {
Msg1(level, "fork(): %s", strerror(errno));
if ((pid = xio_fork(false, level)) < 0) {
Close(xfd->fd);
Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL);
return STAT_RETRYLATER;
}
/* gdb recommends to have env controlled sleep after fork */
if (forkwaitstring = getenv("SOCAT_FORK_WAIT")) {
forkwaitsecs = atoi(forkwaitstring);
Sleep(forkwaitsecs);
}
if (pid == 0) { /* child */
/* no reason to block SIGCHLD in child process */
@ -820,11 +839,6 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
level = E_ERROR;
#endif /* WITH_RETRY */
/* drop parents locks, reset FIPS... */
if (xio_forked_inchild() != 0) {
Exit(1);
}
#if WITH_UNIX
/* with UNIX sockets: only listening parent is allowed to remove
the socket file */
@ -835,8 +849,6 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
}
/* server: continue loop with listen */
Notice1("forked off child process "F_pid, pid);
xio_waitingfor = pid;
/* now we are ready to handle signals */
Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL);
@ -964,65 +976,120 @@ int retropt_socket_pf(struct opt *opts, int *pf) {
}
int xiogetpacketsrc(int fd, union sockaddr_union *pa, socklen_t *palen) {
char infobuff[256];
/* this function calls recvmsg(..., MSG_PEEK, ...) to obtain information about
the arriving packet. in msgh the msg_name pointer must refer to an (empty)
sockaddr storage. */
int xiogetpacketsrc(int fd, struct msghdr *msgh) {
char peekbuff[1];
#if 0
struct msghdr msgh = {0};
#if HAVE_STRUCT_IOVEC
struct iovec iovec;
#endif
char ctrlbuff[5120];
msgh.msg_name = pa;
msgh.msg_namelen = *palen;
#if HAVE_STRUCT_IOVEC
iovec.iov_base = peekbuff;
iovec.iov_len = sizeof(peekbuff);
msgh.msg_iov = &iovec;
msgh.msg_iovlen = 1;
#endif
#if HAVE_STRUCT_MSGHDR_MSGCONTROL
msgh.msg_control = ctrlbuff;
#endif
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
msgh.msg_controllen = sizeof(ctrlbuff);
msgh->msg_iov = &iovec;
msgh->msg_iovlen = 1;
#endif
#if HAVE_STRUCT_MSGHDR_MSGFLAGS
msgh.msg_flags = 0;
msgh->msg_flags = 0;
#endif
if (Recvmsg(fd, &msgh, MSG_PEEK
if (Recvmsg(fd, msgh, MSG_PEEK
#ifdef MSG_TRUNC
|MSG_TRUNC
|MSG_TRUNC
#endif
) < 0) {
Notice1("packet from %s",
sockaddr_info(&pa->soa, infobuff, sizeof(infobuff)));
Warn1("recvmsg(): %s", strerror(errno));
return STAT_RETRYLATER;
}
*palen = msgh.msg_namelen;
return STAT_OK;
}
#else
if (Recvfrom(fd, peekbuff, sizeof(peekbuff), MSG_PEEK
#ifdef MSG_TRUNC
|MSG_TRUNC
#endif
,
&pa->soa, palen) < 0) {
Notice1("packet from %s",
sockaddr_info(&pa->soa, *palen, infobuff, sizeof(infobuff)));
Warn1("recvfrom(): %s", strerror(errno));
return STAT_RETRYLATER;
/* works through the ancillary messages found in the given socket header record
and logs the relevant information (E_DEBUG, E_INFO).
calls protocol/layer specific functions for handling the messages
creates appropriate environment vars if withenv is set */
int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv) {
#if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)
struct cmsghdr *cmsg;
/* parse ancillary messages */
cmsg = CMSG_FIRSTHDR(msgh);
while (cmsg != NULL) {
int num = 0; /* number of data components of a ancill.msg */
int i;
char typbuff[16], *typp;
char nambuff[128], *namp;
char valbuff[256], *valp;
char envbuff[256], *envp;
if (withlog) {
xiodump(CMSG_DATA(cmsg),
cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg),
valbuff, sizeof(valbuff)-1, 0);
Debug4("ancillary message: len="F_socklen", level=%d, type=%d, data=%s",
cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type,
valbuff);
}
/* try to get the anc.msg. contents in handy components, protocol/level
dependent */
switch (cmsg->cmsg_level) {
case SOL_SOCKET:
xiolog_ancillary_socket(cmsg, &num, typbuff, sizeof(typbuff)-1,
nambuff, sizeof(nambuff)-1,
envbuff, sizeof(envbuff)-1,
valbuff, sizeof(valbuff)-1);
break;
case SOL_IP:
xiolog_ancillary_ip(cmsg, &num, typbuff, sizeof(typbuff)-1,
nambuff, sizeof(nambuff)-1,
envbuff, sizeof(envbuff)-1,
valbuff, sizeof(valbuff)-1);
break;
case SOL_IPV6:
xiolog_ancillary_ip6(cmsg, &num, typbuff, sizeof(typbuff)-1,
nambuff, sizeof(nambuff)-1,
envbuff, sizeof(envbuff)-1,
valbuff, sizeof(valbuff)-1);
break;
default:
num = 1;
snprintf(typbuff, sizeof(typbuff)-1, "LEVEL%u", cmsg->cmsg_level);
snprintf(nambuff, sizeof(nambuff)-1, "type%u", cmsg->cmsg_type);
xiodump(CMSG_DATA(cmsg),
cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg),
valbuff, sizeof(valbuff)-1, 0);
}
/* here the info is in typbuff (one string), nambuff (num consecutive
strings), and valbuff (num consecutive strings) */
i = 0;
typp = typbuff; namp = nambuff; envp = envbuff; valp = valbuff;
while (i < num) {
if (withlog) {
Info3("ancillary message: %s: %s=%s", typp, namp, valp);
}
if (withenv) {
if (*envp) {
xiosetenv(envp, valp, 1);
} else if (!strcasecmp(typp+strlen(typp)-strlen(namp), namp)) {
xiosetenv(typp, valp, 1);
} else {
xiosetenv2(typp, namp, valp, 1);
}
}
if (++i == num) break;
namp = strchr(namp, '\0')+1;
envp = strchr(envp, '\0')+1;
valp = strchr(valp, '\0')+1;
}
cmsg = CMSG_NXTHDR(msgh, cmsg);
}
return STAT_OK;
#endif
return 0;
#else /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */
return -1;
#endif /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */
}
@ -1049,6 +1116,7 @@ int xiocheckpeer(xiosingle_t *xfd,
#if WITH_IP4
if (xfd->para.socket.dorange) {
if (pa == NULL) { return -1; }
if (xiocheckrange(pa, &xfd->para.socket.range) < 0) {
char infobuff[256];
Warn1("refusing connection from %s due to range option",
@ -1064,6 +1132,7 @@ int xiocheckpeer(xiosingle_t *xfd,
#if WITH_TCP || WITH_UDP
if (xfd->para.socket.ip.dosourceport) {
if (pa == NULL) { return -1; }
#if WITH_IP4
if (pa->soa.sa_family == AF_INET &&
ntohs(((struct sockaddr_in *)pa)->sin_port) != xfd->para.socket.ip.sourceport) {
@ -1086,6 +1155,7 @@ int xiocheckpeer(xiosingle_t *xfd,
sockaddr_info((struct sockaddr *)pa, 0,
infobuff, sizeof(infobuff)));
} else if (xfd->para.socket.ip.lowport) {
if (pa == NULL) { return -1; }
if (pa->soa.sa_family == AF_INET &&
ntohs(((struct sockaddr_in *)pa)->sin_port) >= IPPORT_RESERVED) {
Warn1("refusing connection from %s due to lowport option",
@ -1127,4 +1197,177 @@ int xiocheckpeer(xiosingle_t *xfd,
return 0; /* permitted */
}
/* converts the ancillary message in *cmsg into a form useable for further
processing. knows the specifics of common message types.
returns the number of resulting syntax elements is *num
returns a sequence of \0 terminated type strings in *typbuff
returns a sequence of \0 terminated name strings in *nambuff
returns a sequence of \0 terminated value strings in *valbuff
the respective len parameters specify the available space in the buffers
returns STAT_OK
*/
static int
xiolog_ancillary_socket(struct cmsghdr *cmsg, int *num,
char *typbuff, int typlen,
char *nambuff, int namlen,
char *envbuff, int envlen,
char *valbuff, int vallen) {
const char *cmsgtype, *cmsgname, *cmsgenvn;
size_t msglen;
struct timeval *tv;
#if defined(CMSG_DATA)
msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg);
switch (cmsg->cmsg_type) {
#ifdef SO_PASSCRED
case SO_PASSCRED: /* this is really a UNIX/LOCAL message */
/*! needs implementation */
#endif /* SO_PASSCRED */
#ifdef SO_RIGHTS
case SO_RIGHTS: /* this is really a UNIX/LOCAL message */
/*! needs implementation */
#endif
default: /* binary data */
snprintf(typbuff, typlen, "SOCKET.%u", cmsg->cmsg_type);
strncpy(nambuff, "data", namlen);
xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
return STAT_OK;
#ifdef SO_TIMESTAMP
# ifdef SCM_TIMESTAMP
case SCM_TIMESTAMP:
# else
case SO_TIMESTAMP:
# endif
tv = (struct timeval *)CMSG_DATA(cmsg);
cmsgtype =
#ifdef SCM_TIMESTAMP
"SCM_TIMESTAMP" /* FreeBSD */
#else
"SO_TIMESTAMP" /* Linux */
#endif
;
cmsgname = "timestamp";
cmsgenvn = "TIMESTAMP";
{ time_t t = tv->tv_sec; ctime_r(&t, valbuff); }
sprintf(strchr(valbuff, '\0')-1/*del \n*/, ", %06ld usecs", tv->tv_usec);
break;
#endif /* defined(SO_TIMESTAMP) */
;
}
/* when we come here we provide a single parameter
with type in cmsgtype, name in cmsgname,
and value already in valbuff */
*num = 1;
if (strlen(cmsgtype) >= typlen) Fatal("buff too short");
strncpy(typbuff, cmsgtype, typlen);
if (strlen(cmsgname) >= namlen) Fatal("buff too short");
strncpy(nambuff, cmsgname, namlen);
if (strlen(cmsgenvn) >= envlen) Fatal("buff too short");
strncpy(envbuff, cmsgenvn, envlen);
return STAT_OK;
#else /* !defined(CMSG_DATA) */
return STAT_NORETRY;
#endif /* !defined(CMSG_DATA) */
}
/* return the name of the interface with given index
or NULL if is fails
The system call requires an arbitrary socket; the calling program may
provide one in parameter ins to avoid creation of a dummy socket. ins must
be <0 if it does not specify a socket fd. */
char *xiogetifname(int ind, char *val, int ins) {
#if 0
int s;
struct ifreq ifr;
if (ins >= 0) {
s = ins;
} else {
if ((s = Socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
Error1("socket(PF_INET, SOCK_DGRAM, IPPROTO_IP): %s", strerror(errno));
return NULL;
}
}
#if HAVE_STRUCT_IFREQ_IFR_INDEX
ifr.ifr_index = ind;
#elif HAVE_STRUCT_IFREQ_IFR_IFINDEX
ifr.ifr_ifindex = ind;
#endif
#ifdef SIOCGIFNAME
if(Ioctl(s, SIOCGIFNAME, &ifr) < 0) {
Info3("ioctl(%d, SIOCGIFNAME, {..., ifr_ifindex=%d, ...}: %s",
s, ifr.ifr_ifindex, strerror(errno));
if (ins < 0) Close(s);
return NULL;
}
#endif /* SIOCGIFNAME */
if (ins < 0) Close(s);
strcpy(val, ifr.ifr_name);
return val;
#else /* ! 0 */
return if_indextoname(ind, val);
#endif
}
/* set environment variables describing (part of) a socket address, e.g.
SOCAT_SOCKADDR. lr (local/remote) specifies a string like "SOCK" or "PEER".
proto should correspond to the third parameter of socket(2) and is used to
determine the presence of port information. */
int xiosetsockaddrenv(const char *lr,
union sockaddr_union *sau, socklen_t salen,
int proto) {
# define XIOSOCKADDRENVLEN 256
char namebuff[XIOSOCKADDRENVLEN];
char valuebuff[XIOSOCKADDRENVLEN];
int idx = 0, result;
strcpy(namebuff, lr);
switch (sau->soa.sa_family) {
case PF_UNIX:
result =
xiosetsockaddrenv_unix(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
valuebuff, XIOSOCKADDRENVLEN,
&sau->un, salen, proto);
xiosetenv(namebuff, valuebuff, 1);
break;
case PF_INET:
do {
result =
xiosetsockaddrenv_ip4(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
valuebuff, XIOSOCKADDRENVLEN,
&sau->ip4, proto);
xiosetenv(namebuff, valuebuff, 1);
namebuff[strlen(lr)] = '\0'; ++idx;
} while (result > 0);
break;
case PF_INET6:
strcpy(namebuff, lr);
do {
result =
xiosetsockaddrenv_ip6(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
valuebuff, XIOSOCKADDRENVLEN,
&sau->ip6, proto);
xiosetenv(namebuff, valuebuff, 1);
namebuff[strlen(lr)] = '\0'; ++idx;
} while (result > 0);
break;
#if LATER
case PF_PACKET:
result = xiosetsockaddrenv_packet(lr, (void *)sau, proto); break;
#endif
default:
result = -1;
break;
}
return result;
# undef XIOSOCKADDRENVLEN
}
#endif /* _WITH_SOCKET */