mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 23:42:34 +00:00
560 lines
17 KiB
C
560 lines
17 KiB
C
/* source: xioread.c */
|
|
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
|
|
/* Published under the GNU General Public License V.2, see file COPYING */
|
|
|
|
/* this is the source of the extended read function */
|
|
|
|
#include "xiosysincludes.h"
|
|
#include "xioopen.h"
|
|
|
|
#include "xio-termios.h"
|
|
#include "xio-socket.h"
|
|
#include "xio-readline.h"
|
|
#include "xio-openssl.h"
|
|
|
|
|
|
/* xioread() performs read() or recvfrom()
|
|
If result is < 0, errno is valid */
|
|
ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
|
|
ssize_t bytes;
|
|
#if WITH_IP6 && 0
|
|
int nexthead;
|
|
#endif
|
|
struct single *pipe;
|
|
int _errno;
|
|
|
|
if (file->tag == XIO_TAG_INVALID || file->tag & XIO_TAG_CLOSED) {
|
|
Error1("xioread(): invalid xiofile descriptor %p", file);
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (file->tag == XIO_TAG_DUAL) {
|
|
pipe = file->dual.stream[0];
|
|
if (pipe->tag == XIO_TAG_INVALID || file->tag & XIO_TAG_CLOSED) {
|
|
Error1("xioread(): invalid xiofile sub descriptor %p[0]", file);
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
} else {
|
|
pipe = &file->stream;
|
|
}
|
|
|
|
if (pipe->readbytes) {
|
|
if (pipe->actbytes == 0) {
|
|
Info1("xioread(%d, ...): readbytes consumed, inserting EOF", pipe->fd);
|
|
return 0; /* EOF by count */
|
|
}
|
|
|
|
if (pipe->actbytes < bufsiz) {
|
|
bufsiz = pipe->actbytes;
|
|
}
|
|
}
|
|
|
|
switch (pipe->dtype & XIODATA_READMASK) {
|
|
case XIOREAD_STREAM:
|
|
do {
|
|
bytes = Read(pipe->fd, buff, bufsiz);
|
|
} while (bytes < 0 && errno == EINTR);
|
|
if (bytes < 0) {
|
|
_errno = errno;
|
|
switch (_errno) {
|
|
case EPIPE:
|
|
case ECONNRESET:
|
|
/*PASSTHROUGH*/
|
|
default:
|
|
Error4("read(%d, %p, "F_Zu"): %s",
|
|
pipe->fd, buff, bufsiz, strerror(_errno));
|
|
}
|
|
errno = _errno;
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case XIOREAD_PTY:
|
|
{
|
|
int eio = 0;
|
|
bool m = false; /* loop message printed? */
|
|
while (true) {
|
|
do {
|
|
bytes = Read(pipe->fd, buff, bufsiz);
|
|
} while (bytes < 0 && errno == EINTR);
|
|
if (bytes < 0) {
|
|
_errno = errno;
|
|
if (_errno != EIO) {
|
|
Error4("read(%d, %p, "F_Zu"): %s", pipe->fd, buff, bufsiz, strerror(_errno));
|
|
errno = _errno;
|
|
return -1;
|
|
}
|
|
if (pipe->para.exec.sitout_eio.tv_sec == 0 &&
|
|
pipe->para.exec.sitout_eio.tv_usec == 0) {
|
|
Notice4("read(%d, %p, "F_Zu"): %s (probably PTY closed)",
|
|
pipe->fd, buff, bufsiz, strerror(_errno));
|
|
return 0;
|
|
}
|
|
if (!m) {
|
|
/* Starting first iteration: calc and report */
|
|
/* Round up to 10ms */
|
|
eio = 100*pipe->para.exec.sitout_eio.tv_sec +
|
|
(pipe->para.exec.sitout_eio.tv_usec+9999)/10000;
|
|
Notice3("xioread(fd=%d): EIO, sitting out %u.%02lus for recovery", pipe->fd, eio/100, (unsigned long)eio%100);
|
|
m = true;
|
|
}
|
|
poll(NULL, 0, 10);
|
|
if (--eio <= 0) {
|
|
/* Timeout */
|
|
Error4("read(%d, %p, "F_Zu"): %s", pipe->fd, buff, bufsiz, strerror(_errno));
|
|
errno = _errno;
|
|
return -1;
|
|
}
|
|
/* Not reached */
|
|
} else
|
|
break; /* no error */
|
|
}
|
|
}
|
|
return bytes;
|
|
break;
|
|
|
|
#if WITH_READLINE
|
|
case XIOREAD_READLINE:
|
|
if ((bytes = xioread_readline(pipe, buff, bufsiz)) < 0) {
|
|
return -1;
|
|
}
|
|
break;
|
|
#endif /* WITH_READLINE */
|
|
|
|
#if WITH_OPENSSL
|
|
case XIOREAD_OPENSSL:
|
|
/* this function prints its error messages */
|
|
if ((bytes = xioread_openssl(pipe, buff, bufsiz)) < 0) {
|
|
return -1;
|
|
}
|
|
break;
|
|
#endif /* WITH_OPENSSL */
|
|
|
|
#if _WITH_SOCKET
|
|
case XIOREAD_RECV:
|
|
if (pipe->dtype & XIOREAD_RECV_FROM) {
|
|
/* Receiving packets in addresses of RECVFROM types, the sender address
|
|
has already been determined in OPEN phase. */
|
|
Debug1("%s(): XIOREAD_RECV and XIOREAD_RECV_FROM (peer checks already done)",
|
|
__func__);
|
|
#if WITH_RAWIP || WITH_UDP || WITH_UNIX
|
|
struct msghdr msgh = {0};
|
|
union sockaddr_union from = {{0}};
|
|
socklen_t fromlen = sizeof(from);
|
|
char infobuff[256];
|
|
char ctrlbuff[1024]; /* ancillary messages */
|
|
int rc;
|
|
|
|
msgh.msg_name = &from;
|
|
msgh.msg_namelen = fromlen;
|
|
#if HAVE_STRUCT_MSGHDR_MSGCONTROL
|
|
msgh.msg_control = ctrlbuff;
|
|
#endif
|
|
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
|
|
msgh.msg_controllen = sizeof(ctrlbuff);
|
|
#endif
|
|
while ((rc = xiogetancillary(pipe->fd, &msgh,
|
|
MSG_PEEK
|
|
#ifdef MSG_TRUNC
|
|
|MSG_TRUNC
|
|
#endif
|
|
)) < 0 &&
|
|
errno == EINTR) ;
|
|
if (rc < 0) return -1;
|
|
|
|
/* Note: we do not call xiodopacketinfo() and xiocheckpeer() here because
|
|
that already happened in xioopen() / _xioopen_dgram_recvfrom() ... */
|
|
|
|
do {
|
|
bytes =
|
|
Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen);
|
|
} while (bytes < 0 && errno == EINTR);
|
|
if (bytes < 0) {
|
|
char infobuff[256];
|
|
_errno = errno;
|
|
Error6("recvfrom(%d, %p, "F_Zu", 0, %s, {"F_socklen"}): %s",
|
|
pipe->fd, buff, bufsiz,
|
|
sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff)),
|
|
fromlen, strerror(errno));
|
|
errno = _errno;
|
|
return -1;
|
|
}
|
|
|
|
#if defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING)
|
|
/* In future versions there may be an option that controls receiving of
|
|
outgoing packets, but currently it is hardcoded that we try to avoid
|
|
them - either by once setting socket option PACKET_IGNORE_OUTGOING
|
|
when available, otherwise by checking flag PACKET_OUTGOING per packet.
|
|
*/
|
|
if (from.soa.sa_family == PF_PACKET) {
|
|
if ((from.ll.sll_pkttype & PACKET_OUTGOING) != 0) {
|
|
Info2("%s(fd=%d): ignoring outgoing packet", __func__, pipe->fd);
|
|
errno = EAGAIN;
|
|
return -1;
|
|
}
|
|
Debug2("%s(fd=%d): packet is not outgoing - process it", __func__, pipe->fd);
|
|
}
|
|
#endif /* defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING) */
|
|
|
|
#if defined(PF_PACKET) && HAVE_STRUCT_TPACKET_AUXDATA
|
|
if (from.soa.sa_family == PF_PACKET) {
|
|
Debug3("xioread(FD=%d, ...): auxdata: flag=%d, vlan-id=%d",
|
|
pipe->fd, pipe->para.socket.ancill_flag.packet_auxdata,
|
|
pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci);
|
|
if (pipe->para.socket.ancill_flag.packet_auxdata &&
|
|
pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci != 0) {
|
|
int offs = 12; /* packet type id in Ethernet header */
|
|
Debug1("xioread(%d, ...): restoring VLAN id from auxdata->tp_vlan_tci",
|
|
pipe->fd);
|
|
if (bytes+4 > bufsiz) {
|
|
Error("buffer too small to restore VLAN id");
|
|
}
|
|
memmove((char *)buff+offs+4, (char *)buff+offs, bytes-offs);
|
|
((unsigned short *)((char *)buff+offs))[0] = htons(ETH_P_8021Q);
|
|
((unsigned short *)((char *)buff+offs))[1] =
|
|
htons(pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci);
|
|
bytes += 4;
|
|
}
|
|
}
|
|
#endif /* defined(PF_PACKET && HAVE_STRUCT_TPACKET_AUXDATA */
|
|
|
|
Notice2("received packet with "F_Zu" bytes from %s",
|
|
bytes,
|
|
sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff)));
|
|
if (bytes == 0) {
|
|
if (!pipe->para.socket.null_eof) {
|
|
errno = EAGAIN; return -1;
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
if (pipe->peersa.soa.sa_family != PF_UNSPEC) {
|
|
/* a peer address is registered, so we need to check if it matches */
|
|
#if 0 /* with UNIX sockets we find inconsistent lengths */
|
|
if (fromlen != pipe->salen) {
|
|
Info("recvfrom(): wrong peer address length, ignoring packet");
|
|
errno = EAGAIN; return -1;
|
|
}
|
|
#endif
|
|
if (pipe->dtype & XIOREAD_RECV_SKIPIP) {
|
|
if (pipe->peersa.soa.sa_family != from.soa.sa_family) {
|
|
Info("recvfrom(): wrong peer protocol, ignoring packet");
|
|
errno = EAGAIN; return -1;
|
|
}
|
|
#if WITH_IP4
|
|
switch (pipe->peersa.soa.sa_family) {
|
|
case PF_INET:
|
|
if (pipe->peersa.ip4.sin_addr.s_addr !=
|
|
from.ip4.sin_addr.s_addr) {
|
|
Info("recvfrom(): wrong peer address, ignoring packet");
|
|
errno = EAGAIN; return -1;
|
|
}
|
|
break;
|
|
}
|
|
#endif /* WITH_IP4 */
|
|
} else {
|
|
switch (pipe->peersa.soa.sa_family) {
|
|
#if 0
|
|
case PF_UNIX:
|
|
if (strncmp(pipe->peersa.un.sun_path, from.un.sun_path,
|
|
sizeof(from.un.sun_path))) {
|
|
Info("recvfrom(): wrong peer address, ignoring packet");
|
|
errno = EAGAIN; return -1;
|
|
}
|
|
break;
|
|
#endif
|
|
#if WITH_IP6
|
|
case PF_INET6:
|
|
/* e.g. Solaris recvfrom sets a __sin6_src_id component */
|
|
if (memcmp(&from.ip6.sin6_addr, &pipe->peersa.ip6.sin6_addr,
|
|
sizeof(from.ip6.sin6_addr)) ||
|
|
from.ip6.sin6_port != pipe->peersa.ip6.sin6_port) {
|
|
Info("recvfrom(): wrong peer address, ignoring packet");
|
|
errno = EAGAIN; return -1;
|
|
}
|
|
break;
|
|
#endif /* WITH_IP6 */
|
|
default:
|
|
if (memcmp(&from, &pipe->peersa, fromlen)) {
|
|
Info("recvfrom(): wrong peer address, ignoring packet");
|
|
errno = EAGAIN; return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
switch(from.soa.sa_family) {
|
|
#if HAVE_STRUCT_IP
|
|
int headlen;
|
|
#endif /* HAVE_STRUCT_IP */
|
|
#if WITH_IP4
|
|
case AF_INET:
|
|
#if HAVE_STRUCT_IP
|
|
if (pipe->dtype & XIOREAD_RECV_SKIPIP) {
|
|
/* IP4 raw sockets include the header when passing a packet to the
|
|
application - we don't need it here. */
|
|
#if HAVE_STRUCT_IP_IP_HL
|
|
headlen = 4*((struct ip *)buff)->ip_hl;
|
|
#else /* happened on Tru64 */
|
|
headlen = 4*((struct ip *)buff)->ip_vhl;
|
|
#endif
|
|
if (headlen > bytes) {
|
|
Warn1("xioread(%d, ...)/IP4: short packet", pipe->fd);
|
|
bytes = 0;
|
|
} else {
|
|
memmove(buff, ((char *)buff)+headlen, bytes-headlen);
|
|
bytes -= headlen;
|
|
}
|
|
}
|
|
#endif /* HAVE_STRUCT_IP */
|
|
break;
|
|
#endif
|
|
#if WITH_IP6
|
|
case AF_INET6:
|
|
/* does not seem to include header on Linux */
|
|
/* but sometimes on AIX */
|
|
break;
|
|
#endif
|
|
default:
|
|
/* do nothing, for now */
|
|
break;
|
|
}
|
|
if (pipe->dtype & XIOREAD_RECV_ONESHOT) {
|
|
#if 1
|
|
pipe->eof = 2;
|
|
#else
|
|
Shutdown(pipe->fd, SHUT_RD);
|
|
#endif
|
|
if (pipe->triggerfd >= 0) {
|
|
Info("notifying parent that socket is ready again");
|
|
Close(pipe->triggerfd);
|
|
pipe->triggerfd = -1;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
if (fromlen != pipe->fd[0].salen) {
|
|
Debug("recvfrom(): wrong peer address length, ignoring packet");
|
|
continue;
|
|
}
|
|
if (memcmp(&from, &pipe->fd[0].peersa.sa, fromlen)) {
|
|
Debug("recvfrom(): other peer address, ignoring packet");
|
|
Debug16("peer: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
|
pipe->fd[0].peersa.space[0],
|
|
pipe->fd[0].peersa.space[1],
|
|
pipe->fd[0].peersa.space[2],
|
|
pipe->fd[0].peersa.space[3],
|
|
pipe->fd[0].peersa.space[4],
|
|
pipe->fd[0].peersa.space[5],
|
|
pipe->fd[0].peersa.space[6],
|
|
pipe->fd[0].peersa.space[7],
|
|
pipe->fd[0].peersa.space[8],
|
|
pipe->fd[0].peersa.space[9],
|
|
pipe->fd[0].peersa.space[10],
|
|
pipe->fd[0].peersa.space[11],
|
|
pipe->fd[0].peersa.space[12],
|
|
pipe->fd[0].peersa.space[13],
|
|
pipe->fd[0].peersa.space[14],
|
|
pipe->fd[0].peersa.space[15]);
|
|
Debug16("from: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
|
from.space[0], from.space[1],
|
|
from.space[2], from.space[3],
|
|
from.space[4], from.space[5],
|
|
from.space[6], from.space[7],
|
|
from.space[8], from.space[9],
|
|
from.space[10], from.space[11],
|
|
from.space[12], from.space[13],
|
|
from.space[14], from.space[15]);
|
|
continue;
|
|
}
|
|
#endif
|
|
#else /* !(WITH_RAWIP || WITH_UDP || WITH_UNIX) */
|
|
Fatal("address requires raw sockets, but they are not compiled in");
|
|
return -1;
|
|
#endif /* !(WITH_RAWIP || WITH_UDP || WITH_UNIX) */
|
|
|
|
} else /* ~XIOREAD_RECV_FROM */ {
|
|
/* Receiving packets without planning to answer to the sender, but we
|
|
might need sender info for some checks, thus we use recvfrom() */
|
|
struct msghdr msgh = {0};
|
|
union sockaddr_union from = {{ 0 }};
|
|
socklen_t fromlen = sizeof(from);
|
|
char infobuff[256];
|
|
char ctrlbuff[1024]; /* ancillary messages */
|
|
int rc;
|
|
|
|
Debug1("%s(): XIOREAD_RECV and not XIOREAD_RECV_FROM (peer checks to be done)",
|
|
__func__);
|
|
|
|
/* get source address */
|
|
msgh.msg_name = &from;
|
|
msgh.msg_namelen = fromlen;
|
|
#if HAVE_STRUCT_MSGHDR_MSGCONTROL
|
|
msgh.msg_control = ctrlbuff;
|
|
#endif
|
|
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
|
|
msgh.msg_controllen = sizeof(ctrlbuff);
|
|
#endif
|
|
while ((rc = xiogetancillary(pipe->fd, &msgh,
|
|
MSG_PEEK
|
|
#ifdef MSG_TRUNC
|
|
|MSG_TRUNC
|
|
#endif
|
|
)) < 0 &&
|
|
errno == EINTR) ;
|
|
if (rc < 0) return -1;
|
|
|
|
xiodopacketinfo(pipe, &msgh, true, false);
|
|
if (xiocheckpeer(pipe, &from, &pipe->para.socket.la) < 0) {
|
|
Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen); /* drop */
|
|
errno = EAGAIN; return -1;
|
|
}
|
|
Info1("permitting packet from %s",
|
|
sockaddr_info((struct sockaddr *)&from, fromlen,
|
|
infobuff, sizeof(infobuff)));
|
|
|
|
do {
|
|
bytes =
|
|
Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen);
|
|
} while (bytes < 0 && errno == EINTR);
|
|
if (bytes < 0) {
|
|
char infobuff[256];
|
|
_errno = errno;
|
|
Error6("recvfrom(%d, %p, "F_Zu", 0, %s, "F_socklen"): %s",
|
|
pipe->fd, buff, bufsiz,
|
|
sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff)),
|
|
fromlen, strerror(errno));
|
|
errno = _errno;
|
|
return -1;
|
|
}
|
|
|
|
#if defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING)
|
|
/* For remarks see similar section above */
|
|
if (from.soa.sa_family == PF_PACKET) {
|
|
if ((from.ll.sll_pkttype & PACKET_OUTGOING) != 0) {
|
|
Info2("%s(fd=%d): ignoring outgoing packet", __func__, pipe->fd);
|
|
errno = EAGAIN;
|
|
return -1;
|
|
}
|
|
Debug2("%s(fd=%d): packet is not outgoing - process it", __func__, pipe->fd);
|
|
}
|
|
#endif /* defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING) */
|
|
|
|
#if defined(PF_PACKET) && HAVE_STRUCT_TPACKET_AUXDATA
|
|
if (from.soa.sa_family == PF_PACKET) {
|
|
Debug3("xioread(%d, ...): auxdata: flag=%d, vlan-id=%d",
|
|
pipe->fd, pipe->para.socket.ancill_flag.packet_auxdata,
|
|
pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci);
|
|
if (pipe->para.socket.ancill_flag.packet_auxdata &&
|
|
pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci &&
|
|
pipe->para.socket.ancill_data_packet_auxdata.tp_net >= 2) {
|
|
Debug2("xioread(%d, ...): restoring VLAN id %d", pipe->fd, pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci);
|
|
int offs = pipe->para.socket.ancill_data_packet_auxdata.tp_net - 2;
|
|
if (bytes+4 > bufsiz) {
|
|
Error("buffer too small to restore VLAN id");
|
|
}
|
|
Debug3("xioread(): memmove(%p, %p, "F_Zu")", (char *)buff+offs+4, (char *)buff+offs, bytes-offs);
|
|
memmove((char *)buff+offs+4, (char *)buff+offs, bytes-offs);
|
|
((unsigned short *)((char *)buff+offs))[0] = htons(ETH_P_8021Q);
|
|
((unsigned short *)((char *)buff+offs))[1] =
|
|
htons(pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci);
|
|
bytes += 4;
|
|
}
|
|
}
|
|
#endif /* defined(PF_PACKET) &&& HAVE_STRUCT_TPACKET_AUXDATA */
|
|
|
|
Notice2("received packet with "F_Zu" bytes from %s",
|
|
bytes,
|
|
sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff)));
|
|
|
|
if (bytes == 0) {
|
|
if (!pipe->para.socket.null_eof) {
|
|
errno = EAGAIN; return -1;
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
switch(from.soa.sa_family) {
|
|
#if HAVE_STRUCT_IP
|
|
int headlen;
|
|
#endif /* HAVE_STRUCT_IP */
|
|
#if WITH_IP4
|
|
case AF_INET:
|
|
#if HAVE_STRUCT_IP
|
|
if (pipe->dtype & XIOREAD_RECV_SKIPIP) {
|
|
/* IP4 raw sockets include the header when passing a packet to the
|
|
application - we don't need it here. */
|
|
#if HAVE_STRUCT_IP_IP_HL
|
|
headlen = 4*((struct ip *)buff)->ip_hl;
|
|
#else /* happened on Tru64 */
|
|
headlen = 4*((struct ip *)buff)->ip_vhl;
|
|
#endif
|
|
if (headlen > bytes) {
|
|
Warn1("xioread(%d, ...)/IP4: short packet", pipe->fd);
|
|
bytes = 0;
|
|
} else {
|
|
memmove(buff, ((char *)buff)+headlen, bytes-headlen);
|
|
bytes -= headlen;
|
|
}
|
|
}
|
|
#endif /* HAVE_STRUCT_IP */
|
|
break;
|
|
#endif
|
|
#if WITH_IP6
|
|
case AF_INET6: /* does not seem to include header... */
|
|
break;
|
|
#endif
|
|
default:
|
|
/* do nothing, for now */
|
|
break;
|
|
}
|
|
|
|
}
|
|
break;
|
|
#endif /* _WITH_SOCKET */
|
|
|
|
default:
|
|
Error("internal: undefined read operation");
|
|
errno = EINVAL; return -1;
|
|
}
|
|
pipe->actbytes -= bytes;
|
|
return bytes;
|
|
}
|
|
|
|
|
|
/* this function is intended only for some special address types where the
|
|
select()/poll() calls cannot strictly determine if (more) read data is
|
|
available. currently this is for the OpenSSL based addresses.
|
|
*/
|
|
ssize_t xiopending(xiofile_t *file) {
|
|
struct single *pipe;
|
|
|
|
if (file->tag == XIO_TAG_INVALID || file->tag & XIO_TAG_CLOSED) {
|
|
Error1("xiopending(): invalid xiofile descriptor %p", file);
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (file->tag == XIO_TAG_DUAL) {
|
|
pipe = file->dual.stream[0];
|
|
if (pipe->tag == XIO_TAG_INVALID || file->tag & XIO_TAG_CLOSED) {
|
|
Error1("xiopending(): invalid xiofile sub descriptor %p[0]", file);
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
} else {
|
|
pipe = &file->stream;
|
|
}
|
|
|
|
switch (pipe->dtype & XIODATA_READMASK) {
|
|
#if WITH_OPENSSL
|
|
case XIOREAD_OPENSSL:
|
|
return xiopending_openssl(pipe);
|
|
#endif /* WITH_OPENSSL */
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|