diff --git a/README b/README index 290d7c0..796f436 100644 --- a/README +++ b/README @@ -164,6 +164,9 @@ Notes: - Source address validation can be enabled for UDP protocol separated by adding the source address after a ';' character: "U=239.192.0.1;239.192.0.2" +- Section id and pid scanners should be disabled after the correct data is + found. This can be made via VDR's channel editor. + Acknowledgements: - The IPTV section filtering code is derived from Linux kernel. diff --git a/protocolhttp.c b/protocolhttp.c index 7416ae0..39c2b79 100644 --- a/protocolhttp.c +++ b/protocolhttp.c @@ -19,7 +19,7 @@ #include "protocolhttp.h" cIptvProtocolHttp::cIptvProtocolHttp() -: streamAddr(NULL), +: streamAddr(strdup("")), streamPath(strdup("/")) { debug("cIptvProtocolHttp::cIptvProtocolHttp()\n"); @@ -39,49 +39,13 @@ bool cIptvProtocolHttp::Connect(void) { debug("cIptvProtocolHttp::Connect()\n"); // Check that stream address is valid - if (!isActive && !isempty(streamAddr) && !isempty(streamPath)) { - // First try only the IP address - sockAddr.sin_addr.s_addr = inet_addr(streamAddr); - - if (sockAddr.sin_addr.s_addr == INADDR_NONE) { - debug("Cannot convert %s directly to internet address\n", streamAddr); - - // It may be a host name, get the name - struct hostent *host; - host = gethostbyname(streamAddr); - if (!host) { - char tmp[64]; - error("%s is not valid address: %s", streamAddr, strerror_r(h_errno, tmp, sizeof(tmp))); - return false; - } - - sockAddr.sin_addr.s_addr = inet_addr(*host->h_addr_list); - } - - // Ensure that socket is valid - OpenSocket(socketPort); - - int err = connect(socketDesc, (struct sockaddr *)&sockAddr, sizeof(sockAddr)); - // Non-blocking sockets always report in-progress error when connected - ERROR_IF_FUNC(err < 0 && errno != EINPROGRESS, "connect()", CloseSocket(), return false); - // Select with 800ms timeout on the socket completion, check if it is writable - int retval = select_single_desc(socketDesc, 800000, true); - if (retval < 0) - return retval; - - // Select has returned. Get socket errors if there are any - int socketStatus = 0; - socklen_t len = sizeof(socketStatus); - getsockopt(socketDesc, SOL_SOCKET, SO_ERROR, &socketStatus, &len); - - // If not any errors, then socket must be ready and connected - if (socketStatus != 0) { - char tmp[64]; - error("Cannot connect to %s: %s", streamAddr, strerror_r(socketStatus, tmp, sizeof(tmp))); + if (!isempty(streamAddr) && !isempty(streamPath)) { + // Ensure that socket is valid and connect + OpenSocket(socketPort, streamAddr); + if (!ConnectSocket()) { CloseSocket(); return false; } - // Formulate and send HTTP request cString buffer = cString::sprintf("GET %s HTTP/1.1\r\n" "Host: %s\r\n" @@ -89,19 +53,17 @@ bool cIptvProtocolHttp::Connect(void) "Range: bytes=0-\r\n" "Connection: Close\r\n" "\r\n", streamPath, streamAddr, PLUGIN_NAME_I18N, VERSION); - debug("Sending http request: %s\n", *buffer); - ssize_t err2 = send(socketDesc, buffer, strlen(buffer), 0); - ERROR_IF_FUNC(err2 < 0, "send()", CloseSocket(), return false); + if (!Write(*buffer, (unsigned int)strlen(*buffer))) { + CloseSocket(); + return false; + } // Now process headers if (!ProcessHeaders()) { CloseSocket(); return false; } - - // Update active flag - isActive = true; } return true; } @@ -109,13 +71,8 @@ bool cIptvProtocolHttp::Connect(void) bool cIptvProtocolHttp::Disconnect(void) { debug("cIptvProtocolHttp::Disconnect()\n"); - // Check that stream address is valid - if (isActive) { - // Close the socket - CloseSocket(); - // Update active flag - isActive = false; - } + // Close the socket + CloseSocket(); return true; } @@ -125,24 +82,14 @@ bool cIptvProtocolHttp::GetHeaderLine(char* dest, unsigned int destLen, debug("cIptvProtocolHttp::GetHeaderLine()\n"); bool linefeed = false; bool newline = false; - char buf[4096]; - char *bufptr = buf; + unsigned char buf[4096]; + unsigned char *bufptr = buf; memset(buf, '\0', sizeof(buf)); recvLen = 0; while (!newline || !linefeed) { - socklen_t addrlen = sizeof(sockAddr); // Wait 500ms for data - int retval = select_single_desc(socketDesc, 500000, false); - // Check if error - if (retval < 0) - return false; - // Check if data available - else if (retval) { - ssize_t retval = recvfrom(socketDesc, bufptr, 1, MSG_DONTWAIT, - (struct sockaddr *)&sockAddr, &addrlen); - if (retval <= 0) - return false; + if (ReadChar(bufptr, 500)) { // Parsing end conditions, if line ends with \r\n if (linefeed && *bufptr == '\n') newline = true; @@ -163,7 +110,7 @@ bool cIptvProtocolHttp::GetHeaderLine(char* dest, unsigned int destLen, } } else { - error("No HTTP response received\n"); + error("No HTTP response received in 500ms\n"); return false; } } diff --git a/socket.c b/socket.c index 05ade5d..0330a29 100644 --- a/socket.c +++ b/socket.c @@ -52,7 +52,7 @@ bool cIptvSocket::OpenSocket(const int Port, const bool isUdp) socketDesc = socket(PF_INET, SOCK_STREAM, 0); ERROR_IF_RET(socketDesc < 0, "socket()", return false); // Make it use non-blocking I/O to avoid stuck read calls - ERROR_IF_FUNC(fcntl(socketDesc, F_SETFL, O_NONBLOCK), "fcntl()", + ERROR_IF_FUNC(fcntl(socketDesc, F_SETFL, O_NONBLOCK), "fcntl(O_NONBLOCK)", CloseSocket(), return false); // Allow multiple sockets to use the same PORT number ERROR_IF_FUNC(setsockopt(socketDesc, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0, "setsockopt(SO_REUSEADDR)", @@ -174,8 +174,11 @@ int cIptvUdpSocket::Read(unsigned char* BufferAddr, unsigned int BufferLen) // Read data from socket if (isActive && socketDesc && BufferAddr && (BufferLen > 0)) len = (int)recvmsg(socketDesc, &msgh, MSG_DONTWAIT); - ERROR_IF_RET(len < 0, "recvmsg()", return -1); - if (len > 0) { + if (len < 0) { + ERROR_IF(errno != EAGAIN, "recvmsg()"); + return -1; + } + else if (len > 0) { // Process auxiliary received data and validate source address for (cmsg = CMSG_FIRSTHDR(&msgh); (sourceAddr != INADDR_ANY) && (cmsg != NULL); cmsg = CMSG_NXTHDR(&msgh, cmsg)) { if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) { @@ -231,12 +234,66 @@ cIptvTcpSocket::~cIptvTcpSocket() debug("cIptvTcpSocket::~cIptvTcpSocket()\n"); } -bool cIptvTcpSocket::OpenSocket(const int Port) +bool cIptvTcpSocket::OpenSocket(const int Port, const char *StreamAddr) { debug("cIptvTcpSocket::OpenSocket()\n"); + + // First try only the IP address + sockAddr.sin_addr.s_addr = inet_addr(StreamAddr); + + if (sockAddr.sin_addr.s_addr == INADDR_NONE) { + debug("Cannot convert %s directly to internet address\n", StreamAddr); + + // It may be a host name, get the name + struct hostent *host; + host = gethostbyname(StreamAddr); + if (!host) { + char tmp[64]; + error("gethostbyname() failed: %s is not valid address: %s", StreamAddr, strerror_r(h_errno, tmp, sizeof(tmp))); + return false; + } + + sockAddr.sin_addr.s_addr = inet_addr(*host->h_addr_list); + } + return cIptvSocket::OpenSocket(Port, false); } +void cIptvTcpSocket::CloseSocket(void) +{ + debug("cIptvTcpSocket::CloseSocket()\n"); + isActive = false; + cIptvSocket::CloseSocket(); +} + +bool cIptvTcpSocket::ConnectSocket(void) +{ + //debug("cIptvTcpSocket::ConnectSocket()\n"); + if (!isActive && (socketDesc >= 0)) { + int retval = connect(socketDesc, (struct sockaddr *)&sockAddr, sizeof(sockAddr)); + // Non-blocking sockets always report in-progress error when connected + ERROR_IF_RET(retval < 0 && errno != EINPROGRESS, "connect()", return false); + // Select with 800ms timeout on the socket completion, check if it is writable + retval = select_single_desc(socketDesc, 800000, true); + if (retval < 0) + return retval; + // Select has returned. Get socket errors if there are any + retval = 0; + socklen_t len = sizeof(retval); + getsockopt(socketDesc, SOL_SOCKET, SO_ERROR, &retval, &len); + // If not any errors, then socket must be ready and connected + if (retval != 0) { + char tmp[64]; + error("Connect() failed: %s", strerror_r(retval, tmp, sizeof(tmp))); + return false; + } + // Update connection flag + isActive = true; + } + + return true; +} + int cIptvTcpSocket::Read(unsigned char* BufferAddr, unsigned int BufferLen) { //debug("cIptvTcpSocket::Read()\n"); @@ -253,3 +310,41 @@ int cIptvTcpSocket::Read(unsigned char* BufferAddr, unsigned int BufferLen) (struct sockaddr *)&sockAddr, &addrlen); return len; } + +bool cIptvTcpSocket::ReadChar(unsigned char* BufferAddr, unsigned int TimeoutMs) +{ + //debug("cIptvTcpSocket::ReadChar()\n"); + // Error out if socket not initialized + if (socketDesc <= 0) { + error("Invalid socket in %s\n", __FUNCTION__); + return false; + } + socklen_t addrlen = sizeof(sockAddr); + // Wait 500ms for data + int retval = select_single_desc(socketDesc, 1000 * TimeoutMs, false); + // Check if error + if (retval < 0) + return false; + // Check if data available + else if (retval) { + retval = (int)recvfrom(socketDesc, BufferAddr, 1, MSG_DONTWAIT, + (struct sockaddr *)&sockAddr, &addrlen); + if (retval <= 0) + return false; + } + + return true; +} + +bool cIptvTcpSocket::Write(const char* BufferAddr, unsigned int BufferLen) +{ + //debug("cIptvTcpSocket::Write()\n"); + // Error out if socket not initialized + if (socketDesc <= 0) { + error("Invalid socket in %s\n", __FUNCTION__); + return false; + } + ERROR_IF_RET(send(socketDesc, BufferAddr, BufferLen, 0) < 0, "send()", return false); + + return true; +} diff --git a/socket.h b/socket.h index 3331cc5..d2b9ea7 100644 --- a/socket.h +++ b/socket.h @@ -45,7 +45,11 @@ public: cIptvTcpSocket(); virtual ~cIptvTcpSocket(); virtual int Read(unsigned char* BufferAddr, unsigned int BufferLen); - bool OpenSocket(const int Port); + bool OpenSocket(const int Port, const char *StreamAddr); + void CloseSocket(void); + bool ConnectSocket(void); + bool ReadChar(unsigned char* BufferAddr, unsigned int TimeoutMs); + bool Write(const char* BufferAddr, unsigned int BufferLen); }; #endif // __IPTV_SOCKET_H