mirror of
https://github.com/rofafor/vdr-plugin-iptv.git
synced 2023-10-10 13:37:03 +02:00
Refactored cIptvTcpSocket class.
This commit is contained in:
parent
ac458b0c36
commit
7629e34d2a
3
README
3
README
@ -164,6 +164,9 @@ Notes:
|
|||||||
- Source address validation can be enabled for UDP protocol separated by
|
- 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"
|
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:
|
Acknowledgements:
|
||||||
|
|
||||||
- The IPTV section filtering code is derived from Linux kernel.
|
- The IPTV section filtering code is derived from Linux kernel.
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#include "protocolhttp.h"
|
#include "protocolhttp.h"
|
||||||
|
|
||||||
cIptvProtocolHttp::cIptvProtocolHttp()
|
cIptvProtocolHttp::cIptvProtocolHttp()
|
||||||
: streamAddr(NULL),
|
: streamAddr(strdup("")),
|
||||||
streamPath(strdup("/"))
|
streamPath(strdup("/"))
|
||||||
{
|
{
|
||||||
debug("cIptvProtocolHttp::cIptvProtocolHttp()\n");
|
debug("cIptvProtocolHttp::cIptvProtocolHttp()\n");
|
||||||
@ -39,49 +39,13 @@ bool cIptvProtocolHttp::Connect(void)
|
|||||||
{
|
{
|
||||||
debug("cIptvProtocolHttp::Connect()\n");
|
debug("cIptvProtocolHttp::Connect()\n");
|
||||||
// Check that stream address is valid
|
// Check that stream address is valid
|
||||||
if (!isActive && !isempty(streamAddr) && !isempty(streamPath)) {
|
if (!isempty(streamAddr) && !isempty(streamPath)) {
|
||||||
// First try only the IP address
|
// Ensure that socket is valid and connect
|
||||||
sockAddr.sin_addr.s_addr = inet_addr(streamAddr);
|
OpenSocket(socketPort, streamAddr);
|
||||||
|
if (!ConnectSocket()) {
|
||||||
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)));
|
|
||||||
CloseSocket();
|
CloseSocket();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formulate and send HTTP request
|
// Formulate and send HTTP request
|
||||||
cString buffer = cString::sprintf("GET %s HTTP/1.1\r\n"
|
cString buffer = cString::sprintf("GET %s HTTP/1.1\r\n"
|
||||||
"Host: %s\r\n"
|
"Host: %s\r\n"
|
||||||
@ -89,19 +53,17 @@ bool cIptvProtocolHttp::Connect(void)
|
|||||||
"Range: bytes=0-\r\n"
|
"Range: bytes=0-\r\n"
|
||||||
"Connection: Close\r\n"
|
"Connection: Close\r\n"
|
||||||
"\r\n", streamPath, streamAddr, PLUGIN_NAME_I18N, VERSION);
|
"\r\n", streamPath, streamAddr, PLUGIN_NAME_I18N, VERSION);
|
||||||
|
|
||||||
debug("Sending http request: %s\n", *buffer);
|
debug("Sending http request: %s\n", *buffer);
|
||||||
ssize_t err2 = send(socketDesc, buffer, strlen(buffer), 0);
|
if (!Write(*buffer, (unsigned int)strlen(*buffer))) {
|
||||||
ERROR_IF_FUNC(err2 < 0, "send()", CloseSocket(), return false);
|
CloseSocket();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Now process headers
|
// Now process headers
|
||||||
if (!ProcessHeaders()) {
|
if (!ProcessHeaders()) {
|
||||||
CloseSocket();
|
CloseSocket();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update active flag
|
|
||||||
isActive = true;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -109,13 +71,8 @@ bool cIptvProtocolHttp::Connect(void)
|
|||||||
bool cIptvProtocolHttp::Disconnect(void)
|
bool cIptvProtocolHttp::Disconnect(void)
|
||||||
{
|
{
|
||||||
debug("cIptvProtocolHttp::Disconnect()\n");
|
debug("cIptvProtocolHttp::Disconnect()\n");
|
||||||
// Check that stream address is valid
|
// Close the socket
|
||||||
if (isActive) {
|
CloseSocket();
|
||||||
// Close the socket
|
|
||||||
CloseSocket();
|
|
||||||
// Update active flag
|
|
||||||
isActive = false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,24 +82,14 @@ bool cIptvProtocolHttp::GetHeaderLine(char* dest, unsigned int destLen,
|
|||||||
debug("cIptvProtocolHttp::GetHeaderLine()\n");
|
debug("cIptvProtocolHttp::GetHeaderLine()\n");
|
||||||
bool linefeed = false;
|
bool linefeed = false;
|
||||||
bool newline = false;
|
bool newline = false;
|
||||||
char buf[4096];
|
unsigned char buf[4096];
|
||||||
char *bufptr = buf;
|
unsigned char *bufptr = buf;
|
||||||
memset(buf, '\0', sizeof(buf));
|
memset(buf, '\0', sizeof(buf));
|
||||||
recvLen = 0;
|
recvLen = 0;
|
||||||
|
|
||||||
while (!newline || !linefeed) {
|
while (!newline || !linefeed) {
|
||||||
socklen_t addrlen = sizeof(sockAddr);
|
|
||||||
// Wait 500ms for data
|
// Wait 500ms for data
|
||||||
int retval = select_single_desc(socketDesc, 500000, false);
|
if (ReadChar(bufptr, 500)) {
|
||||||
// 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;
|
|
||||||
// Parsing end conditions, if line ends with \r\n
|
// Parsing end conditions, if line ends with \r\n
|
||||||
if (linefeed && *bufptr == '\n')
|
if (linefeed && *bufptr == '\n')
|
||||||
newline = true;
|
newline = true;
|
||||||
@ -163,7 +110,7 @@ bool cIptvProtocolHttp::GetHeaderLine(char* dest, unsigned int destLen,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
error("No HTTP response received\n");
|
error("No HTTP response received in 500ms\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
103
socket.c
103
socket.c
@ -52,7 +52,7 @@ bool cIptvSocket::OpenSocket(const int Port, const bool isUdp)
|
|||||||
socketDesc = socket(PF_INET, SOCK_STREAM, 0);
|
socketDesc = socket(PF_INET, SOCK_STREAM, 0);
|
||||||
ERROR_IF_RET(socketDesc < 0, "socket()", return false);
|
ERROR_IF_RET(socketDesc < 0, "socket()", return false);
|
||||||
// Make it use non-blocking I/O to avoid stuck read calls
|
// 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);
|
CloseSocket(), return false);
|
||||||
// Allow multiple sockets to use the same PORT number
|
// 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)",
|
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
|
// Read data from socket
|
||||||
if (isActive && socketDesc && BufferAddr && (BufferLen > 0))
|
if (isActive && socketDesc && BufferAddr && (BufferLen > 0))
|
||||||
len = (int)recvmsg(socketDesc, &msgh, MSG_DONTWAIT);
|
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
|
// Process auxiliary received data and validate source address
|
||||||
for (cmsg = CMSG_FIRSTHDR(&msgh); (sourceAddr != INADDR_ANY) && (cmsg != NULL); cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
|
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)) {
|
if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
|
||||||
@ -231,12 +234,66 @@ cIptvTcpSocket::~cIptvTcpSocket()
|
|||||||
debug("cIptvTcpSocket::~cIptvTcpSocket()\n");
|
debug("cIptvTcpSocket::~cIptvTcpSocket()\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cIptvTcpSocket::OpenSocket(const int Port)
|
bool cIptvTcpSocket::OpenSocket(const int Port, const char *StreamAddr)
|
||||||
{
|
{
|
||||||
debug("cIptvTcpSocket::OpenSocket()\n");
|
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);
|
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)
|
int cIptvTcpSocket::Read(unsigned char* BufferAddr, unsigned int BufferLen)
|
||||||
{
|
{
|
||||||
//debug("cIptvTcpSocket::Read()\n");
|
//debug("cIptvTcpSocket::Read()\n");
|
||||||
@ -253,3 +310,41 @@ int cIptvTcpSocket::Read(unsigned char* BufferAddr, unsigned int BufferLen)
|
|||||||
(struct sockaddr *)&sockAddr, &addrlen);
|
(struct sockaddr *)&sockAddr, &addrlen);
|
||||||
return len;
|
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;
|
||||||
|
}
|
||||||
|
6
socket.h
6
socket.h
@ -45,7 +45,11 @@ public:
|
|||||||
cIptvTcpSocket();
|
cIptvTcpSocket();
|
||||||
virtual ~cIptvTcpSocket();
|
virtual ~cIptvTcpSocket();
|
||||||
virtual int Read(unsigned char* BufferAddr, unsigned int BufferLen);
|
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
|
#endif // __IPTV_SOCKET_H
|
||||||
|
Loading…
Reference in New Issue
Block a user