From cc9e970ea3a98f83ae8289eabdde4672ad535170 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 13 Jul 2019 14:15:43 +0200 Subject: [PATCH] More backports from Pro edition, among which HTTPS/TLS support --- CMakeLists.txt | 3 + lib/defines.h | 2 +- lib/downloader.cpp | 18 +- lib/downloader.h | 3 - lib/dtscmeta.cpp | 1 + lib/http_parser.cpp | 28 +- lib/http_parser.h | 2 +- lib/ogg.cpp | 18 +- lib/procs.cpp | 27 +- lib/shared_memory.cpp | 2 - lib/socket.cpp | 544 +++++++++++++---------- lib/socket.h | 56 +-- lib/ts_packet.cpp | 7 +- src/analysers/analyser_dtsc.cpp | 2 +- src/controller/controller_api.cpp | 1 + src/controller/controller_connectors.cpp | 2 + src/input/input_buffer.cpp | 26 +- src/input/input_dtsc.cpp | 4 +- src/input/input_h264.cpp | 4 +- src/io.cpp | 7 +- src/output/output.cpp | 10 +- src/output/output.h | 6 +- src/output/output_http_internal.cpp | 7 +- src/output/output_https.cpp | 241 ++++++++++ src/output/output_https.h | 34 ++ src/output/output_ts.cpp | 2 +- 26 files changed, 733 insertions(+), 324 deletions(-) create mode 100644 src/output/output_https.cpp create mode 100644 src/output/output_https.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 81b40adf..36648676 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -363,6 +363,9 @@ makeOutput(TS ts ts) makeOutput(HTTPTS httpts http ts) makeOutput(HLS hls http ts) makeOutput(EBML ebml) +if (NOT DEFINED NOSSL ) + makeOutput(HTTPS https)#LTS +endif() add_executable(MistOutHTTP ${BINARY_DIR}/mist/.headers diff --git a/lib/defines.h b/lib/defines.h index 1da61b19..01988d20 100644 --- a/lib/defines.h +++ b/lib/defines.h @@ -107,7 +107,7 @@ static inline void show_stackframe(){} #define STATS_INPUT_DELAY 2 #ifndef INPUT_TIMEOUT -#define INPUT_TIMEOUT STATS_DELAY +#define INPUT_TIMEOUT STATS_DELAY*2 #endif /// The size used for stream headers for live streams diff --git a/lib/downloader.cpp b/lib/downloader.cpp index e2ce801a..d6836c3e 100644 --- a/lib/downloader.cpp +++ b/lib/downloader.cpp @@ -59,16 +59,10 @@ namespace HTTP{ /// Returns a reference to the internal Socket::Connection class instance. Socket::Connection &Downloader::getSocket(){ -#ifdef SSL - if (ssl){return S_SSL;} -#endif return S; } Downloader::~Downloader(){ -#ifdef SSL - if (ssl){S_SSL.close();} -#endif S.close(); } @@ -87,12 +81,12 @@ namespace HTTP{ connectedPort = link.getPort(); #ifdef SSL if (needSSL){ - S_SSL = Socket::SSLConnection(connectedHost, connectedPort, true); + S.open(connectedHost, connectedPort, true, true); }else{ - S = Socket::Connection(connectedHost, connectedPort, true); + S.open(connectedHost, connectedPort, true); } #else - S = Socket::Connection(connectedHost, connectedPort, true); + S.open(connectedHost, connectedPort, true); #endif } }else{ @@ -101,7 +95,7 @@ namespace HTTP{ getSocket().close(); connectedHost = proxyUrl.host; connectedPort = proxyUrl.getPort(); - S = Socket::Connection(connectedHost, connectedPort, true); + S.open(connectedHost, connectedPort, true); } } ssl = needSSL; @@ -218,8 +212,8 @@ namespace HTTP{ MEDIUM_MSG("Posting to %s (%zu/%" PRIu32 ")", link.getUrl().c_str(), retryCount - loop + 1, retryCount); doRequest(link, "POST", payload); - // Not synced? Ignore the response and immediately return false. - if (!sync){return false;} + // Not synced? Ignore the response and immediately return true. + if (!sync){return true;} uint64_t reqTime = Util::bootSecs(); while (getSocket() && Util::bootSecs() < reqTime + dataTimeout){ // No data? Wait for a second or so. diff --git a/lib/downloader.h b/lib/downloader.h index 8ab1ab15..c9a5e683 100644 --- a/lib/downloader.h +++ b/lib/downloader.h @@ -36,9 +36,6 @@ namespace HTTP{ uint32_t connectedPort; ///< Currently connected port number Parser H; ///< HTTP parser for downloader Socket::Connection S; ///< TCP socket for downloader -#ifdef SSL - Socket::SSLConnection S_SSL; ///< SSL socket for downloader -#endif bool ssl; ///< True if ssl is currently in use. std::string authStr; ///< Most recently seen WWW-Authenticate request std::string proxyAuthStr; ///< Most recently seen Proxy-Authenticate request diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index a7b49a13..5061f220 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -1513,6 +1513,7 @@ namespace DTSC { } void Meta::update(long long packTime, long long packOffset, long long packTrack, long long packDataSize, uint64_t packBytePos, bool isKeyframe, long long packSendSize, unsigned long segment_size){ + DONTEVEN_MSG("Updating meta with: t=%lld, o=%lld, s=%lld, t=%lld, p=%lld", packTime, packOffset, packDataSize, packTrack, packBytePos); if (!packSendSize){ //time and trackID are part of the 20-byte header. //the container object adds 4 bytes (plus 2+namelen for each content, see below) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 11522373..85c2fdfb 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -150,6 +150,7 @@ uint32_t HTTP::URL::getDefaultPort() const{ if (protocol == "http"){return 80;} if (protocol == "https"){return 443;} if (protocol == "rtmp"){return 1935;} + if (protocol == "rtmps"){return 443;} if (protocol == "dtsc"){return 4200;} if (protocol == "rtsp"){return 554;} return 0; @@ -418,8 +419,29 @@ std::string &HTTP::Parser::BuildRequest(){ /// Creates and sends a valid HTTP 1.0 or 1.1 request. /// The request is build from internal variables set before this call is made. /// To be precise, method, url, protocol, headers and body are used. -void HTTP::Parser::SendRequest(Socket::Connection &conn, const std::string &reqbody){ +void HTTP::Parser::SendRequest(Socket::Connection &conn, const std::string &reqbody, bool allAtOnce){ /// \todo Include GET/POST variable parsing? + if (allAtOnce){ + /// \TODO Make this less duplicated / more pretty. + + std::map::iterator it; + if (protocol.size() < 5 || protocol[4] != '/'){protocol = "HTTP/1.0";} + builder = method + " " + url + " " + protocol + "\r\n"; + if (reqbody.size()){SetHeader("Content-Length", reqbody.length());} + for (it = headers.begin(); it != headers.end(); it++){ + if ((*it).first != "" && (*it).second != ""){ + builder += (*it).first + ": " + (*it).second + "\r\n"; + } + } + builder += "\r\n"; + if (reqbody.size()){ + builder += reqbody; + }else{ + builder += body; + } + conn.SendNow(builder); + return; + } std::map::iterator it; if (protocol.size() < 5 || protocol[4] != '/'){protocol = "HTTP/1.0";} builder = method + " " + url + " " + protocol + "\r\n"; @@ -827,6 +849,10 @@ bool HTTP::Parser::parse(std::string &HTTPbuffer){ length = atoi(GetHeader("Content-Length").c_str()); if (body.capacity() < length){body.reserve(length);} } + if (GetHeader("Content-length") != ""){ + length = atoi(GetHeader("Content-length").c_str()); + if (body.capacity() < length){body.reserve(length);} + } if (GetHeader("Transfer-Encoding") == "chunked"){ getChunks = true; doingChunk = 0; diff --git a/lib/http_parser.h b/lib/http_parser.h index a2080cb1..9172eae6 100644 --- a/lib/http_parser.h +++ b/lib/http_parser.h @@ -37,7 +37,7 @@ namespace HTTP{ std::string &BuildRequest(); std::string &BuildResponse(); std::string &BuildResponse(std::string code, std::string message); - void SendRequest(Socket::Connection &conn, const std::string &reqbody = ""); + void SendRequest(Socket::Connection &conn, const std::string &reqbody = "", bool allAtOnce = false); void SendResponse(std::string code, std::string message, Socket::Connection &conn); void StartResponse(std::string code, std::string message, Parser &request, Socket::Connection &conn, bool bufferAllChunks = false); diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 6876dd0c..d60c1499 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -107,6 +107,7 @@ namespace OGG { /// Reads an OGG Page from the source and if valid, removes it from source. bool Page::read(std::string & newData){ int len = newData.size(); + int total = 0; segments.clear(); if (newData.size() < 27){ return false; @@ -119,14 +120,25 @@ namespace OGG { if (newData.size() < 27u + getPageSegments()){ //check input size return false; } - newData.erase(0, 27); - memcpy(data + 27, newData.c_str(), getPageSegments()); - newData.erase(0, getPageSegments()); + memcpy(data + 27, newData.data()+27, getPageSegments()); std::deque segSizes = decodeXiphSize(data + 27, getPageSegments()); + for (std::deque::iterator it = segSizes.begin(); it != segSizes.end(); it++){ + total += *it; + } + + total += 27; + //return false if the segment is not complete + total += getPageSegments(); + if(total >= len){ + return false; + } + + newData.erase(0, getPageSegments()+27); for (std::deque::iterator it = segSizes.begin(); it != segSizes.end(); it++){ segments.push_back(std::string(newData.data(), *it)); newData.erase(0, *it); } + INFO_MSG("Erased %lu bytes from the input", len - newData.size()); return true; } diff --git a/lib/procs.cpp b/lib/procs.cpp index 1ee71b9f..72e01ddf 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -100,30 +100,29 @@ void Util::Procs::exit_handler() { return; } - WARN_MSG("Sending SIGINT to remaining %d children", (int)listcopy.size()); - //send sigint to all remaining - if (!listcopy.empty()) { - for (it = listcopy.begin(); it != listcopy.end(); it++) { - DEBUG_MSG(DLVL_DEVEL, "SIGINT %d", *it); - kill(*it, SIGINT); - } - } - - INFO_MSG("Waiting up to 5 seconds for %d children to terminate.", (int)listcopy.size()); + INFO_MSG("Sending SIGINT and waiting up to 10 seconds for %d children to terminate.", (int)listcopy.size()); waiting = 0; - //wait up to 5 seconds for applications to shut down - while (!listcopy.empty() && waiting <= 250) { + //wait up to 10 seconds for applications to shut down + while (!listcopy.empty() && waiting <= 500) { + bool doWait = true; for (it = listcopy.begin(); it != listcopy.end(); it++) { if (!childRunning(*it)) { listcopy.erase(it); + doWait = false; break; } - if (!listcopy.empty()) { + } + if (doWait && !listcopy.empty()) { + if ((waiting % 50) == 0){ + for (it = listcopy.begin(); it != listcopy.end(); it++) { + INFO_MSG("SIGINT %d", *it); + kill(*it, SIGINT); + } + } Util::wait(20); ++waiting; } } - } if (listcopy.empty()) { return; } diff --git a/lib/shared_memory.cpp b/lib/shared_memory.cpp index af973fe5..cfbc34d2 100644 --- a/lib/shared_memory.cpp +++ b/lib/shared_memory.cpp @@ -1192,8 +1192,6 @@ namespace IPC { ///\brief The deconstructor sharedClient::~sharedClient() { mySemaphore.close(); - - } ///\brief Writes data to the shared data diff --git a/lib/socket.cpp b/lib/socket.cpp index 87cc3f10..47fdac60 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -402,25 +402,40 @@ void Socket::Connection::setBoundAddr(){ } } +//Cleans up the socket by dropping the connection. +//Does not call close because it calls shutdown, which would destroy any copies of this socket too. +Socket::Connection::~Connection(){ + drop(); +} + + /// Create a new base socket. This is a basic constructor for converting any valid socket to a /// Socket::Connection. \param sockNo Integer representing the socket to convert. Socket::Connection::Connection(int sockNo){ - sSend = sockNo; - sRecv = -1; - isTrueSocket = Socket::checkTrueSocket(sSend); - setBoundAddr(); - up = 0; - down = 0; - conntime = Util::epoch(); - Error = false; - Blocking = false; - skipCount = 0; + clear(); + open(sockNo, sockNo); }// Socket::Connection basic constructor +/// Open from existing socket connection. +/// Closes any existing connections and resets all internal values beforehand. +/// Simply calls open(sockNo, sockNo) internally. +void Socket::Connection::open(int sockNo){ + open(sockNo, sockNo); +} + /// Simulate a socket using two file descriptors. /// \param write The filedescriptor to write to. /// \param read The filedescriptor to read from. Socket::Connection::Connection(int write, int read){ + clear(); + open(write, read); +}// Socket::Connection basic constructor + +/// Open from two existing file descriptors. +/// Closes any existing connections and resets all internal values beforehand. +void Socket::Connection::open(int write, int read){ + drop(); + clear(); sSend = write; if (write != read){ sRecv = read; @@ -429,17 +444,9 @@ Socket::Connection::Connection(int write, int read){ } isTrueSocket = Socket::checkTrueSocket(sSend); setBoundAddr(); - up = 0; - down = 0; - conntime = Util::epoch(); - Error = false; - Blocking = false; - skipCount = 0; -}// Socket::Connection basic constructor +} -/// Create a new disconnected base socket. This is a basic constructor for placeholder purposes. -/// A socket created like this is always disconnected and should/could be overwritten at some point. -Socket::Connection::Connection(){ +void Socket::Connection::clear(){ sSend = -1; sRecv = -1; isTrueSocket = false; @@ -449,6 +456,20 @@ Socket::Connection::Connection(){ Error = false; Blocking = false; skipCount = 0; +#ifdef SSL + sslConnected = false; + server_fd = 0; + ssl = 0; + conf = 0; + ctr_drbg = 0; + entropy = 0; +#endif +} + +/// Create a new disconnected base socket. This is a basic constructor for placeholder purposes. +/// A socket created like this is always disconnected and should/could be overwritten at some point. +Socket::Connection::Connection(){ + clear(); }// Socket::Connection basic constructor void Socket::Connection::resetCounter(){ @@ -483,12 +504,28 @@ bool isFDBlocking(int FD){ /// Set this socket to be blocking (true) or nonblocking (false). void Socket::Connection::setBlocking(bool blocking){ +#ifdef SSL + if (sslConnected){ + if (blocking == Blocking){return;} + if (blocking){ + mbedtls_net_set_block(server_fd); + Blocking = true; + }else{ + mbedtls_net_set_nonblock(server_fd); + Blocking = false; + } + return; + } +#endif if (sSend >= 0){setFDBlocking(sSend, blocking);} if (sRecv >= 0 && sSend != sRecv){setFDBlocking(sRecv, blocking);} } /// Set this socket to be blocking (true) or nonblocking (false). bool Socket::Connection::isBlocking(){ +#ifdef SSL + if (sslConnected){return Blocking;} +#endif if (sSend >= 0){return isFDBlocking(sSend);} if (sRecv >= 0){return isFDBlocking(sRecv);} return false; @@ -508,6 +545,41 @@ void Socket::Connection::close(){ /// This function does *not* call shutdown, allowing continued use in other /// processes. void Socket::Connection::drop(){ +#ifdef SSL + if (sslConnected){ + DONTEVEN_MSG("SSL close"); + if (ssl){ + mbedtls_ssl_close_notify(ssl); + } + if (server_fd){ + mbedtls_net_free(server_fd); + delete server_fd; + server_fd = 0; + } + if (ssl){ + mbedtls_ssl_free(ssl); + delete ssl; + ssl = 0; + } + if (conf){ + mbedtls_ssl_config_free(conf); + delete conf; + conf = 0; + } + if (ctr_drbg){ + mbedtls_ctr_drbg_free(ctr_drbg); + delete ctr_drbg; + ctr_drbg = 0; + } + if (entropy){ + mbedtls_entropy_free(entropy); + delete entropy; + entropy = 0; + } + sslConnected = false; + return; + } +#endif if (connected()){ if (sSend != -1){ HIGH_MSG("Socket %d closed", sSend); @@ -547,8 +619,15 @@ std::string Socket::Connection::getError(){ /// \param address String containing the location of the Unix socket to connect to. /// \param nonblock Whether the socket should be nonblocking. False by default. Socket::Connection::Connection(std::string address, bool nonblock){ - skipCount = 0; - sRecv = -1; + clear(); + open(address, nonblock); +}// Socket::Connection Unix Constructor + +/// Open Unix connection. +/// Closes any existing connections and resets all internal values beforehand. +void Socket::Connection::open(std::string address, bool nonblock){ + drop(); + clear(); isTrueSocket = true; sSend = socket(PF_UNIX, SOCK_STREAM, 0); if (sSend < 0){ @@ -556,11 +635,6 @@ Socket::Connection::Connection(std::string address, bool nonblock){ FAIL_MSG("Could not create socket! Error: %s", remotehost.c_str()); return; } - Error = false; - Blocking = false; - up = 0; - down = 0; - conntime = Util::epoch(); sockaddr_un addr; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, address.c_str(), address.size() + 1); @@ -576,22 +650,100 @@ Socket::Connection::Connection(std::string address, bool nonblock){ FAIL_MSG("Could not connect to %s! Error: %s", address.c_str(), remotehost.c_str()); close(); } -}// Socket::Connection Unix Constructor +} + +#ifdef SSL +///Local-only function for debugging SSL sockets +static void my_debug(void *ctx, int level, const char *file, int line, const char *str){ + ((void)level); + fprintf((FILE *)ctx, "%s:%04d: %s", file, line, str); + fflush((FILE *)ctx); +} +#endif /// Create a new TCP Socket. This socket will (try to) connect to the given host/port right away. /// \param host String containing the hostname to connect to. /// \param port String containing the port to connect to. /// \param nonblock Whether the socket should be nonblocking. -Socket::Connection::Connection(std::string host, int port, bool nonblock){ - skipCount = 0; - sRecv = -1; +Socket::Connection::Connection(std::string host, int port, bool nonblock, bool with_ssl){ + clear(); + open(host, port, nonblock, with_ssl); +} + +/// Open TCP connection. +/// Closes any existing connections and resets all internal values beforehand. +void Socket::Connection::open(std::string host, int port, bool nonblock, bool with_ssl){ + drop(); + clear(); + if (with_ssl){ +#ifdef SSL + mbedtls_debug_set_threshold(0); + server_fd = new mbedtls_net_context; + ssl = new mbedtls_ssl_context; + conf = new mbedtls_ssl_config; + ctr_drbg = new mbedtls_ctr_drbg_context; + entropy = new mbedtls_entropy_context; + mbedtls_net_init(server_fd); + mbedtls_ssl_init(ssl); + mbedtls_ssl_config_init(conf); + mbedtls_ctr_drbg_init(ctr_drbg); + mbedtls_entropy_init(entropy); + DONTEVEN_MSG("SSL init"); + if (mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, entropy, (const unsigned char *)"meow", + 4) != 0){ + FAIL_MSG("SSL socket init failed"); + close(); + return; + } + DONTEVEN_MSG("SSL connect"); + int ret = 0; + if ((ret = mbedtls_net_connect(server_fd, host.c_str(), JSON::Value(port).asString().c_str(), MBEDTLS_NET_PROTO_TCP)) != 0){ + FAIL_MSG(" failed\n ! mbedtls_net_connect returned %d\n\n", ret); + close(); + return; + } + if ((ret = mbedtls_ssl_config_defaults(conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0){ + FAIL_MSG(" failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret); + close(); + return; + } + mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_NONE); + mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, ctr_drbg); + mbedtls_ssl_conf_dbg(conf, my_debug, stderr); + if ((ret = mbedtls_ssl_setup(ssl, conf)) != 0){ + char estr[200]; + mbedtls_strerror(ret, estr, 200); + FAIL_MSG("SSL setup error %d: %s", ret, estr); + close(); + return; + } + if ((ret = mbedtls_ssl_set_hostname(ssl, host.c_str())) != 0){ + FAIL_MSG(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret); + close(); + return; + } + mbedtls_ssl_set_bio(ssl, server_fd, mbedtls_net_send, mbedtls_net_recv, NULL); + while ((ret = mbedtls_ssl_handshake(ssl)) != 0){ + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){ + char estr[200]; + mbedtls_strerror(ret, estr, 200); + FAIL_MSG("SSL handshake error %d: %s", ret, estr); + close(); + return; + } + } + sslConnected = true; + Blocking = true; + if (nonblock){setBlocking(false);} + DONTEVEN_MSG("SSL connect success"); + return; +#endif + FAIL_MSG("Attempted to open SSL socket without SSL support compiled in!"); + return; + } isTrueSocket = true; struct addrinfo *result, *rp, hints; - Error = false; - Blocking = false; - up = 0; - down = 0; - conntime = Util::epoch(); std::stringstream ss; ss << port; @@ -630,7 +782,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ setsockopt(sSend, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); setBoundAddr(); } -}// Socket::Connection TCP Constructor +} /// Returns the connected-state for this socket. /// Note that this function might be slightly behind the real situation. @@ -638,6 +790,9 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ /// and when the socket is closed manually. /// \returns True if socket is connected, false otherwise. bool Socket::Connection::connected() const{ +#ifdef SSL + if (sslConnected){return true;} +#endif return (sSend >= 0) || (sRecv >= 0); } @@ -721,6 +876,38 @@ void Socket::Connection::skipBytes(uint32_t byteCount){ /// \param len Amount of bytes to write. /// \returns The amount of bytes actually written. unsigned int Socket::Connection::iwrite(const void *buffer, int len){ +#ifdef SSL + if (sslConnected){ + DONTEVEN_MSG("SSL iwrite"); + if (!connected() || len < 1){return 0;} + int r; + r = mbedtls_ssl_write(ssl, (const unsigned char *)buffer, len); + if (r < 0){ + char estr[200]; + mbedtls_strerror(r, estr, 200); + INFO_MSG("Write returns %d: %s", r, estr); + } + if (r < 0){ + switch (errno){ + case MBEDTLS_ERR_SSL_WANT_WRITE: return 0; break; + case MBEDTLS_ERR_SSL_WANT_READ: return 0; break; + case EWOULDBLOCK: return 0; break; + default: + Error = true; + INSANE_MSG("Could not iwrite data! Error: %s", strerror(errno)); + close(); + return 0; + break; + } + } + if (r == 0 && (sSend >= 0)){ + DONTEVEN_MSG("Socket closed by remote"); + close(); + } + up += r; + return r; + } +#endif if (!connected() || len < 1){return 0;} if (skipCount){ // We have bytes to skip writing. @@ -766,6 +953,38 @@ unsigned int Socket::Connection::iwrite(const void *buffer, int len){ /// \param flags Flags to use in the recv call. Ignored on fake sockets. /// \returns The amount of bytes actually read. int Socket::Connection::iread(void *buffer, int len, int flags){ +#ifdef SSL + if (sslConnected){ + DONTEVEN_MSG("SSL iread"); + if (!connected() || len < 1){return 0;} + int r; + /// \TODO Flags ignored... Bad. + r = mbedtls_ssl_read(ssl, (unsigned char *)buffer, len); + if (r < 0){ + switch (errno){ + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: close(); return 0; break; + case MBEDTLS_ERR_SSL_WANT_WRITE: return 0; break; + case MBEDTLS_ERR_SSL_WANT_READ: return 0; break; + case EWOULDBLOCK: return 0; break; + case EINTR: return 0; break; + default: + Error = true; + char estr[200]; + mbedtls_strerror(r, estr, 200); + INFO_MSG("Read returns %d: %s (%s)", r, estr, strerror(errno)); + close(); + return 0; + break; + } + } + if (r == 0){ + DONTEVEN_MSG("Socket closed by remote"); + close(); + } + down += r; + return r; + } +#endif if (!connected() || len < 1){return 0;} int r; if (sRecv != -1 || !isTrueSocket){ @@ -873,6 +1092,56 @@ Socket::Connection::operator bool() const{ return connected(); } +//Copy constructor +Socket::Connection::Connection(const Connection& rhs){ + clear(); + if (!rhs){return;} +#if DEBUG >= DLVL_DEVEL + INFO_MSG("Copying %s socket", rhs.sslConnected?"SSL":"regular"); + BACKTRACE; +#endif + conntime = rhs.conntime; + isTrueSocket = rhs.isTrueSocket; + remotehost = rhs.remotehost; + boundaddr = rhs.boundaddr; + up = rhs.up; + down = rhs.down; + downbuffer = rhs.downbuffer; + if (!rhs.sslConnected){ + if (rhs.sSend >= 0){sSend = dup(rhs.sSend);} + if (rhs.sRecv >= 0){sRecv = dup(rhs.sRecv);} +#if DEBUG >= DLVL_DEVEL + INFO_MSG("Socket original = (%d / %d), copy = (%d / %d)", rhs.sSend, rhs.sRecv, sSend, sRecv); +#endif + } +} + +//Assignment constructor +Socket::Connection& Socket::Connection::operator=(const Socket::Connection& rhs){ + drop(); + clear(); + if (!rhs){return *this;} +#if DEBUG >= DLVL_DEVEL + INFO_MSG("Assigning %s socket", rhs.sslConnected?"SSL":"regular"); + BACKTRACE; +#endif + conntime = rhs.conntime; + isTrueSocket = rhs.isTrueSocket; + remotehost = rhs.remotehost; + boundaddr = rhs.boundaddr; + up = rhs.up; + down = rhs.down; + downbuffer = rhs.downbuffer; + if (!rhs.sslConnected){ + if (rhs.sSend >= 0){sSend = dup(rhs.sSend);} + if (rhs.sRecv >= 0){sRecv = dup(rhs.sRecv);} +#if DEBUG >= DLVL_DEVEL + INFO_MSG("Socket original = (%d / %d), copy = (%d / %d)", rhs.sSend, rhs.sRecv, sSend, sRecv); +#endif + } + return *this; +} + /// Returns true if the given address can be matched with the remote host. /// Can no longer return true after any socket error have occurred. bool Socket::Connection::isAddress(const std::string &addr){ @@ -885,207 +1154,6 @@ bool Socket::Connection::isLocal(){ return Socket::isLocal(remotehost); } -#ifdef SSL -Socket::SSLConnection::SSLConnection() : Socket::Connection::Connection(){ - isConnected = false; - server_fd = 0; - ssl = 0; - conf = 0; - ctr_drbg = 0; - entropy = 0; -} - -static void my_debug(void *ctx, int level, const char *file, int line, const char *str){ - ((void)level); - fprintf((FILE *)ctx, "%s:%04d: %s", file, line, str); - fflush((FILE *)ctx); -} - -Socket::SSLConnection::SSLConnection(std::string hostname, int port, bool nonblock) - : Socket::Connection(){ - mbedtls_debug_set_threshold(0); - isConnected = true; - server_fd = new mbedtls_net_context; - ssl = new mbedtls_ssl_context; - conf = new mbedtls_ssl_config; - ctr_drbg = new mbedtls_ctr_drbg_context; - entropy = new mbedtls_entropy_context; - mbedtls_net_init(server_fd); - mbedtls_ssl_init(ssl); - mbedtls_ssl_config_init(conf); - mbedtls_ctr_drbg_init(ctr_drbg); - mbedtls_entropy_init(entropy); - DONTEVEN_MSG("SSL init"); - if (mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, entropy, (const unsigned char *)"meow", - 4) != 0){ - FAIL_MSG("SSL socket init failed"); - close(); - return; - } - DONTEVEN_MSG("SSL connect"); - int ret = 0; - if ((ret = mbedtls_net_connect(server_fd, hostname.c_str(), JSON::Value(port).asString().c_str(), MBEDTLS_NET_PROTO_TCP)) != 0){ - FAIL_MSG(" failed\n ! mbedtls_net_connect returned %d\n\n", ret); - close(); - return; - } - if ((ret = mbedtls_ssl_config_defaults(conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT)) != 0){ - FAIL_MSG(" failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret); - close(); - return; - } - mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_NONE); - mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, ctr_drbg); - mbedtls_ssl_conf_dbg(conf, my_debug, stderr); - if ((ret = mbedtls_ssl_setup(ssl, conf)) != 0){ - char estr[200]; - mbedtls_strerror(ret, estr, 200); - FAIL_MSG("SSL setup error %d: %s", ret, estr); - close(); - return; - } - if ((ret = mbedtls_ssl_set_hostname(ssl, hostname.c_str())) != 0){ - FAIL_MSG(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret); - close(); - return; - } - mbedtls_ssl_set_bio(ssl, server_fd, mbedtls_net_send, mbedtls_net_recv, NULL); - while ((ret = mbedtls_ssl_handshake(ssl)) != 0){ - if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){ - char estr[200]; - mbedtls_strerror(ret, estr, 200); - FAIL_MSG("SSL handshake error %d: %s", ret, estr); - close(); - return; - } - } - Blocking = true; - if (nonblock){setBlocking(false);} -} - -void Socket::SSLConnection::close(){ - DONTEVEN_MSG("SSL close"); - if (server_fd){ - mbedtls_net_free(server_fd); - delete server_fd; - server_fd = 0; - } - if (ssl){ - mbedtls_ssl_free(ssl); - delete ssl; - ssl = 0; - } - if (conf){ - mbedtls_ssl_config_free(conf); - delete conf; - conf = 0; - } - if (ctr_drbg){ - mbedtls_ctr_drbg_free(ctr_drbg); - delete ctr_drbg; - ctr_drbg = 0; - } - if (entropy){ - mbedtls_entropy_free(entropy); - delete entropy; - entropy = 0; - } - isConnected = false; -} - -/// Incremental read call. This function tries to read len bytes to the buffer from the socket, -/// returning the amount of bytes it actually read. -/// \param buffer Location of the buffer to read to. -/// \param len Amount of bytes to read. -/// \param flags Flags to use in the recv call. Ignored on fake sockets. -/// \returns The amount of bytes actually read. -int Socket::SSLConnection::iread(void *buffer, int len, int flags){ - DONTEVEN_MSG("SSL iread"); - if (!connected() || len < 1){return 0;} - int r; - /// \TODO Flags ignored... Bad. - r = mbedtls_ssl_read(ssl, (unsigned char *)buffer, len); - if (r < 0){ - char estr[200]; - mbedtls_strerror(r, estr, 200); - INFO_MSG("Read returns %d: %s", r, estr); - } - if (r < 0){ - switch (errno){ - case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: close(); return 0; break; - case MBEDTLS_ERR_SSL_WANT_WRITE: return 0; break; - case MBEDTLS_ERR_SSL_WANT_READ: return 0; break; - case EWOULDBLOCK: return 0; break; - case EINTR: return 0; break; - default: - Error = true; - INSANE_MSG("Could not iread data! Error: %s", strerror(errno)); - close(); - return 0; - break; - } - } - if (r == 0){ - DONTEVEN_MSG("Socket closed by remote"); - close(); - } - down += r; - return r; -} - -/// Incremental write call. This function tries to write len bytes to the socket from the buffer, -/// returning the amount of bytes it actually wrote. -/// \param buffer Location of the buffer to write from. -/// \param len Amount of bytes to write. -/// \returns The amount of bytes actually written. -unsigned int Socket::SSLConnection::iwrite(const void *buffer, int len){ - DONTEVEN_MSG("SSL iwrite"); - if (!connected() || len < 1){return 0;} - int r; - r = mbedtls_ssl_write(ssl, (const unsigned char *)buffer, len); - if (r < 0){ - char estr[200]; - mbedtls_strerror(r, estr, 200); - INFO_MSG("Write returns %d: %s", r, estr); - } - if (r < 0){ - switch (errno){ - case MBEDTLS_ERR_SSL_WANT_WRITE: return 0; break; - case MBEDTLS_ERR_SSL_WANT_READ: return 0; break; - case EWOULDBLOCK: return 0; break; - default: - Error = true; - INSANE_MSG("Could not iwrite data! Error: %s", strerror(errno)); - close(); - return 0; - break; - } - } - if (r == 0 && (sSend >= 0)){ - DONTEVEN_MSG("Socket closed by remote"); - close(); - } - up += r; - return r; -} - -bool Socket::SSLConnection::connected() const{ - return isConnected; -} - -void Socket::SSLConnection::setBlocking(bool blocking){ - if (blocking != Blocking){return;} - if (blocking){ - mbedtls_net_set_block(server_fd); - Blocking = true; - }else{ - mbedtls_net_set_nonblock(server_fd); - Blocking = false; - } -} - -#endif /// Create a new base Server. The socket is never connected, and a placeholder for later /// connections. diff --git a/lib/socket.h b/lib/socket.h index 7907fe86..d6ac4249 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -71,6 +71,7 @@ namespace Socket{ /// If they are not identical and sRecv is closed but sSend still open, reading from sSend will be attempted. class Connection{ protected: + void clear(); ///< Clears the internal data structure. Meant only for use in constructors. bool isTrueSocket; int sSend; ///< Write end of socket. int sRecv; ///< Read end of socket. @@ -81,24 +82,46 @@ namespace Socket{ uint64_t down; long long int conntime; Buffer downbuffer; ///< Stores temporary data coming in. - virtual int iread(void *buffer, int len, int flags = 0); ///< Incremental read call. - virtual unsigned int iwrite(const void *buffer, int len); ///< Incremental write call. + int iread(void *buffer, int len, int flags = 0); ///< Incremental read call. + unsigned int iwrite(const void *buffer, int len); ///< Incremental write call. bool iread(Buffer &buffer, int flags = 0); ///< Incremental write call that is compatible with Socket::Buffer. bool iwrite(std::string &buffer); ///< Write call that is compatible with std::string. void setBoundAddr(); +#ifdef SSL + /// optional extension that uses mbedtls for SSL + protected: + bool sslConnected; + int ssl_iread(void *buffer, int len, int flags = 0); ///< Incremental read call. + unsigned int ssl_iwrite(const void *buffer, int len); ///< Incremental write call. + mbedtls_net_context * server_fd; + mbedtls_entropy_context * entropy; + mbedtls_ctr_drbg_context * ctr_drbg; + mbedtls_ssl_context * ssl; + mbedtls_ssl_config * conf; +#endif + public: // friends friend class ::Buffer::user; // constructors Connection(); ///< Create a new disconnected base socket. Connection(int sockNo); ///< Create a new base socket. - Connection(std::string hostname, int port, bool nonblock); ///< Create a new TCP socket. + Connection(std::string hostname, int port, bool nonblock, bool with_ssl = false); ///< Create a new TCP socket. Connection(std::string adres, bool nonblock = false); ///< Create a new Unix Socket. Connection(int write, int read); ///< Simulate a socket using two file descriptors. + // copy/assignment constructors + Connection(const Connection& rhs); + Connection& operator=(const Connection& rhs); + // destructor + ~Connection(); // generic methods - virtual void close(); ///< Close connection. + void open(int sockNo);//Open from existing socket connection. + void open(std::string hostname, int port, bool nonblock, bool with_ssl = false);//Open TCP connection. + void open(std::string adres, bool nonblock = false);//Open Unix connection. + void open(int write, int read);//Open from two existing file descriptors. + void close(); ///< Close connection. void drop(); ///< Close connection without shutdown. - virtual void setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false). + void setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false). bool isBlocking(); ///< Check if this socket is blocking (true) or nonblocking (false). std::string getHost() const; ///< Gets hostname for connection, if available. std::string getBinHost(); @@ -107,7 +130,7 @@ namespace Socket{ int getSocket(); ///< Returns internal socket number. int getPureSocket(); ///< Returns non-piped internal socket number. std::string getError(); ///< Returns a string describing the last error that occured. - virtual bool connected() const; ///< Returns the connected-state for this socket. + bool connected() const; ///< Returns the connected-state for this socket. bool isAddress(const std::string &addr); bool isLocal(); ///< Returns true if remote address is a local address. // buffered i/o methods @@ -136,27 +159,6 @@ namespace Socket{ operator bool() const; }; -#ifdef SSL - /// Version of Socket::Connection that uses mbedtls for SSL - class SSLConnection : public Connection{ - public: - SSLConnection(); - SSLConnection(std::string hostname, int port, bool nonblock); ///< Create a new TCP socket. - void close(); ///< Close connection. - bool connected() const; ///< Returns the connected-state for this socket. - void setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false). - protected: - bool isConnected; - int iread(void *buffer, int len, int flags = 0); ///< Incremental read call. - unsigned int iwrite(const void *buffer, int len); ///< Incremental write call. - mbedtls_net_context * server_fd; - mbedtls_entropy_context * entropy; - mbedtls_ctr_drbg_context * ctr_drbg; - mbedtls_ssl_context * ssl; - mbedtls_ssl_config * conf; - }; -#endif - /// This class is for easily setting up listening socket, either TCP or Unix. class Server{ private: diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index 3882cd88..4b5ec7a9 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -529,9 +529,13 @@ namespace TS { /// \param len The length of this frame. /// \param PTS The timestamp of the frame. std::string & Packet::getPESVideoLeadIn(unsigned int len, unsigned long long PTS, unsigned long long offset, bool isAligned, uint64_t bps) { + if (len){ len += (offset ? 13 : 8); + } if (bps >= 50){ + if (len){ len += 3; + } }else{ bps = 0; } @@ -828,7 +832,7 @@ namespace TS { case 0x15: return "meta PES"; case 0x16: return "meta section"; case 0x1B: return "H264"; - case 0x24: return "H265"; + case 0x24: return "HEVC"; case 0x81: return "AC3"; default: return "unknown"; } @@ -1232,6 +1236,7 @@ namespace TS { }else if (myMeta.tracks[*it].codec == "MP3"){ entry.setStreamType(0x03); }else if (myMeta.tracks[*it].codec == "ID3"){ + entry.setStreamType(0x15); entry.setESInfo(myMeta.tracks[*it].init); } entry.advance(); diff --git a/src/analysers/analyser_dtsc.cpp b/src/analysers/analyser_dtsc.cpp index 044660be..ca810288 100644 --- a/src/analysers/analyser_dtsc.cpp +++ b/src/analysers/analyser_dtsc.cpp @@ -14,7 +14,7 @@ void AnalyserDTSC::init(Util::Config &conf){ bool AnalyserDTSC::open(const std::string &filename){ if (!Analyser::open(filename)){return false;} - conn = Socket::Connection(1, 0); + conn.open(1, 0); totalBytes = 0; return true; } diff --git a/src/controller/controller_api.cpp b/src/controller/controller_api.cpp index 0cb7c907..7827c712 100644 --- a/src/controller/controller_api.cpp +++ b/src/controller/controller_api.cpp @@ -492,6 +492,7 @@ void Controller::handleAPICommands(JSON::Value & Request, JSON::Value & Response Response["capabilities"] = capabilities; } + if(Request.isMember("browse")){ if(Request["browse"] == ""){ Request["browse"] = "."; diff --git a/src/controller/controller_connectors.cpp b/src/controller/controller_connectors.cpp index dcfd6324..fcbe767e 100644 --- a/src/controller/controller_connectors.cpp +++ b/src/controller/controller_connectors.cpp @@ -37,9 +37,11 @@ namespace Controller { } memset(f.mapped, 0, 32); Util::RelAccX A(f.mapped, false); + if (!A.isReady()){ A.addField("cmd", RAX_128STRING); A.addField("pid", RAX_64UINT); A.setReady(); + } uint32_t count = 0; std::map::iterator it; for (it = currentConnectors.begin(); it != currentConnectors.end(); ++it){ diff --git a/src/input/input_buffer.cpp b/src/input/input_buffer.cpp index 1d634f2c..c17b255d 100644 --- a/src/input/input_buffer.cpp +++ b/src/input/input_buffer.cpp @@ -581,16 +581,34 @@ namespace Mist { std::string trackIdentifier = trackMeta.tracks.find(value)->second.getIdentifier(); DEBUG_MSG(DLVL_HIGH, "Attempting colision detection for track %s", trackIdentifier.c_str()); - + //Get the identifier for the track, and attempt colission detection. + int collidesWith = -1; + for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { + //If the identifier of an existing track and the current track match, assume the are the same track and reject the negotiated one. + ///\todo Maybe switch to a new form of detecting collisions, especially with regards to multiple audio languages and camera angles. + if (it->second.getIdentifier() == trackIdentifier) { + collidesWith = it->first; + break; + } + } //Remove the "negotiate" status in either case negotiatingTracks.erase(value); //Set master to true before erasing the page, because we are responsible for cleaning up unused pages nProxy.metaPages[value].master = true; nProxy.metaPages.erase(value); - int finalMap = 3; - if (trackMeta.tracks.find(value)->second.type == "video"){finalMap = 1;} - if (trackMeta.tracks.find(value)->second.type == "audio"){finalMap = 2;} + //Check if the track collides, and whether the track it collides with is active. + if (collidesWith != -1 && activeTracks.count(collidesWith)) { /*LTS*/ + //Print a warning message and set the state of the track to rejected. + WARN_MSG("Collision of temporary track %lu with existing track %d detected. Handling as a new valid track.", value, collidesWith); + collidesWith = -1; + } + uint64_t finalMap = collidesWith; + if (finalMap == -1) { + //No collision has been detected, assign a new final number + finalMap = (myMeta.tracks.size() ? myMeta.tracks.rbegin()->first : 0) + 1; + DEBUG_MSG(DLVL_DEVEL, "No colision detected for temporary track %lu from user %u, assigning final track number %lu", value, id, finalMap); + } //Resume either if we have more than 1 keyframe on the replacement track (assume it was already pushing before the track "dissapeared") //or if the firstms of the replacement track is later than the lastms on the existing track if (!myMeta.tracks.count(finalMap) || trackMeta.tracks.find(value)->second.keys.size() > 1 || trackMeta.tracks.find(value)->second.firstms >= myMeta.tracks[finalMap].lastms) { diff --git a/src/input/input_dtsc.cpp b/src/input/input_dtsc.cpp index 1d970467..9da3fcae 100644 --- a/src/input/input_dtsc.cpp +++ b/src/input/input_dtsc.cpp @@ -144,7 +144,7 @@ namespace Mist { bool inputDTSC::openStreamSource() { std::string source = config->getString("input"); if (source == "-"){ - srcConn = Socket::Connection(fileno(stdout),fileno(stdin)); + srcConn.open(fileno(stdout),fileno(stdin)); return true; } if (source.find("dtsc://") == 0) { @@ -159,7 +159,7 @@ namespace Mist { if (streamName == "") { streamName = givenStream; } - srcConn = Socket::Connection(host, port, true); + srcConn.open(host, port, true); if (!srcConn.connected()){ return false; } diff --git a/src/input/input_h264.cpp b/src/input/input_h264.cpp index f2af2d6d..563eeac2 100644 --- a/src/input/input_h264.cpp +++ b/src/input/input_h264.cpp @@ -44,9 +44,9 @@ namespace Mist{ int fin = -1, fout = -1; inputProcess = Util::Procs::StartPiped(args, &fin, &fout, 0); - myConn = Socket::Connection(-1, fout); + myConn.open(-1, fout); }else{ - myConn = Socket::Connection(fileno(stdout), fileno(stdin)); + myConn.open(fileno(stdout), fileno(stdin)); } myConn.Received().splitter.assign("\000\000\001", 3); myMeta.vod = false; diff --git a/src/io.cpp b/src/io.cpp index d29c7a34..a4dfa954 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -57,16 +57,17 @@ namespace Mist { bool InOutBase::bufferStart(unsigned long tid, unsigned long pageNumber) { VERYHIGH_MSG("bufferStart for stream %s, track %lu, page %lu", streamName.c_str(), tid, pageNumber); //Initialize the stream metadata if it does not yet exist +#ifndef TSLIVE_INPUT if (!nProxy.metaPages.count(0)) { initiateMeta(); } +#endif //If we are a stand-alone player skip track negotiation, as there will be nothing to negotiate with. if (standAlone) { if (!nProxy.trackMap.count(tid)) { nProxy.trackMap[tid] = tid; } } - //Negotiate the requested track if needed. return nProxy.bufferStart(tid, pageNumber, myMeta); } @@ -667,6 +668,10 @@ namespace Mist { tmpMeta.tracks[newTid] = myMeta.tracks[tid]; tmpMeta.tracks[newTid].trackID = newTid; JSON::Value tmpVal = tmpMeta.toJSON(); + if (!myMeta.tracks[tid].type.size() || !myMeta.tracks[tid].codec.size()){ + FAIL_MSG("Negotiating a track without metadata. This is a serious issue, please report this to the developers."); + BACKTRACE; + } std::string tmpStr = tmpVal.toNetPacked(); memcpy(metaPages[tid].mapped, tmpStr.data(), tmpStr.size()); HIGH_MSG("Temporary metadata written for incoming track %lu, handling as track %lu", tid, newTid); diff --git a/src/output/output.cpp b/src/output/output.cpp index 52c7b1f9..90ef457f 100644 --- a/src/output/output.cpp +++ b/src/output/output.cpp @@ -1053,7 +1053,7 @@ namespace Mist{ //actually drop what we found. //if both of the above cases occur, the next prepareNext iteration will take care of that for (std::set::iterator it = dropTracks.begin(); it != dropTracks.end(); ++it){ - dropTrack(*it, "seek/select mismatch", true); + dropTrack(*it, "seek/select mismatch"); } return false; } @@ -1061,7 +1061,7 @@ namespace Mist{ sortedPageInfo nxt = *(buffer.begin()); if (!myMeta.tracks.count(nxt.tid)){ - dropTrack(nxt.tid, "disappeared from metadata", true); + dropTrack(nxt.tid, "disappeared from metadata"); return false; } @@ -1105,7 +1105,7 @@ namespace Mist{ } //for VoD, check if we've reached the end of the track, if so, drop it if (myMeta.vod && nxt.time > myMeta.tracks[nxt.tid].lastms){ - dropTrack(nxt.tid, "Reached end of track"); + dropTrack(nxt.tid, "Reached end of track", false); } //if this is a live stream, we might have just reached the live point. //check where the next key is @@ -1137,9 +1137,9 @@ namespace Mist{ loadPageForKey(nxt.tid, ++nxtKeyNum[nxt.tid]); nxt.offset = 0; if (nProxy.curPage.count(nxt.tid) && nProxy.curPage[nxt.tid].mapped){ - unsigned long long nextTime = getDTSCTime(nProxy.curPage[nxt.tid].mapped, nxt.offset); + uint64_t nextTime = getDTSCTime(nProxy.curPage[nxt.tid].mapped, nxt.offset); if (nextTime && nextTime < nxt.time){ - dropTrack(nxt.tid, "time going backwards"); + dropTrack(nxt.tid, "EOP: time going backwards ("+JSON::Value(nextTime).asString()+" < "+JSON::Value(nxt.time).asString()+")"); }else{ if (nextTime){ nxt.time = nextTime; diff --git a/src/output/output.h b/src/output/output.h index 3b5ae09b..51797e19 100644 --- a/src/output/output.h +++ b/src/output/output.h @@ -22,9 +22,9 @@ namespace Mist { } return (time == rhs.time && tid < rhs.tid); } - unsigned int tid; - long long unsigned int time; - unsigned int offset; + uint64_t tid; + uint64_t time; + uint32_t offset; }; /// The output class is intended to be inherited by MistOut process classes. diff --git a/src/output/output_http_internal.cpp b/src/output/output_http_internal.cpp index c6c7c9a5..f3fbc74d 100644 --- a/src/output/output_http_internal.cpp +++ b/src/output/output_http_internal.cpp @@ -41,8 +41,7 @@ namespace Mist { std::string host = getConnectedHost(); dup2(myConn.getSocket(), STDIN_FILENO); dup2(myConn.getSocket(), STDOUT_FILENO); - myConn.drop(); - myConn = Socket::Connection(STDOUT_FILENO, STDIN_FILENO); + myConn.open(STDOUT_FILENO, STDIN_FILENO); myConn.setHost(host); } if (config->getOption("wrappers",true).size() == 0 || config->getString("wrappers") == ""){ @@ -279,6 +278,10 @@ namespace Mist { std::string devSkin = ""; if (H.GetVar("dev").size()) { devSkin = ",skin:\"dev\""; } + H.SetVar("stream", ""); + H.SetVar("dev", ""); + devSkin += ",urlappend:\"" + H.allVars() + "\""; + H.SetVar("stream", streamName); H.Clean(); H.SetHeader("Content-Type", "text/html"); diff --git a/src/output/output_https.cpp b/src/output/output_https.cpp new file mode 100644 index 00000000..9219fb50 --- /dev/null +++ b/src/output/output_https.cpp @@ -0,0 +1,241 @@ +#include "output_https.h" +#include + +namespace Mist{ + mbedtls_entropy_context OutHTTPS::entropy; + mbedtls_ctr_drbg_context OutHTTPS::ctr_drbg; + mbedtls_ssl_config OutHTTPS::sslConf; + mbedtls_x509_crt OutHTTPS::srvcert; + mbedtls_pk_context OutHTTPS::pkey; + + void OutHTTPS::init(Util::Config *cfg){ + Output::init(cfg); + capa["name"] = "HTTPS"; + capa["friendly"] = "HTTPS (HTTP+TLS)"; + capa["desc"] = "HTTPS connection handler, provides all enabled HTTP-based outputs"; + capa["provides"] = "HTTP"; + capa["protocol"] = "https://"; + capa["required"]["cert"]["name"] = "Certificate"; + capa["required"]["cert"]["help"] = "(Root) certificate(s) file(s) to append to chain"; + capa["required"]["cert"]["option"] = "--cert"; + capa["required"]["cert"]["short"] = "C"; + capa["required"]["cert"]["default"] = ""; + capa["required"]["cert"]["type"] = "str"; + capa["required"]["key"]["name"] = "Key"; + capa["required"]["key"]["help"] = "Private key for SSL"; + capa["required"]["key"]["option"] = "--key"; + capa["required"]["key"]["short"] = "K"; + capa["required"]["key"]["default"] = ""; + capa["required"]["key"]["type"] = "str"; + + capa["optional"]["wrappers"]["name"] = "Active players"; + capa["optional"]["wrappers"]["help"] = "Which players are attempted and in what order."; + capa["optional"]["wrappers"]["default"] = ""; + capa["optional"]["wrappers"]["type"] = "ord_multi_sel"; + capa["optional"]["wrappers"]["allowed"].append("html5"); + capa["optional"]["wrappers"]["allowed"].append("videojs"); + capa["optional"]["wrappers"]["allowed"].append("dashjs"); + capa["optional"]["wrappers"]["allowed"].append("flash_strobe"); + capa["optional"]["wrappers"]["allowed"].append("silverlight"); + capa["optional"]["wrappers"]["allowed"].append("img"); + capa["optional"]["wrappers"]["option"] = "--wrappers"; + capa["optional"]["wrappers"]["short"] = "w"; + cfg->addConnectorOptions(4433, capa); + config = cfg; + } + + OutHTTPS::OutHTTPS(Socket::Connection &C) : Output(C){ + int ret; + mbedtls_net_init(&client_fd); + client_fd.fd = C.getSocket(); + mbedtls_ssl_init(&ssl); + if ((ret = mbedtls_ctr_drbg_reseed(&ctr_drbg, (const unsigned char *)"child", 5)) != 0){ + FAIL_MSG("Could not reseed"); + C.close(); + return; + } + + // Set up the SSL connection + if ((ret = mbedtls_ssl_setup(&ssl, &sslConf)) != 0){ + FAIL_MSG("Could not set up SSL connection"); + C.close(); + return; + } + + // Inform mbedtls how we'd like to use the connection (uses default bio handlers) + // We tell it to use non-blocking IO here + mbedtls_net_set_nonblock(&client_fd); + mbedtls_ssl_set_bio(&ssl, &client_fd, mbedtls_net_send, mbedtls_net_recv, NULL); + // do the SSL handshake + while ((ret = mbedtls_ssl_handshake(&ssl)) != 0){ + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){ + char error_buf[200]; + mbedtls_strerror(ret, error_buf, 200); + MEDIUM_MSG("Could not handshake, SSL error: %s (%d)", error_buf, ret); + C.close(); + return; + }else{ + Util::sleep(100); + } + } + HIGH_MSG("Started SSL connection handler"); + } + + int OutHTTPS::run(){ + unsigned char buf[1024 * 4]; // 4k internal buffer + int ret; + + // Start a MistOutHTTP process, connected to this SSL connection + int fderr = 2; + int fd[2]; + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fd) != 0){ + FAIL_MSG("Could not open anonymous socket for SSL<->HTTP connection!"); + return 1; + } + std::deque args; + args.push_back(Util::getMyPath() + "MistOutHTTP"); + args.push_back("--ip"); + args.push_back(myConn.getHost()); + if (config->getString("nostreamtext").size()){ + args.push_back("--nostreamtext"); + args.push_back(config->getString("nostreamtext")); + } + if (config->getString("pubaddr").size()){ + args.push_back("--public-address"); + args.push_back(config->getString("pubaddr")); + } + args.push_back(""); + Util::Procs::socketList.insert(fd[0]); + pid_t http_proc = Util::Procs::StartPiped(args, &(fd[1]), &(fd[1]), &fderr); + close(fd[1]); + if (http_proc < 2){ + FAIL_MSG("Could not spawn MistOutHTTP process for SSL connection!"); + return 1; + } + Socket::Connection http(fd[0]); + http.setBlocking(false); + Socket::Buffer &http_buf = http.Received(); + + // pass data back and forth between the SSL connection and HTTP process while connected + while (config->is_active && http){ + bool activity = false; + // attempt to read SSL data, pass to HTTP + ret = mbedtls_ssl_read(&ssl, buf, sizeof(buf)); + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){ + if (ret <= 0){ + HIGH_MSG("SSL disconnect!"); + break; + } + // we received ret bytes of data to pass on. Do so. + activity = true; + http.SendNow((const char *)buf, ret); + } + + // attempt to read HTTP data, pass to SSL + if (http.spool() || http_buf.size()){ + // We have data - pass it on + activity = true; + while (http_buf.size() && http){ + int todo = http_buf.get().size(); + int done = 0; + while (done < todo){ + ret = mbedtls_ssl_write(&ssl, (const unsigned char*)http_buf.get().data() + done, todo - done); + if (ret == MBEDTLS_ERR_NET_CONN_RESET || ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT){ + HIGH_MSG("SSL disconnect!"); + http.close(); + break; + } + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){ + done += ret; + }else{ + Util::sleep(50); + } + } + http_buf.get().clear(); + } + } + if (!activity){ + Util::sleep(50); + } + } + // close the HTTP process (close stdio, kill its PID) + http.close(); + Util::Procs::Stop(http_proc); + uint16_t waiting = 0; + while (++waiting < 100){ + if (!Util::Procs::isRunning(http_proc)){break;} + Util::sleep(100); + } + return 0; + } + + + OutHTTPS::~OutHTTPS(){ + HIGH_MSG("Ending SSL connection handler"); + // close when we're done + mbedtls_ssl_close_notify(&ssl); + mbedtls_ssl_free(&ssl); + mbedtls_net_free(&client_fd); + myConn.close(); + } + + /// Listens for HTTPS requests, accepting them and connecting them to a HTTP socket + void OutHTTPS::listener(Util::Config &conf, int (*callback)(Socket::Connection &S)){ + if (config->getOption("cert", true).size() < 2 || config->getOption("key", true).size() < 2){ + FAIL_MSG("The cert/key required options were not passed!"); + return; + } + + //Declare and set up all required mbedtls structures + int ret; + mbedtls_ssl_config_init(&sslConf); + mbedtls_entropy_init(&entropy); + mbedtls_pk_init(&pkey); + mbedtls_x509_crt_init(&srvcert); + mbedtls_ctr_drbg_init(&ctr_drbg); + + // seed the rng + if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)"MistServer", 10)) != 0){ + FAIL_MSG("Could not seed the random number generator!"); + } + + //Read certificate chain(s) from cmdline option(s) + JSON::Value certs = config->getOption("cert", true); + jsonForEach(certs, it){ + if (it->asStringRef().size()){//Ignore empty entries (default is empty) + ret = mbedtls_x509_crt_parse_file(&srvcert, it->asStringRef().c_str()); + if (ret != 0){ + WARN_MSG("Could not load any certificates from file: %s", it->asStringRef().c_str()); + } + } + } + + //Read key from cmdline option + ret = mbedtls_pk_parse_keyfile(&pkey, config->getString("key").c_str(), 0); + if (ret != 0){ + FAIL_MSG("Could not load any keys from file: %s", config->getString("key").c_str()); + return; + } + + if ((ret = mbedtls_ssl_config_defaults(&sslConf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0){ + FAIL_MSG("SSL config defaults failed"); + return; + } + mbedtls_ssl_conf_rng(&sslConf, mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_conf_ca_chain(&sslConf, srvcert.next, NULL); + if ((ret = mbedtls_ssl_conf_own_cert(&sslConf, &srvcert, &pkey)) != 0){ + FAIL_MSG("SSL config own certificate failed"); + return; + } + + Output::listener(conf, callback); + + //Free all the mbedtls structures + mbedtls_x509_crt_free(&srvcert); + mbedtls_pk_free(&pkey); + mbedtls_ssl_config_free(&sslConf); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + } +} + diff --git a/src/output/output_https.h b/src/output/output_https.h new file mode 100644 index 00000000..8a752599 --- /dev/null +++ b/src/output/output_https.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include "output.h" +#include +#include +#include +#include +#include +#include +#include + +namespace Mist { + + class OutHTTPS : public Output { + public: + OutHTTPS(Socket::Connection & C); + virtual ~OutHTTPS(); + void onRequest(){}; + int run(); + static bool listenMode(){return true;} + static void init(Util::Config * cfg); + static void listener(Util::Config & conf, int (*callback)(Socket::Connection & S)); + private: + mbedtls_net_context client_fd; + mbedtls_ssl_context ssl; + static mbedtls_entropy_context entropy; + static mbedtls_ctr_drbg_context ctr_drbg; + static mbedtls_ssl_config sslConf; + static mbedtls_x509_crt srvcert; + static mbedtls_pk_context pkey; + }; +} + +typedef Mist::OutHTTPS mistOut; diff --git a/src/output/output_ts.cpp b/src/output/output_ts.cpp index 2f558434..18815fd2 100644 --- a/src/output/output_ts.cpp +++ b/src/output/output_ts.cpp @@ -38,7 +38,7 @@ namespace Mist { capa["friendly"] = "TS over TCP"; capa["desc"] = "Real time streaming in MPEG2/TS format over raw TCP"; capa["deps"] = ""; - capa["required"]["streamname"]["name"] = "Stream"; + capa["required"]["streamname"]["name"] = "Source stream"; capa["required"]["streamname"]["help"] = "What streamname to serve. For multiple streams, add this protocol multiple times using different ports."; capa["required"]["streamname"]["type"] = "str"; capa["required"]["streamname"]["option"] = "--stream";