mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 15:32:35 +00:00
406 lines
12 KiB
C
406 lines
12 KiB
C
/* xiosocketpair.c */
|
|
/* Copyright Gerhard Rieger */
|
|
/* Published under the GNU General Public License V.2, see file COPYING */
|
|
|
|
/* this is the source of the internal xiosocketpair function */
|
|
|
|
#include "xiosysincludes.h"
|
|
#include "sycls.h"
|
|
#include "compat.h"
|
|
#include "error.h"
|
|
#include "xio.h"
|
|
|
|
|
|
#if defined(HAVE_DEV_PTMX)
|
|
# define PTMX "/dev/ptmx" /* Linux */
|
|
#elif HAVE_DEV_PTC
|
|
# define PTMX "/dev/ptc" /* AIX */
|
|
#endif
|
|
|
|
#define MAXPTYNAMELEN 64
|
|
|
|
int xiopty(int useptmx, int *ttyfdp, int *ptyfdp) {
|
|
int ttyfd, ptyfd = -1;
|
|
char ptyname[MAXPTYNAMELEN];
|
|
struct termios termarg;
|
|
|
|
if (useptmx) {
|
|
if ((ptyfd = Open(PTMX, O_RDWR|O_NOCTTY, 0620)) < 0) {
|
|
Warn1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620): %s",
|
|
strerror(errno));
|
|
/*!*/
|
|
} else {
|
|
;/*0 Info1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620) -> %d", ptyfd);*/
|
|
}
|
|
if (ptyfd >= 0) {
|
|
char *tn = NULL;
|
|
|
|
/* we used PTMX before forking */
|
|
/*0 extern char *ptsname(int);*/
|
|
#if HAVE_GRANTPT /* AIX, not Linux */
|
|
if (Grantpt(ptyfd)/*!*/ < 0) {
|
|
Warn2("grantpt(%d): %s", ptyfd, strerror(errno));
|
|
}
|
|
#endif /* HAVE_GRANTPT */
|
|
#if HAVE_UNLOCKPT
|
|
if (Unlockpt(ptyfd)/*!*/ < 0) {
|
|
Warn2("unlockpt(%d): %s", ptyfd, strerror(errno));
|
|
}
|
|
#endif /* HAVE_UNLOCKPT */
|
|
#if HAVE_PROTOTYPE_LIB_ptsname /* AIX, not Linux */
|
|
if ((tn = Ptsname(ptyfd)) == NULL) {
|
|
Warn2("ptsname(%d): %s", ptyfd, strerror(errno));
|
|
} else {
|
|
Notice1("PTY is %s", tn);
|
|
}
|
|
#endif /* HAVE_PROTOTYPE_LIB_ptsname */
|
|
#if 0
|
|
if (tn == NULL) {
|
|
/*! ttyname_r() */
|
|
if ((tn = Ttyname(ptyfd)) == NULL) {
|
|
Warn2("ttyname(%d): %s", ptyfd, strerror(errno));
|
|
}
|
|
}
|
|
ptyname[0] = '\0'; strncat(ptyname, tn, MAXPTYNAMELEN-1);
|
|
#endif
|
|
if ((ttyfd = Open(tn, O_RDWR|O_NOCTTY, 0620)) < 0) {
|
|
Warn2("open(\"%s\", O_RDWR|O_NOCTTY, 0620): %s", tn, strerror(errno));
|
|
} else {
|
|
/*0 Info2("open(\"%s\", O_RDWR|O_NOCTTY, 0620) -> %d", tn, ttyfd);*/
|
|
}
|
|
|
|
#ifdef I_PUSH
|
|
/* Linux: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> -1 EINVAL */
|
|
/* AIX: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 1 */
|
|
/* SunOS: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 0 */
|
|
/* HP-UX: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 0 */
|
|
if (Ioctl(ttyfd, I_FIND, "ldterm") == 0) {
|
|
Ioctl(ttyfd, I_PUSH, "ptem"); /* 0 */
|
|
Ioctl(ttyfd, I_PUSH, "ldterm"); /* 0 */
|
|
Ioctl(ttyfd, I_PUSH, "ttcompat"); /* HP-UX: -1 */
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#if HAVE_OPENPTY
|
|
if (ptyfd < 0) {
|
|
int result;
|
|
if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) {
|
|
Error4("openpty(%p, %p, %p, NULL, NULL): %s",
|
|
&ptyfd, &ttyfd, ptyname, strerror(errno));
|
|
return -1;
|
|
}
|
|
Notice1("PTY is %s", ptyname);
|
|
}
|
|
#endif /* HAVE_OPENPTY */
|
|
|
|
if (Tcgetattr(ttyfd, &termarg) < 0) {
|
|
Error3("tcgetattr(%d, %p): %s",
|
|
ttyfd, &termarg, strerror(errno));
|
|
}
|
|
#if 0
|
|
cfmakeraw(&termarg);
|
|
#else
|
|
/*!!! share code with xioopts.c raw,echo=0 */
|
|
termarg.c_iflag &=
|
|
~(IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXOFF
|
|
#ifdef IUCLC
|
|
|IUCLC
|
|
#endif
|
|
|IXANY|IMAXBEL);
|
|
termarg.c_iflag |= (0);
|
|
termarg.c_oflag &= ~(OPOST);
|
|
termarg.c_oflag |= (0);
|
|
termarg.c_cflag &= ~(0);
|
|
termarg.c_cflag |= (0);
|
|
termarg.c_lflag &= ~(ECHO|ECHONL|ISIG|ICANON
|
|
#ifdef XCASE
|
|
|XCASE
|
|
#endif
|
|
);
|
|
termarg.c_lflag |= (0);
|
|
termarg.c_cc[VMIN] = 1;
|
|
termarg.c_cc[VTIME] = 0;
|
|
#endif
|
|
if (Tcsetattr(ttyfd, TCSADRAIN, &termarg) < 0) {
|
|
Error3("tcsetattr(%d, TCSADRAIN, %p): %s",
|
|
ttyfd, &termarg, strerror(errno));
|
|
}
|
|
|
|
*ttyfdp = ttyfd;
|
|
*ptyfdp = ptyfd;
|
|
return 0;
|
|
}
|
|
|
|
/* generates a socket pair; supports not only PF_UNIX but also PF_INET */
|
|
int xiosocketpair2(int pf, int socktype, int protocol, int sv[2]) {
|
|
int result;
|
|
|
|
switch (pf) {
|
|
struct sockaddr_in ssin, csin, xsin; /* server, client, compare */
|
|
socklen_t cslen, xslen;
|
|
int sconn, slist, sserv; /* socket FDs */
|
|
|
|
case PF_UNIX:
|
|
result = Socketpair(pf, socktype, protocol, sv);
|
|
if (result < 0) {
|
|
Error5("socketpair(%d, %d, %d, %p): %s",
|
|
pf, socktype, protocol, sv, strerror(errno));
|
|
return -1;
|
|
}
|
|
break;
|
|
#if LATER
|
|
case PF_INET:
|
|
#if 1 /*!!! Linux */
|
|
ssin.sin_family = pf;
|
|
ssin.sin_port = htons(1024+random()%(65536-1024));
|
|
ssin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
#endif /* */
|
|
if ((s = Socket(pf, socktype, protocol)) < 0) {
|
|
Error4("socket(%d, %d, %d): %s",
|
|
pf, socktype, protocol, strerror(errno));
|
|
}
|
|
if (Bind(s, (struct sockaddr *)&ssin, sizeof(ssin)) < 0) {
|
|
Error6("bind(%d, {%u, 0x%x:%u}, "F_Zu"): %s",
|
|
s, ssin.sin_family, ssin.sin_addr.s_addr, ssin.sin_port,
|
|
sizeof(ssin), strerror(errno));
|
|
}
|
|
if (Connect(s, (struct sockaddr *)&ssin, sizeof(ssin)) < 0) {
|
|
Error6("connect(%d, {%u, 0x%x:%u}, "F_Zu"): %s",
|
|
s, ssin.sin_family, ssin.sin_addr.s_addr, ssin.sin_port,
|
|
sizeof(ssin), strerror(errno));
|
|
return -1;
|
|
}
|
|
break;
|
|
#endif /* LATER */
|
|
case PF_INET:
|
|
ssin.sin_family = pf;
|
|
ssin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
if ((slist = Socket(pf, socktype, protocol)) < 0) {
|
|
Error4("socket(%d, %d, %d): %s",
|
|
pf, socktype, protocol, strerror(errno));
|
|
}
|
|
while (true) { /* find a port we can bind to */
|
|
ssin.sin_port = htons(1024+random()%(65536-1024));
|
|
if (Bind(slist, (struct sockaddr *)&ssin, sizeof(ssin)) == 0) break;
|
|
if (errno == EADDRINUSE) {
|
|
Info6("bind(%d, {%u, 0x%x:%u}, "F_Zu"): %s",
|
|
slist, ssin.sin_family, ssin.sin_addr.s_addr,
|
|
ntohs(ssin.sin_port), sizeof(ssin), strerror(errno));
|
|
continue;
|
|
}
|
|
Error6("bind(%d, {%u, 0x%x:%u}, "F_Zu"): %s",
|
|
slist, ssin.sin_family, ssin.sin_addr.s_addr, ssin.sin_port,
|
|
sizeof(ssin), strerror(errno));
|
|
Close(slist);
|
|
return -1;
|
|
}
|
|
Listen(slist, 0);
|
|
if ((sconn = Socket(pf, socktype, protocol)) < 0) {
|
|
Error4("socket(%d, %d, %d): %s",
|
|
pf, socktype, protocol, strerror(errno));
|
|
Close(slist); return -1;
|
|
}
|
|
/* for testing race condition: Sleep(30); */
|
|
if (Connect(sconn, (struct sockaddr *)&ssin, sizeof(ssin)) < 0) {
|
|
Error6("connect(%d, {%u, 0x%x:%u}, "F_Zu"): %s",
|
|
sconn, ssin.sin_family, ssin.sin_addr.s_addr,
|
|
ntohs(ssin.sin_port),
|
|
sizeof(ssin), strerror(errno));
|
|
Close(slist); Close(sconn); return -1;
|
|
}
|
|
cslen = sizeof(csin);
|
|
if (Getsockname(sconn, (struct sockaddr *)&csin, &cslen) < 0) {
|
|
Error4("getsockname(%d, %p, %p): %s",
|
|
sconn, &csin, &cslen, strerror(errno));
|
|
Close(slist); Close(sconn); return -1;
|
|
}
|
|
do {
|
|
xslen = sizeof(xsin);
|
|
if ((sserv = Accept(slist, (struct sockaddr *)&xsin, &xslen)) < 0) {
|
|
Error4("accept(%d, %p, {"F_Zu"}): %s",
|
|
slist, &csin, sizeof(xslen), strerror(errno));
|
|
Close(slist); Close(sconn); return -1;
|
|
}
|
|
if (!memcmp(&csin, &xsin, cslen)) {
|
|
break; /* expected connection */
|
|
}
|
|
Warn4("unexpected connection to 0x%lx:%hu from 0x%lx:%hu",
|
|
ntohl(ssin.sin_addr.s_addr), ntohs(ssin.sin_port),
|
|
ntohl(xsin.sin_addr.s_addr), ntohs(xsin.sin_port));
|
|
} while (true);
|
|
Close(slist);
|
|
sv[0] = sconn;
|
|
sv[1] = sserv;
|
|
break;
|
|
default:
|
|
Error1("xiosocketpair2(): pf=%u not implemented", pf);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
dual should only be != 0 when both directions are used
|
|
returns 0 on success
|
|
*/
|
|
int xiocommpair(int commtype, bool lefttoright, bool righttoleft,
|
|
int dual, xiofd_t *left, xiofd_t *right, ...) {
|
|
va_list ap;
|
|
int domain = -1, socktype = -1, protocol = -1;
|
|
int useptmx = 0;
|
|
/* arrays can be used with pipe(2) and socketpair(2): */
|
|
int svlr[2] = {-1, -1}; /* left to right: rfd, wfd */
|
|
int svrl[2] = {-1, -1}; /* right to left: rfd, wfd */
|
|
|
|
/* get related parameters from parameter list */
|
|
switch (commtype) {
|
|
case XIOCOMM_SOCKETPAIR:
|
|
case XIOCOMM_SOCKETPAIRS:
|
|
va_start(ap, right);
|
|
domain = va_arg(ap, int);
|
|
socktype = va_arg(ap, int);
|
|
protocol = va_arg(ap, int);
|
|
va_end(ap);
|
|
break;
|
|
case XIOCOMM_PTY:
|
|
case XIOCOMM_PTYS:
|
|
va_start(ap, right);
|
|
useptmx = va_arg(ap, int);
|
|
va_end(ap);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (commtype) {
|
|
default: /* unspec */
|
|
Warn1("internal: undefined communication type %d, defaulting to 0",
|
|
commtype);
|
|
commtype = 0;
|
|
/*PASSTHROUGH*/
|
|
case XIOCOMM_SOCKETPAIRS: /* two socketpairs - the default */
|
|
if (lefttoright) {
|
|
if (Socketpair(domain, socktype, protocol, svlr) < 0) {
|
|
Error5("socketpair(%d, %d, %d, %p): %s",
|
|
domain, socktype, protocol, svlr, strerror(errno));
|
|
}
|
|
Shutdown(svlr[0], SHUT_WR);
|
|
}
|
|
if (righttoleft) {
|
|
if (Socketpair(domain, socktype, protocol, svrl) < 0) {
|
|
Error5("socketpair(%d, %d, %d, %p): %s",
|
|
domain, socktype, protocol, svrl, strerror(errno));
|
|
}
|
|
Shutdown(svrl[0], SHUT_WR);
|
|
}
|
|
left->single = right->single = false;
|
|
left->dtype = right->dtype = XIODATA_STREAM;
|
|
left->howtoshut = right->howtoshut = XIOSHUT_DOWN;
|
|
left->howtoclose = right->howtoclose = XIOCLOSE_CLOSE;
|
|
break;
|
|
|
|
case XIOCOMM_PTYS: /* two ptys in raw mode, EOF in canonical mode */
|
|
if (lefttoright) {
|
|
if (xiopty(useptmx, &svlr[0], &svlr[1]) < 0) return -1;
|
|
/* pty is write side, interpretes ^D in canonical mode */
|
|
}
|
|
if (righttoleft) {
|
|
if (xiopty(useptmx, &svrl[0], &svrl[1]) < 0) return -1;
|
|
}
|
|
left->single = right->single = false;
|
|
left->dtype = right->dtype = XIODATA_PTY;
|
|
left->howtoshut = right->howtoshut = XIOSHUT_PTYEOF;
|
|
left->howtoclose = right->howtoclose = XIOCLOSE_CLOSE;
|
|
break;
|
|
|
|
case XIOCOMM_SOCKETPAIR: /* one socketpair */
|
|
if (Socketpair(domain, socktype, protocol, svlr) < 0) {
|
|
Error5("socketpair(%d %d %d, %p): %s",
|
|
domain, socktype, protocol, svlr, strerror(errno));
|
|
return -1;
|
|
}
|
|
left->single = right->single = true;
|
|
left->dtype = right->dtype = XIODATA_STREAM;
|
|
left->howtoshut = right->howtoshut = XIOSHUT_DOWN;
|
|
left->howtoclose = right->howtoclose = XIOCLOSE_CLOSE;
|
|
break;
|
|
|
|
case XIOCOMM_PTY: /* one pty in raw mode, EOF in canonical mode */
|
|
if (xiopty(useptmx, &svlr[0], &svlr[1]) < 0) return -1;
|
|
left->single = right->single = true;
|
|
left->dtype = right->dtype = XIODATA_PTY;
|
|
left->howtoshut = XIOSHUT_PTYEOF;
|
|
right->howtoshut = XIOSHUT_CLOSE;
|
|
left->howtoclose = right->howtoclose = XIOCLOSE_CLOSE;
|
|
break;
|
|
|
|
case XIOCOMM_PIPES: /* two pipes */
|
|
if (lefttoright) {
|
|
if (Pipe(svlr) < 0) {
|
|
Error2("pipe(%p): %s", svlr, strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
if (righttoleft) {
|
|
if (Pipe(svrl) < 0) {
|
|
Error2("pipe(%p): %s", svrl, strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
left->single = right->single = false;
|
|
left->dtype = right->dtype = XIODATA_STREAM;
|
|
left->howtoshut = right->howtoshut = XIOSHUT_CLOSE;
|
|
left->howtoclose = right->howtoclose = XIOCLOSE_CLOSE;
|
|
break;
|
|
|
|
case XIOCOMM_TCP:
|
|
case XIOCOMM_TCP4: /* one TCP/IPv4 socket pair */
|
|
if (xiosocketpair2(PF_INET, SOCK_STREAM, 0, svlr) < 0) {
|
|
Error2("socketpair(PF_UNIX, PF_STREAM, 0, %p): %s",
|
|
svlr, strerror(errno));
|
|
return -1;
|
|
}
|
|
left->single = right->single = true;
|
|
left->dtype = right->dtype = XIODATA_STREAM;
|
|
left->howtoshut = right->howtoshut = XIOSHUT_DOWN;
|
|
left->howtoclose = right->howtoclose = XIOCLOSE_CLOSE;
|
|
break;
|
|
}
|
|
|
|
if (dual && left->single) {
|
|
/* one pair */
|
|
/* dual; we use different FDs for the channels to avoid conflicts
|
|
(happened in dual exec) */
|
|
if ((svrl[1] = Dup(svlr[0])) < 0) {
|
|
Error2("dup(%d): %s", svrl[0], strerror(errno));
|
|
return -1;
|
|
}
|
|
if ((svrl[0] = Dup(svlr[1])) < 0) {
|
|
Error2("dup(%d): %s", svlr[1], strerror(errno));
|
|
return -1;
|
|
}
|
|
} else if (left->single) {
|
|
svrl[1] = svlr[0];
|
|
svrl[0] = svlr[1];
|
|
}
|
|
|
|
/* usually they are not to be passed to exec'd child processes */
|
|
if (lefttoright) {
|
|
Fcntl_l(svlr[0], F_SETFD, 1);
|
|
Fcntl_l(svlr[1], F_SETFD, 1);
|
|
}
|
|
if (righttoleft && (!left->single || dual)) {
|
|
Fcntl_l(svrl[0], F_SETFD, 1);
|
|
Fcntl_l(svrl[1], F_SETFD, 1);
|
|
}
|
|
|
|
left->rfd = svrl[0];
|
|
left->wfd = svlr[1];
|
|
right->rfd = svlr[0];
|
|
right->wfd = svrl[1];
|
|
Notice4("xiocommpair() -> [%d:%d], [%d:%d]",
|
|
left->rfd, left->wfd, right->rfd, right->wfd);
|
|
return 0;
|
|
}
|
|
|