/* source: xioread.c */ /* Copyright Gerhard Rieger 2001-2012 */ /* 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-test.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 fd; int _errno; if (file->tag == XIO_TAG_INVALID) { 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) { Error1("xioread(): invalid xiofile sub descriptor %p[0]", file); errno = EINVAL; return -1; } } else { pipe = &file->stream; } if (pipe->readbytes) { if (pipe->actbytes == 0) { return 0; /* EOF by count */ } if (pipe->actbytes < bufsiz) { bufsiz = pipe->actbytes; } } fd = XIO_GETRDFD(file); switch (pipe->dtype & XIODATA_READMASK) { case XIOREAD_STREAM: do { bytes = Read(fd, buff, bufsiz); } while (bytes < 0 && errno == EINTR); if (bytes < 0) { _errno = errno; switch (_errno) { #if 1 case EPIPE: case ECONNRESET: Warn4("read(%d, %p, "F_Zu"): %s", fd, buff, bufsiz, strerror(_errno)); break; #endif default: Error4("read(%d, %p, "F_Zu"): %s", fd, buff, bufsiz, strerror(_errno)); } return -1; } break; case XIODATA_PTY: do { bytes = Read(fd, buff, bufsiz); } while (bytes < 0 && errno == EINTR); if (bytes < 0) { _errno = errno; if (_errno == EIO) { Notice4("read(%d, %p, "F_Zu"): %s (probably PTY closed)", fd, buff, bufsiz, strerror(_errno)); return 0; } else { Error4("read(%d, %p, "F_Zu"): %s", fd, buff, bufsiz, strerror(_errno)); } errno = _errno; return -1; } break; case XIOREAD_PTY: do { bytes = Read(fd, buff, bufsiz); } while (bytes < 0 && errno == EINTR); if (bytes < 0) { _errno = errno; if (_errno == EIO) { Notice4("read(%d, %p, "F_Zu"): %s (probably PTY closed)", fd, buff, bufsiz, strerror(_errno)); return 0; } else { Error4("read(%d, %p, "F_Zu"): %s", fd, buff, bufsiz, strerror(_errno)); } errno = _errno; return -1; } break; #if WITH_READLINE case XIOREAD_READLINE: if ((bytes = xioread_readline(pipe, buff, bufsiz)) < 0) { return -1; } break; #endif /* WITH_READLINE */ #if WITH_TEST case XIOREAD_TEST: /* this function prints its error messages */ if ((bytes = xioread_test(pipe, buff, bufsiz)) < 0) { return -1; } break; #endif /* WITH_TEST */ #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) { #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 */ 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 if (xiogetpacketsrc(pipe->rfd, &msgh) < 0) { return -1; } do { bytes = Recvfrom(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", fd, buff, bufsiz, sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff)), fromlen, strerror(errno)); errno = _errno; return -1; } /* on packet type we also receive outgoing packets, this is not desired */ #if defined(PF_PACKET) && defined(PACKET_OUTGOING) if (from.soa.sa_family == PF_PACKET) { if ((from.ll.sll_pkttype & PACKET_OUTGOING) == 0) { errno = EAGAIN; return -1; } } #endif /* defined(PF_PACKET) && defined(PACKET_OUTGOING) */ 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) { int headlen; #if WITH_IP4 case AF_INET: 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", fd); bytes = 0; } else { memmove(buff, ((char *)buff)+headlen, bytes-headlen); bytes -= headlen; } } 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->ppid > 0) { Kill(pipe->ppid, SIGUSR1); } } #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 */ Fatal("address requires raw sockets, but they are not compiled in"); return -1; #endif /* !WITH_RAWIP || WITH_UDP || WITH_UNIX */ } else /* ~XIOREAD_RECV_FROM */ { union sockaddr_union from; socklen_t fromlen = sizeof(from); char infobuff[256]; struct msghdr msgh = {0}; char ctrlbuff[1024]; /* ancillary messages */ socket_init(pipe->para.socket.la.soa.sa_family, &from); /* 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 if (xiogetpacketsrc(pipe->rfd, &msgh) < 0) { return -1; } xiodopacketinfo(&msgh, true, false); if (xiocheckpeer(pipe, &from, &pipe->para.socket.la) < 0) { Recvfrom(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(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", fd, buff, bufsiz, sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff)), fromlen, strerror(errno)); errno = _errno; return -1; } 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) { int headlen; #if WITH_IP4 case AF_INET: 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", fd); bytes = 0; } else { memmove(buff, ((char *)buff)+headlen, bytes-headlen); bytes -= headlen; } } 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) { 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) { 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; } }