/* * Copyright (C) 2000-2003 the xine project, * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * abortable i/o helper functions */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include "io_helper.h" /* private constants */ #define XIO_FILE_READ 0 #define XIO_FILE_WRITE 1 #define XIO_TCP_READ 2 #define XIO_TCP_WRITE 3 #define XIO_POLLING_INTERVAL 50000 /* usec */ int xio_tcp_connect_ipv4(xine_stream_t *stream, const char *host, int port) { struct hostent *h; int i, s; h = gethostbyname(host); if (h == NULL) { xine_message(stream, XINE_MSG_UNKNOWN_HOST, "unable to resolve", host, NULL); return -1; } s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (s == -1) { xine_message(stream, XINE_MSG_CONNECTION_REFUSED, "failed to create socket", strerror(errno), NULL); return -1; } #ifndef _MSC_VER if (fcntl (s, F_SETFL, fcntl (s, F_GETFL) | O_NONBLOCK) == -1) { xine_message(stream, XINE_MSG_CONNECTION_REFUSED, "can't put socket in non-blocking mode", strerror(errno), NULL); return -1; } #else { int non_block = 1; int rc; rc = ioctlsocket(s, FIONBIO, &non_block); if (rc == SOCKET_ERROR) { xine_message(stream, XINE_MSG_CONNECTION_REFUSED, "can't put socket in non-blocking mode", strerror(errno), NULL); return -1; } } #endif for (i = 0; h->h_addr_list[i]; i++) { struct in_addr ia; struct sockaddr_in sin; memcpy (&ia, h->h_addr_list[i], 4); sin.sin_family = AF_INET; sin.sin_addr = ia; sin.sin_port = htons(port); #ifndef WIN32 if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 && errno != EINPROGRESS) { #else if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 && WSAGetLastError() != WSAEWOULDBLOCK) { printf("io_helper: WSAGetLastError() = %d\n", WSAGetLastError()); #endif /* WIN32 */ xine_message(stream, XINE_MSG_CONNECTION_REFUSED, strerror(errno), NULL); close(s); continue; } return s; } return -1; } int xio_tcp_connect(xine_stream_t *stream, const char *host, int port) { #ifndef ENABLE_IPV6 return xio_tcp_connect_ipv4(stream, host, port); #else int s; struct addrinfo hints, *res, *tmpaddr; int error; char strport[16]; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_family = PF_UNSPEC; snprintf(strport, sizeof(strport), "%d", port); printf("Resolving host '%s' at port '%s'\n", host, strport); error = getaddrinfo(host, strport, &hints, &res); if (error) { xine_message(stream, XINE_MSG_UNKNOWN_HOST, "unable to resolve", host, NULL); return -1; } tmpaddr = res; while (tmpaddr) { s = socket(tmpaddr->ai_family, SOCK_STREAM, IPPROTO_TCP); if (s == -1) { xine_message(stream, XINE_MSG_CONNECTION_REFUSED, "failed to create socket", strerror(errno), NULL); tmpaddr = tmpaddr->ai_next; continue; } /** * Uncommenting nonblocking features due to IPv6 support. * Need to know if the connect failed, in order to try another * address (if available). Error will be reported if no address * worked. */ #ifndef WIN32 if (connect(s, tmpaddr->ai_addr, tmpaddr->ai_addrlen)==-1 && errno != EINPROGRESS) { #else if (connect(s, tmpaddr->ai_addr, tmpaddr->ai_addrlen)==-1 && WSAGetLastError() != WSAEWOULDBLOCK) { printf("io_helper: WSAGetLastError() = %d\n", WSAGetLastError()); #endif /* WIN32 */ error = errno; close(s); tmpaddr = tmpaddr->ai_next; continue; } else { return s; } tmpaddr = tmpaddr->ai_next; } xine_message(stream, XINE_MSG_CONNECTION_REFUSED, strerror(error), NULL); return -1; #endif } int xio_select (xine_stream_t *stream, int fd, int state, int timeout_msec) { fd_set fdset; fd_set *rset, *wset; struct timeval select_timeout; int timeout_usec, total_time_usec; int ret; timeout_usec = 1000 * timeout_msec; total_time_usec = 0; while (total_time_usec < timeout_usec) { FD_ZERO (&fdset); FD_SET (fd, &fdset); select_timeout.tv_sec = 0; select_timeout.tv_usec = XIO_POLLING_INTERVAL; rset = (state & XIO_READ_READY) ? &fdset : NULL; wset = (state & XIO_WRITE_READY) ? &fdset : NULL; ret = select (fd + 1, rset, wset, NULL, &select_timeout); if (ret == -1) { /* select error */ return XIO_ERROR; } else if (ret == 1) { /* fd is ready */ return XIO_READY; } /* select timeout * aborts current read if action pending. otherwise xine * cannot be stopped when no more data is available. */ if (stream->demux_action_pending) return XIO_ABORTED; total_time_usec += XIO_POLLING_INTERVAL; } return XIO_TIMEOUT; } static off_t xio_rw_abort(xine_stream_t *stream, int fd, int cmd, char *buf, off_t todo) { off_t ret = -1; off_t total = 0; int sret; int state = 0; if ((cmd == XIO_TCP_READ) || (cmd == XIO_FILE_READ)) { state = XIO_READ_READY; } else { state = XIO_WRITE_READY; } while (total < todo) { do { sret = xio_select(stream, fd, state, 500); /* 500 ms */ } while (sret == XIO_TIMEOUT); if (sret != XIO_READY) return -1; switch (cmd) { case XIO_FILE_READ: ret = read(fd, &buf[total], todo - total); break; case XIO_FILE_WRITE: ret = write(fd, &buf[total], todo - total); break; case XIO_TCP_READ: ret = recv(fd, &buf[total], todo - total, 0); break; case XIO_TCP_WRITE: ret = send(fd, &buf[total], todo - total, 0); break; default: assert(1); } /* check EOF */ if (!ret) break; /* check errors */ if (ret < 0) { /* non-blocking mode */ #ifndef WIN32 if (errno == EAGAIN) continue; perror("io_helper: I/O error"); #else if (WSAGetLastError() == WSAEWOULDBLOCK) continue; printf("io_helper: WSAGetLastError() = %d\n", WSAGetLastError()); #endif return ret; } total += ret; } return total; } off_t xio_tcp_read (xine_stream_t *stream, int s, char *buf, off_t todo) { return xio_rw_abort (stream, s, XIO_TCP_READ, buf, todo); } off_t xio_tcp_write (xine_stream_t *stream, int s, char *buf, off_t todo) { return xio_rw_abort (stream, s, XIO_TCP_WRITE, buf, todo); } off_t xio_file_read (xine_stream_t *stream, int s, char *buf, off_t todo) { return xio_rw_abort (stream, s, XIO_FILE_READ, buf, todo); } off_t xio_file_write (xine_stream_t *stream, int s, char *buf, off_t todo) { return xio_rw_abort (stream, s, XIO_FILE_WRITE, buf, todo); }